chainlesschain 0.166.6 → 0.166.9

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 (214) hide show
  1. package/README.md +21 -0
  2. package/package.json +5 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CVnFjTfG.js → AIOps-DJvpifGx.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BxkwuuQC.js → ActionButton-D2UhuF_A.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C2670q6z.js → Analytics-C9ho9obu.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CU_rylW6.js → AppLayout-BvRCpFK3.js} +3 -3
  8. package/src/assets/web-panel/assets/{Artifacts-DGJmYqnj.js → Artifacts-jxrySvpk.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BZOQbINJ.js → Audit-ovEw0ZYl.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-DghB4QwF.js → BackgroundAgents-D7H5z6zS.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-l0mwPbta.js → Backup-BuXob8za.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Bv3WSZOh.js → BaseInput-CtnszuQ2.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-s88nBdvt.js → Chat-B_mYE8fJ.js} +6 -6
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Z8CBuB1a.js → ChatBubbleRenderer-DaiA6CsX.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-B5icasEs.js → Checkbox-ierdtcJD.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CsUFqNud.js → Codegen-3VKPJD_B.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DlBtClaW.js → Col-1EdMyf5P.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-xRn1IdLB.js → Community-D6H7UA8K.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-naIr8a_o.js → Compact-BBAZvYIm.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-Dp9esfFE.js → Compliance-CgsTrAoj.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DQlMYJB-.js → Cowork-tnZgLoTE.js} +4 -4
  22. package/src/assets/web-panel/assets/{Cron-Cd0oNLzJ.js → Cron-CPpZw2Nh.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BtDIx3XX.js → Crosschain-BymBcp5z.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-BOlpzEwg.js → DID-DKPxS_K1.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-DV15Axk1.js → Dashboard-DvJhEmH9.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-COXrewN1.js → Dropdown-b8iQyQIZ.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-EjIt0Cm7.js → EmailListRenderer--0-A-zKb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DJlhwb-R.js → FamilyGuardDashboard-C12iDN06.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BMiFl82k.js → Federation-BRhaE2Z8.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BUcDQyxn.js → FormItemContext-C4dnq7yf.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-C25uFxvK.js → GenericCardRenderer-CGAdbR5n.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-D20uaEUU.js → Git-4eA2fYzL.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-CQxYJjCX.js → Governance-BJuY9Y-9.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-m-sczpS5.js → Inference-DZ5Dy4up.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-CBsIlU0S.js → KnowledgeGraph-BMTjRs4A.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-D0-5bqeu.js → Logs-BKEeQEfJ.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-CwDGFu2Q.js → Marketplace-Dhho4fEg.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-7FxJ3omT.js → McpTools-BW6xxSlU.js} +2 -2
  40. package/src/assets/web-panel/assets/{Memory-DOVyb59U.js → Memory-mPqlpSkq.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-DZXl-Hje.js → MobileBridge-Cb9N_DQI.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DlZ8tRXt.js → MobileProjects-BNs8tah_.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-D7Pjx1s_.js → Mtc-CaxBt8tn.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-BND7lX8p.js → MtcAudit-DRPQbp3t.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BknS7B-p.js → Multisig-CRm-d8bK.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-7C2-KPHt.js → NLProgramming-BCCk3LPs.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-DNWdTndN.js → Notes-Cv8SN8gN.js} +4 -4
  48. package/src/assets/web-panel/assets/{NotificationSettings-DCm4KcJZ.js → NotificationSettings-CggIvTh5.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-DyVwwjJx.js → OrderTableRenderer-BW3_bV12.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-Dl2O_qJ9.js → Organization-BuRE-VCO.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-BOFCvk9Q.js → Overflow-DBrm1gju.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-DgdVowY1.js → P2P-CWVnDYgS.js} +2 -2
  53. package/src/assets/web-panel/assets/PdhVaultBrowser-8RKUIpte.js +7 -0
  54. package/src/assets/web-panel/assets/{Permissions-BHNEUUwa.js → Permissions-tDUAxogW.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-CaZbrOdz.js → PersonalDataHub-Dv2-CFis.js} +4 -4
  56. package/src/assets/web-panel/assets/{Pipeline-DJDnoXmE.js → Pipeline-DM6kvFni.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-CBQr2eX8.js → Privacy-DzK-yL0d.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-Yghqa06z.js → ProjectInit-B5UrNpkj.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-HBAtJi8U.js → ProjectSettings-CxJD43oW.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-uJTz77GI.js → Providers-CYl4jRYK.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-k3M5ouTp.js → QrScannerModal-DeH2T0fH.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-CMji45PH.js → QuickAsk-Bw9k_kTk.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DyoZYPls.js → Recommend-Bl_jGwoi.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-d6u01Kq0.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-D4hIs4vV.js → Reputation-Ds_Dfr-k.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-mzE-ElTg.js → Row-BpQUQTNj.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-cg7J3hI6.js → RssFeed-BatpZBGt.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-6M0TkGSZ.js → Search-D7hOObzi.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-B1GQ67qh.js → Security-dXQ4FKNt.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-D_RwU_5S.js → Services-D8wInGPa.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-TZjk-dsW.js → Skeleton-DXQCoYpM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-CG6FcaCV.js → Skills-D3oLRIVn.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BI190QDv.js → Sla-BmP4-LnJ.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BWmxF2Wu.js → SpeechSettings-DLj-xzro.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CwPiGw6L.js → SyncSettings-8KKaaRMr.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-BIXgCeSk.js → Tasks-GyK42I7C.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-CQFgGCoK.js → Templates-n1y_8UBG.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-DAelEdkL.js → Tenant-B39nBph8.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-BbqWK6qh.js → Terminal-DCQoOKXe.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-cmG3zvY5.js → TimelineRenderer-BkhbDfXk.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-C8444NKU.js → Tokens-BeHIIU8D.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-C1Rpnz0I.js → Trigger-BHvWXVOb.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-u4koPiFD.js → Trust-y2WaOSto.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-oPWKn80Z.js → UkeySign-DBFeJBzt.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D0CUUY_5.js → VideoEditing-BEOWitC-.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-fu9pIAbt.js → Wallet-BITedTe6.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Bc5ciuOx.js → WebAuthn-DMwlJVin.js} +2 -2
  89. package/src/assets/web-panel/assets/{WorkflowEditor-BCin4_4N.js → WorkflowEditor-CKfGFEu1.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-B2jbM-XN.js → chat-DR6gnBGF.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-CUVoUQ_2.js → colors-C_Hw5NCn.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-DgOGpkRH.js → compact-item-B8IPwsEu.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DIxU6eKI.js → createContext-D-u4JVnJ.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DoKRKMav.js → hasIn-CZubdS4D.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-ByjrulIZ.js → index-11ASqx-A.js} +1 -1
  97. package/src/assets/web-panel/assets/index-BQ3213fz.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BwYofQJg.js → index-BXS6wYJZ.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CLdpHe6w.js → index-Bi21Etg4.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-2flsPFIX.js → index-Bizm87cX.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BXb-Smmm.js → index-Bv2132PO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B5ySiGYy.js → index-BzoFanEQ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CWbL1-N0.js → index-C25rOz7O.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Cj-us-D_.js → index-C2VmtNaQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C9TsO9T9.js → index-Cbm8F0M2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-eTWPaeBI.js → index-CfniPP5p.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D2yo6ast.js → index-Cg2bCeol.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CPXdBnGi.js → index-CiXkhOkY.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BCLeNqIL.js → index-Cl5RpreD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BtSPhEyC.js → index-CqmLw8cs.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-eYxUASSG.js → index-Ctje8Vl3.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-i5MB455u.js → index-D1w96gg9.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CpvyOt5Z.js → index-D47jt8ET.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CeWSw-z5.js → index-DJ6pG79m.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B3F_p8NA.js → index-DTosx2Wu.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-B9mSYovv.js → index-DY6MHo9D.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DJMHnbmu.js → index-DcFjRT-K.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D-ufbAJB.js → index-DfnzrUxU.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BFUprhNd.js → index-DjRrqooR.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Co4lDEk-.js → index-DmRcgiYT.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DGTZdZOE.js → index-DpjHpRoq.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-DHugFl3v.js → index-Dt-w3xQ7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B-f6l4Vm.js → index-DuOU5apg.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DtyAuGOm.js → index-LvjAKDSl.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-be8SonCh.js → index-_nL79NT8.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BRMEO2Tl.js → index-auyNYjLo.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C88m9lsi.js → index-cVqbas5C.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CqlwIjJx.js → index-ffe7bvqP.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DpTFx8Xx.js → index-idZKyADQ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Cskz--fd.js → index-k1Xa5iw1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CF80xxPG.js → index-mYZJxGH6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Ds8r3Fzb.js → index-rYBHSDcJ.js} +1 -1
  133. package/src/assets/web-panel/assets/index-vJagfET2.js +1 -0
  134. package/src/assets/web-panel/assets/{index-C846lPGD.js → index-xoNssgz_.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-B4chl7iR.js → initDefaultProps-lcU_dT2r.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-DruN9h96.js → motion-Dc7UNXZ1.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-Cl0UtCgU.js → move-Cll8ZXaL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-FX2ttw70.js → mtc-parser-CAb_yLIM.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-BDCneY46.js → omit-DurYMyTe.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-DycsEg8b.js → pickAttrs-BITQyOlw.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-Bc-dBuMv.js → placementArrow-Dg1rH91i.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve--Wwaf8lu.js → responsiveObserve-D_EWIeac.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-vDmHQqHH.js → slide-lK4d3zsh.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-9c52yv3G.js → statusUtils-Bflq-Git.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-BT4BiO6_.js → styleChecker-Ccp0I-wt.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-C7XxWPcP.js → useFlexGapSupport-CYZbByYA.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-BHoMqh4i.js → useFs-B_x0nxFD.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BOLxvsAG.js → usePersonalDataHub-DW55rL85.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-CA7poeeP.js → vnode-Ca6MH3W8.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-B_sAn-co.js → zoom-NdHelOog.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/graph.js +215 -5
  154. package/src/commands/orchestrate.js +61 -9
  155. package/src/commands/team-distributed.js +342 -2
  156. package/src/commands/team.js +169 -7
  157. package/src/data/changelog.json +46 -0
  158. package/src/gateways/ws/message-dispatcher.js +4 -0
  159. package/src/gateways/ws/session-protocol.js +89 -11
  160. package/src/gateways/ws/ws-agent-handler.js +65 -9
  161. package/src/gateways/ws/ws-approval-gate.js +181 -16
  162. package/src/gateways/ws/ws-server.js +16 -6
  163. package/src/generated/cc-agent-protocol.schema.json +154 -3
  164. package/src/lib/agent-team/task-lease.js +487 -3
  165. package/src/lib/agent-team/team-agent-stream.js +16 -0
  166. package/src/lib/agent-team/team-distributed-graph-bridge.js +257 -0
  167. package/src/lib/agent-team/team-distributed-graph-writer.js +523 -0
  168. package/src/lib/agent-team/team-distributed-queue.js +251 -0
  169. package/src/lib/agent-team/team-graph-runtime-adapter.js +1918 -0
  170. package/src/lib/agent-team/team-runner.js +285 -3
  171. package/src/lib/app-server/graph-runtime.js +1078 -0
  172. package/src/lib/app-server/rollout-store.js +32 -0
  173. package/src/lib/app-server/server.js +200 -0
  174. package/src/lib/approval-grant-ledger.js +69 -0
  175. package/src/lib/autonomous-agent.js +11 -0
  176. package/src/lib/codex-app-server-adapter.js +11 -20
  177. package/src/lib/cowork-graph-authority-adapter.js +455 -0
  178. package/src/lib/dynamic-workflow-facade.js +83 -16
  179. package/src/lib/graph-kernel/adapters.js +93 -20
  180. package/src/lib/graph-kernel/authority.js +324 -0
  181. package/src/lib/graph-kernel/compiler.js +67 -21
  182. package/src/lib/graph-kernel/cutover-authority-resolver.js +245 -0
  183. package/src/lib/graph-kernel/cutover-ledger.js +692 -0
  184. package/src/lib/graph-kernel/event-store.js +15 -1
  185. package/src/lib/graph-kernel/graph-runtime-surfaces.json +1427 -0
  186. package/src/lib/graph-kernel/index.js +4 -0
  187. package/src/lib/graph-kernel/migration-saga.js +309 -0
  188. package/src/lib/graph-kernel/retirement-evidence.js +567 -0
  189. package/src/lib/graph-kernel/runtime-surface-manifest.js +481 -0
  190. package/src/lib/graph-kernel/runtime.js +737 -38
  191. package/src/lib/graph-kernel/store-cutover-evidence.js +354 -0
  192. package/src/lib/graph-kernel/trace-reducer.js +6 -0
  193. package/src/lib/graph-kernel/trigger-adapter.js +60 -3
  194. package/src/lib/interaction-adapter.js +61 -9
  195. package/src/lib/legacy-runtime-authority.js +160 -0
  196. package/src/lib/orchestrator.js +28 -0
  197. package/src/lib/policy-decision-event.js +149 -0
  198. package/src/lib/process-execution-broker/platform-sandbox.js +15 -1
  199. package/src/lib/record-replay/index.js +1 -0
  200. package/src/lib/record-replay/playwright-ui-driver.js +327 -0
  201. package/src/lib/scheduler-kernel/graph-authority-adapter.js +512 -0
  202. package/src/lib/scheduler-kernel/runtime.js +75 -2
  203. package/src/lib/webhook-security.js +484 -36
  204. package/src/runtime/coding-agent-events.cjs +13 -11
  205. package/src/runtime/headless-runner.js +17 -1
  206. package/src/runtime/headless-stream.js +66 -3
  207. package/src/runtime/session-slash-commands.js +57 -6
  208. package/src/assets/web-panel/assets/MarkdownRenderer-BwH414Ms.js +0 -1
  209. package/src/assets/web-panel/assets/PdhVaultBrowser-BiaZfQIM.js +0 -7
  210. package/src/assets/web-panel/assets/Projects-DiCGnMjz.js +0 -1
  211. package/src/assets/web-panel/assets/RemoteSession-s1gR38IO.js +0 -5
  212. package/src/assets/web-panel/assets/devWarning-DiSlgCC4.js +0 -1
  213. package/src/assets/web-panel/assets/index-9WIQEHvv.js +0 -1
  214. package/src/assets/web-panel/assets/index-B7mDobAl.js +0 -1
@@ -28,6 +28,8 @@ import { createHash, randomUUID } from "node:crypto";
28
28
 
29
29
  export const DEFAULT_LEASE_TTL_MS = 60000;
30
30
  export const DEFAULT_MAX_ATTEMPTS = 3;
31
+ export const DEFAULT_QUEUE_WAIT_SLO_MS = 10_000;
32
+ export const DEFAULT_AGING_WINDOW_MS = 2_500;
31
33
  export const ADJUDICATION_DECISIONS = Object.freeze({
32
34
  RETRY: "retry",
33
35
  ACCEPT: "accept",
@@ -154,11 +156,21 @@ export class TaskLeaseRegistry {
154
156
  now = () => Date.now(),
155
157
  defaultTtlMs = DEFAULT_LEASE_TTL_MS,
156
158
  maxAttempts = DEFAULT_MAX_ATTEMPTS,
159
+ queueWaitSloMs = DEFAULT_QUEUE_WAIT_SLO_MS,
160
+ agingWindowMs = DEFAULT_AGING_WINDOW_MS,
157
161
  leaseEpoch = randomUUID(),
158
162
  } = {}) {
159
163
  this._now = typeof now === "function" ? now : () => now;
160
164
  this.defaultTtlMs = defaultTtlMs > 0 ? defaultTtlMs : DEFAULT_LEASE_TTL_MS;
161
165
  this.maxAttempts = maxAttempts > 0 ? maxAttempts : DEFAULT_MAX_ATTEMPTS;
166
+ this.queueWaitSloMs =
167
+ Number.isFinite(queueWaitSloMs) && queueWaitSloMs > 0
168
+ ? Math.floor(queueWaitSloMs)
169
+ : DEFAULT_QUEUE_WAIT_SLO_MS;
170
+ this.agingWindowMs =
171
+ Number.isFinite(agingWindowMs) && agingWindowMs > 0
172
+ ? Math.floor(agingWindowMs)
173
+ : DEFAULT_AGING_WINDOW_MS;
162
174
  this._leaseEpoch = String(leaseEpoch || randomUUID());
163
175
  this._leaseSequence = 0;
164
176
  this._tasks = new SharedTaskList({ groupId, now: this._now });
@@ -175,6 +187,11 @@ export class TaskLeaseRegistry {
175
187
  ["low", new Set()],
176
188
  ]);
177
189
  this._leasedKeys = new Set();
190
+ this._priorityInheritanceCache = null;
191
+ // The ready timestamp is part of the durable scheduler contract. Restarts
192
+ // must not reset a task's queue-wait clock and thereby reintroduce
193
+ // starvation under a sustained high-priority stream.
194
+ this._readySinceByKey = new Map();
178
195
  }
179
196
 
180
197
  /**
@@ -300,6 +317,7 @@ export class TaskLeaseRegistry {
300
317
  }
301
318
  dependents.add(key);
302
319
  }
320
+ this._priorityInheritanceCache = null;
303
321
  this._enqueueIfReady(key);
304
322
  return { ok: true, key, id: created.id };
305
323
  }
@@ -325,6 +343,9 @@ export class TaskLeaseRegistry {
325
343
  attempts: t.metadata?.attempts || 0,
326
344
  lease: lease ? { ...lease } : null,
327
345
  assignee: t.assignee,
346
+ createdBy: t.createdBy ?? null,
347
+ createdAt: t.createdAt,
348
+ updatedAt: t.updatedAt,
328
349
  rev: t.rev,
329
350
  // Full metadata is exposed so executors can read user payload
330
351
  // (e.g. a task's shell `command` or agent `prompt`) — it carries the
@@ -402,12 +423,75 @@ export class TaskLeaseRegistry {
402
423
  this._requeueExpiredLeases(now);
403
424
  const excluded =
404
425
  excludeKeys instanceof Set ? excludeKeys : new Set(excludeKeys || []);
426
+ let selected = null;
405
427
  for (const priority of ["high", "normal", "low"]) {
406
428
  for (const key of this._readyByPriority.get(priority)) {
407
- if (!excluded.has(key)) return key;
429
+ if (excluded.has(key)) continue;
430
+ const scheduling = this.schedulingPriority(key, { now });
431
+ const bothUrgent =
432
+ scheduling.sloUrgent && selected?.scheduling?.sloUrgent;
433
+ if (
434
+ !selected ||
435
+ (bothUrgent &&
436
+ scheduling.queueWaitMs > selected.scheduling.queueWaitMs) ||
437
+ (!bothUrgent &&
438
+ (scheduling.total > selected.scheduling.total ||
439
+ (scheduling.total === selected.scheduling.total &&
440
+ scheduling.queueWaitMs > selected.scheduling.queueWaitMs)))
441
+ ) {
442
+ selected = { key, scheduling };
443
+ }
444
+ // FIFO inside one priority class is sufficient: later members cannot
445
+ // have waited longer than the first non-excluded member.
446
+ break;
408
447
  }
409
448
  }
410
- return null;
449
+ return selected?.key || null;
450
+ }
451
+
452
+ /**
453
+ * Explain the effective scheduling priority for one task.
454
+ *
455
+ * A blocked high-priority descendant donates its priority transitively to
456
+ * the dependency that can unblock it. Ready tasks also age, and once they
457
+ * consume 75% of the declared queue-wait SLO they enter an urgent band that
458
+ * outranks newly arriving work. This makes the SLO enforceable rather than
459
+ * leaving aging as telemetry-only decoration.
460
+ */
461
+ schedulingPriority(key, { now = this._now() } = {}) {
462
+ const task = this.getTask(key);
463
+ if (!task) return null;
464
+ const base = this._priorityScore(task.priority);
465
+ const readySince = this._readySinceByKey.get(key);
466
+ const queueWaitMs = Math.max(
467
+ 0,
468
+ Number(now) -
469
+ Number(
470
+ readySince ??
471
+ task.createdAt ??
472
+ (Number.isFinite(now) ? Number(now) : 0),
473
+ ),
474
+ );
475
+ const inheritance = this._priorityInheritanceFor(key);
476
+ const donatedBase = Math.max(base, inheritance.donatedBase);
477
+ const criticalPathBoost = inheritance.criticalPathBoost;
478
+ const aging = Math.min(1000, Math.floor(queueWaitMs / this.agingWindowMs));
479
+ const sloUrgent = queueWaitMs >= Math.floor(this.queueWaitSloMs * 0.75);
480
+ const sloBoost = sloUrgent ? 10_000 : 0;
481
+ return Object.freeze({
482
+ base,
483
+ donation: Math.max(0, donatedBase - base),
484
+ aging,
485
+ criticalPathBoost,
486
+ sloBoost,
487
+ sloUrgent,
488
+ total: donatedBase + aging + Math.min(1000, criticalPathBoost) + sloBoost,
489
+ queueWaitMs,
490
+ queueWaitSloMs: this.queueWaitSloMs,
491
+ readySince: Number(readySince ?? task.createdAt ?? now),
492
+ queueWaitDeadlineAt:
493
+ Number(readySince ?? task.createdAt ?? now) + this.queueWaitSloMs,
494
+ });
411
495
  }
412
496
 
413
497
  /**
@@ -1465,6 +1549,320 @@ export class TaskLeaseRegistry {
1465
1549
  return { ok: true, priorStatus };
1466
1550
  }
1467
1551
 
1552
+ /**
1553
+ * Repair the compatibility task view from a terminal canonical Graph node.
1554
+ * This exceptional path may correct a fail-closed recovery cancellation when
1555
+ * the immutable Graph ledger proves that the effect/attempt already settled.
1556
+ */
1557
+ applyCanonicalTaskProjection(
1558
+ key,
1559
+ {
1560
+ runId,
1561
+ nodeId,
1562
+ graphStatus,
1563
+ authorityGeneration,
1564
+ eventHead,
1565
+ revisionDigest,
1566
+ evidence = null,
1567
+ now = this._now(),
1568
+ } = {},
1569
+ ) {
1570
+ const terminalStatus =
1571
+ graphStatus === "succeeded"
1572
+ ? TASK_STATUS.COMPLETED
1573
+ : ["failed", "blocked", "cancelled"].includes(graphStatus)
1574
+ ? TASK_STATUS.CANCELLED
1575
+ : null;
1576
+ if (!terminalStatus) {
1577
+ return { ok: false, reason: "graph_not_terminal" };
1578
+ }
1579
+ if (
1580
+ !SAFE_HANDOFF_ID_PATTERN.test(String(runId || "")) ||
1581
+ !SAFE_HANDOFF_ID_PATTERN.test(String(nodeId || "")) ||
1582
+ !Number.isSafeInteger(Number(authorityGeneration)) ||
1583
+ Number(authorityGeneration) < 1 ||
1584
+ !SHA256_PATTERN.test(String(eventHead || "")) ||
1585
+ !SHA256_PATTERN.test(String(revisionDigest || ""))
1586
+ ) {
1587
+ return { ok: false, reason: "invalid_graph_binding" };
1588
+ }
1589
+ let cleanEvidence;
1590
+ try {
1591
+ cleanEvidence = cloneValue(evidence);
1592
+ } catch {
1593
+ return { ok: false, reason: "invalid_graph_evidence" };
1594
+ }
1595
+ const binding = {
1596
+ schema: "chainlesschain.team-graph-task-projection/v1",
1597
+ runId: String(runId),
1598
+ nodeId: String(nodeId),
1599
+ graphStatus,
1600
+ authorityGeneration: Number(authorityGeneration),
1601
+ eventHead: String(eventHead),
1602
+ revisionDigest: String(revisionDigest),
1603
+ evidence: cleanEvidence,
1604
+ };
1605
+ const projectionDigest = digestValue(binding);
1606
+ const id = this._byKey.get(key);
1607
+ const task = id ? this._tasks.get(id) : null;
1608
+ if (!task) return { ok: false, reason: "not_found" };
1609
+ const prior = task.metadata?.canonicalGraphProjection || null;
1610
+ if (prior?.projectionDigest === projectionDigest) {
1611
+ return {
1612
+ ok: true,
1613
+ idempotent: true,
1614
+ status: task.status,
1615
+ projectionDigest,
1616
+ };
1617
+ }
1618
+ if (
1619
+ prior &&
1620
+ (prior.runId !== binding.runId ||
1621
+ prior.nodeId !== binding.nodeId ||
1622
+ prior.revisionDigest !== binding.revisionDigest ||
1623
+ prior.graphStatus !== binding.graphStatus)
1624
+ ) {
1625
+ return { ok: false, reason: "graph_projection_conflict" };
1626
+ }
1627
+ const snapshot = this._tasks.snapshot();
1628
+ const stored = snapshot.tasks.find((candidate) => candidate.id === task.id);
1629
+ if (!stored || stored.rev !== task.rev) {
1630
+ return { ok: false, reason: "concurrent" };
1631
+ }
1632
+ const projectedAt = Number.isFinite(Number(now))
1633
+ ? Number(now)
1634
+ : this._now();
1635
+ const metadata = {
1636
+ ...stored.metadata,
1637
+ lease: null,
1638
+ canonicalGraphProjection: {
1639
+ ...binding,
1640
+ projectionDigest,
1641
+ projectedAt,
1642
+ },
1643
+ };
1644
+ delete metadata.adjudication;
1645
+ if (terminalStatus === TASK_STATUS.COMPLETED) {
1646
+ metadata.result = {
1647
+ canonicalGraph: true,
1648
+ terminalEvidence: cleanEvidence,
1649
+ };
1650
+ delete metadata.lastError;
1651
+ } else {
1652
+ metadata.lastError = `canonical Graph node settled ${graphStatus}`;
1653
+ }
1654
+ stored.status = terminalStatus;
1655
+ stored.assignee = null;
1656
+ stored.updatedAt = projectedAt;
1657
+ stored.rev += 1;
1658
+ stored.metadata = metadata;
1659
+ stored.history = [
1660
+ ...(stored.history || []),
1661
+ {
1662
+ ts: projectedAt,
1663
+ actor: "graph-kernel",
1664
+ action: "canonical-projection-repaired",
1665
+ changes: ["status", "assignee", "metadata"],
1666
+ graphStatus,
1667
+ projectionDigest,
1668
+ },
1669
+ ];
1670
+ this._tasks = SharedTaskList.restore(snapshot, { now: this._now });
1671
+ this._rebuildIndexes();
1672
+ if (terminalStatus === TASK_STATUS.COMPLETED) {
1673
+ for (const dependent of this._dependentsByKey.get(key) || []) {
1674
+ this._enqueueIfReady(dependent, projectedAt);
1675
+ }
1676
+ }
1677
+ return { ok: true, status: terminalStatus, projectionDigest };
1678
+ }
1679
+
1680
+ applyCanonicalHandoffProjection(
1681
+ key,
1682
+ { handoff: source, targetLease = null, now = this._now() } = {},
1683
+ ) {
1684
+ if (!source || typeof source !== "object") {
1685
+ return { ok: false, reason: "invalid_handoff_projection" };
1686
+ }
1687
+ const id = normalizedHandoffId(source.id, () => null);
1688
+ const fromHolder = normalizedHolder(source.fromHolder);
1689
+ const toHolder = normalizedHolder(source.toHolder);
1690
+ const revisionDigest = normalizedDigest(source.revisionDigest);
1691
+ const authorityDigest = normalizedDigest(source.authorityDigest);
1692
+ const artifactIds = normalizedArtifactIds(source.artifactIds);
1693
+ const status = String(source.status || "");
1694
+ const ttlMs = Number(source.ttlMs);
1695
+ if (
1696
+ !id ||
1697
+ !fromHolder ||
1698
+ !toHolder ||
1699
+ fromHolder === toHolder ||
1700
+ !revisionDigest ||
1701
+ !authorityDigest ||
1702
+ !artifactIds ||
1703
+ !TEAM_CUSTODY_HANDOFF_STATUSES.includes(status) ||
1704
+ !Number.isSafeInteger(ttlMs) ||
1705
+ ttlMs <= 0 ||
1706
+ ttlMs > MAX_HANDOFF_TTL_MS ||
1707
+ source.taskKey !== key ||
1708
+ typeof source.fromLeaseId !== "string" ||
1709
+ source.fromLeaseId.length === 0 ||
1710
+ !["string", "number"].includes(typeof source.fromFence)
1711
+ ) {
1712
+ return { ok: false, reason: "invalid_handoff_projection" };
1713
+ }
1714
+ let preconditions;
1715
+ let summary;
1716
+ try {
1717
+ preconditions = cloneValue(source.preconditions);
1718
+ summary = cloneValue(source.summary);
1719
+ } catch {
1720
+ return { ok: false, reason: "invalid_handoff_projection" };
1721
+ }
1722
+ const idempotencyKey = normalizedHandoffId(
1723
+ source.idempotencyKey,
1724
+ () => null,
1725
+ );
1726
+ if (!idempotencyKey) {
1727
+ return { ok: false, reason: "invalid_handoff_projection" };
1728
+ }
1729
+ const binding = {
1730
+ schema: TEAM_CUSTODY_HANDOFF_SCHEMA,
1731
+ id,
1732
+ taskKey: key,
1733
+ fromHolder,
1734
+ fromLeaseId: source.fromLeaseId,
1735
+ fromFence: source.fromFence,
1736
+ toHolder,
1737
+ revisionDigest,
1738
+ authorityDigest,
1739
+ artifactIds,
1740
+ preconditions,
1741
+ summary,
1742
+ idempotencyKey,
1743
+ ttlMs,
1744
+ };
1745
+ const bindingDigest = digestValue(binding);
1746
+ if (source.bindingDigest && source.bindingDigest !== bindingDigest) {
1747
+ return { ok: false, reason: "handoff_binding_conflict" };
1748
+ }
1749
+ let cleanTargetLease = null;
1750
+ if (status === "committed") {
1751
+ try {
1752
+ cleanTargetLease = cloneValue(targetLease || source.targetLease);
1753
+ } catch {
1754
+ return { ok: false, reason: "invalid_target_lease" };
1755
+ }
1756
+ if (
1757
+ !cleanTargetLease ||
1758
+ cleanTargetLease.holder !== toHolder ||
1759
+ typeof cleanTargetLease.leaseId !== "string" ||
1760
+ cleanTargetLease.leaseId.length === 0 ||
1761
+ !Number.isFinite(Number(cleanTargetLease.expiresAt))
1762
+ ) {
1763
+ return { ok: false, reason: "invalid_target_lease" };
1764
+ }
1765
+ }
1766
+ const taskId = this._byKey.get(key);
1767
+ const task = taskId ? this._tasks.get(taskId) : null;
1768
+ if (!task) return { ok: false, reason: "not_found" };
1769
+ const history = handoffHistory(task);
1770
+ const existing = history.find((entry) => entry.id === id);
1771
+ if (existing && existing.bindingDigest !== bindingDigest) {
1772
+ return { ok: false, reason: "handoff_binding_conflict" };
1773
+ }
1774
+ if (existing && existing.status !== status) {
1775
+ const transitions = {
1776
+ offered: new Set([
1777
+ "accepted",
1778
+ "rejected",
1779
+ "committed",
1780
+ "revoked",
1781
+ "expired",
1782
+ ]),
1783
+ accepted: new Set(["committed", "revoked", "expired"]),
1784
+ };
1785
+ if (!transitions[existing.status]?.has(status)) {
1786
+ return { ok: false, reason: "handoff_projection_regressed" };
1787
+ }
1788
+ }
1789
+ if (existing?.status === status) {
1790
+ return { ok: true, idempotent: true, handoff: cloneValue(existing) };
1791
+ }
1792
+ const projectedAt = Number.isFinite(Number(now))
1793
+ ? Number(now)
1794
+ : this._now();
1795
+ const projected = {
1796
+ ...(existing || binding),
1797
+ ...binding,
1798
+ bindingDigest,
1799
+ expiresAtMs: Number(source.expiresAtMs),
1800
+ status,
1801
+ offeredAt: Number(source.offeredAt),
1802
+ acceptedAt: source.acceptedAt == null ? null : Number(source.acceptedAt),
1803
+ rejectedAt: source.rejectedAt == null ? null : Number(source.rejectedAt),
1804
+ committedAt:
1805
+ source.committedAt == null ? null : Number(source.committedAt),
1806
+ revokedAt: source.revokedAt == null ? null : Number(source.revokedAt),
1807
+ expiredAt: source.expiredAt == null ? null : Number(source.expiredAt),
1808
+ targetStartedAt: existing?.targetStartedAt ?? null,
1809
+ targetSettledAt: existing?.targetSettledAt ?? null,
1810
+ targetSettlement: existing?.targetSettlement ?? null,
1811
+ reason:
1812
+ source.reason == null ? null : String(source.reason).slice(0, 1024),
1813
+ acceptedByAttempt: existing?.acceptedByAttempt ?? null,
1814
+ targetLease:
1815
+ status === "committed"
1816
+ ? cloneValue(existing?.targetLease || cleanTargetLease)
1817
+ : null,
1818
+ canonicalProjectedAt: projectedAt,
1819
+ updatedAt: Number(source.updatedAt) || projectedAt,
1820
+ };
1821
+ if (existing) {
1822
+ history[history.indexOf(existing)] = projected;
1823
+ } else {
1824
+ history.push(projected);
1825
+ while (history.length > MAX_HANDOFF_HISTORY) history.shift();
1826
+ }
1827
+ const snapshot = this._tasks.snapshot();
1828
+ const stored = snapshot.tasks.find((candidate) => candidate.id === task.id);
1829
+ if (!stored || stored.rev !== task.rev) {
1830
+ return { ok: false, reason: "concurrent" };
1831
+ }
1832
+ stored.rev += 1;
1833
+ stored.updatedAt = projectedAt;
1834
+ stored.metadata = { ...stored.metadata, custodyHandoffs: history };
1835
+ if (
1836
+ status === "committed" &&
1837
+ ![TASK_STATUS.COMPLETED, TASK_STATUS.CANCELLED].includes(stored.status)
1838
+ ) {
1839
+ stored.status = TASK_STATUS.IN_PROGRESS;
1840
+ stored.assignee = toHolder;
1841
+ stored.metadata.lease = cloneValue(projected.targetLease);
1842
+ } else if (
1843
+ status === "expired" &&
1844
+ stored.metadata?.lease?.leaseId === binding.fromLeaseId
1845
+ ) {
1846
+ stored.status = TASK_STATUS.PENDING;
1847
+ stored.assignee = null;
1848
+ stored.metadata.lease = null;
1849
+ }
1850
+ stored.history = [
1851
+ ...(stored.history || []),
1852
+ {
1853
+ ts: projectedAt,
1854
+ actor: "graph-kernel",
1855
+ action: "canonical-handoff-projected",
1856
+ changes: ["status", "assignee", "metadata"],
1857
+ handoffId: id,
1858
+ handoffStatus: status,
1859
+ },
1860
+ ];
1861
+ this._tasks = SharedTaskList.restore(snapshot, { now: this._now });
1862
+ this._rebuildIndexes();
1863
+ return { ok: true, handoff: cloneValue(projected) };
1864
+ }
1865
+
1468
1866
  /** Bind a pending adjudication to its immutable durable case evidence. */
1469
1867
  bindAdjudicationCase(key, { caseId, registryDigest, sideEffectDigest } = {}) {
1470
1868
  const id = this._byKey.get(key);
@@ -1556,6 +1954,9 @@ export class TaskLeaseRegistry {
1556
1954
  registry: {
1557
1955
  defaultTtlMs: this.defaultTtlMs,
1558
1956
  maxAttempts: this.maxAttempts,
1957
+ queueWaitSloMs: this.queueWaitSloMs,
1958
+ agingWindowMs: this.agingWindowMs,
1959
+ readySinceByKey: Array.from(this._readySinceByKey.entries()),
1559
1960
  byKey: Array.from(this._byKey.entries()),
1560
1961
  },
1561
1962
  tasks: this._tasks.snapshot(),
@@ -1567,9 +1968,12 @@ export class TaskLeaseRegistry {
1567
1968
  now,
1568
1969
  defaultTtlMs: snapshot?.registry?.defaultTtlMs,
1569
1970
  maxAttempts: snapshot?.registry?.maxAttempts,
1971
+ queueWaitSloMs: snapshot?.registry?.queueWaitSloMs,
1972
+ agingWindowMs: snapshot?.registry?.agingWindowMs,
1570
1973
  });
1571
1974
  reg._tasks = SharedTaskList.restore(snapshot.tasks, { now: reg._now });
1572
1975
  reg._byKey = new Map(snapshot?.registry?.byKey || []);
1976
+ reg._readySinceByKey = new Map(snapshot?.registry?.readySinceByKey || []);
1573
1977
  reg._rebuildIndexes();
1574
1978
  return reg;
1575
1979
  }
@@ -1584,6 +1988,7 @@ export class TaskLeaseRegistry {
1584
1988
  patch,
1585
1989
  actor: patch.assignee,
1586
1990
  });
