@senpi/runtime 3.0.117 → 3.0.122

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 (321) hide show
  1. package/README.md +16 -0
  2. package/dist/actions/action-events.d.ts +7 -21
  3. package/dist/actions/action-events.d.ts.map +1 -1
  4. package/dist/actions/action-events.js +26 -54
  5. package/dist/actions/action-events.js.map +1 -1
  6. package/dist/actions/action-runtime-module.d.ts.map +1 -1
  7. package/dist/actions/action-runtime-module.js +7 -38
  8. package/dist/actions/action-runtime-module.js.map +1 -1
  9. package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
  10. package/dist/actions/close-position/close-position.action.js +15 -122
  11. package/dist/actions/close-position/close-position.action.js.map +1 -1
  12. package/dist/actions/decision-engine.d.ts.map +1 -1
  13. package/dist/actions/decision-engine.js +2 -15
  14. package/dist/actions/decision-engine.js.map +1 -1
  15. package/dist/actions/event-derivation.d.ts +2 -71
  16. package/dist/actions/event-derivation.d.ts.map +1 -1
  17. package/dist/actions/event-derivation.js +8 -251
  18. package/dist/actions/event-derivation.js.map +1 -1
  19. package/dist/actions/open-position/open-position.action.d.ts +2 -17
  20. package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
  21. package/dist/actions/open-position/open-position.action.js +56 -162
  22. package/dist/actions/open-position/open-position.action.js.map +1 -1
  23. package/dist/actions/position-tracker/position-tracker.action.d.ts +1 -2
  24. package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
  25. package/dist/actions/position-tracker/position-tracker.action.js +17 -62
  26. package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
  27. package/dist/actions/store/types.d.ts +2 -9
  28. package/dist/actions/store/types.d.ts.map +1 -1
  29. package/dist/actions/store/types.js.map +1 -1
  30. package/dist/actions/types.d.ts +0 -13
  31. package/dist/actions/types.d.ts.map +1 -1
  32. package/dist/actions/types.js +0 -13
  33. package/dist/actions/types.js.map +1 -1
  34. package/dist/cli/guide-commands.d.ts.map +1 -1
  35. package/dist/cli/guide-commands.js +16 -11
  36. package/dist/cli/guide-commands.js.map +1 -1
  37. package/dist/cli/senpi-commands.d.ts.map +1 -1
  38. package/dist/cli/senpi-commands.js +18 -7
  39. package/dist/cli/senpi-commands.js.map +1 -1
  40. package/dist/constants/dsl/index.d.ts +17 -6
  41. package/dist/constants/dsl/index.d.ts.map +1 -1
  42. package/dist/constants/dsl/index.js +17 -6
  43. package/dist/constants/dsl/index.js.map +1 -1
  44. package/dist/dsl/engine/floors.d.ts +25 -1
  45. package/dist/dsl/engine/floors.d.ts.map +1 -1
  46. package/dist/dsl/engine/floors.js +38 -6
  47. package/dist/dsl/engine/floors.js.map +1 -1
  48. package/dist/dsl/engine/index.d.ts +1 -1
  49. package/dist/dsl/engine/index.d.ts.map +1 -1
  50. package/dist/dsl/engine/index.js +1 -1
  51. package/dist/dsl/engine/index.js.map +1 -1
  52. package/dist/dsl/engine/round.d.ts +22 -2
  53. package/dist/dsl/engine/round.d.ts.map +1 -1
  54. package/dist/dsl/engine/round.js +78 -5
  55. package/dist/dsl/engine/round.js.map +1 -1
  56. package/dist/dsl/engine/tick.d.ts.map +1 -1
  57. package/dist/dsl/engine/tick.js +31 -16
  58. package/dist/dsl/engine/tick.js.map +1 -1
  59. package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
  60. package/dist/dsl/engine-client/event-mapping.js +0 -14
  61. package/dist/dsl/engine-client/event-mapping.js.map +1 -1
  62. package/dist/dsl/events/build-closed-event.d.ts +1 -16
  63. package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
  64. package/dist/dsl/events/build-closed-event.js +0 -6
  65. package/dist/dsl/events/build-closed-event.js.map +1 -1
  66. package/dist/dsl/events/handlers.d.ts +0 -7
  67. package/dist/dsl/events/handlers.d.ts.map +1 -1
  68. package/dist/dsl/events/handlers.js +7 -37
  69. package/dist/dsl/events/handlers.js.map +1 -1
  70. package/dist/dsl/events/initial-state.d.ts +1 -1
  71. package/dist/dsl/events/initial-state.d.ts.map +1 -1
  72. package/dist/dsl/events/initial-state.js +4 -7
  73. package/dist/dsl/events/initial-state.js.map +1 -1
  74. package/dist/dsl/monitor/create-monitor.d.ts +2 -6
  75. package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
  76. package/dist/dsl/monitor/create-monitor.js +9 -15
  77. package/dist/dsl/monitor/create-monitor.js.map +1 -1
  78. package/dist/dsl/monitor/dsl-failure-events.d.ts +0 -2
  79. package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -1
  80. package/dist/dsl/monitor/dsl-failure-events.js +1 -2
  81. package/dist/dsl/monitor/dsl-failure-events.js.map +1 -1
  82. package/dist/dsl/monitor/exchange-sync.d.ts +8 -3
  83. package/dist/dsl/monitor/exchange-sync.d.ts.map +1 -1
  84. package/dist/dsl/monitor/exchange-sync.js +16 -4
  85. package/dist/dsl/monitor/exchange-sync.js.map +1 -1
  86. package/dist/dsl/monitor/process-one-position.d.ts +0 -1
  87. package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
  88. package/dist/dsl/monitor/process-one-position.js +113 -62
  89. package/dist/dsl/monitor/process-one-position.js.map +1 -1
  90. package/dist/dsl/plugin/index.d.ts +10 -12
  91. package/dist/dsl/plugin/index.d.ts.map +1 -1
  92. package/dist/dsl/plugin/index.js +14 -12
  93. package/dist/dsl/plugin/index.js.map +1 -1
  94. package/dist/health/types.d.ts +0 -13
  95. package/dist/health/types.d.ts.map +1 -1
  96. package/dist/health/types.js +0 -12
  97. package/dist/health/types.js.map +1 -1
  98. package/dist/index.d.ts +4 -27
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +206 -257
  101. package/dist/index.js.map +1 -1
  102. package/dist/runtime/action-registry.d.ts.map +1 -1
  103. package/dist/runtime/action-registry.js +3 -4
  104. package/dist/runtime/action-registry.js.map +1 -1
  105. package/dist/runtime/auto-update/coordinator.d.ts +16 -0
  106. package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
  107. package/dist/runtime/auto-update/coordinator.js +129 -13
  108. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  109. package/dist/runtime/auto-update/run-update-command.d.ts.map +1 -1
  110. package/dist/runtime/auto-update/run-update-command.js +18 -14
  111. package/dist/runtime/auto-update/run-update-command.js.map +1 -1
  112. package/dist/runtime/auto-update/types.d.ts +17 -1
  113. package/dist/runtime/auto-update/types.d.ts.map +1 -1
  114. package/dist/runtime/auto-update/types.js +11 -0
  115. package/dist/runtime/auto-update/types.js.map +1 -1
  116. package/dist/runtime/boot-skip.d.ts +73 -0
  117. package/dist/runtime/boot-skip.d.ts.map +1 -0
  118. package/dist/runtime/boot-skip.js +113 -0
  119. package/dist/runtime/boot-skip.js.map +1 -0
  120. package/dist/runtime/create-runtime-context.d.ts +2 -0
  121. package/dist/runtime/create-runtime-context.d.ts.map +1 -1
  122. package/dist/runtime/create-runtime-context.js +1 -0
  123. package/dist/runtime/create-runtime-context.js.map +1 -1
  124. package/dist/runtime/domain-events.d.ts.map +1 -1
  125. package/dist/runtime/domain-events.js +0 -7
  126. package/dist/runtime/domain-events.js.map +1 -1
  127. package/dist/runtime/lifecycle-events.d.ts +43 -10
  128. package/dist/runtime/lifecycle-events.d.ts.map +1 -1
  129. package/dist/runtime/lifecycle-events.js +83 -12
  130. package/dist/runtime/lifecycle-events.js.map +1 -1
  131. package/dist/runtime/lifecycle-reaper.d.ts +202 -0
  132. package/dist/runtime/lifecycle-reaper.d.ts.map +1 -0
  133. package/dist/runtime/lifecycle-reaper.js +390 -0
  134. package/dist/runtime/lifecycle-reaper.js.map +1 -0
  135. package/dist/runtime/reconcile-run.d.ts +3 -13
  136. package/dist/runtime/reconcile-run.d.ts.map +1 -1
  137. package/dist/runtime/reconcile-run.js +3 -4
  138. package/dist/runtime/reconcile-run.js.map +1 -1
  139. package/dist/runtime/reconcile-state.d.ts +0 -8
  140. package/dist/runtime/reconcile-state.d.ts.map +1 -1
  141. package/dist/runtime/reconcile-state.js +1 -4
  142. package/dist/runtime/reconcile-state.js.map +1 -1
  143. package/dist/runtime/run.d.ts +5 -1
  144. package/dist/runtime/run.d.ts.map +1 -1
  145. package/dist/runtime/run.js +1 -0
  146. package/dist/runtime/run.js.map +1 -1
  147. package/dist/runtime/runtime-schema.js +3 -3
  148. package/dist/runtime/runtime-schema.js.map +1 -1
  149. package/dist/runtime/runtime.d.ts +2 -1
  150. package/dist/runtime/runtime.d.ts.map +1 -1
  151. package/dist/runtime/runtime.js +4 -27
  152. package/dist/runtime/runtime.js.map +1 -1
  153. package/dist/runtime/supervisor/long-running-supervisor.d.ts +0 -13
  154. package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
  155. package/dist/runtime/supervisor/long-running-supervisor.js +0 -1
  156. package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
  157. package/dist/runtime/update-apply-deps.d.ts +23 -2
  158. package/dist/runtime/update-apply-deps.d.ts.map +1 -1
  159. package/dist/runtime/update-apply-deps.js +11 -5
  160. package/dist/runtime/update-apply-deps.js.map +1 -1
  161. package/dist/runtime/validate-dsl-position-tracking.d.ts.map +1 -1
  162. package/dist/runtime/validate-dsl-position-tracking.js +1 -2
  163. package/dist/runtime/validate-dsl-position-tracking.js.map +1 -1
  164. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
  165. package/dist/runtime-api/handlers/ingest-signals.js +0 -1
  166. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
  167. package/dist/runtime-api/handlers/types.d.ts +0 -5
  168. package/dist/runtime-api/handlers/types.d.ts.map +1 -1
  169. package/dist/runtime-api/intake/intake-events.d.ts +1 -1
  170. package/dist/runtime-api/intake/intake-events.d.ts.map +1 -1
  171. package/dist/runtime-api/intake/intake-events.js +3 -9
  172. package/dist/runtime-api/intake/intake-events.js.map +1 -1
  173. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  174. package/dist/runtime-api/intake/intake-mount.js +19 -4
  175. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  176. package/dist/runtime-api/intake/process-error-post.d.ts +1 -6
  177. package/dist/runtime-api/intake/process-error-post.d.ts.map +1 -1
  178. package/dist/runtime-api/intake/process-error-post.js +1 -5
  179. package/dist/runtime-api/intake/process-error-post.js.map +1 -1
  180. package/dist/runtime-api/intake/process-signal-post.d.ts +1 -7
  181. package/dist/runtime-api/intake/process-signal-post.d.ts.map +1 -1
  182. package/dist/runtime-api/intake/process-signal-post.js +0 -5
  183. package/dist/runtime-api/intake/process-signal-post.js.map +1 -1
  184. package/dist/runtime-api/intake/scaffold-error-router.js.map +1 -1
  185. package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
  186. package/dist/runtime-api/intake/scanner-launcher.js +0 -15
  187. package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
  188. package/dist/runtime-api/intake/types.d.ts +0 -55
  189. package/dist/runtime-api/intake/types.d.ts.map +1 -1
  190. package/dist/runtime-api/routes/signals.d.ts +0 -2
  191. package/dist/runtime-api/routes/signals.d.ts.map +1 -1
  192. package/dist/runtime-api/routes/signals.js +1 -5
  193. package/dist/runtime-api/routes/signals.js.map +1 -1
  194. package/dist/runtime-api/routes/signals.schema.d.ts +0 -43
  195. package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -1
  196. package/dist/runtime-api/routes/signals.schema.js +0 -43
  197. package/dist/runtime-api/routes/signals.schema.js.map +1 -1
  198. package/dist/runtime-api/server.d.ts +1 -7
  199. package/dist/runtime-api/server.d.ts.map +1 -1
  200. package/dist/runtime-api/server.js +0 -1
  201. package/dist/runtime-api/server.js.map +1 -1
  202. package/dist/scanners/engine/engine.d.ts +1 -22
  203. package/dist/scanners/engine/engine.d.ts.map +1 -1
  204. package/dist/scanners/engine/engine.js +11 -78
  205. package/dist/scanners/engine/engine.js.map +1 -1
  206. package/dist/scanners/engine/lifecycle.d.ts +1 -7
  207. package/dist/scanners/engine/lifecycle.d.ts.map +1 -1
  208. package/dist/scanners/events.d.ts +0 -4
  209. package/dist/scanners/events.d.ts.map +1 -1
  210. package/dist/scanners/events.js +0 -2
  211. package/dist/scanners/events.js.map +1 -1
  212. package/dist/scanners/external/scaffold/delivery.py +25 -194
  213. package/dist/scanners/external/scaffold/scaffold.py +31 -172
  214. package/dist/scanners/external/scaffold/senpi_mcp.py +8 -150
  215. package/dist/scanners/external-scanner-receiver.d.ts +0 -17
  216. package/dist/scanners/external-scanner-receiver.d.ts.map +1 -1
  217. package/dist/scanners/external-scanner-receiver.js +0 -8
  218. package/dist/scanners/external-scanner-receiver.js.map +1 -1
  219. package/dist/scanners/runtime-module.d.ts +0 -1
  220. package/dist/scanners/runtime-module.d.ts.map +1 -1
  221. package/dist/scanners/runtime-module.js +9 -66
  222. package/dist/scanners/runtime-module.js.map +1 -1
  223. package/dist/skills-manager/manifest.d.ts.map +1 -1
  224. package/dist/skills-manager/manifest.js +1 -0
  225. package/dist/skills-manager/manifest.js.map +1 -1
  226. package/dist/skills-manager/skills-manifest.json +1 -0
  227. package/dist/telemetry/attributes.d.ts +0 -20
  228. package/dist/telemetry/attributes.d.ts.map +1 -1
  229. package/dist/telemetry/attributes.js +0 -24
  230. package/dist/telemetry/attributes.js.map +1 -1
  231. package/dist/telemetry/event-builders.d.ts +6 -81
  232. package/dist/telemetry/event-builders.d.ts.map +1 -1
  233. package/dist/telemetry/event-builders.js +17 -141
  234. package/dist/telemetry/event-builders.js.map +1 -1
  235. package/dist/telemetry/event-store.d.ts +0 -6
  236. package/dist/telemetry/event-store.d.ts.map +1 -1
  237. package/dist/telemetry/event-store.js +8 -12
  238. package/dist/telemetry/event-store.js.map +1 -1
  239. package/dist/telemetry/metrics.d.ts +2 -14
  240. package/dist/telemetry/metrics.d.ts.map +1 -1
  241. package/dist/telemetry/metrics.js +4 -19
  242. package/dist/telemetry/metrics.js.map +1 -1
  243. package/dist/telemetry/spans/redact-pii.d.ts.map +1 -1
  244. package/dist/telemetry/spans/redact-pii.js +2 -18
  245. package/dist/telemetry/spans/redact-pii.js.map +1 -1
  246. package/dist/telemetry/tracing.d.ts +0 -9
  247. package/dist/telemetry/tracing.d.ts.map +1 -1
  248. package/dist/telemetry/tracing.js +0 -36
  249. package/dist/telemetry/tracing.js.map +1 -1
  250. package/dist/types/dsl/index.d.ts +1 -12
  251. package/dist/types/dsl/index.d.ts.map +1 -1
  252. package/dist/types/runtime.d.ts +8 -0
  253. package/dist/types/runtime.d.ts.map +1 -1
  254. package/dist/types/scanner.d.ts +0 -15
  255. package/dist/types/scanner.d.ts.map +1 -1
  256. package/dist/utils/event-catalog.d.ts +91 -166
  257. package/dist/utils/event-catalog.d.ts.map +1 -1
  258. package/dist/utils/event-catalog.js +13 -18
  259. package/dist/utils/event-catalog.js.map +1 -1
  260. package/dist/utils/keyed-mutex.d.ts +3 -2
  261. package/dist/utils/keyed-mutex.d.ts.map +1 -1
  262. package/dist/utils/keyed-mutex.js +3 -2
  263. package/dist/utils/keyed-mutex.js.map +1 -1
  264. package/dist/utils/logger.d.ts +1 -4
  265. package/dist/utils/logger.d.ts.map +1 -1
  266. package/dist/utils/logger.js.map +1 -1
  267. package/dist/validate/recipe-checks.d.ts.map +1 -1
  268. package/dist/validate/recipe-checks.js +2 -2
  269. package/dist/validate/recipe-checks.js.map +1 -1
  270. package/openclaw.plugin.json +5 -1
  271. package/package.json +1 -1
  272. package/dist/actions/skipped-signal-results.d.ts +0 -25
  273. package/dist/actions/skipped-signal-results.d.ts.map +0 -1
  274. package/dist/actions/skipped-signal-results.js +0 -71
  275. package/dist/actions/skipped-signal-results.js.map +0 -1
  276. package/dist/runtime-api/intake/ingest-request.d.ts +0 -17
  277. package/dist/runtime-api/intake/ingest-request.d.ts.map +0 -1
  278. package/dist/runtime-api/intake/ingest-request.js +0 -34
  279. package/dist/runtime-api/intake/ingest-request.js.map +0 -1
  280. package/dist/runtime-api/intake/scaffold-tick-router.d.ts +0 -39
  281. package/dist/runtime-api/intake/scaffold-tick-router.d.ts.map +0 -1
  282. package/dist/runtime-api/intake/scaffold-tick-router.js +0 -122
  283. package/dist/runtime-api/intake/scaffold-tick-router.js.map +0 -1
  284. package/dist/scanners/external/scaffold/tick_facts.py +0 -130
  285. package/dist/scanners/external/scaffold/ulid.py +0 -58
  286. package/dist/telemetry/live-telemetry.d.ts +0 -238
  287. package/dist/telemetry/live-telemetry.d.ts.map +0 -1
  288. package/dist/telemetry/live-telemetry.js +0 -853
  289. package/dist/telemetry/live-telemetry.js.map +0 -1
  290. package/dist/telemetry/scanner-stats.d.ts +0 -143
  291. package/dist/telemetry/scanner-stats.d.ts.map +0 -1
  292. package/dist/telemetry/scanner-stats.js +0 -426
  293. package/dist/telemetry/scanner-stats.js.map +0 -1
  294. package/dist/telemetry/scanner-tick-feed.d.ts +0 -87
  295. package/dist/telemetry/scanner-tick-feed.d.ts.map +0 -1
  296. package/dist/telemetry/scanner-tick-feed.js +0 -48
  297. package/dist/telemetry/scanner-tick-feed.js.map +0 -1
  298. package/dist/telemetry/strategy-topology.d.ts +0 -99
  299. package/dist/telemetry/strategy-topology.d.ts.map +0 -1
  300. package/dist/telemetry/strategy-topology.js +0 -93
  301. package/dist/telemetry/strategy-topology.js.map +0 -1
  302. package/dist/telemetry/telemetry-publisher.d.ts +0 -63
  303. package/dist/telemetry/telemetry-publisher.d.ts.map +0 -1
  304. package/dist/telemetry/telemetry-publisher.js +0 -229
  305. package/dist/telemetry/telemetry-publisher.js.map +0 -1
  306. package/dist/telemetry/tick-frame.d.ts +0 -212
  307. package/dist/telemetry/tick-frame.d.ts.map +0 -1
  308. package/dist/telemetry/tick-frame.js +0 -421
  309. package/dist/telemetry/tick-frame.js.map +0 -1
  310. package/dist/telemetry/timeline-buffer.d.ts +0 -95
  311. package/dist/telemetry/timeline-buffer.d.ts.map +0 -1
  312. package/dist/telemetry/timeline-buffer.js +0 -237
  313. package/dist/telemetry/timeline-buffer.js.map +0 -1
  314. package/dist/utils/telemetry-required-log.d.ts +0 -60
  315. package/dist/utils/telemetry-required-log.d.ts.map +0 -1
  316. package/dist/utils/telemetry-required-log.js +0 -105
  317. package/dist/utils/telemetry-required-log.js.map +0 -1
  318. package/dist/utils/ulid.d.ts +0 -9
  319. package/dist/utils/ulid.d.ts.map +0 -1
  320. package/dist/utils/ulid.js +0 -43
  321. package/dist/utils/ulid.js.map +0 -1
@@ -0,0 +1,202 @@
1
+ /**
2
+ * The lifecycle reaper: a runtime exists iff its strategy is open.
3
+ *
4
+ * Every close path — raw `strategy_close`, close.py, the web app, a withdraw to $0,
5
+ * `strategy_close_positions` — ends at the backend, and none of them tells the box. Without this
6
+ * module a closed strategy's runtime keeps scanning, logging and posting until something deletes it
7
+ * by hand; boot (since 3.0.119) merely declines to start it again, leaving the registry row behind.
8
+ * The reaper reads strategy status for every registered wallet and removes the runtimes whose
9
+ * strategy the platform reports gone.
10
+ *
11
+ * Safety invariant — deletion needs POSITIVE evidence: `CLOSED` from a successful read. That is the
12
+ * only thing the reaper ever deletes on.
13
+ * Any other dead status (`dead_not_closed`: FAILED, and the INACTIVE/TERMINATED/CLOSING_DONE names
14
+ * `isDead` also accepts) is reported and never deleted. Only CLOSED means a close ran: strategy-manager
15
+ * reaches it through the offboarding flow, which closes the positions first. FAILED is written by
16
+ * onboarding's final-failure path, which refunds the withdrawable balance but closes nothing, and a
17
+ * FAILED strategy cannot be closed afterwards (CloseStrategy accepts only ACTIVE or PAUSED). If it
18
+ * holds positions, the runtime's DSL is their only stop, and deleting the runtime tears it down.
19
+ * A wallet missing from a successful read (`wallet_absent`) is reported and never deleted: real
20
+ * absence is an edge case (0 of ~345 boot wallets over 7 days), while a well-formed answer can omit
21
+ * a live wallet (the backend's address filter is case-sensitive and scoped to the token's user).
22
+ * A failed, timed-out or unrecognisable read, a closing / live / paused / unknown status, and a row
23
+ * without a usable wallet never delete anything.
24
+ */
25
+ import type { EmittableEvent } from "../actions/event-derivation.js";
26
+ import type { ReapTrigger } from "../utils/event-catalog.js";
27
+ import { type WalletStatusRow } from "./boot-skip.js";
28
+ /**
29
+ * What `runtime list` says about a runtime's strategy, as last read.
30
+ *
31
+ * - `open` — ACTIVE, PAUSED, initializing, or any status the reaper does not recognise as gone.
32
+ * - `closing` — a teardown is under way (`CLOSING_POSITIONS`, …); re-checked next pass.
33
+ * - `closed` — the platform reports a dead status.
34
+ * - `absent` — a confirmed read carried no strategy for the wallet.
35
+ * - `unchecked` — not read yet in this process, or never confirmed.
36
+ * - `unknown` — the row has no usable wallet; its lifecycle cannot be read at all.
37
+ */
38
+ export type RuntimeLifecycle = "open" | "closing" | "closed" | "absent" | "unchecked" | "unknown";
39
+ /** One successful status read, as the decision needs it. */
40
+ export interface StatusAnswer {
41
+ /** Lower-cased wallet → platform status, for the wallets the answer carried. */
42
+ readonly statuses: ReadonlyMap<string, string>;
43
+ }
44
+ /** The verdict for one registry row. */
45
+ export type ReapDecision =
46
+ /** No usable wallet: never judged, rendered `unknown`. */
47
+ {
48
+ action: "untracked";
49
+ } | {
50
+ action: "keep";
51
+ lifecycle: "open" | "closing";
52
+ status: string;
53
+ } | {
54
+ action: "reap";
55
+ reason: "closed_status";
56
+ status: string;
57
+ }
58
+ /** Missing from a successful answer: reported, never deleted. */
59
+ | {
60
+ action: "hold";
61
+ reason: "wallet_absent";
62
+ }
63
+ /** Dead, but not CLOSED: no close ran, so it may still hold positions. Reported, never deleted. */
64
+ | {
65
+ action: "hold";
66
+ reason: "dead_not_closed";
67
+ status: string;
68
+ };
69
+ /**
70
+ * Decide one registry row against one successful status read.
71
+ *
72
+ * A failed read never reaches this function — the controller skips the whole pass — so "unreadable"
73
+ * cannot be mistaken for "absent" here by construction.
74
+ */
75
+ export declare function decideReap(entry: {
76
+ id: string;
77
+ wallet?: string;
78
+ }, answer: StatusAnswer): ReapDecision;
79
+ /**
80
+ * The lifecycle a read status maps to; `undefined` means the (confirmed) answer did not carry the
81
+ * wallet.
82
+ */
83
+ export declare function lifecycleForStatus(status: string | undefined): RuntimeLifecycle;
84
+ /**
85
+ * Release default for deleting candidates. `false` = observe-only: candidates are emitted as
86
+ * `runtime.reap_candidate` and nothing is deleted. It flips in the release after the observe-only
87
+ * fleet validation (plan Task 8); `SENPI_REAPER_ENFORCE` overrides it per box.
88
+ */
89
+ export declare const REAPER_ENFORCEMENT_DEFAULT = false;
90
+ /** Delay between `start()` completing and the post-boot pass — lets MCP and the gateway settle. */
91
+ export declare const REAPER_BOOT_DELAY_MS = 120000;
92
+ /** Sweep cadence. Bounds how long a closed strategy that never trades keeps its runtime. */
93
+ export declare const REAPER_SWEEP_INTERVAL_MS: number;
94
+ /**
95
+ * Per-wallet debounce for the `STRATEGY_NOT_ACTIVE` escalation. Matches the backend-pause TTL: the
96
+ * open action re-probes a paused wallet about that often, and each rejection would otherwise re-read.
97
+ */
98
+ export declare const REAPER_WALLET_DEBOUNCE_MS: number;
99
+ /**
100
+ * Whether this box deletes candidates. `SENPI_REAPER_ENFORCE=true|false` wins; any other value (or
101
+ * none) falls back to {@link REAPER_ENFORCEMENT_DEFAULT} — a typo must never turn deletion on.
102
+ */
103
+ export declare function resolveReaperEnforcement(env: NodeJS.ProcessEnv): boolean;
104
+ /**
105
+ * An explicit post-boot delay from `SENPI_REAPER_BOOT_DELAY_MS` (a non-negative integer), else
106
+ * `undefined`. For dev-box testing (run the post-boot pass at once) and the plugin tests; being set
107
+ * at all also arms the reaper's timers where the unit-test seam `SENPI_RUNTIME_API_DISABLED` would
108
+ * otherwise keep them off.
109
+ */
110
+ export declare function resolveReaperBootDelayMs(env: NodeJS.ProcessEnv): number | undefined;
111
+ /**
112
+ * How long `stop()` waits for a pass already under way to finish its current step. A pass checks
113
+ * for the stop before every delete, so this bounds one status read or one delete, not a whole pass.
114
+ */
115
+ export declare const REAPER_STOP_DRAIN_MS = 10000;
116
+ /**
117
+ * How long the reaper waits on one delete. The delete's DSL teardown and `stop()` have no deadline
118
+ * of their own; without this, one that never settles holds the single-flight slot forever and every
119
+ * later sweep and not-active check silently does nothing until restart. On expiry the delete is
120
+ * logged as not removed, the pass moves on, and the delete itself keeps running in the background.
121
+ * It still holds the runtime's change lock, so a later pass's attempt on that id is a CONFLICT, not
122
+ * a second delete.
123
+ */
124
+ export declare const REAPER_DELETE_TIMEOUT_MS: number;
125
+ /** Result of one delete attempt, as the reaper needs it. */
126
+ export type ReaperDeleteResult = {
127
+ ok: true;
128
+ } | {
129
+ ok: false;
130
+ code: string;
131
+ message: string;
132
+ };
133
+ /**
134
+ * Everything the reaper touches, injected so the controller is testable without a gateway.
135
+ * `index.ts` wires these to the registry, `SenpiClient.listStrategies` and `deleteRuntimeCore`.
136
+ */
137
+ export interface LifecycleReaperDeps {
138
+ readRegistry(): Promise<{
139
+ runtimes: ReadonlyArray<{
140
+ id: string;
141
+ wallet?: string;
142
+ }>;
143
+ }>;
144
+ /** One `strategy_list` call for these (lower-cased) wallets. Throws on any failure. */
145
+ readStatuses(wallets: string[], signal: AbortSignal): Promise<readonly WalletStatusRow[]>;
146
+ /**
147
+ * Remove one runtime. `wallet` is the lower-cased wallet the decision was made about; the delete
148
+ * refuses (`ok: false`) if the row under `id` no longer carries it or an install/deploy is running.
149
+ */
150
+ deleteRuntime(id: string, reason: "closed_status", wallet: string): Promise<ReaperDeleteResult>;
151
+ emit(build: () => EmittableEvent | null): void;
152
+ /** Delete candidates (true) or only report them (false). */
153
+ enforce: boolean;
154
+ logger: {
155
+ info(message: string, meta?: object): void;
156
+ warn(message: string, meta?: object): void;
157
+ };
158
+ readTimeoutMs?: number;
159
+ bootDelayMs?: number;
160
+ sweepIntervalMs?: number;
161
+ walletDebounceMs?: number;
162
+ now?: () => number;
163
+ stopDrainMs?: number;
164
+ deleteTimeoutMs?: number;
165
+ }
166
+ /** The reaper as `index.ts` drives it. No method ever throws or rejects. */
167
+ export interface LifecycleReaper {
168
+ /**
169
+ * One pass: read the registry, read every trackable wallet's status in one bounded call, judge
170
+ * each row, and (when enforcing) delete the actionable candidates. `onlyWallet` narrows the pass
171
+ * to that wallet's rows. Single-flight: a pass requested while one runs returns at once.
172
+ */
173
+ runPass(trigger: ReapTrigger, onlyWallet?: string): Promise<void>;
174
+ /**
175
+ * Escalation entry point: a debounced, fire-and-forget pass over one wallet. A no-op until
176
+ * `start()` — boot is still wiring runtimes before that.
177
+ */
178
+ checkWallet(wallet: string): void;
179
+ /** Remember statuses read elsewhere (the boot read) so `lifecycleOf` has them before any pass. */
180
+ recordStatuses(requested: readonly string[], statuses: ReadonlyMap<string, string>): void;
181
+ /** The last known lifecycle of a registry wallet, and the status it came from. */
182
+ lifecycleOf(wallet: string | undefined): {
183
+ lifecycle: RuntimeLifecycle;
184
+ strategyStatus?: string;
185
+ };
186
+ /**
187
+ * Schedule the post-boot pass and the hourly sweep. Both timers are unref'd. Re-arms a stopped
188
+ * reaper (a service restart in the same process); a no-op while already started.
189
+ */
190
+ start(): void;
191
+ /**
192
+ * Clear the timers and wait (bounded by {@link REAPER_STOP_DRAIN_MS}) for a pass already running
193
+ * to finish, so no reaper delete overlaps the service teardown. Any pass until the next `start()`
194
+ * is a no-op.
195
+ */
196
+ stop(): Promise<void>;
197
+ }
198
+ /**
199
+ * Build the reaper. Construction does no I/O; nothing happens until `start()` or `runPass()`.
200
+ */
201
+ export declare function createLifecycleReaper(deps: LifecycleReaperDeps): LifecycleReaper;
202
+ //# sourceMappingURL=lifecycle-reaper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-reaper.d.ts","sourceRoot":"","sources":["../../src/runtime/lifecycle-reaper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,390 @@
1
+ /**
2
+ * The lifecycle reaper: a runtime exists iff its strategy is open.
3
+ *
4
+ * Every close path — raw `strategy_close`, close.py, the web app, a withdraw to $0,
5
+ * `strategy_close_positions` — ends at the backend, and none of them tells the box. Without this
6
+ * module a closed strategy's runtime keeps scanning, logging and posting until something deletes it
7
+ * by hand; boot (since 3.0.119) merely declines to start it again, leaving the registry row behind.
8
+ * The reaper reads strategy status for every registered wallet and removes the runtimes whose
9
+ * strategy the platform reports gone.
10
+ *
11
+ * Safety invariant — deletion needs POSITIVE evidence: `CLOSED` from a successful read. That is the
12
+ * only thing the reaper ever deletes on.
13
+ * Any other dead status (`dead_not_closed`: FAILED, and the INACTIVE/TERMINATED/CLOSING_DONE names
14
+ * `isDead` also accepts) is reported and never deleted. Only CLOSED means a close ran: strategy-manager
15
+ * reaches it through the offboarding flow, which closes the positions first. FAILED is written by
16
+ * onboarding's final-failure path, which refunds the withdrawable balance but closes nothing, and a
17
+ * FAILED strategy cannot be closed afterwards (CloseStrategy accepts only ACTIVE or PAUSED). If it
18
+ * holds positions, the runtime's DSL is their only stop, and deleting the runtime tears it down.
19
+ * A wallet missing from a successful read (`wallet_absent`) is reported and never deleted: real
20
+ * absence is an edge case (0 of ~345 boot wallets over 7 days), while a well-formed answer can omit
21
+ * a live wallet (the backend's address filter is case-sensitive and scoped to the token's user).
22
+ * A failed, timed-out or unrecognisable read, a closing / live / paused / unknown status, and a row
23
+ * without a usable wallet never delete anything.
24
+ */
25
+ import { isClosing, isDead } from "../senpi/strategy-status.js";
26
+ import { isTrackableWallet, readBootWalletStatuses, trackableWalletKeys, } from "./boot-skip.js";
27
+ import { buildReapCandidateEvent, buildReapSkippedEvent, buildReapUnknownWalletEvent, } from "./lifecycle-events.js";
28
+ /** The one status the reaper deletes on; see the module doc for why the other dead ones are held. */
29
+ const REAPABLE_STATUS = "CLOSED";
30
+ /**
31
+ * Decide one registry row against one successful status read.
32
+ *
33
+ * A failed read never reaches this function — the controller skips the whole pass — so "unreadable"
34
+ * cannot be mistaken for "absent" here by construction.
35
+ */
36
+ export function decideReap(entry, answer) {
37
+ if (!isTrackableWallet(entry.wallet))
38
+ return { action: "untracked" };
39
+ const status = answer.statuses.get(entry.wallet.trim().toLowerCase());
40
+ if (status === undefined)
41
+ return { action: "hold", reason: "wallet_absent" };
42
+ if (isDead(status)) {
43
+ return status.toUpperCase() === REAPABLE_STATUS
44
+ ? { action: "reap", reason: "closed_status", status }
45
+ : { action: "hold", reason: "dead_not_closed", status };
46
+ }
47
+ if (isClosing(status))
48
+ return { action: "keep", lifecycle: "closing", status };
49
+ // ACTIVE, PAUSED, initializing, MANUAL_REVIEW, or a status added after this release: all alive
50
+ // as far as the reaper is concerned. Reaping requires positive death.
51
+ return { action: "keep", lifecycle: "open", status };
52
+ }
53
+ /**
54
+ * The lifecycle a read status maps to; `undefined` means the (confirmed) answer did not carry the
55
+ * wallet.
56
+ */
57
+ export function lifecycleForStatus(status) {
58
+ if (status === undefined)
59
+ return "absent";
60
+ if (isDead(status))
61
+ return "closed";
62
+ if (isClosing(status))
63
+ return "closing";
64
+ return "open";
65
+ }
66
+ // ── Controller ──────────────────────────────────────────────────────────────────────────────────
67
+ /**
68
+ * Release default for deleting candidates. `false` = observe-only: candidates are emitted as
69
+ * `runtime.reap_candidate` and nothing is deleted. It flips in the release after the observe-only
70
+ * fleet validation (plan Task 8); `SENPI_REAPER_ENFORCE` overrides it per box.
71
+ */
72
+ export const REAPER_ENFORCEMENT_DEFAULT = false;
73
+ /** Delay between `start()` completing and the post-boot pass — lets MCP and the gateway settle. */
74
+ export const REAPER_BOOT_DELAY_MS = 120_000;
75
+ /** Sweep cadence. Bounds how long a closed strategy that never trades keeps its runtime. */
76
+ export const REAPER_SWEEP_INTERVAL_MS = 60 * 60 * 1000;
77
+ /**
78
+ * Per-wallet debounce for the `STRATEGY_NOT_ACTIVE` escalation. Matches the backend-pause TTL: the
79
+ * open action re-probes a paused wallet about that often, and each rejection would otherwise re-read.
80
+ */
81
+ export const REAPER_WALLET_DEBOUNCE_MS = 5 * 60 * 1000;
82
+ /**
83
+ * Whether this box deletes candidates. `SENPI_REAPER_ENFORCE=true|false` wins; any other value (or
84
+ * none) falls back to {@link REAPER_ENFORCEMENT_DEFAULT} — a typo must never turn deletion on.
85
+ */
86
+ export function resolveReaperEnforcement(env) {
87
+ const raw = env.SENPI_REAPER_ENFORCE?.trim().toLowerCase();
88
+ if (raw === "true")
89
+ return true;
90
+ if (raw === "false")
91
+ return false;
92
+ return REAPER_ENFORCEMENT_DEFAULT;
93
+ }
94
+ /**
95
+ * An explicit post-boot delay from `SENPI_REAPER_BOOT_DELAY_MS` (a non-negative integer), else
96
+ * `undefined`. For dev-box testing (run the post-boot pass at once) and the plugin tests; being set
97
+ * at all also arms the reaper's timers where the unit-test seam `SENPI_RUNTIME_API_DISABLED` would
98
+ * otherwise keep them off.
99
+ */
100
+ export function resolveReaperBootDelayMs(env) {
101
+ const raw = env.SENPI_REAPER_BOOT_DELAY_MS?.trim();
102
+ if (raw === undefined || !/^\d+$/.test(raw))
103
+ return undefined;
104
+ return Number(raw);
105
+ }
106
+ /**
107
+ * How long `stop()` waits for a pass already under way to finish its current step. A pass checks
108
+ * for the stop before every delete, so this bounds one status read or one delete, not a whole pass.
109
+ */
110
+ export const REAPER_STOP_DRAIN_MS = 10_000;
111
+ /**
112
+ * How long the reaper waits on one delete. The delete's DSL teardown and `stop()` have no deadline
113
+ * of their own; without this, one that never settles holds the single-flight slot forever and every
114
+ * later sweep and not-active check silently does nothing until restart. On expiry the delete is
115
+ * logged as not removed, the pass moves on, and the delete itself keeps running in the background.
116
+ * It still holds the runtime's change lock, so a later pass's attempt on that id is a CONFLICT, not
117
+ * a second delete.
118
+ */
119
+ export const REAPER_DELETE_TIMEOUT_MS = 5 * 60 * 1000;
120
+ /** Marker for a wallet a confirmed answer did not carry. */
121
+ const ABSENT = Symbol("absent");
122
+ /**
123
+ * Build the reaper. Construction does no I/O; nothing happens until `start()` or `runPass()`.
124
+ */
125
+ export function createLifecycleReaper(deps) {
126
+ const now = deps.now ?? Date.now;
127
+ const debounceMs = deps.walletDebounceMs ?? REAPER_WALLET_DEBOUNCE_MS;
128
+ const lastWalletCheck = new Map();
129
+ const lastStatus = new Map();
130
+ const reportedUnknown = new Set();
131
+ /**
132
+ * What each runtime was last reported as a candidate for. Every sweep re-reads every row, so a
133
+ * candidate that stays put would log the same warn 24 times a day, and a count of candidate events
134
+ * would overstate distinct runtimes. A runtime is reported again when its reason, status or wallet
135
+ * changes, or when it stops being a candidate and becomes one again. Per process: a restart reports
136
+ * each candidate once more, which also confirms it is still there.
137
+ */
138
+ const reportedCandidate = new Map();
139
+ let bootTimer;
140
+ let sweepTimer;
141
+ let stopped = false;
142
+ /**
143
+ * Set by `start()`, cleared by `stop()`. `checkWallet` waits for it: boot hands the escalation to
144
+ * each runtime while later ones are still being wired, and a delete in that window finds no
145
+ * launcher to stop yet — boot then registers and starts one for a runtime that no longer exists.
146
+ * The post-boot pass covers anything that escalated earlier.
147
+ */
148
+ let started = false;
149
+ /** Bumped by every `stop()`: a pass started before it stays cancelled even after a restart. */
150
+ let generation = 0;
151
+ /** The running pass, if any: the single-flight guard, and what `stop()` drains. */
152
+ let inFlightPass;
153
+ function recordStatuses(requested, statuses) {
154
+ const confirmed = requested.some((w) => statuses.has(w));
155
+ for (const wallet of requested) {
156
+ const status = statuses.get(wallet);
157
+ if (status !== undefined)
158
+ lastStatus.set(wallet, status);
159
+ else if (confirmed)
160
+ lastStatus.set(wallet, ABSENT);
161
+ }
162
+ }
163
+ function lifecycleOf(wallet) {
164
+ if (!isTrackableWallet(wallet))
165
+ return { lifecycle: "unknown" };
166
+ const known = lastStatus.get(wallet.trim().toLowerCase());
167
+ if (known === undefined)
168
+ return { lifecycle: "unchecked" };
169
+ if (known === ABSENT)
170
+ return { lifecycle: "absent" };
171
+ return { lifecycle: lifecycleForStatus(known), strategyStatus: known };
172
+ }
173
+ /** Emit `runtime.reap_candidate` unless this runtime was already reported for the same thing. */
174
+ function reportCandidate(input) {
175
+ const key = `${input.reason}|${input.strategyStatus ?? ""}|${input.address.trim().toLowerCase()}`;
176
+ if (reportedCandidate.get(input.runtimeId) === key)
177
+ return;
178
+ reportedCandidate.set(input.runtimeId, key);
179
+ deps.emit(() => buildReapCandidateEvent(input));
180
+ }
181
+ async function reap(entry, decision, trigger) {
182
+ const wallet = entry.wallet.trim().toLowerCase();
183
+ reportCandidate({
184
+ runtimeId: entry.id,
185
+ address: wallet,
186
+ reason: decision.reason,
187
+ strategyStatus: decision.status,
188
+ enforce: deps.enforce,
189
+ actionable: true,
190
+ trigger,
191
+ });
192
+ if (!deps.enforce)
193
+ return;
194
+ const timeoutMs = deps.deleteTimeoutMs ?? REAPER_DELETE_TIMEOUT_MS;
195
+ let deadline;
196
+ let result;
197
+ try {
198
+ result = await Promise.race([
199
+ deps.deleteRuntime(entry.id, decision.reason, wallet),
200
+ new Promise((resolve) => {
201
+ deadline = setTimeout(() => resolve({
202
+ ok: false,
203
+ code: "TIMEOUT",
204
+ message: `the delete did not finish within ${timeoutMs}ms; it continues in the background`,
205
+ }), timeoutMs);
206
+ deadline.unref?.();
207
+ }),
208
+ ]);
209
+ }
210
+ catch (err) {
211
+ result = { ok: false, code: "THREW", message: err instanceof Error ? err.message : String(err) };
212
+ }
213
+ finally {
214
+ clearTimeout(deadline);
215
+ }
216
+ if (result.ok) {
217
+ // Ids are reused on reinstall; a new runtime under this id that closes again is a new candidate.
218
+ reportedCandidate.delete(entry.id);
219
+ deps.logger.info("[senpi-runtime][reaper] removed runtime: its strategy is gone", {
220
+ runtimeId: entry.id,
221
+ wallet,
222
+ reason: decision.reason,
223
+ });
224
+ }
225
+ else {
226
+ deps.logger.warn("[senpi-runtime][reaper] runtime not removed", {
227
+ runtimeId: entry.id,
228
+ wallet,
229
+ code: result.code,
230
+ err: result.message,
231
+ });
232
+ }
233
+ }
234
+ async function pass(trigger, onlyWallet) {
235
+ const gen = generation;
236
+ const cancelled = () => stopped || gen !== generation;
237
+ let runtimes;
238
+ try {
239
+ runtimes = (await deps.readRegistry()).runtimes;
240
+ }
241
+ catch (err) {
242
+ deps.logger.warn("[senpi-runtime][reaper] registry unreadable; pass skipped", {
243
+ err: err instanceof Error ? err.message : String(err),
244
+ });
245
+ deps.emit(() => buildReapSkippedEvent({ skipReason: "registry_unreadable", trigger, walletCount: 0 }));
246
+ return;
247
+ }
248
+ const scoped = onlyWallet === undefined
249
+ ? runtimes
250
+ : runtimes.filter((e) => isTrackableWallet(e.wallet) && e.wallet.trim().toLowerCase() === onlyWallet);
251
+ if (onlyWallet === undefined) {
252
+ for (const entry of scoped) {
253
+ if (!isTrackableWallet(entry.wallet) && !reportedUnknown.has(entry.id)) {
254
+ reportedUnknown.add(entry.id);
255
+ deps.emit(() => buildReapUnknownWalletEvent(entry.id));
256
+ }
257
+ }
258
+ }
259
+ const wallets = trackableWalletKeys(scoped);
260
+ if (wallets.length === 0 || cancelled())
261
+ return;
262
+ const read = await readBootWalletStatuses(wallets, deps.readStatuses, deps.readTimeoutMs);
263
+ if (!read.ok) {
264
+ deps.logger.warn("[senpi-runtime][reaper] strategy status unreadable; nothing removed this pass", {
265
+ trigger,
266
+ reason: read.reason,
267
+ err: read.message,
268
+ });
269
+ deps.emit(() => buildReapSkippedEvent({
270
+ skipReason: read.reason === "timeout" ? "read_timeout" : "read_failed",
271
+ trigger,
272
+ walletCount: wallets.length,
273
+ }));
274
+ return;
275
+ }
276
+ recordStatuses(wallets, read.statuses);
277
+ const answer = { statuses: read.statuses };
278
+ for (const entry of scoped) {
279
+ if (cancelled())
280
+ return;
281
+ if (!isTrackableWallet(entry.wallet))
282
+ continue;
283
+ const decision = decideReap(entry, answer);
284
+ if (decision.action === "reap") {
285
+ await reap({ id: entry.id, wallet: entry.wallet }, decision, trigger);
286
+ }
287
+ else if (decision.action === "hold") {
288
+ reportCandidate({
289
+ runtimeId: entry.id,
290
+ address: entry.wallet,
291
+ reason: decision.reason,
292
+ strategyStatus: decision.reason === "dead_not_closed" ? decision.status : undefined,
293
+ enforce: deps.enforce,
294
+ actionable: false,
295
+ trigger,
296
+ });
297
+ }
298
+ else {
299
+ reportedCandidate.delete(entry.id);
300
+ }
301
+ }
302
+ // A full pass saw every registered row: forget runtimes that are no longer registered.
303
+ if (onlyWallet === undefined && !cancelled()) {
304
+ const registered = new Set(scoped.map((e) => e.id));
305
+ for (const id of reportedCandidate.keys()) {
306
+ if (!registered.has(id))
307
+ reportedCandidate.delete(id);
308
+ }
309
+ }
310
+ }
311
+ async function runPass(trigger, onlyWallet) {
312
+ if (stopped || inFlightPass !== undefined)
313
+ return;
314
+ inFlightPass = (async () => {
315
+ try {
316
+ await pass(trigger, onlyWallet?.trim().toLowerCase());
317
+ }
318
+ catch (err) {
319
+ // Backstop: every expected failure is handled inside `pass`; nothing may escape to a timer
320
+ // callback or to the open action that escalated.
321
+ deps.logger.warn("[senpi-runtime][reaper] pass failed", {
322
+ trigger,
323
+ err: err instanceof Error ? err.message : String(err),
324
+ });
325
+ }
326
+ })();
327
+ try {
328
+ await inFlightPass;
329
+ }
330
+ finally {
331
+ inFlightPass = undefined;
332
+ }
333
+ }
334
+ function checkWallet(wallet) {
335
+ if (!started || stopped || inFlightPass !== undefined || !isTrackableWallet(wallet))
336
+ return;
337
+ const key = wallet.trim().toLowerCase();
338
+ const last = lastWalletCheck.get(key);
339
+ if (last !== undefined && now() - last < debounceMs)
340
+ return;
341
+ lastWalletCheck.set(key, now());
342
+ void runPass("not_active", key);
343
+ }
344
+ function start() {
345
+ if (bootTimer !== undefined)
346
+ return;
347
+ // The plugin service can be stopped and started again in one process (as the auto-update
348
+ // coordinator is); a restart must re-arm the reaper, not leave it silently off.
349
+ stopped = false;
350
+ started = true;
351
+ bootTimer = setTimeout(() => {
352
+ void runPass("post_boot");
353
+ }, deps.bootDelayMs ?? REAPER_BOOT_DELAY_MS);
354
+ bootTimer.unref?.();
355
+ sweepTimer = setInterval(() => {
356
+ void runPass("sweep");
357
+ }, deps.sweepIntervalMs ?? REAPER_SWEEP_INTERVAL_MS);
358
+ sweepTimer.unref?.();
359
+ }
360
+ async function stop() {
361
+ stopped = true;
362
+ started = false;
363
+ generation += 1;
364
+ if (bootTimer !== undefined)
365
+ clearTimeout(bootTimer);
366
+ if (sweepTimer !== undefined)
367
+ clearInterval(sweepTimer);
368
+ bootTimer = undefined;
369
+ sweepTimer = undefined;
370
+ const running = inFlightPass;
371
+ if (running === undefined)
372
+ return;
373
+ let drainTimer;
374
+ const drained = await Promise.race([
375
+ running.then(() => true),
376
+ new Promise((resolve) => {
377
+ drainTimer = setTimeout(() => resolve(false), deps.stopDrainMs ?? REAPER_STOP_DRAIN_MS);
378
+ drainTimer.unref?.();
379
+ }),
380
+ ]);
381
+ clearTimeout(drainTimer);
382
+ if (!drained) {
383
+ deps.logger.warn("[senpi-runtime][reaper] a pass was still running at stop; teardown continues without it", {
384
+ drainMs: deps.stopDrainMs ?? REAPER_STOP_DRAIN_MS,
385
+ });
386
+ }
387
+ }
388
+ return { runPass, checkWallet, recordStatuses, lifecycleOf, start, stop };
389
+ }
390
+ //# sourceMappingURL=lifecycle-reaper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-reaper.js","sourceRoot":"","sources":["../../src/runtime/lifecycle-reaper.ts"],"names":[],"mappings":"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"}
@@ -22,22 +22,12 @@ export interface RunReconcileDeps {
22
22
  strategyState: StrategyState;
23
23
  senpi: SenpiClient;
24
24
  stateManager: StateManager;
25
- /**
26
- * adoptPosition + resizePosition — satisfied by DslPlugin or a compatible adopter.
27
- * The opts shape is stated inline (not imported from the DSL handlers) so the port stays
28
- * the narrow thing reconcile actually needs: the tick that caused the adopt, when there is one.
29
- */
25
+ /** adoptPosition + resizePosition — satisfied by DslPlugin or a compatible adopter. */
30
26
  adopter: {
31
- adoptPosition(p: PositionOpenedEvent, opts?: {
32
- tickId?: string;
33
- }): Promise<void>;
34
- resizePosition(p: PositionOpenedEvent, opts?: {
35
- tickId?: string;
36
- }): Promise<void>;
27
+ adoptPosition(p: PositionOpenedEvent): Promise<void>;
28
+ resizePosition(p: PositionOpenedEvent): Promise<void>;
37
29
  };
38
30
  bus?: SenpiEventBus;
39
- /** The monitor tick driving this reconcile, when one is; absent on the boot reconcile. */
40
- tickId?: string;
41
31
  /**
42
32
  * Serializes the whole fetch+decide+execute pass against the trade loop / other reconciles.
43
33
  * Default: run directly (T5 will pass a reconcileMutex-backed serializer).
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-run.d.ts","sourceRoot":"","sources":["../../src/runtime/reconcile-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAStD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE;QACP,aAAa,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjF,cAAc,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACnF,CAAC;IACF,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,GACd,mBAAmB,CAWrB;AAID,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC,CA6IxD"}
1
+ {"version":3,"file":"reconcile-run.d.ts","sourceRoot":"","sources":["../../src/runtime/reconcile-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAStD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,uFAAuF;IACvF,OAAO,EAAE;QACP,aAAa,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACrD,cAAc,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACvD,CAAC;IACF,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,GACd,mBAAmB,CAWrB;AAID,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC,CA2IxD"}