@sideboard-ai/core 0.1.100 → 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.js +1 -1
- package/dist/{agents-CLP5BG4O.js → agents-UT5GCJH3.js} +8 -8
- package/dist/{agents-OXZDMPSE.js → agents-XZBAKBO2.js} +8 -8
- 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-MHJV4WS3.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-HYKZEP5A.js → chunk-ETMRBJKA.js} +11 -11
- package/dist/{chunk-OXC3MEFI.js → chunk-H6LP57EF.js} +6 -6
- package/dist/{chunk-GR4JKWR4.js → chunk-HHLZWO7U.js} +5 -5
- package/dist/{chunk-3WA37BRI.js → chunk-KGSVLZV6.js} +11 -4
- package/dist/{chunk-7KDBDMNC.js → chunk-KLBOWJ74.js} +1 -1
- package/dist/{chunk-EQPQTLW6.js → chunk-LANDJIAI.js} +6 -6
- package/dist/{chunk-MRBKGFTL.js → chunk-ND6AO7C5.js} +3 -3
- package/dist/{chunk-WQTTUC4N.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-IFHKPZHA.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-OOBSQCWQ.js → coordinator-prompt-3DFZRMXE.js} +5 -5
- package/dist/{coordinator-prompt-HHRKQWCX.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-YZWYHLQY.js → global-workspace-KJQS5S2C.js} +6 -6
- package/dist/{global-workspace-KYEBFWKB.js → global-workspace-UR4E3CCG.js} +6 -6
- package/dist/index.cjs +626 -153
- package/dist/index.d.cts +37 -5
- package/dist/index.d.ts +37 -5
- package/dist/index.js +545 -121
- package/dist/mcp/run-stdio.cjs +426 -177
- package/dist/mcp/run-stdio.js +330 -127
- 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-ENVUDK6C.js → workspaces-NTIVI6DI.js} +7 -7
- package/dist/{workspaces-NNPD7QVV.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 +1 -1
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -18,14 +18,14 @@ import {
|
|
|
18
18
|
sideboardMcpProfile,
|
|
19
19
|
summarizeTurnStderr,
|
|
20
20
|
turnFailChatText
|
|
21
|
-
} from "../chunk-
|
|
22
|
-
import "../chunk-
|
|
21
|
+
} from "../chunk-LANDJIAI.js";
|
|
22
|
+
import "../chunk-7YKXHBIW.js";
|
|
23
23
|
import {
|
|
24
24
|
addWorkspace,
|
|
25
25
|
ensureWorkspace,
|
|
26
26
|
removeWorkspace,
|
|
27
27
|
syncWorkspacesFromThreads
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-ND6AO7C5.js";
|
|
29
29
|
import {
|
|
30
30
|
extractPresentedPlan,
|
|
31
31
|
readPlanFile,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "../chunk-N5PM7HGQ.js";
|
|
34
34
|
import {
|
|
35
35
|
releaseCaffeinateHoldForThread
|
|
36
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-KLBOWJ74.js";
|
|
37
37
|
import {
|
|
38
38
|
GLOBAL_WORKSPACE_ID,
|
|
39
39
|
assertOrchestratorCapableAgent,
|
|
@@ -44,14 +44,14 @@ import {
|
|
|
44
44
|
isOrchestratorThread,
|
|
45
45
|
isSlackCoordinatorThread,
|
|
46
46
|
orchestratorSessionPoisonedByBuiltins
|
|
47
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-OW34MVDN.js";
|
|
48
48
|
import {
|
|
49
49
|
SLACK_REPLY_FORMATTING,
|
|
50
50
|
coordinatorSystemPrompt,
|
|
51
51
|
coordinatorTurnReminder,
|
|
52
52
|
enrichWorkspacesWithGithub,
|
|
53
53
|
ensureGlobalCoordinatorCwd
|
|
54
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-YJADJH5U.js";
|
|
55
55
|
import {
|
|
56
56
|
addPrStackLayer,
|
|
57
57
|
allocateTeamName,
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
createThreadWorktree,
|
|
63
63
|
currentBranch,
|
|
64
64
|
detectGhStack,
|
|
65
|
+
enqueueByKey,
|
|
65
66
|
fetchPrHead,
|
|
66
67
|
formatGhLandError,
|
|
67
68
|
formatGitAuthModeDirective,
|
|
@@ -94,8 +95,9 @@ import {
|
|
|
94
95
|
takenSlugsFromThread,
|
|
95
96
|
threadDisplayLabel,
|
|
96
97
|
warmGithubAgentAuth,
|
|
98
|
+
withRepoGitLock,
|
|
97
99
|
worktreeNameFromPath
|
|
98
|
-
} from "../chunk-
|
|
100
|
+
} from "../chunk-WA3DVVWG.js";
|
|
99
101
|
import {
|
|
100
102
|
ATTACHMENTS_DIR,
|
|
101
103
|
LEGACY_ATTACHMENTS_DIR,
|
|
@@ -112,7 +114,7 @@ import {
|
|
|
112
114
|
updateThread,
|
|
113
115
|
withThreadLock,
|
|
114
116
|
writeThread
|
|
115
|
-
} from "../chunk-
|
|
117
|
+
} from "../chunk-P3BQ5DOL.js";
|
|
116
118
|
import {
|
|
117
119
|
childEnvWithAppSettings,
|
|
118
120
|
disconnectLinearConnection,
|
|
@@ -130,7 +132,7 @@ import {
|
|
|
130
132
|
stripNestedElectronEnv,
|
|
131
133
|
updateAdvancedSettings,
|
|
132
134
|
writeSecureJson
|
|
133
|
-
} from "../chunk-
|
|
135
|
+
} from "../chunk-WSYCND2U.js";
|
|
134
136
|
import {
|
|
135
137
|
writePrivateFile
|
|
136
138
|
} from "../chunk-NSTQ6QKD.js";
|
|
@@ -143,9 +145,10 @@ import {
|
|
|
143
145
|
runCursorWorktreeSetup,
|
|
144
146
|
sideboardReposDir,
|
|
145
147
|
sideboardWorkspacesDir,
|
|
148
|
+
threadLivePath,
|
|
146
149
|
workspaceSettingsSourceLabel,
|
|
147
150
|
worktreesRoot
|
|
148
|
-
} from "../chunk-
|
|
151
|
+
} from "../chunk-BD2ICC4D.js";
|
|
149
152
|
import {
|
|
150
153
|
enrichPathWithNpmGlobalBin,
|
|
151
154
|
ensureAgentPath,
|
|
@@ -660,7 +663,7 @@ async function refreshSlackReplyBadges(opts) {
|
|
|
660
663
|
}
|
|
661
664
|
|
|
662
665
|
// src/orchestrator/orchestrator.ts
|
|
663
|
-
import { existsSync as
|
|
666
|
+
import { existsSync as existsSync15 } from "fs";
|
|
664
667
|
|
|
665
668
|
// src/agents/spawn.ts
|
|
666
669
|
import { createInterface } from "readline";
|
|
@@ -902,7 +905,7 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
902
905
|
const intervalMs = opts?.intervalMs ?? 32;
|
|
903
906
|
let pending = null;
|
|
904
907
|
let timer = null;
|
|
905
|
-
const
|
|
908
|
+
const flush2 = () => {
|
|
906
909
|
if (timer) {
|
|
907
910
|
clearTimeout(timer);
|
|
908
911
|
timer = null;
|
|
@@ -914,13 +917,13 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
914
917
|
};
|
|
915
918
|
const schedule = () => {
|
|
916
919
|
if (timer) return;
|
|
917
|
-
timer = setTimeout(
|
|
920
|
+
timer = setTimeout(flush2, intervalMs);
|
|
918
921
|
timer.unref?.();
|
|
919
922
|
};
|
|
920
923
|
return {
|
|
921
924
|
push(event) {
|
|
922
925
|
if (!isTextEvent(event)) {
|
|
923
|
-
|
|
926
|
+
flush2();
|
|
924
927
|
emit(event);
|
|
925
928
|
return;
|
|
926
929
|
}
|
|
@@ -928,12 +931,12 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
928
931
|
if (pending && pending.type === event.type) {
|
|
929
932
|
pending.data += event.data;
|
|
930
933
|
} else {
|
|
931
|
-
|
|
934
|
+
flush2();
|
|
932
935
|
pending = { type: event.type, data: event.data };
|
|
933
936
|
}
|
|
934
937
|
schedule();
|
|
935
938
|
},
|
|
936
|
-
flush
|
|
939
|
+
flush: flush2
|
|
937
940
|
};
|
|
938
941
|
}
|
|
939
942
|
|
|
@@ -945,9 +948,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
945
948
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
946
949
|
);
|
|
947
950
|
}
|
|
948
|
-
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-
|
|
951
|
+
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-KJQS5S2C.js");
|
|
949
952
|
if (isGlobalThread2(thread)) {
|
|
950
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-
|
|
953
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-3DFZRMXE.js");
|
|
951
954
|
ensureGlobalCoordinatorCwd2(
|
|
952
955
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
953
956
|
);
|
|
@@ -1888,7 +1891,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
1888
1891
|
return readThread(thread.id) ?? thread;
|
|
1889
1892
|
}
|
|
1890
1893
|
async function listLinearIssues(agent, repoPath) {
|
|
1891
|
-
const { getAdapter: getAdapter2 } = await import("../agents-
|
|
1894
|
+
const { getAdapter: getAdapter2 } = await import("../agents-UT5GCJH3.js");
|
|
1892
1895
|
await requireAgent(agent, { requireLinear: true });
|
|
1893
1896
|
const adapter = getAdapter2(agent);
|
|
1894
1897
|
if (!adapter.listLinearIssues) {
|
|
@@ -2213,8 +2216,11 @@ function worktreeBindingFrom(from) {
|
|
|
2213
2216
|
};
|
|
2214
2217
|
}
|
|
2215
2218
|
function threadsSharingWorktree(worktreePath) {
|
|
2219
|
+
if (!worktreePath) return [];
|
|
2216
2220
|
const key = normalizeWorktreePath(worktreePath);
|
|
2217
|
-
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));
|
|
2218
2224
|
}
|
|
2219
2225
|
function formatTranscriptMarkdown(title, messages) {
|
|
2220
2226
|
const body = formatMessagesAsTranscript(messages, { tools: "full" });
|
|
@@ -2285,9 +2291,132 @@ function forkChatTab(input) {
|
|
|
2285
2291
|
return tab;
|
|
2286
2292
|
}
|
|
2287
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
|
+
|
|
2288
2417
|
// src/review/request-review.ts
|
|
2289
2418
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2290
|
-
import { existsSync as
|
|
2419
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2291
2420
|
import { dirname as dirname2, join as join8 } from "path";
|
|
2292
2421
|
|
|
2293
2422
|
// src/review/review-request-template.ts
|
|
@@ -2433,9 +2562,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
2433
2562
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
2434
2563
|
}
|
|
2435
2564
|
function readTextIfPresent(abs) {
|
|
2436
|
-
if (!
|
|
2565
|
+
if (!existsSync8(abs)) return null;
|
|
2437
2566
|
try {
|
|
2438
|
-
const content =
|
|
2567
|
+
const content = readFileSync6(abs, "utf8");
|
|
2439
2568
|
return content.trim() ? content : null;
|
|
2440
2569
|
} catch {
|
|
2441
2570
|
return null;
|
|
@@ -2443,9 +2572,9 @@ function readTextIfPresent(abs) {
|
|
|
2443
2572
|
}
|
|
2444
2573
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
2445
2574
|
const gitignoreAbs = join8(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
2446
|
-
if (
|
|
2575
|
+
if (existsSync8(gitignoreAbs)) return;
|
|
2447
2576
|
mkdirSync2(dirname2(gitignoreAbs), { recursive: true });
|
|
2448
|
-
|
|
2577
|
+
writeFileSync3(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
2449
2578
|
}
|
|
2450
2579
|
function resolveReviewGuidelines(worktreePath) {
|
|
2451
2580
|
const repoAbs = join8(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -2480,7 +2609,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
2480
2609
|
}
|
|
2481
2610
|
ensureAttachmentsGitignore(worktreePath);
|
|
2482
2611
|
mkdirSync2(dirname2(localAbs), { recursive: true });
|
|
2483
|
-
|
|
2612
|
+
writeFileSync3(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
2484
2613
|
return {
|
|
2485
2614
|
path: REVIEW_REQUEST_PATH,
|
|
2486
2615
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -2676,10 +2805,10 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
2676
2805
|
import { execFileSync } from "child_process";
|
|
2677
2806
|
import {
|
|
2678
2807
|
copyFileSync as copyFileSync2,
|
|
2679
|
-
existsSync as
|
|
2808
|
+
existsSync as existsSync9,
|
|
2680
2809
|
mkdtempSync,
|
|
2681
2810
|
readdirSync as readdirSync3,
|
|
2682
|
-
readFileSync as
|
|
2811
|
+
readFileSync as readFileSync7,
|
|
2683
2812
|
rmSync
|
|
2684
2813
|
} from "fs";
|
|
2685
2814
|
import { tmpdir } from "os";
|
|
@@ -2709,7 +2838,7 @@ function mapAgentType(raw) {
|
|
|
2709
2838
|
return null;
|
|
2710
2839
|
}
|
|
2711
2840
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
2712
|
-
if (!workspacePath || !
|
|
2841
|
+
if (!workspacePath || !existsSync9(CURSOR_SDK_STORE)) return null;
|
|
2713
2842
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
2714
2843
|
let best = null;
|
|
2715
2844
|
let hashes;
|
|
@@ -2720,10 +2849,10 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2720
2849
|
}
|
|
2721
2850
|
for (const hash of hashes) {
|
|
2722
2851
|
const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
2723
|
-
if (!
|
|
2852
|
+
if (!existsSync9(agentsFile)) continue;
|
|
2724
2853
|
let text3;
|
|
2725
2854
|
try {
|
|
2726
|
-
text3 =
|
|
2855
|
+
text3 = readFileSync7(agentsFile, "utf8");
|
|
2727
2856
|
} catch {
|
|
2728
2857
|
continue;
|
|
2729
2858
|
}
|
|
@@ -2747,7 +2876,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
2747
2876
|
return best?.agentId ?? null;
|
|
2748
2877
|
}
|
|
2749
2878
|
async function adoptThread(input) {
|
|
2750
|
-
if (!
|
|
2879
|
+
if (!existsSync9(input.worktreePath)) {
|
|
2751
2880
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
2752
2881
|
}
|
|
2753
2882
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -2766,12 +2895,12 @@ async function adoptThread(input) {
|
|
|
2766
2895
|
messages: input.messages ?? []
|
|
2767
2896
|
});
|
|
2768
2897
|
writeThread(thread);
|
|
2769
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
2898
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-NTIVI6DI.js");
|
|
2770
2899
|
await ensureWorkspace2(repoPath);
|
|
2771
2900
|
return thread;
|
|
2772
2901
|
}
|
|
2773
2902
|
function listConductorWorkspaces() {
|
|
2774
|
-
if (!
|
|
2903
|
+
if (!existsSync9(CONDUCTOR_DB)) {
|
|
2775
2904
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2776
2905
|
}
|
|
2777
2906
|
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
@@ -2780,7 +2909,7 @@ function listConductorWorkspaces() {
|
|
|
2780
2909
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2781
2910
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2782
2911
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2783
|
-
if (
|
|
2912
|
+
if (existsSync9(src)) {
|
|
2784
2913
|
try {
|
|
2785
2914
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
2786
2915
|
} catch {
|
|
@@ -2862,7 +2991,7 @@ function listConductorWorkspaces() {
|
|
|
2862
2991
|
}
|
|
2863
2992
|
}
|
|
2864
2993
|
function importConductorWorkspace(workspaceId) {
|
|
2865
|
-
if (!
|
|
2994
|
+
if (!existsSync9(CONDUCTOR_DB)) {
|
|
2866
2995
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
2867
2996
|
}
|
|
2868
2997
|
const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
|
|
@@ -2871,7 +3000,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2871
3000
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
2872
3001
|
for (const suffix of ["-wal", "-shm"]) {
|
|
2873
3002
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
2874
|
-
if (
|
|
3003
|
+
if (existsSync9(src)) {
|
|
2875
3004
|
try {
|
|
2876
3005
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
2877
3006
|
} catch {
|
|
@@ -2891,7 +3020,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
2891
3020
|
).get(workspaceId);
|
|
2892
3021
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
2893
3022
|
const worktreePath = String(row.workspacePath);
|
|
2894
|
-
if (!
|
|
3023
|
+
if (!existsSync9(worktreePath)) {
|
|
2895
3024
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
2896
3025
|
}
|
|
2897
3026
|
let sessionId = null;
|
|
@@ -2962,7 +3091,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
2962
3091
|
}
|
|
2963
3092
|
|
|
2964
3093
|
// src/threads/stack-layers.ts
|
|
2965
|
-
import { existsSync as
|
|
3094
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2966
3095
|
function stackIdFrom(stack) {
|
|
2967
3096
|
if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
|
|
2968
3097
|
const key = stack.layers.map((l) => l.branchName).join("|");
|
|
@@ -3025,7 +3154,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
3025
3154
|
let createdWorktree = false;
|
|
3026
3155
|
const trees = await listWorktrees(repoPath);
|
|
3027
3156
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
3028
|
-
if (checkedOut?.path &&
|
|
3157
|
+
if (checkedOut?.path && existsSync10(checkedOut.path)) {
|
|
3029
3158
|
if (input.reuseExistingWorktree !== false) {
|
|
3030
3159
|
worktreePath = checkedOut.path;
|
|
3031
3160
|
} else {
|
|
@@ -3167,7 +3296,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
3167
3296
|
async function createPrStack(input, onSetupLine) {
|
|
3168
3297
|
await requireAgent(input.agent);
|
|
3169
3298
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
3170
|
-
if (!
|
|
3299
|
+
if (!existsSync10(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
3171
3300
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
3172
3301
|
const status = await detectGhStack(repoPath);
|
|
3173
3302
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -3234,7 +3363,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
3234
3363
|
}
|
|
3235
3364
|
}
|
|
3236
3365
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
3237
|
-
if (!claimed.has(bootstrap.worktreePath) &&
|
|
3366
|
+
if (!claimed.has(bootstrap.worktreePath) && existsSync10(bootstrap.worktreePath)) {
|
|
3238
3367
|
try {
|
|
3239
3368
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
3240
3369
|
deleteBranch: bootstrap.branchName
|
|
@@ -3246,10 +3375,10 @@ async function createPrStack(input, onSetupLine) {
|
|
|
3246
3375
|
}
|
|
3247
3376
|
|
|
3248
3377
|
// src/diff/diff.ts
|
|
3249
|
-
import { existsSync as
|
|
3378
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
3250
3379
|
import { dirname as dirname3, join as join10 } from "path";
|
|
3251
3380
|
async function inspectGitWorktree(worktreePath) {
|
|
3252
|
-
if (!worktreePath || !
|
|
3381
|
+
if (!worktreePath || !existsSync11(worktreePath)) return "missing_worktree";
|
|
3253
3382
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
3254
3383
|
reject: false
|
|
3255
3384
|
});
|
|
@@ -3257,7 +3386,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
3257
3386
|
return "ok";
|
|
3258
3387
|
}
|
|
3259
3388
|
async function initializeGitRepository(worktreePath) {
|
|
3260
|
-
if (!worktreePath || !
|
|
3389
|
+
if (!worktreePath || !existsSync11(worktreePath)) {
|
|
3261
3390
|
throw new Error("Worktree not found");
|
|
3262
3391
|
}
|
|
3263
3392
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -3397,7 +3526,7 @@ async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
|
3397
3526
|
try {
|
|
3398
3527
|
const st = statSync2(abs);
|
|
3399
3528
|
if (st.isFile() && st.size > maxHunk) {
|
|
3400
|
-
const buf =
|
|
3529
|
+
const buf = readFileSync8(abs).subarray(0, maxHunk);
|
|
3401
3530
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
3402
3531
|
}
|
|
3403
3532
|
} catch {
|
|
@@ -3908,7 +4037,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
3908
4037
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
3909
4038
|
);
|
|
3910
4039
|
}
|
|
3911
|
-
const buf =
|
|
4040
|
+
const buf = readFileSync8(abs);
|
|
3912
4041
|
return {
|
|
3913
4042
|
path: relativePath,
|
|
3914
4043
|
contentBase64: buf.toString("base64"),
|
|
@@ -3923,7 +4052,7 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
3923
4052
|
if (!st.isFile()) {
|
|
3924
4053
|
throw new Error(`Not a file: ${relativePath}`);
|
|
3925
4054
|
}
|
|
3926
|
-
const buf =
|
|
4055
|
+
const buf = readFileSync8(abs);
|
|
3927
4056
|
if (isImageRelativePath(relativePath)) {
|
|
3928
4057
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
3929
4058
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -3968,7 +4097,7 @@ function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
|
3968
4097
|
assertSafeRelativePath(relativePath);
|
|
3969
4098
|
const abs = join10(worktreePath, relativePath);
|
|
3970
4099
|
mkdirSync3(dirname3(abs), { recursive: true });
|
|
3971
|
-
|
|
4100
|
+
writeFileSync4(abs, content, "utf8");
|
|
3972
4101
|
return { path: relativePath };
|
|
3973
4102
|
}
|
|
3974
4103
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -4071,7 +4200,7 @@ async function confirmLand(thread, opts) {
|
|
|
4071
4200
|
}
|
|
4072
4201
|
|
|
4073
4202
|
// src/skills/discover.ts
|
|
4074
|
-
import { existsSync as
|
|
4203
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
|
|
4075
4204
|
import { homedir } from "os";
|
|
4076
4205
|
import { join as join11 } from "path";
|
|
4077
4206
|
function toCommand(name) {
|
|
@@ -4105,7 +4234,7 @@ function parseFrontmatter(content) {
|
|
|
4105
4234
|
}
|
|
4106
4235
|
function readSkill(skillMd, source) {
|
|
4107
4236
|
try {
|
|
4108
|
-
const content =
|
|
4237
|
+
const content = readFileSync9(skillMd, "utf8");
|
|
4109
4238
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
4110
4239
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
4111
4240
|
const name = fmName || dirName;
|
|
@@ -4124,7 +4253,7 @@ function readSkill(skillMd, source) {
|
|
|
4124
4253
|
}
|
|
4125
4254
|
}
|
|
4126
4255
|
function scanSkillsDir(dir, source, out) {
|
|
4127
|
-
if (!
|
|
4256
|
+
if (!existsSync12(dir)) return;
|
|
4128
4257
|
let entries;
|
|
4129
4258
|
try {
|
|
4130
4259
|
entries = readdirSync4(dir);
|
|
@@ -4134,7 +4263,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4134
4263
|
for (const entry of entries) {
|
|
4135
4264
|
if (entry.startsWith(".")) continue;
|
|
4136
4265
|
const skillMd = join11(dir, entry, "SKILL.md");
|
|
4137
|
-
if (!
|
|
4266
|
+
if (!existsSync12(skillMd)) continue;
|
|
4138
4267
|
try {
|
|
4139
4268
|
if (!statSync3(skillMd).isFile()) continue;
|
|
4140
4269
|
} catch {
|
|
@@ -4145,7 +4274,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
4145
4274
|
}
|
|
4146
4275
|
}
|
|
4147
4276
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
4148
|
-
if (!
|
|
4277
|
+
if (!existsSync12(pluginsRoot)) return;
|
|
4149
4278
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
4150
4279
|
if (depth > 7) return;
|
|
4151
4280
|
let entries;
|
|
@@ -4202,7 +4331,7 @@ function discoverSkills(worktreePath) {
|
|
|
4202
4331
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
4203
4332
|
}
|
|
4204
4333
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
4205
|
-
const raw =
|
|
4334
|
+
const raw = readFileSync9(skillPath, "utf8");
|
|
4206
4335
|
if (raw.startsWith("---")) {
|
|
4207
4336
|
const end = raw.indexOf("\n---", 3);
|
|
4208
4337
|
if (end >= 0) {
|
|
@@ -4295,7 +4424,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
4295
4424
|
}
|
|
4296
4425
|
|
|
4297
4426
|
// src/composer/stage-files.ts
|
|
4298
|
-
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";
|
|
4299
4428
|
import { basename as basename3, extname, join as join12 } from "path";
|
|
4300
4429
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4301
4430
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
@@ -4334,19 +4463,19 @@ function ensureAttachmentsDir(worktreePath) {
|
|
|
4334
4463
|
const dir = join12(worktreePath, ATTACHMENTS_DIR);
|
|
4335
4464
|
mkdirSync4(dir, { recursive: true });
|
|
4336
4465
|
const gi = join12(dir, ".gitignore");
|
|
4337
|
-
if (!
|
|
4338
|
-
|
|
4466
|
+
if (!existsSync13(gi)) {
|
|
4467
|
+
writeFileSync5(gi, attachmentsGitignoreBody(), "utf8");
|
|
4339
4468
|
}
|
|
4340
4469
|
return dir;
|
|
4341
4470
|
}
|
|
4342
4471
|
function uniqueAttachmentName(dir, originalName) {
|
|
4343
4472
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
4344
|
-
if (!
|
|
4473
|
+
if (!existsSync13(join12(dir, safe))) return safe;
|
|
4345
4474
|
const ext = extname(safe);
|
|
4346
4475
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
4347
4476
|
for (let i = 1; i < 1e4; i++) {
|
|
4348
4477
|
const candidate = `${stem}-${i}${ext}`;
|
|
4349
|
-
if (!
|
|
4478
|
+
if (!existsSync13(join12(dir, candidate))) return candidate;
|
|
4350
4479
|
}
|
|
4351
4480
|
return `${stem}-${randomUUID4()}${ext}`;
|
|
4352
4481
|
}
|
|
@@ -4410,7 +4539,7 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
4410
4539
|
const destAbs = join12(dir, name);
|
|
4411
4540
|
copyFileSync3(abs, destAbs);
|
|
4412
4541
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4413
|
-
const buf =
|
|
4542
|
+
const buf = readFileSync10(destAbs);
|
|
4414
4543
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4415
4544
|
} catch (err) {
|
|
4416
4545
|
out.push({
|
|
@@ -4423,17 +4552,17 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
4423
4552
|
}
|
|
4424
4553
|
return out;
|
|
4425
4554
|
}
|
|
4426
|
-
function stageBuffersAsAttachments(worktreePath,
|
|
4427
|
-
if (
|
|
4555
|
+
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
4556
|
+
if (buffers2.length === 0) return [];
|
|
4428
4557
|
const dir = ensureAttachmentsDir(worktreePath);
|
|
4429
4558
|
const out = [];
|
|
4430
|
-
for (const item of
|
|
4559
|
+
for (const item of buffers2) {
|
|
4431
4560
|
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
4432
4561
|
try {
|
|
4433
4562
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
4434
4563
|
const name = uniqueAttachmentName(dir, originalName);
|
|
4435
4564
|
const destAbs = join12(dir, name);
|
|
4436
|
-
|
|
4565
|
+
writeFileSync5(destAbs, buf);
|
|
4437
4566
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4438
4567
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
4439
4568
|
} catch (err) {
|
|
@@ -4464,7 +4593,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4464
4593
|
const abs = join12(worktreePath, rel);
|
|
4465
4594
|
const st = statSync4(abs);
|
|
4466
4595
|
if (!st.isFile()) continue;
|
|
4467
|
-
const buf =
|
|
4596
|
+
const buf = readFileSync10(abs);
|
|
4468
4597
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4469
4598
|
} catch (err) {
|
|
4470
4599
|
out.push({
|
|
@@ -4479,7 +4608,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
4479
4608
|
}
|
|
4480
4609
|
|
|
4481
4610
|
// src/agents/instructions.ts
|
|
4482
|
-
import { existsSync as
|
|
4611
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
4483
4612
|
import { join as join13 } from "path";
|
|
4484
4613
|
function normPath(p) {
|
|
4485
4614
|
return p.replace(/\/+$/, "");
|
|
@@ -4669,6 +4798,15 @@ function isPidAlive(pid) {
|
|
|
4669
4798
|
return false;
|
|
4670
4799
|
}
|
|
4671
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;
|
|
4672
4810
|
function writeLiveStatus(threadId, status, lastError) {
|
|
4673
4811
|
const latest = readThread(threadId);
|
|
4674
4812
|
if (!latest || latest.status === "archived") return latest;
|
|
@@ -4746,7 +4884,7 @@ var Orchestrator = class {
|
|
|
4746
4884
|
}
|
|
4747
4885
|
continue;
|
|
4748
4886
|
}
|
|
4749
|
-
if (!
|
|
4887
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
4750
4888
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
4751
4889
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
4752
4890
|
continue;
|
|
@@ -4765,7 +4903,7 @@ var Orchestrator = class {
|
|
|
4765
4903
|
orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
|
|
4766
4904
|
});
|
|
4767
4905
|
}
|
|
4768
|
-
const { autoCleanupOrphansEnabled } = await import("../app-settings-
|
|
4906
|
+
const { autoCleanupOrphansEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
4769
4907
|
if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
|
|
4770
4908
|
await cleanupOrphanWorktrees({ repoPaths });
|
|
4771
4909
|
}
|
|
@@ -4933,7 +5071,7 @@ var Orchestrator = class {
|
|
|
4933
5071
|
}
|
|
4934
5072
|
}
|
|
4935
5073
|
await setup;
|
|
4936
|
-
const { autoRunAfterSetupEnabled } = await import("../app-settings-
|
|
5074
|
+
const { autoRunAfterSetupEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
4937
5075
|
if (autoRunAfterSetupEnabled()) {
|
|
4938
5076
|
try {
|
|
4939
5077
|
await this.startDev(threadId);
|
|
@@ -5067,14 +5205,10 @@ var Orchestrator = class {
|
|
|
5067
5205
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
5068
5206
|
const livePid = current.agentPid;
|
|
5069
5207
|
const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
|
|
5070
|
-
if (inFlight) {
|
|
5208
|
+
if (inFlight || foreignLive) {
|
|
5071
5209
|
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5072
|
-
} else {
|
|
5073
|
-
if (foreignLive) {
|
|
5074
|
-
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
5075
|
-
}
|
|
5076
|
-
void this.drainQueue(thread.id);
|
|
5077
5210
|
}
|
|
5211
|
+
void this.drainQueue(thread.id);
|
|
5078
5212
|
return this.requireThread(thread.id);
|
|
5079
5213
|
}
|
|
5080
5214
|
async drainQueue(threadId) {
|
|
@@ -5103,7 +5237,18 @@ var Orchestrator = class {
|
|
|
5103
5237
|
}
|
|
5104
5238
|
const livePid = thread.agentPid;
|
|
5105
5239
|
if (typeof livePid === "number" && livePid > 0 && isPidAlive(livePid)) {
|
|
5106
|
-
|
|
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
|
+
}
|
|
5107
5252
|
continue;
|
|
5108
5253
|
}
|
|
5109
5254
|
const prompt = thread.queue[0];
|
|
@@ -5211,7 +5356,7 @@ var Orchestrator = class {
|
|
|
5211
5356
|
}
|
|
5212
5357
|
const isBrightsy = fresh.agent === "brightsy";
|
|
5213
5358
|
const isOrchestration = isOrchestratorThread(fresh);
|
|
5214
|
-
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("../app-settings-
|
|
5359
|
+
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("../app-settings-VDJK2BZ2.js");
|
|
5215
5360
|
const gitAuthMode = getGithubGitAuthMode();
|
|
5216
5361
|
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
|
|
5217
5362
|
githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
|
|
@@ -5260,6 +5405,7 @@ var Orchestrator = class {
|
|
|
5260
5405
|
{ cachedPrefix, prompt: agentPrompt },
|
|
5261
5406
|
(event) => {
|
|
5262
5407
|
this.emit({ type: "turn_output", threadId, event });
|
|
5408
|
+
noteTurnLiveEvent(threadId, event);
|
|
5263
5409
|
if (event.type === "session_id") {
|
|
5264
5410
|
updateThread(threadId, { sessionId: event.data });
|
|
5265
5411
|
}
|
|
@@ -5306,7 +5452,7 @@ var Orchestrator = class {
|
|
|
5306
5452
|
if (this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
|
|
5307
5453
|
const sessionId = result.sessionId || this.requireThread(threadId).sessionId || "";
|
|
5308
5454
|
if (sessionId) {
|
|
5309
|
-
const { recoverFinishedCursorRun } = await import("../cursor-recover-
|
|
5455
|
+
const { recoverFinishedCursorRun } = await import("../cursor-recover-JBTIDARH.js");
|
|
5310
5456
|
for (let i = 0; i < 8; i++) {
|
|
5311
5457
|
const recovered = recoverFinishedCursorRun({
|
|
5312
5458
|
agentId: sessionId,
|
|
@@ -5464,6 +5610,7 @@ var Orchestrator = class {
|
|
|
5464
5610
|
this.processes.delete(`${threadId}:agent`);
|
|
5465
5611
|
this.stoppedTurns.delete(threadId);
|
|
5466
5612
|
this.runningCount = Math.max(0, this.runningCount - 1);
|
|
5613
|
+
clearTurnLive(threadId);
|
|
5467
5614
|
try {
|
|
5468
5615
|
updateThread(threadId, { agentPid: null });
|
|
5469
5616
|
} catch {
|
|
@@ -5737,51 +5884,56 @@ var Orchestrator = class {
|
|
|
5737
5884
|
}
|
|
5738
5885
|
return created;
|
|
5739
5886
|
}
|
|
5740
|
-
async waitForTurn(threadRef, timeoutMs = 6e5) {
|
|
5887
|
+
async waitForTurn(threadRef, timeoutMs = 6e5, opts) {
|
|
5741
5888
|
const thread = this.requireThread(threadRef);
|
|
5889
|
+
if (!["running", "queued"].includes(thread.status)) {
|
|
5890
|
+
return thread;
|
|
5891
|
+
}
|
|
5742
5892
|
const start = Date.now();
|
|
5743
5893
|
return new Promise((resolve, reject) => {
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
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
|
+
};
|
|
5748
5903
|
const off = this.on((event) => {
|
|
5749
5904
|
if (event.type === "turn_finished" && event.threadId === thread.id) {
|
|
5750
|
-
off();
|
|
5751
5905
|
const latest = readThread(thread.id);
|
|
5752
5906
|
if (!latest) {
|
|
5753
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5907
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5754
5908
|
return;
|
|
5755
5909
|
}
|
|
5756
|
-
resolve(latest);
|
|
5910
|
+
finish(() => resolve(latest));
|
|
5757
5911
|
}
|
|
5758
5912
|
if (event.type === "error" && event.threadId === thread.id) {
|
|
5759
|
-
off();
|
|
5760
5913
|
const latest = readThread(thread.id);
|
|
5761
5914
|
if (!latest) {
|
|
5762
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5915
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5763
5916
|
return;
|
|
5764
5917
|
}
|
|
5765
|
-
resolve(latest);
|
|
5918
|
+
finish(() => resolve(latest));
|
|
5766
5919
|
}
|
|
5767
5920
|
});
|
|
5768
|
-
|
|
5769
|
-
if (Date.now() - start > timeoutMs) {
|
|
5770
|
-
clearInterval(timer);
|
|
5771
|
-
off();
|
|
5772
|
-
reject(new Error("wait_for_turn timed out"));
|
|
5773
|
-
}
|
|
5921
|
+
timer = setInterval(() => {
|
|
5774
5922
|
const current = readThread(thread.id);
|
|
5775
5923
|
if (!current) {
|
|
5776
|
-
|
|
5777
|
-
off();
|
|
5778
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
5924
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
5779
5925
|
return;
|
|
5780
5926
|
}
|
|
5781
5927
|
if (!["running", "queued"].includes(current.status)) {
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
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
|
+
}
|
|
5785
5937
|
}
|
|
5786
5938
|
}, 200);
|
|
5787
5939
|
});
|
|
@@ -5791,11 +5943,16 @@ var Orchestrator = class {
|
|
|
5791
5943
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
5792
5944
|
const lastError = thread.lastError ?? null;
|
|
5793
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;
|
|
5794
5948
|
return {
|
|
5795
5949
|
text: text3,
|
|
5796
5950
|
status: thread.status,
|
|
5797
5951
|
sessionId: thread.sessionId,
|
|
5798
|
-
lastError
|
|
5952
|
+
lastError,
|
|
5953
|
+
stillRunning,
|
|
5954
|
+
progress: live?.summary ?? null,
|
|
5955
|
+
lastActivityAt: live?.updatedAt ?? null
|
|
5799
5956
|
};
|
|
5800
5957
|
}
|
|
5801
5958
|
assertNotGlobal(thread, action) {
|
|
@@ -5958,7 +6115,7 @@ var Orchestrator = class {
|
|
|
5958
6115
|
updateThread(thread.id, { title: meta.title });
|
|
5959
6116
|
}
|
|
5960
6117
|
}
|
|
5961
|
-
const { autoArchiveOnMergeEnabled } = await import("../app-settings-
|
|
6118
|
+
const { autoArchiveOnMergeEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
5962
6119
|
const latest = this.requireThread(thread.id);
|
|
5963
6120
|
if (!shouldAutoArchiveOnPrMerge({
|
|
5964
6121
|
previousPrState: prevState || null,
|
|
@@ -6221,7 +6378,19 @@ var Orchestrator = class {
|
|
|
6221
6378
|
const thread = this.requireThread(threadRef);
|
|
6222
6379
|
return threadsSharingWorktree(thread.worktreePath);
|
|
6223
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
|
+
}
|
|
6224
6389
|
async archive(threadRef) {
|
|
6390
|
+
const thread = this.requireThread(threadRef);
|
|
6391
|
+
return this.enqueueWorktreeTeardown(thread, () => this.archiveUnlocked(threadRef));
|
|
6392
|
+
}
|
|
6393
|
+
async archiveUnlocked(threadRef) {
|
|
6225
6394
|
const thread = this.requireThread(threadRef);
|
|
6226
6395
|
this.stop(thread.id);
|
|
6227
6396
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -6249,7 +6418,7 @@ var Orchestrator = class {
|
|
|
6249
6418
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
6250
6419
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
6251
6420
|
try {
|
|
6252
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
6421
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-NTIVI6DI.js");
|
|
6253
6422
|
await ensureWorkspace2(thread.repoPath);
|
|
6254
6423
|
} catch {
|
|
6255
6424
|
}
|
|
@@ -6257,6 +6426,10 @@ var Orchestrator = class {
|
|
|
6257
6426
|
return archived;
|
|
6258
6427
|
}
|
|
6259
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) {
|
|
6260
6433
|
const thread = this.requireThread(threadRef);
|
|
6261
6434
|
this.stop(thread.id);
|
|
6262
6435
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -6271,7 +6444,7 @@ var Orchestrator = class {
|
|
|
6271
6444
|
await runArchiveScript(thread.repoPath, thread.worktreePath);
|
|
6272
6445
|
} catch {
|
|
6273
6446
|
}
|
|
6274
|
-
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-
|
|
6447
|
+
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-VDJK2BZ2.js");
|
|
6275
6448
|
const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
|
|
6276
6449
|
await removeWorktree(thread.repoPath, thread.worktreePath, {
|
|
6277
6450
|
deleteBranch: deleteBranch ? thread.branchName : void 0
|
|
@@ -6285,19 +6458,18 @@ var Orchestrator = class {
|
|
|
6285
6458
|
throw new Error("Thread is not archived");
|
|
6286
6459
|
}
|
|
6287
6460
|
if (isGlobalThread(thread)) {
|
|
6288
|
-
const { globalAgentCwd } = await import("../paths-
|
|
6461
|
+
const { globalAgentCwd } = await import("../paths-QCI3MTO5.js");
|
|
6289
6462
|
updateThread(thread.id, { worktreePath: globalAgentCwd() });
|
|
6290
6463
|
const restored2 = setStatus(thread.id, "idle");
|
|
6291
6464
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
6292
6465
|
return restored2;
|
|
6293
6466
|
}
|
|
6294
|
-
if (!
|
|
6295
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-
|
|
6296
|
-
const { execa: execa5 } = await import("execa");
|
|
6467
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
6468
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-NKWN5ZTS.js");
|
|
6297
6469
|
const slug = thread.worktreePath.split("/").pop();
|
|
6298
6470
|
const dest = thread.worktreePath;
|
|
6299
|
-
await
|
|
6300
|
-
|
|
6471
|
+
await withRepoGitLock(thread.repoPath, async () => {
|
|
6472
|
+
await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
|
|
6301
6473
|
});
|
|
6302
6474
|
void createThreadWorktree2;
|
|
6303
6475
|
void slug;
|
|
@@ -6844,6 +7016,15 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
6844
7016
|
return null;
|
|
6845
7017
|
}
|
|
6846
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
|
+
|
|
6847
7028
|
// src/mcp/slack-tools.ts
|
|
6848
7029
|
import { z } from "zod";
|
|
6849
7030
|
|
|
@@ -7471,7 +7652,7 @@ async function startMcpServer() {
|
|
|
7471
7652
|
);
|
|
7472
7653
|
}
|
|
7473
7654
|
try {
|
|
7474
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
7655
|
+
const { maxConcurrentAgents } = await import("../app-settings-VDJK2BZ2.js");
|
|
7475
7656
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
7476
7657
|
} catch {
|
|
7477
7658
|
}
|
|
@@ -7514,7 +7695,9 @@ async function startMcpServer() {
|
|
|
7514
7695
|
const threads = orch.getThreads(true);
|
|
7515
7696
|
const lines = threads.map((t) => {
|
|
7516
7697
|
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : basename4(t.repoPath) || t.repoPath;
|
|
7517
|
-
|
|
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}`;
|
|
7518
7701
|
});
|
|
7519
7702
|
return {
|
|
7520
7703
|
content: [{ type: "text", text: lines.join("\n") || "(no threads)" }]
|
|
@@ -7523,13 +7706,14 @@ async function startMcpServer() {
|
|
|
7523
7706
|
);
|
|
7524
7707
|
server.tool(
|
|
7525
7708
|
"get_thread",
|
|
7526
|
-
"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.",
|
|
7527
7710
|
{ ref: z3.string() },
|
|
7528
7711
|
async ({ ref }) => {
|
|
7529
7712
|
const t = orch.getThread(ref);
|
|
7530
7713
|
if (!t) {
|
|
7531
7714
|
return { content: [{ type: "text", text: `Thread not found: ${ref}` }], isError: true };
|
|
7532
7715
|
}
|
|
7716
|
+
const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
|
|
7533
7717
|
const summary = {
|
|
7534
7718
|
id: t.id,
|
|
7535
7719
|
title: t.title,
|
|
@@ -7544,7 +7728,10 @@ async function startMcpServer() {
|
|
|
7544
7728
|
messageCount: t.messages.length,
|
|
7545
7729
|
devPort: t.devPort,
|
|
7546
7730
|
prUrl: t.prUrl,
|
|
7547
|
-
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
|
|
7548
7735
|
};
|
|
7549
7736
|
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
7550
7737
|
}
|
|
@@ -7680,8 +7867,8 @@ async function startMcpServer() {
|
|
|
7680
7867
|
registerSlackTools(server);
|
|
7681
7868
|
registerLinearTools(server);
|
|
7682
7869
|
if (sideboardMcpProfile() !== "worktree") {
|
|
7683
|
-
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-
|
|
7684
|
-
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");
|
|
7685
7872
|
const accountDefaults = resolveThreadDefaults();
|
|
7686
7873
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
7687
7874
|
server.tool(
|
|
@@ -7870,13 +8057,15 @@ async function startMcpServer() {
|
|
|
7870
8057
|
);
|
|
7871
8058
|
server.tool(
|
|
7872
8059
|
"wait_for_turn",
|
|
7873
|
-
"
|
|
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.",
|
|
7874
8061
|
{
|
|
7875
8062
|
ref: z3.string(),
|
|
7876
8063
|
timeoutMs: z3.number().optional()
|
|
7877
8064
|
},
|
|
7878
8065
|
async ({ ref, timeoutMs }) => {
|
|
7879
|
-
const thread = await orch.waitForTurn(ref, timeoutMs
|
|
8066
|
+
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
8067
|
+
resolveIfStillRunning: true
|
|
8068
|
+
});
|
|
7880
8069
|
const result = orch.getTurnResult(thread.id);
|
|
7881
8070
|
return {
|
|
7882
8071
|
content: [
|
|
@@ -7886,7 +8075,11 @@ async function startMcpServer() {
|
|
|
7886
8075
|
id: thread.id,
|
|
7887
8076
|
status: result.status,
|
|
7888
8077
|
text: result.text,
|
|
7889
|
-
lastError: result.lastError
|
|
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
|
|
7890
8083
|
})
|
|
7891
8084
|
}
|
|
7892
8085
|
]
|
|
@@ -7895,11 +8088,21 @@ async function startMcpServer() {
|
|
|
7895
8088
|
);
|
|
7896
8089
|
server.tool(
|
|
7897
8090
|
"get_turn_result",
|
|
7898
|
-
"
|
|
8091
|
+
"Assistant message when the turn finished, or live progress while stillRunning. Not the full transcript.",
|
|
7899
8092
|
{ ref: z3.string() },
|
|
7900
8093
|
async ({ ref }) => {
|
|
7901
8094
|
const result = orch.getTurnResult(ref);
|
|
7902
|
-
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
|
+
};
|
|
7903
8106
|
}
|
|
7904
8107
|
);
|
|
7905
8108
|
server.tool(
|
|
@@ -8016,7 +8219,7 @@ async function startMcpServer() {
|
|
|
8016
8219
|
);
|
|
8017
8220
|
server.tool(
|
|
8018
8221
|
"request_review",
|
|
8019
|
-
'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.',
|
|
8020
8223
|
{ ref: z3.string().describe("Worktree thread id/ref to review") },
|
|
8021
8224
|
async ({ ref }) => {
|
|
8022
8225
|
try {
|
|
@@ -8044,7 +8247,7 @@ async function startMcpServer() {
|
|
|
8044
8247
|
);
|
|
8045
8248
|
server.tool(
|
|
8046
8249
|
"ask_git",
|
|
8047
|
-
"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.",
|
|
8048
8251
|
{
|
|
8049
8252
|
ref: z3.string().describe("Worktree thread id/ref"),
|
|
8050
8253
|
action: z3.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -8095,7 +8298,7 @@ async function startMcpServer() {
|
|
|
8095
8298
|
);
|
|
8096
8299
|
server.tool(
|
|
8097
8300
|
"fork_worktree",
|
|
8098
|
-
"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.",
|
|
8099
8302
|
{
|
|
8100
8303
|
ref: z3.string().describe("Worktree thread id/ref to fork"),
|
|
8101
8304
|
through_index: z3.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
@@ -8140,7 +8343,7 @@ async function startMcpServer() {
|
|
|
8140
8343
|
);
|
|
8141
8344
|
server.tool(
|
|
8142
8345
|
"fork_chat",
|
|
8143
|
-
"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.",
|
|
8144
8347
|
{
|
|
8145
8348
|
ref: z3.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
8146
8349
|
through_index: z3.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|