dexterm-cli 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/agents/src/index.js +2 -0
- package/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/spectator.js +54 -0
- package/dist/packages/agents/src/story.js +532 -0
- package/dist/packages/agents/src/tiers.js +28 -2
- package/dist/packages/agents/src/tools.js +31 -2
- package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
- package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
- package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
- package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
- package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
- package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
- package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
- package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
- package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
- package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
- package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
- package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
- package/dist/packages/autotrade/src/account.js +74 -0
- package/dist/packages/autotrade/src/backtest.js +357 -0
- package/dist/packages/autotrade/src/basis-text.js +32 -0
- package/dist/packages/autotrade/src/chat/approvals.js +484 -0
- package/dist/packages/autotrade/src/chat/audit.js +140 -0
- package/dist/packages/autotrade/src/chat/config.js +93 -0
- package/dist/packages/autotrade/src/chat/connector.js +24 -0
- package/dist/packages/autotrade/src/chat/control.js +103 -0
- package/dist/packages/autotrade/src/chat/fake.js +100 -0
- package/dist/packages/autotrade/src/chat/format.js +71 -0
- package/dist/packages/autotrade/src/chat/hub.js +187 -0
- package/dist/packages/autotrade/src/chat/index.js +18 -0
- package/dist/packages/autotrade/src/chat/notify.js +292 -0
- package/dist/packages/autotrade/src/chat/pairing.js +46 -0
- package/dist/packages/autotrade/src/chat/redact.js +48 -0
- package/dist/packages/autotrade/src/chat/secrets.js +99 -0
- package/dist/packages/autotrade/src/chat/service.js +248 -0
- package/dist/packages/autotrade/src/chat/telegram.js +290 -0
- package/dist/packages/autotrade/src/desk-executor.js +188 -0
- package/dist/packages/autotrade/src/engine.js +387 -0
- package/dist/packages/autotrade/src/execution-port.js +97 -0
- package/dist/packages/autotrade/src/fleet-config.js +169 -0
- package/dist/packages/autotrade/src/fleet-control.js +276 -0
- package/dist/packages/autotrade/src/fleet.js +639 -0
- package/dist/packages/autotrade/src/index.js +53 -0
- package/dist/packages/autotrade/src/ledger.js +108 -0
- package/dist/packages/autotrade/src/live-source.js +182 -0
- package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
- package/dist/packages/autotrade/src/monitor.js +190 -0
- package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
- package/dist/packages/autotrade/src/paper-accounts.js +72 -0
- package/dist/packages/autotrade/src/replay.js +198 -0
- package/dist/packages/autotrade/src/runner-config.js +99 -0
- package/dist/packages/autotrade/src/runner.js +872 -0
- package/dist/packages/autotrade/src/safety.js +243 -0
- package/dist/packages/autotrade/src/session.js +269 -0
- package/dist/packages/autotrade/src/sessions.js +503 -0
- package/dist/packages/autotrade/src/store.js +250 -0
- package/dist/packages/autotrade/src/strategy.js +256 -0
- package/dist/packages/autotrade/src/trade-ledger.js +241 -0
- package/dist/packages/autotrade/src/yolo.js +140 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +1 -1
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +15 -8
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
- package/dist/packages/cli/src/agent-cmd.js +6 -4
- package/dist/packages/cli/src/agent-spectator.js +17 -1
- package/dist/packages/cli/src/ai-cmd.js +412 -0
- package/dist/packages/cli/src/analyst/backend.js +105 -15
- package/dist/packages/cli/src/analyst/codex.js +14 -9
- package/dist/packages/cli/src/analyst/larp-gauge.js +60 -0
- package/dist/packages/cli/src/analyst/ollama.js +263 -0
- package/dist/packages/cli/src/analyst/session.js +17 -3
- package/dist/packages/cli/src/app-help-gen.js +51 -15
- package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
- package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
- package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
- package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
- package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
- package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
- package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
- package/dist/packages/cli/src/autotrade-cmd.js +168 -0
- package/dist/packages/cli/src/backtest-cmd.js +13 -2
- package/dist/packages/cli/src/display.js +208 -23
- package/dist/packages/cli/src/doctor.js +62 -3
- package/dist/packages/cli/src/export.js +10 -2
- package/dist/packages/cli/src/index.js +1 -1
- package/dist/packages/cli/src/ledger-cmd.js +288 -0
- package/dist/packages/cli/src/live/chart-fills.js +46 -0
- package/dist/packages/cli/src/live/dev-history.js +31 -15
- package/dist/packages/cli/src/live/index.js +28 -90
- package/dist/packages/cli/src/live/screens/newpairs-live.js +1 -1
- package/dist/packages/cli/src/live/screens/token-live.js +48 -15
- package/dist/packages/cli/src/live/screens/watchlist-live.js +1 -1
- package/dist/packages/cli/src/live/trade.js +30 -453
- package/dist/packages/cli/src/live/watchlist.js +1 -1
- package/dist/packages/cli/src/main.js +290 -199
- package/dist/packages/cli/src/mcp-cmd.js +7 -1
- package/dist/packages/cli/src/mcp-follow.js +103 -6
- package/dist/packages/cli/src/plan-cmd.js +15 -5
- package/dist/packages/cli/src/pnl-app.js +31 -21
- package/dist/packages/cli/src/pnl-card.js +158 -21
- package/dist/packages/cli/src/popup.js +38 -6
- package/dist/packages/cli/src/record-cmd.js +141 -20
- package/dist/packages/cli/src/scan/ui.js +7 -1
- package/dist/packages/cli/src/screens/agent-hud.js +773 -0
- package/dist/packages/cli/src/screens/analyst-session.js +11 -4
- package/dist/packages/cli/src/screens/analyst.js +3 -2
- package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
- package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
- package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
- package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
- package/dist/packages/cli/src/screens/brain.js +634 -0
- package/dist/packages/cli/src/screens/newpairs.js +1 -1
- package/dist/packages/cli/src/screens/overlay.js +5 -2
- package/dist/packages/cli/src/screens/plans.js +5 -17
- package/dist/packages/cli/src/screens/pnl-chip.js +144 -0
- package/dist/packages/cli/src/screens/positions.js +6 -28
- package/dist/packages/cli/src/screens/screener.js +1 -0
- package/dist/packages/cli/src/screens/ticket.js +38 -205
- package/dist/packages/cli/src/screens/token.js +18 -9
- package/dist/packages/cli/src/screens/trending.js +1 -1
- package/dist/packages/cli/src/state.js +88 -18
- package/dist/packages/cli/src/telegram-cmd.js +451 -0
- package/dist/packages/cli/src/trade.js +3 -128
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/wallet-cmd.js +39 -340
- package/dist/packages/cli/src/wallet-command.js +8 -11
- package/dist/packages/cli/src/widgets.js +0 -12
- package/dist/packages/custom/src/config.js +20 -2
- package/dist/packages/custom/src/schema.js +41 -10
- package/dist/packages/custom/src/workspace.js +3 -3
- package/dist/packages/data/src/basis.js +191 -0
- package/dist/packages/data/src/detail.js +53 -2
- package/dist/packages/data/src/index.js +2 -14
- package/dist/packages/data/src/larp/score.js +220 -0
- package/dist/packages/data/src/larp/x-account.js +316 -0
- package/dist/packages/data/src/store.js +14 -5
- package/dist/packages/data/src/trade/agent/paper-desk.js +300 -23
- package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
- package/dist/packages/data/src/trade/bridge.js +4 -102
- package/dist/packages/data/src/trade/ledger/export.js +101 -0
- package/dist/packages/data/src/trade/ledger/file.js +135 -0
- package/dist/packages/data/src/trade/ledger/fold.js +191 -0
- package/dist/packages/data/src/trade/ledger/index.js +9 -0
- package/dist/packages/data/src/trade/ledger/schema.js +83 -0
- package/dist/packages/data/src/trade/ledger/sources.js +307 -0
- package/dist/packages/data/src/trade/ledger/stats.js +123 -0
- package/dist/packages/data/src/trade/ledger/tags.js +116 -0
- package/dist/packages/data/src/trade/ledger/view.js +51 -0
- package/dist/packages/data/src/trade/plans/engine.js +7 -0
- package/dist/packages/data/src/trade/plans/index.js +0 -1
- package/dist/packages/data/src/trade/plans/mandate.js +28 -15
- package/dist/packages/data/src/trade/plans/service.js +4 -28
- package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
- package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/dev-history.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/larp-check.md +5 -1
- package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
- package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
- package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +6 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +2 -1
- package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
- package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +7 -3
- package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +9 -7
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +60 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +0 -2
- package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-larp.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +4 -13
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +40 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +10 -7
- package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-agent-hud-spectator-v2.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +46 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
- package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
- package/dist/packages/mcp/src/app-help.js +1 -1
- package/dist/packages/mcp/src/autotrade-tools.js +944 -0
- package/dist/packages/mcp/src/backtest-requests.js +71 -0
- package/dist/packages/mcp/src/engine/config.js +159 -0
- package/dist/packages/mcp/src/engine/index.js +10 -0
- package/dist/packages/mcp/src/engine/loop.js +101 -0
- package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
- package/dist/packages/mcp/src/engine/ollama.js +258 -0
- package/dist/packages/mcp/src/engine/probe.js +110 -0
- package/dist/packages/mcp/src/engine/reason.js +35 -0
- package/dist/packages/mcp/src/engine/restrictions.js +111 -0
- package/dist/packages/mcp/src/index.js +7 -1
- package/dist/packages/mcp/src/ledger-tools.js +346 -0
- package/dist/packages/mcp/src/paper-agent.js +5 -2
- package/dist/packages/mcp/src/server.js +285 -36
- package/dist/packages/mcp/src/spectator-sink.js +85 -0
- package/dist/packages/packs/src/backtest.js +3 -1
- package/dist/packages/packs/src/index.js +1 -0
- package/dist/packages/packs/src/larp-score-backtest.js +119 -0
- package/dist/packages/popup/src/pages/chart-lib.js +169 -35
- package/dist/packages/popup/src/pages/chart.css +18 -0
- package/dist/packages/popup/src/pages/chart.html +6 -1
- package/dist/packages/popup/src/pages/chart.js +136 -10
- package/dist/packages/popup/src/pages/spectator.css +45 -0
- package/dist/packages/popup/src/pages/spectator.html +1 -0
- package/dist/packages/popup/src/pages/spectator.js +69 -0
- package/dist/packages/popup/src/protocol.js +4 -0
- package/dist/packages/popup/src/server.js +9 -1
- package/dist/packages/recorder/src/dev-history.js +7 -3
- package/dist/packages/recorder/src/index.js +2 -0
- package/dist/packages/recorder/src/recorder.js +63 -6
- package/dist/packages/recorder/src/retention.js +100 -0
- package/dist/packages/recorder/src/schema.js +34 -1
- package/dist/packages/recorder/src/ticks.js +237 -0
- package/dist/packages/tui/src/canvas.js +7 -0
- package/dist/packages/tui/src/fmt.js +53 -0
- package/dist/packages/tui/src/index.js +1 -0
- package/dist/packages/tui/src/keymap.js +25 -1
- package/dist/packages/tui/src/markers.js +219 -0
- package/dist/packages/tui/src/widgets.js +158 -6
- package/package.json +2 -1
- package/dist/packages/data/src/trade/caps.js +0 -323
- package/dist/packages/data/src/trade/executor.js +0 -933
- package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
- package/dist/packages/data/src/trade/rpc-exec.js +0 -229
- package/dist/packages/data/src/trade/signer/client.js +0 -197
- package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
- package/dist/packages/data/src/trade/signer/host.js +0 -97
- package/dist/packages/data/src/trade/signer/main.js +0 -121
- package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
- package/dist/packages/data/src/trade/signer/protocol.js +0 -92
- package/dist/packages/data/src/trade/signer/service.js +0 -400
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
- package/dist/packages/data/src/trade/sol-usd.js +0 -86
- package/dist/packages/data/src/trade/swap-build.js +0 -108
- package/dist/packages/data/src/trade/token-delta.js +0 -201
- package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
|
@@ -25,7 +25,7 @@ import { parseMintTarget, parseCoinTarget, resolveCoinTarget, isBareOpen } from
|
|
|
25
25
|
import { THEMES, isThemeName, DEFAULT_THEME, THEME_NAMES, Flusher, ENTER_SCREEN, LEAVE_SCREEN, DEFAULT_KEYMAP, Keymap, nextTheme, parseKeys, keyId, detectColorDepth, parseColorDepth, blinkFrame, blinkFrames, stepVfx, pulse, isCalm, fxBusy, blendActivity, ImageLayer, queryTerminal, resolveProtocol, fireMoment, fireInflight, clearInflight, skipMoment, momentKindFor, playMoment, soundOn, breakpoint, fmtUsd as fmtUsdTui, } from '../../tui/src/index.js';
|
|
26
26
|
import { resolveRpcUrl, redactSecrets, defaultDbPath, extraRpcUrls, wsUrlFor, SOLANA_PUBLIC_RPC_WS, parseHttpUrl, validateAnalystAction, ACTION_RATE, ACTION_TIER, configChangeForAction, ImageService, encodePng, SummaryLimiter, buildSummaryWindow, KolStore, Cache, PackError, normalizeHandle, terminalSafe } from '../../data/src/index.js';
|
|
27
27
|
import { Recorder, recorderEnabled, MaintenanceSchedule } from '../../recorder/src/index.js';
|
|
28
|
-
import { makeFixtureState, makeFeedState, syncFeed, renderFrame, isScreenKey, activityFor, SCREEN_NAMES, analystKey, syncAnalyst, pinNewpairsSelection, ANALYST_TAB_KEY, computeNarrativeView, newPlansUi, plansService, plansRows, newFollowsUi, copyService, copyAutoService, MIRROR_DEFAULT_CAP_USD } from "./state.js";
|
|
28
|
+
import { makeFixtureState, makeFeedState, syncFeed, renderFrame, isScreenKey, activityFor, SCREEN_NAMES, analystKey, syncAnalyst, pinNewpairsSelection, ANALYST_TAB_KEY, computeNarrativeView, newPlansUi, plansService, plansRows, newFollowsUi, copyService, copyAutoService, MIRROR_DEFAULT_CAP_USD, pnlChipFor } from "./state.js";
|
|
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";
|
|
@@ -39,9 +39,14 @@ import { parseFilter } from "./screener/filter.js";
|
|
|
39
39
|
import { screenerView, setFilter, setSort, setPreset, stepPreset, clampSelection, openFilterEditor, openSortMenu, filterEditorKey, sortMenuKey, } from "./screener/view.js";
|
|
40
40
|
import { readBootState, bootPlan, recordBoot, bootTrace, dextermHome } from "./boot-state.js";
|
|
41
41
|
import { socialOf, kolOf, trendingRows, kolRows, nextSocialFilter, feedsPopupModel } from "./social/view.js";
|
|
42
|
-
import { TokenPopups, tokenDetailVMFromView } from "./popup.js";
|
|
42
|
+
import { TokenPopups, tokenDetailVMFromView, currentPositionFills } from "./popup.js";
|
|
43
|
+
import { OwnPnlTracker } from "./screens/pnl-chip.js";
|
|
44
|
+
import { positionRow } from "./live/trade.js";
|
|
43
45
|
import { widgetPage, widgetEnabled, syncWidgetPopups } from "./widgets.js";
|
|
44
46
|
import { recordLarpVerdict } from "./larp-record.js";
|
|
47
|
+
import { newAutotradeUi, defaultDeps as autotradeDeps, autotradeCapturing, autotradeTick, reloadList as autotradeReload, refreshRunner as autotradeRefreshRunner } from "./screens/autotrade-ui.js";
|
|
48
|
+
import { autotradeAction, autotradeKey } from "./screens/autotrade-keys.js";
|
|
49
|
+
import { workerJobs as autotradeWorkerJobs } from "./screens/autotrade-worker.js";
|
|
45
50
|
import { rulesUiOf, rulesTick, rulesMove, beginRuleInput, saveRuleInput, dryRunNow, removeSelectedRule, toggleSelectedRule, dismissSelectedProposal, actOnSelectedProposal, RULE_INPUT_MAX } from "./rules.js";
|
|
46
51
|
import { buildNotifier, hasPushChannel } from "./alerts/notifier.js";
|
|
47
52
|
import { copyAlertToDelivery } from "./alerts/format.js";
|
|
@@ -49,20 +54,22 @@ import { readTelegramCreds, readDiscordCreds } from "./alerts/creds.js";
|
|
|
49
54
|
import { launchIntelCells, launchRetryable } from "./live/intel.js";
|
|
50
55
|
import { AnalystSession } from "./analyst/session.js";
|
|
51
56
|
import { analystCliOnPath, parseEngineChoice, resolveEngine } from "./analyst/codex.js";
|
|
57
|
+
import { analystEngineHas, analystAiChoice } from "./ai-cmd.js";
|
|
52
58
|
import { resolveAnalystResults } from "./analyst/results.js";
|
|
53
59
|
import { parseScanCommand, SCAN_FLAGS, checkScanFlag, SCAN_HELP } from "./scan/command.js";
|
|
54
60
|
import { parseWalletCommand, WALLET_HELP } from "./wallet-command.js";
|
|
55
61
|
import { X_HELP } from "./x-cmd.js";
|
|
56
|
-
import { makeScanUi, MODE_LABEL, SCAN_ACTION_TYPES, SCAN_TICK_MS, SCAN_RECOMPUTE_MS } from "./scan/ui.js";
|
|
62
|
+
import { makeScanUi, scanLog, MODE_LABEL, SCAN_ACTION_TYPES, SCAN_TICK_MS, SCAN_RECOMPUTE_MS } from "./scan/ui.js";
|
|
57
63
|
import { buildObservations, observeSig } from "./scan/observe.js";
|
|
58
64
|
import { cleanText } from "./screener/rows.js";
|
|
59
65
|
import { resolveImagesConfig } from "./images-config.js";
|
|
60
66
|
import { popupImagePath } from "./screens/coin.js";
|
|
61
67
|
import { VoiceController, selectStt, selectTts, SayTts, voiceStyleDefaults } from '../../voice/src/index.js';
|
|
62
|
-
import {
|
|
68
|
+
import { defaultPlansJournalPath } from '../../data/src/trade/plans/index.js';
|
|
63
69
|
import { isWalletAddress, shortAddr, buildMirror, leaderSoldSince, autoSizeCap } from '../../data/src/trade/copy/index.js';
|
|
64
70
|
import { FOLLOW_INPUT_MAX } from "./screens/follows.js";
|
|
65
|
-
import {
|
|
71
|
+
import { HUD_MIN_H, hudAnimating } from "./screens/agent-hud.js";
|
|
72
|
+
import { isPriceMode, configPriceMode, takeBasisFlags, togglePriceMode, setPriceMode, parseDisplayCommand, priceModeNotice, priceModeOf, planLevels, configChartAxis, setChartAxis, chartAxisOf, configChartMarkers, setChartMarkers, chartMarkersOn } from "./display.js";
|
|
66
73
|
import { VERSION } from "./version.js";
|
|
67
74
|
import { initCustom, planWorkspace, planPopupOpens, findWorkspace, nextWorkspaceName, workspaceSlots, saveWorkspace, themeFor, themeByName, cycleTheme, themeNames, CHORD_TTL_MS, MAX_WORKSPACE_NAME } from "./custom.js";
|
|
68
75
|
import { DEFAULT_WORKSPACE, MORNING_HUD, applyAndJournal, policyCheck } from '../../custom/src/index.js';
|
|
@@ -124,19 +131,36 @@ export function buildScreenState(state) {
|
|
|
124
131
|
const sc = state.screener;
|
|
125
132
|
// The screener and the Analyst-tab coin list both show the filtered/sorted screener view, so the
|
|
126
133
|
// selection + visible rows must come from that same view (task-1313); other screens use the raw pool.
|
|
127
|
-
|
|
134
|
+
// The new-pairs screen shows the live PumpPortal list (task-1443 capture r2: it used to report the screener pool
|
|
135
|
+
// here, so an agent watching new pairs could not read what was on screen or what was highlighted).
|
|
136
|
+
const newPairs = state.screen === 'newpairs' && live ? live.newpairs.list() : null;
|
|
137
|
+
const rows = state.screen === 'screener' || state.screen === 'analyst' ? screenerView(sc) : newPairs ?? sc.rows;
|
|
128
138
|
const toRow = (r) => {
|
|
129
139
|
const x = r;
|
|
130
140
|
if (!x?.mint)
|
|
131
141
|
return null;
|
|
132
|
-
|
|
142
|
+
// task-1537: the row's market cap rides along (the analyst's context leads with it by default)
|
|
143
|
+
return { mint: x.mint, symbol: x.symbol ?? '', priceUsd: x.price ?? null, mcap: x.mcap ?? null, chg1h: x.chg?.h1 ?? null, chg24h: x.chg?.h24 ?? null, liq: x.liq ?? null, vol24: x.vol24 ?? null };
|
|
133
144
|
};
|
|
134
|
-
const
|
|
145
|
+
const toNewPairRow = (r) => {
|
|
146
|
+
const x = r;
|
|
147
|
+
if (!x?.mint)
|
|
148
|
+
return null;
|
|
149
|
+
return {
|
|
150
|
+
mint: x.mint, symbol: x.symbol ?? '', priceUsd: x.price ?? null, chg1h: x.chg?.h1 ?? null, chg24h: null, liq: x.liq ?? null, vol24: x.vol ?? null,
|
|
151
|
+
ageSec: Number.isFinite(x.createdAt) ? Math.max(0, Math.round((Date.now() - x.createdAt) / 1000)) : null,
|
|
152
|
+
mcap: x.mcap ?? null, chg5m: x.chg?.m5 ?? null, bonding: x.bonding ?? null, buys: x.buys ?? null, sells: x.sells ?? null,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const mapRow = newPairs ? toNewPairRow : toRow;
|
|
156
|
+
const visible = rows.slice(0, 12).map(mapRow).filter((r) => r !== null);
|
|
135
157
|
let selected = null;
|
|
136
158
|
if (state.screen === 'token' && live?.token?.view)
|
|
137
159
|
selected = { mint: live.token.view.mint, symbol: live.token.view.symbol };
|
|
138
160
|
else {
|
|
139
|
-
|
|
161
|
+
// Same pick as the new-pairs screen's own highlight (selectedMint first, then the cursor row).
|
|
162
|
+
const hit = newPairs && state.selectedMint ? newPairs.find((x) => x.mint === state.selectedMint) : undefined;
|
|
163
|
+
const r = mapRow(hit ?? rows[state.selected || 0]);
|
|
140
164
|
if (r)
|
|
141
165
|
selected = { mint: r.mint, symbol: r.symbol };
|
|
142
166
|
}
|
|
@@ -267,7 +291,7 @@ export function shouldDrawFrame(g) {
|
|
|
267
291
|
return true;
|
|
268
292
|
if (g.calm)
|
|
269
293
|
return g.onTick;
|
|
270
|
-
if (g.fxBusy)
|
|
294
|
+
if (g.fxBusy || g.hud)
|
|
271
295
|
return true;
|
|
272
296
|
return g.frame % g.idleEvery === 0;
|
|
273
297
|
}
|
|
@@ -279,14 +303,17 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
|
|
|
279
303
|
dexterm update [--off|--on] check npm + GitHub for a newer version and print the upgrade command;
|
|
280
304
|
--off/--on toggles the once-a-day startup notice (no telemetry)
|
|
281
305
|
dexterm recorder stats|label|prune|export the history recorder's tables (no network; 'dexterm recorder help')
|
|
282
|
-
dexterm kb list|show|new|edit|check|reset|path your knowledge-base entries (~/.dexterm/kb/entries) over the bundled
|
|
306
|
+
dexterm kb list|show|new|edit|check|reset|path your knowledge-base entries (~/.dexterm/kb/entries) over the bundled 18
|
|
283
307
|
dexterm skill list|show|new|edit|check|reset|path your analyst skills (~/.dexterm/skills) over the bundled ones (read tools only)
|
|
284
308
|
dexterm narrative [--window 1h|24h] [--summary] the narrative board over recorded history (no network, no model)
|
|
285
309
|
dexterm backtest [--subject heuristic|pack_verdicts] [--horizon SEC] honest rug-model metrics over recorded outcomes (no network, no model)
|
|
286
|
-
dexterm
|
|
310
|
+
dexterm ledger trades|positions|summary|equity|compare|export|card|sync|verify the unified trade ledger: paper, agent desk, autotrade runs (no network; 'dexterm ledger help')
|
|
311
|
+
dexterm wallet link|unlink|status|panic|resume link a Phantom wallet via a local bridge (pubkey only); panic halts autotrade + the paper desk, resume needs a TTY keypress
|
|
287
312
|
dexterm plan propose|list|arm|cancel|journal conditional order plans (entry/ladder/take-profit/moon bag/stop); arming needs a physical keypress
|
|
288
|
-
dexterm mandate draft|list|arm|revoke standing capped mandates (
|
|
313
|
+
dexterm mandate draft|list|arm|revoke standing capped mandates (paper fills); arming needs a physical keypress — voice/agents can never arm
|
|
289
314
|
dexterm follow add|remove|list|stats|trades|mirror|purge copy trading tier 1: follow a wallet (your list, local labels), its decoded swaps + FIFO PnL + win rate/early-entry; 'mirror' previews a one-key MIRROR TICKET from the wallet's latest buy (inert; arm in the app)
|
|
315
|
+
dexterm ai [status|engine|model|host|restrictions|probe|ask|agent] the AI engine: your claude / codex CLI or a free LOCAL model on
|
|
316
|
+
your own Ollama (localhost); restriction profiles observe|propose|paper|full ('dexterm ai help')
|
|
290
317
|
dexterm mcp [install <client>|doctor] read-only MCP server for your own agent; 'install' writes the config for claude-code|claude-desktop|cursor|codex|generic (asks first, idempotent, backs up); 'doctor' verifies the connection
|
|
291
318
|
dexterm alerts status|enable|disable|set-telegram|set-discord|test where rule alerts are delivered (desktop / your Telegram / Discord; creds stay local, never logged)
|
|
292
319
|
dexterm watch [--interval S] [--once] [--replay FILE] run your rules headless and push alerts when the terminal is closed (no signer, proposals only)
|
|
@@ -306,6 +333,8 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
|
|
|
306
333
|
--record FILE save every live response (and the PumpPortal session) to FILE (replay it with --replay)
|
|
307
334
|
--spectate-mcp follow an MCP agent's allowed navigation in this terminal; Ctrl+P pauses, Ctrl+K stops,
|
|
308
335
|
and any normal user keypress takes control (refused actions never move the UI)
|
|
336
|
+
--spectate-thoughts FILE (with --spectate-mcp) stream the agent's own words into the thought ribbon from a JSONL
|
|
337
|
+
file it writes (e.g. claude -p --output-format stream-json); display only, scrubbed
|
|
309
338
|
--agent-paper MANDATE_ID (experimental, with --spectate-mcp) let that MCP agent PAPER-trade inside a mandate you
|
|
310
339
|
armed: simulated fills only, never signs or sends, never raises a cap; Ctrl+P / Ctrl+K /
|
|
311
340
|
any key also pause, stop or take over its paper trading
|
|
@@ -323,8 +352,9 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
|
|
|
323
352
|
--selected N highlighted row / token index
|
|
324
353
|
--seed N fixture PRNG seed (default 7)
|
|
325
354
|
--no-ai hide the analyst panel on the token screen
|
|
326
|
-
--analyst-engine E auto | claude | codex: which of YOUR
|
|
327
|
-
if installed, else codex; DEXTERM_ANALYST_ENGINE
|
|
355
|
+
--analyst-engine E auto | claude | codex | ollama: which of YOUR engines runs the analyst (default auto = claude
|
|
356
|
+
if installed, else codex, else a local Ollama model; DEXTERM_ANALYST_ENGINE, config analyst.engine
|
|
357
|
+
or ai.json too; /engine in chat). ollama = a free local model, see 'dexterm ai help'
|
|
328
358
|
|
|
329
359
|
--intensity LEVEL off | normal | fever (default fever: rain, trails, glitch, blink, scanlines at 30 fps;
|
|
330
360
|
normal: 15 fps, sparser rain, no idle aberration; off = calm: neon theme, no motion)
|
|
@@ -343,12 +373,16 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
|
|
|
343
373
|
result only; muted by default (also DEXTERM_SOUND=1); ~/.dexterm/sounds holds the WAVs
|
|
344
374
|
--workspace NAME start in a workspace: default | morning-hud | a file in ~/.dexterm/workspaces/ (task-1358);
|
|
345
375
|
in the app ctrl+w then w (list) · n/N (next/prev) · s (save as) · m (morning HUD) · o (popups) · d · 1-9
|
|
346
|
-
--price-mode MODE
|
|
347
|
-
|
|
376
|
+
--price-mode MODE mcap | price: show MARKET CAP (price × a known supply; the default) or USD price on every screen,
|
|
377
|
+
the CLI text and in the assistants' language for this run (default: config.json display.priceMode,
|
|
378
|
+
mcap; \$ in the app toggles and saves it). --mcap / --price (any command) and
|
|
379
|
+
DEXTERM_PRICE_BASIS=mcap|price do the same for one run
|
|
380
|
+
--mcap / --price the price basis for this run on ANY command (dexterm --price autotrade run …), not saved
|
|
348
381
|
dexterm workspace list|show|remove|init the workspace files (no network)
|
|
349
382
|
dexterm theme list|show|init|check user themes in ~/.dexterm/themes (extends a built-in; contrast + hue guarded)
|
|
350
383
|
dexterm keymap [screen] the effective keys (defaults + config.keymap overrides, marked ·)
|
|
351
|
-
dexterm display [price
|
|
384
|
+
dexterm display [mcap|price] show or set the price basis (default market cap = price × a known supply)
|
|
385
|
+
dexterm display markers [on|off] show or hide the BUY/SELL markers of your fills on both charts
|
|
352
386
|
dexterm rules list|add|remove|enable|disable|check|dry-run|fields
|
|
353
387
|
"when <cond> [for n ticks] then alert|ticket|ask|popup|tag|sound|watch" rules in config.json (task-1358);
|
|
354
388
|
parsed never eval'd; mandatory cooldown + max/h; rules only alert or PROPOSE (ctrl+r in the app)
|
|
@@ -361,14 +395,10 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
|
|
|
361
395
|
AI SCAN MODE (off by default; observe + analyze only — it can never open a ticket, sign or trade):
|
|
362
396
|
${SCAN_HELP}
|
|
363
397
|
|
|
364
|
-
LIVE TRADING (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
--
|
|
368
|
-
--max-sol-out N SOL that may leave the wallet in one tx (default 1, ceiling 5)
|
|
369
|
-
--max-slippage-bps N slippage ceiling (default 500, ceiling 1000)
|
|
370
|
-
--trade-rpc URL the signer's RPC (default devnet; must be devnet unless --mainnet)
|
|
371
|
-
--no-live paper only: never start the signer process
|
|
398
|
+
WALLET + LIVE TRADING (dexterm never holds a key and never signs a transaction):
|
|
399
|
+
live trading happens in the Telegram trading bot of your choice, which holds its own wallet; dexterm sends alerts,
|
|
400
|
+
signals and run control to your chat through your own bot (dexterm telegram setup). dexterm places no live order.
|
|
401
|
+
--no-live accepted for older scripts; changes nothing (trading in dexterm is paper only)
|
|
372
402
|
${WALLET_HELP}
|
|
373
403
|
|
|
374
404
|
LOGIN WITH X (task-1357; your own X app, your limits, your bill; read scopes only, no scraping, dexterm never posts):
|
|
@@ -393,17 +423,15 @@ ${X_HELP}
|
|
|
393
423
|
social (task-1330): T trending in the space · K KOL lists · token screen: s social panel · f filter · x hidden spam
|
|
394
424
|
dexterm kol list | create <name> | add <list> @handle | remove <list> @handle | mute @handle | unmute @handle | enable <list> | disable <list> | import <dir> | import-x
|
|
395
425
|
Login with X (task-1357): dexterm x login · then the SOCIAL panel + TRENDING read X with your own limits · KOL screen: X imports your X lists · o = only this handle
|
|
396
|
-
token: B/S buy/sell ticket (PAPER
|
|
426
|
+
token: B/S buy/sell ticket (PAPER) · P positions · w watch
|
|
397
427
|
token: M bubble map (holder graph: clusters by funder / same-slot buys / transfers; colour = rule) · bubble map: M popup · m treemap · z zoom · D deep scan
|
|
398
428
|
demo events on --fixtures: n new pair (glitch) · u pump (gold rain) · d dump (red rain) · x rug flag
|
|
399
429
|
AI scan: Z on/off · R resume scanning after a lock · esc (on the locked coin) cancel the analysis
|
|
400
430
|
any key skips the boot sequence or a blink transition
|
|
401
431
|
|
|
402
432
|
State: ~/.dexterm/state.json remembers launches (DEXTERM_HOME overrides the directory).
|
|
403
|
-
DEXTERM_OFFLINE=1 refuses live data (tests and CI). Trading is PAPER
|
|
404
|
-
priced from live Jupiter quotes.
|
|
405
|
-
quotes, builds, decodes, simulates and enforces the caps; Phantom signs in your browser; dexterm never
|
|
406
|
-
holds a key. The analyst, voice and scan mode can never open, confirm or sign a live trade.
|
|
433
|
+
DEXTERM_OFFLINE=1 refuses live data (tests and CI). Trading in dexterm is PAPER only — a simulated wallet
|
|
434
|
+
priced from live Jupiter quotes. dexterm never holds a key and never signs or sends a transaction.
|
|
407
435
|
RPC: DEXTERM_RPC_URL, else ~/.dexterm/helius.key (Helius, metered with a credit meter), else the public RPC.
|
|
408
436
|
Updates: at most once a day a live run asks npm + GitHub for the newest version (two anonymous GETs, no telemetry)
|
|
409
437
|
and shows one toast if there is one; DEXTERM_NO_UPDATE_CHECK=1 or 'dexterm update --off' turns that off.`;
|
|
@@ -417,9 +445,8 @@ export function parseArgs(argv) {
|
|
|
417
445
|
scan: false, scanConfig: {},
|
|
418
446
|
noRecorder: false,
|
|
419
447
|
sound: false,
|
|
420
|
-
trade: [], live: true,
|
|
421
448
|
workspace: null, priceMode: null,
|
|
422
|
-
spectateMcp: false, agentPaper: null,
|
|
449
|
+
spectateMcp: false, spectateThoughts: null, agentPaper: null,
|
|
423
450
|
help: false, version: false, unknown: [],
|
|
424
451
|
};
|
|
425
452
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -512,7 +539,7 @@ export function parseArgs(argv) {
|
|
|
512
539
|
const s = str();
|
|
513
540
|
const e = parseEngineChoice(s);
|
|
514
541
|
if (!e)
|
|
515
|
-
throw new Error(`unknown analyst engine ${s} (expected auto|claude|codex)`);
|
|
542
|
+
throw new Error(`unknown analyst engine ${s} (expected auto|claude|codex|ollama)`);
|
|
516
543
|
a.analystEngine = e;
|
|
517
544
|
}
|
|
518
545
|
else if (k === '--calm')
|
|
@@ -590,18 +617,10 @@ export function parseArgs(argv) {
|
|
|
590
617
|
a.noRecorder = true;
|
|
591
618
|
else if (k === '--sound')
|
|
592
619
|
a.sound = true;
|
|
593
|
-
// task-
|
|
594
|
-
|
|
595
|
-
else if (k === '--
|
|
596
|
-
a.
|
|
597
|
-
else if (k === '--max-usd' || k === '--daily-usd' || k === '--max-sol-out' || k === '--max-slippage-bps') {
|
|
598
|
-
a.trade.push(k, String(num()));
|
|
599
|
-
}
|
|
600
|
-
else if (k === '--trade-rpc') {
|
|
601
|
-
a.trade.push('--rpc', str());
|
|
602
|
-
}
|
|
603
|
-
else if (k === '--no-live')
|
|
604
|
-
a.live = false;
|
|
620
|
+
// task-1571: dexterm has no live trading of its own; `--no-live` (0.1.6) is accepted and changes nothing.
|
|
621
|
+
else if (k === '--no-live') { /* paper only is the only mode */ }
|
|
622
|
+
else if (k === '--price' || k === '--mcap')
|
|
623
|
+
a.priceMode = k === '--price' ? 'price' : 'mcap'; // task-1537 (main() also takes them before any subcommand)
|
|
605
624
|
else if (k === '--price-mode') {
|
|
606
625
|
const s = str();
|
|
607
626
|
if (!isPriceMode(s))
|
|
@@ -611,6 +630,8 @@ export function parseArgs(argv) {
|
|
|
611
630
|
// task-1421: follow an agent driving over MCP (the running terminal moves with it; a read-only stream opens).
|
|
612
631
|
else if (k === '--spectate-mcp')
|
|
613
632
|
a.spectateMcp = true;
|
|
633
|
+
else if (k === '--spectate-thoughts')
|
|
634
|
+
a.spectateThoughts = str();
|
|
614
635
|
// task-1443: agent PAPER trading inside a mandate the user armed (validated against the plans journal at start).
|
|
615
636
|
else if (k === '--agent-paper') {
|
|
616
637
|
const s = str();
|
|
@@ -655,6 +676,8 @@ export function parseArgs(argv) {
|
|
|
655
676
|
throw new Error('--scan needs live data or a recording (drop --fixtures)');
|
|
656
677
|
if (a.scan && a.screen)
|
|
657
678
|
throw new Error('--scan is interactive (drop --screen)');
|
|
679
|
+
if (a.spectateThoughts && !a.spectateMcp)
|
|
680
|
+
throw new Error('--spectate-thoughts needs --spectate-mcp (the thought ribbon is part of the follow view)');
|
|
658
681
|
if (a.agentPaper && !a.spectateMcp)
|
|
659
682
|
throw new Error('--agent-paper needs --spectate-mcp (every agent paper trade must be visible in this terminal)');
|
|
660
683
|
if (a.agentPaper && a.db === ':memory:')
|
|
@@ -806,6 +829,8 @@ export function fastLaneEndpoints(rpc) {
|
|
|
806
829
|
return out;
|
|
807
830
|
}
|
|
808
831
|
export async function main(argv, io = process, proc = process) {
|
|
832
|
+
// task-1537: `--price` / `--mcap` anywhere = the price basis for this run (every screen, CLI text and agent prompt).
|
|
833
|
+
argv = takeBasisFlags(argv, io.env);
|
|
809
834
|
// `dexterm mcp` → the read-only MCP server on stdio (SPEC §5.1; `claude mcp add dexterm -- dexterm mcp`),
|
|
810
835
|
// plus `dexterm mcp install <client>` (one-command setup) and `dexterm mcp doctor` (verify the connection).
|
|
811
836
|
if (argv[0] === 'mcp') {
|
|
@@ -834,6 +859,17 @@ export async function main(argv, io = process, proc = process) {
|
|
|
834
859
|
const { narrativeCommand } = await import("./narrative-cmd.js");
|
|
835
860
|
return narrativeCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
|
|
836
861
|
}
|
|
862
|
+
// `dexterm ledger …` → the unified trade ledger (task-1530): reads fold the file + the sources; only `sync` writes. No network, no model, no keys.
|
|
863
|
+
if (argv[0] === 'ledger') {
|
|
864
|
+
const { ledgerCommand } = await import("./ledger-cmd.js");
|
|
865
|
+
return ledgerCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
|
|
866
|
+
}
|
|
867
|
+
// `dexterm autotrade validate|list|backtest|sweep …` and `dexterm backtest --subject strategy` → the paper autotrade engine (task-1503). No network, no model, no keys.
|
|
868
|
+
if (argv[0] === 'autotrade' || argv[0] === 'backtest') {
|
|
869
|
+
const at = await import("./autotrade-cmd.js");
|
|
870
|
+
if (at.handles(argv))
|
|
871
|
+
return at.autotradeCommand(argv, { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
|
|
872
|
+
}
|
|
837
873
|
// `dexterm backtest [--subject heuristic|pack_verdicts] [--horizon SEC]` → honest rug-model metrics over recorded outcomes (task-1356). No network, no model.
|
|
838
874
|
if (argv[0] === 'backtest') {
|
|
839
875
|
const { backtestCommand } = await import("./backtest-cmd.js");
|
|
@@ -896,6 +932,12 @@ export async function main(argv, io = process, proc = process) {
|
|
|
896
932
|
const { alertsCommand } = await import("./alerts-cmd.js");
|
|
897
933
|
return alertsCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
|
|
898
934
|
}
|
|
935
|
+
// `dexterm telegram setup|pair|status|approvals|notify|set|test|audit|revoke` → your own bot for the autotrade runner
|
|
936
|
+
// (task-1527). The token goes in the OS secret store (or a 0600 file), never config/argv/env/logs.
|
|
937
|
+
if (argv[0] === 'telegram') {
|
|
938
|
+
const { telegramCommand } = await import("./telegram-cmd.js");
|
|
939
|
+
return telegramCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
|
|
940
|
+
}
|
|
899
941
|
// `dexterm watch [--interval S] [--once|--ticks N] [--replay FILE]` → run the rules engine headless and
|
|
900
942
|
// PUSH alerts to the enabled channels so they reach you when the terminal is closed (task-1398). No signer.
|
|
901
943
|
if (argv[0] === 'watch') {
|
|
@@ -918,6 +960,11 @@ export async function main(argv, io = process, proc = process) {
|
|
|
918
960
|
}
|
|
919
961
|
// `dexterm wallet <link|unlink|status>` → link a Phantom wallet via the loopback bridge (keys never
|
|
920
962
|
// enter dexterm; only the pubkey is stored), unlink it, or show the link + trade caps (task-1310 T1).
|
|
963
|
+
// task-1571: `dexterm live …` (the 0.1.7 local signer) was removed before release. dexterm holds no key.
|
|
964
|
+
if (argv[0] === 'live') {
|
|
965
|
+
io.stderr.write('dexterm live: not available. dexterm never holds a key and never signs; live trading happens in the Telegram trading bot of your choice (dexterm telegram setup sends alerts and run control to your chat)\n');
|
|
966
|
+
return 2;
|
|
967
|
+
}
|
|
921
968
|
if (argv[0] === 'wallet') {
|
|
922
969
|
const { runWallet } = await import("./wallet-cmd.js");
|
|
923
970
|
return runWallet(argv.slice(1), io);
|
|
@@ -949,6 +996,11 @@ export async function main(argv, io = process, proc = process) {
|
|
|
949
996
|
const { voiceCommand } = await import("./voice-cmd.js");
|
|
950
997
|
return voiceCommand(argv.slice(1), { stdin: io.stdin, stdout: io.stdout, stderr: io.stderr, env: io.env });
|
|
951
998
|
}
|
|
999
|
+
// task-1543: `dexterm ai …` (engine, local model, restriction profile, probe) and `dexterm ask` (a headless question).
|
|
1000
|
+
if (argv[0] === 'ai' || argv[0] === 'ask') {
|
|
1001
|
+
const { aiCommand } = await import("./ai-cmd.js");
|
|
1002
|
+
return aiCommand(argv[0] === 'ask' ? ['ask', ...argv.slice(1)] : argv.slice(1), { stdin: io.stdin, stdout: io.stdout, stderr: io.stderr, env: io.env });
|
|
1003
|
+
}
|
|
952
1004
|
// `dexterm doctor` → a pre-flight that says whether dexterm will run here and how to fix what won't (task-1305).
|
|
953
1005
|
if (argv[0] === 'doctor') {
|
|
954
1006
|
const { defaultDeps, runDoctor, renderDoctor } = await import("./doctor.js");
|
|
@@ -1044,9 +1096,8 @@ export async function main(argv, io = process, proc = process) {
|
|
|
1044
1096
|
});
|
|
1045
1097
|
ctx = new LiveContext(feed.dl, {
|
|
1046
1098
|
mode: 'live', dbPath: dbOpt, rpc: { provider: rpc.provider, basis: rpc.basis }, record: args.record ? feed.recording ?? undefined : undefined, interval: args.interval, fastEndpoints: fastLaneEndpoints(rpc), onLog: args.verbose ? (l) => io.stderr.write(l) : undefined, allowedSigners: join(dextermHome(io.env), 'allowed_signers'),
|
|
1047
|
-
//
|
|
1048
|
-
|
|
1049
|
-
...(args.live && !io.env.DEXTERM_OFFLINE ? { trade: { signerArgv: args.trade } } : {}),
|
|
1099
|
+
// The wallet link (public key only) is available in a real live run (never replay/offline).
|
|
1100
|
+
...(!io.env.DEXTERM_OFFLINE ? { trade: {} } : {}),
|
|
1050
1101
|
});
|
|
1051
1102
|
// task-1329: the history recorder listens to the data layer's normaliser (zero requests of its own)
|
|
1052
1103
|
// and keeps snapshots / feed membership / outcome labels in the same --db file. Live mode only (a
|
|
@@ -1064,7 +1115,7 @@ export async function main(argv, io = process, proc = process) {
|
|
|
1064
1115
|
ctx.newpairs.onCreator = (mint, creator, source, createdAt) => { recorder.recordCreator(mint, creator, source, undefined, createdAt); };
|
|
1065
1116
|
}
|
|
1066
1117
|
if (args.verbose)
|
|
1067
|
-
io.stderr.write(`dexterm: rpc ${rpc.provider} (${rpc.basis}) · db ${dbPath} · recorder ${ctx.recorder ? 'on' : 'off'} ·
|
|
1118
|
+
io.stderr.write(`dexterm: rpc ${rpc.provider} (${rpc.basis}) · db ${dbPath} · recorder ${ctx.recorder ? 'on' : 'off'} · paper trading only (dexterm holds no key)\n`);
|
|
1068
1119
|
}
|
|
1069
1120
|
if (args.preset && !feed.setPreset(args.preset)) {
|
|
1070
1121
|
const v = feed.presets().find((p) => p.id === args.preset);
|
|
@@ -1126,7 +1177,11 @@ export async function main(argv, io = process, proc = process) {
|
|
|
1126
1177
|
}
|
|
1127
1178
|
for (const e of state.custom.themeErrors)
|
|
1128
1179
|
io.stderr.write(`dexterm: ${e}\n`);
|
|
1129
|
-
|
|
1180
|
+
if (args.priceMode)
|
|
1181
|
+
io.env.DEXTERM_PRICE_BASIS = args.priceMode; // task-1537: the agents follow the run's basis
|
|
1182
|
+
state.priceMode = args.priceMode ?? configPriceMode(state.custom.config, io.env); // task-1366: --price-mode wins for the run; task-1537: then --price/--mcap/env, config, mcap
|
|
1183
|
+
state.chartAxis = configChartAxis(state.custom.config); // task-1472: default MARKET CAP, `A` toggles + saves
|
|
1184
|
+
state.chartMarkers = configChartMarkers(state.custom.config); // task-1483: BUY/SELL markers on both charts (default on)
|
|
1130
1185
|
if (args.workspace) {
|
|
1131
1186
|
const r = switchWorkspace(state, args.workspace, []);
|
|
1132
1187
|
if (!r.ok) {
|
|
@@ -1359,6 +1414,18 @@ function followsMove(state, delta) {
|
|
|
1359
1414
|
state.copyUi.selected = Math.max(0, Math.min(n - 1, state.copyUi.selected + delta));
|
|
1360
1415
|
return true;
|
|
1361
1416
|
}
|
|
1417
|
+
// task-1508: open (or return to) the AUTOTRADE screen; the strategy list and the runner status are re-read each time.
|
|
1418
|
+
function openAutotrade(state, stack) {
|
|
1419
|
+
if (state.screen !== 'autotrade')
|
|
1420
|
+
stack.push(state.screen);
|
|
1421
|
+
state.screen = 'autotrade';
|
|
1422
|
+
if (!state.autotradeUi) {
|
|
1423
|
+
state.autotradeUi = newAutotradeUi(autotradeDeps(process.env, autotradeWorkerJobs(process.env)));
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
autotradeReload(state.autotradeUi);
|
|
1427
|
+
autotradeRefreshRunner(state.autotradeUi);
|
|
1428
|
+
}
|
|
1362
1429
|
// task-1358 Tier 3: j/k on the RULES screen move the cursor of the active pane (frozen while a rule is being typed).
|
|
1363
1430
|
function rulesScreenMove(state, delta) {
|
|
1364
1431
|
if (state.screen !== 'rules' || !state.rulesUi)
|
|
@@ -1369,7 +1436,7 @@ function rulesScreenMove(state, delta) {
|
|
|
1369
1436
|
function plansMove(state, delta) {
|
|
1370
1437
|
if (state.screen !== 'plans' || !state.plansUi)
|
|
1371
1438
|
return false;
|
|
1372
|
-
if (state.plansUi.arming || state.plansUi.riskModal
|
|
1439
|
+
if (state.plansUi.arming || state.plansUi.riskModal)
|
|
1373
1440
|
return true;
|
|
1374
1441
|
const n = plansRows(state).length;
|
|
1375
1442
|
if (n <= 0) {
|
|
@@ -1415,6 +1482,14 @@ export function applyAction(state, action, stack) {
|
|
|
1415
1482
|
}
|
|
1416
1483
|
return null;
|
|
1417
1484
|
};
|
|
1485
|
+
// task-1508 AUTOTRADE (paper): `Q` opens the screen; while it is open it handles its own verbs and movement
|
|
1486
|
+
// (esc on its strategies list falls through to the normal back). Nothing it does reaches live mode or the signer.
|
|
1487
|
+
if (action === 'screen.autotrade') {
|
|
1488
|
+
openAutotrade(state, stack);
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
if (state.screen === 'autotrade' && state.autotradeUi && autotradeAction(state.autotradeUi, action))
|
|
1492
|
+
return;
|
|
1418
1493
|
switch (action) {
|
|
1419
1494
|
case 'quit': return 'quit';
|
|
1420
1495
|
case 'screen.screener':
|
|
@@ -2064,24 +2139,6 @@ export function applyAction(state, action, stack) {
|
|
|
2064
2139
|
}
|
|
2065
2140
|
break;
|
|
2066
2141
|
}
|
|
2067
|
-
case 'plans.live': {
|
|
2068
|
-
// task-1363: raise the FIRE PATH modal (paper ⇄ live devnet fast wallet). Exactly like arm and gate this
|
|
2069
|
-
// ONLY sets a modal flag; the switch itself happens in the interactive key loop on a captured physical
|
|
2070
|
-
// keypress, and PlanService.useLive refuses any gesture but 'keypress' — so applyAction (a pipe, an
|
|
2071
|
-
// agent, the voice router) can never move fills off paper.
|
|
2072
|
-
if (state.screen !== 'plans')
|
|
2073
|
-
break;
|
|
2074
|
-
const svc = state.plansSvc;
|
|
2075
|
-
if (!svc)
|
|
2076
|
-
break;
|
|
2077
|
-
if (state.plansUi) {
|
|
2078
|
-
state.plansUi.arming = null;
|
|
2079
|
-
state.plansUi.riskModal = false;
|
|
2080
|
-
state.plansUi.liveModal = true;
|
|
2081
|
-
state.plansUi.notice = null;
|
|
2082
|
-
}
|
|
2083
|
-
break;
|
|
2084
|
-
}
|
|
2085
2142
|
// task-1360 copy trading Tier 1: the FOLLOWED WALLETS screen. Opening it binds the follow store (+ the live
|
|
2086
2143
|
// tracker when there is one). `follows.add` only opens the typed-address line (the loop captures the keys);
|
|
2087
2144
|
// `follows.remove` unfollows the selected row. Nothing here trades, proposes or signs.
|
|
@@ -2471,6 +2528,9 @@ export function applyAction(state, action, stack) {
|
|
|
2471
2528
|
case 'display.priceMode':
|
|
2472
2529
|
state.pendingToast = togglePriceMode(state);
|
|
2473
2530
|
break; // task-1366: display only, persisted
|
|
2531
|
+
case 'display.chartAxis':
|
|
2532
|
+
state.pendingToast = setChartAxis(state);
|
|
2533
|
+
break; // task-1472: both charts' Y axis, display only, persisted
|
|
2474
2534
|
case 'theme.next':
|
|
2475
2535
|
state.theme = state.custom ? cycleTheme(state.custom, state.theme) : nextTheme(state.theme);
|
|
2476
2536
|
break; // task-1358: user themes join the cycle
|
|
@@ -2634,6 +2694,7 @@ export function scanCommand(state, cmd, stack, now = Date.now()) {
|
|
|
2634
2694
|
if (!e.start(now))
|
|
2635
2695
|
return `AI scan is already on (${MODE_LABEL[e.mode]})`;
|
|
2636
2696
|
live.scanMeter = scanMeterOf(e, now);
|
|
2697
|
+
scanLog(scan, now, 'start', '', `scan on · observe-only · locks at score ≥ ${e.cfg.minScore} · liquidity ≥ $${Math.round(e.cfg.minLiqUsd / 1000)}K`);
|
|
2637
2698
|
return `AI scan ON · observe-only · warming up ${e.cfg.warmupS}s · locks at score ≥ ${e.cfg.minScore} · ai ≤ ${e.cfg.maxAiPerHour}/h · Z stop`;
|
|
2638
2699
|
case 'off': {
|
|
2639
2700
|
if (!e.on)
|
|
@@ -2641,6 +2702,7 @@ export function scanCommand(state, cmd, stack, now = Date.now()) {
|
|
|
2641
2702
|
stopTurn();
|
|
2642
2703
|
const was = e.lock?.symbol;
|
|
2643
2704
|
e.stop(now);
|
|
2705
|
+
scanLog(scan, now, 'stop', was ?? '', was ? 'scan stopped · released' : 'scan stopped');
|
|
2644
2706
|
live.scanMeter = null;
|
|
2645
2707
|
scan.returnTo = null;
|
|
2646
2708
|
return `AI scan stopped${was ? ` (released ${sym(was)})` : ''}`;
|
|
@@ -2653,6 +2715,7 @@ export function scanCommand(state, cmd, stack, now = Date.now()) {
|
|
|
2653
2715
|
const locked = e.lock;
|
|
2654
2716
|
stopTurn();
|
|
2655
2717
|
e.resume(now);
|
|
2718
|
+
scanLog(scan, now, 'release', locked?.symbol ?? '', `released · cooling down ${Math.round(e.cfg.coinCooldownS / 60)}m · watching for the next coin`);
|
|
2656
2719
|
live.scanMeter = scanMeterOf(e, now);
|
|
2657
2720
|
// back from the coin the lock pulled up to where the user was, so the ticker is in view
|
|
2658
2721
|
if (locked && state.screen === 'token' && live.token?.view.mint === locked.mint)
|
|
@@ -2686,11 +2749,12 @@ export function applyScanAnalystAction(state, raw) {
|
|
|
2686
2749
|
* The analyst engine for a run (0.1.5): `--analyst-engine`, then DEXTERM_ANALYST_ENGINE, then config
|
|
2687
2750
|
* `analyst.engine`, then auto (claude when installed, else codex). Only the user picks it; models cannot.
|
|
2688
2751
|
*/
|
|
2689
|
-
export function analystEngineFor(flag, env, configured, has = (b) => analystCliOnPath(b, env)) {
|
|
2690
|
-
|
|
2752
|
+
export function analystEngineFor(flag, env, configured, has = (b) => analystCliOnPath(b, env), aiChoice) {
|
|
2753
|
+
// task-1543: ai.json (provider / surfaces.analyst / DEXTERM_AI_PROVIDER) comes after the analyst's own settings.
|
|
2754
|
+
const choice = parseEngineChoice(flag) ?? parseEngineChoice(env.DEXTERM_ANALYST_ENGINE) ?? parseEngineChoice(configured) ?? parseEngineChoice(aiChoice) ?? 'auto';
|
|
2691
2755
|
return resolveEngine(choice, has);
|
|
2692
2756
|
}
|
|
2693
|
-
/** `/engine [auto|claude|codex]` in the chat box (0.1.5). Null when the input is not this command. */
|
|
2757
|
+
/** `/engine [auto|claude|codex|ollama]` in the chat box (0.1.5; ollama task-1543). Null when the input is not this command. */
|
|
2694
2758
|
export function parseEngineCommand(q) {
|
|
2695
2759
|
const m = /^\/engine(?:\s+(\S+))?\s*$/i.exec(q.trim());
|
|
2696
2760
|
if (!m)
|
|
@@ -2698,23 +2762,24 @@ export function parseEngineCommand(q) {
|
|
|
2698
2762
|
if (!m[1])
|
|
2699
2763
|
return { show: true };
|
|
2700
2764
|
const c = parseEngineChoice(m[1]);
|
|
2701
|
-
return c ? { choice: c } : { error: `unknown analyst engine "${m[1].slice(0, 16)}" (use /engine auto|claude|codex)` };
|
|
2765
|
+
return c ? { choice: c } : { error: `unknown analyst engine "${m[1].slice(0, 16)}" (use /engine auto|claude|codex|ollama)` };
|
|
2702
2766
|
}
|
|
2703
2767
|
/**
|
|
2704
2768
|
* Apply `/engine`: switch the CLI for NEW conversations this run (cached per-coin chats are dropped so the next
|
|
2705
2769
|
* `a` starts on the chosen engine; a running turn finishes where it started). Returns the line to show.
|
|
2706
2770
|
*/
|
|
2707
|
-
export function engineCommand(state, cmd, env, has = (
|
|
2708
|
-
const installed = ['claude', 'codex'].filter((b) => has(b));
|
|
2709
|
-
const list = installed.length ? installed.join(' + ') : '
|
|
2771
|
+
export function engineCommand(state, cmd, env, has = analystEngineHas(env)) {
|
|
2772
|
+
const installed = ['claude', 'codex', 'ollama'].filter((b) => has(b));
|
|
2773
|
+
const list = installed.length ? installed.join(' + ') : 'no AI engine yet';
|
|
2710
2774
|
if ('error' in cmd)
|
|
2711
2775
|
return cmd.error;
|
|
2712
2776
|
const cur = state.analystEngine ?? 'claude';
|
|
2713
2777
|
if ('show' in cmd)
|
|
2714
|
-
return `analyst engine: ${cur} · installed: ${list} · /engine claude|codex|auto switches (config analyst.engine keeps it)`;
|
|
2778
|
+
return `analyst engine: ${cur} · installed: ${list} · /engine claude|codex|ollama|auto switches (config analyst.engine keeps it)`;
|
|
2715
2779
|
const next = resolveEngine(cmd.choice, has);
|
|
2780
|
+
const how = next === 'codex' ? 'npm i -g @openai/codex && codex login' : next === 'ollama' ? 'install Ollama (https://ollama.com/download), `ollama pull qwen3:8b`, `dexterm ai engine ollama`' : 'install Claude Code, then run claude once to log in';
|
|
2716
2781
|
if (cmd.choice !== 'auto' && !has(next))
|
|
2717
|
-
return `${next} is not
|
|
2782
|
+
return `${next} is not ${next === 'ollama' ? 'set up' : 'installed'} (installed: ${list}); ${how}`;
|
|
2718
2783
|
if (next !== cur) {
|
|
2719
2784
|
for (const [k, sess] of state.analystByMint ?? []) {
|
|
2720
2785
|
if (!sess.busy)
|
|
@@ -2724,6 +2789,8 @@ export function engineCommand(state, cmd, env, has = (b) => analystCliOnPath(b,
|
|
|
2724
2789
|
state.analyst = null;
|
|
2725
2790
|
}
|
|
2726
2791
|
state.analystEngine = next;
|
|
2792
|
+
if (next === 'ollama')
|
|
2793
|
+
return 'analyst engine: ollama (a local model on your own Ollama, $0) for new conversations · `dexterm ai engine ollama` keeps it';
|
|
2727
2794
|
return `analyst engine: ${next} (your own ${next} CLI and login) for new conversations · set analyst.engine in config.json to keep it`;
|
|
2728
2795
|
}
|
|
2729
2796
|
/**
|
|
@@ -2739,7 +2806,7 @@ export function createScanSession(state, mint, symbol, hooks = {}, deps = {}) {
|
|
|
2739
2806
|
const type = typeof raw.type === 'string' ? raw.type.replace(/[^a-z_]/gi, '').slice(0, 24) : '?';
|
|
2740
2807
|
hooks.onBlocked?.(type, r.error ?? 'refused');
|
|
2741
2808
|
};
|
|
2742
|
-
return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true, ...(state.analystEngine
|
|
2809
|
+
return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true, ...(state.analystEngine && state.analystEngine !== 'claude' ? { engine: state.analystEngine } : {}) }, hooks.onChange ?? (() => { }), deps);
|
|
2743
2810
|
}
|
|
2744
2811
|
/**
|
|
2745
2812
|
* Resolve a mint to an openable seed from what the app already knows (screener view → new pairs →
|
|
@@ -2995,6 +3062,61 @@ export function applyAnalystAction(state, raw, stack) {
|
|
|
2995
3062
|
times.push(now);
|
|
2996
3063
|
return { ok: true, label: res.label };
|
|
2997
3064
|
}
|
|
3065
|
+
/**
|
|
3066
|
+
* task-1466: the price this terminal itself shows for a coin, to mark the followed agent's open PAPER position (the
|
|
3067
|
+
* HUD's live PnL): the open token's push price (LIVE) or polled price, else the screener row's price. null if the
|
|
3068
|
+
* terminal has no price for it. Never the agent's own number.
|
|
3069
|
+
*/
|
|
3070
|
+
export function markPriceFor(state, mint) {
|
|
3071
|
+
const live = state.live;
|
|
3072
|
+
if (live?.token?.view.mint === mint) {
|
|
3073
|
+
const px = (live.badgeFor(mint) === 'LIVE' ? live.livePrice(mint) : null) ?? live.token.view.price;
|
|
3074
|
+
if (typeof px === 'number' && Number.isFinite(px) && px > 0)
|
|
3075
|
+
return px;
|
|
3076
|
+
}
|
|
3077
|
+
const row = state.screener.rows.find((r) => r.mint === mint);
|
|
3078
|
+
if (row && typeof row.price === 'number' && Number.isFinite(row.price) && row.price > 0)
|
|
3079
|
+
return row.price;
|
|
3080
|
+
// task-1473: the held coin scrolled out of the list (run25): its fast-lane push price (the held mint is pinned there)
|
|
3081
|
+
const fast = live?.livePrice(mint) ?? null;
|
|
3082
|
+
return typeof fast === 'number' && Number.isFinite(fast) && fast > 0 ? fast : null;
|
|
3083
|
+
}
|
|
3084
|
+
let ownOpenedMemo = null;
|
|
3085
|
+
/**
|
|
3086
|
+
* task-1473 r2: the user's OWN held position for the AI-scan PnL chip, marked with this terminal's own price (same
|
|
3087
|
+
* task-1323 position row the token screen uses): the scan's locked coin when held, else the open token, else the
|
|
3088
|
+
* first open mint (live first). null when flat. Reads the ledgers only (memoised); never trades, sizes or signs.
|
|
3089
|
+
*/
|
|
3090
|
+
export function ownHeldSample(state, now) {
|
|
3091
|
+
const live = state.live;
|
|
3092
|
+
if (!live)
|
|
3093
|
+
return null;
|
|
3094
|
+
const src = live.positionSource;
|
|
3095
|
+
const open = src.openMints();
|
|
3096
|
+
if (open.length === 0)
|
|
3097
|
+
return null;
|
|
3098
|
+
const prefer = [state.scan?.engine.lock?.mint, live.token?.view.mint].filter((m) => !!m);
|
|
3099
|
+
const mint = prefer.find((m) => open.includes(m)) ?? open[0];
|
|
3100
|
+
const held = src.held(mint);
|
|
3101
|
+
if (!held)
|
|
3102
|
+
return null;
|
|
3103
|
+
const r = positionRow(held, markPriceFor(state, mint), state.sol?.price ?? src.solUsd());
|
|
3104
|
+
if (!ownOpenedMemo || ownOpenedMemo.mint !== mint || now - ownOpenedMemo.at > 5000) {
|
|
3105
|
+
let t = null;
|
|
3106
|
+
try {
|
|
3107
|
+
t = currentPositionFills(live.wallet.trades(500).filter((x) => x.mint === mint))[0]?.t ?? null;
|
|
3108
|
+
}
|
|
3109
|
+
catch {
|
|
3110
|
+
t = null;
|
|
3111
|
+
}
|
|
3112
|
+
ownOpenedMemo = { mint, at: now, t };
|
|
3113
|
+
}
|
|
3114
|
+
return {
|
|
3115
|
+
mint, symbol: held.symbol, kind: 'paper',
|
|
3116
|
+
entryUsd: r.avgEntryUsd, markUsd: r.priceUsd, pnlPct: r.unrealizedPct, pnlUsd: r.unrealizedUsd,
|
|
3117
|
+
openedAt: ownOpenedMemo.t, targetUsd: r.targetUsd,
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
2998
3120
|
/**
|
|
2999
3121
|
* task-1421: move the REAL terminal to mirror an agent's navigation (follow mode, on the owner's own
|
|
3000
3122
|
* machine). This is the same VIEW-ONLY navigation the analyst / go-to path uses — open a token, switch
|
|
@@ -3243,7 +3365,7 @@ export function applyKey(state, key, stack) {
|
|
|
3243
3365
|
popups.push('plans');
|
|
3244
3366
|
const widgets = (p?.openWidgets?.() ?? []).map((w) => w.key); // task-1358 Tier 5: open pack widgets are saved by ref
|
|
3245
3367
|
const entry = saveWorkspace(ui, {
|
|
3246
|
-
name, screen: state.screen, theme: state.theme.name, intensity: state.vfx.intensity, priceMode: state
|
|
3368
|
+
name, screen: state.screen, theme: state.theme.name, intensity: state.vfx.intensity, priceMode: priceModeOf(state),
|
|
3247
3369
|
screener: { preset: sc.preset, filter: sc.query ?? null, sort: `${sc.sort.key}:${sc.sort.dir}` },
|
|
3248
3370
|
token: { tape: !!state.tokenTape, social: !!state.tokenSocial, analyst: state.aiOpen !== false },
|
|
3249
3371
|
popups, widgets,
|
|
@@ -3269,6 +3391,12 @@ export function applyKey(state, key, stack) {
|
|
|
3269
3391
|
// task-1360: typing a wallet address on the FOLLOWED WALLETS screen captures every key until ⏎ / esc.
|
|
3270
3392
|
// task-1358 Tier 3: typing a rule on the RULES screen captures every key until ⏎ / esc. Every keystroke re-runs the
|
|
3271
3393
|
// dry-run ("would match N rows right now"); ⏎ parses + saves (never eval'd), a refusal is the notice.
|
|
3394
|
+
// task-1508: the AUTOTRADE screen's typed lines (a strategy id, a file to import, a field value) and the flatten
|
|
3395
|
+
// confirm capture every key until ⏎ / esc, so q and the screen keys never leak out of a half-typed value.
|
|
3396
|
+
if (state.screen === 'autotrade' && state.autotradeUi && autotradeCapturing(state.autotradeUi)) {
|
|
3397
|
+
autotradeKey(state.autotradeUi, key);
|
|
3398
|
+
return;
|
|
3399
|
+
}
|
|
3272
3400
|
if (state.screen === 'rules' && state.rulesUi && state.rulesUi.input !== null) {
|
|
3273
3401
|
const ui = state.rulesUi;
|
|
3274
3402
|
const cur = state.rulesUi.input;
|
|
@@ -3607,8 +3735,8 @@ export function fpsFor(args, intensity) {
|
|
|
3607
3735
|
const SYNC_ON = '\x1b[?2026h';
|
|
3608
3736
|
const SYNC_OFF = '\x1b[?2026l';
|
|
3609
3737
|
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);
|
|
3738
|
+
// 0.1.5: the analyst engine for new conversations (the user's own claude or codex CLI, or task-1543 a local Ollama model; never a shipped key).
|
|
3739
|
+
state.analystEngine ??= analystEngineFor(args.analystEngine, io.env, state.custom?.config.analyst?.engine, analystEngineHas(io.env), analystAiChoice(io.env));
|
|
3612
3740
|
const out = io.stdout;
|
|
3613
3741
|
const inp = io.stdin;
|
|
3614
3742
|
const rnd = mulberry32(99);
|
|
@@ -3912,6 +4040,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
3912
4040
|
// task-1421: the MCP follow session (set below when `--spectate-mcp` is on). Declared here so the quit /
|
|
3913
4041
|
// terminal-guard teardown closures can stop it.
|
|
3914
4042
|
let followSession = null;
|
|
4043
|
+
let lastHudMark = null; // task-1466: the last terminal price that marked the agent's PAPER position
|
|
3915
4044
|
// task-1443: `--agent-paper <mandate-id>` (with --spectate-mcp) lets that agent PAPER-trade inside the mandate.
|
|
3916
4045
|
const agentPaper = args.spectateMcp && args.agentPaper ? new AgentPaperSwitch({ env: io.env, mandateId: args.agentPaper, db: args.db ?? defaultDbPath(io.env) }) : null;
|
|
3917
4046
|
// Idle economy (task-1303, retuned for task-1319 AC3): between events the ambient rain redraws at
|
|
@@ -4033,7 +4162,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4033
4162
|
// task-1340: a send in flight shows the neutral scanning sweep (never a verdict); it clears when the ticket settles.
|
|
4034
4163
|
if (reason === 'ticket') {
|
|
4035
4164
|
const ph = live.ticket?.ticket.phase;
|
|
4036
|
-
if (ph === 'filling'
|
|
4165
|
+
if (ph === 'filling') {
|
|
4037
4166
|
if (v.moment?.kind !== 'inflight')
|
|
4038
4167
|
fireInflight(v, live.ticket?.ticket.symbol ?? '', ticketOrigin());
|
|
4039
4168
|
}
|
|
@@ -4142,13 +4271,20 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4142
4271
|
}
|
|
4143
4272
|
scheduleDraw();
|
|
4144
4273
|
},
|
|
4145
|
-
|
|
4274
|
+
// task-1466: the HUD dock says what the agent did; the toast is only the fallback for a terminal too short for it.
|
|
4275
|
+
onStatus: (line) => { if (size()[1] < HUD_MIN_H)
|
|
4276
|
+
say(line, 'ai', 4); scheduleDraw(); },
|
|
4146
4277
|
onEvent: () => scheduleDraw(),
|
|
4147
4278
|
onControl: agentPaper ? (st) => agentPaper.control(st) : undefined,
|
|
4148
4279
|
snapshot: () => followSnapshot(state),
|
|
4280
|
+
thoughtsFile: args.spectateThoughts ?? undefined,
|
|
4149
4281
|
});
|
|
4282
|
+
// task-1466 spectator v2: the agent HUD (phase rail, heartbeat, thought ribbon, call timeline, the PAPER
|
|
4283
|
+
// execution moment, pause / kill) draws over this terminal while it follows. Calm mode = reduced motion.
|
|
4284
|
+
state.agentHud = { story: followSession.story, clock: () => Date.now(), dataLabel: args.replay ? 'REPLAY DATA' : null };
|
|
4150
4285
|
void followSession.start();
|
|
4151
|
-
|
|
4286
|
+
if (size()[1] < HUD_MIN_H)
|
|
4287
|
+
say('following agents over MCP · ctrl+p pause · ctrl+k stop · any key takes over', 'ai', 6);
|
|
4152
4288
|
if (agentPaper)
|
|
4153
4289
|
say(agentPaper.start(), 'ai', 8);
|
|
4154
4290
|
}
|
|
@@ -4218,7 +4354,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4218
4354
|
maybeSpeakVoiceAnswer(session); // task-1282: speak a voice-initiated answer when it settles
|
|
4219
4355
|
scheduleDraw();
|
|
4220
4356
|
};
|
|
4221
|
-
const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction, ...(state.analystEngine
|
|
4357
|
+
const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction, ...(state.analystEngine && state.analystEngine !== 'claude' ? { engine: state.analystEngine } : {}) }, onChange);
|
|
4222
4358
|
if (p.question)
|
|
4223
4359
|
session.ask(p.question, buildScreenState(state));
|
|
4224
4360
|
else
|
|
@@ -4256,6 +4392,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4256
4392
|
scan.blocked.push(type);
|
|
4257
4393
|
if (scan.blocked.length > 6)
|
|
4258
4394
|
scan.blocked.shift();
|
|
4395
|
+
scanLog(scan, Date.now(), 'blocked', scan.engine.lock?.symbol ?? '', `blocked analyst action "${type}" · observe-only`);
|
|
4259
4396
|
}
|
|
4260
4397
|
scanSay(`AI scan blocked analyst action "${type}" · observe-only (${error})`, 'warn', 5);
|
|
4261
4398
|
scheduleDraw();
|
|
@@ -4287,6 +4424,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4287
4424
|
state.aiFocused = false;
|
|
4288
4425
|
state.aiScroll = 0;
|
|
4289
4426
|
state.aiRevealAll = false;
|
|
4427
|
+
scanLog(scan, Date.now(), 'lock', d.symbol, `locked · score ${d.score} · ${d.hits.map((h) => h.reason).join(' · ')}`);
|
|
4290
4428
|
startScanSession(d.mint, d.symbol, d.question, `AI scan · score ${d.score} · ${d.hits.map((h) => h.id).join(' + ')} → short read`);
|
|
4291
4429
|
syncAnalyst(state);
|
|
4292
4430
|
live.syncFastSet();
|
|
@@ -4304,6 +4442,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4304
4442
|
const sess = scan.session;
|
|
4305
4443
|
if (!sess)
|
|
4306
4444
|
return;
|
|
4445
|
+
scanLog(scan, Date.now(), 'refresh', d.symbol, `moved: ${d.moved.join(', ')} · refreshing the read`);
|
|
4307
4446
|
sess.ask(d.question, scanScreenState(state, d.mint, d.symbol), `AI scan refresh · ${d.moved.join(' · ')}`);
|
|
4308
4447
|
if (state.analyst === sess) {
|
|
4309
4448
|
state.aiScroll = 0;
|
|
@@ -4342,7 +4481,8 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4342
4481
|
return;
|
|
4343
4482
|
if (scan.session?.busy)
|
|
4344
4483
|
scan.session.interrupt();
|
|
4345
|
-
scan.engine.cancelAnalysis(Date.now())
|
|
4484
|
+
if (scan.engine.cancelAnalysis(Date.now()))
|
|
4485
|
+
scanLog(scan, Date.now(), 'cancel', scan.engine.lock?.symbol ?? '', 'analysis cancelled by you · coin held, nothing else locks');
|
|
4346
4486
|
if (live)
|
|
4347
4487
|
live.scanMeter = scanMeterOf(scan.engine, Date.now());
|
|
4348
4488
|
scanSay(`analysis cancelled · scan holds $${cleanText(scan.engine.lock?.symbol ?? '', 12)} · R resume scan · Z stop`, 'ai', 5);
|
|
@@ -4424,10 +4564,18 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4424
4564
|
livePrice: (mint) => (live.badgeFor(mint) === 'LIVE' ? live.livePrice(mint) : null),
|
|
4425
4565
|
feed: (mint) => (live.badgeFor(mint) === 'LIVE' ? 'LIVE' : 'POLL'),
|
|
4426
4566
|
positions: live.positionSource,
|
|
4427
|
-
fills: (mint) =>
|
|
4567
|
+
fills: (mint) => live.wallet.trades(500).filter((t) => t.mint === mint),
|
|
4428
4568
|
solUsd: () => state.sol?.price ?? live.positions.solUsd(),
|
|
4429
4569
|
imagePath: (v) => popupImagePath(state, v.image),
|
|
4430
4570
|
priceMode: () => priceModeOf(state), // task-1366
|
|
4571
|
+
chartAxis: () => chartAxisOf(state), // task-1472: the popup's Y axis
|
|
4572
|
+
setChartAxis: (a) => { say(setChartAxis(state, a), 'info', 3); if (!done)
|
|
4573
|
+
draw(); }, // task-1472: the popup's MCAP | PRICE control
|
|
4574
|
+
pnlChip: () => pnlChipFor(state, state.agentHud?.clock() ?? Date.now()), // task-1473 r2: the chip on the chart popup
|
|
4575
|
+
markFills: (mint) => live.chartFills.for(mint), // task-1483: BUY/SELL markers (the paper ledger, no fetch)
|
|
4576
|
+
chartMarkers: () => chartMarkersOn(state),
|
|
4577
|
+
setChartMarkers: (on) => { say(setChartMarkers(state, on ? 'on' : 'off'), 'info', 3); if (!done)
|
|
4578
|
+
draw(); }, // task-1483: the popup's MARKERS control
|
|
4431
4579
|
} : null,
|
|
4432
4580
|
});
|
|
4433
4581
|
}
|
|
@@ -4691,7 +4839,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4691
4839
|
else {
|
|
4692
4840
|
try {
|
|
4693
4841
|
const s = seedForMint(state, live, tk.mint, tk.symbol);
|
|
4694
|
-
live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, tk.side
|
|
4842
|
+
live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, tk.side);
|
|
4695
4843
|
if (state.screen !== 'ticket')
|
|
4696
4844
|
stack.push(state.screen);
|
|
4697
4845
|
state.screen = 'ticket';
|
|
@@ -4935,7 +5083,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
4935
5083
|
const res = resolveVoiceToken(d.target);
|
|
4936
5084
|
if (res.kind === 'ok' && live) {
|
|
4937
5085
|
const s = seedForMint(state, live, res.mint);
|
|
4938
|
-
live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, d.side
|
|
5086
|
+
live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, d.side);
|
|
4939
5087
|
if (state.screen !== 'ticket')
|
|
4940
5088
|
stack.push(state.screen);
|
|
4941
5089
|
state.screen = 'ticket';
|
|
@@ -5250,7 +5398,9 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5250
5398
|
// task-1388 GO-TO prompt: while the "open any coin" input is open it owns the keyboard. Route the key
|
|
5251
5399
|
// straight to applyKey (which does the capture / open) and skip the voice, calm, demo and keymap
|
|
5252
5400
|
// handlers below — otherwise a base58 char like v / m / c would toggle voice or calm instead of typing.
|
|
5253
|
-
|
|
5401
|
+
// task-1508: the AUTOTRADE screen's typed lines (an id, a file, a field value) and its flatten confirm own the
|
|
5402
|
+
// keyboard the same way, so v / m / c / n / d / x type instead of toggling voice, calm or a demo event.
|
|
5403
|
+
if (state.goto || (state.screen === 'autotrade' && state.autotradeUi && autotradeCapturing(state.autotradeUi))) {
|
|
5254
5404
|
const before = state.screen;
|
|
5255
5405
|
try {
|
|
5256
5406
|
applyKey(state, key, stack);
|
|
@@ -5480,33 +5630,10 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5480
5630
|
}
|
|
5481
5631
|
// else: fall through to the keymap (quit/navigation remain responsive while filling)
|
|
5482
5632
|
}
|
|
5483
|
-
// task-1328 LIVE: while the signer builds the plan, esc cancels; while Phantom has the plan
|
|
5484
|
-
// (signing) no key does anything — the signer finishes or times out, and nothing here can
|
|
5485
|
-
// confirm, so navigation keys still fall through to the keymap.
|
|
5486
|
-
if (state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase === 'planning') {
|
|
5487
|
-
if (key.name === 'esc') {
|
|
5488
|
-
live.ticket.cancelPlan();
|
|
5489
|
-
draw();
|
|
5490
|
-
continue;
|
|
5491
|
-
}
|
|
5492
|
-
// task-1368: while Jupiter is rate limiting the signer the ticket waits and retries by itself; r retries now.
|
|
5493
|
-
if (key.name === 'char' && (key.ch === 'r' || key.ch === 'R') && !key.ctrl && !key.alt) {
|
|
5494
|
-
live.ticket.retryPlan();
|
|
5495
|
-
draw();
|
|
5496
|
-
continue;
|
|
5497
|
-
}
|
|
5498
|
-
}
|
|
5499
|
-
if (state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase === 'signing') {
|
|
5500
|
-
if (key.name === 'esc') {
|
|
5501
|
-
state.pendingToast = 'waiting for Phantom — approve or reject it in the browser';
|
|
5502
|
-
draw();
|
|
5503
|
-
continue;
|
|
5504
|
-
}
|
|
5505
|
-
}
|
|
5506
5633
|
// PAPER trade ticket (task-1310): the ticket captures every key (digits/y/esc/tab/…) so the
|
|
5507
5634
|
// confirm gate can never be satisfied by a stray key, and `q`/`1`-`4` cannot quit or navigate
|
|
5508
5635
|
// out from under a half-typed order. `filling` is handled above so the user is never trapped.
|
|
5509
|
-
const ticketActive = state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase !== 'filled' && live.ticket.ticket.phase !== 'error' && live.ticket.ticket.phase !== '
|
|
5636
|
+
const ticketActive = state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase !== 'filled' && live.ticket.ticket.phase !== 'error' && live.ticket.ticket.phase !== 'filling';
|
|
5510
5637
|
if (ticketActive && live?.ticket) {
|
|
5511
5638
|
let eff;
|
|
5512
5639
|
try {
|
|
@@ -5612,79 +5739,6 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5612
5739
|
// 'keypress'}) — which the engine takes ONLY from a real keypress, journals, and auto-expires in 24h;
|
|
5613
5740
|
// restoreGate() puts it back. There is no path here for a pipe/agent (TTY-only loop, no way to raise
|
|
5614
5741
|
// the modal except a person pressing G), and signer caps/allow-lists/panic are unaffected either way.
|
|
5615
|
-
// task-1363 — the FIRE PATH modal: paper ⇄ LIVE (devnet, the capped fast wallet). Same captured-keypress
|
|
5616
|
-
// discipline as arm/gate: enter/y switches, esc cancels, every other key is swallowed. Going LIVE needs
|
|
5617
|
-
// the run's signer process (never started in replay/offline/--no-live), the signer on DEVNET with a fast
|
|
5618
|
-
// wallet loaded (checked by LivePlanExecutor.ready), and no armed/filled plans; PlanService.useLive
|
|
5619
|
-
// takes ONLY a keypress gesture and journals the switch. Nothing here signs: fills still go through the
|
|
5620
|
-
// signer's own policy, simulation and caps, and the fast wallet never signs for the main wallet.
|
|
5621
|
-
if (state.screen === 'plans' && state.plansUi?.liveModal) {
|
|
5622
|
-
const ui = state.plansUi;
|
|
5623
|
-
if (key.name === 'esc') {
|
|
5624
|
-
ui.liveModal = false;
|
|
5625
|
-
ui.notice = 'fire path unchanged';
|
|
5626
|
-
draw();
|
|
5627
|
-
continue;
|
|
5628
|
-
}
|
|
5629
|
-
const confirmKey = key.name === 'enter' || (key.name === 'char' && !key.ctrl && !key.alt && (key.ch === 'y' || key.ch === 'Y'));
|
|
5630
|
-
if (confirmKey) {
|
|
5631
|
-
const svc = state.plansSvc;
|
|
5632
|
-
ui.liveModal = false;
|
|
5633
|
-
if (!svc) {
|
|
5634
|
-
ui.notice = 'no plan service';
|
|
5635
|
-
draw();
|
|
5636
|
-
continue;
|
|
5637
|
-
}
|
|
5638
|
-
if (svc.mode() === 'live') {
|
|
5639
|
-
try {
|
|
5640
|
-
svc.usePaper();
|
|
5641
|
-
ui.notice = 'fire path: PAPER — fills are simulated again';
|
|
5642
|
-
}
|
|
5643
|
-
catch (e) {
|
|
5644
|
-
ui.notice = `switch refused: ${describeError(e)}`;
|
|
5645
|
-
}
|
|
5646
|
-
draw();
|
|
5647
|
-
continue;
|
|
5648
|
-
}
|
|
5649
|
-
const port = live?.ensureSigner() ?? null;
|
|
5650
|
-
if (!port) {
|
|
5651
|
-
ui.notice = 'LIVE fills need the signer process: start dexterm live (not replay/offline/--no-live)';
|
|
5652
|
-
draw();
|
|
5653
|
-
continue;
|
|
5654
|
-
}
|
|
5655
|
-
if (svc.engine.openPlans().length) {
|
|
5656
|
-
ui.notice = `switch refused: cancel or finish ${svc.engine.openPlans().length} open plan(s) first`;
|
|
5657
|
-
draw();
|
|
5658
|
-
continue;
|
|
5659
|
-
}
|
|
5660
|
-
const ex = new LivePlanExecutor({ signer: port, clock: { now: () => Date.now(), sleep: (ms) => new Promise((r) => setTimeout(r, ms)) }, solUsd: () => state.sol?.price ?? live?.positions.solUsd() ?? null, onChange: () => { if (!done) {
|
|
5661
|
-
plansTick();
|
|
5662
|
-
draw();
|
|
5663
|
-
} }, log: (l) => args.verbose && io.stderr.write(` plans-live ${l}\n`) });
|
|
5664
|
-
ui.notice = 'checking the signer and the fast wallet…';
|
|
5665
|
-
void ex.ready().then((r) => {
|
|
5666
|
-
if (done)
|
|
5667
|
-
return;
|
|
5668
|
-
if (!r.ok) {
|
|
5669
|
-
ui.notice = `LIVE refused: ${r.why}`;
|
|
5670
|
-
draw();
|
|
5671
|
-
return;
|
|
5672
|
-
}
|
|
5673
|
-
try {
|
|
5674
|
-
svc.useLive(ex, { gesture: 'keypress' });
|
|
5675
|
-
ui.notice = `fire path: ${ex.describe(r.fastWallet)} · balance ${r.balanceLamports === null ? '?' : (Number(r.balanceLamports) / 1e9).toFixed(4)} SOL · L again for paper`;
|
|
5676
|
-
}
|
|
5677
|
-
catch (e) {
|
|
5678
|
-
ui.notice = `switch refused: ${describeError(e)}`;
|
|
5679
|
-
}
|
|
5680
|
-
draw();
|
|
5681
|
-
});
|
|
5682
|
-
draw();
|
|
5683
|
-
continue;
|
|
5684
|
-
}
|
|
5685
|
-
draw();
|
|
5686
|
-
continue;
|
|
5687
|
-
}
|
|
5688
5742
|
if (state.screen === 'plans' && state.plansUi?.riskModal) {
|
|
5689
5743
|
const ui = state.plansUi;
|
|
5690
5744
|
if (key.name === 'esc') {
|
|
@@ -5741,7 +5795,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5741
5795
|
draw();
|
|
5742
5796
|
continue;
|
|
5743
5797
|
}
|
|
5744
|
-
// 0.1.5: `/engine [auto|claude|codex]` switches the analyst
|
|
5798
|
+
// 0.1.5: `/engine [auto|claude|codex|ollama]` switches the analyst engine for new conversations; never sent to a model.
|
|
5745
5799
|
const en0 = q.startsWith('/') ? parseEngineCommand(q) : null;
|
|
5746
5800
|
if (en0) {
|
|
5747
5801
|
state.aiInput = '';
|
|
@@ -5749,6 +5803,14 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5749
5803
|
draw();
|
|
5750
5804
|
continue;
|
|
5751
5805
|
}
|
|
5806
|
+
// task-1537: `/display mcap|price` (also `/basis …`, `/set basis …`) sets the price basis; never sent to a model.
|
|
5807
|
+
const dc0 = q.startsWith('/') ? parseDisplayCommand(q) : null;
|
|
5808
|
+
if (dc0) {
|
|
5809
|
+
state.aiInput = '';
|
|
5810
|
+
say('error' in dc0 ? dc0.error : 'mode' in dc0 ? setPriceMode(state, dc0.mode, '/display') : priceModeNotice(state), 'error' in dc0 ? 'warn' : 'info', 6);
|
|
5811
|
+
draw();
|
|
5812
|
+
continue;
|
|
5813
|
+
}
|
|
5752
5814
|
// task-1324: `/scan …` in the chat input is a command, never sent to claude.
|
|
5753
5815
|
const sc0 = q.startsWith('/') ? parseScanCommand(q) : null;
|
|
5754
5816
|
if (sc0) {
|
|
@@ -5909,7 +5971,8 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5909
5971
|
const sc = state.screener;
|
|
5910
5972
|
const editing = state.screen === 'screener' && sc.mode !== 'table';
|
|
5911
5973
|
const plain = key.name === 'char' && !key.ctrl && !key.alt && key.ch !== undefined;
|
|
5912
|
-
|
|
5974
|
+
// task-1508: on the AUTOTRADE screen `c` marks strategies to compare (calm stays reachable from any other screen).
|
|
5975
|
+
if (!editing && plain && key.ch === 'c' && state.screen !== 'autotrade') {
|
|
5913
5976
|
// calm toggle: off ↔ the last active tier
|
|
5914
5977
|
if (isCalm(v)) {
|
|
5915
5978
|
v.intensity = lastIntensity;
|
|
@@ -5923,7 +5986,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5923
5986
|
flusher.invalidate();
|
|
5924
5987
|
continue;
|
|
5925
5988
|
}
|
|
5926
|
-
if (!editing && plain && sc.source === 'fixtures' && Object.hasOwn(DEMO_KEYS, key.ch)) {
|
|
5989
|
+
if (!editing && plain && sc.source === 'fixtures' && state.screen !== 'autotrade' && Object.hasOwn(DEMO_KEYS, key.ch)) {
|
|
5927
5990
|
const kind = DEMO_KEYS[key.ch];
|
|
5928
5991
|
try {
|
|
5929
5992
|
const text = demoEvent(state, kind, rnd);
|
|
@@ -5992,7 +6055,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
5992
6055
|
if (state.screen === 'plans')
|
|
5993
6056
|
syncPlansPopup(); // arm / cancel / tick status changes reach the popup
|
|
5994
6057
|
syncWidgets(); // task-1358 Tier 5: open pack widgets follow the terminal (throttled per widget + topic)
|
|
5995
|
-
// task-1328: `/wallet …` — performed here because link opens a browser
|
|
6058
|
+
// task-1328: `/wallet …` — performed here because link opens a browser.
|
|
5996
6059
|
if (state.pendingWallet) {
|
|
5997
6060
|
const wc = state.pendingWallet;
|
|
5998
6061
|
state.pendingWallet = null;
|
|
@@ -6004,12 +6067,6 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
6004
6067
|
void live.walletCommand(wc).then((line) => {
|
|
6005
6068
|
if (done)
|
|
6006
6069
|
return;
|
|
6007
|
-
if (wc === 'probe' && live.ticket && live.ticket.ticket.mode === 'live') {
|
|
6008
|
-
if (state.screen !== 'ticket') {
|
|
6009
|
-
stack.push(state.screen);
|
|
6010
|
-
state.screen = 'ticket';
|
|
6011
|
-
}
|
|
6012
|
-
}
|
|
6013
6070
|
say(line, wc === 'panic' ? 'warn' : 'info', 8);
|
|
6014
6071
|
scheduleDraw();
|
|
6015
6072
|
}).catch((e) => { say(`wallet ${wc} failed: ${describeError(e)}`, 'warn', 6); scheduleDraw(); });
|
|
@@ -6112,6 +6169,9 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
6112
6169
|
say(state.updateNotice, 'info', 8);
|
|
6113
6170
|
state.updateNotice = null;
|
|
6114
6171
|
}
|
|
6172
|
+
// task-1508: the autotrade screen re-reads the paper runner's status file every 2 s while it is open.
|
|
6173
|
+
if (state.screen === 'autotrade' && state.autotradeUi)
|
|
6174
|
+
autotradeTick(state.autotradeUi);
|
|
6115
6175
|
if (feed) {
|
|
6116
6176
|
if (!state.paused && Date.now() - lastRefresh >= LIVE_REFRESH_MS)
|
|
6117
6177
|
refresh();
|
|
@@ -6199,6 +6259,36 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
6199
6259
|
}
|
|
6200
6260
|
// market → rain: EMA on activity every tick, mood from the data every 4 s unless an event holds it
|
|
6201
6261
|
blendActivity(v, activityFor(state), v.frame % (tickEvery * 8) === 0 && v.frame >= moodHoldUntil);
|
|
6262
|
+
// task-1466: the agent's open PAPER position is marked with this terminal's own live price (the HUD's PnL).
|
|
6263
|
+
// Only a NEW price marks (a 10 s-old view price must not overwrite a fresher one a tool result carried).
|
|
6264
|
+
if (followSession && state.live && state.live.heldMint !== followSession.story.heldMint) {
|
|
6265
|
+
state.live.heldMint = followSession.story.heldMint;
|
|
6266
|
+
state.live.syncFastSet();
|
|
6267
|
+
} // task-1473
|
|
6268
|
+
if (followSession) {
|
|
6269
|
+
const held = followSession.story.heldMint;
|
|
6270
|
+
const px = held ? markPriceFor(state, held) : null;
|
|
6271
|
+
if (held && px !== null && px !== lastHudMark) {
|
|
6272
|
+
followSession.story.mark(held, px, Date.now());
|
|
6273
|
+
lastHudMark = px;
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
// task-1473 r2: AI scan mode + the user's OWN held position → the PnL chip (band row, dock, chart popup)
|
|
6277
|
+
if (state.scan?.engine.on && state.live) {
|
|
6278
|
+
state.ownPnl ??= new OwnPnlTracker();
|
|
6279
|
+
state.ownPnl.update(ownHeldSample(state, Date.now()), Date.now());
|
|
6280
|
+
if (!followSession && state.live.heldMint !== state.ownPnl.mint) {
|
|
6281
|
+
state.live.heldMint = state.ownPnl.mint;
|
|
6282
|
+
state.live.syncFastSet();
|
|
6283
|
+
} // run25: pin it
|
|
6284
|
+
}
|
|
6285
|
+
else if (state.ownPnl?.mint) {
|
|
6286
|
+
state.ownPnl.update(null, 0);
|
|
6287
|
+
if (!followSession && state.live?.heldMint) {
|
|
6288
|
+
state.live.heldMint = null;
|
|
6289
|
+
state.live.syncFastSet();
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6202
6292
|
}
|
|
6203
6293
|
if (state.ai && state.ai.phase === 'thinking') {
|
|
6204
6294
|
state.ai.ticks += 1;
|
|
@@ -6228,6 +6318,7 @@ export function interactive(state, io, depth, args, proc = process) {
|
|
|
6228
6318
|
aiLive: !!(state.ai && state.ai.phase !== 'done'),
|
|
6229
6319
|
awake: v.frame < wakeUntil,
|
|
6230
6320
|
fxBusy: fxBusy(v),
|
|
6321
|
+
hud: followSession !== null && hudAnimating(followSession.story.view(Date.now()), Date.now()),
|
|
6231
6322
|
frame: v.frame,
|
|
6232
6323
|
idleEvery,
|
|
6233
6324
|
}))
|