@wrongstack/core 0.270.0 → 0.272.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 (76) hide show
  1. package/dist/{agent-bridge-PcHQl_UQ.d.ts → agent-bridge-jVSZiygR.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-SHJW7t8q.d.ts → agent-subagent-runner-DOLIwBRo.d.ts} +7 -7
  3. package/dist/{brain-BYcK__Ym.d.ts → brain-CdbbJWi3.d.ts} +71 -1
  4. package/dist/{compactor-C2RKEBtC.d.ts → compactor-72ug-ZRB.d.ts} +1 -1
  5. package/dist/{config-C_ae2k86.d.ts → config-D2DGoGSQ.d.ts} +29 -2
  6. package/dist/{context-Dp87Bcaq.d.ts → context-Dw55zZ_Q.d.ts} +110 -1
  7. package/dist/coordination/index.d.ts +121 -17
  8. package/dist/coordination/index.js +738 -74
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +599 -86
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +23 -18
  14. package/dist/execution/index.js +136 -41
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +36 -6
  17. package/dist/execution/prompt-enhancer.js +35 -9
  18. package/dist/execution/prompt-enhancer.js.map +1 -1
  19. package/dist/extension/index.d.ts +6 -6
  20. package/dist/{global-mailbox-Bvrz1P3f.d.ts → global-mailbox-CQj_C9Dp.d.ts} +139 -3
  21. package/dist/{goal-preamble-CA_4yiGQ.d.ts → goal-preamble-ZXDjjR1y.d.ts} +9 -9
  22. package/dist/{goal-store-DhuJoUNG.d.ts → goal-store-CcJBd-g1.d.ts} +1 -1
  23. package/dist/hq/index.d.ts +93 -6
  24. package/dist/hq/index.js +616 -46
  25. package/dist/hq/index.js.map +1 -1
  26. package/dist/{index-whDfTANu.d.ts → index-2Lhk5v0o.d.ts} +2 -2
  27. package/dist/{index-CZQ6Pwbs.d.ts → index-BL7BAx0p.d.ts} +8 -8
  28. package/dist/{index-W4VJCzHa.d.ts → index-Qo4kTzgw.d.ts} +5 -5
  29. package/dist/index.d.ts +96 -56
  30. package/dist/index.js +1938 -349
  31. package/dist/index.js.map +1 -1
  32. package/dist/infrastructure/index.d.ts +6 -6
  33. package/dist/infrastructure/index.js +5 -3
  34. package/dist/infrastructure/index.js.map +1 -1
  35. package/dist/kernel/index.d.ts +9 -9
  36. package/dist/kernel/index.js.map +1 -1
  37. package/dist/{mcp-servers-DJdZiRcv.d.ts → mcp-servers-DS-YUXvF.d.ts} +3 -3
  38. package/dist/models/index.d.ts +5 -5
  39. package/dist/models/index.js +28 -5
  40. package/dist/models/index.js.map +1 -1
  41. package/dist/{models-registry-C3a-2-Yd.d.ts → models-registry-DP6pGHet.d.ts} +1 -1
  42. package/dist/{multi-agent-coordinator-CJSpTe5O.d.ts → multi-agent-coordinator-BvbdNQ14.d.ts} +1 -1
  43. package/dist/{null-fleet-bus-QVshIsDx.d.ts → null-fleet-bus-BxTfXBKo.d.ts} +6 -6
  44. package/dist/observability/index.d.ts +2 -2
  45. package/dist/{parallel-eternal-engine-D9y5Pkcc.d.ts → parallel-eternal-engine-Cf-GTegR.d.ts} +9 -9
  46. package/dist/{path-resolver-CnQ8SIfh.d.ts → path-resolver-DztfnFcv.d.ts} +3 -3
  47. package/dist/{permission-CvYQNUqZ.d.ts → permission-CC7XFYWG.d.ts} +1 -1
  48. package/dist/{permission-policy-D5Ss8j4B.d.ts → permission-policy-cYR4RJmw.d.ts} +2 -2
  49. package/dist/{pipeline-l_zzFRh3.d.ts → pipeline-sNIkhXeB.d.ts} +2 -2
  50. package/dist/{plan-templates-NtPgyeJA.d.ts → plan-templates-DYiKFmEb.d.ts} +11 -5
  51. package/dist/{provider-model-resolve-d5poT5y0.d.ts → provider-model-resolve-dYAbTs_i.d.ts} +3 -3
  52. package/dist/{provider-runner-gkctlQV_.d.ts → provider-runner-Dw8x0F7u.d.ts} +3 -3
  53. package/dist/{retry-policy-CtFhfwa8.d.ts → retry-policy-BV7nzeAd.d.ts} +1 -1
  54. package/dist/sdd/index.d.ts +8 -8
  55. package/dist/sdd/index.js +2 -0
  56. package/dist/sdd/index.js.map +1 -1
  57. package/dist/{secret-vault-BLsVmTIK.d.ts → secret-vault-eMBKfheR.d.ts} +9 -1
  58. package/dist/security/index.d.ts +5 -5
  59. package/dist/security/index.js +137 -10
  60. package/dist/security/index.js.map +1 -1
  61. package/dist/{selector-CXl2_y9W.d.ts → selector-C4ORTOid.d.ts} +1 -1
  62. package/dist/{session-event-bridge-Ccud20CC.d.ts → session-event-bridge-CeNpUL9w.d.ts} +1 -1
  63. package/dist/{session-reader-ZeXQmsmE.d.ts → session-reader-BepLSnGL.d.ts} +1 -1
  64. package/dist/storage/index.d.ts +45 -13
  65. package/dist/storage/index.js +374 -113
  66. package/dist/storage/index.js.map +1 -1
  67. package/dist/tools/index.d.ts +2 -2
  68. package/dist/tools/index.js +9 -2
  69. package/dist/tools/index.js.map +1 -1
  70. package/dist/types/index.d.ts +19 -19
  71. package/dist/types/index.js +202 -41
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/utils/index.d.ts +17 -4
  74. package/dist/utils/index.js +48 -9
  75. package/dist/utils/index.js.map +1 -1
  76. package/package.json +1 -1
@@ -5,6 +5,8 @@ import { isAbsolute, resolve } from 'path';
5
5
  import * as os from 'os';
6
6
  import { hostname } from 'os';
7
7
  import { EventEmitter } from 'events';
