@sideboard-ai/core 0.1.157 → 0.1.158
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-LCDXN2B2.js → agents-E3ZPWZRS.js} +1 -1
- package/dist/{agents-C55LAEUR.js → agents-RGE7PSUY.js} +4 -4
- package/dist/{chunk-4I4VKAPZ.js → chunk-3SUZMPGL.js} +2 -2
- package/dist/{chunk-NVXVEGGM.js → chunk-5U4NZLBH.js} +4 -0
- package/dist/{chunk-CJQLPPEJ.js → chunk-A7QXLYBN.js} +1 -1
- package/dist/{chunk-TIJ6QVX5.js → chunk-BLRHO7XQ.js} +7 -2
- package/dist/{chunk-KQ4JOGUH.js → chunk-EPQIOAAY.js} +8 -4
- package/dist/{chunk-SUCJAWV4.js → chunk-FBFIOVDH.js} +375 -105
- package/dist/{chunk-2SDMENAL.js → chunk-JV4VNFMS.js} +1 -1
- package/dist/{chunk-GWEEGE6L.js → chunk-KYKNFJK5.js} +369 -99
- package/dist/{coordinator-prompt-AM47SATF.js → coordinator-prompt-GZESIQNH.js} +3 -3
- package/dist/{global-workspace-QV7CC7SK.js → global-workspace-REI55FZ6.js} +3 -3
- package/dist/index.cjs +456 -183
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +21 -24
- package/dist/mcp/run-stdio.cjs +456 -182
- package/dist/mcp/run-stdio.js +27 -30
- package/dist/{orchestrator-KQO4MXBI.js → orchestrator-J727WIJY.js} +6 -6
- package/dist/{orchestrator-BUH3LJLL.js → orchestrator-ZVW2AC6B.js} +2 -2
- package/dist/{plan-file-OZEBFSGQ.js → plan-file-I24VJVP2.js} +2 -2
- package/dist/{workspaces-Q2VCHIAS.js → workspaces-OCEPZXFD.js} +3 -3
- package/dist/{worktree-PEWDAX5O.js → worktree-VTMBZKY6.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9763,6 +9763,9 @@ function toolDescription(name, input) {
|
|
|
9763
9763
|
if (/present_files$/i.test(name)) {
|
|
9764
9764
|
return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
|
|
9765
9765
|
}
|
|
9766
|
+
if (/wait_for_job$/i.test(name)) {
|
|
9767
|
+
return str2(input?.id) ? `Wait for ${str2(input?.id)}` : "Wait for job";
|
|
9768
|
+
}
|
|
9766
9769
|
if (isSubagentToolName(name)) {
|
|
9767
9770
|
const desc = str2(input?.description);
|
|
9768
9771
|
const sub = asRecord(input?.subagentType);
|
|
@@ -10250,7 +10253,8 @@ var init_profile = __esm({
|
|
|
10250
10253
|
"ask_user",
|
|
10251
10254
|
"present_plan",
|
|
10252
10255
|
"present_schema",
|
|
10253
|
-
"present_files"
|
|
10256
|
+
"present_files",
|
|
10257
|
+
"wait_for_job"
|
|
10254
10258
|
];
|
|
10255
10259
|
WORKTREE_GITHUB_MCP_TOOLS = [
|
|
10256
10260
|
"github_get_issue",
|
|
@@ -10842,7 +10846,8 @@ var init_injected_mcp = __esm({
|
|
|
10842
10846
|
"mcp__sideboard__present_schema",
|
|
10843
10847
|
"mcp__sideboard__present_files",
|
|
10844
10848
|
"mcp__sideboard__ask_user",
|
|
10845
|
-
"mcp__sideboard__present_plan"
|
|
10849
|
+
"mcp__sideboard__present_plan",
|
|
10850
|
+
"mcp__sideboard__wait_for_job"
|
|
10846
10851
|
];
|
|
10847
10852
|
SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
10848
10853
|
SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
@@ -15321,6 +15326,242 @@ var init_child_halt = __esm({
|
|
|
15321
15326
|
}
|
|
15322
15327
|
});
|
|
15323
15328
|
|
|
15329
|
+
// src/mcp/wait-for-turn.ts
|
|
15330
|
+
function mcpWaitForTurnTimeoutMs(requested) {
|
|
15331
|
+
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
15332
|
+
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
15333
|
+
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
15334
|
+
}
|
|
15335
|
+
function mcpWaitStillRunningHint(status) {
|
|
15336
|
+
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
15337
|
+
}
|
|
15338
|
+
function mcpWaitFinishedHint(status) {
|
|
15339
|
+
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
15340
|
+
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
15341
|
+
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
15342
|
+
return void 0;
|
|
15343
|
+
}
|
|
15344
|
+
var MCP_WAIT_FOR_TURN_MAX_MS, MCP_WAIT_STILL_RUNNING_HINT, MCP_WAIT_QUEUED_HINT, MCP_WAIT_STOPPED_HINT, MCP_WAIT_BROKEN_HINT, MCP_WAIT_ERROR_HINT;
|
|
15345
|
+
var init_wait_for_turn = __esm({
|
|
15346
|
+
"src/mcp/wait-for-turn.ts"() {
|
|
15347
|
+
"use strict";
|
|
15348
|
+
MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
15349
|
+
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.";
|
|
15350
|
+
MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
|
|
15351
|
+
MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
|
|
15352
|
+
MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
15353
|
+
MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
|
|
15354
|
+
}
|
|
15355
|
+
});
|
|
15356
|
+
|
|
15357
|
+
// src/mcp/wait-for-job.ts
|
|
15358
|
+
function mcpWaitForJobTimeoutMs(requested) {
|
|
15359
|
+
return mcpWaitForTurnTimeoutMs(requested);
|
|
15360
|
+
}
|
|
15361
|
+
function sanitizeDetachedJobId(id) {
|
|
15362
|
+
const s = id.trim();
|
|
15363
|
+
if (!JOB_ID_RE.test(s)) {
|
|
15364
|
+
throw new Error(`detached-job id must be 1\u201364 chars [A-Za-z0-9._-], got ${JSON.stringify(id)}`);
|
|
15365
|
+
}
|
|
15366
|
+
return s;
|
|
15367
|
+
}
|
|
15368
|
+
function jobAlive(pid) {
|
|
15369
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
15370
|
+
try {
|
|
15371
|
+
process.kill(pid, 0);
|
|
15372
|
+
return true;
|
|
15373
|
+
} catch {
|
|
15374
|
+
return false;
|
|
15375
|
+
}
|
|
15376
|
+
}
|
|
15377
|
+
function readIntFile(file) {
|
|
15378
|
+
if (!(0, import_node_fs40.existsSync)(file)) return null;
|
|
15379
|
+
const n = Number.parseInt((0, import_node_fs40.readFileSync)(file, "utf8").trim(), 10);
|
|
15380
|
+
return Number.isInteger(n) ? n : null;
|
|
15381
|
+
}
|
|
15382
|
+
function jobDir(root, id, legacy = false) {
|
|
15383
|
+
return (0, import_node_path40.join)(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
|
|
15384
|
+
}
|
|
15385
|
+
function resolveJobDir(root, id) {
|
|
15386
|
+
const modern = jobDir(root, id, false);
|
|
15387
|
+
if ((0, import_node_fs40.existsSync)(modern)) return modern;
|
|
15388
|
+
const legacy = jobDir(root, id, true);
|
|
15389
|
+
if ((0, import_node_fs40.existsSync)(legacy)) return legacy;
|
|
15390
|
+
return modern;
|
|
15391
|
+
}
|
|
15392
|
+
function listJobIdsIn(root, rel) {
|
|
15393
|
+
const dir = (0, import_node_path40.join)(root, rel);
|
|
15394
|
+
if (!(0, import_node_fs40.existsSync)(dir)) return [];
|
|
15395
|
+
return (0, import_node_fs40.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
|
|
15396
|
+
}
|
|
15397
|
+
function listRunningDetachedJobs(worktreePath) {
|
|
15398
|
+
const root = worktreePath.trim();
|
|
15399
|
+
if (!root) return [];
|
|
15400
|
+
const ids = /* @__PURE__ */ new Set([
|
|
15401
|
+
...listJobIdsIn(root, DETACHED_JOBS_DIR),
|
|
15402
|
+
...listJobIdsIn(root, LEGACY_DETACHED_JOBS_DIR)
|
|
15403
|
+
]);
|
|
15404
|
+
const running = [];
|
|
15405
|
+
for (const id of ids) {
|
|
15406
|
+
const dir = resolveJobDir(root, id);
|
|
15407
|
+
const pid = readIntFile((0, import_node_path40.join)(dir, "pid"));
|
|
15408
|
+
if (pid != null && jobAlive(pid)) running.push(id);
|
|
15409
|
+
}
|
|
15410
|
+
return running.sort();
|
|
15411
|
+
}
|
|
15412
|
+
function looksLikeDeferredDonePromise(text6) {
|
|
15413
|
+
const t = (text6 ?? "").trim();
|
|
15414
|
+
if (!t) return false;
|
|
15415
|
+
return /\b(i['’]?ll|i will)\s+let you know\b/i.test(t) || /\blet you know when\b/i.test(t) || /\b(check back|ping me)\s+when\b/i.test(t) || /\bi(?:['’]ll| will)\s+(report|update you)\s+when\b/i.test(t);
|
|
15416
|
+
}
|
|
15417
|
+
function formatJobStillRunningContinuePrompt(jobIds) {
|
|
15418
|
+
const ids = jobIds.join(", ");
|
|
15419
|
+
return [
|
|
15420
|
+
`Detached job still running: ${ids}.`,
|
|
15421
|
+
"Do not end this turn. Loop wait_for_job (same id) and present_artifact type=log with content=delta until stillRunning is false.",
|
|
15422
|
+
"Then report the result. Do not tell the user you will let them know later."
|
|
15423
|
+
].join(" ");
|
|
15424
|
+
}
|
|
15425
|
+
function formatDeferredDoneContinuePrompt() {
|
|
15426
|
+
return [
|
|
15427
|
+
"You ended the turn after promising to report later, but no detached job is running.",
|
|
15428
|
+
"If tests/pack/deploy still need to run: start once with detached-job.js, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
15429
|
+
"Do not say you will let the user know later."
|
|
15430
|
+
].join(" ");
|
|
15431
|
+
}
|
|
15432
|
+
function turnWatchedDetachedJob(parts) {
|
|
15433
|
+
return parts.some((p) => {
|
|
15434
|
+
if (p.type !== "tool") return false;
|
|
15435
|
+
if (/wait_for_job$/i.test(p.name ?? "")) return true;
|
|
15436
|
+
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
15437
|
+
return /detached-job\.js\b/i.test(blob);
|
|
15438
|
+
});
|
|
15439
|
+
}
|
|
15440
|
+
function planJobContinue(opts) {
|
|
15441
|
+
if (opts.isOrchestrator) return { action: "none" };
|
|
15442
|
+
if (opts.agent === "brightsy") return { action: "none" };
|
|
15443
|
+
if (opts.queueLength > 0) return { action: "none" };
|
|
15444
|
+
if (opts.continueCount >= MAX_JOB_CONTINUES) return { action: "none" };
|
|
15445
|
+
const farewell = looksLikeDeferredDonePromise(opts.chatText);
|
|
15446
|
+
if (opts.runningJobIds.length > 0 && (farewell || opts.watchedJob)) {
|
|
15447
|
+
return {
|
|
15448
|
+
action: "wait",
|
|
15449
|
+
jobIds: opts.runningJobIds,
|
|
15450
|
+
prompt: formatJobStillRunningContinuePrompt(opts.runningJobIds)
|
|
15451
|
+
};
|
|
15452
|
+
}
|
|
15453
|
+
if (looksLikeDeferredDonePromise(opts.chatText) && !opts.alreadyNudged) {
|
|
15454
|
+
return { action: "nudge", prompt: formatDeferredDoneContinuePrompt() };
|
|
15455
|
+
}
|
|
15456
|
+
return { action: "none" };
|
|
15457
|
+
}
|
|
15458
|
+
function tailProgress(logFile, maxLines = 12) {
|
|
15459
|
+
if (!(0, import_node_fs40.existsSync)(logFile)) return "(no log yet)";
|
|
15460
|
+
const lines = (0, import_node_fs40.readFileSync)(logFile, "utf8").split("\n");
|
|
15461
|
+
return lines.slice(-maxLines).join("\n");
|
|
15462
|
+
}
|
|
15463
|
+
function readLogLines(file) {
|
|
15464
|
+
if (!(0, import_node_fs40.existsSync)(file)) return [];
|
|
15465
|
+
const lines = (0, import_node_fs40.readFileSync)(file, "utf8").split("\n");
|
|
15466
|
+
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
15467
|
+
return lines;
|
|
15468
|
+
}
|
|
15469
|
+
function takeDelta(logFile, cursorFile) {
|
|
15470
|
+
const lines = readLogLines(logFile);
|
|
15471
|
+
const cursor = readIntFile(cursorFile) ?? 0;
|
|
15472
|
+
const start = Math.min(Math.max(0, cursor), lines.length);
|
|
15473
|
+
return { delta: lines.slice(start).join("\n"), nextCursor: lines.length };
|
|
15474
|
+
}
|
|
15475
|
+
function snapshotJob(dir) {
|
|
15476
|
+
const pid = readIntFile((0, import_node_path40.join)(dir, "pid"));
|
|
15477
|
+
const running = pid != null && jobAlive(pid);
|
|
15478
|
+
return {
|
|
15479
|
+
pid,
|
|
15480
|
+
running,
|
|
15481
|
+
exitCode: readIntFile((0, import_node_path40.join)(dir, "exit")),
|
|
15482
|
+
log: (0, import_node_path40.join)(dir, "log"),
|
|
15483
|
+
cursor: (0, import_node_path40.join)(dir, "present.cursor"),
|
|
15484
|
+
progress: tailProgress((0, import_node_path40.join)(dir, "log"))
|
|
15485
|
+
};
|
|
15486
|
+
}
|
|
15487
|
+
function toResult(id, snap, extra) {
|
|
15488
|
+
const failed = extra?.failed === true || !snap.running && snap.exitCode != null && snap.exitCode !== 0;
|
|
15489
|
+
const ok = !snap.running && snap.exitCode === 0;
|
|
15490
|
+
const stillRunning = snap.running && !ok;
|
|
15491
|
+
const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
|
|
15492
|
+
try {
|
|
15493
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path40.dirname)(snap.cursor), { recursive: true });
|
|
15494
|
+
(0, import_node_fs40.writeFileSync)(snap.cursor, `${nextCursor}
|
|
15495
|
+
`);
|
|
15496
|
+
} catch {
|
|
15497
|
+
}
|
|
15498
|
+
const status = ok ? "ok" : failed && !stillRunning ? "failed" : stillRunning ? "running" : "idle";
|
|
15499
|
+
return {
|
|
15500
|
+
stillRunning,
|
|
15501
|
+
ok,
|
|
15502
|
+
failed: Boolean(failed && !stillRunning && !ok),
|
|
15503
|
+
status,
|
|
15504
|
+
id,
|
|
15505
|
+
pid: snap.pid,
|
|
15506
|
+
exitCode: snap.exitCode ?? void 0,
|
|
15507
|
+
delta,
|
|
15508
|
+
progress: extra?.progress ?? snap.progress,
|
|
15509
|
+
hint: stillRunning ? MCP_WAIT_JOB_STILL_RUNNING_HINT : void 0
|
|
15510
|
+
};
|
|
15511
|
+
}
|
|
15512
|
+
async function sleepMs(ms) {
|
|
15513
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
15514
|
+
}
|
|
15515
|
+
async function waitForDetachedJob(cwd, id, opts) {
|
|
15516
|
+
const jobId = sanitizeDetachedJobId(id);
|
|
15517
|
+
const root = cwd.trim() || process.cwd();
|
|
15518
|
+
const dir = resolveJobDir(root, jobId);
|
|
15519
|
+
const timeoutMs = mcpWaitForJobTimeoutMs(opts?.timeoutMs);
|
|
15520
|
+
const sleep2 = opts?.sleep ?? sleepMs;
|
|
15521
|
+
if (!(0, import_node_fs40.existsSync)(dir)) {
|
|
15522
|
+
return {
|
|
15523
|
+
stillRunning: false,
|
|
15524
|
+
ok: false,
|
|
15525
|
+
failed: true,
|
|
15526
|
+
status: "failed",
|
|
15527
|
+
id: jobId,
|
|
15528
|
+
delta: "",
|
|
15529
|
+
progress: "No detached job. Start one first.",
|
|
15530
|
+
hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
|
|
15531
|
+
};
|
|
15532
|
+
}
|
|
15533
|
+
const deadline = Date.now() + timeoutMs;
|
|
15534
|
+
let snap = snapshotJob(dir);
|
|
15535
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
15536
|
+
if (!snap.running && snap.pid == null && snap.progress === "(no log yet)") {
|
|
15537
|
+
return toResult(jobId, snap, {
|
|
15538
|
+
failed: true,
|
|
15539
|
+
progress: "No detached job. Start one first."
|
|
15540
|
+
});
|
|
15541
|
+
}
|
|
15542
|
+
while (Date.now() < deadline) {
|
|
15543
|
+
snap = snapshotJob(dir);
|
|
15544
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
15545
|
+
if (!snap.running) return toResult(jobId, snap, { failed: true });
|
|
15546
|
+
await sleep2(Math.min(2e3, Math.max(50, deadline - Date.now())));
|
|
15547
|
+
}
|
|
15548
|
+
snap = snapshotJob(dir);
|
|
15549
|
+
return toResult(jobId, snap);
|
|
15550
|
+
}
|
|
15551
|
+
var import_node_fs40, import_node_path40, MAX_JOB_CONTINUES, MCP_WAIT_JOB_STILL_RUNNING_HINT, JOB_ID_RE;
|
|
15552
|
+
var init_wait_for_job = __esm({
|
|
15553
|
+
"src/mcp/wait-for-job.ts"() {
|
|
15554
|
+
"use strict";
|
|
15555
|
+
import_node_fs40 = require("fs");
|
|
15556
|
+
import_node_path40 = require("path");
|
|
15557
|
+
init_workspace_scratch();
|
|
15558
|
+
init_wait_for_turn();
|
|
15559
|
+
MAX_JOB_CONTINUES = 8;
|
|
15560
|
+
MCP_WAIT_JOB_STILL_RUNNING_HINT = "Job is still running. present_artifact type=log with the same artifact_id and content=delta (new lines only). Then call wait_for_job again. Do not end the turn or tell the user you will let them know later.";
|
|
15561
|
+
JOB_ID_RE = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
15562
|
+
}
|
|
15563
|
+
});
|
|
15564
|
+
|
|
15324
15565
|
// src/detect/detect.ts
|
|
15325
15566
|
async function detectAgents() {
|
|
15326
15567
|
ensureAgentPath();
|
|
@@ -16447,7 +16688,7 @@ function reuseLiveThread(input, repoPath, match) {
|
|
|
16447
16688
|
}
|
|
16448
16689
|
async function createThread(input, _onSetupLine) {
|
|
16449
16690
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
16450
|
-
if (!(0,
|
|
16691
|
+
if (!(0, import_node_fs41.existsSync)(repoPath)) {
|
|
16451
16692
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
16452
16693
|
}
|
|
16453
16694
|
const reused = reuseLiveThread(input, repoPath, {
|
|
@@ -16627,11 +16868,11 @@ async function listLinearIssues(agent, repoPath) {
|
|
|
16627
16868
|
}
|
|
16628
16869
|
return adapter.listLinearIssues(repoPath);
|
|
16629
16870
|
}
|
|
16630
|
-
var
|
|
16871
|
+
var import_node_fs41;
|
|
16631
16872
|
var init_create = __esm({
|
|
16632
16873
|
"src/threads/create.ts"() {
|
|
16633
16874
|
"use strict";
|
|
16634
|
-
|
|
16875
|
+
import_node_fs41 = require("fs");
|
|
16635
16876
|
init_detect();
|
|
16636
16877
|
init_worktree();
|
|
16637
16878
|
init_home_board();
|
|
@@ -16734,20 +16975,20 @@ function writeTurnLive(threadId, progress) {
|
|
|
16734
16975
|
const path2 = threadLivePath(threadId);
|
|
16735
16976
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
16736
16977
|
try {
|
|
16737
|
-
(0,
|
|
16738
|
-
(0,
|
|
16978
|
+
(0, import_node_fs42.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
|
|
16979
|
+
(0, import_node_fs42.renameSync)(tmp, path2);
|
|
16739
16980
|
} catch {
|
|
16740
16981
|
try {
|
|
16741
|
-
(0,
|
|
16982
|
+
(0, import_node_fs42.unlinkSync)(tmp);
|
|
16742
16983
|
} catch {
|
|
16743
16984
|
}
|
|
16744
16985
|
}
|
|
16745
16986
|
}
|
|
16746
16987
|
function readTurnLive(threadId) {
|
|
16747
16988
|
const path2 = threadLivePath(threadId);
|
|
16748
|
-
if (!(0,
|
|
16989
|
+
if (!(0, import_node_fs42.existsSync)(path2)) return null;
|
|
16749
16990
|
try {
|
|
16750
|
-
const raw = JSON.parse((0,
|
|
16991
|
+
const raw = JSON.parse((0, import_node_fs42.readFileSync)(path2, "utf8"));
|
|
16751
16992
|
if (!raw || typeof raw.summary !== "string") return null;
|
|
16752
16993
|
return raw;
|
|
16753
16994
|
} catch {
|
|
@@ -16759,17 +17000,17 @@ function clearTurnLive(threadId) {
|
|
|
16759
17000
|
if (buf?.timer) clearTimeout(buf.timer);
|
|
16760
17001
|
buffers.delete(threadId);
|
|
16761
17002
|
const path2 = threadLivePath(threadId);
|
|
16762
|
-
if (!(0,
|
|
17003
|
+
if (!(0, import_node_fs42.existsSync)(path2)) return;
|
|
16763
17004
|
try {
|
|
16764
|
-
(0,
|
|
17005
|
+
(0, import_node_fs42.unlinkSync)(path2);
|
|
16765
17006
|
} catch {
|
|
16766
17007
|
}
|
|
16767
17008
|
}
|
|
16768
|
-
var
|
|
17009
|
+
var import_node_fs42, buffers, FLUSH_MS, MAX_PARTS;
|
|
16769
17010
|
var init_turn_live = __esm({
|
|
16770
17011
|
"src/store/turn-live.ts"() {
|
|
16771
17012
|
"use strict";
|
|
16772
|
-
|
|
17013
|
+
import_node_fs42 = require("fs");
|
|
16773
17014
|
init_message_parts();
|
|
16774
17015
|
init_paths();
|
|
16775
17016
|
buffers = /* @__PURE__ */ new Map();
|
|
@@ -16989,7 +17230,7 @@ var init_quota_failover = __esm({
|
|
|
16989
17230
|
// src/threads/adopt.ts
|
|
16990
17231
|
function thisModuleFile() {
|
|
16991
17232
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
16992
|
-
return cjsFile || process.argv[1] || (0,
|
|
17233
|
+
return cjsFile || process.argv[1] || (0, import_node_path41.join)(process.cwd(), "package.json");
|
|
16993
17234
|
}
|
|
16994
17235
|
function openReadonlySqlite(file) {
|
|
16995
17236
|
const req = (0, import_node_module4.createRequire)(thisModuleFile());
|
|
@@ -17007,21 +17248,21 @@ function mapAgentType(raw) {
|
|
|
17007
17248
|
return null;
|
|
17008
17249
|
}
|
|
17009
17250
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
17010
|
-
if (!workspacePath || !(0,
|
|
17251
|
+
if (!workspacePath || !(0, import_node_fs43.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
17011
17252
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
17012
17253
|
let best = null;
|
|
17013
17254
|
let hashes;
|
|
17014
17255
|
try {
|
|
17015
|
-
hashes = (0,
|
|
17256
|
+
hashes = (0, import_node_fs43.readdirSync)(CURSOR_SDK_STORE);
|
|
17016
17257
|
} catch {
|
|
17017
17258
|
return null;
|
|
17018
17259
|
}
|
|
17019
17260
|
for (const hash of hashes) {
|
|
17020
|
-
const agentsFile = (0,
|
|
17021
|
-
if (!(0,
|
|
17261
|
+
const agentsFile = (0, import_node_path41.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
17262
|
+
if (!(0, import_node_fs43.existsSync)(agentsFile)) continue;
|
|
17022
17263
|
let text6;
|
|
17023
17264
|
try {
|
|
17024
|
-
text6 = (0,
|
|
17265
|
+
text6 = (0, import_node_fs43.readFileSync)(agentsFile, "utf8");
|
|
17025
17266
|
} catch {
|
|
17026
17267
|
continue;
|
|
17027
17268
|
}
|
|
@@ -17045,7 +17286,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
17045
17286
|
return best?.agentId ?? null;
|
|
17046
17287
|
}
|
|
17047
17288
|
async function adoptThread(input) {
|
|
17048
|
-
if (!(0,
|
|
17289
|
+
if (!(0, import_node_fs43.existsSync)(input.worktreePath)) {
|
|
17049
17290
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
17050
17291
|
}
|
|
17051
17292
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -17074,18 +17315,18 @@ function conductorDbPath() {
|
|
|
17074
17315
|
return CONDUCTOR_DB;
|
|
17075
17316
|
}
|
|
17076
17317
|
function listConductorWorkspaces() {
|
|
17077
|
-
if (!(0,
|
|
17318
|
+
if (!(0, import_node_fs43.existsSync)(CONDUCTOR_DB)) {
|
|
17078
17319
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
17079
17320
|
}
|
|
17080
|
-
const tmp = (0,
|
|
17081
|
-
const snapshot = (0,
|
|
17321
|
+
const tmp = (0, import_node_fs43.mkdtempSync)((0, import_node_path41.join)((0, import_node_os13.tmpdir)(), "sideboard-conductor-"));
|
|
17322
|
+
const snapshot = (0, import_node_path41.join)(tmp, "conductor.db");
|
|
17082
17323
|
try {
|
|
17083
|
-
(0,
|
|
17324
|
+
(0, import_node_fs43.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
17084
17325
|
for (const suffix of ["-wal", "-shm"]) {
|
|
17085
17326
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
17086
|
-
if ((0,
|
|
17327
|
+
if ((0, import_node_fs43.existsSync)(src)) {
|
|
17087
17328
|
try {
|
|
17088
|
-
(0,
|
|
17329
|
+
(0, import_node_fs43.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
17089
17330
|
} catch {
|
|
17090
17331
|
}
|
|
17091
17332
|
}
|
|
@@ -17161,22 +17402,22 @@ function listConductorWorkspaces() {
|
|
|
17161
17402
|
db.close();
|
|
17162
17403
|
}
|
|
17163
17404
|
} finally {
|
|
17164
|
-
(0,
|
|
17405
|
+
(0, import_node_fs43.rmSync)(tmp, { recursive: true, force: true });
|
|
17165
17406
|
}
|
|
17166
17407
|
}
|
|
17167
17408
|
function importConductorWorkspace(workspaceId) {
|
|
17168
|
-
if (!(0,
|
|
17409
|
+
if (!(0, import_node_fs43.existsSync)(CONDUCTOR_DB)) {
|
|
17169
17410
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
17170
17411
|
}
|
|
17171
|
-
const tmp = (0,
|
|
17172
|
-
const snapshot = (0,
|
|
17412
|
+
const tmp = (0, import_node_fs43.mkdtempSync)((0, import_node_path41.join)((0, import_node_os13.tmpdir)(), "sideboard-conductor-"));
|
|
17413
|
+
const snapshot = (0, import_node_path41.join)(tmp, "conductor.db");
|
|
17173
17414
|
try {
|
|
17174
|
-
(0,
|
|
17415
|
+
(0, import_node_fs43.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
17175
17416
|
for (const suffix of ["-wal", "-shm"]) {
|
|
17176
17417
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
17177
|
-
if ((0,
|
|
17418
|
+
if ((0, import_node_fs43.existsSync)(src)) {
|
|
17178
17419
|
try {
|
|
17179
|
-
(0,
|
|
17420
|
+
(0, import_node_fs43.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
17180
17421
|
} catch {
|
|
17181
17422
|
}
|
|
17182
17423
|
}
|
|
@@ -17194,7 +17435,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
17194
17435
|
).get(workspaceId);
|
|
17195
17436
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
17196
17437
|
const worktreePath = String(row.workspacePath);
|
|
17197
|
-
if (!(0,
|
|
17438
|
+
if (!(0, import_node_fs43.existsSync)(worktreePath)) {
|
|
17198
17439
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
17199
17440
|
}
|
|
17200
17441
|
let sessionId = null;
|
|
@@ -17257,31 +17498,31 @@ function importConductorWorkspace(workspaceId) {
|
|
|
17257
17498
|
db.close();
|
|
17258
17499
|
}
|
|
17259
17500
|
} finally {
|
|
17260
|
-
(0,
|
|
17501
|
+
(0, import_node_fs43.rmSync)(tmp, { recursive: true, force: true });
|
|
17261
17502
|
}
|
|
17262
17503
|
}
|
|
17263
17504
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
17264
17505
|
return importConductorWorkspace(workspaceId);
|
|
17265
17506
|
}
|
|
17266
|
-
var import_node_child_process4,
|
|
17507
|
+
var import_node_child_process4, import_node_fs43, import_node_os13, import_node_path41, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
|
|
17267
17508
|
var init_adopt = __esm({
|
|
17268
17509
|
"src/threads/adopt.ts"() {
|
|
17269
17510
|
"use strict";
|
|
17270
17511
|
import_node_child_process4 = require("child_process");
|
|
17271
|
-
|
|
17512
|
+
import_node_fs43 = require("fs");
|
|
17272
17513
|
import_node_os13 = require("os");
|
|
17273
|
-
|
|
17514
|
+
import_node_path41 = require("path");
|
|
17274
17515
|
import_node_module4 = require("module");
|
|
17275
17516
|
init_worktree();
|
|
17276
17517
|
init_thread_store();
|
|
17277
|
-
CONDUCTOR_APP_SUPPORT = (0,
|
|
17518
|
+
CONDUCTOR_APP_SUPPORT = (0, import_node_path41.join)(
|
|
17278
17519
|
process.env.HOME ?? "",
|
|
17279
17520
|
"Library",
|
|
17280
17521
|
"Application Support",
|
|
17281
17522
|
"com.conductor.app"
|
|
17282
17523
|
);
|
|
17283
|
-
CONDUCTOR_DB = (0,
|
|
17284
|
-
CURSOR_SDK_STORE = (0,
|
|
17524
|
+
CONDUCTOR_DB = (0, import_node_path41.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
17525
|
+
CURSOR_SDK_STORE = (0, import_node_path41.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
17285
17526
|
}
|
|
17286
17527
|
});
|
|
17287
17528
|
|
|
@@ -17348,7 +17589,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
17348
17589
|
let createdWorktree = false;
|
|
17349
17590
|
const trees = await listWorktrees(repoPath);
|
|
17350
17591
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
17351
|
-
if (checkedOut?.path && (0,
|
|
17592
|
+
if (checkedOut?.path && (0, import_node_fs44.existsSync)(checkedOut.path)) {
|
|
17352
17593
|
if (input.reuseExistingWorktree !== false) {
|
|
17353
17594
|
worktreePath = checkedOut.path;
|
|
17354
17595
|
} else {
|
|
@@ -17490,7 +17731,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
17490
17731
|
async function createPrStack(input, onSetupLine) {
|
|
17491
17732
|
await requireAgent(input.agent);
|
|
17492
17733
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
17493
|
-
if (!(0,
|
|
17734
|
+
if (!(0, import_node_fs44.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
17494
17735
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
17495
17736
|
const status = await detectGhStack(repoPath);
|
|
17496
17737
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -17557,7 +17798,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
17557
17798
|
}
|
|
17558
17799
|
}
|
|
17559
17800
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
17560
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
17801
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs44.existsSync)(bootstrap.worktreePath)) {
|
|
17561
17802
|
try {
|
|
17562
17803
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
17563
17804
|
deleteBranch: bootstrap.branchName
|
|
@@ -17577,11 +17818,11 @@ function stackAgentDefaultsFrom(input) {
|
|
|
17577
17818
|
planMode: input.planMode
|
|
17578
17819
|
};
|
|
17579
17820
|
}
|
|
17580
|
-
var
|
|
17821
|
+
var import_node_fs44;
|
|
17581
17822
|
var init_stack_layers = __esm({
|
|
17582
17823
|
"src/threads/stack-layers.ts"() {
|
|
17583
17824
|
"use strict";
|
|
17584
|
-
|
|
17825
|
+
import_node_fs44 = require("fs");
|
|
17585
17826
|
init_detect();
|
|
17586
17827
|
init_run();
|
|
17587
17828
|
init_stack();
|
|
@@ -17594,7 +17835,7 @@ var init_stack_layers = __esm({
|
|
|
17594
17835
|
|
|
17595
17836
|
// src/diff/diff.ts
|
|
17596
17837
|
async function inspectGitWorktree(worktreePath) {
|
|
17597
|
-
if (!worktreePath || !(0,
|
|
17838
|
+
if (!worktreePath || !(0, import_node_fs45.existsSync)(worktreePath)) return "missing_worktree";
|
|
17598
17839
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
17599
17840
|
reject: false
|
|
17600
17841
|
});
|
|
@@ -17602,7 +17843,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
17602
17843
|
return "ok";
|
|
17603
17844
|
}
|
|
17604
17845
|
async function initializeGitRepository(worktreePath) {
|
|
17605
|
-
if (!worktreePath || !(0,
|
|
17846
|
+
if (!worktreePath || !(0, import_node_fs45.existsSync)(worktreePath)) {
|
|
17606
17847
|
throw new Error("Worktree not found");
|
|
17607
17848
|
}
|
|
17608
17849
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -17736,11 +17977,11 @@ new file mode 100644
|
|
|
17736
17977
|
};
|
|
17737
17978
|
}
|
|
17738
17979
|
async function untrackedPatch(worktreePath, path2, maxHunk) {
|
|
17739
|
-
const abs = (0,
|
|
17980
|
+
const abs = (0, import_node_path42.join)(worktreePath, path2);
|
|
17740
17981
|
try {
|
|
17741
|
-
const st = (0,
|
|
17982
|
+
const st = (0, import_node_fs45.statSync)(abs);
|
|
17742
17983
|
if (st.isFile() && st.size > maxHunk) {
|
|
17743
|
-
const buf = (0,
|
|
17984
|
+
const buf = (0, import_node_fs45.readFileSync)(abs).subarray(0, maxHunk);
|
|
17744
17985
|
return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
|
|
17745
17986
|
}
|
|
17746
17987
|
} catch {
|
|
@@ -18229,8 +18470,8 @@ function isImageRelativePath(relativePath) {
|
|
|
18229
18470
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
18230
18471
|
assertSafeRelativePath(relativePath);
|
|
18231
18472
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
18232
|
-
const abs = (0,
|
|
18233
|
-
const st = (0,
|
|
18473
|
+
const abs = (0, import_node_path42.join)(worktreePath, relativePath);
|
|
18474
|
+
const st = (0, import_node_fs45.statSync)(abs);
|
|
18234
18475
|
if (!st.isFile()) {
|
|
18235
18476
|
throw new Error(`Not a file: ${relativePath}`);
|
|
18236
18477
|
}
|
|
@@ -18239,7 +18480,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
18239
18480
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
18240
18481
|
);
|
|
18241
18482
|
}
|
|
18242
|
-
const buf = (0,
|
|
18483
|
+
const buf = (0, import_node_fs45.readFileSync)(abs);
|
|
18243
18484
|
return {
|
|
18244
18485
|
path: relativePath,
|
|
18245
18486
|
contentBase64: buf.toString("base64"),
|
|
@@ -18249,12 +18490,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
18249
18490
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
18250
18491
|
assertSafeRelativePath(relativePath);
|
|
18251
18492
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
18252
|
-
const abs = (0,
|
|
18253
|
-
const st = (0,
|
|
18493
|
+
const abs = (0, import_node_path42.join)(worktreePath, relativePath);
|
|
18494
|
+
const st = (0, import_node_fs45.statSync)(abs);
|
|
18254
18495
|
if (!st.isFile()) {
|
|
18255
18496
|
throw new Error(`Not a file: ${relativePath}`);
|
|
18256
18497
|
}
|
|
18257
|
-
const buf = (0,
|
|
18498
|
+
const buf = (0, import_node_fs45.readFileSync)(abs);
|
|
18258
18499
|
if (isImageRelativePath(relativePath)) {
|
|
18259
18500
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
18260
18501
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -18297,9 +18538,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
18297
18538
|
}
|
|
18298
18539
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
18299
18540
|
assertSafeRelativePath(relativePath);
|
|
18300
|
-
const abs = (0,
|
|
18301
|
-
(0,
|
|
18302
|
-
(0,
|
|
18541
|
+
const abs = (0, import_node_path42.join)(worktreePath, relativePath);
|
|
18542
|
+
(0, import_node_fs45.mkdirSync)((0, import_node_path42.dirname)(abs), { recursive: true });
|
|
18543
|
+
(0, import_node_fs45.writeFileSync)(abs, content, "utf8");
|
|
18303
18544
|
return { path: relativePath };
|
|
18304
18545
|
}
|
|
18305
18546
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -18316,12 +18557,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
|
18316
18557
|
truncated: full.files.length > maxFiles
|
|
18317
18558
|
};
|
|
18318
18559
|
}
|
|
18319
|
-
var
|
|
18560
|
+
var import_node_fs45, import_node_path42, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
|
|
18320
18561
|
var init_diff = __esm({
|
|
18321
18562
|
"src/diff/diff.ts"() {
|
|
18322
18563
|
"use strict";
|
|
18323
|
-
|
|
18324
|
-
|
|
18564
|
+
import_node_fs45 = require("fs");
|
|
18565
|
+
import_node_path42 = require("path");
|
|
18325
18566
|
init_run();
|
|
18326
18567
|
init_worktree();
|
|
18327
18568
|
mergeBaseCache = /* @__PURE__ */ new Map();
|
|
@@ -18470,7 +18711,7 @@ var init_long_running = __esm({
|
|
|
18470
18711
|
|
|
18471
18712
|
A Sideboard or Cursor **worktree turn** SIGTERMs the agent shell (and its process group) when the user sends another message or the turn is interrupted. \`block_until_ms: 0\` is not enough \u2014 the child stays in that group.
|
|
18472
18713
|
|
|
18473
|
-
Do **not** ask the human to check back. Detach, then **wait** in 45s slices (same idea as
|
|
18714
|
+
Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP \`wait_for_job\`, same idea as \`wait_for_turn\`) until \`stillRunning\` is false. Do not end the turn with \u201CI\u2019ll let you know.\u201D
|
|
18474
18715
|
|
|
18475
18716
|
## Tool
|
|
18476
18717
|
|
|
@@ -18480,7 +18721,8 @@ Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" st
|
|
|
18480
18721
|
# Start (exits in ~1s; job survives this turn)
|
|
18481
18722
|
node <detached-job.js> start <id> -- <command> [args...]
|
|
18482
18723
|
|
|
18483
|
-
# Wait \u2014
|
|
18724
|
+
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
18725
|
+
# Shell fallback:
|
|
18484
18726
|
node <detached-job.js> wait <id>
|
|
18485
18727
|
|
|
18486
18728
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
@@ -18493,7 +18735,7 @@ node <detached-job.js> status <id>
|
|
|
18493
18735
|
|
|
18494
18736
|
Wait JSON:
|
|
18495
18737
|
|
|
18496
|
-
- \`stillRunning: true\` \u2192
|
|
18738
|
+
- \`stillRunning: true\` \u2192 **call wait_for_job again** (or shell wait). Progress is in \`progress\` / \`phase\`. Do not start a second job. Do not ping the user.
|
|
18497
18739
|
- \`ok: true\` \u2192 exit 0 \u2192 continue the rest of the task.
|
|
18498
18740
|
- \`failed: true\` \u2192 exit 1 \u2192 read \`progress\`, fix, start **once**.
|
|
18499
18741
|
|
|
@@ -18523,7 +18765,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
18523
18765
|
|
|
18524
18766
|
1. \`start\` once. If JSON says \`already-running\`, do not start again.
|
|
18525
18767
|
2. Immediately \`present_artifact\` \`type=log\` (same \`artifact_id\`, \`status=running\`) \u2014 the human should see **working** in the side column, not a \u201Ccheck back later\u201D message.
|
|
18526
|
-
3. Loop \`
|
|
18768
|
+
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
18527
18769
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
18528
18770
|
|
|
18529
18771
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
|
|
@@ -18589,7 +18831,7 @@ function parseFrontmatter(content) {
|
|
|
18589
18831
|
}
|
|
18590
18832
|
function readSkill(skillMd, source) {
|
|
18591
18833
|
try {
|
|
18592
|
-
const content = (0,
|
|
18834
|
+
const content = (0, import_node_fs46.readFileSync)(skillMd, "utf8");
|
|
18593
18835
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
18594
18836
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
18595
18837
|
const name = fmName || dirName;
|
|
@@ -18608,19 +18850,19 @@ function readSkill(skillMd, source) {
|
|
|
18608
18850
|
}
|
|
18609
18851
|
}
|
|
18610
18852
|
function scanSkillsDir(dir, source, out) {
|
|
18611
|
-
if (!(0,
|
|
18853
|
+
if (!(0, import_node_fs46.existsSync)(dir)) return;
|
|
18612
18854
|
let entries;
|
|
18613
18855
|
try {
|
|
18614
|
-
entries = (0,
|
|
18856
|
+
entries = (0, import_node_fs46.readdirSync)(dir);
|
|
18615
18857
|
} catch {
|
|
18616
18858
|
return;
|
|
18617
18859
|
}
|
|
18618
18860
|
for (const entry of entries) {
|
|
18619
18861
|
if (entry.startsWith(".")) continue;
|
|
18620
|
-
const skillMd = (0,
|
|
18621
|
-
if (!(0,
|
|
18862
|
+
const skillMd = (0, import_node_path43.join)(dir, entry, "SKILL.md");
|
|
18863
|
+
if (!(0, import_node_fs46.existsSync)(skillMd)) continue;
|
|
18622
18864
|
try {
|
|
18623
|
-
if (!(0,
|
|
18865
|
+
if (!(0, import_node_fs46.statSync)(skillMd).isFile()) continue;
|
|
18624
18866
|
} catch {
|
|
18625
18867
|
continue;
|
|
18626
18868
|
}
|
|
@@ -18629,24 +18871,24 @@ function scanSkillsDir(dir, source, out) {
|
|
|
18629
18871
|
}
|
|
18630
18872
|
}
|
|
18631
18873
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
18632
|
-
if (!(0,
|
|
18874
|
+
if (!(0, import_node_fs46.existsSync)(pluginsRoot)) return;
|
|
18633
18875
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
18634
18876
|
if (depth > 7) return;
|
|
18635
18877
|
let entries;
|
|
18636
18878
|
try {
|
|
18637
|
-
entries = (0,
|
|
18879
|
+
entries = (0, import_node_fs46.readdirSync)(dir);
|
|
18638
18880
|
} catch {
|
|
18639
18881
|
return;
|
|
18640
18882
|
}
|
|
18641
18883
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
18642
|
-
const skill = readSkill((0,
|
|
18884
|
+
const skill = readSkill((0, import_node_path43.join)(dir, "SKILL.md"), "cli");
|
|
18643
18885
|
if (skill) out.push(skill);
|
|
18644
18886
|
}
|
|
18645
18887
|
for (const entry of entries) {
|
|
18646
18888
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
18647
|
-
const full = (0,
|
|
18889
|
+
const full = (0, import_node_path43.join)(dir, entry);
|
|
18648
18890
|
try {
|
|
18649
|
-
if (!(0,
|
|
18891
|
+
if (!(0, import_node_fs46.statSync)(full).isDirectory()) continue;
|
|
18650
18892
|
} catch {
|
|
18651
18893
|
continue;
|
|
18652
18894
|
}
|
|
@@ -18664,17 +18906,17 @@ function discoverSkills(worktreePath) {
|
|
|
18664
18906
|
const home = (0, import_node_os14.homedir)();
|
|
18665
18907
|
const collected = [];
|
|
18666
18908
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
18667
|
-
scanSkillsDir((0,
|
|
18909
|
+
scanSkillsDir((0, import_node_path43.join)(worktreePath, rel), "workspace", collected);
|
|
18668
18910
|
}
|
|
18669
18911
|
for (const abs of [
|
|
18670
|
-
(0,
|
|
18671
|
-
(0,
|
|
18672
|
-
(0,
|
|
18673
|
-
(0,
|
|
18912
|
+
(0, import_node_path43.join)(home, ".claude/skills"),
|
|
18913
|
+
(0, import_node_path43.join)(home, ".cursor/skills"),
|
|
18914
|
+
(0, import_node_path43.join)(home, ".sideboard/skills"),
|
|
18915
|
+
(0, import_node_path43.join)(home, ".brightsy/skills")
|
|
18674
18916
|
]) {
|
|
18675
18917
|
scanSkillsDir(abs, "user", collected);
|
|
18676
18918
|
}
|
|
18677
|
-
scanClaudePluginSkills((0,
|
|
18919
|
+
scanClaudePluginSkills((0, import_node_path43.join)(home, ".claude/plugins"), collected);
|
|
18678
18920
|
collected.push(...bundledSkills());
|
|
18679
18921
|
const rank = {
|
|
18680
18922
|
workspace: 0,
|
|
@@ -18700,7 +18942,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
18700
18942
|
\u2026(truncated)` : body;
|
|
18701
18943
|
}
|
|
18702
18944
|
}
|
|
18703
|
-
const raw = (0,
|
|
18945
|
+
const raw = (0, import_node_fs46.readFileSync)(skillPath, "utf8");
|
|
18704
18946
|
if (raw.startsWith("---")) {
|
|
18705
18947
|
const end = raw.indexOf("\n---", 3);
|
|
18706
18948
|
if (end >= 0) {
|
|
@@ -18714,13 +18956,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
18714
18956
|
|
|
18715
18957
|
\u2026(truncated)` : raw;
|
|
18716
18958
|
}
|
|
18717
|
-
var
|
|
18959
|
+
var import_node_fs46, import_node_os14, import_node_path43, BUNDLED_SKILL_PREFIX;
|
|
18718
18960
|
var init_discover = __esm({
|
|
18719
18961
|
"src/skills/discover.ts"() {
|
|
18720
18962
|
"use strict";
|
|
18721
|
-
|
|
18963
|
+
import_node_fs46 = require("fs");
|
|
18722
18964
|
import_node_os14 = require("os");
|
|
18723
|
-
|
|
18965
|
+
import_node_path43 = require("path");
|
|
18724
18966
|
init_long_running();
|
|
18725
18967
|
BUNDLED_SKILL_PREFIX = "bundled:";
|
|
18726
18968
|
}
|
|
@@ -18815,17 +19057,17 @@ var init_expand = __esm({
|
|
|
18815
19057
|
function packagedDetachedJobPath() {
|
|
18816
19058
|
const dir = packagedMcpDir();
|
|
18817
19059
|
if (!dir) return null;
|
|
18818
|
-
const script = (0,
|
|
18819
|
-
return (0,
|
|
19060
|
+
const script = (0, import_node_path44.join)(dir, "scripts", "detached-job.js");
|
|
19061
|
+
return (0, import_node_fs47.existsSync)(script) ? script : null;
|
|
18820
19062
|
}
|
|
18821
19063
|
function resolveDetachedJobScript() {
|
|
18822
19064
|
const packaged = packagedDetachedJobPath();
|
|
18823
19065
|
if (packaged) return packaged;
|
|
18824
|
-
let dir = (0,
|
|
19066
|
+
let dir = (0, import_node_path44.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
|
|
18825
19067
|
for (let i = 0; i < 8; i++) {
|
|
18826
|
-
const candidate = (0,
|
|
18827
|
-
if ((0,
|
|
18828
|
-
const parent = (0,
|
|
19068
|
+
const candidate = (0, import_node_path44.join)(dir, "scripts", "detached-job.js");
|
|
19069
|
+
if ((0, import_node_fs47.existsSync)(candidate)) return candidate;
|
|
19070
|
+
const parent = (0, import_node_path44.dirname)(dir);
|
|
18829
19071
|
if (parent === dir) break;
|
|
18830
19072
|
dir = parent;
|
|
18831
19073
|
}
|
|
@@ -18836,12 +19078,12 @@ function formatDetachedJobInvoke(scriptPath) {
|
|
|
18836
19078
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
18837
19079
|
return "node scripts/detached-job.js";
|
|
18838
19080
|
}
|
|
18839
|
-
var
|
|
19081
|
+
var import_node_fs47, import_node_path44, import_node_url3, import_meta3;
|
|
18840
19082
|
var init_detached_job_path = __esm({
|
|
18841
19083
|
"src/skills/detached-job-path.ts"() {
|
|
18842
19084
|
"use strict";
|
|
18843
|
-
|
|
18844
|
-
|
|
19085
|
+
import_node_fs47 = require("fs");
|
|
19086
|
+
import_node_path44 = require("path");
|
|
18845
19087
|
import_node_url3 = require("url");
|
|
18846
19088
|
init_packaged_runtime();
|
|
18847
19089
|
import_meta3 = {};
|
|
@@ -19062,14 +19304,15 @@ function formatLongRunningDirective(opts) {
|
|
|
19062
19304
|
`Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
|
|
19063
19305
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
19064
19306
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
19065
|
-
`- Loop
|
|
19307
|
+
`- Loop Sideboard MCP \`wait_for_job\` with the same id (returns in ~45s). stillRunning \u2192 present the same id with \`content=delta\` only \u2192 wait_for_job again. Shell fallback: \`${invoke} wait <id>\`. Do not resend the full log or HTML.`,
|
|
19308
|
+
"- Do not end the turn with \u201CI\u2019ll let you know when it\u2019s done.\u201D Stay in the loop until stillRunning is false.",
|
|
19066
19309
|
"- ok \u2192 finish the task. failed \u2192 read the log, fix, start once.",
|
|
19067
19310
|
"State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
|
|
19068
19311
|
].join("\n");
|
|
19069
19312
|
}
|
|
19070
19313
|
function formatLongRunningReminder(opts) {
|
|
19071
19314
|
const invoke = formatDetachedJobInvoke(opts?.scriptPath);
|
|
19072
|
-
return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait, present_artifact type=log (delta). Do not
|
|
19315
|
+
return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait), present_artifact type=log (delta). Do not say you will let the user know later \u2014 stay in the turn.`;
|
|
19073
19316
|
}
|
|
19074
19317
|
function formatArtifactDirective() {
|
|
19075
19318
|
return [
|
|
@@ -19106,11 +19349,11 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
19106
19349
|
const out = [];
|
|
19107
19350
|
for (const rel of candidates) {
|
|
19108
19351
|
if (seenPaths.has(rel)) continue;
|
|
19109
|
-
const abs = (0,
|
|
19110
|
-
if (!(0,
|
|
19352
|
+
const abs = (0, import_node_path45.join)(worktreePath, rel);
|
|
19353
|
+
if (!(0, import_node_fs48.existsSync)(abs)) continue;
|
|
19111
19354
|
try {
|
|
19112
|
-
if (!(0,
|
|
19113
|
-
let content = (0,
|
|
19355
|
+
if (!(0, import_node_fs48.statSync)(abs).isFile()) continue;
|
|
19356
|
+
let content = (0, import_node_fs48.readFileSync)(abs, "utf8");
|
|
19114
19357
|
if (!content.trim()) continue;
|
|
19115
19358
|
if (content.length > MAX_CHARS_PER_FILE) {
|
|
19116
19359
|
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
@@ -19150,12 +19393,12 @@ function withAgentInstructions(prompt, files) {
|
|
|
19150
19393
|
|
|
19151
19394
|
${prompt}`;
|
|
19152
19395
|
}
|
|
19153
|
-
var
|
|
19396
|
+
var import_node_fs48, import_node_path45, GITHUB_TICKET_REF, KEYED_TICKET_REF, FILES_BY_AGENT, MAX_CHARS_PER_FILE;
|
|
19154
19397
|
var init_instructions = __esm({
|
|
19155
19398
|
"src/agents/instructions.ts"() {
|
|
19156
19399
|
"use strict";
|
|
19157
|
-
|
|
19158
|
-
|
|
19400
|
+
import_node_fs48 = require("fs");
|
|
19401
|
+
import_node_path45 = require("path");
|
|
19159
19402
|
init_git_auth_mode();
|
|
19160
19403
|
init_worktree_labels();
|
|
19161
19404
|
init_detached_job_path();
|
|
@@ -19503,40 +19746,40 @@ __export(plan_file_exports, {
|
|
|
19503
19746
|
writePlanFile: () => writePlanFile
|
|
19504
19747
|
});
|
|
19505
19748
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
19506
|
-
const gitignoreAbs = (0,
|
|
19507
|
-
if ((0,
|
|
19508
|
-
(0,
|
|
19509
|
-
(0,
|
|
19749
|
+
const gitignoreAbs = (0, import_node_path46.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
19750
|
+
if ((0, import_node_fs49.existsSync)(gitignoreAbs)) return;
|
|
19751
|
+
(0, import_node_fs49.mkdirSync)((0, import_node_path46.dirname)(gitignoreAbs), { recursive: true });
|
|
19752
|
+
(0, import_node_fs49.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
19510
19753
|
}
|
|
19511
19754
|
function planFileAbs(worktreePath) {
|
|
19512
|
-
return (0,
|
|
19755
|
+
return (0, import_node_path46.join)(worktreePath, PLAN_FILE_REL);
|
|
19513
19756
|
}
|
|
19514
19757
|
function readTextIfPresent2(abs) {
|
|
19515
|
-
if (!(0,
|
|
19758
|
+
if (!(0, import_node_fs49.existsSync)(abs)) return null;
|
|
19516
19759
|
try {
|
|
19517
|
-
const content = (0,
|
|
19760
|
+
const content = (0, import_node_fs49.readFileSync)(abs, "utf8");
|
|
19518
19761
|
return content.trim() ? content : null;
|
|
19519
19762
|
} catch {
|
|
19520
19763
|
return null;
|
|
19521
19764
|
}
|
|
19522
19765
|
}
|
|
19523
19766
|
function readPlanFile(worktreePath) {
|
|
19524
|
-
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0,
|
|
19767
|
+
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path46.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path46.join)(worktreePath, LEGACY_PLAN_FILE_REL));
|
|
19525
19768
|
}
|
|
19526
19769
|
function writePlanFile(worktreePath, content) {
|
|
19527
19770
|
ensureAttachmentsGitignore(worktreePath);
|
|
19528
19771
|
const abs = planFileAbs(worktreePath);
|
|
19529
|
-
(0,
|
|
19772
|
+
(0, import_node_fs49.mkdirSync)((0, import_node_path46.dirname)(abs), { recursive: true });
|
|
19530
19773
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
19531
|
-
(0,
|
|
19774
|
+
(0, import_node_fs49.writeFileSync)(abs, body, "utf8");
|
|
19532
19775
|
return PLAN_FILE_REL;
|
|
19533
19776
|
}
|
|
19534
|
-
var
|
|
19777
|
+
var import_node_fs49, import_node_path46;
|
|
19535
19778
|
var init_plan_file = __esm({
|
|
19536
19779
|
"src/plan/plan-file.ts"() {
|
|
19537
19780
|
"use strict";
|
|
19538
|
-
|
|
19539
|
-
|
|
19781
|
+
import_node_fs49 = require("fs");
|
|
19782
|
+
import_node_path46 = require("path");
|
|
19540
19783
|
init_workspace_scratch();
|
|
19541
19784
|
init_plan_present();
|
|
19542
19785
|
init_plan_present();
|
|
@@ -19584,13 +19827,13 @@ var init_sync_branch = __esm({
|
|
|
19584
19827
|
|
|
19585
19828
|
// src/agents/cursor-store.ts
|
|
19586
19829
|
function cursorSdkStoreDir(threadId) {
|
|
19587
|
-
const root = (0,
|
|
19830
|
+
const root = (0, import_node_path47.join)(appDataDir(), CURSOR_SDK_STORE_DIR);
|
|
19588
19831
|
const id = sanitizeCursorStoreSegment(threadId);
|
|
19589
19832
|
if (!id) return root;
|
|
19590
|
-
return (0,
|
|
19833
|
+
return (0, import_node_path47.join)(root, "threads", id);
|
|
19591
19834
|
}
|
|
19592
19835
|
function cursorSdkRunsNdjsonPath(threadId) {
|
|
19593
|
-
return (0,
|
|
19836
|
+
return (0, import_node_path47.join)(cursorSdkStoreDir(threadId), "runs.ndjson");
|
|
19594
19837
|
}
|
|
19595
19838
|
function cursorSdkRunsNdjsonSearchPaths(threadId) {
|
|
19596
19839
|
const scoped = cursorSdkRunsNdjsonPath(threadId);
|
|
@@ -19603,11 +19846,11 @@ function cursorSdkRunsNdjsonSearchPaths(threadId) {
|
|
|
19603
19846
|
function sanitizeCursorStoreSegment(threadId) {
|
|
19604
19847
|
return (threadId ?? "").trim().replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
19605
19848
|
}
|
|
19606
|
-
var
|
|
19849
|
+
var import_node_path47, CURSOR_SDK_STORE_DIR;
|
|
19607
19850
|
var init_cursor_store = __esm({
|
|
19608
19851
|
"src/agents/cursor-store.ts"() {
|
|
19609
19852
|
"use strict";
|
|
19610
|
-
|
|
19853
|
+
import_node_path47 = require("path");
|
|
19611
19854
|
init_paths();
|
|
19612
19855
|
CURSOR_SDK_STORE_DIR = "cursor-sdk-store";
|
|
19613
19856
|
}
|
|
@@ -19629,9 +19872,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
19629
19872
|
return best;
|
|
19630
19873
|
}
|
|
19631
19874
|
function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
19632
|
-
if (!(0,
|
|
19875
|
+
if (!(0, import_node_fs50.existsSync)(runsPath)) return null;
|
|
19633
19876
|
try {
|
|
19634
|
-
const lines = (0,
|
|
19877
|
+
const lines = (0, import_node_fs50.readFileSync)(runsPath, "utf8").split("\n");
|
|
19635
19878
|
let best = null;
|
|
19636
19879
|
for (const line of lines) {
|
|
19637
19880
|
const trimmed = line.trim();
|
|
@@ -19657,11 +19900,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
|
19657
19900
|
return null;
|
|
19658
19901
|
}
|
|
19659
19902
|
}
|
|
19660
|
-
var
|
|
19903
|
+
var import_node_fs50;
|
|
19661
19904
|
var init_cursor_recover = __esm({
|
|
19662
19905
|
"src/agents/cursor-recover.ts"() {
|
|
19663
19906
|
"use strict";
|
|
19664
|
-
|
|
19907
|
+
import_node_fs50 = require("fs");
|
|
19665
19908
|
init_cursor_store();
|
|
19666
19909
|
}
|
|
19667
19910
|
});
|
|
@@ -19793,13 +20036,13 @@ async function startOrchestration(opts) {
|
|
|
19793
20036
|
}
|
|
19794
20037
|
return updated;
|
|
19795
20038
|
}
|
|
19796
|
-
var import_node_events,
|
|
20039
|
+
var import_node_events, import_node_fs51, LIVE_TURN_SPAWN_GRACE_MS, STALE_AGENT_PID_WAIT_MS, Orchestrator, singleton;
|
|
19797
20040
|
var init_orchestrator = __esm({
|
|
19798
20041
|
"src/orchestrator/orchestrator.ts"() {
|
|
19799
20042
|
"use strict";
|
|
19800
20043
|
import_node_events = require("events");
|
|
19801
20044
|
init_outbound_watch();
|
|
19802
|
-
|
|
20045
|
+
import_node_fs51 = require("fs");
|
|
19803
20046
|
init_error_detail();
|
|
19804
20047
|
init_run();
|
|
19805
20048
|
init_stale_lock();
|
|
@@ -19820,6 +20063,7 @@ var init_orchestrator = __esm({
|
|
|
19820
20063
|
init_thread_store();
|
|
19821
20064
|
init_desktop_host();
|
|
19822
20065
|
init_child_halt();
|
|
20066
|
+
init_wait_for_job();
|
|
19823
20067
|
init_create();
|
|
19824
20068
|
init_cowboy();
|
|
19825
20069
|
init_orchestrator_capable();
|
|
@@ -19884,6 +20128,9 @@ var init_orchestrator = __esm({
|
|
|
19884
20128
|
* finish or a user send so a later crash can recover again.
|
|
19885
20129
|
*/
|
|
19886
20130
|
crashContinued = /* @__PURE__ */ new Set();
|
|
20131
|
+
/** Auto-continues after a worktree turn ended while a detached job still runs. */
|
|
20132
|
+
jobContinueCount = /* @__PURE__ */ new Map();
|
|
20133
|
+
jobContinueNudged = /* @__PURE__ */ new Set();
|
|
19887
20134
|
maxConcurrent;
|
|
19888
20135
|
runningCount = 0;
|
|
19889
20136
|
constructor(opts) {
|
|
@@ -19967,7 +20214,7 @@ var init_orchestrator = __esm({
|
|
|
19967
20214
|
}
|
|
19968
20215
|
continue;
|
|
19969
20216
|
}
|
|
19970
|
-
if (!(0,
|
|
20217
|
+
if (!(0, import_node_fs51.existsSync)(thread.worktreePath)) {
|
|
19971
20218
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
19972
20219
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
19973
20220
|
continue;
|
|
@@ -20182,6 +20429,37 @@ var init_orchestrator = __esm({
|
|
|
20182
20429
|
this.emit({ type: "queue_changed", threadId, queue });
|
|
20183
20430
|
this.haltDrain.delete(threadId);
|
|
20184
20431
|
}
|
|
20432
|
+
/**
|
|
20433
|
+
* Worktree agent ended the turn after “I’ll let you know” (or left a
|
|
20434
|
+
* detached test/pack job running). Queue a continue so the chat does not
|
|
20435
|
+
* go idle with nobody watching the log.
|
|
20436
|
+
*/
|
|
20437
|
+
maybeEnqueueJobContinue(threadId, chatText, parts = []) {
|
|
20438
|
+
if (this.haltDrain.has(threadId)) return;
|
|
20439
|
+
const thread = readThread(threadId);
|
|
20440
|
+
if (!thread || thread.status === "archived") return;
|
|
20441
|
+
const runningJobIds = listRunningDetachedJobs(thread.worktreePath ?? "");
|
|
20442
|
+
const decision = planJobContinue({
|
|
20443
|
+
runningJobIds,
|
|
20444
|
+
chatText,
|
|
20445
|
+
queueLength: thread.queue.length,
|
|
20446
|
+
continueCount: this.jobContinueCount.get(threadId) ?? 0,
|
|
20447
|
+
alreadyNudged: this.jobContinueNudged.has(threadId),
|
|
20448
|
+
isOrchestrator: isOrchestratorThread(thread),
|
|
20449
|
+
agent: thread.agent,
|
|
20450
|
+
watchedJob: turnWatchedDetachedJob(parts)
|
|
20451
|
+
});
|
|
20452
|
+
if (decision.action === "none") {
|
|
20453
|
+
if (runningJobIds.length === 0) this.jobContinueCount.delete(threadId);
|
|
20454
|
+
return;
|
|
20455
|
+
}
|
|
20456
|
+
if (decision.action === "nudge") this.jobContinueNudged.add(threadId);
|
|
20457
|
+
else this.jobContinueCount.set(threadId, (this.jobContinueCount.get(threadId) ?? 0) + 1);
|
|
20458
|
+
const queue = [decision.prompt, ...thread.queue];
|
|
20459
|
+
updateThread(threadId, { queue });
|
|
20460
|
+
this.emit({ type: "queue_changed", threadId, queue });
|
|
20461
|
+
this.haltDrain.delete(threadId);
|
|
20462
|
+
}
|
|
20185
20463
|
getThreads(includeArchived = false) {
|
|
20186
20464
|
return listThreads({ includeArchived });
|
|
20187
20465
|
}
|
|
@@ -20281,6 +20559,8 @@ var init_orchestrator = __esm({
|
|
|
20281
20559
|
}
|
|
20282
20560
|
const queue = [...current.queue, prompt];
|
|
20283
20561
|
this.crashContinued.delete(thread.id);
|
|
20562
|
+
this.jobContinueCount.delete(thread.id);
|
|
20563
|
+
this.jobContinueNudged.delete(thread.id);
|
|
20284
20564
|
this.haltDrain.delete(thread.id);
|
|
20285
20565
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
|
|
20286
20566
|
shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
|
|
@@ -20795,6 +21075,7 @@ var init_orchestrator = __esm({
|
|
|
20795
21075
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
20796
21076
|
if (exitCode === 0) {
|
|
20797
21077
|
this.crashContinued.delete(threadId);
|
|
21078
|
+
this.maybeEnqueueJobContinue(threadId, chatText, parts);
|
|
20798
21079
|
} else {
|
|
20799
21080
|
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
20800
21081
|
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
@@ -21795,7 +22076,7 @@ var init_orchestrator = __esm({
|
|
|
21795
22076
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
21796
22077
|
return restored2;
|
|
21797
22078
|
}
|
|
21798
|
-
if (!(0,
|
|
22079
|
+
if (!(0, import_node_fs51.existsSync)(thread.worktreePath)) {
|
|
21799
22080
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
21800
22081
|
throw new Error(
|
|
21801
22082
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
@@ -24415,7 +24696,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
24415
24696
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
24416
24697
|
var import_zod6 = require("zod");
|
|
24417
24698
|
var import_node_crypto13 = require("crypto");
|
|
24418
|
-
var
|
|
24699
|
+
var import_node_path50 = require("path");
|
|
24419
24700
|
init_orchestrator();
|
|
24420
24701
|
init_worktree();
|
|
24421
24702
|
init_global_workspace();
|
|
@@ -24432,30 +24713,8 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
24432
24713
|
|
|
24433
24714
|
// src/mcp/server.ts
|
|
24434
24715
|
init_profile();
|
|
24435
|
-
|
|
24436
|
-
|
|
24437
|
-
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
24438
|
-
function mcpWaitForTurnTimeoutMs(requested) {
|
|
24439
|
-
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
24440
|
-
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
24441
|
-
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
24442
|
-
}
|
|
24443
|
-
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.";
|
|
24444
|
-
var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
|
|
24445
|
-
function mcpWaitStillRunningHint(status) {
|
|
24446
|
-
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
24447
|
-
}
|
|
24448
|
-
var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
|
|
24449
|
-
var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
24450
|
-
var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
|
|
24451
|
-
function mcpWaitFinishedHint(status) {
|
|
24452
|
-
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
24453
|
-
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
24454
|
-
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
24455
|
-
return void 0;
|
|
24456
|
-
}
|
|
24457
|
-
|
|
24458
|
-
// src/mcp/server.ts
|
|
24716
|
+
init_wait_for_turn();
|
|
24717
|
+
init_wait_for_job();
|
|
24459
24718
|
init_message_parts();
|
|
24460
24719
|
init_turn_live();
|
|
24461
24720
|
|
|
@@ -25606,27 +25865,27 @@ init_gh_errors();
|
|
|
25606
25865
|
init_git_auth_mode();
|
|
25607
25866
|
|
|
25608
25867
|
// src/board/load-home-board.ts
|
|
25609
|
-
var
|
|
25610
|
-
var
|
|
25868
|
+
var import_node_fs53 = require("fs");
|
|
25869
|
+
var import_node_path49 = require("path");
|
|
25611
25870
|
init_worktree();
|
|
25612
25871
|
init_paths();
|
|
25613
25872
|
|
|
25614
25873
|
// src/board/board-pins.ts
|
|
25615
25874
|
var import_node_crypto12 = require("crypto");
|
|
25616
|
-
var
|
|
25617
|
-
var
|
|
25875
|
+
var import_node_fs52 = require("fs");
|
|
25876
|
+
var import_node_path48 = require("path");
|
|
25618
25877
|
init_paths();
|
|
25619
25878
|
init_home_board();
|
|
25620
25879
|
var FILE = "home-board-pins.json";
|
|
25621
25880
|
var VERSION = 1;
|
|
25622
25881
|
function pinsFile() {
|
|
25623
|
-
return (0,
|
|
25882
|
+
return (0, import_node_path48.join)(appDataDir(), FILE);
|
|
25624
25883
|
}
|
|
25625
25884
|
function readDisk() {
|
|
25626
25885
|
const path2 = pinsFile();
|
|
25627
|
-
if (!(0,
|
|
25886
|
+
if (!(0, import_node_fs52.existsSync)(path2)) return [];
|
|
25628
25887
|
try {
|
|
25629
|
-
const raw = JSON.parse((0,
|
|
25888
|
+
const raw = JSON.parse((0, import_node_fs52.readFileSync)(path2, "utf8"));
|
|
25630
25889
|
if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
|
|
25631
25890
|
return raw.items.filter((item) => item?.id && item.kind && item.ref);
|
|
25632
25891
|
} catch {
|
|
@@ -25634,8 +25893,8 @@ function readDisk() {
|
|
|
25634
25893
|
}
|
|
25635
25894
|
}
|
|
25636
25895
|
function writeDisk(items) {
|
|
25637
|
-
(0,
|
|
25638
|
-
(0,
|
|
25896
|
+
(0, import_node_fs52.mkdirSync)(appDataDir(), { recursive: true });
|
|
25897
|
+
(0, import_node_fs52.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
|
|
25639
25898
|
}
|
|
25640
25899
|
function listBoardPins() {
|
|
25641
25900
|
return readDisk();
|
|
@@ -25687,7 +25946,7 @@ function replaceBoardPins(items) {
|
|
|
25687
25946
|
}
|
|
25688
25947
|
function clearBoardPins() {
|
|
25689
25948
|
try {
|
|
25690
|
-
(0,
|
|
25949
|
+
(0, import_node_fs52.unlinkSync)(pinsFile());
|
|
25691
25950
|
} catch {
|
|
25692
25951
|
}
|
|
25693
25952
|
}
|
|
@@ -25704,7 +25963,7 @@ function homeBoardWorkspaceKey(workspaces) {
|
|
|
25704
25963
|
return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
|
|
25705
25964
|
}
|
|
25706
25965
|
function cacheFile() {
|
|
25707
|
-
return (0,
|
|
25966
|
+
return (0, import_node_path49.join)(appDataDir(), "home-board-cache.json");
|
|
25708
25967
|
}
|
|
25709
25968
|
function emptyInputs() {
|
|
25710
25969
|
return {
|
|
@@ -25730,9 +25989,9 @@ function shouldCacheHomeBoardInputs(inputs) {
|
|
|
25730
25989
|
}
|
|
25731
25990
|
function readDiskCache() {
|
|
25732
25991
|
const path2 = cacheFile();
|
|
25733
|
-
if (!(0,
|
|
25992
|
+
if (!(0, import_node_fs53.existsSync)(path2)) return null;
|
|
25734
25993
|
try {
|
|
25735
|
-
const raw = JSON.parse((0,
|
|
25994
|
+
const raw = JSON.parse((0, import_node_fs53.readFileSync)(path2, "utf8"));
|
|
25736
25995
|
if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
|
|
25737
25996
|
return null;
|
|
25738
25997
|
}
|
|
@@ -25746,14 +26005,14 @@ function readDiskCache() {
|
|
|
25746
26005
|
}
|
|
25747
26006
|
function writeDiskCache(entry) {
|
|
25748
26007
|
try {
|
|
25749
|
-
(0,
|
|
25750
|
-
(0,
|
|
26008
|
+
(0, import_node_fs53.mkdirSync)(appDataDir(), { recursive: true });
|
|
26009
|
+
(0, import_node_fs53.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
|
|
25751
26010
|
} catch {
|
|
25752
26011
|
}
|
|
25753
26012
|
}
|
|
25754
26013
|
function deleteDiskCache() {
|
|
25755
26014
|
try {
|
|
25756
|
-
(0,
|
|
26015
|
+
(0, import_node_fs53.unlinkSync)(cacheFile());
|
|
25757
26016
|
} catch {
|
|
25758
26017
|
}
|
|
25759
26018
|
}
|
|
@@ -26060,7 +26319,7 @@ async function startMcpServer() {
|
|
|
26060
26319
|
async () => {
|
|
26061
26320
|
const threads = orch.getThreads(true);
|
|
26062
26321
|
const lines = threads.map((t) => {
|
|
26063
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
26322
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path50.basename)(t.repoPath) || t.repoPath;
|
|
26064
26323
|
const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
|
|
26065
26324
|
const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
|
|
26066
26325
|
const preview = lastMessagePreview(t.messages, 80);
|
|
@@ -26294,6 +26553,20 @@ async function startMcpServer() {
|
|
|
26294
26553
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
26295
26554
|
}
|
|
26296
26555
|
);
|
|
26556
|
+
server.tool(
|
|
26557
|
+
"wait_for_job",
|
|
26558
|
+
"Wait on a detached long job (tests, pack, deploy) started with detached-job.js. MCP clients kill tools around 60s, so this returns within 45s. stillRunning is the source of truth \u2014 if true, present_artifact type=log with content=delta (same artifact_id) and call wait_for_job again. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
26559
|
+
{
|
|
26560
|
+
id: import_zod6.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
26561
|
+
timeoutMs: import_zod6.z.number().optional()
|
|
26562
|
+
},
|
|
26563
|
+
async ({ id, timeoutMs }) => {
|
|
26564
|
+
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
26565
|
+
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
26566
|
+
});
|
|
26567
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
26568
|
+
}
|
|
26569
|
+
);
|
|
26297
26570
|
if (!worktreeProfile) {
|
|
26298
26571
|
registerSlackTools(server);
|
|
26299
26572
|
registerConnectedIssueVendorTools(server);
|
|
@@ -29156,9 +29429,9 @@ var import_node_http3 = require("http");
|
|
|
29156
29429
|
var import_ws3 = require("ws");
|
|
29157
29430
|
|
|
29158
29431
|
// src/slack/relay-static.ts
|
|
29159
|
-
var
|
|
29432
|
+
var import_node_fs54 = require("fs");
|
|
29160
29433
|
var import_promises = require("fs/promises");
|
|
29161
|
-
var
|
|
29434
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
29162
29435
|
var TYPES = {
|
|
29163
29436
|
".css": "text/css; charset=utf-8",
|
|
29164
29437
|
".html": "text/html; charset=utf-8",
|
|
@@ -29189,9 +29462,9 @@ function resolveStaticPath(root, requestUrl) {
|
|
|
29189
29462
|
return null;
|
|
29190
29463
|
}
|
|
29191
29464
|
if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
|
|
29192
|
-
const rootResolved =
|
|
29193
|
-
const candidate =
|
|
29194
|
-
if (candidate !== rootResolved && !candidate.startsWith(rootResolved +
|
|
29465
|
+
const rootResolved = import_node_path51.default.resolve(root);
|
|
29466
|
+
const candidate = import_node_path51.default.resolve(rootResolved, `.${pathname}`);
|
|
29467
|
+
if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path51.default.sep)) {
|
|
29195
29468
|
return null;
|
|
29196
29469
|
}
|
|
29197
29470
|
return candidate;
|
|
@@ -29205,7 +29478,7 @@ async function fileSize(file) {
|
|
|
29205
29478
|
}
|
|
29206
29479
|
}
|
|
29207
29480
|
function sendFile(req, res, file, size) {
|
|
29208
|
-
const ext =
|
|
29481
|
+
const ext = import_node_path51.default.extname(file).toLowerCase();
|
|
29209
29482
|
res.writeHead(200, {
|
|
29210
29483
|
"Content-Type": TYPES[ext] ?? "application/octet-stream",
|
|
29211
29484
|
"Content-Length": size,
|
|
@@ -29215,7 +29488,7 @@ function sendFile(req, res, file, size) {
|
|
|
29215
29488
|
res.end();
|
|
29216
29489
|
return true;
|
|
29217
29490
|
}
|
|
29218
|
-
(0,
|
|
29491
|
+
(0, import_node_fs54.createReadStream)(file).pipe(res);
|
|
29219
29492
|
return true;
|
|
29220
29493
|
}
|
|
29221
29494
|
async function tryServeStatic(req, res, root) {
|
|
@@ -29224,7 +29497,7 @@ async function tryServeStatic(req, res, root) {
|
|
|
29224
29497
|
if (!candidate) return false;
|
|
29225
29498
|
const direct = await fileSize(candidate);
|
|
29226
29499
|
if (direct != null) return sendFile(req, res, candidate, direct);
|
|
29227
|
-
const asIndex =
|
|
29500
|
+
const asIndex = import_node_path51.default.join(candidate, "index.html");
|
|
29228
29501
|
const indexSize = await fileSize(asIndex);
|
|
29229
29502
|
if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
|
|
29230
29503
|
return false;
|