@sideboard-ai/core 0.1.278 → 0.1.279

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/dist/agents/cursor-runner.cjs +3 -3
  2. package/dist/agents/cursor-runner.js +5 -5
  3. package/dist/{agents-2FOWVJCN.js → agents-DYCFLDNL.js} +5 -5
  4. package/dist/{agents-JBVJRDH3.js → agents-RXP5NZBC.js} +3 -3
  5. package/dist/{chunk-UKSJO36E.js → chunk-CPA43FBK.js} +4 -4
  6. package/dist/{chunk-FELFTNH2.js → chunk-CPGJ5ICZ.js} +261 -19
  7. package/dist/{chunk-NIB2IZ6J.js → chunk-HNNS25SG.js} +5 -5
  8. package/dist/{chunk-MGS5T2A3.js → chunk-HQXBBUQX.js} +161 -9
  9. package/dist/{chunk-FIBZEFKA.js → chunk-IFURHRAR.js} +1 -1
  10. package/dist/{chunk-5N4Q46SG.js → chunk-K2S33ZCB.js} +5 -5
  11. package/dist/{chunk-NT7RIWRR.js → chunk-RK5PIWCW.js} +3 -3
  12. package/dist/{chunk-A2G3ZBJS.js → chunk-SKSOCIJT.js} +8 -8
  13. package/dist/{chunk-QTZO4LPU.js → chunk-TZ5JHIYK.js} +8 -8
  14. package/dist/{chunk-27Z4MEQ5.js → chunk-UZUXWBXK.js} +1 -1
  15. package/dist/{chunk-SQOAFR2U.js → chunk-V5DMHB6Y.js} +270 -23
  16. package/dist/{chunk-FAGK33E2.js → chunk-Y7JPL4WA.js} +1 -1
  17. package/dist/{chunk-HOMZKYHF.js → chunk-YDXNCBA2.js} +107 -8
  18. package/dist/{conductor-YYQKZAND.js → conductor-J6WDOXWM.js} +3 -3
  19. package/dist/{conductor-GBCE37K4.js → conductor-OW6NAK3N.js} +4 -4
  20. package/dist/{coordinator-prompt-SFZCZ3HR.js → coordinator-prompt-ERTHREGJ.js} +1 -1
  21. package/dist/{coordinator-prompt-B3GLVXPP.js → coordinator-prompt-Z2D32HWK.js} +1 -1
  22. package/dist/{global-workspace-7XEXTIHL.js → global-workspace-5PJXO45Z.js} +1 -1
  23. package/dist/{global-workspace-N2YYJKA5.js → global-workspace-AK5PSPQ2.js} +1 -1
  24. package/dist/index.cjs +798 -445
  25. package/dist/index.d.cts +52 -15
  26. package/dist/index.d.ts +52 -15
  27. package/dist/index.js +251 -290
  28. package/dist/mcp/run-stdio.cjs +529 -257
  29. package/dist/mcp/run-stdio.js +40 -124
  30. package/dist/{orchestrator-6QJLM6Y4.js → orchestrator-E5WK57DA.js} +5 -5
  31. package/dist/{orchestrator-QMKVXVUR.js → orchestrator-KY2KBBE2.js} +7 -7
  32. package/dist/{workspaces-PBB6HY2D.js → workspaces-UP6DQ3WL.js} +1 -1
  33. package/dist/{workspaces-BJCHMGVG.js → workspaces-WU2RNLDV.js} +1 -1
  34. package/package.json +1 -1