8
+ import { createReadStream } from 'fs';
9
+ import { createInterface } from 'readline';
8
10
 
9
11
  // src/coordination/brain.ts
10
12
  var ObservableBrainArbiter = class {
@@ -5877,6 +5879,68 @@ Working rules:
5877
5879
  - When in doubt, flag as medium rather than ignoring potential issues`
5878
5880
  // Budgets are set by the orchestrator per task — see fleet.ts header.
5879
5881
  };
5882
+ var SHADOW_AGENT = {
5883
+ id: "shadow-agent",
5884
+ name: "Shadow",
5885
+ role: "shadow-agent",
5886
+ prompt: `You are the Shadow Agent \u2014 a silent background monitor for the WrongStack fleet.
5887
+
5888
+ Your job is to observe, detect anomalies, and be ready to intervene \u2014 but only when commanded.
5889
+
5890
+ ## Core Responsibilities
5891
+
5892
+ 1. **Fleet Monitoring** (every 30s)
5893
+ - Call \`fleet_status\` + \`fleet_health\` on each heartbeat
5894
+ - Track what each agent is doing (task descriptions)
5895
+ - Detect stuck agents (>5min no events), idle agents, crashed agents
5896
+
5897
+ 2. **FleetBus Subscription**
5898
+ - Subscribe to \`subagent.*\` events to track lifecycle
5899
+ - Subscribe to \`tool.executed\` to monitor activity
5900
+ - Track agent joins (subagent.started) and leaves (subagent.stopped)
5901
+
5902
+ 3. **Mailbox Surveillance**
5903
+ - Monitor for \`control\` type messages starting with "hoop"
5904
+ - Detect orphan assigns (assign without result within 5min)
5905
+ - Cross-session awareness via shared project mailbox
5906
+
5907
+ 4. **Spike Detection**
5908
+ - Track task duration per agent
5909
+ - Flag agents that spawn and die within <5 seconds
5910
+ - Log spike events with reason (completed/error/killed/timeout)
5911
+
5912
+ 5. **Intervention Commands**
5913
+ Parse these from mailbox control messages:
5914
+ - \`hoop <agentId>\` \u2014 terminate specific agent
5915
+ - \`hoop all\` \u2014 terminate all running agents
5916
+ - \`shadow status\` \u2014 report current fleet snapshot
5917
+ - \`shadow mute\` \u2014 pause heartbeat monitoring
5918
+ - \`shadow resume\` \u2014 resume heartbeat monitoring
5919
+ - \`shadow interval <ms>\` \u2014 change heartbeat interval
5920
+ - \`shadow model <model-id>\` \u2014 change analysis model
5921
+
5922
+ ## Operating Rules
5923
+
5924
+ - **Silent by default**: Use DEBUG level logging unless anomaly detected
5925
+ - **Deterministic**: Same state always produces same actions \u2014 no randomness
5926
+ - **Report on anomaly**: When anomaly detected, use \`mail_send\` to broadcast warning
5927
+ - **Never auto-intervene**: Always report unless explicitly commanded
5928
+ - **Minimal footprint**: Small state, efficient snapshots
5929
+
5930
+ ## Startup Sequence
5931
+
5932
+ 1. Send broadcast: \`shadow:started { intervalMs, model, startTime }\`
5933
+ 2. Subscribe to FleetBus for all relevant events
5934
+ 3. Schedule heartbeat cron job at configured interval
5935
+ 4. Wait for commands or anomalies
5936
+
5937
+ ## Shutdown Sequence
5938
+
5939
+ 1. Cancel all cron jobs (\`cron_cancel\`)
5940
+ 2. Send broadcast: \`shadow:stopped { reason, finalState }\`
5941
+ 3. Clean up FleetBus subscriptions`
5942
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
5943
+ };
5880
5944
  var CRITIC_AGENT = {
5881
5945
  id: "critic",
5882
5946
  name: "Critic",
@@ -5917,6 +5981,7 @@ var FLEET_ROSTER = {
5917
5981
  "refactor-planner": REFACTOR_PLANNER_AGENT,
5918
5982
  "security-scanner": SECURITY_SCANNER_AGENT,
5919
5983
  "critic": CRITIC_AGENT,
5984
+ "shadow-agent": SHADOW_AGENT,
5920
5985
  ...Object.fromEntries(
5921
5986
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
5922
5987
  )
@@ -5928,6 +5993,8 @@ var FLEET_ROSTER_BUDGETS = {
5928
5993
  "refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
5929
5994
  "security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
5930
5995
  "critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
5996
+ "shadow-agent": { timeoutMs: 24 * 60 * 60 * 1e3, maxIterations: 1e4, maxToolCalls: 5e3 },
5997
+ // Long-running background monitor
5931
5998
  ...Object.fromEntries(
5932
5999
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
5933
6000
  )
@@ -8748,6 +8815,8 @@ function generateSessionId(startedAt, model) {
8748
8815
  const modelPart = model ? `_${sanitizeModel(model)}` : "";
8749
8816
  return `${date}/${time}Z${modelPart}_${suffix}`;
8750
8817
  }
8818
+
8819
+ // src/storage/session-store.ts
8751
8820
  var DefaultSessionStore = class _DefaultSessionStore {
8752
8821
  dir;
8753
8822
  events;
@@ -8765,6 +8834,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8765
8834
  _loadCache = /* @__PURE__ */ new Map();
8766
8835
  _indexCache = null;
8767
8836
  static LOAD_CACHE_MAX_ENTRIES = 50;
8837
+ static LIST_SCAN_CONCURRENCY = 32;
8768
8838
  constructor(opts) {
8769
8839
  this.dir = opts.dir;
8770
8840
  this.events = opts.events;
@@ -8987,15 +9057,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8987
9057
  });
8988
9058
  return indexed.slice(0, limit);
8989
9059
  }
8990
- const ids = await this.collectSessionIds(this.dir);
8991
- const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
8992
- const out = sessions.filter((s) => s !== null);
8993
- out.sort((a, b) => {
8994
- if (a.startedAt < b.startedAt) return 1;
8995
- if (a.startedAt > b.startedAt) return -1;
8996
- return a.id.localeCompare(b.id);
8997
- });
8998
- return out.slice(0, limit);
9060
+ return await this.listFromDirectoryScan(limit);
8999
9061
  } catch {
9000
9062
  return [];
9001
9063
  }
@@ -9120,43 +9182,102 @@ var DefaultSessionStore = class _DefaultSessionStore {
9120
9182
  this._indexCache = null;
9121
9183
  return valid.length;
9122
9184
  }
9185
+ async listFromDirectoryScan(limit) {
9186
+ const refs = await this.collectSessionFiles(this.dir);
9187
+ const candidates = await mapWithConcurrency(
9188
+ refs,
9189
+ _DefaultSessionStore.LIST_SCAN_CONCURRENCY,
9190
+ async (ref) => {
9191
+ const manifest = await this.readSummaryManifest(ref.id);
9192
+ if (manifest) return { summary: manifest, needsBackfill: false };
9193
+ const summary = await this.summaryHeaderFor(ref);
9194
+ return summary ? { summary, needsBackfill: true } : null;
9195
+ }
9196
+ );
9197
+ const out = candidates.filter((s) => s !== null);
9198
+ out.sort((a, b) => compareSessionSummaries(a.summary, b.summary));
9199
+ const selected = out.slice(0, limit);
9200
+ const summaries = await mapWithConcurrency(
9201
+ selected,
9202
+ Math.min(_DefaultSessionStore.LIST_SCAN_CONCURRENCY, Math.max(1, limit)),
9203
+ async (candidate) => {
9204
+ if (!candidate.needsBackfill) return candidate.summary;
9205
+ return await this.summaryFor(candidate.summary.id).catch(() => candidate.summary);
9206
+ }
9207
+ );
9208
+ return summaries.filter((s) => s !== null);
9209
+ }
9210
+ async collectSessionFiles(dir, prefix = "", depth = 0) {
9211
+ let entries;
9212
+ try {
9213
+ entries = await fsp6.readdir(dir, { withFileTypes: true });
9214
+ } catch {
9215
+ return [];
9216
+ }
9217
+ const dirEntries = [];
9218
+ const files = [];
9219
+ for (const entry of entries) {
9220
+ if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
9221
+ if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
9222
+ continue;
9223
+ if (entry.isDirectory()) {
9224
+ dirEntries.push(entry);
9225
+ } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
9226
+ if (entry.name === "_index.jsonl") continue;
9227
+ const base = entry.name.replace(/\.jsonl$/, "");
9228
+ const id = prefix ? `${prefix}/${base}` : base;
9229
+ files.push({ id, filePath: path5.join(dir, entry.name) });
9230
+ }
9231
+ }
9232
+ const childFileArrays = await Promise.all(
9233
+ dirEntries.map((entry) => {
9234
+ const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
9235
+ return this.collectSessionFiles(path5.join(dir, entry.name), childPrefix, depth + 1);
9236
+ })
9237
+ );
9238
+ return [...childFileArrays.flat(), ...files];
9239
+ }
9123
9240
  /** Recursively collect session IDs from date-shard subdirectories.
9124
9241
  * IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
9125
9242
  * Skips `.jsonl`/`.summary.json` root files, dot-files, and
9126
9243
  * sub-directories that belong to fleet/subagent sessions. */
