@sideboard-ai/core 0.1.277 → 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
@@ -1,4 +1,5 @@
1
1
  import {
2
+ SIDEBOARD_THREAD_ID_ENV,
2
3
  adjustBrightsyTurnExit,
3
4
  allAdapters,
4
5
  getAdapter,
@@ -7,7 +8,7 @@ import {
7
8
  parseSessionQuotaResetAt,
8
9
  planModeTurnInstruction,
9
10
  resolveQuotaFallbackAgent
10
- } from "./chunk-NIB2IZ6J.js";
11
+ } from "./chunk-HNNS25SG.js";
11
12
  import {
12
13
  REVIEW_TAB_TITLE,
13
14
  applyWorktreePkgCacheEnv,
@@ -17,12 +18,14 @@ import {
17
18
  copyConfiguredFiles,
18
19
  createChatTab,
19
20
  deriveTaskState,
21
+ extractPendingPlanQuestions,
20
22
  forkChatTab,
21
23
  forkMessageSlice,
22
24
  formatReviewWriteGateDirective,
23
25
  formatReviewWriteGateReminder,
24
26
  getDefaultRunScript,
25
27
  getRunMode,
28
+ isAskUserToolName,
26
29
  isJobContinuePrompt,
27
30
  isReviewWriteGatedThread,
28
31
  isWorktreeRunProcessKey,
@@ -44,7 +47,7 @@ import {
44
47
  worktreeDevProcessKey,
45
48
  worktreeRunProcessKey,
46
49
  worktreeSetupProcessKey
47
- } from "./chunk-MGS5T2A3.js";
50
+ } from "./chunk-HQXBBUQX.js";
48
51
  import {
49
52
  applyAgentEvent,
50
53
  clipAgentEventForPaint,
@@ -59,12 +62,13 @@ import {
59
62
  partsToAssistantText,
60
63
  stripBrightsyNdjsonNoise,
61
64
  toolDescription
62
- } from "./chunk-FAGK33E2.js";
65
+ } from "./chunk-Y7JPL4WA.js";
63
66
  import {
64
67
  titleFromPrompt
65
68
  } from "./chunk-7H655NEW.js";
66
69
  import {
67
70
  extractPresentedPlan,
71
+ isPresentPlanToolName,
68
72
  readPlanFile,
69
73
  writePlanFile
70
74
  } from "./chunk-PGPSIEMW.js";
@@ -88,7 +92,7 @@ import {
88
92
  stageAbsolutePathsAsAttachments,
89
93
  stageBuffersAsAttachments,
90
94
  syncWorkspacesFromThreads
91
- } from "./chunk-A2G3ZBJS.js";
95
+ } from "./chunk-SKSOCIJT.js";
92
96
  import {
93
97
  addPrStackLayer,
94
98
  agentNicknameFromWorktreePath,
@@ -162,7 +166,7 @@ import {
162
166
  import {
163
167
  applyAgentRunnerHeapEnv,
164
168
  packagedMcpDir
165
- } from "./chunk-FIBZEFKA.js";
169
+ } from "./chunk-IFURHRAR.js";
166
170
  import {
167
171
  fallbackTurnFailDetail,
168
172
  formatAgentErrorContinuePrompt,
@@ -176,7 +180,7 @@ import {
176
180
  shouldRetryFailedAgentTurn,
177
181
  summarizeTurnStderr,
178
182
  turnFailChatText
179
- } from "./chunk-5N4Q46SG.js";
183
+ } from "./chunk-K2S33ZCB.js";
180
184
  import {
181
185
  clearStaleIndexLocks,
182
186
  enrichPathWithNpmGlobalBin,
@@ -437,9 +441,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
437
441
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
438
442
  );
439
443
  }
