From a8e07500db68f187b19de83823eb7fabf1196021 Mon Sep 17 00:00:00 2001 From: Nicky Leach Date: Thu, 29 Oct 2026 18:36:04 -0700 Subject: [PATCH] fix(runner): preserve runtime activity through the native bridge (#18239) --- doc/run-log-events.md | 6 ++ packages/paperclip-runner/acpx-profiles.json | 4 +- .../src/generated_acpx_profiles.rs | 2 +- .../scripts/copilot-profile-identity.mjs | 4 +- .../drivers/acpx/copilot-installation.test.ts | 26 ++++- .../src/drivers/acpx/generated-profiles.ts | 4 +- .../src/drivers/acpx/profile-compatibility.ts | 4 +- .../codex-app-server-driver.events.test.ts | 53 ++++++++++ .../src/drivers/codex/codex-driver-values.ts | 2 + .../drivers/codex/codex-harness-session.ts | 77 ++++++++------ .../codex/codex-session-notifications.ts | 9 ++ .../src/drivers/codex/codex-session-state.ts | 1 + .../src/live/runnerd-codex-transport.ts | 10 +- .../src/live/runnerd-runtime-activity.test.ts | 100 ++++++++++++++++++ .../copilot-profile-v86-identity.json | 88 +++++++++++++++ .../copilot-profile-v87-identity.json | 88 +++++++++++++++ 16 files changed, 431 insertions(+), 47 deletions(-) create mode 100644 packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts create mode 100644 packages/paperclip-runner/test/fixtures/copilot-profile-v86-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/copilot-profile-v87-identity.json diff --git a/doc/run-log-events.md b/doc/run-log-events.md index 5221b400f19..f3937486b9c 100644 --- a/doc/run-log-events.md +++ b/doc/run-log-events.md @@ -676,6 +676,12 @@ acknowledgement and replay deduplicate delivery. Existing run-read authorization and secret redaction apply to saved and live events. Recording reported activity is not independent proof that it happened and grants no resource permission. +The native transport forwards runtime activity as a run-scoped fact. It retains +the authenticated run identity across warm session attachment. Provider messages +cannot impersonate this transport-owned notification. +New-run activity waits for warm attachment to finish. A failed attachment does +not retarget queued activity to the previous run. + Runner accepts local activity records only with an opaque context captured before the operation. Warm run attachment and process recovery rotate that authority. Runner supplies the run identity from its active authenticated diff --git a/packages/paperclip-runner/acpx-profiles.json b/packages/paperclip-runner/acpx-profiles.json index 462243d04fe..4759711c201 100644 --- a/packages/paperclip-runner/acpx-profiles.json +++ b/packages/paperclip-runner/acpx-profiles.json @@ -42,12 +42,12 @@ "requiresProviderPolicy": true }, "copilot": { - "agentProfileVersion": 85, + "agentProfileVersion": 87, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:267e04ce9335f7641f665d9b952bc4637056ee1cfeac23715b7aefbc3a8df3c9", + "commandDigest": "sha256:ce2f4322cbc545932d6c725c57005eea1f44f2c743d5384c6ea90909563d958d", "requiresProviderPolicy": true }, "claude": { 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 881671f1c04..bbfac6b48eb 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 @@ -61,7 +61,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: None, agent_runtime_version: None, command_digest: - "sha256:267e04ce9335f7641f665d9b952bc4637056ee1cfeac23715b7aefbc3a8df3c9", + "sha256:ce2f4322cbc545932d6c725c57005eea1f44f2c743d5384c6ea90909563d958d", requires_provider_policy: true, qualification_pending: false, }, diff --git a/packages/paperclip-runner/scripts/copilot-profile-identity.mjs b/packages/paperclip-runner/scripts/copilot-profile-identity.mjs index 15777f782cb..e8840ebb75f 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-v85-identity.json"); +const fixture = resolve(root, "test/fixtures/copilot-profile-v87-identity.json"); const historical = JSON.parse(readFileSync(resolve(root, "test/fixtures/copilot-profile-v12-identity.json"), "utf8")); -const declaration = { ...historical.declaration, agentProfileVersion: 85 }; +const declaration = { ...historical.declaration, agentProfileVersion: 87 }; delete declaration.permissionIdentitySourceSha256; const sources = { displayEventSourceSha256: "src/drivers/acpx/copilot-events.ts", 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 049fd34ff22..0d4dba823e7 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,9 @@ 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(85); + expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(87); expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined(); - expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:267e04ce9335f7641f665d9b952bc4637056ee1cfeac23715b7aefbc3a8df3c9"); + expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:ce2f4322cbc545932d6c725c57005eea1f44f2c743d5384c6ea90909563d958d"); }); it("preserves published v65 while binding retained-maintenance waits in v66", async () => { @@ -664,6 +664,28 @@ it("preserves the historical sleep-maintenance v64 identity", async () => { expect(current.commandDigest).not.toBe(prior.commandDigest); }); + it("preserves v85 while binding runtime activity delivery in v86", () => { + const priorBytes = readFileSync(new URL("../../../test/fixtures/copilot-profile-v85-identity.json", import.meta.url)); + expect(createHash("sha256").update(priorBytes).digest("hex")).toBe("15d2790d94d7cc46ac728119ed9b4d07ed2b682be039b3debc716600708d8c1a"); + const prior = JSON.parse(priorBytes.toString("utf8")); + const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v86-identity.json", import.meta.url), "utf8")); + expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual([ + "agentProfileVersion", "nativeSessionRuntimeSourceSha256", "profileCompatibilitySourceSha256", "runtimeRequestBridgeSourceSha256", "runtimeRequestFacadeSourceSha256", + ]); + expect(isSupportedAcpxProfileVersion("copilot", 85)).toBe(true); + }); + + it("preserves v86 while binding activity attachment settlement in v87", () => { + const priorBytes = readFileSync(new URL("../../../test/fixtures/copilot-profile-v86-identity.json", import.meta.url)); + expect(createHash("sha256").update(priorBytes).digest("hex")).toBe("6baeb98adc7132b2afdce8b1d2da1df2f00356677eb4497c49c05aa9def79138"); + const prior = JSON.parse(priorBytes.toString("utf8")); + const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v87-identity.json", import.meta.url), "utf8")); + expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual([ + "agentProfileVersion", "harnessSessionSourceSha256", "profileCompatibilitySourceSha256", "runtimeRequestFacadeSourceSha256", + ]); + expect(isSupportedAcpxProfileVersion("copilot", 86)).toBe(true); + }); + it("admits the current declaration and binds its source policy, receipts and patch hashes", () => { const identity = JSON.parse(readFileSync(new URL(`../../../test/fixtures/copilot-profile-v${QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion}-identity.json`, import.meta.url), "utf8")); expect(identity.declaration.systemInstructionDelivery).toBe(COPILOT_SYSTEM_INSTRUCTION_DELIVERY); diff --git a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts index 01ac0e5227f..131fb0d1286 100644 --- a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts +++ b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts @@ -62,12 +62,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "copilot", - "agentProfileVersion": 85, + "agentProfileVersion": 87, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:267e04ce9335f7641f665d9b952bc4637056ee1cfeac23715b7aefbc3a8df3c9", + "commandDigest": "sha256:ce2f4322cbc545932d6c725c57005eea1f44f2c743d5384c6ea90909563d958d", "permissionPolicy": "interactive" }, "claude": { diff --git a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts index dc7de909eee..651986ef925 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 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84; +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 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86; 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, 30, 31, 32, 33, 34], claude: [1, 2, 3, 4, 5], codex: [1, 2, 3, 4, 5], - grok: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 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, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84], + grok: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 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, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86], cursor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], }; diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts index c97a97507cd..c9e857ea401 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts @@ -43,7 +43,60 @@ import { type PrpStructuredRunResult, } from "./codex-app-server-driver.test-support.js"; +import { RUNNERD_RUNTIME_ACTIVITY } from "./codex-driver-values.js"; + describe("Codex app-server Codex driver", () => { + it("rejects provider-forged and previous-run activity notifications", async () => { + const transport = new FakeCodexTransport(); + const session = await makeDriver([transport]).openSession({ + runId: "activity-current", normalizedSessionId: "activity-session", workingDirectory: WORKSPACE, + }); + const payload = { namespace: "example.runtime", activityKey: "screen.used" }; + transport.push("paperclip/runtimeActivityRecorded", { runId: "activity-current", payload }); + transport.push("paperclip/runtimeActivityRecorded", { + [RUNNERD_RUNTIME_ACTIVITY]: true, runId: "activity-previous", payload, + }); + const turn = await session.startTurn({ message: { role: "user", text: "Finish." } }); + transport.push("turn/started", { threadId: "thread-1", turn: { id: turn.turnId, status: "inProgress" } }); + transport.push("paperclip/runResult", { threadId: "thread-1", turnId: turn.turnId, result }); + transport.push("turn/completed", { threadId: "thread-1", turn: { id: turn.turnId, status: "completed", items: [] } }); + const events = await collectUntilTerminal(session.events()); + expect(events.some(event => event.eventType === "runtime.activity.recorded")).toBe(false); + }); + + it("drops failed-attachment activity and resumes the prior run's notification stream", async () => { + let rejectAttachment!: (error: Error) => void; + const pending = new Promise((_resolve, reject) => { rejectAttachment = reject; }); + class FailedAttachmentTransport extends FakeCodexTransport { + attachRun() { return pending; } + } + const transport = new FailedAttachmentTransport(); + const session = await makeDriver([transport]).openSession({ + runId: "activity-current", normalizedSessionId: "activity-session", workingDirectory: WORKSPACE, + }); + const attaching = expect(session.attachRun!({ runId: "activity-rejected" })).rejects.toThrow("controlled attachment failure"); + await expect(session.attachRun!({ runId: "activity-concurrent" })).rejects.toThrow("codex_run_attach_busy"); + const payload = { namespace: "example.runtime", activityKey: "screen.used" }; + transport.push("paperclip/runtimeActivityRecorded", { + [RUNNERD_RUNTIME_ACTIVITY]: true, runId: "activity-rejected", payload, + }); + await new Promise(resolve => setImmediate(resolve)); + rejectAttachment(new Error("controlled attachment failure")); + await attaching; + transport.push("paperclip/runtimeActivityRecorded", { + [RUNNERD_RUNTIME_ACTIVITY]: true, runId: "activity-current", payload, + }); + const turn = await session.startTurn({ message: { role: "user", text: "Finish." } }); + transport.push("turn/started", { threadId: "thread-1", turn: { id: turn.turnId, status: "inProgress" } }); + transport.push("paperclip/runResult", { threadId: "thread-1", turnId: turn.turnId, result }); + transport.push("turn/completed", { threadId: "thread-1", turn: { id: turn.turnId, status: "completed", items: [] } }); + const events = await collectUntilTerminal(session.events()); + expect(events.filter(event => event.eventType === "runtime.activity.recorded")).toEqual([ + expect.objectContaining({ runId: "activity-current", payload }), + ]); + expect(events.some(event => event.eventType === "run.attached")).toBe(false); + }); + it("forwards credential updates before a turn with P0 priority", async () => { const transport = new FakeCodexTransport(); const session = await makeDriver([transport]).openSession({ diff --git a/packages/paperclip-runner/src/drivers/codex/codex-driver-values.ts b/packages/paperclip-runner/src/drivers/codex/codex-driver-values.ts index 77a7b2ae959..54df5da5804 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-driver-values.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-driver-values.ts @@ -40,6 +40,8 @@ export const RUNNERD_CANONICAL_ITEM = Symbol( "paperclip.runnerd.canonical-item", ); +export const RUNNERD_RUNTIME_ACTIVITY = Symbol("paperclip.runnerd.runtime-activity"); + export function text(value: unknown, fallback = ""): string { return typeof value === "string" ? value : fallback; } diff --git a/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts b/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts index c4ea2a7bbad..d95eb72f2e5 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts @@ -106,48 +106,55 @@ export class CodexHarnessSession this.assertProtocolIntegrity(); const transportOwnsQuiescence = this.transport.attachRun !== undefined; if ( - this.turnStartPending || + this.turnStartPending || this.pendingRunAttachment !== null || (!transportOwnsQuiescence && (this.activeTurnId !== null || this.pendingRuntimeRequestMap.size > 0)) ) { throw new Error("codex_run_attach_busy"); } if (!input.runId) throw new Error("codex_run_attach_invalid"); - await this.transport.attachRun?.({ - runId: input.runId, - turnId: `turn_attachment_${randomUUID().replaceAll("-", "")}`, - itemId: `item_attachment_${randomUUID().replaceAll("-", "")}`, - ...(input.currentRunGrant ? { currentRunGrant: structuredClone(input.currentRunGrant) } : {}), - }); - this.assertProtocolIntegrity(); - if (transportOwnsQuiescence) { - // Runnerd's attachment contract performs two durable readiness probes, - // drains the settled provider tail, and rotates authority atomically. - // Its proof supersedes host reducer state that can remain stale when a - // semantic-result consumer stops before the interrupt terminal arrives. - // Drop only the prior run's already-proven-settled buffered suffix. - this.activeTurnId = null; - this.pendingRuntimeRequestMap.clear(); - this.eventQueue.clear(); + let settle!: () => void; + this.pendingRunAttachment = { runId: input.runId, settled: new Promise(resolve => { settle = resolve; }) }; + try { + await this.transport.attachRun?.({ + runId: input.runId, + turnId: `turn_attachment_${randomUUID().replaceAll("-", "")}`, + itemId: `item_attachment_${randomUUID().replaceAll("-", "")}`, + ...(input.currentRunGrant ? { currentRunGrant: structuredClone(input.currentRunGrant) } : {}), + }); + this.assertProtocolIntegrity(); + if (transportOwnsQuiescence) { + // Runnerd's attachment contract performs two durable readiness probes, + // drains the settled provider tail, and rotates authority atomically. + // Its proof supersedes host reducer state that can remain stale when a + // semantic-result consumer stops before the interrupt terminal arrives. + // Drop only the prior run's already-proven-settled buffered suffix. + this.activeTurnId = null; + this.pendingRuntimeRequestMap.clear(); + this.eventQueue.clear(); + } + if (this.codexUsageBaseline && input.runId !== this.runId) { + this.codexUsageBaseline = { baseline: { ...this.codexUsageBaseline.latest }, latest: { ...this.codexUsageBaseline.latest } }; + this.usageSnapshot = codexRunUsage(this.codexUsageBaseline); + } + this.runId = input.runId; + this.result = null; + this.resultFingerprint = null; + this.resultCallId = null; + this.resultTurnId = null; + this.dispositionOnlyRecoveryConsumed = false; + this.dispositionOnlyRecoveryTurnId = null; + this.terminal = false; + this.terminalTurns.clear(); + this.turnStarted = false; + this.protocolFailed = false; + this.protocolFailureCode = null; + this.protocolFailureMessage = null; + this.emit("run.attached", { runId: input.runId, sameSession: true }); + } finally { + this.pendingRunAttachment = null; + settle(); } - if (this.codexUsageBaseline && input.runId !== this.runId) { - this.codexUsageBaseline = { baseline: { ...this.codexUsageBaseline.latest }, latest: { ...this.codexUsageBaseline.latest } }; - this.usageSnapshot = codexRunUsage(this.codexUsageBaseline); - } - this.runId = input.runId; - this.result = null; - this.resultFingerprint = null; - this.resultCallId = null; - this.resultTurnId = null; - this.dispositionOnlyRecoveryConsumed = false; - this.dispositionOnlyRecoveryTurnId = null; - this.terminal = false; - this.terminalTurns.clear(); - this.turnStarted = false; - this.protocolFailed = false; - this.protocolFailureCode = null; - this.protocolFailureMessage = null; - this.emit("run.attached", { runId: input.runId, sameSession: true }); } contextSnapshot(): CodexModelContextSnapshot { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-session-notifications.ts b/packages/paperclip-runner/src/drivers/codex/codex-session-notifications.ts index 4e995d1ff1d..31ccd65a1fb 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-session-notifications.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-session-notifications.ts @@ -28,6 +28,7 @@ import { recordWorkspaceChanges, } from "./codex-session-workspace.js"; import { + RUNNERD_RUNTIME_ACTIVITY, boundedText, differingJsonPaths, itemFromParams, @@ -105,6 +106,14 @@ async function mapNotification(state: CodexSessionState, notification: CodexRpcN } async function mapNotificationBody(state: CodexSessionState, notification: CodexRpcNotification): Promise { + if (notification.method === "paperclip/runtimeActivityRecorded") { + const params = notification.params; + if ((params as Record)[RUNNERD_RUNTIME_ACTIVITY] !== true) return; + const pending = state.pendingRunAttachment; + if (pending && params.runId === pending.runId && params.runId !== state.runId) await pending.settled; + if (params.runId === state.runId) state.emit("runtime.activity.recorded", record(params.payload)); + return; + } if (!isSupportedCodexNotificationMethod(notification.method)) return; if (notification.method === "item/completed" && notification.params.kind === "steering_acknowledgement" && !notification.params.threadId && !notification.params.turnId && !notification.params.thread && !notification.params.turn) return; diff --git a/packages/paperclip-runner/src/drivers/codex/codex-session-state.ts b/packages/paperclip-runner/src/drivers/codex/codex-session-state.ts index 17e4e7ce38b..53f9da0b699 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-session-state.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-session-state.ts @@ -117,6 +117,7 @@ export class CodexSessionState { resultCallId: string | null = null; resultTurnId: string | null = null; turnStartPending = false; + pendingRunAttachment: { runId: string; settled: Promise } | null = null; /** * Resolves once a pending turn/start settles, on the accepted path or on a * provider rejection. A terminal notification for that turn must wait on diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 5ba7f88d37f..d994f6466de 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -97,7 +97,7 @@ import { releaseMaterializedNativeRuntimeSkills, } from "../drivers/runtime-context-materializer.js"; import { resolvePackagedRunnerBinary } from "./runner-binary.js"; -import { RUNNERD_CANONICAL_ITEM } from "../drivers/codex/codex-driver-values.js"; +import { RUNNERD_CANONICAL_ITEM, RUNNERD_RUNTIME_ACTIVITY } from "../drivers/codex/codex-driver-values.js"; // URL directory conversion preserves a trailing separator while path-derived // build artifacts do not. Normalize once so a source build cannot be @@ -6697,6 +6697,14 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { if (fromDeferredQueue) this.#deferredTurnStartEvents.shift(); else this.#eventSourceSeq = event.sourceSeq; if (event.eventType === "auth.credentials.updated" && this.options.onCredentialsUpdated) continue; + if (event.eventType === "runtime.activity.recorded") { + this.#queue.push({ method: "paperclip/runtimeActivityRecorded", params: { + [RUNNERD_RUNTIME_ACTIVITY]: true, + runId: event.envelope.runId, + payload: eventPayload, + } }); + continue; + } if ( event.eventType === "harness.ready" || event.eventType === "session.started" || diff --git a/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts b/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts new file mode 100644 index 00000000000..96bec323f05 --- /dev/null +++ b/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts @@ -0,0 +1,100 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { mkdtemp, mkdir, readFile, realpath, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { expect, it, vi } from "vitest"; +import { CodexAppServerDriver } from "../drivers/codex/codex-app-server-driver.js"; +import { collectUntilTerminal } from "../drivers/codex/codex-app-server-driver.test-support.js"; +import { validatePrpEvent } from "../protocol/replay-contract.js"; +import { createCodexTaskEnvelope } from "../contracts/codex.js"; +import { createRunnerdCodexTransport, defaultCapabilityRunnerdBinary } from "./runnerd-codex-transport.js"; + +const execute = promisify(execFile); + +it("delivers authenticated runtime activity through the real Runner bridge across runs", async () => { + const root = await realpath(await mkdtemp(join(tmpdir(), "runnerd-runtime-activity-"))); + const runner = process.env.PAPERCLIP_ATTACH_TRANSITION_RUNNER ?? defaultCapabilityRunnerdBinary(); + const workspace = join(root, "workspace"); + await Promise.all([mkdir(workspace), mkdir(join(root, "home"))]); + const environment = { PATH: process.env.PATH, HOME: join(root, "home"), CODEX_HOME: join(root, "codex-home") }; + let releasePrevious!: () => void, previousReleasing!: () => void, observedNewActivity!: () => void; + const previousRelease = new Promise(resolve => { releasePrevious = resolve; }); + const releasing = new Promise(resolve => { previousReleasing = resolve; }); + const observed = new Promise(resolve => { observedNewActivity = resolve; }); + let registrations = 0; + const bundle = createRunnerdCodexTransport({ + runnerBinary: runner, + codexCommand: resolve(import.meta.dirname, "../../runner/target/debug/fake-codex-app-server"), + codexArgs: ["--state-file", join(root, "provider.json")], + stateDirectory: root, environment, + controlPlaneRegistration: async authority => { + if (registrations++ === 0) { + await authority.start(); + return { release: async () => { previousReleasing(); await previousRelease; } }; + } + return { release: () => undefined }; + }, + lifecyclePolicy: { mode: "warm", idleTimeoutMs: 60_000 }, + prpIdentity: { runnerInstanceId: "runner-activity", environmentLeaseId: "lease-activity", + runId: "activity-first", normalizedSessionId: "session-activity", turnId: "turn-activity", itemId: "item-activity" }, + }); + const notifications = bundle.transport.notifications.bind(bundle.transport); + bundle.transport.notifications = async function* () { + for await (const notification of notifications()) { + if (notification.method === "paperclip/runtimeActivityRecorded" && notification.params.runId === "activity-next") observedNewActivity(); + yield notification; + } + }; + const driver = new CodexAppServerDriver({ + approvalPolicy: "never", includeCollaborationModeInstructions: false, environment, + taskEnvelope: createCodexTaskEnvelope({ objective: "Verify runtime activity.", contractRevision: "1", + criteria: [{ id: "objective", requirement: "Finish." }], constraints: [] }), + transportFactory: () => bundle.transport, + }); + const helper = (args: string[]) => execute(runner, args, { env: { + ...environment, PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT: join(root, "runner/runtime-notifications.json"), + } }); + const payload = { namespace: "example.runtime", activityKey: "screen.used" }; + let session: Awaited> | undefined; + let attaching: Promise | undefined; + try { + session = await driver.openSession({ runId: "activity-first", normalizedSessionId: "session-activity", workingDirectory: workspace }); + const firstContext = (await helper(["notification-context"])).stdout.trim(); + for (const runId of ["activity-first", "activity-next"]) { + if (runId === "activity-next") { + attaching = session.attachRun!({ runId }); + await releasing; + await vi.waitFor(async () => { + expect((await helper(["notification-context"])).stdout.trim()).not.toBe(firstContext); + }); + await expect(helper(["record-activity", firstContext, payload.namespace, payload.activityKey])).rejects.toThrow(); + } + const context = (await helper(["notification-context"])).stdout.trim(); + await helper(["record-activity", context, payload.namespace, payload.activityKey]); + await vi.waitFor(async () => { + const journal = JSON.parse(await readFile(join(root, "control-plane/control-plane-state.json"), "utf8")); + expect(journal.committedEvents.some((event: { eventType: string; envelope: { runId: string } }) => + event.eventType === "runtime.activity.recorded" && event.envelope.runId === runId)).toBe(true); + }); + if (attaching) { + await observed; + await new Promise(resolve => setImmediate(resolve)); + releasePrevious(); + await attaching; + } + await session.startTurn({ message: { role: "user", text: "Finish." } }); + const events = await collectUntilTerminal(session.events()); + const activities = events.filter(event => event.eventType === "runtime.activity.recorded"); + expect(activities).toHaveLength(1); + expect(activities[0]).toMatchObject({ runId, sourceKind: "runner", priority: 1, payload }); + expect(validatePrpEvent(activities[0]).ok).toBe(true); + } + } finally { + releasePrevious(); + await attaching?.catch(() => undefined); + await session?.close(); + await bundle.transport.close(); + await rm(root, { recursive: true, force: true }); + } +}, 30_000); diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v86-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v86-identity.json new file mode 100644 index 00000000000..e2d0da77d18 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v86-identity.json @@ -0,0 +1,88 @@ +{ + "commandDigest": "sha256:8d0eb9ed39fe7faabc7c2943738177e96a9a8c80a116ef76d9c1995faa8bf328", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 86, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "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": "b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "semanticDirectDriverSourceSha256": "4ec65aa1d4e905a1e732f2d043bbd39de1ab92f380276ede37d6a72efc19c64d", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb", + "semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b", + "semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "ecef0ada320a762e0a3d91c38c72d4e19e2c8e8be8c060938a47e7f1a029058a", + "configuredEnvironmentRustSourceSha256": "1671cb2735b3fa119d32f08d4a12b21b9f78a0e4d7285eb63c35df79ce17bd3c", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "58841b2d3b47ab9956db20608802edcd0e55cf5856002dc7c51e05e75bdcb8f6", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea", + "nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94", + "transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "8bf5c2ed6d6223144f7eb777e97973655301f5be10d9734f025cfc2b0bdff431", + "runtimeRequestFacadeSourceSha256": "553914b022d4735228c69619c9d209cd4684c4b57649be32d11054859903ba91", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6", + "nativeExecutionSourceSha256": "116c348e232848a80c5556494c109314baafb20fb5c97133d9656c7732ee54be", + "profileCompatibilitySourceSha256": "79740568b1cf863c87b31d5236b96858f0f5c3b42883f639cefc82c80d2ea2a7", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "e5bb6bc58b3f0e957a64b2cd99c710337ffcd9c0e1fadb341f48e033b64b99f5", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "2ed68b9aed9756fdd682721833eb2aeaa58f30f1dc8825c88886cc8e7ee92abe", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "runtimeHostSourceSha256": "26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "runtimeAdapterSourceSha256": "fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +} diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v87-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v87-identity.json new file mode 100644 index 00000000000..d6d6e4ac299 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v87-identity.json @@ -0,0 +1,88 @@ +{ + "commandDigest": "sha256:ce2f4322cbc545932d6c725c57005eea1f44f2c743d5384c6ea90909563d958d", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 87, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "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": "b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "semanticDirectDriverSourceSha256": "4ec65aa1d4e905a1e732f2d043bbd39de1ab92f380276ede37d6a72efc19c64d", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb", + "semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b", + "semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "ecef0ada320a762e0a3d91c38c72d4e19e2c8e8be8c060938a47e7f1a029058a", + "configuredEnvironmentRustSourceSha256": "1671cb2735b3fa119d32f08d4a12b21b9f78a0e4d7285eb63c35df79ce17bd3c", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "58841b2d3b47ab9956db20608802edcd0e55cf5856002dc7c51e05e75bdcb8f6", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea", + "nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851", + "transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "8bf5c2ed6d6223144f7eb777e97973655301f5be10d9734f025cfc2b0bdff431", + "runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6", + "nativeExecutionSourceSha256": "116c348e232848a80c5556494c109314baafb20fb5c97133d9656c7732ee54be", + "profileCompatibilitySourceSha256": "a924a9accec7708823acdd6b7ad0725b5cd9a6ce8d924671d1075907bf0fc8a9", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "e5bb6bc58b3f0e957a64b2cd99c710337ffcd9c0e1fadb341f48e033b64b99f5", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "2ed68b9aed9756fdd682721833eb2aeaa58f30f1dc8825c88886cc8e7ee92abe", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "runtimeHostSourceSha256": "26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "runtimeAdapterSourceSha256": "fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +}