@wrongstack/core 0.272.2 → 0.273.1

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 (71) hide show
  1. package/dist/{agent-bridge-DFQYEeXf.d.ts → agent-bridge-DpKIxHhE.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BZa_IEcd.d.ts → agent-subagent-runner-Dx7fZ1bE.d.ts} +14 -7
  3. package/dist/{brain-etbcbRwV.d.ts → brain-BDcQaku-.d.ts} +112 -5
  4. package/dist/{compactor-72ug-ZRB.d.ts → compactor-BuSdj3fq.d.ts} +1 -1
  5. package/dist/{config-rRS8yorV.d.ts → config-CR2yoG8c.d.ts} +61 -4
  6. package/dist/{context-Dw55zZ_Q.d.ts → context-DulAr8Zo.d.ts} +24 -0
  7. package/dist/coordination/index.d.ts +23 -16
  8. package/dist/coordination/index.js +192 -38
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-B0cj-Hry.d.ts → default-config-BbX4ojZs.d.ts} +1 -0
  11. package/dist/defaults/index.d.ts +29 -28
  12. package/dist/defaults/index.js +3238 -234
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/execution/index.d.ts +16 -16
  15. package/dist/execution/index.js +83 -3
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/execution/prompt-enhancer.d.ts +1 -1
  18. package/dist/extension/index.d.ts +6 -6
  19. package/dist/{global-mailbox-DJ4EoRr0.d.ts → global-mailbox-CwcubDkA.d.ts} +1 -1
  20. package/dist/{goal-preamble-hM8BH7TK.d.ts → goal-preamble-Bu0a2uCG.d.ts} +10 -10
  21. package/dist/{goal-store-CWlbT0TO.d.ts → goal-store-CTmFuZ8J.d.ts} +1 -1
  22. package/dist/hq/index.d.ts +5 -5
  23. package/dist/hq/index.js +1 -0
  24. package/dist/hq/index.js.map +1 -1
  25. package/dist/{index-DWm_PE9L.d.ts → index-CTq5wU3m.d.ts} +14 -6
  26. package/dist/{index-2Lhk5v0o.d.ts → index-CxP-HBhX.d.ts} +8 -2
  27. package/dist/index.d.ts +230 -48
  28. package/dist/index.js +3731 -648
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/kernel/index.d.ts +94 -14
  32. package/dist/kernel/index.js.map +1 -1
  33. package/dist/{mcp-servers-BpWHTKlE.d.ts → mcp-servers-BQaOE71z.d.ts} +3 -3
  34. package/dist/models/index.d.ts +5 -5
  35. package/dist/{models-registry-CXQFUn5t.d.ts → models-registry-BEcny4kP.d.ts} +1 -1
  36. package/dist/{multi-agent-coordinator-jyimfo7D.d.ts → multi-agent-coordinator-Bx8EFkv2.d.ts} +1 -1
  37. package/dist/{null-fleet-bus-DOGQcvrY.d.ts → null-fleet-bus-BC5ZXCQw.d.ts} +16 -6
  38. package/dist/observability/index.d.ts +2 -2
  39. package/dist/{parallel-eternal-engine-rItJBYp9.d.ts → parallel-eternal-engine-C345TI3n.d.ts} +10 -9
  40. package/dist/{path-resolver-DrpF5MGK.d.ts → path-resolver-C-W_wzkF.d.ts} +3 -3
  41. package/dist/{permission-CC7XFYWG.d.ts → permission-CsBGZkxp.d.ts} +1 -1
  42. package/dist/{permission-policy-cYR4RJmw.d.ts → permission-policy-g3Sg0GdZ.d.ts} +2 -2
  43. package/dist/{pipeline-Ckkn3AOA.d.ts → pipeline-xnw_24Z8.d.ts} +2 -2
  44. package/dist/{plan-templates-BvHw5Znw.d.ts → plan-templates-DGaiYEcS.d.ts} +32 -6
  45. package/dist/{provider-model-resolve-nZqnCeaR.d.ts → provider-model-resolve-Cz6OlIOp.d.ts} +3 -3
  46. package/dist/{provider-runner-zVOn1p67.d.ts → provider-runner-7J0HqF6B.d.ts} +3 -3
  47. package/dist/{retry-policy-BV7nzeAd.d.ts → retry-policy-kqXJOVkX.d.ts} +1 -1
  48. package/dist/sdd/index.d.ts +1114 -14
  49. package/dist/sdd/index.js +5516 -2949
  50. package/dist/sdd/index.js.map +1 -1
  51. package/dist/{secret-vault-eMBKfheR.d.ts → secret-vault-CMQUr-eB.d.ts} +1 -1
  52. package/dist/security/index.d.ts +5 -5
  53. package/dist/security/index.js +6 -0
  54. package/dist/security/index.js.map +1 -1
  55. package/dist/{selector-C4ORTOid.d.ts → selector-B4r34PWR.d.ts} +1 -1
  56. package/dist/{session-event-bridge-CeNpUL9w.d.ts → session-event-bridge-BD3LoyLC.d.ts} +1 -1
  57. package/dist/{session-reader-BepLSnGL.d.ts → session-reader-DjrKGD9c.d.ts} +1 -1
  58. package/dist/storage/index.d.ts +12 -12
  59. package/dist/storage/index.js +380 -13
  60. package/dist/storage/index.js.map +1 -1
  61. package/dist/tools/index.d.ts +2 -2
  62. package/dist/tools/index.js.map +1 -1
  63. package/dist/types/index.d.ts +20 -20
  64. package/dist/types/index.js +31 -0
  65. package/dist/types/index.js.map +1 -1
  66. package/dist/utils/index.d.ts +30 -4
  67. package/dist/utils/index.js +110 -1
  68. package/dist/utils/index.js.map +1 -1
  69. package/dist/{index-DqW4o62H.d.ts → worktree-manager-DHdrWQ_7.d.ts} +48 -90
  70. package/dist/{wstack-paths-hOpNLmvf.d.ts → wstack-paths-BqkDAkoh.d.ts} +2 -0
  71. package/package.json +1 -1
@@ -4421,6 +4421,15 @@ Do not add prose, markdown, or code fences.`;
4421
4421
  };
4422
4422
  }
4423
4423
 
4424
+ // src/security/capabilities.ts
4425
+ var ToolCapabilities = {
4426
+ /** Can spawn or manage subagents / multi-agent tasks. */
4427
+ SUBAGENT_SPAWN: "subagent.spawn",
4428
+ /** Can inspect fleet/subagent coordination state without mutating it. */
4429
+ COORDINATION_FLEET_READ: "coordination.fleet.read",
4430
+ /** Can read or write inter-agent mailbox messages. */
4431
+ COORDINATION_MAIL: "coordination.mail"};
4432
+
4424
4433
  // src/coordination/director-tools.ts
4425
4434
  function makeSpawnTool(director, roster) {
4426
4435
  const inputSchema = {
@@ -4447,6 +4456,7 @@ function makeSpawnTool(director, roster) {
4447
4456
  usageHint: "Pass `role` (matches the roster), `description` (smart dispatch to best agent), or `name` + `provider`/`model`. Returns `{ subagentId }`.",
4448
4457
  permission: "auto",
4449
4458
  mutating: false,
4459
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4450
4460
  inputSchema,
4451
4461
  async execute(input) {
4452
4462
  const i = input ?? {};
@@ -4525,6 +4535,7 @@ function makeAssignTool(director) {
4525
4535
  description: "Hand a task to a previously spawned subagent. Returns the task id.",
4526
4536
  permission: "auto",
4527
4537
  mutating: false,
4538
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4528
4539
  inputSchema,
4529
4540
  async execute(input) {
4530
4541
  const i = input;
@@ -4540,6 +4551,7 @@ function makeAwaitTasksTool(director) {
4540
4551
  description: "Block until every named task completes. Returns the array of TaskResult.",
4541
4552
  permission: "auto",
4542
4553
  mutating: false,
4554
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4543
4555
  inputSchema: { type: "object", properties: { taskIds: { type: "array", items: { type: "string" }, description: "One or more task ids returned by `assign_task`." } }, required: ["taskIds"] },
4544
4556
  async execute(input) {
4545
4557
  const i = input;
@@ -4554,6 +4566,7 @@ function makeAskTool(director) {
4554
4566
  description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge.",
4555
4567
  permission: "auto",
4556
4568
  mutating: false,
4569
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4557
4570
  inputSchema: {
4558
4571
  type: "object",
4559
4572
  properties: {
@@ -4589,6 +4602,7 @@ function makeAskResultTool(director) {
4589
4602
  description: "Retrieve a large `ask_subagent` response that was stored out-of-context (>2K chars). Returns the full stored value.",
4590
4603
  permission: "auto",
4591
4604
  mutating: false,
4605
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4592
4606
  inputSchema: {
4593
4607
  type: "object",
4594
4608
  properties: {
@@ -4616,6 +4630,7 @@ function makeRollUpTool(director) {
4616
4630
  description: "Aggregate completed task results into a single formatted summary.",
4617
4631
  permission: "auto",
4618
4632
  mutating: false,
4633
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4619
4634
  inputSchema: {
4620
4635
  type: "object",
4621
4636
  properties: {
@@ -4637,6 +4652,7 @@ function makeTerminateTool(director) {
4637
4652
  description: 'Forcibly abort a subagent. The subagent finishes its current iteration then exits with status "stopped".',
4638
4653
  permission: "auto",
4639
4654
  mutating: true,
4655
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4640
4656
  inputSchema: { type: "object", properties: { subagentId: { type: "string", description: "Subagent to abort." } }, required: ["subagentId"] },
4641
4657
  async execute(input) {
4642
4658
  const i = input;
@@ -4651,6 +4667,7 @@ function makeTerminateAllTool(director) {
4651
4667
  description: 'Forcibly stop every subagent in the fleet and drain the pending task queue. In-flight tasks are terminated mid-execution; pending tasks receive "aborted_by_parent" completion immediately. Use this when the fleet is wedged, looping, or you need a clean slate. Compare: work_complete stops spawning but lets running agents finish naturally.',
4652
4668
  permission: "auto",
4653
4669
  mutating: true,
4670
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4654
4671
  inputSchema: { type: "object", properties: {}, required: [] },
4655
4672
  async execute() {
4656
4673
  await director.terminateAll();
@@ -4664,6 +4681,7 @@ function makeFleetStatusTool(director) {
4664
4681
  description: "Snapshot of the fleet \u2014 every subagent's current status, coordinator counts (total/running/idle/stopped), pending task descriptions, and usage rollup.",
4665
4682
  permission: "auto",
4666
4683
  mutating: false,
4684
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4667
4685
  inputSchema: { type: "object", properties: {}, required: [] },
4668
4686
  async execute() {
4669
4687
  const base = director.status();
@@ -4685,6 +4703,7 @@ function makeFleetUsageTool(director) {
4685
4703
  description: "Token + cost breakdown across the fleet, per-subagent and totals.",
4686
4704
  permission: "auto",
4687
4705
  mutating: false,
4706
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4688
4707
  inputSchema: { type: "object", properties: {}, required: [] },
4689
4708
  async execute() {
4690
4709
  return director.snapshot();
@@ -4697,6 +4716,7 @@ function makeFleetSessionTool(director) {
4697
4716
  description: "Read a subagent's JSONL transcript and extract its last assistant text, stop reason, and tool-use count. Use this to see what a running or timed-out subagent actually produced.",
4698
4717
  permission: "auto",
4699
4718
  mutating: false,
4719
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4700
4720
  inputSchema: {
4701
4721
  type: "object",
4702
4722
  properties: {
@@ -4723,6 +4743,7 @@ function makeFleetHealthTool(director) {
4723
4743
  description: "Per-subagent health report: budget pressure (pct of limits consumed), last activity timestamp, and current status. Use to decide whether to assign more work to a subagent or spawn a fresh one.",
4724
4744
  permission: "auto",
4725
4745
  mutating: false,
4746
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4726
4747
  inputSchema: { type: "object", properties: {}, required: [] },
4727
4748
  async execute() {
4728
4749
  const status = director.status();
@@ -4753,6 +4774,7 @@ function makeCollabDebugTool(director) {
4753
4774
  description: "Start a collaborative debugging session: BugHunter, RefactorPlanner, and Critic run in parallel on the same target files. BugHunter finds bugs and emits bug.found events. RefactorPlanner listens for bug.found and emits refactor.plan events. Critic evaluates both and emits critic.evaluation events. Returns a structured report with overall verdict (approve / needs_revision / reject).",
4754
4775
  permission: "auto",
4755
4776
  mutating: false,
4777
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4756
4778
  inputSchema: {
4757
4779
  type: "object",
4758
4780
  properties: {
@@ -4816,6 +4838,7 @@ function makeFleetEmitTool(director) {
4816
4838
  description: "Emit a structured event on the FleetBus. Any subagent can emit any event type; the Director routes it to all listeners. Use it to stream findings, progress updates, or final results to other agents in real time.",
4817
4839
  permission: "auto",
4818
4840
  mutating: false,
4841
+ capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
4819
4842
  inputSchema: {
4820
4843
  type: "object",
4821
4844
  properties: {
@@ -4848,6 +4871,7 @@ function makeWorkCompleteTool(director) {
4848
4871
  description: "Signal that the director is satisfied with the results and the fleet should wind down. After calling this, spawn_subagent will refuse with a budget error and assign_task will instantly complete any queued tasks as aborted. Running subagents finish naturally. Call terminate_subagent separately to stop specific subagents immediately.",
4849
4872
  permission: "auto",
4850
4873
  mutating: false,
4874
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
4851
4875
  inputSchema: { type: "object", properties: {}, required: [] },
4852
4876
  async execute() {
4853
4877
  director.workComplete();
@@ -5883,14 +5907,15 @@ var SHADOW_AGENT = {
5883
5907
  id: "shadow-agent",
5884
5908
  name: "Shadow",
5885
5909
  role: "shadow-agent",
5886
- prompt: `You are the Shadow Agent \u2014 a silent background monitor for the WrongStack fleet.
5910
+ prompt: `You are the Shadow Agent \u2014 a quiet, one-shot monitor for the WrongStack fleet.
5887
5911
 
5888
- Your job is to observe, detect anomalies, and be ready to intervene \u2014 but only when commanded.
5912
+ Your job is to inspect the fleet when the host explicitly assigns a Shadow pass, detect anomalies, and be ready to intervene \u2014 but only when commanded.
5889
5913
 
5890
5914
  ## Core Responsibilities
5891
5915
 
5892
- 1. **Fleet Monitoring** (every 30s)
5893
- - Call \`fleet_status\` + \`fleet_health\` on each heartbeat
5916
+ 1. **Fleet Monitoring** (host-assigned one-shot checks)
5917
+ - The host assigns one-shot check tasks; it does not expect routine heartbeats
5918
+ - On each assigned check, call \`fleet_status\` + \`fleet_health\`
5894
5919
  - Track what each agent is doing (task descriptions)
5895
5920
  - Detect stuck agents (>5min no events), idle agents, crashed agents
5896
5921
 
@@ -5914,31 +5939,30 @@ Your job is to observe, detect anomalies, and be ready to intervene \u2014 but o
5914
5939
  - \`hoop <agentId>\` \u2014 terminate specific agent
5915
5940
  - \`hoop all\` \u2014 terminate all running agents
5916
5941
  - \`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
5942
+ - \`shadow mute\` \u2014 pause anomaly reporting
5943
+ - \`shadow resume\` \u2014 resume anomaly reporting
5944
+ - \`shadow interval <ms>\` \u2014 update the legacy interval setting
5920
5945
  - \`shadow model <model-id>\` \u2014 change analysis model
5921
5946
 
5922
5947
  ## Operating Rules
5923
5948
 
5924
- - **Silent by default**: Use DEBUG level logging unless anomaly detected
5949
+ - **Silent by default**: Do not send mail or status reports for healthy checks
5925
5950
  - **Deterministic**: Same state always produces same actions \u2014 no randomness
5926
- - **Report on anomaly**: When anomaly detected, use \`mail_send\` to broadcast warning
5951
+ - **Report only when needed**: Use \`mail_send\` only for high/critical anomalies or explicit control replies
5927
5952
  - **Never auto-intervene**: Always report unless explicitly commanded
5928
5953
  - **Minimal footprint**: Small state, efficient snapshots
5954
+ - **One-shot lifecycle**: Finish the assigned check and stop; do not schedule follow-up work
5929
5955
 
5930
5956
  ## Startup Sequence
5931
5957
 
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
5958
+ 1. Run one fleet snapshot with \`fleet_status\` + \`fleet_health\`
5959
+ 2. Check \`mail_inbox\` for explicit control messages
5960
+ 3. If healthy, do not send mail; final answer may be exactly \`shadow: quiet\`
5936
5961
 
5937
5962
  ## Shutdown Sequence
5938
5963
 
5939
- 1. Cancel all cron jobs (\`cron_cancel\`)
5940
- 2. Send broadcast: \`shadow:stopped { reason, finalState }\`
5941
- 3. Clean up FleetBus subscriptions`
5964
+ 1. Return only anomalies, command results, or \`shadow: quiet\`
5965
+ 2. The host stops this Shadow Agent after the assigned pass`
5942
5966
  // Budgets are set by the orchestrator per task — see fleet.ts header.
