@sideboard-ai/core 0.1.306 → 0.1.308

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.
@@ -1288,12 +1288,23 @@ function isAskUserToolName(name) {
1288
1288
  const base = name.replace(/^mcp__sideboard__/i, "");
1289
1289
  return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
1290
1290
  }
1291
+ function questionsFromToolPart(part) {
1292
+ const fromInput = parsePlanQuestionsInput(part.input);
1293
+ if (fromInput.length) return fromInput;
1294
+ const raw = part.result?.trim();
1295
+ if (!raw || !raw.startsWith("{")) return [];
1296
+ try {
1297
+ return parsePlanQuestionsInput(JSON.parse(raw));
1298
+ } catch {
1299
+ return [];
1300
+ }
1301
+ }
1291
1302
  function extractPendingPlanQuestions(parts) {
1292
1303
  if (!parts?.length) return null;
1293
1304
  for (let i = parts.length - 1; i >= 0; i--) {
1294
1305
  const p = parts[i];
1295
1306
  if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
1296
- const questions = parsePlanQuestionsInput(p.input);
1307
+ const questions = questionsFromToolPart(p);
1297
1308
  if (!questions.length) continue;
1298
1309
  return {
1299
1310
  id: p.id || `ask-${i}`,
@@ -1304,13 +1315,47 @@ function extractPendingPlanQuestions(parts) {
1304
1315
  }
1305
1316
  return null;
1306
1317
  }
1318
+ function pendingFromAskBlock(block, dismissedQuestionSignature) {
1319
+ if (!block || block.source !== "ask_user" || !block.questions?.length) return null;
1320
+ const dismissed = dismissedQuestionSignature?.trim();
1321
+ if (dismissed && (!block.questionSignature || block.questionSignature === dismissed)) {
1322
+ return null;
1323
+ }
1324
+ const signature = block.questionSignature?.trim() || planQuestionsSignature(block.questions);
1325
+ return {
1326
+ id: `ask-block:${signature}`,
1327
+ signature,
1328
+ questions: block.questions,
1329
+ source: "ask_user"
1330
+ };
1331
+ }
1332
+ function askBlockStillWaiting(block, messages) {
1333
+ const askedAt = block?.at;
1334
+ if (!askedAt) return false;
1335
+ for (let i = messages.length - 1; i >= 0; i--) {
1336
+ const message = messages[i];
1337
+ if (message.role !== "user" || message.origin === "continue") continue;
1338
+ if (!message.ts) return true;
1339
+ return askedAt > message.ts;
1340
+ }
1341
+ return true;
1342
+ }
1307
1343
  function latestPendingPlanQuestions(input) {
1308
1344
  if (input.userReplied) return null;
1309
1345
  const fromLive = extractPendingPlanQuestions(input.liveParts);
1310
1346
  if (fromLive) return fromLive;
1311
1347
  const last = input.messages[input.messages.length - 1];
1312
- if (last?.role === "user") return null;
1313
- if (last?.role === "agent") return extractPendingPlanQuestions(last.parts);
1348
+ if (last?.role === "agent") {
1349
+ const fromParts = extractPendingPlanQuestions(last.parts);
1350
+ if (fromParts) return fromParts;
1351
+ }
1352
+ const fromBlock = pendingFromAskBlock(
1353
+ input.agentBlock,
1354
+ input.dismissedQuestionSignature
1355
+ );
1356
+ if (fromBlock && askBlockStillWaiting(input.agentBlock, input.messages)) {
1357
+ return fromBlock;
1358
+ }
1314
1359
  return null;
1315
1360
  }
1316
1361
  var PLAN_QUESTION_ANSWERS_PREFIX = "Answers to your questions:";
@@ -1778,7 +1823,7 @@ var PORT_RANGE_SIZE = 10;
1778
1823
  var RECLAIM_RETRY_DELAY_MS = 40;
1779
1824
  var RECLAIM_TRIES = 8;
1780
1825
  async function killReclaimable(ports, worktreePath, signal) {
1781
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-YBXURAUL.js");
1826
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-IPIMGUR4.js");
1782
1827
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1783
1828
  else killListenersOnPorts2(ports);
1784
1829
  }
@@ -55,7 +55,7 @@ import {
55
55
  worktreeDevProcessKey,
56
56
  worktreeRunProcessKey,
57
57
  worktreeSetupProcessKey
58
- } from "./chunk-OVNIP265.js";
58
+ } from "./chunk-CTFBZZGZ.js";
59
59
  import {
60
60
  applyAgentEvent,
61
61
  clipAgentEventForPaint,
@@ -1654,6 +1654,23 @@ function worktreeAgentBlock(group) {
1654
1654
  }
1655
1655
  return best;
1656
1656
  }
1657
+ function sidebarRowAgentBlock(group, opts) {
1658
+ if (opts.active) {
1659
+ const open = group.find((thread) => thread.id === opts.selectedId);
1660
+ if (open) return visibleAgentBlock(open);
1661
+ }
1662
+ return worktreeAgentBlock(group);
1663
+ }
1664
+ function sidebarParentSelectId(group, primaryId, opts) {
1665
+ if (opts.active) return primaryId;
1666
+ return sidebarQuestionThreadId(group, primaryId);
1667
+ }
1668
+ function sidebarQuestionThreadId(group, primaryId) {
1669
+ const block = worktreeAgentBlock(group);
1670
+ if (!block) return primaryId;
1671
+ const owner = group.find((thread) => visibleAgentBlock(thread) === block);
1672
+ return owner?.id ?? primaryId;
1673
+ }
1657
1674
  function threadCardBlockFields(group) {
1658
1675
  const block = worktreeAgentBlock(group);
1659
1676
  return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
@@ -1800,7 +1817,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1800
1817
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1801
1818
  return {
1802
1819
  ...makeAgentBlock("ask_user", reason, at),
1803
- ...pending ? { questionSignature: pending.signature } : {}
1820
+ ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
1804
1821
  };
1805
1822
  }
1806
1823
  if (!planMode || last?.type !== "tool") return null;
@@ -1815,14 +1832,24 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1815
1832
  at
1816
1833
  );