9127
9244
  async collectSessionIds(dir, prefix = "", depth = 0) {
9128
- const ids = [];
9129
9245
  let entries;
9130
9246
  try {
9131
9247
  entries = await fsp6.readdir(dir, { withFileTypes: true });
9132
9248
  } catch {
9133
- return ids;
9249
+ return [];
9134
9250
  }
9251
+ const dirEntries = [];
9252
+ const fileIds = [];
9135
9253
  for (const entry of entries) {
9136
9254
  if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
9137
9255
  if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
9138
9256
  continue;
9139
9257
  if (entry.isDirectory()) {
9140
- const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
9141
- ids.push(...await this.collectSessionIds(path5.join(dir, entry.name), childPrefix, depth + 1));
9258
+ dirEntries.push(entry);
9142
9259
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
9143
9260
  if (entry.name === "_index.jsonl") continue;
9144
9261
  const base = entry.name.replace(/\.jsonl$/, "");
9145
- ids.push(prefix ? `${prefix}/${base}` : base);
9262
+ fileIds.push(prefix ? `${prefix}/${base}` : base);
9146
9263
  }
9147
9264
  }
9148
- return ids;
9265
+ const childIdArrays = await Promise.all(
9266
+ dirEntries.map((entry) => {
9267
+ const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
9268
+ return this.collectSessionIds(path5.join(dir, entry.name), childPrefix, depth + 1);
9269
+ })
9270
+ );
9271
+ return [...childIdArrays.flat(), ...fileIds];
9149
9272
  }
