mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
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:
@@ -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"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user