chainlesschain 0.166.6 → 0.166.7

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 (189) hide show
  1. package/package.json +3 -1
  2. package/src/assets/web-panel/assets/{AIOps-CVnFjTfG.js → AIOps-BlrmQxa2.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BxkwuuQC.js → ActionButton-zyRyLITg.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-C2670q6z.js → Analytics-DwGz7OGN.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-CU_rylW6.js → AppLayout-Dc-nj7Zd.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-DGJmYqnj.js → Artifacts-7Hu3O73p.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-BZOQbINJ.js → Audit-Bnoe3-fR.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DghB4QwF.js → BackgroundAgents-BqYFrDKZ.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-l0mwPbta.js → Backup-B3hwW1nz.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Bv3WSZOh.js → BaseInput-XFVxLA4w.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-s88nBdvt.js → Chat-CWRDNw7e.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Z8CBuB1a.js → ChatBubbleRenderer-Bf30VM8G.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-B5icasEs.js → Checkbox-kFeeBgac.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CsUFqNud.js → Codegen-CCxp8xoI.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DlBtClaW.js → Col-BLKZalle.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-xRn1IdLB.js → Community-DD0eWCez.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-naIr8a_o.js → Compact-CHqHWdy0.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Dp9esfFE.js → Compliance-Bw54uPiK.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DQlMYJB-.js → Cowork-DZ13zKNJ.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-Cd0oNLzJ.js → Cron-Dlj5Jevg.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BtDIx3XX.js → Crosschain-Nvz0aR0j.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BOlpzEwg.js → DID-Ct89eUyY.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DV15Axk1.js → Dashboard-BHUH5IAe.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-COXrewN1.js → Dropdown-BhZG-PO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-EjIt0Cm7.js → EmailListRenderer-ae4RVwwJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DJlhwb-R.js → FamilyGuardDashboard-AOcxLfLr.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BMiFl82k.js → Federation-BK2PJQOF.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BUcDQyxn.js → FormItemContext-C2n3xO2_.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-C25uFxvK.js → GenericCardRenderer-DcyUVFZq.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-D20uaEUU.js → Git-tBae9Lt8.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CQxYJjCX.js → Governance-DmNR3AP4.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-m-sczpS5.js → Inference-CoseEigi.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBsIlU0S.js → KnowledgeGraph-Bdjd1sxr.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-D0-5bqeu.js → Logs-CtlkKIcn.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BwH414Ms.js → MarkdownRenderer-CZsTBEQE.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-CwDGFu2Q.js → Marketplace-BTYb7nEx.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-7FxJ3omT.js → McpTools-D0iUdW3P.js} +2 -2
  38. package/src/assets/web-panel/assets/{Memory-DOVyb59U.js → Memory--gwI7w4Y.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DZXl-Hje.js → MobileBridge-JmFBi24T.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-DlZ8tRXt.js → MobileProjects-BK01cQRx.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-D7Pjx1s_.js → Mtc-u7avzyoO.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-BND7lX8p.js → MtcAudit-gT3z5fo_.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-BknS7B-p.js → Multisig-BIqZTz5n.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-7C2-KPHt.js → NLProgramming-DdhVfXYq.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-DNWdTndN.js → Notes-CBlyUmY3.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-DCm4KcJZ.js → NotificationSettings-DSyI26N3.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DyVwwjJx.js → OrderTableRenderer-B7IEuNz4.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dl2O_qJ9.js → Organization-ozhi4kGS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-BOFCvk9Q.js → Overflow-CBy57RVM.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DgdVowY1.js → P2P-CA-ZRv77.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-DOHc9zm8.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-BHNEUUwa.js → Permissions-DiP632GP.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CaZbrOdz.js → PersonalDataHub-AnU8O-SS.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-DJDnoXmE.js → Pipeline-DDibOUaP.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CBQr2eX8.js → Privacy-D421t36T.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-Yghqa06z.js → ProjectInit-DR879ZPJ.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-HBAtJi8U.js → ProjectSettings-Ded8o6aY.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-DiCGnMjz.js → Projects-BHPxiucB.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-uJTz77GI.js → Providers-Boivuu-E.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-k3M5ouTp.js → QrScannerModal-xKD_ntKp.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CMji45PH.js → QuickAsk-D3y_A6FV.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DyoZYPls.js → Recommend-BjKM-YC2.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-s1gR38IO.js → RemoteSession-CgqKMXG4.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-D4hIs4vV.js → Reputation-Ci2GjH5p.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-mzE-ElTg.js → Row-B0-T2BSh.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-cg7J3hI6.js → RssFeed-Bet-ur-r.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-6M0TkGSZ.js → Search-DWtRAS2R.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B1GQ67qh.js → Security-BDfu2Qn6.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D_RwU_5S.js → Services-CvJmvSMM.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-TZjk-dsW.js → Skeleton-CEbrKpdC.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CG6FcaCV.js → Skills-CjVeiETf.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-BI190QDv.js → Sla-Dibypxum.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BWmxF2Wu.js → SpeechSettings-XflYXp7a.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CwPiGw6L.js → SyncSettings-ro1Ivx1E.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BIXgCeSk.js → Tasks-CHq1qFbo.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CQFgGCoK.js → Templates-CRnUZxtZ.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DAelEdkL.js → Tenant-jUX72JCA.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BbqWK6qh.js → Terminal-BBhAs7pt.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-cmG3zvY5.js → TimelineRenderer-CD5M3RGp.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C8444NKU.js → Tokens-sah3v0gu.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-C1Rpnz0I.js → Trigger-BNT3Uz3s.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-u4koPiFD.js → Trust-BytMGPMo.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-oPWKn80Z.js → UkeySign-BkYVMRQs.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-D0CUUY_5.js → VideoEditing-psvb5Q7Y.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-fu9pIAbt.js → Wallet-PFQTW0MH.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Bc5ciuOx.js → WebAuthn-DgO-OwRT.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BCin4_4N.js → WorkflowEditor-DL2l9VNl.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B2jbM-XN.js → chat-C84akEzN.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CUVoUQ_2.js → colors-DzTWuQKP.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DgOGpkRH.js → compact-item-CR9LJQFv.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DIxU6eKI.js → createContext-CTTov5Kn.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BOhvmlpc.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DoKRKMav.js → hasIn-DALj523o.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-i5MB455u.js → index-3-XJoYgX.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CLdpHe6w.js → index-7ux_XohH.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B3F_p8NA.js → index-AXnERsyV.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D2yo6ast.js → index-B2qpjU-D.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CWbL1-N0.js → index-B6QOJbSn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DtyAuGOm.js → index-BDyt57zL.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DpTFx8Xx.js → index-BHRXKiU1.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CF80xxPG.js → index-BKNIzw0U.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BRMEO2Tl.js → index-BNaQt49r.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-ByjrulIZ.js → index-Be6Os5S8.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BCLeNqIL.js → index-C5eeMl_2.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DJMHnbmu.js → index-CNmTDXmf.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-2flsPFIX.js → index-CXTEpfGe.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BwYofQJg.js → index-CXaCRXN4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-B5ySiGYy.js → index-CZhhFQi9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CeWSw-z5.js → index-Cef0n4k_.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C846lPGD.js → index-CjkPDSX4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C9TsO9T9.js → index-CmELknPe.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CqlwIjJx.js → index-CnxjXIsK.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Co4lDEk-.js → index-CqUgXYIQ.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cj-us-D_.js → index-CunGDV0g.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BFUprhNd.js → index-D0MqUnKO.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DGTZdZOE.js → index-D9ScRFxz.js} +3 -3
  117. package/src/assets/web-panel/assets/{index-C88m9lsi.js → index-DBIPnoTj.js} +1 -1
  118. package/src/assets/web-panel/assets/index-DDyMJexx.js +1 -0
  119. package/src/assets/web-panel/assets/{index-BXb-Smmm.js → index-DH6OG-ci.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-eTWPaeBI.js → index-DZER6Lj3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cskz--fd.js → index-D_Bg0LdK.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D-ufbAJB.js → index-Dah8M4l2.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DHugFl3v.js → index-DjE_ew7U.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B9mSYovv.js → index-DtW6Zr_i.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Ds8r3Fzb.js → index-DxdzSljS.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CPXdBnGi.js → index-QW0EXx14.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B-f6l4Vm.js → index-W9axN_Ce.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CpvyOt5Z.js → index-_8vetPbU.js} +1 -1
  129. package/src/assets/web-panel/assets/index-btiojFYn.js +1 -0
  130. package/src/assets/web-panel/assets/{index-eYxUASSG.js → index-eJcFleIV.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-be8SonCh.js → index-hzzTITR9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BtSPhEyC.js → index-s4CFyn-n.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-B4chl7iR.js → initDefaultProps-JuW8mFj7.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-DruN9h96.js → motion-Cwb0HBve.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Cl0UtCgU.js → move-DHokzMgu.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-FX2ttw70.js → mtc-parser-DX31y6QS.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BDCneY46.js → omit-BCIUYDA6.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DycsEg8b.js → pickAttrs-DMCj4EoZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-Bc-dBuMv.js → placementArrow-Dy5K-u1s.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve--Wwaf8lu.js → responsiveObserve-DkGeZu54.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-vDmHQqHH.js → slide-Dc5dyJdg.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9c52yv3G.js → statusUtils-DIUBjOaC.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BT4BiO6_.js → styleChecker-DmyEOxm5.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-C7XxWPcP.js → useFlexGapSupport-FRhakk1e.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BHoMqh4i.js → useFs-clXyV1WQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BOLxvsAG.js → usePersonalDataHub-UF6DG41z.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-CA7poeeP.js → vnode-CoCt95G7.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B_sAn-co.js → zoom-yVWt-ENm.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/graph.js +215 -5
  151. package/src/commands/team-distributed.js +342 -2
  152. package/src/commands/team.js +169 -7
  153. package/src/data/changelog.json +13 -0
  154. package/src/generated/cc-agent-protocol.schema.json +6 -1
  155. package/src/lib/agent-team/task-lease.js +315 -0
  156. package/src/lib/agent-team/team-agent-stream.js +16 -0
  157. package/src/lib/agent-team/team-distributed-graph-bridge.js +257 -0
  158. package/src/lib/agent-team/team-distributed-graph-writer.js +523 -0
  159. package/src/lib/agent-team/team-distributed-queue.js +251 -0
  160. package/src/lib/agent-team/team-graph-runtime-adapter.js +1918 -0
  161. package/src/lib/agent-team/team-runner.js +225 -2
  162. package/src/lib/app-server/graph-runtime.js +754 -0
  163. package/src/lib/app-server/rollout-store.js +32 -0
  164. package/src/lib/app-server/server.js +140 -0
  165. package/src/lib/autonomous-agent.js +11 -0
  166. package/src/lib/cowork-graph-authority-adapter.js +455 -0
  167. package/src/lib/dynamic-workflow-facade.js +83 -16
  168. package/src/lib/graph-kernel/adapters.js +93 -20
  169. package/src/lib/graph-kernel/authority.js +324 -0
  170. package/src/lib/graph-kernel/compiler.js +1 -8
  171. package/src/lib/graph-kernel/cutover-authority-resolver.js +219 -0
  172. package/src/lib/graph-kernel/cutover-ledger.js +650 -0
  173. package/src/lib/graph-kernel/event-store.js +15 -1
  174. package/src/lib/graph-kernel/graph-runtime-surfaces.json +1427 -0
  175. package/src/lib/graph-kernel/index.js +4 -0
  176. package/src/lib/graph-kernel/migration-saga.js +309 -0
  177. package/src/lib/graph-kernel/runtime-surface-manifest.js +481 -0
  178. package/src/lib/graph-kernel/runtime.js +661 -24
  179. package/src/lib/graph-kernel/store-cutover-evidence.js +354 -0
  180. package/src/lib/graph-kernel/trace-reducer.js +6 -0
  181. package/src/lib/graph-kernel/trigger-adapter.js +60 -3
  182. package/src/lib/legacy-runtime-authority.js +160 -0
  183. package/src/lib/orchestrator.js +28 -0
  184. package/src/lib/scheduler-kernel/graph-authority-adapter.js +512 -0
  185. package/src/lib/scheduler-kernel/runtime.js +75 -2
  186. package/src/assets/web-panel/assets/PdhVaultBrowser-BiaZfQIM.js +0 -7
  187. package/src/assets/web-panel/assets/devWarning-DiSlgCC4.js +0 -1
  188. package/src/assets/web-panel/assets/index-9WIQEHvv.js +0 -1
  189. package/src/assets/web-panel/assets/index-B7mDobAl.js +0 -1
@@ -0,0 +1,1918 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import {
3
+ compileGraphDefinition,
4
+ graphDigest,
5
+ writeScopesOverlap,
6
+ } from "../graph-kernel/compiler.js";
7
+ import { GraphEventStore } from "../graph-kernel/event-store.js";
8
+ import { GraphKernel } from "../graph-kernel/runtime.js";
9
+ import { createGraphAuthorityBinding } from "../graph-kernel/authority.js";
10
+
11
+ const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u;
12
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
13
+
14
+ function adapterError(code, message, details = {}) {
15
+ const error = new Error(message);
16
+ error.name = "TeamGraphRuntimeAdapterError";
17
+ error.code = code;
18
+ Object.assign(error, details);
19
+ return error;
20
+ }
21
+
22
+ function safeIdentifier(value, prefix) {
23
+ const candidate = String(value || "").trim();
24
+ if (IDENTIFIER.test(candidate)) return candidate;
25
+ return `${prefix}-${createHash("sha256")
26
+ .update(candidate, "utf8")
27
+ .digest("hex")
28
+ .slice(0, 32)}`;
29
+ }
30
+
31
+ function priorityValue(value) {
32
+ if (value === "high") return 100;
33
+ if (value === "low") return -100;
34
+ return 0;
35
+ }
36
+
37
+ function teamBudget(input = {}) {
38
+ const output = {};
39
+ for (const [source, target] of [
40
+ ["maxTasks", "turns"],
41
+ ["maxTokens", "tokens"],
42
+ ["maxUsd", "costUsd"],
43
+ ["maxWallMs", "wallMs"],
44
+ ]) {
45
+ if (input[source] == null || input[source] === "") continue;
46
+ const value = Number(input[source]);
47
+ if (Number.isFinite(value) && value >= 0) output[target] = value;
48
+ }
49
+ return output;
50
+ }
51
+
52
+ function compileTeamTaskNode(
53
+ task,
54
+ taskToNode,
55
+ { executionMode, worktree, maxAttempts },
56
+ ) {
57
+ const scopePaths = Array.isArray(task.metadata?.scopePaths)
58
+ ? task.metadata.scopePaths.map(String).filter(Boolean)
59
+ : [];
60
+ const effectful = executionMode !== "dry-run";
61
+ const node = {
62
+ id: taskToNode.get(task.key),
63
+ kind: "task",
64
+ dependsOn: (task.dependsOn || []).map((key) => {
65
+ const dependency = taskToNode.get(key);
66
+ if (!dependency) {
67
+ throw adapterError(
68
+ "CC_TEAM_GRAPH_DEPENDENCY_UNKNOWN",
69
+ `team task ${task.key} depends on unknown task ${key}`,
70
+ );
71
+ }
72
+ return dependency;
73
+ }),
74
+ inputs: [],
75
+ outputs: [],
76
+ effectClass: effectful ? "workspace_write" : "none",
77
+ priority: priorityValue(task.priority),
78
+ retryLimit: Math.max(0, Number(maxAttempts || 1) - 1),
79
+ };
80
+ if (effectful) {
81
+ node.idempotencyKey =
82
+ task.metadata?.idempotencyKey ||
83
+ graphDigest(
84
+ { taskKey: task.key, title: task.title, scopePaths },
85
+ "cc.team.effect/v1",
86
+ );
87
+ node.workspaceIsolation = worktree ? "worktree" : "declared_scope";
88
+ if (!worktree) node.writeSet = scopePaths.length ? scopePaths : ["**"];
89
+ }
90
+ return node;
91
+ }
92
+
93
+ const TEAM_TASK_INTERNAL_METADATA = new Set([
94
+ "key",
95
+ "dependsOn",
96
+ "lease",
97
+ "attempts",
98
+ "lastError",
99
+ "result",
100
+ "custodyHandoffs",
101
+ "adjudication",
102
+ "interruption",
103
+ "abandonedLeaseEvidence",
104
+ "canonicalGraphProjection",
105
+ ]);
106
+
107
+ function persistedTeamTaskDefinition(task) {
108
+ return {
109
+ key: task.key,
110
+ title: task.title,
111
+ dependsOn: [...(task.dependsOn || [])],
112
+ priority: task.priority || "normal",
113
+ createdBy: task.metadata?.createdBy ?? task.createdBy ?? null,
114
+ metadata: Object.fromEntries(
115
+ Object.entries(task.metadata || {})
116
+ .filter(([key]) => !TEAM_TASK_INTERNAL_METADATA.has(key))
117
+ .filter(([, value]) => value !== undefined)
118
+ .map(([key, value]) => [key, JSON.parse(JSON.stringify(value))]),
119
+ ),
120
+ };
121
+ }
122
+
123
+ function taskWriteScopes(task) {
124
+ const scopes = Array.isArray(task?.metadata?.scopePaths)
125
+ ? task.metadata.scopePaths.map(String).filter(Boolean)
126
+ : [];
127
+ return scopes.length > 0 ? scopes : ["**"];
128
+ }
129
+
130
+ function scopesOverlap(left, right) {
131
+ return left.some((a) => right.some((b) => writeScopesOverlap(a, b)));
132
+ }
133
+
134
+ export function compileTeamGraphDefinition(
135
+ registry,
136
+ {
137
+ definitionId = "cli-team",
138
+ revision = 1,
139
+ executionMode = "agent",
140
+ worktree = false,
141
+ budget = {},
142
+ } = {},
143
+ ) {
144
+ if (!registry || typeof registry.list !== "function") {
145
+ throw new TypeError("compileTeamGraphDefinition requires a task registry");
146
+ }
147
+ const tasks = registry.list();
148
+ if (tasks.length === 0) {
149
+ throw adapterError(
150
+ "CC_TEAM_GRAPH_EMPTY",
151
+ "canonical Team Graph requires at least one task",
152
+ );
153
+ }
154
+ const taskToNode = new Map(
155
+ tasks.map((task) => [task.key, safeIdentifier(task.key, "team-node")]),
156
+ );
157
+ if (new Set(taskToNode.values()).size !== taskToNode.size) {
158
+ throw adapterError(
159
+ "CC_TEAM_GRAPH_NODE_COLLISION",
160
+ "team task keys collide after canonical identifier normalization",
161
+ );
162
+ }
163
+ const nodes = tasks.map((task) =>
164
+ compileTeamTaskNode(task, taskToNode, {
165
+ executionMode,
166
+ worktree,
167
+ maxAttempts: registry.maxAttempts,
168
+ }),
169
+ );
170
+ const definition = {
171
+ schemaVersion: 1,
172
+ id: safeIdentifier(definitionId, "team-definition"),
173
+ revision: Math.max(1, Number(revision) || 1),
174
+ nodes,
175
+ edges: [],
176
+ loops: [],
177
+ subgraphCalls: [],
178
+ budget: teamBudget(budget),
179
+ allowedCapabilities: [],
180
+ metadata: {
181
+ originSurface: "cli_team",
182
+ executionMode,
183
+ taskKeyByNodeId: Object.fromEntries(
184
+ [...taskToNode].map(([taskKey, nodeId]) => [nodeId, taskKey]),
185
+ ),
186
+ taskDefinitionsByNodeId: Object.fromEntries(
187
+ tasks.map((task) => [
188
+ taskToNode.get(task.key),
189
+ persistedTeamTaskDefinition(task),
190
+ ]),
191
+ ),
192
+ },
193
+ };
194
+ return Object.freeze({
195
+ compiled: compileGraphDefinition(definition),
196
+ taskToNode,
197
+ nodeToTask: new Map(
198
+ [...taskToNode].map(([taskKey, nodeId]) => [nodeId, taskKey]),
199
+ ),
200
+ });
201
+ }
202
+
203
+ function terminalEvidence(result) {
204
+ const source = result?.terminalEvidence || result || {};
205
+ const evidence = {};
206
+ if (DIGEST.test(String(source.outputDigest || ""))) {
207
+ evidence.outputDigest = source.outputDigest;
208
+ }
209
+ if (source.commit) evidence.commit = String(source.commit);
210
+ if (Array.isArray(source.artifactIds) && source.artifactIds.length) {
211
+ evidence.artifactIds = [...source.artifactIds];
212
+ }
213
+ if (Array.isArray(source.testReceiptIds) && source.testReceiptIds.length) {
214
+ evidence.testReceiptIds = [...source.testReceiptIds];
215
+ }
216
+ if (Object.keys(evidence).length === 0) {
217
+ throw adapterError(
218
+ "CC_TEAM_GRAPH_TERMINAL_EVIDENCE_REQUIRED",
219
+ "canonical Team task success requires immutable output, artifact, commit, or test evidence",
220
+ );
221
+ }
222
+ return evidence;
223
+ }
224
+
225
+ function graphUsage(source) {
226
+ const usage = source?.usage || {};
227
+ const tokens = [
228
+ "input_tokens",
229
+ "output_tokens",
230
+ "cache_read_input_tokens",
231
+ "cache_creation_input_tokens",
232
+ ].reduce((total, key) => total + Math.max(0, Number(usage[key]) || 0), 0);
233
+ return { turns: 1, tokens };
234
+ }
235
+
236
+ const TEAM_MAILBOX_GRAPH_SCHEMA = "chainlesschain.team-graph-mailbox/v1";
237
+
238
+ function mailboxProjectionKey(runId, request, createId) {
239
+ const stable = request.idempotencyKey
240
+ ? {
241
+ runId,
242
+ from: request.from || "coordinator",
243
+ taskKey: request.senderAttempt?.taskKey || null,
244
+ idempotencyKey: String(request.idempotencyKey),
245
+ }
246
+ : {
247
+ runId,
248
+ nonce: String(createId()),
249
+ };
250
+ return safeIdentifier(
251
+ `team-mail:${graphDigest(stable, "cc.team.mailbox-projection/v1").slice(7, 47)}`,
252
+ "team-mail",
253
+ );
254
+ }
255
+
256
+ function graphMessageId(projectionKey, recipient) {
257
+ return safeIdentifier(
258
+ `team-message:${graphDigest(
259
+ { projectionKey, recipient },
260
+ "cc.team.graph-message/v1",
261
+ ).slice(7, 47)}`,
262
+ "team-message",
263
+ );
264
+ }
265
+
266
+ function shadowMailboxProjectionKey(runId, message) {
267
+ return safeIdentifier(
268
+ `team-shadow-mail:${graphDigest(
269
+ {
270
+ runId,
271
+ legacyMessageId: message?.id ?? null,
272
+ payloadDigest: message?.payloadDigest ?? null,
273
+ },
274
+ "cc.team.shadow-mailbox-projection/v1",
275
+ ).slice(7, 47)}`,
276
+ "team-shadow-mail",
277
+ );
278
+ }
279
+
280
+ function mailboxPayload(request, projectionKey) {
281
+ return {
282
+ schema: TEAM_MAILBOX_GRAPH_SCHEMA,
283
+ projectionKey,
284
+ from: request.from || "coordinator",
285
+ to: request.to,
286
+ subject: request.subject ?? null,
287
+ body: request.body,
288
+ mode: request.mode === "followup" ? "followup" : "send",
289
+ causationId: request.causationId ?? null,
290
+ correlationId: request.correlationId ?? null,
291
+ originalIdempotencyKey: request.idempotencyKey ?? null,
292
+ senderAttempt: request.senderAttempt ?? null,
293
+ };
294
+ }
295
+
296
+ export class TeamGraphRuntimeAdapter {
297
+ constructor({
298
+ eventStore = new GraphEventStore(),
299
+ now = Date.now,
300
+ writerLeaseTtlMs = 24 * 60 * 60 * 1000,
301
+ createId = randomUUID,
302
+ } = {}) {
303
+ this.eventStore = eventStore;
304
+ this.now = now;
305
+ this.writerLeaseTtlMs = writerLeaseTtlMs;
306
+ this.createId = createId;
307
+ this.kernel = null;
308
+ this.runId = null;
309
+ this.compiled = null;
310
+ this.taskToNode = null;
311
+ this.nodeToTask = null;
312
+ this.attempts = new Map();
313
+ this.authorityMode = null;
314
+ this.agentIds = new Set(["coordinator"]);
315
+ this.dynamic = false;
316
+ this.producerLease = null;
317
+ this.executionMode = "agent";
318
+ this.worktree = false;
319
+ this.maxAttempts = 1;
320
+ this.taskDefinitions = new Map();
321
+ }
322
+
323
+ runtimeClaims() {
324
+ return Object.freeze({
325
+ originSurface: "cli_team",
326
+ surface: "cli_team",
327
+ execution: "real",
328
+ persistence: "durable",
329
+ isolated: true,
330
+ terminalEvidence: true,
331
+ authorityModes: Object.freeze(["shadow", "canonical"]),
332
+ featureGated: true,
333
+ });
334
+ }
335
+
336
+ open({
337
+ registry,
338
+ runId,
339
+ executionMode,
340
+ worktree = false,
341
+ teammates = 1,
342
+ budget = {},
343
+ authorityMode = "canonical",
344
+ dynamic = false,
345
+ recoveryReceipts = null,
346
+ }) {
347
+ if (!["shadow", "canonical"].includes(authorityMode)) {
348
+ throw adapterError(
349
+ "CC_TEAM_GRAPH_AUTHORITY_MODE_INVALID",
350
+ "Team Graph adapter authorityMode must be shadow or canonical",
351
+ );
352
+ }
353
+ this.authorityMode = authorityMode;
354
+ this.runId = safeIdentifier(runId, "team-run");
355
+ this.dynamic = dynamic === true;
356
+ this.executionMode = executionMode;
357
+ this.worktree = worktree === true;
358
+ this.maxAttempts = Math.max(1, Number(registry.maxAttempts || 1));
359
+ let events = [];
360
+ try {
361
+ events = this.eventStore.read(this.runId);
362
+ } catch (error) {
363
+ if (error?.code !== "CC_ROLLOUT_THREAD_NOT_FOUND") throw error;
364
+ }
365
+ const latest = events.at(-1) || null;
366
+ if (this.dynamic && latest) {
367
+ const persistedDefinitions = Object.values(
368
+ latest.payload?.state?.definition?.metadata?.taskDefinitionsByNodeId ||
369
+ {},
370
+ );
371
+ const missing = persistedDefinitions.filter(
372
+ (definition) => definition?.key && !registry.getTask(definition.key),
373
+ );
374
+ if (missing.length > 0) {
375
+ const repaired = registry.addTasks(missing);
376
+ if (!repaired?.ok) {
377
+ throw adapterError(
378
+ "CC_TEAM_GRAPH_LEGACY_PROJECTION_DIVERGED",
379
+ `could not restore ${missing.length} Graph-owned Team task projection(s): ${repaired?.reason || "unknown"}`,
380
+ );
381
+ }
382
+ }
383
+ }
384
+ const graph = compileTeamGraphDefinition(registry, {
385
+ definitionId: `team-definition:${this.runId}`,
386
+ revision: latest?.payload?.state?.definition?.revision || 1,
387
+ executionMode,
388
+ worktree,
389
+ budget,
390
+ });
391
+ this.compiled = graph.compiled;
392
+ this.taskToNode = graph.taskToNode;
393
+ this.nodeToTask = graph.nodeToTask;
394
+ this.taskDefinitions = new Map(
395
+ registry
396
+ .list()
397
+ .map((task) => [task.key, persistedTeamTaskDefinition(task)]),
398
+ );
399
+ const previousAuthority =
400
+ latest?.payload?.authority || latest?.payload?.state?.authority || null;
401
+ if (
402
+ previousAuthority &&
403
+ previousAuthority.authorityMode !== authorityMode
404
+ ) {
405
+ throw adapterError(
406
+ "CC_GRAPH_MIGRATION_REQUIRED",
407
+ "Team Graph authority mode changed without a migration saga",
408
+ );
409
+ }
410
+ const generation = previousAuthority
411
+ ? Number(previousAuthority.authorityGeneration) + 1
412
+ : 1;
413
+ const writerId = safeIdentifier(
414
+ `cli-team:${this.runId}:writer:${process.pid}:${generation}`,
415
+ "team-writer",
416
+ );
417
+ const writerLeaseId = safeIdentifier(
418
+ `cli-team:${this.runId}:lease:${this.createId()}`,
419
+ "team-writer-lease",
420
+ );
421
+ this.kernel = new GraphKernel({
422
+ eventStore: this.eventStore,
423
+ now: this.now,
424
+ writerId,
425
+ writerLeaseId,
426
+ authoritySource:
427
+ authorityMode === "shadow" ? "graph_kernel_shadow" : "graph_kernel",
428
+ authorityGeneration: generation,
429
+ writerLeaseTtlMs: this.writerLeaseTtlMs,
430
+ });
431
+ let projection;
432
+ if (!latest) {
433
+ projection = this.kernel.startRun(this.compiled, {
434
+ runId: this.runId,
435
+ originSurface: "cli_team",
436
+ });
437
+ } else {
438
+ const authority = createGraphAuthorityBinding({
439
+ logicalRunId: this.runId,
440
+ originSurface: "cli_team",
441
+ authorityMode,
442
+ authoritySource:
443
+ authorityMode === "shadow" ? "graph_kernel_shadow" : "graph_kernel",
444
+ authorityGeneration: generation,
445
+ writerId,
446
+ writerLeaseId,
447
+ writerLeaseExpiresAt: new Date(
448
+ this.now() + this.writerLeaseTtlMs,
449
+ ).toISOString(),
450
+ eventHead: latest.hash,
451
+ projectionVersion: 1,
452
+ });
453
+ projection = this.kernel.recoverRun(this.runId, { authority });
454
+ if (projection.revisionDigest !== this.compiled.revisionDigest) {
455
+ throw adapterError(
456
+ "CC_TEAM_GRAPH_REVISION_CONFLICT",
457
+ "persisted canonical Team Graph does not match the resumed task definition",
458
+ );
459
+ }
460
+ }
461
+ if (projection.status === "reconciliation_required" && recoveryReceipts) {
462
+ const receipts =
463
+ recoveryReceipts instanceof Map
464
+ ? recoveryReceipts
465
+ : new Map(Object.entries(recoveryReceipts));
466
+ for (const effect of this.kernel
467
+ .effectState(this.runId)
468
+ .filter((candidate) => candidate.status === "unknown")) {
469
+ const key = this.nodeToTask.get(effect.nodeId);
470
+ const recovery = key ? receipts.get(key) : null;
471
+ if (!recovery) continue;
472
+ const decision =
473
+ recovery.decision ||
474
+ (recovery.status === "completed" ? "committed" : "failed");
475
+ const receiptDigest =
476
+ recovery.receiptDigest ||
477
+ recovery.terminalEvidence?.outputDigest ||
478
+ null;
479
+ this.kernel.reconcileEffect(this.runId, {
480
+ effectId: effect.id,
481
+ decision,
482
+ receipt:
483
+ decision === "committed"
484
+ ? {
485
+ receiptDigest,
486
+ terminalEvidence: recovery.terminalEvidence || null,
487
+ }
488
+ : null,
489
+ auditDecisionId: safeIdentifier(
490
+ recovery.auditDecisionId ||
491
+ `team-recovery:${graphDigest(
492
+ {
493
+ runId: this.runId,
494
+ key,
495
+ effectId: effect.id,
496
+ decision,
497
+ receiptDigest,
498
+ },
499
+ "cc.team.recovery-decision/v1",
500
+ ).slice(7, 39)}`,
501
+ "team-recovery",
502
+ ),
503
+ });
504
+ }
505
+ projection = this.kernel.getRun(this.runId);
506
+ }
507
+ if (projection.status === "reconciliation_required") {
508
+ throw adapterError(
509
+ "CC_GRAPH_RECONCILIATION_REQUIRED",
510
+ "Team Graph contains an unknown effect requiring audited reconciliation",
511
+ { runId: this.runId },
512
+ );
513
+ }
514
+ if (latest) {
515
+ const effectsByAttempt = new Map();
516
+ for (const effect of this.kernel.effectState(this.runId)) {
517
+ const list = effectsByAttempt.get(effect.attemptId) || [];
518
+ list.push(effect);
519
+ effectsByAttempt.set(effect.attemptId, list);
520
+ }
521
+ const handoffsBySource = new Map();
522
+ for (const handoff of this.kernel.collaborationState(this.runId)
523
+ .handoffs) {
524
+ if (!["offered", "accepted"].includes(handoff.status)) continue;
525
+ const list = handoffsBySource.get(handoff.fromAttemptId) || [];
526
+ list.push(handoff);
527
+ handoffsBySource.set(handoff.fromAttemptId, list);
528
+ }
529
+ for (const attempt of projection.attempts.filter(
530
+ (candidate) =>
531
+ candidate.status === "active" ||
532
+ (candidate.status === "expired" &&
533
+ candidate.participationStatus === "reconciled"),
534
+ )) {
535
+ for (const handoff of handoffsBySource.get(attempt.id) || []) {
536
+ this.kernel.expireHandoffForRecovery(this.runId, handoff.id);
537
+ }
538
+ const effects = effectsByAttempt.get(attempt.id) || [];
539
+ if (
540
+ effects.length > 0 &&
541
+ effects.every((effect) =>
542
+ ["committed", "failed"].includes(effect.status),
543
+ )
544
+ ) {
545
+ // A failed audit decision for a dispatch that never received its
546
+ // authorization response proves no executor side effect began. It
547
+ // restores the node to pending without charging a second Graph
548
+ // attempt; the still-live queue lease can request a fresh boundary.
549
+ if (
550
+ attempt.status === "expired" &&
551
+ attempt.participationStatus === "reconciled" &&
552
+ effects.every((effect) => effect.status === "failed")
553
+ ) {
554
+ continue;
555
+ }
556
+ const resumed = this.kernel.resumeAttempt(this.runId, attempt.id, {
557
+ resumedAttemptId: safeIdentifier(
558
+ `team-attempt-recovery:${graphDigest(
559
+ {
560
+ runId: this.runId,
561
+ attemptId: attempt.id,
562
+ generation,
563
+ },
564
+ "cc.team.assignment-recovery/v1",
565
+ ).slice(7, 39)}`,
566
+ "team-attempt-recovery",
567
+ ),
568
+ leaseId: safeIdentifier(
569
+ `team-lease-recovery:${this.runId}:${generation}:${attempt.nodeId}`,
570
+ "team-lease-recovery",
571
+ ),
572
+ ttlMs: this.writerLeaseTtlMs,
573
+ });
574
+ const committed = effects.filter(
575
+ (effect) => effect.status === "committed",
576
+ );
577
+ if (committed.length === effects.length) {
578
+ const receiptDigest = committed[0]?.receipt?.receiptDigest;
579
+ if (!DIGEST.test(String(receiptDigest || ""))) {
580
+ throw adapterError(
581
+ "CC_GRAPH_RECONCILIATION_REQUIRED",
582
+ "Team Graph recovered a committed effect without an immutable receipt",
583
+ { runId: this.runId, attemptId: attempt.id },
584
+ );
585
+ }
586
+ this.kernel.settleAttempt(this.runId, {
587
+ attemptId: resumed.id,
588
+ leaseId: resumed.leaseId,
589
+ fence: resumed.fence,
590
+ outcome: "succeeded",
591
+ evidence: { outputDigest: receiptDigest },
592
+ usage: { turns: 0, tokens: 0 },
593
+ });
594
+ } else {
595
+ this.kernel.settleAttempt(this.runId, {
596
+ attemptId: resumed.id,
597
+ leaseId: resumed.leaseId,
598
+ fence: resumed.fence,
599
+ outcome: "failed",
600
+ error: "recovered terminal effect failure",
601
+ usage: { turns: 0, tokens: 0 },
602
+ });
603
+ }
604
+ continue;
605
+ }
606
+ try {
607
+ this.kernel.reclaimAttempt(this.runId, attempt.id, {
608
+ reason: "CLI Team writer generation takeover before effect start",
609
+ });
610
+ } catch (error) {
611
+ if (error?.code === "CC_GRAPH_ASSIGNMENT_RECLAIM_UNSAFE") {
612
+ throw adapterError(
613
+ "CC_GRAPH_RECONCILIATION_REQUIRED",
614
+ "Team Graph recovered an assignment that crossed the effect boundary",
615
+ { runId: this.runId, attemptId: attempt.id, cause: error },
616
+ );
617
+ }
618
+ throw error;
619
+ }
620
+ }
621
+ projection = this.kernel.getRun(this.runId);
622
+ }
623
+ if (authorityMode === "canonical") {
624
+ this._reconcileTerminalTaskProjection(registry, projection);
625
+ }
626
+ if (projection.phase === "open") {
627
+ for (let index = 0; index < Math.max(1, teammates); index += 1) {
628
+ const agentId = `teammate-${index + 1}`;
629
+ this.agentIds.add(agentId);
630
+ this.kernel.registerAgent(this.runId, {
631
+ agentId,
632
+ capacity: 1,
633
+ resident: true,
634
+ });
635
+ }
636
+ if (this.dynamic) {
637
+ this.producerLease = this.kernel.acquireProducerLease(this.runId, {
638
+ producerId: "cli-team-followup",
639
+ ttlMs: this.writerLeaseTtlMs,
640
+ leaseId: safeIdentifier(
641
+ `team-producer:${this.runId}:${generation}`,
642
+ "team-producer",
643
+ ),
644
+ });
645
+ projection = this.kernel.getRun(this.runId);
646
+ } else {
647
+ projection = this.kernel.sealRun(this.runId);
648
+ }
649
+ }
650
+ return projection;
651
+ }
652
+
653
+ _reconcileTerminalTaskProjection(registry, projection) {
654
+ const terminal = new Set(["succeeded", "failed", "blocked", "cancelled"]);
655
+ const attempts = new Map(
656
+ projection.attempts.map((attempt) => [attempt.id, attempt]),
657
+ );
658
+ for (const node of projection.nodes) {
659
+ if (!terminal.has(node.status)) continue;
660
+ const key = this.nodeToTask.get(node.nodeId);
661
+ if (!key) continue;
662
+ if (typeof registry.applyCanonicalTaskProjection !== "function") {
663
+ throw adapterError(
664
+ "CC_TEAM_GRAPH_LEGACY_PROJECTION_UNSUPPORTED",
665
+ "Team registry cannot repair a terminal canonical Graph projection",
666
+ { key, nodeId: node.nodeId },
667
+ );
668
+ }
669
+ const accepted = node.acceptedAttemptId
670
+ ? attempts.get(node.acceptedAttemptId)
671
+ : null;
672
+ const result = registry.applyCanonicalTaskProjection(key, {
673
+ runId: projection.id,
674
+ nodeId: node.nodeId,
675
+ graphStatus: node.status,
676
+ authorityGeneration: projection.authorityGeneration,
677
+ eventHead: projection.eventHead,
678
+ revisionDigest: projection.revisionDigest,
679
+ evidence: accepted?.terminalEvidence || null,
680
+ now: this.now(),
681
+ });
682
+ if (!result?.ok) {
683
+ throw adapterError(
684
+ "CC_TEAM_GRAPH_LEGACY_PROJECTION_DIVERGED",
685
+ `legacy Team task projection rejected canonical terminal state: ${result?.reason || "unknown"}`,
686
+ { key, nodeId: node.nodeId, graphStatus: node.status },
687
+ );
688
+ }
689
+ }
690
+ }
691
+
692
+ _mailboxRecipients(mailbox, request) {
693
+ const declared = mailbox.status?.().recipients || [];
694
+ const recipients = new Set([...this.agentIds, ...declared]);
695
+ const from = String(request.from || "coordinator");
696
+ if (request.to === "*") {
697
+ return [...recipients].filter((recipient) => recipient !== from).sort();
698
+ }
699
+ const recipient = String(request.to || "").trim();
700
+ if (!recipient) {
701
+ throw adapterError(
702
+ "CC_TEAM_GRAPH_MESSAGE_RECIPIENT_REQUIRED",
703
+ "canonical Team message requires a recipient",
704
+ );
705
+ }
706
+ if (declared.length > 0 && !recipients.has(recipient)) {
707
+ throw adapterError(
708
+ "CC_TEAM_GRAPH_MESSAGE_RECIPIENT_UNKNOWN",
709
+ `canonical Team message recipient is not registered: ${recipient}`,
710
+ );
711
+ }
712
+ return [recipient];
713
+ }
714
+
715
+ _messageSource(request) {
716
+ const taskKey = request.senderAttempt?.taskKey || null;
717
+ if (taskKey) {
718
+ const active = this.attempts.get(taskKey);
719
+ if (!active?.attempt) {
720
+ throw adapterError(
721
+ "CC_TEAM_GRAPH_MESSAGE_ATTEMPT_MISSING",
722
+ `canonical Team message has no active Graph attempt for ${taskKey}`,
723
+ );
724
+ }
725
+ return {
726
+ fromAttemptId: active.attempt.id,
727
+ leaseId: active.attempt.leaseId,
728
+ fence: active.attempt.fence,
729
+ };
730
+ }
731
+ if (String(request.from || "coordinator") !== "coordinator") {
732
+ throw adapterError(
733
+ "CC_TEAM_GRAPH_MESSAGE_AUTHORITY_REQUIRED",
734
+ "non-coordinator Team messages require attempt-bound sender authority",
735
+ );
736
+ }
737
+ return { systemSource: "team-coordinator" };
738
+ }
739
+
740
+ _sendMailboxMessage(mailbox, request) {
741
+ const projectionKey = mailboxProjectionKey(
742
+ this.runId,
743
+ request,
744
+ this.createId,
745
+ );
746
+ const payload = mailboxPayload(request, projectionKey);
747
+ const source = this._messageSource(request);
748
+ const messages = this._mailboxRecipients(mailbox, request).map(
749
+ (recipient) =>
750
+ this.kernel.sendMessage(this.runId, {
751
+ messageId: graphMessageId(projectionKey, recipient),
752
+ ...source,
753
+ toAgentId: recipient,
754
+ mode: payload.mode,
755
+ payload,
756
+ causationId: payload.causationId,
757
+ correlationId: payload.correlationId,
758
+ dataPolicy: {
759
+ origin: `graph:${this.runId}`,
760
+ trust: "trusted_host",
761
+ sensitivity: "internal",
762
+ allowedSinks: [`agent:${recipient}`],
763
+ },
764
+ }),
765
+ );
766
+ const projected = mailbox.send({
767
+ ...request,
768
+ idempotencyKey: projectionKey,
769
+ });
770
+ for (const message of messages) {
771
+ this.kernel.deliverMessage(this.runId, message.id);
772
+ }
773
+ return projected;
774
+ }
775
+
776
+ _graphMessageIdForLegacy(message, recipient) {
777
+ const projectionKey = String(message?.idempotencyKey || "");
778
+ if (!projectionKey.startsWith("team-mail:")) {
779
+ throw adapterError(
780
+ "CC_TEAM_GRAPH_MESSAGE_PROJECTION_MISSING",
781
+ `legacy Team message ${message?.id ?? "unknown"} is not bound to Graph authority`,
782
+ );
783
+ }
784
+ return graphMessageId(projectionKey, recipient);
785
+ }
786
+
787
+ _receiveMailboxMessages(mailbox, recipient, options = {}) {
788
+ const limit = Math.max(1, Math.min(100, Number(options.limit) || 100));
789
+ const pending = mailbox.peek(recipient).slice(0, limit);
790
+ for (const message of pending) {
791
+ this.kernel.deliverMessage(
792
+ this.runId,
793
+ this._graphMessageIdForLegacy(message, recipient),
794
+ );
795
+ }
796
+ if (pending.length > 0) {
797
+ this.kernel.receiveMessages(this.runId, recipient, {
798
+ markRead: options.markRead === true,
799
+ });
800
+ }
801
+ return mailbox.receive(recipient, options);
802
+ }
803
+
804
+ _acknowledgeMailboxMessages(mailbox, recipient, options = {}) {
805
+ const byId = new Map(mailbox.log().map((message) => [message.id, message]));
806
+ const consumerKey = String(options.consumerKey || "").trim();
807
+ for (const id of options.messageIds || []) {
808
+ const message = byId.get(id);
809
+ if (!message) {
810
+ throw adapterError(
811
+ "CC_TEAM_GRAPH_MESSAGE_PROJECTION_MISSING",
812
+ `legacy Team message ${id} is outside the Graph projection window`,
813
+ );
814
+ }
815
+ const graphId = this._graphMessageIdForLegacy(message, recipient);
816
+ this.kernel.deliverMessage(this.runId, graphId);
817
+ if (options.status === "dead_letter") {
818
+ this.kernel.deadLetterMessage(
819
+ this.runId,
820
+ graphId,
821
+ options.reason || "poison_message",
822
+ );
823
+ } else if (options.status === "read") {
824
+ this.kernel.receiveMessages(this.runId, recipient, { markRead: true });
825
+ } else {
826
+ this.kernel.processMessage(this.runId, graphId, recipient, consumerKey);
827
+ }
828
+ }
829
+ return mailbox.acknowledge(recipient, options);
830
+ }
831
+
832
+ _drainMailbox(mailbox, recipient) {
833
+ const pending = mailbox.peek(recipient);
834
+ for (const message of pending) {
835
+ const graphId = this._graphMessageIdForLegacy(message, recipient);
836
+ this.kernel.deliverMessage(this.runId, graphId);
837
+ this.kernel.processMessage(
838
+ this.runId,
839
+ graphId,
840
+ recipient,
841
+ safeIdentifier(`legacy-drain:${recipient}:${message.id}`, "consumer"),
842
+ );
843
+ }
844
+ return mailbox.drain(recipient);
845
+ }
846
+
847
+ _recordShadowDivergence(onDivergence, details, action) {
848
+ try {
849
+ return action();
850
+ } catch (error) {
851
+ try {
852
+ onDivergence?.({
853
+ ...details,
854
+ code: error?.code || "CC_TEAM_GRAPH_SHADOW_DIVERGED",
855
+ error: error?.message || String(error),
856
+ });
857
+ } catch {
858
+ // Shadow reporting must never become an execution authority.
859
+ }
860
+ return null;
861
+ }
862
+ }
863
+
864
+ _shadowGraphMessage(mailbox, request, projected) {
865
+ const projectionKey = shadowMailboxProjectionKey(this.runId, projected);
866
+ const payload = {
867
+ ...mailboxPayload(request, projectionKey),
868
+ legacyMessageId: projected.id,
869
+ legacyPayloadDigest: projected.payloadDigest || null,
870
+ };
871
+ const source = this._messageSource(request);
872
+ const messages = this._mailboxRecipients(mailbox, request).map(
873
+ (recipient) =>
874
+ this.kernel.sendMessage(this.runId, {
875
+ messageId: graphMessageId(projectionKey, recipient),
876
+ ...source,
877
+ toAgentId: recipient,
878
+ mode: payload.mode,
879
+ payload,
880
+ causationId: payload.causationId,
881
+ correlationId: payload.correlationId,
882
+ dataPolicy: {
883
+ origin: `graph-shadow:${this.runId}`,
884
+ trust: "trusted_host",
885
+ sensitivity: "internal",
886
+ allowedSinks: [`agent:${recipient}`],
887
+ },
888
+ }),
889
+ );
890
+ for (const message of messages) {
891
+ this.kernel.deliverMessage(this.runId, message.id);
892
+ }
893
+ return messages;
894
+ }
895
+
896
+ _shadowGraphMessageId(message, recipient) {
897
+ return graphMessageId(
898
+ shadowMailboxProjectionKey(this.runId, message),
899
+ recipient,
900
+ );
901
+ }
902
+
903
+ _shadowBindMailbox(mailbox, onDivergence) {
904
+ const adapter = this;
905
+ const overrides = {
906
+ send(request = {}) {
907
+ const projected = mailbox.send(request);
908
+ adapter._recordShadowDivergence(
909
+ onDivergence,
910
+ { phase: "message-send", messageId: projected?.id || null },
911
+ () => adapter._shadowGraphMessage(mailbox, request, projected),
912
+ );
913
+ return projected;
914
+ },
915
+ receive(recipient, options = {}) {
916
+ const messages = mailbox.receive(recipient, options);
917
+ adapter._recordShadowDivergence(
918
+ onDivergence,
919
+ { phase: "message-receive", recipient },
920
+ () => {
921
+ for (const message of messages) {
922
+ adapter.kernel.deliverMessage(
923
+ adapter.runId,
924
+ adapter._shadowGraphMessageId(message, recipient),
925
+ );
926
+ }
927
+ if (messages.length > 0) {
928
+ adapter.kernel.receiveMessages(adapter.runId, recipient, {
929
+ markRead: options.markRead === true,
930
+ });
931
+ }
932
+ },
933
+ );
934
+ return messages;
935
+ },
936
+ acknowledge(recipient, options = {}) {
937
+ const result = mailbox.acknowledge(recipient, options);
938
+ adapter._recordShadowDivergence(
939
+ onDivergence,
940
+ { phase: "message-ack", recipient },
941
+ () => {
942
+ const byId = new Map(
943
+ mailbox.log().map((message) => [message.id, message]),
944
+ );
945
+ for (const id of options.messageIds || []) {
946
+ const message = byId.get(id);
947
+ if (!message) {
948
+ throw adapterError(
949
+ "CC_TEAM_GRAPH_MESSAGE_PROJECTION_MISSING",
950
+ `shadow Team message ${id} is outside the legacy log`,
951
+ );
952
+ }
953
+ const graphId = adapter._shadowGraphMessageId(message, recipient);
954
+ adapter.kernel.deliverMessage(adapter.runId, graphId);
955
+ if (options.status === "dead_letter") {
956
+ adapter.kernel.deadLetterMessage(
957
+ adapter.runId,
958
+ graphId,
959
+ options.reason || "poison_message",
960
+ );
961
+ } else if (options.status === "read") {
962
+ adapter.kernel.receiveMessages(adapter.runId, recipient, {
963
+ markRead: true,
964
+ });
965
+ } else {
966
+ adapter.kernel.processMessage(
967
+ adapter.runId,
968
+ graphId,
969
+ recipient,
970
+ String(options.consumerKey || ""),
971
+ );
972
+ }
973
+ }
974
+ },
975
+ );
976
+ return result;
977
+ },
978
+ drain(recipient) {
979
+ const messages = mailbox.drain(recipient);
980
+ adapter._recordShadowDivergence(
981
+ onDivergence,
982
+ { phase: "message-drain", recipient },
983
+ () => {
984
+ for (const message of messages) {
985
+ const graphId = adapter._shadowGraphMessageId(message, recipient);
986
+ adapter.kernel.deliverMessage(adapter.runId, graphId);
987
+ adapter.kernel.processMessage(
988
+ adapter.runId,
989
+ graphId,
990
+ recipient,
991
+ safeIdentifier(
992
+ `shadow-drain:${recipient}:${message.id}`,
993
+ "consumer",
994
+ ),
995
+ );
996
+ }
997
+ },
998
+ );
999
+ return messages;
1000
+ },
1001
+ };
1002
+ return new Proxy(mailbox, {
1003
+ get(target, property, receiver) {
1004
+ if (Object.hasOwn(overrides, property)) return overrides[property];
1005
+ const value = Reflect.get(target, property, receiver);
1006
+ return typeof value === "function" ? value.bind(target) : value;
1007
+ },
1008
+ });
1009
+ }
1010
+
1011
+ _reconcileMailboxProjection(mailbox) {
1012
+ const collaboration = this.kernel.collaborationState(this.runId);
1013
+ const groups = new Map();
1014
+ for (const message of collaboration.messages) {
1015
+ const payload = message.payload;
1016
+ if (
1017
+ payload?.schema !== TEAM_MAILBOX_GRAPH_SCHEMA ||
1018
+ !payload.projectionKey
1019
+ ) {
1020
+ continue;
1021
+ }
1022
+ const group = groups.get(payload.projectionKey) || {
1023
+ payload,
1024
+ messages: [],
1025
+ };
1026
+ group.messages.push(message);
1027
+ groups.set(payload.projectionKey, group);
1028
+ }
1029
+ const consumers = new Map(
1030
+ collaboration.messageConsumers.map((receipt) => [
1031
+ receipt.messageId,
1032
+ receipt,
1033
+ ]),
1034
+ );
1035
+ for (const [projectionKey, group] of groups) {
1036
+ const projected = mailbox.send({
1037
+ from: group.payload.from,
1038
+ to: group.payload.to,
1039
+ subject: group.payload.subject,
1040
+ body: group.payload.body,
1041
+ mode: group.payload.mode,
1042
+ causationId: group.payload.causationId,
1043
+ correlationId: group.payload.correlationId,
1044
+ senderAttempt: group.payload.senderAttempt,
1045
+ idempotencyKey: projectionKey,
1046
+ });
1047
+ for (const message of group.messages) {
1048
+ if (message.status === "admitted") {
1049
+ this.kernel.deliverMessage(this.runId, message.id);
1050
+ }
1051
+ const receipt = consumers.get(message.id);
1052
+ if (receipt) {
1053
+ mailbox.acknowledge(message.toAgentId, {
1054
+ messageIds: [projected.id],
1055
+ consumerKey: receipt.consumerKey,
1056
+ status: "processed",
1057
+ });
1058
+ } else if (message.status === "dead_letter") {
1059
+ mailbox.acknowledge(message.toAgentId, {
1060
+ messageIds: [projected.id],
1061
+ consumerKey: `graph-dead-letter:${message.id}`,
1062
+ status: "dead_letter",
1063
+ reason: message.reason || "poison_message",
1064
+ });
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+
1070
+ _activeHandoffAttempt(key) {
1071
+ const active = this.attempts.get(key)?.attempt;
1072
+ if (!active || active.status !== "active") {
1073
+ throw adapterError(
1074
+ "CC_TEAM_GRAPH_HANDOFF_ATTEMPT_MISSING",
1075
+ `canonical Team handoff has no active Graph attempt for ${key}`,
1076
+ );
1077
+ }
1078
+ return active;
1079
+ }
1080
+
1081
+ _offerRegistryHandoff(registry, key, options = {}) {
1082
+ const attempt = this._activeHandoffAttempt(key);
1083
+ const ttlMs =
1084
+ Number.isSafeInteger(Number(options.ttlMs)) && Number(options.ttlMs) > 0
1085
+ ? Number(options.ttlMs)
1086
+ : 5 * 60 * 1000;
1087
+ const handoffId = safeIdentifier(
1088
+ options.handoffId ||
1089
+ `team-handoff:${graphDigest(
1090
+ { runId: this.runId, key, nonce: this.createId() },
1091
+ "cc.team.handoff/v1",
1092
+ ).slice(7, 39)}`,
1093
+ "team-handoff",
1094
+ );
1095
+ this.kernel.offerHandoff(this.runId, {
1096
+ handoffId,
1097
+ fromAttemptId: attempt.id,
1098
+ leaseId: attempt.leaseId,
1099
+ fence: attempt.fence,
1100
+ toAgentId: options.toHolder,
1101
+ artifactIds: [],
1102
+ preconditions: {
1103
+ ...(options.preconditions || {}),
1104
+ legacyArtifactIds: [...(options.artifactIds || [])],
1105
+ summary: options.summary ?? null,
1106
+ legacyProjection: {
1107
+ fromHolder: options.holder,
1108
+ fromLeaseId: options.leaseId,
1109
+ fromFence: attempt.grant?.legacyFence ?? options.leaseId,
1110
+ idempotencyKey: options.idempotencyKey ?? handoffId,
1111
+ },
1112
+ },
1113
+ ttlMs,
1114
+ });
1115
+ const projected = registry.offerHandoff(key, {
1116
+ ...options,
1117
+ handoffId,
1118
+ ttlMs,
1119
+ revisionDigest: this.kernel.getRun(this.runId).revisionDigest,
1120
+ authorityDigest: this.kernel.getRun(this.runId).authorityDigest,
1121
+ });
1122
+ if (!projected?.ok) {
1123
+ throw adapterError(
1124
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1125
+ `legacy Team handoff offer rejected canonical custody: ${projected?.reason || "unknown"}`,
1126
+ { handoffId, key },
1127
+ );
1128
+ }
1129
+ return projected;
1130
+ }
1131
+
1132
+ _acceptRegistryHandoff(registry, handoffId, options = {}) {
1133
+ this.kernel.acceptHandoff(this.runId, handoffId, options.holder);
1134
+ const projected = registry.acceptHandoff(handoffId, options);
1135
+ if (!projected?.ok) {
1136
+ throw adapterError(
1137
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1138
+ `legacy Team handoff accept rejected canonical custody: ${projected?.reason || "unknown"}`,
1139
+ { handoffId },
1140
+ );
1141
+ }
1142
+ return projected;
1143
+ }
1144
+
1145
+ _rejectRegistryHandoff(registry, handoffId, options = {}) {
1146
+ this.kernel.rejectHandoff(
1147
+ this.runId,
1148
+ handoffId,
1149
+ options.holder,
1150
+ options.reason,
1151
+ );
1152
+ const projected = registry.rejectHandoff(handoffId, options);
1153
+ if (!projected?.ok) {
1154
+ throw adapterError(
1155
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1156
+ `legacy Team handoff reject rejected canonical custody: ${projected?.reason || "unknown"}`,
1157
+ { handoffId },
1158
+ );
1159
+ }
1160
+ return projected;
1161
+ }
1162
+
1163
+ _commitRegistryHandoff(registry, handoffId, options = {}) {
1164
+ const found = registry.findHandoff(handoffId);
1165
+ if (!found) {
1166
+ throw adapterError(
1167
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_MISSING",
1168
+ `legacy Team handoff is missing: ${handoffId}`,
1169
+ );
1170
+ }
1171
+ const committed = this.kernel.commitHandoff(this.runId, handoffId, {
1172
+ attemptId: safeIdentifier(
1173
+ `team-attempt:${graphDigest(
1174
+ { runId: this.runId, handoffId, holder: found.handoff.toHolder },
1175
+ "cc.team.handoff-attempt/v1",
1176
+ ).slice(7, 39)}`,
1177
+ "team-attempt",
1178
+ ),
1179
+ leaseId: safeIdentifier(
1180
+ `graph-handoff-lease:${graphDigest(
1181
+ { runId: this.runId, handoffId },
1182
+ "cc.team.handoff-lease/v1",
1183
+ ).slice(7, 39)}`,
1184
+ "graph-handoff-lease",
1185
+ ),
1186
+ ttlMs: options.ttlMs || this.writerLeaseTtlMs,
1187
+ });
1188
+ this.attempts.set(found.key, {
1189
+ attempt: committed.assignmentAttempt,
1190
+ effect: null,
1191
+ handoffId,
1192
+ });
1193
+ const projected = registry.commitHandoff(handoffId, options);
1194
+ if (!projected?.ok) {
1195
+ throw adapterError(
1196
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1197
+ `legacy Team handoff commit rejected canonical custody: ${projected?.reason || "unknown"}`,
1198
+ { handoffId, key: found.key },
1199
+ );
1200
+ }
1201
+ return projected;
1202
+ }
1203
+
1204
+ _revokeRegistryHandoff(registry, handoffId, options = {}) {
1205
+ const found = registry.findHandoff(handoffId);
1206
+ if (!found) {
1207
+ throw adapterError(
1208
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_MISSING",
1209
+ `legacy Team handoff is missing: ${handoffId}`,
1210
+ );
1211
+ }
1212
+ const attempt = this._activeHandoffAttempt(found.key);
1213
+ this.kernel.revokeHandoff(
1214
+ this.runId,
1215
+ handoffId,
1216
+ attempt.id,
1217
+ attempt.leaseId,
1218
+ attempt.fence,
1219
+ );
1220
+ const projected = registry.revokeHandoff(handoffId, options);
1221
+ if (!projected?.ok) {
1222
+ throw adapterError(
1223
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1224
+ `legacy Team handoff revoke rejected canonical custody: ${projected?.reason || "unknown"}`,
1225
+ { handoffId, key: found.key },
1226
+ );
1227
+ }
1228
+ return projected;
1229
+ }
1230
+
1231
+ _reconcileHandoffProjection(registry) {
1232
+ const handoffs = this.kernel.collaborationState(this.runId).handoffs;
1233
+ if (handoffs.length === 0) return;
1234
+ if (typeof registry.applyCanonicalHandoffProjection !== "function") {
1235
+ throw adapterError(
1236
+ "CC_TEAM_GRAPH_LEGACY_PROJECTION_UNSUPPORTED",
1237
+ "Team registry cannot repair canonical handoff custody",
1238
+ );
1239
+ }
1240
+ const projection = this.kernel.getRun(this.runId);
1241
+ const attempts = new Map(
1242
+ projection.attempts.map((attempt) => [attempt.id, attempt]),
1243
+ );
1244
+ for (const handoff of handoffs) {
1245
+ const key = this.nodeToTask.get(handoff.nodeId);
1246
+ const sourceAttempt = attempts.get(handoff.fromAttemptId);
1247
+ const legacy = handoff.preconditions?.legacyProjection || null;
1248
+ if (!key || !sourceAttempt || !legacy) {
1249
+ throw adapterError(
1250
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_CORRUPT",
1251
+ `canonical Team handoff lacks its legacy projection binding: ${handoff.id}`,
1252
+ { handoffId: handoff.id, nodeId: handoff.nodeId },
1253
+ );
1254
+ }
1255
+ const {
1256
+ legacyArtifactIds = [],
1257
+ summary = null,
1258
+ legacyProjection: _legacyProjection,
1259
+ ...preconditions
1260
+ } = handoff.preconditions || {};
1261
+ const createdAt = Date.parse(handoff.createdAt);
1262
+ const updatedAt = Date.parse(handoff.updatedAt);
1263
+ const expiresAtMs = Number(handoff.expiresAtMs);
1264
+ const targetAttempt = handoff.committedAttemptId
1265
+ ? attempts.get(handoff.committedAttemptId)
1266
+ : null;
1267
+ if (
1268
+ !Number.isFinite(createdAt) ||
1269
+ !Number.isFinite(updatedAt) ||
1270
+ !Number.isFinite(expiresAtMs) ||
1271
+ (handoff.status === "committed" && !targetAttempt)
1272
+ ) {
1273
+ throw adapterError(
1274
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_CORRUPT",
1275
+ `canonical Team handoff has invalid durable timestamps or target custody: ${handoff.id}`,
1276
+ { handoffId: handoff.id },
1277
+ );
1278
+ }
1279
+ const result = registry.applyCanonicalHandoffProjection(key, {
1280
+ handoff: {
1281
+ id: handoff.id,
1282
+ taskKey: key,
1283
+ fromHolder: legacy.fromHolder || sourceAttempt.agentId,
1284
+ fromLeaseId: legacy.fromLeaseId,
1285
+ fromFence: legacy.fromFence,
1286
+ toHolder: handoff.toAgentId,
1287
+ revisionDigest: handoff.revisionDigest,
1288
+ authorityDigest: handoff.authorityDigest,
1289
+ artifactIds: legacyArtifactIds,
1290
+ preconditions,
1291
+ summary,
1292
+ idempotencyKey: legacy.idempotencyKey || handoff.id,
1293
+ ttlMs: Math.max(1, expiresAtMs - createdAt),
1294
+ expiresAtMs,
1295
+ status: handoff.status,
1296
+ offeredAt: createdAt,
1297
+ acceptedAt:
1298
+ handoff.acceptedAt == null ? null : Date.parse(handoff.acceptedAt),
1299
+ rejectedAt:
1300
+ handoff.rejectedAt == null ? null : Date.parse(handoff.rejectedAt),
1301
+ committedAt:
1302
+ handoff.committedAt == null
1303
+ ? null
1304
+ : Date.parse(handoff.committedAt),
1305
+ revokedAt:
1306
+ handoff.revokedAt == null ? null : Date.parse(handoff.revokedAt),
1307
+ expiredAt:
1308
+ handoff.expiredAt == null ? null : Date.parse(handoff.expiredAt),
1309
+ reason: handoff.reason || null,
1310
+ updatedAt,
1311
+ },
1312
+ targetLease: targetAttempt
1313
+ ? {
1314
+ holder: targetAttempt.agentId,
1315
+ leaseId: targetAttempt.leaseId,
1316
+ fencingToken: targetAttempt.fence,
1317
+ acquiredAt: Date.parse(targetAttempt.createdAt),
1318
+ expiresAt: Date.parse(targetAttempt.expiresAt),
1319
+ renewals: 0,
1320
+ stolen: false,
1321
+ handoffId: handoff.id,
1322
+ transferredFromLeaseId: legacy.fromLeaseId,
1323
+ authorityGeneration: targetAttempt.authorityGeneration,
1324
+ writerId: targetAttempt.writerId,
1325
+ }
1326
+ : null,
1327
+ now: this.now(),
1328
+ });
1329
+ if (!result?.ok) {
1330
+ throw adapterError(
1331
+ "CC_TEAM_GRAPH_HANDOFF_PROJECTION_DIVERGED",
1332
+ `legacy Team handoff projection rejected canonical custody: ${result?.reason || "unknown"}`,
1333
+ { handoffId: handoff.id, key },
1334
+ );
1335
+ }
1336
+ }
1337
+ }
1338
+
1339
+ _shadowBindRegistry(registry, onDivergence) {
1340
+ const adapter = this;
1341
+ const observe = (phase, details, action) =>
1342
+ adapter._recordShadowDivergence(
1343
+ onDivergence,
1344
+ { phase, ...details },
1345
+ action,
1346
+ );
1347
+ const overrides = {
1348
+ addTask(definition) {
1349
+ const result = registry.addTask(definition);
1350
+ if (result?.ok && !result.idempotent) {
1351
+ observe("task-append", { key: definition?.key || null }, () =>
1352
+ adapter.appendTask(registry.getTask(definition.key) || definition),
1353
+ );
1354
+ }
1355
+ return result;
1356
+ },
1357
+ offerHandoff(key, options = {}) {
1358
+ const result = registry.offerHandoff(key, options);
1359
+ if (result?.ok) {
1360
+ observe(
1361
+ "handoff-offer",
1362
+ { key, handoffId: result.handoff?.id || null },
1363
+ () => {
1364
+ const attempt = adapter._activeHandoffAttempt(key);
1365
+ const handoff = result.handoff;
1366
+ return adapter.kernel.offerHandoff(adapter.runId, {
1367
+ handoffId: handoff.id,
1368
+ fromAttemptId: attempt.id,
1369
+ leaseId: attempt.leaseId,
1370
+ fence: attempt.fence,
1371
+ toAgentId: handoff.toHolder,
1372
+ artifactIds: [],
1373
+ preconditions: {
1374
+ ...(handoff.preconditions || {}),
1375
+ legacyArtifactIds: [...(handoff.artifactIds || [])],
1376
+ summary: handoff.summary ?? null,
1377
+ legacyProjection: {
1378
+ fromHolder: handoff.fromHolder,
1379
+ fromLeaseId: handoff.fromLeaseId,
1380
+ fromFence: handoff.fromFence,
1381
+ idempotencyKey: handoff.idempotencyKey,
1382
+ },
1383
+ },
1384
+ ttlMs: handoff.ttlMs,
1385
+ });
1386
+ },
1387
+ );
1388
+ }
1389
+ return result;
1390
+ },
1391
+ acceptHandoff(handoffId, options = {}) {
1392
+ const result = registry.acceptHandoff(handoffId, options);
1393
+ if (result?.ok) {
1394
+ observe("handoff-accept", { handoffId }, () =>
1395
+ adapter.kernel.acceptHandoff(
1396
+ adapter.runId,
1397
+ handoffId,
1398
+ options.holder,
1399
+ ),
1400
+ );
1401
+ }
1402
+ return result;
1403
+ },
1404
+ rejectHandoff(handoffId, options = {}) {
1405
+ const result = registry.rejectHandoff(handoffId, options);
1406
+ if (result?.ok) {
1407
+ observe("handoff-reject", { handoffId }, () =>
1408
+ adapter.kernel.rejectHandoff(
1409
+ adapter.runId,
1410
+ handoffId,
1411
+ options.holder,
1412
+ options.reason,
1413
+ ),
1414
+ );
1415
+ }
1416
+ return result;
1417
+ },
1418
+ commitHandoff(handoffId, options = {}) {
1419
+ const result = registry.commitHandoff(handoffId, options);
1420
+ if (result?.ok) {
1421
+ observe("handoff-commit", { handoffId, key: result.key }, () => {
1422
+ const committed = adapter.kernel.commitHandoff(
1423
+ adapter.runId,
1424
+ handoffId,
1425
+ {
1426
+ attemptId: safeIdentifier(
1427
+ `team-shadow-attempt:${graphDigest(
1428
+ {
1429
+ runId: adapter.runId,
1430
+ handoffId,
1431
+ holder: result.handoff.toHolder,
1432
+ },
1433
+ "cc.team.shadow-handoff-attempt/v1",
1434
+ ).slice(7, 39)}`,
1435
+ "team-shadow-attempt",
1436
+ ),
1437
+ leaseId: safeIdentifier(
1438
+ `team-shadow-lease:${graphDigest(
1439
+ { runId: adapter.runId, handoffId },
1440
+ "cc.team.shadow-handoff-lease/v1",
1441
+ ).slice(7, 39)}`,
1442
+ "team-shadow-lease",
1443
+ ),
1444
+ ttlMs: options.ttlMs || adapter.writerLeaseTtlMs,
1445
+ },
1446
+ );
1447
+ adapter.attempts.set(result.key, {
1448
+ attempt: committed.assignmentAttempt,
1449
+ effect: null,
1450
+ handoffId,
1451
+ });
1452
+ return committed;
1453
+ });
1454
+ }
1455
+ return result;
1456
+ },
1457
+ revokeHandoff(handoffId, options = {}) {
1458
+ const result = registry.revokeHandoff(handoffId, options);
1459
+ if (result?.ok) {
1460
+ observe("handoff-revoke", { handoffId, key: result.key }, () => {
1461
+ const found = registry.findHandoff(handoffId);
1462
+ const attempt = adapter._activeHandoffAttempt(
1463
+ result.key || found?.key,
1464
+ );
1465
+ return adapter.kernel.revokeHandoff(
1466
+ adapter.runId,
1467
+ handoffId,
1468
+ attempt.id,
1469
+ attempt.leaseId,
1470
+ attempt.fence,
1471
+ );
1472
+ });
1473
+ }
1474
+ return result;
1475
+ },
1476
+ expireHandoffs(...args) {
1477
+ const result = registry.expireHandoffs(...args);
1478
+ observe("handoff-expire", {}, () => adapter.kernel.tick(adapter.runId));
1479
+ return result;
1480
+ },
1481
+ };
1482
+ return new Proxy(registry, {
1483
+ get(target, property, receiver) {
1484
+ if (Object.hasOwn(overrides, property)) return overrides[property];
1485
+ const value = Reflect.get(target, property, receiver);
1486
+ return typeof value === "function" ? value.bind(target) : value;
1487
+ },
1488
+ set(target, property, value, receiver) {
1489
+ return Reflect.set(target, property, value, receiver);
1490
+ },
1491
+ });
1492
+ }
1493
+
1494
+ bindRegistry(registry, { onDivergence = null } = {}) {
1495
+ if (!registry || typeof registry.getTask !== "function") {
1496
+ throw new TypeError("bindRegistry requires a Team task registry");
1497
+ }
1498
+ if (this.authorityMode === "shadow") {
1499
+ return this._shadowBindRegistry(registry, onDivergence);
1500
+ }
1501
+ if (this.authorityMode !== "canonical") return registry;
1502
+ this._reconcileHandoffProjection(registry);
1503
+ const adapter = this;
1504
+ const overrides = {
1505
+ addTask(definition) {
1506
+ const appended = adapter.appendTask(definition);
1507
+ const projected = registry.addTask(appended.definition);
1508
+ if (!projected?.ok && !registry.getTask(definition?.key)) {
1509
+ throw adapterError(
1510
+ "CC_TEAM_GRAPH_LEGACY_PROJECTION_DIVERGED",
1511
+ `legacy Team registry rejected canonical dynamic task: ${projected?.reason || "unknown"}`,
1512
+ { key: definition?.key || null },
1513
+ );
1514
+ }
1515
+ return projected;
1516
+ },
1517
+ offerHandoff(key, options) {
1518
+ return adapter._offerRegistryHandoff(registry, key, options);
1519
+ },
1520
+ acceptHandoff(handoffId, options) {
1521
+ return adapter._acceptRegistryHandoff(registry, handoffId, options);
1522
+ },
1523
+ rejectHandoff(handoffId, options) {
1524
+ return adapter._rejectRegistryHandoff(registry, handoffId, options);
1525
+ },
1526
+ commitHandoff(handoffId, options) {
1527
+ return adapter._commitRegistryHandoff(registry, handoffId, options);
1528
+ },
1529
+ revokeHandoff(handoffId, options) {
1530
+ return adapter._revokeRegistryHandoff(registry, handoffId, options);
1531
+ },
1532
+ expireHandoffs(...args) {
1533
+ adapter.kernel.tick(adapter.runId);
1534
+ return registry.expireHandoffs(...args);
1535
+ },
1536
+ };
1537
+ return new Proxy(registry, {
1538
+ get(target, property, receiver) {
1539
+ if (Object.hasOwn(overrides, property)) return overrides[property];
1540
+ const value = Reflect.get(target, property, receiver);
1541
+ return typeof value === "function" ? value.bind(target) : value;
1542
+ },
1543
+ set(target, property, value, receiver) {
1544
+ return Reflect.set(target, property, value, receiver);
1545
+ },
1546
+ });
1547
+ }
1548
+
1549
+ bindMailbox(mailbox, { onDivergence = null } = {}) {
1550
+ if (!mailbox || typeof mailbox.send !== "function") {
1551
+ throw new TypeError("bindMailbox requires a Team mailbox adapter");
1552
+ }
1553
+ if (this.authorityMode === "shadow") {
1554
+ return this._shadowBindMailbox(mailbox, onDivergence);
1555
+ }
1556
+ if (this.authorityMode !== "canonical") return mailbox;
1557
+ this._reconcileMailboxProjection(mailbox);
1558
+ const adapter = this;
1559
+ const overrides = {
1560
+ send(request) {
1561
+ return adapter._sendMailboxMessage(mailbox, request || {});
1562
+ },
1563
+ receive(recipient, options) {
1564
+ return adapter._receiveMailboxMessages(mailbox, recipient, options);
1565
+ },
1566
+ acknowledge(recipient, options) {
1567
+ return adapter._acknowledgeMailboxMessages(mailbox, recipient, options);
1568
+ },
1569
+ drain(recipient) {
1570
+ return adapter._drainMailbox(mailbox, recipient);
1571
+ },
1572
+ };
1573
+ return new Proxy(mailbox, {
1574
+ get(target, property, receiver) {
1575
+ if (Object.hasOwn(overrides, property)) return overrides[property];
1576
+ const value = Reflect.get(target, property, receiver);
1577
+ return typeof value === "function" ? value.bind(target) : value;
1578
+ },
1579
+ });
1580
+ }
1581
+
1582
+ nextReadyTaskKey({ excludeKeys = null } = {}) {
1583
+ if (!this.kernel || !this.runId) {
1584
+ throw adapterError(
1585
+ "CC_TEAM_GRAPH_NOT_OPEN",
1586
+ "canonical Team Graph adapter is not open",
1587
+ );
1588
+ }
1589
+ const excluded =
1590
+ excludeKeys instanceof Set ? excludeKeys : new Set(excludeKeys || []);
1591
+ for (const candidate of this.kernel.readyNodes(this.runId)) {
1592
+ const key = this.nodeToTask.get(candidate.nodeId);
1593
+ if (key && !excluded.has(key)) return key;
1594
+ }
1595
+ return null;
1596
+ }
1597
+
1598
+ appendTask(task) {
1599
+ if (!this.dynamic || !this.producerLease) {
1600
+ throw adapterError(
1601
+ "CC_TEAM_GRAPH_DYNAMIC_DISABLED",
1602
+ "canonical Team Graph is not open for dynamic follow-up tasks",
1603
+ );
1604
+ }
1605
+ const definition = persistedTeamTaskDefinition(task || {});
1606
+ if (!definition.key || !definition.title) {
1607
+ throw adapterError(
1608
+ "CC_TEAM_GRAPH_DYNAMIC_TASK_INVALID",
1609
+ "dynamic Team task requires a stable key and title",
1610
+ );
1611
+ }
1612
+ const existing = this.taskDefinitions.get(definition.key);
1613
+ if (existing) {
1614
+ if (
1615
+ graphDigest(existing, "cc.team.task-definition/v1") !==
1616
+ graphDigest(definition, "cc.team.task-definition/v1")
1617
+ ) {
1618
+ throw adapterError(
1619
+ "CC_TEAM_GRAPH_DYNAMIC_TASK_CONFLICT",
1620
+ `dynamic Team task key was reused with different content: ${definition.key}`,
1621
+ );
1622
+ }
1623
+ return Object.freeze({
1624
+ idempotent: true,
1625
+ nodeId: this.taskToNode.get(definition.key),
1626
+ definition,
1627
+ });
1628
+ }
1629
+ const nodeId = safeIdentifier(definition.key, "team-node");
1630
+ if (this.nodeToTask.has(nodeId)) {
1631
+ throw adapterError(
1632
+ "CC_TEAM_GRAPH_NODE_COLLISION",
1633
+ `dynamic Team task collides with an existing Graph node: ${definition.key}`,
1634
+ );
1635
+ }
1636
+ if (this.executionMode !== "dry-run" && !this.worktree) {
1637
+ const ordered = new Set();
1638
+ const visit = (key) => {
1639
+ if (!key || ordered.has(key)) return;
1640
+ ordered.add(key);
1641
+ const dependency = this.taskDefinitions.get(key);
1642
+ for (const parent of dependency?.dependsOn || []) visit(parent);
1643
+ };
1644
+ for (const dependency of definition.dependsOn) visit(dependency);
1645
+ const nextScopes = taskWriteScopes(definition);
1646
+ for (const [key, candidate] of this.taskDefinitions) {
1647
+ if (
1648
+ !ordered.has(key) &&
1649
+ scopesOverlap(nextScopes, taskWriteScopes(candidate))
1650
+ ) {
1651
+ definition.dependsOn.push(key);
1652
+ visit(key);
1653
+ }
1654
+ }
1655
+ definition.dependsOn = [...new Set(definition.dependsOn)];
1656
+ }
1657
+ const nextTaskToNode = new Map(this.taskToNode);
1658
+ nextTaskToNode.set(definition.key, nodeId);
1659
+ const node = compileTeamTaskNode(definition, nextTaskToNode, {
1660
+ executionMode: this.executionMode,
1661
+ worktree: this.worktree,
1662
+ maxAttempts: this.maxAttempts,
1663
+ });
1664
+ const nextDefinitions = new Map(this.taskDefinitions);
1665
+ nextDefinitions.set(definition.key, definition);
1666
+ const requestId = safeIdentifier(
1667
+ `team-append:${graphDigest(
1668
+ { runId: this.runId, definition },
1669
+ "cc.team.dynamic-task/v1",
1670
+ ).slice(7, 39)}`,
1671
+ "team-append",
1672
+ );
1673
+ const result = this.kernel.appendGraph(this.runId, {
1674
+ expectedGraphRevision: this.kernel.getRun(this.runId).graphRevision,
1675
+ requestId,
1676
+ producerLeaseId: this.producerLease.id,
1677
+ producerFence: this.producerLease.fence,
1678
+ nodes: [node],
1679
+ metadataPatch: {
1680
+ taskKeyByNodeId: Object.fromEntries(
1681
+ [...nextTaskToNode].map(([taskKey, id]) => [id, taskKey]),
1682
+ ),
1683
+ taskDefinitionsByNodeId: Object.fromEntries(
1684
+ [...nextDefinitions].map(([taskKey, value]) => [
1685
+ nextTaskToNode.get(taskKey),
1686
+ value,
1687
+ ]),
1688
+ ),
1689
+ },
1690
+ });
1691
+ this.taskToNode = nextTaskToNode;
1692
+ this.nodeToTask.set(nodeId, definition.key);
1693
+ this.taskDefinitions = nextDefinitions;
1694
+ return Object.freeze({ ...result, nodeId, definition });
1695
+ }
1696
+
1697
+ finalize() {
1698
+ if (!this.kernel || !this.runId) return null;
1699
+ const projection = this.kernel.getRun(this.runId);
1700
+ if (projection.phase !== "open") return projection;
1701
+ if (this.producerLease) {
1702
+ try {
1703
+ this.kernel.releaseProducerLease(
1704
+ this.runId,
1705
+ this.producerLease.id,
1706
+ this.producerLease.fence,
1707
+ );
1708
+ } catch (error) {
1709
+ if (error?.code !== "CC_GRAPH_STALE_PRODUCER_LEASE") throw error;
1710
+ }
1711
+ this.producerLease = null;
1712
+ }
1713
+ return this.kernel.sealRun(this.runId);
1714
+ }
1715
+
1716
+ beforeTask({ key, holder, lease, task }) {
1717
+ if (!this.kernel) {
1718
+ throw adapterError(
1719
+ "CC_TEAM_GRAPH_NOT_OPEN",
1720
+ "canonical Team Graph adapter is not open",
1721
+ );
1722
+ }
1723
+ const nodeId = this.taskToNode.get(key);
1724
+ if (!nodeId) {
1725
+ throw adapterError(
1726
+ "CC_TEAM_GRAPH_TASK_UNKNOWN",
1727
+ `canonical Team Graph has no task ${key}`,
1728
+ );
1729
+ }
1730
+ const current = this.attempts.get(key);
1731
+ let attempt =
1732
+ current?.attempt?.status === "active" &&
1733
+ current.attempt.agentId === holder
1734
+ ? current.attempt
1735
+ : null;
1736
+ if (!attempt) {
1737
+ const attemptId = safeIdentifier(
1738
+ `team-attempt:${graphDigest(
1739
+ {
1740
+ runId: this.runId,
1741
+ key,
1742
+ holder,
1743
+ legacyLeaseId: lease?.leaseId || null,
1744
+ legacyFence: lease?.fencingToken ?? null,
1745
+ authorityGeneration: this.kernel.getRun(this.runId)
1746
+ .authorityGeneration,
1747
+ },
1748
+ "cc.team.assignment/v1",
1749
+ ).slice(7, 39)}`,
1750
+ "team-attempt",
1751
+ );
1752
+ attempt = this.kernel.assignNode(this.runId, nodeId, holder, {
1753
+ attemptId,
1754
+ leaseId: safeIdentifier(
1755
+ `graph-lease:${lease?.leaseId || this.createId()}`,
1756
+ "graph-lease",
1757
+ ),
1758
+ ttlMs: this.writerLeaseTtlMs,
1759
+ role: "executor",
1760
+ grant: {
1761
+ legacyLeaseId: lease?.leaseId || null,
1762
+ legacyFence: lease?.fencingToken ?? null,
1763
+ },
1764
+ });
1765
+ }
1766
+ let effect = current?.effect || null;
1767
+ if (attempt.effectIdempotencyKey) {
1768
+ effect = this.kernel.beginEffect(this.runId, {
1769
+ effectId: safeIdentifier(`team-effect:${attempt.id}`, "team-effect"),
1770
+ attemptId: attempt.id,
1771
+ leaseId: attempt.leaseId,
1772
+ fence: attempt.fence,
1773
+ idempotencyKey: attempt.effectIdempotencyKey,
1774
+ operationDigest: graphDigest(
1775
+ {
1776
+ taskKey: key,
1777
+ nodeId,
1778
+ title: task?.title || key,
1779
+ scopePaths: task?.metadata?.scopePaths || [],
1780
+ },
1781
+ "cc.team.operation/v1",
1782
+ ),
1783
+ });
1784
+ if (effect.attemptId !== attempt.id || effect.status !== "started") {
1785
+ throw adapterError(
1786
+ "CC_TEAM_GRAPH_EFFECT_NOT_EXECUTABLE",
1787
+ "canonical Team effect was already owned or terminal under another attempt",
1788
+ { key, attemptId: attempt.id, effectId: effect.id },
1789
+ );
1790
+ }
1791
+ }
1792
+ this.attempts.set(key, { attempt, effect });
1793
+ return attempt;
1794
+ }
1795
+
1796
+ settleTask({ key, task, status, result = null, error = null }) {
1797
+ const active = this.attempts.get(key);
1798
+ if (!active) {
1799
+ throw adapterError(
1800
+ "CC_TEAM_GRAPH_ATTEMPT_MISSING",
1801
+ `canonical Team Graph has no active attempt for ${key}`,
1802
+ );
1803
+ }
1804
+ const { attempt, effect } = active;
1805
+ if (status === "completed") {
1806
+ let evidence;
1807
+ try {
1808
+ evidence = terminalEvidence(result);
1809
+ } catch (error) {
1810
+ if (
1811
+ !["shell", "shell-worktree"].includes(this.executionMode) ||
1812
+ Number(result?.code) !== 0
1813
+ ) {
1814
+ throw error;
1815
+ }
1816
+ evidence = {
1817
+ outputDigest: graphDigest(
1818
+ {
1819
+ runId: this.runId,
1820
+ taskKey: key,
1821
+ nodeId: attempt.nodeId,
1822
+ operationDigest: effect?.operationDigest || null,
1823
+ exitCode: result.code,
1824
+ },
1825
+ "cc.team.shell-execution-receipt/v1",
1826
+ ),
1827
+ };
1828
+ }
1829
+ if (effect) {
1830
+ const receiptDigest =
1831
+ evidence.outputDigest ||
1832
+ graphDigest(evidence, "cc.team.effect-receipt/v1");
1833
+ this.kernel.settleEffect(this.runId, {
1834
+ effectId: effect.id,
1835
+ attemptId: attempt.id,
1836
+ leaseId: attempt.leaseId,
1837
+ fence: attempt.fence,
1838
+ outcome: "committed",
1839
+ receipt: { receiptDigest },
1840
+ });
1841
+ }
1842
+ const settled = this.kernel.settleAttempt(this.runId, {
1843
+ attemptId: attempt.id,
1844
+ leaseId: attempt.leaseId,
1845
+ fence: attempt.fence,
1846
+ outcome: "succeeded",
1847
+ evidence,
1848
+ usage: graphUsage(result),
1849
+ });
1850
+ this.attempts.delete(key);
1851
+ return settled;
1852
+ }
1853
+ if (effect) {
1854
+ const retrySafe = task?.metadata?.retrySafe === true;
1855
+ this.kernel.settleEffect(this.runId, {
1856
+ effectId: effect.id,
1857
+ attemptId: attempt.id,
1858
+ leaseId: attempt.leaseId,
1859
+ fence: attempt.fence,
1860
+ outcome: retrySafe ? "failed" : "unknown",
1861
+ });
1862
+ if (!retrySafe) {
1863
+ this.attempts.delete(key);
1864
+ return this.kernel.getRun(this.runId);
1865
+ }
1866
+ }
1867
+ const settled = this.kernel.settleAttempt(this.runId, {
1868
+ attemptId: attempt.id,
1869
+ leaseId: attempt.leaseId,
1870
+ fence: attempt.fence,
1871
+ outcome: "failed",
1872
+ usage: graphUsage(error),
1873
+ error: error?.message || String(error || "team task failed"),
1874
+ });
1875
+ this.attempts.delete(key);
1876
+ return settled;
1877
+ }
1878
+
1879
+ abandonTask(key, error = null) {
1880
+ const active = this.attempts.get(key);
1881
+ if (!active) return this.status();
1882
+ const { attempt, effect } = active;
1883
+ if (effect) {
1884
+ this.kernel.settleEffect(this.runId, {
1885
+ effectId: effect.id,
1886
+ attemptId: attempt.id,
1887
+ leaseId: attempt.leaseId,
1888
+ fence: attempt.fence,
1889
+ outcome: "unknown",
1890
+ });
1891
+ } else {
1892
+ this.kernel.settleAttempt(this.runId, {
1893
+ attemptId: attempt.id,
1894
+ leaseId: attempt.leaseId,
1895
+ fence: attempt.fence,
1896
+ outcome: "cancelled",
1897
+ error: error?.message || String(error || "legacy projection failed"),
1898
+ });
1899
+ }
1900
+ this.attempts.delete(key);
1901
+ return this.status();
1902
+ }
1903
+
1904
+ status() {
1905
+ if (!this.kernel || !this.runId) return null;
1906
+ return this.kernel.getRun(this.runId);
1907
+ }
1908
+
1909
+ events(options = {}) {
1910
+ if (!this.kernel || !this.runId) return [];
1911
+ return this.kernel.events(this.runId, options);
1912
+ }
1913
+
1914
+ async cancel(reason = "team run cancelled") {
1915
+ if (!this.kernel || !this.runId) return null;
1916
+ return this.kernel.cancelRun(this.runId, { reason });
1917
+ }
1918
+ }