@sideboard-ai/core 0.1.280 → 0.1.281
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/{abletime-VCVPSLSU.js → abletime-WLDBJJUI.js} +3 -3
- package/dist/{abletime-VRATUMDO.js → abletime-X64E3GUJ.js} +3 -3
- package/dist/{abletime-oauth-4K7MICAD.js → abletime-oauth-AXR676W5.js} +2 -2
- package/dist/{abletime-oauth-4C4G2J3R.js → abletime-oauth-UWOCEICP.js} +2 -2
- package/dist/{abletime-rest-PIBCNHF3.js → abletime-rest-LPUDJUEP.js} +2 -2
- package/dist/{abletime-rest-6N63ZVQ3.js → abletime-rest-S4XWEXV2.js} +2 -2
- package/dist/{agents-DYCFLDNL.js → agents-2IE4HKHA.js} +5 -5
- package/dist/{agents-RXP5NZBC.js → agents-XA2E64AW.js} +5 -5
- package/dist/{chunk-DK4DKQ2H.js → chunk-2KNO2GJQ.js} +722 -139
- package/dist/{chunk-TZ5JHIYK.js → chunk-2SJXQ4UX.js} +68 -21
- package/dist/{chunk-K7MWJLR7.js → chunk-5MGLNVNK.js} +6 -906
- package/dist/{chunk-RK5PIWCW.js → chunk-ADXVTKLC.js} +12 -4
- package/dist/{chunk-Y7JPL4WA.js → chunk-BDAUPNHL.js} +3 -4
- package/dist/{chunk-R7HLXD4I.js → chunk-BS3QLVPL.js} +93 -57
- package/dist/{chunk-2P75E243.js → chunk-BYZ7F2BQ.js} +707 -128
- package/dist/{chunk-2J34G3WJ.js → chunk-DGPROCE5.js} +93 -57
- package/dist/{chunk-HQXBBUQX.js → chunk-DQZPF6WL.js} +12 -11
- package/dist/{chunk-YDXNCBA2.js → chunk-EIEAR62M.js} +12 -11
- package/dist/{chunk-XKZAVGTT.js → chunk-I6HSGKMZ.js} +1 -1
- package/dist/chunk-JPH7PYSK.js +1241 -0
- package/dist/{chunk-IPPY3FG5.js → chunk-K5XSTHHP.js} +6 -906
- package/dist/chunk-N5UGX3JL.js +1239 -0
- package/dist/{chunk-HNNS25SG.js → chunk-OCUIZD4Y.js} +14 -4
- package/dist/{chunk-IJDQKC5A.js → chunk-PY4UCMRI.js} +4 -4
- package/dist/{chunk-NQ7JMVSU.js → chunk-U7HAVAGJ.js} +1 -1
- package/dist/{chunk-PZURG3XN.js → chunk-VA55JG2I.js} +4 -4
- package/dist/{chunk-CPA43FBK.js → chunk-W7HIK2A6.js} +3 -4
- package/dist/{chunk-SKSOCIJT.js → chunk-WY2IAB7J.js} +68 -21
- package/dist/{chunk-QINJKNLR.js → chunk-XTPQEZZA.js} +2 -2
- package/dist/{conductor-J6WDOXWM.js → conductor-CNSCOC54.js} +5 -5
- package/dist/{conductor-OW6NAK3N.js → conductor-U776JSXQ.js} +5 -5
- package/dist/{coordinator-prompt-Z2D32HWK.js → coordinator-prompt-4DJIHSE6.js} +3 -3
- package/dist/{coordinator-prompt-ERTHREGJ.js → coordinator-prompt-SVVGU6AZ.js} +3 -3
- package/dist/{global-workspace-5PJXO45Z.js → global-workspace-UPUDWIS6.js} +3 -3
- package/dist/{global-workspace-AK5PSPQ2.js → global-workspace-XON4YZXE.js} +3 -3
- package/dist/index.cjs +3690 -2912
- package/dist/index.d.cts +101 -28
- package/dist/index.d.ts +101 -28
- package/dist/index.js +1013 -1001
- package/dist/mcp/run-stdio.cjs +1975 -1259
- package/dist/mcp/run-stdio.js +743 -785
- package/dist/{orchestrator-XEKODYDQ.js → orchestrator-FPGVKEID.js} +7 -7
- package/dist/{orchestrator-K66OMTQO.js → orchestrator-HWJHK5NL.js} +7 -7
- package/dist/{thread-store-B3ZM4QEZ.js → thread-store-7XBOUQ5K.js} +1 -1
- package/dist/{thread-store-SHCUBWB3.js → thread-store-ZQZDREVH.js} +1 -1
- package/dist/{workspaces-UP6DQ3WL.js → workspaces-7AJ5RZ3P.js} +7 -3
- package/dist/{workspaces-WU2RNLDV.js → workspaces-F76BWCSI.js} +7 -3
- package/dist/{worktree-L57T3KH7.js → worktree-NKLNVNXX.js} +20 -15
- package/dist/{worktree-Q6UJHG25.js → worktree-YTZC3UQN.js} +14 -9
- package/package.json +1 -1
- package/dist/chunk-FWNL62VA.js +0 -308
- package/dist/chunk-JANCPF7Z.js +0 -310
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
parseSessionQuotaResetAt,
|
|
9
9
|
planModeTurnInstruction,
|
|
10
10
|
resolveQuotaFallbackAgent
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-OCUIZD4Y.js";
|
|
12
12
|
import {
|
|
13
13
|
REVIEW_TAB_TITLE,
|
|
14
14
|
applyWorktreePkgCacheEnv,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
worktreeDevProcessKey,
|
|
48
48
|
worktreeRunProcessKey,
|
|
49
49
|
worktreeSetupProcessKey
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-DQZPF6WL.js";
|
|
51
51
|
import {
|
|
52
52
|
applyAgentEvent,
|
|
53
53
|
clipAgentEventForPaint,
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
partsToAssistantText,
|
|
63
63
|
stripBrightsyNdjsonNoise,
|
|
64
64
|
toolDescription
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-BDAUPNHL.js";
|
|
66
66
|
import {
|
|
67
67
|
titleFromPrompt
|
|
68
68
|
} from "./chunk-7H655NEW.js";
|
|
@@ -86,16 +86,18 @@ import {
|
|
|
86
86
|
isGlobalRepoPath,
|
|
87
87
|
isGlobalThread,
|
|
88
88
|
isOrchestratorThread,
|
|
89
|
+
isRemovedWorkspace,
|
|
90
|
+
listWorkspaces,
|
|
89
91
|
orchestratorSessionPoisonedByBuiltins,
|
|
90
92
|
persistPendingFileAttachments,
|
|
91
93
|
removeWorkspace,
|
|
94
|
+
sameWorkspacePath,
|
|
92
95
|
stageAbsolutePathsAsAttachments,
|
|
93
96
|
stageBuffersAsAttachments,
|
|
94
97
|
syncWorkspacesFromThreads
|
|
95
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-WY2IAB7J.js";
|
|
96
99
|
import {
|
|
97
100
|
addPrStackLayer,
|
|
98
|
-
agentNicknameFromWorktreePath,
|
|
99
101
|
allocateTeamSlug,
|
|
100
102
|
canonicalizeRepoPath,
|
|
101
103
|
ciChecksFailed,
|
|
@@ -120,14 +122,11 @@ import {
|
|
|
120
122
|
getPrStack,
|
|
121
123
|
initPrStack,
|
|
122
124
|
isDirty,
|
|
123
|
-
isPlaceholderBranch,
|
|
124
125
|
isSideboardScratchPath,
|
|
125
|
-
isWorktreeIdentityTitle,
|
|
126
126
|
listWorktrees,
|
|
127
127
|
markPrReady,
|
|
128
128
|
mergeAgentGitAuthEnv,
|
|
129
129
|
mergePr,
|
|
130
|
-
normalizeWorktreePath,
|
|
131
130
|
pushBranch,
|
|
132
131
|
removeWorktree,
|
|
133
132
|
resolveAgentGitAuthEnv,
|
|
@@ -137,26 +136,32 @@ import {
|
|
|
137
136
|
resolvePrSelector,
|
|
138
137
|
resolveRepoRoot,
|
|
139
138
|
switchWorktreeBranch,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
withRepoGitLock,
|
|
143
|
-
worktreeDisplayLabelForGroup,
|
|
144
|
-
worktreeIdentityTitles,
|
|
145
|
-
worktreeNameFromPath,
|
|
146
|
-
worktreeSlugForTicket
|
|
147
|
-
} from "./chunk-IPPY3FG5.js";
|
|
139
|
+
withRepoGitLock
|
|
140
|
+
} from "./chunk-K5XSTHHP.js";
|
|
148
141
|
import {
|
|
142
|
+
agentNicknameFromWorktreePath,
|
|
149
143
|
appendMessage,
|
|
150
144
|
createEmptyThread,
|
|
151
145
|
deleteThreadRecord,
|
|
152
146
|
findThreadByRef,
|
|
147
|
+
isPlaceholderBranch,
|
|
148
|
+
isWorktreeIdentityTitle,
|
|
153
149
|
listThreads,
|
|
150
|
+
normalizeWorkspaceTags,
|
|
151
|
+
normalizeWorktreePath,
|
|
154
152
|
readThread,
|
|
155
153
|
setStatus,
|
|
154
|
+
teamSlugFromName,
|
|
155
|
+
ticketSlugForBranch,
|
|
156
156
|
updateThread,
|
|
157
157
|
withThreadLock,
|
|
158
|
+
workspaceTagsFromGroup,
|
|
159
|
+
worktreeDisplayLabelForGroup,
|
|
160
|
+
worktreeIdentityTitles,
|
|
161
|
+
worktreeNameFromPath,
|
|
162
|
+
worktreeSlugForTicket,
|
|
158
163
|
writeThread
|
|
159
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-N5UGX3JL.js";
|
|
160
165
|
import {
|
|
161
166
|
httpFetch
|
|
162
167
|
} from "./chunk-N7WZ6NIN.js";
|
|
@@ -210,6 +215,7 @@ import {
|
|
|
210
215
|
orchestrationQuotaFallbackAgent,
|
|
211
216
|
planHistoryAgePurge,
|
|
212
217
|
planHistoryRetention,
|
|
218
|
+
readViewerContext,
|
|
213
219
|
resolveEffectiveIssueSource,
|
|
214
220
|
resolveNewThreadOptions,
|
|
215
221
|
resolveOrchestratorDefaults,
|
|
@@ -217,8 +223,10 @@ import {
|
|
|
217
223
|
saveAppSettings,
|
|
218
224
|
sumUsageList,
|
|
219
225
|
updateAdvancedSettings,
|
|
226
|
+
updateAppEnvironment,
|
|
220
227
|
usageOnLimit,
|
|
221
|
-
withTurnScopedCost
|
|
228
|
+
withTurnScopedCost,
|
|
229
|
+
writeViewerContext
|
|
222
230
|
} from "./chunk-YNTXWFXT.js";
|
|
223
231
|
import {
|
|
224
232
|
examplePrefixedBranch,
|
|
@@ -251,11 +259,11 @@ import {
|
|
|
251
259
|
import { EventEmitter } from "events";
|
|
252
260
|
|
|
253
261
|
// src/threads/injected-notices.ts
|
|
254
|
-
function isSlackExternalReplyText(
|
|
255
|
-
return
|
|
262
|
+
function isSlackExternalReplyText(text3) {
|
|
263
|
+
return text3.startsWith("Slack reply from ") && text3.includes("not a command");
|
|
256
264
|
}
|
|
257
|
-
function isInjectedNoticeText(
|
|
258
|
-
return isSlackExternalReplyText(
|
|
265
|
+
function isInjectedNoticeText(text3) {
|
|
266
|
+
return isSlackExternalReplyText(text3);
|
|
259
267
|
}
|
|
260
268
|
function pendingInjectedNotices(messages, match = isInjectedNoticeText) {
|
|
261
269
|
let i = messages.length - 1;
|
|
@@ -289,7 +297,7 @@ function formatInjectedNoticesForTurn(notices) {
|
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
// src/orchestrator/orchestrator.ts
|
|
292
|
-
import { randomUUID as
|
|
300
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
293
301
|
import { existsSync as existsSync14 } from "fs";
|
|
294
302
|
|
|
295
303
|
// src/agents/spawn.ts
|
|
@@ -336,8 +344,8 @@ function textSoFar(parts, parentId) {
|
|
|
336
344
|
}
|
|
337
345
|
return out;
|
|
338
346
|
}
|
|
339
|
-
function replaySized(
|
|
340
|
-
return
|
|
347
|
+
function replaySized(text3) {
|
|
348
|
+
return text3.trim().length >= REPLAY_MIN_CHARS;
|
|
341
349
|
}
|
|
342
350
|
function tailAfterAccumulated(accumulated, incoming) {
|
|
343
351
|
if (incoming.startsWith(accumulated)) return incoming.slice(accumulated.length);
|
|
@@ -441,9 +449,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
441
449
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
442
450
|
);
|
|
443
451
|
}
|
|
444
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
452
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-XON4YZXE.js");
|
|
445
453
|
if (isGlobalThread2(thread)) {
|
|
446
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
454
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-4DJIHSE6.js");
|
|
447
455
|
ensureGlobalCoordinatorCwd2(
|
|
448
456
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
449
457
|
);
|
|
@@ -585,10 +593,10 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
585
593
|
outbound.flush();
|
|
586
594
|
const finalized = finalizeParts(parts);
|
|
587
595
|
const rawText = assistantText.trim() || partsToAssistantText(finalized);
|
|
588
|
-
const
|
|
589
|
-
const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null,
|
|
596
|
+
const text3 = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
|
|
597
|
+
const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null, text3) : result.exitCode ?? null;
|
|
590
598
|
onEvent({ type: "exit", data: exitCode });
|
|
591
|
-
return { exitCode, sessionId, assistantText:
|
|
599
|
+
return { exitCode, sessionId, assistantText: text3, parts: finalized, usage };
|
|
592
600
|
});
|
|
593
601
|
return {
|
|
594
602
|
pid: child.pid,
|
|
@@ -1026,8 +1034,8 @@ function sliceRunLogOutput(output, tailChars = DEFAULT_RUN_LOG_TAIL_CHARS) {
|
|
|
1026
1034
|
if (outputChars <= max) return { output, truncated: false, outputChars };
|
|
1027
1035
|
const cut = output.slice(outputChars - max);
|
|
1028
1036
|
const nl = cut.indexOf("\n");
|
|
1029
|
-
const
|
|
1030
|
-
return { output:
|
|
1037
|
+
const text3 = nl >= 0 && nl < cut.length - 1 ? cut.slice(nl + 1) : cut;
|
|
1038
|
+
return { output: text3, truncated: true, outputChars };
|
|
1031
1039
|
}
|
|
1032
1040
|
var PERSIST_DEBOUNCE_MS2 = 500;
|
|
1033
1041
|
function persistNow2(key) {
|
|
@@ -1397,7 +1405,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
1397
1405
|
const dest = join5(sideboardReposDir(), name);
|
|
1398
1406
|
if (existsSync4(dest)) {
|
|
1399
1407
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
1400
|
-
const workspace2 = await
|
|
1408
|
+
const workspace2 = await addWorkspace(repoPath2);
|
|
1401
1409
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
1402
1410
|
}
|
|
1403
1411
|
const result = await execa3("git", ["clone", url, dest], { reject: false });
|
|
@@ -1407,7 +1415,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
1407
1415
|
);
|
|
1408
1416
|
}
|
|
1409
1417
|
const repoPath = await resolveRepoRoot(dest);
|
|
1410
|
-
const workspace = await
|
|
1418
|
+
const workspace = await addWorkspace(repoPath);
|
|
1411
1419
|
return { repoPath, workspace };
|
|
1412
1420
|
}
|
|
1413
1421
|
|
|
@@ -1469,9 +1477,9 @@ function thisProcessShouldDrainAgentQueues() {
|
|
|
1469
1477
|
var MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS = 1500;
|
|
1470
1478
|
var inputRequiredNotified = /* @__PURE__ */ new Set();
|
|
1471
1479
|
function clampMessage(raw) {
|
|
1472
|
-
const
|
|
1473
|
-
if (
|
|
1474
|
-
return `${
|
|
1480
|
+
const text3 = raw.replace(/\s+/g, " ").trim();
|
|
1481
|
+
if (text3.length <= MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS) return text3;
|
|
1482
|
+
return `${text3.slice(0, MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS - 1)}\u2026`;
|
|
1475
1483
|
}
|
|
1476
1484
|
function formatAskUserNotifyMessage(questions) {
|
|
1477
1485
|
const qs = questions.map((q) => q.question?.trim() ?? "").filter(Boolean);
|
|
@@ -1563,10 +1571,10 @@ async function notifyOrchestrator(input) {
|
|
|
1563
1571
|
// src/orchestrator/agent-block.ts
|
|
1564
1572
|
var MAX_AGENT_BLOCK_REASON = 240;
|
|
1565
1573
|
function clampBlockReason(raw) {
|
|
1566
|
-
const
|
|
1567
|
-
if (!
|
|
1568
|
-
if (
|
|
1569
|
-
return `${
|
|
1574
|
+
const text3 = raw.replace(/\s+/g, " ").trim();
|
|
1575
|
+
if (!text3) return "Waiting on a decision";
|
|
1576
|
+
if (text3.length <= MAX_AGENT_BLOCK_REASON) return text3;
|
|
1577
|
+
return `${text3.slice(0, MAX_AGENT_BLOCK_REASON - 1)}\u2026`;
|
|
1570
1578
|
}
|
|
1571
1579
|
function makeAgentBlock(source, reason, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1572
1580
|
return { source, reason: clampBlockReason(reason), at };
|
|
@@ -1708,6 +1716,45 @@ function notifyParentOfChildHalt(child, status, send) {
|
|
|
1708
1716
|
return true;
|
|
1709
1717
|
}
|
|
1710
1718
|
|
|
1719
|
+
// src/orchestrator/stop-reason.ts
|
|
1720
|
+
var STOP_REASON_MAX = 500;
|
|
1721
|
+
function resolveStopReason(opts) {
|
|
1722
|
+
const explicit = opts?.reason?.trim();
|
|
1723
|
+
if (explicit) return explicit.slice(0, STOP_REASON_MAX);
|
|
1724
|
+
if (opts?.continueQueue) return "Stopped to send the next prompt";
|
|
1725
|
+
if (opts?.clearQueue === false) return "Stopped by the user";
|
|
1726
|
+
return "Stopped (queue cleared)";
|
|
1727
|
+
}
|
|
1728
|
+
function reasonToKeepOnStop(existing, incoming) {
|
|
1729
|
+
const next = incoming?.trim();
|
|
1730
|
+
if (next) return next.slice(0, STOP_REASON_MAX);
|
|
1731
|
+
const prior = existing?.trim();
|
|
1732
|
+
if (prior) return prior.slice(0, STOP_REASON_MAX);
|
|
1733
|
+
return "Stopped";
|
|
1734
|
+
}
|
|
1735
|
+
function writeStoppedStatus(threadId, reason) {
|
|
1736
|
+
const latest = readThread(threadId);
|
|
1737
|
+
if (!latest || latest.status === "archived") return latest;
|
|
1738
|
+
return setStatus(threadId, "stopped", reasonToKeepOnStop(latest.lastError, reason));
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
// src/orchestrator/steer-request.ts
|
|
1742
|
+
import { randomUUID } from "crypto";
|
|
1743
|
+
function isUnclaimedSteerRequest(req) {
|
|
1744
|
+
return Boolean(req?.requestId && !req.claimedAt);
|
|
1745
|
+
}
|
|
1746
|
+
function nextSteerRequest(existing) {
|
|
1747
|
+
if (existing?.requestId && !existing.claimedAt) return existing;
|
|
1748
|
+
return { requestId: randomUUID(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1749
|
+
}
|
|
1750
|
+
function claimSteerRequest(threadId) {
|
|
1751
|
+
const pending = readThread(threadId)?.steerRequest;
|
|
1752
|
+
if (!pending?.requestId || pending.claimedAt) return;
|
|
1753
|
+
updateThread(threadId, {
|
|
1754
|
+
steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1711
1758
|
// src/threads/agent-purpose-title.ts
|
|
1712
1759
|
function agentPurposeStamp(input) {
|
|
1713
1760
|
if (input.autoContinue) return null;
|
|
@@ -2238,7 +2285,8 @@ function threadSearchText(thread, workspaceName = "") {
|
|
|
2238
2285
|
thread.branchName,
|
|
2239
2286
|
thread.prUrl ?? "",
|
|
2240
2287
|
workspaceName,
|
|
2241
|
-
thread.repoPath
|
|
2288
|
+
thread.repoPath,
|
|
2289
|
+
...thread.tags ?? []
|
|
2242
2290
|
].join(" ");
|
|
2243
2291
|
}
|
|
2244
2292
|
function inWorkspace(repoPath, filterRepoPath) {
|
|
@@ -2252,8 +2300,8 @@ function visiblePage(items, shown) {
|
|
|
2252
2300
|
hidden: Math.max(0, items.length - n)
|
|
2253
2301
|
};
|
|
2254
2302
|
}
|
|
2255
|
-
function markdownPreviewSource(
|
|
2256
|
-
const trimmed =
|
|
2303
|
+
function markdownPreviewSource(text3, max = 480) {
|
|
2304
|
+
const trimmed = text3.trim();
|
|
2257
2305
|
if (trimmed.length <= max) return trimmed;
|
|
2258
2306
|
const slice = trimmed.slice(0, max);
|
|
2259
2307
|
const nl = slice.lastIndexOf("\n");
|
|
@@ -2270,13 +2318,13 @@ function latestVisibleMessageText(messages, liveParts) {
|
|
|
2270
2318
|
if (!m) continue;
|
|
2271
2319
|
if (m.role === "agent") {
|
|
2272
2320
|
const fromParts = lastAssistantMessageText(m.parts);
|
|
2273
|
-
const
|
|
2274
|
-
if (
|
|
2321
|
+
const text3 = (fromParts || m.text || "").trim();
|
|
2322
|
+
if (text3 && !isInternalAgentStatusText(text3)) return text3;
|
|
2275
2323
|
continue;
|
|
2276
2324
|
}
|
|
2277
2325
|
if (m.role === "user") {
|
|
2278
|
-
const
|
|
2279
|
-
if (
|
|
2326
|
+
const text3 = m.text?.trim();
|
|
2327
|
+
if (text3) return text3;
|
|
2280
2328
|
}
|
|
2281
2329
|
}
|
|
2282
2330
|
return "";
|
|
@@ -2326,6 +2374,7 @@ function findBoardPr(prs, ref, repoPath = "") {
|
|
|
2326
2374
|
function toThreadCard(group) {
|
|
2327
2375
|
const thread = group[0];
|
|
2328
2376
|
const withPr = group.find((t) => t.prUrl?.trim()) ?? thread;
|
|
2377
|
+
const tags = workspaceTagsFromGroup(group);
|
|
2329
2378
|
return {
|
|
2330
2379
|
kind: "thread",
|
|
2331
2380
|
id: thread.id,
|
|
@@ -2338,7 +2387,8 @@ function toThreadCard(group) {
|
|
|
2338
2387
|
prUrl: withPr.prUrl,
|
|
2339
2388
|
link: `sideboard://chat/${thread.id}`,
|
|
2340
2389
|
...group.length > 1 ? { chatCount: group.length } : {},
|
|
2341
|
-
...threadCardBlockFields(group) ?? {}
|
|
2390
|
+
...threadCardBlockFields(group) ?? {},
|
|
2391
|
+
...tags.length > 0 ? { tags } : {}
|
|
2342
2392
|
};
|
|
2343
2393
|
}
|
|
2344
2394
|
function threadMatchesKind(thread, kind) {
|
|
@@ -2414,6 +2464,51 @@ function assembleHomeBoard(input) {
|
|
|
2414
2464
|
}
|
|
2415
2465
|
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.";
|
|
2416
2466
|
|
|
2467
|
+
// src/threads/workspace-tags.ts
|
|
2468
|
+
function workspaceTagTargetRef(opts) {
|
|
2469
|
+
const requested = opts.requestedRef?.trim() || "";
|
|
2470
|
+
if (!opts.ownWorktreeOnly) {
|
|
2471
|
+
if (!requested) throw new Error("Pass a chat id on the workspace to label.");
|
|
2472
|
+
return requested;
|
|
2473
|
+
}
|
|
2474
|
+
const caller = opts.caller;
|
|
2475
|
+
if (!caller || isGlobalThread(caller) || caller.sourceType === "orchestration") {
|
|
2476
|
+
throw new Error("Tags belong on a project workspace. This chat is not one.");
|
|
2477
|
+
}
|
|
2478
|
+
if (!requested || requested === caller.id) return caller.id;
|
|
2479
|
+
const other = opts.find(requested);
|
|
2480
|
+
if (!other || normalizeWorktreePath(other.worktreePath) !== normalizeWorktreePath(caller.worktreePath)) {
|
|
2481
|
+
throw new Error("You can only change tags on this workspace.");
|
|
2482
|
+
}
|
|
2483
|
+
return other.id;
|
|
2484
|
+
}
|
|
2485
|
+
function applyWorkspaceTags(threadRef, tags, mode = "replace") {
|
|
2486
|
+
const thread = findThreadByRef(threadRef);
|
|
2487
|
+
if (!thread) throw new Error(`Thread not found: ${threadRef}`);
|
|
2488
|
+
if (isGlobalThread(thread) || thread.sourceType === "orchestration") {
|
|
2489
|
+
throw new Error("Tags belong on a project workspace. Pass a worktree chat id.");
|
|
2490
|
+
}
|
|
2491
|
+
const incoming = normalizeWorkspaceTags(tags);
|
|
2492
|
+
const siblings = threadsSharingWorktree(thread.worktreePath);
|
|
2493
|
+
const current = workspaceTagsFromGroup(siblings.length > 0 ? siblings : [thread]);
|
|
2494
|
+
let next;
|
|
2495
|
+
if (mode === "add") {
|
|
2496
|
+
next = normalizeWorkspaceTags([...current, ...incoming]);
|
|
2497
|
+
} else if (mode === "remove") {
|
|
2498
|
+
const drop = new Set(incoming.map((tag) => tag.toLowerCase()));
|
|
2499
|
+
next = current.filter((tag) => !drop.has(tag.toLowerCase()));
|
|
2500
|
+
} else {
|
|
2501
|
+
next = incoming;
|
|
2502
|
+
}
|
|
2503
|
+
const targets = siblings.length > 0 ? siblings : [thread];
|
|
2504
|
+
let updated = thread;
|
|
2505
|
+
for (const row of targets) {
|
|
2506
|
+
const written = updateThread(row.id, { tags: next });
|
|
2507
|
+
if (written.id === thread.id) updated = written;
|
|
2508
|
+
}
|
|
2509
|
+
return updated;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2417
2512
|
// src/threads/create.ts
|
|
2418
2513
|
function persistCreateAttachments(worktreePath, attachments) {
|
|
2419
2514
|
return persistPendingFileAttachments(worktreePath, attachments ?? []);
|
|
@@ -2439,20 +2534,26 @@ function reuseLiveThread(input, repoPath, match) {
|
|
|
2439
2534
|
liveThreadsForCreate()
|
|
2440
2535
|
);
|
|
2441
2536
|
if (!existing) return void 0;
|
|
2442
|
-
|
|
2443
|
-
if (
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2537
|
+
let thread = readThread(existing.id) ?? existing;
|
|
2538
|
+
if (input.attachments?.length) {
|
|
2539
|
+
thread = updateThread(thread.id, {
|
|
2540
|
+
attachments: persistCreateAttachments(thread.worktreePath, [
|
|
2541
|
+
...thread.attachments,
|
|
2542
|
+
...input.attachments
|
|
2543
|
+
])
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
if (normalizeWorkspaceTags(input.tags).length > 0) {
|
|
2547
|
+
thread = applyWorkspaceTags(thread.id, input.tags, "add");
|
|
2548
|
+
}
|
|
2549
|
+
return thread;
|
|
2450
2550
|
}
|
|
2451
2551
|
async function createThread(input, _onSetupLine) {
|
|
2452
2552
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
2453
2553
|
if (!existsSync5(repoPath)) {
|
|
2454
2554
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
2455
2555
|
}
|
|
2556
|
+
if (isRemovedWorkspace(repoPath)) throw new Error(`Project was removed: ${repoPath}`);
|
|
2456
2557
|
const reused = reuseLiveThread(input, repoPath, {
|
|
2457
2558
|
sourceType: input.sourceType,
|
|
2458
2559
|
sourceRef: input.sourceRef,
|
|
@@ -2511,7 +2612,8 @@ async function createThread(input, _onSetupLine) {
|
|
|
2511
2612
|
sourceIsFork: false,
|
|
2512
2613
|
parentThreadId: input.parentThreadId ?? null,
|
|
2513
2614
|
status: "idle",
|
|
2514
|
-
cowboy: true
|
|
2615
|
+
cowboy: true,
|
|
2616
|
+
tags: input.tags
|
|
2515
2617
|
});
|
|
2516
2618
|
writeThread(thread2);
|
|
2517
2619
|
await ensureWorkspace(repoPath);
|
|
@@ -2525,7 +2627,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2525
2627
|
let sourceIsFork = false;
|
|
2526
2628
|
if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
|
|
2527
2629
|
if (isAbleTimeConnected() && getIssueSource() === "abletime") {
|
|
2528
|
-
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-
|
|
2630
|
+
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-X64E3GUJ.js");
|
|
2529
2631
|
const task = await ensureAbleTimeTask({
|
|
2530
2632
|
title: title?.trim() || "Untitled work",
|
|
2531
2633
|
description: input.prompt?.trim() || void 0
|
|
@@ -2628,14 +2730,15 @@ async function createThread(input, _onSetupLine) {
|
|
|
2628
2730
|
prTitle,
|
|
2629
2731
|
prAuthorLogin: prAuthorLogin2,
|
|
2630
2732
|
prLabels,
|
|
2631
|
-
prIsDraft
|
|
2733
|
+
prIsDraft,
|
|
2734
|
+
tags: input.tags
|
|
2632
2735
|
});
|
|
2633
2736
|
writeThread(thread);
|
|
2634
2737
|
await ensureWorkspace(repoPath);
|
|
2635
2738
|
return readThread(thread.id) ?? thread;
|
|
2636
2739
|
}
|
|
2637
2740
|
async function listLinearIssues(agent, repoPath) {
|
|
2638
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
2741
|
+
const { getAdapter: getAdapter2 } = await import("./agents-2IE4HKHA.js");
|
|
2639
2742
|
await requireAgent(agent, { requireLinear: true });
|
|
2640
2743
|
const adapter = getAdapter2(agent);
|
|
2641
2744
|
if (!adapter.listLinearIssues) {
|
|
@@ -2724,8 +2827,8 @@ function summarizeTurnLive(parts) {
|
|
|
2724
2827
|
const interesting = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? lastTool;
|
|
2725
2828
|
const lastToolLabel = interesting ? interesting.description || toolDescription(interesting.name, interesting.input) || interesting.name : void 0;
|
|
2726
2829
|
const thinking = lastText(parts, "thinking");
|
|
2727
|
-
const
|
|
2728
|
-
const excerptRaw = thinking ||
|
|
2830
|
+
const text3 = lastText(parts, "text");
|
|
2831
|
+
const excerptRaw = thinking || text3;
|
|
2729
2832
|
const excerpt = excerptRaw.length > 280 ? `${excerptRaw.slice(-280)}` : excerptRaw || void 0;
|
|
2730
2833
|
const summary = liveActivitySummary(parts);
|
|
2731
2834
|
return {
|
|
@@ -2786,6 +2889,9 @@ function writeTurnLive(threadId, progress) {
|
|
|
2786
2889
|
}
|
|
2787
2890
|
}
|
|
2788
2891
|
}
|
|
2892
|
+
function readTurnLiveParts(threadId) {
|
|
2893
|
+
return buffers.get(threadId)?.parts.slice() ?? [];
|
|
2894
|
+
}
|
|
2789
2895
|
function readTurnLive(threadId) {
|
|
2790
2896
|
const path = threadLivePath(threadId);
|
|
2791
2897
|
if (!existsSync6(path)) return null;
|
|
@@ -2874,7 +2980,8 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
2874
2980
|
title: input.title?.trim() || void 0,
|
|
2875
2981
|
parentThreadId: from.id,
|
|
2876
2982
|
attachments: [attachment],
|
|
2877
|
-
reuseExisting: false
|
|
2983
|
+
reuseExisting: false,
|
|
2984
|
+
tags: from.tags
|
|
2878
2985
|
},
|
|
2879
2986
|
onSetupLine
|
|
2880
2987
|
);
|
|
@@ -2882,7 +2989,7 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
2882
2989
|
}
|
|
2883
2990
|
|
|
2884
2991
|
// src/orchestrator/quota-failover.ts
|
|
2885
|
-
import { randomUUID } from "crypto";
|
|
2992
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2886
2993
|
function managedKindForAgent(agent) {
|
|
2887
2994
|
return agent === "claude" || agent === "codex" ? agent : null;
|
|
2888
2995
|
}
|
|
@@ -2968,8 +3075,8 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
2968
3075
|
);
|
|
2969
3076
|
const recent = from.messages.slice(-8).map((m) => {
|
|
2970
3077
|
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
2971
|
-
const
|
|
2972
|
-
return
|
|
3078
|
+
const text3 = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
3079
|
+
return text3 ? `- ${role}: ${text3}` : null;
|
|
2973
3080
|
}).filter(Boolean);
|
|
2974
3081
|
const orch = isOrchestratorThread(from);
|
|
2975
3082
|
const instructions = orch ? [
|
|
@@ -3002,7 +3109,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
3002
3109
|
...instructions
|
|
3003
3110
|
].join("\n");
|
|
3004
3111
|
return {
|
|
3005
|
-
id:
|
|
3112
|
+
id: randomUUID2(),
|
|
3006
3113
|
name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
|
|
3007
3114
|
kind: "transcript",
|
|
3008
3115
|
content: body
|
|
@@ -3097,13 +3204,13 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
3097
3204
|
for (const hash of hashes) {
|
|
3098
3205
|
const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
3099
3206
|
if (!existsSync7(agentsFile)) continue;
|
|
3100
|
-
let
|
|
3207
|
+
let text3;
|
|
3101
3208
|
try {
|
|
3102
|
-
|
|
3209
|
+
text3 = readFileSync5(agentsFile, "utf8");
|
|
3103
3210
|
} catch {
|
|
3104
3211
|
continue;
|
|
3105
3212
|
}
|
|
3106
|
-
for (const line of
|
|
3213
|
+
for (const line of text3.split("\n")) {
|
|
3107
3214
|
const trimmed = line.trim();
|
|
3108
3215
|
if (!trimmed) continue;
|
|
3109
3216
|
try {
|
|
@@ -3142,8 +3249,8 @@ async function adoptThread(input) {
|
|
|
3142
3249
|
messages: input.messages ?? []
|
|
3143
3250
|
});
|
|
3144
3251
|
writeThread(thread);
|
|
3145
|
-
const {
|
|
3146
|
-
await
|
|
3252
|
+
const { addWorkspace: addWorkspace2 } = await import("./workspaces-7AJ5RZ3P.js");
|
|
3253
|
+
await addWorkspace2(repoPath);
|
|
3147
3254
|
const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-RXZPTCNH.js");
|
|
3148
3255
|
await ensureWorktreeSideboardIgnored(input.worktreePath);
|
|
3149
3256
|
return thread;
|
|
@@ -3882,13 +3989,13 @@ function formatStat(files) {
|
|
|
3882
3989
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
3883
3990
|
}
|
|
3884
3991
|
function emptyScopeStats() {
|
|
3885
|
-
const
|
|
3992
|
+
const z4 = emptyStat();
|
|
3886
3993
|
return {
|
|
3887
|
-
commits:
|
|
3888
|
-
uncommitted:
|
|
3889
|
-
staged:
|
|
3890
|
-
unstaged:
|
|
3891
|
-
last_turn:
|
|
3994
|
+
commits: z4,
|
|
3995
|
+
uncommitted: z4,
|
|
3996
|
+
staged: z4,
|
|
3997
|
+
unstaged: z4,
|
|
3998
|
+
last_turn: z4
|
|
3892
3999
|
};
|
|
3893
4000
|
}
|
|
3894
4001
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -5187,12 +5294,15 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
5187
5294
|
lines.push(
|
|
5188
5295
|
"Schedules: `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled."
|
|
5189
5296
|
);
|
|
5297
|
+
lines.push(
|
|
5298
|
+
"`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
|
|
5299
|
+
);
|
|
5190
5300
|
lines.push("");
|
|
5191
5301
|
lines.push(formatProcessGuideDirective({ worktreePath: thread.worktreePath }));
|
|
5192
5302
|
return lines.join("\n");
|
|
5193
5303
|
}
|
|
5194
5304
|
function formatWorktreeReminder() {
|
|
5195
|
-
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat).";
|
|
5305
|
+
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
|
|
5196
5306
|
}
|
|
5197
5307
|
var PR_GOAL_RE = /greptile|\bci\b.*\b(green|pass)|checks?\s+(pass|green)|until\s+.*\b(pass|green|merge|clean)|\bfix ci\b|watch[- ]fix[- ]push/i;
|
|
5198
5308
|
function mentionsPrGoal(prompt) {
|
|
@@ -5225,17 +5335,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
5225
5335
|
];
|
|
5226
5336
|
if (opts.linear) {
|
|
5227
5337
|
lines.push(
|
|
5228
|
-
"- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments, attachments; pass `include=full` if crush.truncated), `linear_download_attachment` (saves to `.context/attachments/`), `linear_comment`, `linear_update_issue` (state, cycle, labels, project, parent, relations), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
5338
|
+
"- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments; rows include the project), `linear_list_projects` (id, name, slug \u2014 use on update), `linear_get_issue` (comments, project, attachments; pass `include=full` if crush.truncated), `linear_download_attachment` (saves to `.context/attachments/`), `linear_comment`, `linear_update_issue` (state, cycle, labels, project, parent, relations), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
5229
5339
|
);
|
|
5230
5340
|
}
|
|
5231
5341
|
if (github) {
|
|
5232
5342
|
lines.push(
|
|
5233
|
-
"- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments, attachments; pass `include=full` if crush.truncated), `github_download_attachment` (saves to `.context/attachments/`), `github_comment`, `github_update_issue` (state open|closed, labels, project, parent, relations), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
5343
|
+
"- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments; rows include projects when `gh` can read them), `github_list_projects` (titles for update), `github_get_issue` (comments, projects, attachments; pass `include=full` if crush.truncated; `projectsError` means run `gh auth refresh -s read:project`), `github_download_attachment` (saves to `.context/attachments/`), `github_comment`, `github_update_issue` (state open|closed, labels, project, parent, relations), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
5234
5344
|
);
|
|
5235
5345
|
}
|
|
5236
5346
|
if (opts.abletime) {
|
|
5237
5347
|
lines.push(
|
|
5238
|
-
"- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments, attachments; pass `include=full` if crush.truncated), `abletime_download_attachment` (saves to `.context/attachments/`), `abletime_comment`, `abletime_update_task` (state, labels, project, parent, relations), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
5348
|
+
"- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments; rows include the project), `abletime_get_task` (comments, project, attachments; pass `include=full` if crush.truncated), `abletime_download_attachment` (saves to `.context/attachments/`), `abletime_comment`, `abletime_update_task` (state, labels, project, parent, relations), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
5239
5349
|
);
|
|
5240
5350
|
}
|
|
5241
5351
|
lines.push(
|
|
@@ -5414,12 +5524,12 @@ function firstString(record, keys) {
|
|
|
5414
5524
|
return "";
|
|
5415
5525
|
}
|
|
5416
5526
|
async function readJson(res) {
|
|
5417
|
-
const
|
|
5418
|
-
if (!
|
|
5527
|
+
const text3 = await res.text();
|
|
5528
|
+
if (!text3.trim()) return null;
|
|
5419
5529
|
try {
|
|
5420
|
-
return JSON.parse(
|
|
5530
|
+
return JSON.parse(text3);
|
|
5421
5531
|
} catch {
|
|
5422
|
-
return
|
|
5532
|
+
return text3;
|
|
5423
5533
|
}
|
|
5424
5534
|
}
|
|
5425
5535
|
async function authorizedGet(url, token) {
|
|
@@ -5570,6 +5680,9 @@ function formatOptionalServicesDirective(integrations) {
|
|
|
5570
5680
|
);
|
|
5571
5681
|
}
|
|
5572
5682
|
}
|
|
5683
|
+
lines.push(
|
|
5684
|
+
"If a connector MCP is listed but needsAuth or returns unauthorized, do not sign in to that MCP. Check the native CLI (or the HTTP API when there is no CLI) \u2014 the token is already in this process."
|
|
5685
|
+
);
|
|
5573
5686
|
lines.push(
|
|
5574
5687
|
"If a CLI is missing, say so or use the HTTP API with the same token. The user can Install CLI from Settings \u2192 Connectors. Do not ask the user to install a vendor MCP."
|
|
5575
5688
|
);
|
|
@@ -5595,7 +5708,451 @@ function formatOptionalServicesDirective(integrations) {
|
|
|
5595
5708
|
}
|
|
5596
5709
|
function formatOptionalServicesReminder(integrations) {
|
|
5597
5710
|
if (connectedOptionalServices(integrations).length === 0) return null;
|
|
5598
|
-
return "Settings \u2192 Connectors: official CLIs / PostHog HTTP with injected env. Write output to `.context/cli/` (not `.context/attachments/`) and read a slice \u2014 never dump raw --json/--expand into the tool result (any agent). stop_job if a fetch hangs. Do not add vendor MCPs.";
|
|
5711
|
+
return "Settings \u2192 Connectors: official CLIs / PostHog HTTP with injected env. If a connector MCP needsAuth or is unauthorized, check the native CLI or HTTP API instead of signing in. Write output to `.context/cli/` (not `.context/attachments/`) and read a slice \u2014 never dump raw --json/--expand into the tool result (any agent). stop_job if a fetch hangs. Do not add vendor MCPs.";
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
// src/mcp/viewer-context-tools.ts
|
|
5715
|
+
import { z as z3 } from "zod";
|
|
5716
|
+
|
|
5717
|
+
// src/mcp/env-tools.ts
|
|
5718
|
+
import { z as z2 } from "zod";
|
|
5719
|
+
|
|
5720
|
+
// src/mcp/issue-list.ts
|
|
5721
|
+
import { z } from "zod";
|
|
5722
|
+
|
|
5723
|
+
// src/integrations/issue-since.ts
|
|
5724
|
+
var ISSUE_SINCE_ERROR = "updatedSince must be an ISO datetime, YYYY-MM-DD, or relative time (yesterday, 2d, 3 hours ago)";
|
|
5725
|
+
var DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
5726
|
+
var DATE_TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
|
|
5727
|
+
var RELATIVE_RE = /^(\d+)\s*(hours?|hrs?|h|days?|d|weeks?|wks?|w)(?:\s+ago)?$/i;
|
|
5728
|
+
function startOfLocalDay(date) {
|
|
5729
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
5730
|
+
}
|
|
5731
|
+
function addLocalDays(date, days) {
|
|
5732
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
|
|
5733
|
+
}
|
|
5734
|
+
function parseIssueSince(input, now = /* @__PURE__ */ new Date()) {
|
|
5735
|
+
const raw = input.trim();
|
|
5736
|
+
if (!raw) throw new Error(ISSUE_SINCE_ERROR);
|
|
5737
|
+
const dateOnly = raw.match(DATE_ONLY_RE);
|
|
5738
|
+
if (dateOnly) {
|
|
5739
|
+
const year = Number(dateOnly[1]);
|
|
5740
|
+
const month = Number(dateOnly[2]);
|
|
5741
|
+
const day = Number(dateOnly[3]);
|
|
5742
|
+
const local = new Date(year, month - 1, day);
|
|
5743
|
+
if (local.getFullYear() !== year || local.getMonth() !== month - 1 || local.getDate() !== day) {
|
|
5744
|
+
throw new Error(ISSUE_SINCE_ERROR);
|
|
5745
|
+
}
|
|
5746
|
+
return startOfLocalDay(local).toISOString();
|
|
5747
|
+
}
|
|
5748
|
+
if (DATE_TIME_RE.test(raw)) {
|
|
5749
|
+
const ms = Date.parse(raw);
|
|
5750
|
+
if (!Number.isFinite(ms)) throw new Error(ISSUE_SINCE_ERROR);
|
|
5751
|
+
return new Date(ms).toISOString();
|
|
5752
|
+
}
|
|
5753
|
+
const lower = raw.toLowerCase();
|
|
5754
|
+
if (lower === "today") return startOfLocalDay(now).toISOString();
|
|
5755
|
+
if (lower === "yesterday") return addLocalDays(startOfLocalDay(now), -1).toISOString();
|
|
5756
|
+
if (lower === "last week" || lower === "lastweek") {
|
|
5757
|
+
return addLocalDays(startOfLocalDay(now), -7).toISOString();
|
|
5758
|
+
}
|
|
5759
|
+
const relative = lower.match(RELATIVE_RE);
|
|
5760
|
+
if (relative) {
|
|
5761
|
+
const n = Number(relative[1]);
|
|
5762
|
+
if (!Number.isInteger(n) || n < 1) throw new Error(ISSUE_SINCE_ERROR);
|
|
5763
|
+
const unit = relative[2];
|
|
5764
|
+
if (unit.startsWith("h")) return new Date(now.getTime() - n * 36e5).toISOString();
|
|
5765
|
+
if (unit.startsWith("w")) return addLocalDays(startOfLocalDay(now), -7 * n).toISOString();
|
|
5766
|
+
return addLocalDays(startOfLocalDay(now), -n).toISOString();
|
|
5767
|
+
}
|
|
5768
|
+
throw new Error(ISSUE_SINCE_ERROR);
|
|
5769
|
+
}
|
|
5770
|
+
function formatGitHubSearchUpdatedSince(iso) {
|
|
5771
|
+
return iso.replace(/\.\d{3}Z$/, "Z");
|
|
5772
|
+
}
|
|
5773
|
+
function issueMatchesUpdatedSince(issue, sinceIso) {
|
|
5774
|
+
const ts = issue.updatedAt || issue.createdAt;
|
|
5775
|
+
if (!ts) return false;
|
|
5776
|
+
const value = Date.parse(ts);
|
|
5777
|
+
const since = Date.parse(sinceIso);
|
|
5778
|
+
return Number.isFinite(value) && Number.isFinite(since) && value >= since;
|
|
5779
|
+
}
|
|
5780
|
+
var ISSUE_COMMENT_PREVIEW_CHARS = 240;
|
|
5781
|
+
function previewIssueCommentBody(body) {
|
|
5782
|
+
const flat = body.replace(/\s+/g, " ").trim();
|
|
5783
|
+
if (flat.length <= ISSUE_COMMENT_PREVIEW_CHARS) return flat;
|
|
5784
|
+
return `${flat.slice(0, ISSUE_COMMENT_PREVIEW_CHARS - 1).trimEnd()}\u2026`;
|
|
5785
|
+
}
|
|
5786
|
+
function issueActivityKind(issue, sinceIso) {
|
|
5787
|
+
const created = issue.createdAt ? Date.parse(issue.createdAt) : Number.NaN;
|
|
5788
|
+
const since = Date.parse(sinceIso);
|
|
5789
|
+
if (Number.isFinite(created) && Number.isFinite(since) && created >= since) return "created";
|
|
5790
|
+
return "updated";
|
|
5791
|
+
}
|
|
5792
|
+
|
|
5793
|
+
// src/mcp/issue-list.ts
|
|
5794
|
+
var MCP_ISSUE_LIST_DEFAULT_LIMIT = 40;
|
|
5795
|
+
var MCP_ISSUE_LIST_MAX_LIMIT = 250;
|
|
5796
|
+
function clampMcpIssueLimit(limit) {
|
|
5797
|
+
if (limit == null || !Number.isFinite(limit)) return MCP_ISSUE_LIST_DEFAULT_LIMIT;
|
|
5798
|
+
return Math.max(1, Math.min(MCP_ISSUE_LIST_MAX_LIMIT, Math.floor(limit)));
|
|
5799
|
+
}
|
|
5800
|
+
function applyIssueListWindow(items, limit) {
|
|
5801
|
+
const truncated = items.length > limit;
|
|
5802
|
+
return {
|
|
5803
|
+
items: truncated ? items.slice(0, limit) : items,
|
|
5804
|
+
truncated
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
var MCP_ISSUE_UPDATED_SINCE_DESCRIPTION = "ISO datetime, YYYY-MM-DD, or relative (yesterday, 2d, 3 hours ago). Tickets updated since then plus new comments. Use for \u201Cany updates since yesterday?\u201D";
|
|
5808
|
+
var mcpIssueUpdatedSinceSchema = z.string().min(1).optional().describe(MCP_ISSUE_UPDATED_SINCE_DESCRIPTION);
|
|
5809
|
+
function compactIssueRow(issue, opts) {
|
|
5810
|
+
const projectNames = (issue.projects ?? []).map((project) => (project.name || project.id || "").trim()).filter(Boolean);
|
|
5811
|
+
return {
|
|
5812
|
+
identifier: issue.identifier,
|
|
5813
|
+
title: issue.title,
|
|
5814
|
+
...issue.assignee ? { assignee: issue.assignee } : {},
|
|
5815
|
+
...issue.labels.length > 0 ? { labels: issue.labels } : {},
|
|
5816
|
+
...issue.teamKey ? { team: issue.teamKey } : {},
|
|
5817
|
+
...issue.cycle?.name ? { cycle: issue.cycle.name } : {},
|
|
5818
|
+
...projectNames.length ? { projects: projectNames } : {},
|
|
5819
|
+
...issue.createdAt ? { createdAt: issue.createdAt } : {},
|
|
5820
|
+
...issue.updatedAt ? { updatedAt: issue.updatedAt } : {},
|
|
5821
|
+
...opts?.since ? { kind: issueActivityKind(issue, opts.since) } : {}
|
|
5822
|
+
};
|
|
5823
|
+
}
|
|
5824
|
+
function formatMcpIssueList(input) {
|
|
5825
|
+
const truncated = input.truncated || Boolean(input.commentsTruncated);
|
|
5826
|
+
const comments = input.comments ?? [];
|
|
5827
|
+
return {
|
|
5828
|
+
...input.source ? { source: input.source } : {},
|
|
5829
|
+
...input.viewer ? { viewer: input.viewer } : {},
|
|
5830
|
+
limit: input.limit,
|
|
5831
|
+
returned: input.issues.length,
|
|
5832
|
+
truncated,
|
|
5833
|
+
...input.since ? { since: input.since } : {},
|
|
5834
|
+
...truncated ? {
|
|
5835
|
+
hint: input.since ? "More issues or comments match. Pass a higher limit (max 250) or a tighter query." : "More issues match. Pass a higher limit (max 250) or a tighter query."
|
|
5836
|
+
} : {},
|
|
5837
|
+
issues: input.issues.map((issue) => compactIssueRow(issue, { since: input.since })),
|
|
5838
|
+
...input.since || comments.length > 0 ? { comments } : {}
|
|
5839
|
+
};
|
|
5840
|
+
}
|
|
5841
|
+
function formatListedIssuesForMcp(result, page) {
|
|
5842
|
+
const windowed = applyIssueListWindow(result.issues, page);
|
|
5843
|
+
const comments = result.comments ?? [];
|
|
5844
|
+
const commentWindow = applyIssueListWindow(comments, page);
|
|
5845
|
+
return formatMcpIssueList({
|
|
5846
|
+
source: result.source,
|
|
5847
|
+
viewer: result.viewer?.name || result.viewer?.login,
|
|
5848
|
+
limit: page,
|
|
5849
|
+
issues: windowed.items,
|
|
5850
|
+
truncated: windowed.truncated,
|
|
5851
|
+
since: result.since,
|
|
5852
|
+
comments: commentWindow.items,
|
|
5853
|
+
commentsTruncated: commentWindow.truncated
|
|
5854
|
+
});
|
|
5855
|
+
}
|
|
5856
|
+
function mcpJson(payload, isError = false) {
|
|
5857
|
+
return {
|
|
5858
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
5859
|
+
...isError ? { isError: true } : {}
|
|
5860
|
+
};
|
|
5861
|
+
}
|
|
5862
|
+
|
|
5863
|
+
// src/mcp/env-tools.ts
|
|
5864
|
+
function text(payload, isError = false) {
|
|
5865
|
+
return mcpJson(payload, isError);
|
|
5866
|
+
}
|
|
5867
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_projects. Orchestration cwd is synthetic \u2014 do not omit repoPath for scope=project. Worktree turns may omit it (uses cwd).";
|
|
5868
|
+
async function projectFor(repoPath) {
|
|
5869
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
5870
|
+
const name = projectNameForRepoPath(hint);
|
|
5871
|
+
if (!name) return null;
|
|
5872
|
+
return { name, prefix: projectEnvPrefix(name) };
|
|
5873
|
+
}
|
|
5874
|
+
function registerEnvTools(server) {
|
|
5875
|
+
server.tool(
|
|
5876
|
+
"list_env",
|
|
5877
|
+
"List Settings \u2192 Environment variable names (not values). scope=project keys use the project prefix (project name, uppercased, plus _). scope=account keys are shared by every project. Pass repoPath from list_projects when calling from orchestration so the reply includes this project prefix.",
|
|
5878
|
+
{
|
|
5879
|
+
repoPath: z2.string().optional().describe(
|
|
5880
|
+
"Registered workspace or worktree path. Required from orchestration. Omit on a worktree turn (uses cwd)."
|
|
5881
|
+
)
|
|
5882
|
+
},
|
|
5883
|
+
async ({ repoPath }) => {
|
|
5884
|
+
try {
|
|
5885
|
+
const project = await projectFor(repoPath);
|
|
5886
|
+
return text({
|
|
5887
|
+
project: project?.name ?? null,
|
|
5888
|
+
prefix: project?.prefix ?? null,
|
|
5889
|
+
keys: listEnvKeys()
|
|
5890
|
+
});
|
|
5891
|
+
} catch (err) {
|
|
5892
|
+
return text({ error: err instanceof Error ? err.message : String(err) }, true);
|
|
5893
|
+
}
|
|
5894
|
+
}
|
|
5895
|
+
);
|
|
5896
|
+
server.tool(
|
|
5897
|
+
"set_env",
|
|
5898
|
+
"Set a Settings \u2192 Environment variable. scope=project prefixes the key with the project name (APPLE_ID on project sideboard is stored as SIDEBOARD_APPLE_ID). scope=account stores the key unchanged and is shared by every project. Values are injected into later agent turns. list_env does not return values.",
|
|
5899
|
+
{
|
|
5900
|
+
key: z2.string().describe("Env name. For scope=project, pass the bare name or the already-prefixed name."),
|
|
5901
|
+
value: z2.string().describe("Value to store. Empty deletes the key."),
|
|
5902
|
+
scope: z2.enum(["account", "project"]).describe("project = prefixed for one repo; account = shared by every project"),
|
|
5903
|
+
repoPath: z2.string().optional().describe(
|
|
5904
|
+
"Required for scope=project from orchestration (path from list_projects). Worktree turns may omit it (cwd)."
|
|
5905
|
+
)
|
|
5906
|
+
},
|
|
5907
|
+
async ({ key, value, scope, repoPath }) => {
|
|
5908
|
+
try {
|
|
5909
|
+
const envScope = scope;
|
|
5910
|
+
if (envScope === "project") {
|
|
5911
|
+
const project = await projectFor(repoPath);
|
|
5912
|
+
if (!project) return text({ ok: false, error: "repoPath required", message: PROJECT_PATH_HINT }, true);
|
|
5913
|
+
const saved2 = setEnvVar({
|
|
5914
|
+
key,
|
|
5915
|
+
value,
|
|
5916
|
+
scope: "project",
|
|
5917
|
+
projectName: project.name
|
|
5918
|
+
});
|
|
5919
|
+
return text({ ok: true, ...saved2 });
|
|
5920
|
+
}
|
|
5921
|
+
const saved = setEnvVar({ key, value, scope: "account" });
|
|
5922
|
+
return text({ ok: true, ...saved });
|
|
5923
|
+
} catch (err) {
|
|
5924
|
+
return text({ error: err instanceof Error ? err.message : String(err) }, true);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
);
|
|
5928
|
+
server.tool(
|
|
5929
|
+
"delete_env",
|
|
5930
|
+
"Delete a Settings \u2192 Environment variable. scope=project uses the project prefix (same rule as set_env). Does not print the stored value.",
|
|
5931
|
+
{
|
|
5932
|
+
key: z2.string().describe("Env name. For scope=project, pass the bare name or the already-prefixed name."),
|
|
5933
|
+
scope: z2.enum(["account", "project"]),
|
|
5934
|
+
repoPath: z2.string().optional().describe(
|
|
5935
|
+
"Required for scope=project from orchestration (path from list_projects). Worktree turns may omit it (cwd)."
|
|
5936
|
+
)
|
|
5937
|
+
},
|
|
5938
|
+
async ({ key, scope, repoPath }) => {
|
|
5939
|
+
try {
|
|
5940
|
+
const envScope = scope;
|
|
5941
|
+
if (envScope === "project") {
|
|
5942
|
+
const project = await projectFor(repoPath);
|
|
5943
|
+
if (!project) return text({ ok: false, error: "repoPath required", message: PROJECT_PATH_HINT }, true);
|
|
5944
|
+
const removed2 = deleteEnvVar({
|
|
5945
|
+
key,
|
|
5946
|
+
scope: "project",
|
|
5947
|
+
projectName: project.name
|
|
5948
|
+
});
|
|
5949
|
+
return text({ ok: true, ...removed2 });
|
|
5950
|
+
}
|
|
5951
|
+
const removed = deleteEnvVar({ key, scope: "account" });
|
|
5952
|
+
return text({ ok: true, ...removed });
|
|
5953
|
+
} catch (err) {
|
|
5954
|
+
return text({ error: err instanceof Error ? err.message : String(err) }, true);
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
);
|
|
5958
|
+
}
|
|
5959
|
+
|
|
5960
|
+
// src/mcp/viewer-context-tools.ts
|
|
5961
|
+
function text2(payload, isError = false) {
|
|
5962
|
+
return mcpJson(payload, isError);
|
|
5963
|
+
}
|
|
5964
|
+
function matchRegisteredWorkspace(repoPath) {
|
|
5965
|
+
const hint = (repoPath ?? "").trim().replace(/\/+$/, "");
|
|
5966
|
+
if (!hint) return null;
|
|
5967
|
+
let best = null;
|
|
5968
|
+
for (const w of listWorkspaces()) {
|
|
5969
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
5970
|
+
if (stored === hint || hint.startsWith(`${stored}/`)) {
|
|
5971
|
+
if (!best || stored.length > best.length) best = stored;
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
return best;
|
|
5975
|
+
}
|
|
5976
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
5977
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
5978
|
+
let root = hint;
|
|
5979
|
+
try {
|
|
5980
|
+
root = await resolveRepoRoot(hint);
|
|
5981
|
+
} catch {
|
|
5982
|
+
root = hint.replace(/\/+$/, "");
|
|
5983
|
+
}
|
|
5984
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
5985
|
+
}
|
|
5986
|
+
var PROJECT_PATH_HINT2 = "Pass repoPath from list_projects. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
5987
|
+
function registerViewerContextTools(server) {
|
|
5988
|
+
server.tool(
|
|
5989
|
+
"get_viewer_context",
|
|
5990
|
+
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from list_projects for project context. Worktree turns may omit repoPath (uses cwd).",
|
|
5991
|
+
{
|
|
5992
|
+
repoPath: z3.string().optional().describe(
|
|
5993
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
5994
|
+
)
|
|
5995
|
+
},
|
|
5996
|
+
async ({ repoPath }) => {
|
|
5997
|
+
try {
|
|
5998
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
5999
|
+
return text2(readViewerContext(path ?? void 0));
|
|
6000
|
+
} catch (err) {
|
|
6001
|
+
return text2(
|
|
6002
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
6003
|
+
true
|
|
6004
|
+
);
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
);
|
|
6008
|
+
server.tool(
|
|
6009
|
+
"update_viewer_context",
|
|
6010
|
+
"Replace account or project user context (Settings \u2192 Agents / Projects). Never call until the user confirmed. Show the proposed text, ask_user (Save this context / Do not save), wait, then pass confirmed=true. Empty context clears that field.",
|
|
6011
|
+
{
|
|
6012
|
+
scope: z3.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
6013
|
+
context: z3.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
6014
|
+
confirmed: z3.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
6015
|
+
repoPath: z3.string().optional().describe(
|
|
6016
|
+
"Required for scope=project from orchestration (path from list_projects). Worktree turns may omit it (cwd)."
|
|
6017
|
+
)
|
|
6018
|
+
},
|
|
6019
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
6020
|
+
try {
|
|
6021
|
+
if (scope === "project") {
|
|
6022
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
6023
|
+
if (!path) {
|
|
6024
|
+
return text2(
|
|
6025
|
+
{
|
|
6026
|
+
ok: false,
|
|
6027
|
+
error: "repoPath required",
|
|
6028
|
+
message: PROJECT_PATH_HINT2
|
|
6029
|
+
},
|
|
6030
|
+
true
|
|
6031
|
+
);
|
|
6032
|
+
}
|
|
6033
|
+
const result2 = writeViewerContext({
|
|
6034
|
+
scope,
|
|
6035
|
+
context,
|
|
6036
|
+
repoPath: path,
|
|
6037
|
+
confirmed: confirmed === true
|
|
6038
|
+
});
|
|
6039
|
+
return text2(result2, !result2.ok);
|
|
6040
|
+
}
|
|
6041
|
+
const result = writeViewerContext({
|
|
6042
|
+
scope,
|
|
6043
|
+
context,
|
|
6044
|
+
confirmed: confirmed === true
|
|
6045
|
+
});
|
|
6046
|
+
return text2(result, !result.ok);
|
|
6047
|
+
} catch (err) {
|
|
6048
|
+
return text2(
|
|
6049
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
6050
|
+
true
|
|
6051
|
+
);
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
);
|
|
6055
|
+
registerEnvTools(server);
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
// src/store/env-vars.ts
|
|
6059
|
+
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6060
|
+
function projectEnvPrefix(projectName) {
|
|
6061
|
+
const slug = projectName.normalize("NFKD").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toUpperCase().slice(0, 40);
|
|
6062
|
+
return `${slug || "PROJECT"}_`;
|
|
6063
|
+
}
|
|
6064
|
+
function assertEnvKey(key) {
|
|
6065
|
+
const k = key.trim();
|
|
6066
|
+
if (!ENV_KEY.test(k)) {
|
|
6067
|
+
throw new Error(
|
|
6068
|
+
`Invalid env key "${key}". Use letters, digits, and underscores, starting with a letter or underscore.`
|
|
6069
|
+
);
|
|
6070
|
+
}
|
|
6071
|
+
return k;
|
|
6072
|
+
}
|
|
6073
|
+
function qualifyProjectEnvKey(prefix, key) {
|
|
6074
|
+
const k = assertEnvKey(key);
|
|
6075
|
+
if (k.toUpperCase().startsWith(prefix)) return prefix + k.slice(prefix.length);
|
|
6076
|
+
return prefix + k;
|
|
6077
|
+
}
|
|
6078
|
+
function projectNameForRepoPath(repoPath) {
|
|
6079
|
+
const matched = matchRegisteredWorkspace(repoPath);
|
|
6080
|
+
if (!matched) return null;
|
|
6081
|
+
const found = listWorkspaces().find((w) => w.path.replace(/\/+$/, "") === matched);
|
|
6082
|
+
return found?.name?.trim() || null;
|
|
6083
|
+
}
|
|
6084
|
+
function projectPrefixes() {
|
|
6085
|
+
return listWorkspaces().map((w) => ({ name: w.name.trim(), prefix: projectEnvPrefix(w.name) })).filter((p) => p.name && p.prefix !== "PROJECT_").sort((a, b) => b.prefix.length - a.prefix.length);
|
|
6086
|
+
}
|
|
6087
|
+
function classifyEnvKey(key) {
|
|
6088
|
+
const hit = projectPrefixes().find((p) => key.startsWith(p.prefix));
|
|
6089
|
+
if (!hit) return { scope: "account" };
|
|
6090
|
+
return { scope: "project", project: hit.name, prefix: hit.prefix };
|
|
6091
|
+
}
|
|
6092
|
+
function listEnvKeys() {
|
|
6093
|
+
return Object.keys(loadAppSettings().environment).sort((a, b) => a.localeCompare(b)).map((key) => ({ key, ...classifyEnvKey(key) }));
|
|
6094
|
+
}
|
|
6095
|
+
function setEnvVar(input) {
|
|
6096
|
+
const value = input.value;
|
|
6097
|
+
if (input.scope === "account") {
|
|
6098
|
+
const key2 = assertEnvKey(input.key);
|
|
6099
|
+
if (value.trim() === "") {
|
|
6100
|
+
updateAppEnvironment({ [key2]: null });
|
|
6101
|
+
return { key: key2, scope: "account" };
|
|
6102
|
+
}
|
|
6103
|
+
updateAppEnvironment({ [key2]: value });
|
|
6104
|
+
return { key: key2, scope: "account" };
|
|
6105
|
+
}
|
|
6106
|
+
const project = input.projectName?.trim();
|
|
6107
|
+
if (!project) throw new Error("Project name required for scope=project.");
|
|
6108
|
+
const prefix = projectEnvPrefix(project);
|
|
6109
|
+
const key = qualifyProjectEnvKey(prefix, input.key);
|
|
6110
|
+
if (value.trim() === "") {
|
|
6111
|
+
updateAppEnvironment({ [key]: null });
|
|
6112
|
+
} else {
|
|
6113
|
+
updateAppEnvironment({ [key]: value });
|
|
6114
|
+
}
|
|
6115
|
+
return { key, scope: "project", prefix, project };
|
|
6116
|
+
}
|
|
6117
|
+
function deleteEnvVar(input) {
|
|
6118
|
+
const current = loadAppSettings().environment;
|
|
6119
|
+
if (input.scope === "account") {
|
|
6120
|
+
const key2 = assertEnvKey(input.key);
|
|
6121
|
+
const deleted2 = Object.prototype.hasOwnProperty.call(current, key2);
|
|
6122
|
+
updateAppEnvironment({ [key2]: null });
|
|
6123
|
+
return { key: key2, scope: "account", deleted: deleted2 };
|
|
6124
|
+
}
|
|
6125
|
+
const project = input.projectName?.trim();
|
|
6126
|
+
if (!project) throw new Error("Project name required for scope=project.");
|
|
6127
|
+
const prefix = projectEnvPrefix(project);
|
|
6128
|
+
const key = qualifyProjectEnvKey(prefix, input.key);
|
|
6129
|
+
const deleted = Object.prototype.hasOwnProperty.call(current, key);
|
|
6130
|
+
updateAppEnvironment({ [key]: null });
|
|
6131
|
+
return { key, scope: "project", deleted, prefix, project };
|
|
6132
|
+
}
|
|
6133
|
+
function formatEnvDirective(projectName) {
|
|
6134
|
+
if (!projectName) {
|
|
6135
|
+
return [
|
|
6136
|
+
"Sideboard environment (Settings \u2192 Environment): list_env, set_env, delete_env.",
|
|
6137
|
+
"scope=project prefixes the key with the registered project name (uppercased, then _). APPLE_ID for a project named sideboard is stored as SIDEBOARD_APPLE_ID.",
|
|
6138
|
+
"scope=account is shared by every project and is not prefixed.",
|
|
6139
|
+
"list_env returns names only. A value is injected into later agent turns."
|
|
6140
|
+
].join(" ");
|
|
6141
|
+
}
|
|
6142
|
+
const prefix = projectEnvPrefix(projectName);
|
|
6143
|
+
return [
|
|
6144
|
+
"Sideboard environment (Settings \u2192 Environment): list_env, set_env, delete_env.",
|
|
6145
|
+
`This project's prefix is ${prefix}. Project secrets must use that prefix \u2014 set_env scope=project applies it (APPLE_ID becomes ${prefix}APPLE_ID).`,
|
|
6146
|
+
"scope=account is only for variables shared by every project.",
|
|
6147
|
+
"list_env returns names only. A value set here is injected into later agent turns."
|
|
6148
|
+
].join(" ");
|
|
6149
|
+
}
|
|
6150
|
+
function formatEnvReminder(projectName) {
|
|
6151
|
+
if (!projectName) {
|
|
6152
|
+
return "Sideboard env: list_env / set_env / delete_env. scope=project prefixes the key with the project name (SIDEBOARD_APPLE_ID). scope=account is shared by every project.";
|
|
6153
|
+
}
|
|
6154
|
+
const prefix = projectEnvPrefix(projectName);
|
|
6155
|
+
return `Sideboard env: list_env / set_env / delete_env. This project's prefix is ${prefix}. scope=project stores ${prefix}<NAME>. scope=account is shared by every project.`;
|
|
5599
6156
|
}
|
|
5600
6157
|
|
|
5601
6158
|
// src/threads/sync-branch.ts
|
|
@@ -5619,7 +6176,7 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5619
6176
|
}
|
|
5620
6177
|
|
|
5621
6178
|
// src/store/schedules.ts
|
|
5622
|
-
import { randomUUID as
|
|
6179
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5623
6180
|
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
5624
6181
|
import { join as join13 } from "path";
|
|
5625
6182
|
import { Cron } from "croner";
|
|
@@ -5764,7 +6321,7 @@ function buildTask(input, now) {
|
|
|
5764
6321
|
const ts = nowIso(now);
|
|
5765
6322
|
const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
|
|
5766
6323
|
return {
|
|
5767
|
-
id:
|
|
6324
|
+
id: randomUUID3(),
|
|
5768
6325
|
name: input.name?.trim() || defaultScheduleName(prompt),
|
|
5769
6326
|
prompt,
|
|
5770
6327
|
enabled: input.enabled !== false,
|
|
@@ -5878,7 +6435,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
5878
6435
|
${prompt}`;
|
|
5879
6436
|
}
|
|
5880
6437
|
async function defaultDeps() {
|
|
5881
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6438
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-FPGVKEID.js");
|
|
5882
6439
|
const orch = getOrchestrator2();
|
|
5883
6440
|
return {
|
|
5884
6441
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -6226,6 +6783,10 @@ var Orchestrator = class {
|
|
|
6226
6783
|
}
|
|
6227
6784
|
continue;
|
|
6228
6785
|
}
|
|
6786
|
+
if (thisProcessShouldDrainAgentQueues() && isUnclaimedSteerRequest(thread.steerRequest) && thread.queue.length > 0) {
|
|
6787
|
+
this.finishSteer(thread.id, false);
|
|
6788
|
+
continue;
|
|
6789
|
+
}
|
|
6229
6790
|
if (thread.queue.length > 0) {
|
|
6230
6791
|
this.haltDrain.delete(thread.id);
|
|
6231
6792
|
this.armDrain(thread.id);
|
|
@@ -6610,8 +7171,12 @@ var Orchestrator = class {
|
|
|
6610
7171
|
async addWorkspace(repoPath) {
|
|
6611
7172
|
return addWorkspace(repoPath);
|
|
6612
7173
|
}
|
|
6613
|
-
removeWorkspace(repoPath) {
|
|
7174
|
+
async removeWorkspace(repoPath) {
|
|
6614
7175
|
removeWorkspace(repoPath);
|
|
7176
|
+
for (const thread of listThreads()) {
|
|
7177
|
+
if (!sameWorkspacePath(thread.repoPath, repoPath)) continue;
|
|
7178
|
+
await this.archive(thread.id).catch(() => void 0);
|
|
7179
|
+
}
|
|
6615
7180
|
}
|
|
6616
7181
|
async adopt(input) {
|
|
6617
7182
|
const thread = await adoptThread(input);
|
|
@@ -6684,11 +7249,11 @@ var Orchestrator = class {
|
|
|
6684
7249
|
return results;
|
|
6685
7250
|
}
|
|
6686
7251
|
/** Edit the text of a not-yet-started queued message. */
|
|
6687
|
-
async editQueuedMessage(threadRef, index,
|
|
7252
|
+
async editQueuedMessage(threadRef, index, text3) {
|
|
6688
7253
|
const thread = this.requireThread(threadRef);
|
|
6689
7254
|
return withThreadLock(thread.id, async () => {
|
|
6690
7255
|
const current = this.requireThread(thread.id);
|
|
6691
|
-
const trimmed =
|
|
7256
|
+
const trimmed = text3.trim();
|
|
6692
7257
|
if (!trimmed || index < 0 || index >= current.queue.length) {
|
|
6693
7258
|
return current;
|
|
6694
7259
|
}
|
|
@@ -6722,33 +7287,37 @@ var Orchestrator = class {
|
|
|
6722
7287
|
return next;
|
|
6723
7288
|
});
|
|
6724
7289
|
}
|
|
6725
|
-
/**
|
|
6726
|
-
* Promote a queued message to run next, interrupting the in-flight turn (if any).
|
|
6727
|
-
* The current turn is stopped without clearing the rest of the queue — drainQueue
|
|
6728
|
-
* picks the promoted message up as soon as the interrupted turn unwinds.
|
|
6729
|
-
*/
|
|
7290
|
+
/** Promote a queued prompt to run next, interrupting the in-flight turn. */
|
|
6730
7291
|
async sendQueuedMessageNow(threadRef, index) {
|
|
6731
7292
|
const thread = this.requireThread(threadRef);
|
|
7293
|
+
const handoff = !thisProcessShouldDrainAgentQueues();
|
|
6732
7294
|
const promoted = await withThreadLock(thread.id, async () => {
|
|
6733
|
-
const
|
|
6734
|
-
if (index < 0 || index >=
|
|
6735
|
-
const next = moveQueuedItemToFront(
|
|
7295
|
+
const current = this.requireThread(thread.id);
|
|
7296
|
+
if (index < 0 || index >= current.queue.length) return false;
|
|
7297
|
+
const next = moveQueuedItemToFront(current.queue, current.queueAttachments, index);
|
|
6736
7298
|
this.haltDrain.delete(thread.id);
|
|
6737
|
-
updateThread(thread.id,
|
|
7299
|
+
updateThread(thread.id, {
|
|
7300
|
+
...next,
|
|
7301
|
+
...handoff ? { steerRequest: nextSteerRequest(current.steerRequest) } : {}
|
|
7302
|
+
});
|
|
6738
7303
|
this.emit({ type: "queue_changed", threadId: thread.id, queue: next.queue });
|
|
6739
7304
|
return true;
|
|
6740
7305
|
});
|
|
6741
|
-
if (!promoted) return this.requireThread(thread.id);
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
7306
|
+
if (!promoted || handoff) return this.requireThread(thread.id);
|
|
7307
|
+
return this.finishSteer(thread.id);
|
|
7308
|
+
}
|
|
7309
|
+
/** Desktop host: interrupt the live turn and start the front-of-queue prompt. */
|
|
7310
|
+
finishSteer(threadId, notifyParent = true) {
|
|
7311
|
+
claimSteerRequest(threadId);
|
|
7312
|
+
this.haltDrain.delete(threadId);
|
|
7313
|
+
const current = this.requireThread(threadId);
|
|
7314
|
+
const inFlight = this.activeTurns.has(threadId) || this.startingTurns.has(threadId);
|
|
7315
|
+
const pid = current.agentPid;
|
|
7316
|
+
if (inFlight || typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
|
|
7317
|
+
this.stop(threadId, { clearQueue: false, continueQueue: true, notifyParent });
|
|
6749
7318
|
}
|
|
6750
|
-
this.armDrain(
|
|
6751
|
-
return this.requireThread(
|
|
7319
|
+
this.armDrain(threadId);
|
|
7320
|
+
return this.requireThread(threadId);
|
|
6752
7321
|
}
|
|
6753
7322
|
armDrain(threadId) {
|
|
6754
7323
|
void this.drainQueue(threadId).catch((err) => {
|
|
@@ -6879,7 +7448,7 @@ var Orchestrator = class {
|
|
|
6879
7448
|
orch: isOrchestratorThread(thread)
|
|
6880
7449
|
});
|
|
6881
7450
|
if (this.stoppedTurns.has(threadId)) {
|
|
6882
|
-
const stopped =
|
|
7451
|
+
const stopped = writeStoppedStatus(threadId);
|
|
6883
7452
|
if (stopped?.status === "stopped") {
|
|
6884
7453
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
6885
7454
|
}
|
|
@@ -6925,7 +7494,9 @@ var Orchestrator = class {
|
|
|
6925
7494
|
ticketId: issueTicket?.id,
|
|
6926
7495
|
ticketProvider: issueTicket?.provider
|
|
6927
7496
|
}) : null;
|
|
6928
|
-
const viewerContextReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatViewerContextReminder()
|
|
7497
|
+
const viewerContextReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? `${formatViewerContextReminder()}
|
|
7498
|
+
|
|
7499
|
+
${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePath))}` : null;
|
|
6929
7500
|
const reviewWriteGateReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) && isReviewWriteGatedThread(
|
|
6930
7501
|
thread,
|
|
6931
7502
|
thread.worktreePath ? threadsSharingWorktree(thread.worktreePath) : []
|
|
@@ -6999,16 +7570,15 @@ var Orchestrator = class {
|
|
|
6999
7570
|
fresh.worktreePath ? threadsSharingWorktree(fresh.worktreePath) : []
|
|
7000
7571
|
) ? null : formatReviewWriteGateDirective();
|
|
7001
7572
|
let viewerContextDirective = null;
|
|
7573
|
+
let envDirective = null;
|
|
7002
7574
|
if (!isBrightsy && !isOrchestration) {
|
|
7575
|
+
const repo = fresh.repoPath || fresh.worktreePath;
|
|
7003
7576
|
try {
|
|
7004
|
-
viewerContextDirective = formatViewerContextDirective(
|
|
7005
|
-
resolveViewerProfileForRepo(fresh.repoPath || fresh.worktreePath)
|
|
7006
|
-
);
|
|
7577
|
+
viewerContextDirective = formatViewerContextDirective(resolveViewerProfileForRepo(repo));
|
|
7007
7578
|
} catch {
|
|
7008
|
-
viewerContextDirective = formatViewerContextDirective(
|
|
7009
|
-
resolveViewerProfileForRepo()
|
|
7010
|
-
);
|
|
7579
|
+
viewerContextDirective = formatViewerContextDirective(resolveViewerProfileForRepo());
|
|
7011
7580
|
}
|
|
7581
|
+
envDirective = formatEnvDirective(projectNameForRepoPath(repo));
|
|
7012
7582
|
}
|
|
7013
7583
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
7014
7584
|
let renameBranchDirective = null;
|
|
@@ -7050,6 +7620,7 @@ var Orchestrator = class {
|
|
|
7050
7620
|
issueToolsDirective,
|
|
7051
7621
|
reviewWriteGateDirective,
|
|
7052
7622
|
viewerContextDirective,
|
|
7623
|
+
envDirective,
|
|
7053
7624
|
artifactDirective,
|
|
7054
7625
|
longRunningDirective,
|
|
7055
7626
|
renameBranchDirective,
|
|
@@ -7179,6 +7750,7 @@ var Orchestrator = class {
|
|
|
7179
7750
|
issueToolsDirective,
|
|
7180
7751
|
reviewWriteGateDirective,
|
|
7181
7752
|
viewerContextDirective,
|
|
7753
|
+
envDirective,
|
|
7182
7754
|
artifactDirective,
|
|
7183
7755
|
longRunningDirective,
|
|
7184
7756
|
renameBranchDirective,
|
|
@@ -7264,7 +7836,7 @@ var Orchestrator = class {
|
|
|
7264
7836
|
}
|
|
7265
7837
|
settleAgentBlock(threadId, parts, exitCode !== 0 && !this.stoppedTurns.has(threadId));
|
|
7266
7838
|
if (this.stoppedTurns.has(threadId)) {
|
|
7267
|
-
const stopped =
|
|
7839
|
+
const stopped = writeStoppedStatus(threadId);
|
|
7268
7840
|
if (stopped?.status === "stopped") {
|
|
7269
7841
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
7270
7842
|
}
|
|
@@ -7315,7 +7887,7 @@ var Orchestrator = class {
|
|
|
7315
7887
|
await syncThreadBranchFromGit(threadId).catch(() => void 0);
|
|
7316
7888
|
settleAgentBlock(threadId, void 0, !this.stoppedTurns.has(threadId));
|
|
7317
7889
|
if (this.stoppedTurns.has(threadId)) {
|
|
7318
|
-
const stopped =
|
|
7890
|
+
const stopped = writeStoppedStatus(threadId);
|
|
7319
7891
|
if (stopped?.status === "stopped") {
|
|
7320
7892
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
7321
7893
|
}
|
|
@@ -7396,7 +7968,7 @@ var Orchestrator = class {
|
|
|
7396
7968
|
} catch {
|
|
7397
7969
|
}
|
|
7398
7970
|
}
|
|
7399
|
-
const stopped =
|
|
7971
|
+
const stopped = writeStoppedStatus(thread.id, resolveStopReason(opts)) ?? readThread(thread.id) ?? thread;
|
|
7400
7972
|
if (stopped.status === "stopped") {
|
|
7401
7973
|
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
7402
7974
|
if (inFlight && opts?.notifyParent !== false) {
|
|
@@ -7575,7 +8147,7 @@ var Orchestrator = class {
|
|
|
7575
8147
|
const request = sameIntent ? pending : {
|
|
7576
8148
|
op,
|
|
7577
8149
|
scriptName: scriptName ?? null,
|
|
7578
|
-
requestId:
|
|
8150
|
+
requestId: randomUUID4(),
|
|
7579
8151
|
requestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7580
8152
|
};
|
|
7581
8153
|
if (!sameIntent) {
|
|
@@ -8020,7 +8592,7 @@ var Orchestrator = class {
|
|
|
8020
8592
|
const stillRunning = this.threadLooksLive(thread);
|
|
8021
8593
|
const rawText = (lastAgent?.text ?? "").trim();
|
|
8022
8594
|
const finishedText = (rawText && !isInternalAgentStatusText(rawText) ? rawText : "") || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
|
|
8023
|
-
const
|
|
8595
|
+
const text3 = stillRunning ? "" : finishedText;
|
|
8024
8596
|
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
8025
8597
|
const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
|
|
8026
8598
|
const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
|
|
@@ -8031,7 +8603,7 @@ var Orchestrator = class {
|
|
|
8031
8603
|
});
|
|
8032
8604
|
const block = thread.agentBlock?.reason?.trim() ? thread.agentBlock : null;
|
|
8033
8605
|
return {
|
|
8034
|
-
text,
|
|
8606
|
+
text: text3,
|
|
8035
8607
|
status: thread.status,
|
|
8036
8608
|
taskState,
|
|
8037
8609
|
sessionId: thread.sessionId,
|
|
@@ -8406,7 +8978,7 @@ var Orchestrator = class {
|
|
|
8406
8978
|
return tab;
|
|
8407
8979
|
}
|
|
8408
8980
|
/**
|
|
8409
|
-
* Desktop git buttons + MCP `ask_git`. Always
|
|
8981
|
+
* Desktop git buttons + MCP `ask_git`. Always steers the worktree agent
|
|
8410
8982
|
* with the action prompt (same path as Resolve). Repository `[prompts]`
|
|
8411
8983
|
* overrides apply when set.
|
|
8412
8984
|
*/
|
|
@@ -8490,6 +9062,9 @@ var Orchestrator = class {
|
|
|
8490
9062
|
if (!next) throw new Error("Title cannot be empty");
|
|
8491
9063
|
return updateThread(thread.id, { title: next, userSetTitle: true });
|
|
8492
9064
|
}
|
|
9065
|
+
setWorkspaceTags(threadRef, tags) {
|
|
9066
|
+
return applyWorkspaceTags(threadRef, tags, "replace");
|
|
9067
|
+
}
|
|
8493
9068
|
async switchThreadBranch(threadRef, branchName) {
|
|
8494
9069
|
const thread = this.requireThread(threadRef);
|
|
8495
9070
|
this.assertNotGlobal(thread, "Switch branch");
|
|
@@ -8571,11 +9146,7 @@ var Orchestrator = class {
|
|
|
8571
9146
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
8572
9147
|
this.enforceHistoryRetention();
|
|
8573
9148
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
8574
|
-
|
|
8575
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UP6DQ3WL.js");
|
|
8576
|
-
await ensureWorkspace2(thread.repoPath);
|
|
8577
|
-
} catch {
|
|
8578
|
-
}
|
|
9149
|
+
await ensureWorkspace(thread.repoPath).catch(() => void 0);
|
|
8579
9150
|
}
|
|
8580
9151
|
return archived;
|
|
8581
9152
|
}
|
|
@@ -8626,16 +9197,12 @@ var Orchestrator = class {
|
|
|
8626
9197
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
8627
9198
|
);
|
|
8628
9199
|
}
|
|
8629
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-Q6UJHG25.js");
|
|
8630
|
-
const slug = thread.worktreePath.split("/").pop();
|
|
8631
9200
|
const dest = thread.worktreePath;
|
|
8632
9201
|
await withRepoGitLock(thread.repoPath, async () => {
|
|
8633
9202
|
await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
|
|
8634
9203
|
});
|
|
8635
9204
|
const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-RXZPTCNH.js");
|
|
8636
9205
|
await ensureWorktreeSideboardIgnored(dest);
|
|
8637
|
-
void createThreadWorktree2;
|
|
8638
|
-
void slug;
|
|
8639
9206
|
}
|
|
8640
9207
|
const restorePatch = {};
|
|
8641
9208
|
let alreadyMerged = normalizePrState(thread.prState) === "MERGED";
|
|
@@ -8659,6 +9226,9 @@ var Orchestrator = class {
|
|
|
8659
9226
|
if (Object.keys(restorePatch).length > 0) {
|
|
8660
9227
|
updateThread(thread.id, restorePatch);
|
|
8661
9228
|
}
|
|
9229
|
+
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
9230
|
+
await addWorkspace(thread.repoPath).catch(() => void 0);
|
|
9231
|
+
}
|
|
8662
9232
|
const restored = setStatus(thread.id, "idle");
|
|
8663
9233
|
this.emit({ type: "status_changed", threadId: restored.id, status: restored.status });
|
|
8664
9234
|
return restored;
|
|
@@ -8736,7 +9306,7 @@ async function startOrchestration(opts) {
|
|
|
8736
9306
|
sourceRef: "default",
|
|
8737
9307
|
...createOpts
|
|
8738
9308
|
}).catch(async () => {
|
|
8739
|
-
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-
|
|
9309
|
+
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-YTZC3UQN.js");
|
|
8740
9310
|
const repo = await resolveRepoRoot2(repoPath);
|
|
8741
9311
|
const def = await resolveDefaultBranch2(repo);
|
|
8742
9312
|
return orch.createThread({
|
|
@@ -8746,7 +9316,7 @@ async function startOrchestration(opts) {
|
|
|
8746
9316
|
repoPath: repo
|
|
8747
9317
|
});
|
|
8748
9318
|
});
|
|
8749
|
-
const { updateThread: upd } = await import("./thread-store-
|
|
9319
|
+
const { updateThread: upd } = await import("./thread-store-ZQZDREVH.js");
|
|
8750
9320
|
const updated = upd(thread.id, {
|
|
8751
9321
|
sourceType: "orchestration",
|
|
8752
9322
|
sourceRef: goal
|
|
@@ -8844,6 +9414,8 @@ export {
|
|
|
8844
9414
|
stampNotifyBlock,
|
|
8845
9415
|
detectAgents,
|
|
8846
9416
|
requireAgent,
|
|
9417
|
+
workspaceTagTargetRef,
|
|
9418
|
+
applyWorkspaceTags,
|
|
8847
9419
|
createThread,
|
|
8848
9420
|
listLinearIssues,
|
|
8849
9421
|
IMPLIED_TICKET_RESOLVE_PROMPT,
|
|
@@ -8851,6 +9423,7 @@ export {
|
|
|
8851
9423
|
isCowboyThread,
|
|
8852
9424
|
isPrimaryCheckoutThread,
|
|
8853
9425
|
shouldRemoveWorktreeOnTeardown,
|
|
9426
|
+
readTurnLiveParts,
|
|
8854
9427
|
readTurnLive,
|
|
8855
9428
|
shouldStampSetupLastError,
|
|
8856
9429
|
isSetupLastError,
|
|
@@ -8931,6 +9504,16 @@ export {
|
|
|
8931
9504
|
connectedOptionalServices,
|
|
8932
9505
|
formatOptionalServicesDirective,
|
|
8933
9506
|
formatOptionalServicesReminder,
|
|
9507
|
+
parseIssueSince,
|
|
9508
|
+
formatGitHubSearchUpdatedSince,
|
|
9509
|
+
issueMatchesUpdatedSince,
|
|
9510
|
+
previewIssueCommentBody,
|
|
9511
|
+
clampMcpIssueLimit,
|
|
9512
|
+
applyIssueListWindow,
|
|
9513
|
+
mcpIssueUpdatedSinceSchema,
|
|
9514
|
+
formatListedIssuesForMcp,
|
|
9515
|
+
mcpJson,
|
|
9516
|
+
registerViewerContextTools,
|
|
8934
9517
|
LIVE_TURN_SPAWN_GRACE_MS,
|
|
8935
9518
|
RUN_SCRIPT_DESKTOP_ADOPT_MS,
|
|
8936
9519
|
isPidAlive,
|