@sideboard-ai/core 0.1.166 → 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.
@@ -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-BJLZGOG5.js");
402
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-XG3QCMOS.js");
403
403
  await getOrchestrator2().send(threadId, prompt);
404
404
  } catch {
405
405
  }
@@ -728,6 +728,8 @@ function createAgentStreamCoalescer(emit, opts) {
728
728
  function applyPromptCacheTtlEnv(agent, env) {
729
729
  if (agent === "claude" && env.FORCE_PROMPT_CACHING_5M !== "1") {
730
730
  env.ENABLE_PROMPT_CACHING_1H ??= "1";
731
+ env.CLAUDE_CODE_PROMPT_CACHE_TTL ??= "1h";
732
+ env.CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL ??= "1h";
731
733
  }
732
734
  if (agent === "opencode" && env.OPENCODE_ANTHROPIC_FORCE_PROMPT_CACHING_5M !== "1") {
733
735
  env.OPENCODE_ANTHROPIC_PROMPT_CACHING_1H ??= "1";
@@ -2607,7 +2609,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
2607
2609
  function formatDeferredDoneContinuePrompt() {
2608
2610
  return [
2609
2611
  "You ended the turn after promising to report later, but no detached job is running.",
2610
- "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.",
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.",
2611
2613
  "Do not say you will let the user know later."
2612
2614
  ].join(" ");
2613
2615
  }
@@ -2616,7 +2618,7 @@ function turnWatchedDetachedJob(parts) {
2616
2618
  if (p.type !== "tool") return false;
2617
2619
  if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
2618
2620
  const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
2619
- return /detached-job\.js\b/i.test(blob);
2621
+ return /detached-job\.(?:js|cjs)\b/i.test(blob);
2620
2622
  });
2621
2623
  }
2622
2624
  function planJobContinue(opts) {
@@ -2709,7 +2711,7 @@ async function waitForDetachedJob(cwd, id, opts) {
2709
2711
  id: jobId,
2710
2712
  delta: "",
2711
2713
  progress: "No detached job. Start one first.",
2712
- hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
2714
+ hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
2713
2715
  };
2714
2716
  }
2715
2717
  const deadline = Date.now() + timeoutMs;
@@ -2768,7 +2770,7 @@ async function stopDetachedJob(cwd, id, opts) {
2768
2770
  id: jobId,
2769
2771
  delta: "",
2770
2772
  progress: "No detached job. Start one first.",
2771
- hint: "Start with detached-job.js start <id> -- <command>, then wait_for_job or stop_job.",
2773
+ hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
2772
2774
  stopped: false,
2773
2775
  reason: "not-found"
2774
2776
  };
@@ -5268,21 +5270,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
5268
5270
 
5269
5271
  ## Tool
5270
5272
 
5271
- Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.js\`, that is the same tool.
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.
5272
5274
 
5273
5275
  \`\`\`bash
5274
5276
  # Start (exits in ~1s; job survives this turn)
5275
- node <detached-job.js> start <id> -- <command> [args...]
5277
+ node <detached-job.cjs> start <id> -- <command> [args...]
5276
5278
 
5277
5279
  # Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
5278
5280
  # Shell fallback:
5279
- node <detached-job.js> wait <id>
5281
+ node <detached-job.cjs> wait <id>
5280
5282
 
5281
5283
  # Block until the process exits (humans / a turn that will not be interrupted)
5282
- node <detached-job.js> wait <id> --until-done
5284
+ node <detached-job.cjs> wait <id> --until-done
5283
5285
 
5284
- node <detached-job.js> stop <id> [--reason TEXT]
5285
- node <detached-job.js> status <id>
5286
+ node <detached-job.cjs> stop <id> [--reason TEXT]
5287
+ node <detached-job.cjs> status <id>
5286
5288
  \`\`\`
5287
5289
 
5288
5290
  \`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
@@ -5313,7 +5315,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
5313
5315
  Ad-hoc pid/log (legacy or another tool\u2019s files):
5314
5316
 
5315
5317
  \`\`\`bash
5316
- node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5318
+ node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5317
5319
  \`\`\`
5318
5320
 
5319
5321
  ## Agent loop
@@ -5322,7 +5324,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5322
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.
5323
5325
  3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
5324
5326
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
5325
- 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.js stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
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.
5326
5328
 
5327
5329
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
5328
5330
 
@@ -5335,7 +5337,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
5335
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:
5336
5338
 
5337
5339
  \`\`\`bash
5338
- node <detached-job.js> start pr-checks -- gh pr checks --watch
5340
+ node <detached-job.cjs> start pr-checks -- gh pr checks --watch
5339
5341
  \`\`\`
5340
5342
 
5341
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.
@@ -5601,19 +5603,26 @@ import { join as join15 } from "path";
5601
5603
  import { existsSync as existsSync15 } from "fs";
5602
5604
  import { dirname as dirname5, join as join14 } from "path";
5603
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
+ }
5604
5614
  function packagedDetachedJobPath() {
5605
5615
  const dir = packagedMcpDir();
5606
5616
  if (!dir) return null;
5607
- const script = join14(dir, "scripts", "detached-job.js");
5608
- return existsSync15(script) ? script : null;
5617
+ return detachedJobScriptIn(dir);
5609
5618
  }
5610
5619
  function resolveDetachedJobScript() {
5611
5620
  const packaged = packagedDetachedJobPath();
5612
5621
  if (packaged) return packaged;
5613
5622
  let dir = dirname5(fileURLToPath(import.meta.url));
5614
5623
  for (let i = 0; i < 8; i++) {
5615
- const candidate = join14(dir, "scripts", "detached-job.js");
5616
- if (existsSync15(candidate)) return candidate;
5624
+ const candidate = detachedJobScriptIn(dir);
5625
+ if (candidate) return candidate;
5617
5626
  const parent = dirname5(dir);
5618
5627
  if (parent === dir) break;
5619
5628
  dir = parent;
@@ -5623,7 +5632,7 @@ function resolveDetachedJobScript() {
5623
5632
  function formatDetachedJobInvoke(scriptPath) {
5624
5633
  const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
5625
5634
  if (resolved) return `node ${JSON.stringify(resolved)}`;
5626
- return "node scripts/detached-job.js";
5635
+ return "node scripts/detached-job.cjs";
5627
5636
  }
5628
5637
 
5629
5638
  // src/agents/instructions.ts
@@ -5817,7 +5826,7 @@ function formatLongRunningDirective(opts) {
5817
5826
  return [
5818
5827
  "Long-running jobs (mandatory when a command may run more than ~30s):",
5819
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.",
5820
- `Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
5829
+ `Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
5821
5830
  `- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
5822
5831
  "- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
5823
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.`,
@@ -6231,7 +6240,7 @@ function formatScheduledPrompt(name, prompt) {
6231
6240
  ${prompt}`;
6232
6241
  }