1817
1834
  }
1835
+ function withStampedQuestions(decision, existing) {
1836
+ if (decision.source !== "ask_user" || decision.questions?.length) return decision;
1837
+ if (existing?.source !== "ask_user" || !existing.questions?.length) return decision;
1838
+ return {
1839
+ ...decision,
1840
+ questions: existing.questions,
1841
+ questionSignature: decision.questionSignature ?? existing.questionSignature
1842
+ };
1843
+ }
1818
1844
  function agentBlockAfterTurn(input) {
1819
1845
  if (input.failed) return null;
1820
1846
  const decision = decisionBlockFromParts(input.parts, input.planMode);
1821
1847
  if (decision) {
1822
- if (isDismissedAskUserBlock(decision, input.dismissedQuestionSignature)) {
1848
+ const next = withStampedQuestions(decision, input.existing);
1849
+ if (isDismissedAskUserBlock(next, input.dismissedQuestionSignature)) {
1823
1850
  return input.existing?.source === "reported" ? input.existing : null;
1824
1851
  }
1825
- return decision;
1852
+ return next;
1826
1853
  }
1827
1854
  if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1828
1855
  return null;
@@ -1865,7 +1892,7 @@ function settleAgentBlock(threadId, parts, failed) {
1865
1892
  });
1866
1893
  const prev = thread.agentBlock ?? null;
1867
1894
  if (prev === next) return;
1868
- if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature) {
1895
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature && planQuestionsSignature(prev.questions ?? []) === planQuestionsSignature(next.questions ?? [])) {
1869
1896
  return;
1870
1897
  }
1871
1898
  updateThread(threadId, { agentBlock: next });
