dexterm-cli 0.1.6 → 0.2.0

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 (395) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/spectator.js +3 -1
  3. package/dist/packages/agents/src/story.js +11 -2
  4. package/dist/packages/agents/src/tiers.js +36 -2
  5. package/dist/packages/agents/src/tools.js +35 -2
  6. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  7. package/dist/packages/autotrade/assets/limits.schema.json +569 -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/examples/executor/README.md +70 -0
  20. package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
  21. package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
  22. package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
  23. package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
  24. package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
  25. package/dist/packages/autotrade/src/account.js +74 -0
  26. package/dist/packages/autotrade/src/backtest.js +358 -0
  27. package/dist/packages/autotrade/src/basis-text.js +32 -0
  28. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  29. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  30. package/dist/packages/autotrade/src/chat/config.js +95 -0
  31. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  32. package/dist/packages/autotrade/src/chat/control.js +104 -0
  33. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  34. package/dist/packages/autotrade/src/chat/format.js +71 -0
  35. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  36. package/dist/packages/autotrade/src/chat/index.js +18 -0
  37. package/dist/packages/autotrade/src/chat/notify.js +302 -0
  38. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  39. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  40. package/dist/packages/autotrade/src/chat/secrets.js +106 -0
  41. package/dist/packages/autotrade/src/chat/service.js +251 -0
  42. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  43. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  44. package/dist/packages/autotrade/src/engine.js +401 -0
  45. package/dist/packages/autotrade/src/execution-port.js +97 -0
  46. package/dist/packages/autotrade/src/executor-health.js +217 -0
  47. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  48. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  49. package/dist/packages/autotrade/src/fleet.js +642 -0
  50. package/dist/packages/autotrade/src/forward.js +355 -0
  51. package/dist/packages/autotrade/src/index.js +72 -0
  52. package/dist/packages/autotrade/src/journey.js +477 -0
  53. package/dist/packages/autotrade/src/ledger.js +108 -0
  54. package/dist/packages/autotrade/src/limits.js +682 -0
  55. package/dist/packages/autotrade/src/live-source.js +182 -0
  56. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  57. package/dist/packages/autotrade/src/monitor.js +190 -0
  58. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  59. package/dist/packages/autotrade/src/outbox.js +394 -0
  60. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  61. package/dist/packages/autotrade/src/paper-limits.js +87 -0
  62. package/dist/packages/autotrade/src/real-mode.js +688 -0
  63. package/dist/packages/autotrade/src/receipts.js +330 -0
  64. package/dist/packages/autotrade/src/replay.js +198 -0
  65. package/dist/packages/autotrade/src/report.js +168 -0
  66. package/dist/packages/autotrade/src/runner-config.js +99 -0
  67. package/dist/packages/autotrade/src/runner.js +923 -0
  68. package/dist/packages/autotrade/src/safety.js +243 -0
  69. package/dist/packages/autotrade/src/service-guard.js +125 -0
  70. package/dist/packages/autotrade/src/service-install.js +213 -0
  71. package/dist/packages/autotrade/src/service-recover.js +232 -0
  72. package/dist/packages/autotrade/src/service.js +289 -0
  73. package/dist/packages/autotrade/src/session.js +269 -0
  74. package/dist/packages/autotrade/src/sessions.js +503 -0
  75. package/dist/packages/autotrade/src/signal-channels.js +312 -0
  76. package/dist/packages/autotrade/src/signal.js +196 -0
  77. package/dist/packages/autotrade/src/store.js +250 -0
  78. package/dist/packages/autotrade/src/strategy.js +258 -0
  79. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  80. package/dist/packages/autotrade/src/yolo.js +140 -0
  81. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  82. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  83. package/dist/packages/cli/src/ai-cmd.js +412 -0
  84. package/dist/packages/cli/src/analyst/backend.js +83 -15
  85. package/dist/packages/cli/src/analyst/codex.js +15 -9
  86. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  87. package/dist/packages/cli/src/analyst/session.js +5 -2
  88. package/dist/packages/cli/src/app-help-gen.js +56 -8
  89. package/dist/packages/cli/src/autotrade-cmd/audit.js +74 -0
  90. package/dist/packages/cli/src/autotrade-cmd/backtest.js +216 -0
  91. package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
  92. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  93. package/dist/packages/cli/src/autotrade-cmd/fleet.js +401 -0
  94. package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
  95. package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
  96. package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
  97. package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
  98. package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
  99. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  100. package/dist/packages/cli/src/autotrade-cmd/run.js +657 -0
  101. package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
  102. package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
  103. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  104. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  105. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  106. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  107. package/dist/packages/cli/src/autotrade-cmd.js +214 -0
  108. package/dist/packages/cli/src/display.js +156 -17
  109. package/dist/packages/cli/src/doctor.js +62 -3
  110. package/dist/packages/cli/src/export.js +10 -2
  111. package/dist/packages/cli/src/index.js +1 -1
  112. package/dist/packages/cli/src/ledger-cmd.js +290 -0
  113. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  114. package/dist/packages/cli/src/live/index.js +23 -89
  115. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  116. package/dist/packages/cli/src/live/trade.js +12 -457
  117. package/dist/packages/cli/src/live/watchlist.js +1 -1
  118. package/dist/packages/cli/src/main.js +147 -190
  119. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  120. package/dist/packages/cli/src/plan-cmd.js +1 -1
  121. package/dist/packages/cli/src/pnl-app.js +31 -21
  122. package/dist/packages/cli/src/pnl-card.js +162 -21
  123. package/dist/packages/cli/src/popup.js +22 -6
  124. package/dist/packages/cli/src/record-cmd.js +141 -20
  125. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  126. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  127. package/dist/packages/cli/src/screens/analyst.js +3 -2
  128. package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
  129. package/dist/packages/cli/src/screens/autotrade-keys.js +182 -0
  130. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  131. package/dist/packages/cli/src/screens/autotrade-screen.js +776 -0
  132. package/dist/packages/cli/src/screens/autotrade-ui.js +650 -0
  133. package/dist/packages/cli/src/screens/autotrade-worker.js +91 -0
  134. package/dist/packages/cli/src/screens/brain.js +37 -11
  135. package/dist/packages/cli/src/screens/overlay.js +5 -2
  136. package/dist/packages/cli/src/screens/plans.js +5 -17
  137. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  138. package/dist/packages/cli/src/screens/positions.js +6 -28
  139. package/dist/packages/cli/src/screens/ticket.js +38 -205
  140. package/dist/packages/cli/src/screens/token.js +3 -3
  141. package/dist/packages/cli/src/state.js +25 -19
  142. package/dist/packages/cli/src/telegram-cmd.js +454 -0
  143. package/dist/packages/cli/src/trade.js +3 -128
  144. package/dist/packages/cli/src/version.js +1 -1
  145. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  146. package/dist/packages/cli/src/wallet-command.js +8 -11
  147. package/dist/packages/cli/src/widgets.js +0 -12
  148. package/dist/packages/custom/src/config.js +20 -2
  149. package/dist/packages/custom/src/schema.js +35 -10
  150. package/dist/packages/custom/src/workspace.js +3 -3
  151. package/dist/packages/data/src/basis.js +191 -0
  152. package/dist/packages/data/src/detail.js +53 -2
  153. package/dist/packages/data/src/index.js +0 -14
  154. package/dist/packages/data/src/store.js +14 -5
  155. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  156. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  157. package/dist/packages/data/src/trade/bridge.js +4 -102
  158. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  159. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  160. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  161. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  162. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  163. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  164. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  165. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  166. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  167. package/dist/packages/data/src/trade/plans/index.js +0 -1
  168. package/dist/packages/data/src/trade/plans/service.js +3 -27
  169. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  170. package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
  171. package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
  172. package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
  173. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  174. package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
  175. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  176. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  177. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  178. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  179. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  180. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  181. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  182. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  183. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  184. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  185. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  186. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +229 -0
  187. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  188. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  189. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  190. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  191. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  192. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  193. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  194. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  195. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  196. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  201. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +51 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +38 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +57 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +50 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  231. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  232. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +39 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +24 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  243. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  244. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  247. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  249. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  251. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  254. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  255. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  256. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  257. package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +31 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  271. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  273. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  274. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  275. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  277. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  278. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  281. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  282. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  284. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  288. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  289. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  290. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  291. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  292. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  293. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  295. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  297. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  298. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  299. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
  302. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +35 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
  309. package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
  310. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  311. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  312. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  313. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  314. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  315. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  316. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  317. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  318. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  319. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  320. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  321. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  322. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  323. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  324. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  325. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  326. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  327. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  328. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  329. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  330. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  331. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  332. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  333. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  334. package/dist/packages/mcp/assets/kb/entries/autotrade.md +40 -0
  335. package/dist/packages/mcp/src/app-help.js +1 -1
  336. package/dist/packages/mcp/src/autotrade-tools.js +1107 -0
  337. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  338. package/dist/packages/mcp/src/engine/config.js +159 -0
  339. package/dist/packages/mcp/src/engine/index.js +10 -0
  340. package/dist/packages/mcp/src/engine/loop.js +101 -0
  341. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  342. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  343. package/dist/packages/mcp/src/engine/probe.js +110 -0
  344. package/dist/packages/mcp/src/engine/reason.js +35 -0
  345. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  346. package/dist/packages/mcp/src/index.js +7 -1
  347. package/dist/packages/mcp/src/ledger-tools.js +494 -0
  348. package/dist/packages/mcp/src/paper-agent.js +2 -0
  349. package/dist/packages/mcp/src/server.js +138 -19
  350. package/dist/packages/packs/src/backtest.js +3 -1
  351. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  352. package/dist/packages/popup/src/pages/chart.css +9 -0
  353. package/dist/packages/popup/src/pages/chart.html +3 -1
  354. package/dist/packages/popup/src/pages/chart.js +104 -7
  355. package/dist/packages/popup/src/protocol.js +2 -0
  356. package/dist/packages/popup/src/server.js +4 -0
  357. package/dist/packages/recorder/src/index.js +2 -0
  358. package/dist/packages/recorder/src/recorder.js +63 -6
  359. package/dist/packages/recorder/src/retention.js +100 -0
  360. package/dist/packages/recorder/src/schema.js +34 -1
  361. package/dist/packages/recorder/src/ticks.js +237 -0
  362. package/dist/packages/tui/src/index.js +1 -0
  363. package/dist/packages/tui/src/keymap.js +28 -1
  364. package/dist/packages/tui/src/markers.js +219 -0
  365. package/dist/packages/tui/src/widgets.js +149 -2
  366. package/package.json +2 -1
  367. package/dist/packages/data/src/trade/caps.js +0 -323
  368. package/dist/packages/data/src/trade/executor.js +0 -933
  369. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  370. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  371. package/dist/packages/data/src/trade/signer/client.js +0 -197
  372. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  373. package/dist/packages/data/src/trade/signer/host.js +0 -97
  374. package/dist/packages/data/src/trade/signer/main.js +0 -121
  375. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  376. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  377. package/dist/packages/data/src/trade/signer/service.js +0 -400
  378. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  379. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  380. package/dist/packages/data/src/trade/swap-build.js +0 -108
  381. package/dist/packages/data/src/trade/token-delta.js +0 -201
  382. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  383. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  384. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  385. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  386. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  387. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  388. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  389. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  390. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  391. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  392. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  393. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  394. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  395. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -7,6 +7,7 @@ import { Canvas } from "./canvas.js";
