@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/mcp/run-stdio.cjs
CHANGED
|
@@ -5491,12 +5491,14 @@ function isWorkspaceScratchPath(relativePath) {
|
|
|
5491
5491
|
const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
5492
5492
|
return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".sideboard" || p.startsWith(".sideboard/") || p === ".context" || p.startsWith(".context/");
|
|
5493
5493
|
}
|
|
5494
|
-
var ATTACHMENTS_DIR, LEGACY_ATTACHMENTS_DIR, ATTACHMENTS_GITIGNORE;
|
|
5494
|
+
var ATTACHMENTS_DIR, DETACHED_JOBS_DIR, LEGACY_ATTACHMENTS_DIR, LEGACY_DETACHED_JOBS_DIR, ATTACHMENTS_GITIGNORE;
|
|
5495
5495
|
var init_workspace_scratch = __esm({
|
|
5496
5496
|
"src/paths/workspace-scratch.ts"() {
|
|
5497
5497
|
"use strict";
|
|
5498
5498
|
ATTACHMENTS_DIR = ".context/attachments";
|
|
5499
|
+
DETACHED_JOBS_DIR = ".context/.sideboard/detached-jobs";
|
|
5499
5500
|
LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
5501
|
+
LEGACY_DETACHED_JOBS_DIR = ".sideboard/detached-jobs";
|
|
5500
5502
|
ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
|
|
5501
5503
|
*
|
|
5502
5504
|
!.gitignore
|
|
@@ -8692,6 +8694,9 @@ function toolDescription(name, input) {
|
|
|
8692
8694
|
if (/present_files$/i.test(name)) {
|
|
8693
8695
|
return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
|
|
8694
8696
|
}
|
|
8697
|
+
if (/wait_for_job$/i.test(name)) {
|
|
8698
|
+
return str2(input?.id) ? `Wait for ${str2(input?.id)}` : "Wait for job";
|
|
8699
|
+
}
|
|
8695
8700
|
if (isSubagentToolName(name)) {
|
|
8696
8701
|
const desc = str2(input?.description);
|
|
8697
8702
|
const sub = asRecord(input?.subagentType);
|
|
@@ -9664,7 +9669,8 @@ var init_injected_mcp = __esm({
|
|
|
9664
9669
|
"mcp__sideboard__present_schema",
|
|
9665
9670
|
"mcp__sideboard__present_files",
|
|
9666
9671
|
"mcp__sideboard__ask_user",
|
|
9667
|
-
"mcp__sideboard__present_plan"
|
|
9672
|
+
"mcp__sideboard__present_plan",
|
|
9673
|
+
"mcp__sideboard__wait_for_job"
|
|
9668
9674
|
];
|
|
9669
9675
|
SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
9670
9676
|
SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
@@ -13686,6 +13692,242 @@ var init_child_halt = __esm({
|
|
|
13686
13692
|
}
|
|
13687
13693
|
});
|
|
13688
13694
|
|
|
13695
|
+
// src/mcp/wait-for-turn.ts
|
|
13696
|
+
function mcpWaitForTurnTimeoutMs(requested) {
|
|
13697
|
+
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
13698
|
+
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
13699
|
+
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
13700
|
+
}
|
|
13701
|
+
function mcpWaitStillRunningHint(status) {
|
|
13702
|
+
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
13703
|
+
}
|
|
13704
|
+
function mcpWaitFinishedHint(status) {
|
|
13705
|
+
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
13706
|
+
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
13707
|
+
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
13708
|
+
return void 0;
|
|
13709
|
+
}
|
|
13710
|
+
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;
|
|
13711
|
+
var init_wait_for_turn = __esm({
|
|
13712
|
+
"src/mcp/wait-for-turn.ts"() {
|
|
13713
|
+
"use strict";
|
|
13714
|
+
MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
13715
|
+
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.";
|
|
13716
|
+
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.";
|
|
13717
|
+
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.";
|
|
13718
|
+
MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
13719
|
+
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.";
|
|
13720
|
+
}
|
|
13721
|
+
});
|
|
13722
|
+
|
|
13723
|
+
// src/mcp/wait-for-job.ts
|
|
13724
|
+
function mcpWaitForJobTimeoutMs(requested) {
|
|
13725
|
+
return mcpWaitForTurnTimeoutMs(requested);
|
|
13726
|
+
}
|
|
13727
|
+
function sanitizeDetachedJobId(id) {
|
|
13728
|
+
const s = id.trim();
|
|
13729
|
+
if (!JOB_ID_RE.test(s)) {
|
|
13730
|
+
throw new Error(`detached-job id must be 1\u201364 chars [A-Za-z0-9._-], got ${JSON.stringify(id)}`);
|
|
13731
|
+
}
|
|
13732
|
+
return s;
|
|
13733
|
+
}
|
|
13734
|
+
function jobAlive(pid) {
|
|
13735
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
13736
|
+
try {
|
|
13737
|
+
process.kill(pid, 0);
|
|
13738
|
+
return true;
|
|
13739
|
+
} catch {
|
|
13740
|
+
return false;
|
|
13741
|
+
}
|
|
13742
|
+
}
|
|
13743
|
+
function readIntFile(file) {
|
|
13744
|
+
if (!(0, import_node_fs38.existsSync)(file)) return null;
|
|
13745
|
+
const n = Number.parseInt((0, import_node_fs38.readFileSync)(file, "utf8").trim(), 10);
|
|
13746
|
+
return Number.isInteger(n) ? n : null;
|
|
13747
|
+
}
|
|
13748
|
+
function jobDir(root, id, legacy = false) {
|
|
13749
|
+
return (0, import_node_path38.join)(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
|
|
13750
|
+
}
|
|
13751
|
+
function resolveJobDir(root, id) {
|
|
13752
|
+
const modern = jobDir(root, id, false);
|
|
13753
|
+
if ((0, import_node_fs38.existsSync)(modern)) return modern;
|
|
13754
|
+
const legacy = jobDir(root, id, true);
|
|
13755
|
+
if ((0, import_node_fs38.existsSync)(legacy)) return legacy;
|
|
13756
|
+
return modern;
|
|
13757
|
+
}
|
|
13758
|
+
function listJobIdsIn(root, rel) {
|
|
13759
|
+
const dir = (0, import_node_path38.join)(root, rel);
|
|
13760
|
+
if (!(0, import_node_fs38.existsSync)(dir)) return [];
|
|
13761
|
+
return (0, import_node_fs38.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
|
|
13762
|
+
}
|
|
13763
|
+
function listRunningDetachedJobs(worktreePath) {
|
|
13764
|
+
const root = worktreePath.trim();
|
|
13765
|
+
if (!root) return [];
|
|
13766
|
+
const ids = /* @__PURE__ */ new Set([
|
|
13767
|
+
...listJobIdsIn(root, DETACHED_JOBS_DIR),
|
|
13768
|
+
...listJobIdsIn(root, LEGACY_DETACHED_JOBS_DIR)
|
|
13769
|
+
]);
|
|
13770
|
+
const running = [];
|
|
13771
|
+
for (const id of ids) {
|
|
13772
|
+
const dir = resolveJobDir(root, id);
|
|
13773
|
+
const pid = readIntFile((0, import_node_path38.join)(dir, "pid"));
|
|
13774
|
+
if (pid != null && jobAlive(pid)) running.push(id);
|
|
13775
|
+
}
|
|
13776
|
+
return running.sort();
|
|
13777
|
+
}
|
|
13778
|
+
function looksLikeDeferredDonePromise(text6) {
|
|
13779
|
+
const t = (text6 ?? "").trim();
|
|
13780
|
+
if (!t) return false;
|
|
13781
|
+
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);
|
|
13782
|
+
}
|
|
13783
|
+
function formatJobStillRunningContinuePrompt(jobIds) {
|
|
13784
|
+
const ids = jobIds.join(", ");
|
|
13785
|
+
return [
|
|
13786
|
+
`Detached job still running: ${ids}.`,
|
|
13787
|
+
"Do not end this turn. Loop wait_for_job (same id) and present_artifact type=log with content=delta until stillRunning is false.",
|
|
13788
|
+
"Then report the result. Do not tell the user you will let them know later."
|
|
13789
|
+
].join(" ");
|
|
13790
|
+
}
|
|
13791
|
+
function formatDeferredDoneContinuePrompt() {
|
|
13792
|
+
return [
|
|
13793
|
+
"You ended the turn after promising to report later, but no detached job is running.",
|
|
13794
|
+
"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.",
|
|
13795
|
+
"Do not say you will let the user know later."
|
|
13796
|
+
].join(" ");
|
|
13797
|
+
}
|
|
13798
|
+
function turnWatchedDetachedJob(parts) {
|
|
13799
|
+
return parts.some((p) => {
|
|
13800
|
+
if (p.type !== "tool") return false;
|
|
13801
|
+
if (/wait_for_job$/i.test(p.name ?? "")) return true;
|
|
13802
|
+
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
13803
|
+
return /detached-job\.js\b/i.test(blob);
|
|
13804
|
+
});
|
|
13805
|
+
}
|
|
13806
|
+
function planJobContinue(opts) {
|
|
13807
|
+
if (opts.isOrchestrator) return { action: "none" };
|
|
13808
|
+
if (opts.agent === "brightsy") return { action: "none" };
|
|
13809
|
+
if (opts.queueLength > 0) return { action: "none" };
|
|
13810
|
+
if (opts.continueCount >= MAX_JOB_CONTINUES) return { action: "none" };
|
|
13811
|
+
const farewell = looksLikeDeferredDonePromise(opts.chatText);
|
|
13812
|
+
if (opts.runningJobIds.length > 0 && (farewell || opts.watchedJob)) {
|
|
13813
|
+
return {
|
|
13814
|
+
action: "wait",
|
|
13815
|
+
jobIds: opts.runningJobIds,
|
|
13816
|
+
prompt: formatJobStillRunningContinuePrompt(opts.runningJobIds)
|
|
13817
|
+
};
|
|
13818
|
+
}
|
|
13819
|
+
if (looksLikeDeferredDonePromise(opts.chatText) && !opts.alreadyNudged) {
|
|
13820
|
+
return { action: "nudge", prompt: formatDeferredDoneContinuePrompt() };
|
|
13821
|
+
}
|
|
13822
|
+
return { action: "none" };
|
|
13823
|
+
}
|
|
13824
|
+
function tailProgress(logFile, maxLines = 12) {
|
|
13825
|
+
if (!(0, import_node_fs38.existsSync)(logFile)) return "(no log yet)";
|
|
13826
|
+
const lines = (0, import_node_fs38.readFileSync)(logFile, "utf8").split("\n");
|
|
13827
|
+
return lines.slice(-maxLines).join("\n");
|
|
13828
|
+
}
|
|
13829
|
+
function readLogLines(file) {
|
|
13830
|
+
if (!(0, import_node_fs38.existsSync)(file)) return [];
|
|
13831
|
+
const lines = (0, import_node_fs38.readFileSync)(file, "utf8").split("\n");
|
|
13832
|
+
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
13833
|
+
return lines;
|
|
13834
|
+
}
|
|
13835
|
+
function takeDelta(logFile, cursorFile) {
|
|
13836
|
+
const lines = readLogLines(logFile);
|
|
13837
|
+
const cursor = readIntFile(cursorFile) ?? 0;
|
|
13838
|
+
const start = Math.min(Math.max(0, cursor), lines.length);
|
|
13839
|
+
return { delta: lines.slice(start).join("\n"), nextCursor: lines.length };
|
|
13840
|
+
}
|
|
13841
|
+
function snapshotJob(dir) {
|
|
13842
|
+
const pid = readIntFile((0, import_node_path38.join)(dir, "pid"));
|
|
13843
|
+
const running = pid != null && jobAlive(pid);
|
|
13844
|
+
return {
|
|
13845
|
+
pid,
|
|
13846
|
+
running,
|
|
13847
|
+
exitCode: readIntFile((0, import_node_path38.join)(dir, "exit")),
|
|
13848
|
+
log: (0, import_node_path38.join)(dir, "log"),
|
|
13849
|
+
cursor: (0, import_node_path38.join)(dir, "present.cursor"),
|
|
13850
|
+
progress: tailProgress((0, import_node_path38.join)(dir, "log"))
|
|
13851
|
+
};
|
|
13852
|
+
}
|
|
13853
|
+
function toResult(id, snap, extra) {
|
|
13854
|
+
const failed = extra?.failed === true || !snap.running && snap.exitCode != null && snap.exitCode !== 0;
|
|
13855
|
+
const ok = !snap.running && snap.exitCode === 0;
|
|
13856
|
+
const stillRunning = snap.running && !ok;
|
|
13857
|
+
const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
|
|
13858
|
+
try {
|
|
13859
|
+
(0, import_node_fs38.mkdirSync)((0, import_node_path38.dirname)(snap.cursor), { recursive: true });
|
|
13860
|
+
(0, import_node_fs38.writeFileSync)(snap.cursor, `${nextCursor}
|
|
13861
|
+
`);
|
|
13862
|
+
} catch {
|
|
13863
|
+
}
|
|
13864
|
+
const status = ok ? "ok" : failed && !stillRunning ? "failed" : stillRunning ? "running" : "idle";
|
|
13865
|
+
return {
|
|
13866
|
+
stillRunning,
|
|
13867
|
+
ok,
|
|
13868
|
+
failed: Boolean(failed && !stillRunning && !ok),
|
|
13869
|
+
status,
|
|
13870
|
+
id,
|
|
13871
|
+
pid: snap.pid,
|
|
13872
|
+
exitCode: snap.exitCode ?? void 0,
|
|
13873
|
+
delta,
|
|
13874
|
+
progress: extra?.progress ?? snap.progress,
|
|
13875
|
+
hint: stillRunning ? MCP_WAIT_JOB_STILL_RUNNING_HINT : void 0
|
|
13876
|
+
};
|
|
13877
|
+
}
|
|
13878
|
+
async function sleepMs(ms) {
|
|
13879
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
13880
|
+
}
|
|
13881
|
+
async function waitForDetachedJob(cwd, id, opts) {
|
|
13882
|
+
const jobId = sanitizeDetachedJobId(id);
|
|
13883
|
+
const root = cwd.trim() || process.cwd();
|
|
13884
|
+
const dir = resolveJobDir(root, jobId);
|
|
13885
|
+
const timeoutMs = mcpWaitForJobTimeoutMs(opts?.timeoutMs);
|
|
13886
|
+
const sleep = opts?.sleep ?? sleepMs;
|
|
13887
|
+
if (!(0, import_node_fs38.existsSync)(dir)) {
|
|
13888
|
+
return {
|
|
13889
|
+
stillRunning: false,
|
|
13890
|
+
ok: false,
|
|
13891
|
+
failed: true,
|
|
13892
|
+
status: "failed",
|
|
13893
|
+
id: jobId,
|
|
13894
|
+
delta: "",
|
|
13895
|
+
progress: "No detached job. Start one first.",
|
|
13896
|
+
hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
|
|
13897
|
+
};
|
|
13898
|
+
}
|
|
13899
|
+
const deadline = Date.now() + timeoutMs;
|
|
13900
|
+
let snap = snapshotJob(dir);
|
|
13901
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
13902
|
+
if (!snap.running && snap.pid == null && snap.progress === "(no log yet)") {
|
|
13903
|
+
return toResult(jobId, snap, {
|
|
13904
|
+
failed: true,
|
|
13905
|
+
progress: "No detached job. Start one first."
|
|
13906
|
+
});
|
|
13907
|
+
}
|
|
13908
|
+
while (Date.now() < deadline) {
|
|
13909
|
+
snap = snapshotJob(dir);
|
|
13910
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
13911
|
+
if (!snap.running) return toResult(jobId, snap, { failed: true });
|
|
13912
|
+
await sleep(Math.min(2e3, Math.max(50, deadline - Date.now())));
|
|
13913
|
+
}
|
|
13914
|
+
snap = snapshotJob(dir);
|
|
13915
|
+
return toResult(jobId, snap);
|
|
13916
|
+
}
|
|
13917
|
+
var import_node_fs38, import_node_path38, MAX_JOB_CONTINUES, MCP_WAIT_JOB_STILL_RUNNING_HINT, JOB_ID_RE;
|
|
13918
|
+
var init_wait_for_job = __esm({
|
|
13919
|
+
"src/mcp/wait-for-job.ts"() {
|
|
13920
|
+
"use strict";
|
|
13921
|
+
import_node_fs38 = require("fs");
|
|
13922
|
+
import_node_path38 = require("path");
|
|
13923
|
+
init_workspace_scratch();
|
|
13924
|
+
init_wait_for_turn();
|
|
13925
|
+
MAX_JOB_CONTINUES = 8;
|
|
13926
|
+
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.";
|
|
13927
|
+
JOB_ID_RE = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
13928
|
+
}
|
|
13929
|
+
});
|
|
13930
|
+
|
|
13689
13931
|
// src/detect/detect.ts
|
|
13690
13932
|
async function requireAgent(agent, opts) {
|
|
13691
13933
|
ensureAgentPath();
|
|
@@ -14770,7 +15012,7 @@ function reuseLiveThread(input, repoPath, match) {
|
|
|
14770
15012
|
}
|
|
14771
15013
|
async function createThread(input, _onSetupLine) {
|
|
14772
15014
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
14773
|
-
if (!(0,
|
|
15015
|
+
if (!(0, import_node_fs39.existsSync)(repoPath)) {
|
|
14774
15016
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
14775
15017
|
}
|
|
14776
15018
|
const reused = reuseLiveThread(input, repoPath, {
|
|
@@ -14941,11 +15183,11 @@ async function createThread(input, _onSetupLine) {
|
|
|
14941
15183
|
await ensureWorkspace(repoPath);
|
|
14942
15184
|
return readThread(thread.id) ?? thread;
|
|
14943
15185
|
}
|
|
14944
|
-
var
|
|
15186
|
+
var import_node_fs39;
|
|
14945
15187
|
var init_create = __esm({
|
|
14946
15188
|
"src/threads/create.ts"() {
|
|
14947
15189
|
"use strict";
|
|
14948
|
-
|
|
15190
|
+
import_node_fs39 = require("fs");
|
|
14949
15191
|
init_detect();
|
|
14950
15192
|
init_worktree();
|
|
14951
15193
|
init_home_board();
|
|
@@ -15048,20 +15290,20 @@ function writeTurnLive(threadId, progress) {
|
|
|
15048
15290
|
const path = threadLivePath(threadId);
|
|
15049
15291
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
15050
15292
|
try {
|
|
15051
|
-
(0,
|
|
15052
|
-
(0,
|
|
15293
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
|
|
15294
|
+
(0, import_node_fs40.renameSync)(tmp, path);
|
|
15053
15295
|
} catch {
|
|
15054
15296
|
try {
|
|
15055
|
-
(0,
|
|
15297
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
15056
15298
|
} catch {
|
|
15057
15299
|
}
|
|
15058
15300
|
}
|
|
15059
15301
|
}
|
|
15060
15302
|
function readTurnLive(threadId) {
|
|
15061
15303
|
const path = threadLivePath(threadId);
|
|
15062
|
-
if (!(0,
|
|
15304
|
+
if (!(0, import_node_fs40.existsSync)(path)) return null;
|
|
15063
15305
|
try {
|
|
15064
|
-
const raw = JSON.parse((0,
|
|
15306
|
+
const raw = JSON.parse((0, import_node_fs40.readFileSync)(path, "utf8"));
|
|
15065
15307
|
if (!raw || typeof raw.summary !== "string") return null;
|
|
15066
15308
|
return raw;
|
|
15067
15309
|
} catch {
|
|
@@ -15073,17 +15315,17 @@ function clearTurnLive(threadId) {
|
|
|
15073
15315
|
if (buf?.timer) clearTimeout(buf.timer);
|
|
15074
15316
|
buffers.delete(threadId);
|
|
15075
15317
|
const path = threadLivePath(threadId);
|
|
15076
|
-
if (!(0,
|
|
15318
|
+
if (!(0, import_node_fs40.existsSync)(path)) return;
|
|
15077
15319
|
try {
|
|
15078
|
-
(0,
|
|
15320
|
+
(0, import_node_fs40.unlinkSync)(path);
|
|
15079
15321
|
} catch {
|
|
15080
15322
|
}
|
|
15081
15323
|
}
|
|
15082
|
-
var
|
|
15324
|
+
var import_node_fs40, buffers, FLUSH_MS, MAX_PARTS;
|
|
15083
15325
|
var init_turn_live = __esm({
|
|
15084
15326
|
"src/store/turn-live.ts"() {
|
|
15085
15327
|
"use strict";
|
|
15086
|
-
|
|
15328
|
+
import_node_fs40 = require("fs");
|
|
15087
15329
|
init_message_parts();
|
|
15088
15330
|
init_paths();
|
|
15089
15331
|
buffers = /* @__PURE__ */ new Map();
|
|
@@ -15300,7 +15542,7 @@ var init_quota_failover = __esm({
|
|
|
15300
15542
|
// src/threads/adopt.ts
|
|
15301
15543
|
function thisModuleFile() {
|
|
15302
15544
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
15303
|
-
return cjsFile || process.argv[1] || (0,
|
|
15545
|
+
return cjsFile || process.argv[1] || (0, import_node_path39.join)(process.cwd(), "package.json");
|
|
15304
15546
|
}
|
|
15305
15547
|
function openReadonlySqlite(file) {
|
|
15306
15548
|
const req = (0, import_node_module4.createRequire)(thisModuleFile());
|
|
@@ -15318,21 +15560,21 @@ function mapAgentType(raw) {
|
|
|
15318
15560
|
return null;
|
|
15319
15561
|
}
|
|
15320
15562
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
15321
|
-
if (!workspacePath || !(0,
|
|
15563
|
+
if (!workspacePath || !(0, import_node_fs41.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
15322
15564
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
15323
15565
|
let best = null;
|
|
15324
15566
|
let hashes;
|
|
15325
15567
|
try {
|
|
15326
|
-
hashes = (0,
|
|
15568
|
+
hashes = (0, import_node_fs41.readdirSync)(CURSOR_SDK_STORE);
|
|
15327
15569
|
} catch {
|
|
15328
15570
|
return null;
|
|
15329
15571
|
}
|
|
15330
15572
|
for (const hash of hashes) {
|
|
15331
|
-
const agentsFile = (0,
|
|
15332
|
-
if (!(0,
|
|
15573
|
+
const agentsFile = (0, import_node_path39.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
15574
|
+
if (!(0, import_node_fs41.existsSync)(agentsFile)) continue;
|
|
15333
15575
|
let text6;
|
|
15334
15576
|
try {
|
|
15335
|
-
text6 = (0,
|
|
15577
|
+
text6 = (0, import_node_fs41.readFileSync)(agentsFile, "utf8");
|
|
15336
15578
|
} catch {
|
|
15337
15579
|
continue;
|
|
15338
15580
|
}
|
|
@@ -15356,7 +15598,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
15356
15598
|
return best?.agentId ?? null;
|
|
15357
15599
|
}
|
|
15358
15600
|
async function adoptThread(input) {
|
|
15359
|
-
if (!(0,
|
|
15601
|
+
if (!(0, import_node_fs41.existsSync)(input.worktreePath)) {
|
|
15360
15602
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
15361
15603
|
}
|
|
15362
15604
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -15382,18 +15624,18 @@ async function adoptThread(input) {
|
|
|
15382
15624
|
return thread;
|
|
15383
15625
|
}
|
|
15384
15626
|
function listConductorWorkspaces() {
|
|
15385
|
-
if (!(0,
|
|
15627
|
+
if (!(0, import_node_fs41.existsSync)(CONDUCTOR_DB)) {
|
|
15386
15628
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
15387
15629
|
}
|
|
15388
|
-
const tmp = (0,
|
|
15389
|
-
const snapshot = (0,
|
|
15630
|
+
const tmp = (0, import_node_fs41.mkdtempSync)((0, import_node_path39.join)((0, import_node_os13.tmpdir)(), "sideboard-conductor-"));
|
|
15631
|
+
const snapshot = (0, import_node_path39.join)(tmp, "conductor.db");
|
|
15390
15632
|
try {
|
|
15391
|
-
(0,
|
|
15633
|
+
(0, import_node_fs41.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
15392
15634
|
for (const suffix of ["-wal", "-shm"]) {
|
|
15393
15635
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
15394
|
-
if ((0,
|
|
15636
|
+
if ((0, import_node_fs41.existsSync)(src)) {
|
|
15395
15637
|
try {
|
|
15396
|
-
(0,
|
|
15638
|
+
(0, import_node_fs41.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
15397
15639
|
} catch {
|
|
15398
15640
|
}
|
|
15399
15641
|
}
|
|
@@ -15469,22 +15711,22 @@ function listConductorWorkspaces() {
|
|
|
15469
15711
|
db.close();
|
|
15470
15712
|
}
|
|
15471
15713
|
} finally {
|
|
15472
|
-
(0,
|
|
15714
|
+
(0, import_node_fs41.rmSync)(tmp, { recursive: true, force: true });
|
|
15473
15715
|
}
|
|
15474
15716
|
}
|
|
15475
15717
|
function importConductorWorkspace(workspaceId) {
|
|
15476
|
-
if (!(0,
|
|
15718
|
+
if (!(0, import_node_fs41.existsSync)(CONDUCTOR_DB)) {
|
|
15477
15719
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
15478
15720
|
}
|
|
15479
|
-
const tmp = (0,
|
|
15480
|
-
const snapshot = (0,
|
|
15721
|
+
const tmp = (0, import_node_fs41.mkdtempSync)((0, import_node_path39.join)((0, import_node_os13.tmpdir)(), "sideboard-conductor-"));
|
|
15722
|
+
const snapshot = (0, import_node_path39.join)(tmp, "conductor.db");
|
|
15481
15723
|
try {
|
|
15482
|
-
(0,
|
|
15724
|
+
(0, import_node_fs41.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
15483
15725
|
for (const suffix of ["-wal", "-shm"]) {
|
|
15484
15726
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
15485
|
-
if ((0,
|
|
15727
|
+
if ((0, import_node_fs41.existsSync)(src)) {
|
|
15486
15728
|
try {
|
|
15487
|
-
(0,
|
|
15729
|
+
(0, import_node_fs41.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
15488
15730
|
} catch {
|
|
15489
15731
|
}
|
|
15490
15732
|
}
|
|
@@ -15502,7 +15744,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
15502
15744
|
).get(workspaceId);
|
|
15503
15745
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
15504
15746
|
const worktreePath = String(row.workspacePath);
|
|
15505
|
-
if (!(0,
|
|
15747
|
+
if (!(0, import_node_fs41.existsSync)(worktreePath)) {
|
|
15506
15748
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
15507
15749
|
}
|
|
15508
15750
|
let sessionId = null;
|
|
@@ -15565,31 +15807,31 @@ function importConductorWorkspace(workspaceId) {
|
|
|
15565
15807
|
db.close();
|
|
15566
15808
|
}
|
|
15567
15809
|
} finally {
|
|
15568
|
-
(0,
|
|
15810
|
+
(0, import_node_fs41.rmSync)(tmp, { recursive: true, force: true });
|
|
15569
15811
|
}
|
|
15570
15812
|
}
|
|
15571
15813
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
15572
15814
|
return importConductorWorkspace(workspaceId);
|
|
15573
15815
|
}
|
|
15574
|
-
var import_node_child_process3,
|
|
15816
|
+
var import_node_child_process3, import_node_fs41, import_node_os13, import_node_path39, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
|
|
15575
15817
|
var init_adopt = __esm({
|
|
15576
15818
|
"src/threads/adopt.ts"() {
|
|
15577
15819
|
"use strict";
|
|
15578
15820
|
import_node_child_process3 = require("child_process");
|
|
15579
|
-
|
|
15821
|
+
import_node_fs41 = require("fs");
|
|
15580
15822
|
import_node_os13 = require("os");
|
|
15581
|
-
|
|
15823
|
+
import_node_path39 = require("path");
|
|
15582
15824
|
import_node_module4 = require("module");
|
|
15583
15825
|
init_worktree();
|
|
15584
15826
|
init_thread_store();
|
|
15585
|
-
CONDUCTOR_APP_SUPPORT = (0,
|
|
15827
|
+
CONDUCTOR_APP_SUPPORT = (0, import_node_path39.join)(
|
|
15586
15828
|
process.env.HOME ?? "",
|
|
15587
15829
|
"Library",
|
|
15588
15830
|
"Application Support",
|
|
15589
15831
|
"com.conductor.app"
|
|
15590
15832
|
);
|
|
15591
|
-
CONDUCTOR_DB = (0,
|
|
15592
|
-
CURSOR_SDK_STORE = (0,
|
|
15833
|
+
CONDUCTOR_DB = (0, import_node_path39.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
15834
|
+
CURSOR_SDK_STORE = (0, import_node_path39.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
15593
15835
|
}
|
|
15594
15836
|
});
|
|
15595
15837
|
|
|
@@ -15656,7 +15898,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
15656
15898
|
let createdWorktree = false;
|
|
15657
15899
|
const trees = await listWorktrees(repoPath);
|
|
15658
15900
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
15659
|
-
if (checkedOut?.path && (0,
|
|
15901
|
+
if (checkedOut?.path && (0, import_node_fs42.existsSync)(checkedOut.path)) {
|
|
15660
15902
|
if (input.reuseExistingWorktree !== false) {
|
|
15661
15903
|
worktreePath = checkedOut.path;
|
|
15662
15904
|
} else {
|
|
@@ -15798,7 +16040,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
15798
16040
|
async function createPrStack(input, onSetupLine) {
|
|
15799
16041
|
await requireAgent(input.agent);
|
|
15800
16042
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
15801
|
-
if (!(0,
|
|
16043
|
+
if (!(0, import_node_fs42.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
15802
16044
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
15803
16045
|
const status = await detectGhStack(repoPath);
|
|
15804
16046
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -15865,7 +16107,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
15865
16107
|
}
|
|
15866
16108
|
}
|
|
15867
16109
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
15868
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
16110
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs42.existsSync)(bootstrap.worktreePath)) {
|
|
15869
16111
|
try {
|
|
15870
16112
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
15871
16113
|
deleteBranch: bootstrap.branchName
|
|
@@ -15875,11 +16117,11 @@ async function createPrStack(input, onSetupLine) {
|
|
|
15875
16117
|
}
|
|
15876
16118
|
return { stack, threads, createdThreadIds };
|
|
15877
16119
|
}
|
|
15878
|
-
var
|
|
16120
|
+
var import_node_fs42;
|
|
15879
16121
|
var init_stack_layers = __esm({
|
|
15880
16122
|
"src/threads/stack-layers.ts"() {
|
|
15881
16123
|
"use strict";
|
|
15882
|
-
|
|
16124
|
+
import_node_fs42 = require("fs");
|
|
15883
16125
|
init_detect();
|
|
15884
16126
|
init_run();
|
|
15885
16127
|
init_stack();
|
|
@@ -15892,7 +16134,7 @@ var init_stack_layers = __esm({
|
|
|
15892
16134
|
|
|
15893
16135
|
// src/diff/diff.ts
|
|
15894
16136
|
async function inspectGitWorktree(worktreePath) {
|
|
15895
|
-
if (!worktreePath || !(0,
|
|
16137
|
+
if (!worktreePath || !(0, import_node_fs43.existsSync)(worktreePath)) return "missing_worktree";
|
|
15896
16138
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
15897
16139
|
reject: false
|
|
15898
16140
|
});
|
|
@@ -15900,7 +16142,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
15900
16142
|
return "ok";
|
|
15901
16143
|
}
|
|
15902
16144
|
async function initializeGitRepository(worktreePath) {
|
|
15903
|
-
if (!worktreePath || !(0,
|
|
16145
|
+
if (!worktreePath || !(0, import_node_fs43.existsSync)(worktreePath)) {
|
|
15904
16146
|
throw new Error("Worktree not found");
|
|
15905
16147
|
}
|
|
15906
16148
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -16034,11 +16276,11 @@ new file mode 100644
|
|
|
16034
16276
|
};
|
|
16035
16277
|
}
|
|
16036
16278
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
16037
|
-
const abs = (0,
|
|
16279
|
+
const abs = (0, import_node_path40.join)(worktreePath, path);
|
|
16038
16280
|
try {
|
|
16039
|
-
const st = (0,
|
|
16281
|
+
const st = (0, import_node_fs43.statSync)(abs);
|
|
16040
16282
|
if (st.isFile() && st.size > maxHunk) {
|
|
16041
|
-
const buf = (0,
|
|
16283
|
+
const buf = (0, import_node_fs43.readFileSync)(abs).subarray(0, maxHunk);
|
|
16042
16284
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
16043
16285
|
}
|
|
16044
16286
|
} catch {
|
|
@@ -16527,8 +16769,8 @@ function isImageRelativePath(relativePath) {
|
|
|
16527
16769
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
16528
16770
|
assertSafeRelativePath(relativePath);
|
|
16529
16771
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
16530
|
-
const abs = (0,
|
|
16531
|
-
const st = (0,
|
|
16772
|
+
const abs = (0, import_node_path40.join)(worktreePath, relativePath);
|
|
16773
|
+
const st = (0, import_node_fs43.statSync)(abs);
|
|
16532
16774
|
if (!st.isFile()) {
|
|
16533
16775
|
throw new Error(`Not a file: ${relativePath}`);
|
|
16534
16776
|
}
|
|
@@ -16537,7 +16779,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
16537
16779
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
16538
16780
|
);
|
|
16539
16781
|
}
|
|
16540
|
-
const buf = (0,
|
|
16782
|
+
const buf = (0, import_node_fs43.readFileSync)(abs);
|
|
16541
16783
|
return {
|
|
16542
16784
|
path: relativePath,
|
|
16543
16785
|
contentBase64: buf.toString("base64"),
|
|
@@ -16547,12 +16789,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
16547
16789
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
16548
16790
|
assertSafeRelativePath(relativePath);
|
|
16549
16791
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
16550
|
-
const abs = (0,
|
|
16551
|
-
const st = (0,
|
|
16792
|
+
const abs = (0, import_node_path40.join)(worktreePath, relativePath);
|
|
16793
|
+
const st = (0, import_node_fs43.statSync)(abs);
|
|
16552
16794
|
if (!st.isFile()) {
|
|
16553
16795
|
throw new Error(`Not a file: ${relativePath}`);
|
|
16554
16796
|
}
|
|
16555
|
-
const buf = (0,
|
|
16797
|
+
const buf = (0, import_node_fs43.readFileSync)(abs);
|
|
16556
16798
|
if (isImageRelativePath(relativePath)) {
|
|
16557
16799
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
16558
16800
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -16595,9 +16837,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
16595
16837
|
}
|
|
16596
16838
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
16597
16839
|
assertSafeRelativePath(relativePath);
|
|
16598
|
-
const abs = (0,
|
|
16599
|
-
(0,
|
|
16600
|
-
(0,
|
|
16840
|
+
const abs = (0, import_node_path40.join)(worktreePath, relativePath);
|
|
16841
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(abs), { recursive: true });
|
|
16842
|
+
(0, import_node_fs43.writeFileSync)(abs, content, "utf8");
|
|
16601
16843
|
return { path: relativePath };
|
|
16602
16844
|
}
|
|
16603
16845
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -16614,12 +16856,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
|
16614
16856
|
truncated: full.files.length > maxFiles
|
|
16615
16857
|
};
|
|
16616
16858
|
}
|
|
16617
|
-
var
|
|
16859
|
+
var import_node_fs43, import_node_path40, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
|
|
16618
16860
|
var init_diff = __esm({
|
|
16619
16861
|
"src/diff/diff.ts"() {
|
|
16620
16862
|
"use strict";
|
|
16621
|
-
|
|
16622
|
-
|
|
16863
|
+
import_node_fs43 = require("fs");
|
|
16864
|
+
import_node_path40 = require("path");
|
|
16623
16865
|
init_run();
|
|
16624
16866
|
init_worktree();
|
|
16625
16867
|
mergeBaseCache = /* @__PURE__ */ new Map();
|
|
@@ -16768,7 +17010,7 @@ var init_long_running = __esm({
|
|
|
16768
17010
|
|
|
16769
17011
|
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.
|
|
16770
17012
|
|
|
16771
|
-
Do **not** ask the human to check back. Detach, then **wait** in 45s slices (same idea as
|
|
17013
|
+
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
|
|
16772
17014
|
|
|
16773
17015
|
## Tool
|
|
16774
17016
|
|
|
@@ -16778,7 +17020,8 @@ Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" st
|
|
|
16778
17020
|
# Start (exits in ~1s; job survives this turn)
|
|
16779
17021
|
node <detached-job.js> start <id> -- <command> [args...]
|
|
16780
17022
|
|
|
16781
|
-
# Wait \u2014
|
|
17023
|
+
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
17024
|
+
# Shell fallback:
|
|
16782
17025
|
node <detached-job.js> wait <id>
|
|
16783
17026
|
|
|
16784
17027
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
@@ -16791,7 +17034,7 @@ node <detached-job.js> status <id>
|
|
|
16791
17034
|
|
|
16792
17035
|
Wait JSON:
|
|
16793
17036
|
|
|
16794
|
-
- \`stillRunning: true\` \u2192
|
|
17037
|
+
- \`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.
|
|
16795
17038
|
- \`ok: true\` \u2192 exit 0 \u2192 continue the rest of the task.
|
|
16796
17039
|
- \`failed: true\` \u2192 exit 1 \u2192 read \`progress\`, fix, start **once**.
|
|
16797
17040
|
|
|
@@ -16821,7 +17064,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
16821
17064
|
|
|
16822
17065
|
1. \`start\` once. If JSON says \`already-running\`, do not start again.
|
|
16823
17066
|
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.
|
|
16824
|
-
3. Loop \`
|
|
17067
|
+
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
16825
17068
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
16826
17069
|
|
|
16827
17070
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
|
|
@@ -16887,7 +17130,7 @@ function parseFrontmatter(content) {
|
|
|
16887
17130
|
}
|
|
16888
17131
|
function readSkill(skillMd, source) {
|
|
16889
17132
|
try {
|
|
16890
|
-
const content = (0,
|
|
17133
|
+
const content = (0, import_node_fs44.readFileSync)(skillMd, "utf8");
|
|
16891
17134
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
16892
17135
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
16893
17136
|
const name = fmName || dirName;
|
|
@@ -16906,19 +17149,19 @@ function readSkill(skillMd, source) {
|
|
|
16906
17149
|
}
|
|
16907
17150
|
}
|
|
16908
17151
|
function scanSkillsDir(dir, source, out) {
|
|
16909
|
-
if (!(0,
|
|
17152
|
+
if (!(0, import_node_fs44.existsSync)(dir)) return;
|
|
16910
17153
|
let entries;
|
|
16911
17154
|
try {
|
|
16912
|
-
entries = (0,
|
|
17155
|
+
entries = (0, import_node_fs44.readdirSync)(dir);
|
|
16913
17156
|
} catch {
|
|
16914
17157
|
return;
|
|
16915
17158
|
}
|
|
16916
17159
|
for (const entry of entries) {
|
|
16917
17160
|
if (entry.startsWith(".")) continue;
|
|
16918
|
-
const skillMd = (0,
|
|
16919
|
-
if (!(0,
|
|
17161
|
+
const skillMd = (0, import_node_path41.join)(dir, entry, "SKILL.md");
|
|
17162
|
+
if (!(0, import_node_fs44.existsSync)(skillMd)) continue;
|
|
16920
17163
|
try {
|
|
16921
|
-
if (!(0,
|
|
17164
|
+
if (!(0, import_node_fs44.statSync)(skillMd).isFile()) continue;
|
|
16922
17165
|
} catch {
|
|
16923
17166
|
continue;
|
|
16924
17167
|
}
|
|
@@ -16927,24 +17170,24 @@ function scanSkillsDir(dir, source, out) {
|
|
|
16927
17170
|
}
|
|
16928
17171
|
}
|
|
16929
17172
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
16930
|
-
if (!(0,
|
|
17173
|
+
if (!(0, import_node_fs44.existsSync)(pluginsRoot)) return;
|
|
16931
17174
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
16932
17175
|
if (depth > 7) return;
|
|
16933
17176
|
let entries;
|
|
16934
17177
|
try {
|
|
16935
|
-
entries = (0,
|
|
17178
|
+
entries = (0, import_node_fs44.readdirSync)(dir);
|
|
16936
17179
|
} catch {
|
|
16937
17180
|
return;
|
|
16938
17181
|
}
|
|
16939
17182
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
16940
|
-
const skill = readSkill((0,
|
|
17183
|
+
const skill = readSkill((0, import_node_path41.join)(dir, "SKILL.md"), "cli");
|
|
16941
17184
|
if (skill) out.push(skill);
|
|
16942
17185
|
}
|
|
16943
17186
|
for (const entry of entries) {
|
|
16944
17187
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
16945
|
-
const full = (0,
|
|
17188
|
+
const full = (0, import_node_path41.join)(dir, entry);
|
|
16946
17189
|
try {
|
|
16947
|
-
if (!(0,
|
|
17190
|
+
if (!(0, import_node_fs44.statSync)(full).isDirectory()) continue;
|
|
16948
17191
|
} catch {
|
|
16949
17192
|
continue;
|
|
16950
17193
|
}
|
|
@@ -16962,17 +17205,17 @@ function discoverSkills(worktreePath) {
|
|
|
16962
17205
|
const home = (0, import_node_os14.homedir)();
|
|
16963
17206
|
const collected = [];
|
|
16964
17207
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
16965
|
-
scanSkillsDir((0,
|
|
17208
|
+
scanSkillsDir((0, import_node_path41.join)(worktreePath, rel), "workspace", collected);
|
|
16966
17209
|
}
|
|
16967
17210
|
for (const abs of [
|
|
16968
|
-
(0,
|
|
16969
|
-
(0,
|
|
16970
|
-
(0,
|
|
16971
|
-
(0,
|
|
17211
|
+
(0, import_node_path41.join)(home, ".claude/skills"),
|
|
17212
|
+
(0, import_node_path41.join)(home, ".cursor/skills"),
|
|
17213
|
+
(0, import_node_path41.join)(home, ".sideboard/skills"),
|
|
17214
|
+
(0, import_node_path41.join)(home, ".brightsy/skills")
|
|
16972
17215
|
]) {
|
|
16973
17216
|
scanSkillsDir(abs, "user", collected);
|
|
16974
17217
|
}
|
|
16975
|
-
scanClaudePluginSkills((0,
|
|
17218
|
+
scanClaudePluginSkills((0, import_node_path41.join)(home, ".claude/plugins"), collected);
|
|
16976
17219
|
collected.push(...bundledSkills());
|
|
16977
17220
|
const rank = {
|
|
16978
17221
|
workspace: 0,
|
|
@@ -16998,7 +17241,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
16998
17241
|
\u2026(truncated)` : body;
|
|
16999
17242
|
}
|
|
17000
17243
|
}
|
|
17001
|
-
const raw = (0,
|
|
17244
|
+
const raw = (0, import_node_fs44.readFileSync)(skillPath, "utf8");
|
|
17002
17245
|
if (raw.startsWith("---")) {
|
|
17003
17246
|
const end = raw.indexOf("\n---", 3);
|
|
17004
17247
|
if (end >= 0) {
|
|
@@ -17012,13 +17255,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
17012
17255
|
|
|
17013
17256
|
\u2026(truncated)` : raw;
|
|
17014
17257
|
}
|
|
17015
|
-
var
|
|
17258
|
+
var import_node_fs44, import_node_os14, import_node_path41, BUNDLED_SKILL_PREFIX;
|
|
17016
17259
|
var init_discover = __esm({
|
|
17017
17260
|
"src/skills/discover.ts"() {
|
|
17018
17261
|
"use strict";
|
|
17019
|
-
|
|
17262
|
+
import_node_fs44 = require("fs");
|
|
17020
17263
|
import_node_os14 = require("os");
|
|
17021
|
-
|
|
17264
|
+
import_node_path41 = require("path");
|
|
17022
17265
|
init_long_running();
|
|
17023
17266
|
BUNDLED_SKILL_PREFIX = "bundled:";
|
|
17024
17267
|
}
|
|
@@ -17113,17 +17356,17 @@ var init_expand = __esm({
|
|
|
17113
17356
|
function packagedDetachedJobPath() {
|
|
17114
17357
|
const dir = packagedMcpDir();
|
|
17115
17358
|
if (!dir) return null;
|
|
17116
|
-
const script = (0,
|
|
17117
|
-
return (0,
|
|
17359
|
+
const script = (0, import_node_path42.join)(dir, "scripts", "detached-job.js");
|
|
17360
|
+
return (0, import_node_fs45.existsSync)(script) ? script : null;
|
|
17118
17361
|
}
|
|
17119
17362
|
function resolveDetachedJobScript() {
|
|
17120
17363
|
const packaged = packagedDetachedJobPath();
|
|
17121
17364
|
if (packaged) return packaged;
|
|
17122
|
-
let dir = (0,
|
|
17365
|
+
let dir = (0, import_node_path42.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
|
|
17123
17366
|
for (let i = 0; i < 8; i++) {
|
|
17124
|
-
const candidate = (0,
|
|
17125
|
-
if ((0,
|
|
17126
|
-
const parent = (0,
|
|
17367
|
+
const candidate = (0, import_node_path42.join)(dir, "scripts", "detached-job.js");
|
|
17368
|
+
if ((0, import_node_fs45.existsSync)(candidate)) return candidate;
|
|
17369
|
+
const parent = (0, import_node_path42.dirname)(dir);
|
|
17127
17370
|
if (parent === dir) break;
|
|
17128
17371
|
dir = parent;
|
|
17129
17372
|
}
|
|
@@ -17134,12 +17377,12 @@ function formatDetachedJobInvoke(scriptPath) {
|
|
|
17134
17377
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
17135
17378
|
return "node scripts/detached-job.js";
|
|
17136
17379
|
}
|
|
17137
|
-
var
|
|
17380
|
+
var import_node_fs45, import_node_path42, import_node_url3, import_meta3;
|
|
17138
17381
|
var init_detached_job_path = __esm({
|
|
17139
17382
|
"src/skills/detached-job-path.ts"() {
|
|
17140
17383
|
"use strict";
|
|
17141
|
-
|
|
17142
|
-
|
|
17384
|
+
import_node_fs45 = require("fs");
|
|
17385
|
+
import_node_path42 = require("path");
|
|
17143
17386
|
import_node_url3 = require("url");
|
|
17144
17387
|
init_packaged_runtime();
|
|
17145
17388
|
import_meta3 = {};
|
|
@@ -17338,14 +17581,15 @@ function formatLongRunningDirective(opts) {
|
|
|
17338
17581
|
`Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
|
|
17339
17582
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
17340
17583
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
17341
|
-
`- Loop
|
|
17584
|
+
`- 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.`,
|
|
17585
|
+
"- Do not end the turn with \u201CI\u2019ll let you know when it\u2019s done.\u201D Stay in the loop until stillRunning is false.",
|
|
17342
17586
|
"- ok \u2192 finish the task. failed \u2192 read the log, fix, start once.",
|
|
17343
17587
|
"State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
|
|
17344
17588
|
].join("\n");
|
|
17345
17589
|
}
|
|
17346
17590
|
function formatLongRunningReminder(opts) {
|
|
17347
17591
|
const invoke = formatDetachedJobInvoke(opts?.scriptPath);
|
|
17348
|
-
return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait, present_artifact type=log (delta). Do not
|
|
17592
|
+
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.`;
|
|
17349
17593
|
}
|
|
17350
17594
|
function formatArtifactDirective() {
|
|
17351
17595
|
return [
|
|
@@ -17375,12 +17619,12 @@ function formatArtifactDirective() {
|
|
|
17375
17619
|
function formatUiReminder() {
|
|
17376
17620
|
return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
|
|
17377
17621
|
}
|
|
17378
|
-
var
|
|
17622
|
+
var import_node_fs46, import_node_path43, GITHUB_TICKET_REF, KEYED_TICKET_REF;
|
|
17379
17623
|
var init_instructions = __esm({
|
|
17380
17624
|
"src/agents/instructions.ts"() {
|
|
17381
17625
|
"use strict";
|
|
17382
|
-
|
|
17383
|
-
|
|
17626
|
+
import_node_fs46 = require("fs");
|
|
17627
|
+
import_node_path43 = require("path");
|
|
17384
17628
|
init_git_auth_mode();
|
|
17385
17629
|
init_worktree_labels();
|
|
17386
17630
|
init_detached_job_path();
|
|
@@ -17552,40 +17796,40 @@ __export(plan_file_exports, {
|
|
|
17552
17796
|
writePlanFile: () => writePlanFile
|
|
17553
17797
|
});
|
|
17554
17798
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
17555
|
-
const gitignoreAbs = (0,
|
|
17556
|
-
if ((0,
|
|
17557
|
-
(0,
|
|
17558
|
-
(0,
|
|
17799
|
+
const gitignoreAbs = (0, import_node_path44.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
17800
|
+
if ((0, import_node_fs47.existsSync)(gitignoreAbs)) return;
|
|
17801
|
+
(0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(gitignoreAbs), { recursive: true });
|
|
17802
|
+
(0, import_node_fs47.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
17559
17803
|
}
|
|
17560
17804
|
function planFileAbs(worktreePath) {
|
|
17561
|
-
return (0,
|
|
17805
|
+
return (0, import_node_path44.join)(worktreePath, PLAN_FILE_REL);
|
|
17562
17806
|
}
|
|
17563
17807
|
function readTextIfPresent2(abs) {
|
|
17564
|
-
if (!(0,
|
|
17808
|
+
if (!(0, import_node_fs47.existsSync)(abs)) return null;
|
|
17565
17809
|
try {
|
|
17566
|
-
const content = (0,
|
|
17810
|
+
const content = (0, import_node_fs47.readFileSync)(abs, "utf8");
|
|
17567
17811
|
return content.trim() ? content : null;
|
|
17568
17812
|
} catch {
|
|
17569
17813
|
return null;
|
|
17570
17814
|
}
|
|
17571
17815
|
}
|
|
17572
17816
|
function readPlanFile(worktreePath) {
|
|
17573
|
-
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0,
|
|
17817
|
+
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path44.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path44.join)(worktreePath, LEGACY_PLAN_FILE_REL));
|
|
17574
17818
|
}
|
|
17575
17819
|
function writePlanFile(worktreePath, content) {
|
|
17576
17820
|
ensureAttachmentsGitignore(worktreePath);
|
|
17577
17821
|
const abs = planFileAbs(worktreePath);
|
|
17578
|
-
(0,
|
|
17822
|
+
(0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(abs), { recursive: true });
|
|
17579
17823
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
17580
|
-
(0,
|
|
17824
|
+
(0, import_node_fs47.writeFileSync)(abs, body, "utf8");
|
|
17581
17825
|
return PLAN_FILE_REL;
|
|
17582
17826
|
}
|
|
17583
|
-
var
|
|
17827
|
+
var import_node_fs47, import_node_path44;
|
|
17584
17828
|
var init_plan_file = __esm({
|
|
17585
17829
|
"src/plan/plan-file.ts"() {
|
|
17586
17830
|
"use strict";
|
|
17587
|
-
|
|
17588
|
-
|
|
17831
|
+
import_node_fs47 = require("fs");
|
|
17832
|
+
import_node_path44 = require("path");
|
|
17589
17833
|
init_workspace_scratch();
|
|
17590
17834
|
init_plan_present();
|
|
17591
17835
|
init_plan_present();
|
|
@@ -17645,7 +17889,7 @@ function setCaffeinateHoldHooks(next) {
|
|
|
17645
17889
|
hooks = next;
|
|
17646
17890
|
}
|
|
17647
17891
|
function caffeinateHoldPath() {
|
|
17648
|
-
return (0,
|
|
17892
|
+
return (0, import_node_path45.join)(appDataDir(), "caffeinate-hold.json");
|
|
17649
17893
|
}
|
|
17650
17894
|
function processAlive(pid) {
|
|
17651
17895
|
if (hooks.processAlive) return hooks.processAlive(pid);
|
|
@@ -17679,9 +17923,9 @@ function uniqueIds(ids) {
|
|
|
17679
17923
|
}
|
|
17680
17924
|
function readHold() {
|
|
17681
17925
|
const path = caffeinateHoldPath();
|
|
17682
|
-
if (!(0,
|
|
17926
|
+
if (!(0, import_node_fs48.existsSync)(path)) return null;
|
|
17683
17927
|
try {
|
|
17684
|
-
const parsed = JSON.parse((0,
|
|
17928
|
+
const parsed = JSON.parse((0, import_node_fs48.readFileSync)(path, "utf8"));
|
|
17685
17929
|
if (typeof parsed?.pid === "number" && parsed.pid > 0) {
|
|
17686
17930
|
return {
|
|
17687
17931
|
pid: parsed.pid,
|
|
@@ -17703,7 +17947,7 @@ function writeHold(pid, threadIds) {
|
|
|
17703
17947
|
}
|
|
17704
17948
|
function clearHold() {
|
|
17705
17949
|
try {
|
|
17706
|
-
(0,
|
|
17950
|
+
(0, import_node_fs48.unlinkSync)(caffeinateHoldPath());
|
|
17707
17951
|
} catch {
|
|
17708
17952
|
}
|
|
17709
17953
|
}
|
|
@@ -17788,13 +18032,13 @@ function releaseCaffeinateHoldForThread(threadId) {
|
|
|
17788
18032
|
}
|
|
17789
18033
|
return setCaffeinateHold(false, { threadId: id });
|
|
17790
18034
|
}
|
|
17791
|
-
var import_node_child_process4,
|
|
18035
|
+
var import_node_child_process4, import_node_fs48, import_node_path45, hooks;
|
|
17792
18036
|
var init_caffeinate_hold = __esm({
|
|
17793
18037
|
"src/store/caffeinate-hold.ts"() {
|
|
17794
18038
|
"use strict";
|
|
17795
18039
|
import_node_child_process4 = require("child_process");
|
|
17796
|
-
|
|
17797
|
-
|
|
18040
|
+
import_node_fs48 = require("fs");
|
|
18041
|
+
import_node_path45 = require("path");
|
|
17798
18042
|
init_paths();
|
|
17799
18043
|
init_private_file();
|
|
17800
18044
|
hooks = {};
|
|
@@ -17803,7 +18047,7 @@ var init_caffeinate_hold = __esm({
|
|
|
17803
18047
|
|
|
17804
18048
|
// src/store/schedules.ts
|
|
17805
18049
|
function schedulesPath() {
|
|
17806
|
-
return (0,
|
|
18050
|
+
return (0, import_node_path46.join)(appDataDir(), "schedules.json");
|
|
17807
18051
|
}
|
|
17808
18052
|
function parseDurationMs(every) {
|
|
17809
18053
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -17903,9 +18147,9 @@ function normalizeTask(raw) {
|
|
|
17903
18147
|
}
|
|
17904
18148
|
function readAll2() {
|
|
17905
18149
|
const path = schedulesPath();
|
|
17906
|
-
if (!(0,
|
|
18150
|
+
if (!(0, import_node_fs49.existsSync)(path)) return [];
|
|
17907
18151
|
try {
|
|
17908
|
-
const parsed = JSON.parse((0,
|
|
18152
|
+
const parsed = JSON.parse((0, import_node_fs49.readFileSync)(path, "utf8"));
|
|
17909
18153
|
if (!Array.isArray(parsed)) return [];
|
|
17910
18154
|
return parsed.map((row) => {
|
|
17911
18155
|
try {
|
|
@@ -18017,13 +18261,13 @@ function recordScheduleRun(id, result) {
|
|
|
18017
18261
|
writeAll2(rows);
|
|
18018
18262
|
return next;
|
|
18019
18263
|
}
|
|
18020
|
-
var import_node_crypto9,
|
|
18264
|
+
var import_node_crypto9, import_node_fs49, import_node_path46, import_croner, DURATION_RE, UNIT_MS;
|
|
18021
18265
|
var init_schedules = __esm({
|
|
18022
18266
|
"src/store/schedules.ts"() {
|
|
18023
18267
|
"use strict";
|
|
18024
18268
|
import_node_crypto9 = require("crypto");
|
|
18025
|
-
|
|
18026
|
-
|
|
18269
|
+
import_node_fs49 = require("fs");
|
|
18270
|
+
import_node_path46 = require("path");
|
|
18027
18271
|
import_croner = require("croner");
|
|
18028
18272
|
init_orchestrator_capable();
|
|
18029
18273
|
init_paths();
|
|
@@ -18165,13 +18409,13 @@ var init_schedule_runner = __esm({
|
|
|
18165
18409
|
|
|
18166
18410
|
// src/agents/cursor-store.ts
|
|
18167
18411
|
function cursorSdkStoreDir(threadId) {
|
|
18168
|
-
const root = (0,
|
|
18412
|
+
const root = (0, import_node_path47.join)(appDataDir(), CURSOR_SDK_STORE_DIR);
|
|
18169
18413
|
const id = sanitizeCursorStoreSegment(threadId);
|
|
18170
18414
|
if (!id) return root;
|
|
18171
|
-
return (0,
|
|
18415
|
+
return (0, import_node_path47.join)(root, "threads", id);
|
|
18172
18416
|
}
|
|
18173
18417
|
function cursorSdkRunsNdjsonPath(threadId) {
|
|
18174
|
-
return (0,
|
|
18418
|
+
return (0, import_node_path47.join)(cursorSdkStoreDir(threadId), "runs.ndjson");
|
|
18175
18419
|
}
|
|
18176
18420
|
function cursorSdkRunsNdjsonSearchPaths(threadId) {
|
|
18177
18421
|
const scoped = cursorSdkRunsNdjsonPath(threadId);
|
|
@@ -18184,11 +18428,11 @@ function cursorSdkRunsNdjsonSearchPaths(threadId) {
|
|
|
18184
18428
|
function sanitizeCursorStoreSegment(threadId) {
|
|
18185
18429
|
return (threadId ?? "").trim().replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
18186
18430
|
}
|
|
18187
|
-
var
|
|
18431
|
+
var import_node_path47, CURSOR_SDK_STORE_DIR;
|
|
18188
18432
|
var init_cursor_store = __esm({
|
|
18189
18433
|
"src/agents/cursor-store.ts"() {
|
|
18190
18434
|
"use strict";
|
|
18191
|
-
|
|
18435
|
+
import_node_path47 = require("path");
|
|
18192
18436
|
init_paths();
|
|
18193
18437
|
CURSOR_SDK_STORE_DIR = "cursor-sdk-store";
|
|
18194
18438
|
}
|
|
@@ -18210,9 +18454,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
18210
18454
|
return best;
|
|
18211
18455
|
}
|
|
18212
18456
|
function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
18213
|
-
if (!(0,
|
|
18457
|
+
if (!(0, import_node_fs50.existsSync)(runsPath)) return null;
|
|
18214
18458
|
try {
|
|
18215
|
-
const lines = (0,
|
|
18459
|
+
const lines = (0, import_node_fs50.readFileSync)(runsPath, "utf8").split("\n");
|
|
18216
18460
|
let best = null;
|
|
18217
18461
|
for (const line of lines) {
|
|
18218
18462
|
const trimmed = line.trim();
|
|
@@ -18238,11 +18482,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
|
18238
18482
|
return null;
|
|
18239
18483
|
}
|
|
18240
18484
|
}
|
|
18241
|
-
var
|
|
18485
|
+
var import_node_fs50;
|
|
18242
18486
|
var init_cursor_recover = __esm({
|
|
18243
18487
|
"src/agents/cursor-recover.ts"() {
|
|
18244
18488
|
"use strict";
|
|
18245
|
-
|
|
18489
|
+
import_node_fs50 = require("fs");
|
|
18246
18490
|
init_cursor_store();
|
|
18247
18491
|
}
|
|
18248
18492
|
});
|
|
@@ -18374,13 +18618,13 @@ async function startOrchestration(opts) {
|
|
|
18374
18618
|
}
|
|
18375
18619
|
return updated;
|
|
18376
18620
|
}
|
|
18377
|
-
var import_node_events,
|
|
18621
|
+
var import_node_events, import_node_fs51, LIVE_TURN_SPAWN_GRACE_MS, STALE_AGENT_PID_WAIT_MS, Orchestrator, singleton;
|
|
18378
18622
|
var init_orchestrator = __esm({
|
|
18379
18623
|
"src/orchestrator/orchestrator.ts"() {
|
|
18380
18624
|
"use strict";
|
|
18381
18625
|
import_node_events = require("events");
|
|
18382
18626
|
init_outbound_watch();
|
|
18383
|
-
|
|
18627
|
+
import_node_fs51 = require("fs");
|
|
18384
18628
|
init_error_detail();
|
|
18385
18629
|
init_run();
|
|
18386
18630
|
init_stale_lock();
|
|
@@ -18401,6 +18645,7 @@ var init_orchestrator = __esm({
|
|
|
18401
18645
|
init_thread_store();
|
|
18402
18646
|
init_desktop_host();
|
|
18403
18647
|
init_child_halt();
|
|
18648
|
+
init_wait_for_job();
|
|
18404
18649
|
init_create();
|
|
18405
18650
|
init_cowboy();
|
|
18406
18651
|
init_orchestrator_capable();
|
|
@@ -18465,6 +18710,9 @@ var init_orchestrator = __esm({
|
|
|
18465
18710
|
* finish or a user send so a later crash can recover again.
|
|
18466
18711
|
*/
|
|
18467
18712
|
crashContinued = /* @__PURE__ */ new Set();
|
|
18713
|
+
/** Auto-continues after a worktree turn ended while a detached job still runs. */
|
|
18714
|
+
jobContinueCount = /* @__PURE__ */ new Map();
|
|
18715
|
+
jobContinueNudged = /* @__PURE__ */ new Set();
|
|
18468
18716
|
maxConcurrent;
|
|
18469
18717
|
runningCount = 0;
|
|
18470
18718
|
constructor(opts) {
|
|
@@ -18548,7 +18796,7 @@ var init_orchestrator = __esm({
|
|
|
18548
18796
|
}
|
|
18549
18797
|
continue;
|
|
18550
18798
|
}
|
|
18551
|
-
if (!(0,
|
|
18799
|
+
if (!(0, import_node_fs51.existsSync)(thread.worktreePath)) {
|
|
18552
18800
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
18553
18801
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
18554
18802
|
continue;
|
|
@@ -18763,6 +19011,37 @@ var init_orchestrator = __esm({
|
|
|
18763
19011
|
this.emit({ type: "queue_changed", threadId, queue });
|
|
18764
19012
|
this.haltDrain.delete(threadId);
|
|
18765
19013
|
}
|
|
19014
|
+
/**
|
|
19015
|
+
* Worktree agent ended the turn after “I’ll let you know” (or left a
|
|
19016
|
+
* detached test/pack job running). Queue a continue so the chat does not
|
|
19017
|
+
* go idle with nobody watching the log.
|
|
19018
|
+
*/
|
|
19019
|
+
maybeEnqueueJobContinue(threadId, chatText, parts = []) {
|
|
19020
|
+
if (this.haltDrain.has(threadId)) return;
|
|
19021
|
+
const thread = readThread(threadId);
|
|
19022
|
+
if (!thread || thread.status === "archived") return;
|
|
19023
|
+
const runningJobIds = listRunningDetachedJobs(thread.worktreePath ?? "");
|
|
19024
|
+
const decision = planJobContinue({
|
|
19025
|
+
runningJobIds,
|
|
19026
|
+
chatText,
|
|
19027
|
+
queueLength: thread.queue.length,
|
|
19028
|
+
continueCount: this.jobContinueCount.get(threadId) ?? 0,
|
|
19029
|
+
alreadyNudged: this.jobContinueNudged.has(threadId),
|
|
19030
|
+
isOrchestrator: isOrchestratorThread(thread),
|
|
19031
|
+
agent: thread.agent,
|
|
19032
|
+
watchedJob: turnWatchedDetachedJob(parts)
|
|
19033
|
+
});
|
|
19034
|
+
if (decision.action === "none") {
|
|
19035
|
+
if (runningJobIds.length === 0) this.jobContinueCount.delete(threadId);
|
|
19036
|
+
return;
|
|
19037
|
+
}
|
|
19038
|
+
if (decision.action === "nudge") this.jobContinueNudged.add(threadId);
|
|
19039
|
+
else this.jobContinueCount.set(threadId, (this.jobContinueCount.get(threadId) ?? 0) + 1);
|
|
19040
|
+
const queue = [decision.prompt, ...thread.queue];
|
|
19041
|
+
updateThread(threadId, { queue });
|
|
19042
|
+
this.emit({ type: "queue_changed", threadId, queue });
|
|
19043
|
+
this.haltDrain.delete(threadId);
|
|
19044
|
+
}
|
|
18766
19045
|
getThreads(includeArchived = false) {
|
|
18767
19046
|
return listThreads({ includeArchived });
|
|
18768
19047
|
}
|
|
@@ -18862,6 +19141,8 @@ var init_orchestrator = __esm({
|
|
|
18862
19141
|
}
|
|
18863
19142
|
const queue = [...current.queue, prompt];
|
|
18864
19143
|
this.crashContinued.delete(thread.id);
|
|
19144
|
+
this.jobContinueCount.delete(thread.id);
|
|
19145
|
+
this.jobContinueNudged.delete(thread.id);
|
|
18865
19146
|
this.haltDrain.delete(thread.id);
|
|
18866
19147
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
|
|
18867
19148
|
shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
|
|
@@ -19376,6 +19657,7 @@ var init_orchestrator = __esm({
|
|
|
19376
19657
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
19377
19658
|
if (exitCode === 0) {
|
|
19378
19659
|
this.crashContinued.delete(threadId);
|
|
19660
|
+
this.maybeEnqueueJobContinue(threadId, chatText, parts);
|
|
19379
19661
|
} else {
|
|
19380
19662
|
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
19381
19663
|
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
@@ -20376,7 +20658,7 @@ var init_orchestrator = __esm({
|
|
|
20376
20658
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
20377
20659
|
return restored2;
|
|
20378
20660
|
}
|
|
20379
|
-
if (!(0,
|
|
20661
|
+
if (!(0, import_node_fs51.existsSync)(thread.worktreePath)) {
|
|
20380
20662
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
20381
20663
|
throw new Error(
|
|
20382
20664
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
@@ -20459,7 +20741,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
20459
20741
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
20460
20742
|
var import_zod6 = require("zod");
|
|
20461
20743
|
var import_node_crypto12 = require("crypto");
|
|
20462
|
-
var
|
|
20744
|
+
var import_node_path50 = require("path");
|
|
20463
20745
|
init_orchestrator();
|
|
20464
20746
|
init_worktree();
|
|
20465
20747
|
|
|
@@ -21308,30 +21590,8 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
21308
21590
|
|
|
21309
21591
|
// src/mcp/server.ts
|
|
21310
21592
|
init_profile();
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
21314
|
-
function mcpWaitForTurnTimeoutMs(requested) {
|
|
21315
|
-
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
21316
|
-
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
21317
|
-
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
21318
|
-
}
|
|
21319
|
-
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.";
|
|
21320
|
-
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.";
|
|
21321
|
-
function mcpWaitStillRunningHint(status) {
|
|
21322
|
-
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
21323
|
-
}
|
|
21324
|
-
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.";
|
|
21325
|
-
var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
21326
|
-
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.";
|
|
21327
|
-
function mcpWaitFinishedHint(status) {
|
|
21328
|
-
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
21329
|
-
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
21330
|
-
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
21331
|
-
return void 0;
|
|
21332
|
-
}
|
|
21333
|
-
|
|
21334
|
-
// src/mcp/server.ts
|
|
21593
|
+
init_wait_for_turn();
|
|
21594
|
+
init_wait_for_job();
|
|
21335
21595
|
init_message_parts();
|
|
21336
21596
|
init_turn_live();
|
|
21337
21597
|
|
|
@@ -22658,27 +22918,27 @@ init_gh_errors();
|
|
|
22658
22918
|
init_git_auth_mode();
|
|
22659
22919
|
|
|
22660
22920
|
// src/board/load-home-board.ts
|
|
22661
|
-
var
|
|
22662
|
-
var
|
|
22921
|
+
var import_node_fs53 = require("fs");
|
|
22922
|
+
var import_node_path49 = require("path");
|
|
22663
22923
|
init_worktree();
|
|
22664
22924
|
init_paths();
|
|
22665
22925
|
|
|
22666
22926
|
// src/board/board-pins.ts
|
|
22667
22927
|
var import_node_crypto11 = require("crypto");
|
|
22668
|
-
var
|
|
22669
|
-
var
|
|
22928
|
+
var import_node_fs52 = require("fs");
|
|
22929
|
+
var import_node_path48 = require("path");
|
|
22670
22930
|
init_paths();
|
|
22671
22931
|
init_home_board();
|
|
22672
22932
|
var FILE = "home-board-pins.json";
|
|
22673
22933
|
var VERSION = 1;
|
|
22674
22934
|
function pinsFile() {
|
|
22675
|
-
return (0,
|
|
22935
|
+
return (0, import_node_path48.join)(appDataDir(), FILE);
|
|
22676
22936
|
}
|
|
22677
22937
|
function readDisk() {
|
|
22678
22938
|
const path = pinsFile();
|
|
22679
|
-
if (!(0,
|
|
22939
|
+
if (!(0, import_node_fs52.existsSync)(path)) return [];
|
|
22680
22940
|
try {
|
|
22681
|
-
const raw = JSON.parse((0,
|
|
22941
|
+
const raw = JSON.parse((0, import_node_fs52.readFileSync)(path, "utf8"));
|
|
22682
22942
|
if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
|
|
22683
22943
|
return raw.items.filter((item) => item?.id && item.kind && item.ref);
|
|
22684
22944
|
} catch {
|
|
@@ -22686,8 +22946,8 @@ function readDisk() {
|
|
|
22686
22946
|
}
|
|
22687
22947
|
}
|
|
22688
22948
|
function writeDisk(items) {
|
|
22689
|
-
(0,
|
|
22690
|
-
(0,
|
|
22949
|
+
(0, import_node_fs52.mkdirSync)(appDataDir(), { recursive: true });
|
|
22950
|
+
(0, import_node_fs52.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
|
|
22691
22951
|
}
|
|
22692
22952
|
function listBoardPins() {
|
|
22693
22953
|
return readDisk();
|
|
@@ -22708,7 +22968,7 @@ function homeBoardWorkspaceKey(workspaces) {
|
|
|
22708
22968
|
return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
|
|
22709
22969
|
}
|
|
22710
22970
|
function cacheFile() {
|
|
22711
|
-
return (0,
|
|
22971
|
+
return (0, import_node_path49.join)(appDataDir(), "home-board-cache.json");
|
|
22712
22972
|
}
|
|
22713
22973
|
function emptyInputs() {
|
|
22714
22974
|
return {
|
|
@@ -22734,9 +22994,9 @@ function shouldCacheHomeBoardInputs(inputs) {
|
|
|
22734
22994
|
}
|
|
22735
22995
|
function readDiskCache() {
|
|
22736
22996
|
const path = cacheFile();
|
|
22737
|
-
if (!(0,
|
|
22997
|
+
if (!(0, import_node_fs53.existsSync)(path)) return null;
|
|
22738
22998
|
try {
|
|
22739
|
-
const raw = JSON.parse((0,
|
|
22999
|
+
const raw = JSON.parse((0, import_node_fs53.readFileSync)(path, "utf8"));
|
|
22740
23000
|
if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
|
|
22741
23001
|
return null;
|
|
22742
23002
|
}
|
|
@@ -22750,8 +23010,8 @@ function readDiskCache() {
|
|
|
22750
23010
|
}
|
|
22751
23011
|
function writeDiskCache(entry) {
|
|
22752
23012
|
try {
|
|
22753
|
-
(0,
|
|
22754
|
-
(0,
|
|
23013
|
+
(0, import_node_fs53.mkdirSync)(appDataDir(), { recursive: true });
|
|
23014
|
+
(0, import_node_fs53.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
|
|
22755
23015
|
} catch {
|
|
22756
23016
|
}
|
|
22757
23017
|
}
|
|
@@ -23050,7 +23310,7 @@ async function startMcpServer() {
|
|
|
23050
23310
|
async () => {
|
|
23051
23311
|
const threads = orch.getThreads(true);
|
|
23052
23312
|
const lines = threads.map((t) => {
|
|
23053
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
23313
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path50.basename)(t.repoPath) || t.repoPath;
|
|
23054
23314
|
const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
|
|
23055
23315
|
const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
|
|
23056
23316
|
const preview = lastMessagePreview(t.messages, 80);
|
|
@@ -23284,6 +23544,20 @@ async function startMcpServer() {
|
|
|
23284
23544
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
23285
23545
|
}
|
|
23286
23546
|
);
|
|
23547
|
+
server.tool(
|
|
23548
|
+
"wait_for_job",
|
|
23549
|
+
"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.",
|
|
23550
|
+
{
|
|
23551
|
+
id: import_zod6.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
23552
|
+
timeoutMs: import_zod6.z.number().optional()
|
|
23553
|
+
},
|
|
23554
|
+
async ({ id, timeoutMs }) => {
|
|
23555
|
+
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
23556
|
+
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
23557
|
+
});
|
|
23558
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
23559
|
+
}
|
|
23560
|
+
);
|
|
23287
23561
|
if (!worktreeProfile) {
|
|
23288
23562
|
registerSlackTools(server);
|
|
23289
23563
|
registerConnectedIssueVendorTools(server);
|