Skip to content

Commit b1ceab2

Browse files
committed
fix(session): reject a parked call when its approval surface fails
1 parent 014c058 commit b1ceab2

1 file changed

Lines changed: 31 additions & 1 deletion

File tree

‎src/session/approval-resume.ts‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,37 @@ export function createApprovalResume(args: {
543543
return timedOut;
544544
});
545545

546-
const outcome = await args.gate.resolveSuspended(request, stillCurrent);
546+
const outcome = await args.gate
547+
.resolveSuspended(request, stillCurrent)
548+
.catch(async (err: unknown) => {
549+
// The surface resolves aborts and timeouts as outcomes, so a throw
550+
// is a real failure. A parked call is never retried (it is not a
551+
// stall), so reject it now instead of leaving it to the reactor's
552+
// own timeout, unless that timeout already answered it.
553+
if (!stillCurrent()) {
554+
dropParked();
555+
throw err;
556+
}
557+
args.registerParkedCancel?.(undefined);
558+
const timedOut =
559+
overlayAbort?.signal.reason === APPROVAL_TIMEOUT_RESULT_TEXT ||
560+
(await timeoutResultFromHistory(
561+
() => parkedAgent.history(),
562+
parkedCallId,
563+
));
564+
if (!timedOut) {
565+
await deliverDecision(
566+
decisionMessage(
567+
correlationId,
568+
"rejected",
569+
"approval surface failed",
570+
),
571+
).catch((deliverErr: unknown) => {
572+
logger.warn`rejection after a failed approval surface not delivered correlation=${redactedCorrelationId(correlationId)} error=${deliverErr instanceof Error ? deliverErr.name : typeof deliverErr}`;
573+
});
574+
}
575+
throw err;
576+
});
547577
if (!overlayAbort.signal.aborted) overlayAbort.abort();
548578
// The overlay is gone, but its aborted controller still feeds every
549579
// permission prompt's signal while registered. A resumed call can prompt

0 commit comments

Comments
 (0)