dexterm-cli 0.1.3 → 0.1.5

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 (259) hide show
  1. package/dist/packages/cli/assets/claude/analyst-system.md +2 -1
  2. package/dist/packages/cli/src/agent-paper.js +60 -0
  3. package/dist/packages/cli/src/analyst/backend.js +183 -0
  4. package/dist/packages/cli/src/analyst/codex.js +301 -0
  5. package/dist/packages/cli/src/analyst/session.js +9 -2
  6. package/dist/packages/cli/src/app-help-gen.js +18 -9
  7. package/dist/packages/cli/src/docs-kb-gen.js +228 -0
  8. package/dist/packages/cli/src/doctor.js +80 -9
  9. package/dist/packages/cli/src/index.js +1 -1
  10. package/dist/packages/cli/src/kb-skill-cmd.js +3 -0
  11. package/dist/packages/cli/src/larp-detected.js +209 -0
  12. package/dist/packages/cli/src/live/dev-history.js +99 -0
  13. package/dist/packages/cli/src/live/newpairs.js +12 -0
  14. package/dist/packages/cli/src/live/screens/token-live.js +11 -1
  15. package/dist/packages/cli/src/live/token.js +39 -1
  16. package/dist/packages/cli/src/main.js +260 -7
  17. package/dist/packages/cli/src/mcp-follow.js +255 -0
  18. package/dist/packages/cli/src/record-cmd.js +2 -0
  19. package/dist/packages/cli/src/rules.js +25 -2
  20. package/dist/packages/cli/src/screens/analyst-session.js +5 -3
  21. package/dist/packages/cli/src/screens/analyst.js +4 -3
  22. package/dist/packages/cli/src/spectate-cmd.js +14 -3
  23. package/dist/packages/cli/src/version.js +1 -1
  24. package/dist/packages/cli/src/x-cmd.js +11 -5
  25. package/dist/packages/custom/src/rules.js +2 -2
  26. package/dist/packages/custom/src/schema.js +5 -1
  27. package/dist/packages/data/src/fixtures.js +28 -6
  28. package/dist/packages/data/src/index.js +3 -0
  29. package/dist/packages/data/src/sources/jupiter.js +1 -0
  30. package/dist/packages/data/src/trade/agent/paper-desk.js +336 -0
  31. package/dist/packages/data/src/trade/agent/rendezvous.js +161 -0
  32. package/dist/packages/mcp/assets/kb/app-help/agent-paper-trade.md +38 -0
  33. package/dist/packages/mcp/assets/kb/app-help/ask-the-analyst.md +1 -1
  34. package/dist/packages/mcp/assets/kb/app-help/connect-an-agent.md +1 -1
  35. package/dist/packages/mcp/assets/kb/app-help/dev-history.md +32 -0
  36. package/dist/packages/mcp/assets/kb/app-help/paper-trade.md +1 -1
  37. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +4 -1
  38. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +1 -0
  39. package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +4 -1
  40. package/dist/packages/mcp/assets/kb/app-help/use-codex-analyst.md +31 -0
  41. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +50 -0
  42. package/dist/packages/mcp/assets/kb/docs/docs-agents-capability-tiers-enforced-server-side.md +38 -0
  43. package/dist/packages/mcp/assets/kb/docs/docs-agents-directory-metadata-prepared-not-submitted.md +36 -0
  44. package/dist/packages/mcp/assets/kb/docs/docs-agents-install-in-one-command.md +51 -0
  45. package/dist/packages/mcp/assets/kb/docs/docs-agents-per-agent-mandates.md +35 -0
  46. package/dist/packages/mcp/assets/kb/docs/docs-agents-recipes.md +43 -0
  47. package/dist/packages/mcp/assets/kb/docs/docs-agents-red-team-coverage.md +30 -0
  48. package/dist/packages/mcp/assets/kb/docs/docs-agents-two-ways-to-connect.md +37 -0
  49. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +36 -0
  50. package/dist/packages/mcp/assets/kb/docs/docs-agents-watch-an-agent-work-spectator-view.md +35 -0
  51. package/dist/packages/mcp/assets/kb/docs/docs-agents.md +26 -0
  52. package/dist/packages/mcp/assets/kb/docs/docs-analyst-answer-style.md +35 -0
  53. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +25 -0
  54. package/dist/packages/mcp/assets/kb/docs/docs-analyst-it-knows-the-app.md +33 -0
  55. package/dist/packages/mcp/assets/kb/docs/docs-analyst-mcp-tools.md +33 -0
  56. package/dist/packages/mcp/assets/kb/docs/docs-analyst-plain-language-filters.md +25 -0
  57. package/dist/packages/mcp/assets/kb/docs/docs-analyst-skills.md +31 -0
  58. package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +33 -0
  59. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-codex-instead-of-claude.md +35 -0
  60. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-can-change-three-tiers.md +33 -0
  61. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +26 -0
  62. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-the-analyst-can-never-do.md +31 -0
  63. package/dist/packages/mcp/assets/kb/docs/docs-analyst.md +23 -0
  64. package/dist/packages/mcp/assets/kb/docs/docs-beginners-1-start-dexterm.md +35 -0
  65. package/dist/packages/mcp/assets/kb/docs/docs-beginners-2-open-a-real-coin-by-contract-address.md +43 -0
  66. package/dist/packages/mcp/assets/kb/docs/docs-beginners-3-read-the-safety-panel.md +40 -0
  67. package/dist/packages/mcp/assets/kb/docs/docs-beginners-4-read-the-holder-panel.md +35 -0
  68. package/dist/packages/mcp/assets/kb/docs/docs-beginners-5-optionally-ask-the-analyst.md +39 -0
  69. package/dist/packages/mcp/assets/kb/docs/docs-beginners-6-export-or-share-what-you-see.md +34 -0
  70. package/dist/packages/mcp/assets/kb/docs/docs-beginners-7-quit-safely.md +34 -0
  71. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +33 -0
  72. package/dist/packages/mcp/assets/kb/docs/docs-beginners-check-the-installation-with-doctor.md +38 -0
  73. package/dist/packages/mcp/assets/kb/docs/docs-beginners-choose-a-setup-path.md +30 -0
  74. package/dist/packages/mcp/assets/kb/docs/docs-beginners-common-mistakes-and-recovery.md +30 -0
  75. package/dist/packages/mcp/assets/kb/docs/docs-beginners-copy-and-paste-without-surprises.md +39 -0
  76. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-data-says-unavailable-stale-or-rate-limited.md +35 -0
  77. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-dexterm-says-command-not-found.md +36 -0
  78. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-optional-help-is-unavailable.md +33 -0
  79. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-paste-does-nothing-or-pastes-twice.md +35 -0
  80. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-the-screen-looks-broken.md +33 -0
  81. package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-you-typed-the-wrong-thing.md +33 -0
  82. package/dist/packages/mcp/assets/kb/docs/docs-beginners-install-node-js-first.md +36 -0
  83. package/dist/packages/mcp/assets/kb/docs/docs-beginners-linux.md +30 -0
  84. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-homebrew.md +46 -0
  85. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-or-linux-npm.md +39 -0
  86. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos.md +30 -0
  87. package/dist/packages/mcp/assets/kb/docs/docs-beginners-open-the-terminal.md +29 -0
  88. package/dist/packages/mcp/assets/kb/docs/docs-beginners-reopen-dexterm-later.md +36 -0
  89. package/dist/packages/mcp/assets/kb/docs/docs-beginners-ten-minute-first-win.md +30 -0
  90. package/dist/packages/mcp/assets/kb/docs/docs-beginners-terminal-shell-prompt-command-output-and-path.md +40 -0
  91. package/dist/packages/mcp/assets/kb/docs/docs-beginners-where-to-go-next.md +32 -0
  92. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-expectations.md +29 -0
  93. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-powershell-experimental.md +39 -0
  94. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows.md +30 -0
  95. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +29 -0
  96. package/dist/packages/mcp/assets/kb/docs/docs-cli-dexterm-help.md +31 -0
  97. package/dist/packages/mcp/assets/kb/docs/docs-cli-filter-grammar.md +37 -0
  98. package/dist/packages/mcp/assets/kb/docs/docs-cli-flags.md +27 -0
  99. package/dist/packages/mcp/assets/kb/docs/docs-cli-invocations.md +43 -0
  100. package/dist/packages/mcp/assets/kb/docs/docs-cli-presets.md +26 -0
  101. package/dist/packages/mcp/assets/kb/docs/docs-cli-scan-commands.md +26 -0
  102. package/dist/packages/mcp/assets/kb/docs/docs-cli-sort-keys.md +27 -0
  103. package/dist/packages/mcp/assets/kb/docs/docs-cli-subcommands.md +26 -0
  104. package/dist/packages/mcp/assets/kb/docs/docs-cli.md +25 -0
  105. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +47 -0
  106. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +41 -0
  107. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +44 -0
  108. package/dist/packages/mcp/assets/kb/docs/docs-configuration-history-recorder.md +33 -0
  109. package/dist/packages/mcp/assets/kb/docs/docs-configuration-packs.md +38 -0
  110. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +35 -0
  111. package/dist/packages/mcp/assets/kb/docs/docs-configuration-push-alerts-and-dexterm-watch.md +45 -0
  112. package/dist/packages/mcp/assets/kb/docs/docs-configuration-rules-engine.md +37 -0
  113. package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-analyst-s-launch.md +29 -0
  114. package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-optional-free-rpc-key.md +42 -0
  115. package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-keymaps.md +29 -0
  116. package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-look.md +27 -0
  117. package/dist/packages/mcp/assets/kb/docs/docs-configuration-widgets.md +31 -0
  118. package/dist/packages/mcp/assets/kb/docs/docs-configuration-workspaces.md +30 -0
  119. package/dist/packages/mcp/assets/kb/docs/docs-configuration.md +25 -0
  120. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-1-follow-a-wallet-see-what-it-does.md +51 -0
  121. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +56 -0
  122. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +47 -0
  123. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +36 -0
  124. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +31 -0
  125. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-cache.md +32 -0
  126. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-health-registry.md +29 -0
  127. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-rate-limits-and-pacing.md +32 -0
  128. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-request-budget-meter.md +30 -0
  129. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +47 -0
  130. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-the-helius-credit-meter.md +30 -0
  131. package/dist/packages/mcp/assets/kb/docs/docs-data-sources.md +27 -0
  132. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-dexterm-doctor.md +33 -0
  133. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +34 -0
  134. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +81 -0
  135. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-next.md +32 -0
  136. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +35 -0
  137. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +34 -0
  138. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-supported-platforms.md +44 -0
  139. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-updating.md +28 -0
  140. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +27 -0
  141. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-global-keys.md +26 -0
  142. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-keys-handled-by-the-loop.md +29 -0
  143. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-per-screen-keys.md +26 -0
  144. package/dist/packages/mcp/assets/kb/docs/docs-keyboard-what-shows.md +27 -0
  145. package/dist/packages/mcp/assets/kb/docs/docs-keyboard.md +26 -0
  146. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +53 -0
  147. package/dist/packages/mcp/assets/kb/docs/docs-larp-open-link-keys.md +26 -0
  148. package/dist/packages/mcp/assets/kb/docs/docs-larp-paste-a-coin-on-the-web.md +35 -0
  149. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +29 -0
  150. package/dist/packages/mcp/assets/kb/docs/docs-larp-timestamped-follow-up-drafts.md +35 -0
  151. package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +33 -0
  152. package/dist/packages/mcp/assets/kb/docs/docs-larp.md +24 -0
  153. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-degraded-states-you-will-see.md +36 -0
  154. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-free-vs-paid-x-api-tiers-your-bill-your-limits.md +37 -0
  155. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-kol-lists-and-the-per-handle-filter.md +28 -0
  156. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-tests.md +25 -0
  157. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-is-read-and-when.md +35 -0
  158. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-you-set-up-once.md +40 -0
  159. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-where-the-tokens-live.md +27 -0
  160. package/dist/packages/mcp/assets/kb/docs/docs-login-with-x.md +26 -0
  161. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +42 -0
  162. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +34 -0
  163. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +35 -0
  164. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +33 -0
  165. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +32 -0
  166. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +32 -0
  167. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +30 -0
  168. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +31 -0
  169. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +44 -0
  170. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +31 -0
  171. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +37 -0
  172. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +41 -0
  173. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +34 -0
  174. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +31 -0
  175. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +34 -0
  176. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +32 -0
  177. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +37 -0
  178. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +41 -0
  179. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +32 -0
  180. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-bubble-map-popup.md +27 -0
  181. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-build-the-native-window.md +33 -0
  182. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-config-dexterm-popup-json.md +38 -0
  183. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-docking-macos.md +27 -0
  184. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-keys-in-the-popup.md +30 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +28 -0
  186. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-security-model.md +33 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-token-and-feeds-popups.md +38 -0
  188. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +30 -0
  189. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart.md +27 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-scan-cost-control.md +28 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-scan-keys-flag-command.md +34 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-scan-safety-four-layers.md +32 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-scan-states.md +27 -0
  194. package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +25 -0
  195. package/dist/packages/mcp/assets/kb/docs/docs-scan-triggers-and-score.md +37 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-scan.md +24 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-canary.md +30 -0
  198. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-task-1401-evaluation.md +35 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-latest-skill-care-run.md +33 -0
  200. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-links-only-larp-baseline-where-it-is-weak.md +35 -0
  201. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-not-measured.md +29 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-rug-verdict-rules.md +33 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-scorecard-source-and-rebuild.md +31 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-scorecard.md +27 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-screens-1-screener.md +35 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-screens-2-new-pairs.md +28 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-screens-3-watchlist.md +27 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-screens-4-analyst-tab.md +28 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-screens-bubble-map.md +32 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-screens-coin-images.md +32 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +44 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-screens-how-well-it-predicts-measured-held-out.md +46 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-screens-live-holder-and-rug-signals.md +51 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +26 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-screens-paper-ticket-and-positions.md +29 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-screens-the-look.md +29 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +36 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-screens-trending-social-and-kol-lists.md +34 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-screens.md +26 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-spectator-1-the-agent-activity-window-follow-mode.md +56 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-spectator-2-the-read-only-stream-spectate.md +42 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-spectator-3-save-and-replay-record-file-dexterm-spectate.md +41 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +39 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +36 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-spectator-quick-start.md +37 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-spectator-security-model.md +35 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-spectator-see-also.md +29 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-spectator-streaming-it.md +32 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +37 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-spectator-what-is-redacted.md +35 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +29 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +33 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +38 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-in-app-messages.md +42 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-reporting-a-problem.md +25 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting.md +23 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-voice-doctor.md +26 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-voice-it-clarifies-it-never-guesses.md +36 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-voice-latency.md +38 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +32 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-voice-privacy.md +26 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-voice-still-unverified.md +27 -0
  243. package/dist/packages/mcp/assets/kb/docs/docs-voice-the-voice-neural-by-default.md +48 -0
  244. package/dist/packages/mcp/assets/kb/docs/docs-voice-what-it-can-never-do.md +27 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-voice-what-you-can-say.md +38 -0
  246. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +25 -0
  247. package/dist/packages/mcp/src/app-help.js +123 -0
  248. package/dist/packages/mcp/src/firewall.js +2 -2
  249. package/dist/packages/mcp/src/index.js +8 -4
  250. package/dist/packages/mcp/src/paper-agent.js +92 -0
  251. package/dist/packages/mcp/src/server.js +261 -29
  252. package/dist/packages/mcp/src/spectator-sink.js +146 -5
  253. package/dist/packages/mcp/src/user-content.js +1 -0
  254. package/dist/packages/recorder/src/dev-history.js +129 -0
  255. package/dist/packages/recorder/src/index.js +1 -0
  256. package/dist/packages/recorder/src/recorder.js +23 -0
  257. package/dist/packages/recorder/src/schema.js +11 -0
  258. package/dist/packages/voice/src/stt/whisper.js +4 -2
  259. package/package.json +6 -5
