chainlesschain 0.162.188 → 0.162.193

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 (337) hide show
  1. package/README.md +61 -11
  2. package/bin/chainlesschain.js +7 -52
  3. package/completions/_cc +209 -0
  4. package/completions/cc.bash +18 -0
  5. package/completions/cc.fish +13 -0
  6. package/completions/cc.ps1 +226 -0
  7. package/package.json +10 -2
  8. package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
  12. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
  18. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
  26. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
  44. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
  48. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
  49. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
  52. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
  53. package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
  54. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
  57. package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
  58. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
  60. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
  69. package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
  70. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
  73. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
  75. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
  93. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
  133. package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
  134. package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
  135. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
  155. package/src/assets/web-panel/index.html +1 -1
  156. package/src/cli-help.js +181 -0
  157. package/src/command-help-index.json +181 -0
  158. package/src/command-manifest.json +1107 -175
  159. package/src/command-surface-policy.js +197 -0
  160. package/src/commands/agent.js +98 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-managed.js +1059 -0
  164. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  165. package/src/commands/checkpoint.js +950 -22
  166. package/src/commands/compact.js +101 -25
  167. package/src/commands/config.js +506 -20
  168. package/src/commands/doctor.js +4 -1
  169. package/src/commands/hub.js +1 -1
  170. package/src/commands/mcp.js +688 -24
  171. package/src/commands/pack.js +16 -2
  172. package/src/commands/plugin.js +20 -12
  173. package/src/commands/session-mcp-recovery.js +218 -0
  174. package/src/commands/session.js +470 -110
  175. package/src/commands/setup.js +36 -10
  176. package/src/commands/skill.js +181 -110
  177. package/src/commands/status.js +21 -5
  178. package/src/commands/team-distributed.js +3324 -0
  179. package/src/commands/team.js +1130 -74
  180. package/src/commands/update.js +68 -6
  181. package/src/constants.js +11 -0
  182. package/src/data/changelog.json +26 -0
  183. package/src/gateways/ws/action-protocol.js +10 -0
  184. package/src/gateways/ws/session-protocol.js +555 -27
  185. package/src/gateways/ws/ws-agent-handler.js +541 -14
  186. package/src/gateways/ws/ws-session-gateway.js +129 -22
  187. package/src/gateways/ws/ws-session-state.js +431 -0
  188. package/src/harness/background-task-manager.js +233 -36
  189. package/src/harness/jsonl-session-store.js +2551 -167
  190. package/src/harness/mcp-client.js +802 -61
  191. package/src/harness/prompt-compressor.js +227 -27
  192. package/src/harness/session-index.js +89 -38
  193. package/src/harness/session-list-index.js +254 -0
  194. package/src/harness/structured-handoff.js +410 -0
  195. package/src/lazy-dispatch.js +747 -84
  196. package/src/lib/__tests__/logger.test.js +50 -2
  197. package/src/lib/advisor-runtime.js +1047 -0
  198. package/src/lib/agent-core.js +2 -0
  199. package/src/lib/agent-sandbox.js +120 -26
  200. package/src/lib/agent-session-export.js +20 -0
  201. package/src/lib/agent-team/task-lease.js +271 -3
  202. package/src/lib/agent-team/team-adjudication.js +1501 -0
  203. package/src/lib/agent-team/team-budget.js +4 -1
  204. package/src/lib/agent-team/team-control-store.js +582 -0
  205. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  206. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  207. package/src/lib/agent-team/team-runner.js +543 -24
  208. package/src/lib/agent-team/team-worktree.js +1403 -95
  209. package/src/lib/artifact-store.js +131 -8
  210. package/src/lib/background-agent-supervisor.js +185 -13
  211. package/src/lib/background-interaction-journal.js +37 -9
  212. package/src/lib/background-launch-profile.js +1058 -0
  213. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  214. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  215. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  216. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  217. package/src/lib/checkpoint-restore-recovery.js +884 -0
  218. package/src/lib/checkpoint-restore-retention.js +126 -0
  219. package/src/lib/checkpoint-restore-saga.js +5079 -0
  220. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  221. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  222. package/src/lib/checkpoint-store.js +1688 -97
  223. package/src/lib/checkpoint-timeline-authority.js +522 -0
  224. package/src/lib/checkpoint-timeline.js +642 -0
  225. package/src/lib/config-keys.js +18 -119
  226. package/src/lib/config-manager.js +446 -27
  227. package/src/lib/config-redaction.js +57 -0
  228. package/src/lib/config-schema.js +853 -0
  229. package/src/lib/cowork-mcp-tools.js +5 -0
  230. package/src/lib/cowork-task-runner.js +458 -63
  231. package/src/lib/delivery-coordinator.js +1234 -0
  232. package/src/lib/delivery-evidence.js +450 -0
  233. package/src/lib/doctor-checkup.js +199 -14
  234. package/src/lib/executable-path.js +34 -0
  235. package/src/lib/fatal-handler.js +16 -0
  236. package/src/lib/file-checkpoint.js +4909 -186
  237. package/src/lib/file-lines.js +125 -0
  238. package/src/lib/headless-config-command.js +16 -5
  239. package/src/lib/hook-runner.cjs +139 -38
  240. package/src/lib/hooks-v2-workspace-context.js +10 -0
  241. package/src/lib/impacted-gate-selector.js +424 -0
  242. package/src/lib/interaction-adapter.js +62 -0
  243. package/src/lib/jsonl-session-store.js +1 -35
  244. package/src/lib/logger.js +66 -14
  245. package/src/lib/managed-checkpoint-render.js +60 -0
  246. package/src/lib/managed-tool-checkpoint.js +194 -0
  247. package/src/lib/mcp-call-ledger-store.js +1069 -0
  248. package/src/lib/mcp-call-ledger.js +747 -0
  249. package/src/lib/mcp-client.js +5 -0
  250. package/src/lib/mcp-conflict-scheduler.js +468 -0
  251. package/src/lib/mcp-effect-contract.js +102 -0
  252. package/src/lib/mcp-headers-helper-trust.js +145 -0
  253. package/src/lib/mcp-headers-helper.js +616 -0
  254. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  255. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  256. package/src/lib/mcp-recovery-adjudication.js +443 -0
  257. package/src/lib/output-context.js +86 -0
  258. package/src/lib/packer/file-identity.js +170 -0
  259. package/src/lib/packer/index.js +3 -0
  260. package/src/lib/packer/native-update-state.js +277 -0
  261. package/src/lib/packer/pack-update-applier.js +1983 -136
  262. package/src/lib/packer/pack-update-checker.js +34 -0
  263. package/src/lib/packer/pack-update-downloader.js +875 -86
  264. package/src/lib/packer/pack-update-signature.js +86 -0
  265. package/src/lib/packer/pkg-config-generator.js +6 -0
  266. package/src/lib/paths.js +65 -10
  267. package/src/lib/plan-mode.js +831 -14
  268. package/src/lib/plan-persistence.js +540 -0
  269. package/src/lib/plugin-runtime/hooks.js +227 -69
  270. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  271. package/src/lib/pr-automation-policy.js +146 -8
  272. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  273. package/src/lib/process-execution-broker/index.js +491 -13
  274. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  275. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  276. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  277. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  278. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  279. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  280. package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
  281. package/src/lib/project-instructions.js +165 -29
  282. package/src/lib/project-mcp-trust.js +67 -0
  283. package/src/lib/public-ide-capability-manifest.js +272 -0
  284. package/src/lib/public-ide-capability-validation.js +351 -0
  285. package/src/lib/secret-store.js +267 -43
  286. package/src/lib/secure-file-identity.js +433 -0
  287. package/src/lib/secure-fs.js +1089 -0
  288. package/src/lib/security-audit.js +58 -0
  289. package/src/lib/service-manager.js +34 -13
  290. package/src/lib/session-budget-runtime.js +1452 -0
  291. package/src/lib/session-host-snapshot.js +388 -0
  292. package/src/lib/session-message-provenance.js +429 -0
  293. package/src/lib/session-projection.js +804 -0
  294. package/src/lib/session-resource-budget.js +1495 -0
  295. package/src/lib/settings-hooks.cjs +138 -22
  296. package/src/lib/settings-loader.cjs +25 -2
  297. package/src/lib/side-effect-ledger-store.js +45 -23
  298. package/src/lib/skill-execution-identity.js +217 -0
  299. package/src/lib/skill-loader.js +299 -18
  300. package/src/lib/sub-agent-context.js +236 -28
  301. package/src/lib/sub-agent-registry.js +5 -0
  302. package/src/lib/subagent-contract.js +94 -0
  303. package/src/lib/todo-manager.js +185 -18
  304. package/src/lib/todo-persistence.js +417 -0
  305. package/src/lib/turn-binding-store.js +41 -23
  306. package/src/lib/turn-binding.js +160 -2
  307. package/src/lib/with-file-lock.js +165 -24
  308. package/src/lib/ws-chat-handler.js +21 -0
  309. package/src/program-base.js +18 -0
  310. package/src/repl/advisor-command.js +103 -0
  311. package/src/repl/agent-repl.js +2175 -146
  312. package/src/repl/btw-command.js +93 -10
  313. package/src/repl/clipboard-image.js +119 -0
  314. package/src/repl/config-summary.js +64 -108
  315. package/src/repl/prompt-editor.js +233 -0
  316. package/src/repl/prompt-interactions.js +344 -0
  317. package/src/repl/prompt-stash.js +354 -0
  318. package/src/repl/prompt-suggestions.js +312 -0
  319. package/src/repl/repl-keybindings.js +227 -0
  320. package/src/repl/session-recap.js +217 -0
  321. package/src/repl/slash-command-registry.js +22 -0
  322. package/src/repl/terminal-layout.js +88 -0
  323. package/src/runtime/agent-core.js +1630 -447
  324. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  325. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  326. package/src/runtime/diagnostics.js +32 -13
  327. package/src/runtime/headless-runner.js +310 -118
  328. package/src/runtime/headless-stream.js +356 -95
  329. package/src/runtime/mcp-config.js +148 -14
  330. package/src/runtime/policies/agent-policy.js +14 -0
  331. package/src/runtime/status-diagnostics-lite.js +137 -0
  332. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  333. package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
  334. package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
  335. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  336. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  337. package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
