@wrongstack/core 0.313.0 → 0.316.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 (343) hide show
  1. package/dist/chronicle/index.js +84 -6391
  2. package/dist/chronicle/legacy-journal-import.d.ts +0 -6
  3. package/dist/chronicle/metrics-ingest.d.ts +14 -0
  4. package/dist/chronicle/metrics-schema.d.ts +10 -1
  5. package/dist/chronicle/metrics-store.d.ts +20 -2
  6. package/dist/chronicle/project-server.js +98 -4874
  7. package/dist/chronicle/sink.d.ts +13 -0
  8. package/dist/chronicle/sqlite-journal.d.ts +20 -2
  9. package/dist/chunk-25LSLC3G.js +334 -0
  10. package/dist/chunk-27QIURLN.js +1073 -0
  11. package/dist/chunk-2FK4WTFF.js +207 -0
  12. package/dist/chunk-2YU4WVBW.js +134 -0
  13. package/dist/chunk-3AGWVSS7.js +4608 -0
  14. package/dist/chunk-3BDV7RL3.js +211 -0
  15. package/dist/chunk-3ECENVHL.js +325 -0
  16. package/dist/chunk-3XPVB7BA.js +135 -0
  17. package/dist/chunk-45WI72ZB.js +16884 -0
  18. package/dist/chunk-4BMVPOHO.js +3748 -0
  19. package/dist/chunk-4I7HGVVA.js +363 -0
  20. package/dist/chunk-4LCZVJVF.js +317 -0
  21. package/dist/chunk-4SJNS7OB.js +217 -0
  22. package/dist/chunk-4VWO3ZXL.js +342 -0
  23. package/dist/chunk-57GEWK5X.js +26 -0
  24. package/dist/chunk-5BMIE7QG.js +46 -0
  25. package/dist/chunk-5S437PRC.js +50 -0
  26. package/dist/chunk-5XDUGTKN.js +511 -0
  27. package/dist/chunk-5YPSXQXM.js +1443 -0
  28. package/dist/chunk-6FHWXLPT.js +10 -0
  29. package/dist/chunk-6H5GEOET.js +2406 -0
  30. package/dist/chunk-6NWEVFEL.js +151 -0
  31. package/dist/chunk-AYW5YINX.js +58 -0
  32. package/dist/chunk-B4AMLZPB.js +9 -0
  33. package/dist/chunk-B7F4WVP4.js +65 -0
  34. package/dist/chunk-BJHVULHO.js +55 -0
  35. package/dist/chunk-BPBKKBUK.js +2085 -0
  36. package/dist/chunk-BYD345OX.js +589 -0
  37. package/dist/chunk-C27KDLOX.js +279 -0
  38. package/dist/chunk-CU546ESW.js +597 -0
  39. package/dist/chunk-DLHHXUAM.js +172 -0
  40. package/dist/chunk-DOTHQSJ2.js +429 -0
  41. package/dist/chunk-DYLPMV4Q.js +711 -0
  42. package/dist/chunk-EODDVJBM.js +82 -0
  43. package/dist/chunk-F3JWNST7.js +361 -0
  44. package/dist/chunk-FC2ZGRF3.js +140 -0
  45. package/dist/chunk-FCBTO76Z.js +5786 -0
  46. package/dist/chunk-FHZVFYSL.js +396 -0
  47. package/dist/chunk-FNXVT7HF.js +52 -0
  48. package/dist/chunk-FWNLEZYG.js +259 -0
  49. package/dist/chunk-FXQLIX3K.js +211 -0
  50. package/dist/chunk-G5S3S5ZN.js +38 -0
  51. package/dist/chunk-GDQ7CZRQ.js +116 -0
  52. package/dist/chunk-GJJGJB2R.js +1374 -0
  53. package/dist/chunk-GTCU6DZ3.js +1 -0
  54. package/dist/chunk-HA4ZYA6T.js +178 -0
  55. package/dist/chunk-HI3JGZR2.js +188 -0
  56. package/dist/chunk-HMLIGAU7.js +395 -0
  57. package/dist/chunk-HXKZ7OBQ.js +303 -0
  58. package/dist/chunk-I7DALG7J.js +292 -0
  59. package/dist/chunk-IRUTEHCE.js +960 -0
  60. package/dist/chunk-IYFBFX34.js +86 -0
  61. package/dist/chunk-J2WRWR37.js +1104 -0
  62. package/dist/chunk-JFPJ6LLQ.js +129 -0
  63. package/dist/chunk-JMULFFLN.js +9 -0
  64. package/dist/chunk-JO4MGB6Z.js +1264 -0
  65. package/dist/chunk-JVKB2TBL.js +48 -0
  66. package/dist/chunk-JX7VBZDK.js +31 -0
  67. package/dist/chunk-K6BXGITA.js +931 -0
  68. package/dist/chunk-KDQJCT2J.js +45 -0
  69. package/dist/chunk-KJ2ZTQSL.js +47 -0
  70. package/dist/chunk-KZMSZV32.js +187 -0
  71. package/dist/chunk-LA7CLDRW.js +129 -0
  72. package/dist/chunk-LHOA5DWA.js +28 -0
  73. package/dist/chunk-LSMMOS2Z.js +119 -0
  74. package/dist/chunk-LX5QWLBL.js +27 -0
  75. package/dist/chunk-LXWCMQNJ.js +789 -0
  76. package/dist/chunk-M27LIEKV.js +2829 -0
  77. package/dist/chunk-MJPHJRAL.js +663 -0
  78. package/dist/chunk-MTL4O5YR.js +228 -0
  79. package/dist/chunk-NDBIPFNY.js +102 -0
  80. package/dist/chunk-NEMIWEXC.js +1199 -0
  81. package/dist/chunk-NHJ75HXY.js +1 -0
  82. package/dist/chunk-NIOAGFPB.js +1510 -0
  83. package/dist/chunk-NS52ISWS.js +355 -0
  84. package/dist/chunk-NSOFPKGT.js +5352 -0
  85. package/dist/chunk-NVIZG63R.js +757 -0
  86. package/dist/chunk-O37BQ65S.js +21 -0
  87. package/dist/chunk-OOYZPOXM.js +308 -0
  88. package/dist/chunk-OSGLYW5J.js +28 -0
  89. package/dist/chunk-PFCMSSUF.js +1640 -0
  90. package/dist/chunk-PFZO3C6H.js +14 -0
  91. package/dist/chunk-PGRVXIMY.js +158 -0
  92. package/dist/chunk-PHH7LD45.js +113 -0
  93. package/dist/chunk-PISBFBJN.js +109 -0
  94. package/dist/chunk-POOOIWE5.js +9 -0
  95. package/dist/chunk-PQNS3U7Z.js +1240 -0
  96. package/dist/chunk-QSTSJOCK.js +402 -0
  97. package/dist/chunk-RFQMOKWK.js +282 -0
  98. package/dist/chunk-RJIZEJ42.js +30 -0
  99. package/dist/chunk-RTOTNK3P.js +484 -0
  100. package/dist/chunk-RXKSDUAU.js +1172 -0
  101. package/dist/chunk-S45JPKQN.js +3317 -0
  102. package/dist/chunk-SAFHACFA.js +152 -0
  103. package/dist/chunk-SQJVFEBV.js +41 -0
  104. package/dist/chunk-SVT4RNTR.js +49 -0
  105. package/dist/chunk-SWUFN7XM.js +43 -0
  106. package/dist/chunk-SXMVYC5H.js +174 -0
  107. package/dist/chunk-SZ6AYUIZ.js +23 -0
  108. package/dist/chunk-TBRL2RCC.js +9 -0
  109. package/dist/chunk-TCCHINEF.js +655 -0
  110. package/dist/chunk-THEGTRAG.js +316 -0
  111. package/dist/chunk-TR4LQE7L.js +2256 -0
  112. package/dist/chunk-TRT2HRWZ.js +18 -0
  113. package/dist/chunk-TWJMGXCI.js +900 -0
  114. package/dist/chunk-U45AXT5D.js +1023 -0
  115. package/dist/chunk-U4FWBSO3.js +509 -0
  116. package/dist/chunk-UBGDX6FI.js +147 -0
  117. package/dist/chunk-UJILKYES.js +75 -0
  118. package/dist/chunk-ULMAQV3T.js +234 -0
  119. package/dist/chunk-VVBP6KGT.js +9 -0
  120. package/dist/chunk-W5VITDM2.js +168 -0
  121. package/dist/chunk-WAHQVNWT.js +194 -0
  122. package/dist/chunk-WBPVKEEE.js +227 -0
  123. package/dist/chunk-WGGYRBXH.js +5434 -0
  124. package/dist/chunk-WLYKU45P.js +5770 -0
  125. package/dist/chunk-WP7BVANJ.js +53 -0
  126. package/dist/chunk-WQQON3SV.js +473 -0
  127. package/dist/chunk-WQTOVMGV.js +119 -0
  128. package/dist/chunk-XBDP4QPW.js +7 -0
  129. package/dist/chunk-XEBHSJLT.js +103 -0
  130. package/dist/chunk-XF4VUMKW.js +231 -0
  131. package/dist/chunk-XQ2ECNK3.js +1 -0
  132. package/dist/chunk-Y5YXDM7D.js +74 -0
  133. package/dist/chunk-YOPAUVU4.js +2588 -0
  134. package/dist/chunk-YPT4A46F.js +362 -0
  135. package/dist/chunk-YWXT3JNM.js +425 -0
  136. package/dist/chunk-YY6APYUM.js +49 -0
  137. package/dist/chunk-Z2IJUO45.js +691 -0
  138. package/dist/chunk-ZHZ2Y2DH.js +1643 -0
  139. package/dist/chunk-ZT7JNIGX.js +152 -0
  140. package/dist/coordination/agent-monitor.d.ts +9 -1
  141. package/dist/coordination/agent-subagent-runner.d.ts +1 -1
  142. package/dist/coordination/agents/agent-prompts.d.ts +8 -0
  143. package/dist/coordination/agents/index.d.ts +33 -1
  144. package/dist/coordination/agents/index.js +184 -6269
  145. package/dist/coordination/agents/phase3-wave1-platform.d.ts +1 -3
  146. package/dist/coordination/agents/phase3-wave2-meta.d.ts +1 -3
  147. package/dist/coordination/agents/phase8-wave3-products.d.ts +1 -3
  148. package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts +1 -3
  149. package/dist/coordination/agents/project-agent-identity.d.ts +11 -11
  150. package/dist/coordination/agents/types.d.ts +13 -2
  151. package/dist/coordination/brain-monitor.d.ts +30 -7
  152. package/dist/coordination/collab-debug.d.ts +1 -1
  153. package/dist/coordination/collab-pause.d.ts +1 -1
  154. package/dist/coordination/delegate-tool.d.ts +7 -5
  155. package/dist/coordination/director-kanban-queue-helpers.d.ts +1 -0
  156. package/dist/coordination/director-options.d.ts +7 -0
  157. package/dist/coordination/director-spawn-model.d.ts +17 -0
  158. package/dist/coordination/director-tools.d.ts +2 -2
  159. package/dist/coordination/director.d.ts +12 -0
  160. package/dist/coordination/dispatcher.d.ts +8 -0
  161. package/dist/coordination/fleet-bus.d.ts +35 -4
  162. package/dist/coordination/fleet-supervisor.d.ts +10 -2
  163. package/dist/coordination/fleet.d.ts +16 -7
  164. package/dist/coordination/index.d.ts +4 -0
  165. package/dist/coordination/index.js +402 -34324
  166. package/dist/coordination/kanban-dispatch-port.d.ts +2 -2
  167. package/dist/coordination/mailbox-http-router.d.ts +4 -3
  168. package/dist/coordination/mailbox-http-validation.d.ts +1 -1
  169. package/dist/coordination/mailbox-project-server.js +48 -3276
  170. package/dist/coordination/mailbox-types.d.ts +24 -6
  171. package/dist/coordination/model-tier-leader.d.ts +122 -0
  172. package/dist/coordination/model-tier.d.ts +122 -0
  173. package/dist/coordination/multi-agent-coordinator.d.ts +24 -0
  174. package/dist/coordination/origin-session.d.ts +16 -0
  175. package/dist/coordination/provider-status-tracker.d.ts +41 -5
  176. package/dist/coordination/session-note-hub.d.ts +44 -0
  177. package/dist/coordination/session-note-tool.d.ts +10 -0
  178. package/dist/core/agent.d.ts +15 -2
  179. package/dist/core/context.d.ts +22 -3
  180. package/dist/core/index.d.ts +2 -0
  181. package/dist/core/index.js +239 -12360
  182. package/dist/core/model-ref.js +5 -23
  183. package/dist/core/provider-runner.d.ts +36 -1
  184. package/dist/core/request-conversation-binding.d.ts +37 -0
  185. package/dist/core/session-notes.d.ts +25 -0
  186. package/dist/core/system-prompt-builder.d.ts +11 -2
  187. package/dist/core/system-prompt-variants.d.ts +78 -0
  188. package/dist/design/index.js +50 -1087
  189. package/dist/execution/auto-compaction-middleware.d.ts +11 -0
  190. package/dist/execution/autonomy-brain.d.ts +2 -1
  191. package/dist/execution/autonomy-prompt-contributor.d.ts +7 -2
  192. package/dist/execution/compaction-core.d.ts +4 -4
  193. package/dist/execution/eternal-autonomy.d.ts +1 -1
  194. package/dist/execution/index.d.ts +1 -1
  195. package/dist/execution/index.js +1615 -21895
  196. package/dist/execution/model-runtime.d.ts +0 -7
  197. package/dist/execution/parallel-eternal-engine.d.ts +7 -7
  198. package/dist/execution/prompt-enhancer.js +16 -535
  199. package/dist/execution/retry-policy.d.ts +31 -1
  200. package/dist/extension/index.js +7 -311
  201. package/dist/goal/index.js +33 -3435
  202. package/dist/hooks/index.js +14 -1138
  203. package/dist/hq/index.js +114 -5139
  204. package/dist/hq/persistence/event-log.d.ts +0 -1
  205. package/dist/hq/persistence.d.ts +4 -4
  206. package/dist/hq/protocol.js +21 -668
  207. package/dist/hq/publisher.d.ts +2 -0
  208. package/dist/index.d.ts +14 -11
  209. package/dist/index.js +1502 -92655
  210. package/dist/infrastructure/index.d.ts +4 -4
  211. package/dist/infrastructure/index.js +504 -5230
  212. package/dist/infrastructure/token-counter.d.ts +9 -0
  213. package/dist/kernel/events/agent-events.d.ts +77 -3
  214. package/dist/kernel/events/brain-events.d.ts +12 -0
  215. package/dist/kernel/events/provider-events.d.ts +22 -1
  216. package/dist/kernel/events/session-events.d.ts +14 -0
  217. package/dist/kernel/events/tool-events.d.ts +13 -0
  218. package/dist/kernel/index.js +14 -992
  219. package/dist/mailbox-attach.d.ts +1 -1
  220. package/dist/models/index.js +41 -1117
  221. package/dist/notifications/index.js +3 -108
  222. package/dist/observability/event-bridge.d.ts +25 -5
  223. package/dist/observability/index.d.ts +9 -8
  224. package/dist/observability/index.js +477 -631
  225. package/dist/observability/tool-usage-source.d.ts +65 -0
  226. package/dist/plugin/index.d.ts +5 -0
  227. package/dist/plugin/index.js +600 -14835
  228. package/dist/plugins/auto-review-plugin.d.ts +3 -2
  229. package/dist/plugins/review-finding-commands.d.ts +0 -8
  230. package/dist/plugins/review-finding-store.js +22 -0
  231. package/dist/plugins/review-finding-types.js +19 -0
  232. package/dist/plugins/review-report-integration.js +21 -0
  233. package/dist/plugins/review-report-store.js +20 -0
  234. package/dist/plugins/review-report-types.js +13 -0
  235. package/dist/prompts/index.js +14 -948
  236. package/dist/registry/index.js +9 -822
  237. package/dist/registry/tool-registry.d.ts +59 -9
  238. package/dist/registry-7N4O3E7W.js +14 -0
  239. package/dist/replay/index.js +6 -84
  240. package/dist/security/capabilities.d.ts +6 -0
  241. package/dist/security/file-permissions.d.ts +1 -1
  242. package/dist/security/index.js +161 -2235
  243. package/dist/security/permission-policy.d.ts +12 -1
  244. package/dist/security/secret-scrubber.d.ts +15 -0
  245. package/dist/session-catalog/index.d.ts +1 -0
  246. package/dist/session-catalog/index.js +41 -2103
  247. package/dist/session-catalog/project-server.js +59 -1435
  248. package/dist/session-catalog/protocol.d.ts +32 -1
  249. package/dist/session-catalog/registry.d.ts +17 -1
  250. package/dist/session-catalog/session-agents.d.ts +56 -0
  251. package/dist/session-catalog/store-rebuild.d.ts +14 -0
  252. package/dist/session-catalog/store-schema.d.ts +26 -0
  253. package/dist/session-catalog/store.d.ts +27 -1
  254. package/dist/session-note-attach.d.ts +9 -0
  255. package/dist/skills/index.js +35 -1578
  256. package/dist/statusline/index.d.ts +43 -0
  257. package/dist/statusline/index.js +102 -0
  258. package/dist/storage/board-store-port.d.ts +2 -2
  259. package/dist/storage/config-loader/in-project-policy.d.ts +29 -0
  260. package/dist/storage/config-loader.d.ts +3 -2
  261. package/dist/storage/file-session-writer.d.ts +4 -2
  262. package/dist/storage/goal-coordination.d.ts +6 -1
  263. package/dist/storage/goal-kanban.d.ts +4 -1
  264. package/dist/storage/index.d.ts +12 -4
  265. package/dist/storage/index.js +198 -15601
  266. package/dist/storage/input-history-store.d.ts +2 -4
  267. package/dist/storage/memory-curator.d.ts +133 -0
  268. package/dist/storage/session-agent-attribution.d.ts +39 -0
  269. package/dist/storage/session-doctor.d.ts +109 -0
  270. package/dist/storage/session-event-bridge.d.ts +5 -7
  271. package/dist/storage/session-outcome.d.ts +42 -0
  272. package/dist/storage/session-recovery.d.ts +55 -0
  273. package/dist/storage/session-store/load-session-data.d.ts +11 -0
  274. package/dist/storage/session-store/prune-helpers.d.ts +1 -1
  275. package/dist/storage/session-store/resume-session.d.ts +0 -2
  276. package/dist/storage/session-store/types.d.ts +8 -0
  277. package/dist/storage/session-store.d.ts +3 -3
  278. package/dist/storage/session-summary-tracker.d.ts +8 -1
  279. package/dist/storage/session-write-buffer.d.ts +18 -0
  280. package/dist/storage/session-writer-truncate.d.ts +2 -0
  281. package/dist/tasking/index.js +10 -676
  282. package/dist/tools/fallback-manage-tools.d.ts +6 -4
  283. package/dist/tools/index.js +52 -9590
  284. package/dist/tools/model-tier-set-tool.d.ts +66 -0
  285. package/dist/types/agent-bridge.d.ts +2 -1
  286. package/dist/types/config/autonomy.d.ts +5 -0
  287. package/dist/types/config/mcp-features.d.ts +7 -0
  288. package/dist/types/config/model-tiers.d.ts +148 -0
  289. package/dist/types/config/root.d.ts +9 -0
  290. package/dist/types/config/tools.d.ts +73 -0
  291. package/dist/types/config/ui.d.ts +1 -1
  292. package/dist/types/config.d.ts +1 -0
  293. package/dist/types/context.d.ts +2 -0
  294. package/dist/types/default-config.d.ts +6 -0
  295. package/dist/types/errors.d.ts +20 -1
  296. package/dist/types/index.d.ts +11 -9
  297. package/dist/types/index.js +131 -1342
  298. package/dist/types/multi-agent.d.ts +43 -0
  299. package/dist/types/plugin.d.ts +1 -1
  300. package/dist/types/provider-runner.d.ts +8 -0
  301. package/dist/types/provider.d.ts +15 -0
  302. package/dist/types/runtime-capability-manifest.d.ts +1 -1
  303. package/dist/types/secret-scrubber.d.ts +15 -0
  304. package/dist/types/session-markers.d.ts +37 -10
  305. package/dist/types/session-markers.js +8 -144
  306. package/dist/types/session-timeline.d.ts +197 -0
  307. package/dist/types/session-timeline.js +12 -0
  308. package/dist/types/session.d.ts +172 -11
  309. package/dist/types/system-prompt.d.ts +27 -0
  310. package/dist/utils/config-backup.d.ts +9 -1
  311. package/dist/utils/error.js +3 -4
  312. package/dist/utils/expect-defined.js +3 -9
  313. package/dist/utils/heap-watchdog.js +6 -706
  314. package/dist/utils/index.js +271 -5685
  315. package/dist/utils/message-invariants.d.ts +4 -1
  316. package/dist/utils/regex-guard.d.ts +1 -1
  317. package/dist/utils/sage-output-block.js +5 -42
  318. package/dist/utils/socket-path.d.ts +1 -1
  319. package/dist/utils/token-estimate.d.ts +6 -2
  320. package/dist/utils/wstack-paths.d.ts +2 -0
  321. package/dist/wiring/proxy-rewrite.d.ts +46 -0
  322. package/dist/wiring/proxy-rewrite.js +24 -121
  323. package/dist/worktree/index.js +6 -887
  324. package/instructions/agents/explore-companion.md +45 -18
  325. package/instructions/agents/memory-curator.md +34 -22
  326. package/instructions/coordination/director-preamble.md +12 -7
  327. package/instructions/coordination/subagent-baseline.md +26 -5
  328. package/instructions/leader-after-task.md +4 -1
  329. package/instructions/llm/chimera-review.md +30 -9
  330. package/instructions/llm/memory-curator.md +31 -0
  331. package/instructions/sections/tool/delegation-compact.md +2 -0
  332. package/instructions/sections/tool/delegation-full.md +5 -1
  333. package/instructions/sections/tool/mailbox-compact.md +5 -1
  334. package/instructions/sections/tool/mailbox-full.md +3 -0
  335. package/instructions/sections/tool/session-note-compact.md +3 -0
  336. package/instructions/sections/tool/session-note-full.md +15 -0
  337. package/instructions/system-lite.md +9 -4
  338. package/instructions/system-pro.md +66 -143
  339. package/instructions/system.md +73 -175
  340. package/package.json +32 -4
  341. package/dist/coordination/agents.d.ts +0 -8
  342. package/dist/coordination/transport.d.ts +0 -2
  343. package/dist/storage/session-writer/session-writer-summary-tracker.d.ts +0 -28
