From 15d67cf39b0f04249fcae244a29bf3c866e5749d Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 1 Oct 2025 15:22:12 -0700 Subject: [PATCH 1/4] Store settings on the thread instead or turn --- sdk/typescript/README.md | 18 +++++++++++++++ sdk/typescript/src/codex.ts | 9 ++++---- sdk/typescript/src/exec.ts | 2 +- sdk/typescript/src/index.ts | 2 +- sdk/typescript/src/thread.ts | 17 ++++++++------ .../src/{turnOptions.ts => threadOptions.ts} | 2 +- sdk/typescript/tests/run.test.ts | 22 +++++++++---------- 7 files changed, 46 insertions(+), 26 deletions(-) rename sdk/typescript/src/{turnOptions.ts => threadOptions.ts} (90%) diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md index 19accb0b30..b74fd44ba3 100644 --- a/sdk/typescript/README.md +++ b/sdk/typescript/README.md @@ -51,3 +51,21 @@ const result = await thread.run("Implement the fix"); console.log(result); ``` + +### Working directory + +By default, Codex will run in the current working directory. You can change the working directory by passing the `workingDirectory` option to the when creating a thread. + +```typescript +const thread = codex.startThread({ + workingDirectory: "/path/to/working/directory", +}); +``` + +To avoid unrecoverable errors, Codex requires the working directory to be a git repository. You can skip the git repository check by passing the `skipGitRepoCheck` option to the when creating a thread. + +```typescript +const thread = codex.startThread({ + skipGitRepoCheck: true, +}); +``` diff --git a/sdk/typescript/src/codex.ts b/sdk/typescript/src/codex.ts index 8f87122839..aac03732e1 100644 --- a/sdk/typescript/src/codex.ts +++ b/sdk/typescript/src/codex.ts @@ -1,6 +1,7 @@ import { CodexOptions } from "./codexOptions"; import { CodexExec } from "./exec"; import { Thread } from "./thread"; +import { TheadOptions } from "./threadOptions"; /** * Codex is the main class for interacting with the Codex agent. @@ -20,8 +21,8 @@ export class Codex { * Starts a new conversation with an agent. * @returns A new thread instance. */ - startThread(): Thread { - return new Thread(this.exec, this.options); + startThread(options: TheadOptions = {}): Thread { + return new Thread(this.exec, this.options, options); } /** @@ -31,7 +32,7 @@ export class Codex { * @param id The id of the thread to resume. * @returns A new thread instance. */ - resumeThread(id: string): Thread { - return new Thread(this.exec, this.options, id); + resumeThread(id: string, options: TheadOptions = {}): Thread { + return new Thread(this.exec, this.options, options, id); } } diff --git a/sdk/typescript/src/exec.ts b/sdk/typescript/src/exec.ts index cdb1982f0d..91ce763909 100644 --- a/sdk/typescript/src/exec.ts +++ b/sdk/typescript/src/exec.ts @@ -2,7 +2,7 @@ import { spawn } from "node:child_process"; import readline from "node:readline"; -import { SandboxMode } from "./turnOptions"; +import { SandboxMode } from "./threadOptions"; import path from "node:path"; import { fileURLToPath } from "node:url"; diff --git a/sdk/typescript/src/index.ts b/sdk/typescript/src/index.ts index f2f84d153c..ef66984810 100644 --- a/sdk/typescript/src/index.ts +++ b/sdk/typescript/src/index.ts @@ -29,4 +29,4 @@ export { Codex } from "./codex"; export type { CodexOptions } from "./codexOptions"; -export type { TurnOptions, ApprovalMode, SandboxMode } from "./turnOptions"; +export type { TheadOptions as TurnOptions, ApprovalMode, SandboxMode } from "./threadOptions"; diff --git a/sdk/typescript/src/thread.ts b/sdk/typescript/src/thread.ts index 7ec710217c..e62bc0354a 100644 --- a/sdk/typescript/src/thread.ts +++ b/sdk/typescript/src/thread.ts @@ -2,7 +2,7 @@ import { CodexOptions } from "./codexOptions"; import { ThreadEvent } from "./events"; import { CodexExec } from "./exec"; import { ThreadItem } from "./items"; -import { TurnOptions } from "./turnOptions"; +import { TheadOptions } from "./threadOptions"; /** Completed turn. */ export type Turn = { @@ -29,27 +29,30 @@ export class Thread { private _exec: CodexExec; private _options: CodexOptions; private _id: string | null; + private _threadOptions: TheadOptions; /** Returns the ID of the thread. Populated after the first turn starts. */ public get id(): string | null { return this._id; } - constructor(exec: CodexExec, options: CodexOptions, id: string | null = null) { + /* @internal */ + constructor(exec: CodexExec, options: CodexOptions, threadOptions: TheadOptions, id: string | null = null) { this._exec = exec; this._options = options; this._id = id; + this._threadOptions = threadOptions; } /** Provides the input to the agent and streams events as they are produced during the turn. */ - async runStreamed(input: string, options?: TurnOptions): Promise { - return { events: this.runStreamedInternal(input, options) }; + async runStreamed(input: string): Promise { + return { events: this.runStreamedInternal(input) }; } private async *runStreamedInternal( input: string, - options?: TurnOptions, ): AsyncGenerator { + const options = this._threadOptions; const generator = this._exec.run({ input, baseUrl: this._options.baseUrl, @@ -75,8 +78,8 @@ export class Thread { } /** Provides the input to the agent and returns the completed turn. */ - async run(input: string, options?: TurnOptions): Promise { - const generator = this.runStreamedInternal(input, options); + async run(input: string): Promise { + const generator = this.runStreamedInternal(input); const items: ThreadItem[] = []; let finalResponse: string = ""; for await (const event of generator) { diff --git a/sdk/typescript/src/turnOptions.ts b/sdk/typescript/src/threadOptions.ts similarity index 90% rename from sdk/typescript/src/turnOptions.ts rename to sdk/typescript/src/threadOptions.ts index ce56d7b2c6..e27761a28a 100644 --- a/sdk/typescript/src/turnOptions.ts +++ b/sdk/typescript/src/threadOptions.ts @@ -2,7 +2,7 @@ export type ApprovalMode = "never" | "on-request" | "on-failure" | "untrusted"; export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access"; -export type TurnOptions = { +export type TheadOptions = { model?: string; sandboxMode?: SandboxMode; workingDirectory?: string; diff --git a/sdk/typescript/tests/run.test.ts b/sdk/typescript/tests/run.test.ts index 2f15237f4f..2b06c7414d 100644 --- a/sdk/typescript/tests/run.test.ts +++ b/sdk/typescript/tests/run.test.ts @@ -109,9 +109,7 @@ describe("Codex", () => { const thread = client.startThread(); await thread.run("first input"); - await thread.run("second input", { - model: "gpt-test-1", - }); + await thread.run("second input"); // Check second request continues the same thread expect(requests.length).toBeGreaterThanOrEqual(2); @@ -119,7 +117,7 @@ describe("Codex", () => { expect(secondRequest).toBeDefined(); const payload = secondRequest!.json; - expect(payload.model).toBe("gpt-test-1"); + expect(payload.input.at(-1)!.content![0]!.text).toBe("second input"); const assistantEntry = payload.input.find( (entry: { role: string }) => entry.role === "assistant", ); @@ -197,11 +195,11 @@ describe("Codex", () => { try { const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" }); - const thread = client.startThread(); - await thread.run("apply options", { + const thread = client.startThread({ model: "gpt-test-1", sandboxMode: "workspace-write", }); + await thread.run("apply options"); const payload = requests[0]; expect(payload).toBeDefined(); @@ -240,11 +238,11 @@ describe("Codex", () => { apiKey: "test", }); - const thread = client.startThread(); - await thread.run("use custom working directory", { + const thread = client.startThread({ workingDirectory, skipGitRepoCheck: true, }); + await thread.run("use custom working directory"); const commandArgs = spawnArgs[0]; expectPair(commandArgs, ["--cd", workingDirectory]); @@ -274,11 +272,11 @@ describe("Codex", () => { apiKey: "test", }); - const thread = client.startThread(); + const thread = client.startThread({ + workingDirectory, + }); await expect( - thread.run("use custom working directory", { - workingDirectory, - }), + thread.run("use custom working directory"), ).rejects.toThrow(/Not inside a trusted directory/); } finally { await close(); From 56890718efb4a0c01d86866073fb6e1b39cd2c3c Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 1 Oct 2025 15:23:34 -0700 Subject: [PATCH 2/4] typescript: export TheadOptions instead of TurnOptions alias in index --- sdk/typescript/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/typescript/src/index.ts b/sdk/typescript/src/index.ts index ef66984810..0bc14b50fd 100644 --- a/sdk/typescript/src/index.ts +++ b/sdk/typescript/src/index.ts @@ -29,4 +29,4 @@ export { Codex } from "./codex"; export type { CodexOptions } from "./codexOptions"; -export type { TheadOptions as TurnOptions, ApprovalMode, SandboxMode } from "./threadOptions"; +export type { TheadOptions, ApprovalMode, SandboxMode } from "./threadOptions"; From 4cb24a30d4b801c1a404be0856526a06cf2ee894 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 1 Oct 2025 15:25:34 -0700 Subject: [PATCH 3/4] thread.ts: format Thread constructor signature and runStreamedInternal definition for readability codexExecSpy.ts: format actualChildProcess assignment for readability run.test.ts: format expect().rejects.toThrow() call for readability --- sdk/typescript/src/thread.ts | 11 +++++++---- sdk/typescript/tests/codexExecSpy.ts | 3 ++- sdk/typescript/tests/run.test.ts | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sdk/typescript/src/thread.ts b/sdk/typescript/src/thread.ts index e62bc0354a..819de973ac 100644 --- a/sdk/typescript/src/thread.ts +++ b/sdk/typescript/src/thread.ts @@ -37,7 +37,12 @@ export class Thread { } /* @internal */ - constructor(exec: CodexExec, options: CodexOptions, threadOptions: TheadOptions, id: string | null = null) { + constructor( + exec: CodexExec, + options: CodexOptions, + threadOptions: TheadOptions, + id: string | null = null, + ) { this._exec = exec; this._options = options; this._id = id; @@ -49,9 +54,7 @@ export class Thread { return { events: this.runStreamedInternal(input) }; } - private async *runStreamedInternal( - input: string, - ): AsyncGenerator { + private async *runStreamedInternal(input: string): AsyncGenerator { const options = this._threadOptions; const generator = this._exec.run({ input, diff --git a/sdk/typescript/tests/codexExecSpy.ts b/sdk/typescript/tests/codexExecSpy.ts index 3715f71e0b..bf7cbd6cc8 100644 --- a/sdk/typescript/tests/codexExecSpy.ts +++ b/sdk/typescript/tests/codexExecSpy.ts @@ -5,7 +5,8 @@ jest.mock("node:child_process", () => { return { ...actual, spawn: jest.fn(actual.spawn) }; }); -const actualChildProcess = jest.requireActual("node:child_process"); +const actualChildProcess = + jest.requireActual("node:child_process"); const spawnMock = child_process.spawn as jest.MockedFunction; export function codexExecSpy(): { args: string[][]; restore: () => void } { diff --git a/sdk/typescript/tests/run.test.ts b/sdk/typescript/tests/run.test.ts index 2b06c7414d..357d8cd0db 100644 --- a/sdk/typescript/tests/run.test.ts +++ b/sdk/typescript/tests/run.test.ts @@ -275,9 +275,9 @@ describe("Codex", () => { const thread = client.startThread({ workingDirectory, }); - await expect( - thread.run("use custom working directory"), - ).rejects.toThrow(/Not inside a trusted directory/); + await expect(thread.run("use custom working directory")).rejects.toThrow( + /Not inside a trusted directory/, + ); } finally { await close(); } From ee72a0ebfae8ecf88dffd8f59b2924d9c4c96851 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 1 Oct 2025 17:19:10 -0700 Subject: [PATCH 4/4] fix(types): correct TheadOptions typo to ThreadOptions throughout codebase --- sdk/typescript/src/codex.ts | 6 +++--- sdk/typescript/src/index.ts | 2 +- sdk/typescript/src/thread.ts | 6 +++--- sdk/typescript/src/threadOptions.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/typescript/src/codex.ts b/sdk/typescript/src/codex.ts index aac03732e1..84376e677e 100644 --- a/sdk/typescript/src/codex.ts +++ b/sdk/typescript/src/codex.ts @@ -1,7 +1,7 @@ import { CodexOptions } from "./codexOptions"; import { CodexExec } from "./exec"; import { Thread } from "./thread"; -import { TheadOptions } from "./threadOptions"; +import { ThreadOptions } from "./threadOptions"; /** * Codex is the main class for interacting with the Codex agent. @@ -21,7 +21,7 @@ export class Codex { * Starts a new conversation with an agent. * @returns A new thread instance. */ - startThread(options: TheadOptions = {}): Thread { + startThread(options: ThreadOptions = {}): Thread { return new Thread(this.exec, this.options, options); } @@ -32,7 +32,7 @@ export class Codex { * @param id The id of the thread to resume. * @returns A new thread instance. */ - resumeThread(id: string, options: TheadOptions = {}): Thread { + resumeThread(id: string, options: ThreadOptions = {}): Thread { return new Thread(this.exec, this.options, options, id); } } diff --git a/sdk/typescript/src/index.ts b/sdk/typescript/src/index.ts index 0bc14b50fd..b759b3c68f 100644 --- a/sdk/typescript/src/index.ts +++ b/sdk/typescript/src/index.ts @@ -29,4 +29,4 @@ export { Codex } from "./codex"; export type { CodexOptions } from "./codexOptions"; -export type { TheadOptions, ApprovalMode, SandboxMode } from "./threadOptions"; +export type { ThreadOptions as TheadOptions, ApprovalMode, SandboxMode } from "./threadOptions"; diff --git a/sdk/typescript/src/thread.ts b/sdk/typescript/src/thread.ts index 819de973ac..fe32a27136 100644 --- a/sdk/typescript/src/thread.ts +++ b/sdk/typescript/src/thread.ts @@ -2,7 +2,7 @@ import { CodexOptions } from "./codexOptions"; import { ThreadEvent } from "./events"; import { CodexExec } from "./exec"; import { ThreadItem } from "./items"; -import { TheadOptions } from "./threadOptions"; +import { ThreadOptions } from "./threadOptions"; /** Completed turn. */ export type Turn = { @@ -29,7 +29,7 @@ export class Thread { private _exec: CodexExec; private _options: CodexOptions; private _id: string | null; - private _threadOptions: TheadOptions; + private _threadOptions: ThreadOptions; /** Returns the ID of the thread. Populated after the first turn starts. */ public get id(): string | null { @@ -40,7 +40,7 @@ export class Thread { constructor( exec: CodexExec, options: CodexOptions, - threadOptions: TheadOptions, + threadOptions: ThreadOptions, id: string | null = null, ) { this._exec = exec; diff --git a/sdk/typescript/src/threadOptions.ts b/sdk/typescript/src/threadOptions.ts index e27761a28a..7f01488d23 100644 --- a/sdk/typescript/src/threadOptions.ts +++ b/sdk/typescript/src/threadOptions.ts @@ -2,7 +2,7 @@ export type ApprovalMode = "never" | "on-request" | "on-failure" | "untrusted"; export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access"; -export type TheadOptions = { +export type ThreadOptions = { model?: string; sandboxMode?: SandboxMode; workingDirectory?: string;