fix(runner): retain stages after provider error translation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-27 20:31:11 -05:00
1 parent 0c904a7e42
commit cb8f9025e4
7 files changed
+37 -8

No files matched your search

+1 -1
View File
@@ -37,7 +37,7 @@
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:6326444af8fd447032ca8ad10ce3a1280af9aeeddacd563cf48b430202dbbefd",
"commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41",
"requiresProviderPolicy": true
},
"claude": {
@@ -47,7 +47,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
agent_runtime_package: None,
agent_runtime_version: None,
command_digest:
"sha256:6326444af8fd447032ca8ad10ce3a1280af9aeeddacd563cf48b430202dbbefd",
"sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41",
requires_provider_policy: true,
},
"claude" => AcpxReleaseProfile {
@@ -89,7 +89,7 @@ describe("Copilot build-owned installation", () => {
it("admits the current profile with environment launch preparation", () => {
expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(70);
expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined();
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:6326444af8fd447032ca8ad10ce3a1280af9aeeddacd563cf48b430202dbbefd");
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41");
});
it("preserves v69 while binding closed startup diagnostics in v70", async () => {
@@ -53,7 +53,7 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:6326444af8fd447032ca8ad10ce3a1280af9aeeddacd563cf48b430202dbbefd",
"commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41",
"permissionPolicy": "interactive"
},
"claude": {
@@ -203,6 +203,35 @@ afterEach(async () => {
});
describe("ACPX runtime host", () => {
it("retains the admission stage when provider classification replaces the error", async () => {
const fixture = await hostFixture();
const openRuntime = vi.fn(async () => { throw new Error("Unauthorized private-token-canary"); });
const failure = await AcpxRuntimeHost.open({ ...fixture.options, agent: "copilot", model: "gpt-5.6-sol",
permissionMode: "approve-all", environment: {COPILOT_GITHUB_TOKEN:"synthetic-test-token"} }, fixture.dependencies({openRuntime})).catch(error => error);
expect(failure).toMatchObject({code:"COPILOT_AUTH_REQUIRED"});
expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_RUNTIME");
expect(failure.message).not.toContain("private-token-canary");
expect(openRuntime).toHaveBeenCalledOnce();
expect(fixture.commandClose).toHaveBeenCalledOnce();
});
it("reports failed Pi skill materialization as skills admission before launch", async () => {
const fixture = await hostFixture();
const digest = "0".repeat(64);
const bundle = {schema:NATIVE_RUNTIME_ASSET_SCHEMA,digest,manifestDigest:digest,
rootPath:join(fixture.root,"missing-assigned-skill"),fileCount:1,totalBytes:1};
const snapshot = {prompt:{revision:PAPERCLIP_EXECUTION_PROMPT_REVISION,text:PAPERCLIP_EXECUTION_PROMPT,digest:nativeRuntimePromptDigest()},
instructions:{entryPath:"AGENTS.md",bundle},skills:[{key:"assigned",runtimeName:"assigned",versionId:"v1",bundle}],
mcp:{assignmentSetId:"none",digest,bindingId:null}};
const context = {...snapshot,aggregateDigest:canonicalNativeRuntimeContextDigest(snapshot)};
const openRuntime = vi.fn(async () => runtimePort());
const failure = await AcpxRuntimeHost.open({ ...fixture.options, agent:"pi",model:"anthropic/claude-sonnet-5",
piThinkingLevel:"low",permissionMode:"approve-all",providerPolicy:{readOnly:false},runtimeContext:context },fixture.dependencies({openRuntime})).catch(error => error);
expect(failure).toBeInstanceOf(Error);
expect(failure.message).toContain("missing-assigned-skill");
expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_SKILLS");
expect(openRuntime).not.toHaveBeenCalled();
expect(fixture.commandClose).not.toHaveBeenCalled();
});
it.each(["installation", "sandbox", "credential", "runtime"] as const)("retains the failed %s admission stage through the sidecar boundary", async stage => {
const fixture = await hostFixture();
const failure = new Error("private-token-canary");
@@ -552,6 +552,7 @@ export class AcpxRuntimeHost {
);
options.signal?.throwIfAborted();
}
admissionStage = "skills";
if (options.agent === "pi") {
const skills = await acquireAbortableAdmissionResource({
signal: options.signal,
@@ -574,7 +575,6 @@ export class AcpxRuntimeHost {
PAPERCLIP_PI_READ_ROOTS: JSON.stringify([...(options.providerPolicy?.readRoots ?? []), ...skills.readRoots]),
});
}
admissionStage = "skills";
if (!preconfigured && (options.agent === "claude" || options.agent === "grok")) {
// The lifetime lease proves the previous provider has stopped. Refresh
// the assigned snapshot before every launch, including durable resume;
@@ -742,8 +742,8 @@ export class AcpxRuntimeHost {
: [],
});
} catch (error) {
recordAcpxAdmissionFailure(error, admissionStage);
error = classifyAcpxProfileError(options.agent, error) ?? error;
recordAcpxAdmissionFailure(error, admissionStage);
const cleanup = cleanupRuntimeResources(
runtime,
toolBridge,
@@ -1,5 +1,5 @@
{
"commandDigest": "sha256:6326444af8fd447032ca8ad10ce3a1280af9aeeddacd563cf48b430202dbbefd",
"commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
@@ -77,7 +77,7 @@
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
"runtimeHostSourceSha256": "f12712a06c9d25ba78f61883d6582237de405e79f38586e20419522e5a7059f2",
"runtimeHostSourceSha256": "be14218ad4477e083cfc32f6680fcfed776056a4084615d6b0efd1e521e66232",
"runtimeAdapterSourceSha256": "7c47c05306ebe7d1ca6b5d046a96f5726a48adec4d27e7c16788f505f25b5db2",
"runtimeSandboxSourceSha256": "981449a4c6d10f682ecf86d834eed07faca30a7d9b419fb8db4973a442a343ec",
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",