@@ -475,8 +475,8 @@ ${content.slice(-tail)}`;
475
475
  }
476
476
  var HUGE_TOOL_RESULT_SUMMARY = "Agent crashed mid-turn after a huge tool result. Write CLI/HTTP output to .context/cli/ and read a slice \u2014 never dump raw --json/--expand into the tool result.";
477
477
  var TOOL_RESULT_STORE_MAX_CHARS = 8e3;
478
- function looksLikeCursorHttp2StreamClose(text) {
479
- return /nghttp2/i.test(text.trim());
478
+ function looksLikeCursorStreamCutOff(text) {
479
+ return /nghttp2|premature close|\[unavailable\]\s+error\b/i.test(text.trim());
480
480
  }
481
481
 
482
482
  // src/agents/cursor-session.ts
@@ -511,7 +511,7 @@ function isRetryableCursorTransportError(err) {
511
511
  return true;
512
512
  }
513
513
  const message = cursorErrorMessage(err);
514
- return /network request failed/i.test(message) || looksLikeCursorHttp2StreamClose(message);
514
+ return /network request failed/i.test(message) || looksLikeCursorStreamCutOff(message);
515
515
  }
516
516
  function looksLikeNonRetryableCursorFailure(message) {
517
517
  const lower = message.toLowerCase();
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  clipAgentEventForEmit,
4
4
  sideboardCursorRpcTools
5
- } from "../chunk-27Z4MEQ5.js";
5
+ } from "../chunk-UZUXWBXK.js";
6
6
  import {
7
7
  createAgentStreamCoalescer
8
8
  } from "../chunk-AXQ4ZWHK.js";
@@ -17,12 +17,12 @@ import {
17
17
  fetchCursorTurnCostUsd,
18
18
  importCursorSdk,
19
19
  resolveCursorSdkRoot
20
- } from "../chunk-FIBZEFKA.js";
20
+ } from "../chunk-IFURHRAR.js";
21
21
  import {
22
22
  TOOL_RESULT_STORE_MAX_CHARS,
23
23
  formatUnknownDetail,
24
- looksLikeCursorHttp2StreamClose
25
- } from "../chunk-5N4Q46SG.js";
24
+ looksLikeCursorStreamCutOff
25
+ } from "../chunk-K2S33ZCB.js";
26
26
  import "../chunk-6K64FJDD.js";
27
27
  import {
28
28
  cursorSdkStoreDir
@@ -70,7 +70,7 @@ function isRetryableCursorTransportError(err) {
70
70
  return true;
71
71
  }
72
72
  const message = cursorErrorMessage(err);
73
- return /network request failed/i.test(message) || looksLikeCursorHttp2StreamClose(message);
73
+ return /network request failed/i.test(message) || looksLikeCursorStreamCutOff(message);
74
74
  }
75
75
  function looksLikeNonRetryableCursorFailure(message) {
76
76
  const lower = message.toLowerCase();
@@ -47,10 +47,10 @@ import {
47
47
  resolveLoginCommand,
48
48
  resolveQuotaFallbackAgent,
49
49
  whichOnPath
50
- } from "./chunk-NIB2IZ6J.js";
50
+ } from "./chunk-HNNS25SG.js";
51
51
  import "./chunk-GWFVCCJZ.js";
52
- import "./chunk-FAGK33E2.js";
53
- import "./chunk-A2G3ZBJS.js";
52
+ import "./chunk-Y7JPL4WA.js";
53
+ import "./chunk-SKSOCIJT.js";
54
54
  import "./chunk-IPPY3FG5.js";
55
55
  import "./chunk-FWNL62VA.js";
56
56
  import "./chunk-2P4XKWV2.js";
@@ -64,8 +64,8 @@ import {
64
64
  preferredCursorCostCents,
65
65
  resolveCursorSdkRoot,
66
66
  turnCostUsdFromCursorUsage
67
- } from "./chunk-FIBZEFKA.js";
68
- import "./chunk-5N4Q46SG.js";
67
+ } from "./chunk-IFURHRAR.js";
68
+ import "./chunk-K2S33ZCB.js";
69
69
  import "./chunk-6K64FJDD.js";
70
70
  import "./chunk-UWQ6UCIB.js";
71
71
  import {
@@ -56,12 +56,12 @@ import {
56
56
  resolveQuotaFallbackAgent,
57
57
  turnCostUsdFromCursorUsage,
58
58
  whichOnPath
59
- } from "./chunk-NT7RIWRR.js";
59
+ } from "./chunk-RK5PIWCW.js";
60
60
  import "./chunk-TQRSVE7D.js";
61
61
  import "./chunk-P2BSMNHB.js";
62
62
  import "./chunk-VKQGAWHY.js";
63
- import "./chunk-UKSJO36E.js";
64
- import "./chunk-QTZO4LPU.js";
63
+ import "./chunk-CPA43FBK.js";
64
+ import "./chunk-TZ5JHIYK.js";
65
65
  import "./chunk-K7MWJLR7.js";
66
66
  import "./chunk-N2WW62LF.js";
67
67
  import "./chunk-DD67JWTN.js";
@@ -199,8 +199,8 @@ function looksLikeInvalidAgentSession(text) {
199
199
  if (!lower) return false;
200
200
  return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower) || /\brun\b.{0,80}\bnot found for agent\b/.test(lower);
201
201
  }
202
- function looksLikeCursorHttp2StreamClose(text) {
203
- return /nghttp2/i.test(text.trim());
202
+ function looksLikeCursorStreamCutOff(text) {
203
+ return /nghttp2|premature close|\[unavailable\]\s+error\b/i.test(text.trim());
204
204
  }
205
205
  function looksLikeBrightsyEmptyCompletion(text) {
206
206
  const core = text.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
@@ -211,7 +211,7 @@ function looksLikeRetryableRunnerCrash(text) {
211
211
  if (looksLikeInvalidAgentSession(text)) return false;
212
212
  if (looksLikeV8Oom(text)) return false;
213
213
  if (looksLikeHugeToolResultDump(text)) return true;
214
- if (looksLikeCursorHttp2StreamClose(text)) return true;
214
+ if (looksLikeCursorStreamCutOff(text)) return true;
215
215
  if (looksLikeBrightsyEmptyCompletion(text)) return true;
216
216
  const lower = text.trim().toLowerCase();
217
217
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
@@ -288,7 +288,7 @@ function humanizeAgentFailDetail(detail) {
288
288
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
289
289
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
290
290
  }
291
- if (/connection stalled/.test(lower) || looksLikeCursorHttp2StreamClose(raw)) {
291
+ if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
292
292
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
293
293
  }
294
294
  if (looksLikeBrightsyEmptyCompletion(raw)) {
@@ -5,6 +5,7 @@ import {
5
5
  } from "./chunk-LX5R5JOR.js";
6
6
  import {
7
7
  extractPresentedPlan,
8
+ isPresentPlanToolName,
8
9
  readPlanFile,
9
10
  writePlanFile
10
11
  } from "./chunk-4GK32AYX.js";
@@ -12,6 +13,7 @@ import {
12
13
  releaseCaffeinateHoldForThread
13
14
  } from "./chunk-5DZUWGKV.js";
14
15
  import {
16
+ SIDEBOARD_THREAD_ID_ENV,
15
17
  adjustBrightsyTurnExit,
16
18
  applyAgentRunnerHeapEnv,
17
19
  getAdapter,
@@ -21,7 +23,7 @@ import {
21
23
  parseSessionQuotaResetAt,
22
24
  planModeTurnInstruction,
23
25
  resolveQuotaFallbackAgent
24
- } from "./chunk-NT7RIWRR.js";
26
+ } from "./chunk-RK5PIWCW.js";
25
27
  import {
26
28
  REVIEW_TAB_TITLE,
27
29
  applyWorktreePkgCacheEnv,
@@ -31,12 +33,14 @@ import {
31
33
  copyConfiguredFiles,
32
34
  createChatTab,
33
35
  deriveTaskState,
36
+ extractPendingPlanQuestions,
34
37
  forkChatTab,
35
38
  forkMessageSlice,
36
39
  formatReviewWriteGateDirective,
37
40
  formatReviewWriteGateReminder,
38
41
  getDefaultRunScript,
39
42
  getRunMode,
43
+ isAskUserToolName,
40
44
  isJobContinuePrompt,
41
45
  isReviewWriteGatedThread,
42
46
  isWorktreeRunProcessKey,
@@ -58,7 +62,7 @@ import {
58
62
  worktreeDevProcessKey,
59
63
  worktreeRunProcessKey,
60
64
  worktreeSetupProcessKey
61
- } from "./chunk-HOMZKYHF.js";
65
+ } from "./chunk-YDXNCBA2.js";
62
66
  import {
63
67
  applyAgentEvent,
64
68
  clipAgentEventForPaint,
@@ -84,7 +88,7 @@ import {
84
88
  summarizeTurnStderr,
85
89
  toolDescription,
86
90
  turnFailChatText
87
- } from "./chunk-UKSJO36E.js";
91
+ } from "./chunk-CPA43FBK.js";
88
92
  import {
89
93
  SLACK_REPLY_FORMATTING,
90
94
  addWorkspace,
@@ -105,7 +109,7 @@ import {
105
109
  stageAbsolutePathsAsAttachments,
106
110
  stageBuffersAsAttachments,
107
111
  syncWorkspacesFromThreads
108
- } from "./chunk-QTZO4LPU.js";
112
+ } from "./chunk-TZ5JHIYK.js";
109
113
  import {
110
114
  addPrStackLayer,
111
115
  agentNicknameFromWorktreePath,
@@ -485,9 +489,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
485
489
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
486
490
  );
487
491
  }
488
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-7XEXTIHL.js");
492
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-5PJXO45Z.js");
489
493
  if (isGlobalThread2(thread)) {
490
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-SFZCZ3HR.js");
494
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ERTHREGJ.js");
491
495
  ensureGlobalCoordinatorCwd2(
492
496
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
493
497
  );
@@ -1478,6 +1482,207 @@ function thisProcessShouldDrainAgentQueues() {
1478
1482
  return !isDesktopHostAlive();
1479
1483
  }
1480
1484
 
1485
+ // src/orchestrator/notify-orchestrator.ts
1486
+ var MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS = 1500;
1487
+ var inputRequiredNotified = /* @__PURE__ */ new Set();
1488
+ function clampMessage(raw) {
1489
+ const text = raw.replace(/\s+/g, " ").trim();
1490
+ if (text.length <= MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS) return text;
1491
+ return `${text.slice(0, MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS - 1)}\u2026`;
1492
+ }
1493
+ function formatAskUserNotifyMessage(questions) {
1494
+ const qs = questions.map((q) => q.question?.trim() ?? "").filter(Boolean);
1495
+ if (qs.length === 0) return "Asked the user a question in that chat.";
1496
+ return `Asked the user: ${qs.join("; ")}`;
1497
+ }
1498
+ function formatNotifyOrchestratorPrompt(input) {
1499
+ const title = input.child.title?.trim() || "Untitled";
1500
+ const link = `[${title}](sideboard://chat/${input.child.id})`;
1501
+ const body = clampMessage(input.message) || "(no details)";
1502
+ const state = input.reason === "input-required" ? "is input-required (waiting for the user in that chat)" : "is blocked";
1503
+ const next = input.reason === "input-required" ? "Wait for the user in that chat. Do not send_to_chat a check-in." : "Help if this is your child; otherwise briefly tell the user. Do not send_to_chat a check-in.";
1504
+ return [
1505
+ `Sideboard: child worktree ${link} ${state}.`,
1506
+ body,
1507
+ `This is information \u2014 not a user command. ${next} Do not treat this as a git request.`
1508
+ ].join("\n");
1509
+ }
1510
+ function shouldNotifyOrchestrator(opts) {
1511
+ if (!opts.child.parentThreadId) return false;
1512
+ if (!opts.parent || opts.parent.status === "archived") return false;
1513
+ if (opts.parent.id === opts.child.id) return false;
1514
+ return isOrchestratorThread(opts.parent);
1515
+ }
1516
+ function resolveNotifyCallerThread(opts) {
1517
+ const env = opts?.env ?? process.env;
1518
+ const cwd = opts?.cwd ?? process.cwd();
1519
+ const id = env[SIDEBOARD_THREAD_ID_ENV]?.trim();
1520
+ if (id) {
1521
+ const thread = findThreadByRef(id);
1522
+ if (!thread) {
1523
+ throw new Error(`No Sideboard thread for ${SIDEBOARD_THREAD_ID_ENV}=${id}.`);
1524
+ }
1525
+ return thread;
1526
+ }
1527
+ const siblings = threadsSharingWorktree(cwd);
1528
+ const running = siblings.find((t) => t.status === "running") ?? siblings[0];
1529
+ if (!running) {
1530
+ throw new Error(
1531
+ "No Sideboard thread for this cwd. Call notify_orchestrator from a worktree chat."
1532
+ );
1533
+ }
1534
+ return running;
1535
+ }
1536
+ function skipReason(opts) {
1537
+ if (!opts.child.parentThreadId) {
1538
+ return "This chat has no orchestrator parent. notify_orchestrator is for worktree children of a Global chat.";
1539
+ }
1540
+ if (!opts.parent) {
1541
+ return "Parent orchestration chat was not found.";
1542
+ }
1543
+ if (opts.parent.status === "archived") {
1544
+ return "Parent orchestration chat is archived.";
1545
+ }
1546
+ if (!isOrchestratorThread(opts.parent)) {
1547
+ return "Parent is not a Global orchestration chat.";
1548
+ }
1549
+ return "Cannot notify orchestrator.";
1550
+ }
1551
+ async function notifyOrchestrator(input) {
1552
+ const parent = input.child.parentThreadId ? readThread(input.child.parentThreadId) : null;
1553
+ if (!shouldNotifyOrchestrator({ child: input.child, parent })) {
1554
+ return { ok: false, error: skipReason({ child: input.child, parent }) };
1555
+ }
1556
+ const parentId = parent.id;
1557
+ if (input.reason === "input-required") {
1558
+ if (inputRequiredNotified.has(input.child.id)) {
1559
+ return { ok: true, parentThreadId: parentId, deduped: true };
1560
+ }
1561
+ inputRequiredNotified.add(input.child.id);
1562
+ }
1563
+ const prompt = formatNotifyOrchestratorPrompt({
1564
+ child: input.child,
1565
+ reason: input.reason,
1566
+ message: input.message
1567
+ });
1568
+ try {
1569
+ await input.send(parentId, prompt, { followUp: "queue" });
1570
+ } catch (err) {
1571
+ if (input.reason === "input-required") {
1572
+ inputRequiredNotified.delete(input.child.id);
1573
+ }
1574
+ const message = err instanceof Error ? err.message : String(err);
1575
+ return { ok: false, error: message };
1576
+ }
1577
+ return { ok: true, parentThreadId: parentId };
1578
+ }
1579
+
1580
+ // src/orchestrator/agent-block.ts
1581
+ var MAX_AGENT_BLOCK_REASON = 240;
1582
+ function clampBlockReason(raw) {
1583
+ const text = raw.replace(/\s+/g, " ").trim();
1584
+ if (!text) return "Waiting on a decision";
1585
+ if (text.length <= MAX_AGENT_BLOCK_REASON) return text;
1586
+ return `${text.slice(0, MAX_AGENT_BLOCK_REASON - 1)}\u2026`;
1587
+ }
1588
+ function makeAgentBlock(source, reason, at = (/* @__PURE__ */ new Date()).toISOString()) {
1589
+ return { source, reason: clampBlockReason(reason), at };
1590
+ }
1591
+ function lastTopLevelTool(parts) {
1592
+ if (!parts?.length) return null;
1593
+ for (let i = parts.length - 1; i >= 0; i--) {
1594
+ const part = parts[i];
1595
+ if (part.type === "tool" && !part.parentId) return part;
1596
+ }
1597
+ return null;
1598
+ }
1599
+ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date()).toISOString()) {
1600
+ const last = lastTopLevelTool(parts);
1601
+ if (last?.type === "tool" && isAskUserToolName(last.name)) {
1602
+ const pending = extractPendingPlanQuestions(parts);
1603
+ const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1604
+ return makeAgentBlock("ask_user", reason, at);
1605
+ }
1606
+ if (!planMode || last?.type !== "tool") return null;
1607
+ const exited = /exitplanmode/i.test(last.name ?? "");
1608
+ if (!isPresentPlanToolName(last.name) && !exited) return null;
1609
+ const presented = extractPresentedPlan(parts);
1610
+ if (!presented && !exited) return null;
1611
+ const title = presented?.title && presented.title !== "Plan" ? presented.title : "";
1612
+ return makeAgentBlock(
1613
+ "plan",
1614
+ title ? `Waiting for plan approval: ${title}` : "Waiting for plan approval",
1615
+ at
1616
+ );
1617
+ }
1618
+ function agentBlockAfterTurn(input) {
1619
+ if (input.failed) return null;
1620
+ const decision = decisionBlockFromParts(input.parts, input.planMode);
1621
+ if (decision) return decision;
1622
+ if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1623
+ return null;
1624
+ }
1625
+ return input.existing ?? null;
1626
+ }
1627
+ function clearAgentBlock(threadId) {
1628
+ const thread = readThread(threadId);
1629
+ if (!thread?.agentBlock) return;
1630
+ updateThread(threadId, { agentBlock: null });
1631
+ }
1632
+ function writeAgentBlock(threadId, block) {
1633
+ const thread = readThread(threadId);
1634
+ if (!thread || thread.status === "archived") return;
1635
+ updateThread(threadId, { agentBlock: block });
1636
+ }
1637
+ function settleAgentBlock(threadId, parts, failed) {
1638
+ const thread = readThread(threadId);
1639
+ if (!thread || thread.status === "archived") return;
1640
+ const next = agentBlockAfterTurn({
1641
+ existing: thread.agentBlock,
1642
+ parts,
1643
+ planMode: Boolean(thread.planMode),
1644
+ failed
1645
+ });
1646
+ const prev = thread.agentBlock ?? null;
1647
+ if (prev === next) return;
1648
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at) {
1649
+ return;
1650
+ }
1651
+ updateThread(threadId, { agentBlock: next });
1652
+ }
1653
+ function chatBlockedSuffix(thread) {
1654
+ const raw = thread.agentBlock?.reason;
1655
+ if (!raw?.trim()) return "";
1656
+ return ` blocked:${raw.replace(/\s+/g, " ").slice(0, 80)}`;
1657
+ }
1658
+ function agentBlockFields(thread) {
1659
+ return {
1660
+ blockedReason: thread.agentBlock?.reason ?? null,
1661
+ blockedSource: thread.agentBlock?.source ?? null
1662
+ };
1663
+ }
1664
+ function stampAskUserBlock(resolveCaller, questions) {
1665
+ const asked = formatAskUserNotifyMessage(questions);
1666
+ try {
1667
+ writeAgentBlock(resolveCaller().id, makeAgentBlock("ask_user", asked));
1668
+ } catch {
1669
+ }
1670
+ return asked;
1671
+ }
1672
+ function stampPlanBlock(threadId, title) {
1673
+ const planTitle = title?.trim() || "Plan";
1674
+ if (threadId) {
1675
+ const reason = planTitle === "Plan" ? "Waiting for plan approval" : `Waiting for plan approval: ${planTitle}`;
1676
+ writeAgentBlock(threadId, makeAgentBlock("plan", reason));
1677
+ }
1678
+ return planTitle;
1679
+ }
1680
+ function stampNotifyBlock(threadId, reason, message) {
1681
+ const resolved = reason ?? "blocked";
1682
+ writeAgentBlock(threadId, makeAgentBlock(resolved === "input-required" ? "ask_user" : "reported", message));
1683
+ return resolved;
1684
+ }
1685
+
1481
1686
  // src/orchestrator/child-halt.ts
