chainlesschain 0.162.95 → 0.162.96

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 (217) hide show
  1. package/README.md +1 -1
  2. package/bin/chainlesschain.js +8 -18
  3. package/package.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DLvvkb9x.js → AIOps-Bo9Ux24z.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BxhQTwqd.js → ActionButton-CExgXSQT.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C4oUXzhG.js → Analytics-BNnZhRTZ.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CjzKJdv3.js → AppLayout-D9xfqFCT.js} +5 -5
  8. package/src/assets/web-panel/assets/{Audit-B-MrugUs.js → Audit-Cpgu8uCw.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-ecIfEpIZ.js → Backup-CHdEJnNE.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BUcSfWnx.js → BaseInput-Cbw-PxBq.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DIc1n_Lk.js → Chat-ChHTZK_6.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BGzg7oVA.js → ChatBubbleRenderer-Dl6z6ZH7.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-BQ3Ueff-.js → Checkbox-Cj8d9bru.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CvSGLnqE.js → Codegen-C2YobW4j.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Br9IXZcE.js → Col-CxfV5FGT.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-tPuLOf0r.js → Community-CiwOASMj.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-vMWH73BE.js → Compact-CTg_2iKW.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Ct9z699x.js → Compliance-HMbsTmtw.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Bxs1kL1y.js → Cowork-DyNDqGR2.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-QEpr2k4U.js → Cron-9-x5M7qy.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-u4xbUw6O.js → Crosschain-4msgDaYJ.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DXTEvyrW.js → DID--2Beb5Po.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-C4pZgzkg.js → Dashboard-T6nq0Lzs.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-LRuqREGe.js → Dropdown-D_Q6T10v.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-D7XI1yBM.js → EmailListRenderer-TnN21bJx.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-3y_SuAMQ.js → FamilyGuardDashboard-kky_uIij.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DbjbJJCW.js → Federation-C1GkNY4B.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-WPpMZcUw.js → FormItemContext-5-_Fc5YV.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-nNT9Tn4E.js → GenericCardRenderer-CmHiB7sr.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DqDaVzwV.js → Git-Cuci_BPI.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BquBZpcg.js → Governance-BU0cO2LD.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-RcPPYijs.js → Inference-BDFNgEcx.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-ByXfZV13.js → KnowledgeGraph-DITXUzHz.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CHT3DUcq.js → Logs-Ea-07CRd.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-Bi0HJ9pN.js → Marketplace-D6OBbRVG.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-QCBxPRRc.js → McpTools-eiKpIJMi.js} +4 -4
  37. package/src/assets/web-panel/assets/{Memory-D4RlxGXn.js → Memory-M-X-T8UF.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileBridge-CU_PV7e-.js → MobileBridge-xcZb9Ekv.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileProjects-c1CMFD8z.js → MobileProjects-DCfoTPRC.js} +1 -1
  40. package/src/assets/web-panel/assets/{Mtc-BezDB1-s.js → Mtc-BWfGDzD8.js} +5 -5
  41. package/src/assets/web-panel/assets/{MtcAudit-CgLpiHpE.js → MtcAudit-imBuld-U.js} +2 -2
  42. package/src/assets/web-panel/assets/{Multisig-vx6bz-bA.js → Multisig-C2kcsq7I.js} +3 -3
  43. package/src/assets/web-panel/assets/{NLProgramming-CGOagg3-.js → NLProgramming-DObvXSXP.js} +1 -1
  44. package/src/assets/web-panel/assets/{Notes-BcuDGYRS.js → Notes-C-6OGJMe.js} +4 -4
  45. package/src/assets/web-panel/assets/{NotificationSettings-ZAGCDUpc.js → NotificationSettings-ub7Ozxci.js} +1 -1
  46. package/src/assets/web-panel/assets/{OrderTableRenderer-B5onE0eX.js → OrderTableRenderer-CR2W89Vk.js} +1 -1
  47. package/src/assets/web-panel/assets/{Organization-y_y20-5Y.js → Organization-DNA_Wty7.js} +4 -4
  48. package/src/assets/web-panel/assets/{Overflow-BEICS31D.js → Overflow-rT4Wb4rQ.js} +1 -1
  49. package/src/assets/web-panel/assets/{P2P-D_WRMzh4.js → P2P-CK-hARKv.js} +2 -2
  50. package/src/assets/web-panel/assets/PdhVaultBrowser-C2pbtNbT.js +7 -0
  51. package/src/assets/web-panel/assets/{Permissions-3aqCc_4f.js → Permissions-B8EIEqGJ.js} +4 -4
  52. package/src/assets/web-panel/assets/{PersonalDataHub-DT3R9g9Y.js → PersonalDataHub-BxNVNU3s.js} +2 -2
  53. package/src/assets/web-panel/assets/{Pipeline-MiMWIhw0.js → Pipeline-CT9pbkyY.js} +1 -1
  54. package/src/assets/web-panel/assets/{Privacy-C3JsLuDa.js → Privacy-DhCNd2CS.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectInit-EhHVi1ff.js → ProjectInit-Qngnx-vQ.js} +2 -2
  56. package/src/assets/web-panel/assets/{ProjectSettings-D_dNEA37.js → ProjectSettings-Dfn-nQc1.js} +2 -2
  57. package/src/assets/web-panel/assets/Projects-BKdiSaPR.js +1 -0
  58. package/src/assets/web-panel/assets/{Providers-CPEhi2iZ.js → Providers-4o9B_n-X.js} +1 -1
  59. package/src/assets/web-panel/assets/{QuickAsk-BFQpMleX.js → QuickAsk-bs1qvF30.js} +1 -1
  60. package/src/assets/web-panel/assets/{Recommend-DzbovRC8.js → Recommend-CuEnKA2K.js} +1 -1
  61. package/src/assets/web-panel/assets/{Reputation-DoBeV3TD.js → Reputation-DbfvmRb2.js} +1 -1
  62. package/src/assets/web-panel/assets/{Row-DthhwX_B.js → Row-DvMJGAI3.js} +1 -1
  63. package/src/assets/web-panel/assets/{RssFeed-Dsf5U0lP.js → RssFeed-oJ0BZvmA.js} +2 -2
  64. package/src/assets/web-panel/assets/{Search-CJPB8kKl.js → Search-DnIVrY9O.js} +1 -1
  65. package/src/assets/web-panel/assets/{Security-DI9MfpdD.js → Security-CIWhI_Tc.js} +3 -3
  66. package/src/assets/web-panel/assets/{Services-BdG0TFGf.js → Services-DLrnpjwm.js} +2 -2
  67. package/src/assets/web-panel/assets/{Skeleton-DSYazY_B.js → Skeleton-CYmqgtVb.js} +1 -1
  68. package/src/assets/web-panel/assets/{Skills-BD309VTT.js → Skills-BUzCkoeu.js} +1 -1
  69. package/src/assets/web-panel/assets/{Sla-CaUlw4_6.js → Sla-DYRE4bnS.js} +1 -1
  70. package/src/assets/web-panel/assets/{SpeechSettings-CZAPkR7H.js → SpeechSettings-_6MurdOg.js} +1 -1
  71. package/src/assets/web-panel/assets/{SyncSettings-CFRSowN3.js → SyncSettings-UIdnF5Li.js} +2 -2
  72. package/src/assets/web-panel/assets/{Tasks-Di8EOluc.js → Tasks-COx2CjT8.js} +1 -1
  73. package/src/assets/web-panel/assets/{Templates-B5FTziwi.js → Templates-BcHAmkZo.js} +1 -1
  74. package/src/assets/web-panel/assets/{Tenant-Dj3XYfBg.js → Tenant-CjYQS8yZ.js} +1 -1
  75. package/src/assets/web-panel/assets/{Terminal-CznH_rJT.js → Terminal-BmfOPcWt.js} +2 -2
  76. package/src/assets/web-panel/assets/{TimelineRenderer-DTHzaXPt.js → TimelineRenderer-D5HuarNQ.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tokens-Bo56nuML.js → Tokens-C_fbProA.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trigger-pyVUVBwb.js → Trigger-JPulpRx8.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trust-Cp7jMkKN.js → Trust-CnO-062O.js} +1 -1
  80. package/src/assets/web-panel/assets/{UkeySign-aPcZCdpz.js → UkeySign-HpNovvau.js} +1 -1
  81. package/src/assets/web-panel/assets/{VideoEditing-DAv5j--w.js → VideoEditing-yMncxH7y.js} +1 -1
  82. package/src/assets/web-panel/assets/{Wallet-DRhK7IDR.js → Wallet-Cug-t7Np.js} +4 -4
  83. package/src/assets/web-panel/assets/{WebAuthn-B2yQJuq8.js → WebAuthn--U1-NR-3.js} +2 -2
  84. package/src/assets/web-panel/assets/{WorkflowEditor-D98DhR54.js → WorkflowEditor-R-XvSJ3S.js} +1 -1
  85. package/src/assets/web-panel/assets/{chat-BXrRNi8C.js → chat-CCI1i6Rp.js} +1 -1
  86. package/src/assets/web-panel/assets/{colors-jQ94T_qn.js → colors-C-mqoaLt.js} +1 -1
  87. package/src/assets/web-panel/assets/{compact-item-KqUJLlF2.js → compact-item-De_N2iRR.js} +1 -1
  88. package/src/assets/web-panel/assets/{createContext-DxM3zuQW.js → createContext-DbTM6sjZ.js} +1 -1
  89. package/src/assets/web-panel/assets/devWarning-OcLPHSl5.js +1 -0
  90. package/src/assets/web-panel/assets/{hasIn-BGm946KV.js → hasIn-D0Adylyc.js} +1 -1
  91. package/src/assets/web-panel/assets/index-7E_mHkDU.js +1 -0
  92. package/src/assets/web-panel/assets/{index-Dib0FsTT.js → index-AY937BxS.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-C5NQai7O.js → index-B6hgy5VC.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-LWTZAud0.js → index-B7XnpLHk.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BXMZLfd8.js → index-B7gw-tDm.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DTUs9t5F.js → index-BBgDgkwW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Ce-YoL4x.js → index-BMmeiw88.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BaU_fSK7.js → index-BU6XtnSx.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BMp41Q1Y.js → index-Bi7VUG0R.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CbQvgx2e.js → index-BtE3cw9m.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DWcd5zDG.js → index-C9ej-MF0.js} +3 -3
  102. package/src/assets/web-panel/assets/{index-BbS0cBU9.js → index-CIEiQl1j.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D1VWL6Lc.js → index-CKgaW9E5.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DRzsaWQy.js → index-CY0KKR5f.js} +1 -1
  105. package/src/assets/web-panel/assets/index-CbV3-WlB.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DSzb9VOG.js → index-Co1UkcFX.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B-2HpfWo.js → index-CzP5d5LW.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DboUBf6R.js → index-D0NJ5lRi.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DZMzj4ay.js → index-D0gBsqTX.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CghRL1dh.js → index-D5F6shtn.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Cz_SFSix.js → index-DVtamtlm.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-T4UJiTi3.js → index-DWT18hDG.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CreXAr5Y.js → index-DaXb4B9P.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C7Z2m28C.js → index-DjQA0Vez.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Crw76vIF.js → index-Ds8NJVEL.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-mZNLT2SP.js → index-DuP-8HWu.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BnEZfi5D.js → index-DwLTiJ31.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-1P0sXNhG.js → index-T3VhH8cO.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B4b7KjRq.js → index-TDY1o0w3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DOYcemym.js → index-TRy2Gc3V.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BAPk6ntc.js → index-W1OMxCmI.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Dgzw6eKr.js → index-ftFRZbuc.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C7sBLech.js → index-iGPGaORA.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-FSYz5aww.js → index-jhy5KQvg.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DZpVYD4j.js → index-qUSZJcgw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-yavsyHif.js → index-w3f6lqkD.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CfE3jxrT.js → index-xdMc71Kh.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-aOLXuHpG.js → index-xhX5eg2Z.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BGjCjsf1.js → index-zkTiW6d6.js} +1 -1
  130. package/src/assets/web-panel/assets/{initDefaultProps-BDoZJI7g.js → initDefaultProps-CIHqouWk.js} +1 -1
  131. package/src/assets/web-panel/assets/{motion-sceLjgp-.js → motion-D1iUKftk.js} +1 -1
  132. package/src/assets/web-panel/assets/{move-CTWQpFa7.js → move-YOpWjmjL.js} +1 -1
  133. package/src/assets/web-panel/assets/{omit-dr-NCrHZ.js → omit-C_1357UY.js} +1 -1
  134. package/src/assets/web-panel/assets/{pickAttrs-DK-GX_Z2.js → pickAttrs-CDb4dq6o.js} +1 -1
  135. package/src/assets/web-panel/assets/{placementArrow-BIPf--18.js → placementArrow-D-OGYyhd.js} +1 -1
  136. package/src/assets/web-panel/assets/{responsiveObserve-BzrWT5tV.js → responsiveObserve-hS-j69A0.js} +1 -1
  137. package/src/assets/web-panel/assets/{slide-D5XhxlET.js → slide-tnjuvphU.js} +1 -1
  138. package/src/assets/web-panel/assets/{statusUtils-6DI-k-9E.js → statusUtils-BERfMMcU.js} +1 -1
  139. package/src/assets/web-panel/assets/{styleChecker-DMw-eyn9.js → styleChecker-DKpl2K9E.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFlexGapSupport-D4oVdliM.js → useFlexGapSupport-C_sX4D3N.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFs-D3PPZnvy.js → useFs-DBcxC1vK.js} +1 -1
  142. package/src/assets/web-panel/assets/{usePersonalDataHub-Gh5ba4KP.js → usePersonalDataHub-CeoDf2XI.js} +1 -1
  143. package/src/assets/web-panel/assets/{vnode-CVEM1u4x.js → vnode-DC9Pmj_u.js} +1 -1
  144. package/src/assets/web-panel/assets/{zoom-ClT_-9RZ.js → zoom-CZA58J7M.js} +1 -1
  145. package/src/assets/web-panel/index.html +1 -1
  146. package/src/commands/activitypub.js +19 -3
  147. package/src/commands/agent.js +25 -0
  148. package/src/commands/ask.js +13 -0
  149. package/src/commands/config.js +17 -1
  150. package/src/commands/cost.js +6 -1
  151. package/src/commands/dao.js +16 -3
  152. package/src/commands/dlp.js +7 -1
  153. package/src/commands/hub.js +13 -0
  154. package/src/commands/mtc.js +37 -1
  155. package/src/gateways/discord/discord-formatter.js +18 -1
  156. package/src/gateways/telegram/telegram-formatter.js +6 -3
  157. package/src/gateways/ws/ws-server.js +19 -1
  158. package/src/harness/plugin-manager.js +20 -0
  159. package/src/harness/worktree-isolator.js +17 -1
  160. package/src/lib/__tests__/model-deprecation.test.js +194 -0
  161. package/src/lib/a2a-protocol.js +23 -9
  162. package/src/lib/agent-core.js +2 -5
  163. package/src/lib/audit-mtc.js +40 -2
  164. package/src/lib/chat-core.js +141 -18
  165. package/src/lib/cli-anything-bridge.js +5 -0
  166. package/src/lib/cli-numeric.js +38 -0
  167. package/src/lib/config-manager.js +37 -6
  168. package/src/lib/cowork/ab-comparator-cli.js +6 -2
  169. package/src/lib/cowork-cron.js +45 -34
  170. package/src/lib/cowork-learning.js +31 -1
  171. package/src/lib/cowork-share.js +39 -3
  172. package/src/lib/cowork-template-marketplace.js +54 -2
  173. package/src/lib/cowork-workflow.js +33 -6
  174. package/src/lib/cross-chain-mtc.js +41 -12
  175. package/src/lib/crypto-manager.js +12 -2
  176. package/src/lib/dbevo.js +19 -8
  177. package/src/lib/downloader.js +24 -9
  178. package/src/lib/evomap-governance.js +4 -4
  179. package/src/lib/evomap-manager.js +54 -7
  180. package/src/lib/fatal-handler.js +48 -0
  181. package/src/lib/file-checkpoint.js +26 -0
  182. package/src/lib/git-integration.js +28 -0
  183. package/src/lib/goal-store.js +36 -10
  184. package/src/lib/hierarchical-memory.js +6 -0
  185. package/src/lib/llm-pricing.js +52 -2
  186. package/src/lib/mcp-oauth.js +37 -7
  187. package/src/lib/memory-manager.js +23 -2
  188. package/src/lib/model-deprecation.js +249 -0
  189. package/src/lib/pdh-feedback-ledger.js +131 -0
  190. package/src/lib/permanent-memory.js +30 -2
  191. package/src/lib/permission-rules.cjs +11 -3
  192. package/src/lib/personal-data-hub-aichat-wizard.js +33 -3
  193. package/src/lib/personal-data-hub-wiring.js +36 -13
  194. package/src/lib/project-instructions.js +10 -4
  195. package/src/lib/runnable-provider.js +62 -31
  196. package/src/lib/session-usage.js +34 -1
  197. package/src/lib/skill-loader.js +4 -1
  198. package/src/lib/skill-packs/generator.js +25 -2
  199. package/src/lib/stream-retry.js +56 -0
  200. package/src/lib/sync-credentials.js +53 -17
  201. package/src/lib/token-tracker.js +12 -2
  202. package/src/lib/with-file-lock.js +87 -0
  203. package/src/repl/agent-repl.js +39 -7
  204. package/src/repl/chat-repl.js +10 -0
  205. package/src/repl/session-cost.js +31 -3
  206. package/src/runtime/agent-core.js +474 -53
  207. package/src/runtime/coding-agent-contract-shared.cjs +58 -1
  208. package/src/runtime/coding-agent-policy.cjs +10 -0
  209. package/src/runtime/file-ref-expander.js +29 -1
  210. package/src/runtime/headless-runner.js +12 -1
  211. package/src/runtime/headless-stream.js +131 -5
  212. package/src/skills/video-editing/tools/commit.js +15 -1
  213. package/src/assets/web-panel/assets/PdhVaultBrowser-C8f5NbbI.js +0 -7
  214. package/src/assets/web-panel/assets/Projects-CXydUXp8.js +0 -1
  215. package/src/assets/web-panel/assets/devWarning-Bo1r5_zX.js +0 -1
  216. package/src/assets/web-panel/assets/index-BCU6dVQ-.js +0 -1
  217. package/src/assets/web-panel/assets/index-N_oOFphx.js +0 -1
