@wrongstack/core 0.1.7 → 0.1.9

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.
@@ -3713,6 +3713,182 @@ function defaultFormatTaskInput(task) {
3713
3713
  return task.description ?? "";
3714
3714
  }
3715
3715
 
3716
+ // src/defaults/fleet-bus.ts
3717
+ var FleetBus = class {
3718
+ byId = /* @__PURE__ */ new Map();
3719
+ byType = /* @__PURE__ */ new Map();
3720
+ any = /* @__PURE__ */ new Set();
3721
+ /**
3722
+ * Hook a subagent's EventBus into the fleet. EventBus is strongly
3723
+ * typed and doesn't expose an `onAny` hook, so we subscribe to the
3724
+ * canonical set of event types a subagent emits during a run. New
3725
+ * event types added to the kernel must be added here too — but the
3726
+ * cost is a tiny single line per type, and the explicit list keeps
3727
+ * the wire format clear.
3728
+ *
3729
+ * Returns a disposer that detaches every subscription; call on
3730
+ * subagent teardown so the listeners don't outlive the run.
3731
+ */
3732
+ attach(subagentId, bus, taskId) {
3733
+ const FORWARDED_TYPES = [
3734
+ "tool.started",
3735
+ "tool.executed",
3736
+ "tool.progress",
3737
+ "tool.confirm_needed",
3738
+ "iteration.started",
3739
+ "iteration.completed",
3740
+ "provider.text_delta",
3741
+ "provider.response",
3742
+ "provider.retry",
3743
+ "provider.error",
3744
+ "session.started",
3745
+ "session.ended",
3746
+ "token.threshold"
3747
+ ];
3748
+ const offs = [];
3749
+ for (const t of FORWARDED_TYPES) {
3750
+ offs.push(
3751
+ bus.on(t, (payload) => {
3752
+ this.emit({ subagentId, taskId, ts: Date.now(), type: t, payload });
3753
+ })
3754
+ );
3755
+ }
3756
+ return () => {
3757
+ for (const off of offs) off();
3758
+ };
3759
+ }
3760
+ /** Subscribe to every event from one subagent. */
3761
+ subscribe(subagentId, handler) {
3762
+ let set = this.byId.get(subagentId);
3763
+ if (!set) {
3764
+ set = /* @__PURE__ */ new Set();
3765
+ this.byId.set(subagentId, set);
3766
+ }
3767
+ set.add(handler);
3768
+ return () => {
3769
+ set.delete(handler);
3770
+ };
3771
+ }
3772
+ /** Subscribe to one event type across all subagents. */
3773
+ filter(type, handler) {
3774
+ let set = this.byType.get(type);
3775
+ if (!set) {
3776
+ set = /* @__PURE__ */ new Set();
3777
+ this.byType.set(type, set);
3778
+ }
3779
+ set.add(handler);
3780
+ return () => {
3781
+ set.delete(handler);
3782
+ };
3783
+ }
3784
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
3785
+ onAny(handler) {
3786
+ this.any.add(handler);
3787
+ return () => {
3788
+ this.any.delete(handler);
3789
+ };
3790
+ }
3791
+ emit(event) {
3792
+ const byId = this.byId.get(event.subagentId);
3793
+ if (byId) for (const h of byId) {
3794
+ try {
3795
+ h(event);
3796
+ } catch {
3797
+ }
3798
+ }
3799
+ const byType = this.byType.get(event.type);
3800
+ if (byType) for (const h of byType) {
3801
+ try {
3802
+ h(event);
3803
+ } catch {
3804
+ }
3805
+ }
3806
+ for (const h of this.any) {
3807
+ try {
3808
+ h(event);
3809
+ } catch {
3810
+ }
3811
+ }
3812
+ }
3813
+ };
3814
+ var FleetUsageAggregator = class {
3815
+ constructor(bus, priceLookup, metaLookup) {
3816
+ this.bus = bus;
3817
+ this.priceLookup = priceLookup;
3818
+ this.metaLookup = metaLookup;
3819
+ bus.filter("provider.response", (e) => this.onProviderResponse(e));
3820
+ bus.filter("tool.executed", (e) => this.onToolExecuted(e));
3821
+ bus.filter("iteration.started", (e) => this.onIterationStarted(e));
3822
+ }
3823
+ bus;
3824
+ priceLookup;
3825
+ metaLookup;
3826
+ perSubagent = /* @__PURE__ */ new Map();
3827
+ total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
3828
+ /** Live snapshot — safe to call from a tool's execute() body. */
3829
+ snapshot() {
3830
+ return {
3831
+ total: { ...this.total },
3832
+ perSubagent: Object.fromEntries(
3833
+ Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
3834
+ )
3835
+ };
3836
+ }
3837
+ ensure(subagentId) {
3838
+ let snap = this.perSubagent.get(subagentId);
3839
+ if (!snap) {
3840
+ const meta = this.metaLookup?.(subagentId);
3841
+ snap = {
3842
+ subagentId,
3843
+ provider: meta?.provider,
3844
+ model: meta?.model,
3845
+ input: 0,
3846
+ output: 0,
3847
+ cacheRead: 0,
3848
+ cacheWrite: 0,
3849
+ cost: 0,
3850
+ toolCalls: 0,
3851
+ iterations: 0,
3852
+ startedAt: Date.now(),
3853
+ lastEventAt: Date.now()
3854
+ };
3855
+ this.perSubagent.set(subagentId, snap);
3856
+ }
3857
+ return snap;
3858
+ }
3859
+ onProviderResponse(e) {
3860
+ const snap = this.ensure(e.subagentId);
3861
+ const p = e.payload;
3862
+ const usage = p?.usage;
3863
+ if (!usage) return;
3864
+ snap.input += usage.input ?? 0;
3865
+ snap.output += usage.output ?? 0;
3866
+ snap.cacheRead += usage.cacheRead ?? 0;
3867
+ snap.cacheWrite += usage.cacheWrite ?? 0;
3868
+ this.total.input += usage.input ?? 0;
3869
+ this.total.output += usage.output ?? 0;
3870
+ this.total.cacheRead += usage.cacheRead ?? 0;
3871
+ this.total.cacheWrite += usage.cacheWrite ?? 0;
3872
+ const price = this.priceLookup?.(e.subagentId);
3873
+ if (price) {
3874
+ 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);
3875
+ snap.cost += delta;
3876
+ this.total.cost += delta;
3877
+ }
3878
+ snap.lastEventAt = e.ts;
3879
+ }
3880
+ onToolExecuted(e) {
3881
+ const snap = this.ensure(e.subagentId);
3882
+ snap.toolCalls += 1;
3883
+ snap.lastEventAt = e.ts;
3884
+ }
3885
+ onIterationStarted(e) {
3886
+ const snap = this.ensure(e.subagentId);
3887
+ snap.iterations += 1;
3888
+ snap.lastEventAt = e.ts;
3889
+ }
3890
+ };
3891
+
3716
3892
  // src/defaults/transport/in-memory-transport.ts