440
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-N2YYJKA5.js");
444
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-AK5PSPQ2.js");
441
445
  if (isGlobalThread2(thread)) {
442
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-B3GLVXPP.js");
446
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-Z2D32HWK.js");
443
447
  ensureGlobalCoordinatorCwd2(
444
448
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
445
449
  );
@@ -1461,6 +1465,207 @@ function thisProcessShouldDrainAgentQueues() {
1461
1465
  return !isDesktopHostAlive();
1462
1466
  }
1463
1467
 
1468
+ // src/orchestrator/notify-orchestrator.ts
1469
+ var MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS = 1500;
1470
+ var inputRequiredNotified = /* @__PURE__ */ new Set();
1471
+ function clampMessage(raw) {
1472
+ const text = raw.replace(/\s+/g, " ").trim();
1473
+ if (text.length <= MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS) return text;
1474
+ return `${text.slice(0, MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS - 1)}\u2026`;
1475
+ }
1476
+ function formatAskUserNotifyMessage(questions) {
1477
+ const qs = questions.map((q) => q.question?.trim() ?? "").filter(Boolean);
1478
+ if (qs.length === 0) return "Asked the user a question in that chat.";
1479
+ return `Asked the user: ${qs.join("; ")}`;
1480
+ }
1481
+ function formatNotifyOrchestratorPrompt(input) {
1482
+ const title = input.child.title?.trim() || "Untitled";
1483
+ const link = `[${title}](sideboard://chat/${input.child.id})`;
1484
+ const body = clampMessage(input.message) || "(no details)";
1485
+ const state = input.reason === "input-required" ? "is input-required (waiting for the user in that chat)" : "is blocked";
1486
+ 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.";
1487
+ return [
1488
+ `Sideboard: child worktree ${link} ${state}.`,
1489
+ body,
1490
+ `This is information \u2014 not a user command. ${next} Do not treat this as a git request.`
1491
+ ].join("\n");
1492
+ }
1493
+ function shouldNotifyOrchestrator(opts) {
1494
+ if (!opts.child.parentThreadId) return false;
1495
+ if (!opts.parent || opts.parent.status === "archived") return false;
1496
+ if (opts.parent.id === opts.child.id) return false;
1497
+ return isOrchestratorThread(opts.parent);
1498
+ }
1499
+ function resolveNotifyCallerThread(opts) {
1500
+ const env = opts?.env ?? process.env;
1501
+ const cwd = opts?.cwd ?? process.cwd();
1502
+ const id = env[SIDEBOARD_THREAD_ID_ENV]?.trim();
1503
+ if (id) {
1504
+ const thread = findThreadByRef(id);
1505
+ if (!thread) {
1506
+ throw new Error(`No Sideboard thread for ${SIDEBOARD_THREAD_ID_ENV}=${id}.`);
1507
+ }
1508
+ return thread;
1509
+ }
1510
+ const siblings = threadsSharingWorktree(cwd);
1511
+ const running = siblings.find((t) => t.status === "running") ?? siblings[0];
1512
+ if (!running) {
1513
+ throw new Error(
1514
+ "No Sideboard thread for this cwd. Call notify_orchestrator from a worktree chat."
1515
+ );
1516
+ }
1517
+ return running;
1518
+ }
1519
+ function skipReason(opts) {
1520
+ if (!opts.child.parentThreadId) {
1521
+ return "This chat has no orchestrator parent. notify_orchestrator is for worktree children of a Global chat.";
1522
+ }
1523
+ if (!opts.parent) {
1524
+ return "Parent orchestration chat was not found.";
1525
+ }
1526
+ if (opts.parent.status === "archived") {
1527
+ return "Parent orchestration chat is archived.";
1528
+ }
1529
+ if (!isOrchestratorThread(opts.parent)) {
1530
+ return "Parent is not a Global orchestration chat.";
1531
+ }
1532
+ return "Cannot notify orchestrator.";
1533
+ }
1534
+ async function notifyOrchestrator(input) {
1535
+ const parent = input.child.parentThreadId ? readThread(input.child.parentThreadId) : null;
1536
+ if (!shouldNotifyOrchestrator({ child: input.child, parent })) {
1537
+ return { ok: false, error: skipReason({ child: input.child, parent }) };
1538
+ }
1539
+ const parentId = parent.id;
1540
+ if (input.reason === "input-required") {
1541
+ if (inputRequiredNotified.has(input.child.id)) {
1542
+ return { ok: true, parentThreadId: parentId, deduped: true };
1543
+ }
1544
+ inputRequiredNotified.add(input.child.id);
1545
+ }
1546
+ const prompt = formatNotifyOrchestratorPrompt({
1547
+ child: input.child,
1548
+ reason: input.reason,
1549
+ message: input.message
1550
+ });
1551
+ try {
1552
+ await input.send(parentId, prompt, { followUp: "queue" });
1553
+ } catch (err) {
1554
+ if (input.reason === "input-required") {
1555
+ inputRequiredNotified.delete(input.child.id);
1556
+ }
1557
+ const message = err instanceof Error ? err.message : String(err);
1558
+ return { ok: false, error: message };
1559
+ }
1560
+ return { ok: true, parentThreadId: parentId };
1561
+ }
1562
+
1563
+ // src/orchestrator/agent-block.ts
1564
+ var MAX_AGENT_BLOCK_REASON = 240;
1565
+ function clampBlockReason(raw) {
1566
+ const text = raw.replace(/\s+/g, " ").trim();
1567
+ if (!text) return "Waiting on a decision";
1568
+ if (text.length <= MAX_AGENT_BLOCK_REASON) return text;
1569
+ return `${text.slice(0, MAX_AGENT_BLOCK_REASON - 1)}\u2026`;
1570
+ }
1571
+ function makeAgentBlock(source, reason, at = (/* @__PURE__ */ new Date()).toISOString()) {
1572
+ return { source, reason: clampBlockReason(reason), at };
1573
+ }
1574
+ function lastTopLevelTool(parts) {
1575
+ if (!parts?.length) return null;
1576
+ for (let i = parts.length - 1; i >= 0; i--) {
1577
+ const part = parts[i];
1578
+ if (part.type === "tool" && !part.parentId) return part;
1579
+ }
1580
+ return null;
1581
+ }
1582
+ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date()).toISOString()) {
1583
+ const last = lastTopLevelTool(parts);
1584
+ if (last?.type === "tool" && isAskUserToolName(last.name)) {
1585
+ const pending = extractPendingPlanQuestions(parts);
1586
+ const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1587
+ return makeAgentBlock("ask_user", reason, at);
1588
+ }
1589
+ if (!planMode || last?.type !== "tool") return null;
1590
+ const exited = /exitplanmode/i.test(last.name ?? "");
1591
+ if (!isPresentPlanToolName(last.name) && !exited) return null;
1592
+ const presented = extractPresentedPlan(parts);
1593
+ if (!presented && !exited) return null;
1594
+ const title = presented?.title && presented.title !== "Plan" ? presented.title : "";
1595
+ return makeAgentBlock(
1596
+ "plan",
1597
+ title ? `Waiting for plan approval: ${title}` : "Waiting for plan approval",
1598
+ at
1599
+ );
1600
+ }
1601
+ function agentBlockAfterTurn(input) {
1602
+ if (input.failed) return null;
1603
+ const decision = decisionBlockFromParts(input.parts, input.planMode);
1604
+ if (decision) return decision;
1605
+ if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1606
+ return null;
1607
+ }
1608
+ return input.existing ?? null;
1609
+ }
1610
+ function clearAgentBlock(threadId) {
1611
+ const thread = readThread(threadId);
1612
+ if (!thread?.agentBlock) return;
1613
+ updateThread(threadId, { agentBlock: null });
1614
+ }
1615
+ function writeAgentBlock(threadId, block) {
1616
+ const thread = readThread(threadId);
1617
+ if (!thread || thread.status === "archived") return;
1618
+ updateThread(threadId, { agentBlock: block });
1619
+ }
1620
+ function settleAgentBlock(threadId, parts, failed) {
1621
+ const thread = readThread(threadId);
1622
+ if (!thread || thread.status === "archived") return;
1623
+ const next = agentBlockAfterTurn({
1624
+ existing: thread.agentBlock,
1625
+ parts,
1626
+ planMode: Boolean(thread.planMode),
1627
+ failed
1628
+ });
1629
+ const prev = thread.agentBlock ?? null;
1630
+ if (prev === next) return;
1631
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at) {
1632
+ return;
1633
+ }
1634
+ updateThread(threadId, { agentBlock: next });
1635
+ }
1636
+ function chatBlockedSuffix(thread) {
1637
+ const raw = thread.agentBlock?.reason;
1638
+ if (!raw?.trim()) return "";
1639
+ return ` blocked:${raw.replace(/\s+/g, " ").slice(0, 80)}`;
1640
+ }
1641
+ function agentBlockFields(thread) {
1642
+ return {
1643
+ blockedReason: thread.agentBlock?.reason ?? null,
1644
+ blockedSource: thread.agentBlock?.source ?? null
1645
+ };
1646
+ }
1647
+ function stampAskUserBlock(resolveCaller, questions) {
1648
+ const asked = formatAskUserNotifyMessage(questions);
1649
+ try {
1650
+ writeAgentBlock(resolveCaller().id, makeAgentBlock("ask_user", asked));
1651
+ } catch {
1652
+ }
1653
+ return asked;
1654
+ }
1655
+ function stampPlanBlock(threadId, title) {
1656
+ const planTitle = title?.trim() || "Plan";
1657
+ if (threadId) {
1658
+ const reason = planTitle === "Plan" ? "Waiting for plan approval" : `Waiting for plan approval: ${planTitle}`;
1659
+ writeAgentBlock(threadId, makeAgentBlock("plan", reason));
1660
+ }
1661
+ return planTitle;
1662
+ }
1663
+ function stampNotifyBlock(threadId, reason, message) {
1664
+ const resolved = reason ?? "blocked";
1665
+ writeAgentBlock(threadId, makeAgentBlock(resolved === "input-required" ? "ask_user" : "reported", message));
1666
+ return resolved;
1667
+ }
1668
+
1464
1669
  // src/orchestrator/child-halt.ts
