@visa/cli 5.0.0-rc.415 → 5.0.0-rc.417

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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @visa/cli
2
2
 
3
- Visa CLI connects a computer to Visa as an **agent**: one durable agent whose
4
- installation key never leaves this machine, approved once by its owner in the
5
- Visa Console. Agents act through the MCP tools this package serves; owners act
6
- in the Console. The terminal only connects.
3
+ Visa CLI connects each app to Visa as its own **agent**. Its installation
4
+ key stays on this machine. The owner approves the agent once in the Visa
5
+ Console. Agents act through the MCP tools this package serves; owners act in
6
+ the Console. The terminal only connects.
7
7
 
8
8
  It speaks the Visa agent API, `/agent/v1`, and nothing else.
9
9
 
@@ -12,7 +12,7 @@ It speaks the Visa agent API, `/agent/v1`, and nothing else.
12
12
  macOS and Linux:
13
13
 
14
14
  ```bash
15
- curl -fsSL https://app.visacli.sh/cli | bash
15
+ bash -o pipefail -c 'curl -fsSL https://app.visacli.sh/cli | bash'
16
16
  ```
17
17
 
18
18
  Windows PowerShell:
@@ -23,6 +23,14 @@ iwr -useb https://app.visacli.sh/install.ps1 | iex
23
23
 
24
24
  Node.js 20+ is required.
25
25
 
26
+ Deployment installers save their server with `visa config target <url>`. Later
27
+ shells and MCP processes reuse that target, including after reinstalling the
28
+ CLI. `VISA_AUTH_URL` overrides it for one environment; an unconfigured RC keeps
29
+ its existing preview default. Official servers and local loopback work directly;
30
+ custom origins require `--allow-custom-server`. Saving a target does not sign in
31
+ or approve an agent. Restart the AI app after changing a saved target so its
32
+ running MCP process uses that server for both requests and owner links.
33
+
26
34
  Managed updates certify the installed MCP bundle with `node dist/mcp-server/index.js --self-check`. It emits one JSON build result without opening a transport or running startup work. Restart readiness compares that version and environment with the loaded server; commerce readiness stays false until separately certified.
27
35
 
28
36
  ## Connect
@@ -31,41 +39,67 @@ Managed updates certify the installed MCP bundle with `node dist/mcp-server/inde
31
39
  visa connect claude # or cursor, codex, zed, ... (any client id the CLI knows)
32
40
  ```
33
41
 
34
- `connect` mounts the Visa MCP server in the AI client, then sets this computer
35
- up as an agent:
42
+ `connect` mounts the Visa MCP server in the AI client, then sets up that app’s
43
+ agent:
36
44
 
37
45
  1. It makes an Ed25519 installation key in this machine's key store (macOS
38
46
  Keychain, Windows DPAPI, Linux Secret Service, or a 0600 file on a headless
39
47
  host with `VISA_RUNTIME_CUSTODY_PROFILE=headless`).
