@sideboard-ai/core 0.1.278 → 0.1.280
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-FELFTNH2.js → chunk-2P75E243.js} +320 -39
- package/dist/{chunk-UKSJO36E.js → chunk-CPA43FBK.js} +4 -4
- package/dist/{chunk-SQOAFR2U.js → chunk-DK4DKQ2H.js} +329 -43
- 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-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 +941 -542
- package/dist/index.d.cts +54 -17
- package/dist/index.d.ts +54 -17
- package/dist/index.js +251 -290
- package/dist/mcp/run-stdio.cjs +663 -345
- package/dist/mcp/run-stdio.js +40 -124
- package/dist/{orchestrator-6QJLM6Y4.js → orchestrator-K66OMTQO.js} +5 -5
- package/dist/{orchestrator-QMKVXVUR.js → orchestrator-XEKODYDQ.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
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-LX5R5JOR.js";
|
|
6
6
|
import {
|
|
7
7
|
extractPresentedPlan,
|
|
8
|
+
isPresentPlanToolName,
|
|
8
9
|
readPlanFile,
|
|
9
10
|
writePlanFile
|
|
10
11
|
} from "./chunk-4GK32AYX.js";
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
releaseCaffeinateHoldForThread
|
|
13
14
|
} from "./chunk-5DZUWGKV.js";
|
|
14
15
|
import {
|
|
16
|
+
SIDEBOARD_THREAD_ID_ENV,
|
|
15
17
|
adjustBrightsyTurnExit,
|
|
16
18
|
applyAgentRunnerHeapEnv,
|
|
17
19
|
getAdapter,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
parseSessionQuotaResetAt,
|
|
22
24
|
planModeTurnInstruction,
|
|
23
25
|
resolveQuotaFallbackAgent
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-RK5PIWCW.js";
|
|
25
27
|
import {
|
|
26
28
|
REVIEW_TAB_TITLE,
|
|
27
29
|
applyWorktreePkgCacheEnv,
|
|
@@ -31,12 +33,14 @@ import {
|
|
|
31
33
|
copyConfiguredFiles,
|
|
32
34
|
createChatTab,
|
|
33
35
|
deriveTaskState,
|
|
36
|
+
extractPendingPlanQuestions,
|
|
34
37
|
forkChatTab,
|
|
35
38
|
forkMessageSlice,
|
|
36
39
|
formatReviewWriteGateDirective,
|
|
37
40
|
formatReviewWriteGateReminder,
|
|
38
41
|
getDefaultRunScript,
|
|
39
42
|
getRunMode,
|
|
43
|
+
isAskUserToolName,
|
|
40
44
|
isJobContinuePrompt,
|
|
41
45
|
isReviewWriteGatedThread,
|
|
42
46
|
isWorktreeRunProcessKey,
|
|
@@ -58,7 +62,7 @@ import {
|
|
|
58
62
|
worktreeDevProcessKey,
|
|
59
63
|
worktreeRunProcessKey,
|
|
60
64
|
worktreeSetupProcessKey
|
|
61
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-YDXNCBA2.js";
|
|
62
66
|
import {
|
|
63
67
|
applyAgentEvent,
|
|
64
68
|
clipAgentEventForPaint,
|
|
@@ -84,7 +88,7 @@ import {
|
|
|
84
88
|
summarizeTurnStderr,
|
|
85
89
|
toolDescription,
|
|
86
90
|
turnFailChatText
|
|
87
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-CPA43FBK.js";
|
|
88
92
|
import {
|
|
89
93
|
SLACK_REPLY_FORMATTING,
|
|
90
94
|
addWorkspace,
|
|
@@ -105,7 +109,7 @@ import {
|
|
|
105
109
|
stageAbsolutePathsAsAttachments,
|
|
106
110
|
stageBuffersAsAttachments,
|
|
107
111
|
syncWorkspacesFromThreads
|
|
108
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-TZ5JHIYK.js";
|
|
109
113
|
import {
|
|
110
114
|
addPrStackLayer,
|
|
111
115
|
agentNicknameFromWorktreePath,
|
|
@@ -485,9 +489,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
485
489
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
486
490
|
);
|
|
487
491
|
}
|
|
488
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
492
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-5PJXO45Z.js");
|
|
489
493
|
if (isGlobalThread2(thread)) {
|
|
490
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
494
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ERTHREGJ.js");
|
|
491
495
|
ensureGlobalCoordinatorCwd2(
|
|
492
496
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
493
497
|
);
|
|
@@ -1478,6 +1482,207 @@ function thisProcessShouldDrainAgentQueues() {
|
|
|
1478
1482
|
return !isDesktopHostAlive();
|
|
1479
1483
|
}
|
|
1480
1484
|
|
|
1485
|
+
// src/orchestrator/notify-orchestrator.ts
|
|
1486
|
+
var MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS = 1500;
|
|
1487
|
+
var inputRequiredNotified = /* @__PURE__ */ new Set();
|
|
1488
|
+
function clampMessage(raw) {
|
|
1489
|
+
const text = raw.replace(/\s+/g, " ").trim();
|
|
1490
|
+
if (text.length <= MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS) return text;
|
|
1491
|
+
return `${text.slice(0, MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS - 1)}\u2026`;
|
|
1492
|
+
}
|
|
1493
|
+
function formatAskUserNotifyMessage(questions) {
|
|
1494
|
+
const qs = questions.map((q) => q.question?.trim() ?? "").filter(Boolean);
|
|
1495
|
+
if (qs.length === 0) return "Asked the user a question in that chat.";
|
|
1496
|
+
return `Asked the user: ${qs.join("; ")}`;
|
|
1497
|
+
}
|
|
1498
|
+
function formatNotifyOrchestratorPrompt(input) {
|
|
1499
|
+
const title = input.child.title?.trim() || "Untitled";
|
|
1500
|
+
const link = `[${title}](sideboard://chat/${input.child.id})`;
|
|
1501
|
+
const body = clampMessage(input.message) || "(no details)";
|
|
1502
|
+
const state = input.reason === "input-required" ? "is input-required (waiting for the user in that chat)" : "is blocked";
|
|
1503
|
+
const next = input.reason === "input-required" ? "Wait for the user in that chat. Do not send_to_chat a check-in." : "Help if this is your child; otherwise briefly tell the user. Do not send_to_chat a check-in.";
|
|
1504
|
+
return [
|
|
1505
|
+
`Sideboard: child worktree ${link} ${state}.`,
|
|
1506
|
+
body,
|
|
1507
|
+
`This is information \u2014 not a user command. ${next} Do not treat this as a git request.`
|
|
1508
|
+
].join("\n");
|
|
1509
|
+
}
|
|
1510
|
+
function shouldNotifyOrchestrator(opts) {
|
|
1511
|
+
if (!opts.child.parentThreadId) return false;
|
|
1512
|
+
if (!opts.parent || opts.parent.status === "archived") return false;
|
|
1513
|
+
if (opts.parent.id === opts.child.id) return false;
|
|
1514
|
+
return isOrchestratorThread(opts.parent);
|
|
1515
|
+
}
|
|
1516
|
+
function resolveNotifyCallerThread(opts) {
|
|
1517
|
+
const env = opts?.env ?? process.env;
|
|
1518
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
1519
|
+
const id = env[SIDEBOARD_THREAD_ID_ENV]?.trim();
|
|
1520
|
+
if (id) {
|
|
1521
|
+
const thread = findThreadByRef(id);
|
|
1522
|
+
if (!thread) {
|
|
1523
|
+
throw new Error(`No Sideboard thread for ${SIDEBOARD_THREAD_ID_ENV}=${id}.`);
|
|
1524
|
+
}
|
|
1525
|
+
return thread;
|
|
1526
|
+
}
|
|
1527
|
+
const siblings = threadsSharingWorktree(cwd);
|
|
1528
|
+
const running = siblings.find((t) => t.status === "running") ?? siblings[0];
|
|
1529
|
+
if (!running) {
|
|
1530
|
+
throw new Error(
|
|
1531
|
+
"No Sideboard thread for this cwd. Call notify_orchestrator from a worktree chat."
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
return running;
|
|
1535
|
+
}
|
|
1536
|
+
function skipReason(opts) {
|
|
1537
|
+
if (!opts.child.parentThreadId) {
|
|
1538
|
+
return "This chat has no orchestrator parent. notify_orchestrator is for worktree children of a Global chat.";
|
|
1539
|
+
}
|
|
1540
|
+
if (!opts.parent) {
|
|
1541
|
+
return "Parent orchestration chat was not found.";
|
|
1542
|
+
}
|
|
1543
|
+
if (opts.parent.status === "archived") {
|
|
1544
|
+
return "Parent orchestration chat is archived.";
|
|
1545
|
+
}
|
|
1546
|
+
if (!isOrchestratorThread(opts.parent)) {
|
|
1547
|
+
return "Parent is not a Global orchestration chat.";
|
|
1548
|
+
}
|
|
1549
|
+
return "Cannot notify orchestrator.";
|
|
1550
|
+
}
|
|
1551
|
+
async function notifyOrchestrator(input) {
|
|
1552
|
+
const parent = input.child.parentThreadId ? readThread(input.child.parentThreadId) : null;
|
|
1553
|
+
if (!shouldNotifyOrchestrator({ child: input.child, parent })) {
|
|
1554
|
+
return { ok: false, error: skipReason({ child: input.child, parent }) };
|
|
1555
|
+
}
|
|
1556
|
+
const parentId = parent.id;
|
|
1557
|
+
if (input.reason === "input-required") {
|
|
1558
|
+
if (inputRequiredNotified.has(input.child.id)) {
|
|
1559
|
+
return { ok: true, parentThreadId: parentId, deduped: true };
|
|
1560
|
+
}
|
|
1561
|
+
inputRequiredNotified.add(input.child.id);
|
|
1562
|
+
}
|
|
1563
|
+
const prompt = formatNotifyOrchestratorPrompt({
|
|
1564
|
+
child: input.child,
|
|
1565
|
+
reason: input.reason,
|
|
1566
|
+
message: input.message
|
|
1567
|
+
});
|
|
1568
|
+
try {
|
|
1569
|
+
await input.send(parentId, prompt, { followUp: "queue" });
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
if (input.reason === "input-required") {
|
|
1572
|
+
inputRequiredNotified.delete(input.child.id);
|
|
1573
|
+
}
|
|
1574
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1575
|
+
return { ok: false, error: message };
|
|
1576
|
+
}
|
|
1577
|
+
return { ok: true, parentThreadId: parentId };
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// src/orchestrator/agent-block.ts
|
|
1581
|
+
var MAX_AGENT_BLOCK_REASON = 240;
|
|
1582
|
+
function clampBlockReason(raw) {
|
|
1583
|
+
const text = raw.replace(/\s+/g, " ").trim();
|
|
1584
|
+
if (!text) return "Waiting on a decision";
|
|
1585
|
+
if (text.length <= MAX_AGENT_BLOCK_REASON) return text;
|
|
1586
|
+
return `${text.slice(0, MAX_AGENT_BLOCK_REASON - 1)}\u2026`;
|
|
1587
|
+
}
|
|
1588
|
+
function makeAgentBlock(source, reason, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1589
|
+
return { source, reason: clampBlockReason(reason), at };
|
|
1590
|
+
}
|
|
1591
|
+
function lastTopLevelTool(parts) {
|
|
1592
|
+
if (!parts?.length) return null;
|
|
1593
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1594
|
+
const part = parts[i];
|
|
1595
|
+
if (part.type === "tool" && !part.parentId) return part;
|
|
1596
|
+
}
|
|
1597
|
+
return null;
|
|
1598
|
+
}
|
|
1599
|
+
function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1600
|
+
const last = lastTopLevelTool(parts);
|
|
1601
|
+
if (last?.type === "tool" && isAskUserToolName(last.name)) {
|
|
1602
|
+
const pending = extractPendingPlanQuestions(parts);
|
|
1603
|
+
const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
|
|
1604
|
+
return makeAgentBlock("ask_user", reason, at);
|
|
1605
|
+
}
|
|
1606
|
+
if (!planMode || last?.type !== "tool") return null;
|
|
1607
|
+
const exited = /exitplanmode/i.test(last.name ?? "");
|
|
1608
|
+
if (!isPresentPlanToolName(last.name) && !exited) return null;
|
|
1609
|
+
const presented = extractPresentedPlan(parts);
|
|
1610
|
+
if (!presented && !exited) return null;
|
|
1611
|
+
const title = presented?.title && presented.title !== "Plan" ? presented.title : "";
|
|
1612
|
+
return makeAgentBlock(
|
|
1613
|
+
"plan",
|
|
1614
|
+
title ? `Waiting for plan approval: ${title}` : "Waiting for plan approval",
|
|
1615
|
+
at
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
function agentBlockAfterTurn(input) {
|
|
1619
|
+
if (input.failed) return null;
|
|
1620
|
+
const decision = decisionBlockFromParts(input.parts, input.planMode);
|
|
1621
|
+
if (decision) return decision;
|
|
1622
|
+
if (input.existing && input.existing.source !== "reported" && lastTopLevelTool(input.parts)) {
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
return input.existing ?? null;
|
|
1626
|
+
}
|
|
1627
|
+
function clearAgentBlock(threadId) {
|
|
1628
|
+
const thread = readThread(threadId);
|
|
1629
|
+
if (!thread?.agentBlock) return;
|
|
1630
|
+
updateThread(threadId, { agentBlock: null });
|
|
1631
|
+
}
|
|
1632
|
+
function writeAgentBlock(threadId, block) {
|
|
1633
|
+
const thread = readThread(threadId);
|
|
1634
|
+
if (!thread || thread.status === "archived") return;
|
|
1635
|
+
updateThread(threadId, { agentBlock: block });
|
|
1636
|
+
}
|
|
1637
|
+
function settleAgentBlock(threadId, parts, failed) {
|
|
1638
|
+
const thread = readThread(threadId);
|
|
1639
|
+
if (!thread || thread.status === "archived") return;
|
|
1640
|
+
const next = agentBlockAfterTurn({
|
|
1641
|
+
existing: thread.agentBlock,
|
|
1642
|
+
parts,
|
|
1643
|
+
planMode: Boolean(thread.planMode),
|
|
1644
|
+
failed
|
|
1645
|
+
});
|
|
1646
|
+
const prev = thread.agentBlock ?? null;
|
|
1647
|
+
if (prev === next) return;
|
|
1648
|
+
if (prev && next && prev.source === next.source && prev.reason === next.reason && prev.at === next.at) {
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
updateThread(threadId, { agentBlock: next });
|
|
1652
|
+
}
|
|
1653
|
+
function chatBlockedSuffix(thread) {
|
|
1654
|
+
const raw = thread.agentBlock?.reason;
|
|
1655
|
+
if (!raw?.trim()) return "";
|
|
1656
|
+
return ` blocked:${raw.replace(/\s+/g, " ").slice(0, 80)}`;
|
|
1657
|
+
}
|
|
1658
|
+
function agentBlockFields(thread) {
|
|
1659
|
+
return {
|
|
1660
|
+
blockedReason: thread.agentBlock?.reason ?? null,
|
|
1661
|
+
blockedSource: thread.agentBlock?.source ?? null
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function stampAskUserBlock(resolveCaller, questions) {
|
|
1665
|
+
const asked = formatAskUserNotifyMessage(questions);
|
|
1666
|
+
try {
|
|
1667
|
+
writeAgentBlock(resolveCaller().id, makeAgentBlock("ask_user", asked));
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
return asked;
|
|
1671
|
+
}
|
|
1672
|
+
function stampPlanBlock(threadId, title) {
|
|
1673
|
+
const planTitle = title?.trim() || "Plan";
|
|
1674
|
+
if (threadId) {
|
|
1675
|
+
const reason = planTitle === "Plan" ? "Waiting for plan approval" : `Waiting for plan approval: ${planTitle}`;
|
|
1676
|
+
writeAgentBlock(threadId, makeAgentBlock("plan", reason));
|
|
1677
|
+
}
|
|
1678
|
+
return planTitle;
|
|
1679
|
+
}
|
|
1680
|
+
function stampNotifyBlock(threadId, reason, message) {
|
|
1681
|
+
const resolved = reason ?? "blocked";
|
|
1682
|
+
writeAgentBlock(threadId, makeAgentBlock(resolved === "input-required" ? "ask_user" : "reported", message));
|
|
1683
|
+
return resolved;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1481
1686
|
// src/orchestrator/child-halt.ts
|
|
1482
1687
|
var HALT_STATUSES = /* @__PURE__ */ new Set(["stopped", "error", "broken"]);
|
|
1483
1688
|
function isIncompleteChildStatus(status) {
|
|
@@ -1577,6 +1782,36 @@ async function requireAgent(agent, opts) {
|
|
|
1577
1782
|
return status;
|
|
1578
1783
|
}
|
|
1579
1784
|
|
|
1785
|
+
// src/board/agent-block-view.ts
|
|
1786
|
+
function worktreeBoardStatus(group) {
|
|
1787
|
+
const order = ["running", "queued", "error", "broken"];
|
|
1788
|
+
for (const status of order) {
|
|
1789
|
+
if (group.some((t) => t.status === status)) return status;
|
|
1790
|
+
}
|
|
1791
|
+
return group[0]?.status ?? "idle";
|
|
1792
|
+
}
|
|
1793
|
+
function visibleAgentBlock(thread) {
|
|
1794
|
+
if (thread.status === "archived" || thread.status === "error" || thread.status === "broken") {
|
|
1795
|
+
return null;
|
|
1796
|
+
}
|
|
1797
|
+
const block = thread.agentBlock;
|
|
1798
|
+
if (!block?.reason?.trim()) return null;
|
|
1799
|
+
return block;
|
|
1800
|
+
}
|
|
1801
|
+
function worktreeAgentBlock(group) {
|
|
1802
|
+
let best = null;
|
|
1803
|
+
for (const thread of group) {
|
|
1804
|
+
const block = visibleAgentBlock(thread);
|
|
1805
|
+
if (!block) continue;
|
|
1806
|
+
if (!best || block.at > best.at) best = block;
|
|
1807
|
+
}
|
|
1808
|
+
return best;
|
|
1809
|
+
}
|
|
1810
|
+
function threadCardBlockFields(group) {
|
|
1811
|
+
const block = worktreeAgentBlock(group);
|
|
1812
|
+
return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1580
1815
|
// src/board/home-board.ts
|
|
1581
1816
|
var GLOBAL_WORKSPACE_ID = "__global__";
|
|
1582
1817
|
function isHomeBoardThread(thread) {
|
|
@@ -1692,13 +1927,6 @@ function classifyWorktreeColumn(group) {
|
|
|
1692
1927
|
}
|
|
1693
1928
|
return "new";
|
|
1694
1929
|
}
|
|
1695
|
-
function worktreeBoardStatus(group) {
|
|
1696
|
-
const order = ["running", "queued", "error", "broken"];
|
|
1697
|
-
for (const status of order) {
|
|
1698
|
-
if (group.some((t) => t.status === status)) return status;
|
|
1699
|
-
}
|
|
1700
|
-
return group[0]?.status ?? "idle";
|
|
1701
|
-
}
|
|
1702
1930
|
function normalizeIssueKey(value) {
|
|
1703
1931
|
return value.trim().toLowerCase().replace(/^#/, "");
|
|
1704
1932
|
}
|
|
@@ -1988,7 +2216,8 @@ function toThreadCard(group) {
|
|
|
1988
2216
|
repoPath: thread.repoPath,
|
|
1989
2217
|
prUrl: withPr.prUrl,
|
|
1990
2218
|
link: `sideboard://chat/${thread.id}`,
|
|
1991
|
-
...group.length > 1 ? { chatCount: group.length } : {}
|
|
2219
|
+
...group.length > 1 ? { chatCount: group.length } : {},
|
|
2220
|
+
...threadCardBlockFields(group) ?? {}
|
|
1992
2221
|
};
|
|
1993
2222
|
}
|
|
1994
2223
|
function threadMatchesKind(thread, kind) {
|
|
@@ -2062,7 +2291,7 @@ function assembleHomeBoard(input) {
|
|
|
2062
2291
|
}
|
|
2063
2292
|
return { columns, hidden, totals };
|
|
2064
2293
|
}
|
|
2065
|
-
var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling
|
|
2294
|
+
var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
|
|
2066
2295
|
|
|
2067
2296
|
// src/threads/create.ts
|
|
2068
2297
|
function persistCreateAttachments(worktreePath, attachments) {
|
|
@@ -2780,7 +3009,7 @@ async function adoptThread(input) {
|
|
|
2780
3009
|
messages: input.messages ?? []
|
|
2781
3010
|
});
|
|
2782
3011
|
writeThread(thread);
|
|
2783
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
3012
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WU2RNLDV.js");
|
|
2784
3013
|
await ensureWorkspace2(repoPath);
|
|
2785
3014
|
const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-5N2KTYIT.js");
|
|
2786
3015
|
await ensureWorktreeSideboardIgnored(input.worktreePath);
|
|
@@ -4214,10 +4443,49 @@ async function confirmLand(thread, opts) {
|
|
|
4214
4443
|
}
|
|
4215
4444
|
}
|
|
4216
4445
|
|
|
4446
|
+
// src/diff/read-worktree-tail.ts
|
|
4447
|
+
import { closeSync, fstatSync, openSync, readSync } from "fs";
|
|
4448
|
+
import { join as join9 } from "path";
|
|
4449
|
+
var WORKTREE_TAIL_BYTES = 2e5;
|
|
4450
|
+
function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TAIL_BYTES) {
|
|
4451
|
+
if (!relativePath || relativePath.includes("..") || relativePath.startsWith("/")) {
|
|
4452
|
+
throw new Error("Invalid path");
|
|
4453
|
+
}
|
|
4454
|
+
const abs = join9(worktreePath, relativePath);
|
|
4455
|
+
const fd = openSync(abs, "r");
|
|
4456
|
+
try {
|
|
4457
|
+
const st = fstatSync(fd);
|
|
4458
|
+
if (!st.isFile()) throw new Error(`Not a file: ${relativePath}`);
|
|
4459
|
+
const size = st.size;
|
|
4460
|
+
const start = Math.max(0, size - maxBytes);
|
|
4461
|
+
const length = size - start;
|
|
4462
|
+
const buf = Buffer.alloc(length);
|
|
4463
|
+
if (length > 0) readSync(fd, buf, 0, length, start);
|
|
4464
|
+
if (buf.subarray(0, Math.min(buf.length, 8e3)).includes(0)) {
|
|
4465
|
+
return {
|
|
4466
|
+
path: relativePath,
|
|
4467
|
+
content: `(binary file, ${size} bytes)`,
|
|
4468
|
+
truncated: false,
|
|
4469
|
+
binary: true,
|
|
4470
|
+
encoding: "utf8"
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
let content = buf.toString("utf8");
|
|
4474
|
+
const truncated = start > 0;
|
|
4475
|
+
if (truncated) {
|
|
4476
|
+
const nl = content.indexOf("\n");
|
|
4477
|
+
if (nl >= 0) content = content.slice(nl + 1);
|
|
4478
|
+
}
|
|
4479
|
+
return { path: relativePath, content, truncated, binary: false, encoding: "utf8" };
|
|
4480
|
+
} finally {
|
|
4481
|
+
closeSync(fd);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
|
|
4217
4485
|
// src/skills/discover.ts
|
|
4218
4486
|
import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
4219
4487
|
import { homedir } from "os";
|
|
4220
|
-
import { join as
|
|
4488
|
+
import { join as join10 } from "path";
|
|
4221
4489
|
|
|
4222
4490
|
// src/skills/bundled/long-running.ts
|
|
4223
4491
|
var LONG_RUNNING_SKILL_COMMAND = "long-running";
|
|
@@ -4386,7 +4654,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4386
4654
|
}
|
|
4387
4655
|
for (const entry of entries) {
|
|
4388
4656
|
if (entry.startsWith(".")) continue;
|
|
4389
|
-
const skillMd =
|
|
4657
|
+
const skillMd = join10(dir, entry, "SKILL.md");
|
|
4390
4658
|
if (!existsSync10(skillMd)) continue;
|
|
4391
4659
|
try {
|
|
4392
4660
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -4408,12 +4676,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
4408
4676
|
return;
|
|
4409
4677
|
}
|
|
4410
4678
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
4411
|
-
const skill = readSkill(
|
|
4679
|
+
const skill = readSkill(join10(dir, "SKILL.md"), "cli");
|
|
4412
4680
|
if (skill) out.push(skill);
|
|
4413
4681
|
}
|
|
4414
4682
|
for (const entry of entries) {
|
|
4415
4683
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
4416
|
-
const full =
|
|
4684
|
+
const full = join10(dir, entry);
|
|
4417
4685
|
try {
|
|
4418
4686
|
if (!statSync3(full).isDirectory()) continue;
|
|
4419
4687
|
} catch {
|
|
@@ -4433,17 +4701,17 @@ function discoverSkills(worktreePath) {
|
|
|
4433
4701
|
const home = homedir();
|
|
4434
4702
|
const collected = [];
|
|
4435
4703
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
4436
|
-
scanSkillsDir(
|
|
4704
|
+
scanSkillsDir(join10(worktreePath, rel), "workspace", collected);
|
|
4437
4705
|
}
|
|
4438
4706
|
for (const abs of [
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4707
|
+
join10(home, ".claude/skills"),
|
|
4708
|
+
join10(home, ".cursor/skills"),
|
|
4709
|
+
join10(home, ".sideboard/skills"),
|
|
4710
|
+
join10(home, ".brightsy/skills")
|
|
4443
4711
|
]) {
|
|
4444
4712
|
scanSkillsDir(abs, "user", collected);
|
|
4445
4713
|
}
|
|
4446
|
-
scanClaudePluginSkills(
|
|
4714
|
+
scanClaudePluginSkills(join10(home, ".claude/plugins"), collected);
|
|
4447
4715
|
collected.push(...bundledSkills());
|
|
4448
4716
|
const rank = {
|
|
4449
4717
|
workspace: 0,
|
|
@@ -4615,11 +4883,11 @@ function shiftQueuedItem(queue, queueAttachments) {
|
|
|
4615
4883
|
|
|
4616
4884
|
// src/agents/instructions.ts
|
|
4617
4885
|
import { existsSync as existsSync12 } from "fs";
|
|
4618
|
-
import { join as
|
|
4886
|
+
import { join as join12 } from "path";
|
|
4619
4887
|
|
|
4620
4888
|
// src/skills/detached-job-path.ts
|
|
4621
4889
|
import { existsSync as existsSync11 } from "fs";
|
|
4622
|
-
import { dirname as dirname2, join as
|
|
4890
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
4623
4891
|
import { fileURLToPath } from "url";
|
|
4624
4892
|
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
4625
4893
|
function compiledModuleDir() {
|
|
@@ -4636,7 +4904,7 @@ function compiledModuleDir() {
|
|
|
4636
4904
|
}
|
|
4637
4905
|
function detachedJobScriptIn(dir) {
|
|
4638
4906
|
for (const name of DETACHED_JOB_FILENAMES) {
|
|
4639
|
-
const script =
|
|
4907
|
+
const script = join11(dir, "scripts", name);
|
|
4640
4908
|
if (existsSync11(script)) return script;
|
|
4641
4909
|
}
|
|
4642
4910
|
return null;
|
|
@@ -4855,9 +5123,9 @@ function formatPrGateDirective() {
|
|
|
4855
5123
|
}
|
|
4856
5124
|
function formatProcessGuideDirective(opts) {
|
|
4857
5125
|
const worktree = opts?.worktreePath?.trim();
|
|
4858
|
-
const hasGraphSkill = !!worktree && existsSync12(
|
|
4859
|
-
const hasReviewSkill = !!worktree && existsSync12(
|
|
4860
|
-
const hasInstallSkill = !!worktree && existsSync12(
|
|
5126
|
+
const hasGraphSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/graph-engineering/SKILL.md"));
|
|
5127
|
+
const hasReviewSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/review/SKILL.md"));
|
|
5128
|
+
const hasInstallSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/worktree-install/SKILL.md"));
|
|
4861
5129
|
return [
|
|
4862
5130
|
"Process guides (recurring work only):",
|
|
4863
5131
|
hasGraphSkill ? "- Migrations, ports, batch fixes, and other fan-out: follow `.claude/skills/graph-engineering/SKILL.md` (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch around it." : null,
|
|
@@ -5023,7 +5291,7 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5023
5291
|
// src/store/schedules.ts
|
|
5024
5292
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5025
5293
|
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
5026
|
-
import { join as
|
|
5294
|
+
import { join as join13 } from "path";
|
|
5027
5295
|
import { Cron } from "croner";
|
|
5028
5296
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
5029
5297
|
var UNIT_MS = {
|
|
@@ -5033,7 +5301,7 @@ var UNIT_MS = {
|
|
|
5033
5301
|
d: 864e5
|
|
5034
5302
|
};
|
|
5035
5303
|
function schedulesPath() {
|
|
5036
|
-
return
|
|
5304
|
+
return join13(appDataDir(), "schedules.json");
|
|
5037
5305
|
}
|
|
5038
5306
|
function parseDurationMs(every) {
|
|
5039
5307
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -5271,7 +5539,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
5271
5539
|
${prompt}`;
|
|
5272
5540
|
}
|
|
5273
5541
|
async function defaultDeps() {
|
|
5274
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
5542
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-K66OMTQO.js");
|
|
5275
5543
|
const orch = getOrchestrator2();
|
|
5276
5544
|
return {
|
|
5277
5545
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -6249,6 +6517,7 @@ var Orchestrator = class {
|
|
|
6249
6517
|
const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
|
|
6250
6518
|
const parked = takePendingTurnAttachments(thread);
|
|
6251
6519
|
const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
|
|
6520
|
+
if (!autoContinue) clearAgentBlock(threadId);
|
|
6252
6521
|
appendMessage(threadId, {
|
|
6253
6522
|
role: "user",
|
|
6254
6523
|
text: promptText,
|
|
@@ -6654,6 +6923,7 @@ var Orchestrator = class {
|
|
|
6654
6923
|
} else {
|
|
6655
6924
|
await syncThreadBranchFromGit(threadId);
|
|
6656
6925
|
}
|
|
6926
|
+
settleAgentBlock(threadId, parts, exitCode !== 0 && !this.stoppedTurns.has(threadId));
|
|
6657
6927
|
if (this.stoppedTurns.has(threadId)) {
|
|
6658
6928
|
const stopped = writeLiveStatus(threadId, "stopped");
|
|
6659
6929
|
if (stopped?.status === "stopped") {
|
|
@@ -6704,6 +6974,7 @@ var Orchestrator = class {
|
|
|
6704
6974
|
} catch (err) {
|
|
6705
6975
|
const message = err instanceof Error ? err.message : String(err);
|
|
6706
6976
|
await syncThreadBranchFromGit(threadId).catch(() => void 0);
|
|
6977
|
+
settleAgentBlock(threadId, void 0, !this.stoppedTurns.has(threadId));
|
|
6707
6978
|
if (this.stoppedTurns.has(threadId)) {
|
|
6708
6979
|
const stopped = writeLiveStatus(threadId, "stopped");
|
|
6709
6980
|
if (stopped?.status === "stopped") {
|
|
@@ -7419,6 +7690,7 @@ var Orchestrator = class {
|
|
|
7419
7690
|
stillRunning,
|
|
7420
7691
|
lastAgentParts: lastAgent?.parts
|
|
7421
7692
|
});
|
|
7693
|
+
const block = thread.agentBlock?.reason?.trim() ? thread.agentBlock : null;
|
|
7422
7694
|
return {
|
|
7423
7695
|
text,
|
|
7424
7696
|
status: thread.status,
|
|
@@ -7432,7 +7704,9 @@ var Orchestrator = class {
|
|
|
7432
7704
|
lastAgent?.ts,
|
|
7433
7705
|
thread.updatedAt
|
|
7434
7706
|
]),
|
|
7435
|
-
usage: stillRunning ? null : lastAgent?.usage ?? null
|
|
7707
|
+
usage: stillRunning ? null : lastAgent?.usage ?? null,
|
|
7708
|
+
blockedReason: block?.reason ?? null,
|
|
7709
|
+
blockedSource: block?.source ?? null
|
|
7436
7710
|
};
|
|
7437
7711
|
}
|
|
7438
7712
|
assertNotGlobal(thread, action) {
|
|
@@ -7493,12 +7767,12 @@ var Orchestrator = class {
|
|
|
7493
7767
|
return "missing";
|
|
7494
7768
|
}
|
|
7495
7769
|
}
|
|
7496
|
-
async readFile(threadRef, relativePath) {
|
|
7770
|
+
async readFile(threadRef, relativePath, tail) {
|
|
7497
7771
|
const thread = this.requireThread(threadRef);
|
|
7498
7772
|
if (relativePath.includes("..") || relativePath.startsWith("/")) {
|
|
7499
7773
|
throw new Error("Invalid path");
|
|
7500
7774
|
}
|
|
7501
|
-
return readWorktreeFile(thread.worktreePath, relativePath);
|
|
7775
|
+
return (tail ? readWorktreeFileTail : readWorktreeFile)(thread.worktreePath, relativePath);
|
|
7502
7776
|
}
|
|
7503
7777
|
async readFileForUpload(threadRef, relativePath) {
|
|
7504
7778
|
const thread = this.requireThread(threadRef);
|
|
@@ -7959,7 +8233,7 @@ var Orchestrator = class {
|
|
|
7959
8233
|
this.enforceHistoryRetention();
|
|
7960
8234
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
7961
8235
|
try {
|
|
7962
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
8236
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WU2RNLDV.js");
|
|
7963
8237
|
await ensureWorkspace2(thread.repoPath);
|
|
7964
8238
|
} catch {
|
|
7965
8239
|
}
|
|
@@ -8150,6 +8424,13 @@ export {
|
|
|
8150
8424
|
MAX_RUN_LOG_CHARS,
|
|
8151
8425
|
DEFAULT_RUN_LOG_TAIL_CHARS,
|
|
8152
8426
|
sliceRunLogOutput,
|
|
8427
|
+
resolveNotifyCallerThread,
|
|
8428
|
+
notifyOrchestrator,
|
|
8429
|
+
chatBlockedSuffix,
|
|
8430
|
+
agentBlockFields,
|
|
8431
|
+
stampAskUserBlock,
|
|
8432
|
+
stampPlanBlock,
|
|
8433
|
+
stampNotifyBlock,
|
|
8153
8434
|
BOARD_COLUMN_DEFS,
|
|
8154
8435
|
HOME_BOARD_CACHE_TTL_MS,
|
|
8155
8436
|
dedupeBoardIssues,
|
|
@@ -199,8 +199,8 @@ function looksLikeInvalidAgentSession(text) {
|
|
|
199
199
|
if (!lower) return false;
|
|
200
200
|
return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower) || /\brun\b.{0,80}\bnot found for agent\b/.test(lower);
|
|
201
201
|
}
|
|
202
|
-
function
|
|
203
|
-
return /nghttp2/i.test(text.trim());
|
|
202
|
+
function looksLikeCursorStreamCutOff(text) {
|
|
203
|
+
return /nghttp2|premature close|\[unavailable\]\s+error\b/i.test(text.trim());
|
|
204
204
|
}
|
|
205
205
|
function looksLikeBrightsyEmptyCompletion(text) {
|
|
206
206
|
const core = text.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
|
|
@@ -211,7 +211,7 @@ function looksLikeRetryableRunnerCrash(text) {
|
|
|
211
211
|
if (looksLikeInvalidAgentSession(text)) return false;
|
|
212
212
|
if (looksLikeV8Oom(text)) return false;
|
|
213
213
|
if (looksLikeHugeToolResultDump(text)) return true;
|
|
214
|
-
if (
|
|
214
|
+
if (looksLikeCursorStreamCutOff(text)) return true;
|
|
215
215
|
if (looksLikeBrightsyEmptyCompletion(text)) return true;
|
|
216
216
|
const lower = text.trim().toLowerCase();
|
|
217
217
|
if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
|
|
@@ -288,7 +288,7 @@ function humanizeAgentFailDetail(detail) {
|
|
|
288
288
|
if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
|
|
289
289
|
return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
|
|
290
290
|
}
|
|
291
|
-
if (/connection stalled/.test(lower) ||
|
|
291
|
+
if (/connection stalled/.test(lower) || looksLikeCursorStreamCutOff(raw)) {
|
|
292
292
|
return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
|
|
293
293
|
}
|
|
294
294
|
if (looksLikeBrightsyEmptyCompletion(raw)) {
|