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
@@ -1,90 +1,26 @@
1
- // dexterm T1 — `dexterm wallet <link|unlink|status|panic|resume>` (SPEC §6.5). A non-TUI one-shot that
2
- // links a Phantom wallet through the loopback bridge (keys never enter dexterm; only the public key is
3
- // kept), unlinks it, prints the current link + the active trade caps, or PANICS live trading (round 2:
4
- // the cheapest safe signer panic path — a kill-switch file every dexterm process re-reads, plus a lock
5
- // on the shared daily-cap ledger). `resume` needs a real TTY keypress; piped input cannot re-arm.
6
- import { WalletBridge, WalletLinkStore, shortPubkey, defaultCaps, capsOnlyDownward, KillSwitch, CapLedger, defaultCapLedgerPath, defaultLiveLedgerPath, LiveLedger, HARD_MAX_PER_TRADE_USD, HARD_MAX_DAILY_USD, HARD_MAX_SOL_OUT, HARD_MAX_SLIPPAGE_BPS, parseSignerArgs, buildSignerService, lamportsToSol, FastWalletStore, defaultFastWalletPath, FAST_MAX_SOL_PER_FILL, createDataLayer, TradeRpc, DEVNET_RPC, } from '../../data/src/index.js';
7
- /**
8
- * Parse the trade-caps flags shared by the wallet command and (later) live trading. DEFECT 6: a USD
9
- * flag above the hard ceiling (e.g. `--max-usd 1000000`, `--daily-usd 1e12`) is REJECTED rather than
10
- * silently accepted, so a fat-fingered cap can never arm a huge money path. Throws on an over-ceiling
11
- * value; the caller surfaces it as a usage error. (defaultCaps also clamps as a second line of defence.)
12
- */
13
- export function parseCapsFlags(argv) {
14
- let mainnet = false;
15
- let perTradeUsd;
16
- let dailyUsd;
17
- let maxSolOut;
18
- let maxSlippageBps;
19
- const fmt = (n, unit) => (unit === '$' ? `$${n}` : `${n} ${unit}`);
20
- const take = (flag, raw, ceiling, unit = '$') => {
21
- const n = Number(raw);
22
- if (!Number.isFinite(n) || n <= 0)
23
- return undefined; // ignore junk; defaults apply
24
- if (n > ceiling)
25
- throw new Error(`${flag} ${fmt(n, unit)} exceeds the hard ceiling of ${fmt(ceiling, unit)}`);
26
- return n;
27
- };
28
- for (let i = 0; i < argv.length; i++) {
29
- const k = argv[i];
30
- if (k === '--mainnet')
31
- mainnet = true;
32
- else if (k === '--max-usd')
33
- perTradeUsd = take('--max-usd', argv[++i], HARD_MAX_PER_TRADE_USD);
34
- else if (k === '--daily-usd')
35
- dailyUsd = take('--daily-usd', argv[++i], HARD_MAX_DAILY_USD);
36
- else if (k === '--max-sol-out')
37
- maxSolOut = take('--max-sol-out', argv[++i], HARD_MAX_SOL_OUT, 'SOL');
38
- else if (k === '--max-slippage-bps')
39
- maxSlippageBps = take('--max-slippage-bps', argv[++i], HARD_MAX_SLIPPAGE_BPS, 'bps');
40
- }
41
- // task-1328: without --mainnet the USD caps can only go DOWN from the defaults (what the signer applies).
42
- return capsOnlyDownward(defaultCaps({ network: mainnet ? 'mainnet' : 'devnet', mainnetEnabled: mainnet, perTradeUsd, dailyUsd, maxSolOut, maxSlippageBps }));
43
- }
44
- function capsLine(caps) {
45
- const net = caps.network === 'mainnet' ? 'MAINNET' : 'devnet';
46
- return `mode: ${caps.network === 'mainnet' ? 'LIVE' : 'DRY-RUN'} · network: ${net} · per-trade $${caps.perTradeUsd} · daily $${caps.dailyUsd} · max impact ${caps.maxPriceImpactPct}% · max SOL out ${caps.maxSolOut} · max slippage ${caps.maxSlippageBps} bps`;
47
- }
48
- function panicLine(ks, ledger) {
1
+ // dexterm — `dexterm wallet <link|unlink|status|panic|resume>` (SPEC §6.5). A non-TUI one-shot that
2
+ // links a Phantom wallet through the loopback bridge (sign-a-message only: dexterm keeps the PUBLIC key,
3
+ // never a private key, and signs nothing), unlinks it, prints the link, or engages the PANIC switch (a
4
+ // kill-switch file every dexterm process re-reads: autotrade runs and the paper agent desk halt).
5
+ // `resume` needs a real TTY keypress; piped input cannot re-arm. task-1571: dexterm holds no key and has
6
+ // no trading wallet; live trading happens in the Telegram trading bot of the user's choice.
7
+ import { WalletBridge, WalletLinkStore, shortPubkey, KillSwitch, } from '../../data/src/index.js';
8
+ function panicLine(ks) {
49
9
  const p = ks.engaged();
50
- const l = ledger?.locked() ?? null;
51
- if (!p && !l)
10
+ if (!p)
52
11
  return null;
53
- const parts = [];
54
- if (p)
55
- parts.push(`panic switch engaged${p.engagedAt ? ` at ${new Date(p.engagedAt).toISOString()}` : ''} (${p.reason})`);
56
- if (l)
57
- parts.push(`cap ledger locked (${l.reason})`);
58
- return `⛔ LIVE TRADING DISABLED — ${parts.join('; ')}. Run \`dexterm wallet resume\` in a terminal to re-enable.`;
12
+ return `⛔ PANIC ENGAGED${p.engagedAt ? ` at ${new Date(p.engagedAt).toISOString()}` : ''} (${p.reason}): autotrade runs and the paper agent desk stay halted. Run \`dexterm wallet resume\` in a terminal to clear it.`;
59
13
  }
60
- function printStatus(io, store, caps, ks, ledger) {
14
+ function printStatus(io, store, ks) {
61
15
  const w = store.get();
62
16
  if (w)
63
- io.stdout.write(`linked: ${shortPubkey(w.publicKey)} (${w.publicKey})\n`);
17
+ io.stdout.write(`linked: ${shortPubkey(w.publicKey)} (${w.publicKey}) · public key only, read-only\n`);
64
18
  else
65
19
  io.stdout.write('linked: (none) — run `dexterm wallet link`\n');
66
- io.stdout.write(`${capsLine(caps)}\n`);
67
- const p = panicLine(ks, ledger);
20
+ const p = panicLine(ks);
68
21
  if (p)
69
22
  io.stdout.write(`${p}\n`);
70
- if (caps.network === 'mainnet')
71
- io.stdout.write('⚠ --mainnet is on: real funds, within the caps above. Devnet is the default; drop --mainnet to stay safe.\n');
72
- }
73
- function liveLine(path) {
74
- try {
75
- const l = new LiveLedger({ path, readOnly: true });
76
- try {
77
- const fills = l.records(1000);
78
- const open = l.positions().filter((p) => p.tokensRaw > 0n);
79
- return `live ledger: ${fills.length} confirmed fill${fills.length === 1 ? '' : 's'} · ${open.length} open position${open.length === 1 ? '' : 's'}${open.length ? ` (${open.map((p) => `$${p.symbol}`).join(', ')})` : ''}`;
80
- }
81
- finally {
82
- l.close();
83
- }
84
- }
85
- catch {
86
- return null;
87
- }
23
+ io.stdout.write('dexterm holds no key and signs nothing. Live trading happens in the Telegram trading bot of your choice (dexterm telegram setup).\n');
88
24
  }
89
25
  /**
90
26
  * Wait for ONE real keypress on a TTY. Resolves with the key, or null when stdin is not a TTY (piped
@@ -132,302 +68,65 @@ export async function ttyKeypress(stdin, timeoutMs) {
132
68
  /** Run the wallet subcommand. Returns a process exit code. */
133
69
  export async function runWallet(argv, io, over = {}) {
134
70
  const sub = argv[0] ?? 'status';
135
- let caps;
136
- try {
137
- caps = parseCapsFlags(argv.slice(1));
138
- }
139
- catch (e) {
140
- io.stderr.write(`dexterm wallet: ${e instanceof Error ? e.message : String(e)}\n`);
71
+ if (sub === 'trading' || sub === 'fast' || sub === 'probe') {
72
+ io.stderr.write(`dexterm wallet ${sub}: removed in 0.1.7. dexterm holds no key and has no trading wallet; live trading happens in the Telegram trading bot of your choice\n`);
141
73
  return 2;
142
74
  }
143
75
  const store = over.store ?? new WalletLinkStore();
144
76
  const ks = over.killSwitch ?? new KillSwitch();
145
- const ledgerPath = over.ledgerPath ?? defaultCapLedgerPath();
146
- const openLedger = () => { try {
147
- return new CapLedger({ path: ledgerPath });
148
- }
149
- catch {
150
- return null;
151
- } };
152
77
  if (sub === 'status') {
153
- const ledger = openLedger();
154
- try {
155
- printStatus(io, store, caps, ks, ledger);
156
- }
157
- finally {
158
- ledger?.close();
159
- }
160
- const ll = liveLine(over.liveLedgerPath ?? defaultLiveLedgerPath());
161
- if (ll)
162
- io.stdout.write(`${ll}\n`);
78
+ printStatus(io, store, ks);
163
79
  return 0;
164
80
  }
165
- // PROBE (task-1328): the devnet plumbing test from a shell — the signer service runs in THIS process
166
- // (same code the TUI's signer child runs), builds the wrap-and-unwrap plan for the linked wallet,
167
- // prints it, waits for a real TTY keypress, then opens Phantom, verifies, re-simulates, submits and
168
- // confirms. Devnet only: the service refuses a non-devnet RPC and --mainnet is not accepted here.
169
- if (sub === 'probe') {
170
- if (argv.includes('--mainnet')) {
171
- io.stderr.write('dexterm wallet probe: devnet only (drop --mainnet)\n');
172
- return 2;
173
- }
174
- if (!store.get()) {
175
- io.stderr.write('dexterm wallet probe: no wallet linked; run `dexterm wallet link` first\n');
176
- return 2;
177
- }
178
- let service;
179
- try {
180
- service = over.signerService ?? buildSignerService(parseSignerArgs(argv.slice(1).filter((a) => a !== '--no-browser'), io.env), (l) => io.stderr.write(`${l}\n`));
181
- }
182
- catch (e) {
183
- io.stderr.write(`dexterm wallet probe: ${e instanceof Error ? e.message : String(e)}\n`);
184
- return 2;
185
- }
186
- const st = service.status();
187
- if (st.network !== 'devnet') {
188
- io.stderr.write(`dexterm wallet probe: the signer RPC is ${st.network}; the probe runs only on devnet\n`);
189
- return 2;
190
- }
191
- if (st.panic || st.ledgerLocked) {
192
- io.stderr.write(`${panicLine(ks, openLedger()) ?? 'panicked'}\n`);
193
- return 2;
194
- }
195
- let plan;
196
- try {
197
- plan = await service.probe();
198
- }
199
- catch (e) {
200
- io.stderr.write(`dexterm wallet probe: plan failed: ${e instanceof Error ? e.message : String(e)}\n`);
201
- return 1;
202
- }
203
- io.stdout.write(`DEVNET PROBE for ${shortPubkey(plan.userPublicKey)} via ${st.rpcHost}\n`);
204
- io.stdout.write(` wraps ${lamportsToSol(BigInt(plan.amountInRaw)).toFixed(4)} SOL into your own WSOL account and closes it back · fee ≈ ${lamportsToSol(BigInt(plan.feeLamports)).toFixed(6)} SOL\n`);
205
- io.stdout.write(` will sign: ${plan.instructions.map((i) => `${i.program}${i.op ? ':' + i.op : ''}`).join(' · ')}\n`);
206
- io.stdout.write(` simulation: ${plan.sim.ran ? (plan.sim.ok ? `ok (${plan.sim.solOutLamports ?? '?'} lamports leave the wallet)` : `FAILED ${plan.sim.error ?? ''}`) : 'not run'} · policy: ${plan.policy.ok ? 'ok' : `REFUSE ${plan.policy.reason}`} · caps: ${plan.precheck.ok ? 'ok' : `REFUSE ${plan.precheck.reason}`}\n`);
207
- if (!plan.policy.ok || !plan.precheck.ok || !plan.sim.ok) {
208
- io.stderr.write('dexterm wallet probe: the signer would refuse this plan; nothing to sign\n');
209
- return 1;
210
- }
211
- const stdin = io.stdin;
212
- if (!stdin || stdin.isTTY !== true) {
213
- io.stderr.write('dexterm wallet probe: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
214
- return 2;
215
- }
216
- io.stdout.write('Press Y to sign this probe in Phantom (any other key aborts)… ');
217
- const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 60_000);
218
- io.stdout.write('\n');
219
- if (key !== 'y' && key !== 'Y') {
220
- io.stdout.write('aborted; nothing signed.\n');
221
- return 1;
222
- }
223
- const out = await service.confirm({ hash: plan.hash });
224
- if (out.kind === 'filled') {
225
- io.stdout.write(`✓ confirmed on devnet · signature ${out.signature}${out.lighthouseTail ? ` · Phantom appended ${out.lighthouseTail} Lighthouse instruction(s)` : ' · no Lighthouse tail'} · ${out.roundTripMs} ms\n`);
226
- return 0;
227
- }
228
- if (out.kind === 'refused') {
229
- io.stdout.write(`✗ refused by the signer: ${out.reason} — ${out.message}\n`);
230
- return 1;
231
- }
232
- if (out.kind === 'send-unknown') {
233
- io.stdout.write(`? send outcome unknown: ${out.error} · signature ${out.signature} · do NOT resend\n`);
234
- return 1;
235
- }
236
- io.stdout.write(`✗ send failed: ${out.error}${out.signature ? ` · signature ${out.signature}` : ''}\n`);
237
- return 1;
238
- }
239
- // FAST WALLET (task-1363): the one key dexterm holds, devnet only, for standing-mandate fills at machine speed.
240
- // create a new devnet key at ~/.dexterm/fast-wallet.json (mode 600) after a real TTY keypress; never overwrites
241
- // status public key, devnet balance, the per-fill ceiling
242
- // airdrop devnet faucet into the fast wallet (--sol N, default 1)
243
- // remove delete the key (TTY keypress; refused while it still holds SOL unless --force)
244
- // There is no export, no import, no mainnet variant, and the signer process only ever READS this file.
245
- if (sub === 'fast') {
246
- if (argv.includes('--mainnet')) {
247
- io.stderr.write('dexterm wallet fast: devnet only (there is no mainnet fast wallet; drop --mainnet)\n');
248
- return 2;
249
- }
250
- const action = argv[1] ?? 'status';
251
- const fastStore = new FastWalletStore(over.fastWalletPath ?? defaultFastWalletPath(io.env));
252
- const rpc = () => {
253
- if (over.fastRpc)
254
- return over.fastRpc;
255
- const dl = createDataLayer({ dbPath: ':memory:', rpcUrl: DEVNET_RPC, rpcFallbackUrl: null });
256
- const r = new TradeRpc(dl.http, { url: DEVNET_RPC });
257
- return { balance: (pk) => r.balance(pk), airdrop: (pk, l) => r.airdrop(pk, l), confirm: (sig) => r.confirm(sig, { timeoutMs: 45_000 }) };
258
- };
259
- const balanceLine = async (pk) => {
260
- try {
261
- const l = await rpc().balance(pk);
262
- return { lamports: l, text: `${lamportsToSol(l).toFixed(4)} SOL (devnet)` };
263
- }
264
- catch (e) {
265
- return { lamports: null, text: `balance unavailable (${e instanceof Error ? e.message : String(e)})` };
266
- }
267
- };
268
- if (action === 'status') {
269
- const rec = fastStore.get();
270
- if (!rec) {
271
- io.stdout.write(`fast wallet: (none) — create one with \`dexterm wallet fast create\` (devnet only)\n`);
272
- return 0;
273
- }
274
- const b = await balanceLine(rec.publicKey);
275
- io.stdout.write(`fast wallet: ${shortPubkey(rec.publicKey)} (${rec.publicKey}) · devnet · created ${new Date(rec.createdAt).toISOString()}\n`);
276
- io.stdout.write(` balance: ${b.text} · per-fill ceiling ${FAST_MAX_SOL_PER_FILL} SOL · the signer's USD per-trade/daily caps apply on every fill\n`);
277
- io.stdout.write(` file: ${fastStore.path} (mode 600; never exported, never printed)\n`);
278
- return 0;
279
- }
280
- if (action === 'create') {
281
- if (fastStore.exists()) {
282
- io.stderr.write(`dexterm wallet fast create: a fast wallet already exists at ${fastStore.path} (remove it first, after draining)\n`);
283
- return 2;
284
- }
285
- const stdin = io.stdin;
286
- if (!stdin || stdin.isTTY !== true) {
287
- io.stderr.write('dexterm wallet fast create: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
288
- return 2;
289
- }
290
- io.stdout.write(`Create a DEVNET fast wallet at ${fastStore.path}?\n armed order plans inside a standing mandate fire from it at machine speed, within the signer's caps and a ${FAST_MAX_SOL_PER_FILL} SOL per-fill ceiling.\n devnet only — this key can never be used on mainnet. Press Y to create (any other key aborts)… `);
291
- const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
292
- io.stdout.write('\n');
293
- if (key !== 'y' && key !== 'Y') {
294
- io.stdout.write('aborted; nothing created.\n');
295
- return 1;
296
- }
297
- const rec = fastStore.create({ network: 'devnet' });
298
- io.stdout.write(`✓ fast wallet created: ${rec.publicKey} (devnet)\n fund it: dexterm wallet fast airdrop · then restart dexterm so the signer loads it\n`);
299
- return 0;
300
- }
301
- if (action === 'airdrop') {
302
- const rec = fastStore.get();
303
- if (!rec) {
304
- io.stderr.write('dexterm wallet fast airdrop: no fast wallet; `dexterm wallet fast create` first\n');
305
- return 2;
306
- }
307
- const solIdx = argv.indexOf('--sol');
308
- const sol = solIdx >= 0 ? Number(argv[solIdx + 1]) : 1;
309
- if (!Number.isFinite(sol) || sol <= 0 || sol > 5) {
310
- io.stderr.write('dexterm wallet fast airdrop: --sol must be in (0, 5]\n');
311
- return 2;
312
- }
313
- try {
314
- const sig = await rpc().airdrop(rec.publicKey, BigInt(Math.round(sol * 1e9)));
315
- io.stdout.write(`airdrop requested: ${sol} SOL (devnet) → ${shortPubkey(rec.publicKey)} · signature ${sig}\n`);
316
- const c = rpc().confirm ? await rpc().confirm(sig).catch(() => null) : null;
317
- if (c)
318
- io.stdout.write(c.confirmed ? '✓ confirmed\n' : `? not confirmed yet (${c.err ?? 'pending'})\n`);
319
- const b = await balanceLine(rec.publicKey);
320
- io.stdout.write(` balance: ${b.text}\n`);
321
- return 0;
322
- }
323
- catch (e) {
324
- io.stderr.write(`dexterm wallet fast airdrop failed: ${e instanceof Error ? e.message : String(e)} — the public devnet faucet is often dry; try https://faucet.solana.com with the address above\n`);
325
- return 1;
326
- }
327
- }
328
- if (action === 'remove') {
329
- const rec = fastStore.get();
330
- if (!rec) {
331
- io.stdout.write('no fast wallet to remove.\n');
332
- return 0;
333
- }
334
- const b = await balanceLine(rec.publicKey);
335
- if (b.lamports !== null && b.lamports > 0n && !argv.includes('--force')) {
336
- io.stderr.write(`dexterm wallet fast remove: the fast wallet still holds ${b.text}; move it out first, or pass --force to delete the key anyway (devnet SOL only)\n`);
337
- return 2;
338
- }
339
- const stdin = io.stdin;
340
- if (!stdin || stdin.isTTY !== true) {
341
- io.stderr.write('dexterm wallet fast remove: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
342
- return 2;
343
- }
344
- io.stdout.write(`Delete the fast wallet ${shortPubkey(rec.publicKey)} (${b.text})? Press Y to delete (any other key aborts)… `);
345
- const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
346
- io.stdout.write('\n');
347
- if (key !== 'y' && key !== 'Y') {
348
- io.stdout.write('aborted; the fast wallet is kept.\n');
349
- return 1;
350
- }
351
- fastStore.remove();
352
- io.stdout.write('✓ fast wallet removed. Restart dexterm so the signer stops loading it.\n');
353
- return 0;
354
- }
355
- io.stderr.write(`dexterm wallet fast: unknown action ${JSON.stringify(action)} (expected status | create | airdrop | remove)\n`);
356
- return 2;
357
- }
358
81
  if (sub === 'unlink') {
359
82
  const had = store.clear();
360
83
  io.stdout.write(had ? 'unlinked.\n' : 'no wallet was linked.\n');
361
84
  return 0;
362
85
  }
363
86
  // PANIC: instant, no confirmation, scriptable from anywhere (a second terminal, a hotkey, an agent).
364
- // Engages the kill-switch file (every executor re-reads it on each plan/confirm) AND locks the shared
365
- // daily-cap ledger so no reservation can be taken even if the file were removed by hand.
87
+ // Engages the kill-switch file; every autotrade runner and the paper agent desk re-read it on each step.
366
88
  if (sub === 'panic') {
367
89
  const reasonIdx = argv.indexOf('--reason');
368
90
  const reason = reasonIdx >= 0 && argv[reasonIdx + 1] ? argv[reasonIdx + 1] : 'manual panic';
369
91
  const rec = ks.engage(reason);
370
- const ledger = openLedger();
371
- let lockNote = '';
372
- if (ledger) {
373
- try {
374
- ledger.lock(reason);
375
- lockNote = ' · cap ledger locked';
376
- }
377
- finally {
378
- ledger.close();
379
- }
380
- }
381
- else
382
- lockNote = ' · (cap ledger could not be opened; the kill-switch file still disables trading)';
383
- io.stdout.write(`⛔ PANIC: live trading disabled (${rec.reason})${lockNote}\n`);
384
- io.stdout.write(`kill switch: ${ks.path}\nRe-enable only from a real terminal: dexterm wallet resume\n`);
92
+ io.stdout.write(`⛔ PANIC: autotrade and the paper agent desk halted (${rec.reason})\n`);
93
+ io.stdout.write(`kill switch: ${ks.path}\nClear it only from a real terminal: dexterm wallet resume\n`);
385
94
  return 0;
386
95
  }
387
96
  // RESUME: the only way back. Requires a physical keypress on a TTY — not piped/scripted input — so a
388
- // model, a script or a compromised popup cannot silently re-arm live trading.
97
+ // model or a script cannot silently clear the panic switch.
389
98
  if (sub === 'resume') {
390
99
  const stdin = io.stdin;
391
100
  if (!stdin || stdin.isTTY !== true) {
392
101
  io.stderr.write('dexterm wallet resume: needs a real terminal keypress (stdin is not a TTY). Refusing.\n');
393
102
  return 2;
394
103
  }
395
- const p = ks.engaged();
396
- const ledger = openLedger();
397
- try {
398
- const l = ledger?.locked() ?? null;
399
- if (!p && !l) {
400
- io.stdout.write('live trading is not panicked; nothing to resume.\n');
401
- return 0;
402
- }
403
- io.stdout.write(`${panicLine(ks, ledger) ?? ''}\nPress Y to re-enable live trading (any other key aborts)… `);
404
- const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
405
- io.stdout.write('\n');
406
- if (key === null) {
407
- io.stderr.write('dexterm wallet resume: no keypress; still panicked.\n');
408
- return 2;
409
- }
410
- if (key !== 'y' && key !== 'Y') {
411
- io.stdout.write('aborted; still panicked.\n');
412
- return 1;
413
- }
414
- ks.release();
415
- ledger?.unlock();
416
- io.stdout.write('✓ live trading re-enabled (caps still apply).\n');
104
+ if (!ks.engaged()) {
105
+ io.stdout.write('the panic switch is not engaged; nothing to resume.\n');
417
106
  return 0;
418
107
  }
419
- finally {
420
- ledger?.close();
108
+ io.stdout.write(`${panicLine(ks) ?? ''}\nPress Y to clear the panic switch (any other key aborts)… `);
109
+ const key = await ttyKeypress(stdin, over.keypressTimeoutMs ?? 30_000);
110
+ io.stdout.write('\n');
111
+ if (key === null) {
112
+ io.stderr.write('dexterm wallet resume: no keypress; still panicked.\n');
113
+ return 2;
114
+ }
115
+ if (key !== 'y' && key !== 'Y') {
116
+ io.stdout.write('aborted; still panicked.\n');
117
+ return 1;
421
118
  }
119
+ ks.release();
120
+ io.stdout.write('✓ panic switch cleared.\n');
121
+ return 0;
422
122
  }
423
123
  if (sub === 'link') {
424
124
  const bridge = over.bridge ?? new WalletBridge(over.open ? { open: over.open } : {});
425
125
  io.stdout.write('Opening your browser to link a Solana wallet (sign-a-message only; no transaction)…\n');
426
126
  try {
427
127
  const res = await bridge.link();
428
- const rec = store.set(res.publicKey, { network: caps.network });
429
- io.stdout.write(`linked: ${shortPubkey(rec.publicKey)} (${rec.publicKey})\n`);
430
- io.stdout.write(`${capsLine(caps)}\n`);
128
+ const rec = store.set(res.publicKey);
129
+ io.stdout.write(`linked: ${shortPubkey(rec.publicKey)} (${rec.publicKey}) · public key only; dexterm never signs with it\n`);
431
130
  return 0;
432
131
  }
433
132
  catch (e) {
@@ -435,6 +134,6 @@ export async function runWallet(argv, io, over = {}) {
435
134
  return 1;
436
135
  }
437
136
  }
438
- io.stderr.write(`dexterm wallet: unknown subcommand ${JSON.stringify(sub)} (expected link | unlink | status | panic | resume | probe | fast)\n`);
137
+ io.stderr.write(`dexterm wallet: unknown subcommand ${JSON.stringify(sub)} (expected link | unlink | status | panic | resume)\n`);
439
138
  return 2;
440
139
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Parse a typed command: `/wallet` · `/wallet status` · `/wallet link` · `/wallet unlink` ·
3
- * `/wallet panic` · `/wallet probe`. The leading slash is optional (the screener's `/` line ate it).
4
- * Anything else — including a real filter — returns null. `resume` is deliberately NOT a command here:
5
- * re-enabling live trading needs a real TTY keypress in `dexterm wallet resume`.
2
+ * Parse a typed command: `/wallet` · `/wallet status` · `/wallet link` · `/wallet unlink` · `/wallet panic`.
3
+ * The leading slash is optional (the screener's `/` line ate it). Anything else — including a real filter —
4
+ * returns null. `resume` is deliberately NOT a command here: clearing the panic switch needs a real TTY
5
+ * keypress in `dexterm wallet resume`.
6
6
  */
7
7
  export function parseWalletCommand(text) {
8
8
  const m = /^\s*\/?wallet(?:\s+(\S+))?\s*$/i.exec(String(text ?? ''));
@@ -17,15 +17,12 @@ export function parseWalletCommand(text) {
17
17
  return 'unlink';
18
18
  if (arg === 'panic')
19
19
  return 'panic';
20
- if (arg === 'probe')
21
- return 'probe';
22
20
  if (arg === 'resume')
23
21
  return { error: 'resume needs a real terminal keypress: run `dexterm wallet resume` in a shell' };
24
- return { error: `unknown /wallet command "${arg.slice(0, 16)}" (status · link · unlink · panic · probe)` };
22
+ return { error: `unknown /wallet command "${arg.slice(0, 16)}" (status · link · unlink · panic)` };
25
23
  }
26
- export const WALLET_HELP = ` /wallet [status] linked wallet (pubkey only), the signer's network + caps, panic state
24
+ export const WALLET_HELP = ` /wallet [status] linked wallet (public key only, read-only) and the panic state
27
25
  /wallet link open the browser to link a Phantom wallet (sign-a-message proof; no transaction)
28
26
  /wallet unlink forget the linked public key
29
- /wallet panic disable live signing at once (kill-switch file + cap-ledger lock); resume needs \`dexterm wallet resume\`
30
- /wallet probe devnet plumbing test: a wrap-and-unwrap transaction you sign in Phantom (fees only)
31
- dexterm wallet fast … the capped DEVNET fast wallet armed order plans fire from: create (TTY keypress) · status · airdrop · remove`;
27
+ /wallet panic halt autotrade runs and the paper agent desk at once (kill-switch file); resume needs \`dexterm wallet resume\`
28
+ dexterm never holds a key and never signs. Live trading happens in the Telegram trading bot of your choice.`;
@@ -109,18 +109,6 @@ export function widgetPositionsVM(state) {
109
109
  rows.push({ symbol: p.symbol, mint: p.mint, side: 'long', tokens: p.tokens, valueSol: p.valueSol, unrealizedSol: p.unrealizedSol, unrealizedPct: p.unrealizedPct, book: 'paper' });
110
110
  }
111
111
  catch { /* no paper book on this run */ }
112
- const lp = live.livePositions;
113
- if (lp) {
114
- try {
115
- for (const p of lp.positions()) {
116
- if (p.tokensRaw <= 0n)
117
- continue;
118
- const h = lp.held(p.mint);
119
- rows.push({ symbol: p.symbol, mint: p.mint, side: 'long', tokens: h?.tokens ?? Number(p.tokensRaw) / 10 ** p.decimals, valueSol: null, unrealizedSol: null, unrealizedPct: null, book: 'live' });
120
- }
121
- }
122
- catch { /* ledger unreadable */ }
123
- }
124
112
  }
125
113
  return { rows, updatedAt: state.now.getTime() };
126
114
  }
@@ -1,5 +1,5 @@
1
1
  // @dexterm/custom — the config loader (task-1358 Tier 1). ~/.dexterm/config.json is the canonical,
2
- // machine-written config (0600, `version: 4`); workspaces are one JSON file each in ~/.dexterm/workspaces/,
2
+ // machine-written config (0600, `version: 5`); workspaces are one JSON file each in ~/.dexterm/workspaces/,
3
3
  // user themes in ~/.dexterm/themes/. THIS MODULE NEVER READS A KEY FILE: `<name>.key` files are the data
4
4
  // layer's business, and the only thing the loader does with a key is move one OUT of an old config.
5
5
  //
@@ -55,7 +55,7 @@ export function loadConfig(home) {
55
55
  // older (or newer) config: migrate, back up, write the new one, write extracted keys to <name>.key (0600)
56
56
  let m;
57
57
  try {
58
- m = migrate(raw);
58
+ m = migrate(raw, { journaledPriceMode: journaledPriceMode(home) });
59
59
  }
60
60
  catch (e) {
61
61
  return { ...base, ok: false, errors: [`${path}: ${e.message}`] };
@@ -95,6 +95,24 @@ export function saveConfig(home, config) {
95
95
  writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
96
96
  renameSync(tmp, path);
97
97
  }
98
+ /**
99
+ * task-1537: the `display.priceMode` value the journal last recorded (every applied / undone / redone op that set it,
100
+ * in order), or null when the journal never touched it. Lets the v4 → v5 migration tell a user's own `price` choice
101
+ * from the old default.
102
+ */
103
+ export function journaledPriceMode(home) {
104
+ let v = null;
105
+ for (const e of readJournal(home)) {
106
+ for (const op of e.ops ?? []) {
107
+ const val = op.path === '/display/priceMode' ? op.value : op.path === '/display' && op.value && typeof op.value === 'object' ? op.value.priceMode : undefined;
108
+ if (op.op === 'remove' && (op.path === '/display/priceMode' || op.path === '/display'))
109
+ v = null;
110
+ else if (val === 'price' || val === 'mcap')
111
+ v = val;
112
+ }
113
+ }
114
+ return v;
115
+ }
98
116
  export function readJournal(home) {
99
117
  try {
100
118
  return readFileSync(journalPath(home), 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l));
@@ -5,7 +5,7 @@
5
5
  // "no runtime deps" rule. KEYS NEVER APPEAR IN THIS SCHEMA: there is no `keys` / `signer` / `trading`
6
6
  // section at all (additional:false makes an attempt to add one a precise refusal), and the v1 migration
7
7
  // demonstrates moving a key OUT of the config file into `~/.dexterm/<name>.key`.
8
- export const CONFIG_VERSION = 4;
8
+ export const CONFIG_VERSION = 5;
9
9
  const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
10
10
  /** Validate `value` against a schema (a node or the name of one in `refs`); returns the error list. */
11
11
  export function validate(schema, value, path = '', errors = [], refs = SCHEMAS) {
@@ -135,17 +135,25 @@ export const WIDGET_REF = '^[a-z][a-z0-9-]{0,39}/[a-z][a-z0-9-]{0,39}(/[a-z][a-z
135
135
  export const WORKSPACE_SCREENS = ['screener', 'token', 'newpairs', 'watchlist', 'analyst', 'positions', 'narrative', 'plans', 'trending'];
136
136
  export const LAYOUT_SCREENS = ['screener', 'token', 'newpairs', 'watchlist', 'analyst', 'positions'];
137
137
  /**
138
- * task-1366: how token prices are DISPLAYED. `price` (default) = USD per token; `mcap` = market cap (price × supply,
139
- * supply from the data layer). A display mode only: the same data underneath, nothing that trades reads it.
138
+ * task-1366: how token prices are DISPLAYED. `mcap` = market cap (price × a known supply); `price` = USD per token.
139
+ * task-1537: `display.priceMode` is THE price-basis key for the UI, the CLI text AND the agents' language, default
140
+ * `mcap` (the v4 → v5 migration moves a `price` that was only the old default). A display / wording mode only: the
141
+ * same data underneath, nothing that trades reads it. @dexterm/data/basis holds the resolver and formatters.
140
142
  */
141
143
  export const PRICE_MODES = ['price', 'mcap'];
142
- export const DEFAULT_PRICE_MODE = 'price';
144
+ export const DEFAULT_PRICE_MODE = 'mcap';
143
145
  /**
144
146
  * task-1472: what the CHART Y axis shows (the terminal token chart and the chart popup), separate from the table/header
145
147
  * price mode: `mcap` (default; absent = mcap, so existing configs get it too) or `price`. `A` / the popup control toggle it.
146
148
  */
147
149
  export const CHART_AXES = ['mcap', 'price'];
148
150
  export const DEFAULT_CHART_AXIS = 'mcap';
151
+ /**
152
+ * task-1483: whether both charts draw the BUY / SELL markers of the ledgers' fills (and the avg-entry line): `on`
153
+ * (default; absent = on) or `off`. The chart popup's MARKERS control and `dexterm display markers` set it. Display only.
154
+ */
155
+ export const CHART_MARKERS = ['on', 'off'];
156
+ export const DEFAULT_CHART_MARKERS = 'on';
149
157
  const tokenProps = () => Object.fromEntries([...THEME_TOKENS.map((t) => [t, 'hex']), ...THEME_RAMPS.map((t) => [t, 'ramp'])]);
150
158
  export const SCHEMAS = {
151
159
  hex: { type: 'string', pattern: HEX },
@@ -242,8 +250,8 @@ export const SCHEMAS = {
242
250
  user: { type: 'object', values: 'userTheme' },
243
251
  } },
244
252
  fx: { type: 'object', additional: false, props: { intensity: { type: 'string', enum: ['off', 'normal', 'fever'] }, sound: { type: 'boolean' } } },
245
- /** task-1366: display options. `priceMode` price | mcap (the `$` key toggles and persists it). task-1472: `chartAxis` mcap | price (`A`; absent = mcap). */
246
- display: { type: 'object', additional: false, props: { priceMode: { type: 'string', enum: PRICE_MODES }, chartAxis: { type: 'string', enum: CHART_AXES } } },
253
+ /** task-1366: display options. `priceMode` price | mcap (the `$` key toggles and persists it). task-1472: `chartAxis` mcap | price (`A`; absent = mcap). task-1483: `chartMarkers` on | off (absent = on). */
254
+ display: { type: 'object', additional: false, props: { priceMode: { type: 'string', enum: PRICE_MODES }, chartAxis: { type: 'string', enum: CHART_AXES }, chartMarkers: { type: 'string', enum: CHART_MARKERS } } },
247
255
  layout: { type: 'object', additional: false, props: {
248
256
  workspace: { type: 'string', pattern: WS_NAME },
249
257
  workspaces: { type: 'object', values: 'workspace' },
@@ -269,7 +277,7 @@ export const SCHEMAS = {
269
277
  } },
270
278
  /** 0.1.5: which of the user's own CLIs runs the analyst. `/analyst` is user-only (DENIED_FOR_MODELS): a model never picks its own engine. */
271
279
  analyst: { type: 'object', additional: false, props: {
272
- engine: { type: 'string', enum: ['auto', 'claude', 'codex'] },
280
+ engine: { type: 'string', enum: ['auto', 'claude', 'codex', 'ollama'] },
273
281
  } },
274
282
  /** task-1393: voice-out preferences. `/voice` is user-only (DENIED_FOR_MODELS), so only the keyboard sets it. */
275
283
  voice: { type: 'object', additional: false, props: {
@@ -524,12 +532,29 @@ export const MIGRATIONS = {
524
532
  3: (c, out) => {
525
533
  const prior = (c.display && typeof c.display === 'object' ? c.display.priceMode : undefined);
526
534
  const priceMode = PRICE_MODES.includes(prior) ? prior : DEFAULT_PRICE_MODE;
527
- out.notes.push(`added display.priceMode = ${priceMode} (press $ in the app to show market cap instead of price)`);
535
+ out.notes.push(`added display.priceMode = ${priceMode} (press $ in the app to switch market cap / USD price)`);
528
536
  return { ...c, version: 4, display: { priceMode } };
529
537
  },
538
+ // v4 → v5 (task-1537): market cap is the default price basis. Every saved config carried `priceMode: "price"`
539
+ // because that was the default written with the first save, so a `price` moves to `mcap` UNLESS the config
540
+ // journal shows the user chose price themselves (`$`, `dexterm display price`, an approved proposal): that stays.
541
+ 4: (c, out) => {
542
+ const d = c.display && typeof c.display === 'object' ? { ...c.display } : {};
543
+ const kept = out.journaledPriceMode === 'price';
544
+ if (d.priceMode === 'price' && !kept) {
545
+ d.priceMode = 'mcap';
546
+ out.notes.push('display.priceMode: market cap is now the default basis (screens, CLI text and the assistant); press $ or run `dexterm display price` to go back to USD price');
547
+ }
548
+ else if (d.priceMode === 'price') {
549
+ out.notes.push('display.priceMode = price kept (you chose it); market cap is the new default, `dexterm display mcap` switches');
550
+ }
551
+ else if (!PRICE_MODES.includes(d.priceMode))
552
+ d.priceMode = DEFAULT_PRICE_MODE;
553
+ return { ...c, version: 5, display: d };
554
+ },
530
555
  };
531
- export function migrate(raw) {
532
- const out = { notes: [], extractedKeys: {}, from: Number(raw?.version ?? 1), to: CONFIG_VERSION };
556
+ export function migrate(raw, hints = {}) {
557
+ const out = { notes: [], extractedKeys: {}, from: Number(raw?.version ?? 1), to: CONFIG_VERSION, journaledPriceMode: hints.journaledPriceMode ?? null };
533
558
  let cur = raw && typeof raw === 'object' ? structuredClone(raw) : {};
534
559
  let v = out.from;
535
560
  if (!Number.isInteger(v) || v < 1)
@@ -6,7 +6,7 @@
6
6
  // Workspaces hold NO keys, NO trading settings and NO code: the schema is additional:false and a pack or a
7
7
  // Jarvis proposal that tries to smuggle anything else is refused by name (schema.ts). Switching a workspace
8
8
  // is a UI action: it can navigate, re-theme and open read-only popups; it can never arm, sign or confirm.
9
- import { validate, POPUP_KINDS, WORKSPACE_SCREENS } from "./schema.js";
9
+ import { validate, DEFAULT_PRICE_MODE, POPUP_KINDS, WORKSPACE_SCREENS } from "./schema.js";
10
10
  import { breakpoint, solve, treeFor, widgetsIn, widgetSpec } from "./layout.js";
11
11
  export const DEFAULT_WORKSPACE = 'default';
12
12
  export const MORNING_HUD = 'morning-hud';
@@ -114,8 +114,8 @@ export function layoutNumbers(ws, w, h) {
114
114
  export function captureWorkspace(name, snap) {
115
115
  const start = WORKSPACE_SCREENS.includes(snap.screen) ? snap.screen : 'screener';
116
116
  const ws = { name, description: `saved from the ${snap.screen} screen`, start, theme: snap.theme, intensity: snap.intensity };
117
- if (snap.priceMode && snap.priceMode !== 'price')
118
- ws.priceMode = snap.priceMode;
117
+ if (snap.priceMode && snap.priceMode !== DEFAULT_PRICE_MODE)
118
+ ws.priceMode = snap.priceMode; // task-1537: the default is mcap
119
119
  if (snap.screens)
120
120
  ws.screens = structuredClone(snap.screens);
121
121
  const sc = {};