chainlesschain 0.162.188 → 0.162.189

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 (188) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DbZft8w0.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-DoOZpwfN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-B0J0KLTW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-D9eIR0qq.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-BnZXEVeo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-X1ce4Bjn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-BvWR-y5v.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BublSGEd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-CR_77Hso.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-DixW52tq.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DDHDpBBA.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CCv1C87K.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-B03Cc3dd.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CA06-6pU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-Bj881J52.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-UZLTPoaL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-kGwzRb_0.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-KundoYM8.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-BYZyoqDe.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BhrrppBX.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-CdC6EguZ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-CcQx3J4y.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-BpijtEzQ.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-DfZjeZTe.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-BFYSf69C.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-Okdi3pJm.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-mH-HebKW.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-xivV69x7.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-1rh_YBwg.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-eAXkNxdY.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-CS6dMVTN.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-Bq_TnkvD.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-KchHtiHt.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-TabqHW9B.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-CsmGcX5X.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-BaPFnnyz.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-2m-sUUf7.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-D5nVZBRu.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-Bo8MLZvT.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-3GDvamGa.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-D2zGuZCH.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-BBVQ4t6t.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-BIOCyOGp.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DVwBGpzc.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-BDnHfIfX.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-BU2Yqq5H.js → OrderTableRenderer-C7Ek20ag.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D3SRKaa7.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-C2B8ahyn.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-2iwQzG65.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-69ca_WeR.js → PdhVaultBrowser-CoPiWqMN.js} +4 -4
  52. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-yV5XNXo5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-uCDJhMUF.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-hN7ucMOA.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-CgU0ptN4.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-CfYPiT6j.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-B0A2C4nw.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-BE5C0ajn.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-CstQPf8W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-Co3z_ynk.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-DMTS6pQ6.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-ETw4odUa.js} +1 -1
  63. package/src/assets/web-panel/assets/RemoteSession-B0rkE09d.js +5 -0
  64. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-BTVqQgKq.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CBKKwwtO.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BHUQ438I.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-B4NqeUsF.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-BRjoYG5_.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-CxQzu3p3.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton-BKzwwmWO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-DrsFcgth.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-D21tmXCH.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-CjyjLfp8.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-Cu45-tnx.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-oMcILYOj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-Bh-Zcr5R.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-68ns63aI.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BLJkFykE.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-xcrJLq-O.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-C6PpFJtf.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-BaKA7tdM.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-BzhA57Jc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-4_U3JkIR.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-162AANxX.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-B1viuql_.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-ZVWazhCy.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-tzdyUk-1.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-DmrHNuwq.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-DAJ_M-PK.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-uIzmYwLs.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-D_ZmuLwb.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DIrncrV1.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-CDt_shTL.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-BAvSmJXW.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-BDF6EQBs.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-BHvlm0In.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-BRa4kJ_S.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-BTCXf1t-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Bbxjfstq.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-Bx4d6c1k.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-ByzGvpR9.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-C16fYKFD.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-C5WcdJH2.js} +3 -3
  104. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-CG0lY4h3.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-CKyd472S.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-CaelyMgF.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CbD_WW_6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-CcZ9xJI9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CeUpKjvi.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-Cvak6Oe2.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-CzXqvMuP.js} +1 -1
  112. package/src/assets/web-panel/assets/index-D-Swwqld.js +1 -0
  113. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-D4n1lGTB.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-D9OE8B3d.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-DBFmcaCQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-DdUMcwRB.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-DeGBoLQA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-DfSSy9uy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-DqDSF2RW.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-DvFhhe6r.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-DwfvPe-j.js} +1 -1
  122. package/src/assets/web-panel/assets/index-FMZAMKy8.js +1 -0
  123. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-FyplHuNt.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-GcKOHD_c.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-OzTxFIJP.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-QsgfSZrV.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-Z8P4BexE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-clSX4PEc.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-ifxmu8SY.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-rS3gl_68.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-rXdwUeE3.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-saf3VNeP.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-pmfaD-aK.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-CaQ2CpYF.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-CkKTqNbI.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-Dd4xsgRI.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-BFuA5YEs.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-DDfxOAJc.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-Be595JCp.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-7byljUM3.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-CTWfOTaY.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-CNZOuIxM.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-DwOcBV78.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-B6MNwAO9.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-CzyQjH9r.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-Dxh3KiVA.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-DVfZiS5N.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-BYme7fbY.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agent.js +22 -0
  151. package/src/commands/checkpoint-managed.js +1059 -0
  152. package/src/commands/checkpoint.js +3 -0
  153. package/src/commands/team-distributed.js +3324 -0
  154. package/src/commands/team.js +1130 -74
  155. package/src/data/changelog.json +13 -0
  156. package/src/lib/agent-core.js +1 -0
  157. package/src/lib/agent-team/task-lease.js +271 -3
  158. package/src/lib/agent-team/team-adjudication.js +1501 -0
  159. package/src/lib/agent-team/team-budget.js +4 -1
  160. package/src/lib/agent-team/team-control-store.js +582 -0
  161. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  162. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  163. package/src/lib/agent-team/team-runner.js +319 -22
  164. package/src/lib/agent-team/team-worktree.js +1403 -95
  165. package/src/lib/managed-checkpoint-render.js +60 -0
  166. package/src/lib/managed-tool-checkpoint.js +194 -0
  167. package/src/lib/process-execution-broker/index.js +438 -5
  168. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  169. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  170. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  171. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  172. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  173. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  174. package/src/lib/process-execution-broker/workspace-transaction.js +3556 -0
  175. package/src/lib/secure-file-identity.js +433 -0
  176. package/src/lib/turn-binding.js +160 -2
  177. package/src/lib/with-file-lock.js +140 -23
  178. package/src/repl/agent-repl.js +19 -1
  179. package/src/runtime/agent-core.js +366 -122
  180. package/src/runtime/headless-runner.js +12 -0
  181. package/src/runtime/headless-stream.js +3 -0
  182. package/src/runtime/policies/agent-policy.js +14 -0
  183. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  184. package/src/assets/web-panel/assets/MobileProjects-COBbEOlj.js +0 -1
  185. package/src/assets/web-panel/assets/RemoteSession-oOVIv9wq.js +0 -5
  186. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  187. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  188. 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