@@ -1888,7 +1915,11 @@ function stampAskUserBlock(resolveCaller, questions) {
1888
1915
  try {
1889
1916
  writeAgentBlock(
1890
1917
  resolveCaller().id,
1891
- parsed.length ? { ...block, questionSignature: planQuestionsSignature(parsed) } : block
1918
+ parsed.length ? {
1919
+ ...block,
1920
+ questionSignature: planQuestionsSignature(parsed),
1921
+ questions: parsed
1922
+ } : block
1892
1923
  );
1893
1924
  } catch {
1894
1925
  }
@@ -5837,6 +5868,15 @@ function connectedOrchestratorTurnReminder(thread, find) {
5837
5868
  function formatWorktreeReminder() {
5838
5869
  return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5839
5870
  }
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.';
5873
+ }
5874
+ function claudeSubagentTurnReminder(thread) {
5875
+ if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
5876
+ return null;
5877
+ }
5878
+ return formatClaudeSubagentReminder();
5879
+ }
5840
5880
  var PR_GOAL_RE = /greptile|\bci\b.*\b(green|pass)|checks?\s+(pass|green)|until\s+.*\b(pass|green|merge|clean)|\bfix ci\b|watch[- ]fix[- ]push/i;
5841
5881
  function mentionsPrGoal(prompt) {
5842
5882
  return PR_GOAL_RE.test(prompt);
@@ -6701,7 +6741,7 @@ function formatScheduledPrompt(name, prompt) {
6701
6741
  ${prompt}`;
6702
6742
  }
6703
6743
  async function defaultDeps() {
6704
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-XP5S4E5F.js");
6744
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SJU5LYAO.js");
6705
6745
  const orch = getOrchestrator2();
6706
6746
  return {
6707
6747
  findThread: (id) => findThreadByRef(id),
@@ -7773,6 +7813,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7773
7813
  );
7774
7814
  const turnReminders = [
7775
7815
  worktreeReminder,
7816
+ claudeSubagentTurnReminder(thread),
7776
7817
  connectedOrchestratorTurnReminder(thread, (id) => readThread(id)),
7777
7818
  optionalServicesReminder,
7778
7819
  issueToolsReminder,
@@ -9611,6 +9652,9 @@ export {
9611
9652
  worktreeBoardStatus,
9612
9653
  visibleAgentBlock,
9613
9654
  worktreeAgentBlock,
9655
+ sidebarRowAgentBlock,
9656
+ sidebarParentSelectId,
9657
+ sidebarQuestionThreadId,
9614
9658
  reviewLabelForRepo,
9615
9659
  classifyWorktreeListBadges,
9616
9660
  isHomeBoardThread,
@@ -9764,6 +9808,8 @@ export {
9764
9808
  formatConnectedOrchestratorReminder,
9765
9809
  connectedOrchestratorTurnReminder,
9766
9810
  formatWorktreeReminder,
9811
+ formatClaudeSubagentReminder,
9812
+ claudeSubagentTurnReminder,
9767
9813
  mentionsPrGoal,
9768
9814
  issueTicketFromThread,
9769
9815
  linearTicketIdFromThread,
@@ -1235,12 +1235,23 @@ function isAskUserToolName(name) {
1235
1235
  const base = name.replace(/^mcp__sideboard__/i, "");
1236
1236
  return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
1237
1237
  }
1238
+ function questionsFromToolPart(part) {
1239
+ const fromInput = parsePlanQuestionsInput(part.input);
1240
+ if (fromInput.length) return fromInput;
1241
+ const raw = part.result?.trim();
1242
+ if (!raw || !raw.startsWith("{")) return [];
1243
+ try {
1244
+ return parsePlanQuestionsInput(JSON.parse(raw));
1245
+ } catch {
1246
+ return [];
1247
+ }
1248
+ }
1238
1249
  function extractPendingPlanQuestions(parts) {
1239
1250
  if (!parts?.length) return null;
1240
1251
  for (let i = parts.length - 1; i >= 0; i--) {
1241
1252
  const p = parts[i];
1242
1253
  if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
1243
- const questions = parsePlanQuestionsInput(p.input);
1254
+ const questions = questionsFromToolPart(p);
1244
1255
  if (!questions.length) continue;
1245
1256
  return {
1246
1257
  id: p.id || `ask-${i}`,
@@ -1676,7 +1687,7 @@ var PORT_RANGE_SIZE = 10;
1676
1687
  var RECLAIM_RETRY_DELAY_MS = 40;
1677
1688
  var RECLAIM_TRIES = 8;
1678
1689
  async function killReclaimable(ports, worktreePath, signal) {
1679
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-ZFRXTVGP.js");
1690
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-E6M3BATZ.js");
1680
1691
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1681
1692
  else killListenersOnPorts2(ports);
1682
1693
  }
@@ -68,7 +68,7 @@ import {
68
68
  worktreeDevProcessKey,
69
69
  worktreeRunProcessKey,
70
70
  worktreeSetupProcessKey
71
- } from "./chunk-5SFA27CF.js";
71
+ } from "./chunk-HRPSFU4Q.js";
72
72
  import {
73
73
  applyAgentEvent,
74
74
  clipAgentEventForPaint,
@@ -1703,7 +1703,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1703
1703
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
1704
1704
  return {
1705
1705
  ...makeAgentBlock("ask_user", reason, at),
1706
- ...pending ? { questionSignature: pending.signature } : {}
1706
+ ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
1707
1707
  };
1708
1708
  }
1709
1709
  if (!planMode || last?.type !== "tool") return null;
@@ -1718,14 +1718,24 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
1718
1718
  at
1719
1719
  );
1720
1720
  }
1721
+ function withStampedQuestions(decision, existing) {
1722
+ if (decision.source !== "ask_user" || decision.questions?.length) return decision;
1723
+ if (existing?.source !== "ask_user" || !existing.questions?.length) return decision;
1724
+ return {
1725
+ ...decision,
1726
+ questions: existing.questions,
1727
+ questionSignature: decision.questionSignature ?? existing.questionSignature
1728
+ };
1729
+ }
1721
1730
  function agentBlockAfterTurn(input) {
1722
1731
  if (input.failed) return null;
1723
1732
  const decision = decisionBlockFromParts(input.parts, input.planMode);
1724
1733
  if (decision) {
1725
- if (isDismissedAskUserBlock(decision, input.dismissedQuestionSignature)) {
1734
+ const next = withStampedQuestions(decision, input.existing);
1735
+ if (isDismissedAskUserBlock(next, input.dismissedQuestionSignature)) {
1726
1736
  return input.existing?.source === "reported" ? input.existing : null;
1727
1737
  }
1728
- return decision;
1738
+ return next;
1729
1739
  }
1730
1740
  if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
1731
1741
  return null;
@@ -1768,7 +1778,7 @@ function settleAgentBlock(threadId, parts, failed) {
1768
1778
  });
1769
1779
  const prev = thread.agentBlock ?? null;
1770
1780
  if (prev === next) return;
1771
- if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature) {
1781
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature && planQuestionsSignature(prev.questions ?? []) === planQuestionsSignature(next.questions ?? [])) {
1772
1782
  return;
1773
1783
  }
1774
1784
  updateThread(threadId, { agentBlock: next });
@@ -1791,7 +1801,11 @@ function stampAskUserBlock(resolveCaller, questions) {
1791
1801
  try {
1792
1802
  writeAgentBlock(
1793
1803
  resolveCaller().id,
1794
- parsed.length ? { ...block, questionSignature: planQuestionsSignature(parsed) } : block
1804
+ parsed.length ? {
1805
+ ...block,
1806
+ questionSignature: planQuestionsSignature(parsed),
1807
+ questions: parsed
1808
+ } : block
1795
1809
  );
1796
1810
  } catch {
1797
1811
  }
@@ -5533,6 +5547,15 @@ function connectedOrchestratorTurnReminder(thread, find) {
5533
5547
  function formatWorktreeReminder() {
5534
5548
  return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5535
5549
  }
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.';
5552
+ }
5553
+ function claudeSubagentTurnReminder(thread) {
5554
+ if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
5555
+ return null;
5556
+ }
5557
+ return formatClaudeSubagentReminder();
5558
+ }
5536
5559
  var PR_GOAL_RE = /greptile|\bci\b.*\b(green|pass)|checks?\s+(pass|green)|until\s+.*\b(pass|green|merge|clean)|\bfix ci\b|watch[- ]fix[- ]push/i;
5537
5560
  function mentionsPrGoal(prompt) {
5538
5561
  return PR_GOAL_RE.test(prompt);
@@ -6366,7 +6389,7 @@ function formatScheduledPrompt(name, prompt) {
6366
6389
  ${prompt}`;
6367
6390
  }
6368
6391
  async function defaultDeps() {
6369
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-UXHU766C.js");
6392
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-JTDQ47H7.js");
6370
6393
  const orch = getOrchestrator2();
6371
6394
  return {
6372
6395
  findThread: (id) => findThreadByRef(id),
@@ -7438,6 +7461,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7438
7461
  );
7439
7462
  const turnReminders = [
7440
7463
  worktreeReminder,
7464
+ claudeSubagentTurnReminder(thread),
7441
7465
  connectedOrchestratorTurnReminder(thread, (id) => readThread(id)),
7442
7466
  optionalServicesReminder,
7443
7467
  issueToolsReminder,
@@ -34,7 +34,7 @@ import {
34
34
  tryReservePort,
35
35
  tryReservePorts,
36
36
  worktreeRunPortsPath
37
- } from "./chunk-5SFA27CF.js";
37
+ } from "./chunk-HRPSFU4Q.js";
38
38
  import "./chunk-5XQ43YZU.js";
39
39
  import "./chunk-X3FSWWW2.js";
40
40
  import "./chunk-DLGGLV2M.js";
@@ -32,7 +32,7 @@ import {
32
32
  tryReservePort,
33
33
  tryReservePorts,
34
34
  worktreeRunPortsPath
35
- } from "./chunk-OVNIP265.js";
35
+ } from "./chunk-CTFBZZGZ.js";
36
36
  import "./chunk-F36QRDM4.js";
37
37
  import "./chunk-5QX2NYXL.js";
38
38
  import "./chunk-BIPXOHZI.js";
package/dist/index.cjs CHANGED
@@ -6746,6 +6746,23 @@ function worktreeAgentBlock(group) {
6746
6746
  }
6747
6747
  return best;
6748
6748
  }
