chainlesschain 0.165.3 → 0.165.4

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 (203) hide show
  1. package/README.md +151 -29
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-BiHNUlbU.js → AIOps-C__nnraW.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-CsKWHEZ-.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-BhIjfjWO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-BjuXFzWZ.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-k6whDkZJ.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-C4FBMYRh.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-C_vRrSXk.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-CngFZeVp.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-B_H9EBRm.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-DLEC5kt7.js} +4 -4
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-m3ClR4xh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-CmLSFrgp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DFGQteHN.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-EUUVORHE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-2PSg0Yr7.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-CcKur7gB.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DK_Fezfa.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-Dbw89xl7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-CH66h_Ec.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-D1igRb17.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-DVAsrobk.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-Dl7W8AQs.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-D5tpaEQC.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BTVcgS9n.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-BRnkJAIz.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-WXLYTQj2.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-BezaUhtc.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-CuRay3Gu.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-C55y2pPY.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-Dk08Uycg.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-C11fSB5V.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-mr0eZc97.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-C0iCSRbb.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-CY-xMG8Z.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-Bplqc_Wc.js} +6 -6
  43. package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BlzwsoSM.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-BBLmTty6.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-DtT1uf1W.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-DcqQAxTK.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-cb0kWoZe.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-DlTlbkUN.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-pp1qs-oY.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-BESLnyYz.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-B36PeT1_.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-CTK2ad-3.js} +6 -6
  55. package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-zJNGXsac.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-b9fz0Qf0.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DjZbZgiB.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.js → ProjectSettings-C-hHJXlD.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-A0yoQ3-3.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DrFgFJNC.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-KC8hF1g4.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-DGTPIk6u.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-QmbwM7Yk.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-BxjJylia.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-B7Wda1J6.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-_plQ_YSA.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-CD2YJZfx.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-qQE7NmwE.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-DgQqkFf7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-C4C0oFFt.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-Px2N2B5c.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DN6C3lQd.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-jm5aH_m7.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DlkJcv67.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-DyrTHUBs.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-mC434PtL.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-o0_s3NgK.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-CpbWcya_.js → TimelineRenderer-DAGlAXOw.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-BnulS_bs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-suQbVXYM.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-DbIJAPo5.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-CQNpbIHi.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-D8aWCERk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-CuLpYfZc.js} +3 -3
  87. package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-CsfG8ZMc.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BYsEAZbd.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-FT4Um3-I.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-CKbS_Wlt.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-CFJtzuJM.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-B9kSRW6K.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DR-x0Xpi.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-7jHr_A_k.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-B3ZX81mK.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-B7osrXqy.js} +3 -3
  98. package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-BGbPnC5x.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-BI5hP_qy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-BK3TiKVq.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-BVPU43b1.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-BW-t9n-q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-BfFCQHL3.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
  105. package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-Bt4fS2eL.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BwR7yc2h.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-Bzz0C1BZ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-C2JvmYRc.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-CGaXUsIs.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CJmIQ9XR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-C_enk9I_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Cr2FLUsD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-CvrCCqJt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-D4LfPvWA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-DMGmg497.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DShSkww-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DTxNy0ZU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-DW4BWjTk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-DWdqx7zI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-Dap9c3eD.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-DcfxhKXR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-DdkIhfi-.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-DeSgw-WV.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-DiNUEpSQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-DjEP7WkH.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-DtkFqPVA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DslTuqro.js → index-KqDaCApw.js} +1 -1
  128. package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
  129. package/src/assets/web-panel/assets/{index-CMu16vPC.js → index-OSzjTzGM.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-QBSeNVO3.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-WE08gMJP.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-r2VI1Ikk.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-tqSEHPRT.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-Dd7n7VgI.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-BwxouRw1.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-DObN0PG9.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B3nOUQc7.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-o9WU2Nbl.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-CL_vvfQ_.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CYrM2tyK.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-k_06cQ96.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-CPKG0pxS.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-BDDEeLTw.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-Cmb7RTh7.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-e2IedC4N.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-CE_gxVnm.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-DW886XV1.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Cjy_673s.js → zoom-Dwo_adaf.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +3 -3
  152. package/src/commands/agent.js +105 -35
  153. package/src/commands/artifacts.js +363 -33
  154. package/src/commands/cowork.js +202 -36
  155. package/src/commands/session-budget.js +155 -0
  156. package/src/commands/session-location.js +948 -0
  157. package/src/commands/session.js +72 -46
  158. package/src/data/changelog.json +13 -0
  159. package/src/gateways/ws/artifact-protocol.js +77 -13
  160. package/src/gateways/ws/session-protocol.js +391 -24
  161. package/src/gateways/ws/ws-agent-handler.js +191 -14
  162. package/src/gateways/ws/ws-server.js +14 -3
  163. package/src/gateways/ws/ws-session-gateway.js +101 -5
  164. package/src/harness/jsonl-session-store.js +1210 -0
  165. package/src/lib/agent-team/team-runner.js +43 -13
  166. package/src/lib/artifact-access-ledger.js +461 -0
  167. package/src/lib/artifact-cleanup-ledger.js +861 -0
  168. package/src/lib/artifact-deletion-ledger.js +677 -0
  169. package/src/lib/artifact-store.js +256 -51
  170. package/src/lib/command-lifecycle-report.js +8 -4
  171. package/src/lib/cowork-adapter.js +9 -5
  172. package/src/lib/cowork-task-runner.js +20 -0
  173. package/src/lib/cowork-workflow.js +178 -20
  174. package/src/lib/direct-model-usage.js +62 -22
  175. package/src/lib/dynamic-workflow-runtime.js +3184 -111
  176. package/src/lib/execution-location-result-apply.js +339 -0
  177. package/src/lib/execution-location-result-artifact.js +271 -0
  178. package/src/lib/execution-location-result-review.js +253 -0
  179. package/src/lib/execution-location-result-store.js +331 -0
  180. package/src/lib/execution-location-result.js +617 -0
  181. package/src/lib/execution-location-target.js +236 -1
  182. package/src/lib/managed-tool-checkpoint.js +47 -4
  183. package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
  184. package/src/lib/session-budget-production-root.js +376 -0
  185. package/src/lib/session-budget-runtime.js +109 -18
  186. package/src/lib/session-budget-usage.js +63 -0
  187. package/src/lib/session-host-snapshot.js +19 -2
  188. package/src/lib/session-resource-budget.js +766 -105
  189. package/src/lib/sub-agent-context.js +69 -22
  190. package/src/lib/web-ui-server.js +12 -1
  191. package/src/repl/agent-repl.js +237 -16
  192. package/src/runtime/agent-core.js +123 -0
  193. package/src/runtime/headless-runner.js +135 -46
  194. package/src/runtime/headless-stream.js +133 -25
  195. package/src/runtime/policies/agent-policy.js +10 -0
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
  197. package/src/assets/web-panel/assets/MarkdownRenderer-SVLOg49H.js +0 -1
  198. package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
  199. package/src/assets/web-panel/assets/Projects-CF-yD6Cn.js +0 -1
  200. package/src/assets/web-panel/assets/Providers-D-etEhg5.js +0 -1
  201. package/src/assets/web-panel/assets/devWarning-DLv_872g.js +0 -1
  202. package/src/assets/web-panel/assets/index-CdiGCibe.js +0 -1
  203. package/src/assets/web-panel/assets/index-D_0z-gyz.js +0 -1
@@ -24,6 +24,7 @@ import fs from "fs";
24
24
  import os from "os";
25
25
  import path from "path";
26
26
  import crypto from "crypto";
27
+ import { withFileLock } from "./with-file-lock.js";
27
28
 
28
29
  export const ARTIFACT_KINDS = Object.freeze([
29
30
  "report",
@@ -62,6 +63,7 @@ export function publicArtifactMetadata(entry) {
62
63
  expiresAt,
63
64
  immutable,
64
65
  recordDigest,
66
+ lineage,
65
67
  } = entry;
66
68
  return {
67
69
  id,
@@ -76,6 +78,7 @@ export function publicArtifactMetadata(entry) {
76
78
  expiresAt,
77
79
  immutable: immutable === true,
78
80
  recordDigest: recordDigest || null,
81
+ ...(lineage ? { lineage } : {}),
79
82
  };
80
83
  }
81
84
 
@@ -120,10 +123,36 @@ function normalizeKind(kind) {
120
123
  return ARTIFACT_KINDS.includes(k) ? k : "other";
121
124
  }
122
125
 
126
+ function normalizeLineage(lineage) {
127
+ if (lineage == null) return null;
128
+ if (typeof lineage !== "object" || Array.isArray(lineage)) {
129
+ throw new Error("artifact lineage must be an object");
130
+ }
131
+ let normalized;
132
+ try {
133
+ normalized = JSON.parse(JSON.stringify(lineage));
134
+ } catch {
135
+ throw new Error("artifact lineage must be JSON serializable");
136
+ }
137
+ if (
138
+ !normalized ||
139
+ typeof normalized !== "object" ||
140
+ Array.isArray(normalized)
141
+ ) {
142
+ throw new Error("artifact lineage must be a JSON object");
143
+ }
144
+ return normalized;
145
+ }
146
+
123
147
  export class ArtifactStore {
124
- constructor({ dir = null, now = () => Date.now() } = {}) {
148
+ constructor({
149
+ dir = null,
150
+ now = () => Date.now(),
151
+ indexLock = withFileLock,
152
+ } = {}) {
125
153
  this.dir = dir || artifactsDir();
126
154
  this._now = typeof now === "function" ? now : () => now;
155
+ this._indexLock = indexLock;
127
156
  }
128
157
 
129
158
  _indexFile() {
@@ -138,6 +167,31 @@ export class ArtifactStore {
138
167
  fs.mkdirSync(this._filesDir(), { recursive: true });
139
168
  }
140
169
 
170
+ _withIndexLock(callback) {
171
+ this._ensureDirs();
172
+ return this._indexLock(this._indexFile(), callback, {
173
+ failIfUnavailable: true,
174
+ timeoutMs: 30_000,
175
+ retryMs: 1,
176
+ maxRetryMs: 8,
177
+ retryJitterMs: 4,
178
+ });
179
+ }
180
+
181
+ /**
182
+ * Run a read-only callback against one locked index generation. The callback
183
+ * may inspect stored bytes before it returns; all ArtifactStore mutations use
184
+ * this same lock and therefore cannot replace that generation mid-read.
185
+ */
186
+ withIndexSnapshot(callback) {
187
+ if (typeof callback !== "function") {
188
+ throw new TypeError("artifact index snapshot requires a callback");
189
+ }
190
+ return this._withIndexLock(() =>
191
+ callback(Object.freeze(this._readEntries())),
192
+ );
193
+ }
194
+
141
195
  /**
142
196
  * Publish a file as an artifact (copy + metadata row).
143
197
  * @param {{ filePath: string, title?: string, kind?: string,
@@ -162,14 +216,16 @@ export class ArtifactStore {
162
216
  );
163
217
  }
164
218
  const body = fs.readFileSync(abs);
165
- return this._publishData(body, {
166
- fileName: path.basename(abs),
167
- title,
168
- kind,
169
- sessionId,
170
- ttlDays,
171
- sourcePath: abs,
172
- });
219
+ return this._withIndexLock(() =>
220
+ this._publishDataUnlocked(body, {
221
+ fileName: path.basename(abs),
222
+ title,
223
+ kind,
224
+ sessionId,
225
+ ttlDays,
226
+ sourcePath: abs,
227
+ }),
228
+ );
173
229
  }
174
230
 
175
231
  /**
@@ -191,20 +247,78 @@ export class ArtifactStore {
191
247
  if (typeof data !== "string" && !Buffer.isBuffer(data)) {
192
248
  throw new Error("publishData requires string or Buffer data");
193
249
  }
194
- return this._publishData(Buffer.from(data), {
195
- fileName,
196
- title,
197
- kind,
198
- mime,
199
- sessionId,
200
- ttlDays,
201
- immutable,
202
- recordDigest,
203
- sourcePath: null,
250
+ return this._withIndexLock(() =>
251
+ this._publishDataUnlocked(Buffer.from(data), {
252
+ fileName,
253
+ title,
254
+ kind,
255
+ mime,
256
+ sessionId,
257
+ ttlDays,
258
+ immutable,
259
+ recordDigest,
260
+ sourcePath: null,
261
+ }),
262
+ );
263
+ }
264
+
265
+ /**
266
+ * Publish generated bytes once for a stable authority digest.
267
+ *
268
+ * The strict cross-process index lock makes response-loss retries return the
269
+ * same metadata row instead of creating duplicate managed artifacts. The
270
+ * caller still owns the semantic validation of the persisted lineage and
271
+ * must verifyIntegrity() before treating the returned entry as readable.
272
+ */
273
+ publishDataOnce({
274
+ data,
275
+ fileName = "artifact.bin",
276
+ title,
277
+ kind,
278
+ mime,
279
+ sessionId = null,
280
+ ttlDays,
281
+ immutable = false,
282
+ recordDigest,
283
+ lineage,
284
+ } = {}) {
285
+ if (typeof data !== "string" && !Buffer.isBuffer(data)) {
286
+ throw new Error("publishDataOnce requires string or Buffer data");
287
+ }
288
+ const stableDigest = String(recordDigest || "");
289
+ if (!/^sha256:[a-f0-9]{64}$/u.test(stableDigest)) {
290
+ throw new Error("publishDataOnce requires a canonical recordDigest");
291
+ }
292
+ const normalizedLineage = normalizeLineage(lineage);
293
+ return this._withIndexLock(() => {
294
+ const matches = this._readEntries().filter(
295
+ (entry) => entry.recordDigest === stableDigest,
296
+ );
297
+ if (matches.length > 1) {
298
+ throw new Error(
299
+ `artifact authority ${stableDigest} has duplicate index rows`,
300
+ );
301
+ }
302
+ if (matches.length === 1) {
303
+ return Object.freeze({ entry: matches[0], published: false });
304
+ }
305
+ const entry = this._publishDataUnlocked(Buffer.from(data), {
306
+ fileName,
307
+ title,
308
+ kind,
309
+ mime,
310
+ sessionId,
311
+ ttlDays,
312
+ immutable,
313
+ recordDigest: stableDigest,
314
+ lineage: normalizedLineage,
315
+ sourcePath: null,
316
+ });
317
+ return Object.freeze({ entry, published: true });
204
318
  });
205
319
  }
206
320
 
207
- _publishData(
321
+ _publishDataUnlocked(
208
322
  body,
209
323
  {
210
324
  fileName,
@@ -215,6 +329,7 @@ export class ArtifactStore {
215
329
  ttlDays,
216
330
  immutable = false,
217
331
  recordDigest = null,
332
+ lineage = null,
218
333
  sourcePath = null,
219
334
  },
220
335
  ) {
@@ -247,10 +362,15 @@ export class ArtifactStore {
247
362
  expiresAt: new Date(createdAt + days * 24 * 60 * 60 * 1000).toISOString(),
248
363
  immutable: immutable === true,
249
364
  recordDigest: recordDigest ? String(recordDigest) : null,
365
+ ...(lineage ? { lineage: normalizeLineage(lineage) } : {}),
250
366
  };
251
367
  this._ensureDirs();
252
368
  const storedPath = path.join(this._filesDir(), entry.file);
253
- fs.writeFileSync(storedPath, body, { flag: "wx" });
369
+ fs.writeFileSync(storedPath, body, {
370
+ flag: "wx",
371
+ mode: 0o600,
372
+ flush: true,
373
+ });
254
374
  if (entry.immutable) {
255
375
  try {
256
376
  fs.chmodSync(storedPath, 0o444);
@@ -259,12 +379,37 @@ export class ArtifactStore {
259
379
  // support; immutable here means append-only/no update API, not WORM.
260
380
  }
261
381
  }
262
- fs.appendFileSync(this._indexFile(), JSON.stringify(entry) + "\n", "utf-8");
382
+ let descriptor = null;
383
+ try {
384
+ descriptor = fs.openSync(
385
+ this._indexFile(),
386
+ fs.constants.O_WRONLY |
387
+ fs.constants.O_APPEND |
388
+ fs.constants.O_CREAT |
389
+ Number(fs.constants.O_NOFOLLOW || 0),
390
+ 0o600,
391
+ );
392
+ const indexStat = fs.fstatSync(descriptor);
393
+ if (!indexStat.isFile() || Number(indexStat.nlink) !== 1) {
394
+ throw new Error("artifact index identity is invalid");
395
+ }
396
+ fs.writeFileSync(descriptor, `${JSON.stringify(entry)}\n`, "utf-8");
397
+ fs.fsyncSync(descriptor);
398
+ } catch (error) {
399
+ try {
400
+ this._deleteStoredFile(entry);
401
+ } catch {
402
+ /* best-effort rollback of an unpublished stored copy */
403
+ }
404
+ throw error;
405
+ } finally {
406
+ if (descriptor !== null) fs.closeSync(descriptor);
407
+ }
263
408
  return entry;
264
409
  }
265
410
 
266
411
  /** All (non-corrupt) metadata rows, oldest first. */
267
- list({ sessionId } = {}) {
412
+ _readEntries({ sessionId } = {}) {
268
413
  let raw;
269
414
  try {
270
415
  raw = fs.readFileSync(this._indexFile(), "utf-8");
@@ -285,6 +430,10 @@ export class ArtifactStore {
285
430
  return sessionId ? out.filter((e) => e.sessionId === sessionId) : out;
286
431
  }
287
432
 
433
+ list(options = {}) {
434
+ return this._withIndexLock(() => this._readEntries(options));
435
+ }
436
+
288
437
  /** One entry by id (or null). */
289
438
  get(id) {
290
439
  return this.list().find((e) => e.id === id) || null;
@@ -348,47 +497,103 @@ export class ArtifactStore {
348
497
 
349
498
  /** Remove one artifact (file + index row). @returns {boolean} found */
350
499
  remove(id) {
351
- const entries = this.list();
352
- const target = entries.find((e) => e.id === id);
353
- if (!target) return false;
354
- try {
355
- this._deleteStoredFile(target);
356
- } catch {
357
- /* best-effort — the index row is the source of truth */
358
- }
359
- this._rewrite(entries.filter((e) => e.id !== id));
360
- return true;
500
+ return this._withIndexLock(() => {
501
+ const entries = this._readEntries();
502
+ const matches = entries.filter((entry) => entry.id === id);
503
+ if (matches.length === 0) return false;
504
+ if (matches.length > 1) {
505
+ throw new Error(`artifact id ${id} has duplicate index rows`);
506
+ }
507
+ const target = matches[0];
508
+ this._rewriteUnlocked(entries.filter((entry) => entry.id !== id));
509
+ try {
510
+ this._deleteStoredFile(target);
511
+ } catch {
512
+ /* best-effort — the locked index generation is the source of truth */
513
+ }
514
+ return true;
515
+ });
361
516
  }
362
517
 
363
518
  /**
364
- * Remove expired artifacts (expiresAt in the past).
519
+ * Low-level legacy primitive for removing expired artifacts.
520
+ * Official CLI and WebSocket callers must use artifact cleanup settlement so
521
+ * retries preserve a frozen scope and leave content-free audit evidence.
365
522
  * @returns {{ removed: number }}
366
523
  */
367
524
  cleanupExpired() {
368
- const entries = this.list();
369
- const now = this._now();
370
- const keep = [];
371
- let removed = 0;
372
- for (const e of entries) {
373
- const exp = Date.parse(e.expiresAt || "");
374
- if (Number.isFinite(exp) && exp <= now) {
525
+ return this._withIndexLock(() => {
526
+ const entries = this._readEntries();
527
+ const now = this._now();
528
+ const keep = [];
529
+ const expired = [];
530
+ for (const entry of entries) {
531
+ const exp = Date.parse(entry.expiresAt || "");
532
+ if (Number.isFinite(exp) && exp <= now) {
533
+ expired.push(entry);
534
+ } else {
535
+ keep.push(entry);
536
+ }
537
+ }
538
+ if (expired.length > 0) this._rewriteUnlocked(keep);
539
+ for (const entry of expired) {
375
540
  try {
376
- this._deleteStoredFile(e);
541
+ this._deleteStoredFile(entry);
377
542
  } catch {
378
- /* best-effort */
543
+ /* best-effort — the locked index generation is the source of truth */
379
544
  }
380
- removed += 1;
381
- } else {
382
- keep.push(e);
383
545
  }
384
- }
385
- if (removed > 0) this._rewrite(keep);
386
- return { removed };
546
+ return { removed: expired.length };
547
+ });
387
548
  }
388
549
 
389
- _rewrite(entries) {
550
+ _rewriteUnlocked(entries) {
390
551
  this._ensureDirs();
391
552
  const text = entries.map((e) => JSON.stringify(e)).join("\n");
392
- fs.writeFileSync(this._indexFile(), text ? text + "\n" : "", "utf-8");
553
+ const indexFile = this._indexFile();
554
+ const temporary = `${indexFile}.${process.pid}.${crypto
555
+ .randomBytes(8)
556
+ .toString("hex")}.tmp`;
557
+ let descriptor = null;
558
+ try {
559
+ descriptor = fs.openSync(
560
+ temporary,
561
+ fs.constants.O_WRONLY |
562
+ fs.constants.O_CREAT |
563
+ fs.constants.O_EXCL |
564
+ Number(fs.constants.O_NOFOLLOW || 0),
565
+ 0o600,
566
+ );
567
+ fs.writeFileSync(descriptor, text ? `${text}\n` : "", "utf-8");
568
+ fs.fsyncSync(descriptor);
569
+ fs.closeSync(descriptor);
570
+ descriptor = null;
571
+ fs.renameSync(temporary, indexFile);
572
+ this._syncIndexDirectory();
573
+ } finally {
574
+ if (descriptor !== null) fs.closeSync(descriptor);
575
+ try {
576
+ fs.rmSync(temporary, { force: true });
577
+ } catch {
578
+ /* best-effort cleanup of an unpublished index generation */
579
+ }
580
+ }
581
+ }
582
+
583
+ _syncIndexDirectory() {
584
+ let descriptor = null;
585
+ try {
586
+ descriptor = fs.openSync(this.dir, "r");
587
+ fs.fsyncSync(descriptor);
588
+ } catch (error) {
589
+ if (
590
+ process.platform !== "win32" ||
591
+ !["EACCES", "EINVAL", "ENOTSUP", "EPERM"].includes(error?.code)
592
+ ) {
593
+ throw error;
594
+ }
595
+ } finally {
596
+ if (descriptor !== null) fs.closeSync(descriptor);
597
+ }
393
598
  }
394
599
  }
@@ -38,12 +38,16 @@ export const COMMAND_ALIAS_DECISION_POLICY = Object.freeze({
38
38
  requiredPlatforms: Object.freeze(["linux", "macos", "windows"]),
39
39
  });
40
40
 
41
- // This repository-owned anchor intentionally remains unconfigured until the
42
- // first public release containing the DELTA lifecycle exporter is published.
43
- // Updating it requires a reviewed code change with all three exact values.
41
+ // Repository-owned anchor for the first public release containing the complete
42
+ // DELTA lifecycle exporter and repository-probe hardening. Coverage must begin
43
+ // at this exact npm publication boundary; report input cannot override it.
44
44
  export const COMMAND_LIFECYCLE_DELTA_RELEASE_POLICY = Object.freeze({
45
45
  schema: "chainlesschain.command-lifecycle-delta-release-policy.v1",
46
- firstPublicRelease: null,
46
+ firstPublicRelease: Object.freeze({
47
+ version: "0.163.4",
48
+ commitSha: "27ed0ac2005e16ce5ddff53990e85b1d13ea0b1d",
49
+ npmPublishedAt: "2026-08-10T19:42:11.053Z",
50
+ }),
47
51
  incompatibleReleases: Object.freeze(["0.162.198", "0.163.3"]),
48
52
  });
49
53
 
@@ -108,8 +108,9 @@ async function invokeChatCall(callWrapper, provider, model, call) {
108
108
  * @param {string} [options.apiKey] - API key override
109
109
  * @param {(request: {call: function, provider: string, model: string}) => Promise<object>} [options.callWrapper]
110
110
  * Optional host boundary around each real provider call. The callback
111
- * resolves to a private `{content, usage?}` envelope; callers still
112
- * receive only the response string.
111
+ * may invoke `call({ signal })` to bind cancellation and resolves to a
112
+ * private `{content, usage?}` envelope; callers still receive only the
113
+ * response string.
113
114
  * @returns {(messages: object[], opts?: object) => Promise<string>}
114
115
  */
115
116
  export function createChatFn(options = {}) {
@@ -145,9 +146,10 @@ export function createChatFn(options = {}) {
145
146
  callWrapper,
146
147
  provider,
147
148
  currentModel,
148
- async () => {
149
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
149
150
  const res = await fetch(`${baseUrl}/api/chat`, {
150
151
  method: "POST",
152
+ signal,
151
153
  headers: { "Content-Type": "application/json" },
152
154
  body: JSON.stringify({
153
155
  model: currentModel,
@@ -185,9 +187,10 @@ export function createChatFn(options = {}) {
185
187
  callWrapper,
186
188
  provider,
187
189
  currentModel,
188
- async () => {
190
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
189
191
  const res = await fetch(`${baseUrl}/messages`, {
190
192
  method: "POST",
193
+ signal,
191
194
  headers: {
192
195
  "Content-Type": "application/json",
193
196
  "x-api-key": key,
@@ -214,9 +217,10 @@ export function createChatFn(options = {}) {
214
217
  callWrapper,
215
218
  provider,
216
219
  currentModel,
217
- async () => {
220
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
218
221
  const res = await fetch(`${baseUrl}/chat/completions`, {
219
222
  method: "POST",
223
+ signal,
220
224
  headers: {
221
225
  "Content-Type": "application/json",
222
226
  Authorization: `Bearer ${key}`,
@@ -518,8 +518,12 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
518
518
  * @param {string} [options.mcpSessionId] - Stable MCP ledger session for resume
519
519
  * @param {string} [options.workflowEffectId] - Canonical durable workflow effect
520
520
  * @param {boolean} [options.strictUsageTelemetry] - Require synchronous durable call observers
521
+ * @param {boolean} [options.managedCheckpoint] - Wrap mutating child tools in workspace transactions
522
+ * @param {string|null} [options.managedCheckpointStateDir] - Optional transaction state root
523
+ * @param {string[]} [options.managedCheckpointExclusions] - Explicit uncovered workspace paths
521
524
  * @param {function} [options.onUsageBoundary] - Provider-call durable start observer
522
525
  * @param {function} [options.onUsageSettlement] - Provider-call durable settlement observer
526
+ * @param {function} [options.onProviderReceipt] - Provider receipt durable prewrite observer
523
527
  * @param {function} [options.onToolCallBoundary] - Tool-call durable start observer
524
528
  * @param {function} [options.onToolCallSettlement] - Tool-call durable settlement observer
525
529
  * @param {(request: object) => boolean|Promise<boolean>} [options.approveMcpLocalCodeExecution]
@@ -540,8 +544,12 @@ export async function runCoworkTask(options = {}) {
540
544
  mcpSessionId = null,
541
545
  workflowEffectId = null,
542
546
  strictUsageTelemetry = false,
547
+ managedCheckpoint = false,
548
+ managedCheckpointStateDir = null,
549
+ managedCheckpointExclusions = [],
543
550
  onUsageBoundary = null,
544
551
  onUsageSettlement = null,
552
+ onProviderReceipt = null,
545
553
  onToolCallBoundary = null,
546
554
  onToolCallSettlement = null,
547
555
  approveMcpLocalCodeExecution = null,
@@ -640,6 +648,7 @@ export async function runCoworkTask(options = {}) {
640
648
  strictUsageTelemetry: true,
641
649
  onUsageBoundary,
642
650
  onUsageSettlement,
651
+ onProviderReceipt,
643
652
  onToolCallBoundary,
644
653
  onToolCallSettlement,
645
654
  }
@@ -678,6 +687,17 @@ export async function runCoworkTask(options = {}) {
678
687
  mcpRuntime.sessionId && mcpRuntime.bindingAllowed
679
688
  ? { sessionId: mcpRuntime.sessionId }
680
689
  : {};
690
+ if (managedCheckpoint === true) {
691
+ loopOptions.managedCheckpoint = true;
692
+ if (managedCheckpointStateDir) {
693
+ loopOptions.managedCheckpointStateDir = managedCheckpointStateDir;
694
+ }
695
+ if (Array.isArray(managedCheckpointExclusions)) {
696
+ loopOptions.managedCheckpointExclusions = [
697
+ ...managedCheckpointExclusions,
698
+ ];
699
+ }
700
+ }
681
701
  if (
682
702
  Array.isArray(template.shellPolicyOverrides) &&
683
703
  template.shellPolicyOverrides.length