chainlesschain 0.162.177 → 0.162.178

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 (207) hide show
  1. package/README.md +10 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DT-VPWuN.js → AIOps-DRftcSue.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DyOreOVN.js → ActionButton-BFvUaMKR.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DslNsKux.js → Analytics-C0-Rru4P.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-bx-BCljj.js +9 -0
  8. package/src/assets/web-panel/assets/{Artifacts-DoaAtEcY.js → Artifacts-DAJ8B3Un.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DPG3WdJ9.js → Audit-hOk4L1lr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-C_JO6R5G.js → BackgroundAgents-3pbDpM9z.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Cv3BooyX.js → Backup-BtziGDn9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-D2uQXWu-.js → BaseInput-kmshphJ3.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-B4Vczyf3.js → Chat-d7tGSosA.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-p-_PBpX_.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-qTgk4VeP.js → Checkbox-C4Eh4Tz5.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-DlLWcIzj.js → Codegen-r7gmhZyA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DNO73qp7.js → Col-U7mDaGlL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-vRDNpc4j.js → Community-Bpu8aH1T.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-CEWtIXni.js → Compact-CHb2XBJe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-KgYC9Fxi.js → Compliance-Cn_r4dey.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-zj5wvkpT.js → Cowork-CdMWfDcz.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-BLDoCozU.js → Cron-BtnREHWy.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BYnff4VY.js → Crosschain-BKI9TSzr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-C75VFsVm.js → DID-pkNubzzJ.js} +2 -2
  25. package/src/assets/web-panel/assets/Dashboard-1rrz1snN.js +3 -0
  26. package/src/assets/web-panel/assets/{Dropdown-B5dbO4Kx.js → Dropdown-DLyFVxDc.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BDipZtHJ.js → EmailListRenderer-CtUWfYVq.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DmIzt8jj.js → FamilyGuardDashboard-B8eX2Qe4.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-D9QOS-uW.js → Federation-D8lsDGus.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-B47HRwAm.js → FormItemContext-CRYt_aIw.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-3R4mXmgV.js → GenericCardRenderer-ClKzY8_N.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DrNXZf1C.js → Git-BXj5Dney.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BGd7JR98.js → Governance-Cm-I8RMz.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-D-2he1ed.js → Inference-CPvbhvgt.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-C0sHIbK5.js → KnowledgeGraph-sYNDZaoi.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DiBQO-jL.js → Logs-BZY_3CRZ.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-DL_5eV2D.js → MarkdownRenderer-up0BW6pv.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-VR59OdrC.js → Marketplace-C5MEJ2Qe.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DrPzwSd3.js → McpTools-ihdrn43I.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-CBgKALwQ.js → Memory-Br8bPMlh.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-pQ9Tn-Mn.js → MobileBridge-BNntgrYW.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-mASB6jyj.js → MobileProjects-BdV915mw.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-DTMgE7HC.js → Mtc-A3wDmlqL.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-CwTnCtiL.js → MtcAudit-CF-mBLWM.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-ngm6WU8f.js → Multisig-Iy2k_MSo.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-4YO4Qww9.js → NLProgramming-kk0W7HUb.js} +1 -1
  47. package/src/assets/web-panel/assets/Notes-B5u5t_4S.js +7 -0
  48. package/src/assets/web-panel/assets/{NotificationSettings-r2H8yPKb.js → NotificationSettings-BN0POf6_.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-DrO7jRWS.js → Organization-DwWQESuN.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-Cnwu4i4t.js → Overflow-DG6klrcC.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-zi-cDwA_.js → P2P-BwJcKkgZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-DBEryDv7.js → PdhVaultBrowser-BwOHG0in.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-lqXK2C3x.js → Permissions-CGMXe4TX.js} +4 -4
  55. package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +8 -0
  56. package/src/assets/web-panel/assets/{Pipeline-Kep5KAGW.js → Pipeline-BzJfeoGt.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-CHdvOAh1.js → Privacy-DLh3Bblx.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-BhKfdwe9.js → ProjectInit-USVWW36c.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Yavk9K8A.js → ProjectSettings-CiiOn62h.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-Bfym6SQn.js → Providers-CBPWWzpu.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-CLSQcrlR.js → QrScannerModal-QVwoRUq3.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-VPa2WKQN.js → QuickAsk-CXnR-2Vn.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-CGXR8jVU.js → Recommend-ClaNzjmF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-C_20YfMB.js → RemoteSession-C3rGw8vJ.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-awZh68UV.js → Reputation-Ci-OWAoP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Ctb4oiYN.js → Row-DRhAwXOS.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-D3sdjvUV.js → RssFeed-BQLJQ2_y.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-C-zfA4kT.js → Search-QQUpkfFd.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-D9XlmHA3.js → Security-DdXKzuKT.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-C3UKnr41.js → Services-x2Zh62Qd.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-BQxagZfF.js → Skeleton-GdY4KwRz.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-Bv4tY1Fd.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-DveENvek.js → Sla-C3QnM-11.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BFjXLNgF.js → SpeechSettings-Bl3ytvmI.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-BpHgbaP9.js → SyncSettings-Wp9IwTsP.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-CF3rIrDe.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-DEiaoyve.js → Templates-CGfvVVM9.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-ITg774nY.js → Tenant-CVXmwQ3U.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-D3ZBcvnb.js → Terminal-bvwrt03E.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BsLU6JjU.js → TimelineRenderer-D0g74KhH.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-Cb5Pquc9.js → Tokens-BH4IwzlT.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-Dwfy-m9N.js → Trigger-DJOpM533.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-CnlSRGG0.js → Trust-B43EPRLr.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-Y4TlvKVo.js → UkeySign-BmBFG-em.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-BpKLtTTO.js → VideoEditing-n9oJwB8k.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-LLBzIq8x.js → Wallet-D5QeivHp.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-BeSRiusP.js → WebAuthn-DcNy7igO.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-BK_l1tXO.js → WorkflowEditor-BVjc7-5-.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-DYd-X7Wg.js → chat-Dy5Tfj3m.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-C9uip71j.js → colors-C4c2CNZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-JBrfjUP6.js → compact-item-DPa3O5vN.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-D03fbuiq.js → createContext-TLcfNA6_.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DLQOR6l-.js → hasIn-D41LVFkp.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-rIxxfdTN.js → index-0oevYjfT.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CChaISMb.js → index-388pLGJ1.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BfOs_bmg.js → index-B8nLBZbv.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-D-1SqEpQ.js → index-BPTGAjee.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BYv-LWdy.js → index-BPtrOBmW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cpn19c9k.js → index-BQiIxHBy.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BpYKLrUT.js → index-BSM1cHVK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CS63r8h3.js → index-BYPr9fkz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Q7mmL6w4.js → index-BsQ7Ev6i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-SO6lgMg0.js → index-Btk9qLL2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-iahNhZwm.js → index-BvmziZSG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BnQgRjsm.js → index-BvopLC_T.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Ba-Ps_AJ.js → index-C7xhTVyP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BuNFCZCx.js → index-CNk_ZooP.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CL0ixCxk.js → index-CQrEHoOG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CChQBM1d.js → index-CSxex23J.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-5YN7F5bO.js → index-CVjgm992.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CwoDpKzW.js → index-CWK4zOPe.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BcBZwlnG.js → index-CX0VsoP4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B46YcgsV.js → index-CXl0YPNG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-l08afIbV.js → index-Cip7LWml.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-6GuLRpBQ.js → index-Cq_hlWNt.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DYLZY4uh.js → index-CzZ49YZL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B0X76LjX.js → index-D2Nkivlm.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BdH9l8wl.js → index-DBUgRs4D.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DEjVkdzq.js +1 -0
  122. package/src/assets/web-panel/assets/{index-D4UY5Z60.js → index-DNsBj2Ob.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Ce0Bf1YF.js → index-Di4X8cfI.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-ByHO3SqZ.js → index-DljFsVGI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C2zmOsyD.js → index-Korc3QIe.js} +1 -1
  126. package/src/assets/web-panel/assets/index-LMOc5xrS.js +1 -0
  127. package/src/assets/web-panel/assets/{index-BNCdRmWf.js → index-MKZDje0H.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-D8KvuDz0.js → index-OHAfRu_3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DaokIHt3.js → index-WEJr8PL5.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CILVyK3b.js → index-WinCp6du.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CVZWiUE_.js → index-Yjr6cqSK.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CqWk1FLG.js → index-iQ6gtFih.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DNMAAUVk.js → index-pt3s7yvV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Ck17i0OD.js → index-tQ3igERQ.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-CG9z1c6w.js → initDefaultProps-BFZu87WZ.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-B1Ky7Zff.js → motion-DJIo7qvt.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DME8X2P1.js → move-Cfr5qknL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-cuJYSWB7.js → mtc-parser-Bgc-OadH.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-UI_EE0uZ.js → omit-X3WI-TNp.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-BIoE_Z_V.js → pickAttrs-pJhU9IxK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B48-9HKN.js → placementArrow-BNg_Vj3T.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-D7x_GDgD.js → responsiveObserve-TeSEGQ5-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-DjzC5_7m.js → slide-D_t_P0bv.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C6BIymhh.js → statusUtils-DrrIMwAT.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-B4dGrdD9.js → styleChecker-BjZCuFRm.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-Bken4aNE.js → useFlexGapSupport-rTFdlqdQ.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-BMlqECqv.js → useFs-BidCpfLX.js} +1 -1
  148. package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +1 -0
  149. package/src/assets/web-panel/assets/{vnode-CKJQmxa0.js → vnode-BrbcRiHZ.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-CaZunuau.js → zoom-PISX7AkR.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/__tests__/hub-wechat.test.js +45 -0
  153. package/src/commands/background-session.js +5 -2
  154. package/src/commands/context.js +33 -1
  155. package/src/commands/hub.js +473 -39
  156. package/src/commands/init.js +212 -110
  157. package/src/commands/llm.js +22 -6
  158. package/src/commands/skill.js +11 -1
  159. package/src/data/changelog.json +76 -270
  160. package/src/gateways/ws/personal-data-hub-protocol.js +22 -2
  161. package/src/harness/golden-transcript.js +3 -0
  162. package/src/index.js +18 -7
  163. package/src/lib/approval-authority-store.js +891 -0
  164. package/src/lib/background-interaction-resolver.js +399 -54
  165. package/src/lib/background-session-transport.js +3 -1
  166. package/src/lib/context-breakdown.js +82 -3
  167. package/src/lib/event-runtime-host.js +225 -0
  168. package/src/lib/event-runtime-store.js +102 -21
  169. package/src/lib/event-runtime-worker.js +70 -2
  170. package/src/lib/hooks-v2-producers.js +33 -8
  171. package/src/lib/hooks-v2-runtime.js +360 -40
  172. package/src/lib/interaction-adapter.js +66 -0
  173. package/src/lib/operation-fingerprint.js +107 -2
  174. package/src/lib/pdh-dedicated-batch-collectors.js +124 -0
  175. package/src/lib/personal-data-hub-wiring.js +327 -52
  176. package/src/lib/process-execution-broker/credential-agent.js +954 -111
  177. package/src/lib/process-execution-broker/index.js +369 -85
  178. package/src/lib/process-execution-broker/platform-sandbox.js +320 -140
  179. package/src/lib/provider-http-error.js +94 -0
  180. package/src/lib/remote-approval-bridge.js +222 -26
  181. package/src/lib/side-effect-ledger-store.js +65 -14
  182. package/src/lib/skill-loader.js +215 -12
  183. package/src/lib/skill-process-broker.js +15 -1
  184. package/src/lib/sub-agent-context.js +42 -0
  185. package/src/lib/turn-binding-store.js +64 -15
  186. package/src/lib/turn-binding.js +85 -16
  187. package/src/repl/agent-repl.js +40 -6
  188. package/src/repl/remote-approval.js +13 -13
  189. package/src/repl/repl-turn-binding.js +46 -16
  190. package/src/runtime/agent-core.js +286 -35
  191. package/src/runtime/coding-agent-contract-shared.cjs +16 -0
  192. package/src/runtime/headless-runner.js +147 -34
  193. package/src/runtime/headless-stream.js +147 -7
  194. package/src/workers/background-agent-worker.js +70 -38
  195. package/src/assets/web-panel/assets/AppLayout-BNJJAYME.js +0 -9
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +0 -1
  197. package/src/assets/web-panel/assets/Dashboard-C4Gutni2.js +0 -3
  198. package/src/assets/web-panel/assets/Notes-DP1k45mF.js +0 -7
  199. package/src/assets/web-panel/assets/OrderTableRenderer-CoM_Zw0J.js +0 -1
  200. package/src/assets/web-panel/assets/PersonalDataHub-b63T3HJM.js +0 -9
  201. package/src/assets/web-panel/assets/Projects-CN8E_5Mq.js +0 -1
  202. package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +0 -1
  203. package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +0 -1
  204. package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +0 -1
  205. package/src/assets/web-panel/assets/index-Bh8BPtqR.js +0 -1
  206. package/src/assets/web-panel/assets/index-CeVrHLzZ.js +0 -1
  207. package/src/assets/web-panel/assets/usePersonalDataHub-DLdrfmMa.js +0 -1
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Process-level lifecycle owner for the durable Event Runtime.
3
+ *
4
+ * Producers only append records. This host owns the single non-overlapping
5
+ * drain loop, dispatches records to registered handlers, and exposes a
6
+ * stoppable lifecycle shared by short CLI commands and resident daemons.
7
+ */
8
+
9
+ import { EventEmitter } from "node:events";
10
+ import { EventRuntimeStore } from "./event-runtime-store.js";
11
+ import { EventRuntimeWorker } from "./event-runtime-worker.js";
12
+
13
+ function routeType(event) {
14
+ return String(
15
+ event?.runtime_type ||
16
+ event?.runtimeType ||
17
+ event?.event ||
18
+ event?.type ||
19
+ "*",
20
+ );
21
+ }
22
+
23
+ function routeOrigin(event) {
24
+ return String(event?.origin || "*");
25
+ }
26
+
27
+ function routeKey(queue, type = "*", origin = "*") {
28
+ return `${queue}:${String(type || "*")}:${String(origin || "*")}`;
29
+ }
30
+
31
+ export class EventRuntimeHost extends EventEmitter {
32
+ constructor({
33
+ store = new EventRuntimeStore(),
34
+ intervalMs = 1000,
35
+ worker = null,
36
+ setTimeoutFn = setTimeout,
37
+ clearTimeoutFn = clearTimeout,
38
+ } = {}) {
39
+ super();
40
+ this.store = store;
41
+ this.intervalMs = Math.max(1, Number(intervalMs) || 1000);
42
+ this.setTimeoutFn = setTimeoutFn;
43
+ this.clearTimeoutFn = clearTimeoutFn;
44
+ this.handlers = new Map();
45
+ this.running = false;
46
+ this.timer = null;
47
+ this.inFlight = null;
48
+ this.lastStats = null;
49
+ this.lastError = null;
50
+ this.worker =
51
+ worker ||
52
+ new EventRuntimeWorker({
53
+ store,
54
+ onInbox: (event, record) =>
55
+ this._dispatch("inbox", event, record),
56
+ onOutbox: (event, record) =>
57
+ this._dispatch("outbox", event, record),
58
+ });
59
+ }
60
+
61
+ registerHandler(
62
+ handler,
63
+ { queue = "inbox", type = "*", origin = "*" } = {},
64
+ ) {
65
+ if (typeof handler !== "function") {
66
+ throw new TypeError("Event Runtime handler must be a function");
67
+ }
68
+ if (queue !== "inbox" && queue !== "outbox") {
69
+ throw new Error(`unsupported event queue: ${queue}`);
70
+ }
71
+ const key = routeKey(queue, type, origin);
72
+ const handlers = this.handlers.get(key) || new Set();
73
+ handlers.add(handler);
74
+ this.handlers.set(key, handlers);
75
+ return () => {
76
+ handlers.delete(handler);
77
+ if (handlers.size === 0) this.handlers.delete(key);
78
+ };
79
+ }
80
+
81
+ async _dispatch(queue, event, record) {
82
+ const type = routeType(event);
83
+ const origin = routeOrigin(event);
84
+ const candidates = [
85
+ routeKey(queue, type, origin),
86
+ routeKey(queue, type, "*"),
87
+ routeKey(queue, "*", origin),
88
+ routeKey(queue, "*", "*"),
89
+ ];
90
+ const handlers = [];
91
+ const seen = new Set();
92
+ for (const key of candidates) {
93
+ for (const handler of this.handlers.get(key) || []) {
94
+ if (seen.has(handler)) continue;
95
+ seen.add(handler);
96
+ handlers.push(handler);
97
+ }
98
+ }
99
+ if (handlers.length === 0 && event?.requiresHandler === true) {
100
+ const error = new Error(
101
+ `No Event Runtime handler for ${queue}:${type}:${origin}`,
102
+ );
103
+ error.code = "CC_EVENT_RUNTIME_HANDLER_UNAVAILABLE";
104
+ throw error;
105
+ }
106
+ const results = await Promise.all(
107
+ handlers.map((handler) => handler(event, record)),
108
+ );
109
+ const delivery = {
110
+ queue,
111
+ type,
112
+ origin,
113
+ handlerCount: handlers.length,
114
+ results,
115
+ };
116
+ this.emit("delivered", delivery, event, record);
117
+ this.emit(`${queue}:${type}`, event, record, delivery);
118
+ return delivery;
119
+ }
120
+
121
+ async runOnce() {
122
+ if (this.inFlight) return this.inFlight;
123
+ this.inFlight = Promise.resolve()
124
+ .then(() => this.worker.runOnce())
125
+ .then((stats) => {
126
+ this.lastStats = stats;
127
+ this.lastError = null;
128
+ this.emit("tick", stats);
129
+ return stats;
130
+ })
131
+ .catch((error) => {
132
+ this.lastError = error;
133
+ this.emit("runtime-error", error);
134
+ throw error;
135
+ })
136
+ .finally(() => {
137
+ this.inFlight = null;
138
+ });
139
+ return this.inFlight;
140
+ }
141
+
142
+ _schedule() {
143
+ if (!this.running || this.timer != null) return;
144
+ this.timer = this.setTimeoutFn(async () => {
145
+ this.timer = null;
146
+ try {
147
+ await this.runOnce();
148
+ } catch {
149
+ // The durable records remain pending/retriable. The host keeps running
150
+ // so a transient lock or filesystem failure does not kill the daemon.
151
+ } finally {
152
+ this._schedule();
153
+ }
154
+ }, this.intervalMs);
155
+ this.timer?.unref?.();
156
+ }
157
+
158
+ start({ immediate = true } = {}) {
159
+ if (this.running) return false;
160
+ this.running = true;
161
+ if (immediate) {
162
+ void this.runOnce()
163
+ .catch(() => {})
164
+ .finally(() => this._schedule());
165
+ } else {
166
+ this._schedule();
167
+ }
168
+ this.emit("started");
169
+ return true;
170
+ }
171
+
172
+ async stop({ drain = true } = {}) {
173
+ const wasRunning = this.running;
174
+ this.running = false;
175
+ if (this.timer != null) {
176
+ this.clearTimeoutFn(this.timer);
177
+ this.timer = null;
178
+ }
179
+ if (drain && this.inFlight) {
180
+ try {
181
+ await this.inFlight;
182
+ } catch {
183
+ // Error already exposed through runtime-error/lastError.
184
+ }
185
+ }
186
+ if (wasRunning) this.emit("stopped");
187
+ return wasRunning;
188
+ }
189
+
190
+ status() {
191
+ return {
192
+ running: this.running,
193
+ inFlight: this.inFlight != null,
194
+ handlerRoutes: this.handlers.size,
195
+ lastStats: this.lastStats,
196
+ lastError: this.lastError?.message || null,
197
+ health: this.store.getHealthSnapshot(),
198
+ };
199
+ }
200
+ }
201
+
202
+ let defaultHost = null;
203
+
204
+ export function getDefaultEventRuntimeHost(options = {}) {
205
+ if (!defaultHost) defaultHost = new EventRuntimeHost(options);
206
+ return defaultHost;
207
+ }
208
+
209
+ export function startDefaultEventRuntimeHost(options = {}) {
210
+ if (process.env.CC_EVENT_RUNTIME_DURABLE !== "1" && options.force !== true) {
211
+ return null;
212
+ }
213
+ const host = getDefaultEventRuntimeHost(options);
214
+ host.start({ immediate: options.immediate !== false });
215
+ return host;
216
+ }
217
+
218
+ export async function stopDefaultEventRuntimeHost(options) {
219
+ return defaultHost ? defaultHost.stop(options) : false;
220
+ }
221
+
222
+ export function _resetDefaultEventRuntimeHostForTests() {
223
+ defaultHost = null;
224
+ }
225
+
@@ -100,7 +100,17 @@ export class EventRuntimeStore {
100
100
  );
101
101
  }
102
102
 
103
- enqueue(queue, event, { id = null, metadata = null } = {}) {
103
+ enqueue(
104
+ queue,
105
+ event,
106
+ {
107
+ id = null,
108
+ metadata = null,
109
+ claimOwner = null,
110
+ leaseMs = this.leaseMs,
111
+ availableAt = null,
112
+ } = {},
113
+ ) {
104
114
  const key = eventId(event, id);
105
115
  return this._mutate(queue, (records) => {
106
116
  const existing = records.find((record) => record.id === key);
@@ -118,15 +128,27 @@ export class EventRuntimeStore {
118
128
  error.code = "CC_EVENT_RUNTIME_BACKPRESSURE";
119
129
  throw error;
120
130
  }
131
+ const createdAt = this._now();
132
+ const claimed = claimOwner != null && String(claimOwner);
121
133
  const record = {
122
134
  id: key,
123
135
  event: clone(event || {}),
124
136
  metadata: clone(metadata),
125
- status: "pending",
126
- attempts: 0,
127
- createdAt: this._now(),
128
- nextAttemptAt: this._now(),
129
- lease: null,
137
+ status: claimed ? "processing" : "pending",
138
+ attempts: claimed ? 1 : 0,
139
+ fence: claimed ? 1 : 0,
140
+ createdAt,
141
+ nextAttemptAt:
142
+ availableAt == null ? createdAt : Math.max(createdAt, Number(availableAt)),
143
+ lease: claimed
144
+ ? {
145
+ owner: String(claimOwner),
146
+ fence: 1,
147
+ claimedAt: createdAt,
148
+ expiresAt:
149
+ createdAt + Math.max(1, Number(leaseMs) || this.leaseMs),
150
+ }
151
+ : null,
130
152
  };
131
153
  records.push(record);
132
154
  return clone(record);
@@ -160,7 +182,13 @@ export class EventRuntimeStore {
160
182
  if (Number(record.nextAttemptAt || 0) > t) continue;
161
183
  record.status = "processing";
162
184
  record.attempts = Number(record.attempts || 0) + 1;
163
- record.lease = { owner: this.owner, claimedAt: t, expiresAt: t + ttl };
185
+ record.fence = Number(record.fence || 0) + 1;
186
+ record.lease = {
187
+ owner: this.owner,
188
+ fence: record.fence,
189
+ claimedAt: t,
190
+ expiresAt: t + ttl,
191
+ };
164
192
  claimed.push(clone(record));
165
193
  }
166
194
  return claimed;
@@ -175,16 +203,68 @@ export class EventRuntimeStore {
175
203
  return this.claim("outbox", options);
176
204
  }
177
205
 
178
- acknowledge(queue, id, result = null, { owner = null } = {}) {
206
+ _leaseMatches(record, { owner = null, fence = null, now = this._now() } = {}) {
207
+ const expectedOwner = owner == null ? null : String(owner);
208
+ const expectedFence =
209
+ fence == null || !Number.isFinite(Number(fence)) ? null : Number(fence);
210
+ if (expectedOwner == null && expectedFence == null) return true;
211
+ if (record.status !== "processing" || !record.lease) return false;
212
+ if (expectedOwner != null && record.lease.owner !== expectedOwner) {
213
+ return false;
214
+ }
215
+ if (
216
+ expectedFence != null &&
217
+ Number(record.lease.fence) !== expectedFence
218
+ ) {
219
+ return false;
220
+ }
221
+ // A holder that let its lease expire must renew before it is allowed to
222
+ // commit. This closes the gap where a slow worker could settle just before
223
+ // a recovery worker claims the same record.
224
+ return Number(record.lease.expiresAt) > Number(now);
225
+ }
226
+
227
+ renew(
228
+ queue,
229
+ id,
230
+ {
231
+ owner = this.owner,
232
+ fence = null,
233
+ now = this._now(),
234
+ leaseMs = this.leaseMs,
235
+ } = {},
236
+ ) {
237
+ const t = Number(now);
238
+ const ttl = Math.max(1, Number(leaseMs) || this.leaseMs);
239
+ return this._mutate(queue, (records) => {
240
+ const record = records.find((item) => item.id === String(id));
241
+ if (!record || !this._leaseMatches(record, { owner, fence, now: t })) {
242
+ return null;
243
+ }
244
+ record.lease.expiresAt = t + ttl;
245
+ record.lease.renewedAt = t;
246
+ return clone(record);
247
+ });
248
+ }
249
+
250
+ renewInbox(id, options) {
251
+ return this.renew("inbox", id, options);
252
+ }
253
+
254
+ renewOutbox(id, options) {
255
+ return this.renew("outbox", id, options);
256
+ }
257
+
258
+ acknowledge(
259
+ queue,
260
+ id,
261
+ result = null,
262
+ { owner = null, fence = null, now = this._now() } = {},
263
+ ) {
179
264
  return this._mutate(queue, (records) => {
180
265
  const record = records.find((item) => item.id === String(id));
181
266
  if (!record) return null;
182
- const expectedOwner = owner ? String(owner) : null;
183
- if (
184
- expectedOwner &&
185
- (record.status !== "processing" ||
186
- record.lease?.owner !== expectedOwner)
187
- ) {
267
+ if (!this._leaseMatches(record, { owner, fence, now })) {
188
268
  return null;
189
269
  }
190
270
  record.status = "done";
@@ -207,17 +287,18 @@ export class EventRuntimeStore {
207
287
  queue,
208
288
  id,
209
289
  error,
210
- { retryDelayMs = 1000, maxAttempts = this.maxAttempts, owner = null } = {},
290
+ {
291
+ retryDelayMs = 1000,
292
+ maxAttempts = this.maxAttempts,
293
+ owner = null,
294
+ fence = null,
295
+ now = this._now(),
296
+ } = {},
211
297
  ) {
212
298
  return this._mutate(queue, (records) => {
213
299
  const record = records.find((item) => item.id === String(id));
214
300
  if (!record) return null;
215
- const expectedOwner = owner ? String(owner) : null;
216
- if (
217
- expectedOwner &&
218
- (record.status !== "processing" ||
219
- record.lease?.owner !== expectedOwner)
220
- ) {
301
+ if (!this._leaseMatches(record, { owner, fence, now })) {
221
302
  return null;
222
303
  }
223
304
  record.error = String(error || "event delivery failed").slice(0, 1000);
@@ -16,6 +16,9 @@ export class EventRuntimeWorker {
16
16
  sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
17
17
  batchSize = 50,
18
18
  retryDelayMs = 1000,
19
+ leaseRenewIntervalMs = null,
20
+ setIntervalFn = setInterval,
21
+ clearIntervalFn = clearInterval,
19
22
  } = {}) {
20
23
  if (!store) throw new Error("EventRuntimeWorker requires a store");
21
24
  this.store = store;
@@ -25,6 +28,60 @@ export class EventRuntimeWorker {
25
28
  this.sleep = sleep;
26
29
  this.batchSize = Math.max(1, Number(batchSize) || 50);
27
30
  this.retryDelayMs = Math.max(0, Number(retryDelayMs) || 0);
31
+ const inferredRenewMs = Math.max(
32
+ 1,
33
+ Math.floor(Number(store.leaseMs || 120000) / 3),
34
+ );
35
+ this.leaseRenewIntervalMs =
36
+ leaseRenewIntervalMs == null
37
+ ? inferredRenewMs
38
+ : Math.max(0, Number(leaseRenewIntervalMs) || 0);
39
+ this.setIntervalFn = setIntervalFn;
40
+ this.clearIntervalFn = clearIntervalFn;
41
+ }
42
+
43
+ _settlementOptions(record) {
44
+ if (!record?.lease?.owner) return {};
45
+ return {
46
+ owner: record.lease.owner,
47
+ ...(record.lease.fence == null ? {} : { fence: record.lease.fence }),
48
+ };
49
+ }
50
+
51
+ _startLeaseRenewal(queue, record) {
52
+ const renew =
53
+ queue === "inbox" ? this.store.renewInbox : this.store.renewOutbox;
54
+ if (
55
+ typeof renew !== "function" ||
56
+ !record?.lease?.owner ||
57
+ this.leaseRenewIntervalMs <= 0
58
+ ) {
59
+ return { lost: false, stop() {} };
60
+ }
61
+ const options = this._settlementOptions(record);
62
+ const state = { lost: false, timer: null };
63
+ const tick = () => {
64
+ if (state.lost) return;
65
+ try {
66
+ const renewed = renew.call(this.store, record.id, options);
67
+ if (renewed == null) state.lost = true;
68
+ } catch {
69
+ // Renewal is part of the correctness boundary. A lock/storage failure
70
+ // means this worker can no longer prove it owns the record.
71
+ state.lost = true;
72
+ }
73
+ };
74
+ state.timer = this.setIntervalFn(tick, this.leaseRenewIntervalMs);
75
+ state.timer?.unref?.();
76
+ return {
77
+ get lost() {
78
+ return state.lost;
79
+ },
80
+ stop: () => {
81
+ if (state.timer != null) this.clearIntervalFn(state.timer);
82
+ state.timer = null;
83
+ },
84
+ };
28
85
  }
29
86
 
30
87
  async _drain(queue, handler, stats) {
@@ -33,9 +90,15 @@ export class EventRuntimeWorker {
33
90
  ? this.store.claimInbox({ limit: this.batchSize, now: this.now() })
34
91
  : this.store.claimOutbox({ limit: this.batchSize, now: this.now() });
35
92
  for (const record of claimed) {
93
+ const renewal = this._startLeaseRenewal(queue, record);
36
94
  try {
37
95
  const result = await handler(record.event, record);
38
- const options = { owner: record.lease?.owner };
96
+ renewal.stop();
97
+ if (renewal.lost) {
98
+ stats[`${queue}LeaseLost`] += 1;
99
+ continue;
100
+ }
101
+ const options = this._settlementOptions(record);
39
102
  const settled =
40
103
  queue === "inbox"
41
104
  ? this.store.acknowledgeInbox(record.id, result, options)
@@ -43,9 +106,14 @@ export class EventRuntimeWorker {
43
106
  if (settled === null && options.owner) stats[`${queue}LeaseLost`] += 1;
44
107
  else stats[`${queue}Acked`] += 1;
45
108
  } catch (error) {
109
+ renewal.stop();
110
+ if (renewal.lost) {
111
+ stats[`${queue}LeaseLost`] += 1;
112
+ continue;
113
+ }
46
114
  const options = {
47
115
  retryDelayMs: this.retryDelayMs,
48
- owner: record.lease?.owner,
116
+ ...this._settlementOptions(record),
49
117
  };
50
118
  const settled = this.store.fail(
51
119
  queue,
@@ -3,13 +3,38 @@
3
3
  * Hook failures never change the surrounding agent or interaction result.
4
4
  */
5
5
  export function emitHooksV2Event(eventName, context = {}, options = {}) {
6
- void import("./hooks-v2-runtime.js")
7
- .then(({ default: runtime }) => {
8
- if (!runtime || typeof runtime.executeHooks !== "function") return;
9
- return runtime.executeHooks(eventName, context, options);
10
- })
11
- .catch(() => {
12
- // Hooks are an optional extension; producer delivery is best-effort.
13
- });
6
+ void executeHooksV2Event(eventName, context, options);
14
7
  }
15
8
 
9
+ /**
10
+ * Decision-capable producer bridge. Callers at a real gate (PreToolUse,
11
+ * Setup, prompt expansion) await this result; observer producers can keep
12
+ * using emitHooksV2Event().
13
+ */
14
+ export async function executeHooksV2Event(
15
+ eventName,
16
+ context = {},
17
+ options = {},
18
+ ) {
19
+ try {
20
+ const { default: runtime } = await import("./hooks-v2-runtime.js");
21
+ if (!runtime || typeof runtime.executeHooks !== "function") {
22
+ return {
23
+ success: options.failClosed !== true,
24
+ blocked: options.failClosed === true,
25
+ decision: options.failClosed === true ? "block" : "continue",
26
+ unavailable: true,
27
+ results: [],
28
+ };
29
+ }
30
+ return await runtime.executeHooks(eventName, context, options);
31
+ } catch (error) {
32
+ return {
33
+ success: options.failClosed !== true,
34
+ blocked: options.failClosed === true,
35
+ decision: options.failClosed === true ? "block" : "continue",
36
+ error: error?.message || String(error),
37
+ results: [],
38
+ };
39
+ }
40
+ }