@senpi/runtime 3.0.6-dev.skill-manager-v2.20260629194505 → 3.0.9-dev.runtime-phase-2.20260707070820

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 (458) hide show
  1. package/dist/actions/action-events.d.ts +67 -0
  2. package/dist/actions/action-events.d.ts.map +1 -0
  3. package/dist/actions/action-events.js +164 -0
  4. package/dist/actions/action-events.js.map +1 -0
  5. package/dist/actions/close-position/close-position.action.d.ts +1 -1
  6. package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
  7. package/dist/actions/close-position/close-position.action.js +108 -86
  8. package/dist/actions/close-position/close-position.action.js.map +1 -1
  9. package/dist/actions/decision-engine.d.ts.map +1 -1
  10. package/dist/actions/decision-engine.js +40 -28
  11. package/dist/actions/decision-engine.js.map +1 -1
  12. package/dist/actions/event-derivation.d.ts +61 -0
  13. package/dist/actions/event-derivation.d.ts.map +1 -0
  14. package/dist/actions/event-derivation.js +180 -0
  15. package/dist/actions/event-derivation.js.map +1 -0
  16. package/dist/actions/llm-decision/anthropic-llm-decision.d.ts +5 -9
  17. package/dist/actions/llm-decision/anthropic-llm-decision.d.ts.map +1 -1
  18. package/dist/actions/llm-decision/anthropic-llm-decision.js +6 -87
  19. package/dist/actions/llm-decision/anthropic-llm-decision.js.map +1 -1
  20. package/dist/actions/llm-decision/factory.d.ts +0 -6
  21. package/dist/actions/llm-decision/factory.d.ts.map +1 -1
  22. package/dist/actions/llm-decision/factory.js +0 -2
  23. package/dist/actions/llm-decision/factory.js.map +1 -1
  24. package/dist/actions/llm-decision/openclaw-llm-decision.d.ts.map +1 -1
  25. package/dist/actions/llm-decision/openclaw-llm-decision.js +26 -7
  26. package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
  27. package/dist/actions/open-position/open-position.action.d.ts +1 -1
  28. package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
  29. package/dist/actions/open-position/open-position.action.js +282 -224
  30. package/dist/actions/open-position/open-position.action.js.map +1 -1
  31. package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
  32. package/dist/actions/position-tracker/position-tracker.action.js +3 -6
  33. package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
  34. package/dist/cli/guide-commands.d.ts.map +1 -1
  35. package/dist/cli/guide-commands.js +61 -0
  36. package/dist/cli/guide-commands.js.map +1 -1
  37. package/dist/cli/senpi-commands.d.ts +14 -0
  38. package/dist/cli/senpi-commands.d.ts.map +1 -1
  39. package/dist/cli/senpi-commands.js +390 -2
  40. package/dist/cli/senpi-commands.js.map +1 -1
  41. package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
  42. package/dist/dsl/engine-client/event-mapping.js +0 -3
  43. package/dist/dsl/engine-client/event-mapping.js.map +1 -1
  44. package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
  45. package/dist/dsl/events/build-closed-event.js +0 -1
  46. package/dist/dsl/events/build-closed-event.js.map +1 -1
  47. package/dist/dsl/events/handlers.d.ts.map +1 -1
  48. package/dist/dsl/events/handlers.js +2 -4
  49. package/dist/dsl/events/handlers.js.map +1 -1
  50. package/dist/dsl/events/initial-state.d.ts.map +1 -1
  51. package/dist/dsl/events/initial-state.js +1 -0
  52. package/dist/dsl/events/initial-state.js.map +1 -1
  53. package/dist/dsl/monitor/create-monitor.d.ts +3 -0
  54. package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
  55. package/dist/dsl/monitor/create-monitor.js +76 -58
  56. package/dist/dsl/monitor/create-monitor.js.map +1 -1
  57. package/dist/dsl/monitor/dsl-failure-events.d.ts +42 -0
  58. package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -0
  59. package/dist/dsl/monitor/dsl-failure-events.js +73 -0
  60. package/dist/dsl/monitor/dsl-failure-events.js.map +1 -0
  61. package/dist/dsl/monitor/process-one-position.d.ts +2 -1
  62. package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
  63. package/dist/dsl/monitor/process-one-position.js +130 -196
  64. package/dist/dsl/monitor/process-one-position.js.map +1 -1
  65. package/dist/dsl/plugin/index.d.ts +10 -0
  66. package/dist/dsl/plugin/index.d.ts.map +1 -1
  67. package/dist/dsl/plugin/index.js +48 -4
  68. package/dist/dsl/plugin/index.js.map +1 -1
  69. package/dist/dsl/plugin/options.d.ts +3 -0
  70. package/dist/dsl/plugin/options.d.ts.map +1 -1
  71. package/dist/health/types.d.ts +14 -0
  72. package/dist/health/types.d.ts.map +1 -1
  73. package/dist/health/types.js.map +1 -1
  74. package/dist/index.d.ts +1 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +255 -77
  77. package/dist/index.js.map +1 -1
  78. package/dist/risk/risk-audit-log.d.ts +7 -0
  79. package/dist/risk/risk-audit-log.d.ts.map +1 -1
  80. package/dist/risk/risk-audit-log.js +48 -1
  81. package/dist/risk/risk-audit-log.js.map +1 -1
  82. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts +2 -0
  83. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts.map +1 -1
  84. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js +24 -3
  85. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js.map +1 -1
  86. package/dist/runtime/auto-update/coordinator.js +2 -2
  87. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  88. package/dist/runtime/create-actions.d.ts.map +1 -1
  89. package/dist/runtime/create-actions.js +40 -15
  90. package/dist/runtime/create-actions.js.map +1 -1
  91. package/dist/runtime/create-runtime-context.d.ts +1 -1
  92. package/dist/runtime/create-runtime-context.d.ts.map +1 -1
  93. package/dist/runtime/create-scanner-module.d.ts +2 -1
  94. package/dist/runtime/create-scanner-module.d.ts.map +1 -1
  95. package/dist/runtime/create-scanner-module.js +2 -1
  96. package/dist/runtime/create-scanner-module.js.map +1 -1
  97. package/dist/runtime/domain-events.d.ts +43 -0
  98. package/dist/runtime/domain-events.d.ts.map +1 -0
  99. package/dist/runtime/domain-events.js +181 -0
  100. package/dist/runtime/domain-events.js.map +1 -0
  101. package/dist/runtime/lifecycle-events.d.ts +86 -0
  102. package/dist/runtime/lifecycle-events.d.ts.map +1 -0
  103. package/dist/runtime/lifecycle-events.js +200 -0
  104. package/dist/runtime/lifecycle-events.js.map +1 -0
  105. package/dist/runtime/notifications.js +2 -2
  106. package/dist/runtime/notifications.js.map +1 -1
  107. package/dist/{tracking → runtime}/position-id-registry.d.ts +14 -1
  108. package/dist/runtime/position-id-registry.d.ts.map +1 -0
  109. package/dist/{tracking → runtime}/position-id-registry.js +20 -3
  110. package/dist/runtime/position-id-registry.js.map +1 -0
  111. package/dist/runtime/run.d.ts +2 -4
  112. package/dist/runtime/run.d.ts.map +1 -1
  113. package/dist/runtime/run.js.map +1 -1
  114. package/dist/runtime/runtime-schema.d.ts +276 -588
  115. package/dist/runtime/runtime-schema.d.ts.map +1 -1
  116. package/dist/runtime/runtime-schema.js +8 -28
  117. package/dist/runtime/runtime-schema.js.map +1 -1
  118. package/dist/runtime/runtime.d.ts +23 -9
  119. package/dist/runtime/runtime.d.ts.map +1 -1
  120. package/dist/runtime/runtime.js +276 -248
  121. package/dist/runtime/runtime.js.map +1 -1
  122. package/dist/runtime/strategy-lifecycle.d.ts.map +1 -1
  123. package/dist/runtime/strategy-lifecycle.js +13 -1
  124. package/dist/runtime/strategy-lifecycle.js.map +1 -1
  125. package/dist/runtime/supervisor/long-running-supervisor.d.ts +3 -0
  126. package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
  127. package/dist/runtime/supervisor/long-running-supervisor.js +27 -53
  128. package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
  129. package/dist/runtime-api/intake/intake-events.d.ts +44 -0
  130. package/dist/runtime-api/intake/intake-events.d.ts.map +1 -0
  131. package/dist/runtime-api/intake/intake-events.js +131 -0
  132. package/dist/runtime-api/intake/intake-events.js.map +1 -0
  133. package/dist/runtime-api/intake/scanner-launcher.d.ts +2 -0
  134. package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
  135. package/dist/runtime-api/intake/scanner-launcher.js +58 -15
  136. package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
  137. package/dist/runtime-api/intake/scanner-registry.d.ts +3 -0
  138. package/dist/runtime-api/intake/scanner-registry.d.ts.map +1 -1
  139. package/dist/runtime-api/intake/scanner-registry.js +3 -0
  140. package/dist/runtime-api/intake/scanner-registry.js.map +1 -1
  141. package/dist/runtime-api/intake/supervisor-events.d.ts +61 -0
  142. package/dist/runtime-api/intake/supervisor-events.d.ts.map +1 -0
  143. package/dist/runtime-api/intake/supervisor-events.js +173 -0
  144. package/dist/runtime-api/intake/supervisor-events.js.map +1 -0
  145. package/dist/runtime-api/routes/signals.d.ts +1 -1
  146. package/dist/runtime-api/routes/signals.d.ts.map +1 -1
  147. package/dist/runtime-api/routes/signals.js +121 -35
  148. package/dist/runtime-api/routes/signals.js.map +1 -1
  149. package/dist/runtime-api/server.d.ts.map +1 -1
  150. package/dist/runtime-api/server.js +10 -1
  151. package/dist/runtime-api/server.js.map +1 -1
  152. package/dist/scanners/engine/data-providers.d.ts +3 -0
  153. package/dist/scanners/engine/data-providers.d.ts.map +1 -1
  154. package/dist/scanners/engine/data-providers.js.map +1 -1
  155. package/dist/scanners/engine/engine.d.ts +18 -0
  156. package/dist/scanners/engine/engine.d.ts.map +1 -1
  157. package/dist/scanners/engine/engine.js +96 -2
  158. package/dist/scanners/engine/engine.js.map +1 -1
  159. package/dist/scanners/engine/input-resolver.d.ts.map +1 -1
  160. package/dist/scanners/engine/input-resolver.js +9 -7
  161. package/dist/scanners/engine/input-resolver.js.map +1 -1
  162. package/dist/scanners/runtime-module.d.ts +3 -0
  163. package/dist/scanners/runtime-module.d.ts.map +1 -1
  164. package/dist/scanners/runtime-module.js +7 -0
  165. package/dist/scanners/runtime-module.js.map +1 -1
  166. package/dist/senpi/client.d.ts +10 -0
  167. package/dist/senpi/client.d.ts.map +1 -1
  168. package/dist/senpi/client.js +113 -46
  169. package/dist/senpi/client.js.map +1 -1
  170. package/dist/skills-manager/bootstrap.d.ts.map +1 -1
  171. package/dist/skills-manager/bootstrap.js +22 -33
  172. package/dist/skills-manager/bootstrap.js.map +1 -1
  173. package/dist/skills-manager/clone-groups.d.ts +20 -0
  174. package/dist/skills-manager/clone-groups.d.ts.map +1 -0
  175. package/dist/skills-manager/clone-groups.js +45 -0
  176. package/dist/skills-manager/clone-groups.js.map +1 -0
  177. package/dist/skills-manager/coordinator.d.ts.map +1 -1
  178. package/dist/skills-manager/coordinator.js +111 -75
  179. package/dist/skills-manager/coordinator.js.map +1 -1
  180. package/dist/skills-manager/manifest.d.ts +24 -0
  181. package/dist/skills-manager/manifest.d.ts.map +1 -1
  182. package/dist/skills-manager/manifest.js +23 -10
  183. package/dist/skills-manager/manifest.js.map +1 -1
  184. package/dist/skills-manager/skills-manifest.json +12 -7
  185. package/dist/skills-manager/types.d.ts +12 -0
  186. package/dist/skills-manager/types.d.ts.map +1 -1
  187. package/dist/state/state-events.d.ts +13 -0
  188. package/dist/state/state-events.d.ts.map +1 -0
  189. package/dist/state/state-events.js +22 -0
  190. package/dist/state/state-events.js.map +1 -0
  191. package/dist/state/state-manager.d.ts.map +1 -1
  192. package/dist/state/state-manager.js +14 -1
  193. package/dist/state/state-manager.js.map +1 -1
  194. package/dist/telemetry/attributes.d.ts +13 -0
  195. package/dist/telemetry/attributes.d.ts.map +1 -0
  196. package/dist/telemetry/attributes.js +22 -0
  197. package/dist/telemetry/attributes.js.map +1 -0
  198. package/dist/{tracking → telemetry}/context.d.ts +12 -5
  199. package/dist/telemetry/context.d.ts.map +1 -0
  200. package/dist/{tracking → telemetry}/context.js +4 -4
  201. package/dist/telemetry/context.js.map +1 -0
  202. package/dist/{recording/records.d.ts → telemetry/event-builders.d.ts} +145 -33
  203. package/dist/telemetry/event-builders.d.ts.map +1 -0
  204. package/dist/{recording/wiring/record-builders.js → telemetry/event-builders.js} +94 -40
  205. package/dist/telemetry/event-builders.js.map +1 -0
  206. package/dist/telemetry/event-store.d.ts +66 -0
  207. package/dist/telemetry/event-store.d.ts.map +1 -0
  208. package/dist/telemetry/event-store.js +202 -0
  209. package/dist/telemetry/event-store.js.map +1 -0
  210. package/dist/telemetry/identity.d.ts +72 -0
  211. package/dist/telemetry/identity.d.ts.map +1 -0
  212. package/dist/{recording/wiring → telemetry}/identity.js +6 -2
  213. package/dist/telemetry/identity.js.map +1 -0
  214. package/dist/telemetry/metrics.d.ts +169 -0
  215. package/dist/telemetry/metrics.d.ts.map +1 -0
  216. package/dist/telemetry/metrics.js +437 -0
  217. package/dist/telemetry/metrics.js.map +1 -0
  218. package/dist/telemetry/otel-sdk.d.ts +93 -0
  219. package/dist/telemetry/otel-sdk.d.ts.map +1 -0
  220. package/dist/telemetry/otel-sdk.js +297 -0
  221. package/dist/telemetry/otel-sdk.js.map +1 -0
  222. package/dist/telemetry/spans/constants.d.ts +12 -0
  223. package/dist/telemetry/spans/constants.d.ts.map +1 -0
  224. package/dist/telemetry/spans/constants.js +12 -0
  225. package/dist/telemetry/spans/constants.js.map +1 -0
  226. package/dist/telemetry/spans/gen-ai.d.ts +74 -0
  227. package/dist/telemetry/spans/gen-ai.d.ts.map +1 -0
  228. package/dist/telemetry/spans/gen-ai.js +339 -0
  229. package/dist/telemetry/spans/gen-ai.js.map +1 -0
  230. package/dist/telemetry/spans/index.d.ts +11 -0
  231. package/dist/telemetry/spans/index.d.ts.map +1 -0
  232. package/dist/telemetry/spans/index.js +11 -0
  233. package/dist/telemetry/spans/index.js.map +1 -0
  234. package/dist/{tracking → telemetry}/spans/redact-pii.d.ts +19 -21
  235. package/dist/telemetry/spans/redact-pii.d.ts.map +1 -0
  236. package/dist/{tracking → telemetry}/spans/redact-pii.js +35 -32
  237. package/dist/telemetry/spans/redact-pii.js.map +1 -0
  238. package/dist/telemetry/spans/sanitize-payload.d.ts.map +1 -0
  239. package/dist/telemetry/spans/sanitize-payload.js.map +1 -0
  240. package/dist/telemetry/spans/span-hooks.d.ts +18 -0
  241. package/dist/telemetry/spans/span-hooks.d.ts.map +1 -0
  242. package/dist/telemetry/spans/span-hooks.js +764 -0
  243. package/dist/telemetry/spans/span-hooks.js.map +1 -0
  244. package/dist/telemetry/tool-plane.d.ts +31 -0
  245. package/dist/telemetry/tool-plane.d.ts.map +1 -0
  246. package/dist/telemetry/tool-plane.js +70 -0
  247. package/dist/telemetry/tool-plane.js.map +1 -0
  248. package/dist/telemetry/trace-identity.d.ts +32 -0
  249. package/dist/telemetry/trace-identity.d.ts.map +1 -0
  250. package/dist/telemetry/trace-identity.js +62 -0
  251. package/dist/telemetry/trace-identity.js.map +1 -0
  252. package/dist/telemetry/tracing.d.ts +33 -0
  253. package/dist/telemetry/tracing.d.ts.map +1 -0
  254. package/dist/telemetry/tracing.js +70 -0
  255. package/dist/telemetry/tracing.js.map +1 -0
  256. package/dist/types/dsl/index.d.ts +8 -9
  257. package/dist/types/dsl/index.d.ts.map +1 -1
  258. package/dist/types/runtime.d.ts +1 -1
  259. package/dist/types/runtime.d.ts.map +1 -1
  260. package/dist/types/scanner.d.ts +2 -0
  261. package/dist/types/scanner.d.ts.map +1 -1
  262. package/dist/utils/event-catalog.d.ts +506 -0
  263. package/dist/utils/event-catalog.d.ts.map +1 -0
  264. package/dist/utils/event-catalog.js +142 -0
  265. package/dist/utils/event-catalog.js.map +1 -0
  266. package/dist/utils/logger.d.ts +63 -5
  267. package/dist/utils/logger.d.ts.map +1 -1
  268. package/dist/utils/logger.js +360 -27
  269. package/dist/utils/logger.js.map +1 -1
  270. package/dist/utils/wallet-address.d.ts +3 -0
  271. package/dist/utils/wallet-address.d.ts.map +1 -0
  272. package/dist/utils/wallet-address.js +17 -0
  273. package/dist/utils/wallet-address.js.map +1 -0
  274. package/openclaw.plugin.json +20 -6
  275. package/package.json +10 -2
  276. package/dist/actions/tracking-helpers.d.ts +0 -28
  277. package/dist/actions/tracking-helpers.d.ts.map +0 -1
  278. package/dist/actions/tracking-helpers.js +0 -93
  279. package/dist/actions/tracking-helpers.js.map +0 -1
  280. package/dist/recording/emitter.d.ts +0 -27
  281. package/dist/recording/emitter.d.ts.map +0 -1
  282. package/dist/recording/emitter.js +0 -2
  283. package/dist/recording/emitter.js.map +0 -1
  284. package/dist/recording/identity.d.ts +0 -34
  285. package/dist/recording/identity.d.ts.map +0 -1
  286. package/dist/recording/identity.js +0 -2
  287. package/dist/recording/identity.js.map +0 -1
  288. package/dist/recording/index.d.ts +0 -11
  289. package/dist/recording/index.d.ts.map +0 -1
  290. package/dist/recording/index.js +0 -2
  291. package/dist/recording/index.js.map +0 -1
  292. package/dist/recording/noop-emitter.d.ts +0 -23
  293. package/dist/recording/noop-emitter.d.ts.map +0 -1
  294. package/dist/recording/noop-emitter.js +0 -22
  295. package/dist/recording/noop-emitter.js.map +0 -1
  296. package/dist/recording/otel/attributes.d.ts +0 -35
  297. package/dist/recording/otel/attributes.d.ts.map +0 -1
  298. package/dist/recording/otel/attributes.js +0 -131
  299. package/dist/recording/otel/attributes.js.map +0 -1
  300. package/dist/recording/otel/constants.d.ts +0 -123
  301. package/dist/recording/otel/constants.d.ts.map +0 -1
  302. package/dist/recording/otel/constants.js +0 -172
  303. package/dist/recording/otel/constants.js.map +0 -1
  304. package/dist/recording/otel/ids.d.ts +0 -25
  305. package/dist/recording/otel/ids.d.ts.map +0 -1
  306. package/dist/recording/otel/ids.js +0 -37
  307. package/dist/recording/otel/ids.js.map +0 -1
  308. package/dist/recording/otel/index.d.ts +0 -13
  309. package/dist/recording/otel/index.d.ts.map +0 -1
  310. package/dist/recording/otel/index.js +0 -9
  311. package/dist/recording/otel/index.js.map +0 -1
  312. package/dist/recording/otel/mappers.d.ts +0 -100
  313. package/dist/recording/otel/mappers.d.ts.map +0 -1
  314. package/dist/recording/otel/mappers.js +0 -683
  315. package/dist/recording/otel/mappers.js.map +0 -1
  316. package/dist/recording/otel/otel-emitter.d.ts +0 -43
  317. package/dist/recording/otel/otel-emitter.d.ts.map +0 -1
  318. package/dist/recording/otel/otel-emitter.js +0 -99
  319. package/dist/recording/otel/otel-emitter.js.map +0 -1
  320. package/dist/recording/otel/redaction.d.ts +0 -47
  321. package/dist/recording/otel/redaction.d.ts.map +0 -1
  322. package/dist/recording/otel/redaction.js +0 -88
  323. package/dist/recording/otel/redaction.js.map +0 -1
  324. package/dist/recording/records.d.ts.map +0 -1
  325. package/dist/recording/records.js +0 -14
  326. package/dist/recording/records.js.map +0 -1
  327. package/dist/recording/transport/batcher.d.ts +0 -59
  328. package/dist/recording/transport/batcher.d.ts.map +0 -1
  329. package/dist/recording/transport/batcher.js +0 -234
  330. package/dist/recording/transport/batcher.js.map +0 -1
  331. package/dist/recording/transport/client.d.ts +0 -41
  332. package/dist/recording/transport/client.d.ts.map +0 -1
  333. package/dist/recording/transport/client.js +0 -69
  334. package/dist/recording/transport/client.js.map +0 -1
  335. package/dist/recording/transport/config.d.ts +0 -53
  336. package/dist/recording/transport/config.d.ts.map +0 -1
  337. package/dist/recording/transport/config.js +0 -41
  338. package/dist/recording/transport/config.js.map +0 -1
  339. package/dist/recording/transport/index.d.ts +0 -16
  340. package/dist/recording/transport/index.d.ts.map +0 -1
  341. package/dist/recording/transport/index.js +0 -12
  342. package/dist/recording/transport/index.js.map +0 -1
  343. package/dist/recording/transport/rows.d.ts +0 -88
  344. package/dist/recording/transport/rows.d.ts.map +0 -1
  345. package/dist/recording/transport/rows.js +0 -15
  346. package/dist/recording/transport/rows.js.map +0 -1
  347. package/dist/recording/wiring/emitter-factory.d.ts +0 -39
  348. package/dist/recording/wiring/emitter-factory.d.ts.map +0 -1
  349. package/dist/recording/wiring/emitter-factory.js +0 -62
  350. package/dist/recording/wiring/emitter-factory.js.map +0 -1
  351. package/dist/recording/wiring/identity.d.ts +0 -27
  352. package/dist/recording/wiring/identity.d.ts.map +0 -1
  353. package/dist/recording/wiring/identity.js.map +0 -1
  354. package/dist/recording/wiring/index.d.ts +0 -12
  355. package/dist/recording/wiring/index.d.ts.map +0 -1
  356. package/dist/recording/wiring/index.js +0 -9
  357. package/dist/recording/wiring/index.js.map +0 -1
  358. package/dist/recording/wiring/record-builders.d.ts +0 -107
  359. package/dist/recording/wiring/record-builders.d.ts.map +0 -1
  360. package/dist/recording/wiring/record-builders.js.map +0 -1
  361. package/dist/recording/wiring/subscriber.d.ts +0 -28
  362. package/dist/recording/wiring/subscriber.d.ts.map +0 -1
  363. package/dist/recording/wiring/subscriber.js +0 -173
  364. package/dist/recording/wiring/subscriber.js.map +0 -1
  365. package/dist/recording/wiring/values.d.ts +0 -19
  366. package/dist/recording/wiring/values.d.ts.map +0 -1
  367. package/dist/recording/wiring/values.js +0 -49
  368. package/dist/recording/wiring/values.js.map +0 -1
  369. package/dist/tracking/build-rehydrate-items.d.ts +0 -15
  370. package/dist/tracking/build-rehydrate-items.d.ts.map +0 -1
  371. package/dist/tracking/build-rehydrate-items.js +0 -19
  372. package/dist/tracking/build-rehydrate-items.js.map +0 -1
  373. package/dist/tracking/constants.d.ts +0 -24
  374. package/dist/tracking/constants.d.ts.map +0 -1
  375. package/dist/tracking/constants.js +0 -52
  376. package/dist/tracking/constants.js.map +0 -1
  377. package/dist/tracking/context.d.ts.map +0 -1
  378. package/dist/tracking/context.js.map +0 -1
  379. package/dist/tracking/index.d.ts +0 -16
  380. package/dist/tracking/index.d.ts.map +0 -1
  381. package/dist/tracking/index.js +0 -11
  382. package/dist/tracking/index.js.map +0 -1
  383. package/dist/tracking/plugin-event-tracker.d.ts +0 -13
  384. package/dist/tracking/plugin-event-tracker.d.ts.map +0 -1
  385. package/dist/tracking/plugin-event-tracker.js +0 -27
  386. package/dist/tracking/plugin-event-tracker.js.map +0 -1
  387. package/dist/tracking/position-id-registry.d.ts.map +0 -1
  388. package/dist/tracking/position-id-registry.js.map +0 -1
  389. package/dist/tracking/spans/constants.d.ts +0 -19
  390. package/dist/tracking/spans/constants.d.ts.map +0 -1
  391. package/dist/tracking/spans/constants.js +0 -31
  392. package/dist/tracking/spans/constants.js.map +0 -1
  393. package/dist/tracking/spans/fan-out-span-transport.d.ts +0 -22
  394. package/dist/tracking/spans/fan-out-span-transport.d.ts.map +0 -1
  395. package/dist/tracking/spans/fan-out-span-transport.js +0 -49
  396. package/dist/tracking/spans/fan-out-span-transport.js.map +0 -1
  397. package/dist/tracking/spans/index.d.ts +0 -13
  398. package/dist/tracking/spans/index.d.ts.map +0 -1
  399. package/dist/tracking/spans/index.js +0 -16
  400. package/dist/tracking/spans/index.js.map +0 -1
  401. package/dist/tracking/spans/otel/fit-row-budget.d.ts +0 -22
  402. package/dist/tracking/spans/otel/fit-row-budget.d.ts.map +0 -1
  403. package/dist/tracking/spans/otel/fit-row-budget.js +0 -91
  404. package/dist/tracking/spans/otel/fit-row-budget.js.map +0 -1
  405. package/dist/tracking/spans/otel/map-span-to-trace-row.d.ts +0 -16
  406. package/dist/tracking/spans/otel/map-span-to-trace-row.d.ts.map +0 -1
  407. package/dist/tracking/spans/otel/map-span-to-trace-row.js +0 -227
  408. package/dist/tracking/spans/otel/map-span-to-trace-row.js.map +0 -1
  409. package/dist/tracking/spans/otel/recording-span-transport.d.ts +0 -43
  410. package/dist/tracking/spans/otel/recording-span-transport.d.ts.map +0 -1
  411. package/dist/tracking/spans/otel/recording-span-transport.js +0 -67
  412. package/dist/tracking/spans/otel/recording-span-transport.js.map +0 -1
  413. package/dist/tracking/spans/otel/span-ids.d.ts +0 -22
  414. package/dist/tracking/spans/otel/span-ids.d.ts.map +0 -1
  415. package/dist/tracking/spans/otel/span-ids.js +0 -32
  416. package/dist/tracking/spans/otel/span-ids.js.map +0 -1
  417. package/dist/tracking/spans/plugin-span-tracker.d.ts +0 -9
  418. package/dist/tracking/spans/plugin-span-tracker.d.ts.map +0 -1
  419. package/dist/tracking/spans/plugin-span-tracker.js +0 -115
  420. package/dist/tracking/spans/plugin-span-tracker.js.map +0 -1
  421. package/dist/tracking/spans/redact-pii.d.ts.map +0 -1
  422. package/dist/tracking/spans/redact-pii.js.map +0 -1
  423. package/dist/tracking/spans/sanitize-payload.d.ts.map +0 -1
  424. package/dist/tracking/spans/sanitize-payload.js.map +0 -1
  425. package/dist/tracking/spans/span-hooks.d.ts +0 -28
  426. package/dist/tracking/spans/span-hooks.d.ts.map +0 -1
  427. package/dist/tracking/spans/span-hooks.js +0 -409
  428. package/dist/tracking/spans/span-hooks.js.map +0 -1
  429. package/dist/tracking/spans/span-tracker.d.ts +0 -133
  430. package/dist/tracking/spans/span-tracker.d.ts.map +0 -1
  431. package/dist/tracking/spans/span-tracker.js +0 -721
  432. package/dist/tracking/spans/span-tracker.js.map +0 -1
  433. package/dist/tracking/spans/span-transport.d.ts +0 -71
  434. package/dist/tracking/spans/span-transport.d.ts.map +0 -1
  435. package/dist/tracking/spans/span-transport.js +0 -240
  436. package/dist/tracking/spans/span-transport.js.map +0 -1
  437. package/dist/tracking/spans/types.d.ts +0 -101
  438. package/dist/tracking/spans/types.d.ts.map +0 -1
  439. package/dist/tracking/spans/types.js +0 -6
  440. package/dist/tracking/spans/types.js.map +0 -1
  441. package/dist/tracking/tracker.d.ts +0 -30
  442. package/dist/tracking/tracker.d.ts.map +0 -1
  443. package/dist/tracking/tracker.js +0 -66
  444. package/dist/tracking/tracker.js.map +0 -1
  445. package/dist/tracking/tracking-handler.d.ts +0 -12
  446. package/dist/tracking/tracking-handler.d.ts.map +0 -1
  447. package/dist/tracking/tracking-handler.js +0 -148
  448. package/dist/tracking/tracking-handler.js.map +0 -1
  449. package/dist/tracking/transport.d.ts +0 -35
  450. package/dist/tracking/transport.d.ts.map +0 -1
  451. package/dist/tracking/transport.js +0 -133
  452. package/dist/tracking/transport.js.map +0 -1
  453. package/dist/tracking/types.d.ts +0 -21
  454. package/dist/tracking/types.d.ts.map +0 -1
  455. package/dist/tracking/types.js +0 -6
  456. package/dist/tracking/types.js.map +0 -1
  457. /package/dist/{tracking → telemetry}/spans/sanitize-payload.d.ts +0 -0
  458. /package/dist/{tracking → telemetry}/spans/sanitize-payload.js +0 -0
