chainlesschain 0.162.184 → 0.162.186

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 (192) hide show
  1. package/bin/chainlesschain.js +21 -5
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DzAl5fGx.js → AIOps-DTXiO-TB.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DAHtMGeC.js → ActionButton-BbmDW-Pz.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BHQaexv5.js → Analytics-CSfgXJuD.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-Phz_pD0n.js → AppLayout-BS0FFLb7.js} +4 -4
  8. package/src/assets/web-panel/assets/{Artifacts-DHt47kCA.js → Artifacts-B19WFPK2.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit--Y_8FULl.js → Audit-CQhZBi7n.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-N1zchH7D.js → BackgroundAgents-BOLZCpGH.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-B_BoyN30.js → Backup-Dz3R_ePD.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Gl4uQHcT.js → BaseInput-CYfgMpax.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-Cg-eIyAZ.js → Chat-5CymMr1j.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BzeUi3JH.js → ChatBubbleRenderer-B2A_TJtl.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-D0MGR2wA.js → Checkbox-CDXHVYcu.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BdObJEFn.js → Codegen-uq9Z9oiS.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col--ghxXT3z.js → Col-BuW1K_65.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-BUFeXOqz.js → Community-Bbg-PvRY.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-DhQO2_Mr.js → Compact-co0pPPTw.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-3HoTqKYd.js → Compliance-B-pFAv7z.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-B3zmO4VW.js → Cowork-54ZQS4gC.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-CJDrZOcc.js → Cron-B1BF2Seq.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-SMl15obd.js → Crosschain-Dey6qQTj.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-CgTrTWrl.js → DID-ltERvfAj.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-dXFKaKol.js → Dashboard-DuWxOcqN.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-qQHEhCvI.js → Dropdown-CrZ8ekTo.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-C_ej0MWH.js → EmailListRenderer-fIyHXhUS.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D6ajoE7I.js → FamilyGuardDashboard-BtRyrRPm.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-Cio2y49h.js → Federation-EUC6Wydj.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-ClJz1cYp.js → FormItemContext-BOpka2G-.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-Dr_8Maca.js → GenericCardRenderer-BXtrrYHn.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-CevLwp5u.js → Git-8WMb7PiR.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-CW9YyQUY.js → Governance-DNWOeDTV.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CQqeAyER.js → Inference-Bne9b_Lb.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-cuGVxHrw.js → KnowledgeGraph-Cp-6Us9b.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DIuHri5d.js → Logs-Ck-uSPAx.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-R91Qw5JA.js → MarkdownRenderer-DxjsdAXw.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-CY1VhFGx.js → Marketplace-Vdq7brrJ.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-BBAbi1vt.js → McpTools-ggLnw4hY.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-BWYGHxJA.js → Memory-BKIjNVCU.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-CPIFkW7A.js → MobileBridge-BElCY25a.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DQyWxCho.js → MobileProjects-DpLgovdH.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-Bo3GnQbx.js → Mtc-bNdrF_Zh.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-C8-c4rSW.js → MtcAudit-Dw8w1Cqk.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-ChqMTtoq.js → Multisig-CfGKvlqc.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-C48TyhvI.js → NLProgramming-D6OJf7YN.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-zogUFWxp.js → Notes-B3n_LzIS.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-v-K9XpSB.js → NotificationSettings-Uu1V_ICM.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-aGyWwWcp.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-Boi1dQfR.js → Organization-qjFFmuwK.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-DM5YWwgA.js → Overflow-J4ZgnwZz.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-C57LKpKK.js → P2P-YEZF-uVH.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BFkPqu6Q.js → PdhVaultBrowser-yqx7fwqk.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-D6fB9YSp.js → Permissions-DdEjlZ02.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-Bp7EefEo.js → PersonalDataHub-DbAPYvns.js} +3 -3
  56. package/src/assets/web-panel/assets/{Pipeline-BVln3hZf.js → Pipeline-Bxlk_ZJU.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-Bf8P3nF6.js → Privacy-CpE-05tz.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-DM6wA2E2.js → ProjectInit-DWlhcGHT.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CGbc5Iho.js → ProjectSettings-BnH6BiTQ.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-CHw2fXDB.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-B2kJcxS6.js → Providers-Bdz_sTyb.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-Cxu7dXYy.js → QrScannerModal-BHh2b3pH.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DTlrTPms.js → QuickAsk-D84M09pe.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Cm3X-cCj.js → Recommend-Czc30K6M.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DNb2kjvt.js → RemoteSession-q_TU5qxt.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-CZUFFjc1.js → Reputation-CnTce4WM.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-DAPgloxA.js → Row-C60UFLay.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-dsx9S793.js → RssFeed-BOUFEu9_.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DnnC8kz-.js → Search-D4__blE-.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-VuZknRMK.js → Security-BoOm7_JK.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-B3XNX8Vx.js → Services-AvzrW6HA.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-Dl1nVtCj.js → Skeleton-BZlrHozi.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-CKaMLuk0.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-HzU56n1z.js → Sla-DYCNJF8h.js} +1 -1
  75. package/src/assets/web-panel/assets/SpeechSettings-BvCSU6d2.js +1 -0
  76. package/src/assets/web-panel/assets/{SyncSettings-DWadz7Sl.js → SyncSettings-B6tW8dVO.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-oNC_2NVz.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-BBAgvrlM.js → Templates-DvLPvvjL.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-BsbbvGN8.js → Tenant-BDt9UBn_.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-uscpZI4q.js → Terminal-ddCUnH-X.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-mTyjNE6r.js → TimelineRenderer-CA4sDL47.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-Bz97oBgs.js → Tokens-By03hnVs.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BTRKC140.js → Trigger-Dz2mH-ZU.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-B4A_KuyX.js → Trust-DShSWHeJ.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DES9TmLU.js → UkeySign-JN8lP3gt.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-ClXs7Qxp.js → VideoEditing-BgPy4gV3.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-IzZqFz-7.js → Wallet-CQGEsXw_.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-BtOP04g_.js → WebAuthn-D8xR-ds_.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-_0cqnopY.js → WorkflowEditor-Df61_p1p.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-ax7193uU.js → chat-fdPby427.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-Bu4WGJah.js → colors-Dimqhufo.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-BX1dlTW0.js → compact-item-DHayKcqZ.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-CktA_Dj7.js → createContext-hZUhypRH.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-ePxoBp92.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-D5wd817l.js → hasIn-Dw7zC_dV.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-ytp2H5Zx.js → index--DVpUpz3.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-tYPSfGbd.js → index-BHJUptSx.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-A4uBPQCz.js → index-BIxxvaGN.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DmVUeWUk.js → index-BM5BNqp9.js} +1 -1
  100. package/src/assets/web-panel/assets/index-BTBDdCoo.js +1 -0
  101. package/src/assets/web-panel/assets/{index-Ci3y1ZAv.js → index-BVHdkz8F.js} +17 -17
  102. package/src/assets/web-panel/assets/{index-Cv7bVWJ5.js → index-BYdNwhkv.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CUForzsG.js → index-Bhmpp3cm.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C8DkWyNr.js → index-BuMSdwno.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BRfXoRuh.js → index-BwnUluxu.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-o14NNnxw.js → index-C1yMYKi-.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C8tLR2TG.js → index-C8XXPz2n.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-5PYYkr8S.js → index-CBQK-LEF.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DkD1K-fF.js → index-CIOE8tt6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CjEJjl5C.js → index-CPZ3U5iH.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D9op8poE.js → index-CTiVrnpR.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DYo9S5OT.js → index-CcpDboUd.js} +1 -1
  113. package/src/assets/web-panel/assets/index-CfOGx8Ns.js +1 -0
  114. package/src/assets/web-panel/assets/{index-D-o4Rwjk.js → index-CsCo_A0I.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-h3KJZr95.js → index-CuaVa8tX.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DF1TysIF.js → index-CvKHpaao.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CfzxR61U.js → index-D7jQEwB6.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Db3CpWTr.js → index-DAjvkQ4K.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DmPJ6T6F.js → index-DC2R4zvM.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C3HmzCLt.js → index-DJP7TjWm.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BAtBNfkr.js → index-DSlda4-6.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D_cycOET.js → index-DVM_KgzF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Dzdr0zDf.js → index-DX1DwDFI.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-bFCaM7Eq.js → index-DnwDYBnG.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Bad2CYeH.js → index-DxoPzPHy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-iHcPtQ2e.js → index-DyEhrvpm.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-jzlzm4VK.js → index-DzJXkoZs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CP8ELJBc.js → index-JE6Udz2r.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CeVZaJSC.js → index-Urt2R5aE.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BLLsUNDc.js → index-cEnkyHtX.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BcGdL3sG.js → index-j0pxzOu7.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-PBLzwYnu.js → index-lPjBQJCy.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CYRE0NJA.js → index-ulvBurtw.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BQtoRyTO.js → index-y5LHQ5Ck.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-D2Iut83B.js → initDefaultProps-B4pR3px5.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-BuaJVdK9.js → motion-CE7dWPPs.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-CaEuM92y.js → move-Hw8cUObc.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-wuKNBwys.js → mtc-parser-CvRjE4Fa.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-7a3eklaq.js → omit-CkFeLVre.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-Dn4z7KvJ.js → pickAttrs-5ZMqmbbb.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-DVn_qT4-.js → placementArrow-BZ4oeZ0q.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-Dodz1OMQ.js → responsiveObserve-lwoiX_Us.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CxoGskbp.js → slide-D_as37l8.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-DBh_epQH.js → statusUtils-BsA-4Bs7.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-RXQ4gG5P.js → styleChecker-BVar4sLQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-1Wxm0HEG.js → useFlexGapSupport-j1IwVpGd.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CI_ySNxJ.js → useFs-7Q9bEnUp.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-C6973rZP.js → usePersonalDataHub-BBwTIRMd.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-CalynwKr.js → vnode-C7Dk-M8w.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-CM1cd-Q0.js → zoom-D3UHaTFB.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/eval.js +7 -0
  153. package/src/commands/status.js +55 -0
  154. package/src/commands/team.js +31 -6
  155. package/src/data/changelog.json +26 -0
  156. package/src/gateways/terminal/PtyManager.js +45 -15
  157. package/src/gateways/ws/ws-agent-handler.js +15 -1
  158. package/src/gateways/ws/ws-session-gateway.js +88 -0
  159. package/src/harness/background-task-command-runner.js +234 -0
  160. package/src/harness/background-task-manager.js +150 -20
  161. package/src/harness/background-task-worker.js +17 -11
  162. package/src/lazy-dispatch.js +12 -1
  163. package/src/lib/background-agent-supervisor.js +46 -0
  164. package/src/lib/event-runtime-host.js +12 -10
  165. package/src/lib/hook-runner.cjs +7 -1
  166. package/src/lib/hook-runner.js +2 -0
  167. package/src/lib/hooks-v2-runtime.js +94 -6
  168. package/src/lib/hooks-v2-workspace-context.js +258 -0
  169. package/src/lib/lsp/__tests__/lsp-client.test.js +1 -4
  170. package/src/lib/observability/index.js +191 -19
  171. package/src/lib/observability/otlp-exporter.js +1209 -207
  172. package/src/lib/otlp-exporter.js +8 -36
  173. package/src/lib/plugin-runtime/bin.js +20 -4
  174. package/src/lib/plugin-runtime/sandbox-policy.js +1 -4
  175. package/src/lib/process-execution-broker/index.js +706 -40
  176. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +212 -2
  177. package/src/lib/process-execution-broker/linux-generic-bwrap.js +124 -6
  178. package/src/lib/process-execution-broker/platform-sandbox.js +6 -2
  179. package/src/program-base.js +9 -2
  180. package/src/repl/agent-repl.js +8 -0
  181. package/src/runtime/agent-core.js +272 -38
  182. package/src/runtime/headless-runner.js +56 -22
  183. package/src/runtime/headless-stream.js +8 -0
  184. package/src/workers/background-agent-worker.js +5 -11
  185. package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +0 -1
  186. package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +0 -1
  187. package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +0 -1
  188. package/src/assets/web-panel/assets/SpeechSettings-CRle7Jl2.js +0 -1
  189. package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +0 -1
  190. package/src/assets/web-panel/assets/devWarning-x877iKKS.js +0 -1
  191. package/src/assets/web-panel/assets/index-D7pfPouB.js +0 -1
  192. package/src/assets/web-panel/assets/index-e4i-VLBd.js +0 -1
@@ -107,6 +107,8 @@ function runHookProcessSync(file, argsOrOptions, maybeOptions) {
107
107
  }
108
108
 
109
109
  export function _restoreProcessRunners() {
110
+ _processDeps.run = brokerRunner;
111
+ _processDeps.runSync = brokerSyncRunner;
110
112
  runner._deps.run = runHookProcess;
111
113
  runner._deps.runSync = runHookProcessSync;
112
114
  }
@@ -30,6 +30,13 @@ import hookShellCommand from "./hook-shell-command.cjs";
30
30
  import permissionRules from "./permission-rules.cjs";
31
31
  import { EventRuntimeStore } from "./event-runtime-store.js";
32
32
  import { getDefaultEventRuntimeHost } from "./event-runtime-host.js";
33
+ import {
34
+ currentHostHooksV2WorkspaceBinding,
35
+ currentHostHooksV2WorkspaceRoot,
36
+ registerHostHooksV2Workspace,
37
+ resolveRegisteredHostHooksV2Workspace,
38
+ runWithHostHooksV2Workspace,
39
+ } from "./hooks-v2-workspace-context.js";
33
40
 
34
41
  const { globToRegExp } = permissionRules;
35
42
  const { buildManagedHookEnvironment } = hookEnvironment;
@@ -279,12 +286,31 @@ export function resolveHookSandboxPolicy(hook = {}, managedPolicy = {}) {
279
286
  return resolved;
280
287
  }
281
288
 
289
+ function hooksRequireWorkspaceBinding(hooks, managedPolicy) {
290
+ return hooks.some((hook) => {
291
+ if (hook.type !== "command") return false;
292
+ try {
293
+ return requiresExplicitHookShell(
294
+ resolveHookSandboxPolicy(hook, managedPolicy),
295
+ );
296
+ } catch {
297
+ // Invalid policy is fail-closed during execution and must remain so after
298
+ // a crash instead of recovering without workspace authority.
299
+ return true;
300
+ }
301
+ });
302
+ }
303
+
282
304
  export function assertManagedHookPolicy(
283
305
  hook,
284
306
  context = {},
285
307
  policy = {},
286
308
  configDir = null,
309
+ workspaceRoot = null,
287
310
  ) {
311
+ // Retained for API compatibility and non-command policy growth, but never
312
+ // consulted as filesystem authority.
313
+ void context;
288
314
  if (!managedListAllows(hook.type, policy.allowedExecutors)) {
289
315
  throw managedPolicyError(
290
316
  `Hook executor is outside the managed allowlist: ${hook.type}`,
@@ -309,7 +335,11 @@ export function assertManagedHookPolicy(
309
335
  `Hook command is outside the managed allowlist: ${executable}`,
310
336
  );
311
337
  }
312
- const cwd = hook.cwd || context.cwd || configDir || process.cwd();
338
+ // The event context is payload, not workspace authority. Keep this
339
+ // selection identical to _execCommand so a forged context.cwd cannot make
340
+ // the managed-root check approve a different directory than the one the
341
+ // Broker will actually execute in.
342
+ const cwd = hook.cwd || configDir || workspaceRoot || process.cwd();
313
343
  if (!isInsideManagedRoot(cwd, policy.workspaceRoots)) {
314
344
  throw managedPolicyError(
315
345
  `Hook working directory is outside managed workspace roots: ${cwd}`,
@@ -435,7 +465,14 @@ class HooksV2Runtime extends EventEmitter {
435
465
  // Both values are host constructor inputs. Hook definitions may select a
436
466
  // cwd inside this root, but can never replace the writable root used to
437
467
  // issue a Linux one-shot execution contract.
438
- this.workspaceRoot = options.workspaceRoot || configDir || null;
468
+ const configuredWorkspaceRoot = options.workspaceRoot || configDir || null;
469
+ this.workspaceBinding =
470
+ typeof configuredWorkspaceRoot === "string" &&
471
+ path.isAbsolute(configuredWorkspaceRoot)
472
+ ? registerHostHooksV2Workspace(configuredWorkspaceRoot)
473
+ : null;
474
+ this.workspaceRoot =
475
+ this.workspaceBinding?.workspaceRoot || configuredWorkspaceRoot;
439
476
  this.durableStore = options.durableStore || null;
440
477
  this.durableOwner =
441
478
  options.durableOwner ||
@@ -587,6 +624,12 @@ class HooksV2Runtime extends EventEmitter {
587
624
  (max, hook) => Math.max(max, Number(hook?.timeoutMs) || 30000),
588
625
  0,
589
626
  );
627
+ const durableWorkspaceBinding =
628
+ this.workspaceBinding || currentHostHooksV2WorkspaceBinding();
629
+ const durableWorkspaceBindingRequired = hooksRequireWorkspaceBinding(
630
+ hooks,
631
+ this.managedPolicy,
632
+ );
590
633
  const durableRecord =
591
634
  this.durableStore && options.skipDurable !== true
592
635
  ? this.durableStore.enqueueInbox(
@@ -598,6 +641,12 @@ class HooksV2Runtime extends EventEmitter {
598
641
  },
599
642
  {
600
643
  id: context.event_id || context.eventId || null,
644
+ metadata: {
645
+ hooksV2WorkspaceBindingId:
646
+ durableWorkspaceBinding?.bindingId || null,
647
+ hooksV2WorkspaceBindingRequired:
648
+ durableWorkspaceBindingRequired,
649
+ },
601
650
  claimOwner: this.durableOwner,
602
651
  leaseMs: longestHookTimeout + this.durableRecoveryBufferMs,
603
652
  },
@@ -644,6 +693,7 @@ class HooksV2Runtime extends EventEmitter {
644
693
  context,
645
694
  this.managedPolicy,
646
695
  this.configDir,
696
+ this.workspaceRoot || currentHostHooksV2WorkspaceRoot(),
647
697
  );
648
698
  let result;
649
699
  switch (hook.type) {
@@ -750,9 +800,11 @@ class HooksV2Runtime extends EventEmitter {
750
800
  const budget = hookBudget(hook);
751
801
  const sandboxPolicy = resolveHookSandboxPolicy(hook, this.managedPolicy);
752
802
  const requiresContract = requiresExplicitHookShell(sandboxPolicy);
803
+ const trustedRootSource =
804
+ this.workspaceRoot || currentHostHooksV2WorkspaceRoot();
753
805
  const trustedRoot = requiresContract
754
806
  ? requireTrustedHookRoot(
755
- this.workspaceRoot,
807
+ trustedRootSource,
756
808
  "Hooks v2 trusted workspace root",
757
809
  )
758
810
  : null;
@@ -1023,12 +1075,48 @@ const hooksRuntime = new HooksV2Runtime(undefined, {
1023
1075
  ? new EventRuntimeStore()
1024
1076
  : null),
1025
1077
  });
1026
- defaultEventRuntimeHost?.registerHandler(
1027
- (event) =>
1028
- hooksRuntime.executeHooks(event.event, event.context || {}, {
1078
+ broker._setHooksEventSink(hooksRuntime);
1079
+
1080
+ export async function executeRecoveredHooksV2Event(
1081
+ runtime,
1082
+ event,
1083
+ record = {},
1084
+ ) {
1085
+ const bindingId = record?.metadata?.hooksV2WorkspaceBindingId || null;
1086
+ const bindingRequired =
1087
+ record?.metadata?.hooksV2WorkspaceBindingRequired === true ||
1088
+ hooksRequireWorkspaceBinding(
1089
+ runtime?.hooks?.get?.(event?.event) || [],
1090
+ runtime?.managedPolicy || {},
1091
+ );
1092
+ if (!bindingId) {
1093
+ if (bindingRequired) {
1094
+ throw sandboxBoundaryError(
1095
+ "durable Hooks v2 recovery requires a trusted host workspace binding",
1096
+ );
1097
+ }
1098
+ return runtime.executeHooks(event.event, event.context || {}, {
1099
+ skipDurable: true,
1100
+ recovered: true,
1101
+ });
1102
+ }
1103
+
1104
+ const binding = resolveRegisteredHostHooksV2Workspace(bindingId);
1105
+ if (!binding) {
1106
+ throw sandboxBoundaryError(
1107
+ "durable Hooks v2 workspace binding is not registered by this host",
1108
+ );
1109
+ }
1110
+ return runWithHostHooksV2Workspace(binding.workspaceRoot, () =>
1111
+ runtime.executeHooks(event.event, event.context || {}, {
1029
1112
  skipDurable: true,
1030
1113
  recovered: true,
1031
1114
  }),
1115
+ );
1116
+ }
1117
+
1118
+ defaultEventRuntimeHost?.registerHandler(
1119
+ (event, record) => executeRecoveredHooksV2Event(hooksRuntime, event, record),
1032
1120
  { queue: "inbox", type: "hooks.v2" },
1033
1121
  );
1034
1122
  export default hooksRuntime;
@@ -0,0 +1,258 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { createHash } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ const trustedWorkspaceStorage = new AsyncLocalStorage();
7
+ const registeredHostWorkspaces = new Map();
8
+ const registeredHostWorkspaceIdentities = new Map();
9
+ const MAX_REGISTERED_HOST_WORKSPACES = 1024;
10
+
11
+ function validateHostWorkspaceRoot(workspaceRoot) {
12
+ if (
13
+ typeof workspaceRoot !== "string" ||
14
+ workspaceRoot.length === 0 ||
15
+ workspaceRoot.includes("\0")
16
+ ) {
17
+ const error = new TypeError(
18
+ "Hooks v2 host workspace root must be a non-empty path",
19
+ );
20
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_INVALID";
21
+ throw error;
22
+ }
23
+ return path.resolve(workspaceRoot);
24
+ }
25
+
26
+ function invalidWorkspaceRoot(message, cause) {
27
+ const error = new Error(message, cause ? { cause } : undefined);
28
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_INVALID";
29
+ return error;
30
+ }
31
+
32
+ function workspaceGeneration(stats) {
33
+ // dev+ino alone is insufficient: filesystems may immediately reuse an inode
34
+ // after a same-path directory replacement. Prefer the stable creation
35
+ // timestamp and fail closed on metadata changes when a filesystem does not
36
+ // expose one.
37
+ return stats.birthtimeNs > 0n
38
+ ? `birth:${stats.birthtimeNs.toString()}`
39
+ : `ctime:${stats.ctimeNs.toString()}`;
40
+ }
41
+
42
+ function captureWorkspaceIdentity(workspaceRoot) {
43
+ const resolvedRoot = validateHostWorkspaceRoot(workspaceRoot);
44
+ let canonicalRoot;
45
+ let stats;
46
+ try {
47
+ canonicalRoot = fs.realpathSync.native(resolvedRoot);
48
+ stats = fs.statSync(canonicalRoot, { bigint: true });
49
+ } catch (error) {
50
+ throw invalidWorkspaceRoot(
51
+ "Hooks v2 host workspace root must resolve to an existing directory",
52
+ error,
53
+ );
54
+ }
55
+ if (!stats.isDirectory()) {
56
+ throw invalidWorkspaceRoot(
57
+ "Hooks v2 host workspace root must resolve to a directory",
58
+ );
59
+ }
60
+ return Object.freeze({
61
+ canonicalRoot,
62
+ device: stats.dev,
63
+ inode: stats.ino,
64
+ generation: workspaceGeneration(stats),
65
+ });
66
+ }
67
+
68
+ function workspaceIdentityKey(identity) {
69
+ return JSON.stringify([
70
+ identity.canonicalRoot,
71
+ identity.device.toString(),
72
+ identity.inode.toString(),
73
+ identity.generation,
74
+ ]);
75
+ }
76
+
77
+ function sameWorkspaceIdentity(left, right) {
78
+ return (
79
+ left.canonicalRoot === right.canonicalRoot &&
80
+ left.device === right.device &&
81
+ left.inode === right.inode &&
82
+ left.generation === right.generation
83
+ );
84
+ }
85
+
86
+ function invalidateWorkspaceRecord(record) {
87
+ if (registeredHostWorkspaces.get(record.binding.bindingId) === record) {
88
+ registeredHostWorkspaces.delete(record.binding.bindingId);
89
+ }
90
+ if (
91
+ registeredHostWorkspaceIdentities.get(record.identityKey) ===
92
+ record.binding.bindingId
93
+ ) {
94
+ registeredHostWorkspaceIdentities.delete(record.identityKey);
95
+ }
96
+ }
97
+
98
+ function verifyWorkspaceRecord(record) {
99
+ if (!record) return null;
100
+ if (registeredHostWorkspaces.get(record.binding.bindingId) !== record) {
101
+ return null;
102
+ }
103
+ let currentIdentity;
104
+ try {
105
+ currentIdentity = captureWorkspaceIdentity(record.binding.workspaceRoot);
106
+ } catch {
107
+ invalidateWorkspaceRecord(record);
108
+ return null;
109
+ }
110
+ if (!sameWorkspaceIdentity(record.identity, currentIdentity)) {
111
+ invalidateWorkspaceRecord(record);
112
+ return null;
113
+ }
114
+ return record.binding;
115
+ }
116
+
117
+ function pruneInvalidWorkspaceRecords() {
118
+ for (const record of Array.from(registeredHostWorkspaces.values())) {
119
+ verifyWorkspaceRecord(record);
120
+ }
121
+ }
122
+
123
+ function workspaceBindingId(identity) {
124
+ return createHash("sha256")
125
+ .update("chainlesschain.hooks-v2-host-workspace.v3\0")
126
+ .update(workspaceIdentityKey(identity), "utf8")
127
+ .digest("hex");
128
+ }
129
+
130
+ /**
131
+ * Register a root selected by trusted host bootstrap code. Durable records
132
+ * persist only the returned opaque stable digest; the plaintext canonical path
133
+ * and directory identity remain process-local.
134
+ */
135
+ export function registerHostHooksV2Workspace(workspaceRoot) {
136
+ const identity = captureWorkspaceIdentity(workspaceRoot);
137
+ const identityKey = workspaceIdentityKey(identity);
138
+ const existingBindingId = registeredHostWorkspaceIdentities.get(identityKey);
139
+ if (existingBindingId) {
140
+ const existingRecord = registeredHostWorkspaces.get(existingBindingId);
141
+ const existingBinding = verifyWorkspaceRecord(existingRecord);
142
+ if (
143
+ existingBinding &&
144
+ sameWorkspaceIdentity(existingRecord.identity, identity)
145
+ ) {
146
+ return existingBinding;
147
+ }
148
+ }
149
+
150
+ // A long-running host may create and remove many isolated worktrees. Sweep
151
+ // stale filesystem identities before enforcing the bounded registry so
152
+ // deleted roots cannot exhaust future trusted registrations.
153
+ if (registeredHostWorkspaces.size >= MAX_REGISTERED_HOST_WORKSPACES) {
154
+ pruneInvalidWorkspaceRecords();
155
+ }
156
+ if (registeredHostWorkspaces.size >= MAX_REGISTERED_HOST_WORKSPACES) {
157
+ const error = new Error(
158
+ "Hooks v2 host workspace registry capacity exceeded",
159
+ );
160
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_LIMIT";
161
+ throw error;
162
+ }
163
+
164
+ const bindingId = workspaceBindingId(identity);
165
+ const collidingRecord = registeredHostWorkspaces.get(bindingId);
166
+ if (collidingRecord) {
167
+ const collidingBinding = verifyWorkspaceRecord(collidingRecord);
168
+ if (
169
+ collidingBinding &&
170
+ sameWorkspaceIdentity(collidingRecord.identity, identity)
171
+ ) {
172
+ registeredHostWorkspaceIdentities.set(identityKey, bindingId);
173
+ return collidingBinding;
174
+ }
175
+ if (
176
+ collidingBinding &&
177
+ collidingRecord.identity.canonicalRoot !== identity.canonicalRoot
178
+ ) {
179
+ const error = new Error("Hooks v2 workspace binding digest collision");
180
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_COLLISION";
181
+ throw error;
182
+ }
183
+ if (collidingBinding) {
184
+ throw invalidWorkspaceRoot(
185
+ "Hooks v2 host workspace identity changed during registration",
186
+ );
187
+ }
188
+ }
189
+
190
+ const binding = Object.freeze({
191
+ bindingId,
192
+ workspaceRoot: identity.canonicalRoot,
193
+ });
194
+ const record = Object.freeze({
195
+ binding,
196
+ identity,
197
+ identityKey,
198
+ });
199
+ registeredHostWorkspaces.set(bindingId, record);
200
+ registeredHostWorkspaceIdentities.set(identityKey, bindingId);
201
+ return binding;
202
+ }
203
+
204
+ /**
205
+ * Run a CLI-host operation with an immutable Hooks v2 workspace authority.
206
+ *
207
+ * Only host bootstrap code may call this with a root selected before hook,
208
+ * plugin, model, or event payload processing. The binding is async-scoped
209
+ * instead of global so concurrent CLI runs cannot replace one another's
210
+ * writable root.
211
+ */
212
+ export function runWithHostHooksV2Workspace(workspaceRoot, callback) {
213
+ if (typeof callback !== "function") {
214
+ throw new TypeError("Hooks v2 workspace callback must be a function");
215
+ }
216
+ const binding = registerHostHooksV2Workspace(workspaceRoot);
217
+ return trustedWorkspaceStorage.run(
218
+ registeredHostWorkspaces.get(binding.bindingId),
219
+ callback,
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Internal consumer seam for HooksV2Runtime. Event and hook payloads never
225
+ * participate in selecting this value.
226
+ */
227
+ export function currentHostHooksV2WorkspaceRoot() {
228
+ return currentHostHooksV2WorkspaceBinding()?.workspaceRoot || null;
229
+ }
230
+
231
+ export function currentHostHooksV2WorkspaceBinding() {
232
+ return verifyWorkspaceRecord(trustedWorkspaceStorage.getStore());
233
+ }
234
+
235
+ /**
236
+ * Resolve an opaque durable binding only against roots registered by the
237
+ * current host process. No persisted or event-supplied path is accepted.
238
+ */
239
+ export function resolveRegisteredHostHooksV2Workspace(bindingId) {
240
+ if (typeof bindingId !== "string" || !/^[a-f0-9]{64}$/.test(bindingId)) {
241
+ return null;
242
+ }
243
+ return verifyWorkspaceRecord(registeredHostWorkspaces.get(bindingId));
244
+ }
245
+
246
+ /**
247
+ * Revoke a process-local binding owned by trusted host lifecycle code.
248
+ * Payloads, hooks, and durable records must never call this release seam.
249
+ */
250
+ export function releaseRegisteredHostHooksV2Workspace(bindingId) {
251
+ if (typeof bindingId !== "string" || !/^[a-f0-9]{64}$/.test(bindingId)) {
252
+ return false;
253
+ }
254
+ const record = registeredHostWorkspaces.get(bindingId);
255
+ if (!record) return false;
256
+ invalidateWorkspaceRecord(record);
257
+ return true;
258
+ }
@@ -102,10 +102,7 @@ describe("LSPClient handshake", () => {
102
102
  const child = new FakeChild();
103
103
  const contract = Object.freeze({ kind: "test-workspace-contract" });
104
104
  const issueAuthority = vi
105
- .spyOn(
106
- executionBroker,
107
- "issueLinuxWorkspaceSandboxExecutionContract",
108
- )
105
+ .spyOn(executionBroker, "issueLinuxWorkspaceSandboxExecutionContract")
109
106
  .mockReturnValue(contract);
110
107
  const brokerSpawn = vi
111
108
  .spyOn(executionBroker, "spawn")
@@ -1,6 +1,10 @@
1
1
  /**
2
- * Observability - 可观测性模块统一导出
3
- * M3 阶段模块:包含分布式追踪 + 性能指标收集
2
+ * Process-level observability runtime.
3
+ *
4
+ * It bridges the existing W3C TraceContext / MetricsCollector and the
5
+ * TelemetryRecorder used by agent, eval and team runs into one standard OTLP
6
+ * Collector exporter. Collector export is opt-in and content remains absent
7
+ * unless the caller explicitly enabled the existing `--otlp-content` switch.
4
8
  */
5
9
 
6
10
  import {
@@ -11,25 +15,193 @@ import {
11
15
  metricsCollector,
12
16
  MetricsCollector,
13
17
  } from "../execution-trace/metrics-collector.js";
18
+ import { OtlpExporter, resolveOtlpConfig } from "./otlp-exporter.js";
14
19
 
15
- export { traceContext, TraceContext, metricsCollector, MetricsCollector };
20
+ export {
21
+ traceContext,
22
+ TraceContext,
23
+ metricsCollector,
24
+ MetricsCollector,
25
+ OtlpExporter,
26
+ resolveOtlpConfig,
27
+ };
16
28
 
17
- /**
18
- * 初始化可观测性模块
19
- * @param {Object} [options={}]
20
- * @param {boolean} [options.tracingEnabled=true] - 是否启用追踪
21
- * @param {boolean} [options.metricsEnabled=true] - 是否启用指标收集
22
- */
23
- export function initObservability(options = {}) {
24
- const { tracingEnabled = true, metricsEnabled = true } = options;
25
-
26
- // 可观测性模块初始化逻辑
27
- return {
28
- tracingEnabled,
29
- metricsEnabled,
30
- traceContext,
31
- metricsCollector,
32
- };
29
+ function collectedMetrics(collector) {
30
+ const raw = collector?.collect?.();
31
+ const source = raw?.resourceMetrics?.[0]?.scopeMetrics?.[0]?.metrics || [];
32
+ return source.map((metric) => {
33
+ if (metric.type === "counter") {
34
+ return {
35
+ name: metric.name,
36
+ type: "sum",
37
+ value: metric.value,
38
+ attributes: metric.labels,
39
+ };
40
+ }
41
+ if (metric.type === "histogram") {
42
+ return {
43
+ name: metric.name,
44
+ type: "histogram",
45
+ count: metric.count,
46
+ sum: metric.sum,
47
+ attributes: metric.labels,
48
+ };
49
+ }
50
+ return {
51
+ name: metric.name,
52
+ type: "gauge",
53
+ value: metric.value,
54
+ attributes: metric.labels,
55
+ };
56
+ });
57
+ }
58
+
59
+ export class ObservabilityRuntime {
60
+ constructor(options = {}, deps = {}) {
61
+ this.traceContext = deps.traceContext || traceContext;
62
+ this.metricsCollector = deps.metricsCollector || metricsCollector;
63
+ this.exporter =
64
+ deps.exporter ||
65
+ new OtlpExporter(
66
+ {
67
+ ...options,
68
+ config:
69
+ options.config ||
70
+ resolveOtlpConfig(options, deps.env || process.env, deps),
71
+ },
72
+ deps,
73
+ );
74
+ this._closed = false;
75
+ this._onSpanEnd = (span) => this.exporter.exportSpans([span]);
76
+ if (this.exporter.enabled) {
77
+ this.traceContext.on("span:end", this._onSpanEnd);
78
+ }
79
+ }
80
+
81
+ get enabled() {
82
+ return this.exporter.enabled && !this._closed;
83
+ }
84
+
85
+ exportRecorder(recorder, { redact = true } = {}) {
86
+ if (!this.enabled || !recorder?.toOtlp) return false;
87
+ return this.exporter.exportTracePayload(recorder.toOtlp({ redact }), {
88
+ redact,
89
+ });
90
+ }
91
+
92
+ exportTeamSummary({ workflowRunId, workflowName, summary, budget } = {}) {
93
+ if (!this.enabled) return false;
94
+ const attributes = {
95
+ "workflow.run_id": workflowRunId || "unknown",
96
+ "workflow.name": workflowName || "team",
97
+ };
98
+ const stats = summary?.stats || {};
99
+ const budgetStatus = budget?.status?.() || budget || {};
100
+ return this.exporter.exportMetrics([
101
+ {
102
+ name: "chainlesschain.team.tasks",
103
+ type: "gauge",
104
+ unit: "{task}",
105
+ value: Number(budgetStatus.tasks ?? summary?.executions) || 0,
106
+ attributes,
107
+ },
108
+ {
109
+ name: "chainlesschain.team.tokens",
110
+ type: "gauge",
111
+ unit: "{token}",
112
+ value: Number(budgetStatus.tokens) || 0,
113
+ attributes,
114
+ },
115
+ {
116
+ name: "chainlesschain.team.cost",
117
+ type: "gauge",
118
+ unit: "USD",
119
+ value: Number(budgetStatus.spentUsd) || 0,
120
+ attributes,
121
+ },
122
+ {
123
+ name: "chainlesschain.team.failures",
124
+ type: "gauge",
125
+ unit: "{failure}",
126
+ value: Number(stats.failed) || 0,
127
+ attributes,
128
+ },
129
+ {
130
+ name: "chainlesschain.team.completed",
131
+ type: "gauge",
132
+ unit: "{task}",
133
+ value: Number(stats.completed) || 0,
134
+ attributes,
135
+ },
136
+ ]);
137
+ }
138
+
139
+ captureMetrics() {
140
+ if (!this.enabled) return false;
141
+ const metrics = collectedMetrics(this.metricsCollector);
142
+ return metrics.length > 0 ? this.exporter.exportMetrics(metrics) : false;
143
+ }
144
+
145
+ async shutdown(options = {}) {
146
+ if (this._closed) return this.exporter.getStats();
147
+ this.traceContext.off("span:end", this._onSpanEnd);
148
+ this.captureMetrics();
149
+ const stats = await this.exporter.shutdown(options);
150
+ this._closed = true;
151
+ return stats;
152
+ }
153
+
154
+ getStats() {
155
+ return this.exporter.getStats();
156
+ }
157
+ }
158
+
159
+ let defaultRuntime = null;
160
+
161
+ export function resolveOtlpEndpointFromArgv(argv = process.argv) {
162
+ const args = Array.isArray(argv) ? argv.slice(2) : [];
163
+ for (let index = 0; index < args.length; index++) {
164
+ const token = args[index];
165
+ if (token === "--otlp-endpoint") return args[index + 1] || null;
166
+ if (token.startsWith("--otlp-endpoint=")) {
167
+ return token.slice("--otlp-endpoint=".length) || null;
168
+ }
169
+ }
170
+ return null;
171
+ }
172
+
173
+ export function initObservability(options = {}, deps = {}) {
174
+ if (defaultRuntime && !defaultRuntime._closed) return defaultRuntime;
175
+ defaultRuntime = new ObservabilityRuntime(options, deps);
176
+ return defaultRuntime;
177
+ }
178
+
179
+ export function getObservabilityRuntime() {
180
+ return defaultRuntime;
181
+ }
182
+
183
+ export function isOtlpCollectorEnabled() {
184
+ return defaultRuntime?.enabled === true;
185
+ }
186
+
187
+ export function exportTelemetryRecorder(recorder, options) {
188
+ return defaultRuntime?.exportRecorder(recorder, options) || false;
189
+ }
190
+
191
+ export function exportTeamTelemetry(summary) {
192
+ return defaultRuntime?.exportTeamSummary(summary) || false;
193
+ }
194
+
195
+ export async function shutdownObservability(options) {
196
+ return defaultRuntime?.shutdown(options);
197
+ }
198
+
199
+ /** Test-only lifecycle seam; avoids state leaking across module-level tests. */
200
+ export async function resetObservabilityForTests() {
201
+ if (defaultRuntime && !defaultRuntime._closed) {
202
+ await defaultRuntime.shutdown({ timeoutMs: 0 });
203
+ }
204
+ defaultRuntime = null;
33
205
  }
34
206
 
35
207
  export * from "../execution-trace/index.js";