40
- 2. It starts a setup and opens the owner's approval page. The owner signs in
41
- to the Visa Console and approves. The agent connects with no spending limit:
42
- its wallet key can sign for the owner and can never spend until the owner
43
- sets a limit in the Console. The CLI never sees an owner key.
44
- 3. It claims the approved setup with the installation key. A finite background
45
- pickup continues after the terminal or MCP host exits, so approval does not
46
- need a second `connect` call. `status` reports pickup progress and fresh
47
- Authority truth; connecting does not mean payments or TAP are ready.
48
-
49
- Before presenting a saved approval link, connect and status read the setup's current server state, including while another process owns pickup. Ended or already approved setups expose no approval URL. Status never claims a setup; unknown server state stays recoverable.
50
-
51
- Pickup stays bound to this setup and key. Its bound covers the setup's initial
52
- expiry plus Authority's one-hour approved completion window (including the
53
- 24-hour emergency setup), with the previous 33-minute minimum and two minutes
54
- for startup and an in-flight safe point. It exits on completion, refusal,
55
- disconnect, supersession or its bound. It is not a daemon or restart service.
56
- OS sleep consumes the saved bound; on wake pickup continues only while time
57
- remains. A hard stop is not automatically restarted. A locked/unavailable key
58
- store refuses without a fallback or extra prompt. The original setup remains available
59
- for recovery on the next invocation. A missing pickup bundle or failed launch
60
- is reported as interrupted, rather than promising automatic completion.
61
-
62
- Every step resumes. Pickup continues after the terminal closes; if it is interrupted,
63
- run `visa connect` again to recover the same setup.
48
+ 2. It starts a setup, sends this computer's name, and opens the owner's
49
+ approval page. The owner signs in to the Visa Console, sees which app and
50
+ computer is asking, and presses **Approve**. Approve asks nothing else: the
51
+ agent starts on **Ask me each time**, and the sheet shows Visa's backstop
52
+ under Details. The CLI never sees an owner key.
53
+ 3. The owner's Approve makes this computer's key live. `visa connect` waits in
54
+ the foreground and links the approved setup; if it is closed first, the next
55
+ `visa connect` or MCP tool call links it. Nothing runs in the background.
56
+ After linking, connect names the Visa account that approved it ("Approved
57
+ by … Not you? Run visa disconnect."). `status` reports fresh Authority
58
+ truth; connecting does not mean payments or TAP are ready.
59
+
60
+ Connect ends on what the agent can do:
61
+
62
+ ```text
63
+ Connected as Claude Code.
64
+ Can spend up to $100 more, asking you each time.
65
+ ```
66
+
67
+ or, for an agent connected to earn only, `Can earn.` A remaining spending step (such as adding funds) follows the limit. Status exposes the current connection and spending step in `connection`.
68
+
69
+ Every connect result, and an unfinished setup in status, carries the one connect `status`: a word (`Connected`, `Finishing…`, `Needs you`, `Didn’t finish` or `Visa needs to look`), at most one owner action (`Continue`, `Remove`, `Retry` or `Contact support`) with its link, and a stable `agent` code. The terminal, MCP and Console say the same word.
70
+
71
+ Limits set at connect start on **Ask me each time**. The owner can change them later in the Console. App connections ask before each payment.
72
+
73
+ Before presenting a saved approval link, connect and status read the setup's current server state. Ended or already approved setups expose no approval URL. Status never claims a setup; unknown server state stays recoverable. A locked or unavailable key store refuses without a fallback or extra prompt, and the original setup stays available on the next invocation.
74
+
75
+ Reconnecting the same app keeps its agent. Connecting a different app starts
76
+ a separate setup, with its own name, mark, key and limits. Older connections
77
+ first recover their app from authenticated status. If that identity is unavailable,
78
+ retry connect later or select the existing agent explicitly; no new setup is started.
79
+ If Visa confirms that no app was recorded, connect asks you to select the existing
80
+ agent explicitly instead of retrying or inferring its app from the caller.
81
+ MCP tools default to the calling app’s agent when exactly one matches. An explicit
82
+ agent selector overrides that default. Without a matching app, the usual agent
83
+ selection applies.
84
+
85
+ This computer remembers the wallet first returned by connect or status. Signing
86
+ refuses a different wallet, owner or organisation; payments also check the saved
87
+ wallet account. Agents connected before this update save it on their next
88
+ successful connect or status read. Until then they keep the previous behaviour.
89
+ Replacing the same agent on this computer keeps its saved wallet identity.
90
+
91
+ Every step resumes. If the terminal closes before the owner approves, run
92
+ `visa connect` again to recover the same setup.
64
93
  `--wait 0` starts the setup and returns; `--no-open` prints the link instead.
94
+ If Visa does not answer, `visa connect` says so at once and exits 1: run it
95
+ again later. A wait for the owner slows down through an outage and keeps the
96
+ same setup.
65
97
 
66
98
  - **A new key for the same agent** (same limits): `visa connect --replace`.
67
99
  - **A setup the owner started in the Console** (including replacing a lost
68
- laptop): `visa connect --setup <key>`.
100
+ laptop): `visa connect --setup <key>`. Where that app is already connected
101
+ on this computer, this reconnects it and adds nothing; add `--another` to
102
+ add a second agent of that app here, or to move another agent onto it.
69
103
  - **After the owner revokes this computer's agent**: `visa connect` sets up a
70
104
  new agent here. Visa's own "removed" answer retires the old agent on this
71
105
  computer first (its key stays only while an unsettled purchase may need it).
@@ -76,9 +110,18 @@ run `visa connect` again to recover the same setup.
76
110
  Active purchase calls retain their installation credential even before the
77
111
  first journal exists. `--force` can forget unsettled purchases after active
78
112
  calls finish; disconnect cannot erase a key while a call can still admit or sign.
79
- `--all` also cancels unfinished local setups and automatic pickup. If an
113
+ `--all` also cancels unfinished local setups. If an
80
114
  unsettled purchase keeps a connected key, its pending replacement is still cancelled.
81
115
  - **Update**: `visa update` (RC stays on `@visa/cli@rc`).
116
+ When Visa names a newer release for this channel, `status` answers `update`
117
+ (at most once a day per computer): the agent runs `visa update`, then the owner
118
+ restarts the app; with `visa-tool` the next call already runs it.
119
+ Updates check a separate release before switching all entry points; interrupted
120
+ downloads keep the previous release usable. Activation rechecks the current release,
121
+ so an overlapping update keeps the active CLI, shell tools and MCP available.
122
+ Releases that cannot start through the installed MCP launcher are refused
123
+ before activation, including incompatible older releases selected by the channel.
124
+ Supervisor-managed runtimes continue to use `visa-runtime-converge`.
82
125
 
83
126
  | Command | What it does |
84
127
  | -------------------------- | ------------------------------------------------------------------------------------- |
@@ -98,7 +141,7 @@ visa-tool --skill # the Visa skill: recovery after a rebuilt VM, and the rule
98
141
  visa-tool --list # the server's rules and the tools with their inputs (JSON)
99
142
  visa-tool status # one call: the tool's answer as JSON, exit 1 on a refusal
100
143
  visa-tool discover '{"target":"https://…"}' # the live price, read only: nothing is paid