7
7
  import { mix, luminance } from "./color.js";
8
8
  import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtPriceBare, fmtCompactUsd, compactDecimalsFor, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
9
9
  import { launchpadColor } from "./theme.js";
10
+ import { chartMarkers, markerGlyph, markerLetter, markerLabel, markerLegend, markerRow, omittedNote, MARKER_PERIOD_MS } from "./markers.js";
10
11
  export const BLOCKS = '▁▂▃▄▅▆▇█';
11
12
  // braille dot bits: column 0 rows 0..3, column 1 rows 0..3 (row 0 = top)
12
13
  const BR_L = [0x01, 0x02, 0x04, 0x40];
@@ -131,7 +132,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
131
132
  const plotW = w - axisW;
132
133
  const plotH = h - volH - timeH;
133
134
  if (plotW < 10 || plotH < 4 || candles.length === 0)
134
- return;
135
+ return null;
135
136
  const CW = 4; // 3-col body with centered wick + 1-col gap
136
137
  const n = Math.min(candles.length, Math.floor(plotW / CW));
137
138
  const data = candles.slice(-n);
@@ -144,6 +145,24 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
144
145
  hi = Math.max(hi, k.h);
145
146
  vmax = Math.max(vmax, k.v);
146
147
  }
148
+ // task-1483: the trade marks for the bars on screen. Their prices (and the avg entry) widen the range when they sit
149
+ // within 3× of the last close, like the popup's levels; a farther price is left off the plot rather than squashing it.
150
+ const tm = opts.trades && (opts.trades.fills.length || opts.trades.avgEntry) ? opts.trades : null;
151
+ const period = MARKER_PERIOD_MS[String(opts.interval || '1h')] ?? 3600e3;
152
+ const set = tm ? chartMarkers(tm.fills, { periodMs: period, barTimes: data.map((k) => k.t) }) : null;
153
+ const lastC = data[data.length - 1].c;
154
+ const near = (p) => typeof p === 'number' && Number.isFinite(p) && p > 0 && lastC > 0 && p / lastC < 3 && lastC / p < 3;
155
+ if (set)
156
+ for (const m of set.markers)
157
+ if (near(m.priceUsd)) {
158
+ lo = Math.min(lo, m.priceUsd);
159
+ hi = Math.max(hi, m.priceUsd);
160
+ }
161
+ const avgUsd = tm?.avgEntry && near(tm.avgEntry.usd) ? tm.avgEntry.usd : null;
162
+ if (avgUsd !== null) {
163
+ lo = Math.min(lo, avgUsd);
164
+ hi = Math.max(hi, avgUsd);
165
+ }
147
166
  const pad = (hi - lo) * 0.06 || hi * 0.01;
