@sideboard-ai/core 0.1.167 → 0.1.168
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/{chunk-IHLRYIPY.js → chunk-NYMFNUT5.js} +28 -21
- package/dist/{chunk-YZUUFSBL.js → chunk-UVLPL2FJ.js} +28 -21
- package/dist/index.cjs +31 -24
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/mcp/run-stdio.cjs +31 -24
- package/dist/mcp/run-stdio.js +5 -5
- package/dist/{orchestrator-VIMO2DP2.js → orchestrator-IIME6KXQ.js} +1 -1
- package/dist/{orchestrator-GJN4LATM.js → orchestrator-XG3QCMOS.js} +1 -1
- package/package.json +1 -1
|
@@ -399,7 +399,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
399
399
|
await continueOnReply(threadId, prompt);
|
|
400
400
|
return;
|
|
401
401
|
}
|
|
402
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
402
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-XG3QCMOS.js");
|
|
403
403
|
await getOrchestrator2().send(threadId, prompt);
|
|
404
404
|
} catch {
|
|
405
405
|
}
|
|
@@ -2609,7 +2609,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
|
|
|
2609
2609
|
function formatDeferredDoneContinuePrompt() {
|
|
2610
2610
|
return [
|
|
2611
2611
|
"You ended the turn after promising to report later, but no detached job is running.",
|
|
2612
|
-
"If tests/pack/deploy still need to run: start once with detached-job.
|
|
2612
|
+
"If tests/pack/deploy still need to run: start once with detached-job.cjs, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
2613
2613
|
"Do not say you will let the user know later."
|
|
2614
2614
|
].join(" ");
|
|
2615
2615
|
}
|
|
@@ -2618,7 +2618,7 @@ function turnWatchedDetachedJob(parts) {
|
|
|
2618
2618
|
if (p.type !== "tool") return false;
|
|
2619
2619
|
if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
|
|
2620
2620
|
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
2621
|
-
return /detached-job\.js\b/i.test(blob);
|
|
2621
|
+
return /detached-job\.(?:js|cjs)\b/i.test(blob);
|
|
2622
2622
|
});
|
|
2623
2623
|
}
|
|
2624
2624
|
function planJobContinue(opts) {
|
|
@@ -2711,7 +2711,7 @@ async function waitForDetachedJob(cwd, id, opts) {
|
|
|
2711
2711
|
id: jobId,
|
|
2712
2712
|
delta: "",
|
|
2713
2713
|
progress: "No detached job. Start one first.",
|
|
2714
|
-
hint: "Start with detached-job.
|
|
2714
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
|
|
2715
2715
|
};
|
|
2716
2716
|
}
|
|
2717
2717
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -2770,7 +2770,7 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
2770
2770
|
id: jobId,
|
|
2771
2771
|
delta: "",
|
|
2772
2772
|
progress: "No detached job. Start one first.",
|
|
2773
|
-
hint: "Start with detached-job.
|
|
2773
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
|
|
2774
2774
|
stopped: false,
|
|
2775
2775
|
reason: "not-found"
|
|
2776
2776
|
};
|
|
@@ -5270,21 +5270,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
|
|
|
5270
5270
|
|
|
5271
5271
|
## Tool
|
|
5272
5272
|
|
|
5273
|
-
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.
|
|
5273
|
+
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.cjs\`, that is the same tool.
|
|
5274
5274
|
|
|
5275
5275
|
\`\`\`bash
|
|
5276
5276
|
# Start (exits in ~1s; job survives this turn)
|
|
5277
|
-
node <detached-job.
|
|
5277
|
+
node <detached-job.cjs> start <id> -- <command> [args...]
|
|
5278
5278
|
|
|
5279
5279
|
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
5280
5280
|
# Shell fallback:
|
|
5281
|
-
node <detached-job.
|
|
5281
|
+
node <detached-job.cjs> wait <id>
|
|
5282
5282
|
|
|
5283
5283
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
5284
|
-
node <detached-job.
|
|
5284
|
+
node <detached-job.cjs> wait <id> --until-done
|
|
5285
5285
|
|
|
5286
|
-
node <detached-job.
|
|
5287
|
-
node <detached-job.
|
|
5286
|
+
node <detached-job.cjs> stop <id> [--reason TEXT]
|
|
5287
|
+
node <detached-job.cjs> status <id>
|
|
5288
5288
|
\`\`\`
|
|
5289
5289
|
|
|
5290
5290
|
\`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
|
|
@@ -5315,7 +5315,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
|
|
|
5315
5315
|
Ad-hoc pid/log (legacy or another tool\u2019s files):
|
|
5316
5316
|
|
|
5317
5317
|
\`\`\`bash
|
|
5318
|
-
node <detached-job.
|
|
5318
|
+
node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
5319
5319
|
\`\`\`
|
|
5320
5320
|
|
|
5321
5321
|
## Agent loop
|
|
@@ -5324,7 +5324,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
5324
5324
|
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.
|
|
5325
5325
|
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
5326
5326
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
5327
|
-
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.
|
|
5327
|
+
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.cjs stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
|
|
5328
5328
|
|
|
5329
5329
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
|
|
5330
5330
|
|
|
@@ -5337,7 +5337,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
|
|
|
5337
5337
|
Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
|
|
5338
5338
|
|
|
5339
5339
|
\`\`\`bash
|
|
5340
|
-
node <detached-job.
|
|
5340
|
+
node <detached-job.cjs> start pr-checks -- gh pr checks --watch
|
|
5341
5341
|
\`\`\`
|
|
5342
5342
|
|
|
5343
5343
|
On miss, fix, commit, push, and watch again until the goal is met or you are blocked. Do not ask the human to poll. Do not merge unless asked.
|
|
@@ -5603,19 +5603,26 @@ import { join as join15 } from "path";
|
|
|
5603
5603
|
import { existsSync as existsSync15 } from "fs";
|
|
5604
5604
|
import { dirname as dirname5, join as join14 } from "path";
|
|
5605
5605
|
import { fileURLToPath } from "url";
|
|
5606
|
+
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
5607
|
+
function detachedJobScriptIn(dir) {
|
|
5608
|
+
for (const name of DETACHED_JOB_FILENAMES) {
|
|
5609
|
+
const script = join14(dir, "scripts", name);
|
|
5610
|
+
if (existsSync15(script)) return script;
|
|
5611
|
+
}
|
|
5612
|
+
return null;
|
|
5613
|
+
}
|
|
5606
5614
|
function packagedDetachedJobPath() {
|
|
5607
5615
|
const dir = packagedMcpDir();
|
|
5608
5616
|
if (!dir) return null;
|
|
5609
|
-
|
|
5610
|
-
return existsSync15(script) ? script : null;
|
|
5617
|
+
return detachedJobScriptIn(dir);
|
|
5611
5618
|
}
|
|
5612
5619
|
function resolveDetachedJobScript() {
|
|
5613
5620
|
const packaged = packagedDetachedJobPath();
|
|
5614
5621
|
if (packaged) return packaged;
|
|
5615
5622
|
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
5616
5623
|
for (let i = 0; i < 8; i++) {
|
|
5617
|
-
const candidate =
|
|
5618
|
-
if (
|
|
5624
|
+
const candidate = detachedJobScriptIn(dir);
|
|
5625
|
+
if (candidate) return candidate;
|
|
5619
5626
|
const parent = dirname5(dir);
|
|
5620
5627
|
if (parent === dir) break;
|
|
5621
5628
|
dir = parent;
|
|
@@ -5625,7 +5632,7 @@ function resolveDetachedJobScript() {
|
|
|
5625
5632
|
function formatDetachedJobInvoke(scriptPath) {
|
|
5626
5633
|
const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
|
|
5627
5634
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
5628
|
-
return "node scripts/detached-job.
|
|
5635
|
+
return "node scripts/detached-job.cjs";
|
|
5629
5636
|
}
|
|
5630
5637
|
|
|
5631
5638
|
// src/agents/instructions.ts
|
|
@@ -5819,7 +5826,7 @@ function formatLongRunningDirective(opts) {
|
|
|
5819
5826
|
return [
|
|
5820
5827
|
"Long-running jobs (mandatory when a command may run more than ~30s):",
|
|
5821
5828
|
"A Sideboard 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. Do not ask the human to poll.",
|
|
5822
|
-
`Helper (same tool as \`scripts/detached-job.
|
|
5829
|
+
`Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
|
|
5823
5830
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
5824
5831
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
5825
5832
|
`- 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.`,
|
|
@@ -6233,7 +6240,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6233
6240
|
${prompt}`;
|
|
6234
6241
|
}
|
|
6235
6242
|
async function defaultDeps() {
|
|
6236
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6243
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-XG3QCMOS.js");
|
|
6237
6244
|
const orch = getOrchestrator2();
|
|
6238
6245
|
return {
|
|
6239
6246
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -464,7 +464,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
464
464
|
await continueOnReply(threadId, prompt);
|
|
465
465
|
return;
|
|
466
466
|
}
|
|
467
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
467
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-IIME6KXQ.js");
|
|
468
468
|
await getOrchestrator2().send(threadId, prompt);
|
|
469
469
|
} catch {
|
|
470
470
|
}
|
|
@@ -2694,7 +2694,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
|
|
|
2694
2694
|
function formatDeferredDoneContinuePrompt() {
|
|
2695
2695
|
return [
|
|
2696
2696
|
"You ended the turn after promising to report later, but no detached job is running.",
|
|
2697
|
-
"If tests/pack/deploy still need to run: start once with detached-job.
|
|
2697
|
+
"If tests/pack/deploy still need to run: start once with detached-job.cjs, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
2698
2698
|
"Do not say you will let the user know later."
|
|
2699
2699
|
].join(" ");
|
|
2700
2700
|
}
|
|
@@ -2703,7 +2703,7 @@ function turnWatchedDetachedJob(parts) {
|
|
|
2703
2703
|
if (p.type !== "tool") return false;
|
|
2704
2704
|
if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
|
|
2705
2705
|
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
2706
|
-
return /detached-job\.js\b/i.test(blob);
|
|
2706
|
+
return /detached-job\.(?:js|cjs)\b/i.test(blob);
|
|
2707
2707
|
});
|
|
2708
2708
|
}
|
|
2709
2709
|
function planJobContinue(opts) {
|
|
@@ -2796,7 +2796,7 @@ async function waitForDetachedJob(cwd, id, opts) {
|
|
|
2796
2796
|
id: jobId,
|
|
2797
2797
|
delta: "",
|
|
2798
2798
|
progress: "No detached job. Start one first.",
|
|
2799
|
-
hint: "Start with detached-job.
|
|
2799
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
|
|
2800
2800
|
};
|
|
2801
2801
|
}
|
|
2802
2802
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -2855,7 +2855,7 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
2855
2855
|
id: jobId,
|
|
2856
2856
|
delta: "",
|
|
2857
2857
|
progress: "No detached job. Start one first.",
|
|
2858
|
-
hint: "Start with detached-job.
|
|
2858
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
|
|
2859
2859
|
stopped: false,
|
|
2860
2860
|
reason: "not-found"
|
|
2861
2861
|
};
|
|
@@ -5422,21 +5422,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
|
|
|
5422
5422
|
|
|
5423
5423
|
## Tool
|
|
5424
5424
|
|
|
5425
|
-
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.
|
|
5425
|
+
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.cjs\`, that is the same tool.
|
|
5426
5426
|
|
|
5427
5427
|
\`\`\`bash
|
|
5428
5428
|
# Start (exits in ~1s; job survives this turn)
|
|
5429
|
-
node <detached-job.
|
|
5429
|
+
node <detached-job.cjs> start <id> -- <command> [args...]
|
|
5430
5430
|
|
|
5431
5431
|
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
5432
5432
|
# Shell fallback:
|
|
5433
|
-
node <detached-job.
|
|
5433
|
+
node <detached-job.cjs> wait <id>
|
|
5434
5434
|
|
|
5435
5435
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
5436
|
-
node <detached-job.
|
|
5436
|
+
node <detached-job.cjs> wait <id> --until-done
|
|
5437
5437
|
|
|
5438
|
-
node <detached-job.
|
|
5439
|
-
node <detached-job.
|
|
5438
|
+
node <detached-job.cjs> stop <id> [--reason TEXT]
|
|
5439
|
+
node <detached-job.cjs> status <id>
|
|
5440
5440
|
\`\`\`
|
|
5441
5441
|
|
|
5442
5442
|
\`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
|
|
@@ -5467,7 +5467,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
|
|
|
5467
5467
|
Ad-hoc pid/log (legacy or another tool\u2019s files):
|
|
5468
5468
|
|
|
5469
5469
|
\`\`\`bash
|
|
5470
|
-
node <detached-job.
|
|
5470
|
+
node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
5471
5471
|
\`\`\`
|
|
5472
5472
|
|
|
5473
5473
|
## Agent loop
|
|
@@ -5476,7 +5476,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
5476
5476
|
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.
|
|
5477
5477
|
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
5478
5478
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
5479
|
-
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.
|
|
5479
|
+
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.cjs stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
|
|
5480
5480
|
|
|
5481
5481
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
|
|
5482
5482
|
|
|
@@ -5489,7 +5489,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
|
|
|
5489
5489
|
Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
|
|
5490
5490
|
|
|
5491
5491
|
\`\`\`bash
|
|
5492
|
-
node <detached-job.
|
|
5492
|
+
node <detached-job.cjs> start pr-checks -- gh pr checks --watch
|
|
5493
5493
|
\`\`\`
|
|
5494
5494
|
|
|
5495
5495
|
On miss, fix, commit, push, and watch again until the goal is met or you are blocked. Do not ask the human to poll. Do not merge unless asked.
|
|
@@ -5755,19 +5755,26 @@ import { join as join15 } from "path";
|
|
|
5755
5755
|
import { existsSync as existsSync15 } from "fs";
|
|
5756
5756
|
import { dirname as dirname5, join as join14 } from "path";
|
|
5757
5757
|
import { fileURLToPath } from "url";
|
|
5758
|
+
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
5759
|
+
function detachedJobScriptIn(dir) {
|
|
5760
|
+
for (const name of DETACHED_JOB_FILENAMES) {
|
|
5761
|
+
const script = join14(dir, "scripts", name);
|
|
5762
|
+
if (existsSync15(script)) return script;
|
|
5763
|
+
}
|
|
5764
|
+
return null;
|
|
5765
|
+
}
|
|
5758
5766
|
function packagedDetachedJobPath() {
|
|
5759
5767
|
const dir = packagedMcpDir();
|
|
5760
5768
|
if (!dir) return null;
|
|
5761
|
-
|
|
5762
|
-
return existsSync15(script) ? script : null;
|
|
5769
|
+
return detachedJobScriptIn(dir);
|
|
5763
5770
|
}
|
|
5764
5771
|
function resolveDetachedJobScript() {
|
|
5765
5772
|
const packaged = packagedDetachedJobPath();
|
|
5766
5773
|
if (packaged) return packaged;
|
|
5767
5774
|
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
5768
5775
|
for (let i = 0; i < 8; i++) {
|
|
5769
|
-
const candidate =
|
|
5770
|
-
if (
|
|
5776
|
+
const candidate = detachedJobScriptIn(dir);
|
|
5777
|
+
if (candidate) return candidate;
|
|
5771
5778
|
const parent = dirname5(dir);
|
|
5772
5779
|
if (parent === dir) break;
|
|
5773
5780
|
dir = parent;
|
|
@@ -5777,7 +5784,7 @@ function resolveDetachedJobScript() {
|
|
|
5777
5784
|
function formatDetachedJobInvoke(scriptPath) {
|
|
5778
5785
|
const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
|
|
5779
5786
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
5780
|
-
return "node scripts/detached-job.
|
|
5787
|
+
return "node scripts/detached-job.cjs";
|
|
5781
5788
|
}
|
|
5782
5789
|
|
|
5783
5790
|
// src/agents/instructions.ts
|
|
@@ -5993,7 +6000,7 @@ function formatLongRunningDirective(opts) {
|
|
|
5993
6000
|
return [
|
|
5994
6001
|
"Long-running jobs (mandatory when a command may run more than ~30s):",
|
|
5995
6002
|
"A Sideboard 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. Do not ask the human to poll.",
|
|
5996
|
-
`Helper (same tool as \`scripts/detached-job.
|
|
6003
|
+
`Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
|
|
5997
6004
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
5998
6005
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
5999
6006
|
`- 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.`,
|
|
@@ -6643,7 +6650,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6643
6650
|
${prompt}`;
|
|
6644
6651
|
}
|
|
6645
6652
|
async function defaultDeps() {
|
|
6646
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6653
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-IIME6KXQ.js");
|
|
6647
6654
|
const orch = getOrchestrator2();
|
|
6648
6655
|
return {
|
|
6649
6656
|
findThread: (id) => findThreadByRef(id),
|
package/dist/index.cjs
CHANGED
|
@@ -15508,7 +15508,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
|
|
|
15508
15508
|
function formatDeferredDoneContinuePrompt() {
|
|
15509
15509
|
return [
|
|
15510
15510
|
"You ended the turn after promising to report later, but no detached job is running.",
|
|
15511
|
-
"If tests/pack/deploy still need to run: start once with detached-job.
|
|
15511
|
+
"If tests/pack/deploy still need to run: start once with detached-job.cjs, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
15512
15512
|
"Do not say you will let the user know later."
|
|
15513
15513
|
].join(" ");
|
|
15514
15514
|
}
|
|
@@ -15517,7 +15517,7 @@ function turnWatchedDetachedJob(parts) {
|
|
|
15517
15517
|
if (p.type !== "tool") return false;
|
|
15518
15518
|
if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
|
|
15519
15519
|
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
15520
|
-
return /detached-job\.js\b/i.test(blob);
|
|
15520
|
+
return /detached-job\.(?:js|cjs)\b/i.test(blob);
|
|
15521
15521
|
});
|
|
15522
15522
|
}
|
|
15523
15523
|
function planJobContinue(opts) {
|
|
@@ -15610,7 +15610,7 @@ async function waitForDetachedJob(cwd, id, opts) {
|
|
|
15610
15610
|
id: jobId,
|
|
15611
15611
|
delta: "",
|
|
15612
15612
|
progress: "No detached job. Start one first.",
|
|
15613
|
-
hint: "Start with detached-job.
|
|
15613
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
|
|
15614
15614
|
};
|
|
15615
15615
|
}
|
|
15616
15616
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -15669,7 +15669,7 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
15669
15669
|
id: jobId,
|
|
15670
15670
|
delta: "",
|
|
15671
15671
|
progress: "No detached job. Start one first.",
|
|
15672
|
-
hint: "Start with detached-job.
|
|
15672
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
|
|
15673
15673
|
stopped: false,
|
|
15674
15674
|
reason: "not-found"
|
|
15675
15675
|
};
|
|
@@ -18905,21 +18905,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
|
|
|
18905
18905
|
|
|
18906
18906
|
## Tool
|
|
18907
18907
|
|
|
18908
|
-
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.
|
|
18908
|
+
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.cjs\`, that is the same tool.
|
|
18909
18909
|
|
|
18910
18910
|
\`\`\`bash
|
|
18911
18911
|
# Start (exits in ~1s; job survives this turn)
|
|
18912
|
-
node <detached-job.
|
|
18912
|
+
node <detached-job.cjs> start <id> -- <command> [args...]
|
|
18913
18913
|
|
|
18914
18914
|
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
18915
18915
|
# Shell fallback:
|
|
18916
|
-
node <detached-job.
|
|
18916
|
+
node <detached-job.cjs> wait <id>
|
|
18917
18917
|
|
|
18918
18918
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
18919
|
-
node <detached-job.
|
|
18919
|
+
node <detached-job.cjs> wait <id> --until-done
|
|
18920
18920
|
|
|
18921
|
-
node <detached-job.
|
|
18922
|
-
node <detached-job.
|
|
18921
|
+
node <detached-job.cjs> stop <id> [--reason TEXT]
|
|
18922
|
+
node <detached-job.cjs> status <id>
|
|
18923
18923
|
\`\`\`
|
|
18924
18924
|
|
|
18925
18925
|
\`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
|
|
@@ -18950,7 +18950,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
|
|
|
18950
18950
|
Ad-hoc pid/log (legacy or another tool\u2019s files):
|
|
18951
18951
|
|
|
18952
18952
|
\`\`\`bash
|
|
18953
|
-
node <detached-job.
|
|
18953
|
+
node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
18954
18954
|
\`\`\`
|
|
18955
18955
|
|
|
18956
18956
|
## Agent loop
|
|
@@ -18959,7 +18959,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
18959
18959
|
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.
|
|
18960
18960
|
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
18961
18961
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
18962
|
-
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.
|
|
18962
|
+
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.cjs stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
|
|
18963
18963
|
|
|
18964
18964
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
|
|
18965
18965
|
|
|
@@ -18972,7 +18972,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
|
|
|
18972
18972
|
Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
|
|
18973
18973
|
|
|
18974
18974
|
\`\`\`bash
|
|
18975
|
-
node <detached-job.
|
|
18975
|
+
node <detached-job.cjs> start pr-checks -- gh pr checks --watch
|
|
18976
18976
|
\`\`\`
|
|
18977
18977
|
|
|
18978
18978
|
On miss, fix, commit, push, and watch again until the goal is met or you are blocked. Do not ask the human to poll. Do not merge unless asked.
|
|
@@ -19251,19 +19251,25 @@ var init_expand = __esm({
|
|
|
19251
19251
|
});
|
|
19252
19252
|
|
|
19253
19253
|
// src/skills/detached-job-path.ts
|
|
19254
|
+
function detachedJobScriptIn(dir) {
|
|
19255
|
+
for (const name of DETACHED_JOB_FILENAMES) {
|
|
19256
|
+
const script = (0, import_node_path44.join)(dir, "scripts", name);
|
|
19257
|
+
if ((0, import_node_fs47.existsSync)(script)) return script;
|
|
19258
|
+
}
|
|
19259
|
+
return null;
|
|
19260
|
+
}
|
|
19254
19261
|
function packagedDetachedJobPath() {
|
|
19255
19262
|
const dir = packagedMcpDir();
|
|
19256
19263
|
if (!dir) return null;
|
|
19257
|
-
|
|
19258
|
-
return (0, import_node_fs47.existsSync)(script) ? script : null;
|
|
19264
|
+
return detachedJobScriptIn(dir);
|
|
19259
19265
|
}
|
|
19260
19266
|
function resolveDetachedJobScript() {
|
|
19261
19267
|
const packaged = packagedDetachedJobPath();
|
|
19262
19268
|
if (packaged) return packaged;
|
|
19263
19269
|
let dir = (0, import_node_path44.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
|
|
19264
19270
|
for (let i = 0; i < 8; i++) {
|
|
19265
|
-
const candidate = (
|
|
19266
|
-
if (
|
|
19271
|
+
const candidate = detachedJobScriptIn(dir);
|
|
19272
|
+
if (candidate) return candidate;
|
|
19267
19273
|
const parent = (0, import_node_path44.dirname)(dir);
|
|
19268
19274
|
if (parent === dir) break;
|
|
19269
19275
|
dir = parent;
|
|
@@ -19273,9 +19279,9 @@ function resolveDetachedJobScript() {
|
|
|
19273
19279
|
function formatDetachedJobInvoke(scriptPath) {
|
|
19274
19280
|
const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
|
|
19275
19281
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
19276
|
-
return "node scripts/detached-job.
|
|
19282
|
+
return "node scripts/detached-job.cjs";
|
|
19277
19283
|
}
|
|
19278
|
-
var import_node_fs47, import_node_path44, import_node_url3, import_meta3;
|
|
19284
|
+
var import_node_fs47, import_node_path44, import_node_url3, import_meta3, DETACHED_JOB_FILENAMES;
|
|
19279
19285
|
var init_detached_job_path = __esm({
|
|
19280
19286
|
"src/skills/detached-job-path.ts"() {
|
|
19281
19287
|
"use strict";
|
|
@@ -19284,6 +19290,7 @@ var init_detached_job_path = __esm({
|
|
|
19284
19290
|
import_node_url3 = require("url");
|
|
19285
19291
|
init_packaged_runtime();
|
|
19286
19292
|
import_meta3 = {};
|
|
19293
|
+
DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
19287
19294
|
}
|
|
19288
19295
|
});
|
|
19289
19296
|
|
|
@@ -19498,7 +19505,7 @@ function formatLongRunningDirective(opts) {
|
|
|
19498
19505
|
return [
|
|
19499
19506
|
"Long-running jobs (mandatory when a command may run more than ~30s):",
|
|
19500
19507
|
"A Sideboard 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. Do not ask the human to poll.",
|
|
19501
|
-
`Helper (same tool as \`scripts/detached-job.
|
|
19508
|
+
`Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
|
|
19502
19509
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
19503
19510
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
19504
19511
|
`- 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.`,
|
|
@@ -27204,9 +27211,9 @@ async function startMcpServer() {
|
|
|
27204
27211
|
);
|
|
27205
27212
|
server.tool(
|
|
27206
27213
|
"wait_for_job",
|
|
27207
|
-
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.
|
|
27214
|
+
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. 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. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
27208
27215
|
{
|
|
27209
|
-
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
27216
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
27210
27217
|
timeoutMs: import_zod8.z.number().optional()
|
|
27211
27218
|
},
|
|
27212
27219
|
async ({ id, timeoutMs }) => {
|
|
@@ -27218,9 +27225,9 @@ async function startMcpServer() {
|
|
|
27218
27225
|
);
|
|
27219
27226
|
server.tool(
|
|
27220
27227
|
"stop_job",
|
|
27221
|
-
"Stop a detached job you started with detached-job.
|
|
27228
|
+
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. After stop, present_artifact type=log with status=failed and the last delta, then decide the next step.",
|
|
27222
27229
|
{
|
|
27223
|
-
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
27230
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
27224
27231
|
reason: import_zod8.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
|
|
27225
27232
|
},
|
|
27226
27233
|
async ({ id, reason }) => {
|
package/dist/index.d.cts
CHANGED
|
@@ -3500,14 +3500,14 @@ declare const BUNDLED_SKILL_PREFIX = "bundled:";
|
|
|
3500
3500
|
declare function discoverSkills(worktreePath: string): SkillInfo[];
|
|
3501
3501
|
declare function readSkillBody(skillPath: string, maxChars?: number): string;
|
|
3502
3502
|
|
|
3503
|
-
/** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.
|
|
3503
|
+
/** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.cjs`). */
|
|
3504
3504
|
declare function packagedDetachedJobPath(): string | null;
|
|
3505
3505
|
/**
|
|
3506
3506
|
* Absolute path to the detach helper agents should exec.
|
|
3507
3507
|
* Packaged app first, then walk up from this module to the repo `scripts/` copy.
|
|
3508
3508
|
*/
|
|
3509
3509
|
declare function resolveDetachedJobScript(): string | null;
|
|
3510
|
-
/** Shell invocation agents can copy (`node "/abs/path/detached-job.
|
|
3510
|
+
/** Shell invocation agents can copy (`node "/abs/path/detached-job.cjs"`). */
|
|
3511
3511
|
declare function formatDetachedJobInvoke(scriptPath?: string | null): string;
|
|
3512
3512
|
|
|
3513
3513
|
/** Sideboard product skill — available in every worktree, not only this repo. */
|
package/dist/index.d.ts
CHANGED
|
@@ -3500,14 +3500,14 @@ declare const BUNDLED_SKILL_PREFIX = "bundled:";
|
|
|
3500
3500
|
declare function discoverSkills(worktreePath: string): SkillInfo[];
|
|
3501
3501
|
declare function readSkillBody(skillPath: string, maxChars?: number): string;
|
|
3502
3502
|
|
|
3503
|
-
/** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.
|
|
3503
|
+
/** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.cjs`). */
|
|
3504
3504
|
declare function packagedDetachedJobPath(): string | null;
|
|
3505
3505
|
/**
|
|
3506
3506
|
* Absolute path to the detach helper agents should exec.
|
|
3507
3507
|
* Packaged app first, then walk up from this module to the repo `scripts/` copy.
|
|
3508
3508
|
*/
|
|
3509
3509
|
declare function resolveDetachedJobScript(): string | null;
|
|
3510
|
-
/** Shell invocation agents can copy (`node "/abs/path/detached-job.
|
|
3510
|
+
/** Shell invocation agents can copy (`node "/abs/path/detached-job.cjs"`). */
|
|
3511
3511
|
declare function formatDetachedJobInvoke(scriptPath?: string | null): string;
|
|
3512
3512
|
|
|
3513
3513
|
/** Sideboard product skill — available in every worktree, not only this repo. */
|
package/dist/index.js
CHANGED
|
@@ -237,7 +237,7 @@ import {
|
|
|
237
237
|
worktreeCleanupSettings,
|
|
238
238
|
wrapReviewSkillMarkdown,
|
|
239
239
|
writeWorktreeFile
|
|
240
|
-
} from "./chunk-
|
|
240
|
+
} from "./chunk-UVLPL2FJ.js";
|
|
241
241
|
import {
|
|
242
242
|
LEGACY_PLAN_FILE_REL,
|
|
243
243
|
PLAN_FILE_NAME,
|
|
@@ -4512,9 +4512,9 @@ async function startMcpServer() {
|
|
|
4512
4512
|
);
|
|
4513
4513
|
server.tool(
|
|
4514
4514
|
"wait_for_job",
|
|
4515
|
-
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.
|
|
4515
|
+
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. 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. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
4516
4516
|
{
|
|
4517
|
-
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
4517
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
4518
4518
|
timeoutMs: z8.number().optional()
|
|
4519
4519
|
},
|
|
4520
4520
|
async ({ id, timeoutMs }) => {
|
|
@@ -4526,9 +4526,9 @@ async function startMcpServer() {
|
|
|
4526
4526
|
);
|
|
4527
4527
|
server.tool(
|
|
4528
4528
|
"stop_job",
|
|
4529
|
-
"Stop a detached job you started with detached-job.
|
|
4529
|
+
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. After stop, present_artifact type=log with status=failed and the last delta, then decide the next step.",
|
|
4530
4530
|
{
|
|
4531
|
-
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
4531
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
4532
4532
|
reason: z8.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
|
|
4533
4533
|
},
|
|
4534
4534
|
async ({ id, reason }) => {
|
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -13860,7 +13860,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
|
|
|
13860
13860
|
function formatDeferredDoneContinuePrompt() {
|
|
13861
13861
|
return [
|
|
13862
13862
|
"You ended the turn after promising to report later, but no detached job is running.",
|
|
13863
|
-
"If tests/pack/deploy still need to run: start once with detached-job.
|
|
13863
|
+
"If tests/pack/deploy still need to run: start once with detached-job.cjs, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
13864
13864
|
"Do not say you will let the user know later."
|
|
13865
13865
|
].join(" ");
|
|
13866
13866
|
}
|
|
@@ -13869,7 +13869,7 @@ function turnWatchedDetachedJob(parts) {
|
|
|
13869
13869
|
if (p.type !== "tool") return false;
|
|
13870
13870
|
if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
|
|
13871
13871
|
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
13872
|
-
return /detached-job\.js\b/i.test(blob);
|
|
13872
|
+
return /detached-job\.(?:js|cjs)\b/i.test(blob);
|
|
13873
13873
|
});
|
|
13874
13874
|
}
|
|
13875
13875
|
function planJobContinue(opts) {
|
|
@@ -13962,7 +13962,7 @@ async function waitForDetachedJob(cwd, id, opts) {
|
|
|
13962
13962
|
id: jobId,
|
|
13963
13963
|
delta: "",
|
|
13964
13964
|
progress: "No detached job. Start one first.",
|
|
13965
|
-
hint: "Start with detached-job.
|
|
13965
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
|
|
13966
13966
|
};
|
|
13967
13967
|
}
|
|
13968
13968
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -14021,7 +14021,7 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
14021
14021
|
id: jobId,
|
|
14022
14022
|
delta: "",
|
|
14023
14023
|
progress: "No detached job. Start one first.",
|
|
14024
|
-
hint: "Start with detached-job.
|
|
14024
|
+
hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
|
|
14025
14025
|
stopped: false,
|
|
14026
14026
|
reason: "not-found"
|
|
14027
14027
|
};
|
|
@@ -17190,21 +17190,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
|
|
|
17190
17190
|
|
|
17191
17191
|
## Tool
|
|
17192
17192
|
|
|
17193
|
-
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.
|
|
17193
|
+
Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.cjs\`, that is the same tool.
|
|
17194
17194
|
|
|
17195
17195
|
\`\`\`bash
|
|
17196
17196
|
# Start (exits in ~1s; job survives this turn)
|
|
17197
|
-
node <detached-job.
|
|
17197
|
+
node <detached-job.cjs> start <id> -- <command> [args...]
|
|
17198
17198
|
|
|
17199
17199
|
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
17200
17200
|
# Shell fallback:
|
|
17201
|
-
node <detached-job.
|
|
17201
|
+
node <detached-job.cjs> wait <id>
|
|
17202
17202
|
|
|
17203
17203
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
17204
|
-
node <detached-job.
|
|
17204
|
+
node <detached-job.cjs> wait <id> --until-done
|
|
17205
17205
|
|
|
17206
|
-
node <detached-job.
|
|
17207
|
-
node <detached-job.
|
|
17206
|
+
node <detached-job.cjs> stop <id> [--reason TEXT]
|
|
17207
|
+
node <detached-job.cjs> status <id>
|
|
17208
17208
|
\`\`\`
|
|
17209
17209
|
|
|
17210
17210
|
\`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
|
|
@@ -17235,7 +17235,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
|
|
|
17235
17235
|
Ad-hoc pid/log (legacy or another tool\u2019s files):
|
|
17236
17236
|
|
|
17237
17237
|
\`\`\`bash
|
|
17238
|
-
node <detached-job.
|
|
17238
|
+
node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
17239
17239
|
\`\`\`
|
|
17240
17240
|
|
|
17241
17241
|
## Agent loop
|
|
@@ -17244,7 +17244,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
17244
17244
|
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.
|
|
17245
17245
|
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
17246
17246
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
17247
|
-
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.
|
|
17247
|
+
5. **Stop** when the job is hanging, buffering forever, watching the wrong thing, or you already have the answer. MCP \`stop_job\` (same id, optional \`reason\`) or \`detached-job.cjs stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
|
|
17248
17248
|
|
|
17249
17249
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
|
|
17250
17250
|
|
|
@@ -17257,7 +17257,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
|
|
|
17257
17257
|
Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
|
|
17258
17258
|
|
|
17259
17259
|
\`\`\`bash
|
|
17260
|
-
node <detached-job.
|
|
17260
|
+
node <detached-job.cjs> start pr-checks -- gh pr checks --watch
|
|
17261
17261
|
\`\`\`
|
|
17262
17262
|
|
|
17263
17263
|
On miss, fix, commit, push, and watch again until the goal is met or you are blocked. Do not ask the human to poll. Do not merge unless asked.
|
|
@@ -17536,19 +17536,25 @@ var init_expand = __esm({
|
|
|
17536
17536
|
});
|
|
17537
17537
|
|
|
17538
17538
|
// src/skills/detached-job-path.ts
|
|
17539
|
+
function detachedJobScriptIn(dir) {
|
|
17540
|
+
for (const name of DETACHED_JOB_FILENAMES) {
|
|
17541
|
+
const script = (0, import_node_path42.join)(dir, "scripts", name);
|
|
17542
|
+
if ((0, import_node_fs45.existsSync)(script)) return script;
|
|
17543
|
+
}
|
|
17544
|
+
return null;
|
|
17545
|
+
}
|
|
17539
17546
|
function packagedDetachedJobPath() {
|
|
17540
17547
|
const dir = packagedMcpDir();
|
|
17541
17548
|
if (!dir) return null;
|
|
17542
|
-
|
|
17543
|
-
return (0, import_node_fs45.existsSync)(script) ? script : null;
|
|
17549
|
+
return detachedJobScriptIn(dir);
|
|
17544
17550
|
}
|
|
17545
17551
|
function resolveDetachedJobScript() {
|
|
17546
17552
|
const packaged = packagedDetachedJobPath();
|
|
17547
17553
|
if (packaged) return packaged;
|
|
17548
17554
|
let dir = (0, import_node_path42.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
|
|
17549
17555
|
for (let i = 0; i < 8; i++) {
|
|
17550
|
-
const candidate = (
|
|
17551
|
-
if (
|
|
17556
|
+
const candidate = detachedJobScriptIn(dir);
|
|
17557
|
+
if (candidate) return candidate;
|
|
17552
17558
|
const parent = (0, import_node_path42.dirname)(dir);
|
|
17553
17559
|
if (parent === dir) break;
|
|
17554
17560
|
dir = parent;
|
|
@@ -17558,9 +17564,9 @@ function resolveDetachedJobScript() {
|
|
|
17558
17564
|
function formatDetachedJobInvoke(scriptPath) {
|
|
17559
17565
|
const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
|
|
17560
17566
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
17561
|
-
return "node scripts/detached-job.
|
|
17567
|
+
return "node scripts/detached-job.cjs";
|
|
17562
17568
|
}
|
|
17563
|
-
var import_node_fs45, import_node_path42, import_node_url3, import_meta3;
|
|
17569
|
+
var import_node_fs45, import_node_path42, import_node_url3, import_meta3, DETACHED_JOB_FILENAMES;
|
|
17564
17570
|
var init_detached_job_path = __esm({
|
|
17565
17571
|
"src/skills/detached-job-path.ts"() {
|
|
17566
17572
|
"use strict";
|
|
@@ -17569,6 +17575,7 @@ var init_detached_job_path = __esm({
|
|
|
17569
17575
|
import_node_url3 = require("url");
|
|
17570
17576
|
init_packaged_runtime();
|
|
17571
17577
|
import_meta3 = {};
|
|
17578
|
+
DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
17572
17579
|
}
|
|
17573
17580
|
});
|
|
17574
17581
|
|
|
@@ -17761,7 +17768,7 @@ function formatLongRunningDirective(opts) {
|
|
|
17761
17768
|
return [
|
|
17762
17769
|
"Long-running jobs (mandatory when a command may run more than ~30s):",
|
|
17763
17770
|
"A Sideboard 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. Do not ask the human to poll.",
|
|
17764
|
-
`Helper (same tool as \`scripts/detached-job.
|
|
17771
|
+
`Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
|
|
17765
17772
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
17766
17773
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
17767
17774
|
`- 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.`,
|
|
@@ -24178,9 +24185,9 @@ async function startMcpServer() {
|
|
|
24178
24185
|
);
|
|
24179
24186
|
server.tool(
|
|
24180
24187
|
"wait_for_job",
|
|
24181
|
-
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.
|
|
24188
|
+
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. 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. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
24182
24189
|
{
|
|
24183
|
-
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
24190
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
24184
24191
|
timeoutMs: import_zod8.z.number().optional()
|
|
24185
24192
|
},
|
|
24186
24193
|
async ({ id, timeoutMs }) => {
|
|
@@ -24192,9 +24199,9 @@ async function startMcpServer() {
|
|
|
24192
24199
|
);
|
|
24193
24200
|
server.tool(
|
|
24194
24201
|
"stop_job",
|
|
24195
|
-
"Stop a detached job you started with detached-job.
|
|
24202
|
+
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. After stop, present_artifact type=log with status=failed and the last delta, then decide the next step.",
|
|
24196
24203
|
{
|
|
24197
|
-
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
24204
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
24198
24205
|
reason: import_zod8.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
|
|
24199
24206
|
},
|
|
24200
24207
|
async ({ id, reason }) => {
|
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
syncBoardPins,
|
|
38
38
|
updateSchedule,
|
|
39
39
|
waitForDetachedJob
|
|
40
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-NYMFNUT5.js";
|
|
41
41
|
import {
|
|
42
42
|
isInternalAgentStatusText,
|
|
43
43
|
listModelsForAgent,
|
|
@@ -3261,9 +3261,9 @@ async function startMcpServer() {
|
|
|
3261
3261
|
);
|
|
3262
3262
|
server.tool(
|
|
3263
3263
|
"wait_for_job",
|
|
3264
|
-
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.
|
|
3264
|
+
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. 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. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
3265
3265
|
{
|
|
3266
|
-
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
3266
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
3267
3267
|
timeoutMs: z8.number().optional()
|
|
3268
3268
|
},
|
|
3269
3269
|
async ({ id, timeoutMs }) => {
|
|
@@ -3275,9 +3275,9 @@ async function startMcpServer() {
|
|
|
3275
3275
|
);
|
|
3276
3276
|
server.tool(
|
|
3277
3277
|
"stop_job",
|
|
3278
|
-
"Stop a detached job you started with detached-job.
|
|
3278
|
+
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. After stop, present_artifact type=log with status=failed and the last delta, then decide the next step.",
|
|
3279
3279
|
{
|
|
3280
|
-
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.
|
|
3280
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
3281
3281
|
reason: z8.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
|
|
3282
3282
|
},
|
|
3283
3283
|
async ({ id, reason }) => {
|