chainlesschain 0.162.187 → 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 (191) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CETaO3s8.js → AIOps-DbZft8w0.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CAe7E9TW.js → ActionButton-DoOZpwfN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BGr2mzwO.js → Analytics-B0J0KLTW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-BEoY-NcT.js → AppLayout-D9eIR0qq.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-DUZivCgt.js → Artifacts-BnZXEVeo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-C3yBs68k.js → Audit-X1ce4Bjn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-C9KwiY3O.js → BackgroundAgents-BvWR-y5v.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BoSWD_C8.js → Backup-BublSGEd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Dp09GkxL.js → BaseInput-CR_77Hso.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-5t9d-Ja0.js → Chat-DixW52tq.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DmVku4GH.js → ChatBubbleRenderer-DDHDpBBA.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-8QJFlkqI.js → Checkbox-CCv1C87K.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BJVFQ3lL.js → Codegen-B03Cc3dd.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-6hdLD0oB.js → Col-CA06-6pU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B8opo0uY.js → Community-Bj881J52.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Dv8HudiN.js → Compact-UZLTPoaL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DhxuCS5N.js → Compliance-kGwzRb_0.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Dggy6VIL.js → Cowork-KundoYM8.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DSmDqJTT.js → Cron-BYZyoqDe.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-7bZq0j5f.js → Crosschain-BhrrppBX.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BWY4kh-H.js → DID-CdC6EguZ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-bOzPZxlu.js → Dashboard-CcQx3J4y.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BTFJHqQu.js → Dropdown-BpijtEzQ.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BC3MOdtT.js → EmailListRenderer-DfZjeZTe.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CFHAdTTY.js → FamilyGuardDashboard-BFYSf69C.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DZ_4aF_n.js → Federation-Okdi3pJm.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BZje6UTj.js → FormItemContext-mH-HebKW.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-D7QfCvjN.js → GenericCardRenderer-xivV69x7.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DuG7qANG.js → Git-1rh_YBwg.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BDmYAgTh.js → Governance-eAXkNxdY.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-DSXAfc1Q.js → Inference-CS6dMVTN.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DusleniP.js → KnowledgeGraph-Bq_TnkvD.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CpImmA8p.js → Logs-KchHtiHt.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BZ6LJBkk.js → MarkdownRenderer-TabqHW9B.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-DXS8CXkE.js → Marketplace-CsmGcX5X.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-D151UfUI.js → McpTools-BaPFnnyz.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DKDxnoA1.js → Memory-2m-sUUf7.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BFZ95iKM.js → MobileBridge-D5nVZBRu.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CDAR-grF.js → MobileProjects-Bo8MLZvT.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-OGJ95xJs.js → Mtc-3GDvamGa.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-BmGTo1XC.js → MtcAudit-D2zGuZCH.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-XumJRqOa.js → Multisig-BBVQ4t6t.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-DHhhRZed.js → NLProgramming-BIOCyOGp.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-B9rGxNNr.js → Notes-DVwBGpzc.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DClLbMYV.js → NotificationSettings-BDnHfIfX.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-I8OohT7N.js → OrderTableRenderer-C7Ek20ag.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-M6WzaYVa.js → Organization-D3SRKaa7.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Cb3B5KtM.js → Overflow-C2B8ahyn.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-BGk57kya.js → P2P-2iwQzG65.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CXcKyPqK.js → PdhVaultBrowser-CoPiWqMN.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-hsfJ-Y0e.js → Permissions-yV5XNXo5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BSBii4js.js → PersonalDataHub-uCDJhMUF.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-BNw52rey.js → Pipeline-hN7ucMOA.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-DUpfue4E.js → Privacy-CgU0ptN4.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C68NsP3h.js → ProjectInit-CfYPiT6j.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BJ2It9v4.js → ProjectSettings-B0A2C4nw.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-BE5C0ajn.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-EnU4VWwS.js → Providers-CstQPf8W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-BRt5-B_I.js → QrScannerModal-Co3z_ynk.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Jd5L9UL2.js → QuickAsk-DMTS6pQ6.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BNDhoVIu.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-D1wQPUNv.js → Reputation-BTVqQgKq.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DnjRegul.js → Row-CBKKwwtO.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C_T4I-nF.js → RssFeed-BHUQ438I.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-C4lrxlLh.js → Search-B4NqeUsF.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-ByYT-VzP.js → Security-BRjoYG5_.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BHpyEqB5.js → Services-CxQzu3p3.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C0I23vG-.js → Skeleton-BKzwwmWO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DGZXz22K.js → Skills-DrsFcgth.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DL_urP9A.js → Sla-D21tmXCH.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-6x-hUNQ_.js → SpeechSettings-CjyjLfp8.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-Crk5TCXq.js → SyncSettings-Cu45-tnx.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-rZGq5PZs.js → Tasks-oMcILYOj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-DT5el8Q8.js → Templates-Bh-Zcr5R.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-mlgeyMK8.js → Tenant-68ns63aI.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-C8HEA-lM.js → Terminal-BLJkFykE.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DvlqBayB.js → TimelineRenderer-xcrJLq-O.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C7lDcNmT.js → Tokens-C6PpFJtf.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-CM8nCnIJ.js → Trigger-BaKA7tdM.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BQDLUQ9x.js → Trust-BzhA57Jc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CVqM0OFE.js → UkeySign-4_U3JkIR.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BvAcQpzT.js → VideoEditing-162AANxX.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-zFVFaAZ2.js → Wallet-B1viuql_.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Cc522ums.js → WebAuthn-ZVWazhCy.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-Bg_wxpDo.js → WorkflowEditor-tzdyUk-1.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-jg0QwC8K.js → chat-DmrHNuwq.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-ALzYz1gO.js → colors-DAJ_M-PK.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DXHYUoqi.js → compact-item-uIzmYwLs.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BsGsBM5m.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-BccMLzxA.js → hasIn-CDt_shTL.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cg9cOVe9.js → index-BAvSmJXW.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BTTeAiOP.js → index-BDF6EQBs.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-C3k3ZwkQ.js → index-BHvlm0In.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CQ1ORdp-.js → index-BRa4kJ_S.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BcJzphrP.js → index-BTCXf1t-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-rIy96PeM.js → index-Bbxjfstq.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CSFESurl.js → index-Bx4d6c1k.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cmg_Npx5.js → index-ByzGvpR9.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CahDve8M.js → index-C16fYKFD.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-2VstcImq.js → index-C5WcdJH2.js} +3 -3
  104. package/src/assets/web-panel/assets/{index-C3Rf8wIM.js → index-CG0lY4h3.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-uzykuL_R.js → index-CKyd472S.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CBVIFX-x.js → index-CaelyMgF.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-TeCof1s3.js → index-CbD_WW_6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BfF0OGKU.js → index-CcZ9xJI9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-oWltafBn.js → index-CeUpKjvi.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CWLWBgfe.js → index-Cvak6Oe2.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C0AbXsDy.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-BfV8RDlg.js → index-D4n1lGTB.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Dxqiu9-F.js → index-D9OE8B3d.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BdCmJ_bG.js → index-DBFmcaCQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DgjQ-kdT.js → index-DdUMcwRB.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BBbbr1YB.js → index-DeGBoLQA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dmk7dD-T.js → index-DfSSy9uy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-JS2LXdd0.js → index-DqDSF2RW.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D9oV2iRR.js → index-DvFhhe6r.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BvsII10i.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-CrZHUsOZ.js → index-FyplHuNt.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLzPzN75.js → index-GcKOHD_c.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-koT8Q86Z.js → index-OzTxFIJP.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-08diKcTm.js → index-QsgfSZrV.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CJg6Wsjc.js → index-Z8P4BexE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-srqW5_-X.js → index-clSX4PEc.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-mIQUoUx1.js → index-ifxmu8SY.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Cz_s25m3.js → index-rS3gl_68.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-UQXedWQz.js → index-rXdwUeE3.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-rcwgNMc0.js → index-saf3VNeP.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-xafkqrW3.js → initDefaultProps-pmfaD-aK.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BrtyQXLk.js → motion-CaQ2CpYF.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BunUyg59.js → move-CkKTqNbI.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-BPwjBeuE.js → mtc-parser-Dd4xsgRI.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BpyqZlNO.js → omit-BFuA5YEs.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DDrV0XjE.js → pickAttrs-DDfxOAJc.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-1A18Pcl2.js → placementArrow-Be595JCp.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-B8Fnlmso.js → responsiveObserve-7byljUM3.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-x-Fwb2Zm.js → slide-CTWfOTaY.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-aAG5yJl_.js → statusUtils-CNZOuIxM.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BRQSTMRL.js → styleChecker-DwOcBV78.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-D5X_KozY.js → useFlexGapSupport-B6MNwAO9.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DN_YIjP-.js → useFs-CzyQjH9r.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BizxAvjw.js → usePersonalDataHub-Dxh3KiVA.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-B5oAxN93.js → vnode-DVfZiS5N.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DJhvyzKv.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/auto-mode.js +47 -0
  152. package/src/commands/checkpoint-managed.js +1059 -0
  153. package/src/commands/checkpoint.js +3 -0
  154. package/src/commands/team-distributed.js +3324 -0
  155. package/src/commands/team.js +1130 -74
  156. package/src/data/auto-mode-safety-eval-v1.json +3294 -0
  157. package/src/data/changelog.json +26 -0
  158. package/src/lib/agent-core.js +1 -0
  159. package/src/lib/agent-team/task-lease.js +271 -3
  160. package/src/lib/agent-team/team-adjudication.js +1501 -0
  161. package/src/lib/agent-team/team-budget.js +4 -1
  162. package/src/lib/agent-team/team-control-store.js +582 -0
  163. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  164. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  165. package/src/lib/agent-team/team-runner.js +319 -22
  166. package/src/lib/agent-team/team-worktree.js +1403 -95
  167. package/src/lib/auto-mode-safety-classifier.js +2034 -0
  168. package/src/lib/auto-mode-safety-eval.js +1022 -0
  169. package/src/lib/managed-checkpoint-render.js +60 -0
  170. package/src/lib/managed-tool-checkpoint.js +194 -0
  171. package/src/lib/process-execution-broker/index.js +438 -5
  172. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  173. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  174. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  175. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  176. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  177. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  178. package/src/lib/process-execution-broker/workspace-transaction.js +3556 -0
  179. package/src/lib/secure-file-identity.js +433 -0
  180. package/src/lib/turn-binding.js +160 -2
  181. package/src/lib/with-file-lock.js +140 -23
  182. package/src/repl/agent-repl.js +19 -1
  183. package/src/runtime/agent-core.js +366 -122
  184. package/src/runtime/headless-runner.js +12 -0
  185. package/src/runtime/headless-stream.js +3 -0
  186. package/src/runtime/policies/agent-policy.js +14 -0
  187. package/src/assets/web-panel/assets/Projects-D2FHLUa-.js +0 -1
  188. package/src/assets/web-panel/assets/RemoteSession-BII41Mel.js +0 -5
  189. package/src/assets/web-panel/assets/devWarning-Dy1GJA4S.js +0 -1
  190. package/src/assets/web-panel/assets/index-8zbMbbuf.js +0 -1
  191. package/src/assets/web-panel/assets/index-C97f71oi.js +0 -1
@@ -138,7 +138,10 @@ export class TeamBudget {
138
138
  this.tasks += 1;
139
139
  if (usage) {
140
140
  this.tokens +=
141
- (Number(usage.input_tokens) || 0) + (Number(usage.output_tokens) || 0);
141
+ (Number(usage.input_tokens) || 0) +
142
+ (Number(usage.output_tokens) || 0) +
143
+ (Number(usage.cache_read_input_tokens) || 0) +
144
+ (Number(usage.cache_creation_input_tokens) || 0);
142
145
  if (Array.isArray(usageRecords) && usageRecords.length > 0) {
143
146
  for (const record of usageRecords) {
144
147
  this._recordCost(record);
@@ -0,0 +1,582 @@
1
+ /**
2
+ * Durable operator controls for a running Agent Team.
3
+ *
4
+ * The team state file is owned for the lifetime of `cc team run`, so an IDE
5
+ * must never edit it directly. This adjacent, hash-chained event log lets a
6
+ * second process request human takeover while preserving the coordinator's
7
+ * exclusive state ownership. Requests are bound to a stable stateId and the
8
+ * canonical state path; acknowledgements are append-only and cross-process
9
+ * serialized. Every interrupt is also bound to one exact holder/lease/fencing
10
+ * identity so a delayed request can never take over a future task attempt.
11
+ */
12
+
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { createHash, randomUUID } from "node:crypto";
16
+ import { withFileLock } from "../with-file-lock.js";
17
+
18
+ export const TEAM_CONTROL_SCHEMA_VERSION = 2;
19
+ export const MAX_TEAM_CONTROL_EVENTS = 10_000;
20
+ export const MAX_TEAM_CONTROL_BYTES = 8 * 1024 * 1024;
21
+
22
+ const STORE_SCHEMA = "cc.team-control";
23
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
24
+ const ID_PATTERN = /^tctl_[a-zA-Z0-9][a-zA-Z0-9._-]{0,126}$/;
25
+ const OUTCOMES = new Set([
26
+ "accepted",
27
+ "not_active",
28
+ "stale_attempt",
29
+ "rejected",
30
+ ]);
31
+
32
+ function hasControlCharacters(value) {
33
+ for (let index = 0; index < value.length; index += 1) {
34
+ const code = value.charCodeAt(index);
35
+ if (code < 32 || code === 127) return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ function controlError(code, message, details = {}) {
41
+ const error = new Error(message);
42
+ error.name = "TeamControlStoreError";
43
+ error.code = code;
44
+ Object.assign(error, details);
45
+ return error;
46
+ }
47
+
48
+ function canonicalJson(value) {
49
+ if (value === null) return "null";
50
+ if (typeof value === "string" || typeof value === "boolean") {
51
+ return JSON.stringify(value);
52
+ }
53
+ if (typeof value === "number") {
54
+ if (!Number.isFinite(value)) {
55
+ throw controlError("TEAM_CONTROL_INVALID", "Non-finite control value");
56
+ }
57
+ return JSON.stringify(value);
58
+ }
59
+ if (Array.isArray(value)) {
60
+ return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
61
+ }
62
+ if (value && typeof value === "object") {
63
+ return `{${Object.keys(value)
64
+ .sort()
65
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
66
+ .join(",")}}`;
67
+ }
68
+ throw controlError("TEAM_CONTROL_INVALID", "Unsupported control value");
69
+ }
70
+
71
+ function digest(domain, value) {
72
+ return `sha256:${createHash("sha256")
73
+ .update(`${domain}\0${canonicalJson(value)}`)
74
+ .digest("hex")}`;
75
+ }
76
+
77
+ function pathIdentity(value) {
78
+ const resolved = path.resolve(value);
79
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
80
+ }
81
+
82
+ function canonicalFile(filePath, { mustExist }) {
83
+ if (typeof filePath !== "string" || filePath.trim() === "") {
84
+ throw controlError("TEAM_CONTROL_UNSAFE_PATH", "Control path is required");
85
+ }
86
+ const requested = path.resolve(filePath);
87
+ const parent = fs.realpathSync.native(path.dirname(requested));
88
+ const target = path.join(parent, path.basename(requested));
89
+ if (!fs.existsSync(target)) {
90
+ if (mustExist) {
91
+ throw controlError(
92
+ "TEAM_CONTROL_UNSAFE_PATH",
93
+ `Required control authority file does not exist: ${target}`,
94
+ );
95
+ }
96
+ return target;
97
+ }
98
+ const entry = fs.lstatSync(target);
99
+ if (entry.isSymbolicLink() || !entry.isFile() || entry.nlink !== 1) {
100
+ throw controlError(
101
+ "TEAM_CONTROL_UNSAFE_PATH",
102
+ `Control authority must be a regular, single-link file: ${target}`,
103
+ );
104
+ }
105
+ if (process.platform !== "win32" && (entry.mode & 0o077) !== 0) {
106
+ throw controlError(
107
+ "TEAM_CONTROL_UNSAFE_PATH",
108
+ `Control authority permissions must be 0600: ${target}`,
109
+ );
110
+ }
111
+ return fs.realpathSync.native(target);
112
+ }
113
+
114
+ function assertIdentifier(value, label, pattern = ID_PATTERN) {
115
+ const text = String(value || "");
116
+ if (!pattern.test(text)) {
117
+ throw controlError("TEAM_CONTROL_INVALID", `Invalid ${label}`);
118
+ }
119
+ return text;
120
+ }
121
+
122
+ function normalizeText(value, label, maxLength) {
123
+ const text = String(value || "").trim();
124
+ if (!text || text.length > maxLength || text.includes("\0")) {
125
+ throw controlError("TEAM_CONTROL_INVALID", `Invalid ${label}`);
126
+ }
127
+ return text;
128
+ }
129
+
130
+ function normalizeAttemptString(value, label, maxLength = 512) {
131
+ if (
132
+ typeof value !== "string" ||
133
+ value.length === 0 ||
134
+ value.length > maxLength ||
135
+ value.trim() !== value ||
136
+ hasControlCharacters(value)
137
+ ) {
138
+ throw controlError("TEAM_CONTROL_INVALID", `Invalid ${label}`);
139
+ }
140
+ return value;
141
+ }
142
+
143
+ function normalizeFencingToken(value) {
144
+ if (Number.isSafeInteger(value) && value > 0) return value;
145
+ if (typeof value === "string") {
146
+ return normalizeAttemptString(value, "fencingToken");
147
+ }
148
+ throw controlError("TEAM_CONTROL_INVALID", "Invalid fencingToken");
149
+ }
150
+
151
+ export function computeTeamControlAttemptDigest({
152
+ holder,
153
+ leaseId,
154
+ fencingToken,
155
+ } = {}) {
156
+ return digest("cc-team-control-attempt-v1", {
157
+ holder: normalizeAttemptString(holder, "holder", 256),
158
+ leaseId: normalizeAttemptString(leaseId, "leaseId"),
159
+ fencingToken: normalizeFencingToken(fencingToken),
160
+ });
161
+ }
162
+
163
+ export function computeTeamControlAdjudicationDigest({
164
+ caseId = null,
165
+ evidenceDigest,
166
+ } = {}) {
167
+ const normalizedEvidence = String(evidenceDigest || "");
168
+ if (!DIGEST_PATTERN.test(normalizedEvidence)) {
169
+ throw controlError("TEAM_CONTROL_INVALID", "Invalid evidenceDigest");
170
+ }
171
+ if (caseId == null) return normalizedEvidence;
172
+ return digest("cc-team-control-adjudication-v1", {
173
+ caseId: normalizeAttemptString(caseId, "caseId"),
174
+ evidenceDigest: normalizedEvidence,
175
+ });
176
+ }
177
+
178
+ function eventDigest(event) {
179
+ const body = { ...event };
180
+ delete body.digest;
181
+ return digest("cc-team-control-event-v2", body);
182
+ }
183
+
184
+ function initialDocument({ statePath, stateId, now }) {
185
+ const statePathDigest = digest(
186
+ "cc-team-control-state-path-v1",
187
+ pathIdentity(statePath),
188
+ );
189
+ return {
190
+ schema: STORE_SCHEMA,
191
+ schemaVersion: TEAM_CONTROL_SCHEMA_VERSION,
192
+ statePathDigest,
193
+ stateId,
194
+ createdAt: now,
195
+ events: [],
196
+ };
197
+ }
198
+
199
+ function replay(document, authority) {
200
+ if (
201
+ !document ||
202
+ document.schema !== STORE_SCHEMA ||
203
+ document.schemaVersion !== TEAM_CONTROL_SCHEMA_VERSION ||
204
+ document.statePathDigest !== authority.statePathDigest ||
205
+ document.stateId !== authority.stateId ||
206
+ !Number.isFinite(document.createdAt) ||
207
+ !Array.isArray(document.events)
208
+ ) {
209
+ throw controlError(
210
+ "TEAM_CONTROL_CORRUPT",
211
+ "Invalid or mismatched team control store",
212
+ );
213
+ }
214
+ const requests = new Map();
215
+ let previousDigest = null;
216
+ for (let index = 0; index < document.events.length; index += 1) {
217
+ const event = document.events[index];
218
+ if (
219
+ !event ||
220
+ event.version !== TEAM_CONTROL_SCHEMA_VERSION ||
221
+ event.sequence !== index + 1 ||
222
+ event.previousDigest !== previousDigest ||
223
+ !DIGEST_PATTERN.test(String(event.digest || "")) ||
224
+ event.digest !== eventDigest(event) ||
225
+ !Number.isFinite(event.at)
226
+ ) {
227
+ throw controlError(
228
+ "TEAM_CONTROL_CORRUPT",
229
+ "Invalid team control hash chain",
230
+ );
231
+ }
232
+ if (event.type === "interrupt.request") {
233
+ assertIdentifier(event.requestId, "requestId");
234
+ if (requests.has(event.requestId)) {
235
+ throw controlError(
236
+ "TEAM_CONTROL_CORRUPT",
237
+ "Duplicate team control request",
238
+ );
239
+ }
240
+ normalizeText(event.taskKey, "taskKey", 256);
241
+ normalizeAttemptString(event.holder, "holder", 256);
242
+ normalizeAttemptString(event.leaseId, "leaseId");
243
+ normalizeFencingToken(event.fencingToken);
244
+ if (
245
+ event.attemptDigest !==
246
+ computeTeamControlAttemptDigest({
247
+ holder: event.holder,
248
+ leaseId: event.leaseId,
249
+ fencingToken: event.fencingToken,
250
+ })
251
+ ) {
252
+ throw controlError(
253
+ "TEAM_CONTROL_CORRUPT",
254
+ "Interrupt attempt digest does not match its lease binding",
255
+ );
256
+ }
257
+ normalizeText(event.actor, "actor", 256);
258
+ normalizeText(event.reason, "reason", 2048);
259
+ requests.set(event.requestId, {
260
+ request: event,
261
+ acknowledgement: null,
262
+ });
263
+ } else if (event.type === "interrupt.ack") {
264
+ const record = requests.get(event.requestId);
265
+ if (!record || record.acknowledgement || !OUTCOMES.has(event.outcome)) {
266
+ throw controlError(
267
+ "TEAM_CONTROL_CORRUPT",
268
+ "Invalid team control acknowledgement",
269
+ );
270
+ }
271
+ normalizeText(event.workerId, "workerId", 256);
272
+ record.acknowledgement = event;
273
+ } else {
274
+ throw controlError(
275
+ "TEAM_CONTROL_CORRUPT",
276
+ `Unknown team control event: ${event.type}`,
277
+ );
278
+ }
279
+ previousDigest = event.digest;
280
+ }
281
+ return { requests, headDigest: previousDigest };
282
+ }
283
+
284
+ function assertAnchor(document, runtime, authority, anchor) {
285
+ if (anchor == null) return;
286
+ if (
287
+ !anchor ||
288
+ anchor.version !== TEAM_CONTROL_SCHEMA_VERSION ||
289
+ anchor.statePathDigest !== authority.statePathDigest ||
290
+ anchor.stateId !== authority.stateId ||
291
+ !Number.isSafeInteger(anchor.lastSequence) ||
292
+ anchor.lastSequence < 0 ||
293
+ anchor.lastSequence > document.events.length
294
+ ) {
295
+ throw controlError(
296
+ "TEAM_CONTROL_ROLLBACK",
297
+ "Team control cursor is not a valid log prefix",
298
+ );
299
+ }
300
+ const anchoredDigest =
301
+ anchor.lastSequence === 0
302
+ ? null
303
+ : document.events[anchor.lastSequence - 1]?.digest;
304
+ if (anchoredDigest !== anchor.headDigest) {
305
+ throw controlError(
306
+ "TEAM_CONTROL_ROLLBACK",
307
+ "Team control cursor digest is not an exact log prefix",
308
+ );
309
+ }
310
+ if (
311
+ document.events.length === anchor.lastSequence &&
312
+ runtime.headDigest !== anchor.headDigest
313
+ ) {
314
+ throw controlError(
315
+ "TEAM_CONTROL_ROLLBACK",
316
+ "Team control log head diverged from its cursor",
317
+ );
318
+ }
319
+ }
320
+
321
+ function readDocument(filePath, authority, now) {
322
+ if (!fs.existsSync(filePath)) {
323
+ return initialDocument({ ...authority, now });
324
+ }
325
+ const entry = fs.lstatSync(filePath);
326
+ if (entry.isSymbolicLink() || !entry.isFile() || entry.nlink !== 1) {
327
+ throw controlError(
328
+ "TEAM_CONTROL_UNSAFE_PATH",
329
+ `Control store must be a regular, single-link file: ${filePath}`,
330
+ );
331
+ }
332
+ if (entry.size <= 0 || entry.size > MAX_TEAM_CONTROL_BYTES) {
333
+ throw controlError(
334
+ "TEAM_CONTROL_CORRUPT",
335
+ "Team control store size is invalid",
336
+ );
337
+ }
338
+ let document;
339
+ try {
340
+ document = JSON.parse(fs.readFileSync(filePath, "utf8"));
341
+ } catch (cause) {
342
+ throw controlError(
343
+ "TEAM_CONTROL_CORRUPT",
344
+ "Unreadable team control store",
345
+ {
346
+ cause,
347
+ },
348
+ );
349
+ }
350
+ replay(document, authority);
351
+ return document;
352
+ }
353
+
354
+ function atomicWrite(filePath, document) {
355
+ const contents = `${JSON.stringify(document, null, 2)}\n`;
356
+ if (Buffer.byteLength(contents, "utf8") > MAX_TEAM_CONTROL_BYTES) {
357
+ throw controlError("TEAM_CONTROL_LIMIT", "Team control store is full");
358
+ }
359
+ const temporary = path.join(
360
+ path.dirname(filePath),
361
+ `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
362
+ );
363
+ let descriptor = null;
364
+ try {
365
+ descriptor = fs.openSync(temporary, "wx", 0o600);
366
+ fs.writeFileSync(descriptor, contents, "utf8");
367
+ fs.fsyncSync(descriptor);
368
+ fs.closeSync(descriptor);
369
+ descriptor = null;
370
+ fs.renameSync(temporary, filePath);
371
+ } finally {
372
+ if (descriptor != null) {
373
+ try {
374
+ fs.closeSync(descriptor);
375
+ } catch {
376
+ /* preserve the primary failure */
377
+ }
378
+ }
379
+ try {
380
+ if (fs.existsSync(temporary)) fs.rmSync(temporary, { force: true });
381
+ } catch {
382
+ /* preserve the primary failure */
383
+ }
384
+ }
385
+ }
386
+
387
+ function append(document, event) {
388
+ if (document.events.length >= MAX_TEAM_CONTROL_EVENTS) {
389
+ throw controlError(
390
+ "TEAM_CONTROL_LIMIT",
391
+ "Team control event limit reached",
392
+ );
393
+ }
394
+ const previousDigest =
395
+ document.events[document.events.length - 1]?.digest || null;
396
+ const next = {
397
+ version: TEAM_CONTROL_SCHEMA_VERSION,
398
+ sequence: document.events.length + 1,
399
+ previousDigest,
400
+ ...event,
401
+ };
402
+ next.digest = eventDigest(next);
403
+ document.events.push(next);
404
+ return next;
405
+ }
406
+
407
+ export class TeamControlStore {
408
+ constructor({
409
+ statePath,
410
+ stateId,
411
+ filePath = null,
412
+ now = () => Date.now(),
413
+ } = {}) {
414
+ this.statePath = canonicalFile(statePath, { mustExist: true });
415
+ this.stateId = assertIdentifier(
416
+ stateId,
417
+ "stateId",
418
+ /^team_state_[a-zA-Z0-9][a-zA-Z0-9._-]{0,126}$/,
419
+ );
420
+ this.filePath = canonicalFile(
421
+ filePath || `${this.statePath}.controls.json`,
422
+ { mustExist: false },
423
+ );
424
+ this._now = typeof now === "function" ? now : () => Number(now);
425
+ this.authority = {
426
+ statePath: this.statePath,
427
+ statePathDigest: digest(
428
+ "cc-team-control-state-path-v1",
429
+ pathIdentity(this.statePath),
430
+ ),
431
+ stateId: this.stateId,
432
+ };
433
+ }
434
+
435
+ _locked(mutator, { write = false } = {}) {
436
+ return withFileLock(
437
+ this.filePath,
438
+ () => {
439
+ const currentStatePath = canonicalFile(this.statePath, {
440
+ mustExist: true,
441
+ });
442
+ if (pathIdentity(currentStatePath) !== pathIdentity(this.statePath)) {
443
+ throw controlError(
444
+ "TEAM_CONTROL_UNSAFE_PATH",
445
+ "Team state canonical path changed",
446
+ );
447
+ }
448
+ const document = readDocument(
449
+ this.filePath,
450
+ this.authority,
451
+ this._now(),
452
+ );
453
+ const result = mutator(document, replay(document, this.authority));
454
+ if (write) atomicWrite(this.filePath, document);
455
+ return result;
456
+ },
457
+ { failIfUnavailable: true },
458
+ );
459
+ }
460
+
461
+ requestInterrupt({
462
+ requestId = `tctl_${randomUUID()}`,
463
+ taskKey,
464
+ holder,
465
+ leaseId,
466
+ fencingToken,
467
+ actor,
468
+ reason,
469
+ } = {}) {
470
+ const normalizedId = assertIdentifier(requestId, "requestId");
471
+ const normalizedTask = normalizeText(taskKey, "taskKey", 256);
472
+ const normalizedHolder = normalizeAttemptString(holder, "holder", 256);
473
+ const normalizedLeaseId = normalizeAttemptString(leaseId, "leaseId");
474
+ const normalizedFencingToken = normalizeFencingToken(fencingToken);
475
+ const attemptDigest = computeTeamControlAttemptDigest({
476
+ holder: normalizedHolder,
477
+ leaseId: normalizedLeaseId,
478
+ fencingToken: normalizedFencingToken,
479
+ });
480
+ const normalizedActor = normalizeText(actor, "actor", 256);
481
+ const normalizedReason = normalizeText(reason, "reason", 2048);
482
+ return this._locked(
483
+ (document, runtime) => {
484
+ const prior = runtime.requests.get(normalizedId);
485
+ if (prior) {
486
+ const request = prior.request;
487
+ if (
488
+ request.taskKey === normalizedTask &&
489
+ request.holder === normalizedHolder &&
490
+ request.leaseId === normalizedLeaseId &&
491
+ request.fencingToken === normalizedFencingToken &&
492
+ request.actor === normalizedActor &&
493
+ request.reason === normalizedReason
494
+ ) {
495
+ return { ok: true, idempotent: true, request: { ...request } };
496
+ }
497
+ throw controlError(
498
+ "TEAM_CONTROL_CONFLICT",
499
+ "Control request id is already bound to different input",
500
+ );
501
+ }
502
+ const request = append(document, {
503
+ type: "interrupt.request",
504
+ at: this._now(),
505
+ requestId: normalizedId,
506
+ taskKey: normalizedTask,
507
+ holder: normalizedHolder,
508
+ leaseId: normalizedLeaseId,
509
+ fencingToken: normalizedFencingToken,
510
+ attemptDigest,
511
+ actor: normalizedActor,
512
+ reason: normalizedReason,
513
+ });
514
+ return { ok: true, request: { ...request } };
515
+ },
516
+ { write: true },
517
+ );
518
+ }
519
+
520
+ pending({ anchor = null } = {}) {
521
+ return this._locked((document, runtime) => {
522
+ assertAnchor(document, runtime, this.authority, anchor);
523
+ return Array.from(runtime.requests.values())
524
+ .filter((record) => !record.acknowledgement)
525
+ .map((record) => ({ ...record.request }));
526
+ });
527
+ }
528
+
529
+ acknowledge({ requestId, outcome, workerId } = {}) {
530
+ const normalizedId = assertIdentifier(requestId, "requestId");
531
+ if (!OUTCOMES.has(outcome)) {
532
+ throw controlError("TEAM_CONTROL_INVALID", "Invalid control outcome");
533
+ }
534
+ const normalizedWorker = normalizeText(workerId, "workerId", 256);
535
+ return this._locked(
536
+ (document, runtime) => {
537
+ const record = runtime.requests.get(normalizedId);
538
+ if (!record) {
539
+ throw controlError(
540
+ "TEAM_CONTROL_NOT_FOUND",
541
+ `Control request not found: ${normalizedId}`,
542
+ );
543
+ }
544
+ if (record.acknowledgement) {
545
+ if (
546
+ record.acknowledgement.outcome === outcome &&
547
+ record.acknowledgement.workerId === normalizedWorker
548
+ ) {
549
+ return {
550
+ ok: true,
551
+ idempotent: true,
552
+ acknowledgement: { ...record.acknowledgement },
553
+ };
554
+ }
555
+ throw controlError(
556
+ "TEAM_CONTROL_CONFLICT",
557
+ "Control request already has a different acknowledgement",
558
+ );
559
+ }
560
+ const acknowledgement = append(document, {
561
+ type: "interrupt.ack",
562
+ at: this._now(),
563
+ requestId: normalizedId,
564
+ outcome,
565
+ workerId: normalizedWorker,
566
+ });
567
+ return { ok: true, acknowledgement: { ...acknowledgement } };
568
+ },
569
+ { write: true },
570
+ );
571
+ }
572
+
573
+ cursor() {
574
+ return this._locked((document, runtime) => ({
575
+ version: TEAM_CONTROL_SCHEMA_VERSION,
576
+ statePathDigest: this.authority.statePathDigest,
577
+ stateId: this.stateId,
578
+ lastSequence: document.events.length,
579
+ headDigest: runtime.headDigest,
580
+ }));
581
+ }
582
+ }