@visa/cli 4.1.0-rc.99 → 5.0.0-rc.335

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 (85) hide show
  1. package/README.md +282 -156
  2. package/dist/cli.js +333 -532
  3. package/dist/managed-runtime/resolve-and-update.mjs +268 -0
  4. package/dist/managed-runtime/runtime-readiness.mjs +126 -0
  5. package/dist/managed-runtime/update-and-restart.mjs +1079 -0
  6. package/dist/mcp-apps/ucp-checkout.html +280 -0
  7. package/dist/mcp-server/index.js +234 -387
  8. package/dist/merchant-ucp-mcp/index.js +7 -0
  9. package/dist/skills/pair-visa-agent/RUNTIMES.md +56 -26
  10. package/dist/skills/pair-visa-agent/SKILL.md +319 -320
  11. package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +407 -0
  12. package/dist/skills/pair-visa-agent/scripts/setup.mjs +310 -30
  13. package/dist/skills/visa-shopify-checkout/SKILL.md +122 -0
  14. package/dist/skills/visa-shopify-checkout/references/evidence-and-states.md +37 -0
  15. package/dist/skills/visa-ucp-shopping/SKILL.md +86 -0
  16. package/install.ps1 +10 -6
  17. package/install.sh +7 -2
  18. package/native/bin/darwin-arm64/visa-runtime-signer +0 -0
  19. package/native/bin/darwin-x64/visa-runtime-signer +0 -0
  20. package/native/bin/linux-arm64/visa-runtime-signer +0 -0
  21. package/native/bin/linux-x64/visa-runtime-signer +0 -0
  22. package/native/bin/win32-arm64/visa-runtime-signer.exe +0 -0
  23. package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
  24. package/native/bin/win32-x64/visa-runtime-signer.exe +0 -0
  25. package/package.json +23 -31
  26. package/server.json +3 -3
  27. package/dist/checkout-engine/adapters/generic.d.ts +0 -23
  28. package/dist/checkout-engine/adapters/generic.js +0 -216
  29. package/dist/checkout-engine/adapters/index.d.ts +0 -10
  30. package/dist/checkout-engine/adapters/index.js +0 -24
  31. package/dist/checkout-engine/adapters/shopify.d.ts +0 -31
  32. package/dist/checkout-engine/adapters/shopify.js +0 -423
  33. package/dist/checkout-engine/adapters/stripe-like.d.ts +0 -10
  34. package/dist/checkout-engine/adapters/stripe-like.js +0 -21
  35. package/dist/checkout-engine/amount.d.ts +0 -15
  36. package/dist/checkout-engine/amount.js +0 -72
  37. package/dist/checkout-engine/browser-launch.d.ts +0 -46
  38. package/dist/checkout-engine/browser-launch.js +0 -81
  39. package/dist/checkout-engine/ceremony.d.ts +0 -64
  40. package/dist/checkout-engine/ceremony.js +0 -261
  41. package/dist/checkout-engine/cli-engine.d.ts +0 -227
  42. package/dist/checkout-engine/cli-engine.js +0 -779
  43. package/dist/checkout-engine/detect.d.ts +0 -61
  44. package/dist/checkout-engine/detect.js +0 -398
  45. package/dist/checkout-engine/evidence.d.ts +0 -25
  46. package/dist/checkout-engine/evidence.js +0 -104
  47. package/dist/checkout-engine/executor.d.ts +0 -176
  48. package/dist/checkout-engine/executor.js +0 -1325
  49. package/dist/checkout-engine/hosted-approval.d.ts +0 -187
  50. package/dist/checkout-engine/hosted-approval.js +0 -478
  51. package/dist/checkout-engine/index.d.ts +0 -6
  52. package/dist/checkout-engine/index.js +0 -8
  53. package/dist/checkout-engine/inline-target.d.ts +0 -13
  54. package/dist/checkout-engine/inline-target.js +0 -37
  55. package/dist/checkout-engine/instrument.d.ts +0 -61
  56. package/dist/checkout-engine/instrument.js +0 -87
  57. package/dist/checkout-engine/live-fill-approval.d.ts +0 -43
  58. package/dist/checkout-engine/live-fill-approval.js +0 -90
  59. package/dist/checkout-engine/mandate/card-mandate.d.ts +0 -121
  60. package/dist/checkout-engine/mandate/card-mandate.js +0 -227
  61. package/dist/checkout-engine/mandate/mandate-ledger.d.ts +0 -142
  62. package/dist/checkout-engine/mandate/mandate-ledger.js +0 -338
  63. package/dist/checkout-engine/mandate.d.ts +0 -25
  64. package/dist/checkout-engine/mandate.js +0 -100
  65. package/dist/checkout-engine/outcome.d.ts +0 -30
  66. package/dist/checkout-engine/outcome.js +0 -225
  67. package/dist/checkout-engine/owner-only-file.d.ts +0 -19
  68. package/dist/checkout-engine/owner-only-file.js +0 -41
  69. package/dist/checkout-engine/package.json +0 -3
  70. package/dist/checkout-engine/receipt.d.ts +0 -81
  71. package/dist/checkout-engine/receipt.js +0 -109
  72. package/dist/checkout-engine/repo-env.d.ts +0 -11
  73. package/dist/checkout-engine/repo-env.js +0 -23
  74. package/dist/checkout-engine/trace-handles.d.ts +0 -8
  75. package/dist/checkout-engine/trace-handles.js +0 -12
  76. package/dist/checkout-engine/types.d.ts +0 -44
  77. package/dist/checkout-engine/types.js +0 -2
  78. package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
  79. package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
  80. package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +0 -82
  81. package/dist/checkout-engine/vgs-gateway/server-mint-client.js +0 -180
  82. package/dist/checkout-engine/vgs-live-instrument.d.ts +0 -179
  83. package/dist/checkout-engine/vgs-live-instrument.js +0 -296
  84. package/dist/checkout-engine/vic-confirmation.d.ts +0 -34
  85. package/dist/checkout-engine/vic-confirmation.js +0 -39
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: pair-visa-agent
3
- description: Pair a Visa CLI v4 agent identity to this runtime. Present one authorization link, the returned public confirmation code, and the full public request-key fingerprint to the human, then poll until the runtime's local Ed25519 identity is activated. Use when the user says "pair my agent", "enroll my Visa CLI", "connect my Visa agent", or "set up my agent identity".
4
- compatibility: Needs the `visa` CLI (`npm i -g @visa/cli@rc`) — run `node scripts/setup.mjs` to install it if missing — plus network access to the Visa authorization service. Works in OpenClaw, Hermes, or any Agent Skills runtime.
3
+ description: Set up a Visa CLI v4 agent through the single protected enrollment implementation (MCP `agent_enroll` or `visa connect`), and report its live capabilities honestly. The owner always approves in their browser. Use when the user says "pair my agent", "connect Visa", "enroll my Visa CLI", "set up my agent", or "let this agent pay".
4
+ compatibility: Needs the `visa` CLI (`npm i -g @visa/cli@rc`) — run `node scripts/setup.mjs` to install it if missing — plus network access to the Visa authorization service. Works in OpenClaw, Hermes, Claude Code, Codex, or any Agent Skills runtime.
5
5
  allowed-tools: Bash(visa:*) Bash(visa-cli:*) Bash(node:*) Bash(npm:*) Bash(npx:*)
