@wrongstack/core 0.6.4 → 0.6.6

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.
Files changed (66) hide show
  1. package/dist/{agent-bridge-eb7qnNrd.d.ts → agent-bridge-BBXK_ppx.d.ts} +1 -1
  2. package/dist/agent-subagent-runner-DsSm9lKN.d.ts +174 -0
  3. package/dist/{compactor-RIPuTtWK.d.ts → compactor-C8NhpSt5.d.ts} +1 -1
  4. package/dist/{config-BGGuP_Ar.d.ts → config-DfC6g6KV.d.ts} +1 -1
  5. package/dist/{context-CDRyrkKQ.d.ts → context-DN5v-uQX.d.ts} +11 -0
  6. package/dist/coordination/index.d.ts +10 -9
  7. package/dist/coordination/index.js +113 -20
  8. package/dist/coordination/index.js.map +1 -1
  9. package/dist/defaults/index.d.ts +22 -21
  10. package/dist/defaults/index.js +1359 -919
  11. package/dist/defaults/index.js.map +1 -1
  12. package/dist/{events-BHuIHekD.d.ts → events-CJqwQl8G.d.ts} +17 -1
  13. package/dist/execution/index.d.ts +116 -13
  14. package/dist/execution/index.js +1438 -131
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +6 -6
  17. package/dist/{goal-store-DVCfj7Ff.d.ts → goal-store-HHgaq5ue.d.ts} +3 -3
  18. package/dist/{index-CPcDqvZh.d.ts → index-CXnWsGBp.d.ts} +11 -175
  19. package/dist/{index-BOn9NK7D.d.ts → index-DcnXDPdY.d.ts} +6 -6
  20. package/dist/index.d.ts +29 -28
  21. package/dist/index.js +1105 -666
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -6
  24. package/dist/kernel/index.d.ts +9 -9
  25. package/dist/kernel/index.js.map +1 -1
  26. package/dist/{mcp-servers-DBdh3cee.d.ts → mcp-servers-CevFHHM1.d.ts} +3 -3
  27. package/dist/models/index.d.ts +2 -2
  28. package/dist/models/index.js +5 -16
  29. package/dist/models/index.js.map +1 -1
  30. package/dist/{multi-agent-CxSb-9dQ.d.ts → multi-agent-D5IbASk_.d.ts} +16 -4
  31. package/dist/observability/index.d.ts +2 -2
  32. package/dist/{path-resolver-CMGNadvq.d.ts → path-resolver-CBx_q1HA.d.ts} +2 -2
  33. package/dist/{plan-templates-BJflQY2i.d.ts → plan-templates-BEOllUJV.d.ts} +5 -4
  34. package/dist/{provider-runner-BFgNXpaP.d.ts → provider-runner-Byh5TcJs.d.ts} +3 -3
  35. package/dist/{retry-policy-LKS8MHsB.d.ts → retry-policy-BZSIMxrJ.d.ts} +1 -1
  36. package/dist/sdd/index.d.ts +9 -4
  37. package/dist/sdd/index.js +21 -0
  38. package/dist/sdd/index.js.map +1 -1
  39. package/dist/{secret-scrubber-CfMdAJ_l.d.ts → secret-scrubber-CT7wefiO.d.ts} +1 -1
  40. package/dist/{secret-scrubber-BzQR5BiL.d.ts → secret-scrubber-I0QHY_ob.d.ts} +1 -1
  41. package/dist/security/index.d.ts +3 -3
  42. package/dist/{selector-C7HqnZJU.d.ts → selector-DDb_mq9X.d.ts} +1 -1
  43. package/dist/{session-reader-CzfRA6Vk.d.ts → session-reader-B9nVkziM.d.ts} +1 -1
  44. package/dist/storage/index.d.ts +6 -6
  45. package/dist/storage/index.js +29 -3
  46. package/dist/storage/index.js.map +1 -1
  47. package/dist/{system-prompt-Dl2QY1_B.d.ts → system-prompt-gL06H9P4.d.ts} +1 -1
  48. package/dist/{task-graph-BITvWt4t.d.ts → task-graph-D1YQbpxF.d.ts} +1 -0
  49. package/dist/{tool-executor-FoxBjULX.d.ts → tool-executor-BF7QfYVE.d.ts} +4 -4
  50. package/dist/types/index.d.ts +16 -16
  51. package/dist/types/index.js +5 -16
  52. package/dist/types/index.js.map +1 -1
  53. package/dist/utils/index.d.ts +1 -1
  54. package/package.json +1 -1
  55. package/skills/audit-log/SKILL.md +57 -28
  56. package/skills/bug-hunter/SKILL.md +85 -61
  57. package/skills/git-flow/SKILL.md +73 -18
  58. package/skills/multi-agent/SKILL.md +69 -40
  59. package/skills/node-modern/SKILL.md +111 -19
  60. package/skills/prompt-engineering/SKILL.md +97 -16
  61. package/skills/react-modern/SKILL.md +104 -18
  62. package/skills/refactor-planner/SKILL.md +73 -43
  63. package/skills/sdd/SKILL.md +123 -165
  64. package/skills/security-scanner/SKILL.md +95 -93
  65. package/skills/skill-creator/SKILL.md +58 -25
  66. package/skills/typescript-strict/SKILL.md +107 -15
package/dist/index.js CHANGED
@@ -2201,7 +2201,7 @@ When designing or reviewing architecture:
2201
2201
  - Evaluate scalability and future growth
2202
2202
  - Check for appropriate design patterns
2203
2203
  - Assess coupling and cohesion
2204
- - Look forSOLID principle violations
2204
+ - Look for SOLID principle violations
2205
2205
  - Evaluate data modeling decisions
2206
2206
  - Check for eventual consistency issues
2207
2207
  - Assess API design and contract stability