101
- visa-tool pay '{"target":"https://…","max":"0.05","request_id":"<new UUID>"}' # pays at most max
144
+ visa-tool pay '{"target":"https://…","max":"0.05","idempotency_key":"<new UUID>"}' # pays at most max
102
145
  ```
103
146
 
104
147
  On a computer with no keyring, every command needs
@@ -110,6 +153,10 @@ higher `skill_version`, the app asks its owner for the new text. Muse will not
110
153
  save a program's output as a skill, so its owner pastes the same text from the
111
154
  Console's Muse message.
112
155
 
156
+ A delivered answer stays available from `payment_status` during a temporary read outage. Settlement stays unconfirmed until Visa can read the original purchase again.
157
+
158
+ An outage answer carries `retry_after_seconds` when one was named; wait that long, then repeat the call. A local file that can no longer be read is refused with the step that repairs it.
159
+
113
160
  ## MCP tools
114
161
 
115
162
  Concurrent calls stay bound to their selected agent for every payment phase;
@@ -122,21 +169,70 @@ stable launcher) serves the laptop column of the one Visa tool catalogue:
122
169
  | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
123
170
  | `status` | Who this agent is, what is ready, and who takes the next step. Call first. |
124
171
  | `connect` | Connect this computer (or replace its key). Returns the owner link; picks up approval automatically. |
125
- | `discover` | Search paid services or probe one URL. Answers `unavailable` until the discovery source is live. |
126
- | `pay` | Pay a URL that answers HTTP 402 with a Tempo charge (OUSD). Needs `max` and a new `request_id` per purchase. **Moves real money.** |
127
- | `payment_status` | Read one purchase by its `request_id`, optionally waiting for it to change. |
128
- | `payment_cancel` | Cancel a purchase that has not executed. |
172
+ | `discover` | Search paid services or probe one URL with GET and no body. Answers `unavailable` until the discovery source is live. |
173
+ | `pay` | Pay a URL that answers HTTP 402 with a Tempo charge (OUSD). Needs `max` including the network fee and a new `idempotency_key` per purchase. Optional `userRequest`: the person's words, kept by Visa for product analysis. **Moves real money.** |
174
+ | `task_post` | Post a claim task with a reward in USD and an `idempotency_key`; uses the existing payment limits and approval. |
175
+ | `task_action` | Claim, submit, accept or release a task. Claim and submit show the owner the task and reward; submission also binds the work fingerprint and shows a work preview. Approve once, then repeat the same call; submitting requires a merchant receipt for the reviewed work. Submit and accept pay and need `max`. Every action first needs Visa's clearance of that task. **Moves real money.** |
176
+ | `task_list` | List open screened tasks (or one named task with `task_id`) and short-lived clearances for task actions. Never pays. |
177
+ | `task_status` | Read a task’s status, reward and available claimant or submission. Never pays. |
178
+ | `payment_status` | Read one purchase by its `idempotency_key`: a plain read. To wait, repeat the call that started it (it holds on your turn). |
179
+ | `payment_cancel` | Cancel a purchase awaiting owner approval. |
129
180
  | `history` | This agent's purchases, newest first. |
130
181
 
182
+ `task_action` is optional; use only tools advertised by this connection.
183
+
184
+ Each tool advertises an extensible object `outputSchema`. Local and hosted MCP
185
+ return that object in `structuredContent`, with a two-line summary first in
186
+ `content` and the same object as one line of JSON in a second text block. Text-only
187
+ clients should read the JSON block; display clients should show the summary.
188
+ A purchase answer carries `idempotency_key` and a compact `payment` (no
189
+ `attempts[]`); `payment_status` with `full: true` returns the whole purchase.
190
+ Each deadline has a relative twin (`owner.in_seconds`, `deadline_in_seconds`).
191
+ For a purchase, the summary is the answer's `message` on two lines (the result,
192
+ then what to do), the same on every surface; it never repeats server or merchant
193
+ text. Other payload fields and refusal references remain available in the object.
194
+ The shell adapter, `visa-tool`, prints the object as JSON and appends a
195
+ `Reference: <id>` line on refusals; it does not print the MCP display summary.
196
+
197
+ When a `discover` query finds no services, its `next` hint offers `task_post` with
198
+ the query as the suggested description. Posting requires an explicit `reward`
199
+ (USD decimal), `idempotency_key`, optional `duration_seconds` (default 3600) and an
200
+ optional documented `terms_preset` (`standard`, `fast` or `flexible`; a staked task
201
+ names one). Unknown fields are refused. Local rules give fast feedback; the edge must allow the exact draft before paying.
202
+ Tasks needing review are not posted. The CLI obtains fresh clearance before each
203
+ task action; callers do not supply it to `task_action`. Submit needs `text` and
204
+ `max`; accept needs `max`. A missing or invalid `max` is corrected on the same task action with the same `idempotency_key`. Reuse the same arguments and idempotency key to resume; a task response
205
+ does not prove payment settlement. Listed tasks show `net_reward` and any stake;
206
+ a task that pays the worker nothing is never listed. A task outage or malformed task answer is named in
207
+ `task_screening`, while successful service results remain available. `task_status` reads `/api/tasks/:id` without paying.
208
+ Taskmarket defaults to its testnet on Tempo Moderato and to `api.taskmarket.dev`
209
+ on mainnet; `VISA_TASKMARKET_URL=off` turns tasks off. The live testnet currently refuses access-key signatures;
210
+ the harness models requested support separately.
211
+
212
+ `task_post` accepts `require_verified: "tap:identity:verified"` (verified workers),
213
+ `"tap:identity:known"` (known or verified), or `"none"`. This is Tempo-only.
214
+ Listings and status expose `verified_by` and `tap_verifier`; screening binds the
215
+ requirement. Testnet creation returns `tap_simulated`: its directory proves only
216
+ possession of a key enrolled with Taskmarket. Mainnet worker actions refuse with
217
+ `verifier_unavailable` while Taskmarket's verifier is disconnected.
218
+ Hosted requesters can post required tasks; hosted workers cannot claim or submit
219
+ them. Device workers use the protected identity signer for claim, upload request
220
+ and submission, retaining separate wallet approval and payment controls. The key
221
+ must resolve with matching public bytes and sufficient trust in Taskmarket's
222
+ selected directory. Visa directory publication alone does not enroll a key in
223
+ Taskmarket's simulated directory. Automatic simulated enrollment and task updates
224
+ are not offered by these tools. No private key is exported to Taskmarket's CLI.
225
+
131
226
  How `pay` works: the CLI builds the exact Tempo charge from the merchant's 402,
132
227
  asks Visa to admit it with this installation's proof, and, when the wallet
133
228
  provider needs this laptop's own approval, reads the signing activity from the
134
229
  provider itself, checks it against its own charge and votes. The paid request
135
230
  carries the agent's TAP signature; with no TAP key the CLI refuses before
136
- anything is signed. Reusing a `request_id` never pays twice: Visa's record
231
+ anything is signed. Each paying merchant request and its paid retry send `Idempotency-Key` with the same
232
+ `idempotency_key`. Unpaid price previews send no idempotency header. Reusing an `idempotency_key` never pays twice: Visa's record
137
233
  answers. When Visa verifies that the old execution was unpaid and explicitly
138
234
  returns the agent's `resume_payment` continuation, `payment_status` directs this
139
- computer to call `pay` with the same arguments and `request_id`. It prepares a
235
+ computer to call `pay` with the same arguments and `idempotency_key`. It prepares a
140
236
  fresh unsigned quote and execution for the original purchase; Visa rechecks
141
237
  current authority and limits. Admission is recorded before its HTTP request;
142
238
  a missing server row during the original 120-second observation window does
@@ -150,22 +246,43 @@ An authorized purchase without a signing continuation remains unknown and read-o
150
246
 
151
247
  When Visa reconciles a merchant-wallet hold, the settled purchase remains paid and cannot be retried. `pay` preserves Visa's guidance, and `payment_status` exposes its existing owner support continuation. Recovery depends on the merchant's settings; this continuation does not authorize a claim. A missing receipt, unavailable answer or client delivery report does not establish a merchant hold.
152
248
 
153
- Only Visa’s canonical settled state is reported as paid; a merchant answer does not prove settlement. An unknown outcome is never reported as "nothing was paid". Free merchant answers are returned as a preview up to 1 MiB without payment or a kept file; the cap ends the read without waiting for EOF. If a paid answer file or its final journal cannot be saved, the original call still returns its bounded preview and canonical payment result; retries never pay again. A preview with no file does not promise local retention. If a payment read fails through a network interruption or a body interruption after a successful HTTP response, or returns retryable `503 unavailable`, a verified delivered answer remains available locally with settlement unknown; prepared, sending and incomplete receiving attempts still require the original read to succeed. Explicit HTTP refusals and complete malformed Visa responses remain errors. Kept paid answers are checked against the digest recorded as they arrived. A missing, changed, or previously undigested answer is unavailable; its purchase history remains and the paid request is never resent.
154
-
155
- Refusals are JSON with a `code` from the Visa error families (for example
156
- `setup_required`, `limit_reached`, `not_enabled`), a `message` and a `fix`.
157
- When the merchant's charge exceeds `pay.max`, the local `limit_reached` refusal
249
+ When a Tempo payment needs the owner's approval, `pay` answers at once with the
250
+ approval link. The agent shows it once, then calls `pay` again with the same
251
+ `idempotency_key` and arguments: that call holds on the purchase's turn (Visa's
252
+ hold, 25 s, or the whole decision window when the MCP client takes progress)
253
+ and, once the owner approves, resumes and pays in the same call, under the
254
+ same purchase lock, so a second call never pays twice. A decline or expiry
255
+ inside the hold answers at once and signs nothing. Nothing runs in the
256
+ background and nothing of the request is written to disk: the agent's own
257
+ call holds the arguments. A Taskmarket signature the owner approves is
258
+ finished the same way, by the repeated `sign_action` or `task_action` call.
259
+
260
+ A native payment awaiting approval resumes with `pay`, the same `idempotency_key` and arguments after the owner approves.
261
+
262
+ Only Visa’s canonical settled state is reported as paid; a merchant answer does not prove settlement. A finishing payment (on its way, or being checked) is never reported as "nothing was paid". Free merchant answers are returned as a preview up to 1 MiB without payment or a kept file; the cap ends the read without waiting for EOF. If a paid answer file or its final journal cannot be saved, the original call still returns its bounded preview and canonical payment result; retries never pay again. A preview with no file does not promise local retention. If a payment read fails through a network interruption or a body interruption after a successful HTTP response, or returns retryable `503 unavailable`, a verified delivered answer remains available locally with settlement unknown; prepared, sending and incomplete receiving attempts still require the original read to succeed. Explicit HTTP refusals and complete malformed Visa responses remain errors. Kept paid answers are checked against the digest recorded as they arrived. A missing, changed, or previously undigested answer is unavailable; its purchase history remains and the paid request is never resent.
263
+
264
+ Every answer carries `turn` (`owner`, `agent`, `visa`, `done`) and `next`
265
+ (`none`, `call_again`, `new_request`, `stop`, `connect`, `update_cli`). Do
266
+ what `next` says. When `turn` is `owner`, first give the owner `owner.say`,
267
+ once. A refusal adds `code` (branch on it) and `detail` (diagnostic). `code` is one of the Visa error families (for example
268
+ `setup_required`, `limit_reached`, `not_enabled`).
269
+ For an OUSD charge, allow up to $0.01 above the quoted price for the network fee; when the quote names a funding ceiling, the `max` covers that too.
270
+ When the charge plus network fee ceiling exceeds `pay.max`, the local `limit_reached` refusal
158
271
  names the price above max; budget and cap refusals keep their own diagnostics.
159
272
  A saved merchant challenge that differs from its prepared payment is refused
160
273
  with a charge-mismatch diagnostic before signing or sending the paid request.
161
274
 
275
+ The known Muse shell connection leaves other apps’ MCP settings and status lines untouched.
276
+
277
+ Live status refreshes a renamed agent’s name on this computer. Owner steps for limits and paused apps link to that app in the Console. Unavailable tools direct the agent to the available tools; the owner handles withdrawals and conversions in Wallet.
278
+
162
279
  ## Status line
163
280
 
164
281
  `connect` installs a Claude Code status line (`visa-cli config statusline`).
165
282
  It draws from a cache a background read refreshes at most every 30 seconds:
166
283
 
167
284
  ```
