@senpi/runtime 3.0.6-dev.runtime-phase-2.20260630134410 → 3.0.6-dev.runtime-phase-2.20260630134511

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 (322) hide show
  1. package/dist/actions/{tracking-helpers.d.ts → action-events.d.ts} +24 -23
  2. package/dist/actions/action-events.d.ts.map +1 -0
  3. package/dist/actions/{tracking-helpers.js → action-events.js} +64 -82
  4. package/dist/actions/action-events.js.map +1 -0
  5. package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
  6. package/dist/actions/close-position/close-position.action.js +8 -17
  7. package/dist/actions/close-position/close-position.action.js.map +1 -1
  8. package/dist/actions/event-derivation.d.ts +7 -6
  9. package/dist/actions/event-derivation.d.ts.map +1 -1
  10. package/dist/actions/event-derivation.js +6 -5
  11. package/dist/actions/event-derivation.js.map +1 -1
  12. package/dist/actions/llm-decision/openclaw-llm-decision.js +5 -5
  13. package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
  14. package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
  15. package/dist/actions/open-position/open-position.action.js +9 -17
  16. package/dist/actions/open-position/open-position.action.js.map +1 -1
  17. package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
  18. package/dist/actions/position-tracker/position-tracker.action.js +1 -2
  19. package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
  20. package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
  21. package/dist/dsl/engine-client/event-mapping.js +0 -3
  22. package/dist/dsl/engine-client/event-mapping.js.map +1 -1
  23. package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
  24. package/dist/dsl/events/build-closed-event.js +0 -1
  25. package/dist/dsl/events/build-closed-event.js.map +1 -1
  26. package/dist/dsl/events/handlers.d.ts.map +1 -1
  27. package/dist/dsl/events/handlers.js +2 -4
  28. package/dist/dsl/events/handlers.js.map +1 -1
  29. package/dist/dsl/events/initial-state.d.ts.map +1 -1
  30. package/dist/dsl/events/initial-state.js +1 -0
  31. package/dist/dsl/events/initial-state.js.map +1 -1
  32. package/dist/dsl/monitor/create-monitor.d.ts +1 -1
  33. package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
  34. package/dist/dsl/monitor/create-monitor.js +0 -30
  35. package/dist/dsl/monitor/create-monitor.js.map +1 -1
  36. package/dist/dsl/monitor/dsl-failure-events.d.ts +42 -0
  37. package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -0
  38. package/dist/dsl/monitor/dsl-failure-events.js +73 -0
  39. package/dist/dsl/monitor/dsl-failure-events.js.map +1 -0
  40. package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
  41. package/dist/dsl/monitor/process-one-position.js +46 -158
  42. package/dist/dsl/monitor/process-one-position.js.map +1 -1
  43. package/dist/dsl/plugin/index.js +3 -3
  44. package/dist/dsl/plugin/index.js.map +1 -1
  45. package/dist/dsl/plugin/options.d.ts +1 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +45 -58
  48. package/dist/index.js.map +1 -1
  49. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts +2 -0
  50. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts.map +1 -1
  51. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js +24 -3
  52. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js.map +1 -1
  53. package/dist/runtime/create-runtime-context.d.ts +1 -1
  54. package/dist/runtime/create-runtime-context.d.ts.map +1 -1
  55. package/dist/runtime/create-scanner-module.d.ts +1 -1
  56. package/dist/runtime/domain-events.d.ts +4 -4
  57. package/dist/runtime/domain-events.d.ts.map +1 -1
  58. package/dist/runtime/domain-events.js +4 -4
  59. package/dist/runtime/domain-events.js.map +1 -1
  60. package/dist/runtime/lifecycle-events.d.ts +46 -3
  61. package/dist/runtime/lifecycle-events.d.ts.map +1 -1
  62. package/dist/runtime/lifecycle-events.js +66 -5
  63. package/dist/runtime/lifecycle-events.js.map +1 -1
  64. package/dist/{tracking → runtime}/position-id-registry.d.ts +14 -1
  65. package/dist/runtime/position-id-registry.d.ts.map +1 -0
  66. package/dist/{tracking → runtime}/position-id-registry.js +20 -3
  67. package/dist/runtime/position-id-registry.js.map +1 -0
  68. package/dist/runtime/run.d.ts +2 -4
  69. package/dist/runtime/run.d.ts.map +1 -1
  70. package/dist/runtime/run.js.map +1 -1
  71. package/dist/runtime/runtime-schema.d.ts +156 -419
  72. package/dist/runtime/runtime-schema.d.ts.map +1 -1
  73. package/dist/runtime/runtime-schema.js +8 -28
  74. package/dist/runtime/runtime-schema.js.map +1 -1
  75. package/dist/runtime/runtime.d.ts +10 -9
  76. package/dist/runtime/runtime.d.ts.map +1 -1
  77. package/dist/runtime/runtime.js +116 -182
  78. package/dist/runtime/runtime.js.map +1 -1
  79. package/dist/runtime/supervisor/long-running-supervisor.d.ts +3 -0
  80. package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
  81. package/dist/runtime/supervisor/long-running-supervisor.js +27 -53
  82. package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
  83. package/dist/runtime-api/intake/intake-events.d.ts +44 -0
  84. package/dist/runtime-api/intake/intake-events.d.ts.map +1 -0
  85. package/dist/runtime-api/intake/intake-events.js +131 -0
  86. package/dist/runtime-api/intake/intake-events.js.map +1 -0
  87. package/dist/runtime-api/intake/scanner-launcher.d.ts +2 -0
  88. package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
  89. package/dist/runtime-api/intake/scanner-launcher.js +58 -15
  90. package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
  91. package/dist/runtime-api/intake/supervisor-events.d.ts +61 -0
  92. package/dist/runtime-api/intake/supervisor-events.d.ts.map +1 -0
  93. package/dist/runtime-api/intake/supervisor-events.js +173 -0
  94. package/dist/runtime-api/intake/supervisor-events.js.map +1 -0
  95. package/dist/runtime-api/routes/signals.d.ts.map +1 -1
  96. package/dist/runtime-api/routes/signals.js +119 -27
  97. package/dist/runtime-api/routes/signals.js.map +1 -1
  98. package/dist/runtime-api/server.d.ts.map +1 -1
  99. package/dist/runtime-api/server.js +10 -1
  100. package/dist/runtime-api/server.js.map +1 -1
  101. package/dist/scanners/engine/data-providers.d.ts +1 -1
  102. package/dist/scanners/engine/engine.d.ts.map +1 -1
  103. package/dist/scanners/engine/engine.js +3 -0
  104. package/dist/scanners/engine/engine.js.map +1 -1
  105. package/dist/scanners/runtime-module.d.ts +1 -1
  106. package/dist/senpi/client.d.ts.map +1 -1
  107. package/dist/senpi/client.js +51 -6
  108. package/dist/senpi/client.js.map +1 -1
  109. package/dist/state/state-events.d.ts +13 -0
  110. package/dist/state/state-events.d.ts.map +1 -0
  111. package/dist/state/state-events.js +22 -0
  112. package/dist/state/state-events.js.map +1 -0
  113. package/dist/state/state-manager.d.ts.map +1 -1
  114. package/dist/state/state-manager.js +14 -1
  115. package/dist/state/state-manager.js.map +1 -1
  116. package/dist/telemetry/attributes.d.ts +13 -0
  117. package/dist/telemetry/attributes.d.ts.map +1 -0
  118. package/dist/telemetry/attributes.js +22 -0
  119. package/dist/telemetry/attributes.js.map +1 -0
  120. package/dist/{tracking → telemetry}/context.d.ts +6 -5
  121. package/dist/telemetry/context.d.ts.map +1 -0
  122. package/dist/{tracking → telemetry}/context.js +4 -4
  123. package/dist/telemetry/context.js.map +1 -0
  124. package/dist/{recording/records.d.ts → telemetry/event-builders.d.ts} +138 -32
  125. package/dist/telemetry/event-builders.d.ts.map +1 -0
  126. package/dist/{recording/wiring/record-builders.js → telemetry/event-builders.js} +90 -39
  127. package/dist/telemetry/event-builders.js.map +1 -0
  128. package/dist/telemetry/identity.d.ts +72 -0
  129. package/dist/telemetry/identity.d.ts.map +1 -0
  130. package/dist/{recording/wiring → telemetry}/identity.js +6 -2
  131. package/dist/telemetry/identity.js.map +1 -0
  132. package/dist/telemetry/metrics.d.ts +137 -15
  133. package/dist/telemetry/metrics.d.ts.map +1 -1
  134. package/dist/telemetry/metrics.js +357 -74
  135. package/dist/telemetry/metrics.js.map +1 -1
  136. package/dist/telemetry/otel-sdk.d.ts +1 -1
  137. package/dist/telemetry/otel-sdk.js +1 -1
  138. package/dist/telemetry/spans/constants.d.ts.map +1 -0
  139. package/dist/telemetry/spans/constants.js.map +1 -0
  140. package/dist/{tracking → telemetry}/spans/gen-ai.d.ts +4 -0
  141. package/dist/telemetry/spans/gen-ai.d.ts.map +1 -0
  142. package/dist/{tracking → telemetry}/spans/gen-ai.js +9 -0
  143. package/dist/telemetry/spans/gen-ai.js.map +1 -0
  144. package/dist/telemetry/spans/index.d.ts.map +1 -0
  145. package/dist/telemetry/spans/index.js.map +1 -0
  146. package/dist/telemetry/spans/redact-pii.d.ts.map +1 -0
  147. package/dist/{tracking → telemetry}/spans/redact-pii.js +4 -1
  148. package/dist/telemetry/spans/redact-pii.js.map +1 -0
  149. package/dist/telemetry/spans/sanitize-payload.d.ts.map +1 -0
  150. package/dist/telemetry/spans/sanitize-payload.js.map +1 -0
  151. package/dist/telemetry/spans/span-hooks.d.ts +18 -0
  152. package/dist/telemetry/spans/span-hooks.d.ts.map +1 -0
  153. package/dist/{tracking → telemetry}/spans/span-hooks.js +183 -28
  154. package/dist/telemetry/spans/span-hooks.js.map +1 -0
  155. package/dist/telemetry/tool-plane.d.ts +31 -0
  156. package/dist/telemetry/tool-plane.d.ts.map +1 -0
  157. package/dist/telemetry/tool-plane.js +70 -0
  158. package/dist/telemetry/tool-plane.js.map +1 -0
  159. package/dist/telemetry/trace-identity.d.ts +1 -1
  160. package/dist/telemetry/trace-identity.d.ts.map +1 -1
  161. package/dist/telemetry/trace-identity.js +3 -0
  162. package/dist/telemetry/trace-identity.js.map +1 -1
  163. package/dist/types/dsl/index.d.ts +8 -9
  164. package/dist/types/dsl/index.d.ts.map +1 -1
  165. package/dist/types/runtime.d.ts +1 -1
  166. package/dist/types/runtime.d.ts.map +1 -1
  167. package/dist/utils/event-catalog.d.ts +239 -2
  168. package/dist/utils/event-catalog.d.ts.map +1 -1
  169. package/dist/utils/event-catalog.js +53 -2
  170. package/dist/utils/event-catalog.js.map +1 -1
  171. package/dist/utils/logger.d.ts +5 -0
  172. package/dist/utils/logger.d.ts.map +1 -1
  173. package/dist/utils/logger.js +21 -12
  174. package/dist/utils/logger.js.map +1 -1
  175. package/package.json +1 -1
  176. package/dist/actions/tracking-helpers.d.ts.map +0 -1
  177. package/dist/actions/tracking-helpers.js.map +0 -1
  178. package/dist/recording/emitter.d.ts +0 -27
  179. package/dist/recording/emitter.d.ts.map +0 -1
  180. package/dist/recording/emitter.js +0 -2
  181. package/dist/recording/emitter.js.map +0 -1
  182. package/dist/recording/identity.d.ts +0 -34
  183. package/dist/recording/identity.d.ts.map +0 -1
  184. package/dist/recording/identity.js +0 -2
  185. package/dist/recording/identity.js.map +0 -1
  186. package/dist/recording/index.d.ts +0 -11
  187. package/dist/recording/index.d.ts.map +0 -1
  188. package/dist/recording/index.js +0 -2
  189. package/dist/recording/index.js.map +0 -1
  190. package/dist/recording/noop-emitter.d.ts +0 -23
  191. package/dist/recording/noop-emitter.d.ts.map +0 -1
  192. package/dist/recording/noop-emitter.js +0 -22
  193. package/dist/recording/noop-emitter.js.map +0 -1
  194. package/dist/recording/otel/attributes.d.ts +0 -37
  195. package/dist/recording/otel/attributes.d.ts.map +0 -1
  196. package/dist/recording/otel/attributes.js +0 -131
  197. package/dist/recording/otel/attributes.js.map +0 -1
  198. package/dist/recording/otel/constants.d.ts +0 -123
  199. package/dist/recording/otel/constants.d.ts.map +0 -1
  200. package/dist/recording/otel/constants.js +0 -172
  201. package/dist/recording/otel/constants.js.map +0 -1
  202. package/dist/recording/otel/ids.d.ts +0 -25
  203. package/dist/recording/otel/ids.d.ts.map +0 -1
  204. package/dist/recording/otel/ids.js +0 -37
  205. package/dist/recording/otel/ids.js.map +0 -1
  206. package/dist/recording/otel/index.d.ts +0 -13
  207. package/dist/recording/otel/index.d.ts.map +0 -1
  208. package/dist/recording/otel/index.js +0 -9
  209. package/dist/recording/otel/index.js.map +0 -1
  210. package/dist/recording/otel/mappers.d.ts +0 -100
  211. package/dist/recording/otel/mappers.d.ts.map +0 -1
  212. package/dist/recording/otel/mappers.js +0 -684
  213. package/dist/recording/otel/mappers.js.map +0 -1
  214. package/dist/recording/otel/otel-emitter.d.ts +0 -43
  215. package/dist/recording/otel/otel-emitter.d.ts.map +0 -1
  216. package/dist/recording/otel/otel-emitter.js +0 -99
  217. package/dist/recording/otel/otel-emitter.js.map +0 -1
  218. package/dist/recording/otel/redaction.d.ts +0 -47
  219. package/dist/recording/otel/redaction.d.ts.map +0 -1
  220. package/dist/recording/otel/redaction.js +0 -88
  221. package/dist/recording/otel/redaction.js.map +0 -1
  222. package/dist/recording/records.d.ts.map +0 -1
  223. package/dist/recording/records.js +0 -14
  224. package/dist/recording/records.js.map +0 -1
  225. package/dist/recording/transport/batcher.d.ts +0 -59
  226. package/dist/recording/transport/batcher.d.ts.map +0 -1
  227. package/dist/recording/transport/batcher.js +0 -234
  228. package/dist/recording/transport/batcher.js.map +0 -1
  229. package/dist/recording/transport/client.d.ts +0 -41
  230. package/dist/recording/transport/client.d.ts.map +0 -1
  231. package/dist/recording/transport/client.js +0 -69
  232. package/dist/recording/transport/client.js.map +0 -1
  233. package/dist/recording/transport/config.d.ts +0 -53
  234. package/dist/recording/transport/config.d.ts.map +0 -1
  235. package/dist/recording/transport/config.js +0 -41
  236. package/dist/recording/transport/config.js.map +0 -1
  237. package/dist/recording/transport/index.d.ts +0 -16
  238. package/dist/recording/transport/index.d.ts.map +0 -1
  239. package/dist/recording/transport/index.js +0 -12
  240. package/dist/recording/transport/index.js.map +0 -1
  241. package/dist/recording/transport/rows.d.ts +0 -88
  242. package/dist/recording/transport/rows.d.ts.map +0 -1
  243. package/dist/recording/transport/rows.js +0 -15
  244. package/dist/recording/transport/rows.js.map +0 -1
  245. package/dist/recording/wiring/emitter-factory.d.ts +0 -34
  246. package/dist/recording/wiring/emitter-factory.d.ts.map +0 -1
  247. package/dist/recording/wiring/emitter-factory.js +0 -44
  248. package/dist/recording/wiring/emitter-factory.js.map +0 -1
  249. package/dist/recording/wiring/identity.d.ts +0 -27
  250. package/dist/recording/wiring/identity.d.ts.map +0 -1
  251. package/dist/recording/wiring/identity.js.map +0 -1
  252. package/dist/recording/wiring/index.d.ts +0 -12
  253. package/dist/recording/wiring/index.d.ts.map +0 -1
  254. package/dist/recording/wiring/index.js +0 -9
  255. package/dist/recording/wiring/index.js.map +0 -1
  256. package/dist/recording/wiring/record-builders.d.ts +0 -107
  257. package/dist/recording/wiring/record-builders.d.ts.map +0 -1
  258. package/dist/recording/wiring/record-builders.js.map +0 -1
  259. package/dist/recording/wiring/subscriber.d.ts +0 -28
  260. package/dist/recording/wiring/subscriber.d.ts.map +0 -1
  261. package/dist/recording/wiring/subscriber.js +0 -173
  262. package/dist/recording/wiring/subscriber.js.map +0 -1
  263. package/dist/recording/wiring/values.d.ts +0 -19
  264. package/dist/recording/wiring/values.d.ts.map +0 -1
  265. package/dist/recording/wiring/values.js +0 -49
  266. package/dist/recording/wiring/values.js.map +0 -1
  267. package/dist/tracking/build-rehydrate-items.d.ts +0 -15
  268. package/dist/tracking/build-rehydrate-items.d.ts.map +0 -1
  269. package/dist/tracking/build-rehydrate-items.js +0 -19
  270. package/dist/tracking/build-rehydrate-items.js.map +0 -1
  271. package/dist/tracking/constants.d.ts +0 -24
  272. package/dist/tracking/constants.d.ts.map +0 -1
  273. package/dist/tracking/constants.js +0 -52
  274. package/dist/tracking/constants.js.map +0 -1
  275. package/dist/tracking/context.d.ts.map +0 -1
  276. package/dist/tracking/context.js.map +0 -1
  277. package/dist/tracking/index.d.ts +0 -16
  278. package/dist/tracking/index.d.ts.map +0 -1
  279. package/dist/tracking/index.js +0 -11
  280. package/dist/tracking/index.js.map +0 -1
  281. package/dist/tracking/plugin-event-tracker.d.ts +0 -13
  282. package/dist/tracking/plugin-event-tracker.d.ts.map +0 -1
  283. package/dist/tracking/plugin-event-tracker.js +0 -27
  284. package/dist/tracking/plugin-event-tracker.js.map +0 -1
  285. package/dist/tracking/position-id-registry.d.ts.map +0 -1
  286. package/dist/tracking/position-id-registry.js.map +0 -1
  287. package/dist/tracking/spans/constants.d.ts.map +0 -1
  288. package/dist/tracking/spans/constants.js.map +0 -1
  289. package/dist/tracking/spans/gen-ai.d.ts.map +0 -1
  290. package/dist/tracking/spans/gen-ai.js.map +0 -1
  291. package/dist/tracking/spans/index.d.ts.map +0 -1
  292. package/dist/tracking/spans/index.js.map +0 -1
  293. package/dist/tracking/spans/redact-pii.d.ts.map +0 -1
  294. package/dist/tracking/spans/redact-pii.js.map +0 -1
  295. package/dist/tracking/spans/sanitize-payload.d.ts.map +0 -1
  296. package/dist/tracking/spans/sanitize-payload.js.map +0 -1
  297. package/dist/tracking/spans/span-hooks.d.ts +0 -11
  298. package/dist/tracking/spans/span-hooks.d.ts.map +0 -1
  299. package/dist/tracking/spans/span-hooks.js.map +0 -1
  300. package/dist/tracking/tracker.d.ts +0 -30
  301. package/dist/tracking/tracker.d.ts.map +0 -1
  302. package/dist/tracking/tracker.js +0 -66
  303. package/dist/tracking/tracker.js.map +0 -1
  304. package/dist/tracking/tracking-handler.d.ts +0 -12
  305. package/dist/tracking/tracking-handler.d.ts.map +0 -1
  306. package/dist/tracking/tracking-handler.js +0 -148
  307. package/dist/tracking/tracking-handler.js.map +0 -1
  308. package/dist/tracking/transport.d.ts +0 -35
  309. package/dist/tracking/transport.d.ts.map +0 -1
  310. package/dist/tracking/transport.js +0 -133
  311. package/dist/tracking/transport.js.map +0 -1
  312. package/dist/tracking/types.d.ts +0 -21
  313. package/dist/tracking/types.d.ts.map +0 -1
  314. package/dist/tracking/types.js +0 -6
  315. package/dist/tracking/types.js.map +0 -1
  316. /package/dist/{tracking → telemetry}/spans/constants.d.ts +0 -0
  317. /package/dist/{tracking → telemetry}/spans/constants.js +0 -0
  318. /package/dist/{tracking → telemetry}/spans/index.d.ts +0 -0
  319. /package/dist/{tracking → telemetry}/spans/index.js +0 -0
  320. /package/dist/{tracking → telemetry}/spans/redact-pii.d.ts +0 -0
  321. /package/dist/{tracking → telemetry}/spans/sanitize-payload.d.ts +0 -0
  322. /package/dist/{tracking → telemetry}/spans/sanitize-payload.js +0 -0
