@sideboard-ai/core 0.1.307 → 0.1.309

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.
@@ -638,6 +638,33 @@ var CURSOR_STREAM_CUTOFF = /nghttp2|premature close|\[unavailable\]\s+error\b|ht
638
638
  function looksLikeCursorStreamCutOff(text) {
639
639
  return CURSOR_STREAM_CUTOFF.test(text.trim());
640
640
  }
641
+ function cursorModelDisplayName(model) {
642
+ const raw = (model ?? "").trim();
643
+ if (!raw || /^(auto|default)$/i.test(raw)) return "";
644
+ if (/^grok-/i.test(raw)) return raw.replace(/^grok-/i, "Grok ");
645
+ return raw;
646
+ }
647
+ function formatCursorStreamIdleAfterToolsError(model) {
648
+ const label = cursorModelDisplayName(model);
649
+ return label ? `Cursor stopped responding after a tool call (${label})` : "Cursor stopped responding after a tool call";
650
+ }
651
+ function createCursorStreamToolProgress() {
652
+ return { completedTool: false, assistantAfterTool: false };
653
+ }
654
+ function noteCursorAgentEvent(state, event) {
655
+ if (event.type === "tool_result" && event.partial !== true) {
656
+ state.completedTool = true;
657
+ state.assistantAfterTool = false;
658
+ return;
659
+ }
660
+ if (event.type === "stdout" && !event.parentId) {
661
+ const text = typeof event.data === "string" ? event.data.trim() : "";
662
+ if (state.completedTool && text) state.assistantAfterTool = true;
663
+ }
664
+ }
665
+ function isCursorIdleAfterCompletedTool(state) {
666
+ return state.completedTool && !state.assistantAfterTool;
667
+ }
641
668
 
642
669
  // src/agents/error-detail.ts
643
670
  function formatUnknownDetail(err) {
@@ -2081,25 +2108,29 @@ async function main() {
2081
2108
  } catch {
2082
2109
  }
2083
2110
  let lastUsageRunId;
2111
+ const toolProgress = createCursorStreamToolProgress();
2084
2112
  const run2 = await sendPrompt(agent, prompt, {
2085
2113
  onDelta: ({ update }) => {
2086
2114
  bump();
2087
2115
  for (const event of cursorDeltaToEvents(update)) {
2116
+ noteCursorAgentEvent(toolProgress, event);
2088
2117
  stream.push(event);
2089
2118
  }
2090
2119
  }
2091
2120
  });
2092
2121
  liveRun = run2;
2093
2122
  let streamIdle = false;
2123
+ let streamIdleAfterTools = false;
2094
2124
  try {
2095
2125
  for await (const msg of iterateUntilIdle(
2096
2126
  run2.stream(),
2097
2127
  CURSOR_STREAM_IDLE_MS,
2098
2128
  () => {
2099
2129
  streamIdle = true;
2130
+ streamIdleAfterTools = isCursorIdleAfterCompletedTool(toolProgress);
2100
2131
  emit({
2101
2132
  type: "stderr",
2102
- data: "Cursor stream idle \u2014 finishing the turn (SDK stream did not close)"
2133
+ data: streamIdleAfterTools ? `Cursor run failed: ${formatCursorStreamIdleAfterToolsError(req.model)}` : "Cursor stream idle \u2014 finishing the turn (SDK stream did not close)"
2103
2134
  });
2104
2135
  void cancelLiveRun();
2105
2136
  },
@@ -2111,6 +2142,7 @@ async function main() {
2111
2142
  lastUsageRunId = sdkMsg.run_id.trim();
2112
2143
  }
2113
2144
  for (const event of cursorSdkMessageToEvents(msg)) {
2145
+ noteCursorAgentEvent(toolProgress, event);
2114
2146
  stream.push(event);
2115
2147
  }
2116
2148
  }
@@ -2121,6 +2153,9 @@ async function main() {
2121
2153
  setTimeout(() => resolve2({ status: "cancelled" }), 5e3);
2122
2154
  })
2123
2155
  ]) : await run2.wait();
