fix(gateway): prevent restart replay after final delivery

Record channel delivery custody before recipient-visible sends so restart recovery cannot duplicate accepted finals.

Ambiguous legacy markers now fail closed with a visible interruption instead of blind replay.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
Ayaan Zaidi
2026-08-10 16:42:14 +05:30
committed by GitHub
parent 1569400ea7
commit 1f75018600
74 changed files with 3655 additions and 2174 deletions
+47 -4
View File
@@ -1,5 +1,6 @@
import { getReplyPayloadMetadata } from "../../auto-reply/reply-payload.js";
import type { CliDeps } from "../../cli/deps.types.js";
import { buildRestartRecoveryClaimCleanupPatch } from "../../config/sessions/restart-recovery-state.js";
import type { RestartRecoveryTerminalDeliveryEvidenceResult } from "../../config/sessions/restart-recovery-types.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import { assertAgentRunLifecycleGenerationCurrent } from "../../infra/agent-events.js";
@@ -69,6 +70,7 @@ export async function finalizeEmbeddedAgentCommand(params: {
cwd,
agentDir,
outboundSession,
runId,
agentCfg,
} = params.prepared;
const {
@@ -373,7 +375,8 @@ export async function finalizeEmbeddedAgentCommand(params: {
!params.suppressVisibleSessionEffects &&
!sessionReboundDuringRun
) {
const entry = sessionStore[sessionKey] ?? sessionEntry;
const entry =
(await resolveFreshSessionEntryForDelivery?.()) ?? sessionStore[sessionKey] ?? sessionEntry;
if (!entry) {
throw new Error("Cannot clear pending delivery without a session entry");
}
@@ -382,15 +385,55 @@ export async function finalizeEmbeddedAgentCommand(params: {
params.opts.deliver === true &&
!pendingFinalDeliveryMarker.hasSendableFinalPayload &&
entry.pendingFinalDelivery?.kind === "transport-only";
if (deliveryResult?.deliverySucceeded === true || clearStaleTransportOnly) {
const clearOwnedPendingFinal =
deliveryResult?.deliverySucceeded === true &&
pendingFinalDeliveryMarker.pendingFinalDeliveryIntentId !== undefined;
// Preserve the exact local claim through sibling session writes so a delivered
// source is tombstoned before admission release can erase its ownership fields.
const recoveryClaimEntry =
entry.restartRecoveryDeliveryRunId === runId
? entry
: sessionEntry?.restartRecoveryDeliveryRunId === runId
? sessionEntry
: params.sessionEntry?.restartRecoveryDeliveryRunId === runId
? params.sessionEntry
: undefined;
if (clearOwnedPendingFinal || clearStaleTransportOnly || recoveryClaimEntry) {
const now = Date.now();
sessionEntry = await persistSessionEntry({
sessionStore,
sessionKey,
storePath,
initialEntry: entry,
entry: clearPendingFinalDelivery(entry, Date.now()),
entry: {
...(clearOwnedPendingFinal || clearStaleTransportOnly
? clearPendingFinalDelivery(entry, now)
: { ...entry, updatedAt: now }),
...(recoveryClaimEntry
? buildRestartRecoveryClaimCleanupPatch({
entry: {
...recoveryClaimEntry,
restartRecoveryTerminalDeliveryEvidence:
entry.restartRecoveryTerminalDeliveryEvidence,
restartRecoveryTerminalRunIds: entry.restartRecoveryTerminalRunIds,
},
recordTerminalSource: true,
terminalDeliveryEvidence: buildRestartRecoveryTerminalDeliveryEvidence(
deliveryResult ?? result,
),
terminalRunId: runId,
})
: {}),
},
shouldPersist: (current) =>
shouldPersistCurrentRunSessionCleanup(current, runOwnedSessionId),
shouldPersistCurrentRunSessionCleanup(current, runOwnedSessionId) &&
(!recoveryClaimEntry ||
current?.restartRecoveryDeliveryRunId === undefined ||
current.restartRecoveryDeliveryRunId === runId) &&
(!clearOwnedPendingFinal ||
current?.pendingFinalDelivery?.intentId ===
pendingFinalDeliveryMarker.pendingFinalDeliveryIntentId) &&
(!clearStaleTransportOnly || current?.pendingFinalDelivery?.kind === "transport-only"),
});
}
}