@@ -31,22 +31,32 @@ import { createActionNotificationSubscriber, isActionLifecycleNotificationsEnabl
31
31
  import { MaxLeverageCache } from "../leverage/max-leverage-cache.js";
32
32
  import { buildStateLayout } from "./state-layout.js";
33
33
  import { createLogger } from "../utils/logger.js";
34
- import { buildScannerSummaryEvent, buildRuntimeStartedEvent, buildRuntimeStoppedEvent, buildRuntimeErrorEvent, } from "./lifecycle-events.js";
34
+ import { buildScannerSummaryEvent, buildRuntimeStartedEvent, buildRuntimeStoppedEvent, buildRuntimeErrorEvent, buildQueueBackpressureEvent, } from "./lifecycle-events.js";
35
35
  import { emitEvent } from "../actions/event-derivation.js";
36
36
  import { createDomainEventSubscriber } from "./domain-events.js";
37
- import { createTrackingHandler, EventTracker, BatchingTransport, NoOpTransport, TRACKING_API_BASE_URL, PositionIdRegistry, positionRegistryKey, buildRehydrateItems, } from "../tracking/index.js";
37
+ import { PositionIdRegistry, buildRehydrateItems, } from "./position-id-registry.js";
38
38
  import { VERSION } from "../version.js";
39
39
  import { randomUUID } from "node:crypto";
40
- import { trackingContext } from "../tracking/context.js";
41
- import { buildIdentity, createRecordingComponents, createRecordingSubscriber, } from "../recording/wiring/index.js";
40
+ import { trackingContext } from "../telemetry/context.js";
41
+ import { buildIdentity, } from "../telemetry/identity.js";
42
42
  import { runReconcile } from "./reconcile-run.js";
43
43
  import { KeyedMutex } from "../utils/keyed-mutex.js";
44
44
  import { withSpan } from "../telemetry/tracing.js";
45
+ import { registerDslActivePositionsGauge, registerQueueDepthGauge, recordScannerRun, recordSignalQueueWait, recordRuntimeHeartbeat, } from "../telemetry/metrics.js";
45
46
  import { isOtlpEndpointConfigured } from "../telemetry/otel-sdk.js";
46
47
  import { identityTraceAttributes, setTraceIdentityAttributes, } from "../telemetry/trace-identity.js";
47
48
  const logger = createLogger("[senpi-core][runtime]");
48
49
  /** Fixed retry interval for the boot-reconcile loop. */
49
50
  const RECONCILE_RETRY_INTERVAL_MS = 15_000;
51
+ /**
52
+ * Signal-queue depth at which the serial consumer is judged to be falling behind. It processes one
53
+ * signal at a time (each may run an LLM decision), so a backlog this deep means inflow is outpacing
54
+ * processing — a `runtime.queue_backpressure` alert. Hardcoded: the depth a stall shows at is a
55
+ * runtime invariant, not a per-recipe tuning knob.
56
+ */
57
+ const SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD = 10;
58
+ /** Default liveness-heartbeat window (~15 min) when `health.window_seconds` is unset. */
59
+ const DEFAULT_LIVENESS_WINDOW_SECONDS = 900;
50
60
  export class Runtime {
51
61
  config;
52
62
  options;
@@ -59,10 +69,7 @@ export class Runtime {
59
69
  // riskGuardian removed — gate decisions are now real-time in checkGate
60
70
  senpi = null;
61
71
  healthStateModule = null;
62
- trackingHandler = null;
63
- recordingSubscriber = null;
64
72
  domainEventSubscriber = null;
65
- recordingBatcher = null;
66
73
  /** Lazy runtime identity builder; stamps per-trace identity at the signal.process seam. */
67
74
  buildTraceIdentity;
68
75
  traceIdentity;
@@ -74,13 +81,17 @@ export class Runtime {
74
81
  signalQueueWait = null;
75
82
  signalConsumerStopped = false;
76
83
  signalConsumerDone = null;
84
+ /** Disposer for this runtime's `runtime.queue_depth` gauge observation; set on start, called on stop. */
85
+ queueDepthGaugeDispose = null;
86
+ /** Disposer for this runtime's `dsl.active_positions` gauge observation; set on start, called on stop. */
87
+ dslActivePositionsGaugeDispose = null;
88
+ /** Interval emitting this runtime's liveness heartbeat; set on start, cleared on stop. */
89
+ livenessHeartbeatTimer = null;
77
90
  runActionsForSignal;
78
91
  strategyConfig;
79
92
  actionRuntimeModule;
80
93
  /** Stored so we can call off() in stop() and avoid retaining Runtime via scanner EventEmitter. */
81
94
  onScannerRunComplete = null;
82
- onScannerRunSkip = null;
83
- onScannerRunError = null;
84
95
  /** Cached strategy id resolved from the runtime wallet on first {@link auditQuery} call. */
85
96
  cachedStrategyId = null;
86
97
  /** In-flight resolution promise; ensures concurrent first-burst callers reuse one `listStrategies`. */
@@ -307,18 +318,6 @@ export class Runtime {
307
318
  validateDslPositionTracking(this.config, hasDsl);
308
319
  this.bus = createRuntimeBus();
309
320
  this.hookSystem = createHookSystem(this.bus);
310
- // Event tracking: passive bus → EventTracker → BatchingTransport POST (requires JWT-capable API key).
311
- // Without SENPI_API_KEY, NoOpTransport discards all events; warn once at startup.
312
- const trackingApiKey = apiKey ?? process.env.SENPI_AUTH_TOKEN ?? process.env.SENPI_API_KEY;
313
- const trackingBaseUrl = process.env.SENPI_TRACKING_URL ?? TRACKING_API_BASE_URL;
314
- const trackingTransport = trackingApiKey
315
- ? new BatchingTransport({ baseUrl: trackingBaseUrl, apiKey: trackingApiKey })
316
- : (() => {
317
- logger.warn("event tracking disabled: set SENPI_API_KEY (or pass apiKey) so events reach senpi-agent-telemetry; without it NoOpTransport drops all telemetry");
318
- return new NoOpTransport();
319
- })();
320
- const tracker = new EventTracker(trackingTransport, this.config.name, VERSION, this.config.strategy?.wallet ?? "");
321
- this.trackingHandler = createTrackingHandler(this.bus, tracker);
322
321
  this.stateManager = new StateManager(stateDir);
323
322
  await this.stateManager.hydrate();
324
323
  this.positionIdRegistry = new PositionIdRegistry();
@@ -342,15 +341,8 @@ export class Runtime {
342
341
  this.senpi = senpi;
343
342
  this.strategyConfig = resolveStrategyConfig(this.config);
344
343
  await this.rehydratePositionIdRegistry();
345
- // Telemetry recording: subscribe the emitter to the bus (on by default; degrades to a noop
346
- // emitter when disabled or when no token is present — never a boot failure). Identity + config
347
- // fingerprint are built lazily from the unresolved config — only on the live path, and never
348
- // from the resolved config (which would inline secrets).
349
- const telemetryConfig = this.config.telemetry;
350
- // The OTLP exporter env is the single telemetry master switch: the OTel SDK (stood
351
- // up at plugin register()) and this legacy recording pipeline both gate on it. With
352
- // it set, recording is on (degrading to a noop emitter when no token is present);
353
- // without it, both are off.
344
+ // The OTLP exporter env is the telemetry master switch: the OTel SDK (stood up at plugin
345
+ // register()) and the domain-event log both gate on it.
354
346
  const telemetryEnabled = isOtlpEndpointConfigured();
355
347
  // Lazy identity builder. Built only on the live path (never from the resolved
356
348
  // config, which would inline secrets).
@@ -358,42 +350,20 @@ export class Runtime {
358
350
  name: this.config.name,
359
351
  recipeVersion: this.config.version,
360
352
  strategyAddress: this.strategyConfig.address,
353
+ group: this.config.group,
361
354
  bootId: this.runtimeBootId,
362
355
  unresolvedConfig: this.options.unresolvedConfig,
363
356
  });
364
357
  // Share the builder with the signal.process root span (resolved lazily, once per boot).
365
358
  this.buildTraceIdentity = buildIdentityEnvelope;
366
- const recording = createRecordingComponents({
367
- enabled: telemetryEnabled,
368
- token: trackingApiKey,
369
- identity: buildIdentityEnvelope,
370
- scopeVersion: VERSION,
371
- baseUrl: telemetryConfig?.url,
372
- // v2.0.0: recipe carries flush_interval_seconds; the transport expects ms.
373
- flushIntervalMs: typeof telemetryConfig?.flush_interval_seconds === "number"
374
- ? telemetryConfig.flush_interval_seconds * 1000
375
- : undefined,
376
- });
377
- this.recordingBatcher = recording.batcher;
378
- // Only attach the subscriber when telemetry is live (batcher present): with a noop emitter,
379
- // per-event record assembly on the serial signal path would be pure dead work.
380
- if (recording.batcher) {
381
- this.recordingSubscriber = createRecordingSubscriber(this.bus, recording.emitter, {
382
- unresolvedConfig: this.options.unresolvedConfig,
383
- // Narrow read-only accessor over the registry — recording never sees the
384
- // registry or RuntimeContext; sync map read, safe on the bus handler path.
385
- resolvePositionId: (address, dex, asset) => this.positionIdRegistry.get(positionRegistryKey(address, dex, asset))?.positionId,
386
- });
387
- }
388
- // The event-log half of the real domain events (position.*/dsl.*) → OTel Logs API. Gated on the
389
- // enable flag (not the recording batcher): it rides the OTel SDK's log provider, independent of
390
- // the custom recording pipeline. Per-event assembly on the serial signal path is dead work when
359
+ // The event-log half of the real domain events (position.*/dsl.*) → OTel Logs API. Rides the
360
+ // OTel SDK's log provider. Per-event assembly on the serial signal path is dead work when
391
361
  // telemetry is off, so only attach when enabled.
392
362
  if (telemetryEnabled) {
393
363
  this.domainEventSubscriber = createDomainEventSubscriber(this.bus);
394
364
  }
395
365
  // Everything from here to `started = true` can throw; without the catch, stop() would
396
- // early-return on `!this.started` and the recording batcher/subscriber would leak.
366
+ // early-return on `!this.started` and the domain-event subscriber would leak.
397
367
  try {
398
368
  // try {
399
369
  // const { reconcileStateWithBackend } = await import("./reconcile-state.js");
@@ -545,32 +515,40 @@ export class Runtime {
545
515
  },
546
516
  });
547
517
  this.onScannerRunComplete = (event) => {
548
- const correlationId = randomUUID();
549
- // Telemetry-owned per-scan-run id (NOT the deprecated correlation id): stamped on the
550
- // scanner_run emit and echoed through the pass so scan + pass land in one trace. Prefer
551
- // the id minted at the scanner.run span so scan + pass share one senpi.run.id.
518
+ // Per-scan-run id: stamped on the scanner_run emit and echoed through the pass so scan +
519
+ // pass land in one trace. Prefer the id minted at the scanner.run span so scan + pass
520
+ // share one senpi.run.id.
552
521
  const runId = event.result.runId ?? randomUUID();
553
- this.signalQueue.push({ result: event.result, correlationId, enqueuedAt: Date.now(), runId });
522
+ this.signalQueue.push({ result: event.result, enqueuedAt: Date.now(), runId });
523
+ // Edge-triggered backpressure alarm: depth rises one enqueue at a time, so `=== threshold`
524
+ // fires exactly once as it crosses up and re-arms only after the consumer drains back below.
525
+ if (this.signalQueue.length === SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD) {
526
+ emitEvent(logger, () => buildQueueBackpressureEvent({
527
+ depth: this.signalQueue.length,
528
+ threshold: SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD,
529
+ scannerId: event.result.scannerId,
530
+ address: this.strategyConfig.address,
531
+ }));
532
+ }
554
533
  if (this.signalQueueWait) {
555
534
  const w = this.signalQueueWait;
556
535
  this.signalQueueWait = null;
557
536
  w();
558
537
  }
559
- // Emit scanner run for tracking (startedAt is data stamping for telemetry durations)
538
+ // Scanner-run summary payload (startedAt is data stamping for telemetry durations).
560
539
  const scannerRunPayload = {
561
540
  address: event.result.address,
562
541
  data: {
563
542
  ...event.result,
564
543
  startedAt: event.startedAt,
565
544
  runId,
566
- __correlationId: correlationId,
567
545
  },
568
546
  timestamp: new Date().toISOString(),
569
547
  };
570
- this.bus.emit("tracking.scanner_run", scannerRunPayload);
571
- // First-class event log alongside the legacy bus emit. The build runs inside emitEvent's
572
- // guard — telemetry never disturbs the scan path.
548
+ // First-class event log. The build runs inside emitEvent's guard — telemetry never
549
+ // disturbs the scan path.
573
550
  emitEvent(logger, () => buildScannerSummaryEvent(scannerRunPayload, Date.now()));
551
+ recordScannerRun({ scannerId: event.result.scannerId, outcome: event.result.status });
574
552
  // Forward scanner summary alerts (e.g. watchdog) to notification service
575
553
  const alerts = event.result.summary?.alerts;
576
554
  if (Array.isArray(alerts) && alerts.length > 0) {
@@ -580,37 +558,6 @@ export class Runtime {
580
558
  }
581
559
  };
582
560
  this.scannerModule.on("scanner:run:complete", this.onScannerRunComplete);
583
- // Surface skipped/errored scanner runs on the bus for telemetry (add-only observation events —
584
- // distinct names so existing `tracking.scanner_run` consumers see an unchanged payload mix).
585
- // Neither enqueues a signal: only completed runs feed the signal queue.
586
- this.onScannerRunSkip = (event) => {
587
- this.bus.emit("tracking.scanner_run_skipped", {
588
- address: event.address,
589
- data: {
590
- scannerId: event.scannerId,
591
- status: event.status,
592
- reason: event.reason,
593
- timestamp: event.timestamp,
594
- startedAt: event.startedAt,
595
- },
596
- timestamp: new Date().toISOString(),
597
- });
598
- };
599
- this.onScannerRunError = (event) => {
600
- this.bus.emit("tracking.scanner_run_error", {
601
- address: event.address,
602
- data: {
603
- scannerId: event.scannerId,
604
- status: "error",
605
- reason: typeof event.error === "string" ? event.error : String(event.error),
606
- timestamp: event.timestamp,
607
- startedAt: event.startedAt,
608
- },
609
- timestamp: new Date().toISOString(),
610
- });
611
- };
612
- this.scannerModule.on("scanner:run:skip", this.onScannerRunSkip);
613
- this.scannerModule.on("scanner:run:error", this.onScannerRunError);
614
561
  this.signalConsumerDone = this.runSignalConsumer();
615
562
  logger.info("Runtime.start() invoking scannerModule.start()");
616
563
  await this.scannerModule.start();
@@ -654,14 +601,6 @@ export class Runtime {
654
601
  this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
655
602
  this.onScannerRunComplete = null;
656
603
  }
657
- if (this.onScannerRunSkip != null) {
658
- this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
659
- this.onScannerRunSkip = null;
660
- }
661
- if (this.onScannerRunError != null) {
662
- this.scannerModule.off("scanner:run:error", this.onScannerRunError);
663
- this.onScannerRunError = null;
664
- }
665
604
  if (this.actionNotificationSubscriber != null) {
666
605
  this.actionNotificationSubscriber.dispose();
667
606
  this.actionNotificationSubscriber = null;
@@ -678,25 +617,13 @@ export class Runtime {
678
617
  catch (cleanupErr) {
679
618
  logger.warn("[runtime] partial-state cleanup error on failed start (ignored)", { cleanupErr });
680
619
  }
681
- // Recording-only cleanup (telemetry must not leak on a failed boot); the error rethrows.
682
- await this.disposeRecording();
620
+ // Telemetry cleanup (must not leak on a failed boot); the error rethrows.
621
+ this.disposeSubscribers();
683
622
  throw err;
684
623
  }
685
- this.bus.emit("tracking.runtime_started", {
686
- address: this.strategyConfig.address,
687
- data: {
688
- config: this.config,
689
- strategyAddress: this.strategyConfig.address,
690
- runtimeVersion: VERSION,
691
- hasDsl: this.dslPlugin != null,
692
- scannerCount: this.config.scanners?.length ?? 0,
693
- actionCount: this.config.actions?.length ?? 0,
694
- },
695
- timestamp: new Date().toISOString(),
696
- });
697
- // First-class event log. Carries the UNRESOLVED config hash (the form the recording identity
698
- // hashes), so the printed hash matches `senpi.config.hash` on the rows — not the resolved config
699
- // the bus event carries. The build runs inside emitEvent's guard — telemetry never blocks boot.
624
+ // First-class event log. Carries the UNRESOLVED config hash (the form `computeConfigHash` uses),
625
+ // so the printed hash matches `senpi.config.hash` on the spans/logs. The build runs inside
626
+ // emitEvent's guard — telemetry never blocks boot.
700
627
  emitEvent(logger, () => buildRuntimeStartedEvent({
701
628
  address: this.strategyConfig.address,
702
629
  recipeName: this.config.name,
@@ -706,7 +633,50 @@ export class Runtime {
706
633
  actionCount: this.config.actions?.length ?? 0,
707
634
  hasDsl: this.dslPlugin != null,
708
635
  unresolvedConfig: this.options.unresolvedConfig,
636
+ slots: this.strategyConfig.slots,
637
+ tradingRisk: this.strategyConfig.tradingRisk,
638
+ marginPerSlot: this.strategyConfig.marginPerSlot,
639
+ marginPct: this.strategyConfig.marginPct,
640
+ defaultLeverage: this.strategyConfig.defaultLeverage,
709
641
  }));
642
+ // Observable gauge of the serial signal-queue depth — its absence/stall is alertable without a
643
+ // trace or log scan. Reads the live depth at each collection; disposed in stop().
644
+ const walletDim = this.strategyConfig.address.toLowerCase();
645
+ const group = this.config.group;
646
+ this.queueDepthGaugeDispose = registerQueueDepthGauge({
647
+ strategyAddress: walletDim,
648
+ group,
649
+ read: () => this.getSignalQueueDepth(),
650
+ });
651
+ // Liveness heartbeat: a positive tick every window, dimensioned by wallet/group. A dead runtime
652
+ // can't emit a "down" event — only the absence of these ticks detects it. Fire one now so a
653
+ // runtime that dies before its first window still leaves a mark. Unref'd so it never holds the
654
+ // process open; cleared in stop().
655
+ const windowSec = this.config.health?.window_seconds ?? DEFAULT_LIVENESS_WINDOW_SECONDS;
656
+ recordRuntimeHeartbeat({ strategyAddress: walletDim, group });
657
+ this.livenessHeartbeatTimer = setInterval(() => recordRuntimeHeartbeat({ strategyAddress: walletDim, group }), windowSec * 1000);
658
+ this.livenessHeartbeatTimer.unref?.();
659
+ // Observable gauge of active DSL-managed positions, split by exit phase. Only meaningful when this
660
+ // runtime runs the exit engine; counts this wallet's positions live at each collection (no snapshot).
661
+ if (this.dslPlugin) {
662
+ this.dslActivePositionsGaugeDispose = registerDslActivePositionsGauge({
663
+ strategyAddress: walletDim,
664
+ group,
665
+ read: () => {
666
+ let phase1 = 0;
667
+ let phase2 = 0;
668
+ for (const p of this.dslPlugin?.listActivePositions() ?? []) {
669
+ if (p.address?.toLowerCase() !== walletDim)
670
+ continue;
671
+ if (p.phase === 2)
672
+ phase2++;
673
+ else
674
+ phase1++;
675
+ }
676
+ return { phase1, phase2 };
677
+ },
678
+ });
679
+ }
710
680
  }
711
681
  /**
712
682
  * Cancel exchange SLs and archive DSL state for the strategy, then log removal.
@@ -722,7 +692,7 @@ export class Runtime {
722
692
  }
723
693
  /** Tear down DSL for the runtime strategy before deleting the runtime (gateway / CLI). */
724
694
  async teardownDslForRuntimeDeletion() {
725
- return this.removeStrategy(this.strategyConfig.address);
695
+ await this.removeStrategy(this.strategyConfig.address);
726
696
  }
727
697
  /**
728
698
  * Resolve the per-boot trace identity once. The envelope is immutable per boot, so build it
@@ -758,14 +728,14 @@ export class Runtime {
758
728
  if (!item)
759
729
  continue;
760
730
  // Pin tracking context for this signal flow so LLM spans (emitted by
761
- // OpenClawLlmDecision / AnthropicLlmDecision) can stamp correlation_id,
731
+ // OpenClawLlmDecision / AnthropicLlmDecision) can stamp run id,
762
732
  // address, recipe_name, runtime_id, and runtime_version automatically.
763
733
  const queuedItem = item;
764
734
  // Resolved once here so the same identity rides both the trace (via the span stamp)
765
735
  // and the logs (via the context bag below) — one source, no drift.
766
736
  const identity = this.resolveTraceIdentity();
767
737
  await trackingContext.run({
768
- correlationId: queuedItem.correlationId,
738
+ runId: queuedItem.runId,
769
739
  address: queuedItem.result.address,
770
740
  recipeName: this.config.name,
771
741
  runtimeId: this.options.runtimeId ?? "",
@@ -806,7 +776,7 @@ export class Runtime {
806
776
  // Identity (incl. strategy address) rides every span via the per-trace stamp below;
807
777
  // run.id is the join key back to the producing scanner run.
808
778
  const spanAttrs = {};
809
- if (queuedItem.runId != null) {
779
+ if (queuedItem.runId) {
810
780
  spanAttrs["senpi.run.id"] = queuedItem.runId;
811
781
  }
812
782
  // Carry the producing scanner id so the signal span joins/filters by scanner,
@@ -822,8 +792,10 @@ export class Runtime {
822
792
  // Measured from enqueue, but the pre-gate backlog is evicted above before the
823
793
  // span opens, so every signal that reaches here was enqueued after the (one-shot)
824
794
  // reconcile gate resolved — i.e. this is pure FIFO wait, not gate wait.
825
- span.setAttribute("senpi.queue.wait_ms", Date.now() - queuedItem.enqueuedAt);
795
+ const queueWaitMs = Date.now() - queuedItem.enqueuedAt;
796
+ span.setAttribute("senpi.queue.wait_ms", queueWaitMs);
826
797
  span.setAttribute("senpi.queue.depth", this.signalQueue.length);
798
+ recordSignalQueueWait(queueWaitMs / 1000, { scannerId: queuedItem.result.scannerId });
827
799
  try {
828
800
  const usedSlotsByStrategy = await this.stateManager.getUsedSlotsByStrategy([this.strategyConfig.address]);
829
801
  const payload = buildActionScanPayload(queuedItem.result, this.strategyConfig, {
@@ -836,7 +808,6 @@ export class Runtime {
836
808
  address: queuedItem.result.address,
837
809
  data: {
838
810
  ...payload,
839
- __correlationId: queuedItem.correlationId,
840
811
  __scanRunId: queuedItem.runId,
841
812
  },
842
813
  timestamp: new Date().toISOString(),
@@ -924,23 +895,6 @@ export class Runtime {
924
895
  }
925
896
  const durationMs = Date.now() - t0;
926
897
  const { adopted, resized, archived } = result.counts;
927
- // Telemetry must never break reconcile control flow. bus.emit is a Node
928
- // EventEmitter: a listener that throws synchronously makes emit re-throw,
929
- // which would reject this loop and leave the gate forever closed. Contain it.
930
- try {
931
- this.bus.emit("tracking.runtime_reconciled", {
932
- address,
933
- data: { adopted, resized, archived, success: result.success, attempt, durationMs },
934
- timestamp: new Date().toISOString(),
935
- });
936
- }
937
- catch (err) {
938
- logger.warn("[runtime][reconcile] tracking.runtime_reconciled listener threw; contained", {
939
- address,
940
- attempt,
941
- err,
942
- });
943
- }
944
898
  if (result.success) {
945
899
  logger.info("[runtime][reconcile] boot reconcile succeeded", { address, attempt, adopted, resized, archived });
946
900
  this.resolveReconcileGate();
@@ -959,22 +913,14 @@ export class Runtime {
959
913
  gate.resolve();
960
914
  }
961
915
  /**
962
- * Dispose the recording subscriber, then flush + stop the batcher (best-effort). Idempotent;
963
- * shared by stop() and the failed-start cleanup path in start().
916
+ * Dispose the domain-event subscriber. Idempotent; shared by stop() and the failed-start
917
+ * cleanup path in start().
964
918
  */
965
- async disposeRecording() {
966
- if (this.recordingSubscriber) {
967
- this.recordingSubscriber.dispose();
968
- this.recordingSubscriber = null;
969
- }
919
+ disposeSubscribers() {
970
920
  if (this.domainEventSubscriber) {
971
921
  this.domainEventSubscriber.dispose();
972
922
  this.domainEventSubscriber = null;
973
923
  }
974
- if (this.recordingBatcher) {
975
- await this.recordingBatcher.shutdown().catch(() => { });
976
- this.recordingBatcher = null;
977
- }
978
924
  }
979
925
  async stop() {
980
926
  if (!this.started)
@@ -989,18 +935,20 @@ export class Runtime {
989
935
  // Resolve the gate so the consumer doesn't hang waiting for a reconcile that won't come.
990
936
  this.reconcileGate?.resolve();
991
937
  const uptimeMs = Date.now() - (this.startedAt ?? Date.now());
992
- this.bus.emit("tracking.runtime_stopped", {
993
- address: this.strategyConfig.address,
994
- data: {
995
- uptimeMs,
996
- runtimeVersion: VERSION,
997
- strategyAddress: this.strategyConfig.address,
998
- },
999
- timestamp: new Date().toISOString(),
1000
- });
1001
- // First-class event log alongside the legacy bus emit. The build runs inside emitEvent's guard
1002
- // — telemetry never blocks shutdown.
938
+ // First-class event log. The build runs inside emitEvent's guard — telemetry never blocks shutdown.
1003
939
  emitEvent(logger, () => buildRuntimeStoppedEvent(VERSION, uptimeMs, this.strategyConfig.address));
940
+ if (this.queueDepthGaugeDispose) {
941
+ this.queueDepthGaugeDispose();
942
+ this.queueDepthGaugeDispose = null;
943
+ }
944
+ if (this.dslActivePositionsGaugeDispose) {
945
+ this.dslActivePositionsGaugeDispose();
946
+ this.dslActivePositionsGaugeDispose = null;
947
+ }
948
+ if (this.livenessHeartbeatTimer) {
949
+ clearInterval(this.livenessHeartbeatTimer);
950
+ this.livenessHeartbeatTimer = null;
951
+ }
1004
952
  this.signalConsumerStopped = true;
1005
953
  if (this.signalQueueWait) {
1006
954
  const w = this.signalQueueWait;
@@ -1016,14 +964,6 @@ export class Runtime {
1016
964
  this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
1017
965
  this.onScannerRunComplete = null;
1018
966
  }
1019
- if (this.onScannerRunSkip != null) {
1020
- this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
1021
- this.onScannerRunSkip = null;
1022
- }
1023
- if (this.onScannerRunError != null) {
1024
- this.scannerModule.off("scanner:run:error", this.onScannerRunError);
1025
- this.onScannerRunError = null;
1026
- }
1027
967
  if (this.actionNotificationSubscriber != null) {
1028
968
  this.actionNotificationSubscriber.dispose();
1029
969
  this.actionNotificationSubscriber = null;
@@ -1038,13 +978,7 @@ export class Runtime {
1038
978
  this.senpi = null;
1039
979
  }
1040
980
  this.healthStateModule = null;
1041
- if (this.trackingHandler) {
1042
- await this.trackingHandler.dispose();
1043
- this.trackingHandler = null;
1044
- }
1045
- // Telemetry recording teardown: stop the subscriber, then flush + stop the batcher (best-effort)
1046
- // so the just-recorded runtime_stopped row ships before the process exits.
1047
- await this.disposeRecording();
981
+ this.disposeSubscribers();
1048
982
  if (this.bus.removeAllListeners) {
1049
983
  this.bus.removeAllListeners();
1050
984
  }