2156
+ if (streamIdleAfterTools) {
2157
+ return 2;
2158
+ }
2124
2159
  if (result.status === "error") {
2125
2160
  const detail = formatUnknownDetail(result.error);
2126
2161
  if (isAgentBusyError(detail)) {
@@ -5,7 +5,7 @@ import {
5
5
  guardCursorCustomTool,
6
6
  shouldReturnRunErrorToAgent,
7
7
  sideboardCursorRpcTools
8
- } from "../chunk-YJBUYGPN.js";
8
+ } from "../chunk-NCHOTN3C.js";
9
9
  import {
10
10
  createAgentStreamCoalescer
11
11
  } from "../chunk-AXQ4ZWHK.js";
@@ -20,12 +20,16 @@ import {
20
20
  fetchCursorTurnCostUsd,
21
21
  importCursorSdk,
22
22
  resolveCursorSdkRoot
23
- } from "../chunk-MMVMQ34G.js";
23
+ } from "../chunk-UA3YKQ3D.js";
24
24
  import {
25
25
  TOOL_RESULT_STORE_MAX_CHARS,
26
+ createCursorStreamToolProgress,
27
+ formatCursorStreamIdleAfterToolsError,
26
28
  formatUnknownDetail,
27
- looksLikeCursorStreamCutOff
28
- } from "../chunk-HO5R3ZRH.js";
29
+ isCursorIdleAfterCompletedTool,
30
+ looksLikeCursorStreamCutOff,
31
+ noteCursorAgentEvent
32
+ } from "../chunk-5XNT6BCG.js";
29
33
  import "../chunk-6K64FJDD.js";
30
34
  import {
31
35
  cursorSdkStoreDir
@@ -829,25 +833,29 @@ async function main() {
829
833
  } catch {
830
834
  }
831
835
  let lastUsageRunId;
836
+ const toolProgress = createCursorStreamToolProgress();
832
837
  const run = await sendPrompt(agent, prompt, {
833
838
  onDelta: ({ update }) => {
834
839
  bump();
835
840
  for (const event of cursorDeltaToEvents(update)) {
841
+ noteCursorAgentEvent(toolProgress, event);
836
842
  stream.push(event);
837
843
  }
838
844
  }
839
845
  });
840
846
  liveRun = run;
841
847
  let streamIdle = false;
848
+ let streamIdleAfterTools = false;
842
849
  try {
843
850
  for await (const msg of iterateUntilIdle(
844
851
  run.stream(),
845
852
  CURSOR_STREAM_IDLE_MS,
846
853
  () => {
847
854
  streamIdle = true;
855
+ streamIdleAfterTools = isCursorIdleAfterCompletedTool(toolProgress);
848
856
  emit({
849
857
  type: "stderr",
850
- data: "Cursor stream idle \u2014 finishing the turn (SDK stream did not close)"
858
+ data: streamIdleAfterTools ? `Cursor run failed: ${formatCursorStreamIdleAfterToolsError(req.model)}` : "Cursor stream idle \u2014 finishing the turn (SDK stream did not close)"
851
859
  });
852
860
  void cancelLiveRun();
853
861
  },
@@ -859,6 +867,7 @@ async function main() {
859
867
  lastUsageRunId = sdkMsg.run_id.trim();
860
868
  }
861
869
  for (const event of cursorSdkMessageToEvents(msg)) {
870
+ noteCursorAgentEvent(toolProgress, event);
862
871
  stream.push(event);
863
872
  }
864
873
  }
@@ -869,6 +878,9 @@ async function main() {
869
878
  setTimeout(() => resolve2({ status: "cancelled" }), 5e3);
870
879
  })
871
880
  ]) : await run.wait();
