chainlesschain 0.166.2 → 0.166.3

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 (170) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BnZ24WRk.js → AIOps-DZTvbk_H.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ivXY7MGb.js → ActionButton-IaEVUN3o.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BQS4OD-k.js → Analytics-CCQab0gl.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-wOBuOKTO.js → AppLayout-Bs_0GVpx.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-BYuC4nw-.js → Artifacts-Bm83-yJv.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-BIeVfBt6.js → Audit-Bah1XDR5.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-hIuyhmJ7.js → BackgroundAgents-BPZA0B5d.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-B6ohdgPQ.js → Backup-BMydWFiM.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BzgYCw6h.js → BaseInput-lhopFSZz.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CwahvZU1.js → Chat-Bbbs5BP1.js} +4 -4
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B-1IZ_k8.js → ChatBubbleRenderer-BIAfOKCj.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-26EDP2x0.js → Checkbox-CLEMA9J4.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DYLw1Zvd.js → Codegen-deCSwmnK.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DH_zAvD-.js → Col-CH-n8haq.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DURrTXJu.js → Community-DJuAl6dw.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CQyBqN0p.js → Compact-BMyLBmgs.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-a0DfgKHD.js → Compliance-BK75Ss93.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-C6fY_5Ke.js → Cowork-5XbJwKYq.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DOwmpbTe.js → Cron-Ezgir7IZ.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DEPOIEUs.js → Crosschain-CKuMoYA7.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DaKc-CEV.js → DID-CxS244cH.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-C-0KLXq5.js → Dashboard-wrUKrsJy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CnsDukzr.js → Dropdown-A6I91Z2J.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Bi_ACD4N.js → EmailListRenderer-BofaiRYY.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZASpnx3.js → FamilyGuardDashboard-D3eeNA64.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-43oTtzyp.js → Federation-DzK-3AjS.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-oyVadnkZ.js → FormItemContext-Dm6mYA8b.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Cwvfd6TZ.js → GenericCardRenderer-BeWL-Q1b.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-Cgi2wvU0.js → Git-CtGCxg9p.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-l8TWDKZ0.js → Governance-DZvFB46G.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-Dc8j6TaO.js → Inference-ntqp4cmw.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DgcO-Fwz.js → KnowledgeGraph-CrLYbqLe.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DjiZpiwa.js → Logs-CKA1PRhC.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-DtwoKxDJ.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-BeDn1hFc.js → Marketplace-B8aPsXYv.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-PGdVfWK6.js → McpTools-DQOgNdGS.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-e76lY3a1.js → Memory-i9IK9B0M.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DKlQqot_.js → MobileBridge-BynYvpv9.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-C4g6WOEr.js → MobileProjects-DixivGuQ.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-1j3NpRQq.js → Mtc-OWPvWVNr.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-DgwiVd1C.js → MtcAudit-Cqpg5C4A.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-Dzg5lZZY.js → Multisig-CT5kMJIZ.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-D7GYhst-.js → NLProgramming-BXD0Dz0h.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Blcfvaic.js → Notes-T1w5p8N5.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-Dc4Y6vfr.js → NotificationSettings-DFCThpKJ.js} +1 -1
  47. package/src/assets/web-panel/assets/OrderTableRenderer-BJOuM0Kl.js +1 -0
  48. package/src/assets/web-panel/assets/{Organization-DBXq024L.js → Organization-CSFcw86q.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-CFPbdx1Y.js → Overflow-BxVMuLTk.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-w7z61NR5.js → P2P-B8qxY_yX.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-B7eGGZOa.js → PdhVaultBrowser-Bcq27Exy.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-Df5Cl50D.js → Permissions-CGBvJVXP.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-C-We7RF6.js → PersonalDataHub-DM9IEeJ3.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-B6fHPnKy.js → Pipeline-wEskx2-p.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B85BdiIc.js → Privacy-DIA02lLJ.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CcOyazSe.js → ProjectInit-uMXlNLHV.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BE53KfuU.js → ProjectSettings-Sy3NOtt7.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-DOvMOoe_.js → Projects-xFjfOkQM.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-5l7sT9lq.js → Providers-CNyOpbg8.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DE37RXy4.js → QrScannerModal-Dk1UVRwI.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-BGmGCo7j.js → QuickAsk-B-UqLoLG.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BFdx8Nrm.js → Recommend-CiTBVZvN.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C1w-j_hB.js → RemoteSession-BhABd516.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BOvn-10y.js → Reputation-Dq1xTCcN.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-db-a1YO9.js → Row-DbFc4sc7.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-DeS43ZGM.js → RssFeed-D7IBkkLm.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-BaQYNfeb.js → Search-BVyMlGoa.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-yt1ESHDg.js → Security-BzMm9Be_.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-BcNqPUsS.js → Services-Dfk0kied.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-X3ceUQWf.js → Skeleton-C8B5mUit.js} +1 -1
  71. package/src/assets/web-panel/assets/Skills-BNTPoJn0.js +1 -0
  72. package/src/assets/web-panel/assets/{Sla-C_fdfbuJ.js → Sla-gnFelKfT.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-CDqohXXQ.js → SpeechSettings-B12gCQkK.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-B25maYEY.js → SyncSettings-CHWk9cpj.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-B_zs9jkJ.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-DUH0iuc4.js → Templates-PjdppQrI.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DrvhHf9T.js → Tenant-CH3aARxE.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-C2lJVYHA.js → Terminal-BqqrYyg3.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-C1Hf_jYn.js → TimelineRenderer-Dx9soq9f.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DniS1NpS.js → Tokens-C66KpDDZ.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-4jrcb8oA.js → Trigger-DHxgGsap.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DiBte55w.js → Trust-3cct4QxN.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CZRtc4VB.js → UkeySign-Cs6UfDEH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BtYGJDmg.js → VideoEditing-jbu2EAQa.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D1nrTm7H.js → Wallet-BIykFB9D.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DI91acpx.js → WebAuthn-DFgqKmHG.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BtQjI50d.js → WorkflowEditor-CNg5bBiH.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-I4A-f4FC.js → chat-B-6wzojE.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-QZkDkFVt.js → colors-BHAq7y9y.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item--FtCdzpr.js → compact-item-CGJnSTnu.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CaIKIKxP.js → createContext-Cx0ZF512.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CNq_RRw4.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DLYUII6-.js → hasIn-DibIw3z7.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-OQPIS_am.js → index-0NWSa3Xq.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-3rVtgqa2.js → index-746-mNhw.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BxPoPofq.js → index-Aaeca8o4.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D4CXFU-w.js → index-B1jAbIxx.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BLGJmgmx.js → index-B28jdxwR.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CL4c5D6G.js → index-B2rOMBeN.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-vfiDxdN2.js → index-BAoFiV3n.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Du2wUavy.js → index-BCtW4F8-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DAuf8r5o.js → index-BEXM3qSV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BGWSmE3G.js → index-BFTGp-aF.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-eTqL91aV.js → index-BJhS6i02.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CQ7OBHka.js → index-BO-XxBD5.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-hfiZ-i2b.js → index-BR2BiNtx.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CBtII-nl.js → index-BRflLAdr.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Ch0IFpGy.js → index-BTWb3sbj.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B65QxJaG.js → index-BZ7GbrgF.js} +1 -1
  110. package/src/assets/web-panel/assets/index-B_H-RZ9i.js +1 -0
  111. package/src/assets/web-panel/assets/{index-BebTT7iR.js → index-BiMLsIwd.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Ygi2rYL1.js → index-Bmqjip63.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DGLHSCHM.js → index-BoySHSgZ.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DJrd_CR3.js → index-Bz3J0eVB.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DK2r6Bv7.js → index-C6M2yHVE.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-yayaayEA.js → index-CH2A_Fdn.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Ooo_5jHo.js → index-CUX9mq3M.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D2xETCkj.js → index-CY2E4Y99.js} +1 -1
  119. package/src/assets/web-panel/assets/index-CkH9UQQL.js +1 -0
  120. package/src/assets/web-panel/assets/{index-CisYjy88.js → index-CoKu7Q9r.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DA_TPdzX.js → index-CrjkH_QU.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Bs0qPPFb.js → index-Cuity3gy.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BkJgWW_L.js → index-CwcWpW1q.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Deg2S9ZJ.js → index-Cz5ety6j.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CXaeZCRP.js → index-DXNcm44v.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DVitI3UQ.js → index-DjCJIttz.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CgMm85BY.js → index-DnsVRqCa.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-21I79XSf.js → index-SbQacweG.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BcgtvJbC.js → index-g0V_mLQ9.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BvdwNnHC.js → index-puIs3mTs.js} +3 -3
  131. package/src/assets/web-panel/assets/{index-0DYBQ-6e.js → index-sI_1E0co.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C3PnIEOH.js → index-woOjtit-.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DDkTvw8k.js → initDefaultProps-Gbl9QOqg.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-fkr96tN5.js → motion-BHh6ZzZu.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-CGBV0SV2.js → move-mX6Rwrd1.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DAsCliFt.js → mtc-parser-CuzZa_kI.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BM8PMmKN.js → omit-DjANYkpi.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-C6SdWoTK.js → pickAttrs-C_3v40wY.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BG0EVu-d.js → placementArrow-L2HgRede.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-L10IvfYC.js → responsiveObserve-DIkGMHyT.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-TddIkbrn.js → slide-C1hp5tD0.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-FlveF2Yn.js → statusUtils-DH1W2uFA.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BrXRyebg.js → styleChecker-dEaKQcv2.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-B7_oiDXZ.js → useFlexGapSupport-CD7j1zxt.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-CQYeRYWb.js → useFs-C8jXMvEM.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-DspIkhVi.js → usePersonalDataHub-C4r-8mys.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-BCwmwMAW.js → vnode-DDuI6wgY.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B0cEGluV.js → zoom-Dd-DteT6.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/team.js +165 -53
  151. package/src/data/changelog.json +13 -0
  152. package/src/lib/agent-team/task-lease.js +718 -6
  153. package/src/lib/agent-team/team-graph-projection.js +303 -0
  154. package/src/lib/agent-team/team-message-bridge.js +83 -5
  155. package/src/lib/agent-team/team-message-tools.js +34 -1
  156. package/src/lib/agent-team/team-runner.js +1131 -9
  157. package/src/lib/agent-team/team-session-message-adapter.js +720 -0
  158. package/src/lib/app-server/cli-agent-kernel-adapter.js +1 -0
  159. package/src/lib/app-server/protocol.js +43 -0
  160. package/src/lib/app-server/server.js +8 -11
  161. package/src/lib/approval-grant-ledger.js +187 -0
  162. package/src/lib/session-message-fabric.js +265 -3
  163. package/src/runtime/headless-stream.js +184 -19
  164. package/src/assets/web-panel/assets/MarkdownRenderer-B5MFCi0N.js +0 -1
  165. package/src/assets/web-panel/assets/OrderTableRenderer-dgM87XIn.js +0 -1
  166. package/src/assets/web-panel/assets/Skills-DmYeax6C.js +0 -1
  167. package/src/assets/web-panel/assets/Tasks-3ILwauYw.js +0 -1
  168. package/src/assets/web-panel/assets/devWarning-D7qVrkrO.js +0 -1
  169. package/src/assets/web-panel/assets/index-D9Y_ZwZn.js +0 -1
  170. package/src/assets/web-panel/assets/index-D_cPes4G.js +0 -1
@@ -162,6 +162,7 @@ export class CliAgentKernelAdapter {
162
162
  type: "approval",
163
163
  id: event.id,
164
164
  approve: approvalBoolean(decision),
165
+ decision,
165
166
  binding: event.binding,
166
167
  });
