@reefclaw/connect 0.1.10 → 0.1.12

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 (396) hide show
  1. package/assets/bridge/gateway/event-parser.d.ts +6 -1
  2. package/assets/bridge/gateway/event-parser.js +19 -2
  3. package/assets/bridge/gateway/heartbeat-cron.d.ts +27 -0
  4. package/assets/bridge/gateway/heartbeat-cron.js +85 -0
  5. package/assets/bridge/gateway/poller.d.ts +1 -0
  6. package/assets/bridge/gateway/poller.js +14 -2
  7. package/assets/bridge/providers/gateway.d.ts +24 -2
  8. package/assets/bridge/providers/gateway.js +124 -25
  9. package/assets/bridge/setup.js +6 -51
  10. package/assets/shared/signals/conditions/registry.js +11 -2
  11. package/assets/shared/signals/strategy-adapter.js +17 -7
  12. package/assets/skill/SKILL.md +2 -0
  13. package/dist/deps.js +2 -2
  14. package/dist/plugin.js +35 -7
  15. package/package.json +1 -1
  16. package/assets/plugin/audit/mode-transition-audit.d.ts +0 -11
  17. package/assets/plugin/audit/mode-transition-audit.js +0 -29
  18. package/assets/plugin/balance-utils.d.ts +0 -36
  19. package/assets/plugin/balance-utils.js +0 -98
  20. package/assets/plugin/ccxt/binance-ban-gate.d.ts +0 -47
  21. package/assets/plugin/ccxt/binance-ban-gate.js +0 -420
  22. package/assets/plugin/ccxt/binance-private.d.ts +0 -346
  23. package/assets/plugin/ccxt/binance-private.js +0 -1525
  24. package/assets/plugin/ccxt/binance-public.d.ts +0 -34
  25. package/assets/plugin/ccxt/binance-public.js +0 -180
  26. package/assets/plugin/ccxt/intel-public.d.ts +0 -25
  27. package/assets/plugin/ccxt/intel-public.js +0 -80
  28. package/assets/plugin/ccxt/public-market-data-api.d.ts +0 -12
  29. package/assets/plugin/ccxt/public-market-data-api.js +0 -9
  30. package/assets/plugin/config/agent-config-client.d.ts +0 -77
  31. package/assets/plugin/config/agent-config-client.js +0 -188
  32. package/assets/plugin/config/agent-config-poller.d.ts +0 -32
  33. package/assets/plugin/config/agent-config-poller.js +0 -101
  34. package/assets/plugin/config/brackets-config.d.ts +0 -22
  35. package/assets/plugin/config/brackets-config.js +0 -58
  36. package/assets/plugin/config/entitlement-gate.d.ts +0 -51
  37. package/assets/plugin/config/entitlement-gate.js +0 -137
  38. package/assets/plugin/config/gate-store.d.ts +0 -18
  39. package/assets/plugin/config/gate-store.js +0 -61
  40. package/assets/plugin/config/plugin-config-io.d.ts +0 -215
  41. package/assets/plugin/config/plugin-config-io.js +0 -111
  42. package/assets/plugin/config/position-review-config.d.ts +0 -35
  43. package/assets/plugin/config/position-review-config.js +0 -105
  44. package/assets/plugin/config/tool-gate.d.ts +0 -53
  45. package/assets/plugin/config/tool-gate.js +0 -128
  46. package/assets/plugin/config/user-data-stream-config.d.ts +0 -89
  47. package/assets/plugin/config/user-data-stream-config.js +0 -239
  48. package/assets/plugin/connector-supervisor.d.ts +0 -36
  49. package/assets/plugin/connector-supervisor.js +0 -149
  50. package/assets/plugin/exchange-adapter.d.ts +0 -65
  51. package/assets/plugin/exchange-adapter.js +0 -4
  52. package/assets/plugin/index.d.ts +0 -30
  53. package/assets/plugin/index.js +0 -2678
  54. package/assets/plugin/ingest/pending-entry-metadata.d.ts +0 -74
  55. package/assets/plugin/ingest/pending-entry-metadata.js +0 -236
  56. package/assets/plugin/ingest/position-auto-capture.d.ts +0 -103
  57. package/assets/plugin/ingest/position-auto-capture.js +0 -411
  58. package/assets/plugin/ingest/position-decisions-client.d.ts +0 -322
  59. package/assets/plugin/ingest/position-decisions-client.js +0 -296
  60. package/assets/plugin/ingest/readiness-reporter.d.ts +0 -42
  61. package/assets/plugin/ingest/readiness-reporter.js +0 -163
  62. package/assets/plugin/ingest/reconcile-db-vs-exchange.d.ts +0 -13
  63. package/assets/plugin/ingest/reconcile-db-vs-exchange.js +0 -114
  64. package/assets/plugin/ingest/reconciler-cleanup.d.ts +0 -37
  65. package/assets/plugin/ingest/reconciler-cleanup.js +0 -147
  66. package/assets/plugin/ingest/rest-gap-filler.d.ts +0 -191
  67. package/assets/plugin/ingest/rest-gap-filler.js +0 -565
  68. package/assets/plugin/ingest/touched-symbols-store.d.ts +0 -25
  69. package/assets/plugin/ingest/touched-symbols-store.js +0 -96
  70. package/assets/plugin/ingest/trade-store-client.d.ts +0 -40
  71. package/assets/plugin/ingest/trade-store-client.js +0 -125
  72. package/assets/plugin/ingest/ws-ingest.d.ts +0 -43
  73. package/assets/plugin/ingest/ws-ingest.js +0 -126
  74. package/assets/plugin/learning/setup-family.d.ts +0 -21
  75. package/assets/plugin/learning/setup-family.js +0 -103
  76. package/assets/plugin/lifecycle/install-signal-handlers.d.ts +0 -33
  77. package/assets/plugin/lifecycle/install-signal-handlers.js +0 -112
  78. package/assets/plugin/lifecycle/shutdown-coordinator.d.ts +0 -43
  79. package/assets/plugin/lifecycle/shutdown-coordinator.js +0 -131
  80. package/assets/plugin/lifecycle/trading-operation-lock.d.ts +0 -17
  81. package/assets/plugin/lifecycle/trading-operation-lock.js +0 -14
  82. package/assets/plugin/live/bracket-id.d.ts +0 -26
  83. package/assets/plugin/live/bracket-id.js +0 -112
  84. package/assets/plugin/live/bracket-ledger.d.ts +0 -54
  85. package/assets/plugin/live/bracket-ledger.js +0 -267
  86. package/assets/plugin/live/bracket-manager.d.ts +0 -82
  87. package/assets/plugin/live/bracket-manager.js +0 -478
  88. package/assets/plugin/live/bracket-params.d.ts +0 -22
  89. package/assets/plugin/live/bracket-params.js +0 -124
  90. package/assets/plugin/live/bracket-reconciler.d.ts +0 -95
  91. package/assets/plugin/live/bracket-reconciler.js +0 -573
  92. package/assets/plugin/live/bracket-types.d.ts +0 -102
  93. package/assets/plugin/live/bracket-types.js +0 -8
  94. package/assets/plugin/live/deposit-tracker.d.ts +0 -62
  95. package/assets/plugin/live/deposit-tracker.js +0 -97
  96. package/assets/plugin/live/emergency-controls.d.ts +0 -32
  97. package/assets/plugin/live/emergency-controls.js +0 -226
  98. package/assets/plugin/live/exchange-errors.d.ts +0 -12
  99. package/assets/plugin/live/exchange-errors.js +0 -130
  100. package/assets/plugin/live/exchange-info-cache.d.ts +0 -37
  101. package/assets/plugin/live/exchange-info-cache.js +0 -134
  102. package/assets/plugin/live/fact-subscriber.d.ts +0 -78
  103. package/assets/plugin/live/fact-subscriber.js +0 -182
  104. package/assets/plugin/live/intent-journal.d.ts +0 -42
  105. package/assets/plugin/live/intent-journal.js +0 -122
  106. package/assets/plugin/live/listen-key-manager.d.ts +0 -70
  107. package/assets/plugin/live/listen-key-manager.js +0 -169
  108. package/assets/plugin/live/live-adapter.d.ts +0 -287
  109. package/assets/plugin/live/live-adapter.js +0 -1777
  110. package/assets/plugin/live/live-balance-enricher.d.ts +0 -32
  111. package/assets/plugin/live/live-balance-enricher.js +0 -104
  112. package/assets/plugin/live/live-bracket-api.d.ts +0 -13
  113. package/assets/plugin/live/live-bracket-api.js +0 -20
  114. package/assets/plugin/live/live-state-store.d.ts +0 -194
  115. package/assets/plugin/live/live-state-store.js +0 -450
  116. package/assets/plugin/live/local-signal-service.d.ts +0 -57
  117. package/assets/plugin/live/local-signal-service.js +0 -151
  118. package/assets/plugin/live/local-strategy-evaluator.d.ts +0 -62
  119. package/assets/plugin/live/local-strategy-evaluator.js +0 -131
  120. package/assets/plugin/live/microstructure-assembler.d.ts +0 -54
  121. package/assets/plugin/live/microstructure-assembler.js +0 -148
  122. package/assets/plugin/live/order-poller.d.ts +0 -29
  123. package/assets/plugin/live/order-poller.js +0 -125
  124. package/assets/plugin/live/position-state-store.d.ts +0 -83
  125. package/assets/plugin/live/position-state-store.js +0 -237
  126. package/assets/plugin/live/proposal-decision-listener.d.ts +0 -70
  127. package/assets/plugin/live/proposal-decision-listener.js +0 -292
  128. package/assets/plugin/live/proposal-manager.d.ts +0 -76
  129. package/assets/plugin/live/proposal-manager.js +0 -140
  130. package/assets/plugin/live/rate-limiter.d.ts +0 -47
  131. package/assets/plugin/live/rate-limiter.js +0 -159
  132. package/assets/plugin/live/reconciler.d.ts +0 -39
  133. package/assets/plugin/live/reconciler.js +0 -175
  134. package/assets/plugin/live/setup-buckets.d.ts +0 -7
  135. package/assets/plugin/live/setup-buckets.js +0 -33
  136. package/assets/plugin/live/slippage-tracker.d.ts +0 -45
  137. package/assets/plugin/live/slippage-tracker.js +0 -78
  138. package/assets/plugin/live/stop-watcher.d.ts +0 -60
  139. package/assets/plugin/live/stop-watcher.js +0 -215
  140. package/assets/plugin/live/user-data-active-probe.d.ts +0 -54
  141. package/assets/plugin/live/user-data-active-probe.js +0 -180
  142. package/assets/plugin/live/user-data-stream-controller.d.ts +0 -200
  143. package/assets/plugin/live/user-data-stream-controller.js +0 -579
  144. package/assets/plugin/live/user-data-stream-ws.d.ts +0 -22
  145. package/assets/plugin/live/user-data-stream-ws.js +0 -63
  146. package/assets/plugin/live/user-data-stream.d.ts +0 -243
  147. package/assets/plugin/live/user-data-stream.js +0 -704
  148. package/assets/plugin/logger.d.ts +0 -2
  149. package/assets/plugin/logger.js +0 -2
  150. package/assets/plugin/mfe.d.ts +0 -21
  151. package/assets/plugin/mfe.js +0 -68
  152. package/assets/plugin/onboarding/mode-ladder.d.ts +0 -1
  153. package/assets/plugin/onboarding/mode-ladder.js +0 -3
  154. package/assets/plugin/onboarding/runtime.d.ts +0 -84
  155. package/assets/plugin/onboarding/runtime.js +0 -173
  156. package/assets/plugin/openclaw.plugin.json +0 -92
  157. package/assets/plugin/paper-adapter.d.ts +0 -24
  158. package/assets/plugin/paper-adapter.js +0 -91
  159. package/assets/plugin/persistence/state-manager.d.ts +0 -66
  160. package/assets/plugin/persistence/state-manager.js +0 -222
  161. package/assets/plugin/pinned-plan.d.ts +0 -9
  162. package/assets/plugin/pinned-plan.js +0 -23
  163. package/assets/plugin/portfolio/wave9-admission.d.ts +0 -67
  164. package/assets/plugin/portfolio/wave9-admission.js +0 -262
  165. package/assets/plugin/portfolio/wave9-policy.d.ts +0 -36
  166. package/assets/plugin/portfolio/wave9-policy.js +0 -183
  167. package/assets/plugin/risk/pre-trade-check.d.ts +0 -38
  168. package/assets/plugin/risk/pre-trade-check.js +0 -345
  169. package/assets/plugin/risk/pre-trade-types.d.ts +0 -60
  170. package/assets/plugin/risk/pre-trade-types.js +0 -3
  171. package/assets/plugin/shadow/shadow-tracker.d.ts +0 -36
  172. package/assets/plugin/shadow/shadow-tracker.js +0 -151
  173. package/assets/plugin/shadow/types.d.ts +0 -42
  174. package/assets/plugin/shadow/types.js +0 -20
  175. package/assets/plugin/shared/indicators-extended.d.ts +0 -52
  176. package/assets/plugin/shared/indicators-extended.js +0 -291
  177. package/assets/plugin/shared/indicators.d.ts +0 -15
  178. package/assets/plugin/shared/indicators.js +0 -114
  179. package/assets/plugin/signals/conditions/registry.d.ts +0 -16
  180. package/assets/plugin/signals/conditions/registry.js +0 -1324
  181. package/assets/plugin/signals/conditions/types.d.ts +0 -1
  182. package/assets/plugin/signals/conditions/types.js +0 -4
  183. package/assets/plugin/signals/direction-rules.d.ts +0 -3
  184. package/assets/plugin/signals/direction-rules.js +0 -24
  185. package/assets/plugin/signals/entry-rules.d.ts +0 -6
  186. package/assets/plugin/signals/entry-rules.js +0 -33
  187. package/assets/plugin/signals/serialize-context.d.ts +0 -4
  188. package/assets/plugin/signals/serialize-context.js +0 -39
  189. package/assets/plugin/signals/stop-rules.d.ts +0 -3
  190. package/assets/plugin/signals/stop-rules.js +0 -48
  191. package/assets/plugin/signals/strategy-adapter.d.ts +0 -47
  192. package/assets/plugin/signals/strategy-adapter.js +0 -199
  193. package/assets/plugin/signals/types.d.ts +0 -1
  194. package/assets/plugin/signals/types.js +0 -8
  195. package/assets/plugin/simulator/exchange-simulator.d.ts +0 -93
  196. package/assets/plugin/simulator/exchange-simulator.js +0 -689
  197. package/assets/plugin/simulator/fill-engine.d.ts +0 -53
  198. package/assets/plugin/simulator/fill-engine.js +0 -280
  199. package/assets/plugin/simulator/paper-market-feed.d.ts +0 -26
  200. package/assets/plugin/simulator/paper-market-feed.js +0 -104
  201. package/assets/plugin/simulator/realistic-fills.d.ts +0 -59
  202. package/assets/plugin/simulator/realistic-fills.js +0 -175
  203. package/assets/plugin/simulator/types.d.ts +0 -224
  204. package/assets/plugin/simulator/types.js +0 -43
  205. package/assets/plugin/strategy/builtin-strategies.d.ts +0 -2
  206. package/assets/plugin/strategy/builtin-strategies.js +0 -113
  207. package/assets/plugin/strategy/condition-registry.d.ts +0 -3
  208. package/assets/plugin/strategy/condition-registry.js +0 -153
  209. package/assets/plugin/strategy/evaluator.d.ts +0 -70
  210. package/assets/plugin/strategy/evaluator.js +0 -98
  211. package/assets/plugin/tools/assessment-validation.d.ts +0 -120
  212. package/assets/plugin/tools/assessment-validation.js +0 -415
  213. package/assets/plugin/tools/attach-brackets.d.ts +0 -39
  214. package/assets/plugin/tools/attach-brackets.js +0 -399
  215. package/assets/plugin/tools/audit-bracket-protection.d.ts +0 -49
  216. package/assets/plugin/tools/audit-bracket-protection.js +0 -527
  217. package/assets/plugin/tools/cancel-all-orders.d.ts +0 -9
  218. package/assets/plugin/tools/cancel-all-orders.js +0 -8
  219. package/assets/plugin/tools/cancel-order.d.ts +0 -14
  220. package/assets/plugin/tools/cancel-order.js +0 -60
  221. package/assets/plugin/tools/check-position-health.d.ts +0 -46
  222. package/assets/plugin/tools/check-position-health.js +0 -194
  223. package/assets/plugin/tools/clear-exchange-credentials.d.ts +0 -24
  224. package/assets/plugin/tools/clear-exchange-credentials.js +0 -70
  225. package/assets/plugin/tools/close-position.d.ts +0 -45
  226. package/assets/plugin/tools/close-position.js +0 -722
  227. package/assets/plugin/tools/create-order.d.ts +0 -82
  228. package/assets/plugin/tools/create-order.js +0 -1516
  229. package/assets/plugin/tools/exit-gate.d.ts +0 -58
  230. package/assets/plugin/tools/exit-gate.js +0 -162
  231. package/assets/plugin/tools/fetch-balance.d.ts +0 -5
  232. package/assets/plugin/tools/fetch-balance.js +0 -4
  233. package/assets/plugin/tools/fetch-ohlcv.d.ts +0 -11
  234. package/assets/plugin/tools/fetch-ohlcv.js +0 -8
  235. package/assets/plugin/tools/fetch-open-orders.d.ts +0 -7
  236. package/assets/plugin/tools/fetch-open-orders.js +0 -4
  237. package/assets/plugin/tools/fetch-positions.d.ts +0 -7
  238. package/assets/plugin/tools/fetch-positions.js +0 -4
  239. package/assets/plugin/tools/fetch-ticker.d.ts +0 -11
  240. package/assets/plugin/tools/fetch-ticker.js +0 -5
  241. package/assets/plugin/tools/get-agent-profile.d.ts +0 -4
  242. package/assets/plugin/tools/get-agent-profile.js +0 -6
  243. package/assets/plugin/tools/get-analytics.d.ts +0 -6
  244. package/assets/plugin/tools/get-analytics.js +0 -7
  245. package/assets/plugin/tools/get-backtest.d.ts +0 -12
  246. package/assets/plugin/tools/get-backtest.js +0 -91
  247. package/assets/plugin/tools/get-basis.d.ts +0 -7
  248. package/assets/plugin/tools/get-basis.js +0 -7
  249. package/assets/plugin/tools/get-bracket-config.d.ts +0 -11
  250. package/assets/plugin/tools/get-bracket-config.js +0 -24
  251. package/assets/plugin/tools/get-cascade-risk.d.ts +0 -7
  252. package/assets/plugin/tools/get-cascade-risk.js +0 -8
  253. package/assets/plugin/tools/get-crypto-metrics.d.ts +0 -18
  254. package/assets/plugin/tools/get-crypto-metrics.js +0 -62
  255. package/assets/plugin/tools/get-cvd.d.ts +0 -6
  256. package/assets/plugin/tools/get-cvd.js +0 -6
  257. package/assets/plugin/tools/get-divergences.d.ts +0 -6
  258. package/assets/plugin/tools/get-divergences.js +0 -6
  259. package/assets/plugin/tools/get-funding-context.d.ts +0 -6
  260. package/assets/plugin/tools/get-funding-context.js +0 -16
  261. package/assets/plugin/tools/get-liquidation-levels.d.ts +0 -7
  262. package/assets/plugin/tools/get-liquidation-levels.js +0 -7
  263. package/assets/plugin/tools/get-liquidation-pulse.d.ts +0 -9
  264. package/assets/plugin/tools/get-liquidation-pulse.js +0 -22
  265. package/assets/plugin/tools/get-market-breadth.d.ts +0 -6
  266. package/assets/plugin/tools/get-market-breadth.js +0 -8
  267. package/assets/plugin/tools/get-market-intel.d.ts +0 -19
  268. package/assets/plugin/tools/get-market-intel.js +0 -116
  269. package/assets/plugin/tools/get-market-structure.d.ts +0 -47
  270. package/assets/plugin/tools/get-market-structure.js +0 -198
  271. package/assets/plugin/tools/get-my-mined-patterns.d.ts +0 -20
  272. package/assets/plugin/tools/get-my-mined-patterns.js +0 -61
  273. package/assets/plugin/tools/get-my-proposed-learnings.d.ts +0 -20
  274. package/assets/plugin/tools/get-my-proposed-learnings.js +0 -55
  275. package/assets/plugin/tools/get-my-recent-reviews.d.ts +0 -22
  276. package/assets/plugin/tools/get-my-recent-reviews.js +0 -66
  277. package/assets/plugin/tools/get-orderbook.d.ts +0 -21
  278. package/assets/plugin/tools/get-orderbook.js +0 -32
  279. package/assets/plugin/tools/get-pattern-scan.d.ts +0 -7
  280. package/assets/plugin/tools/get-pattern-scan.js +0 -8
  281. package/assets/plugin/tools/get-regime.d.ts +0 -6
  282. package/assets/plugin/tools/get-regime.js +0 -7
  283. package/assets/plugin/tools/get-relevant-learnings.d.ts +0 -21
  284. package/assets/plugin/tools/get-relevant-learnings.js +0 -65
  285. package/assets/plugin/tools/get-resting-liquidity.d.ts +0 -6
  286. package/assets/plugin/tools/get-resting-liquidity.js +0 -11
  287. package/assets/plugin/tools/get-risk-scenario.d.ts +0 -29
  288. package/assets/plugin/tools/get-risk-scenario.js +0 -47
  289. package/assets/plugin/tools/get-risk-summary.d.ts +0 -51
  290. package/assets/plugin/tools/get-risk-summary.js +0 -118
  291. package/assets/plugin/tools/get-sentiment.d.ts +0 -4
  292. package/assets/plugin/tools/get-sentiment.js +0 -6
  293. package/assets/plugin/tools/get-session-review.d.ts +0 -7
  294. package/assets/plugin/tools/get-session-review.js +0 -8
  295. package/assets/plugin/tools/get-setup-detail.d.ts +0 -7
  296. package/assets/plugin/tools/get-setup-detail.js +0 -311
  297. package/assets/plugin/tools/get-signals.d.ts +0 -15
  298. package/assets/plugin/tools/get-signals.js +0 -54
  299. package/assets/plugin/tools/get-sizing.d.ts +0 -6
  300. package/assets/plugin/tools/get-sizing.js +0 -6
  301. package/assets/plugin/tools/get-trade-feedback.d.ts +0 -7
  302. package/assets/plugin/tools/get-trade-feedback.js +0 -8
  303. package/assets/plugin/tools/get-trade-flow.d.ts +0 -7
  304. package/assets/plugin/tools/get-trade-flow.js +0 -7
  305. package/assets/plugin/tools/get-volume-analysis.d.ts +0 -21
  306. package/assets/plugin/tools/get-volume-analysis.js +0 -74
  307. package/assets/plugin/tools/get-volume-profile.d.ts +0 -7
  308. package/assets/plugin/tools/get-volume-profile.js +0 -7
  309. package/assets/plugin/tools/get-wave9-status.d.ts +0 -127
  310. package/assets/plugin/tools/get-wave9-status.js +0 -796
  311. package/assets/plugin/tools/helpers.d.ts +0 -26
  312. package/assets/plugin/tools/helpers.js +0 -39
  313. package/assets/plugin/tools/intel-api.d.ts +0 -34
  314. package/assets/plugin/tools/intel-api.js +0 -119
  315. package/assets/plugin/tools/intel-cache.d.ts +0 -25
  316. package/assets/plugin/tools/intel-cache.js +0 -148
  317. package/assets/plugin/tools/list-strategies.d.ts +0 -17
  318. package/assets/plugin/tools/list-strategies.js +0 -23
  319. package/assets/plugin/tools/modify-stop.d.ts +0 -21
  320. package/assets/plugin/tools/modify-stop.js +0 -120
  321. package/assets/plugin/tools/modify-target.d.ts +0 -21
  322. package/assets/plugin/tools/modify-target.js +0 -110
  323. package/assets/plugin/tools/propose-learning.d.ts +0 -22
  324. package/assets/plugin/tools/propose-learning.js +0 -65
  325. package/assets/plugin/tools/query-review-outcomes.d.ts +0 -30
  326. package/assets/plugin/tools/query-review-outcomes.js +0 -64
  327. package/assets/plugin/tools/query-trades.d.ts +0 -21
  328. package/assets/plugin/tools/query-trades.js +0 -37
  329. package/assets/plugin/tools/record-position-reviews.d.ts +0 -38
  330. package/assets/plugin/tools/record-position-reviews.js +0 -147
  331. package/assets/plugin/tools/save-strategy.d.ts +0 -16
  332. package/assets/plugin/tools/save-strategy.js +0 -46
  333. package/assets/plugin/tools/scan-pairs.d.ts +0 -18
  334. package/assets/plugin/tools/scan-pairs.js +0 -231
  335. package/assets/plugin/tools/score-setup.d.ts +0 -31
  336. package/assets/plugin/tools/score-setup.js +0 -268
  337. package/assets/plugin/tools/set-bracket-requirement.d.ts +0 -18
  338. package/assets/plugin/tools/set-bracket-requirement.js +0 -81
  339. package/assets/plugin/tools/set-exchange-credentials.d.ts +0 -25
  340. package/assets/plugin/tools/set-exchange-credentials.js +0 -80
  341. package/assets/plugin/tools/set-trading-mode.d.ts +0 -26
  342. package/assets/plugin/tools/set-trading-mode.js +0 -135
  343. package/assets/plugin/tools/test-exchange-credentials.d.ts +0 -16
  344. package/assets/plugin/tools/test-exchange-credentials.js +0 -100
  345. package/assets/plugin/tools/toggle-strategy.d.ts +0 -8
  346. package/assets/plugin/tools/toggle-strategy.js +0 -15
  347. package/assets/plugin/trading-params-cache.d.ts +0 -26
  348. package/assets/plugin/trading-params-cache.js +0 -52
  349. package/assets/plugin/types.d.ts +0 -134
  350. package/assets/plugin/types.js +0 -7
  351. package/assets/plugin/util/plugin-paths.d.ts +0 -3
  352. package/assets/plugin/util/plugin-paths.js +0 -15
  353. package/assets/plugin/venues/hyperliquid/hl-balance.d.ts +0 -116
  354. package/assets/plugin/venues/hyperliquid/hl-balance.js +0 -145
  355. package/assets/plugin/venues/hyperliquid/hl-brackets.d.ts +0 -102
  356. package/assets/plugin/venues/hyperliquid/hl-brackets.js +0 -172
  357. package/assets/plugin/venues/hyperliquid/hl-cloid.d.ts +0 -22
  358. package/assets/plugin/venues/hyperliquid/hl-cloid.js +0 -82
  359. package/assets/plugin/venues/hyperliquid/hl-info-cache.d.ts +0 -46
  360. package/assets/plugin/venues/hyperliquid/hl-info-cache.js +0 -125
  361. package/assets/plugin/venues/hyperliquid/hl-live-adapter.d.ts +0 -88
  362. package/assets/plugin/venues/hyperliquid/hl-live-adapter.js +0 -353
  363. package/assets/plugin/venues/hyperliquid/hl-precision.d.ts +0 -61
  364. package/assets/plugin/venues/hyperliquid/hl-precision.js +0 -176
  365. package/assets/plugin/venues/hyperliquid/hl-private.d.ts +0 -88
  366. package/assets/plugin/venues/hyperliquid/hl-private.js +0 -357
  367. package/assets/plugin/venues/hyperliquid/hl-public.d.ts +0 -79
  368. package/assets/plugin/venues/hyperliquid/hl-public.js +0 -429
  369. package/assets/plugin/venues/hyperliquid/hl-rate-gate.d.ts +0 -57
  370. package/assets/plugin/venues/hyperliquid/hl-rate-gate.js +0 -220
  371. package/assets/plugin/venues/hyperliquid/hl-user-stream.d.ts +0 -90
  372. package/assets/plugin/venues/hyperliquid/hl-user-stream.js +0 -220
  373. package/assets/plugin/venues/registry.d.ts +0 -38
  374. package/assets/plugin/venues/registry.js +0 -46
  375. package/assets/plugin/venues/symbols.d.ts +0 -43
  376. package/assets/plugin/venues/symbols.js +0 -107
  377. package/assets/plugin/wave9/live-account-capture.d.ts +0 -67
  378. package/assets/plugin/wave9/live-account-capture.js +0 -435
  379. package/assets/plugin/wave9/live-autonomous-protection.d.ts +0 -39
  380. package/assets/plugin/wave9/live-autonomous-protection.js +0 -112
  381. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.d.ts +0 -33
  382. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.js +0 -115
  383. package/assets/plugin/wave9/live-execution-ledger.d.ts +0 -107
  384. package/assets/plugin/wave9/live-execution-ledger.js +0 -498
  385. package/assets/plugin/wave9/live-position-confirmation.d.ts +0 -18
  386. package/assets/plugin/wave9/live-position-confirmation.js +0 -111
  387. package/assets/plugin/wave9/live-residual-protection.d.ts +0 -18
  388. package/assets/plugin/wave9/live-residual-protection.js +0 -250
  389. package/assets/plugin/wave9/live-startup-reconciliation.d.ts +0 -38
  390. package/assets/plugin/wave9/live-startup-reconciliation.js +0 -454
  391. package/assets/plugin/wave9/live-symbol-ownership.d.ts +0 -20
  392. package/assets/plugin/wave9/live-symbol-ownership.js +0 -132
  393. package/assets/plugin/wave9/paper-admission-guard.d.ts +0 -199
  394. package/assets/plugin/wave9/paper-admission-guard.js +0 -650
  395. package/assets/plugin/wave9/usdm-evidence-provider.d.ts +0 -42
  396. package/assets/plugin/wave9/usdm-evidence-provider.js +0 -133