1991
+ if (patch.status !== undefined) this._priorityInheritanceCache = null;
1587
1992
  return true;
1588
1993
  } catch {
1589
1994
  // ConcurrencyError (rev changed under us) — caller re-reads and retries.
@@ -1655,8 +2060,72 @@ export class TaskLeaseRegistry {
1655
2060
  : "normal";
1656
2061
  }
1657
2062
 
1658
- _removeReady(key) {
2063
+ _priorityScore(priority) {
2064
+ return priority === "high" ? 2 : priority === "low" ? 0 : 1;
2065
+ }
2066
+
2067
+ _priorityInheritanceFor(key) {
2068
+ if (!this._priorityInheritanceCache) {
2069
+ const keys = [...this._byKey.keys()];
2070
+ const indegree = new Map(
2071
+ keys.map((candidate) => [
2072
+ candidate,
2073
+ (this._depsByKey.get(candidate) || []).filter((dependency) =>
2074
+ this._byKey.has(dependency),
2075
+ ).length,
2076
+ ]),
2077
+ );
2078
+ const ready = keys.filter((candidate) => indegree.get(candidate) === 0);
2079
+ const order = [];
2080
+ for (let index = 0; index < ready.length; index += 1) {
2081
+ const candidate = ready[index];
2082
+ order.push(candidate);
2083
+ for (const dependent of this._dependentsByKey.get(candidate) || []) {
2084
+ if (!indegree.has(dependent)) continue;
2085
+ indegree.set(dependent, indegree.get(dependent) - 1);
2086
+ if (indegree.get(dependent) === 0) ready.push(dependent);
2087
+ }
2088
+ }
2089
+ const inherited = new Map();
2090
+ for (const candidate of order.reverse()) {
2091
+ const task = this.getTask(candidate);
2092
+ let donatedBase = this._priorityScore(task?.priority);
2093
+ let criticalPathBoost = 0;
2094
+ for (const dependent of this._dependentsByKey.get(candidate) || []) {
2095
+ const dependentTask = this.getTask(dependent);
2096
+ if (
2097
+ !dependentTask ||
2098
+ [TASK_STATUS.COMPLETED, TASK_STATUS.CANCELLED].includes(
2099
+ dependentTask.status,
2100
+ )
2101
+ ) {
2102
+ continue;
2103
+ }
2104
+ const child = inherited.get(dependent) || {
2105
+ donatedBase: this._priorityScore(dependentTask.priority),
2106
+ criticalPathBoost: 0,
2107
+ };
2108
+ donatedBase = Math.max(donatedBase, child.donatedBase);
2109
+ criticalPathBoost = Math.max(
2110
+ criticalPathBoost,
2111
+ child.criticalPathBoost + 1,
2112
+ );
2113
+ }
2114
+ inherited.set(candidate, { donatedBase, criticalPathBoost });
2115
+ }
2116
+ this._priorityInheritanceCache = inherited;
2117
+ }
2118
+ return (
2119
+ this._priorityInheritanceCache.get(key) || {
2120
+ donatedBase: this._priorityScore(this.getTask(key)?.priority),
2121
+ criticalPathBoost: 0,
2122
+ }
2123
+ );
2124
+ }
2125
+
2126
+ _removeReady(key, { clearWait = true } = {}) {
1659
2127
  for (const queue of this._readyByPriority.values()) queue.delete(key);
2128
+ if (clearWait) this._readySinceByKey.delete(key);
1660
2129
  }
1661
2130
 
1662
2131
  _dependenciesCompleted(key) {
@@ -1697,6 +2166,9 @@ export class TaskLeaseRegistry {
1697
2166
  return false;
1698
2167
  }
1699
2168
  this._leasedKeys.delete(key);
2169
+ if (!this._readySinceByKey.has(key)) {
2170
+ this._readySinceByKey.set(key, Number(now));
2171
+ }
1700
2172
  this._readyByPriority.get(this._priorityFor(task)).add(key);
1701
2173
  return true;
1702
2174
  }
@@ -1712,6 +2184,7 @@ export class TaskLeaseRegistry {
1712
2184
  }
1713
2185
 
1714
2186
  _rebuildIndexes() {
2187
+ const restoredReadySince = new Map(this._readySinceByKey || []);
1715
2188
  this._depsByKey = new Map();
1716
2189
  this._dependentsByKey = new Map();
1717
2190
  this._readyByPriority = new Map([
@@ -1720,6 +2193,8 @@ export class TaskLeaseRegistry {
1720
2193
  ["low", new Set()],
1721
2194
  ]);
1722
2195
  this._leasedKeys = new Set();
2196
+ this._readySinceByKey = restoredReadySince;
2197
+ this._priorityInheritanceCache = null;
1723
2198
  for (const task of this._tasks.list()) {
1724
2199
  const key = task.metadata?.key;
1725
2200
  const dependencies = task.metadata?.dependsOn || [];
@@ -1738,5 +2213,14 @@ export class TaskLeaseRegistry {
1738
2213
  }
1739
2214
  const now = this._now();
1740
2215
  for (const key of this._byKey.keys()) this._enqueueIfReady(key, now);
2216
+ for (const priority of ["high", "normal", "low"]) {
2217
+ const ordered = [...this._readyByPriority.get(priority)].sort(
2218
+ (left, right) =>
2219
+ (this._readySinceByKey.get(left) || 0) -
2220
+ (this._readySinceByKey.get(right) || 0) ||
2221
+ left.localeCompare(right),
2222
+ );
2223
+ this._readyByPriority.set(priority, new Set(ordered));
2224
+ }
1741
2225
  }
1742
2226
  }
@@ -7,6 +7,7 @@
7
7
  * are never copied into parser state or returned to callers.
8
8
  */
9
9
 
10
+ import { createHash } from "node:crypto";
10
11
  import { TextDecoder } from "node:util";
11
12
 
12
13
  export const DEFAULT_TEAM_AGENT_MAX_LINE_BYTES = 1024 * 1024;
@@ -96,6 +97,7 @@ export class TeamAgentStreamParser {
96
97
  #tokenUsageEvents = 0;
97
98
  #usageByIdentity = new Map();
98
99
  #sawTerminalResult = false;
100
+ #terminalResultDigest = null;
99
101
  #finished = false;
100
102
  #failed = null;
101
103
  #finalSummary = null;
@@ -204,6 +206,16 @@ export class TeamAgentStreamParser {
204
206
  return this.#copyLiveSummary();
205
207
  }
206
208
 
209
+ /**
210
+ * Return an immutable digest of the exact terminal result record without
211
+ * retaining or exposing its prompt/result body.
212
+ */
213
+ terminalEvidence() {
214
+ return this.#terminalResultDigest
215
+ ? Object.freeze({ outputDigest: this.#terminalResultDigest })
216
+ : null;
217
+ }
218
+
207
219
  #copyLiveSummary() {
208
220
  return {
209
221
  provider: this.#provider,
@@ -291,6 +303,9 @@ export class TeamAgentStreamParser {
291
303
  }
292
304
  if (line.trim() === "") return;
293
305
 
306
+ const lineDigest = `sha256:${createHash("sha256")
307
+ .update(line, "utf8")
308
+ .digest("hex")}`;
294
309
  let event;
295
310
  try {
296
311
  event = JSON.parse(line);
@@ -345,6 +360,7 @@ export class TeamAgentStreamParser {
345
360
 
346
361
  if (event.type === "result") {
347
362
  this.#sawTerminalResult = true;
363
+ this.#terminalResultDigest = lineDigest;
348
364
  this.#lastResultProvider =
349
365
  typeof event.provider === "string" ? event.provider : this.#provider;
350
366
  this.#lastResultModel =