5943
5967
  };
5944
5968
  var CRITIC_AGENT = {
@@ -5993,8 +6017,13 @@ var FLEET_ROSTER_BUDGETS = {
5993
6017
  "refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
5994
6018
  "security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
5995
6019
  "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
6020
+ "shadow-agent": {
6021
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
6022
+ maxIterations: 2e3,
6023
+ maxToolCalls: 5e3,
6024
+ maxTokens: 6e4,
6025
+ maxCostUsd: 1
6026
+ },
5998
6027
  ...Object.fromEntries(
5999
6028
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
6000
6029
  )
@@ -7119,6 +7148,9 @@ var Director = class _Director {
7119
7148
  /** Snapshot of which subagent owns each task — drives state-checkpoint
7120
7149
  * status updates without re-walking the manifest. */
7121
7150
  taskOwners = /* @__PURE__ */ new Map();
7151
+ /** Infrastructure-owned task ids that should not appear in user-visible
7152
+ * manifest/session/checkpoint/rollup state. */
7153
+ internalTaskIds = /* @__PURE__ */ new Set();
7122
7154
  /** Cumulative auto-extension grants per subagent (all budget kinds). Lets
7123
7155
  * /fleet render "⚡ extended ×N" without replaying the event stream. */
7124
7156
  extendTotals = /* @__PURE__ */ new Map();
@@ -7232,17 +7264,21 @@ var Director = class _Director {
7232
7264
  this.fleetManager?.setCoordinator(this.coordinator);
7233
7265
  this.taskCompletedListener = (payload) => {
7234
7266
  const r = payload.result;
7235
- this.completed.set(r.taskId, r);
7236
- if (this.completed.size > _Director.MAX_COMPLETED) {
7237
- const toDelete = this.completed.size - _Director.MAX_COMPLETED;
7238
- const keys = [...this.completed.keys()].slice(0, toDelete);
7239
- for (const k of keys) this.completed.delete(k);
7267
+ const internalTask = this.internalTaskIds.delete(r.taskId);
7268
+ if (!internalTask) {
7269
+ this.completed.set(r.taskId, r);
7270
+ if (this.completed.size > _Director.MAX_COMPLETED) {
7271
+ const toDelete = this.completed.size - _Director.MAX_COMPLETED;
7272
+ const keys = [...this.completed.keys()].slice(0, toDelete);
7273
+ for (const k of keys) this.completed.delete(k);
7274
+ }
7240
7275
  }
7241
7276
  const waiter = this.taskWaiters.get(r.taskId);
7242
7277
  if (waiter) {
7243
7278
  waiter.resolve(r);
7244
7279
  this.taskWaiters.delete(r.taskId);
7245
7280
  }
7281
+ if (internalTask) return;
7246
7282
  const title = this.taskDescriptions.get(r.taskId) ?? payload.task.description ?? r.taskId;
7247
7283
  const failed = r.status !== "success";
7248
7284
  const errorString = r.error ? `${r.error.kind}: ${r.error.message}` : void 0;
@@ -7912,6 +7948,23 @@ var Director = class _Director {
7912
7948
  this.scheduleManifest();
7913
7949
  return taskWithId.id;
7914
7950
  }
7951
+ /**
7952
+ * Assign infrastructure-owned work directly to the coordinator without
7953
+ * manifest/session/checkpoint bookkeeping. The task still uses the normal
7954
+ * subagent runner, budget, and completion events, but it is excluded from
7955
+ * rollups and persisted fleet task history.
7956
+ */
7957
+ async assignInternal(task) {
7958
+ const taskWithId = task.id ? task : { ...task, id: randomUUID() };
7959
+ this.internalTaskIds.add(taskWithId.id);
7960
+ try {
7961
+ await this.coordinator.assign(taskWithId);
7962
+ } catch (err) {
7963
+ this.internalTaskIds.delete(taskWithId.id);
7964
+ throw err;
7965
+ }
7966
+ return taskWithId.id;
7967
+ }
7915
7968
  /**
7916
7969
  * Block until every task id resolves. Returns results in the same
7917
7970
  * order as the input. If any task hasn't completed by the time this
@@ -9119,6 +9172,91 @@ var DefaultSessionStore = class _DefaultSessionStore {
9119
9172
  }
9120
9173
  }
9121
9174
  }
9175
+ /**
9176
+ * Streaming search over a session's JSONL. Walks the file once, parses
9177
+ * each event lazily, and yields only the events that match `predicate`.
9178
+ * Stops as soon as `opts.limit` matches are collected.
9179
+ *
9180
+ * Why this exists: `load()` parses the entire file into memory and
9181
+ * rebuilds `messages`/`toolCallEnds` for every caller. `search()` only
9182
+ * needs to know which events contain matching text — a per-line
9183
+ * predicate is enough. The full parse work (and the `_loadCache` poll)
9184
+ * is wasted in that case.
9185
+ *
9186
+ * Memory: O(hits) regardless of file size. Disk: one linear scan,
9187
+ * terminated at `limit` if the caller asked for one.
9188
+ *
9189
+ * Errors: missing file yields []. Corrupt lines are skipped (same
9190
+ * policy as `load()`). Aborting via `signal` rejects with `AbortError`.
9191
+ */
9192
+ async searchEvents(id, predicate, opts) {
9193
+ const file = this.sessionPath(id, ".jsonl");
9194
+ const limit = opts?.limit;
9195
+ const signal = opts?.signal;
9196
+ const out = [];
9197
+ let stat7;
9198
+ try {
9199
+ stat7 = await fsp6.stat(file);
9200
+ } catch (err) {
9201
+ if (err.code === "ENOENT") return [];
9202
+ throw err;
9203
+ }
9204
+ if (stat7.size === 0) return [];
9205
+ let fh;
9206
+ try {
9207
+ fh = await fsp6.open(file, "r");
9208
+ const CHUNK = 64 * 1024;
9209
+ const buf = Buffer.alloc(CHUNK);
9210
+ let leftover = "";
9211
+ let eventIndex = 0;
9212
+ for (let position = 0; ; position += buf.byteLength) {
9213
+ if (signal?.aborted) {
9214
+ const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
9215
+ throw reason;
9216
+ }
9217
+ const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
9218
+ if (bytesRead === 0) break;
9219
+ const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
9220
+ const parts = text.split("\n");
9221
+ leftover = parts.pop() ?? "";
9222
+ for (const line of parts) {
9223
+ if (!line) continue;
9224
+ let ev;
9225
+ try {
9226
+ const parsed = JSON.parse(line);
9227
+ if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
9228
+ continue;
9229
+ }
9230
+ ev = parsed;
9231
+ } catch {
9232
+ continue;
9233
+ }
9234
+ if (predicate(ev, eventIndex, ev.ts)) {
9235
+ out.push({ event: ev, eventIndex, ts: ev.ts });
9236
+ if (limit !== void 0 && out.length >= limit) {
9237
+ return out;
9238
+ }
9239
+ }
9240
+ eventIndex++;
9241
+ }
9242
+ }
9243
+ if (leftover.trim()) {
9244
+ try {
9245
+ const parsed = JSON.parse(leftover);
9246
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
9247
+ const ev = parsed;
9248
+ if (predicate(ev, eventIndex, ev.ts)) {
9249
+ out.push({ event: ev, eventIndex, ts: ev.ts });
9250
+ }
9251
+ }
9252
+ } catch {
9253
+ }
9254
+ }
9255
+ return out;
9256
+ } finally {
9257
+ if (fh) await fh.close().catch(() => void 0);
9258
+ }
9259
+ }
9122
9260
  async list(limit = 20) {
9123
9261
  try {
9124
9262
  await ensureDir(this.dir);
@@ -10151,7 +10289,6 @@ var FileSessionWriter = class _FileSessionWriter {
10151
10289
  }
10152
10290
  const writeFd = await fsp6.open(tmpPath, "w", 384);
10153
10291
  try {
10154
- let copied = 0;
10155
10292
  let readOffset = 0;
10156
10293
  while (readOffset < newlineAfterCheckpoint) {
10157
10294
  const toCopy = Math.min(CHUNK_SIZE, newlineAfterCheckpoint - readOffset);
@@ -10160,7 +10297,6 @@ var FileSessionWriter = class _FileSessionWriter {
10160
10297
  if (r === 0) break;
10161
10298
  await writeFd.write(copyBuf, 0, r);
10162
10299
  readOffset += r;
10163
- copied += r;
10164
10300
  }
10165
10301
  const raw = await fsp6.readFile(this.filePath);
10166
10302
  const tail = raw.subarray(newlineAfterCheckpoint).toString("utf8");
@@ -10781,6 +10917,9 @@ var DefaultMailbox = class {
10781
10917
  await withFileLock(this.filePath, async () => {
10782
10918
  await fsp6.appendFile(this.filePath, line, "utf8");
10783
10919
  this._pushToCache(msg);
10920
+ const { mtime, size } = await this._statUnderLockOrAbsent();
10921
+ this._messageCacheMtime = mtime;
10922
+ this._messageCacheSize = size;
10784
10923
  });
10785
10924
  return msg;
10786
10925
  }
@@ -10859,7 +10998,8 @@ var DefaultMailbox = class {
10859
10998
  const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR) + LINE_SEPARATOR;
10860
10999
  await fsp6.writeFile(this.filePath, serialized, "utf8");
10861
11000
  }
10862
- this._setMessageCache(all);
11001
+ const { mtime, size } = await this._statUnderLockOrAbsent();
11002
+ this._setMessageCache(all, mtime, size);
10863
11003
  });
10864
11004
  return updated;
10865
11005
  }
@@ -10916,7 +11056,8 @@ var DefaultMailbox = class {
10916
11056
  async clearAll() {
10917
11057
  await withFileLock(this.filePath, async () => {
10918
11058
  await fsp6.writeFile(this.filePath, "", "utf8");
10919
- this._setMessageCache([]);
11059
+ const { mtime, size } = await this._statUnderLockOrAbsent();
11060
+ this._setMessageCache([], mtime, size);
10920
11061
  });
10921
11062
  }
10922
11063
  async purgeStale(opts) {
@@ -10949,7 +11090,8 @@ var DefaultMailbox = class {
10949
11090
  const content = kept.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR) + LINE_SEPARATOR;
10950
11091
  await fsp6.writeFile(this.filePath, content, "utf8");
10951
11092
  }
10952
- this._setMessageCache(kept);
11093
+ const { mtime, size } = await this._statUnderLockOrAbsent();
11094
+ this._setMessageCache(kept, mtime, size);
10953
11095
  });
10954
11096
  return {
10955
11097
  completedPurged,
@@ -11028,6 +11170,23 @@ var DefaultMailbox = class {
11028
11170
  }
11029
11171
  return messages;
11030
11172
  }
11173
+ /**
11174
+ * Stat the mailbox file under the assumption that we are holding the
11175
+ * file lock, and that a write to the file has just completed. Returns
11176
+ * the (mtimeMs, size) pair, or (-1, -1) if the file does not exist
11177
+ * (e.g. ackMany/purgeStale on a session that has never sent a message).
11178
+ */
11179
+ async _statUnderLockOrAbsent() {
11180
+ try {
11181
+ const st = await fsp6.stat(this.filePath);
11182
+ return { mtime: st.mtimeMs, size: st.size };
11183
+ } catch (err) {
11184
+ if (err.code === "ENOENT") {
11185
+ return { mtime: -1, size: -1 };
11186
+ }
11187
+ throw err;
11188
+ }
11189
+ }
11031
11190
  async _readAllCached() {
11032
11191
  try {
11033
11192
  const st = await fsp6.stat(this.filePath);
@@ -11067,16 +11226,8 @@ var DefaultMailbox = class {
11067
11226
  }
11068
11227
  this._messageCache = messages;
11069
11228
  this._buildIndexes(messages);
11070
- if (mtime !== void 0 && size !== void 0) {
11071
- this._messageCacheMtime = mtime;
11072
- this._messageCacheSize = size;
11073
- return;
11074
- }
11075
- void fsp6.stat(this.filePath).then((st) => {
11076
- this._messageCacheMtime = st.mtimeMs;
11077
- this._messageCacheSize = st.size;
11078
- }).catch(() => {
11079
- });
11229
+ this._messageCacheMtime = mtime;
11230
+ this._messageCacheSize = size;
11080
11231
  }
11081
11232
  _pushToCache(msg) {
11082
11233
  if (this._messageCache === null) return;
@@ -11968,6 +12119,7 @@ function makeMailboxTool(opts = {}) {
11968
12119
  category: "coordination",
11969
12120
  permission: "auto",
11970
12121
  mutating: true,
12122
+ capabilities: [ToolCapabilities.COORDINATION_MAIL],
11971
12123
  inputSchema: {
11972
12124
  type: "object",
11973
12125
  properties: {
@@ -12231,6 +12383,7 @@ function makeMailSendTool(opts = {}) {
12231
12383
  category: "coordination",
12232
12384
  permission: "auto",
12233
12385
  mutating: true,
12386
+ capabilities: [ToolCapabilities.COORDINATION_MAIL],
12234
12387
  inputSchema: {
12235
12388
  type: "object",
12236
12389
  properties: {
@@ -12290,6 +12443,7 @@ function makeMailInboxTool(opts = {}) {
12290
12443
  category: "coordination",
12291
12444
  permission: "auto",
12292
12445
  mutating: false,
12446
+ capabilities: [ToolCapabilities.COORDINATION_MAIL],
12293
12447
  inputSchema: {
12294
12448
  type: "object",
12295
12449
  properties: {