@wrongstack/core 0.306.4 → 0.307.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 (113) hide show
  1. package/dist/chronicle/index.js +957 -953
  2. package/dist/chronicle/metrics-ingest.d.ts +55 -0
  3. package/dist/chronicle/metrics-schema.d.ts +86 -0
  4. package/dist/chronicle/metrics-store.d.ts +5 -120
  5. package/dist/chronicle/project-server.js +1083 -1084
  6. package/dist/chronicle/sqlite-journal-quota.d.ts +39 -0
  7. package/dist/chronicle/sqlite-journal-schema.d.ts +25 -0
  8. package/dist/chronicle/sqlite-journal.d.ts +12 -177
  9. package/dist/coordination/agents/index.js +1803 -1701
  10. package/dist/coordination/agents/project-agent-capture.d.ts +48 -0
  11. package/dist/coordination/agents/project-agent-identity.d.ts +3 -129
  12. package/dist/coordination/agents/project-agent-roster.d.ts +17 -0
  13. package/dist/coordination/agents/types.d.ts +6 -9
  14. package/dist/coordination/index.js +6051 -5880
  15. package/dist/coordination/mail-tools.d.ts +1 -1
  16. package/dist/coordination/mailbox-http-actor-query.d.ts +16 -0
  17. package/dist/coordination/mailbox-http-router.d.ts +0 -48
  18. package/dist/coordination/mailbox-http-sse.d.ts +12 -0
  19. package/dist/coordination/mailbox-message-types.d.ts +71 -0
  20. package/dist/coordination/mailbox-predicates.d.ts +13 -0
  21. package/dist/coordination/mailbox-project-server.js +1 -1
  22. package/dist/coordination/mailbox-session-sync.d.ts +8 -0
  23. package/dist/coordination/mailbox-types.d.ts +6 -793
  24. package/dist/core/agent-loop-context.d.ts +28 -0
  25. package/dist/core/agent-loop-detector.d.ts +25 -0
  26. package/dist/core/fallback-doctor.d.ts +72 -0
  27. package/dist/core/index.d.ts +1 -0
  28. package/dist/core/index.js +1859 -1335
  29. package/dist/defaults/index.js +3647 -3559
  30. package/dist/execution/autonomy-brain-llm.d.ts +55 -0
  31. package/dist/execution/autonomy-brain.d.ts +3 -156
  32. package/dist/execution/compaction-budget.d.ts +38 -0
  33. package/dist/execution/compaction-core.d.ts +3 -170
  34. package/dist/execution/compaction-elision.d.ts +58 -0
  35. package/dist/execution/council-orchestrator-helpers.d.ts +39 -0
  36. package/dist/execution/council-orchestrator.d.ts +3 -42
  37. package/dist/execution/council-response-parser.d.ts +56 -0
  38. package/dist/execution/index.js +2475 -2449
  39. package/dist/execution/prompt-enhancer.js +11 -3
  40. package/dist/execution/tool-executor-guard.d.ts +18 -0
  41. package/dist/execution/tool-executor-runner.d.ts +12 -0
  42. package/dist/execution/tool-executor.d.ts +0 -75
  43. package/dist/goal/index.js +19 -6
  44. package/dist/hq/index.js +8 -2
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +8021 -7405
  47. package/dist/infrastructure/index.js +3 -1
  48. package/dist/kernel/events/agent-events.d.ts +2 -0
  49. package/dist/kernel/events/provider-events.d.ts +13 -0
  50. package/dist/plugin/index.js +2590 -2561
  51. package/dist/plugins/auto-review-config.d.ts +53 -0
  52. package/dist/plugins/auto-review-git.d.ts +19 -0
  53. package/dist/plugins/auto-review-plugin.d.ts +2 -140
  54. package/dist/plugins/review-finding-verification.d.ts +7 -0
  55. package/dist/prompts/index.d.ts +1 -0
  56. package/dist/prompts/index.js +601 -0
  57. package/dist/prompts/prompt-journal.d.ts +103 -0
  58. package/dist/security/index.js +627 -652
  59. package/dist/security/permission-explain.d.ts +19 -0
  60. package/dist/security/permission-policy.d.ts +0 -101
  61. package/dist/security/yolo-risk.d.ts +5 -4
  62. package/dist/session-catalog/index.js +220 -212
  63. package/dist/session-catalog/project-server.js +227 -219
  64. package/dist/session-catalog/store-rebuild.d.ts +9 -0
  65. package/dist/session-catalog/store-schema.d.ts +48 -0
  66. package/dist/session-catalog/store.d.ts +0 -17
  67. package/dist/storage/director-state.d.ts +6 -0
  68. package/dist/storage/file-session-writer.d.ts +2 -58
  69. package/dist/storage/index.d.ts +2 -1
  70. package/dist/storage/index.js +2057 -1925
  71. package/dist/storage/orphan-lock-cleaner.d.ts +15 -0
  72. package/dist/storage/queue-store.d.ts +6 -0
  73. package/dist/storage/session-recovery.d.ts +9 -0
  74. package/dist/storage/session-store/rename-session.d.ts +16 -0
  75. package/dist/storage/session-store/resume-session.d.ts +26 -0
  76. package/dist/storage/session-store/session-store-clear.d.ts +11 -0
  77. package/dist/storage/session-store/session-store-index.d.ts +13 -0
  78. package/dist/storage/session-store.d.ts +1 -129
  79. package/dist/storage/session-summary-tracker.d.ts +39 -0
  80. package/dist/storage/session-write-buffer.d.ts +43 -0
  81. package/dist/storage/session-writer/session-writer-flush.d.ts +4 -0
  82. package/dist/storage/session-writer/session-writer-summary-tracker.d.ts +28 -0
  83. package/dist/tools/fallback-agent-model-assign-tool.d.ts +18 -0
  84. package/dist/tools/fallback-leader-model-set-tool.d.ts +18 -0
  85. package/dist/tools/fallback-manage-tools.d.ts +8 -45
  86. package/dist/tools/fallback-profile-manage-tool.d.ts +16 -0
  87. package/dist/tools/fallback-provider-key-set-tool.d.ts +17 -0
  88. package/dist/tools/fallback-provider-manage-tool.d.ts +26 -0
  89. package/dist/tools/index.js +5124 -5071
  90. package/dist/types/config/root.d.ts +7 -2
  91. package/dist/types/default-config.d.ts +1 -1
  92. package/dist/types/index.d.ts +54 -55
  93. package/dist/types/index.js +953 -932
  94. package/dist/types/runtime-capability-manifest.d.ts +5 -5
  95. package/dist/types/session-markers.d.ts +12 -0
  96. package/dist/types/session-markers.js +19 -0
  97. package/dist/worktree/worktree-manager.d.ts +1 -1
  98. package/instructions/agents/architect.md +2 -0
  99. package/instructions/agents/code-reviewer.md +1 -0
  100. package/instructions/agents/debugger.md +2 -0
  101. package/instructions/agents/executor.md +2 -0
  102. package/instructions/agents/explore.md +3 -1
  103. package/instructions/agents/refactor.md +2 -0
  104. package/instructions/agents/reviewer.md +1 -0
  105. package/instructions/coordination/subagent-baseline.md +68 -7
  106. package/instructions/llm/prompt-enhancer.md +2 -1
  107. package/instructions/sections/tool/common-patterns.md +30 -0
  108. package/instructions/sections/tool/delegation-compact.md +3 -1
  109. package/instructions/sections/tool/delegation-full.md +5 -1
  110. package/instructions/system-lite.md +32 -4
  111. package/instructions/system-pro.md +63 -9
  112. package/instructions/system.md +56 -11
  113. package/package.json +9 -5
