@sideboard-ai/core 0.1.99 → 0.1.103
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 +22 -22
- package/dist/agents/cursor-runner.js +2 -2
- package/dist/{agents-ESJKIQQA.js → agents-UT5GCJH3.js} +8 -8
- package/dist/{agents-3MWWWSMF.js → agents-XZBAKBO2.js} +9 -9
- package/dist/{app-settings-BRF7VKTQ.js → app-settings-EKJM2344.js} +2 -2
- package/dist/{app-settings-ZXPAB3QN.js → app-settings-VDJK2BZ2.js} +2 -2
- package/dist/{caffeinate-hold-U3QJBV67.js → caffeinate-hold-NBRUWYXO.js} +2 -2
- package/dist/{caffeinate-hold-I7SG6UQR.js → caffeinate-hold-X3JNPR42.js} +2 -2
- package/dist/{chunk-UYQYK2RY.js → chunk-27MCESES.js} +3 -3
- package/dist/{chunk-QSLE4VEM.js → chunk-2GO3YKBA.js} +4 -4
- package/dist/{chunk-DKHGWYWR.js → chunk-7YKXHBIW.js} +1 -1
- package/dist/{chunk-JL4SJ6V7.js → chunk-BD2ICC4D.js} +4 -0
- package/dist/{chunk-NZPGMNFC.js → chunk-DCOZABNT.js} +1 -1
- package/dist/{chunk-CIRXAYWS.js → chunk-DHAGMJIK.js} +55 -28
- package/dist/{chunk-XUWDLRAE.js → chunk-ETMRBJKA.js} +11 -11
- package/dist/{chunk-MBP3XG57.js → chunk-H6LP57EF.js} +7 -7
- package/dist/{chunk-FO67IJTY.js → chunk-HHLZWO7U.js} +5 -5
- package/dist/{chunk-GXSYI7FH.js → chunk-HUKCGRAT.js} +209 -59
- package/dist/{chunk-3WA37BRI.js → chunk-KGSVLZV6.js} +11 -4
- package/dist/{chunk-7KDBDMNC.js → chunk-KLBOWJ74.js} +1 -1
- package/dist/{chunk-FYS2BULQ.js → chunk-LANDJIAI.js} +213 -63
- package/dist/{chunk-OANJQTVG.js → chunk-ND6AO7C5.js} +3 -3
- package/dist/{chunk-HI2OTFFR.js → chunk-OW34MVDN.js} +5 -5
- package/dist/{chunk-JOF3XIEM.js → chunk-P3BQ5DOL.js} +11 -4
- package/dist/{chunk-B4R2VB2A.js → chunk-QZQEXME2.js} +4 -0
- package/dist/{chunk-R7BQBSDT.js → chunk-WA3DVVWG.js} +55 -28
- package/dist/{chunk-6LFV4VFI.js → chunk-WSYCND2U.js} +1 -1
- package/dist/{chunk-FT2SQOL4.js → chunk-XHZJJWLI.js} +1 -1
- package/dist/{chunk-XH2GS2LO.js → chunk-YJADJH5U.js} +11 -11
- package/dist/{connected-teams-SWHJWK2A.js → connected-teams-J4I5AKMR.js} +2 -2
- package/dist/{connected-teams-TLVKZPJP.js → connected-teams-M5XNXRI5.js} +2 -2
- package/dist/{coordinator-prompt-OQOOD5ET.js → coordinator-prompt-3DFZRMXE.js} +5 -5
- package/dist/{coordinator-prompt-AKEY4WSO.js → coordinator-prompt-UWHSD65V.js} +5 -5
- package/dist/{cursor-recover-S5ORPW34.js → cursor-recover-JBTIDARH.js} +1 -1
- package/dist/{cursor-recover-G34BYKL2.js → cursor-recover-ZUJG6XRQ.js} +1 -1
- package/dist/{global-workspace-WMF3BJP5.js → global-workspace-KJQS5S2C.js} +6 -6
- package/dist/{global-workspace-RSQXRLT7.js → global-workspace-UR4E3CCG.js} +6 -6
- package/dist/index.cjs +851 -232
- package/dist/index.d.cts +39 -6
- package/dist/index.d.ts +39 -6
- package/dist/index.js +561 -138
- package/dist/mcp/run-stdio.cjs +651 -256
- package/dist/mcp/run-stdio.js +345 -143
- package/dist/{paths-TZX4L5X7.js → paths-QCI3MTO5.js} +3 -1
- package/dist/{paths-JQM2XRAD.js → paths-UMEDHOS5.js} +3 -1
- package/dist/{thread-store-XAKWCV2E.js → thread-store-4OUEQ2DB.js} +2 -2
- package/dist/{thread-store-V7NOOXKE.js → thread-store-DQJGDKIJ.js} +2 -2
- package/dist/{workspaces-MZVQHRSJ.js → workspaces-NTIVI6DI.js} +7 -7
- package/dist/{workspaces-4ZY4QPWQ.js → workspaces-TKJXPNV4.js} +7 -7
- package/dist/{worktree-EO5QAGJU.js → worktree-NKWN5ZTS.js} +4 -4
- package/dist/{worktree-DA4BOV7G.js → worktree-PEUPFAKL.js} +4 -4
- package/package.json +2 -2
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
fallbackTurnFailDetail,
|
|
7
7
|
formatTurnExitError,
|
|
8
8
|
getAdapter,
|
|
9
|
-
humanizeAgentFailDetail,
|
|
10
9
|
isSessionQuotaLimit,
|
|
11
10
|
listModelsForAgent,
|
|
12
11
|
looksLikeAgentFailureMessage,
|
|
@@ -15,16 +14,18 @@ import {
|
|
|
15
14
|
parseSessionQuotaResetAt,
|
|
16
15
|
pushTurnStderr,
|
|
17
16
|
resolveQuotaFallbackAgent,
|
|
17
|
+
shouldRetryFailedAgentTurn,
|
|
18
18
|
sideboardMcpProfile,
|
|
19
|
-
summarizeTurnStderr
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
summarizeTurnStderr,
|
|
20
|
+
turnFailChatText
|
|
21
|
+
} from "../chunk-LANDJIAI.js";
|
|
22
|
+
import "../chunk-7YKXHBIW.js";
|
|
22
23
|
import {
|
|
23
24
|
addWorkspace,
|
|
24
25
|
ensureWorkspace,
|
|
25
26
|
removeWorkspace,
|
|
26
27
|
syncWorkspacesFromThreads
|
|
27
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-ND6AO7C5.js";
|
|
28
29
|
import {
|
|
29
30
|
extractPresentedPlan,
|
|
30
31
|
readPlanFile,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
} from "../chunk-N5PM7HGQ.js";
|
|
33
34
|
import {
|
|
34
35
|
releaseCaffeinateHoldForThread
|
|
35
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-KLBOWJ74.js";
|
|
36
37
|
import {
|
|
37
38
|
GLOBAL_WORKSPACE_ID,
|
|
38
39
|
assertOrchestratorCapableAgent,
|
|
@@ -43,14 +44,14 @@ import {
|
|
|
43
44
|
isOrchestratorThread,
|
|
44
45
|
isSlackCoordinatorThread,
|
|
45
46
|
orchestratorSessionPoisonedByBuiltins
|
|
46
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-OW34MVDN.js";
|
|
47
48
|
import {
|
|
48
49
|
SLACK_REPLY_FORMATTING,
|
|
49
50
|
coordinatorSystemPrompt,
|
|
50
51
|
coordinatorTurnReminder,
|
|
51
52
|
enrichWorkspacesWithGithub,
|
|
52
53
|
ensureGlobalCoordinatorCwd
|
|
53
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-YJADJH5U.js";
|
|
54
55
|
import {
|
|
55
56
|
addPrStackLayer,
|
|
56
57
|
allocateTeamName,
|
|
@@ -61,6 +62,7 @@ import {
|
|
|
61
62
|
createThreadWorktree,
|
|
62
63
|
currentBranch,
|
|
63
64
|
detectGhStack,
|
|
65
|
+
enqueueByKey,
|
|
64
66
|
fetchPrHead,
|
|
65
67
|
formatGhLandError,
|
|
66
68
|
formatGitAuthModeDirective,
|
|
@@ -93,8 +95,9 @@ import {
|
|
|
93
95
|
takenSlugsFromThread,
|
|
94
96
|
threadDisplayLabel,
|
|
95
97
|
warmGithubAgentAuth,
|
|
98
|
+
withRepoGitLock,
|
|
96
99
|
worktreeNameFromPath
|
|
97
|
-
} from "../chunk-
|
|
100
|
+
} from "../chunk-WA3DVVWG.js";
|
|
98
101
|
import {
|
|
99
102
|
ATTACHMENTS_DIR,
|
|
100
103
|
LEGACY_ATTACHMENTS_DIR,
|
|
@@ -111,7 +114,7 @@ import {
|
|
|
111
114
|
updateThread,
|
|
112
115
|
withThreadLock,
|
|
113
116
|
writeThread
|
|
114
|
-
} from "../chunk-
|
|
117
|
+
} from "../chunk-P3BQ5DOL.js";
|
|
115
118
|
import {
|
|
116
119
|
childEnvWithAppSettings,
|
|
117
120
|
disconnectLinearConnection,
|
|
@@ -129,7 +132,7 @@ import {
|
|
|
129
132
|
stripNestedElectronEnv,
|
|
130
133
|
updateAdvancedSettings,
|
|
131
134
|
writeSecureJson
|
|
132
|
-
} from "../chunk-
|
|
135
|
+
} from "../chunk-WSYCND2U.js";
|
|
133
136
|
import {
|
|
134
137
|
writePrivateFile
|
|
135
138
|
} from "../chunk-NSTQ6QKD.js";
|
|
@@ -142,9 +145,10 @@ import {
|
|
|
142
145
|
runCursorWorktreeSetup,
|
|
143
146
|
sideboardReposDir,
|
|
144
147
|
sideboardWorkspacesDir,
|
|
148
|
+
threadLivePath,
|
|
145
149
|
workspaceSettingsSourceLabel,
|
|
146
150
|
worktreesRoot
|
|
147
|
-
} from "../chunk-
|
|
151
|
+
} from "../chunk-BD2ICC4D.js";
|
|
148
152
|
import {
|
|
149
153
|
enrichPathWithNpmGlobalBin,
|
|
150
154
|
ensureAgentPath,
|
|
@@ -659,7 +663,7 @@ async function refreshSlackReplyBadges(opts) {
|
|
|
659
663
|
}
|
|
660
664
|
|
|
661
665
|
// src/orchestrator/orchestrator.ts
|
|
662
|
-
import { existsSync as
|
|
666
|
+
import { existsSync as existsSync15 } from "fs";
|
|
663
667
|
|
|
664
668
|
// src/agents/spawn.ts
|
|
665
669
|
import { createInterface } from "readline";
|
|
@@ -901,7 +905,7 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
901
905
|
const intervalMs = opts?.intervalMs ?? 32;
|
|
902
906
|
let pending = null;
|
|
903
907
|
let timer = null;
|
|
904
|
-
const
|
|
908
|
+
const flush2 = () => {
|
|
905
909
|
if (timer) {
|
|
906
910
|
clearTimeout(timer);
|
|
907
911
|
timer = null;
|
|
@@ -913,13 +917,13 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
913
917
|
};
|
|
914
918
|
const schedule = () => {
|
|
915
919
|
if (timer) return;
|
|
916
|
-
timer = setTimeout(
|
|
920
|
+
timer = setTimeout(flush2, intervalMs);
|
|
917
921
|
timer.unref?.();
|
|
918
922
|
};
|
|
919
923
|
return {
|
|
920
924
|
push(event) {
|
|
921
925
|
if (!isTextEvent(event)) {
|
|
922
|
-
|
|
926
|
+
flush2();
|
|
923
927
|
emit(event);
|
|
924
928
|
return;
|
|
925
929
|
}
|
|
@@ -927,12 +931,12 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
927
931
|
if (pending && pending.type === event.type) {
|
|
928
932
|
pending.data += event.data;
|
|
929
933
|
} else {
|
|
930
|
-
|
|
934
|
+
flush2();
|
|
931
935
|
pending = { type: event.type, data: event.data };
|
|
932
936
|
}
|
|
933
937
|
schedule();
|
|
934
938
|
},
|
|
935
|
-
flush
|
|
939
|
+
flush: flush2
|
|
936
940
|
};
|
|
937
941
|
}
|
|
938
942
|
|
|
@@ -944,9 +948,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
944
948
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
945
949
|
);
|
|
946
950
|
}
|
|
947
|
-
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-
|
|
951
|
+
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-KJQS5S2C.js");
|
|
948
952
|
if (isGlobalThread2(thread)) {
|
|
949
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-
|
|
953
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-3DFZRMXE.js");
|
|
950
954
|
ensureGlobalCoordinatorCwd2(
|
|
951
955
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
952
956
|
);
|
|
@@ -1887,7 +1891,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
1887
1891
|
return readThread(thread.id) ?? thread;
|
|
1888
1892
|
}
|
|
1889
1893
|
async function listLinearIssues(agent, repoPath) {
|
|
1890
|
-
const { getAdapter: getAdapter2 } = await import("../agents-
|
|
1894
|
+
const { getAdapter: getAdapter2 } = await import("../agents-UT5GCJH3.js");
|
|
1891
1895
|
await requireAgent(agent, { requireLinear: true });
|
|
1892
1896
|
const adapter = getAdapter2(agent);
|
|
1893
1897
|
if (!adapter.listLinearIssues) {
|
|
@@ -2212,8 +2216,11 @@ function worktreeBindingFrom(from) {
|
|
|
2212
2216
|
};
|
|
2213
2217
|
}
|
|
2214
2218
|
function threadsSharingWorktree(worktreePath) {
|
|
2219
|
+
if (!worktreePath) return [];
|
|
2215
2220
|
const key = normalizeWorktreePath(worktreePath);
|
|
2216
|
-
return listThreads({ includeArchived: true }).filter(
|
|
2221
|
+
return listThreads({ includeArchived: true }).filter(
|
|
2222
|
+
(t) => Boolean(t.worktreePath) && sameWorktreePath(t.worktreePath, key) && t.status !== "archived"
|
|
2223
|
+
).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
2217
2224
|
}
|
|
2218
2225
|
function formatTranscriptMarkdown(title, messages) {
|
|
2219
2226
|
const body = formatMessagesAsTranscript(messages, { tools: "full" });
|
|
@@ -2284,9 +2291,132 @@ function forkChatTab(input) {
|
|
|
2284
2291
|
return tab;
|
|
2285
2292
|
}
|
|
2286
2293
|
|
|
2294
|
+
// src/store/turn-live.ts
|
|
2295
|
+
import {
|
|
2296
|
+
existsSync as existsSync7,
|
|
2297
|
+
renameSync,
|
|
2298
|
+
unlinkSync as unlinkSync2,
|
|
2299
|
+
writeFileSync as writeFileSync2,
|
|
2300
|
+
readFileSync as readFileSync5
|
|
2301
|
+
} from "fs";
|
|
2302
|
+
var buffers = /* @__PURE__ */ new Map();
|
|
2303
|
+
var FLUSH_MS = 800;
|
|
2304
|
+
var MAX_PARTS = 80;
|
|
2305
|
+
function trimParts(parts) {
|
|
2306
|
+
if (parts.length <= MAX_PARTS) return parts;
|
|
2307
|
+
return parts.slice(-Math.floor(MAX_PARTS / 2));
|
|
2308
|
+
}
|
|
2309
|
+
function lastText(parts, type) {
|
|
2310
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
2311
|
+
const p = parts[i];
|
|
2312
|
+
if (p.type === type && p.text.trim()) return p.text.trim();
|
|
2313
|
+
}
|
|
2314
|
+
return "";
|
|
2315
|
+
}
|
|
2316
|
+
function summarizeTurnLive(parts) {
|
|
2317
|
+
const tools = parts.filter(
|
|
2318
|
+
(p) => p.type === "tool"
|
|
2319
|
+
);
|
|
2320
|
+
const lastTool = tools[tools.length - 1];
|
|
2321
|
+
const lastToolLabel = lastTool ? lastTool.description || toolDescription(lastTool.name, lastTool.input) || lastTool.name : void 0;
|
|
2322
|
+
const running = lastTool?.status === "running";
|
|
2323
|
+
const thinking = lastText(parts, "thinking");
|
|
2324
|
+
const text3 = lastText(parts, "text");
|
|
2325
|
+
const excerptRaw = thinking || text3;
|
|
2326
|
+
const excerpt = excerptRaw.length > 280 ? `${excerptRaw.slice(-280)}` : excerptRaw || void 0;
|
|
2327
|
+
let summary = "Working\u2026";
|
|
2328
|
+
if (lastToolLabel) {
|
|
2329
|
+
const verb = running ? lastToolLabel : `Finished ${lastToolLabel}`;
|
|
2330
|
+
summary = tools.length > 1 ? `${verb} (${tools.length} tools)` : verb;
|
|
2331
|
+
} else if (thinking) {
|
|
2332
|
+
summary = "Thinking\u2026";
|
|
2333
|
+
} else if (text3) {
|
|
2334
|
+
summary = "Writing reply\u2026";
|
|
2335
|
+
}
|
|
2336
|
+
return {
|
|
2337
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2338
|
+
summary,
|
|
2339
|
+
lastTool: lastToolLabel,
|
|
2340
|
+
toolCount: tools.length,
|
|
2341
|
+
excerpt
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
function flush(threadId) {
|
|
2345
|
+
const buf = buffers.get(threadId);
|
|
2346
|
+
if (!buf) return;
|
|
2347
|
+
if (buf.timer) {
|
|
2348
|
+
clearTimeout(buf.timer);
|
|
2349
|
+
buf.timer = null;
|
|
2350
|
+
}
|
|
2351
|
+
writeTurnLive(threadId, summarizeTurnLive(buf.parts));
|
|
2352
|
+
}
|
|
2353
|
+
function scheduleFlush(threadId) {
|
|
2354
|
+
const buf = buffers.get(threadId);
|
|
2355
|
+
if (!buf) return;
|
|
2356
|
+
if (buf.timer) return;
|
|
2357
|
+
buf.timer = setTimeout(() => {
|
|
2358
|
+
const live = buffers.get(threadId);
|
|
2359
|
+
if (live) live.timer = null;
|
|
2360
|
+
flush(threadId);
|
|
2361
|
+
}, FLUSH_MS);
|
|
2362
|
+
}
|
|
2363
|
+
function noteTurnLiveEvent(threadId, event) {
|
|
2364
|
+
if (event.type === "session_id" || event.type === "usage" || event.type === "stderr") {
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
const prev = buffers.get(threadId) ?? { parts: [], timer: null };
|
|
2368
|
+
prev.parts = trimParts(applyAgentEvent(prev.parts, event));
|
|
2369
|
+
buffers.set(threadId, prev);
|
|
2370
|
+
if (event.type === "exit") {
|
|
2371
|
+
flush(threadId);
|
|
2372
|
+
const cur = buffers.get(threadId);
|
|
2373
|
+
if (cur?.timer) clearTimeout(cur.timer);
|
|
2374
|
+
buffers.delete(threadId);
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
const immediate = event.type === "tool_use" || event.type === "tool_result";
|
|
2378
|
+
if (immediate) flush(threadId);
|
|
2379
|
+
else scheduleFlush(threadId);
|
|
2380
|
+
}
|
|
2381
|
+
function writeTurnLive(threadId, progress) {
|
|
2382
|
+
const path = threadLivePath(threadId);
|
|
2383
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
2384
|
+
try {
|
|
2385
|
+
writeFileSync2(tmp, JSON.stringify(progress), "utf8");
|
|
2386
|
+
renameSync(tmp, path);
|
|
2387
|
+
} catch {
|
|
2388
|
+
try {
|
|
2389
|
+
unlinkSync2(tmp);
|
|
2390
|
+
} catch {
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
function readTurnLive(threadId) {
|
|
2395
|
+
const path = threadLivePath(threadId);
|
|
2396
|
+
if (!existsSync7(path)) return null;
|
|
2397
|
+
try {
|
|
2398
|
+
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
2399
|
+
if (!raw || typeof raw.summary !== "string") return null;
|
|
2400
|
+
return raw;
|
|
2401
|
+
} catch {
|
|
2402
|
+
return null;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
function clearTurnLive(threadId) {
|
|
2406
|
+
const buf = buffers.get(threadId);
|
|
2407
|
+
if (buf?.timer) clearTimeout(buf.timer);
|
|
2408
|
+
buffers.delete(threadId);
|
|
2409
|
+
const path = threadLivePath(threadId);
|
|
2410
|
+
if (!existsSync7(path)) return;
|
|
2411
|
+
try {
|
|
2412
|
+
unlinkSync2(path);
|
|
2413
|
+
} catch {
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2287
2417
|
// src/review/request-review.ts
|
|
2288
2418
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2289
|
-
import { existsSync as
|
|
2419
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2290
2420
|
import { dirname as dirname2, join as join8 } from "path";
|
|
2291
2421
|
|
|
2292
2422
|
// src/review/review-request-template.ts
|
|
@@ -2432,9 +2562,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
2432
2562
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
2433
2563
|
}
|
|
2434
2564
|
function readTextIfPresent(abs) {
|
|
2435
|
-
if (!
|
|
2565
|
+
if (!existsSync8(abs)) return null;
|
|
2436
2566
|
try {
|
|
2437
|
-
const content =
|
|
2567
|
+
const content = readFileSync6(abs, "utf8");
|
|
2438
2568
|
return content.trim() ? content : null;
|
|
2439
2569
|
} catch {
|
|
2440
2570
|
return null;
|
|
@@ -2442,9 +2572,9 @@ function readTextIfPresent(abs) {
|
|
|
2442
2572
|
}
|
|
2443
2573
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
2444
2574
|
const gitignoreAbs = join8(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
2445
|
-
if (
|
|
2575
|
+
if (existsSync8(gitignoreAbs)) return;
|
|
2446
2576
|
mkdirSync2(dirname2(gitignoreAbs), { recursive: true });
|
|
2447
|
-
|
|
2577
|
+
writeFileSync3(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
2448
2578
|
}
|
|
2449
2579
|
function resolveReviewGuidelines(worktreePath) {
|
|
2450
2580
|
const repoAbs = join8(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -2479,7 +2609,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
2479
2609
|
}
|
|
2480
2610
|
ensureAttachmentsGitignore(worktreePath);
|
|
2481
2611
|
mkdirSync2(dirname2(localAbs), { recursive: true });
|
|
2482
|
-
|
|
2612
|
+
writeFileSync3(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
2483
2613
|
return {
|
|
2484
2614
|
path: REVIEW_REQUEST_PATH,
|
|
2485
2615
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -2675,10 +2805,10 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
2675
2805
|
import { execFileSync } from "child_process";
|
|
2676
2806
|
import {
|
|
2677
2807
|
copyFileSync as copyFileSync2,
|
|
2678
|
-
existsSync as
|
|
2808
|
+
existsSync as existsSync9,
|
|
2679
2809
|
mkdtempSync,
|
|
2680
2810
|
readdirSync as readdirSync3,
|
|
2681
|
-
readFileSync as
|
|
2811
|
+
readFileSync as readFileSync7,
|
|
2682
2812
|
rmSync
|
|
2683
2813
|
} from "fs";
|
|
2684
2814
|
import { tmpdir } from "os";
|
|
@@ -2708,7 +2838,7 @@ function mapAgentType(raw) {
|
|
|
2708
2838
|
return null;
|
|
2709
2839
|
}
|
|
2710
2840
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
2711
|
-
if (!workspacePath || !
|
|
2841
|
+
if (!workspacePath || !existsSync9(CURSOR_SDK_STORE)) return null;
|
|
2712
2842
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
2713
2843
|
let best = null;
|
|
2714
2844
|
let hashes;
|
|
@@ -2719,10 +2849,10 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2719
2849
|
}
|
|
2720
2850
|
for (const hash of hashes) {
|
|
2721
2851
|
const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
2722
|
-
if (!
|
|
2852
|
+
if (!existsSync9(agentsFile)) continue;
|
|
2723
2853
|
let text3;
|
|
2724
2854
|
try {
|
|
2725
|
-
text3 =
|
|
2855
|
+
text3 = readFileSync7(agentsFile, "utf8");
|
|
2726
2856
|
} catch {
|
|
2727
2857
|
continue;
|
|
2728
2858
|
}
|
|
@@ -2746,7 +2876,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2746
2876
|
return best?.agentId ?? null;
|
|
2747
2877
|
}
|
|
2748
2878
|
async function adoptThread(input) {
|
|
2749
|
-
if (!
|
|
2879
|
+
if (!existsSync9(input.worktreePath)) {
|
|
2750
2880
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
2751
2881
|
}
|
|
2752
2882
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -2765,12 +2895,12 @@ async function adoptThread(input) {
|
|
|
2765
2895
|
messages: input.messages ?? []
|
|
2766
2896
|
});
|
|
2767
2897
|
writeThread(thread);
|
|
2768
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
2898
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-NTIVI6DI.js");
|
|
2769
2899
|
await ensureWorkspace2(repoPath);
|
|
2770
2900
|
return thread;
|
|
2771
2901
|
}
|
|
2772
2902
|
function listConductorWorkspaces() {
|
|
2773
|
-
if (!
|
|
2903
|
+
if (!existsSync9(CONDUCTOR_DB)) {
|
|
2774
2904
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2775
2905
|
}
|
|
2776
2906
|
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
@@ -2779,7 +2909,7 @@ function listConductorWorkspaces() {
|
|
|
2779
2909
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2780
2910
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2781
2911
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2782
|
-
if (
|
|
2912
|
+
if (existsSync9(src)) {
|
|
2783
2913
|
try {
|
|
2784
2914
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
2785
2915
|
} catch {
|
|
@@ -2861,7 +2991,7 @@ function listConductorWorkspaces() {
|
|
|
2861
2991
|
}
|
|
2862
2992
|
}
|
|
2863
2993
|
function importConductorWorkspace(workspaceId) {
|
|
2864
|
-
if (!
|
|
2994
|
+
if (!existsSync9(CONDUCTOR_DB)) {
|
|
2865
2995
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2866
2996
|
}
|
|
2867
2997
|
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
@@ -2870,7 +3000,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2870
3000
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2871
3001
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2872
3002
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2873
|
-
if (
|
|
3003
|
+
if (existsSync9(src)) {
|
|
2874
3004
|
try {
|
|
2875
3005
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
2876
3006
|
} catch {
|
|
@@ -2890,7 +3020,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2890
3020
|
).get(workspaceId);
|
|
2891
3021
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
2892
3022
|
const worktreePath = String(row.workspacePath);
|
|
2893
|
-
if (!
|
|
3023
|
+
if (!existsSync9(worktreePath)) {
|
|
2894
3024
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
2895
3025
|
}
|
|
2896
3026
|
let sessionId = null;
|
|
@@ -2961,7 +3091,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
2961
3091
|
}
|
|
2962
3092
|
|
|
2963
3093
|
// src/threads/stack-layers.ts
|
|
2964
|
-
import { existsSync as
|
|
3094
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2965
3095
|
function stackIdFrom(stack) {
|
|
2966
3096
|
if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
|
|
2967
3097
|
const key = stack.layers.map((l) => l.branchName).join("|");
|
|
@@ -3024,7 +3154,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
3024
3154
|
let createdWorktree = false;
|
|
3025
3155
|
const trees = await listWorktrees(repoPath);
|
|
3026
3156
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
3027
|
-
if (checkedOut?.path &&
|
|
3157
|
+
if (checkedOut?.path && existsSync10(checkedOut.path)) {
|
|
3028
3158
|
if (input.reuseExistingWorktree !== false) {
|
|
3029
3159
|
worktreePath = checkedOut.path;
|
|
3030
3160
|
} else {
|
|
@@ -3166,7 +3296,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
3166
3296
|
async function createPrStack(input, onSetupLine) {
|
|
3167
3297
|
await requireAgent(input.agent);
|
|
3168
3298
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
3169
|
-
if (!
|
|
3299
|
+
if (!existsSync10(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
3170
3300
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
3171
3301
|
const status = await detectGhStack(repoPath);
|
|
3172
3302
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -3233,7 +3363,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
3233
3363
|
}
|
|
3234
3364
|
}
|
|
3235
3365
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
3236
|
-
if (!claimed.has(bootstrap.worktreePath) &&
|
|
3366
|
+
if (!claimed.has(bootstrap.worktreePath) && existsSync10(bootstrap.worktreePath)) {
|
|
3237
3367
|
try {
|
|
3238
3368
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
3239
3369
|
deleteBranch: bootstrap.branchName
|
|
@@ -3245,10 +3375,10 @@ async function createPrStack(input, onSetupLine) {
|
|
|
3245
3375
|
}
|
|
3246
3376
|
|
|
3247
3377
|
// src/diff/diff.ts
|
|
3248
|
-
import { existsSync as
|
|
3378
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
3249
3379
|
import { dirname as dirname3, join as join10 } from "path";
|
|
3250
3380
|
async function inspectGitWorktree(worktreePath) {
|
|
3251
|
-
if (!worktreePath || !
|
|
3381
|
+
if (!worktreePath || !existsSync11(worktreePath)) return "missing_worktree";
|
|
3252
3382
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
3253
3383
|
reject: false
|
|
3254
3384
|
});
|
|
@@ -3256,7 +3386,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
3256
3386
|
return "ok";
|
|
3257
3387
|
}
|
|
3258
3388
|
async function initializeGitRepository(worktreePath) {
|
|
3259
|
-
if (!worktreePath || !
|
|
3389
|
+
if (!worktreePath || !existsSync11(worktreePath)) {
|
|
3260
3390
|
throw new Error("Worktree not found");
|
|
3261
3391
|
}
|
|
3262
3392
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -3396,7 +3526,7 @@ async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
|
3396
3526
|
try {
|
|
3397
3527
|
const st = statSync2(abs);
|
|
3398
3528
|
if (st.isFile() && st.size > maxHunk) {
|
|
3399
|
-
const buf =
|
|
3529
|
+
const buf = readFileSync8(abs).subarray(0, maxHunk);
|
|
3400
3530
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
3401
3531
|
}
|
|
3402
3532
|
} catch {
|
|
@@ -3907,7 +4037,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
3907
4037
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
3908
4038
|
);
|
|
3909
4039
|
}
|
|
3910
|
-
const buf =
|
|
4040
|
+
const buf = readFileSync8(abs);
|
|
3911
4041
|
return {
|
|
3912
4042
|
path: relativePath,
|
|
3913
4043
|
contentBase64: buf.toString("base64"),
|
|
@@ -3922,7 +4052,7 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
3922
4052
|
if (!st.isFile()) {
|
|
3923
4053
|
throw new Error(`Not a file: ${relativePath}`);
|
|
3924
4054
|
}
|
|
3925
|
-
const buf =
|
|
4055
|
+
const buf = readFileSync8(abs);
|
|
3926
4056
|
if (isImageRelativePath(relativePath)) {
|
|
3927
4057
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
3928
4058
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -3967,7 +4097,7 @@ function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
|
3967
4097
|
assertSafeRelativePath(relativePath);
|
|
3968
4098
|
const abs = join10(worktreePath, relativePath);
|
|
3969
4099
|
mkdirSync3(dirname3(abs), { recursive: true });
|
|
3970
|
-
|
|
4100
|
+
writeFileSync4(abs, content, "utf8");
|
|
3971
4101
|
return { path: relativePath };
|
|
3972
4102
|
}
|
|
3973
4103
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -4070,7 +4200,7 @@ async function confirmLand(thread, opts) {
|
|
|
4070
4200
|
}
|
|
4071
4201
|
|
|
4072
4202
|
// src/skills/discover.ts
|
|
4073
|
-
import { existsSync as
|
|
4203
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
|
|
4074
4204
|
import { homedir } from "os";
|
|
4075
4205
|
import { join as join11 } from "path";
|
|
4076
4206
|
function toCommand(name) {
|
|
@@ -4104,7 +4234,7 @@ function parseFrontmatter(content) {
|
|
|
4104
4234
|
}
|
|
4105
4235
|
function readSkill(skillMd, source) {
|
|
4106
4236
|
try {
|
|
4107
|
-
const content =
|
|
4237
|
+
const content = readFileSync9(skillMd, "utf8");
|
|
4108
4238
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
4109
4239
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
4110
4240
|
const name = fmName || dirName;
|
|
@@ -4123,7 +4253,7 @@ function readSkill(skillMd, source) {
|
|
|
4123
4253
|
}
|
|
4124
4254
|
}
|
|
4125
4255
|
function scanSkillsDir(dir, source, out) {
|
|
4126
|
-
if (!
|
|
4256
|
+
if (!existsSync12(dir)) return;
|
|
4127
4257
|
let entries;
|
|
4128
4258
|
try {
|
|
4129
4259
|
entries = readdirSync4(dir);
|
|
@@ -4133,7 +4263,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4133
4263
|
for (const entry of entries) {
|
|
4134
4264
|
if (entry.startsWith(".")) continue;
|
|
4135
4265
|
const skillMd = join11(dir, entry, "SKILL.md");
|
|
4136
|
-
if (!
|
|
4266
|
+
if (!existsSync12(skillMd)) continue;
|
|
4137
4267
|
try {
|
|
4138
4268
|
if (!statSync3(skillMd).isFile()) continue;
|
|
4139
4269
|
} catch {
|
|
@@ -4144,7 +4274,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4144
4274
|
}
|
|
4145
4275
|
}
|
|
4146
4276
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
4147
|
-
if (!
|
|
4277
|
+
if (!existsSync12(pluginsRoot)) return;
|
|
4148
4278
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
4149
4279
|
if (depth > 7) return;
|
|
4150
4280
|
let entries;
|
|
@@ -4201,7 +4331,7 @@ function discoverSkills(worktreePath) {
|
|
|
4201
4331
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
4202
4332
|
}
|
|
4203
4333
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
4204
|
-
const raw =
|
|
4334
|
+
const raw = readFileSync9(skillPath, "utf8");
|
|
4205
4335
|
if (raw.startsWith("---")) {
|
|
4206
4336
|
const end = raw.indexOf("\n---", 3);
|
|
4207
4337
|
if (end >= 0) {
|
|
@@ -4294,7 +4424,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
4294
4424
|
}
|
|
4295
4425
|
|
|
4296
4426
|
// src/composer/stage-files.ts
|
|
4297
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
4427
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4298
4428
|
import { basename as basename3, extname, join as join12 } from "path";
|
|
4299
4429
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4300
4430
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
@@ -4333,19 +4463,19 @@ function ensureAttachmentsDir(worktreePath) {
|
|
|
4333
4463
|
const dir = join12(worktreePath, ATTACHMENTS_DIR);
|
|
4334
4464
|
mkdirSync4(dir, { recursive: true });
|
|
4335
4465
|
const gi = join12(dir, ".gitignore");
|
|
4336
|
-
if (!
|
|
4337
|
-
|
|
4466
|
+
if (!existsSync13(gi)) {
|
|
4467
|
+
writeFileSync5(gi, attachmentsGitignoreBody(), "utf8");
|
|
4338
4468
|
}
|
|
4339
4469
|
return dir;
|
|
4340
4470
|
}
|
|
4341
4471
|
function uniqueAttachmentName(dir, originalName) {
|
|
4342
4472
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
4343
|
-
if (!
|
|
4473
|
+
if (!existsSync13(join12(dir, safe))) return safe;
|
|
4344
4474
|
const ext = extname(safe);
|
|
4345
4475
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
4346
4476
|
for (let i = 1; i < 1e4; i++) {
|
|
4347
4477
|
const candidate = `${stem}-${i}${ext}`;
|
|
4348
|
-
if (!
|
|
4478
|
+
if (!existsSync13(join12(dir, candidate))) return candidate;
|
|
4349
4479
|
}
|
|
4350
4480
|
return `${stem}-${randomUUID4()}${ext}`;
|
|
4351
4481
|
}
|
|
@@ -4409,7 +4539,7 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
4409
4539
|
const destAbs = join12(dir, name);
|
|
4410
4540
|
copyFileSync3(abs, destAbs);
|
|
4411
4541
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4412
|
-
const buf =
|
|
4542
|
+
const buf = readFileSync10(destAbs);
|
|
4413
4543
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4414
4544
|
} catch (err) {
|
|
4415
4545
|
out.push({
|
|
@@ -4422,17 +4552,17 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
4422
4552
|
}
|
|
4423
4553
|
return out;
|
|
4424
4554
|
}
|
|
4425
|
-
function stageBuffersAsAttachments(worktreePath,
|
|
4426
|
-
if (
|
|
4555
|
+
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
4556
|
+
if (buffers2.length === 0) return [];
|
|
4427
4557
|
const dir = ensureAttachmentsDir(worktreePath);
|
|
4428
4558
|
const out = [];
|
|
4429
|
-
for (const item of
|
|
4559
|
+
for (const item of buffers2) {
|
|
4430
4560
|
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
4431
4561
|
try {
|
|
4432
4562
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
4433
4563
|
const name = uniqueAttachmentName(dir, originalName);
|
|
4434
4564
|
const destAbs = join12(dir, name);
|
|
4435
|
-
|
|
4565
|
+
writeFileSync5(destAbs, buf);
|
|
4436
4566
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4437
4567
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
4438
4568
|
} catch (err) {
|
|
@@ -4463,7 +4593,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4463
4593
|
const abs = join12(worktreePath, rel);
|
|
4464
4594
|
const st = statSync4(abs);
|
|
4465
4595
|
if (!st.isFile()) continue;
|
|
4466
|
-
const buf =
|
|
4596
|
+
const buf = readFileSync10(abs);
|
|
4467
4597
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4468
4598
|
} catch (err) {
|
|
4469
4599
|
out.push({
|
|
@@ -4478,7 +4608,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4478
4608
|
}
|
|
4479
4609
|
|
|
4480
4610
|
// src/agents/instructions.ts
|
|
4481
|
-
import { existsSync as
|
|
4611
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
4482
4612
|
import { join as join13 } from "path";
|
|
4483
4613
|
function normPath(p) {
|
|
4484
4614
|
return p.replace(/\/+$/, "");
|
|
@@ -4668,6 +4798,15 @@ function isPidAlive(pid) {
|
|
|
4668
4798
|
return false;
|
|
4669
4799
|
}
|
|
4670
4800
|
}
|
|
4801
|
+
async function waitForPidExit(pid, timeoutMs) {
|
|
4802
|
+
const start = Date.now();
|
|
4803
|
+
while (isPidAlive(pid)) {
|
|
4804
|
+
if (Date.now() - start >= timeoutMs) return false;
|
|
4805
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
4806
|
+
}
|
|
4807
|
+
return true;
|
|
4808
|
+
}
|
|
4809
|
+
var STALE_AGENT_PID_WAIT_MS = 2500;
|
|
4671
4810
|
function writeLiveStatus(threadId, status, lastError) {
|
|
4672
4811
|
const latest = readThread(threadId);
|
|
4673
4812
|
if (!latest || latest.status === "archived") return latest;
|
|
@@ -4745,7 +4884,7 @@ var Orchestrator = class {
|
|
|
4745
4884
|
}
|
|
4746
4885
|
continue;
|
|
4747
4886
|
}
|
|
4748
|
-
if (!
|
|
4887
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
4749
4888
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
4750
4889
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
4751
4890
|
continue;
|
|
@@ -4764,7 +4903,7 @@ var Orchestrator = class {
|
|
|
4764
4903
|
orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
|
|
4765
4904
|
});
|
|
4766
4905
|
}
|
|
4767
|
-
const { autoCleanupOrphansEnabled } = await import("../app-settings-
|
|
4906
|
+
const { autoCleanupOrphansEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
4768
4907
|
if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
|
|
4769
4908
|
await cleanupOrphanWorktrees({ repoPaths });
|
|
4770
4909
|
}
|
|
@@ -4932,7 +5071,7 @@ var Orchestrator = class {
|
|
|
4932
5071
|
}
|
|
4933
5072
|
}
|
|
4934
5073
|
await setup;
|
|
4935
|
-
const { autoRunAfterSetupEnabled } = await import("../app-settings-
|
|
5074
|
+
const { autoRunAfterSetupEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
4936
5075
|
if (autoRunAfterSetupEnabled()) {
|
|
4937
5076
|
try {
|
|
4938
5077
|
await this.startDev(threadId);
|
|
@@ -5066,14 +5205,10 @@ var Orchestrator = class {
|
|
|
5066
5205
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
5067
5206
|
const livePid = current.agentPid;
|
|
5068
5207
|
const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
|
|
5069
|
-
if (inFlight) {
|
|
5208
|
+
if (inFlight || foreignLive) {
|
|
5070
5209
|
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5071
|
-
} else {
|
|
5072
|
-
if (foreignLive) {
|
|
5073
|
-
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5074
|
-
}
|
|
5075
|
-
void this.drainQueue(thread.id);
|
|
5076
5210
|
}
|
|
5211
|
+
void this.drainQueue(thread.id);
|
|
5077
5212
|
return this.requireThread(thread.id);
|
|
5078
5213
|
}
|
|
5079
5214
|
async drainQueue(threadId) {
|
|
@@ -5102,7 +5237,18 @@ var Orchestrator = class {
|
|
|
5102
5237
|
}
|
|
5103
5238
|
const livePid = thread.agentPid;
|
|
5104
5239
|
if (typeof livePid === "number" && livePid > 0 && isPidAlive(livePid)) {
|
|
5105
|
-
|
|
5240
|
+
const exited = await waitForPidExit(livePid, STALE_AGENT_PID_WAIT_MS);
|
|
5241
|
+
if (!exited && isPidAlive(livePid)) {
|
|
5242
|
+
try {
|
|
5243
|
+
process.kill(livePid, "SIGKILL");
|
|
5244
|
+
} catch {
|
|
5245
|
+
}
|
|
5246
|
+
await waitForPidExit(livePid, 400);
|
|
5247
|
+
try {
|
|
5248
|
+
updateThread(threadId, { agentPid: null });
|
|
5249
|
+
} catch {
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5106
5252
|
continue;
|
|
5107
5253
|
}
|
|
5108
5254
|
const prompt = thread.queue[0];
|
|
@@ -5210,7 +5356,7 @@ var Orchestrator = class {
|
|
|
5210
5356
|
}
|
|
5211
5357
|
const isBrightsy = fresh.agent === "brightsy";
|
|
5212
5358
|
const isOrchestration = isOrchestratorThread(fresh);
|
|
5213
|
-
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("../app-settings-
|
|
5359
|
+
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("../app-settings-VDJK2BZ2.js");
|
|
5214
5360
|
const gitAuthMode = getGithubGitAuthMode();
|
|
5215
5361
|
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
|
|
5216
5362
|
githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
|
|
@@ -5259,6 +5405,7 @@ var Orchestrator = class {
|
|
|
5259
5405
|
{ cachedPrefix, prompt: agentPrompt },
|
|
5260
5406
|
(event) => {
|
|
5261
5407
|
this.emit({ type: "turn_output", threadId, event });
|
|
5408
|
+
noteTurnLiveEvent(threadId, event);
|
|
5262
5409
|
if (event.type === "session_id") {
|
|
5263
5410
|
updateThread(threadId, { sessionId: event.data });
|
|
5264
5411
|
}
|
|
@@ -5305,7 +5452,7 @@ var Orchestrator = class {
|
|
|
5305
5452
|
if (this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
|
|
5306
5453
|
const sessionId = result.sessionId || this.requireThread(threadId).sessionId || "";
|
|
5307
5454
|
if (sessionId) {
|
|
5308
|
-
const { recoverFinishedCursorRun } = await import("../cursor-recover-
|
|
5455
|
+
const { recoverFinishedCursorRun } = await import("../cursor-recover-JBTIDARH.js");
|
|
5309
5456
|
for (let i = 0; i < 8; i++) {
|
|
5310
5457
|
const recovered = recoverFinishedCursorRun({
|
|
5311
5458
|
agentId: sessionId,
|
|
@@ -5322,19 +5469,16 @@ var Orchestrator = class {
|
|
|
5322
5469
|
}
|
|
5323
5470
|
let lastStderr = summarizeTurnStderr(stderrTail);
|
|
5324
5471
|
let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
5325
|
-
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) &&
|
|
5472
|
+
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
|
|
5473
|
+
hasSession: Boolean(this.requireThread(threadId).sessionId)
|
|
5474
|
+
})) {
|
|
5326
5475
|
updateThread(threadId, { sessionId: null });
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
"Agent session missing \u2014 starting a fresh session"
|
|
5330
|
-
);
|
|
5476
|
+
const retryNote = looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
|
|
5477
|
+
pushTurnStderr(stderrTail, retryNote);
|
|
5331
5478
|
this.emit({
|
|
5332
5479
|
type: "turn_output",
|
|
5333
5480
|
threadId,
|
|
5334
|
-
event: {
|
|
5335
|
-
type: "stderr",
|
|
5336
|
-
data: "Agent session missing \u2014 starting a fresh session"
|
|
5337
|
-
}
|
|
5481
|
+
event: { type: "stderr", data: retryNote }
|
|
5338
5482
|
});
|
|
5339
5483
|
const retryThread = this.requireThread(threadId);
|
|
5340
5484
|
const prior = retryThread.messages.slice(0, -1);
|
|
@@ -5383,10 +5527,7 @@ var Orchestrator = class {
|
|
|
5383
5527
|
lastStderr = summarizeTurnStderr(stderrTail);
|
|
5384
5528
|
detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
5385
5529
|
}
|
|
5386
|
-
let chatText = assistantText;
|
|
5387
|
-
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
5388
|
-
chatText = humanizeAgentFailDetail(detail);
|
|
5389
|
-
}
|
|
5530
|
+
let chatText = this.stoppedTurns.has(threadId) ? assistantText : turnFailChatText({ exitCode, assistantText, detail });
|
|
5390
5531
|
if (chatText || parts.length > 0) {
|
|
5391
5532
|
appendMessage(threadId, {
|
|
5392
5533
|
role: "agent",
|
|
@@ -5469,6 +5610,7 @@ var Orchestrator = class {
|
|
|
5469
5610
|
this.processes.delete(`${threadId}:agent`);
|
|
5470
5611
|
this.stoppedTurns.delete(threadId);
|
|
5471
5612
|
this.runningCount = Math.max(0, this.runningCount - 1);
|
|
5613
|
+
clearTurnLive(threadId);
|
|
5472
5614
|
try {
|
|
5473
5615
|
updateThread(threadId, { agentPid: null });
|
|
5474
5616
|
} catch {
|
|
@@ -5742,51 +5884,56 @@ var Orchestrator = class {
|
|
|
5742
5884
|
}
|
|
5743
5885
|
return created;
|
|
5744
5886
|
}
|
|
5745
|
-
async waitForTurn(threadRef, timeoutMs = 6e5) {
|
|
5887
|
+
async waitForTurn(threadRef, timeoutMs = 6e5, opts) {
|
|
5746
5888
|
const thread = this.requireThread(threadRef);
|
|
5889
|
+
if (!["running", "queued"].includes(thread.status)) {
|
|
5890
|
+
return thread;
|
|
5891
|
+
}
|
|
5747
5892
|
const start = Date.now();
|
|
5748
5893
|
return new Promise((resolve, reject) => {
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5894
|
+
let settled = false;
|
|
5895
|
+
let timer;
|
|
5896
|
+
const finish = (fn) => {
|
|
5897
|
+
if (settled) return;
|
|
5898
|
+
settled = true;
|
|
5899
|
+
if (timer) clearInterval(timer);
|
|
5900
|
+
off();
|
|
5901
|
+
fn();
|
|
5902
|
+
};
|
|
5753
5903
|
const off = this.on((event) => {
|
|
5754
5904
|
if (event.type === "turn_finished" && event.threadId === thread.id) {
|
|
5755
|
-
off();
|
|
5756
5905
|
const latest = readThread(thread.id);
|
|
5757
5906
|
if (!latest) {
|
|
5758
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5907
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5759
5908
|
return;
|
|
5760
5909
|
}
|
|
5761
|
-
resolve(latest);
|
|
5910
|
+
finish(() => resolve(latest));
|
|
5762
5911
|
}
|
|
5763
5912
|
if (event.type === "error" && event.threadId === thread.id) {
|
|
5764
|
-
off();
|
|
5765
5913
|
const latest = readThread(thread.id);
|
|
5766
5914
|
if (!latest) {
|
|
5767
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5915
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5768
5916
|
return;
|
|
5769
5917
|
}
|
|
5770
|
-
resolve(latest);
|
|
5918
|
+
finish(() => resolve(latest));
|
|
5771
5919
|
}
|
|
5772
5920
|
});
|
|
5773
|
-
|
|
5774
|
-
if (Date.now() - start > timeoutMs) {
|
|
5775
|
-
clearInterval(timer);
|
|
5776
|
-
off();
|
|
5777
|
-
reject(new Error("wait_for_turn timed out"));
|
|
5778
|
-
}
|
|
5921
|
+
timer = setInterval(() => {
|
|
5779
5922
|
const current = readThread(thread.id);
|
|
5780
5923
|
if (!current) {
|
|
5781
|
-
|
|
5782
|
-
off();
|
|
5783
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5924
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5784
5925
|
return;
|
|
5785
5926
|
}
|
|
5786
5927
|
if (!["running", "queued"].includes(current.status)) {
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5928
|
+
finish(() => resolve(current));
|
|
5929
|
+
return;
|
|
5930
|
+
}
|
|
5931
|
+
if (Date.now() - start > timeoutMs) {
|
|
5932
|
+
if (opts?.resolveIfStillRunning) {
|
|
5933
|
+
finish(() => resolve(current));
|
|
5934
|
+
} else {
|
|
5935
|
+
finish(() => reject(new Error("wait_for_turn timed out")));
|
|
5936
|
+
}
|
|
5790
5937
|
}
|
|
5791
5938
|
}, 200);
|
|
5792
5939
|
});
|
|
@@ -5794,10 +5941,18 @@ var Orchestrator = class {
|
|
|
5794
5941
|
getTurnResult(threadRef) {
|
|
5795
5942
|
const thread = this.requireThread(threadRef);
|
|
5796
5943
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
5944
|
+
const lastError = thread.lastError ?? null;
|
|
5945
|
+
const text3 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
|
|
5946
|
+
const stillRunning = thread.status === "running" || thread.status === "queued";
|
|
5947
|
+
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
5797
5948
|
return {
|
|
5798
|
-
text:
|
|
5949
|
+
text: text3,
|
|
5799
5950
|
status: thread.status,
|
|
5800
|
-
sessionId: thread.sessionId
|
|
5951
|
+
sessionId: thread.sessionId,
|
|
5952
|
+
lastError,
|
|
5953
|
+
stillRunning,
|
|
5954
|
+
progress: live?.summary ?? null,
|
|
5955
|
+
lastActivityAt: live?.updatedAt ?? null
|
|
5801
5956
|
};
|
|
5802
5957
|
}
|
|
5803
5958
|
assertNotGlobal(thread, action) {
|
|
@@ -5960,7 +6115,7 @@ var Orchestrator = class {
|
|
|
5960
6115
|
updateThread(thread.id, { title: meta.title });
|
|
5961
6116
|
}
|
|
5962
6117
|
}
|
|
5963
|
-
const { autoArchiveOnMergeEnabled } = await import("../app-settings-
|
|
6118
|
+
const { autoArchiveOnMergeEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
5964
6119
|
const latest = this.requireThread(thread.id);
|
|
5965
6120
|
if (!shouldAutoArchiveOnPrMerge({
|
|
5966
6121
|
previousPrState: prevState || null,
|
|
@@ -6223,7 +6378,19 @@ var Orchestrator = class {
|
|
|
6223
6378
|
const thread = this.requireThread(threadRef);
|
|
6224
6379
|
return threadsSharingWorktree(thread.worktreePath);
|
|
6225
6380
|
}
|
|
6381
|
+
/**
|
|
6382
|
+
* Last-tab teardown must see siblings already archived. Serialize per
|
|
6383
|
+
* worktree so parallel archive/purge of sibling tabs still removes once.
|
|
6384
|
+
*/
|
|
6385
|
+
enqueueWorktreeTeardown(thread, fn) {
|
|
6386
|
+
const key = isGlobalThread(thread) ? `archive:global:${thread.id}` : `archive:wt:${normalizeWorktreePath(thread.worktreePath)}`;
|
|
6387
|
+
return enqueueByKey(key, fn);
|
|
6388
|
+
}
|
|
6226
6389
|
async archive(threadRef) {
|
|
6390
|
+
const thread = this.requireThread(threadRef);
|
|
6391
|
+
return this.enqueueWorktreeTeardown(thread, () => this.archiveUnlocked(threadRef));
|
|
6392
|
+
}
|
|
6393
|
+
async archiveUnlocked(threadRef) {
|
|
6227
6394
|
const thread = this.requireThread(threadRef);
|
|
6228
6395
|
this.stop(thread.id);
|
|
6229
6396
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -6251,7 +6418,7 @@ var Orchestrator = class {
|
|
|
6251
6418
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
6252
6419
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
6253
6420
|
try {
|
|
6254
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
6421
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-NTIVI6DI.js");
|
|
6255
6422
|
await ensureWorkspace2(thread.repoPath);
|
|
6256
6423
|
} catch {
|
|
6257
6424
|
}
|
|
@@ -6259,6 +6426,10 @@ var Orchestrator = class {
|
|
|
6259
6426
|
return archived;
|
|
6260
6427
|
}
|
|
6261
6428
|
async purge(threadRef, opts) {
|
|
6429
|
+
const thread = this.requireThread(threadRef);
|
|
6430
|
+
await this.enqueueWorktreeTeardown(thread, () => this.purgeUnlocked(threadRef, opts));
|
|
6431
|
+
}
|
|
6432
|
+
async purgeUnlocked(threadRef, opts) {
|
|
6262
6433
|
const thread = this.requireThread(threadRef);
|
|
6263
6434
|
this.stop(thread.id);
|
|
6264
6435
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -6273,7 +6444,7 @@ var Orchestrator = class {
|
|
|
6273
6444
|
await runArchiveScript(thread.repoPath, thread.worktreePath);
|
|
6274
6445
|
} catch {
|
|
6275
6446
|
}
|
|
6276
|
-
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-
|
|
6447
|
+
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
6277
6448
|
const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
|
|
6278
6449
|
await removeWorktree(thread.repoPath, thread.worktreePath, {
|
|
6279
6450
|
deleteBranch: deleteBranch ? thread.branchName : void 0
|
|
@@ -6287,19 +6458,18 @@ var Orchestrator = class {
|
|
|
6287
6458
|
throw new Error("Thread is not archived");
|
|
6288
6459
|
}
|
|
6289
6460
|
if (isGlobalThread(thread)) {
|
|
6290
|
-
const { globalAgentCwd } = await import("../paths-
|
|
6461
|
+
const { globalAgentCwd } = await import("../paths-QCI3MTO5.js");
|
|
6291
6462
|
updateThread(thread.id, { worktreePath: globalAgentCwd() });
|
|
6292
6463
|
const restored2 = setStatus(thread.id, "idle");
|
|
6293
6464
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
6294
6465
|
return restored2;
|
|
6295
6466
|
}
|
|
6296
|
-
if (!
|
|
6297
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-
|
|
6298
|
-
const { execa: execa5 } = await import("execa");
|
|
6467
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
6468
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-NKWN5ZTS.js");
|
|
6299
6469
|
const slug = thread.worktreePath.split("/").pop();
|
|
6300
6470
|
const dest = thread.worktreePath;
|
|
6301
|
-
await
|
|
6302
|
-
|
|
6471
|
+
await withRepoGitLock(thread.repoPath, async () => {
|
|
6472
|
+
await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
|
|
6303
6473
|
});
|
|
6304
6474
|
void createThreadWorktree2;
|
|
6305
6475
|
void slug;
|
|
@@ -6846,6 +7016,15 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
6846
7016
|
return null;
|
|
6847
7017
|
}
|
|
6848
7018
|
|
|
7019
|
+
// src/mcp/wait-for-turn.ts
|
|
7020
|
+
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
7021
|
+
function mcpWaitForTurnTimeoutMs(requested) {
|
|
7022
|
+
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
7023
|
+
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
7024
|
+
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
7025
|
+
}
|
|
7026
|
+
var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
|
|
7027
|
+
|
|
6849
7028
|
// src/mcp/slack-tools.ts
|
|
6850
7029
|
import { z } from "zod";
|
|
6851
7030
|
|
|
@@ -7473,7 +7652,7 @@ async function startMcpServer() {
|
|
|
7473
7652
|
);
|
|
7474
7653
|
}
|
|
7475
7654
|
try {
|
|
7476
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
7655
|
+
const { maxConcurrentAgents } = await import("../app-settings-VDJK2BZ2.js");
|
|
7477
7656
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
7478
7657
|
} catch {
|
|
7479
7658
|
}
|
|
@@ -7516,7 +7695,9 @@ async function startMcpServer() {
|
|
|
7516
7695
|
const threads = orch.getThreads(true);
|
|
7517
7696
|
const lines = threads.map((t) => {
|
|
7518
7697
|
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : basename4(t.repoPath) || t.repoPath;
|
|
7519
|
-
|
|
7698
|
+
const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
|
|
7699
|
+
const progress = live?.summary ? ` ${live.summary}` : "";
|
|
7700
|
+
return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}${progress}`;
|
|
7520
7701
|
});
|
|
7521
7702
|
return {
|
|
7522
7703
|
content: [{ type: "text", text: lines.join("\n") || "(no threads)" }]
|
|
@@ -7525,13 +7706,14 @@ async function startMcpServer() {
|
|
|
7525
7706
|
);
|
|
7526
7707
|
server.tool(
|
|
7527
7708
|
"get_thread",
|
|
7528
|
-
"Get a compact thread summary by id/ref",
|
|
7709
|
+
"Get a compact thread summary by id/ref. While running, includes progress (last tool/thinking) and lastActivityAt.",
|
|
7529
7710
|
{ ref: z3.string() },
|
|
7530
7711
|
async ({ ref }) => {
|
|
7531
7712
|
const t = orch.getThread(ref);
|
|
7532
7713
|
if (!t) {
|
|
7533
7714
|
return { content: [{ type: "text", text: `Thread not found: ${ref}` }], isError: true };
|
|
7534
7715
|
}
|
|
7716
|
+
const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
|
|
7535
7717
|
const summary = {
|
|
7536
7718
|
id: t.id,
|
|
7537
7719
|
title: t.title,
|
|
@@ -7546,7 +7728,10 @@ async function startMcpServer() {
|
|
|
7546
7728
|
messageCount: t.messages.length,
|
|
7547
7729
|
devPort: t.devPort,
|
|
7548
7730
|
prUrl: t.prUrl,
|
|
7549
|
-
lastError: t.lastError ?? null
|
|
7731
|
+
lastError: t.lastError ?? null,
|
|
7732
|
+
stillRunning: t.status === "running" || t.status === "queued",
|
|
7733
|
+
progress: live?.summary ?? null,
|
|
7734
|
+
lastActivityAt: live?.updatedAt ?? null
|
|
7550
7735
|
};
|
|
7551
7736
|
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
7552
7737
|
}
|
|
@@ -7682,8 +7867,8 @@ async function startMcpServer() {
|
|
|
7682
7867
|
registerSlackTools(server);
|
|
7683
7868
|
registerLinearTools(server);
|
|
7684
7869
|
if (sideboardMcpProfile() !== "worktree") {
|
|
7685
|
-
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-
|
|
7686
|
-
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults } = await import("../app-settings-
|
|
7870
|
+
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-NBRUWYXO.js");
|
|
7871
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults } = await import("../app-settings-VDJK2BZ2.js");
|
|
7687
7872
|
const accountDefaults = resolveThreadDefaults();
|
|
7688
7873
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
7689
7874
|
server.tool(
|
|
@@ -7872,13 +8057,15 @@ async function startMcpServer() {
|
|
|
7872
8057
|
);
|
|
7873
8058
|
server.tool(
|
|
7874
8059
|
"wait_for_turn",
|
|
7875
|
-
"
|
|
8060
|
+
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. If stillRunning is true, progress is tools/thinking \u2014 call wait_for_turn again. Do not send a check-in prompt or assume a hang. On status error, lastError/text is the failure.",
|
|
7876
8061
|
{
|
|
7877
8062
|
ref: z3.string(),
|
|
7878
8063
|
timeoutMs: z3.number().optional()
|
|
7879
8064
|
},
|
|
7880
8065
|
async ({ ref, timeoutMs }) => {
|
|
7881
|
-
const thread = await orch.waitForTurn(ref, timeoutMs
|
|
8066
|
+
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
8067
|
+
resolveIfStillRunning: true
|
|
8068
|
+
});
|
|
7882
8069
|
const result = orch.getTurnResult(thread.id);
|
|
7883
8070
|
return {
|
|
7884
8071
|
content: [
|
|
@@ -7887,7 +8074,12 @@ async function startMcpServer() {
|
|
|
7887
8074
|
text: JSON.stringify({
|
|
7888
8075
|
id: thread.id,
|
|
7889
8076
|
status: result.status,
|
|
7890
|
-
text: result.text
|
|
8077
|
+
text: result.text,
|
|
8078
|
+
lastError: result.lastError,
|
|
8079
|
+
stillRunning: result.stillRunning,
|
|
8080
|
+
progress: result.progress,
|
|
8081
|
+
lastActivityAt: result.lastActivityAt,
|
|
8082
|
+
hint: result.stillRunning ? MCP_WAIT_STILL_RUNNING_HINT : void 0
|
|
7891
8083
|
})
|
|
7892
8084
|
}
|
|
7893
8085
|
]
|
|
@@ -7896,11 +8088,21 @@ async function startMcpServer() {
|
|
|
7896
8088
|
);
|
|
7897
8089
|
server.tool(
|
|
7898
8090
|
"get_turn_result",
|
|
7899
|
-
"
|
|
8091
|
+
"Assistant message when the turn finished, or live progress while stillRunning. Not the full transcript.",
|
|
7900
8092
|
{ ref: z3.string() },
|
|
7901
8093
|
async ({ ref }) => {
|
|
7902
8094
|
const result = orch.getTurnResult(ref);
|
|
7903
|
-
return {
|
|
8095
|
+
return {
|
|
8096
|
+
content: [
|
|
8097
|
+
{
|
|
8098
|
+
type: "text",
|
|
8099
|
+
text: JSON.stringify({
|
|
8100
|
+
...result,
|
|
8101
|
+
hint: result.stillRunning ? MCP_WAIT_STILL_RUNNING_HINT : void 0
|
|
8102
|
+
})
|
|
8103
|
+
}
|
|
8104
|
+
]
|
|
8105
|
+
};
|
|
7904
8106
|
}
|
|
7905
8107
|
);
|
|
7906
8108
|
server.tool(
|
|
@@ -8017,7 +8219,7 @@ async function startMcpServer() {
|
|
|
8017
8219
|
);
|
|
8018
8220
|
server.tool(
|
|
8019
8221
|
"request_review",
|
|
8020
|
-
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
|
|
8222
|
+
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
|
|
8021
8223
|
{ ref: z3.string().describe("Worktree thread id/ref to review") },
|
|
8022
8224
|
async ({ ref }) => {
|
|
8023
8225
|
try {
|
|
@@ -8045,7 +8247,7 @@ async function startMcpServer() {
|
|
|
8045
8247
|
);
|
|
8046
8248
|
server.tool(
|
|
8047
8249
|
"ask_git",
|
|
8048
|
-
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn. Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
|
|
8250
|
+
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
|
|
8049
8251
|
{
|
|
8050
8252
|
ref: z3.string().describe("Worktree thread id/ref"),
|
|
8051
8253
|
action: z3.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -8096,7 +8298,7 @@ async function startMcpServer() {
|
|
|
8096
8298
|
);
|
|
8097
8299
|
server.tool(
|
|
8098
8300
|
"fork_worktree",
|
|
8099
|
-
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
8301
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
8100
8302
|
{
|
|
8101
8303
|
ref: z3.string().describe("Worktree thread id/ref to fork"),
|
|
8102
8304
|
through_index: z3.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
@@ -8141,7 +8343,7 @@ async function startMcpServer() {
|
|
|
8141
8343
|
);
|
|
8142
8344
|
server.tool(
|
|
8143
8345
|
"fork_chat",
|
|
8144
|
-
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
8346
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
8145
8347
|
{
|
|
8146
8348
|
ref: z3.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
8147
8349
|
through_index: z3.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|