167
168
  }
@@ -53,6 +53,49 @@ export function validateAppServerMessage(value) {
53
53
  });
54
54
  }
55
55
 
56
+ const definitionSchemas = new Map();
57
+
58
+ /** Validate one canonical $defs value without maintaining a second hand-written union. */
59
+ export function validateAppServerDefinition(name, value) {
60
+ const definition = APP_SERVER_SCHEMA.$defs?.[name];
61
+ if (!definition) {
62
+ return Object.freeze({
63
+ ok: false,
64
+ errors: Object.freeze([
65
+ Object.freeze({
66
+ path: "#",
67
+ message: `unknown protocol definition ${String(name)}`,
68
+ }),
69
+ ]),
70
+ });
71
+ }
72
+ let definitionSchema = definitionSchemas.get(name);
73
+ if (!definitionSchema) {
74
+ definitionSchema = {
75
+ $schema: APP_SERVER_SCHEMA.$schema,
76
+ $ref: `#/$defs/${name}`,
77
+ $defs: APP_SERVER_SCHEMA.$defs,
78
+ };
79
+ definitionSchemas.set(name, definitionSchema);
80
+ }
81
+ const result = validate(value, definitionSchema);
82
+ return Object.freeze({
83
+ ok: result.valid,
84
+ errors: Object.freeze(
85
+ result.errors.map((error) =>
86
+ Object.freeze({
87
+ path: error.instancePath || error.schemaPath || "#",
88
+ message: error.message || error.code || "invalid value",
89
+ }),
90
+ ),
91
+ ),
92
+ });
93
+ }
94
+
95
+ export function validateApprovalDecision(value) {
96
+ return validateAppServerDefinition("ApprovalDecision", value);
97
+ }
98
+
56
99
  export function rpcResult(id, result) {
57
100
  return { jsonrpc: "2.0", id, result: result ?? null };
58
101
  }
