diff --git a/doc/grok-native-runner.md b/doc/grok-native-runner.md index 851c60d23c4..7a8ba74fa65 100644 --- a/doc/grok-native-runner.md +++ b/doc/grok-native-runner.md @@ -26,6 +26,11 @@ acknowledgement and the effective model before a prompt. A lazy restored handle must connect through read-only native session inspection first. Reapplying the bound metadata on restore prevents Grok from resetting effort to its default. Missing configuration, a rejected value, or effort drift stops admission. +Admission failures keep a closed Grok reason code through the sidecar boundary. +Other startup failures retain their closed admission stage when no more specific +known error code is available. These diagnostics contain no native payload, +credential, arbitrary provider message, or workspace path. They do not change +admission or retry policy. Omitting effort retains Grok's provider default. This setting remains outside the model-visible task prompt. Historical execution inputs retain their schemas. diff --git a/packages/paperclip-runner/acpx-profiles.json b/packages/paperclip-runner/acpx-profiles.json index c96818e7445..1df6ae1c2de 100644 --- a/packages/paperclip-runner/acpx-profiles.json +++ b/packages/paperclip-runner/acpx-profiles.json @@ -32,12 +32,12 @@ "requiresProviderPolicy": true }, "copilot": { - "agentProfileVersion": 69, + "agentProfileVersion": 70, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719", + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", "requiresProviderPolicy": true }, "claude": { diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs index 43edd3e0196..a0b963ad2b6 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs @@ -893,6 +893,23 @@ fn stderr_diagnostic_categories(value: &str) -> BTreeSet<&'static str> { fn response_error_classification(error: &ResponseError) -> &'static str { match error.code.as_str() { + "GROK_EFFORT_ACKNOWLEDGEMENT_MISSING" => return "grok_effort_acknowledgement_missing", + "GROK_EFFORT_MISMATCH" => return "grok_effort_mismatch", + "GROK_MODEL_MISMATCH" => return "grok_model_mismatch", + "GROK_SESSION_MISMATCH" => return "grok_session_mismatch", + "GROK_CONFIGURATION_CONFLICT" => return "grok_configuration_conflict", + "GROK_CONFIGURATION_DRIFT" => return "grok_configuration_drift", + "GROK_ADMISSION_PROTOCOL_VIOLATION" => return "grok_admission_protocol_violation", + "ACPX_ADMISSION_BINDING" => return "admission_binding", + "ACPX_ADMISSION_INSTALLATION" => return "admission_installation", + "ACPX_ADMISSION_SANDBOX" => return "admission_sandbox", + "ACPX_ADMISSION_CREDENTIAL" => return "admission_credential", + "ACPX_ADMISSION_CONFIGURATION" => return "admission_configuration", + "ACPX_ADMISSION_SKILLS" => return "admission_skills", + "ACPX_ADMISSION_COMMAND" => return "admission_command", + "ACPX_ADMISSION_TOOL_BRIDGE" => return "admission_tool_bridge", + "ACPX_ADMISSION_RUNTIME" => return "admission_runtime", + "ACPX_ADMISSION_IDENTITY" => return "admission_identity", "ACPX_RUNTIME_PACKAGE_MISSING" => return "runtime_package_missing", "ACP_MODEL_UNSUPPORTED" => return "requested_model_unsupported", "AGENT_STARTUP_FAILED" => return "agent_startup_failed", @@ -1120,6 +1137,20 @@ mod tests { message: message.to_owned(), retryable: false, }; + for (code, expected) in [ + ("ACPX_ADMISSION_SANDBOX", "admission_sandbox"), + ("ACPX_ADMISSION_CREDENTIAL", "admission_credential"), + ("ACPX_ADMISSION_RUNTIME", "admission_runtime"), + ( + "GROK_EFFORT_ACKNOWLEDGEMENT_MISSING", + "grok_effort_acknowledgement_missing", + ), + ] { + assert_eq!( + response_error_classification(&error(code, "private-token-canary")), + expected + ); + } assert_eq!( response_error_classification(&error( "acpx_sidecar_command_failed", diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs index 1e9eadd2971..563855b6e9c 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs @@ -262,7 +262,7 @@ fn run() -> Result<(), Box> { "id": id, "ok": false, "error": { - "code": "Q7Z9", + "code": request["params"]["errorCode"].as_str().unwrap_or("Q7Z9"), "message": "violet-circuit-4821", "retryable": false, }, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs index f99ff39b7a0..d5ed6013196 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs @@ -47,7 +47,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: None, agent_runtime_version: None, command_digest: - "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719", + "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", requires_provider_policy: true, }, "claude" => AcpxReleaseProfile { diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_sidecar_transport.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_sidecar_transport.rs index 08ed56fdba0..62cda02e06e 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_sidecar_transport.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_sidecar_transport.rs @@ -219,6 +219,34 @@ fn keeps_valid_command_rejections_separate_from_protocol_failures() { transport.shutdown().expect("fake sidecar should stop"); } +#[test] +fn preserves_closed_admission_categories_over_the_sidecar_pipe() { + let mut transport = transport("remote-error", Duration::from_secs(1)); + for (code, category) in [ + ("ACPX_ADMISSION_INSTALLATION", "admission_installation"), + ("ACPX_ADMISSION_CREDENTIAL", "admission_credential"), + ( + "GROK_EFFORT_ACKNOWLEDGEMENT_MISSING", + "grok_effort_acknowledgement_missing", + ), + ("GROK_MODEL_MISMATCH", "grok_model_mismatch"), + ("ACPX_ADMISSION_CREDENTIAL_SECRET", "unclassified"), + ("GROK_MODEL_MISMATCH_SECRET", "unclassified"), + ] { + let error = transport + .request( + GeneratedAcpxSidecarCommand::SessionOpen, + json!({"errorCode":code}), + ) + .unwrap_err(); + let message = error.to_string(); + assert!(message.contains(&format!("classification={category}"))); + assert!(!message.contains("violet-circuit-4821")); + assert!(!message.contains("SECRET")); + } + transport.shutdown().unwrap(); +} + #[test] fn redacts_sidecar_stderr_when_the_process_exits() { let mut transport = transport("exit-secret", Duration::from_secs(1)); diff --git a/packages/paperclip-runner/scripts/copilot-profile-identity.mjs b/packages/paperclip-runner/scripts/copilot-profile-identity.mjs index 21737c1085a..400c9615d48 100644 --- a/packages/paperclip-runner/scripts/copilot-profile-identity.mjs +++ b/packages/paperclip-runner/scripts/copilot-profile-identity.mjs @@ -7,9 +7,9 @@ import { fileURLToPath } from "node:url"; import { resolve, dirname } from "node:path"; const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const fixture = resolve(root, "test/fixtures/copilot-profile-v69-identity.json"); +const fixture = resolve(root, "test/fixtures/copilot-profile-v70-identity.json"); const historical = JSON.parse(readFileSync(resolve(root, "test/fixtures/copilot-profile-v12-identity.json"), "utf8")); -const declaration = { ...historical.declaration, agentProfileVersion: 69 }; +const declaration = { ...historical.declaration, agentProfileVersion: 70 }; delete declaration.permissionIdentitySourceSha256; const sources = { displayEventSourceSha256: "src/drivers/acpx/copilot-events.ts", @@ -44,6 +44,8 @@ const sources = { assetOwnershipSourceSha256: "src/drivers/acpx/provider-assets-root.ts", assetInstallerSourceSha256: "scripts/install-copilot-assets.mjs", credentialBindingSourceSha256: "src/drivers/acpx/environment.ts", + admissionDiagnosticsSourceSha256: "src/drivers/acpx/admission-diagnostics.ts", + grokAdmissionSourceSha256: "src/drivers/acpx/grok-effort.ts", sidecarErrorSourceSha256: "src/cli/acpx-sidecar-input.ts", profileSourceSha256: "src/drivers/acpx/copilot-profile.ts", metadataProbeSourceSha256: "src/drivers/copilot-metadata-probe.ts", diff --git a/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts b/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts index 0b6a280bdb3..6ebd3259074 100644 --- a/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts +++ b/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts @@ -1,4 +1,6 @@ import { describe, expect, it } from "vitest"; +import { createGrokEffortAdmission } from "../drivers/acpx/grok-effort.js"; +import { recordAcpxAdmissionFailure } from "../drivers/acpx/admission-diagnostics.js"; import { acpxBootstrapBlockedError, @@ -8,6 +10,35 @@ import { } from "./acpx-sidecar-input.js"; describe("ACPX sidecar input sequencing", () => { + it("prefers a known cause to a stage and ignores provider-supplied stage fields", () => { + const cause = Object.assign(new Error("private-token-canary"), { code: "AUTH_REQUIRED" }); + const error = new AggregateError([cause], "private-wrapper"); + recordAcpxAdmissionFailure(error, "runtime"); + expect(acpxSidecarErrorCode(error)).toBe("AUTH_REQUIRED"); + expect(acpxSidecarErrorCode(Object.assign(new Error("private-token-canary"), { + acpxAdmissionStage: "credential", code: "ACPX_ADMISSION_CREDENTIAL", + }))).toBe("acpx_sidecar_command_failed"); + }); + it("retains a closed fallback stage through aggregate cleanup without changing the original error", () => { + const error = Object.freeze(new Error("private-token-canary")); + expect(recordAcpxAdmissionFailure(error, "credential")).toBe(error); + recordAcpxAdmissionFailure(error, "runtime"); + expect(acpxSidecarErrorCode(new AggregateError([error], "private-wrapper"))).toBe("ACPX_ADMISSION_CREDENTIAL"); + expect(acpxSidecarErrorCode(new Error("ACPX_ADMISSION_CREDENTIAL private-token-canary"))).toBe("acpx_sidecar_command_failed"); + }); + it("preserves a Grok admission rejection without exporting the native payload", () => { + const admission = createGrokEffortAdmission("high", "grok-4.7"); + const guard = admission.createGuard(); + guard("outbound", { id: 1, method: "session/new", params: {} }); + let failure: Error | undefined; + try { + guard("inbound", { id: 1, result: { sessionId: "private-session", _meta: { privateToken: "private-token-canary" } } }); + } catch (error) { failure = error as Error; } + expect(failure).toBeInstanceOf(Error); + expect(acpxSidecarErrorCode(failure!)).toBe("GROK_EFFORT_ACKNOWLEDGEMENT_MISSING"); + expect(failure!.message).not.toContain("private-token-canary"); + expect(() => admission.assertReady()).toThrow(failure); + }); it("classifies a retired tool callback without copying provider text into its identity", () => { const error = Object.assign(new Error("private-token-canary"), { code: "ACPX_TOOL_CALL_STALE" }); expect(acpxSidecarErrorCode(error)).toBe("ACPX_TOOL_CALL_STALE"); diff --git a/packages/paperclip-runner/src/cli/acpx-sidecar-input.ts b/packages/paperclip-runner/src/cli/acpx-sidecar-input.ts index e9f4c645028..5d544314fdd 100644 --- a/packages/paperclip-runner/src/cli/acpx-sidecar-input.ts +++ b/packages/paperclip-runner/src/cli/acpx-sidecar-input.ts @@ -1,4 +1,6 @@ +import { GROK_ADMISSION_ERROR_CODES } from "../drivers/acpx/grok-effort.js"; import { enqueueSerialInput } from "./serial-input-queue.js"; +import { acpxAdmissionFailureCode } from "../drivers/acpx/admission-diagnostics.js"; const ACPX_BOOTSTRAP_COMMANDS = new Set(["initialize", "session.open"]); @@ -38,10 +40,12 @@ export function acpxBootstrapBlockedError( export function acpxSidecarErrorCode(error: Error): string { const pending: Error[] = [error]; const observed = new Set(); + let admissionCode: string | undefined; while (pending.length > 0 && observed.size < 16) { const current = pending.shift()!; if (observed.has(current)) continue; observed.add(current); + admissionCode ??= acpxAdmissionFailureCode(current); const code = directAcpxSidecarErrorCode(current); if (code !== null) return code; @@ -53,7 +57,7 @@ export function acpxSidecarErrorCode(error: Error): string { } if (details.cause instanceof Error) pending.push(details.cause); } - return "acpx_sidecar_command_failed"; + return admissionCode ?? "acpx_sidecar_command_failed"; } function directAcpxSidecarErrorCode(error: Error): string | null { @@ -131,6 +135,7 @@ const GENERIC_ACPX_OUTPUT_CODES = new Set([ ]); const STABLE_ACPX_SIDECAR_CODES = new Set([ + ...GROK_ADMISSION_ERROR_CODES, "ACPX_RUNTIME_PACKAGE_MISSING", "COPILOT_POLICY_VIOLATION", "COPILOT_DETACHED_WORK_UNSUPPORTED", diff --git a/packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts b/packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts new file mode 100644 index 00000000000..c350ccbbaf4 --- /dev/null +++ b/packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts @@ -0,0 +1,26 @@ +/** Closed startup stages, independent of provider output and credentials. */ +export const ACPX_ADMISSION_CODES = { + binding: "ACPX_ADMISSION_BINDING", + installation: "ACPX_ADMISSION_INSTALLATION", + sandbox: "ACPX_ADMISSION_SANDBOX", + credential: "ACPX_ADMISSION_CREDENTIAL", + configuration: "ACPX_ADMISSION_CONFIGURATION", + skills: "ACPX_ADMISSION_SKILLS", + command: "ACPX_ADMISSION_COMMAND", + tool_bridge: "ACPX_ADMISSION_TOOL_BRIDGE", + runtime: "ACPX_ADMISSION_RUNTIME", + identity: "ACPX_ADMISSION_IDENTITY", +} as const; + +const failures = new WeakMap(); + +/** Preserve the original error and cleanup semantics, including cancellation. */ +export function recordAcpxAdmissionFailure(error: unknown, stage: keyof typeof ACPX_ADMISSION_CODES): unknown { + if (error instanceof Error && !failures.has(error)) failures.set(error, stage); + return error; +} + +export function acpxAdmissionFailureCode(error: Error): string | undefined { + const stage = failures.get(error); + return stage === undefined ? undefined : ACPX_ADMISSION_CODES[stage]; +} diff --git a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts index 4436f17831c..0a9c647d407 100644 --- a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts @@ -87,9 +87,26 @@ vi.mock("./installation-integrity.js", () => ({ verifyNativeAcpxInstallation: vi describe("Copilot build-owned installation", () => { it("admits the current profile with environment launch preparation", () => { - expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(69); + expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(70); expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined(); - expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719"); + expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc"); + }); + + it("preserves v69 while binding closed startup diagnostics in v70", async () => { + const bytes = readFileSync(new URL("../../../test/fixtures/copilot-profile-v69-identity.json", import.meta.url)); + expect(createHash("sha256").update(bytes).digest("hex")).toBe("a878caa5f40cfad517e852384507b50c1098e219f1dd6822335b1119b03eaea5"); + const prior = JSON.parse(bytes.toString("utf8")); + const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v70-identity.json", import.meta.url), "utf8")); + expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual([ + "admissionDiagnosticsSourceSha256", "agentProfileVersion", "grokAdmissionSourceSha256", + "profileCompatibilitySourceSha256", "runtimeHostSourceSha256", "sidecarErrorSourceSha256", + ]); + for (const [name, field] of [["admission-diagnostics.ts", "admissionDiagnosticsSourceSha256"], ["grok-effort.ts", "grokAdmissionSourceSha256"]]) { + expect(current.declaration[field]).toBe(createHash("sha256").update(readFileSync(new URL(name!, import.meta.url))).digest("hex")); + } + expect(isSupportedAcpxProfileVersion("copilot", 69)).toBe(true); + await expect(verifyCopilotInstallation({ ...QUALIFIED_ACPX_PROFILES.copilot, + agentProfileVersion: 69, commandDigest: prior.commandDigest })).rejects.toThrow("exact pinned"); }); it("preserves published and candidate history while binding native Grok admission in v69", async () => { diff --git a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts index 9fcf232a819..b42655d2f28 100644 --- a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts +++ b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts @@ -48,12 +48,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "copilot", - "agentProfileVersion": 69, + "agentProfileVersion": 70, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719", + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", "permissionPolicy": "interactive" }, "claude": { diff --git a/packages/paperclip-runner/src/drivers/acpx/grok-effort.ts b/packages/paperclip-runner/src/drivers/acpx/grok-effort.ts index 8dd25d3f123..f8f1bea2c65 100644 --- a/packages/paperclip-runner/src/drivers/acpx/grok-effort.ts +++ b/packages/paperclip-runner/src/drivers/acpx/grok-effort.ts @@ -1,3 +1,13 @@ +export const GROK_ADMISSION_ERROR_CODES = [ + "GROK_EFFORT_ACKNOWLEDGEMENT_MISSING", + "GROK_EFFORT_MISMATCH", + "GROK_MODEL_MISMATCH", + "GROK_SESSION_MISMATCH", + "GROK_CONFIGURATION_CONFLICT", + "GROK_CONFIGURATION_DRIFT", + "GROK_ADMISSION_PROTOCOL_VIOLATION", +] as const; + export type GrokEffort = "minimal" | "low" | "medium" | "high" | "xhigh"; export function resolveGrokEffort(agent: string, value: unknown): GrokEffort | undefined { if (value === undefined || value === "") return undefined; @@ -8,22 +18,23 @@ export function resolveGrokEffort(agent: string, value: unknown): GrokEffort | u export function createGrokEffortAdmission(expected: GrokEffort, expectedModel: string) { type State = { sessionId: string | null; ready: boolean; error: Error | null }; let current: State | null = null; - const failure = (detail: string) => new Error(`Grok effort admission failed: ${detail}`); + const failure = (code: typeof GROK_ADMISSION_ERROR_CODES[number], detail: string) => + Object.assign(new Error(`Grok effort admission failed: ${detail}`), { code }); const assertReady = () => { if (current?.error) throw current.error; - if (!current?.sessionId || !current.ready) throw failure("native session does not acknowledge the selected effort"); + if (!current?.sessionId || !current.ready) throw failure("GROK_EFFORT_ACKNOWLEDGEMENT_MISSING", "native session does not acknowledge the selected effort"); }; const readAcknowledgement = (result: Record) => { const config = object(object(result._meta)["x.ai/sessionConfig"]); const options = Array.isArray(config.options) ? config.options.map(object) : []; const selected = options.filter(option => option.category === "mode" && option.selected === true); - if (selected.length !== 1) throw failure("native effort selection is missing or ambiguous"); - if (selected[0]!.id !== expected) throw failure("native effort differs from the selected effort"); + if (selected.length !== 1) throw failure("GROK_EFFORT_ACKNOWLEDGEMENT_MISSING", "native effort selection is missing or ambiguous"); + if (selected[0]!.id !== expected) throw failure("GROK_EFFORT_MISMATCH", "native effort differs from the selected effort"); const model = object(result.models).currentModelId; const selectedModels = options.filter(option => option.category === "model" && option.selected === true); if (typeof model !== "string" || !model.trim() || model !== expectedModel || selectedModels.length > 1 || selectedModels.length === 1 && selectedModels[0]!.id !== model) { - throw failure("native model acknowledgement conflicts with the selected model"); + throw failure("GROK_MODEL_MISMATCH", "native model acknowledgement conflicts with the selected model"); } }; return { @@ -34,26 +45,26 @@ export function createGrokEffortAdmission(expected: GrokEffort, expectedModel: s current = state; let pending: { id: string | number; method: string; sessionId: unknown } | null = null; return (direction: "inbound" | "outbound", value: unknown): void => { - if (current !== state) throw failure("connection authority was replaced"); + if (current !== state) throw failure("GROK_ADMISSION_PROTOCOL_VIOLATION", "connection authority was replaced"); if (state.error) throw state.error; const message = object(value), params = object(message.params); try { if (direction === "outbound") { if (message.method === "session/prompt") { assertReady(); - if (params.sessionId !== state.sessionId) throw failure("prompt belongs to a different session"); + if (params.sessionId !== state.sessionId) throw failure("GROK_SESSION_MISMATCH", "prompt belongs to a different session"); } else if (message.method === "session/new" || message.method === "session/load" || message.method === "session/resume") { - if (typeof message.id !== "string" && typeof message.id !== "number" || pending) throw failure("uncorrelated or overlapping session admission"); + if (typeof message.id !== "string" && typeof message.id !== "number" || pending) throw failure("GROK_ADMISSION_PROTOCOL_VIOLATION", "uncorrelated or overlapping session admission"); const meta = object(params._meta); if (meta.reasoningEffort !== undefined && meta.reasoningEffort !== expected || meta.modelId !== undefined && meta.modelId !== expectedModel) { - throw failure("native session request conflicts with the bound configuration"); + throw failure("GROK_CONFIGURATION_CONFLICT", "native session request conflicts with the bound configuration"); } params._meta = { ...meta, modelId: expectedModel, reasoningEffort: expected }; state.ready = false; state.sessionId = null; pending = { id: message.id, method: message.method, sessionId: params.sessionId }; } else if (message.method === "session/set_config_option" || message.method === "session/set_model") { - throw failure("model and effort must be selected when the native session opens"); + throw failure("GROK_ADMISSION_PROTOCOL_VIOLATION", "model and effort must be selected when the native session opens"); } return; } @@ -61,7 +72,7 @@ export function createGrokEffortAdmission(expected: GrokEffort, expectedModel: s if (!state.ready) return; const update = object(params.update); if (params.sessionId !== state.sessionId || update.reasoning_effort !== expected - || update.model_id !== expectedModel) throw failure("native model or effort drifted from the selected configuration"); + || update.model_id !== expectedModel) throw failure("GROK_CONFIGURATION_DRIFT", "native model or effort drifted from the selected configuration"); return; } if (message.method !== undefined || !pending || message.id !== pending.id) return; @@ -69,14 +80,14 @@ export function createGrokEffortAdmission(expected: GrokEffort, expectedModel: s if (message.error !== undefined) return; const result = object(message.result); const sessionId = request.method === "session/new" ? result.sessionId : request.sessionId; - if (typeof sessionId !== "string" || !sessionId.trim()) throw failure("native acknowledgement has no session identity"); + if (typeof sessionId !== "string" || !sessionId.trim()) throw failure("GROK_SESSION_MISMATCH", "native acknowledgement has no session identity"); const detail = object(object(result._meta)["x.ai/sessionDetail"]); if (result.sessionId !== undefined && result.sessionId !== sessionId - || detail.sessionId !== undefined && detail.sessionId !== sessionId) throw failure("native acknowledgement belongs to a different session"); + || detail.sessionId !== undefined && detail.sessionId !== sessionId) throw failure("GROK_SESSION_MISMATCH", "native acknowledgement belongs to a different session"); readAcknowledgement(result); state.sessionId = sessionId; state.ready = true; } catch (error) { - state.error = error instanceof Error ? error : failure("invalid native acknowledgement"); + state.error = error instanceof Error ? error : failure("GROK_ADMISSION_PROTOCOL_VIOLATION", "invalid native acknowledgement"); throw state.error; } }; diff --git a/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts b/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts index 6770890cf53..2b2e8798eec 100644 --- a/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts @@ -5,6 +5,38 @@ import { join } from "node:path"; import { createAcpRuntime, createAgentRegistry, createRuntimeStore, type AcpRuntimeHandle } from "acpx/runtime"; import { expect, it } from "vitest"; import { createGrokEffortAdmission } from "./grok-effort.js"; +import { acpxSidecarErrorCode } from "../../cli/acpx-sidecar-input.js"; + +it("retains a native Grok acknowledgement failure through the real ACPX connection", async () => { + const root = await mkdtemp(join(tmpdir(), "grok-rejected-admission-")); + const fixture = join(root, "grok.cjs"); + await writeFile(fixture, String.raw` + const readline = require("node:readline"); + readline.createInterface({input:process.stdin}).on("line", line => { + const request = JSON.parse(line); + const result = request.method === "initialize" + ? {protocolVersion:1,agentCapabilities:{},authMethods:[]} + : {sessionId:"private-session",models:{currentModelId:"grok-4.7",availableModels:[{modelId:"grok-4.7",name:"grok-4.7"}]},_meta:{privateToken:"private-token-canary"}}; + process.stdout.write(JSON.stringify({jsonrpc:"2.0",id:request.id,result})+"\n"); + }); + `); + const admission = createGrokEffortAdmission("high", "grok-4.7"); + const children: ReturnType[] = []; + const runtime = createAcpRuntime({ cwd: root, sessionStore: createRuntimeStore({stateDir:join(root,"state")}), + agentRegistry: createAgentRegistry({overrides:{grok:[fixture,"agent","stdio"]}}), permissionMode:"approve-reads", + protocolGuardFactory: () => admission.createGuard(), + spawnAgent: () => { const child = spawn(process.execPath,[fixture],{cwd:root,env:{},stdio:"pipe"}); children.push(child); return child; }, + }); + try { + const failure = await runtime.ensureSession({sessionKey:"native-rejection",agent:"grok",mode:"persistent",sessionOptions:{model:"grok-4.7"}}).catch(error => error); + expect(acpxSidecarErrorCode(failure)).toBe("GROK_EFFORT_ACKNOWLEDGEMENT_MISSING"); + expect(failure.message).not.toContain("private-token-canary"); + expect(() => admission.assertReady()).toThrow("missing or ambiguous"); + } finally { + for (const child of children) if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL"); + await rm(root,{recursive:true,force:true}); + } +}); it.each(["low", "high"] as const)("transports and restores native Grok %s effort through the real ACPX client", async effort => { const root = await mkdtemp(join(tmpdir(), "grok-native-session-options-")); diff --git a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts index 6f9990c550f..a11d0c684ff 100644 --- a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts +++ b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts @@ -2,13 +2,13 @@ import { QUALIFIED_ACPX_PROFILE_DATA } from "./generated-profiles.js"; /** Decodable historical profile revisions, not permission to launch them. * Exact current profile/package/digest admission is checked separately. */ -type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68; +type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69; type CurrentAcpxProfileVersion = typeof QUALIFIED_ACPX_PROFILE_DATA[keyof typeof QUALIFIED_ACPX_PROFILE_DATA]["agentProfileVersion"]; export type AcpxProfileVersion = HistoricalAcpxProfileVersion | CurrentAcpxProfileVersion; const historicalVersions: Readonly> = { pi: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], claude: [1, 2, 3, 4, 5], codex: [1, 2, 3, 4, 5], - grok: [1, 2, 3, 4, 5], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68], + grok: [1, 2, 3, 4, 5], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69], cursor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], }; diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts index f97924870e9..8d14bb2f9da 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts @@ -21,6 +21,7 @@ import { } from "../../contracts/runtime-context.js"; import { releaseMaterializedNativeRuntimeSkills } from "../runtime-context-materializer.js"; import { openCodexAcpxRuntime } from "./codex-runtime-adapter.js"; +import { acpxSidecarErrorCode } from "../../cli/acpx-sidecar-input.js"; import { stageManagedCodexCredential } from "./codex-credentials.js"; import type { VerifiedAcpxCommandLease, @@ -202,6 +203,72 @@ afterEach(async () => { }); describe("ACPX runtime host", () => { + it.each(["copilot", "cursor"] as const)("reports missing %s credentials as credential admission", async agent => { + const fixture = await hostFixture(); + const openRuntime = vi.fn(async () => runtimePort()); + const failure = await AcpxRuntimeHost.open({ ...fixture.options, agent, model:"gpt-5.6-sol", + permissionMode:"approve-all",environment:{} }, fixture.dependencies({openRuntime})).catch(error => error); + expect(failure).toMatchObject({code:agent === "copilot" ? "COPILOT_AUTH_REQUIRED" : "CURSOR_CREDENTIALS_MISSING"}); + expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_CREDENTIAL"); + expect(openRuntime).not.toHaveBeenCalled(); + expect(fixture.commandClose).not.toHaveBeenCalled(); + }); + it("retains binding admission when the selected workspace is missing", async () => { + const fixture = await hostFixture(); + await rm(fixture.options.workingDirectory,{recursive:true}); + const openRuntime = vi.fn(async () => runtimePort()); + const dependencies = fixture.dependencies({openRuntime}); + const verifyInstallation = vi.fn(dependencies.verifyInstallation!); + const failure = await AcpxRuntimeHost.open({ ...fixture.options,agent:"codex",model:"gpt-5.6-sol", + permissionMode:"approve-all" }, {...dependencies,verifyInstallation}).catch(error => error); + expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_BINDING"); + expect(verifyInstallation).not.toHaveBeenCalled(); + expect(openRuntime).not.toHaveBeenCalled(); + }); + 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"); + const openRuntime = vi.fn(async () => { throw failure; }); + const dependencies = fixture.dependencies({ openRuntime }); + if (stage === "installation") dependencies.verifyInstallation = async () => { throw failure; }; + if (stage === "sandbox") dependencies.prepareSandbox = async () => { throw failure; }; + if (stage === "credential") dependencies.stageCredential = async () => { throw failure; }; + const rejected = await AcpxRuntimeHost.open({ ...fixture.options, agent: "codex", model: "gpt-5.6-sol", + permissionMode: "approve-all", environment: { OPENAI_API_KEY: "synthetic-test-key" } }, dependencies).catch(error => error); + expect(rejected).toBe(failure); + expect(acpxSidecarErrorCode(rejected)).toBe(`ACPX_ADMISSION_${stage.toUpperCase()}`); + if (stage !== "runtime") expect(openRuntime).not.toHaveBeenCalled(); + if (stage === "runtime") expect(fixture.commandClose).toHaveBeenCalledOnce(); + }); it("rejects preconfigured Codex before admission resources or runtime launch", async () => { const fixture = await hostFixture(); const openRuntime = vi.fn(), prepareSandbox = vi.fn(); diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts index ddba9de9944..38b39f80593 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts @@ -1,4 +1,5 @@ import { resolveGrokDisableWebSearch } from "./grok-web-search.js"; +import { recordAcpxAdmissionFailure, type ACPX_ADMISSION_CODES } from "./admission-diagnostics.js"; import { composeCopilotSystemInstructions } from "./copilot-profile.js"; import { resolveClaudeEffort } from "./claude-effort.js"; import { resolveGrokEffort } from "./grok-effort.js"; @@ -384,7 +385,7 @@ export class AcpxRuntimeHost { ? { providerPolicy: options.providerPolicy } : {}), }), dependencies.retainAdmissionCleanup, - ); + ).catch(error => { throw recordAcpxAdmissionFailure(error, "binding"); }); if (options.expectedIdentity) { verifyExpectedAcpxIdentity(options.expectedIdentity, binding, null); } @@ -405,7 +406,7 @@ export class AcpxRuntimeHost { profile, ), dependencies.retainAdmissionCleanup, - ); + ).catch(error => { throw recordAcpxAdmissionFailure(error, "installation"); }); if (installation.commandDigest !== profile.commandDigest) { throw new Error("Verified ACPX installation does not match its profile"); } @@ -451,6 +452,7 @@ export class AcpxRuntimeHost { retainedRejectedRuntimeAdmissions.add(retained); retainRuntimeHostCleanup(ownedCleanup); }; + let admissionStage: keyof typeof ACPX_ADMISSION_CODES = "sandbox"; try { const sandbox = await runAbortableAdmissionStage( options.signal, @@ -473,6 +475,7 @@ export class AcpxRuntimeHost { }), dependencies.retainAdmissionCleanup, ); + admissionStage = "credential"; if (options.agent === "codex" && !preconfigured) { credential = await acquireAbortableAdmissionResource({ signal: options.signal, @@ -508,6 +511,7 @@ export class AcpxRuntimeHost { }); } options.signal?.throwIfAborted(); + admissionStage = "configuration"; const configurationLifetime = credential; await runAbortableAdmissionStage( options.signal, @@ -522,7 +526,9 @@ export class AcpxRuntimeHost { }, ); options.signal?.throwIfAborted(); + admissionStage = "credential"; assertAcpxProfileEnvironment(options.agent, sandbox.launchEnvironment); + admissionStage = "configuration"; let launchEnvironment = sandbox.launchEnvironment; // This copy is registered by the authenticated controller for its company, // agent and run. Environment/config values cannot widen the grant. Each @@ -548,6 +554,7 @@ export class AcpxRuntimeHost { ); options.signal?.throwIfAborted(); } + admissionStage = "skills"; if (options.agent === "pi") { const skills = await acquireAbortableAdmissionResource({ signal: options.signal, @@ -582,6 +589,7 @@ export class AcpxRuntimeHost { ); options.signal?.throwIfAborted(); } + admissionStage = "command"; command = await acquireAbortableAdmissionResource({ signal: options.signal, acquire: () => installation.openCommand({ signal: options.signal }), @@ -595,6 +603,7 @@ export class AcpxRuntimeHost { signal => installation.openCommand({ signal }), ); command = commandOwner.command; + admissionStage = "tool_bridge"; toolBridge = options.semanticTools ? await acquireAbortableAdmissionResource({ signal: options.signal, @@ -613,6 +622,7 @@ export class AcpxRuntimeHost { if (admittedLifetime === null) { throw new Error("ACPX provider lifetime lease is unavailable"); } + admissionStage = "runtime"; runtime = await acquireAbortableAdmissionResource({ signal: options.signal, acquire: async () => { @@ -683,6 +693,7 @@ export class AcpxRuntimeHost { }); }, }); + admissionStage = "identity"; const runtimeIdentity = await runAbortableAdmissionStage( options.signal, () => @@ -734,6 +745,7 @@ export class AcpxRuntimeHost { }); } catch (error) { error = classifyAcpxProfileError(options.agent, error) ?? error; + recordAcpxAdmissionFailure(error, admissionStage); const cleanup = cleanupRuntimeResources( runtime, toolBridge, diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json new file mode 100644 index 00000000000..8908e2f0fb1 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json @@ -0,0 +1,87 @@ +{ + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 70, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "00d6af17216cb0cad902b48633eb19bf0ec6b05837a984cb24661fca593f09c1", + "policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3", + "distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107", + "agentFilesBinding": "registered-runtime-context-v1", + "systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a", + "upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0", + "patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679", + "messageIdentityContract": "copilot-native-message-id-v1", + "ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1", + "permissionContextContract": "copilot-edit-permission-context-v1", + "permissionContextSourceSha256": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b", + "permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668", + "permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9", + "permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f", + "permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4", + "semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2", + "semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791", + "semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5", + "toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706", + "semanticNormalizedInputContract": "validated-forwarded-input-commit-v1", + "semanticSidecarSourceSha256": "7d6e948b7cff4df52a9afc916db46ace0f42b4b2d3134ba0c21ee0a928857e62", + "semanticDirectDriverSourceSha256": "d683429427dd8a1a5efe50952018ea486f808bdbf0a92fc5946ee267fe2c72ee", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "f22d9caf2035b600abc1a6625ae23ca97f1409d7e29ed5b7264bb8f9784c7dc9", + "semanticSchemaBundleSourceSha256": "4cae579aac836762fdbf92c5447425d8b756e87a5766bc0c7a1885a90541c8d6", + "semanticSidecarProtocolSourceSha256": "26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "5c9008d121979a3a8987e6b8a5752172eafdc4a2b5d725a73f3bbb8fb83be8ff", + "configuredEnvironmentRustSourceSha256": "85c87fae62b8a9377e4f0a4c00302c7519fae4397b1c48253f8f2b7ba7faf994", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "3e61c4cdd4b78e38fcb16eaadae4f1fd65ce3c7127184ea7a80948c8e5d77f88", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "1fe6fa9e6b96d84d70432b85a57c473f833606d4a521563d5022b4d3c41576ce", + "nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94", + "transportContractSourceSha256": "03fca93d36a89b0cc0917335e293ad097fdf04356d21b307827bb0ea4c13f523", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "93a631b8439df1590b0cf442cf84a9b05f004f12bfee90291651ccfc0861effe", + "runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "56a177d3e702914818dca4ca50e061f0d25f988bfc4c0582c36ce23c1696d82f", + "nativeExecutionSourceSha256": "d60b8ea63bad975c0144f0702b2749d7db9a7da50680a6147f20d4d11f2d5297", + "profileCompatibilitySourceSha256": "0832086e776ae28048f78ce87664bd196b73349899168746833d943e399bed76", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "1785e3f31e5dad5a13b53e25d8cf9482a9bd89a27b6c127830aed6b81acc605c", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "7586d44b47c83395498dc8806287cd3538ef58177ba7c5949f5766ed105a0161", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "runtimeHostSourceSha256": "c779acab9ae4bbab4a13002d2e12c8c1acb208d0c4b575e7c5697fc3e7bb44c3", + "runtimeAdapterSourceSha256": "7c47c05306ebe7d1ca6b5d046a96f5726a48adec4d27e7c16788f505f25b5db2", + "runtimeSandboxSourceSha256": "981449a4c6d10f682ecf86d834eed07faca30a7d9b419fb8db4973a442a343ec", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +}