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

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 (472) 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 +264 -79
  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/config.d.ts +2 -2
  130. package/dist/runtime-api/config.js +4 -4
  131. package/dist/runtime-api/config.js.map +1 -1
  132. package/dist/runtime-api/handlers/ingest-signals.d.ts +5 -3
  133. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
  134. package/dist/runtime-api/handlers/ingest-signals.js +14 -5
  135. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
  136. package/dist/runtime-api/intake/ingest-mutex.d.ts +16 -0
  137. package/dist/runtime-api/intake/ingest-mutex.d.ts.map +1 -0
  138. package/dist/runtime-api/intake/ingest-mutex.js +18 -0
  139. package/dist/runtime-api/intake/ingest-mutex.js.map +1 -0
  140. package/dist/runtime-api/intake/intake-events.d.ts +44 -0
  141. package/dist/runtime-api/intake/intake-events.d.ts.map +1 -0
  142. package/dist/runtime-api/intake/intake-events.js +131 -0
  143. package/dist/runtime-api/intake/intake-events.js.map +1 -0
  144. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  145. package/dist/runtime-api/intake/intake-mount.js +8 -2
  146. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  147. package/dist/runtime-api/intake/scanner-launcher.d.ts +2 -0
  148. package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
  149. package/dist/runtime-api/intake/scanner-launcher.js +58 -15
  150. package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
  151. package/dist/runtime-api/intake/scanner-registry.d.ts +3 -0
  152. package/dist/runtime-api/intake/scanner-registry.d.ts.map +1 -1
  153. package/dist/runtime-api/intake/scanner-registry.js +3 -0
  154. package/dist/runtime-api/intake/scanner-registry.js.map +1 -1
  155. package/dist/runtime-api/intake/supervisor-events.d.ts +61 -0
  156. package/dist/runtime-api/intake/supervisor-events.d.ts.map +1 -0
  157. package/dist/runtime-api/intake/supervisor-events.js +173 -0
  158. package/dist/runtime-api/intake/supervisor-events.js.map +1 -0
  159. package/dist/runtime-api/routes/signals.d.ts +1 -1
  160. package/dist/runtime-api/routes/signals.d.ts.map +1 -1
  161. package/dist/runtime-api/routes/signals.js +121 -35
  162. package/dist/runtime-api/routes/signals.js.map +1 -1
  163. package/dist/runtime-api/server.d.ts.map +1 -1
  164. package/dist/runtime-api/server.js +10 -1
  165. package/dist/runtime-api/server.js.map +1 -1
  166. package/dist/scanners/engine/data-providers.d.ts +3 -0
  167. package/dist/scanners/engine/data-providers.d.ts.map +1 -1
  168. package/dist/scanners/engine/data-providers.js.map +1 -1
  169. package/dist/scanners/engine/engine.d.ts +18 -0
  170. package/dist/scanners/engine/engine.d.ts.map +1 -1
  171. package/dist/scanners/engine/engine.js +96 -2
  172. package/dist/scanners/engine/engine.js.map +1 -1
  173. package/dist/scanners/engine/input-resolver.d.ts.map +1 -1
  174. package/dist/scanners/engine/input-resolver.js +9 -7
  175. package/dist/scanners/engine/input-resolver.js.map +1 -1
  176. package/dist/scanners/runtime-module.d.ts +3 -0
  177. package/dist/scanners/runtime-module.d.ts.map +1 -1
  178. package/dist/scanners/runtime-module.js +7 -0
  179. package/dist/scanners/runtime-module.js.map +1 -1
  180. package/dist/senpi/client.d.ts +10 -0
  181. package/dist/senpi/client.d.ts.map +1 -1
  182. package/dist/senpi/client.js +113 -46
  183. package/dist/senpi/client.js.map +1 -1
  184. package/dist/skills-manager/bootstrap.d.ts.map +1 -1
  185. package/dist/skills-manager/bootstrap.js +22 -33
  186. package/dist/skills-manager/bootstrap.js.map +1 -1
  187. package/dist/skills-manager/clone-groups.d.ts +20 -0
  188. package/dist/skills-manager/clone-groups.d.ts.map +1 -0
  189. package/dist/skills-manager/clone-groups.js +45 -0
  190. package/dist/skills-manager/clone-groups.js.map +1 -0
  191. package/dist/skills-manager/coordinator.d.ts.map +1 -1
  192. package/dist/skills-manager/coordinator.js +111 -75
  193. package/dist/skills-manager/coordinator.js.map +1 -1
  194. package/dist/skills-manager/manifest.d.ts +24 -0
  195. package/dist/skills-manager/manifest.d.ts.map +1 -1
  196. package/dist/skills-manager/manifest.js +23 -10
  197. package/dist/skills-manager/manifest.js.map +1 -1
  198. package/dist/skills-manager/skills-manifest.json +12 -7
  199. package/dist/skills-manager/types.d.ts +12 -0
  200. package/dist/skills-manager/types.d.ts.map +1 -1
  201. package/dist/state/state-events.d.ts +13 -0
  202. package/dist/state/state-events.d.ts.map +1 -0
  203. package/dist/state/state-events.js +22 -0
  204. package/dist/state/state-events.js.map +1 -0
  205. package/dist/state/state-manager.d.ts.map +1 -1
  206. package/dist/state/state-manager.js +14 -1
  207. package/dist/state/state-manager.js.map +1 -1
  208. package/dist/telemetry/attributes.d.ts +13 -0
  209. package/dist/telemetry/attributes.d.ts.map +1 -0
  210. package/dist/telemetry/attributes.js +22 -0
  211. package/dist/telemetry/attributes.js.map +1 -0
  212. package/dist/{tracking → telemetry}/context.d.ts +12 -5
  213. package/dist/telemetry/context.d.ts.map +1 -0
  214. package/dist/{tracking → telemetry}/context.js +4 -4
  215. package/dist/telemetry/context.js.map +1 -0
  216. package/dist/{recording/records.d.ts → telemetry/event-builders.d.ts} +145 -33
  217. package/dist/telemetry/event-builders.d.ts.map +1 -0
  218. package/dist/{recording/wiring/record-builders.js → telemetry/event-builders.js} +94 -40
  219. package/dist/telemetry/event-builders.js.map +1 -0
  220. package/dist/telemetry/event-store.d.ts +66 -0
  221. package/dist/telemetry/event-store.d.ts.map +1 -0
  222. package/dist/telemetry/event-store.js +202 -0
  223. package/dist/telemetry/event-store.js.map +1 -0
  224. package/dist/telemetry/identity.d.ts +72 -0
  225. package/dist/telemetry/identity.d.ts.map +1 -0
  226. package/dist/{recording/wiring → telemetry}/identity.js +6 -2
  227. package/dist/telemetry/identity.js.map +1 -0
  228. package/dist/telemetry/metrics.d.ts +169 -0
  229. package/dist/telemetry/metrics.d.ts.map +1 -0
  230. package/dist/telemetry/metrics.js +437 -0
  231. package/dist/telemetry/metrics.js.map +1 -0
  232. package/dist/telemetry/otel-sdk.d.ts +93 -0
  233. package/dist/telemetry/otel-sdk.d.ts.map +1 -0
  234. package/dist/telemetry/otel-sdk.js +297 -0
  235. package/dist/telemetry/otel-sdk.js.map +1 -0
  236. package/dist/telemetry/spans/constants.d.ts +12 -0
  237. package/dist/telemetry/spans/constants.d.ts.map +1 -0
  238. package/dist/telemetry/spans/constants.js +12 -0
  239. package/dist/telemetry/spans/constants.js.map +1 -0
  240. package/dist/telemetry/spans/gen-ai.d.ts +74 -0
  241. package/dist/telemetry/spans/gen-ai.d.ts.map +1 -0
  242. package/dist/telemetry/spans/gen-ai.js +339 -0
  243. package/dist/telemetry/spans/gen-ai.js.map +1 -0
  244. package/dist/telemetry/spans/index.d.ts +11 -0
  245. package/dist/telemetry/spans/index.d.ts.map +1 -0
  246. package/dist/telemetry/spans/index.js +11 -0
  247. package/dist/telemetry/spans/index.js.map +1 -0
  248. package/dist/{tracking → telemetry}/spans/redact-pii.d.ts +19 -21
  249. package/dist/telemetry/spans/redact-pii.d.ts.map +1 -0
  250. package/dist/{tracking → telemetry}/spans/redact-pii.js +35 -32
  251. package/dist/telemetry/spans/redact-pii.js.map +1 -0
  252. package/dist/telemetry/spans/sanitize-payload.d.ts.map +1 -0
  253. package/dist/telemetry/spans/sanitize-payload.js.map +1 -0
  254. package/dist/telemetry/spans/span-hooks.d.ts +18 -0
  255. package/dist/telemetry/spans/span-hooks.d.ts.map +1 -0
  256. package/dist/telemetry/spans/span-hooks.js +764 -0
  257. package/dist/telemetry/spans/span-hooks.js.map +1 -0
  258. package/dist/telemetry/tool-plane.d.ts +31 -0
  259. package/dist/telemetry/tool-plane.d.ts.map +1 -0
  260. package/dist/telemetry/tool-plane.js +70 -0
  261. package/dist/telemetry/tool-plane.js.map +1 -0
  262. package/dist/telemetry/trace-identity.d.ts +32 -0
  263. package/dist/telemetry/trace-identity.d.ts.map +1 -0
  264. package/dist/telemetry/trace-identity.js +62 -0
  265. package/dist/telemetry/trace-identity.js.map +1 -0
  266. package/dist/telemetry/tracing.d.ts +33 -0
  267. package/dist/telemetry/tracing.d.ts.map +1 -0
  268. package/dist/telemetry/tracing.js +70 -0
  269. package/dist/telemetry/tracing.js.map +1 -0
  270. package/dist/types/dsl/index.d.ts +8 -9
  271. package/dist/types/dsl/index.d.ts.map +1 -1
  272. package/dist/types/runtime.d.ts +1 -1
  273. package/dist/types/runtime.d.ts.map +1 -1
  274. package/dist/types/scanner.d.ts +2 -0
  275. package/dist/types/scanner.d.ts.map +1 -1
  276. package/dist/utils/event-catalog.d.ts +506 -0
  277. package/dist/utils/event-catalog.d.ts.map +1 -0
  278. package/dist/utils/event-catalog.js +142 -0
  279. package/dist/utils/event-catalog.js.map +1 -0
  280. package/dist/utils/logger.d.ts +63 -5
  281. package/dist/utils/logger.d.ts.map +1 -1
  282. package/dist/utils/logger.js +360 -27
  283. package/dist/utils/logger.js.map +1 -1
  284. package/dist/utils/wallet-address.d.ts +3 -0
  285. package/dist/utils/wallet-address.d.ts.map +1 -0
  286. package/dist/utils/wallet-address.js +17 -0
  287. package/dist/utils/wallet-address.js.map +1 -0
  288. package/openclaw.plugin.json +20 -6
  289. package/package.json +10 -2
  290. package/dist/actions/tracking-helpers.d.ts +0 -28
  291. package/dist/actions/tracking-helpers.d.ts.map +0 -1
  292. package/dist/actions/tracking-helpers.js +0 -93
  293. package/dist/actions/tracking-helpers.js.map +0 -1
  294. package/dist/recording/emitter.d.ts +0 -27
  295. package/dist/recording/emitter.d.ts.map +0 -1
  296. package/dist/recording/emitter.js +0 -2
  297. package/dist/recording/emitter.js.map +0 -1
  298. package/dist/recording/identity.d.ts +0 -34
  299. package/dist/recording/identity.d.ts.map +0 -1
  300. package/dist/recording/identity.js +0 -2
  301. package/dist/recording/identity.js.map +0 -1
  302. package/dist/recording/index.d.ts +0 -11
  303. package/dist/recording/index.d.ts.map +0 -1
  304. package/dist/recording/index.js +0 -2
  305. package/dist/recording/index.js.map +0 -1
  306. package/dist/recording/noop-emitter.d.ts +0 -23
  307. package/dist/recording/noop-emitter.d.ts.map +0 -1
  308. package/dist/recording/noop-emitter.js +0 -22
  309. package/dist/recording/noop-emitter.js.map +0 -1
  310. package/dist/recording/otel/attributes.d.ts +0 -35
  311. package/dist/recording/otel/attributes.d.ts.map +0 -1
  312. package/dist/recording/otel/attributes.js +0 -131
  313. package/dist/recording/otel/attributes.js.map +0 -1
  314. package/dist/recording/otel/constants.d.ts +0 -123
  315. package/dist/recording/otel/constants.d.ts.map +0 -1
  316. package/dist/recording/otel/constants.js +0 -172
  317. package/dist/recording/otel/constants.js.map +0 -1
  318. package/dist/recording/otel/ids.d.ts +0 -25
  319. package/dist/recording/otel/ids.d.ts.map +0 -1
  320. package/dist/recording/otel/ids.js +0 -37
  321. package/dist/recording/otel/ids.js.map +0 -1
  322. package/dist/recording/otel/index.d.ts +0 -13
  323. package/dist/recording/otel/index.d.ts.map +0 -1
  324. package/dist/recording/otel/index.js +0 -9
  325. package/dist/recording/otel/index.js.map +0 -1
  326. package/dist/recording/otel/mappers.d.ts +0 -100
  327. package/dist/recording/otel/mappers.d.ts.map +0 -1
  328. package/dist/recording/otel/mappers.js +0 -683
  329. package/dist/recording/otel/mappers.js.map +0 -1
  330. package/dist/recording/otel/otel-emitter.d.ts +0 -43
  331. package/dist/recording/otel/otel-emitter.d.ts.map +0 -1
  332. package/dist/recording/otel/otel-emitter.js +0 -99
  333. package/dist/recording/otel/otel-emitter.js.map +0 -1
  334. package/dist/recording/otel/redaction.d.ts +0 -47
  335. package/dist/recording/otel/redaction.d.ts.map +0 -1
  336. package/dist/recording/otel/redaction.js +0 -88
  337. package/dist/recording/otel/redaction.js.map +0 -1
  338. package/dist/recording/records.d.ts.map +0 -1
  339. package/dist/recording/records.js +0 -14
  340. package/dist/recording/records.js.map +0 -1
  341. package/dist/recording/transport/batcher.d.ts +0 -59
  342. package/dist/recording/transport/batcher.d.ts.map +0 -1
  343. package/dist/recording/transport/batcher.js +0 -234
  344. package/dist/recording/transport/batcher.js.map +0 -1
  345. package/dist/recording/transport/client.d.ts +0 -41
  346. package/dist/recording/transport/client.d.ts.map +0 -1
  347. package/dist/recording/transport/client.js +0 -69
  348. package/dist/recording/transport/client.js.map +0 -1
  349. package/dist/recording/transport/config.d.ts +0 -53
  350. package/dist/recording/transport/config.d.ts.map +0 -1
  351. package/dist/recording/transport/config.js +0 -41
  352. package/dist/recording/transport/config.js.map +0 -1
  353. package/dist/recording/transport/index.d.ts +0 -16
  354. package/dist/recording/transport/index.d.ts.map +0 -1
  355. package/dist/recording/transport/index.js +0 -12
  356. package/dist/recording/transport/index.js.map +0 -1
  357. package/dist/recording/transport/rows.d.ts +0 -88
  358. package/dist/recording/transport/rows.d.ts.map +0 -1
  359. package/dist/recording/transport/rows.js +0 -15
  360. package/dist/recording/transport/rows.js.map +0 -1
  361. package/dist/recording/wiring/emitter-factory.d.ts +0 -39
  362. package/dist/recording/wiring/emitter-factory.d.ts.map +0 -1
  363. package/dist/recording/wiring/emitter-factory.js +0 -62
  364. package/dist/recording/wiring/emitter-factory.js.map +0 -1
  365. package/dist/recording/wiring/identity.d.ts +0 -27
  366. package/dist/recording/wiring/identity.d.ts.map +0 -1
  367. package/dist/recording/wiring/identity.js.map +0 -1
  368. package/dist/recording/wiring/index.d.ts +0 -12
  369. package/dist/recording/wiring/index.d.ts.map +0 -1
  370. package/dist/recording/wiring/index.js +0 -9
  371. package/dist/recording/wiring/index.js.map +0 -1
  372. package/dist/recording/wiring/record-builders.d.ts +0 -107
  373. package/dist/recording/wiring/record-builders.d.ts.map +0 -1
  374. package/dist/recording/wiring/record-builders.js.map +0 -1
  375. package/dist/recording/wiring/subscriber.d.ts +0 -28
  376. package/dist/recording/wiring/subscriber.d.ts.map +0 -1
  377. package/dist/recording/wiring/subscriber.js +0 -173
  378. package/dist/recording/wiring/subscriber.js.map +0 -1
  379. package/dist/recording/wiring/values.d.ts +0 -19
  380. package/dist/recording/wiring/values.d.ts.map +0 -1
  381. package/dist/recording/wiring/values.js +0 -49
  382. package/dist/recording/wiring/values.js.map +0 -1
  383. package/dist/tracking/build-rehydrate-items.d.ts +0 -15
  384. package/dist/tracking/build-rehydrate-items.d.ts.map +0 -1
  385. package/dist/tracking/build-rehydrate-items.js +0 -19
  386. package/dist/tracking/build-rehydrate-items.js.map +0 -1
  387. package/dist/tracking/constants.d.ts +0 -24
  388. package/dist/tracking/constants.d.ts.map +0 -1
  389. package/dist/tracking/constants.js +0 -52
  390. package/dist/tracking/constants.js.map +0 -1
  391. package/dist/tracking/context.d.ts.map +0 -1
  392. package/dist/tracking/context.js.map +0 -1
  393. package/dist/tracking/index.d.ts +0 -16
  394. package/dist/tracking/index.d.ts.map +0 -1
  395. package/dist/tracking/index.js +0 -11
  396. package/dist/tracking/index.js.map +0 -1
  397. package/dist/tracking/plugin-event-tracker.d.ts +0 -13
  398. package/dist/tracking/plugin-event-tracker.d.ts.map +0 -1
  399. package/dist/tracking/plugin-event-tracker.js +0 -27
  400. package/dist/tracking/plugin-event-tracker.js.map +0 -1
  401. package/dist/tracking/position-id-registry.d.ts.map +0 -1
  402. package/dist/tracking/position-id-registry.js.map +0 -1
  403. package/dist/tracking/spans/constants.d.ts +0 -19
  404. package/dist/tracking/spans/constants.d.ts.map +0 -1
  405. package/dist/tracking/spans/constants.js +0 -31
  406. package/dist/tracking/spans/constants.js.map +0 -1
  407. package/dist/tracking/spans/fan-out-span-transport.d.ts +0 -22
  408. package/dist/tracking/spans/fan-out-span-transport.d.ts.map +0 -1
  409. package/dist/tracking/spans/fan-out-span-transport.js +0 -49
  410. package/dist/tracking/spans/fan-out-span-transport.js.map +0 -1
  411. package/dist/tracking/spans/index.d.ts +0 -13
  412. package/dist/tracking/spans/index.d.ts.map +0 -1
  413. package/dist/tracking/spans/index.js +0 -16
  414. package/dist/tracking/spans/index.js.map +0 -1
  415. package/dist/tracking/spans/otel/fit-row-budget.d.ts +0 -22
  416. package/dist/tracking/spans/otel/fit-row-budget.d.ts.map +0 -1
  417. package/dist/tracking/spans/otel/fit-row-budget.js +0 -91
  418. package/dist/tracking/spans/otel/fit-row-budget.js.map +0 -1
  419. package/dist/tracking/spans/otel/map-span-to-trace-row.d.ts +0 -16
  420. package/dist/tracking/spans/otel/map-span-to-trace-row.d.ts.map +0 -1
  421. package/dist/tracking/spans/otel/map-span-to-trace-row.js +0 -227
  422. package/dist/tracking/spans/otel/map-span-to-trace-row.js.map +0 -1
  423. package/dist/tracking/spans/otel/recording-span-transport.d.ts +0 -43
  424. package/dist/tracking/spans/otel/recording-span-transport.d.ts.map +0 -1
  425. package/dist/tracking/spans/otel/recording-span-transport.js +0 -67
  426. package/dist/tracking/spans/otel/recording-span-transport.js.map +0 -1
  427. package/dist/tracking/spans/otel/span-ids.d.ts +0 -22
  428. package/dist/tracking/spans/otel/span-ids.d.ts.map +0 -1
  429. package/dist/tracking/spans/otel/span-ids.js +0 -32
  430. package/dist/tracking/spans/otel/span-ids.js.map +0 -1
  431. package/dist/tracking/spans/plugin-span-tracker.d.ts +0 -9
  432. package/dist/tracking/spans/plugin-span-tracker.d.ts.map +0 -1
  433. package/dist/tracking/spans/plugin-span-tracker.js +0 -115
  434. package/dist/tracking/spans/plugin-span-tracker.js.map +0 -1
  435. package/dist/tracking/spans/redact-pii.d.ts.map +0 -1
  436. package/dist/tracking/spans/redact-pii.js.map +0 -1
  437. package/dist/tracking/spans/sanitize-payload.d.ts.map +0 -1
  438. package/dist/tracking/spans/sanitize-payload.js.map +0 -1
  439. package/dist/tracking/spans/span-hooks.d.ts +0 -28
  440. package/dist/tracking/spans/span-hooks.d.ts.map +0 -1
  441. package/dist/tracking/spans/span-hooks.js +0 -409
  442. package/dist/tracking/spans/span-hooks.js.map +0 -1
  443. package/dist/tracking/spans/span-tracker.d.ts +0 -133
  444. package/dist/tracking/spans/span-tracker.d.ts.map +0 -1
  445. package/dist/tracking/spans/span-tracker.js +0 -721
  446. package/dist/tracking/spans/span-tracker.js.map +0 -1
  447. package/dist/tracking/spans/span-transport.d.ts +0 -71
  448. package/dist/tracking/spans/span-transport.d.ts.map +0 -1
  449. package/dist/tracking/spans/span-transport.js +0 -240
  450. package/dist/tracking/spans/span-transport.js.map +0 -1
  451. package/dist/tracking/spans/types.d.ts +0 -101
  452. package/dist/tracking/spans/types.d.ts.map +0 -1
  453. package/dist/tracking/spans/types.js +0 -6
  454. package/dist/tracking/spans/types.js.map +0 -1
  455. package/dist/tracking/tracker.d.ts +0 -30
  456. package/dist/tracking/tracker.d.ts.map +0 -1
  457. package/dist/tracking/tracker.js +0 -66
  458. package/dist/tracking/tracker.js.map +0 -1
  459. package/dist/tracking/tracking-handler.d.ts +0 -12
  460. package/dist/tracking/tracking-handler.d.ts.map +0 -1
  461. package/dist/tracking/tracking-handler.js +0 -148
  462. package/dist/tracking/tracking-handler.js.map +0 -1
  463. package/dist/tracking/transport.d.ts +0 -35
  464. package/dist/tracking/transport.d.ts.map +0 -1
  465. package/dist/tracking/transport.js +0 -133
  466. package/dist/tracking/transport.js.map +0 -1
  467. package/dist/tracking/types.d.ts +0 -21
  468. package/dist/tracking/types.d.ts.map +0 -1
  469. package/dist/tracking/types.js +0 -6
  470. package/dist/tracking/types.js.map +0 -1
  471. /package/dist/{tracking → telemetry}/spans/sanitize-payload.d.ts +0 -0
  472. /package/dist/{tracking → telemetry}/spans/sanitize-payload.js +0 -0
