fix(voice-call): keep retryable errors replayable

This commit is contained in:
Vincent Koc
2026-04-13 17:50:18 +01:00
parent 31233a1995
commit 20248c475f
2 changed files with 48 additions and 3 deletions

View File

@@ -416,4 +416,45 @@ describe("processEvent (functional)", () => {
expect(call.transcript).toHaveLength(1);
expect(Array.from(ctx.processedEventIds)).toEqual(["stable-key-1"]);
});
it("keeps retryable call.error events replayable", () => {
const now = Date.now();
const ctx = createContext();
ctx.activeCalls.set("call-retryable-error", {
callId: "call-retryable-error",
providerCallId: "provider-retryable-error",
provider: "plivo",
direction: "outbound",
state: "active",
from: "+15550000000",
to: "+15550000001",
startedAt: now,
transcript: [],
processedEventIds: [],
metadata: {},
});
ctx.providerCallIdMap.set("provider-retryable-error", "call-retryable-error");
const event: NormalizedEvent = {
id: "evt-retryable-error",
dedupeKey: "stable-retryable-error",
type: "call.error",
callId: "call-retryable-error",
providerCallId: "provider-retryable-error",
timestamp: now + 1,
error: "temporary upstream failure",
retryable: true,
};
processEvent(ctx, event);
processEvent(ctx, event);
const call = ctx.activeCalls.get("call-retryable-error");
if (!call) {
throw new Error("expected retryable error call to remain active");
}
expect(call.state).toBe("active");
expect(Array.from(ctx.processedEventIds)).toEqual([]);
expect(call.processedEventIds).toEqual([]);
});
});

View File

@@ -161,8 +161,6 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void {
return;
}
ctx.processedEventIds.add(dedupeKey);
if (event.providerCallId && event.providerCallId !== call.providerCallId) {
const previousProviderCallId = call.providerCallId;
call.providerCallId = event.providerCallId;
@@ -175,7 +173,11 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void {
}
}
call.processedEventIds.push(dedupeKey);
const shouldCommitReplayKey = !(event.type === "call.error" && event.retryable);
if (shouldCommitReplayKey) {
ctx.processedEventIds.add(dedupeKey);
call.processedEventIds.push(dedupeKey);
}
switch (event.type) {
case "call.initiated":
@@ -247,6 +249,8 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void {
});
return;
}
// Keep retryable provider errors replayable so a redelivery can still
// drive later recovery or terminal handling for the same event key.
break;
}