diff --git a/.dockerignore b/.dockerignore index 8ae5f323da0..f0436b4e6e7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -56,6 +56,7 @@ packages/paperclip-runner/test-fixtures/pi-acp/** !packages/paperclip-runner/test-fixtures/pi-acp/profile-v22-identity.json !packages/paperclip-runner/test-fixtures/pi-acp/profile-v23-identity.json !packages/paperclip-runner/test-fixtures/pi-acp/profile-v24-identity.json +!packages/paperclip-runner/test-fixtures/pi-acp/profile-v25-identity.json !packages/paperclip-runner/docs/capability-contract.md # check:runner-workflow-traceability access()es every regression test the # stress-traceability spec names — those are src/**/*.test.ts files, so diff --git a/doc/architecture/runner-pi-capabilities.md b/doc/architecture/runner-pi-capabilities.md index 8b57d05c844..253fa9296ff 100644 --- a/doc/architecture/runner-pi-capabilities.md +++ b/doc/architecture/runner-pi-capabilities.md @@ -2,7 +2,7 @@ ## Current source integration — 2026-10-12 -Pi profile 24 (`sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b`) +Pi profile 25 (`sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a`) advances the shared sandbox source attestation after Claude settings initialization moved behind provider lifetime ownership. Profile 23 remains an immutable historical identity. Pi's provider executable closures, model/effort options and diff --git a/doc/architecture/runner-rich-acp-capabilities.md b/doc/architecture/runner-rich-acp-capabilities.md index 8ad361096f3..90137ed4c97 100644 --- a/doc/architecture/runner-rich-acp-capabilities.md +++ b/doc/architecture/runner-rich-acp-capabilities.md @@ -2,7 +2,7 @@ ## Current source integration — 2026-10-12 -Pi profile 24 (`sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b`) +Pi profile 25 (`sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a`) advances the shared sandbox source attestation after Claude settings initialization moved behind provider lifetime ownership. Profile 23 remains an immutable historical identity. Pi's provider executable closures, model/effort options and diff --git a/packages/paperclip-runner/acpx-profiles.json b/packages/paperclip-runner/acpx-profiles.json index 289c782947e..a26968b621e 100644 --- a/packages/paperclip-runner/acpx-profiles.json +++ b/packages/paperclip-runner/acpx-profiles.json @@ -14,12 +14,12 @@ "requiresProviderPolicy": false }, "pi": { - "agentProfileVersion": 24, + "agentProfileVersion": 25, "agentServerPackage": "pi-acp", "agentServerVersion": "0.0.33", "agentRuntimePackage": "@earendil-works/pi-coding-agent", "agentRuntimeVersion": "1.0.0", - "commandDigest": "sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b", + "commandDigest": "sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a", "requiresProviderPolicy": true }, "cursor": { diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs index 6c28264f235..cfc5f9d8a1c 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs @@ -3200,7 +3200,7 @@ mod tests { #[test] fn pi_profile_matches_published_identity_and_rejects_prior_profiles() { let published: Value = serde_json::from_str(include_str!( - "../../../../test-fixtures/pi-acp/profile-v24-identity.json" + "../../../../test-fixtures/pi-acp/profile-v25-identity.json" )) .unwrap(); let mut value = descriptor("codex"); @@ -3217,6 +3217,7 @@ mod tests { current.validate(&context()).unwrap(); for prior in [ + include_str!("../../../../test-fixtures/pi-acp/profile-v24-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v23-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v15-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v16-identity.json"), 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 99f4d02739b..5498dbfe2d9 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 @@ -29,7 +29,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: Some("@earendil-works/pi-coding-agent"), agent_runtime_version: Some("1.0.0"), command_digest: - "sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b", + "sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a", requires_provider_policy: true, }, "cursor" => AcpxReleaseProfile { diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs index 22630885bfc..39143816c96 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs @@ -17,7 +17,7 @@ use sha2::{Digest, Sha256}; const CODEX_ACPX_DIGEST: &str = "sha256:c4538599d1ab767db5dff50934f13bb5ba313a59d9c4a83e993fac4617ea63d3"; const PI_ACPX_DIGEST: &str = - "sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b"; + "sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a"; fn temporary_directory(label: &str) -> PathBuf { let nonce = SystemTime::now() diff --git a/packages/paperclip-runner/src/drivers/acpx/claude-effort.ts b/packages/paperclip-runner/src/drivers/acpx/claude-effort.ts index 504ddd08afe..e8b048a53b5 100644 --- a/packages/paperclip-runner/src/drivers/acpx/claude-effort.ts +++ b/packages/paperclip-runner/src/drivers/acpx/claude-effort.ts @@ -8,8 +8,6 @@ export function resolveClaudeEffort(agent: string, value: unknown): ClaudeEffort return value; } export function createClaudeEffortAdmission(expected: ClaudeEffort) { - // Every connection, including session/load, must select and acknowledge the - // configured effort. A restored preference alone never admits a prompt. return createAcpxConfigAdmission(expected, { label: "Claude effort", configId: "effort", supportedValues: ["default", "low", "medium", "high", "xhigh", "max"] }); } diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-credentials.test.ts b/packages/paperclip-runner/src/drivers/acpx/codex-credentials.test.ts index c13ffcb1596..f1ac121755d 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-credentials.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-credentials.test.ts @@ -1699,9 +1699,6 @@ async function silentPrimaryQuorumFixture( if (failure?.status === "rejected") throw failure.reason; }, }; - // Only fixture preparation may retry. Release the unselected candidates - // immediately before the caller stages once. A foreign bind racing this - // handoff remains a visible production-call failure, never a hidden retry. const released = await Promise.allSettled( owned .filter((_, index) => !occupiedIndices.includes(index)) diff --git a/packages/paperclip-runner/src/drivers/acpx/config-admission.ts b/packages/paperclip-runner/src/drivers/acpx/config-admission.ts index be597f10e16..73e5faf73bc 100644 --- a/packages/paperclip-runner/src/drivers/acpx/config-admission.ts +++ b/packages/paperclip-runner/src/drivers/acpx/config-admission.ts @@ -2,8 +2,6 @@ function object(value: unknown): Record { return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; } -/** Native mode is independent of permissions. Every connection must prove its - * current mode before a prompt; a persisted ACPX preference is not proof. */ export function createAcpxConfigAdmission(expected: string, options: { label: string; configId: string; supportedValues: readonly string[]; restoring?: boolean; sessionMode?: boolean }) { type State = { sessionId: string | null; mode: string | null; error: Error | null }; let current: State | null = null; @@ -17,8 +15,6 @@ export function createAcpxConfigAdmission(expected: string, options: { label: st const configs = Array.isArray(result.configOptions) ? result.configOptions.filter(value => object(value).id === options.configId) : []; if (configs.length !== 1) throw failure("native mode configuration is missing or ambiguous"); const mode = object(configs[0]).currentValue; - // Before exact model selection, Pi can legitimately report a different - // native model's level. Observation alone never admits the first prompt. if (typeof mode !== "string" || !options.supportedValues.includes(mode)) throw failure("native mode is unsupported"); if (session && options.sessionMode && object(result.modes).currentModeId !== mode) throw failure("native mode acknowledgements conflict"); return mode; @@ -56,16 +52,12 @@ export function createAcpxConfigAdmission(expected: string, options: { label: st pending.set(message.id, { method, sessionId: params.sessionId, selectsMode: params.configId === options.configId }); } else if (options.sessionMode && method === "session/set_mode") { if (!state.sessionId || params.sessionId !== state.sessionId || params.modeId !== expected) throw failure("mode control changes the selected mode"); - // This method only returns {}; require a later config/session - // acknowledgement rather than treating that empty result as proof. state.mode = null; } return; } if (message.method === "session/update" && object(params.update).sessionUpdate === "config_option_update") { const update = object(params.update); - // Config updates may contain only model options. A mode entry is - // authoritative observation, but never an admission acknowledgement. if (Array.isArray(update.configOptions) && update.configOptions.some(value => object(value).id === options.configId)) { if (state.sessionId && params.sessionId !== state.sessionId) throw failure("mode update belongs to a different session"); const observed = readMode(update, false); diff --git a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts index 20b368c2ab4..ecc8eea459e 100644 --- a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts +++ b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts @@ -22,12 +22,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "pi", - "agentProfileVersion": 24, + "agentProfileVersion": 25, "agentServerPackage": "pi-acp", "agentServerVersion": "0.0.33", "agentRuntimePackage": "@earendil-works/pi-coding-agent", "agentRuntimeVersion": "1.0.0", - "commandDigest": "sha256:d25d674cda9a0853bdbf0521025099f1424c573d2f533630cb67d90e1bfcda3b", + "commandDigest": "sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a", "permissionPolicy": "interactive" }, "cursor": { diff --git a/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts b/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts index b3a848ec8c0..640bfb7b954 100644 --- a/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts @@ -104,11 +104,19 @@ describe("Pi installation factory", () => { expect(current.commandDigest).not.toBe(prior.commandDigest); }); + it("retains profile 24 while binding the comment-free sandbox source in profile 25", async () => { + const read = async (version: number) => JSON.parse(await readFile(new URL(`../../../test-fixtures/pi-acp/profile-v${version}-identity.json`, import.meta.url), "utf8")); + const prior = await read(24), current = await read(25); + expect(Object.keys(current.declaration).filter(key => JSON.stringify(current.declaration[key]) !== JSON.stringify(prior.declaration[key])).sort()) + .toEqual(["agentProfileVersion", "runtimeSandboxSourceSha256"]); + expect(current.commandDigest).not.toBe(prior.commandDigest); + }); + it("binds the profile declaration to the reviewed patch and platform closure pins", async () => { const hash = (bytes: string | Uint8Array) => createHash("sha256").update(bytes).digest("hex"); const canonical = (value: any): string => Array.isArray(value) ? `[${value.map(canonical).join(",")}]` : value && typeof value === "object" ? `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}` : JSON.stringify(value); - const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v24-identity.json", import.meta.url), "utf8")); + const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v25-identity.json", import.meta.url), "utf8")); const declaration = identity.declaration; expect(declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1"); expect(identity.commandDigest).toBe(`sha256:${hash(canonical(declaration))}`); @@ -139,21 +147,21 @@ describe("Pi installation factory", () => { it("rejects legacy profiles and tampered executable identities", () => { expect(() => assertPiInstallationProfile(candidate())).not.toThrow(); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 24"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 24"); - for (const priorVersion of [15, 16, 17, 18, 19, 20, 21, 22, 23]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 24"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 25"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 25"); + for (const priorVersion of [15, 16, 17, 18, 19, 20, 21, 22, 23, 24]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 25"); for (const changed of [{ agentServerVersion: "latest" }, { commandDigest: `sha256:${"0".repeat(64)}` }, { agentRuntimePackage: "ambient-pi" }]) { expect(() => assertPiInstallationProfile({ ...candidate(), ...changed })).toThrow("trusted declaration"); } diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts index 9bd4402b9be..fe353e6a6f7 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts @@ -483,8 +483,6 @@ export class AcpxRuntimeHost { }); } options.signal?.throwIfAborted(); - // Await this write under ownership even if cancellation arrives. Releasing - // the lease before it settles could let a late write affect the next owner. await sandbox.initializeOwnedConfiguration?.(); options.signal?.throwIfAborted(); assertAcpxProfileEnvironment(options.agent, sandbox.launchEnvironment); diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts index 6b315ca4470..aebebe86c5c 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts @@ -53,7 +53,6 @@ export interface AcpxRuntimeSandbox { cacheDirectory: string; agentHomeDirectory: string; workspaceRecordPath: string; - /** Mutate provider settings only while the host holds its lifetime lease. */ initializeOwnedConfiguration?: () => Promise; launchEnvironment: Readonly; persistedEnvironment: Readonly; @@ -405,9 +404,6 @@ export async function prepareAcpxRuntimeSandbox(input: { `${JSON.stringify({ model: input.binding.requestedModel, availableModels: [input.binding.requestedModel], - // Claude reloads effort from settings on a new ACP connection. Keep - // the bound choice available during model verification and resume; - // the protocol guard still requires a provider acknowledgement. ...(input.binding.claudeEffort ? { effortLevel: input.binding.claudeEffort } : {}), ...((input.binding.permissionMode === "approve-reads" || input.binding.permissionMode === "approve-paperclip") ? { permissions: { allow: claudePaperclipPermissionRules(input.tools ?? [], input.binding.permissionMode) } } diff --git a/packages/paperclip-runner/src/live/live-session.test.ts b/packages/paperclip-runner/src/live/live-session.test.ts index 6e050568d29..9292aa316ab 100644 --- a/packages/paperclip-runner/src/live/live-session.test.ts +++ b/packages/paperclip-runner/src/live/live-session.test.ts @@ -1766,9 +1766,6 @@ describe("Capability live runnerd and Codex session", () => { expect((await store.load(replacement.id))?.status).toBe("suspended"); }); - // This case includes a deliberate 500 ms killed turn, a resumed turn with - // its own 5-second bound, and durable writes. The enclosing test must not - // expire at the resumed turn's deadline before those assertions can finish. it("durably resumes a distinct attempt, preserves partial usage, and deduplicates the semantic effect", async () => { const state = providerState(); const directory = await mkdtemp(join(tmpdir(), "capability-live-durable-")); diff --git a/packages/paperclip-runner/test-fixtures/pi-acp/profile-v25-identity.json b/packages/paperclip-runner/test-fixtures/pi-acp/profile-v25-identity.json new file mode 100644 index 00000000000..8c3e9a4f4cd --- /dev/null +++ b/packages/paperclip-runner/test-fixtures/pi-acp/profile-v25-identity.json @@ -0,0 +1,50 @@ +{ + "commandDigest": "sha256:dd67387487dadb08da76534c057dc0aa059f7698d1779cbd5fc452813828167a", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "pi", + "agentProfileVersion": 25, + "acpxVersion": "0.13.1", + "agentServerVersion": "0.0.33", + "agentRuntimeVersion": "1.0.0", + "nodeVersion": "24.21.0", + "closure": { + "darwin-arm64": "282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf", + "darwin-x64": "64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e", + "linux-x64": "713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410" + }, + "wrapperSha256": "9d129b3d38772e93e97080aa6c4e574ac5df4e47ce5bc331a484a9fbf8188b36", + "helperSha256": "41e0490b617da0d60e0c8ec58ef311236f945129d99f816cff6897f78da7f91d", + "extensionSha256": "f5818573a355702388072b28926897db546a5cf346b5171f646582ce5f9102a4", + "agentFilesBinding": "registered-runtime-context-v1", + "nativeQuestions": "select-confirm-input-editor-v4-root-field-types", + "pathPolicy": "sdk-normalization-v1", + "mcpToolNames": "provider-safe-exact-source-v1", + "acpxPatchSha256": "00d6af17216cb0cad902b48633eb19bf0ec6b05837a984cb24661fca593f09c1", + "nativeAssistantMessages": "pi1-rpc-deltas-v3-bound-message-and-tool-receipts", + "nativeNotices": "session-extension-v2-bounded-runtime-failure", + "messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd", + "noticeProjectionSha256": "014485adc690998e395d334ea3ae67f72be6060a2fc78a1185df7b8d1da28fbf", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "nativeInputDisposition": "queued-only-v1", + "cacheWarming": "disabled-owned-decision-v1", + "thinkingLevel": { + "supported": [ + "off", + "low", + "high", + "max" + ], + "newConfigurationDefault": "low", + "admission": "explicit-native-effective-before-prompt-v1", + "recovery": "mode-bound-v1" + }, + "modelSelection": "caller-selected-native-verified-v1", + "providerConfigurationSourceSha256": "d5b4c1a426afc85f040c9b55358d1d645299586dc8cec72d1be2509187ca3865", + "credentialEnvironmentSourceSha256": "f8dc40ea1aa894bd44754a35a8f65fbcaa2f4c7ffc88cc5fdaa965e0d378d9bc", + "runtimeSandboxSourceSha256": "144d62b3415cfedf272b5b052e14af1e41fe50a644af84247fc9c20c7ef7edde", + "recoveryIdentitySourceSha256": "8479e04da1ae2f7742e34f045b0270b99e9c3ae39a5b8fb9f164e5776d83224c", + "nativeCancellation": "acknowledged-cancelled-terminal-preserves-usage-v1", + "dependencySecurityPatchSha256": "5273a195b952f233336122842faaf650193c04f36b9359d20d4d63079b0ce814" + } +}