@koda-sl/baker-bridge 0.56.0-dev.7f6a084be → 0.56.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +37 -12
  2. package/dist/hono/agent-session.d.ts +6 -0
  3. package/dist/hono/agent-session.d.ts.map +1 -1
  4. package/dist/hono/agent-session.js +48 -1
  5. package/dist/hono/agent-session.js.map +1 -1
  6. package/dist/hono/agent-session.test.js +60 -2
  7. package/dist/hono/agent-session.test.js.map +1 -1
  8. package/dist/hono/agent.test.js +1 -0
  9. package/dist/hono/agent.test.js.map +1 -1
  10. package/dist/hono/bridge-job.d.ts +10 -0
  11. package/dist/hono/bridge-job.d.ts.map +1 -1
  12. package/dist/hono/bridge-job.js +209 -110
  13. package/dist/hono/bridge-job.js.map +1 -1
  14. package/dist/hono/bridge-job.test.js +253 -27
  15. package/dist/hono/bridge-job.test.js.map +1 -1
  16. package/dist/hono/context-usage.d.ts +8 -1
  17. package/dist/hono/context-usage.d.ts.map +1 -1
  18. package/dist/hono/context-usage.js +35 -10
  19. package/dist/hono/context-usage.js.map +1 -1
  20. package/dist/hono/context-usage.test.js +38 -34
  21. package/dist/hono/context-usage.test.js.map +1 -1
  22. package/dist/hono/model-router.d.ts +4 -0
  23. package/dist/hono/model-router.d.ts.map +1 -1
  24. package/dist/hono/model-router.js +22 -1
  25. package/dist/hono/model-router.js.map +1 -1
  26. package/dist/hono/model-router.test.js +33 -1
  27. package/dist/hono/model-router.test.js.map +1 -1
  28. package/dist/hono/session-continuity.d.ts +8 -0
  29. package/dist/hono/session-continuity.d.ts.map +1 -1
  30. package/dist/hono/session-continuity.integration.test.js +1 -0
  31. package/dist/hono/session-continuity.integration.test.js.map +1 -1
  32. package/dist/hono/session-continuity.js +23 -0
  33. package/dist/hono/session-continuity.js.map +1 -1
  34. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-job.d.ts","sourceRoot":"","sources":["../../src/hono/bridge-job.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bridge-job.d.ts","sourceRoot":"","sources":["../../src/hono/bridge-job.ts"],"names":[],"mappings":"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"}
@@ -2,13 +2,14 @@ import { readdir, readFile, stat } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { applyTaskLifecycleMessage } from "./agent-tasks.js";
5
- import { pickContextUsage } from "./context-usage.js";
5
+ import { pickContextWindow, turnContextUsage } from "./context-usage.js";
6
6
  import { FLOW_INPUT_TOOL_NAME, normalizeFlowInputToolInput, sanitizeFlowRequests, } from "./flow-input.js";
7
7
  import { composioScopesToInvalidate, reportMcpInvalid, } from "./mcp-servers.js";
8
+ import { contextWindowForModel } from "./model-router.js";
8
9
  import { bakerPlugins } from "./plugins.js";
9
10
  import { appendCurrentReferenceContext } from "./prompt-context.js";
10
11
  import { bridgeRuntimeFiles } from "./runtime-files.js";
11
- import { applyCurrentSenderAttribution, buildCleanBoundary, buildRehydratedPrompt, emptyInProgressSession, isResumeSessionError, observeInProgressSession, prepareSessionAttempt, } from "./session-continuity.js";
12
+ import { applyCurrentSenderAttribution, buildCleanBoundary, buildRehydratedPrompt, emptyInProgressSession, isResumeSessionError, observeInProgressSession, prepareSessionAttempt, stallContinuationAttempt, } from "./session-continuity.js";
12
13
  import { verbatimSlashCommand } from "./slash-commands.js";