6749
+ function sidebarRowAgentBlock(group, opts) {
6750
+ if (opts.active) {
6751
+ const open = group.find((thread) => thread.id === opts.selectedId);
6752
+ if (open) return visibleAgentBlock(open);
6753
+ }
6754
+ return worktreeAgentBlock(group);
6755
+ }
6756
+ function sidebarParentSelectId(group, primaryId, opts) {
6757
+ if (opts.active) return primaryId;
6758
+ return sidebarQuestionThreadId(group, primaryId);
6759
+ }
6760
+ function sidebarQuestionThreadId(group, primaryId) {
6761
+ const block = worktreeAgentBlock(group);
6762
+ if (!block) return primaryId;
6763
+ const owner = group.find((thread) => visibleAgentBlock(thread) === block);
6764
+ return owner?.id ?? primaryId;
6765
+ }
6749
6766
  function threadCardBlockFields(group) {
6750
6767
  const block = worktreeAgentBlock(group);
6751
6768
  return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
@@ -8339,12 +8356,23 @@ function isAskUserToolName(name) {
8339
8356
  const base = name.replace(/^mcp__sideboard__/i, "");
8340
8357
  return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
8341
8358
  }
8359
+ function questionsFromToolPart(part) {
8360
+ const fromInput = parsePlanQuestionsInput(part.input);
8361
+ if (fromInput.length) return fromInput;
8362
+ const raw = part.result?.trim();
8363
+ if (!raw || !raw.startsWith("{")) return [];
8364
+ try {
8365
+ return parsePlanQuestionsInput(JSON.parse(raw));
8366
+ } catch {
8367
+ return [];
8368
+ }
8369
+ }
8342
8370
  function extractPendingPlanQuestions(parts) {
8343
8371
  if (!parts?.length) return null;
8344
8372
  for (let i = parts.length - 1; i >= 0; i--) {
8345
8373
  const p = parts[i];
8346
8374
  if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
8347
- const questions2 = parsePlanQuestionsInput(p.input);
8375
+ const questions2 = questionsFromToolPart(p);
8348
8376
  if (!questions2.length) continue;
8349
8377
  return {
8350
8378
  id: p.id || `ask-${i}`,
@@ -8355,13 +8383,47 @@ function extractPendingPlanQuestions(parts) {
8355
8383
  }
8356
8384
  return null;
8357
8385
  }
8386
+ function pendingFromAskBlock(block, dismissedQuestionSignature) {
8387
+ if (!block || block.source !== "ask_user" || !block.questions?.length) return null;
8388
+ const dismissed = dismissedQuestionSignature?.trim();
8389
+ if (dismissed && (!block.questionSignature || block.questionSignature === dismissed)) {
8390
+ return null;
8391
+ }
8392
+ const signature = block.questionSignature?.trim() || planQuestionsSignature(block.questions);
8393
+ return {
8394
+ id: `ask-block:${signature}`,
8395
+ signature,
8396
+ questions: block.questions,
8397
+ source: "ask_user"
8398
+ };
8399
+ }
8400
+ function askBlockStillWaiting(block, messages) {
8401
+ const askedAt = block?.at;
8402
+ if (!askedAt) return false;
8403
+ for (let i = messages.length - 1; i >= 0; i--) {
8404
+ const message = messages[i];
8405
+ if (message.role !== "user" || message.origin === "continue") continue;
8406
+ if (!message.ts) return true;
8407
+ return askedAt > message.ts;
8408
+ }
8409
+ return true;
8410
+ }
8358
8411
  function latestPendingPlanQuestions(input) {
8359
8412
  if (input.userReplied) return null;
8360
8413
  const fromLive = extractPendingPlanQuestions(input.liveParts);
8361
8414
  if (fromLive) return fromLive;
8362
8415
  const last = input.messages[input.messages.length - 1];
8363
- if (last?.role === "user") return null;
8364
- if (last?.role === "agent") return extractPendingPlanQuestions(last.parts);
8416
+ if (last?.role === "agent") {
8417
+ const fromParts = extractPendingPlanQuestions(last.parts);
8418
+ if (fromParts) return fromParts;
8419
+ }
8420
+ const fromBlock = pendingFromAskBlock(
8421
+ input.agentBlock,
8422
+ input.dismissedQuestionSignature
8423
+ );
8424
+ if (fromBlock && askBlockStillWaiting(input.agentBlock, input.messages)) {
8425
+ return fromBlock;
8426
+ }
8365
8427
  return null;
8366
8428
  }
8367
8429
  function isPlanQuestionAnswersMessage(text7) {
@@ -22161,7 +22223,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
22161
22223
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
22162
22224
  return {
22163
22225
  ...makeAgentBlock("ask_user", reason, at),
22164
- ...pending ? { questionSignature: pending.signature } : {}
22226
+ ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
22165
22227
  };
22166
22228
  }
22167
22229
  if (!planMode || last?.type !== "tool") return null;
@@ -22176,14 +22238,24 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
22176
22238
  at
22177
22239
  );
22178
22240
  }
22241
+ function withStampedQuestions(decision, existing) {
22242
+ if (decision.source !== "ask_user" || decision.questions?.length) return decision;
22243
+ if (existing?.source !== "ask_user" || !existing.questions?.length) return decision;
22244
+ return {
22245
+ ...decision,
22246
+ questions: existing.questions,
22247
+ questionSignature: decision.questionSignature ?? existing.questionSignature
22248
+ };
22249
+ }
22179
22250
  function agentBlockAfterTurn(input) {
22180
22251
  if (input.failed) return null;
22181
22252
  const decision = decisionBlockFromParts(input.parts, input.planMode);
22182
22253
  if (decision) {
22183
- if (isDismissedAskUserBlock(decision, input.dismissedQuestionSignature)) {
22254
+ const next = withStampedQuestions(decision, input.existing);
22255
+ if (isDismissedAskUserBlock(next, input.dismissedQuestionSignature)) {
22184
22256
  return input.existing?.source === "reported" ? input.existing : null;
22185
22257
  }
22186
- return decision;
22258
+ return next;
22187
22259
  }
22188
22260
  if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
22189
22261
  return null;
@@ -22226,7 +22298,7 @@ function settleAgentBlock(threadId, parts, failed) {
22226
22298
  });
22227
22299
  const prev = thread.agentBlock ?? null;
22228
22300
  if (prev === next) return;
22229
- if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature) {
22301
+ if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at && prev.questionSignature === next.questionSignature && planQuestionsSignature(prev.questions ?? []) === planQuestionsSignature(next.questions ?? [])) {
22230
22302
  return;
22231
22303
  }
22232
22304
  updateThread(threadId, { agentBlock: next });
@@ -22249,7 +22321,11 @@ function stampAskUserBlock(resolveCaller, questions2) {
22249
22321
  try {
22250
22322
  writeAgentBlock(
22251
22323
  resolveCaller().id,
22252
- parsed.length ? { ...block, questionSignature: planQuestionsSignature(parsed) } : block
22324
+ parsed.length ? {
22325
+ ...block,
22326
+ questionSignature: planQuestionsSignature(parsed),
22327
+ questions: parsed
22328
+ } : block
22253
22329
  );
22254
22330
  } catch {
22255
22331
  }
@@ -27622,6 +27698,15 @@ function connectedOrchestratorTurnReminder(thread, find) {
27622
27698
  function formatWorktreeReminder() {
27623
27699
  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).";
27624
27700
  }
27701
+ 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.';
27703
+ }
27704
+ function claudeSubagentTurnReminder(thread) {
27705
+ if (thread.agent !== "claude" || thread.planMode || isOrchestratorThread(thread)) {
27706
+ return null;
27707
+ }
27708
+ return formatClaudeSubagentReminder();
27709
+ }
27625
27710
  function mentionsPrGoal(prompt) {
27626
27711
  return PR_GOAL_RE.test(prompt);
27627
27712
  }
@@ -29903,6 +29988,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
29903
29988
  );