9150
9273
  async summaryFor(id) {
9151
9274
  const manifest = this.sessionPath(id, ".summary.json");
9152
9275
  const t0 = Date.now();
9153
9276
  let outcome = "success";
9154
9277
  let errorMsg;
9278
+ const fromManifest = await this.readSummaryManifest(id, t0);
9279
+ if (fromManifest) return fromManifest;
9155
9280
  try {
9156
- const raw = await fsp6.readFile(manifest, "utf8");
9157
- this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
9158
- return JSON.parse(raw);
9159
- } catch {
9160
9281
  const full = this.sessionPath(id, ".jsonl");
9161
9282
  const stat7 = await fsp6.stat(full);
9162
9283
  const summary = await this.summarize(id, stat7.mtime.toISOString());
@@ -9175,6 +9296,78 @@ var DefaultSessionStore = class _DefaultSessionStore {
9175
9296
  errorMsg = "summary fallback \u2014 manifest rebuilt";
9176
9297
  this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
9177
9298
  return summary;
9299
+ } catch (err) {
9300
+ outcome = "failure";
9301
+ errorMsg = toErrorMessage(err);
9302
+ this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
9303
+ return {
9304
+ id,
9305
+ title: "(damaged)",
9306
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
9307
+ model: "unknown",
9308
+ provider: "unknown",
9309
+ tokenTotal: 0
9310
+ };
9311
+ }
9312
+ }
9313
+ async readSummaryManifest(id, startTime = Date.now()) {
9314
+ const manifest = this.sessionPath(id, ".summary.json");
9315
+ try {
9316
+ const raw = await fsp6.readFile(manifest, "utf8");
9317
+ this.emitRead(id, manifest, "summary", "success", Date.now() - startTime);
9318
+ return JSON.parse(raw);
9319
+ } catch {
9320
+ return null;
9321
+ }
9322
+ }
9323
+ async summaryHeaderFor(ref) {
9324
+ let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
9325
+ try {
9326
+ const stat7 = await fsp6.stat(ref.filePath);
9327
+ if (!stat7.isFile()) {
9328
+ return {
9329
+ id: ref.id,
9330
+ title: "(damaged)",
9331
+ startedAt: stat7.mtime.toISOString(),
9332
+ model: "unknown",
9333
+ provider: "unknown",
9334
+ tokenTotal: 0
9335
+ };
9336
+ }
9337
+ mtime = stat7.mtime.toISOString();
9338
+ } catch {
9339
+ return null;
9340
+ }
9341
+ try {
9342
+ for await (const event of this.iterSessionEvents(ref.filePath)) {
9343
+ if (event.type === "session_start") {
9344
+ return {
9345
+ id: ref.id,
9346
+ title: "(empty session)",
9347
+ startedAt: event.ts,
9348
+ model: event.model ?? "unknown",
9349
+ provider: event.provider ?? "unknown",
9350
+ tokenTotal: 0
9351
+ };
9352
+ }
9353
+ }
9354
+ return {
9355
+ id: ref.id,
9356
+ title: "(empty session)",
9357
+ startedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
9358
+ model: "unknown",
9359
+ provider: "unknown",
9360
+ tokenTotal: 0
9361
+ };
9362
+ } catch {
9363
+ return {
9364
+ id: ref.id,
9365
+ title: "(damaged)",
9366
+ startedAt: mtime,
9367
+ model: "unknown",
9368
+ provider: "unknown",
9369
+ tokenTotal: 0
9370
+ };
9178
9371
  }
9179
9372
  }
9180
9373
  /**
@@ -9299,39 +9492,62 @@ var DefaultSessionStore = class _DefaultSessionStore {
9299
9492
  }
9300
9493
  async summarize(id, mtime) {
9301
9494
  try {
9302
- const data = await this.load(id);
9303
- const firstUser = data.events.find((e) => e.type === "user_input");
9304
- const title = firstUser && firstUser.type === "user_input" ? userInputTitle(firstUser.content) : "(empty session)";
9495
+ const file = this.sessionPath(id, ".jsonl");
9496
+ let title = "(empty session)";
9497
+ let startedAt = (/* @__PURE__ */ new Date(0)).toISOString();
9498
+ let endedAt;
9499
+ let model = "unknown";
9500
+ let provider = "unknown";
9501
+ let tokenIn = 0;
9502
+ let tokenOut = 0;
9305
9503
  let iterationCount = 0;
9306
9504
  let toolCallCount = 0;
9307
9505
  let toolErrorCount = 0;
9308
9506
  let fileChangeCount = 0;
9309
9507
  const toolBreakdown = {};
9310
9508
  let outcome;
