mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
fix: bind crash cleanup to normalized session-open receipts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
37ad1edf06
commit
3592b04c2b
2 files changed
+7
-5
No files matched your search
@@ -3858,7 +3858,7 @@ describe("retained native cleanup activation", () => {
|
||||
describe("stopped native conversation physical cleanup", () => {
|
||||
it.each(["codex", "acpx"].flatMap(provider => [
|
||||
"stopped", "provider_alive", "worker_alive", "missing_receipt", "wrong_receipt", "new_launch",
|
||||
"foreign_run", "foreign_company", "foreign_runner", "remote", "unreleased", "changed_state", "changed_pid", "symlink", "startup_intent", "pending_identity", "wrong_schema", "replacement", "replacement_alive", "agent_alive", "checkpoint_owner_alive", "diagnostic_owner_alive",
|
||||
"foreign_run", "foreign_company", "foreign_runner", "remote", "unreleased", "changed_state", "changed_pid", "symlink", "startup_intent", "pending_identity", "wrong_schema", "replacement", "replacement_alive", "agent_alive", "checkpoint_owner_alive", "diagnostic_owner_alive", "normalized_session_receipt",
|
||||
].map(mode => ({ provider, mode }))))("$provider $mode", async ({ provider, mode }) => {
|
||||
const base = await mkdtemp(join(tmpdir(), "native-conversation-cleanup-"));
|
||||
const previous = process.env.PAPERCLIP_RUNNER_STATE_DIR;
|
||||
@@ -3894,8 +3894,9 @@ describe("stopped native conversation physical cleanup", () => {
|
||||
...(mode === "startup_intent" ? { startupAttempt: { phase: "intent" } } : {}),
|
||||
...(mode === "pending_identity" ? { pendingEvents: [{ eventType: "session.started" }] } : {}),
|
||||
};
|
||||
const normalizedEvent = mode === "normalized_session_receipt" ? { ...event, turnId: undefined, itemId: undefined } : event;
|
||||
const receipt = { sourceEventId: `${identity.runnerInstanceId}:${identity.runId}:1`,
|
||||
sourcePayloadSha256: mode === "wrong_receipt" ? "wrong" : hash(event), payload: { prpEvent: event } };
|
||||
sourcePayloadSha256: mode === "wrong_receipt" ? "wrong" : hash(normalizedEvent), payload: { prpEvent: normalizedEvent } };
|
||||
const stop = { eventType: mode === "new_launch" ? "native.process_start_requested" : "native.local_process_stopped",
|
||||
payload: { processPid: mode === "worker_alive" ? process.pid : 99_999_999, processGroupId: mode === "worker_alive" ? process.pid : 99_999_999 } };
|
||||
let queryIndex = 0;
|
||||
@@ -3919,11 +3920,11 @@ describe("stopped native conversation physical cleanup", () => {
|
||||
await writeFile(join(root, `runner/${provider}-provider-state.json`), JSON.stringify(providerState));
|
||||
if (mode === "symlink") { await rename(runnerPath, join(base, "external")); await symlink(join(base, "external"), runnerPath); }
|
||||
const proof = await verifyStoppedNativeSessionForContinuation(db, run);
|
||||
if (["stopped", "changed_state", "changed_pid", "replacement"].includes(mode)) {
|
||||
if (["stopped", "changed_state", "changed_pid", "replacement", "normalized_session_receipt"].includes(mode)) {
|
||||
expect(proof).not.toBeNull();
|
||||
if (mode === "changed_state") await writeFile(runnerPath, "{}");
|
||||
const kill = mode === "changed_pid" ? vi.spyOn(process, "kill").mockReturnValue(true) : null;
|
||||
try { expect(proof!.retire()).toBe(mode === "stopped" || mode === "replacement"); } finally { kill?.mockRestore(); }
|
||||
try { expect(proof!.retire()).toBe(["stopped", "replacement", "normalized_session_receipt"].includes(mode)); } finally { kill?.mockRestore(); }
|
||||
expect(await readFile(join(root, `runner/${provider}-provider-state.json`), "utf8")).toBe(JSON.stringify(providerState));
|
||||
} else expect(proof).toBeNull();
|
||||
} finally {
|
||||
|
||||
@@ -2429,8 +2429,9 @@ export async function verifyStoppedNativeSessionForContinuation(
|
||||
const durableProvider = record(durable.payload);
|
||||
if (!receipt || !validatePrpEvent(durable).ok || durable.sourceInstanceId !== event.sourceInstanceId ||
|
||||
durable.runId !== run.id || durable.normalizedSessionId !== run.nativeSessionId ||
|
||||
// Normalized session-open receipts omit turn/item IDs. Those belong
|
||||
// to the durable runner identity checked above, not the open event.
|
||||
durable.sourceSeq !== event.sourceSeq || durable.eventType !== event.eventType ||
|
||||
durable.turnId !== event.turnId || durable.itemId !== event.itemId ||
|
||||
receipt.sourcePayloadSha256 !== nativeSha256(durable) ||
|
||||
(durableProvider.processId !== undefined && durableProvider.processId !== provider.processId) ||
|
||||
(durableProvider.driverSessionId ?? durableProvider.providerSessionId) !== provider.providerSessionId) return null;
|
||||
|
||||
Reference in new issue
Block a user