@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
|
@@ -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);
|
|
@@ -4360,10 +4589,49 @@ async function confirmLand(thread, opts) {
|
|
|
4360
4589
|
}
|
|
4361
4590
|
}
|
|
4362
4591
|
|
|
4592
|
+
// src/diff/read-worktree-tail.ts
|
|
4593
|
+
import { closeSync, fstatSync, openSync, readSync } from "fs";
|
|
4594
|
+
import { join as join9 } from "path";
|
|
4595
|
+
var WORKTREE_TAIL_BYTES = 2e5;
|
|
4596
|
+
function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TAIL_BYTES) {
|
|
4597
|
+
if (!relativePath || relativePath.includes("..") || relativePath.startsWith("/")) {
|
|
4598
|
+
throw new Error("Invalid path");
|
|
4599
|
+
}
|
|
4600
|
+
const abs = join9(worktreePath, relativePath);
|
|
4601
|
+
const fd = openSync(abs, "r");
|
|
4602
|
+
try {
|
|
4603
|
+
const st = fstatSync(fd);
|
|
4604
|
+
if (!st.isFile()) throw new Error(`Not a file: ${relativePath}`);
|
|
4605
|
+
const size = st.size;
|
|
4606
|
+
const start = Math.max(0, size - maxBytes);
|
|
4607
|
+
const length = size - start;
|
|
4608
|
+
const buf = Buffer.alloc(length);
|
|
4609
|
+
if (length > 0) readSync(fd, buf, 0, length, start);
|
|
4610
|
+
if (buf.subarray(0, Math.min(buf.length, 8e3)).includes(0)) {
|
|
4611
|
+
return {
|
|
4612
|
+
path: relativePath,
|
|
4613
|
+
content: `(binary file, ${size} bytes)`,
|
|
4614
|
+
truncated: false,
|
|
4615
|
+
binary: true,
|
|
4616
|
+
encoding: "utf8"
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
let content = buf.toString("utf8");
|
|
4620
|
+
const truncated = start > 0;
|
|
4621
|
+
if (truncated) {
|
|
4622
|
+
const nl = content.indexOf("\n");
|
|
4623
|
+
if (nl >= 0) content = content.slice(nl + 1);
|
|
4624
|
+
}
|
|
4625
|
+
return { path: relativePath, content, truncated, binary: false, encoding: "utf8" };
|
|
4626
|
+
} finally {
|
|
4627
|
+
closeSync(fd);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4363
4631
|
// src/skills/discover.ts
|
|
4364
4632
|
import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
4365
4633
|
import { homedir } from "os";
|
|
4366
|
-
import { join as
|
|
4634
|
+
import { join as join10 } from "path";
|
|
4367
4635
|
|
|
4368
4636
|
// src/skills/bundled/long-running.ts
|
|
4369
4637
|
var LONG_RUNNING_SKILL_COMMAND = "long-running";
|
|
@@ -4532,7 +4800,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4532
4800
|
}
|
|
4533
4801
|
for (const entry of entries) {
|
|
4534
4802
|
if (entry.startsWith(".")) continue;
|
|
4535
|
-
const skillMd =
|
|
4803
|
+
const skillMd = join10(dir, entry, "SKILL.md");
|
|
4536
4804
|
if (!existsSync10(skillMd)) continue;
|
|
4537
4805
|
try {
|
|
4538
4806
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -4554,12 +4822,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
4554
4822
|
return;
|
|
4555
4823
|
}
|
|
4556
4824
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
4557
|
-
const skill = readSkill(
|
|
4825
|
+
const skill = readSkill(join10(dir, "SKILL.md"), "cli");
|
|
4558
4826
|
if (skill) out.push(skill);
|
|
4559
4827
|
}
|
|
4560
4828
|
for (const entry of entries) {
|
|
4561
4829
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
4562
|
-
const full =
|
|
4830
|
+
const full = join10(dir, entry);
|
|
4563
4831
|
try {
|
|
4564
4832
|
if (!statSync3(full).isDirectory()) continue;
|
|
4565
4833
|
} catch {
|
|
@@ -4579,17 +4847,17 @@ function discoverSkills(worktreePath) {
|
|
|
4579
4847
|
const home = homedir();
|
|
4580
4848
|
const collected = [];
|
|
4581
4849
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
4582
|
-
scanSkillsDir(
|
|
4850
|
+
scanSkillsDir(join10(worktreePath, rel), "workspace", collected);
|
|
4583
4851
|
}
|
|
4584
4852
|
for (const abs of [
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4853
|
+
join10(home, ".claude/skills"),
|
|
4854
|
+
join10(home, ".cursor/skills"),
|
|
4855
|
+
join10(home, ".sideboard/skills"),
|
|
4856
|
+
join10(home, ".brightsy/skills")
|
|
4589
4857
|
]) {
|
|
4590
4858
|
scanSkillsDir(abs, "user", collected);
|
|
4591
4859
|
}
|
|
4592
|
-
scanClaudePluginSkills(
|
|
4860
|
+
scanClaudePluginSkills(join10(home, ".claude/plugins"), collected);
|
|
4593
4861
|
collected.push(...bundledSkills());
|
|
4594
4862
|
const rank = {
|
|
4595
4863
|
workspace: 0,
|
|
@@ -4761,11 +5029,11 @@ function shiftQueuedItem(queue, queueAttachments) {
|
|
|
4761
5029
|
|
|
4762
5030
|
// src/agents/instructions.ts
|
|
4763
5031
|
import { existsSync as existsSync12 } from "fs";
|
|
4764
|
-
import { join as
|
|
5032
|
+
import { join as join12 } from "path";
|
|
4765
5033
|
|
|
4766
5034
|
// src/skills/detached-job-path.ts
|
|
4767
5035
|
import { existsSync as existsSync11 } from "fs";
|
|
4768
|
-
import { dirname as dirname2, join as
|
|
5036
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
4769
5037
|
import { fileURLToPath } from "url";
|
|
4770
5038
|
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
4771
5039
|
function compiledModuleDir() {
|
|
@@ -4782,7 +5050,7 @@ function compiledModuleDir() {
|
|
|
4782
5050
|
}
|
|
4783
5051
|
function detachedJobScriptIn(dir) {
|
|
4784
5052
|
for (const name of DETACHED_JOB_FILENAMES) {
|
|
4785
|
-
const script =
|
|
5053
|
+
const script = join11(dir, "scripts", name);
|
|
4786
5054
|
if (existsSync11(script)) return script;
|
|
4787
5055
|
}
|
|
4788
5056
|
return null;
|
|
@@ -5023,9 +5291,9 @@ function formatPrGateDirective() {
|
|
|
5023
5291
|
}
|
|
5024
5292
|
function formatProcessGuideDirective(opts) {
|
|
5025
5293
|
const worktree = opts?.worktreePath?.trim();
|
|
5026
|
-
const hasGraphSkill = !!worktree && existsSync12(
|
|
5027
|
-
const hasReviewSkill = !!worktree && existsSync12(
|
|
5028
|
-
const hasInstallSkill = !!worktree && existsSync12(
|
|
5294
|
+
const hasGraphSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/graph-engineering/SKILL.md"));
|
|
5295
|
+
const hasReviewSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/review/SKILL.md"));
|
|
5296
|
+
const hasInstallSkill = !!worktree && existsSync12(join12(worktree, ".claude/skills/worktree-install/SKILL.md"));
|
|
5029
5297
|
return [
|
|
5030
5298
|
"Process guides (recurring work only):",
|
|
5031
5299
|
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,
|
|
@@ -5353,7 +5621,7 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5353
5621
|
// src/store/schedules.ts
|
|
5354
5622
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5355
5623
|
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
5356
|
-
import { join as
|
|
5624
|
+
import { join as join13 } from "path";
|
|
5357
5625
|
import { Cron } from "croner";
|
|
5358
5626
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
5359
5627
|
var UNIT_MS = {
|
|
@@ -5363,7 +5631,7 @@ var UNIT_MS = {
|
|
|
5363
5631
|
d: 864e5
|
|
5364
5632
|
};
|
|
5365
5633
|
function schedulesPath() {
|
|
5366
|
-
return
|
|
5634
|
+
return join13(appDataDir(), "schedules.json");
|
|
5367
5635
|
}
|
|
5368
5636
|
function parseDurationMs(every) {
|
|
5369
5637
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -5610,7 +5878,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
5610
5878
|
${prompt}`;
|
|
5611
5879
|
}
|
|
5612
5880
|
async function defaultDeps() {
|
|
5613
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
5881
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-XEKODYDQ.js");
|
|
5614
5882
|
const orch = getOrchestrator2();
|
|
5615
5883
|
return {
|
|
5616
5884
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -6588,6 +6856,7 @@ var Orchestrator = class {
|
|
|
6588
6856
|
const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
|
|
6589
6857
|
const parked = takePendingTurnAttachments(thread);
|
|
6590
6858
|
const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
|
|
6859
|
+
if (!autoContinue) clearAgentBlock(threadId);
|
|
6591
6860
|
appendMessage(threadId, {
|
|
6592
6861
|
role: "user",
|
|
6593
6862
|
text: promptText,
|
|
@@ -6993,6 +7262,7 @@ var Orchestrator = class {
|
|
|
6993
7262
|
} else {
|
|
6994
7263
|
await syncThreadBranchFromGit(threadId);
|
|
6995
7264
|
}
|
|
7265
|
+
settleAgentBlock(threadId, parts, exitCode !== 0 && !this.stoppedTurns.has(threadId));
|
|
6996
7266
|
if (this.stoppedTurns.has(threadId)) {
|
|
6997
7267
|
const stopped = writeLiveStatus(threadId, "stopped");
|
|
6998
7268
|
if (stopped?.status === "stopped") {
|
|
@@ -7043,6 +7313,7 @@ var Orchestrator = class {
|
|
|
7043
7313
|
} catch (err) {
|
|
7044
7314
|
const message = err instanceof Error ? err.message : String(err);
|
|
7045
7315
|
await syncThreadBranchFromGit(threadId).catch(() => void 0);
|
|
7316
|
+
settleAgentBlock(threadId, void 0, !this.stoppedTurns.has(threadId));
|
|
7046
7317
|
if (this.stoppedTurns.has(threadId)) {
|
|
7047
7318
|
const stopped = writeLiveStatus(threadId, "stopped");
|
|
7048
7319
|
if (stopped?.status === "stopped") {
|
|
@@ -7758,6 +8029,7 @@ var Orchestrator = class {
|
|
|
7758
8029
|
stillRunning,
|
|
7759
8030
|
lastAgentParts: lastAgent?.parts
|
|
7760
8031
|
});
|
|
8032
|
+
const block = thread.agentBlock?.reason?.trim() ? thread.agentBlock : null;
|
|
7761
8033
|
return {
|
|
7762
8034
|
text,
|
|
7763
8035
|
status: thread.status,
|
|
@@ -7771,7 +8043,9 @@ var Orchestrator = class {
|
|
|
7771
8043
|
lastAgent?.ts,
|
|
7772
8044
|
thread.updatedAt
|
|
7773
8045
|
]),
|
|
7774
|
-
usage: stillRunning ? null : lastAgent?.usage ?? null
|
|
8046
|
+
usage: stillRunning ? null : lastAgent?.usage ?? null,
|
|
8047
|
+
blockedReason: block?.reason ?? null,
|
|
8048
|
+
blockedSource: block?.source ?? null
|
|
7775
8049
|
};
|
|
7776
8050
|
}
|
|
7777
8051
|
assertNotGlobal(thread, action) {
|
|
@@ -7832,12 +8106,12 @@ var Orchestrator = class {
|
|
|
7832
8106
|
return "missing";
|
|
7833
8107
|
}
|
|
7834
8108
|
}
|
|
7835
|
-
async readFile(threadRef, relativePath) {
|
|
8109
|
+
async readFile(threadRef, relativePath, tail) {
|
|
7836
8110
|
const thread = this.requireThread(threadRef);
|
|
7837
8111
|
if (relativePath.includes("..") || relativePath.startsWith("/")) {
|
|
7838
8112
|
throw new Error("Invalid path");
|
|
7839
8113
|
}
|
|
7840
|
-
return readWorktreeFile(thread.worktreePath, relativePath);
|
|
8114
|
+
return (tail ? readWorktreeFileTail : readWorktreeFile)(thread.worktreePath, relativePath);
|
|
7841
8115
|
}
|
|
7842
8116
|
async readFileForUpload(threadRef, relativePath) {
|
|
7843
8117
|
const thread = this.requireThread(threadRef);
|
|
@@ -8298,7 +8572,7 @@ var Orchestrator = class {
|
|
|
8298
8572
|
this.enforceHistoryRetention();
|
|
8299
8573
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
8300
8574
|
try {
|
|
8301
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
8575
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UP6DQ3WL.js");
|
|
8302
8576
|
await ensureWorkspace2(thread.repoPath);
|
|
8303
8577
|
} catch {
|
|
8304
8578
|
}
|
|
@@ -8486,6 +8760,9 @@ async function startOrchestration(opts) {
|
|
|
8486
8760
|
export {
|
|
8487
8761
|
isUnclaimedRunScriptRequest,
|
|
8488
8762
|
isAdoptableRunScriptRequest,
|
|
8763
|
+
worktreeBoardStatus,
|
|
8764
|
+
visibleAgentBlock,
|
|
8765
|
+
worktreeAgentBlock,
|
|
8489
8766
|
isHomeBoardThread,
|
|
8490
8767
|
BOARD_COLUMN_DEFS,
|
|
8491
8768
|
HOME_BOARD_CACHE_TTL_MS,
|
|
@@ -8494,7 +8771,6 @@ export {
|
|
|
8494
8771
|
classifyWorktreeColumn,
|
|
8495
8772
|
reviewLabelForRepo,
|
|
8496
8773
|
classifyWorktreeListBadges,
|
|
8497
|
-
worktreeBoardStatus,
|
|
8498
8774
|
dedupeBoardIssues,
|
|
8499
8775
|
issueNeedsWorkspacePick,
|
|
8500
8776
|
isAccountWideIssueSource,
|
|
@@ -8556,6 +8832,16 @@ export {
|
|
|
8556
8832
|
shouldRunHistoryCleanup,
|
|
8557
8833
|
applyThreadIntoMain,
|
|
8558
8834
|
cloneRepoIntoSideboard,
|
|
8835
|
+
formatAskUserNotifyMessage,
|
|
8836
|
+
formatNotifyOrchestratorPrompt,
|
|
8837
|
+
shouldNotifyOrchestrator,
|
|
8838
|
+
resolveNotifyCallerThread,
|
|
8839
|
+
notifyOrchestrator,
|
|
8840
|
+
chatBlockedSuffix,
|
|
8841
|
+
agentBlockFields,
|
|
8842
|
+
stampAskUserBlock,
|
|
8843
|
+
stampPlanBlock,
|
|
8844
|
+
stampNotifyBlock,
|
|
8559
8845
|
detectAgents,
|
|
8560
8846
|
requireAgent,
|
|
8561
8847
|
createThread,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
looksLikeSkillBody,
|
|
10
10
|
skillCommandFromBody,
|
|
11
11
|
withEventParentId
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-Y7JPL4WA.js";
|
|
13
13
|
import {
|
|
14
14
|
isOrchestratorThread
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-SKSOCIJT.js";
|
|
16
16
|
import {
|
|
17
17
|
codexUnattendedGitConfigArgs,
|
|
18
18
|
mergeAgentGitAuthEnv,
|
|
@@ -38,13 +38,13 @@ import {
|
|
|
38
38
|
packagedMcpStdioPath,
|
|
39
39
|
parseCursorRunnerLine,
|
|
40
40
|
resolveNodeLaunch
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-IFURHRAR.js";
|
|
42
42
|
import {
|
|
43
43
|
extractJsonErrorMessage,
|
|
44
44
|
formatUnknownDetail,
|
|
45
45
|
looksLikeAgentFailureMessage,
|
|
46
46
|
looksLikeBrightsyEmptyCompletion
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-K2S33ZCB.js";
|
|
48
48
|
import {
|
|
49
49
|
enrichPathWithNpmGlobalBin,
|
|
50
50
|
isConductorBundledCli,
|
|
@@ -1745,7 +1745,7 @@ var claudeAdapter = {
|
|
|
1745
1745
|
);
|
|
1746
1746
|
}
|
|
1747
1747
|
const mode = permissionMode(thread);
|
|
1748
|
-
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-
|
|
1748
|
+
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-AK5PSPQ2.js");
|
|
1749
1749
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
1750
1750
|
const rpc = worktreeAgentRpc(isOrchestrator);
|
|
1751
1751
|
const injectedServers = await buildInjectedMcpServers({
|