6
6
  metadata:
7
7
  author: visa
8
8
  homepage: https://visacli.sh/agents
9
- version: '0.6.8'
9
+ version: '0.8.0'
10
10
  # OpenClaw-namespaced extension (agentskills.io keeps `metadata` free-form, so
11
11
  # non-standard runtime config lives here — `user-invocable` is not a standard
12
12
  # top-level field). OpenClaw auto-installs `install[]` when `requires.bins` are
@@ -27,35 +27,85 @@ metadata:
27
27
  - visa-cli
28
28
  ---
29
29
 
30
- # Pair a Visa CLI v4 agent identity
30
+ # Set up a Visa agent
31
31
 
32
- Pair this runtime's locally generated identity with a human-approved Visa agent. Pairing
33
- protocol v2 has one ceremony and three equivalent ways to drive it:
32
+ One protected enrollment carries everything: this runtime's identity, the binding to
33
+ **this device**, and the spending limits — behind a **single** owner approval in their
34
+ browser.
34
35
 
35
- - **`pair_agent_start` / `pair_agent_poll`** — OpenClaw plugin tools.
36
- - **`enroll_agent` with `action: "start"` / `action: "claim"`** — the `visa` MCP server tool.
37
- - **`visa agent enroll --format json` / `visa agent enroll-claim --format json`** — the raw
38
- CLI and universal fallback.
36
+ It has two entrances to the exact same protected enrollment implementation: the same
37
+ native signer, Authority routes, owner browser approval, and limits. Never invent or mix
38
+ in a second ceremony.
39
39
 
40
- Use the first surface available. Do not mix this flow with older pairing or login flows;
41
- all three surfaces above wrap the same v2 enrollment ceremony and local pending state.
40
+ - **You have the Visa MCP server mounted:** call `agent_enroll` and hand the owner the one
41
+ link and short code it returns. Call it again with `wait: true` once they have the page
42
+ open; it finishes activation only after they approve.
43
+ - **The owner is at a terminal:** they run the command below.
42
44
 
43
- ## What pairing establishes
45
+ ```
46
+ # the human runs this:
47
+ visa connect
48
+
49
+ → it prints a URL and a short code
50
+ → the human opens the URL and enters the code at /agent/enroll/protected-agent
51
+ → the human approves the device and its spending limits (one approval)
52
+ → the command finishes; `get_status` now reports pairing.paired: true
53
+ ```
54
+
55
+ The installed CLI selects its own service origins from its release channel. Never ask
56
+ for, guess, or construct a Visa URL.
57
+
58
+ Running the same command again resumes an interrupted enrolment; `--restart` replaces an
59
+ unclaimed request with a fresh code. `--ceiling <usd>` and `--per-transaction <usd>`
60
+ propose limits the owner confirms on the approval page.
61
+
62
+ ## The older ceremonies were deleted — do not call them
63
+
64
+ `setup_start`, `setup_status`, `setup_resume`, `setup_cancel`, `setup_agent`,
65
+ `enroll_agent`, `agent_connect`, `agent_connect_poll`, `agent_connect_cancel`,
66
+ `agent_handoff_claim` and `agent_pairing_cancel` are gone, along with the whole
67
+ `visa setup` and `visa agent` command groups — including the retired
68
+ `agent pair`, `agent create`, `agent verify`, `agent enroll-claim`,
69
+ `agent claim`, `agent pairing-resume`, `agent connect`, `agent handoff-claim`
70
+ and every `agent grant-*` verb.
44
71
 
45
- Pairing activates an agent identity on this runtime:
72
+ **`visa connect` is the LIVE door and is not on that list.** It is the
73
+ top-level command, not the retired `visa agent connect` subcommand: one is the
74
+ one door, the other is a ceremony that no longer exists.
75
+
76
+ Calling any of them returns one refusal:
77
+
78
+ ```json
79
+ {
80
+ "code": "legacy_door_removed",
81
+ "kind": "caller",
82
+ "fix": "visa connect (shell) or agent_enroll (MCP)"
83
+ }
84
+ ```
46
85
 
47
- - `agentId` is the server-assigned, stable identifier for the agent.
48
- - The runtime creates and retains the private Ed25519 identity key. It sends only the
49
- public JWK to the service.
50
- - `identityKeyJkt` is the thumbprint of the currently bound Ed25519 public key. It can
51
- change when that key rotates, so it must never be presented or stored as the stable
52
- agent identifier.
53
- - The human receives the authorization URL, stable agent ID, and full public request-key
54
- fingerprint for exact comparison. Private key material, the local claim token, and signed
55
- protocol messages stay with the runtime.
56
- - An `activated` result means **identity paired only**. Payment methods, an email address,
57
- and tap bindings are separate configuration that may be added later. Do not infer any
58
- of those capabilities from pairing success.
86
+ That code means the door no longer exists. **Relay the replacement and stop.** Do not
87
+ retry it, do not try a variant spelling, and do not report it to the human as an outage:
88
+ nothing was signed, paired, or paid.
89
+
90
+ ## What "set up" means here
91
+
92
+ Each completed protected enrollment mints a **new** agent: a new server-assigned agent,
93
+ a new device-held Ed25519 identity key, and the wallet limits the owner approved.
94
+
95
+ - The runtime keeps the private Ed25519 key and sends only the public JWK.
96
+ - The limits are the ones the owner actually approved on the page. Nothing you asked for
97
+ is granted until they approve it.
98
+ - An email address, a `.visa` mesh name, and TAP bindings remain separate, later
99
+ configuration. Do not infer them from a finished enrolment.
100
+
101
+ **Where the `agentId` comes from — read this before you quote one.** Do not invent it and
102
+ do not read it out of the command's terminal output, which you often cannot see. Read it
103
+ from `get_status` or `agent_capabilities` once the enrolment finishes. Never substitute a
104
+ correlation id, a confirmation code, or an origin for an `agentId`.
105
+
106
+ **Re-running the command does not repair an agent that already exists** — it creates a
107
+ second one. To fix a capability an existing agent is missing, see "Already set up — do NOT
108
+ enrol again" near the end.
59
109
 