@@ -16,6 +16,10 @@
16
16
 
17
17
  import { emitHooksV2Event } from "../hooks-v2-producers.js";
18
18
 
19
+ function leaseFencingToken(lease) {
20
+ return lease?.fencingToken ?? lease?.leaseId ?? null;
21
+ }
22
+
19
23
  export class TeamRunner {
20
24
  /**
21
25
  * @param {TaskLeaseRegistry} registry
@@ -42,6 +46,10 @@ export class TeamRunner {
42
46
  // Team-wide token/USD/time/task budget (null → unbounded). Consulted before
43
47
  // every claim; folded after every settled task.
44
48
  this.budget = opts.budget || null;
49
+ // Optional process/session-wide authority shared with non-team sub-agents,
50
+ // tools and background work. TeamBudget remains the team-specific pricing
51
+ // and reservation facade; this budget owns global concurrency + cascade.
52
+ this.sessionBudget = opts.sessionBudget || null;
45
53
  this.budgetForTask =
46
54
  typeof opts.budgetForTask === "function" ? opts.budgetForTask : null;
47
55
  // Directed/broadcast messaging between teammates (null → disabled).
@@ -68,6 +76,11 @@ export class TeamRunner {
68
76
  typeof opts.beforeTask === "function" ? opts.beforeTask : null;
69
77
  this.afterTask =
70
78
  typeof opts.afterTask === "function" ? opts.afterTask : null;
79
+ // A lease that expires and is reacquired receives a new fencing identity.
80
+ // Persist that identity synchronously before the executor can settle under
81
+ // it. Ordinary renewals keep the same identity and do not call this hook.
82
+ this.onLeaseChanged =
83
+ typeof opts.onLeaseChanged === "function" ? opts.onLeaseChanged : null;
71
84
  this._executions = 0;
72
85
  this._reservedExecutions = 0;
73
86
  this._inFlight = 0;
@@ -78,6 +91,7 @@ export class TeamRunner {
78
91
  this._activeKeys = new Set();
79
92
  this._members = new Map(); // holder → lifecycle record
80
93
  this._budgetStopped = false;
94
+ this._registryBudgetReason = null;
81
95
  this._fatalError = null;
82
96
  // Idle workers wait on graph progress instead of polling the full graph
83
97
  // through setTimeout(0). This matters once a team has many workers but only
@@ -85,6 +99,7 @@ export class TeamRunner {
85
99
  this._progressWaiters = new Set();
86
100
  this._workerCount = 1;
87
101
  this._claimControllers = new Set();
102
+ this._claimsByKey = new Map();
88
103
  this._wallTimer = null;
89
104
  }
90
105
 
@@ -152,6 +167,121 @@ export class TeamRunner {
152
167
  }
153
168
  }
154
169
 
170
+ /** Snapshot of local in-flight claims for an IDE/operator control surface. */
171
+ activeClaims() {
172
+ return Array.from(this._claimsByKey.values()).map((claim) => ({
173
+ key: claim.key,
174
+ holder: claim.holder,
175
+ leaseId: claim.leaseId,
176
+ fencingToken: claim.fencingToken,
177
+ interrupted: !!claim.interruption,
178
+ interruption: claim.interruption ? { ...claim.interruption } : null,
179
+ }));
180
+ }
181
+
182
+ /**
183
+ * Request human takeover of an executing task.
184
+ *
185
+ * Aborting the executor cannot prove whether an external side effect landed,
186
+ * so the task always settles fail-closed into adjudication rather than being
187
+ * silently retried. A durable control log supplies requestId/evidenceDigest
188
+ * in production and must bind the exact active holder/lease/fencing identity;
189
+ * keeping this method synchronous makes it safe to call from a file-control
190
+ * watcher or an embedded IDE host.
191
+ */
192
+ interruptTask(
193
+ key,
194
+ {
195
+ holder = null,
196
+ leaseId = null,
197
+ fencingToken = null,
198
+ reason = "human takeover requested",
199
+ actor = "human",
200
+ requestId = null,
201
+ evidenceDigest = null,
202
+ } = {},
203
+ ) {
204
+ const claim = this._claimsByKey.get(key);
205
+ if (!claim) return { ok: false, reason: "not_active" };
206
+ if (
207
+ holder !== claim.holder ||
208
+ leaseId !== claim.leaseId ||
209
+ fencingToken !== claim.fencingToken
210
+ ) {
211
+ return { ok: false, reason: "stale_attempt" };
212
+ }
213
+ if (claim.interruption) {
214
+ if (
215
+ requestId &&
216
+ claim.interruption.requestId &&
217
+ requestId !== claim.interruption.requestId
218
+ ) {
219
+ return { ok: false, reason: "already_interrupted" };
220
+ }
221
+ return {
222
+ ok: true,
223
+ idempotent: true,
224
+ interruption: { ...claim.interruption },
225
+ };
226
+ }
227
+ claim.interruption = {
228
+ holder: claim.holder,
229
+ leaseId: claim.leaseId,
230
+ fencingToken: claim.fencingToken,
231
+ requestId: requestId || null,
232
+ actor: actor ? String(actor) : "human",
233
+ reason: String(reason || "human takeover requested"),
234
+ evidenceDigest: evidenceDigest || null,
235
+ requestedAt: this._now(),
236
+ };
237
+ const error = this._interruptionError(claim);
238
+ try {
239
+ claim.abortController.abort(error);
240
+ } catch {
241
+ /* settlement remains fenced by the task lease */
242
+ }
243
+ this._emit("task:interrupt-requested", {
244
+ key,
245
+ holder: claim.holder,
246
+ leaseId: claim.leaseId,
247
+ fencingToken: claim.fencingToken,
248
+ requestId: claim.interruption.requestId,
249
+ actor: claim.interruption.actor,
250
+ reason: claim.interruption.reason,
251
+ });
252
+ this._signalProgress();
253
+ return { ok: true, interruption: { ...claim.interruption } };
254
+ }
255
+
256
+ /**
257
+ * Fail the coordinator closed (for example when its durable control log is
258
+ * corrupt or rolled back). Active executors are aborted and settle as
259
+ * adjudication-required because their external outcome may be unknown.
260
+ */
261
+ abortRun(error, { requireAdjudication = true } = {}) {
262
+ const failure =
263
+ error instanceof Error ? error : new Error(String(error || "team abort"));
264
+ if (requireAdjudication) {
265
+ failure.retryable = false;
266
+ failure.adjudication = {
267
+ code: failure.code || "TEAM_RUN_ABORTED_ADJUDICATION_REQUIRED",
268
+ reason: failure.message,
269
+ evidenceDigest: null,
270
+ requestedAt: this._now(),
271
+ };
272
+ }
273
+ this._setFatal(failure, { phase: "external-control" });
274
+ for (const claim of this._claimsByKey.values()) {
275
+ claim.coordinatorAbort = failure;
276
+ try {
277
+ claim.abortController.abort(failure);
278
+ } catch {
279
+ /* settlement remains fenced by the task lease */
280
+ }
281
+ }
282
+ return { ok: true, activeClaims: this._claimsByKey.size };
283
+ }
284
+
155
285
  /**
156
286
  * Run the graph to completion (all tasks terminal) or until no teammate can
157
287
  * make progress (everything remaining is blocked/leased). Returns a summary.
@@ -185,7 +315,9 @@ export class TeamRunner {
185
315
  }
186
316
  for (const controller of this._claimControllers) {
187
317
  try {
188
- controller.abort(new Error("Team wall-clock budget exhausted"));
318
+ const error = new Error("Team wall-clock budget exhausted");
319
+ error.code = "TEAM_WALL_BUDGET_EXHAUSTED";
320
+ controller.abort(error);
189
321
  } catch {
190
322
  /* every settlement remains fenced by its lease id */
191
323
  }