6233
6242
  async function defaultDeps() {
6234
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-BJLZGOG5.js");
6243
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-XG3QCMOS.js");
6235
6244
  const orch = getOrchestrator2();
6236
6245
  return {
6237
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-SQ37MAS5.js");
467
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-IIME6KXQ.js");
468
468
  await getOrchestrator2().send(threadId, prompt);
469
469
  } catch {
470
470
  }
@@ -732,6 +732,8 @@ import { execa } from "execa";
732
732
  function applyPromptCacheTtlEnv(agent, env) {
733
733
  if (agent === "claude" && env.FORCE_PROMPT_CACHING_5M !== "1") {
734
734
  env.ENABLE_PROMPT_CACHING_1H ??= "1";
735
+ env.CLAUDE_CODE_PROMPT_CACHE_TTL ??= "1h";
736
+ env.CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL ??= "1h";
735
737
  }
736
738
  if (agent === "opencode" && env.OPENCODE_ANTHROPIC_FORCE_PROMPT_CACHING_5M !== "1") {
737
739
  env.OPENCODE_ANTHROPIC_PROMPT_CACHING_1H ??= "1";
@@ -2692,7 +2694,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
2692
2694
  function formatDeferredDoneContinuePrompt() {
2693
2695
  return [
2694
2696
  "You ended the turn after promising to report later, but no detached job is running.",
2695
- "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.",
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.",
2696
2698
  "Do not say you will let the user know later."
2697
2699
  ].join(" ");
2698
2700
  }
@@ -2701,7 +2703,7 @@ function turnWatchedDetachedJob(parts) {
2701
2703
  if (p.type !== "tool") return false;
2702
2704
  if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
2703
2705
  const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
2704
- return /detached-job\.js\b/i.test(blob);
2706
+ return /detached-job\.(?:js|cjs)\b/i.test(blob);
2705
2707
  });
2706
2708
  }
2707
2709
  function planJobContinue(opts) {
@@ -2794,7 +2796,7 @@ async function waitForDetachedJob(cwd, id, opts) {
2794
2796
  id: jobId,
2795
2797
  delta: "",
2796
2798
  progress: "No detached job. Start one first.",
2797
- hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
2799
+ hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
2798
2800
  };
2799
2801
  }
2800
2802
  const deadline = Date.now() + timeoutMs;
@@ -2853,7 +2855,7 @@ async function stopDetachedJob(cwd, id, opts) {
2853
2855
  id: jobId,
2854
2856
  delta: "",
2855
2857
  progress: "No detached job. Start one first.",
2856
- hint: "Start with detached-job.js start <id> -- <command>, then wait_for_job or stop_job.",
2858
+ hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
2857
2859
  stopped: false,
2858
2860
  reason: "not-found"
2859
2861
  };
@@ -5420,21 +5422,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
5420
5422
 
5421
5423
  ## Tool
5422
5424
 
5423
- Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.js\`, that is the same tool.
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.
5424
5426
 
5425
5427
  \`\`\`bash
5426
5428
  # Start (exits in ~1s; job survives this turn)
5427
- node <detached-job.js> start <id> -- <command> [args...]
5429
+ node <detached-job.cjs> start <id> -- <command> [args...]
5428
5430
 
5429
5431
  # Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
5430
5432
  # Shell fallback:
5431
- node <detached-job.js> wait <id>
5433
+ node <detached-job.cjs> wait <id>
5432
5434
 
5433
5435
  # Block until the process exits (humans / a turn that will not be interrupted)
5434
- node <detached-job.js> wait <id> --until-done
5436
+ node <detached-job.cjs> wait <id> --until-done
5435
5437
 
5436
- node <detached-job.js> stop <id> [--reason TEXT]
5437
- node <detached-job.js> status <id>
5438
+ node <detached-job.cjs> stop <id> [--reason TEXT]
5439
+ node <detached-job.cjs> status <id>
5438
5440
  \`\`\`
5439
5441
 
5440
5442
  \`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
@@ -5465,7 +5467,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
5465
5467
  Ad-hoc pid/log (legacy or another tool\u2019s files):
5466
5468
 
5467
5469
  \`\`\`bash
5468
- node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5470
+ node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5469
5471
  \`\`\`
5470
5472
 
5471
5473
  ## Agent loop
@@ -5474,7 +5476,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
5474
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.
5475
5477
  3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
5476
5478
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
5477
- 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.js stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
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.
5478
5480
 
5479
5481
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
5480
5482
 
@@ -5487,7 +5489,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
5487
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:
5488
5490
 
5489
5491
  \`\`\`bash
5490
- node <detached-job.js> start pr-checks -- gh pr checks --watch
5492
+ node <detached-job.cjs> start pr-checks -- gh pr checks --watch
5491
5493
  \`\`\`
5492
5494
 
5493
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.
@@ -5753,19 +5755,26 @@ import { join as join15 } from "path";
5753
5755
  import { existsSync as existsSync15 } from "fs";
5754
5756
  import { dirname as dirname5, join as join14 } from "path";
5755
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
+ }
5756
5766
  function packagedDetachedJobPath() {
5757
5767
  const dir = packagedMcpDir();
5758
5768
  if (!dir) return null;
5759
- const script = join14(dir, "scripts", "detached-job.js");
5760
- return existsSync15(script) ? script : null;
5769
+ return detachedJobScriptIn(dir);
5761
5770
  }
5762
5771
  function resolveDetachedJobScript() {
5763
5772
  const packaged = packagedDetachedJobPath();
5764
5773
  if (packaged) return packaged;
5765
5774
  let dir = dirname5(fileURLToPath(import.meta.url));
5766
5775
  for (let i = 0; i < 8; i++) {
5767
- const candidate = join14(dir, "scripts", "detached-job.js");
5768
- if (existsSync15(candidate)) return candidate;
5776
+ const candidate = detachedJobScriptIn(dir);
5777
+ if (candidate) return candidate;
5769
5778
  const parent = dirname5(dir);
5770
5779
  if (parent === dir) break;
5771
5780
  dir = parent;
@@ -5775,7 +5784,7 @@ function resolveDetachedJobScript() {
5775
5784
  function formatDetachedJobInvoke(scriptPath) {
5776
5785
  const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
5777
5786
  if (resolved) return `node ${JSON.stringify(resolved)}`;
5778
- return "node scripts/detached-job.js";
5787
+ return "node scripts/detached-job.cjs";
5779
5788
  }
5780
5789
 
5781
5790
  // src/agents/instructions.ts
@@ -5991,7 +6000,7 @@ function formatLongRunningDirective(opts) {
5991
6000
  return [
5992
6001
  "Long-running jobs (mandatory when a command may run more than ~30s):",
5993
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.",
5994
- `Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
6003
+ `Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
5995
6004
  `- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
5996
6005
  "- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
5997
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.`,
@@ -6641,7 +6650,7 @@ function formatScheduledPrompt(name, prompt) {
6641
6650
  ${prompt}`;
6642
6651
  }
