diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index cbafb2d1d1a..e8f0d5a9425 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -4,7 +4,23 @@ export const DEFAULT_CODEX_LOCAL_MODEL = "gpt-5.3-codex"; export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true; export const CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS = ["gpt-5.4"] as const; +function normalizeModelId(model: string | null | undefined): string { + return typeof model === "string" ? model.trim() : ""; +} + +export function isCodexLocalKnownModel(model: string | null | undefined): boolean { + const normalizedModel = normalizeModelId(model); + if (!normalizedModel) return false; + return models.some((entry) => entry.id === normalizedModel); +} + +export function isCodexLocalManualModel(model: string | null | undefined): boolean { + const normalizedModel = normalizeModelId(model); + return Boolean(normalizedModel) && !isCodexLocalKnownModel(normalizedModel); +} + export function isCodexLocalFastModeSupported(model: string | null | undefined): boolean { + if (isCodexLocalManualModel(model)) return true; const normalizedModel = typeof model === "string" ? model.trim() : ""; return CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS.includes( normalizedModel as (typeof CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS)[number], @@ -35,7 +51,7 @@ Core fields: - modelReasoningEffort (string, optional): reasoning effort override (minimal|low|medium|high|xhigh) passed via -c model_reasoning_effort=... - promptTemplate (string, optional): run prompt template - search (boolean, optional): run codex with --search -- fastMode (boolean, optional): enable Codex Fast mode; currently supported on GPT-5.4 only and consumes credits faster +- fastMode (boolean, optional): enable Codex Fast mode; supported on 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 @@ -54,6 +70,6 @@ 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. In managed-home mode (the default) this is ~/.paperclip/instances//companies//codex-home/skills/; when CODEX_HOME is explicitly overridden in adapter config, that override is used instead. - Unless explicitly overridden in adapter config, Paperclip runs Codex with a per-company managed CODEX_HOME under the active Paperclip instance and seeds auth/config from the shared Codex home (the CODEX_HOME env var, when set, or ~/.codex). - Some model/tool combinations reject certain effort levels (for example minimal with web search enabled). -- Fast mode is currently supported on GPT-5.4 only. When enabled, Paperclip applies \`service_tier="fast"\` and \`features.fast_mode=true\`. +- Fast mode is supported on 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. `; 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 c291ae53b79..7b4eaa0013d 100644 --- a/packages/adapters/codex-local/src/server/codex-args.test.ts +++ b/packages/adapters/codex-local/src/server/codex-args.test.ts @@ -26,6 +26,28 @@ describe("buildCodexExecArgs", () => { ]); }); + it("enables Codex fast mode overrides for manual models", () => { + const result = buildCodexExecArgs({ + model: "gpt-5.5", + fastMode: true, + }); + + expect(result.fastModeRequested).toBe(true); + expect(result.fastModeApplied).toBe(true); + expect(result.fastModeIgnoredReason).toBeNull(); + expect(result.args).toEqual([ + "exec", + "--json", + "--model", + "gpt-5.5", + "-c", + 'service_tier="fast"', + "-c", + "features.fast_mode=true", + "-", + ]); + }); + it("ignores fast mode for unsupported models", () => { const result = buildCodexExecArgs({ model: "gpt-5.3-codex", @@ -34,7 +56,9 @@ describe("buildCodexExecArgs", () => { expect(result.fastModeRequested).toBe(true); expect(result.fastModeApplied).toBe(false); - expect(result.fastModeIgnoredReason).toContain("currently only supported on gpt-5.4"); + expect(result.fastModeIgnoredReason).toContain( + "currently only supported on gpt-5.4 or manually configured model IDs", + ); expect(result.args).toEqual([ "exec", "--json", diff --git a/packages/adapters/codex-local/src/server/codex-args.ts b/packages/adapters/codex-local/src/server/codex-args.ts index 76756814182..f8081aad6fb 100644 --- a/packages/adapters/codex-local/src/server/codex-args.ts +++ b/packages/adapters/codex-local/src/server/codex-args.ts @@ -25,7 +25,7 @@ function asRecord(value: unknown): Record { } function formatFastModeSupportedModels(): string { - return CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS.join(", "); + return `${CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS.join(", ")} or manually configured model IDs`; } export function buildCodexExecArgs( diff --git a/packages/adapters/codex-local/src/server/test.ts b/packages/adapters/codex-local/src/server/test.ts index f19ed0078cc..5e4a0656dfc 100644 --- a/packages/adapters/codex-local/src/server/test.ts +++ b/packages/adapters/codex-local/src/server/test.ts @@ -146,7 +146,7 @@ export async function testEnvironment( code: "codex_fast_mode_unsupported_model", level: "warn", message: execArgs.fastModeIgnoredReason, - hint: "Switch the agent model to GPT-5.4 to enable Codex Fast mode.", + hint: "Switch the agent model to GPT-5.4 or enter a manual model ID to enable Codex Fast mode.", }); } diff --git a/ui/src/adapters/codex-local/config-fields.tsx b/ui/src/adapters/codex-local/config-fields.tsx index b1a24bb9244..3731edc2408 100644 --- a/ui/src/adapters/codex-local/config-fields.tsx +++ b/ui/src/adapters/codex-local/config-fields.tsx @@ -10,6 +10,7 @@ import { LocalWorkspaceRuntimeFields } from "../local-workspace-runtime-fields"; import { CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS, isCodexLocalFastModeSupported, + isCodexLocalManualModel, } from "@paperclipai/adapter-codex-local"; const inputClass = @@ -37,8 +38,14 @@ export function CodexLocalConfigFields({ const currentModel = isCreate ? String(values!.model ?? "") : eff("adapterConfig", "model", String(config.model ?? "")); + const fastModeManualModel = isCodexLocalManualModel(currentModel); const fastModeSupported = isCodexLocalFastModeSupported(currentModel); const supportedModelsLabel = CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS.join(", "); + const fastModeMessage = fastModeManualModel + ? "Fast mode will be passed through for this manual model. If Codex rejects it, turn the toggle off." + : fastModeSupported + ? "Fast mode consumes credits/tokens much faster than standard Codex runs." + : `Fast mode currently only works on ${supportedModelsLabel} or manual model IDs. Paperclip will ignore this toggle until the model is switched.`; return ( <> @@ -112,9 +119,7 @@ export function CodexLocalConfigFields({ /> {fastModeEnabled && (
- {fastModeSupported - ? "Fast mode consumes credits/tokens much faster than standard Codex runs." - : `Fast mode currently only works on ${supportedModelsLabel}. Paperclip will ignore this toggle until the model is switched.`} + {fastModeMessage}
)} = { 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 currently supported on GPT-5.4 only.", + fastMode: "Enable Codex Fast mode. This burns credits/tokens much faster and is supported on 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}}.",