dexterm-cli 0.1.5 → 0.1.7

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 (379) hide show
  1. package/dist/packages/agents/src/index.js +2 -0
  2. package/dist/packages/agents/src/schema.js +22 -1
  3. package/dist/packages/agents/src/spectator.js +54 -0
  4. package/dist/packages/agents/src/story.js +532 -0
  5. package/dist/packages/agents/src/tiers.js +28 -2
  6. package/dist/packages/agents/src/tools.js +31 -2
  7. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  8. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  9. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  10. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  11. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  12. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  13. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  14. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  15. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  16. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  17. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  18. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  19. package/dist/packages/autotrade/src/account.js +74 -0
  20. package/dist/packages/autotrade/src/backtest.js +357 -0
  21. package/dist/packages/autotrade/src/basis-text.js +32 -0
  22. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  23. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  24. package/dist/packages/autotrade/src/chat/config.js +93 -0
  25. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  26. package/dist/packages/autotrade/src/chat/control.js +103 -0
  27. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  28. package/dist/packages/autotrade/src/chat/format.js +71 -0
  29. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  30. package/dist/packages/autotrade/src/chat/index.js +18 -0
  31. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  32. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  33. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  34. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  35. package/dist/packages/autotrade/src/chat/service.js +248 -0
  36. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  37. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  38. package/dist/packages/autotrade/src/engine.js +387 -0
  39. package/dist/packages/autotrade/src/execution-port.js +97 -0
  40. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  41. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  42. package/dist/packages/autotrade/src/fleet.js +639 -0
  43. package/dist/packages/autotrade/src/index.js +53 -0
  44. package/dist/packages/autotrade/src/ledger.js +108 -0
  45. package/dist/packages/autotrade/src/live-source.js +182 -0
  46. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  47. package/dist/packages/autotrade/src/monitor.js +190 -0
  48. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  49. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  50. package/dist/packages/autotrade/src/replay.js +198 -0
  51. package/dist/packages/autotrade/src/runner-config.js +99 -0
  52. package/dist/packages/autotrade/src/runner.js +872 -0
  53. package/dist/packages/autotrade/src/safety.js +243 -0
  54. package/dist/packages/autotrade/src/session.js +269 -0
  55. package/dist/packages/autotrade/src/sessions.js +503 -0
  56. package/dist/packages/autotrade/src/store.js +250 -0
  57. package/dist/packages/autotrade/src/strategy.js +256 -0
  58. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  59. package/dist/packages/autotrade/src/yolo.js +140 -0
  60. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  61. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +1 -1
  62. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +15 -8
  63. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  64. package/dist/packages/cli/src/agent-cmd.js +6 -4
  65. package/dist/packages/cli/src/agent-spectator.js +17 -1
  66. package/dist/packages/cli/src/ai-cmd.js +412 -0
  67. package/dist/packages/cli/src/analyst/backend.js +105 -15
  68. package/dist/packages/cli/src/analyst/codex.js +14 -9
  69. package/dist/packages/cli/src/analyst/larp-gauge.js +60 -0
  70. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  71. package/dist/packages/cli/src/analyst/session.js +17 -3
  72. package/dist/packages/cli/src/app-help-gen.js +51 -15
  73. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  74. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  75. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  76. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  77. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  78. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  79. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  80. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  81. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  82. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  83. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  84. package/dist/packages/cli/src/backtest-cmd.js +13 -2
  85. package/dist/packages/cli/src/display.js +208 -23
  86. package/dist/packages/cli/src/doctor.js +62 -3
  87. package/dist/packages/cli/src/export.js +10 -2
  88. package/dist/packages/cli/src/index.js +1 -1
  89. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  90. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  91. package/dist/packages/cli/src/live/dev-history.js +31 -15
  92. package/dist/packages/cli/src/live/index.js +28 -90
  93. package/dist/packages/cli/src/live/screens/newpairs-live.js +1 -1
  94. package/dist/packages/cli/src/live/screens/token-live.js +48 -15
  95. package/dist/packages/cli/src/live/screens/watchlist-live.js +1 -1
  96. package/dist/packages/cli/src/live/trade.js +30 -453
  97. package/dist/packages/cli/src/live/watchlist.js +1 -1
  98. package/dist/packages/cli/src/main.js +290 -199
  99. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  100. package/dist/packages/cli/src/mcp-follow.js +103 -6
  101. package/dist/packages/cli/src/plan-cmd.js +15 -5
  102. package/dist/packages/cli/src/pnl-app.js +31 -21
  103. package/dist/packages/cli/src/pnl-card.js +158 -21
  104. package/dist/packages/cli/src/popup.js +38 -6
  105. package/dist/packages/cli/src/record-cmd.js +141 -20
  106. package/dist/packages/cli/src/scan/ui.js +7 -1
  107. package/dist/packages/cli/src/screens/agent-hud.js +773 -0
  108. package/dist/packages/cli/src/screens/analyst-session.js +11 -4
  109. package/dist/packages/cli/src/screens/analyst.js +3 -2
  110. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  111. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  112. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  113. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  114. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  115. package/dist/packages/cli/src/screens/brain.js +634 -0
  116. package/dist/packages/cli/src/screens/newpairs.js +1 -1
  117. package/dist/packages/cli/src/screens/overlay.js +5 -2
  118. package/dist/packages/cli/src/screens/plans.js +5 -17
  119. package/dist/packages/cli/src/screens/pnl-chip.js +144 -0
  120. package/dist/packages/cli/src/screens/positions.js +6 -28
  121. package/dist/packages/cli/src/screens/screener.js +1 -0
  122. package/dist/packages/cli/src/screens/ticket.js +38 -205
  123. package/dist/packages/cli/src/screens/token.js +18 -9
  124. package/dist/packages/cli/src/screens/trending.js +1 -1
  125. package/dist/packages/cli/src/state.js +88 -18
  126. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  127. package/dist/packages/cli/src/trade.js +3 -128
  128. package/dist/packages/cli/src/version.js +1 -1
  129. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  130. package/dist/packages/cli/src/wallet-command.js +8 -11
  131. package/dist/packages/cli/src/widgets.js +0 -12
  132. package/dist/packages/custom/src/config.js +20 -2
  133. package/dist/packages/custom/src/schema.js +41 -10
  134. package/dist/packages/custom/src/workspace.js +3 -3
  135. package/dist/packages/data/src/basis.js +191 -0
  136. package/dist/packages/data/src/detail.js +53 -2
  137. package/dist/packages/data/src/index.js +2 -14
  138. package/dist/packages/data/src/larp/score.js +220 -0
  139. package/dist/packages/data/src/larp/x-account.js +316 -0
  140. package/dist/packages/data/src/store.js +14 -5
  141. package/dist/packages/data/src/trade/agent/paper-desk.js +300 -23
  142. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  143. package/dist/packages/data/src/trade/bridge.js +4 -102
  144. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  145. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  146. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  147. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  148. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  149. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  150. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  151. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  152. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  153. package/dist/packages/data/src/trade/plans/engine.js +7 -0
  154. package/dist/packages/data/src/trade/plans/index.js +0 -1
  155. package/dist/packages/data/src/trade/plans/mandate.js +28 -15
  156. package/dist/packages/data/src/trade/plans/service.js +4 -28
  157. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  158. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  159. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  160. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  161. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  162. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  163. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  164. package/dist/packages/mcp/assets/kb/app-help/dev-history.md +1 -1
  165. package/dist/packages/mcp/assets/kb/app-help/larp-check.md +5 -1
  166. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  167. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  168. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  169. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  170. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  171. package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +1 -1
  172. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  173. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  174. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  175. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  176. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  177. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  178. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +6 -0
  179. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  180. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  181. package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +2 -1
  182. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  183. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  184. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +7 -3
  186. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  188. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  189. package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +2 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  194. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  195. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  198. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  200. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  201. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  219. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  220. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +32 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  223. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  225. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +9 -7
  226. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  227. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  228. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  229. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  230. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  233. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  234. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  235. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +1 -1
  236. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  237. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +1 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +60 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +0 -2
  240. package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +4 -1
  241. package/dist/packages/mcp/assets/kb/docs/docs-larp.md +1 -1
  242. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  243. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  244. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  246. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  248. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  250. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  264. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +4 -13
  266. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  267. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +40 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  269. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  271. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  273. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  274. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  275. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +1 -1
  277. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +3 -1
  278. package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +2 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +10 -7
  281. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  282. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +1 -1
  284. package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +2 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-agent-hud-spectator-v2.md +58 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +46 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +1 -1
  288. package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +1 -1
  289. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  290. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  291. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  292. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  293. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  295. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  297. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  298. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  299. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  302. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  309. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  310. package/dist/packages/mcp/src/app-help.js +1 -1
  311. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  312. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  313. package/dist/packages/mcp/src/engine/config.js +159 -0
  314. package/dist/packages/mcp/src/engine/index.js +10 -0
  315. package/dist/packages/mcp/src/engine/loop.js +101 -0
  316. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  317. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  318. package/dist/packages/mcp/src/engine/probe.js +110 -0
  319. package/dist/packages/mcp/src/engine/reason.js +35 -0
  320. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  321. package/dist/packages/mcp/src/index.js +7 -1
  322. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  323. package/dist/packages/mcp/src/paper-agent.js +5 -2
  324. package/dist/packages/mcp/src/server.js +285 -36
  325. package/dist/packages/mcp/src/spectator-sink.js +85 -0
  326. package/dist/packages/packs/src/backtest.js +3 -1
  327. package/dist/packages/packs/src/index.js +1 -0
  328. package/dist/packages/packs/src/larp-score-backtest.js +119 -0
  329. package/dist/packages/popup/src/pages/chart-lib.js +169 -35
  330. package/dist/packages/popup/src/pages/chart.css +18 -0
  331. package/dist/packages/popup/src/pages/chart.html +6 -1
  332. package/dist/packages/popup/src/pages/chart.js +136 -10
  333. package/dist/packages/popup/src/pages/spectator.css +45 -0
  334. package/dist/packages/popup/src/pages/spectator.html +1 -0
  335. package/dist/packages/popup/src/pages/spectator.js +69 -0
  336. package/dist/packages/popup/src/protocol.js +4 -0
  337. package/dist/packages/popup/src/server.js +9 -1
  338. package/dist/packages/recorder/src/dev-history.js +7 -3
  339. package/dist/packages/recorder/src/index.js +2 -0
  340. package/dist/packages/recorder/src/recorder.js +63 -6
  341. package/dist/packages/recorder/src/retention.js +100 -0
  342. package/dist/packages/recorder/src/schema.js +34 -1
  343. package/dist/packages/recorder/src/ticks.js +237 -0
  344. package/dist/packages/tui/src/canvas.js +7 -0
  345. package/dist/packages/tui/src/fmt.js +53 -0
  346. package/dist/packages/tui/src/index.js +1 -0
  347. package/dist/packages/tui/src/keymap.js +25 -1
  348. package/dist/packages/tui/src/markers.js +219 -0
  349. package/dist/packages/tui/src/widgets.js +158 -6
  350. package/package.json +2 -1
  351. package/dist/packages/data/src/trade/caps.js +0 -323
  352. package/dist/packages/data/src/trade/executor.js +0 -933
  353. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  354. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  355. package/dist/packages/data/src/trade/signer/client.js +0 -197
  356. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  357. package/dist/packages/data/src/trade/signer/host.js +0 -97
  358. package/dist/packages/data/src/trade/signer/main.js +0 -121
  359. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  360. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  361. package/dist/packages/data/src/trade/signer/service.js +0 -400
  362. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  363. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  364. package/dist/packages/data/src/trade/swap-build.js +0 -108
  365. package/dist/packages/data/src/trade/token-delta.js +0 -201
  366. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  367. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  368. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  369. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  370. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  371. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  372. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  373. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  374. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  375. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  376. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  377. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  378. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  379. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -12,8 +12,11 @@
12
12
  const $ = (id) => document.getElementById(id);
13
13
  const titleEl = $('title'), legendEl = $('legend'), liveEl = $('live'), bannerEl = $('banner'), hintEl = $('hint');
14
14
  const posEl = $('pos'), posTitleEl = $('pos-title'), posCapEl = $('pos-cap'), posCellsEl = $('pos-cells');
15
+ const chipEl = $('pnlchip');
15
16
  const imgSlot = $('coin-img-slot');
16
17
  const chartEl = $('chart'), drawCv = $('draw'), dctx = drawCv.getContext('2d');
18
+ // task-1483: the BUY / SELL marker hover card (built with textContent only)
19
+ const tipEl = document.createElement('div'); tipEl.className = 'mtip'; tipEl.hidden = true; chartEl.parentNode.appendChild(tipEl);
17
20
  const STALE_MS = 20_000;
18
21
  const DEFAULT_IND = ['volume', 'tape', 'ema9', 'ema21'];
19
22
  /** Exposed for the headless harness (measure/popup-v2-1325): first paint + setData → repaint latency. */
@@ -25,6 +28,8 @@
25
28
  draw: null, tPending: null, hover: null, drawings: [], lastDataAt: 0, firstPainted: false, fitNext: true,
26
29
  banners: { fallback: null, data: null },
27
30
  display: C.displayOf(null), // task-1366: price | mcap (from the VM's priceMode + supply)
31
+ marks: null, // task-1483: { set, on } — the BUY / SELL markers for the bars on screen (chart-lib chartMarkers)
32
+ mouse: null, // task-1483: crosshair point for the marker hover card
28
33
  };
29
34
 
30
35
  const banner = () => {
@@ -57,7 +62,7 @@
57
62
  if (type === 2) return String(d.getUTCDate());
58
63
  return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
59
64
  }
60
- const volFmt = (v) => C.fmtUsd(v);
65
+ const volFmt = (v) => C.fmtCompactUsd(v); // task-1472: volume stays USD, compact k/M
61
66
 
62
67
  // ---------------------------------------------------------------- chart + series