@@ -12,6 +12,7 @@ import { createRiskGuard, createRiskGuardStub } from "./risk-guard.js";
12
12
  import { createStrategyLifecycle } from "./strategy-lifecycle.js";
13
13
  // RiskGuardianService removed — gate decisions are now real-time in checkGate
14
14
  import { createRiskAuditLog } from "../risk/risk-audit-log.js";
15
+ import { createEventStore, registerEventSink, unregisterEventSink, flushEventSink } from "../telemetry/event-store.js";
15
16
  import { createNotificationService } from "./notifications.js";
16
17
  import { resolveStrategyConfig } from "./map-strategies.js";
17
18
  import { mapRuntimeExitToDslConfig } from "./map-exit-config.js";
@@ -31,16 +32,32 @@ import { createActionNotificationSubscriber, isActionLifecycleNotificationsEnabl
31
32
  import { MaxLeverageCache } from "../leverage/max-leverage-cache.js";
32
33
  import { buildStateLayout } from "./state-layout.js";
33
34
  import { createLogger } from "../utils/logger.js";
34
- import { createTrackingHandler, EventTracker, BatchingTransport, NoOpTransport, TRACKING_API_BASE_URL, PositionIdRegistry, positionRegistryKey, buildRehydrateItems, } from "../tracking/index.js";
35
+ import { buildScannerSummaryEvent, buildRuntimeStartedEvent, buildRuntimeStoppedEvent, buildRuntimeErrorEvent, buildQueueBackpressureEvent, } from "./lifecycle-events.js";
36
+ import { emitEvent } from "../actions/event-derivation.js";
37
+ import { createDomainEventSubscriber } from "./domain-events.js";
38
+ import { PositionIdRegistry, buildRehydrateItems, } from "./position-id-registry.js";
35
39
  import { VERSION } from "../version.js";
36
40
  import { randomUUID } from "node:crypto";
37
- import { trackingContext } from "../tracking/context.js";
38
- import { buildIdentity, createRecordingComponents, createRecordingSubscriber, resolveTelemetryEnabled, } from "../recording/wiring/index.js";
41
+ import { trackingContext } from "../telemetry/context.js";
42
+ import { buildIdentity, } from "../telemetry/identity.js";
39
43
  import { runReconcile } from "./reconcile-run.js";
40
44
  import { KeyedMutex } from "../utils/keyed-mutex.js";
45
+ import { withSpan } from "../telemetry/tracing.js";
46
+ import { registerDslActivePositionsGauge, registerQueueDepthGauge, recordScannerRun, recordSignalQueueWait, recordRuntimeHeartbeat, } from "../telemetry/metrics.js";
47
+ import { isOtlpEndpointConfigured } from "../telemetry/otel-sdk.js";
48
+ import { identityTraceAttributes, setTraceIdentityAttributes, } from "../telemetry/trace-identity.js";
41
49
  const logger = createLogger("[senpi-core][runtime]");
42
50
  /** Fixed retry interval for the boot-reconcile loop. */
43
51
  const RECONCILE_RETRY_INTERVAL_MS = 15_000;
52
+ /**
53
+ * Signal-queue depth at which the serial consumer is judged to be falling behind. It processes one
54
+ * signal at a time (each may run an LLM decision), so a backlog this deep means inflow is outpacing
55
+ * processing — a `runtime.queue_backpressure` alert. Hardcoded: the depth a stall shows at is a
56
+ * runtime invariant, not a per-recipe tuning knob.
57
+ */
58
+ const SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD = 10;
59
+ /** Default liveness-heartbeat window (~15 min) when `health.window_seconds` is unset. */
60
+ const DEFAULT_LIVENESS_WINDOW_SECONDS = 900;
44
61
  export class Runtime {
45
62
  config;
46
63
  options;
@@ -53,9 +70,13 @@ export class Runtime {
53
70
  // riskGuardian removed — gate decisions are now real-time in checkGate
54
71
  senpi = null;
55
72
  healthStateModule = null;
56
- trackingHandler = null;
57
- recordingSubscriber = null;
58
- recordingBatcher = null;
73
+ domainEventSubscriber = null;
74
+ /** Wallet under which this runtime's local event sink is registered; cleared on teardown. */
75
+ eventSinkAddress = null;
76
+ /** Lazy runtime identity builder; stamps per-trace identity at the signal.process seam. */
77
+ buildTraceIdentity;
78
+ traceIdentity;
79
+ traceIdentityResolved = false;
59
80
  started = false;
60
81
  startedAt = null;
61
82
  /** Serial signal processing: queue and consumer so only one signal is handled at a time. */
@@ -63,13 +84,17 @@ export class Runtime {
63
84
  signalQueueWait = null;
64
85
  signalConsumerStopped = false;
65
86
  signalConsumerDone = null;
87
+ /** Disposer for this runtime's `runtime.queue_depth` gauge observation; set on start, called on stop. */
88
+ queueDepthGaugeDispose = null;
89
+ /** Disposer for this runtime's `dsl.active_positions` gauge observation; set on start, called on stop. */
90
+ dslActivePositionsGaugeDispose = null;
91
+ /** Interval emitting this runtime's liveness heartbeat; set on start, cleared on stop. */
92
+ livenessHeartbeatTimer = null;
66
93
  runActionsForSignal;
67
94
  strategyConfig;
68
95
  actionRuntimeModule;
69
96
  /** Stored so we can call off() in stop() and avoid retaining Runtime via scanner EventEmitter. */
70
97
  onScannerRunComplete = null;
71
- onScannerRunSkip = null;
72
- onScannerRunError = null;
73
98
  /** Cached strategy id resolved from the runtime wallet on first {@link auditQuery} call. */
74
99
  cachedStrategyId = null;
75
100
  /** In-flight resolution promise; ensures concurrent first-burst callers reuse one `listStrategies`. */
@@ -137,20 +162,7 @@ export class Runtime {
137
162
  if (!this.started) {
138
163
  throw new Error("Runtime must be started before accepting external scanner ingests.");
139
164
  }
140
- console.info("[senpi-runtime] [ingest-trace] runtime.ingestExternalScannerData", JSON.stringify({
141
- address: this.strategyConfig.address,
142
- scanner: request.scanner,
143
- hasAsset: request.asset !== undefined,
144
- hasData: request.data !== undefined,
145
- hasSignals: request.signals !== undefined,
146
- dataKeys: request.data ? Object.keys(request.data) : [],
147
- }));
148
165
  const result = await this.scannerModule.ingestExternalScannerData(this.strategyConfig.address, request);
149
- console.info("[senpi-runtime] [ingest-trace] runtime.ingestExternalScannerData → done", JSON.stringify({
150
- accepted: result.accepted,
151
- signalCount: result.signalCount,
152
- contextUpdated: result.contextUpdated,
153
- }));
154
166
  return result;
155
167
  }
156
168
  /**
@@ -309,18 +321,6 @@ export class Runtime {
309
321
  validateDslPositionTracking(this.config, hasDsl);
310
322
  this.bus = createRuntimeBus();
311
323
  this.hookSystem = createHookSystem(this.bus);
312
- // Event tracking: passive bus → EventTracker → BatchingTransport POST (requires JWT-capable API key).
313
- // Without SENPI_API_KEY, NoOpTransport discards all events; warn once at startup.
314
- const trackingApiKey = apiKey ?? process.env.SENPI_AUTH_TOKEN ?? process.env.SENPI_API_KEY;
315
- const trackingBaseUrl = process.env.SENPI_TRACKING_URL ?? TRACKING_API_BASE_URL;
316
- const trackingTransport = trackingApiKey
317
- ? new BatchingTransport({ baseUrl: trackingBaseUrl, apiKey: trackingApiKey })
318
- : (() => {
319
- logger.warn("event tracking disabled: set SENPI_API_KEY (or pass apiKey) so events reach senpi-agent-telemetry; without it NoOpTransport drops all telemetry");
320
- return new NoOpTransport();
321
- })();
322
- const tracker = new EventTracker(trackingTransport, this.config.name, VERSION, this.config.strategy?.wallet ?? "");
323
- this.trackingHandler = createTrackingHandler(this.bus, tracker);
324
324
  this.stateManager = new StateManager(stateDir);
325
325
  await this.stateManager.hydrate();
326
326
  this.positionIdRegistry = new PositionIdRegistry();
@@ -344,41 +344,29 @@ export class Runtime {
344
344
  this.senpi = senpi;
345
345
  this.strategyConfig = resolveStrategyConfig(this.config);
346
346
  await this.rehydratePositionIdRegistry();
347
- // Telemetry recording: subscribe the emitter to the bus (on by default; degrades to a noop
348
- // emitter when disabled or when no token is present — never a boot failure). Identity + config
349
- // fingerprint are built lazily from the unresolved config — only on the live path, and never
350
- // from the resolved config (which would inline secrets).
351
- const telemetryConfig = this.config.telemetry;
352
- const recording = createRecordingComponents({
353
- enabled: resolveTelemetryEnabled(telemetryConfig?.enabled, process.env.SENPI_TELEMETRY_ENABLED),
354
- token: trackingApiKey,
355
- identity: () => buildIdentity({
356
- name: this.config.name,
357
- recipeVersion: this.config.version,
358
- strategyAddress: this.strategyConfig.address,
359
- bootId: this.runtimeBootId,
360
- unresolvedConfig: this.options.unresolvedConfig,
361
- }),
362
- scopeVersion: VERSION,
363
- baseUrl: telemetryConfig?.url,
364
- // v2.0.0: recipe carries flush_interval_seconds; the transport expects ms.
365
- flushIntervalMs: typeof telemetryConfig?.flush_interval_seconds === "number"
366
- ? telemetryConfig.flush_interval_seconds * 1000
367
- : undefined,
347
+ // The OTLP exporter env is the telemetry master switch: the OTel SDK (stood up at plugin
348
+ // register()) and the domain-event log both gate on it.
349
+ const telemetryEnabled = isOtlpEndpointConfigured();
350
+ // Lazy identity builder. Built only on the live path (never from the resolved
351
+ // config, which would inline secrets).
352
+ const buildIdentityEnvelope = () => buildIdentity({
353
+ name: this.config.name,
354
+ recipeVersion: this.config.version,
355
+ strategyAddress: this.strategyConfig.address,
356
+ group: this.config.group,
357
+ bootId: this.runtimeBootId,
358
+ unresolvedConfig: this.options.unresolvedConfig,
368
359
  });
369
- this.recordingBatcher = recording.batcher;
370
- // Only attach the subscriber when telemetry is live (batcher present): with a noop emitter,
371
- // per-event record assembly on the serial signal path would be pure dead work.
372
- if (recording.batcher) {
373
- this.recordingSubscriber = createRecordingSubscriber(this.bus, recording.emitter, {
374
- unresolvedConfig: this.options.unresolvedConfig,
375
- // Narrow read-only accessor over the registry — recording never sees the
376
- // registry or RuntimeContext; sync map read, safe on the bus handler path.
377
- resolvePositionId: (address, dex, asset) => this.positionIdRegistry.get(positionRegistryKey(address, dex, asset))?.positionId,
378
- });
360
+ // Share the builder with the signal.process root span (resolved lazily, once per boot).
361
+ this.buildTraceIdentity = buildIdentityEnvelope;
362
+ // The event-log half of the real domain events (position.*/dsl.*) → OTel Logs API. Rides the
363
+ // OTel SDK's log provider. Per-event assembly on the serial signal path is dead work when
364
+ // telemetry is off, so only attach when enabled.
365
+ if (telemetryEnabled) {
366
+ this.domainEventSubscriber = createDomainEventSubscriber(this.bus);
379
367
  }
380
368
  // Everything from here to `started = true` can throw; without the catch, stop() would
381
- // early-return on `!this.started` and the recording batcher/subscriber would leak.
369
+ // early-return on `!this.started` and the domain-event subscriber would leak.
382
370
  try {
383
371
  // try {
384
372
  // const { reconcileStateWithBackend } = await import("./reconcile-state.js");
@@ -395,6 +383,10 @@ export class Runtime {
395
383
  const riskBlock = this.config.risk;
396
384
  const strategyAddress = this.strategyConfig.address;
397
385
  const auditLog = riskBlock ? createRiskAuditLog(stateDir) : undefined;
386
+ // Local domain-event ring. Always on (independent of the OTLP collector) so the agent can query
387
+ // the trade narrative locally; the singleton logger taps it by wallet via this registration.
388
+ registerEventSink(strategyAddress, createEventStore(stateDir, strategyAddress));
389
+ this.eventSinkAddress = strategyAddress;
398
390
  // In-memory strategy-lifecycle tracker: owns the self-pause state that
399
391
  // RiskGuard used to hold and fires the hook + notification on a running→paused
400
392
  // transition. RiskGuard stays stateless and only reports gate evaluations.
@@ -423,7 +415,7 @@ export class Runtime {
423
415
  getDslStatesForStrategy: scannerProviders?.getDslStatesForStrategy
424
416
  ?? (async (address) => this.stateManager.listDslStates(address)),
425
417
  };
426
- this.scannerModule = createScannerModuleFromRuntimeConfig(this.config, layout.scannerRoot, augmentedProviders);
418
+ this.scannerModule = createScannerModuleFromRuntimeConfig(this.config, layout.scannerRoot, augmentedProviders, undefined, buildIdentityEnvelope);
427
419
  logger.info("Runtime.start() scanner module created, calling start()");
428
420
  // Pre-load max leverage cache (non-blocking — log warning on failure)
429
421
  const maxLeverageCache = new MaxLeverageCache(senpi);
@@ -493,6 +485,7 @@ export class Runtime {
493
485
  // against the trade loop on the same reconcileMutex key, and can resolve the boot gate.
494
486
  reconcileSerialize: (fn) => this.reconcileMutex.withKeyLock("reconcile", fn),
495
487
  reconcileGateResolve: () => this.resolveReconcileGate(),
488
+ identity: buildIdentityEnvelope,
496
489
  });
497
490
  }
498
491
  else {
@@ -529,34 +522,40 @@ export class Runtime {
529
522
  },
530
523
  });
531
524
  this.onScannerRunComplete = (event) => {
532
- console.info("[senpi-runtime] [ingest-trace] onScannerRunComplete → enqueue", JSON.stringify({
533
- address: event.result.address,
534
- scannerId: event.result.scannerId,
535
- signalCount: Array.isArray(event.result.signals) ? event.result.signals.length : 0,
536
- status: event.result.status,
537
- queueDepth: this.signalQueue.length + 1,
538
- }));
539
- const correlationId = randomUUID();
540
- // Telemetry-owned per-scan-run id (NOT the deprecated correlation id): stamped on the
541
- // scanner_run emit and echoed through the pass so scan + pass land in one trace.
542
- const runId = randomUUID();
543
- this.signalQueue.push({ result: event.result, correlationId, enqueuedAt: Date.now(), runId });
525
+ // Per-scan-run id: stamped on the scanner_run emit and echoed through the pass so scan +
526
+ // pass land in one trace. Prefer the id minted at the scanner.run span so scan + pass
527
+ // share one senpi.run.id.
528
+ const runId = event.result.runId ?? randomUUID();
529
+ this.signalQueue.push({ result: event.result, enqueuedAt: Date.now(), runId });
530
+ // Edge-triggered backpressure alarm: depth rises one enqueue at a time, so `=== threshold`
531
+ // fires exactly once as it crosses up and re-arms only after the consumer drains back below.
532
+ if (this.signalQueue.length === SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD) {
533
+ emitEvent(logger, () => buildQueueBackpressureEvent({
534
+ depth: this.signalQueue.length,
535
+ threshold: SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD,
536
+ scannerId: event.result.scannerId,
537
+ address: this.strategyConfig.address,
538
+ }));
539
+ }
544
540
  if (this.signalQueueWait) {
545
541
  const w = this.signalQueueWait;
546
542
  this.signalQueueWait = null;
547
543
  w();
548
544
  }
549
- // Emit scanner run for tracking (startedAt is data stamping for telemetry durations)
550
- this.bus.emit("tracking.scanner_run", {
545
+ // Scanner-run summary payload (startedAt is data stamping for telemetry durations).
546
+ const scannerRunPayload = {
551
547
  address: event.result.address,
552
548
  data: {
553
549
  ...event.result,
554
550
  startedAt: event.startedAt,
555
551
  runId,
556
- __correlationId: correlationId,
557
552
  },
558
553
  timestamp: new Date().toISOString(),
559
- });
554
+ };
555
+ // First-class event log. The build runs inside emitEvent's guard — telemetry never
556
+ // disturbs the scan path.
557
+ emitEvent(logger, () => buildScannerSummaryEvent(scannerRunPayload, Date.now()));
558
+ recordScannerRun({ scannerId: event.result.scannerId, outcome: event.result.status });
560
559
  // Forward scanner summary alerts (e.g. watchdog) to notification service
561
560
  const alerts = event.result.summary?.alerts;
562
561
  if (Array.isArray(alerts) && alerts.length > 0) {
@@ -566,37 +565,6 @@ export class Runtime {
566
565
  }
567
566
  };
568
567
  this.scannerModule.on("scanner:run:complete", this.onScannerRunComplete);
569
- // Surface skipped/errored scanner runs on the bus for telemetry (add-only observation events —
570
- // distinct names so existing `tracking.scanner_run` consumers see an unchanged payload mix).
571
- // Neither enqueues a signal: only completed runs feed the signal queue.
572
- this.onScannerRunSkip = (event) => {
573
- this.bus.emit("tracking.scanner_run_skipped", {
574
- address: event.address,
575
- data: {
576
- scannerId: event.scannerId,
577
- status: event.status,
578
- reason: event.reason,
579
- timestamp: event.timestamp,
580
- startedAt: event.startedAt,
581
- },
582
- timestamp: new Date().toISOString(),
583
- });
584
- };
585
- this.onScannerRunError = (event) => {
586
- this.bus.emit("tracking.scanner_run_error", {
587
- address: event.address,
588
- data: {
589
- scannerId: event.scannerId,
590
- status: "error",
591
- reason: typeof event.error === "string" ? event.error : String(event.error),
592
- timestamp: event.timestamp,
593
- startedAt: event.startedAt,
594
- },
595
- timestamp: new Date().toISOString(),
596
- });
597
- };
598
- this.scannerModule.on("scanner:run:skip", this.onScannerRunSkip);
599
- this.scannerModule.on("scanner:run:error", this.onScannerRunError);
600
568
  this.signalConsumerDone = this.runSignalConsumer();
601
569
  logger.info("Runtime.start() invoking scannerModule.start()");
602
570
  await this.scannerModule.start();
@@ -640,14 +608,6 @@ export class Runtime {
640
608
  this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
641
609
  this.onScannerRunComplete = null;
642
610
  }
643
- if (this.onScannerRunSkip != null) {
644
- this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
645
- this.onScannerRunSkip = null;
646
- }
647
- if (this.onScannerRunError != null) {
648
- this.scannerModule.off("scanner:run:error", this.onScannerRunError);
649
- this.onScannerRunError = null;
650
- }
651
611
  if (this.actionNotificationSubscriber != null) {
652
612
  this.actionNotificationSubscriber.dispose();
653
613
  this.actionNotificationSubscriber = null;
@@ -664,22 +624,66 @@ export class Runtime {
664
624
  catch (cleanupErr) {
665
625
  logger.warn("[runtime] partial-state cleanup error on failed start (ignored)", { cleanupErr });
666
626
  }
667
- // Recording-only cleanup (telemetry must not leak on a failed boot); the error rethrows.
668
- await this.disposeRecording();
627
+ // Telemetry cleanup (must not leak on a failed boot); the error rethrows.
628
+ await this.disposeSubscribers();
669
629
  throw err;
670
630
  }
671
- this.bus.emit("tracking.runtime_started", {
631
+ // First-class event log. Carries the UNRESOLVED config hash (the form `computeConfigHash` uses),
632
+ // so the printed hash matches `senpi.config.hash` on the spans/logs. The build runs inside
633
+ // emitEvent's guard — telemetry never blocks boot.
634
+ emitEvent(logger, () => buildRuntimeStartedEvent({
672
635
  address: this.strategyConfig.address,
673
- data: {
674
- config: this.config,
675
- strategyAddress: this.strategyConfig.address,
676
- runtimeVersion: VERSION,
677
- hasDsl: this.dslPlugin != null,
678
- scannerCount: this.config.scanners?.length ?? 0,
679
- actionCount: this.config.actions?.length ?? 0,
680
- },
681
- timestamp: new Date().toISOString(),
636
+ recipeName: this.config.name,
637
+ recipeVersion: this.config.version,
638
+ runtimeVersion: VERSION,
639
+ scannerCount: this.config.scanners?.length ?? 0,
640
+ actionCount: this.config.actions?.length ?? 0,
641
+ hasDsl: this.dslPlugin != null,
642
+ unresolvedConfig: this.options.unresolvedConfig,
643
+ slots: this.strategyConfig.slots,
644
+ tradingRisk: this.strategyConfig.tradingRisk,
645
+ marginPerSlot: this.strategyConfig.marginPerSlot,
646
+ marginPct: this.strategyConfig.marginPct,
647
+ defaultLeverage: this.strategyConfig.defaultLeverage,
648
+ }));
649
+ // Observable gauge of the serial signal-queue depth — its absence/stall is alertable without a
650
+ // trace or log scan. Reads the live depth at each collection; disposed in stop().
651
+ const walletDim = this.strategyConfig.address.toLowerCase();
652
+ const group = this.config.group;
653
+ this.queueDepthGaugeDispose = registerQueueDepthGauge({
654
+ strategyAddress: walletDim,
655
+ group,
656
+ read: () => this.getSignalQueueDepth(),
682
657
  });
658
+ // Liveness heartbeat: a positive tick every window, dimensioned by wallet/group. A dead runtime
659
+ // can't emit a "down" event — only the absence of these ticks detects it. Fire one now so a
660
+ // runtime that dies before its first window still leaves a mark. Unref'd so it never holds the
661
+ // process open; cleared in stop().
662
+ const windowSec = this.config.health?.window_seconds ?? DEFAULT_LIVENESS_WINDOW_SECONDS;
663
+ recordRuntimeHeartbeat({ strategyAddress: walletDim, group });
664
+ this.livenessHeartbeatTimer = setInterval(() => recordRuntimeHeartbeat({ strategyAddress: walletDim, group }), windowSec * 1000);
665
+ this.livenessHeartbeatTimer.unref?.();
666
+ // Observable gauge of active DSL-managed positions, split by exit phase. Only meaningful when this
667
+ // runtime runs the exit engine; counts this wallet's positions live at each collection (no snapshot).
668
+ if (this.dslPlugin) {
669
+ this.dslActivePositionsGaugeDispose = registerDslActivePositionsGauge({
670
+ strategyAddress: walletDim,
671
+ group,
672
+ read: () => {
673
+ let phase1 = 0;
674
+ let phase2 = 0;
675
+ for (const p of this.dslPlugin?.listActivePositions() ?? []) {
676
+ if (p.address?.toLowerCase() !== walletDim)
677
+ continue;
678
+ if (p.phase === 2)
679
+ phase2++;
680
+ else
681
+ phase1++;
682
+ }
683
+ return { phase1, phase2 };
684
+ },
685
+ });
686
+ }
683
687
  }
684
688
  /**
685
689
  * Cancel exchange SLs and archive DSL state for the strategy, then log removal.
@@ -695,7 +699,24 @@ export class Runtime {
695
699
  }
696
700
  /** Tear down DSL for the runtime strategy before deleting the runtime (gateway / CLI). */
697
701
  async teardownDslForRuntimeDeletion() {
698
- return this.removeStrategy(this.strategyConfig.address);
702
+ await this.removeStrategy(this.strategyConfig.address);
703
+ }
704
+ /**
705
+ * Resolve the per-boot trace identity once. The envelope is immutable per boot, so build it
706
+ * lazily and cache it. The build is guarded: a failing identity builder must never abort
707
+ * signal processing.
708
+ */
709
+ resolveTraceIdentity() {
710
+ if (!this.traceIdentityResolved) {
711
+ this.traceIdentityResolved = true;
712
+ try {
713
+ this.traceIdentity = this.buildTraceIdentity?.();
714
+ }
715
+ catch (err) {
716
+ logger.warn("[runtime] failed to build trace identity; continuing without it", { err });
717
+ }
718
+ }
719
+ return this.traceIdentity;
699
720
  }
700
721
  /**
701
722
  * Single consumer: process one scanner:run:complete at a time so slot counts stay correct.
@@ -714,84 +735,113 @@ export class Runtime {
714
735
  if (!item)
715
736
  continue;
716
737
  // Pin tracking context for this signal flow so LLM spans (emitted by
717
- // OpenClawLlmDecision / AnthropicLlmDecision) can stamp correlation_id,
738
+ // OpenClawLlmDecision / AnthropicLlmDecision) can stamp run id,
718
739
  // address, recipe_name, runtime_id, and runtime_version automatically.
719
740
  const queuedItem = item;
741
+ // Resolved once here so the same identity rides both the trace (via the span stamp)
742
+ // and the logs (via the context bag below) — one source, no drift.
743
+ const identity = this.resolveTraceIdentity();
720
744
  await trackingContext.run({
721
- correlationId: queuedItem.correlationId,
745
+ runId: queuedItem.runId,
722
746
  address: queuedItem.result.address,
723
747
  recipeName: this.config.name,
724
748
  runtimeId: this.options.runtimeId ?? "",
725
749
  runtimeVersion: VERSION,
726
750
  runtimeBootId: this.runtimeBootId,
751
+ identityAttributes: identity
752
+ ? identityTraceAttributes(identity)
753
+ : undefined,
727
754
  }, async () => {
728
- try {
729
- // Block until the first boot reconcile succeeds (or runtime is stopped).
730
- await this.reconcileGate?.promise;
731
- // If stop() resolved the gate during shutdown, do NOT trade and do NOT
732
- // contend for reconcileMutex — a boot reconcile that is still in-flight
733
- // (or stuck) holds that key, and blocking here would hang stop()'s
734
- // `await signalConsumerDone`. Bail before the mutex acquire.
735
- if (this.signalConsumerStopped) {
736
- return;
737
- }
738
- // Evict the blackout backlog. Items enqueued before the gate
739
- // resolved were formed while the runtime could not confirm its own
740
- // positions (unreconciled / exchange-flaky), so acting on them now is
741
- // stale — the scanner re-emits fresh signals once reconciled. Strict
742
- // `<`: items enqueued at/after gate-open are processed normally. This
743
- // is gate-scoped, NOT a queue TTL — nothing is evicted once the
744
- // backlog is cleared.
745
- if (this.gateResolvedAt != null && queuedItem.enqueuedAt < this.gateResolvedAt) {
746
- this.staleSignalsEvicted++;
747
- logger.warn("[runtime] evicting stale pre-reconcile signal (enqueued before gate open)", {
748
- address: queuedItem.result.address,
749
- scannerId: queuedItem.result.scannerId,
750
- enqueuedAt: queuedItem.enqueuedAt,
751
- gateResolvedAt: this.gateResolvedAt,
752
- totalEvicted: this.staleSignalsEvicted,
753
- });
754
- return;
755
- }
756
- console.info("[senpi-runtime] [ingest-trace] consumer dequeue → runActionsForSignal", JSON.stringify({
757
- address: queuedItem.result.address,
758
- scannerId: queuedItem.result.scannerId,
759
- signalCount: Array.isArray(queuedItem.result.signals) ? queuedItem.result.signals.length : 0,
760
- queueRemaining: this.signalQueue.length,
761
- }));
762
- const usedSlotsByStrategy = await this.stateManager.getUsedSlotsByStrategy([this.strategyConfig.address]);
763
- const payload = buildActionScanPayload(queuedItem.result, this.strategyConfig, {
764
- usedSlotsByStrategy,
765
- address: queuedItem.result.address,
766
- });
767
- const hookEvent = {
768
- type: HOOK_EVENT.ON_SIGNAL_DETECTED,
769
- runtimeName: this.config.name,
770
- address: queuedItem.result.address,
771
- data: {
772
- ...payload,
773
- __correlationId: queuedItem.correlationId,
774
- __scanRunId: queuedItem.runId,
775
- },
776
- timestamp: new Date().toISOString(),
777
- };
778
- this.bus.emit(HOOK_EVENT.ON_SIGNAL_DETECTED, hookEvent);
779
- // Serialize trade loop vs reconcile — reconcileMutex outer, key-lock inner.
780
- await this.reconcileMutex.withKeyLock("reconcile", () => this.runActionsForSignal(hookEvent));
781
- console.info("[senpi-runtime] [ingest-trace] runActionsForSignal complete", JSON.stringify({ address: queuedItem.result.address, scannerId: queuedItem.result.scannerId }));
755
+ // Block until the first boot reconcile succeeds (or runtime is stopped).
756
+ await this.reconcileGate?.promise;
757
+ // If stop() resolved the gate during shutdown, do NOT trade and do NOT
758
+ // contend for reconcileMutex — a boot reconcile that is still in-flight
759
+ // (or stuck) holds that key, and blocking here would hang stop()'s
760
+ // `await signalConsumerDone`. Bail before the mutex acquire.
761
+ if (this.signalConsumerStopped) {
762
+ return;
782
763
  }
783
- catch (err) {
784
- const message = err instanceof Error ? err.message : String(err);
785
- const stack = err instanceof Error ? err.stack : undefined;
786
- console.error("[senpi-runtime] signal processing error", message, stack ?? "");
787
- this.bus.emit("signal_processing_error", {
788
- error: err,
764
+ // Evict the blackout backlog. Items enqueued before the gate
765
+ // resolved were formed while the runtime could not confirm its own
766
+ // positions (unreconciled / exchange-flaky), so acting on them now is
767
+ // stale — the scanner re-emits fresh signals once reconciled. Strict
768
+ // `<`: items enqueued at/after gate-open are processed normally. This
769
+ // is gate-scoped, NOT a queue TTL — nothing is evicted once the
770
+ // backlog is cleared.
771
+ if (this.gateResolvedAt != null && queuedItem.enqueuedAt < this.gateResolvedAt) {
772
+ this.staleSignalsEvicted++;
773
+ logger.warn("[runtime] evicting stale pre-reconcile signal (enqueued before gate open)", {
789
774
  address: queuedItem.result.address,
790
775
  scannerId: queuedItem.result.scannerId,
791
- runId: queuedItem.runId,
792
- timestamp: new Date().toISOString(),
776
+ enqueuedAt: queuedItem.enqueuedAt,
777
+ gateResolvedAt: this.gateResolvedAt,
778
+ totalEvicted: this.staleSignalsEvicted,
793
779
  });
780
+ return;
781
+ }
782
+ // Root span born at dequeue; spans for the action handlers nest beneath it.
783
+ // Identity (incl. strategy address) rides every span via the per-trace stamp below;
784
+ // run.id is the join key back to the producing scanner run.
785
+ const spanAttrs = {};
786
+ if (queuedItem.runId) {
787
+ spanAttrs["senpi.run.id"] = queuedItem.runId;
788
+ }
789
+ // Carry the producing scanner id so the signal span joins/filters by scanner,
790
+ // matching the key the sibling scanner.run span stamps.
791
+ if (queuedItem.result.scannerId) {
792
+ spanAttrs["senpi.scanner.id"] = queuedItem.result.scannerId;
794
793
  }
794
+ await withSpan("signal.process", spanAttrs, async (span) => {
795
+ if (identity)
796
+ await setTraceIdentityAttributes(identity);
797
+ // Serial FIFO: time spent waiting behind earlier signals, and the depth still
798
+ // behind this one — the backpressure a per-pass duration alone can't show.
799
+ // Measured from enqueue, but the pre-gate backlog is evicted above before the
800
+ // span opens, so every signal that reaches here was enqueued after the (one-shot)
801
+ // reconcile gate resolved — i.e. this is pure FIFO wait, not gate wait.
802
+ const queueWaitMs = Date.now() - queuedItem.enqueuedAt;
803
+ span.setAttribute("senpi.queue.wait_ms", queueWaitMs);
804
+ span.setAttribute("senpi.queue.depth", this.signalQueue.length);
805
+ recordSignalQueueWait(queueWaitMs / 1000, { scannerId: queuedItem.result.scannerId });
806
+ try {
807
+ const usedSlotsByStrategy = await this.stateManager.getUsedSlotsByStrategy([this.strategyConfig.address]);
808
+ const payload = buildActionScanPayload(queuedItem.result, this.strategyConfig, {
809
+ usedSlotsByStrategy,
810
+ address: queuedItem.result.address,
811
+ });
812
+ const hookEvent = {
813
+ type: HOOK_EVENT.ON_SIGNAL_DETECTED,
814
+ runtimeName: this.config.name,
815
+ address: queuedItem.result.address,
816
+ data: {
817
+ ...payload,
818
+ __scanRunId: queuedItem.runId,
819
+ },
820
+ timestamp: new Date().toISOString(),
821
+ };
822
+ this.bus.emit(HOOK_EVENT.ON_SIGNAL_DETECTED, hookEvent);
823
+ // Serialize trade loop vs reconcile — reconcileMutex outer, key-lock inner.
824
+ await this.reconcileMutex.withKeyLock("reconcile", () => this.runActionsForSignal(hookEvent));
825
+ console.info("[senpi-runtime] [ingest-trace] runActionsForSignal complete", JSON.stringify({ address: queuedItem.result.address, scannerId: queuedItem.result.scannerId }));
826
+ }
827
+ catch (err) {
828
+ const message = err instanceof Error ? err.message : String(err);
829
+ // Structured error log: normalizes to exception.* attributes and, since the catch
830
+ // swallows the error (so withSpan's auto-record never fires), records it on the active
831
+ // span + sets ERROR status. Replaces the prior console.error + manual span calls.
832
+ logger.error("signal processing error", err);
833
+ this.bus.emit("signal_processing_error", {
834
+ error: err,
835
+ address: queuedItem.result.address,
836
+ scannerId: queuedItem.result.scannerId,
837
+ runId: queuedItem.runId,
838
+ timestamp: new Date().toISOString(),
839
+ });
840
+ // First-class event log alongside the legacy bus emit. The build runs inside
841
+ // emitEvent's guard — telemetry never disturbs the consumer.
842
+ emitEvent(logger, () => buildRuntimeErrorEvent("signal_processing", queuedItem.result.scannerId, message));
843
+ }
844
+ });
795
845
  });
796
846
  }
797
847
  }
@@ -852,23 +902,6 @@ export class Runtime {
852
902
  }
853
903
  const durationMs = Date.now() - t0;
854
904
  const { adopted, resized, archived } = result.counts;
855
- // Telemetry must never break reconcile control flow. bus.emit is a Node
856
- // EventEmitter: a listener that throws synchronously makes emit re-throw,
857
- // which would reject this loop and leave the gate forever closed. Contain it.
858
- try {
859
- this.bus.emit("tracking.runtime_reconciled", {
860
- address,
861
- data: { adopted, resized, archived, success: result.success, attempt, durationMs },
862
- timestamp: new Date().toISOString(),
863
- });
864
- }
865
- catch (err) {
866
- logger.warn("[runtime][reconcile] tracking.runtime_reconciled listener threw; contained", {
867
- address,
868
- attempt,
869
- err,
870
- });
871
- }
872
905
  if (result.success) {
873
906
  logger.info("[runtime][reconcile] boot reconcile succeeded", { address, attempt, adopted, resized, archived });
874
907
  this.resolveReconcileGate();
@@ -887,17 +920,20 @@ export class Runtime {
887
920
  gate.resolve();
888
921
  }
889
922
  /**
890
- * Dispose the recording subscriber, then flush + stop the batcher (best-effort). Idempotent;
891
- * shared by stop() and the failed-start cleanup path in start().
923
+ * Dispose the domain-event subscriber. Idempotent; shared by stop() and the failed-start
924
+ * cleanup path in start().
892
925
  */
893
- async disposeRecording() {
894
- if (this.recordingSubscriber) {
895
- this.recordingSubscriber.dispose();
896
- this.recordingSubscriber = null;
926
+ async disposeSubscribers() {
927
+ if (this.domainEventSubscriber) {
928
+ this.domainEventSubscriber.dispose();
929
+ this.domainEventSubscriber = null;
897
930
  }
898
- if (this.recordingBatcher) {
899
- await this.recordingBatcher.shutdown().catch(() => { });
900
- this.recordingBatcher = null;
931
+ if (this.eventSinkAddress !== null) {
932
+ // Drain the sink's queued fire-and-forget appends before dropping it, so a graceful stop
933
+ // doesn't discard events that were enqueued but not yet written.
934
+ await flushEventSink(this.eventSinkAddress);
935
+ unregisterEventSink(this.eventSinkAddress);
936
+ this.eventSinkAddress = null;
901
937
  }
902
938
  }
903
939
  async stop() {
@@ -912,15 +948,21 @@ export class Runtime {
912
948
  }
913
949
  // Resolve the gate so the consumer doesn't hang waiting for a reconcile that won't come.
914
950
  this.reconcileGate?.resolve();
915
- this.bus.emit("tracking.runtime_stopped", {
916
- address: this.strategyConfig.address,
917
- data: {
918
- uptimeMs: Date.now() - (this.startedAt ?? Date.now()),
919
- runtimeVersion: VERSION,
920
- strategyAddress: this.strategyConfig.address,
921
- },
922
- timestamp: new Date().toISOString(),
923
- });
951
+ const uptimeMs = Date.now() - (this.startedAt ?? Date.now());
952
+ // First-class event log. The build runs inside emitEvent's guard — telemetry never blocks shutdown.
953
+ emitEvent(logger, () => buildRuntimeStoppedEvent(VERSION, uptimeMs, this.strategyConfig.address));
954
+ if (this.queueDepthGaugeDispose) {
955
+ this.queueDepthGaugeDispose();
956
+ this.queueDepthGaugeDispose = null;
957
+ }
958
+ if (this.dslActivePositionsGaugeDispose) {
959
+ this.dslActivePositionsGaugeDispose();
960
+ this.dslActivePositionsGaugeDispose = null;
961
+ }
962
+ if (this.livenessHeartbeatTimer) {
963
+ clearInterval(this.livenessHeartbeatTimer);
964
+ this.livenessHeartbeatTimer = null;
965
+ }
924
966
  this.signalConsumerStopped = true;
925
967
  if (this.signalQueueWait) {
926
968
  const w = this.signalQueueWait;
@@ -936,14 +978,6 @@ export class Runtime {
936
978
  this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
937
979
  this.onScannerRunComplete = null;
938
980
  }
939
- if (this.onScannerRunSkip != null) {
940
- this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
941
- this.onScannerRunSkip = null;
942
- }
943
- if (this.onScannerRunError != null) {
944
- this.scannerModule.off("scanner:run:error", this.onScannerRunError);
945
- this.onScannerRunError = null;
946
- }
947
981
  if (this.actionNotificationSubscriber != null) {
948
982
  this.actionNotificationSubscriber.dispose();
949
983
  this.actionNotificationSubscriber = null;
@@ -958,13 +992,7 @@ export class Runtime {
958
992
  this.senpi = null;
959
993
  }
960
994
  this.healthStateModule = null;
961
- if (this.trackingHandler) {
962
- await this.trackingHandler.dispose();
963
- this.trackingHandler = null;
964
- }
965
- // Telemetry recording teardown: stop the subscriber, then flush + stop the batcher (best-effort)
966
- // so the just-recorded runtime_stopped row ships before the process exits.
967
- await this.disposeRecording();
995
+ await this.disposeSubscribers();
968
996
  if (this.bus.removeAllListeners) {
969
997
  this.bus.removeAllListeners();
970
998
  }