Files
PaperClipAI/server/src/services/agent-runner-selection.test.ts
T
DottaandPaperclip 1f55073f96 fix(cursor-cloud): support API-only controllers on managed stacks (#18310)
## Thinking Path

> - Paperclip helps people manage AI agents at work.
> - Cursor Cloud hosts execution behind its API.
> - Product setup still expected a Paperclip Computer.
> - Managed-only tenants therefore rejected valid setup.
> - This admits the validated API-owned controller.
> - Authority, Stop proof, budgets and cleanup remain enforced.

## Linked Issues or Issue Description

Refs #18068 and #18069; both dependencies have merged.

**What happened?**

Managed-only setup still requested a Computer. Live tests exposed
missing Cursor Stop proof and duplicate replay.

**Expected behavior**

Use Cursor’s API controller. Verify provider/controller termination and
replay each log identity once.

**Steps to reproduce**

Enable native Runner/managed-only. Configure Cursor model/repository/key
without a Computer, then Test, finish setup, run and Stop.

## What Changed

- Derive API-only placement from validated native Cursor configuration;
reject placement/command/workspace/home overrides and keep
Kubernetes-only execution closed.
- Skip Computers/checkout/workspace realization. Keep scoped tools,
completion, private staging, budgets, leases and recovery. Persist
placement; reject contradictions.
- Align Cloud cards, Test, Save and Finish setup; hold unready drafts.
Legacy keeps its Computer, credential and policy checks.
- Verify Stop from exact API ownership, audited intent,
provider-terminal proof, no Computer/process ownership and controller
retirement. Recheck immediate/deferred retirement; wake existing
recovery on durable evidence.
- Clear warnings when the actual stopped target later has authoritative
confirmation. Keep missing/other-run negatives and the existing
predicate.
- Publish safe status refresh after full-proof ACK. Retain bounded
native/physical cancellation history; add no poller or weaker proof
path.

## Verification

Core merged as `3910352c923a`; Product merged as `27d35314be8e`. Public
branch `5f86eb66b081` has tree `67344151d303`. Its exact-head Greptile
summary is 5/5; the fresh audit found zero unresolved threads and
complete pagination. The controlled workspace retry and required summary
passed. All 59 current-head contexts are terminal and clean. Prior
green-36 evidence keeps its identity.

[Current
CI](https://github.com/paperclipai/paperclip/actions/runs/41680511731)
first failed workspaces-a 1/2: the CLI company-import/export beforeAll
30-second runtime-info/child-PID/HTTP readiness wait expired. All 431 UI
files/4,714 tests and 285 other CLI tests passed (one skip). The cause
remains UNKNOWN. Child output records startup recovery complete, but
buffered CI prefixes do not establish its relation to the waiter
deadline; runtime-file/PID/last-health state was not captured. Both
unchanged local probes were RED. The second observed dependency
preflight still pending at the deadline; observer overhead was not
quantified. Neither proves the CI cause. After all functional jobs
finished, only the queued metadata summary was cancelled; the other 48
records stayed unchanged. Attempt 1 retains 46 successes, the original
failure, one skip and the cancelled summary. One same-head debug retry
was admitted at 18:42 UTC on November 3. Its workspace job passed at
19:23 UTC on the same checkout: UI 431 files/4,714 tests and CLI 33
files/286 tests, including company import/export. Test caching was
disabled. The new `ci / verify` summary passed; workflow attempt 2
completed SUCCESS. Required `ci / e2e` also passed. The final check at
19:53 UTC on November 3 found all 59 current-head contexts terminal and
clean: zero pending and zero failing. The 47 inherited completed/skipped
rows are not new executions. No code, assertion or timeout changed;
success does not explain the original failure or erase the local REDs.

CI checkout is `1ea9d8608ced`/tree `78cb1ec13d59` (parents
`2e37c73c05fe`, 5f86); both workflows use 2e37. Its 20 extra paths add
low-memory build/Rust task-memory. Scripts cohere; later matrix changes
only labels, with no newer debug-cache steps. Static audit found no
blocker. Supported API arguments omit the memory flag: no default
monitor/instruction changes. Flagged external launchers can change
Cursor instructions/digests and remain unqualified.

The merge added 41 master-identical paths, retained the green-36 inbox
hunk and found UI fixtures matching upstream. Startup/backup, recovery
projection, sleep diagnostics and binary backup change production.
Static audit found eight Stop/ACK owner blobs and 37 sleep-work SQL
templates unchanged, with sleep fences/single-flight accounting intact.
Exact precommit tree: 416/416 across eight server files, binary backup
1/1, server types PASS; repeated scopes are not summed.

The deferred-retirement P1 reproduced three real-DB/owned-process
failures; missing proof stayed unverified. Recovery passed 47 process
cases/33 proof negatives. Warning repair binds actual targets to
same-run proof. Validation passed 729 scoped UI cases, a separate
359-case thread run, 111 backend cases (84 CAS, 6 assembled, 21
activity/privacy), types/tokens. Proof validators/predicate are
unchanged; no new poller or increased timeout.

The d8d3 attempt stays FAILED: 45 passes, two workspaces-a failures,
derived verify failure and one skip. Both shards shared three failures:
two successful mocks omitted the account guard’s required userId. The
two-line fixture repair changed no guard, assertion or timeout. Three
failures reproduced; 54 access/auth tests then passed, including
account-change/denial negatives, plus UI types/tokens.

The 69285 attempt stays FAILED: 49 jobs, 46 passed, serialized shard 9
failed, its verify aggregate failed and one selection job skipped. The
shard passed 575/576 cases. Its aborted-snapshot case expected nine
computes but saw eight: a final HTTP 200 coalesced while seven ordinary
reads had not proved the aborted compute settled. Earlier CI and the
unmodified local baseline passed with a miss; no local RED is claimed.
The repair awaits the snapshot, allows exactly one extra GET if
coalesced, then requires a miss and retains nine computes/all HTTP429
assertions. No production code, hook, loop, sleep or timeout changed.
Six inbox and seven cache/admission cases passed locally, plus server
types; 30 unrelated cases were filtered. Green 36 passed 576/576 without
taking the extra-GET branch; local runs also missed that schedule.
Current CI event merge 1ea9 now passed 576/576 and naturally exercised
the former failure: coalesced (count 2, 1ms), then exactly one fresh
miss (count 1, 16ms), with strict nine-compute/HTTP429 assertions
intact. No instrumentation or retry was added.

Earlier compatibility passed scoped remote/inbox, routine, API-placement
and UI tests, plus types/migrations/snapshots/maps/tokens. The 89-path
adoption restored fleet routing without workflow overrides or added
skips. Each result retains its source identity.

Live qualification remains actual `9af1a5de42a2`: unchanged Artifact
grader PASSED 17/17 and active-Stop grader PASSED 21/21. All three
provider test resources were deleted canonically with GET404;
post-delete download returned the exact 27 bytes. Histories stayed
stable (Artifact 121 and Stop 67 canonical identities), with 21 company
runs, zero active work and no accounting/cancellation/reservation debt
or integrity findings. Saved settings reopened; no-prompt Test/Linux
admission passed with `claude-sonnet-4-5`. No new Save is claimed.

The first Stop sampling case stays FAILED on a 10.373-second gap.
Unchanged retry kept all 24 samples: 35.076 seconds quiet, maximum gap
4.584 seconds, plus a separate CANCELLED/artifact-absent read after 180
seconds. Initial grading rejected locator spelling before functional
checks; metadata changed only after same-file/inode/byte proof, with raw
evidence/oracles unchanged. Original Question/Plan/reconnect and
historical deleted-artifact results keep their identities.

Git trees verified 11 history-only conflicts and six earlier mainline
paths. Hosted 9af to the public BRANCH differs in 222 paths; 13,500
other mode/blob leaves match. Runner runtime implementations and six
observer/driver pins match 9af on that branch only; inherited
`drivers/acpx/runtime-host.test.ts` differs. No all-Runner/src,
CI/latest-master runtime or image equality is claimed. Later static
audits through `8ab89f41e499` found no concrete API-only blocker. That
base adds completed-history sleep proofs and an MCP cleanup migration;
protected Stop/ACK owners are unchanged. These later changes are outside
this CI and have no live qualification here. Host duplicate-card and
later recovery/UI repairs retain separate proof, not deployed 9af
identity. Direct-key coverage requires absent aiConnection; API-only
placement skips inherited remote-workspace disk-full paths, and
config-read gates match their parent.

Other retained failures: stale fixture 132/133; E8 48/135 with 87
reset-hook failures; Mac 17 SQL failures/skips;
title/startup/custody/Stop/deletion unknown causes and bounded
recoveries; superseded warning/50 duplicate identities; pre-repair
ACK/Core CI failures. Fixture RED/GREEN passed 7/7 browser and 121/121
shutdown tests with a failing broken-unsubscribe control; stable WSP
wrapper passed 128 after physical teardown failure. No full local-suite
pass is inferred. All paid attempts remain recorded; vendor dollar cost
is UNKNOWN.

## Risks

Experimental: Cursor independently acknowledges neither the effective
model nor instruction digest before the first prompt. Explicit values
are submitted and behavior checked live; unpriced provider dollar cost
stays UNKNOWN.

The placement exception applies only to validated native Cursor API
execution. Other local work remains denied under managed-only policy.
Missing/stale Stop or cleanup proof stays pending; damaged archives
remain failed. Explicit Legacy keeps its existing policy owners.

## Model Used

OpenAI Codex/GPT-6 family: tools, execution, browser checks, delegated
review. API ID/context window unavailable.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used and the limits of available model
metadata
- [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 linked public issues or described the issue in this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch describes the change and contains no internal ticket ID
- [x] I have run the scoped local tests reported above; retained
failures are stated separately
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented the risks
- [x] All required CI gates pass on the current head
- [x] Fresh Greptile is 5/5 with no unresolved findings on the current
head
- [x] I will address all reviewer comments before requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 13:55:39 -06:00

895 lines
78 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { agentHarnessType, agentRunner, paperclipRunnerProfileForHarness, paperclipRunnerSupportsPlatform, PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION, createAgentSchema, updateAgentSchema, testAdapterEnvironmentSchema } from "@paperclipai/shared";
const state = vi.hoisted(() => ({ disabled: [] as string[], overridden: new Set<string>(), settings: {} as Record<string, unknown>, getEnvironment: vi.fn(), bindings: vi.fn(), activeTemplate: vi.fn().mockResolvedValue(null), secretPaths: vi.fn().mockResolvedValue(new Set(["apiKey"])), managedEnvironment: vi.fn(), kubernetesEnvironment: vi.fn(), ensureKubernetesEnvironment: vi.fn(), ssh: vi.fn(), resolveEnvironment: vi.fn(), sandboxQualification: vi.fn(), pluginByKey: vi.fn() }));
vi.mock("../adapters/registry.js", () => ({ findActiveServerAdapter: (type: string) => ({ type }), hasActiveAdapterOverride: (type: string) => state.overridden.has(type), waitForExternalAdapters: async () => {} }));
vi.mock("./adapter-plugin-store.js", () => ({ getDisabledAdapterTypes: () => state.disabled }));
vi.mock("./adapter-settings.js", async (original) => ({
...await original(),
adapterSettingsService: () => ({ getDisabledTypes: async () => state.disabled }),
}));
vi.mock("./instance-settings.js", () => ({ instanceSettingsService: () => ({ get: async () => state.settings }) }));
vi.mock("./environments.js", () => ({ environmentService: () => ({ getById: state.getEnvironment, listBoundCompanyIds: state.bindings, findManagedSandboxEnvironment: state.managedEnvironment, findKubernetesEnvironment: state.kubernetesEnvironment, ensureKubernetesEnvironment: state.ensureKubernetesEnvironment }) }));
vi.mock("./environment-custom-images.js", () => ({ environmentCustomImageService: () => ({ getActiveTemplate: state.activeTemplate }) }));
vi.mock("./environment-probe.js", () => ({ readSandboxPlatformQualification: state.sandboxQualification }));
vi.mock("./environment-config.js", async () => ({
parseEnvironmentDriverConfig: (await vi.importActual<typeof import("./environment-config.js")>("./environment-config.js")).parseEnvironmentDriverConfig,
resolveEnvironmentDriverConfigForRuntime: state.resolveEnvironment,
resolveSandboxProviderSecretRefPaths: state.secretPaths,
}));
vi.mock("./plugin-registry.js", () => ({ pluginRegistryService: () => ({ getByKey: state.pluginByKey }) }));
vi.mock("@paperclipai/adapter-utils/ssh", () => ({ runSshCommand: state.ssh }));
import { AGENT_RUNNER_SSH_PLATFORM_COMMAND, agentRunnerAvailability, resolveAgentRunnerTaskDriverForCompany, resolveAgentRunnerTargetForCompany, resolveNewAgentRunner, resolveNewAgentRunnerForCompany } from "./agent-runner-selection.js";
import { buildCodexAcpConfig, resolveCodexExecutionEngine } from "@paperclipai/adapter-codex-local/server";
import { secretService } from "./secrets.js";
import * as localPlatform from "../lib/agent-runner-platform.js";
import { fingerprintEnvironmentSandboxProviderConfig as fingerprint } from "./environment-custom-image-runtime.js";
const target = { driver: "local", platform: "linux", architecture: "x64" };
const qualifiedTargets = [target, { driver: "local", platform: "linux", architecture: "arm64" }, { driver: "local", platform: "darwin", architecture: "arm64" }, { driver: "local", platform: "darwin", architecture: "x64" }];
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")!;
const originalArchitecture = Object.getOwnPropertyDescriptor(process, "arch")!;
const managedCloudConfig = { v: 1, mode: "cloud", catalogVersion: "test", features: {}, plugins: { autoInstall: ["daytona"] } };
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllEnvs(); Object.defineProperty(process, "platform", originalPlatform); Object.defineProperty(process, "arch", originalArchitecture); });
describe("server-owned Codex runner selection", () => {
beforeEach(() => { state.activeTemplate.mockReset().mockResolvedValue(null); });
beforeEach(() => { state.disabled = []; state.overridden.clear(); state.settings = {}; state.getEnvironment.mockReset(); state.bindings.mockReset().mockResolvedValue([]); state.managedEnvironment.mockReset(); state.kubernetesEnvironment.mockReset().mockResolvedValue(null); state.ensureKubernetesEnvironment.mockReset(); state.ssh.mockReset().mockResolvedValue({ stdout: "Linux\nx86_64\n" }); state.sandboxQualification.mockReset().mockResolvedValue(null); state.resolveEnvironment.mockReset().mockResolvedValue({ driver: "ssh", config: {} }); });
it("uses the supplied managed adapter snapshot for Cursor availability", () => {
vi.stubEnv("PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN", "synthetic-cloud-test-token");
expect(() => agentRunnerAvailability("cursor_cloud", target)).toThrow("fresh adapter settings snapshot");
expect(agentRunnerAvailability("cursor_cloud", target, []).supportedRunners).toContain("paperclip");
for (const disabled of ["cursor_cloud", "paperclip_runner"]) {
expect(agentRunnerAvailability("cursor_cloud", target, [disabled]).supportedRunners,
`the current ${disabled} disable must affect Cursor selection`).not.toContain("paperclip");
}
});
it("keeps Cursor Cloud legacy by default and converts only an equivalent explicitly selected profile", () => {
const config = { model: "caller-model", repoUrl: "https://github.com/acme/repo", repoStartingRef: "feature", runtimeEnvType: "cloud", env: { CURSOR_API_KEY: { type: "secret_ref", secretId: "saved-key", version: "latest" } }, instructionsFilePath: "/assigned/AGENTS.md", autoCreatePR: false };
expect(agentRunnerAvailability("cursor_cloud", target)).toEqual({ supportedRunners: ["paperclip", "legacy"], defaultRunner: "legacy" });
expect(resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, target })).toEqual({ adapterType: "cursor_cloud", adapterConfig: config });
for (const runner of [undefined, "auto"] as const) {
for (const driver of ["unavailable", "sandbox", "ssh"]) {
expect(() => resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner, target: { driver } }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_unavailable" } }));
}
}
expect(resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner: "legacy", target: { driver: "sandbox" } }))
.toEqual({ adapterType: "cursor_cloud", adapterConfig: config });
const converted = resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner: "paperclip", target });
expect(converted).toEqual({ adapterType: "paperclip_runner", adapterConfig: { model: config.model, repoUrl: config.repoUrl, repoRef: "feature", cloudEnvType: "cloud", env: config.env, instructionsFilePath: config.instructionsFilePath, autoCreatePR: false, provider: "cursor_cloud", lifecycleMode: "per_turn" } });
expect(resolveNewAgentRunner({ ...converted, runner: "legacy", target })).toEqual({ adapterType: "cursor_cloud", adapterConfig: config });
});
it("rejects unsupported Cursor Cloud settings and keeps API placement independent of the selected Computer", () => {
const config = { model: "caller-model", repoUrl: "https://github.com/acme/repo" };
for (const patch of [{ promptTemplate: "custom instructions" }, { runtimeEnvType: "pool" }, { model: "" }]) {
expect(() => resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: { ...config, ...patch }, runner: "paperclip", target })).toThrow();
}
expect(config).toEqual({ model: "caller-model", repoUrl: "https://github.com/acme/repo" });
expect(agentRunnerAvailability("cursor_cloud", { ...target, driver: "ssh" })).toEqual({ supportedRunners: ["paperclip"], defaultRunner: "paperclip" });
expect(resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner: "paperclip", target: { ...target, driver: "ssh" } })).toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { provider: "cursor_cloud" } });
});
it("saves native Cursor Cloud without probing or binding a managed Computer", async () => {
state.settings = { defaultEnvironmentId: "managed", experimental: { enableManagedSandboxOnly: true } };
const saved = await resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType: "cursor_cloud", runner: "paperclip", defaultEnvironmentId: "managed",
adapterConfig: { model: "caller-model", repoUrl: "https://github.com/acme/repo" },
});
expect(saved).toMatchObject({ adapterType: "paperclip_runner", defaultEnvironmentId: null, adapterConfig: { provider: "cursor_cloud" } });
expect(state.getEnvironment).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
});
it("refreshes managed adapter policy before API-only Cursor admission without a Computer", async () => {
vi.stubEnv("PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN", "synthetic-cloud-test-token");
state.settings = { experimental: { enableManagedSandboxOnly: true, enableNativeRunner: true } };
state.managedEnvironment.mockResolvedValue(null);
const input = { adapterType: "cursor_cloud", runner: "auto" as const, setupOnly: true, adapterConfig: {}, disabledTypes: [] };
expect(await resolveNewAgentRunnerForCompany({} as never, "company", input))
.toMatchObject({ adapterType: "paperclip_runner", defaultEnvironmentId: null, adapterConfig: { provider: "cursor_cloud" } });
for (const disabled of ["cursor_cloud", "paperclip_runner"]) {
state.disabled = [disabled];
await expect(resolveNewAgentRunnerForCompany({} as never, "company", input))
.rejects.toMatchObject({ status: 422, details: { code: "agent_runner_unavailable" } });
}
await expect(resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType: "paperclip_runner", adapterConfig: { provider: "cursor_cloud", model: "caller-model", repoUrl: "https://github.com/acme/repo", lifecycleMode: "per_turn" },
})).rejects.toMatchObject({ status: 422, details: { code: "agent_runner_unavailable" } });
expect(state.managedEnvironment).not.toHaveBeenCalled();
expect(state.getEnvironment).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
});
it.each([undefined, "auto"] as const)("defaults a new managed Cursor setup draft to the native API controller without a Computer (%s)", async runner => {
state.settings = { experimental: { enableManagedSandboxOnly: true, enableNativeRunner: true } };
state.managedEnvironment.mockResolvedValue(null);
const saved = await resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType: "cursor_cloud", runner, setupOnly: true, adapterConfig: {},
});
expect(saved).toMatchObject({ adapterType: "paperclip_runner", defaultEnvironmentId: null,
adapterConfig: { provider: "cursor_cloud", lifecycleMode: "per_turn" } });
expect(state.managedEnvironment).not.toHaveBeenCalled();
expect(state.getEnvironment).not.toHaveBeenCalled();
});
it.each([
{ setupOnly: false, enabled: true },
{ setupOnly: true, enabled: false },
])("does not use the new-draft Cursor API default outside its admission scope (%j)", async ({ setupOnly, enabled }) => {
state.settings = { experimental: { enableManagedSandboxOnly: true, enableNativeRunner: enabled } };
state.managedEnvironment.mockResolvedValue(null);
await expect(resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType: "cursor_cloud", runner: "auto", setupOnly, adapterConfig: {},
})).rejects.toMatchObject({ status: 422, details: { code: "managed_sandbox_unavailable" } });
});
it("preserves an explicit Legacy choice on a held Cursor setup draft", async () => {
state.settings = { experimental: { enableManagedSandboxOnly: true, enableNativeRunner: true } };
expect(await resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType: "cursor_cloud", runner: "legacy", setupOnly: true, adapterConfig: {},
})).toEqual({ adapterType: "cursor_cloud", adapterConfig: {} });
expect(state.managedEnvironment).not.toHaveBeenCalled();
});
it("refuses native Cursor save and availability under Kubernetes-only policy without provisioning", async () => {
state.settings = { general: { executionMode: "kubernetes" } };
await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "cursor_cloud", runner: "paperclip",
adapterConfig: { model: "caller-model", repoUrl: "https://github.com/acme/repo" } }))
.rejects.toMatchObject({ status: 422, details: { code: "provider_api_kubernetes_execution_unsupported" } });
const selected = await resolveAgentRunnerTargetForCompany({} as never, "company");
expect(agentRunnerAvailability("cursor_cloud", selected).supportedRunners).toEqual([]);
expect(state.getEnvironment).not.toHaveBeenCalled();
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
});
it("keeps legacy Cursor Cloud environment bindings and rejects their unsupported native conversion", () => {
const config = { model: "caller-model", repoUrl: "https://github.com/acme/repo",
env: { CURSOR_API_KEY: { type: "secret_ref", secretId: "saved-key", version: "latest" }, GH_TOKEN: { type: "secret_ref", secretId: "hosted-key", version: "latest" } } };
expect(resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner: "legacy", target }))
.toEqual({ adapterType: "cursor_cloud", adapterConfig: config });
expect(() => resolveNewAgentRunner({ adapterType: "cursor_cloud", adapterConfig: config, runner: "paperclip", target }))
.toThrow("Configure hosted secrets in your Cursor Cloud environment");
expect(config.env.GH_TOKEN).toEqual({ type: "secret_ref", secretId: "hosted-key", version: "latest" });
});
it.each(["x64", "arm64"])("keeps detected musl/unknown Linux %s legacy and preserves saved execution", architecture => {
for (const libc of ["musl", "unknown"] as const) {
const target = { driver: "local", platform: "linux", architecture, libc };
expect(agentRunnerAvailability("codex_local", target)).toEqual({ supportedRunners: ["legacy"], defaultRunner: "legacy" });
const input = { adapterType: "codex_local", adapterConfig: { extraArgs: ["--sandbox", "read-only"] }, target };
expect(resolveNewAgentRunner(input)).toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
expect(() => resolveNewAgentRunner({ ...input, runner: "paperclip" })).toThrow("unavailable");
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol" }, target };
expect(resolveNewAgentRunner(saved)).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
expect(resolveNewAgentRunner({ ...saved, runner: "paperclip" })).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
expect(agentRunnerAvailability("codex_local", { ...target, libc: "glibc" }).defaultRunner).toBe("paperclip");
}
});
it.each([[undefined, false], ["2.38", false], ["2.39", true], ["3.0", true], [" 2.39 ", true], ["malformed", false], ["", false]] as const)("uses the measured local libc version (%s) for creation", async (glibcVersionRuntime, native) => {
Object.defineProperty(process, "platform", { ...originalPlatform, value: "linux" });
Object.defineProperty(process, "arch", { ...originalArchitecture, value: "arm64" });
vi.spyOn(localPlatform, "localAgentRunnerTarget").mockReturnValue({ driver: "local", platform: "linux", architecture: "arm64", libc: glibcVersionRuntime ? "glibc" : "unknown", ...(glibcVersionRuntime ? { glibcVersion: glibcVersionRuntime.trim() } : {}) });
const localTarget = localPlatform.localAgentRunnerTarget();
expect(localTarget).toEqual({ driver: "local", platform: "linux", architecture: "arm64", libc: glibcVersionRuntime ? "glibc" : "unknown", ...(glibcVersionRuntime ? { glibcVersion: glibcVersionRuntime.trim() } : {}) });
expect(agentRunnerAvailability("codex_local").defaultRunner).toBe(native ? "paperclip" : "legacy");
const saved = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local" });
expect(saved.adapterType).toBe(native ? "paperclip_runner" : "codex_local");
});
it.each([
["2.38", false], ["2.39", true], ["2.40", true], ["3.0", true], ["2.9", false],
["glibc 2.39", true], [" 2.39 ", true], ["2.39.1", true], ["02.039", true],
["2.39ubuntu1", false], ["unknown", false], ["", false], ["2", false], ["2.-39", false],
["999999999999999999999999.0", false],
])("qualifies the measured GNU/Linux daemon ABI for glibc %s (%s)", (glibcVersion, native) => {
expect(PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION).toBe("2.39");
for (const architecture of ["x64", "arm64"]) {
const target = { driver: "local", platform: "linux", architecture, libc: "glibc" as const, glibcVersion: String(glibcVersion) };
expect(paperclipRunnerSupportsPlatform("codex_local", target.platform, architecture, target.libc, target.glibcVersion)).toBe(native);
expect(agentRunnerAvailability("codex_local", target).defaultRunner).toBe(native ? "paperclip" : "legacy");
expect(resolveNewAgentRunner({ adapterType: "codex_local", target }).adapterType).toBe(native ? "paperclip_runner" : "codex_local");
if (!native) expect(() => resolveNewAgentRunner({ adapterType: "codex_local", runner: "paperclip", target })).toThrow("unavailable");
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol" }, target, runner: "paperclip" as const };
expect(resolveNewAgentRunner(saved)).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
}
});
it("keeps an unidentified local Linux libc legacy if the platform probe is unavailable", () => {
Object.defineProperty(process, "platform", { ...originalPlatform, value: "linux" });
vi.spyOn(localPlatform, "localAgentRunnerTarget").mockReturnValue({ driver: "local", platform: "linux", architecture: "x64", libc: "unknown" });
expect(localPlatform.localAgentRunnerTarget()).toMatchObject({ libc: "unknown" });
expect(agentRunnerAvailability("codex_local").defaultRunner).toBe("legacy");
});
it.each(qualifiedTargets)("uses native Codex automatically with compatible configuration and is idempotent on $platform/$architecture", target => {
const input = { adapterType: "codex_local", target, adapterConfig: { model: "gpt-5.6-sol", modelReasoningEffort: "high", env: { OPENAI_API_KEY: { type: "secret_ref", secretId: "secret" } }, cwd: "/workspace", instructionsFilePath: "/workspace/AGENTS.md", workspaceStrategy: { type: "git_worktree" }, timeoutSec: 60, search: false, fastMode: false, dangerouslyBypassApprovalsAndSandbox: true } };
const resolved = resolveNewAgentRunner(input);
expect(resolved).toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { provider: "codex", codexPermissionMode: "never", modelReasoningEffort: "high", env: input.adapterConfig.env, workspaceStrategy: input.adapterConfig.workspaceStrategy, timeoutSec: 60 } });
expect(resolved.adapterConfig).toHaveProperty("search", false);
expect(resolveNewAgentRunner(resolved)).toEqual(resolved);
expect(input.adapterConfig).toHaveProperty("search", false);
});
it.each(["cursor", "process", "external"])("keeps %s default unchanged", adapterType => {
expect(resolveNewAgentRunner({ adapterType, target, adapterConfig: { custom: "preserved" } })).toEqual({ adapterType, adapterConfig: { custom: "preserved" } });
});
it.each(qualifiedTargets.filter(target => !(target.platform === "linux" && target.architecture === "arm64")))("defaults Pi to native on $platform/$architecture and round trips exact thinking", target => {
const adapterConfig = { model: "openrouter/anthropic/claude-sonnet-4.6", thinking: "high", command: "pi", env: { OPENROUTER_API_KEY: { type: "secret_ref", secretId: "connection" } }, cwd: "/workspace" };
const selected = resolveNewAgentRunner({ adapterType: "pi_local", adapterConfig, target });
expect(agentRunnerAvailability("pi_local", target).defaultRunner).toBe("paperclip");
expect(selected).toEqual({ adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "pi", model: adapterConfig.model, piThinkingLevel: "high", acpxPermissionMode: "approve-all", lifecycleMode: "per_turn", cwd: adapterConfig.cwd, env: adapterConfig.env } });
expect(resolveNewAgentRunner(selected)).toEqual(selected);
expect(resolveNewAgentRunner({ ...selected, runner: "legacy" })).toEqual({ adapterType: "pi_local", adapterConfig: { model: adapterConfig.model, thinking: "high", cwd: adapterConfig.cwd, env: adapterConfig.env } });
});
it.each(["minimal", "medium", "xhigh"])("keeps unsupported Pi thinking %s explicit", thinking => {
const input = { adapterType: "pi_local", target, adapterConfig: { model: "custom/model", thinking } };
expect(() => resolveNewAgentRunner(input)).toThrow("Legacy runner");
expect(resolveNewAgentRunner({ ...input, runner: "legacy" })).toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
});
it.each([{ command: "custom-pi" }, { args: ["--extension", "custom.ts"] }, { extraArgs: "--no-tools" }, { stdoutLogMode: "raw" }, { thinking: "low", piThinkingLevel: "high" }])("rejects unsupported Pi configuration %j", extra => {
expect(() => resolveNewAgentRunner({ adapterType: "pi_local", target, adapterConfig: { model: "custom/model", ...extra } })).toThrow("Legacy runner");
});
it.each([undefined, "", "model", "provider/"])("requires a Pi provider/model outside inactive setup: %s", model => {
expect(() => resolveNewAgentRunner({ adapterType: "pi_local", target, adapterConfig: { model } })).toThrow("provider/model");
});
it("allows an inactive Pi draft before model selection and preserves saved native policy", () => {
expect(resolveNewAgentRunner({ adapterType: "pi_local", target, setupOnly: true })).toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "pi", model: "", piThinkingLevel: "low" } });
const saved = { adapterType: "paperclip_runner", target, adapterConfig: { provider: "acpx", acpxAgent: "pi", model: "custom/model", piThinkingLevel: "max", acpxPermissionMode: "approve-reads", lifecycleMode: "warm", idleTimeoutMs: 1234 } };
expect(resolveNewAgentRunner(saved)).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
expect(() => resolveNewAgentRunner({ ...saved, runner: "legacy" })).toThrow("Legacy Pi cannot honor");
});
it.each([{ driver: "local", platform: "win32", architecture: "x64" }, { driver: "local", platform: "linux", architecture: "arm64", libc: "glibc" as const, glibcVersion: "2.39" }, { driver: "ssh", platform: "linux", architecture: "arm64", libc: "glibc" as const, glibcVersion: "2.39" }, { driver: "sandbox" }, { driver: "local", platform: "linux", architecture: "x64", libc: "musl" as const }])("keeps Pi legacy on unqualified $driver/$platform targets", target => {
const input = { adapterType: "pi_local", adapterConfig: { model: "custom/model", thinking: "medium" }, target };
expect(agentRunnerAvailability("pi_local", target).defaultRunner).toBe("legacy");
expect(resolveNewAgentRunner(input)).toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
expect(() => resolveNewAgentRunner({ ...input, runner: "paperclip" })).toThrow("unavailable");
});
it.each(["minimal", "medium", "xhigh"])("honors an explicit Legacy Pi thinking edit to %s when resuming a native draft", thinking => {
expect(resolveNewAgentRunner({ adapterType: "paperclip_runner", runner: "legacy", adapterConfig: { provider: "acpx", acpxAgent: "pi", model: "custom/model", piThinkingLevel: "max", thinking } })).toEqual({ adapterType: "pi_local", adapterConfig: { model: "custom/model", thinking } });
});
it("honors disabled and overridden Pi adapters", () => {
const input = { adapterType: "pi_local", target, adapterConfig: { model: "custom/model" } };
state.overridden.add("pi_local");
expect(resolveNewAgentRunner(input).adapterType).toBe("pi_local");
state.overridden.clear(); state.disabled = ["paperclip_runner"];
expect(resolveNewAgentRunner(input).adapterType).toBe("pi_local");
state.disabled = ["pi_local"];
expect(() => resolveNewAgentRunner(input)).toThrow("Pi is disabled");
});
it.each(qualifiedTargets.filter(target => !(target.platform === "linux" && target.architecture === "arm64")))("defaults OpenCode to its native provider on $platform/$architecture", target => {
const adapterConfig = { model: "openrouter/anthropic/claude-sonnet-4.6", dangerouslySkipPermissions: true, command: "opencode", env: { OPENROUTER_API_KEY: { type: "secret_ref", secretId: "connection" } }, cwd: "/workspace", instructionsFilePath: "/workspace/AGENTS.md" };
const selected = resolveNewAgentRunner({ adapterType: "opencode_local", adapterConfig, target });
expect(agentRunnerAvailability("opencode_local", target).defaultRunner).toBe("paperclip");
expect(selected).toEqual({ adapterType: "paperclip_runner", adapterConfig: { provider: "opencode", model: adapterConfig.model, env: adapterConfig.env, cwd: adapterConfig.cwd, instructionsFilePath: adapterConfig.instructionsFilePath, opencodePermissionMode: "allow", lifecycleMode: "per_turn" } });
expect(resolveNewAgentRunner(selected)).toEqual(selected);
expect(resolveNewAgentRunner({ ...selected, runner: "legacy" })).toMatchObject({ adapterType: "opencode_local", adapterConfig: { model: adapterConfig.model, dangerouslySkipPermissions: true } });
});
it.each([{ driver: "local", platform: "win32", architecture: "x64" }, { driver: "local", platform: "linux", architecture: "arm64", libc: "glibc" as const, glibcVersion: "2.39" }, { driver: "ssh", platform: "linux", architecture: "arm64", libc: "glibc" as const, glibcVersion: "2.39" }, { driver: "sandbox", platform: "linux", architecture: "arm64", libc: "glibc" as const, glibcVersion: "2.39" }, { driver: "local", platform: "linux", architecture: "arm64", libc: "musl" as const }, { driver: "local", platform: "linux", architecture: "x64", libc: "glibc" as const, glibcVersion: "2.38" }, { driver: "sandbox" }])("keeps OpenCode legacy on unqualified $driver/$platform targets", target => {
const input = { adapterType: "opencode_local", adapterConfig: { model: "custom/model", command: "custom-opencode" }, target };
expect(agentRunnerAvailability("opencode_local", target)).toEqual({ supportedRunners: ["legacy"], defaultRunner: "legacy" });
expect(resolveNewAgentRunner(input)).toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
expect(() => resolveNewAgentRunner({ ...input, runner: "paperclip" })).toThrow("unavailable");
});
it.each([{ command: "custom-opencode" }, { variant: "high" }, { extraArgs: ["--agent", "plan"] }, { args: ["--attach", "http://localhost"] }, { dangerouslySkipPermissions: false }])("rejects unsupported OpenCode settings instead of discarding them: %j", adapterConfig => {
const input = { adapterType: "opencode_local", adapterConfig: { model: "custom/model", ...adapterConfig }, target };
expect(() => resolveNewAgentRunner(input)).toThrow("Legacy runner");
expect(resolveNewAgentRunner({ ...input, runner: "legacy" })).toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
});
it.each([undefined, "", "model", "provider/"])("requires an explicit OpenCode provider/model: %s", model => {
expect(() => resolveNewAgentRunner({ adapterType: "opencode_local", adapterConfig: { model }, target })).toThrow("provider/model");
});
it("stores an inactive native OpenCode setup draft before model selection, but cannot hire it", () => {
const input = { adapterType: "opencode_local", target };
expect(resolveNewAgentRunner({ ...input, setupOnly: true })).toEqual({ adapterType: "paperclip_runner", adapterConfig: { provider: "opencode", model: "", opencodePermissionMode: "allow", lifecycleMode: "per_turn" } });
expect(() => resolveNewAgentRunner(input)).toThrow("provider/model");
});
it("honors native OpenCode permissions and refuses to discard them on return to legacy", () => {
const selected = resolveNewAgentRunner({ adapterType: "opencode_local", adapterConfig: { model: "custom/model", opencodePermissionMode: "ask" }, target });
expect(selected.adapterConfig.opencodePermissionMode).toBe("ask");
expect(() => resolveNewAgentRunner({ ...selected, runner: "legacy" })).toThrow("opencodePermissionMode");
});
it("resolves OpenCode against the company sandbox qualification and preserves disabled/overridden adapters", async () => {
const input = { adapterType: "opencode_local", adapterConfig: { model: "custom/model" } };
state.settings = { defaultEnvironmentId: "sandbox" };
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active", config: { provider: "daytona" } });
expect((await resolveNewAgentRunnerForCompany({} as never, "company", input)).adapterType).toBe("opencode_local");
state.sandboxQualification.mockResolvedValue({ platform: "linux", architecture: "x64", libc: "glibc", glibcVersion: "2.39" });
expect((await resolveNewAgentRunnerForCompany({} as never, "company", input)).adapterType).toBe("paperclip_runner");
state.overridden.add("opencode_local");
expect(resolveNewAgentRunner({ ...input, target }).adapterType).toBe("opencode_local");
state.overridden.clear(); state.disabled = ["paperclip_runner"];
expect(resolveNewAgentRunner({ ...input, target }).adapterType).toBe("opencode_local");
state.disabled = ["opencode_local"];
expect(() => resolveNewAgentRunner({ ...input, target })).toThrow("OpenCode is disabled");
});
it.each(qualifiedTargets)("allows an explicit legacy runner and native to legacy round trip on $platform/$architecture", target => {
expect(resolveNewAgentRunner({ adapterType: "codex_local", target, runner: "legacy", adapterConfig: { extraArgs: ["--search"] } }).adapterType).toBe("codex_local");
expect(resolveNewAgentRunner({ adapterType: "paperclip_runner", runner: "legacy", adapterConfig: { provider: "codex", model: "gpt-5.6-sol", lifecycleMode: "per_turn", codexPermissionMode: "never", modelReasoningEffort: "high" } })).toEqual({ adapterType: "codex_local", adapterConfig: { model: "gpt-5.6-sol", modelReasoningEffort: "high", dangerouslyBypassApprovalsAndSandbox: true } });
});
it("preserves explicit legacy permission policy alongside compatible native lifecycle defaults", () => {
expect(resolveNewAgentRunner({ adapterType: "codex_local", runner: "legacy", adapterConfig: { dangerouslyBypassApprovalsAndSandbox: false, lifecycleMode: "per_turn" } }))
.toEqual({ adapterType: "codex_local", adapterConfig: { dangerouslyBypassApprovalsAndSandbox: false } });
expect(resolveNewAgentRunner({ adapterType: "paperclip_runner", runner: "legacy", adapterConfig: { provider: "codex", codexPermissionMode: "never", lifecycleMode: "per_turn", dangerouslyBypassApprovalsAndSandbox: false } }))
.toEqual({ adapterType: "codex_local", adapterConfig: { dangerouslyBypassApprovalsAndSandbox: false } });
});
it.each([{ platform: "win32", architecture: "x64" }, { platform: "linux", architecture: "arm" }, { platform: "darwin", architecture: "ia32" }])("keeps unqualified $platform/$architecture targets legacy while preserving explicit native profiles", ({ platform, architecture }) => {
const input = { adapterType: "codex_local", target: { driver: "local", platform, architecture } };
expect(resolveNewAgentRunner(input).adapterType).toBe("codex_local");
expect(() => resolveNewAgentRunner({ ...input, runner: "paperclip" })).toThrow("unavailable");
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol" }, target: input.target };
expect(resolveNewAgentRunner(saved)).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
expect(resolveNewAgentRunner({ ...saved, runner: "paperclip" })).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
});
it.each(qualifiedTargets)("automatically keeps unsupported targets and active external overrides legacy on $platform/$architecture", target => {
const input = { adapterType: "codex_local", target: { driver: "local", platform: "win32", architecture: "x64" } };
expect(resolveNewAgentRunner(input).adapterType).toBe("codex_local");
expect(() => resolveNewAgentRunner({ ...input, runner: "paperclip" })).toThrow("unavailable");
state.overridden.add("codex_local");
expect(agentRunnerAvailability("codex_local", target).defaultRunner).toBe("legacy");
expect(resolveNewAgentRunner({ adapterType: "codex_local", target }).adapterType).toBe("codex_local");
state.overridden.clear();
state.overridden.add("paperclip_runner");
expect(resolveNewAgentRunner({ adapterType: "codex_local", target }).adapterType).toBe("codex_local");
});
it.each(qualifiedTargets)("does not select a disabled runner on $platform/$architecture", target => {
state.disabled = ["paperclip_runner"];
expect(resolveNewAgentRunner({ adapterType: "codex_local", target }).adapterType).toBe("codex_local");
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", target, runner: "paperclip" })).toThrow("unavailable");
state.disabled = ["codex_local"];
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", target })).toThrow("Codex is disabled");
});
it.each(["extraArgs", "agentCommand", "networkScope", "filesystemScope", "search"])("rejects incompatible %s without changing runner", field => {
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", target, adapterConfig: { [field]: true } })).toThrow("Legacy runner");
try { resolveNewAgentRunner({ adapterType: "codex_local", target, adapterConfig: { [field]: true } }); }
catch (error) { expect(error).toMatchObject({ status: 422, details: { fields: [field] } }); }
});
it.each([
["agentCommand", "acpAgentCommand", "custom-codex-acp"],
["stateDir", "acpStateDir", "/custom/acp-state"],
["mode", "acpMode", "oneshot"],
["permissionMode", "acpPermissionMode", "deny-all"],
["nonInteractivePermissions", "acpNonInteractivePermissions", "fail"],
["warmHandleIdleMs", "acpWarmHandleIdleMs", 30_000],
])("rejects effective Legacy ACP alias %s/%s before automatic native translation", (canonical, alias, value) => {
const adapterConfig = { engine: "acp", [alias]: value };
expect(buildCodexAcpConfig(adapterConfig)).toHaveProperty(canonical, value);
for (const runner of ["auto", "paperclip"] as const) {
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: [alias] } }));
}
expect(resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner: "legacy" }))
.toEqual({ adapterType: "codex_local", adapterConfig });
});
it.each([
["agentCommand", "acpAgentCommand", "canonical-command", "alias-command"],
["stateDir", "acpStateDir", "/canonical-state", "/alias-state"],
["permissionMode", "acpPermissionMode", "approve-all", "deny-all"],
])("checks the effective canonical ACP %s ahead of its alias", (canonical, alias, value, aliasedValue) => {
const adapterConfig = { engine: "acp", [canonical]: value, [alias]: aliasedValue };
expect(buildCodexAcpConfig(adapterConfig)).toHaveProperty(canonical, value);
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: [canonical] } }));
});
it("uses Legacy ACP canonical precedence and removes compatible or shadowed aliases idempotently", () => {
const adapterConfig = { engine: "acp", mode: "persistent", acpMode: "oneshot",
nonInteractivePermissions: "deny", acpNonInteractivePermissions: "fail",
warmHandleIdleMs: 0, acpWarmHandleIdleMs: 30_000 };
expect(buildCodexAcpConfig(adapterConfig)).toMatchObject({ mode: "persistent", nonInteractivePermissions: "deny", warmHandleIdleMs: 0 });
const resolved = resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target });
expect(resolved).toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { codexPermissionMode: "never" } });
for (const field of Object.keys(adapterConfig)) expect(resolved.adapterConfig).not.toHaveProperty(field);
expect(resolveNewAgentRunner(resolved)).toEqual(resolved);
expect(resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner: "legacy" }))
.toEqual({ adapterType: "codex_local", adapterConfig });
});
it("accepts equivalent ACP default aliases without persisting legacy-only controls", () => {
const adapterConfig = { engine: "acp", acpMode: " persistent ", acpNonInteractivePermissions: " deny ", acpWarmHandleIdleMs: 0 };
expect(buildCodexAcpConfig(adapterConfig)).toMatchObject({ mode: "persistent", nonInteractivePermissions: "deny", warmHandleIdleMs: 0 });
const resolved = resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target });
expect(resolved.adapterType).toBe("paperclip_runner");
for (const field of Object.keys(adapterConfig)) expect(resolved.adapterConfig).not.toHaveProperty(field);
expect(resolveNewAgentRunner(resolved)).toEqual(resolved);
});
it("uses nullish ACP idle precedence instead of treating a canonical zero as absent", () => {
const adapterConfig = { engine: "acp", warmHandleIdleMs: null, acpWarmHandleIdleMs: 30_000 };
expect(buildCodexAcpConfig(adapterConfig)).toHaveProperty("warmHandleIdleMs", 30_000);
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: ["acpWarmHandleIdleMs"] } }));
});
it("uses a nonempty ACP alias when its canonical string is blank", () => {
const adapterConfig = { engine: "acp", permissionMode: " ", acpPermissionMode: " deny-all " };
expect(buildCodexAcpConfig(adapterConfig)).toHaveProperty("permissionMode", "deny-all");
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: ["acpPermissionMode"] } }));
});
it.each([undefined, "acp"])("rejects effective Legacy ACP network denial for engine=%s before native selection", async engine => {
const adapterConfig = { ...(engine ? { engine } : {}), env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" } };
expect(resolveCodexExecutionEngine(adapterConfig).engine).toBe("acp");
expect(buildCodexAcpConfig(adapterConfig)).toMatchObject({ env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" } });
const { codexNetworkAccess } = await import(new URL(
"../../../packages/paperclip-runner/src/drivers/codex/codex-security-config.ts", import.meta.url,
).href);
expect(codexNetworkAccess({ PAPERCLIP_RUNNER_NETWORK_ACCESS: "enabled", PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" })).toBe(true);
for (const runner of [undefined, "auto", "paperclip"] as const) {
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: ["env.PAPERCLIP_CODEX_ACP_NETWORK_ACCESS"] } }));
}
expect(resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner: "legacy" }))
.toEqual({ adapterType: "codex_local", adapterConfig });
expect(adapterConfig.env).toEqual({ PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" });
});
it.each([
{ type: "plain", value: "false" },
{ type: "secret_ref", secretId: "fixture-policy", version: "latest" },
{ type: "user_secret_ref", key: "fixture-policy", version: "latest" },
])("does not discard a bound ACP network restriction or guess its value (%j)", async binding => {
const adapterConfig = { engine: "acp", env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: binding } };
if (binding.type === "plain") {
const resolved = await secretService({} as never).resolveEnvBindings("fixture-company", adapterConfig.env);
expect(buildCodexAcpConfig({ ...adapterConfig, env: resolved.env }))
.toMatchObject({ env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" } });
}
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: ["env.PAPERCLIP_CODEX_ACP_NETWORK_ACCESS"] } }));
expect(resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target, runner: "legacy" }))
.toEqual({ adapterType: "codex_local", adapterConfig });
});
it.each([
{ extraArgs: ["-c", "sandbox_workspace_write.network_access=true"] },
{ extraArgs: [], args: ["-c", "sandbox_workspace_write.network_access=true"] },
])("retains actionable argument errors when Legacy arguments override a network flag (%j)", args => {
const adapterConfig = { engine: "acp", ...args, env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" } };
expect(buildCodexAcpConfig(adapterConfig)).toMatchObject({ env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "true" } });
expect(() => resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }))
.toThrow(expect.objectContaining({ status: 422, details: { code: "agent_runner_config_incompatible", fields: [args.extraArgs.length ? "extraArgs" : "args"] } }));
});
it.each(["true", "False", " false ", { type: "plain", value: "true" }])("keeps compatible ACP environment settings (%j) and native idempotence", value => {
const adapterConfig = { engine: "acp", env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: value, OPENAI_API_KEY: { type: "secret_ref", secretId: "fixture-api-key" } } };
const literal = typeof value === "string" ? value : value.value;
expect(buildCodexAcpConfig({ ...adapterConfig, env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: literal } }))
.toMatchObject({ env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "true" } });
const resolved = resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target });
expect(resolved.adapterType).toBe("paperclip_runner");
expect(resolved.adapterConfig.env).toEqual(adapterConfig.env);
expect(resolveNewAgentRunner(resolved)).toEqual(resolved);
});
it("keeps a saved native profile and an explicitly selected Legacy CLI's unused ACP flag unchanged", () => {
const adapterConfig = { engine: "cli", env: { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" } };
expect(resolveCodexExecutionEngine(adapterConfig).engine).toBe("cli");
expect(resolveNewAgentRunner({ adapterType: "codex_local", adapterConfig, target }).adapterType).toBe("paperclip_runner");
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", env: adapterConfig.env } };
expect(resolveNewAgentRunner(saved)).toEqual(saved);
});
it("preserves explicit historical native profiles, including managed configurations", () => {
for (const config of [{ provider: "acpx", acpxAgent: "claude", model: "claude-sonnet-5" }, { provider: "claude_managed", managedProfileId: "profile", managedAgentsRetentionAcknowledged: true }, { provider: "aws_agentcore", agentCoreProfileId: "profile" }]) expect(resolveNewAgentRunner({ adapterType: "paperclip_runner", adapterConfig: config })).toEqual({ adapterType: "paperclip_runner", adapterConfig: config });
});
it("advertises Legacy for a forced Kubernetes target before lazy environment creation", async () => {
state.settings = { general: { executionMode: "kubernetes" } };
const selected = await resolveAgentRunnerTargetForCompany({} as never, "company");
expect(selected).toEqual({ driver: "sandbox", executionMode: "kubernetes" });
expect(agentRunnerAvailability("codex_local", selected, state.disabled)).toEqual({ supportedRunners: ["legacy"], defaultRunner: "legacy" });
expect(state.kubernetesEnvironment).toHaveBeenCalledWith("company");
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
});
it.each([undefined, "auto"] as const)("preserves Codex %s creation before a forced Kubernetes environment exists", async runner => {
state.settings = { general: { executionMode: "kubernetes" } };
const adapterConfig = { model: "gpt-5.6-sol", extraArgs: ["--search"], env: { OPENAI_API_KEY: { type: "secret_ref", secretId: "synthetic-secret" } } };
const resolved = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", adapterConfig, runner });
expect(resolved).toEqual({ adapterType: "codex_local", adapterConfig });
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
});
it("rejects explicit Paperclip execution on an unqualified forced Kubernetes target without provisioning", async () => {
state.settings = { general: { executionMode: "kubernetes" } };
await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", runner: "paperclip" }))
.rejects.toMatchObject({ status: 422, message: expect.stringContaining("Legacy runner"), details: { code: "agent_runner_unavailable" } });
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
});
it("preserves explicit Legacy and recorded native execution without reading the forced Kubernetes target", async () => {
state.settings = { general: { executionMode: "kubernetes" } };
for (const input of [
{ adapterType: "codex_local", runner: "legacy" as const, adapterConfig: { extraArgs: ["--search"] } },
{ adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol" } },
]) {
expect(await resolveNewAgentRunnerForCompany({} as never, "company", input))
.toEqual({ adapterType: input.adapterType, adapterConfig: input.adapterConfig });
}
expect(state.kubernetesEnvironment).not.toHaveBeenCalled();
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
});
it("retains company checks for an existing forced Kubernetes environment", async () => {
state.settings = { general: { executionMode: "kubernetes" } };
state.kubernetesEnvironment.mockResolvedValue({ id: "kubernetes", driver: "sandbox", status: "active", config: { provider: "kubernetes" } });
state.bindings.mockResolvedValue(["another-company"]);
await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local" }))
.rejects.toMatchObject({ status: 403, details: { code: "environment_company_mismatch" } });
expect(state.ensureKubernetesEnvironment).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
});
it("rejects a foreign environment before resolving credentials or probing its platform", async () => {
state.getEnvironment.mockResolvedValue({ id: "foreign", driver: "ssh", status: "active", config: {} });
state.bindings.mockResolvedValue(["another-company"]);
await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", defaultEnvironmentId: "foreign" })).rejects.toMatchObject({ status: 403, details: { code: "environment_company_mismatch" } });
expect(state.resolveEnvironment).not.toHaveBeenCalled(); expect(state.ssh).not.toHaveBeenCalled();
});
it("uses dispatch's instance default for a null agent override and checks the selected SSH platform", async () => {
state.settings = { defaultEnvironmentId: "default" };
state.getEnvironment.mockResolvedValue({ id: "default", driver: "ssh", status: "active", config: {} });
state.ssh.mockResolvedValue({ stdout: "Linux\naarch64\n" });
const result = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", defaultEnvironmentId: null });
expect(state.getEnvironment).toHaveBeenCalledWith("default"); expect(state.bindings).toHaveBeenCalledWith("default");
expect(result.adapterType).toBe("paperclip_runner");
});
it.each([
["Linux\nx86_64\n", "paperclip", "paperclip_runner"],
["Darwin\narm64\n", "paperclip", "paperclip_runner"],
["Darwin\nx86_64\n", "paperclip", "paperclip_runner"],
["Linux\naarch64\n", "paperclip", "paperclip_runner"],
["Linux\narm64\n", "paperclip", "paperclip_runner"],
["Linux\nx86_64\nglibc\n", "paperclip", "paperclip_runner"],
["Linux\naarch64\nglibc\n", "paperclip", "paperclip_runner"],
["Linux\nx86_64\nglibc\nglibc 2.38\n", "legacy", "codex_local"],
["Linux\naarch64\nglibc\nglibc 2.38\n", "legacy", "codex_local"],
["Linux\nx86_64\nglibc\nglibc 2.39\n", "paperclip", "paperclip_runner"],
["Linux\naarch64\nglibc\nglibc 3.0\n", "paperclip", "paperclip_runner"],
["Linux\nx86_64\nglibc\nmalformed\n", "legacy", "codex_local"],
["Linux\nx86_64\nmusl\n", "legacy", "codex_local"],
["Linux\naarch64\nmusl\n", "legacy", "codex_local"],
["Linux\nx86_64\nunknown\n", "legacy", "codex_local"],
["Linux\narmv7l\n", "legacy", "codex_local"],
])("uses the same selected SSH target for discovery and saving (%s)", async (stdout, expectedRunner, expectedAdapter) => {
state.getEnvironment.mockResolvedValue({ id: "selected", driver: "ssh", status: "active", config: {} });
state.ssh.mockResolvedValue({ stdout });
const discoveredTarget = await resolveAgentRunnerTargetForCompany({} as never, "company", "selected");
expect(agentRunnerAvailability("codex_local", discoveredTarget).defaultRunner).toBe(expectedRunner);
const saved = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", defaultEnvironmentId: "selected" });
expect(saved.adapterType).toBe(expectedAdapter);
expect(state.ssh).toHaveBeenCalledTimes(2);
expect(state.ssh).toHaveBeenCalledWith({}, AGENT_RUNNER_SSH_PLATFORM_COMMAND, { timeoutMs: 10_000 });
});
it("preserves recorded explicit native execution without rechecking creation defaults", async () => {
const recorded = { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol" }, runner: "paperclip" as const };
expect(await resolveNewAgentRunnerForCompany({} as never, "company", recorded)).toEqual({ adapterType: recorded.adapterType, adapterConfig: recorded.adapterConfig });
expect(state.getEnvironment).not.toHaveBeenCalled();
expect(state.ssh).not.toHaveBeenCalled();
});
it.each([
[{ platform: "linux", architecture: "x64", libc: "glibc", glibcVersion: "2.39" }, "paperclip_runner"],
[{ platform: "linux", architecture: "arm64", libc: "glibc", glibcVersion: "3.0" }, "paperclip_runner"],
[{ platform: "linux", architecture: "arm64", libc: "glibc", glibcVersion: "2.38" }, "codex_local"],
[{ platform: "linux", architecture: "x64", libc: "glibc" }, "codex_local"],
[{ platform: "linux", architecture: "arm64", libc: "musl" }, "codex_local"],
[{ platform: "linux", architecture: "unknown", libc: "glibc", glibcVersion: "2.39" }, "codex_local"],
])("uses cached selected-image measurements for sandbox creation/discovery (%j)", async (facts, expected) => {
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active", config: { provider: "daytona" } });
state.sandboxQualification.mockResolvedValue(facts);
const selectedTarget = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability("codex_local", selectedTarget).defaultRunner).toBe(expected === "paperclip_runner" ? "paperclip" : "legacy");
const result = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", defaultEnvironmentId: "sandbox" });
expect(result.adapterType).toBe(expected);
expect(state.ssh).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
if (expected === "codex_local") await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", runner: "paperclip", defaultEnvironmentId: "sandbox" })).rejects.toMatchObject({ status: 422 });
});
it("does not infer a sandbox image architecture from its driver", async () => {
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active", config: { provider: "daytona" } });
const result = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", defaultEnvironmentId: "sandbox" });
expect(result.adapterType).toBe("codex_local"); expect(state.ssh).not.toHaveBeenCalled();
expect(state.sandboxQualification).toHaveBeenCalledWith({}, expect.objectContaining({ id: "sandbox" }), "company");
await expect(resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", runner: "paperclip", defaultEnvironmentId: "sandbox" })).rejects.toMatchObject({ status: 422, details: { code: "agent_runner_unavailable" } });
});
it.each(["codex_local", "claude_local", "opencode_local"])("selects %s on a managed Cloud image without saved platform qualification", async adapterType => {
vi.stubEnv("PAPERCLIP_MANAGED_CONFIG", JSON.stringify({ ...managedCloudConfig, environments: [{ name: "Paperclip Computer", provider: "daytona", config: {} }] }));
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active",
config: { provider: "daytona", snapshot: "replacement-snapshot" }, metadata: { managedByPaperclip: true } });
state.resolveEnvironment.mockRejectedValue(new Error("Saved provider secret is unavailable."));
const selected = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability(adapterType, selected, state.disabled).defaultRunner).toBe("paperclip");
expect(await resolveNewAgentRunnerForCompany({} as never, "company", {
adapterType, defaultEnvironmentId: "sandbox", adapterConfig: { model: adapterType === "opencode_local" ? "provider/model" : undefined },
})).toMatchObject({ adapterType: "paperclip_runner" });
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.resolveEnvironment).not.toHaveBeenCalled();
state.disabled.push("paperclip_runner");
expect(agentRunnerAvailability(adapterType, selected, state.disabled).defaultRunner).toBe("legacy");
});
it.each(["self-hosted", "custom-environment", "captured-template", "stale-marker"])("keeps qualification for a %s sandbox", async kind => {
vi.stubEnv("PAPERCLIP_MANAGED_CONFIG", kind === "self-hosted" ? undefined : JSON.stringify({ ...managedCloudConfig,
environments: kind === "stale-marker" ? [] : [{ name: "Paperclip Computer", provider: "daytona", config: {} }] }));
vi.stubEnv("PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN", undefined);
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active",
config: { provider: "daytona", snapshot: "custom-snapshot" },
metadata: { managedByPaperclip: kind !== "custom-environment" } });
state.activeTemplate.mockResolvedValue(kind === "captured-template" ? { id: "template", provider: "daytona", templateRef: "custom-snapshot" } : null);
const selected = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability("codex_local", selected, state.disabled).defaultRunner).toBe("legacy");
expect(state.sandboxQualification).toHaveBeenCalled();
});
it("ignores a stale captured image and preserves a matching custom image without resolving credentials", async () => {
vi.stubEnv("PAPERCLIP_MANAGED_CONFIG", JSON.stringify({ ...managedCloudConfig, environments: [{ name: "Paperclip Computer", provider: "daytona", config: {} }] }));
const config = { provider: "daytona", snapshot: "replacement-snapshot", reuseLease: false, apiKey: "a6b9e7d0-f886-471a-b461-640dd03e8d0f" };
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active", config, metadata: { managedByPaperclip: true } });
state.resolveEnvironment.mockRejectedValue(new Error("Saved provider secret is unavailable."));
const template = { id: "template", provider: "daytona", templateRef: "custom-snapshot",
sourceEnvironmentConfigFingerprint: fingerprint({ ...config, snapshot: "previous-snapshot" }, { excludePaths: ["apiKey"] }) };
state.activeTemplate.mockResolvedValue(template);
const current = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability("codex_local", current, state.disabled).defaultRunner).toBe("paperclip");
expect(await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "codex_local", runner: "paperclip", defaultEnvironmentId: "sandbox" }))
.toMatchObject({ adapterType: "paperclip_runner" });
expect(state.sandboxQualification).not.toHaveBeenCalled();
state.activeTemplate.mockResolvedValue({ ...template, sourceEnvironmentConfigFingerprint: fingerprint(config, { excludePaths: ["apiKey"] }) });
const custom = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability("codex_local", custom, state.disabled).defaultRunner).toBe("legacy");
expect(state.sandboxQualification).toHaveBeenCalled();
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active", config: { ...config, timeoutMs: "invalid" }, metadata: { managedByPaperclip: true } });
const invalid = await resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox");
expect(agentRunnerAvailability("codex_local", invalid, state.disabled).defaultRunner).toBe("legacy");
expect(state.resolveEnvironment).not.toHaveBeenCalled();
});
it("checks company access before trusting a managed Cloud image", async () => {
vi.stubEnv("PAPERCLIP_MANAGED_CONFIG", "{}");
state.getEnvironment.mockResolvedValue({ id: "sandbox", driver: "sandbox", status: "active",
config: { provider: "daytona" }, metadata: { managedByPaperclip: true } });
state.bindings.mockResolvedValue(["another-company"]);
await expect(resolveAgentRunnerTargetForCompany({} as never, "company", "sandbox"))
.rejects.toMatchObject({ status: 403, details: { code: "environment_company_mismatch" } });
expect(state.resolveEnvironment).not.toHaveBeenCalled();
});
});
describe("Grok runner migration", () => {
const target = { driver: "sandbox", platform: "linux", architecture: "x64", libc: "glibc" as const, glibcVersion: "2.39" };
beforeEach(() => { state.disabled = []; state.overridden.clear(); });
it("selects Grok's native profile and preserves the model and account", () => {
const aiConnection = { mode: "fixed", connectionId: "grok-account" };
const adapterConfig = { model: "grok-4.7", reasoningEffort: "high", command: "grok", aiConnection, cwd: "/workspace" };
expect(paperclipRunnerProfileForHarness("grok_local")).toEqual({ provider: "acpx", acpxAgent: "grok" });
expect(resolveNewAgentRunner({ adapterType: "grok_local", runner: "paperclip", adapterConfig, target }))
.toEqual({ adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "grok", model: "grok-4.7", reasoningEffort: "high", disableWebSearch: true, aiConnection, cwd: "/workspace", acpxPermissionMode: "approve-all", lifecycleMode: "per_turn" } });
});
it.each([undefined, "", "grok-build"])("resolves legacy default model %s before native admission", model => {
expect(resolveNewAgentRunner({ adapterType: "grok_local", runner: "paperclip", target, adapterConfig: { model } }).adapterConfig)
.toMatchObject({ model: "grok-4.7", disableWebSearch: true });
});
it.each([undefined, true, false])("preserves effective legacy web search policy %s", disableWebSearch => {
const result = resolveNewAgentRunner({ adapterType: "grok_local", runner: "paperclip", target, adapterConfig: { disableWebSearch } });
expect(result.adapterConfig.disableWebSearch).toBe(disableWebSearch ?? true);
});
it.each([
{ platform: "darwin", architecture: "x64" },
{ platform: "linux", architecture: "arm64" },
{ platform: "win32", architecture: "x64" },
])("keeps unqualified Grok $platform/$architecture on Legacy", platform => {
expect(agentRunnerAvailability("grok_local", { ...target, ...platform }).supportedRunners).toEqual(["legacy"]);
});
it("retains High effort when switching back and rejects restricted native permission loss", () => {
const selected = resolveNewAgentRunner({ adapterType: "grok_local", runner: "paperclip", target,
adapterConfig: { model: "grok-4.7", reasoningEffort: "high" } });
expect(resolveNewAgentRunner({ ...selected, runner: "legacy", target })).toEqual({
adapterType: "grok_local", adapterConfig: { model: "grok-4.7", reasoningEffort: "high", disableWebSearch: true },
});
expect(() => resolveNewAgentRunner({ ...selected, runner: "legacy", target,
adapterConfig: { ...selected.adapterConfig, acpxPermissionMode: "deny-all" } })).toThrow("cannot honor");
});
it.each(["modelReasoningEffort", "effort"])("preserves native %s when switching to Legacy", key => {
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "grok", model: "grok-4.7", [key]: "high" } };
expect(resolveNewAgentRunner({ ...saved, runner: "legacy", target })).toEqual({
adapterType: "grok_local", adapterConfig: { model: "grok-4.7", reasoningEffort: "high", disableWebSearch: false },
});
});
it.each([{ command: "custom-grok" }, { extraArgs: ["--unsafe"] }, { permissionMode: "dontAsk" }, { alwaysApprove: false }, { maxTurns: 5 }])("rejects incompatible settings %j without mutating them", adapterConfig => {
const before = structuredClone(adapterConfig);
expect(() => resolveNewAgentRunner({ adapterType: "grok_local", runner: "paperclip", adapterConfig, target })).toThrow("cannot honor");
expect(adapterConfig).toEqual(before);
});
});
describe("Codex creation runner contract", () => {
it("graduates qualified harnesses and retains saved provider identity", () => {
expect(paperclipRunnerProfileForHarness("codex_local")).toEqual({ provider: "codex" });
expect(paperclipRunnerProfileForHarness("claude_local")).toEqual({ provider: "acpx", acpxAgent: "claude" });
expect(paperclipRunnerProfileForHarness("opencode_local")).toEqual({ provider: "opencode" });
expect(paperclipRunnerProfileForHarness("pi_local")).toEqual({ provider: "acpx", acpxAgent: "pi" });
for (const harness of ["cursor", "process"]) expect(paperclipRunnerProfileForHarness(harness)).toBeUndefined();
expect(agentHarnessType("paperclip_runner", { provider: "codex" })).toBe("codex_local");
expect(agentHarnessType("paperclip_runner", { provider: "acpx", acpxAgent: "claude" })).toBe("claude_local");
expect(agentHarnessType("paperclip_runner", { provider: "unknown" })).toBe("unknown");
expect(agentHarnessType("paperclip_runner", { provider: "acpx", acpxAgent: "unknown" })).toBe("acpx:unknown");
expect(agentRunner("codex_local")).toBe("legacy");
expect(agentRunner("paperclip_runner")).toBe("paperclip");
});
it("qualifies only the Codex daemon targets shipped in public server packages", () => {
for (const { platform, architecture } of qualifiedTargets) expect(paperclipRunnerSupportsPlatform("codex_local", platform, architecture)).toBe(true);
for (const architecture of ["x64", "arm64"]) {
expect(paperclipRunnerSupportsPlatform("codex_local", "linux", architecture, "glibc")).toBe(true);
expect(paperclipRunnerSupportsPlatform("codex_local", "linux", architecture, "musl")).toBe(false);
expect(paperclipRunnerSupportsPlatform("codex_local", "linux", architecture, "unknown")).toBe(false);
}
for (const [platform, architecture] of [["darwin", "ia32"], ["linux", "arm"], ["win32", "x64"], ["win32", "arm64"], ["freebsd", "x64"]]) expect(paperclipRunnerSupportsPlatform("codex_local", platform, architecture)).toBe(false);
expect(paperclipRunnerSupportsPlatform("claude_local", "darwin", "arm64")).toBe(true);
});
it("keeps auto out of saved update defaults and validates request intent", () => {
expect(createAgentSchema.parse({ name: "Codex", adapterType: "codex_local" }).runner).toBeUndefined();
expect(updateAgentSchema.parse({ title: "same execution" })).not.toHaveProperty("runner");
expect(testAdapterEnvironmentSchema.parse({ runner: "legacy" }).runner).toBe("legacy");
expect(createAgentSchema.safeParse({ name: "Codex", adapterType: "codex_local", runner: "other" }).success).toBe(false);
});
});
describe("Runner-only plugin environments", () => {
const pluginEnvironment = { id: "computer", driver: "plugin", status: "active", config: { pluginKey: "acme.computers", driverKey: "tasks", driverConfig: {} } };
const plugin = (driver: Record<string, unknown>, capabilities = ["environment.drivers.register"]) => ({
id: "plugin-id", status: "ready", manifestJson: { capabilities, environmentDrivers: [{ driverKey: "tasks", displayName: "Tasks", configSchema: {}, ...driver }] },
});
beforeEach(() => {
state.disabled = []; state.overridden.clear(); state.settings = {};
state.getEnvironment.mockReset().mockResolvedValue(pluginEnvironment); state.bindings.mockReset().mockResolvedValue([]);
state.kubernetesEnvironment.mockReset().mockResolvedValue(null); state.ssh.mockReset(); state.sandboxQualification.mockReset();
state.pluginByKey.mockReset().mockResolvedValue(plugin({ supportsTasks: true }));
});
it("offers only Paperclip Runner for Codex and Claude without a platform", async () => {
const target = await resolveAgentRunnerTargetForCompany({} as never, "company", "computer");
expect(target).toEqual({ driver: "plugin", runnerTasks: true });
expect(state.pluginByKey).toHaveBeenCalledWith("acme.computers");
for (const harness of ["codex_local", "claude_local"]) {
expect(agentRunnerAvailability(harness, target)).toEqual({ supportedRunners: ["paperclip"], defaultRunner: "paperclip" });
}
expect(agentRunnerAvailability("gemini_local", target)).toEqual({ supportedRunners: ["legacy"], defaultRunner: "legacy" });
expect(state.ssh).not.toHaveBeenCalled();
});
it("carries warm task capability from the selected driver without provider gates", async () => {
state.pluginByKey.mockResolvedValue(plugin({ supportsTasks: true, warmTaskVersion: 1 }));
expect(await resolveAgentRunnerTargetForCompany({} as never, "company", "computer"))
.toEqual({ driver: "plugin", runnerTasks: true, warmTaskVersion: 1 });
expect(await resolveAgentRunnerTaskDriverForCompany({} as never, "company", "computer"))
.toMatchObject({ driverKey: "tasks", supportsTasks: true, warmTaskVersion: 1 });
});
it.each(["ssh", "sandbox"])("does not qualify a %s runner while inspecting hire defaults", async driver => {
state.getEnvironment.mockResolvedValue({ ...pluginEnvironment, driver, config: { provider: "fake" } });
expect(await resolveAgentRunnerTaskDriverForCompany({} as never, "company", "computer")).toBeUndefined();
expect(state.ssh).not.toHaveBeenCalled();
expect(state.sandboxQualification).not.toHaveBeenCalled();
expect(state.pluginByKey).not.toHaveBeenCalled();
if (driver === "sandbox") await expect(resolveAgentRunnerTargetForCompany({} as never, "company", "computer"))
.rejects.toThrow("The fake sandbox provider cannot execute runs.");
});
it("saves a Claude agent on the plugin environment as Paperclip Runner", async () => {
const saved = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "claude_local", defaultEnvironmentId: "computer" });
expect(saved).toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "claude" } });
const explicit = await resolveNewAgentRunnerForCompany({} as never, "company", { adapterType: "claude_local", runner: "paperclip", defaultEnvironmentId: "computer" });
expect(explicit.adapterType).toBe("paperclip_runner");
});
it.each([
["a driver without Runner tasks", plugin({})],
["a different driver key", { ...plugin({ supportsTasks: true }), manifestJson: { capabilities: ["environment.drivers.register"], environmentDrivers: [{ driverKey: "other", supportsTasks: true }] } }],
["a plugin without the driver capability", plugin({ supportsTasks: true }, [])],
["an unknown plugin", null],
])("keeps the existing plugin target for %s", async (_label, record) => {
state.pluginByKey.mockResolvedValue(record);
const target = await resolveAgentRunnerTargetForCompany({} as never, "company", "computer");
expect(target).toEqual({ driver: "plugin" });
expect(agentRunnerAvailability("claude_local", target)).toEqual({ supportedRunners: ["legacy"], defaultRunner: "legacy" });
});
it("ignores a malformed plugin environment config", async () => {
state.getEnvironment.mockResolvedValue({ ...pluginEnvironment, config: { pluginKey: "acme.computers" } });
expect(await resolveAgentRunnerTargetForCompany({} as never, "company", "computer")).toEqual({ driver: "plugin" });
expect(state.pluginByKey).not.toHaveBeenCalled();
});
});
describe("Claude runner defaults", () => {
const mac = { driver: "local", platform: "darwin", architecture: "arm64" };
beforeEach(() => { state.disabled = []; state.overridden.clear(); });
it.each(qualifiedTargets)("defaults Claude to the qualified $platform $architecture profile", target => {
expect(agentRunnerAvailability("claude_local", target)).toEqual({ supportedRunners: ["paperclip", "legacy"], defaultRunner: "paperclip" });
expect(resolveNewAgentRunner({ adapterType: "claude_local", target })).toEqual({
adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "claude", model: "claude-opus-5-5", acpxPermissionMode: "approve-all", lifecycleMode: "per_turn" },
});
expect(resolveNewAgentRunner({ adapterType: "claude_local", target, runner: "legacy", adapterConfig: { model: "custom", dangerouslySkipPermissions: true } }))
.toEqual({ adapterType: "claude_local", adapterConfig: { model: "custom", dangerouslySkipPermissions: true } });
});
it.each(["x64", "arm64"])("keeps Darwin SSH %s automatic selection Legacy while preserving explicit assets", architecture => {
const target = { driver: "ssh", platform: "darwin", architecture };
expect(agentRunnerAvailability("claude_local", target)).toEqual({ supportedRunners: ["paperclip", "legacy"], defaultRunner: "legacy" });
const config = { model: "custom-model", dangerouslySkipPermissions: false };
expect(resolveNewAgentRunner({ adapterType: "claude_local", target, adapterConfig: config })).toEqual({ adapterType: "claude_local", adapterConfig: config });
const assets = { runnerRemoteProviderPackPath: "/operator/mac-pack", runnerRemoteBinaryPath: "/operator/mac-daemon" };
expect(resolveNewAgentRunner({ adapterType: "claude_local", target, runner: "paperclip", adapterConfig: assets }))
.toMatchObject({ adapterType: "paperclip_runner", adapterConfig: { ...assets, provider: "acpx", acpxAgent: "claude" } });
const saved = { adapterType: "paperclip_runner", adapterConfig: { ...assets, provider: "acpx", acpxAgent: "claude" }, target };
expect(resolveNewAgentRunner(saved)).toEqual({ adapterType: saved.adapterType, adapterConfig: saved.adapterConfig });
});
it.each(["x64", "arm64"])("keeps incompatible Linux Claude %s targets legacy", architecture => {
for (const libc of ["musl", "unknown"] as const) {
expect(agentRunnerAvailability("claude_local", { driver: "local", platform: "linux", architecture, libc }).defaultRunner).toBe("legacy");
}
expect(agentRunnerAvailability("claude_local", { driver: "local", platform: "linux", architecture, libc: "glibc", glibcVersion: "2.17" }).defaultRunner).toBe("legacy");
});
it("saves native Claude with Opus 5.5, preserving credentials and shared settings", () => {
const config = { env: { ANTHROPIC_API_KEY: { type: "secret_ref", secretId: "key" } }, instructionsFilePath: "/workspace/AGENTS.md", timeoutSec: 180, dangerouslySkipPermissions: true };
const result = resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: config });
expect(result).toEqual({ adapterType: "paperclip_runner", adapterConfig: { env: config.env, instructionsFilePath: config.instructionsFilePath, timeoutSec: 180, provider: "acpx", acpxAgent: "claude", model: "claude-opus-5-5", acpxPermissionMode: "approve-all", lifecycleMode: "per_turn" } });
expect(resolveNewAgentRunner(result)).toEqual(result);
expect(config.dangerouslySkipPermissions).toBe(true);
});
it("preserves explicit models and Legacy choices", () => {
const legacy = { adapterType: "claude_local", target: mac, runner: "legacy" as const, adapterConfig: { model: "custom", chrome: true } };
expect(resolveNewAgentRunner(legacy)).toEqual({ adapterType: legacy.adapterType, adapterConfig: legacy.adapterConfig });
expect(resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { model: "custom" } }).adapterConfig.model).toBe("custom");
});
it.each(["claude-sonnet-5-5", "custom-model"])("preserves the plain environment model binding %s", model => {
const env = { ANTHROPIC_MODEL: { type: "plain", value: model } };
const native = resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { env } });
expect(native.adapterConfig.model).toBe(model);
expect(native.adapterConfig.env).toEqual(env);
});
it.each(["ANTHROPIC_MODEL", "CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX", "ANTHROPIC_BEDROCK_BASE_URL"])("rejects unresolved %s before choosing a default", key => {
const env = { [key]: { type: "secret_ref", secretId: "override" } };
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { env } })).toThrow(`env.${key}`);
expect(resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { env, model: "custom-model" } }).adapterConfig.model).toBe("custom-model");
});
it("does not pin the ordinary model over a plain provider-routing binding", () => {
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac,
adapterConfig: { env: { CLAUDE_CODE_USE_BEDROCK: { type: "plain", value: "1" } } } })).toThrow("Legacy runner");
});
it.each(["permissionMode", "acpPermissionMode"])("translates the legacy Claude %s without granting broader permissions", key => {
for (const mode of ["deny-all", "approve-reads", "approve-all"]) {
const native = resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { engine: "acp", [key]: mode } });
expect(native.adapterConfig.acpxPermissionMode).toBe(mode);
expect(native.adapterConfig[key]).toBeUndefined();
}
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { [key]: "deny-all", acpxPermissionMode: "approve-all" } })).toThrow("cannot honor");
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { [key]: "unrecognized" } })).toThrow("Legacy runner");
});
it("retains saved native Claude and allows an explicit compatible Legacy switch", () => {
const saved = { adapterType: "paperclip_runner", adapterConfig: { provider: "acpx", acpxAgent: "claude", model: "custom", acpxPermissionMode: "approve-all", lifecycleMode: "per_turn" } };
expect(resolveNewAgentRunner(saved)).toEqual(saved);
expect(resolveNewAgentRunner({ ...saved, runner: "legacy" })).toEqual({ adapterType: "claude_local", adapterConfig: { model: "custom", dangerouslySkipPermissions: true } });
expect(() => resolveNewAgentRunner({ ...saved, runner: "legacy", adapterConfig: { ...saved.adapterConfig, acpxPermissionMode: "deny-all" } })).toThrow("cannot honor");
});
it.each(["maxTurnsPerRun", "chrome", "warmHandleIdleMs", "acpWarmHandleIdleMs"])("warns and ignores obsolete Claude %s on conversion and native saves", field => {
for (const adapterType of ["claude_local", "paperclip_runner"]) {
const onConfigurationWarnings = vi.fn();
const result = resolveNewAgentRunner({ adapterType, target: mac, runner: "paperclip",
adapterConfig: { provider: "acpx", acpxAgent: "claude", model: "claude-opus-5-5", [field]: 1000 }, onConfigurationWarnings });
expect(result.adapterType).toBe("paperclip_runner");
expect(result.adapterConfig).not.toHaveProperty(field);
expect(onConfigurationWarnings).toHaveBeenCalledWith([expect.objectContaining({ field, code: "runner_option_ignored" })]);
}
});
it.each(["agentCommand", "networkScope"])("rejects unsupported %s without fallback", key => {
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { [key]: "configured" } })).toThrow("Legacy runner");
});
it.each(["low", "medium", "high", "xhigh", "max"])("translates Claude effort %s and preserves it on an explicit Legacy switch", effort => {
const native = resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { effort } });
expect(native.adapterConfig.modelReasoningEffort).toBe(effort);
expect(native.adapterConfig.effort).toBeUndefined();
expect(resolveNewAgentRunner({ ...native, runner: "legacy" }).adapterConfig).toMatchObject({ effort });
});
it("rejects invalid effort and unknown platform defaults", () => {
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac, adapterConfig: { effort: "ultra" } })).toThrow("Claude effort");
expect(agentRunnerAvailability("claude_local", { driver: "sandbox" }).defaultRunner).toBe("legacy");
});
it("keeps unqualified targets and overrides legacy", () => {
expect(agentRunnerAvailability("claude_local", { driver: "local", platform: "win32", architecture: "x64" }).defaultRunner).toBe("legacy");
state.overridden.add("claude_local");
expect(agentRunnerAvailability("claude_local", mac).defaultRunner).toBe("legacy");
state.overridden.clear(); state.disabled = ["claude_local"];
expect(() => resolveNewAgentRunner({ adapterType: "claude_local", target: mac })).toThrow("Claude is disabled");
});
});