1465
1670
  var HALT_STATUSES = /* @__PURE__ */ new Set(["stopped", "error", "broken"]);
1466
1671
  function isIncompleteChildStatus(status) {
@@ -1565,6 +1770,36 @@ async function requireAgent(agent, opts) {
1565
1770
  return status;
1566
1771
  }
1567
1772
 
1773
+ // src/board/agent-block-view.ts
1774
+ function worktreeBoardStatus(group) {
1775
+ const order = ["running", "queued", "error", "broken"];
1776
+ for (const status of order) {
1777
+ if (group.some((t) => t.status === status)) return status;
1778
+ }
1779
+ return group[0]?.status ?? "idle";
1780
+ }
1781
+ function visibleAgentBlock(thread) {
1782
+ if (thread.status === "archived" || thread.status === "error" || thread.status === "broken") {
1783
+ return null;
1784
+ }
1785
+ const block = thread.agentBlock;
1786
+ if (!block?.reason?.trim()) return null;
1787
+ return block;
1788
+ }
1789
+ function worktreeAgentBlock(group) {
1790
+ let best = null;
1791
+ for (const thread of group) {
1792
+ const block = visibleAgentBlock(thread);
1793
+ if (!block) continue;
1794
+ if (!best || block.at > best.at) best = block;
1795
+ }
1796
+ return best;
1797
+ }
1798
+ function threadCardBlockFields(group) {
1799
+ const block = worktreeAgentBlock(group);
1800
+ return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
1801
+ }
1802
+
1568
1803
  // src/board/home-board.ts
1569
1804
  var GLOBAL_WORKSPACE_ID = "__global__";
1570
1805
  function isHomeBoardThread(thread) {
@@ -1747,13 +1982,6 @@ function classifyWorktreeListBadges(group, viewerLogin = "", reviewLabel = "") {
1747
1982
  }
1748
1983
  ];
1749
1984
  }