@@ -1,625 +1,91 @@
1
- /**
2
- * Mailbox persistent inter-agent messaging system with cross-session support.
3
- *
4
- * Agents can leave notes for specific agents or broadcast to all. Each agent
5
- * periodically checks the mailbox or retrieves messages via tool calls.
6
- *
7
- * ## Cross-session communication
8
- *
9
- * The mailbox is stored at **project level** (`~/.wrongstack/projects/<slug>/_mailbox.sqlite`, owned by one detached
10
- * project server and reached over IPC),
11
- * so agents in different terminal sessions / WebUI tabs working on the same
12
- * canonical project can communicate live, even when they run in different
13
- * processes, clients, branches, or linked Git worktrees.
14
- *
15
- * ## Agent registration
16
- *
17
- * Every agent that uses the mailbox registers itself with a heartbeat.
18
- * Other agents can discover online agents via `getOnlineAgents()`.
19
- * Stale agents (no heartbeat > 60s) are pruned automatically.
20
- *
21
- * ## Read receipts
22
- *
23
- * Each message tracks per-recipient read status via a `readBy` map:
24
- * `{ "agentId": "ISO8601" }`. When agent X reads a message, its entry
25
- * is added. The WebUI shows who read what and when.
26
- *
27
- * @module mailbox-types
28
- */
29
- import { type MailboxMessageType } from './mailbox-type-properties.js';
1
+ import type { MailboxMessageType } from './mailbox-type-properties.js';
2
+ import type { MailboxAudience, MailboxMessage, MailboxSessionAffinity, MailboxTaskContext } from './mailbox-message-types.js';
30
3
  export { MAILBOX_TYPE_PROPERTIES, type MailboxMessageType, type MailboxTypeCategory, } from './mailbox-type-properties.js';