@@ -47,17 +47,20 @@ export function toTelegramMarkdown(markdown) {
47
47
  // Escape special characters in text
48
48
  result = escapeMarkdownV2(result);
49
49
 
50
- // Restore code blocks and inline code (unescaped)
50
+ // Restore code blocks and inline code (unescaped). Use a replacement FUNCTION
51
+ // so `$`-sequences inside the code (e.g. `$&`, `$1`, `$$` in sed/regex/shell)
52
+ // are inserted literally — a string replacement would interpret them as
53
+ // String.replace patterns and corrupt the code.
51
54
  for (let i = codeBlocks.length - 1; i >= 0; i--) {
52
55
  result = result.replace(
53
56
  `__CODE_BLOCK_${i}__`.replace(ESCAPE_CHARS, "\\$1"),
54
- codeBlocks[i],
57
+ () => codeBlocks[i],
55
58
  );
56
59
  }
57
60
  for (let i = inlineCode.length - 1; i >= 0; i--) {
58
61
  result = result.replace(
59
62
  `__INLINE_CODE_${i}__`.replace(ESCAPE_CHARS, "\\$1"),
60
- inlineCode[i],
63
+ () => inlineCode[i],
61
64
  );
62
65
  }
63
66
 
@@ -12,6 +12,7 @@
12
12
 
13
13
  import { EventEmitter } from "node:events";
14
14
  import { spawn } from "node:child_process";
15
+ import { createHash, timingSafeEqual } from "node:crypto";
15
16
  import { fileURLToPath } from "node:url";
16
17
  import { dirname, join } from "node:path";
17
18
  import { WebSocketServer } from "ws";
@@ -525,10 +526,27 @@ export class ChainlessChainWSServer extends EventEmitter {
525
526
  return handleLlmChat(this, id, ws, message);
526
527
  }
527
528
 
529
+ /**
530
+ * Constant-time check of a client-supplied token against the configured one.
531
+ * A plain `===` leaks timing that a network client could use to recover the
532
+ * token byte-by-byte; hash both to a fixed length and compare in constant
533
+ * time. Preserves the original semantics: no configured token matches only an
534
+ * absent token (the auto-authenticated path).
535
+ * @private
536
+ */
537
+ _tokenMatches(provided) {
538
+ const expected = this.token;
539
+ if (expected == null) return provided == null;
540
+ if (typeof provided !== "string") return false;
541
+ const a = createHash("sha256").update(provided).digest();
542
+ const b = createHash("sha256").update(String(expected)).digest();
543
+ return timingSafeEqual(a, b);
544
+ }
545
+
528
546
  /** @private */
529
547
  _handleAuth(clientId, ws, message) {
530
548
  const { id, token } = message;
531
- const success = token === this.token;
549
+ const success = this._tokenMatches(token);
532
550
  const client = this.clients.get(clientId);
533
551
 
534
552
  if (success && client) {
@@ -352,10 +352,20 @@ export function installPluginSkills(db, pluginName, pluginPath, skills) {
352
352
  const installed = [];
353
353
 
354
354
  for (const skill of skills) {
355
+ if (!skill || typeof skill.name !== "string" || typeof skill.path !== "string") {
356
+ continue;
357
+ }
355
358
  const srcDir = path.resolve(pluginPath, skill.path);
359
+ // Reject manifest entries that escape the plugin (read) or the marketplace
360
+ // (arbitrary file WRITE) via `../` — a malicious plugin could otherwise drop
361
+ // files anywhere (e.g. overwrite ~/.bashrc) during install.
362
+ if (!_isWithin(pluginPath, srcDir)) continue;
356
363
  if (!fs.existsSync(srcDir)) continue;
357
364
 
358
365
  const destDir = path.join(marketplaceDir, skill.name);
366
+ if (!_isWithin(marketplaceDir, destDir) || path.resolve(destDir) === path.resolve(marketplaceDir)) {
367
+ continue;
368
+ }
359
369
  fs.mkdirSync(destDir, { recursive: true });
360
370
 
361
371
  // Copy skill files
@@ -419,6 +429,16 @@ export function getPluginSkills(db, pluginName) {
419
429
  /**
420
430
  * Recursively copy a directory
421
431
  */
432
+ /**
433
+ * True when `child` resolves to `parent` or a path inside it. Used to keep a
434
+ * (semi-trusted) plugin manifest from escaping its sandbox via `../` in
435
+ * skill.path / skill.name.
436
+ */
437
+ function _isWithin(parent, child) {
438
+ const rel = path.relative(path.resolve(parent), path.resolve(child));
439
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
440
+ }
441
+
422
442
  function _copyDirSync(src, dest) {
423
443
  fs.mkdirSync(dest, { recursive: true });
424
444
  const entries = fs.readdirSync(src, { withFileTypes: true });
@@ -10,11 +10,17 @@
10
10
  import { execSync } from "node:child_process";
11
11
  import { existsSync, rmSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
- import { isGitRepo, gitExec } from "../lib/git-integration.js";
13
+ import {
14
+ isGitRepo,
15
+ gitExec,
16
+ assertSafeGitRef,
17
+ } from "../lib/git-integration.js";
14
18
 
15
19
  const WORKTREE_DIR = ".worktrees";
16
20
 
17
21
  export function createWorktree(repoDir, branchName, baseBranch) {
22
+ assertSafeGitRef(branchName, "branch name");
23
+ if (baseBranch) assertSafeGitRef(baseBranch, "base branch");
18
24
  if (!isGitRepo(repoDir)) {
19
25
  throw new Error("Not a git repository");
20
26
  }
@@ -157,6 +163,8 @@ export function cleanupAgentWorktrees(repoDir) {
157
163
  }
158
164
 
159
165
  export function diffWorktree(repoDir, branchName, options = {}) {
166
+ assertSafeGitRef(branchName, "branch name");
167
+ if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
160
168
  const base = options.baseBranch || "HEAD";
161
169
  const filePath = options.filePath || null;
162
170
  const fileArg = filePath ? ` -- "${filePath}"` : "";
@@ -204,6 +212,8 @@ export function diffWorktree(repoDir, branchName, options = {}) {
204
212
  }
205
213
 
206
214
  export function mergeWorktree(repoDir, branchName, options = {}) {
215
+ assertSafeGitRef(branchName, "branch name");
216
+ if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
207
217
  const strategy = options.strategy || "merge";
208
218
  const deleteBranch = options.deleteBranch !== false;
209
219
 
@@ -285,6 +295,8 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
285
295
  }
286
296
 
287
297
  export function previewWorktreeMerge(repoDir, branchName, options = {}) {
298
+ assertSafeGitRef(branchName, "branch name");
299
+ if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
288
300
  const strategy = options.strategy || "merge";
289
301
  const baseBranch = _resolveBaseBranchForMerge(repoDir, options.baseBranch);
290
302
  const worktree = _findWorktreeByBranch(repoDir, branchName);
@@ -374,6 +386,8 @@ export function applyWorktreeAutomationCandidate(
374
386
  branchName,
375
387
  options = {},
376
388
  ) {
389
+ assertSafeGitRef(branchName, "branch name");
390
+ if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
377
391
  const filePath = options.filePath || null;
378
392
  const candidateId = options.candidateId || null;
379
393
  const conflictType = options.conflictType || null;
@@ -473,6 +487,8 @@ export function applyWorktreeAutomationCandidate(
473
487
  }
474
488
 
475
489
  export function worktreeLog(repoDir, branchName, baseBranch = "HEAD") {
490
+ assertSafeGitRef(branchName, "branch name");
491
+ assertSafeGitRef(baseBranch, "base branch");
476
492
  try {
477
493
  const output = gitExec(
478
494
  `log ${baseBranch}..${branchName} --pretty=format:"%h|%s|%ci"`,
@@ -0,0 +1,194 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import {
3
+ DEPRECATED_MODELS,
4
+ checkModelDeprecation,
5
+ formatModelDeprecationWarning,
6
+ maybeWarnDeprecatedModel,
7
+ _warned,
8
+ } from "../model-deprecation.js";
9
+
10
+ describe("checkModelDeprecation", () => {
11
+ it("flags an exact retired snapshot", () => {
12
+ const info = checkModelDeprecation("claude-3-opus-20240229");
13
+ expect(info).toBeTruthy();
14
+ expect(info.id).toBe("claude-3-opus-20240229");
15
+ expect(info.state).toBe("retired");
16
+ expect(info.replacement).toMatch(/claude-opus-4-8/);
17
+ });
18
+
19
+ it("flags retired Claude 3.5 / 3.7 families by prefix (the common ones)", () => {
20
+ expect(checkModelDeprecation("claude-3-5-sonnet-20240620")).toBeTruthy();
21
+ expect(checkModelDeprecation("claude-3-5-sonnet-20241022")).toBeTruthy();
22
+ expect(checkModelDeprecation("claude-3-5-haiku-20241022")).toBeTruthy();
23
+ expect(checkModelDeprecation("claude-3-7-sonnet-20250219")).toBeTruthy();
24
+ expect(checkModelDeprecation("claude-3-haiku-20240307")).toBeTruthy();
25
+ });
26
+
27
+ it("flags the retired ORIGINAL Claude 4 snapshots (exact dated id)", () => {
28
+ expect(checkModelDeprecation("claude-opus-4-20250514").state).toBe(
29
+ "retired",
30
+ );
31
+ expect(checkModelDeprecation("claude-sonnet-4-20250514").state).toBe(
32
+ "retired",
33
+ );
34
+ });
35
+
36
+ it("flags deprecated-but-live Claude Opus 4.1 with state=deprecated", () => {
37
+ const info = checkModelDeprecation("claude-opus-4-1-20250805");
38
+ expect(info).toBeTruthy();
39
+ expect(info.state).toBe("deprecated");
40
+ });
41
+
42
+ it("prefix-matches a retired family (claude-2 → claude-2.1)", () => {
43
+ expect(checkModelDeprecation("claude-2.1")).toBeTruthy();
44
+ expect(checkModelDeprecation("claude-2.0")).toBeTruthy();
45
+ expect(checkModelDeprecation("claude-instant-1.2")).toBeTruthy();
46
+ });
47
+
48
+ it("flags retired OpenAI snapshots", () => {
49
+ expect(checkModelDeprecation("gpt-4-0314")).toBeTruthy();
50
+ expect(checkModelDeprecation("gpt-4-32k-0613")).toBeTruthy();
51
+ expect(checkModelDeprecation("gpt-3.5-turbo-0301")).toBeTruthy();
52
+ expect(checkModelDeprecation("text-davinci-003")).toBeTruthy();
53
+ });
54
+
55
+ it("is case-insensitive and trims", () => {
56
+ expect(checkModelDeprecation(" GPT-4-0314 ")).toBeTruthy();
57
+ expect(checkModelDeprecation("Claude-2.1")).toBeTruthy();
58
+ });
59
+
60
+ it("CRITICAL: does NOT flag ACTIVE Claude 4 models (prefix-collision guard)", () => {
61
+ // These share a prefix with the retired 4.x snapshots — the entries use
62
+ // exact dated ids precisely so these stay silent.
63
+ expect(checkModelDeprecation("claude-opus-4-8")).toBeNull();
64
+ expect(checkModelDeprecation("claude-opus-4-7")).toBeNull();
65
+ expect(checkModelDeprecation("claude-opus-4-6")).toBeNull();
66
+ expect(checkModelDeprecation("claude-opus-4-5-20251101")).toBeNull();
67
+ expect(checkModelDeprecation("claude-sonnet-4-6")).toBeNull();
68
+ expect(checkModelDeprecation("claude-sonnet-4-5-20250929")).toBeNull();
69
+ expect(checkModelDeprecation("claude-haiku-4-5-20251001")).toBeNull();
70
+ expect(checkModelDeprecation("claude-haiku-4-5")).toBeNull();
71
+ });
72
+
73
+ it("does NOT flag other current GA / provider models", () => {
74
+ expect(checkModelDeprecation("gpt-4o")).toBeNull();
75
+ expect(checkModelDeprecation("gpt-4o-mini")).toBeNull();
76
+ expect(checkModelDeprecation("gpt-5")).toBeNull();
77
+ expect(checkModelDeprecation("gpt-4-turbo")).toBeNull();
78
+ // provider-specific ids (volcengine/doubao/ollama) never match
79
+ expect(checkModelDeprecation("doubao-seed-2-0-lite-260215")).toBeNull();
80
+ expect(checkModelDeprecation("qwen2.5-coder:7b")).toBeNull();
81
+ });
82
+
83
+ it("handles empty / non-string input", () => {
84
+ expect(checkModelDeprecation("")).toBeNull();
85
+ expect(checkModelDeprecation(null)).toBeNull();
86
+ expect(checkModelDeprecation(undefined)).toBeNull();
87
+ expect(checkModelDeprecation(42)).toBeNull();
88
+ });
89
+
90
+ it("every list entry is internally consistent and self-matches", () => {
91
+ for (const e of DEPRECATED_MODELS) {
92
+ expect(typeof e.id).toBe("string");
93
+ expect(e.id.length).toBeGreaterThan(0);
94
+ expect(["retired", "deprecated"]).toContain(e.state);
95
+ expect(typeof e.reason).toBe("string");
96
+ expect(typeof e.replacement).toBe("string");
97
+ expect(checkModelDeprecation(e.id)).toBeTruthy();
98
+ // the recommended replacement must itself never be flagged as retired
99
+ const firstReplacement = e.replacement.split(/\s+or\s+|\s+/)[0];
100
+ expect(checkModelDeprecation(firstReplacement)).toBeNull();
101
+ }
102
+ });
103
+ });
104
+
105
+ describe("formatModelDeprecationWarning", () => {
106
+ it("uses 'has been retired' for retired models", () => {
107
+ const info = checkModelDeprecation("claude-3-5-sonnet-20241022");
108
+ const line = formatModelDeprecationWarning(info);
109
+ expect(line).toMatch(/claude-3-5-sonnet-20241022/);
110
+ expect(line).toMatch(/has been retired/);
111
+ expect(line).toMatch(/claude-sonnet-4-6/);
112
+ expect(line).toMatch(/CC_MODEL_NOTICE=0/);
113
+ });
114
+
115
+ it("uses 'is deprecated' for deprecated-but-live models", () => {
116
+ const info = checkModelDeprecation("claude-opus-4-1-20250805");
117
+ const line = formatModelDeprecationWarning(info);
118
+ expect(line).toMatch(/is deprecated/);
119
+ expect(line).not.toMatch(/has been retired/);
120
+ });
121
+ });
122
+
123
+ describe("maybeWarnDeprecatedModel", () => {
124
+ let lines;
125
+ let print;
126
+ beforeEach(() => {
127
+ _warned.clear();
128
+ lines = [];
129
+ print = (l) => lines.push(l);
130
+ });
131
+
132
+ it("warns once for a deprecated model", () => {
133
+ const r = maybeWarnDeprecatedModel({ model: "claude-2.1", env: {}, print });
134
+ expect(r.warned).toBe(true);
135
+ expect(r.info.id).toBe("claude-2.1");
136
+ expect(lines).toHaveLength(1);
137
+ expect(lines[0]).toMatch(/claude-2\.1/);
138
+ });
139
+
140
+ it("warns for a retired Claude 3.5 Sonnet (the common stale id)", () => {
141
+ const r = maybeWarnDeprecatedModel({
142
+ model: "claude-3-5-sonnet-20241022",
143
+ env: {},
144
+ print,
145
+ });
146
+ expect(r.warned).toBe(true);
147
+ expect(lines[0]).toMatch(/has been retired/);
148
+ });
149
+
150
+ it("does not repeat the same model within a process", () => {
151
+ maybeWarnDeprecatedModel({ model: "gpt-4-0314", env: {}, print });
152
+ const r = maybeWarnDeprecatedModel({ model: "gpt-4-0314", env: {}, print });
153
+ expect(r.warned).toBe(false);
154
+ expect(r.info).toBeTruthy(); // still reports the match, just doesn't re-print
155
+ expect(lines).toHaveLength(1);
156
+ });
157
+
158
+ it("stays silent for a current model", () => {
159
+ const r = maybeWarnDeprecatedModel({
160
+ model: "claude-opus-4-8",
161
+ env: {},
162
+ print,
163
+ });
164
+ expect(r.warned).toBe(false);
165
+ expect(r.info).toBeNull();
166
+ expect(lines).toHaveLength(0);
167
+ });
168
+
169
+ it("honors CC_MODEL_NOTICE=0", () => {
170
+ const r = maybeWarnDeprecatedModel({
171
+ model: "claude-2.1",
172
+ env: { CC_MODEL_NOTICE: "0" },
173
+ print,
174
+ });
175
+ expect(r.warned).toBe(false);
176
+ expect(lines).toHaveLength(0);
177
+ });
178
+
179
+ it("fail-open on a throwing print", () => {
180
+ const r = maybeWarnDeprecatedModel({
181
+ model: "claude-2.1",
182
+ env: {},
183
+ print: () => {
184
+ throw new Error("boom");
185
+ },
186
+ });
187
+ expect(r.warned).toBe(false); // swallowed, no throw
188
+ });
189
+
190
+ it("no-ops on missing model", () => {
191
+ expect(maybeWarnDeprecatedModel({ env: {}, print }).warned).toBe(false);
192
+ expect(lines).toHaveLength(0);
193
+ });
194
+ });
@@ -5,6 +5,20 @@
5
5
  * capability negotiation, and peer discovery.
6
6
  */
7
7
 
8
+ // Safe JSON-array parse for values read back from the DB (capabilities / skills /
9
+ // history / artifacts are stored as JSON strings). Written via JSON.stringify so
10
+ // normally valid, but a corrupted or externally-edited DB must not crash agent
11
+ // discovery / task lifecycle with an uncaught SyntaxError — fall back to [].
12
+ function parseJsonArray(value) {
13
+ if (value == null || value === "") return [];
14
+ try {
15
+ const parsed = JSON.parse(value);
16
+ return Array.isArray(parsed) ? parsed : [];
17
+ } catch {
18
+ return [];
19
+ }
20
+ }
21
+
8
22
  // ─── Task statuses ───────────────────────────────────────────────
9
23
  export const TASK_STATUS = {
10
24
  SUBMITTED: "submitted",
@@ -165,8 +179,8 @@ export function discoverAgents(db, filter = {}) {
165
179
  // Parse JSON fields
166
180
  rows = rows.map((r) => ({
167
181
  ...r,
168
- capabilities: JSON.parse(r.capabilities || "[]"),
169
- skills: JSON.parse(r.skills || "[]"),
182
+ capabilities: parseJsonArray(r.capabilities),
183
+ skills: parseJsonArray(r.skills),
170
184
  }));
171
185
 
172
186
  if (filter.capability) {
@@ -220,7 +234,7 @@ export function completeTask(db, taskId, output, artifacts = []) {
220
234
 
221
235
  const now = nowISO();
222
236
  const task = _getTask(db, taskId);
223
- const history = JSON.parse(task.history || "[]");
237
+ const history = parseJsonArray(task.history);
224
238
  history.push({ status: TASK_STATUS.COMPLETED, timestamp: now });
225
239
 
226
240
  db.prepare(
@@ -249,7 +263,7 @@ export function failTask(db, taskId, error) {
249
263
 
250
264
  const now = nowISO();
251
265
  const task = _getTask(db, taskId);
252
- const history = JSON.parse(task.history || "[]");
266
+ const history = parseJsonArray(task.history);
253
267
  history.push({ status: TASK_STATUS.FAILED, timestamp: now });
254
268
 
255
269
  db.prepare(
@@ -276,8 +290,8 @@ export function getTaskStatus(db, taskId) {
276
290
  const task = _getTask(db, taskId);
277
291
  return {
278
292
  ...task,
279
- history: JSON.parse(task.history || "[]"),
280
- artifacts: JSON.parse(task.artifacts || "[]"),
293
+ history: parseJsonArray(task.history),
294
+ artifacts: parseJsonArray(task.artifacts),
281
295
  };
282
296
  }
283
297
 
@@ -305,7 +319,7 @@ export function negotiateCapability(db, agentId, requiredCapabilities) {
305
319
  .get(agentId);
306
320
  if (!card) throw new Error(`Agent not found: ${agentId}`);
307
321
 
308
- const agentCaps = JSON.parse(card.capabilities || "[]");
322
+ const agentCaps = parseJsonArray(card.capabilities);
309
323
  const supported = requiredCapabilities.filter((c) => agentCaps.includes(c));
310
324
  const missing = requiredCapabilities.filter((c) => !agentCaps.includes(c));
311
325
 
@@ -329,8 +343,8 @@ export function listPeers(db) {
329
343
  .all();
330
344
  return rows.map((r) => ({
331
345
  ...r,
332
- capabilities: JSON.parse(r.capabilities || "[]"),
333
- skills: JSON.parse(r.skills || "[]"),
346
+ capabilities: parseJsonArray(r.capabilities),
347
+ skills: parseJsonArray(r.skills),
334
348
  }));
335
349
  }
336
350
 
@@ -1,8 +1,4 @@
1
- /**
2
- * @deprecated Re-export shim; canonical impl is `../runtime/agent-core.js`
3
- * (CLI Runtime Convergence, Phase 6b). Import from there in new code.
4
- */
5
-
1
+ /** @deprecated Thin re-export shim; canonical impl is `../runtime/agent-core.js` (CLI Runtime Convergence, Phase 6b) — import from there in new code. */
6
2
  export {
7
3
  AGENT_TOOLS,
8
4
  AGENT_TOOL_REGISTRY,
@@ -33,6 +29,7 @@ export {
33
29
  _streamErrorDisposition,
34
30
  _isRetryableStreamError,
35
31
  _retryStreamingChat,
32
+ _iterateStreamWithStall,
36
33
  _toAnthropicMessages,
37
34
  _anthropicThinkingParams,
38
35
  _normalizeAnthropicResponse,
@@ -22,6 +22,7 @@ import path from "node:path";
22
22
  import crypto from "node:crypto";
23
23
  import { ed25519 as nobleEd25519 } from "@noble/curves/ed25519.js";
24
24
  import mtcLib from "@chainlesschain/core-mtc";
25
+ import { withFileLock } from "./with-file-lock.js";
25
26
 
26
27
  const { sha256, jcs, encodeHashStr, ed25519, assembleBatch } = mtcLib;
27
28
 
@@ -70,6 +71,30 @@ function ensureDirs(dir) {
70
71
  }
71
72
  }
72
73
 
74
+ /**
75
+ * Atomically write a UTF-8 file (temp sibling + rename). Used for the config
76
+ * and per-event staging records: a crash mid-write would truncate config.json
77
+ * or leave a partial event that closeBatch silently drops as malformed. Temp
78
+ * names end in `.tmp` (never `.json`), so a hard-crash leftover is ignored by
79
+ * listStagingEvents' `.json` filter. NOTE: the issuer key (flag "wx" exclusive
80
+ * create) and closeBatch (whole tmp-dir rename) have their own atomicity and
81
+ * are intentionally left untouched.
82
+ */
83
+ function atomicWriteFileSync(filePath, data) {
84
+ const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
85
+ try {
86
+ fs.writeFileSync(tmp, data, "utf-8");
87
+ fs.renameSync(tmp, filePath);
88
+ } catch (err) {
89
+ try {
90
+ if (fs.existsSync(tmp)) fs.unlinkSync(tmp);
91
+ } catch {
92
+ /* best-effort temp cleanup */
93
+ }
94
+ throw err;
95
+ }
96
+ }
97
+
73
98
  // ─────────────────────────────────────────────────────────────────────
74
99
  // Config
75
100
  // ─────────────────────────────────────────────────────────────────────
@@ -107,7 +132,7 @@ export function saveAuditMtcConfig(dir, patch) {
107
132
  if (typeof merged.issuer !== "string" || !merged.issuer) {
108
133
  throw new TypeError("config.issuer must be non-empty string");
109
134
  }
110
- fs.writeFileSync(configPath(dir), JSON.stringify(merged, null, 2), "utf-8");
135
+ atomicWriteFileSync(configPath(dir), JSON.stringify(merged, null, 2));
111
136
  return merged;
112
137
  }
113
138
 
@@ -227,7 +252,7 @@ export function emitEvent(dir, body, opts = {}) {
227
252
  };
228
253
 
229
254
  const filePath = path.join(stagingDir(dir), `${eventId}.json`);
230
- fs.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
255
+ atomicWriteFileSync(filePath, JSON.stringify(record, null, 2));
231
256
  return { eventId, path: filePath, record };
232
257
  }
233
258
 
@@ -293,6 +318,19 @@ function listStagingEvents(dir) {
293
318
  * @param {string} [opts.issuer] - override config.issuer
294
319
  */
295
320
  export function closeBatch(dir, opts = {}) {
321
+ // Serialize the whole staging-scan → batch-write → staging-cleanup section
322
+ // across processes. Without it, two concurrent closes (e.g. overlapping
323
+ // `cc audit mtc reconcile` runs) both scan the same staging events, allocate
324
+ // the same batch id, and the defensive rmSync(finalDir) lets the second
325
+ // silently overwrite the first's audit batch. Best-effort lock (never hangs);
326
+ // generous timeout since the section includes Merkle assembly + signing.
327
+ ensureDirs(dir);
328
+ return withFileLock(batchesDir(dir), () => _closeBatchImpl(dir, opts), {
329
+ timeoutMs: 15000,
330
+ });
331
+ }
332
+
333
+ function _closeBatchImpl(dir, opts = {}) {
296
334
  ensureDirs(dir);
297
335
  const cfg = loadAuditMtcConfig(dir);
298
336
  const events = listStagingEvents(dir);