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
@@ -1,14 +1,11 @@
1
- // dexterm — trade ticket screen (task-1310 T0 PAPER; task-1328 T2 LIVE). PAPER: a simulated buy/sell
2
- // priced from a live keyless Jupiter quote, every frame labelled PAPER. LIVE: the same ticket in LIVE
3
- // mode — ⏎ asks the SIGNER PROCESS to quote → build → decode → simulate and shows the PLAN (what will be
4
- // signed, in/out, impact as a percent, fee, route, the signer's caps verdict); Y is the keypress; the
5
- // signer then opens Phantom. The banner's network comes from the signer's own status (classified from
6
- // the RPC it talks to) — never from a flag the UI holds, so the label cannot disagree with the
7
- // connection. Pure renderer: the state machine + bindings live elsewhere; nothing here signs.
8
- import { Canvas, badge, header, footer, breakpoint, mix, isCalm, fmtPrice, fmtPriceBare, fmtAmount, fmtPct, } from '../../../tui/src/index.js';
1
+ // dexterm — trade ticket screen (task-1310 T0). PAPER only: a simulated buy/sell priced from a live keyless
2
+ // Jupiter quote, every frame labelled PAPER. task-1571: there is no LIVE ticket — dexterm holds no key and signs
3
+ // nothing; live trading happens in the Telegram trading bot of the user's choice. Pure renderer.
4
+ import { Canvas, badge, header, footer, breakpoint, mix, isCalm, fmtPrice, fmtPriceBare, fmtAmount, fmtPct, fmtMcap, } from '../../../tui/src/index.js';
5
+ import { priceModeOf, supplyForMint } from "../display.js";
9
6
  import { lamportsToSol, rawToTokens } from '../../../data/src/index.js';
10
7
  import { headerModel } from "../state.js";
11
- import { slippageBps, simFailureHint } from "../trade.js";
8
+ import { slippageBps } from "../trade.js";
12
9
  function fmtSol(sol, digits = 4) {
13
10
  const a = Math.abs(sol);
14
11
  return `${a.toFixed(digits)} SOL`;
@@ -22,83 +19,41 @@ function tokenAmount(raw, decimals, symbol, decimalsKnown) {
22
19
  return `${symbol}: decimals unknown yet (raw ${raw.toString()})`;
23
20
  return `${fmtAmount(rawToTokens(raw, decimals))} ${symbol}`;
24
21
  }
25
- /** Word-wrap `text` into at most `maxLines` lines of `width` columns; the last line is cut with an ellipsis. */
26
- function wrapLines(text, width, maxLines) {
27
- const words = text.split(/\s+/).filter((w) => w.length > 0);
28
- const lines = [];
29
- let cur = '';
30
- for (const w of words) {
31
- const next = cur ? `${cur} ${w}` : w;
32
- if (next.length <= width) {
33
- cur = next;
34
- continue;
35
- }
36
- if (cur)
37
- lines.push(cur);
38
- cur = w.length > width ? `${w.slice(0, Math.max(1, width - 1))}…` : w;
39
- if (lines.length === maxLines)
40
- break;
41
- }
42
- if (cur && lines.length < maxLines)
43
- lines.push(cur);
44
- if (lines.length > maxLines)
45
- lines.length = maxLines;
46
- const joined = lines.join(' ');
47
- if (joined.length < text.length && lines.length === maxLines) {
48
- const last = lines[maxLines - 1];
49
- lines[maxLines - 1] = last.length >= width - 1 ? `${last.slice(0, Math.max(1, width - 1))}…` : `${last}…`;
50
- }
51
- return lines;
52
- }
53
22
  export function renderTicket(c, state) {
54
23
  const t = c.theme;
55
24
  header(c, headerModel(state));
56
25
  const bp = breakpoint(c.w);
57
26
  // Live binding is the source of truth; the seeded demo view backs fixture/one-shot frames.
58
27
  const view = (state.live?.ticket ? state.live.ticket.view() : state.ticket) ?? null;
59
- const live = view?.live ?? null;
60
- const isLive = !!live;
61
- // Always-on banner strip (never hidden, any state). PAPER = warn tint; LIVE = the signer's network.
28
+ // Always-on banner strip (never hidden, any state).
62
29
  const bannerY = 2;
63
- const net = live?.signer?.network ?? null; // the SIGNER's classified network, or unknown
64
- const mainnet = net === 'mainnet';
65
- const bannerColor = !isLive ? t.warn : mainnet ? t.down : t.up;
66
- c.fill(0, bannerY, c.w, 1, ' ', { bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16) });
67
- const banner = !isLive
68
- ? ' ◈ PAPER TRADING — simulated · no real orders · no keys · no signing '
69
- : net === null
70
- ? ' ◉ LIVE — signer not reachable · nothing can be signed '
71
- : mainnet
72
- ? ` ⚠ LIVE · MAINNET — REAL FUNDS · caps $${live?.signer?.caps.perTradeUsd ?? '?'}/trade $${live?.signer?.caps.dailyUsd ?? '?'}/day · Phantom signs · dexterm holds no key `
73
- : ` ◉ LIVE · DEVNET — real signing via Phantom · devnet funds only · dexterm holds no key `;
74
- c.text(2, bannerY, banner, { fg: bannerColor, bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16), b: true });
30
+ const bannerColor = t.warn;
31
+ c.fill(0, bannerY, c.w, 1, ' ', { bg: mix(t.bg, bannerColor, 0.16) });
32
+ c.text(2, bannerY, ' ◈ PAPER TRADING — simulated · no real orders · no keys · no signing ', { fg: bannerColor, bg: mix(t.bg, bannerColor, 0.16), b: true });
75
33
  const linked = state.live?.linkedWallet;
76
34
  if (linked)
77
- c.textR(2, bannerY, c.w - 3, `linked: ${linked.publicKey.slice(0, 4)}…${linked.publicKey.slice(-4)} `, { fg: t.muted, bg: mix(t.bg, bannerColor, mainnet ? 0.3 : 0.16) });
35
+ c.textR(2, bannerY, c.w - 3, `linked: ${linked.publicKey.slice(0, 4)}…${linked.publicKey.slice(-4)} `, { fg: t.muted, bg: mix(t.bg, bannerColor, 0.16) });
78
36
  c.noRain(0, bannerY, c.w, 1);
79
- if (live && (live.signer?.panic || live.signer?.ledgerLocked)) {
80
- c.fill(0, bannerY + 1, c.w, 1, ' ', { bg: mix(t.bg, t.down, 0.3) });
81
- c.text(2, bannerY + 1, ' ⛔ PANIC — live signing is disabled · re-enable only from a shell: dexterm wallet resume ', { fg: t.down, bg: mix(t.bg, t.down, 0.3), b: true });
82
- c.noRain(0, bannerY + 1, c.w, 1);
83
- }
84
37
  if (!view) {
85
- c.text(2, 4, 'Open a trade ticket from a token screen: B buy · S sell (L on the ticket flips PAPER ⇄ LIVE)', { fg: t.muted, bg: t.bg });
38
+ c.text(2, 4, 'Open a paper trade ticket from a token screen: B buy · S sell', { fg: t.muted, bg: t.bg });
86
39
  footer(c, [['esc', 'back'], ['P', 'positions'], ['q', 'quit']], t.name, isCalm(state.vfx));
87
40
  return;
88
41
  }