1750
- function worktreeBoardStatus(group) {
1751
- const order = ["running", "queued", "error", "broken"];
1752
- for (const status of order) {
1753
- if (group.some((t) => t.status === status)) return status;
1754
- }
1755
- return group[0]?.status ?? "idle";
1756
- }
1757
1985
  function normalizeIssueKey(value) {
1758
1986
  return value.trim().toLowerCase().replace(/^#/, "");
1759
1987
  }
@@ -2109,7 +2337,8 @@ function toThreadCard(group) {
2109
2337
  repoPath: thread.repoPath,
2110
2338
  prUrl: withPr.prUrl,
2111
2339
  link: `sideboard://chat/${thread.id}`,
2112
- ...group.length > 1 ? { chatCount: group.length } : {}
2340
+ ...group.length > 1 ? { chatCount: group.length } : {},
2341
+ ...threadCardBlockFields(group) ?? {}
2113
2342
  };
2114
2343
  }
2115
2344
  function threadMatchesKind(thread, kind) {
@@ -2183,7 +2412,7 @@ function assembleHomeBoard(input) {
2183
2412
  }
2184
2413
  return { columns, hidden, totals };
2185
2414
  }
2186
- 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.";
2415
+ 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.";
2187
2416
 
2188
2417
  // src/threads/create.ts
