fix(boat): retain verified ACPX provider packs across deployments

This commit is contained in:
Dotta committed 2026-10-10 20:44:45 -05:00
1 parent a946f0a084
commit 66ac692e44
4 files changed
+331 -2

No files matched your search

@@ -355,6 +355,7 @@ import {
REMOTE_RUNNER_CHILD_LAUNCH_SCRIPT,
verifyRemoteRunnerReattachment,
readRemoteProviderPackManifest,
validateRemoteProviderPackIdentity,
readBundledRemoteProviderPackManifest,
providerSessionIdentityFromDurableProviderState,
durableProviderCheckpointFailureReason,
@@ -1468,6 +1469,13 @@ describe("remote provider pack manifest", () => {
}),
);
await writeManifest();
const retainedManifest = readRemoteProviderPackManifest(root);
expect(validateRemoteProviderPackIdentity(retainedManifest).digest).toBe(retainedManifest.digest);
expect(() => validateRemoteProviderPackIdentity({ ...retainedManifest, digest: `sha256:${"0".repeat(64)}` }))
.toThrow("manifest digest mismatch");
const changedPins = structuredClone(retainedManifest);
changedPins.payload.pins = { ...changedPins.payload.pins, nodeMinimum: "0.0.0" as typeof changedPins.payload.pins.nodeMinimum };
expect(() => validateRemoteProviderPackIdentity(changedPins)).toThrow("pins or source revision");
expect(readRemoteProviderPackManifest(root).payload.pins.opencode).toBe(
"1.18.34",
);
@@ -42,7 +42,7 @@ import { nativeCompletionFeedback } from "./native-completion-feedback.js";
import { hasAcknowledgedNativeReassignmentStopIntent, hasAcknowledgedNativeStopIntent } from "../acknowledged-native-stop.js";
import { stoppedCodexTurnIsTextOnly } from "./stopped-codex-turn.js";
import { prepareVerifiedRemoteProviderPack } from "./remote-provider-pack.js";
import { computerProviderPackCachePath, prepareComputerProviderPackCache } from "./remote-provider-pack-cache.js";
import { computerProviderPackCachePath, prepareComputerProviderPackCache, pinnedComputerProviderPack, computerAcpxLaunchProfileDigest, readPinnedComputerProviderMetadata, assertPinnedComputerProviderState } from "./remote-provider-pack-cache.js";
import { selectRemotePiCompanion } from "./remote-pi-companion.js";
import { readNativeLocalProcessStop, PROCESS_START_REQUESTED } from "../native-local-process-stop.js";
import { remoteLeaseCleanupScope } from "../remote-execution-termination.js";
@@ -10230,6 +10230,10 @@ function readRemoteProviderPackIdentity(packRoot: string, verifyControllerFiles:
{ cause: error },
);
}
return validateRemoteProviderPackIdentity(manifest, packRoot, verifyControllerFiles);
}
export function validateRemoteProviderPackIdentity(manifest: RemoteProviderPackManifest, packRoot = "", verifyControllerFiles = false): RemoteProviderPackManifest {
const payload = manifest?.payload;
if (
manifest?.schema !== REMOTE_PROVIDER_PACK_SCHEMA ||
@@ -12032,7 +12036,31 @@ async function createRunnerdBackendWithinSessionClaim(
const useBundledRemoteImageAssets = requiresRemoteProviderPack && !configuredProviderPackRoot &&
(input.execution.provider.kind === "opencode" ||
(input.execution.provider.kind === "acpx" && input.execution.provider.agent === "cursor"));
if (useBundledRemoteImageAssets) {
const retainedComputerPack = remoteTarget?.transport === "computer" &&
input.execution.provider.kind === "acpx" && durableBinding && durableIdentity && remoteCommandRunner && remoteStorage
? pinnedComputerProviderPack({
agentHome: remoteTarget.fileAuthority.agentHome, identity: durableIdentity,
provider: input.execution.provider,
control: JSON.parse(readBoundedNativeFile(resolve(root, "control-plane", "control-plane-state.json"),
NATIVE_CONTROL_PLANE_STATE_MAX_BYTES, "runner_durable_identity_too_large").toString("utf8")),
}) : null;
if (retainedComputerPack && remoteCommandRunner && remoteStorage) {
// The local attachment template chooses the digest. Remote content cannot
// authorize a newer pack or rewrite the existing provider's launch identity.
const [manifest, state] = await readPinnedComputerProviderMetadata({ runner: remoteCommandRunner,
packRoot: retainedComputerPack.root, stateDirectory: posix.join(remoteStorage.sessionRoot, "runner") });
expectedProviderPackManifest = validateRemoteProviderPackIdentity(manifest as RemoteProviderPackManifest);
if (expectedProviderPackManifest.digest !== retainedComputerPack.digest ||
expectedProviderPackManifest.payload.target.platform !== "linux" ||
expectedProviderPackManifest.payload.target.architecture !== "x64")
throw new Error("runner_remote_provider_pack_provenance_mismatch");
const launchDigest = computerAcpxLaunchProfileDigest({ authorityDigest: retainedComputerPack.digest,
command: retainedComputerPack.command, sidecar: retainedComputerPack.sidecar,
commandSha256: expectedProviderPackManifest.payload.artifacts.nodeCommand.sha256,
sidecarSha256: expectedProviderPackManifest.payload.artifacts.acpxSidecar.sha256 });
assertPinnedComputerProviderState({ state, launchProfileDigest: launchDigest,
normalizedSessionId: nativeSessionKey(input.execution), command: retainedComputerPack.command, sidecar: retainedComputerPack.sidecar });
} else if (useBundledRemoteImageAssets) {
expectedProviderPackManifest = readBundledRemoteProviderPackManifest();
} else if (requiresRemoteProviderPack) {
if (
@@ -12608,6 +12636,7 @@ async function createRunnerdBackendWithinSessionClaim(
runner: remoteCommandRunner,
verify: (root) => measureNativeRunnerSpan(input.trace, "provider_pack.verify", () => verifyRemoteProviderPack(root)),
stage: async (root, runner) => {
if (retainedComputerPack) throw new Error("runner_remote_provider_pack_retained_artifacts_unavailable");
if (!configuredProviderPackRoot) {
throw new Error("runner_remote_provider_artifact_incompatible: configure the build-owned provider pack for first use on this computer");
}
@@ -0,0 +1,204 @@
import { spawn } from "node:child_process";
import { promises as fs } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { CommandManagedRuntimeRunner } from "@paperclipai/adapter-utils/command-managed-runtime";
import { computerProviderPackCachePath, prepareComputerProviderPackCache, pinnedComputerProviderPack, computerAcpxLaunchProfileDigest, readPinnedComputerProviderMetadata, assertPinnedComputerProviderState } from "./remote-provider-pack-cache.js";
const roots: string[] = [];
const digest = `sha256:${"a".repeat(64)}`;
describe("controller-pinned computer provider pack", () => {
it("checks the retained provider's session and exact launch profile without rewriting it", () => {
const state = { schema: "paperclip.runner.acpx-provider-state.v3", launchProfileDigest: digest,
descriptor: { normalizedSessionId: "session", sidecarCommand: "/pack/node", sidecarArgs: ["/pack/sidecar"] } };
const input = { state, normalizedSessionId: "session", launchProfileDigest: digest, command: "/pack/node", sidecar: "/pack/sidecar" };
expect(() => assertPinnedComputerProviderState(input)).not.toThrow();
const before = JSON.stringify(state);
expect(() => assertPinnedComputerProviderState({ ...input, normalizedSessionId: "foreign" })).toThrow("provenance_mismatch");
expect(() => assertPinnedComputerProviderState({ ...input, launchProfileDigest: `sha256:${"b".repeat(64)}` })).toThrow("provenance_mismatch");
expect(JSON.stringify(state)).toBe(before);
});
it.each(["valid", "pack-parent-symlink", "state-parent-symlink", "hardlink"])("reads bounded metadata with %s", async (kind) => {
const home = await fs.realpath(await fs.mkdtemp(join(tmpdir(), "provider-provenance-")));
roots.push(home);
let packRoot = join(home, "pack"); let stateDirectory = join(home, "runner");
await fs.mkdir(packRoot); await fs.mkdir(stateDirectory);
await fs.writeFile(join(packRoot, "provider-pack.json"), JSON.stringify({ digest }));
await fs.writeFile(join(stateDirectory, "acpx-provider-state.json"), JSON.stringify({ launchProfileDigest: digest }));
if (kind === "pack-parent-symlink") { await fs.symlink(packRoot, join(home, "pack-link")); packRoot = join(home, "pack-link"); }
if (kind === "state-parent-symlink") { await fs.symlink(stateDirectory, join(home, "runner-link")); stateDirectory = join(home, "runner-link"); }
if (kind === "hardlink") await fs.link(join(packRoot, "provider-pack.json"), join(home, "shared.json"));
const result = readPinnedComputerProviderMetadata({ runner, packRoot, stateDirectory });
if (kind === "valid") await expect(result).resolves.toEqual([{ digest }, { launchProfileDigest: digest }]);
else await expect(result).rejects.toThrow("provenance_unavailable");
});
it("matches the runner's length-prefixed launch profile contract", () => {
const profile = { authorityDigest: digest, command: "/pack/node", commandSha256: `sha256:${"b".repeat(64)}`,
sidecar: "/pack/sidecar", sidecarSha256: `sha256:${"c".repeat(64)}` };
const expected = "sha256:5d91648a1247fac8633317f80d39ad05e47961894bf8b631c47e10d031753233";
expect(computerAcpxLaunchProfileDigest(profile)).toBe(expected);
for (const key of Object.keys(profile) as Array<keyof typeof profile>) {
expect(computerAcpxLaunchProfileDigest({ ...profile, [key]: `${profile[key]}-changed` })).not.toBe(expected);
}
});
function pinned() {
const identity = { runId: "prior", normalizedSessionId: "session", runnerInstanceId: "runner", environmentLeaseId: "lease" };
const root = computerProviderPackCachePath("/agents/a", digest);
return { agentHome: "/agents/a", identity, provider: { agent: "claude", model: "model" },
control: { schema: "paperclip.runner.durable.control-plane-state.v1", identity: { ...identity }, runAttachTemplate: { provider: {
kind: "acpx", provider: "acpx", driver: "acpx_runtime", agent: "claude", model: "model", normalizedSessionId: "session",
sidecarCommand: `${root}/node_modules/node/bin/node`, sidecarArgs: [`${root}/dist/cli/acpx-runtime-sidecar.cjs`],
} } } };
}
it("retains pack A when the next controller deploys pack B", () => {
const selected = pinnedComputerProviderPack(pinned());
expect(selected.digest).toBe(digest);
expect(selected.root).not.toBe(computerProviderPackCachePath("/agents/a", `sha256:${"b".repeat(64)}`));
});
it.each(["missing", "identity", "foreign-home", "sidecar", "arguments", "provider"])("rejects %s provenance", (kind) => {
const input = pinned();
if (kind === "missing") delete (input.control as { runAttachTemplate?: unknown }).runAttachTemplate;
if (kind === "identity") input.control.identity.environmentLeaseId = "foreign";
if (kind === "foreign-home") input.agentHome = "/agents/b";
if (kind === "sidecar") input.control.runAttachTemplate.provider.sidecarArgs[0] += "/../evil";
if (kind === "arguments") input.control.runAttachTemplate.provider.sidecarArgs.push("--evil");
if (kind === "provider") input.provider.agent = "codex";
expect(() => pinnedComputerProviderPack(input)).toThrow("runner_remote_provider_pack_provenance_unavailable");
});
});
const runner: CommandManagedRuntimeRunner = {
execute: (input) => new Promise((resolve, reject) => {
const child = spawn(input.command, input.args, { cwd: input.cwd, env: { ...process.env, ...input.env } });
let stdout = ""; let stderr = "";
child.stdout.on("data", (chunk) => { stdout += chunk; void input.onLog?.("stdout", String(chunk)); });
child.stderr.on("data", (chunk) => { stderr += chunk; });
child.on("error", reject);
child.on("close", (exitCode, signal) => resolve({ exitCode, signal, stdout, stderr, timedOut: false, pid: child.pid ?? null, startedAt: null }));
child.stdin.end(input.stdin);
}),
};
async function fixture() {
const home = await fs.realpath(await fs.mkdtemp(join(tmpdir(), "provider-cache-")));
roots.push(home);
return {
cacheRoot: computerProviderPackCachePath(home, digest), sessionKey: "task-a",
owner: { ownerId: "owner-a", generation: 1 }, runner,
verify: async (root: string) => { expect(await fs.readFile(join(root, "payload"), "utf8")).toBe("verified bytes"); },
stage: async (root: string, guarded: CommandManagedRuntimeRunner) => {
const result = await guarded.execute({ command: "python3", args: ["-c", "import os,sys;os.mkdir(sys.argv[1],0o700);open(os.path.join(sys.argv[1],'payload'),'w').write(sys.stdin.read())", root], stdin: "verified bytes" });
if (result.exitCode !== 0) throw new Error(result.stderr);
},
};
}
function deferred() { let resolve!: () => void; const promise = new Promise<void>((done) => { resolve = done; }); return { promise, resolve }; }
afterEach(async () => { await Promise.all(roots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))); });
describe("persistent computer provider pack cache", () => {
it("shares immutable identity across tasks, with separate agents and digests", () => {
expect(computerProviderPackCachePath("/agents/a", digest)).not.toBe(computerProviderPackCachePath("/agents/b", digest));
expect(computerProviderPackCachePath("/agents/a", digest)).not.toBe(computerProviderPackCachePath("/agents/a", `sha256:${"b".repeat(64)}`));
expect(() => computerProviderPackCachePath("/agents/../a", digest)).toThrow("identity_invalid");
});
it("verifies a shared hit before the compression/upload callback and excludes its ownership marker", async () => {
const input = await fixture();
const stage = vi.fn(input.stage); const verify = vi.fn(input.verify);
await expect(prepareComputerProviderPackCache({ ...input, stage, verify })).resolves.toBe("published");
await expect(prepareComputerProviderPackCache({ ...input, stage, verify, sessionKey: "task-b" })).resolves.toBe("reused");
expect(stage).toHaveBeenCalledTimes(1);
expect(verify).toHaveBeenCalledTimes(3);
expect(await fs.readdir(input.cacheRoot)).toEqual(["payload"]);
});
it("fails closed on a corrupt published pack, preserving its exact contents", async () => {
const input = await fixture();
await fs.mkdir(input.cacheRoot, { recursive: true });
await fs.writeFile(join(input.cacheRoot, "payload"), "corrupt");
const stage = vi.fn(input.stage);
await expect(prepareComputerProviderPackCache({ ...input, stage })).rejects.toThrow("operator remove or quarantine this exact cache");
expect(stage).not.toHaveBeenCalled();
expect(await fs.readFile(join(input.cacheRoot, "payload"), "utf8")).toBe("corrupt");
});
it("publishes without replacement when two tasks race and verifies the winner", async () => {
const input = await fixture(); const both = deferred(); let staged = 0;
const stage = async (root: string, guarded: CommandManagedRuntimeRunner) => {
await input.stage(root, guarded); if (++staged === 2) both.resolve(); await both.promise;
};
await Promise.all(["task-a", "task-b"].map((sessionKey) => prepareComputerProviderPackCache({ ...input, sessionKey, stage })));
expect(staged).toBe(2);
await input.verify(input.cacheRoot);
const directories = await fs.readdir(join(dirname(input.cacheRoot), ".uploads"));
for (const session of directories) expect(await fs.readdir(join(dirname(input.cacheRoot), ".uploads", session, "a".repeat(64)))).toEqual(["lock"]);
});
it("cleans only its marked private upload after failure", async () => {
const input = await fixture(); const unrelated = join(dirname(input.cacheRoot), ".uploads", "user-data");
await fs.mkdir(unrelated, { recursive: true }); await fs.writeFile(join(unrelated, "keep"), "mine");
let attempted = "";
await expect(prepareComputerProviderPackCache({ ...input, stage: async (root, guarded) => {
attempted = dirname(root); await input.stage(root, guarded); throw new Error("upload disconnected");
} })).rejects.toThrow("upload disconnected");
await expect(fs.stat(attempted)).rejects.toMatchObject({ code: "ENOENT" });
expect(await fs.readFile(join(unrelated, "keep"), "utf8")).toBe("mine");
});
it("rejects delayed old writes and keeps the replacement when a session retries", async () => {
const input = await fixture(); const oldReady = deferred(); const releaseOld = deferred();
let oldRoot = ""; let oldRunner!: CommandManagedRuntimeRunner;
const old = prepareComputerProviderPackCache({ ...input, stage: async (root, guarded) => {
oldRoot = root; oldRunner = guarded; await input.stage(root, guarded); oldReady.resolve(); await releaseOld.promise;
throw new Error("old attempt retired");
} });
const rejectedOld = expect(old).rejects.toThrow("old attempt retired");
await oldReady.promise;
await prepareComputerProviderPackCache({ ...input, owner: { ...input.owner, generation: 2 } });
const staleWrite = await oldRunner.execute({ command: "python3", args: ["-c", "import os,sys;os.makedirs(sys.argv[1],exist_ok=True)", oldRoot] });
expect(staleWrite.exitCode).not.toBe(0);
expect(staleWrite.stderr).toContain("stale upload generation");
releaseOld.resolve(); await rejectedOld;
await input.verify(input.cacheRoot);
await expect(fs.stat(dirname(oldRoot))).rejects.toMatchObject({ code: "ENOENT" });
});
it("waits for an in-flight guarded writer before replacing its receipt", async () => {
const input = await fixture(); const entered = deferred(); const releaseStage = deferred();
const releaseFile = join(roots.at(-1)!, "release-writer");
let write!: Promise<Awaited<ReturnType<CommandManagedRuntimeRunner["execute"]>>>;
const old = prepareComputerProviderPackCache({ ...input, stage: async (root, guarded) => {
await input.stage(root, guarded);
write = guarded.execute({ command: "python3", args: ["-c", "import os,sys,time;print('entered',flush=True)\nwhile not os.path.exists(sys.argv[1]):time.sleep(0.01)", releaseFile], onLog: async (_stream, text) => { if (text.includes("entered")) entered.resolve(); } });
await releaseStage.promise; await write; throw new Error("old retired");
} });
const rejectedOld = expect(old).rejects.toThrow("old retired");
await entered.promise;
let replacementStaged = false;
const replacement = prepareComputerProviderPackCache({ ...input, owner: { ...input.owner, generation: 2 }, stage: async (root, guarded) => {
replacementStaged = true; await input.stage(root, guarded);
} });
await new Promise((resolve) => setTimeout(resolve, 100));
expect(replacementStaged).toBe(false);
await fs.writeFile(releaseFile, "release");
expect((await write).exitCode).toBe(0);
await replacement; releaseStage.resolve(); await rejectedOld;
await input.verify(input.cacheRoot);
});
it("reclaims an interrupted marked upload on the next attempt", async () => {
const input = await fixture(); let attempted = "";
const disconnected: CommandManagedRuntimeRunner = { execute: (command) => {
if (command.args?.[2] === "cleanup") return Promise.reject(new Error("controller disconnected"));
return runner.execute(command);
} };
await expect(prepareComputerProviderPackCache({ ...input, runner: disconnected, stage: async (root, guarded) => {
attempted = dirname(root); await input.stage(root, guarded); throw new Error("interrupted");
} })).rejects.toThrow("interrupted");
expect((await fs.stat(attempted)).isDirectory()).toBe(true);
// Another task can publish while this session is disconnected. Retrying
// this session must reclaim its exact old upload even on the shared hit.
await prepareComputerProviderPackCache({ ...input, sessionKey: "another-task" });
await expect(prepareComputerProviderPackCache({ ...input, owner: { ...input.owner, generation: 2 } })).resolves.toBe("reused");
await expect(fs.stat(attempted)).rejects.toMatchObject({ code: "ENOENT" });
});
});
@@ -2,6 +2,94 @@ import { createHash, randomUUID } from "node:crypto";
import { posix } from "node:path";
import type { CommandManagedRuntimeRunner } from "@paperclipai/adapter-utils/command-managed-runtime";
/** The controller's retained attachment template pins content-addressed paths.
* Remote manifests may supply bytes, but cannot choose a new code authority. */
export function pinnedComputerProviderPack(input: {
agentHome: string; identity: Record<string, unknown>; control: unknown;
provider: { agent: string; model: string };
}): { digest: string; root: string; command: string; sidecar: string } {
const object = (value: unknown): Record<string, unknown> =>
value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
const control = object(input.control);
const identity = object(control.identity);
const provider = object(object(control.runAttachTemplate).provider);
const fail = (): never => { throw new Error("runner_remote_provider_pack_provenance_unavailable"); };
if (control.schema !== "paperclip.runner.durable.control-plane-state.v1" ||
["runId", "normalizedSessionId", "runnerInstanceId", "environmentLeaseId"].some(key =>
typeof input.identity[key] !== "string" || !input.identity[key] || identity[key] !== input.identity[key]) ||
provider.kind !== "acpx" || provider.provider !== "acpx" || provider.driver !== "acpx_runtime" ||
provider.agent !== input.provider.agent || provider.model !== input.provider.model ||
provider.normalizedSessionId !== identity.normalizedSessionId || typeof provider.sidecarCommand !== "string" ||
!Array.isArray(provider.sidecarArgs) || provider.sidecarArgs.length !== 1) return fail();
const match = provider.sidecarCommand.match(/\/provider-packs\/([a-f0-9]{64})\/node_modules\/node\/bin\/node$/);
if (!match) return fail();
const digest = `sha256:${match[1]}`;
const root = computerProviderPackCachePath(input.agentHome, digest);
const command = `${root}/node_modules/node/bin/node`;
const sidecar = `${root}/dist/cli/acpx-runtime-sidecar.cjs`;
if (provider.sidecarCommand !== command || provider.sidecarArgs[0] !== sidecar) return fail();
return { digest, root, command, sidecar };
}
/** Mirrors the runner's length-prefixed ACPX launch profile v1 digest. */
export function computerAcpxLaunchProfileDigest(input: {
authorityDigest: string; command: string; commandSha256: string; sidecar: string; sidecarSha256: string;
}): string {
const digest = createHash("sha256");
const integer = (n: number) => { const bytes = Buffer.alloc(8); bytes.writeBigUInt64BE(BigInt(n)); return bytes; };
const update = (value: string | Buffer) => { const bytes = typeof value === "string" ? Buffer.from(value) : value; digest.update(integer(bytes.length)); digest.update(bytes); };
update("paperclip.runner.acpx-launch-profile.v1");
update(input.authorityDigest); update(input.command); update(integer(1)); update(input.sidecar); update(integer(2));
for (const [path, sha256] of [[input.command, input.commandSha256], [input.sidecar, input.sidecarSha256]].sort(([a], [b]) => a! < b! ? -1 : a! > b! ? 1 : 0)) {
update(path!); update(sha256!);
}
return `sha256:${digest.digest("hex")}`;
}
export async function readPinnedComputerProviderMetadata(input: {
runner: CommandManagedRuntimeRunner; packRoot: string; stateDirectory: string;
}): Promise<[unknown, unknown]> {
for (const path of [input.packRoot, input.stateDirectory]) {
if (!posix.isAbsolute(path) || posix.normalize(path) !== path)
throw new Error("runner_remote_provider_pack_provenance_unavailable");
}
const result = await input.runner.execute({ command: "python3", args: ["-c", String.raw`
import os,sys,json,stat
values=[]
for path in sys.argv[1:]:
parts=path.split('/')[1:];parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY)
try:
for part in parts[:-1]:
child=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=parent)
os.close(parent);parent=child
source=os.open(parts[-1],os.O_RDONLY|os.O_NOFOLLOW,dir_fd=parent)
with os.fdopen(source,'rb') as f:
info=os.fstat(f.fileno())
if not stat.S_ISREG(info.st_mode) or info.st_nlink!=1 or info.st_size>16777216:raise RuntimeError('invalid retained provider metadata')
data=f.read(16777217)
if len(data)>16777216:raise RuntimeError('invalid retained provider metadata')
values.append(json.loads(data))
finally:os.close(parent)
print(json.dumps(values))`, posix.join(input.packRoot, "provider-pack.json"),
posix.join(input.stateDirectory, "acpx-provider-state.json")], bypassSession: true, timeoutMs: 10_000 });
if (result.exitCode !== 0 || result.timedOut) throw new Error("runner_remote_provider_pack_provenance_unavailable");
const value: unknown = JSON.parse(result.stdout);
if (!Array.isArray(value) || value.length !== 2) throw new Error("runner_remote_provider_pack_provenance_unavailable");
return [value[0], value[1]];
}
export function assertPinnedComputerProviderState(input: {
state: unknown; normalizedSessionId: string; launchProfileDigest: string; command: string; sidecar: string;
}): void {
const object = (value: unknown): Record<string, unknown> =>
value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
const state = object(input.state); const descriptor = object(state.descriptor);
if (state.schema !== "paperclip.runner.acpx-provider-state.v3" || state.launchProfileDigest !== input.launchProfileDigest ||
descriptor.normalizedSessionId !== input.normalizedSessionId || descriptor.sidecarCommand !== input.command ||
!Array.isArray(descriptor.sidecarArgs) || descriptor.sidecarArgs.length !== 1 || descriptor.sidecarArgs[0] !== input.sidecar)
throw new Error("runner_remote_provider_pack_provenance_mismatch");
}
export function computerProviderPackCachePath(agentHome: string, digest: string): string {
if (!posix.isAbsolute(agentHome) || posix.normalize(agentHome) !== agentHome || !/^sha256:[a-f0-9]{64}$/.test(digest)) {
throw new Error("runner_remote_provider_cache_identity_invalid");