@@ -2288,37 +2288,26 @@ When refactoring code:
2288
2288
  prompt: `## Brief Mode
2289
2289
 
2290
2290
  You are WrongStack, a fast, no-nonsense AI coding agent.
2291
-
2292
- You operate inside the user's terminal. Read files, run commands, make changes \u2014 get to the point.
2291
+ Get to the point \u2014 read files, run commands, make changes.
2293
2292
 
2294
2293
  ### Operating rules
2295
-
2296
2294
  1. **Read first.** Inspect relevant files before touching anything.
2297
2295
  2. **Edit surgically.** Use edit tool for existing files, write only for new ones.
2298
- 3. **One sentence before action.** State what you're doing, then do it. No preambles.
2296
+ 3. **One sentence before action.** State what you're doing, then do it.
2299
2297
  4. **Say what happened.** After tool calls, one line: success, failure, or what's next.
2300
- 5. **Be honest.** Admit when you don't know or something failed. No fake progress.
2298
+ 5. **Be honest.** Admit when you don't know or something failed. No filler.
2301
2299
  6. **Keep moving.** Task done? Stop. More work needed? State it and continue.
2302
2300
 
2303
2301
  ### Decision rules
2304
-
2305
2302
  - **Ambiguous task?** Ask. One question, get clarity, proceed.
2306
2303
  - **Clear task, unknown approach?** Pick one reasonable path, execute, report.
2307
2304
  - **Tool fails?** Retry once with adjusted params, then report.
2308
- - **Permission denied?** Stop. Acknowledge. Ask what they want instead.
2309
- - **Context filling up?** Compact proactively, don't wait.
2310
2305
 
2311
2306
  ### Output style
2312
-
2313
2307
  - Prose paragraphs (no bullet points unless unavoidable)
2314
2308
  - Code blocks for code, backticks for paths/commands
2315
2309
  - One-liner sufficient? One liner.
2316
- - No "Great question!", "Here's what I did:", or similar filler.
2317
- - Max 3 sentences per paragraph.
2318
-
2319
- ### Focus
2320
-
2321
- Stay on task. Fix only what's asked. Don't refactor surrounding code unless explicitly requested. Own your output \u2014 don't call it "done" or "production-ready"; the user decides that.`,
2310
+ - Max 3 sentences per paragraph.`,
2322
2311
  tags: ["fast", "concise", "direct"],
2323
2312
  toolPreferences: ["read", "edit", "bash"]
2324
2313
  },
@@ -4023,6 +4012,20 @@ var DefaultSessionStore = class {
4023
4012
  await fsp2.unlink(path6.join(this.dir, `${id}.jsonl`));
4024
4013
  await fsp2.unlink(path6.join(this.dir, `${id}.summary.json`)).catch(() => void 0);
4025
4014
  }
4015
+ async clearHistory(id) {
4016
+ const file = path6.join(this.dir, `${id}.jsonl`);
4017
+ const meta = path6.join(this.dir, `${id}.summary.json`);
4018
+ const record = `${JSON.stringify({
4019
+ type: "session_start",
4020
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4021
+ id,
4022
+ model: "unknown",
4023
+ provider: "unknown"
4024
+ })}
4025
+ `;
4026
+ await fsp2.writeFile(file, record, "utf8");
4027
+ await fsp2.unlink(meta).catch(() => void 0);
4028
+ }
4026
4029
  async summarize(id, mtime) {
4027
4030
  try {
4028
4031
  const data = await this.load(id);
@@ -4328,10 +4331,21 @@ var FileSessionWriter = class {
4328
4331
  });
4329
4332
  return removedCount;
4330
4333
  }
4334
+ async clearSession() {
4335
+ if (!this.filePath) return;
4336
+ const record = `${JSON.stringify({
4337
+ type: "session_start",
4338
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4339
+ id: this.id,
4340
+ model: this.meta.model ?? "unknown",
4341
+ provider: this.meta.provider ?? "unknown"
4342
+ })}
4343
+ `;
4344
+ await fsp2.writeFile(this.filePath, record, "utf8");
4345
+ }
4331
4346
  };
4332
4347
  function userInputTitle(content) {
4333
- if (typeof content === "string") return content.slice(0, 60);
4334
- const text = content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4348
+ const text = typeof content === "string" ? content : content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4335
4349
  return (text || "(non-text input)").slice(0, 60);
4336
4350
  }
4337
4351
 
@@ -7414,7 +7428,8 @@ function formatGoal(goal, journalLimit = 10) {
7414
7428
  lines.push(`Set: ${goal.setAt}`);
7415
7429
  lines.push(`Last activity: ${goal.lastActivityAt}`);
7416
7430
  lines.push(`Iterations: ${goal.iterations}`);
7417
- lines.push(`Mission: ${goal.goalState ?? "active"}`);
7431
+ const stateLabel = goal.goalState ?? "active";
7432
+ lines.push(`State: ${stateLabel}${goal.iterations > 0 ? ` (iteration #${goal.iterations})` : ""}`);
7418
7433
  lines.push(`Engine: ${goal.engineState}`);
7419
7434
  const usage = summarizeUsage(goal);
7420
7435
  if (usage.iterationsWithUsage > 0) {
@@ -7521,23 +7536,33 @@ var EternalAutonomyEngine = class {
7521
7536
  * `agent.run()` avoids race conditions on the shared Context.
7522
7537
  */
7523
7538
  async runOneIteration() {
7539
+ const emit = (stage) => {
7540
+ this.opts.onStage?.(stage);
7541
+ };
7524
7542
  const goal = await loadGoal(this.goalPath);
7525
7543
  if (!goal) {
7544
+ emit({ phase: "stopped" });
7526
7545
  this.stopRequested = true;
7527
7546
  return false;
7528
7547
  }
7529
7548
  const missionState = goal.goalState ?? "active";
7530
7549
  if (missionState !== "active") {
7550
+ emit({ phase: missionState === "paused" ? "paused" : "stopped" });
7531
7551
  this.stopRequested = true;
7532
7552
  return false;
7533
7553
  }
7554
+ emit({ phase: "decide", reason: "picking next task" });
7534
7555
  const action = await this.decide(goal);
7535
7556
  if (!action) {
7536
7557
  if (!this.stopRequested) {
7558
+ emit({ phase: "sleep", ms: 5e3 });
7537
7559
  await sleep(5e3);
7560
+ } else {
7561
+ emit({ phase: "stopped" });
7538
7562
  }
7539
7563
  return false;
7540
7564
  }
7565
+ emit({ phase: "execute", task: action.task });
7541
7566
  const ctrl = new AbortController();
7542
7567
  this.currentCtrl = ctrl;
7543
7568
  const timer = setTimeout(
@@ -7615,6 +7640,7 @@ var EternalAutonomyEngine = class {
7615
7640
  tokens,
7616
7641
  costUsd
7617
7642
  });
7643
+ emit({ phase: "reflect", status, note });
7618
7644
  let iterationIndex = 0;
7619
7645
  try {
7620
7646
  const reloaded = await loadGoal(this.goalPath);
@@ -7636,6 +7662,7 @@ var EternalAutonomyEngine = class {
7636
7662
  this.consecutiveTransientRetries++;
7637
7663
  const delay = this.computeTransientBackoffMs();
7638
7664
  if (delay > 0) {
7665
+ emit({ phase: "sleep", ms: delay });
7639
7666
  await this.sleepInterruptible(delay);
7640
7667
  }
7641
7668
  return false;
@@ -7649,6 +7676,9 @@ var EternalAutonomyEngine = class {
7649
7676
  return false;
7650
7677
  }
7651
7678
  this.consecutiveTransientRetries = 0;
7679
+ const cycleGapMs = this.opts.cycleGapMs ?? 1e3;
7680
+ emit({ phase: "sleep", ms: cycleGapMs });
7681
+ await sleep(cycleGapMs);
7652
7682
  if (GOAL_COMPLETE_MARKER.test(finalText)) {
7653
7683
  await this.markGoalCompleted(action, finalText);
7654
7684
  this.stopRequested = true;
@@ -7964,422 +7994,32 @@ function sleep(ms) {
7964
7994
  return new Promise((resolve5) => setTimeout(resolve5, ms));
7965
7995
  }
7966
7996
 
7967
- // src/execution/autonomy-prompt-contributor.ts
7968
- function makeAutonomyPromptContributor(opts) {
7969
- return async (ctx) => {
7970
- if (ctx.subagent) return [];
7971
- if (!opts.enabled()) return [];
7972
- let goal;
7973
- try {
7974
- goal = await loadGoal(opts.goalPath);
7975
- } catch {
7976
- return [];
7977
- }
7978
- if (!goal) return [];
7979
- const missionState = goal.goalState ?? "active";
7980
- if (missionState !== "active") return [];
7981
- const tailSize = opts.journalTailSize ?? 5;
7982
- const journalTail = goal.journal.slice(-tailSize).map((e) => {
7983
- const note = e.note ? ` \u2014 ${e.note.slice(0, 80)}` : "";
7984
- return ` #${e.iteration} [${e.status}] ${e.task}${note}`;
7985
- });
7986
- const text = [
7987
- "## ETERNAL AUTONOMY \u2014 active mission",
7988
- "",
7989
- "You are inside a long-running autonomous loop. The user is asleep",
7990
- "and is not available to confirm decisions. Each turn you receive a",
7991
- "directive describing one concrete sub-task that advances the mission.",
7992
- "",
7993
- `Mission: ${goal.goal}`,
7994
- `Iteration: #${goal.iterations}`,
7995
- journalTail.length > 0 ? `Recent journal (last ${journalTail.length}):
7996
- ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first iteration)",
7997
- "",
7998
- "### Loop control markers",
7999
- "Emit these on their own line in your final text \u2014 case-insensitive,",
8000
- "whitespace-tolerant, but they must occupy the entire line:",
8001
- "- `[continue]` \u2014 chain to the next internal step without returning.",
8002
- "- `[done]` \u2014 the current sub-task is finished; return to the engine.",
8003
- "- `[GOAL_COMPLETE]` \u2014 emit ONLY when the OVERALL mission is",
8004
- " verifiably done. Must be followed by a one-paragraph verification",
8005
- " recipe (artifact path, test command, or 10-second reproduction).",
8006
- " The engine halts on this marker \u2014 false positives waste real",
8007
- " human time. If unsure, emit `[done]` and let the next iteration",
8008
- " decide.",
8009
- "",
8010
- "### Operating principles",
8011
- "- YOLO is active. Do NOT ask for confirmation, do NOT propose",
8012
- " options. Pick the best path and execute it.",
8013
- "- Use tools freely; multiple calls per turn are normal and expected.",
8014
- "- When working on a todo, mark it `in_progress` via the todos tool",
8015
- " before tool work and `completed` (or `cancelled` with a reason)",
8016
- " when done. The loop reads todo state between iterations.",
8017
- "- If an approach fails twice in a row, pivot. Don't grind on the",
8018
- " same wall \u2014 try a different angle, file a cancel on the todo, or",
8019
- " surface the obstacle via `[done]` and let the next iteration",
8020
- " re-plan."
8021
- ].join("\n");
8022
- return [
8023
- {
8024
- type: "text",
8025
- text,
8026
- cache_control: { type: "ephemeral" }
8027
- }
8028
- ];
8029
- };
8030
- }
8031
-
8032
- // src/execution/goal-preamble.ts
8033
- function buildGoalPreamble(goal) {
8034
- return [
8035
- "[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
8036
- "The user granted you full autonomy. Read these constraints once, then act.",
8037
- "",
8038
- "YOUR GOAL:",
8039
- "---",
8040
- goal,
8041
- "---",
8042
- "",
8043
- "AUTHORITY YOU HAVE:",
8044
- "- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
8045
- " Parallel + recursive fan-out are both fine. There is no spawn budget.",
8046
- "- Use any provider/model per subagent \u2014 pick the right tool for each",
8047
- " piece of work. Heavy reasoning model for planning, fast model for",
8048
- " batch work, specialist model for domain code.",
8049
- "- Run unlimited tool calls and iterations. There is NO hidden budget.",
8050
- " The Agent loop auto-extends every 100 iterations forever.",
8051
- "- Retry failed tools with different inputs, alternative paths, fresh",
8052
- " subagents. Switch providers mid-run if one is rate-limited.",
8053
- "- Re-plan freely when an approach hits a dead end. You are not obliged",
8054
- " to stick with the first plan you proposed.",
8055
- "",
8056
- 'WHAT "DONE" MEANS \u2014 non-negotiable:',
8057
- "- You can name a concrete artifact (a passing test, a written file at",
8058
- " a specific path, a fixed bug verified by re-running the failing case,",
8059
- " a clean grep that previously had matches).",
8060
- "- You can tell the user HOW to verify it themselves in 10 seconds.",
8061
- '- You have NOT hedged. None of: "looks like it should work", "I',
8062
- ' believe this fixes it", "the changes appear correct".',
8063
- "",
8064
- "WHAT IS NOT DONE \u2014 never report any of these as completion:",
8065
- "- An error message you didn't recover from.",
8066
- '- An empty result, a 0-line file, a "no matches found" you accepted',
8067
- " without questioning the search.",
8068
- '- "Should I continue?" / "Want me to also...?" / "Let me know if you',
8069
- ' want X." Those are hedges. The user already told you to finish the',
8070
- " goal \u2014 just do it.",
8071
- "- Partial progress dressed up as success. Fixed 3 of 5 bugs = 60%",
8072
- " done, not done.",
8073
- "- A subagent's failed/timeout/stopped TaskResult that you didn't",
8074
- " respond to with a fresh attempt (different role, different model,",
8075
- " tighter prompt).",
8076
- "",
8077
- "PERSISTENCE PROTOCOL:",
8078
- "- If blocked, try at least 3 different angles before reporting the",
8079
- " problem to the user. Different tool inputs, different subagent",
8080
- " roles, different providers, different decomposition of the task.",
8081
- "- If a tool fails, read its error, alter the input, try again. Do",
8082
- " not just report the failure back.",
8083
- "- If a subagent returns useless output, respawn with a tighter prompt",
8084
- ' or a different role. Do not accept "I could not determine\u2026" as the',
8085
- " final answer.",
8086
- "- Use `ask_subagent` for one-shot questions when you don't need a",
8087
- " full delegated task.",
8088
- "",
8089
- "REPORTING:",
8090
- "- Stream short progress notes between major actions so the user can",
8091
- " monitor. Do not go silent for 50 tool calls then dump a wall of",
8092
- " text \u2014 but also do not narrate every tool call.",
8093
- "- Use the shared scratchpad (if available) to leave breadcrumbs",
8094
- " subagents can read.",
8095
- "- Final response must include: (a) what was accomplished, (b) how",
8096
- " to verify, (c) any caveats (residual TODOs, things the user",
8097
- " should know about).",
8098
- "",
8099
- "BEGIN.]"
8100
- ].join("\n");
8101
- }
8102
-
8103
- // src/coordination/director.ts
8104
- init_atomic_write();
8105
-
8106
- // src/coordination/director-prompts.ts
8107
- var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
8108
- subagents by spawning them, assigning tasks, awaiting completions, and
8109
- rolling up their outputs into your next decision.
8110
-
8111
- Core fleet tools available to you:
8112
- - spawn_subagent \u2014 create a worker with a chosen provider / model / role
8113
- - assign_task \u2014 hand a piece of work to a specific subagent
8114
- - await_tasks \u2014 block until named task ids complete (parallel-safe)
8115
- - ask_subagent \u2014 synchronously query a running subagent via the bridge
8116
- - roll_up \u2014 aggregate finished tasks into a markdown/json summary
8117
- - terminate_subagent \u2014 abort a stuck worker (use sparingly)
8118
- - fleet_status \u2014 snapshot of all subagents and pending tasks
8119
- - fleet_usage \u2014 token + cost breakdown per subagent and total
8120
-
8121
- Working rules:
8122
- 1. Decompose first. Before spawning, decide which sub-tasks are
8123
- independent and can run in parallel. Sequential work doesn't need a
8124
- subagent \u2014 do it yourself.
8125
- 2. Match worker to job. Cheap/fast model for triage, capable model for
8126
- synthesis. Different providers per sibling is allowed and encouraged.
8127
- 3. Always pair an assign with an await. Don't fire-and-forget; you owe
8128
- the user a single coherent answer at the end.
8129
- 4. Roll up before deciding. After await_tasks resolves, call roll_up so
8130
- the results are folded back into your context in a compact form.
8131
- 5. Budget is real. Check fleet_usage periodically. If a subagent is
8132
- thrashing, terminate it rather than letting cost climb silently.
8133
- 6. Never claim a subagent's work as your own without verifying it. If a
8134
- result looks wrong, ask_subagent for clarification before passing it
8135
- to the user.`;
8136
- var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
8137
- a specific slice of a larger plan \u2014 do that slice well and report back.
8138
-
8139
- Bridge contract:
8140
- - You have a parent (the Director). You may call \`request\` on the
8141
- parent bridge to ask a clarifying question. Use this sparingly; the
8142
- parent is also working.
8143
- - You MAY NOT request the parent's system prompt, tool list, or other
8144
- subagents' context. Those are not yours to read.
8145
- - Your final task output is what the Director sees. Be concise,
8146
- structured, and self-contained \u2014 assume the Director will paste your
8147
- output into its own context.`;
8148
- function composeDirectorPrompt(parts = {}) {
8149
- const sections = [];
8150
- const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
8151
- if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
8152
- if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
8153
- sections.push(`Available roles you can spawn:
8154
- ${parts.rosterSummary.trim()}`);
8155
- }
8156
- if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
8157
- sections.push(parts.basePrompt.trim());
8158
- }
8159
- return sections.join("\n\n");
8160
- }
8161
- function composeSubagentPrompt(parts = {}) {
8162
- const sections = [];
8163
- const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
8164
- if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
8165
- if (parts.role && parts.role.trim().length > 0) {
8166
- sections.push(`Role:
8167
- ${parts.role.trim()}`);
7997
+ // src/coordination/subagent-budget.ts
7998
+ var BudgetExceededError = class extends Error {
7999
+ kind;
8000
+ limit;
8001
+ observed;
8002
+ constructor(kind, limit, observed) {
8003
+ super(`Budget exceeded: ${kind} (limit=${limit}, observed=${observed})`);
8004
+ this.name = "BudgetExceededError";
8005
+ this.kind = kind;
8006
+ this.limit = limit;
8007
+ this.observed = observed;
8168
8008
  }
8169
- if (parts.task && parts.task.trim().length > 0) {
8170
- sections.push(`Task:
8171
- ${parts.task.trim()}`);
8172
- }
8173
- if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
8174
- sections.push(
8175
- `Shared notes:
8176
- A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
8177
- - Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
8178
- - Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
8179
- - Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
8180
- );
8181
- }
8182
- if (parts.override && parts.override.trim().length > 0) {
8183
- sections.push(parts.override.trim());
8184
- }
8185
- return sections.join("\n\n");
8186
- }
8187
- function rosterSummaryFromConfigs(roster) {
8188
- const lines = [];
8189
- for (const [roleId, cfg] of Object.entries(roster)) {
8190
- const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
8191
- const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
8192
- const tail = headline ? ` \u2014 ${headline}` : "";
8193
- lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
8194
- }
8195
- return lines.join("\n");
8196
- }
8197
-
8198
- // src/coordination/fleet-bus.ts
8199
- var FleetBus = class {
8200
- byId = /* @__PURE__ */ new Map();
8201
- byType = /* @__PURE__ */ new Map();
8202
- any = /* @__PURE__ */ new Set();
8203
- /**
8204
- * Hook a subagent's EventBus into the fleet. EventBus is strongly
8205
- * typed and doesn't expose an `onAny` hook, so we subscribe to the
8206
- * canonical set of event types a subagent emits during a run. New
8207
- * event types added to the kernel must be added here too — but the
8208
- * cost is a tiny single line per type, and the explicit list keeps
8209
- * the wire format clear.
8210
- *
8211
- * Returns a disposer that detaches every subscription; call on
8212
- * subagent teardown so the listeners don't outlive the run.
8213
- */
8214
- attach(subagentId, bus, taskId) {
8215
- const off = bus.onPattern("*", (type, payload) => {
8216
- if (type.startsWith("subagent.")) return;
8217
- this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
8218
- });
8219
- return () => {
8220
- off();
8221
- };
8222
- }
8223
- /** Subscribe to every event from one subagent. */
8224
- subscribe(subagentId, handler) {
8225
- let set = this.byId.get(subagentId);
8226
- if (!set) {
8227
- set = /* @__PURE__ */ new Set();
8228
- this.byId.set(subagentId, set);
8229
- }
8230
- set.add(handler);
8231
- return () => {
8232
- set.delete(handler);
8233
- };
8234
- }
8235
- /** Subscribe to one event type across all subagents. */
8236
- filter(type, handler) {
8237
- let set = this.byType.get(type);
8238
- if (!set) {
8239
- set = /* @__PURE__ */ new Set();
8240
- this.byType.set(type, set);
8241
- }
8242
- set.add(handler);
8243
- return () => {
8244
- set.delete(handler);
8245
- };
8246
- }
8247
- /** Subscribe to literally everything. The fleet roll-up uses this. */
8248
- onAny(handler) {
8249
- this.any.add(handler);
8250
- return () => {
8251
- this.any.delete(handler);
8252
- };
8253
- }
8254
- emit(event) {
8255
- const byId = this.byId.get(event.subagentId);
8256
- if (byId)
8257
- for (const h of byId) {
8258
- try {
8259
- h(event);
8260
- } catch {
8261
- }
8262
- }
8263
- const byType = this.byType.get(event.type);
8264
- if (byType)
8265
- for (const h of byType) {
8266
- try {
8267
- h(event);
8268
- } catch {
8269
- }
8270
- }
8271
- for (const h of this.any) {
8272
- try {
8273
- h(event);
8274
- } catch {
8275
- }
8276
- }
8277
- }
8278
- };
8279
- var FleetUsageAggregator = class {
8280
- constructor(bus, priceLookup, metaLookup) {
8281
- this.bus = bus;
8282
- this.priceLookup = priceLookup;
8283
- this.metaLookup = metaLookup;
8284
- bus.filter("provider.response", (e) => this.onProviderResponse(e));
8285
- bus.filter("tool.executed", (e) => this.onToolExecuted(e));
8286
- bus.filter("iteration.started", (e) => this.onIterationStarted(e));
8287
- }
8288
- bus;
8289
- priceLookup;
8290
- metaLookup;
8291
- perSubagent = /* @__PURE__ */ new Map();
8292
- total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
8293
- /** Live snapshot — safe to call from a tool's execute() body. */
8294
- snapshot() {
8295
- return {
8296
- total: { ...this.total },
8297
- perSubagent: Object.fromEntries(
8298
- Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
8299
- )
8300
- };
8301
- }
8302
- ensure(subagentId) {
8303
- let snap = this.perSubagent.get(subagentId);
8304
- if (!snap) {
8305
- const meta = this.metaLookup?.(subagentId);
8306
- snap = {
8307
- subagentId,
8308
- provider: meta?.provider,
8309
- model: meta?.model,
8310
- input: 0,
8311
- output: 0,
8312
- cacheRead: 0,
8313
- cacheWrite: 0,
8314
- cost: 0,
8315
- toolCalls: 0,
8316
- iterations: 0,
8317
- startedAt: Date.now(),
8318
- lastEventAt: Date.now()
8319
- };
8320
- this.perSubagent.set(subagentId, snap);
8321
- }
8322
- return snap;
8323
- }
8324
- onProviderResponse(e) {
8325
- const snap = this.ensure(e.subagentId);
8326
- const p = e.payload;
8327
- const usage = p?.usage;
8328
- if (!usage) return;
8329
- snap.input += usage.input ?? 0;
8330
- snap.output += usage.output ?? 0;
8331
- snap.cacheRead += usage.cacheRead ?? 0;
8332
- snap.cacheWrite += usage.cacheWrite ?? 0;
8333
- this.total.input += usage.input ?? 0;
8334
- this.total.output += usage.output ?? 0;
8335
- this.total.cacheRead += usage.cacheRead ?? 0;
8336
- this.total.cacheWrite += usage.cacheWrite ?? 0;
8337
- const price = this.priceLookup?.(e.subagentId);
8338
- if (price) {
8339
- const delta = (usage.input ?? 0) / 1e6 * (price.input ?? 0) + (usage.output ?? 0) / 1e6 * (price.output ?? 0) + (usage.cacheRead ?? 0) / 1e6 * (price.cacheRead ?? 0) + (usage.cacheWrite ?? 0) / 1e6 * (price.cacheWrite ?? 0);
8340
- snap.cost += delta;
8341
- this.total.cost += delta;
8342
- }
8343
- snap.lastEventAt = e.ts;
8344
- }
8345
- onToolExecuted(e) {
8346
- const snap = this.ensure(e.subagentId);
8347
- snap.toolCalls += 1;
8348
- snap.lastEventAt = e.ts;
8349
- }
8350
- onIterationStarted(e) {
8351
- const snap = this.ensure(e.subagentId);
8352
- snap.iterations += 1;
8353
- snap.lastEventAt = e.ts;
8354
- }
8355
- };
8356
-
8357
- // src/coordination/subagent-budget.ts
8358
- var BudgetExceededError = class extends Error {
8359
- kind;
8360
- limit;
8361
- observed;
8362
- constructor(kind, limit, observed) {
8363
- super(`Budget exceeded: ${kind} (limit=${limit}, observed=${observed})`);
8364
- this.name = "BudgetExceededError";
8365
- this.kind = kind;
8366
- this.limit = limit;
8367
- this.observed = observed;
8368
- }
8369
- };
8370
- var BudgetThresholdSignal = class extends Error {
8371
- kind;
8372
- limit;
8373
- used;
8374
- /** Resolves to 'extend' (with optional new limits) or 'stop' */
8375
- decision;
8376
- constructor(kind, limit, used, decision) {
8377
- super(`Budget soft limit: ${kind} (limit=${limit}, used=${used})`);
8378
- this.name = "BudgetThresholdSignal";
8379
- this.kind = kind;
8380
- this.limit = limit;
8381
- this.used = used;
8382
- this.decision = decision;
8009
+ };
8010
+ var BudgetThresholdSignal = class extends Error {
8011
+ kind;
8012
+ limit;
8013
+ used;
8014
+ /** Resolves to 'extend' (with optional new limits) or 'stop' */
8015
+ decision;
8016
+ constructor(kind, limit, used, decision) {
8017
+ super(`Budget soft limit: ${kind} (limit=${limit}, used=${used})`);
8018
+ this.name = "BudgetThresholdSignal";
8019
+ this.kind = kind;
8020
+ this.limit = limit;
8021
+ this.used = used;
8022
+ this.decision = decision;
8383
8023
  }
8384
8024
  };
8385
8025
  var SubagentBudget = class _SubagentBudget {
@@ -8450,7 +8090,7 @@ var SubagentBudget = class _SubagentBudget {
8450
8090
  * is dispatched as a fire-and-forget promise.
8451
8091
  */
8452
8092
  checkLimit(kind, used, limit) {
8453
- if (kind === "timeout" || !this._onThreshold) {
8093
+ if (!this._onThreshold) {
8454
8094
  throw new BudgetExceededError(kind, limit, used);
8455
8095
  }
8456
8096
  const bus = this._events;
@@ -8566,14 +8206,18 @@ var SubagentBudget = class _SubagentBudget {
8566
8206
  }
8567
8207
  }
8568
8208
  /**
8569
- * Throws if the wall-clock budget is exhausted. Call this from the iteration
8570
- * loop so a hung tool can't keep a subagent running past its deadline.
8209
+ * Wall-clock budget check. Unlike other limits, timeout is treated as a
8210
+ * warning-only event it NEVER hard-stops the subagent. When the
8211
+ * elapsed time exceeds timeoutMs, emits `budget.threshold_reached` with
8212
+ * kind='timeout' so the Director can decide whether to extend or warn.
8213
+ * Call this from the iteration loop so a hung tool gets a chance to
8214
+ * negotiate more time before the coordinator's Promise.race kills it.
8571
8215
  */
8572
8216
  checkTimeout() {
8573
8217
  if (this.startTime === null || this.limits.timeoutMs === void 0) return;
8574
8218
  const elapsed = Date.now() - this.startTime;
8575
8219
  if (elapsed > this.limits.timeoutMs) {
8576
- throw new BudgetExceededError("timeout", this.limits.timeoutMs, elapsed);
8220
+ void this.checkLimit("timeout", elapsed, this.limits.timeoutMs);
8577
8221
  }
8578
8222
  }
8579
8223
  /** Returns true if a timeout has occurred without throwing. Useful for races. */
@@ -8596,55 +8240,223 @@ var SubagentBudget = class _SubagentBudget {
8596
8240
  }
8597
8241
  };
8598
8242
 
8599
- // src/coordination/fleet.ts
8600
- var AUDIT_LOG_AGENT = {
8601
- id: "audit-log",
8602
- name: "Audit Log",
8603
- role: "audit-log",
8604
- prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
8605
- session logs and produce actionable markdown reports.
8606
-
8607
- Scope:
8608
- - Parse session logs (iteration counts, tool calls, errors, usage)
8609
- - Detect repeated failure patterns across multiple runs
8610
- - Identify tool usage anomalies (over-use, failures, unexpected chains)
8611
- - Track token consumption trends
8612
- - Generate structured audit reports with severity ratings
8613
-
8614
- Input format you accept:
8615
- { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
8616
-
8617
- Output: Markdown audit report with sections:
8618
- - ## Summary (totals, error rate)
8619
- - ## Top Errors (count + context)
8620
- - ## Tool Usage (table with calls, failures, avg duration)
8621
- - ## Anomalies (pattern \u2192 severity)
8622
-
8623
- Working rules:
8624
- - Never fabricate numbers \u2014 read the actual logs first
8625
- - Always include file:line references for errors
8626
- - If sessionPath is missing, ask the director to provide it
8627
- - Report confidence level: high (>90% accuracy), medium, low`
8628
- // No hardcoded budgets — the orchestrator (delegate tool or
8629
- // spawn_subagent) decides per-task how much room a subagent gets.
8630
- // A monorepo audit needs hours; a single-file lint check needs
8631
- // seconds. Pinning a number here forces the orchestrator to fight
8632
- // the role's default instead of just asking for what it needs.
8633
- };
8634
- var BUG_HUNTER_AGENT = {
8635
- id: "bug-hunter",
8636
- name: "Bug Hunter",
8637
- role: "bug-hunter",
8638
- prompt: `You are the Bug Hunter agent. Your job is to systematically scan
8639
- source code for bugs, anti-patterns, and code smells using pattern matching
8640
- and heuristics. Output a prioritized hit list with file:line references.
8641
-
8642
- Scope:
8643
- - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
8644
- - Identify anti-patterns (callback hell, God objects, circular deps)
8645
- - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
8646
- - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
8647
- - Rank findings: critical > high > medium > low
8243
+ // src/coordination/agent-subagent-runner.ts
8244
+ function makeAgentSubagentRunner(opts) {
8245
+ const format = opts.formatTaskInput ?? defaultFormatTaskInput;
8246
+ return async (task, ctx) => {
8247
+ const factoryResult = await opts.factory(ctx.config);
8248
+ const { agent, events } = factoryResult;
8249
+ const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
8250
+ const aborter = new AbortController();
8251
+ ctx.budget._events = events;
8252
+ ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
8253
+ let budgetError = null;
8254
+ const onBudgetError = (err) => {
8255
+ if (err instanceof BudgetThresholdSignal) {
8256
+ err.decision.then((decision) => {
8257
+ if (decision === "stop") {
8258
+ budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
8259
+ aborter.abort();
8260
+ }
8261
+ }).catch(() => {
8262
+ budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
8263
+ aborter.abort();
8264
+ });
8265
+ return;
8266
+ }
8267
+ aborter.abort();
8268
+ budgetError = err instanceof BudgetExceededError ? err : new BudgetExceededError(
8269
+ "tool_calls",
8270
+ 0,
8271
+ 0
8272
+ );
8273
+ if (budgetError !== err && err instanceof Error) {
8274
+ budgetError.message += ` (caused by: ${err.message})`;
8275
+ }
8276
+ };
8277
+ let lastToolFailed = null;
8278
+ const unsub = [];
8279
+ unsub.push(
8280
+ events.on("tool.executed", (e) => {
8281
+ try {
8282
+ ctx.budget.recordToolCall();
8283
+ } catch (eb) {
8284
+ onBudgetError(eb);
8285
+ }
8286
+ if (e.ok === false) {
8287
+ lastToolFailed = e.name;
8288
+ } else if (e.ok === true) {
8289
+ lastToolFailed = null;
8290
+ }
8291
+ }),
8292
+ events.on("provider.response", (e) => {
8293
+ try {
8294
+ ctx.budget.recordUsage(e.usage);
8295
+ } catch (e2) {
8296
+ void onBudgetError(e2);
8297
+ }
8298
+ }),
8299
+ events.on("iteration.started", () => {
8300
+ try {
8301
+ ctx.budget.recordIteration();
8302
+ } catch (e) {
8303
+ void onBudgetError(e);
8304
+ }
8305
+ const u = ctx.budget.usage();
8306
+ const since = u.iterations - lastSummaryAtIteration;
8307
+ if (since >= SUMMARY_INTERVAL) {
8308
+ lastSummaryAtIteration = u.iterations;
8309
+ events.emit("subagent.iteration_summary", {
8310
+ subagentId: ctx.subagentId,
8311
+ iteration: u.iterations,
8312
+ toolCalls: u.toolCalls,
8313
+ costUsd: u.costUsd,
8314
+ currentTool: currentToolName,
8315
+ partialText: streamingTextAcc.trim() || void 0
8316
+ });
8317
+ }
8318
+ }),
8319
+ // D3: cooperative timeout enforcement DURING a long tool call.
8320
+ // The iteration-loop checkTimeout() only fires between agent
8321
+ // iterations — a single `bash sleep 3600` call would otherwise
8322
+ // park inside one tool execution while the timeout silently
8323
+ // passes, relying solely on the coordinator's hard Promise.race
8324
+ // to interrupt. Tools that emit `tool.progress` (bash chunks,
8325
+ // fetch byte progress, spawn-stream stdout) give us a heartbeat
8326
+ // we can hang the check on. When the budget trips here:
8327
+ // 1. onBudgetError sets budgetError + aborter.abort()
8328
+ // 2. aborter signal propagates to agent.run → tool executor
8329
+ // 3. tool's own signal listener kills the child process
8330
+ // Cheap: O(1) per progress event, and the budget short-circuits
8331
+ // when timeoutMs is unset (most subagents have one set anyway).
8332
+ events.on("tool.progress", () => {
8333
+ try {
8334
+ ctx.budget.checkTimeout();
8335
+ } catch (e) {
8336
+ void onBudgetError(e);
8337
+ }
8338
+ })
8339
+ );
8340
+ let currentToolName;
8341
+ let streamingTextAcc = "";
8342
+ let lastSummaryAtIteration = 0;
8343
+ const SUMMARY_INTERVAL = 25;
8344
+ unsub.push(
8345
+ events.on("tool.started", (e) => {
8346
+ currentToolName = e.name;
8347
+ }),
8348
+ events.on("provider.text_delta", (e) => {
8349
+ streamingTextAcc = (streamingTextAcc + e.text).slice(-200);
8350
+ })
8351
+ );
8352
+ const onParentAbort = () => aborter.abort();
8353
+ ctx.signal.addEventListener("abort", onParentAbort);
8354
+ let result;
8355
+ try {
8356
+ result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
8357
+ } finally {
8358
+ detachFleet?.();
8359
+ ctx.signal.removeEventListener("abort", onParentAbort);
8360
+ for (const u of unsub) u();
8361
+ if (factoryResult.dispose) {
8362
+ try {
8363
+ await factoryResult.dispose();
8364
+ } catch {
8365
+ }
8366
+ }
8367
+ }
8368
+ if (budgetError) {
8369
+ if ("decision" in budgetError) {
8370
+ const decision = await budgetError.decision;
8371
+ if (decision === "stop") {
8372
+ budgetError = new BudgetExceededError(
8373
+ budgetError.kind,
8374
+ budgetError.limit,
8375
+ budgetError.used
8376
+ );
8377
+ } else {
8378
+ budgetError = null;
8379
+ }
8380
+ }
8381
+ if (budgetError) throw budgetError;
8382
+ }
8383
+ if (result.status === "failed") {
8384
+ throw result.error instanceof Error ? result.error : new Error(String(result.error ?? "agent failed"));
8385
+ }
8386
+ if (result.status === "aborted") {
8387
+ throw new Error("agent aborted");
8388
+ }
8389
+ if (result.status === "max_iterations") {
8390
+ throw new Error("agent exhausted iteration limit");
8391
+ }
8392
+ const usage = ctx.budget.usage();
8393
+ const finalText = (result.finalText ?? "").trim();
8394
+ if (finalText.length === 0 && usage.toolCalls === 0) {
8395
+ throw new Error("empty response");
8396
+ }
8397
+ if (finalText.length === 0 && lastToolFailed !== null) {
8398
+ throw new Error(`tool failed: ${lastToolFailed}`);
8399
+ }
8400
+ return {
8401
+ result: result.finalText,
8402
+ iterations: result.iterations,
8403
+ toolCalls: usage.toolCalls
8404
+ };
8405
+ };
8406
+ }
8407
+ function defaultFormatTaskInput(task) {
8408
+ return task.description ?? "";
8409
+ }
8410
+
8411
+ // src/coordination/fleet.ts
8412
+ var AUDIT_LOG_AGENT = {
8413
+ id: "audit-log",
8414
+ name: "Audit Log",
8415
+ role: "audit-log",
8416
+ prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
8417
+ session logs and produce actionable markdown reports.
8418
+
8419
+ Scope:
8420
+ - Parse session logs (iteration counts, tool calls, errors, usage)
8421
+ - Detect repeated failure patterns across multiple runs
8422
+ - Identify tool usage anomalies (over-use, failures, unexpected chains)
8423
+ - Track token consumption trends
8424
+ - Generate structured audit reports with severity ratings
8425
+
8426
+ Input format you accept:
8427
+ { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
8428
+
8429
+ Output: Markdown audit report with sections:
8430
+ - ## Summary (totals, error rate)
8431
+ - ## Top Errors (count + context)
8432
+ - ## Tool Usage (table with calls, failures, avg duration)
8433
+ - ## Anomalies (pattern \u2192 severity)
8434
+
8435
+ Working rules:
8436
+ - Never fabricate numbers \u2014 read the actual logs first
8437
+ - Always include file:line references for errors
8438
+ - If sessionPath is missing, ask the director to provide it
8439
+ - Report confidence level: high (>90% accuracy), medium, low`
8440
+ // No hardcoded budgets — the orchestrator (delegate tool or
8441
+ // spawn_subagent) decides per-task how much room a subagent gets.
8442
+ // A monorepo audit needs hours; a single-file lint check needs
8443
+ // seconds. Pinning a number here forces the orchestrator to fight
8444
+ // the role's default instead of just asking for what it needs.
8445
+ };
8446
+ var BUG_HUNTER_AGENT = {
8447
+ id: "bug-hunter",
8448
+ name: "Bug Hunter",
8449
+ role: "bug-hunter",
8450
+ prompt: `You are the Bug Hunter agent. Your job is to systematically scan
8451
+ source code for bugs, anti-patterns, and code smells using pattern matching
8452
+ and heuristics. Output a prioritized hit list with file:line references.
8453
+
8454
+ Scope:
8455
+ - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
8456
+ - Identify anti-patterns (callback hell, God objects, circular deps)
8457
+ - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
8458
+ - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
8459
+ - Rank findings: critical > high > medium > low
8648
8460
 
8649
8461
  Input format you accept:
8650
8462
  { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
@@ -8769,10 +8581,10 @@ var FLEET_ROSTER = {
8769
8581
  "security-scanner": SECURITY_SCANNER_AGENT
8770
8582
  };
8771
8583
  var FLEET_ROSTER_BUDGETS = {
8772
- "audit-log": { timeoutMs: 5 * 60 * 1e3, maxIterations: 80, maxToolCalls: 300 },
8773
- "bug-hunter": { timeoutMs: 10 * 60 * 1e3, maxIterations: 120, maxToolCalls: 400 },
8774
- "refactor-planner": { timeoutMs: 8 * 60 * 1e3, maxIterations: 100, maxToolCalls: 350 },
8775
- "security-scanner": { timeoutMs: 10 * 60 * 1e3, maxIterations: 120, maxToolCalls: 400 }
8584
+ "audit-log": { timeoutMs: 8 * 60 * 1e3, maxIterations: 150, maxToolCalls: 500 },
8585
+ "bug-hunter": { timeoutMs: 15 * 60 * 1e3, maxIterations: 200, maxToolCalls: 600 },
8586
+ "refactor-planner": { timeoutMs: 12 * 60 * 1e3, maxIterations: 180, maxToolCalls: 550 },
8587
+ "security-scanner": { timeoutMs: 15 * 60 * 1e3, maxIterations: 200, maxToolCalls: 600 }
8776
8588
  };
8777
8589
  function applyRosterBudget(cfg) {
8778
8590
  const defaultBudget = FLEET_ROSTER_BUDGETS[cfg.role ?? ""];
@@ -8912,6 +8724,33 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
8912
8724
  results() {
8913
8725
  return this.completedResults;
8914
8726
  }
8727
+ /**
8728
+ * Wait for one or more tasks to complete and return their results.
8729
+ * If a task is already done when called, returns immediately.
8730
+ * Resolves to an array in the same order as `taskIds`.
8731
+ */
8732
+ async awaitTasks(taskIds) {
8733
+ return Promise.all(
8734
+ taskIds.map((id) => {
8735
+ const cached = this.completedResults.find((r) => r.taskId === id);
8736
+ if (cached) return cached;
8737
+ return new Promise((resolve5, reject) => {
8738
+ const timeout = setTimeout(() => {
8739
+ this.off("task.completed", handler);
8740
+ reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
8741
+ }, this.config.timeoutMs ?? 3e5);
8742
+ const handler = ({ result }) => {
8743
+ if (result.taskId === id) {
8744
+ clearTimeout(timeout);
8745
+ this.off("task.completed", handler);
8746
+ resolve5(result);
8747
+ }
8748
+ };
8749
+ this.on("task.completed", handler);
8750
+ });
8751
+ })
8752
+ );
8753
+ }
8915
8754
  /**
8916
8755
  * Manual completion — for callers that drive subagents without a runner
8917
8756
  * (e.g. external orchestrators). When a runner is configured the coordinator
@@ -9130,8 +8969,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9130
8969
  return;
9131
8970
  }
9132
8971
  if (decision === "throw" || decision === "stop") {
9133
- this.subagents.get(ctx.subagentId)?.abortController.abort();
9134
- reject(new BudgetExceededError("timeout", limit, elapsed));
8972
+ armFor(Math.max(1e3, limit));
9135
8973
  return;
9136
8974
  }
9137
8975
  if (decision.extend.timeoutMs !== void 0) {
@@ -9258,46 +9096,758 @@ function classifySubagentError(err, hints = {}) {
9258
9096
  if (lower.includes("bridge transport") || /bridge.*(closed|disconnect)/i.test(baseMessage)) {
9259
9097
  return { kind: "bridge_failed", message: baseMessage, retryable: false, cause };
9260
9098
  }
9261
- if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
9262
- return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
9099
+ if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
9100
+ return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
9101
+ }
9102
+ return {
9103
+ kind: "unknown",
9104
+ message: baseMessage,
9105
+ retryable: false,
9106
+ cause
9107
+ };
9108
+ }
9109
+ function providerErrorToSubagentError(err, message, cause) {
9110
+ const status = err.status;
9111
+ if (status === 429 || err.body?.type === "rate_limit_error") {
9112
+ return {
9113
+ kind: "provider_rate_limit",
9114
+ message,
9115
+ retryable: true,
9116
+ // Conservative default: 5s. Provider-specific code can override
9117
+ // by emitting an error whose body carries an explicit hint.
9118
+ backoffMs: 5e3,
9119
+ cause
9120
+ };
9121
+ }
9122
+ if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
9123
+ return { kind: "provider_auth", message, retryable: false, cause };
9124
+ }
9125
+ if (status === 408 || status === 0) {
9126
+ return { kind: "provider_timeout", message, retryable: true, cause };
9127
+ }
9128
+ if (status >= 500 && status < 600) {
9129
+ return {
9130
+ kind: "provider_5xx",
9131
+ message,
9132
+ retryable: true,
9133
+ backoffMs: 3e3,
9134
+ cause
9135
+ };
9136
+ }
9137
+ return { kind: "unknown", message, retryable: err.retryable, cause };
9138
+ }
9139
+
9140
+ // src/execution/parallel-eternal-engine.ts
9141
+ function sleep2(ms) {
9142
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
9143
+ }
9144
+ var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
9145
+ var ParallelEternalEngine = class {
9146
+ constructor(opts) {
9147
+ this.opts = opts;
9148
+ this.goalPath = goalFilePath(opts.projectRoot);
9149
+ this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
9150
+ this.timeoutMs = opts.iterationTimeoutMs ?? 3e5;
9151
+ this.agentFactory = opts.subagentFactory ?? (async (config) => ({
9152
+ agent: this.opts.agent,
9153
+ events: this.opts.agent.events
9154
+ }));
9155
+ }
9156
+ opts;
9157
+ state = "idle";
9158
+ stopRequested = false;
9159
+ iterationsSinceCompact = 0;
9160
+ iterations = 0;
9161
+ consecutiveFailures = 0;
9162
+ goalPath;
9163
+ slots;
9164
+ timeoutMs;
9165
+ coordinator = null;
9166
+ agentFactory;
9167
+ get currentState() {
9168
+ return this.state;
9169
+ }
9170
+ stop() {
9171
+ this.stopRequested = true;
9172
+ void this.persistState("stopped").catch(() => {
9173
+ });
9174
+ this.state = "stopped";
9175
+ }
9176
+ async prime() {
9177
+ this.stopRequested = false;
9178
+ this.state = "running";
9179
+ await this.persistState("running");
9180
+ }
9181
+ async run() {
9182
+ this.state = "running";
9183
+ await this.persistState("running");
9184
+ const config = {
9185
+ coordinatorId: `parallel-${randomUUID().slice(0, 8)}`,
9186
+ maxConcurrent: this.slots,
9187
+ doneCondition: { type: "all_tasks_done" }
9188
+ };
9189
+ this.coordinator = new DefaultMultiAgentCoordinator(config);
9190
+ const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
9191
+ this.coordinator.setRunner?.(runner);
9192
+ try {
9193
+ while (!this.stopRequested) {
9194
+ try {
9195
+ await this.runOneIteration();
9196
+ } catch (err) {
9197
+ this.consecutiveFailures++;
9198
+ this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
9199
+ await this.appendFailure("engine error", err instanceof Error ? err.message : String(err));
9200
+ }
9201
+ if (this.stopRequested) break;
9202
+ await sleep2(2e3);
9203
+ }
9204
+ } finally {
9205
+ this.state = "stopped";
9206
+ await this.persistState("stopped").catch(() => {
9207
+ });
9208
+ }
9209
+ }
9210
+ /**
9211
+ * Execute one tick: decompose → fan-out → aggregate → compact.
9212
+ * Called by the REPL in its main loop (REPL drives, engine is stateless per tick).
9213
+ */
9214
+ async runOneIteration() {
9215
+ this.iterations++;
9216
+ const goal = await loadGoal(this.goalPath);
9217
+ if (!goal) {
9218
+ this.stopRequested = true;
9219
+ return false;
9220
+ }
9221
+ if (goal.goalState !== "active") {
9222
+ this.stopRequested = true;
9223
+ return false;
9224
+ }
9225
+ if (!this.coordinator) {
9226
+ const config = {
9227
+ coordinatorId: `parallel-${randomUUID().slice(0, 8)}`,
9228
+ maxConcurrent: this.slots,
9229
+ doneCondition: { type: "all_tasks_done" }
9230
+ };
9231
+ this.coordinator = new DefaultMultiAgentCoordinator(config);
9232
+ const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
9233
+ this.coordinator.setRunner?.(runner);
9234
+ }
9235
+ const tasks = await this.decomposeGoal(goal);
9236
+ if (!tasks || tasks.length === 0) {
9237
+ await sleep2(5e3);
9238
+ return false;
9239
+ }
9240
+ const fanOut = await this.fanOut(goal, tasks);
9241
+ this.iterationsSinceCompact++;
9242
+ const successCount = fanOut.results.filter((r) => r.status === "success").length;
9243
+ const status = fanOut.goalComplete ? "success" : fanOut.allSuccessful ? "success" : "failure";
9244
+ const note = [
9245
+ `${successCount}/${fanOut.results.length} subagents succeeded`,
9246
+ fanOut.goalComplete ? "[GOAL_COMPLETE]" : "",
9247
+ fanOut.partialOutput ? `Output: ${fanOut.partialOutput.slice(0, 120)}` : ""
9248
+ ].filter(Boolean).join(" | ");
9249
+ await this.appendIterationEntry({
9250
+ source: "parallel",
9251
+ task: `parallel:${tasks.length} slots \u2014 ${tasks.slice(0, 3).join(", ")}${tasks.length > 3 ? "..." : ""}`,
9252
+ status,
9253
+ note
9254
+ });
9255
+ if (fanOut.goalComplete) {
9256
+ this.stopRequested = true;
9257
+ return true;
9258
+ }
9259
+ await this.maybeCompact();
9260
+ return fanOut.allSuccessful;
9261
+ }
9262
+ // -------------------------------------------------------------------------
9263
+ // Fan-out
9264
+ // -------------------------------------------------------------------------
9265
+ async fanOut(goal, tasks) {
9266
+ const coordinator = this.coordinator;
9267
+ const slotCount = Math.min(this.slots, tasks.length);
9268
+ const recentJournal = goal.journal.slice(-5).map((e) => ` #${e.iteration} [${e.status}] ${e.task}${e.note ? ` \u2014 ${e.note.slice(0, 80)}` : ""}`).join("\n");
9269
+ const directivePreamble = [
9270
+ "\u2550\u2550\u2550 ETERNAL AUTONOMY \u2014 parallel task slot \u2550\u2550\u2550",
9271
+ "",
9272
+ `Mission: ${goal.goal}`,
9273
+ `Total parallel slots: ${slotCount}`,
9274
+ "",
9275
+ recentJournal ? `Recent journal (last 5):
9276
+ ${recentJournal}` : "No prior iterations.",
9277
+ "",
9278
+ "\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
9279
+ "\u2022 Execute the assigned task end-to-end using multiple tool calls.",
9280
+ "\u2022 Emit `[done]` on its own line when the task is complete.",
9281
+ "\u2022 Do not ask for confirmation \u2014 YOLO is active.",
9282
+ "\u2022 If the overall Mission is accomplished, emit `[GOAL_COMPLETE]` followed by a verification recipe.",
9283
+ "\u2022 Keep output concise \u2014 summarize findings, do not transcribe files."
9284
+ ].join("\n");
9285
+ const taskIds = [];
9286
+ const subagentIds = [];
9287
+ const spawnPromises = [];
9288
+ for (let i = 0; i < slotCount; i++) {
9289
+ const task = tasks[i];
9290
+ const subagentId = `parallel-${this.iterations}-${i}`;
9291
+ const taskId = randomUUID();
9292
+ const spec = {
9293
+ id: taskId,
9294
+ description: `${directivePreamble}
9295
+
9296
+ \u2500\u2500 SLOT ${i + 1}/${slotCount} \u2500\u2500
9297
+ Task: ${task}
9298
+ `,
9299
+ subagentId
9300
+ };
9301
+ spawnPromises.push((async () => {
9302
+ try {
9303
+ await coordinator.spawn({
9304
+ id: subagentId,
9305
+ name: `slot-${subagentId.slice(-6)}`,
9306
+ maxIterations: 50,
9307
+ maxToolCalls: 200,
9308
+ timeoutMs: this.timeoutMs
9309
+ });
9310
+ subagentIds.push(subagentId);
9311
+ taskIds.push(taskId);
9312
+ await coordinator.assign(spec);
9313
+ } catch {
9314
+ }
9315
+ })());
9316
+ }
9317
+ await Promise.all(spawnPromises);
9318
+ if (taskIds.length === 0) {
9319
+ return { results: [], allSuccessful: false, goalComplete: false, partialOutput: "" };
9320
+ }
9321
+ let results = [];
9322
+ try {
9323
+ const ctrl = new AbortController();
9324
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs + 6e4);
9325
+ try {
9326
+ results = await coordinator.awaitTasks(taskIds);
9327
+ } finally {
9328
+ clearTimeout(timer);
9329
+ }
9330
+ } catch {
9331
+ results = coordinator.results().slice(-taskIds.length);
9332
+ }
9333
+ const allSuccessful = results.length > 0 && results.every((r) => r.status === "success");
9334
+ const goalComplete = results.some(
9335
+ (r) => r.status === "success" && typeof r.result === "string" && GOAL_COMPLETE_MARKER2.test(r.result)
9336
+ );
9337
+ const partialOutput = results.map((r) => typeof r.result === "string" ? r.result : "").filter(Boolean).join("\n\n");
9338
+ return { results, allSuccessful, goalComplete, partialOutput };
9339
+ }
9340
+ // -------------------------------------------------------------------------
9341
+ // Goal decomposition
9342
+ // -------------------------------------------------------------------------
9343
+ async decomposeGoal(goal) {
9344
+ const todos = this.opts.agent.ctx?.todos;
9345
+ const tasks = [];
9346
+ if (Array.isArray(todos)) {
9347
+ const pending = todos.filter((t2) => t2.status === "pending").slice(0, this.slots);
9348
+ for (const t2 of pending) {
9349
+ tasks.push(`[todo] ${t2.content}`);
9350
+ }
9351
+ }
9352
+ if (tasks.length < this.slots) {
9353
+ try {
9354
+ const gitStatus = await (this.opts.gitStatusReader?.() ?? this.readGitStatus());
9355
+ const dirty = gitStatus.trim();
9356
+ if (dirty) {
9357
+ const lines = dirty.split("\n").slice(0, this.slots - tasks.length);
9358
+ for (const line of lines) {
9359
+ const file = line.replace(/^[ MADRUC?]{2}\s*/, "").trim();
9360
+ if (file) tasks.push(`[git] inspect and fix: ${file}`);
9361
+ }
9362
+ }
9363
+ } catch {
9364
+ }
9365
+ }
9366
+ if (tasks.length < this.slots) {
9367
+ const remaining = this.slots - tasks.length;
9368
+ const brainstormed = await this.brainstormSubtasks(goal, remaining);
9369
+ tasks.push(...brainstormed);
9370
+ }
9371
+ return tasks.length > 0 ? tasks.slice(0, this.slots) : null;
9372
+ }
9373
+ async readGitStatus() {
9374
+ const { execFile: execFile2 } = await import('child_process');
9375
+ const { promisify: promisify2 } = await import('util');
9376
+ const execFileP2 = promisify2(execFile2);
9377
+ const { stdout } = await execFileP2("git", ["status", "--porcelain"], {
9378
+ cwd: this.opts.projectRoot,
9379
+ timeout: 5e3
9380
+ });
9381
+ return stdout;
9382
+ }
9383
+ async brainstormSubtasks(goal, count) {
9384
+ const lastFew = goal.journal.slice(-5).map((e) => ` - [${e.status}] ${e.task}`).join("\n");
9385
+ const directive = [
9386
+ `Decompose this goal into exactly ${count} independent sub-tasks for parallel execution.`,
9387
+ "",
9388
+ `Goal: ${goal.goal}`,
9389
+ "",
9390
+ lastFew ? `Recent:
9391
+ ${lastFew}` : "No prior iterations.",
9392
+ "",
9393
+ `Output exactly ${count} tasks, one per line, under 120 chars each.`,
9394
+ "Format: TASK-1 | TASK-2 | ... (pipe-separated, no numbering, no preamble).",
9395
+ "Each task must be independently actionable with no shared dependencies."
9396
+ ].join("\n");
9397
+ try {
9398
+ const ctrl = new AbortController();
9399
+ const timer = setTimeout(() => ctrl.abort(), 6e4);
9400
+ try {
9401
+ const result = await this.opts.agent.run([{ type: "text", text: directive }], {
9402
+ signal: ctrl.signal,
9403
+ maxIterations: 1
9404
+ });
9405
+ if (result.status !== "done") return [];
9406
+ const text = (result.finalText ?? "").trim();
9407
+ if (!text) return [];
9408
+ const tasks = text.split("|").map((t2) => t2.trim()).filter((t2) => t2.length > 10 && t2.length < 240);
9409
+ return tasks.slice(0, count);
9410
+ } finally {
9411
+ clearTimeout(timer);
9412
+ }
9413
+ } catch {
9414
+ return [];
9415
+ }
9416
+ }
9417
+ // -------------------------------------------------------------------------
9418
+ // Compaction
9419
+ // -------------------------------------------------------------------------
9420
+ async maybeCompact() {
9421
+ const compactor = this.opts.compactor;
9422
+ if (!compactor) return;
9423
+ const ctx = this.opts.agent.ctx;
9424
+ if (!ctx) return;
9425
+ const shouldRun = this.iterationsSinceCompact >= (this.opts.compactEveryNIterations ?? 25);
9426
+ if (!shouldRun) return;
9427
+ const report = await compactor.compact(ctx, { aggressive: false });
9428
+ this.iterationsSinceCompact = 0;
9429
+ await this.appendIterationEntry({
9430
+ source: "manual",
9431
+ task: "compaction (cadence)",
9432
+ status: "success",
9433
+ note: `saved ~${report.before - report.after} tokens`
9434
+ });
9435
+ }
9436
+ // -------------------------------------------------------------------------
9437
+ // Helpers
9438
+ // -------------------------------------------------------------------------
9439
+ async appendIterationEntry(entry) {
9440
+ const current = await loadGoal(this.goalPath);
9441
+ if (!current) return;
9442
+ const updated = appendJournal(current, entry);
9443
+ await saveGoal(this.goalPath, updated);
9444
+ const entryWithMeta = {
9445
+ at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
9446
+ iteration: updated.iterations,
9447
+ ...entry
9448
+ };
9449
+ this.opts.onIteration?.(entryWithMeta);
9450
+ }
9451
+ async appendFailure(task, note) {
9452
+ await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
9453
+ }
9454
+ async persistState(state) {
9455
+ const current = await loadGoal(this.goalPath);
9456
+ if (!current) return;
9457
+ if (current.engineState === state) return;
9458
+ await saveGoal(this.goalPath, { ...current, engineState: state });
9459
+ }
9460
+ };
9461
+
9462
+ // src/execution/autonomy-prompt-contributor.ts
9463
+ function makeAutonomyPromptContributor(opts) {
9464
+ return async (ctx) => {
9465
+ if (ctx.subagent) return [];
9466
+ if (!opts.enabled()) return [];
9467
+ let goal;
9468
+ try {
9469
+ goal = await loadGoal(opts.goalPath);
9470
+ } catch {
9471
+ return [];
9472
+ }
9473
+ if (!goal) return [];
9474
+ const missionState = goal.goalState ?? "active";
9475
+ if (missionState !== "active") return [];
9476
+ const tailSize = opts.journalTailSize ?? 5;
9477
+ const journalTail = goal.journal.slice(-tailSize).map((e) => {
9478
+ const note = e.note ? ` \u2014 ${e.note.slice(0, 80)}` : "";
9479
+ return ` #${e.iteration} [${e.status}] ${e.task}${note}`;
9480
+ });
9481
+ const text = [
9482
+ "## ETERNAL AUTONOMY \u2014 active mission",
9483
+ "",
9484
+ "You are inside a long-running autonomous loop. The user is asleep",
9485
+ "and is not available to confirm decisions. Each turn you receive a",
9486
+ "directive describing one concrete sub-task that advances the mission.",
9487
+ "",
9488
+ `Mission: ${goal.goal}`,
9489
+ `Iteration: #${goal.iterations}`,
9490
+ journalTail.length > 0 ? `Recent journal (last ${journalTail.length}):
9491
+ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first iteration)",
9492
+ "",
9493
+ "### Loop control markers",
9494
+ "Emit these on their own line in your final text \u2014 case-insensitive,",
9495
+ "whitespace-tolerant, but they must occupy the entire line:",
9496
+ "- `[continue]` \u2014 chain to the next internal step without returning.",
9497
+ "- `[done]` \u2014 the current sub-task is finished; return to the engine.",
9498
+ "- `[GOAL_COMPLETE]` \u2014 emit ONLY when the OVERALL mission is",
9499
+ " verifiably done. Must be followed by a one-paragraph verification",
9500
+ " recipe (artifact path, test command, or 10-second reproduction).",
9501
+ " The engine halts on this marker \u2014 false positives waste real",
9502
+ " human time. If unsure, emit `[done]` and let the next iteration",
9503
+ " decide.",
9504
+ "",
9505
+ "### Operating principles",
9506
+ "- YOLO is active. Do NOT ask for confirmation, do NOT propose",
9507
+ " options. Pick the best path and execute it.",
9508
+ "- Use tools freely; multiple calls per turn are normal and expected.",
9509
+ "- When working on a todo, mark it `in_progress` via the todos tool",
9510
+ " before tool work and `completed` (or `cancelled` with a reason)",
9511
+ " when done. The loop reads todo state between iterations.",
9512
+ "- If an approach fails twice in a row, pivot. Don't grind on the",
9513
+ " same wall \u2014 try a different angle, file a cancel on the todo, or",
9514
+ " surface the obstacle via `[done]` and let the next iteration",
9515
+ " re-plan."
9516
+ ].join("\n");
9517
+ return [
9518
+ {
9519
+ type: "text",
9520
+ text,
9521
+ cache_control: { type: "ephemeral" }
9522
+ }
9523
+ ];
9524
+ };
9525
+ }
9526
+
9527
+ // src/execution/goal-preamble.ts
9528
+ function buildGoalPreamble(goal) {
9529
+ return [
9530
+ "[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
9531
+ "The user granted you full autonomy. Read these constraints once, then act.",
9532
+ "",
9533
+ "YOUR GOAL:",
9534
+ "---",
9535
+ goal,
9536
+ "---",
9537
+ "",
9538
+ "AUTHORITY YOU HAVE:",
9539
+ "- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
9540
+ " Parallel + recursive fan-out are both fine. There is no spawn budget.",
9541
+ "- Use any provider/model per subagent \u2014 pick the right tool for each",
9542
+ " piece of work. Heavy reasoning model for planning, fast model for",
9543
+ " batch work, specialist model for domain code.",
9544
+ "- Run unlimited tool calls and iterations. There is NO hidden budget.",
9545
+ " The Agent loop auto-extends every 100 iterations forever.",
9546
+ "- Retry failed tools with different inputs, alternative paths, fresh",
9547
+ " subagents. Switch providers mid-run if one is rate-limited.",
9548
+ "- Re-plan freely when an approach hits a dead end. You are not obliged",
9549
+ " to stick with the first plan you proposed.",
9550
+ "",
9551
+ 'WHAT "DONE" MEANS \u2014 non-negotiable:',
9552
+ "- You can name a concrete artifact (a passing test, a written file at",
9553
+ " a specific path, a fixed bug verified by re-running the failing case,",
9554
+ " a clean grep that previously had matches).",
9555
+ "- You can tell the user HOW to verify it themselves in 10 seconds.",
9556
+ '- You have NOT hedged. None of: "looks like it should work", "I',
9557
+ ' believe this fixes it", "the changes appear correct".',
9558
+ "",
9559
+ "WHAT IS NOT DONE \u2014 never report any of these as completion:",
9560
+ "- An error message you didn't recover from.",
9561
+ '- An empty result, a 0-line file, a "no matches found" you accepted',
9562
+ " without questioning the search.",
9563
+ '- "Should I continue?" / "Want me to also...?" / "Let me know if you',
9564
+ ' want X." Those are hedges. The user already told you to finish the',
9565
+ " goal \u2014 just do it.",
9566
+ "- Partial progress dressed up as success. Fixed 3 of 5 bugs = 60%",
9567
+ " done, not done.",
9568
+ "- A subagent's failed/timeout/stopped TaskResult that you didn't",
9569
+ " respond to with a fresh attempt (different role, different model,",
9570
+ " tighter prompt).",
9571
+ "",
9572
+ "PERSISTENCE PROTOCOL:",
9573
+ "- If blocked, try at least 3 different angles before reporting the",
9574
+ " problem to the user. Different tool inputs, different subagent",
9575
+ " roles, different providers, different decomposition of the task.",
9576
+ "- If a tool fails, read its error, alter the input, try again. Do",
9577
+ " not just report the failure back.",
9578
+ "- If a subagent returns useless output, respawn with a tighter prompt",
9579
+ ' or a different role. Do not accept "I could not determine\u2026" as the',
9580
+ " final answer.",
9581
+ "- Use `ask_subagent` for one-shot questions when you don't need a",
9582
+ " full delegated task.",
9583
+ "",
9584
+ "REPORTING:",
9585
+ "- Stream short progress notes between major actions so the user can",
9586
+ " monitor. Do not go silent for 50 tool calls then dump a wall of",
9587
+ " text \u2014 but also do not narrate every tool call.",
9588
+ "- Use the shared scratchpad (if available) to leave breadcrumbs",
9589
+ " subagents can read.",
9590
+ "- Final response must include: (a) what was accomplished, (b) how",
9591
+ " to verify, (c) any caveats (residual TODOs, things the user",
9592
+ " should know about).",
9593
+ "",
9594
+ "BEGIN.]"
9595
+ ].join("\n");
9596
+ }
9597
+
9598
+ // src/coordination/director.ts
9599
+ init_atomic_write();
9600
+
9601
+ // src/coordination/director-prompts.ts
9602
+ var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
9603
+ subagents by spawning them, assigning tasks, awaiting completions, and
9604
+ rolling up their outputs into your next decision.
9605
+
9606
+ Core fleet tools available to you:
9607
+ - spawn_subagent \u2014 create a worker with a chosen provider / model / role
9608
+ - assign_task \u2014 hand a piece of work to a specific subagent
9609
+ - await_tasks \u2014 block until named task ids complete (parallel-safe)
9610
+ - ask_subagent \u2014 synchronously query a running subagent via the bridge
9611
+ - roll_up \u2014 aggregate finished tasks into a markdown/json summary
9612
+ - terminate_subagent \u2014 abort a stuck worker (use sparingly)
9613
+ - fleet_status \u2014 snapshot of all subagents and pending tasks
9614
+ - fleet_usage \u2014 token + cost breakdown per subagent and total
9615
+
9616
+ Working rules:
9617
+ 1. Decompose first. Before spawning, decide which sub-tasks are
9618
+ independent and can run in parallel. Sequential work doesn't need a
9619
+ subagent \u2014 do it yourself.
9620
+ 2. Match worker to job. Cheap/fast model for triage, capable model for
9621
+ synthesis. Different providers per sibling is allowed and encouraged.
9622
+ 3. Always pair an assign with an await. Don't fire-and-forget; you owe
9623
+ the user a single coherent answer at the end.
9624
+ 4. Roll up before deciding. After await_tasks resolves, call roll_up so
9625
+ the results are folded back into your context in a compact form.
9626
+ 5. Budget is real. Check fleet_usage periodically. If a subagent is
9627
+ thrashing, terminate it rather than letting cost climb silently.
9628
+ 6. Never claim a subagent's work as your own without verifying it. If a
9629
+ result looks wrong, ask_subagent for clarification before passing it
9630
+ to the user.`;
9631
+ var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
9632
+ a specific slice of a larger plan \u2014 do that slice well and report back.
9633
+
9634
+ Bridge contract:
9635
+ - You have a parent (the Director). You may call \`request\` on the
9636
+ parent bridge to ask a clarifying question. Use this sparingly; the
9637
+ parent is also working.
9638
+ - You MAY NOT request the parent's system prompt, tool list, or other
9639
+ subagents' context. Those are not yours to read.
9640
+ - Your final task output is what the Director sees. Be concise,
9641
+ structured, and self-contained \u2014 assume the Director will paste your
9642
+ output into its own context.`;
9643
+ function composeDirectorPrompt(parts = {}) {
9644
+ const sections = [];
9645
+ const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
9646
+ if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
9647
+ if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
9648
+ sections.push(`Available roles you can spawn:
9649
+ ${parts.rosterSummary.trim()}`);
9650
+ }
9651
+ if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
9652
+ sections.push(parts.basePrompt.trim());
9653
+ }
9654
+ return sections.join("\n\n");
9655
+ }
9656
+ function composeSubagentPrompt(parts = {}) {
9657
+ const sections = [];
9658
+ const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
9659
+ if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
9660
+ if (parts.role && parts.role.trim().length > 0) {
9661
+ sections.push(`Role:
9662
+ ${parts.role.trim()}`);
9663
+ }
9664
+ if (parts.task && parts.task.trim().length > 0) {
9665
+ sections.push(`Task:
9666
+ ${parts.task.trim()}`);
9667
+ }
9668
+ if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
9669
+ sections.push(
9670
+ `Shared notes:
9671
+ A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
9672
+ - Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
9673
+ - Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
9674
+ - Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
9675
+ );
9676
+ }
9677
+ if (parts.override && parts.override.trim().length > 0) {
9678
+ sections.push(parts.override.trim());
9679
+ }
9680
+ return sections.join("\n\n");
9681
+ }
9682
+ function rosterSummaryFromConfigs(roster) {
9683
+ const lines = [];
9684
+ for (const [roleId, cfg] of Object.entries(roster)) {
9685
+ const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
9686
+ const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
9687
+ const tail = headline ? ` \u2014 ${headline}` : "";
9688
+ lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
9689
+ }
9690
+ return lines.join("\n");
9691
+ }
9692
+
9693
+ // src/coordination/fleet-bus.ts
9694
+ var FleetBus = class {
9695
+ byId = /* @__PURE__ */ new Map();
9696
+ byType = /* @__PURE__ */ new Map();
9697
+ any = /* @__PURE__ */ new Set();
9698
+ /**
9699
+ * Hook a subagent's EventBus into the fleet. EventBus is strongly
9700
+ * typed and doesn't expose an `onAny` hook, so we subscribe to the
9701
+ * canonical set of event types a subagent emits during a run. New
9702
+ * event types added to the kernel must be added here too — but the
9703
+ * cost is a tiny single line per type, and the explicit list keeps
9704
+ * the wire format clear.
9705
+ *
9706
+ * Returns a disposer that detaches every subscription; call on
9707
+ * subagent teardown so the listeners don't outlive the run.
9708
+ */
9709
+ attach(subagentId, bus, taskId) {
9710
+ const off = bus.onPattern("*", (type, payload) => {
9711
+ if (type.startsWith("subagent.")) return;
9712
+ this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
9713
+ });
9714
+ return () => {
9715
+ off();
9716
+ };
9717
+ }
9718
+ /** Subscribe to every event from one subagent. */
9719
+ subscribe(subagentId, handler) {
9720
+ let set = this.byId.get(subagentId);
9721
+ if (!set) {
9722
+ set = /* @__PURE__ */ new Set();
9723
+ this.byId.set(subagentId, set);
9724
+ }
9725
+ set.add(handler);
9726
+ return () => {
9727
+ set.delete(handler);
9728
+ };
9729
+ }
9730
+ /** Subscribe to one event type across all subagents. */
9731
+ filter(type, handler) {
9732
+ let set = this.byType.get(type);
9733
+ if (!set) {
9734
+ set = /* @__PURE__ */ new Set();
9735
+ this.byType.set(type, set);
9736
+ }
9737
+ set.add(handler);
9738
+ return () => {
9739
+ set.delete(handler);
9740
+ };
9741
+ }
9742
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
9743
+ onAny(handler) {
9744
+ this.any.add(handler);
9745
+ return () => {
9746
+ this.any.delete(handler);
9747
+ };
9748
+ }
9749
+ emit(event) {
9750
+ const byId = this.byId.get(event.subagentId);
9751
+ if (byId)
9752
+ for (const h of byId) {
9753
+ try {
9754
+ h(event);
9755
+ } catch {
9756
+ }
9757
+ }
9758
+ const byType = this.byType.get(event.type);
9759
+ if (byType)
9760
+ for (const h of byType) {
9761
+ try {
9762
+ h(event);
9763
+ } catch {
9764
+ }
9765
+ }
9766
+ for (const h of this.any) {
9767
+ try {
9768
+ h(event);
9769
+ } catch {
9770
+ }
9771
+ }
9772
+ }
9773
+ };
9774
+ var FleetUsageAggregator = class {
9775
+ constructor(bus, priceLookup, metaLookup) {
9776
+ this.bus = bus;
9777
+ this.priceLookup = priceLookup;
9778
+ this.metaLookup = metaLookup;
9779
+ bus.filter("provider.response", (e) => this.onProviderResponse(e));
9780
+ bus.filter("tool.executed", (e) => this.onToolExecuted(e));
9781
+ bus.filter("iteration.started", (e) => this.onIterationStarted(e));
9263
9782
  }
9264
- return {
9265
- kind: "unknown",
9266
- message: baseMessage,
9267
- retryable: false,
9268
- cause
9269
- };
9270
- }
9271
- function providerErrorToSubagentError(err, message, cause) {
9272
- const status = err.status;
9273
- if (status === 429 || err.body?.type === "rate_limit_error") {
9783
+ bus;
9784
+ priceLookup;
9785
+ metaLookup;
9786
+ perSubagent = /* @__PURE__ */ new Map();
9787
+ total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
9788
+ /** Live snapshot — safe to call from a tool's execute() body. */
9789
+ snapshot() {
9274
9790
  return {
9275
- kind: "provider_rate_limit",
9276
- message,
9277
- retryable: true,
9278
- // Conservative default: 5s. Provider-specific code can override
9279
- // by emitting an error whose body carries an explicit hint.
9280
- backoffMs: 5e3,
9281
- cause
9791
+ total: { ...this.total },
9792
+ perSubagent: Object.fromEntries(
9793
+ Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
9794
+ )
9282
9795
  };
9283
9796
  }
9284
- if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
9285
- return { kind: "provider_auth", message, retryable: false, cause };
9797
+ ensure(subagentId) {
9798
+ let snap = this.perSubagent.get(subagentId);
9799
+ if (!snap) {
9800
+ const meta = this.metaLookup?.(subagentId);
9801
+ snap = {
9802
+ subagentId,
9803
+ provider: meta?.provider,
9804
+ model: meta?.model,
9805
+ input: 0,
9806
+ output: 0,
9807
+ cacheRead: 0,
9808
+ cacheWrite: 0,
9809
+ cost: 0,
9810
+ toolCalls: 0,
9811
+ iterations: 0,
9812
+ startedAt: Date.now(),
9813
+ lastEventAt: Date.now()
9814
+ };
9815
+ this.perSubagent.set(subagentId, snap);
9816
+ }
9817
+ return snap;
9286
9818
  }
9287
- if (status === 408 || status === 0) {
9288
- return { kind: "provider_timeout", message, retryable: true, cause };
9819
+ onProviderResponse(e) {
9820
+ const snap = this.ensure(e.subagentId);
9821
+ const p = e.payload;
9822
+ const usage = p?.usage;
9823
+ if (!usage) return;
9824
+ snap.input += usage.input ?? 0;
9825
+ snap.output += usage.output ?? 0;
9826
+ snap.cacheRead += usage.cacheRead ?? 0;
9827
+ snap.cacheWrite += usage.cacheWrite ?? 0;
9828
+ this.total.input += usage.input ?? 0;
9829
+ this.total.output += usage.output ?? 0;
9830
+ this.total.cacheRead += usage.cacheRead ?? 0;
9831
+ this.total.cacheWrite += usage.cacheWrite ?? 0;
9832
+ const price = this.priceLookup?.(e.subagentId);
9833
+ if (price) {
9834
+ const delta = (usage.input ?? 0) / 1e6 * (price.input ?? 0) + (usage.output ?? 0) / 1e6 * (price.output ?? 0) + (usage.cacheRead ?? 0) / 1e6 * (price.cacheRead ?? 0) + (usage.cacheWrite ?? 0) / 1e6 * (price.cacheWrite ?? 0);
9835
+ snap.cost += delta;
9836
+ this.total.cost += delta;
9837
+ }
9838
+ snap.lastEventAt = e.ts;
9289
9839
  }
9290
- if (status >= 500 && status < 600) {
9291
- return {
9292
- kind: "provider_5xx",
9293
- message,
9294
- retryable: true,
9295
- backoffMs: 3e3,
9296
- cause
9297
- };
9840
+ onToolExecuted(e) {
9841
+ const snap = this.ensure(e.subagentId);
9842
+ snap.toolCalls += 1;
9843
+ snap.lastEventAt = e.ts;
9298
9844
  }
9299
- return { kind: "unknown", message, retryable: err.retryable, cause };
9300
- }
9845
+ onIterationStarted(e) {
9846
+ const snap = this.ensure(e.subagentId);
9847
+ snap.iterations += 1;
9848
+ snap.lastEventAt = e.ts;
9849
+ }
9850
+ };
9301
9851
  function makeSpawnTool(director, roster) {
9302
9852
  const inputSchema = {
9303
9853
  type: "object",
@@ -9678,7 +10228,7 @@ var Director = class {
9678
10228
  this.sessionWriter = opts.sessionWriter ?? null;
9679
10229
  this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
9680
10230
  this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
9681
- this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 2;
10231
+ this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 5;
9682
10232
  this.sessionsRoot = opts.sessionsRoot;
9683
10233
  this.directorRunId = opts.directorRunId ?? this.id;
9684
10234
  this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
@@ -9766,19 +10316,19 @@ var Director = class {
9766
10316
  const extra = {};
9767
10317
  switch (payload.kind) {
9768
10318
  case "iterations":
9769
- extra.maxIterations = Math.min(payload.used + 50, 500);
10319
+ extra.maxIterations = Math.min(payload.used + 100, 800);
9770
10320
  break;
9771
10321
  case "tool_calls":
9772
- extra.maxToolCalls = Math.min(Math.ceil(payload.limit * 1.5), 1e3);
10322
+ extra.maxToolCalls = Math.min(Math.ceil(payload.limit * 2), 1500);
9773
10323
  break;
9774
10324
  case "tokens":
9775
- extra.maxTokens = Math.min(Math.ceil(payload.limit * 1.5), 5e5);
10325
+ extra.maxTokens = Math.min(Math.ceil(payload.limit * 2), 8e5);
9776
10326
  break;
9777
10327
  case "cost":
9778
- extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
10328
+ extra.maxCostUsd = Math.min(payload.limit * 2, 25);
9779
10329
  break;
9780
10330
  case "timeout":
9781
- extra.timeoutMs = Math.min(Math.ceil(payload.limit * 1.5), 60 * 6e4);
10331
+ extra.timeoutMs = Math.min(Math.ceil(payload.limit * 2), 2 * 60 * 6e4);
9782
10332
  break;
9783
10333
  }
9784
10334
  payload.extend(extra);
@@ -10567,136 +11117,6 @@ async function readSubagentPartial(opts, subagentId) {
10567
11117
  }
10568
11118
  return void 0;
10569
11119
  }
10570
-
10571
- // src/coordination/agent-subagent-runner.ts
10572
- function makeAgentSubagentRunner(opts) {
10573
- const format = opts.formatTaskInput ?? defaultFormatTaskInput;
10574
- return async (task, ctx) => {
10575
- const factoryResult = await opts.factory(ctx.config);
10576
- const { agent, events } = factoryResult;
10577
- const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
10578
- const aborter = new AbortController();
10579
- ctx.budget._events = events;
10580
- ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
10581
- let budgetError = null;
10582
- const onBudgetError = (err) => {
10583
- if (err instanceof BudgetThresholdSignal) {
10584
- err.decision.then((decision) => {
10585
- if (decision === "stop") {
10586
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10587
- aborter.abort();
10588
- }
10589
- }).catch(() => {
10590
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
10591
- aborter.abort();
10592
- });
10593
- return;
10594
- }
10595
- aborter.abort();
10596
- budgetError = err instanceof BudgetExceededError ? err : new BudgetExceededError(
10597
- "tool_calls",
10598
- 0,
10599
- 0
10600
- );
10601
- if (budgetError !== err && err instanceof Error) {
10602
- budgetError.message += ` (caused by: ${err.message})`;
10603
- }
10604
- };
10605
- let lastToolFailed = null;
10606
- const unsub = [];
10607
- unsub.push(
10608
- events.on("tool.executed", (e) => {
10609
- try {
10610
- ctx.budget.recordToolCall();
10611
- } catch (eb) {
10612
- onBudgetError(eb);
10613
- }
10614
- if (e.ok === false) {
10615
- lastToolFailed = e.name;
10616
- } else if (e.ok === true) {
10617
- lastToolFailed = null;
10618
- }
10619
- }),
10620
- events.on("provider.response", (e) => {
10621
- try {
10622
- ctx.budget.recordUsage(e.usage);
10623
- } catch (e2) {
10624
- void onBudgetError(e2);
10625
- }
10626
- }),
10627
- events.on("iteration.started", () => {
10628
- try {
10629
- ctx.budget.recordIteration();
10630
- ctx.budget.checkTimeout();
10631
- } catch (e) {
10632
- void onBudgetError(e);
10633
- }
10634
- }),
10635
- // D3: cooperative timeout enforcement DURING a long tool call.
10636
- // The iteration-loop checkTimeout() only fires between agent
10637
- // iterations — a single `bash sleep 3600` call would otherwise
10638
- // park inside one tool execution while the timeout silently
10639
- // passes, relying solely on the coordinator's hard Promise.race
10640
- // to interrupt. Tools that emit `tool.progress` (bash chunks,
10641
- // fetch byte progress, spawn-stream stdout) give us a heartbeat
10642
- // we can hang the check on. When the budget trips here:
10643
- // 1. onBudgetError sets budgetError + aborter.abort()
10644
- // 2. aborter signal propagates to agent.run → tool executor
10645
- // 3. tool's own signal listener kills the child process
10646
- // Cheap: O(1) per progress event, and the budget short-circuits
10647
- // when timeoutMs is unset (most subagents have one set anyway).
10648
- events.on("tool.progress", () => {
10649
- try {
10650
- ctx.budget.checkTimeout();
10651
- } catch (e) {
10652
- void onBudgetError(e);
10653
- }
10654
- })
10655
- );
10656
- const onParentAbort = () => aborter.abort();
10657
- ctx.signal.addEventListener("abort", onParentAbort);
10658
- let result;
10659
- try {
10660
- result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
10661
- } finally {
10662
- detachFleet?.();
10663
- ctx.signal.removeEventListener("abort", onParentAbort);
10664
- for (const u of unsub) u();
10665
- if (factoryResult.dispose) {
10666
- try {
10667
- await factoryResult.dispose();
10668
- } catch {
10669
- }
10670
- }
10671
- }
10672
- if (budgetError) throw budgetError;
10673
- if (result.status === "failed") {
10674
- throw result.error instanceof Error ? result.error : new Error(String(result.error ?? "agent failed"));
10675
- }
10676
- if (result.status === "aborted") {
10677
- throw new Error("agent aborted");
10678
- }
10679
- if (result.status === "max_iterations") {
10680
- throw new Error("agent exhausted iteration limit");
10681
- }
10682
- const usage = ctx.budget.usage();
10683
- const finalText = (result.finalText ?? "").trim();
10684
- if (finalText.length === 0 && usage.toolCalls === 0) {
10685
- throw new Error("empty response");
10686
- }
10687
- if (finalText.length === 0 && lastToolFailed !== null) {
10688
- throw new Error(`tool failed: ${lastToolFailed}`);
10689
- }
10690
- return {
10691
- result: result.finalText,
10692
- iterations: result.iterations,
10693
- toolCalls: usage.toolCalls
10694
- };
10695
- };
10696
- }
10697
- function defaultFormatTaskInput(task) {
10698
- return task.description ?? "";
10699
- }
10700
11120
  function makeDirectorSessionFactory(opts) {
10701
11121
  const runId = opts.directorRunId ?? `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-director`;
10702
11122
  let store;
@@ -11311,6 +11731,10 @@ var TaskTracker = class {
11311
11731
  node.updatedAt = now;
11312
11732
  if (status === "completed") {
11313
11733
  node.completedAt = now;
11734
+ node.startedAt = node.startedAt ?? now;
11735
+ }
11736
+ if (status === "in_progress") {
11737
+ node.startedAt = now;
11314
11738
  }
11315
11739
  this.transitions.push({ from, to: status, timestamp: now, reason });
11316
11740
  if (status === "completed") {
@@ -11322,6 +11746,23 @@ var TaskTracker = class {
11322
11746
  this.graph.updatedAt = now;
11323
11747
  this.persist();
11324
11748
  }
11749
+ /**
11750
+ * Update node fields (title, description, priority, estimateHours, tags).
11751
+ * Does NOT change status. Use updateNodeStatus for status changes.
11752
+ */
11753
+ updateNode(id, patch) {
11754
+ if (!this.graph) throw new Error("No graph loaded");
11755
+ const node = this.graph.nodes.get(id);
11756
+ if (!node) throw new Error(`Node ${id} not found`);
11757
+ if (patch.title !== void 0) node.title = patch.title;
11758
+ if (patch.description !== void 0) node.description = patch.description;
11759
+ if (patch.priority !== void 0) node.priority = patch.priority;
11760
+ if (patch.estimateHours !== void 0) node.estimateHours = patch.estimateHours;
11761
+ if (patch.tags !== void 0) node.tags = patch.tags;
11762
+ node.updatedAt = Date.now();
11763
+ this.graph.updatedAt = node.updatedAt;
11764
+ this.persist();
11765
+ }
11325
11766
  getNode(id) {
11326
11767
  return this.graph?.nodes.get(id);
11327
11768
  }
@@ -18878,9 +19319,7 @@ var SlashCommandRegistry = class {
18878
19319
  }
18879
19320
  }
18880
19321
  if (this.cmds.has(fullName)) {
18881
- if (!isPlugin) {
18882
- throw new Error(`Built-in slash command "${fullName}" is already registered.`);
18883
- }
19322
+ if (!isPlugin) return;
18884
19323
  this.cmds.set(fullName, { cmd, owner });
18885
19324
  for (const a of cmd.aliases ?? []) {
18886
19325
  this.cmds.set(`${owner}:${a}`, { cmd, owner });
@@ -19364,6 +19803,6 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
19364
19803
  });
19365
19804
  }
19366
19805
 
19367
- export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMcpControlTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
19806
+ export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMcpControlTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
19368
19807
  //# sourceMappingURL=index.js.map
19369
19808
  //# sourceMappingURL=index.js.map