macro-agent 0.1.12 → 0.2.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 (116) hide show
  1. package/dist/acp/macro-agent.d.ts.map +1 -1
  2. package/dist/acp/macro-agent.js +18 -40
  3. package/dist/acp/macro-agent.js.map +1 -1
  4. package/dist/agent/agent-manager-v2.d.ts.map +1 -1
  5. package/dist/agent/agent-manager-v2.js +241 -8
  6. package/dist/agent/agent-manager-v2.js.map +1 -1
  7. package/dist/agent/types.d.ts +47 -0
  8. package/dist/agent/types.d.ts.map +1 -1
  9. package/dist/agent/types.js.map +1 -1
  10. package/dist/boot-v2.d.ts +33 -0
  11. package/dist/boot-v2.d.ts.map +1 -1
  12. package/dist/boot-v2.js +144 -11
  13. package/dist/boot-v2.js.map +1 -1
  14. package/dist/cli/acp.js +0 -0
  15. package/dist/cli/inbox-mcp-proxy.d.ts +36 -0
  16. package/dist/cli/inbox-mcp-proxy.d.ts.map +1 -0
  17. package/dist/cli/inbox-mcp-proxy.js +51 -0
  18. package/dist/cli/inbox-mcp-proxy.js.map +1 -0
  19. package/dist/cli/index.js +0 -0
  20. package/dist/cli/mcp.js +0 -0
  21. package/dist/dispatch/loadout-translation.d.ts +100 -0
  22. package/dist/dispatch/loadout-translation.d.ts.map +1 -0
  23. package/dist/dispatch/loadout-translation.js +90 -0
  24. package/dist/dispatch/loadout-translation.js.map +1 -0
  25. package/dist/dispatch/mail-inbound-consumer.d.ts +136 -0
  26. package/dist/dispatch/mail-inbound-consumer.d.ts.map +1 -0
  27. package/dist/dispatch/mail-inbound-consumer.js +360 -0
  28. package/dist/dispatch/mail-inbound-consumer.js.map +1 -0
  29. package/dist/dispatch/mail-inbound-reuse-consumer.d.ts +75 -0
  30. package/dist/dispatch/mail-inbound-reuse-consumer.d.ts.map +1 -0
  31. package/dist/dispatch/mail-inbound-reuse-consumer.js +325 -0
  32. package/dist/dispatch/mail-inbound-reuse-consumer.js.map +1 -0
  33. package/dist/dispatch/permission-evaluator.d.ts +68 -0
  34. package/dist/dispatch/permission-evaluator.d.ts.map +1 -0
  35. package/dist/dispatch/permission-evaluator.js +159 -0
  36. package/dist/dispatch/permission-evaluator.js.map +1 -0
  37. package/dist/dispatch/permission-overlay.d.ts +64 -0
  38. package/dist/dispatch/permission-overlay.d.ts.map +1 -0
  39. package/dist/dispatch/permission-overlay.js +72 -0
  40. package/dist/dispatch/permission-overlay.js.map +1 -0
  41. package/dist/dispatch/permissions-handler.d.ts +71 -0
  42. package/dist/dispatch/permissions-handler.d.ts.map +1 -0
  43. package/dist/dispatch/permissions-handler.js +83 -0
  44. package/dist/dispatch/permissions-handler.js.map +1 -0
  45. package/dist/dispatch/spawn-agent-handler.d.ts +84 -0
  46. package/dist/dispatch/spawn-agent-handler.d.ts.map +1 -0
  47. package/dist/dispatch/spawn-agent-handler.js +85 -0
  48. package/dist/dispatch/spawn-agent-handler.js.map +1 -0
  49. package/dist/lifecycle/handlers-v2.d.ts +7 -0
  50. package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
  51. package/dist/lifecycle/handlers-v2.js +27 -0
  52. package/dist/lifecycle/handlers-v2.js.map +1 -1
  53. package/dist/map/lifecycle-bridge.d.ts +18 -0
  54. package/dist/map/lifecycle-bridge.d.ts.map +1 -1
  55. package/dist/map/lifecycle-bridge.js +23 -1
  56. package/dist/map/lifecycle-bridge.js.map +1 -1
  57. package/dist/map/mail-bridge.d.ts +55 -0
  58. package/dist/map/mail-bridge.d.ts.map +1 -0
  59. package/dist/map/mail-bridge.js +115 -0
  60. package/dist/map/mail-bridge.js.map +1 -0
  61. package/dist/map/repo-workspace.d.ts +46 -0
  62. package/dist/map/repo-workspace.d.ts.map +1 -0
  63. package/dist/map/repo-workspace.js +39 -0
  64. package/dist/map/repo-workspace.js.map +1 -0
  65. package/dist/map/server.d.ts.map +1 -1
  66. package/dist/map/server.js +1 -0
  67. package/dist/map/server.js.map +1 -1
  68. package/dist/map/sidecar.d.ts.map +1 -1
  69. package/dist/map/sidecar.js +308 -1
  70. package/dist/map/sidecar.js.map +1 -1
  71. package/dist/map/types.d.ts +29 -0
  72. package/dist/map/types.d.ts.map +1 -1
  73. package/dist/mcp/tools/done-v2.d.ts.map +1 -1
  74. package/dist/mcp/tools/done-v2.js +1 -0
  75. package/dist/mcp/tools/done-v2.js.map +1 -1
  76. package/dist/teams/team-loader.d.ts.map +1 -1
  77. package/dist/teams/team-loader.js.map +1 -1
  78. package/dist/teams/team-runtime-v2.d.ts.map +1 -1
  79. package/dist/teams/team-runtime-v2.js +2 -0
  80. package/dist/teams/team-runtime-v2.js.map +1 -1
  81. package/package.json +7 -5
  82. package/src/acp/macro-agent.ts +20 -42
  83. package/src/agent/__tests__/agent-manager-v2.permission-interception.test.ts +296 -0
  84. package/src/agent/__tests__/agent-manager-v2.permissions.test.ts +233 -0
  85. package/src/agent/agent-manager-v2.ts +269 -8
  86. package/src/agent/types.ts +51 -0
  87. package/src/boot-v2.ts +192 -12
  88. package/src/cli/inbox-mcp-proxy.ts +56 -0
  89. package/src/dispatch/CLAUDE.md +129 -0
  90. package/src/dispatch/__tests__/loadout-translation.test.ts +141 -0
  91. package/src/dispatch/__tests__/mail-inbound-consumer.integration.test.ts +519 -0
  92. package/src/dispatch/__tests__/mail-inbound-consumer.test.ts +800 -0
  93. package/src/dispatch/__tests__/mail-inbound-reuse-consumer.test.ts +575 -0
  94. package/src/dispatch/__tests__/permission-evaluator.test.ts +196 -0
  95. package/src/dispatch/__tests__/permission-overlay.test.ts +56 -0
  96. package/src/dispatch/__tests__/permissions-handler.test.ts +168 -0
  97. package/src/dispatch/__tests__/spawn-agent-handler.test.ts +282 -0
  98. package/src/dispatch/loadout-translation.ts +138 -0
  99. package/src/dispatch/mail-inbound-consumer.ts +560 -0
  100. package/src/dispatch/mail-inbound-reuse-consumer.ts +479 -0
  101. package/src/dispatch/permission-evaluator.ts +191 -0
  102. package/src/dispatch/permission-overlay.ts +89 -0
  103. package/src/dispatch/permissions-handler.ts +112 -0
  104. package/src/dispatch/spawn-agent-handler.ts +160 -0
  105. package/src/lifecycle/handlers-v2.ts +34 -0
  106. package/src/map/__tests__/lifecycle-bridge.test.ts +64 -0
  107. package/src/map/__tests__/mail-bridge.test.ts +196 -0
  108. package/src/map/lifecycle-bridge.ts +48 -2
  109. package/src/map/mail-bridge.ts +203 -0
  110. package/src/map/repo-workspace.ts +82 -0
  111. package/src/map/server.ts +1 -0
  112. package/src/map/sidecar.ts +431 -1
  113. package/src/map/types.ts +34 -0
  114. package/src/mcp/tools/done-v2.ts +1 -0
  115. package/src/teams/team-loader.ts +3 -1
  116. package/src/teams/team-runtime-v2.ts +2 -0
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Mail-Inbound Consumer
3
+ *
4
+ * Receives hub-driven `x-dispatch/work` envelopes from the local agent-inbox
5
+ * and spawns a worker agent to handle them — without requiring the optional
6
+ * outbound swarm-dispatch orchestrator (`config.dispatch.enabled`).
7
+ *
8
+ * This makes mail-inbound dispatch a **default capability** of macro-agent: as
9
+ * long as the MAP sidecar is connected and the inbox is running, any hub that
10
+ * delivers work via `mail/turn.received` will be served.
11
+ *
12
+ * ## Data flow
13
+ *
14
+ * hub sends `mail/turn.received`
15
+ * → mail-bridge translates {type,body} → {schema,data} + _conversationId
16
+ * → inboxAdapter delivers to local inbox (recipient = dispatcherAgentId)
17
+ * → inbox.events fires "inbox.message"
18
+ * → consumer classifies: schema === 'x-dispatch/work'?
19
+ * yes → spawn worker via agentManager.spawn()
20
+ * → record agentId → conversationId in side map
21
+ * → worker calls done(summary="…SENTINEL…")
22
+ * → handlers-v2 stores _lastSummary in agentStore metadata (parentId null branch)
23
+ * → agentManager.onLifecycleEvent fires "stopped"
24
+ * → consumer reads _lastSummary + conversationId
25
+ * → mapSidecar.postMailTurn(conversationId, agentId, summary) [fire-and-forget]
26
+ *
27
+ * @module dispatch/mail-inbound-consumer
28
+ */
29
+ import { loadoutToSpawnOptions } from "./loadout-translation.js";
30
+ // ─────────────────────────────────────────────────────────────────
31
+ // Implementation
32
+ // ─────────────────────────────────────────────────────────────────
33
+ /**
34
+ * Wire the mail-inbound consumer.
35
+ *
36
+ * Returns a `stop()` handle that detaches all listeners.
37
+ * Safe to call multiple times (idempotent cleanup).
38
+ */
39
+ export function createMailInboundConsumer(opts) {
40
+ const { dispatcherAgentId, inboxEvents, agentManager, agentStore, getSidecar, getRepoManager, getRepoTransport, log = (msg) => console.log(msg), } = opts;
41
+ // ── Side-channel maps ────────────────────────────────────────
42
+ // agentId → conversationId: populated when a worker is spawned for a
43
+ // mail-inbound envelope; read when the agent's stopped event fires.
44
+ const agentConversationMap = new Map();
45
+ // taskId → expiresAt: idempotency guard keyed on the dispatch envelope's
46
+ // task identifier. The local inbox can re-fire `inbox.message` for the
47
+ // same logical delivery — without this guard, a single bridged turn would
48
+ // trigger N concurrent spawn() calls, each producing a long-lived ACP
49
+ // subprocess.
50
+ //
51
+ // Bounded by TTL so the map cannot grow unbounded over a long-running
52
+ // deployment. SEEN_TASK_TTL_MS is generous (1 hour) — re-deliveries within
53
+ // that window are dropped, beyond it the dedup expires and a stale retry
54
+ // could legitimately re-spawn (preferable to permanent memory growth).
55
+ const SEEN_TASK_TTL_MS = 60 * 60 * 1000;
56
+ const seenTaskIds = new Map();
57
+ function pruneSeenTaskIds() {
58
+ const now = Date.now();
59
+ for (const [id, expiresAt] of seenTaskIds) {
60
+ if (expiresAt <= now)
61
+ seenTaskIds.delete(id);
62
+ }
63
+ }
64
+ // Counter for envelopes dropped because they are malformed (no taskId).
65
+ // Surfaced via the consumer handle's stats() method so operators can
66
+ // distinguish "no work" from "work is broken".
67
+ let droppedMalformedCount = 0;
68
+ log(`[mail-inbound] Consumer ready — listening for x-dispatch/work envelopes ` +
69
+ `(recipient=${dispatcherAgentId})`);
70
+ // ── Pre-spawn repo mount ─────────────────────────────────────
71
+ // Resolves the worker's cwd from the dispatch envelope's repo metadata.
72
+ // When clone_policy is 'allowed' and the repo isn't already attached,
73
+ // clones to clone_path (or a default under cwd) then attaches+declares.
74
+ // Best-effort: failures log a warning and return undefined (worker
75
+ // spawns without a repo-specific cwd).
76
+ async function resolveRepoCwd(repoMeta, taskId) {
77
+ const manager = getRepoManager?.();
78
+ if (!manager)
79
+ return undefined;
80
+ const canonicalUrl = repoMeta.canonical_url;
81
+ if (!canonicalUrl)
82
+ return undefined;
83
+ // Check if the repo is already attached (by canonical URL match).
84
+ const existing = manager.list().find((h) => h.identity.canonicalUrl === canonicalUrl);
85
+ if (existing) {
86
+ log(`[mail-inbound] Repo already attached at ${existing.localPath} for taskId=${taskId}`);
87
+ return existing.localPath;
88
+ }
89
+ // Not attached — clone only if explicitly allowed.
90
+ if (repoMeta.clone_policy !== 'allowed') {
91
+ log(`[mail-inbound] Repo ${canonicalUrl} not attached and clone_policy=${repoMeta.clone_policy ?? 'none'} — ` +
92
+ `skipping mount for taskId=${taskId}`);
93
+ return undefined;
94
+ }
95
+ const clonePath = repoMeta.clone_path ?? `/tmp/openhive-repos/${repoMeta.repo_id}`;
96
+ try {
97
+ const { execSync } = await import("node:child_process");
98
+ // Clone if the directory doesn't exist yet.
99
+ const fs = await import("node:fs");
100
+ if (!fs.existsSync(clonePath)) {
101
+ log(`[mail-inbound] Cloning ${canonicalUrl} → ${clonePath} for taskId=${taskId}`);
102
+ execSync(`git clone --depth 1 ${canonicalUrl} ${clonePath}`, {
103
+ stdio: "pipe",
104
+ timeout: 120_000,
105
+ });
106
+ }
107
+ // Checkout target branch if specified.
108
+ if (repoMeta.branch) {
109
+ try {
110
+ execSync(`git -C ${clonePath} fetch origin ${repoMeta.branch} --depth 1`, {
111
+ stdio: "pipe",
112
+ timeout: 60_000,
113
+ });
114
+ execSync(`git -C ${clonePath} checkout ${repoMeta.branch}`, {
115
+ stdio: "pipe",
116
+ timeout: 30_000,
117
+ });
118
+ }
119
+ catch {
120
+ log(`[mail-inbound] Branch checkout failed for ${repoMeta.branch} — continuing on default branch`);
121
+ }
122
+ }
123
+ // Attach to the repo manager so future dispatches find it.
124
+ const handle = await manager.attach({
125
+ remoteUrl: canonicalUrl,
126
+ localPath: clonePath,
127
+ currentBranch: repoMeta.branch,
128
+ });
129
+ // Declare the new workspace to the hub (best-effort).
130
+ const transport = getRepoTransport?.();
131
+ if (transport) {
132
+ try {
133
+ const bindings = manager.list().map((h) => ({
134
+ canonical_url: h.identity.canonicalUrl,
135
+ local_path: h.localPath,
136
+ }));
137
+ await transport.notify("x-workspace/repo.declare", { bindings });
138
+ }
139
+ catch {
140
+ // Non-fatal — the hub may not support workspace declarations.
141
+ }
142
+ }
143
+ log(`[mail-inbound] Mounted repo at ${handle.localPath} for taskId=${taskId}`);
144
+ return handle.localPath;
145
+ }
146
+ catch (err) {
147
+ log(`[mail-inbound] Pre-spawn repo mount failed for taskId=${taskId}: ` +
148
+ `${err.message ?? String(err)}`);
149
+ return undefined;
150
+ }
151
+ }
152
+ // ── Inbox message listener ───────────────────────────────────
153
+ const onMessage = (event) => {
154
+ // Only handle messages delivered to our dispatcher recipient.
155
+ if (event.agentId !== dispatcherAgentId)
156
+ return;
157
+ const content = event.message?.content;
158
+ if (content?.schema !== "x-dispatch/work")
159
+ return;
160
+ const data = content.data;
161
+ if (!data?.taskId) {
162
+ droppedMalformedCount++;
163
+ log(`[mail-inbound] Dropping malformed envelope (no taskId, total=${droppedMalformedCount}) — ` +
164
+ `keys=${Object.keys(data ?? {}).join(',')} from=${event.message?.sender_id ?? '?'}`);
165
+ return;
166
+ }
167
+ const taskId = data.taskId;
168
+ pruneSeenTaskIds();
169
+ const seenExpiresAt = seenTaskIds.get(taskId);
170
+ if (seenExpiresAt !== undefined && seenExpiresAt > Date.now()) {
171
+ // Already spawned a worker for this dispatch within the dedup window
172
+ // — silently ignore the re-delivery. The hub treats dispatch as
173
+ // exactly-once on the worker side, so dropping is correct.
174
+ return;
175
+ }
176
+ seenTaskIds.set(taskId, Date.now() + SEEN_TASK_TTL_MS);
177
+ const conversationId = content._conversationId;
178
+ const prompt = data.prompt ?? data.content ?? "";
179
+ // Validate the envelope's role against the local role registry. Unknown
180
+ // role names (e.g. team-role-ref roles like 'executor' surfaced by hubs
181
+ // that don't share macro-agent's role taxonomy) silently fall back to
182
+ // GenericRole inside `resolveRole`, which has `lifecycle.type='persistent'`
183
+ // and no system-prompt instruction to call `done()`. That breaks the
184
+ // mail-reply path because the worker stops without writing
185
+ // `_lastSummary`, so we end up logging "Worker stopped but _lastSummary
186
+ // is empty — no reply turn posted" and the hub never sees the answer.
187
+ //
188
+ // Use 'worker' as the fallback (ephemeral lifecycle + LIFECYCLE.DONE
189
+ // capability + system prompt that mandates `done()`) so unknown roles
190
+ // get a sensible default that completes the reply round-trip.
191
+ const requestedRole = data.role;
192
+ const roleRegistry = agentManager.getRoleRegistry?.();
193
+ const knownRole = requestedRole && roleRegistry?.getRole(requestedRole) !== undefined;
194
+ const role = knownRole ? requestedRole : "worker";
195
+ if (requestedRole && !knownRole) {
196
+ log(`[mail-inbound] Unknown role '${requestedRole}' for taskId=${taskId} — falling back to 'worker'`);
197
+ }
198
+ // Loadout-derived structured fields ride in the envelope. We prefer the
199
+ // canonical top-level `data.loadout` slot (Step 3 of the ACP+lifecycle
200
+ // plan) but fall back to the legacy `data.metadata.permissions` shape
201
+ // for one deprecation cycle so older hubs that haven't rolled the new
202
+ // wire shape continue to work.
203
+ //
204
+ // `loadoutToSpawnOptions` is shared with the new `dispatch/spawn-agent`
205
+ // MAP handler so both wire paths produce identical spawn options.
206
+ //
207
+ // `fullAutonomous: true` because mail-inbound workers have no human in
208
+ // the loop to answer `ask` rules — collapse them to `allow` (vs. the
209
+ // safer `deny` default for spawns where a human might still be reached).
210
+ let wireLoadout = data.loadout;
211
+ if (!wireLoadout) {
212
+ const legacyPermissions = data.metadata?.permissions;
213
+ const legacyMcpProviders = data.metadata?.mcpProviders;
214
+ if (legacyPermissions || legacyMcpProviders) {
215
+ wireLoadout = {
216
+ ...(legacyPermissions ? { permissions: legacyPermissions } : {}),
217
+ ...(legacyMcpProviders ? { mcpProviders: legacyMcpProviders } : {}),
218
+ };
219
+ }
220
+ }
221
+ const spawnLoadoutOpts = loadoutToSpawnOptions(wireLoadout, {
222
+ fullAutonomous: true,
223
+ });
224
+ // Extract repo metadata from the envelope for pre-spawn mount.
225
+ const repoMeta = {
226
+ repo_id: data.metadata?.repo_id,
227
+ canonical_url: data.metadata?.canonical_url,
228
+ branch: data.metadata?.branch,
229
+ commit_sha: data.metadata?.commit_sha,
230
+ clone_policy: data.metadata?.clone_policy,
231
+ clone_path: data.metadata?.clone_path,
232
+ };
233
+ log(`[mail-inbound] Received x-dispatch/work taskId=${taskId} ` +
234
+ `conv=${conversationId ?? "(none)"} role=${role}` +
235
+ (repoMeta.repo_id ? ` repo=${repoMeta.repo_id}` : "") +
236
+ (spawnLoadoutOpts.permissions
237
+ ? ` permissions=${JSON.stringify(spawnLoadoutOpts.permissions)}`
238
+ : ""));
239
+ // Spawn is async — fire and forget. Errors are logged, not thrown.
240
+ // Pre-spawn mount resolves the worker's cwd from the repo metadata
241
+ // before spawning. Best-effort: mount failures proceed without a
242
+ // repo-specific cwd.
243
+ (async () => {
244
+ let repoCwd;
245
+ if (repoMeta.repo_id) {
246
+ repoCwd = await resolveRepoCwd(repoMeta, taskId);
247
+ }
248
+ const spawned = await agentManager.spawn({
249
+ task: prompt,
250
+ task_id: taskId,
251
+ role,
252
+ parent: null,
253
+ // Mail-inbound dispatch workers run sandboxed — strip the host's
254
+ // user-level Claude setting sources so installed plugin MCP servers
255
+ // (claude-code-swarm, oh-my-claudecode, …) don't auto-load and hang
256
+ // session/new on environments where the host services aren't reachable.
257
+ isolatedSettings: true,
258
+ ...(repoCwd ? { cwd: repoCwd } : {}),
259
+ ...spawnLoadoutOpts,
260
+ });
261
+ log(`[mail-inbound] Spawned worker agentId=${spawned.id} for taskId=${taskId}` +
262
+ (repoCwd ? ` cwd=${repoCwd}` : ""));
263
+ if (conversationId) {
264
+ agentConversationMap.set(spawned.id, conversationId);
265
+ }
266
+ // Spawn only creates an idle ACP session — the task lives in the
267
+ // system prompt as instructions. To get the model to actually do
268
+ // the work, send the prompt as a user message via promptUntilDone.
269
+ // This drives the worker to completion (done() called) so the
270
+ // lifecycle stopped listener below fires and posts the reply
271
+ // back to the hub. Fire-and-forget; errors are logged.
272
+ try {
273
+ await agentManager.promptUntilDone(spawned.id, prompt, {
274
+ maxFollowUps: 0,
275
+ });
276
+ }
277
+ catch (err) {
278
+ log(`[mail-inbound] promptUntilDone failed for agentId=${spawned.id}: ` +
279
+ `${err.message ?? String(err)}`);
280
+ }
281
+ })().catch((err) => {
282
+ log(`[mail-inbound] Spawn failed for taskId=${taskId}: ${err.message ?? String(err)}`);
283
+ });
284
+ };
285
+ inboxEvents.on("inbox.message", onMessage);
286
+ // ── Lifecycle stopped listener ───────────────────────────────
287
+ const unsubscribeLifecycle = agentManager.onLifecycleEvent((event) => {
288
+ if (event.type !== "stopped")
289
+ return;
290
+ const agentId = event.agent.id;
291
+ const conversationId = agentConversationMap.get(agentId);
292
+ if (!conversationId)
293
+ return; // not a mail-inbound worker we spawned
294
+ agentConversationMap.delete(agentId);
295
+ // Read the summary stored by handlers-v2 for parentless workers.
296
+ const record = agentStore.getAgent(agentId);
297
+ const summary = record?.metadata?._lastSummary;
298
+ if (!summary) {
299
+ log(`[mail-inbound] Worker agentId=${agentId} stopped but _lastSummary is empty — ` +
300
+ `no reply turn posted`);
301
+ return;
302
+ }
303
+ log(`[mail-inbound] Worker agentId=${agentId} stopped — posting reply to ` +
304
+ `conv=${conversationId}`);
305
+ const sidecar = getSidecar();
306
+ if (!sidecar?.postMailTurn) {
307
+ log(`[mail-inbound] No sidecar/postMailTurn — reply turn dropped`);
308
+ return;
309
+ }
310
+ sidecar.postMailTurn(conversationId, agentId, summary)
311
+ .then(() => {
312
+ // Clear the stored summary so it can't replay if the same agentId
313
+ // is ever reused for another dispatch (the AgentManager generally
314
+ // mints fresh ids, but this is cheap insurance against a future
315
+ // change).
316
+ try {
317
+ const existingMeta = agentStore.getAgent(agentId)?.metadata ?? {};
318
+ const { _lastSummary: _drop, ...rest } = existingMeta;
319
+ void _drop;
320
+ agentStore.updateAgent(agentId, { metadata: rest });
321
+ }
322
+ catch {
323
+ // best-effort — store may be closing during shutdown
324
+ }
325
+ })
326
+ .catch(() => {
327
+ // best-effort — hub may be temporarily unreachable
328
+ });
329
+ });
330
+ // ── Cleanup ──────────────────────────────────────────────────
331
+ let stopped = false;
332
+ return {
333
+ stop() {
334
+ if (stopped)
335
+ return;
336
+ stopped = true;
337
+ try {
338
+ if (inboxEvents.off) {
339
+ inboxEvents.off("inbox.message", onMessage);
340
+ }
341
+ else if (inboxEvents.removeListener) {
342
+ inboxEvents.removeListener("inbox.message", onMessage);
343
+ }
344
+ }
345
+ catch {
346
+ // best effort
347
+ }
348
+ unsubscribeLifecycle();
349
+ log(`[mail-inbound] Consumer stopped`);
350
+ },
351
+ stats() {
352
+ pruneSeenTaskIds();
353
+ return {
354
+ droppedMalformed: droppedMalformedCount,
355
+ seenTaskIds: seenTaskIds.size,
356
+ };
357
+ },
358
+ };
359
+ }
360
+ //# sourceMappingURL=mail-inbound-consumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-inbound-consumer.js","sourceRoot":"","sources":["../../src/dispatch/mail-inbound-consumer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAAE,qBAAqB,EAAoB,MAAM,0BAA0B,CAAC;AAsHnF,oEAAoE;AACpE,iBAAiB;AACjB,oEAAoE;AAEpE;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAAgC;IAEhC,MAAM,EACJ,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GACxC,GAAG,IAAI,CAAC;IAET,gEAAgE;IAChE,qEAAqE;IACrE,oEAAoE;IACpE,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvD,yEAAyE;IACzE,uEAAuE;IACvE,0EAA0E;IAC1E,sEAAsE;IACtE,cAAc;IACd,EAAE;IACF,sEAAsE;IACtE,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,SAAS,gBAAgB;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,WAAW,EAAE,CAAC;YAC1C,IAAI,SAAS,IAAI,GAAG;gBAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,qEAAqE;IACrE,+CAA+C;IAC/C,IAAI,qBAAqB,GAAG,CAAC,CAAC;IAE9B,GAAG,CACD,0EAA0E;QACxE,cAAc,iBAAiB,GAAG,CACrC,CAAC;IAEF,gEAAgE;IAChE,wEAAwE;IACxE,sEAAsE;IACtE,wEAAwE;IACxE,mEAAmE;IACnE,uCAAuC;IACvC,KAAK,UAAU,cAAc,CAC3B,QAA8B,EAC9B,MAAc;QAEd,MAAM,OAAO,GAAG,cAAc,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAE/B,MAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC;QAC5C,IAAI,CAAC,YAAY;YAAE,OAAO,SAAS,CAAC;QAEpC,kEAAkE;QAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,KAAK,YAAY,CAChD,CAAC;QACF,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,CAAC,2CAA2C,QAAQ,CAAC,SAAS,eAAe,MAAM,EAAE,CAAC,CAAC;YAC1F,OAAO,QAAQ,CAAC,SAAS,CAAC;QAC5B,CAAC;QAED,mDAAmD;QACnD,IAAI,QAAQ,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACxC,GAAG,CACD,uBAAuB,YAAY,kCAAkC,QAAQ,CAAC,YAAY,IAAI,MAAM,KAAK;gBACvG,6BAA6B,MAAM,EAAE,CACxC,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,IAAI,uBAAuB,QAAQ,CAAC,OAAO,EAAE,CAAC;QACnF,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAExD,4CAA4C;YAC5C,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,GAAG,CAAC,0BAA0B,YAAY,MAAM,SAAS,eAAe,MAAM,EAAE,CAAC,CAAC;gBAClF,QAAQ,CAAC,uBAAuB,YAAY,IAAI,SAAS,EAAE,EAAE;oBAC3D,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,OAAO;iBACjB,CAAC,CAAC;YACL,CAAC;YAED,uCAAuC;YACvC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACpB,IAAI,CAAC;oBACH,QAAQ,CAAC,UAAU,SAAS,iBAAiB,QAAQ,CAAC,MAAM,YAAY,EAAE;wBACxE,KAAK,EAAE,MAAM;wBACb,OAAO,EAAE,MAAM;qBAChB,CAAC,CAAC;oBACH,QAAQ,CAAC,UAAU,SAAS,aAAa,QAAQ,CAAC,MAAM,EAAE,EAAE;wBAC1D,KAAK,EAAE,MAAM;wBACb,OAAO,EAAE,MAAM;qBAChB,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,GAAG,CAAC,6CAA6C,QAAQ,CAAC,MAAM,iCAAiC,CAAC,CAAC;gBACrG,CAAC;YACH,CAAC;YAED,2DAA2D;YAC3D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC;gBAClC,SAAS,EAAE,YAAY;gBACvB,SAAS,EAAE,SAAS;gBACpB,aAAa,EAAE,QAAQ,CAAC,MAAM;aAC/B,CAAC,CAAC;YAEH,sDAAsD;YACtD,MAAM,SAAS,GAAG,gBAAgB,EAAE,EAAE,CAAC;YACvC,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC1C,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY;wBACtC,UAAU,EAAE,CAAC,CAAC,SAAS;qBACxB,CAAC,CAAC,CAAC;oBACJ,MAAM,SAAS,CAAC,MAAM,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACnE,CAAC;gBAAC,MAAM,CAAC;oBACP,8DAA8D;gBAChE,CAAC;YACH,CAAC;YAED,GAAG,CAAC,kCAAkC,MAAM,CAAC,SAAS,eAAe,MAAM,EAAE,CAAC,CAAC;YAC/E,OAAO,MAAM,CAAC,SAAS,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CACD,yDAAyD,MAAM,IAAI;gBACjE,GAAI,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7C,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,MAAM,SAAS,GAAG,CAAC,KAAwB,EAAQ,EAAE;QACnD,8DAA8D;QAC9D,IAAI,KAAK,CAAC,OAAO,KAAK,iBAAiB;YAAE,OAAO;QAEhD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAalB,CAAC;QAEd,IAAI,OAAO,EAAE,MAAM,KAAK,iBAAiB;YAAE,OAAO;QAElD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YAClB,qBAAqB,EAAE,CAAC;YACxB,GAAG,CACD,gEAAgE,qBAAqB,MAAM;gBACzF,QAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,CACtF,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,gBAAgB,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC9D,qEAAqE;YACrE,gEAAgE;YAChE,2DAA2D;YAC3D,OAAO;QACT,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,CAAC;QAEvD,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QAEjD,wEAAwE;QACxE,wEAAwE;QACxE,sEAAsE;QACtE,4EAA4E;QAC5E,qEAAqE;QACrE,2DAA2D;QAC3D,wEAAwE;QACxE,sEAAsE;QACtE,EAAE;QACF,qEAAqE;QACrE,sEAAsE;QACtE,8DAA8D;QAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC;QAChC,MAAM,YAAY,GAAG,YAAY,CAAC,eAAe,EAAE,EAAE,CAAC;QACtD,MAAM,SAAS,GACb,aAAa,IAAI,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,SAAS,CAAC;QACtE,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,aAAc,CAAC,CAAC,CAAC,QAAQ,CAAC;QACnD,IAAI,aAAa,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,GAAG,CACD,gCAAgC,aAAa,gBAAgB,MAAM,6BAA6B,CACjG,CAAC;QACJ,CAAC;QAED,wEAAwE;QACxE,uEAAuE;QACvE,sEAAsE;QACtE,sEAAsE;QACtE,+BAA+B;QAC/B,EAAE;QACF,wEAAwE;QACxE,kEAAkE;QAClE,EAAE;QACF,uEAAuE;QACvE,qEAAqE;QACrE,yEAAyE;QACzE,IAAI,WAAW,GAA4B,IAAI,CAAC,OAAO,CAAC;QACxD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,iBAAiB,GAAG,IAAI,CAAC,QAAQ,EAAE,WAE5B,CAAC;YACd,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,EAAE,YAE7B,CAAC;YACd,IAAI,iBAAiB,IAAI,kBAAkB,EAAE,CAAC;gBAC5C,WAAW,GAAG;oBACZ,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChE,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpE,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,WAAW,EAAE;YAC1D,cAAc,EAAE,IAAI;SACrB,CAAC,CAAC;QAEH,+DAA+D;QAC/D,MAAM,QAAQ,GAAyB;YACrC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,OAA6B;YACrD,aAAa,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAmC;YACjE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,MAA4B;YACnD,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAgC;YAC3D,YAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAkC;YAC/D,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAgC;SAC5D,CAAC;QAEF,GAAG,CACD,kDAAkD,MAAM,GAAG;YACzD,QAAQ,cAAc,IAAI,QAAQ,SAAS,IAAI,EAAE;YACjD,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,CAAC,gBAAgB,CAAC,WAAW;gBAC3B,CAAC,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAAE;gBAChE,CAAC,CAAC,EAAE,CAAC,CACV,CAAC;QAEF,mEAAmE;QACnE,mEAAmE;QACnE,iEAAiE;QACjE,qBAAqB;QACrB,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,OAA2B,CAAC;YAChC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC;gBACvC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,MAAM;gBACf,IAAI;gBACJ,MAAM,EAAE,IAAI;gBACZ,iEAAiE;gBACjE,oEAAoE;gBACpE,oEAAoE;gBACpE,wEAAwE;gBACxE,gBAAgB,EAAE,IAAI;gBACtB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpC,GAAG,gBAAgB;aACpB,CAAC,CAAC;YAEH,GAAG,CACD,yCAAyC,OAAO,CAAC,EAAE,eAAe,MAAM,EAAE;gBACxE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACrC,CAAC;YACF,IAAI,cAAc,EAAE,CAAC;gBACnB,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;YACvD,CAAC;YAED,iEAAiE;YACjE,iEAAiE;YACjE,mEAAmE;YACnE,8DAA8D;YAC9D,6DAA6D;YAC7D,uDAAuD;YACvD,IAAI,CAAC;gBACH,MAAM,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE;oBACrD,YAAY,EAAE,CAAC;iBAChB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CACD,qDAAqD,OAAO,CAAC,EAAE,IAAI;oBACjE,GAAI,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7C,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YAC1B,GAAG,CACD,0CAA0C,MAAM,KAC7C,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CACtC,EAAE,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,WAAW,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAE3C,gEAAgE;IAChE,MAAM,oBAAoB,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC,KAAK,EAAE,EAAE;QACnE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO;QAErC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,cAAc,GAAG,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,CAAC,cAAc;YAAE,OAAO,CAAC,uCAAuC;QAEpE,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAErC,iEAAiE;QACjE,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,EAAE,QAAQ,EAAE,YAAkC,CAAC;QACrE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CACD,iCAAiC,OAAO,uCAAuC;gBAC7E,sBAAsB,CACzB,CAAC;YACF,OAAO;QACT,CAAC;QAED,GAAG,CACD,iCAAiC,OAAO,8BAA8B;YACpE,QAAQ,cAAc,EAAE,CAC3B,CAAC;QAEF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YAC3B,GAAG,CAAC,6DAA6D,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC;aACnD,IAAI,CAAC,GAAG,EAAE;YACT,kEAAkE;YAClE,kEAAkE;YAClE,gEAAgE;YAChE,WAAW;YACX,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC;gBAClE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,YAAuC,CAAC;gBACjF,KAAK,KAAK,CAAC;gBACX,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;YACvD,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,mDAAmD;QACrD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,gEAAgE;IAChE,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO;QACL,IAAI;YACF,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,CAAC;gBACH,IAAI,WAAW,CAAC,GAAG,EAAE,CAAC;oBACpB,WAAW,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;gBAC9C,CAAC;qBAAM,IAAI,WAAW,CAAC,cAAc,EAAE,CAAC;oBACtC,WAAW,CAAC,cAAc,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,cAAc;YAChB,CAAC;YACD,oBAAoB,EAAE,CAAC;YACvB,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACzC,CAAC;QACD,KAAK;YACH,gBAAgB,EAAE,CAAC;YACnB,OAAO;gBACL,gBAAgB,EAAE,qBAAqB;gBACvC,WAAW,EAAE,WAAW,CAAC,IAAI;aAC9B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Mail-Inbound Reuse Consumer
3
+ *
4
+ * Receives hub-driven `x-dispatch/work` envelopes addressed to **non-sidecar**
5
+ * agents — long-lived team workers, coordinators, etc. — and drives them
6
+ * through the dispatch turn using their existing session, then posts the
7
+ * summary back as a mail turn.
8
+ *
9
+ * Mirrors `mail-inbound-consumer.ts` but with three semantic differences:
10
+ *
11
+ * 1. Filters envelopes addressed to ANY non-sidecar agent (the existing
12
+ * consumer filters for the dispatcher recipient).
13
+ * 2. Does **not** spawn — it drives the existing agent's session via
14
+ * `agentManager.prompt(agentId, prompt)` and watches for `done()` in
15
+ * the update stream.
16
+ * 3. Tracks `inflightDispatches` per agentId. A second envelope arriving
17
+ * while the same agent is already processing a dispatch is rejected
18
+ * with `recipient_busy` so the orchestrator can retry against another
19
+ * agent (or fall back to fresh-spawn). Reject is **dispatch-scoped**
20
+ * — non-dispatch work on the agent (peer messages, user chat) does
21
+ * NOT trigger the busy reject; that work stacks naturally.
22
+ *
23
+ * Reply path: captures `args.summary` from the done() tool call's rawInput
24
+ * directly off the update stream, so it works for both parented and
25
+ * parentless target agents (the parented branch in `handlers-v2` does NOT
26
+ * stash `_lastSummary` — only parentless agents do — but we don't need
27
+ * that path because we observe done() in-stream).
28
+ *
29
+ * @module dispatch/mail-inbound-reuse-consumer
30
+ */
31
+ import type { AgentManager } from "../agent/agent-manager.js";
32
+ import type { AgentStore } from "../agent/agent-store.js";
33
+ import type { InboxEvents, MailInboundSidecar } from "./mail-inbound-consumer.js";
34
+ export interface MailInboundReuseConsumerOptions {
35
+ /**
36
+ * The sidecar agent ID. Envelopes addressed to this id are handled by
37
+ * the original `mail-inbound-consumer` (fresh-spawn path); the reuse
38
+ * consumer ignores them so the two consumers' filters don't overlap.
39
+ */
40
+ dispatcherAgentId: string;
41
+ /** Raw inbox event emitter (from inboxAdapter.getInbox().events). */
42
+ inboxEvents: InboxEvents;
43
+ /** Agent lifecycle manager — used to drive the existing session. */
44
+ agentManager: AgentManager;
45
+ /** Agent store — used to confirm the target agent is running. */
46
+ agentStore: AgentStore;
47
+ /**
48
+ * Optional sidecar reference. Populated after step 13 in boot-v2 via
49
+ * the shared systemRef. The consumer accesses it lazily at reply time.
50
+ */
51
+ getSidecar: () => MailInboundSidecar | null | undefined;
52
+ /** Optional logger (default: console.log). */
53
+ log?: (msg: string) => void;
54
+ }
55
+ export interface MailInboundReuseConsumerStats {
56
+ /** Count of envelopes dropped because they lacked a taskId. */
57
+ droppedMalformed: number;
58
+ /** Number of distinct taskIds currently tracked for dedup. */
59
+ seenTaskIds: number;
60
+ /** Number of rejects emitted because the target agent was already busy with a dispatch. */
61
+ busyRejects: number;
62
+ /** Currently in-flight dispatches keyed by agentId. */
63
+ inflightCount: number;
64
+ }
65
+ export interface MailInboundReuseConsumer {
66
+ stop(): void;
67
+ stats(): MailInboundReuseConsumerStats;
68
+ }
69
+ /**
70
+ * Wire the mail-inbound reuse consumer.
71
+ *
72
+ * Returns a `stop()` handle that detaches the inbox listener.
73
+ */
74
+ export declare function createMailInboundReuseConsumer(opts: MailInboundReuseConsumerOptions): MailInboundReuseConsumer;
75
+ //# sourceMappingURL=mail-inbound-reuse-consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-inbound-reuse-consumer.d.ts","sourceRoot":"","sources":["../../src/dispatch/mail-inbound-reuse-consumer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,KAAK,EACV,WAAW,EAEX,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAUpC,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B,qEAAqE;IACrE,WAAW,EAAE,WAAW,CAAC;IAEzB,oEAAoE;IACpE,YAAY,EAAE,YAAY,CAAC;IAE3B,iEAAiE;IACjE,UAAU,EAAE,UAAU,CAAC;IAEvB;;;OAGG;IACH,UAAU,EAAE,MAAM,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAC;IAExD,8CAA8C;IAC9C,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,6BAA6B;IAC5C,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,6BAA6B,CAAC;CACxC;AAUD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,+BAA+B,GACpC,wBAAwB,CAoX1B"}