@@ -210,6 +342,7 @@ export class TeamRunner {
210
342
  armWallTimer();
211
343
  }
212
344
  }
345
+ this.sessionBudget?.start?.();
213
346
  const workers = [];
214
347
  const workerCount = Math.min(
215
348
  this.teammates,
@@ -244,7 +377,11 @@ export class TeamRunner {
244
377
  requestedTeammates: this.teammates,
245
378
  activeTeammates: workerCount,
246
379
  budgetStopped: this._budgetStopped,
247
- budgetReason: this.budget ? this.budget.reason() : null,
380
+ budgetReason:
381
+ this.budget?.reason?.() ||
382
+ this.sessionBudget?.reason?.() ||
383
+ this._registryBudgetReason,
384
+ sessionBudgetStatus: this.sessionBudget?.status?.() || null,
248
385
  members: this.members(),
249
386
  messages: this.mailbox ? this.mailbox.size() : 0,
250
387
  mailboxStatus: this.mailbox?.status?.() || null,
@@ -265,6 +402,15 @@ export class TeamRunner {
265
402
  this._setState(holder, "shutdown", { reason: "fatal-error" });
266
403
  return;
267
404
  }
405
+ if (this.sessionBudget?.signal?.aborted) {
406
+ const reason = this.sessionBudget.reason?.() || "session-aborted";
407
+ if (!this._budgetStopped) {
408
+ this._budgetStopped = true;
409
+ this._emit("run:budget-exhausted", { reason });
410
+ }
411
+ this._setState(holder, "shutdown", { reason });
412
+ return;
413
+ }
268
414
  if (this._executions + this._reservedExecutions >= this.maxTasks) {
269
415
  this._emit("run:budget-exhausted", { maxTasks: this.maxTasks });
270
416
  this._setState(holder, "shutdown", { reason: "max-tasks" });
@@ -319,16 +465,26 @@ export class TeamRunner {
319
465
  }
320
466
  const acq = this.registry.acquire(key, { holder, ttlMs: this.ttlMs });
321
467
  if (!acq.ok) {
468
+ if (
469
+ ["max-tasks", "max-tokens", "max-usd", "max-wall-ms"].includes(
470
+ acq.reason,
471
+ )
472
+ ) {
473
+ this._budgetStopped = true;
474
+ this._registryBudgetReason = acq.reason;
475
+ this._emit("run:budget-exhausted", { reason: acq.reason });
476
+ this._setState(holder, "shutdown", { reason: acq.reason });
477
+ this._signalProgress();
478
+ return;
479
+ }
322
480
  // Lost the race to a peer (or it just got blocked) — try another.
323
481
  this._setState(holder, "idle");
324
482
  await this._tick();
325
483
  continue;
326
484
  }
327
- const budgetReservation = this._reserveTaskBudget(
328
- key,
329
- this.registry.getTask(key),
330
- acq.lease,
331
- );
485
+ const budgetReservation = acq.budgetReservation
486
+ ? { ok: true, ...acq.budgetReservation }
487
+ : this._reserveTaskBudget(key, this.registry.getTask(key), acq.lease);
332
488
  if (!budgetReservation.ok) {
333
489
  this.registry.release(key, {
334
490
  holder,
@@ -350,8 +506,74 @@ export class TeamRunner {
350
506
  });
351
507
  return;
352
508
  }
509
+ let sessionWork;
510
+ try {
511
+ sessionWork = this._reserveSessionWork(key, acq.lease);
512
+ } catch (error) {
513
+ this.registry.release(key, {
514
+ holder,
515
+ leaseId: acq.lease.leaseId,
516
+ });
517
+ if (budgetReservation.id) {
518
+ this.budget?.releaseReservation?.(budgetReservation.id);
519
+ }
520
+ this._setFatal(error, {
521
+ phase: "session-budget-acquire",
522
+ key,
523
+ holder,
524
+ });
525
+ this._setState(holder, "shutdown", { reason: "fatal-error" });
526
+ return;
527
+ }
528
+ if (!sessionWork.ok) {
529
+ this.registry.release(key, {
530
+ holder,
531
+ leaseId: acq.lease.leaseId,
532
+ });
533
+ if (budgetReservation.id) {
534
+ this.budget?.releaseReservation?.(budgetReservation.id);
535
+ }
536
+ if (sessionWork.retryable) {
537
+ this._setState(holder, "idle");
538
+ // The occupied global slot may belong to a background task outside
539
+ // this runner, so no TeamRunner progress event is guaranteed.
540
+ await this._tick();
541
+ continue;
542
+ }
543
+ if (!this._budgetStopped) {
544
+ this._budgetStopped = true;
545
+ this._emit("run:budget-exhausted", {
546
+ reason: sessionWork.reason,
547
+ });
548
+ }
549
+ this._setState(holder, "shutdown", {
550
+ reason: sessionWork.reason,
551
+ });
552
+ return;
553
+ }
353
554
  this._reservedExecutions++;
354
- const claim = this._beginClaim(holder, key, acq.lease, budgetReservation);
555
+ let claim;
556
+ try {
557
+ claim = this._beginClaim(
558
+ holder,
559
+ key,
560
+ acq.lease,
561
+ budgetReservation,
562
+ sessionWork,
563
+ );
564
+ } catch (error) {
565
+ this._reservedExecutions = Math.max(0, this._reservedExecutions - 1);
566
+ this.registry.release(key, {
567
+ holder,
568
+ leaseId: acq.lease.leaseId,
569
+ });
570
+ if (budgetReservation.id) {
571
+ this.budget?.releaseReservation?.(budgetReservation.id);
572
+ }
573
+ this._setFatal(error, { phase: "session-budget-bind", key, holder });
574
+ this._setState(holder, "shutdown", { reason: "fatal-error" });
575
+ return;
576
+ }
355
577
  if (!this._acquireScope(holder, key)) {
356
578
  this._abandonClaim(holder, key, claim);
357
579
  if (this._fatalError) {
@@ -378,6 +600,33 @@ export class TeamRunner {
378
600
  this._setState(holder, "shutdown", { reason: "fatal-error" });
379
601
  return;
380
602
  }
603
+ // `beforeTask` may persist a durable claim slowly enough to cross the
604
+ // wall-clock deadline. The timer's AbortSignal is advisory: this is the
605
+ // last authoritative fence before handing control to the executor.
606
+ // Preserve an explicit human interruption so `_execute` can settle it
607
+ // fail-closed for adjudication instead of silently abandoning it.
608
+ const preparedBudgetReason = this.budget?.reason?.();
609
+ const preparedSessionReason = this.sessionBudget?.signal?.aborted
610
+ ? this.sessionBudget.reason?.() || "session-aborted"
611
+ : null;
612
+ if (
613
+ !claim.interruption &&
614
+ !claim.coordinatorAbort &&
615
+ (preparedBudgetReason === "max-wall-ms" || preparedSessionReason)
616
+ ) {
617
+ const reason = preparedSessionReason || preparedBudgetReason;
618
+ if (!this._budgetStopped) {
619
+ this._budgetStopped = true;
620
+ this._emit("run:budget-exhausted", {
621
+ reason,
622
+ });
623
+ }
624
+ this._abandonClaim(holder, key, claim);
625
+ this._setState(holder, "shutdown", {
626
+ reason,
627
+ });
628
+ return;
629
+ }
381
630
  await this._execute(holder, key, claim);
382
631
  }
383
632
  }
@@ -439,23 +688,70 @@ export class TeamRunner {
439
688
  };
440
689
  }
441
690
 
442
- _beginClaim(holder, key, lease, budgetReservation = null) {
691
+ _reserveSessionWork(key, lease) {
692
+ if (!this.sessionBudget?.acquireWork) return { ok: true, release: null };
693
+ return this.sessionBudget.acquireWork({
694
+ id: `team-task:${lease?.leaseId || key}`,
695
+ kind: "team-task",
696
+ depth: 1,
697
+ });
698
+ }
699
+
700
+ _beginClaim(
701
+ holder,
702
+ key,
703
+ lease,
704
+ budgetReservation = null,
705
+ sessionWork = null,
706
+ ) {
443
707
  const claim = {
444
708
  holder,
445
709
  key,
446
710
  lease,
447
711
  leaseId: lease?.leaseId,
712
+ fencingToken: leaseFencingToken(lease),
448
713
  heartbeat: null,
449
714
  lost: false,
450
715
  abortController: new AbortController(),
451
716
  budgetReservation,
452
717
  budgetSettled: false,
718
+ sessionWork,
719
+ sessionBudgetSettled: false,
720
+ sessionBudgetUsageHandledByExecutor: false,
721
+ sessionBudgetView: null,
722
+ unregisterSessionAbortable: null,
723
+ interruption: null,
724
+ coordinatorAbort: null,
453
725
  };
454
726
  // Fence the task immediately after acquire, before any async durable hook.
455
727
  // Otherwise a slow beforeTask can outlive the TTL and a peer can run the
456
728
  // same side effect concurrently.
457
729
  this._activeKeys.add(key);
458
730
  this._claimControllers.add(claim.abortController);
731
+ this._claimsByKey.set(key, claim);
732
+ if (this.sessionBudget?.registerAbortable && sessionWork?.id) {
733
+ try {
734
+ claim.unregisterSessionAbortable = this.sessionBudget.registerAbortable(
735
+ `team-claim:${sessionWork.id}`,
736
+ (reason) => {
737
+ if (!claim.abortController.signal.aborted) {
738
+ claim.abortController.abort(reason);
739
+ }
740
+ },
741
+ );
742
+ } catch (error) {
743
+ this._activeKeys.delete(key);
744
+ this._claimControllers.delete(claim.abortController);
745
+ this._claimsByKey.delete(key);
746
+ try {
747
+ sessionWork.release?.();
748
+ } catch {
749
+ // Preserve the registration failure as the authoritative cause. A
750
+ // conforming SessionResourceBudget release is idempotent/nonthrowing.
751
+ }
752
+ throw error;
753
+ }
754
+ }
459
755
  const effectiveTtl =
460
756
  this.ttlMs > 0 ? this.ttlMs : this.registry.defaultTtlMs || 60000;
461
757
  const every = Math.max(
@@ -470,6 +766,71 @@ export class TeamRunner {
470
766
  return claim;
471
767
  }
472
768
 
769
+ _applyRenewedLease(claim, lease) {
770
+ const previousLease = claim.lease;
771
+ const previousLeaseId = claim.leaseId;
772
+ const previousFencingToken = claim.fencingToken;
773
+ claim.lease = lease;
774
+ claim.leaseId = lease?.leaseId;
775
+ claim.fencingToken = leaseFencingToken(lease);
776
+ const identityChanged =
777
+ claim.leaseId !== previousLeaseId ||
778
+ claim.fencingToken !== previousFencingToken;
779
+ if (!identityChanged || !this.onLeaseChanged) {
780
+ return { ok: true, identityChanged };
781
+ }
782
+ try {
783
+ const callbackResult = this.onLeaseChanged({
784
+ key: claim.key,
785
+ holder: claim.holder,
786
+ previousLease: previousLease ? { ...previousLease } : null,
787
+ lease: lease ? { ...lease } : null,
788
+ previousLeaseId,
789
+ leaseId: claim.leaseId,
790
+ previousFencingToken,
791
+ fencingToken: claim.fencingToken,
792
+ });
793
+ if (
794
+ callbackResult &&
795
+ (typeof callbackResult === "object" ||
796
+ typeof callbackResult === "function") &&
797
+ typeof callbackResult.then === "function"
798
+ ) {
799
+ Promise.resolve(callbackResult).catch(() => {});
800
+ throw new Error("onLeaseChanged must be synchronous");
801
+ }
802
+ return { ok: true, identityChanged: true };
803
+ } catch (error) {
804
+ const cause =
805
+ error instanceof Error
806
+ ? error
807
+ : new Error(String(error || "lease identity persistence failed"));
808
+ const failure = new Error(cause.message, { cause });
809
+ failure.code = "TEAM_LEASE_CHANGE_PERSIST_FAILED";
810
+ failure.retryable = false;
811
+ failure.adjudication = failure.adjudication || {
812
+ code: failure.code,
813
+ reason: failure.message,
814
+ evidenceDigest: null,
815
+ requestedAt: this._now(),
816
+ holder: claim.holder,
817
+ leaseId: claim.leaseId,
818
+ fencingToken: claim.fencingToken,
819
+ };
820
+ claim.coordinatorAbort = failure;
821
+ this._setFatal(failure, {
822
+ phase: "lease-change",
823
+ key: claim.key,
824
+ holder: claim.holder,
825
+ });
826
+ return {
827
+ ok: false,
828
+ reason: "lease_change_persist_failed",
829
+ error: failure,
830
+ };
831
+ }
832
+ }
833
+
473
834
  _renewClaim(claim) {
474
835
  if (!claim || claim.lost) {
475
836
  return { ok: false, reason: "lease_lost" };
@@ -489,11 +850,12 @@ export class TeamRunner {
489
850
  ttlMs: this.ttlMs,
490
851
  });
491
852
  if (result?.ok) {
492
- claim.lease = result.lease;
493
- claim.leaseId = result.lease.leaseId;
853
+ const applied = this._applyRenewedLease(claim, result.lease);
854
+ if (!applied.ok) result = applied;
494
855
  }
495
856
  } else if (result?.ok) {
496
- claim.lease = result.lease;
857
+ const applied = this._applyRenewedLease(claim, result.lease);
858
+ if (!applied.ok) result = applied;
497
859
  }
498
860
  } catch {
499
861
  result = { ok: false, reason: "renew_failed" };
@@ -502,7 +864,7 @@ export class TeamRunner {
502
864
  claim.lost = true;
503
865
  try {
504
866
  claim.abortController.abort(
505
- new Error(`Lease for task "${claim.key}" was lost`),
867
+ result?.error || new Error(`Lease for task "${claim.key}" was lost`),
506
868
  );
507
869
  } catch {
508
870
  /* the fencing token still prevents stale settlement */
@@ -517,6 +879,112 @@ export class TeamRunner {
517
879
  if (claim.heartbeat) clearInterval(claim.heartbeat);
518
880
  this._activeKeys.delete(claim.key);
519
881
  this._claimControllers.delete(claim.abortController);
882
+ if (this._claimsByKey.get(claim.key) === claim) {
883
+ this._claimsByKey.delete(claim.key);
884
+ }
885
+ try {
886
+ claim.unregisterSessionAbortable?.();
887
+ } catch {
888
+ // The session budget may already have delivered and cleared the callback.
889
+ }
890
+ claim.unregisterSessionAbortable = null;
891
+ try {
892
+ claim.sessionWork?.release?.();
893
+ } catch {
894
+ // Session work leases are idempotent.
895
+ }
896
+ claim.sessionWork = null;
897
+ claim.sessionBudgetView = null;
898
+ }
899
+
900
+ _sessionBudgetForClaim(claim) {
901
+ if (!this.sessionBudget || !claim) return this.sessionBudget;
902
+ if (claim.sessionBudgetView) return claim.sessionBudgetView;
903
+ const authority = this.sessionBudget;
904
+ const methods = new Map();
905
+ claim.sessionBudgetView = new Proxy(authority, {
906
+ get(target, property) {
907
+ const value = Reflect.get(target, property, target);
908
+ if (typeof value !== "function") return value;
909
+ if (methods.has(property)) return methods.get(property);
910
+ const method =
911
+ property === "recordUsage"
912
+ ? (...args) => {
913
+ // Usage charged through the inherited authority is already in
914
+ // the global ledger. Remember that ownership so the task's
915
+ // returned aggregate is not charged a second time.
916
+ claim.sessionBudgetUsageHandledByExecutor = true;
917
+ return value.apply(target, args);
918
+ }
919
+ : value.bind(target);
920
+ methods.set(property, method);
921
+ return method;
922
+ },
923
+ set(target, property, value) {
924
+ return Reflect.set(target, property, value, target);
925
+ },
926
+ });
927
+ return claim.sessionBudgetView;
928
+ }
929
+
930
+ _recordSessionUsage(claim, source) {
931
+ if (!this.sessionBudget || claim.sessionBudgetSettled) return;
932
+ claim.sessionBudgetSettled = true;
933
+ if (claim.sessionBudgetUsageHandledByExecutor) return;
934
+ this.sessionBudget.recordUsage({
935
+ usage: source?.usage || null,
936
+ usageRecords: source?.usageRecords || null,
937
+ provider: source?.provider,
938
+ model: source?.model,
939
+ });
940
+ }
941
+
942
+ _sessionBudgetFailure(executionResult = null) {
943
+ const sharedReason = this.sessionBudget?.signal?.reason;
944
+ // Never attach per-task usage to the shared AbortSignal reason: every
945
+ // sibling observes that same Error object and would then re-record the
946
+ // triggering task's usage as its own failure.
947
+ const failure = new Error(
948
+ sharedReason?.message ||
949
+ `Session budget exhausted: ${this.sessionBudget?.reason?.() || "session-aborted"}`,
950
+ sharedReason instanceof Error ? { cause: sharedReason } : undefined,
951
+ );
952
+ failure.code = sharedReason?.code || "ERR_SESSION_RESOURCE_BUDGET";
953
+ failure.budgetReason =
954
+ sharedReason?.budgetReason || this.sessionBudget?.reason?.();
955
+ failure.retryable = false;
956
+ if (executionResult && typeof executionResult === "object") {
957
+ failure.usage = executionResult.usage || null;
958
+ failure.usageRecords = executionResult.usageRecords || null;
959
+ failure.provider = executionResult.provider;
960
+ failure.model = executionResult.model;
961
+ }
962
+ return failure;
963
+ }
964
+
965
+ _interruptionError(claim, executionResult = null) {
966
+ const interruption = claim?.interruption || {};
967
+ const error = new Error(interruption.reason || "human takeover requested");
968
+ error.code = "TEAM_TASK_HUMAN_INTERRUPTED";
969
+ error.retryable = false;
970
+ error.adjudication = {
971
+ code: error.code,
972
+ reason: error.message,
973
+ evidenceDigest: interruption.evidenceDigest || null,
974
+ requestedAt: interruption.requestedAt || this._now(),
975
+ requestId: interruption.requestId || null,
976
+ actor: interruption.actor || "human",
977
+ holder: interruption.holder || claim?.holder || null,
978
+ leaseId: interruption.leaseId || claim?.leaseId || null,
979
+ fencingToken: interruption.fencingToken ?? claim?.fencingToken ?? null,
980
+ };
981
+ if (executionResult && typeof executionResult === "object") {
982
+ error.usage = executionResult.usage || null;
983
+ error.usageRecords = executionResult.usageRecords || null;
984
+ error.provider = executionResult.provider;
985
+ error.model = executionResult.model;
986
+ }
987
+ return error;
520
988
  }
521
989
 
522
990
  _abandonClaim(holder, key, claim) {
@@ -683,6 +1151,18 @@ export class TeamRunner {
683
1151
  })
684
1152
  : null;
685
1153
  try {
1154
+ if (claim.interruption) {
1155
+ throw this._interruptionError(claim);
1156
+ }
1157
+ if (claim.coordinatorAbort) {
1158
+ throw claim.coordinatorAbort;
1159
+ }
1160
+ // Synchronous observers above (`onEvent`, mailbox, tracing) may abort the
1161
+ // shared authority after the worker's post-beforeTask check. This is the
1162
+ // final fail-closed fence immediately before executor side effects.
1163
+ if (this.sessionBudget?.signal?.aborted) {
1164
+ throw this._sessionBudgetFailure();
1165
+ }
686
1166
  const result = await this.runTask({
687
1167
  key,
688
1168
  task,
@@ -693,8 +1173,40 @@ export class TeamRunner {
693
1173
  mailbox: this.mailbox,
694
1174
  budget: this.budget,
695
1175
  budgetReservation: claim.budgetReservation,
1176
+ sessionBudget: this._sessionBudgetForClaim(claim),
696
1177
  signal: claim.abortController.signal,
697
1178
  });
1179
+ // The executor may ignore AbortSignal or finish concurrently with an IDE
1180
+ // takeover request. Never turn that race into an automatic success:
1181
+ // preserve usage, then fail closed for explicit adjudication.
1182
+ if (claim.interruption) {
1183
+ throw this._interruptionError(claim, result);
1184
+ }
1185
+ if (claim.coordinatorAbort) {
1186
+ throw claim.coordinatorAbort;
1187
+ }
1188
+ this._recordSessionUsage(claim, result);
1189
+ if (this.sessionBudget?.signal?.aborted) {
1190
+ throw this._sessionBudgetFailure(result);
1191
+ }
1192
+ // Executors are allowed to return after observing (or even ignoring) an
1193
+ // aborted signal. Re-read the authoritative budget before recording or
1194
+ // completing so a late result can never become a phantom success.
1195
+ if (
1196
+ this.budget?.reason?.() === "max-wall-ms" ||
1197
+ claim.abortController.signal.reason?.code ===
1198
+ "TEAM_WALL_BUDGET_EXHAUSTED"
1199
+ ) {
1200
+ if (!this._budgetStopped) {
1201
+ this._budgetStopped = true;
1202
+ this._emit("run:budget-exhausted", {
1203
+ reason: "max-wall-ms",
1204
+ });
1205
+ }
1206
+ const error = new Error("Team wall-clock budget exhausted");
1207
+ error.code = "TEAM_WALL_BUDGET_EXHAUSTED";
1208
+ throw error;
1209
+ }
698
1210
  // Fold usage/cost into the team budget regardless — the task DID execute
699
1211
  // and consumed resources. `--exec` shell tasks carry no usage → only the
700
1212
  // task-count / wall-clock dimensions move.
@@ -763,8 +1275,9 @@ export class TeamRunner {
763
1275
  });
764
1276
  }
765
1277
  } catch (err) {
1278
+ const failure = claim.coordinatorAbort || err;
766
1279
  if (span) {
767
- span.recordException(err, "task_failure");
1280
+ span.recordException(failure, "task_failure");
768
1281
  span.end({ status: "error" });
769
1282
  }
770
1283
  // A failed task still consumed a task-count slot (and any wall-clock) — fold
@@ -772,47 +1285,53 @@ export class TeamRunner {
772
1285
  if (this.budget && !claim.budgetSettled) {
773
1286
  this.budget.record(
774
1287
  {
775
- usage: err?.usage || null,
776
- usageRecords: err?.usageRecords || null,
777
- provider: err?.provider,
778
- model: err?.model,
1288
+ usage: failure?.usage || null,
1289
+ usageRecords: failure?.usageRecords || null,
1290
+ provider: failure?.provider,
1291
+ model: failure?.model,
779
1292
  reservationId: claim.budgetReservation?.id,
780
1293
  },
781
1294
  this._now(),
782
1295
  );
783
1296
  claim.budgetSettled = true;
784
1297
  }
1298
+ this._recordSessionUsage(claim, failure);
785
1299
  const outcome = this.registry.fail(key, {
786
1300
  holder,
787
1301
  leaseId: claim.leaseId,
788
- error: err?.message || String(err),
789
- retryable: err?.retryable !== false,
1302
+ error: failure?.message || String(failure),
1303
+ retryable: failure?.retryable !== false,
1304
+ adjudication: failure?.adjudication || null,
790
1305
  });
791
1306
  if (outcome?.ok) {
792
1307
  this._setState(holder, "failed-task", {
793
- error: err?.message || String(err),
1308
+ error: failure?.message || String(failure),
794
1309
  });
795
1310
  this._emit("task:failed", {
796
1311
  key,
797
1312
  holder,
798
- error: err?.message || String(err),
1313
+ error: failure?.message || String(failure),
799
1314
  retry: outcome.retry === true,
800
1315
  attempts: outcome.attempts,
1316
+ interrupted: failure?.code === "TEAM_TASK_HUMAN_INTERRUPTED",
1317
+ requestId: failure?.adjudication?.requestId || null,
801
1318
  });
802
1319
  await this._notifySettlement({
803
1320
  key,
804
1321
  holder,
805
1322
  status: outcome.retry === true ? "pending" : "failed",
806
- error: err?.message || String(err),
1323
+ error: failure?.message || String(failure),
807
1324
  retry: outcome.retry === true,
808
1325
  attempts: outcome.attempts,
1326
+ interrupted: failure?.code === "TEAM_TASK_HUMAN_INTERRUPTED",
1327
+ requestId: failure?.adjudication?.requestId || null,
809
1328
  });
810
1329
  } else {
811
1330
  this._emit("task:failure-discarded", {
812
1331
  key,
813
1332
  holder,
814
1333
  reason: outcome?.reason || "lease_lost",
815
- error: err?.message || String(err),
1334
+ error: failure?.message || String(failure),
816
1335
  ms: this._now() - startedAt,
817
1336
  });
818
1337
  await this._notifySettlement({