168
- ◆ VISA │ claude 2 ● ready │ wallet $3.20 left of $50/day ▰▰▰▰▰▰ auto up to $5 │ last $0.25 fal.ai 4m │ staging
285
+ ◆ VISA │ claude 2 ● ready │ wallet $3.20 left of $50/day ▰▰▰▰▰▰ auto up to $5 │ last $0.25 fal.ai 4m
169
286
  Visa-mono │ Opus 5.5 │ context 34% ▰▰▱▱▱▱ │ 5h limit 24%
170
287
  ```
171
288
 
@@ -176,6 +293,7 @@ Visa-mono │ Opus 5.5 │ context 34% ▰▰▱▱▱▱ │ 5h limit 24%
176
293
  how close it is, and whether the agent pays automatically or asks each time.
177
294
  - **Freshness:** facts older than ten minutes say `as of`, and a failed read
178
295
  says `offline` with their age; a failed read never shows `ready` or `$0` as if live.
296
+ - **A server other than Visa's own** is named last (`staging`, `local` or its host).
179
297
  - **This Claude session** (second line): folder, model, context used and the
180
298
  5-hour window.
181
299
 
@@ -190,8 +308,9 @@ and honours `NO_COLOR`.
190
308
  | `VISA_CLI_HOME` | Where this CLI keeps its state (default `~/.visa-cli`). |
191
309
  | `VISA_RUNTIME_CUSTODY_PROFILE` | `os` (default) or `headless` (a 0600 file key store). |
192
310
  | `VISA_SUPPRESS_BROWSER=1` | Print links instead of opening a browser. |
311
+ | `VISA_TASKMARKET_URL` | Task service base URL; defaults to the Taskmarket testnet on Moderato and `https://api.taskmarket.dev` on mainnet. `off` disables tasks. |
193
312
  | `VISA_TEMPO_RPC_URL` | A Tempo RPC for building charges (https, or this machine). |
