fix: preserve saved setup and match sandbox runner targets

Share actual-target artifact selection across remote Codex setup and execution. Keep unchanged saved setup choices outside new-selection rules while retaining runtime and authentication validation. Update the served platform guide.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 14:12:14 -05:00
1 parent 665c155472
commit e79254b2fa
10 files changed
+182 -30

No files matched your search

+27 -2
View File
@@ -4,7 +4,7 @@ Updated 2026-10-12.
## Latest checkpoint: ARM qualification and master integration
The native runtime, protocol, Codex adapter/driver, selection code, and provider
The Rust daemon, protocol, Codex driver, creation policy, and provider
requirements remain unchanged from qualified source
`a200495cc7488f4d59da570407e516cf702e5af8`. Its verified 18-package archive
retains that literal identity. All four hosted daemon producers, Linux x64 clean
@@ -53,7 +53,32 @@ The last verified preview restart serves `2026.1005.0+352.git.0690801f13` at
configurations and both historical native runs. The downloaded Apple Silicon
daemon requires no source build or binary override.
Next action: require fresh CI/review on the integrated candidate, refresh the
Fresh review at `665c155` reports three valid findings. Remote sandbox setup and
execution now share the existing actual-target OS/architecture resolver rather
than selecting the Mac controller's daemon. Thirteen new baseline failures
reproduce the issue; 124 target/setup/backend and retained-artifact/version
controls pass after the fix, with server typecheck. Header, digest, preinstalled
artifact, and remote build-metadata verification stay strict. Foreign artifacts
are read/hashed on the controller and executed through the remote launcher.
Local and non-Codex paths remain unchanged. Receipt:
`/private/tmp/codex-default-sandbox-target-fix-receipt.json`.
Unchanged saved setup choices now bypass only new-selection resolution, retaining
later secret, company/environment, authentication, and runtime validation. The
actual disabled legacy baseline returned 422 before its probe. Treatment passes
175 existing controls plus a native-provider switch negative, with server
typecheck. New, explicit, draft, and changed-harness choices keep the disabled
adapter policy. Receipt: `/private/tmp/pr2-disabled-saved-codex-owned-receipt.json`.
The actual served Codex guide reproduces stale Mac/ARM legacy text at `665c155`.
It now uses the shared GNU libc floor, lists qualified Linux/Mac architectures,
distinguishes legacy fields, and preserves existing-runner and failure semantics.
Adapter typecheck passes; the updated served guide needs final-preview proof.
These corrections update server setup/execution and guide source. The qualified
`a200495` package startup and historical task evidence retain their literal
component boundaries; no new managed live proof or paid turn is claimed.
Next action: require fresh CI/review on the corrected integrated candidate, refresh the
matched preview, and publish final conclusions in the canonical PR's evidence
index. Human code-owner approval, managed staging onboarding, and actual
post-merge Cloud composition remain outstanding. No merge or production
+4 -1
View File
@@ -1,6 +1,7 @@
import {
PAPERCLIP_RUNNER_DEFAULT_MODELS,
} from "@paperclipai/adapter-utils";
import { PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION } from "@paperclipai/shared";
export const type = "codex_local";
export const label = "Codex";
@@ -212,7 +213,9 @@ export const models = [
export const agentConfigurationDoc = `# codex_local agent configuration
New agents select this Codex harness with adapterType: codex_local. On qualified Linux x64 targets, automatic selection stores adapterType: paperclip_runner with provider: codex. macOS, Windows, and Linux ARM retain legacy defaults until their runner artifacts are qualified. Other harness defaults are unchanged. Set the request-level runner: legacy to use this adapter's execution and custom settings. Existing agents and reviewed pending hires retain their saved runner; runtime errors never silently switch runners.
New agents select the Codex harness with adapterType: codex_local and an optional request-level runner: auto, paperclip, or legacy. Automatic selection uses Paperclip Runner on qualified Linux x64/ARM64 targets with GNU libc ${PAPERCLIP_RUNNER_MINIMUM_GLIBC_VERSION} or newer and macOS arm64/x64 targets. It stores adapterType: paperclip_runner with provider: codex. Windows, 32-bit ARM, Alpine/musl, and older or unknown Linux libc retain automatic legacy selection. Qualification and artifact checks use the selected execution environment, including its architecture, rather than the controller's platform. Other harness defaults are unchanged.
Set runner: legacy to retain this adapter's execution and custom settings. Explicit runner: paperclip requires a qualified target. Existing agents and reviewed pending hires retain their saved runner; authentication, model, dependency, and runtime errors never silently switch runners. The fields below describe legacy codex_local execution. Native creation translates supported settings and rejects unsupported settings with the explicit Legacy runner option.
Adapter: codex_local
@@ -657,6 +657,78 @@ describe("agent routes adapter validation", () => {
expect(res.status, JSON.stringify(res.body)).toBe(200);
});
it.each(["pass", "fail"] as const)("tests saved legacy Codex on a disabled adapter and retains its %s runtime verdict", async (status) => {
const adapterConfig = { model: "gpt-5.4", extraArgs: ["--legacy-setting"], env: { CODEX_HOME: "/saved-codex-home" } };
mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterConfig });
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
const probe = vi.fn(async () => ({ adapterType: "codex_local", status, testedAt: new Date(0).toISOString(),
checks: status === "fail" ? [{ code: "adapter_auth_missing", level: "error" as const, message: "Sign in to the saved Codex account." }] : [] }));
const { registerServerAdapter } = await import("../adapters/index.js");
registerServerAdapter({ ...externalAdapter, type: "codex_local", testEnvironment: probe });
try {
const app = await createApp();
const res = await requestApp(app, baseUrl => request(baseUrl)
.post("/api/companies/company-1/adapters/codex_local/test-environment")
.send({ agentId: "11111111-1111-4111-8111-111111111111", adapterConfig: {
...adapterConfig, env: { CODEX_HOME: { type: "plain", value: "***REDACTED***" } },
} }));
expect(res.status, JSON.stringify(res.body)).toBe(200);
expect(res.body).toMatchObject({ adapterType: "codex_local", status });
expect(probe).toHaveBeenCalledOnce();
expect(probe).toHaveBeenCalledWith(expect.objectContaining({ adapterType: "codex_local", config: adapterConfig }));
expect(mockSecretService.normalizeAdapterConfigForPersistence).toHaveBeenCalledWith("company-1", adapterConfig, expect.objectContaining({ adapterType: "codex_local" }));
expect(mockAgentService.create).not.toHaveBeenCalled();
expect(mockAgentService.update).not.toHaveBeenCalled();
} finally { await unregisterTestAdapter("codex_local"); }
});
it.each(["draft", "explicit legacy", "explicit auto", "explicit native", "changed harness"] as const)("rejects a disabled Codex setup selection for %s", async (mode) => {
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
if (mode === "changed harness") mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterType: "external_test" });
const probe = vi.fn();
const { registerServerAdapter } = await import("../adapters/index.js");
registerServerAdapter({ ...externalAdapter, type: "codex_local", testEnvironment: probe });
try {
const runner = mode === "explicit legacy" ? "legacy" : mode === "explicit auto" ? "auto" : mode === "explicit native" ? "paperclip" : undefined;
const app = await createApp();
const res = await requestApp(app, baseUrl => request(baseUrl)
.post("/api/companies/company-1/adapters/codex_local/test-environment")
.send({ adapterConfig: {}, ...(mode === "draft" ? {} : { agentId: "11111111-1111-4111-8111-111111111111" }), ...(runner ? { runner } : {}) }));
expect(res.status, JSON.stringify(res.body)).toBe(422);
expect(res.body.details).toMatchObject({ code: "agent_runner_unavailable" });
expect(probe).not.toHaveBeenCalled();
expect(mockSecretService.normalizeAdapterConfigForPersistence).not.toHaveBeenCalled();
} finally { await unregisterTestAdapter("codex_local"); }
});
it("still rejects new Codex creation when its adapter is disabled", async () => {
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
const app = await createApp();
const res = await requestApp(app, baseUrl => request(baseUrl)
.post("/api/companies/company-1/agents").send({ name: "New Codex", adapterType: "codex_local" }));
expect(res.status, JSON.stringify(res.body)).toBe(422);
expect(res.body.details).toMatchObject({ code: "agent_runner_unavailable" });
expect(mockAgentService.create).not.toHaveBeenCalled();
});
it("rechecks selection policy when a saved native adapter switches its underlying harness", async () => {
mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterType: "paperclip_runner", adapterConfig: { provider: "codex" } });
mockInstanceSettingsService.getExperimental.mockResolvedValue({ enableNativeRunner: false });
const probe = vi.fn();
const { registerServerAdapter } = await import("../adapters/index.js");
registerServerAdapter({ ...externalAdapter, type: "paperclip_runner", testEnvironment: probe });
try {
const app = await createApp();
const res = await requestApp(app, baseUrl => request(baseUrl)
.post("/api/companies/company-1/adapters/paperclip_runner/test-environment")
.send({ agentId: "11111111-1111-4111-8111-111111111111", adapterConfig: { provider: "acpx", acpxAgent: "claude" } }));
expect(res.status, JSON.stringify(res.body)).toBe(422);
expect(res.body.details).toMatchObject({ code: "paperclip_runner_rollout_disabled" });
expect(probe).not.toHaveBeenCalled();
expect(mockSecretService.normalizeAdapterConfigForPersistence).not.toHaveBeenCalled();
} finally { await unregisterTestAdapter("paperclip_runner"); }
});
it("still creates an agent on an adapter that is registered and enabled", async () => {
const { registerServerAdapter } = await import("../adapters/index.js");
registerServerAdapter(externalAdapter);
+12 -6
View File
@@ -3142,15 +3142,21 @@ export function agentRoutes(
? restoreRedactedAgentEnv(inputAdapterConfig, savedAgent.adapterConfig)
: inputAdapterConfig;
}
const selection = await resolveNewAgentRunnerForCompany(db, companyId, {
adapterType: type, adapterConfig: adapterConfigForTest,
runner: req.body.runner ?? (savedAgent && savedAgent.adapterType === type ? (type === "paperclip_runner" ? undefined : "legacy") : undefined),
defaultEnvironmentId: requestedEnvironmentId,
});
const retainsSavedExecution = savedAgent && req.body.runner === undefined
&& savedAgent.adapterType === type
&& agentHarnessType(type, adapterConfigForTest) === agentHarnessType(savedAgent.adapterType, savedAgent.adapterConfig);
// Preserve saved execution; the adapter probe below still validates runtime readiness.
const selection = retainsSavedExecution
? { adapterType: type, adapterConfig: adapterConfigForTest }
: await resolveNewAgentRunnerForCompany(db, companyId, {
adapterType: type, adapterConfig: adapterConfigForTest,
runner: req.body.runner,
defaultEnvironmentId: requestedEnvironmentId,
});
type = selection.adapterType;
adapterConfigForTest = selection.adapterConfig;
const adapter = requireServerAdapter(type);
if (!savedAgent || req.body.runner !== undefined || type !== savedAgent.adapterType) await assertSelectableAdapterType(type, adapterConfigForTest);
if (!retainsSavedExecution) await assertSelectableAdapterType(type, adapterConfigForTest);
if (poolBinding?.mode === "router") {
await validateManagedAgentBinding(req, companyId, savedAgentId ?? "", type, adapterConfigForTest, poolBinding, requestedEnvironmentId, false, !savedAgentId);
res.json({ adapterType: type, status: "warn", testedAt: new Date().toISOString(), checks: [{ code: "ai_connection_pool_task_test_required", level: "warn", message: "Pool configuration is available. Run a task to verify the selected account and harness; this preview does not allocate a task or contact a provider." }] });
@@ -12,7 +12,7 @@ import {
resolveRemotePaperclipRunnerBinary,
} from "./native-codex-runner.js";
afterEach(() => vi.restoreAllMocks());
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); });
describe("installed runner selection", () => {
it("uses the runner's platform-aware packaged selection", async () => {
@@ -46,7 +46,7 @@ describe("installed runner selection", () => {
});
});
describe("selected SSH daemon target", () => {
describe("selected remote daemon target", () => {
function header(target: string) {
const bytes = Buffer.alloc(64);
if (target.startsWith("darwin")) {
@@ -72,6 +72,21 @@ describe("selected SSH daemon target", () => {
expect(target.execute).toHaveBeenCalledExactlyOnceWith({ command: "sh", args: ["-c", "uname -s; uname -m"], cwd: "/selected-workspace", bypassSession: true, timeoutMs: 10_000 });
} finally { await rm(root, { recursive: true, force: true }); }
});
it.each(["arm64", "x64"].flatMap(controllerArch => ["arm64", "x64"].map(targetArch => ({ controllerArch, targetArch }))))(
"selects the actual Linux $targetArch sandbox bytes on a Mac $controllerArch controller", async ({ controllerArch, targetArch }) => {
const root = await mkdtemp(join(tmpdir(), "paperclip-sandbox-daemon-"));
const binary = join(root, "paperclip-runnerd");
try {
vi.stubGlobal("process", { ...process, platform: "darwin", arch: controllerArch });
await writeFile(binary, header(`linux-${targetArch}`));
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(binary);
const target = remote(`Linux\n${targetArch === "arm64" ? "aarch64" : "x86_64"}\n`);
expect(await resolveRemotePaperclipRunnerBinary({ runner: target, remoteCwd: "/sandbox-workspace" })).toBe(binary);
expect(select).toHaveBeenCalledExactlyOnceWith("linux", targetArch);
expect(target.execute).toHaveBeenCalledExactlyOnceWith({ command: "sh", args: ["-c", "uname -s; uname -m"], cwd: "/sandbox-workspace", bypassSession: true, timeoutMs: 10_000 });
} finally { await rm(root, { recursive: true, force: true }); }
},
);
it.each(["matching", "wrong-architecture", "malformed", "missing"])("verifies an explicit controller-owned remote artifact (%s)", async scenario => {
const root = await mkdtemp(join(tmpdir(), "paperclip-ssh-daemon-"));
const binary = join(root, "configured-runnerd");
@@ -208,7 +208,7 @@ export function resolvePaperclipRunnerBinary(
);
}
/** Select controller-owned bytes for the selected SSH host, never its controller. */
/** Select controller-owned bytes for the actual remote host, never its controller. */
export async function resolveRemotePaperclipRunnerBinary(input: {
runner: CommandManagedRuntimeRunner;
remoteCwd: string;
@@ -220,7 +220,7 @@ export async function resolveRemotePaperclipRunnerBinary(input: {
const platform = os === "Linux" ? "linux" : os === "Darwin" ? "darwin" : null;
const architecture = arch === "x86_64" ? "x64" : arch === "aarch64" || arch === "arm64" ? "arm64" : null;
if (probe.exitCode !== 0 || probe.timedOut || !platform || !architecture) {
throw new Error("runner_remote_artifact_platform_mismatch: could not identify a supported remote OS and architecture; check the SSH host and retry");
throw new Error("runner_remote_artifact_platform_mismatch: could not identify a supported remote OS and architecture; check the selected execution environment and retry");
}
const configured = input.configuredPath?.trim();
if (configured && !isAbsolute(configured)) throw new Error("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH must be an absolute path");
@@ -12218,10 +12218,8 @@ describe("runnerd provider runtime wiring", () => {
// The mocked remote executes metadata probes; artifact staging only needs bytes.
// Keep this regression independent of a locally compiled Rust runner binary.
const controllerArtifact = join(isolatedStateDirectory, "paperclip-runnerd");
if (needsReplacement || retained || image === "preinstalled-exact") {
await writeFile(controllerArtifact, "fixture runner artifact");
state.resolveRunnerBinary.mockReturnValueOnce(controllerArtifact);
}
await writeFile(controllerArtifact, "fixture runner artifact");
state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
const onLog = vi.fn(async () => undefined);
const syncIn = vi.fn(async () => undefined);
const remoteExecute = vi.fn(
@@ -12430,21 +12428,27 @@ describe("runnerd provider runtime wiring", () => {
});
it.each(["explicit", "linux-arm64", "linux-x64"])("binds an SSH launch to the selected controller-owned runner artifact (%s)", async mode => {
it.each(["ssh", "sandbox"].flatMap(transport => ["explicit", "linux-arm64", "linux-x64", "missing", "mismatch"].map(mode => ({ transport, mode }))))
("binds a $transport launch to the selected controller-owned runner artifact ($mode)", async ({ transport, mode }) => {
const remoteCwd = "/home/daytona/paperclip-workspace";
const controllerArtifact = `/controller/artifacts/${mode}/paperclip-runnerd`;
state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
const explicit = mode === "explicit" || mode === "missing" || mode === "mismatch";
const error = mode === "missing" ? "runner_remote_artifact_unavailable" : mode === "mismatch" ? "runner_remote_artifact_platform_mismatch" : null;
if (error) state.resolveRemoteRunnerBinary.mockRejectedValue(new Error(error));
else state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
const remoteExecution = {
...execution,
binding: { ...execution.binding, runId: "run-remote-runner-artifact" },
workspace: { ...execution.workspace, cwd: "/host/paperclip-workspace" },
} as NativeExecutionInputV1;
await createRunnerdBackend({
const backend = createRunnerdBackend({
db: leaseDb(remoteExecution),
execution: remoteExecution,
runnerInstanceId: "runner",
runnerExecutionTarget: {
runnerExecutionTarget: transport === "sandbox" ? {
kind: "remote", transport: "sandbox", providerKey: "test", remoteCwd, runner: { execute: vi.fn() },
} : {
kind: "remote",
transport: "ssh",
remoteCwd,
@@ -12459,15 +12463,25 @@ describe("runnerd provider runtime wiring", () => {
strictHostKeyChecking: true,
},
},
...(mode === "explicit" ? { runnerRemoteBinaryPath: controllerArtifact } : {}),
...(explicit ? { runnerRemoteBinaryPath: controllerArtifact } : {}),
});
if (error) {
await expect(backend).rejects.toThrow(error);
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: controllerArtifact }));
expect(state.resolveRunnerBinary).not.toHaveBeenCalled();
expect(state.createBackend).not.toHaveBeenCalled();
expect(state.createTransport).not.toHaveBeenCalled();
return;
}
await backend;
state.createTransport.mockClear();
state.createBackend.mock.calls.at(-1)![1].codexTransportFactory!();
expect(state.createTransport).toHaveBeenCalledWith(
expect.objectContaining({ runnerBinary: controllerArtifact }),
);
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: mode === "explicit" ? controllerArtifact : undefined }));
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: explicit ? controllerArtifact : undefined }));
expect(state.resolveRunnerBinary).not.toHaveBeenCalled();
});
@@ -11551,7 +11551,7 @@ async function createRunnerdBackendWithinSessionClaim(
// filesystem; the remote launcher separately owns the sandbox command path.
// When an explicit remote artifact is configured, prepareRemoteRunner stages
// these exact bytes at remoteBinary before launch.
const targetMatchedCodexArtifact = remoteTarget?.transport === "ssh" && input.execution.provider.kind === "codex";
const targetMatchedCodexArtifact = remoteTarget !== null && input.execution.provider.kind === "codex";
const controllerRunnerBinary = targetMatchedCodexArtifact
? await resolveRemotePaperclipRunnerBinary({ runner: remoteCommandRunner!, remoteCwd: remoteTarget.remoteCwd, configuredPath: input.runnerRemoteBinaryPath })
: remoteTarget
@@ -35,6 +35,7 @@ describe("Codex selected native account verification", () => {
vi.stubEnv("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH", "");
});
afterEach(async () => {
vi.unstubAllGlobals();
vi.unstubAllEnvs();
for (const [input] of nativeProbe.mock.calls) if (input.runtimeDirectory) await rm(input.runtimeDirectory, { recursive: true, force: true });
});
@@ -238,7 +239,12 @@ describe("Codex selected native account verification", () => {
if (confirmed) await expect(stat(root)).rejects.toThrow();
else expect((await stat(root)).isDirectory()).toBe(true);
});
it("uses the same preparation and controller PRP for a sandbox, without a guest provider pack", async () => {
it.each(["arm64", "x64"].flatMap(controllerArch => ["arm64", "x64"].flatMap(targetArch => [false, true].map(explicit => ({ controllerArch, targetArch, explicit })))))
("uses target $targetArch artifacts and controller PRP from Mac $controllerArch for sandbox setup (override=$explicit)", async ({ controllerArch, targetArch, explicit }) => {
vi.stubGlobal("process", { ...process, platform: "darwin", arch: controllerArch });
const selectedBinary = `/controller/artifacts/linux-${targetArch}/paperclip-runnerd`;
selectedRemoteRunner.mockResolvedValue(selectedBinary);
if (explicit) vi.stubEnv("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH", selectedBinary);
nativeProbe.mockImplementation(async input => {
const registration = await input.transportOptions.controlPlaneRegistration({});
expect(registration.connection.mode).toBe("connect");
@@ -248,10 +254,23 @@ describe("Codex selected native account verification", () => {
const target = { ...context.executionTarget, runner: { execute: sshExecute } };
expect((await testNativeRunnerAuthentication({ ...context, executionTarget: target }, "codex", "gpt-6.1-sol")).status).toBe("pass");
expect(sshRunner).not.toHaveBeenCalled();
expect(nativeArtifacts).toHaveBeenCalledWith(expect.objectContaining({ target, runner: target.runner }));
expect(selectedRemoteRunner).toHaveBeenCalledExactlyOnceWith({ runner: target.runner, remoteCwd: target.remoteCwd, configuredPath: explicit ? selectedBinary : null });
expect(selectedRunner).not.toHaveBeenCalled();
expect(nativeArtifacts).toHaveBeenCalledWith(expect.objectContaining({ target, runner: target.runner, controllerRunnerBinary: selectedBinary, targetPlatformArtifactVerified: true }));
expect(nativeProbe.mock.calls[0][0].transportOptions.runnerBinary).toBe(selectedBinary);
expect(nativeArtifacts.mock.results[0].value.prepare).toHaveBeenCalledWith("dial_wss");
expect(execute).not.toHaveBeenCalled();
});
it.each(["runner_remote_artifact_unavailable", "runner_remote_artifact_platform_mismatch"])("fails sandbox %s before setup or a controller fallback", async code => {
selectedRemoteRunner.mockRejectedValue(new Error(code));
const target = { ...context.executionTarget, runner: { execute: sshExecute } };
const result = await testNativeRunnerAuthentication({ ...context, executionTarget: target }, "codex", "gpt-6.1-sol");
expect(result.status).toBe("fail");
expect(result.checks[0].message).toContain(code);
expect(selectedRunner).not.toHaveBeenCalled(); expect(nativeArtifacts).not.toHaveBeenCalled();
expect(nativeProbe).not.toHaveBeenCalled(); expect(sshExecute).not.toHaveBeenCalled();
expect(execute).not.toHaveBeenCalled();
});
it("prepares listener artifacts before acquiring sandbox ingress and owns its release", async () => {
const order: string[] = [];
const close = vi.fn();
@@ -63,11 +63,9 @@ async function probeRemoteNativeCodex(options: {
const remoteCodexNpmSpec = process.env.PAPERCLIP_RUNNER_REMOTE_CODEX_NPM_SPEC?.trim() || null;
const remoteCodexBinary = remoteCodexNpmSpec ? posix.join(remoteRoot, "harnesses", "codex", "node_modules", ".bin", "codex") : posix.join(remoteRoot, "bin", "codex");
const explicitRunner = process.env.PAPERCLIP_RUNNER_REMOTE_BINARY_PATH?.trim() || null;
const controllerRunnerBinary = target.transport === "ssh"
? await resolveRemotePaperclipRunnerBinary({ runner, remoteCwd: target.remoteCwd, configuredPath: explicitRunner })
: explicitRunner || resolvePaperclipRunnerBinary();
const controllerRunnerBinary = await resolveRemotePaperclipRunnerBinary({ runner, remoteCwd: target.remoteCwd, configuredPath: explicitRunner });
const artifacts = createRemoteNativeArtifactPreparation({ target, runner, remoteBinary, controllerRunnerBinary, remoteRuntimeRoot: remoteRoot, remoteCodexBinary,
targetPlatformArtifactVerified: target.transport === "ssh",
targetPlatformArtifactVerified: true,
runnerRemoteBinaryPath: explicitRunner, runnerRemoteCodexPath: process.env.PAPERCLIP_RUNNER_REMOTE_CODEX_PATH?.trim(), runnerRemoteCodexNpmSpec: remoteCodexNpmSpec, model: options.model });
const requireSuccess = (result: { timedOut: boolean; exitCode: number | null }, message: string) => {
if (result.timedOut || result.exitCode !== 0) throw new Error(message);