29904
29989
  const turnReminders = [
29905
29990
  worktreeReminder,
29991
+ claudeSubagentTurnReminder(thread),
29906
29992
  connectedOrchestratorTurnReminder(thread, (id) => readThread(id)),
29907
29993
  optionalServicesReminder,
29908
29994
  issueToolsReminder,
@@ -32052,6 +32138,7 @@ __export(index_exports, {
32052
32138
  classifyWorktreeListBadges: () => classifyWorktreeListBadges,
32053
32139
  claudeAdapter: () => claudeAdapter,
32054
32140
  claudeChromeEnabled: () => claudeChromeEnabled,
32141
+ claudeSubagentTurnReminder: () => claudeSubagentTurnReminder,
32055
32142
  claudeUsageOverLimitWindows: () => claudeUsageOverLimitWindows,
32056
32143
  claudeUserSettingsPath: () => claudeUserSettingsPath,
32057
32144
  cleanupArchivedHistory: () => cleanupArchivedHistory,
@@ -32203,6 +32290,7 @@ __export(index_exports, {
32203
32290
  formatAskUserNotifyMessage: () => formatAskUserNotifyMessage,
32204
32291
  formatBrightsyFetchError: () => formatBrightsyFetchError,
32205
32292
  formatClaudeExtraUsageDetail: () => formatClaudeExtraUsageDetail,
32293
+ formatClaudeSubagentReminder: () => formatClaudeSubagentReminder,
32206
32294
  formatClaudeUsageCompact: () => formatClaudeUsageCompact,
32207
32295
  formatClaudeUsageFetchedAt: () => formatClaudeUsageFetchedAt,
32208
32296
  formatClaudeUsageOverLimitConfirm: () => formatClaudeUsageOverLimitConfirm,
@@ -32739,6 +32827,9 @@ __export(index_exports, {
32739
32827
  shouldRunWorktreeCleanup: () => shouldRunWorktreeCleanup,
32740
32828
  shouldStampSetupLastError: () => shouldStampSetupLastError,
32741
32829
  showCostEnabled: () => showCostEnabled,
32830
+ sidebarParentSelectId: () => sidebarParentSelectId,
32831
+ sidebarQuestionThreadId: () => sidebarQuestionThreadId,
32832
+ sidebarRowAgentBlock: () => sidebarRowAgentBlock,
32742
32833
  sideboardHomeDir: () => sideboardHomeDir,
32743
32834
  sideboardMcpProfile: () => sideboardMcpProfile,
32744
32835
  sideboardReposDir: () => sideboardReposDir,
@@ -35060,10 +35151,12 @@ function emitChatTurn(opts, chatId, threadId) {
35060
35151
  return;
35061
35152
  }
35062
35153
  const result = getOrchestrator().getTurnResult(thread.id);
35063
- const pending = extractPendingPlanQuestions(
35064
- [...thread.messages].reverse().find((m) => m.role === "agent")?.parts
35065
- );
35066
- if (result.taskState === "input-required" && pending) {
35154
+ const pending = latestPendingPlanQuestions({
35155
+ messages: thread.messages,
35156
+ agentBlock: thread.agentBlock,
35157
+ dismissedQuestionSignature: thread.dismissedQuestionSignature
35158
+ });
35159
+ if (pending && result.taskState !== "failed" && result.taskState !== "canceled" && (result.taskState === "input-required" || thread.agentBlock?.source === "ask_user")) {
35067
35160
  emitControl(opts, {
35068
35161
  op: "ask",
35069
35162
  chatId,
@@ -35430,7 +35523,11 @@ init_ask_user();
35430
35523
  function republishPendingAsk(opts, chatId) {
35431
35524
  const thread = readPhoneThread(chatId);
35432
35525
  if (!thread) return;
35433
- const pending = latestPendingPlanQuestions({ messages: thread.messages });
35526
+ const pending = latestPendingPlanQuestions({
35527
+ messages: thread.messages,
35528
+ agentBlock: thread.agentBlock,
35529
+ dismissedQuestionSignature: thread.dismissedQuestionSignature
35530
+ });
35434
35531
  if (!pending) return;
35435
35532
  emitControl(opts, {
35436
35533
  op: "ask",
@@ -37689,10 +37786,12 @@ function runCoordinatorPrompt(opts, body, generation) {
37689
37786
  thread = readThread(thread.id) ?? thread;
37690
37787
  if (thread.status === "stopped") return;
37691
37788
  const result = orch.getTurnResult(thread.id);
37692
- const pending = extractPendingPlanQuestions(
37693
- [...thread.messages].reverse().find((m) => m.role === "agent")?.parts
37694
- );
37695
- if (result.taskState === "input-required" && pending) {
37789
+ const pending = latestPendingPlanQuestions({
37790
+ messages: thread.messages,
37791
+ agentBlock: thread.agentBlock,
37792
+ dismissedQuestionSignature: thread.dismissedQuestionSignature
37793
+ });
37794
+ if (pending && result.taskState !== "failed" && result.taskState !== "canceled" && (result.taskState === "input-required" || thread.agentBlock?.source === "ask_user")) {
37696
37795
  opts.onOutbound({
37697
37796
  type: "ask_user",
37698
37797
  text: result.text.trim(),
@@ -46899,6 +46998,7 @@ async function pollSlackOutboundWatches(opts) {
46899
46998
  classifyWorktreeListBadges,
46900
46999
  claudeAdapter,
46901
47000
  claudeChromeEnabled,
47001
+ claudeSubagentTurnReminder,
46902
47002
  claudeUsageOverLimitWindows,
46903
47003
  claudeUserSettingsPath,
46904
47004
  cleanupArchivedHistory,
@@ -47050,6 +47150,7 @@ async function pollSlackOutboundWatches(opts) {
47050
47150
  formatAskUserNotifyMessage,
47051
47151
  formatBrightsyFetchError,
47052
47152
  formatClaudeExtraUsageDetail,
47153
+ formatClaudeSubagentReminder,
47053
47154
  formatClaudeUsageCompact,
47054
47155
  formatClaudeUsageFetchedAt,
47055
47156
  formatClaudeUsageOverLimitConfirm,
@@ -47586,6 +47687,9 @@ async function pollSlackOutboundWatches(opts) {
47586
47687
  shouldRunWorktreeCleanup,
47587
47688
  shouldStampSetupLastError,
47588
47689
  showCostEnabled,
47690
+ sidebarParentSelectId,
47691
+ sidebarQuestionThreadId,
47692
+ sidebarRowAgentBlock,
47589
47693
  sideboardHomeDir,
47590
47694
  sideboardMcpProfile,
47591
47695
  sideboardReposDir,