31
- /**
32
- * The ten mail types each carry a distinct **semantic category**, a **sender
33
- * contract** (when the sender must use it), and a **recipient contract** (how
34
- * the runtime dispatches it and what the recipient agent must do).
35
- *
36
- * ───── Type semantics — decision matrix for senders ──────────────────────
37
- *
38
- * ## Categories
39
- *
40
- * | Category | Types | Purpose |
41
- * |--------------|---------------------------------------|--------------------------------|
42
- * | Actionable | `ask`, `assign`, `steer`, `review` | Require a substantive response |
43
- * | Informational| `note`, `btw`, `result`, `status` | Consume for context, no action |
44
- * | Routing | `broadcast` | Multi-recipient envelope |
45
- * | Control | `control` | Out-of-band signal (no render) |
46
- *
47
- * ## Per-type contract
48
- *
49
- * ### Actionable types
50
- *
51
- * | Type | When to send | Recipient must |
52
- * |----------|-----------------------------------------------------|----------------------------------------------------|
53
- * | `ask` | Blocking question — you need an answer to proceed | Answer as soon as possible; the sender is waiting. |
54
- * | `assign` | Delegating a task | Accept or decline; act on it when current op allows. |
55
- * | `steer` | Mid-task direction change — the recipient is | Pause current approach, adjust per instruction, |
56
- * | | already working on something and you need them | then resume. Rendered first in the mailbox block. |
57
- * | | to change course NOW | |
58
- * | `review` | Requesting a code/doc/PR review (passive) | Inspect when convenient; no immediate reply needed.|
59
- *
60
- * ### Informational types
61
- *
62
- * | Type | When to send | Recipient must |
63
- * |----------|-----------------------------------------------------|----------------------------------------------------|
64
- * | `note` | General-purpose FYI — a message that isn't any | Read for context; no reply needed. The untyped |
65
- * | | of the more specific types | default for directed messages. |
66
- * | `btw` | Low-priority aside — "by the way" | Absorb the information and stay on current task; |
67
- * | | | no reply needed. Injected via BTW block (separate |
68
- * | | | from the main mailbox fold) to minimise disruption.|
69
- * | `result` | Subagent/task completion notice — share the | Factor into next decision; treat as evidence, not |
70
- * | | outcome of finished work | a new task. |
71
- * | `status` | Agent or system status update (heartbeat, spawn, | Use to avoid redundant work; never act on it as |
72
- * | | task progress, error). Machine-generated. | a task or question. |
73
- * | `broadcast` | Multi-recipient envelope — the same message for | Read if addressed to you (direct, alias, session, |
74
- * | | every agent on the project. Auto-selected when | or `*`). The `*` recipient means "everyone". |
75
- * | | `to` is `"*"` or `"@session"` in `mail_send`. | |
76
- *
77
- * ### Control type
78
- *
79
- * | Type | When to send | Recipient must |
80
- * |----------|-----------------------------------------------------|----------------------------------------------------|
81
- * | `control`| Out-of-band signal (interrupt, halt, redirect). | NEVER folded into conversation content. The agent |
82
- * | | Machine-generated by the runtime, not by agents. | loop intercepts it separately. `control:interrupt` |
83
- * | | | causes a cooperative halt at the next iteration |
84
- * | | | boundary. |
85
- *
86
- * ───── Dispatch behavior (runtime contract) ──────────────────────────────
87
- *
88
- * The mailbox system enforces these dispatch rules:
89
- *
90
- * 1. **Send-side**: `mail_send` auto-defaults the type: `broadcast` when
91
- * `to` is `"*"` or `"@session:..."`, otherwise `note`.
92
- * 2. **Send-side validation**: `assign` always requires a specific `to`
93
- * (not `"*"`). `control` is reserved for runtime use — agents passing it
94
- * via the tool surface will be rejected.
95
- * 3. **Render-order guarantee**: `steer` messages are ALWAYS rendered first
96
- * in `buildMailboxBlock()`, before any other type, to ensure mid-task
97
- * direction changes are seen before other action items.
98
- * 4. **Control isolation**: `control`-type messages are filtered by
99
- * `injectPendingMailboxMessages()` and NEVER enter the folded
100
- * conversation block — they are out-of-band signals only.
101
- * 5. **Background routing**: in `background` delivery mode, only the
102
- * actionable message types (`steer`, `ask`, `assign`, `result`,
103
- * `review`) are escalated; `note`, `btw`, `status`, and `broadcast`
104
- * are suppressed to minimise disruption during tool work.
105
- * 6. **Awareness polling**: `btw` messages intercepted by background
106
- * polling are queued via `setBtwNote()` for injection at a safe loop
107
- * boundary, not folded inline.
108
- * 7. **Agent registry**: `getAgentStatuses()` reads the dedicated `agents`
109
- * table, not mailbox message content. That table is populated by
110
- * `registerAgent` / `heartbeat` — never by `status`-type messages, which
111
- * are ordinary mail and carry no presence meaning. There is no fallback
112
- * that derives presence from message content: the registry is either the
113
- * owner's table or nothing. (It was `_mailbox.registry.json` before the
114
- * SQLite cutover; both that file and the derive-from-messages fallback are
115
- * gone.)
116
- * 8. **Request-scoped context**: delivered raw mailbox blocks are removed
117
- * after one successful provider evaluation. Durable assistant/tool/task
118
- * consequences remain; routine mail does not occupy later requests.
119
- *
120
- * When a type is missing from any dispatch table, the fallback is:
121
- * - Render with `📨 <TYPE>` label (generic emoji prefix)
122
- * - Route inline (not background)
123
- * - No special instruction added
124
- */
125
- /**
126
- * Which class of agent may consume a mailbox message.
127
- *
128
- * `leaders` is a delivery boundary, not merely a UI hint: agent-loop and
129
- * inbox readers must exclude these messages for subagents. The optional
130
- * persisted field keeps older JSONL records backwards-compatible (`all`).
131
- */
132
- export type MailboxAudience = 'all' | 'leaders';
133
- /** Return the stable base portion of a session-qualified mailbox identity. */
134
- export declare function mailboxIdentityBase(agentId: string): string;
135
- /** Whether a mailbox identity belongs to the session's main/leader agent. */
136
- export declare function isMailboxLeader(agentId: string, role?: string): boolean;
137
- /**
138
- * Whether a sender identity belongs to a named agent family.
139
- *
140
- * Matches the family id exactly, or as the `<family>-<suffix>` prefix that a
141
- * spawned worker carries. Both forms occur in practice for the same logical
142
- * sender: a pipeline agent may post under its plain id (`dep-watcher`, which
143
- * `makeDependencyWatcherConfig` sends as), or a subagent may post under the
144
- * name it was spawned with — `host-subagent-factory` sets `ctx.agentId` from
145
- * the spawn name, so the tech-stack worker spawned as
146
- * `tech-stack-package.json` writes mail from `tech-stack-package.json@<tag>`.
147
- * A consumer gating on the plain family id with `===` would silently reject
148
- * its own pipeline.
149
- *
150
- * Same shape as the existing `chimera` / `chimera-*` check in
151
- * `applyMailboxSendPolicy` and `host-subagent-factory`.
152
- *
153
- * Session/process qualifiers are stripped first, so `<family>@<tag>` and
154
- * `<family>#<pid>` match too.
155
- */
156
- export declare function isMailboxSenderInFamily(senderId: string, family: string): boolean;
157
- /** Whether a message may be consumed by the supplied agent identity. */
158
- export declare function isMailboxMessageVisibleTo(message: Pick<MailboxMessage, 'audience'>, agentId: string, role?: string): boolean;
159
- /**
160
- * Shape of the optional secondary lookup the leader filter performs when a
161
- * message arrives without an affinity token (legacy sender / older build).
162
- *
163
- * `resolveChimeraReportSessionId(reportId)` returns the persisted
164
- * `ReviewReport.sessionId` for that report id, or `undefined` when the
165
- * report is unknown. May be sync or async — the filter awaits the result.
166
- */
167
- export interface MailboxSessionAffinityContext {
168
- /**
169
- * Look up the originating session id of a chimera report by its report id.
170
- * Return `undefined` for unknown reports. Sync or async.
171
- */
172
- resolveChimeraReportSessionId?: ((reportId: string) => string | undefined | Promise<string | undefined>) | undefined;
173
- /**
174
- * Legacy / one-shot opt-in. The flag has TWO consultation points inside
175
- * {@link acceptMailboxMessageForSession}:
176
- *
177
- * 1. When the recipient does not pass a `currentSessionId` (rule 2),
178
- * a token-bearing message is accepted iff this is `true`.
179
- * 2. When a token-bearing message lacks a resolvable `sessionId` AND
180
- * the persisted-report resolver returns no match (rule 5), the
181
- * message is accepted iff this is `true`.
182
- *
183
- * Token-LESS messages are NOT gated by this flag — they are accepted
184
- * unconditionally by rule 1 as the discriminator for legitimate
185
- * subagent `result` / `review` traffic that does not stamp an affinity
186
- * token (task-auctioneer, cascade handlers, legacy senders).
187
- *
188
- * This flag does NOT override an explicit mismatch: a message that
189
- * carries `sessionAffinity.sessionId !== currentSessionId` is dropped
190
- * regardless of this flag, because the sender explicitly claimed a
191
- * different session — honoring it would re-open the leak.
192
- *
193
- * Default `false`.
194
- */
195
- allowUnscoped?: boolean | undefined;
196
- }
197
- /**
198
- * Decide whether a mailbox message belongs to the recipient's current session
199
- * and should therefore be delivered to the leader's inbox.
200
- *
201
- * The actual code path is:
202
- *
203
- * 1. {@link isAffectedBySessionAffinity} check. If the message has no
204
- * affinity token, the filter does NOT apply and the message is accepted.
205
- * This is the **discriminator** that protects legitimate subagent
206
- * `result` / `review` traffic (task-auctioneer, cascade handlers) from
207
- * collateral drops — they do not stamp an affinity token, so they fall
208
- * through here. When a token is present, every token is enforced regardless
209
- * of its optional `kind`; malformed or unrecognized kinds must not bypass
210
- * a mismatched `sessionAffinity.sessionId`.
211
- * 2. No-session-id branch. If the recipient did not pass a `currentSessionId`,
212
- * accept only when `ctx.allowUnscoped === true`. Default: **fail-closed**
213
- * (drop) — the safe behavior, not a silent fail-open.
214
- * 3. Explicit `sessionAffinity.sessionId` match. If the sender stamped a
215
- * session id and it does not match the recipient's current session id,
216
- * drop immediately. The sender claimed a different session; honoring it
217
- * would re-open the cross-session leak. This drop happens even when
218
- * `allowUnscoped` is set — a wrong session is misconfiguration, not a
219
- * legacy case.
220
- * 4. Persisted-report fallback. If the token lacks a `sessionId` but carries
221
- * a `reportId`, await `ctx.resolveChimeraReportSessionId(reportId)` and
222
- * accept if the persisted `ReviewReport.sessionId` matches. Accepts
223
- * sync or async resolvers.
224
- * 5. `allowUnscoped` opt-in. If the previous steps did not accept and
225
- * `ctx.allowUnscoped === true`, accept (legacy / one-shot tool
226
- * compatibility).
227
- * 6. Otherwise drop (`false`).
228
- *
229
- * PR #314 guard contract: presence checks intentionally use strict
230
- * `!== undefined` comparisons for both the affinity token and its optional
231
- * fields. Do not simplify them to nullish checks: malformed persisted `null`
232
- * values must remain on the token-bearing path and fail closed in the shape
233
- * guard rather than being treated as absent.
234
- *
235
- * Trust note: the filter accepts the sender-asserted `sessionAffinity.sessionId`
236
- * without store cross-check (rule 3). The persisted-report fallback (rule 4)
237
- * is the only server-side check. A sender that fabricates both an affinity
238
- * token and a persisted report could impersonate another session — guard
239
- * chimera's send path so only the actual originating session can stamp the
240
- * token, and do not relax rule 3.
241
- *
242
- * The helper is side-effect-free except for awaiting the optional resolver.
243
- * It lives next to {@link isMailboxMessageVisibleTo} so mailbox consumers
244
- * can compose both filters without taking a dependency on the persistence
245
- * layer.
246
- */
247
- export declare function acceptMailboxMessageForSession(message: Pick<MailboxMessage, 'type' | 'sessionAffinity'>, currentSessionId: string | undefined, ctx?: MailboxSessionAffinityContext): Promise<boolean>;
248
- /**
249
- * Synchronous wrapper for callers that cannot await the resolver
250
- * (e.g. legacy event handlers). If `ctx.resolveChimeraReportSessionId` is
251
- * async, it is called without awaiting and the result is ignored — the
252
- * rule degrades to "match affinity.sessionId, otherwise drop unless
253
- * allowUnscoped".
254
- *
255
- * Prefer {@link acceptMailboxMessageForSession} whenever the caller can
256
- * await.
257
- */
258
- export declare function acceptMailboxMessageForSessionSync(message: Pick<MailboxMessage, 'type' | 'sessionAffinity'>, currentSessionId: string | undefined, ctx?: MailboxSessionAffinityContext): boolean;
259
- /** Category + expectsReply are provided by MAILBOX_TYPE_PROPERTIES directly. */
260
- /**
261
- * Validate that a given (type, to) pair is internally consistent.
262
- * Throws when the combination breaks a fundamental rule.
263
- */
264
- export declare function validateSendType(type: MailboxMessageType, to: string): void;
265
- /**
266
- * Per-recipient read status. `readBy` maps agentId → ISO8601 timestamp of
267
- * when that agent first read the message. An empty map means unread by all.
268
- */
269
- export interface ReadReceipts {
270
- [agentId: string]: string;
271
- }
272
- export interface MailboxMessage {
273
- /** Unique message id (UUID). */
274
- id: string;
275
- /** Sender agent id. */
276
- from: string;
277
- /** Recipient agent id, or '*' for broadcast. */
278
- to: string;
279
- /** Message category. */
280
- type: MailboxMessageType;
281
- /** Delivery audience. Omitted legacy values mean `all`. */
282
- audience?: MailboxAudience | undefined;
283
- /** Short subject line — one sentence. */
284
- subject: string;
285
- /** Full message content. */
286
- body: string;
287
- /** Priority — high priority messages surface first. */
288
- priority: 'low' | 'normal' | 'high';
289
- /**
290
- * Per-recipient read receipts. agentId → ISO8601 when they first read it.
291
- * Replaces the old single `read: boolean` + `readAt` fields.
292
- */
293
- readBy: ReadReceipts;
294
- /** Has any recipient acted on / completed this? */
295
- completed: boolean;
296
- /** Who completed it (agentId). */
297
- completedBy?: string | undefined;
298
- /** Optional summary of what happened after handling. */
299
- outcome?: string | undefined;
300
- /** ISO8601 — when the message was sent. */
301
- timestamp: string;
302
- /** ISO8601 — when the message was marked complete. */
303
- completedAt?: string | undefined;
304
- /**
305
- * ISO8601 — when the message was soft-deleted. When present, the
306
- * default `Mailbox.query()` filter excludes the message from the
307
- * normal inbox view; {@link Mailbox.restore} clears the
308
- * field to undo the delete. Hard deletes (removing the line from
309
- * the JSONL) are reserved for the CLI and never happen via the
310
- * server route handlers.
311
- */
312
- deletedAt?: string | undefined;
313
- /** When the soft-delete happened, the agentId that issued it. */
314
- deletedBy?: string | undefined;
315
- /** If this is a reply, the id of the parent message. */
316
- replyTo?: string | undefined;
317
- /** For assign-type messages — task context for agent discovery. */
318
- taskContext?: MailboxTaskContext | undefined;
319
- /** Session id of the sender. Enables cross-session communication. */
320
- senderSessionId?: string | undefined;
321
- /**
322
- * Session-affinity token persisted on the message. When set, the
323
- * recipient's leader filter uses it to drop the message if the
324
- * recipient's current session id does not match. See
325
- * {@link MailboxSessionAffinity} for the contract.
326
- */
327
- sessionAffinity?: MailboxSessionAffinity | undefined;
328
- /**
329
- * ISO8601 — when the message expires. Set at send time from `ttlMs`
330
- * (default: 24h via AUTO_COMPACT_DEFAULT_TTL_MS). The auto-compaction
331
- * sweep removes messages whose `expiresAt` is in the past. When
332
- * undefined, the compaction sweep uses the default TTL from the
333
- * caller's options.
334
- */
335
- expiresAt?: string | undefined;
336
- }
337
- export interface MailboxTaskContext {
338
- /** The role that should handle this task (e.g. "tech-stack", "audit-log"). */
339
- agentRole?: string | undefined;
340
- /** Human-readable agent name (e.g. "Tesla (Executor)"). */
341
- agentName?: string | undefined;
342
- /** Task id if already assigned via coordinator. */
343
- taskId?: string | undefined;
344
- /** Current task status. */
345
- status?: 'pending' | 'in_progress' | 'completed' | 'failed' | undefined;
346
- }
4
+ export { expandMailboxCapabilities, hasMailboxCapability, MAILBOX_CAPABILITY_IMPLICATIONS, type MailboxActorContext, type MailboxAuthMode, type MailboxCapability, type MailboxPrincipalKind, } from './mailbox-auth-types.js';
5
+ export { isActionRequiredForActor, isMailboxLeader, isMailboxMessageVisibleTo, isMailboxReceiptRecordV2, isMailboxSenderInFamily, mailboxIdentityBase, normalizeRecipient, SESSION_RECIPIENT_PREFIX, sessionRecipient, validateSendType, } from './mailbox-predicates.js';
6
+ export { acceptMailboxMessageForSession, acceptMailboxMessageForSessionSync, type MailboxSessionAffinityContext, } from './mailbox-session-sync.js';
7
+ export { type ActorMailboxMessage, type MailboxAudience, type MailboxLegacyReportSessionAffinity, type MailboxMessage, type MailboxReceiptRecordV2, type MailboxScopedSessionAffinity, type MailboxSessionAffinity, type MailboxTaskContext, type ReadReceipts, } from './mailbox-message-types.js';
347
8
  export interface RegisteredAgent {
348
- /** Unique agent id. */
349
9
  agentId: string;
350
- /** Session id this agent belongs to. */
351
10
  sessionId: string;
352
- /** Human-readable name. */
353
11
  name: string;
354
- /** Role (e.g. "leader", "tech-stack", "bug-hunter"). */
355
12
  role?: string | undefined;
356
- /** Current status. */
357
13
  status: 'idle' | 'running' | 'streaming' | 'waiting_user' | 'error';
358
- /** Current tool being executed, if any. */
359
14
  currentTool?: string | undefined;
360
- /** Current task description. */
361
15
  currentTask?: string | undefined;
362
- /** Iteration count so far. */
363
16
  iterations: number;
364
- /** Tool calls so far. */
365
17
  toolCalls: number;
366
- /** ISO8601 — registered at. */
367
18
  registeredAt: string;
368
- /** ISO8601 — last heartbeat (updated on every mailbox op). */
369
19
  lastSeenAt: string;
370
- /** Which process registered this agent (PID). */
371
20
  pid: number;
372
- /** Where the agent is running (e.g. "cli", "webui"). */
373
21
  source?: 'cli' | 'webui' | 'mcp' | 'acp' | 'http' | undefined;
374
22
  }
