@sideboard-ai/core 0.1.308 → 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,
@@ -1687,7 +1687,7 @@ var PORT_RANGE_SIZE = 10;
1687
1687
  var RECLAIM_RETRY_DELAY_MS = 40;
1688
1688
  var RECLAIM_TRIES = 8;
1689
1689
  async function killReclaimable(ports, worktreePath, signal) {
1690
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-E6M3BATZ.js");
1690
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-AMZ5SKG2.js");
1691
1691
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1692
1692
  else killListenersOnPorts2(ports);
1693
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-CTFBZZGZ.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,
@@ -3027,7 +3027,7 @@ async function createThread(input, _onSetupLine) {
3027
3027
  return readThread(thread.id) ?? thread;
3028
3028
  }
3029
3029
  async function listLinearIssues(agent, repoPath) {
3030
- const { getAdapter: getAdapter2 } = await import("./agents-IWNQOQOU.js");
3030
+ const { getAdapter: getAdapter2 } = await import("./agents-DG2UPJ6N.js");
3031
3031
  await requireAgent(agent, { requireLinear: true });
3032
3032
  const adapter = getAdapter2(agent);
3033
3033
  if (!adapter.listLinearIssues) {
@@ -5869,7 +5869,7 @@ function formatWorktreeReminder() {
5869
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).";
5870
5870
  }
5871
5871
  function formatClaudeSubagentReminder() {
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.';
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.';
5873
5873
  }
5874
5874
  function claudeSubagentTurnReminder(thread) {
5875
5875
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -6741,7 +6741,7 @@ function formatScheduledPrompt(name, prompt) {
6741
6741
  ${prompt}`;
6742
6742
  }
6743
6743
  async function defaultDeps() {
6744
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SJU5LYAO.js");
6744
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-T76JRIIN.js");
6745
6745
  const orch = getOrchestrator2();
6746
6746
  return {
6747
6747
  findThread: (id) => findThreadByRef(id),
@@ -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-HRPSFU4Q.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,
@@ -5548,7 +5548,7 @@ function formatWorktreeReminder() {
5548
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).";
5549
5549
  }
5550
5550
  function formatClaudeSubagentReminder() {
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.';
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.';
5552
5552
  }
5553
5553
  function claudeSubagentTurnReminder(thread) {
5554
5554
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -6389,7 +6389,7 @@ function formatScheduledPrompt(name, prompt) {
6389
6389
  ${prompt}`;
6390
6390
  }
6391
6391
  async function defaultDeps() {
6392
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-JTDQ47H7.js");
6392
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-4OIMDLIR.js");
6393
6393
  const orch = getOrchestrator2();
6394
6394
  return {
6395
6395
  findThread: (id) => findThreadByRef(id),
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  extractJsonErrorMessage,
3
3
  formatUnknownDetail
4
- } from "./chunk-HO5R3ZRH.js";
4
+ } from "./chunk-5XNT6BCG.js";
5
5
  import {
6
6
  npmGlobalNodeModules,
7
7
  run
@@ -2,7 +2,7 @@ import {
2
2
  compactSummaryPart,
3
3
  isInternalAgentStatusText,
4
4
  lastAssistantMessageText
5
- } from "./chunk-F36QRDM4.js";
5
+ } from "./chunk-7Q327QF2.js";
6
6
  import {
7
7
  isCloudCoordinatorThread,
8
8
  isGlobalThread,
@@ -29,7 +29,7 @@ import {
29
29
  } from "./chunk-2P4XKWV2.js";
30
30
  import {
31
31
  clipToolResultForStore
32
- } from "./chunk-HO5R3ZRH.js";
32
+ } from "./chunk-5XNT6BCG.js";
33
33
  import {
34
34
  ATTACHMENTS_DIR,
35
35
  DETACHED_JOBS_DIR,
@@ -1823,7 +1823,7 @@ var PORT_RANGE_SIZE = 10;
1823
1823
  var RECLAIM_RETRY_DELAY_MS = 40;
1824
1824
  var RECLAIM_TRIES = 8;
1825
1825
  async function killReclaimable(ports, worktreePath, signal) {
1826
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-IPIMGUR4.js");
1826
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-OFXA5BST.js");
1827
1827
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1828
1828
  else killListenersOnPorts2(ports);
1829
1829
  }
@@ -21,7 +21,7 @@ import {
21
21
  looksLikeSkillBody,
22
22
  skillCommandFromBody,
23
23
  withEventParentId
24
- } from "./chunk-5XQ43YZU.js";
24
+ } from "./chunk-I5TESHDN.js";
25
25
  import {
26
26
  isOrchestratorThread
27
27
  } from "./chunk-X3FSWWW2.js";
@@ -34,8 +34,8 @@ import {
34
34
  tryReservePort,
35
35
  tryReservePorts,
36
36
  worktreeRunPortsPath
37
- } from "./chunk-HRPSFU4Q.js";
38
- import "./chunk-5XQ43YZU.js";
37
+ } from "./chunk-AILQRQ26.js";
38
+ import "./chunk-I5TESHDN.js";
39
39
  import "./chunk-X3FSWWW2.js";
40
40
  import "./chunk-DLGGLV2M.js";
41
41
  import "./chunk-N2WW62LF.js";
@@ -32,14 +32,14 @@ import {
32
32
  tryReservePort,
33
33
  tryReservePorts,
34
34
  worktreeRunPortsPath
35
- } from "./chunk-CTFBZZGZ.js";
36
- import "./chunk-F36QRDM4.js";
35
+ } from "./chunk-VBVSJUM2.js";
36
+ import "./chunk-7Q327QF2.js";
37
37
  import "./chunk-5QX2NYXL.js";
38
38
  import "./chunk-BIPXOHZI.js";
39
39
  import "./chunk-HG25AYBV.js";
40
40
  import "./chunk-2P4XKWV2.js";
41
41
  import "./chunk-F2PVRF3B.js";
42
- import "./chunk-HO5R3ZRH.js";
42
+ import "./chunk-5XNT6BCG.js";
43
43
  import "./chunk-6K64FJDD.js";
44
44
  import "./chunk-LG6RJBIY.js";
45
45
  import "./chunk-QVN2WOKJ.js";
package/dist/index.cjs CHANGED
@@ -5362,6 +5362,12 @@ var init_thread_store = __esm({
5362
5362
  function looksLikeCursorStreamCutOff(text7) {
5363
5363
  return CURSOR_STREAM_CUTOFF.test(text7.trim());
5364
5364
  }
5365
+ function looksLikeCursorStreamIdleAfterTools(text7) {
5366
+ return CURSOR_STREAM_IDLE_AFTER_TOOLS.test(text7.trim());
5367
+ }
5368
+ function looksLikeCursorStreamFailure(text7) {
5369
+ return looksLikeCursorStreamCutOff(text7) || looksLikeCursorStreamIdleAfterTools(text7);
5370
+ }
5365
5371
  function cursorStreamCutOffContinueLimit(detail) {
5366
5372
  if (looksLikeCursorStreamCutOff(detail) || /connection stalled/i.test(detail)) {
5367
5373
  return CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
@@ -5373,11 +5379,12 @@ function nextCrashContinueCount(detail, used) {
5373
5379
  if (used >= limit) return null;
5374
5380
  return used + 1;
5375
5381
  }
5376
- var CURSOR_STREAM_CUTOFF, CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
5382
+ var CURSOR_STREAM_CUTOFF, CURSOR_STREAM_IDLE_AFTER_TOOLS, CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
5377
5383
  var init_cursor_stream_cutoff = __esm({
5378
5384
  "src/agents/cursor-stream-cutoff.ts"() {
5379
5385
  "use strict";
5380
5386
  CURSOR_STREAM_CUTOFF = /nghttp2|premature close|\[unavailable\]\s+error\b|http\/2 stream closed|\bcancel \(0x8\)|error code cancel\b/i;
5387
+ CURSOR_STREAM_IDLE_AFTER_TOOLS = /stopped responding after a tool call/i;
5381
5388
  CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT = 3;
5382
5389
  }
5383
5390
  });
@@ -5581,7 +5588,7 @@ function looksLikeRetryableRunnerCrash(text7) {
5581
5588
  if (looksLikeInvalidAgentSession(text7)) return false;
5582
5589
  if (looksLikeV8Oom(text7)) return false;
5583
5590
  if (looksLikeHugeToolResultDump(text7)) return true;
5584
- if (looksLikeCursorStreamCutOff(text7)) return true;
5591
+ if (looksLikeCursorStreamFailure(text7)) return true;
5585
5592
  if (looksLikeBrightsyEmptyCompletion(text7)) return true;
5586
5593
  const lower = text7.trim().toLowerCase();
5587
5594
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
@@ -5658,7 +5665,7 @@ function humanizeAgentFailDetail(detail) {
5658
5665
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
5659
5666
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
5660
5667
  }
5661
- if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
5668
+ if (/connection stalled/.test(lower) || looksLikeCursorStreamFailure(raw)) {
5662
5669
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
5663
5670
  }
5664
5671
  if (looksLikeBrightsyEmptyCompletion(raw)) {
@@ -27699,7 +27706,7 @@ function formatWorktreeReminder() {
27699
27706
  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).";
27700
27707
  }
27701
27708
  function formatClaudeSubagentReminder() {
27702
- 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.';
27709
+ 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.';
27703
27710
  }
27704
27711
  function claudeSubagentTurnReminder(thread) {
27705
27712
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -34407,10 +34414,9 @@ function parsePhoneMcpUi(value, op, chatId) {
34407
34414
  }
34408
34415
  function phoneMcpToolPayload(parts, tool, callId) {
34409
34416
  if (!parts) return null;
34410
- const matches = parts.filter(
34411
- (part2) => part2.type === "tool" && part2.name === tool && !part2.parentId
34417
+ const part = parts.find(
34418
+ (row2) => row2.type === "tool" && row2.id === callId && row2.name === tool && !row2.parentId
34412
34419
  );
34413
- const part = matches.find((row2) => row2.id === callId) ?? matches[matches.length - 1];
34414
34420
  if (!part) return null;
34415
34421
  return {
34416
34422
  input: part.input ?? {},
@@ -34418,6 +34424,15 @@ function phoneMcpToolPayload(parts, tool, callId) {
34418
34424
  isError: part.status === "error"
34419
34425
  };
34420
34426
  }
34427
+ function phoneMcpToolPayloadFromMessages(messages, tool, callId) {
34428
+ for (let i = messages.length - 1; i >= 0; i--) {
34429
+ const message = messages[i];
34430
+ if (message?.role !== "agent") continue;
34431
+ const payload = phoneMcpToolPayload(message.parts, tool, callId);
34432
+ if (payload) return payload;
34433
+ }
34434
+ return null;
34435
+ }
34421
34436
 
34422
34437
  // src/remote/phone-stream.ts
34423
34438
  init_message_parts();
@@ -34477,8 +34492,8 @@ function listPhoneMessages(thread) {
34477
34492
  const names = (message.attachments ?? []).map((item) => item.name.trim()).filter(Boolean);
34478
34493
  const visible = [phoneVisibleText(message.role, message.text), names.length ? names.join(", ") : ""].filter(Boolean).join("\n");
34479
34494
  const text7 = message.role === "agent" ? phoneArtifactText(visible, message.parts, MESSAGE_LIMIT) : clip(visible, MESSAGE_LIMIT);
34480
- if (!text7) continue;
34481
34495
  const apps = message.role === "agent" ? phoneMcpApps(thread.agent, message.parts) : [];
34496
+ if (!text7 && apps.length === 0) continue;
34482
34497
  out.push({ role: message.role, text: text7, ...apps.length ? { apps } : {} });
34483
34498
  }
34484
34499
  const live = withLiveTail(thread, out);
@@ -35240,8 +35255,7 @@ function dispatchPhoneMcpUi(cmd, opts) {
35240
35255
  );
35241
35256
  return true;
35242
35257
  }
35243
- const agentMessage = [...thread.messages].reverse().find((message) => message.role === "agent");
35244
- const payload = phoneMcpToolPayload(agentMessage?.parts, cmd.tool, cmd.callId);
35258
+ const payload = phoneMcpToolPayloadFromMessages(thread.messages, cmd.tool, cmd.callId);
35245
35259
  void getMcpUiHost().viewForClaudeTool(cmd.tool, projectDirs).then((view) => {
35246
35260
  if (!view || view.html.length > PHONE_HTML_CAP) {
35247
35261
  emitControl(opts, { op: "mcp-ui", chatId, reqId, view: null });
package/dist/index.d.cts CHANGED
@@ -4455,8 +4455,10 @@ declare function formatWorktreeReminder(): string;
4455
4455
  * Claude Code's default `worktree.bgIsolation` is `worktree`: a second
4456
4456
  * write-capable agent in this checkout is blocked, and the tool text tells
4457
4457
  * the parent to re-spawn with `isolation: "worktree"` (or to set
4458
- * `bgIsolation: none`). Models stop and ask the user. The parent should
4459
- * partition the files and continue. Do not forbid `ask_user` in general —
4458
+ * `bgIsolation: none`). Models stop and ask the user. That note is another
4459
+ * worker in this same chat (or one Claude still has marked running). The
4460
+ * parent partitions the files, continues, and does not report the note.
4461
+ * Do not forbid `ask_user` in general —
4460
4462
  * only asking whether to isolate. Cursor, Codex, and OpenCode subagents
4461
4463
  * share this checkout (no isolation worktree / `bgIsolation` block), and
4462
4464
  * Brightsy has no nested writer, so they do not get this reminder.
package/dist/index.d.ts CHANGED
@@ -4455,8 +4455,10 @@ declare function formatWorktreeReminder(): string;
4455
4455
  * Claude Code's default `worktree.bgIsolation` is `worktree`: a second
4456
4456
  * write-capable agent in this checkout is blocked, and the tool text tells
4457
4457
  * the parent to re-spawn with `isolation: "worktree"` (or to set
4458
- * `bgIsolation: none`). Models stop and ask the user. The parent should
4459
- * partition the files and continue. Do not forbid `ask_user` in general —
4458
+ * `bgIsolation: none`). Models stop and ask the user. That note is another
4459
+ * worker in this same chat (or one Claude still has marked running). The
4460
+ * parent partitions the files, continues, and does not report the note.
4461
+ * Do not forbid `ask_user` in general —
4460
4462
  * only asking whether to isolate. Cursor, Codex, and OpenCode subagents
4461
4463
  * share this checkout (no isolation worktree / `bgIsolation` block), and
4462
4464
  * Brightsy has no nested writer, so they do not get this reminder.
package/dist/index.js CHANGED
@@ -201,7 +201,7 @@ import {
201
201
  worktreeCleanupSettings,
202
202
  writeAgentBlock,
203
203
  writeWorktreeFile
204
- } from "./chunk-GXFOZ5XL.js";
204
+ } from "./chunk-M6PAT6HG.js";
205
205
  import {
206
206
  LEGACY_PLAN_FILE_REL,
207
207
  PLAN_FILE_NAME,
@@ -295,7 +295,7 @@ import {
295
295
  userMcpNamesToDisable,
296
296
  worktreeMcpToolNames,
297
297
  writeInjectedMcpConfig
298
- } from "./chunk-G2L3CR3A.js";
298
+ } from "./chunk-FMSLOWKR.js";
299
299
  import {
300
300
  brightsyMcpServerName,
301
301
  connectBrightsyTeam,
@@ -424,7 +424,7 @@ import {
424
424
  workspaceAgentChatSummaries,
425
425
  worktreeRunPortsPath,
426
426
  wrapReviewSkillMarkdown
427
- } from "./chunk-CTFBZZGZ.js";
427
+ } from "./chunk-VBVSJUM2.js";
428
428
  import {
429
429
  STDERR_EVENT_MAX_CHARS,
430
430
  STDOUT_EVENT_MAX_CHARS,
@@ -466,7 +466,7 @@ import {
466
466
  visibleToolRowDetail,
467
467
  withEventParentId,
468
468
  withEventsParentId
469
- } from "./chunk-F36QRDM4.js";
469
+ } from "./chunk-7Q327QF2.js";
470
470
  import "./chunk-7H655NEW.js";
471
471
  import {
472
472
  ABLETIME_OAUTH_CALLBACK_PATH,
@@ -783,7 +783,7 @@ import {
783
783
  import {
784
784
  clipMcpToolResult,
785
785
  cursorToolErrorText
786
- } from "./chunk-YJBUYGPN.js";
786
+ } from "./chunk-NCHOTN3C.js";
787
787
  import "./chunk-AXQ4ZWHK.js";
788
788
  import {
789
789
  AGENT_RUNNER_MAX_OLD_SPACE_MB,
@@ -799,8 +799,8 @@ import {
799
799
  trustSystemCertificates,
800
800
  turnCostUsdFromCursorUsage,
801
801
  withMaxOldSpaceSize
802
- } from "./chunk-MMVMQ34G.js";
803
- import "./chunk-HO5R3ZRH.js";
802
+ } from "./chunk-UA3YKQ3D.js";
803
+ import "./chunk-5XNT6BCG.js";
804
804
  import {
805
805
  ATTACHMENTS_DIR,
806
806
  CLI_DIR,
@@ -2468,10 +2468,9 @@ function parsePhoneMcpUi(value, op, chatId) {
2468
2468
  }
2469
2469
  function phoneMcpToolPayload(parts, tool, callId) {
2470
2470
  if (!parts) return null;
2471
- const matches = parts.filter(
2472
- (part2) => part2.type === "tool" && part2.name === tool && !part2.parentId
2471
+ const part = parts.find(
2472
+ (row2) => row2.type === "tool" && row2.id === callId && row2.name === tool && !row2.parentId
2473
2473
  );
2474
- const part = matches.find((row2) => row2.id === callId) ?? matches[matches.length - 1];
2475
2474
  if (!part) return null;
2476
2475
  return {
2477
2476
  input: part.input ?? {},
@@ -2479,6 +2478,15 @@ function phoneMcpToolPayload(parts, tool, callId) {
2479
2478
  isError: part.status === "error"
2480
2479
  };
2481
2480
  }
2481
+ function phoneMcpToolPayloadFromMessages(messages, tool, callId) {
2482
+ for (let i = messages.length - 1; i >= 0; i--) {
2483
+ const message = messages[i];
2484
+ if (message?.role !== "agent") continue;
2485
+ const payload = phoneMcpToolPayload(message.parts, tool, callId);
2486
+ if (payload) return payload;
2487
+ }
2488
+ return null;
2489
+ }
2482
2490
 
2483
2491
  // src/remote/phone-stream.ts
2484
2492
  var PHONE_STREAM_FLUSH_MS = 80;
@@ -2537,8 +2545,8 @@ function listPhoneMessages(thread) {
2537
2545
  const names = (message.attachments ?? []).map((item) => item.name.trim()).filter(Boolean);
2538
2546
  const visible = [phoneVisibleText(message.role, message.text), names.length ? names.join(", ") : ""].filter(Boolean).join("\n");
2539
2547
  const text5 = message.role === "agent" ? phoneArtifactText(visible, message.parts, MESSAGE_LIMIT) : clip(visible, MESSAGE_LIMIT);
2540
- if (!text5) continue;
2541
2548
  const apps = message.role === "agent" ? phoneMcpApps(thread.agent, message.parts) : [];
2549
+ if (!text5 && apps.length === 0) continue;
2542
2550
  out.push({ role: message.role, text: text5, ...apps.length ? { apps } : {} });
2543
2551
  }
2544
2552
  const live = withLiveTail(thread, out);
@@ -3291,8 +3299,7 @@ function dispatchPhoneMcpUi(cmd, opts) {
3291
3299
  );
3292
3300
  return true;
3293
3301
  }
3294
- const agentMessage = [...thread.messages].reverse().find((message) => message.role === "agent");
3295
- const payload = phoneMcpToolPayload(agentMessage?.parts, cmd.tool, cmd.callId);
3302
+ const payload = phoneMcpToolPayloadFromMessages(thread.messages, cmd.tool, cmd.callId);
3296
3303
  void getMcpUiHost().viewForClaudeTool(cmd.tool, projectDirs).then((view) => {
3297
3304
  if (!view || view.html.length > PHONE_HTML_CAP) {
3298
3305
  emitControl(opts, { op: "mcp-ui", chatId, reqId, view: null });
@@ -8884,7 +8891,7 @@ async function startHeadlessHost(opts) {
8884
8891
  };
8885
8892
  const refreshRunning = async () => {
8886
8893
  try {
8887
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SJU5LYAO.js");
8894
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-T76JRIIN.js");
8888
8895
  agentsRunning = getOrchestrator2().getRuntime().running;
8889
8896
  } catch {
8890
8897
  agentsRunning = 0;
@@ -10816,7 +10823,7 @@ async function handleJobStop(params, _ctx) {
10816
10823
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
10817
10824
  }
10818
10825
  async function defaultTurnWait(ref, timeoutMs) {
10819
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SJU5LYAO.js");
10826
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-T76JRIIN.js");
10820
10827
  const orch = getOrchestrator2();
10821
10828
  const thread = await orch.waitForTurn(ref, timeoutMs, {
10822
10829
  resolveIfStillRunning: true
@@ -14189,7 +14196,7 @@ async function continueSourceThread(threadId, prompt) {
14189
14196
  await continueOnReply(threadId, prompt);
14190
14197
  return;
14191
14198
  }
14192
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SJU5LYAO.js");
14199
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-T76JRIIN.js");
14193
14200
  await getOrchestrator2().send(threadId, prompt);
14194
14201
  } catch {
14195
14202
  }
@@ -784,6 +784,12 @@ var init_system_ca = __esm({
784
784
  function looksLikeCursorStreamCutOff(text7) {
785
785
  return CURSOR_STREAM_CUTOFF.test(text7.trim());
786
786
  }
787
+ function looksLikeCursorStreamIdleAfterTools(text7) {
788
+ return CURSOR_STREAM_IDLE_AFTER_TOOLS.test(text7.trim());
789
+ }
790
+ function looksLikeCursorStreamFailure(text7) {
791
+ return looksLikeCursorStreamCutOff(text7) || looksLikeCursorStreamIdleAfterTools(text7);
792
+ }
787
793
  function cursorStreamCutOffContinueLimit(detail) {
788
794
  if (looksLikeCursorStreamCutOff(detail) || /connection stalled/i.test(detail)) {
789
795
  return CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
@@ -795,11 +801,12 @@ function nextCrashContinueCount(detail, used) {
795
801
  if (used >= limit) return null;
796
802
  return used + 1;
797
803
  }
798
- var CURSOR_STREAM_CUTOFF, CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
804
+ var CURSOR_STREAM_CUTOFF, CURSOR_STREAM_IDLE_AFTER_TOOLS, CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT;
799
805
  var init_cursor_stream_cutoff = __esm({
800
806
  "src/agents/cursor-stream-cutoff.ts"() {
801
807
  "use strict";
802
808
  CURSOR_STREAM_CUTOFF = /nghttp2|premature close|\[unavailable\]\s+error\b|http\/2 stream closed|\bcancel \(0x8\)|error code cancel\b/i;
809
+ CURSOR_STREAM_IDLE_AFTER_TOOLS = /stopped responding after a tool call/i;
803
810
  CURSOR_STREAM_CUTOFF_CONTINUE_LIMIT = 3;
804
811
  }
805
812
  });
@@ -1003,7 +1010,7 @@ function looksLikeRetryableRunnerCrash(text7) {
1003
1010
  if (looksLikeInvalidAgentSession(text7)) return false;
1004
1011
  if (looksLikeV8Oom(text7)) return false;
1005
1012
  if (looksLikeHugeToolResultDump(text7)) return true;
1006
- if (looksLikeCursorStreamCutOff(text7)) return true;
1013
+ if (looksLikeCursorStreamFailure(text7)) return true;
1007
1014
  if (looksLikeBrightsyEmptyCompletion(text7)) return true;
1008
1015
  const lower = text7.trim().toLowerCase();
1009
1016
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
@@ -1080,7 +1087,7 @@ function humanizeAgentFailDetail(detail) {
1080
1087
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
1081
1088
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
1082
1089
  }
1083
- if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
1090
+ if (/connection stalled/.test(lower) || looksLikeCursorStreamFailure(raw)) {
1084
1091
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
1085
1092
  }
1086
1093
  if (looksLikeBrightsyEmptyCompletion(raw)) {
@@ -24883,7 +24890,7 @@ function formatWorktreeReminder() {
24883
24890
  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).";
24884
24891
  }
24885
24892
  function formatClaudeSubagentReminder() {
24886
- 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.';
24893
+ 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.';
24887
24894
  }
24888
24895
  function claudeSubagentTurnReminder(thread) {
24889
24896
  if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
@@ -56,7 +56,7 @@ import {
56
56
  updateSchedule,
57
57
  workspaceTagTargetRef,
58
58
  writeAgentBlock
59
- } from "../chunk-T6TEANP5.js";
59
+ } from "../chunk-OU5V56OI.js";
60
60
  import "../chunk-LX5R5JOR.js";
61
61
  import {
62
62
  commentAbleTimeTask,
@@ -99,7 +99,7 @@ import {
99
99
  sideboardMcpProfile,
100
100
  trustSystemCertificates,
101
101
  worktreeMcpToolNames
102
- } from "../chunk-EZYZGDWU.js";
102
+ } from "../chunk-VXI7FFGO.js";
103
103
  import "../chunk-EGTCXNQM.js";
104
104
  import "../chunk-42JGPPVS.js";
105
105
  import "../chunk-PXMLMBMI.js";
@@ -116,14 +116,14 @@ import {
116
116
  waitForDetachedJob,
117
117
  waitForTurnToolResult,
118
118
  workspaceAgentChatSummaries
119
- } from "../chunk-HRPSFU4Q.js";
119
+ } from "../chunk-AILQRQ26.js";
120
120
  import {
121
121
  HUGE_TOOL_RESULT_SUMMARY,
122
122
  clipToolResultForStore,
123
123
  formatUnknownDetail,
124
124
  isInternalAgentStatusText,
125
125
  looksLikeHugeToolResultDump
126
- } from "../chunk-5XQ43YZU.js";
126
+ } from "../chunk-I5TESHDN.js";
127
127
  import {
128
128
  GLOBAL_WORKSPACE_ID,
129
129
  isCloudCoordinatorThread
@@ -11,18 +11,18 @@ import {
11
11
  resolveSendFollowUp,
12
12
  startOrchestration,
13
13
  waitForPidExit
14
- } from "./chunk-T6TEANP5.js";
14
+ } from "./chunk-OU5V56OI.js";
15
15
  import "./chunk-LX5R5JOR.js";
16
16
  import "./chunk-62ESCTJ4.js";
17
17
  import "./chunk-4GK32AYX.js";
18
18
  import "./chunk-DC3VZXMA.js";
19
- import "./chunk-EZYZGDWU.js";
19
+ import "./chunk-VXI7FFGO.js";
20
20
  import "./chunk-EGTCXNQM.js";
21
21
  import "./chunk-42JGPPVS.js";
22
22
  import "./chunk-PXMLMBMI.js";
23
23
  import "./chunk-2GPQZZLY.js";
24
- import "./chunk-HRPSFU4Q.js";
25
- import "./chunk-5XQ43YZU.js";
24
+ import "./chunk-AILQRQ26.js";
25
+ import "./chunk-I5TESHDN.js";
26
26
  import "./chunk-X3FSWWW2.js";
27
27
  import "./chunk-DLGGLV2M.js";
28
28
  import "./chunk-N2WW62LF.js";
@@ -9,12 +9,12 @@ import {
9
9
  resolveSendFollowUp,
10
10
  startOrchestration,
11
11
  waitForPidExit
12
- } from "./chunk-GXFOZ5XL.js";
12
+ } from "./chunk-M6PAT6HG.js";
13
13
  import "./chunk-PGPSIEMW.js";
14
- import "./chunk-G2L3CR3A.js";
14
+ import "./chunk-FMSLOWKR.js";
15
15
  import "./chunk-VEVFHO6K.js";
16
- import "./chunk-CTFBZZGZ.js";
17
- import "./chunk-F36QRDM4.js";
16
+ import "./chunk-VBVSJUM2.js";
17
+ import "./chunk-7Q327QF2.js";
18
18
  import "./chunk-7H655NEW.js";
19
19
  import "./chunk-WRZRX4Q7.js";
20
20
  import "./chunk-U4WACWBC.js";
@@ -26,8 +26,8 @@ import "./chunk-33KDPEK4.js";
26
26
  import "./chunk-JAT3JFOS.js";
27
27
  import "./chunk-F2PVRF3B.js";
28
28
  import "./chunk-AXQ4ZWHK.js";
29
- import "./chunk-MMVMQ34G.js";
30
- import "./chunk-HO5R3ZRH.js";
29
+ import "./chunk-UA3YKQ3D.js";
30
+ import "./chunk-5XNT6BCG.js";
31
31
  import "./chunk-6K64FJDD.js";
32
32
  import "./chunk-LG6RJBIY.js";
33
33
  import "./chunk-QVN2WOKJ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.308",
3
+ "version": "0.1.309",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",