@@ -0,0 +1,764 @@
1
+ /**
2
+ * registerSpanHooks: wires the OpenClaw plugin hooks (llm_input/llm_output,
3
+ * model_call_ended, before/after_tool_call, the four subagent_* hooks, agent_end) to real
4
+ * OTel gen_ai spans via the shared `getTracer()`:
5
+ * - llm_input → llm_output → `chat` (CLIENT)
6
+ * - model_call_ended → outcome/failure on the `chat` span (the only failed-turn signal)
7
+ * - tool_call → `execute_tool` (INTERNAL), parented to the model span
8
+ * - subagent_* lifecycle → `invoke_agent` (INTERNAL), parented to the model span
9
+ * - agent_end → ends any still-open spans for the run
10
+ *
11
+ * Open and finalize are separate callbacks, so we can't use `withSpan`; we `startSpan`
12
+ * and hold the handle in a session-keyed registry until the finalize hook, recording
13
+ * errors directly on the held span (it's never the active span, so `logger.error` won't).
14
+ *
15
+ * Identity rides as `senpi.*` attributes from the `X-Senpi-*` headers (ALS doesn't cross
16
+ * the loopback fetch). The trading trace is joined by the `traceparent` header when present
17
+ * (nesting under `signal.process`), else by a `senpi.run.id` attribute. Handlers
18
+ * never throw.
19
+ */
20
+ import { SpanKind, context as otelContext, propagation, trace, } from "@opentelemetry/api";
21
+ import { getTracer } from "../../telemetry/tracing.js";
22
+ import { HEADER_ADDRESS, HEADER_CORRELATION_ID, HEADER_RECIPE_NAME, HEADER_RUNTIME_ID, HEADER_RUNTIME_VERSION, } from "./constants.js";
23
+ import { GEN_AI, errorTypeOf, firstNumber, normalizeGenAiMessages, recordSpanError, setDefinedAttributes, setGenAiContentAttribute, toFinishReasons, } from "./gen-ai.js";
24
+ import { elapsedSeconds, recordToolCall, recordInvokeAgentDuration, recordOperationDuration, recordTokenUsage, } from "../../telemetry/metrics.js";
25
+ import { emitToolCalled, offeredToolNames, requestedToolNames, } from "../../telemetry/tool-plane.js";
26
+ import { createLogger } from "../../utils/logger.js";
27
+ const logger = createLogger("[senpi-core][span-hooks]");
28
+ /**
29
+ * Grace window the `agent_end` sweep holds a finished model span before ending it, so a
30
+ * trailing `llm_output` (observed ~1 ms later in the codex/harness flow, but timed generously)
31
+ * can finalize it with token usage + output messages first. Unref'd, so it never holds the
32
+ * process open; an orphan run with no `llm_output` lets the timer end the span.
33
+ */
34
+ export const MODEL_SPAN_END_GRACE_MS = 2000;
35
+ export function registerSpanHooks(options) {
36
+ const { api } = options;
37
+ const tracer = getTracer();
38
+ // Cap on drift events per turn so a flood of invented tool calls can't balloon the log.
39
+ const MAX_DRIFT_EVENTS_PER_TURN = 8;
40
+ // Held model spans, keyed by (sessionKey, runId). The tool/subagent spans parent
41
+ // off the model span for the same session/run.
42
+ const modelSpans = new Map();
43
+ // Held tool spans, keyed by (sessionKey, runId, toolCallId|toolName seq).
44
+ const toolSpans = new Map();
45
+ // Held subagent spans, keyed by childSessionKey (sessionKey/runId = the host).
46
+ const subagentSpans = new Map();
47
+ let toolSeq = 0;
48
+ // Tool hooks often arrive with a ctx that lacks sessionKey/runId. Remember
49
+ // (agentId → sessionKey/runId) from llm_input, plus the last-seen pair, so tool
50
+ // hooks can resolve their host session.
51
+ const sessionByAgentId = new Map();
52
+ let lastActive = null;
53
+ const rememberSession = (agentCtx, sessionKey, runId) => {
54
+ const agentId = nonEmptyString(asRecord(agentCtx)?.agentId);
55
+ if (agentId)
56
+ sessionByAgentId.set(agentId, { sessionKey, runId });
57
+ lastActive = { sessionKey, runId };
58
+ };
59
+ const resolveToolSession = (ctx) => {
60
+ const direct = {
61
+ sessionKey: readSessionKey(ctx, undefined),
62
+ runId: readRunId(ctx, undefined),
63
+ };
64
+ if (direct.sessionKey && direct.runId) {
65
+ return { sessionKey: direct.sessionKey, runId: direct.runId };
66
+ }
67
+ const agentId = nonEmptyString(asRecord(ctx)?.agentId);
68
+ if (agentId) {
69
+ const hit = sessionByAgentId.get(agentId);
70
+ if (hit)
71
+ return hit;
72
+ }
73
+ // Last resort: the most-recent session. Only safe when unambiguous — two or more
74
+ // model spans open at once means concurrent runs share this process and `lastActive`
75
+ // could belong to either, so drop the span rather than misattribute it across runs.
76
+ return modelSpans.size <= 1 ? lastActive : null;
77
+ };
78
+ /** Build a parent Context for tool/subagent spans from the host model span (if any). */
79
+ const parentContextForModel = (sessionKey, runId) => {
80
+ const model = modelSpans.get(sessionLlmKey(sessionKey, runId));
81
+ return model ? trace.setSpan(otelContext.active(), model.span) : otelContext.active();
82
+ };
83
+ // ─── LLM (`chat` span) ───────────────────────────────────────────────────
84
+ api.on("llm_input", (event, agentCtx) => {
85
+ try {
86
+ const identity = readIdentity(agentCtx);
87
+ const sessionKey = readSessionKey(agentCtx, event);
88
+ const runId = readRunId(agentCtx, event);
89
+ if (!sessionKey || !runId) {
90
+ logger.warn("llm_input missing sessionKey/runId; skipping span");
91
+ return;
92
+ }
93
+ rememberSession(agentCtx, sessionKey, runId);
94
+ const key = sessionLlmKey(sessionKey, runId);
95
+ // A duplicate llm_input for the same (session, run) means a new turn opened
96
+ // without the previous one closing — end the prior span before replacing it.
97
+ const prior = modelSpans.get(key);
98
+ if (prior)
99
+ endSpan(prior.span);
100
+ const ev = asRecord(event) ?? {};
101
+ const requestModel = nonEmptyString(ev.model);
102
+ const provider = nonEmptyString(ev.provider);
103
+ const span = tracer.startSpan(`chat ${requestModel ?? "unknown"}`, { kind: SpanKind.CLIENT },
104
+ // Parent off the trading trace when the traceparent header threaded through.
105
+ parentContextFromHeaders(agentCtx));
106
+ const held = {
107
+ span,
108
+ sessionKey,
109
+ runId,
110
+ startMs: Date.now(),
111
+ provider,
112
+ model: requestModel,
113
+ // Offered tools — the baseline for the llm_output drift diff. Set only when definitions
114
+ // were delivered (undefined → no diff, i.e. conversation access off); an empty set is
115
+ // authoritative (zero tools offered, so any requested tool is drift).
116
+ offeredTools: Array.isArray(ev.tools) ? offeredToolNames(ev.tools) : undefined,
117
+ };
118
+ modelSpans.set(key, held);
119
+ setDefinedAttributes(span, {
120
+ [GEN_AI.OPERATION_NAME]: "chat",
121
+ // Session key as the conversation id, so consecutive turns of one session correlate (a
122
+ // single per-turn trace otherwise can't be tied to its neighbours — e.g. a runaway loop).
123
+ [GEN_AI.CONVERSATION_ID]: sessionKey,
124
+ [GEN_AI.PROVIDER_NAME]: provider,
125
+ [GEN_AI.REQUEST_MODEL]: requestModel,
126
+ [GEN_AI.REQUEST_TEMPERATURE]: firstNumber(ev.temperature),
127
+ [GEN_AI.REQUEST_MAX_TOKENS]: firstNumber(ev.maxTokens),
128
+ ...identityAttributes(identity),
129
+ });
130
+ // Content (redacted + capped). Stamping is async and the span ends in llm_output,
131
+ // so track the promise for the finalize to await — else a fast round-trip could end
132
+ // the span before the input content lands.
133
+ held.pending = Promise.allSettled([
134
+ setGenAiContentAttribute(span, GEN_AI.SYSTEM_INSTRUCTIONS, buildSystemInstructions(ev)),
135
+ setGenAiContentAttribute(span, GEN_AI.INPUT_MESSAGES, buildInputMessages(ev)),
136
+ setGenAiContentAttribute(span, GEN_AI.TOOL_DEFINITIONS, ev.tools),
137
+ ]);
138
+ }
139
+ catch (err) {
140
+ logger.warn("llm_input handler failed", { err: errMsg(err) });
141
+ }
142
+ });
143
+ api.on("llm_output", (event, agentCtx) => {
144
+ try {
145
+ const sessionKey = readSessionKey(agentCtx, event);
146
+ const runId = readRunId(agentCtx, event);
147
+ if (!sessionKey || !runId) {
148
+ logger.warn("llm_output missing sessionKey/runId; skipping span");
149
+ return;
150
+ }
151
+ rememberSession(agentCtx, sessionKey, runId);
152
+ const held = modelSpans.get(sessionLlmKey(sessionKey, runId));
153
+ if (!held) {
154
+ logger.debug("llm_output: no open chat span", { sessionKey, runId });
155
+ return;
156
+ }
157
+ const span = held.span;
158
+ modelSpans.delete(sessionLlmKey(sessionKey, runId));
159
+ // Cancel any deferred teardown the agent_end sweep scheduled — we finalize the span here.
160
+ if (held.endTimer)
161
+ clearTimeout(held.endTimer);
162
+ const ev = asRecord(event) ?? {};
163
+ const usage = asRecord(ev.usage);
164
+ // Token field naming varies by provider/wrapper; try every known spelling.
165
+ const inputTokens = firstNumber(usage?.input_tokens, usage?.prompt_tokens, usage?.promptTokens, usage?.input);
166
+ const outputTokens = firstNumber(usage?.output_tokens, usage?.completion_tokens, usage?.completionTokens, usage?.output);
167
+ // Cache + total tokens quantify context growth — the signal a silent compaction otherwise hides.
168
+ const cacheReadTokens = firstNumber(usage?.cacheRead, usage?.cache_read_input_tokens);
169
+ const cacheWriteTokens = firstNumber(usage?.cacheWrite, usage?.cache_creation_input_tokens);
170
+ const totalTokens = firstNumber(usage?.total, usage?.total_tokens);
171
+ setDefinedAttributes(span, {
172
+ [GEN_AI.RESPONSE_MODEL]: nonEmptyString(ev.model),
173
+ [GEN_AI.USAGE_INPUT_TOKENS]: inputTokens,
174
+ [GEN_AI.USAGE_OUTPUT_TOKENS]: outputTokens,
175
+ [GEN_AI.USAGE_CACHE_READ_TOKENS]: cacheReadTokens,
176
+ [GEN_AI.USAGE_CACHE_WRITE_TOKENS]: cacheWriteTokens,
177
+ [GEN_AI.USAGE_TOTAL_TOKENS]: totalTokens,
178
+ });
179
+ // A failed turn never reaches llm_output; outcome/finish is sourced from
180
+ // model_call_ended, so this hook only carries the success-path content + usage.
181
+ const dims = { provider: held.provider, model: held.model };
182
+ recordTokenUsage(dims, {
183
+ input: inputTokens,
184
+ output: outputTokens,
185
+ cacheRead: cacheReadTokens,
186
+ cacheWrite: cacheWriteTokens,
187
+ });
188
+ recordOperationDuration(elapsedSeconds(held.startMs), dims);
189
+ const outputMessages = buildOutputMessages(ev);
190
+ // Drift alarm: a tool the model requested but was never offered (requested − offered) is a
191
+ // coverage/contract hole — surface each invented name as an `unknown`-outcome event for the
192
+ // agent to read. Runs only when the offered set was captured (i.e. conversation access is on);
193
+ // deduped + capped so a flood of invented calls can't balloon the log.
194
+ if (held.offeredTools) {
195
+ const offered = held.offeredTools;
196
+ const seen = new Set();
197
+ for (const name of requestedToolNames(outputMessages)) {
198
+ if (offered.has(name) || seen.has(name))
199
+ continue;
200
+ if (seen.size >= MAX_DRIFT_EVENTS_PER_TURN)
201
+ break;
202
+ seen.add(name);
203
+ emitToolCalled({ name, origin: "agent", outcome: "unknown" });
204
+ // Count the drift on the metric plane (no duration — the invented call never ran).
205
+ recordToolCall({ toolName: name, origin: "agent", outcome: "unknown" });
206
+ }
207
+ }
208
+ void (async () => {
209
+ try {
210
+ if (held.pending)
211
+ await held.pending;
212
+ await setGenAiContentAttribute(span, GEN_AI.OUTPUT_MESSAGES, outputMessages);
213
+ }
214
+ finally {
215
+ try {
216
+ span.end();
217
+ }
218
+ catch {
219
+ /* best effort */
220
+ }
221
+ }
222
+ })();
223
+ }
224
+ catch (err) {
225
+ logger.warn("llm_output handler failed", { err: errMsg(err) });
226
+ }
227
+ });
228
+ // ─── Model-call outcome (enriches the held `chat` span) ────────────────────
229
+ // The provider-call result: `outcome`, `failureKind`, timing. This is the ONLY
230
+ // signal for a failed/aborted turn — `llm_output` never fires when the call
231
+ // errors, so without this a wedged turn is indistinguishable from a healthy one.
232
+ api.on("model_call_ended", (event, agentCtx) => {
233
+ try {
234
+ const sessionKey = readSessionKey(agentCtx, event);
235
+ const runId = readRunId(agentCtx, event);
236
+ if (!sessionKey || !runId)
237
+ return;
238
+ const held = modelSpans.get(sessionLlmKey(sessionKey, runId));
239
+ if (!held)
240
+ return; // span already finalized, or not one we opened — best effort.
241
+ const ev = asRecord(event) ?? {};
242
+ const outcome = nonEmptyString(ev.outcome);
243
+ const failureKind = nonEmptyString(ev.failureKind);
244
+ const errorCategory = nonEmptyString(ev.errorCategory);
245
+ setDefinedAttributes(held.span, {
246
+ "senpi.gen_ai.outcome": outcome,
247
+ "senpi.gen_ai.failure_kind": failureKind,
248
+ "senpi.gen_ai.error_category": errorCategory,
249
+ "senpi.gen_ai.model_call_duration_ms": firstNumber(ev.durationMs),
250
+ });
251
+ if (outcome === "error") {
252
+ // Mark errored so the agent_end sweep doesn't re-record the same failure.
253
+ held.errored = true;
254
+ setDefinedAttributes(held.span, {
255
+ [GEN_AI.RESPONSE_FINISH_REASONS]: toFinishReasons(failureKind ?? "error"),
256
+ });
257
+ recordSpanError(held.span, failureKind ?? errorCategory ?? "model_call_error");
258
+ }
259
+ }
260
+ catch (err) {
261
+ logger.warn("model_call_ended handler failed", { err: errMsg(err) });
262
+ }
263
+ });
264
+ // ─── Tool (`execute_tool` span) ────────────────────────────────────────────
265
+ api.on("before_tool_call", (event, toolCtx) => {
266
+ try {
267
+ const identity = readIdentity(toolCtx);
268
+ const resolved = resolveToolSession(toolCtx);
269
+ if (!resolved) {
270
+ logger.debug("before_tool_call session unresolved (no llm_input seen yet); skipping");
271
+ return;
272
+ }
273
+ const ev = asRecord(event);
274
+ const toolName = nonEmptyString(ev?.toolName) ?? nonEmptyString(ev?.name) ?? "unknown";
275
+ const toolCallId = nonEmptyString(ev?.toolCallId) ??
276
+ nonEmptyString(asRecord(toolCtx)?.toolCallId) ??
277
+ null;
278
+ const key = toolKey(resolved.sessionKey, resolved.runId, toolCallId, toolName, ++toolSeq);
279
+ const span = tracer.startSpan(`execute_tool ${toolName}`, { kind: SpanKind.INTERNAL }, parentContextForModel(resolved.sessionKey, resolved.runId));
280
+ const held = {
281
+ span,
282
+ sessionKey: resolved.sessionKey,
283
+ runId: resolved.runId,
284
+ startMs: Date.now(),
285
+ toolName,
286
+ };
287
+ toolSpans.set(key, held);
288
+ setDefinedAttributes(span, {
289
+ [GEN_AI.OPERATION_NAME]: "execute_tool",
290
+ [GEN_AI.CONVERSATION_ID]: resolved.sessionKey,
291
+ [GEN_AI.TOOL_NAME]: toolName,
292
+ [GEN_AI.TOOL_CALL_ID]: toolCallId ?? undefined,
293
+ // Best-effort skill attribution from a skill-file read (no first-class skill hook exists).
294
+ "senpi.skill.name": skillNameFromToolArgs(ev?.params ?? ev?.input),
295
+ ...identityAttributes(identity),
296
+ });
297
+ // Args stamping is async; the finalize (after_tool_call) awaits this so a fast
298
+ // tool doesn't end the span before the args land.
299
+ held.pending = setGenAiContentAttribute(span, GEN_AI.TOOL_CALL_ARGUMENTS, ev?.params ?? ev?.input);
300
+ }
301
+ catch (err) {
302
+ logger.warn("before_tool_call handler failed", { err: errMsg(err) });
303
+ }
304
+ });
305
+ api.on("after_tool_call", (event, toolCtx) => {
306
+ try {
307
+ const resolved = resolveToolSession(toolCtx);
308
+ if (!resolved) {
309
+ logger.debug("after_tool_call session unresolved; skipping");
310
+ return;
311
+ }
312
+ const ev = asRecord(event);
313
+ const toolName = nonEmptyString(ev?.toolName) ?? nonEmptyString(ev?.name) ?? "unknown";
314
+ const error = ev?.error;
315
+ const errored = error !== undefined && error !== null;
316
+ // Tool-plane event (narrative): a failed tool call — the agent-blind hole, greppable instead
317
+ // of span-only. A successful call already rides its span + (state-changing) its domain event,
318
+ // so it is not re-evented here. Emitted before the span lookup so a failure is never lost when
319
+ // the matching before_tool_call span wasn't registered.
320
+ if (errored) {
321
+ emitToolCalled({
322
+ name: toolName,
323
+ origin: "agent",
324
+ outcome: "error",
325
+ errorType: errorTypeOf(error),
326
+ errorMessage: error instanceof Error ? error.message : String(error),
327
+ });
328
+ }
329
+ const toolCallId = nonEmptyString(ev?.toolCallId) ??
330
+ nonEmptyString(asRecord(toolCtx)?.toolCallId) ??
331
+ null;
332
+ const foundKey = findToolKey(toolSpans, resolved.sessionKey, resolved.runId, toolCallId, toolName);
333
+ if (!foundKey) {
334
+ logger.debug("after_tool_call: no open tool span", { toolName, toolCallId });
335
+ return;
336
+ }
337
+ const held = toolSpans.get(foundKey);
338
+ const span = held.span;
339
+ toolSpans.delete(foundKey);
340
+ const result = ev?.result;
341
+ recordToolCall({
342
+ toolName: held.toolName,
343
+ origin: "agent",
344
+ outcome: errored ? "error" : "ok",
345
+ errorType: errored ? errorTypeOf(error) : undefined,
346
+ durationSec: elapsedSeconds(held.startMs),
347
+ });
348
+ void (async () => {
349
+ try {
350
+ if (held.pending)
351
+ await held.pending;
352
+ if (errored) {
353
+ recordSpanError(span, error);
354
+ }
355
+ else {
356
+ await setGenAiContentAttribute(span, GEN_AI.TOOL_CALL_RESULT, result);
357
+ }
358
+ }
359
+ finally {
360
+ try {
361
+ span.end();
362
+ }
363
+ catch {
364
+ /* best effort */
365
+ }
366
+ }
367
+ })();
368
+ }
369
+ catch (err) {
370
+ logger.warn("after_tool_call handler failed", { err: errMsg(err) });
371
+ }
372
+ });
373
+ // ─── Subagent (`invoke_agent` span, four-hook lifecycle) ───────────────────
374
+ api.on("subagent_spawning", (event, subagentCtx) => {
375
+ try {
376
+ const identity = readIdentity(subagentCtx);
377
+ const ev = asRecord(event);
378
+ const ctxObj = asRecord(subagentCtx);
379
+ const hostSessionKey = nonEmptyString(ctxObj?.requesterSessionKey) ??
380
+ nonEmptyString(ctxObj?.sessionKey) ??
381
+ "";
382
+ const hostRunId = nonEmptyString(ctxObj?.runId) ?? nonEmptyString(ctxObj?.requesterRunId) ?? "";
383
+ const childSessionKey = nonEmptyString(ev?.childSessionKey) ?? nonEmptyString(ctxObj?.childSessionKey);
384
+ if (!childSessionKey) {
385
+ logger.warn("subagent_spawning missing childSessionKey; skipping span");
386
+ return;
387
+ }
388
+ const prior = subagentSpans.get(childSessionKey);
389
+ if (prior)
390
+ endSpan(prior.span);
391
+ const agentName = nonEmptyString(ev?.label) ?? nonEmptyString(ev?.agentId) ?? "unknown";
392
+ const span = tracer.startSpan(`invoke_agent ${agentName}`, { kind: SpanKind.INTERNAL }, hostSessionKey && hostRunId
393
+ ? parentContextForModel(hostSessionKey, hostRunId)
394
+ : otelContext.active());
395
+ // sessionKey/runId carry the HOST run so the agent_end sweep can reach this span.
396
+ subagentSpans.set(childSessionKey, {
397
+ span,
398
+ sessionKey: hostSessionKey,
399
+ runId: hostRunId,
400
+ startMs: Date.now(),
401
+ agentName,
402
+ });
403
+ setDefinedAttributes(span, {
404
+ [GEN_AI.OPERATION_NAME]: "invoke_agent",
405
+ [GEN_AI.CONVERSATION_ID]: nonEmptyString(hostSessionKey),
406
+ [GEN_AI.AGENT_NAME]: agentName,
407
+ "senpi.subagent.mode": nonEmptyString(ev?.mode),
408
+ "senpi.subagent.requester": nonEmptyString(ev?.requester),
409
+ "senpi.subagent.thread_requested": typeof ev?.threadRequested === "boolean" ? ev.threadRequested : undefined,
410
+ ...identityAttributes(identity),
411
+ });
412
+ }
413
+ catch (err) {
414
+ logger.warn("subagent_spawning handler failed", { err: errMsg(err) });
415
+ }
416
+ });
417
+ api.on("subagent_spawned", (event, subagentCtx) => {
418
+ try {
419
+ const ev = asRecord(event);
420
+ const ctxObj = asRecord(subagentCtx);
421
+ const childSessionKey = nonEmptyString(ev?.childSessionKey) ?? nonEmptyString(ctxObj?.childSessionKey);
422
+ if (!childSessionKey)
423
+ return;
424
+ const held = subagentSpans.get(childSessionKey);
425
+ if (!held)
426
+ return;
427
+ const span = held.span;
428
+ setDefinedAttributes(span, {
429
+ "senpi.subagent.run_id": nonEmptyString(ev?.runId) ?? nonEmptyString(ctxObj?.runId),
430
+ "senpi.subagent.mode": nonEmptyString(ev?.mode),
431
+ "senpi.subagent.thread_requested": typeof ev?.threadRequested === "boolean" ? ev.threadRequested : undefined,
432
+ });
433
+ }
434
+ catch (err) {
435
+ logger.warn("subagent_spawned handler failed", { err: errMsg(err) });
436
+ }
437
+ });
438
+ api.on("subagent_delivery_target", (event, subagentCtx) => {
439
+ try {
440
+ const ev = asRecord(event);
441
+ const ctxObj = asRecord(subagentCtx);
442
+ const childSessionKey = nonEmptyString(ev?.childSessionKey) ?? nonEmptyString(ctxObj?.childSessionKey);
443
+ if (!childSessionKey)
444
+ return;
445
+ const held = subagentSpans.get(childSessionKey);
446
+ if (!held)
447
+ return;
448
+ const span = held.span;
449
+ const origin = asRecord(ev?.requesterOrigin) ?? {};
450
+ setDefinedAttributes(span, {
451
+ "senpi.subagent.delivery.channel": nonEmptyString(origin.channel),
452
+ "senpi.subagent.delivery.account_id": nonEmptyString(origin.accountId),
453
+ "senpi.subagent.delivery.to": nonEmptyString(origin.to),
454
+ "senpi.subagent.delivery.child_run_id": nonEmptyString(ev?.childRunId),
455
+ "senpi.subagent.delivery.spawn_mode": nonEmptyString(ev?.spawnMode),
456
+ });
457
+ }
458
+ catch (err) {
459
+ logger.warn("subagent_delivery_target handler failed", { err: errMsg(err) });
460
+ }
461
+ });
462
+ api.on("subagent_ended", (event, subagentCtx) => {
463
+ try {
464
+ const ev = asRecord(event);
465
+ const ctxObj = asRecord(subagentCtx);
466
+ const childSessionKey = nonEmptyString(ev?.targetSessionKey) ??
467
+ nonEmptyString(ctxObj?.childSessionKey) ??
468
+ nonEmptyString(ev?.childSessionKey);
469
+ if (!childSessionKey)
470
+ return;
471
+ const held = subagentSpans.get(childSessionKey);
472
+ if (!held)
473
+ return;
474
+ const span = held.span;
475
+ subagentSpans.delete(childSessionKey);
476
+ const error = ev?.error;
477
+ const errored = error !== undefined && error !== null;
478
+ recordInvokeAgentDuration(elapsedSeconds(held.startMs), {
479
+ agentName: held.agentName,
480
+ errorType: errored ? errorTypeOf(error) : undefined,
481
+ });
482
+ if (errored) {
483
+ recordSpanError(span, error);
484
+ }
485
+ else {
486
+ setDefinedAttributes(span, {
487
+ "senpi.subagent.outcome": nonEmptyString(ev?.outcome),
488
+ "senpi.subagent.reason": nonEmptyString(ev?.reason),
489
+ });
490
+ }
491
+ try {
492
+ span.end();
493
+ }
494
+ catch {
495
+ /* best effort */
496
+ }
497
+ }
498
+ catch (err) {
499
+ logger.warn("subagent_ended handler failed", { err: errMsg(err) });
500
+ }
501
+ });
502
+ // ─── Agent end (orphan sweep) ──────────────────────────────────────────────
503
+ api.on("agent_end", (event, agentCtx) => {
504
+ try {
505
+ const sessionKey = readSessionKey(agentCtx, event);
506
+ const runId = readRunId(agentCtx, event);
507
+ if (!sessionKey || !runId) {
508
+ logger.warn("agent_end missing sessionKey/runId; skipping sweep");
509
+ return;
510
+ }
511
+ const ev = asRecord(event);
512
+ const outcomeError = ev?.error;
513
+ // End any span still open under this (session, run): the model span, its tool
514
+ // children, and any subagents it hosted. Matching is by the stored host
515
+ // session/run, so nothing leaks past the run's end (incl. subagents, which have
516
+ // no other guaranteed terminator).
517
+ const llmKey = sessionLlmKey(sessionKey, runId);
518
+ const model = modelSpans.get(llmKey);
519
+ if (model) {
520
+ const errored = outcomeError !== undefined && outcomeError !== null;
521
+ recordOperationDuration(elapsedSeconds(model.startMs), {
522
+ provider: model.provider,
523
+ model: model.model,
524
+ errorType: errored ? errorTypeOf(outcomeError) : undefined,
525
+ });
526
+ // Skip if model_call_ended already recorded the failure on this span.
527
+ if (errored && !model.errored)
528
+ recordSpanError(model.span, outcomeError);
529
+ // Defer the span end: agent_end fires ~1 ms before the trailing llm_output that
530
+ // carries token usage + output messages. Hold the span (leave it in modelSpans) a
531
+ // short grace so that llm_output can finalize it; if none arrives, the timer ends it.
532
+ if (!model.endTimer) {
533
+ model.endTimer = setTimeout(() => {
534
+ if (modelSpans.get(llmKey) === model)
535
+ modelSpans.delete(llmKey);
536
+ endSpan(model.span);
537
+ }, MODEL_SPAN_END_GRACE_MS);
538
+ model.endTimer.unref?.();
539
+ }
540
+ }
541
+ for (const [k, held] of toolSpans) {
542
+ if (held.sessionKey === sessionKey && held.runId === runId) {
543
+ toolSpans.delete(k);
544
+ endSpan(held.span);
545
+ }
546
+ }
547
+ for (const [k, held] of subagentSpans) {
548
+ if (held.sessionKey === sessionKey && held.runId === runId) {
549
+ subagentSpans.delete(k);
550
+ endSpan(held.span);
551
+ }
552
+ }
553
+ // Evict the session-resolution memos for this run so they can't grow unbounded
554
+ // over the lifetime of the (long-lived) host process.
555
+ for (const [id, sess] of sessionByAgentId) {
556
+ if (sess.sessionKey === sessionKey && sess.runId === runId)
557
+ sessionByAgentId.delete(id);
558
+ }
559
+ if (lastActive && lastActive.sessionKey === sessionKey && lastActive.runId === runId) {
560
+ lastActive = null;
561
+ }
562
+ }
563
+ catch (err) {
564
+ logger.warn("agent_end handler failed", { err: errMsg(err) });
565
+ }
566
+ });
567
+ }
568
+ // ─── Span key helpers ───────────────────────────────────────────────────────
569
+ const SEP = " ";
570
+ function sessionLlmKey(sessionKey, runId) {
571
+ return `${sessionKey}${SEP}${runId}${SEP}llm`;
572
+ }
573
+ function toolKey(sessionKey, runId, toolCallId, toolName, seq) {
574
+ return toolCallId
575
+ ? `${sessionKey}${SEP}${runId}${SEP}toolcall${SEP}${toolCallId}`
576
+ : `${sessionKey}${SEP}${runId}${SEP}tool${SEP}${toolName}${SEP}${seq}`;
577
+ }
578
+ /** Find the open tool span key within one (session, run): by toolCallId first, else by tool name. */
579
+ function findToolKey(toolSpans, sessionKey, runId, toolCallId, toolName) {
580
+ const prefix = `${sessionKey}${SEP}${runId}${SEP}`;
581
+ if (toolCallId) {
582
+ const suffix = `${SEP}toolcall${SEP}${toolCallId}`;
583
+ for (const key of toolSpans.keys()) {
584
+ if (key.startsWith(prefix) && key.endsWith(suffix))
585
+ return key;
586
+ }
587
+ }
588
+ const namePart = `${SEP}tool${SEP}${toolName}${SEP}`;
589
+ for (const key of toolSpans.keys()) {
590
+ if (key.startsWith(prefix) && key.includes(namePart))
591
+ return key;
592
+ }
593
+ return undefined;
594
+ }
595
+ function endSpan(span) {
596
+ try {
597
+ span.end();
598
+ }
599
+ catch {
600
+ /* best effort */
601
+ }
602
+ }
603
+ // ─── Identity + trace correlation ─────────────────────────────────────────────
604
+ const ADDRESS_RE = /^0x[a-f0-9]{40}$/i;
605
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
606
+ /** Cap on a free-form header value stamped as an attribute (defence vs oversized header). */
607
+ const MAX_HEADER_ATTR_CHARS = 256;
608
+ function clampHeader(v) {
609
+ return v.length > MAX_HEADER_ATTR_CHARS ? v.slice(0, MAX_HEADER_ATTR_CHARS) : v;
610
+ }
611
+ /**
612
+ * Map the senpi identity headers to `senpi.*` span attributes (keys match the on-site
613
+ * identity bag; the headers carry the runtime id as `senpi.runtime.id`, vs on-site
614
+ * `senpi.runtime`). Headers are caller-influenceable, so join keys are validated — a
615
+ * malformed address / correlation id is dropped, not stamped as garbage.
616
+ */
617
+ function identityAttributes(identity) {
618
+ const attrs = {};
619
+ if (identity.address) {
620
+ const lowered = identity.address.toLowerCase();
621
+ if (ADDRESS_RE.test(lowered))
622
+ attrs["senpi.strategy.address"] = lowered;
623
+ }
624
+ if (identity.recipeName)
625
+ attrs["senpi.recipe"] = clampHeader(identity.recipeName);
626
+ if (identity.runtimeVersion)
627
+ attrs["senpi.recipe.version"] = clampHeader(identity.runtimeVersion);
628
+ if (identity.runtimeId)
629
+ attrs["senpi.runtime.id"] = clampHeader(identity.runtimeId);
630
+ // Fallback join key to the trading trace when traceparent didn't thread through.
631
+ if (identity.runId && UUID_RE.test(identity.runId)) {
632
+ attrs["senpi.run.id"] = identity.runId;
633
+ }
634
+ return attrs;
635
+ }
636
+ function readIdentity(agentCtx) {
637
+ const headers = readHeaders(agentCtx);
638
+ return {
639
+ runId: headers[HEADER_CORRELATION_ID],
640
+ address: headers[HEADER_ADDRESS],
641
+ recipeName: headers[HEADER_RECIPE_NAME],
642
+ runtimeId: headers[HEADER_RUNTIME_ID],
643
+ runtimeVersion: headers[HEADER_RUNTIME_VERSION],
644
+ };
645
+ }
646
+ /** Extract a W3C traceparent from the request headers → a parent Context (else active). */
647
+ function parentContextFromHeaders(agentCtx) {
648
+ try {
649
+ const headers = readHeaders(agentCtx);
650
+ if (headers.traceparent === undefined)
651
+ return otelContext.active();
652
+ return propagation.extract(otelContext.active(), headers);
653
+ }
654
+ catch {
655
+ return otelContext.active();
656
+ }
657
+ }
658
+ // ─── Field readers ────────────────────────────────────────────────────────────
659
+ function asRecord(v) {
660
+ if (v && typeof v === "object" && !Array.isArray(v)) {
661
+ return v;
662
+ }
663
+ return undefined;
664
+ }
665
+ function nonEmptyString(v) {
666
+ return typeof v === "string" && v.length > 0 ? v : undefined;
667
+ }
668
+ /** A skill loaded by the agent reading a file under `…/skills/<name>/…` (e.g. its SKILL.md). */
669
+ const SKILL_PATH_RE = /(?:^|\/)skills\/([^/]+)\//;
670
+ /**
671
+ * Best-effort skill attribution: there is no first-class skill-invocation hook, so a skill is
672
+ * detectable only by the agent reading its file. Returns the skill name when a tool-call arg path
673
+ * points under a `skills/<name>/` dir, else undefined. Heuristic — a real gateway skill hook,
674
+ * once it exists, would supersede this.
675
+ */
676
+ function skillNameFromToolArgs(args) {
677
+ const rec = asRecord(args);
678
+ if (!rec)
679
+ return undefined;
680
+ const path = nonEmptyString(rec.path) ?? nonEmptyString(rec.file_path) ?? nonEmptyString(rec.filePath);
681
+ if (!path)
682
+ return undefined;
683
+ return SKILL_PATH_RE.exec(path)?.[1];
684
+ }
685
+ /** Pull request headers out of agentCtx; several common locations are checked. */
686
+ function readHeaders(agentCtx) {
687
+ const ctxObj = asRecord(agentCtx);
688
+ if (!ctxObj)
689
+ return {};
690
+ const candidates = [
691
+ asRecord(ctxObj.headers),
692
+ asRecord(asRecord(ctxObj.request)?.headers),
693
+ asRecord(ctxObj.requestHeaders),
694
+ asRecord(asRecord(ctxObj.meta)?.headers),
695
+ ];
696
+ const out = {};
697
+ for (const candidate of candidates) {
698
+ if (!candidate)
699
+ continue;
700
+ for (const [k, v] of Object.entries(candidate)) {
701
+ const lk = k.toLowerCase();
702
+ if (typeof v === "string" && v.length > 0 && out[lk] === undefined) {
703
+ out[lk] = v;
704
+ }
705
+ else if (Array.isArray(v) && v.length > 0 && typeof v[0] === "string") {
706
+ out[lk] = v[0];
707
+ }
708
+ }
709
+ }
710
+ return out;
711
+ }
712
+ function readSessionKey(ctx, event) {
713
+ return (nonEmptyString(asRecord(ctx)?.sessionKey) ??
714
+ nonEmptyString(asRecord(ctx)?.sessionId) ??
715
+ nonEmptyString(asRecord(event)?.sessionKey) ??
716
+ nonEmptyString(asRecord(event)?.sessionId));
717
+ }
718
+ function readRunId(ctx, event) {
719
+ return nonEmptyString(asRecord(ctx)?.runId) ?? nonEmptyString(asRecord(event)?.runId);
720
+ }
721
+ // ─── gen_ai content builders ──────────────────────────────────────────────────
722
+ /** System instructions as `[{type:"text", content}]` per the gen_ai shape. */
723
+ function buildSystemInstructions(ev) {
724
+ const system = ev.systemPrompt ?? ev.system;
725
+ if (typeof system !== "string" || system.length === 0)
726
+ return undefined;
727
+ return [{ type: "text", content: system }];
728
+ }
729
+ /** Input messages: the history (or messages), with any standalone `prompt` as a user turn. */
730
+ function buildInputMessages(ev) {
731
+ const history = ev.historyMessages ?? ev.messages;
732
+ const messages = Array.isArray(history) ? [...history] : history ? [history] : [];
733
+ if (typeof ev.prompt === "string" && ev.prompt.length > 0) {
734
+ messages.push({ role: "user", content: ev.prompt });
735
+ }
736
+ return normalizeGenAiMessages(messages, "user");
737
+ }
738
+ /**
739
+ * Output messages. Prefer the structured `lastAssistant` — it carries tool calls, which the
740
+ * text-only `assistantTexts` does not. The catch the old `assistantTexts ?? lastAssistant`
741
+ * missed: `assistantTexts` is `[]` (not nullish) on a tool-call-only turn, so `??` never fell
742
+ * through and the turn's output was dropped. Fall back to the text chunks, then any raw content.
743
+ */
744
+ function buildOutputMessages(ev) {
745
+ const last = ev.lastAssistant;
746
+ if (last !== undefined && last !== null) {
747
+ // A full message object (has a role) passes through; a content array/string/object
748
+ // becomes the body of one assistant message.
749
+ const rec = asRecord(last);
750
+ const message = rec && typeof rec.role === "string" ? last : { role: "assistant", content: last };
751
+ return normalizeGenAiMessages([message], "assistant");
752
+ }
753
+ const texts = Array.isArray(ev.assistantTexts)
754
+ ? ev.assistantTexts.filter((t) => typeof t === "string" && t.length > 0)
755
+ : [];
756
+ const content = texts.length > 0 ? texts : ev.content;
757
+ if (content === undefined || content === null)
758
+ return undefined;
759
+ return normalizeGenAiMessages([{ role: "assistant", content }], "assistant");
760
+ }
761
+ function errMsg(err) {
762
+ return err instanceof Error ? err.message : String(err);
763
+ }
764
+ //# sourceMappingURL=span-hooks.js.map