@@ -0,0 +1,1264 @@
1
+ import {
2
+ MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS,
3
+ MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION,
4
+ SQLITE_MAILBOX_FILE,
5
+ encodeMailboxProjectServerMessage,
6
+ isMailboxProjectServerMessage,
7
+ mailboxProjectServerEndpoint,
8
+ mailboxProjectServerMetadataPath
9
+ } from "./chunk-PQNS3U7Z.js";
10
+ import {
11
+ HQ_MAILBOX_SNAPSHOT_MIN_INTERVAL_MS,
12
+ resolveProjectDir,
13
+ resolveSendTypeSafe
14
+ } from "./chunk-IRUTEHCE.js";
15
+ import {
16
+ acceptMailboxMessageForSession,
17
+ isMailboxMessageVisibleTo,
18
+ mailboxIdentityBase,
19
+ normalizeRecipient
20
+ } from "./chunk-FWNLEZYG.js";
21
+ import {
22
+ ToolCapabilities
23
+ } from "./chunk-UBGDX6FI.js";
24
+ import {
25
+ wstackGlobalRoot
26
+ } from "./chunk-SAFHACFA.js";
27
+ import {
28
+ toErrorMessage
29
+ } from "./chunk-TBRL2RCC.js";
30
+
31
+ // src/coordination/mailbox-project-server-client.ts
32
+ import { spawn } from "node:child_process";
33
+ import * as fs from "node:fs";
34
+ import * as net from "node:net";
35
+ import * as path from "node:path";
36
+ import { fileURLToPath } from "node:url";
37
+ var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
38
+ var SERVER_START_TIMEOUT_MS = 1e4;
39
+ var DEFAULT_CALL_TIMEOUT_MS = 3e4;
40
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
41
+ var AUTH_RETRY_DELAY_MS = 150;
42
+ var AUTH_RETRY_MAX_ATTEMPTS = 13;
43
+ function normalizePath(value) {
44
+ const resolved = path.resolve(value);
45
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
46
+ }
47
+ function resolveProjectServerUrl() {
48
+ for (const relative of [
49
+ // Unbundled / per-subpath output: dist/coordination/index.js.
50
+ "./mailbox-project-server.js",
51
+ // Root-level output — dist/index.js and, since core is built with code
52
+ // splitting, the shared chunks that sit next to it. Without this entry the
53
+ // lookup silently reports missing-build and every process quietly opens
54
+ // its own store, which is exactly the "one mailbox per project" invariant
55
+ // this resolver exists to protect.
56
+ "./coordination/mailbox-project-server.js",
57
+ "../../dist/coordination/mailbox-project-server.js"
58
+ ]) {
59
+ try {
60
+ const url = new URL(relative, import.meta.url);
61
+ if (url.protocol === "file:" && fs.existsSync(fileURLToPath(url))) return url;
62
+ } catch {
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ function isMailboxProjectServerAvailable() {
68
+ return resolveProjectServerUrl() !== null;
69
+ }
70
+ function delay(ms) {
71
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
72
+ }
73
+ function isUnauthorizedMailboxError(error) {
74
+ return error instanceof Error && error.name === "UnauthorizedMailboxRequest";
75
+ }
76
+ var MailboxProjectServerConnection = class {
77
+ constructor(projectDir) {
78
+ this.projectDir = projectDir;
79
+ this.projectDir = path.resolve(projectDir);
80
+ this.endpoint = mailboxProjectServerEndpoint(this.projectDir);
81
+ this.state = {
82
+ status: isMailboxProjectServerAvailable() ? "offline" : "unavailable",
83
+ connected: false,
84
+ projectDir: this.projectDir,
85
+ endpoint: this.endpoint
86
+ };
87
+ }
88
+ projectDir;
89
+ endpoint;
90
+ socket = null;
91
+ info = null;
92
+ buffer = "";
93
+ connecting = null;
94
+ connectResolve = null;
95
+ connectReject = null;
96
+ nextId = 1;
97
+ /**
98
+ * WS-027: read from the daemon's owner-only metadata file, never from the
99
+ * `hello` frame — the daemon sends `hello` to every socket that connects, so
100
+ * a token carried there would be handed to exactly the caller it must
101
+ * refuse (the mistake WS-028 found in the SAGE daemon).
102
+ */
103
+ authToken;
104
+ heartbeatTimer = null;
105
+ pending = /* @__PURE__ */ new Map();
106
+ eventListeners = /* @__PURE__ */ new Set();
107
+ mailboxEventListeners = /* @__PURE__ */ new Set();
108
+ stateListeners = /* @__PURE__ */ new Set();
109
+ state;
110
+ getState() {
111
+ return { ...this.state };
112
+ }
113
+ onStateChange(listener) {
114
+ this.stateListeners.add(listener);
115
+ return () => this.stateListeners.delete(listener);
116
+ }
117
+ onEvent(listener) {
118
+ this.eventListeners.add(listener);
119
+ return () => this.eventListeners.delete(listener);
120
+ }
121
+ onMailboxEvent(listener) {
122
+ this.mailboxEventListeners.add(listener);
123
+ return () => this.mailboxEventListeners.delete(listener);
124
+ }
125
+ async connect() {
126
+ await this.ensureConnected(true);
127
+ }
128
+ async status() {
129
+ return this.call("ping", {}, { timeoutMs: 3e3 });
130
+ }
131
+ /**
132
+ * Inspect an existing project server without starting one.
133
+ * Returns null when no server is running (ECONNREFUSED / timeout).
134
+ * Mirrors the SAGE `status()` probe pattern.
135
+ */
136
+ async probeStatus() {
137
+ try {
138
+ await this.ensureConnected(false);
139
+ return await this.request(
140
+ { type: "request", op: "ping", args: {} },
141
+ 3e3
142
+ );
143
+ } catch {
144
+ this.close();
145
+ return null;
146
+ }
147
+ }
148
+ async call(op, args, options = {}) {
149
+ const timeoutMs = options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
150
+ let lastError;
151
+ for (let attempt = 0; attempt <= AUTH_RETRY_MAX_ATTEMPTS; attempt++) {
152
+ try {
153
+ await this.ensureConnected(true);
154
+ return await this.request(
155
+ { type: "request", op, args },
156
+ timeoutMs
157
+ );
158
+ } catch (error) {
159
+ lastError = error;
160
+ const retriable = isUnauthorizedMailboxError(error) && attempt < AUTH_RETRY_MAX_ATTEMPTS;
161
+ if (!retriable) throw error;
162
+ await delay(AUTH_RETRY_DELAY_MS);
163
+ }
164
+ }
165
+ throw lastError;
166
+ }
167
+ async shutdown(reason) {
168
+ try {
169
+ await this.ensureConnected(false);
170
+ } catch {
171
+ return { stopped: false, reason: "offline" };
172
+ }
173
+ const pid = this.info?.pid;
174
+ const result = await this.request(
175
+ { type: "shutdown", reason },
176
+ 5e3
177
+ );
178
+ return { ...result, ...pid !== void 0 && result.pid === void 0 ? { pid } : {} };
179
+ }
180
+ close() {
181
+ this.stopHeartbeat();
182
+ const socket = this.socket;
183
+ this.socket = null;
184
+ this.info = null;
185
+ socket?.destroy();
186
+ this.rejectPending(new Error("Mailbox project server connection closed"));
187
+ this.transition("offline");
188
+ }
189
+ transition(status, error) {
190
+ this.state = {
191
+ status,
192
+ connected: status === "connected",
193
+ projectDir: this.projectDir,
194
+ endpoint: this.endpoint,
195
+ ...status === "connected" && this.info ? { pid: this.info.pid } : {},
196
+ ...error !== void 0 ? { lastError: error instanceof Error ? error.message : String(error) } : {}
197
+ };
198
+ for (const listener of [...this.stateListeners]) listener({ ...this.state });
199
+ }
200
+ async ensureConnected(spawnIfMissing) {
201
+ if (this.socket && !this.socket.destroyed && this.info) return;
202
+ if (this.connecting) return this.connecting;
203
+ if (!isMailboxProjectServerAvailable()) {
204
+ this.transition("unavailable");
205
+ throw new Error(
206
+ "Built mailbox project server is unavailable. Run `pnpm --filter @wrongstack/core build`. The mailbox has no in-process mode: there is no env var to fall back to, by design."
207
+ );
208
+ }
209
+ this.transition("connecting");
210
+ this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
211
+ this.transition("error", error);
212
+ throw error;
213
+ }).finally(() => {
214
+ this.connecting = null;
215
+ });
216
+ return this.connecting;
217
+ }
218
+ async connectWithElection(spawnIfMissing) {
219
+ const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
220
+ let spawned = false;
221
+ let lastError = new Error("Mailbox project server unavailable");
222
+ while (Date.now() < deadline) {
223
+ try {
224
+ await this.connectOnce();
225
+ return;
226
+ } catch (error) {
227
+ lastError = error;
228
+ }
229
+ if (!spawnIfMissing) break;
230
+ if (!spawned) {
231
+ this.spawnDetachedServer();
232
+ spawned = true;
233
+ }
234
+ await delay(75);
235
+ }
236
+ throw lastError;
237
+ }
238
+ connectOnce() {
239
+ this.socket?.destroy();
240
+ this.socket = null;
241
+ this.info = null;
242
+ this.buffer = "";
243
+ this.authToken = void 0;
244
+ return new Promise((resolve3, reject) => {
245
+ const socket = net.createConnection(this.endpoint);
246
+ this.socket = socket;
247
+ socket.setEncoding("utf8");
248
+ const timer = setTimeout(() => {
249
+ this.connectReject?.(new Error("Mailbox project server handshake timed out"));
250
+ socket.destroy();
251
+ }, CONNECT_ATTEMPT_TIMEOUT_MS);
252
+ timer.unref?.();
253
+ this.connectResolve = () => {
254
+ clearTimeout(timer);
255
+ this.connectResolve = null;
256
+ this.connectReject = null;
257
+ this.startHeartbeat();
258
+ this.transition("connected");
259
+ resolve3();
260
+ void this.request({ type: "request", op: "ping", args: {} }, 3e3).catch(
261
+ () => void 0
262
+ );
263
+ };
264
+ this.connectReject = (error) => {
265
+ clearTimeout(timer);
266
+ this.connectResolve = null;
267
+ this.connectReject = null;
268
+ reject(error);
269
+ };
270
+ socket.on("data", (chunk) => this.onData(socket, chunk));
271
+ socket.on("error", (error) => {
272
+ if (!this.info) this.connectReject?.(error);
273
+ });
274
+ socket.on("close", () => this.onClose(socket));
275
+ });
276
+ }
277
+ /**
278
+ * The token, read lazily and re-read while still unknown.
279
+ *
280
+ * The daemon starts listening before it writes its metadata (endpoint
281
+ * ownership has to be won first, and clobbering another daemon's file to win
282
+ * it would be worse), so a client that connects in that window sees no file
283
+ * yet. Re-reading until one is found costs a single small `readFileSync`.
284
+ */
285
+ currentAuthToken() {
286
+ if (this.authToken === void 0) {
287
+ try {
288
+ const raw = fs.readFileSync(mailboxProjectServerMetadataPath(this.projectDir), "utf8");
289
+ const parsed = JSON.parse(raw);
290
+ if (typeof parsed.authToken === "string" && parsed.authToken.length > 0) {
291
+ this.authToken = parsed.authToken;
292
+ }
293
+ } catch {
294
+ }
295
+ }
296
+ return this.authToken;
297
+ }
298
+ request(message, timeoutMs) {
299
+ const socket = this.socket;
300
+ if (!socket || socket.destroyed) {
301
+ return Promise.reject(new Error("Mailbox project server connection is unavailable"));
302
+ }
303
+ const id = this.nextId++;
304
+ const encoded = encodeMailboxProjectServerMessage({
305
+ ...message,
306
+ id,
307
+ authToken: this.currentAuthToken()
308
+ });
309
+ if (encoded.length > MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS) {
310
+ return Promise.reject(new Error("Mailbox project server request exceeded frame limit"));
311
+ }
312
+ return new Promise((resolve3, reject) => {
313
+ const timer = setTimeout(() => {
314
+ const pending = this.pending.get(id);
315
+ if (!pending) return;
316
+ this.pending.delete(id);
317
+ pending.reject(new Error(`Mailbox ${message.type} exceeded its ${timeoutMs}ms timeout`));
318
+ }, timeoutMs);
319
+ timer.unref?.();
320
+ this.pending.set(id, { resolve: resolve3, reject, timer });
321
+ socket.write(encoded);
322
+ });
323
+ }
324
+ onData(socket, chunk) {
325
+ if (socket !== this.socket) return;
326
+ this.buffer += chunk;
327
+ while (true) {
328
+ const newline = this.buffer.indexOf("\n");
329
+ if (newline < 0) {
330
+ if (this.buffer.length > MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS) {
331
+ socket.destroy(new Error("Mailbox project server response exceeded frame limit"));
332
+ }
333
+ return;
334
+ }
335
+ if (newline > MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS) {
336
+ socket.destroy(new Error("Mailbox project server response exceeded frame limit"));
337
+ return;
338
+ }
339
+ const line = this.buffer.slice(0, newline);
340
+ this.buffer = this.buffer.slice(newline + 1);
341
+ if (!line) continue;
342
+ let parsed;
343
+ try {
344
+ parsed = JSON.parse(line);
345
+ } catch {
346
+ socket.destroy(new Error("Invalid mailbox project server response"));
347
+ return;
348
+ }
349
+ if (!isMailboxProjectServerMessage(parsed)) {
350
+ socket.destroy(new Error("Invalid mailbox project server response"));
351
+ return;
352
+ }
353
+ this.onMessage(parsed);
354
+ }
355
+ }
356
+ onMessage(message) {
357
+ if (message.type === "hello") {
358
+ if (message.protocolVersion !== MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION) {
359
+ this.connectReject?.(
360
+ new Error(
361
+ `Mailbox protocol mismatch: client=${MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
362
+ )
363
+ );
364
+ this.socket?.destroy();
365
+ return;
366
+ }
367
+ if (normalizePath(message.projectDir) !== normalizePath(this.projectDir)) {
368
+ this.connectReject?.(new Error("Mailbox project server identity mismatch"));
369
+ this.socket?.destroy();
370
+ return;
371
+ }
372
+ this.info = message;
373
+ this.connectResolve?.();
374
+ return;
375
+ }
376
+ if (message.type === "event") {
377
+ for (const listener of [...this.eventListeners]) listener(message.event, message.payload);
378
+ return;
379
+ }
380
+ if (message.type === "mailbox-event") {
381
+ for (const listener of [...this.mailboxEventListeners]) listener(message.event);
382
+ return;
383
+ }
384
+ const pending = this.pending.get(message.id);
385
+ if (!pending) return;
386
+ this.pending.delete(message.id);
387
+ clearTimeout(pending.timer);
388
+ if (message.ok) pending.resolve(message.result);
389
+ else {
390
+ if (message.errorName === "UnauthorizedMailboxRequest") this.authToken = void 0;
391
+ const error = new Error(message.error);
392
+ if (message.errorName) error.name = message.errorName;
393
+ pending.reject(error);
394
+ }
395
+ }
396
+ onClose(socket) {
397
+ if (socket !== this.socket) return;
398
+ this.socket = null;
399
+ this.info = null;
400
+ this.stopHeartbeat();
401
+ const error = new Error("Mailbox project server connection closed");
402
+ this.connectReject?.(error);
403
+ this.connectResolve = null;
404
+ this.connectReject = null;
405
+ this.rejectPending(error);
406
+ this.transition("offline");
407
+ }
408
+ rejectPending(error) {
409
+ const pending = [...this.pending.values()];
410
+ this.pending.clear();
411
+ for (const item of pending) {
412
+ clearTimeout(item.timer);
413
+ item.reject(error);
414
+ }
415
+ }
416
+ startHeartbeat() {
417
+ this.stopHeartbeat();
418
+ const configured = Number(process.env["WRONGSTACK_MAILBOX_CLIENT_HEARTBEAT_MS"]);
419
+ const intervalMs = Number.isFinite(configured) && configured >= 50 ? configured : DEFAULT_HEARTBEAT_INTERVAL_MS;
420
+ this.heartbeatTimer = setInterval(() => {
421
+ const socket = this.socket;
422
+ if (socket && !socket.destroyed) {
423
+ socket.write(encodeMailboxProjectServerMessage({ type: "heartbeat" }));
424
+ }
425
+ }, intervalMs);
426
+ this.heartbeatTimer.unref?.();
427
+ }
428
+ stopHeartbeat() {
429
+ if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
430
+ this.heartbeatTimer = null;
431
+ }
432
+ spawnDetachedServer() {
433
+ const url = resolveProjectServerUrl();
434
+ if (!url) throw new Error("Mailbox project server entrypoint is unavailable");
435
+ fs.mkdirSync(this.projectDir, { recursive: true });
436
+ const child = spawn(process.execPath, [fileURLToPath(url), "--project-dir", this.projectDir], {
437
+ cwd: this.projectDir,
438
+ detached: process.platform !== "win32",
439
+ stdio: "ignore",
440
+ windowsHide: true,
441
+ env: process.env
442
+ });
443
+ child.unref();
444
+ }
445
+ };
446
+
447
+ // src/coordination/remote-mailbox.ts
448
+ import * as path2 from "node:path";
449
+ var HQ_MAILBOX_EVENT_MAX_PENDING = 256;
450
+ var sharedRemoteMailboxes = /* @__PURE__ */ new Map();
451
+ var sharedProjectConnections = /* @__PURE__ */ new Map();
452
+ function dependencyCache() {
453
+ return { byPublisher: /* @__PURE__ */ new WeakMap() };
454
+ }
455
+ var RemoteMailbox = class {
456
+ projectDir;
457
+ messagePath;
458
+ registryPath;
459
+ clientRegistryPath;
460
+ connection;
461
+ ownsConnection;
462
+ sharedConnectionKey;
463
+ events;
464
+ hqPublisherRef;
465
+ eventEmitter;
466
+ unsubscribeEvent;
467
+ unsubscribeMailboxEvent;
468
+ cacheEviction;
469
+ closed = false;
470
+ /** Pending coalesced HQ snapshot; see {@link scheduleHqSnapshot}. */
471
+ hqSnapshotTimer;
472
+ hqSnapshotLastAt = 0;
473
+ /** At most one full snapshot may query/map/serialize mailbox state at a time. */
474
+ hqSnapshotInFlight;
475
+ /** Latest scope requested while a timer or snapshot is already active. */
476
+ hqSnapshotPendingMailboxId;
477
+ /** Message deltas are serialized so a burst cannot launch one full mailbox
478
+ * query per event concurrently. Newer updates for the same message replace
479
+ * older pending ones; the periodic snapshot remains the lossless rollup. */
480
+ hqEventPending = /* @__PURE__ */ new Map();
481
+ hqEventInFlight;
482
+ hqEventCoalesced = 0;
483
+ hqEventDropped = 0;
484
+ constructor(optionsOrProjectDir, events, hqPublisher, eventEmitter) {
485
+ const options = typeof optionsOrProjectDir === "string" ? {
486
+ projectDir: optionsOrProjectDir,
487
+ events,
488
+ hqPublisher,
489
+ eventEmitter
490
+ } : optionsOrProjectDir;
491
+ this.projectDir = path2.resolve(options.projectDir);
492
+ this.messagePath = path2.join(this.projectDir, SQLITE_MAILBOX_FILE);
493
+ this.registryPath = this.messagePath;
494
+ this.clientRegistryPath = this.messagePath;
495
+ this.events = options.events;
496
+ this.hqPublisherRef = options.hqPublisher;
497
+ this.eventEmitter = options.eventEmitter;
498
+ if (options.isolatedConnection) {
499
+ this.connection = new MailboxProjectServerConnection(this.projectDir);
500
+ this.ownsConnection = true;
501
+ } else {
502
+ let entry = sharedProjectConnections.get(this.projectDir);
503
+ if (!entry) {
504
+ entry = {
505
+ connection: new MailboxProjectServerConnection(this.projectDir),
506
+ references: 0
507
+ };
508
+ sharedProjectConnections.set(this.projectDir, entry);
509
+ }
510
+ entry.references++;
511
+ this.connection = entry.connection;
512
+ this.sharedConnectionKey = this.projectDir;
513
+ this.ownsConnection = false;
514
+ }
515
+ this.unsubscribeEvent = this.connection.onEvent((event, payload) => {
516
+ this.events?.emitCustom(event, payload);
517
+ this.publishHqRegistryEvent(event, payload);
518
+ });
519
+ this.unsubscribeMailboxEvent = this.connection.onMailboxEvent((event) => {
520
+ this.eventEmitter?.emit(event);
521
+ this.publishHqEvent(event);
522
+ });
523
+ void this.connection.connect().catch(() => {
524
+ });
525
+ }
526
+ getConnectionState() {
527
+ return this.connection.getState();
528
+ }
529
+ onConnectionStateChange(listener) {
530
+ return this.connection.onStateChange(listener);
531
+ }
532
+ async initialize() {
533
+ await this.connection.connect();
534
+ await this.connection.status();
535
+ }
536
+ async status() {
537
+ return this.connection.status();
538
+ }
539
+ send(input) {
540
+ return this.connection.call("send", { input });
541
+ }
542
+ sendRuntimeControl(input) {
543
+ return this.connection.call("sendRuntimeControl", { input });
544
+ }
545
+ query(query) {
546
+ return this.connection.call("query", { query });
547
+ }
548
+ ack(input) {
549
+ return this.connection.call("ack", { input });
550
+ }
551
+ ackMany(input) {
552
+ return this.connection.call("ackMany", { input });
553
+ }
554
+ unreadCount(forAgentId, sessionId) {
555
+ return this.connection.call("unreadCount", { forAgentId, sessionId });
556
+ }
557
+ softDelete(mailId, by) {
558
+ return this.connection.call("softDelete", { mailId, by });
559
+ }
560
+ restore(mailId) {
561
+ return this.connection.call("restore", { mailId });
562
+ }
563
+ registerAgent(input) {
564
+ return this.connection.call("registerAgent", { input });
565
+ }
566
+ deregisterAgent(agentId) {
567
+ return this.connection.call("deregisterAgent", { agentId });
568
+ }
569
+ heartbeat(input) {
570
+ return this.connection.call("heartbeat", { input });
571
+ }
572
+ getAgentStatuses() {
573
+ return this.connection.call("getAgentStatuses", {});
574
+ }
575
+ getOnlineAgents() {
576
+ return this.connection.call("getOnlineAgents", {});
577
+ }
578
+ purgeAgents(maxAgeMs) {
579
+ return this.connection.call("purgeAgents", { maxAgeMs });
580
+ }
581
+ registerClient(input) {
582
+ return this.connection.call("registerClient", { input });
583
+ }
584
+ deregisterClient(clientId) {
585
+ return this.connection.call("deregisterClient", { clientId });
586
+ }
587
+ clientHeartbeat(input) {
588
+ return this.connection.call("clientHeartbeat", { input });
589
+ }
590
+ getClientStatuses() {
591
+ return this.connection.call("getClientStatuses", {});
592
+ }
593
+ purgeClients() {
594
+ return this.connection.call("purgeClients", {});
595
+ }
596
+ clearAll() {
597
+ return this.connection.call("clearAll", {});
598
+ }
599
+ purgeStale(options) {
600
+ return this.connection.call("purgeStale", { options });
601
+ }
602
+ autoCompact(options) {
603
+ return this.connection.call("autoCompact", { options }, { timeoutMs: 2 * 6e4 });
604
+ }
605
+ credentialIssue(options) {
606
+ return this.connection.call("credentialIssue", { options });
607
+ }
608
+ credentialVerify(credentialId, secret) {
609
+ return this.connection.call("credentialVerify", { credentialId, secret });
610
+ }
611
+ credentialRevoke(credentialId, reason, by) {
612
+ return this.connection.call("credentialRevoke", { credentialId, reason, by });
613
+ }
614
+ credentialRotate(credentialId, options) {
615
+ return this.connection.call("credentialRotate", { credentialId, options });
616
+ }
617
+ credentialGet(credentialId) {
618
+ return this.connection.call("credentialGet", { credentialId });
619
+ }
620
+ credentialList() {
621
+ return this.connection.call("credentialList", {});
622
+ }
623
+ credentialStatusCounts() {
624
+ return this.connection.call("credentialStatusCounts", {});
625
+ }
626
+ startAutoCompactTimer(_options) {
627
+ return () => {
628
+ };
629
+ }
630
+ async close() {
631
+ if (this.closed) return;
632
+ this.closed = true;
633
+ if (this.hqSnapshotTimer !== void 0) {
634
+ clearTimeout(this.hqSnapshotTimer);
635
+ this.hqSnapshotTimer = void 0;
636
+ }
637
+ this.hqSnapshotPendingMailboxId = void 0;
638
+ this.hqEventPending.clear();
639
+ this.cacheEviction?.();
640
+ this.cacheEviction = void 0;
641
+ this.unsubscribeEvent();
642
+ this.unsubscribeMailboxEvent();
643
+ if (this.ownsConnection) {
644
+ this.connection.close();
645
+ return;
646
+ }
647
+ if (this.sharedConnectionKey !== void 0) {
648
+ const entry = sharedProjectConnections.get(this.sharedConnectionKey);
649
+ if (entry?.connection === this.connection) {
650
+ entry.references = Math.max(0, entry.references - 1);
651
+ if (entry.references === 0) {
652
+ entry.connection.close();
653
+ sharedProjectConnections.delete(this.sharedConnectionKey);
654
+ }
655
+ }
656
+ }
657
+ }
658
+ /** @internal Remove a process-shared wrapper from its cache when explicitly closed. */
659
+ setCacheEviction(evict) {
660
+ this.cacheEviction = evict;
661
+ }
662
+ /** Lightweight diagnostics for the process memory flight recorder. */
663
+ getHqSnapshotStats() {
664
+ return {
665
+ inFlight: this.hqSnapshotInFlight !== void 0,
666
+ pending: this.hqSnapshotPendingMailboxId !== void 0,
667
+ timerScheduled: this.hqSnapshotTimer !== void 0,
668
+ eventInFlight: this.hqEventInFlight !== void 0,
669
+ pendingEvents: this.hqEventPending.size,
670
+ coalescedEvents: this.hqEventCoalesced,
671
+ droppedEvents: this.hqEventDropped
672
+ };
673
+ }
674
+ get hqPublisher() {
675
+ return typeof this.hqPublisherRef === "function" ? this.hqPublisherRef() : this.hqPublisherRef;
676
+ }
677
+ /**
678
+ * Publish a full HQ mailbox snapshot, at most once per
679
+ * {@link HQ_MAILBOX_SNAPSHOT_MIN_INTERVAL_MS}.
680
+ *
681
+ * Trailing-edge: the first caller after the interval schedules one, and
682
+ * every caller in the window folds into it. The snapshot is a rollup of
683
+ * current state, so a coalesced publish carries strictly more up-to-date
684
+ * information than the ones it replaced — dropping them loses nothing.
685
+ */
686
+ scheduleHqSnapshot(mailboxId) {
687
+ if (this.closed) return;
688
+ this.hqSnapshotPendingMailboxId = mailboxId;
689
+ if (this.hqSnapshotTimer !== void 0 || this.hqSnapshotInFlight !== void 0) return;
690
+ const elapsed = Date.now() - this.hqSnapshotLastAt;
691
+ const delay2 = Math.max(0, HQ_MAILBOX_SNAPSHOT_MIN_INTERVAL_MS - elapsed);
692
+ this.hqSnapshotTimer = setTimeout(() => {
693
+ this.hqSnapshotTimer = void 0;
694
+ const pendingMailboxId = this.hqSnapshotPendingMailboxId;
695
+ this.hqSnapshotPendingMailboxId = void 0;
696
+ const publisher = this.hqPublisher;
697
+ if (this.closed || !publisher || pendingMailboxId === void 0) return;
698
+ this.hqSnapshotLastAt = Date.now();
699
+ const inFlight = (async () => {
700
+ try {
701
+ await publisher.publishMailboxSnapshot(this, { mailboxId: pendingMailboxId });
702
+ } catch {
703
+ } finally {
704
+ this.hqSnapshotInFlight = void 0;
705
+ const nextMailboxId = this.hqSnapshotPendingMailboxId;
706
+ if (!this.closed && nextMailboxId !== void 0) {
707
+ this.scheduleHqSnapshot(nextMailboxId);
708
+ }
709
+ }
710
+ })();
711
+ this.hqSnapshotInFlight = inFlight;
712
+ }, delay2);
713
+ this.hqSnapshotTimer.unref?.();
714
+ }
715
+ publishHqEvent(event) {
716
+ if (!this.hqPublisher || this.closed) return;
717
+ if (this.hqEventPending.has(event.messageId)) {
718
+ this.hqEventCoalesced += 1;
719
+ } else if (this.hqEventPending.size >= HQ_MAILBOX_EVENT_MAX_PENDING) {
720
+ const oldest = this.hqEventPending.keys().next().value;
721
+ if (oldest !== void 0) this.hqEventPending.delete(oldest);
722
+ this.hqEventDropped += 1;
723
+ }
724
+ this.hqEventPending.set(event.messageId, event);
725
+ this.drainHqEvents();
726
+ }
727
+ drainHqEvents() {
728
+ if (this.closed || this.hqEventInFlight !== void 0) return;
729
+ const next = this.hqEventPending.entries().next().value;
730
+ if (!next) return;
731
+ const [messageId, event] = next;
732
+ this.hqEventPending.delete(messageId);
733
+ const publisher = this.hqPublisher;
734
+ if (!publisher) {
735
+ this.hqEventPending.clear();
736
+ return;
737
+ }
738
+ const mailboxId = `${path2.basename(this.projectDir)}:mailbox`;
739
+ const inFlight = this.query({ includeDeleted: true, limit: 100 }).then((messages) => {
740
+ const message = messages.find((candidate) => candidate.id === event.messageId);
741
+ const action = event.type === "message.sent" ? "message.sent" : "message.updated";
742
+ publisher.publishMailboxEvent({
743
+ mailboxId,
744
+ action,
745
+ ...message ? { message } : {},
746
+ timestamp: event.timestamp
747
+ });
748
+ this.scheduleHqSnapshot(mailboxId);
749
+ }).catch(() => {
750
+ }).finally(() => {
751
+ this.hqEventInFlight = void 0;
752
+ this.drainHqEvents();
753
+ });
754
+ this.hqEventInFlight = inFlight;
755
+ }
756
+ publishHqRegistryEvent(event, payload) {
757
+ const publisher = this.hqPublisher;
758
+ if (!publisher || this.closed || !event.startsWith("mailbox.agent_") && !event.startsWith("mailbox.client_")) {
759
+ return;
760
+ }
761
+ const mailboxId = `${path2.basename(this.projectDir)}:mailbox`;
762
+ const record = typeof payload === "object" && payload !== null ? payload : {};
763
+ const agentId = typeof record["agentId"] === "string" ? record["agentId"] : void 0;
764
+ const action = event === "mailbox.agent_registered" ? "agent.registered" : event === "mailbox.agent_heartbeat" ? "agent.heartbeat" : event === "mailbox.agent_deregistered" ? "agent.deregistered" : void 0;
765
+ if (action !== "agent.registered") {
766
+ if (action) {
767
+ publisher.publishMailboxEvent({
768
+ mailboxId,
769
+ action,
770
+ ...agentId ? { summary: agentId } : {}
771
+ });
772
+ }
773
+ if (action !== "agent.heartbeat") this.scheduleHqSnapshot(mailboxId);
774
+ return;
775
+ }
776
+ void this.getAgentStatuses().then((statuses) => {
777
+ if (this.closed) return;
778
+ const agent = agentId ? statuses.find((candidate) => candidate.agentId === agentId) : void 0;
779
+ publisher.publishMailboxEvent({
780
+ mailboxId,
781
+ action,
782
+ ...agent ? { agent } : {},
783
+ ...agentId ? { summary: agentId } : {}
784
+ });
785
+ this.scheduleHqSnapshot(mailboxId);
786
+ }).catch(() => {
787
+ });
788
+ }
789
+ };
790
+ function createProjectMailbox(options) {
791
+ return new RemoteMailbox(options);
792
+ }
793
+ function getSharedProjectMailbox(projectDir, events, hqPublisher) {
794
+ const key = path2.resolve(projectDir);
795
+ let projectCache = sharedRemoteMailboxes.get(key);
796
+ if (!projectCache) {
797
+ projectCache = {
798
+ withoutEvents: dependencyCache(),
799
+ byEvents: /* @__PURE__ */ new WeakMap()
800
+ };
801
+ sharedRemoteMailboxes.set(key, projectCache);
802
+ }
803
+ let dependencies = projectCache.withoutEvents;
804
+ if (events) {
805
+ dependencies = projectCache.byEvents.get(events) ?? dependencyCache();
806
+ projectCache.byEvents.set(events, dependencies);
807
+ }
808
+ if (!hqPublisher) {
809
+ if (!dependencies.withoutPublisher) {
810
+ const mailbox2 = new RemoteMailbox(key, events);
811
+ dependencies.withoutPublisher = mailbox2;
812
+ mailbox2.setCacheEviction(() => {
813
+ if (dependencies.withoutPublisher === mailbox2) {
814
+ delete dependencies.withoutPublisher;
815
+ }
816
+ });
817
+ }
818
+ return dependencies.withoutPublisher;
819
+ }
820
+ const publisherKey = Object(hqPublisher);
821
+ const existing = dependencies.byPublisher.get(publisherKey);
822
+ if (existing) return existing;
823
+ const mailbox = new RemoteMailbox(key, events, hqPublisher);
824
+ dependencies.byPublisher.set(publisherKey, mailbox);
825
+ mailbox.setCacheEviction(() => {
826
+ if (dependencies.byPublisher.get(publisherKey) === mailbox) {
827
+ dependencies.byPublisher.delete(publisherKey);
828
+ }
829
+ });
830
+ return mailbox;
831
+ }
832
+
833
+ // src/coordination/mailbox-tool.ts
834
+ import { createHash } from "node:crypto";
835
+ function defaultResolveProjectDir(ctx) {
836
+ return resolveProjectDir(ctx.projectRoot, wstackGlobalRoot());
837
+ }
838
+ function mailboxSessionTag(sessionId) {
839
+ return createHash("sha256").update(sessionId).digest("hex").slice(0, 8);
840
+ }
841
+ function resolveMailboxIdentity(ctx, fallbackBase = "leader") {
842
+ const fieldId = ctx.agentId && ctx.agentId !== "unknown" ? ctx.agentId : void 0;
843
+ const baseId = ctx.meta["agentId"] ?? fieldId ?? fallbackBase;
844
+ const sessionId = ctx.meta["sessionId"] ?? ctx.session?.id ?? "default";
845
+ const callerId = ctx.meta["globalAgentId"] ?? `${baseId}@${mailboxSessionTag(sessionId)}`;
846
+ const fieldName = ctx.agentName && ctx.agentName !== "Unknown Agent" ? ctx.agentName : void 0;
847
+ const name = ctx.meta["agentName"] ?? fieldName ?? baseId;
848
+ const role = ctx.meta["agentRole"];
849
+ return { baseId, callerId, name, role, sessionId };
850
+ }
851
+ function applyMailboxSendPolicy(ctx, identity, requestedTo, requestedAudience) {
852
+ const policy = ctx.meta["mailboxSendPolicy"];
853
+ const isChimeraIdentity = (value) => {
854
+ const normalized = value.trim().toLowerCase();
855
+ return normalized === "chimera" || normalized.startsWith("chimera-");
856
+ };
857
+ const chimeraSender = isChimeraIdentity(identity.baseId) || isChimeraIdentity(identity.name);
858
+ if (policy === "leaders-only" || chimeraSender) {
859
+ return { to: "leader", audience: "leaders" };
860
+ }
861
+ return {
862
+ to: requestedTo,
863
+ ...requestedAudience !== void 0 ? { audience: requestedAudience } : {}
864
+ };
865
+ }
866
+ function makeMailboxTool(opts = {}) {
867
+ const resolveMailbox = opts.resolveMailbox ?? ((ctx) => {
868
+ const dir = opts.projectDir ?? defaultResolveProjectDir(ctx);
869
+ return getSharedProjectMailbox(dir, opts.events);
870
+ });
871
+ const agentId = opts.agentId ?? "leader";
872
+ const sessionId = opts.sessionId ?? "default";
873
+ const shortHint = "Sub-commands: check (unread), send (exact/base/@session/project broadcast), ack (read/complete), query (filter), status (all agents), online (active only), unread (count).";
874
+ return {
875
+ name: "mailbox",
876
+ description: 'Low-level inter-agent mailbox with 7 actions (check, send, ack, query, status, online, unread). For most use cases, prefer the simpler `mail_send` and `mail_inbox` tools \u2014 they cover the common send/read operations with a cleaner interface. This tool is for advanced queries (filter by sender, sender session, priority, since-timestamp), agent status/online lists, and message ack/control. Use to="@session" only for session-local coordination; use "*" or a base alias when another session may be affected.',
877
+ usageHint: shortHint + " For simple send/read, use mail_send / mail_inbox instead.",
878
+ category: "Coordination",
879
+ permission: "auto",
880
+ mutating: true,
881
+ capabilities: [ToolCapabilities.COORDINATION_MAIL],
882
+ inputSchema: {
883
+ type: "object",
884
+ properties: {
885
+ action: {
886
+ type: "string",
887
+ enum: ["check", "send", "ack", "query", "status", "online", "unread"],
888
+ description: "Which mailbox operation to perform."
889
+ },
890
+ to: { type: "string", description: "Recipient agent id, base alias, '@session' for the sender's session, or '*' / 'all' for project broadcast." },
891
+ type: { type: "string", enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result", "review", "control"], description: "Required message intent. Actionable: ask (blocking question), assign (task delegation, must have specific to), steer (mid-course direction), review (passive ask). Informational: note (general FYI), btw (low-priority aside), result (completion notice), status (system update). Routing: broadcast (multi-recipient). Reserved: control (runtime only, agents cannot send)." },
892
+ subject: { type: "string", description: "Short subject line." },
893
+ body: { type: "string", description: "Full message content." },
894
+ priority: { type: "string", enum: ["low", "normal", "high"] },
895
+ audience: { type: "string", enum: ["all", "leaders"], description: 'Delivery audience. "leaders" prevents subagent consumption.' },
896
+ replyTo: { type: "string", description: "Reply to a specific message id." },
897
+ messageId: { type: "string", description: "Message id to acknowledge. Required for 'ack'." },
898
+ read: { type: "boolean", description: "Mark as read (adds read receipt)." },
899
+ markRead: { type: "boolean", description: "For action=check, add read receipts for returned messages (default true)." },
900
+ completed: { type: "boolean", description: "Mark as completed. For action=check, completes every returned message." },
901
+ outcome: { type: "string", description: "Outcome summary when marking complete." },
902
+ unreadBy: { type: "string", description: "Filter messages unread by this agent. Used by 'check'." },
903
+ incompleteOnly: { type: "boolean", description: "Only incomplete messages." },
904
+ from: { type: "string", description: "Filter by sender." },
905
+ minPriority: { type: "string", enum: ["low", "normal", "high"] },
906
+ since: { type: "string", description: "ISO8601 timestamp \u2014 only messages after this." },
907
+ sessionId: { type: "string", description: "Filter query results by sender session id." },
908
+ limit: { type: "number", description: "Max messages to return." }
909
+ },
910
+ required: ["action"]
911
+ },
912
+ async execute(input, ctx) {
913
+ const mb = resolveMailbox(ctx);
914
+ const i = input ?? {};
915
+ const action = i.action;
916
+ const identity = resolveMailboxIdentity(ctx, agentId);
917
+ const baseCallerId = identity.baseId;
918
+ const callerId = identity.callerId;
919
+ const callerSessionId = ctx.meta["sessionId"] ?? (ctx.session?.id ?? sessionId);
920
+ try {
921
+ await mb.registerAgent({
922
+ agentId: callerId,
923
+ sessionId: callerSessionId,
924
+ name: identity.name,
925
+ role: identity.role,
926
+ pid: process.pid,
927
+ source: ctx.meta["source"] ?? "cli"
928
+ });
929
+ } catch {
930
+ }
931
+ try {
932
+ await mb.heartbeat({ agentId: callerId });
933
+ } catch {
934
+ }
935
+ switch (action) {
936
+ case "check":
937
+ return executeCheck(mb, callerId, callerSessionId, [baseCallerId], identity.role, i);
938
+ case "send":
939
+ return executeSend(mb, identity, callerSessionId, i, ctx);
940
+ case "ack":
941
+ return executeAck(mb, callerId, i);
942
+ case "query":
943
+ return executeQuery(mb, callerId, callerSessionId, identity.role, i);
944
+ case "status":
945
+ return executeStatus(mb);
946
+ case "online":
947
+ return executeOnline(mb);
948
+ case "unread":
949
+ return executeUnread(mb, callerId, callerSessionId, [baseCallerId], identity.role);
950
+ default:
951
+ return { ok: false, error: `Unknown action: "${action}". Use check, send, ack, query, status, online, or unread.` };
952
+ }
953
+ }
954
+ };
955
+ }
956
+ async function executeCheck(mb, agentId, sessionId, aliases, role, i) {
957
+ const limit = i.limit ?? 20;
958
+ const markRead = i.markRead ?? true;
959
+ const completed = i.completed ?? false;
960
+ const outcome = i.outcome;
961
+ const targets = [
962
+ agentId,
963
+ ...aliases.filter((al) => al && al !== agentId),
964
+ `@session:${sessionId}`
965
+ ];
966
+ const batches = await Promise.all(
967
+ targets.map(
968
+ (to) => mb.query({ to, unreadBy: agentId, readerRole: role, limit, minPriority: "low" }).catch(() => [])
969
+ )
970
+ );
971
+ const seen = /* @__PURE__ */ new Set();
972
+ const candidates = batches.flat().filter((m) => {
973
+ if (seen.has(m.id)) return false;
974
+ if (!isMailboxMessageVisibleTo(m, agentId, role)) return false;
975
+ seen.add(m.id);
976
+ return true;
977
+ });
978
+ const messages = [];
979
+ for (const m of candidates) {
980
+ if (await acceptMailboxMessageForSession(m, sessionId)) {
981
+ messages.push(m);
982
+ }
983
+ }
984
+ const acked = markRead || completed ? await mb.ackMany({
985
+ acks: messages.map((m) => ({
986
+ messageId: m.id,
987
+ readerId: agentId,
988
+ read: markRead,
989
+ completed,
990
+ outcome: completed ? outcome : void 0
991
+ }))
992
+ }).catch(() => messages) : messages;
993
+ return {
994
+ ok: true,
995
+ count: acked.length,
996
+ messages: acked.map((m) => formatMessage(m, agentId)),
997
+ summary: acked.length === 0 ? "No unread messages." : `${acked.length} unread message(s)${markRead ? " (marked read)" : ""}${completed ? " (completed)" : ""}.`
998
+ };
999
+ }
1000
+ async function executeSend(mb, identity, sessionId, i, ctx) {
1001
+ const to = i.to;
1002
+ const tp = i.type;
1003
+ const subject = i.subject;
1004
+ const body = i.body;
1005
+ const audience = i.audience;
1006
+ if (!to) return { ok: false, error: '"to" is required.' };
1007
+ if (!tp) return { ok: false, error: '"type" is required.' };
1008
+ if (!subject) return { ok: false, error: '"subject" is required.' };
1009
+ if (body === void 0 || body === null) return { ok: false, error: '"body" is required.' };
1010
+ if (audience !== void 0 && audience !== "all" && audience !== "leaders") {
1011
+ return { ok: false, error: '"audience" must be "all" or "leaders".' };
1012
+ }
1013
+ let normalizedTo;
1014
+ try {
1015
+ normalizedTo = normalizeRecipient(to, sessionId);
1016
+ } catch (err) {
1017
+ return { ok: false, error: `"to" is invalid: ${toErrorMessage(err)}` };
1018
+ }
1019
+ const delivery = applyMailboxSendPolicy(ctx, identity, normalizedTo, audience);
1020
+ const typeResult = resolveSendTypeSafe(tp, delivery.to);
1021
+ if (!typeResult.ok) return { ok: false, error: `"type" is invalid: ${typeResult.error}` };
1022
+ const msg = await mb.send({
1023
+ from: identity.callerId,
1024
+ to: delivery.to,
1025
+ type: typeResult.type,
1026
+ subject,
1027
+ body,
1028
+ audience: delivery.audience,
1029
+ priority: i.priority ?? "normal",
1030
+ replyTo: i.replyTo,
1031
+ senderSessionId: sessionId,
1032
+ // Same scoping rule as `mail_send`: with four tabs live, every one of them
1033
+ // has a leader on this mailbox and an unscoped "to: leader" is accepted by
1034
+ // all four. Only the ambiguous alias, and only from a sender that carries
1035
+ // an explicit owning stamp — see `scopeAgentMailToOwningSession`.
1036
+ ...typeof ctx.meta["sessionId"] === "string" && ctx.meta["sessionId"].length > 0 && delivery.to.trim().toLowerCase() === "leader" ? { sessionAffinity: { sessionId } } : {}
1037
+ });
1038
+ return {
1039
+ ok: true,
1040
+ messageId: msg.id,
1041
+ to: msg.to,
1042
+ type: msg.type,
1043
+ timestamp: msg.timestamp,
1044
+ summary: `Message sent to ${msg.to === "*" ? "all agents" : msg.to}. Id: ${msg.id}`
1045
+ };
1046
+ }
1047
+ async function executeAck(mb, agentId, i) {
1048
+ const messageId = i.messageId;
1049
+ if (!messageId) return { ok: false, error: '"messageId" is required.' };
1050
+ const updated = await mb.ack({
1051
+ messageId,
1052
+ readerId: agentId,
1053
+ read: i.read,
1054
+ completed: i.completed,
1055
+ outcome: i.outcome
1056
+ });
1057
+ if (!updated) return { ok: false, error: `Message "${messageId}" not found.` };
1058
+ return {
1059
+ ok: true,
1060
+ messageId: updated.id,
1061
+ readBy: Object.keys(updated.readBy),
1062
+ readByCount: Object.keys(updated.readBy).length,
1063
+ completed: updated.completed,
1064
+ completedBy: updated.completedBy,
1065
+ outcome: updated.outcome,
1066
+ summary: `Message ${messageId} acknowledged. Read by ${Object.keys(updated.readBy).length} agent(s), Completed: ${updated.completed}.`
1067
+ };
1068
+ }
1069
+ async function executeQuery(mb, agentId, sessionId, role, i) {
1070
+ const limit = i.limit ?? 50;
1071
+ const messages = await mb.query({
1072
+ to: i.to,
1073
+ from: i.from,
1074
+ unreadBy: i.unreadBy,
1075
+ incompleteOnly: i.incompleteOnly,
1076
+ type: i.type,
1077
+ minPriority: i.minPriority,
1078
+ since: i.since,
1079
+ sessionId: i.sessionId,
1080
+ limit
1081
+ });
1082
+ const visible = [];
1083
+ for (const message of messages) {
1084
+ if (isMailboxMessageVisibleTo(message, agentId, role) && await acceptMailboxMessageForSession(message, sessionId)) {
1085
+ visible.push(message);
1086
+ }
1087
+ }
1088
+ return { ok: true, count: visible.length, messages: visible, summary: `${visible.length} message(s).` };
1089
+ }
1090
+ async function executeStatus(mb) {
1091
+ const agents = await mb.getAgentStatuses();
1092
+ return {
1093
+ ok: true,
1094
+ count: agents.length,
1095
+ agents: agents.map((a) => ({
1096
+ agentId: a.agentId,
1097
+ name: a.name,
1098
+ role: a.role,
1099
+ sessionId: a.sessionId,
1100
+ status: a.status,
1101
+ currentTool: a.currentTool,
1102
+ currentTask: a.currentTask,
1103
+ iterations: a.iterations,
1104
+ toolCalls: a.toolCalls,
1105
+ lastSeenAt: a.lastSeenAt,
1106
+ online: a.online,
1107
+ pid: a.pid,
1108
+ source: a.source
1109
+ })),
1110
+ summary: `${agents.filter((a) => a.online).length} online, ${agents.length} total.`
1111
+ };
1112
+ }
1113
+ async function executeOnline(mb) {
1114
+ const agents = await mb.getOnlineAgents();
1115
+ return {
1116
+ ok: true,
1117
+ count: agents.length,
1118
+ agents: agents.map((a) => ({
1119
+ agentId: a.agentId,
1120
+ name: a.name,
1121
+ role: a.role,
1122
+ sessionId: a.sessionId,
1123
+ status: a.status,
1124
+ currentTool: a.currentTool,
1125
+ currentTask: a.currentTask,
1126
+ lastSeenAt: a.lastSeenAt,
1127
+ source: a.source
1128
+ })),
1129
+ summary: `${agents.length} online agent(s).`
1130
+ };
1131
+ }
1132
+ async function executeUnread(mb, agentId, sessionId, aliases = [], role) {
1133
+ const targets = [
1134
+ agentId,
1135
+ ...aliases.filter((al) => al && al !== agentId),
1136
+ `@session:${sessionId}`
1137
+ ];
1138
+ const batches = await Promise.all(
1139
+ targets.map((to) => mb.query({ to, unreadBy: agentId, readerRole: role, limit: 200 }).catch(() => []))
1140
+ );
1141
+ const ids = /* @__PURE__ */ new Set();
1142
+ for (const m of batches.flat()) {
1143
+ if (!isMailboxMessageVisibleTo(m, agentId, role)) continue;
1144
+ if (await acceptMailboxMessageForSession(m, sessionId)) ids.add(m.id);
1145
+ }
1146
+ return { ok: true, count: ids.size, summary: `${ids.size} unread message(s) for you.` };
1147
+ }
1148
+ function formatMessage(m, readerId) {
1149
+ const maxBody = 2e3;
1150
+ const truncated = m.body.length > maxBody ? `${m.body.slice(0, maxBody)}\u2026 [truncated]` : m.body;
1151
+ return {
1152
+ id: m.id,
1153
+ from: m.from,
1154
+ to: m.to,
1155
+ type: m.type,
1156
+ audience: m.audience ?? "all",
1157
+ subject: m.subject,
1158
+ body: truncated,
1159
+ priority: m.priority,
1160
+ readByMe: readerId in m.readBy,
1161
+ readByCount: Object.keys(m.readBy).length,
1162
+ readBy: m.readBy,
1163
+ completed: m.completed,
1164
+ completedBy: m.completedBy,
1165
+ outcome: m.outcome,
1166
+ timestamp: m.timestamp,
1167
+ replyTo: m.replyTo,
1168
+ senderSessionId: m.senderSessionId
1169
+ };
1170
+ }
1171
+
1172
+ // src/coordination/session-note-hub.ts
1173
+ function resolveInboxSessionId(inbox) {
1174
+ return typeof inbox.sessionId === "function" ? inbox.sessionId() : inbox.sessionId;
1175
+ }
1176
+ function identityKeys(inbox) {
1177
+ const keys = /* @__PURE__ */ new Set();
1178
+ const add = (raw) => {
1179
+ if (typeof raw !== "string") return;
1180
+ const value = raw.trim().toLowerCase();
1181
+ if (!value) return;
1182
+ keys.add(value);
1183
+ keys.add(mailboxIdentityBase(value).toLowerCase());
1184
+ };
1185
+ add(inbox.agentId);
1186
+ for (const alias of inbox.aliases ?? []) add(alias);
1187
+ return keys;
1188
+ }
1189
+ var SessionNoteHub = class {
1190
+ inboxes = /* @__PURE__ */ new Set();
1191
+ buses = /* @__PURE__ */ new Map();
1192
+ register(inbox) {
1193
+ this.inboxes.add(inbox);
1194
+ const rawSid = resolveInboxSessionId(inbox);
1195
+ const sid = rawSid ? rawSid.trim().replace(/\\/g, "/") : void 0;
1196
+ if (sid && inbox.events && !this.buses.has(sid)) this.buses.set(sid, inbox.events);
1197
+ return () => {
1198
+ this.inboxes.delete(inbox);
1199
+ };
1200
+ }
1201
+ post(input) {
1202
+ const sessionId = input.sessionId.trim().replace(/\\/g, "/");
1203
+ const body = input.body.trim();
1204
+ if (!sessionId || !body) return { delivered: 0 };
1205
+ const to = input.to.trim().toLowerCase();
1206
+ const from = input.from.trim() || "unknown";
1207
+ const broadcast = to === "@session" || to === "*" || to === "all";
1208
+ const note = {
1209
+ from,
1210
+ kind: input.kind,
1211
+ body,
1212
+ ...input.subject?.trim() ? { subject: input.subject.trim() } : {}
1213
+ };
1214
+ let delivered = 0;
1215
+ const fromKey = from.toLowerCase();
1216
+ for (const inbox of this.inboxes) {
1217
+ const inboxSid = resolveInboxSessionId(inbox)?.trim().replace(/\\/g, "/");
1218
+ if (inboxSid !== sessionId) continue;
1219
+ const keys = identityKeys(inbox);
1220
+ if (keys.has(fromKey)) continue;
1221
+ if (!broadcast && !keys.has(to)) continue;
1222
+ try {
1223
+ inbox.deliver(note);
1224
+ delivered += 1;
1225
+ } catch {
1226
+ }
1227
+ }
1228
+ try {
1229
+ const bus = input.events ?? this.buses.get(sessionId);
1230
+ bus?.emit("session.note", {
1231
+ sessionId,
1232
+ from,
1233
+ to: input.to.trim(),
1234
+ kind: input.kind,
1235
+ body: note.body,
1236
+ subject: note.subject,
1237
+ ts: Date.now()
1238
+ });
1239
+ } catch {
1240
+ }
1241
+ return { delivered };
1242
+ }
1243
+ };
1244
+ var sessionNoteHub = new SessionNoteHub();
1245
+ function postSessionNote(input) {
1246
+ return sessionNoteHub.post(input);
1247
+ }
1248
+
1249
+ export {
1250
+ isMailboxProjectServerAvailable,
1251
+ MailboxProjectServerConnection,
1252
+ RemoteMailbox,
1253
+ createProjectMailbox,
1254
+ getSharedProjectMailbox,
1255
+ defaultResolveProjectDir,
1256
+ mailboxSessionTag,
1257
+ resolveMailboxIdentity,
1258
+ applyMailboxSendPolicy,
1259
+ makeMailboxTool,
1260
+ SessionNoteHub,
1261
+ sessionNoteHub,
1262
+ postSessionNote
1263
+ };
1264
+ //# sourceMappingURL=chunk-JO4MGB6Z.js.map