@@ -170,8 +170,13 @@ export interface ParsedEvent<E extends ProviderEventName = ProviderEventName> {
170
170
  export declare class EventParser {
171
171
  /** Buffered assistant text per runId, with timestamps for expiry */
172
172
  private assistantBuffers;
173
- /** Last decision timestamp (set on lifecycle 'start') */
173
+ /** Last decision timestamp (set on lifecycle 'end' of a non-errored run) */
174
174
  private lastDecisionTs;
175
+ /** Runs that emitted a lifecycle 'error' — their 'end' must NOT count as a
176
+ * decision. Issue #194: bumping on 'start' kept "last decision" fresh
177
+ * through an 18h all-models-failed outage, so the dashboard's 2×-interval
178
+ * "unresponsive" rung could never fire. Bounded (oldest evicted). */
179
+ private erroredRuns;
175
180
  /** Error timestamps within the rolling window */
176
181
  private errorTimestamps;
177
182
  /** Symbol for balance currency extraction */
@@ -417,8 +417,13 @@ const BUFFER_INACTIVITY_MS = 120_000; // 120 seconds — matches webapp streamin
417
417
  export class EventParser {
418
418
  /** Buffered assistant text per runId, with timestamps for expiry */
419
419
  assistantBuffers = new Map();
420
- /** Last decision timestamp (set on lifecycle 'start') */
420
+ /** Last decision timestamp (set on lifecycle 'end' of a non-errored run) */
421
421
  lastDecisionTs = null;
422
+ /** Runs that emitted a lifecycle 'error' — their 'end' must NOT count as a
423
+ * decision. Issue #194: bumping on 'start' kept "last decision" fresh
424
+ * through an 18h all-models-failed outage, so the dashboard's 2×-interval
425
+ * "unresponsive" rung could never fire. Bounded (oldest evicted). */
426
+ erroredRuns = new Set();
422
427
  /** Error timestamps within the rolling window */
423
428
  errorTimestamps = [];
424
429
  /** Symbol for balance currency extraction */
@@ -465,10 +470,15 @@ export class EventParser {
465
470
  }
466
471
  switch (phase) {
467
472
  case 'start':
468
- this.lastDecisionTs = new Date().toISOString();
469
473
  logger.debug(TAG, `Agent turn started: ${runId}`);
470
474
  return [];
471
475
  case 'end': {
476
+ // A decision = a turn that ENDED without erroring. Bumping on 'start'
477
+ // (pre-#194) kept the timestamp fresh while every run failed, hiding
478
+ // an all-models-failed outage from the unresponsive heuristics.
479
+ if (!this.erroredRuns.delete(runId)) {
480
+ this.lastDecisionTs = new Date().toISOString();
481
+ }
472
482
  logger.debug(TAG, `Agent turn ended: ${runId}`);
473
483
  return this.flushAssistantBuffer(runId);
474
484
  }
@@ -476,6 +486,13 @@ export class EventParser {
476
486
  const errMsg = data.error ?? 'Unknown agent error';
477
487
  logger.warn(TAG, `Agent error (run ${runId}): ${errMsg}`);
478
488
  this.errorTimestamps.push(Date.now());
489
+ this.erroredRuns.add(runId);
490
+ // Bound the set: an errored run whose 'end' never arrives would leak.
491
+ if (this.erroredRuns.size > 100) {
492
+ const oldest = this.erroredRuns.values().next().value;
493
+ if (oldest !== undefined)
494
+ this.erroredRuns.delete(oldest);
495
+ }
479
496
  // Still flush the buffer — partial responses are better than nothing
480
497
  return this.flushAssistantBuffer(runId);
481
498
  }
@@ -0,0 +1,27 @@
1
+ export declare const HEARTBEAT_CRON_NAME = "reefclaw-heartbeat";
2
+ export declare const HEARTBEAT_EVERY_MS: number;
3
+ export declare const HEARTBEAT_MESSAGE: string;
4
+ export interface CronRpcClient {
5
+ sendRpc(method: string, params?: Record<string, unknown>): Promise<unknown>;
6
+ }
7
+ /**
8
+ * Whether an existing cron job counts as "the heartbeat already exists".
9
+ * Union of the two matchers already in the codebase: the old setup path
10
+ * matched /reefclaw/, while resolveHeartbeatSeconds() (the cadence reader that
11
+ * drives the dashboard header) matches /heart\s*beat/. A job satisfying either
12
+ * must suppress creation — prod/wisekid boxes carry heartbeat jobs whose names
13
+ * predate the reefclaw- prefix, and a duplicate 15m heartbeat would double the
14
+ * agent's token burn.
15
+ */
16
+ export declare function isHeartbeatLikeName(name: unknown): boolean;
17
+ export type EnsureHeartbeatOutcome = 'already_ensured' | 'found_existing' | 'created' | 'failed';
18
+ /**
19
+ * Ensure a heartbeat cron exists. Non-fatal by contract: every failure path
20
+ * returns 'failed' rather than throwing, and only success writes the marker so
21
+ * a transient gateway error retries on the next boot.
22
+ */
23
+ export declare function ensureHeartbeatCron(opts: {
24
+ rpc: CronRpcClient;
25
+ markerPath: string;
26
+ log: (msg: string) => void;
27
+ }): Promise<EnsureHeartbeatOutcome>;
@@ -0,0 +1,85 @@
1
+ // Ensures the ReefClaw heartbeat cron job exists — once per install — via
2
+ // gateway RPC (`cron.list` / `cron.add`).
3
+ //
4
+ // This replaces the old setup-time shell-out to the openclaw CLI. The CLI's
5
+ // cron commands are themselves gateway RPC clients, so shelling out bought
6
+ // nothing except a subprocess + PATH dependency — and it ran at
7
+ // setup time, when the gateway is typically NOT up yet (setup's printed next
8
+ // step is "Start your OpenClaw agent"), which is why creation silently
9
+ // no-op'd on fresh installs without a running gateway. The bridge holds an
10
+ // authenticated gateway WS by construction, so the ensure runs there instead.
11
+ //
12
+ // Once-per-install semantics: a marker file records that the ensure has run to
13
+ // completion. Without it, a user who deliberately deleted their heartbeat cron
14
+ // would get it resurrected on every bridge boot — the old setup-time path ran
15
+ // once, and that operator-override behaviour must be preserved.
16
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
17
+ import { dirname } from 'path';
18
+ export const HEARTBEAT_CRON_NAME = 'reefclaw-heartbeat';
19
+ export const HEARTBEAT_EVERY_MS = 15 * 60 * 1000;
20
+ // Byte-identical to the message the setup-time CLI path created, so new jobs
21
+ // match every existing install's job.
22
+ export const HEARTBEAT_MESSAGE = 'Heartbeat. Run SESSION START mandatory checks, then the full Decision Loop (Steps 0-9) from SKILL.md. ' +
23
+ 'Check tradingMode from fetch_balance — it is your source of truth for paper vs live. ' +
24
+ 'Use tools in parallel where possible.';
25
+ /**
26
+ * Whether an existing cron job counts as "the heartbeat already exists".
27
+ * Union of the two matchers already in the codebase: the old setup path
28
+ * matched /reefclaw/, while resolveHeartbeatSeconds() (the cadence reader that
29
+ * drives the dashboard header) matches /heart\s*beat/. A job satisfying either
30
+ * must suppress creation — prod/wisekid boxes carry heartbeat jobs whose names
31
+ * predate the reefclaw- prefix, and a duplicate 15m heartbeat would double the
32
+ * agent's token burn.
33
+ */
34
+ export function isHeartbeatLikeName(name) {
35
+ return typeof name === 'string' && /reefclaw|heart\s*beat/i.test(name);
36
+ }
37
+ /**
38
+ * Ensure a heartbeat cron exists. Non-fatal by contract: every failure path
39
+ * returns 'failed' rather than throwing, and only success writes the marker so
40
+ * a transient gateway error retries on the next boot.
41
+ */
42
+ export async function ensureHeartbeatCron(opts) {
43
+ const { rpc, markerPath, log } = opts;
44
+ try {
45
+ if (existsSync(markerPath))
46
+ return 'already_ensured';
47
+ // includeDisabled: a job the user disabled still means "exists" — re-adding
48
+ // a duplicate next to a deliberately-disabled one is worse than doing nothing.
49
+ const listResp = (await rpc.sendRpc('cron.list', { includeDisabled: true }));
50
+ const jobs = Array.isArray(listResp?.jobs) ? listResp.jobs : null;
51
+ if (!jobs) {
52
+ log('Heartbeat cron: skipped (cron.list returned no job list)');
53
+ return 'failed';
54
+ }
55
+ const existing = jobs.find((j) => isHeartbeatLikeName(j?.name));
56
+ if (existing) {
57
+ writeMarker(markerPath, { ensuredAt: new Date().toISOString(), found: String(existing.name) });
58
+ log(`Heartbeat cron: already exists (${String(existing.name)})`);
59
+ return 'found_existing';
60
+ }
61
+ // Parity with the params the CLI built for the old setup command
62
+ // (`openclaw cron add --name reefclaw-heartbeat --every 15m --session isolated --message ...`):
63
+ // isolated agentTurn defaults to delivery announce on the 'last' channel.
64
+ await rpc.sendRpc('cron.add', {
65
+ name: HEARTBEAT_CRON_NAME,
66
+ enabled: true,
67
+ schedule: { kind: 'every', everyMs: HEARTBEAT_EVERY_MS },
68
+ sessionTarget: 'isolated',
69
+ wakeMode: 'now',
70
+ payload: { kind: 'agentTurn', message: HEARTBEAT_MESSAGE },
71
+ delivery: { mode: 'announce', channel: 'last' },
72
+ });
73
+ writeMarker(markerPath, { ensuredAt: new Date().toISOString(), created: HEARTBEAT_CRON_NAME });
74
+ log(`Heartbeat cron: created (${HEARTBEAT_CRON_NAME}, every 15m)`);
75
+ return 'created';
76
+ }
77
+ catch (err) {
78
+ log(`Heartbeat cron: skipped (${err instanceof Error ? err.message.split('\n')[0] : String(err)})`);
79
+ return 'failed';
80
+ }
81
+ }
82
+ function writeMarker(markerPath, data) {
83
+ mkdirSync(dirname(markerPath), { recursive: true });
84
+ writeFileSync(markerPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
85
+ }
@@ -4,6 +4,7 @@ import type { TickerData, CandleData, OrderData, MarketStructureData, CryptoMetr
4
4
  export declare const TICKER_INTERVAL_MS = 5000;
5
5
  export declare const OHLCV_INTERVAL_MS = 15000;
6
6
  export declare const POSITIONS_INTERVAL_MS = 10000;
7
+ export declare const OPEN_ORDERS_INTERVAL_MS = 60000;
7
8
  export declare const MARKET_STRUCTURE_INTERVAL_MS = 60000;
8
9
  /** CCXT position — minimal shape needed by poller */
9
10
  export interface CcxtPosition {
@@ -25,7 +25,17 @@ export const OHLCV_INTERVAL_MS = 15_000;
25
25
  // startup handshake is extra live-system surface area for no extra weight.
26
26
  export const POSITIONS_INTERVAL_MS = 10_000;
27
27
  const BALANCE_INTERVAL_MS = 10_000;
28
- const OPEN_ORDERS_INTERVAL_MS = 3_000;
28
+ // 2026-07-21: 3s → 60s, and the poll is now UNSCOPED (no symbol filter). Its
29
+ // only consumer is the fallback cache behind Kill and the reconcile snapshot
30
+ // (no event emission — live order updates ride the WS stream), and both of
31
+ // those are portfolio-wide by contract: a { symbol }-scoped cache silently
32
+ // degraded them to one symbol whenever the fresh all-symbol fetch failed.
33
+ // Unscoped fetchOpenOrders costs Binance weight 80 (regular + algo merged) vs
34
+ // 2 scoped, so the cadence drops to 60s (80/min, vs 40/min before; the old 3s
35
+ // cadence unscoped would be 1600/min ≈ the whole ceiling). Matches the
36
+ // plugin's own 60s algo-refresh; in userDataStream enforce mode the read is
37
+ // served from the WS store at zero REST weight anyway.
38
+ export const OPEN_ORDERS_INTERVAL_MS = 60_000;
29
39
  // 2026-05-15: 30s → 60s. get_market_structure fetches SIX timeframes of
30
40
  // OHLCV per poll (the heaviest single skill poll). Multi-timeframe trend
31
41
  // structure does not meaningfully change in 30s; 60s keeps the Agent-State
@@ -357,7 +367,9 @@ export class Poller {
357
367
  }
358
368
  async pollOpenOrders() {
359
369
  const tool = this.toolMap.fetch_open_orders;
360
- const result = await this.http.invoke(tool, { symbol: this.symbol });
370
+ // Fetch ALL open orders (no symbol filter) — this cache is the fallback
371
+ // for the portfolio-wide Kill + reconcile snapshot (see interval comment).
372
+ const result = await this.http.invoke(tool, {});
361
373
  const orders = [];
362
374
  if (Array.isArray(result.data)) {
363
375
  for (const ccxt of result.data) {
@@ -29,6 +29,8 @@ export declare class GatewayProvider implements OpenClawProvider {
29
29
  private agentIdentity;
30
30
  /** Emit the one-time rollout probe (gateway response shape) only on the first attempt. */
31
31
  private loggedAgentIdentityProbe;
32
+ /** One-shot latch for the boot-time heartbeat-cron ensure (re-armed on failure). */
33
+ private heartbeatCronEnsureStarted;
32
34
  /** Live heartbeat cadence (seconds), read from the OpenClaw cron store; cached 60s. */
33
35
  private heartbeatSeconds?;
34
36
  private heartbeatReadAtMs;
@@ -61,6 +63,18 @@ export declare class GatewayProvider implements OpenClawProvider {
61
63
  /** Track whether we've received at least one balance poll (needed for equity calculation). */
62
64
  private hasReceivedBalance;
63
65
  private readonly quoteCurrency;
66
+ /** issue #213 RED-zone debounce: consecutive RED evaluations + when the
67
+ * streak began + when the position snapshot last refreshed. A RED verdict
68
+ * must PERSIST and SURVIVE a positions refresh before auto-flatten fires —
69
+ * in paper mode the wallet drops by FULL NOTIONAL at open, so a balance
70
+ * poll landing before the new position reaches the snapshot reads the
71
+ * entry's own notional as a phantom drawdown (observed live: a 38%-of-NAV
72
+ * entry reported as a -38.41% RED zone 2s after its fill and was
73
+ * auto-flattened). A race artifact cannot outlive one positions poll; a
74
+ * real drawdown easily persists. */
75
+ private redZoneStreak;
76
+ private redZoneSince;
77
+ private positionsUpdatedAt;
64
78
  private orderTimestamps;
65
79
  private cancelTimestamps;
66
80
  private agentStateInterval;
@@ -301,8 +315,16 @@ export declare class GatewayProvider implements OpenClawProvider {
301
315
  */
302
316
  private reportTradeResult;
303
317
  /**
304
- * Seed lastTradeTs from Intelligence API on startup. One-shot, best-effort.
305
- * Prevents "Last Trade: Never" after every restart when the agent is flat.
318
+ * Seed lastTradeTs from the webapp's exchange-keyed fills ledger on startup.
319
+ * One-shot, best-effort. Prevents "Last Trade: Never" after every restart
320
+ * when the agent is flat.
321
+ *
322
+ * ★ Issue #194: the previous seed read intel's `trade_results` (closed
323
+ * round-trips only, single-symbol), which showed "last trade 365h ago"
324
+ * while the fills ledger had fills from the previous evening. The ledger
325
+ * (`GET /api/internal/trades`) is the source of truth for fills — seed from
326
+ * it, filtered to the current trading book. On any failure leave null:
327
+ * truthful-unknown beats confidently-wrong.
306
328
  */
307
329
  private seedLastTradeTs;
308
330
  /** Symbol formatted for intelligence API (BTC/USDT → BTCUSDT). */
@@ -11,16 +11,26 @@ import { GatewayWsClient } from '../gateway/gateway-ws-client.js';
11
11
  import { discoverTools } from '../gateway/tool-discovery.js';
12
12
  import { EventParser, mapCcxtBalance, mapCcxtOrder, extractLiquidationFields, extractBracketField, } from '../gateway/event-parser.js';
13
13
  import { Poller } from '../gateway/poller.js';
14
+ import { ensureHeartbeatCron } from '../gateway/heartbeat-cron.js';
14
15
  import { computeEquity as _computeEquity, computePositionNotional as _computePositionNotional, computeRiskMetrics as _computeRiskMetrics, DEFAULT_RISK_LIMITS, } from './risk-calculator.js';
15
16
  import { executeKill as _executeKill, executeFlatten as _executeFlatten, executePause as _executePause, executeResume as _executeResume, } from './emergency-commands.js';
16
17
  import { executeSetTradingMode, executeGetBracketConfig, executeSetBracketRequirement, executeSetExchangeCredentials, executeTestExchangeCredentials, executeClearExchangeCredentials, } from './onboarding-commands.js';
17
18
  const TAG = 'gateway';
18
- // ---- Session NAV persistence ----
19
- // Persists sessionStartNav per date so Day P&L survives skill restarts.
20
- const SESSION_NAV_FILENAME = 'session-nav.json';
19
+ // ---- Day-start NAV persistence ----
20
+ // Persists sessionStartNav (the UTC-day P&L anchor) per date so Day P&L
21
+ // survives skill restarts. The file was historically named session-nav.json;
22
+ // renamed to day-start-nav.json 2026-07-22 — "session" was a misnomer (the
23
+ // value anchors the trading DAY, not any auth/session state) and tripped
24
+ // ClawHub's sensitive-file-read heuristic. Reads fall back to the legacy name
25
+ // once, writes go to the new name only.
26
+ const DAY_NAV_FILENAME = 'day-start-nav.json';
27
+ const LEGACY_DAY_NAV_FILENAME = 'session-nav.json';
21
28
  const TRADING_MODE_FILENAME = 'trading-mode.json';
22
- function getSessionNavPath() {
23
- return join(homedir(), '.openclaw', 'workspace', SESSION_NAV_FILENAME);
29
+ function getDayNavPath() {
30
+ return join(homedir(), '.openclaw', 'workspace', DAY_NAV_FILENAME);
31
+ }
32
+ function getLegacyDayNavPath() {
33
+ return join(homedir(), '.openclaw', 'workspace', LEGACY_DAY_NAV_FILENAME);
24
34
  }
25
35
  function getTradingModePath() {
26
36
  return join(homedir(), '.openclaw', 'workspace', TRADING_MODE_FILENAME);
@@ -40,17 +50,20 @@ export function shouldAnchorSessionNav(args) {
40
50
  return args.mode === 'PAPER' && args.sessionDate !== args.today; // paper midnight rollover
41
51
  }
42
52
  function loadSessionStartNav() {
43
- try {
44
- const raw = readFileSync(getSessionNavPath(), 'utf-8');
45
- const data = JSON.parse(raw);
46
- if (data.date === todayDateStr() && typeof data.sessionStartNav === 'number' && data.sessionStartNav > 0) {
47
- return data.sessionStartNav;
53
+ for (const path of [getDayNavPath(), getLegacyDayNavPath()]) {
54
+ try {
55
+ const raw = readFileSync(path, 'utf-8');
56
+ const data = JSON.parse(raw);
57
+ if (data.date === todayDateStr() && typeof data.sessionStartNav === 'number' && data.sessionStartNav > 0) {
58
+ return data.sessionStartNav;
59
+ }
60
+ return null; // Different day or invalid — don't let a stale legacy file shadow it
61
+ }
62
+ catch {
63
+ /* missing or corrupt — try the legacy name */
48
64
  }
49
- return null; // Different day or invalid
50
- }
51
- catch {
52
- return null; // File doesn't exist or is corrupt
53
65
  }
66
+ return null;
54
67
  }
55
68
  /**
56
69
  * Persisted across skill restarts so the FIRST snapshot the gateway emits
@@ -90,7 +103,11 @@ function saveSessionStartNav(nav) {
90
103
  const dir = join(homedir(), '.openclaw', 'workspace');
91
104
  mkdirSync(dir, { recursive: true });
92
105
  const data = { date: todayDateStr(), sessionStartNav: nav };
93
- writeFileSync(getSessionNavPath(), JSON.stringify(data, null, 2), 'utf-8');
106
+ writeFileSync(getDayNavPath(), JSON.stringify(data, null, 2), 'utf-8');
107
+ try {
108
+ unlinkSync(getLegacyDayNavPath());
109
+ }
110
+ catch { /* legacy file already gone */ }
94
111
  }
95
112
  catch (err) {
96
113
  logger.warn(TAG, `Failed to save session NAV: ${err instanceof Error ? err.message : String(err)}`);
@@ -122,6 +139,8 @@ export class GatewayProvider {
122
139
  agentIdentity = {};
123
140
  /** Emit the one-time rollout probe (gateway response shape) only on the first attempt. */
124
141
  loggedAgentIdentityProbe = false;
142
+ /** One-shot latch for the boot-time heartbeat-cron ensure (re-armed on failure). */
143
+ heartbeatCronEnsureStarted = false;
125
144
  /** Live heartbeat cadence (seconds), read from the OpenClaw cron store; cached 60s. */
126
145
  heartbeatSeconds;
127
146
  heartbeatReadAtMs = 0;
@@ -154,6 +173,18 @@ export class GatewayProvider {
154
173
  /** Track whether we've received at least one balance poll (needed for equity calculation). */
155
174
  hasReceivedBalance = false;
156
175
  quoteCurrency;
176
+ /** issue #213 RED-zone debounce: consecutive RED evaluations + when the
177
+ * streak began + when the position snapshot last refreshed. A RED verdict
178
+ * must PERSIST and SURVIVE a positions refresh before auto-flatten fires —
179
+ * in paper mode the wallet drops by FULL NOTIONAL at open, so a balance
180
+ * poll landing before the new position reaches the snapshot reads the
181
+ * entry's own notional as a phantom drawdown (observed live: a 38%-of-NAV
182
+ * entry reported as a -38.41% RED zone 2s after its fill and was
183
+ * auto-flattened). A race artifact cannot outlive one positions poll; a
184
+ * real drawdown easily persists. */
185
+ redZoneStreak = 0;
186
+ redZoneSince = 0;
187
+ positionsUpdatedAt = 0;
157
188
  // ---- Rate tracking ----
158
189
  orderTimestamps = [];
159
190
  cancelTimestamps = [];
@@ -658,6 +689,7 @@ export class GatewayProvider {
658
689
  }
659
690
  // Update internal state with fresh data (poller may be paused during reconnect)
660
691
  this.positions = positions;
692
+ this.positionsUpdatedAt = Date.now();
661
693
  this.balance = balance;
662
694
  this.openOrders = openOrders;
663
695
  // Compute current NAV for portfolio percent calculation (consistent with computeRiskMetrics)
@@ -1057,6 +1089,24 @@ export class GatewayProvider {
1057
1089
  // every (re)connect so a model swap — which restarts the gateway and drops
1058
1090
  // this socket — is reflected on the next handshake.
1059
1091
  void this.refreshAgentIdentity();
1092
+ // Ensure the heartbeat cron exists — via gateway RPC, once per install
1093
+ // (marker-file guarded; a deliberate operator delete is never resurrected).
1094
+ // Lives here instead of setup because setup runs before the gateway is up.
1095
+ if (!this.heartbeatCronEnsureStarted) {
1096
+ this.heartbeatCronEnsureStarted = true;
1097
+ const ws = this.wsClient;
1098
+ if (ws) {
1099
+ void ensureHeartbeatCron({
1100
+ rpc: ws,
1101
+ markerPath: join(homedir(), '.openclaw', 'workspace', 'heartbeat-cron-ensured.json'),
1102
+ log: (msg) => logger.info(TAG, msg),
1103
+ }).then((outcome) => {
1104
+ // A transient failure retries on the next (re)connect, not just next boot.
1105
+ if (outcome === 'failed')
1106
+ this.heartbeatCronEnsureStarted = false;
1107
+ });
1108
+ }
1109
+ }
1060
1110
  // Start periodic agent state emission (every 5s)
1061
1111
  if (!this.agentStateInterval) {
1062
1112
  this.agentStateInterval = setInterval(() => {
@@ -1201,6 +1251,7 @@ export class GatewayProvider {
1201
1251
  }
1202
1252
  }
1203
1253
  this.positions = positions;
1254
+ this.positionsUpdatedAt = Date.now();
1204
1255
  this.hasReceivedPositions = true;
1205
1256
  this.trySetSessionStartNav();
1206
1257
  this.emitRiskUpdate();
@@ -1575,7 +1626,11 @@ export class GatewayProvider {
1575
1626
  this.hasReceivedPositions = false;
1576
1627
  this.pendingEmptyPoll = false;
1577
1628
  try {
1578
- unlinkSync(getSessionNavPath());
1629
+ unlinkSync(getDayNavPath());
1630
+ }
1631
+ catch { /* ok if missing */ }
1632
+ try {
1633
+ unlinkSync(getLegacyDayNavPath());
1579
1634
  }
1580
1635
  catch { /* ok if missing */ }
1581
1636
  logger.info(TAG, `Session start NAV reset on mode change (${prevMode} → ${this.tradingMode})`);
@@ -1890,10 +1945,32 @@ export class GatewayProvider {
1890
1945
  // physically impossible under normal market conditions on a bracketed
1891
1946
  // account. If the math says worse than that, it's bad data — log loudly
1892
1947
  // and skip instead of closing real positions on a calculation error.
1948
+ // 4. Persistence debounce (issue #213): the balance poll and the position
1949
+ // snapshot are not atomic. In PAPER mode the simulator deducts FULL
1950
+ // NOTIONAL from the wallet at open, so in the seconds after a fill the
1951
+ // wallet already reflects the deduction while `this.positions` does not
1952
+ // yet contain the new position — equity spuriously drops by exactly the
1953
+ // entry's notional and any entry >2.5% of NAV reads as a RED drawdown
1954
+ // (observed live: -38.41% reported 2s after a 38%-of-NAV entry; the
1955
+ // just-opened position was auto-flattened). A RED verdict therefore
1956
+ // only licenses the flatten once it (a) persists across REQUIRED_STREAK
1957
+ // consecutive evaluations, (b) has been standing MIN_RED_PERSISTENCE_MS,
1958
+ // and (c) has SURVIVED at least one positions refresh since it began —
1959
+ // the race artifact cannot outlive one positions poll (~10s), while a
1960
+ // real -2.5% drawdown trivially persists 25s. Cost on a real event:
1961
+ // the flatten fires ~25-35s later. Cost of a false one: a closed
1962
+ // position, a rejected-orders window, and a corrupted trading record.
1893
1963
  const uptimeMs = Date.now() - this.startedAt;
1894
1964
  const STARTUP_GRACE_PERIOD_MS = 30_000;
1895
1965
  const IMPOSSIBLE_DRAWDOWN = -0.5;
1966
+ const REQUIRED_RED_STREAK = 3;
1967
+ const MIN_RED_PERSISTENCE_MS = 25_000;
1896
1968
  if (metrics.drawdownZone === 'RED' && this.agentMode === 'ACTIVE') {
1969
+ if (this.redZoneStreak === 0)
1970
+ this.redZoneSince = Date.now();
1971
+ this.redZoneStreak++;
1972
+ const redForMs = Date.now() - this.redZoneSince;
1973
+ const survivedPositionsRefresh = this.positionsUpdatedAt > this.redZoneSince;
1897
1974
  if (uptimeMs < STARTUP_GRACE_PERIOD_MS) {
1898
1975
  logger.warn(TAG, `RED zone drawdown detected (${metrics.drawdownZoneMessage}) but within startup grace period (${Math.round(uptimeMs / 1000)}s) — skipping auto-flatten`);
1899
1976
  }
@@ -1903,6 +1980,11 @@ export class GatewayProvider {
1903
1980
  else if (metrics.utilization.dailyDrawdown < IMPOSSIBLE_DRAWDOWN) {
1904
1981
  logger.error(TAG, `RED zone drawdown ${(metrics.utilization.dailyDrawdown * 100).toFixed(1)}% exceeds sanity floor ${IMPOSSIBLE_DRAWDOWN * 100}% — refusing auto-flatten, likely equity calculation error (balance.total=${this.balance.total}, sessionStartNav=${this.sessionStartNav}, positions=${this.positions.length})`);
1905
1982
  }
1983
+ else if (this.redZoneStreak < REQUIRED_RED_STREAK ||
1984
+ redForMs < MIN_RED_PERSISTENCE_MS ||
1985
+ !survivedPositionsRefresh) {
1986
+ logger.warn(TAG, `RED zone drawdown detected (${metrics.drawdownZoneMessage}) — deferring auto-flatten until the verdict persists (streak ${this.redZoneStreak}/${REQUIRED_RED_STREAK}, ${Math.round(redForMs / 1000)}s/${MIN_RED_PERSISTENCE_MS / 1000}s, survivedPositionsRefresh=${survivedPositionsRefresh}). A fresh entry's notional reads as phantom drawdown until the position snapshot catches up (issue #213); a real drawdown will persist and flatten on a later evaluation.`);
1987
+ }
1906
1988
  else {
1907
1989
  logger.warn(TAG, `RED zone drawdown detected (${metrics.drawdownZoneMessage}) — auto-flattening all positions`);
1908
1990
  // Route through executeEmergency (NOT executeFlatten directly) so the
@@ -1917,6 +1999,11 @@ export class GatewayProvider {
1917
1999
  });
1918
2000
  }
1919
2001
  }
2002
+ else {
2003
+ // Any non-RED evaluation clears the streak — the race artifact resolves
2004
+ // the moment the positions snapshot includes the new position.
2005
+ this.redZoneStreak = 0;
2006
+ }
1920
2007
  // Include position snapshots + balance in risk_update so the webapp can
1921
2008
  // update KPI/PnL without depending on market_data ticker events.
1922
2009
  const equity = this.computeEquity();
@@ -2097,27 +2184,39 @@ export class GatewayProvider {
2097
2184
  this.inFlightReports.push(report);
2098
2185
  }
2099
2186
  /**
2100
- * Seed lastTradeTs from Intelligence API on startup. One-shot, best-effort.
2101
- * Prevents "Last Trade: Never" after every restart when the agent is flat.
2187
+ * Seed lastTradeTs from the webapp's exchange-keyed fills ledger on startup.
2188
+ * One-shot, best-effort. Prevents "Last Trade: Never" after every restart
2189
+ * when the agent is flat.
2190
+ *
2191
+ * ★ Issue #194: the previous seed read intel's `trade_results` (closed
2192
+ * round-trips only, single-symbol), which showed "last trade 365h ago"
2193
+ * while the fills ledger had fills from the previous evening. The ledger
2194
+ * (`GET /api/internal/trades`) is the source of truth for fills — seed from
2195
+ * it, filtered to the current trading book. On any failure leave null:
2196
+ * truthful-unknown beats confidently-wrong.
2102
2197
  */
2103
2198
  async seedLastTradeTs() {
2104
2199
  if (this.lastTradeTs)
2105
2200
  return; // Already set (e.g. from WS event during init)
2106
- const url = this.config.intelligenceUrl;
2107
2201
  const token = this.config.connectionToken;
2108
- if (!url || !token)
2202
+ if (!token)
2109
2203
  return;
2204
+ // www is load-bearing: reefclaw.com 307-redirects and Node fetch strips
2205
+ // the Authorization header on cross-origin redirect (same as bridge.ts).
2206
+ const base = (process.env.REEFCLAW_API_URL || 'https://www.reefclaw.com').replace(/\/$/, '');
2207
+ const mode = this.tradingMode === 'PAPER' ? 'paper' : 'live';
2110
2208
  try {
2111
- const res = await fetch(`${url}/api/analytics/${this.config.symbol.replace('/', '')}/trades?limit=1`, {
2209
+ const res = await fetch(`${base}/api/internal/trades?limit=1&mode=${mode}`, {
2112
2210
  headers: { Authorization: `Bearer ${token}` },
2113
2211
  signal: AbortSignal.timeout(5_000),
2114
2212
  });
2115
2213
  if (!res.ok)
2116
2214
  return;
2117
- const trades = await res.json();
2118
- if (trades.length > 0 && trades[0].timestamp) {
2119
- this.lastTradeTs = trades[0].timestamp;
2120
- logger.info(TAG, `Seeded lastTradeTs from Intelligence: ${this.lastTradeTs}`);
2215
+ const body = (await res.json());
2216
+ const ts = body.trades?.[0]?.fillTimestamp;
2217
+ if (ts) {
2218
+ this.lastTradeTs = ts;
2219
+ logger.info(TAG, `Seeded lastTradeTs from trades ledger (${mode}): ${this.lastTradeTs}`);
2121
2220
  this.emitAgentState(); // Re-emit with updated timestamp
2122
2221
  }
2123
2222
  }
@@ -1,7 +1,6 @@
1
1
  // Interactive setup helper for the ReefClaw skill.
2
2
  // Validates token format, tests relay connection, saves to OpenClaw config.
3
3
  import WebSocket from 'ws';
4
- import { execSync } from 'node:child_process';
5
4
  import { writeOpenClawConfig } from './config.js';
6
5
  import { logger } from './logger.js';
7
6
  const TAG = 'setup';
@@ -149,8 +148,12 @@ export async function runSetup(tokenOrBundle, userId, relayUrl) {
149
148
  }
150
149
  // Save to config
151
150
  writeOpenClawConfig(token, resolvedUserId, resolvedRelayUrl);
152
- // Create heartbeat cron job if none exists
153
- ensureHeartbeatCron();
151
+ // Heartbeat cron is ensured by the bridge at first gateway connect (via
152
+ // cron.list/cron.add RPC — see gateway/heartbeat-cron.ts). Setup can't do it:
153
+ // the gateway isn't running yet at this point, which is also why the old
154
+ // setup-time shell-out to the openclaw CLI silently no-op'd on fresh
155
+ // installs (the CLI's cron commands are gateway RPC clients too).
156
+ console.log(' Heartbeat cron: created automatically when the agent first connects (every 15m)');
154
157
  console.log('');
155
158
  console.log(' Setup complete! Token saved to ~/.openclaw/config.json');
156
159
  console.log('');
@@ -161,54 +164,6 @@ export async function runSetup(tokenOrBundle, userId, relayUrl) {
161
164
  console.log(' 4. Your agent checks the market every 15 minutes (adjustable via chat)');
162
165
  console.log('');
163
166
  }
164
- // ---- Heartbeat cron ----
165
- /**
166
- * Ensures a heartbeat cron job exists for ReefClaw.
167
- * Checks `openclaw cron list` for an existing job with "reefclaw" in the name.
168
- * If none found, creates one with 15m default interval.
169
- * Non-fatal — if openclaw CLI isn't available or cron fails, setup still succeeds.
170
- */
171
- function ensureHeartbeatCron() {
172
- try {
173
- // Check if a reefclaw cron already exists
174
- const listOutput = execSync('openclaw cron list --json 2>/dev/null', {
175
- encoding: 'utf-8',
176
- timeout: 15_000,
177
- });
178
- // Look for any job with "reefclaw" in the name
179
- try {
180
- const jobs = JSON.parse(listOutput);
181
- const existing = (Array.isArray(jobs) ? jobs : []).find((j) => j.name?.toLowerCase().includes('reefclaw'));
182
- if (existing) {
183
- console.log(` Heartbeat cron: already exists (${existing.name})`);
184
- return;
185
- }
186
- }
187
- catch {
188
- // JSON parse failed — maybe not JSON output, check raw text
189
- if (listOutput.toLowerCase().includes('reefclaw')) {
190
- console.log(' Heartbeat cron: already exists');
191
- return;
192
- }
193
- }
194
- // No existing job — create one
195
- console.log(' Creating heartbeat cron (every 15m)...');
196
- execSync('openclaw cron add --name reefclaw-heartbeat --every 15m --session isolated ' +
197
- "--message 'Heartbeat. Run SESSION START mandatory checks, then the full Decision Loop (Steps 0-9) from SKILL.md. Check tradingMode from fetch_balance — it is your source of truth for paper vs live. Use tools in parallel where possible.' 2>&1", { encoding: 'utf-8', timeout: 15_000 });
198
- console.log(' Heartbeat cron: created (every 15 minutes)');
199
- console.log(' Tip: Ask your agent "change heartbeat to 10 minutes" to adjust');
200
- }
201
- catch (err) {
202
- // Non-fatal — user can create cron manually or agent can create it on first session
203
- const msg = err instanceof Error ? err.message : String(err);
204
- if (msg.includes('not found') || msg.includes('ENOENT')) {
205
- console.log(' Heartbeat cron: skipped (openclaw CLI not in PATH)');
206
- }
207
- else {
208
- console.log(` Heartbeat cron: skipped (${msg.split('\n')[0]})`);
209
- }
210
- }
211
- }
212
167
  function printSetupInstructions() {
213
168
  console.log(' To connect your OpenClaw agent to ReefClaw:');
214
169
  console.log('');
@@ -153,11 +153,20 @@ register('price_sweep', (ctx, params, _dir, condCtx) => {
153
153
  sweepLevel = nearestHigh;
154
154
  }
155
155
  }
156
- // Store in shared context for entry/stop rules and direction
156
+ // Store in shared context for entry/stop rules and direction.
157
+ // sweptDirection is written non-destructively, like the funding_extreme /
158
+ // cvd_divergence setters: only when a sweep actually resolved a side. An
159
+ // unconditional write (including null on a no-sweep bar) clobbered a
160
+ // direction set by an earlier condition in the same evaluation, so a
161
+ // from_sweep/from_funding strategy listing price_sweep AFTER another
162
+ // setter could never fire.
157
163
  condCtx.sweepLevel = sweepLevel;
158
164
  condCtx.recentLow = recentLow;
159
165
  condCtx.recentHigh = recentHigh;
160
- condCtx.sweptDirection = sweptLow ? 'LONG' : sweptHigh ? 'SHORT' : null;
166
+ if (sweptLow)
167
+ condCtx.sweptDirection = 'LONG';
168
+ else if (sweptHigh)
169
+ condCtx.sweptDirection = 'SHORT';
161
170
  const met = sweptLow || sweptHigh;
162
171
  return {
163
172
  met,