dexterm-cli 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/packages/cli/src/agent-paper.js +60 -0
  2. package/dist/packages/cli/src/analyst/backend.js +183 -0
  3. package/dist/packages/cli/src/analyst/codex.js +301 -0
  4. package/dist/packages/cli/src/analyst/session.js +9 -2
  5. package/dist/packages/cli/src/app-help-gen.js +15 -6
  6. package/dist/packages/cli/src/doctor.js +58 -2
  7. package/dist/packages/cli/src/live/dev-history.js +99 -0
  8. package/dist/packages/cli/src/live/newpairs.js +12 -0
  9. package/dist/packages/cli/src/live/screens/token-live.js +11 -1
  10. package/dist/packages/cli/src/live/token.js +1 -0
  11. package/dist/packages/cli/src/main.js +122 -8
  12. package/dist/packages/cli/src/mcp-follow.js +37 -7
  13. package/dist/packages/cli/src/record-cmd.js +2 -0
  14. package/dist/packages/cli/src/rules.js +25 -2
  15. package/dist/packages/cli/src/screens/analyst-session.js +5 -3
  16. package/dist/packages/cli/src/screens/analyst.js +4 -3
  17. package/dist/packages/cli/src/version.js +1 -1
  18. package/dist/packages/custom/src/rules.js +2 -2
  19. package/dist/packages/custom/src/schema.js +5 -1
  20. package/dist/packages/data/src/fixtures.js +28 -6
  21. package/dist/packages/data/src/index.js +3 -0
  22. package/dist/packages/data/src/sources/jupiter.js +1 -0
  23. package/dist/packages/data/src/trade/agent/paper-desk.js +336 -0
  24. package/dist/packages/data/src/trade/agent/rendezvous.js +161 -0
  25. package/dist/packages/mcp/assets/kb/app-help/agent-paper-trade.md +38 -0
  26. package/dist/packages/mcp/assets/kb/app-help/ask-the-analyst.md +1 -1
  27. package/dist/packages/mcp/assets/kb/app-help/connect-an-agent.md +1 -1
  28. package/dist/packages/mcp/assets/kb/app-help/dev-history.md +32 -0
  29. package/dist/packages/mcp/assets/kb/app-help/paper-trade.md +1 -1
  30. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +4 -1
  31. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +1 -0
  32. package/dist/packages/mcp/assets/kb/app-help/use-codex-analyst.md +31 -0
  33. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +50 -0
  34. package/dist/packages/mcp/assets/kb/docs/docs-agents-directory-metadata-prepared-not-submitted.md +36 -0
  35. package/dist/packages/mcp/assets/kb/docs/docs-agents-install-in-one-command.md +2 -0
  36. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  37. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-codex-instead-of-claude.md +35 -0
  38. package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-or-linux-npm.md +1 -1
  39. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-expectations.md +1 -1
  40. package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-powershell-experimental.md +1 -1
  41. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +2 -0
  42. package/dist/packages/mcp/assets/kb/docs/docs-configuration-rules-engine.md +1 -0
  43. package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-analyst-s-launch.md +2 -0
  44. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +13 -3
  45. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  46. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-supported-platforms.md +2 -2
  47. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +0 -2
  48. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +32 -0
  49. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +1 -1
  50. package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +44 -0
  51. package/dist/packages/mcp/assets/kb/docs/docs-screens-how-well-it-predicts-measured-held-out.md +46 -0
  52. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +1 -1
  53. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +2 -0
  54. package/dist/packages/mcp/src/index.js +5 -2
  55. package/dist/packages/mcp/src/paper-agent.js +92 -0
  56. package/dist/packages/mcp/src/server.js +206 -12
  57. package/dist/packages/mcp/src/spectator-sink.js +73 -4
  58. package/dist/packages/mcp/src/user-content.js +1 -0
  59. package/dist/packages/recorder/src/dev-history.js +129 -0
  60. package/dist/packages/recorder/src/index.js +1 -0
  61. package/dist/packages/recorder/src/recorder.js +23 -0
  62. package/dist/packages/recorder/src/schema.js +11 -0
  63. package/package.json +1 -1
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: docs-agents-agent-paper-trading
3
+ title: Bring your own agent › Agent paper trading
4
+ kind: docs
5
+ page: agents
6
+ page_title: Bring your own agent
7
+ heading: Agent paper trading
8
+ anchor: agent-paper-trading
9
+ url: https://dexterm.xyz/docs/agents#agent-paper-trading
10
+ tags:
11
+ - docs
12
+ - agents
13
+ - agent
14
+ - paper
15
+ - trading
16
+ - bring
17
+ - own
18
+ confidence: high
19
+ owner: maintainers
20
+ origin: docs-generated
21
+ reviewed_by: generated@2026-10-16
22
+ generated_from: dexterm/site content/docs (task-1420)
23
+ sources:
24
+ - site/content/docs/agents.md
25
+ date: 2026-10-16
26
+ ---
27
+
28
+ **Status: experimental, paper only, off by default (since 0.1.5).** By default an agent can only read and navigate; it cannot trade at all. If you choose to, you can let an agent you connected over `dexterm mcp` run a full **paper** trade loop inside a **trading mandate** you armed yourself. Nothing here is live: every fill books the **simulated** paper wallet.
29
+
30
+ Two steps, both yours:
31
+
32
+ 1. Draft and arm a mandate (`dexterm mandate draft` / `arm`, or the plans screen). Its caps are per-trade, per-coin and total size, a loss limit, open positions, price impact, allowed sides and an optional mint allow-list, and arming needs a physical keypress.
33
+ 2. Start the terminal with the agent activity view and name that mandate: `dexterm --spectate-mcp --agent-paper <mandate-id>`. Quitting turns it off again.
34
+
35
+ While it is on, the agent can use six tools:
36
+
37
+ - `paper_open_ticket` opens a simulated ticket for a mint and side. It stays inert until confirmed.
38
+ - `paper_size_ticket` sets the simulated size. A buy over any cap is **refused**, never clamped up.
39
+ - `paper_confirm` re-checks the caps and your controls, fetches a fresh keyless quote and books the simulated fill.
40
+ - `paper_close` closes a fraction of a held simulated position.
41
+ - `paper_positions` and `paper_pnl` read the simulated positions and PnL. They are read-only.
42
+
43
+ The guarantees, each covered by tests:
44
+
45
+ - **It can never sign or send.** No MCP tool can build, sign or send a real transaction, touch a wallet or key, or change a cap. The paper tools book the simulated ledger only. A registry test enumerates every MCP tool, fails on any tool not on its reviewed allow-list, and checks that the paper path imports nothing that signs, sends or arms.
46
+ - **Caps come from your mandate.** The agent's tools never carry caps: on every call the server re-reads the mandate from the hash-chained plans journal you armed it in, and a journal that fails its hash check is refused. That protects the mandate from the agent's tools; it is not tamper-proofing against other software running as your own user. The agent runs at the most restricted confirmation tier, so its per-trade cap is a quarter of the mandate's. Revoking the mandate, letting it expire or pressing the panic key stops the agent on its next call.
47
+ - **You stay in control, and you see everything.** Every call and every refusal shows in this terminal's agent activity, labelled PAPER, and is appended to its own hash-chained audit journal. Ctrl+P pauses and resumes, Ctrl+K stops it for the rest of the run, and any key while the agent is driving takes over; all of them halt its paper calls. Trading calls are refused while nothing is watching.
48
+ - **Only the agent you connected.** The in-app analyst and the scan never get the paper tools. The loopback agent bus does not offer them either.
49
+
50
+ Figures are simulated. They are for learning the flow, not a result, and never a prediction or a recommendation. This is not financial advice.
@@ -0,0 +1,36 @@
1
+ ---
2
+ id: docs-agents-directory-metadata-prepared-not-submitted
3
+ title: Bring your own agent › Install in one command › Directory metadata (prepared, not submitted)
4
+ kind: docs
5
+ page: agents
6
+ page_title: Bring your own agent
7
+ heading: Directory metadata (prepared, not submitted)
8
+ anchor: directory-metadata-prepared-not-submitted
9
+ url: https://dexterm.xyz/docs/agents#directory-metadata-prepared-not-submitted
10
+ tags:
11
+ - docs
12
+ - agents
13
+ - directory
14
+ - metadata
15
+ - prepared
16
+ - submitted
17
+ - install
18
+ - command
19
+ - bring
20
+ - own
21
+ - agent
22
+ confidence: high
23
+ owner: maintainers
24
+ origin: docs-generated
25
+ reviewed_by: generated@2026-10-16
26
+ generated_from: dexterm/site content/docs (task-1420)
27
+ sources:
28
+ - site/content/docs/agents.md
29
+ date: 2026-10-16
30
+ ---
31
+
32
+ The source repository includes an official MCP Registry `server.json` and a
33
+ Smithery MCPB manifest. They describe the same local stdio command and the same
34
+ (generated from the app: see https://dexterm.xyz/docs/agents#directory-metadata-prepared-not-submitted; app-help has the exact keys and commands)-tool surface that `dexterm mcp doctor` reports. No directory
35
+ listing is implied: publication remains a separate maintainer action, and the
36
+ Registry package marker must first ship in a new `dexterm-cli` release.
@@ -47,3 +47,5 @@ $ dexterm mcp doctor # spawn the server and list the tools you
47
47
  Flags: `--config FILE` writes to a specific file instead of the client default; `--print` only prints; `--node` emits a `node <path> mcp` command for when `dexterm` is not on your PATH; `--yes` skips the confirmation prompt. On a non-interactive shell the command prints the plan and writes nothing unless you pass `--yes`.
48
48
 
49
49
  `dexterm mcp doctor` spawns the server over stdio, does the MCP handshake, and lists the tools, prompts and resources the agent sees — proof the connection works before you point an agent at it.
50
+
51
+ Codex is supported both ways: as an MCP client here (`dexterm mcp install codex`), and, since 0.1.5, as the engine that runs the in-app analyst on your own Codex login (use Codex instead of Claude (https://dexterm.xyz/docs/analyst#use-codex-instead-of-claude)).
@@ -22,4 +22,4 @@ sources:
22
22
  date: 2026-10-16
23
23
  ---
24
24
 
25
- The panel shows a one-line fix (`npm i -g @anthropic-ai/claude-code && claude login`) and the rest of dexterm keeps working. `dexterm doctor` reports the Claude CLI line as a warning, not a failure.
25
+ The panel shows a one-line fix (`npm i -g @anthropic-ai/claude-code && claude login`) and the rest of dexterm keeps working. `dexterm doctor` reports the Claude CLI line as a warning, not a failure. With Codex installed, `auto` runs the analyst on Codex instead.
@@ -0,0 +1,35 @@
1
+ ---
2
+ id: docs-analyst-use-codex-instead-of-claude
3
+ title: The analyst › Use Codex instead of Claude
4
+ kind: docs
5
+ page: analyst
6
+ page_title: The analyst
7
+ heading: Use Codex instead of Claude
8
+ anchor: use-codex-instead-of-claude
9
+ url: https://dexterm.xyz/docs/analyst#use-codex-instead-of-claude
10
+ tags:
11
+ - docs
12
+ - analyst
13
+ - use
14
+ - codex
15
+ - instead
16
+ - claude
17
+ confidence: high
18
+ owner: maintainers
19
+ origin: docs-generated
20
+ reviewed_by: generated@2026-10-16
21
+ generated_from: dexterm/site content/docs (task-1420)
22
+ sources:
23
+ - site/content/docs/analyst.md
24
+ date: 2026-10-16
25
+ ---
26
+
27
+ *Since 0.1.5.* The analyst can run on **your own Codex CLI login** instead of Claude. dexterm ships no key and reads no credential: the `codex` you set up and signed in to does the work, on your plan.
28
+
29
+ - **Pick it:** `--analyst-engine codex` at launch, `DEXTERM_ANALYST_ENGINE=codex`, `"analyst": {"engine": "codex"}` in `~/.dexterm/config.json`, or type `/engine codex` in the chat box (it applies to new conversations; `/engine` alone shows the current engine and which CLIs it found). The default, `auto`, prefers Claude Code and falls back to Codex. Only you choose: the analyst cannot change its own engine.
30
+ - **Same guarantees, enforced in the Codex CLI itself.** dexterm runs `codex exec --json` with your Codex config and rules files ignored, apps, plugins, hooks, skills discovery and sub-agents off, the shell and exec tools removed, the sandbox read-only, approvals `never` and web search off. Its only MCP server is the read-only `dexterm mcp`, limited by an allow-list: the read tools plus the UI actions the session allows (none in scan mode (https://dexterm.xyz/docs/scan)), never a paper or trade tool. If Codex still reports a command, a file change, a web search or any other tool, dexterm stops the turn and says so. The firewalled CONTEXT block, the skills and your own skill overlays are the same; the skill playbook is passed inline because Codex has no plugin directory.
31
+ - **What is different.** Codex reports whole messages, so an answer appears per message rather than word by word. The caption shows `codex plan` instead of a dollar cost (Codex reports tokens, not cost). The model is your Codex default for your login; pin one with `DEXTERM_CODEX_MODEL`. Which models a ChatGPT plan allows is decided by your plan, not by dexterm.
32
+ - **Codex errors are one line each:** no Codex CLI (`npm i -g @openai/codex && codex login`), a logged-out Codex (`codex login`), or an old one (update; the lock needs codex-cli 0.163.0 or newer, and an unknown lock flag fails the run instead of loosening it). `dexterm doctor` shows a Codex CLI line with the version and whether you are logged in. On Windows dexterm only starts a `codex.exe` on PATH; the npm `codex.cmd` shim is not started.
33
+ - **How we checked.** Offline tests drive a fake `codex` through the real app path, and a measurement harness runs the real codex-cli 0.163.0 against a local mock model (no account, no model): the tool list Codex sends is the dexterm allow-list only, and forged shell, exec, patch, sub-agent and ticket calls come back "unsupported" without running. Answer quality and speed against Claude have not been benchmarked.
34
+
35
+ Codex is also supported the other way round, as an MCP client that drives dexterm: `dexterm mcp install codex` (see bring your own agent (https://dexterm.xyz/docs/agents#install-in-one-command)).
@@ -36,4 +36,4 @@ date: 2026-10-16
36
36
  npm i -g dexterm-cli
37
37
  ```
38
38
 
39
- Linux x64 with Node 22 and 24 and macOS on Intel processors have passed the full automated tests plus a clean install-and-start check. Apple Silicon macOS has not yet run the project's written automated test. No `sudo` (the command that grants administrator power) is required for this npm path.
39
+ Linux x64 with Node 22 and 24 and macOS on Intel processors have passed the full automated tests plus a clean install-and-start check. Apple Silicon macOS passed the same automated tests in CI for 0.1.4. No `sudo` (the command that grants administrator power) is required for this npm path.
@@ -26,4 +26,4 @@ sources:
26
26
  date: 2026-10-16
27
27
  ---
28
28
 
29
- Windows x64 is experimental and has not yet run the written CI job. File permissions rely on your Windows user profile; voice has no Windows speech engine; browser-opening actions show a URL instead; `doctor` may print a macOS/Linux repair command; and the npm-installed `claude.cmd` is not detected as a usable analyst binary. These limits do not change the key rule: dexterm never holds your wallet keys.
29
+ Windows x64 is experimental: the full CI suite passed for 0.1.4, though some file-permission checks are skipped. File permissions rely on your Windows user profile; voice has no Windows speech engine; browser-opening actions show a URL instead; `doctor` may print a macOS/Linux repair command; and the npm-installed `claude.cmd` is not detected as a usable analyst binary. These limits do not change the key rule: dexterm never holds your wallet keys.
@@ -36,4 +36,4 @@ Install Node.js LTS first, reopen PowerShell, and run:
36
36
  npm i -g dexterm-cli
37
37
  ```
38
38
 
39
- The Windows path is experimental and not yet tested by the project. The core read-only screens are expected to work, but voice has no Windows engine, browser-opening shortcuts do not work, and some repair instructions are written for macOS/Linux. See Windows expectations (https://dexterm.xyz/docs/beginners#windows-expectations).
39
+ The Windows path is experimental: the project's CI suite passed for 0.1.4, but nobody has used it much by hand. The core read-only screens are expected to work, but voice has no Windows engine, browser-opening shortcuts do not work, and some repair instructions are written for macOS/Linux. See Windows expectations (https://dexterm.xyz/docs/beginners#windows-expectations).
@@ -33,6 +33,8 @@ date: 2026-10-16
33
33
  | `DEXTERM_INTENSITY` | `off` · `normal` · `fever` |
34
34
  | `DEXTERM_OFFLINE=1` | refuse live data; `--fixtures` or `--replay` only (tests and CI) |
35
35
  | `DEXTERM_ANALYST_MODEL` | the Claude model for analyst turns (default `(generated from the app: see https://dexterm.xyz/docs/configuration#environment-variables; app-help has the exact keys and commands)`) |
36
+ | `DEXTERM_ANALYST_ENGINE` | `auto` (default) · `claude` · `codex`: which of your CLIs runs the analyst (since 0.1.5; also `--analyst-engine` and `analyst.engine` in `config.json`) |
37
+ | `DEXTERM_CODEX_MODEL` | pin the Codex model for analyst turns (default: your Codex default) |
36
38
  | `DEXTERM_POPUP_SHELL` | path to a prebuilt native chart window |
37
39
  | `DEXTERM_POPUP_HOTKEY` · `DEXTERM_POPUP_HOTKEY_CLOSE` | override the popup chords without a config file |
38
40
  | `DEXTERM_RECORDER=0` | history recorder off (same as `--no-recorder`; `off`, `false` and `no` work too) |
@@ -31,6 +31,7 @@ vol5m.velocity > 3 and cluster < 20 and liq > 25K for 2 ticks then alert(up, "su
31
31
  ```
32
32
 
33
33
  - The open token's live holder and rug signals (https://dexterm.xyz/docs/screens#live-holder-and-rug-signals) are rule fields too: `top10`, `dev`, `bundled`, `snipers`, `fresh`, `cluster`, `lplocked`, `bonding`, `mintable`, `freezable`, with `.delta` and `.velocity` (`dexterm rules fields` lists them all).
34
+ - Since 0.1.5: `devrugs` is the coin's creator's labelled prior rugs from the local record (dev history (https://dexterm.xyz/docs/screens#dev-history); unknown = no match), e.g. `when devrugs >= 2 then alert(warn, "new coin from a wallet that rugged before") scope newpairs`.
34
35
  - The text is parsed, never evaluated as code. Every rule has a cooldown (default 10 m) and a max per hour (default 12, hard cap 120), so a bad rule is annoying, not dangerous.
35
36
  - Rules can **alert**, annotate your own watchlist (`tag`, `watch`), open the read-only windows and ring the bell, or **propose**. `ticket` and `ask` do not act: each fire becomes a proposal card on the rules screen (status proposal, your keypress required, armed by nobody); acting on it opens the PAPER ticket in its editing phase, where you size and confirm, or asks the analyst. Nothing a rule does reaches an order plan, a mandate, the signer or the gate, and a rule argument named like money (`arm`, `auto`, `confirm`, `execute`, `sign`, `send`) is refused at parse time, in text and in JSON.
36
37
  - `ctrl+r` opens the RULES screen: the list with enabled / last fired / source, `i` types a new rule with a live "would match N of M rows right now" dry-run, `tab` the proposals pane, `space` toggles, `d` removes. `dexterm rules list | add "<rule>" | remove | enable | disable <id> | check "<rule>" | dry-run "<rule>" | fields`. Rules live in `config.json`, every change through the journal.
@@ -25,3 +25,5 @@ date: 2026-10-16
25
25
  ---
26
26
 
27
27
  For the record, the analyst is spawned as `claude -p` with `--output-format stream-json`, `--tools ''` (no built-in tools), `--strict-mcp-config` pointing at the dexterm server only, `--allowedTools mcp__dexterm__*`, `--permission-mode dontAsk`, `--setting-sources ''` (none of your Claude Code settings or other plugins), the dexterm plugin directory for the skills, the analyst system prompt appended, a JSON schema for structured skills, and `--disallowedTools` for the action tools in scan mode. It cannot read files, run commands or reach any other MCP server from inside dexterm.
28
+
29
+ On Codex (since 0.1.5) it is spawned as `codex exec --json` with `--ignore-user-config` and `--ignore-rules`, `--disable` for `shell_tool`, `unified_exec`, `apps`, `plugins`, `hooks`, `multi_agent`, `browser_use` and `computer_use` (an unknown name fails the run), `sandbox_mode="read-only"`, `approval_policy="never"`, `web_search="disabled"`, the system prompt as `developer_instructions`, one `dexterm` MCP server with an `enabled_tools` allow-list and its environment forwarded by name (no value on the command line), and `--output-schema` for structured skills. The same lock is repeated on every resumed turn. `analyst.engine` in `config.json` is yours alone: a model or agent proposal that touches it is refused.
@@ -23,6 +23,16 @@ sources:
23
23
  date: 2026-10-16
24
24
  ---
25
25
 
26
+ The repository README's short path is: choose one installer, then start dexterm.
27
+
28
+ ```sh repository quick start
29
+ $ npm i -g dexterm-cli
30
+ $ brew install dexterm-xyz/dexterm/dexterm
31
+ $ dexterm
32
+ ```
33
+
34
+ Do not run both install lines. The first is npm; the second is the one-line Homebrew alternative for macOS.
35
+
26
36
  ```sh install with npm
27
37
  $ npm i -g dexterm-cli
28
38
  $ dexterm doctor
@@ -31,10 +41,10 @@ $ dexterm doctor
31
41
  The package is named `dexterm-cli` because npm refuses the name `dexterm` as too similar to `xterm`. The command it installs is `dexterm`. The package ships compiled JavaScript, so it needs no build step. Every GitHub release carries a build-provenance attestation and `SHA256SUMS` you can verify (see the trust page (https://dexterm.xyz/trust/)).
32
42
 
33
43
  ```sh install from the release
34
- $ # download dexterm-0.1.3-src.tar.gz and SHA256SUMS from the releases page, then:
44
+ $ # download dexterm-0.1.5-src.tar.gz and SHA256SUMS from the releases page, then:
35
45
  $ shasum -a 256 -c SHA256SUMS --ignore-missing
36
- $ gh attestation verify dexterm-0.1.3-src.tar.gz --repo dexterm-xyz/dexterm
37
- $ tar -xzf dexterm-0.1.3-src.tar.gz && cd dexterm-0.1.3
46
+ $ gh attestation verify dexterm-0.1.5-src.tar.gz --repo dexterm-xyz/dexterm
47
+ $ tar -xzf dexterm-0.1.5-src.tar.gz && cd dexterm-0.1.5
38
48
  $ ./install.sh
39
49
  $ dexterm doctor
40
50
  ```
@@ -32,4 +32,4 @@ date: 2026-10-16
32
32
  | optional: a free RPC key | the ranked top-20 holder list and faster chain reads | Helius, Alchemy, QuickNode or Ankr free tier; see configuration (https://dexterm.xyz/docs/configuration). |
33
33
  | optional: Rust (`cargo`) | the native docked chart window | without it the chart popup opens in your browser; see popup chart (https://dexterm.xyz/docs/popup-chart). |
34
34
 
35
- > note: dexterm 0.1.3 is out (2026-10-16), open source under AGPL-3.0-only at github.com/dexterm-xyz/dexterm (https://github.com/dexterm-xyz/dexterm). It is early: trading is experimental and has known bugs. The release ships checksums, a build-provenance attestation and an SBOM, and its VirusTotal result (including flags we explain) is on the trust page (https://dexterm.xyz/trust/). It is also on npm as `dexterm-cli` (https://www.npmjs.com/package/dexterm-cli); the command it installs is still `dexterm`.
35
+ > note: dexterm 0.1.5 is out (2026-10-18), open source under AGPL-3.0-only at github.com/dexterm-xyz/dexterm (https://github.com/dexterm-xyz/dexterm). It is early: trading is experimental and has known bugs. The release ships checksums, a build-provenance attestation and an SBOM, and its VirusTotal result (including flags we explain) is on the trust page (https://dexterm.xyz/trust/). It is also on npm as `dexterm-cli` (https://www.npmjs.com/package/dexterm-cli); the command it installs is still `dexterm`.
@@ -30,8 +30,8 @@ This table says what each platform has actually been through, not what we expect
30
30
  |---|---|---|
31
31
  | Linux x64, Node 22 and 24 | tested | the full suite on every push in CI (Ubuntu), and the same steps in Docker (`node:22-bookworm`, `node:24-bookworm`), including the install smoke |
32
32
  | macOS on Intel | tested | the full suite on every change, the install smoke, and a Homebrew install (slow: Homebrew builds Node from source on Intel) |
33
- | macOS on Apple Silicon | not yet tested | a CI leg on an Apple Silicon runner (`macos-14`) and a manual Homebrew job (tap, trust, install, `brew test`) are written and lint clean; they run for the first time with the next release |
34
- | Windows x64 | experimental, not yet tested | a CI leg (`windows-latest`) is written: typecheck and the install smoke must pass, the test suite may fail without failing the run. It has not run yet. Known gaps are listed below |
33
+ | macOS on Apple Silicon | tested in CI (0.1.4) | the full suite on an Apple Silicon runner (`macos-14`) passed on the 0.1.4 release candidate. The manual Homebrew job (tap, trust, install, `brew test`) has not run yet |
34
+ | Windows x64 | experimental, tested in CI (0.1.4) | the `windows-latest` leg runs the full suite and it passed on the 0.1.4 release candidate (a few tests that need POSIX file permissions or Unix-only tools are skipped, and ACL-based file isolation is not verified). Known gaps are listed below |
35
35
  | WSL2 | not tested separately | it is Linux, so expect the Linux behaviour |
36
36
 
37
37
  Known gaps on Windows (none of them affect key safety: dexterm never holds a wallet key on any platform):
@@ -25,8 +25,6 @@ date: 2026-10-16
25
25
 
26
26
  **The part of the LARP check that can be replayed on history is weak, and the model stage cannot be replayed at all. dexterm prints only what it measured, including the weak numbers.**
27
27
 
28
- The public measurement scorecard (https://dexterm.xyz/scorecard/) is generated directly from the checked-in research JSON. It includes the held-out dataset, confidence intervals, rolling skill-care result, frozen canary and every currently unmeasured stage; the weak 0.054 links-only MCC is not rounded into a favorable claim.
29
-
30
28
  **Skills backtest (0.1.2).** `dexterm backtest --subject skills` replays the replayable parts of the default analyst skills on recorded history: 1,054 coins from the project's recorder, labelled by a 72 h keyless outcome (bad = rug or dead). Tuning only ever sees the train split; the numbers below are the later, held-out test split of 328 coins, of which 18.6 % went bad.
31
29
 
32
30
  - **LARP check, links only.** The links-based baseline is a weak signal: MCC 0.039 before tuning, 0.054 after. Its tuned `LARP` grade caught 4 of 14 bad coins, too few to rate. `EARLY_BUT_REAL` coins still went bad 10.3 % of the time, and coins with no website 29.2 %. The skill now treats the baseline as a weak prior and never calls `LARP` from links alone.
@@ -0,0 +1,32 @@
1
+ ---
2
+ id: docs-paper-trading-letting-an-agent-paper-trade-experimental
3
+ title: Paper trading, live trading and the safety model › Letting an agent paper trade (experimental)
4
+ kind: docs
5
+ page: paper-trading
6
+ page_title: Paper trading, live trading and the safety model
7
+ heading: Letting an agent paper trade (experimental)
8
+ anchor: letting-an-agent-paper-trade-experimental
9
+ url: https://dexterm.xyz/docs/paper-trading#letting-an-agent-paper-trade-experimental
10
+ tags:
11
+ - docs
12
+ - paper-trading
13
+ - letting
14
+ - agent
15
+ - paper
16
+ - trade
17
+ - experimental
18
+ - trading
19
+ - live
20
+ - safety
21
+ - model
22
+ confidence: high
23
+ owner: maintainers
24
+ origin: docs-generated
25
+ reviewed_by: generated@2026-10-16
26
+ generated_from: dexterm/site content/docs (task-1420)
27
+ sources:
28
+ - site/content/docs/paper-trading.md
29
+ date: 2026-10-16
30
+ ---
31
+
32
+ If you want to, you can let an agent connected over `dexterm mcp` run the whole PAPER loop (open, size, confirm and close) inside a **trading mandate** you armed, by starting `dexterm --spectate-mcp --agent-paper <mandate-id>`. It is paper only. The agent can never sign, send a real transaction, touch keys or raise a cap, a size over your caps is refused, and you can pause, take over or stop it at any time. See Agent paper trading (https://dexterm.xyz/docs/agents#agent-paper-trading). It is experimental and off by default, and simulated figures are not a result.
@@ -32,6 +32,6 @@ These are the invariants from the spec; hold us to them:
32
32
  1. **Non-custodial.** You hold the keys. dexterm builds, simulates and hands off; your wallet signs. The Phantom bridge stores only your public key.
33
33
  2. **Keypress gate on by default.** Models and voice create proposals; arming needs a physical keypress or a wallet approval. Opting out, if ever offered, needs an explicit "this is less safe" confirmation, is local, logged and reversible, and the signer caps still apply.
34
34
  3. **Caps live in the signer, not the UI.** The signer re-simulates and re-checks every transaction (per-trade and daily USD caps, price-impact and slippage ceilings, the SOL-out cap, the token-balance diff, a positive description of what a Jupiter swap looks like: exactly one route instruction bound to your order, no token transfers, burns or approvals to anyone) and refuses out of policy even if the UI, the model or a popup is compromised.
35
- 4. **The AI has no interface to the signer.** No build, sign or send tool exists for the model; its only write-shaped tool opens a PAPER ticket, and a test proves that even on a live-capable context it yields a paper ticket and the signer is never called. The scan mode and the MCP server import nothing signer-shaped.
35
+ 4. **The AI has no interface to the signer.** No build, sign or send tool exists for the model. Its write-shaped tools are paper only: `open_ticket` opens a PAPER ticket, and (from 0.1.5, only if you start `--agent-paper` on a mandate you armed) an agent you connected can use the `paper_*` tools to book the **simulated** ledger inside that mandate. A registry test enumerates every MCP tool and fails if any could reach a signing, sending or cap-changing path, and the paper path imports nothing signer-shaped. Even on a live-capable context the model's ticket yields a paper ticket and the signer is never called. The scan mode and the MCP server import nothing signer-shaped.
36
36
 
37
37
  > warn: paper results are not predictive, and live results are real. Paper fills take the quote at its word; a real fill pays real slippage, priority fees and failed transactions, and most memecoins go to zero. Nothing here is financial advice.
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: docs-screens-dev-history
3
+ title: Screens tour › Dev history
4
+ kind: docs
5
+ page: screens
6
+ page_title: Screens tour
7
+ heading: Dev history
8
+ anchor: dev-history
9
+ url: https://dexterm.xyz/docs/screens#dev-history
10
+ tags:
11
+ - docs
12
+ - screens
13
+ - dev
14
+ - history
15
+ - tour
16
+ confidence: high
17
+ owner: maintainers
18
+ origin: docs-generated
19
+ reviewed_by: generated@2026-10-16
20
+ generated_from: dexterm/site content/docs (task-1420)
21
+ sources:
22
+ - site/content/docs/screens.md
23
+ date: 2026-10-16
24
+ ---
25
+
26
+ *Since 0.1.5.* "Has this deployer rugged before?" The ON-CHAIN panel adds the creator's **recorded** track record, from dexterm's own history recorder (https://dexterm.xyz/docs/configuration#history-recorder), keyless:
27
+
28
+ | row | what it shows |
29
+ | --- | --- |
30
+ | Dev rugs (recorded) | rugs out of the creator's **labelled** prior launches, with the sample (`2/5 · n=5`); red at two or more (the `serial_rugger` flag), `unknown` with no labelled record |
31
+ | Dev launches seen | prior launches the recorder linked to this wallet, and which source named the creator |
32
+ | Dev sold its bag | median minutes from launch to the dev's exit on prior launches (held over 0.5 % of supply, then 0.1 % or less), with n |
33
+ | Dev linked | the creator's funder or the linked cluster it sits in (same funder, same-block bundle, transfers) from the wallet-intel scan (https://dexterm.xyz/docs/screens#bubble-map), and how many of those wallets launched coins that rugged |
34
+
35
+ - **Where the creator comes from.** The wallet a keyless source names when dexterm sees the coin: Jupiter's `dev` field on every token row, a PumpPortal create, or RugCheck's new-token feed. The first source wins and is recorded with the time. GoPlus is asked only by the MCP tool, as a last resort.
36
+ - **Where the outcomes come from.** The recorder's code-only labels (rug, dead, fade, flat, runner; the 72-hour label when known, else the longest one settled). They are the keyless-proxy tier: a rug is a liquidity collapse the recorder saw, not a verified on-chain drain.
37
+ - **Honest by construction.** Every number names its sample and age. No record is `unknown`, never "clean". Only coins this machine's recorder saw count, so a deployer's launches dexterm never saw are missing; run `dexterm record` (https://dexterm.xyz/docs/configuration#headless-recorder-dexterm-record) to keep it fed. It is a risk flag from observed history, never a certificate.
38
+ - **Alerts.** The rule field `devrugs` is the creator's labelled rugs (unknown = no match). A brand-new pair from a wallet with a record joins the rule rows, so this fires on launch:
39
+
40
+ ```text
41
+ when devrugs >= 2 then alert(warn, "new coin from a wallet that rugged before") scope newpairs
42
+ ```
43
+
44
+ - **For agents.** The read-only `dev_history` MCP tool returns the same report by mint or creator wallet; `get_dev_history` and the dossier fill prior launches and rugs from it (tier `local-history`), so the deterministic analysis pack raises `serial_rugger` on recorded history.
@@ -0,0 +1,46 @@
1
+ ---
2
+ id: docs-screens-how-well-it-predicts-measured-held-out
3
+ title: Screens tour › Dev history › How well it predicts (measured, held out)
4
+ kind: docs
5
+ page: screens
6
+ page_title: Screens tour
7
+ heading: How well it predicts (measured, held out)
8
+ anchor: how-well-it-predicts-measured-held-out
9
+ url: https://dexterm.xyz/docs/screens#how-well-it-predicts-measured-held-out
10
+ tags:
11
+ - docs
12
+ - screens
13
+ - well
14
+ - predicts
15
+ - measured
16
+ - held
17
+ - out
18
+ - dev
19
+ - history
20
+ - tour
21
+ confidence: high
22
+ owner: maintainers
23
+ origin: docs-generated
24
+ reviewed_by: generated@2026-10-16
25
+ generated_from: dexterm/site content/docs (task-1420)
26
+ sources:
27
+ - site/content/docs/screens.md
28
+ date: 2026-10-16
29
+ ---
30
+
31
+ Measured 2026-10-18 on the history recorder's own coins, with the same held-out window as the skill backtest (https://dexterm.xyz/docs/larp) (the newest 30 % of coins by first sighting, never used to tune anything; the rule is fixed in advance: `serial_rugger` = two or more labelled rugs). Each coin is judged only on what was known at its first sighting: prior launches recorded before it, and labels already settled by then. Outcomes are the recorder's 72-hour labels (keyless-proxy tier): **rug**, and **bad** = rug or dead. The labelled history covers 2026-10-10 to 2026-10-13.
32
+
33
+ - **Too few repeat deployers to measure the rug-history signal yet.** Of 9405 creator wallets behind 24724 recorded coins, 2127 launched two or more of them. In the held-out window 1 of 143 coins (skill-backtest set) and 4 of 387 (all 72-hour-labelled coins) had a creator with a prior labelled rug; across all 1289 labelled coins, 4. There is no number to publish for "a prior rug predicts a rug" until the record is longer. The panel says `unknown` in exactly this case.
34
+ - **What the held-out window does show.** Base rates: rug 5.7% (22/387, 95% CI 3.8–8.5%), bad 41.6% (161/387, 95% CI 36.8–46.6%). Coins whose creator had labelled prior launches and no rug: rug 6.4% (5/78, 95% CI 2.8–14.1%), bad 30.8% (24/78, 95% CI 21.6–41.7%); that is a small sample and the intervals overlap the base rate, so it is not evidence that a "clean" record means anything.
35
+ - **Exploratory, chosen after seeing the result above** (so treat it as a lead, not a finding): coins whose creator had three or more labelled prior launches, mostly *not* rug or dead, went bad 16.1% (5/31, 95% CI 7.1–32.6%) of the time in the held-out window, against 44.0% (155/352, 95% CI 38.9–49.3%) for coins with fewer labelled priors; only 4 coins had a creator whose priors were mostly bad, too few to say anything.
36
+ - **Why so thin:** the recorder's labelled history is about a week old, and a prior launch only counts once its own label had settled before the coin was first seen.
37
+ - **Jupiter's count of coins this dev minted** (keyless, at first sighting, held-out, 367 coins with a value; base bad rate 39.5% (145/367, 95% CI 34.6–44.6%)):
38
+
39
+ | dev minted | coins | rug | bad (rug or dead) |
40
+ | --- | --- | --- | --- |
41
+ | 1 | 128 | 3.9% (5/128, 95% CI 1.7–8.8%) | 43.8% (56/128, 95% CI 35.5–52.4%) |
42
+ | 2-5 | 59 | 0.0% (0/59, 95% CI 0.0–6.1%) | 20.3% (12/59, 95% CI 12.0–32.3%) |
43
+ | 6-20 | 28 | 3.6% (1/28, 95% CI 0.6–17.7%) | 28.6% (8/28, 95% CI 15.2–47.1%) |
44
+ | 21+ | 152 | 4.6% (7/152, 95% CI 2.2–9.2%) | 45.4% (69/152, 95% CI 37.7–53.3%) |
45
+
46
+ We will re-run this as the recorder grows and publish the result, good or bad. It is a risk flag from observed history, never a certificate.
@@ -32,7 +32,7 @@ date: 2026-10-16
32
32
  An agent connected over MCP (Claude Code, Claude Desktop, Cursor, Codex — set up with `dexterm mcp install`) runs the `dexterm mcp` server as its own process, which the app cannot see into. Since 0.1.4, run the app with `dexterm --spectate-mcp` and the running terminal **follows** that agent:
33
33
 
34
34
  - While the flag is on, the app writes a small local rendezvous under `~/.dexterm` (mode 0600). The `dexterm mcp` server notices it and starts emitting one record per tool call to a local channel the app tails. When you are not following, the file is absent and the server writes nothing — there is no cost when nobody is watching.
35
- - A **navigation** call (open a token, switch screen or feed, set a filter, highlight a row, open the paper ticket) moves the real screen, with a status line like `agent <name>: opened BONK`. These are the same read-only moves the analyst makes; nothing an agent does here can size, confirm, sign, send or raise a cap.
35
+ - A **navigation** call (open a token, switch screen or feed, set a filter, highlight a row, open the paper ticket) moves the real screen, with a status line like `agent <name>: opened BONK`. These are the same read-only moves the analyst makes; nothing an agent does here can size, confirm, sign, send or raise a cap. From 0.1.5, if you start with `--agent-paper <mandate-id>`, the agent's **simulated** trades show here too as `PAPER …` rows. They never move your screen, and Ctrl+P, Ctrl+K and taking over halt them (agent paper trading (https://dexterm.xyz/docs/agents#agent-paper-trading)).
36
36
  - The controls are at your keyboard: **ctrl+p** pauses and resumes the mirroring, **ctrl+k** stops following (it removes the rendezvous, so the server stops emitting), and **any other key** hands control back to you — the agent can never move the screen out from under you.
37
37
  - A read-only, loopback, token-gated stream opens too, so `dexterm spectate --info` shows its endpoint while you follow. Every record is authenticated by the rendezvous token before it is folded, and every shared copy is redacted (see what is redacted (https://dexterm.xyz/docs/spectator#what-is-redacted)). This is on the owner's own machine, so follow mode itself shows full detail.
38
38
 
@@ -29,6 +29,8 @@ date: 2026-10-16
29
29
  | `! Terminal colour: 256-colour only` | the terminal cannot do 24-bit colour | use a truecolor terminal (iTerm2, WezTerm, Ghostty, Kitty, Alacritty, Windows Terminal) or set `COLORTERM=truecolor`. The app runs, flattened. |
30
30
  | `! Terminal colour: no colour` | `NO_COLOR` is set, or a monochrome terminal | unset `NO_COLOR` for the full look; dexterm works in monochrome. |
31
31
  | `! Claude CLI: not found` | `claude` is not on PATH | only the analyst needs it: install Claude Code and run `claude` once to log in. Everything else works. |
32
+ | `! Codex CLI: … not logged in` | the optional Codex engine is installed but logged out | `codex login`, or keep the analyst on Claude. |
33
+ | `! Codex CLI: … older than 0.163.0` | the read-only lock needs a newer codex | `npm i -g @openai/codex@latest`. |
32
34
  | `! Helius RPC: no key` | no key configured | optional. Holders and some safety fields show `needs key`; see configuration (https://dexterm.xyz/docs/configuration#the-optional-free-rpc-key). |
33
35
  | `✗ Data sources: unreachable …` | a required host did not answer | check your connection, VPN or firewall. If only some hosts fail they may be blocked on your network. `--fixtures` or `DEXTERM_OFFLINE=1` browse offline demo data. |
34
36
  | `! Data sources: … PumpPortal …` | only the new-pairs host is down | the rest works; new pairs may be blocked on your network. |
@@ -1,12 +1,14 @@
1
1
  // @dexterm/mcp — read-only MCP server over stdio (SPEC §5, D-09 / task-1302).
2
2
  // Every tool is read-only and schema-described; nothing in this package may build,
3
3
  // sign or send a transaction (SPEC §5.2, §6.3).
4
- export { runStdioServer, selfTest, McpTools, TOOLS, READ_ONLY_TOOLS, appHelpAnswer, firewall, listPrompts, getPrompt, resourceList, resourceRead, overlayCounts, userSkillPreamble, BUNDLED_KB_DIR, BUNDLED_SKILLS_DIR } from "./server.js";
4
+ export { runStdioServer, selfTest, McpTools, TOOLS, READ_ONLY_TOOLS, PAPER_WRITE_TOOLS, PAPER_READ_TOOLS, appHelpAnswer, firewall, listPrompts, getPrompt, resourceList, resourceRead, overlayCounts, userSkillPreamble, BUNDLED_KB_DIR, BUNDLED_SKILLS_DIR } from "./server.js";
5
+ // task-1443: resolve the paper agent desk from the running app's rendezvous (null when agent paper trading is off).
6
+ export { resolvePaperAgentDesk, paperAgentDeskDb } from "./paper-agent.js";
5
7
  export { OverlayStore, mergeOverlay, loadBundled, checkKb, checkSkill, moneyToolMentions, isMoneyToolName, parseFrontmatter, dextermHome, userKbDir, userSkillsDir, userDir, contentJournalPath, journalContent, writeOverlayFile, resetOverlayFile, templateFor, KB_LIMITS, SKILL_LIMITS, KB_ID, SKILL_NAME, KB_RULE, MONEY_TOOLS, } from "./user-content.js";
6
8
  export { buildDossier } from "./dossier.js";
7
9
  // task-1406 + task-1421: the spectator sink the tool-call path writes to when a spectator surface is open
8
10
  // (via DEXTERM_SPECTATOR_FILE, or the ~/.dexterm rendezvous an external `dexterm mcp` client uses).
9
- export { recordForToolCall, maxAgeSec, fileSpectatorSinkFromEnv, resolveSpectatorSink, rendezvousPath, MCP_SPECTATOR_RENDEZVOUS, } from "./spectator-sink.js";
11
+ export { recordForToolCall, maxAgeSec, fileSpectatorSinkFromEnv, resolveSpectatorSink, rendezvousPath, MCP_SPECTATOR_RENDEZVOUS, MCP_FOLLOW_SCREEN, FOLLOW_NAV_ACTIONS, followScreenPath, readFollowScreen, writeFollowScreen, clearFollowScreen, } from "./spectator-sink.js";
10
12
  export { loadAppHelp, searchAppHelp, getAppHelp, APP_HELP_DIR, loadDocsKb, searchDocs, getDocs, searchHelp, DOCS_KB_DIR, helpIntent } from "./app-help.js";
11
13
  /** Tool names served in v1 (SPEC §5.2 core + the task-1293 KB/case tools + the task-1314 dossier). */
12
14
  export const PLANNED_TOOLS = [
@@ -20,6 +22,7 @@ export const PLANNED_TOOLS = [
20
22
  'get_candles',
21
23
  'rugcheck',
22
24
  'get_dev_history',
25
+ 'dev_history',
23
26
  'kb_lookup',
24
27
  'kb_search',
25
28
  'case_lookup',
@@ -0,0 +1,92 @@
1
+ // task-1443 — the MCP side of agent paper trading. When the running dexterm has the spectator view open and the
2
+ // user named an armed mandate for agent paper trading, it writes a rendezvous under ~/.dexterm; this resolves it
3
+ // into a PaperAgentDesk the paper tools dispatch to. When the rendezvous is absent the resolver returns null and
4
+ // the tools refuse — so agent paper trading is OFF by default and nothing here ever signs, sends or touches a
5
+ // wallet/key.
6
+ //
7
+ // The desk books into the SAME paper wallet SQLite file the app uses (WAL, concurrent-safe). Its caps are
8
+ // REPLAYED from the app's hash-chained plans journal (never read from the rendezvous), re-checked on every call
9
+ // (mtime-cached), so a revoke or panic in the app halts the agent. It prices the mandate's USD caps from a
10
+ // keyless SOL/USD read (fail-closed when older than 60 s), reads the control file on every mutating call
11
+ // (pause / take-over / stop), and journals every call to its own hash chain. The only trade op it can reach is
12
+ // the keyless swap-QUOTE + the PAPER executor (see paper-desk.ts).
13
+ import { PaperAgentDesk, PaperWallet, PaperExecutor, SharedJournal, systemClock, readPaperAgentRendezvous, readDeskControl, rendezvousMtimeMs, fileMtimeMs, loadMandateFromJournal, plansJournalPathFor, paperAgentJournalPath, withPriority, } from '../../data/src/index.js';
14
+ const WSOL = 'So11111111111111111111111111111111111111112';
15
+ /** A SOL/USD read older than this prices no cap (same freshness rule as the signer's resolveSolUsd). */
16
+ export const PAPER_SOL_USD_MAX_AGE_MS = 60_000;
17
+ const SOL_USD_REFRESH_MS = 20_000;
18
+ let cache = null;
19
+ let sol = null;
20
+ let mandateCache = null;
21
+ function teardown() {
22
+ if (cache) {
23
+ try {
24
+ cache.wallet.close();
25
+ }
26
+ catch { /* ignore */ }
27
+ cache = null;
28
+ }
29
+ mandateCache = null;
30
+ }
31
+ /** Replay the mandate from the plans journal, re-read whenever the journal changes (revoke / panic / expiry). */
32
+ function currentMandate(env, id) {
33
+ const path = plansJournalPathFor(env);
34
+ const mtimeMs = fileMtimeMs(path);
35
+ if (mandateCache && mandateCache.id === id && mandateCache.mtimeMs === mtimeMs)
36
+ return mandateCache.m;
37
+ const r = loadMandateFromJournal(path, id, systemClock);
38
+ mandateCache = { id, mtimeMs, m: r.mandate };
39
+ return r.mandate;
40
+ }
41
+ /** Keep the SOL/USD read fresh enough to price the USD caps; a failed read leaves the old one to age out. */
42
+ async function refreshSolUsd(data) {
43
+ const now = Date.now();
44
+ if (sol && now - sol.at < SOL_USD_REFRESH_MS)
45
+ return;
46
+ try {
47
+ const p = (await data.jupiter.price([WSOL])).get(WSOL)?.usdPrice;
48
+ if (typeof p === 'number' && Number.isFinite(p) && p > 0)
49
+ sol = { price: p, at: Date.now() };
50
+ }
51
+ catch { /* keep the last value; it ages out */ }
52
+ }
53
+ /**
54
+ * Resolve the paper agent desk for this `dexterm mcp` process, or null when agent paper trading is disabled.
55
+ * Re-reads the rendezvous (mtime-cached) so enabling/disabling in the running app is picked up; the mandate
56
+ * and the control state are re-read inside the desk on every call, not cached across journal changes.
57
+ */
58
+ export async function resolvePaperAgentDesk(data, env = process.env) {
59
+ const mtimeMs = rendezvousMtimeMs(env);
60
+ if (mtimeMs === 0) {
61
+ teardown();
62
+ return null;
63
+ } // not enabled
64
+ const rv = readPaperAgentRendezvous(env);
65
+ if (!rv) {
66
+ teardown();
67
+ return null;
68
+ } // present but unreadable → off (fail closed)
69
+ await refreshSolUsd(data);
70
+ if (cache && cache.mtimeMs === mtimeMs && cache.db === rv.db && cache.mandateId === rv.mandateId)
71
+ return cache.desk;
72
+ // The rendezvous changed (or first use): rebuild on the current wallet + mandate.
73
+ teardown();
74
+ const wallet = new PaperWallet({ path: rv.db, clock: systemClock });
75
+ const executor = new PaperExecutor(wallet, { clock: systemClock });
76
+ const desk = new PaperAgentDesk({
77
+ mandate: () => currentMandate(env, rv.mandateId),
78
+ wallet,
79
+ executor,
80
+ // 'high', like the human ticket (task-1387): a paper exit must not queue behind the agent's own read polling on
81
+ // the shared keyless `jupiter` bucket (task-1443 live run: a scale-out was refused on a 429 mid-poll).
82
+ quote: (req, signal) => withPriority('high', () => data.jupiterSwap.quote(req, signal)),
83
+ solUsd: () => (sol && Date.now() - sol.at <= PAPER_SOL_USD_MAX_AGE_MS ? sol.price : null),
84
+ control: () => readDeskControl(env),
85
+ journal: new SharedJournal(paperAgentJournalPath(env), systemClock),
86
+ agent: rv.agent ?? 'mcp-agent',
87
+ });
88
+ cache = { mtimeMs, db: rv.db, mandateId: rv.mandateId, wallet, desk };
89
+ return desk;
90
+ }
91
+ /** Exported for tests: the current cached rendezvous db, if any. */
92
+ export function paperAgentDeskDb() { return cache?.db ?? null; }