mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 14:19:00 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Each agent runs on an adapter, and the operator picks the agent's model from the list the adapter advertises. > - The `codex_local` adapter advertises a static, curated list. The server returns that list as written, but the list itself is not in a useful order: the default `gpt-5.6-sol` sits above the whole GPT-6 family (#14878). > - This pull request orders the list the way the ChatGPT app orders Codex models: newest model version first, then by decreasing capability inside each version, with older models at the end. > - It is stacked on #14917, which makes the model dropdown show a hand-ordered list as the adapter advertises it. Without that change the picker shows every list alphabetically. > - The benefit is that a user who knows Codex finds the right model at once, and older models sit at the end of the list. ## Linked Issues or Issue Description Fixes #14878. Related: #14917 (the Claude counterpart, #14877) carries the dropdown change this PR relies on. This PR contains that commit until #14917 lands; after that it rebases to the Codex commit alone. ## What Changed - `packages/adapters/codex-local/src/index.ts`: reorder the advertised `models` list. GPT-6 (`astra`, `sol`, `luna`) first, then GPT-5.6 (`sol`, `terra`, `luna`), then `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, then the o-series and `codex-mini-latest` in their existing relative order. `DEFAULT_CODEX_LOCAL_MODEL` is unchanged. - `packages/adapters/codex-local/src/index.test.ts`: the metadata test now asserts the full order of the GPT entries. The ChatGPT app also lists GPT-6.1 Sol first. That model is not in Paperclip's list today. Adding a model needs reasoning-effort and fast-mode entries as well, so it is out of scope for an ordering fix. ## Verification Run from the repository root: ```sh pnpm exec vitest run packages/adapters/codex-local server/src/__tests__/adapter-models.test.ts pnpm --filter @paperclipai/adapter-codex-local typecheck ``` Red on `master`, green here: the updated metadata test fails against the unmodified list because `gpt-5.6-sol` comes first. Manual check: open an agent that uses `codex_local` and open the model dropdown. The list reads gpt-6-astra, gpt-6-sol, gpt-6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5, gpt-5-mini, gpt-5-nano, o3, o4-mini, o3-mini, Codex Mini. ## Risks - Low risk. The list content and the default model do not change; only the order does. The server returns this list as-is for the built-in Codex adapter and the server test compares against the exported list, so no server change is needed. - The first entry is no longer the default model. Nothing reads the first entry as a default: `DEFAULT_CODEX_LOCAL_MODEL` is resolved separately. ## Model Used Anthropic Claude Fable 5.1 (`claude-fable-5-1`) through Claude Code, extended thinking on, with tool use for reading the repository, running vitest and tsc, and editing files. The account holder reviewed the change and owns the commit. ## 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 --------- Co-authored-by: abderbj <115119179+abderbj@users.noreply.github.com>
181 lines
12 KiB
TypeScript
181 lines
12 KiB
TypeScript
import {
|
|
PAPERCLIP_RUNNER_DEFAULT_MODELS,
|
|
} from "@paperclipai/adapter-utils";
|
|
|
|
export const type = "codex_local";
|
|
export const label = "Codex";
|
|
|
|
export const SANDBOX_INSTALL_COMMAND = "npm install -g @openai/codex";
|
|
|
|
// Use the concrete `gpt-5.6-sol` slug (Codex's own default for the 5.6 family) rather than the
|
|
// bare `gpt-5.6` alias: OpenAI ships no model metadata for the bare slug, so passing it makes the
|
|
// Codex CLI warn ("Model metadata for `gpt-5.6` not found") and fall back to generic context limits.
|
|
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-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-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;
|
|
}
|
|
}
|
|
|
|
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-astra", label: "gpt-6-astra" },
|
|
{ id: "gpt-6-sol", label: "gpt-6-sol" },
|
|
{ id: "gpt-6-luna", label: "gpt-6-luna" },
|
|
// DEFAULT_CODEX_LOCAL_MODEL is gpt-5.6-sol, the first 5.6 entry.
|
|
{ id: DEFAULT_CODEX_LOCAL_MODEL, label: DEFAULT_CODEX_LOCAL_MODEL },
|
|
{ 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
|
|
|
|
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/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-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).
|
|
- Fast mode is supported on GPT-6 (astra/sol/luna), 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.
|
|
`;
|