375
23
  export interface MailboxAgentStatus {
376
- /** Agent id. */
377
24
  agentId: string;
378
- /** Human-readable name. */
379
25
  name: string;
380
- /** Role. */
381
26
  role?: string | undefined;
382
- /** Session id. */
383
27
  sessionId: string;
384
- /** Current status. */
385
28
  status: 'idle' | 'running' | 'streaming' | 'waiting_user' | 'error' | 'offline';
386
- /** Current tool being executed, if any. */
387
29
  currentTool?: string | undefined;
388
- /** Current task description. */
389
30
  currentTask?: string | undefined;
390
- /** Iteration count so far. */
391
31
  iterations: number;
392
- /** Tool calls so far. */
393
32
  toolCalls: number;
394
- /** ISO8601 — last activity timestamp. */
395
33
  lastActivityAt: string;
396
- /** ISO8601 — last heartbeat. */
397
34
  lastSeenAt: string;
398
- /** Whether this agent is currently online (heartbeat within threshold). */
399
35
  online: boolean;
400
- /** Which process. */
401
36
  pid: number;
402
- /** Source. */
403
37
  source?: 'cli' | 'webui' | 'mcp' | 'acp' | 'http' | undefined;
404
38
  }
405
39
  export interface MailboxQuery {
406
- /**
407
- * Restrict the result to these message ids.
408
- *
409
- * Exists so a caller that already knows which messages it cares about can
410
- * ask about exactly those instead of pulling a set and scanning it. The
411
- * HTTP bridge's per-actor visibility checks did the latter — answering
412
- * "is this one message mine?" by materializing every message addressed to
413
- * the actor, which on a busy project meant a full table read (plus the
414
- * whole receipt table, plus a retention projection per row) for every
415
- * single `ack`.
416
- *
417
- * An empty array matches nothing. Untrusted request codecs deliberately do
418
- * NOT accept this field — `validateQuery` whitelists, so it stays a
419
- * server-side narrowing rather than something a caller can widen.
420
- */
421
40
  ids?: readonly string[] | undefined;
422
- /** Filter by recipient agent id. */
423
41
  to?: string | undefined;
424
- /** Filter by sender agent id. */
425
42
  from?: string | undefined;
426
- /** Only messages unread by this agent. */
427
43
  unreadBy?: string | undefined;
428
- /** Trusted caller role used with `unreadBy` for audience filtering. */
429
44
  readerRole?: string | undefined;
430
- /** Only incomplete messages. */
431
45
  incompleteOnly?: boolean | undefined;
432
- /**
433
- * Internal trusted-read option: retain folded per-actor receipt state so a
434
- * boundary can derive an actor-safe projection. Untrusted query codecs must
435
- * never accept this field from request payloads.
436
- */
437
46
  includeReceiptState?: boolean | undefined;
438
- /** Filter by message type. */
439
47
  type?: MailboxMessageType | undefined;
440
- /** Filter by priority (>= this level). */
441
48
  minPriority?: 'low' | 'normal' | 'high' | undefined;
442
- /** Maximum number of messages to return. */
443
49
  limit?: number | undefined;
444
- /** ISO8601 — only messages after this timestamp. */
445
50
  since?: string | undefined;
446
- /** Filter by the sender's session id (`MailboxMessage.senderSessionId`). */
447
51
  sessionId?: string | undefined;
448
- /**
449
- * Include soft-deleted messages (where `deletedAt` is set). When
450
- * `false` (the default), soft-deleted messages are filtered out so
451
- * the normal inbox view stays clean. The "trash" view passes
452
- * `true` to surface them.
453
- */
454
52
  includeDeleted?: boolean | undefined;
455
- /**
456
- * Filter by replyTo parent message id (UUID). When set, only messages
457
- * whose `replyTo` exactly matches this value are returned. An empty
458
- * string matches nothing — empty strings are technically allowed by
459
- * `send()` (it passes through `MailboxSendInput.replyTo` directly with
460
- * no normalization), but are never produced by chimera/execution callers.
461
- * The query filter is exact-match.
462
- * Useful for polling the response to a specific `ask` message.
463
- */
464
53
  replyTo?: string | undefined;
465
54
  }
