dexterm-cli 0.1.6 → 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 (331) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/story.js +11 -2
  3. package/dist/packages/agents/src/tiers.js +27 -2
  4. package/dist/packages/agents/src/tools.js +30 -2
  5. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  6. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  7. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  8. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  9. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  10. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  11. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  12. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  13. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  14. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  15. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  16. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  17. package/dist/packages/autotrade/src/account.js +74 -0
  18. package/dist/packages/autotrade/src/backtest.js +357 -0
  19. package/dist/packages/autotrade/src/basis-text.js +32 -0
  20. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  21. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  22. package/dist/packages/autotrade/src/chat/config.js +93 -0
  23. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  24. package/dist/packages/autotrade/src/chat/control.js +103 -0
  25. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  26. package/dist/packages/autotrade/src/chat/format.js +71 -0
  27. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  28. package/dist/packages/autotrade/src/chat/index.js +18 -0
  29. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  30. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  31. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  32. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  33. package/dist/packages/autotrade/src/chat/service.js +248 -0
  34. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  35. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  36. package/dist/packages/autotrade/src/engine.js +387 -0
  37. package/dist/packages/autotrade/src/execution-port.js +97 -0
  38. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  39. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  40. package/dist/packages/autotrade/src/fleet.js +639 -0
  41. package/dist/packages/autotrade/src/index.js +53 -0
  42. package/dist/packages/autotrade/src/ledger.js +108 -0
  43. package/dist/packages/autotrade/src/live-source.js +182 -0
  44. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  45. package/dist/packages/autotrade/src/monitor.js +190 -0
  46. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  47. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  48. package/dist/packages/autotrade/src/replay.js +198 -0
  49. package/dist/packages/autotrade/src/runner-config.js +99 -0
  50. package/dist/packages/autotrade/src/runner.js +872 -0
  51. package/dist/packages/autotrade/src/safety.js +243 -0
  52. package/dist/packages/autotrade/src/session.js +269 -0
  53. package/dist/packages/autotrade/src/sessions.js +503 -0
  54. package/dist/packages/autotrade/src/store.js +250 -0
  55. package/dist/packages/autotrade/src/strategy.js +256 -0
  56. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  57. package/dist/packages/autotrade/src/yolo.js +140 -0
  58. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  59. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  60. package/dist/packages/cli/src/ai-cmd.js +412 -0
  61. package/dist/packages/cli/src/analyst/backend.js +82 -15
  62. package/dist/packages/cli/src/analyst/codex.js +14 -9
  63. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  64. package/dist/packages/cli/src/analyst/session.js +5 -2
  65. package/dist/packages/cli/src/app-help-gen.js +44 -8
  66. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  67. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  68. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  69. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  70. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  71. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  72. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  73. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  74. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  75. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  76. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  77. package/dist/packages/cli/src/display.js +156 -17
  78. package/dist/packages/cli/src/doctor.js +62 -3
  79. package/dist/packages/cli/src/export.js +10 -2
  80. package/dist/packages/cli/src/index.js +1 -1
  81. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  82. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  83. package/dist/packages/cli/src/live/index.js +23 -89
  84. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  85. package/dist/packages/cli/src/live/trade.js +12 -457
  86. package/dist/packages/cli/src/live/watchlist.js +1 -1
  87. package/dist/packages/cli/src/main.js +147 -190
  88. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  89. package/dist/packages/cli/src/plan-cmd.js +1 -1
  90. package/dist/packages/cli/src/pnl-app.js +31 -21
  91. package/dist/packages/cli/src/pnl-card.js +158 -21
  92. package/dist/packages/cli/src/popup.js +22 -6
  93. package/dist/packages/cli/src/record-cmd.js +141 -20
  94. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  95. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  96. package/dist/packages/cli/src/screens/analyst.js +3 -2
  97. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  98. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  99. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  100. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  101. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  102. package/dist/packages/cli/src/screens/brain.js +37 -11
  103. package/dist/packages/cli/src/screens/overlay.js +5 -2
  104. package/dist/packages/cli/src/screens/plans.js +5 -17
  105. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  106. package/dist/packages/cli/src/screens/positions.js +6 -28
  107. package/dist/packages/cli/src/screens/ticket.js +38 -205
  108. package/dist/packages/cli/src/screens/token.js +3 -3
  109. package/dist/packages/cli/src/state.js +25 -19
  110. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  111. package/dist/packages/cli/src/trade.js +3 -128
  112. package/dist/packages/cli/src/version.js +1 -1
  113. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  114. package/dist/packages/cli/src/wallet-command.js +8 -11
  115. package/dist/packages/cli/src/widgets.js +0 -12
  116. package/dist/packages/custom/src/config.js +20 -2
  117. package/dist/packages/custom/src/schema.js +35 -10
  118. package/dist/packages/custom/src/workspace.js +3 -3
  119. package/dist/packages/data/src/basis.js +191 -0
  120. package/dist/packages/data/src/detail.js +53 -2
  121. package/dist/packages/data/src/index.js +0 -14
  122. package/dist/packages/data/src/store.js +14 -5
  123. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  124. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  125. package/dist/packages/data/src/trade/bridge.js +4 -102
  126. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  127. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  128. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  129. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  130. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  131. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  132. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  133. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  134. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  135. package/dist/packages/data/src/trade/plans/index.js +0 -1
  136. package/dist/packages/data/src/trade/plans/service.js +3 -27
  137. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  138. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  139. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  140. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  141. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  142. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  143. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  144. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  145. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  146. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  147. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  148. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  149. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  150. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  151. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  152. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  153. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  154. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  155. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  156. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  157. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  158. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  159. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  160. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  161. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  162. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  163. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  164. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  165. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  166. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  167. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  168. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  169. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  170. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  171. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  172. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  173. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  174. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  175. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  176. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  177. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  178. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  179. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  180. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  181. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  182. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  183. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  184. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  186. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  188. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  189. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  194. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  195. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  197. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  201. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  202. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  203. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  204. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  205. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  208. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  209. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  210. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  211. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  233. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  235. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  236. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  237. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  238. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  240. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  242. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  243. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  244. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  260. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  262. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  263. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  270. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  271. package/dist/packages/mcp/src/app-help.js +1 -1
  272. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  273. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  274. package/dist/packages/mcp/src/engine/config.js +159 -0
  275. package/dist/packages/mcp/src/engine/index.js +10 -0
  276. package/dist/packages/mcp/src/engine/loop.js +101 -0
  277. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  278. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  279. package/dist/packages/mcp/src/engine/probe.js +110 -0
  280. package/dist/packages/mcp/src/engine/reason.js +35 -0
  281. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  282. package/dist/packages/mcp/src/index.js +7 -1
  283. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  284. package/dist/packages/mcp/src/paper-agent.js +2 -0
  285. package/dist/packages/mcp/src/server.js +127 -19
  286. package/dist/packages/packs/src/backtest.js +3 -1
  287. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  288. package/dist/packages/popup/src/pages/chart.css +9 -0
  289. package/dist/packages/popup/src/pages/chart.html +3 -1
  290. package/dist/packages/popup/src/pages/chart.js +104 -7
  291. package/dist/packages/popup/src/protocol.js +2 -0
  292. package/dist/packages/popup/src/server.js +4 -0
  293. package/dist/packages/recorder/src/index.js +2 -0
  294. package/dist/packages/recorder/src/recorder.js +63 -6
  295. package/dist/packages/recorder/src/retention.js +100 -0
  296. package/dist/packages/recorder/src/schema.js +34 -1
  297. package/dist/packages/recorder/src/ticks.js +237 -0
  298. package/dist/packages/tui/src/index.js +1 -0
  299. package/dist/packages/tui/src/keymap.js +21 -1
  300. package/dist/packages/tui/src/markers.js +219 -0
  301. package/dist/packages/tui/src/widgets.js +149 -2
  302. package/package.json +2 -1
  303. package/dist/packages/data/src/trade/caps.js +0 -323
  304. package/dist/packages/data/src/trade/executor.js +0 -933
  305. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  306. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  307. package/dist/packages/data/src/trade/signer/client.js +0 -197
  308. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  309. package/dist/packages/data/src/trade/signer/host.js +0 -97
  310. package/dist/packages/data/src/trade/signer/main.js +0 -121
  311. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  312. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  313. package/dist/packages/data/src/trade/signer/service.js +0 -400
  314. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  315. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  316. package/dist/packages/data/src/trade/swap-build.js +0 -108
  317. package/dist/packages/data/src/trade/token-delta.js +0 -201
  318. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  319. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  320. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  321. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  322. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  323. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  324. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  325. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  326. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  327. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  328. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  329. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  330. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  331. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,346 @@
