mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 01:50:13 +01:00
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Codex agents run through Paperclip Runner or the legacy
`codex_local` harness, and the runner E2E suites run paid Codex cells
> - The shared Codex default and many selections preferred `gpt-5.6-sol`
> - The reviewed OpenAI rate card in
`server/src/services/codex-pricing.ts` (`openai-standard-2026-09-30`)
prices `gpt-6.1-sol` at $2 input / $0.10 cached / $10 output per 1M
tokens, and `gpt-5.6-sol` at $4 / $0.40 / $20
> - So every default run and every paid Codex eval cell cost twice as
much as necessary
> - This pull request moves every place that prefers or selects
`gpt-5.6-sol` to `gpt-6.1-sol`, and keeps historical facts, rate cards
and compatibility fallbacks on `gpt-5.6-sol`
> - The benefit is half the token price for new default Codex agents and
for the Codex E2E and TestOps cells
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The default Codex model for new agents and the Codex model that the
runner E2E suites and TestOps plans select.
**Subsystem affected**
Adapters (`adapter-utils`, `codex-local`), server native runtime
fallback, UI AI connection pools, Paperclip Runner devtools and live
evals, runner E2E catalog and TestOps mappings.
**Current behavior**
New Codex agents without an explicit model store `gpt-5.6-sol`. The
runner E2E `runner-codex` profile, the Reliability `queue-steer` plan
cell, the Product regression TestOps mapping and the ACPX Codex
qualification selection all use `gpt-5.6-sol`.
**Proposed behavior**
They use `gpt-6.1-sol`. Existing agents that store a model keep it.
`gpt-5.6-sol` stays selectable.
**Reason and benefit**
`gpt-6.1-sol` costs half as much per token on the in-repo reviewed rate
card. The current OpenAI pricing page (retrieved 2026-11-06) shows the
same short-context rates and also lists long-context rates of $4 / $0.20
/ $15 for `gpt-6.1-sol` against $8 / $0.80 / $30 for `gpt-5.6-sol`.
**Breaking changes**
None for stored configurations. See Risks.
## What Changed
Changed defaults and selections (all now `gpt-6.1-sol`):
- `PAPERCLIP_RUNNER_DEFAULT_MODELS.codex` in
`packages/adapter-utils/src/paperclip-runner-permissions.ts`. This also
changes `DEFAULT_CODEX_LOCAL_MODEL`,
`resolvePaperclipRunnerModel("codex", …)`, runner config normalization,
`codex_local` → Paperclip Runner conversion, the UI new-agent and
agent-config defaults, the composer default model and the public MCP
effort lookup.
- `codex-local` `models`: the 5.6 entry is now the literal
`gpt-5.6-sol`, because it used to be `DEFAULT_CODEX_LOCAL_MODEL`. The
list order and contents do not change.
- AI connection pool: a new OpenAI member profile uses
`PAPERCLIP_RUNNER_DEFAULT_MODELS.codex` instead of a hard-coded
`gpt-5.6-sol`.
- Paperclip Runner devtools ACPX Codex default and preset, the
`runner-codex-sol` local provider smoke, and the `acpx-codex-sol` live
workflow candidate.
- `tests/acpx-qualification-models.ts` Codex selection (runner E2E and
runner acceptance ACPX Codex profiles).
- Runner E2E: the `runner-codex` profile follows the default, so all
native Codex cells move. This includes the `runner-codex` cells among
the 52 standard `everyday-workflows` cells and the seven Codex
reliability cells. `runner-codex-mini` stays on `gpt-5.4-mini`. The
Reliability plan `queue-steer` `steeringProfile` is now
`['runner-codex', 'gpt-6.1-sol']`. Its executionId
(`everyday-workflows.runner-codex.daytona.queue-steer`), its fullName
and its TestOps case identity do not change. No separate profile is
necessary.
- `product-regression-testops.mjs`: the declared model for every
`runner-codex` case is now `gpt-6.1-sol` (it was already that for
`configured-repository`).
- `tests/runner-e2e/installed-cli.ts`: the credential-free installed
startup proof opens a session on the default model.
- `tests/runner-e2e/scheduled-prices.json`: add a `gpt-6.1-sol` price
(`openai-standard-2026-11-06`, including 30m/12h cache writes and
long-context rates from the OpenAI pricing page). The weekly scheduled
Codex cells need an exact price for their model, or admission fails
closed.
- Storybook: the composer model picker fixtures, the connection pool
stories and host, and the agent settings preview show `gpt-6.1-sol` as
the default.
- Docs: `doc/DEVELOPING.md` (the Codex conversion default) and
`tests/runner-e2e/EVERYDAY-WORKFLOWS.md` (local matrix, reliability
extras, `queue-steer` row).
- Tests that assert the default now expect `gpt-6.1-sol`. New
assertions: the default has the `0.159.0` CLI floor, the compatibility
fallback is floor-free, the `queue-steer` plan cell runs `runner-codex`
on `gpt-6.1-sol`, and all native Codex everyday cells (standard and
reliability) use `gpt-6.1-sol`.
Deliberately kept on `gpt-5.6-sol`:
- Rate cards: `server/src/services/codex-pricing.ts` and
`packages/paperclip-runner/src/evals/model-pricing.ts`. Old receipts
need them. (The runner eval rate card has no `gpt-6.1-sol` entry. An
unpriced candidate is reported as unpriced, not as zero. I did not add a
rate there because that card has its own version and source.)
- The remote Codex CLI compatibility fallback. A new
`CODEX_LOCAL_COMPATIBILITY_FALLBACK_MODEL = "gpt-5.6-sol"` replaces the
default in `codexModelFallbackCandidates`. `gpt-6.1-sol` needs Codex
0.159.0 or newer, so it cannot be the floor-free last resort. Fallback
behavior is unchanged.
- `tests/runner-e2e/provider-usage.ts`: the implicit request
cache-policy attribution stays qualified only for `gpt-5.6-sol`. I did
not verify that policy for `gpt-6.1-sol`. A `gpt-6.1-sol` response that
omits the write-TTL split keeps its cache-write cost unknown, which is
the existing behavior for unqualified models.
- `tests/runner-e2e/scheduled-measurements.json`: historical
`gpt-5.6-sol` measurements. They no longer match the current suite
definitions, so the forecast reports them as historical only. The
forecast tests now build their synthetic cohorts on the current
definition hash.
- Model catalogs that list `gpt-5.6-sol` as available (`codex-local`,
`cursor-local`, `opencode-local`, Speko voice diagnostics, Pi picker
fixture).
- The `gpt-5.6` → `gpt-5.6-sol` alias for old stored configs.
- Dated or evidence docs: `doc/plans/*`, `doc/adapter-model-audit-*`,
`QUALIFICATION-*`, `doc/connections/MEMORY.md`,
`doc/connections/TASK-REVIEWS-VERIFICATION.md`,
`tests/runner-e2e/CHAT-FEATURES.md`, the Slack eval evidence, and the
S49 soak signature text (the soak failure happened on `gpt-5.6-sol`).
- Test fixtures that parse, price, migrate or round-trip an existing
`gpt-5.6-sol` value (Rust runner tests, driver tests, pricing tests,
stored-config tests, stories that show an existing agent).
## Verification
- `npx vitest run` on every server, UI, adapter-utils and codex-local
test file that mentions `gpt-5.6-sol` or the default, plus all of
`packages/adapter-utils` and `packages/adapters/codex-local`: 5,483
passed. Three timeouts under load (`sandbox-managed-runtime`,
`acp.quota`) passed when rerun.
- `packages/paperclip-runner`: `vitest run` for `src/eval`, `src/evals`,
`src/drivers/acpx`, `src/drivers/codex`, `src/cli`, `src/contracts`,
`src/backends`: 2,641 passed.
- `node --test tests/runner-e2e/reliability-testops.test.mjs
tests/runner-e2e/product-regression-testops.test.mjs`: 28 passed.
- `node tests/runner-e2e/run-unit.mjs`: all model, catalog, reliability,
scheduled and TestOps tests pass. Failures remain only in
`copilot-protection-flow`, `denied-target-isolation-flow`,
`pi-controls-flow`, `workflow-target` and `unit-launcher`. These five
also fail on unmodified master in this local environment (filesystem
watch and git environment), and they do not touch models.
- `vitest run --config tests/runner-acceptance/vitest.config.ts`: 19
passed.
- `pnpm test:e2e:runner -- --list --suite everyday-workflows --group
reliability`: the seven `everyday-workflows.runner-codex.daytona.<case>`
cells show `gpt-6.1-sol` with `OPENAI_API_KEY,DAYTONA_API_KEY`.
`--matrix-json` gives 16 reliability cells with the same credentials as
before. `--list --all` shows no `gpt-5.6-sol` cell.
- Typecheck: `adapter-utils`, `adapter-codex-local`, `server`, `ui`,
`paperclip-runner` (TypeScript), and `pnpm test:e2e:runner:typecheck`.
- `pnpm check:token-gates`: clean.
- Not run: paid cells, Rust tests (no Rust change), the browser suites,
and the full `pnpm -r typecheck` / `pnpm build` (CI runs them).
## Risks
- Agents without an explicit model change model on their next save or
normalization, and Paperclip Runner rows with a blank model use
`gpt-6.1-sol` at execution. Stored models, including `gpt-5.6-sol`, are
never rewritten. No migration is necessary.
- `gpt-6.1-sol` needs Codex CLI 0.159.0 or newer with ChatGPT sign-in.
The runner pins `@openai/codex@0.160.0`. On a remote target with an
older image CLI, the existing compatibility check falls back to
`gpt-6-sol` and then `gpt-5.6-sol`, and that probe now runs for
default-model agents too. A legacy `codex_local` agent created with the
new default on a host with an older Codex CLI gets the existing
actionable "upgrade Codex" Test result.
- Qualification: the native Codex profile uses `adapter_constant` model
qualification, so it follows the default. No qualification gate is
dropped. The ACPX Codex qualification selection moves to `gpt-6.1-sol`.
Its next qualification run uses the new model. The provider-usage
cache-policy attribution stays limited to `gpt-5.6-sol`.
- Runner E2E history: the Codex cells change their suite definition
hashes, so earlier `gpt-5.6-sol` results are a separate cohort and do
not count as current.
- The `gpt-6.1-sol` scheduled price uses the file's existing `expiresAt`
(2026-11-19). It needs the same review as the other entries before then.
- No `.github/` change. The Reliability queue-steer cell keeps the
`runner-codex` profile id, so the existing workflow credential and setup
steps still match.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, running in
Claude Code with tool use (shell, file edits, web fetch of the OpenAI
pricing page).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Related: #19604 (commits the Reliability TestOps identities; this PR
changes only `steeringProfile` in the same file).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
297 lines
19 KiB
TypeScript
297 lines
19 KiB
TypeScript
import {
|
|
PAPERCLIP_RUNNER_DEFAULT_MODELS,
|
|
} from "@paperclipai/adapter-utils";
|
|
import { PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION } from "@paperclipai/shared";
|
|
|
|
export const type = "codex_local";
|
|
export const label = "Codex";
|
|
|
|
export const SANDBOX_INSTALL_COMMAND = "npm install -g @openai/codex";
|
|
|
|
// The default is `gpt-6.1-sol`: the reviewed OpenAI rate card in server/src/services/codex-pricing.ts
|
|
// prices it at half of `gpt-5.6-sol`. It applies only to agents without an explicit model; a stored
|
|
// model is never rewritten. Always use a concrete slug rather than a bare family alias such as
|
|
// `gpt-5.6`: OpenAI ships no model metadata for bare slugs, so the Codex CLI warns and falls back to
|
|
// generic context limits.
|
|
export const DEFAULT_CODEX_LOCAL_MODEL = PAPERCLIP_RUNNER_DEFAULT_MODELS.codex;
|
|
// The floor-free model a remote Codex CLI too old for the requested model falls back to. It stays
|
|
// `gpt-5.6-sol` because the default `gpt-6.1-sol` itself needs Codex 0.159.0 or newer.
|
|
export const CODEX_LOCAL_COMPATIBILITY_FALLBACK_MODEL = "gpt-5.6-sol";
|
|
export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true;
|
|
|
|
export function resolveCodexLocalCliPolicy(config: unknown, options: { networkAccess?: boolean } = {}) {
|
|
const record = typeof config === "object" && config !== null && !Array.isArray(config)
|
|
? config as Record<string, unknown> : {};
|
|
const stringArgs = (value: unknown): string[] => Array.isArray(value)
|
|
? value.filter((item): item is string => typeof item === "string") : [];
|
|
const configuredExtraArgs = stringArgs(record.extraArgs);
|
|
const extraArgs = configuredExtraArgs.length > 0 ? configuredExtraArgs : stringArgs(record.args);
|
|
const explicitSandbox = extraArgs.some((arg) =>
|
|
/^(--sandbox(?:=|$)|-s|--profile(?:=|$)|-p|--full-auto$|--yolo$|--dangerously-bypass-approvals-and-sandbox$)/.test(arg)
|
|
|| /^(?:(?:--config=|-c=?)\s*)?(?:sandbox_mode|profile)\s*=/.test(arg),
|
|
);
|
|
const explicitPermissionRestriction = extraArgs.some((arg) =>
|
|
/^(?:(?:--config=|-c=?)\s*)?(?:approval_policy\s*=|sandbox_workspace_write\.network_access\s*=\s*false)/.test(arg),
|
|
);
|
|
const defaultBypass = !explicitSandbox && !explicitPermissionRestriction
|
|
&& options.networkAccess !== false && DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX;
|
|
const bypass = typeof record.dangerouslyBypassApprovalsAndSandbox === "boolean"
|
|
? record.dangerouslyBypassApprovalsAndSandbox
|
|
: typeof record.dangerouslyBypassSandbox === "boolean" ? record.dangerouslyBypassSandbox : defaultBypass;
|
|
return { extraArgs, explicitSandbox, bypass };
|
|
}
|
|
|
|
export const CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS = [
|
|
"gpt-6-astra",
|
|
"gpt-6.1-sol",
|
|
"gpt-6-sol",
|
|
"gpt-6-luna",
|
|
"gpt-5.6-sol",
|
|
"gpt-5.6-terra",
|
|
"gpt-5.6-luna",
|
|
"gpt-5.5",
|
|
"gpt-5.4",
|
|
] as const;
|
|
|
|
function normalizeModelId(model: string | null | undefined): string {
|
|
return typeof model === "string" ? model.trim() : "";
|
|
}
|
|
|
|
// Legacy model aliases with no OpenAI-published metadata are rewritten to the concrete slug the
|
|
// Codex CLI actually knows. Without this, agents still configured with the bare `gpt-5.6` alias
|
|
// (the old default) keep triggering "Model metadata for `gpt-5.6` not found" warnings and Codex
|
|
// falls back to generic context-window limits, even on a Codex build that ships the 5.6 family.
|
|
const CODEX_LOCAL_MODEL_ALIASES: Readonly<Record<string, string>> = {
|
|
"gpt-5.6": "gpt-5.6-sol",
|
|
};
|
|
|
|
const CODEX_LOCAL_DEFAULT_REASONING_EFFORTS = [
|
|
"minimal",
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
] as const;
|
|
|
|
const CODEX_LOCAL_MAX_REASONING_EFFORTS = [
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
"max",
|
|
] as const;
|
|
|
|
const CODEX_LOCAL_ULTRA_REASONING_EFFORTS = [
|
|
...CODEX_LOCAL_MAX_REASONING_EFFORTS,
|
|
"ultra",
|
|
] as const;
|
|
|
|
export type CodexLocalReasoningEffort =
|
|
| (typeof CODEX_LOCAL_DEFAULT_REASONING_EFFORTS)[number]
|
|
| (typeof CODEX_LOCAL_ULTRA_REASONING_EFFORTS)[number];
|
|
|
|
export function normalizeCodexModel(model: string | null | undefined): string {
|
|
const normalizedModel = normalizeModelId(model);
|
|
return CODEX_LOCAL_MODEL_ALIASES[normalizedModel] ?? normalizedModel;
|
|
}
|
|
|
|
export function codexLocalReasoningEffortsForModel(
|
|
model: string | null | undefined,
|
|
): readonly CodexLocalReasoningEffort[] {
|
|
const normalizedModel = normalizeCodexModel(model);
|
|
switch (normalizedModel) {
|
|
case "gpt-6-astra":
|
|
case "gpt-6.1-sol":
|
|
case "gpt-6-sol":
|
|
case "gpt-5.6-sol":
|
|
case "gpt-5.6-terra":
|
|
return CODEX_LOCAL_ULTRA_REASONING_EFFORTS;
|
|
case "gpt-6-luna":
|
|
case "gpt-5.6-luna":
|
|
return CODEX_LOCAL_MAX_REASONING_EFFORTS;
|
|
default:
|
|
return CODEX_LOCAL_DEFAULT_REASONING_EFFORTS;
|
|
}
|
|
}
|
|
|
|
// The Codex backend accepts a model for ChatGPT sign-in only from a client that identifies as a
|
|
// recent enough CLI (the request carries the CLI version). An older `codex` fails every turn with
|
|
// "The '<model>' model is not supported when using Codex with a ChatGPT account.", which reads
|
|
// like an account problem when it is really a stale install, typically a sandbox image baked
|
|
// before the catalog gained the model. Record the oldest stable release verified to work so the
|
|
// Test and the remote runner can name the actual gap. Omit models with no verified floor; the
|
|
// install pin in the runner and images is always at or above every floor listed here.
|
|
const CODEX_LOCAL_MODEL_MINIMUM_CLI_VERSIONS: Readonly<Record<string, string>> = {
|
|
// 0.156.1 and older are rejected (openai/codex#49396); 0.159.0 and 0.159.2 are accepted
|
|
// (openai/codex#49464). No stable release between those was verified either way.
|
|
"gpt-6.1-sol": "0.159.0",
|
|
// Codex 0.157.0 release notes: "Add GPT-6 Sol and Luna to the model catalog".
|
|
"gpt-6-sol": "0.157.0",
|
|
"gpt-6-luna": "0.157.0",
|
|
};
|
|
|
|
export function minimumCodexCliVersionForModel(model: string | null | undefined): string | null {
|
|
return CODEX_LOCAL_MODEL_MINIMUM_CLI_VERSIONS[normalizeCodexModel(model)] ?? null;
|
|
}
|
|
|
|
export interface CodexChatGptRetirement {
|
|
retiredOn: string;
|
|
replacement: string;
|
|
}
|
|
|
|
// Models OpenAI retired from Codex with ChatGPT sign-in (https://learn.chatgpt.com/docs/models).
|
|
// The OpenAI API and API-key Codex sessions still serve them, so they stay in the picker; the
|
|
// Test names the retirement instead of suggesting a stale CLI or a plan restriction.
|
|
const CODEX_LOCAL_CHATGPT_RETIRED_MODELS: Readonly<Record<string, CodexChatGptRetirement>> = {
|
|
"gpt-5.5": { retiredOn: "2026-10-14", replacement: "gpt-6.1-sol (gpt-6-luna on Free and Go plans)" },
|
|
"gpt-5.4": { retiredOn: "2026-08-31", replacement: "gpt-6-sol" },
|
|
"gpt-5.4-mini": { retiredOn: "2026-08-31", replacement: "gpt-6-luna" },
|
|
};
|
|
|
|
export function codexChatGptRetirementForModel(model: string | null | undefined): CodexChatGptRetirement | null {
|
|
return CODEX_LOCAL_CHATGPT_RETIRED_MODELS[normalizeCodexModel(model)] ?? null;
|
|
}
|
|
|
|
/** Test hint for a ChatGPT-account rejection of a retired model; shared by the legacy and native Tests. */
|
|
export function codexChatGptRetirementHint(model: string | null | undefined): string | null {
|
|
const retirement = codexChatGptRetirementForModel(model);
|
|
if (!retirement) return null;
|
|
return `OpenAI retired ${normalizeCodexModel(model)} from Codex with ChatGPT sign-in on ${retirement.retiredOn}. Choose ${retirement.replacement}, or use an OpenAI API key; API-key sessions are not affected.`;
|
|
}
|
|
|
|
const STABLE_CODEX_CLI_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
|
|
function stableCodexCliVersionParts(version: string): number[] | null {
|
|
const match = STABLE_CODEX_CLI_VERSION.exec(version.trim());
|
|
if (!match) return null;
|
|
const parts = match.slice(1).map(Number);
|
|
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
}
|
|
|
|
/** Read the version from `codex --version` output; prerelease or ambiguous output yields null. */
|
|
export function parseCodexCliVersionOutput(output: string): string | null {
|
|
const versions = output
|
|
.split(/\r?\n/)
|
|
.map((line) => /^codex-cli (\S+)$/.exec(line.trim())?.[1])
|
|
.filter((version): version is string => version !== undefined);
|
|
if (versions.length !== 1 || !stableCodexCliVersionParts(versions[0]!)) return null;
|
|
return versions[0]!;
|
|
}
|
|
|
|
export function codexCliVersionAtLeast(version: string, minimum: string): boolean {
|
|
const parsedVersion = stableCodexCliVersionParts(version);
|
|
const parsedMinimum = stableCodexCliVersionParts(minimum);
|
|
if (!parsedVersion || !parsedMinimum) return false;
|
|
for (let index = 0; index < parsedMinimum.length; index += 1) {
|
|
if (parsedVersion[index] !== parsedMinimum[index]) {
|
|
return parsedVersion[index]! > parsedMinimum[index]!;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export const CODEX_CHATGPT_MODEL_REJECTION_RE =
|
|
/The '([^']+)' model is not supported when using Codex with a ChatGPT account\./;
|
|
|
|
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() : "";
|
|
// Empty means we're omitting --model so the Codex CLI picks its own default.
|
|
// Manual model IDs are also treated as supported: pass the fast-mode overrides
|
|
// through and let the CLI reject them if the chosen model can't use them.
|
|
if (!normalizedModel) return true;
|
|
return CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS.includes(
|
|
normalizedModel as (typeof CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS)[number],
|
|
);
|
|
}
|
|
|
|
// Ordered the way the ChatGPT app orders Codex models (#14878): newest model version first,
|
|
// then by decreasing capability inside each version, older models at the end. The server
|
|
// returns this list as written and the model dropdown keeps its order.
|
|
export const models = [
|
|
{ id: "gpt-6.1-sol", label: "gpt-6.1-sol" },
|
|
{ id: "gpt-6-astra", label: "gpt-6-astra" },
|
|
{ id: "gpt-6-sol", label: "gpt-6-sol" },
|
|
{ id: "gpt-6-luna", label: "gpt-6-luna" },
|
|
{ id: "gpt-5.6-sol", label: "gpt-5.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.5", label: "gpt-5.5" },
|
|
{ id: "gpt-5.4", label: "gpt-5.4" },
|
|
{ id: "gpt-5.4-mini", label: "gpt-5.4-mini" },
|
|
{ id: "gpt-5", label: "gpt-5" },
|
|
{ id: "gpt-5-mini", label: "gpt-5-mini" },
|
|
{ id: "gpt-5-nano", label: "gpt-5-nano" },
|
|
{ id: "o3", label: "o3" },
|
|
{ id: "o4-mini", label: "o4-mini" },
|
|
{ id: "o3-mini", label: "o3-mini" },
|
|
{ id: "codex-mini-latest", label: "Codex Mini" },
|
|
];
|
|
|
|
export const agentConfigurationDoc = `# codex_local agent configuration
|
|
|
|
New agents select the Codex harness with adapterType: codex_local and an optional request-level runner: auto, paperclip, or legacy. Automatic selection uses Paperclip Runner on qualified Linux x64/ARM64 targets with GNU libc ${PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION} or newer and macOS arm64/x64 targets. It stores adapterType: paperclip_runner with provider: codex. Windows, 32-bit ARM, Alpine/musl, and older or unknown Linux libc retain automatic legacy selection. Qualification and artifact checks use the selected execution environment, including its architecture, rather than the controller's platform. Other harness defaults are unchanged.
|
|
|
|
Set runner: legacy to retain this adapter's execution and custom settings. Explicit runner: paperclip requires a qualified target. Existing agents and reviewed pending hires retain their saved runner; authentication, model, dependency, and runtime errors never silently switch runners. The fields below describe legacy codex_local execution. Native creation translates supported settings and rejects unsupported settings with the explicit Legacy runner option.
|
|
|
|
Adapter: codex_local
|
|
|
|
Core fields:
|
|
- engine (string, optional): defaults to ACP, including legacy unset/"auto" values. Missing prerequisites and execution failures fail the run without changing engines. Set "cli" to explicitly select the CLI engine.
|
|
- 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, GPT-6.1 Sol, GPT-6 Sol, and GPT-5.6 Sol/Terra support low|medium|high|xhigh|max|ultra; GPT-6 Luna and GPT-5.6 Luna support low|medium|high|xhigh|max
|
|
- 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/sol/luna), GPT-6.1 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
|
|
- env (object, optional): KEY=VALUE environment variables
|
|
- workspaceStrategy (object, optional): execution workspace strategy; currently supports { type: "git_worktree", baseRef?, branchTemplate?, worktreeParentDir? }
|
|
- workspaceRuntime (object, optional): reserved for workspace runtime metadata; workspace runtime services are manually controlled from the workspace UI and are not auto-started by heartbeats
|
|
- filesystemScope (string, optional): set to "workspace" to confine local CLI filesystem access with Bubblewrap. Off by default. The workspace and managed CODEX_HOME remain writable; other host paths are hidden.
|
|
- filesystemExtraPaths (array, optional): additional absolute host paths exposed inside the workspace sandbox. String entries are read-only; object entries use { path: "/absolute/path", access: "ro" | "rw" }.
|
|
- filesystemSandboxCommand (string, optional): Bubblewrap executable name or absolute path; defaults to "bwrap". Linux only.
|
|
- networkScope (string, optional): "deny" blocks all network egress; "allowlist" permits only networkAllowlist targets through Paperclip's HTTP(S) proxy. Off by default.
|
|
- networkAllowlist (string[], optional): exact hostnames, hostname:port entries, or origin URLs. Include the configured Codex provider origin, such as "api.openai.com" or a custom model provider gateway.
|
|
|
|
Operational fields:
|
|
- timeoutSec (number, optional): run timeout in seconds
|
|
- graceSec (number, optional): SIGTERM grace period in seconds
|
|
- outputInactivityTimeoutMs (number | null, optional): inactivity monitor around the codex child. Resets whenever the child emits stdout/stderr bytes or, on Linux, its process group shows meaningful CPU, disk I/O, or child-process churn during a silent build. Defaults to 30 * 60_000 ms when unset or non-positive. Set to \`null\` to disable the monitor entirely (only do this for known-slow tasks; the platform-level 1h silent-run safety net still applies). On fire, the adapter sends SIGTERM to the process group, waits 5s, then SIGKILL, and surfaces the run as failed with errorMessage "monitor: no codex activity (output or process) for {N}m {S}s".
|
|
- agentCommand (string, optional): ACP server command override used only when engine="acp"; defaults to the package-local codex-acp binary
|
|
- mode (string, optional): ACP session mode when engine="acp"; persistent or oneshot
|
|
- nonInteractivePermissions (string, optional): ACP non-interactive permission fallback when engine="acp"; deny or fail
|
|
- stateDir (string, optional): ACP state directory override when engine="acp"
|
|
- warmHandleIdleMs (number, optional): warm ACP process idle timeout when engine="acp"; defaults to 0
|
|
|
|
Notes:
|
|
- filesystemScope and networkScope are spawn-level confinement and are orthogonal to Codex approval/sandbox flags. Both require Bubblewrap on the host and explicit engine="cli"; default or explicit ACP is rejected because ACP confinement is not yet supported. networkScope="allowlist" injects HTTP_PROXY/HTTPS_PROXY for the CLI while its private network namespace blocks direct sockets, so every required provider/API hostname must be listed explicitly.
|
|
- Prompts are piped via stdin (Codex receives "-" prompt argument).
|
|
- If instructionsFilePath is configured, Paperclip prepends that file's contents to the stdin prompt on every run.
|
|
- Codex exec automatically applies repo-scoped AGENTS.md instructions from the active workspace. Paperclip cannot suppress that discovery in exec mode, so repo AGENTS.md files may still apply even when you only configured an explicit instructionsFilePath.
|
|
- 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/<id>/companies/<companyId>/agents/<agentId>/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).
|
|
- OpenAI retired gpt-5.5 (October 14, 2026), gpt-5.4 and gpt-5.4-mini (August 31, 2026) from Codex with ChatGPT sign-in. They remain selectable for OpenAI API-key sessions; with ChatGPT sign-in the Test names the retirement and its replacement.
|
|
- With ChatGPT sign-in, model availability depends on the installed Codex capabilities. Legacy setup reports known model/version mismatches. Native setup tests the actual app-server and selected model; a version number alone does not reject or replace an installed CLI. If the provider rejects the model, select an available model or update Codex in the execution environment.
|
|
- Fast mode is supported on GPT-6 (astra/sol/luna), GPT-6.1 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.
|
|
- Codex ACP is the preferred auto lane when Node >=24.11.0 and the Codex ACP server are available. It reuses shared ACP prompt/runtime guidance, selected skill materialization into CODEX_HOME/skills, model/reasoning/fast-mode session config, and existing quota-window reporting. Missing ACP prerequisites fail both default and explicit ACP runs with an actionable setup error; the adapter never switches engines automatically.
|
|
`;
|