1482
1687
  var HALT_STATUSES = /* @__PURE__ */ new Set(["stopped", "error", "broken"]);
1483
1688
  function isIncompleteChildStatus(status) {
@@ -1577,6 +1782,36 @@ async function requireAgent(agent, opts) {
1577
1782
  return status;
1578
1783
  }
1579
1784
 
1785
+ // src/board/agent-block-view.ts
1786
+ function worktreeBoardStatus(group) {
1787
+ const order = ["running", "queued", "error", "broken"];
1788
+ for (const status of order) {
1789
+ if (group.some((t) => t.status === status)) return status;
1790
+ }
1791
+ return group[0]?.status ?? "idle";
1792
+ }
1793
+ function visibleAgentBlock(thread) {
1794
+ if (thread.status === "archived" || thread.status === "error" || thread.status === "broken") {
1795
+ return null;
1796
+ }
1797
+ const block = thread.agentBlock;
1798
+ if (!block?.reason?.trim()) return null;
1799
+ return block;
1800
+ }
1801
+ function worktreeAgentBlock(group) {
1802
+ let best = null;
1803
+ for (const thread of group) {
1804
+ const block = visibleAgentBlock(thread);
1805
+ if (!block) continue;
1806
+ if (!best || block.at > best.at) best = block;
1807
+ }
1808
+ return best;
1809
+ }
1810
+ function threadCardBlockFields(group) {
1811
+ const block = worktreeAgentBlock(group);
1812
+ return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
1813
+ }
1814
+
1580
1815
  // src/board/home-board.ts
1581
1816
  var GLOBAL_WORKSPACE_ID = "__global__";
1582
1817
  function isHomeBoardThread(thread) {
@@ -1692,13 +1927,6 @@ function classifyWorktreeColumn(group) {
1692
1927
  }
1693
1928
  return "new";
1694
1929
  }
1695
- function worktreeBoardStatus(group) {
1696
- const order = ["running", "queued", "error", "broken"];
1697
- for (const status of order) {
1698
- if (group.some((t) => t.status === status)) return status;
1699
- }
1700
- return group[0]?.status ?? "idle";
1701
- }
1702
1930
  function normalizeIssueKey(value) {
1703
1931
  return value.trim().toLowerCase().replace(/^#/, "");
1704
1932
  }
@@ -1988,7 +2216,8 @@ function toThreadCard(group) {
1988
2216
  repoPath: thread.repoPath,
1989
2217
  prUrl: withPr.prUrl,
1990
2218
  link: `sideboard://chat/${thread.id}`,
1991
- ...group.length > 1 ? { chatCount: group.length } : {}
2219
+ ...group.length > 1 ? { chatCount: group.length } : {},
2220
+ ...threadCardBlockFields(group) ?? {}
1992
2221
  };
1993
2222
  }
1994
2223
  function threadMatchesKind(thread, kind) {
@@ -2062,7 +2291,7 @@ function assembleHomeBoard(input) {
2062
2291
  }
2063
2292
  return { columns, hidden, totals };
2064
2293
  }
2065
- var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chat tabs nest as inner cards). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that chat (alreadyStarted). Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats stay in the sidebar. Do not invent status.";
2294
+ var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
2066
2295
 