63
68
  const chart = L.createChart(chartEl, {
@@ -80,14 +85,15 @@
80
85
  handleScroll: { mouseWheel: true, pressedMouseMove: true, horzTouchDrag: true, vertTouchDrag: false },
81
86
  handleScale: { axisPressedMouseMove: { time: true, price: true }, axisDoubleClickReset: true, mouseWheel: true, pinch: true },
82
87
  });
83
- // task-1366: the axis formatter follows the terminal's price mode (mcap = price × the current supply, K/M/B)
88
+ // task-1366/1472: the axis formatter follows the terminal's chart axis (mcap = price × the current supply, compact k/M/B)
84
89
  const pricePF = (p) => ({ type: 'custom', formatter: C.axisFormatter(S.display), minMove: C.priceFormatFor(p).minMove });
85
90
  // Keep the position levels (entry / stop / target) and the live price in view: autoscale widens to include them
86
91
  // when they are within 3× of the last close (a far-away level would squash the candles; its row cell still shows it).
87
92
  function levels() {
88
93
  const vm = S.vm || {}, pos = vm.position || null, out = [];
89
94
  const last = S.bars.length ? S.bars[S.bars.length - 1].close : null;
90
- for (const v of [pos && pos.avgEntryUsd, pos && pos.stopUsd, pos && pos.targetUsd, vm.feed === 'LIVE' ? vm.livePrice : null]) {
95
+ const mk = S.marks && S.marks.on ? S.marks.set.markers.map((m) => m.priceUsd) : []; // task-1483: keep the fills in view
96
+ for (const v of [pos && pos.avgEntryUsd, pos && pos.stopUsd, pos && pos.targetUsd, vm.feed === 'LIVE' ? vm.livePrice : null, ...mk]) {
91
97
  if (typeof v === 'number' && v > 0 && last && v / last < 3 && last / v < 3) out.push(v);
92
98
  }
93
99
  return out;
@@ -159,13 +165,18 @@
159
165
  DT.onTheme(applyChartTheme);
160
166
 
161
167
  // ---------------------------------------------------------------- toolbar (buttons built with textContent only)
162
- const btns = { tf: new Map(), styles: new Map(), scales: new Map(), inds: new Map(), draws: new Map() };
168
+ const btns = { tf: new Map(), axis: new Map(), marks: new Map(), styles: new Map(), scales: new Map(), inds: new Map(), draws: new Map() };
163
169
  function mkBtn(group, id, label, title, onClick) {
164
170
  const b = document.createElement('button'); b.type = 'button'; b.className = 'tb'; b.textContent = label; b.title = title;
165
171
  b.addEventListener('click', (e) => { e.preventDefault(); onClick(); });
166
172
  $(group).appendChild(b); btns[group].set(id, b); return b;
167
173
  }
168
174
  C.INTERVALS.forEach((iv, i) => mkBtn('tf', iv, iv, `timeframe ${iv} (${i + 1})`, () => requestInterval(iv)));
175
+ // task-1472: MCAP | PRICE asks the terminal to switch (and save) the chart axis; the next VM re-labels the page
176
+ mkBtn('axis', 'mcap', 'MCAP', 'Y axis: market cap (A / y toggles; saved by the terminal)', () => setAxis('mcap'));
177
+ mkBtn('axis', 'price', 'PRICE', 'Y axis: USD price (A / y toggles; saved by the terminal)', () => setAxis('price'));
178
+ // task-1483: MARKERS on/off asks the terminal to switch (and save) display.chartMarkers; the next VM carries it
179
+ mkBtn('marks', 'on', 'MARKERS', 'BUY / SELL markers of your paper + live fills (k toggles; saved by the terminal)', () => setMarkers(!(S.vm && S.vm.marks && S.vm.marks.on)));
169
180
  mkBtn('styles', 'candles', 'candles', 'candlesticks (c cycles)', () => setStyle('candles', true));
170
181
  mkBtn('styles', 'line', 'line', 'line (c cycles)', () => setStyle('line', true));
171
182
  mkBtn('styles', 'area', 'area', 'area (c cycles)', () => setStyle('area', true));
@@ -183,6 +194,10 @@
183
194
  mkBtn('draws', 'x', '✕ clear', 'clear drawings (x; backspace = undo last)', () => clearDrawings());
184
195
  function syncToolbar() {
185
196
  for (const [iv, b] of btns.tf) { b.classList.toggle('on', iv === S.interval); b.classList.toggle('pending', iv === S.pendingInterval && iv !== S.interval); }
197
+ const ax = S.vm && S.vm.priceMode === 'mcap' ? 'mcap' : 'price';
198
+ for (const [k, b] of btns.axis) b.classList.toggle('on', k === ax);
199
+ const mOn = !!(S.vm && S.vm.marks && S.vm.marks.on);
200
+ for (const [, b] of btns.marks) { b.classList.toggle('on', mOn); b.hidden = !(S.vm && S.vm.marks); }
186
201
  for (const [k, b] of btns.styles) b.classList.toggle('on', k === S.style);
187
202
  for (const [k, b] of btns.scales) b.classList.toggle('on', k === S.scale);
188
203
  for (const [k, b] of btns.inds) b.classList.toggle('on', S.ind.has(k));
@@ -196,6 +211,16 @@
196
211
  S.pendingInterval = iv; S.reqAt = performance.now(); syncToolbar();
197
212
  DT.send({ type: 'event', name: 'chart.interval', payload: { interval: iv } });
198
213
  }
214
+ /** task-1472: ask the terminal for a Y axis; nothing changes here until its next VM carries the new mode. */
215
+ function setAxis(axis) {
216
+ if (axis !== 'mcap' && axis !== 'price') return;
217
+ DT.send({ type: 'event', name: 'chart.axis', payload: { axis } });
218
+ }
219
+ /** task-1483: ask the terminal to show / hide the markers; nothing changes here until its next VM says so. */
220
+ function setMarkers(on) {
221
+ if (typeof on !== 'boolean') return;
222
+ DT.send({ type: 'event', name: 'chart.markers', payload: { on } });
223
+ }
199
224
  function setStyle(st, user) {
200
225
  if (!C.STYLES.includes(st) || st === S.style) return;
201
226
  S.style = st;
@@ -260,9 +285,82 @@
260
285
  const times = new Set(S.bars.map((b) => b.time));
261
286
  const list = [];
262
287
  if (S.ind.has('tape')) list.push(...C.tapeMarkers(vm.trades, periodMs(), times, { up: DT.rgba(T.up, 0.85), down: DT.rgba(T.down, 0.85) }));
263
- if (vm.position && vm.position.fills) list.push(...C.fillMarkers(vm.position.fills, periodMs(), times, { buy: T.accent, sell: T.warn }));
264
288
  list.sort((a, b) => a.time - b.time);
265
289
  markers[S.style].setMarkers(list);
290
+ computeMarks();
291
+ }
292
+
293
+ // ---------------------------------------------------------------- task-1483: BUY / SELL markers of the ledgers' fills
294
+ // Bucketed to THIS page's timeframe by the bars' real times (re-bucketed on every timeframe change), drawn on the
295
+ // overlay canvas so paper (hollow) and live (solid) read apart, re-clustered when zooming out, hover card per marker.
296
+ function computeMarks() {
297
+ const mk = S.vm && S.vm.marks;
298
+ if (!mk) { S.marks = null; return; }
299
+ S.marks = { on: !!mk.on, truncated: mk.truncated | 0, set: C.chartMarkers(mk.fills, periodMs(), S.bars.map((b) => b.t)) };
300
+ }
301
+ const MARK_PX = 6;
302
+ let markHits = [];
303
+ function drawMarks(ts, s, paneH) {
304
+ markHits = [];
305
+ const mk = S.marks;
306
+ if (!mk || !mk.on || !S.bars.length) return;
307
+ const x0 = ts.logicalToCoordinate(0), x1 = ts.logicalToCoordinate(1);
308
+ const spacing = x0 != null && x1 != null ? Math.abs(x1 - x0) : 7;
309
+ // closed round trips: a faint wash entry → exit (green when sold above the entry)
310
+ for (const r of mk.set.trips) {
311
+ const a = C.logicalOf(S.bars, C.chartTime(Math.floor(r.openT / periodMs()) * periodMs()), periodMs() / 1000);
312
+ const b = C.logicalOf(S.bars, C.chartTime(Math.floor(r.closeT / periodMs()) * periodMs()), periodMs() / 1000);
313
+ const xa = ts.logicalToCoordinate(a), xb = ts.logicalToCoordinate(b);
314
+ if (xa == null || xb == null) continue;
315
+ const win = r.entryUsd !== null && r.exitUsd !== null ? r.exitUsd >= r.entryUsd : null;
316
+ dctx.fillStyle = DT.rgba(win === null ? T.muted : win ? T.up : T.down, 0.07);
317
+ dctx.fillRect(Math.min(xa, xb) - spacing / 2, 0, Math.abs(xb - xa) + spacing, paneH);
318
+ }
319
+ const shown = C.clusterMarkers(mk.set.markers, spacing, 14);
320
+ const used = new Map();
321
+ dctx.font = 'bold 10px ui-monospace, Menlo, monospace'; dctx.textAlign = 'center'; dctx.textBaseline = 'middle';
322
+ for (const m of shown) {
323
+ const x = ts.logicalToCoordinate(m.index);
324
+ const bar = S.bars[m.index];
325
+ if (x == null || !bar) continue;
326
+ const buy = m.side === 'buy';
327
+ let y = m.priceUsd !== null ? s.priceToCoordinate(m.priceUsd) : s.priceToCoordinate(buy ? bar.low : bar.high);
328
+ if (y == null) continue;
329
+ if (m.priceUsd === null) y += buy ? 4 : -4; // no recorded price: against the bar, never at a made-up price
330
+ // a second marker on the same bar and side steps away from the bar, never across it
331
+ const k = `${m.index}|${m.side}`; const n = used.get(k) || 0; used.set(k, n + 1);
332
+ if (n) y += (buy ? 1 : -1) * n * (MARK_PX * 1.6 + 12);
333
+ if (y < -20 || y > paneH + 20) continue;
334
+ const g = C.markerShape(m.side, x, y, MARK_PX);
335
+ const col = buy ? T.up : T.down;
336
+ dctx.beginPath(); dctx.moveTo(g.tri[0][0], g.tri[0][1]); dctx.lineTo(g.tri[1][0], g.tri[1][1]); dctx.lineTo(g.tri[2][0], g.tri[2][1]); dctx.closePath();
337
+ // a dark halo first, so a green buy still reads on a green candle (and a red sell on a red one)
338
+ dctx.setLineDash([]); dctx.lineJoin = 'round'; dctx.lineWidth = 4; dctx.strokeStyle = DT.rgba(T.bg, 0.92); dctx.stroke();
339
+ if (m.source === 'live') { dctx.fillStyle = col; dctx.fill(); dctx.lineWidth = 1.2; dctx.strokeStyle = DT.rgba(T.text, 0.95); dctx.stroke(); } // solid + a light edge: never mistaken for a hollow paper mark on a same-colour candle
340
+ else { dctx.fillStyle = DT.rgba(T.bg, 0.85); dctx.fill(); dctx.fillStyle = DT.rgba(col, 0.22); dctx.fill(); dctx.lineWidth = 1.6; dctx.strokeStyle = col; dctx.stroke(); }
341
+ const txt = `${C.markerLetter(m.side)}${m.count > 1 ? '×' + m.count : ''}`;
342
+ dctx.lineWidth = 3; dctx.strokeStyle = DT.rgba(T.bg, 0.92); dctx.strokeText(txt, g.labelX, g.labelY);
343
+ dctx.fillStyle = col; dctx.fillText(txt, g.labelX, g.labelY);
344
+ markHits.push({ m, hit: g.hit, x, y });
345
+ }
346
+ hoverMarks();
347
+ }
348
+ /** The hover card for the marker under the crosshair (time, price, size, source). Text only. */
349
+ function hoverMarks() {
350
+ const p = S.mouse;
351
+ const hit = p ? markHits.find((h) => p.x >= h.hit.x0 && p.x <= h.hit.x1 && p.y >= h.hit.y0 && p.y <= h.hit.y1) : null;
352
+ if (!hit) { if (!tipEl.hidden) tipEl.hidden = true; return; }
353
+ const lines = C.markerTooltip(hit.m, S.display, (ms) => fmtTime(C.chartTime(ms)));
354
+ const key = lines.join('|');
355
+ if (tipEl.dataset.k !== key) {
356
+ tipEl.dataset.k = key;
357
+ while (tipEl.firstChild) tipEl.removeChild(tipEl.firstChild);
358
+ lines.forEach((t, i) => tipEl.appendChild(el('div', i === 0 ? `h ${hit.m.side}` : i === lines.length - 1 ? `src ${hit.m.source}` : null, t)));
359
+ }
360
+ tipEl.hidden = false;
361
+ const w = chartEl.clientWidth;
362
+ const left = hit.x + 14 + 190 > w ? hit.x - 14 - tipEl.offsetWidth : hit.x + 14;
363
+ tipEl.style.left = `${Math.max(4, left)}px`; tipEl.style.top = `${Math.max(4, hit.y - 20)}px`;
266
364
  }
267
365
 
268
366
  function renderOverlays() {
@@ -270,7 +368,8 @@
270
368
  const pos = vm.position || null;
271
369
  if (pos && typeof pos.avgEntryUsd === 'number' && pos.avgEntryUsd > 0) {
272
370
  const pct = typeof pos.unrealizedPct === 'number' ? ` ${C.fmtPct(pos.unrealizedPct, 1)}` : '';
273
- setLine('entry', { price: pos.avgEntryUsd, color: T.warn, lineWidth: 1, lineStyle: L.LineStyle.Solid, axisLabelVisible: true, axisLabelColor: T.warn, axisLabelTextColor: T.bg, title: `ENTRY ${pos.sizeLabel}${pct}` });
371
+ // task-1483: the average entry of the open position is a DASHED line (fills are the triangles)
372
+ setLine('entry', { price: pos.avgEntryUsd, color: pos.kind === 'paper' ? T.warn : T.text, lineWidth: 1, lineStyle: L.LineStyle.LargeDashed, axisLabelVisible: true, axisLabelColor: pos.kind === 'paper' ? T.warn : T.text, axisLabelTextColor: T.bg, title: `AVG ENTRY ${pos.sizeLabel}${pct}` });
274
373
  } else setLine('entry', null);
275
374
  setLine('stop', pos && typeof pos.stopUsd === 'number' && pos.stopUsd > 0 ? { price: pos.stopUsd, color: T.down, lineWidth: 1, lineStyle: L.LineStyle.Dashed, axisLabelVisible: true, title: 'STOP' } : null);
276
375
  setLine('target', pos && typeof pos.targetUsd === 'number' && pos.targetUsd > 0 ? { price: pos.targetUsd, color: T.up, lineWidth: 1, lineStyle: L.LineStyle.Dashed, axisLabelVisible: true, title: 'TARGET' } : null);
@@ -331,8 +430,14 @@
331
430
  r2.appendChild(s);
332
431
  }
333
432
  if (r2.firstChild) legendEl.appendChild(r2);
433
+ // task-1483: which markers are on screen, and what the window left off (never moved onto another bar)
434
+ if (S.marks && S.marks.on) {
435
+ const lg = C.markerLegend(S.marks.set.markers);
436
+ const om = C.omittedNote({ ...S.marks.set, before: S.marks.set.before + S.marks.truncated });
437
+ if (lg || om) { const r3 = el('div', 'row3'); if (lg) r3.appendChild(el('span', S.marks.set.markers.some((m) => m.source === 'paper') ? 'paper' : null, lg)); if (om) r3.appendChild(el('span', null, `${lg ? ' · ' : ''}${om}`)); legendEl.appendChild(r3); }
438
+ }
334
439
  }
335
- chart.subscribeCrosshairMove((p) => { S.hover = p && p.time != null ? p.time : null; renderLegend(S.hover); });
440
+ chart.subscribeCrosshairMove((p) => { S.hover = p && p.time != null ? p.time : null; S.mouse = p && p.point ? { x: p.point.x, y: p.point.y } : null; renderLegend(S.hover); });
336
441
 
337
442
  // ---------------------------------------------------------------- header, LIVE badge, banner, position row
338
443
  function renderHeader() {
@@ -397,6 +502,23 @@
397
502
  }
398
503
  }