148
167
  lo -= pad;
149
168
  hi += pad;
@@ -162,6 +181,30 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
162
181
  c.put(x + k, y + row, '·', gridStyle);
163
182
  c.textR(x + plotW, y + row, axisW, ' ' + fmtAxis(price), labelStyle);
164
183
  }
184
+ // task-1483: closed round trips, entry bar → exit bar, as a faint wash behind the candles (green = sold above the entry)
185
+ if (set && tm?.band !== false) {
186
+ for (const r of set.trips) {
187
+ const a = Math.max(0, data.findIndex((k) => k.t + period > r.openT));
188
+ let b = n - 1;
189
+ for (let i = n - 1; i >= 0; i--)
190
+ if (data[i].t <= r.closeT) {
191
+ b = i;
192
+ break;
193
+ }
194
+ const win = r.entryUsd !== null && r.exitUsd !== null ? r.exitUsd >= r.entryUsd : null;
195
+ const tint = mix(bg, win === null ? t.muted : win ? t.up : t.down, 0.07);
196
+ for (let i = a; i <= b; i++)
197
+ for (let q = 0; q < CW; q++)
198
+ for (let row = 0; row < plotH; row++) {
199
+ const cx = x + i * CW + q;
200
+ if (cx >= x + plotW)
201
+ continue;
202
+ const gi = c.idx(cx, y + row);
203
+ if (!c.hard[gi])
204
+ c.bg[gi] = tint;
205
+ }
206
+ }
207
+ }
165
208
  // last price line + tag
166
209
  const last = data[data.length - 1];
167
210
  const lastRow = Math.round(rowsOf(last.c));
@@ -229,6 +272,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
229
272
  }
230
273
  }
231
274
  }
