Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ The adapter advertises ACP auth methods during initialization. Clients can authe
- `INITIAL_AGENT_MODE` - initial mode id: `read-only`, `agent`, or `agent-full-access`.
- `NO_BROWSER` - hide browser-based ChatGPT auth when set.
- `APP_SERVER_LOGS` - directory for adapter logs.
- `ACP_DISABLE_TITLE_GENERATION` - set to `1` to skip the adapter's extra model call for session titles. Applies to every session in this adapter process; unset or other values retain automatic generation. Existing titles, prompt-derived fallback titles, and `/rename` remain available.

## Development

Expand Down
1 change: 1 addition & 0 deletions readme-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Set `CODEX_PATH` to run a different Codex binary; versions other than the one sp
- `INITIAL_AGENT_MODE` - initial mode id: `read-only`, `agent`, or `agent-full-access`.
- `NO_BROWSER` - hide browser-based ChatGPT auth when set.
- `APP_SERVER_LOGS` - directory for adapter logs.
- `ACP_DISABLE_TITLE_GENERATION` - set to `1` to skip the adapter's extra model call for session titles. Applies to every session in this adapter process; unset or other values retain automatic generation. Existing titles, prompt-derived fallback titles, and `/rename` remain available. This is an adapter-specific option, not an ACP protocol setting.

### Quick start

Expand Down
1 change: 1 addition & 0 deletions src/TitleGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class TitleGenerator {
* not turn.items — turn.items contains only agent output).
*/
onTurnCompleted(userPromptText: string): void {
if (process.env["ACP_DISABLE_TITLE_GENERATION"] === "1") return;
if (this.generated) return;
const src = this.getSessionTitleSource();
// "explicit": user renamed or session loaded with a name — skip
Expand Down
53 changes: 53 additions & 0 deletions src/__tests__/TitleGenerator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { CodexAppServerClient } from "../CodexAppServerClient";
import { TitleGenerator } from "../TitleGenerator";

describe("TitleGenerator", () => {
afterEach(() => vi.unstubAllEnvs());

function client() {
return {
threadStart: vi.fn().mockResolvedValue({ thread: { id: "title-thread" } }),
runTurn: vi.fn().mockResolvedValue({
turn: { items: [{ type: "agentMessage", text: '{"title":"Review project memory"}' }] },
}),
threadSetName: vi.fn().mockResolvedValue({}),
};
}

it("starts no title thread or model request when disabled", async () => {
vi.stubEnv("ACP_DISABLE_TITLE_GENERATION", "1");
const codex = client();
const titles = new TitleGenerator(
codex as unknown as CodexAppServerClient, "session", "/workspace", () => "unset",
);

titles.onTurnCompleted("Review the project's memory notes");
titles.onTurnCompleted("Review another note");
await new Promise<void>(resolve => setImmediate(resolve));

expect(codex.threadStart).not.toHaveBeenCalled();
expect(codex.runTurn).not.toHaveBeenCalled();
expect(codex.threadSetName).not.toHaveBeenCalled();
});

it.each([undefined, "0"])("still generates a title when the switch is %s", async value => {
vi.stubEnv("ACP_DISABLE_TITLE_GENERATION", value);
const codex = client();
const titles = new TitleGenerator(
codex as unknown as CodexAppServerClient, "session", "/workspace", () => "unset",
);

titles.onTurnCompleted("Review the project's memory notes");
await vi.waitFor(() => expect(codex.threadSetName).toHaveBeenCalledWith({
threadId: "session",
name: "Review project memory",
}));

expect(codex.threadStart).toHaveBeenCalledExactlyOnceWith({
cwd: "/workspace",
ephemeral: true,
});
expect(codex.runTurn).toHaveBeenCalledTimes(1);
});
});