3717
3893
  var InMemoryBridgeTransport = class {
3718
3894
  subs = /* @__PURE__ */ new Map();
@@ -3840,6 +4016,914 @@ function createMessage(type, from, payload, to) {
3840
4016
  };
3841
4017
  }
3842
4018
 
4019
+ // src/defaults/director-prompts.ts
4020
+ var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
4021
+ subagents by spawning them, assigning tasks, awaiting completions, and
4022
+ rolling up their outputs into your next decision.
4023
+
4024
+ Core fleet tools available to you:
4025
+ - spawn_subagent \u2014 create a worker with a chosen provider / model / role
4026
+ - assign_task \u2014 hand a piece of work to a specific subagent
4027
+ - await_tasks \u2014 block until named task ids complete (parallel-safe)
4028
+ - ask_subagent \u2014 synchronously query a running subagent via the bridge
4029
+ - roll_up \u2014 aggregate finished tasks into a markdown/json summary
4030
+ - terminate_subagent \u2014 abort a stuck worker (use sparingly)
4031
+ - fleet_status \u2014 snapshot of all subagents and pending tasks
4032
+ - fleet_usage \u2014 token + cost breakdown per subagent and total
4033
+
4034
+ Working rules:
4035
+ 1. Decompose first. Before spawning, decide which sub-tasks are
4036
+ independent and can run in parallel. Sequential work doesn't need a
4037
+ subagent \u2014 do it yourself.
4038
+ 2. Match worker to job. Cheap/fast model for triage, capable model for
4039
+ synthesis. Different providers per sibling is allowed and encouraged.
4040
+ 3. Always pair an assign with an await. Don't fire-and-forget; you owe
4041
+ the user a single coherent answer at the end.
4042
+ 4. Roll up before deciding. After await_tasks resolves, call roll_up so
4043
+ the results are folded back into your context in a compact form.
4044
+ 5. Budget is real. Check fleet_usage periodically. If a subagent is
4045
+ thrashing, terminate it rather than letting cost climb silently.
4046
+ 6. Never claim a subagent's work as your own without verifying it. If a
4047
+ result looks wrong, ask_subagent for clarification before passing it
4048
+ to the user.`;
4049
+ var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
4050
+ a specific slice of a larger plan \u2014 do that slice well and report back.
4051
+
4052
+ Bridge contract:
4053
+ - You have a parent (the Director). You may call \`request\` on the
4054
+ parent bridge to ask a clarifying question. Use this sparingly; the
4055
+ parent is also working.
4056
+ - You MAY NOT request the parent's system prompt, tool list, or other
4057
+ subagents' context. Those are not yours to read.
4058
+ - Your final task output is what the Director sees. Be concise,
4059
+ structured, and self-contained \u2014 assume the Director will paste your
4060
+ output into its own context.`;
4061
+ function composeDirectorPrompt(parts = {}) {
4062
+ const sections = [];
4063
+ const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
4064
+ if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
4065
+ if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
4066
+ sections.push(`Available roles you can spawn:
4067
+ ${parts.rosterSummary.trim()}`);
4068
+ }
4069
+ if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
4070
+ sections.push(parts.basePrompt.trim());
4071
+ }
4072
+ return sections.join("\n\n");
4073
+ }
4074
+ function composeSubagentPrompt(parts = {}) {
4075
+ const sections = [];
4076
+ const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
4077
+ if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
4078
+ if (parts.role && parts.role.trim().length > 0) {
4079
+ sections.push(`Role:
4080
+ ${parts.role.trim()}`);
4081
+ }
4082
+ if (parts.task && parts.task.trim().length > 0) {
4083
+ sections.push(`Task:
4084
+ ${parts.task.trim()}`);
4085
+ }
4086
+ if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
4087
+ sections.push(
4088
+ `Shared notes:
4089
+ A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
4090
+ - Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
4091
+ - Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
4092
+ - Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
4093
+ );
4094
+ }
4095
+ if (parts.override && parts.override.trim().length > 0) {
4096
+ sections.push(parts.override.trim());
4097
+ }
4098
+ return sections.join("\n\n");
4099
+ }
4100
+ function rosterSummaryFromConfigs(roster) {
4101
+ const lines = [];
4102
+ for (const [roleId, cfg] of Object.entries(roster)) {
4103
+ const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
4104
+ const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
4105
+ const tail = headline ? ` \u2014 ${headline}` : "";
4106
+ lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
4107
+ }
4108
+ return lines.join("\n");
4109
+ }
4110
+
4111
+ // src/defaults/director.ts
4112
+ var DirectorBudgetError = class extends Error {
4113
+ kind;
4114
+ limit;
4115
+ observed;
4116
+ constructor(kind, limit, observed) {
4117
+ super(
4118
+ kind === "max_spawns" ? `Director spawn budget exceeded: tried to spawn #${observed} but maxSpawns is ${limit}` : `Director spawn depth budget exceeded: this director is at depth ${observed} and maxSpawnDepth is ${limit}`
4119
+ );
4120
+ this.name = "DirectorBudgetError";
4121
+ this.kind = kind;
4122
+ this.limit = limit;
4123
+ this.observed = observed;
4124
+ }
4125
+ };
4126
+ var Director = class {
4127
+ id;
4128
+ fleet;
4129
+ usage;
4130
+ /**
4131
+ * Director-side bridge endpoint. Subagents are wired to the same
4132
+ * in-memory transport so the director can `ask()` them synchronously
4133
+ * and they can `send()` progress back. Exposed so external code (e.g.
4134
+ * the TUI) can subscribe to inbound messages.
4135
+ */
4136
+ bridge;
4137
+ transport;
4138
+ coordinator;
4139
+ /** Resolves with the matching `TaskResult` the first time the
4140
+ * coordinator emits `task.completed` for a given task id. Each entry
4141
+ * is created lazily on first poll/await and cleared once consumed. */
4142
+ taskWaiters = /* @__PURE__ */ new Map();
4143
+ /** Cache of completed results in case the consumer asks AFTER the
4144
+ * coordinator already fired the event — `awaitTasks(['t-1'])` after
4145
+ * t-1 finished should resolve immediately, not hang. */
4146
+ completed = /* @__PURE__ */ new Map();
4147
+ /** Per-subagent provider/model metadata, captured at spawn time so the
4148
+ * FleetUsageAggregator's metaLookup can surface readable rows. */
4149
+ subagentMeta = /* @__PURE__ */ new Map();
4150
+ priceLookups = /* @__PURE__ */ new Map();
4151
+ /** Bridge endpoints we created per subagent (so we can `stop()` them
4152
+ * on shutdown and free transport subscriptions). */
4153
+ subagentBridges = /* @__PURE__ */ new Map();
4154
+ /** Tracks per-spawn config + assigned task ids for manifest writing. */
4155
+ manifestEntries = /* @__PURE__ */ new Map();
4156
+ manifestPath;
4157
+ roster;
4158
+ directorPreamble;
4159
+ subagentBaseline;
4160
+ /** Absolute path to the fleet's shared scratchpad directory, or null
4161
+ * when none was configured. Exposed as a readonly getter for callers
4162
+ * that need to surface the path to the user (e.g. the CLI logging
4163
+ * the location after `--director` boots). */
4164
+ sharedScratchpadPath;
4165
+ /** Spawn cap (lifetime total). Infinity means unlimited. */
4166
+ maxSpawns;
4167
+ /** Nesting cap. The N-th director in a chain has `spawnDepth = N-1`. */
4168
+ maxSpawnDepth;
4169
+ /** This director's position in a director chain. Root director = 0. */
4170
+ spawnDepth;
4171
+ /** Live spawn counter for `maxSpawns` enforcement. */
4172
+ spawnCount = 0;
4173
+ constructor(opts) {
4174
+ this.id = opts.config.coordinatorId || randomUUID();
4175
+ this.manifestPath = opts.manifestPath;
4176
+ this.roster = opts.roster;
4177
+ this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
4178
+ this.subagentBaseline = opts.subagentBaseline ?? DEFAULT_SUBAGENT_BASELINE;
4179
+ this.sharedScratchpadPath = opts.sharedScratchpadPath ?? null;
4180
+ this.maxSpawns = opts.maxSpawns ?? Infinity;
4181
+ this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
4182
+ this.spawnDepth = opts.spawnDepth ?? 0;
4183
+ if (this.sharedScratchpadPath) {
4184
+ void fsp.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(() => void 0);
4185
+ }
4186
+ this.transport = new InMemoryBridgeTransport();
4187
+ this.bridge = new InMemoryAgentBridge(
4188
+ { agentId: this.id, coordinatorId: this.id },
4189
+ this.transport
4190
+ );
4191
+ this.fleet = new FleetBus();
4192
+ this.usage = new FleetUsageAggregator(
4193
+ this.fleet,
4194
+ (id) => this.priceLookups.get(id),
4195
+ (id) => this.subagentMeta.get(id)
4196
+ );
4197
+ this.coordinator = new DefaultMultiAgentCoordinator(
4198
+ { ...opts.config, coordinatorId: this.id },
4199
+ { runner: opts.runner }
4200
+ );
4201
+ this.coordinator.on("task.completed", (payload) => {
4202
+ const r = payload.result;
4203
+ this.completed.set(r.taskId, r);
4204
+ const waiter = this.taskWaiters.get(r.taskId);
4205
+ if (waiter) {
4206
+ waiter.resolve(r);
4207
+ this.taskWaiters.delete(r.taskId);
4208
+ }
4209
+ });
4210
+ }
4211
+ /**
4212
+ * Spawn a subagent. Identical to the coordinator's `spawn()` but
4213
+ * captures provider/model metadata for the usage aggregator and
4214
+ * lets the FleetBus attach to the runner's EventBus when the task
4215
+ * actually runs (see `attachSubagentBus`).
4216
+ *
4217
+ * Caller-supplied `priceLookup` is optional but recommended — without
4218
+ * it the `cost` column in `usage.snapshot()` stays at 0.
4219
+ */
4220
+ async spawn(config, priceLookup) {
4221
+ if (this.spawnDepth >= this.maxSpawnDepth) {
4222
+ throw new DirectorBudgetError("max_spawn_depth", this.maxSpawnDepth, this.spawnDepth);
4223
+ }
4224
+ if (this.spawnCount >= this.maxSpawns) {
4225
+ throw new DirectorBudgetError("max_spawns", this.maxSpawns, this.spawnCount + 1);
4226
+ }
4227
+ this.spawnCount += 1;
4228
+ const result = await this.coordinator.spawn(config);
4229
+ this.subagentMeta.set(result.subagentId, {
4230
+ provider: config.provider,
4231
+ model: config.model
4232
+ });
4233
+ if (priceLookup) this.priceLookups.set(result.subagentId, priceLookup);
4234
+ const subagentBridge = new InMemoryAgentBridge(
4235
+ { agentId: result.subagentId, coordinatorId: this.id },
4236
+ this.transport
4237
+ );
4238
+ this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
4239
+ this.subagentBridges.set(result.subagentId, subagentBridge);
4240
+ this.manifestEntries.set(result.subagentId, {
4241
+ subagentId: result.subagentId,
4242
+ name: config.name,
4243
+ role: config.role,
4244
+ provider: config.provider,
4245
+ model: config.model,
4246
+ taskIds: []
4247
+ });
4248
+ return result.subagentId;
4249
+ }
4250
+ /**
4251
+ * Synchronously ask a subagent something via the bridge. Sends a
4252
+ * `task` message addressed to the subagent and awaits a matching
4253
+ * reply (matched by message id). Subagent runners that handle these
4254
+ * requests subscribe to `ctx.bridge` and reply with a message whose
4255
+ * `id` equals the incoming request's id (see `InMemoryAgentBridge`'s
4256
+ * `request<T>` implementation).
4257
+ *
4258
+ * Returns the response payload directly (the bridge wrapper is
4259
+ * unwrapped for ergonomics). Times out after `timeoutMs` (default
4260
+ * matches the bridge's own default of 30s) — surface those rejections
4261
+ * to the caller as actionable errors instead of letting tools hang.
4262
+ */
4263
+ async ask(subagentId, payload, timeoutMs) {
4264
+ if (!this.subagentBridges.has(subagentId)) {
4265
+ throw new Error(
4266
+ `ask: unknown subagent "${subagentId}" (spawn() it first; current fleet: ${Array.from(this.subagentBridges.keys()).join(", ") || "(empty)"})`
4267
+ );
4268
+ }
4269
+ const msg = {
4270
+ id: randomUUID(),
4271
+ type: "task",
4272
+ from: this.id,
4273
+ to: subagentId,
4274
+ payload,
4275
+ timestamp: Date.now(),
4276
+ priority: "normal"
4277
+ };
4278
+ const reply = await this.bridge.request(msg, timeoutMs);
4279
+ return reply.payload;
4280
+ }
4281
+ /**
4282
+ * Read completed task results and format them as a structured text
4283
+ * block the director's LLM can paste into its own context. The
4284
+ * Director keeps every completed `TaskResult` in `completed` so this
4285
+ * is a pure read — no bridge round-trip, cheap to call.
4286
+ *
4287
+ * The returned string is intentionally markdown-flavored: headers per
4288
+ * subagent, a one-line meta row (iter / tools / ms), and the task's
4289
+ * result text. Pass `style: 'json'` for a programmatic shape instead
4290
+ * (useful when the director model is doing structured-output work).
4291
+ */
4292
+ rollUp(taskIds, style = "markdown") {
4293
+ const rows = taskIds.map((id) => this.completed.get(id)).filter(
4294
+ (r) => !!r
4295
+ );
4296
+ if (style === "json") {
4297
+ return JSON.stringify(
4298
+ rows.map((r) => ({
4299
+ taskId: r.taskId,
4300
+ subagentId: r.subagentId,
4301
+ status: r.status,
4302
+ iterations: r.iterations,
4303
+ toolCalls: r.toolCalls,
4304
+ durationMs: r.durationMs,
4305
+ result: r.result,
4306
+ error: r.error
4307
+ })),
4308
+ null,
4309
+ 2
4310
+ );
4311
+ }
4312
+ if (rows.length === 0) {
4313
+ return "_No completed tasks for the requested ids \u2014 try waiting first._";
4314
+ }
4315
+ const lines = [];
4316
+ for (const r of rows) {
4317
+ const meta = this.subagentMeta.get(r.subagentId);
4318
+ const tag = meta?.provider && meta?.model ? ` \xB7 ${meta.provider}/${meta.model}` : "";
4319
+ lines.push(`### ${r.subagentId}${tag}`);
4320
+ lines.push(
4321
+ `_${r.status} \u2014 ${r.iterations} iter \xB7 ${r.toolCalls} tools \xB7 ${r.durationMs}ms_`
4322
+ );
4323
+ lines.push("");
4324
+ if (r.error) lines.push(`**Error:** ${r.error}`);
4325
+ else if (typeof r.result === "string") lines.push(r.result);
4326
+ else if (r.result !== void 0) lines.push("```json\n" + JSON.stringify(r.result, null, 2) + "\n```");
4327
+ else lines.push("_(no output)_");
4328
+ lines.push("");
4329
+ }
4330
+ return lines.join("\n").trimEnd();
4331
+ }
4332
+ /**
4333
+ * Write the fleet manifest to `manifestPath`. Returns the path written
4334
+ * or null when no path was configured. Captures every spawn + its
4335
+ * assigned tasks — paired with per-subagent JSONLs, this is enough to
4336
+ * replay an entire director run.
4337
+ */
4338
+ async writeManifest() {
4339
+ if (!this.manifestPath) return null;
4340
+ const manifest = {
4341
+ directorRunId: this.id,
4342
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString(),
4343
+ children: Array.from(this.manifestEntries.values()).map((e) => ({
4344
+ ...e,
4345
+ // Surface final status from `completed` when available — manifest
4346
+ // becomes much more useful for replay when it carries the
4347
+ // success/failure state.
4348
+ results: e.taskIds.map((tid) => {
4349
+ const r = this.completed.get(tid);
4350
+ return r ? {
4351
+ taskId: tid,
4352
+ status: r.status,
4353
+ iterations: r.iterations,
4354
+ toolCalls: r.toolCalls,
4355
+ durationMs: r.durationMs
4356
+ } : { taskId: tid, status: "pending" };
4357
+ })
4358
+ })),
4359
+ usage: this.usage.snapshot()
4360
+ };
4361
+ await fsp.mkdir(path2.dirname(this.manifestPath), { recursive: true });
4362
+ await fsp.writeFile(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
4363
+ return this.manifestPath;
4364
+ }
4365
+ /**
4366
+ * Tear down the director: stop every subagent, close every bridge
4367
+ * endpoint, and (when configured) write the final manifest. Idempotent
4368
+ * — calling shutdown twice is a no-op on the second invocation.
4369
+ */
4370
+ async shutdown() {
4371
+ await this.coordinator.stopAll();
4372
+ for (const b of this.subagentBridges.values()) {
4373
+ await b.stop().catch(() => void 0);
4374
+ }
4375
+ this.subagentBridges.clear();
4376
+ await this.bridge.stop().catch(() => void 0);
4377
+ if (this.manifestPath) await this.writeManifest().catch(() => void 0);
4378
+ }
4379
+ /**
4380
+ * Hand a task to the coordinator. Returns the assigned task id so
4381
+ * callers can wait on it via `awaitTasks([id])`. The coordinator's
4382
+ * concurrency limit applies — the task may queue before running.
4383
+ */
4384
+ async assign(task) {
4385
+ const taskWithId = task.id ? task : { ...task, id: randomUUID() };
4386
+ if (task.subagentId) {
4387
+ const entry = this.manifestEntries.get(task.subagentId);
4388
+ if (entry) entry.taskIds.push(taskWithId.id);
4389
+ }
4390
+ await this.coordinator.assign(taskWithId);
4391
+ return taskWithId.id;
4392
+ }
4393
+ /**
4394
+ * Block until every task id resolves. Returns results in the same
4395
+ * order as the input. If any task hasn't completed by the time this
4396
+ * is called, the promise hangs until it does — pair with a timeout
4397
+ * at the caller if that's a concern. Resolves immediately for ids
4398
+ * whose results were already cached.
4399
+ */
4400
+ awaitTasks(taskIds) {
4401
+ return Promise.all(taskIds.map((id) => {
4402
+ const cached = this.completed.get(id);
4403
+ if (cached) return cached;
4404
+ const existing = this.taskWaiters.get(id);
4405
+ if (existing) return existing.promise;
4406
+ let resolve3;
4407
+ const promise = new Promise((res) => {
4408
+ resolve3 = res;
4409
+ });
4410
+ this.taskWaiters.set(id, { promise, resolve: resolve3 });
4411
+ return promise;
4412
+ }));
4413
+ }
4414
+ async terminate(subagentId) {
4415
+ await this.coordinator.stop(subagentId);
4416
+ }
4417
+ async terminateAll() {
4418
+ await this.coordinator.stopAll();
4419
+ }
4420
+ status() {
4421
+ return this.coordinator.getStatus();
4422
+ }
4423
+ /**
4424
+ * Subscribe to coordinator events. Currently only `task.completed` is
4425
+ * exposed (the others are internal lifecycle). Returns an unsubscribe
4426
+ * function. External callers (e.g. the CLI's `MultiAgentHost`) use this
4427
+ * to drive their own pending/results tracking without poking the
4428
+ * coordinator directly.
4429
+ */
4430
+ on(event, handler) {
4431
+ this.coordinator.on(event, handler);
4432
+ return () => {
4433
+ this.coordinator.off(event, handler);
4434
+ };
4435
+ }
4436
+ /**
4437
+ * Snapshot of every task that has resolved (success, failed, timeout,
4438
+ * stopped) since the director started. Returned in completion order
4439
+ * via the internal map's iteration order. Used by `/fleet status` to
4440
+ * paint the completed table without reaching into private state.
4441
+ */
4442
+ completedResults() {
4443
+ return Array.from(this.completed.values());
4444
+ }
4445
+ snapshot() {
4446
+ return this.usage.snapshot();
4447
+ }
4448
+ /**
4449
+ * Compose the leader/director-agent system prompt: fleet preamble +
4450
+ * (optional) roster summary + user base prompt. Pass the result to your
4451
+ * leader Agent's `ctx.systemPrompt` when constructing it.
4452
+ *
4453
+ * `basePrompt` defaults to `config.leaderSystemPrompt` so callers can
4454
+ * use the no-arg form when the multi-agent config already carries it.
4455
+ */
4456
+ leaderSystemPrompt(basePrompt) {
4457
+ return composeDirectorPrompt({
4458
+ basePrompt: basePrompt ?? this.coordinator.config.leaderSystemPrompt,
4459
+ directorPreamble: this.directorPreamble,
4460
+ rosterSummary: this.roster ? rosterSummaryFromConfigs(this.roster) : void 0
4461
+ });
4462
+ }
4463
+ /**
4464
+ * Compose a subagent's system prompt for a given `SubagentConfig`:
4465
+ * baseline + role + task + per-spawn override. Returned by value — does
4466
+ * not mutate the config. Factories (the user-supplied `AgentFactory`)
4467
+ * should call this when building each subagent's Agent so the bridge
4468
+ * contract, role context, and override are all surfaced.
4469
+ *
4470
+ * When `taskBrief` is omitted the Task section is dropped. Pass the
4471
+ * actual task description here to reinforce it in the system prompt
4472
+ * (the runner already passes it as user input — duplicating in the
4473
+ * system prompt is optional but improves anchoring on small models).
4474
+ */
4475
+ subagentSystemPrompt(config, taskBrief) {
4476
+ return composeSubagentPrompt({
4477
+ baseline: this.subagentBaseline,
4478
+ role: config.prompt,
4479
+ task: taskBrief,
4480
+ sharedScratchpad: this.sharedScratchpadPath ?? void 0,
4481
+ override: config.systemPromptOverride
4482
+ });
4483
+ }
4484
+ /**
4485
+ * Build the tool set the LLM-driven director uses to orchestrate.
4486
+ * Returns an array of `Tool` definitions; register these on the
4487
+ * director's `Agent` to expose `spawn_subagent`, `assign_task`, etc.
4488
+ * Each tool's `execute()` delegates straight to the matching method
4489
+ * above.
4490
+ *
4491
+ * Tools all carry `permission: 'auto'` — the *user* has already
4492
+ * approved running the director when they kicked off the run, so
4493
+ * gating individual orchestration calls behind a confirm prompt
4494
+ * would just be noise. The actual subagent tools they spawn are
4495
+ * still permission-checked normally.
4496
+ */
4497
+ tools(roster) {
4498
+ const t = [
4499
+ makeSpawnTool(this, roster),
4500
+ makeAssignTool(this),
4501
+ makeAwaitTasksTool(this),
4502
+ makeAskTool(this),
4503
+ makeRollUpTool(this),
4504
+ makeTerminateTool(this),
4505
+ makeFleetStatusTool(this),
4506
+ makeFleetUsageTool(this)
4507
+ ];
4508
+ return t;
4509
+ }
4510
+ };
4511
+ function makeSpawnTool(director, roster) {
4512
+ const inputSchema = {
4513
+ type: "object",
4514
+ properties: {
4515
+ role: { type: "string", description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields." },
4516
+ name: { type: "string", description: "Display name for the subagent. Required when not using roster." },
4517
+ provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
4518
+ model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
4519
+ systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
4520
+ maxIterations: { type: "number" },
4521
+ maxToolCalls: { type: "number" },
4522
+ maxCostUsd: { type: "number" }
4523
+ },
4524
+ required: []
4525
+ };
4526
+ return {
4527
+ name: "spawn_subagent",
4528
+ description: "Create a new subagent under this director. Returns the subagent id. Use this when you need a worker with a specific provider, model, or role to handle a piece of the plan.",
4529
+ usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
4530
+ permission: "auto",
4531
+ mutating: false,
4532
+ inputSchema,
4533
+ async execute(input) {
4534
+ const i = input ?? {};
4535
+ const role = typeof i.role === "string" ? i.role : void 0;
4536
+ const base = role && roster ? roster[role] : void 0;
4537
+ if (role && !base) {
4538
+ return { error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}` };
4539
+ }
4540
+ const cfg = {
4541
+ ...base ?? { name: i.name ?? "subagent" }
4542
+ };
4543
+ if (typeof i.name === "string") cfg.name = i.name;
4544
+ if (typeof i.provider === "string") cfg.provider = i.provider;
4545
+ if (typeof i.model === "string") cfg.model = i.model;
4546
+ if (typeof i.systemPromptOverride === "string") cfg.systemPromptOverride = i.systemPromptOverride;
4547
+ if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
4548
+ if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
4549
+ if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
4550
+ try {
4551
+ const subagentId = await director.spawn(cfg);
4552
+ return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
4553
+ } catch (err) {
4554
+ if (err instanceof DirectorBudgetError) {
4555
+ return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
4556
+ }
4557
+ return { error: err instanceof Error ? err.message : String(err) };
4558
+ }
4559
+ }
4560
+ };
4561
+ }
4562
+ function makeAssignTool(director) {
4563
+ const inputSchema = {
4564
+ type: "object",
4565
+ properties: {
4566
+ subagentId: { type: "string", description: "Target subagent id. Required." },
4567
+ description: { type: "string", description: "The task in natural language \u2014 what you want this subagent to do." },
4568
+ maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
4569
+ timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
4570
+ },
4571
+ required: ["subagentId", "description"]
4572
+ };
4573
+ return {
4574
+ name: "assign_task",
4575
+ description: "Hand a task to a previously spawned subagent. Returns the task id \u2014 pass it to `await_tasks` to block on completion.",
4576
+ permission: "auto",
4577
+ mutating: false,
4578
+ inputSchema,
4579
+ async execute(input) {
4580
+ const i = input;
4581
+ const task = {
4582
+ id: randomUUID(),
4583
+ description: i.description,
4584
+ subagentId: i.subagentId,
4585
+ maxToolCalls: i.maxToolCalls,
4586
+ timeoutMs: i.timeoutMs
4587
+ };
4588
+ const taskId = await director.assign(task);
4589
+ return { taskId, subagentId: i.subagentId };
4590
+ }
4591
+ };
4592
+ }
4593
+ function makeAwaitTasksTool(director) {
4594
+ const inputSchema = {
4595
+ type: "object",
4596
+ properties: {
4597
+ taskIds: {
4598
+ type: "array",
4599
+ items: { type: "string" },
4600
+ description: "One or more task ids returned by `assign_task`. The call blocks until every id resolves."
4601
+ }
4602
+ },
4603
+ required: ["taskIds"]
4604
+ };
4605
+ return {
4606
+ name: "await_tasks",
4607
+ description: "Block until every named task completes. Returns the array of TaskResult \u2014 use this to gather subagent output before deciding the next step.",
4608
+ permission: "auto",
4609
+ mutating: false,
4610
+ inputSchema,
4611
+ async execute(input) {
4612
+ const i = input;
4613
+ const results = await director.awaitTasks(i.taskIds);
4614
+ return { results };
4615
+ }
4616
+ };
4617
+ }
4618
+ function makeAskTool(director) {
4619
+ const inputSchema = {
4620
+ type: "object",
4621
+ properties: {
4622
+ subagentId: { type: "string", description: "Subagent to ask. Must be a previously spawned id." },
4623
+ question: { type: "string", description: "The question or instruction. Sent as the bridge message payload." },
4624
+ timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
4625
+ },
4626
+ required: ["subagentId", "question"]
4627
+ };
4628
+ return {
4629
+ name: "ask_subagent",
4630
+ description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge (or the timeout fires). Use this when you need a one-shot answer without spawning a fresh task.",
4631
+ permission: "auto",
4632
+ mutating: false,
4633
+ inputSchema,
4634
+ async execute(input) {
4635
+ const i = input;
4636
+ try {
4637
+ const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
4638
+ return { ok: true, answer };
4639
+ } catch (err) {
4640
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
4641
+ }
4642
+ }
4643
+ };
4644
+ }
4645
+ function makeRollUpTool(director) {
4646
+ const inputSchema = {
4647
+ type: "object",
4648
+ properties: {
4649
+ taskIds: {
4650
+ type: "array",
4651
+ items: { type: "string" },
4652
+ description: "Completed task ids to aggregate. Pass the ids returned by previous `assign_task` calls."
4653
+ },
4654
+ style: {
4655
+ type: "string",
4656
+ enum: ["markdown", "json"],
4657
+ description: "Output flavor \u2014 markdown (default) for in-prompt summarization, json for structured downstream processing."
4658
+ }
4659
+ },
4660
+ required: ["taskIds"]
4661
+ };
4662
+ return {
4663
+ name: "roll_up",
4664
+ description: "Aggregate completed task results into a single formatted summary. Use this after `await_tasks` to fold subagent outputs back into the director's context before deciding the next step.",
4665
+ permission: "auto",
4666
+ mutating: false,
4667
+ inputSchema,
4668
+ async execute(input) {
4669
+ const i = input;
4670
+ const summary = director.rollUp(i.taskIds, i.style ?? "markdown");
4671
+ return { summary, count: i.taskIds.length };
4672
+ }
4673
+ };
4674
+ }
4675
+ function makeTerminateTool(director) {
4676
+ const inputSchema = {
4677
+ type: "object",
4678
+ properties: {
4679
+ subagentId: { type: "string", description: "Subagent to abort." }
4680
+ },
4681
+ required: ["subagentId"]
4682
+ };
4683
+ return {
4684
+ name: "terminate_subagent",
4685
+ description: 'Forcibly abort a subagent. Use sparingly \u2014 prefer waiting on the natural budget to expire. The current task (if any) ends with status "stopped".',
4686
+ permission: "auto",
4687
+ mutating: true,
4688
+ inputSchema,
4689
+ async execute(input) {
4690
+ const i = input;
4691
+ await director.terminate(i.subagentId);
4692
+ return { ok: true };
4693
+ }
4694
+ };
4695
+ }
4696
+ function makeFleetStatusTool(director) {
4697
+ return {
4698
+ name: "fleet_status",
4699
+ description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts, and the running total iteration count. Cheap; call freely.",
4700
+ permission: "auto",
4701
+ mutating: false,
4702
+ inputSchema: { type: "object", properties: {}, required: [] },
4703
+ async execute() {
4704
+ return director.status();
4705
+ }
4706
+ };
4707
+ }
4708
+ function makeFleetUsageTool(director) {
4709
+ return {
4710
+ name: "fleet_usage",
4711
+ description: "Token + cost breakdown across the fleet, per-subagent and totals. Use this to reason about which workers to assign costly tasks to or when to wrap up to stay within budget.",
4712
+ permission: "auto",
4713
+ mutating: false,
4714
+ inputSchema: { type: "object", properties: {}, required: [] },
4715
+ async execute() {
4716
+ return director.snapshot();
4717
+ }
4718
+ };
4719
+ }
4720
+ function makeDirectorSessionFactory(opts) {
4721
+ const runId = opts.directorRunId ?? `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-director`;
4722
+ let store;
4723
+ let dir;
4724
+ if (opts.store) {
4725
+ store = opts.store;
4726
+ dir = opts.sessionsRoot ? path2.join(opts.sessionsRoot, runId) : "(caller-managed)";
4727
+ } else if (opts.sessionsRoot) {
4728
+ dir = path2.join(opts.sessionsRoot, runId);
4729
+ store = new DefaultSessionStore({ dir });
4730
+ } else {
4731
+ throw new Error(
4732
+ "makeDirectorSessionFactory requires either `store` or `sessionsRoot`"
4733
+ );
4734
+ }
4735
+ return {
4736
+ dir,
4737
+ directorRunId: runId,
4738
+ async createSubagentSession({ subagentId, provider, model, title }) {
4739
+ return store.create({
4740
+ id: subagentId,
4741
+ title: title ?? subagentId,
4742
+ provider: provider ?? "unknown",
4743
+ model: model ?? "unknown"
4744
+ });
4745
+ }
4746
+ };
4747
+ }
4748
+
4749
+ // src/defaults/agents/fleet.ts
4750
+ var AUDIT_LOG_AGENT = {
4751
+ id: "audit-log",
4752
+ name: "Audit Log",
4753
+ role: "audit-log",
4754
+ prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
4755
+ session logs and produce actionable markdown reports.
4756
+
4757
+ Scope:
4758
+ - Parse session logs (iteration counts, tool calls, errors, usage)
4759
+ - Detect repeated failure patterns across multiple runs
4760
+ - Identify tool usage anomalies (over-use, failures, unexpected chains)
4761
+ - Track token consumption trends
4762
+ - Generate structured audit reports with severity ratings
4763
+
4764
+ Input format you accept:
4765
+ { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
4766
+
4767
+ Output: Markdown audit report with sections:
4768
+ - ## Summary (totals, error rate)
4769
+ - ## Top Errors (count + context)
4770
+ - ## Tool Usage (table with calls, failures, avg duration)
4771
+ - ## Anomalies (pattern \u2192 severity)
4772
+
4773
+ Working rules:
4774
+ - Never fabricate numbers \u2014 read the actual logs first
4775
+ - Always include file:line references for errors
4776
+ - If sessionPath is missing, ask the director to provide it
4777
+ - Report confidence level: high (>90% accuracy), medium, low`,
4778
+ maxIterations: 50,
4779
+ maxToolCalls: 200,
4780
+ timeoutMs: 12e4
4781
+ };
4782
+ var BUG_HUNTER_AGENT = {
4783
+ id: "bug-hunter",
4784
+ name: "Bug Hunter",
4785
+ role: "bug-hunter",
4786
+ prompt: `You are the Bug Hunter agent. Your job is to systematically scan
4787
+ source code for bugs, anti-patterns, and code smells using pattern matching
4788
+ and heuristics. Output a prioritized hit list with file:line references.
4789
+
4790
+ Scope:
4791
+ - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
4792
+ - Identify anti-patterns (callback hell, God objects, circular deps)
4793
+ - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
4794
+ - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
4795
+ - Rank findings: critical > high > medium > low
4796
+
4797
+ Input format you accept:
4798
+ { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
4799
+
4800
+ Output: Markdown bug hunt report:
4801
+ - ## Critical (must fix first)
4802
+ - ## High (should fix)
4803
+ - ## Medium
4804
+ - ## Low (consider)
4805
+ Each entry: **[TYPE]** \`file:line\` \u2014 description + suggested fix
4806
+
4807
+ Bug pattern reference you know:
4808
+ | Pattern | Regex hint | Severity |
4809
+ |---------|------------|----------|
4810
+ | Uncaught promise | /.then\\(.*\\)/ without catch | high |
4811
+ | Event leak | on\\( without off/removeListener | high |
4812
+ | Hardcoded secret | [a-zA-Z0-9/_-]{20,} in config files | critical |
4813
+ | unsafe any | : any\\b or <any> | medium |
4814
+ | innerHTML | innerHTML\\s*= | high |
4815
+
4816
+ Working rules:
4817
+ - Never scan node_modules \u2014 it's noise
4818
+ - Always include file:line for every finding
4819
+ - If >30% of findings are false positives, note the confidence level
4820
+ - Ask director for clarification if paths are ambiguous`,
4821
+ maxIterations: 80,
4822
+ maxToolCalls: 300,
4823
+ timeoutMs: 18e4
4824
+ };
4825
+ var REFACTOR_PLANNER_AGENT = {
4826
+ id: "refactor-planner",
4827
+ name: "Refactor Planner",
4828
+ role: "refactor-planner",
4829
+ prompt: `You are the Refactor Planner agent. Your job is to analyze code
4830
+ structure and produce a concrete, phased refactoring plan with risk
4831
+ assessment, dependency ordering, and rollback strategy.
4832
+
4833
+ Scope:
4834
+ - Map module-level dependencies (import graph)
4835
+ - Identify coupling hotspots (high fan-in/out modules)
4836
+ - Assess refactoring risk by complexity and test coverage
4837
+ - Generate phased plans with checkpoint milestones
4838
+ - Produce diff-friendly task lists (one task = one concern)
4839
+
4840
+ Input format you accept:
4841
+ { "task": "plan | assess | roadmap", "target": "src/core", "constraint": "no-breaking-changes | minimal-downtime | full-rewrite", "focus": "architecture | performance | maintainability" }
4842
+
4843
+ Output: Markdown refactor plan:
4844
+ - ## Phase 1: Low Risk / High Payoff (do first)
4845
+ Table: | # | Task | Module | Risk | Est. Time |
4846
+ - ## Phase 2: Medium Risk
4847
+ - ## Phase 3: High Risk (requires full regression)
4848
+ - ## Dependency Graph (abbreviated ASCII)
4849
+ - ## Rollback Strategy
4850
+ - ## Exit Criteria (checkbox list)
4851
+
4852
+ Risk scoring criteria:
4853
+ | Factor | Low | Medium | High |
4854
+ |--------|-----|--------|------|
4855
+ | Cyclomatic complexity | <10 | 10-20 | >20 |
4856
+ | Test coverage | >80% | 50-80% | <50% |
4857
+ | Fan-out (imports) | <5 | 5-15 | >15 |
4858
+
4859
+ Working rules:
4860
+ - Always include rollback strategy \u2014 every refactor can fail
4861
+ - Merge tasks that take <1h into a single phase
4862
+ - Respect team constraints (reviewer availability, parallelization)
4863
+ - Never plan without analyzing the actual code first`,
4864
+ maxIterations: 60,
4865
+ maxToolCalls: 250,
4866
+ timeoutMs: 15e4
4867
+ };
4868
+ var SECURITY_SCANNER_AGENT = {
4869
+ id: "security-scanner",
4870
+ name: "Security Scanner",
4871
+ role: "security-scanner",
4872
+ prompt: `You are the Security Scanner agent. Your job is to scan code,
4873
+ configs, and dependencies for security issues from hardcoded secrets to
4874
+ supply chain risks.
4875
+
4876
+ Scope:
4877
+ - Detect hardcoded secrets: API keys, tokens, passwords, private keys
4878
+ - Find injection vectors: eval, innerHTML, SQL concat, shell injection
4879
+ - Identify insecure patterns: weak crypto, hardcoded IVs, disabled TLS
4880
+ - Scan dependencies for known CVEs (via npm/pnpm audit)
4881
+ - Flag supply chain risks: postinstall hooks, unverified scripts, .npmrc
4882
+
4883
+ Input format you accept:
4884
+ { "task": "scan | audit | secrets | dependencies", "paths": ["src", "config"], "depth": "quick | normal | deep" }
4885
+
4886
+ Output: Markdown security report:
4887
+ - ## CRITICAL: Secrets Found (with code snippets)
4888
+ - ## HIGH: Injection Vectors
4889
+ - ## MEDIUM: Insecure Patterns
4890
+ - ## Dependency Issues (CVE list)
4891
+ - ## Summary table (severity \u2192 count)
4892
+ - ## Remediation Checklist (with checkboxes)
4893
+
4894
+ Secret patterns you detect:
4895
+ | Pattern | Example | Severity |
4896
+ |---------|---------|----------|
4897
+ | AWS Access Key | AKIAIOSFODNN7EXAMPLE | critical |
4898
+ | AWS Secret Key | [a-zA-Z0-9/+=]{40} base64 | critical |
4899
+ | GitHub Token | ghp_[a-zA-Z0-9]{36} | critical |
4900
+ | Private Key PEM | -----BEGIN.*PRIVATE KEY----- | critical |
4901
+ | JWT | eyJ[a-zA-Z0-9_-]+ | high |
4902
+
4903
+ Injection patterns:
4904
+ | Construct | Safe alternative |
4905
+ |-----------|-----------------|
4906
+ | eval(str) | new Function() or parse |
4907
+ | innerHTML = x | textContent or sanitize |
4908
+ | exec(\`cmd \${x}\`) | execFile with args array |
4909
+
4910
+ Working rules:
4911
+ - Never scan node_modules \u2014 use npm audit instead
4912
+ - Always provide remediation steps, not just findings
4913
+ - Verify regex-based secrets before flagging (false positive risk)
4914
+ - When in doubt, flag as medium rather than ignoring potential issues`,
4915
+ maxIterations: 70,
4916
+ maxToolCalls: 280,
4917
+ timeoutMs: 16e4
4918
+ };
4919
+ var FLEET_ROSTER = {
4920
+ "audit-log": AUDIT_LOG_AGENT,
4921
+ "bug-hunter": BUG_HUNTER_AGENT,
4922
+ "refactor-planner": REFACTOR_PLANNER_AGENT,
4923
+ "security-scanner": SECURITY_SCANNER_AGENT
4924
+ };
4925
+ var ALL_FLEET_AGENTS = Object.values(FLEET_ROSTER);
4926
+
3843
4927
  // src/defaults/autonomous-runner.ts
3844
4928
  var DoneConditionChecker = class {
3845
4929
  constructor(condition) {
@@ -3881,6 +4965,16 @@ var AutonomousRunner = class {
3881
4965
  stopped = false;
3882
4966
  doneChecker;
3883
4967
  async run() {
4968
+ const offToolExecuted = this.opts.agent.events?.on?.("tool.executed", () => {
4969
+ this.toolCalls++;
4970
+ });
4971
+ try {
4972
+ return await this.runLoop();
4973
+ } finally {
4974
+ offToolExecuted?.();
4975
+ }
4976
+ }
4977
+ async runLoop() {
3884
4978
  while (!this.stopped) {
3885
4979
  const check = this.doneChecker.check({
3886
4980
  iterations: this.iterations,
@@ -3907,7 +5001,6 @@ var AutonomousRunner = class {
3907
5001
  );
3908
5002
  this.iterations++;
3909
5003
  this.lastOutput = result.finalText;
3910
- this.toolCalls++;
3911
5004
  if (result.status === "failed" || result.status === "aborted") {
3912
5005
  const failedResult = {
3913
5006
  status: result.status,
@@ -5801,7 +6894,7 @@ var PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
5801
6894
  async function startMetricsServer(opts) {
5802
6895
  const { createServer } = await import('http');
5803
6896
  const host = opts.host ?? "127.0.0.1";
5804
- const path13 = opts.path ?? "/metrics";
6897
+ const path15 = opts.path ?? "/metrics";
5805
6898
  const healthPath = opts.healthPath ?? "/healthz";
5806
6899
  const healthRegistry = opts.healthRegistry;
5807
6900
  const server = createServer((req, res) => {
@@ -5811,7 +6904,7 @@ async function startMetricsServer(opts) {
5811
6904
  return;
5812
6905
  }
5813
6906
  const url = req.url.split("?")[0];
5814
- if (url === path13) {
6907
+ if (url === path15) {
5815
6908
  let body;
5816
6909
  try {
5817
6910
  body = renderPrometheus(opts.sink.snapshot());
@@ -5862,7 +6955,7 @@ async function startMetricsServer(opts) {
5862
6955
  const boundPort = typeof addr === "object" && addr ? addr.port : opts.port;
5863
6956
  return {
5864
6957
  port: boundPort,
5865
- url: `http://${host}:${boundPort}${path13}`,
6958
+ url: `http://${host}:${boundPort}${path15}`,
5866
6959
  close: () => new Promise((resolve3, reject) => {
5867
6960
  server.close((err) => err ? reject(err) : resolve3());
5868
6961
  })
@@ -6396,6 +7489,6 @@ var allServers = () => ({
6396
7489
  sentinel: { ...sentinelServer(), enabled: false }
6397
7490
  });
6398
7491
 
6399
- export { AutoCompactionMiddleware, AutonomousRunner, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, DefaultTokenCounter, DoneConditionChecker, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, RecoveryLock, SelectiveCompactor, SpecDrivenDev, SpecParser, SubagentBudget, TaskFlow, TaskGenerator, TaskTracker, ToolExecutor, allServers, awsServer, blockServer, braveSearchServer, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, context7Server, contextManagerTool, createContextManagerTool, createMessage, decryptConfigSecrets, encryptConfigSecrets, everArtServer, filesystemServer, githubServer, googleMapsServer, loadProjectModes, loadUserModes, makeAgentSubagentRunner, migratePlaintextSecrets, renderPrometheus, rewriteConfigEncrypted, runConfigMigrations, sentinelServer, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, wireMetricsToEvents };
7492
+ export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, DefaultTokenCounter, Director, DirectorBudgetError, DoneConditionChecker, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SelectiveCompactor, SpecDrivenDev, SpecParser, SubagentBudget, TaskFlow, TaskGenerator, TaskTracker, ToolExecutor, allServers, awsServer, blockServer, braveSearchServer, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createContextManagerTool, createMessage, decryptConfigSecrets, encryptConfigSecrets, everArtServer, filesystemServer, githubServer, googleMapsServer, loadProjectModes, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, migratePlaintextSecrets, renderPrometheus, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, sentinelServer, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, wireMetricsToEvents };
6400
7493
  //# sourceMappingURL=index.js.map
6401
7494
  //# sourceMappingURL=index.js.map