@@ -29,6 +29,9 @@ import { makeFixtureState, makeFeedState, syncFeed, renderFrame, isScreenKey, ac
29
29
  import { mulberry32, tick, spawnPair, shock } from "./fixtures.js";
30
30
  import { createLiveFeed, createReplayFeed } from "./feed.js";
31
31
  import { LiveContext, INTERVALS, WATCH_REFRESH_MS, POSITIONS_REFRESH_MS, popupBubbleVM } from "./live/index.js";
32
+ import { FOLLOW_NAV_ACTIONS } from '../../mcp/src/index.js';
33
+ import { McpFollowSession } from "./mcp-follow.js";
34
+ import { AgentPaperSwitch } from "./agent-paper.js";
32
35
  import { TOKEN_REFRESH_MS, CandlePrefetcher } from "./live/token.js";
33
36
  import { PRESETS, presetSpec } from "./screener/presets.js";
34
37
  import { parseSortArg, SORT_KEYS } from "./screener/sort.js";
@@ -45,6 +48,7 @@ import { copyAlertToDelivery } from "./alerts/format.js";
45
48
  import { readTelegramCreds, readDiscordCreds } from "./alerts/creds.js";
46
49
  import { launchIntelCells, launchRetryable } from "./live/intel.js";
47
50
  import { AnalystSession } from "./analyst/session.js";
51
+ import { analystCliOnPath, parseEngineChoice, resolveEngine } from "./analyst/codex.js";
48
52
  import { resolveAnalystResults } from "./analyst/results.js";
49
53
  import { parseScanCommand, SCAN_FLAGS, checkScanFlag, SCAN_HELP } from "./scan/command.js";
50
54
  import { parseWalletCommand, WALLET_HELP } from "./wallet-command.js";
@@ -100,6 +104,21 @@ export function appStateBlock(state) {
100
104
  alerts, // booleans only — never a token, webhook or key
101
105
  };
102
106
  }
