diff --git a/packages/adapters/codex-local/src/index.test.ts b/packages/adapters/codex-local/src/index.test.ts index c70529e6daf..19c45b01dc3 100644 --- a/packages/adapters/codex-local/src/index.test.ts +++ b/packages/adapters/codex-local/src/index.test.ts @@ -14,21 +14,23 @@ describe("codex local adapter metadata", () => { // Default to the concrete gpt-5.6-sol slug — Codex ships no metadata for the bare gpt-5.6 // alias, so it must not be advertised or used as the default (it triggers a fallback warning). expect(DEFAULT_CODEX_LOCAL_MODEL).toBe("gpt-5.6-sol"); - expect(modelIds.slice(0, 4)).toEqual([ + expect(modelIds.slice(0, 5)).toEqual([ "gpt-5.6-sol", "gpt-6-astra", + "gpt-6-sol", "gpt-5.6-terra", "gpt-5.6-luna", ]); expect(modelIds).not.toContain("gpt-5.6"); expect(isCodexLocalFastModeSupported(DEFAULT_CODEX_LOCAL_MODEL)).toBe(true); expect(isCodexLocalFastModeSupported("gpt-6-astra")).toBe(true); + expect(isCodexLocalFastModeSupported("gpt-6-sol")).toBe(true); expect(modelIds).not.toContain("gpt-5.3-codex"); expect(modelIds).not.toContain("gpt-5.3-codex-spark"); }); - it("uses the reasoning efforts advertised for GPT-6 Astra", () => { - expect(codexLocalReasoningEffortsForModel("gpt-6-astra")).toEqual([ + it.each(["gpt-6-astra", "gpt-6-sol", " gpt-6-sol "])("uses the reasoning efforts advertised for %s", (model) => { + expect(codexLocalReasoningEffortsForModel(model)).toEqual([ "low", "medium", "high", diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index 4bd62ddeffc..d93fec2e612 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -14,6 +14,7 @@ export const DEFAULT_CODEX_LOCAL_MODEL = PAPERCLIP_RUNNER_DEFAULT_MODELS.codex; export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true; export const CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS = [ "gpt-6-astra", + "gpt-6-sol", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", @@ -41,7 +42,7 @@ const CODEX_LOCAL_DEFAULT_REASONING_EFFORTS = [ "xhigh", ] as const; -const CODEX_LOCAL_ASTRA_REASONING_EFFORTS = [ +const CODEX_LOCAL_GPT_6_REASONING_EFFORTS = [ "low", "medium", "high", @@ -52,7 +53,7 @@ const CODEX_LOCAL_ASTRA_REASONING_EFFORTS = [ export type CodexLocalReasoningEffort = | (typeof CODEX_LOCAL_DEFAULT_REASONING_EFFORTS)[number] - | (typeof CODEX_LOCAL_ASTRA_REASONING_EFFORTS)[number]; + | (typeof CODEX_LOCAL_GPT_6_REASONING_EFFORTS)[number]; export function normalizeCodexModel(model: string | null | undefined): string { const normalizedModel = normalizeModelId(model); @@ -62,8 +63,9 @@ export function normalizeCodexModel(model: string | null | undefined): string { export function codexLocalReasoningEffortsForModel( model: string | null | undefined, ): readonly CodexLocalReasoningEffort[] { - return normalizeCodexModel(model) === "gpt-6-astra" - ? CODEX_LOCAL_ASTRA_REASONING_EFFORTS + const normalizedModel = normalizeCodexModel(model); + return normalizedModel === "gpt-6-astra" || normalizedModel === "gpt-6-sol" + ? CODEX_LOCAL_GPT_6_REASONING_EFFORTS : CODEX_LOCAL_DEFAULT_REASONING_EFFORTS; } @@ -94,6 +96,7 @@ export const models = [ // DEFAULT_CODEX_LOCAL_MODEL is gpt-5.6-sol, so it doubles as the first (default) 5.6 entry. { id: DEFAULT_CODEX_LOCAL_MODEL, label: DEFAULT_CODEX_LOCAL_MODEL }, { id: "gpt-6-astra", label: "gpt-6-astra" }, + { id: "gpt-6-sol", label: "gpt-6-sol" }, { id: "gpt-5.6-terra", label: "gpt-5.6-terra" }, { id: "gpt-5.6-luna", label: "gpt-5.6-luna" }, { id: "gpt-5.4", label: "gpt-5.4" }, @@ -116,10 +119,10 @@ Core fields: - cwd (string, optional): default absolute working directory fallback for the agent process (created if missing when possible) - instructionsFilePath (string, optional): absolute path to a markdown instructions file prepended to stdin prompt at runtime - model (string, optional): Codex model id -- modelReasoningEffort (string, optional): reasoning effort override passed via -c model_reasoning_effort=...; GPT-6 Astra supports low|medium|high|xhigh|max|ultra +- modelReasoningEffort (string, optional): reasoning effort override passed via -c model_reasoning_effort=...; GPT-6 Astra and Sol support low|medium|high|xhigh|max|ultra - promptTemplate (string, optional): run prompt template - search (boolean, optional): run codex with --search -- fastMode (boolean, optional): enable Codex Fast mode; supported on GPT-6 Astra, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and passed through for manual model IDs +- fastMode (boolean, optional): enable Codex Fast mode; supported on GPT-6 Astra and Sol, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and passed through for manual model IDs - dangerouslyBypassApprovalsAndSandbox (boolean, optional): run with bypass flag - command (string, optional): defaults to "codex" - extraArgs (string[], optional): additional CLI args @@ -150,7 +153,7 @@ Notes: - Paperclip injects desired local skills into the effective CODEX_HOME/skills/ directory at execution time so Codex can discover "$paperclip" and related skills without polluting the project working directory. For new and updated agents, Paperclip assigns an isolated managed home at ~/.paperclip/instances//companies//agents//codex-home/skills/; when CODEX_HOME is explicitly overridden in adapter config, that override is used instead. - New and updated codex_local agents persist an empty OPENAI_API_KEY override by default so a host-level OPENAI_API_KEY cannot leak into Codex runs through process inheritance. Explicit CODEX_HOME overrides must not point at the shared company codex-home, $CODEX_HOME, or ~/.codex. - Some model/tool combinations reject certain effort levels (for example minimal with web search enabled). -- Fast mode is supported on GPT-6 Astra, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and manual model IDs. When enabled for those models, Paperclip applies \`service_tier="fast"\` and \`features.fast_mode=true\`. +- Fast mode is supported on GPT-6 Astra and Sol, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and manual model IDs. When enabled for those models, Paperclip applies \`service_tier="fast"\` and \`features.fast_mode=true\`. - When Paperclip realizes a workspace/runtime for a run, it injects PAPERCLIP_WORKSPACE_* and PAPERCLIP_RUNTIME_* env vars for agent-side tooling. - The ACP engine keeps its workspace sandbox and enables network access on each turn. Explicit sandbox_workspace_write.network_access overrides in extraArgs (or env.PAPERCLIP_CODEX_ACP_NETWORK_ACCESS="false") disable it; execution-target network denial wins. The bundled ACP patch is needed because upstream mode presets override Codex config.toml on every turn. - The CLI engine defaults to a writable workspace sandbox with network access for unattended work and Paperclip API calls. It does not enable the dangerous bypass flag. Explicit sandbox modes/profiles and network overrides in extraArgs retain their meaning. An execution-target network denial remains enforced. diff --git a/packages/adapters/codex-local/src/server/acp.test.ts b/packages/adapters/codex-local/src/server/acp.test.ts index e98a1881e4f..9ac37d61f82 100644 --- a/packages/adapters/codex-local/src/server/acp.test.ts +++ b/packages/adapters/codex-local/src/server/acp.test.ts @@ -508,14 +508,14 @@ describe("codex_local ACP lane", () => { }); }); - it("forwards GPT-6 Astra controls to the ACPX Codex target", () => { + it.each(["gpt-6-astra", "gpt-6-sol"])("forwards %s controls to the ACPX Codex target", (model) => { expect(buildCodexAcpConfig({ engine: "acp", - model: "gpt-6-astra", + model, modelReasoningEffort: "ultra", fastMode: true, })).toMatchObject({ - model: "gpt-6-astra", + model, modelReasoningEffort: "ultra", fastMode: true, }); diff --git a/packages/adapters/codex-local/src/server/codex-args.test.ts b/packages/adapters/codex-local/src/server/codex-args.test.ts index 0cdfa266db4..3d519ba57d1 100644 --- a/packages/adapters/codex-local/src/server/codex-args.test.ts +++ b/packages/adapters/codex-local/src/server/codex-args.test.ts @@ -9,14 +9,14 @@ describe("buildCodexExecArgs", () => { if (resumeSessionId) expect(args.slice(-3)).toEqual(["resume", resumeSessionId, "-"]); }); - it("forwards GPT-6 Astra, its ultra reasoning effort, and fast mode", () => { + it.each(["gpt-6-astra", "gpt-6-sol"])("forwards %s, its ultra reasoning effort, and fast mode", (model) => { const result = buildCodexExecArgs({ - model: "gpt-6-astra", + model, modelReasoningEffort: "ultra", fastMode: true, }); - expect(result.model).toBe("gpt-6-astra"); + expect(result.model).toBe(model); expect(result.fastModeApplied).toBe(true); expect(result.fastModeIgnoredReason).toBeNull(); expect(result.args).toEqual([ @@ -24,7 +24,7 @@ describe("buildCodexExecArgs", () => { "--json", "--dangerously-bypass-approvals-and-sandbox", "--model", - "gpt-6-astra", + model, "-c", 'model_reasoning_effort="ultra"', "-c", @@ -148,7 +148,7 @@ describe("buildCodexExecArgs", () => { expect(result.fastModeRequested).toBe(true); expect(result.fastModeApplied).toBe(false); expect(result.fastModeIgnoredReason).toContain( - "currently only supported on gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 or manually configured model IDs", + "currently only supported on gpt-6-astra, gpt-6-sol, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 or manually configured model IDs", ); expect(result.args).toEqual([ "exec", diff --git a/packages/adapters/codex-local/src/ui/build-config.test.ts b/packages/adapters/codex-local/src/ui/build-config.test.ts index d8736bd94d4..df998dd1301 100644 --- a/packages/adapters/codex-local/src/ui/build-config.test.ts +++ b/packages/adapters/codex-local/src/ui/build-config.test.ts @@ -63,17 +63,17 @@ describe("buildCodexLocalConfig", () => { }); }); - it("persists the exact GPT-6 Astra model and supported controls", () => { + it.each(["gpt-6-astra", "gpt-6-sol"])("persists the exact %s model and supported controls", (model) => { const config = buildCodexLocalConfig( makeValues({ - model: "gpt-6-astra", + model, thinkingEffort: "ultra", fastMode: true, }), ); expect(config).toMatchObject({ - model: "gpt-6-astra", + model, modelReasoningEffort: "ultra", fastMode: true, }); diff --git a/ui/src/components/agent-config-primitives.tsx b/ui/src/components/agent-config-primitives.tsx index 118a53dd751..6748939001c 100644 --- a/ui/src/components/agent-config-primitives.tsx +++ b/ui/src/components/agent-config-primitives.tsx @@ -34,7 +34,7 @@ export const help: Record = { dangerouslySkipPermissions: "Run unattended by auto-approving adapter permission prompts when supported.", dangerouslyBypassSandbox: "Run Codex without sandbox restrictions. Required for filesystem/network access.", search: "Enable Codex web search capability during runs.", - fastMode: "Enable Codex Fast mode. This burns credits/tokens much faster and is supported on GPT-6 Astra, GPT-5.6, GPT-5.5, GPT-5.4, and manual Codex model IDs.", + fastMode: "Enable Codex Fast mode. This burns credits/tokens much faster and is supported on GPT-6 Astra and Sol, GPT-5.6, GPT-5.5, GPT-5.4, and manual Codex model IDs.", workspaceStrategy: "How Paperclip should realize an execution workspace for this agent. Keep project_primary for normal cwd execution, or use git_worktree for issue-scoped isolated checkouts.", workspaceBaseRef: "Base git ref used when creating a worktree branch. Leave blank to use the resolved workspace ref or HEAD.", workspaceBranchTemplate: "Template for naming derived branches. Supports {{issue.identifier}}, {{issue.title}}, {{agent.name}}, {{project.id}}, {{workspace.repoRef}}, and {{slug}}.", diff --git a/ui/src/lib/codex-reasoning-effort.test.ts b/ui/src/lib/codex-reasoning-effort.test.ts index 293d6fe5d37..233eca07137 100644 --- a/ui/src/lib/codex-reasoning-effort.test.ts +++ b/ui/src/lib/codex-reasoning-effort.test.ts @@ -4,8 +4,8 @@ import { describe, expect, it } from "vitest"; import { codexReasoningEffortOptions } from "./codex-reasoning-effort"; describe("codexReasoningEffortOptions", () => { - it("exposes only the supported GPT-6 Astra reasoning efforts", () => { - expect(codexReasoningEffortOptions("gpt-6-astra")).toEqual([ + it.each(["gpt-6-astra", "gpt-6-sol"])("exposes only the supported %s reasoning efforts", (model) => { + expect(codexReasoningEffortOptions(model)).toEqual([ { value: "", label: "Default" }, { value: "low", label: "Low" }, { value: "medium", label: "Medium" },