zidane 9.1.1 → 9.1.3

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 (279) hide show
  1. package/dist/{acp-Di9WvMDF.js → acp-CeCavNlg.js} +7 -7
  2. package/dist/{acp-Di9WvMDF.js.map → acp-CeCavNlg.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-DJhpTS-B.js → agent-Dh_XZ7fj.js} +74 -19
  7. package/dist/agent-Dh_XZ7fj.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{auth-store-port-DFgf22oo.js → auth-store-port-BKpjlJ8a.js} +2 -2
  11. package/dist/{auth-store-port-DFgf22oo.js.map → auth-store-port-BKpjlJ8a.js.map} +1 -1
  12. package/dist/{auto-update-restart-BFB7Ek87.js → auto-update-restart-CQJtndMG.js} +277 -41
  13. package/dist/auto-update-restart-CQJtndMG.js.map +1 -0
  14. package/dist/{browser-BcoCCB_n.js → browser-BkdeKnxO.js} +56 -2
  15. package/dist/browser-BkdeKnxO.js.map +1 -0
  16. package/dist/{browser-entry-ypi8CyE-.d.ts → browser-entry-BCwruqNa.d.ts} +10 -10
  17. package/dist/{browser-entry-ypi8CyE-.d.ts.map → browser-entry-BCwruqNa.d.ts.map} +1 -1
  18. package/dist/channels.d.ts +5 -5
  19. package/dist/chat/browser.d.ts +4 -4
  20. package/dist/chat/browser.js +6 -6
  21. package/dist/chat/pure.d.ts +5 -5
  22. package/dist/chat/pure.js +8 -8
  23. package/dist/chat.d.ts +5 -5
  24. package/dist/chat.js +55 -55
  25. package/dist/chat.js.map +1 -1
  26. package/dist/{cli-shared-CabruH8Z.js → cli-shared-DeRHgYvl.js} +4 -4
  27. package/dist/{cli-shared-CabruH8Z.js.map → cli-shared-DeRHgYvl.js.map} +1 -1
  28. package/dist/cli-style-DBEm6CyT.js +105 -0
  29. package/dist/cli-style-DBEm6CyT.js.map +1 -0
  30. package/dist/{client-core-CIixwznM.d.ts → client-core-C_4m_oL6.d.ts} +4 -4
  31. package/dist/{client-core-CIixwznM.d.ts.map → client-core-C_4m_oL6.d.ts.map} +1 -1
  32. package/dist/{client-CCaIXWue.d.ts → client-ygTASGis.d.ts} +2 -2
  33. package/dist/{client-CCaIXWue.d.ts.map → client-ygTASGis.d.ts.map} +1 -1
  34. package/dist/codemode-worker.js +8 -5
  35. package/dist/codemode-worker.js.map +1 -1
  36. package/dist/codemode.d.ts +16 -0
  37. package/dist/codemode.d.ts.map +1 -1
  38. package/dist/codemode.js +2 -2
  39. package/dist/{connection-loop-QmWL-fJf.d.ts → connection-loop-D_pRONCr.d.ts} +4 -4
  40. package/dist/{connection-loop-QmWL-fJf.d.ts.map → connection-loop-D_pRONCr.d.ts.map} +1 -1
  41. package/dist/contexts/daytona.d.ts +3 -3
  42. package/dist/contexts/docker.d.ts +1 -1
  43. package/dist/contexts/e2b.d.ts +2 -2
  44. package/dist/contexts/sandbox.d.ts +1 -1
  45. package/dist/{contexts-C72b0mMa.js → contexts-DLZTSRHU.js} +17 -106
  46. package/dist/contexts-DLZTSRHU.js.map +1 -0
  47. package/dist/contexts.d.ts +4 -4
  48. package/dist/contexts.js +2 -2
  49. package/dist/{credentials-wwETW8rI.js → credentials-Bqg-qNBM.js} +2 -2
  50. package/dist/{credentials-wwETW8rI.js.map → credentials-Bqg-qNBM.js.map} +1 -1
  51. package/dist/{cwd-picker-CcekODCt.js → cwd-picker-BsHA6HKv.js} +4 -4
  52. package/dist/{cwd-picker-CcekODCt.js.map → cwd-picker-BsHA6HKv.js.map} +1 -1
  53. package/dist/daemon-cli.js +4 -3
  54. package/dist/daemon-cli.js.map +1 -1
  55. package/dist/{decisions-CXmK0nz9.js → decisions-hsw5XS2g.js} +3 -1
  56. package/dist/decisions-hsw5XS2g.js.map +1 -0
  57. package/dist/{discovery-DycXFxdA.js → discovery-CX7nbMCn.js} +4 -4
  58. package/dist/{discovery-DycXFxdA.js.map → discovery-CX7nbMCn.js.map} +1 -1
  59. package/dist/durable/sqlite.d.ts +2 -2
  60. package/dist/durable.d.ts +1 -1
  61. package/dist/{engine-VQ86XGdG.js → engine-CxBGgU94.js} +6 -6
  62. package/dist/{engine-VQ86XGdG.js.map → engine-CxBGgU94.js.map} +1 -1
  63. package/dist/{eval-Bod0I1W_.js → eval-CfGUDAkN.js} +3 -3
  64. package/dist/{eval-Bod0I1W_.js.map → eval-CfGUDAkN.js.map} +1 -1
  65. package/dist/eval.d.ts +1 -1
  66. package/dist/eval.js +1 -1
  67. package/dist/{executor-C7RQrkEq.js → executor-j_LFRBwS.js} +4 -4
  68. package/dist/{executor-C7RQrkEq.js.map → executor-j_LFRBwS.js.map} +1 -1
  69. package/dist/{extension-mark-BqKdEZwF.js → extension-mark-_rhkDWL5.js} +9 -6
  70. package/dist/extension-mark-_rhkDWL5.js.map +1 -0
  71. package/dist/extensions/testing.d.ts +1 -1
  72. package/dist/extensions/testing.js +2 -2
  73. package/dist/{extensions-BCx4QuGB.js → extensions-ADlhBQ_1.js} +8 -8
  74. package/dist/{extensions-BCx4QuGB.js.map → extensions-ADlhBQ_1.js.map} +1 -1
  75. package/dist/{extensions-client-D8tEAcMw.d.ts → extensions-client-D4Q2XRKV.d.ts} +31 -5
  76. package/dist/extensions-client-D4Q2XRKV.d.ts.map +1 -0
  77. package/dist/extensions.d.ts +1 -1
  78. package/dist/extensions.js +6 -6
  79. package/dist/{fold-rules-DvLK8pbf.d.ts → fold-rules-1DpDwG9h.d.ts} +2 -2
  80. package/dist/{fold-rules-DvLK8pbf.d.ts.map → fold-rules-1DpDwG9h.d.ts.map} +1 -1
  81. package/dist/{google-Crva32sk.js → google-B-Dhmzzf.js} +2 -2
  82. package/dist/{google-Crva32sk.js.map → google-B-Dhmzzf.js.map} +1 -1
  83. package/dist/{headless-CCm-jLLT.js → headless-CQm19BL0.js} +4 -4
  84. package/dist/{headless-CCm-jLLT.js.map → headless-CQm19BL0.js.map} +1 -1
  85. package/dist/headless.d.ts +1 -1
  86. package/dist/headless.js +1 -1
  87. package/dist/history-worker.js +1 -1
  88. package/dist/{host-wO_6GW0y.js → host-C61ntFBC.js} +276 -103
  89. package/dist/host-C61ntFBC.js.map +1 -0
  90. package/dist/{host-BHMs9jsN.js → host-DEoEzQal.js} +13 -4
  91. package/dist/host-DEoEzQal.js.map +1 -0
  92. package/dist/{host-scoped-settings-CrbcqOkP.js → host-scoped-settings-WBYaCork.js} +2 -2
  93. package/dist/{host-scoped-settings-CrbcqOkP.js.map → host-scoped-settings-WBYaCork.js.map} +1 -1
  94. package/dist/{host-signals-Jsjd_-4g.js → host-signals-Cb2TlGtN.js} +5 -5
  95. package/dist/{host-signals-Jsjd_-4g.js.map → host-signals-Cb2TlGtN.js.map} +1 -1
  96. package/dist/{host-version-CNAr9pov.js → host-version-BIx4CuBM.js} +2 -2
  97. package/dist/{host-version-CNAr9pov.js.map → host-version-BIx4CuBM.js.map} +1 -1
  98. package/dist/{index-C6nnKu32.d.ts → index-B0PLo9jn.d.ts} +4 -4
  99. package/dist/{index-C6nnKu32.d.ts.map → index-B0PLo9jn.d.ts.map} +1 -1
  100. package/dist/{index-Djnc8Peh.d.ts → index-De4x-s76.d.ts} +14 -14
  101. package/dist/{index-Djnc8Peh.d.ts.map → index-De4x-s76.d.ts.map} +1 -1
  102. package/dist/{index-CGbOWa7y.d.ts → index-WlE93Q6d.d.ts} +411 -3
  103. package/dist/index-WlE93Q6d.d.ts.map +1 -0
  104. package/dist/{index-CCf_WeTQ.d.ts → index-f-XWIoYo.d.ts} +2 -2
  105. package/dist/{index-CCf_WeTQ.d.ts.map → index-f-XWIoYo.d.ts.map} +1 -1
  106. package/dist/index.d.ts +5 -5
  107. package/dist/index.js +17 -17
  108. package/dist/ipc/browser-types.d.ts +304 -7
  109. package/dist/ipc/browser-types.d.ts.map +1 -1
  110. package/dist/ipc/browser.d.ts +4 -4
  111. package/dist/ipc/browser.js +2 -2
  112. package/dist/ipc/remote.d.ts +6 -6
  113. package/dist/ipc/remote.js +3 -3
  114. package/dist/{ipc-BzWhIybr.js → ipc-DiH_P-wr.js} +4 -4
  115. package/dist/{ipc-BzWhIybr.js.map → ipc-DiH_P-wr.js.map} +1 -1
  116. package/dist/ipc.d.ts +10 -10
  117. package/dist/ipc.js +8 -8
  118. package/dist/{json-Dj-YATkZ.js → json-BjClr43H.js} +2 -20
  119. package/dist/json-BjClr43H.js.map +1 -0
  120. package/dist/{links-CJG_rFvf.js → links-BbC_lOnn.js} +9 -9
  121. package/dist/{links-CJG_rFvf.js.map → links-BbC_lOnn.js.map} +1 -1
  122. package/dist/{loaded-entry-DTURJo_R.js → loaded-entry-C0vkCVNQ.js} +2 -2
  123. package/dist/{loaded-entry-DTURJo_R.js.map → loaded-entry-C0vkCVNQ.js.map} +1 -1
  124. package/dist/mcp.d.ts +1 -1
  125. package/dist/{media-type-BtA0cbfI.d.ts → media-type-ZSmAGHvg.d.ts} +188 -7
  126. package/dist/media-type-ZSmAGHvg.d.ts.map +1 -0
  127. package/dist/{memory-DuVGrzbl.js → memory-C3w8nO8f.js} +132 -31
  128. package/dist/memory-C3w8nO8f.js.map +1 -0
  129. package/dist/model-test-CAtJ76kM.js.map +1 -1
  130. package/dist/{move-session-kylExJ5H.js → move-session-D7ILECs-.js} +4 -4
  131. package/dist/{move-session-kylExJ5H.js.map → move-session-D7ILECs-.js.map} +1 -1
  132. package/dist/{nested-call-record-DlzPeTB7.js → nested-call-record-OayszZBQ.js} +4 -3
  133. package/dist/nested-call-record-OayszZBQ.js.map +1 -0
  134. package/dist/output/stream-json.d.ts +1 -1
  135. package/dist/output/terminal.d.ts +1 -1
  136. package/dist/{path-display-COVV71Yu.js → path-display-DRAEpKOU.js} +3 -3
  137. package/dist/{path-display-COVV71Yu.js.map → path-display-DRAEpKOU.js.map} +1 -1
  138. package/dist/plugins.d.ts +1 -1
  139. package/dist/{prelude-CVKoUzV8.js → prelude-BwiSR1uA.js} +16 -5
  140. package/dist/prelude-BwiSR1uA.js.map +1 -0
  141. package/dist/{presets-CBt6rFfS.js → presets-CMY43nUP.js} +3 -3
  142. package/dist/{presets-CBt6rFfS.js.map → presets-CMY43nUP.js.map} +1 -1
  143. package/dist/presets.d.ts +1 -1
  144. package/dist/presets.js +1 -1
  145. package/dist/{protocol-CEVqBO-q.d.ts → protocol-C2lqKuMd.d.ts} +50 -7
  146. package/dist/protocol-C2lqKuMd.d.ts.map +1 -0
  147. package/dist/protocol-DOGyHgLQ.js.map +1 -1
  148. package/dist/protocol-NSllElLw.js.map +1 -1
  149. package/dist/providers/anthropic.d.ts +1 -1
  150. package/dist/providers/arcee.d.ts +1 -1
  151. package/dist/providers/baseten.d.ts +1 -1
  152. package/dist/providers/cerebras.d.ts +1 -1
  153. package/dist/providers/google.d.ts +1 -1
  154. package/dist/providers/google.js +1 -1
  155. package/dist/providers/local.d.ts +1 -1
  156. package/dist/providers/neuralwatt.d.ts +1 -1
  157. package/dist/providers/openai-compat.d.ts +1 -1
  158. package/dist/providers/openai.d.ts +1 -1
  159. package/dist/providers/openrouter.d.ts +1 -1
  160. package/dist/providers/xai.d.ts +1 -1
  161. package/dist/{providers-Z0TaBp1O.js → providers-CpXV_uLE.js} +5 -5
  162. package/dist/providers-CpXV_uLE.js.map +1 -0
  163. package/dist/providers.d.ts +1 -1
  164. package/dist/providers.js +1 -1
  165. package/dist/{readonly-db-D7Z-k0pQ.js → readonly-db-DrbT8tX6.js} +2 -2
  166. package/dist/{readonly-db-D7Z-k0pQ.js.map → readonly-db-DrbT8tX6.js.map} +1 -1
  167. package/dist/{registry-BKjeBnoy.js → registry-CJHvmvaP.js} +2 -2
  168. package/dist/{registry-BKjeBnoy.js.map → registry-CJHvmvaP.js.map} +1 -1
  169. package/dist/{registry-B6Ww76iW.js → registry-CN6JlZKj.js} +2 -2
  170. package/dist/{registry-B6Ww76iW.js.map → registry-CN6JlZKj.js.map} +1 -1
  171. package/dist/{registry-B9INJwJR.js → registry-CnnKaY9s.js} +324 -39
  172. package/dist/registry-CnnKaY9s.js.map +1 -0
  173. package/dist/{remote-hosts-BMWoHCWO.js → remote-hosts-DoO9CJUQ.js} +4 -4
  174. package/dist/{remote-hosts-BMWoHCWO.js.map → remote-hosts-DoO9CJUQ.js.map} +1 -1
  175. package/dist/{repo-url-D4Dr8NfZ.js → repo-url-DLjjuVk_.js} +169 -2
  176. package/dist/repo-url-DLjjuVk_.js.map +1 -0
  177. package/dist/restate.d.ts +2 -2
  178. package/dist/{sandbox-BwiPGKF7.d.ts → sandbox-B4OW6oNx.d.ts} +2 -2
  179. package/dist/{sandbox-BwiPGKF7.d.ts.map → sandbox-B4OW6oNx.d.ts.map} +1 -1
  180. package/dist/script-progress-CrZ507Qq.js +900 -0
  181. package/dist/script-progress-CrZ507Qq.js.map +1 -0
  182. package/dist/session/sqlite.d.ts +1 -1
  183. package/dist/session.d.ts +1 -1
  184. package/dist/{setting-BQBSyUq0.js → setting-D_FpKFMw.js} +6 -6
  185. package/dist/{setting-BQBSyUq0.js.map → setting-D_FpKFMw.js.map} +1 -1
  186. package/dist/{settings-catalog-4IU2i_jG.js → settings-catalog-DEZlXP51.js} +14 -2
  187. package/dist/settings-catalog-DEZlXP51.js.map +1 -0
  188. package/dist/{settings-keys-iI_Y-7MC.js → settings-keys-CW2UgHgb.js} +3 -3
  189. package/dist/{settings-keys-iI_Y-7MC.js.map → settings-keys-CW2UgHgb.js.map} +1 -1
  190. package/dist/{skills-CFXRItJ6.js → skills-CEbosUlM.js} +2 -2
  191. package/dist/{skills-CFXRItJ6.js.map → skills-CEbosUlM.js.map} +1 -1
  192. package/dist/skills.d.ts +1 -1
  193. package/dist/skills.js +1 -1
  194. package/dist/{source-Bl1Z501r.js → source-3ZnEPOSa.js} +3 -2
  195. package/dist/source-3ZnEPOSa.js.map +1 -0
  196. package/dist/{sqlite-runtime-BZ2L4RaG.d.ts → sqlite-runtime-CS_LBaAN.d.ts} +2 -2
  197. package/dist/sqlite-runtime-CS_LBaAN.d.ts.map +1 -0
  198. package/dist/{src-BY_ND2xn.js → src-BmTXDta8.js} +2 -2
  199. package/dist/{src-BY_ND2xn.js.map → src-BmTXDta8.js.map} +1 -1
  200. package/dist/{store-C6gI7FfZ.js → store-ByCDaKgJ.js} +14 -3
  201. package/dist/store-ByCDaKgJ.js.map +1 -0
  202. package/dist/{system-freeze-m5o1sGIM.js → system-freeze-XgDPIwQS.js} +3 -3
  203. package/dist/{system-freeze-m5o1sGIM.js.map → system-freeze-XgDPIwQS.js.map} +1 -1
  204. package/dist/{todo-edits-CbukbjyV.js → todo-edits-Dyj32ds6.js} +4 -3
  205. package/dist/todo-edits-Dyj32ds6.js.map +1 -0
  206. package/dist/tools/fetch-url.d.ts +1 -1
  207. package/dist/tools/web-search.d.ts +1 -1
  208. package/dist/{tools-CVTGhggj.js → tools-BsrGQ8K4.js} +2 -2
  209. package/dist/{tools-CVTGhggj.js.map → tools-BsrGQ8K4.js.map} +1 -1
  210. package/dist/tools.d.ts +1 -1
  211. package/dist/tools.js +3 -3
  212. package/dist/{transcript-anchors-CiSi_UBn.js → transcript-anchors-DMjvyUjk.js} +3 -3
  213. package/dist/{transcript-anchors-CiSi_UBn.js.map → transcript-anchors-DMjvyUjk.js.map} +1 -1
  214. package/dist/{transcript-reconcile-jbSBjLq2.js → transcript-reconcile-DpQO85wl.js} +3 -3
  215. package/dist/{transcript-reconcile-jbSBjLq2.js.map → transcript-reconcile-DpQO85wl.js.map} +1 -1
  216. package/dist/tui.d.ts +9 -9
  217. package/dist/tui.d.ts.map +1 -1
  218. package/dist/tui.js +289 -38
  219. package/dist/tui.js.map +1 -1
  220. package/dist/{types-bDQagoiL.d.ts → types-2HrgolQz.d.ts} +20 -8
  221. package/dist/types-2HrgolQz.d.ts.map +1 -0
  222. package/dist/types-4q1bFG53.js +122 -0
  223. package/dist/types-4q1bFG53.js.map +1 -0
  224. package/dist/{types-DnRd9_cA.d.ts → types-SXqk6hLD.d.ts} +3 -3
  225. package/dist/{types-DnRd9_cA.d.ts.map → types-SXqk6hLD.d.ts.map} +1 -1
  226. package/dist/types-myxheQkw.js.map +1 -1
  227. package/dist/types.d.ts +3 -3
  228. package/dist/{usage-summary-BfcXjiSM.js → usage-summary-BM7B3JdI.js} +2 -2
  229. package/dist/{usage-summary-BfcXjiSM.js.map → usage-summary-BM7B3JdI.js.map} +1 -1
  230. package/dist/{version-DqqqvHDP.js → version-szdW1Od9.js} +2 -2
  231. package/dist/{version-DqqqvHDP.js.map → version-szdW1Od9.js.map} +1 -1
  232. package/dist/{wait-task-BX4NcOOm.js → wait-task-BiI5UaZQ.js} +13 -3
  233. package/dist/wait-task-BiI5UaZQ.js.map +1 -0
  234. package/dist/{wire-CLm2rMK0.js → wire-C3l9jl9x.js} +2 -2
  235. package/dist/{wire-CLm2rMK0.js.map → wire-C3l9jl9x.js.map} +1 -1
  236. package/dist/{write-file-0E6TUJ9A.js → write-file-Dquu7MzB.js} +5 -3
  237. package/dist/write-file-Dquu7MzB.js.map +1 -0
  238. package/dist/{ws-DfGGYQ4W.js → ws-CEHi0CiB.js} +4 -4
  239. package/dist/{ws-DfGGYQ4W.js.map → ws-CEHi0CiB.js.map} +1 -1
  240. package/dist/{ws-client-CFMbhsNq.js → ws-client-t8XNWxcq.js} +2 -2
  241. package/dist/{ws-client-CFMbhsNq.js.map → ws-client-t8XNWxcq.js.map} +1 -1
  242. package/docs/CHAT.md +55 -0
  243. package/docs/CODEMODE.md +37 -0
  244. package/docs/EXTENSIONS.md +8 -6
  245. package/docs/RELEASE-NOTES-NEXT.md +48 -1
  246. package/docs/RELEASE.md +9 -1
  247. package/package.json +2 -3
  248. package/dist/agent-DJhpTS-B.js.map +0 -1
  249. package/dist/auto-update-restart-BFB7Ek87.js.map +0 -1
  250. package/dist/browser-BcoCCB_n.js.map +0 -1
  251. package/dist/contexts-C72b0mMa.js.map +0 -1
  252. package/dist/decisions-CXmK0nz9.js.map +0 -1
  253. package/dist/extension-mark-BqKdEZwF.js.map +0 -1
  254. package/dist/extensions-client-D8tEAcMw.d.ts.map +0 -1
  255. package/dist/host-BHMs9jsN.js.map +0 -1
  256. package/dist/host-wO_6GW0y.js.map +0 -1
  257. package/dist/index-CGbOWa7y.d.ts.map +0 -1
  258. package/dist/json-Dj-YATkZ.js.map +0 -1
  259. package/dist/media-type-BtA0cbfI.d.ts.map +0 -1
  260. package/dist/memory-DuVGrzbl.js.map +0 -1
  261. package/dist/nested-call-events-BYk8XvGX.js +0 -182
  262. package/dist/nested-call-events-BYk8XvGX.js.map +0 -1
  263. package/dist/nested-call-record-DlzPeTB7.js.map +0 -1
  264. package/dist/prelude-CVKoUzV8.js.map +0 -1
  265. package/dist/protocol-CEVqBO-q.d.ts.map +0 -1
  266. package/dist/providers-Z0TaBp1O.js.map +0 -1
  267. package/dist/registry-B9INJwJR.js.map +0 -1
  268. package/dist/repo-url-D4Dr8NfZ.js.map +0 -1
  269. package/dist/settings-catalog-4IU2i_jG.js.map +0 -1
  270. package/dist/source-Bl1Z501r.js.map +0 -1
  271. package/dist/sqlite-runtime-BZ2L4RaG.d.ts.map +0 -1
  272. package/dist/store-C6gI7FfZ.js.map +0 -1
  273. package/dist/todo-edits-CbukbjyV.js.map +0 -1
  274. package/dist/types-DTMEhKJO.js +0 -14
  275. package/dist/types-DTMEhKJO.js.map +0 -1
  276. package/dist/types-bDQagoiL.d.ts.map +0 -1
  277. package/dist/wait-task-BX4NcOOm.js.map +0 -1
  278. package/dist/write-file-0E6TUJ9A.js.map +0 -1
  279. package/scripts/eval.ts +0 -179
@@ -1 +1 @@
1
- {"version":3,"file":"src-BY_ND2xn.js","names":["prefixed"],"sources":["../src/behaviors/local-reliability.ts","../src/chat/inline-autocomplete.ts","../src/github/gh-cli.ts","../src/github/state-file.ts","../src/mcp/login.ts","../src/metrics-hooks.ts","../src/metrics.ts","../src/mutation-validation.ts","../src/perf/event-loop.ts","../src/perf/ps.ts","../src/perf/gpu.ts","../src/perf/os-threads.ts","../src/perf/owned.ts","../src/perf/processes.ts","../src/perf/ring.ts","../src/perf/sampler.ts","../src/perf/host.ts","../src/tracing-attrs.ts","../src/tracing-decision-hooks.ts","../src/tracing-mcp-hooks.ts","../src/tracing-run-hooks.ts","../src/tracing-runtime.ts","../src/tracing-spawn-hooks.ts","../src/tracing-tool-hooks.ts","../src/tracing.ts"],"sourcesContent":["import type { AgentBehavior } from '../types'\n\nconst DEFAULT_LOCAL_RELIABILITY: AgentBehavior = {\n maxConcurrentTools: 3,\n readFileDefaults: {\n limit: 400,\n maxBytes: 64 * 1024,\n },\n toolOutputBudget: 32 * 1024,\n textToolCallRescue: { mode: 'steer' },\n thinkingOverrunPolicy: {\n mode: 'steer-next-turn',\n thresholdTokens: 2048,\n },\n}\n\n/**\n * Opt-in behavior overlay for brittle/local OpenAI-compatible endpoints.\n * Returns plain `AgentBehavior` so hosts can spread it before or after their\n * own overrides; no built-in profile uses this helper by default.\n * @param overrides - Behavior fields that replace the defaults. `readFileDefaults` is merged key by key.\n */\nexport function localReliabilityBehavior(overrides: AgentBehavior = {}): AgentBehavior {\n return {\n ...DEFAULT_LOCAL_RELIABILITY,\n ...overrides,\n readFileDefaults: {\n ...DEFAULT_LOCAL_RELIABILITY.readFileDefaults,\n ...(overrides.readFileDefaults ?? {}),\n },\n textToolCallRescue: overrides.textToolCallRescue ?? DEFAULT_LOCAL_RELIABILITY.textToolCallRescue,\n thinkingOverrunPolicy: overrides.thinkingOverrunPolicy ?? DEFAULT_LOCAL_RELIABILITY.thinkingOverrunPolicy,\n }\n}\n","/**\n * Inline (ghost-text) autocomplete for the prompt.\n *\n * Distinct from the trigger-based completion popover (`@` files, `/`\n * commands): providers here suggest plain text SUFFIX candidates to\n * append at the cursor — the first renders as dim ghost text, up/down\n * rotates through the rest, Tab accepts. No popover.\n *\n * Providers are queried in registration order with a shared AbortSignal;\n * the engine returns the first provider's non-empty candidate list.\n * Extensions add providers via `ctx.registerInlineAutocompleteProvider`\n * — the built-in path provider ({@link createPathAutocompleteProvider})\n * is always first.\n */\n\nimport { readdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\n\nexport { type InlineGhost, patchInlineGhost } from './inline-ghost'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Input handed to an {@link InlineAutocompleteProvider} on each query. */\nexport interface InlineAutocompleteContext {\n /** Full prompt buffer. */\n text: string\n /** Cursor offset into `text`. Suggestions complete the token ENDING here. */\n cursor: number\n /** Workspace directory relative paths resolve against. */\n cwd: string\n /** Aborted when the buffer changes before the provider resolves. */\n signal: AbortSignal\n}\n\n/**\n * Source of inline ghost-text suggestions. Providers are queried in order by {@link resolveInlineSuggestion}.\n */\nexport interface InlineAutocompleteProvider {\n /** Stable id — used for first-wins dedupe across extensions. */\n id: string\n /**\n * Per-provider deadline override (ms), capped at\n * {@link MAX_PROVIDER_DEADLINE_MS}. The default\n * ({@link PROVIDER_DEADLINE_MS}) suits instant sources like directory\n * listings; a provider backed by a slow source (e.g. a local LLM) can\n * extend its own budget — the wait is async (typing is never blocked,\n * an edit aborts the query) and only delays providers ordered AFTER\n * this one.\n */\n deadlineMs?: number\n /**\n * Return the ghost suffix (or an ordered list of candidate suffixes —\n * the host shows the first and lets the user rotate with up/down) to\n * append at the cursor, or null/''/[] for no suggestion. Must respect\n * its deadline budget — it runs per keystroke (debounced by the\n * host).\n */\n suggest: (ctx: InlineAutocompleteContext) => string | readonly string[] | null | Promise<string | readonly string[] | null>\n}\n\n// ---------------------------------------------------------------------------\n// Engine\n// ---------------------------------------------------------------------------\n\n/** Default per-provider deadline — a hung provider is treated as \"no suggestion\". */\nconst PROVIDER_DEADLINE_MS = 150\n/** Hard cap on a provider's `deadlineMs` — past this, waiting for a ghost reads as broken. */\nexport const MAX_PROVIDER_DEADLINE_MS = 2_000\n\n/**\n * Query `providers` in order; the first provider yielding a non-empty\n * candidate list wins (a bare string normalizes to a 1-element list).\n * Provider errors are swallowed (a broken extension provider must not\n * break typing), and each provider races its deadline —\n * {@link PROVIDER_DEADLINE_MS} by default, a provider's own `deadlineMs`\n * (capped at {@link MAX_PROVIDER_DEADLINE_MS}) when declared — so one\n * hung provider can't permanently suppress the ghosts of providers\n * behind it. Returns null when aborted or nothing matched.\n *\n * @param providers - Providers in query order.\n * @param ctx - Prompt buffer, cursor, working directory, and abort signal.\n * @param deadlineMs - Per-provider deadline in milliseconds when the provider declares none. Defaults to 150.\n * @param maxDeadlineMs - Cap applied to a provider's own `deadlineMs`. Defaults to {@link MAX_PROVIDER_DEADLINE_MS}.\n */\nexport async function resolveInlineSuggestion(\n providers: readonly InlineAutocompleteProvider[],\n ctx: InlineAutocompleteContext,\n deadlineMs = PROVIDER_DEADLINE_MS,\n // Injectable cap — the host always uses the real MAX_PROVIDER_DEADLINE_MS;\n // tests pass a small value to prove clamping without a wall-clock stall.\n maxDeadlineMs = MAX_PROVIDER_DEADLINE_MS,\n): Promise<{ suffixes: readonly string[], providerId: string } | null> {\n const TIMEOUT = Symbol('timeout')\n for (const provider of providers) {\n if (ctx.signal.aborted)\n return null\n try {\n let timer: ReturnType<typeof setTimeout> | undefined\n const budget = provider.deadlineMs !== undefined\n ? Math.min(Math.max(provider.deadlineMs, 1), maxDeadlineMs)\n : deadlineMs\n const result = await Promise.race([\n Promise.resolve(provider.suggest(ctx)),\n new Promise<typeof TIMEOUT>((r) => { timer = setTimeout(r, budget, TIMEOUT) }),\n ])\n if (timer !== undefined)\n clearTimeout(timer)\n if (result === TIMEOUT)\n continue\n if (ctx.signal.aborted)\n return null\n const suffixes = (typeof result === 'string' ? [result] : result ?? [])\n .filter(s => typeof s === 'string' && s.length > 0)\n if (suffixes.length > 0)\n return { suffixes, providerId: provider.id }\n }\n catch {\n // Isolation-first: skip broken providers.\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Built-in path provider\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the whitespace-delimited token that ends at `cursor`. Returns\n * null when the cursor sits right after whitespace (nothing to complete).\n *\n * @param text - Prompt buffer.\n * @param cursor - Cursor offset into `text`.\n */\nexport function tokenEndingAtCursor(text: string, cursor: number): string | null {\n const before = text.slice(0, cursor)\n const match = before.match(/\\S+$/)\n return match ? match[0] : null\n}\n\n/**\n * Does this token carry an explicit path shape (`/` separator or a `~`\n * prefix)? Path-shaped tokens complete from the first character after\n * the separator; bare tokens (`sr` → `c/`) still complete against the\n * cwd listing but need ≥2 chars first — see the provider.\n */\nfunction looksLikePath(token: string): boolean {\n return token.includes('/') || token.startsWith('~')\n}\n\n/**\n * Cap on candidates returned for an ambiguous fragment — rotation past\n * a dozen-odd entries stops being faster than typing another character.\n */\nconst MAX_PATH_CANDIDATES = 16\n\n/** Directory listing seam — injectable for tests. */\nexport interface PathProviderIo {\n readdir?: (dir: string) => { name: string, isDirectory: boolean }[]\n}\n\nfunction defaultReaddir(dir: string): { name: string, isDirectory: boolean }[] {\n return readdirSync(dir, { withFileTypes: true }).map(e => ({ name: e.name, isDirectory: e.isDirectory() }))\n}\n\n/**\n * Built-in provider: complete filesystem paths under `cwd` (or absolute /\n * `~` paths). Given the token `src/chat/comp`, lists `src/chat/` and\n * returns every matching continuation of `comp` as a candidate list —\n * directories first, alphabetical, `/`-terminated for directories so\n * typing can continue. The host ghosts the first candidate and rotates\n * through the rest on up/down.\n *\n * Bare tokens (no `/` yet) are matched against the cwd listing too, so\n * a directory ghost appears as soon as the first characters of `src`\n * are typed — but only from the second character on, so 1-letter prose\n * words don't ghost-spam.\n *\n * Dotfiles are only offered when the typed fragment itself starts with\n * `.` — the usual completion convention.\n *\n * @param opts - Optional working-directory getter and directory-listing override.\n */\nexport function createPathAutocompleteProvider(opts: {\n cwd?: () => string\n io?: PathProviderIo\n} = {}): InlineAutocompleteProvider {\n const readdir = opts.io?.readdir ?? defaultReaddir\n // Prefer the per-invocation `ctx.cwd` (host truth); fall back to the\n // factory-supplied getter, then `process.cwd()`.\n const fallbackCwd = opts.cwd ?? (() => process.cwd())\n return {\n id: 'paths',\n suggest(ctx) {\n const token = tokenEndingAtCursor(ctx.text, ctx.cursor)\n if (!token)\n return null\n // Bare tokens complete against the cwd listing (dirname('sr') is '.',\n // so the resolution below just works) but require ≥2 typed chars.\n if (!looksLikePath(token) && token.length < 2)\n return null\n // Resolve the directory being completed and the fragment typed so\n // far. A trailing `/` means \"complete entries of this directory\".\n const expanded = token.startsWith('~')\n ? join(homedir(), token.slice(1))\n : token\n const endsWithSep = expanded.endsWith('/')\n const dirPart = endsWithSep ? expanded : dirname(expanded)\n const fragment = endsWithSep ? '' : basename(expanded)\n const baseCwd = ctx.cwd || fallbackCwd()\n const dir = isAbsolute(dirPart) ? dirPart : resolve(baseCwd, dirPart)\n let entries: { name: string, isDirectory: boolean }[]\n try {\n entries = readdir(dir)\n }\n catch {\n return null\n }\n const matches = entries.filter(e =>\n e.name.startsWith(fragment)\n && (fragment.startsWith('.') || !e.name.startsWith('.')),\n )\n // Directories first (they're what you're navigating through), then\n // alphabetical within each group.\n matches.sort((a, b) =>\n a.isDirectory !== b.isDirectory\n ? (a.isDirectory ? -1 : 1)\n : a.name.localeCompare(b.name))\n const suffixes = matches\n .map(e => e.name.slice(fragment.length) + (e.isDirectory ? '/' : ''))\n .filter(s => s.length > 0)\n .slice(0, MAX_PATH_CANDIDATES)\n return suffixes.length > 0 ? suffixes : null\n },\n }\n}\n","/**\n * Reading the `gh` CLI's rate-limit failures. `gh` prints GitHub's message on\n * stderr and exits non-zero; its wording has moved between versions, so the\n * match is loose and a miss is just a generic failure.\n */\n\n/**\n * `primary` (the hourly quota is spent), `secondary` (too fast), or `null`.\n * @param stderr - Standard error text of a failed `gh` command.\n * Mirrored by `ghRateLimitKind` in `extensions/git-flow/shared.ts`: change both together.\n */\nexport function isGhRateLimitError(stderr: string): 'primary' | 'secondary' | null {\n if (/secondary rate limit|abuse detection|was submitted too quickly/i.test(stderr))\n return 'secondary'\n if (/(?:api )?rate limit (?:already )?exceeded|rate[- ]limited/i.test(stderr))\n return 'primary'\n return null\n}\n\n/**\n * The reset of a quota from `gh api rate_limit` output (epoch seconds in\n * `resources.<name>.reset`), as local epoch ms; `undefined` when unreadable.\n * @param json - Output of `gh api rate_limit`.\n * @param resource - Quota to read the reset of.\n */\nexport function rateLimitResetFromJson(json: string, resource: 'core' | 'graphql' = 'graphql'): number | undefined {\n try {\n const body = JSON.parse(json) as { resources?: Record<string, { reset?: unknown }> }\n const reset = body.resources?.[resource]?.reset\n return typeof reset === 'number' && Number.isFinite(reset) ? reset * 1000 : undefined\n }\n catch {\n return undefined\n }\n}\n","/**\n * The file half of {@link GithubStateStore}: `<dir>/github-state.json`,\n * shared by every zidane process on the machine. Split from `state.ts`\n * because it needs `node:fs`, and the PR helpers reach browser bundles\n * through `zidane/chat/pure`.\n */\nimport type { GithubState, GithubStateStore } from './state'\nimport { readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { OWNER_ONLY, writeFileAtomic } from '../atomic-write'\nimport { emptyGithubState, mergeGithubState, parseGithubState, pruneGithubState } from './state'\n\nexport const GITHUB_STATE_FILENAME = 'github-state.json'\n\n/** How long quota observations wait before reaching disk (pauses don't wait). */\nconst WRITE_DEBOUNCE_MS = 500\n\n/**\n * State backed by `<dir>/github-state.json`. Reads re-parse only when the\n * file's mtime moved; writes merge with what is on disk first, so two\n * processes writing at once lose nothing that matters. A failed write\n * (read-only home, full disk) switches the store to memory only.\n * @param dir - Directory that holds `github-state.json`.\n * @param opts - Optional clock override, `now`, in epoch milliseconds.\n */\nexport function createFileGithubStateStore(dir: string, opts: { now?: () => number } = {}): GithubStateStore {\n const now = opts.now ?? Date.now\n const path = join(dir, GITHUB_STATE_FILENAME)\n let state: GithubState = emptyGithubState()\n let mtime = -1\n let timer: ReturnType<typeof setTimeout> | null = null\n let broken = false\n\n const readDisk = (): GithubState | null => {\n try {\n const stat = statSync(path)\n if (stat.mtimeMs === mtime)\n return null\n mtime = stat.mtimeMs\n return parseGithubState(readFileSync(path, 'utf8'))\n }\n catch {\n return null\n }\n }\n\n const refresh = () => {\n if (broken)\n return\n const disk = readDisk()\n if (disk)\n state = mergeGithubState(disk, state)\n }\n\n function flush(): void {\n if (timer) {\n clearTimeout(timer)\n timer = null\n process.removeListener('exit', flush)\n }\n if (broken)\n return\n refresh()\n pruneGithubState(state, now())\n try {\n writeFileAtomic(path, JSON.stringify(state), OWNER_ONLY)\n try {\n mtime = statSync(path).mtimeMs\n }\n catch { /* re-read next time */ }\n }\n catch {\n broken = true\n }\n }\n\n return {\n read: () => {\n refresh()\n return state\n },\n update: (change, o) => {\n refresh()\n change(state)\n if (o?.urgent) {\n flush()\n return\n }\n if (!timer) {\n timer = setTimeout(flush, WRITE_DEBOUNCE_MS)\n ;(timer as { unref?: () => void }).unref?.()\n // The timer is unref'd: a process quitting with a write pending flushes it on exit.\n process.once('exit', flush)\n }\n },\n }\n}\n","/**\n * Interactive OAuth login orchestrator for one MCP server.\n *\n * Composes the three pieces shipped separately:\n * - `startOAuthCallback` (`./oauth-callback`) — local loopback HTTP\n * listener that catches the `?code=...` redirect.\n * - `McpOAuthProvider` (`./oauth-provider`) — SDK adapter that persists\n * tokens / client info via an injected `McpCredentialStore`.\n * - The MCP SDK's transport `authProvider` + `finishAuth` plumbing —\n * `client.connect()` triggers the SDK to call `redirectToAuthorization`\n * (which opens the browser); we wait for the loopback callback to\n * deliver the code, hand it to `transport.finishAuth`, and reconnect.\n *\n * Returns once tokens are persisted. Caller decides whether to immediately\n * reconnect the server (re-run `connectMcpServers` / `agent.warmup()`) or\n * defer to the next session activation. The returned `tools` array is\n * provided as a convenience — callers that want the connection live in\n * this call rather than rebuilding can wire them in directly.\n */\n\nimport type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { McpServerConfig } from '../types'\nimport type { McpOAuthCallbackPageRenderer, OAuthCallbackHandle } from './oauth-callback'\nimport type { McpCredentialStore, McpOAuthProvider } from './oauth-provider'\nimport { randomBytes } from 'node:crypto'\nimport { expandMcpConfigEnv } from './index'\nimport { startOAuthCallback } from './oauth-callback'\nimport { isOAuthTokenRejectionError, mcpOAuthProviderFor } from './oauth-provider'\nimport { sseToJsonFetchIfNeeded } from './sse-to-json-fetch'\nimport { createTolerantClient } from './tolerant-client'\n\n/** Options for {@link loginMcpServer}. */\nexport interface LoginMcpServerOptions {\n /** Persistence — same store the bootstrap path reads from. */\n store: McpCredentialStore\n /**\n * Invoked with the authorization URL once it's ready. Hosts typically\n * (a) emit `mcp:auth:url` for the TUI, and (b) call `tryOpenBrowser`.\n * The URL is identical to the one passed to the `mcp:auth:url` hook\n * fired automatically — this callback is a synchronous hook for callers\n * that don't want to wire the agent hook machinery.\n */\n onAuthorizationUrl?: (url: URL) => void | Promise<void>\n /** Cancels the flow (esc / close modal / SIGINT). */\n signal?: AbortSignal\n /**\n * Renders the post-redirect browser page — hosts pass their themed\n * renderer (zidane's TUI: `renderDefaultCallbackPage`) so MCP logins\n * end on the same screen as provider OAuth logins. The server name\n * rides as `page.provider`.\n */\n renderCallbackPage?: McpOAuthCallbackPageRenderer\n /** Agent hooks. The flow emits `mcp:auth:url`/`success`/`error` when wired. */\n hooks?: Hookable<AgentHooks>\n /** Override `client_name` shown on consent screens. Default: 'zidane'. */\n clientName?: string\n /** Override the requested OAuth scope. */\n scope?: string\n /**\n * Override the loopback callback path. Default: `/callback`. Useful only\n * for servers that pinned a different path during registration. Ignored\n * when `oauth.callbackPort` is set (the pinned redirect is always `/callback`).\n */\n callbackPath?: string\n /**\n * Maximum time to wait for the user to complete the browser flow, in ms.\n * The user can also cancel via `signal`. Default: 5 minutes.\n */\n timeoutMs?: number\n /**\n * Maximum time for the PRE-authorization phase — the `client.connect()`\n * that triggers discovery, dynamic registration, and the authorization\n * redirect. Default: 30s.\n *\n * Separate from {@link timeoutMs} because it covers machine-speed HTTP,\n * not a human in a browser. It exists because the MCP SDK's `connect()`\n * accepts no `AbortSignal`: an unreachable or slow-loris server would\n * otherwise hang here forever, un-cancellable, with the host UI parked on\n * a spinner that no keystroke can dismiss.\n */\n connectTimeoutMs?: number\n /**\n * Where the code comes from instead of a loopback listener of this\n * process: a remote sign-in, whose browser redirects to the CLIENT's\n * loopback (`redirectUri`), the client forwarding the code. With it, no\n * listener is bound here.\n */\n callback?: OAuthCallbackHandle\n /**\n * OAuth `state` for the authorization URL (`McpOAuthProviderOptions.state`); the caller checks it.\n * Omitted: a random one is generated and checked against the loopback callback here\n * (some servers, e.g. CircleCI, reject an authorization request without `state`).\n */\n state?: string\n}\n\n/** Outcome of a successful {@link loginMcpServer} flow. */\nexport interface LoginMcpServerResult {\n /** Stored OAuth tokens after a successful exchange. */\n tokens: NonNullable<ReturnType<McpOAuthProvider['tokens']>>\n /**\n * Upstream tool descriptors discovered after re-connecting with the new\n * tokens. The server's `enabledTools` / `disabledTools` filters are NOT\n * applied here — that's a bootstrap concern. Hosts that want filtering\n * should pass the result through `connectMcpServers` rebuild on the next\n * session activation rather than reusing this list verbatim.\n */\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>\n}\n\nconst DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60_000\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000\n\n/**\n * Run the full interactive OAuth flow for `config`. Only supports `sse` and\n * `streamable-http` transports — `stdio` MCP servers don't speak OAuth.\n *\n * Throws on:\n * - Wrong transport.\n * - Abort signal.\n * - Browser-side error (user denied, server rejected, etc.).\n * - Code exchange failure.\n * - Post-exchange connect failure.\n *\n * Always closes the loopback callback server before returning, success or\n * failure.\n * @param config - Server to sign in to. Must use the `sse` or `streamable-http` transport and have a `url`.\n * @param options - Credential store and flow settings. See {@link LoginMcpServerOptions}.\n */\nexport async function loginMcpServer(\n config: McpServerConfig,\n options: LoginMcpServerOptions,\n): Promise<LoginMcpServerResult> {\n if (config.transport !== 'sse' && config.transport !== 'streamable-http')\n throw new Error(`MCP OAuth: can't sign in over \"${config.transport}\". Only sse and streamable-http support it.`)\n if (!config.url)\n throw new Error(`MCP OAuth: server \"${config.name}\" is missing a url`)\n\n const hooks = options.hooks\n\n // Flipped in `finally`. A lost `raceAbort` ABANDONS `client.connect()`\n // rather than cancelling it (the SDK takes no signal), so the SDK can\n // reach the authorization step long after this function already threw —\n // timeout or abort. The `onAuthorizationUrl` closure below must go dead\n // at that point: a late `mcp:auth:url` would resurrect the host's auth UI\n // (and pop a browser) for a flow whose loopback callback server is\n // already closed and which nothing is driving.\n let settled = false\n\n let callback: OAuthCallbackHandle | undefined\n try {\n callback = options.callback ?? await startOAuthCallback({\n signal: options.signal,\n // A pre-registered client's redirect must match the one registered\n // with the provider, so its port is pinned (`oauth.callbackPort`) and\n // its path stays the default `/callback` (`pinnedMcpRedirectUri`).\n ...(config.oauth?.callbackPort !== undefined\n ? { port: config.oauth.callbackPort }\n : { path: options.callbackPath }),\n ...(options.renderCallbackPage ? { renderPage: options.renderCallbackPage } : {}),\n provider: config.name,\n })\n\n const handle = callback\n const state = options.state ?? randomBytes(24).toString('base64url')\n const provider = mcpOAuthProviderFor(config, {\n store: options.store,\n redirectUri: handle.redirectUri,\n clientName: options.clientName,\n ...(options.scope !== undefined ? { scope: options.scope } : {}),\n state,\n onAuthorizationUrl: async (url) => {\n // Late callback from an abandoned connect — see `settled` above.\n // The signal check additionally covers an abort that races the\n // authorization step while the flow is still nominally live.\n if (settled || options.signal?.aborted)\n return\n await hooks?.callHook('mcp:auth:url', { name: config.name, url: url.toString() })\n await options.onAuthorizationUrl?.(url)\n },\n })\n\n let transport = await createInteractiveTransport(config, provider)\n const client = await createTolerantClient({ name: 'zidane', version: '1.0.0' })\n\n // First connect → SDK has no tokens, so it calls\n // `provider.redirectToAuthorization(url)` (which routes to our\n // `onAuthorizationUrl`), then throws `UnauthorizedError`. This is the\n // expected path — we catch and wait on the loopback for the code.\n //\n // With STALE tokens stored, the SDK first attempts a refresh instead —\n // and when the refresh token itself is dead, `auth()` rethrows the\n // token-endpoint rejection (`InvalidGrantError`) WITHOUT opening the\n // browser. Wipe the dead tokens and retry once: the tokenless retry\n // routes straight to the authorization redirect. Without this, a\n // revoked refresh token would make the panel re-login itself fail — the one\n // flow that's supposed to recover from dead credentials.\n let needsAuth = false\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n for (let attempt = 0; ; attempt++) {\n try {\n // Bounded + cancellable: `connect()` takes no signal, so we race it\n // rather than await it bare. The losing connect keeps running in the\n // background until its own sockets give up — acceptable, because the\n // alternative is a host UI stuck on \"starting login flow…\" with no\n // way out.\n await raceAbort(\n client.connect(transport),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not respond within ${connectTimeoutMs}ms. The server may be unreachable.`,\n )\n break\n }\n catch (err) {\n if (isUnauthorizedError(err)) {\n needsAuth = true\n break\n }\n if (attempt === 0 && isOAuthTokenRejectionError(err)) {\n await provider.invalidateCredentials('tokens')\n await transport.close().catch(() => {})\n transport = await createInteractiveTransport(config, provider)\n continue\n }\n await client.close().catch(() => {})\n throw err\n }\n }\n\n if (needsAuth) {\n const { code, state: returnedState } = await raceLoginCallback(handle, options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS, options.signal)\n if (options.state === undefined && returnedState !== state)\n throw new Error(`MCP OAuth: \"${config.name}\" returned a mismatched state; the sign-in was refused. Start it again.`)\n // SDK exchanges code → tokens; provider.saveTokens persists them.\n await (transport as { finishAuth: (code: string) => Promise<void> }).finishAuth(code)\n // Reconnect with a FRESH transport. The previous one is already in\n // the \"started\" state from the failed connect — reusing it throws\n // \"StreamableHTTPClientTransport already started!\" inside\n // `transport.start()`. The SDK's canonical example\n // (`simpleOAuthClient.js`) uses the same pattern: recurse with a\n // new transport, reuse the client. Close the stale transport\n // first so its abort controllers + sockets don't leak.\n await transport.close().catch(() => {})\n const freshTransport = await createInteractiveTransport(config, provider)\n // Bounded for the same reason as the first connect: tokens are already\n // persisted at this point, so a server that stops responding here must\n // surface as an error the user can retry from, not a permanent spinner.\n await raceAbort(\n client.connect(freshTransport),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not respond after the token exchange (within ${connectTimeoutMs}ms)`,\n )\n }\n\n const { tools } = await raceAbort(\n client.listTools(),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not return its tool list within ${connectTimeoutMs}ms`,\n )\n await client.close()\n\n const tokens = provider.tokens()\n if (!tokens)\n throw new Error(`MCP OAuth: login for \"${config.name}\" returned no tokens (server may have rejected the exchange silently)`)\n\n await hooks?.callHook('mcp:auth:success', { name: config.name })\n return { tokens, tools }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n await hooks?.callHook('mcp:auth:error', { name: config.name, error })\n throw error\n }\n finally {\n settled = true\n await callback?.close()\n }\n}\n\n/**\n * Build an `sse` / `streamable-http` transport pre-wired with `authProvider`.\n * Mirrors the bootstrap-side `createTransport` shape but inlined here so the\n * login flow doesn't depend on the bootstrap module's private export.\n */\nasync function createInteractiveTransport(rawConfig: McpServerConfig, authProvider: OAuthClientProvider) {\n // Same `${VAR}` resolution as the bootstrap-side `createTransport` —\n // url/headers may carry env placeholders that must never hit the wire raw.\n const config = expandMcpConfigEnv(rawConfig)\n if (config.transport === 'sse') {\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')\n return new SSEClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n authProvider,\n })\n }\n // streamable-http\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js')\n return new StreamableHTTPClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n fetch: sseToJsonFetchIfNeeded(),\n authProvider,\n })\n}\n\nfunction isUnauthorizedError(err: unknown): boolean {\n if (!err || typeof err !== 'object')\n return false\n const e = err as { name?: string, message?: string, constructor?: { name?: string } }\n if (e.name === 'UnauthorizedError' || e.constructor?.name === 'UnauthorizedError')\n return true\n return typeof e.message === 'string' && e.message.toLowerCase().startsWith('unauthorized')\n}\n\n/**\n * Race `promise` against the external abort signal and a hard timeout.\n *\n * Used for phases we can't cancel from the inside (the SDK's `connect()`\n * accepts no signal). The underlying work isn't stopped — it's abandoned —\n * so callers must treat a rejection as \"this attempt is over\" and close\n * whatever they own. Rejects with `'OAuth login aborted'` on abort so the\n * host can tell a user cancel apart from a genuine failure.\n */\nasync function raceAbort<T>(\n promise: Promise<T>,\n signal: AbortSignal | undefined,\n timeoutMs: number,\n timeoutMessage: string,\n): Promise<T> {\n if (signal?.aborted)\n throw new Error('OAuth login aborted')\n // Nothing consumes the rejection when the race is lost — pre-attach a\n // no-op catch so an abandoned connect can't raise an unhandled rejection.\n promise.catch(() => {})\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<T>((resolve, reject) => {\n timer = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs)\n if (signal) {\n onAbort = () => reject(new Error('OAuth login aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n promise.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n\n/**\n * Wait on the callback handle, with a hard timeout AND honor the external\n * abort signal. Unlike `callback.promise` alone, this provides a deterministic\n * failure mode for \"user opened the browser then walked away\" — the modal\n * doesn't hang forever waiting for a redirect that may never come.\n */\nasync function raceLoginCallback(\n handle: OAuthCallbackHandle,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<{ code: string, state?: string }> {\n if (signal?.aborted)\n throw new Error('OAuth login aborted')\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<{ code: string, state?: string }>((resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`OAuth login timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n if (signal) {\n onAbort = () => reject(new Error('OAuth login aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n handle.promise.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n","/**\n * Hook-group installers behind {@link createMetricsHooks} (`./metrics`).\n *\n * Each installer registers one slice of the hooks and pushes its unregister\n * fns onto the shared `unregisters` array. `createMetricsHooks` calls them in\n * the order run, tool, MCP and decision, which fixes the hook registration\n * order.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { Counter, Histogram, MetricAttributes, UpDownCounter } from './metrics'\n\n/** Instruments created once per {@link createMetricsHooks} call. */\nexport interface MetricsInstruments {\n turnDuration: Histogram | undefined\n tokenUsage: Histogram | undefined\n ttft: Histogram | undefined\n toolDuration: Histogram | undefined\n toolOutput: Histogram | undefined\n mcpToolDuration: Histogram | undefined\n mcpBootstrapDuration: Histogram | undefined\n decisionDuration: Histogram | undefined\n decisions: Counter | undefined\n runsStarted: Counter | undefined\n runsCompleted: Counter | undefined\n aborts: Counter | undefined\n toolCalls: Counter | undefined\n toolErrors: Counter | undefined\n mcpToolErrors: Counter | undefined\n streamErrors: Counter | undefined\n validationRejects: Counter | undefined\n validationCoercions: Counter | undefined\n gateBlocks: Counter | undefined\n budgetExceeded: Counter | undefined\n toolBudgetExceeded: Counter | undefined\n pairingRepairs: Counter | undefined\n oauthRefreshes: Counter | undefined\n mcpErrors: Counter | undefined\n mcpAuthRequired: Counter | undefined\n costMeter: Counter | undefined\n runsActive: UpDownCounter | undefined\n}\n\n/** Per-install book-keeping + guarded record/add helpers. */\nexport interface MetricsInstallState {\n // Per-instrument book-keeping. Each map tracks instrument start times\n // keyed by the identifier that distinguishes concurrent in-flight\n // instances of that kind.\n turnStart: Map<string, number> // turnId -> startedAt\n toolStart: Map<string, number> // callId -> startedAt\n mcpStart: Map<string, number> // callId -> startedAt\n record: <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ) => void\n add: <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ) => void\n}\n\n/**\n * Drop `undefined` entries before handing attributes to a metrics\n * backend. OTel's `@opentelemetry/api` rejects `undefined` attribute\n * values (and Prometheus / StatsD adapters silently mis-label them); the\n * helper's own API surface allows `undefined` for ergonomic call sites\n * (`{...(cond ? { foo } : {})}`-style spreads), so we strip here at the\n * boundary.\n */\nfunction mergeAttrs(\n base: MetricAttributes | undefined,\n extra: MetricAttributes,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (base) {\n for (const [k, v] of Object.entries(base)) {\n if (v !== undefined)\n out[k] = v\n }\n }\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/** Build the per-install state: fresh start-time maps + guarded emitters. */\nexport function createMetricsInstallState(\n base: MetricAttributes | undefined,\n onError: (kind: string, err: unknown) => void,\n): MetricsInstallState {\n const turnStart = new Map<string, number>() // turnId -> startedAt\n const toolStart = new Map<string, number>() // callId -> startedAt\n const mcpStart = new Map<string, number>() // callId -> startedAt\n\n const record = <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.record(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n const add = <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.add(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n return { turnStart, toolStart, mcpStart, record, add }\n}\n\n/** Agent lifecycle + turn / stream metrics. */\nexport function installMetricsRunHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { turnDuration, tokenUsage, ttft, runsStarted, runsCompleted, aborts, streamErrors, costMeter, runsActive } = m\n const { turnStart, toolStart, mcpStart, record, add } = state\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n const attrs: MetricAttributes = {\n 'gen_ai.agent.depth': ctx.depth,\n ...(ctx.agentName ? { 'gen_ai.agent.name': ctx.agentName } : {}),\n }\n add(runsStarted, 1, attrs, 'runs')\n add(runsActive, 1, attrs, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:done', () => {\n add(runsCompleted, 1, {}, 'runs.completed')\n add(runsActive, -1, {}, 'runs.active')\n // Aborted turns skip `turn:after` / `tool:after` / `mcp:tool:after`,\n // stranding their start entries until uninstall — the run is over,\n // so flush them here.\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n add(aborts, 1, {}, 'aborts')\n }))\n\n // ---- Turn / stream metrics -------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnStart.set(ctx.turnId, Date.now())\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const started = turnStart.get(ctx.turnId)\n turnStart.delete(ctx.turnId)\n const tagsBase: MetricAttributes = {\n 'gen_ai.operation.name': 'chat',\n ...(ctx.usage.modelId ? { 'gen_ai.response.model': ctx.usage.modelId } : {}),\n ...(ctx.usage.finishReason ? { 'gen_ai.response.finish_reason': ctx.usage.finishReason } : {}),\n }\n if (typeof started === 'number')\n record(turnDuration, Date.now() - started, tagsBase, 'turn.duration')\n if (typeof ctx.usage.timeToFirstTokenMs === 'number')\n record(ttft, ctx.usage.timeToFirstTokenMs, tagsBase, 'ttft')\n\n record(tokenUsage, ctx.usage.input, { ...tagsBase, 'gen_ai.token.type': 'input' }, 'token.input')\n record(tokenUsage, ctx.usage.output, { ...tagsBase, 'gen_ai.token.type': 'output' }, 'token.output')\n if (typeof ctx.usage.cacheRead === 'number' && ctx.usage.cacheRead > 0)\n record(tokenUsage, ctx.usage.cacheRead, { ...tagsBase, 'gen_ai.token.type': 'cache_read' }, 'token.cache_read')\n if (typeof ctx.usage.cacheCreation === 'number' && ctx.usage.cacheCreation > 0)\n record(tokenUsage, ctx.usage.cacheCreation, { ...tagsBase, 'gen_ai.token.type': 'cache_creation' }, 'token.cache_creation')\n\n if (typeof ctx.usage.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, tagsBase, 'cost')\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n add(streamErrors, 1, {\n 'http.response.status_code': ctx.statusCode,\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n }, 'stream.error')\n }))\n}\n\n/** Native tool, validation, budget, pairing and OAuth metrics. */\nexport function installMetricsToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { toolDuration, toolOutput, toolCalls, toolErrors, validationRejects, validationCoercions, gateBlocks, budgetExceeded, toolBudgetExceeded, pairingRepairs, oauthRefreshes } = m\n const { toolStart, record, add } = state\n\n // ---- Native tool metrics ---------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n toolStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const started = toolStart.get(ctx.callId)\n toolStart.delete(ctx.callId)\n const tags: MetricAttributes = { 'gen_ai.tool.name': ctx.name }\n if (typeof started === 'number')\n record(toolDuration, Date.now() - started, tags, 'tool.duration')\n record(toolOutput, ctx.outputBytes, tags, 'tool.output_bytes')\n }))\n\n // NOTE: `tool:error` must NOT delete `toolStart[callId]` — on the\n // execute-error path the loop still fires `tool:after` afterwards\n // (with the `Tool error: …` payload), and that's where the errored\n // call's duration gets recorded + the entry cleaned up.\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n add(toolErrors, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'error.type': ctx.error.name,\n }, 'tool.errors')\n }))\n\n // Mid-flight per-call cancels close with `tool:cancelled` instead of\n // `tool:after` — clean up the start entry here or it leaks.\n unregisters.push(hooks.hook('tool:cancelled', (ctx) => {\n toolStart.delete(ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n add(toolCalls, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'outcome': ctx.outcome,\n }, 'tool.calls')\n if (ctx.outcome === 'gate-block') {\n add(gateBlocks, 1, {\n 'gen_ai.tool.name': ctx.name,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n }, 'gate.blocks')\n }\n // Unknown-tool dispatches fire `tool:before` (with `unknown: true`)\n // but never `tool:after` — drop the start entry so hallucinated\n // tool names can't grow the map unboundedly. `tool:dispatched` for\n // real executions fires BEFORE `tool:before`, so this only affects\n // the unknown path.\n if (ctx.outcome === 'unknown')\n toolStart.delete(ctx.callId)\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n add(validationRejects, 1, { 'gen_ai.tool.name': ctx.name }, 'validation.rejects')\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n add(validationCoercions, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'coercions': ctx.coercions.length,\n }, 'validation.coercions')\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n add(budgetExceeded, 1, {\n bytes: ctx.bytes,\n budget: ctx.budget,\n }, 'budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n add(toolBudgetExceeded, 1, {\n 'gen_ai.tool.name': ctx.tool,\n 'mode': ctx.mode,\n 'count': ctx.count,\n 'max': ctx.max,\n }, 'tool_budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n add(pairingRepairs, 1, { mode: ctx.mode }, 'pairing.repairs')\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n add(oauthRefreshes, 1, { provider: ctx.provider, source: ctx.source }, 'oauth.refreshes')\n }))\n}\n\n/** MCP tool / bootstrap / server metrics. */\nexport function installMetricsMcpHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { mcpToolDuration, mcpBootstrapDuration, mcpToolErrors, mcpErrors, mcpAuthRequired } = m\n const { mcpStart, record, add } = state\n\n // ---- MCP metrics -----------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n mcpStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const started = mcpStart.get(ctx.callId)\n mcpStart.delete(ctx.callId)\n const tags: MetricAttributes = {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n }\n if (typeof started === 'number')\n record(mcpToolDuration, Date.now() - started, tags, 'mcp.tool.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n mcpStart.delete(ctx.callId)\n add(mcpToolErrors, 1, {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n 'error.type': ctx.error.name,\n }, 'mcp.tool.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n record(mcpBootstrapDuration, ctx.durationMs, {\n 'gen_ai.mcp.server': ctx.name,\n 'ok': ctx.ok,\n }, 'mcp.bootstrap.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n add(mcpErrors, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'error.type': ctx.error.name,\n }, 'mcp.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n add(mcpAuthRequired, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'transport': ctx.transport,\n 'reason': ctx.reason,\n }, 'mcp.auth.required')\n }))\n}\n\n/** Decision request metrics. */\nexport function installMetricsDecisionHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { decisionDuration, decisions, costMeter } = m\n const { record, add } = state\n\n unregisters.push(hooks.hook('decision:end', (ctx) => {\n const tags: MetricAttributes = {\n 'gen_ai.system': ctx.provider,\n 'gen_ai.request.model': ctx.model,\n 'outcome': ctx.ok ? 'ok' : 'error',\n 'error.type': ctx.reason,\n }\n record(decisionDuration, ctx.durationMs, tags, 'decision.duration')\n add(decisions, 1, tags, 'decisions')\n if (typeof ctx.usage?.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, { 'gen_ai.system': ctx.provider, 'gen_ai.request.model': ctx.model }, 'cost')\n }))\n}\n","/**\n * Metrics helper — wraps agent lifecycle hooks in caller-provided meters.\n *\n * Symmetric with {@link createTracingHooks} from `./tracing`. Where the\n * tracer answers \"what happened in this run?\" by producing spans, the\n * metrics helper answers \"how is the fleet behaving?\" by emitting\n * counters / histograms / up-down counters. Both are tracer-agnostic —\n * the caller supplies a `Meter` (OTel-API-shaped) and the helper plugs\n * the right hook on each metric.\n *\n * Instrument families:\n *\n * ### Histograms (distribution metrics)\n * - `gen_ai.client.operation.duration` (ms) — wall-clock per turn (`turn:before` → `turn:after`).\n * - `gen_ai.client.token.usage` — `input` / `output` token counts per turn (tagged by `gen_ai.token.type`).\n * - `gen_ai.client.time_to_first_token` (ms) — per-turn TTFT from `TurnUsage.timeToFirstTokenMs`.\n * - `gen_ai.tool.duration` (ms) — native tool execution time (`tool:before` → `tool:after`).\n * - `gen_ai.tool.output_bytes` — `tool:after.outputBytes`.\n * - `gen_ai.mcp.tool.duration` (ms) — MCP tool execution time.\n * - `gen_ai.mcp.bootstrap.duration` (ms) — MCP server bootstrap (already measured by harness).\n * - `gen_ai.decision.duration` (ms) — `decision:end.durationMs` (tagged by `outcome` / `error.type`).\n *\n * ### Counters (monotonic)\n * - `gen_ai.agent.runs` — `agent:start`.\n * - `gen_ai.agent.runs.completed` — `agent:done`.\n * - `gen_ai.agent.aborts` — `agent:abort`.\n * - `gen_ai.tool.calls` — `tool:dispatched` (tagged by `outcome`).\n * - `gen_ai.tool.errors` — `tool:error`.\n * - `gen_ai.mcp.tool.errors` — `mcp:tool:error`.\n * - `gen_ai.stream.errors` — `stream:error` (tagged by `status_code`).\n * - `gen_ai.validation.rejects` — `validation:reject`.\n * - `gen_ai.validation.coercions` — `validation:coerce`.\n * - `gen_ai.gate.blocks` — `tool:dispatched` outcome `gate-block`.\n * - `gen_ai.budget.exceeded` — `budget:exceeded`.\n * - `gen_ai.tool_budget.exceeded` — `tool-budget:exceeded` (tagged by `mode`).\n * - `gen_ai.pairing.repairs` — `pairing:repair` (tagged by `mode`).\n * - `gen_ai.oauth.refreshes` — `oauth:refresh`.\n * - `gen_ai.mcp.errors` — `mcp:error`.\n * - `gen_ai.mcp.auth.required` — `mcp:auth:required`.\n * - `gen_ai.decisions` — `decision:end` (tagged by `outcome` / `error.type`).\n * - `gen_ai.cost_usd` — accumulated cost (added on `turn:after` and `decision:end`).\n *\n * ### Up-down counter (gauge-shaped)\n * - `gen_ai.agent.runs.active` — incremented on `agent:start`, decremented on `agent:done`.\n *\n * The `Meter` shape mirrors `@opentelemetry/api`'s `Meter` exactly — passing\n * `metrics.getMeter('zidane')` works directly. Hosts using a different\n * metrics backend (StatsD, Prometheus client, custom in-memory aggregator)\n * implement the same three factory methods and the helper plugs in\n * unchanged.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { MetricsInstruments } from './metrics-hooks'\nimport { createMetricsInstallState, installMetricsDecisionHooks, installMetricsMcpHooks, installMetricsRunHooks, installMetricsToolHooks } from './metrics-hooks'\n\n// ---------------------------------------------------------------------------\n// OTel-shaped Meter interface\n// ---------------------------------------------------------------------------\n\n/** Attributes attached to a measurement. */\nexport type MetricAttributes = Record<string, string | number | boolean | undefined>\n\n/** Instrument that only increases. */\nexport interface Counter {\n /** Adds a non-negative `value`, with optional attributes. */\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Instrument that records a distribution of values. */\nexport interface Histogram {\n /** Records one measurement, with optional attributes. */\n record: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Instrument whose value can go up and down. */\nexport interface UpDownCounter {\n /** Adds a positive or negative `value`, with optional attributes. */\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Options passed to the meter when an instrument is created. */\nexport interface InstrumentOptions {\n /** Human-readable description of the instrument. */\n description?: string\n /** Unit of the measurement, for example `'ms'` or `'{token}'`. */\n unit?: string\n}\n\n/**\n * Minimal Meter interface — structurally identical to OTel's `Meter`.\n * Hosts passing `metrics.getMeter(name)` (from `@opentelemetry/api`)\n * satisfy this without adaptation.\n */\nexport interface Meter {\n /** Creates a counter instrument with the given name. */\n createCounter: (name: string, options?: InstrumentOptions) => Counter\n /** Creates a histogram instrument with the given name. */\n createHistogram: (name: string, options?: InstrumentOptions) => Histogram\n /** Creates an up/down counter instrument with the given name. */\n createUpDownCounter: (name: string, options?: InstrumentOptions) => UpDownCounter\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createMetricsHooks}. */\nexport interface MetricsHooksOptions {\n /** OpenTelemetry-shaped meter that creates the instruments. */\n meter: Meter\n /**\n * Optional prefix prepended to every instrument name. Default: no prefix\n * (instrument names follow OTel Gen AI semantic conventions verbatim,\n * which is the most-portable shape). Set to e.g. `'zidane.'` to\n * namespace inside a shared meter registry.\n */\n namespace?: string\n /**\n * Optional baseline attributes applied to every measurement. Typical\n * use: `{ service: 'tui', env: 'prod' }`. Per-event attributes win on\n * key collision.\n */\n baseAttributes?: MetricAttributes\n /**\n * Error sink for meter failures. The helper still swallows the throw\n * so a broken backend can't crash a run; this callback surfaces the\n * failure for ops dashboards.\n */\n onError?: (kind: string, err: unknown) => void\n}\n\n/** Value returned by {@link createMetricsHooks}. */\nexport interface MetricsHookSet {\n /**\n * Attaches every metrics hook handler to the given agent hooks instance.\n * Returns an `uninstall` function that detaches the handlers.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction prefixed(prefix: string | undefined, name: string): string {\n return prefix ? `${prefix}${name}` : name\n}\n\n// ---------------------------------------------------------------------------\n// createMetricsHooks\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of metrics hook handlers that can be installed on an agent.\n *\n * @example OpenTelemetry\n * ```ts\n * import { metrics } from '@opentelemetry/api'\n * const meter = metrics.getMeter('zidane')\n * const m = createMetricsHooks({ meter, baseAttributes: { service: 'tui' } })\n * const uninstall = m.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @param options - Meter plus optional `namespace`, `baseAttributes` and `onError`.\n */\nexport function createMetricsHooks(options: MetricsHooksOptions): MetricsHookSet {\n const ns = options.namespace\n const base = options.baseAttributes\n const onError = options.onError ?? (() => {})\n\n const make = {\n counter: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createCounter(prefixed(ns, name), opts), name, onError),\n histogram: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createHistogram(prefixed(ns, name), opts), name, onError),\n upDown: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createUpDownCounter(prefixed(ns, name), opts), name, onError),\n }\n\n // Instruments — created once at install time.\n const turnDuration = make.histogram('gen_ai.client.operation.duration', { unit: 'ms', description: 'Per-turn LLM operation duration.' })\n const tokenUsage = make.histogram('gen_ai.client.token.usage', { unit: 'tokens', description: 'Per-turn token usage (tagged by gen_ai.token.type).' })\n const ttft = make.histogram('gen_ai.client.time_to_first_token', { unit: 'ms', description: 'Per-turn time to first token.' })\n const toolDuration = make.histogram('gen_ai.tool.duration', { unit: 'ms', description: 'Native tool execution wall clock.' })\n const toolOutput = make.histogram('gen_ai.tool.output_bytes', { unit: 'By', description: 'Native tool output bytes.' })\n const mcpToolDuration = make.histogram('gen_ai.mcp.tool.duration', { unit: 'ms', description: 'MCP tool execution wall clock.' })\n const mcpBootstrapDuration = make.histogram('gen_ai.mcp.bootstrap.duration', { unit: 'ms', description: 'MCP server bootstrap duration.' })\n const decisionDuration = make.histogram('gen_ai.decision.duration', { unit: 'ms', description: 'Decision request wall clock.' })\n\n const runsStarted = make.counter('gen_ai.agent.runs', { description: 'Runs started.' })\n const runsCompleted = make.counter('gen_ai.agent.runs.completed', { description: 'Runs completed.' })\n const aborts = make.counter('gen_ai.agent.aborts', { description: 'Run abort events.' })\n const toolCalls = make.counter('gen_ai.tool.calls', { description: 'Tool dispatches (tagged by outcome).' })\n const toolErrors = make.counter('gen_ai.tool.errors', { description: 'Native tool errors.' })\n const mcpToolErrors = make.counter('gen_ai.mcp.tool.errors', { description: 'MCP tool errors.' })\n const streamErrors = make.counter('gen_ai.stream.errors', { description: 'Provider stream errors.' })\n const validationRejects = make.counter('gen_ai.validation.rejects', { description: 'Tool input validation rejects.' })\n const validationCoercions = make.counter('gen_ai.validation.coercions', { description: 'Tool input auto-coercions.' })\n const gateBlocks = make.counter('gen_ai.gate.blocks', { description: 'Tool-gate refusals.' })\n const budgetExceeded = make.counter('gen_ai.budget.exceeded', { description: 'Per-turn byte budget exceedances.' })\n const toolBudgetExceeded = make.counter('gen_ai.tool_budget.exceeded', { description: 'Per-tool dispatch budget exceedances.' })\n const pairingRepairs = make.counter('gen_ai.pairing.repairs', { description: 'Tool-pairing repair counts.' })\n const oauthRefreshes = make.counter('gen_ai.oauth.refreshes', { description: 'Provider OAuth credential refreshes.' })\n const mcpErrors = make.counter('gen_ai.mcp.errors', { description: 'MCP server errors.' })\n const mcpAuthRequired = make.counter('gen_ai.mcp.auth.required', { description: 'MCP bootstrap blocked on missing auth.' })\n const decisions = make.counter('gen_ai.decisions', { description: 'Decision requests (tagged by outcome).' })\n const costMeter = make.counter('gen_ai.cost_usd', { unit: 'USD', description: 'Cumulative cost across turns.' })\n\n const runsActive = make.upDown('gen_ai.agent.runs.active', { description: 'In-flight agent runs.' })\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const state = createMetricsInstallState(base, onError)\n const { turnStart, toolStart, mcpStart } = state\n const instruments: MetricsInstruments = {\n turnDuration,\n tokenUsage,\n ttft,\n toolDuration,\n toolOutput,\n mcpToolDuration,\n mcpBootstrapDuration,\n decisionDuration,\n decisions,\n runsStarted,\n runsCompleted,\n aborts,\n toolCalls,\n toolErrors,\n mcpToolErrors,\n streamErrors,\n validationRejects,\n validationCoercions,\n gateBlocks,\n budgetExceeded,\n toolBudgetExceeded,\n pairingRepairs,\n oauthRefreshes,\n mcpErrors,\n mcpAuthRequired,\n costMeter,\n runsActive,\n }\n\n const unregisters: Array<() => void> = []\n\n installMetricsRunHooks(hooks, unregisters, instruments, state)\n installMetricsToolHooks(hooks, unregisters, instruments, state)\n installMetricsMcpHooks(hooks, unregisters, instruments, state)\n installMetricsDecisionHooks(hooks, unregisters, instruments, state)\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }\n },\n }\n}\n\n/**\n * Defensive factory wrapper — returns undefined when meter creation\n * throws. Lets a half-broken meter (one unsupported instrument) still\n * give partial metrics rather than collapsing the whole install.\n */\nfunction safeFactory<T>(\n factory: () => T,\n name: string,\n onError: (kind: string, err: unknown) => void,\n): T | undefined {\n try {\n return factory()\n }\n catch (err) {\n try {\n onError(`createInstrument:${name}`, err)\n }\n catch { /* ignore */ }\n return undefined\n }\n}\n","import type { AgentHookMap } from './agent'\nimport type { ToolOutcome, ToolResultContent } from './types'\n\ntype MaybePromise<T> = T | Promise<T>\n\n/** Tool outcomes that count as a successful mutation. */\nexport type SuccessfulMutationOutcome = Extract<ToolOutcome, 'created' | 'updated' | 'edited'>\n\n/** One successful file mutation (`created`, `updated` or `edited`) made by a tool call. */\nexport interface MutationValidationMutation {\n /** Id of the turn that made the call. */\n turnId: string\n /** Id of the tool call. */\n callId: string\n /** Name of the tool that made the change. */\n tool: string\n /** Display name of the tool. */\n displayName: string\n /** Input the tool was called with. */\n input: Record<string, unknown>\n /** Outcome the tool reported for the call. */\n outcome: SuccessfulMutationOutcome\n /** Result the tool returned for the call. */\n result: string | ToolResultContent[]\n}\n\n/** Successful mutations from one tool-results round, passed to `validate`. */\nexport interface MutationValidationBatch {\n /** Id of the turn that produced the mutations. */\n turnId: string\n /** Successful mutations since the last validation, in call order. */\n mutations: readonly MutationValidationMutation[]\n}\n\n/** Result of a validation: `{ ok: true }`, or `{ ok: false }` with an optional message and error list. */\nexport type MutationValidationResult\n = | { ok: true }\n | { ok: false, message?: string, errors?: readonly string[] }\n\n/** Details of a failed validation, passed to `onFailure`. */\nexport interface MutationValidationFailure {\n /** Batch that failed validation. */\n batch: MutationValidationBatch\n /** Failing result returned by `validate`. */\n result: Extract<MutationValidationResult, { ok: false }>\n /** Message built for the failure, from `buildFailureMessage` or the default. */\n message: string\n /** Number of failures so far in the current run, including this one. */\n failureCount: number\n}\n\n/** Options for {@link createMutationValidationHooks}. */\nexport interface MutationValidationHooksOptions {\n /** Checks a batch of successful mutations. Return `{ ok: false }` to report a failure, or nothing to pass. */\n validate: (batch: MutationValidationBatch) => MaybePromise<MutationValidationResult | void>\n /**\n * Called when validation fails and the run has not crossed\n * `maxFailuresPerRun`. Hosts typically call `agent.followUp(message)` here.\n */\n onFailure?: (failure: MutationValidationFailure) => MaybePromise<void>\n /** Builds the message reported to the model for a failure. Defaults to a summary of the changed files and the validation details. */\n buildFailureMessage?: (failure: Omit<MutationValidationFailure, 'message'>) => string\n /** Called when `validate` throws. The error is not reported to the model. */\n onError?: (error: unknown, batch: MutationValidationBatch) => MaybePromise<void>\n /**\n * Maximum number of failures reported per run. Later failures are ignored.\n * @default 2\n */\n maxFailuresPerRun?: number\n}\n\nfunction defaultFailureMessage(failure: Omit<MutationValidationFailure, 'message'>): string {\n const changed = failure.batch.mutations\n .map(m => `${m.displayName} ${String(m.input.path ?? m.callId)}`)\n .join(', ')\n const details = failure.result.message\n ?? failure.result.errors?.join('\\n')\n ?? 'Validation failed after the last file mutation.'\n const seen = failure.failureCount > 1 ? ` (failure ${failure.failureCount})` : ''\n return `[Mutation validation failed${seen}] ${changed}\\n${details}\\nFix the validation errors with the smallest focused change.`\n}\n\nfunction isSuccessfulMutation(outcome: ToolOutcome | undefined): outcome is SuccessfulMutationOutcome {\n return outcome === 'created' || outcome === 'updated' || outcome === 'edited'\n}\n\n/**\n * Build host-side hooks for post-mutation validation without changing the\n * built-in edit/write tools. The helper only observes successful mutations\n * reported through `tool:after.outcome`; validation commands and recovery\n * prompts remain host policy.\n *\n * @param options - Validator and optional failure handling. `validate` is required.\n */\nexport function createMutationValidationHooks(options: MutationValidationHooksOptions): AgentHookMap {\n const pending: MutationValidationMutation[] = []\n let failureCount = 0\n const maxFailures = Math.max(1, Math.floor(options.maxFailuresPerRun ?? 2))\n\n return {\n 'tool:after': (ctx) => {\n if (!isSuccessfulMutation(ctx.outcome))\n return\n pending.push({\n turnId: ctx.turnId,\n callId: ctx.callId,\n tool: ctx.name,\n displayName: ctx.displayName,\n input: ctx.input,\n outcome: ctx.outcome,\n result: ctx.result,\n })\n },\n 'tool-results:after': async (ctx) => {\n if (pending.length === 0)\n return\n const mutations = pending.splice(0)\n const batch: MutationValidationBatch = {\n turnId: ctx.turnId,\n mutations,\n }\n let result: MutationValidationResult | void\n try {\n result = await options.validate(batch)\n }\n catch (err) {\n await options.onError?.(err, batch)\n return\n }\n if (!result || result.ok)\n return\n failureCount++\n if (failureCount > maxFailures)\n return\n const withoutMessage = { batch, result, failureCount }\n const message = options.buildFailureMessage?.(withoutMessage)\n ?? defaultFailureMessage(withoutMessage)\n await options.onFailure?.({ ...withoutMessage, message })\n },\n 'agent:done': () => {\n pending.length = 0\n // `agent:done` fires at the end of every run, so resetting here gives\n // `maxFailuresPerRun` true per-run semantics even when this hook map is\n // registered at agent level and reused across runs.\n failureCount = 0\n },\n }\n}\n","/**\n * A host's main thread: how busy it was and how late timers ran (the delay\n * histogram, on only while sampling: its timer would otherwise wake an idle\n * process 50 times a second).\n *\n * Busy is the thread's own CPU time over wall time\n * (`process.threadCpuUsage()`): Electron runs Node on Chromium's message\n * loop, where Node's event-loop utilisation reads 0. On a runtime without\n * it, utilisation is the fallback.\n */\nimport type { PerfLag } from './types'\nimport { monitorEventLoopDelay, performance } from 'node:perf_hooks'\n\n// Inferred, not imported by name: Bun's `node:perf_hooks` types (an app type-checking\n// against the published package with `bun` types) do not name them.\ntype IntervalHistogram = ReturnType<typeof monitorEventLoopDelay>\ntype EventLoopUtilization = ReturnType<typeof performance.eventLoopUtilization>\n\n/** A host's main thread, measured: how busy it was, and (while on) how late its timers ran. */\nexport interface MainThreadMeter {\n /** Turns the delay histogram on. */\n start: () => void\n /** Turns it off. */\n stop: () => void\n /** Busy share since the previous read (0–1), and the delay over that span when on. */\n read: () => { busy: number, lag: PerfLag | null }\n /** Busy share since the process started (0–1). */\n sinceStart: () => number\n}\n\nconst NS_PER_MS = 1e6\nconst RESOLUTION_MS = 20\n\ntype ThreadCpu = () => { user: number, system: number }\n\n/** The CPU time of this thread, µs, when the runtime offers it (Node 23.9+, Bun). */\nfunction threadCpu(): ThreadCpu | null {\n const fn = (process as { threadCpuUsage?: ThreadCpu }).threadCpuUsage\n return typeof fn === 'function' ? () => fn.call(process) : null\n}\n\n/**\n * The histogram's figures less the timer's own interval. Node records each\n * whole interval (never under the resolution), Bun only how late it ran:\n * the resolution comes off when the shortest one holds it.\n */\nfunction lagOf(histogram: IntervalHistogram): PerfLag {\n const base = histogram.min / NS_PER_MS >= RESOLUTION_MS * 0.9 ? RESOLUTION_MS : 0\n const ms = (ns: number): number => Math.max(0, ns / NS_PER_MS - base)\n return { p50: ms(histogram.percentile(50)), p99: ms(histogram.percentile(99)), max: ms(histogram.max) }\n}\n\n/**\n * Creates the meter.\n *\n * @param cpu - The thread's CPU time (tests); default `process.threadCpuUsage`.\n * @param now - Wall clock, ms (tests).\n */\nexport function createMainThreadMeter(cpu: ThreadCpu | null = threadCpu(), now: () => number = () => performance.now()): MainThreadMeter {\n const used = (): number => {\n const usage = cpu!()\n return (usage.user + usage.system) / 1000\n }\n let lastCpu = cpu ? used() : 0\n let lastAt = now()\n let lastElu: EventLoopUtilization = performance.eventLoopUtilization()\n let histogram: IntervalHistogram | null = null\n return {\n start() {\n if (histogram)\n return\n histogram = monitorEventLoopDelay({ resolution: RESOLUTION_MS })\n histogram.enable()\n },\n stop() {\n histogram?.disable()\n histogram = null\n },\n read() {\n let busy: number\n if (cpu) {\n const at = now()\n const ms = used()\n busy = at > lastAt ? Math.min(1, Math.max(0, (ms - lastCpu) / (at - lastAt))) : 0\n lastCpu = ms\n lastAt = at\n }\n else {\n const elu = performance.eventLoopUtilization()\n busy = performance.eventLoopUtilization(elu, lastElu).utilization\n lastElu = elu\n }\n let lag: PerfLag | null = null\n if (histogram && histogram.count > 0) {\n lag = lagOf(histogram)\n histogram.reset()\n }\n return { busy, lag }\n },\n sinceStart: () =>\n cpu ? Math.min(1, used() / Math.max(1, process.uptime() * 1000)) : performance.eventLoopUtilization().utilization,\n }\n}\n","/**\n * The OS view of the process tree: one `ps` for every process, a host's\n * descendants picked out of it, CPU from the change in cumulative CPU time\n * between two samples, and a readable label from the command line. Pure, but\n * for the readers ({@link readProcessTable}, {@link readProcessTree},\n * {@link readLinuxCpuSeconds}), which run the commands.\n */\nimport { execFile } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { basename } from 'node:path'\n\n/** One OS process, as `ps` reports it. */\nexport interface PsRow {\n /** The process id. */\n pid: number\n /** Its parent's process id. */\n ppid: number\n /** Its process group (`=== pid`: it leads one). */\n pgid: number\n /** Resident memory, KiB. */\n rssKb: number\n /** CPU time used since it started (user + system), seconds. */\n cpuSeconds: number\n /** The command line. */\n args: string\n}\n\n/**\n * `ps`'s `time` column in seconds: `m:ss.cc` (macOS), `[dd-]hh:mm:ss` (Linux).\n *\n * @param value - The column.\n * @returns Seconds, `NaN` when it doesn't parse.\n */\nexport function parsePsTime(value: string): number {\n const match = /^(?:(\\d+)-)?([\\d:.]+)$/.exec(value.trim())\n if (!match)\n return Number.NaN\n const days = match[1] ? Number(match[1]) : 0\n const parts = match[2]!.split(':').map(Number)\n if (parts.some(p => Number.isNaN(p)))\n return Number.NaN\n let seconds = 0\n for (const part of parts) seconds = seconds * 60 + part\n return days * 86_400 + seconds\n}\n\n/**\n * Parses `ps -A -o pid=,ppid=,pgid=,rss=,time=,args=` (run with `LC_ALL=C`).\n *\n * @param text - Its output.\n * @returns One row per line that parses.\n */\nexport function parsePsOutput(text: string): PsRow[] {\n const rows: PsRow[] = []\n for (const line of text.split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\S.*)$/.exec(line)\n if (!match)\n continue\n const cpuSeconds = parsePsTime(match[5]!)\n if (Number.isNaN(cpuSeconds))\n continue\n rows.push({\n pid: Number(match[1]),\n ppid: Number(match[2]),\n pgid: Number(match[3]),\n rssKb: Number(match[4]),\n cpuSeconds,\n args: match[6]!.trim(),\n })\n }\n return rows\n}\n\n/**\n * The rows under each root (the roots included), parents before children.\n *\n * @param rows - Every process.\n * @param roots - The trees' roots (the app, the daemon, a server). A root `ps` doesn't list is skipped.\n * @returns The rows of the trees, in walk order.\n */\nexport function descendants(rows: readonly PsRow[], roots: readonly number[]): PsRow[] {\n const byPid = new Map(rows.map(row => [row.pid, row]))\n const children = new Map<number, PsRow[]>()\n for (const row of rows) {\n const list = children.get(row.ppid)\n if (list)\n list.push(row)\n else\n children.set(row.ppid, [row])\n }\n const out: PsRow[] = []\n const seen = new Set<number>()\n const walk = (row: PsRow): void => {\n if (seen.has(row.pid))\n return\n seen.add(row.pid)\n out.push(row)\n for (const child of children.get(row.pid) ?? []) walk(child)\n }\n for (const root of roots) {\n const row = byPid.get(root)\n if (row)\n walk(row)\n }\n return out\n}\n\nconst INTERPRETERS = new Set(['node', 'bun', 'deno', 'python', 'python3', 'ruby', 'sh', 'bash', 'zsh', 'npx', 'bunx', 'uv', 'uvx'])\n\n/**\n * A short name for a command line: the program, or the script an interpreter runs\n * (`node …/typescript-language-server --stdio` → `typescript-language-server (node)`).\n *\n * @param args - The command line.\n * @returns The label.\n */\nexport function commandLabel(args: string): string {\n const parts = args.split(/\\s+/).filter(Boolean)\n if (parts.length === 0)\n return '?'\n // A login shell shows as `-zsh`.\n const program = basename(parts[0]!).replace(/^-/, '')\n // `sh -c '…'` runs an inline command, not a script: the shell is the name.\n if (INTERPRETERS.has(program) && !parts.includes('-c')) {\n const script = parts.slice(1).find(part => !part.startsWith('-'))\n if (script)\n return `${basename(script)} (${program})`\n }\n return program\n}\n\n/**\n * CPU use between two readings, in percent of one core.\n *\n * @param before - CPU seconds at the first reading.\n * @param after - CPU seconds at the second.\n * @param elapsedMs - Wall time between them.\n * @returns The percentage, `0` when it can't be told.\n */\nexport function cpuPercent(before: number, after: number, elapsedMs: number): number {\n if (!(elapsedMs > 0) || !(after >= before))\n return 0\n return ((after - before) * 1000 * 100) / elapsedMs\n}\n\n/**\n * The monitor's own commands (`ps` lists itself, and `ioreg` when it runs\n * alongside): not the host's. Held until a collector clears them at the start\n * of its next tick, since `ps` has exited by the time its output is parsed.\n */\nexport const ownCommands = new Set<number>()\n\n/**\n * Runs a command and resolves its stdout, in the C locale (`ps` writes `0,5` in a French one).\n *\n * @param file - The program.\n * @param args - Its arguments.\n * @param timeoutMs - Killed past this.\n */\nexport function run(file: string, args: readonly string[], timeoutMs = 5_000): Promise<string> {\n return new Promise((resolve, reject) => {\n const child = execFile(\n file,\n args,\n { env: { ...process.env, LC_ALL: 'C' }, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 },\n (error, stdout) => (error ? reject(error) : resolve(stdout)),\n )\n // Bounded even with no tick to clear it (thread reads on expand).\n if (ownCommands.size > 64)\n ownCommands.clear()\n if (child.pid !== undefined)\n ownCommands.add(child.pid)\n })\n}\n\n/** Every process on the machine, from one `ps` call. */\nexport async function readProcessTable(): Promise<PsRow[]> {\n return parsePsOutput(await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,rss=,time=,args=']))\n}\n\n/**\n * The trees under `roots` (`descendants`), the monitor's own commands left\n * out. On Linux each row's CPU time is read from `/proc`: its `ps` rounds\n * it to whole seconds.\n *\n * @param roots - The trees' roots.\n */\nexport async function readProcessTree(roots: readonly number[]): Promise<PsRow[]> {\n const tree = descendants(await readProcessTable(), roots).filter(row => !ownCommands.has(row.pid))\n if (process.platform !== 'linux')\n return tree\n return Promise.all(tree.map(async row => ({ ...row, cpuSeconds: (await readLinuxCpuSeconds(row.pid)) ?? row.cpuSeconds })))\n}\n\n/**\n * `/proc/<pid>/stat`'s user + system time in seconds (Linux's `ps` rounds it to whole seconds).\n *\n * @param stat - The file's text.\n * @param ticksPerSecond - `CLK_TCK`.\n * @returns Seconds, `NaN` when it doesn't parse.\n */\nexport function parseProcStatCpu(stat: string, ticksPerSecond = 100): number {\n // The command name is in parentheses and may hold spaces: count fields after the last `)`.\n const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ')\n // Fields 14 and 15 of the file are 12 and 13 after the name and state.\n const utime = Number(fields[11])\n const stime = Number(fields[12])\n return Number.isNaN(utime) || Number.isNaN(stime) ? Number.NaN : (utime + stime) / ticksPerSecond\n}\n\n/**\n * A Linux process's CPU seconds.\n *\n * @param pid - The process.\n * @returns Seconds, `null` when it has gone.\n */\nexport async function readLinuxCpuSeconds(pid: number): Promise<number | null> {\n try {\n const seconds = parseProcStatCpu(await readFile(`/proc/${pid}/stat`, 'utf8'))\n return Number.isNaN(seconds) ? null : seconds\n }\n catch {\n return null\n }\n}\n","/**\n * Whole-GPU use on macOS, from `ioreg`'s accelerator statistics. macOS has no\n * public per-process figure: this is every app's use, and the UI says so.\n */\nimport type { PerfGpu } from './types'\nimport { run } from './ps'\n\n/** `ioreg` costs more than `ps`, and the GPU figure moves slower. */\nexport const GPU_EVERY_MS = 5_000\n\nconst STAT = (name: string): RegExp => new RegExp(`\"${name.replace(/[%()]/g, '\\\\$&')}\"=(\\\\d+)`)\n\n/**\n * Reads `ioreg -r -d 1 -w 0 -c IOAccelerator`'s `PerformanceStatistics`.\n *\n * @param text - Its output.\n * @returns The figures of the first accelerator that reports them, `null` when none does.\n */\nexport function parseIoreg(text: string): PerfGpu | null {\n const device = STAT('Device Utilization %').exec(text)\n if (!device)\n return null\n const renderer = STAT('Renderer Utilization %').exec(text)\n const tiler = STAT('Tiler Utilization %').exec(text)\n const memory = STAT('In use system memory').exec(text)\n return {\n device: Number(device[1]),\n ...(renderer ? { renderer: Number(renderer[1]) } : {}),\n ...(tiler ? { tiler: Number(tiler[1]) } : {}),\n ...(memory ? { memory: Number(memory[1]) } : {}),\n }\n}\n\n/** The GPU's use now; `null` off macOS or when `ioreg` fails. */\nexport async function readGpu(): Promise<PerfGpu | null> {\n if (process.platform !== 'darwin')\n return null\n try {\n return parseIoreg(await run('ioreg', ['-r', '-d', '1', '-w', '0', '-c', 'IOAccelerator']))\n }\n catch {\n return null\n }\n}\n\n/**\n * A GPU reader for a sampler: `ioreg` at most every `everyMs` (macOS only),\n * the last figure in between.\n *\n * @param everyMs - The least time between two reads.\n * @param read - The reader (tests); default {@link readGpu}.\n * @returns `(now) => figure`; `due(now)` says whether that call runs `ioreg`.\n */\nexport function createGpuReader(everyMs = GPU_EVERY_MS, read: () => Promise<PerfGpu | null> = readGpu): {\n due: (now: number) => boolean\n read: (now: number) => Promise<PerfGpu | null>\n} {\n let last: PerfGpu | null = null\n let at = Number.NEGATIVE_INFINITY\n const due = (now: number): boolean => process.platform === 'darwin' && now - at >= everyMs\n return {\n due,\n async read(now) {\n if (!due(now))\n return last\n last = await read()\n at = now\n return last\n },\n }\n}\n","/**\n * A process's OS threads, read when someone expands it (never sampled).\n * macOS `ps -M` lists them without names; Linux `ps -L` names them.\n */\nimport type { PerfOsThread } from './types'\nimport { run } from './ps'\n\n/**\n * Parses `ps -M -p <pid>` (macOS): one line per thread, the first with the\n * user and command around the same columns.\n *\n * @param text - Its output.\n * @returns The threads, busiest first.\n */\nexport function parseMacThreads(text: string): PerfOsThread[] {\n const threads: PerfOsThread[] = []\n for (const line of text.split('\\n').slice(1)) {\n const match = /\\s(\\d+\\.\\d+)\\s+\\S+\\s+\\S+\\s+[\\d:.]+\\s+[\\d:.]+/.exec(line)\n if (!match)\n continue\n threads.push({ id: String(threads.length + 1), label: '', cpu: Number(match[1]) })\n }\n return threads.sort((a, b) => b.cpu - a.cpu)\n}\n\n/**\n * Parses `ps -L -o tid=,pcpu=,comm= -p <pid>` (Linux).\n *\n * @param text - Its output.\n * @returns The threads, busiest first.\n */\nexport function parseLinuxThreads(text: string): PerfOsThread[] {\n const threads: PerfOsThread[] = []\n for (const line of text.split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+(?:\\.\\d+)?)\\s+(\\S.*)$/.exec(line)\n if (match)\n threads.push({ id: match[1]!, label: match[3]!.trim(), cpu: Number(match[2]) })\n }\n return threads.sort((a, b) => b.cpu - a.cpu)\n}\n\n/**\n * The OS threads of a process, busiest first.\n *\n * @param pid - The process.\n * @returns Empty on a platform without a reader, or when the process has gone.\n */\nexport async function readOsThreads(pid: number): Promise<PerfOsThread[]> {\n try {\n if (process.platform === 'darwin')\n return parseMacThreads(await run('ps', ['-M', '-p', String(pid)]))\n if (process.platform === 'linux')\n return parseLinuxThreads(await run('ps', ['-L', '-o', 'tid=,pcpu=,comm=', '-p', String(pid)]))\n }\n catch {\n // Gone between the sample and the click.\n }\n return []\n}\n","/**\n * The pids Zidane started for its own work (MCP servers, git, linters): this\n * process's registry and, when another host's tree is sampled too, that\n * host's (the desktop app's daemon, `daemon.ownedProcesses`). They lead\n * their own process group like a session's command, so `./processes.ts`\n * needs this list to count them as the app's. Read with them, the MCP\n * servers' processes (`mcp.instances`): which server a pid runs and whether\n * sessions share it; and the pids started for a session\n * (`daemon.sessionProcesses`): which session a process and its subtree run for.\n *\n * The other host's answers never hold a tick for long: one round of requests\n * at a time, awaited at most `timeoutMs`; past that, or when one fails, the\n * tick uses its last answer (a late one lands for the next tick). An older\n * host that lacks a method answers nothing for it. A new host (another pid)\n * starts empty.\n */\nimport type { PerfProcessMcp, PerfProcessSession } from './types'\nimport { AcpErrorCode } from '../acp/types'\n\n/** One owned process; the role is not read here. */\nexport interface OwnedPid {\n /** The process id. */\n pid: number\n}\n\n/** One process started for a session, as `daemon.sessionProcesses` lists it. */\nexport interface SessionPid {\n /** The process id. */\n pid: number\n /** The session's id on its host. */\n sessionId: string\n}\n\n/** One MCP server process, as `mcp.instances` lists it. */\nexport interface OwnedMcpInstance {\n /** The MCP server's name. */\n name: string\n /** One process for every session (`true`), or one session's. */\n shared: boolean\n /** Sessions holding it now. */\n sessions: number\n /** Its process; absent before it started (or for a remote server). */\n pid?: number\n}\n\n/** Another host whose tree is sampled too (the desktop app's daemon), as far as this module needs it. */\nexport interface OwnedDaemon {\n /** The host's process id: a new pid is a new host, whose answers start empty. */\n pid: number\n /** `daemon.ownedProcesses`. */\n ownedProcesses: () => Promise<readonly OwnedPid[]>\n /** `mcp.instances`. */\n mcpInstances: () => Promise<readonly OwnedMcpInstance[]>\n /** `daemon.sessionProcesses`. */\n sessionProcesses: () => Promise<readonly SessionPid[]>\n}\n\nexport interface OwnedDeps {\n /** This process's registry (`listOwnedProcesses`). */\n local: () => readonly OwnedPid[]\n /** This process's MCP server processes (its host's `mcpServers.instances()`). */\n localMcp: () => readonly OwnedMcpInstance[]\n /** This process's session processes (`listSessionProcesses`). */\n localSessions: () => readonly SessionPid[]\n /** The other host; `null` when there is none. Default: none. */\n daemon?: () => OwnedDaemon | null\n /** How long a tick waits for the other host. Default 500 ms. */\n timeoutMs?: number\n}\n\n/** One tick's reading. */\nexport interface Owned {\n /** Every pid Zidane started for its own work, local and the other host's. */\n pids: Set<number>\n /** The MCP server each MCP process runs, by pid. */\n mcp: Map<number, PerfProcessMcp>\n /** The session each process was started for (its id on its host), by pid. */\n sessions: Map<number, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 500\n\n/** The host predates the method: it answers nothing for it. */\nfunction isMethodNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === AcpErrorCode.MethodNotFound\n}\n\n/** Resolves when `promise` settles or after `ms`, whichever is first. */\nasync function within(promise: Promise<void>, ms: number): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const late = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, ms)\n timer.unref?.()\n })\n try {\n await Promise.race([promise, late])\n }\n finally {\n clearTimeout(timer)\n }\n}\n\ninterface DaemonAnswer {\n daemonPid: number\n pids: readonly number[]\n mcp: readonly OwnedMcpInstance[]\n sessions: readonly SessionPid[]\n}\n\n/**\n * The owned-process reader a sampler calls once a tick: the local registries, and the other host's last answers.\n *\n * @param deps - The local registries and the other host.\n * @returns A reader for one tick: the owned pids, the MCP processes and the session processes, local and the other host's.\n */\nexport function createOwnedProcesses(deps: OwnedDeps): () => Promise<Owned> {\n const { local, localMcp, localSessions, daemon = () => null, timeoutMs = DEFAULT_TIMEOUT_MS } = deps\n /** The last answer, per daemon pid. */\n let last: DaemonAnswer | null = null\n let pending: Promise<void> | null = null\n\n const ask = (target: OwnedDaemon): Promise<void> => {\n const keep = (part: Partial<Omit<DaemonAnswer, 'daemonPid'>>): void => {\n if (last?.daemonPid === target.pid)\n last = { ...last, ...part }\n }\n // Any failure but a missing method keeps the last answer.\n const settle = <T>(request: Promise<T>, onAnswer: (answer: T | null) => void): Promise<void> =>\n request.then(onAnswer, (err: unknown) => {\n if (isMethodNotFound(err))\n onAnswer(null)\n })\n const request: Promise<void> = Promise.all([\n settle(target.ownedProcesses(), rows => keep({ pids: rows?.map(row => row.pid) ?? [] })),\n settle(target.mcpInstances(), rows => keep({ mcp: rows ?? [] })),\n settle(target.sessionProcesses(), rows => keep({ sessions: rows ?? [] })),\n ])\n .then(() => undefined)\n .finally(() => {\n if (pending === request)\n pending = null\n })\n return request\n }\n\n const daemonAnswer = async (): Promise<DaemonAnswer | null> => {\n const target = daemon()\n if (!target) {\n last = null\n return null\n }\n if (last?.daemonPid !== target.pid) {\n last = { daemonPid: target.pid, pids: [], mcp: [], sessions: [] }\n pending = null\n }\n pending ??= ask(target)\n await within(pending, timeoutMs)\n return last\n }\n\n return async () => {\n const answer = await daemonAnswer()\n const mcp = new Map<number, PerfProcessMcp>()\n for (const { pid, name, shared, sessions } of [...readLocalMcp(localMcp), ...(answer?.mcp ?? [])]) {\n if (pid !== undefined)\n mcp.set(pid, { name, shared, sessions })\n }\n const sessions = new Map<number, string>()\n for (const { pid, sessionId } of [...localSessions(), ...(answer?.sessions ?? [])]) sessions.set(pid, sessionId)\n return { pids: new Set([...local().map(entry => entry.pid), ...(answer?.pids ?? [])]), mcp, sessions }\n }\n}\n\n/**\n * Names each process's session as the reader does, and titles it, read once per session.\n *\n * @param sessions - The session id of each process (`Owned.sessions`).\n * @param titleOf - A session's title; `undefined` while it has none.\n * @param keyOf - The reader's key for a session id (the desktop app's `SessionKey`). Default: the id.\n * @returns The session of each process, by pid.\n */\nexport function labelSessions(\n sessions: ReadonlyMap<number, string>,\n titleOf: (sessionId: string) => string | undefined,\n keyOf: (sessionId: string) => string = id => id,\n): Map<number, PerfProcessSession> {\n const named = new Map<string, PerfProcessSession>()\n const out = new Map<number, PerfProcessSession>()\n for (const [pid, sessionId] of sessions) {\n let session = named.get(sessionId)\n if (!session) {\n const title = titleOf(sessionId)\n session = { id: keyOf(sessionId), ...(title ? { title } : {}) }\n named.set(sessionId, session)\n }\n out.set(pid, session)\n }\n return out\n}\n\n/** The local host's MCP processes; none when it can't say. */\nfunction readLocalMcp(read: () => readonly OwnedMcpInstance[]): readonly OwnedMcpInstance[] {\n try {\n return read()\n }\n catch {\n return []\n }\n}\n","/**\n * A host's process tree as performance rows: the OS rows under the host's\n * processes (language servers, shells, MCP servers, dev servers), each with\n * its CPU from the change in CPU time since the previous sample, in percent\n * of one core, and whose cost it is ({@link PerfOrigin}). Pure.\n *\n * Whose cost: Zidane's own (`app`) are the host's processes (the desktop\n * app's Electron processes, the daemon, a server) and what they start to do\n * the app's work (MCP and language servers, git, the linters, the monitor's\n * `ps`). `launched` is what a session or the user ran. Every place that\n * runs a command for them (the shell tool, background tasks, terminals, the\n * composer's `!` commands) starts it as the leader of a new process group,\n * so that stopping it stops its whole tree. So a direct child of a host\n * process that leads its own group starts a launched subtree, and everything\n * under it is launched. Some of the app's own spawns lead a group too, for\n * the same reason (MCP servers, and git, gh and the linters through\n * `runBounded`): their spawner registers them in the owned-process registry\n * (`src/contexts/owned-processes.ts`, read by `./owned.ts`), and an owned\n * pid and its subtree are the app's. The others (language servers) stay in\n * their parent's group. The command line can't tell: `sh -c 'bun bench.ts'`\n * execs into `bun`, which reads like an MCP server started with `bun`.\n *\n * Whose session: a process a session started (its shell tool's commands and\n * background tasks, its terminals, its own MCP servers, its `!` commands;\n * the session-process registry, read by `./owned.ts`) runs for it, and so\n * does everything under it.\n */\nimport type { PsRow } from './ps'\nimport type { PerfOrigin, PerfProcess, PerfProcessKind, PerfProcessMcp, PerfProcessSession, PerfTotals } from './types'\nimport { commandLabel, cpuPercent } from './ps'\n\n/** CPU seconds per pid at the previous sample, for the rows' CPU. */\nexport type CpuMemory = Map<number, { cpuSeconds: number, at: number }>\n\n/** How a tree's root shows: a host process (the daemon, a server). */\nexport interface TreeRoot {\n /** What the root process is. */\n kind: PerfProcessKind\n /** Its row's name: \"Daemon\", \"Server\". */\n label: string\n}\n\n/** What {@link treeProcesses} turns into rows. */\nexport interface TreeInput {\n /** The OS rows under the roots (`descendants`), roots included, parents first. */\n tree: readonly PsRow[]\n /** The rows shown as roots (no parent), with their kind and label. */\n roots: ReadonlyMap<number, TreeRoot>\n /** The host's own processes: a group leader started directly under one is launched work, unless owned. */\n hosts: ReadonlySet<number>\n /** Rows another source lists (the desktop app's Electron processes): their CPU baseline is kept, no row is made. */\n listed?: ReadonlySet<number>\n /** The pids Zidane started for its own work (MCP servers, tool runs): never launched, though they lead a group. */\n owned: ReadonlySet<number>\n /** The MCP server each MCP process runs, by pid (`./owned.ts`). */\n mcp?: ReadonlyMap<number, PerfProcessMcp>\n /** The session each process a session started runs for, by pid: it and its subtree carry it. */\n sessions?: ReadonlyMap<number, PerfProcessSession>\n /** Read and updated: the rows' CPU seconds (rows seen now are kept, rows gone are dropped). */\n previous: CpuMemory\n /** When the rows were read (epoch ms): the CPU's time base. */\n now: number\n}\n\n/**\n * One row per OS process of the tree, in the tree's order (`listed` ones left out).\n *\n * @param input - The tree, its roots and hosts, what Zidane owns and the CPU memory.\n * @returns The processes.\n */\nexport function treeProcesses(input: TreeInput): PerfProcess[] {\n const { tree, roots, hosts, listed, owned, mcp, sessions, previous, now } = input\n const inTree = new Set(tree.map(row => row.pid))\n const launched = new Set<number>()\n const sessionOf = new Map<number, PerfProcessSession>()\n const out: PerfProcess[] = []\n for (const row of tree) {\n const before = previous.get(row.pid)\n previous.set(row.pid, { cpuSeconds: row.cpuSeconds, at: now })\n if (listed?.has(row.pid))\n continue\n const root = roots.get(row.pid)\n // The rule is in this module's doc. `tree` lists parents first, so a launched parent is known by now.\n const startsLaunched = hosts.has(row.ppid) && row.pgid === row.pid && !owned.has(row.pid)\n const isLaunched = !root && (launched.has(row.ppid) || startsLaunched)\n if (isLaunched)\n launched.add(row.pid)\n const server = mcp?.get(row.pid)\n // Parents come first: a session's process is known before its children.\n const session = root ? undefined : (sessions?.get(row.pid) ?? sessionOf.get(row.ppid))\n if (session)\n sessionOf.set(row.pid, session)\n out.push({\n pid: row.pid,\n parent: root ? null : inTree.has(row.ppid) ? row.ppid : null,\n kind: root?.kind ?? 'child',\n label: root?.label ?? commandLabel(row.args),\n detail: row.args,\n origin: isLaunched ? 'launched' : 'app',\n cpu: before ? cpuPercent(before.cpuSeconds, row.cpuSeconds, now - before.at) : 0,\n memory: row.rssKb * 1024,\n ...(server ? { mcp: server } : {}),\n ...(session ? { session } : {}),\n })\n }\n for (const pid of previous.keys()) {\n if (!inTree.has(pid))\n previous.delete(pid)\n }\n return out\n}\n\n/**\n * CPU and memory summed per origin.\n *\n * @param processes - One sample's.\n */\nexport function originTotals(processes: readonly PerfProcess[]): Record<PerfOrigin, PerfTotals> {\n const totals: Record<PerfOrigin, PerfTotals> = { app: { cpu: 0, memory: 0 }, launched: { cpu: 0, memory: 0 } }\n for (const process of processes) {\n totals[process.origin].cpu += process.cpu\n totals[process.origin].memory += process.memory\n }\n return totals\n}\n","/** A fixed-capacity list that drops its oldest item when full. */\nexport interface Ring<T> {\n /** Adds an item, dropping the oldest when full. */\n push: (item: T) => void\n /** Oldest first. */\n items: () => T[]\n /** The newest item, if any. */\n last: () => T | undefined\n /** Empties it. */\n clear: () => void\n}\n\n/**\n * Creates a ring.\n *\n * @param capacity - How many items it keeps.\n */\nexport function createRing<T>(capacity: number): Ring<T> {\n const buffer: T[] = []\n let start = 0\n return {\n push(item) {\n if (buffer.length < capacity) {\n buffer.push(item)\n }\n else {\n buffer[start] = item\n start = (start + 1) % capacity\n }\n },\n items: () => [...buffer.slice(start), ...buffer.slice(0, start)],\n last: () => (buffer.length === 0 ? undefined : buffer[(start + buffer.length - 1) % buffer.length]),\n clear() {\n buffer.length = 0\n start = 0\n },\n }\n}\n","/**\n * When sampling runs: only while at least one watcher is subscribed (a\n * window with Settings › Performance or the overlay open, a connection that\n * called `perf.watch`). The first watcher starts the loop, the last one\n * stops it. Ticks never overlap: the next one is scheduled once the previous\n * sample is in. Samples land in a ring (the last 5 minutes at 2 s).\n */\nimport { createRing } from './ring'\n\n/** The timer the loop waits on (tests drive their own). */\nexport interface SamplerTimers {\n set: (run: () => void, ms: number) => unknown\n clear: (handle: unknown) => void\n}\n\n/** What {@link createSampler} samples, and how often. */\nexport interface SamplerOptions<T> {\n /** Takes one sample. */\n collect: () => Promise<T>\n /** Called when the loop starts and stops (the delay histogram, the windows' answers). */\n onActive?: (active: boolean) => void\n /** Between the end of one sample and the start of the next. Default {@link SAMPLE_INTERVAL_MS}. */\n intervalMs?: number\n /** Samples kept. Default {@link SAMPLE_CAPACITY}. */\n capacity?: number\n /** The timer the loop waits on. Default real timers that never hold the process open. */\n timers?: SamplerTimers\n}\n\n/** A sampling loop that runs only while someone watches. */\nexport interface Sampler<T> {\n /** Gets each sample from now on; the first subscriber starts the loop. Returns the unsubscribe. */\n subscribe: (listener: (sample: T) => void) => () => void\n /** The samples held, oldest first. */\n history: () => T[]\n /** Whether the loop runs. */\n active: () => boolean\n /** Resolves with the next sample, starting the loop for it if nobody watches. */\n next: () => Promise<T>\n}\n\n/** Between two samples, ms. */\nexport const SAMPLE_INTERVAL_MS = 2_000\n/** Samples kept: 5 minutes at {@link SAMPLE_INTERVAL_MS}. */\nexport const SAMPLE_CAPACITY = 150\n\n/** Real timers that never hold the process open. */\nconst REAL_TIMERS: SamplerTimers = {\n set: (run, ms) => {\n const timer = setTimeout(run, ms)\n timer.unref?.()\n return timer\n },\n clear: handle => clearTimeout(handle as ReturnType<typeof setTimeout>),\n}\n\n/**\n * A sampling loop over `collect`: it starts with the first subscriber, stops with the last, never overlaps two ticks, and keeps the last `capacity` samples.\n *\n * @param options - What to sample (`collect`), the activity callback, the interval, the capacity and the timers.\n */\nexport function createSampler<T>({\n collect,\n onActive,\n intervalMs = SAMPLE_INTERVAL_MS,\n capacity = SAMPLE_CAPACITY,\n timers = REAL_TIMERS,\n}: SamplerOptions<T>): Sampler<T> {\n const ring = createRing<T>(capacity)\n const listeners = new Set<(sample: T) => void>()\n let timer: unknown = null\n let running = false\n\n const tick = async (): Promise<void> => {\n timer = null\n let sample: T | null = null\n try {\n sample = await collect()\n }\n catch {\n // A failed read skips one sample; the next tick tries again.\n }\n if (!running)\n return\n if (sample) {\n ring.push(sample)\n for (const listener of [...listeners]) listener(sample)\n }\n if (running)\n timer = timers.set(() => void tick(), intervalMs)\n }\n\n const start = (): void => {\n if (running)\n return\n running = true\n onActive?.(true)\n // The first read only sets the baselines CPU figures are diffed against.\n timer = timers.set(() => void tick(), intervalMs)\n void collect().catch(() => undefined)\n }\n const stop = (): void => {\n if (!running)\n return\n running = false\n if (timer !== null)\n timers.clear(timer)\n timer = null\n onActive?.(false)\n }\n\n const subscribe = (listener: (sample: T) => void): (() => void) => {\n listeners.add(listener)\n start()\n return () => {\n if (!listeners.delete(listener))\n return\n if (listeners.size === 0)\n stop()\n }\n }\n\n return {\n subscribe,\n history: () => ring.items(),\n active: () => running,\n next: () =>\n new Promise((resolve) => {\n const off = subscribe((sample) => {\n off()\n resolve(sample)\n })\n }),\n }\n}\n","/**\n * A host's own performance figures, for `perf.*` (`src/ipc/daemon/handlers/perf.ts`):\n * the host process (the daemon, a server) as the root of its tree, with what\n * it runs for itself and for each session (`./processes.ts`), its event\n * loop, the GPU (macOS) and the monitor's own cost. A host has no windows,\n * so a sample's channels and slow frames are empty. Sampling runs only while\n * something watches (`./sampler.ts`).\n */\nimport type { OwnedMcpInstance } from './owned'\nimport type { CpuMemory } from './processes'\nimport type { Sampler } from './sampler'\nimport type { PerfOsThread, PerfSample } from './types'\nimport { availableParallelism } from 'node:os'\nimport { performance } from 'node:perf_hooks'\nimport { listOwnedProcesses } from '../contexts/owned-processes'\nimport { listSessionProcesses } from '../contexts/session-processes'\nimport { createMainThreadMeter } from './event-loop'\nimport { createGpuReader } from './gpu'\nimport { readOsThreads } from './os-threads'\nimport { createOwnedProcesses, labelSessions } from './owned'\nimport { originTotals, treeProcesses } from './processes'\nimport { ownCommands, readProcessTree } from './ps'\nimport { createSampler } from './sampler'\n\n/** What {@link createHostPerf} needs of its host. */\nexport interface HostPerfOptions {\n /** The host process's row: `Server`, `Daemon`. Its event loop is `<label> event loop`. */\n label: string\n /** The MCP server processes of the host's sessions (its `mcpServers.instances()`). */\n mcpInstances: () => readonly OwnedMcpInstance[]\n /** A session's title; `undefined` while it has none. */\n sessionTitle: (sessionId: string) => Promise<string | undefined>\n /** Between two samples. Default 2 s. */\n intervalMs?: number\n}\n\n/** A host's performance monitor: what `perf.watch`, `perf.history` and `perf.threads` answer from. */\nexport interface HostPerf {\n /** Gets each sample from now on; the first watcher starts sampling, the last unsubscribe stops it. */\n watch: Sampler<PerfSample>['subscribe']\n /** The samples held, oldest first. */\n history: () => PerfSample[]\n /** A process's OS threads, busiest first. Rejects with {@link PerfProcessNotFoundError} for a pid outside the host's tree. */\n threads: (pid: number) => Promise<PerfOsThread[]>\n}\n\n/** `perf.threads` asked for a process the host doesn't list. */\nexport class PerfProcessNotFoundError extends Error {\n /**\n * @param pid - The process asked for.\n */\n constructor(\n /** The process asked for. */\n readonly pid: number,\n ) {\n super(`Process ${pid} is not one of this host's.`)\n this.name = 'PerfProcessNotFoundError'\n }\n}\n\n/** Each session's title, read once per tick; one that fails reads as none. */\nasync function titles(sessions: ReadonlyMap<number, string>, read: HostPerfOptions['sessionTitle']): Promise<Map<string, string>> {\n const out = new Map<string, string>()\n await Promise.all([...new Set(sessions.values())].map(async (sessionId) => {\n const title = await read(sessionId).catch(() => undefined)\n if (title)\n out.set(sessionId, title)\n }))\n return out\n}\n\n/**\n * Creates the host's monitor. Nothing runs until the first watch.\n *\n * @param options - The host's label, MCP processes and session titles.\n */\nexport function createHostPerf(options: HostPerfOptions): HostPerf {\n const { label, mcpInstances, sessionTitle, intervalMs } = options\n const meter = createMainThreadMeter()\n const gpu = createGpuReader()\n const owned = createOwnedProcesses({ local: listOwnedProcesses, localMcp: mcpInstances, localSessions: listSessionProcesses })\n const previous: CpuMemory = new Map()\n const cores = availableParallelism()\n const since = Date.now()\n const roots = new Map([[process.pid, { kind: 'daemon' as const, label }]])\n const hosts = new Set([process.pid])\n let lastAt = since\n\n const collect = async (): Promise<PerfSample> => {\n ownCommands.clear()\n const now = Date.now()\n const elapsedMs = now - lastAt\n lastAt = now\n const ownedPending = owned()\n const commandsStart = performance.now()\n const [tree, gpuNow] = await Promise.all([readProcessTree([process.pid]).catch(() => []), gpu.read(now)])\n const commandMs = performance.now() - commandsStart\n const reading = await ownedPending\n const named = await titles(reading.sessions, sessionTitle)\n\n const start = performance.now()\n const processes = treeProcesses({\n tree,\n roots,\n hosts,\n owned: reading.pids,\n mcp: reading.mcp,\n sessions: labelSessions(reading.sessions, id => named.get(id)),\n previous,\n now,\n })\n const { busy, lag } = meter.read()\n const totals = originTotals(processes)\n const mainMs = performance.now() - start\n return {\n at: now,\n intervalMs: elapsedMs,\n cores,\n processes,\n threads: [{ id: 'main', label: `${label} event loop`, pid: process.pid, kind: 'main', busy, heap: process.memoryUsage().heapUsed }],\n channels: [],\n frames: [],\n countersSince: since,\n lag,\n gpu: gpuNow,\n totals: totals.app,\n launched: totals.launched,\n self: { cpu: elapsedMs > 0 ? (mainMs * 100) / elapsedMs : 0, commandMs },\n }\n }\n\n const sampler = createSampler({\n collect,\n onActive: active => (active ? meter.start() : meter.stop()),\n ...(intervalMs !== undefined ? { intervalMs } : {}),\n })\n\n return {\n watch: sampler.subscribe,\n history: sampler.history,\n async threads(pid) {\n const tree = await readProcessTree([process.pid])\n if (!tree.some(row => row.pid === pid))\n throw new PerfProcessNotFoundError(pid)\n return readOsThreads(pid)\n },\n }\n}\n","/**\n * Gen-AI attribute keys and span-attribute builders shared by the\n * {@link createTracingHooks} (`./tracing`) hook installers.\n */\n\nimport type { TracingConventions } from './tracing'\nimport type { SessionMessage } from './types'\n\n// ---------------------------------------------------------------------------\n// Gen AI semantic-convention attribute keys\n// ---------------------------------------------------------------------------\n\n/**\n * Gen-AI attribute keys for the Sentry and OTel conventions. Keys whose\n * comment names one convention are emitted only when `conventions` selects\n * it. The rest carry identical semantics in both.\n */\nexport const GEN_AI = {\n system: 'gen_ai.system',\n operationName: 'gen_ai.operation.name',\n requestModel: 'gen_ai.request.model',\n responseModel: 'gen_ai.response.model',\n responseFinishReasons: 'gen_ai.response.finish_reasons',\n responseId: 'gen_ai.response.id',\n responseStreaming: 'gen_ai.response.streaming',\n responseTokensPerSecond: 'gen_ai.response.tokens_per_second',\n // Sentry uses seconds; OTel uses ms. We emit one or both based on `conventions`.\n responseTimeToFirstTokenSeconds: 'gen_ai.response.time_to_first_token',\n responseTimeToFirstTokenMs: 'gen_ai.client.time_to_first_token',\n // Usage — Sentry expects input_tokens to INCLUDE cached, with subkeys for cache slices.\n usageInputTokens: 'gen_ai.usage.input_tokens',\n usageOutputTokens: 'gen_ai.usage.output_tokens',\n usageTotalTokens: 'gen_ai.usage.total_tokens',\n // Sentry-specific cache keys.\n usageInputTokensCached: 'gen_ai.usage.input_tokens.cached',\n usageInputTokensCacheWrite: 'gen_ai.usage.input_tokens.cache_write',\n usageOutputTokensReasoning: 'gen_ai.usage.output_tokens.reasoning',\n // OTel cache keys (older / vendor-neutral).\n usageCacheReadInputTokens: 'gen_ai.usage.cache_read_input_tokens',\n usageCacheCreationInputTokens: 'gen_ai.usage.cache_creation_input_tokens',\n usageReasoningTokens: 'gen_ai.usage.reasoning_tokens',\n // Cost — Sentry splits, OTel rolls up.\n costTotalTokens: 'gen_ai.cost.total_tokens',\n costInputTokens: 'gen_ai.cost.input_tokens',\n costOutputTokens: 'gen_ai.cost.output_tokens',\n usageCostUsd: 'gen_ai.usage.cost_usd',\n // Tool — Sentry uses `tool.call.arguments` / `tool.call.result`; OTel uses `tool.input` / `tool.output` (deprecated in Sentry).\n toolName: 'gen_ai.tool.name',\n toolDescription: 'gen_ai.tool.description',\n toolType: 'gen_ai.tool.type',\n toolCallId: 'gen_ai.tool.call.id',\n toolCallArguments: 'gen_ai.tool.call.arguments',\n toolCallResult: 'gen_ai.tool.call.result',\n toolMessage: 'gen_ai.tool.message',\n toolInputDeprecated: 'gen_ai.tool.input',\n toolOutputDeprecated: 'gen_ai.tool.output',\n // Request configuration.\n requestMaxTokens: 'gen_ai.request.max_tokens',\n requestTemperature: 'gen_ai.request.temperature',\n requestTopP: 'gen_ai.request.top_p',\n requestTopK: 'gen_ai.request.top_k',\n requestSeed: 'gen_ai.request.seed',\n requestFrequencyPenalty: 'gen_ai.request.frequency_penalty',\n requestPresencePenalty: 'gen_ai.request.presence_penalty',\n // Message content surfaces (Sentry conversation viewer).\n inputMessages: 'gen_ai.input.messages',\n /** zidane: oldest messages left out of `inputMessages` by its size cap. */\n inputMessagesDropped: 'gen_ai.agent.input_messages_dropped',\n outputMessages: 'gen_ai.output.messages',\n systemInstructions: 'gen_ai.system_instructions',\n toolDefinitions: 'gen_ai.tool.definitions',\n // Agent identity.\n agentName: 'gen_ai.agent.name',\n agentRunId: 'gen_ai.agent.run.id',\n agentParentRunId: 'gen_ai.agent.parent_run.id',\n agentDepth: 'gen_ai.agent.depth',\n pipelineName: 'gen_ai.pipeline.name',\n turn: 'gen_ai.agent.turn',\n // MCP-specific (zidane extension; no Sentry op equivalent).\n mcpServer: 'gen_ai.mcp.server',\n mcpToolCount: 'gen_ai.mcp.tool_count',\n} as const\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Compose namespace prefix once. */\nexport function prefixed(namespace: string | undefined, name: string): string {\n return namespace ? `${namespace}/${name}` : name\n}\n\n/** Drop undefined entries — tracers vary on how they render `undefined`. */\nexport function compact(attrs: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/**\n * One part of a message in Sentry's `{role, parts: [{type, content}]}` shape.\n * Per Sentry footnote [7], binary blobs in image content are replaced with\n * `\"[Blob substitute]\"`.\n */\ninterface SentryMessagePart {\n type: 'text' | 'tool_call' | 'tool_result' | 'reasoning'\n content?: string\n name?: string\n arguments?: string\n result?: string\n call_id?: string\n}\ninterface SentryMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n parts: SentryMessagePart[]\n}\n\n/**\n * Wide-shape block record. `SessionContentBlock` is a discriminated\n * union — accepting the wide record lets us peek at the discriminator\n * without re-importing the entire union at this layer.\n */\ninterface AnyBlock { type: string, [k: string]: unknown }\n\nfunction blockToParts(block: AnyBlock): SentryMessagePart[] {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', content: typeof block.text === 'string' ? block.text : '' }]\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n return [{ type: 'text', content: '[Blob substitute]' }]\n case 'tool_call':\n return [{\n type: 'tool_call',\n ...(typeof block.name === 'string' ? { name: block.name } : {}),\n ...(typeof block.id === 'string' ? { call_id: block.id } : {}),\n arguments: safeJson(block.input),\n }]\n case 'tool_result': {\n const output = block.output\n const text = typeof output === 'string'\n ? output\n : Array.isArray(output)\n ? (output as Array<{ type: string, text?: string }>)\n .map(b => (b.type === 'image' || b.type === 'audio' || b.type === 'video' || b.type === 'document') ? '[Blob substitute]' : (b.text ?? ''))\n .join('\\n')\n : ''\n return [{\n type: 'tool_result',\n ...(typeof block.callId === 'string' ? { call_id: block.callId } : {}),\n content: text,\n }]\n }\n case 'thinking':\n return [{ type: 'reasoning', content: typeof block.text === 'string' ? block.text : '' }]\n default:\n return []\n }\n}\n\n/**\n * Convert a zidane message (a `SessionMessage` or `StreamOptions.messages`\n * entry) into Sentry's message shape. Tool calls become `{type: 'tool_call'}`\n * parts, tool results become `{type: 'tool_result'}` parts.\n */\nfunction messageToSentry(msg: { role: 'user' | 'assistant', content: readonly AnyBlock[] }): SentryMessage {\n const parts: SentryMessagePart[] = []\n for (const block of msg.content)\n parts.push(...blockToParts(block))\n return { role: msg.role, parts }\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value ?? null)\n }\n catch {\n return '\"[unserializable]\"'\n }\n}\n\n/**\n * Stringify an array of `SessionMessage`s into Sentry's `gen_ai.input.messages`\n * format. Each entry's role + content is mapped via `messageToSentry`.\n * Returns `undefined` when the input is empty or unserializable.\n */\nexport function stringifyMessages(messages: readonly SessionMessage[]): string | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const sentry = messages.map(m => messageToSentry({\n role: m.role,\n content: m.content as readonly AnyBlock[],\n }))\n return JSON.stringify(sentry)\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Upper bound on a chat span's `gen_ai.input.messages`, in characters. The\n * attribute carries the whole outgoing transcript, so without a bound a long\n * session serializes (and ships) every earlier message again on every turn.\n */\nexport const INPUT_MESSAGES_MAX_CHARS = 128 * 1024\n\n/**\n * {@link stringifyMessages} for the newest messages that fit in `maxChars`\n * (the newest one always does), serializing nothing older than the cut.\n * `dropped` counts the oldest messages left out; with none dropped, `json`\n * is exactly what `stringifyMessages` returns.\n */\nexport function stringifyRecentMessages(messages: readonly SessionMessage[], maxChars: number): { json: string, dropped: number } | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const kept: string[] = []\n let size = 2\n let i = messages.length - 1\n for (; i >= 0; i--) {\n const m = messages[i]!\n const json = JSON.stringify(messageToSentry({ role: m.role, content: m.content as readonly AnyBlock[] }))\n if (kept.length > 0 && size + json.length + 1 > maxChars)\n break\n kept.push(json)\n size += json.length + 1\n }\n return { json: `[${kept.reverse().join(',')}]`, dropped: i + 1 }\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Stringify a `StreamOptions.tools` array into Sentry's\n * `gen_ai.tool.definitions` format: `[{name, description?, parameters?}, …]`.\n *\n * The input is `unknown[]` because `StreamOptions.tools` carries the\n * provider-formatted shape (Anthropic / OpenAI / OpenAI-compat all\n * differ). We probe the three names that overlap (`name`, `description`,\n * `inputSchema` / `parameters`) and degrade gracefully otherwise.\n */\nexport function stringifyToolDefs(tools: readonly unknown[] | undefined): string | undefined {\n if (!tools || tools.length === 0)\n return undefined\n try {\n return JSON.stringify(tools.map((raw) => {\n if (!raw || typeof raw !== 'object')\n return { name: 'anonymous' }\n const t = raw as Record<string, unknown>\n const name = typeof t.name === 'string' ? t.name : 'anonymous'\n const description = typeof t.description === 'string' ? t.description : undefined\n // OpenAI's wire format calls it `parameters`; Anthropic / ToolSpec\n // call it `input_schema` / `inputSchema`. Probe all three.\n const parameters = t.parameters ?? t.input_schema ?? t.inputSchema\n return {\n name,\n ...(description ? { description } : {}),\n ...(parameters !== undefined ? { parameters } : {}),\n }\n }))\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Build the usage attribute set per the configured conventions. Sentry\n * expects `input_tokens` to include cached, with `input_tokens.cached`\n * + `input_tokens.cache_write` subkeys; OTel splits cache into\n * separate top-level keys. `'both'` emits everything.\n */\nexport function buildUsageAttrs(\n usage: {\n input: number\n output: number\n cacheRead?: number\n cacheCreation?: number\n thinking?: number\n cost?: number\n modelId?: string\n finishReason?: string\n timeToFirstTokenMs?: number\n },\n conventions: TracingConventions,\n): Record<string, unknown> {\n const cacheRead = usage.cacheRead ?? 0\n const cacheWrite = usage.cacheCreation ?? 0\n // Per Sentry: `input_tokens` includes cached. zidane reports `input` as\n // the uncached new-input slice, so the headline number is the sum.\n const inputInclCache = usage.input + cacheRead + cacheWrite\n const totalTokens = inputInclCache + usage.output\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const wantOtel = conventions === 'otel' || conventions === 'both'\n\n const out: Record<string, unknown> = {\n [GEN_AI.responseModel]: usage.modelId,\n [GEN_AI.responseFinishReasons]: usage.finishReason ? [usage.finishReason] : undefined,\n [GEN_AI.usageOutputTokens]: usage.output,\n [GEN_AI.usageTotalTokens]: totalTokens,\n }\n\n if (wantSentry) {\n out[GEN_AI.usageInputTokens] = inputInclCache\n if (cacheRead > 0)\n out[GEN_AI.usageInputTokensCached] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageInputTokensCacheWrite] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageOutputTokensReasoning] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.costTotalTokens] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenSeconds] = usage.timeToFirstTokenMs / 1000\n }\n if (wantOtel) {\n // OTel: input_tokens is the uncached slice; cache is separate.\n if (!wantSentry)\n out[GEN_AI.usageInputTokens] = usage.input\n if (cacheRead > 0)\n out[GEN_AI.usageCacheReadInputTokens] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageCacheCreationInputTokens] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageReasoningTokens] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.usageCostUsd] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenMs] = usage.timeToFirstTokenMs\n }\n return out\n}\n","/**\n * Decision span installer behind {@link createTracingHooks} (`./tracing`):\n * one `decide <model>` span per `decision:start` → `decision:end`.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI } from './tracing-attrs'\n\nexport function installTracingDecisionHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { conventions, captureContent, decisionSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n unregisters.push(hooks.hook('decision:start', (ctx) => {\n const span = safeStartSpan(`decide ${ctx.model ?? 'unresolved'}`, {\n [GEN_AI.operationName]: 'decide',\n [GEN_AI.system]: ctx.provider,\n [GEN_AI.requestModel]: ctx.model,\n 'sentry.op': 'gen_ai.decide',\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.decision.id': ctx.decisionId,\n 'gen_ai.decision.questions': ctx.questionNames.join(','),\n 'gen_ai.decision.input_kind': ctx.inputKind,\n })\n if (span)\n decisionSpans.set(ctx.decisionId, span)\n }))\n\n unregisters.push(hooks.hook('decision:end', (ctx) => {\n const span = decisionSpans.get(ctx.decisionId)\n if (!span)\n return\n const attrs: Record<string, unknown> = {\n [GEN_AI.responseModel]: ctx.ok ? ctx.model : undefined,\n 'gen_ai.decision.ok': ctx.ok,\n ...(ctx.usage ? buildUsageAttrs(ctx.usage, conventions) : {}),\n }\n if (!ctx.ok) {\n attrs['error.type'] = ctx.reason\n attrs['error.message'] = ctx.message\n }\n if (captureContent && ctx.answers)\n attrs['gen_ai.decision.answers'] = redact('decision-answers', JSON.stringify(ctx.answers), { decisionId: ctx.decisionId })\n safeSetAttrs(span, attrs)\n safeEnd(decisionSpans, ctx.decisionId)\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { GEN_AI } from './tracing-attrs'\n\n/** MCP `execute_tool` span. */\nexport function installTracingMcpToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, mcpSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n // -----------------------------------------------------------------\n // MCP tool span — OTel Gen AI `execute_tool` operation, MCP variant\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.server}.${ctx.tool}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.toolName]: ctx.displayName,\n // MCP tools are Sentry's \"extension\" tool type — backed by an\n // out-of-process server rather than a local function.\n [GEN_AI.toolType]: 'extension',\n [GEN_AI.toolCallId]: ctx.callId,\n [GEN_AI.mcpServer]: ctx.server,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.mcp.tool': ctx.tool,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.run.id': ctx.runId,\n ...(legacy\n ? {\n server: ctx.server,\n tool: ctx.tool,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n mcpSpans.set(ctx.callId, span)\n if (captureContent) {\n try {\n const redacted = redact('mcp-tool-input', JSON.stringify(ctx.input), {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch { /* ignore */ }\n }\n }\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const span = mcpSpans.get(ctx.callId)\n safeSetAttrs(span, { 'gen_ai.tool.output_bytes': ctx.outputBytes })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('mcp-tool-result', ctx.result, {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n safeSetAttrs(mcpSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(mcpSpans, ctx.callId)\n }))\n}\n\n/** MCP server bootstrap span + server-level events. */\nexport function installTracingMcpServerHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { bootstrapSpans, safeStartSpan, safeSetAttrs, safeEnd, safeAddEvent } = rt\n\n // -----------------------------------------------------------------\n // MCP server bootstrap span\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:start', (ctx) => {\n const span = safeStartSpan(\n `mcp.bootstrap ${ctx.name}`,\n {\n [GEN_AI.operationName]: 'mcp.bootstrap',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.mcpServer]: ctx.name,\n 'gen_ai.mcp.transport': ctx.transport,\n },\n )\n if (span)\n bootstrapSpans.set(ctx.name, span)\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n const ok = ctx.ok\n safeSetAttrs(span, {\n 'gen_ai.mcp.bootstrap.duration_ms': ctx.durationMs,\n 'gen_ai.mcp.bootstrap.ok': ok,\n ...(ok\n ? {\n [GEN_AI.mcpToolCount]: ctx.toolCount,\n 'gen_ai.mcp.lazy': ctx.lazy,\n 'gen_ai.mcp.cached': ctx.cached,\n }\n : {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n }),\n })\n safeEnd(bootstrapSpans, ctx.name)\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n safeAddEvent(span ?? rt.activeRunSpan, 'gen_ai.mcp.auth.required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.mcp.error', {\n 'server': ctx.name,\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI, INPUT_MESSAGES_MAX_CHARS, stringifyMessages, stringifyRecentMessages, stringifyToolDefs } from './tracing-attrs'\n\n/** `invoke_agent` root span: `agent:start` / `agent:done` / `agent:abort`. */\nexport function installTracingAgentHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, runSpans, turnSpans, toolSpans, mcpSpans, spawnSpans, bootstrapSpans, turnTrack, safeStartSpan, safeSetAttrs, endAll } = rt\n\n // -----------------------------------------------------------------\n // agent.run (root)\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n rt.activeSystem = ctx.providerName\n // Empty-string names produce `invoke_agent ` (trailing space).\n // Treat empty as \"no name\" so the span name reads cleanly.\n const displayName = ctx.agentName && ctx.agentName.length > 0\n ? ctx.agentName\n : 'agent'\n const span = safeStartSpan(\n `invoke_agent ${displayName}`,\n {\n [GEN_AI.operationName]: 'invoke_agent',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.agentName]: ctx.agentName,\n [GEN_AI.agentRunId]: ctx.runId,\n [GEN_AI.agentParentRunId]: ctx.parentRunId,\n [GEN_AI.agentDepth]: ctx.depth,\n // Sentry-specific span op hint — backends that key off\n // `op` (Sentry Python/JS) read this directly; OTel\n // backends ignore unknown keys, so it's harmless.\n 'sentry.op': 'gen_ai.invoke_agent',\n ...(legacy\n ? {\n runId: ctx.runId,\n parentRunId: ctx.parentRunId,\n agentName: ctx.agentName,\n depth: ctx.depth,\n }\n : {}),\n },\n ctx.tracingContext,\n )\n if (span) {\n runSpans.set(ctx.runId, span)\n rt.activeRunSpan = span\n }\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n // `agent:done` doesn't carry a runId on its payload (it's `AgentStats`),\n // so close every open run span and stamp the totals on each. This\n // assumes one in-flight run per install.\n const usageAttrs = buildUsageAttrs({\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n timeToFirstTokenMs: stats.timeTillFirstTokenMs,\n }, conventions)\n for (const [, span] of runSpans) {\n safeSetAttrs(span, {\n ...usageAttrs,\n [GEN_AI.turn]: stats.turns,\n 'gen_ai.agent.elapsed_ms': stats.elapsed,\n ...(legacy\n ? {\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n totalCacheRead: stats.totalCacheRead,\n totalCacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n turns: stats.turns,\n elapsed: stats.elapsed,\n }\n : {}),\n })\n }\n endAll(runSpans)\n // Defensive — close anything else still hanging open.\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n rt.activeRunSpan = undefined\n rt.activeSystem = undefined\n turnTrack.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n for (const [, span] of runSpans)\n safeSetAttrs(span, { 'gen_ai.agent.status': 'aborted' })\n }))\n}\n\n/** `chat` turn span + stream events. */\nexport function installTracingTurnHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, turnSpans, turnTrack, safeStartSpan, safeSetAttrs, safeEnd, safeAddEvent, redact } = rt\n\n // -----------------------------------------------------------------\n // Turn span — OTel Gen AI `chat` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n const requestedModel = ctx.options.model\n // Built-in Sentry/OTel attrs that don't depend on content capture.\n const baseAttrs: Record<string, unknown> = {\n [GEN_AI.operationName]: 'chat',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.requestModel]: requestedModel,\n [GEN_AI.turn]: ctx.turn,\n [GEN_AI.responseStreaming]: true,\n [GEN_AI.requestMaxTokens]: ctx.options.maxTokens,\n 'sentry.op': 'gen_ai.chat',\n 'gen_ai.agent.turn_id': ctx.turnId,\n ...(legacy ? { turnId: ctx.turnId, turn: ctx.turn } : {}),\n }\n // Optionally surface conversation content — prompts, system\n // instructions, available tools. Each piece flows through the\n // redact seam so hosts can scrub secrets without losing the\n // structural attribute. Skipping content keeps the span cheap\n // and privacy-safe.\n if (captureContent) {\n const systemText = ctx.options.system\n if (systemText) {\n baseAttrs[GEN_AI.systemInstructions] = redact('system', systemText, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n // Newest messages first, under a size cap: the full transcript on every\n // turn is quadratic over a session.\n const inputMsgs = stringifyRecentMessages(ctx.options.messages, INPUT_MESSAGES_MAX_CHARS)\n if (inputMsgs) {\n baseAttrs[GEN_AI.inputMessages] = redact('prompt', inputMsgs.json, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n if (inputMsgs.dropped > 0)\n baseAttrs[GEN_AI.inputMessagesDropped] = inputMsgs.dropped\n }\n const toolDefs = stringifyToolDefs(ctx.options.tools)\n if (toolDefs)\n baseAttrs[GEN_AI.toolDefinitions] = toolDefs\n }\n const span = safeStartSpan(\n `chat${requestedModel ? ` ${requestedModel}` : ''}`,\n baseAttrs,\n )\n if (span) {\n turnSpans.set(ctx.turnId, span)\n turnTrack.set(ctx.turnId, { startedAt: Date.now() })\n }\n }))\n\n unregisters.push(hooks.hook('stream:start', (ctx) => {\n safeAddEvent(turnSpans.get(ctx.turnId), 'gen_ai.stream.start', {\n startedAt: ctx.startedAt,\n })\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const tracked = turnTrack.get(ctx.turnId)\n turnTrack.delete(ctx.turnId)\n // Tokens-per-second derivation. Use wall-clock duration since the\n // turn span opened, fall back to TTFT-adjusted slice when present.\n let tokensPerSecond: number | undefined\n if (tracked && ctx.usage.output > 0) {\n const elapsedMs = Date.now() - tracked.startedAt\n if (elapsedMs > 0)\n tokensPerSecond = (ctx.usage.output / elapsedMs) * 1000\n }\n const usageAttrs = buildUsageAttrs(ctx.usage, conventions)\n // Cumulative-through-this-turn totals. We compute `input_tokens`\n // here to mirror `gen_ai.usage.input_tokens`'s semantic on the\n // same span: it INCLUDES cached when Sentry conventions are in\n // play, matches the uncached slice when OTel-only. Without this\n // alignment the two keys (`usage.*` vs `cumulative.*`) would\n // silently differ in arithmetic.\n const cumInputTokens = wantSentry\n ? ctx.cumulativeUsage.input + ctx.cumulativeUsage.cacheRead + ctx.cumulativeUsage.cacheCreation\n : ctx.cumulativeUsage.input\n const attrs: Record<string, unknown> = {\n ...usageAttrs,\n [GEN_AI.responseTokensPerSecond]: tokensPerSecond,\n 'gen_ai.agent.cumulative.input_tokens': cumInputTokens,\n 'gen_ai.agent.cumulative.output_tokens': ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.total_tokens': cumInputTokens + ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.cache_read_input_tokens': ctx.cumulativeUsage.cacheRead,\n 'gen_ai.agent.cumulative.cache_creation_input_tokens': ctx.cumulativeUsage.cacheCreation,\n 'gen_ai.agent.cumulative.cost_usd': ctx.cumulativeUsage.cost,\n 'gen_ai.agent.cumulative.turns': ctx.cumulativeUsage.turns,\n ...(legacy\n ? {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n finishReason: ctx.usage.finishReason,\n modelId: ctx.usage.modelId,\n }\n : {}),\n }\n // Output messages — the assistant turn that just landed. Sentry's\n // conversation viewer keys off this; skipped when content capture\n // is off. `system` turns aren't serialized (Sentry's schema only\n // accepts user / assistant / system / tool — and we never emit\n // system role from the loop).\n if (captureContent && ctx.message && ctx.message.role !== 'system' && ctx.message.role !== 'command') {\n const outputMsgs = stringifyMessages([{\n role: ctx.message.role,\n content: ctx.message.content,\n }])\n if (outputMsgs)\n attrs[GEN_AI.outputMessages] = redact('stream-text', outputMsgs, { turnId: ctx.turnId, turn: ctx.turn })\n }\n safeSetAttrs(span, attrs)\n safeEnd(turnSpans, ctx.turnId)\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const message = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n safeSetAttrs(span, {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n safeAddEvent(span, 'gen_ai.stream.error', {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n }))\n}\n","/**\n * Per-install tracing runtime for {@link createTracingHooks} (`./tracing`):\n * span book-keeping maps, the mutable active-run pointers, and the guarded\n * tracer-call helpers shared by every hook-group installer.\n */\n\nimport type { AgentHooks } from './agent-hooks'\nimport type { InterceptorRegistry } from './extensions/hooks'\nimport type { Span, TracingConventions, TracingHooksOptions } from './tracing'\nimport { compact, prefixed } from './tracing-attrs'\n\n/** Install-scoped state + guarded tracer helpers. */\nexport interface TracingRuntime {\n options: TracingHooksOptions\n legacy: boolean\n conventions: TracingConventions\n wantSentry: boolean\n captureContent: boolean\n onError: (kind: string, err: unknown) => void\n // Span book-keeping. Each map keys by the identifier that's unique\n // across concurrent in-flight spans of that kind.\n runSpans: Map<string, Span> // keyed by runId\n turnSpans: Map<string, Span> // keyed by turnId\n toolSpans: Map<string, Span> // keyed by callId\n mcpSpans: Map<string, Span> // keyed by callId\n spawnSpans: Map<string, Span> // keyed by spawn id\n bootstrapSpans: Map<string, Span> // keyed by server name\n decisionSpans: Map<string, Span> // keyed by decisionId\n // Provider system identifier (`anthropic`, `openai`, …) captured at\n // `agent:start`. Stamped onto every chat / tool span belonging to\n // this run so Sentry's per-vendor breakdowns work. Reset on\n // `agent:done`.\n activeSystem: string | undefined\n // Active turn book-keeping for tokens-per-second derivation.\n turnTrack: Map<string, { startedAt: number }>\n // Active run span ref — used so non-span events (gate blocks,\n // budget hits, validation rejects, policy/pairing repairs) can\n // attach via `addEvent` on the in-flight run span. The run span is\n // the natural parent for cross-cutting policy events: it outlives\n // turn spans (which close before tool dispatch starts in the\n // current loop), and tool spans (which haven't opened on a\n // gate-block path).\n activeRunSpan: Span | undefined\n safeStartSpan: (\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ) => Span | undefined\n safeSetAttrs: (span: Span | undefined, attrs: Record<string, unknown>) => void\n safeEnd: (map: Map<string, Span>, key: string) => void\n endAll: (map: Map<string, Span>) => void\n safeAddEvent: (span: Span | undefined, name: string, attrs: Record<string, unknown>) => void\n redact: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n}\n\n/** Factory-level settings resolved once in {@link createTracingHooks}. */\nexport interface TracingRuntimeConfig {\n namespace: string | undefined\n legacy: boolean\n conventions: TracingConventions\n wantSentry: boolean\n captureContent: boolean\n onError: (kind: string, err: unknown) => void\n /**\n * The extension interceptors: `redact` runs the `tracing.redact` chain\n * after `options.redact`. Absent without an extension runtime.\n */\n interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'>\n}\n\n/** What `tracing:redact` names a value's kind. */\ntype RedactKind = Parameters<AgentHooks['tracing:redact']>[0]['kind']\n\n/** Build a fresh runtime — called once per `install()`. */\nexport function createTracingRuntime(\n options: TracingHooksOptions,\n config: TracingRuntimeConfig,\n): TracingRuntime {\n const { namespace, legacy, conventions, wantSentry, captureContent, onError, interceptors } = config\n\n function safeStartSpan(\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ): Span | undefined {\n try {\n return options.startSpan(prefixed(namespace, name), compact(attrs), parentContext)\n }\n catch (err) {\n try {\n onError('startSpan', err)\n }\n catch { /* ignore */ }\n return undefined\n }\n }\n\n function safeSetAttrs(span: Span | undefined, attrs: Record<string, unknown>): void {\n if (!span?.setAttributes)\n return\n try {\n span.setAttributes(compact(attrs))\n }\n catch (err) {\n try {\n onError('setAttributes', err)\n }\n catch { /* ignore */ }\n }\n }\n\n function safeEnd(map: Map<string, Span>, key: string): void {\n const span = map.get(key)\n if (!span)\n return\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n map.delete(key)\n }\n\n function endAll(map: Map<string, Span>): void {\n for (const [, span] of map) {\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n }\n map.clear()\n }\n\n function safeAddEvent(span: Span | undefined, name: string, attrs: Record<string, unknown>): void {\n if (!span)\n return\n if (typeof span.addEvent === 'function') {\n try {\n span.addEvent(name, compact(attrs))\n }\n catch (err) {\n try {\n onError('addEvent', err)\n }\n catch { /* ignore */ }\n }\n return\n }\n // Tracer doesn't support events — fall back to a single\n // `event.<name>.*` attribute so the data lands somewhere.\n safeSetAttrs(span, Object.fromEntries(\n Object.entries(compact(attrs)).map(([k, v]) => [`event.${name}.${k}`, v]),\n ))\n }\n\n function redactWithOption(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n if (typeof options.redact !== 'function')\n return value\n try {\n return options.redact(kind, value, meta)\n }\n catch (err) {\n try {\n onError('redact', err)\n }\n catch { /* ignore */ }\n return value\n }\n }\n\n function redact(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n const redacted = redactWithOption(kind, value, meta)\n if (!interceptors?.has('tracing.redact'))\n return redacted\n return interceptors.runInterceptorsSync('tracing.redact', { kind: kind as RedactKind, value, ...(meta ? { meta } : {}) }, redacted)\n }\n\n return {\n options,\n legacy,\n conventions,\n wantSentry,\n captureContent,\n onError,\n runSpans: new Map<string, Span>(),\n turnSpans: new Map<string, Span>(),\n toolSpans: new Map<string, Span>(),\n mcpSpans: new Map<string, Span>(),\n spawnSpans: new Map<string, Span>(),\n bootstrapSpans: new Map<string, Span>(),\n decisionSpans: new Map<string, Span>(),\n activeSystem: undefined,\n turnTrack: new Map<string, { startedAt: number }>(),\n activeRunSpan: undefined,\n safeStartSpan,\n safeSetAttrs,\n safeEnd,\n endAll,\n safeAddEvent,\n redact,\n }\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI } from './tracing-attrs'\n\n/** `handoff` spawn span + child trace-context injection. */\nexport function installTracingSpawnHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { options, conventions, onError, spawnSpans, safeStartSpan, safeSetAttrs, safeEnd } = rt\n\n // -----------------------------------------------------------------\n // Spawn span — parent-side. Also injects trace context for the child.\n //\n // Direct spawns only, on purpose: nested spawns also reach this bus as\n // `child:spawn:*`, but their handoff span and carrier belong to the\n // SPAWNER's tracer (installed on the spawner's bus, where `spawn:*`\n // fires with the spawner's active span in scope). Handling them here\n // would duplicate those spans and parent them under this agent's\n // ambient span — which is why the bubbled copy carries no carrier.\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n // Sentry: \"handoff from {from_agent} to {to_agent}\". We don't\n // always know the child agent's name at spawn time — fall back\n // to the truncated task as the destination identifier.\n const toAgent = ctx.task.length > 60 ? `${ctx.task.slice(0, 57)}…` : ctx.task\n const span = safeStartSpan(\n `handoff to ${toAgent}`,\n {\n [GEN_AI.operationName]: 'handoff',\n [GEN_AI.system]: rt.activeSystem,\n 'sentry.op': 'gen_ai.handoff',\n 'gen_ai.agent.spawn.id': ctx.id,\n 'gen_ai.agent.spawn.task': ctx.task,\n 'gen_ai.agent.depth': ctx.depth,\n },\n )\n if (span)\n spawnSpans.set(ctx.id, span)\n\n // Inject parent trace context for the child agent. The spawn tool\n // passes whatever we write into `ctx.tracingContext` to the child's\n // `agent.run({ tracingContext })` — see `tools/spawn.ts`.\n if (typeof options.getActiveTraceContext === 'function' && ctx.tracingContext) {\n let carrier: Readonly<Record<string, string>> | undefined\n try {\n carrier = options.getActiveTraceContext()\n }\n catch (err) {\n try {\n onError('getActiveTraceContext', err)\n }\n catch { /* ignore */ }\n }\n if (carrier) {\n for (const [k, v] of Object.entries(carrier))\n ctx.tracingContext[k] = v\n }\n }\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n const span = spawnSpans.get(ctx.id)\n const childUsage = buildUsageAttrs({\n input: ctx.stats.totalIn,\n output: ctx.stats.totalOut,\n cacheRead: ctx.stats.totalCacheRead,\n cacheCreation: ctx.stats.totalCacheCreation,\n cost: ctx.stats.cost,\n }, conventions)\n safeSetAttrs(span, {\n 'gen_ai.agent.spawn.status': ctx.status ?? 'completed',\n 'gen_ai.agent.spawn.depth': ctx.depth,\n ...childUsage,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n safeSetAttrs(spawnSpans.get(ctx.id), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one contiguous slice of the tracing hook\n * registration sequence and pushes its unregister fns onto the shared\n * `unregisters` array; {@link createTracingHooks} calls them in a fixed\n * order, so registration order is deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { GEN_AI } from './tracing-attrs'\n\n/** Native `execute_tool` span. */\nexport function installTracingToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, toolSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n // -----------------------------------------------------------------\n // Native tool span — OTel Gen AI `execute_tool` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.displayName}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolType]: 'function',\n [GEN_AI.toolCallId]: ctx.callId,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.tool.display_name': ctx.displayName,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.depth': ctx.depth,\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.agent.parent_run.id': ctx.parentRunId,\n ...(legacy\n ? {\n toolName: ctx.name,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n toolSpans.set(ctx.callId, span)\n if (captureContent) {\n // Attach (possibly redacted) input as an attribute. Sentry's\n // canonical key is `gen_ai.tool.call.arguments`; OTel's\n // older key is `gen_ai.tool.input` (now deprecated in\n // Sentry). Emit one or both based on `conventions`.\n try {\n const serialized = JSON.stringify(ctx.input)\n const redacted = redact('tool-input', serialized, {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch {\n // Non-serializable input (circular refs) — skip the attr;\n // the tool name and call id are still on the span.\n }\n }\n }\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const span = toolSpans.get(ctx.callId)\n safeSetAttrs(span, {\n 'gen_ai.tool.output_bytes': ctx.outputBytes,\n 'gen_ai.tool.coercions': ctx.coercions?.length ?? 0,\n })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('tool-result', ctx.result, {\n toolName: ctx.name,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(toolSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n\n // A per-call cancel closes the call with `tool:cancelled` instead of\n // `tool:after` / `tool:error`: end the span here, not at agent:done.\n unregisters.push(hooks.hook('tool:cancelled', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'gen_ai.tool.status': 'cancelled',\n 'gen_ai.tool.cancel_reason': ctx.reason,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n}\n\n/** Gate / validation / budget / pairing / OAuth events on the run span. */\nexport function installTracingPolicyEventHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { safeAddEvent } = rt\n\n // Tool gate / dispatch outcomes. Listen on `tool:dispatched` (fires\n // AFTER every `tool:gate` listener has had a chance to mutate\n // `block` / `result`) so the recorded `outcome` reflects the final\n // decision rather than the as-entered state. Attaches to the\n // in-flight run span; the chat span has already closed by tool\n // dispatch time in the current loop ordering, and no tool span has\n // opened for a `gate-block` / `unknown` / `invalid-input` path.\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.gate.block', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }\n else if (ctx.outcome === 'gate-substitute') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.gate.substitute', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'unknown') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool.unknown', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'invalid-input') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool.invalid_input', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.validation.reject', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.validation.coerce', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n coercions: ctx.coercions,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.budget.exceeded', {\n turnId: ctx.turnId,\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool_budget.exceeded', {\n [GEN_AI.toolName]: ctx.tool,\n turnId: ctx.turnId,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.pairing.repair', {\n mode: ctx.mode,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.oauth.refresh', {\n provider: ctx.provider,\n providerId: ctx.providerId,\n source: ctx.source,\n })\n }))\n}\n","/**\n * Tracing helper — wraps agent lifecycle hooks in caller-provided spans.\n *\n * Zidane is hooks-native; rather than introducing a plugin abstraction just for\n * observability, we ship a small helper that turns an arbitrary `startSpan`\n * function into a bundle of hook handlers. Works with Sentry, OpenTelemetry,\n * Datadog — any tracer that can hand back an object with an `end()` method.\n *\n * Attribute conventions follow the Sentry AI Agents spec (which itself\n * tracks OpenTelemetry Gen AI semconv v1.36, with a few divergences). The\n * `conventions` option lets callers pick `'sentry'` (default — Sentry's\n * exact keys), `'otel'` (vendor-neutral OTel 1.36), or `'both'` (emit\n * every variant for maximum portability).\n *\n * Span coverage:\n *\n * - `invoke_agent <name?>` — root span, opens on `agent:start`, closes on `agent:done`. Sentry op `gen_ai.invoke_agent`.\n * - `chat <model?>` — per-turn LLM call, opens on `turn:before`, closes on `turn:after`. Sentry op `gen_ai.chat`.\n * - `execute_tool <name>` — native tool execution, opens on `tool:before`, closes on `tool:after` / `tool:error` / `tool:cancelled`. Sentry op `gen_ai.execute_tool`.\n * - `execute_tool <server>.<name>` — MCP tool variant, same op. Tagged with `gen_ai.tool.type: 'extension'`.\n * - `handoff to <task>` — subagent handoff (parent→child), opens on `spawn:before`, closes on `spawn:complete` / `spawn:error`. Sentry op `gen_ai.handoff`.\n * Also injects parent trace context into the spawn carrier when `propagator` is configured.\n * - `mcp.bootstrap <server>` — server bootstrap, opens on `mcp:bootstrap:start`, closes on `mcp:bootstrap:end`. Zidane-specific (no Sentry op equivalent).\n *\n * Span events (not spans — recorded on the active span via `addEvent`):\n *\n * - `gen_ai.gate.block` — a `tool:gate` listener refused a call.\n * - `gen_ai.validation.reject` — tool input failed schema validation.\n * - `gen_ai.validation.coerce` — tool input was auto-coerced to satisfy the schema.\n * - `gen_ai.budget.exceeded` — byte budget exceeded (`budget:exceeded`).\n * - `gen_ai.tool_budget.exceeded` — per-tool dispatch cap exceeded.\n * - `gen_ai.pairing.repair` — pre-send pairing repair fired.\n * - `gen_ai.stream.error` — provider stream errored (carries `statusCode` / `requestId`).\n * - `gen_ai.mcp.auth.required` — MCP server bootstrap blocked on missing OAuth tokens.\n * - `gen_ai.oauth.refresh` — provider OAuth credentials refreshed.\n *\n * Attribute naming follows the Sentry AI Agents conventions\n * (https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents/) by\n * default — which closely track OTel Gen AI semconv v1.36 with a few\n * intentional differences (`usage.input_tokens.cached` instead of\n * `usage.cache_read_input_tokens`, `cost.total_tokens` instead of\n * `usage.cost_usd`, seconds instead of ms for `time_to_first_token`).\n * Set `conventions: 'otel'` to emit only the vendor-neutral OTel keys, or\n * `'both'` to emit everything for maximum portability. Legacy attribute\n * names (`inputTokens`, `toolName`, `displayName`, …) are also emitted by\n * default for back-compat; set `legacyAttributes: false` to drop them.\n *\n * On `agent:done` or `uninstall()` it closes any spans still open (defensive —\n * normally all tool/turn spans have matching end hooks, but an abort\n * mid-execution can leave dangling ones).\n *\n * Returns an `install(hooks)` helper that registers every handler at once and\n * yields an `uninstall()` function for cleanup. Safe across multiple runs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { InterceptorRegistry } from './extensions/hooks'\nimport { agentExtensionWiringOf } from './agent-extension-wiring'\nimport { GEN_AI } from './tracing-attrs'\nimport { installTracingDecisionHooks } from './tracing-decision-hooks'\nimport { installTracingMcpServerHooks, installTracingMcpToolHooks } from './tracing-mcp-hooks'\nimport { installTracingAgentHooks, installTracingTurnHooks } from './tracing-run-hooks'\nimport { createTracingRuntime } from './tracing-runtime'\nimport { installTracingSpawnHooks } from './tracing-spawn-hooks'\nimport { installTracingPolicyEventHooks, installTracingToolHooks } from './tracing-tool-hooks'\n\n/** Minimal span shape — any tracer that provides these methods is compatible. */\nexport interface Span {\n /** Close the span. Called exactly once per span. */\n end: () => void\n /** Optional: attach additional attributes after the span is started (ignored if unsupported). */\n setAttributes?: (attrs: Record<string, unknown>) => void\n /**\n * Optional: record a structured event on the span. Used for non-span\n * occurrences (gate blocks, validation rejects, budget hits, pairing\n * repairs). Maps to OTel's `Span.addEvent` and Sentry's `addBreadcrumb`.\n * Tracers without an event surface should treat this as a no-op (the\n * helper falls back to `setAttributes` with an `event.*` key prefix).\n */\n addEvent?: (name: string, attrs?: Record<string, unknown>) => void\n}\n\n/**\n * Function that opens a span. Caller-provided so we stay tracer-agnostic.\n *\n * `parentContext` carries opaque trace metadata propagated from another\n * agent (typically a W3C `{ traceparent, tracestate }` carrier received on\n * `agent:start.tracingContext` after a parent agent's tracer injected it on\n * `spawn:before`). Implementations that integrate with OTel should extract\n * the carrier into a `Context` and use it as the parent when opening the\n * span; implementations that don't care about cross-agent linkage can\n * ignore the third argument entirely.\n */\nexport type StartSpan = (\n name: string,\n attrs?: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n) => Span\n\n/** Gen-AI attribute convention set emitted on spans: `'sentry'`, `'otel'` or `'both'`. */\nexport type TracingConventions = 'sentry' | 'otel' | 'both'\n\n/** Options for {@link createTracingHooks}. */\nexport interface TracingHooksOptions {\n /** Tracer seam. Receives a span name + attributes; must return a `Span`. */\n startSpan: StartSpan\n /**\n * Optional attribute namespace. Prepended to every span name with a `/`\n * separator (e.g. `\"agent\"` → `agent/chat <model>`, `agent/execute_tool Bash`).\n *\n * Empty / undefined = no prefix.\n */\n namespace?: string\n /**\n * Which Gen-AI attribute conventions to emit.\n *\n * - `'sentry'` (default) — Sentry AI Agents conventions. Most common\n * choice — Sentry, Datadog AI, Honeycomb derived dashboards and\n * Langfuse all consume these.\n * - `'otel'` — vendor-neutral OpenTelemetry Gen AI semconv v1.36.\n * Diverges from Sentry on cache-token keys, cost keys, and TTFT unit.\n * - `'both'` — emit every variant. Doubles the per-span attribute\n * payload; pick when shipping to multiple backends with different\n * ingestion conventions.\n */\n conventions?: TracingConventions\n /**\n * Capture the actual prompt / completion text on spans\n * (`gen_ai.input.messages`, `gen_ai.output.messages`,\n * `gen_ai.system_instructions`, `gen_ai.tool.definitions`,\n * `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result`).\n * `gen_ai.input.messages` keeps the newest messages that fit in 128 KB;\n * `gen_ai.agent.input_messages_dropped` counts the older ones left out.\n *\n * Defaults to `true` (required for the Sentry AI Agents conversation\n * viewer). Full prompts and tool arguments can carry secrets and PII;\n * captured content flows through `redact`, so pair the default with a\n * redactor when payloads may hold secrets, or set `false` to keep all\n * content-shaped data in-process.\n */\n captureMessageContent?: boolean\n /**\n * Emit legacy attribute keys (`inputTokens`, `outputTokens`, `toolName`,\n * `displayName`, `finishReason`, `modelId`) alongside the new\n * `gen_ai.*` semconv keys. Defaults to `true` so existing dashboards\n * keep working. Set `false` once you've migrated all consumers off the\n * legacy names.\n */\n legacyAttributes?: boolean\n /**\n * Error sink for tracer failures. Without it, tracer failures are swallowed\n * silently. Called when `Span.end()`, `setAttributes()`, `addEvent()`, or\n * `startSpan()` throws — so degraded tracers stay visible. The helper\n * still does not let tracer errors propagate into the agent loop.\n *\n * `kind` identifies which step failed (`'startSpan'`, `'end'`,\n * `'setAttributes'`, `'addEvent'`, `'redact'`, `'getActiveTraceContext'`).\n * Default: no-op (silent).\n */\n onError?: (kind: string, err: unknown) => void\n /**\n * Optional callback the helper invokes on `spawn:before` (after opening\n * the spawn span) to read the active trace context. Whatever it returns\n * is written into `ctx.tracingContext` and forwarded to the child's\n * `agent.run({ tracingContext })`. Typical use:\n *\n * ```ts\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * }\n * ```\n *\n * Returning `undefined` or an empty object leaves the carrier empty\n * and the child runs with no parent context. The helper's own\n * `propagator` is preferred over this when both are set (the\n * propagator runs inside the helper; this callback is the escape\n * hatch for hosts wanting full control).\n */\n getActiveTraceContext?: () => Readonly<Record<string, string>> | undefined\n /**\n * Optional in-process span redactor. When set, every potentially\n * sensitive string (tool input, tool result, system prompt) flows\n * through this fn before landing on a span attribute / event. Returning\n * the input unchanged is a no-op.\n *\n * Composes with the harness-wide `tracing:redact` hook: if a host has\n * already registered a hook handler that mutates `ctx.redacted`, the\n * tracer reuses that pipeline; setting `redact` here registers an extra\n * step that runs only for the tracer's attributes. The hook is the\n * canonical path; `redact` is the convenience surface for tracer-only\n * setups.\n */\n redact?: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n /**\n * The extension interceptors whose `tracing.redact` chain runs after\n * {@link redact}. Default: those of the agent whose hooks `install` gets\n * (`AgentOptions.extensionRuntime`); none for an agent without an extension runtime.\n */\n interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'>\n}\n\n/** Value returned by {@link createTracingHooks} — install entrypoint. */\nexport interface TracingHookSet {\n /**\n * Attach every hook handler to the given agent hooks instance.\n *\n * @returns an `uninstall` function that detaches every handler and closes any\n * still-open spans. Safe to call multiple times.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of tracing hook handlers that can be installed on an agent.\n *\n * @example Sentry\n * ```ts\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs) => Sentry.startInactiveSpan({ name, attributes: attrs }),\n * })\n * const uninstall = tracing.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @example OpenTelemetry with parent-context propagation\n * ```ts\n * import { trace, context, propagation } from '@opentelemetry/api'\n *\n * const tracer = trace.getTracer('zidane')\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs, parentCtx) => {\n * const ctx = parentCtx ? propagation.extract(context.active(), parentCtx) : context.active()\n * return tracer.startSpan(name, { attributes: attrs }, ctx)\n * },\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * },\n * })\n * ```\n *\n * @param options - Tracer seam and span options. `startSpan` is required.\n */\nexport function createTracingHooks(options: TracingHooksOptions): TracingHookSet {\n const namespace = options.namespace\n const legacy = options.legacyAttributes ?? true\n const conventions = options.conventions ?? 'sentry'\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const captureContent = options.captureMessageContent !== false\n const onError = options.onError ?? (() => {})\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const rt = createTracingRuntime(options, {\n namespace,\n legacy,\n conventions,\n wantSentry,\n captureContent,\n onError,\n ...interceptorsFor(options, hooks),\n })\n const { runSpans, turnSpans, toolSpans, mcpSpans, spawnSpans, bootstrapSpans, decisionSpans, endAll } = rt\n\n const unregisters: Array<() => void> = []\n\n installTracingAgentHooks(hooks, unregisters, rt)\n installTracingTurnHooks(hooks, unregisters, rt)\n installTracingToolHooks(hooks, unregisters, rt)\n installTracingPolicyEventHooks(hooks, unregisters, rt)\n installTracingMcpToolHooks(hooks, unregisters, rt)\n installTracingMcpServerHooks(hooks, unregisters, rt)\n installTracingSpawnHooks(hooks, unregisters, rt)\n installTracingDecisionHooks(hooks, unregisters, rt)\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n endAll(runSpans)\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n endAll(decisionSpans)\n rt.activeRunSpan = undefined\n }\n },\n }\n}\n\n/** The `tracing.redact` chain a tracer installed on `hooks` runs: the option's, else the agent's own. */\nfunction interceptorsFor(options: TracingHooksOptions, hooks: Hookable<AgentHooks>): { interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'> } {\n const interceptors = options.interceptors ?? agentExtensionWiringOf(hooks)?.extensionRuntime.interceptors\n return interceptors ? { interceptors } : {}\n}\n\n/**\n * OpenTelemetry Gen AI semantic-convention attribute keys used by the\n * built-in tracer. Exported so consumers integrating with raw OTel SDKs\n * (instead of going through `createTracingHooks`) can stay aligned with\n * the names the harness itself emits.\n */\nexport const GEN_AI_ATTRIBUTES = GEN_AI\n"],"mappings":"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"}
1
+ {"version":3,"file":"src-BmTXDta8.js","names":["prefixed"],"sources":["../src/behaviors/local-reliability.ts","../src/chat/inline-autocomplete.ts","../src/github/gh-cli.ts","../src/github/state-file.ts","../src/mcp/login.ts","../src/metrics-hooks.ts","../src/metrics.ts","../src/mutation-validation.ts","../src/perf/event-loop.ts","../src/perf/ps.ts","../src/perf/gpu.ts","../src/perf/os-threads.ts","../src/perf/owned.ts","../src/perf/processes.ts","../src/perf/ring.ts","../src/perf/sampler.ts","../src/perf/host.ts","../src/tracing-attrs.ts","../src/tracing-decision-hooks.ts","../src/tracing-mcp-hooks.ts","../src/tracing-run-hooks.ts","../src/tracing-runtime.ts","../src/tracing-spawn-hooks.ts","../src/tracing-tool-hooks.ts","../src/tracing.ts"],"sourcesContent":["import type { AgentBehavior } from '../types'\n\nconst DEFAULT_LOCAL_RELIABILITY: AgentBehavior = {\n maxConcurrentTools: 3,\n readFileDefaults: {\n limit: 400,\n maxBytes: 64 * 1024,\n },\n toolOutputBudget: 32 * 1024,\n textToolCallRescue: { mode: 'steer' },\n thinkingOverrunPolicy: {\n mode: 'steer-next-turn',\n thresholdTokens: 2048,\n },\n}\n\n/**\n * Opt-in behavior overlay for brittle/local OpenAI-compatible endpoints.\n * Returns plain `AgentBehavior` so hosts can spread it before or after their\n * own overrides; no built-in profile uses this helper by default.\n * @param overrides - Behavior fields that replace the defaults. `readFileDefaults` is merged key by key.\n */\nexport function localReliabilityBehavior(overrides: AgentBehavior = {}): AgentBehavior {\n return {\n ...DEFAULT_LOCAL_RELIABILITY,\n ...overrides,\n readFileDefaults: {\n ...DEFAULT_LOCAL_RELIABILITY.readFileDefaults,\n ...(overrides.readFileDefaults ?? {}),\n },\n textToolCallRescue: overrides.textToolCallRescue ?? DEFAULT_LOCAL_RELIABILITY.textToolCallRescue,\n thinkingOverrunPolicy: overrides.thinkingOverrunPolicy ?? DEFAULT_LOCAL_RELIABILITY.thinkingOverrunPolicy,\n }\n}\n","/**\n * Inline (ghost-text) autocomplete for the prompt.\n *\n * Distinct from the trigger-based completion popover (`@` files, `/`\n * commands): providers here suggest plain text SUFFIX candidates to\n * append at the cursor — the first renders as dim ghost text, up/down\n * rotates through the rest, Tab accepts. No popover.\n *\n * Providers are queried in registration order with a shared AbortSignal;\n * the engine returns the first provider's non-empty candidate list.\n * Extensions add providers via `ctx.registerInlineAutocompleteProvider`\n * — the built-in path provider ({@link createPathAutocompleteProvider})\n * is always first.\n */\n\nimport { readdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\n\nexport { type InlineGhost, patchInlineGhost } from './inline-ghost'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Input handed to an {@link InlineAutocompleteProvider} on each query. */\nexport interface InlineAutocompleteContext {\n /** Full prompt buffer. */\n text: string\n /** Cursor offset into `text`. Suggestions complete the token ENDING here. */\n cursor: number\n /** Workspace directory relative paths resolve against. */\n cwd: string\n /** Aborted when the buffer changes before the provider resolves. */\n signal: AbortSignal\n}\n\n/**\n * Source of inline ghost-text suggestions. Providers are queried in order by {@link resolveInlineSuggestion}.\n */\nexport interface InlineAutocompleteProvider {\n /** Stable id — used for first-wins dedupe across extensions. */\n id: string\n /**\n * Per-provider deadline override (ms), capped at\n * {@link MAX_PROVIDER_DEADLINE_MS}. The default\n * ({@link PROVIDER_DEADLINE_MS}) suits instant sources like directory\n * listings; a provider backed by a slow source (e.g. a local LLM) can\n * extend its own budget — the wait is async (typing is never blocked,\n * an edit aborts the query) and only delays providers ordered AFTER\n * this one.\n */\n deadlineMs?: number\n /**\n * Return the ghost suffix (or an ordered list of candidate suffixes —\n * the host shows the first and lets the user rotate with up/down) to\n * append at the cursor, or null/''/[] for no suggestion. Must respect\n * its deadline budget — it runs per keystroke (debounced by the\n * host).\n */\n suggest: (ctx: InlineAutocompleteContext) => string | readonly string[] | null | Promise<string | readonly string[] | null>\n}\n\n// ---------------------------------------------------------------------------\n// Engine\n// ---------------------------------------------------------------------------\n\n/** Default per-provider deadline — a hung provider is treated as \"no suggestion\". */\nconst PROVIDER_DEADLINE_MS = 150\n/** Hard cap on a provider's `deadlineMs` — past this, waiting for a ghost reads as broken. */\nexport const MAX_PROVIDER_DEADLINE_MS = 2_000\n\n/**\n * Query `providers` in order; the first provider yielding a non-empty\n * candidate list wins (a bare string normalizes to a 1-element list).\n * Provider errors are swallowed (a broken extension provider must not\n * break typing), and each provider races its deadline —\n * {@link PROVIDER_DEADLINE_MS} by default, a provider's own `deadlineMs`\n * (capped at {@link MAX_PROVIDER_DEADLINE_MS}) when declared — so one\n * hung provider can't permanently suppress the ghosts of providers\n * behind it. Returns null when aborted or nothing matched.\n *\n * @param providers - Providers in query order.\n * @param ctx - Prompt buffer, cursor, working directory, and abort signal.\n * @param deadlineMs - Per-provider deadline in milliseconds when the provider declares none. Defaults to 150.\n * @param maxDeadlineMs - Cap applied to a provider's own `deadlineMs`. Defaults to {@link MAX_PROVIDER_DEADLINE_MS}.\n */\nexport async function resolveInlineSuggestion(\n providers: readonly InlineAutocompleteProvider[],\n ctx: InlineAutocompleteContext,\n deadlineMs = PROVIDER_DEADLINE_MS,\n // Injectable cap — the host always uses the real MAX_PROVIDER_DEADLINE_MS;\n // tests pass a small value to prove clamping without a wall-clock stall.\n maxDeadlineMs = MAX_PROVIDER_DEADLINE_MS,\n): Promise<{ suffixes: readonly string[], providerId: string } | null> {\n const TIMEOUT = Symbol('timeout')\n for (const provider of providers) {\n if (ctx.signal.aborted)\n return null\n try {\n let timer: ReturnType<typeof setTimeout> | undefined\n const budget = provider.deadlineMs !== undefined\n ? Math.min(Math.max(provider.deadlineMs, 1), maxDeadlineMs)\n : deadlineMs\n const result = await Promise.race([\n Promise.resolve(provider.suggest(ctx)),\n new Promise<typeof TIMEOUT>((r) => { timer = setTimeout(r, budget, TIMEOUT) }),\n ])\n if (timer !== undefined)\n clearTimeout(timer)\n if (result === TIMEOUT)\n continue\n if (ctx.signal.aborted)\n return null\n const suffixes = (typeof result === 'string' ? [result] : result ?? [])\n .filter(s => typeof s === 'string' && s.length > 0)\n if (suffixes.length > 0)\n return { suffixes, providerId: provider.id }\n }\n catch {\n // Isolation-first: skip broken providers.\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Built-in path provider\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the whitespace-delimited token that ends at `cursor`. Returns\n * null when the cursor sits right after whitespace (nothing to complete).\n *\n * @param text - Prompt buffer.\n * @param cursor - Cursor offset into `text`.\n */\nexport function tokenEndingAtCursor(text: string, cursor: number): string | null {\n const before = text.slice(0, cursor)\n const match = before.match(/\\S+$/)\n return match ? match[0] : null\n}\n\n/**\n * Does this token carry an explicit path shape (`/` separator or a `~`\n * prefix)? Path-shaped tokens complete from the first character after\n * the separator; bare tokens (`sr` → `c/`) still complete against the\n * cwd listing but need ≥2 chars first — see the provider.\n */\nfunction looksLikePath(token: string): boolean {\n return token.includes('/') || token.startsWith('~')\n}\n\n/**\n * Cap on candidates returned for an ambiguous fragment — rotation past\n * a dozen-odd entries stops being faster than typing another character.\n */\nconst MAX_PATH_CANDIDATES = 16\n\n/** Directory listing seam — injectable for tests. */\nexport interface PathProviderIo {\n readdir?: (dir: string) => { name: string, isDirectory: boolean }[]\n}\n\nfunction defaultReaddir(dir: string): { name: string, isDirectory: boolean }[] {\n return readdirSync(dir, { withFileTypes: true }).map(e => ({ name: e.name, isDirectory: e.isDirectory() }))\n}\n\n/**\n * Built-in provider: complete filesystem paths under `cwd` (or absolute /\n * `~` paths). Given the token `src/chat/comp`, lists `src/chat/` and\n * returns every matching continuation of `comp` as a candidate list —\n * directories first, alphabetical, `/`-terminated for directories so\n * typing can continue. The host ghosts the first candidate and rotates\n * through the rest on up/down.\n *\n * Bare tokens (no `/` yet) are matched against the cwd listing too, so\n * a directory ghost appears as soon as the first characters of `src`\n * are typed — but only from the second character on, so 1-letter prose\n * words don't ghost-spam.\n *\n * Dotfiles are only offered when the typed fragment itself starts with\n * `.` — the usual completion convention.\n *\n * @param opts - Optional working-directory getter and directory-listing override.\n */\nexport function createPathAutocompleteProvider(opts: {\n cwd?: () => string\n io?: PathProviderIo\n} = {}): InlineAutocompleteProvider {\n const readdir = opts.io?.readdir ?? defaultReaddir\n // Prefer the per-invocation `ctx.cwd` (host truth); fall back to the\n // factory-supplied getter, then `process.cwd()`.\n const fallbackCwd = opts.cwd ?? (() => process.cwd())\n return {\n id: 'paths',\n suggest(ctx) {\n const token = tokenEndingAtCursor(ctx.text, ctx.cursor)\n if (!token)\n return null\n // Bare tokens complete against the cwd listing (dirname('sr') is '.',\n // so the resolution below just works) but require ≥2 typed chars.\n if (!looksLikePath(token) && token.length < 2)\n return null\n // Resolve the directory being completed and the fragment typed so\n // far. A trailing `/` means \"complete entries of this directory\".\n const expanded = token.startsWith('~')\n ? join(homedir(), token.slice(1))\n : token\n const endsWithSep = expanded.endsWith('/')\n const dirPart = endsWithSep ? expanded : dirname(expanded)\n const fragment = endsWithSep ? '' : basename(expanded)\n const baseCwd = ctx.cwd || fallbackCwd()\n const dir = isAbsolute(dirPart) ? dirPart : resolve(baseCwd, dirPart)\n let entries: { name: string, isDirectory: boolean }[]\n try {\n entries = readdir(dir)\n }\n catch {\n return null\n }\n const matches = entries.filter(e =>\n e.name.startsWith(fragment)\n && (fragment.startsWith('.') || !e.name.startsWith('.')),\n )\n // Directories first (they're what you're navigating through), then\n // alphabetical within each group.\n matches.sort((a, b) =>\n a.isDirectory !== b.isDirectory\n ? (a.isDirectory ? -1 : 1)\n : a.name.localeCompare(b.name))\n const suffixes = matches\n .map(e => e.name.slice(fragment.length) + (e.isDirectory ? '/' : ''))\n .filter(s => s.length > 0)\n .slice(0, MAX_PATH_CANDIDATES)\n return suffixes.length > 0 ? suffixes : null\n },\n }\n}\n","/**\n * Reading the `gh` CLI's rate-limit failures. `gh` prints GitHub's message on\n * stderr and exits non-zero; its wording has moved between versions, so the\n * match is loose and a miss is just a generic failure.\n */\n\n/**\n * `primary` (the hourly quota is spent), `secondary` (too fast), or `null`.\n * @param stderr - Standard error text of a failed `gh` command.\n * Mirrored by `ghRateLimitKind` in `extensions/git-flow/shared.ts`: change both together.\n */\nexport function isGhRateLimitError(stderr: string): 'primary' | 'secondary' | null {\n if (/secondary rate limit|abuse detection|was submitted too quickly/i.test(stderr))\n return 'secondary'\n if (/(?:api )?rate limit (?:already )?exceeded|rate[- ]limited/i.test(stderr))\n return 'primary'\n return null\n}\n\n/**\n * The reset of a quota from `gh api rate_limit` output (epoch seconds in\n * `resources.<name>.reset`), as local epoch ms; `undefined` when unreadable.\n * @param json - Output of `gh api rate_limit`.\n * @param resource - Quota to read the reset of.\n */\nexport function rateLimitResetFromJson(json: string, resource: 'core' | 'graphql' = 'graphql'): number | undefined {\n try {\n const body = JSON.parse(json) as { resources?: Record<string, { reset?: unknown }> }\n const reset = body.resources?.[resource]?.reset\n return typeof reset === 'number' && Number.isFinite(reset) ? reset * 1000 : undefined\n }\n catch {\n return undefined\n }\n}\n","/**\n * The file half of {@link GithubStateStore}: `<dir>/github-state.json`,\n * shared by every zidane process on the machine. Split from `state.ts`\n * because it needs `node:fs`, and the PR helpers reach browser bundles\n * through `zidane/chat/pure`.\n */\nimport type { GithubState, GithubStateStore } from './state'\nimport { readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { OWNER_ONLY, writeFileAtomic } from '../atomic-write'\nimport { emptyGithubState, mergeGithubState, parseGithubState, pruneGithubState } from './state'\n\nexport const GITHUB_STATE_FILENAME = 'github-state.json'\n\n/** How long quota observations wait before reaching disk (pauses don't wait). */\nconst WRITE_DEBOUNCE_MS = 500\n\n/**\n * State backed by `<dir>/github-state.json`. Reads re-parse only when the\n * file's mtime moved; writes merge with what is on disk first, so two\n * processes writing at once lose nothing that matters. A failed write\n * (read-only home, full disk) switches the store to memory only.\n * @param dir - Directory that holds `github-state.json`.\n * @param opts - Optional clock override, `now`, in epoch milliseconds.\n */\nexport function createFileGithubStateStore(dir: string, opts: { now?: () => number } = {}): GithubStateStore {\n const now = opts.now ?? Date.now\n const path = join(dir, GITHUB_STATE_FILENAME)\n let state: GithubState = emptyGithubState()\n let mtime = -1\n let timer: ReturnType<typeof setTimeout> | null = null\n let broken = false\n\n const readDisk = (): GithubState | null => {\n try {\n const stat = statSync(path)\n if (stat.mtimeMs === mtime)\n return null\n mtime = stat.mtimeMs\n return parseGithubState(readFileSync(path, 'utf8'))\n }\n catch {\n return null\n }\n }\n\n const refresh = () => {\n if (broken)\n return\n const disk = readDisk()\n if (disk)\n state = mergeGithubState(disk, state)\n }\n\n function flush(): void {\n if (timer) {\n clearTimeout(timer)\n timer = null\n process.removeListener('exit', flush)\n }\n if (broken)\n return\n refresh()\n pruneGithubState(state, now())\n try {\n writeFileAtomic(path, JSON.stringify(state), OWNER_ONLY)\n try {\n mtime = statSync(path).mtimeMs\n }\n catch { /* re-read next time */ }\n }\n catch {\n broken = true\n }\n }\n\n return {\n read: () => {\n refresh()\n return state\n },\n update: (change, o) => {\n refresh()\n change(state)\n if (o?.urgent) {\n flush()\n return\n }\n if (!timer) {\n timer = setTimeout(flush, WRITE_DEBOUNCE_MS)\n ;(timer as { unref?: () => void }).unref?.()\n // The timer is unref'd: a process quitting with a write pending flushes it on exit.\n process.once('exit', flush)\n }\n },\n }\n}\n","/**\n * Interactive OAuth login orchestrator for one MCP server.\n *\n * Composes the three pieces shipped separately:\n * - `startOAuthCallback` (`./oauth-callback`) — local loopback HTTP\n * listener that catches the `?code=...` redirect.\n * - `McpOAuthProvider` (`./oauth-provider`) — SDK adapter that persists\n * tokens / client info via an injected `McpCredentialStore`.\n * - The MCP SDK's transport `authProvider` + `finishAuth` plumbing —\n * `client.connect()` triggers the SDK to call `redirectToAuthorization`\n * (which opens the browser); we wait for the loopback callback to\n * deliver the code, hand it to `transport.finishAuth`, and reconnect.\n *\n * Returns once tokens are persisted. Caller decides whether to immediately\n * reconnect the server (re-run `connectMcpServers` / `agent.warmup()`) or\n * defer to the next session activation. The returned `tools` array is\n * provided as a convenience — callers that want the connection live in\n * this call rather than rebuilding can wire them in directly.\n */\n\nimport type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { McpServerConfig } from '../types'\nimport type { McpOAuthCallbackPageRenderer, OAuthCallbackHandle } from './oauth-callback'\nimport type { McpCredentialStore, McpOAuthProvider } from './oauth-provider'\nimport { randomBytes } from 'node:crypto'\nimport { expandMcpConfigEnv } from './index'\nimport { startOAuthCallback } from './oauth-callback'\nimport { isOAuthTokenRejectionError, mcpOAuthProviderFor } from './oauth-provider'\nimport { sseToJsonFetchIfNeeded } from './sse-to-json-fetch'\nimport { createTolerantClient } from './tolerant-client'\n\n/** Options for {@link loginMcpServer}. */\nexport interface LoginMcpServerOptions {\n /** Persistence — same store the bootstrap path reads from. */\n store: McpCredentialStore\n /**\n * Invoked with the authorization URL once it's ready. Hosts typically\n * (a) emit `mcp:auth:url` for the TUI, and (b) call `tryOpenBrowser`.\n * The URL is identical to the one passed to the `mcp:auth:url` hook\n * fired automatically — this callback is a synchronous hook for callers\n * that don't want to wire the agent hook machinery.\n */\n onAuthorizationUrl?: (url: URL) => void | Promise<void>\n /** Cancels the flow (esc / close modal / SIGINT). */\n signal?: AbortSignal\n /**\n * Renders the post-redirect browser page — hosts pass their themed\n * renderer (zidane's TUI: `renderDefaultCallbackPage`) so MCP logins\n * end on the same screen as provider OAuth logins. The server name\n * rides as `page.provider`.\n */\n renderCallbackPage?: McpOAuthCallbackPageRenderer\n /** Agent hooks. The flow emits `mcp:auth:url`/`success`/`error` when wired. */\n hooks?: Hookable<AgentHooks>\n /** Override `client_name` shown on consent screens. Default: 'zidane'. */\n clientName?: string\n /** Override the requested OAuth scope. */\n scope?: string\n /**\n * Override the loopback callback path. Default: `/callback`. Useful only\n * for servers that pinned a different path during registration. Ignored\n * when `oauth.callbackPort` is set (the pinned redirect is always `/callback`).\n */\n callbackPath?: string\n /**\n * Maximum time to wait for the user to complete the browser flow, in ms.\n * The user can also cancel via `signal`. Default: 5 minutes.\n */\n timeoutMs?: number\n /**\n * Maximum time for the PRE-authorization phase — the `client.connect()`\n * that triggers discovery, dynamic registration, and the authorization\n * redirect. Default: 30s.\n *\n * Separate from {@link timeoutMs} because it covers machine-speed HTTP,\n * not a human in a browser. It exists because the MCP SDK's `connect()`\n * accepts no `AbortSignal`: an unreachable or slow-loris server would\n * otherwise hang here forever, un-cancellable, with the host UI parked on\n * a spinner that no keystroke can dismiss.\n */\n connectTimeoutMs?: number\n /**\n * Where the code comes from instead of a loopback listener of this\n * process: a remote sign-in, whose browser redirects to the CLIENT's\n * loopback (`redirectUri`), the client forwarding the code. With it, no\n * listener is bound here.\n */\n callback?: OAuthCallbackHandle\n /**\n * OAuth `state` for the authorization URL (`McpOAuthProviderOptions.state`); the caller checks it.\n * Omitted: a random one is generated and checked against the loopback callback here\n * (some servers, e.g. CircleCI, reject an authorization request without `state`).\n */\n state?: string\n}\n\n/** Outcome of a successful {@link loginMcpServer} flow. */\nexport interface LoginMcpServerResult {\n /** Stored OAuth tokens after a successful exchange. */\n tokens: NonNullable<ReturnType<McpOAuthProvider['tokens']>>\n /**\n * Upstream tool descriptors discovered after re-connecting with the new\n * tokens. The server's `enabledTools` / `disabledTools` filters are NOT\n * applied here — that's a bootstrap concern. Hosts that want filtering\n * should pass the result through `connectMcpServers` rebuild on the next\n * session activation rather than reusing this list verbatim.\n */\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>\n}\n\nconst DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60_000\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000\n\n/**\n * Run the full interactive OAuth flow for `config`. Only supports `sse` and\n * `streamable-http` transports — `stdio` MCP servers don't speak OAuth.\n *\n * Throws on:\n * - Wrong transport.\n * - Abort signal.\n * - Browser-side error (user denied, server rejected, etc.).\n * - Code exchange failure.\n * - Post-exchange connect failure.\n *\n * Always closes the loopback callback server before returning, success or\n * failure.\n * @param config - Server to sign in to. Must use the `sse` or `streamable-http` transport and have a `url`.\n * @param options - Credential store and flow settings. See {@link LoginMcpServerOptions}.\n */\nexport async function loginMcpServer(\n config: McpServerConfig,\n options: LoginMcpServerOptions,\n): Promise<LoginMcpServerResult> {\n if (config.transport !== 'sse' && config.transport !== 'streamable-http')\n throw new Error(`MCP OAuth: can't sign in over \"${config.transport}\". Only sse and streamable-http support it.`)\n if (!config.url)\n throw new Error(`MCP OAuth: server \"${config.name}\" is missing a url`)\n\n const hooks = options.hooks\n\n // Flipped in `finally`. A lost `raceAbort` ABANDONS `client.connect()`\n // rather than cancelling it (the SDK takes no signal), so the SDK can\n // reach the authorization step long after this function already threw —\n // timeout or abort. The `onAuthorizationUrl` closure below must go dead\n // at that point: a late `mcp:auth:url` would resurrect the host's auth UI\n // (and pop a browser) for a flow whose loopback callback server is\n // already closed and which nothing is driving.\n let settled = false\n\n let callback: OAuthCallbackHandle | undefined\n try {\n callback = options.callback ?? await startOAuthCallback({\n signal: options.signal,\n // A pre-registered client's redirect must match the one registered\n // with the provider, so its port is pinned (`oauth.callbackPort`) and\n // its path stays the default `/callback` (`pinnedMcpRedirectUri`).\n ...(config.oauth?.callbackPort !== undefined\n ? { port: config.oauth.callbackPort }\n : { path: options.callbackPath }),\n ...(options.renderCallbackPage ? { renderPage: options.renderCallbackPage } : {}),\n provider: config.name,\n })\n\n const handle = callback\n const state = options.state ?? randomBytes(24).toString('base64url')\n const provider = mcpOAuthProviderFor(config, {\n store: options.store,\n redirectUri: handle.redirectUri,\n clientName: options.clientName,\n ...(options.scope !== undefined ? { scope: options.scope } : {}),\n state,\n onAuthorizationUrl: async (url) => {\n // Late callback from an abandoned connect — see `settled` above.\n // The signal check additionally covers an abort that races the\n // authorization step while the flow is still nominally live.\n if (settled || options.signal?.aborted)\n return\n await hooks?.callHook('mcp:auth:url', { name: config.name, url: url.toString() })\n await options.onAuthorizationUrl?.(url)\n },\n })\n\n let transport = await createInteractiveTransport(config, provider)\n const client = await createTolerantClient({ name: 'zidane', version: '1.0.0' })\n\n // First connect → SDK has no tokens, so it calls\n // `provider.redirectToAuthorization(url)` (which routes to our\n // `onAuthorizationUrl`), then throws `UnauthorizedError`. This is the\n // expected path — we catch and wait on the loopback for the code.\n //\n // With STALE tokens stored, the SDK first attempts a refresh instead —\n // and when the refresh token itself is dead, `auth()` rethrows the\n // token-endpoint rejection (`InvalidGrantError`) WITHOUT opening the\n // browser. Wipe the dead tokens and retry once: the tokenless retry\n // routes straight to the authorization redirect. Without this, a\n // revoked refresh token would make the panel re-login itself fail — the one\n // flow that's supposed to recover from dead credentials.\n let needsAuth = false\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n for (let attempt = 0; ; attempt++) {\n try {\n // Bounded + cancellable: `connect()` takes no signal, so we race it\n // rather than await it bare. The losing connect keeps running in the\n // background until its own sockets give up — acceptable, because the\n // alternative is a host UI stuck on \"starting login flow…\" with no\n // way out.\n await raceAbort(\n client.connect(transport),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not respond within ${connectTimeoutMs}ms. The server may be unreachable.`,\n )\n break\n }\n catch (err) {\n if (isUnauthorizedError(err)) {\n needsAuth = true\n break\n }\n if (attempt === 0 && isOAuthTokenRejectionError(err)) {\n await provider.invalidateCredentials('tokens')\n await transport.close().catch(() => {})\n transport = await createInteractiveTransport(config, provider)\n continue\n }\n await client.close().catch(() => {})\n throw err\n }\n }\n\n if (needsAuth) {\n const { code, state: returnedState } = await raceLoginCallback(handle, options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS, options.signal)\n if (options.state === undefined && returnedState !== state)\n throw new Error(`MCP OAuth: \"${config.name}\" returned a mismatched state; the sign-in was refused. Start it again.`)\n // SDK exchanges code → tokens; provider.saveTokens persists them.\n await (transport as { finishAuth: (code: string) => Promise<void> }).finishAuth(code)\n // Reconnect with a FRESH transport. The previous one is already in\n // the \"started\" state from the failed connect — reusing it throws\n // \"StreamableHTTPClientTransport already started!\" inside\n // `transport.start()`. The SDK's canonical example\n // (`simpleOAuthClient.js`) uses the same pattern: recurse with a\n // new transport, reuse the client. Close the stale transport\n // first so its abort controllers + sockets don't leak.\n await transport.close().catch(() => {})\n const freshTransport = await createInteractiveTransport(config, provider)\n // Bounded for the same reason as the first connect: tokens are already\n // persisted at this point, so a server that stops responding here must\n // surface as an error the user can retry from, not a permanent spinner.\n await raceAbort(\n client.connect(freshTransport),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not respond after the token exchange (within ${connectTimeoutMs}ms)`,\n )\n }\n\n const { tools } = await raceAbort(\n client.listTools(),\n options.signal,\n connectTimeoutMs,\n `MCP OAuth: \"${config.name}\" did not return its tool list within ${connectTimeoutMs}ms`,\n )\n await client.close()\n\n const tokens = provider.tokens()\n if (!tokens)\n throw new Error(`MCP OAuth: login for \"${config.name}\" returned no tokens (server may have rejected the exchange silently)`)\n\n await hooks?.callHook('mcp:auth:success', { name: config.name })\n return { tokens, tools }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n await hooks?.callHook('mcp:auth:error', { name: config.name, error })\n throw error\n }\n finally {\n settled = true\n await callback?.close()\n }\n}\n\n/**\n * Build an `sse` / `streamable-http` transport pre-wired with `authProvider`.\n * Mirrors the bootstrap-side `createTransport` shape but inlined here so the\n * login flow doesn't depend on the bootstrap module's private export.\n */\nasync function createInteractiveTransport(rawConfig: McpServerConfig, authProvider: OAuthClientProvider) {\n // Same `${VAR}` resolution as the bootstrap-side `createTransport` —\n // url/headers may carry env placeholders that must never hit the wire raw.\n const config = expandMcpConfigEnv(rawConfig)\n if (config.transport === 'sse') {\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')\n return new SSEClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n authProvider,\n })\n }\n // streamable-http\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js')\n return new StreamableHTTPClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n fetch: sseToJsonFetchIfNeeded(),\n authProvider,\n })\n}\n\nfunction isUnauthorizedError(err: unknown): boolean {\n if (!err || typeof err !== 'object')\n return false\n const e = err as { name?: string, message?: string, constructor?: { name?: string } }\n if (e.name === 'UnauthorizedError' || e.constructor?.name === 'UnauthorizedError')\n return true\n return typeof e.message === 'string' && e.message.toLowerCase().startsWith('unauthorized')\n}\n\n/**\n * Race `promise` against the external abort signal and a hard timeout.\n *\n * Used for phases we can't cancel from the inside (the SDK's `connect()`\n * accepts no signal). The underlying work isn't stopped — it's abandoned —\n * so callers must treat a rejection as \"this attempt is over\" and close\n * whatever they own. Rejects with `'OAuth login aborted'` on abort so the\n * host can tell a user cancel apart from a genuine failure.\n */\nasync function raceAbort<T>(\n promise: Promise<T>,\n signal: AbortSignal | undefined,\n timeoutMs: number,\n timeoutMessage: string,\n): Promise<T> {\n if (signal?.aborted)\n throw new Error('OAuth login aborted')\n // Nothing consumes the rejection when the race is lost — pre-attach a\n // no-op catch so an abandoned connect can't raise an unhandled rejection.\n promise.catch(() => {})\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<T>((resolve, reject) => {\n timer = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs)\n if (signal) {\n onAbort = () => reject(new Error('OAuth login aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n promise.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n\n/**\n * Wait on the callback handle, with a hard timeout AND honor the external\n * abort signal. Unlike `callback.promise` alone, this provides a deterministic\n * failure mode for \"user opened the browser then walked away\" — the modal\n * doesn't hang forever waiting for a redirect that may never come.\n */\nasync function raceLoginCallback(\n handle: OAuthCallbackHandle,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<{ code: string, state?: string }> {\n if (signal?.aborted)\n throw new Error('OAuth login aborted')\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<{ code: string, state?: string }>((resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`OAuth login timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n if (signal) {\n onAbort = () => reject(new Error('OAuth login aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n handle.promise.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n","/**\n * Hook-group installers behind {@link createMetricsHooks} (`./metrics`).\n *\n * Each installer registers one slice of the hooks and pushes its unregister\n * fns onto the shared `unregisters` array. `createMetricsHooks` calls them in\n * the order run, tool, MCP and decision, which fixes the hook registration\n * order.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { Counter, Histogram, MetricAttributes, UpDownCounter } from './metrics'\n\n/** Instruments created once per {@link createMetricsHooks} call. */\nexport interface MetricsInstruments {\n turnDuration: Histogram | undefined\n tokenUsage: Histogram | undefined\n ttft: Histogram | undefined\n toolDuration: Histogram | undefined\n toolOutput: Histogram | undefined\n mcpToolDuration: Histogram | undefined\n mcpBootstrapDuration: Histogram | undefined\n decisionDuration: Histogram | undefined\n decisions: Counter | undefined\n runsStarted: Counter | undefined\n runsCompleted: Counter | undefined\n aborts: Counter | undefined\n toolCalls: Counter | undefined\n toolErrors: Counter | undefined\n mcpToolErrors: Counter | undefined\n streamErrors: Counter | undefined\n validationRejects: Counter | undefined\n validationCoercions: Counter | undefined\n gateBlocks: Counter | undefined\n budgetExceeded: Counter | undefined\n toolBudgetExceeded: Counter | undefined\n pairingRepairs: Counter | undefined\n oauthRefreshes: Counter | undefined\n mcpErrors: Counter | undefined\n mcpAuthRequired: Counter | undefined\n costMeter: Counter | undefined\n runsActive: UpDownCounter | undefined\n}\n\n/** Per-install book-keeping + guarded record/add helpers. */\nexport interface MetricsInstallState {\n // Per-instrument book-keeping. Each map tracks instrument start times\n // keyed by the identifier that distinguishes concurrent in-flight\n // instances of that kind.\n turnStart: Map<string, number> // turnId -> startedAt\n toolStart: Map<string, number> // callId -> startedAt\n mcpStart: Map<string, number> // callId -> startedAt\n record: <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ) => void\n add: <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ) => void\n}\n\n/**\n * Drop `undefined` entries before handing attributes to a metrics\n * backend. OTel's `@opentelemetry/api` rejects `undefined` attribute\n * values (and Prometheus / StatsD adapters silently mis-label them); the\n * helper's own API surface allows `undefined` for ergonomic call sites\n * (`{...(cond ? { foo } : {})}`-style spreads), so we strip here at the\n * boundary.\n */\nfunction mergeAttrs(\n base: MetricAttributes | undefined,\n extra: MetricAttributes,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (base) {\n for (const [k, v] of Object.entries(base)) {\n if (v !== undefined)\n out[k] = v\n }\n }\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/** Build the per-install state: fresh start-time maps + guarded emitters. */\nexport function createMetricsInstallState(\n base: MetricAttributes | undefined,\n onError: (kind: string, err: unknown) => void,\n): MetricsInstallState {\n const turnStart = new Map<string, number>() // turnId -> startedAt\n const toolStart = new Map<string, number>() // callId -> startedAt\n const mcpStart = new Map<string, number>() // callId -> startedAt\n\n const record = <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.record(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n const add = <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.add(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n return { turnStart, toolStart, mcpStart, record, add }\n}\n\n/** Agent lifecycle + turn / stream metrics. */\nexport function installMetricsRunHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { turnDuration, tokenUsage, ttft, runsStarted, runsCompleted, aborts, streamErrors, costMeter, runsActive } = m\n const { turnStart, toolStart, mcpStart, record, add } = state\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n const attrs: MetricAttributes = {\n 'gen_ai.agent.depth': ctx.depth,\n ...(ctx.agentName ? { 'gen_ai.agent.name': ctx.agentName } : {}),\n }\n add(runsStarted, 1, attrs, 'runs')\n add(runsActive, 1, attrs, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:done', () => {\n add(runsCompleted, 1, {}, 'runs.completed')\n add(runsActive, -1, {}, 'runs.active')\n // Aborted turns skip `turn:after` / `tool:after` / `mcp:tool:after`,\n // stranding their start entries until uninstall — the run is over,\n // so flush them here.\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n add(aborts, 1, {}, 'aborts')\n }))\n\n // ---- Turn / stream metrics -------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnStart.set(ctx.turnId, Date.now())\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const started = turnStart.get(ctx.turnId)\n turnStart.delete(ctx.turnId)\n const tagsBase: MetricAttributes = {\n 'gen_ai.operation.name': 'chat',\n ...(ctx.usage.modelId ? { 'gen_ai.response.model': ctx.usage.modelId } : {}),\n ...(ctx.usage.finishReason ? { 'gen_ai.response.finish_reason': ctx.usage.finishReason } : {}),\n }\n if (typeof started === 'number')\n record(turnDuration, Date.now() - started, tagsBase, 'turn.duration')\n if (typeof ctx.usage.timeToFirstTokenMs === 'number')\n record(ttft, ctx.usage.timeToFirstTokenMs, tagsBase, 'ttft')\n\n record(tokenUsage, ctx.usage.input, { ...tagsBase, 'gen_ai.token.type': 'input' }, 'token.input')\n record(tokenUsage, ctx.usage.output, { ...tagsBase, 'gen_ai.token.type': 'output' }, 'token.output')\n if (typeof ctx.usage.cacheRead === 'number' && ctx.usage.cacheRead > 0)\n record(tokenUsage, ctx.usage.cacheRead, { ...tagsBase, 'gen_ai.token.type': 'cache_read' }, 'token.cache_read')\n if (typeof ctx.usage.cacheCreation === 'number' && ctx.usage.cacheCreation > 0)\n record(tokenUsage, ctx.usage.cacheCreation, { ...tagsBase, 'gen_ai.token.type': 'cache_creation' }, 'token.cache_creation')\n\n if (typeof ctx.usage.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, tagsBase, 'cost')\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n add(streamErrors, 1, {\n 'http.response.status_code': ctx.statusCode,\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n }, 'stream.error')\n }))\n}\n\n/** Native tool, validation, budget, pairing and OAuth metrics. */\nexport function installMetricsToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { toolDuration, toolOutput, toolCalls, toolErrors, validationRejects, validationCoercions, gateBlocks, budgetExceeded, toolBudgetExceeded, pairingRepairs, oauthRefreshes } = m\n const { toolStart, record, add } = state\n\n // ---- Native tool metrics ---------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n toolStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const started = toolStart.get(ctx.callId)\n toolStart.delete(ctx.callId)\n const tags: MetricAttributes = { 'gen_ai.tool.name': ctx.name }\n if (typeof started === 'number')\n record(toolDuration, Date.now() - started, tags, 'tool.duration')\n record(toolOutput, ctx.outputBytes, tags, 'tool.output_bytes')\n }))\n\n // NOTE: `tool:error` must NOT delete `toolStart[callId]` — on the\n // execute-error path the loop still fires `tool:after` afterwards\n // (with the `Tool error: …` payload), and that's where the errored\n // call's duration gets recorded + the entry cleaned up.\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n add(toolErrors, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'error.type': ctx.error.name,\n }, 'tool.errors')\n }))\n\n // Mid-flight per-call cancels close with `tool:cancelled` instead of\n // `tool:after` — clean up the start entry here or it leaks.\n unregisters.push(hooks.hook('tool:cancelled', (ctx) => {\n toolStart.delete(ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n add(toolCalls, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'outcome': ctx.outcome,\n }, 'tool.calls')\n if (ctx.outcome === 'gate-block') {\n add(gateBlocks, 1, {\n 'gen_ai.tool.name': ctx.name,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n }, 'gate.blocks')\n }\n // Unknown-tool dispatches fire `tool:before` (with `unknown: true`)\n // but never `tool:after` — drop the start entry so hallucinated\n // tool names can't grow the map unboundedly. `tool:dispatched` for\n // real executions fires BEFORE `tool:before`, so this only affects\n // the unknown path.\n if (ctx.outcome === 'unknown')\n toolStart.delete(ctx.callId)\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n add(validationRejects, 1, { 'gen_ai.tool.name': ctx.name }, 'validation.rejects')\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n add(validationCoercions, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'coercions': ctx.coercions.length,\n }, 'validation.coercions')\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n add(budgetExceeded, 1, {\n bytes: ctx.bytes,\n budget: ctx.budget,\n }, 'budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n add(toolBudgetExceeded, 1, {\n 'gen_ai.tool.name': ctx.tool,\n 'mode': ctx.mode,\n 'count': ctx.count,\n 'max': ctx.max,\n }, 'tool_budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n add(pairingRepairs, 1, { mode: ctx.mode }, 'pairing.repairs')\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n add(oauthRefreshes, 1, { provider: ctx.provider, source: ctx.source }, 'oauth.refreshes')\n }))\n}\n\n/** MCP tool / bootstrap / server metrics. */\nexport function installMetricsMcpHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { mcpToolDuration, mcpBootstrapDuration, mcpToolErrors, mcpErrors, mcpAuthRequired } = m\n const { mcpStart, record, add } = state\n\n // ---- MCP metrics -----------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n mcpStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const started = mcpStart.get(ctx.callId)\n mcpStart.delete(ctx.callId)\n const tags: MetricAttributes = {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n }\n if (typeof started === 'number')\n record(mcpToolDuration, Date.now() - started, tags, 'mcp.tool.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n mcpStart.delete(ctx.callId)\n add(mcpToolErrors, 1, {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n 'error.type': ctx.error.name,\n }, 'mcp.tool.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n record(mcpBootstrapDuration, ctx.durationMs, {\n 'gen_ai.mcp.server': ctx.name,\n 'ok': ctx.ok,\n }, 'mcp.bootstrap.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n add(mcpErrors, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'error.type': ctx.error.name,\n }, 'mcp.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n add(mcpAuthRequired, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'transport': ctx.transport,\n 'reason': ctx.reason,\n }, 'mcp.auth.required')\n }))\n}\n\n/** Decision request metrics. */\nexport function installMetricsDecisionHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n m: MetricsInstruments,\n state: MetricsInstallState,\n): void {\n const { decisionDuration, decisions, costMeter } = m\n const { record, add } = state\n\n unregisters.push(hooks.hook('decision:end', (ctx) => {\n const tags: MetricAttributes = {\n 'gen_ai.system': ctx.provider,\n 'gen_ai.request.model': ctx.model,\n 'outcome': ctx.ok ? 'ok' : 'error',\n 'error.type': ctx.reason,\n }\n record(decisionDuration, ctx.durationMs, tags, 'decision.duration')\n add(decisions, 1, tags, 'decisions')\n if (typeof ctx.usage?.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, { 'gen_ai.system': ctx.provider, 'gen_ai.request.model': ctx.model }, 'cost')\n }))\n}\n","/**\n * Metrics helper — wraps agent lifecycle hooks in caller-provided meters.\n *\n * Symmetric with {@link createTracingHooks} from `./tracing`. Where the\n * tracer answers \"what happened in this run?\" by producing spans, the\n * metrics helper answers \"how is the fleet behaving?\" by emitting\n * counters / histograms / up-down counters. Both are tracer-agnostic —\n * the caller supplies a `Meter` (OTel-API-shaped) and the helper plugs\n * the right hook on each metric.\n *\n * Instrument families:\n *\n * ### Histograms (distribution metrics)\n * - `gen_ai.client.operation.duration` (ms) — wall-clock per turn (`turn:before` → `turn:after`).\n * - `gen_ai.client.token.usage` — `input` / `output` token counts per turn (tagged by `gen_ai.token.type`).\n * - `gen_ai.client.time_to_first_token` (ms) — per-turn TTFT from `TurnUsage.timeToFirstTokenMs`.\n * - `gen_ai.tool.duration` (ms) — native tool execution time (`tool:before` → `tool:after`).\n * - `gen_ai.tool.output_bytes` — `tool:after.outputBytes`.\n * - `gen_ai.mcp.tool.duration` (ms) — MCP tool execution time.\n * - `gen_ai.mcp.bootstrap.duration` (ms) — MCP server bootstrap (already measured by harness).\n * - `gen_ai.decision.duration` (ms) — `decision:end.durationMs` (tagged by `outcome` / `error.type`).\n *\n * ### Counters (monotonic)\n * - `gen_ai.agent.runs` — `agent:start`.\n * - `gen_ai.agent.runs.completed` — `agent:done`.\n * - `gen_ai.agent.aborts` — `agent:abort`.\n * - `gen_ai.tool.calls` — `tool:dispatched` (tagged by `outcome`).\n * - `gen_ai.tool.errors` — `tool:error`.\n * - `gen_ai.mcp.tool.errors` — `mcp:tool:error`.\n * - `gen_ai.stream.errors` — `stream:error` (tagged by `status_code`).\n * - `gen_ai.validation.rejects` — `validation:reject`.\n * - `gen_ai.validation.coercions` — `validation:coerce`.\n * - `gen_ai.gate.blocks` — `tool:dispatched` outcome `gate-block`.\n * - `gen_ai.budget.exceeded` — `budget:exceeded`.\n * - `gen_ai.tool_budget.exceeded` — `tool-budget:exceeded` (tagged by `mode`).\n * - `gen_ai.pairing.repairs` — `pairing:repair` (tagged by `mode`).\n * - `gen_ai.oauth.refreshes` — `oauth:refresh`.\n * - `gen_ai.mcp.errors` — `mcp:error`.\n * - `gen_ai.mcp.auth.required` — `mcp:auth:required`.\n * - `gen_ai.decisions` — `decision:end` (tagged by `outcome` / `error.type`).\n * - `gen_ai.cost_usd` — accumulated cost (added on `turn:after` and `decision:end`).\n *\n * ### Up-down counter (gauge-shaped)\n * - `gen_ai.agent.runs.active` — incremented on `agent:start`, decremented on `agent:done`.\n *\n * The `Meter` shape mirrors `@opentelemetry/api`'s `Meter` exactly — passing\n * `metrics.getMeter('zidane')` works directly. Hosts using a different\n * metrics backend (StatsD, Prometheus client, custom in-memory aggregator)\n * implement the same three factory methods and the helper plugs in\n * unchanged.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { MetricsInstruments } from './metrics-hooks'\nimport { createMetricsInstallState, installMetricsDecisionHooks, installMetricsMcpHooks, installMetricsRunHooks, installMetricsToolHooks } from './metrics-hooks'\n\n// ---------------------------------------------------------------------------\n// OTel-shaped Meter interface\n// ---------------------------------------------------------------------------\n\n/** Attributes attached to a measurement. */\nexport type MetricAttributes = Record<string, string | number | boolean | undefined>\n\n/** Instrument that only increases. */\nexport interface Counter {\n /** Adds a non-negative `value`, with optional attributes. */\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Instrument that records a distribution of values. */\nexport interface Histogram {\n /** Records one measurement, with optional attributes. */\n record: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Instrument whose value can go up and down. */\nexport interface UpDownCounter {\n /** Adds a positive or negative `value`, with optional attributes. */\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\n/** Options passed to the meter when an instrument is created. */\nexport interface InstrumentOptions {\n /** Human-readable description of the instrument. */\n description?: string\n /** Unit of the measurement, for example `'ms'` or `'{token}'`. */\n unit?: string\n}\n\n/**\n * Minimal Meter interface — structurally identical to OTel's `Meter`.\n * Hosts passing `metrics.getMeter(name)` (from `@opentelemetry/api`)\n * satisfy this without adaptation.\n */\nexport interface Meter {\n /** Creates a counter instrument with the given name. */\n createCounter: (name: string, options?: InstrumentOptions) => Counter\n /** Creates a histogram instrument with the given name. */\n createHistogram: (name: string, options?: InstrumentOptions) => Histogram\n /** Creates an up/down counter instrument with the given name. */\n createUpDownCounter: (name: string, options?: InstrumentOptions) => UpDownCounter\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createMetricsHooks}. */\nexport interface MetricsHooksOptions {\n /** OpenTelemetry-shaped meter that creates the instruments. */\n meter: Meter\n /**\n * Optional prefix prepended to every instrument name. Default: no prefix\n * (instrument names follow OTel Gen AI semantic conventions verbatim,\n * which is the most-portable shape). Set to e.g. `'zidane.'` to\n * namespace inside a shared meter registry.\n */\n namespace?: string\n /**\n * Optional baseline attributes applied to every measurement. Typical\n * use: `{ service: 'tui', env: 'prod' }`. Per-event attributes win on\n * key collision.\n */\n baseAttributes?: MetricAttributes\n /**\n * Error sink for meter failures. The helper still swallows the throw\n * so a broken backend can't crash a run; this callback surfaces the\n * failure for ops dashboards.\n */\n onError?: (kind: string, err: unknown) => void\n}\n\n/** Value returned by {@link createMetricsHooks}. */\nexport interface MetricsHookSet {\n /**\n * Attaches every metrics hook handler to the given agent hooks instance.\n * Returns an `uninstall` function that detaches the handlers.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction prefixed(prefix: string | undefined, name: string): string {\n return prefix ? `${prefix}${name}` : name\n}\n\n// ---------------------------------------------------------------------------\n// createMetricsHooks\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of metrics hook handlers that can be installed on an agent.\n *\n * @example OpenTelemetry\n * ```ts\n * import { metrics } from '@opentelemetry/api'\n * const meter = metrics.getMeter('zidane')\n * const m = createMetricsHooks({ meter, baseAttributes: { service: 'tui' } })\n * const uninstall = m.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @param options - Meter plus optional `namespace`, `baseAttributes` and `onError`.\n */\nexport function createMetricsHooks(options: MetricsHooksOptions): MetricsHookSet {\n const ns = options.namespace\n const base = options.baseAttributes\n const onError = options.onError ?? (() => {})\n\n const make = {\n counter: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createCounter(prefixed(ns, name), opts), name, onError),\n histogram: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createHistogram(prefixed(ns, name), opts), name, onError),\n upDown: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createUpDownCounter(prefixed(ns, name), opts), name, onError),\n }\n\n // Instruments — created once at install time.\n const turnDuration = make.histogram('gen_ai.client.operation.duration', { unit: 'ms', description: 'Per-turn LLM operation duration.' })\n const tokenUsage = make.histogram('gen_ai.client.token.usage', { unit: 'tokens', description: 'Per-turn token usage (tagged by gen_ai.token.type).' })\n const ttft = make.histogram('gen_ai.client.time_to_first_token', { unit: 'ms', description: 'Per-turn time to first token.' })\n const toolDuration = make.histogram('gen_ai.tool.duration', { unit: 'ms', description: 'Native tool execution wall clock.' })\n const toolOutput = make.histogram('gen_ai.tool.output_bytes', { unit: 'By', description: 'Native tool output bytes.' })\n const mcpToolDuration = make.histogram('gen_ai.mcp.tool.duration', { unit: 'ms', description: 'MCP tool execution wall clock.' })\n const mcpBootstrapDuration = make.histogram('gen_ai.mcp.bootstrap.duration', { unit: 'ms', description: 'MCP server bootstrap duration.' })\n const decisionDuration = make.histogram('gen_ai.decision.duration', { unit: 'ms', description: 'Decision request wall clock.' })\n\n const runsStarted = make.counter('gen_ai.agent.runs', { description: 'Runs started.' })\n const runsCompleted = make.counter('gen_ai.agent.runs.completed', { description: 'Runs completed.' })\n const aborts = make.counter('gen_ai.agent.aborts', { description: 'Run abort events.' })\n const toolCalls = make.counter('gen_ai.tool.calls', { description: 'Tool dispatches (tagged by outcome).' })\n const toolErrors = make.counter('gen_ai.tool.errors', { description: 'Native tool errors.' })\n const mcpToolErrors = make.counter('gen_ai.mcp.tool.errors', { description: 'MCP tool errors.' })\n const streamErrors = make.counter('gen_ai.stream.errors', { description: 'Provider stream errors.' })\n const validationRejects = make.counter('gen_ai.validation.rejects', { description: 'Tool input validation rejects.' })\n const validationCoercions = make.counter('gen_ai.validation.coercions', { description: 'Tool input auto-coercions.' })\n const gateBlocks = make.counter('gen_ai.gate.blocks', { description: 'Tool-gate refusals.' })\n const budgetExceeded = make.counter('gen_ai.budget.exceeded', { description: 'Per-turn byte budget exceedances.' })\n const toolBudgetExceeded = make.counter('gen_ai.tool_budget.exceeded', { description: 'Per-tool dispatch budget exceedances.' })\n const pairingRepairs = make.counter('gen_ai.pairing.repairs', { description: 'Tool-pairing repair counts.' })\n const oauthRefreshes = make.counter('gen_ai.oauth.refreshes', { description: 'Provider OAuth credential refreshes.' })\n const mcpErrors = make.counter('gen_ai.mcp.errors', { description: 'MCP server errors.' })\n const mcpAuthRequired = make.counter('gen_ai.mcp.auth.required', { description: 'MCP bootstrap blocked on missing auth.' })\n const decisions = make.counter('gen_ai.decisions', { description: 'Decision requests (tagged by outcome).' })\n const costMeter = make.counter('gen_ai.cost_usd', { unit: 'USD', description: 'Cumulative cost across turns.' })\n\n const runsActive = make.upDown('gen_ai.agent.runs.active', { description: 'In-flight agent runs.' })\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const state = createMetricsInstallState(base, onError)\n const { turnStart, toolStart, mcpStart } = state\n const instruments: MetricsInstruments = {\n turnDuration,\n tokenUsage,\n ttft,\n toolDuration,\n toolOutput,\n mcpToolDuration,\n mcpBootstrapDuration,\n decisionDuration,\n decisions,\n runsStarted,\n runsCompleted,\n aborts,\n toolCalls,\n toolErrors,\n mcpToolErrors,\n streamErrors,\n validationRejects,\n validationCoercions,\n gateBlocks,\n budgetExceeded,\n toolBudgetExceeded,\n pairingRepairs,\n oauthRefreshes,\n mcpErrors,\n mcpAuthRequired,\n costMeter,\n runsActive,\n }\n\n const unregisters: Array<() => void> = []\n\n installMetricsRunHooks(hooks, unregisters, instruments, state)\n installMetricsToolHooks(hooks, unregisters, instruments, state)\n installMetricsMcpHooks(hooks, unregisters, instruments, state)\n installMetricsDecisionHooks(hooks, unregisters, instruments, state)\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }\n },\n }\n}\n\n/**\n * Defensive factory wrapper — returns undefined when meter creation\n * throws. Lets a half-broken meter (one unsupported instrument) still\n * give partial metrics rather than collapsing the whole install.\n */\nfunction safeFactory<T>(\n factory: () => T,\n name: string,\n onError: (kind: string, err: unknown) => void,\n): T | undefined {\n try {\n return factory()\n }\n catch (err) {\n try {\n onError(`createInstrument:${name}`, err)\n }\n catch { /* ignore */ }\n return undefined\n }\n}\n","import type { AgentHookMap } from './agent'\nimport type { ToolOutcome, ToolResultContent } from './types'\n\ntype MaybePromise<T> = T | Promise<T>\n\n/** Tool outcomes that count as a successful mutation. */\nexport type SuccessfulMutationOutcome = Extract<ToolOutcome, 'created' | 'updated' | 'edited'>\n\n/** One successful file mutation (`created`, `updated` or `edited`) made by a tool call. */\nexport interface MutationValidationMutation {\n /** Id of the turn that made the call. */\n turnId: string\n /** Id of the tool call. */\n callId: string\n /** Name of the tool that made the change. */\n tool: string\n /** Display name of the tool. */\n displayName: string\n /** Input the tool was called with. */\n input: Record<string, unknown>\n /** Outcome the tool reported for the call. */\n outcome: SuccessfulMutationOutcome\n /** Result the tool returned for the call. */\n result: string | ToolResultContent[]\n}\n\n/** Successful mutations from one tool-results round, passed to `validate`. */\nexport interface MutationValidationBatch {\n /** Id of the turn that produced the mutations. */\n turnId: string\n /** Successful mutations since the last validation, in call order. */\n mutations: readonly MutationValidationMutation[]\n}\n\n/** Result of a validation: `{ ok: true }`, or `{ ok: false }` with an optional message and error list. */\nexport type MutationValidationResult\n = | { ok: true }\n | { ok: false, message?: string, errors?: readonly string[] }\n\n/** Details of a failed validation, passed to `onFailure`. */\nexport interface MutationValidationFailure {\n /** Batch that failed validation. */\n batch: MutationValidationBatch\n /** Failing result returned by `validate`. */\n result: Extract<MutationValidationResult, { ok: false }>\n /** Message built for the failure, from `buildFailureMessage` or the default. */\n message: string\n /** Number of failures so far in the current run, including this one. */\n failureCount: number\n}\n\n/** Options for {@link createMutationValidationHooks}. */\nexport interface MutationValidationHooksOptions {\n /** Checks a batch of successful mutations. Return `{ ok: false }` to report a failure, or nothing to pass. */\n validate: (batch: MutationValidationBatch) => MaybePromise<MutationValidationResult | void>\n /**\n * Called when validation fails and the run has not crossed\n * `maxFailuresPerRun`. Hosts typically call `agent.followUp(message)` here.\n */\n onFailure?: (failure: MutationValidationFailure) => MaybePromise<void>\n /** Builds the message reported to the model for a failure. Defaults to a summary of the changed files and the validation details. */\n buildFailureMessage?: (failure: Omit<MutationValidationFailure, 'message'>) => string\n /** Called when `validate` throws. The error is not reported to the model. */\n onError?: (error: unknown, batch: MutationValidationBatch) => MaybePromise<void>\n /**\n * Maximum number of failures reported per run. Later failures are ignored.\n * @default 2\n */\n maxFailuresPerRun?: number\n}\n\nfunction defaultFailureMessage(failure: Omit<MutationValidationFailure, 'message'>): string {\n const changed = failure.batch.mutations\n .map(m => `${m.displayName} ${String(m.input.path ?? m.callId)}`)\n .join(', ')\n const details = failure.result.message\n ?? failure.result.errors?.join('\\n')\n ?? 'Validation failed after the last file mutation.'\n const seen = failure.failureCount > 1 ? ` (failure ${failure.failureCount})` : ''\n return `[Mutation validation failed${seen}] ${changed}\\n${details}\\nFix the validation errors with the smallest focused change.`\n}\n\nfunction isSuccessfulMutation(outcome: ToolOutcome | undefined): outcome is SuccessfulMutationOutcome {\n return outcome === 'created' || outcome === 'updated' || outcome === 'edited'\n}\n\n/**\n * Build host-side hooks for post-mutation validation without changing the\n * built-in edit/write tools. The helper only observes successful mutations\n * reported through `tool:after.outcome`; validation commands and recovery\n * prompts remain host policy.\n *\n * @param options - Validator and optional failure handling. `validate` is required.\n */\nexport function createMutationValidationHooks(options: MutationValidationHooksOptions): AgentHookMap {\n const pending: MutationValidationMutation[] = []\n let failureCount = 0\n const maxFailures = Math.max(1, Math.floor(options.maxFailuresPerRun ?? 2))\n\n return {\n 'tool:after': (ctx) => {\n if (!isSuccessfulMutation(ctx.outcome))\n return\n pending.push({\n turnId: ctx.turnId,\n callId: ctx.callId,\n tool: ctx.name,\n displayName: ctx.displayName,\n input: ctx.input,\n outcome: ctx.outcome,\n result: ctx.result,\n })\n },\n 'tool-results:after': async (ctx) => {\n if (pending.length === 0)\n return\n const mutations = pending.splice(0)\n const batch: MutationValidationBatch = {\n turnId: ctx.turnId,\n mutations,\n }\n let result: MutationValidationResult | void\n try {\n result = await options.validate(batch)\n }\n catch (err) {\n await options.onError?.(err, batch)\n return\n }\n if (!result || result.ok)\n return\n failureCount++\n if (failureCount > maxFailures)\n return\n const withoutMessage = { batch, result, failureCount }\n const message = options.buildFailureMessage?.(withoutMessage)\n ?? defaultFailureMessage(withoutMessage)\n await options.onFailure?.({ ...withoutMessage, message })\n },\n 'agent:done': () => {\n pending.length = 0\n // `agent:done` fires at the end of every run, so resetting here gives\n // `maxFailuresPerRun` true per-run semantics even when this hook map is\n // registered at agent level and reused across runs.\n failureCount = 0\n },\n }\n}\n","/**\n * A host's main thread: how busy it was and how late timers ran (the delay\n * histogram, on only while sampling: its timer would otherwise wake an idle\n * process 50 times a second).\n *\n * Busy is the thread's own CPU time over wall time\n * (`process.threadCpuUsage()`): Electron runs Node on Chromium's message\n * loop, where Node's event-loop utilisation reads 0. On a runtime without\n * it, utilisation is the fallback.\n */\nimport type { PerfLag } from './types'\nimport { monitorEventLoopDelay, performance } from 'node:perf_hooks'\n\n// Inferred, not imported by name: Bun's `node:perf_hooks` types (an app type-checking\n// against the published package with `bun` types) do not name them.\ntype IntervalHistogram = ReturnType<typeof monitorEventLoopDelay>\ntype EventLoopUtilization = ReturnType<typeof performance.eventLoopUtilization>\n\n/** A host's main thread, measured: how busy it was, and (while on) how late its timers ran. */\nexport interface MainThreadMeter {\n /** Turns the delay histogram on. */\n start: () => void\n /** Turns it off. */\n stop: () => void\n /** Busy share since the previous read (0–1), and the delay over that span when on. */\n read: () => { busy: number, lag: PerfLag | null }\n /** Busy share since the process started (0–1). */\n sinceStart: () => number\n}\n\nconst NS_PER_MS = 1e6\nconst RESOLUTION_MS = 20\n\ntype ThreadCpu = () => { user: number, system: number }\n\n/** The CPU time of this thread, µs, when the runtime offers it (Node 23.9+, Bun). */\nfunction threadCpu(): ThreadCpu | null {\n const fn = (process as { threadCpuUsage?: ThreadCpu }).threadCpuUsage\n return typeof fn === 'function' ? () => fn.call(process) : null\n}\n\n/**\n * The histogram's figures less the timer's own interval. Node records each\n * whole interval (never under the resolution), Bun only how late it ran:\n * the resolution comes off when the shortest one holds it.\n */\nfunction lagOf(histogram: IntervalHistogram): PerfLag {\n const base = histogram.min / NS_PER_MS >= RESOLUTION_MS * 0.9 ? RESOLUTION_MS : 0\n const ms = (ns: number): number => Math.max(0, ns / NS_PER_MS - base)\n return { p50: ms(histogram.percentile(50)), p99: ms(histogram.percentile(99)), max: ms(histogram.max) }\n}\n\n/**\n * Creates the meter.\n *\n * @param cpu - The thread's CPU time (tests); default `process.threadCpuUsage`.\n * @param now - Wall clock, ms (tests).\n */\nexport function createMainThreadMeter(cpu: ThreadCpu | null = threadCpu(), now: () => number = () => performance.now()): MainThreadMeter {\n const used = (): number => {\n const usage = cpu!()\n return (usage.user + usage.system) / 1000\n }\n let lastCpu = cpu ? used() : 0\n let lastAt = now()\n let lastElu: EventLoopUtilization = performance.eventLoopUtilization()\n let histogram: IntervalHistogram | null = null\n return {\n start() {\n if (histogram)\n return\n histogram = monitorEventLoopDelay({ resolution: RESOLUTION_MS })\n histogram.enable()\n },\n stop() {\n histogram?.disable()\n histogram = null\n },\n read() {\n let busy: number\n if (cpu) {\n const at = now()\n const ms = used()\n busy = at > lastAt ? Math.min(1, Math.max(0, (ms - lastCpu) / (at - lastAt))) : 0\n lastCpu = ms\n lastAt = at\n }\n else {\n const elu = performance.eventLoopUtilization()\n busy = performance.eventLoopUtilization(elu, lastElu).utilization\n lastElu = elu\n }\n let lag: PerfLag | null = null\n if (histogram && histogram.count > 0) {\n lag = lagOf(histogram)\n histogram.reset()\n }\n return { busy, lag }\n },\n sinceStart: () =>\n cpu ? Math.min(1, used() / Math.max(1, process.uptime() * 1000)) : performance.eventLoopUtilization().utilization,\n }\n}\n","/**\n * The OS view of the process tree: one `ps` for every process, a host's\n * descendants picked out of it, CPU from the change in cumulative CPU time\n * between two samples, and a readable label from the command line. Pure, but\n * for the readers ({@link readProcessTable}, {@link readProcessTree},\n * {@link readLinuxCpuSeconds}), which run the commands.\n */\nimport { execFile } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { basename } from 'node:path'\n\n/** One OS process, as `ps` reports it. */\nexport interface PsRow {\n /** The process id. */\n pid: number\n /** Its parent's process id. */\n ppid: number\n /** Its process group (`=== pid`: it leads one). */\n pgid: number\n /** Resident memory, KiB. */\n rssKb: number\n /** CPU time used since it started (user + system), seconds. */\n cpuSeconds: number\n /** The command line. */\n args: string\n}\n\n/**\n * `ps`'s `time` column in seconds: `m:ss.cc` (macOS), `[dd-]hh:mm:ss` (Linux).\n *\n * @param value - The column.\n * @returns Seconds, `NaN` when it doesn't parse.\n */\nexport function parsePsTime(value: string): number {\n const match = /^(?:(\\d+)-)?([\\d:.]+)$/.exec(value.trim())\n if (!match)\n return Number.NaN\n const days = match[1] ? Number(match[1]) : 0\n const parts = match[2]!.split(':').map(Number)\n if (parts.some(p => Number.isNaN(p)))\n return Number.NaN\n let seconds = 0\n for (const part of parts) seconds = seconds * 60 + part\n return days * 86_400 + seconds\n}\n\n/**\n * Parses `ps -A -o pid=,ppid=,pgid=,rss=,time=,args=` (run with `LC_ALL=C`).\n *\n * @param text - Its output.\n * @returns One row per line that parses.\n */\nexport function parsePsOutput(text: string): PsRow[] {\n const rows: PsRow[] = []\n for (const line of text.split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\S.*)$/.exec(line)\n if (!match)\n continue\n const cpuSeconds = parsePsTime(match[5]!)\n if (Number.isNaN(cpuSeconds))\n continue\n rows.push({\n pid: Number(match[1]),\n ppid: Number(match[2]),\n pgid: Number(match[3]),\n rssKb: Number(match[4]),\n cpuSeconds,\n args: match[6]!.trim(),\n })\n }\n return rows\n}\n\n/**\n * The rows under each root (the roots included), parents before children.\n *\n * @param rows - Every process.\n * @param roots - The trees' roots (the app, the daemon, a server). A root `ps` doesn't list is skipped.\n * @returns The rows of the trees, in walk order.\n */\nexport function descendants(rows: readonly PsRow[], roots: readonly number[]): PsRow[] {\n const byPid = new Map(rows.map(row => [row.pid, row]))\n const children = new Map<number, PsRow[]>()\n for (const row of rows) {\n const list = children.get(row.ppid)\n if (list)\n list.push(row)\n else\n children.set(row.ppid, [row])\n }\n const out: PsRow[] = []\n const seen = new Set<number>()\n const walk = (row: PsRow): void => {\n if (seen.has(row.pid))\n return\n seen.add(row.pid)\n out.push(row)\n for (const child of children.get(row.pid) ?? []) walk(child)\n }\n for (const root of roots) {\n const row = byPid.get(root)\n if (row)\n walk(row)\n }\n return out\n}\n\nconst INTERPRETERS = new Set(['node', 'bun', 'deno', 'python', 'python3', 'ruby', 'sh', 'bash', 'zsh', 'npx', 'bunx', 'uv', 'uvx'])\n\n/**\n * A short name for a command line: the program, or the script an interpreter runs\n * (`node …/typescript-language-server --stdio` → `typescript-language-server (node)`).\n *\n * @param args - The command line.\n * @returns The label.\n */\nexport function commandLabel(args: string): string {\n const parts = args.split(/\\s+/).filter(Boolean)\n if (parts.length === 0)\n return '?'\n // A login shell shows as `-zsh`.\n const program = basename(parts[0]!).replace(/^-/, '')\n // `sh -c '…'` runs an inline command, not a script: the shell is the name.\n if (INTERPRETERS.has(program) && !parts.includes('-c')) {\n const script = parts.slice(1).find(part => !part.startsWith('-'))\n if (script)\n return `${basename(script)} (${program})`\n }\n return program\n}\n\n/**\n * CPU use between two readings, in percent of one core.\n *\n * @param before - CPU seconds at the first reading.\n * @param after - CPU seconds at the second.\n * @param elapsedMs - Wall time between them.\n * @returns The percentage, `0` when it can't be told.\n */\nexport function cpuPercent(before: number, after: number, elapsedMs: number): number {\n if (!(elapsedMs > 0) || !(after >= before))\n return 0\n return ((after - before) * 1000 * 100) / elapsedMs\n}\n\n/**\n * The monitor's own commands (`ps` lists itself, and `ioreg` when it runs\n * alongside): not the host's. Held until a collector clears them at the start\n * of its next tick, since `ps` has exited by the time its output is parsed.\n */\nexport const ownCommands = new Set<number>()\n\n/**\n * Runs a command and resolves its stdout, in the C locale (`ps` writes `0,5` in a French one).\n *\n * @param file - The program.\n * @param args - Its arguments.\n * @param timeoutMs - Killed past this.\n */\nexport function run(file: string, args: readonly string[], timeoutMs = 5_000): Promise<string> {\n return new Promise((resolve, reject) => {\n const child = execFile(\n file,\n args,\n { env: { ...process.env, LC_ALL: 'C' }, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 },\n (error, stdout) => (error ? reject(error) : resolve(stdout)),\n )\n // Bounded even with no tick to clear it (thread reads on expand).\n if (ownCommands.size > 64)\n ownCommands.clear()\n if (child.pid !== undefined)\n ownCommands.add(child.pid)\n })\n}\n\n/** Every process on the machine, from one `ps` call. */\nexport async function readProcessTable(): Promise<PsRow[]> {\n return parsePsOutput(await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,rss=,time=,args=']))\n}\n\n/**\n * The trees under `roots` (`descendants`), the monitor's own commands left\n * out. On Linux each row's CPU time is read from `/proc`: its `ps` rounds\n * it to whole seconds.\n *\n * @param roots - The trees' roots.\n */\nexport async function readProcessTree(roots: readonly number[]): Promise<PsRow[]> {\n const tree = descendants(await readProcessTable(), roots).filter(row => !ownCommands.has(row.pid))\n if (process.platform !== 'linux')\n return tree\n return Promise.all(tree.map(async row => ({ ...row, cpuSeconds: (await readLinuxCpuSeconds(row.pid)) ?? row.cpuSeconds })))\n}\n\n/**\n * `/proc/<pid>/stat`'s user + system time in seconds (Linux's `ps` rounds it to whole seconds).\n *\n * @param stat - The file's text.\n * @param ticksPerSecond - `CLK_TCK`.\n * @returns Seconds, `NaN` when it doesn't parse.\n */\nexport function parseProcStatCpu(stat: string, ticksPerSecond = 100): number {\n // The command name is in parentheses and may hold spaces: count fields after the last `)`.\n const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ')\n // Fields 14 and 15 of the file are 12 and 13 after the name and state.\n const utime = Number(fields[11])\n const stime = Number(fields[12])\n return Number.isNaN(utime) || Number.isNaN(stime) ? Number.NaN : (utime + stime) / ticksPerSecond\n}\n\n/**\n * A Linux process's CPU seconds.\n *\n * @param pid - The process.\n * @returns Seconds, `null` when it has gone.\n */\nexport async function readLinuxCpuSeconds(pid: number): Promise<number | null> {\n try {\n const seconds = parseProcStatCpu(await readFile(`/proc/${pid}/stat`, 'utf8'))\n return Number.isNaN(seconds) ? null : seconds\n }\n catch {\n return null\n }\n}\n","/**\n * Whole-GPU use on macOS, from `ioreg`'s accelerator statistics. macOS has no\n * public per-process figure: this is every app's use, and the UI says so.\n */\nimport type { PerfGpu } from './types'\nimport { run } from './ps'\n\n/** `ioreg` costs more than `ps`, and the GPU figure moves slower. */\nexport const GPU_EVERY_MS = 5_000\n\nconst STAT = (name: string): RegExp => new RegExp(`\"${name.replace(/[%()]/g, '\\\\$&')}\"=(\\\\d+)`)\n\n/**\n * Reads `ioreg -r -d 1 -w 0 -c IOAccelerator`'s `PerformanceStatistics`.\n *\n * @param text - Its output.\n * @returns The figures of the first accelerator that reports them, `null` when none does.\n */\nexport function parseIoreg(text: string): PerfGpu | null {\n const device = STAT('Device Utilization %').exec(text)\n if (!device)\n return null\n const renderer = STAT('Renderer Utilization %').exec(text)\n const tiler = STAT('Tiler Utilization %').exec(text)\n const memory = STAT('In use system memory').exec(text)\n return {\n device: Number(device[1]),\n ...(renderer ? { renderer: Number(renderer[1]) } : {}),\n ...(tiler ? { tiler: Number(tiler[1]) } : {}),\n ...(memory ? { memory: Number(memory[1]) } : {}),\n }\n}\n\n/** The GPU's use now; `null` off macOS or when `ioreg` fails. */\nexport async function readGpu(): Promise<PerfGpu | null> {\n if (process.platform !== 'darwin')\n return null\n try {\n return parseIoreg(await run('ioreg', ['-r', '-d', '1', '-w', '0', '-c', 'IOAccelerator']))\n }\n catch {\n return null\n }\n}\n\n/**\n * A GPU reader for a sampler: `ioreg` at most every `everyMs` (macOS only),\n * the last figure in between.\n *\n * @param everyMs - The least time between two reads.\n * @param read - The reader (tests); default {@link readGpu}.\n * @returns `(now) => figure`; `due(now)` says whether that call runs `ioreg`.\n */\nexport function createGpuReader(everyMs = GPU_EVERY_MS, read: () => Promise<PerfGpu | null> = readGpu): {\n due: (now: number) => boolean\n read: (now: number) => Promise<PerfGpu | null>\n} {\n let last: PerfGpu | null = null\n let at = Number.NEGATIVE_INFINITY\n const due = (now: number): boolean => process.platform === 'darwin' && now - at >= everyMs\n return {\n due,\n async read(now) {\n if (!due(now))\n return last\n last = await read()\n at = now\n return last\n },\n }\n}\n","/**\n * A process's OS threads, read when someone expands it (never sampled).\n * macOS `ps -M` lists them without names; Linux `ps -L` names them.\n */\nimport type { PerfOsThread } from './types'\nimport { run } from './ps'\n\n/**\n * Parses `ps -M -p <pid>` (macOS): one line per thread, the first with the\n * user and command around the same columns.\n *\n * @param text - Its output.\n * @returns The threads, busiest first.\n */\nexport function parseMacThreads(text: string): PerfOsThread[] {\n const threads: PerfOsThread[] = []\n for (const line of text.split('\\n').slice(1)) {\n const match = /\\s(\\d+\\.\\d+)\\s+\\S+\\s+\\S+\\s+[\\d:.]+\\s+[\\d:.]+/.exec(line)\n if (!match)\n continue\n threads.push({ id: String(threads.length + 1), label: '', cpu: Number(match[1]) })\n }\n return threads.sort((a, b) => b.cpu - a.cpu)\n}\n\n/**\n * Parses `ps -L -o tid=,pcpu=,comm= -p <pid>` (Linux).\n *\n * @param text - Its output.\n * @returns The threads, busiest first.\n */\nexport function parseLinuxThreads(text: string): PerfOsThread[] {\n const threads: PerfOsThread[] = []\n for (const line of text.split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+(?:\\.\\d+)?)\\s+(\\S.*)$/.exec(line)\n if (match)\n threads.push({ id: match[1]!, label: match[3]!.trim(), cpu: Number(match[2]) })\n }\n return threads.sort((a, b) => b.cpu - a.cpu)\n}\n\n/**\n * The OS threads of a process, busiest first.\n *\n * @param pid - The process.\n * @returns Empty on a platform without a reader, or when the process has gone.\n */\nexport async function readOsThreads(pid: number): Promise<PerfOsThread[]> {\n try {\n if (process.platform === 'darwin')\n return parseMacThreads(await run('ps', ['-M', '-p', String(pid)]))\n if (process.platform === 'linux')\n return parseLinuxThreads(await run('ps', ['-L', '-o', 'tid=,pcpu=,comm=', '-p', String(pid)]))\n }\n catch {\n // Gone between the sample and the click.\n }\n return []\n}\n","/**\n * The pids Zidane started for its own work (MCP servers, git, linters): this\n * process's registry and, when another host's tree is sampled too, that\n * host's (the desktop app's daemon, `daemon.ownedProcesses`). They lead\n * their own process group like a session's command, so `./processes.ts`\n * needs this list to count them as the app's. Read with them, the MCP\n * servers' processes (`mcp.instances`): which server a pid runs and whether\n * sessions share it; and the pids started for a session\n * (`daemon.sessionProcesses`): which session a process and its subtree run for.\n *\n * The other host's answers never hold a tick for long: one round of requests\n * at a time, awaited at most `timeoutMs`; past that, or when one fails, the\n * tick uses its last answer (a late one lands for the next tick). An older\n * host that lacks a method answers nothing for it. A new host (another pid)\n * starts empty.\n */\nimport type { PerfProcessMcp, PerfProcessSession } from './types'\nimport { AcpErrorCode } from '../acp/types'\n\n/** One owned process; the role is not read here. */\nexport interface OwnedPid {\n /** The process id. */\n pid: number\n}\n\n/** One process started for a session, as `daemon.sessionProcesses` lists it. */\nexport interface SessionPid {\n /** The process id. */\n pid: number\n /** The session's id on its host. */\n sessionId: string\n}\n\n/** One MCP server process, as `mcp.instances` lists it. */\nexport interface OwnedMcpInstance {\n /** The MCP server's name. */\n name: string\n /** One process for every session (`true`), or one session's. */\n shared: boolean\n /** Sessions holding it now. */\n sessions: number\n /** Its process; absent before it started (or for a remote server). */\n pid?: number\n}\n\n/** Another host whose tree is sampled too (the desktop app's daemon), as far as this module needs it. */\nexport interface OwnedDaemon {\n /** The host's process id: a new pid is a new host, whose answers start empty. */\n pid: number\n /** `daemon.ownedProcesses`. */\n ownedProcesses: () => Promise<readonly OwnedPid[]>\n /** `mcp.instances`. */\n mcpInstances: () => Promise<readonly OwnedMcpInstance[]>\n /** `daemon.sessionProcesses`. */\n sessionProcesses: () => Promise<readonly SessionPid[]>\n}\n\nexport interface OwnedDeps {\n /** This process's registry (`listOwnedProcesses`). */\n local: () => readonly OwnedPid[]\n /** This process's MCP server processes (its host's `mcpServers.instances()`). */\n localMcp: () => readonly OwnedMcpInstance[]\n /** This process's session processes (`listSessionProcesses`). */\n localSessions: () => readonly SessionPid[]\n /** The other host; `null` when there is none. Default: none. */\n daemon?: () => OwnedDaemon | null\n /** How long a tick waits for the other host. Default 500 ms. */\n timeoutMs?: number\n}\n\n/** One tick's reading. */\nexport interface Owned {\n /** Every pid Zidane started for its own work, local and the other host's. */\n pids: Set<number>\n /** The MCP server each MCP process runs, by pid. */\n mcp: Map<number, PerfProcessMcp>\n /** The session each process was started for (its id on its host), by pid. */\n sessions: Map<number, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 500\n\n/** The host predates the method: it answers nothing for it. */\nfunction isMethodNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === AcpErrorCode.MethodNotFound\n}\n\n/** Resolves when `promise` settles or after `ms`, whichever is first. */\nasync function within(promise: Promise<void>, ms: number): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const late = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, ms)\n timer.unref?.()\n })\n try {\n await Promise.race([promise, late])\n }\n finally {\n clearTimeout(timer)\n }\n}\n\ninterface DaemonAnswer {\n daemonPid: number\n pids: readonly number[]\n mcp: readonly OwnedMcpInstance[]\n sessions: readonly SessionPid[]\n}\n\n/**\n * The owned-process reader a sampler calls once a tick: the local registries, and the other host's last answers.\n *\n * @param deps - The local registries and the other host.\n * @returns A reader for one tick: the owned pids, the MCP processes and the session processes, local and the other host's.\n */\nexport function createOwnedProcesses(deps: OwnedDeps): () => Promise<Owned> {\n const { local, localMcp, localSessions, daemon = () => null, timeoutMs = DEFAULT_TIMEOUT_MS } = deps\n /** The last answer, per daemon pid. */\n let last: DaemonAnswer | null = null\n let pending: Promise<void> | null = null\n\n const ask = (target: OwnedDaemon): Promise<void> => {\n const keep = (part: Partial<Omit<DaemonAnswer, 'daemonPid'>>): void => {\n if (last?.daemonPid === target.pid)\n last = { ...last, ...part }\n }\n // Any failure but a missing method keeps the last answer.\n const settle = <T>(request: Promise<T>, onAnswer: (answer: T | null) => void): Promise<void> =>\n request.then(onAnswer, (err: unknown) => {\n if (isMethodNotFound(err))\n onAnswer(null)\n })\n const request: Promise<void> = Promise.all([\n settle(target.ownedProcesses(), rows => keep({ pids: rows?.map(row => row.pid) ?? [] })),\n settle(target.mcpInstances(), rows => keep({ mcp: rows ?? [] })),\n settle(target.sessionProcesses(), rows => keep({ sessions: rows ?? [] })),\n ])\n .then(() => undefined)\n .finally(() => {\n if (pending === request)\n pending = null\n })\n return request\n }\n\n const daemonAnswer = async (): Promise<DaemonAnswer | null> => {\n const target = daemon()\n if (!target) {\n last = null\n return null\n }\n if (last?.daemonPid !== target.pid) {\n last = { daemonPid: target.pid, pids: [], mcp: [], sessions: [] }\n pending = null\n }\n pending ??= ask(target)\n await within(pending, timeoutMs)\n return last\n }\n\n return async () => {\n const answer = await daemonAnswer()\n const mcp = new Map<number, PerfProcessMcp>()\n for (const { pid, name, shared, sessions } of [...readLocalMcp(localMcp), ...(answer?.mcp ?? [])]) {\n if (pid !== undefined)\n mcp.set(pid, { name, shared, sessions })\n }\n const sessions = new Map<number, string>()\n for (const { pid, sessionId } of [...localSessions(), ...(answer?.sessions ?? [])]) sessions.set(pid, sessionId)\n return { pids: new Set([...local().map(entry => entry.pid), ...(answer?.pids ?? [])]), mcp, sessions }\n }\n}\n\n/**\n * Names each process's session as the reader does, and titles it, read once per session.\n *\n * @param sessions - The session id of each process (`Owned.sessions`).\n * @param titleOf - A session's title; `undefined` while it has none.\n * @param keyOf - The reader's key for a session id (the desktop app's `SessionKey`). Default: the id.\n * @returns The session of each process, by pid.\n */\nexport function labelSessions(\n sessions: ReadonlyMap<number, string>,\n titleOf: (sessionId: string) => string | undefined,\n keyOf: (sessionId: string) => string = id => id,\n): Map<number, PerfProcessSession> {\n const named = new Map<string, PerfProcessSession>()\n const out = new Map<number, PerfProcessSession>()\n for (const [pid, sessionId] of sessions) {\n let session = named.get(sessionId)\n if (!session) {\n const title = titleOf(sessionId)\n session = { id: keyOf(sessionId), ...(title ? { title } : {}) }\n named.set(sessionId, session)\n }\n out.set(pid, session)\n }\n return out\n}\n\n/** The local host's MCP processes; none when it can't say. */\nfunction readLocalMcp(read: () => readonly OwnedMcpInstance[]): readonly OwnedMcpInstance[] {\n try {\n return read()\n }\n catch {\n return []\n }\n}\n","/**\n * A host's process tree as performance rows: the OS rows under the host's\n * processes (language servers, shells, MCP servers, dev servers), each with\n * its CPU from the change in CPU time since the previous sample, in percent\n * of one core, and whose cost it is ({@link PerfOrigin}). Pure.\n *\n * Whose cost: Zidane's own (`app`) are the host's processes (the desktop\n * app's Electron processes, the daemon, a server) and what they start to do\n * the app's work (MCP and language servers, git, the linters, the monitor's\n * `ps`). `launched` is what a session or the user ran. Every place that\n * runs a command for them (the shell tool, background tasks, terminals, the\n * composer's `!` commands) starts it as the leader of a new process group,\n * so that stopping it stops its whole tree. So a direct child of a host\n * process that leads its own group starts a launched subtree, and everything\n * under it is launched. Some of the app's own spawns lead a group too, for\n * the same reason (MCP servers, and git, gh and the linters through\n * `runBounded`): their spawner registers them in the owned-process registry\n * (`src/contexts/owned-processes.ts`, read by `./owned.ts`), and an owned\n * pid and its subtree are the app's. The others (language servers) stay in\n * their parent's group. The command line can't tell: `sh -c 'bun bench.ts'`\n * execs into `bun`, which reads like an MCP server started with `bun`.\n *\n * Whose session: a process a session started (its shell tool's commands and\n * background tasks, its terminals, its own MCP servers, its `!` commands;\n * the session-process registry, read by `./owned.ts`) runs for it, and so\n * does everything under it.\n */\nimport type { PsRow } from './ps'\nimport type { PerfOrigin, PerfProcess, PerfProcessKind, PerfProcessMcp, PerfProcessSession, PerfTotals } from './types'\nimport { commandLabel, cpuPercent } from './ps'\n\n/** CPU seconds per pid at the previous sample, for the rows' CPU. */\nexport type CpuMemory = Map<number, { cpuSeconds: number, at: number }>\n\n/** How a tree's root shows: a host process (the daemon, a server). */\nexport interface TreeRoot {\n /** What the root process is. */\n kind: PerfProcessKind\n /** Its row's name: \"Daemon\", \"Server\". */\n label: string\n}\n\n/** What {@link treeProcesses} turns into rows. */\nexport interface TreeInput {\n /** The OS rows under the roots (`descendants`), roots included, parents first. */\n tree: readonly PsRow[]\n /** The rows shown as roots (no parent), with their kind and label. */\n roots: ReadonlyMap<number, TreeRoot>\n /** The host's own processes: a group leader started directly under one is launched work, unless owned. */\n hosts: ReadonlySet<number>\n /** Rows another source lists (the desktop app's Electron processes): their CPU baseline is kept, no row is made. */\n listed?: ReadonlySet<number>\n /** The pids Zidane started for its own work (MCP servers, tool runs): never launched, though they lead a group. */\n owned: ReadonlySet<number>\n /** The MCP server each MCP process runs, by pid (`./owned.ts`). */\n mcp?: ReadonlyMap<number, PerfProcessMcp>\n /** The session each process a session started runs for, by pid: it and its subtree carry it. */\n sessions?: ReadonlyMap<number, PerfProcessSession>\n /** Read and updated: the rows' CPU seconds (rows seen now are kept, rows gone are dropped). */\n previous: CpuMemory\n /** When the rows were read (epoch ms): the CPU's time base. */\n now: number\n}\n\n/**\n * One row per OS process of the tree, in the tree's order (`listed` ones left out).\n *\n * @param input - The tree, its roots and hosts, what Zidane owns and the CPU memory.\n * @returns The processes.\n */\nexport function treeProcesses(input: TreeInput): PerfProcess[] {\n const { tree, roots, hosts, listed, owned, mcp, sessions, previous, now } = input\n const inTree = new Set(tree.map(row => row.pid))\n const launched = new Set<number>()\n const sessionOf = new Map<number, PerfProcessSession>()\n const out: PerfProcess[] = []\n for (const row of tree) {\n const before = previous.get(row.pid)\n previous.set(row.pid, { cpuSeconds: row.cpuSeconds, at: now })\n if (listed?.has(row.pid))\n continue\n const root = roots.get(row.pid)\n // The rule is in this module's doc. `tree` lists parents first, so a launched parent is known by now.\n const startsLaunched = hosts.has(row.ppid) && row.pgid === row.pid && !owned.has(row.pid)\n const isLaunched = !root && (launched.has(row.ppid) || startsLaunched)\n if (isLaunched)\n launched.add(row.pid)\n const server = mcp?.get(row.pid)\n // Parents come first: a session's process is known before its children.\n const session = root ? undefined : (sessions?.get(row.pid) ?? sessionOf.get(row.ppid))\n if (session)\n sessionOf.set(row.pid, session)\n out.push({\n pid: row.pid,\n parent: root ? null : inTree.has(row.ppid) ? row.ppid : null,\n kind: root?.kind ?? 'child',\n label: root?.label ?? commandLabel(row.args),\n detail: row.args,\n origin: isLaunched ? 'launched' : 'app',\n cpu: before ? cpuPercent(before.cpuSeconds, row.cpuSeconds, now - before.at) : 0,\n memory: row.rssKb * 1024,\n ...(server ? { mcp: server } : {}),\n ...(session ? { session } : {}),\n })\n }\n for (const pid of previous.keys()) {\n if (!inTree.has(pid))\n previous.delete(pid)\n }\n return out\n}\n\n/**\n * CPU and memory summed per origin.\n *\n * @param processes - One sample's.\n */\nexport function originTotals(processes: readonly PerfProcess[]): Record<PerfOrigin, PerfTotals> {\n const totals: Record<PerfOrigin, PerfTotals> = { app: { cpu: 0, memory: 0 }, launched: { cpu: 0, memory: 0 } }\n for (const process of processes) {\n totals[process.origin].cpu += process.cpu\n totals[process.origin].memory += process.memory\n }\n return totals\n}\n","/** A fixed-capacity list that drops its oldest item when full. */\nexport interface Ring<T> {\n /** Adds an item, dropping the oldest when full. */\n push: (item: T) => void\n /** Oldest first. */\n items: () => T[]\n /** The newest item, if any. */\n last: () => T | undefined\n /** Empties it. */\n clear: () => void\n}\n\n/**\n * Creates a ring.\n *\n * @param capacity - How many items it keeps.\n */\nexport function createRing<T>(capacity: number): Ring<T> {\n const buffer: T[] = []\n let start = 0\n return {\n push(item) {\n if (buffer.length < capacity) {\n buffer.push(item)\n }\n else {\n buffer[start] = item\n start = (start + 1) % capacity\n }\n },\n items: () => [...buffer.slice(start), ...buffer.slice(0, start)],\n last: () => (buffer.length === 0 ? undefined : buffer[(start + buffer.length - 1) % buffer.length]),\n clear() {\n buffer.length = 0\n start = 0\n },\n }\n}\n","/**\n * When sampling runs: only while at least one watcher is subscribed (a\n * window with Settings › Performance or the overlay open, a connection that\n * called `perf.watch`). The first watcher starts the loop, the last one\n * stops it. Ticks never overlap: the next one is scheduled once the previous\n * sample is in. Samples land in a ring (the last 5 minutes at 2 s).\n */\nimport { createRing } from './ring'\n\n/** The timer the loop waits on (tests drive their own). */\nexport interface SamplerTimers {\n set: (run: () => void, ms: number) => unknown\n clear: (handle: unknown) => void\n}\n\n/** What {@link createSampler} samples, and how often. */\nexport interface SamplerOptions<T> {\n /** Takes one sample. */\n collect: () => Promise<T>\n /** Called when the loop starts and stops (the delay histogram, the windows' answers). */\n onActive?: (active: boolean) => void\n /** Between the end of one sample and the start of the next. Default {@link SAMPLE_INTERVAL_MS}. */\n intervalMs?: number\n /** Samples kept. Default {@link SAMPLE_CAPACITY}. */\n capacity?: number\n /** The timer the loop waits on. Default real timers that never hold the process open. */\n timers?: SamplerTimers\n}\n\n/** A sampling loop that runs only while someone watches. */\nexport interface Sampler<T> {\n /** Gets each sample from now on; the first subscriber starts the loop. Returns the unsubscribe. */\n subscribe: (listener: (sample: T) => void) => () => void\n /** The samples held, oldest first. */\n history: () => T[]\n /** Whether the loop runs. */\n active: () => boolean\n /** Resolves with the next sample, starting the loop for it if nobody watches. */\n next: () => Promise<T>\n}\n\n/** Between two samples, ms. */\nexport const SAMPLE_INTERVAL_MS = 2_000\n/** Samples kept: 5 minutes at {@link SAMPLE_INTERVAL_MS}. */\nexport const SAMPLE_CAPACITY = 150\n\n/** Real timers that never hold the process open. */\nconst REAL_TIMERS: SamplerTimers = {\n set: (run, ms) => {\n const timer = setTimeout(run, ms)\n timer.unref?.()\n return timer\n },\n clear: handle => clearTimeout(handle as ReturnType<typeof setTimeout>),\n}\n\n/**\n * A sampling loop over `collect`: it starts with the first subscriber, stops with the last, never overlaps two ticks, and keeps the last `capacity` samples.\n *\n * @param options - What to sample (`collect`), the activity callback, the interval, the capacity and the timers.\n */\nexport function createSampler<T>({\n collect,\n onActive,\n intervalMs = SAMPLE_INTERVAL_MS,\n capacity = SAMPLE_CAPACITY,\n timers = REAL_TIMERS,\n}: SamplerOptions<T>): Sampler<T> {\n const ring = createRing<T>(capacity)\n const listeners = new Set<(sample: T) => void>()\n let timer: unknown = null\n let running = false\n\n const tick = async (): Promise<void> => {\n timer = null\n let sample: T | null = null\n try {\n sample = await collect()\n }\n catch {\n // A failed read skips one sample; the next tick tries again.\n }\n if (!running)\n return\n if (sample) {\n ring.push(sample)\n for (const listener of [...listeners]) listener(sample)\n }\n if (running)\n timer = timers.set(() => void tick(), intervalMs)\n }\n\n const start = (): void => {\n if (running)\n return\n running = true\n onActive?.(true)\n // The first read only sets the baselines CPU figures are diffed against.\n timer = timers.set(() => void tick(), intervalMs)\n void collect().catch(() => undefined)\n }\n const stop = (): void => {\n if (!running)\n return\n running = false\n if (timer !== null)\n timers.clear(timer)\n timer = null\n onActive?.(false)\n }\n\n const subscribe = (listener: (sample: T) => void): (() => void) => {\n listeners.add(listener)\n start()\n return () => {\n if (!listeners.delete(listener))\n return\n if (listeners.size === 0)\n stop()\n }\n }\n\n return {\n subscribe,\n history: () => ring.items(),\n active: () => running,\n next: () =>\n new Promise((resolve) => {\n const off = subscribe((sample) => {\n off()\n resolve(sample)\n })\n }),\n }\n}\n","/**\n * A host's own performance figures, for `perf.*` (`src/ipc/daemon/handlers/perf.ts`):\n * the host process (the daemon, a server) as the root of its tree, with what\n * it runs for itself and for each session (`./processes.ts`), its event\n * loop, the GPU (macOS) and the monitor's own cost. A host has no windows,\n * so a sample's channels and slow frames are empty. Sampling runs only while\n * something watches (`./sampler.ts`).\n */\nimport type { OwnedMcpInstance } from './owned'\nimport type { CpuMemory } from './processes'\nimport type { Sampler } from './sampler'\nimport type { PerfOsThread, PerfSample } from './types'\nimport { availableParallelism } from 'node:os'\nimport { performance } from 'node:perf_hooks'\nimport { listOwnedProcesses } from '../contexts/owned-processes'\nimport { listSessionProcesses } from '../contexts/session-processes'\nimport { createMainThreadMeter } from './event-loop'\nimport { createGpuReader } from './gpu'\nimport { readOsThreads } from './os-threads'\nimport { createOwnedProcesses, labelSessions } from './owned'\nimport { originTotals, treeProcesses } from './processes'\nimport { ownCommands, readProcessTree } from './ps'\nimport { createSampler } from './sampler'\n\n/** What {@link createHostPerf} needs of its host. */\nexport interface HostPerfOptions {\n /** The host process's row: `Server`, `Daemon`. Its event loop is `<label> event loop`. */\n label: string\n /** The MCP server processes of the host's sessions (its `mcpServers.instances()`). */\n mcpInstances: () => readonly OwnedMcpInstance[]\n /** A session's title; `undefined` while it has none. */\n sessionTitle: (sessionId: string) => Promise<string | undefined>\n /** Between two samples. Default 2 s. */\n intervalMs?: number\n}\n\n/** A host's performance monitor: what `perf.watch`, `perf.history` and `perf.threads` answer from. */\nexport interface HostPerf {\n /** Gets each sample from now on; the first watcher starts sampling, the last unsubscribe stops it. */\n watch: Sampler<PerfSample>['subscribe']\n /** The samples held, oldest first. */\n history: () => PerfSample[]\n /** A process's OS threads, busiest first. Rejects with {@link PerfProcessNotFoundError} for a pid outside the host's tree. */\n threads: (pid: number) => Promise<PerfOsThread[]>\n}\n\n/** `perf.threads` asked for a process the host doesn't list. */\nexport class PerfProcessNotFoundError extends Error {\n /**\n * @param pid - The process asked for.\n */\n constructor(\n /** The process asked for. */\n readonly pid: number,\n ) {\n super(`Process ${pid} is not one of this host's.`)\n this.name = 'PerfProcessNotFoundError'\n }\n}\n\n/** Each session's title, read once per tick; one that fails reads as none. */\nasync function titles(sessions: ReadonlyMap<number, string>, read: HostPerfOptions['sessionTitle']): Promise<Map<string, string>> {\n const out = new Map<string, string>()\n await Promise.all([...new Set(sessions.values())].map(async (sessionId) => {\n const title = await read(sessionId).catch(() => undefined)\n if (title)\n out.set(sessionId, title)\n }))\n return out\n}\n\n/**\n * Creates the host's monitor. Nothing runs until the first watch.\n *\n * @param options - The host's label, MCP processes and session titles.\n */\nexport function createHostPerf(options: HostPerfOptions): HostPerf {\n const { label, mcpInstances, sessionTitle, intervalMs } = options\n const meter = createMainThreadMeter()\n const gpu = createGpuReader()\n const owned = createOwnedProcesses({ local: listOwnedProcesses, localMcp: mcpInstances, localSessions: listSessionProcesses })\n const previous: CpuMemory = new Map()\n const cores = availableParallelism()\n const since = Date.now()\n const roots = new Map([[process.pid, { kind: 'daemon' as const, label }]])\n const hosts = new Set([process.pid])\n let lastAt = since\n\n const collect = async (): Promise<PerfSample> => {\n ownCommands.clear()\n const now = Date.now()\n const elapsedMs = now - lastAt\n lastAt = now\n const ownedPending = owned()\n const commandsStart = performance.now()\n const [tree, gpuNow] = await Promise.all([readProcessTree([process.pid]).catch(() => []), gpu.read(now)])\n const commandMs = performance.now() - commandsStart\n const reading = await ownedPending\n const named = await titles(reading.sessions, sessionTitle)\n\n const start = performance.now()\n const processes = treeProcesses({\n tree,\n roots,\n hosts,\n owned: reading.pids,\n mcp: reading.mcp,\n sessions: labelSessions(reading.sessions, id => named.get(id)),\n previous,\n now,\n })\n const { busy, lag } = meter.read()\n const totals = originTotals(processes)\n const mainMs = performance.now() - start\n return {\n at: now,\n intervalMs: elapsedMs,\n cores,\n processes,\n threads: [{ id: 'main', label: `${label} event loop`, pid: process.pid, kind: 'main', busy, heap: process.memoryUsage().heapUsed }],\n channels: [],\n frames: [],\n countersSince: since,\n lag,\n gpu: gpuNow,\n totals: totals.app,\n launched: totals.launched,\n self: { cpu: elapsedMs > 0 ? (mainMs * 100) / elapsedMs : 0, commandMs },\n }\n }\n\n const sampler = createSampler({\n collect,\n onActive: active => (active ? meter.start() : meter.stop()),\n ...(intervalMs !== undefined ? { intervalMs } : {}),\n })\n\n return {\n watch: sampler.subscribe,\n history: sampler.history,\n async threads(pid) {\n const tree = await readProcessTree([process.pid])\n if (!tree.some(row => row.pid === pid))\n throw new PerfProcessNotFoundError(pid)\n return readOsThreads(pid)\n },\n }\n}\n","/**\n * Gen-AI attribute keys and span-attribute builders shared by the\n * {@link createTracingHooks} (`./tracing`) hook installers.\n */\n\nimport type { TracingConventions } from './tracing'\nimport type { SessionMessage } from './types'\n\n// ---------------------------------------------------------------------------\n// Gen AI semantic-convention attribute keys\n// ---------------------------------------------------------------------------\n\n/**\n * Gen-AI attribute keys for the Sentry and OTel conventions. Keys whose\n * comment names one convention are emitted only when `conventions` selects\n * it. The rest carry identical semantics in both.\n */\nexport const GEN_AI = {\n system: 'gen_ai.system',\n operationName: 'gen_ai.operation.name',\n requestModel: 'gen_ai.request.model',\n responseModel: 'gen_ai.response.model',\n responseFinishReasons: 'gen_ai.response.finish_reasons',\n responseId: 'gen_ai.response.id',\n responseStreaming: 'gen_ai.response.streaming',\n responseTokensPerSecond: 'gen_ai.response.tokens_per_second',\n // Sentry uses seconds; OTel uses ms. We emit one or both based on `conventions`.\n responseTimeToFirstTokenSeconds: 'gen_ai.response.time_to_first_token',\n responseTimeToFirstTokenMs: 'gen_ai.client.time_to_first_token',\n // Usage — Sentry expects input_tokens to INCLUDE cached, with subkeys for cache slices.\n usageInputTokens: 'gen_ai.usage.input_tokens',\n usageOutputTokens: 'gen_ai.usage.output_tokens',\n usageTotalTokens: 'gen_ai.usage.total_tokens',\n // Sentry-specific cache keys.\n usageInputTokensCached: 'gen_ai.usage.input_tokens.cached',\n usageInputTokensCacheWrite: 'gen_ai.usage.input_tokens.cache_write',\n usageOutputTokensReasoning: 'gen_ai.usage.output_tokens.reasoning',\n // OTel cache keys (older / vendor-neutral).\n usageCacheReadInputTokens: 'gen_ai.usage.cache_read_input_tokens',\n usageCacheCreationInputTokens: 'gen_ai.usage.cache_creation_input_tokens',\n usageReasoningTokens: 'gen_ai.usage.reasoning_tokens',\n // Cost — Sentry splits, OTel rolls up.\n costTotalTokens: 'gen_ai.cost.total_tokens',\n costInputTokens: 'gen_ai.cost.input_tokens',\n costOutputTokens: 'gen_ai.cost.output_tokens',\n usageCostUsd: 'gen_ai.usage.cost_usd',\n // Tool — Sentry uses `tool.call.arguments` / `tool.call.result`; OTel uses `tool.input` / `tool.output` (deprecated in Sentry).\n toolName: 'gen_ai.tool.name',\n toolDescription: 'gen_ai.tool.description',\n toolType: 'gen_ai.tool.type',\n toolCallId: 'gen_ai.tool.call.id',\n toolCallArguments: 'gen_ai.tool.call.arguments',\n toolCallResult: 'gen_ai.tool.call.result',\n toolMessage: 'gen_ai.tool.message',\n toolInputDeprecated: 'gen_ai.tool.input',\n toolOutputDeprecated: 'gen_ai.tool.output',\n // Request configuration.\n requestMaxTokens: 'gen_ai.request.max_tokens',\n requestTemperature: 'gen_ai.request.temperature',\n requestTopP: 'gen_ai.request.top_p',\n requestTopK: 'gen_ai.request.top_k',\n requestSeed: 'gen_ai.request.seed',\n requestFrequencyPenalty: 'gen_ai.request.frequency_penalty',\n requestPresencePenalty: 'gen_ai.request.presence_penalty',\n // Message content surfaces (Sentry conversation viewer).\n inputMessages: 'gen_ai.input.messages',\n /** zidane: oldest messages left out of `inputMessages` by its size cap. */\n inputMessagesDropped: 'gen_ai.agent.input_messages_dropped',\n outputMessages: 'gen_ai.output.messages',\n systemInstructions: 'gen_ai.system_instructions',\n toolDefinitions: 'gen_ai.tool.definitions',\n // Agent identity.\n agentName: 'gen_ai.agent.name',\n agentRunId: 'gen_ai.agent.run.id',\n agentParentRunId: 'gen_ai.agent.parent_run.id',\n agentDepth: 'gen_ai.agent.depth',\n pipelineName: 'gen_ai.pipeline.name',\n turn: 'gen_ai.agent.turn',\n // MCP-specific (zidane extension; no Sentry op equivalent).\n mcpServer: 'gen_ai.mcp.server',\n mcpToolCount: 'gen_ai.mcp.tool_count',\n} as const\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Compose namespace prefix once. */\nexport function prefixed(namespace: string | undefined, name: string): string {\n return namespace ? `${namespace}/${name}` : name\n}\n\n/** Drop undefined entries — tracers vary on how they render `undefined`. */\nexport function compact(attrs: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/**\n * One part of a message in Sentry's `{role, parts: [{type, content}]}` shape.\n * Per Sentry footnote [7], binary blobs in image content are replaced with\n * `\"[Blob substitute]\"`.\n */\ninterface SentryMessagePart {\n type: 'text' | 'tool_call' | 'tool_result' | 'reasoning'\n content?: string\n name?: string\n arguments?: string\n result?: string\n call_id?: string\n}\ninterface SentryMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n parts: SentryMessagePart[]\n}\n\n/**\n * Wide-shape block record. `SessionContentBlock` is a discriminated\n * union — accepting the wide record lets us peek at the discriminator\n * without re-importing the entire union at this layer.\n */\ninterface AnyBlock { type: string, [k: string]: unknown }\n\nfunction blockToParts(block: AnyBlock): SentryMessagePart[] {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', content: typeof block.text === 'string' ? block.text : '' }]\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n return [{ type: 'text', content: '[Blob substitute]' }]\n case 'tool_call':\n return [{\n type: 'tool_call',\n ...(typeof block.name === 'string' ? { name: block.name } : {}),\n ...(typeof block.id === 'string' ? { call_id: block.id } : {}),\n arguments: safeJson(block.input),\n }]\n case 'tool_result': {\n const output = block.output\n const text = typeof output === 'string'\n ? output\n : Array.isArray(output)\n ? (output as Array<{ type: string, text?: string }>)\n .map(b => (b.type === 'image' || b.type === 'audio' || b.type === 'video' || b.type === 'document') ? '[Blob substitute]' : (b.text ?? ''))\n .join('\\n')\n : ''\n return [{\n type: 'tool_result',\n ...(typeof block.callId === 'string' ? { call_id: block.callId } : {}),\n content: text,\n }]\n }\n case 'thinking':\n return [{ type: 'reasoning', content: typeof block.text === 'string' ? block.text : '' }]\n default:\n return []\n }\n}\n\n/**\n * Convert a zidane message (a `SessionMessage` or `StreamOptions.messages`\n * entry) into Sentry's message shape. Tool calls become `{type: 'tool_call'}`\n * parts, tool results become `{type: 'tool_result'}` parts.\n */\nfunction messageToSentry(msg: { role: 'user' | 'assistant', content: readonly AnyBlock[] }): SentryMessage {\n const parts: SentryMessagePart[] = []\n for (const block of msg.content)\n parts.push(...blockToParts(block))\n return { role: msg.role, parts }\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value ?? null)\n }\n catch {\n return '\"[unserializable]\"'\n }\n}\n\n/**\n * Stringify an array of `SessionMessage`s into Sentry's `gen_ai.input.messages`\n * format. Each entry's role + content is mapped via `messageToSentry`.\n * Returns `undefined` when the input is empty or unserializable.\n */\nexport function stringifyMessages(messages: readonly SessionMessage[]): string | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const sentry = messages.map(m => messageToSentry({\n role: m.role,\n content: m.content as readonly AnyBlock[],\n }))\n return JSON.stringify(sentry)\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Upper bound on a chat span's `gen_ai.input.messages`, in characters. The\n * attribute carries the whole outgoing transcript, so without a bound a long\n * session serializes (and ships) every earlier message again on every turn.\n */\nexport const INPUT_MESSAGES_MAX_CHARS = 128 * 1024\n\n/**\n * {@link stringifyMessages} for the newest messages that fit in `maxChars`\n * (the newest one always does), serializing nothing older than the cut.\n * `dropped` counts the oldest messages left out; with none dropped, `json`\n * is exactly what `stringifyMessages` returns.\n */\nexport function stringifyRecentMessages(messages: readonly SessionMessage[], maxChars: number): { json: string, dropped: number } | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const kept: string[] = []\n let size = 2\n let i = messages.length - 1\n for (; i >= 0; i--) {\n const m = messages[i]!\n const json = JSON.stringify(messageToSentry({ role: m.role, content: m.content as readonly AnyBlock[] }))\n if (kept.length > 0 && size + json.length + 1 > maxChars)\n break\n kept.push(json)\n size += json.length + 1\n }\n return { json: `[${kept.reverse().join(',')}]`, dropped: i + 1 }\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Stringify a `StreamOptions.tools` array into Sentry's\n * `gen_ai.tool.definitions` format: `[{name, description?, parameters?}, …]`.\n *\n * The input is `unknown[]` because `StreamOptions.tools` carries the\n * provider-formatted shape (Anthropic / OpenAI / OpenAI-compat all\n * differ). We probe the three names that overlap (`name`, `description`,\n * `inputSchema` / `parameters`) and degrade gracefully otherwise.\n */\nexport function stringifyToolDefs(tools: readonly unknown[] | undefined): string | undefined {\n if (!tools || tools.length === 0)\n return undefined\n try {\n return JSON.stringify(tools.map((raw) => {\n if (!raw || typeof raw !== 'object')\n return { name: 'anonymous' }\n const t = raw as Record<string, unknown>\n const name = typeof t.name === 'string' ? t.name : 'anonymous'\n const description = typeof t.description === 'string' ? t.description : undefined\n // OpenAI's wire format calls it `parameters`; Anthropic / ToolSpec\n // call it `input_schema` / `inputSchema`. Probe all three.\n const parameters = t.parameters ?? t.input_schema ?? t.inputSchema\n return {\n name,\n ...(description ? { description } : {}),\n ...(parameters !== undefined ? { parameters } : {}),\n }\n }))\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Build the usage attribute set per the configured conventions. Sentry\n * expects `input_tokens` to include cached, with `input_tokens.cached`\n * + `input_tokens.cache_write` subkeys; OTel splits cache into\n * separate top-level keys. `'both'` emits everything.\n */\nexport function buildUsageAttrs(\n usage: {\n input: number\n output: number\n cacheRead?: number\n cacheCreation?: number\n thinking?: number\n cost?: number\n modelId?: string\n finishReason?: string\n timeToFirstTokenMs?: number\n },\n conventions: TracingConventions,\n): Record<string, unknown> {\n const cacheRead = usage.cacheRead ?? 0\n const cacheWrite = usage.cacheCreation ?? 0\n // Per Sentry: `input_tokens` includes cached. zidane reports `input` as\n // the uncached new-input slice, so the headline number is the sum.\n const inputInclCache = usage.input + cacheRead + cacheWrite\n const totalTokens = inputInclCache + usage.output\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const wantOtel = conventions === 'otel' || conventions === 'both'\n\n const out: Record<string, unknown> = {\n [GEN_AI.responseModel]: usage.modelId,\n [GEN_AI.responseFinishReasons]: usage.finishReason ? [usage.finishReason] : undefined,\n [GEN_AI.usageOutputTokens]: usage.output,\n [GEN_AI.usageTotalTokens]: totalTokens,\n }\n\n if (wantSentry) {\n out[GEN_AI.usageInputTokens] = inputInclCache\n if (cacheRead > 0)\n out[GEN_AI.usageInputTokensCached] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageInputTokensCacheWrite] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageOutputTokensReasoning] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.costTotalTokens] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenSeconds] = usage.timeToFirstTokenMs / 1000\n }\n if (wantOtel) {\n // OTel: input_tokens is the uncached slice; cache is separate.\n if (!wantSentry)\n out[GEN_AI.usageInputTokens] = usage.input\n if (cacheRead > 0)\n out[GEN_AI.usageCacheReadInputTokens] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageCacheCreationInputTokens] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageReasoningTokens] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.usageCostUsd] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenMs] = usage.timeToFirstTokenMs\n }\n return out\n}\n","/**\n * Decision span installer behind {@link createTracingHooks} (`./tracing`):\n * one `decide <model>` span per `decision:start` → `decision:end`.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI } from './tracing-attrs'\n\nexport function installTracingDecisionHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { conventions, captureContent, decisionSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n unregisters.push(hooks.hook('decision:start', (ctx) => {\n const span = safeStartSpan(`decide ${ctx.model ?? 'unresolved'}`, {\n [GEN_AI.operationName]: 'decide',\n [GEN_AI.system]: ctx.provider,\n [GEN_AI.requestModel]: ctx.model,\n 'sentry.op': 'gen_ai.decide',\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.decision.id': ctx.decisionId,\n 'gen_ai.decision.questions': ctx.questionNames.join(','),\n 'gen_ai.decision.input_kind': ctx.inputKind,\n })\n if (span)\n decisionSpans.set(ctx.decisionId, span)\n }))\n\n unregisters.push(hooks.hook('decision:end', (ctx) => {\n const span = decisionSpans.get(ctx.decisionId)\n if (!span)\n return\n const attrs: Record<string, unknown> = {\n [GEN_AI.responseModel]: ctx.ok ? ctx.model : undefined,\n 'gen_ai.decision.ok': ctx.ok,\n ...(ctx.usage ? buildUsageAttrs(ctx.usage, conventions) : {}),\n }\n if (!ctx.ok) {\n attrs['error.type'] = ctx.reason\n attrs['error.message'] = ctx.message\n }\n if (captureContent && ctx.answers)\n attrs['gen_ai.decision.answers'] = redact('decision-answers', JSON.stringify(ctx.answers), { decisionId: ctx.decisionId })\n safeSetAttrs(span, attrs)\n safeEnd(decisionSpans, ctx.decisionId)\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { GEN_AI } from './tracing-attrs'\n\n/** MCP `execute_tool` span. */\nexport function installTracingMcpToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, mcpSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n // -----------------------------------------------------------------\n // MCP tool span — OTel Gen AI `execute_tool` operation, MCP variant\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.server}.${ctx.tool}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.toolName]: ctx.displayName,\n // MCP tools are Sentry's \"extension\" tool type — backed by an\n // out-of-process server rather than a local function.\n [GEN_AI.toolType]: 'extension',\n [GEN_AI.toolCallId]: ctx.callId,\n [GEN_AI.mcpServer]: ctx.server,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.mcp.tool': ctx.tool,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.run.id': ctx.runId,\n ...(legacy\n ? {\n server: ctx.server,\n tool: ctx.tool,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n mcpSpans.set(ctx.callId, span)\n if (captureContent) {\n try {\n const redacted = redact('mcp-tool-input', JSON.stringify(ctx.input), {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch { /* ignore */ }\n }\n }\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const span = mcpSpans.get(ctx.callId)\n safeSetAttrs(span, { 'gen_ai.tool.output_bytes': ctx.outputBytes })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('mcp-tool-result', ctx.result, {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n safeSetAttrs(mcpSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(mcpSpans, ctx.callId)\n }))\n}\n\n/** MCP server bootstrap span + server-level events. */\nexport function installTracingMcpServerHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { bootstrapSpans, safeStartSpan, safeSetAttrs, safeEnd, safeAddEvent } = rt\n\n // -----------------------------------------------------------------\n // MCP server bootstrap span\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:start', (ctx) => {\n const span = safeStartSpan(\n `mcp.bootstrap ${ctx.name}`,\n {\n [GEN_AI.operationName]: 'mcp.bootstrap',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.mcpServer]: ctx.name,\n 'gen_ai.mcp.transport': ctx.transport,\n },\n )\n if (span)\n bootstrapSpans.set(ctx.name, span)\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n const ok = ctx.ok\n safeSetAttrs(span, {\n 'gen_ai.mcp.bootstrap.duration_ms': ctx.durationMs,\n 'gen_ai.mcp.bootstrap.ok': ok,\n ...(ok\n ? {\n [GEN_AI.mcpToolCount]: ctx.toolCount,\n 'gen_ai.mcp.lazy': ctx.lazy,\n 'gen_ai.mcp.cached': ctx.cached,\n }\n : {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n }),\n })\n safeEnd(bootstrapSpans, ctx.name)\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n safeAddEvent(span ?? rt.activeRunSpan, 'gen_ai.mcp.auth.required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.mcp.error', {\n 'server': ctx.name,\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI, INPUT_MESSAGES_MAX_CHARS, stringifyMessages, stringifyRecentMessages, stringifyToolDefs } from './tracing-attrs'\n\n/** `invoke_agent` root span: `agent:start` / `agent:done` / `agent:abort`. */\nexport function installTracingAgentHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, runSpans, turnSpans, toolSpans, mcpSpans, spawnSpans, bootstrapSpans, turnTrack, safeStartSpan, safeSetAttrs, endAll } = rt\n\n // -----------------------------------------------------------------\n // agent.run (root)\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n rt.activeSystem = ctx.providerName\n // Empty-string names produce `invoke_agent ` (trailing space).\n // Treat empty as \"no name\" so the span name reads cleanly.\n const displayName = ctx.agentName && ctx.agentName.length > 0\n ? ctx.agentName\n : 'agent'\n const span = safeStartSpan(\n `invoke_agent ${displayName}`,\n {\n [GEN_AI.operationName]: 'invoke_agent',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.agentName]: ctx.agentName,\n [GEN_AI.agentRunId]: ctx.runId,\n [GEN_AI.agentParentRunId]: ctx.parentRunId,\n [GEN_AI.agentDepth]: ctx.depth,\n // Sentry-specific span op hint — backends that key off\n // `op` (Sentry Python/JS) read this directly; OTel\n // backends ignore unknown keys, so it's harmless.\n 'sentry.op': 'gen_ai.invoke_agent',\n ...(legacy\n ? {\n runId: ctx.runId,\n parentRunId: ctx.parentRunId,\n agentName: ctx.agentName,\n depth: ctx.depth,\n }\n : {}),\n },\n ctx.tracingContext,\n )\n if (span) {\n runSpans.set(ctx.runId, span)\n rt.activeRunSpan = span\n }\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n // `agent:done` doesn't carry a runId on its payload (it's `AgentStats`),\n // so close every open run span and stamp the totals on each. This\n // assumes one in-flight run per install.\n const usageAttrs = buildUsageAttrs({\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n timeToFirstTokenMs: stats.timeTillFirstTokenMs,\n }, conventions)\n for (const [, span] of runSpans) {\n safeSetAttrs(span, {\n ...usageAttrs,\n [GEN_AI.turn]: stats.turns,\n 'gen_ai.agent.elapsed_ms': stats.elapsed,\n ...(legacy\n ? {\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n totalCacheRead: stats.totalCacheRead,\n totalCacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n turns: stats.turns,\n elapsed: stats.elapsed,\n }\n : {}),\n })\n }\n endAll(runSpans)\n // Defensive — close anything else still hanging open.\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n rt.activeRunSpan = undefined\n rt.activeSystem = undefined\n turnTrack.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n for (const [, span] of runSpans)\n safeSetAttrs(span, { 'gen_ai.agent.status': 'aborted' })\n }))\n}\n\n/** `chat` turn span + stream events. */\nexport function installTracingTurnHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, turnSpans, turnTrack, safeStartSpan, safeSetAttrs, safeEnd, safeAddEvent, redact } = rt\n\n // -----------------------------------------------------------------\n // Turn span — OTel Gen AI `chat` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n const requestedModel = ctx.options.model\n // Built-in Sentry/OTel attrs that don't depend on content capture.\n const baseAttrs: Record<string, unknown> = {\n [GEN_AI.operationName]: 'chat',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.requestModel]: requestedModel,\n [GEN_AI.turn]: ctx.turn,\n [GEN_AI.responseStreaming]: true,\n [GEN_AI.requestMaxTokens]: ctx.options.maxTokens,\n 'sentry.op': 'gen_ai.chat',\n 'gen_ai.agent.turn_id': ctx.turnId,\n ...(legacy ? { turnId: ctx.turnId, turn: ctx.turn } : {}),\n }\n // Optionally surface conversation content — prompts, system\n // instructions, available tools. Each piece flows through the\n // redact seam so hosts can scrub secrets without losing the\n // structural attribute. Skipping content keeps the span cheap\n // and privacy-safe.\n if (captureContent) {\n const systemText = ctx.options.system\n if (systemText) {\n baseAttrs[GEN_AI.systemInstructions] = redact('system', systemText, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n // Newest messages first, under a size cap: the full transcript on every\n // turn is quadratic over a session.\n const inputMsgs = stringifyRecentMessages(ctx.options.messages, INPUT_MESSAGES_MAX_CHARS)\n if (inputMsgs) {\n baseAttrs[GEN_AI.inputMessages] = redact('prompt', inputMsgs.json, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n if (inputMsgs.dropped > 0)\n baseAttrs[GEN_AI.inputMessagesDropped] = inputMsgs.dropped\n }\n const toolDefs = stringifyToolDefs(ctx.options.tools)\n if (toolDefs)\n baseAttrs[GEN_AI.toolDefinitions] = toolDefs\n }\n const span = safeStartSpan(\n `chat${requestedModel ? ` ${requestedModel}` : ''}`,\n baseAttrs,\n )\n if (span) {\n turnSpans.set(ctx.turnId, span)\n turnTrack.set(ctx.turnId, { startedAt: Date.now() })\n }\n }))\n\n unregisters.push(hooks.hook('stream:start', (ctx) => {\n safeAddEvent(turnSpans.get(ctx.turnId), 'gen_ai.stream.start', {\n startedAt: ctx.startedAt,\n })\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const tracked = turnTrack.get(ctx.turnId)\n turnTrack.delete(ctx.turnId)\n // Tokens-per-second derivation. Use wall-clock duration since the\n // turn span opened, fall back to TTFT-adjusted slice when present.\n let tokensPerSecond: number | undefined\n if (tracked && ctx.usage.output > 0) {\n const elapsedMs = Date.now() - tracked.startedAt\n if (elapsedMs > 0)\n tokensPerSecond = (ctx.usage.output / elapsedMs) * 1000\n }\n const usageAttrs = buildUsageAttrs(ctx.usage, conventions)\n // Cumulative-through-this-turn totals. We compute `input_tokens`\n // here to mirror `gen_ai.usage.input_tokens`'s semantic on the\n // same span: it INCLUDES cached when Sentry conventions are in\n // play, matches the uncached slice when OTel-only. Without this\n // alignment the two keys (`usage.*` vs `cumulative.*`) would\n // silently differ in arithmetic.\n const cumInputTokens = wantSentry\n ? ctx.cumulativeUsage.input + ctx.cumulativeUsage.cacheRead + ctx.cumulativeUsage.cacheCreation\n : ctx.cumulativeUsage.input\n const attrs: Record<string, unknown> = {\n ...usageAttrs,\n [GEN_AI.responseTokensPerSecond]: tokensPerSecond,\n 'gen_ai.agent.cumulative.input_tokens': cumInputTokens,\n 'gen_ai.agent.cumulative.output_tokens': ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.total_tokens': cumInputTokens + ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.cache_read_input_tokens': ctx.cumulativeUsage.cacheRead,\n 'gen_ai.agent.cumulative.cache_creation_input_tokens': ctx.cumulativeUsage.cacheCreation,\n 'gen_ai.agent.cumulative.cost_usd': ctx.cumulativeUsage.cost,\n 'gen_ai.agent.cumulative.turns': ctx.cumulativeUsage.turns,\n ...(legacy\n ? {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n finishReason: ctx.usage.finishReason,\n modelId: ctx.usage.modelId,\n }\n : {}),\n }\n // Output messages — the assistant turn that just landed. Sentry's\n // conversation viewer keys off this; skipped when content capture\n // is off. `system` turns aren't serialized (Sentry's schema only\n // accepts user / assistant / system / tool — and we never emit\n // system role from the loop).\n if (captureContent && ctx.message && ctx.message.role !== 'system' && ctx.message.role !== 'command') {\n const outputMsgs = stringifyMessages([{\n role: ctx.message.role,\n content: ctx.message.content,\n }])\n if (outputMsgs)\n attrs[GEN_AI.outputMessages] = redact('stream-text', outputMsgs, { turnId: ctx.turnId, turn: ctx.turn })\n }\n safeSetAttrs(span, attrs)\n safeEnd(turnSpans, ctx.turnId)\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const message = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n safeSetAttrs(span, {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n safeAddEvent(span, 'gen_ai.stream.error', {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n }))\n}\n","/**\n * Per-install tracing runtime for {@link createTracingHooks} (`./tracing`):\n * span book-keeping maps, the mutable active-run pointers, and the guarded\n * tracer-call helpers shared by every hook-group installer.\n */\n\nimport type { AgentHooks } from './agent-hooks'\nimport type { InterceptorRegistry } from './extensions/hooks'\nimport type { Span, TracingConventions, TracingHooksOptions } from './tracing'\nimport { compact, prefixed } from './tracing-attrs'\n\n/** Install-scoped state + guarded tracer helpers. */\nexport interface TracingRuntime {\n options: TracingHooksOptions\n legacy: boolean\n conventions: TracingConventions\n wantSentry: boolean\n captureContent: boolean\n onError: (kind: string, err: unknown) => void\n // Span book-keeping. Each map keys by the identifier that's unique\n // across concurrent in-flight spans of that kind.\n runSpans: Map<string, Span> // keyed by runId\n turnSpans: Map<string, Span> // keyed by turnId\n toolSpans: Map<string, Span> // keyed by callId\n mcpSpans: Map<string, Span> // keyed by callId\n spawnSpans: Map<string, Span> // keyed by spawn id\n bootstrapSpans: Map<string, Span> // keyed by server name\n decisionSpans: Map<string, Span> // keyed by decisionId\n // Provider system identifier (`anthropic`, `openai`, …) captured at\n // `agent:start`. Stamped onto every chat / tool span belonging to\n // this run so Sentry's per-vendor breakdowns work. Reset on\n // `agent:done`.\n activeSystem: string | undefined\n // Active turn book-keeping for tokens-per-second derivation.\n turnTrack: Map<string, { startedAt: number }>\n // Active run span ref — used so non-span events (gate blocks,\n // budget hits, validation rejects, policy/pairing repairs) can\n // attach via `addEvent` on the in-flight run span. The run span is\n // the natural parent for cross-cutting policy events: it outlives\n // turn spans (which close before tool dispatch starts in the\n // current loop), and tool spans (which haven't opened on a\n // gate-block path).\n activeRunSpan: Span | undefined\n safeStartSpan: (\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ) => Span | undefined\n safeSetAttrs: (span: Span | undefined, attrs: Record<string, unknown>) => void\n safeEnd: (map: Map<string, Span>, key: string) => void\n endAll: (map: Map<string, Span>) => void\n safeAddEvent: (span: Span | undefined, name: string, attrs: Record<string, unknown>) => void\n redact: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n}\n\n/** Factory-level settings resolved once in {@link createTracingHooks}. */\nexport interface TracingRuntimeConfig {\n namespace: string | undefined\n legacy: boolean\n conventions: TracingConventions\n wantSentry: boolean\n captureContent: boolean\n onError: (kind: string, err: unknown) => void\n /**\n * The extension interceptors: `redact` runs the `tracing.redact` chain\n * after `options.redact`. Absent without an extension runtime.\n */\n interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'>\n}\n\n/** What `tracing:redact` names a value's kind. */\ntype RedactKind = Parameters<AgentHooks['tracing:redact']>[0]['kind']\n\n/** Build a fresh runtime — called once per `install()`. */\nexport function createTracingRuntime(\n options: TracingHooksOptions,\n config: TracingRuntimeConfig,\n): TracingRuntime {\n const { namespace, legacy, conventions, wantSentry, captureContent, onError, interceptors } = config\n\n function safeStartSpan(\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ): Span | undefined {\n try {\n return options.startSpan(prefixed(namespace, name), compact(attrs), parentContext)\n }\n catch (err) {\n try {\n onError('startSpan', err)\n }\n catch { /* ignore */ }\n return undefined\n }\n }\n\n function safeSetAttrs(span: Span | undefined, attrs: Record<string, unknown>): void {\n if (!span?.setAttributes)\n return\n try {\n span.setAttributes(compact(attrs))\n }\n catch (err) {\n try {\n onError('setAttributes', err)\n }\n catch { /* ignore */ }\n }\n }\n\n function safeEnd(map: Map<string, Span>, key: string): void {\n const span = map.get(key)\n if (!span)\n return\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n map.delete(key)\n }\n\n function endAll(map: Map<string, Span>): void {\n for (const [, span] of map) {\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n }\n map.clear()\n }\n\n function safeAddEvent(span: Span | undefined, name: string, attrs: Record<string, unknown>): void {\n if (!span)\n return\n if (typeof span.addEvent === 'function') {\n try {\n span.addEvent(name, compact(attrs))\n }\n catch (err) {\n try {\n onError('addEvent', err)\n }\n catch { /* ignore */ }\n }\n return\n }\n // Tracer doesn't support events — fall back to a single\n // `event.<name>.*` attribute so the data lands somewhere.\n safeSetAttrs(span, Object.fromEntries(\n Object.entries(compact(attrs)).map(([k, v]) => [`event.${name}.${k}`, v]),\n ))\n }\n\n function redactWithOption(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n if (typeof options.redact !== 'function')\n return value\n try {\n return options.redact(kind, value, meta)\n }\n catch (err) {\n try {\n onError('redact', err)\n }\n catch { /* ignore */ }\n return value\n }\n }\n\n function redact(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n const redacted = redactWithOption(kind, value, meta)\n if (!interceptors?.has('tracing.redact'))\n return redacted\n return interceptors.runInterceptorsSync('tracing.redact', { kind: kind as RedactKind, value, ...(meta ? { meta } : {}) }, redacted)\n }\n\n return {\n options,\n legacy,\n conventions,\n wantSentry,\n captureContent,\n onError,\n runSpans: new Map<string, Span>(),\n turnSpans: new Map<string, Span>(),\n toolSpans: new Map<string, Span>(),\n mcpSpans: new Map<string, Span>(),\n spawnSpans: new Map<string, Span>(),\n bootstrapSpans: new Map<string, Span>(),\n decisionSpans: new Map<string, Span>(),\n activeSystem: undefined,\n turnTrack: new Map<string, { startedAt: number }>(),\n activeRunSpan: undefined,\n safeStartSpan,\n safeSetAttrs,\n safeEnd,\n endAll,\n safeAddEvent,\n redact,\n }\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one slice of the tracing hooks and pushes its\n * unregister fns onto the shared `unregisters` array. `createTracingHooks`\n * calls the installers in a fixed order, so registration order is\n * deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { buildUsageAttrs, GEN_AI } from './tracing-attrs'\n\n/** `handoff` spawn span + child trace-context injection. */\nexport function installTracingSpawnHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { options, conventions, onError, spawnSpans, safeStartSpan, safeSetAttrs, safeEnd } = rt\n\n // -----------------------------------------------------------------\n // Spawn span — parent-side. Also injects trace context for the child.\n //\n // Direct spawns only, on purpose: nested spawns also reach this bus as\n // `child:spawn:*`, but their handoff span and carrier belong to the\n // SPAWNER's tracer (installed on the spawner's bus, where `spawn:*`\n // fires with the spawner's active span in scope). Handling them here\n // would duplicate those spans and parent them under this agent's\n // ambient span — which is why the bubbled copy carries no carrier.\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n // Sentry: \"handoff from {from_agent} to {to_agent}\". We don't\n // always know the child agent's name at spawn time — fall back\n // to the truncated task as the destination identifier.\n const toAgent = ctx.task.length > 60 ? `${ctx.task.slice(0, 57)}…` : ctx.task\n const span = safeStartSpan(\n `handoff to ${toAgent}`,\n {\n [GEN_AI.operationName]: 'handoff',\n [GEN_AI.system]: rt.activeSystem,\n 'sentry.op': 'gen_ai.handoff',\n 'gen_ai.agent.spawn.id': ctx.id,\n 'gen_ai.agent.spawn.task': ctx.task,\n 'gen_ai.agent.depth': ctx.depth,\n },\n )\n if (span)\n spawnSpans.set(ctx.id, span)\n\n // Inject parent trace context for the child agent. The spawn tool\n // passes whatever we write into `ctx.tracingContext` to the child's\n // `agent.run({ tracingContext })` — see `tools/spawn.ts`.\n if (typeof options.getActiveTraceContext === 'function' && ctx.tracingContext) {\n let carrier: Readonly<Record<string, string>> | undefined\n try {\n carrier = options.getActiveTraceContext()\n }\n catch (err) {\n try {\n onError('getActiveTraceContext', err)\n }\n catch { /* ignore */ }\n }\n if (carrier) {\n for (const [k, v] of Object.entries(carrier))\n ctx.tracingContext[k] = v\n }\n }\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n const span = spawnSpans.get(ctx.id)\n const childUsage = buildUsageAttrs({\n input: ctx.stats.totalIn,\n output: ctx.stats.totalOut,\n cacheRead: ctx.stats.totalCacheRead,\n cacheCreation: ctx.stats.totalCacheCreation,\n cost: ctx.stats.cost,\n }, conventions)\n safeSetAttrs(span, {\n 'gen_ai.agent.spawn.status': ctx.status ?? 'completed',\n 'gen_ai.agent.spawn.depth': ctx.depth,\n ...childUsage,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n safeSetAttrs(spawnSpans.get(ctx.id), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n}\n","/**\n * Hook-group installers behind {@link createTracingHooks} (`./tracing`).\n *\n * Each installer registers one contiguous slice of the tracing hook\n * registration sequence and pushes its unregister fns onto the shared\n * `unregisters` array; {@link createTracingHooks} calls them in a fixed\n * order, so registration order is deterministic across installs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { TracingRuntime } from './tracing-runtime'\nimport { GEN_AI } from './tracing-attrs'\n\n/** Native `execute_tool` span. */\nexport function installTracingToolHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { legacy, conventions, wantSentry, captureContent, toolSpans, safeStartSpan, safeSetAttrs, safeEnd, redact } = rt\n\n // -----------------------------------------------------------------\n // Native tool span — OTel Gen AI `execute_tool` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.displayName}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: rt.activeSystem,\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolType]: 'function',\n [GEN_AI.toolCallId]: ctx.callId,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.tool.display_name': ctx.displayName,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.depth': ctx.depth,\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.agent.parent_run.id': ctx.parentRunId,\n ...(legacy\n ? {\n toolName: ctx.name,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n toolSpans.set(ctx.callId, span)\n if (captureContent) {\n // Attach (possibly redacted) input as an attribute. Sentry's\n // canonical key is `gen_ai.tool.call.arguments`; OTel's\n // older key is `gen_ai.tool.input` (now deprecated in\n // Sentry). Emit one or both based on `conventions`.\n try {\n const serialized = JSON.stringify(ctx.input)\n const redacted = redact('tool-input', serialized, {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch {\n // Non-serializable input (circular refs) — skip the attr;\n // the tool name and call id are still on the span.\n }\n }\n }\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const span = toolSpans.get(ctx.callId)\n safeSetAttrs(span, {\n 'gen_ai.tool.output_bytes': ctx.outputBytes,\n 'gen_ai.tool.coercions': ctx.coercions?.length ?? 0,\n })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('tool-result', ctx.result, {\n toolName: ctx.name,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(toolSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n\n // A per-call cancel closes the call with `tool:cancelled` instead of\n // `tool:after` / `tool:error`: end the span here, not at agent:done.\n unregisters.push(hooks.hook('tool:cancelled', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'gen_ai.tool.status': 'cancelled',\n 'gen_ai.tool.cancel_reason': ctx.reason,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n}\n\n/** Gate / validation / budget / pairing / OAuth events on the run span. */\nexport function installTracingPolicyEventHooks(\n hooks: Hookable<AgentHooks>,\n unregisters: Array<() => void>,\n rt: TracingRuntime,\n): void {\n const { safeAddEvent } = rt\n\n // Tool gate / dispatch outcomes. Listen on `tool:dispatched` (fires\n // AFTER every `tool:gate` listener has had a chance to mutate\n // `block` / `result`) so the recorded `outcome` reflects the final\n // decision rather than the as-entered state. Attaches to the\n // in-flight run span; the chat span has already closed by tool\n // dispatch time in the current loop ordering, and no tool span has\n // opened for a `gate-block` / `unknown` / `invalid-input` path.\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.gate.block', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }\n else if (ctx.outcome === 'gate-substitute') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.gate.substitute', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'unknown') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool.unknown', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'invalid-input') {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool.invalid_input', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.validation.reject', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.validation.coerce', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n coercions: ctx.coercions,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.budget.exceeded', {\n turnId: ctx.turnId,\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.tool_budget.exceeded', {\n [GEN_AI.toolName]: ctx.tool,\n turnId: ctx.turnId,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.pairing.repair', {\n mode: ctx.mode,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n safeAddEvent(rt.activeRunSpan, 'gen_ai.oauth.refresh', {\n provider: ctx.provider,\n providerId: ctx.providerId,\n source: ctx.source,\n })\n }))\n}\n","/**\n * Tracing helper — wraps agent lifecycle hooks in caller-provided spans.\n *\n * Zidane is hooks-native; rather than introducing a plugin abstraction just for\n * observability, we ship a small helper that turns an arbitrary `startSpan`\n * function into a bundle of hook handlers. Works with Sentry, OpenTelemetry,\n * Datadog — any tracer that can hand back an object with an `end()` method.\n *\n * Attribute conventions follow the Sentry AI Agents spec (which itself\n * tracks OpenTelemetry Gen AI semconv v1.36, with a few divergences). The\n * `conventions` option lets callers pick `'sentry'` (default — Sentry's\n * exact keys), `'otel'` (vendor-neutral OTel 1.36), or `'both'` (emit\n * every variant for maximum portability).\n *\n * Span coverage:\n *\n * - `invoke_agent <name?>` — root span, opens on `agent:start`, closes on `agent:done`. Sentry op `gen_ai.invoke_agent`.\n * - `chat <model?>` — per-turn LLM call, opens on `turn:before`, closes on `turn:after`. Sentry op `gen_ai.chat`.\n * - `execute_tool <name>` — native tool execution, opens on `tool:before`, closes on `tool:after` / `tool:error` / `tool:cancelled`. Sentry op `gen_ai.execute_tool`.\n * - `execute_tool <server>.<name>` — MCP tool variant, same op. Tagged with `gen_ai.tool.type: 'extension'`.\n * - `handoff to <task>` — subagent handoff (parent→child), opens on `spawn:before`, closes on `spawn:complete` / `spawn:error`. Sentry op `gen_ai.handoff`.\n * Also injects parent trace context into the spawn carrier when `propagator` is configured.\n * - `mcp.bootstrap <server>` — server bootstrap, opens on `mcp:bootstrap:start`, closes on `mcp:bootstrap:end`. Zidane-specific (no Sentry op equivalent).\n *\n * Span events (not spans — recorded on the active span via `addEvent`):\n *\n * - `gen_ai.gate.block` — a `tool:gate` listener refused a call.\n * - `gen_ai.validation.reject` — tool input failed schema validation.\n * - `gen_ai.validation.coerce` — tool input was auto-coerced to satisfy the schema.\n * - `gen_ai.budget.exceeded` — byte budget exceeded (`budget:exceeded`).\n * - `gen_ai.tool_budget.exceeded` — per-tool dispatch cap exceeded.\n * - `gen_ai.pairing.repair` — pre-send pairing repair fired.\n * - `gen_ai.stream.error` — provider stream errored (carries `statusCode` / `requestId`).\n * - `gen_ai.mcp.auth.required` — MCP server bootstrap blocked on missing OAuth tokens.\n * - `gen_ai.oauth.refresh` — provider OAuth credentials refreshed.\n *\n * Attribute naming follows the Sentry AI Agents conventions\n * (https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents/) by\n * default — which closely track OTel Gen AI semconv v1.36 with a few\n * intentional differences (`usage.input_tokens.cached` instead of\n * `usage.cache_read_input_tokens`, `cost.total_tokens` instead of\n * `usage.cost_usd`, seconds instead of ms for `time_to_first_token`).\n * Set `conventions: 'otel'` to emit only the vendor-neutral OTel keys, or\n * `'both'` to emit everything for maximum portability. Legacy attribute\n * names (`inputTokens`, `toolName`, `displayName`, …) are also emitted by\n * default for back-compat; set `legacyAttributes: false` to drop them.\n *\n * On `agent:done` or `uninstall()` it closes any spans still open (defensive —\n * normally all tool/turn spans have matching end hooks, but an abort\n * mid-execution can leave dangling ones).\n *\n * Returns an `install(hooks)` helper that registers every handler at once and\n * yields an `uninstall()` function for cleanup. Safe across multiple runs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { InterceptorRegistry } from './extensions/hooks'\nimport { agentExtensionWiringOf } from './agent-extension-wiring'\nimport { GEN_AI } from './tracing-attrs'\nimport { installTracingDecisionHooks } from './tracing-decision-hooks'\nimport { installTracingMcpServerHooks, installTracingMcpToolHooks } from './tracing-mcp-hooks'\nimport { installTracingAgentHooks, installTracingTurnHooks } from './tracing-run-hooks'\nimport { createTracingRuntime } from './tracing-runtime'\nimport { installTracingSpawnHooks } from './tracing-spawn-hooks'\nimport { installTracingPolicyEventHooks, installTracingToolHooks } from './tracing-tool-hooks'\n\n/** Minimal span shape — any tracer that provides these methods is compatible. */\nexport interface Span {\n /** Close the span. Called exactly once per span. */\n end: () => void\n /** Optional: attach additional attributes after the span is started (ignored if unsupported). */\n setAttributes?: (attrs: Record<string, unknown>) => void\n /**\n * Optional: record a structured event on the span. Used for non-span\n * occurrences (gate blocks, validation rejects, budget hits, pairing\n * repairs). Maps to OTel's `Span.addEvent` and Sentry's `addBreadcrumb`.\n * Tracers without an event surface should treat this as a no-op (the\n * helper falls back to `setAttributes` with an `event.*` key prefix).\n */\n addEvent?: (name: string, attrs?: Record<string, unknown>) => void\n}\n\n/**\n * Function that opens a span. Caller-provided so we stay tracer-agnostic.\n *\n * `parentContext` carries opaque trace metadata propagated from another\n * agent (typically a W3C `{ traceparent, tracestate }` carrier received on\n * `agent:start.tracingContext` after a parent agent's tracer injected it on\n * `spawn:before`). Implementations that integrate with OTel should extract\n * the carrier into a `Context` and use it as the parent when opening the\n * span; implementations that don't care about cross-agent linkage can\n * ignore the third argument entirely.\n */\nexport type StartSpan = (\n name: string,\n attrs?: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n) => Span\n\n/** Gen-AI attribute convention set emitted on spans: `'sentry'`, `'otel'` or `'both'`. */\nexport type TracingConventions = 'sentry' | 'otel' | 'both'\n\n/** Options for {@link createTracingHooks}. */\nexport interface TracingHooksOptions {\n /** Tracer seam. Receives a span name + attributes; must return a `Span`. */\n startSpan: StartSpan\n /**\n * Optional attribute namespace. Prepended to every span name with a `/`\n * separator (e.g. `\"agent\"` → `agent/chat <model>`, `agent/execute_tool Bash`).\n *\n * Empty / undefined = no prefix.\n */\n namespace?: string\n /**\n * Which Gen-AI attribute conventions to emit.\n *\n * - `'sentry'` (default) — Sentry AI Agents conventions. Most common\n * choice — Sentry, Datadog AI, Honeycomb derived dashboards and\n * Langfuse all consume these.\n * - `'otel'` — vendor-neutral OpenTelemetry Gen AI semconv v1.36.\n * Diverges from Sentry on cache-token keys, cost keys, and TTFT unit.\n * - `'both'` — emit every variant. Doubles the per-span attribute\n * payload; pick when shipping to multiple backends with different\n * ingestion conventions.\n */\n conventions?: TracingConventions\n /**\n * Capture the actual prompt / completion text on spans\n * (`gen_ai.input.messages`, `gen_ai.output.messages`,\n * `gen_ai.system_instructions`, `gen_ai.tool.definitions`,\n * `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result`).\n * `gen_ai.input.messages` keeps the newest messages that fit in 128 KB;\n * `gen_ai.agent.input_messages_dropped` counts the older ones left out.\n *\n * Defaults to `true` (required for the Sentry AI Agents conversation\n * viewer). Full prompts and tool arguments can carry secrets and PII;\n * captured content flows through `redact`, so pair the default with a\n * redactor when payloads may hold secrets, or set `false` to keep all\n * content-shaped data in-process.\n */\n captureMessageContent?: boolean\n /**\n * Emit legacy attribute keys (`inputTokens`, `outputTokens`, `toolName`,\n * `displayName`, `finishReason`, `modelId`) alongside the new\n * `gen_ai.*` semconv keys. Defaults to `true` so existing dashboards\n * keep working. Set `false` once you've migrated all consumers off the\n * legacy names.\n */\n legacyAttributes?: boolean\n /**\n * Error sink for tracer failures. Without it, tracer failures are swallowed\n * silently. Called when `Span.end()`, `setAttributes()`, `addEvent()`, or\n * `startSpan()` throws — so degraded tracers stay visible. The helper\n * still does not let tracer errors propagate into the agent loop.\n *\n * `kind` identifies which step failed (`'startSpan'`, `'end'`,\n * `'setAttributes'`, `'addEvent'`, `'redact'`, `'getActiveTraceContext'`).\n * Default: no-op (silent).\n */\n onError?: (kind: string, err: unknown) => void\n /**\n * Optional callback the helper invokes on `spawn:before` (after opening\n * the spawn span) to read the active trace context. Whatever it returns\n * is written into `ctx.tracingContext` and forwarded to the child's\n * `agent.run({ tracingContext })`. Typical use:\n *\n * ```ts\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * }\n * ```\n *\n * Returning `undefined` or an empty object leaves the carrier empty\n * and the child runs with no parent context. The helper's own\n * `propagator` is preferred over this when both are set (the\n * propagator runs inside the helper; this callback is the escape\n * hatch for hosts wanting full control).\n */\n getActiveTraceContext?: () => Readonly<Record<string, string>> | undefined\n /**\n * Optional in-process span redactor. When set, every potentially\n * sensitive string (tool input, tool result, system prompt) flows\n * through this fn before landing on a span attribute / event. Returning\n * the input unchanged is a no-op.\n *\n * Composes with the harness-wide `tracing:redact` hook: if a host has\n * already registered a hook handler that mutates `ctx.redacted`, the\n * tracer reuses that pipeline; setting `redact` here registers an extra\n * step that runs only for the tracer's attributes. The hook is the\n * canonical path; `redact` is the convenience surface for tracer-only\n * setups.\n */\n redact?: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n /**\n * The extension interceptors whose `tracing.redact` chain runs after\n * {@link redact}. Default: those of the agent whose hooks `install` gets\n * (`AgentOptions.extensionRuntime`); none for an agent without an extension runtime.\n */\n interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'>\n}\n\n/** Value returned by {@link createTracingHooks} — install entrypoint. */\nexport interface TracingHookSet {\n /**\n * Attach every hook handler to the given agent hooks instance.\n *\n * @returns an `uninstall` function that detaches every handler and closes any\n * still-open spans. Safe to call multiple times.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of tracing hook handlers that can be installed on an agent.\n *\n * @example Sentry\n * ```ts\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs) => Sentry.startInactiveSpan({ name, attributes: attrs }),\n * })\n * const uninstall = tracing.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @example OpenTelemetry with parent-context propagation\n * ```ts\n * import { trace, context, propagation } from '@opentelemetry/api'\n *\n * const tracer = trace.getTracer('zidane')\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs, parentCtx) => {\n * const ctx = parentCtx ? propagation.extract(context.active(), parentCtx) : context.active()\n * return tracer.startSpan(name, { attributes: attrs }, ctx)\n * },\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * },\n * })\n * ```\n *\n * @param options - Tracer seam and span options. `startSpan` is required.\n */\nexport function createTracingHooks(options: TracingHooksOptions): TracingHookSet {\n const namespace = options.namespace\n const legacy = options.legacyAttributes ?? true\n const conventions = options.conventions ?? 'sentry'\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const captureContent = options.captureMessageContent !== false\n const onError = options.onError ?? (() => {})\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const rt = createTracingRuntime(options, {\n namespace,\n legacy,\n conventions,\n wantSentry,\n captureContent,\n onError,\n ...interceptorsFor(options, hooks),\n })\n const { runSpans, turnSpans, toolSpans, mcpSpans, spawnSpans, bootstrapSpans, decisionSpans, endAll } = rt\n\n const unregisters: Array<() => void> = []\n\n installTracingAgentHooks(hooks, unregisters, rt)\n installTracingTurnHooks(hooks, unregisters, rt)\n installTracingToolHooks(hooks, unregisters, rt)\n installTracingPolicyEventHooks(hooks, unregisters, rt)\n installTracingMcpToolHooks(hooks, unregisters, rt)\n installTracingMcpServerHooks(hooks, unregisters, rt)\n installTracingSpawnHooks(hooks, unregisters, rt)\n installTracingDecisionHooks(hooks, unregisters, rt)\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n endAll(runSpans)\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n endAll(decisionSpans)\n rt.activeRunSpan = undefined\n }\n },\n }\n}\n\n/** The `tracing.redact` chain a tracer installed on `hooks` runs: the option's, else the agent's own. */\nfunction interceptorsFor(options: TracingHooksOptions, hooks: Hookable<AgentHooks>): { interceptors?: Pick<InterceptorRegistry, 'has' | 'runInterceptorsSync'> } {\n const interceptors = options.interceptors ?? agentExtensionWiringOf(hooks)?.extensionRuntime.interceptors\n return interceptors ? { interceptors } : {}\n}\n\n/**\n * OpenTelemetry Gen AI semantic-convention attribute keys used by the\n * built-in tracer. Exported so consumers integrating with raw OTel SDKs\n * (instead of going through `createTracingHooks`) can stay aligned with\n * the names the harness itself emits.\n */\nexport const GEN_AI_ATTRIBUTES = GEN_AI\n"],"mappings":"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"}