@wrongstack/core 0.31.1 → 0.41.0

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 (34) hide show
  1. package/dist/{agent-subagent-runner-DpZTLdBe.d.ts → agent-subagent-runner-C66vi4Gq.d.ts} +1 -1
  2. package/dist/{config-BUEGM4JP.d.ts → config-ZRCf7sTu.d.ts} +21 -1
  3. package/dist/coordination/index.d.ts +7 -7
  4. package/dist/coordination/index.js +3051 -2947
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +8 -8
  7. package/dist/defaults/index.js +1435 -1321
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/execution/index.d.ts +5 -5
  10. package/dist/extension/index.d.ts +2 -2
  11. package/dist/{index-ysfO_DlX.d.ts → index-6_csX32J.d.ts} +1 -1
  12. package/dist/{index-pXJdVLe0.d.ts → index-DkVgH3wC.d.ts} +31 -1
  13. package/dist/index.d.ts +135 -15
  14. package/dist/index.js +1236 -931
  15. package/dist/index.js.map +1 -1
  16. package/dist/infrastructure/index.d.ts +2 -2
  17. package/dist/infrastructure/index.js +17 -3
  18. package/dist/infrastructure/index.js.map +1 -1
  19. package/dist/kernel/index.d.ts +2 -2
  20. package/dist/{mcp-servers-BzB3r7_c.d.ts → mcp-servers-DONdo-XM.d.ts} +1 -1
  21. package/dist/{multi-agent-coordinator-DOXSgtom.d.ts → multi-agent-coordinator-BUsjiRWl.d.ts} +1 -1
  22. package/dist/{null-fleet-bus-CAQDGsKc.d.ts → null-fleet-bus-FvgHnZah.d.ts} +169 -131
  23. package/dist/{plan-templates-BZMi-VpU.d.ts → plan-templates-DYCeRCDN.d.ts} +1 -1
  24. package/dist/sdd/index.d.ts +3 -3
  25. package/dist/storage/index.d.ts +2 -2
  26. package/dist/{tool-executor-BAi4WI2d.d.ts → tool-executor-BpK-SWtJ.d.ts} +1 -1
  27. package/dist/types/index.d.ts +3 -3
  28. package/dist/types/index.js +17 -3
  29. package/dist/types/index.js.map +1 -1
  30. package/dist/utils/index.d.ts +107 -1
  31. package/dist/utils/index.js +53 -2
  32. package/dist/utils/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/skills/multi-agent/SKILL.md +57 -1
package/dist/index.js CHANGED
@@ -1295,11 +1295,62 @@ function deepMerge(a, b) {
1295
1295
  return out;
1296
1296
  }
1297
1297
 
1298
+ // src/utils/term.ts
1299
+ var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
1300
+ var hasStdin = () => typeof process !== "undefined" && !!process.stdin;
1301
+ function isStdoutTTY() {
1302
+ return hasStdout() && Boolean(process.stdout.isTTY);
1303
+ }
1304
+ function isStdinTTY() {
1305
+ return hasStdin() && Boolean(process.stdin.isTTY);
1306
+ }
1307
+ function isInteractive() {
1308
+ return isStdinTTY() && isStdoutTTY();
1309
+ }
1310
+ function getTermSize() {
1311
+ if (!hasStdout()) return { rows: 24, cols: 80 };
1312
+ return {
1313
+ rows: process.stdout.rows ?? 24,
1314
+ cols: process.stdout.columns ?? 80
1315
+ };
1316
+ }
1317
+ function onResize(cb, stream = process.stdout) {
1318
+ if (!stream || typeof stream.on !== "function") return () => {
1319
+ };
1320
+ const handler = () => {
1321
+ cb({
1322
+ rows: stream.rows ?? 24,
1323
+ cols: stream.columns ?? 80
1324
+ });
1325
+ };
1326
+ stream.on("resize", handler);
1327
+ return () => {
1328
+ stream.off("resize", handler);
1329
+ };
1330
+ }
1331
+ function setRawMode(input, mode) {
1332
+ if (!input || input.isTTY !== true) return false;
1333
+ if (typeof input.setRawMode !== "function") return false;
1334
+ input.setRawMode(mode);
1335
+ return true;
1336
+ }
1337
+ function writeTo(s, stream) {
1338
+ if (!stream || typeof stream.write !== "function") return false;
1339
+ stream.write(s);
1340
+ return true;
1341
+ }
1342
+ function writeOut(s, stream = process.stdout) {
1343
+ return writeTo(s, stream);
1344
+ }
1345
+ function writeErr(s, stream = process.stderr) {
1346
+ return writeTo(s, stream);
1347
+ }
1348
+
1298
1349
  // src/utils/color.ts
1299
1350
  var isColorTty = () => {
1300
1351
  if (process.env.NO_COLOR) return false;
1301
1352
  if (process.env.FORCE_COLOR) return true;
1302
- return Boolean(process.stdout?.isTTY);
1353
+ return isStdoutTTY();
1303
1354
  };
1304
1355
  var COLOR = isColorTty();
1305
1356
  var wrap = (open5, close) => (s) => COLOR ? `\x1B[${open5}m${s}\x1B[${close}m` : s;