@@ -12,6 +12,7 @@ import {
12
12
  rpcError,
13
13
  rpcNotification,
14
14
  rpcResult,
15
+ validateApprovalDecision,
15
16
  validateAppServerMessage,
16
17
  } from "./protocol.js";
17
18
 
@@ -746,28 +747,24 @@ export class CcAppServer {
746
747
  ? event.risk
747
748
  : "high",
748
749
  reason: event.reason || "The Agent Kernel requires approval",
749
- requestedPermissions: [],
750
+ requestedPermissions: Array.isArray(event.requested_permissions)
751
+ ? event.requested_permissions
752
+ : [],
750
753
  };
751
754
  }
752
755
 
753
756
  async _requestApproval(turn, event) {
754
757
  const request = this._approvalRequest(turn, event);
755
758
  const result = await this._requestClient("approval/decide", { request });
756
- const decision = requireObject(result, "approval decision");
757
- const allowed = new Set([
758
- "acceptOnce",
759
- "acceptForTurn",
760
- "acceptForSession",
761
- "decline",
762
- "cancel",
763
- ]);
764
- if (!allowed.has(decision.kind)) {
759
+ const validation = validateApprovalDecision(result);
760
+ if (!validation.ok) {
765
761
  throw new JsonRpcError(
766
762
  JSON_RPC_ERROR.INVALID_PARAMS,
767
763
  "client returned an invalid approval decision",
764
+ { errors: validation.errors },
768
765
  );
769
766
  }
770
- return decision;
767
+ return result;
771
768
  }
772
769
 
773
770
  async _completeTurn(turn, status, result) {
@@ -0,0 +1,187 @@
1
+ import { createHash } from "node:crypto";
2
+ import { validateAppServerDefinition } from "./app-server/protocol.js";
3
+
4
+ export const APPROVAL_GRANTS_EVENT = "approval_grants";
5
+ export const APPROVAL_GRANTS_SCHEMA = "chainlesschain.approval-grants/v1";
6
+
7
+ function stableValue(value) {
8
+ if (Array.isArray(value)) return value.map(stableValue);
9
+ if (!value || typeof value !== "object") return value;
10
+ return Object.fromEntries(
11
+ Object.keys(value)
12
+ .filter((key) => value[key] !== undefined)
13
+ .sort()
14
+ .map((key) => [key, stableValue(value[key])]),
15
+ );
16
+ }
17
+
18
+ function stableJson(value) {
19
+ return JSON.stringify(stableValue(value));
20
+ }
21
+
22
+ function boundedScope(value) {
23
+ const serialized = stableJson(value);
24
+ if (serialized.length <= 1024) return serialized;
25
+ return `sha256:${createHash("sha256").update(serialized).digest("hex")}`;
26
+ }
27
+
28
+ function boundedCapability(tool) {
29
+ const candidate = `tool:${String(tool || "unknown")}`;
30
+ if (candidate.length <= 128) return candidate;
31
+ return `tool:sha256:${createHash("sha256").update(candidate).digest("hex")}`;
32
+ }
33
+
34
+ export function approvalPermissionForContext(ctx = {}, { cwd = null } = {}) {
35
+ const tool = ctx.tool || ctx.toolName || ctx.name || "unknown";
36
+ const args =
37
+ ctx.args ?? (ctx.command != null ? { command: ctx.command } : null);
38
+ const policy = ctx.rule || ctx.riskLevel || ctx.risk || null;
39
+ return Object.freeze({
40
+ capability: boundedCapability(tool),
41
+ scope: boundedScope({ cwd: cwd || ctx.cwd || null, args, policy }),
42
+ });
43
+ }
44
+
45
+ function permissionKey(permission) {
46
+ return `${permission.capability}\0${permission.scope}`;
47
+ }
48
+
49
+ function livePermission(permission, now) {
50
+ const validation = validateAppServerDefinition("PermissionGrant", permission);
51
+ if (!validation.ok) return false;
52
+ return !permission.expiresAt || Date.parse(permission.expiresAt) > now;
53
+ }
54
+
55
+ function samePermission(left, right) {
56
+ return permissionKey(left) === permissionKey(right);
57
+ }
58
+
59
+ function normalizePersistedEntry(entry, now) {
60
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
61
+ if (!livePermission(entry.permission, now)) return null;
62
+ if (
63
+ typeof entry.binding !== "string" ||
64
+ !entry.binding ||
65
+ typeof entry.grantedAt !== "string" ||
66
+ Number.isNaN(Date.parse(entry.grantedAt))
67
+ ) {
68
+ return null;
69
+ }
70
+ return Object.freeze({
71
+ permission: Object.freeze({ ...entry.permission }),
72
+ binding: entry.binding,
73
+ grantedAt: entry.grantedAt,
74
+ });
75
+ }
76
+
77
+ export class ApprovalGrantLedger {
78
+ constructor({ sessionId, now = () => Date.now() } = {}) {
79
+ if (!sessionId) throw new TypeError("approval grant sessionId is required");
80
+ this.sessionId = String(sessionId);
81
+ this.now = now;
82
+ this.turnId = null;
83
+ this.turnGrants = new Map();
84
+ this.sessionGrants = new Map();
85
+ this.revision = 0;
86
+ }
87
+
88
+ beginTurn(turnId) {
89
+ this.turnId = String(turnId);
90
+ this.turnGrants.clear();
91
+ }
92
+
93
+ allows(permission) {
94
+ const now = this.now();
95
+ const key = permissionKey(permission);
96
+ for (const grants of [this.turnGrants, this.sessionGrants]) {
97
+ const entry = grants.get(key);
98
+ if (!entry) continue;
99
+ if (livePermission(entry.permission, now)) return true;
100
+ grants.delete(key);
101
+ }
102
+ return false;
103
+ }
104
+
105
+ applyDecision(decision, requiredPermission, binding) {
106
+ if (
107
+ decision?.kind !== "acceptForTurn" &&
108
+ decision?.kind !== "acceptForSession"
109
+ ) {
110
+ return Object.freeze({
111
+ decision,
112
+ granted: Object.freeze([]),
113
+ persistedScope: false,
114
+ });
115
+ }
116
+ const candidates =
117
+ decision.permissions === undefined
118
+ ? [requiredPermission]
119
+ : decision.permissions;
120
+ const target =
121
+ decision.kind === "acceptForSession"
122
+ ? this.sessionGrants
123
+ : this.turnGrants;
124
+ const granted = candidates
125
+ .filter(
126
+ (permission) =>
127
+ livePermission(permission, this.now()) &&
128
+ samePermission(permission, requiredPermission) &&
129
+ (target.has(permissionKey(permission)) || target.size < 64),
130
+ )
131
+ .slice(0, 1)
132
+ .map((permission) => Object.freeze({ ...permission }));
133
+ const grantedAt = new Date(this.now()).toISOString();
134
+ for (const permission of granted) {
135
+ target.set(
136
+ permissionKey(permission),
137
+ Object.freeze({ permission, binding, grantedAt }),
138
+ );
139
+ }
140
+ if (granted.length) this.revision += 1;
141
+ return Object.freeze({
142
+ decision: Object.freeze({ ...decision, permissions: granted }),
143
+ granted: Object.freeze(granted),
144
+ persistedScope:
145
+ decision.kind === "acceptForSession" && granted.length > 0,
146
+ });
147
+ }
148
+
149
+ toJSON() {
150
+ return {
151
+ schema: APPROVAL_GRANTS_SCHEMA,
152
+ sessionId: this.sessionId,
153
+ revision: this.revision,
154
+ grants: [...this.sessionGrants.values()].map((entry) => ({
155
+ permission: { ...entry.permission },
156
+ binding: entry.binding,
157
+ grantedAt: entry.grantedAt,
158
+ })),
159
+ };
160
+ }
161
+
162
+ static fromJSON(value, { sessionId, now = () => Date.now() } = {}) {
163
+ const ledger = new ApprovalGrantLedger({ sessionId, now });
164
+ if (
165
+ !value ||
166
+ typeof value !== "object" ||
167
+ Array.isArray(value) ||
168
+ value.schema !== APPROVAL_GRANTS_SCHEMA ||
169
+ value.sessionId !== ledger.sessionId ||
170
+ !Number.isSafeInteger(value.revision) ||
171
+ value.revision < 0 ||
172
+ !Array.isArray(value.grants) ||
173
+ value.grants.length > 64
174
+ ) {
175
+ throw new TypeError("invalid persisted approval grant ledger");
176
+ }
177
+ for (const candidate of value.grants) {
178
+ const entry = normalizePersistedEntry(candidate, now());
179
+ if (!entry) {
180
+ throw new TypeError("invalid persisted approval grant entry");
181
+ }
182
+ ledger.sessionGrants.set(permissionKey(entry.permission), entry);
183
+ }
184
+ ledger.revision = value.revision;
185
+ return ledger;
186
+ }
187
+ }
@@ -38,11 +38,15 @@ export const SESSION_MESSAGE_RECEIPT_STATUSES = Object.freeze([
38
38
  "full",
39
39
  "rate_limited",
40
40
  "expired",
41
+ "read",
42
+ "processed",
43
+ "dead_letter",
41
44
  ]);
42
45
 
43
46
  export const SESSION_MESSAGE_FABRIC_LIMITS = Object.freeze({
44
47
  maxMessageBytes: 256 * 1024,
45
48
  maxPendingPerRecipient: 100,
49
+ maxPendingBytes: 32 * 1024 * 1024,
46
50
  maxReceiptHistory: 5_000,
47
51
  maxMessagesPerSenderWindow: 120,
48
52
  senderRateWindowMs: 60_000,
@@ -62,9 +66,21 @@ export const SESSION_MESSAGE_FABRIC_ERROR_CODES = Object.freeze({
62
66
  });
63
67
 
64
68
  const PENDING_STATUSES = new Set(["held", "delivered"]);
69
+ const MESSAGE_STATUSES = new Set([
70
+ "delivered",
71
+ "held",
72
+ "refused",
73
+ "full",
74
+ "rate_limited",
75
+ "expired",
76
+ ]);
77
+ const PROCESSING_STATUSES = new Set(["read", "processed", "dead_letter"]);
78
+ const TERMINAL_PROCESSING_STATUSES = new Set(["processed", "dead_letter"]);
65
79
  const RECEIPT_STATUSES = new Set(SESSION_MESSAGE_RECEIPT_STATUSES);
66
80
  const POLICIES = new Set(SESSION_MESSAGE_POLICIES);
67
81
  const NAME_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/u;
82
+ // Bounded ids explicitly reject ASCII control characters.
83
+ // eslint-disable-next-line no-control-regex
68
84
  const ID_PATTERN = /^[^\u0000-\u001f\u007f]{1,256}$/u;
69
85
  const MESSAGE_ID_PATTERN = /^[a-zA-Z0-9._:-]{1,128}$/u;
70
86
 
@@ -324,6 +340,11 @@ function updateReceipt(state, message, status, reason, now) {
324
340
  updatedAt: now,
325
341
  idleNotifiedAt: null,
326
342
  readAt: null,
343
+ processedAt: null,
344
+ deadLetteredAt: null,
345
+ deliveryCount: 0,
346
+ consumerKey: null,
347
+ recipientAttempt: null,
327
348
  };
328
349
  state.receipts.push(receipt);
329
350
  } else {
@@ -402,6 +423,7 @@ function compactSettledMessages(state) {
402
423
  state.messages = state.messages.filter((message) => {
403
424
  if (!PENDING_STATUSES.has(message.status)) return false;
404
425
  if (message.acknowledgedAt == null) return true;
426
+ if (message.retainAfterAck === true) return true;
405
427
  if (message.notifyWhenIdle !== true) return false;
406
428
  const receipt = receiptFor(
407
429
  state,
@@ -413,6 +435,30 @@ function compactSettledMessages(state) {
413
435
  return state.messages.length !== before;
414
436
  }
415
437
 
438
+ function trimRetainedSettledMessages(state, limit) {
439
+ const retained = state.messages
440
+ .filter(
441
+ (message) =>
442
+ message.retainAfterAck === true && message.acknowledgedAt != null,
443
+ )
444
+ .sort(
445
+ (left, right) =>
446
+ left.acknowledgedAt - right.acknowledgedAt ||
447
+ left.createdAt - right.createdAt ||
448
+ left.messageId.localeCompare(right.messageId),
449
+ );
450
+ let changed = false;
451
+ while (retained.length > limit) {
452
+ const target = retained.shift();
453
+ const index = state.messages.indexOf(target);
454
+ if (index >= 0) {
455
+ state.messages.splice(index, 1);
456
+ changed = true;
457
+ }
458
+ }
459
+ return changed;
460
+ }
461
+
416
462
  function applyExpectedMessage(state, channel, message, recipient, now) {
417
463
  if (recipient.policy === "refuse") {
418
464
  updateReceipt(state, message, "refused", "policy_refuse", now);
@@ -556,7 +602,7 @@ function validateState(state) {
556
602
  !MESSAGE_ID_PATTERN.test(message?.messageId || "") ||
557
603
  !Number.isSafeInteger(message?.sequence) ||
558
604
  message.sequence < 1 ||
559
- !RECEIPT_STATUSES.has(message?.status) ||
605
+ !MESSAGE_STATUSES.has(message?.status) ||
560
606
  !channelIds.has(message?.channelId) ||
561
607
  typeof message?.payloadDigest !== "string"
562
608
  ) {
@@ -572,6 +618,13 @@ function validateState(state) {
572
618
  ) {
573
619
  fail("invalid receipt");
574
620
  }
621
+ if (
622
+ receipt.deliveryCount != null &&
623
+ (!Number.isSafeInteger(receipt.deliveryCount) ||
624
+ receipt.deliveryCount < 0)
625
+ ) {
626
+ fail("invalid receipt delivery count");
627
+ }
575
628
  }
576
629
  const bucketAuthorities = new Set();
577
630
  for (const bucket of state.rateBuckets || []) {
@@ -606,6 +659,7 @@ export class SessionMessageFabric {
606
659
  lockTimeoutMs = 10_000,
607
660
  maxMessageBytes = SESSION_MESSAGE_FABRIC_LIMITS.maxMessageBytes,
608
661
  maxPendingPerRecipient = SESSION_MESSAGE_FABRIC_LIMITS.maxPendingPerRecipient,
662
+ maxPendingBytes = SESSION_MESSAGE_FABRIC_LIMITS.maxPendingBytes,
609
663
  maxReceiptHistory = SESSION_MESSAGE_FABRIC_LIMITS.maxReceiptHistory,
610
664
  maxMessagesPerSenderWindow = SESSION_MESSAGE_FABRIC_LIMITS.maxMessagesPerSenderWindow,
611
665
  senderRateWindowMs = SESSION_MESSAGE_FABRIC_LIMITS.senderRateWindowMs,
@@ -636,6 +690,10 @@ export class SessionMessageFabric {
636
690
  maxPendingPerRecipient,
637
691
  SESSION_MESSAGE_FABRIC_LIMITS.maxPendingPerRecipient,
638
692
  ),
693
+ maxPendingBytes: positiveInteger(
694
+ maxPendingBytes,
695
+ SESSION_MESSAGE_FABRIC_LIMITS.maxPendingBytes,
696
+ ),
639
697
  maxReceiptHistory: positiveInteger(
640
698
  maxReceiptHistory,
641
699
  SESSION_MESSAGE_FABRIC_LIMITS.maxReceiptHistory,
@@ -770,6 +828,10 @@ export class SessionMessageFabric {
770
828
  );
771
829
  const outcome = mutator(state, now) || { value: undefined };
772
830
  const compacted = compactSettledMessages(state);
831
+ const trimmedSettled = trimRetainedSettledMessages(
832
+ state,
833
+ this._limits.maxReceiptHistory,
834
+ );
773
835
  const trimmed = trimReceiptHistory(
774
836
  state,
775
837
  this._limits.maxReceiptHistory,
@@ -778,6 +840,7 @@ export class SessionMessageFabric {
778
840
  swept ||
779
841
  prunedRateBuckets ||
780
842
  compacted ||
843
+ trimmedSettled ||
781
844
  trimmed ||
782
845
  outcome.changed === true
783
846
  ) {
@@ -985,6 +1048,7 @@ export class SessionMessageFabric {
985
1048
  sequence = null,
986
1049
  ttlMs = this._limits.defaultTtlMs,
987
1050
  notifyWhenIdle = false,
1051
+ retainAfterAck = false,
988
1052
  } = {}) {
989
1053
  const id = messageId(requestedMessageId, this._createId);
990
1054
  const cleanSubject = subject == null ? null : String(subject).slice(0, 512);
@@ -1108,6 +1172,7 @@ export class SessionMessageFabric {
1108
1172
  status: "held",
1109
1173
  reason: "out_of_order",
1110
1174
  notifyWhenIdle: notifyWhenIdle === true,
1175
+ retainAfterAck: retainAfterAck === true,
1111
1176
  createdAt: now,
1112
1177
  updatedAt: now,
1113
1178
  deliveredAt: null,
@@ -1137,10 +1202,17 @@ export class SessionMessageFabric {
1137
1202
  value: clone(receiptFor(state, sender.authorityId, id)),
1138
1203
  };
1139
1204
  }
1205
+ const pendingBytes = state.messages
1206
+ .filter(
1207
+ (entry) =>
1208
+ PENDING_STATUSES.has(entry.status) && entry.acknowledgedAt == null,
1209
+ )
1210
+ .reduce((sum, entry) => sum + (entry.messageBytes || 0), 0);
1140
1211
  if (
1141
1212
  recipient.policy !== "refuse" &&
1142
- pendingForRecipient(state, recipient).length >=
1143
- this._limits.maxPendingPerRecipient
1213
+ (pendingForRecipient(state, recipient).length >=
1214
+ this._limits.maxPendingPerRecipient ||
1215
+ pendingBytes + messageBytes > this._limits.maxPendingBytes)
1144
1216
  ) {
1145
1217
  updateReceipt(state, message, "full", "queue_capacity", now);
1146
1218
  return {
@@ -1194,6 +1266,196 @@ export class SessionMessageFabric {
1194
1266
  });
1195
1267
  }
1196
1268
 
1269
+ /**
1270
+ * Deliver an at-least-once batch without retiring it. This is deliberately
1271
+ * separate from `inbox(..., { acknowledge: true })`, whose historical API
1272
+ * means "read and remove". Processing callers must explicitly settle each
1273
+ * message with `acknowledge()`.
1274
+ */
1275
+ receive(selector, { limit = 100, markRead = false } = {}) {
1276
+ const boundedLimit = positiveInteger(limit, 100, 100);
1277
+ return this._transaction((state, now) => {
1278
+ const endpoint = requireEndpoint(state, selector);
1279
+ const messages = pendingForRecipient(state, endpoint)
1280
+ .filter((message) => message.status === "delivered")
1281
+ .sort(
1282
+ (left, right) =>
1283
+ (left.senderAuthority === right.senderAuthority
1284
+ ? left.sequence - right.sequence
1285
+ : left.createdAt - right.createdAt) ||
1286
+ left.senderAuthority.localeCompare(right.senderAuthority),
1287
+ )
1288
+ .slice(0, boundedLimit);
1289
+ for (const message of messages) {
1290
+ const receipt = receiptFor(
1291
+ state,
1292
+ message.senderAuthority,
1293
+ message.messageId,
1294
+ );
1295
+ if (!receipt) continue;
1296
+ receipt.deliveryCount = (receipt.deliveryCount || 0) + 1;
1297
+ receipt.deliveredAt ||= message.deliveredAt || now;
1298
+ if (markRead) {
1299
+ receipt.status = "read";
1300
+ receipt.readAt ||= now;
1301
+ } else if (!PROCESSING_STATUSES.has(receipt.status)) {
1302
+ receipt.status = "delivered";
1303
+ }
1304
+ receipt.updatedAt = now;
1305
+ }
1306
+ return {
1307
+ changed: messages.length > 0,
1308
+ value: clone(
1309
+ messages.map((message) => ({
1310
+ ...message,
1311
+ delivery: receiptFor(
1312
+ state,
1313
+ message.senderAuthority,
1314
+ message.messageId,
1315
+ ),
1316
+ })),
1317
+ ),
1318
+ };
1319
+ });
1320
+ }
1321
+
1322
+ /** Persist read/processed/dead-letter evidence for explicit physical ids. */
1323
+ acknowledge(
1324
+ selector,
1325
+ {
1326
+ messageIds = [],
1327
+ consumerKey,
1328
+ status = "processed",
1329
+ reason = null,
1330
+ recipientAttempt = null,
1331
+ } = {},
1332
+ ) {
1333
+ if (
1334
+ !Array.isArray(messageIds) ||
1335
+ messageIds.length === 0 ||
1336
+ messageIds.length > 100 ||
1337
+ messageIds.some((id) => !MESSAGE_ID_PATTERN.test(String(id || ""))) ||
1338
+ new Set(messageIds.map(String)).size !== messageIds.length
1339
+ ) {
1340
+ throw fabricError(
1341
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.INVALID_ARGUMENT,
1342
+ "messageIds must contain 1-100 unique message identifiers",
1343
+ );
1344
+ }
1345
+ if (!PROCESSING_STATUSES.has(status)) {
1346
+ throw fabricError(
1347
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.INVALID_ARGUMENT,
1348
+ "processing status must be read, processed or dead_letter",
1349
+ );
1350
+ }
1351
+ const safeConsumerKey = boundedId(consumerKey, "consumerKey");
1352
+ const safeAttempt =
1353
+ recipientAttempt == null ? null : jsonBody(recipientAttempt).value;
1354
+ const safeReason = String(reason || "poison_message").slice(0, 1024);
1355
+ const ids = messageIds.map(String);
1356
+ return this._transaction((state, now) => {
1357
+ const endpoint = requireEndpoint(state, selector);
1358
+ const plans = ids.map((id) => {
1359
+ const receipt = state.receipts.find(
1360
+ (candidate) =>
1361
+ candidate.messageId === id &&
1362
+ candidate.recipientAuthority === endpoint.authorityId &&
1363
+ candidate.recipientEpoch === endpoint.epoch,
1364
+ );
1365
+ const message = state.messages.find(
1366
+ (candidate) =>
1367
+ candidate.messageId === id &&
1368
+ candidate.recipientAuthority === endpoint.authorityId &&
1369
+ candidate.recipientEpoch === endpoint.epoch,
1370
+ );
1371
+ if (
1372
+ !receipt ||
1373
+ (!message && !TERMINAL_PROCESSING_STATUSES.has(receipt.status))
1374
+ ) {
1375
+ throw fabricError(
1376
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.INVALID_ARGUMENT,
1377
+ `message is not addressed to the active recipient: ${id}`,
1378
+ );
1379
+ }
1380
+ if (TERMINAL_PROCESSING_STATUSES.has(receipt.status)) {
1381
+ if (receipt.consumerKey !== safeConsumerKey) {
1382
+ throw fabricError(
1383
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.MESSAGE_ID_CONFLICT,
1384
+ `message was settled by another consumer: ${id}`,
1385
+ );
1386
+ }
1387
+ if (receipt.status !== status) {
1388
+ throw fabricError(
1389
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.MESSAGE_ID_CONFLICT,
1390
+ `message already has terminal status ${receipt.status}: ${id}`,
1391
+ );
1392
+ }
1393
+ return { receipt, message, replay: true };
1394
+ }
1395
+ return { receipt, message, replay: false };
1396
+ });
1397
+ const receipts = [];
1398
+ for (const plan of plans) {
1399
+ const { receipt, message, replay } = plan;
1400
+ if (replay) {
1401
+ receipts.push(clone(receipt));
1402
+ continue;
1403
+ }
1404
+ receipt.status = status;
1405
+ receipt.consumerKey = safeConsumerKey;
1406
+ receipt.recipientAttempt = safeAttempt;
1407
+ receipt.updatedAt = now;
1408
+ if (status === "read") receipt.readAt ||= now;
1409
+ if (status === "processed") {
1410
+ receipt.readAt ||= now;
1411
+ receipt.processedAt ||= now;
1412
+ message.acknowledgedAt = now;
1413
+ }
1414
+ if (status === "dead_letter") {
1415
+ receipt.deadLetteredAt ||= now;
1416
+ receipt.reason = safeReason;
1417
+ message.acknowledgedAt = now;
1418
+ }
1419
+ receipts.push(clone(receipt));
1420
+ }
1421
+ return { changed: plans.some((plan) => !plan.replay), value: receipts };
1422
+ });
1423
+ }
1424
+
1425
+ /** Bounded retained history for authoritative adapters and audit reducers. */
1426
+ history() {
1427
+ return this._transaction((state) => ({
1428
+ value: clone(
1429
+ state.messages.sort(
1430
+ (left, right) =>
1431
+ left.createdAt - right.createdAt ||
1432
+ left.messageId.localeCompare(right.messageId),
1433
+ ),
1434
+ ),
1435
+ }));
1436
+ }
1437
+
1438
+ /**
1439
+ * One-revision authority view for host adapters and deterministic reducers.
1440
+ * Unlike a sequence of projection/history/receipt reads, this cannot combine
1441
+ * fields from different concurrent commits.
1442
+ */
1443
+ auditSnapshot() {
1444
+ return this._transaction((state) => ({
1445
+ value: {
1446
+ schema: SESSION_MESSAGE_FABRIC_SCHEMA,
1447
+ version: state.version,
1448
+ authority: "cli",
1449
+ revision: state.revision,
1450
+ digest: state.digest,
1451
+ limits: clone(this._limits),
1452
+ endpoints: clone(state.endpoints),
1453
+ messages: clone(state.messages),
1454
+ receipts: clone(state.receipts),
1455
+ },
1456
+ }));
1457
+ }
1458
+
1197
1459
  receipts(selector) {
1198
1460
  return this._transaction((state) => {
1199
1461
  const endpoint = requireEndpoint(state, selector);