@@ -68,6 +72,11 @@ export class TeamRunner {
68
72
  typeof opts.beforeTask === "function" ? opts.beforeTask : null;
69
73
  this.afterTask =
70
74
  typeof opts.afterTask === "function" ? opts.afterTask : null;
75
+ // A lease that expires and is reacquired receives a new fencing identity.
76
+ // Persist that identity synchronously before the executor can settle under
77
+ // it. Ordinary renewals keep the same identity and do not call this hook.
78
+ this.onLeaseChanged =
79
+ typeof opts.onLeaseChanged === "function" ? opts.onLeaseChanged : null;
71
80
  this._executions = 0;
72
81
  this._reservedExecutions = 0;
73
82
  this._inFlight = 0;
@@ -78,6 +87,7 @@ export class TeamRunner {
78
87
  this._activeKeys = new Set();
79
88
  this._members = new Map(); // holder → lifecycle record
80
89
  this._budgetStopped = false;
90
+ this._registryBudgetReason = null;
81
91
  this._fatalError = null;
82
92
  // Idle workers wait on graph progress instead of polling the full graph
83
93
  // through setTimeout(0). This matters once a team has many workers but only
@@ -85,6 +95,7 @@ export class TeamRunner {
85
95
  this._progressWaiters = new Set();
86
96
  this._workerCount = 1;
87
97
  this._claimControllers = new Set();
98
+ this._claimsByKey = new Map();
88
99
  this._wallTimer = null;
89
100
  }
90
101
 
@@ -152,6 +163,121 @@ export class TeamRunner {
152
163
  }
153
164
  }
154
165
 
166
+ /** Snapshot of local in-flight claims for an IDE/operator control surface. */
167
+ activeClaims() {
168
+ return Array.from(this._claimsByKey.values()).map((claim) => ({
169
+ key: claim.key,
170
+ holder: claim.holder,
171
+ leaseId: claim.leaseId,
172
+ fencingToken: claim.fencingToken,
173
+ interrupted: !!claim.interruption,
174
+ interruption: claim.interruption ? { ...claim.interruption } : null,
175
+ }));
176
+ }
177
+
178
+ /**
179
+ * Request human takeover of an executing task.
180
+ *
181
+ * Aborting the executor cannot prove whether an external side effect landed,
182
+ * so the task always settles fail-closed into adjudication rather than being
183
+ * silently retried. A durable control log supplies requestId/evidenceDigest
184
+ * in production and must bind the exact active holder/lease/fencing identity;
185
+ * keeping this method synchronous makes it safe to call from a file-control
186
+ * watcher or an embedded IDE host.
187
+ */
188
+ interruptTask(
189
+ key,
190
+ {
191
+ holder = null,
192
+ leaseId = null,
193
+ fencingToken = null,
194
+ reason = "human takeover requested",
195
+ actor = "human",
196
+ requestId = null,
197
+ evidenceDigest = null,
198
+ } = {},
199
+ ) {
200
+ const claim = this._claimsByKey.get(key);
201
+ if (!claim) return { ok: false, reason: "not_active" };
202
+ if (
203
+ holder !== claim.holder ||
204
+ leaseId !== claim.leaseId ||
205
+ fencingToken !== claim.fencingToken
206
+ ) {
207
+ return { ok: false, reason: "stale_attempt" };
208
+ }
209
+ if (claim.interruption) {
210
+ if (
211
+ requestId &&
212
+ claim.interruption.requestId &&
213
+ requestId !== claim.interruption.requestId
214
+ ) {
215
+ return { ok: false, reason: "already_interrupted" };
216
+ }
217
+ return {
218
+ ok: true,
219
+ idempotent: true,
220
+ interruption: { ...claim.interruption },
221
+ };
222
+ }
223
+ claim.interruption = {
224
+ holder: claim.holder,
225
+ leaseId: claim.leaseId,
226
+ fencingToken: claim.fencingToken,
227
+ requestId: requestId || null,
228
+ actor: actor ? String(actor) : "human",
229
+ reason: String(reason || "human takeover requested"),
230
+ evidenceDigest: evidenceDigest || null,
231
+ requestedAt: this._now(),
232
+ };
233
+ const error = this._interruptionError(claim);
234
+ try {
235
+ claim.abortController.abort(error);
236
+ } catch {
237
+ /* settlement remains fenced by the task lease */
238
+ }
239
+ this._emit("task:interrupt-requested", {
240
+ key,
241
+ holder: claim.holder,
242
+ leaseId: claim.leaseId,
243
+ fencingToken: claim.fencingToken,
244
+ requestId: claim.interruption.requestId,
245
+ actor: claim.interruption.actor,
246
+ reason: claim.interruption.reason,
247
+ });
248
+ this._signalProgress();
249
+ return { ok: true, interruption: { ...claim.interruption } };
250
+ }
251
+
252
+ /**
253
+ * Fail the coordinator closed (for example when its durable control log is
254
+ * corrupt or rolled back). Active executors are aborted and settle as
255
+ * adjudication-required because their external outcome may be unknown.
256
+ */
257
+ abortRun(error, { requireAdjudication = true } = {}) {
258
+ const failure =
259
+ error instanceof Error ? error : new Error(String(error || "team abort"));
260
+ if (requireAdjudication) {
261
+ failure.retryable = false;
262
+ failure.adjudication = {
263
+ code: failure.code || "TEAM_RUN_ABORTED_ADJUDICATION_REQUIRED",
264
+ reason: failure.message,
265
+ evidenceDigest: null,
266
+ requestedAt: this._now(),
267
+ };
268
+ }
269
+ this._setFatal(failure, { phase: "external-control" });
270
+ for (const claim of this._claimsByKey.values()) {
271
+ claim.coordinatorAbort = failure;
272
+ try {
273
+ claim.abortController.abort(failure);
274
+ } catch {
275
+ /* settlement remains fenced by the task lease */
276
+ }
277
+ }
278
+ return { ok: true, activeClaims: this._claimsByKey.size };
279
+ }
280
+
155
281
  /**
156
282
  * Run the graph to completion (all tasks terminal) or until no teammate can
157
283
  * make progress (everything remaining is blocked/leased). Returns a summary.
@@ -185,7 +311,9 @@ export class TeamRunner {
185
311
  }
186
312
  for (const controller of this._claimControllers) {
187
313
  try {
188
- controller.abort(new Error("Team wall-clock budget exhausted"));
314
+ const error = new Error("Team wall-clock budget exhausted");
315
+ error.code = "TEAM_WALL_BUDGET_EXHAUSTED";
316
+ controller.abort(error);
189
317
  } catch {
190
318
  /* every settlement remains fenced by its lease id */
191
319
  }
