@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.
- package/dist/agents/cursor-runner.cjs +3 -3
- package/dist/agents/cursor-runner.js +5 -5
- package/dist/{agents-2FOWVJCN.js → agents-DYCFLDNL.js} +5 -5
- package/dist/{agents-JBVJRDH3.js → agents-RXP5NZBC.js} +3 -3
- package/dist/{chunk-UKSJO36E.js → chunk-CPA43FBK.js} +4 -4
- package/dist/{chunk-FELFTNH2.js → chunk-CPGJ5ICZ.js} +261 -19
- package/dist/{chunk-NIB2IZ6J.js → chunk-HNNS25SG.js} +5 -5
- package/dist/{chunk-MGS5T2A3.js → chunk-HQXBBUQX.js} +161 -9
- package/dist/{chunk-FIBZEFKA.js → chunk-IFURHRAR.js} +1 -1
- package/dist/{chunk-5N4Q46SG.js → chunk-K2S33ZCB.js} +5 -5
- package/dist/{chunk-NT7RIWRR.js → chunk-RK5PIWCW.js} +3 -3
- package/dist/{chunk-A2G3ZBJS.js → chunk-SKSOCIJT.js} +8 -8
- package/dist/{chunk-QTZO4LPU.js → chunk-TZ5JHIYK.js} +8 -8
- package/dist/{chunk-27Z4MEQ5.js → chunk-UZUXWBXK.js} +1 -1
- package/dist/{chunk-SQOAFR2U.js → chunk-V5DMHB6Y.js} +270 -23
- package/dist/{chunk-FAGK33E2.js → chunk-Y7JPL4WA.js} +1 -1
- package/dist/{chunk-HOMZKYHF.js → chunk-YDXNCBA2.js} +107 -8
- package/dist/{conductor-YYQKZAND.js → conductor-J6WDOXWM.js} +3 -3
- package/dist/{conductor-GBCE37K4.js → conductor-OW6NAK3N.js} +4 -4
- package/dist/{coordinator-prompt-SFZCZ3HR.js → coordinator-prompt-ERTHREGJ.js} +1 -1
- package/dist/{coordinator-prompt-B3GLVXPP.js → coordinator-prompt-Z2D32HWK.js} +1 -1
- package/dist/{global-workspace-7XEXTIHL.js → global-workspace-5PJXO45Z.js} +1 -1
- package/dist/{global-workspace-N2YYJKA5.js → global-workspace-AK5PSPQ2.js} +1 -1
- package/dist/index.cjs +798 -445
- package/dist/index.d.cts +52 -15
- package/dist/index.d.ts +52 -15
- package/dist/index.js +251 -290
- package/dist/mcp/run-stdio.cjs +529 -257
- package/dist/mcp/run-stdio.js +40 -124
- package/dist/{orchestrator-6QJLM6Y4.js → orchestrator-E5WK57DA.js} +5 -5
- package/dist/{orchestrator-QMKVXVUR.js → orchestrator-KY2KBBE2.js} +7 -7
- package/dist/{workspaces-PBB6HY2D.js → workspaces-UP6DQ3WL.js} +1 -1
- package/dist/{workspaces-BJCHMGVG.js → workspaces-WU2RNLDV.js} +1 -1
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
444
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-AK5PSPQ2.js");
|
|
441
445
|
if (isGlobalThread2(thread)) {
|
|
442
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
compactSummaryPart,
|
|
6
6
|
isInternalAgentStatusText,
|
|
7
7
|
lastAssistantMessageText
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CPA43FBK.js";
|
|
9
9
|
import {
|
|
10
10
|
isCloudCoordinatorThread,
|
|
11
11
|
isGlobalThread,
|
|
12
12
|
isOrchestratorThread,
|
|
13
13
|
isSlackCoordinatorThread
|
|
14
|
-
} from "./chunk-
|
|
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
|
|
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
|
-
|
|
1248
|
-
|
|
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-
|
|
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-
|
|
37
|
-
import "./chunk-
|
|
38
|
-
import "./chunk-
|
|
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";
|