89
42
  const ts = view.ticket;
43
+ const mode = priceModeOf(state); // task-1537: fills and prices read as market caps by default
44
+ const sup = supplyForMint(state, ts.mint);
90
45
  const buy = ts.side === 'buy';
91
46
  const sideColor = buy ? t.up : t.down;
92
47
  const top = 4;
93
48
  const w = Math.min(bp === 'compact' ? c.w - 2 : 78, c.w - 2);
94
49
  const x0 = 1;
95
50
  const h = c.h - top - 1;
96
- const title = isLive ? `◉ LIVE ${live?.probe ? 'DEVNET PROBE' : `${ts.side.toUpperCase()} TICKET`}` : `◈ PAPER ${ts.side.toUpperCase()} TICKET`;
97
- const caption = isLive ? `signer process · jupiter quote · phantom signs${net ? ` · ${net}` : ''}` : 'jupiter keyless quote · paper fill';
51
+ const title = `◈ PAPER ${ts.side.toUpperCase()} TICKET`;
52
+ const caption = 'jupiter keyless quote · paper fill';
98
53
  // task-1340: the ticket is PLAIN by construction — no heading (no chromatic split on its title) and every cell of
99
54
  // its rect is shielded below, so no effect pass (rain, mosh, shake, scanlines, trade moments) can write a glyph, a
100
55
  // colour or a background into it in any phase. The banner rows above it are shielded too.
101
- const box = c.box(x0, top, w, h, { title, titleFg: isLive && mainnet ? t.down : sideColor, caption, captionFg: t.dim, border: mix(t.border, isLive && mainnet ? t.down : sideColor, 0.5), plain: true });
56
+ const box = c.box(x0, top, w, h, { title, titleFg: sideColor, caption, captionFg: t.dim, border: mix(t.border, sideColor, 0.5), plain: true });
102
57
  c.shieldRect(x0, top, w, h);
103
58
  c.shieldRect(0, bannerY, c.w, 2);
104
59
  const bg = box.bg;