466
- /** Canonical prefix for mail addressed to every agent in one session. */
467
- export declare const SESSION_RECIPIENT_PREFIX = "@session:";
468
- /** Build the canonical recipient address for a session-scoped broadcast. */
469
- export declare function sessionRecipient(sessionId: string): string;
470
- /**
471
- * Normalize a recipient address.
472
- *
473
- * - `"all"` (any casing) is canonicalized to `'*'`.
474
- * - `"@session"` (any casing) is canonicalized to
475
- * `"@session:<sessionId>"`; callers must provide the sender's session id.
476
- * - Already-canonical `"@session:<sessionId>"` addresses are preserved.
477
- */
478
- export declare function normalizeRecipient(to: string, sessionId?: string): string;
479
55
  export interface MailboxSendInput {
480
- /** Sender agent id. */
481
56
  from: string;
482
- /** Recipient agent id, '*' / "all" for project broadcast, or "@session" for the sender's session. */
483
57
  to: string;
484
- /** Message category. */
485
58
  type: MailboxMessageType;
486
- /** Restrict consumption to main/leader agents. Default: `all`. */
487
59
  audience?: MailboxAudience | undefined;
488
- /** Short subject line. */
489
60
  subject: string;
490
- /** Full message content. */
491
61
  body: string;
492
- /** Priority. Default: 'normal'. */
493
62
  priority?: 'low' | 'normal' | 'high' | undefined;
494
- /** If replying, the id of the parent message. */
495
63
  replyTo?: string | undefined;
496
- /** Task context for assign-type messages. */
497
64
  taskContext?: MailboxTaskContext | undefined;
498
- /** Sender session id. Required when `to` is the `"@session"` alias. */
499
65
  senderSessionId?: string | undefined;
500
- /**
501
- * Session-affinity token. When set, the recipient's mailbox filter uses it
502
- * to drop the message if the recipient's current session id does not match.
503
- *
504
- * Why two fields instead of one: a chimera report is generated by a reviewer
505
- * subagent that runs inside the originating session (so `senderSessionId`
506
- * is correct), but a mailbox envelope may also be relayed by an outer
507
- * pipeline (auto-review cascade, broadcast wrapper) where the recipient
508
- * needs to know which session the report *belongs to*, not who *physically
509
- * called send()*. `sessionId` carries the originating session;
510
- * `reportId` carries the chimera report id so the leader's filter
511
- * can also look the report up in the persisted review-report store.
512
- *
513
- * A session-affinity token is **required** for any `chimera.*` review
514
- * delivery wrapper — this is the trust boundary that prevents a leader
515
- * from acting on reports emitted by another session, even when those
516
- * reports land in its mailbox.
517
- */
518
66
  sessionAffinity?: MailboxSessionAffinity | undefined;
519
- /**
520
- * Time-to-live in milliseconds. When set, the message's `expiresAt` is
521
- * computed as `now + ttlMs` at send time. The auto-compaction sweep
522
- * removes expired messages. Default: none (use compaction sweep default).
523
- */
524
67
  ttlMs?: number | undefined;
525
68
  }
