diff --git a/doc/adapter-model-audit-2026-10-23.md b/doc/adapter-model-audit-2026-10-23.md new file mode 100644 index 00000000000..8f63cf14deb --- /dev/null +++ b/doc/adapter-model-audit-2026-10-23.md @@ -0,0 +1,126 @@ +# Adapter model and harness audit: October 23, 2026 + +This audit follows the [October 2, 2026 audit](adapter-model-audit-2026-10-02.md). +It covers model selection in the coding-agent adapters and the CLI pins in the +evaluation sandbox image. Catalog entries identify models; provider accounts +and installed CLIs determine access. No agent defaults or saved model +selections are migrated. + +## Model changes + +| Adapter | Changes from the audit | +| --- | --- | +| Codex | No new selectable model IDs. Codex 0.160.0 through 0.164.0 ship the same bundled model set, which the picker already lists (including GPT-6 Astra, GPT-5.6 Terra, and GPT-5.6 Luna). OpenAI retired `gpt-5.5` (October 14, 2026) and `gpt-5.4` / `gpt-5.4-mini` (August 31, 2026) from Codex with ChatGPT sign-in. The API and API-key Codex sessions still serve them, so they stay in the picker. When the backend rejects one of them for a ChatGPT account, the environment Test (legacy CLI and Paperclip Runner) now names the retirement and the documented replacement instead of suggesting a stale CLI, a plan restriction, or generic account checks. | +| OpenCode | Add `anthropic/claude-haiku-5-5` and `google/gemini-4-argon` to the static fallback used by remote environments and the Paperclip Runner picker. Both IDs are in the OpenCode model registry (models.dev). | +| Claude Code (direct and Bedrock) | Claude Haiku 5.5 is covered by [#16708](https://github.com/paperclipai/paperclip/pull/16708); this audit does not duplicate it. Haiku 5.5 needs Claude Code 2.1.293 or later and supports all five effort levels. | +| Gemini CLI | Gemini 4 Argon (`gemini-4-argon`) is generally available on the Gemini API, but Gemini CLI 0.65.0 does not register it. Deferred. | +| Grok Build, Kimi Code, Cursor | No new verified model IDs. Grok 4.7 and Kimi K3 remain the newest documented models. | + +## Harness changes + +| Harness | Before | After | +| --- | --- | --- | +| Grok CLI (`@xai-official/grok`) | 1.0.46 | 1.0.70 | +| Gemini CLI | 0.62.0 | 0.65.0 | +| Kimi Code CLI | 2.1.1 | 2.3.3 | +| Cursor CLI (sandbox layer) | 2026.10.01-e373342 | 2026.10.22-27dcf5f | +| GitHub CLI | 2.102.0 | 2.103.3 | +| Codex CLI (shared provider pack) | 0.160.0 | Unchanged here; [#17245](https://github.com/paperclipai/paperclip/pull/17245) moves it to 0.163.0 | +| Claude Agent SDK / Claude Code | 0.3.286 / 2.1.286 | Unchanged; see "Deferred items" | +| OpenCode (shared provider pack) | 1.18.34 | Unchanged; see "Deferred items" | +| Hermes | 0.19.0 | 0.19.0 (newest on PyPI) | + +These pins change only the evaluation image's sandbox layer. They must stay +aligned with the fleet sandbox image definition. + +## Sources and verification + +Research date: October 23, 2026. Harness pins rechecked October 26, 2026. + +- [Codex models](https://learn.chatgpt.com/docs/models) states the GPT-5.5 + retirement ("Effective October 14, 2026 ... GPT-5.5 retires from Codex for + users signed in with ChatGPT"), its replacements (GPT-6.1 Sol on paid plans, + GPT-6 Luna on Free and Go), the GPT-5.4 retirement and its replacements + (`gpt-6-sol`, `gpt-6-luna`), and that API-key sessions are not affected. +- The bundled `codex-rs/models-manager/models.json` is unchanged in model set + from [rust-v0.160.0](https://github.com/openai/codex/releases/tag/rust-v0.160.0) + to [rust-v0.164.0](https://github.com/openai/codex/releases/tag/rust-v0.164.0), + the npm `latest` release on October 26, 2026. +- [Claude Haiku 5.5](https://platform.claude.com/docs/en/models/haiku-5-5/overview) + was released October 7, 2026. The + [Claude Code changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) + entry for 2.1.293 adds `claude-haiku-5-5`. +- [models.dev](https://models.dev/api.json) lists `anthropic/claude-haiku-5-5` + and `google/gemini-4-argon`. The OpenCode 1.18.34 executable's bundled + snapshot predates both. Paperclip Runner binds the selected model into the + OpenCode provider configuration, so the snapshot does not limit it. +- [Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon) + is generally available since October 13, 2026. Gemini CLI 0.65.0 + (`packages/core/src/config/models.ts`) has no entry for it, and pull requests + adding it to the CLI are still open. +- Grok CLI 1.0.70, Gemini CLI 0.65.0, and Kimi Code 2.3.3 are the npm `latest` + tags on October 26, 2026. xAI publishes no Grok CLI changelog; `-m`, + `--effort`, and `grok models` are unchanged in the + [CLI reference](https://docs.x.ai/build/cli/reference.md) and in + `grok --help` from 1.0.70. + [Kimi Code 2.2.0 and 2.3.0](https://github.com/MoonshotAI/kimi-code/releases) + remove the `kaos` MCP executor and require qualified plugin skill names; + Paperclip uses neither. Kimi Code 2.3.2 stops the agent from launching + swarms by itself, so `kimi -p` and ACP sessions no longer start swarms; + 2.3.1 and 2.3.3 are fixes and prompt changes. `-m`, `-p`, + `--output-format`, and `acp` are unchanged in `kimi --help` from 2.3.3. + Gemini CLI 0.65.0 makes workspace settings read-only in untrusted folders; + Paperclip writes only home settings and sets `GEMINI_CLI_TRUST_WORKSPACE` + for remote runs. +- Cursor CLI 2026.10.22-27dcf5f is the version `https://cursor.com/install` + resolves. The archive SHA-256 was computed from the versioned Linux x64 + tarball, and the extracted executable reports that version. +- [GitHub CLI 2.103.3](https://github.com/cli/cli/releases/tag/v2.103.3): the + pinned digest matches the release `checksums.txt`. 2.103.1 through 2.103.3 + are fixes on top of 2.103.0. +- [Hermes on PyPI](https://pypi.org/project/hermes-agent/) remains at 0.19.0. + Newer Hermes releases (v0.22.4) are published only through GitHub, the + installer, and Docker. + +## Deferred items + +- **Claude runtime 2.1.286 → 2.1.308.** Claude Code's `stable` channel is + 2.1.308 (`latest` is 2.1.309), with Agent SDK 0.3.308. The range changes + headless defaults that Paperclip runs depend on: fork subagents are on by + default in `-p` and the Agent SDK (2.1.302), sessions with no permission + mode start in `default` mode on third-party providers (2.1.302), and MCP + allow rules match whole server names (2.1.299). This needs a separate runner + qualification together with the patched `claude-agent-acp` bridge. Until then, + Haiku 5.5 on the CLI lane needs a 2.1.293+ CLI. Once #16708 records that + minimum, sandbox runs on an older image prepare a private CLI at the model + minimum before launch. +- **OpenCode 1.19.x and 2.x.** OpenCode 1.19.3 generates reasoning variants + from model options instead of catalog variants, which affects the variants + Paperclip Runner binds. OpenCode 2 changes the SDK, event, and plugin + contracts. Both need qualification. +- **ACP bridges.** ACPX 0.20.0, `claude-agent-acp` 0.94.0 (ACP SDK 2.0), and + `codex-acp` 2.2.2 (breaking tool-call contract in 2.0.0) need a + qualification campaign rather than a pin bump. +- **GitHub Copilot CLI 1.0.103.** The qualified Copilot profile binds + Copilot 1.0.88 by immutable identity; a new profile version needs its own + qualification. Copilot discovers models from the ACP session, so new Copilot + models need no catalog change. +- **Pi 1.4.0** and **Hermes 0.22.x** need runtime qualification; Hermes no + longer publishes to PyPI. +- **Gemini 4 Argon** in the Gemini CLI and Cursor catalogs: the CLI does not + register it yet, and Cursor documents display names only. +- **Codex default model.** Codex 0.161.0 makes `gpt-6.1-sol` its default. + Paperclip keeps `gpt-5.6-sol` so saved agents and spending do not change. +- **Hidden Codex aliases** (`gpt-daybreak-*`) are restricted to eligible + accounts. +- **Claude Sonnet 4.5** is deprecated and retires November 30, 2026. It stays + in the picker until then. +- **Claude Mythos 5.1** remains restricted to verified organizations. + +## Host-environment baseline failures + +Recorded once for this audit on the unchanged base (`c5ca29d`), Node 25.9.0: + +- `server/src/__tests__/agent-hire-ai-connections.test.ts` fails at import + with `AI_InvalidArgumentError: baseURL must be a non-empty string.` because + the host injects an empty AI provider URL. Unrelated to this change. diff --git a/docker/daytona-runner/Dockerfile b/docker/daytona-runner/Dockerfile index 39b606cd0c5..cb791d6acac 100644 --- a/docker/daytona-runner/Dockerfile +++ b/docker/daytona-runner/Dockerfile @@ -65,9 +65,9 @@ ENV PATH=${PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT}/node_modules/.bin:/usr/local/sha # Never add a second CLI version to work around a runner compatibility pin. RUN npm uninstall -g @anthropic-ai/claude-code @openai/codex opencode-ai \ && npm install -g \ - @xai-official/grok@1.0.46 \ - @google/gemini-cli@0.62.0 \ - @moonshot-ai/kimi-code@2.1.1 \ + @xai-official/grok@1.0.70 \ + @google/gemini-cli@0.65.0 \ + @moonshot-ai/kimi-code@2.3.3 \ && npm cache clean --force # Hermes requires Python >=3.11,<3.14; the image's default Python is newer. @@ -78,8 +78,8 @@ RUN /usr/bin/python3 -m venv /opt/hermes \ && /opt/hermes/bin/pip install --no-cache-dir hermes-agent==0.19.0 \ && ln -sf /opt/hermes/bin/hermes /usr/local/bin/hermes -ARG CURSOR_VERSION=2026.10.01-e373342 -ARG CURSOR_SHA256_AMD64=a79726c6e644520e993970be4c45775a6889802b67abe461a677a53219ae28e8 +ARG CURSOR_VERSION=2026.10.22-27dcf5f +ARG CURSOR_SHA256_AMD64=85296944e4600a83c4b764f578d1502bf9c416db200d33a593e8f3ecabfdc294 RUN set -eu; \ arch="$(dpkg --print-architecture)"; \ [ "$arch" = "amd64" ] || { echo "FATAL: cursor pin only covers amd64, not $arch" >&2; exit 1; }; \ @@ -93,8 +93,8 @@ RUN set -eu; \ ln -sf /usr/local/bin/cursor-agent /usr/local/bin/agent; \ chmod -R a+rX /opt/cursor -ARG GH_VERSION=2.102.0 -ARG GH_SHA256_AMD64=bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386 +ARG GH_VERSION=2.103.3 +ARG GH_SHA256_AMD64=4c806978ffce1cec70853302a56d70ac6da4f19bae7adca4e1a8ab00633dd91d RUN set -eu; \ arch="$(dpkg --print-architecture)"; \ [ "$arch" = "amd64" ] || { echo "FATAL: gh pin only covers amd64, not $arch" >&2; exit 1; }; \ diff --git a/docker/daytona-runner/README.md b/docker/daytona-runner/README.md index 6b56a538f91..70e1ca7ea19 100644 --- a/docker/daytona-runner/README.md +++ b/docker/daytona-runner/README.md @@ -27,11 +27,11 @@ pack. Claude Code 2.1.280 is the minimum for minimum for Sonnet 5.5; the pinned runtime also supports Fable 5.1. Codex 0.160.0 ships bundled metadata for the current [GPT-6.1 Sol, GPT-6 Sol, and GPT-6 Luna model IDs](https://learn.chatgpt.com/docs/models). -The same refresh pins Grok CLI 1.0.46, Gemini CLI 0.62.0, Kimi Code 2.1.1, -Cursor CLI 2026.10.01-e373342, and GitHub CLI 2.102.0 in the sandbox layer. -The native Cursor provider uses its separately verified distribution, pinned to -2026.09.26-dd393fe; it does not use the sandbox layer’s global Cursor executable. -Grok CLI 1.0.46 supports the current +The sandbox layer pins Grok CLI 1.0.70, Gemini CLI 0.65.0, Kimi Code 2.3.3, +Cursor CLI 2026.10.22-27dcf5f, and GitHub CLI 2.103.3 (October 2026 +refresh). The native Cursor provider uses its separately verified distribution, +pinned to 2026.09.26-dd393fe; it does not use the sandbox layer’s global Cursor +executable. Grok CLI 1.0.70 supports the current [Grok 4.7](https://docs.x.ai/developers/grok-4-7) model family. Hermes stays at 0.19.0, the newest release on PyPI. diff --git a/packages/adapters/codex-local/src/index.test.ts b/packages/adapters/codex-local/src/index.test.ts index fcb53825d60..7976cab2e32 100644 --- a/packages/adapters/codex-local/src/index.test.ts +++ b/packages/adapters/codex-local/src/index.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { CODEX_CHATGPT_MODEL_REJECTION_RE, + codexChatGptRetirementForModel, codexCliVersionAtLeast, codexLocalReasoningEffortsForModel, DEFAULT_CODEX_LOCAL_MODEL, @@ -25,6 +26,20 @@ describe("codex model CLI floors", () => { }, ); + it("records models retired from Codex with ChatGPT sign-in and keeps them selectable for API keys", () => { + expect(codexChatGptRetirementForModel("gpt-5.5")).toEqual({ + retiredOn: "2026-10-14", + replacement: "gpt-6.1-sol (gpt-6-luna on Free and Go plans)", + }); + expect(codexChatGptRetirementForModel(" gpt-5.4 ")).toEqual({ retiredOn: "2026-08-31", replacement: "gpt-6-sol" }); + expect(codexChatGptRetirementForModel("gpt-5.4-mini")).toEqual({ retiredOn: "2026-08-31", replacement: "gpt-6-luna" }); + for (const model of [DEFAULT_CODEX_LOCAL_MODEL, "gpt-6.1-sol", "custom-model", "", null, undefined]) { + expect(codexChatGptRetirementForModel(model)).toBeNull(); + } + const modelIds = models.map((model) => model.id); + expect(modelIds).toEqual(expect.arrayContaining(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini"])); + }); + it("compares stable versions numerically and rejects prerelease or ambiguous input", () => { expect(codexCliVersionAtLeast("0.159.0", "0.159.0")).toBe(true); expect(codexCliVersionAtLeast("0.160.1", "0.159.0")).toBe(true); diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index c06e881c729..c5bb78db66a 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -129,6 +129,31 @@ export function minimumCodexCliVersionForModel(model: string | null | undefined) 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> = { + "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 { @@ -257,6 +282,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). +- 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. diff --git a/packages/adapters/codex-local/src/server/cli-version.ts b/packages/adapters/codex-local/src/server/cli-version.ts index aaafc9479af..097242acfe5 100644 --- a/packages/adapters/codex-local/src/server/cli-version.ts +++ b/packages/adapters/codex-local/src/server/cli-version.ts @@ -3,6 +3,7 @@ import type { AdapterExecutionTarget } from "@paperclipai/adapter-utils/executio import { runAdapterExecutionTargetProcess } from "@paperclipai/adapter-utils/execution-target"; import { CODEX_CHATGPT_MODEL_REJECTION_RE, + codexChatGptRetirementHint, codexCliVersionAtLeast, minimumCodexCliVersionForModel, normalizeCodexModel, @@ -110,6 +111,7 @@ export function codexHelloProbeModelRejectionCheck(input: { if (!match) return null; const model = match[1]!; const minimum = minimumCodexCliVersionForModel(model); + const retirementHint = codexChatGptRetirementHint(model); const versionNote = input.detectedVersion ? ` Detected Codex CLI ${input.detectedVersion}.` : ""; @@ -118,8 +120,8 @@ export function codexHelloProbeModelRejectionCheck(input: { level: "error", message: `The Codex backend rejected ${model} for this ChatGPT account.`, detail: `${match[0]}${versionNote}`, - hint: minimum + hint: retirementHint ?? (minimum ? `${model} needs Codex CLI ${minimum} or newer with ChatGPT sign-in. ${sandboxUpgradeHint(input.targetIsSandbox)} If the CLI is already new enough, the plan does not include this model (Enterprise and Edu administrators must enable it) or the rollout has not reached the account; choose another model or use an OpenAI API key.` - : `The ChatGPT plan does not include ${model}, the rollout has not reached the account, or the installed Codex CLI is older than the model requires. ${sandboxUpgradeHint(input.targetIsSandbox)} Otherwise choose another model or use an OpenAI API key.`, + : `The ChatGPT plan does not include ${model}, the rollout has not reached the account, or the installed Codex CLI is older than the model requires. ${sandboxUpgradeHint(input.targetIsSandbox)} Otherwise choose another model or use an OpenAI API key.`), }; } diff --git a/packages/adapters/codex-local/src/server/test.remote.test.ts b/packages/adapters/codex-local/src/server/test.remote.test.ts index 9efe9ebfd90..4cab7a6d0cb 100644 --- a/packages/adapters/codex-local/src/server/test.remote.test.ts +++ b/packages/adapters/codex-local/src/server/test.remote.test.ts @@ -372,6 +372,35 @@ describe("codex remote environment diagnostics", () => { expect(result.checks.some((check) => check.code === "codex_hello_probe_auth_required")).toBe(false); }); + it("names the ChatGPT retirement when the backend rejects a retired model", async () => { + runAdapterExecutionTargetProcess.mockResolvedValueOnce({ + exitCode: 1, + signal: null, + timedOut: false, + stdout: "", + stderr: "ERROR: {\"type\":\"error\",\"status\":400,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"The 'gpt-5.5' model is not supported when using Codex with a ChatGPT account.\"}}", + pid: 223, + startedAt: new Date().toISOString(), + }); + + const result = await testEnvironment({ + companyId: "company-1", + adapterType: "codex_local", + config: { engine: "cli", command: "codex", model: "gpt-5.5" }, + executionTarget: sandboxTarget(), + environmentName: "QA Daytona", + }); + + expect(result.status).toBe("fail"); + const rejected = result.checks.find((check) => check.code === "codex_hello_probe_model_rejected"); + expect(rejected).toMatchObject({ + level: "error", + message: "The Codex backend rejected gpt-5.5 for this ChatGPT account.", + hint: "OpenAI retired gpt-5.5 from Codex with ChatGPT sign-in on 2026-10-14. Choose gpt-6.1-sol (gpt-6-luna on Free and Go plans), or use an OpenAI API key; API-key sessions are not affected.", + }); + expect(rejected?.hint).not.toContain("Upgrade Codex"); + }); + it("emits the canonical adapter_auth_missing check when a sandbox hello probe reports missing auth", async () => { // The sandbox has no seedable credentials, so the hello probe returns an // authentication-required error. The Test must emit the neutral canonical diff --git a/packages/adapters/opencode-local/src/index.ts b/packages/adapters/opencode-local/src/index.ts index 6d4d08a89c0..c32d9417a8d 100644 --- a/packages/adapters/opencode-local/src/index.ts +++ b/packages/adapters/opencode-local/src/index.ts @@ -65,6 +65,8 @@ export const models: Array<{ id: string; label: string }> = [ { id: "anthropic/claude-fable-5-1", label: "anthropic/claude-fable-5-1" }, { id: "anthropic/claude-sonnet-5-5", label: "anthropic/claude-sonnet-5-5" }, { id: "anthropic/claude-sonnet-5", label: "anthropic/claude-sonnet-5" }, + { id: "anthropic/claude-haiku-5-5", label: "anthropic/claude-haiku-5-5" }, + { id: "google/gemini-4-argon", label: "google/gemini-4-argon" }, { id: "google/gemini-3.8-flash", label: "google/gemini-3.8-flash" }, { id: "xai/grok-4.7", label: "xai/grok-4.7" }, { id: "openai/gpt-5.5", label: "openai/gpt-5.5" }, diff --git a/server/src/services/native-runtime/setup-readiness.test.ts b/server/src/services/native-runtime/setup-readiness.test.ts index cffe993feb4..139cdcde839 100644 --- a/server/src/services/native-runtime/setup-readiness.test.ts +++ b/server/src/services/native-runtime/setup-readiness.test.ts @@ -432,6 +432,20 @@ describe("Codex selected native account verification", () => { expect(nativeProbe).toHaveBeenCalledOnce(); expect(execute).not.toHaveBeenCalled(); }); + it("names the ChatGPT retirement when the backend rejects a retired model", async () => { + nativeProbe.mockRejectedValue(new Error(JSON.stringify({ type: "error", status: 400, + error: { type: "invalid_request_error", message: "The 'gpt-5.5' model is not supported when using Codex with a ChatGPT account." } }))); + const result = await testNativeRunnerAuthentication(context, "codex", "gpt-5.5"); + expect(result.status).toBe("fail"); + expect(result.checks[0]).toMatchObject({ code: "codex_hello_probe_failed", level: "error" }); + expect(result.checks[0].hint).toContain("OpenAI retired gpt-5.5 from Codex with ChatGPT sign-in on 2026-10-14"); + expect(result.checks[0].hint).toContain("gpt-6.1-sol"); + }); + it("keeps the generic hint when the backend rejects a model that is not retired", async () => { + nativeProbe.mockRejectedValue(new Error("The 'gpt-6.1-sol' model is not supported when using Codex with a ChatGPT account.")); + const result = await testNativeRunnerAuthentication(context, "codex", "gpt-6.1-sol"); + expect(result.checks[0].hint).toBe("Check the selected account, model access, and native runtime prerequisites, then retry. Legacy runner is available explicitly in Advanced."); + }); it.each([ ["authentication required: selected-account", true], ["Model is unavailable", false], diff --git a/server/src/services/native-runtime/setup-readiness.ts b/server/src/services/native-runtime/setup-readiness.ts index dc83185de53..bad070d1266 100644 --- a/server/src/services/native-runtime/setup-readiness.ts +++ b/server/src/services/native-runtime/setup-readiness.ts @@ -2,6 +2,7 @@ import { constants } from "node:fs"; import { lstat, mkdir, mkdtemp, open, realpath, rm, stat, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, posix } from "node:path"; +import { CODEX_CHATGPT_MODEL_REJECTION_RE, codexChatGptRetirementHint } from "@paperclipai/adapter-codex-local"; import { copyBackCodexAuth } from "@paperclipai/adapter-codex-local/server"; import type { AdapterEnvironmentTestContext, AdapterEnvironmentTestResult } from "@paperclipai/adapter-utils"; import { ADAPTER_AUTH_MISSING_CHECK_CODE } from "@paperclipai/shared"; @@ -255,8 +256,10 @@ export async function testNativeRunnerAuthentication(context: AdapterEnvironment } catch (error) { const message = redactNativeProbeMessage(error instanceof Error ? error.message : "The selected native runtime could not verify this account.", { ...environment, ...(sourceAuthJson ? { [authKey]: sourceAuthJson } : {}) }); const authentication = /(?:invalid (?:api[- ]?key|auth(?:entication)? token)|authentication (?:failed|required)|unauthenticated|unauthorized|not authenticated|please (?:log|sign) in|not logged in|\b(?:401|403)\b)/i.test(message); + const rejectedModel = provider === "codex" ? CODEX_CHATGPT_MODEL_REJECTION_RE.exec(message)?.[1] : undefined; + const retirementHint = rejectedModel ? codexChatGptRetirementHint(rejectedModel) : null; return { adapterType: "paperclip_runner", status: "fail", testedAt: new Date().toISOString(), checks: [{ code: `${provider}_hello_probe_${authentication ? "auth_required" : /timed out/i.test(message) ? "timeout" : "failed"}`, level: "error", message, - hint: "Check the selected account, model access, and native runtime prerequisites, then retry. Legacy runner is available explicitly in Advanced." }, + hint: retirementHint ?? "Check the selected account, model access, and native runtime prerequisites, then retry. Legacy runner is available explicitly in Advanced." }, ...(authentication && provider === "codex" ? [{ code: ADAPTER_AUTH_MISSING_CHECK_CODE, level: "error" as const, message: "The selected Codex account needs authentication.", hint: "Sign in to Codex or choose an authenticated account, then test again." }] : [])] };