@@ -109,24 +64,18 @@ export function renderTicket(c, state) {
109
64
  let x = c.text(ix, y, `$${ts.symbol}`, { fg: t.borderFocus, bg, b: true });
110
65
  x = c.text(x + 1, y, `${ts.mint.slice(0, 4)}…${ts.mint.slice(-4)}`, { fg: t.dim, bg });
111
66
  x = badge(c, x + 2, y, buy ? '▲ BUY' : '▼ SELL', sideColor, { on: bg, bold: true }) + 1;
112
- x = badge(c, x + 1, y, isLive ? 'LIVE' : 'PAPER', isLive ? (mainnet ? t.down : t.up) : t.warn, { on: bg, bold: true }) + 1;
113
- c.textR(ix, y, iw, ts.phase === 'editing' ? '← → flip side · L paper⇄live' : '', { fg: t.dim, bg });
67
+ x = badge(c, x + 1, y, 'PAPER', t.warn, { on: bg, bold: true }) + 1;
68
+ c.textR(ix, y, iw, ts.phase === 'editing' ? '← → flip side' : '', { fg: t.dim, bg });
114
69
  y += 2;
115
70
  // size field
116
- if (!live?.probe) {
117
- const sizeUnit = buy ? (ts.sizeUnit === 'sol' ? 'SOL' : '% of balance') : '% of position';
118
- field(c, ix, y, iw, bg, 'Size', `${ts.sizeInput || '—'} ${sizeUnit}`, ts.field === 'size' && ts.phase === 'editing', t);
119
- y += 1;
120
- field(c, ix, y, iw, bg, 'Slippage', `${slippageBps(ts)} bps`, ts.field === 'slippage' && ts.phase === 'editing', t);
121
- y += 2;
122
- }
123
- const showPlan = isLive && live && live.plan && (ts.phase === 'plan' || ts.phase === 'signing' || ts.phase === 'filled' || ts.phase === 'refused' || ts.phase === 'error');
124
- if (showPlan && live?.plan) {
125
- y = renderPlan(c, ix, y, iw, bg, live, ts.decimals, ts.symbol, buy, state.now.getTime());
126
- }
127
- else {
128
- // quote block (paper warm quote; LIVE editing shows the same keyless quote as a preview)
129
- c.text(ix, y, isLive ? 'PREVIEW QUOTE (keyless; the signer re-quotes on ⏎)' : 'QUOTE', { fg: t.dim, bg });
71
+ const sizeUnit = buy ? (ts.sizeUnit === 'sol' ? 'SOL' : '% of balance') : '% of position';
72
+ field(c, ix, y, iw, bg, 'Size', `${ts.sizeInput || '—'} ${sizeUnit}`, ts.field === 'size' && ts.phase === 'editing', t);
73
+ y += 1;
74
+ field(c, ix, y, iw, bg, 'Slippage', `${slippageBps(ts)} bps`, ts.field === 'slippage' && ts.phase === 'editing', t);
75
+ y += 2;
76
+ {
77
+ // quote block (the paper warm quote)
78
+ c.text(ix, y, 'QUOTE', { fg: t.dim, bg });
130
79
  c.textR(ix, y, iw, view.loading ? '· fetching…' : view.quote ? `· ${Math.max(0, Math.round((view.quoteAgeMs ?? 0) / 1000))}s ago` : view.error ? '· unavailable' : '', { fg: view.error ? t.down : t.dim, bg });
131
80
  c.hline(box.x + 1, y + 1, box.w - 2, { fg: t.border, bg, soft: true });
132
81
  y += 2;
@@ -139,51 +88,29 @@ export function renderTicket(c, state) {
139
88
  const recv = buy ? tokenAmount(tokenAbs, ts.decimals, ts.symbol, decimalsKnown) : `${fmtSol(lamportsToSol(preview.solDelta))}`;
140
89
  kv(c, ix, y++, iw, bg, 'You pay', spend, t, buy ? t.down : t.text);
141
90
  kv(c, ix, y++, iw, bg, 'You receive', recv, t, buy && !decimalsKnown ? t.warn : t.up);
142
- kv(c, ix, y++, iw, bg, 'Price', decimalsKnown ? `${fmtPriceBare(preview.priceSol)} SOL${preview.priceUsd !== null ? ` · ${fmtPrice(preview.priceUsd)}` : ''}` : 'per-token price needs the decimals', t, decimalsKnown ? t.text : t.warn);
91
+ // task-1537: in the market-cap basis (default) the fill price reads as the cap it buys/sells at, the price beside it
92
+ const capAt = mode === 'mcap' && sup !== null && preview.priceUsd !== null ? fmtMcap(preview.priceUsd * sup) : null;
93
+ kv(c, ix, y++, iw, bg, capAt ? 'At mcap' : 'Price', decimalsKnown ? `${capAt ? `${capAt} · ` : ''}${fmtPriceBare(preview.priceSol)} SOL${preview.priceUsd !== null ? ` · ${fmtPrice(preview.priceUsd)}` : ''}` : 'per-token price needs the decimals', t, decimalsKnown ? t.text : t.warn);
143
94
  const impact = preview.priceImpactPct;
144
95
  kv(c, ix, y++, iw, bg, 'Price impact', impact === null ? '—' : fmtPct(impact, { sign: false }), t, impact !== null && impact > 5 ? t.down : impact !== null && impact > 1 ? t.warn : t.muted);
145
96
  kv(c, ix, y++, iw, bg, 'Min received', buy ? tokenAmount(q.otherAmountThreshold, ts.decimals, ts.symbol, decimalsKnown) : fmtSol(lamportsToSol(q.otherAmountThreshold)), t, t.muted);
146
- kv(c, ix, y++, iw, bg, isLive ? 'Est. fee' : 'Sim fee', fmtSol(lamportsToSol(preview.feeLamports), 6), t, t.muted);
97
+ kv(c, ix, y++, iw, bg, 'Sim fee', fmtSol(lamportsToSol(preview.feeLamports), 6), t, t.muted);
147
98
  kv(c, ix, y++, iw, bg, 'Route', q.route.length ? q.route.join(' → ') : 'direct', t, t.accent);
148
99
  }
149
100
  else if (view.error) {
150
101
  c.text(ix, y++, `⚠ ${view.error}`, { fg: t.down, bg }, iw);
151
102
  }
152
- else if (live?.probe) {
153
- c.text(ix, y++, 'wrap 0.001 SOL into your own WSOL account, sync, memo, close it back — fees only', { fg: t.dim, bg }, iw);
154
- }
155
103
  else {
156
104
  c.text(ix, y++, view.loading ? 'fetching a live quote…' : 'enter a size to see a quote', { fg: t.dim, bg }, iw);
157
105
  }
158
- // task-1368 items b + c: BEFORE ⏎ on a LIVE ticket — the size in USD against the signer's caps (red when the
159
- // signer would refuse it, with the size that fits), and the devnet-cannot-fill-mainnet-tokens line.
160
- if (isLive && live && !live.probe && ts.phase === 'editing') {
161
- const pf = live.preflight ?? null;
162
- if (pf) {
163
- const color = pf.over ? t.down : pf.sizeUsd === null && pf.sizeSol > 0 ? t.warn : t.muted;
164
- for (const line of wrapLines(`${pf.over ? '✗ ' : ''}${pf.line}`, iw, 2))
165
- c.text(ix, y++, line, { fg: color, bg, b: pf.over }, iw);
166
- }
167
- if (live.devnetWarning)
168
- for (const line of wrapLines(`⚠ ${live.devnetWarning}`, iw, 2))
169
- c.text(ix, y++, line, { fg: t.warn, bg, b: true }, iw);
170
- }
171
106
  y += 1;
172
107
  }
173
108
  // wallet line
174
109
  c.hline(box.x + 1, y, box.w - 2, { fg: t.border, bg, soft: true });
175
110
  y += 1;
176
- if (isLive) {
177
- kv(c, ix, y++, iw, bg, 'Signer', live?.signer ? `${live.signer.network.toUpperCase()} · ${live.signer.rpcHost} · pid ${live.signer.pid}` : (live?.error ?? 'not reachable'), t, live?.signer ? t.text : t.down);
178
- if (live?.signer)
179
- kv(c, ix, y++, iw, bg, 'Caps', `$${live.signer.caps.perTradeUsd}/trade · $${live.signer.caps.dailyUsd}/day · spent $${live.signer.spentTodayUsd.toFixed(2)} · impact ≤ ${live.signer.caps.maxPriceImpactPct}% · ≤ ${live.signer.caps.maxSolOut} SOL out`, t, t.muted);
180
- kv(c, ix, y++, iw, bg, 'Wallet', live?.linked ? `${live.linked} (Phantom signs; dexterm holds no key)` : 'none linked', t, live?.linked ? t.text : t.down);
181
- }
182
- else {
183
- kv(c, ix, y++, iw, bg, 'Paper balance', fmtSol(view.balanceSol), t, t.text);
184
- }
111
+ kv(c, ix, y++, iw, bg, 'Paper balance', fmtSol(view.balanceSol), t, t.text);
185
112
  if (!buy || view.positionTokens > 0)
186
- kv(c, ix, y++, iw, bg, `Position`, `${fmtAmount(view.positionTokens)} ${ts.symbol}${isLive ? ' (live ledger)' : ''}`, t, t.muted);
113
+ kv(c, ix, y++, iw, bg, `Position`, `${fmtAmount(view.positionTokens)} ${ts.symbol}`, t, t.muted);
187
114
  y += 1;
188
115
  // gate / receipt region
189
116
  const gy = box.y + box.h - 3;
@@ -201,127 +128,33 @@ export function renderTicket(c, state) {
201
128
  if (ts.message)
202
129
  c.text(ix, gy + 1, ts.message, { fg: t.warn, bg: tint }, iw);
203
130
  }
204
- else if (ts.phase === 'planning' && live?.retryAt) {
205
- // task-1368: rate limited by Jupiter — the plan is NOT dropped; countdown + r / esc.
206
- const left = Math.max(0, Math.ceil((live.retryAt - state.now.getTime()) / 1000));
207
- const what = (live.retryMessage ?? 'Jupiter is rate limiting this IP').replace(/; retry in \d+s$/, '');
208
- c.fill(box.x + 1, gy, box.w - 2, 2, ' ', { bg: mix(bg, t.warn, 0.18) });
209
- c.text(ix, gy, `⚠ ${what}, retrying in ${left}s`, { fg: t.warn, bg: mix(bg, t.warn, 0.18), b: true }, iw);
210
- c.text(ix, gy + 1, 'r = retry now · esc = cancel · the plan is kept (nothing was built or signed)', { fg: t.muted, bg: mix(bg, t.warn, 0.18) }, iw);
211
- }
212
- else if (ts.phase === 'planning') {
213
- c.text(ix, gy, 'signer: quote → build → decode → policy → simulate… (esc cancels)', { fg: t.ai, bg, b: true }, iw);
214
- }
215
- else if (ts.phase === 'plan' && live?.plan) {
216
- const refused = !live.plan.policy.ok || !live.plan.precheck.ok;
217
- c.fill(box.x + 1, gy, box.w - 2, 2, ' ', { bg: tint });
218
- if (refused) {
219
- const v = !live.plan.policy.ok ? live.plan.policy : live.plan.precheck;
220
- c.text(ix, gy, `✗ the signer WILL REFUSE this plan: ${v.reason ?? ''} — ${v.message ?? ''}`, { fg: t.down, bg: tint, b: true }, iw);
221
- c.text(ix, gy + 1, 'esc back to editing (Y asks anyway and gets the signer\'s refusal)', { fg: t.muted, bg: tint }, iw);
222
- }
223
- else if (ts.typebackRequired) {
224
- c.text(ix, gy, `⚠ large order — type the size (${ts.sizeInput}) then press Y to sign in Phantom:`, { fg: t.warn, bg: tint, b: true }, iw);
225
- c.text(ix, gy + 1, `▌ ${ts.typeback || ''}`, { fg: t.text, bg: tint, b: true });
226
- }
227
- else {
228
- c.text(ix, gy, `${mainnet ? '⚠ REAL FUNDS · ' : ''}Y = sign this exact plan in Phantom (esc cancels · any other key cancels)`, { fg: mainnet ? t.down : sideColor, bg: tint, b: true }, iw);
229
- c.text(ix, gy + 1, `the signer re-checks caps, verifies the signed bytes and re-simulates before it submits`, { fg: t.muted, bg: tint }, iw);
230
- }
231
- if (ts.message)
232
- c.text(ix, gy + 1, ts.message, { fg: t.warn, bg: tint }, iw);
233
- }
234
- else if (ts.phase === 'signing') {
235
- c.text(ix, gy, 'approve in Phantom (browser) — the signer verifies, re-simulates and submits…', { fg: t.ai, bg, b: true }, iw);
236
- c.text(ix, gy + 1, 'nothing is sent until the signed bytes match the plan', { fg: t.muted, bg }, iw);
237
- }
238
131
  else if (ts.phase === 'filling') {
239
132
  const line = view.rateLimited
240
133
  ? '⚠ rate limited by Jupiter — retrying… (esc cancels)'
241
134
  : 'booking paper fill from a fresh quote… (esc cancels)';
242
135
  c.text(ix, gy, line, { fg: view.rateLimited ? t.warn : t.ai, bg, b: true }, iw);
243
136
  }
244
- else if (ts.phase === 'filled' && isLive && live?.outcome?.kind === 'filled') {
245
- const o = live.outcome;
246
- const got = live.probe ? `wrap-and-unwrap confirmed · fee ${fmtSol(lamportsToSol(BigInt(o.fill.feeLamports)), 6)}` : `${fmtAmount(rawToTokens(o.fill.side === 'buy' ? BigInt(o.fill.tokenDelta) : -BigInt(o.fill.tokenDelta), o.fill.decimals))} ${o.fill.symbol} @ ${fmtPriceBare(o.fill.priceSol)} SOL`;
247
- c.text(ix, gy, `✓ LIVE ${o.network.toUpperCase()} ${live.probe ? 'PROBE' : o.fill.side.toUpperCase()} confirmed — ${got}${o.lighthouseTail ? ` · +${o.lighthouseTail} Lighthouse` : ''}`, { fg: t.up, bg, b: true }, iw);
248
- c.text(ix, gy + 1, `sig ${o.signature.slice(0, 12)}…${o.signature.slice(-6)} · esc=back · P=positions`, { fg: t.muted, bg }, iw);
249
- }
250
137
  else if (ts.phase === 'filled' && view.receipt) {
251
138
  const r = view.receipt;
252
139
  const got = r.side === 'buy' ? `${fmtAmount(rawToTokens(r.tokenDelta, r.decimals))} ${r.symbol}` : fmtSol(lamportsToSol(r.solDelta));
253
- c.text(ix, gy, `✓ PAPER ${r.side.toUpperCase()} filled — ${got} @ ${fmtPriceBare(r.priceSol)} SOL`, { fg: t.up, bg, b: true }, iw);
140
+ const at = mode === 'mcap' && sup !== null && r.priceUsd !== null ? `${fmtMcap(r.priceUsd * sup)} mcap (${fmtPriceBare(r.priceSol)} SOL)` : `${fmtPriceBare(r.priceSol)} SOL`; // task-1537
141
+ c.text(ix, gy, `✓ PAPER ${r.side.toUpperCase()} filled — ${got} @ ${at}`, { fg: t.up, bg, b: true }, iw);
254
142
  const extra = r.side === 'sell' ? `realized ${r.realizedLamports >= 0n ? '+' : ''}${fmtSol(lamportsToSol(r.realizedLamports))}` : 'cost booked to position';
255
143
  c.text(ix, gy + 1, `${extra} · esc=back to token · P=positions`, { fg: t.muted, bg }, iw);
256
144
  }
257
- else if (ts.phase === 'refused') {
258
- c.text(ix, gy, `✗ REFUSED by the signer — ${ts.message ?? 'refused'}`, { fg: t.down, bg, b: true }, iw);
259
- c.text(ix, gy + 1, 'nothing was signed or sent · esc=back · any key = new ticket', { fg: t.muted, bg }, iw);
260
- }
261
145
  else if (ts.phase === 'error') {
262
- const unknown = live?.outcome?.kind === 'send-unknown';
263
146
  c.text(ix, gy, `⚠ ${ts.message ?? 'order failed'}`, { fg: t.down, bg, b: true }, iw);
264
- c.text(ix, gy + 1, unknown && live?.outcome?.kind === 'send-unknown' ? `sig ${live.outcome.signature.slice(0, 12)}… · DO NOT resend · check the explorer · esc=back` : 'esc=back to token · P=positions', { fg: unknown ? t.warn : t.muted, bg }, iw);
147
+ c.text(ix, gy + 1, 'esc=back to token · P=positions', { fg: t.muted, bg }, iw);
265
148
  }
266
149
  else {
267
- c.text(ix, gy, isLive ? `⏎ build the plan in the signer → Y signs in Phantom · gate ${ts.gateOn ? 'ON' : 'off'}` : `⏎ review → Y confirm · gate ${ts.gateOn ? 'ON' : 'off'} (keypress required)`, { fg: t.muted, bg }, iw);
150
+ c.text(ix, gy, `⏎ review → Y confirm · gate ${ts.gateOn ? 'ON' : 'off'} (keypress required)`, { fg: t.muted, bg }, iw);
268
151
  if (ts.message)
269
152
  c.text(ix, gy + 1, ts.message, { fg: t.warn, bg }, iw);
270
153
  }
271
- const keys = [['⏎', isLive ? 'plan' : 'review'], ['Y', isLive ? 'sign' : 'confirm'], ['L', isLive ? 'paper' : 'live'], ['tab', 'field'], ['←→', 'side'], ['u', 'unit']];
272
- if (isLive && buy && ts.phase === 'editing' && !live?.probe)
273
- keys.push(['f', 'fit cap']);
274
- if (ts.phase === 'planning' && live?.retryAt)
275
- keys.push(['r', 'retry now']);
154
+ const keys = [['⏎', 'review'], ['Y', 'confirm'], ['tab', 'field'], ['←→', 'side'], ['u', 'unit']];
276
155
  keys.push(['esc', 'cancel'], ['P', 'positions']);
277
156
  footer(c, keys, t.name, isCalm(state.vfx));
278
157
  }
279
- /** The PLAN block: everything the signer will sign, from the signer's own view. */
280
- function renderPlan(c, ix, y, iw, bg, live, decimals, symbol, buy, now) {
281
- const t = c.theme;
282
- const p = live.plan;
283
- const left = Math.max(0, Math.round((p.expiresAt - now) / 1000));
284
- c.text(ix, y, `PLAN · ${p.network.toUpperCase()} · ${p.kind === 'probe' ? 'devnet probe' : 'jupiter swap'}`, { fg: p.network === 'mainnet' ? t.down : t.up, bg, b: true });
285
- c.textR(ix, y, iw, `expires in ${left}s · ${p.hash.slice(0, 8)}`, { fg: left < 15 ? t.warn : t.dim, bg });
286
- c.hline(ix - 1, y + 1, iw + 2, { fg: t.border, bg, soft: true });
287
- y += 2;
288
- const inRaw = BigInt(p.amountInRaw), outRaw = BigInt(p.amountOutRaw), minRaw = BigInt(p.minOutRaw);
289
- if (p.kind === 'probe') {
290
- kv(c, ix, y++, iw, bg, 'Wraps', `${fmtSol(lamportsToSol(inRaw))} → your own WSOL account → back to you`, t, t.text);
291
- }
292
- else {
293
- // The plan's decimals are the ones the order was built with (the signer echoes them back).
294
- const pay = buy ? fmtSol(lamportsToSol(inRaw)) : `${fmtAmount(rawToTokens(inRaw, p.decimals ?? decimals))} ${symbol}`;
295
- const recv = buy ? `${fmtAmount(rawToTokens(outRaw, p.decimals ?? decimals))} ${symbol}` : fmtSol(lamportsToSol(outRaw));
296
- const min = buy ? `${fmtAmount(rawToTokens(minRaw, p.decimals ?? decimals))} ${symbol}` : fmtSol(lamportsToSol(minRaw));
297
- kv(c, ix, y++, iw, bg, 'You pay', pay, t, buy ? t.down : t.text);
298
- kv(c, ix, y++, iw, bg, 'You receive', recv, t, t.up);
299
- kv(c, ix, y++, iw, bg, 'Min received', `${min} (${p.slippageBps} bps slippage)`, t, t.muted);
300
- const impact = p.priceImpactPct;
301
- kv(c, ix, y++, iw, bg, 'Price impact', impact === null ? 'unknown' : fmtPct(impact, { sign: false }), t, impact !== null && impact > 3 ? t.down : impact !== null && impact > 1 ? t.warn : t.muted);
302
- kv(c, ix, y++, iw, bg, 'Route', p.route.length ? p.route.join(' → ') : 'direct', t, t.accent);
303
- }
304
- kv(c, ix, y++, iw, bg, 'Fee', `${fmtSol(lamportsToSol(BigInt(p.feeLamports)), 6)}${p.prioritizationFeeLamports ? ` (incl. ${p.prioritizationFeeLamports} lamports priority)` : ''}`, t, t.muted);
305
- kv(c, ix, y++, iw, bg, 'Notional', p.notionalUsd !== null ? `$${p.notionalUsd.toFixed(2)} (SOL $${p.solUsd.price?.toFixed(2) ?? '?'} via ${p.solUsd.sources?.join('+') ?? '?'})` : (p.network === 'mainnet' ? `unpriced — ${p.solUsd.reason ?? ''}` : 'devnet (no USD value)'), t, p.notionalUsd === null && p.network === 'mainnet' ? t.down : t.muted);
306
- if (!p.sim.ran || p.sim.ok) {
307
- kv(c, ix, y++, iw, bg, 'Simulation', !p.sim.ran ? 'not run' : `ok · ${p.sim.solOutLamports !== null ? `${fmtSol(lamportsToSol(BigInt(p.sim.solOutLamports)), 6)} leaves the wallet` : 'wallet delta unknown'}`, t, p.sim.ok ? t.up : t.down);
308
- }
309
- else {
310
- // task-1368 item f: the FULL RPC error, wrapped (it used to be cut to `invalid transaction: Tr…`), then the hint.
311
- kv(c, ix, y++, iw, bg, 'Simulation', 'FAILED', t, t.down);
312
- for (const line of wrapLines(p.sim.error ?? 'unknown error', iw - 2, 3))
313
- c.text(ix + 2, y++, line, { fg: t.down, bg }, iw - 2);
314
- const hint = simFailureHint(p, live.signer ? { perTradeUsd: live.signer.caps.perTradeUsd, dailyUsd: live.signer.caps.dailyUsd } : undefined);
315
- if (hint)
316
- for (const line of wrapLines(`↳ ${hint}`, iw - 2, 4))
317
- c.text(ix + 2, y++, line, { fg: t.warn, bg }, iw - 2);
318
- }
319
- const ins = p.instructions.map((i) => `${i.program}${i.op ? `:${i.op}` : ''}`).join(' · ');
320
- kv(c, ix, y++, iw, bg, 'Will sign', ins || '—', t, t.text);
321
- kv(c, ix, y++, iw, bg, 'Policy', p.policy.ok ? 'swap shape ok · route bound to this plan · no fee account' : `REFUSE ${p.policy.reason}: ${p.policy.message}`, t, p.policy.ok ? t.up : t.down);
322
- kv(c, ix, y++, iw, bg, 'Caps', p.precheck.ok ? 'within caps' : `REFUSE ${p.precheck.reason}: ${p.precheck.message}`, t, p.precheck.ok ? t.up : t.down);
323
- return y + 1;
324
- }
325
158
  function field(c, x, y, w, bg, label, value, active, t) {
326
159
  const lblW = 10;
327
160
  c.text(x, y, label, { fg: t.dim, bg });
@@ -8,8 +8,9 @@ import { renderTokenLive } from "../live/screens/token-live.js";
8
8
  import { aiTranscript } from "../fixtures.js";
9
9
  import { renderAnalystSession } from "./analyst-session.js";
10
10
  import { renderSocialPanel } from "./social-panel.js";
11
+ import { brainFor, factsFromFixture, renderBrainPanel } from "./brain.js";
11
12
  import { layoutFor } from "../custom.js";
12
- import { chartMode, priceModeOf } from "../display.js";
13
+ import { chartMode, chartTitle, chartAxisOf, priceModeOf, levelCell, supplyRatio } from "../display.js";
13
14
  export function renderToken(c, state) {
14
15
  if (state.live?.token && state.screener.source !== 'fixtures')
15
16
  return renderTokenLive(c, state, state.live.token.view); // task-1298: live data path
@@ -48,13 +49,13 @@ export function renderToken(c, state) {
48
49
  // price line: pump glow = gold bg band, dump = red band (the rain ramp of the moment)
49
50
  const priceBg = pumping ? mix(t.bg, t.rainPump[2], 0.3) : dumping ? mix(t.bg, t.rainDump[2], 0.3) : t.bg;
50
51
  // task-1366: mcap mode leads with the market cap and keeps the price beside it (fixture tokens always carry a supply)
51
- const cm = chartMode(priceModeOf(state), tk.price, tk.mcap);
52
- if (cm.kind === 'mcap') {
52
+ const pm = chartMode(priceModeOf(state), tk.price, tk.mcap);
53
+ if (pm.kind === 'mcap') {
53
54
  x = c.text(1, y, fmtMcap(tk.mcap), { fg: pumping ? t.rainPump[3] : t.text, bg: priceBg, b: true });
54
- x = c.text(x, y, ` (${fmtPrice(tk.price)})`, { fg: t.dim, bg: priceBg });
55
+ x = c.text(x, y, ` (${fmtPrice(tk.price)})`, { fg: priceBg === t.bg ? t.dim : t.text, bg: priceBg }); // task-1537: readable on the pump / dump band
55
56
  }
56
57
  else
57
- x = c.text(1, y, fmtPrice(tk.price), { fg: cm.label ? t.warn : pumping ? t.rainPump[3] : t.text, bg: priceBg, b: true });
58
+ x = c.text(1, y, fmtPrice(tk.price), { fg: pm.label ? t.warn : pumping ? t.rainPump[3] : t.text, bg: priceBg, b: true });
58
59
  const deltas = [['5m', tk.chg.m5], ['1h', tk.chg.h1], ['6h', tk.chg.h6], ['24h', tk.chg.h24]];
59
60
  x = c.text(x, y, ' ', { bg: t.bg });
60
61
  for (const [k, vv] of deltas) {
@@ -70,7 +71,9 @@ export function renderToken(c, state) {
70
71
  y += 1;
71
72
  // ---- chart
72
73
  const chartH = Math.max(10, Math.floor((bottom - y) * lay.chartFrac));
73
- const cb = c.box(0, y, mainW, chartH, { title: cm.label ? (cm.kind === 'mcap' ? `${tk.symbol} ${cm.label} · 1h` : `${tk.symbol}/SOL · ${cm.label} · 1h`) : `${tk.symbol}/SOL · 1h`, caption: 'ohlcv: geckoterminal' });
74
+ // task-1472: the chart Y axis is its own setting (default MARKET CAP, `A` toggles); the header above keeps `$`
75
+ const cm = chartMode(chartAxisOf(state), tk.price, tk.mcap);
76
+ const cb = c.box(0, y, mainW, chartH, { title: chartTitle(tk.symbol, '1h', cm, 'SOL'), caption: 'ohlcv: geckoterminal' });
74
77
  const tfx = cb.x + cb.w - 2;
75
78
  const tfs = ['1m', '5m', '15m', '1h', '4h', '1d'];
76
79
  let tx = tfx - tfs.reduce((s, f) => s + f.length + 1, 0) - 24;
@@ -159,7 +162,7 @@ export function renderToken(c, state) {
159
162
  let cx = c.text(hb.x + 1, hy, fmtAgeSec(tr.ago).padStart(4), { fg: t.dim, bg: hb.bg });
160
163
  cx = c.text(cx + 1, hy, tr.side === 'buy' ? '▲BUY ' : '▼SELL', { fg: color, bg: hb.bg, b: true });
161
164
  cx = c.textR(cx + 1, hy, 8, fmtUsd(tr.usd), { fg: t.text, bg: hb.bg });
162
- cx = c.text(cx + 2, hy, fmtPrice(tr.price), { fg: t.muted, bg: hb.bg });
165
+ cx = c.text(cx + 2, hy, levelCell(priceModeOf(state), supplyRatio(tk.price, tk.mcap), tr.price), { fg: t.muted, bg: hb.bg }); // task-1537
163
166
  cx = c.text(cx + 2, hy, shortAddr(tr.addr, 3), { fg: t.dim, bg: hb.bg });
164
167
  if (tr.tag)
165
168
  badge(c, cx + 1, hy, tr.tag, tr.tag === 'smart' ? t.accent : t.warn, { on: hb.bg });
@@ -171,8 +174,14 @@ export function renderToken(c, state) {
171
174
  }
172
175
  }
173
176
  }
174
- if (showAi)
175
- analystPanel(c, mainW, top, aiW, bottom - top, tk, state);
177
+ if (showAi) {
178
+ // task-1473: in an auto mode (following an agent / AI scan) the panel is the BRAIN VIEW; otherwise the analyst
179
+ const brain = brainFor(state, factsFromFixture(tk, state.larpByMint?.get(tk.mint)?.verdict ?? null), isCalm(v), state.live?.scanMeterLabel?.() ?? '');
180
+ if (brain)
181
+ renderBrainPanel(c, mainW, top, aiW, bottom - top, brain.model, brain.opts);
182
+ else
183
+ analystPanel(c, mainW, top, aiW, bottom - top, tk, state);
184
+ }
176
185
  footer(c, state.tokenSocial
177
186
  ? [['esc', 'back'], ['s', 'on-chain'], ['f', 'filter'], ['x', state.socialShowSpam ? 'hide spam' : 'show hidden'], ['K', 'KOL lists'], ['a', showAi ? 'hide analyst' : 'analyst'], ['w', 'watch'], ['q', 'quit']]
178
187
  : [['esc', 'back'], ['g', 'go to'], ['a', showAi ? 'hide analyst' : 'analyst'], ['h/t/r', 'holders/trades/traders'], ['w', 'watch'], ['b', 'alert'], ['1-6', 'timeframe'], ['q', 'quit']], state.theme.name, isCalm(v));
@@ -70,7 +70,7 @@ export function renderTrending(c, state) {
70
70
  if (rows.length === 0)
71
71
  c.textC(box.x, box.y + 2, box.w, all.length ? 'no rows match the filter — f cycles filters, n clears the narrative' : 'nothing trending yet — the keyless venues refresh every 60 s', { fg: t.muted, bg, i: true });
72
72
  else
73
- table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows, selected: sel, scroll, bg });
73
+ table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows, selected: sel, scroll, bg, rowKey: (r) => r.mint });
74
74
  // sub-panels
75
75
  const sy = top + boardH;
76
76
  const news = feed.newsItems(12);
@@ -1,11 +1,13 @@
1
1
  // dexterm — app state consumed by the screens. Screens are pure render(canvas, state) functions;
2
2
  // renderFrame() draws the screen, the toast, then runs the effects post-pass (VISUAL-V2 §3).
3
- import { Canvas, THEMES, applyFx, toast, makeVfx, setSources, marketActivity, candleActivity, isCalm, } from '../../tui/src/index.js';
3
+ import { Canvas, THEMES, applyFx, toast, makeVfx, setSources, marketActivity, candleActivity, isCalm, breakpoint, fmtMcap, } from '../../tui/src/index.js';
4
4
  import { makeFixtures } from "./fixtures.js";
5
5
  import { fromFixture } from "./screener/rows.js";
6
6
  import { makeScreenerState, screenerView } from "./screener/view.js";
7
7
  import { renderScreener } from "./screens/screener.js";
8
8
  import { renderToken } from "./screens/token.js";
9
+ import { agentBrainActive } from "./screens/brain.js";
10
+ import { pnlChipVM, renderPnlChip, CHIP_PRICE_LEVEL } from "./screens/pnl-chip.js";
9
11
  import { renderNewPairs } from "./screens/newpairs.js";
10
12
  import { renderWatchlist } from "./screens/watchlist.js";
11
13
  import { renderAnalyst } from "./screens/analyst.js";
@@ -20,6 +22,7 @@ import { renderNarrative } from "./screens/narrative.js";
20
22
  import { renderPlans } from "./screens/plans.js";
21
23
  import { renderFollows } from "./screens/follows.js";
22
24
  import { renderRules } from "./screens/rules.js";
25
+ import { renderAutotrade } from "./screens/autotrade-screen.js";
23
26
  import { CopyLive } from "./live/copy.js";
24
27
  import { CopyAuto, defaultCopyJournalPath, leaderSoldSince } from '../../data/src/trade/copy/index.js';
25
28
  import { computeBoard, loadDefaultLexicon, recentNarrativeRows } from '../../packs/src/index.js';
@@ -28,7 +31,9 @@ import { openTicket } from "./trade.js";
28
31
  import { computeFill } from '../../data/src/index.js';
29
32
  import { renderScanTicker, scanIndicator } from "./scan/ui.js";
30
33
  import { renderVoiceHud } from "./voice-hud.js";
34
+ import { composeHud, DOCK_ROWS, HUD_MIN_H } from "./screens/agent-hud.js";
31
35
  import { workspaceBadge } from "./custom.js";
36
+ import { chartAxisOf, supplyRatio, priceModeOf, supplyForMint } from "./display.js";
32
37
  /** Map key for the Analyst-tab's own conversation (not tied to a single token). */
33
38
  export const ANALYST_TAB_KEY = '__analyst_tab__';
34
39
  /**
@@ -97,7 +102,7 @@ export function computeNarrativeView(state, window, now = Date.now()) {
97
102
  return { board: null, window, computedAt: now, source: 'none', selected: 0, error: e instanceof Error ? e.message : String(e) };
98
103
  }
99
104
  }
100
- export function newPlansUi() { return { tab: 'plans', selected: 0, notice: null, arming: null, riskModal: false, liveModal: false }; }
105
+ export function newPlansUi() { return { tab: 'plans', selected: 0, notice: null, arming: null, riskModal: false }; }
101
106
  /**
102
107
  * task-1363: how old a board price may be and still fire a plan. The terminal feeds the plan engine from its
103
108
  * screener/watchlist refresh (about every 10 s) and the token screen, so the engine's 3 s default would never
@@ -175,6 +180,7 @@ export const RENDERERS = {
175
180
  plans: renderPlans,
176
181
  follows: renderFollows,
177
182
  rules: renderRules,
183
+ autotrade: renderAutotrade,
178
184
  };
179
185
  export const SCREEN_NAMES = Object.keys(RENDERERS);
180
186
  export function isScreenKey(s) {
@@ -202,6 +208,9 @@ export function isDemoScreen(s) {
202
208
  // task-1360: the follows screen shows the user's own list + the tracker's real state (unavailable when keyless).
203
209
  if (s.screen === 'follows')
204
210
  return false;
211
+ // task-1508: the autotrade screen reads the user's own strategy files and the runner's status file (PAPER badge).
212
+ if (s.screen === 'autotrade')
213
+ return false;
205
214
  // analyst screen: a live Claude session (even mid-run or failed) is real output, not demo data
206
215
  return !s.analyst;
207
216
  }
@@ -232,13 +241,10 @@ export function headerModel(s) {
232
241
  feedLabel = 'jupiter · gecko · boosts · coingecko · rss · keyless';
233
242
  if (s.screen === 'kol')
234
243
  feedLabel = 'kol lists · sqlite';
235
- // task-1355: the plans screen is the signer trust domain; it is paper by default and says so.
236
- // task-1363: once the fire path is LIVE (devnet fast wallet, keypress opt-in) the header says so; anything not
237
- // provably paper renders LIVE (fail-safe).
244
+ // task-1355: the plans screen fires on PAPER only (task-1571: there is no live fire path) and says so.
238
245
  if (s.screen === 'plans') {
239
- const paper = s.plansSvc ? s.plansSvc.mode() === 'paper' : true;
240
- status = paper ? '◈ PAPER · plans journal' : '◉ LIVE · DEVNET · fast wallet';
241
- feedLabel = paper ? 'order plans · standing mandates · hash-chained journal' : 'order plans · signer process · fast wallet · devnet';
246
+ status = '◈ PAPER · plans journal';
247
+ feedLabel = 'order plans · standing mandates · hash-chained journal';
242
248
  }
243
249
  // task-1360: the follows screen reads public history for the user's own list; it never trades.
244
250
  if (s.screen === 'follows')
@@ -246,14 +252,13 @@ export function headerModel(s) {
246
252
  // task-1358: the rules screen evaluates the screener rows locally; rules only alert or PROPOSE.
247
253
  if (s.screen === 'rules')
248
254
  feedLabel = 'rules · config.json · alerts + proposals only · nothing arms';
255
+ // task-1508: autotrade is paper only; the screen edits strategy files and reads recorded history.
256
+ if (s.screen === 'autotrade') {
257
+ status = '◈ PAPER · autotrade';
258
+ feedLabel = 'strategies · recorded history · paper runner status';
259
+ }
249
260
  if (s.screen === 'bubbles')
250
261
  feedLabel = `${s.live?.rpc.provider === 'helius' ? 'helius history · ' : ''}rpc holders · rules · sqlite`;
251
- // task-1328: a LIVE ticket says so in the header, with the SIGNER's network (never a UI flag).
252
- if (s.screen === 'ticket' && s.live?.ticket?.ticket.mode === 'live') {
253
- const net = s.live.ticket.view().live?.signer?.network ?? null;
254
- status = net === 'mainnet' ? '⚠ LIVE · MAINNET · real funds' : net === 'devnet' ? '◉ LIVE · DEVNET' : '◉ LIVE · signer unreachable';
255
- feedLabel = 'signer process · jupiter · phantom';
256
- }
257
262
  // task-1324: while AI scan mode is on the header ALWAYS says so (state + the scan's analyst meter +
258
263
  // any Helius credit warning), on every screen — the running indicator wins over every other status.
259
264
  let scanStatus = null;
@@ -298,6 +303,8 @@ export function makeFixtureState(opts = {}) {
298
303
  larpByMint: new Map(),
299
304
  colorDepth: opts.colorDepth ?? 'truecolor',
300
305
  };
306
+ if (opts.priceMode)
307
+ state.priceMode = opts.priceMode;
301
308
  refreshVfx(state);
302
309
  if (screen === 'ticket' || screen === 'positions')
303
310
  seedDemoTrade(state);
@@ -426,8 +433,57 @@ export function refreshVfx(state) {
426
433
  state.vfx.activity = act.activity;
427
434
  state.vfx.mood = act.mood;
428
435
  }
429
- /** Render one frame of the current screen into a fresh canvas: screen → toast → effects. */
436
+ /**
437
+ * Render one frame of the current screen into a fresh canvas: screen → toast → effects. task-1466: while the
438
+ * terminal follows an agent, the screen renders DOCK_ROWS shorter and the agent HUD's dock takes the bottom rows
439
+ * (so no data is hidden), with its overlays (row glow, cursor, cards, flashes, the kill cut) over the screen.
440
+ */
430
441
  export function renderFrame(state, w, h) {
442
+ const hud = state.agentHud;
443
+ if (!hud || state.screen === 'boot' || h < HUD_MIN_H)
444
+ return renderScreenFrame(state, w, h, true);
445
+ const now = hud.clock();
446
+ const screen = renderScreenFrame(state, w, h - DOCK_ROWS, false);
447
+ const view = hud.story.view(now);
448
+ // task-1473: on the token screen the analyst panel is the agent's brain view (same rule the screen used)
449
+ const brain = state.screen === 'token' && state.aiOpen !== false && breakpoint(w) !== 'compact' && !state.aiFocused && agentBrainActive(view, now);
450
+ return composeHud(new Canvas(w, h, state.theme), screen, view, {
451
+ now, reduced: !!hud.reduced || isCalm(state.vfx), depth: state.colorDepth, dataLabel: hud.dataLabel ?? null, screen: state.screen, brain,
452
+ chip: pnlChipFor(state, now, view),
453
+ mcapScale: priceModeOf(state) === 'mcap' ? (mint) => hudMcapScale(state, mint) : undefined, // task-1472; task-1537: the HUD's text follows the price basis (the charts keep A)
454
+ });
455
+ }
456
+ /** task-1472: the supply ratio for the HUD's position card: the open token's (live, else fixture), else its screener row. */
457
+ function hudMcapScale(state, mint) {
458
+ const v = state.live?.token?.view;
459
+ if (v && v.mint === mint)
460
+ return supplyRatio(v.price, v.mcap);
461
+ const tk = state.token;
462
+ if (tk && tk.mint === mint)
463
+ return supplyRatio(tk.price, tk.mcap);
464
+ const row = state.screener.rows.find((r) => r.mint === mint); // the card also shows over the lists (task-1473)
465
+ return row ? supplyRatio(row.price, row.mcap) : null;
466
+ }
467
+ /**
468
+ * task-1473: the POSITION PnL chip for the app's current auto mode, or null. Following an agent: its open PAPER
469
+ * ticket (the folded story). Else, while AI scan mode runs: the user's own held paper / live position (ownPnl).
470
+ * The same chip feeds the HUD dock, the band row (no dock) and the chart popup. Display only.
471
+ */
472
+ export function pnlChipFor(state, now, view) {
473
+ const hud = state.agentHud;
474
+ // task-1537: entry / mark / TP read as market caps by default (the coin's known supply), else prices
475
+ const mode = priceModeOf(state);
476
+ const lvl = (u, mint) => { const sup = mode === 'mcap' ? supplyForMint(state, mint) : null; return sup !== null ? fmtMcap(u * sup) : CHIP_PRICE_LEVEL(u, mint); };
477
+ if (hud) {
478
+ const m = pnlChipVM(view ?? hud.story.view(now), now, lvl);
479
+ if (m)
480
+ return m;
481
+ }
482
+ if (state.scan?.engine.on && state.ownPnl)
483
+ return state.ownPnl.vm(now, lvl);
484
+ return null;
485
+ }
486
+ function renderScreenFrame(state, w, h, bandChip = false) {
431
487
  const c = new Canvas(w, h, state.theme);
432
488
  c.fill(0, 0, w, h, ' ', { bg: state.theme.bg });
433
489
  c.imageMode = state.images && state.imageProtocol && state.imageProtocol !== 'blocks' ? 'pixels' : 'cells';
@@ -445,11 +501,25 @@ export function renderFrame(state, w, h) {
445
501
  renderVoiceHud(c, state.voice, state.theme);
446
502
  // task-1358: the workspace badge (non-default name, a pending ctrl+w chord, the save-as input) on the band
447
503
  // under the header, right-aligned, drawn after the effects so it is always legible. Nothing on `default`.
504
+ // task-1473 r2: no HUD dock (short terminal, or AI scan mode): the PnL chip rides the band, right-aligned,
505
+ // and the workspace badge moves to its left.
506
+ let bandRight = w - 1;
507
+ if (bandChip && state.screen !== 'boot') {
508
+ const now = state.agentHud?.clock() ?? state.now.getTime();
509
+ const chip = pnlChipFor(state, now);
510
+ if (chip) {
511
+ const x0 = renderPnlChip(c, w - 1, 1, Math.max(36, Math.floor(w * 0.7)), chip, { now, reduced: !!state.agentHud?.reduced || isCalm(state.vfx) });
512
+ if (x0 < w - 1) {
513
+ c.noRain(x0, 1, w - 1 - x0, 1);
514
+ bandRight = x0 - 1;
515
+ }
516
+ }
517
+ }
448
518
  const badge = state.screen !== 'boot' ? workspaceBadge(state.custom, state.now.getTime()) : null;
449
- if (badge) {
519
+ if (badge && bandRight > 4) {
450
520
  const t = state.theme;
451
- const text = badge.text.slice(0, Math.max(0, w - 2));
452
- const x = Math.max(0, w - text.length - 1);
521
+ const text = badge.text.slice(0, Math.max(0, bandRight - 1));
522
+ const x = Math.max(0, bandRight - text.length);
453
523
  c.noRain(x, 1, text.length, 1);
454
524
  c.text(x, 1, text, badge.kind === 'input' ? { fg: t.text, bg: t.accentSoft, b: true } : badge.kind === 'chord' ? { fg: t.warn, bg: t.warnSoft, b: true } : { fg: t.accent, bg: t.headBg, b: true });
455
525
  }