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
@@ -0,0 +1,634 @@
1
+ // dexterm — the ANALYST panel's BRAIN VIEW (task-1473). While the app runs in an auto mode, i.e. it follows an agent
2
+ // driving dexterm (`--spectate-mcp`, the task-1466 HUD) or AI scan mode is on, the analyst panel on the token screen
3
+ // shows what the auto mode is thinking instead of the "press a for a dossier" help. Top to bottom:
4
+ // 1. REASONING: the agent's thoughts (thoughts feed) typing in with a clock and the phase tag, newest at the bottom,
5
+ // older lines dimmed. Without a thoughts feed, dexterm's own narration of each call, labelled as such. In scan
6
+ // mode: the scan's own log plus the scan analyst's answer.
7
+ // 2. VETTING $SYM: the checklist for the coin in focus, each box with the number the app computed and pass / warn /
8
+ // fail / pending (pending = the number is not in yet). A ◆ marks the checks the agent itself ran.
9
+ // 3. DATA: mcap, price, liquidity, 5m / 1h, buy/sell flow, holders, and a candle trace (task-1483: with a B / S cell
10
+ // on the candle of every paper / live fill of the coin, from the ledgers; the rest is said, never moved).
11
+ // 4. PLAN · POSITION (only while a PAPER ticket / position exists): entry, size, TP / stop, mark PnL, peak, time held,
12
+ // what it is waiting for.
13
+ // 5. DECISIONS: the session log (passed X because…, rejected Y, entered Z) and the scoreboard (PAPER, simulated).
14
+ // Every number comes from data the app already holds (the story folded from spectator records, the token view on
15
+ // screen, the scan engine). Nothing here fetches, and nothing here can arm, sign, size, confirm or raise a cap: the
16
+ // panel has no input. Agent text arrives scrubbed and clipped (story.ribbonText) with addresses shortened.
17
+ // Smaller panels drop blocks in priority order (decisions, then data, then the vetting list folds to two columns);
18
+ // calm mode shows the same information with no typing; NO_COLOR keeps meaning in glyphs (✓ ! ✗ · ◆ ❯ ›).
19
+ import { mix, clock, decryptText, fmtUsd, fmtPrice, fmtMcap, fmtNum, wrap, chartMarkers, markerLetter, MARKER_PERIOD_MS } from '../../../tui/src/index.js';
20
+ import { ribbonText, ribbonLines, fmtPx } from '../../../agents/src/index.js';
21
+ // ---------------------------------------------------------------------------------------------------------------
22
+ // formatting (deterministic, no locale)
23
+ const sgnPct = (p, d = 1) => `${p >= 0 ? '+' : '−'}${Math.abs(p).toFixed(d)}%`;
24
+ const sgnRatio = (r, d = 1) => sgnPct(r * 100, d);
25
+ const sgnSol = (s) => `${s >= 0 ? '+' : '−'}${Math.abs(s).toFixed(4)} SOL`;
26
+ const sgnUsd = (n) => `${n >= 0 ? '+' : '−'}$${Math.abs(n).toFixed(2)}`;
27
+ export function fmtDur(ms) {
28
+ const s = Math.max(0, Math.round(ms / 1000));
29
+ if (s < 60)
30
+ return `${s}s`;
31
+ const m = Math.floor(s / 60);
32
+ return m < 60 ? `${m}m${String(s % 60).padStart(2, '0')}s` : `${Math.floor(m / 60)}h${String(m % 60).padStart(2, '0')}m`;
33
+ }
34
+ const clip = (s, n) => (s.length > n ? s.slice(0, Math.max(1, n - 1)) + '…' : s);
35
+ export const PHASE_TAG = {
36
+ idle: 'IDLE', scanning: 'SCANNING', vetting: 'VETTING', sizing: 'SIZING', executing: 'EXECUTING', holding: 'HOLDING', exiting: 'EXITING',
37
+ };
38
+ // ---------------------------------------------------------------------------------------------------------------
39
+ // auto-mode detection
40
+ /** How long after the agent's last call the brain view stays up (a holding agent polls far more often). */
41
+ export const AGENT_QUIET_MS = 180_000;
42
+ /**
43
+ * Whether the agent-follow brain view applies: following, not killed, and the agent has acted recently (or holds a
44
+ * position). Before the first call, after a kill, or once the agent has gone quiet, the normal analyst panel returns.
45
+ */
46
+ export function agentBrainActive(v, now) {
47
+ if (!v || v.control === 'killed')
48
+ return false;
49
+ if (v.arc && (v.arc.stage === 'open' || v.arc.stage === 'ticket' || v.arc.stage === 'sized'))
50
+ return true;
51
+ return v.lastCallAt !== null && now - v.lastCallAt < AGENT_QUIET_MS;
52
+ }
53
+ // ---------------------------------------------------------------------------------------------------------------
54
+ // facts adapters (the numbers the token screen already holds)
55
+ const distinct = (xs) => new Set(xs).size;
56
+ export function factsFromFixture(tk, larpVerdict = null) {
57
+ const buys = tk.trades.filter((x) => x.side === 'buy').length;
58
+ return {
59
+ mint: tk.mint, symbol: tk.symbol, price: tk.price, mcap: tk.mcap, liq: tk.liq, chg5m: tk.chg.m5, chg1h: tk.chg.h1,
60
+ buys: tk.trades.length ? buys : null, sells: tk.trades.length ? tk.trades.length - buys : null,
61
+ wallets: tk.trades.length ? distinct(tk.trades.map((x) => x.addr)) : null, holders: tk.holders, top10: tk.top10,
62
+ mintAuth: tk.mintAuth, freezeAuth: tk.freezeAuth, lpLocked: tk.lpLocked,
63
+ dev: [`${tk.devHistory.launches} launches · ${tk.devHistory.rugged} rugs`, tk.devHistory.rugged ? 'warn' : 'ok'],
64
+ rugScore: tk.rug.score, larpVerdict, closes: tk.candles.map((k) => k.c),
65
+ };
66
+ }
67
+ /** From the live token view. `extra` carries what the screen derived this frame (top-10 ex-pool, the dev row). */
68
+ export function factsFromView(v, extra = {}) {
69
+ const tr = v.trades;
70
+ const buys = tr.filter((x) => x.side === 'buy').length;
71
+ return {
72
+ mint: v.mint, symbol: v.symbol, price: extra.price ?? v.price, mcap: v.mcap, liq: v.liq, chg5m: v.chg.m5, chg1h: v.chg.h1,
73
+ buys: tr.length ? buys : null, sells: tr.length ? tr.length - buys : null, wallets: tr.length ? distinct(tr.map((x) => x.wallet)) : null,
74
+ holders: v.holders, top10: extra.top10 ?? v.top10, mintAuth: v.mintAuth, freezeAuth: v.freezeAuth, lpLocked: v.lpLocked,
75
+ dev: extra.dev ?? (v.devMints === null ? null : [`${v.devMints} mints by dev (jup)`, v.devMints > 3 ? 'warn' : 'ok']),
76
+ rugScore: v.rug?.score ?? null, larpVerdict: extra.larpVerdict ?? null, closes: v.candles.map((k) => k.c),
77
+ closeTimes: v.candles.map((k) => k.t), periodMs: MARKER_PERIOD_MS[v.interval], fills: extra.fills ?? undefined,
78
+ };
79
+ }
80
+ /** task-1483: the trace's B / S cells for the last `width` closes (the same model as the charts; display only). */
81
+ export function traceMarks(f, width = 240) {
82
+ if (!f || !f.fills?.length || !f.closeTimes || f.closeTimes.length !== f.closes.length || !f.periodMs)
83
+ return null;
84
+ const times = f.closeTimes.slice(-width);
85
+ const set = chartMarkers(f.fills, { periodMs: f.periodMs, barTimes: times });
86
+ const off0 = f.closes.length - times.length;
87
+ return { cells: set.markers.map((m) => ({ index: off0 + m.index, side: m.side, source: m.source, count: m.count })), off: set.before + set.after + set.gap };
88
+ }
89
+ // ---------------------------------------------------------------------------------------------------------------
90
+ // 1) reasoning stream
91
+ /** Agent thoughts and dexterm narration, newest last (≤ max). */
92
+ export function agentStream(v, tz, max = 24) {
93
+ const hasAgent = v.thoughts.some((x) => x.source === 'agent');
94
+ const lines = v.thoughts.slice(-max).map((x) => ({
95
+ t: x.t, clock: clock(new Date(x.t), tz), tag: PHASE_TAG[x.phase ?? 'idle'], text: x.text,
96
+ kind: x.source === 'agent' ? 'agent' : 'narration', demo: !!x.demo,
97
+ }));
98
+ const demo = lines.some((l) => l.demo);
99
+ return { lines, label: hasAgent ? 'agent thoughts + dexterm narration' : 'dexterm narration · no thoughts feed', demo };
100
+ }
101
+ const SCAN_TAG = { start: 'SCANNING', lock: 'LOCKED', refresh: 'READING', release: 'SCANNING', stop: 'OFF', cancel: 'PAUSED', blocked: 'BLOCKED' };
102
+ /** The scan's log lines plus the analyst's current answer (tool lines, then its sentences), newest last. */
103
+ export function scanStream(log, answer, tz, max = 24) {
104
+ const out = log.map((e) => ({ t: e.t, clock: clock(new Date(e.t), tz), tag: SCAN_TAG[e.kind], text: ribbonText(`${e.symbol ? `$${e.symbol} · ` : ''}${e.text}`, 140), kind: 'scan', demo: false }));
105
+ if (answer) {
106
+ const t0 = answer.startedAt;
107
+ answer.toolLines.slice(-4).forEach((tl, i) => out.push({ t: t0 + i, clock: clock(new Date(t0), tz), tag: 'READING', text: ribbonText(`↳ ${tl}`, 120), kind: 'narration', demo: false }));
108
+ const tEnd = answer.doneAt ?? t0 + 5;
109
+ for (const s of ribbonLines(answer.text, 140, 6))
110
+ out.push({ t: tEnd, clock: clock(new Date(tEnd), tz), tag: answer.status === 'done' ? 'READ' : 'READING', text: s, kind: 'agent', demo: false });
111
+ if (answer.verdict)
112
+ out.push({ t: tEnd + 1, clock: clock(new Date(tEnd), tz), tag: 'VERDICT', text: ribbonText(`verdict ${answer.verdict}`, 60), kind: 'agent', demo: false });
113
+ }
114
+ out.sort((a, b) => a.t - b.t);
115
+ return out.slice(-max);
116
+ }
117
+ // ---------------------------------------------------------------------------------------------------------------
118
+ // 2) vetting checklist
119
+ /** The app's liquidity floor (the AI scan's minimum liquidity, DEFAULT_SCAN_CONFIG.minLiqUsd). */
120
+ export const LIQ_FLOOR_USD = 10_000;
121
+ const st = (l) => (l === 'ok' ? 'pass' : l === 'warn' ? 'warn' : l === 'bad' ? 'fail' : 'pending');
122
+ /**
123
+ * The checklist for the coin in focus. Values and thresholds are the token screen's (ON-CHAIN panel); a box is
124
+ * `pending` until its number exists. `focus` adds what the agent's own checks returned (verdict, rug heuristic,
125
+ * LARP flags) and which checks it ran (◆).
126
+ */
127
+ export function vetRows(f, focus, floorUsd = LIQ_FLOOR_USD) {
128
+ const ran = new Set(focus && focus.mint === f.mint ? focus.checks : []);
129
+ const fc = focus && focus.mint === f.mint ? focus : null;
130
+ const rows = [];
131
+ // momentum
132
+ {
133
+ const a = f.chg5m, b = f.chg1h;
134
+ const lvl = a === null && b === null ? 'na' : (a ?? 0) > 0 && (b ?? 0) > 0 ? 'ok' : (a ?? 0) < 0 && (b ?? 0) < 0 ? 'bad' : 'warn';
135
+ rows.push({ key: 'momentum', label: 'momentum', value: a === null && b === null ? '—' : `5m ${a === null ? '—' : sgnPct(a)} · 1h ${b === null ? '—' : sgnPct(b)}`, state: st(lvl), byAgent: ran.has('chart') || ran.has('price') });
136
+ }
137
+ // liquidity vs floor
138
+ rows.push({ key: 'liquidity', label: 'liquidity', value: f.liq === null ? '—' : `${fmtUsd(f.liq, { digits: 1 })} ${f.liq >= floorUsd ? '≥' : '<'} ${fmtUsd(floorUsd, { digits: 0 })} floor`, state: f.liq === null ? 'pending' : f.liq >= floorUsd ? 'pass' : 'fail', byAgent: ran.has('price') });
139
+ // tape
140
+ {
141
+ const b = f.buys, s = f.sells;
142
+ const lvl = b === null || s === null ? 'na' : b >= s ? 'ok' : s > b * 2 ? 'bad' : 'warn';
143
+ rows.push({ key: 'tape', label: 'tape b/s', value: b === null || s === null ? '—' : `${b} buys / ${s} sells${f.wallets !== null ? ` · ${f.wallets} wallets` : ''}`, state: st(lvl), byAgent: ran.has('tape') });
144
+ }
145
+ // holders
146
+ rows.push({ key: 'holders', label: 'top-10 hold', value: f.top10 === null ? '—' : `${f.top10.toFixed(1)}%`, state: f.top10 === null ? 'pending' : f.top10 < 25 ? 'pass' : f.top10 < 35 ? 'warn' : 'fail', byAgent: ran.has('holders') });
147
+ // authorities
148
+ {
149
+ const m = f.mintAuth, z = f.freezeAuth;
150
+ const lvl = m === null && z === null ? 'na' : m || z ? 'bad' : m === null || z === null ? 'warn' : 'ok';
151
+ rows.push({ key: 'auth', label: 'mint/freeze', value: m === null && z === null ? '—' : `mint ${m === null ? '—' : m ? 'ENABLED' : 'revoked'} · freeze ${z === null ? '—' : z ? 'ENABLED' : 'revoked'}`, state: st(lvl), byAgent: ran.has('rug') });
152
+ }
153
+ // LP lock
154
+ rows.push({ key: 'lp', label: 'LP lock', value: f.lpLocked === null ? '—' : `${Math.round(f.lpLocked)}% locked/burned`, state: f.lpLocked === null ? 'pending' : f.lpLocked >= 90 ? 'pass' : 'fail', byAgent: ran.has('rug') });
155
+ // dev history
156
+ rows.push({ key: 'dev', label: 'dev history', value: f.dev ? f.dev[0] : '—', state: f.dev ? st(f.dev[1]) : 'pending', byAgent: ran.has('dev') });
157
+ // LARP flags: the agent's larp_check evidence first, else an existing detector verdict
158
+ if (fc?.larp) {
159
+ const n = fc.larp.flags;
160
+ rows.push({ key: 'larp', label: 'LARP flags', value: `${n} flag${n === 1 ? '' : 's'}${fc.larp.items[0] ? ` · ${fc.larp.items[0]}` : ''}`, state: n === 0 ? 'pass' : n <= 2 ? 'warn' : 'fail', byAgent: true });
161
+ }
162
+ else {
163
+ const v = f.larpVerdict;
164
+ const lvl = !v ? 'na' : v === 'LARP' ? 'bad' : v === 'REAL_PRODUCT' || v === 'EARLY_BUT_REAL' ? 'ok' : 'warn';
165
+ rows.push({ key: 'larp', label: 'LARP flags', value: v ? `detector: ${v.replace(/_/g, ' ').toLowerCase()}` : '—', state: st(lvl), byAgent: ran.has('larp') });
166
+ }
167
+ // rug heuristic (the analyze_token pre-registered heuristic) or the RugCheck score
168
+ if (fc?.verdict?.rug != null) {
169
+ const r = fc.verdict.rug;
170
+ rows.push({ key: 'rug', label: 'rug heuristic', value: `${(r * 100).toFixed(0)}%`, state: r < 0.3 ? 'pass' : r < 0.6 ? 'warn' : 'fail', byAgent: true });
171
+ }
172
+ else {
173
+ const r = f.rugScore;
174
+ rows.push({ key: 'rug', label: 'rug score', value: r === null ? '—' : `RugCheck ${Math.round(r)}/100`, state: r === null ? 'pending' : r < 30 ? 'pass' : r < 60 ? 'warn' : 'fail', byAgent: ran.has('rug') });
175
+ }
176
+ // verdict
177
+ {
178
+ const v = fc?.verdict?.label ?? null;
179
+ rows.push({ key: 'verdict', label: 'verdict', value: v ?? (fc ? 'not yet' : '—'), state: !v ? 'pending' : /WATCH|BUY|ENTER/i.test(v) ? 'pass' : /AVOID|RUG|SKIP/i.test(v) ? 'fail' : 'warn', byAgent: !!v });
180
+ }
181
+ return rows;
182
+ }
183
+ // ---------------------------------------------------------------------------------------------------------------
184
+ // 3) data strip
185
+ export function dataCells(f) {
186
+ const dir = (n) => (n === null || n === 0 ? 0 : n > 0 ? 1 : -1);
187
+ const flow = f.buys !== null && f.sells !== null ? `${f.buys}/${f.sells}` : '—';
188
+ return [
189
+ { k: 'mcap', v: f.mcap === null ? '—' : fmtUsd(f.mcap, { digits: 1 }) },
190
+ { k: 'price', v: f.price === null ? '—' : fmtPrice(f.price) },
191
+ { k: 'liq', v: f.liq === null ? '—' : fmtUsd(f.liq, { digits: 1 }) },
192
+ { k: '5m', v: f.chg5m === null ? '—' : sgnPct(f.chg5m), dir: dir(f.chg5m) },
193
+ { k: '1h', v: f.chg1h === null ? '—' : sgnPct(f.chg1h), dir: dir(f.chg1h) },
194
+ { k: 'b/s', v: flow, dir: f.buys !== null && f.sells !== null ? dir(f.buys - f.sells) : 0 },
195
+ { k: 'holders', v: f.holders === null ? '—' : fmtNum(f.holders) },
196
+ ];
197
+ }
198
+ // ---------------------------------------------------------------------------------------------------------------
199
+ // 4) plan + position
200
+ const TP_RE = /\b(?:TP ?1|TP|take[- ]profit|target)\b[^.;!?]{0,24}?([+]?\d{1,4}(?:\.\d+)?%|\$\d+(?:[.,]\d+)*|\d+(?:\.\d+)?x)/i;
201
+ const STOP_RE = /\b(?:stop(?:[- ]loss)?|SL)\b[^.;!?]{0,20}?([−-]\d{1,3}(?:\.\d+)?%|\$\d+(?:[.,]\d+)*)/i;
202
+ const NO_STOP_RE = /\bno (?:hard )?stop\b/i;
203
+ /** The take-profit / stop the agent SAID (its words, newest first): display text only, never an order. */
204
+ export function planFromThoughts(thoughts, since) {
205
+ let tp = null, stop = null;
206
+ for (let i = thoughts.length - 1; i >= 0; i--) {
207
+ const x = thoughts[i];
208
+ if (x.source !== 'agent' || x.t < since - 60_000)
209
+ continue;
210
+ if (!tp) {
211
+ const m = TP_RE.exec(x.text);
212
+ if (m)
213
+ tp = m[1].replace('-', '−');
214
+ }
215
+ if (!stop) {
216
+ if (NO_STOP_RE.test(x.text))
217
+ stop = 'no stop';
218
+ else {
219
+ const m = STOP_RE.exec(x.text);
220
+ if (m)
221
+ stop = m[1].replace('-', '−');
222
+ }
223
+ }
224
+ if (tp && stop)
225
+ break;
226
+ }
227
+ return { tp, stop };
228
+ }
229
+ /** The PLAN / POSITION block for the agent's PAPER arc (null when there is no ticket or position). */
230
+ /** task-1537: the coin's supply from its facts (mcap ÷ price at the same tick), null when either is missing. */
231
+ function capScale(f) {
232
+ return f.mcap !== null && f.price !== null && f.price > 0 && f.mcap > 0 ? { mint: f.mint, supply: f.mcap / f.price } : null;
233
+ }
234
+ export function planVM(v, now, cap = null) {
235
+ const a = v.arc;
236
+ if (!a || a.stage === 'closed' || a.stage === 'refused')
237
+ return null;
238
+ const said = planFromThoughts(v.thoughts, a.openedAt);
239
+ const open = a.stage === 'open';
240
+ // task-1537: entry / mark as market caps when the card's coin has a known supply, else prices
241
+ const lvl = (u) => (cap && cap.mint === a.mint ? fmtMcap(u * cap.supply) : `$${fmtPx(u)}`);
242
+ const pnlUsd = open && a.pnlPct !== null && a.sizeUsd !== null ? a.sizeUsd * a.pnlPct : null;
243
+ const dd = open && a.pnlPct !== null && a.peakPct !== null ? (1 + a.pnlPct) / (1 + a.peakPct) - 1 : null;
244
+ const tp = said.tp ? `TP ${said.tp} (agent's plan)` : 'TP agent-managed';
245
+ const stop = said.stop === 'no stop' || !said.stop ? 'no stop' : `stop ${said.stop} (agent's plan)`;
246
+ const waiting = !open
247
+ ? (a.sizeUsd !== null ? 'the agent to confirm the sized ticket' : 'the agent to size the ticket')
248
+ : said.tp ? `TP ${said.tp} · agent-managed exit, no resting order on PAPER` : 'the agent\'s exit call · no resting orders on PAPER';
249
+ return {
250
+ symbol: a.symbol, stage: a.stage,
251
+ entry: a.entryUsd !== null ? lvl(a.entryUsd) : '—',
252
+ size: a.sizeUsd !== null ? `$${a.sizeUsd.toFixed(2)}${a.spentSol > 0 ? ` (${a.spentSol.toFixed(4)} SOL)` : ''}` : a.spentSol > 0 ? `${a.spentSol.toFixed(4)} SOL` : 'sizing…',
253
+ tp, stop,
254
+ pnlPct: open ? a.pnlPct : null, pnlUsd, peakPct: open ? a.peakPct : null, ddPct: dd,
255
+ heldMs: open && a.filledAt !== null ? now - a.filledAt : null,
256
+ mark: a.mark ? lvl(a.mark.priceUsd) : '—',
257
+ waiting, trace: a.trace.map((s) => s.pct),
258
+ refused: open && a.refusal && v.beats.refusedAt !== null && now - v.beats.refusedAt < 3500 ? a.refusal : null,
259
+ };
260
+ }
261
+ // ---------------------------------------------------------------------------------------------------------------
262
+ // 5) decision log + scoreboard
263
+ const DEC_GLYPH = { pass: ['·', 'na'], reject: ['✗', 'bad'], enter: ['▲', 'ok'], exit: ['▼', 'warn'], refused: ['⊘', 'bad'] };
264
+ const DEC_VERB = { pass: 'passed', reject: 'rejected', enter: '', exit: '', refused: '' };
265
+ export function decisionLog(v, tz) {
266
+ return v.decisions.map((d) => {
267
+ const [glyph, level] = DEC_GLYPH[d.kind];
268
+ const verb = DEC_VERB[d.kind];
269
+ return { t: d.t, clock: clock(new Date(d.t), tz).slice(0, 5), glyph, level, symbol: d.symbol, text: verb ? `${verb} · ${d.text}` : d.text, said: d.said };
270
+ });
271
+ }
272
+ export function scoreLine(s) {
273
+ return `PAPER simulated · vetted ${s.attempts} · entries ${s.entries} · closed ${s.closed}${s.closed ? ` (${s.wins}W)` : ''} · ${sgnSol(s.realizedSol)}`;
274
+ }
275
+ export function scanLogLines(log, tz) {
276
+ const lv = { start: ['◉', 'na'], lock: ['▲', 'ok'], refresh: ['↻', 'na'], release: ['·', 'na'], stop: ['■', 'warn'], cancel: ['❚', 'warn'], blocked: ['⊘', 'bad'] };
277
+ return log.map((e) => ({ t: e.t, clock: clock(new Date(e.t), tz).slice(0, 5), glyph: lv[e.kind][0], level: lv[e.kind][1], symbol: e.symbol, text: e.text, said: null }));
278
+ }
279
+ // ---------------------------------------------------------------------------------------------------------------
280
+ // the whole model
281
+ export function agentBrainModel(v, facts, now, tz, mcap = false) {
282
+ const s = agentStream(v, tz);
283
+ const status = v.control === 'paused' ? '❚❚ paused by you · its next call is held' : v.control === 'takeover' ? 'you are driving · the agent is frozen' : null;
284
+ return {
285
+ mode: 'agent', title: `✦ BRAIN · ${v.agent}`, streamLabel: s.label, demo: s.demo, stream: s.lines, status,
286
+ coin: facts ? { symbol: facts.symbol, mint: facts.mint } : v.focus ? { symbol: v.focus.symbol, mint: v.focus.mint } : null,
287
+ vet: facts ? vetRows(facts, v.focus) : [],
288
+ data: facts ? dataCells(facts) : [], closes: facts?.closes ?? [], marks: traceMarks(facts),
289
+ plan: planVM(v, now, mcap && facts ? capScale(facts) : null), log: decisionLog(v, tz), score: scoreLine(v.score),
290
+ };
291
+ }
292
+ export function scanBrainModel(s, facts, tz) {
293
+ const locks = s.log.filter((e) => e.kind === 'lock').length;
294
+ const reads = s.log.filter((e) => e.kind === 'lock' || e.kind === 'refresh').length;
295
+ return {
296
+ mode: 'scan', title: '✦ BRAIN · AI scan', streamLabel: 'scan log + the scan analyst (observe-only)', demo: false,
297
+ stream: scanStream(s.log, s.answer, tz), status: `◉ ${s.mode}${s.lock ? ` $${s.lock.symbol} · score ${s.lock.score} · read ${s.lock.reads}` : ''} · ${s.scanned} coins${s.note ? ` · ${s.note}` : ''}`,
298
+ coin: facts ? { symbol: facts.symbol, mint: facts.mint } : null,
299
+ vet: facts ? vetRows(facts, null, s.floorUsd) : [], data: facts ? dataCells(facts) : [], closes: facts?.closes ?? [], marks: traceMarks(facts),
300
+ plan: null, log: scanLogLines(s.log, tz), score: `locks ${locks} · reads ${reads} · ${s.usage || 'ai cap'} · observe-only · never trades`,
301
+ };
302
+ }
303
+ /** Rows each block wants. */
304
+ const VET_FULL = (n) => 1 + n;
305
+ const VET_COMPACT = (n) => 1 + Math.ceil(n / 2);
306
+ const DATA_ROWS = 3;
307
+ const PLAN_ROWS = 6;
308
+ const LOG_ROWS = (n) => 2 + Math.min(6, Math.max(1, n));
309
+ const STREAM_MIN = 4;
310
+ /**
311
+ * Fit the blocks into `h` rows (inside the border) of a panel `w` wide. Priority to keep: plan > stream > vetting >
312
+ * data > decisions; the vetting list folds to two columns before it goes. The stream takes what is left. Returned in
313
+ * draw order (top to bottom: stream, vetting, data, plan, decisions).
314
+ */
315
+ export function layoutBlocks(m, w, h) {
316
+ const wants = [];
317
+ const nVet = m.vet.length;
318
+ let vet = nVet ? { id: 'vet', rows: VET_FULL(nVet) } : null;
319
+ let data = m.data.length ? { id: 'data', rows: DATA_ROWS } : null;
320
+ let log = { id: 'log', rows: LOG_ROWS(m.log.length) };
321
+ const plan = m.plan ? { id: 'plan', rows: PLAN_ROWS } : null;
322
+ const used = () => (vet?.rows ?? 0) + (data?.rows ?? 0) + (log?.rows ?? 0) + (plan?.rows ?? 0);
323
+ const room = () => h - used();
324
+ // the stream wants a third of the panel when it can have it
325
+ const want = Math.max(STREAM_MIN, Math.floor(h / 3));
326
+ if (room() < want && log)
327
+ log = log.rows > 4 && room() + (log.rows - 4) >= want ? { id: 'log', rows: 4 } : null;
328
+ if (room() < want && data)
329
+ data = null;
330
+ if (room() < want && vet && w >= 44 && nVet > 2)
331
+ vet = { id: 'vet', rows: VET_COMPACT(nVet), compact: true };
332
+ if (room() < STREAM_MIN && vet)
333
+ vet = null;
334
+ if (room() < STREAM_MIN && log)
335
+ log = null;
336
+ const streamRows = Math.max(0, room());
337
+ wants.push({ id: 'stream', rows: streamRows });
338
+ if (vet)
339
+ wants.push(vet);
340
+ if (data)
341
+ wants.push(data);
342
+ if (plan)
343
+ wants.push(plan);
344
+ if (log)
345
+ wants.push(log);
346
+ return wants.filter((b) => b.rows > 0);
347
+ }
348
+ const STATE_GLYPH = { pass: '✓', warn: '!', fail: '✗', pending: '·' };
349
+ function levelColor(c, l) {
350
+ const t = c.theme;
351
+ return l === 'ok' || l === 'pass' ? t.up : l === 'warn' ? t.warn : l === 'bad' || l === 'fail' ? t.down : t.dim;
352
+ }
353
+ function section(c, x, y, w, title, right, bg, col) {
354
+ const t = c.theme;
355
+ c.hline(x, y, w, { fg: mix(bg, t.border, 0.9), bg, soft: true });
356
+ const tx = c.text(x + 1, y, ` ${title} `, { fg: col, bg, b: true });
357
+ if (right) {
358
+ const r = clip(right, Math.max(0, x + w - tx - 3));
359
+ if (r)
360
+ c.text(x + w - r.length - 2, y, ` ${r}`, { fg: t.dim, bg });
361
+ }
362
+ }
363
+ /** Draw the brain view into the analyst panel rectangle. */
364
+ export function renderBrainPanel(c, x, y, w, h, m, o) {
365
+ const t = c.theme;
366
+ const bc = mix(t.border, t.ai, 0.55);
367
+ const cap = m.mode === 'agent' ? 'display only · cannot trade' : 'observe-only · never trades';
368
+ const box = c.box(x, y, w, h, { title: m.title, titleFg: t.ai, caption: cap, captionFg: t.dim, border: bc });
369
+ const bg = box.bg;
370
+ const iw = box.w;
371
+ c.noRain(box.x, box.y, box.w, box.h);
372
+ const blocks = layoutBlocks(m, iw, box.h);
373
+ let cy = box.y;
374
+ for (const b of blocks) {
375
+ const y0 = cy;
376
+ if (b.id === 'stream')
377
+ drawStream(c, box.x, y0, iw, b.rows, m, o, bg);
378
+ else if (b.id === 'vet')
379
+ drawVet(c, box.x, y0, iw, b.rows, m, b.compact === true, bg);
380
+ else if (b.id === 'data')
381
+ drawData(c, box.x, y0, iw, m, bg);
382
+ else if (b.id === 'plan' && m.plan)
383
+ drawPlan(c, box.x, y0, iw, m.plan, o, bg);
384
+ else if (b.id === 'log')
385
+ drawLog(c, box.x, y0, iw, b.rows, m, bg);
386
+ cy += b.rows;
387
+ }
388
+ }
389
+ function drawStream(c, x, y, w, rows, m, o, bg) {
390
+ const t = c.theme;
391
+ if (rows <= 0)
392
+ return;
393
+ // header: what is talking, plus the coral demo-text tag when a stand-in feed wrote the lines
394
+ let hx = c.text(x + 1, y, 'REASONING', { fg: t.ai, bg, b: true });
395
+ hx = c.text(hx + 1, y, `· ${m.streamLabel}`, { fg: t.dim, bg }, x + w - hx - 2);
396
+ if (m.demo) {
397
+ const tag = ' ✦ claude lines: demo text ';
398
+ c.text(x + w - tag.length - 1, y, tag, { fg: t.bg, bg: t.ai, b: true });
399
+ }
400
+ const statusRows = m.status ? 1 : 0;
401
+ const area = rows - 1 - statusRows;
402
+ const bottom = y + 1 + area; // exclusive
403
+ if (m.status)
404
+ c.text(x + 1, bottom, clip(m.status, w - 2), { fg: t.warn, bg, b: true });
405
+ if (area <= 0)
406
+ return;
407
+ if (!m.stream.length) {
408
+ c.text(x + 2, y + 1, m.mode === 'agent' ? '› waiting for the agent\'s first move' : '› watching… nothing has locked yet', { fg: t.dim, bg, i: true }, w - 3);
409
+ return;
410
+ }
411
+ // "HH:MM:SS " + "EXECUTING " + a space; a narrow panel drops the clock and keeps the phase tag
412
+ const narrow = w < 56;
413
+ const gutter = narrow ? 11 : 9 + 11;
414
+ const tw = Math.max(8, w - gutter - 3);
415
+ const vrows = [];
416
+ for (let i = m.stream.length - 1, rank = 0; i >= 0 && vrows.length < area; i--, rank++) {
417
+ const ln = m.stream[i];
418
+ const lead = ln.kind === 'agent' ? '❯ ' : ln.kind === 'scan' ? '◉ ' : '› ';
419
+ const body = lead + ln.text;
420
+ const parts = rank === 0 ? wrap(body, tw).slice(0, 3) : [clip(body, tw)];
421
+ if (rank === 0 && wrap(body, tw).length > 3)
422
+ parts[2] = clip(parts[2] + ' …', tw);
423
+ for (let k = parts.length - 1; k >= 0 && vrows.length < area; k--)
424
+ vrows.push({ line: ln, text: parts[k], first: k === 0, newest: rank === 0, rank });
425
+ }
426
+ vrows.reverse();
427
+ let yy = bottom - vrows.length;
428
+ // typing budget for the newest line (calm mode: shown at once)
429
+ const newest = m.stream[m.stream.length - 1];
430
+ let budget = o.calm ? Infinity : Math.max(0, Math.floor((o.now - newest.t) / 14));
431
+ for (const r of vrows) {
432
+ const fade = r.newest ? 0 : Math.min(0.62, 0.12 + r.rank * 0.07);
433
+ const base = r.line.kind === 'agent' ? mix(t.text, t.ai, 0.3) : r.line.kind === 'scan' ? t.text : t.muted;
434
+ const fg = mix(base, bg, fade);
435
+ if (r.first) {
436
+ if (!narrow)
437
+ c.text(x + 1, yy, r.line.clock, { fg: mix(t.dim, bg, fade * 0.6), bg });
438
+ c.text(x + (narrow ? 1 : 10), yy, r.line.tag.slice(0, 9), { fg: mix(tagColor(c, r.line.tag), bg, fade), bg, b: r.newest });
439
+ }
440
+ const st = { fg, bg, i: r.line.kind === 'narration' };
441
+ if (r.newest && budget < r.text.length) {
442
+ const end = decryptText(c, x + gutter, yy, r.text, Math.max(0, budget), Math.floor(o.now / 33), st, tw);
443
+ if (budget >= 0)
444
+ c.put(Math.min(end, x + w - 2), yy, '▌', { fg: t.ai, bg, soft: true });
445
+ budget = -1;
446
+ }
447
+ else if (r.newest && budget < 0) {
448
+ // the rest of a line still typing: nothing yet
449
+ }
450
+ else {
451
+ c.text(x + gutter, yy, r.text, st, tw);
452
+ if (r.newest)
453
+ budget -= r.text.length + 1;
454
+ }
455
+ yy += 1;
456
+ }
457
+ }
458
+ function tagColor(c, tag) {
459
+ const t = c.theme;
460
+ switch (tag) {
461
+ case 'SCANNING': return t.accent;
462
+ case 'VETTING':
463
+ case 'READING':
464
+ case 'READ': return t.borderFocus;
465
+ case 'SIZING': return t.meteora;
466
+ case 'EXECUTING':
467
+ case 'LOCKED': return t.up;
468
+ case 'HOLDING': return t.magenta;
469
+ case 'EXITING':
470
+ case 'VERDICT': return t.ai;
471
+ case 'BLOCKED':
472
+ case 'OFF': return t.down;
473
+ case 'PAUSED': return t.warn;
474
+ default: return t.dim;
475
+ }
476
+ }
477
+ function drawVet(c, x, y, w, rows, m, compact, bg) {
478
+ const t = c.theme;
479
+ const done = m.vet.filter((r) => r.state !== 'pending').length;
480
+ section(c, x, y, w, `VETTING${m.coin ? ` $${m.coin.symbol}` : ''}`, `${done}/${m.vet.length} evaluated${m.mode === 'agent' ? ' · ◆ agent ran it' : ''}`, bg, t.borderFocus);
481
+ const rowsAvail = rows - 1;
482
+ if (!compact) {
483
+ m.vet.slice(0, rowsAvail).forEach((r, i) => {
484
+ const yy = y + 1 + i;
485
+ const col = levelColor(c, r.state);
486
+ c.text(x + 1, yy, STATE_GLYPH[r.state], { fg: col, bg, b: r.state !== 'pending' });
487
+ c.text(x + 3, yy, r.byAgent ? '◆' : ' ', { fg: t.ai, bg });
488
+ c.text(x + 5, yy, r.label, { fg: t.muted, bg }, 13);
489
+ const val = clip(r.value, Math.max(4, w - 26));
490
+ c.text(x + 19, yy, val, { fg: r.state === 'pending' ? t.dim : r.state === 'pass' ? t.text : col, bg, b: r.state === 'fail', i: r.state === 'pending' });
491
+ const word = r.state === 'pending' ? 'pending' : r.state;
492
+ c.textR(x + 1, yy, w - 2, word, { fg: col, bg, b: r.state === 'fail' });
493
+ });
494
+ return;
495
+ }
496
+ // two columns: glyph + label + short value
497
+ const colW = Math.floor((w - 2) / 2);
498
+ m.vet.forEach((r, i) => {
499
+ const row = Math.floor(i / 2);
500
+ if (row >= rowsAvail)
501
+ return;
502
+ const cx = x + 1 + (i % 2) * colW;
503
+ const yy = y + 1 + row;
504
+ const col = levelColor(c, r.state);
505
+ c.text(cx, yy, STATE_GLYPH[r.state], { fg: col, bg, b: true });
506
+ c.text(cx + 2, yy, clip(`${r.label} ${r.value === '—' ? '' : r.value.split(' · ')[0]}`, colW - 3), { fg: r.state === 'pending' ? t.dim : t.text, bg });
507
+ });
508
+ }
509
+ function drawData(c, x, y, w, m, bg) {
510
+ const t = c.theme;
511
+ section(c, x, y, w, 'DATA', m.marks?.cells.length ? 'app numbers · B/S = your fills' : 'app numbers · no extra fetch', bg, t.accent);
512
+ let cx = x + 1;
513
+ for (const d of m.data) {
514
+ const need = d.k.length + 1 + d.v.length + 2;
515
+ if (cx + need > x + w - 1)
516
+ break;
517
+ cx = c.text(cx, y + 1, d.k, { fg: t.dim, bg });
518
+ const fg = d.dir === 1 ? t.up : d.dir === -1 ? t.down : t.text;
519
+ cx = c.text(cx + 1, y + 1, d.v, { fg, bg, b: d.dir !== undefined && d.dir !== 0 }) + 2;
520
+ }
521
+ // the candle trace: one cell per close (most recent at the right)
522
+ const LV = '▁▂▃▄▅▆▇█';
523
+ const tw = w - 4;
524
+ const cl = m.closes.slice(-tw);
525
+ if (cl.length > 1) {
526
+ const lo = Math.min(...cl), hi = Math.max(...cl), span = hi - lo || 1;
527
+ const up = cl[cl.length - 1] >= cl[0];
528
+ c.text(x + 1, y + 2, up ? '▲' : '▼', { fg: up ? t.up : t.down, bg, b: true });
529
+ cl.forEach((v, i) => c.put(x + 3 + i, y + 2, LV[Math.round(((v - lo) / span) * 7)], { fg: mix(bg, up ? t.up : t.down, 0.45 + 0.55 * (i / Math.max(1, cl.length - 1))), bg, soft: true }));
530
+ // task-1483: a B / S cell on the candle of each fill (live = solid, paper = tinted); a buy wins a shared cell's colour
531
+ const first = m.closes.length - cl.length;
532
+ for (const mk of m.marks?.cells ?? []) {
533
+ const i = mk.index - first;
534
+ if (i < 0 || i >= cl.length)
535
+ continue;
536
+ const col = mk.side === 'buy' ? t.up : t.down;
537
+ c.put(x + 3 + i, y + 2, markerLetter(mk.side), mk.source === 'live' ? { fg: '#000000', bg: col, b: true, hard: true } : { fg: col, bg: mix(bg, col, 0.25), b: true, hard: true });
538
+ }
539
+ }
540
+ else
541
+ c.text(x + 1, y + 2, '· no candles yet', { fg: t.dim, bg, i: true });
542
+ }
543
+ function drawPlan(c, x, y, w, p, o, bg) {
544
+ const t = c.theme;
545
+ const open = p.stage === 'open';
546
+ section(c, x, y, w, open ? `POSITION · PAPER $${p.symbol}` : `PLAN · PAPER $${p.symbol}`, open ? `held ${p.heldMs !== null ? fmtDur(p.heldMs) : '—'}` : p.stage === 'sized' ? 'sized · not filled' : 'ticket open', bg, t.magenta);
547
+ const pc = p.pnlPct === null ? t.muted : p.pnlPct >= 0 ? t.up : t.down;
548
+ let yy = y + 1;
549
+ if (open) {
550
+ let cx = c.text(x + 1, yy, 'MARK PnL ', { fg: t.dim, bg });
551
+ cx = c.text(cx, yy, p.pnlPct === null ? '—' : sgnRatio(p.pnlPct), { fg: pc, bg, b: true });
552
+ if (p.pnlUsd !== null)
553
+ cx = c.text(cx + 1, yy, `(${sgnUsd(p.pnlUsd)} paper)`, { fg: pc, bg });
554
+ if (p.peakPct !== null)
555
+ cx = c.text(cx + 2, yy, `peak ${sgnRatio(p.peakPct)}`, { fg: p.peakPct >= 0 ? t.up : t.down, bg });
556
+ if (p.ddPct !== null && p.ddPct < -0.0005)
557
+ c.text(cx + 2, yy, `dd ${sgnRatio(p.ddPct)}`, { fg: t.warn, bg }, x + w - cx - 3);
558
+ // trace at the right of the next row
559
+ }
560
+ else
561
+ c.text(x + 1, yy, `size ${p.size}`, { fg: t.text, bg, b: true }, w - 2);
562
+ yy++;
563
+ c.text(x + 1, yy, clip(`entry ${p.entry} · mark ${p.mark} · size ${p.size}`, w - 2), { fg: t.muted, bg });
564
+ yy++;
565
+ c.text(x + 1, yy, clip(`${p.tp} · ${p.stop}`, w - 2), { fg: t.text, bg });
566
+ yy++;
567
+ if (p.refused)
568
+ c.text(x + 1, yy, clip(` ✗ REFUSED BY THE DESK · ${p.refused} `, w - 2), { fg: t.bg, bg: t.down, b: true });
569
+ else
570
+ c.text(x + 1, yy, clip(`waiting for ${p.waiting}`, w - 2), { fg: t.ai, bg, i: true });
571
+ yy++;
572
+ const tr = p.trace.slice(-(w - 4));
573
+ if (tr.length > 1) {
574
+ const lo = Math.min(0, ...tr), hi = Math.max(0, ...tr), span = hi - lo || 1;
575
+ const LV = '▁▂▃▄▅▆▇█';
576
+ tr.forEach((v, i) => c.put(x + 2 + i, yy, LV[Math.round(((v - lo) / span) * 7)], { fg: v >= 0 ? t.up : t.down, bg, soft: true }));
577
+ }
578
+ else
579
+ c.text(x + 1, yy, open ? '· waiting for the first mark tick' : '· PAPER · simulated ledger', { fg: t.dim, bg, i: true });
580
+ void o;
581
+ }
582
+ function drawLog(c, x, y, w, rows, m, bg) {
583
+ const t = c.theme;
584
+ section(c, x, y, w, m.mode === 'agent' ? 'DECISIONS' : 'SCAN LOG', 'this session', bg, t.ai);
585
+ const area = rows - 2;
586
+ const lines = m.log.slice(-Math.max(0, area));
587
+ if (!lines.length)
588
+ c.text(x + 2, y + 1, m.mode === 'agent' ? '· no decisions yet' : '· nothing locked yet', { fg: t.dim, bg, i: true });
589
+ lines.forEach((l, i) => {
590
+ const yy = y + 1 + i;
591
+ const col = levelColor(c, l.level);
592
+ let cx = c.text(x + 1, yy, l.clock, { fg: t.dim, bg });
593
+ cx = c.text(cx + 1, yy, l.glyph, { fg: col, bg, b: true });
594
+ if (l.symbol)
595
+ cx = c.text(cx + 1, yy, `$${l.symbol}`, { fg: t.text, bg, b: true });
596
+ cx = c.text(cx + 1, yy, l.text, { fg: l.level === 'na' ? t.muted : col, bg }, x + w - cx - 2);
597
+ if (l.said && cx + 8 < x + w - 1)
598
+ c.text(cx + 1, yy, `❯ ${l.said}`, { fg: mix(t.ai, bg, 0.35), bg, i: true }, x + w - cx - 2);
599
+ });
600
+ if (m.score)
601
+ c.text(x + 1, y + rows - 1, clip(m.score, w - 2), { fg: t.warn, bg, b: true });
602
+ }
603
+ /**
604
+ * The brain view for the coin on screen, or null when the normal analyst panel should draw: no auto mode, the agent
605
+ * was killed / went quiet / has not acted yet, the scan is off, or the user is typing into the analyst.
606
+ */
607
+ export function brainFor(h, facts, calm, scanUsage = '') {
608
+ if (h.aiFocused)
609
+ return null;
610
+ const hud = h.agentHud;
611
+ if (hud) {
612
+ const now = hud.clock();
613
+ const v = hud.story.view(now);
614
+ if (agentBrainActive(v, now))
615
+ return { model: agentBrainModel(v, facts, now, h.timeZone, h.priceMode !== 'price'), opts: { now, calm: calm || !!hud.reduced, depth: h.colorDepth, frame: h.vfx.frame } };
616
+ }
617
+ const scan = h.scan;
618
+ if (scan?.engine.on) {
619
+ const e = scan.engine;
620
+ const now = h.now.getTime();
621
+ // the scan's brain shows on the locked coin (or any coin while it is still scanning)
622
+ if (e.lock && facts && e.lock.mint !== facts.mint)
623
+ return null;
624
+ const sess = e.lock ? h.analystByMint?.get(e.lock.mint) : null;
625
+ const answer = isScanAnswer(sess) ? sess : null;
626
+ const model = scanBrainModel({ mode: e.mode.toUpperCase(), lock: e.lock, note: e.note, scanned: e.scanned, floorUsd: e.cfg.minLiqUsd, log: scan.log ?? [], answer, usage: scanUsage }, facts, h.timeZone);
627
+ return { model, opts: { now, calm, depth: h.colorDepth, frame: h.vfx.frame } };
628
+ }
629
+ return null;
630
+ }
631
+ function isScanAnswer(x) {
632
+ const o = x;
633
+ return !!o && typeof o === 'object' && typeof o.text === 'string' && Array.isArray(o.toolLines) && typeof o.startedAt === 'number';
634
+ }
@@ -75,7 +75,7 @@ export function renderNewPairs(c, state) {
75
75
  cv.text(x, y, '✓ clean', { fg: mix(bg, t.up, 0.7), bg });
76
76
  } },
77
77
  ];
78
- table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows: pairs, selected: sel, scroll: 0, bg: box.bg });
78
+ table(c, box.x + 1, box.y, box.w - 2, box.h, { columns: cols, rows: pairs, selected: sel, scroll: 0, bg: box.bg, rowKey: (r) => r.mint });
79
79
  if (sideW) {
80
80
  c.rainZone(tableW, top, 1, bottom - top, 1);
81
81
  pairSpotlight(c, c.w - sideW, top, sideW, bottom - top, pairs[sel], priceModeOf(state));