399
504
 
505
+ // task-1473 r2: the POSITION PnL chip (formatted terminal-side; textContent only, display only)
506
+ function renderChip() {
507
+ const chip = S.vm && S.vm.chip;
508
+ if (!chipEl) return;
509
+ if (!chip || !Array.isArray(chip.segments) || !chip.segments.length) { chipEl.hidden = true; return; }
510
+ chipEl.hidden = false;
511
+ while (chipEl.firstChild) chipEl.removeChild(chipEl.firstChild);
512
+ const tone = ['up', 'down'].includes(chip.tone) ? chip.tone : '';
513
+ for (const sg of chip.segments.slice(0, 8)) {
514
+ const id = ['head', 'pnl', 'peak', 'px', 'held', 'dd', 'tp'].includes(sg.id) ? sg.id : 'x';
515
+ let cls = `seg ${id}`;
516
+ if (id === 'pnl') cls += ` ${tone}${chip.newPeak ? ' flash' : ''}`;
517
+ if (id === 'peak') cls += ` ${/^peak −/.test(String(sg.text)) ? 'down' : 'up'}`;
518
+ chipEl.appendChild(el('span', cls, String(sg.text).slice(0, 48)));
519
+ }
520
+ }
521
+
400
522
  // ---------------------------------------------------------------- incoming view models