60
110
  ## Getting this skill
61
111
 
@@ -64,33 +114,39 @@ monorepo is required:
64
114
 
65
115
  ```sh
66
116
  npm install -g @visa/cli@rc
67
- visa agent skill
117
+ visa connect
68
118
  ```
69
119
 
70
- `visa agent skill` auto-detects OpenClaw, Hermes, and Claude Code, falling back to the
71
- project-local `./.agents/skills`. Pass `--runtime <name>` or `--dir <path>` to choose a
72
- target, `--force` to overwrite, or `--print` to read without writing. Reload or restart
73
- the agent runtime after installation so it registers the skill.
120
+ `visa connect` auto-detects the runtime it is running in. It plants this skill into that
121
+ runtime's own skills directory (OpenClaw, Hermes, Claude Code and Codex each read their
122
+ own), mounts the Visa MCP server, signs the owner in, enrols this machine's agent and
123
+ renews its device lease — skipping whatever is already done. Pass a client explicitly
124
+ (`visa connect codex`) when auto-detection is not what you want, and `--restart` to
125
+ replace an edited skill. Reload or restart the agent runtime afterwards so it registers
126
+ the skill.
74
127
 
75
128
  Access remains enforced by the Visa service. Installing the public package or skill does
76
129
  not authorize an account to pair.
77
130
 
78
- OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
131
+ OpenClaw users receive the skill via `visa connect openclaw` (or through the `@visa/visa-cli-openclaw` plugin when running from source).
79
132
 
80
133
  ## Getting set up
81
134
 
82
135
  1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
83
136
  idempotent provisioner `node scripts/setup.mjs`. The `@latest` tag may not yet expose
84
- the v4 agent commands. Installation puts `visa` and `visa-cli` on `PATH` and includes
137
+ the v4 setup tools. Installation puts `visa` and `visa-cli` on `PATH` and includes
85
138
  `@visa/cli/dist/mcp-server/index.js`.
86
- 2. **Mount the MCP server when the runtime supports MCP.**
87
- - **OpenClaw:** installing `@visa/visa-cli-openclaw` auto-mounts the server by writing
88
- `mcp.servers["visa-cli"]` in `~/.openclaw/openclaw.json`.
89
- - **Hermes or another supported runtime:** run `visa-cli connect hermes` or
90
- `visa-cli connect <runtime>`. For Hermes, the entry is written under `mcp_servers`
91
- in `~/.hermes/config.yaml`.
92
- - **No MCP integration:** use the raw `visa agent …` commands.
93
- 3. **Pair.** Follow the core flow below.
139
+ 2. **Run `visa connect <client>`.** One command: it plants the skill, writes the MCP
140
+ entry, signs the owner in, enrols this machine's agent and renews its device lease.
141
+ - **OpenClaw:** `visa connect openclaw` writes `mcp.servers["visa-cli"]` in
142
+ `~/.openclaw/openclaw.json`. (Installing `@visa/visa-cli-openclaw` from
143
+ source/tarball also auto-mounts the server).
144
+ - **Hermes:** `visa connect hermes` writes the entry under `mcp_servers` in
145
+ `~/.hermes/config.yaml`.
146
+ - **No MCP integration:** everything below still works — connecting is a shell
147
+ command, and `--format json` is available on every command.
148
+ 3. **Check what is owed.** `visa status` prints the owner, the agent, its limits, which
149
+ clients are mounted, and one `next:` line when something is blocking.
94
150
 
95
151
  ## MCP mounting examples
96
152
 
@@ -112,323 +168,263 @@ OpenClaw (`~/.openclaw/openclaw.json`):
112
168
  }
113
169
  ```
114
170
 
115
- Hermes (`~/.hermes/config.yaml`):
171
+ Hermes (`~/.hermes/config.yaml`). **Hermes passes ONLY this `env:` map to the MCP
172
+ subprocess — it does NOT inherit the gateway environment.** Omitting a required variable
173
+ (an RC access code, the right `HOME`, `PATH`) makes the server exit on every start while
174
+ `agent_capabilities` — which reads on-disk grant state, not live tool registration — can
175
+ still report rails as available. Always set the map explicitly:
116
176
 
117
177
  ```yaml
118
178
  mcp_servers:
119
179
  visa-cli:
120
180
  command: node
121
181
  args: ['<npm root -g>/@visa/cli/dist/mcp-server/index.js']