9311
- const lastEvent = data.events[data.events.length - 1];
9312
- for (const e of data.events) {
9313
- if (e.type === "in_flight_start") iterationCount++;
9509
+ let lastEventType;
9510
+ let hasError = false;
9511
+ let sawStart = false;
9512
+ for await (const e of this.iterSessionEvents(file)) {
9513
+ lastEventType = e.type;
9514
+ if (e.type === "session_start") {
9515
+ if (!sawStart) {
9516
+ sawStart = true;
9517
+ startedAt = e.ts;
9518
+ model = e.model ?? "unknown";
9519
+ provider = e.provider ?? "unknown";
9520
+ }
9521
+ } else if (e.type === "session_end") {
9522
+ endedAt = e.ts;
9523
+ } else if (e.type === "user_input") {
9524
+ if (title === "(empty session)") title = userInputTitle(e.content);
9525
+ } else if (e.type === "llm_response") {
9526
+ tokenIn += e.usage.input ?? 0;
9527
+ tokenOut += e.usage.output ?? 0;
9528
+ } else if (e.type === "in_flight_start") iterationCount++;
9314
9529
  else if (e.type === "tool_call_start") {
9315
9530
  toolCallCount++;
9316
9531
  toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;
9317
9532
  } else if (e.type === "tool_result" && e.isError) toolErrorCount++;
9318
9533
  else if (e.type === "file_snapshot") fileChangeCount += e.files.length;
9534
+ else if (e.type === "error" || e.type === "provider_error") hasError = true;
9319
9535
  }
9320
- if (lastEvent?.type === "session_end") {
9536
+ if (lastEventType === "session_end") {
9321
9537
  outcome = "completed";
9322
- } else if (lastEvent?.type === "in_flight_start") {
9538
+ } else if (lastEventType === "in_flight_start") {
9323
9539
  outcome = "aborted";
9324
- } else if (data.events.some((e) => e.type === "error")) {
9540
+ } else if (hasError) {
9325
9541
  outcome = "error";
9326
9542
  }
9327
9543
  return {
9328
9544
  id,
9329
9545
  title,
9330
- startedAt: data.metadata.startedAt,
9331
- endedAt: data.metadata.endedAt,
9332
- model: data.metadata.model ?? "unknown",
9333
- provider: data.metadata.provider ?? "unknown",
9334
- tokenTotal: data.usage.input + data.usage.output,
9546
+ startedAt,
9547
+ endedAt,
9548
+ model,
9549
+ provider,
9550
+ tokenTotal: tokenIn + tokenOut,
9335
9551
  iterationCount: iterationCount > 0 ? iterationCount : void 0,
9336
9552
  toolCallCount: toolCallCount > 0 ? toolCallCount : void 0,
9337
9553
  toolErrorCount: toolErrorCount > 0 ? toolErrorCount : void 0,
@@ -9350,6 +9566,25 @@ var DefaultSessionStore = class _DefaultSessionStore {
9350
9566
  };
9351
9567
  }
9352
9568
  }
9569
+ async *iterSessionEvents(file) {
9570
+ const stream = createReadStream(file, { encoding: "utf8" });
9571
+ const lines = createInterface({ input: stream, crlfDelay: Infinity });
9572
+ try {
9573
+ for await (const line of lines) {
9574
+ if (!line.trim()) continue;
9575
+ try {
9576
+ const parsed = JSON.parse(line);
9577
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
9578
+ yield parsed;
9579
+ }
9580
+ } catch {
9581
+ }
9582
+ }
9583
+ } finally {
9584
+ lines.close();
9585
+ stream.destroy();
9586
+ }
9587
+ }
9353
9588
  metaFromEvents(id, events) {
9354
9589
  const start = events.find((e) => e.type === "session_start");
9355
9590
  const end = events.findLast((e) => e.type === "session_end");
@@ -10013,6 +10248,27 @@ function userInputTitle(content) {
10013
10248
  const text = typeof content === "string" ? content : content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
10014
10249
  return (text || "(non-text input)").slice(0, 60);
10015
10250
  }
10251
+ function compareSessionSummaries(a, b) {
10252
+ if (a.startedAt < b.startedAt) return 1;
10253
+ if (a.startedAt > b.startedAt) return -1;
10254
+ return a.id.localeCompare(b.id);
10255
+ }
10256
+ async function mapWithConcurrency(items, concurrency, fn) {
10257
+ if (items.length === 0) return [];
10258
+ const out = new Array(items.length);
10259
+ let next = 0;
10260
+ const workerCount = Math.min(Math.max(1, concurrency), items.length);
10261
+ const workers = Array.from({ length: workerCount }, async () => {
10262
+ for (; ; ) {
10263
+ const idx = next++;
10264
+ if (idx >= items.length) return;
10265
+ const item = items[idx];
10266
+ if (item !== void 0) out[idx] = await fn(item);
10267
+ }
10268
+ });
10269
+ await Promise.all(workers);
10270
+ return out;
10271
+ }
10016
10272
 
10017
10273
  // src/coordination/director-session.ts
10018
10274
  function makeDirectorSessionFactory(opts) {
@@ -10487,6 +10743,10 @@ var DefaultMailbox = class {
10487
10743
  _messageCache = null;
10488
10744
  _messageCacheMtime = -1;
10489
10745
  _messageCacheSize = -1;
10746
+ /** Primary index: recipient → Set of messages (points into _messageCache). */
10747
+ _byTo = /* @__PURE__ */ new Map();
10748
+ /** Secondary index: sender → Set of messages (points into _messageCache). */
10749
+ _byFrom = /* @__PURE__ */ new Map();
10490
10750
  constructor(sessionDir) {
10491
10751
  this.filePath = path5.join(sessionDir, MAILBOX_FILE);
10492
10752
  }
@@ -10522,12 +10782,30 @@ var DefaultMailbox = class {
10522
10782
  }
10523
10783
  // ── Query ─────────────────────────────────────────────────────────────
10524
10784
  async query(q) {
10525
- const all = await this._readAllCached();
10785
+ const needFullScan = q.unreadBy !== void 0 || q.since !== void 0;
10786
+ let candidates;
10787
+ if (needFullScan) {
10788
+ candidates = await this._readAllCached();
10789
+ } else {
10790
+ await this._readAllCached();
10791
+ if (q.to !== void 0) {
10792
+ const direct = this._byTo.get(q.to);
10793
+ const broadcast = this._byTo.get("*");
10794
+ const combined = /* @__PURE__ */ new Map();
10795
+ if (direct) for (const m of direct) combined.set(m.id, m);
10796
+ if (broadcast) for (const m of broadcast) combined.set(m.id, m);
10797
+ candidates = Array.from(combined.values());
10798
+ } else if (q.from !== void 0) {
10799
+ candidates = Array.from(this._byFrom.get(q.from) ?? []);
10800
+ } else {
10801
+ candidates = await this._readAllCached();
10802
+ }
10803
+ }
10526
10804
  const limit = q.limit ?? 50;
10527
10805
  const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
10528
10806
  const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
10529
10807
  const filtered = [];
10530
- for (const msg of all) {
10808
+ for (const msg of candidates) {
10531
10809
  if (q.to !== void 0 && msg.to !== q.to && msg.to !== "*") continue;
10532
10810
  if (q.from !== void 0 && msg.from !== q.from) continue;
10533
10811
  if (q.unreadBy !== void 0 && q.unreadBy in msg.readBy) continue;
@@ -10628,6 +10906,8 @@ var DefaultMailbox = class {
10628
10906
  this._messageCache = null;
10629
10907
  this._messageCacheMtime = -1;
10630
10908
  this._messageCacheSize = -1;
10909
+ this._byTo.clear();
10910
+ this._byFrom.clear();
10631
10911
  }
10632
10912
  async clearAll() {
10633
10913
  await withFileLock(this.filePath, async () => {
@@ -10686,36 +10966,81 @@ var DefaultMailbox = class {
10686
10966
  async _readAll() {
10687
10967
  try {
10688
10968
  const raw = await fsp6.readFile(this.filePath, "utf8");
10689
- const lines = raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
10690
- const messages = [];
10691
- for (const line of lines) {
10692
- try {
10693
- const parsed = JSON.parse(line);
10694
- if (!parsed["readBy"]) {
10695
- const readBy = {};
10696
- if (parsed["read"] && parsed["readAt"]) {
10697
- readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
10698
- }
10699
- parsed["readBy"] = readBy;
10700
- delete parsed["read"];
10701
- delete parsed["readAt"];
10702
- }
10703
- messages.push(parsed);
10704
- } catch {
10705
- }
10706
- }
10707
- return messages;
10969
+ return this._parseLines(raw);
10708
10970
  } catch (err) {
10709
10971
  if (err.code === "ENOENT") return [];
10710
10972
  throw err;
10711
10973
  }
10712
10974
  }
10975
+ /**
10976
+ * Read only newly-appended bytes from the file and append them to the
10977
+ * in-memory cache, avoiding a full re-read when the file only grew.
10978
+ */
10979
+ async _readNewMessagesOnly(fd, oldSize, newSize) {
10980
+ const tailLen = newSize - oldSize;
10981
+ const buf = Buffer.alloc(tailLen);
10982
+ await fd.read(buf, 0, tailLen, oldSize);
10983
+ const tail = buf.toString("utf8");
10984
+ for (const line of tail.split(LINE_SEPARATOR)) {
10985
+ if (!line.trim()) continue;
10986
+ try {
10987
+ const parsed = JSON.parse(line);
10988
+ if (!parsed["readBy"]) {
10989
+ const readBy = {};
10990
+ if (parsed["read"] && parsed["readAt"]) {
10991
+ readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
10992
+ }
10993
+ parsed["readBy"] = readBy;
10994
+ delete parsed["read"];
10995
+ delete parsed["readAt"];
10996
+ }
10997
+ const msg = parsed;
10998
+ this._messageCache.push(msg);
10999
+ this._indexMsg(msg);
11000
+ } catch {
11001
+ }
11002
+ }
11003
+ return this._messageCache;
11004
+ }
11005
+ /** Parse a JSONL string into MailboxMessage[], including migration. */
11006
+ _parseLines(raw) {
11007
+ const lines = raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
11008
+ const messages = [];
11009
+ for (const line of lines) {
11010
+ try {
11011
+ const parsed = JSON.parse(line);
11012
+ if (!parsed["readBy"]) {
11013
+ const readBy = {};
11014
+ if (parsed["read"] && parsed["readAt"]) {
11015
+ readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
11016
+ }
11017
+ parsed["readBy"] = readBy;
11018
+ delete parsed["read"];
11019
+ delete parsed["readAt"];
11020
+ }
11021
+ messages.push(parsed);
11022
+ } catch {
11023
+ }
11024
+ }
11025
+ return messages;
11026
+ }
10713
11027
  async _readAllCached() {
10714
11028
  try {
10715
11029
  const st = await fsp6.stat(this.filePath);
10716
11030
  if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
10717
11031
  return this._messageCache;
10718
11032
  }
11033
+ if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
11034
+ const fd = await fsp6.open(this.filePath, "r");
11035
+ try {
11036
+ const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
11037
+ this._messageCacheMtime = st.mtimeMs;
11038
+ this._messageCacheSize = st.size;
11039
+ return updated;
11040
+ } finally {
11041
+ await fd.close();
11042
+ }
11043
+ }
10719
11044
  const all = await this._readAll();
10720
11045
  this._setMessageCache(all, st.mtimeMs, st.size);
10721
11046
  return all;
@@ -10732,9 +11057,12 @@ var DefaultMailbox = class {
10732
11057
  this._messageCache = null;
10733
11058
  this._messageCacheMtime = -1;
10734
11059
  this._messageCacheSize = -1;
11060
+ this._byTo.clear();
11061
+ this._byFrom.clear();
10735
11062
  return;
10736
11063
  }
10737
11064
  this._messageCache = messages;
11065
+ this._buildIndexes(messages);
10738
11066
  if (mtime !== void 0 && size !== void 0) {
10739
11067
  this._messageCacheMtime = mtime;
10740
11068
  this._messageCacheSize = size;
@@ -10752,9 +11080,35 @@ var DefaultMailbox = class {
10752
11080
  this._messageCache = null;
10753
11081
  this._messageCacheMtime = -1;
10754
11082
  this._messageCacheSize = -1;
11083
+ this._byTo.clear();
11084
+ this._byFrom.clear();
10755
11085
  return;
10756
11086
  }
10757
11087
  this._messageCache.push(msg);
11088
+ this._indexMsg(msg);
11089
+ }
11090
+ /** Rebuild both indexes from a full message list. */
11091
+ _buildIndexes(messages) {
11092
+ this._byTo.clear();
11093
+ this._byFrom.clear();
11094
+ for (const msg of messages) {
11095
+ this._indexMsg(msg);
11096
+ }
11097
+ }
11098
+ /** Add a single message to both indexes. */
11099
+ _indexMsg(msg) {
11100
+ const toSet = this._byTo.get(msg.to);
11101
+ if (toSet) {
11102
+ toSet.add(msg);
11103
+ } else {
11104
+ this._byTo.set(msg.to, /* @__PURE__ */ new Set([msg]));
11105
+ }
11106
+ const fromSet = this._byFrom.get(msg.from);
11107
+ if (fromSet) {
11108
+ fromSet.add(msg);
11109
+ } else {
11110
+ this._byFrom.set(msg.from, /* @__PURE__ */ new Set([msg]));
11111
+ }
10758
11112
  }
10759
11113
  };
10760
11114
  var BrainMonitor = class {
@@ -11330,30 +11684,69 @@ var GlobalMailbox = class {
11330
11684
  async _readMessages() {
11331
11685
  try {
11332
11686
  const raw = await fsp6.readFile(this.messagePath, "utf8");
11333
- const lines = raw.split(LINE_SEPARATOR2).filter((l) => l.trim().length > 0);
11334
- const messages = [];
11335
- for (const line of lines) {
11336
- try {
11337
- const parsed = JSON.parse(line);
11338
- if (!parsed["readBy"]) {
11339
- const readBy = {};
11340
- if (parsed["read"] && parsed["readAt"]) {
11341
- readBy[parsed["to"]] = parsed["readAt"];
11342
- }
11343
- parsed["readBy"] = readBy;
11344
- delete parsed["read"];
11345
- delete parsed["readAt"];
11346
- }
11347
- messages.push(parsed);
11348
- } catch {
11349
- }
11350
- }
11351
- return messages;
11687
+ return this._parseLines(raw);
11352
11688
  } catch (err) {
11353
11689
  if (err.code === "ENOENT") return [];
11354
11690
  throw err;
11355
11691
  }
11356
11692
  }
11693
+ /**
11694
+ * Read only newly-appended bytes from the file and append them to the
11695
+ * in-memory cache. This avoids re-reading and re-parsing the entire file
11696
+ * when another process appended messages since our last read.
11697
+ *
11698
+ * Only safe when the file grew in size (messages are append-only). When
11699
+ * the file was rewritten (ack/purge changed existing content), callers
11700
+ * must fall back to {@link _readMessages}.
11701
+ *
11702
+ * @returns The (now up-to-date) message cache.
11703
+ */
11704
+ async _readNewMessagesOnly(fd, oldSize, newSize) {
11705
+ const tailLen = newSize - oldSize;
11706
+ const buf = Buffer.alloc(tailLen);
11707
+ await fd.read(buf, 0, tailLen, oldSize);
11708
+ const tail = buf.toString("utf8");
11709
+ for (const line of tail.split(LINE_SEPARATOR2)) {
11710
+ if (!line.trim()) continue;
11711
+ try {
11712
+ const parsed = JSON.parse(line);
11713
+ if (!parsed["readBy"]) {
11714
+ const readBy = {};
11715
+ if (parsed["read"] && parsed["readAt"]) {
11716
+ readBy[parsed["to"]] = parsed["readAt"];
11717
+ }
11718
+ parsed["readBy"] = readBy;
11719
+ delete parsed["read"];
11720
+ delete parsed["readAt"];
11721
+ }
11722
+ this._messageCache.push(parsed);
11723
+ } catch {
11724
+ }
11725
+ }
11726
+ return this._messageCache;
11727
+ }
11728
+ /** Parse a JSONL string into MailboxMessage[], including migration. */
11729
+ _parseLines(raw) {
11730
+ const lines = raw.split(LINE_SEPARATOR2).filter((l) => l.trim().length > 0);
11731
+ const messages = [];
11732
+ for (const line of lines) {
11733
+ try {
11734
+ const parsed = JSON.parse(line);
11735
+ if (!parsed["readBy"]) {
11736
+ const readBy = {};
11737
+ if (parsed["read"] && parsed["readAt"]) {
11738
+ readBy[parsed["to"]] = parsed["readAt"];
11739
+ }
11740
+ parsed["readBy"] = readBy;
11741
+ delete parsed["read"];
11742
+ delete parsed["readAt"];
11743
+ }
11744
+ messages.push(parsed);
11745
+ } catch {
11746
+ }
11747
+ }
11748
+ return messages;
11749
+ }
11357
11750
  /**
11358
11751
  * Read messages, then adopt the result as the in-memory cache. Use this
11359
11752
  * from writers that just took the file lock — the read reflects the
@@ -11373,6 +11766,10 @@ var GlobalMailbox = class {
11373
11766
  * stat matches the cached mtime+size we return the cached array — no
11374
11767
  * file read and no JSON.parse — collapsing the per-iteration query
11375
11768
  * cost on the mailbox-loop hot path.
11769
+ *
11770
+ * When the file only grew (new messages appended by another process),
11771
+ * we read and parse just the tail bytes instead of the entire file.
11772
+ * This avoids re-parsing the full 10K-message history on every check.
11376
11773
  */
11377
11774
  async _readMessagesCached() {
11378
11775
  try {
@@ -11380,6 +11777,17 @@ var GlobalMailbox = class {
11380
11777
  if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
11381
11778
  return this._messageCache;
11382
11779
  }
11780
+ if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
11781
+ const fd = await fsp6.open(this.messagePath, "r");
11782
+ try {
11783
+ const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
11784
+ this._messageCacheMtime = st.mtimeMs;
11785
+ this._messageCacheSize = st.size;
11786
+ return updated;
11787
+ } finally {
11788
+ await fd.close();
11789
+ }
11790
+ }
11383
11791
  const all = await this._readMessages();
11384
11792
  this._setMessageCache(all, st.mtimeMs, st.size);
11385
11793
  return all;
@@ -14814,7 +15222,263 @@ ${input.detail}`
14814
15222
  this.onCoordinatorEvent?.(event);
14815
15223
  }
14816
15224
  };
15225
+ var AgentMonitorService = class {
15226
+ _fleetBus;
15227
+ _events;
15228
+ _transcriptsDir;
15229
+ _maxEntries;
15230
+ _streamEnabled;
15231
+ _onEntry;
15232
+ /** Per-subagent virtual sessions. */
15233
+ _sessions = /* @__PURE__ */ new Map();
15234
+ /** Disposers for FleetBus subscriptions, keyed by subagentId. */
15235
+ _subscriptions = /* @__PURE__ */ new Map();
15236
+ /** Generic fleet-wide subscription disposer. */
15237
+ _fleetDisposer;
15238
+ /** Track whether service is running. */
15239
+ _started = false;
15240
+ constructor(opts) {
15241
+ this._fleetBus = opts.fleetBus;
15242
+ this._events = opts.events;
15243
+ this._transcriptsDir = opts.transcriptsDir;
15244
+ this._maxEntries = opts.maxEntriesPerAgent ?? 500;
15245
+ this._streamEnabled = opts.streamEnabled ?? false;
15246
+ this._onEntry = opts.onEntry;
15247
+ }
15248
+ // ── Public API ────────────────────────────────────────────────────
15249
+ /** Set the FleetBus to listen on. Must be called before `start()`. */
15250
+ setFleetBus(bus) {
15251
+ this._fleetBus = bus;
15252
+ }
15253
+ get streamEnabled() {
15254
+ return this._streamEnabled;
15255
+ }
15256
+ /** Enable/disable streaming agent conversations to the main chat timeline. */
15257
+ setStreamEnabled(enabled) {
15258
+ this._streamEnabled = enabled;
15259
+ }
15260
+ /** Get a snapshot of all known agent sessions. */
15261
+ getAllSessions() {
15262
+ return Array.from(this._sessions.values());
15263
+ }
15264
+ /** Get a specific agent's virtual session, or undefined. */
15265
+ getSession(subagentId) {
15266
+ return this._sessions.get(subagentId);
15267
+ }
15268
+ /** Get transcript entries for a specific agent, newest first. */
15269
+ getTranscript(subagentId, limit = 50) {
15270
+ const session = this._sessions.get(subagentId);
15271
+ if (!session) return [];
15272
+ return session.transcript.slice(-limit).reverse();
15273
+ }
15274
+ /** Set a callback for each new timeline entry (HQ bridge). */
15275
+ setOnEntry(handler) {
15276
+ this._onEntry = handler;
15277
+ }
15278
+ // ── Lifecycle ──────────────────────────────────────────────────────
15279
+ /** Start listening to FleetBus events. */
15280
+ start() {
15281
+ if (this._started) return;
15282
+ if (!this._fleetBus) {
15283
+ this._started = true;
15284
+ return;
15285
+ }
15286
+ this._started = true;
15287
+ this._fleetDisposer = this._fleetBus.onAny((event) => {
15288
+ this._routeEvent(event.subagentId, event.type, event.payload);
15289
+ });
15290
+ }
15291
+ /** Stop listening and clean up all subscriptions. */
15292
+ stop() {
15293
+ if (!this._started) return;
15294
+ this._started = false;
15295
+ if (this._fleetDisposer) {
15296
+ this._fleetDisposer();
15297
+ this._fleetDisposer = void 0;
15298
+ }
15299
+ for (const disposer of this._subscriptions.values()) {
15300
+ disposer();
15301
+ }
15302
+ this._subscriptions.clear();
15303
+ }
15304
+ /** Ensure a subagent is being tracked. Called when a subagent spawns. */
15305
+ trackSubagent(subagentId, agentName, task) {
15306
+ if (this._sessions.has(subagentId)) return;
15307
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15308
+ const session = {
15309
+ subagentId,
15310
+ agentName,
15311
+ createdAt: now,
15312
+ status: "spawned",
15313
+ task,
15314
+ transcript: []
15315
+ };
15316
+ this._sessions.set(subagentId, session);
15317
+ this._addEntry(subagentId, {
15318
+ id: this._uid(),
15319
+ subagentId,
15320
+ agentName,
15321
+ ts: now,
15322
+ kind: "system",
15323
+ content: task ? `\u{1F3AF} Spawned: ${task}` : "\u{1F916} Agent spawned",
15324
+ iteration: 0
15325
+ });
15326
+ this._events.emit("agent.status_changed", {
15327
+ subagentId,
15328
+ agentName,
15329
+ status: "spawned",
15330
+ ts: now,
15331
+ summary: task,
15332
+ task
15333
+ });
15334
+ }
15335
+ /** Mark a subagent as completed/failed/etc. Called on subagent finish. */
15336
+ completeSubagent(subagentId, status, summary) {
15337
+ const session = this._sessions.get(subagentId);
15338
+ if (!session) return;
15339
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15340
+ session.status = status;
15341
+ this._addEntry(subagentId, {
15342
+ id: this._uid(),
15343
+ subagentId,
15344
+ agentName: session.agentName,
15345
+ ts: now,
15346
+ kind: "status",
15347
+ content: summary ?? `Agent ${status}`,
15348
+ iteration: 999
15349
+ });
15350
+ this._events.emit("agent.status_changed", {
15351
+ subagentId,
15352
+ agentName: session.agentName,
15353
+ status,
15354
+ ts: now,
15355
+ summary,
15356
+ task: session.task
15357
+ });
15358
+ }
15359
+ // ── Internal ───────────────────────────────────────────────────────
15360
+ _routeEvent(subagentId, type, payload) {
15361
+ const session = this._sessions.get(subagentId);
15362
+ if (!session) return;
15363
+ switch (type) {
15364
+ case "provider.text_delta": {
15365
+ const text = payload.text;
15366
+ if (!text || text.length === 0) return;
15367
+ const iteration = payload.iteration ?? 0;
15368
+ this._addEntry(subagentId, {
15369
+ id: this._uid(),
15370
+ subagentId,
15371
+ agentName: session.agentName,
15372
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15373
+ kind: "text",
15374
+ content: text,
15375
+ iteration
15376
+ });
15377
+ break;
15378
+ }
15379
+ case "provider.thinking_delta": {
15380
+ const text = payload.text;
15381
+ if (!text || text.length === 0) return;
15382
+ const iteration = payload.iteration ?? 0;
15383
+ this._addEntry(subagentId, {
15384
+ id: this._uid(),
15385
+ subagentId,
15386
+ agentName: session.agentName,
15387
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15388
+ kind: "text",
15389
+ content: `\u{1F9E0} ${text}`,
15390
+ iteration
15391
+ });
15392
+ break;
15393
+ }
15394
+ case "tool.started": {
15395
+ const name = payload.name;
15396
+ if (!name) return;
15397
+ this._addEntry(subagentId, {
15398
+ id: this._uid(),
15399
+ subagentId,
15400
+ agentName: session.agentName,
15401
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15402
+ kind: "tool_use",
15403
+ content: `\u{1F527} ${name}()`,
15404
+ iteration: payload.iteration ?? 0,
15405
+ toolName: name
15406
+ });
15407
+ break;
15408
+ }
15409
+ case "tool.executed": {
15410
+ const name = payload.name;
15411
+ const ok = payload.ok;
15412
+ const durationMs = payload.durationMs;
15413
+ if (!name) return;
15414
+ const statusIcon = ok ? "\u2705" : "\u274C";
15415
+ const duration = durationMs !== void 0 ? ` (${durationMs}ms)` : "";
15416
+ this._addEntry(subagentId, {
15417
+ id: this._uid(),
15418
+ subagentId,
15419
+ agentName: session.agentName,
15420
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15421
+ kind: "tool_result",
15422
+ content: `${statusIcon} ${name}${duration}`,
15423
+ iteration: payload.iteration ?? 0,
15424
+ toolName: name,
15425
+ toolOk: ok
15426
+ });
15427
+ break;
15428
+ }
15429
+ case "iteration.completed": {
15430
+ const index = payload.index ?? 0;
15431
+ if (index > 0 && index % 5 === 0) {
15432
+ this._addEntry(subagentId, {
15433
+ id: this._uid(),
15434
+ subagentId,
15435
+ agentName: session.agentName,
15436
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15437
+ kind: "status",
15438
+ content: `\u{1F504} Iteration ${index}`,
15439
+ iteration: index
15440
+ });
15441
+ }
15442
+ break;
15443
+ }
15444
+ }
15445
+ }
15446
+ _addEntry(subagentId, entry) {
15447
+ const session = this._sessions.get(subagentId);
15448
+ if (!session) return;
15449
+ session.transcript.push(entry);
15450
+ if (session.transcript.length > this._maxEntries) {
15451
+ session.transcript.splice(0, session.transcript.length - this._maxEntries);
15452
+ }
15453
+ this._appendToFile(subagentId, entry).catch(() => {
15454
+ });
15455
+ this._events.emit("agent.timeline.message", {
15456
+ subagentId: entry.subagentId,
15457
+ agentName: entry.agentName,
15458
+ content: entry.content,
15459
+ kind: entry.kind === "tool_result" ? "tool_use" : entry.kind === "system" ? "status" : entry.kind,
15460
+ iteration: entry.iteration,
15461
+ ts: entry.ts,
15462
+ toolName: entry.toolName,
15463
+ costUsd: entry.costUsd
15464
+ });
15465
+ this._onEntry?.(entry);
15466
+ }
15467
+ async _appendToFile(subagentId, entry) {
15468
+ const dir = path5.join(this._transcriptsDir, subagentId);
15469
+ await fsp6.mkdir(dir, { recursive: true });
15470
+ const filePath = path5.join(dir, "transcript.jsonl");
15471
+ const line = JSON.stringify(entry) + "\n";
15472
+ await fsp6.appendFile(filePath, line, { encoding: "utf8" });
15473
+ }
15474
+ _uid() {
15475
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
15476
+ }
15477
+ };
15478
+ function createAgentMonitorService(opts) {
15479
+ return new AgentMonitorService(opts);
15480
+ }
14817
15481
 
14818
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
15482
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AgentMonitorService, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createAgentMonitorService, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
14819
15483
  //# sourceMappingURL=index.js.map
14820
15484
  //# sourceMappingURL=index.js.map