@@ -1403,10 +1454,10 @@ var DefaultLogger = class _DefaultLogger {
1403
1454
  if (r <= LEVEL_RANK.warn || this.level === "debug" || this.level === "trace") {
1404
1455
  const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
1405
1456
  if (ctx !== void 0) {
1406
- process.stderr.write(`${head} ${formatCtx(ctx)}
1457
+ writeErr(`${head} ${formatCtx(ctx)}
1407
1458
  `);
1408
1459
  } else {
1409
- process.stderr.write(`${head}
1460
+ writeErr(`${head}
1410
1461
  `);
1411
1462
  }
1412
1463
  }
@@ -5487,9 +5538,9 @@ var DefaultMemoryStore = class {
5487
5538
  async readAll() {
5488
5539
  const parts = [];
5489
5540
  for (const scope of ["project-agents", "project-memory", "user-memory"]) {
5490
- const writeErr = this.writeErrors.get(scope);
5491
- if (writeErr) {
5492
- parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
5541
+ const writeErr2 = this.writeErrors.get(scope);
5542
+ if (writeErr2) {
5543
+ parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
5493
5544
  }
5494
5545
  const body = await this.read(scope);
5495
5546
  if (body.trim()) parts.push(`## ${labelOf(scope)}
@@ -13686,357 +13737,631 @@ function buildGoalPreamble(goal) {
13686
13737
  "BEGIN.]"
13687
13738
  ].join("\n");
13688
13739
  }
13689
-
13690
- // src/coordination/director.ts
13691
13740
  init_atomic_write();
13692
-
13693
- // src/coordination/large-answer-store.ts
13694
- var LargeAnswerStore = class {
13741
+ var DEFAULT_MAX_TARGET_FILES = 30;
13742
+ var CollabSession = class extends EventEmitter {
13743
+ sessionId;
13744
+ options;
13745
+ snapshot;
13746
+ director;
13747
+ fleetBus;
13748
+ subagentIds = /* @__PURE__ */ new Map();
13749
+ // role → subagentId
13750
+ bugs = /* @__PURE__ */ new Map();
13751
+ plans = /* @__PURE__ */ new Map();
13752
+ evaluations = /* @__PURE__ */ new Map();
13753
+ disposers = new Array();
13754
+ settled = false;
13755
+ timeoutMs;
13756
+ cancelled = false;
13757
+ alerts = [];
13758
+ /** Tracks tool call counts per subagent for progress-based timeout decisions. */
13759
+ progressBySubagent = /* @__PURE__ */ new Map();
13760
+ /** Last tool call count when a timeout warning was handled. */
13761
+ lastTimeoutProgress = /* @__PURE__ */ new Map();
13762
+ /** Session-level timeout timer handle (cleared on cancel or natural completion). */
13763
+ _timeoutTimer;
13764
+ constructor(director, fleetBus, options) {
13765
+ super();
13766
+ this.sessionId = randomUUID();
13767
+ this.options = options;
13768
+ this.director = director;
13769
+ this.fleetBus = fleetBus;
13770
+ this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
13771
+ if (options.prebuiltSnapshot) {
13772
+ this.snapshot = options.prebuiltSnapshot;
13773
+ } else {
13774
+ this.snapshot = {
13775
+ id: this.sessionId,
13776
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13777
+ files: []
13778
+ };
13779
+ }
13780
+ }
13781
+ get id() {
13782
+ return this.sessionId;
13783
+ }
13784
+ getSessionAlerts() {
13785
+ return [...this.alerts];
13786
+ }
13787
+ isCancelled() {
13788
+ return this.cancelled;
13789
+ }
13695
13790
  /**
13696
- * Responses above this size (in characters) are stored out-of-context.
13697
- * Below this, the full answer is returned inline (no overhead).
13698
- * Default: 2000 chars ≈ 400-600 tokens.
13791
+ * Snapshot of role subagentId map. The Director calls coordinator.stop()
13792
+ * for each agent when cancelling the session, using this map to enumerate
13793
+ * all three collab agents.
13699
13794
  */
13700
- sizeThreshold;
13701
- store = /* @__PURE__ */ new Map();
13702
- constructor(sizeThreshold = 2e3) {
13703
- this.sizeThreshold = sizeThreshold;
13795
+ getSubagentIds() {
13796
+ return new Map(this.subagentIds);
13704
13797
  }
13705
13798
  /**
13706
- * Store a value, returning a summary + key for inline use.
13707
- * If the value is below sizeThreshold, returns it as-is (no store entry).
13799
+ * Returns the effective file limit for this session.
13800
+ * Priority: explicit `maxTargetFiles` > dynamic from `contextWindow` > `DEFAULT_MAX_TARGET_FILES`.
13708
13801
  */
13709
- storeAnswer(value) {
13710
- if (value === void 0 || value === null) {
13711
- return { summary: String(value), inline: true };
13802
+ effectiveFileLimit() {
13803
+ if (this.options.maxTargetFiles !== void 0) {
13804
+ return this.options.maxTargetFiles;
13712
13805
  }
13713
- const serialized = typeof value === "string" ? value : JSON.stringify(value);
13714
- const size = serialized.length;
13715
- if (size <= this.sizeThreshold) {
13716
- return { summary: serialized.slice(0, 500), inline: true };
13806
+ if (this.options.contextWindow !== void 0) {
13807
+ return Math.max(5, Math.floor(this.options.contextWindow * 0.4 / 2e3));
13717
13808
  }
13718
- const key = `a-${hashStr(serialized)}`;
13719
- this.store.set(key, {
13720
- key,
13721
- value,
13722
- size,
13723
- storedAt: Date.now()
13724
- });
13725
- return {
13726
- key,
13727
- summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
13728
- inline: false
13729
- };
13809
+ return DEFAULT_MAX_TARGET_FILES;
13730
13810
  }
13731
- /**
13732
- * Retrieve a previously stored answer by its key.
13733
- * Returns undefined if the key is unknown or the store was cleared.
13734
- */
13735
- retrieveAnswer(key) {
13736
- return this.store.get(key)?.value;
13811
+ async buildSnapshot() {
13812
+ if (this.snapshot.files.length > 0) return this.snapshot;
13813
+ const allFiles = [];
13814
+ for (const pattern of this.options.targetPaths) {
13815
+ const expanded = await expandGlob(pattern);
13816
+ allFiles.push(...expanded);
13817
+ }
13818
+ const limit = this.effectiveFileLimit();
13819
+ if (allFiles.length > limit) {
13820
+ const hint = this.options.contextWindow ? `contextWindow=${this.options.contextWindow} \u2192 calculated limit=${limit}` : `default limit=${DEFAULT_MAX_TARGET_FILES}`;
13821
+ throw new Error(
13822
+ `[collab_debug] Target has ${allFiles.length} files, which exceeds the limit (${hint}). Narrow the target or pass maxTargetFiles / contextWindow to override. For large codebases, run package-by-package or module-by-module sessions instead of targeting the entire repo.`
13823
+ );
13824
+ }
13825
+ for (const filePath of allFiles) {
13826
+ try {
13827
+ const content = await fsp3.readFile(filePath, "utf8");
13828
+ const ext = filePath.split(".").pop() ?? "";
13829
+ const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
13830
+ this.snapshot.files.push({ path: filePath, content, language });
13831
+ } catch {
13832
+ this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
13833
+ }
13834
+ }
13835
+ return this.snapshot;
13737
13836
  }
13738
13837
  /**
13739
- * Check if a key exists in the store.
13838
+ * Cancel the session. Emits director.cancel_collab on the FleetBus so all
13839
+ * collab agents finish early. The session-level timeout timer is also cleared.
13840
+ * Safe to call multiple times (idempotent after first call).
13740
13841
  */
13741
- hasAnswer(key) {
13742
- return this.store.has(key);
13743
- }
13744
- /** Number of stored entries. */
13745
- get size() {
13746
- return this.store.size;
13747
- }
13748
- /** Total characters stored. */
13749
- get totalChars() {
13750
- let total = 0;
13751
- for (const e of this.store.values()) total += e.size;
13752
- return total;
13842
+ cancel(reason = "Director cancelled collab session") {
13843
+ if (this.settled) return;
13844
+ this.cancelled = true;
13845
+ if (this._timeoutTimer) {
13846
+ clearTimeout(this._timeoutTimer);
13847
+ this._timeoutTimer = void 0;
13848
+ }
13849
+ this.fleetBus.emit({
13850
+ subagentId: this.director.id,
13851
+ ts: Date.now(),
13852
+ type: "director.cancel_collab",
13853
+ payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
13854
+ });
13855
+ this.fleetBus.emit({
13856
+ subagentId: this.director.id,
13857
+ ts: Date.now(),
13858
+ type: "collab.cancelled",
13859
+ payload: { sessionId: this.sessionId, reason }
13860
+ });
13753
13861
  }
13754
- /** Clear all stored entries. Call at the end of a director run. */
13755
- clear() {
13756
- this.store.clear();
13862
+ async start() {
13863
+ if (this.settled) throw new Error("session already settled");
13864
+ this.settled = true;
13865
+ await this.buildSnapshot();
13866
+ this.wireFleetBus();
13867
+ const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
13868
+ this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
13869
+ this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
13870
+ this.spawnAgent("critic", this.buildCriticTask())
13871
+ ]);
13872
+ this.subagentIds.set("bug-hunter", bugHunterId);
13873
+ this.subagentIds.set("refactor-planner", refactorPlannerId);
13874
+ this.subagentIds.set("critic", criticId);
13875
+ const timeout = new Promise((_, reject) => {
13876
+ this._timeoutTimer = setTimeout(() => {
13877
+ this.cancel("Session-level timeout reached");
13878
+ reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
13879
+ }, this.timeoutMs);
13880
+ });
13881
+ let results = null;
13882
+ try {
13883
+ results = await Promise.race([
13884
+ Promise.all([
13885
+ this.director.awaitTasks([bugHunterId]),
13886
+ this.director.awaitTasks([refactorPlannerId]),
13887
+ this.director.awaitTasks([criticId])
13888
+ ]),
13889
+ timeout
13890
+ ]);
13891
+ } catch (err) {
13892
+ if (this._timeoutTimer) {
13893
+ clearTimeout(this._timeoutTimer);
13894
+ this._timeoutTimer = void 0;
13895
+ }
13896
+ this.cleanup();
13897
+ const error = err instanceof Error ? err : new Error(String(err));
13898
+ this.emit("session.error", error);
13899
+ throw error;
13900
+ }
13901
+ for (const result of results.flat()) {
13902
+ await this.parseAndEmit(result);
13903
+ }
13904
+ const report = this.assembleReport();
13905
+ this.cleanup();
13906
+ this.emit("session.done", report);
13907
+ return report;
13757
13908
  }
13758
- };
13759
- function hashStr(s) {
13760
- let h = 5381;
13761
- for (let i = 0; i < s.length; i++) {
13762
- h = h * 33 ^ s.charCodeAt(i);
13909
+ async parseAndEmit(result) {
13910
+ if (result.status !== "success" || result.result == null) return;
13911
+ const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
13912
+ for (const obj of this.extractJsonObjects(text)) {
13913
+ const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
13914
+ if (!type) continue;
13915
+ this.fleetBus.emit({
13916
+ subagentId: result.subagentId,
13917
+ taskId: result.taskId,
13918
+ ts: Date.now(),
13919
+ type,
13920
+ payload: obj
13921
+ });
13922
+ }
13763
13923
  }
13764
- return (h >>> 0).toString(36);
13765
- }
13766
-
13767
- // src/coordination/director-prompts.ts
13768
- var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
13769
- subagents by spawning them, assigning tasks, awaiting completions, and
13770
- rolling up their outputs into your next decision.
13771
-
13772
- Core fleet tools available to you:
13773
- - spawn_subagent \u2014 create a worker with a chosen provider / model / role
13774
- - assign_task \u2014 hand a piece of work to a specific subagent
13775
- - await_tasks \u2014 block until named task ids complete (parallel-safe)
13776
- - ask_subagent \u2014 synchronously query a running subagent via the bridge
13777
- - roll_up \u2014 aggregate finished tasks into a markdown/json summary
13778
- - terminate_subagent \u2014 abort a stuck worker (use sparingly)
13779
- - fleet_status \u2014 snapshot of all subagents and pending tasks
13780
- - fleet_usage \u2014 token + cost breakdown per subagent and total
13781
-
13782
- Working rules:
13783
- 1. Decompose first. Before spawning, decide which sub-tasks are
13784
- independent and can run in parallel. Sequential work doesn't need a
13785
- subagent \u2014 do it yourself.
13786
- 2. Match worker to job. Cheap/fast model for triage, capable model for
13787
- synthesis. Different providers per sibling is allowed and encouraged.
13788
- 3. Always pair an assign with an await. Don't fire-and-forget; you owe
13789
- the user a single coherent answer at the end.
13790
- 4. Roll up before deciding. After await_tasks resolves, call roll_up so
13791
- the results are folded back into your context in a compact form.
13792
- 5. Budget is real. Check fleet_usage periodically. If a subagent is
13793
- thrashing, terminate it rather than letting cost climb silently.
13794
- 6. Never claim a subagent's work as your own without verifying it. If a
13795
- result looks wrong, ask_subagent for clarification before passing it
13796
- to the user.
13797
- 7. Wind down when satisfied. When the results are good enough, call
13798
- work_complete \u2014 no new subagents will spawn and queued tasks complete
13799
- as aborted. Running subagents finish naturally. Call terminate_subagent
13800
- only for ones you need to stop immediately.`;
13801
- var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
13802
- a specific slice of a larger plan \u2014 do that slice well and report back.
13803
-
13804
- Bridge contract:
13805
- - You have a parent (the Director). You may call \`request\` on the
13806
- parent bridge to ask a clarifying question. Use this sparingly; the
13807
- parent is also working.
13808
- - You MAY NOT request the parent's system prompt, tool list, or other
13809
- subagents' context. Those are not yours to read.
13810
- - Your final task output is what the Director sees. Be concise,
13811
- structured, and self-contained \u2014 assume the Director will paste your
13812
- output into its own context.`;
13813
- function composeDirectorPrompt(parts = {}) {
13814
- const sections = [];
13815
- const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
13816
- if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
13817
- if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
13818
- sections.push(`Available roles you can spawn:
13819
- ${parts.rosterSummary.trim()}`);
13820
- }
13821
- if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
13822
- sections.push(parts.basePrompt.trim());
13823
- }
13824
- return sections.join("\n\n");
13825
- }
13826
- function composeSubagentPrompt(parts = {}) {
13827
- const sections = [];
13828
- const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
13829
- if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
13830
- if (parts.role && parts.role.trim().length > 0) {
13831
- sections.push(`Role:
13832
- ${parts.role.trim()}`);
13833
- }
13834
- if (parts.task && parts.task.trim().length > 0) {
13835
- sections.push(`Task:
13836
- ${parts.task.trim()}`);
13837
- }
13838
- if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
13839
- sections.push(
13840
- `Shared notes:
13841
- A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
13842
- - Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
13843
- - Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
13844
- - Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
13845
- );
13846
- }
13847
- if (parts.override && parts.override.trim().length > 0) {
13848
- sections.push(parts.override.trim());
13849
- }
13850
- return sections.join("\n\n");
13851
- }
13852
- function rosterSummaryFromConfigs(roster) {
13853
- const lines = [];
13854
- for (const [roleId, cfg] of Object.entries(roster)) {
13855
- const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
13856
- const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
13857
- const tail = headline ? ` \u2014 ${headline}` : "";
13858
- lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
13859
- }
13860
- return lines.join("\n");
13861
- }
13862
-
13863
- // src/coordination/fleet-bus.ts
13864
- var FleetBus = class {
13865
- byId = /* @__PURE__ */ new Map();
13866
- byType = /* @__PURE__ */ new Map();
13867
- any = /* @__PURE__ */ new Set();
13868
- /**
13869
- * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
13870
- * `onPattern('*')`) to forward all events with subagent attribution, so
13871
- * new kernel event types are automatically forwarded without any manual
13872
- * registration. `subagent.*` events are excluded because they originate
13873
- * from MultiAgentHost on the parent bus, not the subagent's own bus.
13874
- *
13875
- * Returns a disposer that detaches every subscription; call on
13876
- * subagent teardown so the listeners don't outlive the run.
13877
- */
13878
- attach(subagentId, bus, taskId) {
13879
- const off = bus.onAny((type, payload) => {
13880
- if (type.startsWith("subagent.")) return;
13881
- this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
13882
- });
13883
- return () => {
13884
- off();
13885
- };
13886
- }
13887
- /** Subscribe to every event from one subagent. */
13888
- subscribe(subagentId, handler) {
13889
- let set = this.byId.get(subagentId);
13890
- if (!set) {
13891
- set = /* @__PURE__ */ new Set();
13892
- this.byId.set(subagentId, set);
13924
+ extractJsonObjects(text) {
13925
+ const objects = [];
13926
+ let depth = 0;
13927
+ let start = -1;
13928
+ let inString = false;
13929
+ let escaped = false;
13930
+ for (let i = 0; i < text.length; i++) {
13931
+ const ch = text[i];
13932
+ if (inString) {
13933
+ if (escaped) escaped = false;
13934
+ else if (ch === "\\") escaped = true;
13935
+ else if (ch === '"') inString = false;
13936
+ continue;
13937
+ }
13938
+ if (ch === '"') {
13939
+ inString = true;
13940
+ } else if (ch === "{") {
13941
+ if (depth === 0) start = i;
13942
+ depth++;
13943
+ } else if (ch === "}" && depth > 0) {
13944
+ depth--;
13945
+ if (depth === 0 && start >= 0) {
13946
+ const candidate = text.slice(start, i + 1);
13947
+ try {
13948
+ const parsed = JSON.parse(candidate);
13949
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
13950
+ objects.push(parsed);
13951
+ }
13952
+ } catch {
13953
+ }
13954
+ start = -1;
13955
+ }
13956
+ }
13893
13957
  }
13894
- set.add(handler);
13895
- return () => {
13896
- set.delete(handler);
13897
- };
13958
+ return objects;
13898
13959
  }
13899
- /** Subscribe to one event type across all subagents. */
13900
- filter(type, handler) {
13901
- let set = this.byType.get(type);
13902
- if (!set) {
13903
- set = /* @__PURE__ */ new Set();
13904
- this.byType.set(type, set);
13960
+ budgetForRole(role) {
13961
+ if (this.options.budgetOverrides?.[role]) {
13962
+ return this.options.budgetOverrides[role];
13905
13963
  }
13906
- set.add(handler);
13907
- return () => {
13908
- set.delete(handler);
13964
+ const defaults = {
13965
+ "bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
13966
+ "refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
13967
+ "critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
13909
13968
  };
13969
+ return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
13910
13970
  }
13911
- /** Subscribe to literally everything. The fleet roll-up uses this. */
13912
- onAny(handler) {
13913
- this.any.add(handler);
13914
- return () => {
13915
- this.any.delete(handler);
13971
+ async spawnAgent(role, taskBrief) {
13972
+ const budget = this.budgetForRole(role);
13973
+ const cfg = {
13974
+ id: `${role}-${this.sessionId}`,
13975
+ name: role,
13976
+ role,
13977
+ tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
13978
+ maxIterations: budget.maxIterations,
13979
+ maxToolCalls: budget.maxToolCalls,
13980
+ timeoutMs: budget.timeoutMs
13916
13981
  };
13982
+ const subagentId = await this.director.spawn(cfg);
13983
+ await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
13984
+ return subagentId;
13917
13985
  }
13918
- emit(event) {
13919
- const byId = this.byId.get(event.subagentId);
13920
- if (byId)
13921
- for (const h of byId) {
13922
- try {
13923
- h(event);
13924
- } catch {
13925
- }
13926
- }
13927
- const byType = this.byType.get(event.type);
13928
- if (byType)
13929
- for (const h of byType) {
13930
- try {
13931
- h(event);
13932
- } catch {
13933
- }
13934
- }
13935
- for (const h of this.any) {
13936
- try {
13937
- h(event);
13938
- } catch {
13939
- }
13940
- }
13986
+ buildBugHunterTask() {
13987
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
13988
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
13989
+ ${f.content}`).join("\n\n");
13990
+ return `You are BugHunter. Scan the following files for bugs and code smells.
13991
+
13992
+ Target files:
13993
+ ${fileContents}
13994
+
13995
+ For each bug found, emit it using the fleet_emit tool immediately:
13996
+ { "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
13997
+
13998
+ After scanning all files, write your full markdown bug report to:
13999
+ ${scratchpad}/bug-hunter-report-${this.sessionId}.md
14000
+
14001
+ Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
13941
14002
  }
13942
- };
13943
- var FleetUsageAggregator = class {
13944
- constructor(bus, priceLookup, metaLookup) {
13945
- this.priceLookup = priceLookup;
13946
- this.metaLookup = metaLookup;
13947
- this.unsub.push(bus.filter("provider.response", (e) => this.onProviderResponse(e)));
13948
- this.unsub.push(bus.filter("tool.executed", (e) => this.onToolExecuted(e)));
13949
- this.unsub.push(bus.filter("iteration.started", (e) => this.onIterationStarted(e)));
14003
+ buildRefactorPlannerTask() {
14004
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
14005
+ const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
14006
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
14007
+ ${f.content}`).join("\n\n");
14008
+ return `You are RefactorPlanner. Plan refactorings for the following files.
14009
+
14010
+ Target files:
14011
+ ${fileContents}
14012
+
14013
+ Read the BugHunter report at: ${bugHunterReportPath}
14014
+
14015
+ For each bug you can address, emit a refactor plan using fleet_emit:
14016
+ { "type": "refactor.plan", "payload": { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [{ "number": 1, "title": "<phase>", "tasks": ["<task>"], "risk": "<low|medium|high>" }], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } } }
14017
+
14018
+ Also write your full markdown plan to:
14019
+ ${scratchpad}/refactor-plan-${this.sessionId}.md
14020
+
14021
+ Emit each plan immediately. Do not wait until planning is complete.`;
13950
14022
  }
13951
- priceLookup;
13952
- metaLookup;
13953
- perSubagent = /* @__PURE__ */ new Map();
13954
- total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
13955
- unsub = new Array();
13956
- /**
13957
- * Remove a terminated subagent's data from the aggregator and subtract its
13958
- * contribution from the running totals. Call this when a subagent is removed
13959
- * from the fleet so the aggregator doesn't accumulate unbounded data for
13960
- * entities that will never emit events again.
13961
- */
13962
- removeSubagent(subagentId) {
13963
- const snap = this.perSubagent.get(subagentId);
13964
- if (!snap) return;
13965
- this.perSubagent.delete(subagentId);
13966
- this.total.input -= snap.input;
13967
- this.total.output -= snap.output;
13968
- this.total.cacheRead -= snap.cacheRead;
13969
- this.total.cacheWrite -= snap.cacheWrite;
13970
- this.total.cost -= snap.cost;
13971
- }
13972
- /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
13973
- dispose() {
13974
- for (const off of this.unsub) off();
13975
- this.unsub.length = 0;
13976
- }
13977
- /** Live snapshot — safe to call from a tool's execute() body. */
13978
- snapshot() {
13979
- return {
13980
- total: { ...this.total },
13981
- perSubagent: Object.fromEntries(
13982
- Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
13983
- )
13984
- };
14023
+ buildCriticTask() {
14024
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
14025
+ const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
14026
+ const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
14027
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
14028
+ ${f.content}`).join("\n\n");
14029
+ return `You are Critic. Evaluate bug findings and refactor plans.
14030
+
14031
+ Target files:
14032
+ ${fileContents}
14033
+
14034
+ Read the BugHunter report at: ${bugHunterReportPath}
14035
+ Read the RefactorPlanner report at: ${refactorPlanPath}
14036
+
14037
+ For each bug and refactor plan, emit your evaluation using fleet_emit:
14038
+ { "type": "critic.evaluation", "payload": { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": ["<strength>"], "weaknesses": ["<weakness>"], "concerns": [{ "description": "<concern>", "severity": "<blocking|advisory>" }] } } }
14039
+
14040
+ After all evaluations, write your markdown report to:
14041
+ ${scratchpad}/critic-report-${this.sessionId}.md
14042
+
14043
+ Emit each evaluation immediately. Do not wait until you have read all reports.`;
13985
14044
  }
13986
- ensure(subagentId) {
13987
- let snap = this.perSubagent.get(subagentId);
13988
- if (!snap) {
13989
- const meta = this.metaLookup?.(subagentId);
13990
- snap = {
13991
- subagentId,
13992
- provider: meta?.provider,
13993
- model: meta?.model,
13994
- input: 0,
13995
- output: 0,
13996
- cacheRead: 0,
13997
- cacheWrite: 0,
13998
- cost: 0,
13999
- toolCalls: 0,
14000
- iterations: 0,
14001
- startedAt: Date.now(),
14002
- lastEventAt: Date.now()
14045
+ wireFleetBus() {
14046
+ const dTool = this.fleetBus.filter("tool.executed", (e) => {
14047
+ this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
14048
+ });
14049
+ this.disposers.push(dTool);
14050
+ const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
14051
+ const payload = e.payload;
14052
+ const role = this.roleFromSubagentId(e.subagentId);
14053
+ if (!role) return;
14054
+ const btwNotes = this.director.getLeaderBtwNotes();
14055
+ const alert = {
14056
+ sessionId: this.sessionId,
14057
+ subagentId: e.subagentId,
14058
+ role,
14059
+ level: "warning" /* WARNING */,
14060
+ message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
14061
+ budgetKind: payload.kind,
14062
+ elapsedMs: payload.timeoutMs,
14063
+ limit: payload.limit,
14064
+ btwNotes
14003
14065
  };
14004
- this.perSubagent.set(subagentId, snap);
14005
- }
14006
- return snap;
14066
+ this.alerts.push(alert);
14067
+ this.fleetBus.emit({
14068
+ subagentId: e.subagentId,
14069
+ ts: Date.now(),
14070
+ type: "collab.warning",
14071
+ payload: alert
14072
+ });
14073
+ const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
14074
+ if (decision === "cancel") {
14075
+ this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
14076
+ return;
14077
+ }
14078
+ if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
14079
+ const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
14080
+ const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
14081
+ if (progress <= lastProgress) {
14082
+ payload.deny();
14083
+ return;
14084
+ }
14085
+ this.lastTimeoutProgress.set(e.subagentId, progress);
14086
+ const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
14087
+ setImmediate(() => {
14088
+ payload.extend({ timeoutMs: newLimit });
14089
+ });
14090
+ return;
14091
+ }
14092
+ if (decision === "extend") {
14093
+ setImmediate(() => {
14094
+ const base = Math.max(payload.limit, payload.used);
14095
+ const extra = {};
14096
+ switch (payload.kind) {
14097
+ case "iterations":
14098
+ extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
14099
+ break;
14100
+ case "tool_calls":
14101
+ extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
14102
+ break;
14103
+ case "tokens":
14104
+ extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
14105
+ break;
14106
+ case "cost":
14107
+ extra.maxCostUsd = Math.min(base * 1.5, 100);
14108
+ break;
14109
+ }
14110
+ payload.extend(extra);
14111
+ });
14112
+ return;
14113
+ }
14114
+ if (payload.kind !== "timeout") {
14115
+ setImmediate(() => {
14116
+ const base = Math.max(payload.limit, payload.used);
14117
+ const extra = {};
14118
+ switch (payload.kind) {
14119
+ case "iterations":
14120
+ extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
14121
+ break;
14122
+ case "tool_calls":
14123
+ extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
14124
+ break;
14125
+ case "tokens":
14126
+ extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
14127
+ break;
14128
+ case "cost":
14129
+ extra.maxCostUsd = Math.min(base * 1.25, 100);
14130
+ break;
14131
+ }
14132
+ payload.extend(extra);
14133
+ });
14134
+ }
14135
+ });
14136
+ this.disposers.push(dBudget);
14137
+ const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
14138
+ const payload = e.payload;
14139
+ if (payload.sessionId !== this.sessionId) return;
14140
+ this.cancelled = true;
14141
+ if (this._timeoutTimer) {
14142
+ clearTimeout(this._timeoutTimer);
14143
+ this._timeoutTimer = void 0;
14144
+ }
14145
+ this.fleetBus.emit({
14146
+ subagentId: this.director.id,
14147
+ ts: Date.now(),
14148
+ type: "collab.cancelled",
14149
+ payload: { sessionId: this.sessionId, reason: payload.reason }
14150
+ });
14151
+ });
14152
+ this.disposers.push(dCancel);
14153
+ const d1 = this.fleetBus.filter("bug.found", (e) => {
14154
+ const payload = e.payload;
14155
+ if (payload?.finding) {
14156
+ this.bugs.set(payload.finding.id, payload.finding);
14157
+ this.emit("bug.found", payload);
14158
+ }
14159
+ });
14160
+ this.disposers.push(d1);
14161
+ const d2 = this.fleetBus.filter("refactor.plan", (e) => {
14162
+ const payload = e.payload;
14163
+ if (payload?.plan) {
14164
+ this.plans.set(payload.plan.id, payload.plan);
14165
+ this.emit("refactor.plan", payload);
14166
+ }
14167
+ });
14168
+ this.disposers.push(d2);
14169
+ const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
14170
+ const payload = e.payload;
14171
+ if (payload?.evaluation) {
14172
+ this.evaluations.set(payload.evaluation.id, payload.evaluation);
14173
+ this.emit("critic.evaluation", payload);
14174
+ }
14175
+ });
14176
+ this.disposers.push(d3);
14007
14177
  }
14008
- onProviderResponse(e) {
14009
- const snap = this.ensure(e.subagentId);
14010
- const p = e.payload;
14011
- const usage = p?.usage;
14012
- if (!usage) return;
14013
- snap.input += usage.input ?? 0;
14014
- snap.output += usage.output ?? 0;
14015
- snap.cacheRead += usage.cacheRead ?? 0;
14016
- snap.cacheWrite += usage.cacheWrite ?? 0;
14017
- this.total.input += usage.input ?? 0;
14018
- this.total.output += usage.output ?? 0;
14019
- this.total.cacheRead += usage.cacheRead ?? 0;
14020
- this.total.cacheWrite += usage.cacheWrite ?? 0;
14021
- const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
14022
- if (price) {
14023
- 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);
14024
- snap.cost += delta;
14025
- this.total.cost += delta;
14178
+ roleFromSubagentId(subagentId) {
14179
+ for (const [role, id] of this.subagentIds) {
14180
+ if (id === subagentId) return role;
14026
14181
  }
14027
- snap.lastEventAt = e.ts;
14182
+ const match = subagentId.match(/^(bug-hunter|refactor-planner|critic)/);
14183
+ return match?.[1] ?? null;
14028
14184
  }
14029
- onToolExecuted(e) {
14030
- const snap = this.ensure(e.subagentId);
14031
- snap.toolCalls += 1;
14032
- snap.lastEventAt = e.ts;
14185
+ assembleReport() {
14186
+ const bugList = Array.from(this.bugs.values());
14187
+ const planList = Array.from(this.plans.values());
14188
+ const evalList = Array.from(this.evaluations.values());
14189
+ let disposition = "completed";
14190
+ if (this.cancelled) disposition = "cancelled";
14191
+ const verdictOrder = {
14192
+ approve: 0,
14193
+ needs_revision: 1,
14194
+ reject: 2
14195
+ };
14196
+ const overallVerdict = evalList.reduce(
14197
+ (worst, eval_) => {
14198
+ const w = verdictOrder[worst];
14199
+ const c = verdictOrder[eval_.verdict];
14200
+ return c > w ? eval_.verdict : worst;
14201
+ },
14202
+ "approve"
14203
+ );
14204
+ const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
14205
+ return {
14206
+ sessionId: this.sessionId,
14207
+ startedAt: this.snapshot.createdAt,
14208
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
14209
+ targetPaths: this.options.targetPaths,
14210
+ disposition,
14211
+ bugs: bugList,
14212
+ refactorPlans: planList,
14213
+ evaluations: evalList,
14214
+ alerts: [...this.alerts],
14215
+ overallVerdict,
14216
+ summary
14217
+ };
14033
14218
  }
14034
- onIterationStarted(e) {
14035
- const snap = this.ensure(e.subagentId);
14036
- snap.iterations += 1;
14037
- snap.lastEventAt = e.ts;
14219
+ buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
14220
+ const lines = [
14221
+ `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
14222
+ "",
14223
+ `**Target:** ${this.options.targetPaths.join(", ")}`,
14224
+ `**Disposition:** ${disposition.toUpperCase()}`,
14225
+ `**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
14226
+ ""
14227
+ ];
14228
+ if (this.alerts.length > 0) {
14229
+ lines.push("### Alerts", "");
14230
+ for (const alert of this.alerts) {
14231
+ lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
14232
+ }
14233
+ lines.push("");
14234
+ }
14235
+ if (bugs.length > 0) {
14236
+ lines.push("### Bugs Found", "");
14237
+ for (const b of bugs) {
14238
+ lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
14239
+ }
14240
+ lines.push("");
14241
+ }
14242
+ if (plans.length > 0) {
14243
+ lines.push("### Refactor Plans", "");
14244
+ for (const p of plans) {
14245
+ lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
14246
+ for (const phase of p.phases) {
14247
+ lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
14248
+ }
14249
+ }
14250
+ lines.push("");
14251
+ }
14252
+ if (evals.length > 0) {
14253
+ lines.push("### Critic Evaluations", "");
14254
+ for (const e of evals) {
14255
+ lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
14256
+ for (const c of e.concerns) {
14257
+ if (c.severity === "blocking") lines.push(` - ${c.description}`);
14258
+ }
14259
+ }
14260
+ lines.push("");
14261
+ }
14262
+ return lines.join("\n");
14263
+ }
14264
+ cleanup() {
14265
+ for (const dispose of this.disposers) dispose();
14266
+ this.disposers.length = 0;
14038
14267
  }
14039
14268
  };
14269
+
14270
+ // src/coordination/director-prompts.ts
14271
+ var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
14272
+ subagents by spawning them, assigning tasks, awaiting completions, and
14273
+ rolling up their outputs into your next decision.
14274
+
14275
+ Core fleet tools available to you:
14276
+ - spawn_subagent \u2014 create a worker with a chosen provider / model / role
14277
+ - assign_task \u2014 hand a piece of work to a specific subagent
14278
+ - await_tasks \u2014 block until named task ids complete (parallel-safe)
14279
+ - ask_subagent \u2014 synchronously query a running subagent via the bridge
14280
+ - roll_up \u2014 aggregate finished tasks into a markdown/json summary
14281
+ - terminate_subagent \u2014 abort a stuck worker (use sparingly)
14282
+ - fleet_status \u2014 snapshot of all subagents and pending tasks
14283
+ - fleet_usage \u2014 token + cost breakdown per subagent and total
14284
+
14285
+ Working rules:
14286
+ 1. Decompose first. Before spawning, decide which sub-tasks are
14287
+ independent and can run in parallel. Sequential work doesn't need a
14288
+ subagent \u2014 do it yourself.
14289
+ 2. Match worker to job. Cheap/fast model for triage, capable model for
14290
+ synthesis. Different providers per sibling is allowed and encouraged.
14291
+ 3. Always pair an assign with an await. Don't fire-and-forget; you owe
14292
+ the user a single coherent answer at the end.
14293
+ 4. Roll up before deciding. After await_tasks resolves, call roll_up so
14294
+ the results are folded back into your context in a compact form.
14295
+ 5. Budget is real. Check fleet_usage periodically. If a subagent is
14296
+ thrashing, terminate it rather than letting cost climb silently.
14297
+ 6. Never claim a subagent's work as your own without verifying it. If a
14298
+ result looks wrong, ask_subagent for clarification before passing it
14299
+ to the user.
14300
+ 7. Wind down when satisfied. When the results are good enough, call
14301
+ work_complete \u2014 no new subagents will spawn and queued tasks complete
14302
+ as aborted. Running subagents finish naturally. Call terminate_subagent
14303
+ only for ones you need to stop immediately.`;
14304
+ var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
14305
+ a specific slice of a larger plan \u2014 do that slice well and report back.
14306
+
14307
+ Bridge contract:
14308
+ - You have a parent (the Director). You may call \`request\` on the
14309
+ parent bridge to ask a clarifying question. Use this sparingly; the
14310
+ parent is also working.
14311
+ - You MAY NOT request the parent's system prompt, tool list, or other
14312
+ subagents' context. Those are not yours to read.
14313
+ - Your final task output is what the Director sees. Be concise,
14314
+ structured, and self-contained \u2014 assume the Director will paste your
14315
+ output into its own context.`;
14316
+ function composeDirectorPrompt(parts = {}) {
14317
+ const sections = [];
14318
+ const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
14319
+ if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
14320
+ if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
14321
+ sections.push(`Available roles you can spawn:
14322
+ ${parts.rosterSummary.trim()}`);
14323
+ }
14324
+ if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
14325
+ sections.push(parts.basePrompt.trim());
14326
+ }
14327
+ return sections.join("\n\n");
14328
+ }
14329
+ function composeSubagentPrompt(parts = {}) {
14330
+ const sections = [];
14331
+ const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
14332
+ if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
14333
+ if (parts.role && parts.role.trim().length > 0) {
14334
+ sections.push(`Role:
14335
+ ${parts.role.trim()}`);
14336
+ }
14337
+ if (parts.task && parts.task.trim().length > 0) {
14338
+ sections.push(`Task:
14339
+ ${parts.task.trim()}`);
14340
+ }
14341
+ if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
14342
+ sections.push(
14343
+ `Shared notes:
14344
+ A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
14345
+ - Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
14346
+ - Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
14347
+ - Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
14348
+ );
14349
+ }
14350
+ if (parts.override && parts.override.trim().length > 0) {
14351
+ sections.push(parts.override.trim());
14352
+ }
14353
+ return sections.join("\n\n");
14354
+ }
14355
+ function rosterSummaryFromConfigs(roster) {
14356
+ const lines = [];
14357
+ for (const [roleId, cfg] of Object.entries(roster)) {
14358
+ const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
14359
+ const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
14360
+ const tail = headline ? ` \u2014 ${headline}` : "";
14361
+ lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
14362
+ }
14363
+ return lines.join("\n");
14364
+ }
14040
14365
  function makeSpawnTool(director, roster) {
14041
14366
  const inputSchema = {
14042
14367
  type: "object",
@@ -14380,6 +14705,16 @@ function makeCollabDebugTool(director) {
14380
14705
  type: "number",
14381
14706
  minimum: 1,
14382
14707
  description: "Timeout in ms. Default: 600000 (10 minutes)."
14708
+ },
14709
+ maxTargetFiles: {
14710
+ type: "number",
14711
+ minimum: 1,
14712
+ description: "Maximum number of files to include in the snapshot. If not set, the limit is computed dynamically from contextWindow or falls back to the default (30)."
14713
+ },
14714
+ contextWindow: {
14715
+ type: "number",
14716
+ minimum: 1,
14717
+ description: "Context window size (tokens) of the model. When provided and maxTargetFiles is not set, the file limit is computed dynamically as floor((contextWindow * 0.4) / 2000)."
14383
14718
  }
14384
14719
  },
14385
14720
  required: ["targetPaths"]
@@ -14391,7 +14726,9 @@ function makeCollabDebugTool(director) {
14391
14726
  }
14392
14727
  const options = {
14393
14728
  targetPaths: i.targetPaths,
14394
- timeoutMs: i.timeoutMs
14729
+ timeoutMs: i.timeoutMs,
14730
+ maxTargetFiles: i.maxTargetFiles,
14731
+ contextWindow: i.contextWindow
14395
14732
  };
14396
14733
  try {
14397
14734
  const report = await director.spawnCollab(options);
@@ -14402,564 +14739,347 @@ function makeCollabDebugTool(director) {
14402
14739
  planCount: report.refactorPlans.length,
14403
14740
  evaluationCount: report.evaluations.length,
14404
14741
  summary: report.summary,
14405
- bugs: report.bugs,
14406
- refactorPlans: report.refactorPlans,
14407
- evaluations: report.evaluations
14408
- };
14409
- } catch (err) {
14410
- const msg = err instanceof Error ? err.message : String(err);
14411
- return { error: "collab_debug failed: " + msg };
14412
- }
14413
- }
14414
- };
14415
- }
14416
- function makeFleetEmitTool(director) {
14417
- return {
14418
- name: "fleet_emit",
14419
- description: "Emit a structured event on the FleetBus. Any subagent can emit any event type; the Director routes it to all listeners. Use it to stream findings, progress updates, or final results to other agents in real time.",
14420
- permission: "auto",
14421
- mutating: false,
14422
- inputSchema: {
14423
- type: "object",
14424
- properties: {
14425
- type: {
14426
- type: "string",
14427
- description: "Event type string (e.g. bug.found, refactor.plan, critic.evaluation, progress, result)."
14428
- },
14429
- payload: {
14430
- type: "object",
14431
- description: "Event payload. Structure depends on event type. Use null if no payload."
14432
- }
14433
- },
14434
- required: ["type"]
14435
- },
14436
- async execute(input) {
14437
- const i = input;
14438
- director.fleet.emit({
14439
- subagentId: director.id,
14440
- ts: Date.now(),
14441
- type: i.type,
14442
- payload: i.payload ?? {}
14443
- });
14444
- return { ok: true, event: i.type };
14445
- }
14446
- };
14447
- }
14448
- function makeWorkCompleteTool(director) {
14449
- return {
14450
- name: "work_complete",
14451
- description: "Signal that the director is satisfied with the results and the fleet should wind down. After calling this, spawn_subagent will refuse with a budget error and assign_task will instantly complete any queued tasks as aborted. Running subagents finish naturally. Call terminate_subagent separately to stop specific subagents immediately.",
14452
- permission: "auto",
14453
- mutating: false,
14454
- inputSchema: { type: "object", properties: {}, required: [] },
14455
- async execute() {
14456
- director.workComplete();
14457
- return { ok: true, message: "Fleet wind-down signaled. No new spawns or task dispatches." };
14458
- }
14459
- };
14460
- }
14461
- var CollabSession = class extends EventEmitter {
14462
- sessionId;
14463
- options;
14464
- snapshot;
14465
- director;
14466
- fleetBus;
14467
- subagentIds = /* @__PURE__ */ new Map();
14468
- // role → subagentId
14469
- bugs = /* @__PURE__ */ new Map();
14470
- plans = /* @__PURE__ */ new Map();
14471
- evaluations = /* @__PURE__ */ new Map();
14472
- disposers = new Array();
14473
- settled = false;
14474
- timeoutMs;
14475
- cancelled = false;
14476
- alerts = [];
14477
- /** Tracks tool call counts per subagent for progress-based timeout decisions. */
14478
- progressBySubagent = /* @__PURE__ */ new Map();
14479
- /** Last tool call count when a timeout warning was handled. */
14480
- lastTimeoutProgress = /* @__PURE__ */ new Map();
14481
- /** Session-level timeout timer handle (cleared on cancel or natural completion). */
14482
- _timeoutTimer;
14483
- constructor(director, fleetBus, options) {
14484
- super();
14485
- this.sessionId = randomUUID();
14486
- this.options = options;
14487
- this.director = director;
14488
- this.fleetBus = fleetBus;
14489
- this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
14490
- if (options.prebuiltSnapshot) {
14491
- this.snapshot = options.prebuiltSnapshot;
14492
- } else {
14493
- this.snapshot = {
14494
- id: this.sessionId,
14495
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
14496
- files: []
14497
- };
14498
- }
14499
- }
14500
- get id() {
14501
- return this.sessionId;
14502
- }
14503
- getSessionAlerts() {
14504
- return [...this.alerts];
14505
- }
14506
- isCancelled() {
14507
- return this.cancelled;
14508
- }
14509
- /**
14510
- * Snapshot of role → subagentId map. The Director calls coordinator.stop()
14511
- * for each agent when cancelling the session, using this map to enumerate
14512
- * all three collab agents.
14513
- */
14514
- getSubagentIds() {
14515
- return new Map(this.subagentIds);
14516
- }
14517
- async buildSnapshot() {
14518
- if (this.snapshot.files.length > 0) return this.snapshot;
14519
- for (const filePath of (await Promise.all(this.options.targetPaths.map((p) => expandGlob(p)))).flat()) {
14520
- try {
14521
- const content = await fsp3.readFile(filePath, "utf8");
14522
- const ext = filePath.split(".").pop() ?? "";
14523
- const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
14524
- this.snapshot.files.push({ path: filePath, content, language });
14525
- } catch {
14526
- this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
14527
- }
14528
- }
14529
- return this.snapshot;
14530
- }
14531
- /**
14532
- * Cancel the session. Emits director.cancel_collab on the FleetBus so all
14533
- * collab agents finish early. The session-level timeout timer is also cleared.
14534
- * Safe to call multiple times (idempotent after first call).
14535
- */
14536
- cancel(reason = "Director cancelled collab session") {
14537
- if (this.settled) return;
14538
- this.cancelled = true;
14539
- if (this._timeoutTimer) {
14540
- clearTimeout(this._timeoutTimer);
14541
- this._timeoutTimer = void 0;
14542
- }
14543
- this.fleetBus.emit({
14544
- subagentId: this.director.id,
14545
- ts: Date.now(),
14546
- type: "director.cancel_collab",
14547
- payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
14548
- });
14549
- this.fleetBus.emit({
14550
- subagentId: this.director.id,
14551
- ts: Date.now(),
14552
- type: "collab.cancelled",
14553
- payload: { sessionId: this.sessionId, reason }
14554
- });
14555
- }
14556
- async start() {
14557
- if (this.settled) throw new Error("session already settled");
14558
- this.settled = true;
14559
- await this.buildSnapshot();
14560
- this.wireFleetBus();
14561
- const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
14562
- this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
14563
- this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
14564
- this.spawnAgent("critic", this.buildCriticTask())
14565
- ]);
14566
- this.subagentIds.set("bug-hunter", bugHunterId);
14567
- this.subagentIds.set("refactor-planner", refactorPlannerId);
14568
- this.subagentIds.set("critic", criticId);
14569
- const timeout = new Promise((_, reject) => {
14570
- this._timeoutTimer = setTimeout(() => {
14571
- this.cancel("Session-level timeout reached");
14572
- reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
14573
- }, this.timeoutMs);
14574
- });
14575
- let results = null;
14576
- try {
14577
- results = await Promise.race([
14578
- Promise.all([
14579
- this.director.awaitTasks([bugHunterId]),
14580
- this.director.awaitTasks([refactorPlannerId]),
14581
- this.director.awaitTasks([criticId])
14582
- ]),
14583
- timeout
14584
- ]);
14585
- } catch (err) {
14586
- if (this._timeoutTimer) {
14587
- clearTimeout(this._timeoutTimer);
14588
- this._timeoutTimer = void 0;
14589
- }
14590
- this.cleanup();
14591
- const error = err instanceof Error ? err : new Error(String(err));
14592
- this.emit("session.error", error);
14593
- throw error;
14594
- }
14595
- for (const result of results.flat()) {
14596
- await this.parseAndEmit(result);
14597
- }
14598
- const report = this.assembleReport();
14599
- this.cleanup();
14600
- this.emit("session.done", report);
14601
- return report;
14602
- }
14603
- async parseAndEmit(result) {
14604
- if (result.status !== "success" || result.result == null) return;
14605
- const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
14606
- for (const obj of this.extractJsonObjects(text)) {
14607
- const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
14608
- if (!type) continue;
14609
- this.fleetBus.emit({
14610
- subagentId: result.subagentId,
14611
- taskId: result.taskId,
14612
- ts: Date.now(),
14613
- type,
14614
- payload: obj
14615
- });
14616
- }
14617
- }
14618
- extractJsonObjects(text) {
14619
- const objects = [];
14620
- let depth = 0;
14621
- let start = -1;
14622
- let inString = false;
14623
- let escaped = false;
14624
- for (let i = 0; i < text.length; i++) {
14625
- const ch = text[i];
14626
- if (inString) {
14627
- if (escaped) escaped = false;
14628
- else if (ch === "\\") escaped = true;
14629
- else if (ch === '"') inString = false;
14630
- continue;
14631
- }
14632
- if (ch === '"') {
14633
- inString = true;
14634
- } else if (ch === "{") {
14635
- if (depth === 0) start = i;
14636
- depth++;
14637
- } else if (ch === "}" && depth > 0) {
14638
- depth--;
14639
- if (depth === 0 && start >= 0) {
14640
- const candidate = text.slice(start, i + 1);
14641
- try {
14642
- const parsed = JSON.parse(candidate);
14643
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
14644
- objects.push(parsed);
14645
- }
14646
- } catch {
14647
- }
14648
- start = -1;
14649
- }
14650
- }
14651
- }
14652
- return objects;
14653
- }
14654
- budgetForRole(role) {
14655
- if (this.options.budgetOverrides?.[role]) {
14656
- return this.options.budgetOverrides[role];
14657
- }
14658
- const defaults = {
14659
- "bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
14660
- "refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
14661
- "critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
14662
- };
14663
- return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
14664
- }
14665
- async spawnAgent(role, taskBrief) {
14666
- const budget = this.budgetForRole(role);
14667
- const cfg = {
14668
- id: `${role}-${this.sessionId}`,
14669
- name: role,
14670
- role,
14671
- tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
14672
- maxIterations: budget.maxIterations,
14673
- maxToolCalls: budget.maxToolCalls,
14674
- timeoutMs: budget.timeoutMs
14675
- };
14676
- const subagentId = await this.director.spawn(cfg);
14677
- await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
14678
- return subagentId;
14679
- }
14680
- buildBugHunterTask() {
14681
- const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
14682
- const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
14683
- ${f.content}`).join("\n\n");
14684
- return `You are BugHunter. Scan the following files for bugs and code smells.
14685
-
14686
- Target files:
14687
- ${fileContents}
14688
-
14689
- For each bug found, emit it using the fleet_emit tool immediately:
14690
- { "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
14691
-
14692
- After scanning all files, write your full markdown bug report to:
14693
- ${scratchpad}/bug-hunter-report-${this.sessionId}.md
14694
-
14695
- Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
14696
- }
14697
- buildRefactorPlannerTask() {
14698
- const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
14699
- const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
14700
- const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
14701
- ${f.content}`).join("\n\n");
14702
- return `You are RefactorPlanner. Plan refactorings for the following files.
14703
-
14704
- Target files:
14705
- ${fileContents}
14706
-
14707
- Read the BugHunter report at: ${bugHunterReportPath}
14708
-
14709
- For each bug you can address, emit a refactor plan using fleet_emit:
14710
- { "type": "refactor.plan", "payload": { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [{ "number": 1, "title": "<phase>", "tasks": ["<task>"], "risk": "<low|medium|high>" }], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } } }
14711
-
14712
- Also write your full markdown plan to:
14713
- ${scratchpad}/refactor-plan-${this.sessionId}.md
14714
-
14715
- Emit each plan immediately. Do not wait until planning is complete.`;
14716
- }
14717
- buildCriticTask() {
14718
- const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
14719
- const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
14720
- const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
14721
- const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
14722
- ${f.content}`).join("\n\n");
14723
- return `You are Critic. Evaluate bug findings and refactor plans.
14724
-
14725
- Target files:
14726
- ${fileContents}
14727
-
14728
- Read the BugHunter report at: ${bugHunterReportPath}
14729
- Read the RefactorPlanner report at: ${refactorPlanPath}
14730
-
14731
- For each bug and refactor plan, emit your evaluation using fleet_emit:
14732
- { "type": "critic.evaluation", "payload": { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": ["<strength>"], "weaknesses": ["<weakness>"], "concerns": [{ "description": "<concern>", "severity": "<blocking|advisory>" }] } } }
14733
-
14734
- After all evaluations, write your markdown report to:
14735
- ${scratchpad}/critic-report-${this.sessionId}.md
14736
-
14737
- Emit each evaluation immediately. Do not wait until you have read all reports.`;
14738
- }
14739
- wireFleetBus() {
14740
- const dTool = this.fleetBus.filter("tool.executed", (e) => {
14741
- this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
14742
- });
14743
- this.disposers.push(dTool);
14744
- const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
14745
- const payload = e.payload;
14746
- const role = this.roleFromSubagentId(e.subagentId);
14747
- if (!role) return;
14748
- const btwNotes = this.director.getLeaderBtwNotes();
14749
- const alert = {
14750
- sessionId: this.sessionId,
14751
- subagentId: e.subagentId,
14752
- role,
14753
- level: "warning" /* WARNING */,
14754
- message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
14755
- budgetKind: payload.kind,
14756
- elapsedMs: payload.timeoutMs,
14757
- limit: payload.limit,
14758
- btwNotes
14759
- };
14760
- this.alerts.push(alert);
14761
- this.fleetBus.emit({
14762
- subagentId: e.subagentId,
14763
- ts: Date.now(),
14764
- type: "collab.warning",
14765
- payload: alert
14766
- });
14767
- const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
14768
- if (decision === "cancel") {
14769
- this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
14770
- return;
14771
- }
14772
- if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
14773
- const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
14774
- const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
14775
- if (progress <= lastProgress) {
14776
- payload.deny();
14777
- return;
14778
- }
14779
- this.lastTimeoutProgress.set(e.subagentId, progress);
14780
- const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
14781
- setImmediate(() => {
14782
- payload.extend({ timeoutMs: newLimit });
14783
- });
14784
- return;
14785
- }
14786
- if (decision === "extend") {
14787
- setImmediate(() => {
14788
- const base = Math.max(payload.limit, payload.used);
14789
- const extra = {};
14790
- switch (payload.kind) {
14791
- case "iterations":
14792
- extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
14793
- break;
14794
- case "tool_calls":
14795
- extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
14796
- break;
14797
- case "tokens":
14798
- extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
14799
- break;
14800
- case "cost":
14801
- extra.maxCostUsd = Math.min(base * 1.5, 100);
14802
- break;
14803
- }
14804
- payload.extend(extra);
14805
- });
14806
- return;
14807
- }
14808
- if (payload.kind !== "timeout") {
14809
- setImmediate(() => {
14810
- const base = Math.max(payload.limit, payload.used);
14811
- const extra = {};
14812
- switch (payload.kind) {
14813
- case "iterations":
14814
- extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
14815
- break;
14816
- case "tool_calls":
14817
- extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
14818
- break;
14819
- case "tokens":
14820
- extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
14821
- break;
14822
- case "cost":
14823
- extra.maxCostUsd = Math.min(base * 1.25, 100);
14824
- break;
14825
- }
14826
- payload.extend(extra);
14827
- });
14828
- }
14829
- });
14830
- this.disposers.push(dBudget);
14831
- const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
14832
- const payload = e.payload;
14833
- if (payload.sessionId !== this.sessionId) return;
14834
- this.cancelled = true;
14835
- if (this._timeoutTimer) {
14836
- clearTimeout(this._timeoutTimer);
14837
- this._timeoutTimer = void 0;
14742
+ bugs: report.bugs,
14743
+ refactorPlans: report.refactorPlans,
14744
+ evaluations: report.evaluations
14745
+ };
14746
+ } catch (err) {
14747
+ const msg = err instanceof Error ? err.message : String(err);
14748
+ return { error: "collab_debug failed: " + msg };
14838
14749
  }
14839
- this.fleetBus.emit({
14840
- subagentId: this.director.id,
14750
+ }
14751
+ };
14752
+ }
14753
+ function makeFleetEmitTool(director) {
14754
+ return {
14755
+ name: "fleet_emit",
14756
+ description: "Emit a structured event on the FleetBus. Any subagent can emit any event type; the Director routes it to all listeners. Use it to stream findings, progress updates, or final results to other agents in real time.",
14757
+ permission: "auto",
14758
+ mutating: false,
14759
+ inputSchema: {
14760
+ type: "object",
14761
+ properties: {
14762
+ type: {
14763
+ type: "string",
14764
+ description: "Event type string (e.g. bug.found, refactor.plan, critic.evaluation, progress, result)."
14765
+ },
14766
+ payload: {
14767
+ type: "object",
14768
+ description: "Event payload. Structure depends on event type. Use null if no payload."
14769
+ }
14770
+ },
14771
+ required: ["type"]
14772
+ },
14773
+ async execute(input) {
14774
+ const i = input;
14775
+ director.fleet.emit({
14776
+ subagentId: director.id,
14841
14777
  ts: Date.now(),
14842
- type: "collab.cancelled",
14843
- payload: { sessionId: this.sessionId, reason: payload.reason }
14778
+ type: i.type,
14779
+ payload: i.payload ?? {}
14844
14780
  });
14781
+ return { ok: true, event: i.type };
14782
+ }
14783
+ };
14784
+ }
14785
+ function makeWorkCompleteTool(director) {
14786
+ return {
14787
+ name: "work_complete",
14788
+ description: "Signal that the director is satisfied with the results and the fleet should wind down. After calling this, spawn_subagent will refuse with a budget error and assign_task will instantly complete any queued tasks as aborted. Running subagents finish naturally. Call terminate_subagent separately to stop specific subagents immediately.",
14789
+ permission: "auto",
14790
+ mutating: false,
14791
+ inputSchema: { type: "object", properties: {}, required: [] },
14792
+ async execute() {
14793
+ director.workComplete();
14794
+ return { ok: true, message: "Fleet wind-down signaled. No new spawns or task dispatches." };
14795
+ }
14796
+ };
14797
+ }
14798
+
14799
+ // src/coordination/fleet-bus.ts
14800
+ var FleetBus = class {
14801
+ byId = /* @__PURE__ */ new Map();
14802
+ byType = /* @__PURE__ */ new Map();
14803
+ any = /* @__PURE__ */ new Set();
14804
+ /**
14805
+ * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
14806
+ * `onPattern('*')`) to forward all events with subagent attribution, so
14807
+ * new kernel event types are automatically forwarded without any manual
14808
+ * registration. `subagent.*` events are excluded because they originate
14809
+ * from MultiAgentHost on the parent bus, not the subagent's own bus.
14810
+ *
14811
+ * Returns a disposer that detaches every subscription; call on
14812
+ * subagent teardown so the listeners don't outlive the run.
14813
+ */
14814
+ attach(subagentId, bus, taskId) {
14815
+ const off = bus.onAny((type, payload) => {
14816
+ if (type.startsWith("subagent.")) return;
14817
+ this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
14845
14818
  });
14846
- this.disposers.push(dCancel);
14847
- const d1 = this.fleetBus.filter("bug.found", (e) => {
14848
- const payload = e.payload;
14849
- if (payload?.finding) {
14850
- this.bugs.set(payload.finding.id, payload.finding);
14851
- this.emit("bug.found", payload);
14819
+ return () => {
14820
+ off();
14821
+ };
14822
+ }
14823
+ /** Subscribe to every event from one subagent. */
14824
+ subscribe(subagentId, handler) {
14825
+ let set = this.byId.get(subagentId);
14826
+ if (!set) {
14827
+ set = /* @__PURE__ */ new Set();
14828
+ this.byId.set(subagentId, set);
14829
+ }
14830
+ set.add(handler);
14831
+ return () => {
14832
+ set.delete(handler);
14833
+ };
14834
+ }
14835
+ /** Subscribe to one event type across all subagents. */
14836
+ filter(type, handler) {
14837
+ let set = this.byType.get(type);
14838
+ if (!set) {
14839
+ set = /* @__PURE__ */ new Set();
14840
+ this.byType.set(type, set);
14841
+ }
14842
+ set.add(handler);
14843
+ return () => {
14844
+ set.delete(handler);
14845
+ };
14846
+ }
14847
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
14848
+ onAny(handler) {
14849
+ this.any.add(handler);
14850
+ return () => {
14851
+ this.any.delete(handler);
14852
+ };
14853
+ }
14854
+ emit(event) {
14855
+ const byId = this.byId.get(event.subagentId);
14856
+ if (byId)
14857
+ for (const h of byId) {
14858
+ try {
14859
+ h(event);
14860
+ } catch {
14861
+ }
14852
14862
  }
14853
- });
14854
- this.disposers.push(d1);
14855
- const d2 = this.fleetBus.filter("refactor.plan", (e) => {
14856
- const payload = e.payload;
14857
- if (payload?.plan) {
14858
- this.plans.set(payload.plan.id, payload.plan);
14859
- this.emit("refactor.plan", payload);
14863
+ const byType = this.byType.get(event.type);
14864
+ if (byType)
14865
+ for (const h of byType) {
14866
+ try {
14867
+ h(event);
14868
+ } catch {
14869
+ }
14860
14870
  }
14861
- });
14862
- this.disposers.push(d2);
14863
- const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
14864
- const payload = e.payload;
14865
- if (payload?.evaluation) {
14866
- this.evaluations.set(payload.evaluation.id, payload.evaluation);
14867
- this.emit("critic.evaluation", payload);
14871
+ for (const h of this.any) {
14872
+ try {
14873
+ h(event);
14874
+ } catch {
14868
14875
  }
14869
- });
14870
- this.disposers.push(d3);
14876
+ }
14877
+ }
14878
+ };
14879
+ var FleetUsageAggregator = class {
14880
+ constructor(bus, priceLookup, metaLookup) {
14881
+ this.priceLookup = priceLookup;
14882
+ this.metaLookup = metaLookup;
14883
+ this.unsub.push(bus.filter("provider.response", (e) => this.onProviderResponse(e)));
14884
+ this.unsub.push(bus.filter("tool.executed", (e) => this.onToolExecuted(e)));
14885
+ this.unsub.push(bus.filter("iteration.started", (e) => this.onIterationStarted(e)));
14886
+ }
14887
+ priceLookup;
14888
+ metaLookup;
14889
+ perSubagent = /* @__PURE__ */ new Map();
14890
+ total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
14891
+ unsub = new Array();
14892
+ /**
14893
+ * Remove a terminated subagent's data from the aggregator and subtract its
14894
+ * contribution from the running totals. Call this when a subagent is removed
14895
+ * from the fleet so the aggregator doesn't accumulate unbounded data for
14896
+ * entities that will never emit events again.
14897
+ */
14898
+ removeSubagent(subagentId) {
14899
+ const snap = this.perSubagent.get(subagentId);
14900
+ if (!snap) return;
14901
+ this.perSubagent.delete(subagentId);
14902
+ this.total.input -= snap.input;
14903
+ this.total.output -= snap.output;
14904
+ this.total.cacheRead -= snap.cacheRead;
14905
+ this.total.cacheWrite -= snap.cacheWrite;
14906
+ this.total.cost -= snap.cost;
14907
+ }
14908
+ /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
14909
+ dispose() {
14910
+ for (const off of this.unsub) off();
14911
+ this.unsub.length = 0;
14912
+ }
14913
+ /** Live snapshot — safe to call from a tool's execute() body. */
14914
+ snapshot() {
14915
+ return {
14916
+ total: { ...this.total },
14917
+ perSubagent: Object.fromEntries(
14918
+ Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
14919
+ )
14920
+ };
14921
+ }
14922
+ ensure(subagentId) {
14923
+ let snap = this.perSubagent.get(subagentId);
14924
+ if (!snap) {
14925
+ const meta = this.metaLookup?.(subagentId);
14926
+ snap = {
14927
+ subagentId,
14928
+ provider: meta?.provider,
14929
+ model: meta?.model,
14930
+ input: 0,
14931
+ output: 0,
14932
+ cacheRead: 0,
14933
+ cacheWrite: 0,
14934
+ cost: 0,
14935
+ toolCalls: 0,
14936
+ iterations: 0,
14937
+ startedAt: Date.now(),
14938
+ lastEventAt: Date.now()
14939
+ };
14940
+ this.perSubagent.set(subagentId, snap);
14941
+ }
14942
+ return snap;
14943
+ }
14944
+ onProviderResponse(e) {
14945
+ const snap = this.ensure(e.subagentId);
14946
+ const p = e.payload;
14947
+ const usage = p?.usage;
14948
+ if (!usage) return;
14949
+ snap.input += usage.input ?? 0;
14950
+ snap.output += usage.output ?? 0;
14951
+ snap.cacheRead += usage.cacheRead ?? 0;
14952
+ snap.cacheWrite += usage.cacheWrite ?? 0;
14953
+ this.total.input += usage.input ?? 0;
14954
+ this.total.output += usage.output ?? 0;
14955
+ this.total.cacheRead += usage.cacheRead ?? 0;
14956
+ this.total.cacheWrite += usage.cacheWrite ?? 0;
14957
+ const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
14958
+ if (price) {
14959
+ 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);
14960
+ snap.cost += delta;
14961
+ this.total.cost += delta;
14962
+ }
14963
+ snap.lastEventAt = e.ts;
14964
+ }
14965
+ onToolExecuted(e) {
14966
+ const snap = this.ensure(e.subagentId);
14967
+ snap.toolCalls += 1;
14968
+ snap.lastEventAt = e.ts;
14969
+ }
14970
+ onIterationStarted(e) {
14971
+ const snap = this.ensure(e.subagentId);
14972
+ snap.iterations += 1;
14973
+ snap.lastEventAt = e.ts;
14974
+ }
14975
+ };
14976
+
14977
+ // src/coordination/large-answer-store.ts
14978
+ var LargeAnswerStore = class {
14979
+ /**
14980
+ * Responses above this size (in characters) are stored out-of-context.
14981
+ * Below this, the full answer is returned inline (no overhead).
14982
+ * Default: 2000 chars ≈ 400-600 tokens.
14983
+ */
14984
+ sizeThreshold;
14985
+ store = /* @__PURE__ */ new Map();
14986
+ constructor(sizeThreshold = 2e3) {
14987
+ this.sizeThreshold = sizeThreshold;
14871
14988
  }
14872
- roleFromSubagentId(subagentId) {
14873
- for (const [role, id] of this.subagentIds) {
14874
- if (id === subagentId) return role;
14989
+ /**
14990
+ * Store a value, returning a summary + key for inline use.
14991
+ * If the value is below sizeThreshold, returns it as-is (no store entry).
14992
+ */
14993
+ storeAnswer(value) {
14994
+ if (value === void 0 || value === null) {
14995
+ return { summary: String(value), inline: true };
14875
14996
  }
14876
- const match = subagentId.match(/^(bug-hunter|refactor-planner|critic)/);
14877
- return match?.[1] ?? null;
14878
- }
14879
- assembleReport() {
14880
- const bugList = Array.from(this.bugs.values());
14881
- const planList = Array.from(this.plans.values());
14882
- const evalList = Array.from(this.evaluations.values());
14883
- let disposition = "completed";
14884
- if (this.cancelled) disposition = "cancelled";
14885
- const verdictOrder = {
14886
- approve: 0,
14887
- needs_revision: 1,
14888
- reject: 2
14889
- };
14890
- const overallVerdict = evalList.reduce(
14891
- (worst, eval_) => {
14892
- const w = verdictOrder[worst];
14893
- const c = verdictOrder[eval_.verdict];
14894
- return c > w ? eval_.verdict : worst;
14895
- },
14896
- "approve"
14897
- );
14898
- const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
14997
+ const serialized = typeof value === "string" ? value : JSON.stringify(value);
14998
+ const size = serialized.length;
14999
+ if (size <= this.sizeThreshold) {
15000
+ return { summary: serialized.slice(0, 500), inline: true };
15001
+ }
15002
+ const key = `a-${hashStr(serialized)}`;
15003
+ this.store.set(key, {
15004
+ key,
15005
+ value,
15006
+ size,
15007
+ storedAt: Date.now()
15008
+ });
14899
15009
  return {
14900
- sessionId: this.sessionId,
14901
- startedAt: this.snapshot.createdAt,
14902
- completedAt: (/* @__PURE__ */ new Date()).toISOString(),
14903
- targetPaths: this.options.targetPaths,
14904
- disposition,
14905
- bugs: bugList,
14906
- refactorPlans: planList,
14907
- evaluations: evalList,
14908
- alerts: [...this.alerts],
14909
- overallVerdict,
14910
- summary
15010
+ key,
15011
+ summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
15012
+ inline: false
14911
15013
  };
14912
15014
  }
14913
- buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
14914
- const lines = [
14915
- `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
14916
- "",
14917
- `**Target:** ${this.options.targetPaths.join(", ")}`,
14918
- `**Disposition:** ${disposition.toUpperCase()}`,
14919
- `**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
14920
- ""
14921
- ];
14922
- if (this.alerts.length > 0) {
14923
- lines.push("### Alerts", "");
14924
- for (const alert of this.alerts) {
14925
- lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
14926
- }
14927
- lines.push("");
14928
- }
14929
- if (bugs.length > 0) {
14930
- lines.push("### Bugs Found", "");
14931
- for (const b of bugs) {
14932
- lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
14933
- }
14934
- lines.push("");
14935
- }
14936
- if (plans.length > 0) {
14937
- lines.push("### Refactor Plans", "");
14938
- for (const p of plans) {
14939
- lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
14940
- for (const phase of p.phases) {
14941
- lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
14942
- }
14943
- }
14944
- lines.push("");
14945
- }
14946
- if (evals.length > 0) {
14947
- lines.push("### Critic Evaluations", "");
14948
- for (const e of evals) {
14949
- lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
14950
- for (const c of e.concerns) {
14951
- if (c.severity === "blocking") lines.push(` - ${c.description}`);
14952
- }
14953
- }
14954
- lines.push("");
14955
- }
14956
- return lines.join("\n");
15015
+ /**
15016
+ * Retrieve a previously stored answer by its key.
15017
+ * Returns undefined if the key is unknown or the store was cleared.
15018
+ */
15019
+ retrieveAnswer(key) {
15020
+ return this.store.get(key)?.value;
14957
15021
  }
14958
- cleanup() {
14959
- for (const dispose of this.disposers) dispose();
14960
- this.disposers.length = 0;
15022
+ /**
15023
+ * Check if a key exists in the store.
15024
+ */
15025
+ hasAnswer(key) {
15026
+ return this.store.has(key);
15027
+ }
15028
+ /** Number of stored entries. */
15029
+ get size() {
15030
+ return this.store.size;
15031
+ }
15032
+ /** Total characters stored. */
15033
+ get totalChars() {
15034
+ let total = 0;
15035
+ for (const e of this.store.values()) total += e.size;
15036
+ return total;
15037
+ }
15038
+ /** Clear all stored entries. Call at the end of a director run. */
15039
+ clear() {
15040
+ this.store.clear();
14961
15041
  }
14962
15042
  };
15043
+ function hashStr(s) {
15044
+ let h = 5381;
15045
+ for (let i = 0; i < s.length; i++) {
15046
+ h = h * 33 ^ s.charCodeAt(i);
15047
+ }
15048
+ return (h >>> 0).toString(36);
15049
+ }
15050
+
15051
+ // src/coordination/model-matrix.ts
15052
+ var MATRIX_PHASE_KEYS = Object.keys(AGENTS_BY_PHASE);
15053
+ var ROLE_TO_PHASE = (() => {
15054
+ const map = {};
15055
+ for (const [phase, defs] of Object.entries(AGENTS_BY_PHASE)) {
15056
+ for (const def of defs) {
15057
+ const role = def.config.role;
15058
+ if (role) map[role] = phase;
15059
+ }
15060
+ }
15061
+ return map;
15062
+ })();
15063
+ function phaseForRole(role) {
15064
+ return role ? ROLE_TO_PHASE[role] : void 0;
15065
+ }
15066
+ function resolveModelMatrix(matrix, role) {
15067
+ if (!matrix) return void 0;
15068
+ if (role && matrix[role]) return matrix[role];
15069
+ const phase = phaseForRole(role);
15070
+ if (phase && matrix[phase]) return matrix[phase];
15071
+ if (matrix["*"]) return matrix["*"];
15072
+ return void 0;
15073
+ }
15074
+ function matrixKeyKind(key) {
15075
+ if (key === "*") return "default";
15076
+ if (key in AGENT_CATALOG) return "role";
15077
+ if (MATRIX_PHASE_KEYS.includes(key)) return "phase";
15078
+ return "unknown";
15079
+ }
15080
+ function isValidMatrixKey(key) {
15081
+ return matrixKeyKind(key) !== "unknown";
15082
+ }
14963
15083
 
14964
15084
  // src/coordination/director.ts
14965
15085
  var FleetSpawnBudgetError = class extends Error {
@@ -15131,6 +15251,9 @@ var Director = class _Director {
15131
15251
  maxLeaderContextLoad;
15132
15252
  /** Provider's max context window in tokens. */
15133
15253
  maxContext;
15254
+ /** Per-task model matrix (static record or live getter); resolved
15255
+ * per-spawn when no explicit model is set. */
15256
+ modelMatrix;
15134
15257
  /**
15135
15258
  * When set by `workComplete()`, the director stops dispatching new tasks
15136
15259
  * and terminates all running subagents. Used when the director's LLM decides
@@ -15161,20 +15284,23 @@ var Director = class _Director {
15161
15284
  this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 5;
15162
15285
  this.maxLeaderContextLoad = opts.maxLeaderContextLoad ?? 0.85;
15163
15286
  this.maxContext = opts.maxContext ?? 128e3;
15287
+ this.modelMatrix = opts.modelMatrix;
15164
15288
  this.sessionsRoot = opts.sessionsRoot;
15165
15289
  this.directorRunId = opts.directorRunId ?? this.id;
15166
- this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
15167
- directorRunId: this.id,
15168
- maxSpawns: opts.maxSpawns,
15169
- spawnDepth: this.spawnDepth,
15170
- maxSpawnDepth: this.maxSpawnDepth,
15171
- directorBudget: opts.directorBudget
15172
- }, opts.checkpointDebounceMs ?? 250) : null;
15290
+ this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
15291
+ opts.stateCheckpointPath,
15292
+ {
15293
+ directorRunId: this.id,
15294
+ maxSpawns: opts.maxSpawns,
15295
+ spawnDepth: this.spawnDepth,
15296
+ maxSpawnDepth: this.maxSpawnDepth,
15297
+ directorBudget: opts.directorBudget
15298
+ },
15299
+ opts.checkpointDebounceMs ?? 250
15300
+ ) : null;
15173
15301
  this.fleetManager = opts.fleetManager;
15174
15302
  if (this.sharedScratchpadPath) {
15175
- void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
15176
- (err) => this.logShutdownError("shared_scratchpad_mkdir", err)
15177
- );
15303
+ void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
15178
15304
  }
15179
15305
  this.transport = new InMemoryBridgeTransport();
15180
15306
  this.bridge = new InMemoryAgentBridge(
@@ -15352,7 +15478,12 @@ var Director = class _Director {
15352
15478
  */
15353
15479
  workComplete() {
15354
15480
  this.workCompleteFlag = true;
15355
- this.fleet.emit({ subagentId: this.id, ts: Date.now(), type: "director.work_complete", payload: {} });
15481
+ this.fleet.emit({
15482
+ subagentId: this.id,
15483
+ ts: Date.now(),
15484
+ type: "director.work_complete",
15485
+ payload: {}
15486
+ });
15356
15487
  }
15357
15488
  /** Returns true if `workComplete()` has been called on this director. */
15358
15489
  isWorkComplete() {
@@ -15484,13 +15615,25 @@ var Director = class _Director {
15484
15615
  "workComplete() has been called \u2014 director closed further spawning"
15485
15616
  );
15486
15617
  }
15618
+ if (!config.model && this.modelMatrix) {
15619
+ const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
15620
+ const entry = resolveModelMatrix(matrix, config.role);
15621
+ if (entry) {
15622
+ config.model = entry.model;
15623
+ if (entry.provider) config.provider = entry.provider;
15624
+ }
15625
+ }
15487
15626
  if (this.fleetManager) {
15488
15627
  const rejection = this.fleetManager.canSpawn(config);
15489
15628
  if (rejection) {
15490
- if (rejection.kind === "max_spawn_depth") throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
15491
- if (rejection.kind === "max_spawns") throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
15492
- if (rejection.kind === "max_cost_usd") throw new FleetCostCapError(rejection.limit, rejection.observed);
15493
- if (rejection.kind === "max_context_load") throw new FleetContextOverflowError(rejection.limit, rejection.observed);
15629
+ if (rejection.kind === "max_spawn_depth")
15630
+ throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
15631
+ if (rejection.kind === "max_spawns")
15632
+ throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
15633
+ if (rejection.kind === "max_cost_usd")
15634
+ throw new FleetCostCapError(rejection.limit, rejection.observed);
15635
+ if (rejection.kind === "max_context_load")
15636
+ throw new FleetContextOverflowError(rejection.limit, rejection.observed);
15494
15637
  }
15495
15638
  } else {
15496
15639
  if (this.spawnDepth >= this.maxSpawnDepth) {
@@ -15520,7 +15663,9 @@ var Director = class _Director {
15520
15663
  this.fleetManager.assignNicknameAndRecord(config);
15521
15664
  } else {
15522
15665
  config.name = assignNickname(role, this._usedNicknames);
15523
- this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
15666
+ this._usedNicknames.add(
15667
+ config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-")
15668
+ );
15524
15669
  }
15525
15670
  }
15526
15671
  result = await this.coordinator.spawn(config);
@@ -15542,6 +15687,20 @@ var Director = class _Director {
15542
15687
  );
15543
15688
  this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
15544
15689
  this.subagentBridges.set(result.subagentId, subagentBridge);
15690
+ this.fleet.emit({
15691
+ subagentId: result.subagentId,
15692
+ ts: Date.now(),
15693
+ type: "subagent.spawned",
15694
+ payload: {
15695
+ subagentId: result.subagentId,
15696
+ taskId: "",
15697
+ // taskId will be set when assign() is called
15698
+ name: config.name,
15699
+ role: config.role,
15700
+ provider: config.provider,
15701
+ model: config.model
15702
+ }
15703
+ });
15545
15704
  if (!this.fleetManager) {
15546
15705
  this.manifestEntries.set(result.subagentId, {
15547
15706
  subagentId: result.subagentId,
@@ -15741,7 +15900,11 @@ var Director = class _Director {
15741
15900
  subagentId: taskWithId.subagentId ?? "unassigned",
15742
15901
  taskId: taskWithId.id,
15743
15902
  status: "stopped",
15744
- error: { kind: "aborted_by_parent", message: "Director called workComplete() \u2014 no further tasks will run", retryable: false },
15903
+ error: {
15904
+ kind: "aborted_by_parent",
15905
+ message: "Director called workComplete() \u2014 no further tasks will run",
15906
+ retryable: false
15907
+ },
15745
15908
  iterations: 0,
15746
15909
  toolCalls: 0,
15747
15910
  durationMs: 0
@@ -22931,22 +23094,24 @@ var ReportGenerator = class {
22931
23094
  medium: "#ca8a04",
22932
23095
  low: "#16a34a"
22933
23096
  };
22934
- const rows = result.findings.map(
22935
- (f) => `
23097
+ const rows = result.findings.map((f) => {
23098
+ const sevColor = severityColors[f.severity] ?? "#000000";
23099
+ const loc = `${escapeHtml(f.file)}${f.line ? `:${escapeHtml(String(f.line))}` : ""}`;
23100
+ return `
22936
23101
  <tr>
22937
- <td style="color: ${severityColors[f.severity]}; font-weight: bold;">${f.severity.toUpperCase()}</td>
22938
- <td>${f.category}</td>
22939
- <td>${f.title}</td>
22940
- <td><code>${f.file}${f.line ? `:${f.line}` : ""}</code></td>
22941
- <td>${f.remediation}</td>
23102
+ <td style="color: ${escapeHtml(sevColor)}; font-weight: bold;">${escapeHtml(f.severity.toUpperCase())}</td>
23103
+ <td>${escapeHtml(f.category)}</td>
23104
+ <td>${escapeHtml(f.title)}</td>
23105
+ <td><code>${loc}</code></td>
23106
+ <td>${escapeHtml(f.remediation)}</td>
22942
23107
  </tr>
22943
- `
22944
- ).join("");
23108
+ `;
23109
+ }).join("");
22945
23110
  return `
22946
23111
  <!DOCTYPE html>
22947
23112
  <html>
22948
23113
  <head>
22949
- <title>Security Scan Report - ${new Date(result.timestamp).toLocaleDateString()}</title>
23114
+ <title>Security Scan Report - ${escapeHtml(new Date(result.timestamp).toLocaleDateString())}</title>
22950
23115
  <style>
22951
23116
  body { font-family: system-ui, sans-serif; margin: 2rem; }
22952
23117
  table { border-collapse: collapse; width: 100%; }
@@ -22957,9 +23122,9 @@ var ReportGenerator = class {
22957
23122
  </head>
22958
23123
  <body>
22959
23124
  <h1>Security Scan Report</h1>
22960
- <p><strong>Generated:</strong> ${new Date(result.timestamp).toLocaleString()}</p>
22961
- <p><strong>Project:</strong> ${result.projectRoot}</p>
22962
- <p><strong>Tech Stack:</strong> ${result.techStack.stack} (${result.techStack.packageManager})</p>
23125
+ <p><strong>Generated:</strong> ${escapeHtml(new Date(result.timestamp).toLocaleString())}</p>
23126
+ <p><strong>Project:</strong> ${escapeHtml(result.projectRoot)}</p>
23127
+ <p><strong>Tech Stack:</strong> ${escapeHtml(result.techStack.stack)} (${escapeHtml(result.techStack.packageManager)})</p>
22963
23128
 
22964
23129
  <h2>Summary</h2>
22965
23130
  <ul>
@@ -22985,6 +23150,9 @@ var ReportGenerator = class {
22985
23150
  `.trim();
22986
23151
  }
22987
23152
  };
23153
+ function escapeHtml(value) {
23154
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
23155
+ }
22988
23156
  var defaultReportGenerator = new ReportGenerator();
22989
23157
 
22990
23158
  // src/security-scanner/gitignore-updater.ts
@@ -26996,6 +27164,7 @@ var PhaseOrchestrator = class {
26996
27164
  maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
26997
27165
  maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
26998
27166
  maxRetries: opts.maxRetries ?? 2,
27167
+ maxVerifyAttempts: opts.maxVerifyAttempts ?? 2,
26999
27168
  autonomous: opts.autonomous ?? true,
27000
27169
  phaseDelayMs: opts.phaseDelayMs ?? 0,
27001
27170
  stopOnFailure: opts.stopOnFailure ?? true,
@@ -27042,7 +27211,10 @@ var PhaseOrchestrator = class {
27042
27211
  resume() {
27043
27212
  this.paused = false;
27044
27213
  this.tick().catch((err) => {
27045
- console.error("[phase-orchestrator] tick failed:", err instanceof Error ? err.message : String(err));
27214
+ console.error(
27215
+ "[phase-orchestrator] tick failed:",
27216
+ err instanceof Error ? err.message : String(err)
27217
+ );
27046
27218
  });
27047
27219
  }
27048
27220
  /** Tamamen durdur — aktif fazlar da durdurulur */
@@ -27123,33 +27295,30 @@ var PhaseOrchestrator = class {
27123
27295
  failed: failedTasks
27124
27296
  });
27125
27297
  if (failedTasks > 0 && this.opts.stopOnFailure) {
27126
- this.updatePhaseStatus(phase, "failed");
27127
- phase.completedAt = Date.now();
27128
- phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
27129
- this.runningPhases.delete(phase.id);
27130
- this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
27131
- this.emit("phase.failed", {
27132
- phaseId: phase.id,
27133
- name: phase.name,
27134
- error: `${failedTasks} task(s) failed`
27135
- });
27136
- this.ctx.onPhaseFail?.(phase, new Error(`${failedTasks} task(s) failed`));
27137
- await this.keepWorktreeForReview(phase);
27138
- } else {
27139
- this.updatePhaseStatus(phase, "completed");
27140
- phase.completedAt = Date.now();
27141
- phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
27142
- this.runningPhases.delete(phase.id);
27143
- this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
27144
- this.graph.completedPhaseIds.push(phase.id);
27145
- this.emit("phase.completed", {
27146
- phaseId: phase.id,
27147
- name: phase.name,
27148
- durationMs: phase.actualDurationMs
27149
- });
27150
- this.ctx.onPhaseComplete?.(phase);
27151
- await this.commitAndEnqueueMerge(phase);
27298
+ await this.failPhaseAfterTasks(phase, `${failedTasks} task(s) failed`);
27299
+ return;
27300
+ }
27301
+ const verdict = await this.runVerifyGate(phase);
27302
+ if (!verdict.ok) {
27303
+ await this.failPhaseAfterTasks(
27304
+ phase,
27305
+ `verification failed${verdict.output ? `: ${this.truncate(verdict.output)}` : ""}`
27306
+ );
27307
+ return;
27152
27308
  }
27309
+ this.updatePhaseStatus(phase, "completed");
27310
+ phase.completedAt = Date.now();
27311
+ phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
27312
+ this.runningPhases.delete(phase.id);
27313
+ this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
27314
+ this.graph.completedPhaseIds.push(phase.id);
27315
+ this.emit("phase.completed", {
27316
+ phaseId: phase.id,
27317
+ name: phase.name,
27318
+ durationMs: phase.actualDurationMs
27319
+ });
27320
+ this.ctx.onPhaseComplete?.(phase);
27321
+ await this.commitAndEnqueueMerge(phase);
27153
27322
  } catch (error) {
27154
27323
  this.updatePhaseStatus(phase, "failed");
27155
27324
  phase.completedAt = Date.now();
@@ -27166,6 +27335,69 @@ var PhaseOrchestrator = class {
27166
27335
  await this.keepWorktreeForReview(phase);
27167
27336
  }
27168
27337
  }
27338
+ // ─── Verification gate ──────────────────────────────────────────────────────
27339
+ /**
27340
+ * Run the verification gate for a phase whose tasks all succeeded. Verifies in
27341
+ * the phase's worktree; on failure, runs the repair pass and re-verifies, up to
27342
+ * `maxVerifyAttempts` repairs. Returns the final verdict. When no `verifyPhase`
27343
+ * callback is wired the gate is a no-op and always passes.
27344
+ */
27345
+ async runVerifyGate(phase) {
27346
+ if (!this.ctx.verifyPhase) return { ok: true };
27347
+ const env = this.worktreeEnv(phase);
27348
+ for (let attempt = 0; attempt <= this.opts.maxVerifyAttempts; attempt++) {
27349
+ if (this.stopped) return { ok: false, output: "stopped before verification completed" };
27350
+ this.emit("phase.verifying", { phaseId: phase.id, name: phase.name, attempt });
27351
+ let verdict;
27352
+ try {
27353
+ verdict = await this.ctx.verifyPhase(phase, env);
27354
+ } catch (err) {
27355
+ verdict = { ok: false, output: err instanceof Error ? err.message : String(err) };
27356
+ }
27357
+ if (verdict.ok) return { ok: true };
27358
+ this.emit("phase.verifyFailed", {
27359
+ phaseId: phase.id,
27360
+ name: phase.name,
27361
+ attempt,
27362
+ error: verdict.output
27363
+ });
27364
+ if (attempt >= this.opts.maxVerifyAttempts || !this.ctx.repairPhase || this.stopped) {
27365
+ return { ok: false, output: verdict.output };
27366
+ }
27367
+ this.emit("phase.repairing", { phaseId: phase.id, name: phase.name, attempt: attempt + 1 });
27368
+ try {
27369
+ await this.ctx.repairPhase(
27370
+ phase,
27371
+ verdict.output ?? "verification failed",
27372
+ attempt + 1,
27373
+ env
27374
+ );
27375
+ } catch {
27376
+ }
27377
+ }
27378
+ return { ok: false };
27379
+ }
27380
+ /** Worktree env (cwd/branch) for a phase, or undefined if it runs on the shared tree. */
27381
+ worktreeEnv(phase) {
27382
+ const handle = this.phaseWorktrees.get(phase.id);
27383
+ return handle ? { cwd: handle.dir, branch: handle.branch } : void 0;
27384
+ }
27385
+ /** Shared failure bookkeeping for a phase whose tasks ran but the phase failed. */
27386
+ async failPhaseAfterTasks(phase, error) {
27387
+ this.updatePhaseStatus(phase, "failed");
27388
+ phase.completedAt = Date.now();
27389
+ phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
27390
+ this.runningPhases.delete(phase.id);
27391
+ this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
27392
+ this.emit("phase.failed", { phaseId: phase.id, name: phase.name, error });
27393
+ this.ctx.onPhaseFail?.(phase, new Error(error));
27394
+ await this.keepWorktreeForReview(phase);
27395
+ }
27396
+ /** Trim long verifier output so it fits cleanly in an event/error message. */
27397
+ truncate(text, max = 500) {
27398
+ const t2 = text.trim();
27399
+ return t2.length <= max ? t2 : `${t2.slice(0, max)}\u2026 (+${t2.length - max} chars)`;
27400
+ }
27169
27401
  // ─── Worktree integration ───────────────────────────────────────────────────
27170
27402
  /**
27171
27403
  * Commit the phase's worktree changes, then enqueue the merge back into the
@@ -27188,11 +27420,26 @@ var PhaseOrchestrator = class {
27188
27420
  })();
27189
27421
  this.phaseMergePromise.set(phase.id, merged);
27190
27422
  }
27191
- /** Squash-merge one phase. Conflicts mark the worktree needs-review (run continues). */
27423
+ /**
27424
+ * Squash-merge one phase. When a `resolveConflict` callback is wired, a merge
27425
+ * conflict is handed to it (a resolver subagent) before giving up; only if
27426
+ * that fails does the worktree fall to needs-review and the run continues.
27427
+ */
27192
27428
  async mergeOne(phase, handle) {
27193
27429
  if (!this.worktrees) return;
27194
27430
  try {
27195
- const result = await this.worktrees.merge(handle, { squash: true });
27431
+ const resolve10 = this.ctx.resolveConflict ? async (info) => {
27432
+ this.emit("phase.conflictResolving", {
27433
+ phaseId: phase.id,
27434
+ name: phase.name,
27435
+ files: info.conflictFiles
27436
+ });
27437
+ return this.ctx.resolveConflict(phase, info);
27438
+ } : void 0;
27439
+ const result = await this.worktrees.merge(handle, { squash: true, resolve: resolve10 });
27440
+ if (result.resolved) {
27441
+ this.emit("phase.conflictResolved", { phaseId: phase.id, name: phase.name });
27442
+ }
27196
27443
  await this.worktrees.release(handle, { keep: !result.ok });
27197
27444
  } catch (err) {
27198
27445
  this.emit("phase.failed", {
@@ -27256,7 +27503,11 @@ var PhaseOrchestrator = class {
27256
27503
  const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
27257
27504
  if (currentRetries < this.opts.maxRetries) {
27258
27505
  this.taskRetryCounts.set(taskKey, currentRetries + 1);
27259
- tracker.updateNodeStatus(task.id, "pending", `Retry ${currentRetries + 1}/${this.opts.maxRetries}`);
27506
+ tracker.updateNodeStatus(
27507
+ task.id,
27508
+ "pending",
27509
+ `Retry ${currentRetries + 1}/${this.opts.maxRetries}`
27510
+ );
27260
27511
  this.emit("phase.taskRetrying", {
27261
27512
  phaseId: phase.id,
27262
27513
  taskId: task.id,
@@ -27265,7 +27516,11 @@ var PhaseOrchestrator = class {
27265
27516
  maxRetries: this.opts.maxRetries
27266
27517
  });
27267
27518
  } else {
27268
- tracker.updateNodeStatus(task.id, "failed", error instanceof Error ? error.message : String(error));
27519
+ tracker.updateNodeStatus(
27520
+ task.id,
27521
+ "failed",
27522
+ error instanceof Error ? error.message : String(error)
27523
+ );
27269
27524
  this.emit("phase.taskFailed", {
27270
27525
  phaseId: phase.id,
27271
27526
  taskId: task.id,
@@ -28216,6 +28471,10 @@ var WorktreeManager = class {
28216
28471
  ${merged.stderr}`);
28217
28472
  const fromIndex = await this.unmergedFiles();
28218
28473
  const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
28474
+ if (opts.resolve) {
28475
+ const finalized = await this.tryResolveConflict(handle, conflictFiles, opts);
28476
+ if (finalized) return finalized;
28477
+ }
28219
28478
  await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot);
28220
28479
  handle.conflictFiles = conflictFiles;
28221
28480
  this.setStatus(handle, "needs-review", { lastError: merged.stderr });
@@ -28246,6 +28505,52 @@ ${merged.stderr}`);
28246
28505
  });
28247
28506
  return { ok: true };
28248
28507
  }
28508
+ /**
28509
+ * Run the caller-supplied resolver against a conflicted squash-merge, then
28510
+ * commit if it cleared every marker. Returns a successful `MergeResult` on a
28511
+ * clean resolution, or `null` to signal the caller should fall back to the
28512
+ * abort path. Never leaves the base tree committed-but-dirty: a partial or
28513
+ * failed resolution returns `null` and the caller hard-resets.
28514
+ */
28515
+ async tryResolveConflict(handle, conflictFiles, opts) {
28516
+ let resolved = false;
28517
+ try {
28518
+ resolved = await opts.resolve({ conflictFiles, cwd: this.projectRoot });
28519
+ } catch {
28520
+ resolved = false;
28521
+ }
28522
+ if (!resolved) return null;
28523
+ await this.runGit(["add", "-A"], this.projectRoot);
28524
+ if (await this.hasConflictMarkers()) return null;
28525
+ const idArgs = await this.identityArgs(this.projectRoot);
28526
+ const msg = opts.message ?? `merge ${handle.branch} (squash, conflict resolved)`;
28527
+ const commit = await this.runGit([...idArgs, "commit", "-m", msg], this.projectRoot);
28528
+ if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
28529
+ return null;
28530
+ }
28531
+ handle.conflictFiles = conflictFiles;
28532
+ this.setStatus(handle, "merged");
28533
+ this.emit("worktree.merged", {
28534
+ handleId: handle.id,
28535
+ ownerId: handle.ownerId,
28536
+ branch: handle.branch,
28537
+ baseBranch: handle.baseBranch,
28538
+ squash: true
28539
+ });
28540
+ return { ok: true, resolved: true, conflictFiles };
28541
+ }
28542
+ /**
28543
+ * True when staged content still carries conflict markers. `git diff --cached
28544
+ * --check` exits nonzero and prints a "leftover conflict marker" line for each
28545
+ * survivor; whitespace-only errors (also flagged by --check) are ignored so a
28546
+ * clean resolution with unrelated whitespace is not rejected.
28547
+ */
28548
+ async hasConflictMarkers() {
28549
+ const check = await this.runGit(["diff", "--cached", "--check"], this.projectRoot);
28550
+ if (check.code === 0) return false;
28551
+ return /conflict marker/i.test(`${check.stdout}
28552
+ ${check.stderr}`);
28553
+ }
28249
28554
  /**
28250
28555
  * Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
28251
28556
  * are left on disk for inspection.
@@ -29648,6 +29953,6 @@ ${formatPlan(updated)}`
29648
29953
  };
29649
29954
  }
29650
29955
 
29651
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, 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, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
29956
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, onResize, parseContinueDirective, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, setRawMode, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, writeErr, writeOut, zaiVisionServer };
29652
29957
  //# sourceMappingURL=index.js.map
29653
29958
  //# sourceMappingURL=index.js.map