chainlesschain 0.166.2 → 0.166.4

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 (173) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-BnZ24WRk.js → AIOps-HcOh0ZbH.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-ivXY7MGb.js → ActionButton-DWjDqBhd.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BQS4OD-k.js → Analytics-C6FS-y3W.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-wOBuOKTO.js → AppLayout-DCN5cldF.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-BYuC4nw-.js → Artifacts-Crd-KE8P.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BIeVfBt6.js → Audit-BexuTQZc.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-hIuyhmJ7.js → BackgroundAgents-Dydkh-b5.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-B6ohdgPQ.js → Backup-BYq21wMP.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BzgYCw6h.js → BaseInput-CmNZP6yM.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-CwahvZU1.js → Chat-J6bD0zCl.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B-1IZ_k8.js → ChatBubbleRenderer-MEoaygnL.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-26EDP2x0.js → Checkbox-Ce-9vNmK.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-DYLw1Zvd.js → Codegen-zf7-Y-Eh.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DH_zAvD-.js → Col-D-TkY0FD.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DURrTXJu.js → Community-l300NA-1.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CQyBqN0p.js → Compact-D-9Q_hxc.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-a0DfgKHD.js → Compliance-DAJ8lLiQ.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-C6fY_5Ke.js → Cowork-xmzZ967S.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-DOwmpbTe.js → Cron-CeGqO5W4.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DEPOIEUs.js → Crosschain-DXN0AE-4.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DaKc-CEV.js → DID-CtIKASPr.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-C-0KLXq5.js → Dashboard-DqRwT8NX.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-CnsDukzr.js → Dropdown-ddIVljO1.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-Bi_ACD4N.js → EmailListRenderer-CcsFzV4y.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZASpnx3.js → FamilyGuardDashboard-PnlAUGcG.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-43oTtzyp.js → Federation-BlmLuQrL.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-oyVadnkZ.js → FormItemContext-dDovBj1Y.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Cwvfd6TZ.js → GenericCardRenderer-DTvGshFr.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Cgi2wvU0.js → Git-DuFR2xgA.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-l8TWDKZ0.js → Governance-9EaNcJuq.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-Dc8j6TaO.js → Inference-B-iYWH_r.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DgcO-Fwz.js → KnowledgeGraph-BGBaKiLX.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DjiZpiwa.js → Logs-G3wmXzfa.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-B5MFCi0N.js → MarkdownRenderer-hk0YwjTR.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BeDn1hFc.js → Marketplace-BNARBeAv.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-PGdVfWK6.js → McpTools-DST2cSSK.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-e76lY3a1.js → Memory-piwDLsVH.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-DKlQqot_.js → MobileBridge-D-W8zHdm.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-C4g6WOEr.js → MobileProjects-CwaYHlVP.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-1j3NpRQq.js → Mtc-C2dxnN7Q.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-DgwiVd1C.js → MtcAudit-BwPZsV3N.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-Dzg5lZZY.js → Multisig-CUt8w2al.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-D7GYhst-.js → NLProgramming-DwxErx27.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-Blcfvaic.js → Notes-B-mXAZ84.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-Dc4Y6vfr.js → NotificationSettings-Bd1fBAzd.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-CCInnavw.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-DBXq024L.js → Organization-DTTONYZT.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-CFPbdx1Y.js → Overflow-H6lCZ1rE.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-w7z61NR5.js → P2P-DyBZzeYW.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-B7eGGZOa.js → PdhVaultBrowser-jj7LhT04.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-Df5Cl50D.js → Permissions-B5nSQwsr.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-C-We7RF6.js → PersonalDataHub-CACaQtlY.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-B6fHPnKy.js → Pipeline-5y-DR_Bw.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-B85BdiIc.js → Privacy-BMSA3Ehd.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-CcOyazSe.js → ProjectInit-VCOj1dTa.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-BE53KfuU.js → ProjectSettings-CJaXO_iE.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-DOvMOoe_.js → Projects-C21-MQ3H.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-5l7sT9lq.js → Providers-DZ1FNKUR.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-DE37RXy4.js → QrScannerModal-6DKc9UBX.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BGmGCo7j.js → QuickAsk-Dyd0Ckrk.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BFdx8Nrm.js → Recommend-CYW4-GJH.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-C1w-j_hB.js → RemoteSession-DLrfPy4F.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-BOvn-10y.js → Reputation-DfqPo1FI.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-db-a1YO9.js → Row-CR89H2x5.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-DeS43ZGM.js → RssFeed-B9QY7R-F.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-BaQYNfeb.js → Search-CUDnzu4w.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-yt1ESHDg.js → Security-CJ0CtM_Z.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-BcNqPUsS.js → Services-CyrQ6F5U.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-X3ceUQWf.js → Skeleton-DcwnKatQ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DmYeax6C.js → Skills-BhKkYF3_.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-C_fdfbuJ.js → Sla-CWh-awh7.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-CDqohXXQ.js → SpeechSettings-BaUMNzx7.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B25maYEY.js → SyncSettings-D1Ijwwxl.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-DFqjHi3T.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-DUH0iuc4.js → Templates-Ct-9_ki3.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DrvhHf9T.js → Tenant-zBkgyilh.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-C2lJVYHA.js → Terminal-VVO2Hjfi.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-C1Hf_jYn.js → TimelineRenderer-CX9pIkx3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DniS1NpS.js → Tokens-DHaD6e_m.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-4jrcb8oA.js → Trigger-CFq9j3WV.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DiBte55w.js → Trust-DuF7gIYX.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CZRtc4VB.js → UkeySign-DbuidXJX.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BtYGJDmg.js → VideoEditing-B8IA9_qE.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D1nrTm7H.js → Wallet-DdCglSIK.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-DI91acpx.js → WebAuthn-CeteRsA8.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BtQjI50d.js → WorkflowEditor-DJhIbG__.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-I4A-f4FC.js → chat-srOi_Wef.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-QZkDkFVt.js → colors-HpVHsNGK.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item--FtCdzpr.js → compact-item-DsLhrqWe.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CaIKIKxP.js → createContext-aOasIUuE.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-B_acr36n.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DLYUII6-.js → hasIn-D5BVMimS.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DGLHSCHM.js → index-B615KD2o.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-3rVtgqa2.js → index-BCp70IlO.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Deg2S9ZJ.js → index-BD64r0Tx.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-yayaayEA.js → index-BGBvkNN3.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B65QxJaG.js → index-BNbsjeEc.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BxPoPofq.js → index-BTwDhhvl.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C3PnIEOH.js → index-BigtYchO.js} +1 -1
  102. package/src/assets/web-panel/assets/index-BnLYpdox.js +1 -0
  103. package/src/assets/web-panel/assets/{index-BLGJmgmx.js → index-Bu6TVdgQ.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CL4c5D6G.js → index-C4tPtHU0.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Du2wUavy.js → index-CGVHW0nF.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DA_TPdzX.js → index-CQEoGqMG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Ch0IFpGy.js → index-CQXp_uNd.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CBtII-nl.js → index-CSxJ4oRd.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-21I79XSf.js → index-CUUs12L9.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DAuf8r5o.js → index-CVp_dHCs.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bs0qPPFb.js → index-ChGUPEwS.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DVitI3UQ.js → index-CvhVCHk-.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DK2r6Bv7.js → index-D1xZMQrN.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Ooo_5jHo.js → index-D2XRXkZU.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DJrd_CR3.js → index-DQ5j1VKg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CXaeZCRP.js → index-DRCIPsdm.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CQ7OBHka.js → index-DV4y5rUj.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-eTqL91aV.js → index-DcLBzIiI.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CisYjy88.js → index-DiXsis7m.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D4CXFU-w.js → index-Dpj-rEoV.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BebTT7iR.js → index-Dum_r_oY.js} +1 -1
  122. package/src/assets/web-panel/assets/index-Dzmwsl_v.js +1 -0
  123. package/src/assets/web-panel/assets/{index-vfiDxdN2.js → index-LSz0ZOB8.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Ygi2rYL1.js → index-Ow3SRXRX.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-hfiZ-i2b.js → index-P6IMLmLT.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CgMm85BY.js → index-V0gpVTbS.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-0DYBQ-6e.js → index-b0Y0hDhX.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BGWSmE3G.js → index-dPFzlII1.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-OQPIS_am.js → index-fZnlPk7H.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BkJgWW_L.js → index-kYqi6zYp.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BvdwNnHC.js → index-lFOfvZDn.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-BcgtvJbC.js → index-q--LtRh9.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D2xETCkj.js → index-vQ6jy8Ew.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DDkTvw8k.js → initDefaultProps-1NU9yNpr.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-fkr96tN5.js → motion-BZk2hQ6c.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-CGBV0SV2.js → move-BNvublME.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DAsCliFt.js → mtc-parser-DWk5BUoL.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BM8PMmKN.js → omit-DztYxX3x.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-C6SdWoTK.js → pickAttrs-DURL8mtg.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BG0EVu-d.js → placementArrow-B820R56z.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-L10IvfYC.js → responsiveObserve-DUAkGJL1.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-TddIkbrn.js → slide-uZQeNSo6.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-FlveF2Yn.js → statusUtils-CGR1wFVq.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BrXRyebg.js → styleChecker-BIIPOMsQ.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-B7_oiDXZ.js → useFlexGapSupport-rIB9c0g2.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CQYeRYWb.js → useFs-CrJe1BJi.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-DspIkhVi.js → usePersonalDataHub-DyEoD3Qm.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BCwmwMAW.js → vnode-CjohPxI0.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-B0cEGluV.js → zoom-C5POL1_6.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/team.js +165 -53
  152. package/src/data/changelog.json +26 -0
  153. package/src/gateways/ws/remote-session-protocol.js +20 -15
  154. package/src/generated/cc-agent-protocol.schema.json +67 -1
  155. package/src/lib/agent-team/task-lease.js +718 -6
  156. package/src/lib/agent-team/team-graph-projection.js +303 -0
  157. package/src/lib/agent-team/team-message-bridge.js +83 -5
  158. package/src/lib/agent-team/team-message-tools.js +34 -1
  159. package/src/lib/agent-team/team-runner.js +1131 -9
  160. package/src/lib/agent-team/team-session-message-adapter.js +720 -0
  161. package/src/lib/app-server/cli-agent-kernel-adapter.js +1 -0
  162. package/src/lib/app-server/protocol.js +44 -8
  163. package/src/lib/app-server/server.js +8 -11
  164. package/src/lib/approval-grant-ledger.js +187 -0
  165. package/src/lib/remote-approval-bridge.js +8 -12
  166. package/src/lib/remote-approval-decision.js +87 -0
  167. package/src/lib/session-message-fabric.js +265 -3
  168. package/src/runtime/headless-stream.js +184 -19
  169. package/src/assets/web-panel/assets/OrderTableRenderer-dgM87XIn.js +0 -1
  170. package/src/assets/web-panel/assets/Tasks-3ILwauYw.js +0 -1
  171. package/src/assets/web-panel/assets/devWarning-D7qVrkrO.js +0 -1
  172. package/src/assets/web-panel/assets/index-D9Y_ZwZn.js +0 -1
  173. package/src/assets/web-panel/assets/index-D_cPes4G.js +0 -1
@@ -0,0 +1,720 @@
1
+ /**
2
+ * Team mailbox compatibility surface backed by SessionMessageFabric.
3
+ *
4
+ * The adapter intentionally keeps TeamRunner's narrow synchronous API while
5
+ * moving durable admission, ordering, TTL, offline holding, receipts and
6
+ * sender-rate buckets into one cross-process locked authority. A logical Team
7
+ * broadcast expands to recipient-scoped fabric records with one stable Team id.
8
+ */
9
+
10
+ import fs from "node:fs";
11
+ import { createHash, randomUUID } from "node:crypto";
12
+ import {
13
+ SessionMessageFabric,
14
+ SESSION_MESSAGE_FABRIC_ERROR_CODES,
15
+ } from "../session-message-fabric.js";
16
+ import {
17
+ DEFAULT_TEAM_MAILBOX_LIMITS,
18
+ TEAM_MAILBOX_ERROR_CODES,
19
+ TeamMailbox,
20
+ TeamMailboxError,
21
+ } from "./team-mailbox.js";
22
+
23
+ export const TEAM_SESSION_MESSAGE_ADAPTER_SCHEMA =
24
+ "chainlesschain.team-session-message-adapter/v1";
25
+
26
+ const ENVELOPE_SCHEMA = "chainlesschain.team-message-envelope/v1";
27
+ function clone(value) {
28
+ return JSON.parse(JSON.stringify(value));
29
+ }
30
+
31
+ function digest(value) {
32
+ return createHash("sha256").update(String(value), "utf8").digest("hex");
33
+ }
34
+
35
+ function safeName(value, label) {
36
+ const name = String(value || "").trim();
37
+ if (!name || name === "*" || name.length > 64) {
38
+ throw new TeamMailboxError(
39
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
40
+ `${label} must be a non-empty endpoint name of at most 64 characters`,
41
+ );
42
+ }
43
+ return name;
44
+ }
45
+
46
+ function jsonValue(value, label) {
47
+ try {
48
+ const serialized = JSON.stringify(value);
49
+ if (serialized === undefined) throw new TypeError("not JSON data");
50
+ return JSON.parse(serialized);
51
+ } catch (cause) {
52
+ throw new TeamMailboxError(
53
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
54
+ `${label} must be JSON-serializable`,
55
+ { cause },
56
+ );
57
+ }
58
+ }
59
+
60
+ function logicalId(stableToken) {
61
+ // 13 hexadecimal digits stay below Number.MAX_SAFE_INTEGER. Collision is
62
+ // checked against retained authority before admission and fails closed.
63
+ return Number.parseInt(digest(stableToken).slice(0, 13), 16) + 1;
64
+ }
65
+
66
+ function physicalId(stableToken, recipient) {
67
+ return `tm-${digest(stableToken).slice(0, 32)}-${digest(recipient).slice(0, 12)}`;
68
+ }
69
+
70
+ function teamError(
71
+ error,
72
+ fallbackCode = TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
73
+ ) {
74
+ if (error instanceof TeamMailboxError) return error;
75
+ const code =
76
+ error?.code === SESSION_MESSAGE_FABRIC_ERROR_CODES.MESSAGE_TOO_LARGE
77
+ ? TEAM_MAILBOX_ERROR_CODES.MESSAGE_TOO_LARGE
78
+ : error?.code === SESSION_MESSAGE_FABRIC_ERROR_CODES.MESSAGE_ID_CONFLICT
79
+ ? TEAM_MAILBOX_ERROR_CODES.ACK_CONFLICT
80
+ : fallbackCode;
81
+ return new TeamMailboxError(code, error?.message || "Team message failed", {
82
+ cause: error,
83
+ ...(Number.isSafeInteger(error?.retryAfterMs)
84
+ ? { retryAfterMs: error.retryAfterMs }
85
+ : {}),
86
+ });
87
+ }
88
+
89
+ function envelopeFor(record, teamId) {
90
+ const envelope = record?.body;
91
+ return envelope?.schema === ENVELOPE_SCHEMA && envelope.teamId === teamId
92
+ ? envelope
93
+ : null;
94
+ }
95
+
96
+ export class TeamSessionMessageAdapter {
97
+ constructor({
98
+ statePath,
99
+ teamId,
100
+ recipients = [],
101
+ now = () => Date.now(),
102
+ createId = randomUUID,
103
+ maxMessageBytes = DEFAULT_TEAM_MAILBOX_LIMITS.maxMessageBytes,
104
+ maxMessages = DEFAULT_TEAM_MAILBOX_LIMITS.maxMessages,
105
+ maxTotalBytes = DEFAULT_TEAM_MAILBOX_LIMITS.maxTotalBytes,
106
+ maxReceiptHistory = DEFAULT_TEAM_MAILBOX_LIMITS.maxReceiptHistory,
107
+ maxMessagesPerSenderWindow,
108
+ senderRateWindowMs,
109
+ defaultTtlMs,
110
+ maxTtlMs,
111
+ } = {}) {
112
+ if (!statePath) throw new TypeError("team message statePath is required");
113
+ this.statePath = String(statePath);
114
+ this.teamId = safeName(teamId, "teamId");
115
+ this._now = typeof now === "function" ? now : () => now;
116
+ this._createId = createId;
117
+ this._recipients = new Set();
118
+ this._limits = {
119
+ maxMessageBytes,
120
+ maxMessages,
121
+ maxTotalBytes,
122
+ maxReceiptHistory,
123
+ };
124
+ this.fabric = new SessionMessageFabric({
125
+ statePath: this.statePath,
126
+ now: this._now,
127
+ createId,
128
+ maxMessageBytes,
129
+ maxPendingPerRecipient: maxMessages,
130
+ maxPendingBytes: maxTotalBytes,
131
+ maxReceiptHistory,
132
+ ...(maxMessagesPerSenderWindow == null
133
+ ? {}
134
+ : { maxMessagesPerSenderWindow }),
135
+ ...(senderRateWindowMs == null ? {} : { senderRateWindowMs }),
136
+ ...(defaultTtlMs == null ? {} : { defaultTtlMs }),
137
+ ...(maxTtlMs == null ? {} : { maxTtlMs }),
138
+ });
139
+ this._ensureEndpoint("coordinator");
140
+ this.registerRecipients(recipients);
141
+ }
142
+
143
+ _sessionId(name) {
144
+ return `${this.teamId}:${name}`;
145
+ }
146
+
147
+ _endpoint(name) {
148
+ return this.fabric
149
+ .projection()
150
+ .endpoints.find((endpoint) => endpoint.name === name);
151
+ }
152
+
153
+ _ensureEndpoint(value) {
154
+ const name = safeName(value || "coordinator", "endpoint").toLowerCase();
155
+ const existing = this._endpoint(name);
156
+ if (existing) return existing;
157
+ return this.fabric.register({
158
+ sessionId: this._sessionId(name),
159
+ name,
160
+ idle: name !== "coordinator",
161
+ });
162
+ }
163
+
164
+ registerRecipients(recipients = []) {
165
+ const values =
166
+ typeof recipients === "string"
167
+ ? [recipients]
168
+ : Array.from(recipients || []);
169
+ for (const value of values) {
170
+ const recipient = safeName(value, "recipient").toLowerCase();
171
+ if (recipient === "coordinator") continue;
172
+ this._ensureEndpoint(recipient);
173
+ this._recipients.add(recipient);
174
+ }
175
+ return [...this._recipients];
176
+ }
177
+
178
+ setRecipientState(value, state) {
179
+ const recipient = safeName(value, "recipient").toLowerCase();
180
+ this._ensureEndpoint(recipient);
181
+ if (state === "running") {
182
+ this.fabric.reconnect(recipient);
183
+ this.fabric.setIdle(recipient, false);
184
+ } else {
185
+ this.fabric.setIdle(recipient, true);
186
+ this.fabric.disconnect(recipient);
187
+ }
188
+ return this._endpoint(recipient);
189
+ }
190
+
191
+ _records(authority = this.fabric.auditSnapshot()) {
192
+ return authority.messages
193
+ .map((record) => ({ record, envelope: envelopeFor(record, this.teamId) }))
194
+ .filter((entry) => entry.envelope);
195
+ }
196
+
197
+ _logicalMessage(envelope, delivery = null) {
198
+ return {
199
+ ...clone(envelope.message),
200
+ ...(delivery
201
+ ? {
202
+ delivery: {
203
+ recipient: envelope.recipient,
204
+ messageId: envelope.message.id,
205
+ status: delivery.status,
206
+ deliveryCount: delivery.deliveryCount || 0,
207
+ deliveredAt: delivery.deliveredAt || null,
208
+ readAt: delivery.readAt || null,
209
+ processedAt: delivery.processedAt || null,
210
+ deadLetteredAt: delivery.deadLetteredAt || null,
211
+ consumerKey: delivery.consumerKey || null,
212
+ reason: delivery.reason || null,
213
+ recipientAttempt: delivery.recipientAttempt || null,
214
+ },
215
+ }
216
+ : {}),
217
+ };
218
+ }
219
+
220
+ _existingMessages(id) {
221
+ return this._records().filter(({ envelope }) => envelope.message.id === id);
222
+ }
223
+
224
+ send({
225
+ from = "coordinator",
226
+ to,
227
+ subject = null,
228
+ body = null,
229
+ mode = "send",
230
+ idempotencyKey = null,
231
+ causationId = null,
232
+ correlationId = null,
233
+ senderAttempt = null,
234
+ ttlMs = undefined,
235
+ } = {}) {
236
+ const sender = safeName(from || "coordinator", "sender").toLowerCase();
237
+ const target = String(to || "")
238
+ .trim()
239
+ .toLowerCase();
240
+ if (target !== "*") safeName(target, "recipient");
241
+ this._ensureEndpoint(sender);
242
+ const recipients =
243
+ target === "*"
244
+ ? [...this._recipients].filter((recipient) => recipient !== sender)
245
+ : [target];
246
+ if (recipients.length === 0) {
247
+ throw new TeamMailboxError(
248
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
249
+ "Team broadcast has no registered recipients",
250
+ );
251
+ }
252
+ for (const recipient of recipients) {
253
+ if (recipient !== "coordinator" && !this._recipients.has(recipient)) {
254
+ throw new TeamMailboxError(
255
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
256
+ `Unknown team message recipient: ${recipient}`,
257
+ );
258
+ }
259
+ this._ensureEndpoint(recipient);
260
+ }
261
+ const senderTaskKey = String(senderAttempt?.taskKey || "");
262
+ const stableKey =
263
+ idempotencyKey == null
264
+ ? this._createId()
265
+ : `${sender}\0${senderTaskKey}\0${String(idempotencyKey)}`;
266
+ const stableToken = `${this.teamId}\0${stableKey}`;
267
+ const id = logicalId(stableToken);
268
+ let serializedPayload;
269
+ try {
270
+ serializedPayload = JSON.stringify({
271
+ from: sender,
272
+ to: target,
273
+ subject,
274
+ body,
275
+ mode: mode === "followup" ? "followup" : "send",
276
+ causationId,
277
+ correlationId,
278
+ });
279
+ } catch (error) {
280
+ throw teamError(error);
281
+ }
282
+ const payloadDigest = digest(serializedPayload);
283
+ const existing = this._existingMessages(id);
284
+ for (const entry of existing) {
285
+ if (entry.envelope.message.payloadDigest !== `sha256:${payloadDigest}`) {
286
+ throw new TeamMailboxError(
287
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
288
+ "Team logical message id collided with different content",
289
+ );
290
+ }
291
+ }
292
+ const existingRecipients = new Set(
293
+ existing.map(({ envelope }) => envelope.recipient),
294
+ );
295
+ const message = existing[0]
296
+ ? clone(existing[0].envelope.message)
297
+ : jsonValue(
298
+ {
299
+ id,
300
+ from: sender,
301
+ to: target,
302
+ subject,
303
+ body,
304
+ ts: this._now(),
305
+ ...(mode === "followup" ? { mode: "followup" } : {}),
306
+ ...(idempotencyKey == null
307
+ ? {}
308
+ : { idempotencyKey: String(idempotencyKey) }),
309
+ ...(causationId == null ? {} : { causationId }),
310
+ ...(correlationId == null ? {} : { correlationId }),
311
+ ...(senderAttempt == null ? {} : { senderAttempt }),
312
+ payloadDigest: `sha256:${payloadDigest}`,
313
+ },
314
+ "team message",
315
+ );
316
+ try {
317
+ for (const recipient of recipients) {
318
+ if (existingRecipients.has(recipient)) continue;
319
+ const receipt = this.fabric.send({
320
+ from: sender,
321
+ to: recipient,
322
+ subject,
323
+ body: {
324
+ schema: ENVELOPE_SCHEMA,
325
+ teamId: this.teamId,
326
+ recipient,
327
+ message,
328
+ },
329
+ messageId: physicalId(stableToken, recipient),
330
+ retainAfterAck: true,
331
+ ...(ttlMs == null ? {} : { ttlMs }),
332
+ });
333
+ if (!["delivered", "held"].includes(receipt.status)) {
334
+ const code =
335
+ receipt.status === "full"
336
+ ? TEAM_MAILBOX_ERROR_CODES.CAPACITY_EXCEEDED
337
+ : TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID;
338
+ throw new TeamMailboxError(
339
+ code,
340
+ `Team message admission ${receipt.status}: ${receipt.reason || "unknown"}`,
341
+ { receipt, retryAfterMs: receipt.retryAfterMs || null },
342
+ );
343
+ }
344
+ }
345
+ return clone(message);
346
+ } catch (error) {
347
+ throw teamError(error);
348
+ }
349
+ }
350
+
351
+ _inbox(recipient, { receive = false, limit = 100, markRead = false } = {}) {
352
+ const target = safeName(recipient, "recipient").toLowerCase();
353
+ this._ensureEndpoint(target);
354
+ const records = receive
355
+ ? this.fabric.receive(target, { limit, markRead })
356
+ : this.fabric.inbox(target).slice(0, limit);
357
+ return records
358
+ .map((record) => {
359
+ const envelope = envelopeFor(record, this.teamId);
360
+ return envelope
361
+ ? this._logicalMessage(envelope, record.delivery || null)
362
+ : null;
363
+ })
364
+ .filter(Boolean);
365
+ }
366
+
367
+ peek(recipient) {
368
+ return this._inbox(recipient);
369
+ }
370
+
371
+ receive(recipient, options = {}) {
372
+ return this._inbox(recipient, { ...options, receive: true });
373
+ }
374
+
375
+ drain(recipient) {
376
+ const messages = this.receive(recipient, { limit: 100, markRead: true });
377
+ if (messages.length > 0) {
378
+ this.acknowledge(recipient, {
379
+ messageIds: messages.map((message) => message.id),
380
+ consumerKey: `legacy-drain:${recipient}`,
381
+ status: "processed",
382
+ });
383
+ }
384
+ return messages.map((message) => {
385
+ const result = clone(message);
386
+ delete result.delivery;
387
+ return result;
388
+ });
389
+ }
390
+
391
+ acknowledge(
392
+ recipient,
393
+ {
394
+ messageIds = [],
395
+ consumerKey,
396
+ status = "processed",
397
+ reason = null,
398
+ recipientAttempt = null,
399
+ } = {},
400
+ ) {
401
+ const target = safeName(recipient, "recipient").toLowerCase();
402
+ if (
403
+ !Array.isArray(messageIds) ||
404
+ messageIds.length === 0 ||
405
+ messageIds.some((id) => !Number.isSafeInteger(id) || id <= 0)
406
+ ) {
407
+ throw new TeamMailboxError(
408
+ TEAM_MAILBOX_ERROR_CODES.ACK_INVALID,
409
+ "Team message ids must be positive integers",
410
+ );
411
+ }
412
+ const records = this._records();
413
+ const physicalIds = messageIds.map((id) => {
414
+ const match = records.find(
415
+ ({ record, envelope }) =>
416
+ envelope.recipient === target &&
417
+ envelope.message.id === id &&
418
+ record.recipientName === target,
419
+ );
420
+ if (!match) {
421
+ throw new TeamMailboxError(
422
+ TEAM_MAILBOX_ERROR_CODES.ACK_INVALID,
423
+ `Team message ${id} is not addressed to ${target}`,
424
+ );
425
+ }
426
+ return match.record.messageId;
427
+ });
428
+ try {
429
+ const receipts = this.fabric.acknowledge(target, {
430
+ messageIds: physicalIds,
431
+ consumerKey,
432
+ status,
433
+ reason,
434
+ recipientAttempt,
435
+ });
436
+ return {
437
+ receipts: receipts.map((receipt, index) => ({
438
+ recipient: target,
439
+ messageId: messageIds[index],
440
+ status: receipt.status,
441
+ deliveryCount: receipt.deliveryCount || 0,
442
+ deliveredAt: receipt.deliveredAt || null,
443
+ readAt: receipt.readAt || null,
444
+ processedAt: receipt.processedAt || null,
445
+ deadLetteredAt: receipt.deadLetteredAt || null,
446
+ consumerKey: receipt.consumerKey || null,
447
+ reason: receipt.reason || null,
448
+ recipientAttempt: receipt.recipientAttempt || null,
449
+ })),
450
+ cursor: null,
451
+ };
452
+ } catch (error) {
453
+ throw teamError(error, TEAM_MAILBOX_ERROR_CODES.ACK_INVALID);
454
+ }
455
+ }
456
+
457
+ pendingCount(recipient) {
458
+ return this.peek(recipient).length;
459
+ }
460
+
461
+ log() {
462
+ return this._logFromRecords(this._records());
463
+ }
464
+
465
+ _logFromRecords(records) {
466
+ const messages = new Map();
467
+ for (const { envelope } of records) {
468
+ messages.set(envelope.message.id, clone(envelope.message));
469
+ }
470
+ return [...messages.values()].sort(
471
+ (left, right) => left.ts - right.ts || left.id - right.id,
472
+ );
473
+ }
474
+
475
+ size() {
476
+ return this.log().length;
477
+ }
478
+
479
+ _teamReceipts(authority = this.fabric.auditSnapshot()) {
480
+ const records = this._records(authority);
481
+ const byPhysicalId = new Map(
482
+ records.map((entry) => [entry.record.messageId, entry]),
483
+ );
484
+ const receipts = [];
485
+ for (const receipt of authority.receipts) {
486
+ const entry = byPhysicalId.get(receipt.messageId);
487
+ if (!entry || receipt.status === "held") continue;
488
+ const recipient = entry.envelope.recipient;
489
+ const mappedStatus = ["read", "processed", "dead_letter"].includes(
490
+ receipt.status,
491
+ )
492
+ ? receipt.status
493
+ : "delivered";
494
+ receipts.push([
495
+ `${recipient}\0${entry.envelope.message.id}`,
496
+ {
497
+ recipient,
498
+ messageId: entry.envelope.message.id,
499
+ status: mappedStatus,
500
+ deliveryCount: receipt.deliveryCount || 0,
501
+ deliveredAt: receipt.deliveredAt || null,
502
+ readAt: receipt.readAt || null,
503
+ processedAt: receipt.processedAt || null,
504
+ deadLetteredAt: receipt.deadLetteredAt || null,
505
+ consumerKey: receipt.consumerKey || null,
506
+ reason: receipt.reason || null,
507
+ recipientAttempt: receipt.recipientAttempt || null,
508
+ },
509
+ ]);
510
+ }
511
+ return receipts;
512
+ }
513
+
514
+ _pressureFromAuthority(authority) {
515
+ const counts = new Map();
516
+ let pendingBytes = 0;
517
+ for (const message of authority.messages) {
518
+ if (
519
+ !["delivered", "held"].includes(message.status) ||
520
+ message.acknowledgedAt != null
521
+ ) {
522
+ continue;
523
+ }
524
+ counts.set(
525
+ message.recipientAuthority,
526
+ (counts.get(message.recipientAuthority) || 0) + 1,
527
+ );
528
+ pendingBytes += message.messageBytes || 0;
529
+ }
530
+ const pending = Math.max(0, ...counts.values());
531
+ const messageRatio = pending / Math.max(1, this._limits.maxMessages);
532
+ const byteRatio = pendingBytes / Math.max(1, this._limits.maxTotalBytes);
533
+ const ratio = Math.max(messageRatio, byteRatio);
534
+ return {
535
+ ratio,
536
+ messageRatio,
537
+ byteRatio,
538
+ level:
539
+ ratio >= 1
540
+ ? "full"
541
+ : ratio >= 0.95
542
+ ? "critical"
543
+ : ratio >= 0.8
544
+ ? "high"
545
+ : "normal",
546
+ full: ratio >= 1,
547
+ };
548
+ }
549
+
550
+ pressure() {
551
+ return this._pressureFromAuthority(this.fabric.auditSnapshot());
552
+ }
553
+
554
+ status() {
555
+ const authority = this.fabric.auditSnapshot();
556
+ const log = this._logFromRecords(this._records(authority));
557
+ const receipts = this._teamReceipts(authority).map(
558
+ ([, receipt]) => receipt,
559
+ );
560
+ const totalBytes = Buffer.byteLength(JSON.stringify(log), "utf8");
561
+ return {
562
+ messages: log.length,
563
+ totalBytes,
564
+ limits: { ...this._limits },
565
+ recipients: [...this._recipients],
566
+ registeredRecipients: this._recipients.size,
567
+ pressure: this._pressureFromAuthority(authority),
568
+ counters: {
569
+ acceptedMessages: log.length,
570
+ acceptedBytes: totalBytes,
571
+ rejectedMessages: 0,
572
+ rejectedBytes: 0,
573
+ compactionRuns: 0,
574
+ compactedMessages: 0,
575
+ compactedBytes: 0,
576
+ deliveryAttempts: receipts.reduce(
577
+ (sum, receipt) => sum + (receipt.deliveryCount || 0),
578
+ 0,
579
+ ),
580
+ processedMessages: receipts.filter(
581
+ (receipt) => receipt.status === "processed",
582
+ ).length,
583
+ deadLetteredMessages: receipts.filter(
584
+ (receipt) => receipt.status === "dead_letter",
585
+ ).length,
586
+ idempotentReplays: 0,
587
+ },
588
+ receipts: receipts.length,
589
+ idempotencyRecords: 0,
590
+ authority: "session-message-fabric",
591
+ fabric: {
592
+ revision: authority.revision,
593
+ digest: authority.digest,
594
+ },
595
+ };
596
+ }
597
+
598
+ snapshot() {
599
+ const authority = this.fabric.auditSnapshot();
600
+ const log = this._logFromRecords(this._records(authority));
601
+ const receipts = this._teamReceipts(authority);
602
+ const receiptValues = receipts.map(([, receipt]) => receipt);
603
+ const totalBytes = Buffer.byteLength(JSON.stringify(log), "utf8");
604
+ const counters = {
605
+ acceptedMessages: log.length,
606
+ acceptedBytes: totalBytes,
607
+ rejectedMessages: 0,
608
+ rejectedBytes: 0,
609
+ compactionRuns: 0,
610
+ compactedMessages: 0,
611
+ compactedBytes: 0,
612
+ deliveryAttempts: receiptValues.reduce(
613
+ (sum, receipt) => sum + (receipt.deliveryCount || 0),
614
+ 0,
615
+ ),
616
+ processedMessages: receiptValues.filter(
617
+ (receipt) => receipt.status === "processed",
618
+ ).length,
619
+ deadLetteredMessages: receiptValues.filter(
620
+ (receipt) => receipt.status === "dead_letter",
621
+ ).length,
622
+ idempotentReplays: 0,
623
+ };
624
+ return {
625
+ version: 4,
626
+ authority: "session-message-fabric",
627
+ adapterSchema: TEAM_SESSION_MESSAGE_ADAPTER_SCHEMA,
628
+ statePath: this.statePath,
629
+ teamId: this.teamId,
630
+ fabricRevision: authority.revision,
631
+ fabricDigest: authority.digest,
632
+ limits: { ...this._limits },
633
+ log,
634
+ seq: 0,
635
+ delivered: [],
636
+ recipients: [...this._recipients],
637
+ totalBytes,
638
+ counters,
639
+ receipts,
640
+ idempotency: [],
641
+ };
642
+ }
643
+
644
+ static restore(snapshot, options = {}) {
645
+ if (
646
+ snapshot?.authority !== "session-message-fabric" ||
647
+ snapshot?.adapterSchema !== TEAM_SESSION_MESSAGE_ADAPTER_SCHEMA
648
+ ) {
649
+ throw new TeamMailboxError(
650
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
651
+ "Team message snapshot is not a SessionMessageFabric authority",
652
+ );
653
+ }
654
+ const statePath = options.statePath || snapshot.statePath;
655
+ if (!fs.existsSync(statePath)) {
656
+ throw new TeamMailboxError(
657
+ TEAM_MAILBOX_ERROR_CODES.MESSAGE_INVALID,
658
+ `Team message authority not found: ${statePath}`,
659
+ );
660
+ }
661
+ return new TeamSessionMessageAdapter({
662
+ ...options,
663
+ statePath,
664
+ teamId: options.teamId || snapshot.teamId,
665
+ recipients: snapshot.recipients || [],
666
+ ...(snapshot.limits || {}),
667
+ });
668
+ }
669
+
670
+ static migrateLegacy(snapshot, options = {}) {
671
+ const legacy = TeamMailbox.restore(snapshot, { now: options.now });
672
+ const adapter = new TeamSessionMessageAdapter({
673
+ ...options,
674
+ recipients: legacy.status().recipients,
675
+ });
676
+ for (const message of legacy.log()) {
677
+ adapter._importLegacyMessage(message);
678
+ }
679
+ for (const [, receipt] of snapshot.receipts || []) {
680
+ if (!["read", "processed", "dead_letter"].includes(receipt.status)) {
681
+ continue;
682
+ }
683
+ adapter.acknowledge(receipt.recipient, {
684
+ messageIds: [receipt.messageId],
685
+ consumerKey: receipt.consumerKey || `legacy:${receipt.messageId}`,
686
+ status: receipt.status,
687
+ reason: receipt.reason,
688
+ recipientAttempt: receipt.recipientAttempt,
689
+ });
690
+ }
691
+ return adapter;
692
+ }
693
+
694
+ _importLegacyMessage(message) {
695
+ const recipients =
696
+ message.to === "*"
697
+ ? [...this._recipients].filter(
698
+ (recipient) => recipient !== message.from,
699
+ )
700
+ : [message.to];
701
+ this._ensureEndpoint(message.from || "coordinator");
702
+ for (const recipient of recipients) {
703
+ this._ensureEndpoint(recipient);
704
+ const token = `${this.teamId}\0legacy\0${message.id}`;
705
+ this.fabric.send({
706
+ from: message.from || "coordinator",
707
+ to: recipient,
708
+ subject: message.subject,
709
+ body: {
710
+ schema: ENVELOPE_SCHEMA,
711
+ teamId: this.teamId,
712
+ recipient,
713
+ message: clone(message),
714
+ },
715
+ messageId: physicalId(token, recipient),
716
+ retainAfterAck: true,
717
+ });
718
+ }
719
+ }
720
+ }