107
+ /**
108
+ * task-1443: the reduced screen snapshot a terminal following an EXTERNAL agent (`--spectate-mcp`) publishes for
109
+ * that agent's `dexterm mcp` server: what is on screen plus a VIEW-ONLY nav allow-list. Never the watchlist,
110
+ * tickets, positions or app settings, and only the four follow nav action types (no watchlist add, no ticket).
111
+ */
112
+ export function followSnapshot(state) {
113
+ const ss = buildScreenState(state);
114
+ const a = ss.actions;
115
+ return {
116
+ screen: ss.screen, selected: ss.selected, selectedDetail: ss.selectedDetail, filter: ss.filter, sort: ss.sort,
117
+ visible: ss.visible, visibleMints: ss.visibleMints,
118
+ actions: { enabled: a.enabled, mints: a.mints, feeds: a.feeds, sortKeys: a.sortKeys, screens: a.screens, workspaces: [], themes: [], types: [...FOLLOW_NAV_ACTIONS] },
119
+ now: ss.now,
120
+ };
121
+ }
103
122
  export function buildScreenState(state) {
104
123
  const live = state.live;
105
124
  const sc = state.screener;
@@ -272,6 +291,7 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
272
291
  dexterm alerts status|enable|disable|set-telegram|set-discord|test where rule alerts are delivered (desktop / your Telegram / Discord; creds stay local, never logged)
273
292
  dexterm watch [--interval S] [--once] [--replay FILE] run your rules headless and push alerts when the terminal is closed (no signer, proposals only)
274
293
  dexterm record run|install|uninstall|status keep the history recorder fed with no TUI open, as a user-level launchd agent (no network beyond the data layer; 'dexterm record help')
294
+ dexterm larp-detected --out DIR [--db PATH] build timestamped high-risk follow-up drafts/cards from recorder history; never posts
275
295
  dexterm --fixtures interactive demo on seeded fixtures
276
296
  dexterm --screen <name> one-shot frame to stdout (live)
277
297
  dexterm --screen <name> --fixtures one-shot frame from fixtures
@@ -284,6 +304,11 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
284
304
  --sort KEY[:asc|desc] ${SORT_KEYS.map((k) => k.key).join(' | ')}
285
305
  --preset NAME ${PRESETS.map((p) => p.id).join(' | ')}
286
306
  --record FILE save every live response (and the PumpPortal session) to FILE (replay it with --replay)
307
+ --spectate-mcp follow an MCP agent's allowed navigation in this terminal; Ctrl+P pauses, Ctrl+K stops,
308
+ and any normal user keypress takes control (refused actions never move the UI)
309
+ --agent-paper MANDATE_ID (experimental, with --spectate-mcp) let that MCP agent PAPER-trade inside a mandate you
310
+ armed: simulated fills only, never signs or sends, never raises a cap; Ctrl+P / Ctrl+K /
311
+ any key also pause, stop or take over its paper trading
287
312
  --max-requests N hard cap on live requests for this run (default: none)
288
313
  --max-credits N hard cap on Helius credits for this run (default: 5000)
289
314
  --mint MINT|LINK token to open on the token screen (a CA or a dexscreener / pump.fun / solscan / birdeye link)
@@ -298,6 +323,8 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
298
323
  --selected N highlighted row / token index
299
324
  --seed N fixture PRNG seed (default 7)
300
325
  --no-ai hide the analyst panel on the token screen
326
+ --analyst-engine E auto | claude | codex: which of YOUR installed CLIs runs the analyst (default auto = claude
327
+ if installed, else codex; DEXTERM_ANALYST_ENGINE or config analyst.engine too; /engine in chat)
301
328
 
302
329
  --intensity LEVEL off | normal | fever (default fever: rain, trails, glitch, blink, scanlines at 30 fps;
303
330
  normal: 15 fps, sparser rain, no idle aberration; off = calm: neon theme, no motion)
@@ -385,13 +412,14 @@ export function parseArgs(argv) {
385
412
  screen: null, fixtures: false, replay: null, record: null, filter: null, sort: null, preset: null, maxRequests: 0, maxCredits: 0, db: null,
386
413
  mint: null, interval: '1h', listen: 20, verbose: false,
387
414
  width: null, height: null, html: null, export: null, theme: DEFAULT_THEME, selected: 0, seed: 7,
388
- noAi: false, title: null, plain: false, ansi: false, color: null, tz: null,
415
+ noAi: false, analystEngine: null, title: null, plain: false, ansi: false, color: null, tz: null,
389
416
  calm: false, intensity: null, fps: null, frame: 0, t: 1, event: null, ai: null, mood: null, activity: null, noBoot: false, fit: null, fontPx: null, asciiRain: false, noImages: false,
390
417
  scan: false, scanConfig: {},
391
418
  noRecorder: false,
392
419
  sound: false,
393
420
  trade: [], live: true,
394
421
  workspace: null, priceMode: null,
422
+ spectateMcp: false, agentPaper: null,
395
423
  help: false, version: false, unknown: [],
396
424
  };
397
425
  for (let i = 0; i < argv.length; i++) {
@@ -480,6 +508,13 @@ export function parseArgs(argv) {
480
508
  }
481
509
  else if (k === '--no-ai')
482
510
  a.noAi = true;
511
+ else if (k === '--analyst-engine') {
512
+ const s = str();
513
+ const e = parseEngineChoice(s);
514
+ if (!e)
515
+ throw new Error(`unknown analyst engine ${s} (expected auto|claude|codex)`);
516
+ a.analystEngine = e;
517
+ }
483
518
  else if (k === '--calm')
484
519
  a.calm = true;
485
520
  else if (k === '--intensity') {
@@ -573,6 +608,16 @@ export function parseArgs(argv) {
573
608
  throw new Error(`--price-mode expects price|mcap, got ${JSON.stringify(s.slice(0, 20))}`);
574
609
  a.priceMode = s;
575
610
  }
611
+ // task-1421: follow an agent driving over MCP (the running terminal moves with it; a read-only stream opens).
612
+ else if (k === '--spectate-mcp')
613
+ a.spectateMcp = true;
614
+ // task-1443: agent PAPER trading inside a mandate the user armed (validated against the plans journal at start).
615
+ else if (k === '--agent-paper') {
616
+ const s = str();
617
+ if (!/^[A-Za-z0-9_-]{1,64}$/.test(s))
618
+ throw new Error(`--agent-paper expects a mandate id (see \`dexterm mandate list\`), got ${JSON.stringify(s.slice(0, 20))}`);
619
+ a.agentPaper = s;
620
+ }
576
621
  else if (k === '--workspace') {
577
622
  const s = str();
578
623
  if (!/^[a-z][a-z0-9-]{0,39}(\/[a-z][a-z0-9-]{0,39})?$/.test(s))
@@ -610,6 +655,10 @@ export function parseArgs(argv) {
610
655
  throw new Error('--scan needs live data or a recording (drop --fixtures)');
611
656
  if (a.scan && a.screen)
612
657
  throw new Error('--scan is interactive (drop --screen)');
658
+ if (a.agentPaper && !a.spectateMcp)
659
+ throw new Error('--agent-paper needs --spectate-mcp (every agent paper trade must be visible in this terminal)');
660
+ if (a.agentPaper && a.db === ':memory:')
661
+ throw new Error('--agent-paper needs an on-disk --db (the agent books into the same paper wallet)');
613
662
  return a;
614
663
  }
615
664
  /**
@@ -790,6 +839,11 @@ export async function main(argv, io = process, proc = process) {
790
839
  const { backtestCommand } = await import("./backtest-cmd.js");
791
840
  return backtestCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
792
841
  }
842
+ // Local-only marketing evidence pipeline (task-1433): drafts and cards for human review, never posts.
843
+ if (argv[0] === 'larp-detected') {
844
+ const { larpDetectedCommand } = await import("./larp-detected.js");
845
+ return larpDetectedCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
846
+ }
793
847
  // `dexterm kol …` → KOL lists on the shared DB (task-1330): list/create/add/remove/mute/unmute/enable/disable/import.
794
848
  if (argv[0] === 'kol')
795
849
  return kolCommand(argv.slice(1), io);
@@ -1006,6 +1060,8 @@ export async function main(argv, io = process, proc = process) {
1006
1060
  });
1007
1061
  recorder.attach(feed.dl.store);
1008
1062
  ctx.recorder = recorder;
1063
+ // 0.1.5 dev history: PumpPortal / RugCheck creators of new pairs go into the creator index too.
1064
+ ctx.newpairs.onCreator = (mint, creator, source, createdAt) => { recorder.recordCreator(mint, creator, source, undefined, createdAt); };
1009
1065
  }
1010
1066
  if (args.verbose)
1011
1067
  io.stderr.write(`dexterm: rpc ${rpc.provider} (${rpc.basis}) · db ${dbPath} · recorder ${ctx.recorder ? 'on' : 'off'} · live trading ${args.live && !io.env.DEXTERM_OFFLINE ? 'available' : 'off'}\n`);
@@ -2626,6 +2682,50 @@ export function applyScanAnalystAction(state, raw) {
2626
2682
  const res = validateAnalystAction(raw, { ...allow, types: SCAN_ACTION_TYPES });
2627
2683
  return { ok: false, error: res.ok ? 'AI scan mode is observe-only' : res.error };
2628
2684
  }
2685
+ /**
2686
+ * The analyst engine for a run (0.1.5): `--analyst-engine`, then DEXTERM_ANALYST_ENGINE, then config
2687
+ * `analyst.engine`, then auto (claude when installed, else codex). Only the user picks it; models cannot.
2688
+ */
2689
+ export function analystEngineFor(flag, env, configured, has = (b) => analystCliOnPath(b, env)) {
2690
+ const choice = parseEngineChoice(flag) ?? parseEngineChoice(env.DEXTERM_ANALYST_ENGINE) ?? parseEngineChoice(configured) ?? 'auto';
2691
+ return resolveEngine(choice, has);
2692
+ }
2693
+ /** `/engine [auto|claude|codex]` in the chat box (0.1.5). Null when the input is not this command. */
2694
+ export function parseEngineCommand(q) {
2695
+ const m = /^\/engine(?:\s+(\S+))?\s*$/i.exec(q.trim());
2696
+ if (!m)
2697
+ return null;
2698
+ if (!m[1])
2699
+ return { show: true };
2700
+ const c = parseEngineChoice(m[1]);
2701
+ return c ? { choice: c } : { error: `unknown analyst engine "${m[1].slice(0, 16)}" (use /engine auto|claude|codex)` };
2702
+ }
2703
+ /**
2704
+ * Apply `/engine`: switch the CLI for NEW conversations this run (cached per-coin chats are dropped so the next
2705
+ * `a` starts on the chosen engine; a running turn finishes where it started). Returns the line to show.
2706
+ */
2707
+ export function engineCommand(state, cmd, env, has = (b) => analystCliOnPath(b, env)) {
2708
+ const installed = ['claude', 'codex'].filter((b) => has(b));
2709
+ const list = installed.length ? installed.join(' + ') : 'neither claude nor codex';
2710
+ if ('error' in cmd)
2711
+ return cmd.error;
2712
+ const cur = state.analystEngine ?? 'claude';
2713
+ if ('show' in cmd)
2714
+ return `analyst engine: ${cur} · installed: ${list} · /engine claude|codex|auto switches (config analyst.engine keeps it)`;
2715
+ const next = resolveEngine(cmd.choice, has);
2716
+ if (cmd.choice !== 'auto' && !has(next))
2717
+ return `${next} is not installed (installed: ${list}); ${next === 'codex' ? 'npm i -g @openai/codex && codex login' : 'install Claude Code, then run claude once to log in'}`;
2718
+ if (next !== cur) {
2719
+ for (const [k, sess] of state.analystByMint ?? []) {
2720
+ if (!sess.busy)
2721
+ state.analystByMint.delete(k);
2722
+ }
2723
+ if (state.analyst && !state.analyst.busy && state.analyst.engine !== next)
2724
+ state.analyst = null;
2725
+ }
2726
+ state.analystEngine = next;
2727
+ return `analyst engine: ${next} (your own ${next} CLI and login) for new conversations · set analyst.engine in config.json to keep it`;
2728
+ }
2629
2729
  /**
2630
2730
  * The ONE way a scan-driven analyst conversation is built (task-1324; the loop and the safety tests both
2631
2731
  * use it): the locked coin as the selected coin, `actionTypes: []` (observe-only at the validator, the
@@ -2639,7 +2739,7 @@ export function createScanSession(state, mint, symbol, hooks = {}, deps = {}) {
2639
2739
  const type = typeof raw.type === 'string' ? raw.type.replace(/[^a-z_]/gi, '').slice(0, 24) : '?';
2640
2740
  hooks.onBlocked?.(type, r.error ?? 'refused');
2641
2741
  };
2642
- return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true }, hooks.onChange ?? (() => { }), deps);
2742
+ return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true, ...(state.analystEngine === 'codex' ? { engine: 'codex' } : {}) }, hooks.onChange ?? (() => { }), deps);
2643
2743
  }
2644
2744
  /**
2645
2745
  * Resolve a mint to an openable seed from what the app already knows (screener view → new pairs →
@@ -2895,6 +2995,76 @@ export function applyAnalystAction(state, raw, stack) {
2895
2995
  times.push(now);
2896
2996
  return { ok: true, label: res.label };
2897
2997
  }
2998
+ /**
2999
+ * task-1421: move the REAL terminal to mirror an agent's navigation (follow mode, on the owner's own
3000
+ * machine). This is the same VIEW-ONLY navigation the analyst / go-to path uses — open a token, switch
3001
+ * screen/feed, set a filter, highlight a row, open the PAPER ticket editor. It never trades, sizes, confirms
3002
+ * or signs, and the key loop flips control to the user before any of it runs, so the human always wins the
3003
+ * screen. Unlike the analyst boundary it does not re-check the on-screen allow-list: the agent already made
3004
+ * the call on its own server and this only reflects where it went. A no-op when live navigation is off.
3005
+ */
3006
+ export function applyFollowNav(state, nav, stack) {
3007
+ const sc = state.screener;
3008
+ const live = state.live && state.screener.source !== 'fixtures' ? state.live : null;
3009
+ const toScreen = (next) => { if (state.screen !== next) {
3010
+ stack.push(state.screen);
3011
+ state.screen = next;
3012
+ } };
3013
+ if (nav.mint && (!nav.screen || nav.screen === 'token')) {
3014
+ if (!live)
3015
+ return;
3016
+ try {
3017
+ live.openToken(seedForMint(state, live, nav.mint));
3018
+ toScreen('token');
3019
+ }
3020
+ catch { /* a stale mint never breaks follow */ }
3021
+ return;
3022
+ }
3023
+ if (nav.screen === 'ticket') {
3024
+ if (!live || !nav.mint)
3025
+ return;
3026
+ try {
3027
+ const seed = seedForMint(state, live, nav.mint);
3028
+ live.openTicket({ mint: seed.mint, symbol: seed.symbol, name: seed.name }, nav.side === 'sell' ? 'sell' : 'buy');
3029
+ toScreen('ticket');
3030
+ }
3031
+ catch { /* ignore */ }
3032
+ return;
3033
+ }
3034
+ if (nav.screen === 'watchlist') {
3035
+ if (live && nav.mint) {
3036
+ try {
3037
+ if (!live.watch.has(nav.mint))
3038
+ live.watch.add(LiveContext.candidate(seedForMint(state, live, nav.mint)));
3039
+ }
3040
+ catch { /* ignore */ }
3041
+ }
3042
+ toScreen('watchlist');
3043
+ return;
3044
+ }
3045
+ if (nav.feed || nav.filter || nav.highlight || nav.screen === 'screener') {
3046
+ toScreen('screener');
3047
+ if (nav.feed) {
3048
+ if (setPreset(sc, nav.feed) && state.feed)
3049
+ state.feed.setPreset(sc.preset);
3050
+ }
3051
+ if (nav.filter) {
3052
+ try {
3053
+ setFilter(sc, nav.filter);
3054
+ }
3055
+ catch { /* an invalid agent filter is ignored */ }
3056
+ }
3057
+ if (nav.highlight) {
3058
+ const i = screenerView(sc).findIndex((r) => r.mint === nav.highlight);
3059
+ if (i >= 0)
3060
+ state.selected = i;
3061
+ }
3062
+ state.selected = clampSelection(sc, state.selected);
3063
+ return;
3064
+ }
3065
+ if (nav.screen && isScreenKey(nav.screen))
3066
+ toScreen(nav.screen);
3067
+ }
2898
3068
  /**
2899
3069
  * task-1413: apply an accepted tier-(b) config proposal through the journaled path as actor `analyst`
2900
3070
  * (so `ctrl+z` undoes it), creating the parent section when absent (exactly like the `$` / `,` handlers),
@@ -3393,7 +3563,14 @@ export function terminalGuard(io, onFatal = () => { }, proc = process, hooks = {
3393
3563
  proc.exit(1);
3394
3564
  };
3395
3565
  const onExit = () => restore();
3396
- const onSignal = (sig) => { restore(); proc.exit(sig === 'SIGTERM' ? 143 : 129); };
3566
+ const onSignal = (sig) => {
3567
+ restore();
3568
+ try {
3569
+ hooks.onSignal?.();
3570
+ }
3571
+ catch { /* cleanup is best-effort; the exit still happens */ }
3572
+ proc.exit(sig === 'SIGTERM' ? 143 : 129);
3573
+ };
3397
3574
  const onTerm = () => onSignal('SIGTERM');
3398
3575
  const onHup = () => onSignal('SIGHUP');
3399
3576
  let installed = false;
@@ -3430,6 +3607,8 @@ export function fpsFor(args, intensity) {
3430
3607
  const SYNC_ON = '\x1b[?2026h';
3431
3608
  const SYNC_OFF = '\x1b[?2026l';
3432
3609
  export function interactive(state, io, depth, args, proc = process) {
3610
+ // 0.1.5: the analyst engine for new conversations (the user's own claude or codex CLI; never a shipped key).
3611
+ state.analystEngine ??= analystEngineFor(args.analystEngine, io.env, state.custom?.config.analyst?.engine);
3433
3612
  const out = io.stdout;
3434
3613
  const inp = io.stdin;
3435
3614
  const rnd = mulberry32(99);
@@ -3730,6 +3909,11 @@ export function interactive(state, io, depth, args, proc = process) {
3730
3909
  let timer = null;
3731
3910
  let lastRefresh = 0;
3732
3911
  let done = false;
3912
+ // task-1421: the MCP follow session (set below when `--spectate-mcp` is on). Declared here so the quit /
3913
+ // terminal-guard teardown closures can stop it.
3914
+ let followSession = null;
3915
+ // task-1443: `--agent-paper <mandate-id>` (with --spectate-mcp) lets that agent PAPER-trade inside the mandate.
3916
+ const agentPaper = args.spectateMcp && args.agentPaper ? new AgentPaperSwitch({ env: io.env, mandateId: args.agentPaper, db: args.db ?? defaultDbPath(io.env) }) : null;
3733
3917
  // Idle economy (task-1303, retuned for task-1319 AC3): between events the ambient rain redraws at
3734
3918
  // IDLE_FPS instead of the full frame cap, cutting idle CPU. A keypress opens a KEY_WAKE_S full-rate
3735
3919
  // window (`wakeUntil`) so input and transitions stay smooth; data landings paint at once on their own
@@ -3758,10 +3942,13 @@ export function interactive(state, io, depth, args, proc = process) {
3758
3942
  say(`trade moment failed: ${describeError(e)}`, 'warn');
3759
3943
  }
3760
3944
  };
3761
- const guard = terminalGuard(io, () => { done = true; prefetcher?.close(); if (timer)
3762
- clearInterval(timer); state.analyst?.cancel(); if (state.analystByMint)
3945
+ const teardown = () => { done = true; prefetcher?.close(); if (timer)
3946
+ clearInterval(timer); void followSession?.stop(); agentPaper?.stop(); state.analyst?.cancel(); if (state.analystByMint)
3763
3947
  for (const s of state.analystByMint.values())
3764
- s.cancel(); state.popups?.shutdown(); voice?.disable(); images?.close(); live?.close(); feed?.close(); }, proc, { beforeLeave: () => layer?.teardown() ?? '' });
3948
+ s.cancel(); state.popups?.shutdown(); voice?.disable(); images?.close(); live?.close(); feed?.close(); };
3949
+ // task-1443: SIGTERM/SIGHUP (a closed terminal window) runs the same teardown as a fatal error, so the agent-paper
3950
+ // rendezvous, the spectator rendezvous and the --record file never outlive the terminal that owns them.
3951
+ const guard = terminalGuard(io, teardown, proc, { beforeLeave: () => layer?.teardown() ?? '', onSignal: teardown });
3765
3952
  let lastTokenRefresh = 0;
3766
3953
  let lastWatchRefresh = 0;
3767
3954
  let lastPositionsRefresh = 0;
@@ -3930,6 +4117,41 @@ export function interactive(state, io, depth, args, proc = process) {
3930
4117
  // task-1326: an image landing (decoded, or found missing) repaints at most once per macrotask.
3931
4118
  if (images)
3932
4119
  images.onChange = () => scheduleDraw();
4120
+ // task-1421: `--spectate-mcp` → follow an agent driving over MCP. The rendezvous lets a `dexterm mcp`
4121
+ // server the external client spawned emit here; we tail it, move the terminal for navigation calls, and
4122
+ // the owner's controls (ctrl+p pause/resume · ctrl+k stop · any key = take over) are handled in onInput.
4123
+ // A read-only, loopback, token-gated stream opens too (`dexterm spectate --info`). Off (and zero-cost)
4124
+ // unless the flag is set; nothing it folds can arm, sign, confirm, send or raise a cap.
4125
+ if (args.spectateMcp) {
4126
+ followSession = new McpFollowSession({
4127
+ env: io.env,
4128
+ stream: true,
4129
+ onNav: (nav) => {
4130
+ const before = state.screen;
4131
+ try {
4132
+ applyFollowNav(state, nav, stack);
4133
+ }
4134
+ catch { /* a stale mint never breaks follow */ }
4135
+ if (state.screen !== before) {
4136
+ try {
4137
+ startTransition();
4138
+ }
4139
+ catch {
4140
+ transition = null;
4141
+ }
4142
+ }
4143
+ scheduleDraw();
4144
+ },
4145
+ onStatus: (line) => { say(line, 'ai', 4); scheduleDraw(); },
4146
+ onEvent: () => scheduleDraw(),
4147
+ onControl: agentPaper ? (st) => agentPaper.control(st) : undefined,
4148
+ snapshot: () => followSnapshot(state),
4149
+ });
4150
+ void followSession.start();
4151
+ say('following agents over MCP · ctrl+p pause · ctrl+k stop · any key takes over', 'ai', 6);
4152
+ if (agentPaper)
4153
+ say(agentPaper.start(), 'ai', 8);
4154
+ }
3933
4155
  // Launch a real Claude analyst session when a key asked for one (applyAction cannot spawn). A
3934
4156
  // `question` means start it as a chat turn, not a paid structured skill run (task-1311, QA B3).
3935
4157
  const beginAnalyst = () => {
@@ -3996,7 +4218,7 @@ export function interactive(state, io, depth, args, proc = process) {
3996
4218
  maybeSpeakVoiceAnswer(session); // task-1282: speak a voice-initiated answer when it settles
3997
4219
  scheduleDraw();
3998
4220
  };
3999
- const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction }, onChange);
4221
+ const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction, ...(state.analystEngine === 'codex' ? { engine: 'codex' } : {}) }, onChange);
4000
4222
  if (p.question)
4001
4223
  session.ask(p.question, buildScreenState(state));
4002
4224
  else
@@ -4919,6 +5141,8 @@ export function interactive(state, io, depth, args, proc = process) {
4919
5141
  prefetcher?.close();
4920
5142
  if (timer)
4921
5143
  clearInterval(timer);
5144
+ void followSession?.stop();
5145
+ agentPaper?.stop();
4922
5146
  guard.restore();
4923
5147
  guard.uninstall();
4924
5148
  cancelAllAnalysts();
@@ -4973,6 +5197,27 @@ export function interactive(state, io, depth, args, proc = process) {
4973
5197
  quit();
4974
5198
  return;
4975
5199
  }
5200
+ // task-1421 follow-mode controls (only while following an MCP agent). ctrl+p pauses / resumes the
5201
+ // terminal mirroring; ctrl+k stops following (removes the rendezvous so the server stops emitting);
5202
+ // ANY other key while the agent is driving hands control to the user — the human and the agent never
5203
+ // fight over the screen. The control keys are consumed; the take-over key still does its own job.
5204
+ if (followSession) {
5205
+ if (key.ctrl && key.ch === 'k') {
5206
+ followSession.kill();
5207
+ draw();
5208
+ continue;
5209
+ }
5210
+ if (key.ctrl && key.ch === 'p') {
5211
+ if (followSession.isPaused)
5212
+ followSession.resume();
5213
+ else
5214
+ followSession.pause();
5215
+ draw();
5216
+ continue;
5217
+ }
5218
+ if (followSession.controlState === 'agent')
5219
+ followSession.userKey(); // take over, then fall through
5220
+ }
4976
5221
  // task-1413: a pending analyst config PROPOSAL owns the next key. `y` accepts and journals it as
4977
5222
  // actor 'analyst' (ctrl+z undoes); any other key dismisses it. A stray key never persists a change.
4978
5223
  // Two guards (defect c): the proposal does NOT steal a key while a TEXT INPUT is open — the GO-TO
@@ -5496,6 +5741,14 @@ export function interactive(state, io, depth, args, proc = process) {
5496
5741
  draw();
5497
5742
  continue;
5498
5743
  }
5744
+ // 0.1.5: `/engine [auto|claude|codex]` switches the analyst CLI for new conversations; never sent to a model.
5745
+ const en0 = q.startsWith('/') ? parseEngineCommand(q) : null;
5746
+ if (en0) {
5747
+ state.aiInput = '';
5748
+ say(engineCommand(state, en0, io.env), typeof en0 === 'object' && 'error' in en0 ? 'warn' : 'info', 6);
5749
+ draw();
5750
+ continue;
5751
+ }
5499
5752
  // task-1324: `/scan …` in the chat input is a command, never sent to claude.
5500
5753
  const sc0 = q.startsWith('/') ? parseScanCommand(q) : null;
5501
5754
  if (sc0) {