401
523
  function onData(vm) {
402
524
  if (!vm || typeof vm !== 'object') return;
@@ -409,7 +531,7 @@
409
531
  // task-1366: a mode / supply change re-labels the axis in place (the bars are prices either way)
410
532
  const disp = C.displayOf(vm);
411
533
  if (disp.key !== S.display.key) { S.display = disp; applyPriceFormat(); }
412
- if (tail && (!vm.seriesKey || vm.seriesKey !== S.key)) { S.vm = { ...vm, candles: [] }; renderHeader(); renderPosition(); renderOverlays(); return; }
534
+ if (tail && (!vm.seriesKey || vm.seriesKey !== S.key)) { S.vm = { ...vm, candles: [] }; renderHeader(); renderPosition(); renderChip(); renderOverlays(); computeMarks(); syncToolbar(); return; }
413
535
  const intervalChanged = vm.interval !== S.interval;
414
536
  S.interval = vm.interval;
415
537
  const answered = S.pendingInterval === vm.interval ? S.reqAt : null;
@@ -425,7 +547,7 @@
425
547
  if (intervalChanged || S.key !== prevKey) S.fitNext = true;
426
548
  renderFull();
427
549
  }
428
- renderHeader(); renderLegend(S.hover); renderDataBanner(); renderPosition(); syncToolbar(); renderLive();
550
+ renderHeader(); renderLegend(S.hover); renderDataBanner(); renderPosition(); renderChip(); syncToolbar(); renderLive();
429
551
  // rain mood follows direction (gold on a pump, red-shift on a dump), as in the v1 HUD; chg is in percent
430
552
  const dir = vm.chg ? (vm.chg.h24 ?? vm.chg.h1 ?? 0) : 0;
431
553
  if (rain) { rain.setMood(dir > 1 ? 'rainPump' : dir < -1 ? 'rainDump' : 'rain'); rain.setActivity(Math.min(1, 0.4 + Math.abs(dir) / 100)); rain.setSources([String(vm.symbol || ''), String(vm.mint || '').slice(0, 6)]); }
@@ -492,10 +614,12 @@
492
614
  dctx.clearRect(0, 0, w, h);
493
615
  const segs = S.drawings.filter((d) => d.kind === 't').map((d) => [d.a, d.b]);
494
616
  if (S.tPending && mouse) segs.push([S.tPending, mouse]);
495
- if (!segs.length || !S.bars.length) return;
617
+ if (!S.bars.length) return;
496
618
  const ts = chart.timeScale(), s = active(), pS = periodMs() / 1000;
497
619
  const paneH = chart.panes()[0] ? chart.panes()[0].getHeight() : h;
498
620
  dctx.save(); dctx.beginPath(); dctx.rect(0, 0, ts.width(), paneH); dctx.clip();
621
+ drawMarks(ts, s, paneH); // task-1483
622
+ if (!segs.length) { dctx.restore(); return; }
499
623
  dctx.strokeStyle = T.accent; dctx.fillStyle = T.accent; dctx.lineWidth = 1.5;