182
+ # Hermes does NOT inherit the gateway env. This map is the entire
183
+ # subprocess environment; omit VISA_RC_CODE and the server exits on boot.
184
+ env:
185
+ HOME: /home/<user> # the home that holds this runtime's .visa-cli state
186
+ VISA_RC_CODE: <access code>
187
+ PATH: /usr/local/bin:/usr/bin:/bin
122
188
  ```
123
189
 
190
+ Hermes also loads skills **per profile** from `~/.hermes/profiles/<profile>/skills/`, not
191
+ from `~/.hermes/skills/`. `visa connect hermes` resolves this automatically: it targets
192
+ the single profile when exactly one exists (or the one named by `HERMES_PROFILE`), and
193
+ **fails loudly** on a multi-profile box instead of planting into the flat dir nothing
194
+ reads — set `HERMES_PROFILE` to choose.
195
+
196
+ **Hermes sanitizes MCP server names when registering tools.** A server declared
197
+ `visa-cli` in `mcp_servers:` registers its tools as `mcp__visa_cli__<tool>` — with an
198
+ UNDERSCORE, not the declared hyphen. Anything that hardcodes `mcp__visa-cli__<tool>` gets
199
+ `unknown tool` on every call while looking correct in review. Read tool names off the
200
+ live registry; never derive them from the config key.
201
+
124
202
  In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
125
203
  existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
126
204
 
127
- ## JSON and exit-code contract
205
+ ## Sign in first — the wallet is owner-bound
206
+
207
+ The USDC wallet is delegated out of the owner's own wallet, so this runtime needs a live
208
+ owner session. Establish it **before** the enrolment command runs:
209
+
210
+ - [ ] Call `agent_login` (MCP, default action `"start"`) or run
211
+ `visa connect --format json` (CLI). The result carries a sign-in `browserUrl`
212
+ and a short 6-character `confirmCode`.
213
+ - [ ] Relay **both** to the human in your reply — the bare URL on its own line, and the
214
+ code. You are very often not in a terminal they can see; the chat message is the
215
+ only place these values reach them.
216
+ - [ ] The human opens the link, signs in (Google or email), and **types the confirmation
217
+ code into the sign-in page** — into the browser, never back to you in chat.
218
+ - [ ] Claim the session: `agent_login {"action":"claim"}` (the CLI command polls on its
219
+ own). Once claimed, the session token is stored locally.
128
220
 
129
- Prefer structured output and parse it; never scrape prose.
221
+ `agent_login` establishes the OWNER's session on this device. It creates no agent and
222
+ grants no spending authority — those come from the enrolment command and the approval the
223
+ owner gives in their browser.
130
224
 
131
- - The OpenClaw and MCP tools return structured objects directly.
132
- - Raw CLI commands support `--format json`.
133
- - `visa agent enroll-claim` exits `0` when activated, `3` when the human has not finished,
134
- and `1` on a terminal failure.
225
+ If something reports `{"code":"session_required"}` or "Not logged in", that is this
226
+ ordering rule, not a fault: run `agent_login`, drive the sign-in above to a claimed
227
+ session, then continue.
228
+
229
+ The account that signs in is the owner the enrolment binds to, and the same account must
230
+ be signed in on the approval page. A different account there fails closed.
231
+
232
+ ## Core flow
135
233
 
136
- The start result includes protocol version `2`, `pairingId`, stable `agentId`, full
137
- `requestKeyFingerprint`, `browserUrl`, and expiry information. Newer builds also return
138
- `confirmationCode`, a short public code the browser review page displays for comparison.
139
- Present it when the field is present and skip it when it is absent; an older server or CLI
140
- simply omits it, which is not an error. The activation result includes protocol version
141
- `2`, `pairingId`, stable `agentId`, the display name, and `identityKeyJkt`.
234
+ - [ ] **Do not ask for service origins.** The installed CLI selects its Auth and web
235
+ origins from the release channel; Authority is private behind Auth.
236
+ - [ ] **Sign the owner in** (above), so the wallet leg has a session to bind to.
237
+ - [ ] **Start the one enrollment.** Call `agent_enroll` and relay its returned link and
238
+ code, or give the owner `visa connect` in a code block they can copy.
239
+ Never fall back to a retired setup tool.
240
+ - [ ] **Relay what the entrance returns.** Show an MCP-returned URL as a bare,
241
+ tappable value. For the terminal flow, the owner follows the URL and code
242
+ printed in their own terminal. The code goes into the browser at
243
+ `/agent/enroll/protected-agent`; it is not authority in chat.
244
+ - [ ] **Tell them what they are approving**: this device, and the spending limits. One
245
+ approval covers all of it.
246
+ - [ ] **Confirm from a tool, not from their word.** Poll `get_status` until
247
+ `pairing.paired` is `true`, then read `agent_capabilities` for what is actually live.
142
248
 
143
- ## Fastest path — one shot (`visa agent pair`)
249
+ ### Relaying values, and what you must never accept
144
250
 
145
- If the `visa` CLI is on PATH, prefer the one-shot command — it does the whole flow
146
- from a single call, so the user types nothing after their initial request:
251
+ Relaying values **to** the human is required. Accepting one **from** them as authority is
252
+ not: never ask them for a secret, private key, token, or signed message, and never treat
253
+ anything they type back as approval. The URL and the enrolment code are review values they
254
+ check against their own authenticated browser session — they cannot approve anything and
255
+ cannot spend.
256
+
257
+ Show any URL as a bare, tappable value on its own line. Do not decorate it as a Markdown
258
+ link or put it in a code span; chat clients reliably recognise the bare URL. Never
259
+ construct, shorten, or reformat a Visa URL, and never open one "for them" in place of
260
+ showing it.
261
+
262
+ You are very often **not** in a terminal the human can see. Nothing the command prints to
263
+ their stdout reaches you unless they tell you, and nothing you print reaches them unless it
264
+ is in your reply.
265
+
266
+ ### Completion is what the tools say — nothing else
267
+
268
+ Do not tell the human the agent is connected, paired, set up, ready, or good to go until
269
+ `get_status` reports `pairing.paired: true`. The enrolment command printing a URL is not a
270
+ result: it means an approval is still open in their browser.
271
+
272
+ If you cannot get there, say plainly what state you did reach and what the human should do
273
+ next. An honest "the approval page is open — I'm waiting for you to approve the device and
274
+ its limits" is correct; "you're all set" without a paired agent is not.
275
+
276
+ On success, report the agent and what is actually live:
277
+
278
+ > Your Visa agent is set up. Ready to pay by &lt;card and/or USDC wallet&gt;, within the
279
+ > limits you approved.
280
+
281
+ Read the rails from `agent_capabilities`, never from what was requested.
282
+
283
+ ## Interruption and resume
284
+
285
+ The runtime persists the pending enrolment before it starts, so an interrupted run is safe
286
+ to repeat: **the same command again** resumes it, with the same request and the same
287
+ device key.
288
+
289
+ - Same command, no flags changed — resumes and reprints the URL and code.
290
+ - `--restart` — replaces an unclaimed request with a fresh terminal code. Use it only when
291
+ the previous code is genuinely unusable; it is not a retry button.
292
+ - `--wait` — keeps the command polling until the owner has approved, instead of returning
293
+ after printing the link.
294
+
295
+ Do not tell the human to run a second, different enrolment because the first went quiet.
296
+ Two enrolments mean two agents, two identities, and a confused owner. Do not delete or edit
297
+ local pending files to fix a transient failure, and never copy pending state between
298
+ runtimes.
299
+
300
+ ## Management commands (an agent that already exists)
147
301
 
148
302
  ```