194
- | `VISA_RC_CODE` | The access code a release-candidate build asks for. |
313
+ | `VISA_RC_CODE` | The access code a release-candidate build asks for. A build from this repository (`pnpm --filter @visa/cli build`, or `pnpm turbo run build --filter=@visa/cli...`) made with it set accepts that code. |
195
314
  | `VISA_CLI_HUD_THEME` | `light` or `dark` status line colours, when the terminal does not say (`COLORFGBG`). |
196
315
 
197
316
  ## Data on this machine
@@ -201,27 +320,133 @@ and honours `NO_COLOR`.
201
320
  | OS key store, or `~/.visa-cli/agent-keys/` (headless) | The installation key. |
202
321
  | `~/.visa-cli/agent.json` | This computer's agent (ids, name) and, while a setup is open, its one-time setup secret (0600). |
203
322
  | `~/.visa-cli/hud-facts.json` | The status line's cache: the agent's last read state, never keys or tokens. |
204
- | `~/.visa-cli/purchases/` | Server- and agent-scoped records needed to finish after interruption, and merchant answers up to 50 MiB (0600 in a 0700 directory). Canonical settlement or a final unpaid outcome removes the prepared charge and unsigned transaction. Purchase records and already-kept answers are not automatically deleted, including on `visa disconnect --all`. Credential/security lifetimes and terminal preparation cleanup remain separate. |
323
+ | `~/.visa-cli/purchases/` | Server- and agent-scoped records needed to finish after interruption, and merchant answers up to 50 MiB (0600 in a 0700 directory). When local storage can be updated, canonical settlement or a final unpaid outcome removes the prepared charge and unsigned transaction. Purchase records and already-kept answers are not automatically deleted, including on `visa disconnect --all`. Credential/security lifetimes and terminal preparation cleanup remain separate. |
205
324
 