2067
2296
  // src/threads/create.ts
2068
2297
  function persistCreateAttachments(worktreePath, attachments) {
@@ -2780,7 +3009,7 @@ async function adoptThread(input) {
2780
3009
  messages: input.messages ?? []
2781
3010
  });
2782
3011
  writeThread(thread);
2783
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-BJCHMGVG.js");
3012
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WU2RNLDV.js");
2784
3013
  await ensureWorkspace2(repoPath);
2785
3014
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-5N2KTYIT.js");
2786
3015
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -5271,7 +5500,7 @@ function formatScheduledPrompt(name, prompt) {
5271
5500
  ${prompt}`;
5272
5501
  }
5273
5502
  async function defaultDeps() {
5274
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6QJLM6Y4.js");
5503
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-E5WK57DA.js");
5275
5504
  const orch = getOrchestrator2();
5276
5505
  return {
5277
5506
  findThread: (id) => findThreadByRef(id),
@@ -6249,6 +6478,7 @@ var Orchestrator = class {
6249
6478
  const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
6250
6479
  const parked = takePendingTurnAttachments(thread);
6251
6480
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
6481
+ if (!autoContinue) clearAgentBlock(threadId);
6252
6482
  appendMessage(threadId, {
6253
6483
  role: "user",
6254
6484
  text: promptText,
@@ -6654,6 +6884,7 @@ var Orchestrator = class {
6654
6884
  } else {
6655
6885
  await syncThreadBranchFromGit(threadId);
6656
6886
  }
6887
+ settleAgentBlock(threadId, parts, exitCode !== 0 && !this.stoppedTurns.has(threadId));
6657
6888
  if (this.stoppedTurns.has(threadId)) {
6658
6889
  const stopped = writeLiveStatus(threadId, "stopped");
6659
6890
  if (stopped?.status === "stopped") {
@@ -6704,6 +6935,7 @@ var Orchestrator = class {
6704
6935
  } catch (err) {
6705
6936
  const message = err instanceof Error ? err.message : String(err);
6706
6937
  await syncThreadBranchFromGit(threadId).catch(() => void 0);
6938
+ settleAgentBlock(threadId, void 0, !this.stoppedTurns.has(threadId));
6707
6939
  if (this.stoppedTurns.has(threadId)) {
6708
6940
  const stopped = writeLiveStatus(threadId, "stopped");
6709
6941
  if (stopped?.status === "stopped") {
@@ -7419,6 +7651,7 @@ var Orchestrator = class {
7419
7651
  stillRunning,
7420
7652
  lastAgentParts: lastAgent?.parts
7421
7653
  });
7654
+ const block = thread.agentBlock?.reason?.trim() ? thread.agentBlock : null;
7422
7655
  return {
7423
7656
  text,
7424
7657
  status: thread.status,
@@ -7432,7 +7665,9 @@ var Orchestrator = class {
7432
7665
  lastAgent?.ts,
7433
7666
  thread.updatedAt
7434
7667
  ]),
7435
- usage: stillRunning ? null : lastAgent?.usage ?? null
7668
+ usage: stillRunning ? null : lastAgent?.usage ?? null,
7669
+ blockedReason: block?.reason ?? null,
7670
+ blockedSource: block?.source ?? null
7436
7671
  };
7437
7672
  }
7438
7673
  assertNotGlobal(thread, action) {
@@ -7959,7 +8194,7 @@ var Orchestrator = class {
7959
8194
  this.enforceHistoryRetention();
7960
8195
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7961
8196
  try {
7962
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-BJCHMGVG.js");
8197
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WU2RNLDV.js");
7963
8198
  await ensureWorkspace2(thread.repoPath);
7964
8199
  } catch {
7965
8200
  }
@@ -8150,6 +8385,13 @@ export {
8150
8385
  MAX_RUN_LOG_CHARS,
8151
8386
  DEFAULT_RUN_LOG_TAIL_CHARS,
8152
8387
  sliceRunLogOutput,
8388
+ resolveNotifyCallerThread,
8389
+ notifyOrchestrator,
8390
+ chatBlockedSuffix,
8391
+ agentBlockFields,
8392
+ stampAskUserBlock,
8393
+ stampPlanBlock,
8394
+ stampNotifyBlock,
8153
8395
  BOARD_COLUMN_DEFS,
8154
8396
  HOME_BOARD_CACHE_TTL_MS,
8155
8397
  dedupeBoardIssues,
@@ -9,10 +9,10 @@ import {
9
9
  looksLikeSkillBody,
10
10
  skillCommandFromBody,
11
11
  withEventParentId
12
- } from "./chunk-FAGK33E2.js";
12
+ } from "./chunk-Y7JPL4WA.js";
13
13
  import {
14
14
  isOrchestratorThread
15
- } from "./chunk-A2G3ZBJS.js";
15
+ } from "./chunk-SKSOCIJT.js";
16
16
  import {
17
17
  codexUnattendedGitConfigArgs,
18
18
  mergeAgentGitAuthEnv,
@@ -38,13 +38,13 @@ import {
38
38
  packagedMcpStdioPath,
39
39
  parseCursorRunnerLine,
40
40
  resolveNodeLaunch
41
- } from "./chunk-FIBZEFKA.js";
41
+ } from "./chunk-IFURHRAR.js";
42
42
  import {
43
43
  extractJsonErrorMessage,
44
44
  formatUnknownDetail,
45
45
  looksLikeAgentFailureMessage,
46
46
  looksLikeBrightsyEmptyCompletion
47
- } from "./chunk-5N4Q46SG.js";
47
+ } from "./chunk-K2S33ZCB.js";
48
48
  import {
49
49
  enrichPathWithNpmGlobalBin,
50
50
  isConductorBundledCli,
@@ -1745,7 +1745,7 @@ var claudeAdapter = {
1745
1745
  );
1746
1746
  }
1747
1747
  const mode = permissionMode(thread);
1748
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-N2YYJKA5.js");
1748
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-AK5PSPQ2.js");
1749
1749
  const isOrchestrator = isOrchestratorThread2(thread);
1750
1750
  const rpc = worktreeAgentRpc(isOrchestrator);
1751
1751
  const injectedServers = await buildInjectedMcpServers({