@connortessaro/pai 0.4.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Connor Tessaro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,308 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/logo-dark.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset=".github/assets/logo-light.svg">
5
+ <img alt="Phantom Logo" src=".github/assets/logo-dark.svg" width="56" height="62">
6
+ </picture>
7
+ </p>
8
+
9
+ <h1 align="center">pai</h1>
10
+
11
+ <p align="center">
12
+ <strong>Keys, money, and subagent controls for AI agents</strong><br>
13
+ Built by <a href="https://phantom.codes">Phantom AI</a>
14
+ </p>
15
+
16
+ <p align="center">
17
+ <a href="https://github.com/connortessaro/pai/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/connortessaro/pai/ci.yml?branch=main&label=CI&color=161b22&labelColor=0d1117" alt="CI"></a>&nbsp;
18
+ <a href="package.json"><img src="https://img.shields.io/badge/version-0.3.0-161b22?labelColor=0d1117" alt="Version 0.3.0"></a>&nbsp;
19
+ <a href="package.json"><img src="https://img.shields.io/badge/node-%3E%3D24-161b22?labelColor=0d1117" alt="Node >= 24"></a>&nbsp;
20
+ <a href="docs/reference.md#mcp-tools"><img src="https://img.shields.io/badge/MCP-16_tools-161b22?labelColor=0d1117" alt="MCP Compatible"></a>&nbsp;
21
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-161b22?labelColor=0d1117" alt="MIT License"></a>
22
+ </p>
23
+
24
+ <p align="center">
25
+ <a href="#install">Install</a> •
26
+ <a href="#quickstart">Quickstart</a> •
27
+ <a href="#how-it-works">How It Works</a> •
28
+ <a href="#features">Features</a> •
29
+ <a href="#commands">Commands</a> •
30
+ <a href="#use-with-an-mcp-client">MCP Server</a> •
31
+ <a href="docs/reference.md">API Reference</a>
32
+ </p>
33
+
34
+ ---
35
+
36
+ `pai` runs on your machine and manages a [Phantom AI](https://phantom.codes) key.
37
+ Your agent can hand each subagent a child key with its own spending limit, lifetime, and rate cap.
38
+ It also sets plans and model routing, pays for credit from an agent wallet, and checks which model answered a call.
39
+
40
+ `pi`, `Claude Code`, `Codex`, and `Cursor` drive it through a skill, and any MCP client can use `pai mcp`.
41
+
42
+ The CLI is MIT licensed. The Phantom AI API it calls is a hosted service.
43
+
44
+ ## How It Works
45
+
46
+ ```text
47
+ ┌────────────────────────┐
48
+ │ Main Phantom Key │
49
+ │ (prepaid, no id) │
50
+ └───────────┬────────────┘
51
+ │
52
+ ┌───────────────────────┼───────────────────────┐
53
+ │ pai child │ pai child │ pai wallet
54
+ ▼ ▼ ▼
55
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
56
+ │ Researcher │ │ Code Agent │ │ Agent Wallet │
57
+ │ Claude Code │ │ Cursor │ │ (Solana) │
58
+ │ $0.50 limit │ │ $1.00 limit │ │ auto-topup │
59
+ │ 6 hour TTL │ │ $0.10 / min │ │ below $1.00 │
60
+ └──────┬───────┘ └──────┬───────┘ └──────────────┘
61
+ │ │
62
+ └───────────┬───────────┘
63
+ ▼
64
+ ┌───────────────────────┐
65
+ │ Phantom AI Gateway │ ──> https://phantom.codes/v1
66
+ │ (zero prompt logs) │ ──> Ed25519 signed receipts
67
+ └───────────────────────┘
68
+ ```
69
+
70
+ ## Features
71
+
72
+ - **Scoped Subagent Keys** — Create child keys that spend the parent's balance up to a limit, with lifetimes and rate caps (`pai child`). Revoke one with `pai burn`.
73
+ - **Autonomous Agent Wallet** — Local Solana keypairs allow running agents to self-fund inference on chain. Capped by environment-only limits (`PHANTOM_WALLET_MAX_USD`).
74
+ - **Multi-Agent Skill & MCP** — Native skill installation for `pi`, `Claude Code`, `Codex`, and `Cursor`, plus a 16-tool Model Context Protocol (MCP) server over stdio.
75
+ - **Cryptographic Receipts** — Upstream responses return an Ed25519 signature verifying the model served, actual token counts, and cost (`pai verify`).
76
+ - **Local Tooling Suite** — Private local markdown memory notebooks, throwaway Docker sandboxes, and safe IMAP/SMTP mail drafting.
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ npm i -g @connortessaro/pai
82
+ # or, without npm's registry:
83
+ npx github:connortessaro/pai --help
84
+ ```
85
+
86
+ The package also installs the same CLI as `phantom-key`, its earlier name.
87
+
88
+ ## Quickstart
89
+
90
+ ```bash
91
+ # 1. Login once and install the skill for your installed agents
92
+ pai login # paste your key once; pai saves it with mode 600
93
+ pai setup # configures pi, Claude Code, Codex, and Cursor
94
+
95
+ # 2. Check balance and active caps
96
+ pai balance
97
+
98
+ # 3. Mint a child key for an autonomous subagent
99
+ pai child --limit 0.50 --ttl 6 --rate 0.10 --save researcher
100
+
101
+ # 4. Run the subagent as the scoped child
102
+ PHANTOM_KEY_NAME=researcher claude
103
+ ```
104
+
105
+ ## Use It From an Agent
106
+
107
+ `pai setup` installs the `phantom-ai` skill for each agent it finds in your
108
+ home directory (`pi`, `Claude Code`, `Codex`, `Cursor`). You can then prompt your agent in
109
+ plain words, for example:
110
+
111
+ > *"Give a subagent $0.50 for 6 hours with a $0.10/min rate cap."*
112
+
113
+ - `--agent pi` (or `claude`, `codex`, `cursor`) sets up one agent.
114
+ - `--mcp` also registers the MCP server with Claude Code, Codex, and Cursor.
115
+ - `--agent claude --provider` runs Claude Code's own model calls on Phantom AI: it sets `ANTHROPIC_BASE_URL`, turns on tool search, and gives Claude Code the key through `apiKeyHelper` (`pai key show`), so the key never lands in `settings.json`. Add `--model auto` or any model id; `--provider off` undoes it.
116
+ - `pi` has no MCP and uses the CLI through the skill; install there with:
117
+ ```bash
118
+ pi install npm:@connortessaro/pai
119
+ ```
120
+
121
+ ## Commands
122
+
123
+ `pai --help` prints every command and flag. [docs/reference.md](docs/reference.md)
124
+ has the complete text, the MCP tools, and the environment variables, all generated
125
+ from the code.
126
+
127
+ | Group | Commands | What they do |
128
+ | --- | --- | --- |
129
+ | **Setup** | `login`, `logout`, `setup` | Save your key, and install the skill for your agents |
130
+ | **Keys** | `balance`, `child`, `children`, `key`, `rotate`, `burn` | Show credit, create child keys paid from this key, save keys by name, and replace or revoke a key |
131
+ | **Caps and plans** | `budget`, `plan` | Set a monthly cap and a per-minute cap, or money for a period |
132
+ | **Routing** | `route` | Choose which models `model: "auto"` can run, and the rules that pick one |
133
+ | **Credit** | `buy`, `payment` | Buy credit with crypto |
134
+ | **Agent wallet** | `wallet`, `autotopup`, `buy --pay` | Keep Solana wallets and pay for credit from them. See [Let the agent pay](#let-the-agent-pay) |
135
+ | **Receipts** | `verify` | Check which model answered a call, from its signed receipt |
136
+ | **Local tools** | `memory`, `mail`, `browser`, `sandbox` | See the sections below |
137
+ | **MCP** | `mcp` | Run an MCP server over stdio. See [Use with an MCP client](#use-with-an-mcp-client) |
138
+
139
+ A command runs as the saved key named by `PHANTOM_KEY_NAME` if it is set, then
140
+ as `PHANTOM_API_KEY`, then as the `login` key. A subagent started with
141
+ `PHANTOM_KEY_NAME=<child>` runs as the child even when your parent key is exported.
142
+
143
+ ## Memory
144
+
145
+ Each agent gets a private notebook on this machine. Notes are markdown files in
146
+ `~/.config/phantom-key/memory/<space>/`, so you can read, edit, or commit them.
147
+ `pai` sends none of them anywhere.
148
+
149
+ | Command | What it does |
150
+ | --- | --- |
151
+ | `memory add "text" [--tag a,b] [--title t]` | Keeps a note. Pipe long notes on stdin |
152
+ | `memory search <words> [--tag t] [--any] [--limit n]` | Notes with every word (or any word with `--any`), best match first. Returns 10 by default |
153
+ | `memory list [--tag t] [--limit n]` / `memory show <id>` / `memory rm <id>` | The newest notes (50 by default), one note, or delete one |
154
+ | `memory spaces` | Each notebook and how many notes it holds |
155
+
156
+ `pai` picks the notebook from `--space`, then `PAI_MEMORY_SPACE`, then the saved
157
+ key name (`PHANTOM_KEY_NAME`), then `main`, so each subagent keeps its own.
158
+
159
+ ## Mail
160
+
161
+ `pai` reads your own mailbox over IMAP and SMTP. It saves the login, with an app
162
+ password, in `~/.config/phantom-key/mail.json` (mode 600). Gmail, Outlook,
163
+ iCloud, and Fastmail addresses have presets; for any other provider, pass
164
+ `--imap host:port --smtp host:port`.
165
+
166
+ | Command | What it does |
167
+ | --- | --- |
168
+ | `mail setup --user you@gmail.com` | Checks the login by listing one message, then saves it. A wrong password is not saved. The password comes from a prompt or `PAI_MAIL_PASSWORD` |
169
+ | `mail status` | Shows the mailbox and whether sending is on |
170
+ | `mail list [--unread] [--from x] [--limit n] [--folder f]` / `mail search <words>` / `mail read <uid>` | Reads mail from `INBOX` by default. `list` returns 20 messages by default. `read` cuts a message at 20,000 characters |
171
+ | `mail draft --to a --subject s [--reply <uid>] [--body "..."]` | Saves a draft in your Drafts folder and sends nothing. Without `--body`, pai reads the body from stdin |
172
+ | `mail send` (same flags) | Sends only when `PAI_MAIL_SEND=1`, to at most `PAI_MAIL_MAX_PER_DAY` recipients (default 10) in 24 hours, and only to `PAI_MAIL_SEND_TO` (addresses or `@domain`s) when set. pai reads all three from the environment, so an agent can't turn sending on with a flag. With sending on, the `create_child_key` tool requires `save_as`, so a new key never lands in the agent's context. SMTP on 587 and IMAP on 143 require STARTTLS |
173
+
174
+ ## Browser and Sandbox
175
+
176
+ `pai` wraps two existing tools:
177
+
178
+ | Command | What it does |
179
+ | --- | --- |
180
+ | `browser setup [--install]` | Checks for [agent-browser](https://github.com/vercel-labs/agent-browser), a headless Chrome built for agents. `--install` installs it with npm |
181
+ | `browser <command>` | Runs agent-browser (`open <url>`, `snapshot -i`, `click @e1`, `fill @e2 "text"`, `screenshot`). Each space gets its own session and Chrome profile in `~/.config/phantom-key/browser/<space>/`, so a subagent keeps its own logins. `AGENT_BROWSER_PROFILE=Default` uses your own Chrome profile instead |
182
+ | `sandbox check` | Reports whether Docker or Podman is running |
183
+ | `sandbox run [--image i] [--net] [--write] [--timeout s] -- <command>` | Runs a command in a throwaway container (default image `node:24-slim`). The container has no network unless `--net`, and mounts this folder read-only unless `--write`. It runs as an unprivileged user with all capabilities dropped, 2 CPUs and 2 GB of memory. It stops after 300 seconds by default and exits 124 on timeout |
184
+
185
+ ## Output Format
186
+
187
+ Commands print JSON on stdout, so you can pipe into `jq`. `--table` prints
188
+ a formatted table for human inspection. `browser` and `sandbox run` pass through the exit code of the
189
+ command they ran.
190
+
191
+ | Exit code | Meaning |
192
+ | --- | --- |
193
+ | `0` | Success |
194
+ | `1` | Any other error |
195
+ | `2` | Key rejected (401/403). Get a new key; a retry fails the same way |
196
+
197
+ ## Use With an MCP Client
198
+
199
+ `pai mcp` starts an MCP server over stdio. Add it to your client's config:
200
+
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "phantom": {
205
+ "command": "npx",
206
+ "args": ["-y", "@connortessaro/pai", "mcp"]
207
+ }
208
+ }
209
+ }
210
+ ```
211
+
212
+ [docs/reference.md](docs/reference.md#mcp-tools) lists every tool with its
213
+ inputs and whether it is read-only or destructive.
214
+
215
+ `delete_key` authenticates as the key you pass in `api_key` or `key_name` and
216
+ deletes that key. It refuses the key the server runs as; use `pai burn` for
217
+ that.
218
+
219
+ ## Let the Agent Pay
220
+
221
+ The agent can maintain its own Solana wallets and buy credit with them. Each wallet
222
+ is a Solana keypair in `~/.config/phantom-key/wallets/` (mode 600). The wallet
223
+ pays Phantom AI's Solana address, and Phantom AI finds the payment on chain.
224
+
225
+ 1. **Create a wallet**, and back up its file:
226
+ ```bash
227
+ pai wallet create --table # saved as "main"
228
+ pai wallet create --name work --table # a second one
229
+ ```
230
+ 2. **Fund the address** with USDC on Solana, plus some SOL for fees (at least 0.003 SOL).
231
+ 3. **Set the safety cap**:
232
+ ```bash
233
+ export PHANTOM_WALLET_MAX_USD=10
234
+ ```
235
+ Without this variable, `pai` refuses to pay. The total over 24 hours is
236
+ capped by `PHANTOM_WALLET_MAX_USD_PER_DAY`, which defaults to the same
237
+ amount: one payment a day. Raise it for `autotopup`.
238
+ 4. **Pay**:
239
+ ```bash
240
+ pai buy --amount 5 --pay
241
+ ```
242
+ With more than one wallet saved, `pai` asks in a terminal which one pays. It
243
+ then reports each step:
244
+ 1. Getting a payment request from Phantom AI (amount, recipient, and unique reference)
245
+ 2. Sending USDC or SOL from your wallet
246
+ 3. Confirming on Solana
247
+ 4. Phantom AI finding the payment on chain by its reference
248
+ 5. Credit balance before and after
249
+
250
+ A payment request expires after 30 minutes.
251
+
252
+ You can also prompt your agent: *"Top up my Phantom AI key with $5 from my Phantom agent wallet."*
253
+ Over MCP it calls `list_wallets`, asks which wallet if not specified, then calls `pay_for_credit`.
254
+
255
+ To refill automatically when the balance runs low:
256
+
257
+ ```bash
258
+ pai autotopup --below 1 --amount 5 --wallet main --every 10
259
+ ```
260
+
261
+ ### Safeguards
262
+
263
+ - `pai` pays the recipient and exact amount the payment request names, with that request's reference attached.
264
+ - `pai` reads `PHANTOM_WALLET_MAX_USD` and `PHANTOM_WALLET_MAX_USD_PER_DAY` strictly from the environment; no flag or tool argument can raise them.
265
+ - Before signing, `pai` checks the payment request against what it asked for: the coin, the USDC mint, and an amount no more than requested (for SOL, within 10% of Coinbase's price). A wrong or hostile `PHANTOM_BASE_URL` cannot make it sign more.
266
+ - One payment runs at a time, across processes.
267
+ - `pai` verifies the wallet balance before requesting a payment.
268
+ - `pai` records payments in `~/.config/phantom-key` before sending, and keeps the record until Phantom AI reports the payment credited or dead. If a run crashes, times out or loses the connection, the next run waits for that payment instead of paying again.
269
+ - These limits bind an agent that reaches pai through MCP. An agent with its own shell can set environment variables and edit the state folder, so give such an agent a wallet holding only what you are willing for it to spend.
270
+ - Store wallet secrets in a local secrets manager (e.g. [KRU](https://github.com/omaekumiko2-create/kru)) and inject `PHANTOM_WALLET_KEY` at runtime.
271
+
272
+ ## Configuration
273
+
274
+ `pai` reads its settings from environment variables. Run `pai --help` or see
275
+ [docs/reference.md#environment-variables](docs/reference.md#environment-variables) for the full list,
276
+ including secrets, environment-only flags, and caps.
277
+
278
+ ## Requirements
279
+
280
+ - **Node 24** or newer
281
+ - Runtime dependencies: `@modelcontextprotocol/sdk` and `zod` for `mcp`, `@solana/kit` for wallet payments, and `imapflow`, `mailparser`, `nodemailer` for `mail`.
282
+
283
+ Read [phantom.codes/docs/concepts](https://phantom.codes/docs/concepts) for key lifecycle and subagent hierarchy details.
284
+
285
+ ## Development
286
+
287
+ ```bash
288
+ npm ci
289
+ npm test # vitest
290
+ npm run typecheck
291
+ npm run docs # regenerate docs/reference.md
292
+ node src/pai.mts --help # run from source
293
+ ```
294
+
295
+ ## Contributing
296
+
297
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, style, and how to add a
298
+ command or MCP tool. [CHANGELOG.md](CHANGELOG.md) lists changes by release.
299
+
300
+ ## Security
301
+
302
+ Report vulnerabilities privately through a
303
+ [GitHub security advisory](https://github.com/connortessaro/pai/security/advisories/new).
304
+ See [SECURITY.md](SECURITY.md).
305
+
306
+ ## License
307
+
308
+ [MIT](LICENSE)
package/bin.mjs ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The installed entry point.
4
+ *
5
+ * Two reasons this is plain JS rather than the .mts itself. Node refuses to
6
+ * strip types from anything under node_modules at all, so a published
7
+ * TypeScript source cannot run in a dependency; and npm links the bin as an
8
+ * extensionless `pai`, which Node would not recognise as TypeScript
9
+ * even if it would. So `npm run build` compiles the source to dist/ and this
10
+ * file, which never needs compiling, is what npm points at.
11
+ */
12
+ import { main } from './dist/pai.mjs';
13
+
14
+ await main();