13
14
  import { createBakerUiServer, normalizeTagInputToolInput, sanitizeTagChanges, TAG_INPUT_TOOL_NAME, } from "./tag-input.js";
14
15
  export function parsePlanDecision(value) {
@@ -54,6 +55,28 @@ export function remapAnswersToQuestionText(questions, answers) {
54
55
  return Object.keys(byText).length > 0 ? byText : answers;
55
56
  }
56
57
  const DEFAULT_ASK_USER_QUESTION_TIMEOUT_MS = 15 * 60 * 1000;
58
+ // A model request that streams nothing for this long is a hung upstream
59
+ // connection (observed in production: an OpenRouter request that never yields
60
+ // and never errors). Only armed while the CLI should be talking to the model —
61
+ // right after a tool_result goes back or between stream deltas — never during
62
+ // tool execution, compaction, or a blocking dashboard prompt, where silence is
63
+ // legitimate. Kept well above worst-case time-to-first-token on huge contexts.
64
+ const DEFAULT_STALL_TIMEOUT_MS = 3 * 60 * 1000;
65
+ const DEFAULT_STALL_CHECK_INTERVAL_MS = 15_000;
66
+ // Retries resume the CLI session in place, so each one is cheap; past this the
67
+ // upstream is genuinely down and the turn should fail legibly.
68
+ const MAX_STALL_RETRIES = 2;
69
+ // Agent-facing deny message when a blocking dashboard prompt (question, plan
70
+ // review, tag/flow form) goes unanswered past the wait window. The turn must
71
+ // end cleanly — never error — so the user can pick up with a normal message.
72
+ const PROMPT_UNANSWERED_MESSAGE = "The user has not answered this yet. This is not an error. Stop waiting: end your turn with a brief note " +
73
+ "about what you need from them, so they can reply whenever they're ready. Do not call this tool again this turn.";
74
+ export class StreamStallError extends Error {
75
+ constructor() {
76
+ super("The connection to the model stalled and could not be recovered. Send a message to continue.");
77
+ this.name = "StreamStallError";
78
+ }
79
+ }
57
80
  const CLAUDE_PLAN_MAX_BYTES = 512 * 1024;
58
81
  // Rejecting a plan denies ExitPlanMode. We must NOT interrupt the query: an
59
82
  // interrupt tears the turn down mid-tool-call, so Claude Code ends with an
@@ -277,6 +300,8 @@ export class BridgeJobExecutor {
277
300
  prepareAttachments;
278
301
  mcpServersProvider;
279
302
  askUserQuestionTimeoutMs;
303
+ stallTimeoutMs;
304
+ stallCheckIntervalMs;
280
305
  now;
281
306
  constructor(dependencies) {
282
307
  this.sdk = dependencies.sdk;
@@ -286,6 +311,8 @@ export class BridgeJobExecutor {
286
311
  this.prepareAttachments = dependencies.prepareAttachments ?? (async () => "");
287
312
  this.mcpServersProvider = dependencies.mcpServersProvider;
288
313
  this.askUserQuestionTimeoutMs = dependencies.askUserQuestionTimeoutMs ?? DEFAULT_ASK_USER_QUESTION_TIMEOUT_MS;
314
+ this.stallTimeoutMs = dependencies.stallTimeoutMs ?? DEFAULT_STALL_TIMEOUT_MS;
315
+ this.stallCheckIntervalMs = dependencies.stallCheckIntervalMs ?? DEFAULT_STALL_CHECK_INTERVAL_MS;
289
316
  this.now = dependencies.now ?? Date.now;
290
317
  }
291
318
  async execute(job, controls) {
@@ -356,7 +383,25 @@ export class BridgeJobExecutor {
356
383
  return { costUsd: 0, attemptedRehydration: attempt.attemptedRehydration };
357
384
  }
358
385
  state.model = selected.model;
359
- const costUsd = await this.runQueryOnce(job, controls, state, attempt, selected);
386
+ // A stalled stream closes the wedged query and resumes the CLI session in
387
+ // place — the turn survives a hung upstream request instead of dying at the
388
+ // 30-minute session watchdog with all mid-turn work stranded.
389
+ let costUsd = 0;
390
+ let current = attempt;
391
+ for (let stallRetries = 0;;) {
392
+ try {
393
+ costUsd += await this.runQueryOnce(job, controls, state, current, selected);
394
+ break;
395
+ }
396
+ catch (error) {
397
+ if (!(error instanceof StreamStallError) || stallRetries >= MAX_STALL_RETRIES || !controls.isActive()) {
398
+ throw error;
399
+ }
400
+ stallRetries += 1;
401
+ console.error(`[BridgeJob] model stream stalled — resuming session (retry ${stallRetries}/${MAX_STALL_RETRIES}) for thread ${job.threadId}`);
402
+ current = stallContinuationAttempt(current, state.inProgressSession);
403
+ }
404
+ }
360
405
  if (controls.isActive()) {
361
406
  void this.relay.routerSample({ threadId: job.threadId, turnId: job.turnId }, selected.model, selected.routerMessages);
362
407
  }
@@ -516,8 +561,30 @@ export class BridgeJobExecutor {
516
561
  query.close?.();
517
562
  };
518
563
  controls.abortSignal.addEventListener("abort", closeQuery, { once: true });
564
+ try {
565
+ costUsd = await this.drainQuery(job, controls, state, query, closeQuery);
566
+ }
567
+ finally {
568
+ controls.abortSignal.removeEventListener("abort", closeQuery);
569
+ }
570
+ if (!controls.isActive()) {
571
+ closeQuery();
572
+ }
573
+ if (!state.inProgressSession.sawCleanResult && controls.isActive()) {
574
+ await this.clearIfDirty(job, controls, state, "missing_result");
575
+ }
576
+ return costUsd;
577
+ }
578
+ // Consumes the query's message stream under stall protection. Throws
579
+ // StreamStallError when the stream went silent past the stall window while
580
+ // the model should have been streaming — the caller retries by resuming the
581
+ // in-progress session.
582
+ async drainQuery(job, controls, state, query, closeQuery) {
583
+ const stallDetector = this.createStallDetector(job, controls, closeQuery);
584
+ let costUsd = 0;
519
585
  try {
520
586
  for await (const msg of query) {
587
+ stallDetector.observeMessage(msg);
521
588
  costUsd = await this.consumeSdkMessage(job, controls, state, msg, costUsd);
522
589
  if (!controls.isActive()) {
523
590
  closeQuery();
@@ -527,19 +594,64 @@ export class BridgeJobExecutor {
527
594
  }
528
595
  catch (error) {
529
596
  closeQuery();
597
+ if (stallDetector.stalled() && controls.isActive()) {
598
+ throw new StreamStallError();
599
+ }
530
600
  throw error;
531
601
  }
532
602
  finally {
533
- controls.abortSignal.removeEventListener("abort", closeQuery);
603
+ stallDetector.dispose();
534
604
  }
535
- if (!controls.isActive()) {
536
- closeQuery();
537
- }
538
- if (!state.inProgressSession.sawCleanResult && controls.isActive()) {
539
- await this.clearIfDirty(job, controls, state, "missing_result");
605
+ if (stallDetector.stalled() && controls.isActive()) {
606
+ throw new StreamStallError();
540
607
  }
541
608
  return costUsd;
542
609
  }
610
+ // Stall detection: armed only while the CLI should be talking to the model —
611
+ // right after a tool_result goes back (the next API request), between stream
612
+ // deltas, and at query start (CLI boot + first request complete well inside
613
+ // the stall window, and a retry that hangs before its first message must not
614
+ // escape detection). Silence is legitimate during tool execution, compaction,
615
+ // and blocking dashboard prompts, so those phases stay disarmed (the session
616
+ // watchdog still backstops them). On stall the wedged query is closed so the
617
+ // caller can retry by resuming the session.
618
+ createStallDetector(job, controls, closeQuery) {
619
+ let stalled = false;
620
+ let awaitingModel = true;
621
+ let lastMessageAt = this.now();
622
+ const hasPendingPrompt = () => controls.pendingQuestions.size > 0 ||
623
+ controls.pendingPlans.size > 0 ||
624
+ controls.pendingTagInputs.size > 0 ||
625
+ controls.pendingFlowInputs.size > 0;
626
+ const timer = setInterval(() => {
627
+ // Keep the clock fresh while a blocking dashboard prompt is outstanding.
628
+ // The user can take far longer than the stall window to answer (a prompt
629
+ // waits up to 15 min); without this, the first tick after they answer
630
+ // would see a stale `lastMessageAt` and misfire a stall on a healthy turn.
631
+ if (hasPendingPrompt()) {
632
+ lastMessageAt = this.now();
633
+ return;
634
+ }
635
+ if (!awaitingModel || stalled) {
636
+ return;
637
+ }
638
+ if (this.now() - lastMessageAt <= this.stallTimeoutMs) {
639
+ return;
640
+ }
641
+ stalled = true;
642
+ console.error(`[BridgeJob] no stream data for ${this.stallTimeoutMs}ms while awaiting the model — closing query for retry (thread ${job.threadId})`);
643
+ closeQuery();
644
+ }, this.stallCheckIntervalMs);
645
+ timer.unref?.();
646
+ return {
647
+ observeMessage: (msg) => {
648
+ lastMessageAt = this.now();
649
+ awaitingModel = msg.type === "user" || msg.type === "stream_event";
650
+ },
651
+ stalled: () => stalled,
652
+ dispose: () => clearInterval(timer),
653
+ };
654
+ }
543
655
  async consumeSdkMessage(job, controls, state, msg, costUsd) {
544
656
  if (!controls.isActive()) {
545
657
  return costUsd;
@@ -558,13 +670,30 @@ export class BridgeJobExecutor {
558
670
  throw controlChannelClosedError();
559
671
  }
560
672
  }
673
+ // Track the occupancy of the last main-conversation request. We read the
674
+ // meter from a single request's usage, not the turn's aggregate totals —
675
+ // and only from the main conversation, so subagent work never pollutes it.
676
+ if (msg.type === "assistant" && !msg.parent_tool_use_id && !msg.subagent_type) {
677
+ const usage = msg.message.usage;
678
+ if (usage) {
679
+ state.lastMainRequestUsage = usage;
680
+ }
681
+ }
561
682
  if (msg.type === "result") {
562
683
  state.observedCostUsd = msg.total_cost_usd;
563
- // The turn result reports per-model usage including each model's context
564
- // window, so we can surface how full the conversation's context is without
565
- // a hardcoded window table. Relay it so the dashboard can show the meter
566
- // and warn as it fills. A relay failure must never fail the turn.
567
- const contextUsage = pickContextUsage(msg.modelUsage);
684
+ // Surface how full the conversation's context is. Tokens come from the last
685
+ // main-conversation request's own usage — the real occupancy of the window
686
+ // at that request. We deliberately do NOT use the result's per-model token
687
+ // totals: those aggregate every API round-trip in the turn (one per tool
688
+ // call, plus any same-model subagent work), so a handful of tool calls
689
+ // inflates them past the window and pins the meter at 100% on an almost-
690
+ // empty chat. The window is the model's real OpenRouter context length
691
+ // (opus/sonnet 4.6 = 1M); we resolve it from the model id because the
692
+ // runtime under-reports it through the OpenRouter passthrough, falling back
693
+ // to the reported window for unknown models. A relay failure must never
694
+ // fail the turn.
695
+ const window = contextWindowForModel(state.model) ?? pickContextWindow(msg.modelUsage);
696
+ const contextUsage = turnContextUsage(state.lastMainRequestUsage, window);
568
697
  if (contextUsage) {
569
698
  await this.relay.contextUsage({ threadId: job.threadId, turnId: job.turnId }, contextUsage);
570
699
  }
@@ -667,25 +796,26 @@ export class BridgeJobExecutor {
667
796
  }
668
797
  await this.sessionStore.clear(job.threadId, { runToken: controls.runToken, reason });
669
798
  }
670
- async handleAskUserQuestion(job, controls, input, toolUseId, signal) {
671
- const target = { threadId: job.threadId, turnId: job.turnId };
672
- const posted = await this.relay.inputRequest(target, { toolUseId, questions: input.questions });
673
- if (!posted) {
674
- console.error(`[AskUserQuestion] failed to post input-request for ${toolUseId} — frontend may not show question UI`);
675
- }
676
- const answers = await new Promise((resolve, reject) => {
799
+ // Parks the turn on a blocking dashboard prompt. An unanswered prompt must
800
+ // NOT error the turn: the wait window ending resolves to `unanswered` so the
801
+ // caller can deny the tool gracefully — the agent ends its turn cleanly and
802
+ // the user replies with a normal message whenever they return.
803
+ awaitPromptDecision(input) {
804
+ const { label, signal, controls, pending, toolUseId } = input;
805
+ return new Promise((resolve, reject) => {
677
806
  if (signal?.aborted || controls.abortSignal.aborted) {
678
- reject(new Error("AskUserQuestion aborted before awaiting answer"));
807
+ reject(new Error(`${label} aborted before awaiting decision`));
679
808
  return;
680
809
  }
681
810
  const timeout = setTimeout(() => {
682
- controls.pendingQuestions.delete(toolUseId);
683
- reject(new Error(`AskUserQuestion timed out after ${this.askUserQuestionTimeoutMs}ms`));
811
+ pending.delete(toolUseId);
812
+ console.error(`[${label}] no answer after ${this.askUserQuestionTimeoutMs}ms — ending the wait gracefully`);
813
+ resolve({ kind: "unanswered" });
684
814
  }, this.askUserQuestionTimeoutMs);
685
- controls.pendingQuestions.set(toolUseId, {
686
- resolve: (answers) => {
815
+ pending.set(toolUseId, {
816
+ resolve: (value) => {
687
817
  clearTimeout(timeout);
688
- resolve(answers);
818
+ resolve({ kind: "answered", value });
689
819
  },
690
820
  reject: (reason) => {
691
821
  clearTimeout(timeout);
@@ -694,17 +824,34 @@ export class BridgeJobExecutor {
694
824
  });
695
825
  const abort = () => {
696
826
  clearTimeout(timeout);
697
- controls.pendingQuestions.delete(toolUseId);
698
- reject(new Error("AskUserQuestion aborted"));
827
+ pending.delete(toolUseId);
828
+ reject(new Error(`${label} aborted`));
699
829
  };
700
830
  signal?.addEventListener("abort", abort, { once: true });
701
831
  controls.abortSignal.addEventListener("abort", abort, { once: true });
702
832
  });
833
+ }
834
+ async handleAskUserQuestion(job, controls, input, toolUseId, signal) {
835
+ const target = { threadId: job.threadId, turnId: job.turnId };
836
+ const posted = await this.relay.inputRequest(target, { toolUseId, questions: input.questions });
837
+ if (!posted) {
838
+ console.error(`[AskUserQuestion] failed to post input-request for ${toolUseId} — frontend may not show question UI`);
839
+ }
840
+ const decision = await this.awaitPromptDecision({
841
+ label: "AskUserQuestion",
842
+ signal,
843
+ controls,
844
+ pending: controls.pendingQuestions,
845
+ toolUseId,
846
+ });
703
847
  void this.relay.inputResolved(target);
848
+ if (decision.kind === "unanswered") {
849
+ return { behavior: "deny", message: PROMPT_UNANSWERED_MESSAGE };
850
+ }
704
851
  // The SDK matches AskUserQuestion answers to questions by TEXT, so hand it
705
852
  // the documented `{ [questionText]: answer }` shape, not the dashboard's
706
853
  // positional `q{i}` keys — otherwise the tool reports "dismissed".
707
- const answersByQuestionText = remapAnswersToQuestionText(input.questions, answers);
854
+ const answersByQuestionText = remapAnswersToQuestionText(input.questions, decision.value);
708
855
  return { behavior: "allow", updatedInput: { questions: input.questions, answers: answersByQuestionText } };
709
856
  }
710
857
  async handleTagInputRequest(job, controls, input, toolUseId, signal) {
@@ -733,37 +880,20 @@ export class BridgeJobExecutor {
733
880
  message: "The tag approval form could not be delivered to the dashboard — retry request_tag_input in a moment.",
734
881
  };
735
882
  }
736
- const resolution = await new Promise((resolve, reject) => {
737
- if (signal?.aborted || controls.abortSignal.aborted) {
738
- reject(new Error("request_tag_input aborted before awaiting resolution"));
739
- return;
740
- }
741
- const timeout = setTimeout(() => {
742
- controls.pendingTagInputs.delete(toolUseId);
743
- reject(new Error(`request_tag_input timed out after ${this.askUserQuestionTimeoutMs}ms`));
744
- }, this.askUserQuestionTimeoutMs);
745
- controls.pendingTagInputs.set(toolUseId, {
746
- resolve: (result) => {
747
- clearTimeout(timeout);
748
- resolve(result);
749
- },
750
- reject: (reason) => {
751
- clearTimeout(timeout);
752
- reject(reason);
753
- },
754
- });
755
- const abort = () => {
756
- clearTimeout(timeout);
757
- controls.pendingTagInputs.delete(toolUseId);
758
- reject(new Error("request_tag_input aborted"));
759
- };
760
- signal?.addEventListener("abort", abort, { once: true });
761
- controls.abortSignal.addEventListener("abort", abort, { once: true });
883
+ const decision = await this.awaitPromptDecision({
884
+ label: "request_tag_input",
885
+ signal,
886
+ controls,
887
+ pending: controls.pendingTagInputs,
888
+ toolUseId,
762
889
  });
763
890
  void this.relay.tagInputResolved(target);
891
+ if (decision.kind === "unanswered") {
892
+ return { behavior: "deny", message: PROMPT_UNANSWERED_MESSAGE };
893
+ }
764
894
  // The MCP tool handler echoes `resolution` back as the tool result — the
765
895
  // redacted per-tab results are all the model ever sees.
766
- return { behavior: "allow", updatedInput: { changes: sanitizedChanges, resolution } };
896
+ return { behavior: "allow", updatedInput: { changes: sanitizedChanges, resolution: decision.value } };
767
897
  }
768
898
  async handleFlowInputRequest(job, controls, input, toolUseId, signal) {
769
899
  const target = { threadId: job.threadId, turnId: job.turnId };
@@ -787,38 +917,21 @@ export class BridgeJobExecutor {
787
917
  message: "The form-configuration request could not be delivered to the dashboard — retry request_flow_input in a moment.",
788
918
  };
789
919
  }
790
- const resolution = await new Promise((resolve, reject) => {
791
- if (signal?.aborted || controls.abortSignal.aborted) {
792
- reject(new Error("request_flow_input aborted before awaiting resolution"));
793
- return;
794
- }
795
- const timeout = setTimeout(() => {
796
- controls.pendingFlowInputs.delete(toolUseId);
797
- reject(new Error(`request_flow_input timed out after ${this.askUserQuestionTimeoutMs}ms`));
798
- }, this.askUserQuestionTimeoutMs);
799
- controls.pendingFlowInputs.set(toolUseId, {
800
- resolve: (result) => {
801
- clearTimeout(timeout);
802
- resolve(result);
803
- },
804
- reject: (reason) => {
805
- clearTimeout(timeout);
806
- reject(reason);
807
- },
808
- });
809
- const abort = () => {
810
- clearTimeout(timeout);
811
- controls.pendingFlowInputs.delete(toolUseId);
812
- reject(new Error("request_flow_input aborted"));
813
- };
814
- signal?.addEventListener("abort", abort, { once: true });
815
- controls.abortSignal.addEventListener("abort", abort, { once: true });
920
+ const decision = await this.awaitPromptDecision({
921
+ label: "request_flow_input",
922
+ signal,
923
+ controls,
924
+ pending: controls.pendingFlowInputs,
925
+ toolUseId,
816
926
  });
817
927
  void this.relay.flowInputResolved(target);
928
+ if (decision.kind === "unanswered") {
929
+ return { behavior: "deny", message: PROMPT_UNANSWERED_MESSAGE };
930
+ }
818
931
  // The MCP tool handler echoes `resolution` — the redacted per-tab results are
819
932
  // all the model ever sees; secret values are written into the flow file by the
820
933
  // bridge's /flow-input-decision handler and never reach here.
821
- return { behavior: "allow", updatedInput: { requests: sanitizedRequests, resolution } };
934
+ return { behavior: "allow", updatedInput: { requests: sanitizedRequests, resolution: decision.value } };
822
935
  }
823
936
  async handlePlanReview(job, controls, input, toolUseId, signal) {
824
937
  const target = { threadId: job.threadId, turnId: job.turnId };
@@ -828,34 +941,20 @@ export class BridgeJobExecutor {
828
941
  if (!posted) {
829
942
  console.error(`[ExitPlanMode] failed to post plan-review for ${toolUseId} — frontend may not show plan UI`);
830
943
  }
831
- const decision = await new Promise((resolve, reject) => {
832
- if (signal?.aborted || controls.abortSignal.aborted) {
833
- reject(new Error("ExitPlanMode aborted before awaiting decision"));
834
- return;
835
- }
836
- const timeout = setTimeout(() => {
837
- controls.pendingPlans.delete(toolUseId);
838
- reject(new Error(`ExitPlanMode timed out after ${this.askUserQuestionTimeoutMs}ms`));
839
- }, this.askUserQuestionTimeoutMs);
840
- controls.pendingPlans.set(toolUseId, {
841
- resolve: (decision) => {
842
- clearTimeout(timeout);
843
- resolve(decision);
844
- },
845
- reject: (reason) => {
846
- clearTimeout(timeout);
847
- reject(reason);
848
- },
849
- });
850
- const abort = () => {
851
- clearTimeout(timeout);
852
- controls.pendingPlans.delete(toolUseId);
853
- reject(new Error("ExitPlanMode aborted"));
854
- };
855
- signal?.addEventListener("abort", abort, { once: true });
856
- controls.abortSignal.addEventListener("abort", abort, { once: true });
944
+ const reviewed = await this.awaitPromptDecision({
945
+ label: "ExitPlanMode",
946
+ signal,
947
+ controls,
948
+ pending: controls.pendingPlans,
949
+ toolUseId,
857
950
  });
858
951
  void this.relay.planResolved(target);
952
+ if (reviewed.kind === "unanswered") {
953
+ // Deny WITHOUT interrupt (same invariant as plan rejection) so the turn
954
+ // ends cleanly; the user can approve with a normal message later.
955
+ return { behavior: "deny", message: PROMPT_UNANSWERED_MESSAGE };
956
+ }
957
+ const decision = reviewed.value;
859
958
  if (decision.kind === "approve") {
860
959
  return { behavior: "allow", updatedInput: input };
861
960
  }