275
+ const info = set ? drawTradeMarks(c, { x, y, plotW, plotH, n, CW, bg, lo, hi, axisW, fmtAxis, lastRow: lastRow >= 0 && lastRow < plotH ? lastRow : null, data, set, avgUsd, avgSource: tm?.avgEntry?.source ?? 'paper', scale: s }) : null;
232
276
  if (volH) {
233
277
  const vy = y + plotH;
234
278
  for (let i = 0; i < n; i++) {
@@ -260,6 +304,108 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
260
304
  c.text(cx, ty, lab, labelStyle);
261
305
  }
262
306
  c.textR(x + plotW, ty, axisW, (opts.interval || '1h') + ' UTC', { fg: t.dim, bg });
307
+ return info;
308
+ }
309
+ const freeCell = (c, cx, cy) => { const ch = c.ch[c.idx(cx, cy)]; return ch === ' ' || ch === '·' || ch === '┄'; };
310
+ /**
311
+ * task-1483: the avg-entry line, the B / S markers and their legend, on top of the drawn candles. Glyph + letter on
312
+ * every marker (△B ▽S paper, ▲B ▼S live), so NO_COLOR and calm read the same; colour only adds to it.
313
+ */
314
+ function drawTradeMarks(c, g) {
315
+ const t = c.theme;
316
+ const { x, y, plotW, plotH, CW, bg } = g;
317
+ const rowsOf = (p) => ((g.hi - p) / (g.hi - g.lo)) * plotH;
318
+ // the open position's average entry: a dashed line through the empty cells (candles stay on top) + an axis tag
319
+ let avgRow = null;
320
+ let avgShown = false;
321
+ if (g.avgUsd !== null) {
322
+ avgRow = markerRow(g.avgUsd, g.lo, g.hi, plotH);
323
+ if (avgRow !== null) {
324
+ const col = g.avgSource === 'paper' ? t.warn : t.text;
325
+ for (let k = 0; k < plotW; k++)
326
+ if (freeCell(c, x + k, y + avgRow)) {
327
+ c.put(x + k, y + avgRow, '┄', { fg: mix(bg, col, 0.8), bg, soft: true });
328
+ avgShown = true;
329
+ }
330
+ // `avg` at the line's left end so it reads without colour (also over the last-price dashes when both share a row),
331
+ // and the value as an axis tag
332
+ const labelOk = (cx) => freeCell(c, cx, y + avgRow) || c.ch[c.idx(cx, y + avgRow)] === '╌';
333
+ if (labelOk(x) && labelOk(x + 1) && labelOk(x + 2)) {
334
+ c.text(x, y + avgRow, 'avg', { fg: col, bg, b: true, hard: true });
335
+ avgShown = true;
336
+ }
337
+ if (avgRow !== g.lastRow) {
338
+ c.fill(x + plotW, y + avgRow, g.axisW, 1, ' ', { bg }); // the grid label under it goes, not half of it
339
+ c.textR(x + plotW, y + avgRow, g.axisW, ' ' + g.fmtAxis(g.avgUsd), { fg: '#000000', bg: col, b: true });
340
+ avgShown = true;
341
+ }
342
+ }
343
+ }
344
+ const used = new Set();
345
+ const drawn = [];
346
+ let offPlot = 0;
347
+ for (const m of g.set.markers) {
348
+ const k = g.data[m.index];
349
+ const col0 = x + m.index * CW;
350
+ const buy = m.side === 'buy';
351
+ // the fill's own price; no price → against the bar (below the low for a buy, above the high for a sell)
352
+ let row = m.priceUsd !== null ? markerRow(m.priceUsd, g.lo, g.hi, plotH) : Math.floor(rowsOf(buy ? k.l : k.h)) + (buy ? 1 : -1);
353
+ if (row === null || row < 0 || row >= plotH) {
354
+ offPlot++;
355
+ continue;
356
+ }
357
+ // two marks in one bar on the same row (buy + sell, or paper + live): step away from the bar, never across it
358
+ while (row >= 0 && row < plotH && (used.has((row << 12) | m.index)))
359
+ row += buy ? 1 : -1;
360
+ if (row < 0 || row >= plotH) {
361
+ offPlot++;
362
+ continue;
363
+ }
364
+ used.add((row << 12) | m.index);
365
+ const sideCol = buy ? t.up : t.down;
366
+ const st = m.source === 'live' ? { fg: '#000000', bg: sideCol, b: true, hard: true } : { fg: sideCol, bg: mix(bg, sideCol, 0.2), b: true, hard: true };
367
+ c.put(col0 + 1, y + row, markerGlyph(m.side, m.source), st);
368
+ c.put(col0 + 2, y + row, markerLetter(m.side), st);
369
+ if (m.count > 1 && col0 + 3 < x + plotW)
370
+ c.put(col0 + 3, y + row, m.count > 9 ? '+' : String(m.count), st);
371
+ drawn.push({ marker: m, col: col0 + 1, row });
372
+ }
373
+ // the newest marker gets a compact label (B 41k) where the row has room, right of it first, else left
374
+ const newest = drawn[drawn.length - 1];
375
+ if (newest) {
376
+ const label = markerLabel(newest.marker, g.fmtAxis, 1);
377
+ const fits = (sx) => sx >= x && sx + label.length <= x + plotW && [...label].every((_, i) => freeCell(c, sx + i, y + newest.row));
378
+ const right = newest.col + 3 + (newest.marker.count > 1 ? 1 : 0);
379
+ const sx = fits(right) ? right : fits(newest.col - 2 - label.length) ? newest.col - 2 - label.length : null;
380
+ if (sx !== null)
381
+ c.text(sx, y + newest.row, label, { fg: newest.marker.side === 'buy' ? t.up : t.down, bg, hard: true });
382
+ }
383
+ // legend + what was left off, on the top plot row where it is empty (else the bottom row), never over a candle
384
+ const note = [markerLegend(drawn.map((d) => d.marker), avgShown), omittedNote(g.set)].filter(Boolean).join(' · ');
385
+ if (note) {
386
+ const text = note.length > plotW - 2 ? note.slice(0, Math.max(0, plotW - 3)) + '…' : note;
387
+ // the outermost rows first (top, bottom, then inwards); in a row, the leftmost run of empty cells long enough
388
+ const rows = [];
389
+ for (let i = 0; i < Math.ceil(plotH / 2); i++) {
390
+ rows.push(i);
391
+ if (plotH - 1 - i !== i)
392
+ rows.push(plotH - 1 - i);
393
+ }
394
+ const len = [...text].length;
395
+ place: for (const row of rows) {
396
+ if (row === avgRow || row === g.lastRow || drawn.some((d) => d.row === row))
397
+ continue;
398
+ let run = 0;
399
+ for (let cx = x + 1; cx < x + plotW; cx++) {
400
+ run = freeCell(c, cx, y + row) ? run + 1 : 0;
401
+ if (run === len) {
402
+ c.text(cx - len + 1, y + row, text, { fg: t.dim, bg, i: true });
403
+ break place;
404
+ }
405
+ }
406
+ }
407
+ }
408
+ return { set: g.set, drawn, offPlot, avgRow };
263
409
  }