@@ -244,7 +372,9 @@ export class TeamRunner {
244
372
  requestedTeammates: this.teammates,
245
373
  activeTeammates: workerCount,
246
374
  budgetStopped: this._budgetStopped,
247
- budgetReason: this.budget ? this.budget.reason() : null,
375
+ budgetReason: this.budget
376
+ ? this.budget.reason()
377
+ : this._registryBudgetReason,
248
378
  members: this.members(),
249
379
  messages: this.mailbox ? this.mailbox.size() : 0,
250
380
  mailboxStatus: this.mailbox?.status?.() || null,
@@ -319,16 +449,26 @@ export class TeamRunner {
319
449
  }
320
450
  const acq = this.registry.acquire(key, { holder, ttlMs: this.ttlMs });
321
451
  if (!acq.ok) {
452
+ if (
453
+ ["max-tasks", "max-tokens", "max-usd", "max-wall-ms"].includes(
454
+ acq.reason,
455
+ )
456
+ ) {
457
+ this._budgetStopped = true;
458
+ this._registryBudgetReason = acq.reason;
459
+ this._emit("run:budget-exhausted", { reason: acq.reason });
460
+ this._setState(holder, "shutdown", { reason: acq.reason });
461
+ this._signalProgress();
462
+ return;
463
+ }
322
464
  // Lost the race to a peer (or it just got blocked) — try another.
323
465
  this._setState(holder, "idle");
324
466
  await this._tick();
325
467
  continue;
326
468
  }
327
- const budgetReservation = this._reserveTaskBudget(
328
- key,
329
- this.registry.getTask(key),
330
- acq.lease,
331
- );
469
+ const budgetReservation = acq.budgetReservation
470
+ ? { ok: true, ...acq.budgetReservation }
471
+ : this._reserveTaskBudget(key, this.registry.getTask(key), acq.lease);
332
472
  if (!budgetReservation.ok) {
333
473
  this.registry.release(key, {
334
474
  holder,
@@ -378,6 +518,29 @@ export class TeamRunner {
378
518
  this._setState(holder, "shutdown", { reason: "fatal-error" });
379
519
  return;
380
520
  }
521
+ // `beforeTask` may persist a durable claim slowly enough to cross the
522
+ // wall-clock deadline. The timer's AbortSignal is advisory: this is the
523
+ // last authoritative fence before handing control to the executor.
524
+ // Preserve an explicit human interruption so `_execute` can settle it
525
+ // fail-closed for adjudication instead of silently abandoning it.
526
+ const preparedBudgetReason = this.budget?.reason?.();
527
+ if (
528
+ !claim.interruption &&
529
+ !claim.coordinatorAbort &&
530
+ preparedBudgetReason === "max-wall-ms"
531
+ ) {
532
+ if (!this._budgetStopped) {
533
+ this._budgetStopped = true;
534
+ this._emit("run:budget-exhausted", {
535
+ reason: preparedBudgetReason,
536
+ });
537
+ }
538
+ this._abandonClaim(holder, key, claim);
539
+ this._setState(holder, "shutdown", {
540
+ reason: preparedBudgetReason,
541
+ });
542
+ return;
543
+ }
381
544
  await this._execute(holder, key, claim);
382
545
  }
383
546
  }
@@ -445,17 +608,21 @@ export class TeamRunner {
445
608
  key,
446
609
  lease,
447
610
  leaseId: lease?.leaseId,
611
+ fencingToken: leaseFencingToken(lease),
448
612
  heartbeat: null,
449
613
  lost: false,
450
614
  abortController: new AbortController(),
451
615
  budgetReservation,
452
616
  budgetSettled: false,
617
+ interruption: null,
618
+ coordinatorAbort: null,
453
619
  };
454
620
  // Fence the task immediately after acquire, before any async durable hook.
455
621
  // Otherwise a slow beforeTask can outlive the TTL and a peer can run the
456
622
  // same side effect concurrently.
457
623
  this._activeKeys.add(key);
458
624
  this._claimControllers.add(claim.abortController);
625
+ this._claimsByKey.set(key, claim);
459
626
  const effectiveTtl =
460
627
  this.ttlMs > 0 ? this.ttlMs : this.registry.defaultTtlMs || 60000;
461
628
  const every = Math.max(
@@ -470,6 +637,71 @@ export class TeamRunner {
470
637
  return claim;
471
638
  }
472
639
 
640
+ _applyRenewedLease(claim, lease) {
641
+ const previousLease = claim.lease;
642
+ const previousLeaseId = claim.leaseId;
643
+ const previousFencingToken = claim.fencingToken;
644
+ claim.lease = lease;
645
+ claim.leaseId = lease?.leaseId;
646
+ claim.fencingToken = leaseFencingToken(lease);
647
+ const identityChanged =
648
+ claim.leaseId !== previousLeaseId ||
649
+ claim.fencingToken !== previousFencingToken;
650
+ if (!identityChanged || !this.onLeaseChanged) {
651
+ return { ok: true, identityChanged };
652
+ }
653
+ try {
654
+ const callbackResult = this.onLeaseChanged({
655
+ key: claim.key,
656
+ holder: claim.holder,
657
+ previousLease: previousLease ? { ...previousLease } : null,
658
+ lease: lease ? { ...lease } : null,
659
+ previousLeaseId,
660
+ leaseId: claim.leaseId,
661
+ previousFencingToken,
662
+ fencingToken: claim.fencingToken,
663
+ });
664
+ if (
665
+ callbackResult &&
666
+ (typeof callbackResult === "object" ||
667
+ typeof callbackResult === "function") &&
668
+ typeof callbackResult.then === "function"
669
+ ) {
670
+ Promise.resolve(callbackResult).catch(() => {});
671
+ throw new Error("onLeaseChanged must be synchronous");
672
+ }
673
+ return { ok: true, identityChanged: true };
674
+ } catch (error) {
675
+ const cause =
676
+ error instanceof Error
677
+ ? error
678
+ : new Error(String(error || "lease identity persistence failed"));
679
+ const failure = new Error(cause.message, { cause });
680
+ failure.code = "TEAM_LEASE_CHANGE_PERSIST_FAILED";
681
+ failure.retryable = false;
682
+ failure.adjudication = failure.adjudication || {
683
+ code: failure.code,
684
+ reason: failure.message,
685
+ evidenceDigest: null,
686
+ requestedAt: this._now(),
687
+ holder: claim.holder,
688
+ leaseId: claim.leaseId,
689
+ fencingToken: claim.fencingToken,
690
+ };
691
+ claim.coordinatorAbort = failure;
692
+ this._setFatal(failure, {
693
+ phase: "lease-change",
694
+ key: claim.key,
695
+ holder: claim.holder,
696
+ });
697
+ return {
698
+ ok: false,
699
+ reason: "lease_change_persist_failed",
700
+ error: failure,
701
+ };
702
+ }
703
+ }
704
+
473
705
  _renewClaim(claim) {
474
706
  if (!claim || claim.lost) {
475
707
  return { ok: false, reason: "lease_lost" };
@@ -489,11 +721,12 @@ export class TeamRunner {
489
721
  ttlMs: this.ttlMs,
490
722
  });
491
723
  if (result?.ok) {
492
- claim.lease = result.lease;
493
- claim.leaseId = result.lease.leaseId;
724
+ const applied = this._applyRenewedLease(claim, result.lease);
725
+ if (!applied.ok) result = applied;
494
726
  }
495
727
  } else if (result?.ok) {
496
- claim.lease = result.lease;
728
+ const applied = this._applyRenewedLease(claim, result.lease);
729
+ if (!applied.ok) result = applied;
497
730
  }
498
731
  } catch {
499
732
  result = { ok: false, reason: "renew_failed" };
@@ -502,7 +735,7 @@ export class TeamRunner {
502
735
  claim.lost = true;
503
736
  try {
504
737
  claim.abortController.abort(
505
- new Error(`Lease for task "${claim.key}" was lost`),
738
+ result?.error || new Error(`Lease for task "${claim.key}" was lost`),
506
739
  );
507
740
  } catch {
508
741
  /* the fencing token still prevents stale settlement */
@@ -517,6 +750,34 @@ export class TeamRunner {
517
750
  if (claim.heartbeat) clearInterval(claim.heartbeat);
518
751
  this._activeKeys.delete(claim.key);
519
752
  this._claimControllers.delete(claim.abortController);
753
+ if (this._claimsByKey.get(claim.key) === claim) {
754
+ this._claimsByKey.delete(claim.key);
755
+ }
756
+ }
757
+
758
+ _interruptionError(claim, executionResult = null) {
759
+ const interruption = claim?.interruption || {};
760
+ const error = new Error(interruption.reason || "human takeover requested");
761
+ error.code = "TEAM_TASK_HUMAN_INTERRUPTED";
762
+ error.retryable = false;
763
+ error.adjudication = {
764
+ code: error.code,
765
+ reason: error.message,
766
+ evidenceDigest: interruption.evidenceDigest || null,
767
+ requestedAt: interruption.requestedAt || this._now(),
768
+ requestId: interruption.requestId || null,
769
+ actor: interruption.actor || "human",
770
+ holder: interruption.holder || claim?.holder || null,
771
+ leaseId: interruption.leaseId || claim?.leaseId || null,
772
+ fencingToken: interruption.fencingToken ?? claim?.fencingToken ?? null,
773
+ };
774
+ if (executionResult && typeof executionResult === "object") {
775
+ error.usage = executionResult.usage || null;
776
+ error.usageRecords = executionResult.usageRecords || null;
777
+ error.provider = executionResult.provider;
778
+ error.model = executionResult.model;
779
+ }
780
+ return error;
520
781
  }
521
782
 
522
783
  _abandonClaim(holder, key, claim) {
@@ -683,6 +944,9 @@ export class TeamRunner {
683
944
  })
684
945
  : null;
685
946
  try {
947
+ if (claim.interruption) {
948
+ throw this._interruptionError(claim);
949
+ }
686
950
  const result = await this.runTask({
687
951
  key,
688
952
  task,
@@ -695,6 +959,33 @@ export class TeamRunner {
695
959
  budgetReservation: claim.budgetReservation,
696
960
  signal: claim.abortController.signal,
697
961
  });
962
+ // The executor may ignore AbortSignal or finish concurrently with an IDE
963
+ // takeover request. Never turn that race into an automatic success:
964
+ // preserve usage, then fail closed for explicit adjudication.
965
+ if (claim.interruption) {
966
+ throw this._interruptionError(claim, result);
967
+ }
968
+ if (claim.coordinatorAbort) {
969
+ throw claim.coordinatorAbort;
970
+ }
971
+ // Executors are allowed to return after observing (or even ignoring) an
972
+ // aborted signal. Re-read the authoritative budget before recording or
973
+ // completing so a late result can never become a phantom success.
974
+ if (
975
+ this.budget?.reason?.() === "max-wall-ms" ||
976
+ claim.abortController.signal.reason?.code ===
977
+ "TEAM_WALL_BUDGET_EXHAUSTED"
978
+ ) {
979
+ if (!this._budgetStopped) {
980
+ this._budgetStopped = true;
981
+ this._emit("run:budget-exhausted", {
982
+ reason: "max-wall-ms",
983
+ });
984
+ }
985
+ const error = new Error("Team wall-clock budget exhausted");
986
+ error.code = "TEAM_WALL_BUDGET_EXHAUSTED";
987
+ throw error;
988
+ }
698
989
  // Fold usage/cost into the team budget regardless — the task DID execute
699
990
  // and consumed resources. `--exec` shell tasks carry no usage → only the
700
991
  // task-count / wall-clock dimensions move.
@@ -763,8 +1054,9 @@ export class TeamRunner {
763
1054
  });
764
1055
  }
765
1056
  } catch (err) {
1057
+ const failure = claim.coordinatorAbort || err;
766
1058
  if (span) {
767
- span.recordException(err, "task_failure");
1059
+ span.recordException(failure, "task_failure");
768
1060
  span.end({ status: "error" });
769
1061
  }
770
1062
  // A failed task still consumed a task-count slot (and any wall-clock) — fold
@@ -772,10 +1064,10 @@ export class TeamRunner {
772
1064
  if (this.budget && !claim.budgetSettled) {
773
1065
  this.budget.record(
774
1066
  {
775
- usage: err?.usage || null,
776
- usageRecords: err?.usageRecords || null,
777
- provider: err?.provider,
778
- model: err?.model,
1067
+ usage: failure?.usage || null,
1068
+ usageRecords: failure?.usageRecords || null,
1069
+ provider: failure?.provider,
1070
+ model: failure?.model,
779
1071
  reservationId: claim.budgetReservation?.id,
780
1072
  },
781
1073
  this._now(),
@@ -785,34 +1077,39 @@ export class TeamRunner {
785
1077
  const outcome = this.registry.fail(key, {
786
1078
  holder,
787
1079
  leaseId: claim.leaseId,
788
- error: err?.message || String(err),
789
- retryable: err?.retryable !== false,
1080
+ error: failure?.message || String(failure),
1081
+ retryable: failure?.retryable !== false,
1082
+ adjudication: failure?.adjudication || null,
790
1083
  });
791
1084
  if (outcome?.ok) {
792
1085
  this._setState(holder, "failed-task", {
793
- error: err?.message || String(err),
1086
+ error: failure?.message || String(failure),
794
1087
  });
795
1088
  this._emit("task:failed", {
796
1089
  key,
797
1090
  holder,
798
- error: err?.message || String(err),
1091
+ error: failure?.message || String(failure),
799
1092
  retry: outcome.retry === true,
800
1093
  attempts: outcome.attempts,
1094
+ interrupted: failure?.code === "TEAM_TASK_HUMAN_INTERRUPTED",
1095
+ requestId: failure?.adjudication?.requestId || null,
801
1096
  });
802
1097
  await this._notifySettlement({
803
1098
  key,
804
1099
  holder,
805
1100
  status: outcome.retry === true ? "pending" : "failed",
806
- error: err?.message || String(err),
1101
+ error: failure?.message || String(failure),
807
1102
  retry: outcome.retry === true,
808
1103
  attempts: outcome.attempts,
1104
+ interrupted: failure?.code === "TEAM_TASK_HUMAN_INTERRUPTED",
1105
+ requestId: failure?.adjudication?.requestId || null,
809
1106
  });
810
1107
  } else {
811
1108
  this._emit("task:failure-discarded", {
812
1109
  key,
813
1110
  holder,
814
1111
  reason: outcome?.reason || "lease_lost",
815
- error: err?.message || String(err),
1112
+ error: failure?.message || String(failure),
816
1113
  ms: this._now() - startedAt,
817
1114
  });
818
1115
  await this._notifySettlement({