Merge branch 'codex/dependable-qualification-checkpoint' into codex/dependable-finalization-recovery

* codex/dependable-qualification-checkpoint:
  fix: carry server-owned workspace continuations through runner bridges
This commit is contained in:
Dotta committed 2026-10-21 10:05:50 -05:00
commit 408ffba914
18 files changed
+270 -28

No files matched your search

+2 -2
View File
@@ -32,12 +32,12 @@
"requiresProviderPolicy": true
},
"copilot": {
"agentProfileVersion": 54,
"agentProfileVersion": 55,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:171d865b9b6ff2eda3926dc957caf2993cd7e1fa1b4359e8b60b83f589bd67fa",
"commandDigest": "sha256:2c9e7f0fee36d2f7f0b253a86f3587b7bf2d9406f774b3b91e13678881ee7d48",
"requiresProviderPolicy": true
},
"claude": {
@@ -659,6 +659,11 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let expected_canonical_task_context_file =
argument(&args, "--expected-canonical-task-context-file");
let emit_tool_call = args.iter().any(|value| value == "--emit-tool-call");
let terminal_result = argument(&args, "--terminal-result-file")
.map(|path| -> Result<Value, Box<dyn std::error::Error>> {
Ok(serde_json::from_str(&fs::read_to_string(path)?)?)
})
.transpose()?;
let replay_completed_tool_call = args
.iter()
.any(|value| value == "--replay-completed-tool-call");
@@ -976,6 +981,12 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}
continue;
}
if message.get("method").is_none() && message.get("id") == Some(&json!("terminal-result-request")) {
let success = message.pointer("/result/success") == Some(&json!(true));
log_call(call_log.as_deref(), if success { "terminal-result:accepted" } else { "terminal-result:rejected" })?;
finish_turn(&state_path, &mut state, if success { "completed" } else { "failed" })?;
continue;
}
if message.get("method").is_none() && message.get("id") == Some(&json!("tool-request-1")) {
if message.pointer("/result/success") == Some(&json!(false)) {
log_call(call_log.as_deref(), "tool-response:failure")?;
@@ -1706,6 +1717,12 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
"arguments":{"query":{"PRIVATE_QUERY":"private search"}}
}}),
)?;
} else if let Some(result) = &terminal_result {
send(json!({
"id": "terminal-result-request", "method": "item/tool/call",
"params": { "threadId": state.thread_id, "turnId": provider_turn_id,
"callId": "terminal-result-call", "tool": "paperclip_finish", "arguments": result }
}))?;
} else if emit_tool_call {
send(json!({
"id": "tool-request-1",
@@ -47,7 +47,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
agent_runtime_package: None,
agent_runtime_version: None,
command_digest:
"sha256:171d865b9b6ff2eda3926dc957caf2993cd7e1fa1b4359e8b60b83f589bd67fa",
"sha256:2c9e7f0fee36d2f7f0b253a86f3587b7bf2d9406f774b3b91e13678881ee7d48",
requires_provider_policy: true,
},
"claude" => AcpxReleaseProfile {
@@ -557,13 +557,14 @@ fn admit_terminal_tool_authority(
"paperclip_finish" => {
matches!(disposition.as_str(), "done" | "needs_review")
|| (disposition == "yielded"
&& matches!(
input
.get("continuation")
.and_then(|continuation| continuation.get("kind"))
.and_then(Value::as_str),
Some("response_wake" | "monitor" | "case_handoff")
))
&& (input.get("continuation").map_or(true, Value::is_null)
|| matches!(
input
.get("continuation")
.and_then(|continuation| continuation.get("kind"))
.and_then(Value::as_str),
Some("response_wake" | "monitor" | "case_handoff" | "same_agent")
)))
}
"paperclip_block" => disposition == "blocked",
_ => false,
@@ -5311,7 +5312,27 @@ mod tests {
}
#[test]
fn terminal_tool_authority_rejects_an_unbound_yield() {
fn terminal_tool_authority_preserves_controller_resolved_workspace_yields() {
for continuation in [None, Some(Value::Null), Some(json!({
"kind": "same_agent",
"summary": "Continue the prepared repository.",
"idempotencyKey": "repository:task:receipt:run"
}))] {
let mut state = opencode_result_state();
let mut result = valid_opencode_result();
result["reportedWorkDisposition"] = json!("yielded");
if let Some(value) = continuation {
result["continuation"] = value;
}
admit_terminal_tool_authority(&mut state, "paperclip_finish", &result, false).unwrap();
assert_eq!(state.active_provider_result_disposition.as_deref(), Some("yielded"));
assert_eq!(state.active_provider_result_fingerprint.as_deref(), Some(semantic_value_digest(&result).as_str()));
assert!(state.validate().is_ok());
}
}
#[test]
fn terminal_tool_authority_does_not_record_a_controller_rejected_yield() {
let mut state = opencode_result_state();
let mut result = valid_opencode_result();
result["reportedWorkDisposition"] = json!("yielded");
@@ -5321,9 +5342,10 @@ mod tests {
"idempotencyKey": "same-agent-1"
});
assert!(
admit_terminal_tool_authority(&mut state, "paperclip_finish", &result, false,).is_err()
);
admit_terminal_tool_authority(&mut state, "paperclip_finish", &result, true).unwrap();
assert!(state.active_provider_result_fingerprint.is_none());
assert!(state.active_provider_result_disposition.is_none());
assert!(state.validate().is_ok());
}
#[test]
@@ -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-v54-identity.json");
const fixture = resolve(root, "test/fixtures/copilot-profile-v55-identity.json");
const historical = JSON.parse(readFileSync(resolve(root, "test/fixtures/copilot-profile-v12-identity.json"), "utf8"));
const declaration = { ...historical.declaration, agentProfileVersion: 54 };
const declaration = { ...historical.declaration, agentProfileVersion: 55 };
delete declaration.permissionIdentitySourceSha256;
const sources = {
displayEventSourceSha256: "src/drivers/acpx/copilot-events.ts",
@@ -317,10 +317,10 @@ describe("qualified ACPX runtime sidecar", () => {
}
});
it.each([true, false])("resolves an omitted continuation before the production sidecar validator only with a saved monitor: %s", async eligible => {
it.each([["monitor", true], ["monitor", false], ["same_agent", true], ["same_agent", false]] as const)("resolves an omitted continuation before the production sidecar validator with saved %s authority=%s", async (kind, eligible) => {
const raw = { reportedWorkDisposition: "yielded", summary: "The review fix is published.", evidence: [], verification: [],
completionClaim: { contractRevision: "1", objectiveSatisfied: false, criteria: [], remainingWork: [{ description: "Wait for checks.", blocksCompletion: true }] } };
const continuation = { kind: "monitor", summary: "Wait for the saved task monitor.", idempotencyKey: "task-monitor:task:deadline" };
const continuation = { kind, summary: "Continue from saved task state.", idempotencyKey: "saved:task:receipt" };
const tools = new Map<string, any>(), emitted: any[] = [];
const handler = loadWaitForTool({ tools, emitted, validate: validatePrpStructuredRunResult });
const commands = loadActualToolCommands(tools);
@@ -5,6 +5,7 @@ import {
PRP_BLOCK_RESULT_OUTPUT_SCHEMA,
PRP_BLOCK_RESULT_PROVIDER_INPUT_SCHEMA,
PRP_BLOCK_TOOL_DESCRIPTION,
PRP_COMPLETION_RESULT_BRIDGE_INPUT_SCHEMA,
PRP_COMPLETION_RESULT_OUTPUT_SCHEMA,
PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA,
PRP_COMPLETION_TOOL_DESCRIPTION,
@@ -59,6 +60,21 @@ describe("provider-neutral completion result schema", () => {
expect(validate(report)).toBe(false);
});
it("carries a normalized workspace continuation only on the internal bridge", () => {
const bridge = new Ajv2020({ strict: false }).compile(PRP_COMPLETION_RESULT_BRIDGE_INPUT_SCHEMA);
const provider = new Ajv2020({ strict: false }).compile(PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA);
const report = { ...baseResult, reportedWorkDisposition: "yielded", continuation: {
kind: "same_agent", summary: "Use the staged task checkout.", idempotencyKey: "repository:task:receipt:run" } };
expect(bridge(report)).toBe(true);
expect(provider(report)).toBe(false);
for (const invalid of [
{ ...report, reportedWorkDisposition: "done" },
{ ...report, continuation: { ...report.continuation, kind: "retry" } },
{ ...report, continuation: { ...report.continuation, idempotencyKey: "" } },
]) expect(bridge(invalid)).toBe(false);
expect(bridge(baseResult)).toBe(true);
});
it("allows done with no verification and no actionable attention", () => {
expect(validate(structuredClone(baseResult))).toBe(true);
});
@@ -384,6 +384,20 @@ export const PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA = {
else: { properties: { continuation: { type: "null" } } },
} as const;
export const PRP_COMPLETION_RESULT_BRIDGE_INPUT_SCHEMA = {
...PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA,
properties: {
...PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA.properties,
continuation: {
...PRP_COMPLETION_RESULT_PROVIDER_INPUT_SCHEMA.properties.continuation,
properties: {
...waitContinuationSchema.properties,
kind: { type: "string", enum: [...waitContinuationSchema.properties.kind.enum, "same_agent"] },
},
},
},
} as const;
export const PRP_BLOCK_RESULT_PROVIDER_INPUT_SCHEMA = {
type: "object",
additionalProperties: true,
@@ -87,9 +87,21 @@ 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(54);
expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(55);
expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined();
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:171d865b9b6ff2eda3926dc957caf2993cd7e1fa1b4359e8b60b83f589bd67fa");
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:2c9e7f0fee36d2f7f0b253a86f3587b7bf2d9406f774b3b91e13678881ee7d48");
});
it("preserves v54 while binding current workspace continuation revalidation in v55", () => {
const prior = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v54-identity.json", import.meta.url), "utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v55-identity.json", import.meta.url), "utf8"));
expect(prior.commandDigest).toBe("sha256:171d865b9b6ff2eda3926dc957caf2993cd7e1fa1b4359e8b60b83f589bd67fa");
expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual([
"agentProfileVersion", "profileCompatibilitySourceSha256", "runtimeRequestBridgeSourceSha256",
"semanticCompletionContractSourceSha256", "semanticContinuationSourceSha256",
]);
expect(isSupportedAcpxProfileVersion("copilot", 54)).toBe(true);
expect(current.commandDigest).not.toBe(prior.commandDigest);
});
it("preserves v53 while binding repository setup completion guidance in v54", () => {
@@ -48,12 +48,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"protocolVersion": 1,
"acpxVersion": "0.13.1",
"agent": "copilot",
"agentProfileVersion": 54,
"agentProfileVersion": 55,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:171d865b9b6ff2eda3926dc957caf2993cd7e1fa1b4359e8b60b83f589bd67fa",
"commandDigest": "sha256:2c9e7f0fee36d2f7f0b253a86f3587b7bf2d9406f774b3b91e13678881ee7d48",
"permissionPolicy": "interactive"
},
"claude": {
@@ -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;
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;
type CurrentAcpxProfileVersion = typeof QUALIFIED_ACPX_PROFILE_DATA[keyof typeof QUALIFIED_ACPX_PROFILE_DATA]["agentProfileVersion"];
export type AcpxProfileVersion = HistoricalAcpxProfileVersion | CurrentAcpxProfileVersion;
const historicalVersions: Readonly<Record<string, readonly HistoricalAcpxProfileVersion[]>> = {
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], 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],
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],
cursor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
};
@@ -47,7 +47,7 @@ import { RUNNERD_CANONICAL_ITEM } from "./codex-driver-values.js";
describe("Codex app-server Codex driver", () => {
it.each([
["monitor", false], ["monitor", true], ["same_agent", false], ["same_agent", true],
["monitor", false], ["monitor", true], ["same_agent", false], ["same_agent", true], ["same_agent", "explicit"],
] as const)("uses saved %s continuation with nullable input=%s", async (kind, nullable) => {
const transport = new FakeCodexTransport();
const continuation = { kind, summary: "Continue from saved task state", idempotencyKey: "saved:task:receipt" };
@@ -59,7 +59,7 @@ describe("Codex app-server Codex driver", () => {
});
try {
await session.startTurn({ message: { role: "user", text: "Check the published PR later." } });
const report = { ...structuredClone(result), reportedWorkDisposition: "yielded", ...(nullable ? { continuation: null } : {}) };
const report = { ...structuredClone(result), reportedWorkDisposition: "yielded", ...(nullable === "explicit" ? { continuation } : nullable ? { continuation: null } : {}) };
const response = await transport.invoke({ id: "finish-monitor", method: "item/tool/call", params: {
threadId: "thread-1", turnId: "turn-1", callId: "finish-monitor", tool: "paperclip_finish", arguments: report,
} });
@@ -25,6 +25,20 @@ describe("controller-owned completion continuation", () => {
.toEqual({ ...yielded, continuation: preparation });
});
it("rechecks a sidecar-resolved workspace continuation against current server authority", async () => {
const preparation = { kind: "same_agent", summary: "Continue with the prepared repository.", idempotencyKey: "repository:task:receipt:run" };
const report = { ...yielded, continuation: preparation };
const read = vi.fn().mockResolvedValue({ ...context, completionContinuation: preparation });
const resolved = await resolveCompletionContinuation(report, "run", read);
expect(resolved).toEqual(report);
expect(read).toHaveBeenCalledExactlyOnceWith();
for (const response of [null, { ...context, completionContinuation: { ...preparation, idempotencyKey: "foreign" } },
{ ...context, completionContinuation: { ...preparation, summary: "changed" } },
{ ...context, completionContinuation: preparation, run: { id: "foreign" } }]) {
await expect(resolveCompletionContinuation(report, "run", async () => response)).rejects.toThrow();
}
});
it.each([
null, { ...context, run: { id: "other" } }, { ...context, activeTask: { executionRunId: "other" } },
{ ...context, completionContinuation: null }, { ...context, completionContinuation: { kind: "response_wake" } },
@@ -5,7 +5,8 @@ export async function resolveCompletionContinuation(
readTaskContext?: () => Promise<unknown>,
): Promise<unknown> {
const report = object(value);
if (report.reportedWorkDisposition !== "yielded" || report.continuation != null) return value;
const supplied = object(report.continuation);
if (report.reportedWorkDisposition !== "yielded" || (report.continuation != null && supplied.kind !== "same_agent")) return value;
const response = readTaskContext ? await readTaskContext() : null;
const text = object(response).type === "inputText" ? object(response).text : response;
let context: Record<string, unknown>;
@@ -19,6 +20,10 @@ export async function resolveCompletionContinuation(
typeof continuation.idempotencyKey !== "string" || !continuation.idempotencyKey.trim()) {
throw new Error("Cannot yield without a continuation: no eligible saved monitor or workspace continuation exists on this task. Save a monitor for a deferred check, provide an explicit response_wake for a real response wait, or continue the unfinished work. Do not repeat completed actions.");
}
if (report.continuation != null && (continuation.kind !== "same_agent" || supplied.summary !== continuation.summary ||
supplied.idempotencyKey !== continuation.idempotencyKey)) {
throw new Error("The supplied workspace continuation does not match current server-owned task state. Read the current task context and preserve its authority.");
}
return { ...report, continuation: { kind: continuation.kind, summary: continuation.summary, idempotencyKey: continuation.idempotencyKey } };
}
@@ -1,3 +1,4 @@
import { PRP_COMPLETION_TOOL_NAME, PRP_COMPLETION_RESULT_BRIDGE_INPUT_SCHEMA } from "../contracts/completion-result.js";
import { RunnerLaunchPreparationStore } from "./runner-launch-preparation-store.js";
import type { PreparedRunnerProviderLaunch, RunnerProviderLaunchRequirements } from "./runner-provider-launch.js";
import { validatePreparedRunnerLaunch, runnerProviderLaunchRequirements, createRunnerProviderLaunch, createRunnerdCodexAppServerArgs, trustedRuntimeReadOnlyRoots } from "./runner-provider-launch.js";
@@ -3211,7 +3212,7 @@ function authorizedToolSet(
operationId: String(tool.name ?? ""),
version: 1,
description: String(tool.description ?? ""),
inputSchema: record(tool.inputSchema),
inputSchema: tool.name === PRP_COMPLETION_TOOL_NAME ? PRP_COMPLETION_RESULT_BRIDGE_INPUT_SCHEMA : record(tool.inputSchema),
responseSchema: {},
}))
.sort((left, right) =>
@@ -0,0 +1,56 @@
import { mkdtemp, mkdir, readFile, rm, writeFile } 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 { createCodexTaskEnvelope } from "../contracts/codex.js";
import { createCapabilityRunnerdCodexTransport, defaultCapabilityRunnerdBinary } from "./runnerd-codex-transport.js";
import { collectUntilTerminal } from "../drivers/codex/codex-app-server-driver.test-support.js";
it.each(["omitted", "null", "resolved"] as const)("retains a server-owned workspace yield through the actual Rust bridge (%s)", async form => {
const root = await mkdtemp(join(tmpdir(), "runnerd-saved-continuation-")), runId = "run-saved-workspace";
const continuation = { kind: "same_agent", summary: "Continue with the prepared task repository.", idempotencyKey: "repository:task:receipt:run" };
const report = { schema: "paperclip.run_result.v1", reportedWorkDisposition: "yielded", summary: "The repository is staged; the requested verification continues.",
completionClaim: { contractRevision: "1", objectiveSatisfied: false, criteria: [{ criterionId: "objective", status: "not_satisfied", evidenceRefs: [] }],
remainingWork: [{ description: "Read the prepared repository and deliver its result.", blocksCompletion: true }] },
evidence: [], verification: [], attentionRequests: [], artifacts: [],
...(form === "resolved" ? { continuation } : form === "null" ? { continuation: null } : {}) };
const reportFile = join(root, "provider-report.json"), callLog = join(root, "provider-calls.txt");
await writeFile(reportFile, JSON.stringify(report));
const workspace = join(root, "workspace");
await Promise.all([mkdir(join(root, "home")), mkdir(workspace)]);
const environment = { PATH: process.env.PATH, HOME: join(root, "home"), CODEX_HOME: join(root, "codex-home"), LANG: "C.UTF-8" };
const bundle = createCapabilityRunnerdCodexTransport({
runnerBinary: process.env.PAPERCLIP_ATTACH_TRANSITION_RUNNER ?? defaultCapabilityRunnerdBinary(),
codexCommand: resolve(import.meta.dirname, "../../runner/target/debug/fake-codex-app-server"),
codexArgs: ["--state-file", join(root, "provider-state.json"), "--call-log", callLog, "--terminal-result-file", reportFile],
stateDirectory: root, environment,
prpIdentity: { runnerInstanceId: "runner-saved-workspace", environmentLeaseId: "lease-saved-workspace", runId, normalizedSessionId: "session-saved-workspace", turnId: "turn-saved-workspace", itemId: "item-saved-workspace" },
});
const dynamicToolHandler = vi.fn(async () => ({ run: { id: runId }, activeTask: { executionRunId: runId }, completionContinuation: continuation }));
const completionFeedback = vi.fn(async () => "The saved repository continuation is accepted.");
const driver = new CodexAppServerDriver({ approvalPolicy: "never", includeCollaborationModeInstructions: false, environment,
taskEnvelope: createCodexTaskEnvelope({ objective: "Verify the prepared repository.", contractRevision: "1", criteria: [{ id: "objective", requirement: "Deliver the result." }], constraints: [] }),
dynamicTools: [{ name: "get_task_context", description: "Read current task authority.", inputSchema: { type: "object", properties: {}, additionalProperties: false } }],
dynamicToolHandler, completionFeedback, transportFactory: () => bundle.transport, requireProviderSessionIdentity: true });
let session: Awaited<ReturnType<typeof driver.openSession>> | undefined;
try {
session = await driver.openSession({ runId, normalizedSessionId: "session-saved-workspace", workingDirectory: workspace });
await session.startTurn({ message: { role: "user", text: "Continue with the prepared checkout." } });
const events = await collectUntilTerminal(session.events());
expect(completionFeedback).toHaveBeenCalledExactlyOnceWith({ ...report, continuation });
expect(dynamicToolHandler).toHaveBeenCalledOnce();
expect(events.filter(event => event.eventType === "run.result.proposed")).toHaveLength(1);
expect(events.at(-1)?.eventType).toBe("turn.completed");
expect((await session.snapshot()).semanticResult?.result).toEqual({ ...report, continuation });
expect(await readFile(callLog, "utf8")).toContain("terminal-result:accepted");
const journal = JSON.parse(await readFile(join(root, "control-plane/control-plane-state.json"), "utf8"));
const terminals = journal.committedEvents.filter((event: { eventType: string }) => event.eventType === "run.terminal");
expect(terminals).toHaveLength(1);
expect(terminals[0].envelope.payload).toMatchObject({ runId, eventType: "run.terminal", payload: { runTerminalState: "succeeded", reportedWorkDisposition: "yielded" } });
} finally {
await session?.close();
await bundle.transport.close();
await rm(root, { recursive: true, force: true });
}
}, 30_000);
@@ -0,0 +1,84 @@
{
"commandDigest": "sha256:2c9e7f0fee36d2f7f0b253a86f3587b7bf2d9406f774b3b91e13678881ee7d48",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
"agentProfileVersion": 55,
"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": "89e3dde25ff35bfd4d7f520fd9d14af3cb9027f182a2d51431d298f1015be580",
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
"semanticSidecarSourceSha256": "cc90b59bdfe6a82f7fa3e819e8671f71f7b14047e68942e8086618ffbaacf59f",
"semanticDirectDriverSourceSha256": "ff572638f2cb5d9f8249ff8e2f412311c1e3af209ef0bf7e5ea9f10a21d91103",
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
"semanticCompletionContractSourceSha256": "3343df6362252d44d12b079ff78c8f6539d1544f3ce5ec36ff36db70ab0fac31",
"semanticValidatorsSourceSha256": "f22d9caf2035b600abc1a6625ae23ca97f1409d7e29ed5b7264bb8f9784c7dc9",
"semanticSchemaBundleSourceSha256": "016391a087cc217aafed4ba1f4efafc6e4881a7525952dce824b7f4d24530603",
"semanticSidecarProtocolSourceSha256": "03c0c95b900f9dc18f29f6be27b8c79bc0bcab29780190d46a74e95328081236",
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
"configuredEnvironmentSourceSha256": "5c9008d121979a3a8987e6b8a5752172eafdc4a2b5d725a73f3bbb8fb83be8ff",
"configuredEnvironmentRustSourceSha256": "85c87fae62b8a9377e4f0a4c00302c7519fae4397b1c48253f8f2b7ba7faf994",
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
"profileInstallationSourceSha256": "e4aacb08015fa9104dce2b0c7d150cc432231c8a3e07d2cec2a325b388f7f382",
"attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2",
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
"nativeSessionRuntimeSourceSha256": "e8a3bee66a1ff1c6f251b5ae57453ffb908336da9c4a6d98f41f94f7ee94a8e8",
"nativeRuntimeContextSourceSha256": "df93bc8bcfa9dfdcf26ad6a69ea0bd3e56ef186c5e962b243fb2288f7074584b",
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
"harnessSessionContractSourceSha256": "03f4cd7f76f34f6d82e009b66aa51f18f6e23dc235b660778060e35b6ea07412",
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
"harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94",
"transportContractSourceSha256": "4fa9593226ff3280bc12140bfcbc2c0986e4cc8d4733381c334138dd03b08f46",
"nativeBackendSourceSha256": "2406483fd35a564971c22e10fc2bad26bae16ff5a3fb6c65690fcf156522ab6d",
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
"nativeInstructionsSourceSha256": "ee8df5d85b263d5d2d0dc9ede8602cb7daf6388687108fe05e2371f5f56c3492",
"providerLaunchSourceSha256": "cba71368f82ae8d300123efa09f23744e19e82142788fa9ad238338fefec9624",
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
"runtimeRequestBridgeSourceSha256": "fb2e91e9a7f4b10ae0d936e1e1c74ac9095733ef67fe816491be45791048efa1",
"runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295",
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
"guardedInstallationSourceSha256": "f6d0c33de38259efefa0f4e0f9737cd30cfd3996a8e875e3fbdb8c72dc133dca",
"nativeExecutionSourceSha256": "fc7c6568feb0d8d709e9baa51839420c9b90336b4865a9b8405aba06e4d1e9c4",
"profileCompatibilitySourceSha256": "d4edec781031a44f7e96a1e4ef398af4675529b7ec0f9c56c4915d9ec81a20cf",
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
"assetOwnershipSourceSha256": "1785e3f31e5dad5a13b53e25d8cf9482a9bd89a27b6c127830aed6b81acc605c",
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
"credentialBindingSourceSha256": "54fd09ad430dac04df98d829061f6701e73b5bf2a0f4de836d2ac36ee511009f",
"sidecarErrorSourceSha256": "b09af4577f8bf42eee7036b2ad9842202cfc763e72b53c3287f82c6090cdad80",
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
"semanticContinuationSourceSha256": "d248e47a0fe7f2536d1e64cea0b0b410f34104800cd643d995b4473104ecc8e0",
"runtimeHostSourceSha256": "60e5454f030e87f2afa8b1fb120b26adb126b6b17d0e5e82f49c77f76f466cbb",
"runtimeAdapterSourceSha256": "e4c764c2c3c7dd655a965b39472bd1ad8d577d1e7258b25594b171c9429752b8",
"runtimeSandboxSourceSha256": "92dfa53f5a3ed0062913ae417eeccec243b430d444495efde2a8e9c4c02e78ea",
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
}
}
@@ -59,6 +59,7 @@ export const DAYTONA_IMAGE_INPUT_PATHS = [
"packages/paperclip-runner/test/fixtures/copilot-profile-v48-identity.json",
"packages/paperclip-runner/test/fixtures/copilot-profile-v49-identity.json",
"packages/paperclip-runner/test/fixtures/copilot-profile-v54-identity.json",
"packages/paperclip-runner/test/fixtures/copilot-profile-v55-identity.json",
"packages/paperclip-runner/scripts/materialize-cursor-distribution.mjs",
"packages/paperclip-runner/scripts/cursor-runtime-patch.mjs",
"packages/paperclip-runner/scripts/build-verified-provider-entrypoints.mjs",