500
624
  for (const [a, b] of segs) {
501
625
  const xa = ts.logicalToCoordinate(C.logicalOf(S.bars, a.time, pS)), xb = ts.logicalToCoordinate(C.logicalOf(S.bars, b.time, pS));
@@ -514,6 +638,7 @@
514
638
  const n = parseInt(k, 10);
515
639
  if (n >= 1 && n <= 6) { requestInterval(C.INTERVALS[n - 1]); return; }
516
640
  switch (k) {
641
+ case 'A': case 'y': setAxis(S.vm && S.vm.priceMode === 'mcap' ? 'price' : 'mcap'); break; // task-1472
517
642
  case 'c': setStyle(C.STYLES[(C.STYLES.indexOf(S.style) + 1) % C.STYLES.length], true); break;
518
643
  case 'a': setScale('auto', true); break;
519
644
  case 'l': setScale(S.scale === 'log' ? 'auto' : 'log', true); break;
@@ -523,6 +648,7 @@
523
648
  case 'm': toggleInd('volma'); break;
524
649
  case 'v': toggleInd('volume'); break;
525
650
  case 'b': toggleInd('tape'); break;
651
+ case 'k': setMarkers(!(S.vm && S.vm.marks && S.vm.marks.on)); break; // task-1483
526
652
  case 'h': armDraw('h'); break;
527
653
  case 't': armDraw('t'); break;
528
654
  case 'x': clearDrawings(); break;
@@ -43,3 +43,48 @@
43
43
  .spec-act-age { color: var(--warn); }
44
44
 
45
45
  .spec-foot-redact { color: var(--up); font-weight: 600; }
46
+
47
+ /* task-1466 spectator v2: the agent story (phase rail + heartbeat, thought ribbon, call timeline, vetting + PAPER cards) */
48
+ .spec-story { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; font: 12px ui-monospace, Menlo, monospace; }
49
+ .spec-rail { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
50
+ .spec-heart { width: 10px; height: 10px; border-radius: 50%; background: var(--down); margin-right: 6px; box-shadow: 0 0 8px var(--down); animation: spec-beat 1s infinite ease-out; }
51
+ .spec-heart.still { animation: none; background: var(--warn); box-shadow: none; }
52
+ .spec-heart.dead { animation: none; background: var(--dim); box-shadow: none; }
53
+ @keyframes spec-beat { 0% { transform: scale(1.35); opacity: 1; } 30% { transform: scale(0.9); opacity: 0.7; } 100% { transform: scale(1); opacity: 0.85; } }
54
+ .spec-bpm { color: var(--muted); margin-right: 8px; min-width: 44px; }
55
+ .spec-phase { padding: 2px 8px; border-radius: 4px; color: var(--dim); border: 1px solid transparent; letter-spacing: 0.05em; }
56
+ .spec-phase.done { color: var(--muted); }
57
+ .spec-phase.on { color: #000; background: var(--accent); font-weight: 700; box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 60%, transparent); }
58
+ .spec-phase.on.p-vetting { background: var(--borderFocus); box-shadow: 0 0 12px color-mix(in srgb, var(--borderFocus) 60%, transparent); }
59
+ .spec-phase.on.p-sizing { background: var(--warn); }
60
+ .spec-phase.on.p-executing { background: var(--up); }
61
+ .spec-phase.on.p-holding { background: var(--pump); }
62
+ .spec-phase.on.p-exiting { background: var(--ai, var(--accent)); }
63
+ .spec-sep { color: var(--border); }
64
+ .spec-paper { margin-left: auto; padding: 1px 6px; border-radius: 4px; background: var(--warn); color: #000; font-weight: 700; }
65
+ .spec-ctlstate { padding: 1px 6px; border-radius: 4px; font-weight: 700; color: #000; }
66
+ .spec-ctlstate.paused { background: var(--warn); }
67
+ .spec-ctlstate.takeover { background: var(--accent); }
68
+ .spec-ctlstate.killed { background: var(--down); color: #fff; }
69
+ .spec-ribbon { padding: 6px 8px; border-left: 3px solid var(--ai, var(--accent)); background: color-mix(in srgb, var(--panel) 85%, transparent); color: var(--text); min-height: 1.4em; }
70
+ .spec-ribbon.ours { border-left-color: var(--border); color: var(--muted); font-style: italic; }
71
+ .spec-ribbon-who { color: var(--ai, var(--accent)); margin-right: 6px; font-style: normal; }
72
+ .spec-timeline { display: flex; align-items: flex-start; gap: 0; overflow: hidden; justify-content: flex-end; }
73
+ .spec-node { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 0 2px; }
74
+ .spec-node-l { color: var(--text); white-space: nowrap; }
75
+ .spec-node.paper .spec-node-l { color: var(--warn); font-weight: 700; }
76
+ .spec-chip { font-size: 10px; padding: 0 5px; border-radius: 3px; background: color-mix(in srgb, var(--up) 18%, transparent); color: var(--up); }
77
+ .spec-chip.slow { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
78
+ .spec-chip.fail, .spec-chip.refused { background: color-mix(in srgb, var(--down) 22%, transparent); color: var(--down); }
79
+ .spec-link { color: var(--border); align-self: flex-start; margin-top: 1px; }
80
+ .spec-cards { display: flex; flex-wrap: wrap; gap: 8px; }
81
+ .spec-card { flex: 1 1 220px; border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; background: color-mix(in srgb, var(--panel) 85%, transparent); }
82
+ .spec-card-h { color: #000; background: var(--borderFocus); font-weight: 700; padding: 0 6px; border-radius: 3px; display: inline-block; margin-bottom: 4px; }
83
+ .spec-card.trade .spec-card-h { background: var(--pump); }
84
+ .spec-check { margin-right: 8px; color: var(--dim); }
85
+ .spec-check.ok { color: var(--up); font-weight: 600; }
86
+ .spec-big { font-size: 30px; font-weight: 800; letter-spacing: 0.02em; line-height: 1.1; }
87
+ .spec-big.up { color: var(--up); text-shadow: 0 0 14px color-mix(in srgb, var(--up) 45%, transparent); }
88
+ .spec-big.down { color: var(--down); }
89
+ .spec-kv { color: var(--muted); }
90
+ .spec-kv b { color: var(--text); font-weight: 600; }
@@ -12,6 +12,7 @@
12
12
  <div id="hud" class="hud">
13
13
  <div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">dexterm · WATCH THE AGENT COOK</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
14
14
  <div class="stage"><canvas id="rain"></canvas><div class="body">
15
+ <div id="story" class="spec-story"></div>
15
16
  <div id="screen" class="spec-screen"></div>
16
17
  <div id="agents" class="spec-agents"></div>
17
18
  <div class="spec-activity-head">agent activity</div>
@@ -14,8 +14,77 @@
14
14
 
15
15
  let vm = null;
16
16
 
17
+ const pctS = (p) => (p == null ? '—' : `${p >= 0 ? '+' : '−'}${Math.abs(p * 100).toFixed(1)}%`);
18
+ const px = (n) => (n == null ? '—' : n >= 1 ? `$${n.toFixed(4)}` : `$${Number(n.toPrecision(3))}`);
19
+ const fmtMs = (ms) => (ms == null ? '·' : ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`);
20
+ const CHECKS = ['price', 'tape', 'chart', 'holders', 'rug', 'dev', 'larp', 'verdict'];
21
+
22
+ // task-1466 spectator v2: the agent story above the activity list (textContent only; strings redacted TUI-side).
23
+ function renderStory(st) {
24
+ const host = $('story'); host.textContent = '';
25
+ if (!st) return;
26
+ const rail = el('div', 'spec-rail');
27
+ const heart = el('span', 'spec-heart' + (st.control === 'killed' ? ' dead' : st.control !== 'running' ? ' still' : ''));
28
+ if (st.bpm > 0) heart.style.animationDuration = `${(60 / st.bpm).toFixed(2)}s`;
29
+ rail.appendChild(heart);
30
+ rail.appendChild(el('span', 'spec-bpm', st.control === 'killed' ? '— bpm' : `${st.bpm} bpm`));
31
+ const ai = st.rail.indexOf(st.phase);
32
+ st.rail.forEach((p, i) => {
33
+ if (i) rail.appendChild(el('span', 'spec-sep', '─'));
34
+ const on = i === ai && st.control !== 'killed';
35
+ rail.appendChild(el('span', 'spec-phase p-' + p + (on ? ' on' : i < ai ? ' done' : ''), (on ? '▸' : '') + p.toUpperCase() + (on && st.sub ? ` · ${st.sub}` : '')));
36
+ });
37
+ if (st.control !== 'running') rail.appendChild(el('span', 'spec-ctlstate ' + st.control, st.control === 'killed' ? '■ KILLED' : st.control === 'paused' ? '❚❚ PAUSED' : 'YOU ARE DRIVING'));
38
+ rail.appendChild(el('span', 'spec-paper', 'PAPER'));
39
+ host.appendChild(rail);
40
+ const th = (st.thoughts || []).slice().reverse();
41
+ const agentT = th.find((x) => x.source === 'agent');
42
+ const ours = th.find((x) => x.source === 'dexterm');
43
+ const pick = agentT && (!ours || agentT.t >= ours.t - 1500 || Date.now() - agentT.t < 9000) ? agentT : ours || agentT;
44
+ const rib = el('div', 'spec-ribbon' + (pick && pick.source === 'dexterm' ? ' ours' : ''));
45
+ if (pick) { if (pick.source === 'agent') rib.appendChild(el('span', 'spec-ribbon-who', '❯')); rib.appendChild(el('span', '', pick.text)); }
46
+ else rib.appendChild(el('span', 'spec-dim', 'the agent has not said anything yet'));
47
+ host.appendChild(rib);
48
+ const tl = el('div', 'spec-timeline');
49
+ (st.nodes || []).slice(-14).forEach((n, i) => {
50
+ if (i) tl.appendChild(el('span', 'spec-link', '──'));
51
+ const node = el('div', 'spec-node' + (n.paper ? ' paper' : ''));
52
+ node.appendChild(el('span', 'spec-node-l', `${n.status === 'refused' ? '✗' : n.paper ? '■' : '●'} ${n.label}${n.count > 1 ? ` ×${n.count}` : ''}`));
53
+ node.appendChild(el('span', 'spec-chip ' + n.status, n.status === 'refused' ? 'refused' : fmtMs(n.ms)));
54
+ tl.appendChild(node);
55
+ });
56
+ host.appendChild(tl);
57
+ const cards = el('div', 'spec-cards');
58
+ if (st.focus) {
59
+ const c = el('div', 'spec-card');
60
+ c.appendChild(el('span', 'spec-card-h', `VETTING $${st.focus.symbol}`));
61
+ const row = el('div');
62
+ for (const k of CHECKS) row.appendChild(el('span', 'spec-check' + (st.focus.checks.includes(k) ? ' ok' : ''), `${st.focus.checks.includes(k) ? '✓' : '·'}${k}`));
63
+ c.appendChild(row);
64
+ if (st.focus.larpFlags != null) c.appendChild(el('div', 'spec-kv', `LARP evidence: ${st.focus.larpFlags} flag${st.focus.larpFlags === 1 ? '' : 's'} (not a verdict)`));
65
+ if (st.focus.verdict) c.appendChild(el('div', 'spec-kv', `verdict ${st.focus.verdict}${st.focus.rug != null ? ` · rug heuristic ${Math.round(st.focus.rug * 100)}%` : ''}`));
66
+ cards.appendChild(c);
67
+ }
68
+ if (st.arc) {
69
+ const a = st.arc;
70
+ const c = el('div', 'spec-card trade');
71
+ c.appendChild(el('span', 'spec-card-h', `PAPER · $${a.symbol} · ${a.stage === 'closed' ? 'RESULT' : a.stage === 'open' ? 'HOLDING' : a.stage === 'refused' ? 'REFUSED' : 'TICKET'}`));
72
+ const big = a.stage === 'closed' ? a.resultPct : a.pnlPct;
73
+ if (big != null) c.appendChild(el('div', 'spec-big ' + (big >= 0 ? 'up' : 'down'), pctS(big)));
74
+ const kv = el('div', 'spec-kv');
75
+ kv.textContent = [a.sizeUsd != null ? `size $${a.sizeUsd.toFixed(2)}` : null, a.entryUsd != null ? `entry ${px(a.entryUsd)}` : null, a.markUsd != null && a.stage !== 'closed' ? `mark ${px(a.markUsd)}` : null,
76
+ a.peakPct != null ? `peak ${pctS(a.peakPct)}` : null, a.exits ? `exits ${a.exits}` : null, a.realizedSol != null ? `realized ${a.realizedSol >= 0 ? '+' : ''}${a.realizedSol.toFixed(4)} SOL` : null,
77
+ a.refusal ? `refused: ${a.refusal}` : null].filter(Boolean).join(' · ');
78
+ c.appendChild(kv);
79
+ c.appendChild(el('div', 'spec-dim', 'simulated paper trade · not a result'));
80
+ cards.appendChild(c);
81
+ }
82
+ if (cards.childNodes.length) host.appendChild(cards);
83
+ }
84
+
17
85
  function render() {
18
86
  if (!vm) return;
87
+ renderStory(vm.story || null);
19
88
 
20
89
  const scr = $('screen'); scr.textContent = '';
21
90
  const s = vm.screen || {};
@@ -28,4 +28,8 @@ export const CHART_INDICATORS = ['ema9', 'ema21', 'vwap', 'volma', 'volume', 'ta
28
28
  export const CHART_STYLES = ['candles', 'line', 'area'];
29
29
  /** Price-scale mode: auto (normal, autoscaled), logarithmic, percent (from the first visible bar). */
30
30
  export const CHART_SCALES = ['auto', 'log', 'percent'];
31
+ /** task-1472: what the chart's Y axis shows (the popup's MCAP | PRICE control / `A` ask the terminal to switch). */
32
+ export const CHART_AXES = ['mcap', 'price'];
33
+ /** The most fills one chart VM carries (newest kept). */
34
+ export const CHART_MARKS_MAX = 500;
31
35
  export const PROTOCOL_VERSION = 'dexterm-popup/1';
@@ -26,7 +26,7 @@ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
26
26
  import { readFileSync, existsSync } from 'node:fs';
27
27
  import { fileURLToPath } from 'node:url';
28
28
  import path from 'node:path';
29
- import { PROTOCOL_VERSION, CHART_INTERVALS, CHART_INDICATORS, CHART_STYLES, CHART_SCALES, WIDGET_TOPICS, WIDGET_HTML_MAX } from "./protocol.js";
29
+ import { PROTOCOL_VERSION, CHART_INTERVALS, CHART_INDICATORS, CHART_STYLES, CHART_SCALES, CHART_AXES, WIDGET_TOPICS, WIDGET_HTML_MAX } from "./protocol.js";
30
30
  import { auditWidgetHtml, inlineHashes, normalizeWidgetHtml } from "./widget-html.js";
31
31
  const here = path.dirname(fileURLToPath(import.meta.url));
32
32
  const PAGES_DIR = path.join(here, 'pages');
@@ -48,6 +48,10 @@ export const POPUP_EVENTS = new Set([
48
48
  'event:popup.close-request', 'event:popup.focus', 'event:popup.drag',
49
49
  // task-1325 chart v2 requests (payloads validated by validateEventPayload below)
50
50
  'event:chart.interval', 'event:chart.indicator', 'event:chart.style', 'event:chart.scale', 'event:chart.painted',
51
+ // task-1472: the chart's Y axis (market cap ⇄ price): a display preference the terminal saves, nothing else
52
+ 'event:chart.axis',
53
+ // task-1483: show / hide the chart's BUY / SELL markers (display.chartMarkers): a display preference, nothing else
54
+ 'event:chart.markers',
51
55
  // task-1345 agent roster: per-agent pause / kick, by agent name (the only writes this popup can send)
52
56
  'event:roster.pause', 'event:roster.resume', 'event:roster.kick',
53
57
  // task-1355 order plans: cancel a plan by id (the ONLY write the plans popup can send — never arm/cap/sign)
@@ -94,6 +98,10 @@ export function validateEventPayload(name, payload) {
94
98
  return oneOf(CHART_STYLES, p.style) ? { style: p.style } : null;
95
99
  case 'chart.scale':
96
100
  return oneOf(CHART_SCALES, p.scale) ? { scale: p.scale } : null;
101
+ case 'chart.axis':
102
+ return oneOf(CHART_AXES, p.axis) ? { axis: p.axis } : null;
103
+ case 'chart.markers': // task-1483: show / hide the BUY / SELL markers (a display setting; nothing else)
104
+ return typeof p.on === 'boolean' ? { on: p.on } : null;
97
105
  case 'chart.painted':
98
106
  return (p.kind === 'first' || p.kind === 'data') && finiteIn(p.ms, 0, 600_000) && finiteIn(p.sentAt, 0, 1e15)
99
107
  ? { kind: p.kind, ms: p.ms, sentAt: p.sentAt } : null;
@@ -38,7 +38,7 @@ export function queryDevHistory(db, q) {
38
38
  creator: q.creator, status: 'unknown', asOf, source: DEV_HISTORY_SOURCE,
39
39
  launches: { n: 0, newestT: null, ageSec: null },
40
40
  labelled: { n: 0, rug: 0, dead: 0, fade: 0, flat: 0, runner: 0, horizon: '72h preferred', tier: 'keyless-proxy', newestT: null, ageSec: null },
41
- rugRate: null, badRate: null, devExit: { n: 0, medianMin: null, within60m: 0 }, serialRugger: false, recent: [],
41
+ rugRate: null, badRate: null, devExit: { n: 0, medianMin: null, within60m: 0, newestT: null, ageSec: null }, serialRugger: false, recent: [],
42
42
  };
43
43
  let links;
44
44
  try {
@@ -56,6 +56,7 @@ export function queryDevHistory(db, q) {
56
56
  const counts = { rug: 0, dead: 0, fade: 0, flat: 0, runner: 0 };
57
57
  let labelledN = 0, labelledNewest = null;
58
58
  const exits = [];
59
+ let exitNewest = null;
59
60
  const launches = [];
60
61
  for (const l of links) {
61
62
  const firstT = Number(l.first_t);
@@ -93,6 +94,7 @@ export function queryDevHistory(db, q) {
93
94
  const from = createdAt ?? Number(series[held].t);
94
95
  devExitMin = Math.max(0, (Number(out.t) - from) / 60_000);
95
96
  exits.push(devExitMin);
97
+ exitNewest = Math.max(exitNewest ?? 0, Number(out.t));
96
98
  }
97
99
  }
98
100
  }
@@ -109,12 +111,14 @@ export function queryDevHistory(db, q) {
109
111
  labelled: { ...empty.labelled, n: labelledN, ...counts, newestT: labelledNewest, ageSec: labelledNewest === null ? null : Math.max(0, Math.round((asOf - labelledNewest) / 1000)) },
110
112
  rugRate: labelledN ? counts.rug / labelledN : null,
111
113
  badRate: labelledN ? (counts.rug + counts.dead) / labelledN : null,
112
- devExit: { n: exits.length, medianMin: median(exits), within60m: exits.filter((m) => m <= 60).length },
114
+ devExit: { n: exits.length, medianMin: median(exits), within60m: exits.filter((m) => m <= 60).length, newestT: exitNewest, ageSec: exitNewest === null ? null : Math.max(0, Math.round((asOf - exitNewest) / 1000)) },
113
115
  serialRugger: counts.rug >= SERIAL_RUGGER_MIN,
114
116
  recent: launches,
115
117
  };
116
118
  }
117
- const ago = (sec) => sec === null ? 'age unknown' : sec < 3600 ? `${Math.max(1, Math.round(sec / 60))}m ago` : sec < 172800 ? `${Math.round(sec / 3600)}h ago` : `${Math.round(sec / 86400)}d ago`;
119
+ /** Compact age for a panel cell: `5m`, `3h`, `40d`; `age ?` when unknown. */
120
+ export const ageShort = (sec) => sec === null ? 'age ?' : sec < 3600 ? `${Math.max(1, Math.round(sec / 60))}m` : sec < 172800 ? `${Math.round(sec / 3600)}h` : `${Math.round(sec / 86400)}d`;
121
+ const ago = (sec) => sec === null ? 'age unknown' : `${ageShort(sec)} ago`;
118
122
  /**
119
123
  * The one-line summary every surface shows (DEV panel, rug-signal row, MCP note): counts with the sample size and
120
124
  * the age, or `unknown` with why. Plain wording: a risk flag from observed history, never "rug confirmed" or "safe".
@@ -9,6 +9,8 @@ export * from "./open.js";
9
9
  export * from "./verdicts.js";
10
10
  export * from "./schedule.js";
11
11
  export * from "./dev-history.js";
12
+ export * from "./ticks.js";
13
+ export * from "./retention.js";
12
14
  export { recorderCli, RECORDER_HELP } from "./cli.js";
13
15
  /** `--no-recorder` / `DEXTERM_RECORDER=0` resolution: the recorder is on in live mode unless either says no. */
14
16
  export function recorderEnabled(env, flagOff) {