526
- /**
527
- * Session-affinity token carried on a mailbox message.
528
- *
529
- * The token is the *only* signal a recipient's leader filter uses to decide
530
- * whether a chimera report belongs to the recipient's own session. Without
531
- * it, any leader that polls the project-wide mailbox would see — and feel
532
- * compelled to act on — chimera reports emitted by every other session in
533
- * the project.
534
- */
535
- export type MailboxSessionAffinity = MailboxScopedSessionAffinity | MailboxLegacyReportSessionAffinity;
536
- export interface MailboxScopedSessionAffinity {
537
- /**
538
- * The originating session id. The recipient's leader filter compares this
539
- * against its own current session id and drops the message on mismatch.
540
- */
541
- sessionId: string;
542
- /**
543
- * Originating chimera report id (UUID). Used as a secondary lookup only
544
- * when the message is a legacy report token without `sessionId`.
545
- */
546
- reportId?: string | undefined;
547
- /** Free-form tag for filtering / metrics, e.g. `'chimera.review'`. */
548
- kind?: string | undefined;
549
- }
550
- export interface MailboxLegacyReportSessionAffinity {
551
- /**
552
- * Legacy report-only tokens deliberately omit `sessionId`; any present
553
- * session id is authoritative and must be checked before report lookup.
554
- */
555
- sessionId?: undefined;
556
- /** Originating chimera report id (UUID) used for persisted-session lookup. */
557
- reportId: string;
558
- /** Free-form tag for filtering / metrics, e.g. `'chimera.review'`. */
559
- kind?: string | undefined;
560
- }
561
- /**
562
- * Append-only ack record stored in the JSONL alongside messages.
563
- *
564
- * Instead of rewriting the entire mailbox file to mark a message as read or
565
- * completed, we append a small ack record. At read time, ack records are
566
- * folded into their target messages. The `__ack` discriminator distinguishes
567
- * ack records from regular messages.
568
- *
569
- * Compaction (autoCompact / purgeStale) folds these into the messages and
570
- * removes the ack lines from the file, keeping the file bounded.
571
- */
572
69
  export interface AckRecord {
573
- /** Discriminator — always `true` to distinguish from MailboxMessage. */
574
70
  __ack: true;
575
- /** The message this ack applies to. */
576
71
  messageId: string;
577
- /** Agent acknowledging the message. */
578
72
  readerId: string;
579
- /** ISO8601 timestamp of the ack. */
580
73
  timestamp: string;
581
- /** Was the message read? */
582
74
  read: boolean;
583
- /** Was the message marked completed? */
584
75
  completed?: boolean | undefined;
585
- /** Who completed it (when completed === true). */
586
76
  completedBy?: string | undefined;
587
- /** Optional outcome summary. */
588
77
  outcome?: string | undefined;
589
- /**
590
- * Soft-delete or restore the target message.
591
- * - `true`: set `deletedAt`/`deletedBy` on the message
592
- * - `false`: clear `deletedAt`/`deletedBy` on the message
593
- * - `undefined`: not a delete/restore operation (backward-compat default)
594
- *
595
- * When `deleted` is `true`, `deletedBy` records who performed the delete.
596
- */
597
78
  deleted?: boolean | undefined;
598
- /** Who deleted the message (set when `deleted === true`). */
599
79
  deletedBy?: string | undefined;
600
80
  }
601
81
  export interface MailboxAckInput {
602
- /** Message id to acknowledge. */
603
82
  messageId: string;
604
- /** Agent id of who is reading/acking. */
605
83
  readerId: string;
606
- /** Mark as read by this agent? Defaults to true if not specified. */
607
84
  read?: boolean | undefined;
608
- /** Mark as completed? */
609
85
  completed?: boolean | undefined;
610
- /** Optional outcome summary. */
611
86
  outcome?: string | undefined;
612
87
  }
613
- /**
614
- * Batch acknowledgment input — applies a batch of acks under a single file
615
- * lock + single file rewrite. Each entry has the same shape as
616
- * {@link MailboxAckInput} minus the per-batch defaults documented on
617
- * `ackMany`. Use this when an agent is acking several fresh messages at
618
- * once (the common case in the mailbox loop) — it collapses N full-file
619
- * rewrites into one.
620
- */
621
88
  export interface MailboxAckBatchInput {
622
- /** Ack entries to apply. */
623
89
  acks: MailboxAckInput[];
624
90
  }