149
- visa agent pair --format json
303
+ visa status --format json # owner, agent, limits, mounts, every readiness gate
304
+ visa limits --format json # what it may spend; with amounts, the owner approves
305
+ visa activity --format json # payments across every rail
306
+ visa disconnect --revoke # drop this machine's spending authority
150
307
  ```
151
308
 
152
- It returns `browserUrl`, `pairingId`, stable `agentId`, the full public
153
- `requestKeyFingerprint`, and — on newer builds — `confirmationCode` **immediately**. It
154
- starts a detached activation process only after the pending identity and private key are
155
- durable. Present the link, returned confirmation code, and fingerprint for browser
156
- comparison; omit only a confirmation code the result did not provide. When `claiming` is `background`,
157
- activation completes after approval without another command; if it is `manual`, run the
158
- returned `recoveryCommand` after approval. Use the step-by-step flow
159
- below when `pair` is unavailable or when driving the plugin/MCP tools — `enroll_agent`
160
- action `start` reports the same `claiming` / `recoveryCommand` fields and starts the same
161
- detached poller.
162
-
163
- ## Returning & already-connected — do NOT re-pair
164
-
165
- If `pair`/`enroll` returns **`already_connected`** (or `alreadyConnected: true`), this
166
- device is **already set up** as an agent — a fresh pairing is neither needed nor
167
- possible (re-pairing an existing identity silently dead-ends). Do this instead:
168
-
169
- 1. **Tell the user plainly:** "This device is already connected as `<name>.visa`."
170
- Read the name from the response. Do **not** start another hand-off. `--new` /
171
- `force:true` exists only if they explicitly want a _second, separate_ agent.
172
- 2. **Report status honestly — "connected" is five separate things**, not one. Never
173
- imply the agent can spend just because it's connected. Don't guess from prose —
174
- read it live from tools: `agent_capabilities` returns the DERIVED capability map
175
- (the identity + wallet + mail base plus card/tap/subway availability),
176
- `get_status` reports enrollment / account / version, and `agent_login` establishes
177
- or confirms the account session that spending grants require.
178
- - **Identity** — connected (`.visa` name bound to _this user's_ account). ✓ once `already_connected`.
179
- - **Spending** — a _separate human approval_, and there are two rails, each
180
- approved on the **v4 agent dashboard** (`app.visacli.sh/agent/enroll`) — not any
181
- account-settings page (that legacy surface is retired). The Turnkey
182
- **wallet:x402** rail (stablecoin) is approved with the owner's **sign-in session**
183
- (the Google/email auth-proxy) — **no passkey**. The **card:vic** rail is approved
184
- with a **passkey**. You **cannot** self-grant either; there is no
185
- `visa agent add-rail` command, and never self-mint a wallet with `wallet_init` on
186
- mainnet — it throws until the owner's delegation lands.
187
- - **Mesh (`.visa` messaging)** — with `SUBWAY_MESH=visa`, a returning device now
188
- registers on `pair`; if `meshRegistered` is false, `visa register <name>` joins it.
309
+ `visa status` is the one command that answers "can it spend, and what is missing" — it
310
+ carries the readiness gates the old `spendability` and `preflight` commands each answered
311
+ a fragment of. Pausing, resuming, renaming and revoking an agent are owner actions in the
312
+ Visa Console, not terminal commands.
313
+
314
+ None of these creates an identity. Prefer structured output and parse it; never scrape
315
+ prose.
316
+
317
+ ## Already set up — do NOT enrol again
318
+
319
+ If this device already holds an agent, a fresh enrolment is not needed and creates a
320
+ _second, separate_ agent. Do this instead:
321
+
322
+ 1. **Tell the user plainly:** "This device is already set up as `<name>`." Read the name
323
+ from `agent_capabilities` or `visa status`. Start another enrolment only if they
324
+ explicitly want a second agent.
325
+ 2. **Report status honestly — "set up" is several separate things.** Never imply the agent
326
+ can spend just because it exists. Read it live from tools rather than guessing from
327
+ prose: `agent_capabilities` returns the DERIVED capability map, `get_status` reports
328
+ pairing / account / version, `visa status --format json` answers "can it spend, and
329
+ what is missing", and `agent_login` establishes or confirms the account session.
330
+ - **Identity** — bound to _this user's_ account, on _this device_.
331
+ - **Spending** — the limits the owner approved in the browser. You **cannot** self-grant
332
+ either rail, and never self-mint a wallet with `wallet_init` on mainnet — it throws
333
+ `WalletCredentialRequiredError` until the owner's delegation lands.
334
+ - **Mesh (`.visa` messaging)** — separate; `visa register <name>` joins it.
189
335
  - **Trusted (TAP)** — follows spend/provisioning; don't promise it before then.
190
- 3. **Scope everything to the user.** The identity is bound to the account the user
191
- signed in with (their email); the wallet + spend limits are theirs. Speak in terms
192
- of "your agent / your account / the limits you approved," never a shared identity.
193
-
194
- Missing **spend** blocks _checkout mandates_, not a delegated x402 wallet — but that
195
- wallet must first be acquired by owner approval (below); it is never self-minted.
196
-
197
- ## What you can do once paired — granting spend authority (card or wallet)
198
-
199
- Pairing binds **identity only**. To let this agent pay, the human owner must **delegate**
200
- a spend rail — the runtime never self-mints one. There are two rails, and **one** ceremony
201
- drives both:
202
-
203
- - **`wallet:x402`** (Turnkey stablecoin) — owner approves with their **sign-in session, no
204
- passkey**. On mainnet `wallet_init` throws `WalletCredentialRequiredError` until this
205
- delegation lands, so never call it as a setup step.
206
- - **`card:vic`** (Visa card checkout) — owner approves with a **passkey**.
207
-
208
- The contract is identical for both rails, and it is **one command from you, one approval
209
- from the owner**. You never hand a command, code, or URL back to the human after they
210
- approve — you poll to completion yourself:
211
-
212
- 1. **You run one command** naming the rail and caps.
213
- 2. **The owner opens the returned link and approves once** (they may adjust the amount).
214
- 3. **You poll to activation** — the owner does nothing further.
215
-
216
- - [ ] **Pair** the identity (the flow above). Identity only — no rail yet.
217
- - [ ] **Establish the owner session once.** Grant creation is an account operation, so this
218
- runtime needs a live owner session: `agent_login` (MCP) or `visa agent login` (CLI).
219
- This is a short-lived session established once for the grant — **not** a re-pair, and
220
- **not** something the owner repeats per payment. If `agent_connect` later returns
221
- `{"code":"session_required"}`, the session lapsed — run `agent_login` again.
222
- - [ ] **Initiate the grant from MCP — no shelling, no invented URLs.** Call `agent_connect`
223
- with the rail and caps: `{"rail":"card","ceiling":"<usd>","perTransaction":"<usd>"}`
224
- (or `"rail":"wallet"`); omit `agentId` to target the most recently paired agent. It
225
- returns `{ url, code, attachId, willGrant, expiresAt }`. Present the **bare `url` and
226
- `code` exactly as returned** — never construct, shorten, or guess a Visa URL, and never
227
- open it yourself. The crypto approval happens in the owner's browser and cannot run
228
- inline in chat. (`setup_agent {"rail":"card"|"wallet"}` returns the same next-step map
229
- if you need it.)
230
- - [ ] **Owner approves once** on the **v4 agent dashboard** (`app.visacli.sh/agent/enroll`),
231
- confirming the caps — **card with a passkey, wallet with their sign-in session**. You
232
- cannot approve on their behalf.
233
- - [ ] **Poll to activation from MCP.** Call `agent_connect_poll` (`{"attachId":"<from
234
- agent_connect>"}`; or resume by `agentId`) — one bounded poll per call. It returns
235
- `{"ok":false,"state":"...","blockedByKind":"awaiting_human_approval"}` while pending;
236
- call again until `{"ok":true,"state":"grant_activated","caps":...}` (wallet also
237
- returns `fundAddress`). Activation registers the delegated signer + caps: wallet writes
238
- the Turnkey credential (`turnkey.json`); card writes the card pointer. It never spends.
239
- - [ ] **Confirm the rail is live.** Wallet: `wallet_status` / `visa wallet show`
240
- (`agent_capabilities.wallet.available`). Card: `agent_capabilities.card.available`.
241
- Only a delegated credential — not the served tool list — means the rail is usable.
242
- - [ ] **Then spend against the owner-approved caps.** Wallet: set the policy with
243
- `wallet_policy_set` (per-transaction / daily / session USD caps + optional network and
244
- merchant allow/deny lists that refuse an x402 payment BEFORE it is signed), then
245
- `wallet_pay`. Card: `start_card_mandate` then `pay_merchant`. Never raise a
246
- human-approved limit yourself.
247
-
248
- **Raw CLI equivalent (one shot).** If you cannot drive MCP, the same ceremony runs from the
249
- CLI and `--wait` polls to activation in a single call. Present the URL it prints — don't
250
- invent one:
336
+ 3. **Scope everything to the user.** The identity is bound to the account they signed in
337
+ with; the wallet and limits are theirs. Speak in terms of "your agent / your account /
338
+ the limits you approved", never a shared identity.
251
339
 
252
- ```
253
- visa agent grant-card <agentId> --ceiling <usd> --per-transaction <usd> --wait
254
- visa agent grant-wallet <agentId> --ceiling <usd> --per-transaction <usd> --wait
255
- ```
340
+ ## An existing agent is missing a capability
341
+
342
+ There is no rail-add ceremony any more: `visa agent grant-card` / `grant-wallet` /
343
+ `grant-activate` / `grant-claim` and the `agent_connect` tools were deleted, and calling
344
+ one returns `legacy_door_removed`.
345
+
346
+ What to do instead:
347
+
348
+ - [ ] **Diagnose first.** `visa status --format json` and `agent_capabilities` say
349
+ exactly what is missing. Do not start anything before you know which of identity,
350
+ wallet delegation, card authority or funding is absent.
351
+ - [ ] **If the device's identity custody is broken**, `visa connect` repairs it in place
352
+ without minting a new agent — it renews the device lease and skips every step that
353
+ is already done.
354
+ - [ ] **If the OWNER never approved that authority**, only they can add it. Say so, name
355
+ what is missing, and stop. Enrolling again mints a NEW agent — it does not upgrade
356
+ this one, and doing it silently leaves the owner with two agents and one funded
357
+ wallet.
256
358
 
257
- Use the stable `agentId` from pairing or `visa agent list` — never invent or alter the id.
258
359
  A policy refusal or timeout means **nothing was signed**: report it and stop; do not retry
259
- with a different command or a reconstructed URL.
360
+ with a different command or a reconstructed URL. Never raise a human-approved limit
361
+ yourself.
260
362
 
261
- Once a wallet is delegated and a policy is set, these are the served wallet tools this agent
262
- can actually call: `wallet_discover` (search the public x402 Bazaar), `wallet_probe` (read a
263
- challenge without paying), `wallet_pay` / `wallet_directory_pay` (pay, policy-enforced),
264
- `wallet_history` / `wallet_reconcile` (local ledger + resolve `reconciling` holds),
265
- `wallet_fund` (funding address + faucet), and `wallet_export` (export key material —
266
- dangerous). All spending is gated by the owner-approved local policy caps.
363
+ ## Spending, once a rail is live
267
364
 
268
- ## Core flow
365
+ Wallet: set the policy with `wallet_policy_set` (per-transaction / daily / session USD
366
+ caps plus optional network and merchant allow/deny lists that refuse an x402 payment
367
+ BEFORE it is signed), then `wallet_pay`. The served wallet tools are `wallet_discover`
368
+ (search the public x402 Bazaar), `wallet_probe` (read a challenge without paying),
369
+ `wallet_pay` / `wallet_directory_pay` (pay, policy-enforced), `wallet_history` /
370
+ `wallet_reconcile` (local ledger + resolve `reconciling` holds), `wallet_fund` (funding
371
+ address + faucet), and `wallet_export` (export key material — dangerous). All spending is
372
+ gated by the owner-approved local policy caps.
373
+
374
+ Card: not available in this build. Visa CLI ships no browser checkout (#8940), so
375
+ `start_card_mandate` returns `card_browser_checkout_removed` and creates no mandate, and
376
+ every `pay_merchant` action refuses with the same code. Nothing is charged. Do not offer
377
+ card checkout to the user, and do not relay a card approval URL; the wallet rail above is
378
+ the payment path.
379
+
380
+ On a hosted runtime, **every payment requires the owner's browser
381
+ approval** until the protected no-tap executor lands. Enrollment, a session, or a spending
382
+ grant does not approve a later hosted payment. Relay the hosted approval URL and wait for
383
+ the owner's decision before reporting success or retrying.
269
384
 
270
- - [ ] Start the pairing with `pair_agent_start`, `enroll_agent` action `start`, or
271
- `visa agent enroll --format json`.
272
- - [ ] Present the authorization URL, stable agent ID, full public fingerprint, and the
273
- `confirmationCode` when the start result carries one, to the human.
274
- - [ ] Poll with `pair_agent_poll`, `enroll_agent` action `claim`, or
275
- `visa agent enroll-claim --format json`.
276
- - [ ] Report the stable `agentId` and that the identity is paired on this runtime.
277
-
278
- ### Completion is `activated` — nothing else
279
-
280
- Only a poll result of `activated` means this runtime is paired. Until you have one, do not
281
- tell the human the agent is connected, paired, enrolled, set up, ready, or good to go, and
282
- do not move on to spending, mail, or mesh steps that assume an identity.
283
-
284
- `start` returning successfully is not completion. It returns a stable `agentId` and a
285
- `requestKeyFingerprint` **before any human has approved anything** — those are review
286
- values for the browser comparison, not evidence of pairing. Reporting an `agentId` as
287
- though it were a finished pairing is the most likely way to mislead the human here, because
288
- the number looks like a result.
289
-
290
- If you cannot reach `activated`, say plainly what state you did reach and what the human
291
- should do next. An honest "approved but not yet activated — I'm still polling" is correct;
292
- "you're all set" without an `activated` result is not.
293
-
294
- ### 1. Start
295
-
296
- Start once. If there is already a pending pairing, poll it before creating another.
297
-
298
- The result provides `browserUrl`, stable `agentId`, `requestKeyFingerprint`, and — on newer
299
- builds — `confirmationCode`. Show the URL as a bare, tappable value on its own line. Do not
300
- decorate it as a Markdown link or put it in a code span; chat clients reliably recognize the
301
- bare URL. Show the complete fingerprint without truncation and tell the human to approve
302
- only when every character matches the browser review page.
303
-
304
- You are very often **not** in a terminal the human can see. Nothing you print to stdout
305
- reaches them. Every value the browser asks them to compare has to appear in your reply, or
306
- the comparison silently becomes "click approve and hope" — which is the whole failure this
307
- step exists to prevent.
308
-
309
- Use this concise shape:
310
-
311
- > 🔐 Pair your Visa agent — open this authorization page:
312
- >
313
- > 👉 &lt;browserUrl, bare and on its own line&gt;
314
- >
315
- > Stable agent ID: &lt;agentId&gt;
316
- >
317
- > Confirmation code — this exact code should appear on the page:
318
- > &lt;confirmationCode&gt;
319
- >
320
- > Public request-key fingerprint — compare every character in the browser:
321
- > &lt;requestKeyFingerprint, complete and untruncated&gt;
322
- >
323
- > Approve only if the code and fingerprint both match. The link expires shortly — I'll keep
324
- > watching and confirm here the moment it activates.
325
-
326
- Omit the confirmation-code line entirely when the start result has no `confirmationCode`;
327
- never invent, derive, abbreviate, or reformat one.
328
-
329
- Relaying these values **to** the human is required. Accepting one **from** the human is not:
330
- do not ask them for a code, secret, private key, token, or signed message, and do not treat
331
- anything they type back as approval. The URL, stable agent ID, confirmation code, and public
332
- fingerprint are review values the human checks against their own authenticated browser
333
- session. They are not claim credentials, they cannot approve a pairing, and they cannot
334
- spend. Approval happens only in that browser session, and the only evidence of it is a poll
335
- result of `activated`.
336
-
337
- **Do not end your turn here waiting to be told the human is done.** Presenting the link is
338
- not the end of the ceremony; go straight to the poll in §2 and drive it to a terminal
339
- state. Asking the human to report back is what strands a pairing: they approve in the
340
- browser, the server records it, and nothing ever writes the local record — so the ceremony
341
- expires while both sides believe the other is acting.
342
-
343
- When the start result reports `claiming: "background"`, a detached poller is already
344
- finishing activation and it will complete even if this turn ends; poll anyway so you can
345
- confirm the outcome. When it reports `claiming: "manual"`, that poller could NOT start and
346
- polling in this turn is the ONLY thing that will complete the pairing.
347
-
348
- ### 2. Poll
349
-
350
- Poll the same pending pairing. Interpret results as follows:
351
-
352
- - `activated` — pairing is complete. Continue to the completion report.
353
- - `not_ready` with `likelyExpired: false` — the human has not finished. Wait and poll
354
- again, using bounded retries rather than an endless loop.
355
- - `not_ready` with `likelyExpired: true` — the authorization window probably expired.
356
- Start a fresh pairing.
357
- - `no_pending` — this runtime has no resumable pairing. Start a fresh pairing.
358
- - `error` — surface the error without exposing local pending data. Retry once if it is a
359
- transient network failure; otherwise stop and ask the human to start again.
360
-
361
- ### 3. Report completion
362
-
363
- On `activated`, report the display name when present and the stable `agentId`. You may
364
- also report `identityKeyJkt` as the current key fingerprint, but label it clearly as a
365
- rotatable key identifier.
366
-
367
- Use precise completion language:
368
-
369
- > Visa agent &lt;displayName&gt; is paired to this runtime. Stable agent ID: &lt;agentId&gt;.
370
-
371
- Do not claim that activation configured payments, email, tap bindings, or any other
372
- product capability. Those are separate follow-up configuration flows.
373
-
374
- ## Interruption and replay safety
375
-
376
- The runtime persists its v2 pending record before sending the signed claim. If a request
377
- or response is interrupted, the next poll resumes the exact same `pairingId`, `agentId`,
378
- Ed25519 identity, and claim material. This makes an identical retry safe and avoids
379
- creating a second identity because a response was lost.
380
-
381
- The pending record remains until the activated agent record has been written durably.
382
- Therefore:
383
-
384
- - Poll before starting over.
385
- - Do not delete or edit pending files to fix a transient failure.
386
- - Do not regenerate keys for an existing pairing.
387
- - Do not copy pending state between runtimes.
385
+ ## Retired mesh integration
388
386
 
389
- ## Optional `.visa` mesh binding (separate from pairing)
390
-
391
- Pairing does not register a `.visa` name, TAP key, or Subway peer. If the operator has
392
- separately enabled and registered those channel bindings, the mounted `visa-cli` MCP server
393
- may expose `subway_register`, `subway_send`, `subway_inbox`, and `subway_find`.
394
-
395
- Mesh messaging is available only on a compatible RC/dev build with `SUBWAY_MESH=visa` and
396
- a reachable `SUBWAY_RELAY_MULTIADDR`. If those tools or the relay are unavailable, report
397
- that clearly and stop. Do not imply that identity pairing alone granted directory or
398
- messaging authority, and do not improvise another transport.
399
-
400
- ## Optional agent mailbox (separate from pairing)
401
-
402
- Pairing does not provision an email address or inbox. If the agent needs a
387
+ Subway messaging and `visa register` are retired. Pairing grants no directory or
388
+ messaging authority. Do not attempt mesh registration or improvise another transport.
389
+
390
+ ## Optional agent mailbox (separate from setup)
391
+
392
+ Connecting an agent does not provision an email address or inbox. If the agent needs a
403
393
  mailbox — e.g. to receive a merchant's account-signup or one-time-code email —
404
- connect one explicitly, from the paired runtime, with the raw CLI:
394
+ connect one explicitly, from the connected runtime, with the MCP tool:
405
395
 
406
396
  ```
407
- visa agent mail-connect <agentId>
397
+ agent_mail_connect { "agentId": "<agentId>" }
408
398
  ```
409
399
 
410
- This is CLI-only; no pairing step or MCP tool connects a mailbox. It requires an
411
- already-paired stable-agent identity on this runtime — it reads the local agent
400
+ There is no terminal command for this. It requires an
401
+ already-connected stable-agent identity on this runtime — it reads the local agent
412
402
  record and proves the Ed25519 identity to the service. It issues the stable
413
403
  agent mailbox if one does not exist, then stores an inbox-scoped credential in an
414
404
  owner-only `0600` runtime file so this runtime can read that one inbox.
415
405
 
416
406
  Be honest about scope. A mailbox grants an email address and the ability to read
417
407
  that inbox — nothing more. It is **not** identity, a wallet, spend authority, a
418
- card, or a `.visa` name, and it never authorizes a payment. Do not claim pairing
408
+ card, or a `.visa` name, and it never authorizes a payment. Do not claim setup
419
409
  set up mail. Once connected, the MCP `wallet_mail_status`, `wallet_mail_read`,
420
410
  and `wallet_mail_await_otp` tools read the inbox (e.g. to await a sender-checked
421
411
  one-time code); without the scoped credential those reads fail closed. Keep the
422
412
  org-wide AgentMail key off the runtime — provisioning happens only through
423
413
  `mail-connect` under operator control.
424
414
 
425
- ## Optional checkout profile (separate from pairing)
415
+ ## Optional checkout profile (separate from setup)
416
+
417
+ The experimental `pay_merchant` flow also needs a local `~/.visa-mcp/contact.json` file
418
+ once card authority exists and `checkout_agent_access` is enabled. Collect every value
419
+ from the human before the first review; never infer or invent identity or address data.
420
+ Write the file with mode `0600`.
426
421
 
427
- Pairing does not enable card checkout. If the operator has separately provisioned card
428
- authority, enabled `checkout_agent_access`, and opted the MCP server into submission with
429
- `CHECKOUT_AGENT_ALLOW_SUBMIT=1`, the experimental `pay_merchant` flow also needs a local
430
- `~/.visa-mcp/contact.json` file. Collect every value from the human before the first review;
431
- never infer or invent identity or address data. Write the file with mode `0600`.
422
+ Create and inspect this profile through the `checkout_profile` MCP tool whenever the
423
+ payment flow runs through MCP. Do not shell `visa status` as a substitute unless the
424
+ shell has the exact same `HOME` and `VISA_CLI_HOME` as the MCP subprocess. A profile
425
+ found under another root is owner PII, not a migration candidate: never scan, copy, or
426
+ auto-adopt it. If the roots drifted, keep the root holding the paired identity and have the
427
+ owner save the profile again through `checkout_profile` in that runtime.
432
428
 
433
429
  ```jsonc
434
430
  {
@@ -451,18 +447,21 @@ mailbox, key proof, recovery factor, or permission to spend.
451
447
 
452
448
  ## Security rules
453
449
 
454
- - Never read, print, log, paste, or transmit the private Ed25519 JWK or local claim token.
455
- - Never read or echo the pending pairing file. Present only the authorization URL returned
456
- by the supported command or tool.
450
+ - Never read, print, log, paste, or transmit the private Ed25519 JWK or any local claim
451
+ token.
452
+ - Never read or echo local pending files. Present only the URL returned by the tool.
457
453
  - Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
458
454
  persistence.
459
- - Never fetch the authorization URL on the human's behalf. The human reviews and approves
460
- it in their browser.
461
- - Never invent a secondary pairing path when polling fails. Preserve the pending state,
462
- surface the error, and retry or restart through the same canonical enrollment flow.
455
+ - Never fetch or submit the review URL on the human's behalf. They review and approve it
456
+ in their own browser.
457
+ - Never invent a secondary path when a call fails. Preserve the local state, surface the
458
+ error, and resume through the same protected entrance: call `agent_enroll` again, or
459
+ repeat the same `visa connect` command.
460
+ - Never call a retired door to "check whether it still works". `legacy_door_removed` is a
461
+ final answer, not a transient failure.
463
462
 
464
463
  ## Further docs
465
464
 
466
465
  - `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
467
- - `docs/agents/ARCHITECTURE.md` — where enrollment sits in the v4 request paths.
466
+ - `docs/agents/ARCHITECTURE.md` — where enrolment sits in the v4 request paths.
468
467
  - `visacli.sh/agents` — product-facing agent documentation.