881
+ if (streamIdleAfterTools) {
882
+ return 2;
883
+ }
872
884
  if (result.status === "error") {
873
885
  const detail = formatUnknownDetail(result.error);
874
886
  if (isAgentBusyError(detail)) {
@@ -37,9 +37,9 @@ import {
37
37
  planModeTurnInstruction,
38
38
  resolveCursorModelId,
39
39
  resolveQuotaFallbackAgent
40
- } from "./chunk-G2L3CR3A.js";
40
+ } from "./chunk-FMSLOWKR.js";
41
41
  import "./chunk-VEVFHO6K.js";
42
- import "./chunk-F36QRDM4.js";
42
+ import "./chunk-7Q327QF2.js";
43
43
  import {
44
44
  getAgentSetupInfo,
45
45
  installAgent,
@@ -67,8 +67,8 @@ import {
67
67
  preferredCursorCostCents,
68
68
  resolveCursorSdkRoot,
69
69
  turnCostUsdFromCursorUsage
70
- } from "./chunk-MMVMQ34G.js";
71
- import "./chunk-HO5R3ZRH.js";
70
+ } from "./chunk-UA3YKQ3D.js";
71
+ import "./chunk-5XNT6BCG.js";
72
72
  import "./chunk-6K64FJDD.js";
73
73
  import "./chunk-LG6RJBIY.js";
74
74
  import {
@@ -46,7 +46,7 @@ import {
46
46
  resolveCursorSdkRoot,
47
47
  resolveQuotaFallbackAgent,
48
48
  turnCostUsdFromCursorUsage
49
- } from "./chunk-EZYZGDWU.js";
49
+ } from "./chunk-VXI7FFGO.js";
50
50
  import {
51
51
  getAgentSetupInfo,
52
52
  installAgent,
@@ -62,7 +62,7 @@ import {
62
62
  import "./chunk-42JGPPVS.js";
63
63
  import "./chunk-PXMLMBMI.js";
64
64
  import "./chunk-2GPQZZLY.js";
65
- import "./chunk-5XQ43YZU.js";
65
+ import "./chunk-I5TESHDN.js";
66
66
  import "./chunk-X3FSWWW2.js";
67
67
  import "./chunk-DLGGLV2M.js";
68
68
  import "./chunk-N2WW62LF.js";
@@ -1,9 +1,16 @@
1
1
  // src/agents/cursor-stream-cutoff.ts
2
2
  var CURSOR_STREAM_CUTOFF = /nghttp2|premature close|\[unavailable\]\s+error\b|http\/2 stream closed|\bcancel \(0x8\)|error code cancel\b/i;
3
+ var CURSOR_STREAM_IDLE_AFTER_TOOLS = /stopped responding after a tool call/i;
3
4
  var CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT = 3;
4
5
  function looksLikeCursorStreamCutOff(text) {
5
6
  return CURSOR_STREAM_CUTOFF.test(text.trim());
6
7
  }
8
+ function looksLikeCursorStreamIdleAfterTools(text) {
9
+ return CURSOR_STREAM_IDLE_AFTER_TOOLS.test(text.trim());
10
+ }
11
+ function looksLikeCursorStreamFailure(text) {
12
+ return looksLikeCursorStreamCutOff(text) || looksLikeCursorStreamIdleAfterTools(text);
13
+ }
7
14
  function cursorStreamCutOffContinueLimit(detail) {
8
15
  if (looksLikeCursorStreamCutOff(detail) || /connection stalled/i.test(detail)) {
9
16
  return CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
@@ -15,6 +22,33 @@ function nextCrashContinueCount(detail, used) {
15
22
  if (used >= limit) return null;
16
23
  return used + 1;
17
24
  }
25
+ function cursorModelDisplayName(model) {
26
+ const raw = (model ?? "").trim();
27
+ if (!raw || /^(auto|default)$/i.test(raw)) return "";
28
+ if (/^grok-/i.test(raw)) return raw.replace(/^grok-/i, "Grok ");
29
+ return raw;
30
+ }
31
+ function formatCursorStreamIdleAfterToolsError(model) {
32
+ const label = cursorModelDisplayName(model);
33
+ return label ? `Cursor stopped responding after a tool call (${label})` : "Cursor stopped responding after a tool call";
34
+ }
35
+ function createCursorStreamToolProgress() {
36
+ return { completedTool: false, assistantAfterTool: false };
37
+ }
38
+ function noteCursorAgentEvent(state, event) {
39
+ if (event.type === "tool_result" && event.partial !== true) {
40
+ state.completedTool = true;
41
+ state.assistantAfterTool = false;
42
+ return;
43
+ }
44
+ if (event.type === "stdout" && !event.parentId) {
45
+ const text = typeof event.data === "string" ? event.data.trim() : "";
46
+ if (state.completedTool && text) state.assistantAfterTool = true;
47
+ }
48
+ }
49
+ function isCursorIdleAfterCompletedTool(state) {
50
+ return state.completedTool && !state.assistantAfterTool;
51
+ }
18
52
 
19
53
  // src/agents/error-detail.ts
20
54
  function formatUnknownDetail(err) {
@@ -223,7 +257,7 @@ function looksLikeRetryableRunnerCrash(text) {
223
257
  if (looksLikeInvalidAgentSession(text)) return false;
224
258
  if (looksLikeV8Oom(text)) return false;
225
259
  if (looksLikeHugeToolResultDump(text)) return true;
226
- if (looksLikeCursorStreamCutOff(text)) return true;
260
+ if (looksLikeCursorStreamFailure(text)) return true;
227
261
  if (looksLikeBrightsyEmptyCompletion(text)) return true;
228
262
  const lower = text.trim().toLowerCase();
229
263
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
@@ -300,7 +334,7 @@ function humanizeAgentFailDetail(detail) {
300
334
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
301
335
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
302
336
  }
303
- if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
337
+ if (/connection stalled/.test(lower) || looksLikeCursorStreamFailure(raw)) {
304
338
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
305
339
  }
306
340
  if (looksLikeBrightsyEmptyCompletion(raw)) {
@@ -387,6 +421,10 @@ function formatTurnExitError(exitCode, stderrSummary) {
387
421
  export {
388
422
  looksLikeCursorStreamCutOff,
389
423
  nextCrashContinueCount,
424
+ formatCursorStreamIdleAfterToolsError,
425
+ createCursorStreamToolProgress,
426
+ noteCursorAgentEvent,
427
+ isCursorIdleAfterCompletedTool,
390
428
  formatUnknownDetail,
391
429
  extractJsonErrorMessage,
392
430
  pushTurnStderr,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  clipToolResultForStore
3
- } from "./chunk-HO5R3ZRH.js";
3
+ } from "./chunk-5XNT6BCG.js";
4
4
 
5
5
  // src/agents/message-parts.ts
6
6
  function asRecord(input) {
@@ -5,7 +5,7 @@ import {
5
5
  compactSummaryPart,
6
6
  isInternalAgentStatusText,
7
7
  lastAssistantMessageText
8
- } from "./chunk-5XQ43YZU.js";
8
+ } from "./chunk-I5TESHDN.js";
9
9
  import {
10
10
  isCloudCoordinatorThread,
11
11
  isGlobalThread,
@@ -1235,12 +1235,23 @@ function isAskUserToolName(name) {
1235
1235
  const base = name.replace(/^mcp__sideboard__/i, "");
1236
1236
  return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
1237
1237
  }
1238
+ function questionsFromToolPart(part) {
1239
+ const fromInput = parsePlanQuestionsInput(part.input);
1240
+ if (fromInput.length) return fromInput;
1241
+ const raw = part.result?.trim();
1242
+ if (!raw || !raw.startsWith("{")) return [];
1243
+ try {
1244
+ return parsePlanQuestionsInput(JSON.parse(raw));
1245
+ } catch {
1246
+ return [];
1247
+ }
1248
+ }
1238
1249
  function extractPendingPlanQuestions(parts) {
1239
1250
  if (!parts?.length) return null;
1240
1251
  for (let i = parts.length - 1; i >= 0; i--) {
1241
1252
  const p = parts[i];
1242
1253
  if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
1243
- const questions = parsePlanQuestionsInput(p.input);
1254
+ const questions = questionsFromToolPart(p);
1244
1255
  if (!questions.length) continue;
1245
1256
  return {
1246
1257
  id: p.id || `ask-${i}`,
@@ -1676,7 +1687,7 @@ var PORT_RANGE_SIZE = 10;
1676
1687
  var RECLAIM_RETRY_DELAY_MS = 40;
1677
1688
  var RECLAIM_TRIES = 8;
1678
1689
  async function killReclaimable(ports, worktreePath, signal) {
1679
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-ZFRXTVGP.js");
1690
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-AMZ5SKG2.js");
1680
1691
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1681
1692
  else killListenersOnPorts2(ports);
1682
1693
  }
@@ -9,7 +9,7 @@ import {
9
9
  looksLikeSkillBody,
10
10
  skillCommandFromBody,
11
11
  withEventParentId
12
- } from "./chunk-F36QRDM4.js";
12
+ } from "./chunk-7Q327QF2.js";
13
13
  import {
14
14
  isOrchestratorThread
15
15
  } from "./chunk-5QX2NYXL.js";
@@ -45,13 +45,13 @@ import {
45
45
  packagedMcpStdioPath,
46
46
  parseCursorRunnerLine,
47
47
  resolveNodeLaunch
48
- } from "./chunk-MMVMQ34G.js";
48
+ } from "./chunk-UA3YKQ3D.js";
49
49
  import {
50
50
  extractJsonErrorMessage,
51
51
  formatUnknownDetail,
52
52
  looksLikeAgentFailureMessage,
53
53
  looksLikeBrightsyEmptyCompletion
54
- } from "./chunk-HO5R3ZRH.js";
54
+ } from "./chunk-5XNT6BCG.js";
55
55
  import {
56
56
  run
57
57
  } from "./chunk-QVN2WOKJ.js";
@@ -3,10 +3,17 @@
3
3
 
4
4
  // src/agents/cursor-stream-cutoff.ts
5
5
  var CURSOR_STREAM_CUTOFF = /nghttp2|premature close|\[unavailable\]\s+error\b|http\/2 stream closed|\bcancel \(0x8\)|error code cancel\b/i;
6
+ var CURSOR_STREAM_IDLE_AFTER_TOOLS = /stopped responding after a tool call/i;
6
7
  var CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT = 3;
7
8
  function looksLikeCursorStreamCutOff(text) {
8
9
  return CURSOR_STREAM_CUTOFF.test(text.trim());
9
10
  }
11
+ function looksLikeCursorStreamIdleAfterTools(text) {
12
+ return CURSOR_STREAM_IDLE_AFTER_TOOLS.test(text.trim());
13
+ }
14
+ function looksLikeCursorStreamFailure(text) {
15
+ return looksLikeCursorStreamCutOff(text) || looksLikeCursorStreamIdleAfterTools(text);
16
+ }
10
17
  function cursorStreamCutOffContinueLimit(detail) {
11
18
  if (looksLikeCursorStreamCutOff(detail) || /connection stalled/i.test(detail)) {
12
19
  return CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
@@ -226,7 +233,7 @@ function looksLikeRetryableRunnerCrash(text) {
226
233
  if (looksLikeInvalidAgentSession(text)) return false;
227
234
  if (looksLikeV8Oom(text)) return false;
228
235
  if (looksLikeHugeToolResultDump(text)) return true;
229
- if (looksLikeCursorStreamCutOff(text)) return true;
236
+ if (looksLikeCursorStreamFailure(text)) return true;
230
237
  if (looksLikeBrightsyEmptyCompletion(text)) return true;
231
238
  const lower = text.trim().toLowerCase();
232
239
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
@@ -303,7 +310,7 @@ function humanizeAgentFailDetail(detail) {
303
310
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
304
311
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
305
312
  }
306
- if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
313
+ if (/connection stalled/.test(lower) || looksLikeCursorStreamFailure(raw)) {
307
314
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
308
315
  }
309
316
  if (looksLikeBrightsyEmptyCompletion(raw)) {
@@ -14,7 +14,7 @@ import {
14
14
  parseSessionQuotaResetAt,
15
15
  planModeTurnInstruction,
16
16
  resolveQuotaFallbackAgent
17
- } from "./chunk-G2L3CR3A.js";
17
+ } from "./chunk-FMSLOWKR.js";
18
18
  import {
19
19
  REVIEW_TAB_TITLE,
20
20
  applyWorktreePkgCacheEnv,
@@ -55,7 +55,7 @@ import {
55
55
  worktreeDevProcessKey,
56
56
  worktreeRunProcessKey,
57
57
  worktreeSetupProcessKey
58
- } from "./chunk-OVNIP265.js";
58
+ } from "./chunk-VBVSJUM2.js";
59
59
  import {
60
60
  applyAgentEvent,
61
61
  clipAgentEventForPaint,
@@ -70,7 +70,7 @@ import {
70
70
  partsToAssistantText,
71
71
  stripBrightsyNdjsonNoise,
72
72
  toolDescription
73
- } from "./chunk-F36QRDM4.js";
73
+ } from "./chunk-7Q327QF2.js";
74
74
  import {
75
75
  titleFromPrompt
76
76
  } from "./chunk-7H655NEW.js";
@@ -181,7 +181,7 @@ import {
181
181
  import {
182
182
  applyAgentRunnerHeapEnv,
183
183
  packagedMcpDir
184
- } from "./chunk-MMVMQ34G.js";
184
+ } from "./chunk-UA3YKQ3D.js";
185
185
  import {
186
186
  fallbackTurnFailDetail,
187
187
  formatAgentErrorContinuePrompt,
@@ -196,7 +196,7 @@ import {
196
196
  shouldRetryFailedAgentTurn,
197
197
  summarizeTurnStderr,
198
198
  turnFailChatText
199
- } from "./chunk-HO5R3ZRH.js";
199
+ } from "./chunk-5XNT6BCG.js";
200
200
  import {
201
201
  clearStaleIndexLocks,
202
202
  enrichPathWithNpmGlobalBin,
@@ -1654,6 +1654,23 @@ function worktreeAgentBlock(group) {
1654
1654
  }
1655
1655
  return best;
1656
1656
  }
1657
+ function sidebarRowAgentBlock(group, opts) {
1658
+ if (opts.active) {
1659
+ const open = group.find((thread) => thread.id === opts.selectedId);
1660
+ if (open) return visibleAgentBlock(open);
1661
+ }
1662
+ return worktreeAgentBlock(group);
1663
+ }
1664
+ function sidebarParentSelectId(group, primaryId, opts) {
1665
+ if (opts.active) return primaryId;
1666
+ return sidebarQuestionThreadId(group, primaryId);
1667
+ }
1668
+ function sidebarQuestionThreadId(group, primaryId) {
1669
+ const block = worktreeAgentBlock(group);
1670
+ if (!block) return primaryId;
1671
+ const owner = group.find((thread) => visibleAgentBlock(thread) === block);
1672
+ return owner?.id ?? primaryId;
1673
+ }
1657
1674
  function threadCardBlockFields(group) {
1658
1675
  const block = worktreeAgentBlock(group);
1659
1676
  return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
@@ -1800,7 +1817,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1800
1817
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1801
1818
  return {
1802
1819
  ...makeAgentBlock("ask_user", reason, at),
1803
- ...pending ? { questionSignature: pending.signature } : {}
1820
+ ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
1804
1821
  };
1805
1822
  }
1806
1823
  if (!planMode || last?.type !== "tool") return null;
@@ -1815,14 +1832,24 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1815
1832
  at
1816
1833
  );
1817
1834
  }
1835
+ function withStampedQuestions(decision, existing) {
1836
+ if (decision.source !== "ask_user" || decision.questions?.length) return decision;
1837
+ if (existing?.source !== "ask_user" || !existing.questions?.length) return decision;
1838
+ return {
1839
+ ...decision,
1840
+ questions: existing.questions,
1841
+ questionSignature: decision.questionSignature ?? existing.questionSignature
1842
+ };
1843
+ }
1818
1844
  function agentBlockAfterTurn(input) {
1819
1845
  if (input.failed) return null;
1820
1846
  const decision = decisionBlockFromParts(input.parts, input.planMode);
1821
1847
  if (decision) {
1822
- if (isDismissedAskUserBlock(decision, input.dismissedQuestionSignature)) {
1848
+ const next = withStampedQuestions(decision, input.existing);
1849
+ if (isDismissedAskUserBlock(next, input.dismissedQuestionSignature)) {
1823
1850
  return input.existing?.source === "reported" ? input.existing : null;
1824
1851
  }
1825
- return decision;
1852
+ return next;
1826
1853
  }
1827
1854
  if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1828
1855
  return null;
@@ -1865,7 +1892,7 @@ function settleAgentBlock(threadId, parts, failed) {
1865
1892
  });
1866
1893
  const prev = thread.agentBlock ?? null;
1867
1894
  if (prev === next) return;
1868
- if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature) {
1895
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature && planQuestionsSignature(prev.questions ?? []) === planQuestionsSignature(next.questions ?? [])) {
1869
1896
  return;
1870
1897
  }
1871
1898
  updateThread(threadId, { agentBlock: next });
@@ -1888,7 +1915,11 @@ function stampAskUserBlock(resolveCaller, questions) {
1888
1915
  try {
1889
1916
  writeAgentBlock(
1890
1917
  resolveCaller().id,
1891
- parsed.length ? { ...block, questionSignature: planQuestionsSignature(parsed) } : block
1918
+ parsed.length ? {
1919
+ ...block,
1920
+ questionSignature: planQuestionsSignature(parsed),
1921
+ questions: parsed
1922
+ } : block
1892
1923
  );
1893
1924
  } catch {
1894
1925
  }
@@ -2996,7 +3027,7 @@ async function createThread(input, _onSetupLine) {
2996
3027
  return readThread(thread.id) ?? thread;
2997
3028
  }
2998
3029
  async function listLinearIssues(agent, repoPath) {
2999
- const { getAdapter: getAdapter2 } = await import("./agents-IWNQOQOU.js");
3030
+ const { getAdapter: getAdapter2 } = await import("./agents-DG2UPJ6N.js");
3000
3031
  await requireAgent(agent, { requireLinear: true });
3001
3032
  const adapter = getAdapter2(agent);
3002
3033
  if (!adapter.listLinearIssues) {
@@ -5838,7 +5869,7 @@ function formatWorktreeReminder() {
5838
5869
  return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5839
5870
  }
5840
5871
  function formatClaudeSubagentReminder() {
5841
- return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself.';
5872
+ return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
5842
5873
  }
5843
5874
  function claudeSubagentTurnReminder(thread) {
5844
5875
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -6710,7 +6741,7 @@ function formatScheduledPrompt(name, prompt) {
6710
6741
  ${prompt}`;
6711
6742
  }
6712
6743
  async function defaultDeps() {
6713
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-XRTUGSKX.js");
6744
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-T76JRIIN.js");
6714
6745
  const orch = getOrchestrator2();
6715
6746
  return {
6716
6747
  findThread: (id) => findThreadByRef(id),
@@ -9621,6 +9652,9 @@ export {
9621
9652
  worktreeBoardStatus,
9622
9653
  visibleAgentBlock,
9623
9654
  worktreeAgentBlock,
9655
+ sidebarRowAgentBlock,
9656
+ sidebarParentSelectId,
9657
+ sidebarQuestionThreadId,
9624
9658
  reviewLabelForRepo,
9625
9659
  classifyWorktreeListBadges,
9626
9660
  isHomeBoardThread,
@@ -4,7 +4,7 @@ import {
4
4
  formatUnknownDetail,
5
5
  looksLikeCursorStreamCutOff,
6
6
  looksLikeHugeToolResultDump
7
- } from "./chunk-HO5R3ZRH.js";
7
+ } from "./chunk-5XNT6BCG.js";
8
8
  import {
9
9
  CLI_DIR
10
10
  } from "./chunk-6K64FJDD.js";
@@ -27,7 +27,7 @@ import {
27
27
  parseSessionQuotaResetAt,
28
28
  planModeTurnInstruction,
29
29
  resolveQuotaFallbackAgent
30
- } from "./chunk-EZYZGDWU.js";
30
+ } from "./chunk-VXI7FFGO.js";
31
31
  import {
32
32
  REVIEW_TAB_TITLE,
33
33
  applyWorktreePkgCacheEnv,
@@ -68,7 +68,7 @@ import {
68
68
  worktreeDevProcessKey,
69
69
  worktreeRunProcessKey,
70
70
  worktreeSetupProcessKey
71
- } from "./chunk-5SFA27CF.js";
71
+ } from "./chunk-AILQRQ26.js";
72
72
  import {
73
73
  applyAgentEvent,
74
74
  clipAgentEventForPaint,
@@ -95,7 +95,7 @@ import {
95
95
  summarizeTurnStderr,
96
96
  toolDescription,
97
97
  turnFailChatText
98
- } from "./chunk-5XQ43YZU.js";
98
+ } from "./chunk-I5TESHDN.js";
99
99
  import {
100
100
  SLACK_REPLY_FORMATTING,
101
101
  addWorkspace,
@@ -1703,7 +1703,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1703
1703
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1704
1704
  return {
1705
1705
  ...makeAgentBlock("ask_user", reason, at),
1706
- ...pending ? { questionSignature: pending.signature } : {}
1706
+ ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
1707
1707
  };
1708
1708
  }
1709
1709
  if (!planMode || last?.type !== "tool") return null;
@@ -1718,14 +1718,24 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1718
1718
  at
1719
1719
  );
1720
1720
  }
1721
+ function withStampedQuestions(decision, existing) {
1722
+ if (decision.source !== "ask_user" || decision.questions?.length) return decision;
1723
+ if (existing?.source !== "ask_user" || !existing.questions?.length) return decision;
1724
+ return {
1725
+ ...decision,
1726
+ questions: existing.questions,
1727
+ questionSignature: decision.questionSignature ?? existing.questionSignature
1728
+ };
1729
+ }
1721
1730
  function agentBlockAfterTurn(input) {
1722
1731
  if (input.failed) return null;
1723
1732
  const decision = decisionBlockFromParts(input.parts, input.planMode);
1724
1733
  if (decision) {
1725
- if (isDismissedAskUserBlock(decision, input.dismissedQuestionSignature)) {
1734
+ const next = withStampedQuestions(decision, input.existing);
1735
+ if (isDismissedAskUserBlock(next, input.dismissedQuestionSignature)) {
1726
1736
  return input.existing?.source === "reported" ? input.existing : null;
1727
1737
  }
1728
- return decision;
1738
+ return next;
1729
1739
  }
1730
1740
  if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1731
1741
  return null;
@@ -1768,7 +1778,7 @@ function settleAgentBlock(threadId, parts, failed) {
1768
1778
  });
1769
1779
  const prev = thread.agentBlock ?? null;
1770
1780
  if (prev === next) return;
1771
- if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature) {
1781
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature && planQuestionsSignature(prev.questions ?? []) === planQuestionsSignature(next.questions ?? [])) {
1772
1782
  return;
1773
1783
  }
1774
1784
  updateThread(threadId, { agentBlock: next });
@@ -1791,7 +1801,11 @@ function stampAskUserBlock(resolveCaller, questions) {
1791
1801
  try {
1792
1802
  writeAgentBlock(
1793
1803
  resolveCaller().id,
1794
- parsed.length ? { ...block, questionSignature: planQuestionsSignature(parsed) } : block
1804
+ parsed.length ? {
1805
+ ...block,
1806
+ questionSignature: planQuestionsSignature(parsed),
1807
+ questions: parsed
1808
+ } : block
1795
1809
  );
1796
1810
  } catch {
1797
1811
  }
@@ -5534,7 +5548,7 @@ function formatWorktreeReminder() {
5534
5548
  return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5535
5549
  }
5536
5550
  function formatClaudeSubagentReminder() {
5537
- return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself.';
5551
+ return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
5538
5552
  }
5539
5553
  function claudeSubagentTurnReminder(thread) {
5540
5554
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -6375,7 +6389,7 @@ function formatScheduledPrompt(name, prompt) {
6375
6389
  ${prompt}`;
6376
6390
  }
6377
6391
  async function defaultDeps() {
6378
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-MB2Q6KIR.js");
6392
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-4OIMDLIR.js");
6379
6393
  const orch = getOrchestrator2();
6380
6394
  return {
6381
6395
  findThread: (id) => findThreadByRef(id),