1
+ // @dexterm/mcp — the TRADE LEDGER tools and their neighbours (task-1530). Read-only, plus one PROPOSE tool.
2
+ //
3
+ // ledger_trades observe trades from the unified ledger (paper, desk, autotrade, live), filtered, newest first
4
+ // ledger_positions observe the open trades (cost, held time, profile); no price fetch, no balance
5
+ // ledger_equity observe the realized equity curve and its drawdown
6
+ // ledger_summary observe win rate (with its 95 % interval), P/L, drawdown, rug rate, per-profile / exit / strategy
7
+ // ledger_compare observe one row per strategy version: the forward-test table
8
+ // candidate_dossier observe what an autotrade run recorded about a candidate: the rules it matched, the tick,
9
+ // the session's decisions, the clamp, the fills and the close
10
+ // outcomes_lookup observe the recorder's outcome labels (rug / dead / fade / flat / runner) for a coin, or for
11
+ // every candidate of a run (what the skips turned into)
12
+ // telegram_status observe the Telegram connector's settings: enabled, paired chats (a count), approvals, notify
13
+ // trading_status observe where live trading happens (task-1571: not in dexterm; it holds no key and places no
14
+ // live order) and whether the panic switch is engaged
15
+ // backtest_requests observe the backtests this agent asked for, and their results once the user ran them
16
+ // backtest_request propose ask the user to run a longer backtest (a request file; the user runs or dismisses it)
17
+ //
18
+ // Nothing here can trade, sign, change a cap, enable live or touch a key: the ledger is read through
19
+ // `@dexterm/autotrade`'s loader (pure file reads, loaded lazily: @dexterm/autotrade imports this package). Text that
20
+ // came from a token or an agent is firewalled before it is returned.
21
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
22
+ import { join } from 'node:path';
23
+ import { cleanLedgerText, compareStrategies, equityCurve, filterTrades, summarize } from '../../data/src/trade/ledger/index.js';
24
+ import { firewall } from "./firewall.js";
25
+ import { dextermHome } from "./user-content.js";
26
+ import { BacktestRequests, MAX_PENDING_REQUESTS, REQUEST_MAX_DAYS, REQUEST_RATE_PER_HOUR } from "./backtest-requests.js";
27
+ export const LEDGER_READ_TOOLS = [
28
+ 'ledger_trades', 'ledger_positions', 'ledger_equity', 'ledger_summary', 'ledger_compare',
29
+ 'candidate_dossier', 'outcomes_lookup', 'telegram_status', 'trading_status', 'backtest_requests',
30
+ ];
31
+ /** Write-shaped (a request file), so not on the read-only surface a skill may name. */
32
+ export const LEDGER_PROPOSE_TOOLS = ['backtest_request'];
33
+ export const LEDGER_TOOLS = [...LEDGER_READ_TOOLS, ...LEDGER_PROPOSE_TOOLS];
34
+ const MINT = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
35
+ const RUN_ID = /^[0-9TZ]+-[a-z][a-z0-9-]{1,31}-[0-9a-f]{4}$/;
36
+ const DAY_MS = 86_400_000;
37
+ const fw = (s, kind) => (s ? firewall(s, 'ledger', kind) : null);
38
+ const iso = (t) => (typeof t === 'number' && Number.isFinite(t) ? new Date(t < 1e12 ? t * 1000 : t).toISOString() : null);
39
+ /** The compact, firewalled view of one trade an agent gets (fills only when asked). */
40
+ export function tradeView(t, o = {}) {
41
+ return {
42
+ tradeId: t.tradeId, source: t.source, walletMode: t.walletMode, status: t.status, mint: t.mint, symbol: fw(t.symbol, 'symbol'),
43
+ strategy: t.strategy, runId: t.runId, account: t.account ?? null,
44
+ profile: t.profile, reprofiles: t.reprofiles.map((r) => ({ at: iso(r.t), from: r.from, to: r.to, reason: fw(r.reason, 'reason') })),
45
+ entryReason: { text: fw(t.entryReason.text, 'reason'), source: t.entryReason.source },
46
+ exitReason: { code: t.exitReason.code, text: fw(t.exitReason.text, 'reason'), source: t.exitReason.source },
47
+ openedAt: iso(t.openedAt), closedAt: iso(t.closedAt), holdSec: t.holdSec,
48
+ sizeSol: t.sizeSol, sizeUsd: t.sizeUsd, pctOfBook: t.pctOfBook,
49
+ entry: { priceSol: t.entryPriceSol, priceUsd: t.entryPriceUsd, mcapUsd: t.entryMcapUsd },
50
+ exit: { priceSol: t.exitPriceSol, priceUsd: t.exitPriceUsd, mcapUsd: t.exitMcapUsd },
51
+ supply: t.supply, pnlSol: t.pnlSol, pnlUsd: t.pnlUsd, pnlPct: t.pnlPct,
52
+ feesSol: t.feesSol, impactPct: { entry: t.impactPctEntry, exit: t.impactPctExit }, slippageBps: t.slippageBps,
53
+ mfePct: t.mfePct, maePct: t.maePct, excursionBasis: t.excursionBasis, rug: t.rug, rugWhy: t.rugWhy, stuck: t.stuck,
54
+ txSigs: t.txSigs, model: t.model,
55
+ ...(o.fills ? { fills: t.fills.map((f) => ({ ...f, at: iso(f.t) })) } : {}),
56
+ };
57
+ }
58
+ /** Tool args → a ledger filter. Unknown values are refused, never guessed. */
59
+ export function filterFromArgs(a, now) {
60
+ const f = {};
61
+ const pick = (k, allowed) => {
62
+ if (a[k] === undefined)
63
+ return undefined;
64
+ return allowed.includes(a[k]) ? a[k] : false;
65
+ };
66
+ const source = pick('source', ['paper', 'desk', 'autotrade', 'live']);
67
+ const mode = pick('mode', ['paper', 'devnet', 'live']);
68
+ const status = pick('status', ['open', 'closed']);
69
+ if (source === false || mode === false || status === false)
70
+ return { ok: false, error: 'source is paper|desk|autotrade|live, mode is paper|devnet|live, status is open|closed' };
71
+ if (source)
72
+ f.source = source;
73
+ if (mode)
74
+ f.mode = mode;
75
+ if (status)
76
+ f.status = status;
77
+ for (const [k, key, re] of [['strategy', 'strategy', /^[a-z][a-z0-9-]{0,31}(@[0-9a-z.]{1,16})?$/], ['profile', 'profile', /^[a-z][a-z0-9-]{0,31}$/], ['exit_reason', 'exitReason', /^[a-z][a-z0-9-]{0,31}$/]]) {
78
+ if (a[k] === undefined)
79
+ continue;
80
+ if (typeof a[k] !== 'string' || !re.test(a[k]))
81
+ return { ok: false, error: `${k} is not a valid id` };
82
+ f[key] = a[k];
83
+ }
84
+ if (a.mint !== undefined) {
85
+ if (typeof a.mint !== 'string' || !MINT.test(a.mint))
86
+ return { ok: false, error: 'mint is not a Solana address' };
87
+ f.mint = a.mint;
88
+ }
89
+ if (a.run_id !== undefined) {
90
+ if (typeof a.run_id !== 'string' || !RUN_ID.test(a.run_id))
91
+ return { ok: false, error: 'run_id is not a run id' };
92
+ f.runId = a.run_id;
93
+ }
94
+ if (a.account !== undefined) {
95
+ if (typeof a.account !== 'string' || !/^[a-z][a-z0-9-]{1,31}$/.test(a.account))
96
+ return { ok: false, error: 'account is not a paper account id' };
97
+ f.account = a.account;
98
+ }
99
+ if (a.days !== undefined) {
100
+ const d = Number(a.days);
101
+ if (!Number.isFinite(d) || d <= 0 || d > 3650)
102
+ return { ok: false, error: 'days is a number from 0 to 3650' };
103
+ f.since = now - d * DAY_MS;
104
+ }
105
+ return { ok: true, filter: f };
106
+ }
107
+ export class LedgerTools {
108
+ env;
109
+ actor;
110
+ now;
111
+ requestTimes = [];
112
+ o;
113
+ constructor(o = {}) {
114
+ this.o = o;
115
+ this.env = o.env ?? process.env;
116
+ this.actor = o.actor && /^[a-z][a-z0-9:._-]{0,63}$/.test(o.actor) ? o.actor : 'mcp-agent';
117
+ this.now = o.now ?? Date.now;
118
+ }
119
+ home() { return dextermHome(this.env); }
120
+ async view() {
121
+ if (this.o.view)
122
+ return this.o.view();
123
+ const at = await import('../../autotrade/src/index.js');
124
+ return at.loadLedgerView({ env: this.env, now: this.now() });
125
+ }
126
+ async call(name, args) {
127
+ try {
128
+ switch (name) {
129
+ case 'ledger_trades':
130
+ case 'ledger_positions':
131
+ case 'ledger_equity':
132
+ case 'ledger_summary':
133
+ case 'ledger_compare': return await this.ledger(name, args);
134
+ case 'candidate_dossier': return await this.dossier(args);
135
+ case 'outcomes_lookup': return await this.outcomes(args);
136
+ case 'telegram_status': return this.telegram();
137
+ case 'trading_status': return this.trading();
138
+ case 'backtest_requests': return this.requests();
139
+ case 'backtest_request': return await this.request(args);
140
+ default: return { error: `unknown ledger tool ${name}` };
141
+ }
142
+ }
143
+ catch (e) {
144
+ return { error: firewall(e instanceof Error ? e.message : String(e), 'ledger', 'error') ?? 'error' };
145
+ }
146
+ }
147
+ // ───────────────────────────── the ledger reads ─────────────────────────────
148
+ async ledger(name, args) {
149
+ const fr = filterFromArgs(args, this.now());
150
+ if (!fr.ok)
151
+ return { error: fr.error };
152
+ const v = await this.view();
153
+ const filter = name === 'ledger_positions' ? { ...fr.filter, status: 'open' } : fr.filter;
154
+ const rows = filterTrades(v.trades, filter);
155
+ const integrity = { ledger: v.file.check.ok ? `verified, ${v.file.events.length} events` : `FAILS its check at line ${v.file.check.at}: ${v.file.check.error}`, pendingFromSources: v.pending.length, problems: v.sources.filter((s) => s.problem).map((s) => s.problem) };
156
+ const modes = [...new Set(rows.map((t) => t.walletMode))];
157
+ const base = { readOnly: true, modes, integrity, note: modes.length > 1 ? 'PAPER, DEVNET and LIVE trades are in this set: filter by mode before comparing results.' : modes.includes('paper') ? 'PAPER (simulated) trades: never present them as results.' : undefined };
158
+ if (name === 'ledger_trades' || name === 'ledger_positions') {
159
+ const limit = Math.min(200, Math.max(1, Number.isInteger(args.limit) ? Number(args.limit) : 50));
160
+ const newest = [...rows].sort((a, b) => b.openedAt - a.openedAt).slice(0, limit);
161
+ return { ...base, count: rows.length, shown: newest.length, trades: newest.map((t) => tradeView(t, { fills: args.fills === true })) };
162
+ }
163
+ if (name === 'ledger_equity') {
164
+ const c = equityCurve(rows);
165
+ const max = 200;
166
+ const step = Math.max(1, Math.ceil(c.points.length / max));
167
+ const pts = c.points.filter((_, i) => i % step === 0 || i === c.points.length - 1).map((p) => ({ at: iso(p.t), tradeId: p.tradeId, pnl: p.pnl, cum: p.cum, drawdown: p.drawdown }));
168
+ return { ...base, unit: c.unit, startingBook: c.start, final: c.final, maxDrawdown: c.maxDrawdown, maxDrawdownPct: c.maxDrawdownPct, points: pts, sampled: step > 1 ? `every ${step}th of ${c.points.length} closes` : undefined };
169
+ }
170
+ if (name === 'ledger_summary')
171
+ return { ...base, summary: summarize(rows) };
172
+ return { ...base, ...compareStrategies(rows), note: 'One row per strategy version; a small trade count means little (see winRate.lo / hi).' };
173
+ }
174
+ // ───────────────────────────── a run's record of one candidate ─────────────────────────────
175
+ async dossier(args) {
176
+ if (typeof args.mint !== 'string' || !MINT.test(args.mint))
177
+ return { error: 'mint is required (a Solana address)' };
178
+ if (args.run_id !== undefined && (typeof args.run_id !== 'string' || !RUN_ID.test(args.run_id)))
179
+ return { error: 'run_id is not a run id' };
180
+ const dir = join(this.home(), 'autotrade', 'runs');
181
+ if (!existsSync(dir))
182
+ return { found: false, note: 'no autotrade runs on this machine' };
183
+ const runs = readdirSync(dir).filter((f) => /\.jsonl$/.test(f)).map((f) => f.slice(0, -6)).filter((r) => RUN_ID.test(r)).sort().reverse();
184
+ const pick = args.run_id ? runs.filter((r) => r === args.run_id) : runs.slice(0, 30);
185
+ for (const runId of pick) {
186
+ const p = join(dir, `${runId}.jsonl`);
187
+ if (statSync(p).size > 64 * 1024 * 1024)
188
+ continue;
189
+ const evs = [];
190
+ for (const l of readFileSync(p, 'utf8').split('\n')) {
191
+ if (!l.trim())
192
+ continue;
193
+ try {
194
+ evs.push(JSON.parse(l));
195
+ }
196
+ catch {
197
+ break;
198
+ }
199
+ }
200
+ const cands = evs.filter((e) => e.kind === 'candidate' && e.mint === args.mint);
201
+ if (!cands.length)
202
+ continue;
203
+ const start = evs.find((e) => e.kind === 'run_start') ?? {};
204
+ const sids = new Set(evs.filter((e) => e.kind === 'session_start' && e.mint === args.mint).map((e) => String(e.sessionId)));
205
+ const pos = new Set(evs.filter((e) => e.kind === 'fill' && e.mint === args.mint).map((e) => String(e.positionId)));
206
+ const decisions = evs.filter((e) => e.kind === 'reply' && sids.has(String(e.sessionId))).map((e) => {
207
+ const d = e.decision;
208
+ return { at: iso(e.t), sessionId: e.sessionId, costUsd: e.costUsd, ...(d?.ok ? { action: d.decision?.action, profile: d.decision?.profile ?? null, sizePct: d.decision?.sizePct ?? null, reason: fw(d.decision?.reason, 'reason') } : { invalid: fw(d?.error ?? 'invalid', 'error') }) };
209
+ });
210
+ const tick = (cands[cands.length - 1].tick ?? {});
211
+ return {
212
+ found: true, readOnly: true, runId, strategy: { id: start.strategy ?? null, version: start.version ?? null, hash: start.strategyHash ?? null }, mode: start.mode ?? 'paper',
213
+ mint: args.mint, symbol: fw(cands[cands.length - 1].symbol, 'symbol'),
214
+ candidate: cands.map((c) => ({ at: iso(c.t), matched: c.matched })),
215
+ tick: Object.fromEntries(Object.entries(tick).filter(([, v]) => typeof v === 'number' || typeof v === 'boolean' || v === null)),
216
+ sessions: [...sids], decisions,
217
+ clamp: evs.filter((e) => e.kind === 'clamp' && e.mint === args.mint).map((e) => ({ at: iso(e.t), result: e.result })),
218
+ fills: evs.filter((e) => e.kind === 'fill' && e.mint === args.mint).map((e) => ({ at: iso(e.t), side: e.side, usd: e.usd, price: e.avgPrice ?? e.price, reason: e.reason, profile: e.profile ?? null, detail: fw(e.detail, 'detail') })),
219
+ closed: evs.filter((e) => e.kind === 'trade_closed' && pos.has(String(e.trade?.id))).map((e) => { const tr = e.trade; return { at: iso(e.t), exitReason: tr.exitReason, pnlUsd: tr.pnlUsd, pnlPct: tr.pnlPct, stuck: tr.stuck === true }; }),
220
+ note: 'What the run recorded (the session\'s own dossier text is kept only in a --verbose run). For the coin as it is now, call get_dossier.',
221
+ };
222
+ }
223
+ return { found: false, note: args.run_id ? `run ${String(args.run_id)} has no candidate ${args.mint}` : `no candidate ${args.mint} in the last ${pick.length} runs` };
224
+ }
225
+ // ───────────────────────────── the recorder's outcome labels ─────────────────────────────
226
+ async outcomes(args) {
227
+ const db = this.o.historyDb?.() ?? null;
228
+ if (!db)
229
+ return { error: 'no recorder history on this machine (outcomes come from the recorder DB; run the app with the recorder on)' };
230
+ const limit = Math.min(100, Math.max(1, Number.isInteger(args.limit) ? Number(args.limit) : 20));
231
+ const q = db.prepare('SELECT mint, t0, horizon_sec, t1, label, label_tier, mcap_ratio, liq_ratio, price_ratio, max_mcap_ratio FROM outcomes WHERE mint = ? ORDER BY t0 DESC, horizon_sec ASC LIMIT ?');
232
+ const row = (r) => ({ t0: iso(Number(r.t0)), horizonSec: Number(r.horizon_sec), observedAt: r.t1 === null ? null : iso(Number(r.t1)), label: r.label, tier: r.label_tier, mcapRatio: r.mcap_ratio, liqRatio: r.liq_ratio, priceRatio: r.price_ratio, maxMcapRatio: r.max_mcap_ratio });
233
+ const legend = 'Labels are code-only, from recorded numbers: rug, dead, fade, flat, runner, unobserved (keyless-proxy unless chain-verified). Not a prediction.';
234
+ if (args.mint !== undefined) {
235
+ if (typeof args.mint !== 'string' || !MINT.test(args.mint))
236
+ return { error: 'mint is not a Solana address' };
237
+ return { readOnly: true, mint: args.mint, outcomes: q.all(args.mint, limit).map(row), legend };
238
+ }
239
+ if (typeof args.run_id !== 'string' || !RUN_ID.test(args.run_id))
240
+ return { error: 'pass mint or run_id' };
241
+ const p = join(this.home(), 'autotrade', 'runs', `${args.run_id}.jsonl`);
242
+ if (!existsSync(p))
243
+ return { error: `no run ${args.run_id}` };
244
+ const seen = new Map();
245
+ for (const l of readFileSync(p, 'utf8').split('\n')) {
246
+ if (!l.trim())
247
+ continue;
248
+ let e;
249
+ try {
250
+ e = JSON.parse(l);
251
+ }
252
+ catch {
253
+ break;
254
+ }
255
+ const mint = typeof e.mint === 'string' ? e.mint : null;
256
+ if (!mint || !MINT.test(mint))
257
+ continue;
258
+ if (e.kind === 'candidate' && !seen.has(mint))
259
+ seen.set(mint, { symbol: e.symbol, at: Number(e.t), entered: false });
260
+ if (e.kind === 'fill' && e.side === 'buy' && seen.has(mint))
261
+ seen.get(mint).entered = true;
262
+ }
263
+ const coins = [...seen.entries()].slice(0, limit).map(([mint, c]) => ({ mint, symbol: fw(String(c.symbol ?? ''), 'symbol'), candidateAt: iso(c.at), entered: c.entered, outcomes: q.all(mint, 6).map(row) }));
264
+ return { readOnly: true, runId: args.run_id, candidates: seen.size, shown: coins.length, coins, legend };
265
+ }
266
+ // ───────────────────────────── telegram (settings only) ─────────────────────────────
267
+ telegram() {
268
+ const p = join(this.home(), 'connectors.json');
269
+ if (!existsSync(p))
270
+ return { readOnly: true, configured: false, note: 'Telegram is not set up (`dexterm telegram setup` on this machine).' };
271
+ let raw;
272
+ try {
273
+ raw = JSON.parse(readFileSync(p, 'utf8'));
274
+ }
275
+ catch {
276
+ return { readOnly: true, configured: false, error: 'connectors.json is not readable JSON (the bot stays off)' };
277
+ }
278
+ const t = (raw.telegram && typeof raw.telegram === 'object' ? raw.telegram : {});
279
+ const allow = Array.isArray(t.allow) ? t.allow : [];
280
+ const n = (t.notify && typeof t.notify === 'object' ? t.notify : {});
281
+ const tiers = Array.isArray(t.tiers) ? t.tiers.filter((x) => x === 'notify' || x === 'control' || x === 'approve') : [];
282
+ return {
283
+ readOnly: true, configured: true, enabled: t.enabled === true, botConfigured: !!t.bot,
284
+ tokenStore: t.secretStore === 'keychain' || t.secretStore === 'libsecret' || t.secretStore === 'file' ? t.secretStore : null,
285
+ pairedChats: allow.length, tiers, approvals: t.approvals === 'on' ? 'on' : 'off',
286
+ approvalExpirySec: typeof t.approvalExpirySec === 'number' ? t.approvalExpirySec : null,
287
+ notify: Object.fromEntries(['candidates', 'entries', 'closes', 'halts', 'spend', 'daily'].map((k) => [k, typeof n[k] === 'boolean' || typeof n[k] === 'string' ? n[k] : null])),
288
+ quietHours: typeof t.quietHours === 'string' ? t.quietHours : null, dailySummaryAt: typeof t.dailySummaryAt === 'string' ? t.dailySummaryAt : null,
289
+ note: 'Settings only (no token, no chat or sender ids). The bot runs inside `dexterm autotrade run` when a chat is paired; only local commands change these settings.',
290
+ };
291
+ }
292
+ // ───────────────────────────── live trading (task-1571: not in dexterm) ───────────────
293
+ trading() {
294
+ const panic = existsSync(join(this.home(), 'trading.panic'));
295
+ return {
296
+ readOnly: true,
297
+ liveTrading: 'not in dexterm: dexterm holds no key, signs nothing and places no live order. Live trading happens in the Telegram trading bot the user chose, which holds its own wallet; dexterm only sends alerts and run control to the user\'s own bot (telegram_status).',
298
+ paperTrading: 'local: the paper ticket, paper accounts and paper autotrade runs (ledger_* tools)',
299
+ panicSwitch: panic ? 'engaged (autotrade runs and the paper agent desk are halted; only `dexterm wallet resume` in a terminal clears it)' : 'off',
300
+ };
301
+ }
302
+ // ───────────────────────────── backtest requests ─────────────────────────────
303
+ requests() {
304
+ const rs = new BacktestRequests(this.home()).list().slice(0, 20);
305
+ return { readOnly: true, requests: rs.map((r) => ({ requestId: r.requestId, id: r.id, days: r.days, everyCandidate: r.everyCandidate, status: r.status, why: fw(r.why, 'why'), createdAt: iso(r.createdAt), resolvedAt: iso(r.resolvedAt ?? null), result: r.result ?? null })), note: 'The user runs a request with `dexterm autotrade requests run <requestId>`; results are backtests on recorded history, not predictions.' };
306
+ }
307
+ async request(args) {
308
+ const id = args.id;
309
+ if (typeof id !== 'string' || !/^[a-z][a-z0-9-]{1,31}$/.test(id))
310
+ return { error: 'id must be a strategy id from strategy_list' };
311
+ const days = Number(args.days ?? 30);
312
+ if (!Number.isInteger(days) || days < 1 || days > REQUEST_MAX_DAYS)
313
+ return { error: `days must be a whole number from 1 to ${REQUEST_MAX_DAYS}` };
314
+ // stored as plain one-line text (the user reads it in `dexterm autotrade requests`); fenced when an agent reads it back
315
+ const why = cleanLedgerText(args.why, 200);
316
+ if (!why)
317
+ return { error: 'why is required (one line: what the backtest should answer)' };
318
+ const t = this.now();
319
+ while (this.requestTimes.length && t - this.requestTimes[0] > 3_600_000)
320
+ this.requestTimes.shift();
321
+ if (this.requestTimes.length >= REQUEST_RATE_PER_HOUR)
322
+ return { error: `at most ${REQUEST_RATE_PER_HOUR} backtest requests per hour`, reason: 'rate-limited' };
323
+ const files = new BacktestRequests(this.home());
324
+ if (files.list({ status: 'pending' }).length >= MAX_PENDING_REQUESTS)
325
+ return { error: `${MAX_PENDING_REQUESTS} requests are already waiting for the user; wait for them to be run or dismissed`, reason: 'quota' };
326
+ const info = await (this.o.strategyInfo ?? defaultStrategyInfo(this.env, this.now))(id);
327
+ if (!info)
328
+ return { error: `no strategy ${id}` };
329
+ if (!info.ok)
330
+ return { error: `${id} does not validate: ${info.why ?? 'invalid'}` };
331
+ this.requestTimes.push(t);
332
+ const r = files.create({ id, version: info.version, strategyHash: info.hash, days, everyCandidate: args.every_candidate === true, why, actor: this.actor, createdAt: t });
333
+ return { proposal: true, requestId: r.requestId, id, days, everyCandidate: r.everyCandidate, status: 'pending', note: `Nothing ran. The user runs it with \`dexterm autotrade requests run ${r.requestId}\` (or dismisses it); read the result with backtest_requests.` };
334
+ }
335
+ }
336
+ function defaultStrategyInfo(env, now) {
337
+ return async (id) => {
338
+ const at = await import('../../autotrade/src/index.js');
339
+ const got = new at.StrategyStore({ home: dextermHome(env), now }).get(id);
340
+ if (!got)
341
+ return null;
342
+ if (!got.ok)
343
+ return { ok: false, version: null, hash: null, why: got.errors.map(at.fmtIssue).join('; ').slice(0, 200) };
344
+ return { ok: true, version: String(got.strategy.version ?? ''), hash: at.strategyHash(got.strategy) };
345
+ };
346
+ }
@@ -85,6 +85,8 @@ export async function resolvePaperAgentDesk(data, env = process.env, opts = {})
85
85
  control: () => readDeskControl(env),
86
86
  journal: new SharedJournal(paperAgentJournalPath(env), systemClock),
87
87
  agent: rv.agent ?? 'mcp-agent',
88
+ // task-1530: the launcher may name the model session (journaled on each fill so the trade ledger can join its cost)
89
+ sessionId: () => env.DEXTERM_SESSION_ID ?? null,
88
90
  });
89
91
  cache = { mtimeMs, db: rv.db, mandateId: rv.mandateId, wallet, desk };
90
92
  return desk;