6643
6652
  async function defaultDeps() {
6644
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SQ37MAS5.js");
6653
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-IIME6KXQ.js");
6645
6654
  const orch = getOrchestrator2();
6646
6655
  return {
6647
6656
  findThread: (id) => findThreadByRef(id),
package/dist/index.cjs CHANGED
@@ -13584,6 +13584,8 @@ var init_cursor_stream_coalesce = __esm({
13584
13584
  function applyPromptCacheTtlEnv(agent, env) {
13585
13585
  if (agent === "claude" && env.FORCE_PROMPT_CACHING_5M !== "1") {
13586
13586
  env.ENABLE_PROMPT_CACHING_1H ??= "1";
13587
+ env.CLAUDE_CODE_PROMPT_CACHE_TTL ??= "1h";
13588
+ env.CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL ??= "1h";
13587
13589
  }
13588
13590
  if (agent === "opencode" && env.OPENCODE_ANTHROPIC_FORCE_PROMPT_CACHING_5M !== "1") {
13589
13591
  env.OPENCODE_ANTHROPIC_PROMPT_CACHING_1H ??= "1";
@@ -15506,7 +15508,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
15506
15508
  function formatDeferredDoneContinuePrompt() {
15507
15509
  return [
15508
15510
  "You ended the turn after promising to report later, but no detached job is running.",
15509
- "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.",
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.",
15510
15512
  "Do not say you will let the user know later."
15511
15513
  ].join(" ");
15512
15514
  }
@@ -15515,7 +15517,7 @@ function turnWatchedDetachedJob(parts) {
15515
15517
  if (p.type !== "tool") return false;
15516
15518
  if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
15517
15519
  const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
15518
- return /detached-job\.js\b/i.test(blob);
15520
+ return /detached-job\.(?:js|cjs)\b/i.test(blob);
15519
15521
  });
15520
15522
  }
15521
15523
  function planJobContinue(opts) {
@@ -15608,7 +15610,7 @@ async function waitForDetachedJob(cwd, id, opts) {
15608
15610
  id: jobId,
15609
15611
  delta: "",
15610
15612
  progress: "No detached job. Start one first.",
15611
- hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
15613
+ hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
15612
15614
  };
15613
15615
  }
15614
15616
  const deadline = Date.now() + timeoutMs;
@@ -15667,7 +15669,7 @@ async function stopDetachedJob(cwd, id, opts) {
15667
15669
  id: jobId,
15668
15670
  delta: "",
15669
15671
  progress: "No detached job. Start one first.",
15670
- hint: "Start with detached-job.js start <id> -- <command>, then wait_for_job or stop_job.",
15672
+ hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
15671
15673
  stopped: false,
15672
15674
  reason: "not-found"
15673
15675
  };
@@ -18903,21 +18905,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
18903
18905
 
18904
18906
  ## Tool
18905
18907
 
18906
- Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.js\`, that is the same tool.
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.
18907
18909
 
18908
18910
  \`\`\`bash
18909
18911
  # Start (exits in ~1s; job survives this turn)
18910
- node <detached-job.js> start <id> -- <command> [args...]
18912
+ node <detached-job.cjs> start <id> -- <command> [args...]
18911
18913
 
18912
18914
  # Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
18913
18915
  # Shell fallback:
18914
- node <detached-job.js> wait <id>
18916
+ node <detached-job.cjs> wait <id>
18915
18917
 
18916
18918
  # Block until the process exits (humans / a turn that will not be interrupted)
18917
- node <detached-job.js> wait <id> --until-done
18919
+ node <detached-job.cjs> wait <id> --until-done
18918
18920
 
18919
- node <detached-job.js> stop <id> [--reason TEXT]
18920
- node <detached-job.js> status <id>
18921
+ node <detached-job.cjs> stop <id> [--reason TEXT]
18922
+ node <detached-job.cjs> status <id>
18921
18923
  \`\`\`
18922
18924
 
18923
18925
  \`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
@@ -18948,7 +18950,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
18948
18950
  Ad-hoc pid/log (legacy or another tool\u2019s files):
18949
18951
 
18950
18952
  \`\`\`bash
18951
- node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
18953
+ node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
18952
18954
  \`\`\`
18953
18955
 
18954
18956
  ## Agent loop
@@ -18957,7 +18959,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
18957
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.
18958
18960
  3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
18959
18961
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
18960
- 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.js stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
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.
18961
18963
 
18962
18964
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
18963
18965
 
@@ -18970,7 +18972,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
18970
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:
18971
18973
 
18972
18974
  \`\`\`bash
18973
- node <detached-job.js> start pr-checks -- gh pr checks --watch
18975
+ node <detached-job.cjs> start pr-checks -- gh pr checks --watch
18974
18976
  \`\`\`
18975
18977
 
18976
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.
@@ -19249,19 +19251,25 @@ var init_expand = __esm({
19249
19251
  });
19250
19252
 
19251
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
+ }
19252
19261
  function packagedDetachedJobPath() {
19253
19262
  const dir = packagedMcpDir();
19254
19263
  if (!dir) return null;
19255
- const script = (0, import_node_path44.join)(dir, "scripts", "detached-job.js");
19256
- return (0, import_node_fs47.existsSync)(script) ? script : null;
19264
+ return detachedJobScriptIn(dir);
19257
19265
  }
19258
19266
  function resolveDetachedJobScript() {
19259
19267
  const packaged = packagedDetachedJobPath();
19260
19268
  if (packaged) return packaged;
19261
19269
  let dir = (0, import_node_path44.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
19262
19270
  for (let i = 0; i < 8; i++) {
19263
- const candidate = (0, import_node_path44.join)(dir, "scripts", "detached-job.js");
19264
- if ((0, import_node_fs47.existsSync)(candidate)) return candidate;
19271
+ const candidate = detachedJobScriptIn(dir);
19272
+ if (candidate) return candidate;
19265
19273
  const parent = (0, import_node_path44.dirname)(dir);
19266
19274
  if (parent === dir) break;
19267
19275
  dir = parent;
@@ -19271,9 +19279,9 @@ function resolveDetachedJobScript() {
19271
19279
  function formatDetachedJobInvoke(scriptPath) {
19272
19280
  const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
19273
19281
  if (resolved) return `node ${JSON.stringify(resolved)}`;
19274
- return "node scripts/detached-job.js";
19282
+ return "node scripts/detached-job.cjs";
19275
19283
  }
19276
- 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;
19277
19285
  var init_detached_job_path = __esm({
19278
19286
  "src/skills/detached-job-path.ts"() {
19279
19287
  "use strict";
@@ -19282,6 +19290,7 @@ var init_detached_job_path = __esm({
19282
19290
  import_node_url3 = require("url");
19283
19291
  init_packaged_runtime();
19284
19292
  import_meta3 = {};
19293
+ DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
19285
19294
  }
19286
19295
  });
19287
19296
 
@@ -19496,7 +19505,7 @@ function formatLongRunningDirective(opts) {
19496
19505
  return [
19497
19506
  "Long-running jobs (mandatory when a command may run more than ~30s):",
19498
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.",
19499
- `Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
19508
+ `Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
19500
19509
  `- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
19501
19510
  "- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
19502
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.`,
@@ -27202,9 +27211,9 @@ async function startMcpServer() {
27202
27211
  );
27203
27212
  server.tool(
27204
27213
  "wait_for_job",
27205
- "Wait on a detached long job (tests, pack, deploy, connector CLI) 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. 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.",
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.",
27206
27215
  {
27207
- id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
27216
+ id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
27208
27217
  timeoutMs: import_zod8.z.number().optional()
27209
27218
  },
27210
27219
  async ({ id, timeoutMs }) => {
@@ -27216,9 +27225,9 @@ async function startMcpServer() {
27216
27225
  );
27217
27226
  server.tool(
27218
27227
  "stop_job",
27219
- "Stop a detached job you started with detached-job.js 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.",
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.",
27220
27229
  {
27221
- id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
27230
+ id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
27222
27231
  reason: import_zod8.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
27223
27232
  },
27224
27233
  async ({ id, reason }) => {
package/dist/index.d.cts CHANGED
@@ -2981,6 +2981,11 @@ interface SpawnTurnHandle {
2981
2981
  /**
2982
2982
  * Opt into 1h prompt-cache TTL for desktop gaps (read a diff, Slack, schedules).
2983
2983
  * Claude Code / OpenCode default to 5m on API keys. Honor an explicit 5m force.
2984
+ *
2985
+ * Claude Code v2.1.242+ prefers `CLAUDE_CODE_PROMPT_CACHE_TTL` /
2986
+ * `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL` over `ENABLE_PROMPT_CACHING_1H`
2987
+ * (and over `promptCacheTtl` in settings). Set both so a 5m settings default
2988
+ * does not silently win after a CLI upgrade. Older CLIs ignore the new keys.
2984
2989
  */
2985
2990
  declare function applyPromptCacheTtlEnv(agent: AgentKind, env: NodeJS.ProcessEnv): void;
2986
2991
  declare function spawnAgentTurn(thread: Thread, input: string | AgentTurnInput, onEvent: (event: AgentEvent) => void): Promise<SpawnTurnHandle>;
@@ -3495,14 +3500,14 @@ declare const BUNDLED_SKILL_PREFIX = "bundled:";
3495
3500
  declare function discoverSkills(worktreePath: string): SkillInfo[];
3496
3501
  declare function readSkillBody(skillPath: string, maxChars?: number): string;
3497
3502
 
3498
- /** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.js`). */
3503
+ /** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.cjs`). */
3499
3504
  declare function packagedDetachedJobPath(): string | null;
3500
3505
  /**
3501
3506
  * Absolute path to the detach helper agents should exec.
3502
3507
  * Packaged app first, then walk up from this module to the repo `scripts/` copy.
3503
3508
  */
3504
3509
  declare function resolveDetachedJobScript(): string | null;
3505
- /** Shell invocation agents can copy (`node "/abs/path/detached-job.js"`). */
3510
+ /** Shell invocation agents can copy (`node "/abs/path/detached-job.cjs"`). */
3506
3511
  declare function formatDetachedJobInvoke(scriptPath?: string | null): string;
3507
3512
 
3508
3513
  /** Sideboard product skill — available in every worktree, not only this repo. */
package/dist/index.d.ts CHANGED
@@ -2981,6 +2981,11 @@ interface SpawnTurnHandle {
2981
2981
  /**
2982
2982
  * Opt into 1h prompt-cache TTL for desktop gaps (read a diff, Slack, schedules).
2983
2983
  * Claude Code / OpenCode default to 5m on API keys. Honor an explicit 5m force.
2984
+ *
2985
+ * Claude Code v2.1.242+ prefers `CLAUDE_CODE_PROMPT_CACHE_TTL` /
2986
+ * `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL` over `ENABLE_PROMPT_CACHING_1H`
2987
+ * (and over `promptCacheTtl` in settings). Set both so a 5m settings default
2988
+ * does not silently win after a CLI upgrade. Older CLIs ignore the new keys.
2984
2989
  */
2985
2990
  declare function applyPromptCacheTtlEnv(agent: AgentKind, env: NodeJS.ProcessEnv): void;
2986
2991
  declare function spawnAgentTurn(thread: Thread, input: string | AgentTurnInput, onEvent: (event: AgentEvent) => void): Promise<SpawnTurnHandle>;
@@ -3495,14 +3500,14 @@ declare const BUNDLED_SKILL_PREFIX = "bundled:";
3495
3500
  declare function discoverSkills(worktreePath: string): SkillInfo[];
3496
3501
  declare function readSkillBody(skillPath: string, maxChars?: number): string;
3497
3502
 
3498
- /** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.js`). */
3503
+ /** Packaged extraResources copy (`Contents/Resources/sideboard-mcp/scripts/detached-job.cjs`). */
3499
3504
  declare function packagedDetachedJobPath(): string | null;
3500
3505
  /**
3501
3506
  * Absolute path to the detach helper agents should exec.
3502
3507
  * Packaged app first, then walk up from this module to the repo `scripts/` copy.
3503
3508
  */
3504
3509
  declare function resolveDetachedJobScript(): string | null;
3505
- /** Shell invocation agents can copy (`node "/abs/path/detached-job.js"`). */
3510
+ /** Shell invocation agents can copy (`node "/abs/path/detached-job.cjs"`). */
3506
3511
  declare function formatDetachedJobInvoke(scriptPath?: string | null): string;
3507
3512
 
3508
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-U7NHWCPE.js";
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.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. 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.",
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.js start)"),
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.js 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.",
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.js start)"),
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 }) => {
@@ -11844,6 +11844,8 @@ var init_cursor_stream_coalesce = __esm({
11844
11844
  function applyPromptCacheTtlEnv(agent, env) {
11845
11845
  if (agent === "claude" && env.FORCE_PROMPT_CACHING_5M !== "1") {
11846
11846
  env.ENABLE_PROMPT_CACHING_1H ??= "1";
11847
+ env.CLAUDE_CODE_PROMPT_CACHE_TTL ??= "1h";
11848
+ env.CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL ??= "1h";
11847
11849
  }
11848
11850
  if (agent === "opencode" && env.OPENCODE_ANTHROPIC_FORCE_PROMPT_CACHING_5M !== "1") {
11849
11851
  env.OPENCODE_ANTHROPIC_PROMPT_CACHING_1H ??= "1";
@@ -13858,7 +13860,7 @@ function formatJobStillRunningContinuePrompt(jobIds) {
13858
13860
  function formatDeferredDoneContinuePrompt() {
13859
13861
  return [
13860
13862
  "You ended the turn after promising to report later, but no detached job is running.",
13861
- "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.",
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.",
13862
13864
  "Do not say you will let the user know later."
13863
13865
  ].join(" ");
13864
13866
  }
@@ -13867,7 +13869,7 @@ function turnWatchedDetachedJob(parts) {
13867
13869
  if (p.type !== "tool") return false;
13868
13870
  if (/wait_for_job$/i.test(p.name ?? "") || /stop_job$/i.test(p.name ?? "")) return true;
13869
13871
  const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
13870
- return /detached-job\.js\b/i.test(blob);
13872
+ return /detached-job\.(?:js|cjs)\b/i.test(blob);
13871
13873
  });
13872
13874
  }
13873
13875
  function planJobContinue(opts) {
@@ -13960,7 +13962,7 @@ async function waitForDetachedJob(cwd, id, opts) {
13960
13962
  id: jobId,
13961
13963
  delta: "",
13962
13964
  progress: "No detached job. Start one first.",
13963
- hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
13965
+ hint: "Start with detached-job.cjs start <id> -- <command>, then call wait_for_job again."
13964
13966
  };
13965
13967
  }
13966
13968
  const deadline = Date.now() + timeoutMs;
@@ -14019,7 +14021,7 @@ async function stopDetachedJob(cwd, id, opts) {
14019
14021
  id: jobId,
14020
14022
  delta: "",
14021
14023
  progress: "No detached job. Start one first.",
14022
- hint: "Start with detached-job.js start <id> -- <command>, then wait_for_job or stop_job.",
14024
+ hint: "Start with detached-job.cjs start <id> -- <command>, then wait_for_job or stop_job.",
14023
14025
  stopped: false,
14024
14026
  reason: "not-found"
14025
14027
  };
@@ -17188,21 +17190,21 @@ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP
17188
17190
 
17189
17191
  ## Tool
17190
17192
 
17191
- Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.js\`, that is the same tool.
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.
17192
17194
 
17193
17195
  \`\`\`bash
17194
17196
  # Start (exits in ~1s; job survives this turn)
17195
- node <detached-job.js> start <id> -- <command> [args...]
17197
+ node <detached-job.cjs> start <id> -- <command> [args...]
17196
17198
 
17197
17199
  # Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
17198
17200
  # Shell fallback:
17199
- node <detached-job.js> wait <id>
17201
+ node <detached-job.cjs> wait <id>
17200
17202
 
17201
17203
  # Block until the process exits (humans / a turn that will not be interrupted)
17202
- node <detached-job.js> wait <id> --until-done
17204
+ node <detached-job.cjs> wait <id> --until-done
17203
17205
 
17204
- node <detached-job.js> stop <id> [--reason TEXT]
17205
- node <detached-job.js> status <id>
17206
+ node <detached-job.cjs> stop <id> [--reason TEXT]
17207
+ node <detached-job.cjs> status <id>
17206
17208
  \`\`\`
17207
17209
 
17208
17210
  \`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.context/.sideboard/detached-jobs/<id>/\` (local scratch).
@@ -17233,7 +17235,7 @@ After **start**, present once (\`status=running\`, empty or first lines). After
17233
17235
  Ad-hoc pid/log (legacy or another tool\u2019s files):
17234
17236
 
17235
17237
  \`\`\`bash
17236
- node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
17238
+ node <detached-job.cjs> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
17237
17239
  \`\`\`
17238
17240
 
17239
17241
  ## Agent loop
@@ -17242,7 +17244,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
17242
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.
17243
17245
  3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
17244
17246
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
17245
- 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.js stop <id>\`. Do **not** stop a pack/test/deploy that is clearly making progress.
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.
17246
17248
 
17247
17249
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait \u2014 unless you decided to stop.
17248
17250
 
@@ -17255,7 +17257,7 @@ Huge \`--json\` / \`--expand\` dumps crash any worktree agent mid-turn (Claude /
17255
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:
17256
17258
 
17257
17259
  \`\`\`bash
17258
- node <detached-job.js> start pr-checks -- gh pr checks --watch
17260
+ node <detached-job.cjs> start pr-checks -- gh pr checks --watch
17259
17261
  \`\`\`
17260
17262
 
17261
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.
@@ -17534,19 +17536,25 @@ var init_expand = __esm({
17534
17536
  });
17535
17537
 
17536
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
+ }
17537
17546
  function packagedDetachedJobPath() {
17538
17547
  const dir = packagedMcpDir();
17539
17548
  if (!dir) return null;
17540
- const script = (0, import_node_path42.join)(dir, "scripts", "detached-job.js");
17541
- return (0, import_node_fs45.existsSync)(script) ? script : null;
17549
+ return detachedJobScriptIn(dir);
17542
17550
  }
17543
17551
  function resolveDetachedJobScript() {
17544
17552
  const packaged = packagedDetachedJobPath();
17545
17553
  if (packaged) return packaged;
17546
17554
  let dir = (0, import_node_path42.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
17547
17555
  for (let i = 0; i < 8; i++) {
17548
- const candidate = (0, import_node_path42.join)(dir, "scripts", "detached-job.js");
17549
- if ((0, import_node_fs45.existsSync)(candidate)) return candidate;
17556
+ const candidate = detachedJobScriptIn(dir);
17557
+ if (candidate) return candidate;
17550
17558
  const parent = (0, import_node_path42.dirname)(dir);
17551
17559
  if (parent === dir) break;
17552
17560
  dir = parent;
@@ -17556,9 +17564,9 @@ function resolveDetachedJobScript() {
17556
17564
  function formatDetachedJobInvoke(scriptPath) {
17557
17565
  const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
17558
17566
  if (resolved) return `node ${JSON.stringify(resolved)}`;
17559
- return "node scripts/detached-job.js";
17567
+ return "node scripts/detached-job.cjs";
17560
17568
  }
17561
- 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;
17562
17570
  var init_detached_job_path = __esm({
17563
17571
  "src/skills/detached-job-path.ts"() {
17564
17572
  "use strict";
@@ -17567,6 +17575,7 @@ var init_detached_job_path = __esm({
17567
17575
  import_node_url3 = require("url");
17568
17576
  init_packaged_runtime();
17569
17577
  import_meta3 = {};
17578
+ DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
17570
17579
  }
17571
17580
  });
17572
17581
 
@@ -17759,7 +17768,7 @@ function formatLongRunningDirective(opts) {
17759
17768
  return [
17760
17769
  "Long-running jobs (mandatory when a command may run more than ~30s):",
17761
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.",
17762
- `Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
17771
+ `Helper (same tool as \`scripts/detached-job.cjs\` when that file exists in the worktree): \`${invoke}\``,
17763
17772
  `- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
17764
17773
  "- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
17765
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.`,
@@ -24176,9 +24185,9 @@ async function startMcpServer() {
24176
24185
  );
24177
24186
  server.tool(
24178
24187
  "wait_for_job",
24179
- "Wait on a detached long job (tests, pack, deploy, connector CLI) 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. 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.",
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.",
24180
24189
  {
24181
- id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
24190
+ id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
24182
24191
  timeoutMs: import_zod8.z.number().optional()
24183
24192
  },
24184
24193
  async ({ id, timeoutMs }) => {
@@ -24190,9 +24199,9 @@ async function startMcpServer() {
24190
24199
  );
24191
24200
  server.tool(
24192
24201
  "stop_job",
24193
- "Stop a detached job you started with detached-job.js 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.",
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.",
24194
24203
  {
24195
- id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
24204
+ id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
24196
24205
  reason: import_zod8.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
24197
24206
  },
24198
24207
  async ({ id, reason }) => {
@@ -37,7 +37,7 @@ import {
37
37
  syncBoardPins,
38
38
  updateSchedule,
39
39
  waitForDetachedJob
40
- } from "../chunk-D6YXN25M.js";
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.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. 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.",
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.js start)"),
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.js 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.",
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.js start)"),
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 }) => {
@@ -5,7 +5,7 @@ import {
5
5
  isPidAlive,
6
6
  startOrchestration,
7
7
  waitForPidExit
8
- } from "./chunk-U7NHWCPE.js";
8
+ } from "./chunk-UVLPL2FJ.js";
9
9
  import "./chunk-DJU54RYW.js";
10
10
  import "./chunk-UIR4VDYA.js";
11
11
  import "./chunk-HSGSQA42.js";
@@ -7,7 +7,7 @@ import {
7
7
  isPidAlive,
8
8
  startOrchestration,
9
9
  waitForPidExit
10
- } from "./chunk-D6YXN25M.js";
10
+ } from "./chunk-NYMFNUT5.js";
11
11
  import "./chunk-3A54S244.js";
12
12
  import "./chunk-LZ6QMEQC.js";
13
13
  import "./chunk-S42XV45P.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.166",
4
- "description": "Sideboard core \u2014 orchestration, agents, git worktrees, MCP server",
3
+ "version": "0.1.168",
4
+ "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",