625
91
  export interface AgentRegistrationInput {
@@ -632,35 +98,21 @@ export interface AgentRegistrationInput {
632
98
  }
633
99
  export type ClientSource = 'repl' | 'tui' | 'webui' | 'http';
634
100
  export interface RegisteredClient {
635
- /** Unique client id. */
636
101
  clientId: string;
637
- /** Session/project context id. */
638
102
  sessionId: string;
639
- /** Human-readable name (e.g. "TUI [main]", "WebUI [chrome]"). */
640
103
  name: string;
641
- /** Client type. */
642
104
  source: ClientSource;
643
- /** ISO8601 — registered at. */
644
105
  registeredAt: string;
645
- /** ISO8601 — last heartbeat. */
646
106
  lastSeenAt: string;
647
- /** Which process. */
648
107
  pid: number;
649
108
  }
650
109
  export interface ClientStatus {
651
- /** Client id. */
652
110
  clientId: string;
653
- /** Human-readable name. */
654
111
  name: string;
655
- /** Client type. */
656
112
  source: ClientSource;
657
- /** Session id. */
658
113
  sessionId: string;
659
- /** ISO8601 — last activity timestamp. */
660
114
  lastSeenAt: string;
661
- /** Whether this client is currently online (heartbeat within threshold). */
662
115
  online: boolean;
663
- /** Which process. */
664
116
  pid: number;
665
117
  }
666
118
  export interface ClientRegistrationInput {
@@ -672,7 +124,6 @@ export interface ClientRegistrationInput {
672
124
  }
673
125
  export interface ClientHeartbeatInput {
674
126
  clientId: string;
675
- /** Active session id for this client. When present, updates the registry entry. */
676
127
  sessionId?: string | undefined;
677
128
  }
678
129
  export interface AgentHeartbeatInput {
@@ -684,302 +135,64 @@ export interface AgentHeartbeatInput {
684
135
  toolCalls?: number | undefined;
685
136
  }
686
137
  export interface PurgeOptions {
687
- /**
688
- * Purge completed messages older than this many milliseconds.
689
- * Default: 1 day (86_400_000 ms)
690
- */
691
138
  completedMaxAgeMs?: number | undefined;
692
- /**
693
- * Purge incomplete messages older than this many milliseconds.
694
- * Default: 7 days (604_800_000 ms)
695
- */
696
139
  incompleteMaxAgeMs?: number | undefined;
697
140
  }
698
141
  export interface PurgeResult {
699
- /** Messages removed because they were completed and too old. */
700
142
  completedPurged: number;
701
- /** Messages removed because they were incomplete and too old. */
702
143
  incompletePurged: number;
703
- /** Total messages removed. */
704
144
  totalPurged: number;
705
- /** Messages remaining in the mailbox after purge. */
706
145
  remaining: number;
707
146
  }
708
147
  export interface AutoCompactOptions {
709
- /**
710
- * Remove messages read by ALL currently-online agents that are older
711
- * than this many milliseconds since the last read receipt was stamped.
712
- * Default: 10 minutes (AUTO_COMPACT_READ_MAX_AGE_MS).
713
- */
714
148
  readMaxAgeMs?: number | undefined;
715
- /**
716
- * Default TTL for messages without an explicit `expiresAt`. Messages
717
- * whose `timestamp` is older than `now - defaultTtlMs` are removed.
718
- * Default: 24 hours (AUTO_COMPACT_DEFAULT_TTL_MS).
719
- */
720
149
  defaultTtlMs?: number | undefined;
721
- /**
722
- * Per-message-type TTL overrides, consulted before `defaultTtlMs` for
723
- * messages with no explicit `expiresAt`. Keyed by `MailboxMessageType`.
724
- * Default: {@link AUTO_COMPACT_TYPE_TTL_MS} (transient `status` chatter
725
- * expires in 30 minutes instead of 24 hours).
726
- */
727
150
  typeTtlMs?: Readonly<Record<string, number>> | undefined;
728
- /**
729
- * Also run `purgeStale` logic in the same pass — purge completed
730
- * messages older than this many ms. Default: 1 day.
731
- */
732
151
  completedMaxAgeMs?: number | undefined;
733
- /**
734
- * Also run `purgeStale` logic in the same pass — purge incomplete
735
- * messages older than this many ms. Default: 7 days.
736
- */
737
152
  incompleteMaxAgeMs?: number | undefined;
738
- /**
739
- * Interval for the background auto-compact timer.
740
- * Default: 5 minutes (AUTO_COMPACT_INTERVAL_MS).
741
- */
742
153
  intervalMs?: number | undefined;
743
- /**
744
- * Also run `purgeStale` logic in the same pass (completed > 1 day,
745
- * incomplete > 7 days). Default: true.
746
- */
747
154
  includePurgeStale?: boolean | undefined;
748
155
  }
749
156
  export interface AutoCompactResult {
750
- /** Messages removed because they were read by all online agents. */
751
157
  readByAllRemoved: number;
752
- /** Messages removed because their TTL expired (explicit or default). */
753
158
  expiredRemoved: number;
754
- /** Messages removed by the purgeStale pass (if enabled). */
755
159
  stalePurged: number;
756
- /** Total messages removed. */
757
160
  totalRemoved: number;
758
- /** Messages remaining in the mailbox after compaction. */
759
161
  remaining: number;
760
162
  }
761
163
  export interface Mailbox {
762
- /** Send a message. Returns the created message. */
763
164
  send(input: MailboxSendInput): Promise<MailboxMessage>;
764
- /** Query messages matching criteria. */
765
165
  query(query: MailboxQuery): Promise<MailboxMessage[]>;
766
- /** Acknowledge a message (read/complete). Returns updated message. */
767
166
  ack(input: MailboxAckInput): Promise<MailboxMessage | null>;
768
- /**
769
- * Acknowledge many messages in one shot. Acquires the file lock once and
770
- * rewrites the message file once, regardless of how many acks are in the
771
- * batch. Returns the messages that were actually updated (messages whose
772
- * ids are not in the file are skipped silently).
773
- *
774
- * This is the preferred path when an agent has multiple fresh messages
775
- * to receipt at once — the per-message {@link ack} path does a full
776
- * read-modify-rewrite of the mailbox file for every call.
777
- */
778
167
  ackMany(input: MailboxAckBatchInput): Promise<MailboxMessage[]>;
779
- /**
780
- * Soft-delete a message. Sets `deletedAt` to the current timestamp
781
- * and records the acting agent in `deletedBy`. Reversible via
782
- * {@link restore}. The default `query()` filter hides the message
783
- * once `deletedAt` is set; pass `includeDeleted: true` to see the
784
- * trash.
785
- */
786
168
  softDelete(mailId: string, by: string): Promise<MailboxMessage | null>;
787
- /**
788
- * Undo a {@link softDelete}. Clears `deletedAt` and `deletedBy` on
789
- * the message. No-op (returns the message as-is) if the message is
790
- * not soft-deleted.
791
- */
792
169
  restore(mailId: string): Promise<MailboxMessage | null>;
793
- /** Get a snapshot of online/offline agents and their current tasks. */
794
170
  getAgentStatuses(): Promise<MailboxAgentStatus[]>;
795
- /**
796
- * Get only online agents (heartbeat within 60s).
797
- * Useful for "who can I talk to right now?" queries.
798
- */
799
171
  getOnlineAgents(): Promise<MailboxAgentStatus[]>;
800
- /**
801
- * Register an agent. Called once per agent on first mailbox use.
802
- * Subsequent calls are idempotent — they update lastSeenAt.
803
- */
804
172
  registerAgent(input: AgentRegistrationInput): Promise<void>;
805
- /** Remove an agent from the registry entirely. Called on session shutdown. */
806
173
  deregisterAgent(agentId: string): Promise<void>;
807
- /**
808
- * Update agent heartbeat and optional status fields.
809
- * Called periodically (every tool call / iteration).
810
- */
811
174
  heartbeat(input: AgentHeartbeatInput): Promise<void>;
812
- /**
813
- * Count unread messages for a specific agent.
814
- * Used for "new mail" notifications without pulling full message bodies.
815
- */
816
175
  unreadCount(forAgentId: string, sessionId?: string): Promise<number>;
817
- /** Close and flush any pending writes. */
818
176
  close(): Promise<void>;
819
- /**
820
- * Delete all messages from the mailbox file.
821
- * Agents and read receipts are preserved; only messages are cleared.
822
- */
823
177
  clearAll(): Promise<void>;
824
- /**
825
- * Purge orphaned and stale messages from the mailbox.
826
- *
827
- * Stale messages are:
828
- * - Completed messages older than `completedMaxAgeMs` (default: 1 day)
829
- * - Incomplete messages older than `incompleteMaxAgeMs` (default: 7 days)
830
- *
831
- * This does NOT touch agent registrations or client registry.
832
- */
833
178
  purgeStale(opts?: PurgeOptions): Promise<PurgeResult>;
834
- /**
835
- * Auto-compact: remove messages that are no longer needed.
836
- *
837
- * Two cleanup passes run in a single file rewrite:
838
- * 1. **Read-by-all**: Messages read by every currently-online agent,
839
- * older than `readMaxAgeMs` (default 10 min).
840
- * 2. **Expired**: Messages whose `expiresAt` is in the past, or whose
841
- * `timestamp` is older than `defaultTtlMs` (default 24h) when no
842
- * explicit `expiresAt` is set.
843
- *
844
- * Also runs `purgeStale` logic (completed > 1 day, incomplete > 7 days)
845
- * in the same pass to avoid a second rewrite.
846
- */
847
179
  autoCompact(opts?: AutoCompactOptions): Promise<AutoCompactResult>;
848
- /**
849
- * Start a background timer that periodically calls `autoCompact`.
850
- * Returns a dispose function that stops the timer. Idempotent —
851
- * calling start twice replaces the prior timer.
852
- */
853
180
  startAutoCompactTimer(opts?: AutoCompactOptions): () => void;
854
- /**
855
- * Register a client (REPL/TUI/WebUI). Called once per client on startup.
856
- * Subsequent calls are idempotent — they update lastSeenAt.
857
- */
858
181
  registerClient(input: ClientRegistrationInput): Promise<void>;
859
- /**
860
- * Update client heartbeat. Called periodically (every 15s for clients).
861
- */
862
182
  clientHeartbeat(input: ClientHeartbeatInput): Promise<void>;
863
- /** Remove a client immediately on clean shutdown. */
864
183
  deregisterClient(clientId: string): Promise<void>;
865
- /**
866
- * Get snapshot of online/offline clients and their last activity.
867
- */
868
184
  getClientStatuses(): Promise<ClientStatus[]>;
869
- /**
870
- * Explicitly purge stale clients from the registry.
871
- * Removes client entries whose lastSeenAt is older than CLIENT_STALE_MS.
872
- * Returns the number of entries purged.
873
- */
874
185
  purgeClients(): Promise<number>;
875
186
  }
876
- export { expandMailboxCapabilities, hasMailboxCapability, MAILBOX_CAPABILITY_IMPLICATIONS, type MailboxActorContext, type MailboxAuthMode, type MailboxCapability, type MailboxPrincipalKind, } from './mailbox-auth-types.js';
877
- /**
878
- * Per-recipient delivery/action state for a single message.
879
- *
880
- * Keyed by actor ID. Each entry tracks when the actor read, completed,
881
- * or otherwise interacted with the message — independently of other actors.
882
- */
883
187
  export interface MailboxRecipientState {
884
- /** Actor ID this state belongs to. */
885
188
  actorId: string;
886
- /** ISO8601 — when this actor first read the message. */
887
189
  readAt?: string | undefined;
888
- /** ISO8601 — when this actor completed the message. */
889
190
  completedAt?: string | undefined;
890
- /** Who recorded the completion (usually same as actorId). */
891
191
  completedBy?: string | undefined;
892
- /** Optional outcome summary recorded by this actor. */
893
- outcome?: string | undefined;
894
- }
895
- /**
896
- * V2 JSONL receipt record. Appended alongside messages and v1 ack records.
897
- *
898
- * Has an explicit `__mailboxReceipt: 2` discriminator so:
899
- * 1. The v2 reader folds these into per-actor `MailboxRecipientState`.
900
- * 2. A v1 reader ignores the unknown JSON line (no `__ack` field).
901
- *
902
- * Fold algebra (applied during materialization):
903
- * - Keyed by `(messageId, actorId)`.
904
- * - `read`: first-write-wins (earliest read timestamp is preserved).
905
- * - `completed`: monotonic upward (once `true`, cannot revert unless an
906
- * explicit reopen record with `completed: false` is appended).
907
- * - `outcome`: last-write-wins.
908
- * - Duplicate records (same messageId, actorId, timestamp): idempotent no-ops.
909
- */
910
- export interface MailboxReceiptRecordV2 {
911
- /** Discriminator — always `2` to distinguish from messages and v1 acks. */
912
- __mailboxReceipt: 2;
913
- /** Target message this receipt applies to. */
914
- messageId: string;
915
- /** Actor this receipt belongs to. */
916
- actorId: string;
917
- /** ISO8601 — when the receipt event occurred. */
918
- timestamp: string;
919
- /** Was the message read by this actor? */
920
- read?: boolean | undefined;
921
- /** Was the message completed by this actor? */
922
- completed?: boolean | undefined;
923
- /** Optional outcome summary. */
924
192
  outcome?: string | undefined;
925
193
  }
926
- /**
927
- * Check if a parsed JSONL value is a v2 receipt record.
928
- * Validates the discriminator AND required structural fields.
929
- */
930
- export declare function isMailboxReceiptRecordV2(value: unknown): value is MailboxReceiptRecordV2;
931
- /**
932
- * Materialized message with per-actor recipient state.
933
- *
934
- * This is the internal representation after folding all v1 acks and v2
935
- * receipt records. It carries both the legacy fields (for backward
936
- * compatibility) and the new actor-specific state map.
937
- *
938
- * `legacyGlobalCompletion` is set ONLY for historical v1 fan-out messages
939
- * that were globally completed. It is never set for new v2 writes.
940
- */
941
194
  export interface MailboxMessageProjection extends MailboxMessage {
942
- /** Per-actor delivery/action state, keyed by actorId. */
943
195
  recipientState: Readonly<Record<string, MailboxRecipientState>>;
944
- /**
945
- * True ONLY for historical v1 fan-out messages that were globally completed
946
- * (completed before the v2 migration). These remain globally suppressed to
947
- * prevent upgrade re-delivery. New v2 writes NEVER set this.
948
- */
949
- legacyGlobalCompletion?: boolean | undefined;
950
- }
951
- /**
952
- * Self-facing message — what a specific actor sees.
953
- *
954
- * This does NOT extend `MailboxMessage` because self-facing responses must NOT
955
- * contain aggregate receipt metadata (`readBy`, `completedBy`, `completedAt`,
956
- * `outcome`) that would leak other actors' activity. Only actor-specific
957
- * derived fields are added on top of the non-sensitive message fields.
958
- */
959
- export interface ActorMailboxMessage extends Omit<MailboxMessage, 'readBy' | 'completed' | 'completedBy' | 'completedAt' | 'outcome'> {
960
- /** Has this actor read the message? */
961
- readByMe: boolean;
962
- /** Has this actor completed the message? */
963
- completedByMe: boolean;
964
- /** Does this message require action from this actor? */
965
- actionRequiredForMe: boolean;
966
- /** This actor's outcome, if any. */
967
- myOutcome?: string | undefined;
968
- /**
969
- * True for historical v1 fan-out messages that were globally completed.
970
- * Lets the UI distinguish "completed by me" from "completed globally
971
- * before migration."
972
- */
973
196
  legacyGlobalCompletion?: boolean | undefined;
974
197
  }
975
- /**
976
- * Derive `actionRequiredForMe` from the canonical type properties and actor state.
977
- *
978
- * Defined as:
979
- * `MAILBOX_TYPE_PROPERTIES[type].requiresAction && visible && !completedByMe && !deleted && !legacyGlobalCompletion`
980
- *
981
- * For historical legacy-global messages, `actionRequiredForMe` is always false
982
- * because the message is suppressed and should not re-enter any actor's flow.
983
- */
984
- export declare function isActionRequiredForActor(message: Pick<MailboxMessage, 'type' | 'deletedAt' | 'completed'>, projection: Pick<ActorMailboxMessage, 'completedByMe' | 'legacyGlobalCompletion'>): boolean;
985
198
  //# sourceMappingURL=mailbox-types.d.ts.map