2189
2418
  function persistCreateAttachments(worktreePath, attachments) {
@@ -2406,7 +2635,7 @@ async function createThread(input, _onSetupLine) {
2406
2635
  return readThread(thread.id) ?? thread;
2407
2636
  }
2408
2637
  async function listLinearIssues(agent, repoPath) {
2409
- const { getAdapter: getAdapter2 } = await import("./agents-2FOWVJCN.js");
2638
+ const { getAdapter: getAdapter2 } = await import("./agents-DYCFLDNL.js");
2410
2639
  await requireAgent(agent, { requireLinear: true });
2411
2640
  const adapter = getAdapter2(agent);
2412
2641
  if (!adapter.listLinearIssues) {
@@ -2913,7 +3142,7 @@ async function adoptThread(input) {
2913
3142
  messages: input.messages ?? []
2914
3143
  });
2915
3144
  writeThread(thread);
2916
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PBB6HY2D.js");
3145
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UP6DQ3WL.js");
2917
3146
  await ensureWorkspace2(repoPath);
2918
3147
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-RXZPTCNH.js");
2919
3148
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -5610,7 +5839,7 @@ function formatScheduledPrompt(name, prompt) {
5610
5839
  ${prompt}`;
5611
5840
  }
5612
5841
  async function defaultDeps() {
5613
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-QMKVXVUR.js");
5842
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-KY2KBBE2.js");
5614
5843
  const orch = getOrchestrator2();
5615
5844
  return {
5616
5845
  findThread: (id) => findThreadByRef(id),
@@ -6588,6 +6817,7 @@ var Orchestrator = class {
6588
6817
  const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
6589
6818
  const parked = takePendingTurnAttachments(thread);
6590
6819
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
6820
+ if (!autoContinue) clearAgentBlock(threadId);
6591
6821
  appendMessage(threadId, {
6592
6822
  role: "user",
6593
6823
  text: promptText,
@@ -6993,6 +7223,7 @@ var Orchestrator = class {
6993
7223
  } else {
6994
7224
  await syncThreadBranchFromGit(threadId);
6995
7225
  }
7226
+ settleAgentBlock(threadId, parts, exitCode !== 0 && !this.stoppedTurns.has(threadId));
6996
7227
  if (this.stoppedTurns.has(threadId)) {
6997
7228
  const stopped = writeLiveStatus(threadId, "stopped");
6998
7229
  if (stopped?.status === "stopped") {
@@ -7043,6 +7274,7 @@ var Orchestrator = class {
7043
7274
  } catch (err) {
7044
7275
  const message = err instanceof Error ? err.message : String(err);
7045
7276
  await syncThreadBranchFromGit(threadId).catch(() => void 0);
7277
+ settleAgentBlock(threadId, void 0, !this.stoppedTurns.has(threadId));
7046
7278
  if (this.stoppedTurns.has(threadId)) {
7047
7279
  const stopped = writeLiveStatus(threadId, "stopped");
7048
7280
  if (stopped?.status === "stopped") {
@@ -7758,6 +7990,7 @@ var Orchestrator = class {
7758
7990
  stillRunning,
7759
7991
  lastAgentParts: lastAgent?.parts
7760
7992
  });
7993
+ const block = thread.agentBlock?.reason?.trim() ? thread.agentBlock : null;
7761
7994
  return {
7762
7995
  text,
7763
7996
  status: thread.status,
@@ -7771,7 +8004,9 @@ var Orchestrator = class {
7771
8004
  lastAgent?.ts,
7772
8005
  thread.updatedAt
7773
8006
  ]),
7774
- usage: stillRunning ? null : lastAgent?.usage ?? null
8007
+ usage: stillRunning ? null : lastAgent?.usage ?? null,
8008
+ blockedReason: block?.reason ?? null,
8009
+ blockedSource: block?.source ?? null
7775
8010
  };
7776
8011
  }
7777
8012
  assertNotGlobal(thread, action) {
@@ -8298,7 +8533,7 @@ var Orchestrator = class {
8298
8533
  this.enforceHistoryRetention();
8299
8534
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
8300
8535
  try {
8301
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PBB6HY2D.js");
8536
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UP6DQ3WL.js");
8302
8537
  await ensureWorkspace2(thread.repoPath);
8303
8538
  } catch {
8304
8539
  }
@@ -8486,6 +8721,9 @@ async function startOrchestration(opts) {
8486
8721
  export {
8487
8722
  isUnclaimedRunScriptRequest,
8488
8723
  isAdoptableRunScriptRequest,
8724
+ worktreeBoardStatus,
8725
+ visibleAgentBlock,
8726
+ worktreeAgentBlock,
8489
8727
  isHomeBoardThread,
8490
8728
  BOARD_COLUMN_DEFS,
8491
8729
  HOME_BOARD_CACHE_TTL_MS,
@@ -8494,7 +8732,6 @@ export {
8494
8732
  classifyWorktreeColumn,
8495
8733
  reviewLabelForRepo,
8496
8734
  classifyWorktreeListBadges,
8497
- worktreeBoardStatus,
8498
8735
  dedupeBoardIssues,
8499
8736
  issueNeedsWorkspacePick,
8500
8737
  isAccountWideIssueSource,
@@ -8556,6 +8793,16 @@ export {
8556
8793
  shouldRunHistoryCleanup,
8557
8794
  applyThreadIntoMain,
8558
8795
  cloneRepoIntoSideboard,
8796
+ formatAskUserNotifyMessage,
8797
+ formatNotifyOrchestratorPrompt,
8798
+ shouldNotifyOrchestrator,
8799
+ resolveNotifyCallerThread,
8800
+ notifyOrchestrator,
8801
+ chatBlockedSuffix,
8802
+ agentBlockFields,
8803
+ stampAskUserBlock,
8804
+ stampPlanBlock,
8805
+ stampNotifyBlock,
8559
8806
  detectAgents,
8560
8807
  requireAgent,
8561
8808
  createThread,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  clipToolResultForStore
3
- } from "./chunk-5N4Q46SG.js";
3
+ } from "./chunk-K2S33ZCB.js";
4
4
 
5
5
  // src/agents/message-parts.ts
6
6
  function asRecord(input) {
@@ -5,13 +5,13 @@ import {
5
5
  compactSummaryPart,
6
6
  isInternalAgentStatusText,
7
7
  lastAssistantMessageText
8
- } from "./chunk-UKSJO36E.js";
8
+ } from "./chunk-CPA43FBK.js";
9
9
  import {
10
10
  isCloudCoordinatorThread,
11
11
  isGlobalThread,
12
12
  isOrchestratorThread,
13
13
  isSlackCoordinatorThread
14
- } from "./chunk-QTZO4LPU.js";
14
+ } from "./chunk-TZ5JHIYK.js";
15
15
  import {
16
16
  allocateTeamName,
17
17
  mergeAgentGitAuthEnv,
@@ -676,6 +676,17 @@ function threadsSharingWorktree(worktreePath) {
676
676
  (t) => Boolean(t.worktreePath) && sameWorktreePath(t.worktreePath, key) && t.status !== "archived"
677
677
  ).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
678
678
  }
679
+ function workspaceAgentChatSummaries(worktreePath) {
680
+ if (!worktreePath) return [];
681
+ return threadsSharingWorktree(worktreePath).map((t) => ({
682
+ id: t.id,
683
+ title: t.title,
684
+ agent: t.agent,
685
+ status: t.status,
686
+ link: `sideboard://chat/${t.id}`,
687
+ ...t.agentBlock?.reason ? { blockedReason: t.agentBlock.reason, blockedSource: t.agentBlock.source } : {}
688
+ }));
689
+ }
679
690
  function formatTranscriptMarkdown(title, messages) {
680
691
  const body = formatMessagesAsTranscript(messages, { tools: "full" });
681
692
  return [`# Transcript: ${title}`, "", `Forked ${(/* @__PURE__ */ new Date()).toISOString()}`, "", body].join("\n");
@@ -1166,6 +1177,78 @@ function jobTreeAlive(pid) {
1166
1177
  return jobAlive(pid) || processGroupAlive(pid);
1167
1178
  }
1168
1179
 
1180
+ // src/plan/ask-user.ts
1181
+ function planQuestionsSignature(questions) {
1182
+ return questions.map((q) => {
1183
+ const opts = q.options.map((o) => o.label).join("");
1184
+ return `${q.question}${opts}`;
1185
+ }).join("");
1186
+ }
1187
+ function asRecord(v) {
1188
+ return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
1189
+ }
1190
+ function parseOptions(raw) {
1191
+ if (!Array.isArray(raw)) return [];
1192
+ const out = [];
1193
+ for (const item of raw) {
1194
+ if (typeof item === "string" && item.trim()) {
1195
+ out.push({ label: item.trim() });
1196
+ continue;
1197
+ }
1198
+ const o = asRecord(item);
1199
+ if (!o) continue;
1200
+ const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
1201
+ if (!label) continue;
1202
+ const description = typeof o.description === "string" && o.description.trim() ? o.description.trim() : void 0;
1203
+ out.push({ label, description });
1204
+ }
1205
+ return out;
1206
+ }
1207
+ function parseOneQuestion(raw) {
1208
+ const o = asRecord(raw);
1209
+ if (!o) return null;
1210
+ const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
1211
+ if (!question) return null;
1212
+ const options = parseOptions(o.options);
1213
+ if (options.length < 1) return null;
1214
+ const header = typeof o.header === "string" && o.header.trim() ? o.header.trim() : void 0;
1215
+ const multiSelect = Boolean(o.multiSelect ?? o.multi_select);
1216
+ return { question, header, multiSelect, options };
1217
+ }
1218
+ function parsePlanQuestionsInput(input) {
1219
+ const root = asRecord(input);
1220
+ if (!root) return [];
1221
+ const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
1222
+ const out = [];
1223
+ for (const item of list) {
1224
+ const q = parseOneQuestion(item);
1225
+ if (q) out.push(q);
1226
+ }
1227
+ return out;
1228
+ }
1229
+ var ASK_USER_TOOL_RE = /^(AskUserQuestion|ask_user|mcp__sideboard__ask_user)$/i;
1230
+ function isAskUserToolName(name) {
1231
+ if (!name) return false;
1232
+ const base = name.replace(/^mcp__sideboard__/i, "");
1233
+ return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
1234
+ }
1235
+ function extractPendingPlanQuestions(parts) {
1236
+ if (!parts?.length) return null;
1237
+ for (let i = parts.length - 1; i >= 0; i--) {
1238
+ const p = parts[i];
1239
+ if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
1240
+ const questions = parsePlanQuestionsInput(p.input);
1241
+ if (!questions.length) continue;
1242
+ return {
1243
+ id: p.id || `ask-${i}`,
1244
+ signature: planQuestionsSignature(questions),
1245
+ questions,
1246
+ source: p.name || "ask_user"
1247
+ };
1248
+ }
1249
+ return null;
1250
+ }
1251
+
1169
1252
  // src/orchestrator/task-state.ts
1170
1253
  function isIncompleteTaskState(state) {
1171
1254
  return state === "failed" || state === "canceled";
@@ -1173,13 +1256,12 @@ function isIncompleteTaskState(state) {
1173
1256
  function needsCoordinatorAction(state) {
1174
1257
  return isIncompleteTaskState(state) || state === "input-required";
1175
1258
  }
1176
- var ASK_USER_TOOL = /(^|[_.:/])ask_user$/i;
1177
1259
  function endedOnAskUser(parts) {
1178
1260
  if (!parts?.length) return false;
1179
1261
  for (let i = parts.length - 1; i >= 0; i--) {
1180
1262
  const p = parts[i];
1181
1263
  if (p.type === "tool" && !p.parentId) {
1182
- return ASK_USER_TOOL.test(p.name);
1264
+ return isAskUserToolName(p.name);
1183
1265
  }
1184
1266
  }
1185
1267
  return false;
@@ -1212,7 +1294,17 @@ var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not
1212
1294
  var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
1213
1295
  var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
1214
1296
  var MCP_WAIT_INPUT_REQUIRED_HINT = "Child asked the user a question (ask_user). taskState is input-required. Wait for the user to answer in that chat \u2014 do not send_to_chat a check-in.";
1215
- function mcpWaitTaskHint(taskState, status) {
1297
+ function mcpWaitTaskHint(taskState, status, blockedReason) {
1298
+ const why = blockedReason?.trim();
1299
+ if (why && taskState !== "failed" && taskState !== "canceled") {
1300
+ if (taskState === "submitted" || taskState === "working") {
1301
+ return `Child is blocked: ${why} It has not finished the turn. Do not send_to_chat a check-in.`;
1302
+ }
1303
+ if (taskState === "input-required") {
1304
+ return `Child is blocked: ${why} Wait for the user in that chat \u2014 do not send_to_chat a check-in.`;
1305
+ }
1306
+ return `Child is blocked: ${why} Help if this is your child; otherwise tell the user. Do not send_to_chat a check-in.`;
1307
+ }
1216
1308
  switch (taskState) {
1217
1309
  case "submitted":
1218
1310
  return MCP_WAIT_QUEUED_HINT;
@@ -1241,11 +1333,15 @@ function lastActivityAtForWait(stillRunning, liveUpdatedAt, fallbacks) {
1241
1333
  return null;
1242
1334
  }
1243
1335
  function waitForTurnToolResult(input) {
1336
+ const blockedReason = input.blockedReason?.trim() ? input.blockedReason.trim() : null;
1337
+ const waiting = Boolean(blockedReason) && !input.stillRunning && input.taskState !== "failed" && input.taskState !== "canceled";
1244
1338
  return {
1245
1339
  ...input,
1246
1340
  usage: input.usage ?? null,
1247
- hint: mcpWaitTaskHint(input.taskState, input.status),
1248
- incomplete: needsCoordinatorAction(input.taskState)
1341
+ blockedReason,
1342
+ blockedSource: blockedReason ? input.blockedSource ?? null : null,
1343
+ hint: mcpWaitTaskHint(input.taskState, input.status, blockedReason),
1344
+ incomplete: needsCoordinatorAction(input.taskState) || waiting
1249
1345
  };
1250
1346
  }
1251
1347
 
@@ -1577,7 +1673,7 @@ var PORT_RANGE_SIZE = 10;
1577
1673
  var RECLAIM_RETRY_DELAY_MS = 40;
1578
1674
  var RECLAIM_TRIES = 8;
1579
1675
  async function killReclaimable(ports, worktreePath, signal) {
1580
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-YYQKZAND.js");
1676
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-J6WDOXWM.js");
1581
1677
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1582
1678
  else killListenersOnPorts2(ports);
1583
1679
  }
@@ -2210,6 +2306,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2210
2306
  }
2211
2307
 
2212
2308
  export {
2309
+ isAskUserToolName,
2310
+ extractPendingPlanQuestions,
2213
2311
  needsCoordinatorAction,
2214
2312
  deriveTaskState,
2215
2313
  mcpWaitForTurnTimeoutMs,
@@ -2227,6 +2325,7 @@ export {
2227
2325
  resolveWorktreeSetupLog,
2228
2326
  mergeWorktreeActiveRuns,
2229
2327
  threadsSharingWorktree,
2328
+ workspaceAgentChatSummaries,
2230
2329
  forkMessageSlice,
2231
2330
  buildForkTranscriptAttachment,
2232
2331
  createChatTab,
@@ -33,9 +33,9 @@ import {
33
33
  tryReservePort,
34
34
  tryReservePorts,
35
35
  worktreeRunPortsPath
36
- } from "./chunk-HOMZKYHF.js";
37
- import "./chunk-UKSJO36E.js";
38
- import "./chunk-QTZO4LPU.js";
36
+ } from "./chunk-YDXNCBA2.js";
37
+ import "./chunk-CPA43FBK.js";
38
+ import "./chunk-TZ5JHIYK.js";
39
39
  import "./chunk-K7MWJLR7.js";
40
40
  import "./chunk-N2WW62LF.js";
41
41
  import "./chunk-DD67JWTN.js";