264
410
  /** Horizontal progress bar: gradient fill, hot head cell, one-cell ▓ glow past the head, ░ empty. pct 0..100 */
265
411
  export function bar(c, x, y, w, pct, opts = {}) {
@@ -483,7 +629,8 @@ export function tradeTape(c, x, y, w, h, trades, now, opts = {}) {
483
629
  continue;
484
630
  }
485
631
  cx = c.textR(cx + 1, yy, 8, fmtUsd(tr.usd), { fg: t.text, bg });
486
- cx = c.text(cx + 2, yy, tr.price > 0 ? fmtPrice(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
632
+ // task-1537: the caller's price basis (a market cap at the coin's supply by default); plain price without one
633
+ cx = c.text(cx + 2, yy, tr.price > 0 ? (opts.priceFmt ?? fmtPrice)(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
487
634
  c.text(cx + 2, yy, shortAddr(tr.wallet, 3), { fg: t.dim, bg }, Math.max(0, x + w - cx - 2));
488
635
  n += 1;
489
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dexterm-cli",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "mcpName": "io.github.dexterm-xyz/dexterm",
5
5
  "description": "dexterm: terminal DEX screener with a local Claude CLI analyst",
6
6
  "author": "The dexterm authors",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@dexterm/agents": "0.0.1",
28
+ "@dexterm/autotrade": "0.0.1",
28
29
  "@dexterm/custom": "0.0.1",
29
30
  "@dexterm/data": "0.0.1",
30
31
  "@dexterm/mcp": "0.0.1",
@@ -1,323 +0,0 @@
1
- // dexterm T1 — signer-side trade caps (SPEC §6.0 invariant 3, §6.2 CL-04). The policy the UI, the
2
- // model, the voice router and a compromised popup CANNOT bypass: every real trade is re-checked here
3
- // against hard limits before anything is signed or submitted, and refused out of policy.
4
- //
5
- // Hard rules (task-1310 scope §3):
6
- // * DEVNET is the default. MAINNET requires `mainnetEnabled` (the `--mainnet` flag) AND a known USD
7
- // notional AND it must pass a per-trade USD cap (default $5) and a daily USD cap (default $20).
8
- // * A plan whose simulation failed, whose price impact exceeds 3%, or whose signed/submitted intent
9
- // differs from the shown plan (plan-hash mismatch) is refused.
10
- // * Round 2 (SPEC §6.2): the decoded transaction must pass the program allow-list / forbidden-
11
- // instruction scan, the simulated SOL OUT of the wallet must not exceed the plan (+ a fee
12
- // allowance) nor the absolute SOL-out cap, slippage has a ceiling, and a mainnet notional must be
13
- // strictly positive and known.
14
- // * Caps live here, not in the UI. This module holds no keys and performs no I/O beyond the daily
15
- // ledger; it only decides ALLOW / REFUSE.
16
- //
17
- // This file imports no wallet, signing or crypto-key library. `node:crypto` is used only for a SHA-256
18
- // plan hash (binding), never for key material.
19
- import { createHash } from 'node:crypto';
20
- import { DatabaseSync } from 'node:sqlite';
21
- import { mkdirSync } from 'node:fs';
22
- import { dirname, join } from 'node:path';
23
- import { homedir } from 'node:os';
24
- import { systemClock } from "../clock.js";
25
- export const DEFAULT_PER_TRADE_USD = 5;
26
- export const DEFAULT_DAILY_USD = 20;
27
- export const MAX_PRICE_IMPACT_PCT = 3;
28
- /** Absolute SOL that may leave the wallet in one transaction (any network). */
29
- export const DEFAULT_MAX_SOL_OUT = 1;
30
- export const HARD_MAX_SOL_OUT = 5;
31
- /** Slippage ceiling (SPEC §6.2 "slippage exceeds the configured maximum"). */
32
- export const DEFAULT_MAX_SLIPPAGE_BPS = 500;
33
- export const HARD_MAX_SLIPPAGE_BPS = 1000;
34
- /**
35
- * How much MORE SOL than the planned SOL leg a transaction may consume: network fee + priority fee +
36
- * up to two ATA rents (2 × 0.00203928 SOL). A buy of 0.5 SOL may simulate as 0.5 + fees; 0.51 may not.
37
- */
38
- export const SOL_OVERHEAD_ALLOWANCE_LAMPORTS = 10000000n; // 0.01 SOL
39
- // DEFECT 6: hard ceilings the caps can NEVER exceed, however they are configured. `--max-usd 1000000`
40
- // or `--daily-usd 1e12` must not be accepted as-is. defaultCaps() clamps any override down to these,
41
- // the CLI flag parser rejects an over-ceiling value outright, and (round 2) checkCaps clamps AGAIN at
42
- // the decision point so a caps object literal or a mutated object cannot raise them either.
43
- export const HARD_MAX_PER_TRADE_USD = 500;
44
- export const HARD_MAX_DAILY_USD = 2000;
45
- function clampCap(v, dflt, hardMax) {
46
- const n = typeof v === 'number' ? v : Number(v);
47
- if (!(n > 0) || !Number.isFinite(n))
48
- return dflt;
49
- return Math.min(n, hardMax);
50
- }
51
- /**
52
- * Safe defaults: devnet, mainnet disabled, tiny caps. Override only from explicit flags. The result is
53
- * FROZEN (round 2): mutating a caps object after construction throws instead of raising a cap.
54
- */
55
- export function defaultCaps(over = {}) {
56
- const caps = {
57
- network: over.network === 'mainnet' ? 'mainnet' : 'devnet',
58
- mainnetEnabled: over.mainnetEnabled === true,
59
- // Defensive clamps: a caller can only make caps SMALLER than the defaults by override, never silently
60
- // larger through a bad value. Non-finite / negative values fall back to the safe default; an
61
- // over-ceiling value is clamped DOWN to the hard ceiling (DEFECT 6).
62
- perTradeUsd: clampCap(over.perTradeUsd, DEFAULT_PER_TRADE_USD, HARD_MAX_PER_TRADE_USD),
63
- dailyUsd: clampCap(over.dailyUsd, DEFAULT_DAILY_USD, HARD_MAX_DAILY_USD),
64
- maxPriceImpactPct: clampCap(over.maxPriceImpactPct, MAX_PRICE_IMPACT_PCT, MAX_PRICE_IMPACT_PCT),
65
- maxSolOut: clampCap(over.maxSolOut, DEFAULT_MAX_SOL_OUT, HARD_MAX_SOL_OUT),
66
- maxSlippageBps: clampCap(over.maxSlippageBps, DEFAULT_MAX_SLIPPAGE_BPS, HARD_MAX_SLIPPAGE_BPS),
67
- };
68
- // mainnet is only ever on when explicitly enabled AND the network is mainnet.
69
- if (caps.network === 'mainnet' && !caps.mainnetEnabled)
70
- caps.network = 'devnet';
71
- return Object.freeze(caps);
72
- }
73
- /**
74
- * task-1328: without `--mainnet` the USD caps are configurable only DOWNWARD — a value above the
75
- * defaults ($5 / $20) is clamped back to the default. Raising them needs the explicit mainnet flag
76
- * (and the hard ceilings still apply). The signer applies this to the caps it was started with.
77
- */
78
- export function capsOnlyDownward(caps) {
79
- if (caps.mainnetEnabled)
80
- return caps;
81
- return defaultCaps({
82
- ...caps,
83
- perTradeUsd: Math.min(caps.perTradeUsd, DEFAULT_PER_TRADE_USD),
84
- dailyUsd: Math.min(caps.dailyUsd, DEFAULT_DAILY_USD),
85
- // task-1337 (verify-1328 minor): the SOL-out and slippage ceilings are downward-only too without
86
- // --mainnet; `--max-slippage-bps 900` used to be honoured on a devnet-labelled signer.
87
- maxSolOut: Math.min(caps.maxSolOut, DEFAULT_MAX_SOL_OUT),
88
- maxSlippageBps: Math.min(caps.maxSlippageBps, DEFAULT_MAX_SLIPPAGE_BPS),
89
- });
90
- }
91
- /**
92
- * The caps as the decision point actually applies them: every numeric cap clamped to its hard ceiling
93
- * (or its default when missing/invalid), whatever object the caller handed in. A `TradeCaps` literal
94
- * `{perTradeUsd: 1e9}` is therefore worth exactly HARD_MAX_PER_TRADE_USD here (re-verify MEDIUM #6).
95
- */
96
- export function effectiveCaps(caps) {
97
- const c = caps ?? {};
98
- return defaultCaps({
99
- network: c.network, mainnetEnabled: c.mainnetEnabled,
100
- perTradeUsd: c.perTradeUsd, dailyUsd: c.dailyUsd, maxPriceImpactPct: c.maxPriceImpactPct,
101
- maxSolOut: c.maxSolOut, maxSlippageBps: c.maxSlippageBps,
102
- });
103
- }
104
- const ok = { ok: true };
105
- function refuse(reason, message) {
106
- return { ok: false, reason, message };
107
- }
108
- function lamportsToSolText(l) { return (Number(l) / 1e9).toFixed(4); }
109
- export function checkCaps(plan, rawCaps, spentTodayUsd, opts = {}) {
110
- const caps = effectiveCaps(rawCaps);
111
- // 1. Network gate — mainnet is the dangerous one.
112
- if (plan.network === 'mainnet') {
113
- if (!caps.mainnetEnabled || caps.network !== 'mainnet') {
114
- return refuse('mainnet-not-enabled', 'mainnet trading requires the --mainnet flag (and an explicit opt-in)');
115
- }
116
- }
117
- else if (plan.network !== caps.network) {
118
- return refuse('network-mismatch', `plan network ${plan.network} does not match configured ${caps.network}`);
119
- }
120
- // 2. Static transaction policy (SPEC §6.2): quote binding, program allow-list, forbidden
121
- // instructions, signer. Checked BEFORE the simulation gate because a plan that failed policy was
122
- // (deliberately) never simulated. A plan with no verdict at all is refused: only the executor's
123
- // decoder produces one, so its absence means the bytes were never inspected.
124
- const pol = plan.txPolicy;
125
- if (!pol || typeof pol !== 'object' || pol.ok !== true) {
126
- if (pol && pol.ok === false)
127
- return refuse(pol.reason, pol.message);
128
- return refuse('program-not-allowed', 'the transaction was not inspected');
129
- }
130
- // 3. Simulation must have run and passed (no confirm on a red sim).
131
- if (!plan.simulated)
132
- return refuse('not-simulated', 'the transaction was not simulated');
133
- if (!plan.simOk)
134
- return refuse('simulation-failed', `simulation failed: ${plan.simError ?? 'unknown error'}`);
135
- // 4. Price impact ceiling (any network). Unknown impact is refused on mainnet (re-verify LOW #10).
136
- if (plan.priceImpactPct === null || !Number.isFinite(plan.priceImpactPct)) {
137
- if (plan.network === 'mainnet')
138
- return refuse('price-impact', 'the quote carried no price impact; refusing on mainnet');
139
- }
140
- else if (plan.priceImpactPct > caps.maxPriceImpactPct) {
141
- return refuse('price-impact', `price impact ${plan.priceImpactPct.toFixed(2)}% exceeds the ${caps.maxPriceImpactPct}% ceiling`);
142
- }
143
- // 5. Slippage ceiling (any network).
144
- if (!Number.isFinite(plan.slippageBps) || plan.slippageBps < 0 || plan.slippageBps > caps.maxSlippageBps) {
145
- return refuse('slippage', `slippage ${plan.slippageBps} bps exceeds the ${caps.maxSlippageBps} bps ceiling`);
146
- }
147
- // 6. SOL out of the wallet, from the SIMULATED balance delta (any network): must be known, must not
148
- // exceed the planned SOL leg plus the fee allowance, and must not exceed the absolute cap.
149
- if (typeof plan.solOutLamports !== 'bigint') {
150
- return refuse('sol-delta-unknown', 'the simulation did not report the wallet balance change');
151
- }
152
- if (typeof plan.solLegLamports !== 'bigint' || plan.solLegLamports < 0n) {
153
- return refuse('sol-out-cap', 'the plan carries no SOL leg');
154
- }
155
- if (plan.solOutLamports > plan.solLegLamports + SOL_OVERHEAD_ALLOWANCE_LAMPORTS) {
156
- return refuse('sol-out-cap', `the transaction moves ${lamportsToSolText(plan.solOutLamports)} SOL out but the plan is ${lamportsToSolText(plan.solLegLamports)} SOL`);
157
- }
158
- const maxOutLamports = BigInt(Math.round(caps.maxSolOut * 1e9));
159
- if (plan.solOutLamports > maxOutLamports) {
160
- return refuse('sol-out-cap', `${lamportsToSolText(plan.solOutLamports)} SOL out exceeds the ${caps.maxSolOut} SOL per-transaction cap`);
161
- }
162
- // 7. USD caps — enforced on mainnet (devnet funds have no value), and on devnet when the caller asks
163
- // (task-1363 fast-wallet lane). Mainnet with no known USD value, or a zero/negative one, is refused
164
- // rather than waved through (round 2: `<= 0`, was `< 0`).
165
- if (plan.network === 'mainnet' || opts.enforceUsd === true) {
166
- if (plan.notionalUsd === null || !Number.isFinite(plan.notionalUsd) || plan.notionalUsd <= 0) {
167
- return refuse('no-usd-value', `cannot price this trade in USD (or it prices at $0); refusing${plan.network === 'mainnet' ? ' on mainnet' : ' (USD caps are enforced on this lane)'}`);
168
- }
169
- if (plan.notionalUsd > caps.perTradeUsd) {
170
- return refuse('per-trade-cap', `trade $${plan.notionalUsd.toFixed(2)} exceeds the per-trade cap $${caps.perTradeUsd.toFixed(2)}`);
171
- }
172
- const spent = Number.isFinite(spentTodayUsd) && spentTodayUsd > 0 ? spentTodayUsd : 0;
173
- if (spent + plan.notionalUsd > caps.dailyUsd) {
174
- return refuse('daily-cap', `trade $${plan.notionalUsd.toFixed(2)} would exceed today's remaining budget ($${Math.max(0, caps.dailyUsd - spent).toFixed(2)} of $${caps.dailyUsd.toFixed(2)})`);
175
- }
176
- }
177
- return ok;
178
- }
179
- /**
180
- * A plan hash binds a confirm/signature to the exact plan the user saw (SPEC §6.5 plan-hash binding).
181
- * If any material field changes between display and submit, the hash changes and the confirm is void.
182
- * Amounts are included so an inflated size or a swapped mint invalidates the confirm.
183
- */
184
- export function planHash(plan) {
185
- const canonical = [
186
- plan.network, plan.mint, plan.side,
187
- plan.amountInRaw.toString(), plan.amountOutRaw.toString(),
188
- String(plan.slippageBps), plan.route.join('>'),
189
- typeof plan.solLegLamports === 'bigint' ? plan.solLegLamports.toString() : 'none',
190
- typeof plan.solOutLamports === 'bigint' ? plan.solOutLamports.toString() : 'none',
191
- ].join('|');
192
- return createHash('sha256').update(canonical).digest('hex');
193
- }
194
- /** True when two plans are the same trade (same hash). Used to refuse a mutated plan at submit time. */
195
- export function samePlan(a, b) {
196
- return planHash(a) === planHash(b);
197
- }
198
- function utcDay(now) {
199
- return new Date(now).toISOString().slice(0, 10); // YYYY-MM-DD (UTC)
200
- }
201
- /** Where the CLI keeps the daily-cap ledger (shared by every dexterm process on this machine). */
202
- export function defaultCapLedgerPath() {
203
- return join(homedir(), '.dexterm', 'trade-caps.sqlite');
204
- }
205
- /**
206
- * Persistent daily-spend ledger for the USD daily cap. Backed by node:sqlite so the cap survives a
207
- * restart (a compromised UI cannot "forget" today's spend by relaunching). An in-memory DB is used
208
- * when no path is given (tests). Only realized mainnet USD is recorded.
209
- *
210
- * Round 2: `tryReserve` checks AND charges inside one `BEGIN IMMEDIATE` transaction, so two dexterm
211
- * PROCESSES sharing the file cannot both pass the daily check (re-verify MEDIUM #7); a `busy_timeout`
212
- * makes a concurrent open wait instead of throwing SQLITE_BUSY. `lock()` is the panic path: a locked
213
- * ledger reserves nothing until `unlock()`.
214
- */
215
- export class CapLedger {
216
- db;
217
- clock;
218
- constructor(opts = {}) {
219
- const path = opts.path ?? ':memory:';
220
- if (path !== ':memory:')
221
- mkdirSync(dirname(path), { recursive: true });
222
- this.db = new DatabaseSync(path);
223
- this.clock = opts.clock ?? systemClock;
224
- this.db.exec(`
225
- PRAGMA busy_timeout = 5000;
226
- PRAGMA journal_mode = WAL;
227
- CREATE TABLE IF NOT EXISTS trade_day_spend (
228
- day TEXT PRIMARY KEY,
229
- usd REAL NOT NULL DEFAULT 0,
230
- trades INTEGER NOT NULL DEFAULT 0
231
- ) WITHOUT ROWID;
232
- CREATE TABLE IF NOT EXISTS trade_lock (
233
- id INTEGER PRIMARY KEY CHECK (id = 1),
234
- locked_at INTEGER NOT NULL,
235
- reason TEXT NOT NULL
236
- );
237
- `);
238
- }
239
- /** USD already spent on mainnet today (UTC). */
240
- spentToday(now = this.clock.now()) {
241
- const row = this.db.prepare('SELECT usd FROM trade_day_spend WHERE day = ?').get(utcDay(now));
242
- return row && Number.isFinite(row.usd) ? Number(row.usd) : 0;
243
- }
244
- /** Record a realized mainnet trade's USD against today's budget. Devnet trades record nothing. */
245
- record(usd, now = this.clock.now()) {
246
- if (!(usd > 0))
247
- return;
248
- const day = utcDay(now);
249
- this.db.prepare(`INSERT INTO trade_day_spend (day, usd, trades) VALUES (?, ?, 1)
250
- ON CONFLICT(day) DO UPDATE SET usd = usd + excluded.usd, trades = trades + 1`).run(day, usd);
251
- }
252
- /**
253
- * Atomically check `spent + usd <= dailyCap` and reserve `usd` for today. The SELECT and the INSERT
254
- * run inside BEGIN IMMEDIATE, which takes the write lock first, so concurrent processes serialize
255
- * here. Returns the reservation's DAY so a refund credits the same day even across midnight.
256
- */
257
- tryReserve(usd, dailyCap, now = this.clock.now()) {
258
- const day = utcDay(now);
259
- if (!(usd > 0))
260
- return { ok: true, day, usd: 0 };
261
- this.db.exec('BEGIN IMMEDIATE');
262
- try {
263
- if (this.lockedRow()) {
264
- this.db.exec('ROLLBACK');
265
- return { ok: false, reason: 'panicked', spent: this.spentToday(now) };
266
- }
267
- const row = this.db.prepare('SELECT usd FROM trade_day_spend WHERE day = ?').get(day);
268
- const spent = row && Number.isFinite(row.usd) && Number(row.usd) > 0 ? Number(row.usd) : 0;
269
- if (spent + usd > dailyCap) {
270
- this.db.exec('ROLLBACK');
271
- return { ok: false, reason: 'daily-cap', spent };
272
- }
273
- this.db.prepare(`INSERT INTO trade_day_spend (day, usd, trades) VALUES (?, ?, 1)
274
- ON CONFLICT(day) DO UPDATE SET usd = usd + excluded.usd, trades = trades + 1`).run(day, usd);
275
- this.db.exec('COMMIT');
276
- return { ok: true, day, usd };
277
- }
278
- catch (e) {
279
- try {
280
- this.db.exec('ROLLBACK');
281
- }
282
- catch { /* not in a transaction */ }
283
- throw e;
284
- }
285
- }
286
- /**
287
- * Undo a reservation (DEFECT 5): the executor charges a trade's USD BEFORE it signs/submits so a
288
- * burst of concurrent trades cannot each pass the daily check and collectively blow the cap. If the
289
- * send definitely never left (sign failed, tx-byte mismatch, a pre-broadcast JSON-RPC rejection) the
290
- * reservation is refunded here, to the DAY it was reserved on (re-verify LOW #9). A send that was
291
- * broadcast, or MAY have been (transport error), is NOT refunded — the funds may have moved.
292
- */
293
- refund(usd, dayOrNow = this.clock.now()) {
294
- if (!(usd > 0))
295
- return;
296
- const day = typeof dayOrNow === 'string' ? dayOrNow : utcDay(dayOrNow);
297
- // Floor at 0; one trade's refund can never drive the day negative.
298
- this.db.prepare(`UPDATE trade_day_spend SET usd = MAX(0, usd - ?), trades = MAX(0, trades - 1) WHERE day = ?`).run(usd, day);
299
- }
300
- lockedRow() {
301
- const row = this.db.prepare('SELECT locked_at, reason FROM trade_lock WHERE id = 1').get();
302
- return row ?? null;
303
- }
304
- /** The panic lock, if engaged. Re-read every call so a lock from another process is seen at once. */
305
- locked() {
306
- const row = this.lockedRow();
307
- return row ? { lockedAt: Number(row.locked_at), reason: String(row.reason) } : null;
308
- }
309
- /** PANIC: refuse every reservation until unlock(). Idempotent. */
310
- lock(reason = 'panic', now = this.clock.now()) {
311
- this.db.prepare('INSERT OR IGNORE INTO trade_lock (id, locked_at, reason) VALUES (1, ?, ?)').run(now, reason.slice(0, 200));
312
- }
313
- unlock() {
314
- const r = this.db.prepare('DELETE FROM trade_lock WHERE id = 1').run();
315
- return Number(r.changes) > 0;
316
- }
317
- close() {
318
- try {
319
- this.db.close();
320
- }
321
- catch { /* already closed */ }
322
- }
323
- }