[SDK] Add network_access and web_search options to TypeScript SDK (#6367)
## Summary This PR adds two new optional boolean fields to `ThreadOptions` in the TypeScript SDK: - **`networkAccess`**: Enables network access in the sandbox by setting `sandbox_workspace_write.network_access` config - **`webSearch`**: Enables the web search tool by setting `tools.web_search` config These options map to existing Codex configuration options and are properly threaded through the SDK layers: 1. `ThreadOptions` (threadOptions.ts) - User-facing API 2. `CodexExecArgs` (exec.ts) - Internal execution args 3. CLI flags via `--config` in the `codex exec` command ## Changes - `sdk/typescript/src/threadOptions.ts`: Added `networkAccess` and `webSearch` fields to `ThreadOptions` type - `sdk/typescript/src/exec.ts`: Added fields to `CodexExecArgs` and CLI flag generation - `sdk/typescript/src/thread.ts`: Pass options through to exec layer ## Test Plan - [x] Build succeeds (`pnpm build`) - [x] Linter passes (`pnpm lint`) - [x] Type definitions are properly exported - [ ] Manual testing with sample code (to be done by reviewer) --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import path from "node:path";
|
|||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
import { SandboxMode, ModelReasoningEffort } from "./threadOptions";
|
import { SandboxMode, ModelReasoningEffort, ApprovalMode } from "./threadOptions";
|
||||||
|
|
||||||
export type CodexExecArgs = {
|
export type CodexExecArgs = {
|
||||||
input: string;
|
input: string;
|
||||||
@@ -24,6 +24,12 @@ export type CodexExecArgs = {
|
|||||||
outputSchemaFile?: string;
|
outputSchemaFile?: string;
|
||||||
// --config model_reasoning_effort
|
// --config model_reasoning_effort
|
||||||
modelReasoningEffort?: ModelReasoningEffort;
|
modelReasoningEffort?: ModelReasoningEffort;
|
||||||
|
// --config sandbox_workspace_write.network_access
|
||||||
|
networkAccessEnabled?: boolean;
|
||||||
|
// --config features.web_search_request
|
||||||
|
webSearchEnabled?: boolean;
|
||||||
|
// --config approval_policy
|
||||||
|
approvalPolicy?: ApprovalMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
const INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
||||||
@@ -62,6 +68,18 @@ export class CodexExec {
|
|||||||
commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
|
commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.networkAccessEnabled !== undefined) {
|
||||||
|
commandArgs.push("--config", `sandbox_workspace_write.network_access=${args.networkAccessEnabled}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.webSearchEnabled !== undefined) {
|
||||||
|
commandArgs.push("--config", `features.web_search_request=${args.webSearchEnabled}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.approvalPolicy) {
|
||||||
|
commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`);
|
||||||
|
}
|
||||||
|
|
||||||
if (args.images?.length) {
|
if (args.images?.length) {
|
||||||
for (const image of args.images) {
|
for (const image of args.images) {
|
||||||
commandArgs.push("--image", image);
|
commandArgs.push("--image", image);
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ export class Thread {
|
|||||||
skipGitRepoCheck: options?.skipGitRepoCheck,
|
skipGitRepoCheck: options?.skipGitRepoCheck,
|
||||||
outputSchemaFile: schemaPath,
|
outputSchemaFile: schemaPath,
|
||||||
modelReasoningEffort: options?.modelReasoningEffort,
|
modelReasoningEffort: options?.modelReasoningEffort,
|
||||||
|
networkAccessEnabled: options?.networkAccessEnabled,
|
||||||
|
webSearchEnabled: options?.webSearchEnabled,
|
||||||
|
approvalPolicy: options?.approvalPolicy,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
for await (const item of generator) {
|
for await (const item of generator) {
|
||||||
|
|||||||
@@ -10,4 +10,7 @@ export type ThreadOptions = {
|
|||||||
workingDirectory?: string;
|
workingDirectory?: string;
|
||||||
skipGitRepoCheck?: boolean;
|
skipGitRepoCheck?: boolean;
|
||||||
modelReasoningEffort?: ModelReasoningEffort;
|
modelReasoningEffort?: ModelReasoningEffort;
|
||||||
|
networkAccessEnabled?: boolean;
|
||||||
|
webSearchEnabled?: boolean;
|
||||||
|
approvalPolicy?: ApprovalMode;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -254,6 +254,99 @@ describe("Codex", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("passes networkAccessEnabled to exec", async () => {
|
||||||
|
const { url, close } = await startResponsesTestProxy({
|
||||||
|
statusCode: 200,
|
||||||
|
responseBodies: [
|
||||||
|
sse(
|
||||||
|
responseStarted("response_1"),
|
||||||
|
assistantMessage("Network access enabled", "item_1"),
|
||||||
|
responseCompleted("response_1"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { args: spawnArgs, restore } = codexExecSpy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||||
|
|
||||||
|
const thread = client.startThread({
|
||||||
|
networkAccessEnabled: true,
|
||||||
|
});
|
||||||
|
await thread.run("test network access");
|
||||||
|
|
||||||
|
const commandArgs = spawnArgs[0];
|
||||||
|
expect(commandArgs).toBeDefined();
|
||||||
|
expectPair(commandArgs, ["--config", "sandbox_workspace_write.network_access=true"]);
|
||||||
|
} finally {
|
||||||
|
restore();
|
||||||
|
await close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes webSearchEnabled to exec", async () => {
|
||||||
|
const { url, close } = await startResponsesTestProxy({
|
||||||
|
statusCode: 200,
|
||||||
|
responseBodies: [
|
||||||
|
sse(
|
||||||
|
responseStarted("response_1"),
|
||||||
|
assistantMessage("Web search enabled", "item_1"),
|
||||||
|
responseCompleted("response_1"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { args: spawnArgs, restore } = codexExecSpy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||||
|
|
||||||
|
const thread = client.startThread({
|
||||||
|
webSearchEnabled: true,
|
||||||
|
});
|
||||||
|
await thread.run("test web search");
|
||||||
|
|
||||||
|
const commandArgs = spawnArgs[0];
|
||||||
|
expect(commandArgs).toBeDefined();
|
||||||
|
expectPair(commandArgs, ["--config", "features.web_search_request=true"]);
|
||||||
|
} finally {
|
||||||
|
restore();
|
||||||
|
await close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes approvalPolicy to exec", async () => {
|
||||||
|
const { url, close } = await startResponsesTestProxy({
|
||||||
|
statusCode: 200,
|
||||||
|
responseBodies: [
|
||||||
|
sse(
|
||||||
|
responseStarted("response_1"),
|
||||||
|
assistantMessage("Approval policy set", "item_1"),
|
||||||
|
responseCompleted("response_1"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { args: spawnArgs, restore } = codexExecSpy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||||
|
|
||||||
|
const thread = client.startThread({
|
||||||
|
approvalPolicy: "on-request",
|
||||||
|
});
|
||||||
|
await thread.run("test approval policy");
|
||||||
|
|
||||||
|
const commandArgs = spawnArgs[0];
|
||||||
|
expect(commandArgs).toBeDefined();
|
||||||
|
expectPair(commandArgs, ["--config", 'approval_policy="on-request"']);
|
||||||
|
} finally {
|
||||||
|
restore();
|
||||||
|
await close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("writes output schema to a temporary file and forwards it", async () => {
|
it("writes output schema to a temporary file and forwards it", async () => {
|
||||||
const { url, close, requests } = await startResponsesTestProxy({
|
const { url, close, requests } = await startResponsesTestProxy({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
Reference in New Issue
Block a user