206
325
  ### Managed runtime convergence
207
326
 
208
327
  The package also installs `visa-runtime-converge` for supervisor-owned
209
- OpenClaw/Hermes deployments. See
210
- [`packages/visa-cli-openclaw/RUNTIMES.md`](https://github.com/Visa-Crypto-Labs/Visa-mono/blob/staging/packages/visa-cli-openclaw/RUNTIMES.md).
328
+ OpenClaw/Hermes deployments. See `RUNTIMES.md` in the
329
+ `@visa/visa-cli-openclaw` package.
211
330
 
212
- ### Cutover B hosted approval context
331
+ ### Hosted approval context
213
332
 
214
- The target hosted REST and MCP `pay` input accepts optional `original`, using the
215
- same canonical v1 terms and transient public replay request as payment prepare.
333
+ The hosted REST and MCP `pay` input accepts optional `original`, using the
334
+ same canonical v1 terms and transient confidential replay request as payment prepare.
216
335
  Supply the actual original signing intent; a description or commitment hash
217
- cannot reconstruct it. Reuse the existing `request_id` to resume the purchase.
336
+ cannot reconstruct it. Reuse the existing `idempotency_key` to resume the purchase.
218
337
 
219
- The existing owner-session `GET /owner/v1/approvals/:id` may return `detail` with
338
+ The owner-session `GET /owner/v1/approvals/:id` may return `detail` with
220
339
  exact `requestedTerms` and an optional prepared payment. Null or absent detail
221
- means no available retained context. The private approval-page purpose may retain
222
- original signing intent under its configurable, initially enabled product policy;
223
- HTTP replay bodies and headers remain transient and context never goes to logs
224
- or telemetry. The owner and original payment/approval/deadline bindings remain
225
- required. Hosted Tempo payments ask the owner each time and resume the same
226
- approved purchase. Internal admission and current spending checks still apply.
227
- This source contract does not establish deployed availability.
340
+ means no retained context. The approval page may retain the original signing
341
+ intent under a configurable product policy, on by default; HTTP replay bodies
342
+ and headers remain transient and context never goes to logs or telemetry. The
343
+ owner and original payment/approval/deadline bindings remain required. Hosted
344
+ Tempo payments ask the owner each time and resume the same approved purchase.
345
+ Current spending checks still apply.
346
+
347
+ ### Protected message signing
348
+
349
+ Status separates deployment `message_signing.configured` from this agent's
350
+ `message_signing.readiness`. Follow an incomplete readiness continuation.
351
+ `ready` means prerequisites are present, not that a message is authorized;
352
+ service admission and separate owner consent still apply. Older status answers
353
+ without readiness do not establish it from connection or configuration alone.
354
+
355
+ `visa-tool sign_action` and the local/hosted MCP tool accept `idempotency_key`,
356
+ `scheme: "personal_sign"`, the original UTF-8 `message` and an HTTPS-origin
357
+ `audience`. Use the same idempotency key and original inputs to resume. The selected
358
+ agent determines the wallet and Tempo access key. The signature is an
359
+ account-bound Tempo keychain V2 envelope; a service must support that envelope.
360
+ Audience and network metadata constrain issuance. The external signature binds
361
+ them only when the service includes them in its signed message.
362
+
363
+ Message permission is separate from payment limits. The owner can approve the
364
+ exact message or, on a device, an explicit literal prefix using
365
+ `permission_scope`. Matching device requests name that `permission_id`.
366
+ Permissions currently last at most five minutes and the current key expiry;
367
+ renewal needs new owner consent. Hosted signatures require exact owner consent
368
+ and an independent owner vote each time; a hosted repeat of the request waits up
369
+ to 40 seconds for the owner. A hosted Taskmarket claim, submit or release needs
370
+ no clearance from the agent: Visa screens the task on every call and at the
371
+ owner's approval, and refuses a changed or missing task before signing. A
372
+ hosted connector is also served `task_status`, `task_action` and `task_post`:
373
+ Visa sends the Taskmarket requests for it, each signature through hosted
374
+ `sign_action` (a submission is one owner approval for both signatures) and each
375
+ payment through hosted `pay`, so the owner approves every one. Signing
376
+ may authorize escrow or other financial effects at a service even though no new
377
+ payment occurs.
378
+
379
+ `GET /agent/v1/signatures/:request_id` reports metadata only. Repeating the
380
+ original `sign_action` recovers a known provider activity and verifies the
381
+ signature again before delivery. A dispatch with a lost activity locator stays
382
+ unknown and is never replaced. The private owner page can revoke consent and
383
+ dispose of its message context; commitments and dispatch fences remain.
384
+
385
+ The production signer refuses unconfigured, unknown or ambiguous message
386
+ namespaces. Trusted policy configuration and protected-provider qualification
387
+ are activation requirements. No marketplace commands, EIP-712, root-wallet
388
+ signing, ERC-8004 registration or automatic legal acceptance are added.
389
+
390
+ ### HTTP identity
391
+
392
+ Protected signer preparation retains an assigned directory Key ID, registered
393
+ agent domain and known public directory origin when the registry supplies them.
394
+ The current signing profile still uses the cryptographic key thumbprint in its
395
+ signed headers. Directory UUIDs require a protected signing-contract upgrade.
396
+ HTTP identity v5 can cover a caller-supplied `Signature-Agent` header, but this
397
+ metadata is not automatically sent. Retaining metadata alone does not establish
398
+ merchant interoperability.
399
+
400
+ `visa-tool attest_http_request` and local/hosted MCP accept a canonical HTTPS
401
+ `url`, `method` (`GET`, `HEAD`, `POST`), application `headers` as pairs and
402
+ `body_sha256` as a base64url SHA-256 digest. They return the exact normalized
403
+ request and short-lived TAP identity headers. The agent sends them with its own
404
+ HTTP client. Nothing is sent or paid by this tool; a 402 still needs `pay`.
405
+
406
+ This uses the TAP identity key and standing owner TAP consent. `sign_action`
407
+ continues to govern wallet-key messages. Requests can authorize service actions,
408
+ so use the identity deliberately. Requires gate v5; older deployments refuse.
409
+ See [the protocol, SDK and merchant contract](../../docs/design/target/http-attestation.md).
410
+
411
+ ### Merchant headers for Tempo MPP
412
+
413
+ `pay` accepts `headers` as a string map or name/value pairs. Supply the merchant's
414
+ required receipt and stable operation key, such as `Idempotency-Key`, unchanged
415
+ on recovery. They are independent of Visa's purchase UUID. Permitted names are
416
+ case-insensitive, values stay exact, and request commitments detect changes.
417
+ Reserved transport, wallet/payment authorization and Visa headers are refused.
418
+ Raw values stay transient and do not enter logs or public history. Redirects are
419
+ not followed with these headers.
420
+
421
+ Registry documentation follows the package release channel: prerelease builds use preview docs; stable builds use production docs.
422
+
423
+ `status` accepts only the optional `agent` selector. Unknown arguments are refused;
424
+ correct them on the same tool.
425
+
426
+ Correct a malformed idempotency_key on the same tool that refused it. Resuming an existing operation keeps its original idempotency_key.
427
+
428
+ Hosted discover searches by query. To price a URL, use discover with target in Visa CLI on a connected computer.
429
+
430
+ A validation refusal describes the rejected input. It does not establish an earlier purchase’s outcome; read that purchase with payment_status and its original reference.
431
+
432
+ A refused or unavailable task directs the agent to another permitted task. A task needing review directs it to Visa support; these refusals do not direct purchase-status reads.
433
+
434
+ For a signing input conflict, repeat sign_action with its original inputs and idempotency_key. The repeat_original_sign_action continuation refers to signing; payment_status retrieves purchases.
435
+
436
+ A wrong computer clock is refused as `agent_clock_skew` (or `agent_signature_signature_stale` when minting a token) with the owner's `fix_clock` step: nothing was signed or sent. Once the clock updates automatically, retry the same request; reconnecting does not help.
437
+
438
+ An account-wide daily limit offers a smaller payment, a later attempt or Visa
439
+ support. Raising an agent's spending limit cannot change that account limit.
440
+
441
+ `history` accepts the previous answer's `page.next_cursor` as `cursor`, unchanged,
442
+ to read the next page on local and hosted tools.
443
+
444
+ `discover` accepts `limit` from 1 to 50. A search returns the matching services
445
+ available, which can be fewer than the requested limit.
446
+
447
+ A final `payment_status` or `payment_cancel` read updates this computer's purchase
448
+ record. Disconnect releases a key once its purchases are final; an uncertain or
449
+ in-flight purchase keeps its key and any saved merchant answer.
450
+
451
+ HTTP identity requests for a paused agent retain the owner's resume step,
452
+ including when its public identity entry is no longer available.
@@ -0,0 +1,15 @@
1
+ // The bootstrap package stays installed while a replacement is downloaded.
2
+ const fs = require('node:fs')
3
+ const path = require('node:path')
4
+ module.exports = function activePackage(bootstrap) {
5
+ const root = path.join(bootstrap, '.self-update')
6
+ let version
7
+ try {
8
+ version = JSON.parse(fs.readFileSync(path.join(root, 'active.json'), 'utf8')).version
9
+ } catch (error) {
10
+ if (error.code === 'ENOENT') return bootstrap
11
+ throw error
12
+ }
13
+ if (!/^\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(version)) throw new Error('Invalid CLI release')
14
+ return path.join(root, 'releases', version, 'node_modules', '@visa', 'cli')
15
+ }
package/bin/update.cjs ADDED
@@ -0,0 +1,122 @@
1
+ const fs = require('node:fs')
2
+ const path = require('node:path')
3
+ const { pathToFileURL } = require('node:url')
4
+ const { execFileSync } = require('node:child_process')
5
+ const { randomUUID } = require('node:crypto')
6
+
7
+ module.exports = async function update(bootstrap, tag, logger) {
8
+ const activePackage = require('./active-package.cjs')
9
+ const root = path.join(bootstrap, '.self-update')
10
+ const helpers = await import(
11
+ pathToFileURL(path.join(__dirname, '..', 'dist', 'managed-runtime', 'update-and-restart.mjs'))
12
+ .href
13
+ )
14
+ if (process.env.VISA_MANAGED_RUNTIME_DIR) {
15
+ const managed = await import(
16
+ pathToFileURL(path.join(__dirname, '..', 'dist', 'managed-runtime', 'resolve-and-update.mjs'))
17
+ .href
18
+ )
19
+ await managed.runManagedRuntimeConvergence(process.env, {
20
+ logger: { log: (message) => logger.info('update:managed', { message }) },
21
+ })
22
+ return
23
+ }
24
+ const id = randomUUID()
25
+ const options = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 300_000 }
26
+ // Four registry/install commands and two probes must finish before this lease can expire.
27
+ const unlock = helpers.acquireUpdateLock(root, id, { leaseMs: options.timeout * 6 + 60_000 })
28
+ const npm = (command, args, opts) => {
29
+ const output = execFileSync(command, args, opts)
30
+ logger.info('update:package_manager', { command: args[0], output: String(output ?? '') })
31
+ return output
32
+ }
33
+ try {
34
+ // A preceding updater may have activated a release while this one loaded.
35
+ const active = activePackage(bootstrap)
36
+ const version = JSON.parse(
37
+ npm('npm', ['view', `@visa/cli@${tag}`, 'version', '--json'], options)
38
+ )
39
+ if (
40
+ typeof version !== 'string' ||
41
+ !/^\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(version) ||
42
+ version.includes('-rc.') !== (tag === 'rc')
43
+ )
44
+ throw new Error('Registry did not return an exact release on this channel')
45
+ if (JSON.parse(fs.readFileSync(path.join(active, 'package.json'), 'utf8')).version === version)
46
+ return
47
+ const release = helpers.stageExactRelease(
48
+ {
49
+ managedDir: root,
50
+ exactVersion: version,
51
+ commandTimeoutMs: options.timeout,
52
+ correlationId: id,
53
+ },
54
+ {
55
+ execFileSync: npm,
56
+ logger: { log: (message) => logger.info('update:stage', { message }) },
57
+ }
58
+ )
59
+ const installed = path.join(release, 'node_modules', '@visa', 'cli')
60
+ checkStagedRelease(installed, version, options)
61
+ const checked = JSON.parse(
62
+ execFileSync(
63
+ process.execPath,
64
+ [
65
+ '-e',
66
+ `const server = require(process.argv[1]);
67
+ if (typeof server.startMcpServer !== 'function') throw new Error('Incompatible MCP launcher');
68
+ server.startMcpServer();`,
69
+ path.join(installed, 'dist', 'mcp-server', 'index.js'),
70
+ '--self-check',
71
+ ],
72
+ options
73
+ )
74
+ )
75
+ if (checked.ok !== true || checked.build?.version !== version)
76
+ throw new Error('Staged MCP server did not load')
77
+ // Readers see the complete old release or the complete new release. The
78
+ // old package and every previous release remain usable after interruption.
79
+ const next = path.join(root, `.active-${id}.json`)
80
+ fs.writeFileSync(next, JSON.stringify({ version }), { mode: 0o600, flag: 'wx' })
81
+ fs.renameSync(next, path.join(root, 'active.json'))
82
+ } finally {
83
+ unlock()
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Loads a staged release's bundles, not only its version line (`--version`
89
+ * answers before any bundle loads): a release whose CLI, tool or MCP bundle
90
+ * fails to load is never activated, so `visa update` always stays reachable.
91
+ */
92
+ function checkStagedRelease(installed, version, options) {
93
+ let loaded
94
+ try {
95
+ loaded = JSON.parse(
96
+ execFileSync(
97
+ process.execPath,
98
+ [path.join(installed, 'bin', 'visa-cli.js'), '--self-check'],
99
+ options
100
+ ).trim()
101
+ )
102
+ } catch (error) {
103
+ throw new Error('Staged CLI did not load', { cause: error })
104
+ }
105
+ if (loaded?.ok !== true || loaded.version !== version)
106
+ throw new Error('Staged CLI version did not match')
107
+ execFileSync(
108
+ process.execPath,
109
+ [
110
+ '-e',
111
+ `const tool = require(process.argv[1]);
112
+ const answers = require(process.argv[2]);
113
+ if (typeof tool.main !== 'function' || typeof answers.answerToolCall !== 'function')
114
+ throw new Error('Incompatible tool bundle');`,
115
+ path.join(installed, 'dist', 'tool-call.js'),
116
+ path.join(installed, 'dist', 'mcp-server', 'tool-answers.js'),
117
+ ],
118
+ options
119
+ )
120
+ }
121
+
122
+ module.exports.checkStagedRelease = checkStagedRelease
package/bin/visa-cli.js CHANGED
@@ -1,2 +1,13 @@
1
1
  #!/usr/bin/env node
2
- require('../dist/cli.js');
2
+ // Compiled code is cached on disk (Node's own cache), so a repeat start skips
3
+ // recompiling the bundles (L7-4). Older Nodes without it start as before.
4
+ try {
5
+ require('node:module').enableCompileCache?.()
6
+ } catch {}
7
+ const path = require('node:path')
8
+ const active = require('./active-package.cjs')(path.dirname(__dirname))
9
+ const argv = process.argv.slice(2)
10
+ // The version answers before any bundle loads (L7-4).
11
+ if (argv.length === 1 && (argv[0] === '--version' || argv[0] === '-V'))
12
+ process.stdout.write(`${require(path.join(active, 'package.json')).version}\n`)
13
+ else require(path.join(active, 'dist', 'cli.js'))