@visa/cli 4.1.0-rc.26 → 4.1.0-rc.260
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 +202 -46
- package/dist/checkout-engine/adapters/generic.d.ts +69 -0
- package/dist/checkout-engine/adapters/generic.js +383 -58
- package/dist/checkout-engine/adapters/index.d.ts +4 -1
- package/dist/checkout-engine/adapters/index.js +10 -3
- package/dist/checkout-engine/adapters/shopify.d.ts +80 -0
- package/dist/checkout-engine/adapters/shopify.js +688 -0
- package/dist/checkout-engine/amount.d.ts +15 -0
- package/dist/checkout-engine/amount.js +72 -0
- package/dist/checkout-engine/browser-launch.d.ts +9 -4
- package/dist/checkout-engine/browser-launch.js +19 -4
- package/dist/checkout-engine/browserbase-browser.d.ts +24 -0
- package/dist/checkout-engine/browserbase-browser.js +186 -0
- package/dist/checkout-engine/cli-engine.d.ts +206 -32
- package/dist/checkout-engine/cli-engine.js +772 -216
- package/dist/checkout-engine/confirmed-merchants.d.ts +31 -0
- package/dist/checkout-engine/confirmed-merchants.js +165 -0
- package/dist/checkout-engine/detect.d.ts +1 -1
- package/dist/checkout-engine/detect.js +6 -0
- package/dist/checkout-engine/evidence.d.ts +1 -1
- package/dist/checkout-engine/executor.d.ts +61 -3
- package/dist/checkout-engine/executor.js +550 -144
- package/dist/checkout-engine/hosted-approval.d.ts +69 -9
- package/dist/checkout-engine/hosted-approval.js +211 -21
- package/dist/checkout-engine/index.d.ts +6 -2
- package/dist/checkout-engine/index.js +5 -1
- package/dist/checkout-engine/instrument.d.ts +6 -0
- package/dist/checkout-engine/known-merchants.d.ts +10 -0
- package/dist/checkout-engine/known-merchants.js +38 -0
- package/dist/checkout-engine/live-fill-approval.d.ts +5 -11
- package/dist/checkout-engine/live-fill-approval.js +20 -34
- package/dist/checkout-engine/mandate/card-mandate.d.ts +6 -2
- package/dist/checkout-engine/mandate/card-mandate.js +10 -5
- package/dist/checkout-engine/mandate/mandate-ledger.d.ts +63 -23
- package/dist/checkout-engine/mandate/mandate-ledger.js +124 -17
- package/dist/checkout-engine/receipt-dir.d.ts +6 -0
- package/dist/checkout-engine/receipt-dir.js +8 -0
- package/dist/checkout-engine/receipt.d.ts +56 -2
- package/dist/checkout-engine/receipt.js +55 -16
- package/dist/checkout-engine/shopify-primary-domain.d.ts +25 -0
- package/dist/checkout-engine/shopify-primary-domain.js +96 -0
- package/dist/checkout-engine/trace-handles.d.ts +8 -0
- package/dist/checkout-engine/trace-handles.js +12 -0
- package/dist/checkout-engine/types.d.ts +15 -2
- package/dist/checkout-engine/unresolved-charges.d.ts +34 -0
- package/dist/checkout-engine/unresolved-charges.js +134 -0
- package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +26 -7
- package/dist/checkout-engine/vgs-gateway/server-mint-client.js +43 -3
- package/dist/checkout-engine/vgs-live-instrument.d.ts +11 -35
- package/dist/checkout-engine/vgs-live-instrument.js +14 -74
- package/dist/checkout-engine/vic-confirmation.d.ts +18 -0
- package/dist/checkout-engine/vic-confirmation.js +9 -3
- package/dist/checkout-engine/web-bot-auth.d.ts +98 -0
- package/dist/checkout-engine/web-bot-auth.js +218 -0
- package/dist/cli.js +899 -387
- package/dist/mcp-apps/ucp-checkout.html +280 -0
- package/dist/mcp-server/index.js +725 -254
- package/dist/merchant-ucp-mcp/index.js +6 -0
- package/dist/skills/pair-visa-agent/RUNTIMES.md +122 -79
- package/dist/skills/pair-visa-agent/SKILL.md +433 -318
- package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +407 -0
- package/dist/skills/pair-visa-agent/scripts/setup.mjs +310 -30
- package/dist/skills/visa-shopify-checkout/SKILL.md +122 -0
- package/dist/skills/visa-shopify-checkout/references/evidence-and-states.md +37 -0
- package/dist/skills/visa-ucp-shopping/SKILL.md +86 -0
- package/dist/subway-direct.mjs +1 -0
- package/install.ps1 +7 -6
- package/install.sh +3 -3
- package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
- package/package.json +31 -28
- package/server.json +4 -4
- package/dist/checkout-engine/inline-target.d.ts +0 -13
- package/dist/checkout-engine/inline-target.js +0 -37
- package/dist/checkout-engine/pay-args.d.ts +0 -14
- package/dist/checkout-engine/pay-args.js +0 -44
- package/dist/checkout-engine/pay.d.ts +0 -1
- package/dist/checkout-engine/pay.js +0 -13
- package/dist/checkout-engine/repo-env.d.ts +0 -11
- package/dist/checkout-engine/repo-env.js +0 -23
- package/dist/checkout-engine/run-live-fill.d.ts +0 -1
- package/dist/checkout-engine/run-live-fill.js +0 -493
- package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
- package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pair-visa-agent
|
|
3
|
-
description:
|
|
4
|
-
compatibility: Needs the `visa` CLI (`npm i -g @visa/cli@rc`) — run `node scripts/setup.mjs` to
|
|
3
|
+
description: Connect a Visa CLI v4 agent — identity plus every payment rail it needs — through ONE setup operation the human approves once in their browser. Ask what to call the agent, call setup_start, relay the review link, then poll setup_status until it is ready. 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.
|
|
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
|
|
@@ -15,7 +15,7 @@ metadata:
|
|
|
15
15
|
# until 4.1.0 is promoted to latest.
|
|
16
16
|
openclaw:
|
|
17
17
|
user-invocable: true
|
|
18
|
-
emoji: '
|
|
18
|
+
emoji: '🔐'
|
|
19
19
|
requires:
|
|
20
20
|
bins:
|
|
21
21
|
- visa
|
|
@@ -27,80 +27,96 @@ metadata:
|
|
|
27
27
|
- visa-cli
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# Connect a Visa agent
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
ever typed back into you** — the code is an out-of-band check the human reads to confirm
|
|
36
|
-
the page they are on is the flow you started.
|
|
32
|
+
One setup operation carries everything: this runtime's identity **and** every payment
|
|
33
|
+
rail the agent needs, behind a **single** owner approval on a single review page. You ask
|
|
34
|
+
once, the human clicks Connect once, and you finish the rest yourself.
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
OpenClaw, Hermes, or any agent runtime that can run the `visa` CLI or mount its MCP
|
|
40
|
-
server (see Runtimes). Use whichever pairing surface your runtime exposes; they all wrap
|
|
41
|
-
the same two `visa` commands:
|
|
36
|
+
**The whole flow is two tool calls and, for the human, one or two clicks.**
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
```
|
|
39
|
+
setup_start {"name": "<what they call it>", "rails": ["card", "wallet"]}
|
|
40
|
+
→ relay the returned browserUrl to the human
|
|
41
|
+
→ human clicks "Connect agent" (click 1)
|
|
42
|
+
→ for the wallet rail, one "Approve" (click 2 — same page, no second budget)
|
|
43
|
+
setup_status (repeat at the returned pollAfterMs)
|
|
44
|
+
→ each call drives the agent's own steps; stop when nextAction.kind is "done"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Nothing else. Do **not** walk `enroll_agent` → `agent_connect` → `agent_connect_poll` →
|
|
48
|
+
`start_card_mandate` for a new agent: that legacy sequence spends three extra owner
|
|
49
|
+
approvals reaching the same place, and this skill replaces it.
|
|
50
|
+
|
|
51
|
+
## What "connected" means here
|
|
52
|
+
|
|
53
|
+
`setup_start` mints a **new** agent: a new server-assigned agent, a new device-held Ed25519
|
|
54
|
+
identity key, and whichever rails you asked for. When the operation reports ready:
|
|
55
|
+
|
|
56
|
+
- The runtime keeps the private Ed25519 key and sends only the public JWK.
|
|
57
|
+
- The rails in `readiness.rails` are the ones the owner actually approved. A rail you did
|
|
58
|
+
not request is not configured, and pairing has never implied one.
|
|
59
|
+
- An email address, a `.visa` mesh name, and TAP bindings remain separate, later
|
|
60
|
+
configuration. Do not infer them from a finished setup.
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
**Where the `agentId` comes from — read this before you quote one.** The setup status body
|
|
63
|
+
carries `operationId`, `state`, `readiness`, `nextAction`, `pollAfterMs`, `correlationId`
|
|
64
|
+
and `agent: {name, runtime?, device?}`. It does **not** carry `agentId`, and it never
|
|
65
|
+
carries `identityKeyJkt` (that is a field of the older pairing flow). `agentId` appears on
|
|
66
|
+
exactly one result: the `setup_status` call that performed the activation, which returns it
|
|
67
|
+
alongside `walked`. If you need the id and that call is not the one in front of you — the
|
|
68
|
+
background watcher often finishes the last rung — read it from `get_status` or
|
|
69
|
+
`agent_capabilities` rather than guessing. **Never substitute the `so_…` operation id or a
|
|
70
|
+
`correlationId` for an `agentId`:** they are different things, and feeding an operation id
|
|
71
|
+
to a grant ceremony fails in a way that looks like a Visa outage.
|
|
72
|
+
|
|
73
|
+
**`setup_start` cannot add a rail to an agent that already exists** — it always creates a
|
|
74
|
+
new one. To give an already-connected agent another rail, see "Adding a rail to an agent
|
|
75
|
+
that already exists" near the end.
|
|
49
76
|
|
|
50
77
|
## Getting this skill
|
|
51
78
|
|
|
52
|
-
The skill ships
|
|
53
|
-
|
|
79
|
+
The skill ships inside the public `@visa/cli` npm package. No clone of the private
|
|
80
|
+
monorepo is required:
|
|
54
81
|
|
|
82
|
+
```sh
|
|
83
|
+
npm install -g @visa/cli@rc
|
|
84
|
+
visa agent skill
|
|
55
85
|
```
|
|
56
|
-
npm install -g @visa/cli@rc # public npm — puts `visa` on PATH
|
|
57
|
-
visa agent skill # plants this skill into your runtime's skills dir
|
|
58
|
-
```
|
|
59
86
|
|
|
60
|
-
`visa agent skill` auto-detects OpenClaw
|
|
61
|
-
project-local `./.agents/skills
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
3. **Pair** — run the Core flow below. This binds a `.visa` identity + delegated wallet + card
|
|
94
|
-
to this device.
|
|
95
|
-
4. **Transact** — once paired, use the mounted tools (see "What you can do once paired").
|
|
96
|
-
|
|
97
|
-
## Runtimes
|
|
98
|
-
|
|
99
|
-
One artifact, both runtimes. The `visa` CLI is the portable substrate; mount its MCP
|
|
100
|
-
server so the runtime exposes the tools (both use the same server entrypoint — replace
|
|
101
|
-
`<npm root -g>` with the output of `npm root -g`):
|
|
102
|
-
|
|
103
|
-
**OpenClaw** (`~/.openclaw/openclaw.json`):
|
|
87
|
+
`visa agent skill` auto-detects OpenClaw, Hermes, Claude Code, and Codex, falling back to
|
|
88
|
+
the project-local `./.agents/skills`. Pass `--runtime <name>` or `--dir <path>` to choose a
|
|
89
|
+
target, `--force` to overwrite, or `--print` to read without writing. Reload or restart
|
|
90
|
+
the agent runtime after installation so it registers the skill.
|
|
91
|
+
|
|
92
|
+
Access remains enforced by the Visa service. Installing the public package or skill does
|
|
93
|
+
not authorize an account to pair.
|
|
94
|
+
|
|
95
|
+
OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
|
|
96
|
+
|
|
97
|
+
## Getting set up
|
|
98
|
+
|
|
99
|
+
1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
|
|
100
|
+
idempotent provisioner `node scripts/setup.mjs`. The `@latest` tag may not yet expose
|
|
101
|
+
the v4 setup tools. Installation puts `visa` and `visa-cli` on `PATH` and includes
|
|
102
|
+
`@visa/cli/dist/mcp-server/index.js`.
|
|
103
|
+
2. **Mount the MCP server when the runtime supports MCP.**
|
|
104
|
+
- **OpenClaw:** installing `@visa/visa-cli-openclaw` auto-mounts the server by writing
|
|
105
|
+
`mcp.servers["visa-cli"]` in `~/.openclaw/openclaw.json`.
|
|
106
|
+
- **Hermes or another supported runtime:** run `visa-cli connect hermes` or
|
|
107
|
+
`visa-cli connect <runtime>`. For Hermes, the entry is written under `mcp_servers`
|
|
108
|
+
in `~/.hermes/config.yaml`.
|
|
109
|
+
- **No MCP integration:** use the raw `visa setup …` commands.
|
|
110
|
+
3. **Sign the owner in** when the setup includes the wallet rail — see "Sign in first"
|
|
111
|
+
below.
|
|
112
|
+
4. **Connect.** Follow the core flow below.
|
|
113
|
+
|
|
114
|
+
## MCP mounting examples
|
|
115
|
+
|
|
116
|
+
Both runtimes use the same server entrypoint. Replace `<npm root -g>` with the output of
|
|
117
|
+
`npm root -g`.
|
|
118
|
+
|
|
119
|
+
OpenClaw (`~/.openclaw/openclaw.json`):
|
|
104
120
|
|
|
105
121
|
```json
|
|
106
122
|
{
|
|
@@ -115,288 +131,387 @@ server so the runtime exposes the tools (both use the same server entrypoint —
|
|
|
115
131
|
}
|
|
116
132
|
```
|
|
117
133
|
|
|
118
|
-
|
|
134
|
+
Hermes (`~/.hermes/config.yaml`). **Hermes passes ONLY this `env:` map to the MCP
|
|
135
|
+
subprocess — it does NOT inherit the gateway environment.** Omitting a required variable
|
|
136
|
+
(an RC access code, the right `HOME`, `PATH`) makes the server exit on every start while
|
|
137
|
+
`agent_capabilities` — which reads on-disk grant state, not live tool registration — can
|
|
138
|
+
still report rails as available. Always set the map explicitly:
|
|
119
139
|
|
|
120
140
|
```yaml
|
|
121
141
|
mcp_servers:
|
|
122
142
|
visa-cli:
|
|
123
143
|
command: node
|
|
124
144
|
args: ['<npm root -g>/@visa/cli/dist/mcp-server/index.js']
|
|
145
|
+
# Hermes does NOT inherit the gateway env. This map is the entire
|
|
146
|
+
# subprocess environment; omit VISA_RC_CODE and the server exits on boot.
|
|
147
|
+
env:
|
|
148
|
+
HOME: /home/<user> # the home that holds this runtime's .visa-cli state
|
|
149
|
+
VISA_RC_CODE: <access code>
|
|
150
|
+
PATH: /usr/local/bin:/usr/bin:/bin
|
|
125
151
|
```
|
|
126
152
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
Hermes also loads skills **per profile** from `~/.hermes/profiles/<profile>/skills/`, not
|
|
154
|
+
from `~/.hermes/skills/`. `visa agent skill --runtime hermes` resolves this automatically:
|
|
155
|
+
it targets the single profile when exactly one exists (or the one named by
|
|
156
|
+
`HERMES_PROFILE`), and **fails loudly** on a multi-profile box instead of planting into
|
|
157
|
+
the flat dir nothing reads — pass `--dir ~/.hermes/profiles/<profile>/skills` to choose.
|
|
158
|
+
|
|
159
|
+
**Hermes sanitizes MCP server names when registering tools.** A server declared
|
|
160
|
+
`visa-cli` in `mcp_servers:` registers its tools as `mcp__visa_cli__<tool>` — with an
|
|
161
|
+
UNDERSCORE, not the declared hyphen. Anything that hardcodes `mcp__visa-cli__<tool>` gets
|
|
162
|
+
`unknown tool` on every call while looking correct in review. Read tool names off the
|
|
163
|
+
live registry; never derive them from the config key.
|
|
164
|
+
|
|
165
|
+
In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
|
|
166
|
+
existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
|
|
167
|
+
|
|
168
|
+
## Sign in first — the wallet rail is owner-bound
|
|
169
|
+
|
|
170
|
+
The USDC wallet rail is delegated out of the owner's own wallet, so this runtime needs a
|
|
171
|
+
live owner session before that leg can run. Establish it **before** calling `setup_start`
|
|
172
|
+
with `"wallet"` in `rails`:
|
|
173
|
+
|
|
174
|
+
- [ ] Call `agent_login` (MCP, default action `"start"`) or run
|
|
175
|
+
`visa agent login --format json` (CLI). The result carries a sign-in `browserUrl`
|
|
176
|
+
and a short 6-character `confirmCode`.
|
|
177
|
+
- [ ] Relay **both** to the human in your reply — the bare URL on its own line, and the
|
|
178
|
+
code. You are very often not in a terminal they can see; the chat message is the
|
|
179
|
+
only place these values reach them.
|
|
180
|
+
- [ ] The human opens the link, signs in (Google or email), and **types the confirmation
|
|
181
|
+
code into the sign-in page** — into the browser, never back to you in chat.
|
|
182
|
+
- [ ] Claim the session: `agent_login {"action":"claim"}` (the CLI command polls on its
|
|
183
|
+
own). Once claimed, the session token is stored locally.
|
|
184
|
+
|
|
185
|
+
If a wallet leg reports `{"code":"session_required"}` or "Not logged in", that is this
|
|
186
|
+
ordering rule, not a fault: run `agent_login`, drive the sign-in above to a claimed
|
|
187
|
+
session, then call `setup_status` again — the walk resumes where it stopped.
|
|
188
|
+
|
|
189
|
+
The account that signs in is the owner the setup binds to, and the same account must be
|
|
190
|
+
signed in on the review page. A different account there fails closed with
|
|
191
|
+
`owner_profile_mismatch`. The **card-only** setup does not need this leg first.
|
|
192
|
+
|
|
193
|
+
## Core flow
|
|
194
|
+
|
|
195
|
+
- [ ] **Ask what to call the agent.** `name` is required, and it is what the human sees on
|
|
196
|
+
the approval page. Do not invent one.
|
|
197
|
+
- [ ] **Ask which rails**, unless they already said. `"card"` is the Visa card rail,
|
|
198
|
+
`"wallet"` is the USDC (x402) rail; `rails` defaults to `["card"]`. Both in one
|
|
199
|
+
request costs the owner no extra approval — they share one budget.
|
|
200
|
+
- [ ] **Call `setup_start`** with the name and rails. It mints the identity key, creates
|
|
201
|
+
the operation, and opens the owner's browser at the review page.
|
|
202
|
+
- [ ] **Relay `browserUrl` to the human**, with `compareCode` when the result carries one
|
|
203
|
+
(see below). Never open it "for them" in place of showing it, and never approve.
|
|
204
|
+
- [ ] **Call `setup_status`** at the returned `pollAfterMs`, repeatedly. Each call also
|
|
205
|
+
executes the runtime's own steps — this is how the agent gets connected, so a setup
|
|
206
|
+
you never poll is a setup that never finishes.
|
|
207
|
+
- [ ] **Render `nextAction.label` verbatim** to the human each time it changes.
|
|
208
|
+
- [ ] **Stop when `nextAction.kind` is `done`** (or any terminal state). Then report.
|
|
209
|
+
|
|
210
|
+
### The one rule for reading a status
|
|
211
|
+
|
|
212
|
+
**`nextAction` is the server's decision. Render it; never compute your own.** Every
|
|
213
|
+
surface — this runtime, the review page, the Console — says the same sentence about the
|
|
214
|
+
same operation because they all render this one field. `nextAction.actor` tells you whose
|
|
215
|
+
turn it is:
|
|
216
|
+
|
|
217
|
+
- `actor: "agent"` — **yours**. Keep calling `setup_status`; it performs the step. Do not
|
|
218
|
+
ask the human for anything, and do not end your turn waiting to be told they are done.
|
|
219
|
+
- `actor: "human"` — **theirs**. Show `nextAction.label`, plus `nextAction.url` when the
|
|
220
|
+
action carries one. Then keep polling: they act in the browser, not in chat.
|
|
221
|
+
- `actor: "none"` — finished (`kind: "done"`) or terminal. Stop polling and report.
|
|
222
|
+
|
|
223
|
+
Honour `pollAfterMs` (`0` means stop) and `nextAction.afterAction` (`"poll"`, `"stop"`,
|
|
224
|
+
`"open_url_again"`). An agent that honours both cannot spin or give up early.
|
|
225
|
+
|
|
226
|
+
### Relaying the link and the compare code
|
|
227
|
+
|
|
228
|
+
The review page shows a short code to compare **unless** it can tell by machine that the
|
|
229
|
+
agent asking is the one that opened it — in which case it says "Opened from this device by
|
|
230
|
+
the agent that asked" and shows no code. Which of the two happens is not knowable when you
|
|
231
|
+
call `setup_start`: the page has not been opened yet, so the server carries no verdict, and
|
|
232
|
+
neither does this runtime (launching a browser is not proof one loaded it).
|
|
233
|
+
|
|
234
|
+
So the rule is: **relay `compareCode` whenever the result carries one, and phrase it as a
|
|
235
|
+
condition.** Never announce that there will be no code — being wrong that way leaves the
|
|
236
|
+
human staring at an anti-phishing code with nothing to check it against. The tool result's
|
|
237
|
+
`message` is already written this way; prefer it verbatim.
|
|
238
|
+
|
|
239
|
+
> 🔐 Connect your Visa agent — open this page to review and approve:
|
|
240
|
+
>
|
|
241
|
+
> 👉 <browserUrl, bare and on its own line>
|
|
242
|
+
>
|
|
243
|
+
> If the page shows a code, check it matches this one and don't approve if it differs:
|
|
244
|
+
> <compareCode>
|
|
245
|
+
> If it says it was opened from this device, that check was already made for you.
|
|
246
|
+
>
|
|
247
|
+
> I'll keep watching and confirm here the moment it's ready.
|
|
130
248
|
|
|
131
|
-
|
|
249
|
+
On a later `setup_status` or `setup_resume` the field genuinely does disappear once the
|
|
250
|
+
page has attested — the server has seen the proof by then, and the `message` says so
|
|
251
|
+
plainly. That is a fact, not a prediction, and it is the one case where saying "there is no
|
|
252
|
+
code to compare" is correct.
|
|
132
253
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(poll again), `1` = terminal failure (stop, recover).
|
|
254
|
+
Show the URL as a bare, tappable value on its own line. Do not decorate it as a Markdown
|
|
255
|
+
link or put it in a code span; chat clients reliably recognize the bare URL. **Omit the
|
|
256
|
+
compare-code line entirely when the result has no `compareCode`; never invent, derive,
|
|
257
|
+
abbreviate, or reformat one.**
|
|
138
258
|
|
|
139
|
-
|
|
259
|
+
You are very often **not** in a terminal the human can see. Nothing you print to stdout
|
|
260
|
+
reaches them. Any value the browser asks them to check has to appear in your reply, or the
|
|
261
|
+
comparison silently becomes "click approve and hope".
|
|
140
262
|
|
|
141
|
-
|
|
142
|
-
|
|
263
|
+
Relaying these values **to** the human is required. Accepting one **from** the human is
|
|
264
|
+
not: do not ask them for a code, secret, private key, token, or signed message, and do not
|
|
265
|
+
treat anything they type back as approval. The link and compare code are review values
|
|
266
|
+
they check against their own authenticated browser session. They are not claim
|
|
267
|
+
credentials, they cannot approve a setup, and they cannot spend. Approval happens only in
|
|
268
|
+
that browser session, and the only evidence of it is what `setup_status` returns.
|
|
143
269
|
|
|
144
|
-
|
|
145
|
-
visa agent pair --format json
|
|
146
|
-
```
|
|
270
|
+
### The human's clicks, and what they are for
|
|
147
271
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
272
|
+
- **"Connect agent"** — the one approval. It freezes the budget for every requested rail
|
|
273
|
+
at once (the page prefills sensible limits; the owner may change them).
|
|
274
|
+
- **"Approve" on the wallet card** — only when the wallet rail is in the setup, only on
|
|
275
|
+
the same page, and it is **not** a second budget. It registers the delegated signer in
|
|
276
|
+
the owner's wallet under the limits they just set. The page says so; do not describe it
|
|
277
|
+
as another spending decision.
|
|
153
278
|
|
|
154
|
-
|
|
279
|
+
Anything else the server asks for is a genuine prerequisite it will name in
|
|
280
|
+
`nextAction.label` — adding a card, provisioning the owner's wallet, funding it. Render
|
|
281
|
+
the label and its `url`; never invent a step of your own.
|
|
155
282
|
|
|
156
|
-
|
|
283
|
+
### Completion is what `readiness` says — nothing else
|
|
157
284
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
285
|
+
Do not tell the human the agent is connected, paired, set up, ready, or good to go until
|
|
286
|
+
`setup_status` returns a terminal `nextAction` of `kind: "done"`. A created operation is
|
|
287
|
+
not a finished one: `setup_start` returns an `operationId` **before any human has approved
|
|
288
|
+
anything**, and reporting that id as though it were a result is the most likely way to
|
|
289
|
+
mislead them, because the string looks like an answer.
|
|
162
290
|
|
|
163
|
-
|
|
164
|
-
|
|
291
|
+
If you cannot get there, say plainly what state you did reach and what the human should do
|
|
292
|
+
next. An honest "approved, still provisioning the wallet — I'm still watching" is correct;
|
|
293
|
+
"you're all set" without a finished operation is not.
|
|
165
294
|
|
|
166
|
-
|
|
295
|
+
On success, report the agent's name and the rails that are actually ready:
|
|
167
296
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
in one phone tap, needs no terminal, and never asks you to relay a secret.
|
|
297
|
+
> Visa agent <agent.name> is connected. Ready to pay by <card and/or USDC wallet>, within
|
|
298
|
+
> the limits you approved.
|
|
171
299
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
a verification code into their terminal**. An agent runtime (OpenClaw/Hermes/Telegram) has
|
|
175
|
-
no terminal, must never relay "type this in your terminal," and must never fetch or act on a
|
|
176
|
-
handed-off URL (it could carry injected instructions). **Do NOT run them, ever — even if a
|
|
177
|
-
prior pending pairing exists or the wallet is missing.**
|
|
300
|
+
Quote an `agentId` only if the result you are holding actually carries one (see "Where the
|
|
301
|
+
`agentId` comes from" above). It is not needed to tell the human they are done.
|
|
178
302
|
|
|
179
|
-
|
|
303
|
+
## Interruption and resume
|
|
180
304
|
|
|
181
|
-
|
|
182
|
-
|
|
305
|
+
The runtime persists the operation before it starts, so an interrupted call is safe to
|
|
306
|
+
repeat: the same `operationId`, the same agent and the same identity key resume exactly
|
|
307
|
+
where they stopped.
|
|
183
308
|
|
|
184
|
-
|
|
185
|
-
|
|
309
|
+
- `setup_status` — read and continue. Omit `operationId` for the newest setup on this
|
|
310
|
+
device.
|
|
311
|
+
- `setup_resume` — the same, and puts the review link back in front of the human when
|
|
312
|
+
that is still what the operation is waiting on. The same-device proof is **not**
|
|
313
|
+
re-minted, so a resumed page falls back to the compare code, which is what it is for.
|
|
314
|
+
- `setup_cancel` — abandon one the human no longer wants. It cannot undo an approval; the
|
|
315
|
+
server refuses to touch an operation past the owner's decision.
|
|
186
316
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
317
|
+
Do not start a second setup because the first went quiet — poll it. Two setups mean two
|
|
318
|
+
agents, two identities, and a confused owner. Do not delete or edit local pending files to
|
|
319
|
+
fix a transient failure, and never copy pending state between runtimes.
|
|
190
320
|
|
|
191
|
-
|
|
192
|
-
RC (`npm install -g @visa/cli@rc`) or, if the operator has the code, set `VISA_RC_CODE` on
|
|
193
|
-
the host. Then retry the hand-off.
|
|
321
|
+
## Raw CLI equivalent
|
|
194
322
|
|
|
195
|
-
|
|
323
|
+
If MCP is unavailable, the same ceremony runs from the shell. The name is a positional
|
|
324
|
+
argument:
|
|
196
325
|
|
|
197
|
-
|
|
326
|
+
```
|
|
327
|
+
visa setup start "<name>" --rails card,wallet --format json
|
|
328
|
+
visa setup status [operationId] --format json
|
|
329
|
+
visa setup open [operationId] --format json
|
|
330
|
+
visa setup cancel [operationId] --format json
|
|
331
|
+
visa setup list --format json
|
|
332
|
+
```
|
|
198
333
|
|
|
199
|
-
|
|
334
|
+
`visa setup status` drives the agent's steps exactly as the MCP tool does — run it until
|
|
335
|
+
the operation is finished. Prefer structured output and parse it; never scrape prose.
|
|
336
|
+
|
|
337
|
+
## The human already created this agent in the Console
|
|
338
|
+
|
|
339
|
+
When the human says they created the agent on the Visa Console and holds a one-time claim
|
|
340
|
+
code, redeem the code instead of starting a new setup — the code carries server-held,
|
|
341
|
+
pre-approved terms.
|
|
342
|
+
|
|
343
|
+
The MCP tool `agent_handoff_claim` exists only on `@visa/cli` **4.1.0-rc.159 and newer**
|
|
344
|
+
— on an older CLI it is absent from the served tool list and the only path is shelling
|
|
345
|
+
`visa agent handoff-claim <code> --format json`, a different integration with different
|
|
346
|
+
failure modes (the approval URL and verification code arrive mid-run as a structured
|
|
347
|
+
stderr frame). Never assume the tool from documentation alone: check the served list.
|
|
348
|
+
In a handoff claim the card auto-activates from the mint consent, and the
|
|
349
|
+
wallet still needs one browser approval. Console handoff codes are owner-pinned by the
|
|
350
|
+
code itself and do NOT require an active owner session.
|
|
351
|
+
|
|
352
|
+
If they created it in the Console but have **no** code, still call `setup_start`: it
|
|
353
|
+
adopts the request they already made — their Create click was the approval — rather than
|
|
354
|
+
asking them to approve a second time.
|
|
355
|
+
|
|
356
|
+
## Already connected — do NOT connect again
|
|
357
|
+
|
|
358
|
+
If this device already holds an agent, a fresh setup is not needed and creates a
|
|
359
|
+
_second, separate_ agent. Do this instead:
|
|
360
|
+
|
|
361
|
+
1. **Tell the user plainly:** "This device is already connected as `<name>.visa`." Read
|
|
362
|
+
the name from the response. Start another setup only if they explicitly want a second
|
|
363
|
+
agent.
|
|
364
|
+
2. **Report status honestly — "connected" is several separate things.** Never imply the
|
|
365
|
+
agent can spend just because it is connected. Read it live from tools rather than
|
|
366
|
+
guessing from prose: `agent_capabilities` returns the DERIVED capability map (the
|
|
367
|
+
identity + wallet + mail base plus card/tap/subway availability), `get_status` reports
|
|
368
|
+
enrollment / account / version, and `agent_login` establishes or confirms the account
|
|
369
|
+
session.
|
|
370
|
+
- **Identity** — connected (`.visa` name bound to _this user's_ account).
|
|
371
|
+
- **Spending** — the rails the owner actually approved. The Turnkey **wallet:x402**
|
|
372
|
+
rail (stablecoin) is approved with the owner's **sign-in session** — **no passkey**.
|
|
373
|
+
The **card:vic** rail is approved on the **v4 agent dashboard**
|
|
374
|
+
(`app.visacli.sh/agent/enroll`), never any account-settings page (that legacy surface
|
|
375
|
+
is retired). You **cannot** self-grant either, and never self-mint a wallet with
|
|
376
|
+
`wallet_init` on mainnet — it throws `WalletCredentialRequiredError` until the
|
|
377
|
+
owner's delegation lands.
|
|
378
|
+
- **Mesh (`.visa` messaging)** — separate; `visa register <name>` joins it.
|
|
379
|
+
- **Trusted (TAP)** — follows spend/provisioning; don't promise it before then.
|
|
380
|
+
3. **Scope everything to the user.** The identity is bound to the account they signed in
|
|
381
|
+
with; the wallet and limits are theirs. Speak in terms of "your agent / your account /
|
|
382
|
+
the limits you approved", never a shared identity.
|
|
383
|
+
|
|
384
|
+
## Adding a rail to an agent that already exists
|
|
385
|
+
|
|
386
|
+
`setup_start` always mints a new agent, so it is the wrong tool here. For an
|
|
387
|
+
already-connected agent that needs another rail, run the single-rail grant ceremony
|
|
388
|
+
against its **exact `agentId`** — never a name, never "the most recent one":
|
|
389
|
+
|
|
390
|
+
- [ ] **Establish the owner session** if this runtime does not have one: `agent_login`
|
|
391
|
+
(MCP) or `visa agent login` (CLI). Short-lived, established once — not a re-pair,
|
|
392
|
+
and not something the owner repeats per payment.
|
|
393
|
+
- [ ] **Initiate the grant from MCP.** `agent_connect` with the rail, caps, and the exact
|
|
394
|
+
`agentId`:
|
|
395
|
+
`{"agentId":"<agentId>","rail":"card","ceiling":"<usd>","perTransaction":"<usd>"}`
|
|
396
|
+
(or `"rail":"wallet"`). It returns `{ url, code, attachId, willGrant, expiresAt }`.
|
|
397
|
+
Present the **bare `url` and `code` exactly as returned** — never construct,
|
|
398
|
+
shorten, or guess a Visa URL, and never open it yourself.
|
|
399
|
+
- [ ] **The owner approves once** on the v4 agent dashboard, confirming the caps.
|
|
400
|
+
- [ ] **Poll to activation** with `agent_connect_poll` (`{"attachId":"<from
|
|
401
|
+
agent_connect>"}`) — one bounded poll per call, until
|
|
402
|
+
`{"ok":true,"state":"grant_activated"}`.
|
|
403
|
+
- [ ] **Confirm the rail is live.** Wallet: `wallet_status` / `visa wallet show`
|
|
404
|
+
(`agent_capabilities.wallet.available`). Card: `agent_capabilities.card.available`.
|
|
405
|
+
Only a delegated credential — not the served tool list — means the rail is usable.
|
|
406
|
+
|
|
407
|
+
Shell equivalents, which poll to activation in one call:
|
|
200
408
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
parsing. Add a short "👉 Tap to open on your phone" cue so it reads as an action.
|
|
206
|
-
- **Emphasize the confirm code.** Show `confirmCode` in **bold** or `monospace` — both are
|
|
207
|
-
Telegram-safe (mono needs no escaping). It's read/compared, never clicked.
|
|
208
|
-
- **Relative expiry.** "expires in about 15 minutes" — never the raw `expiresAt` ISO/UTC
|
|
209
|
-
timestamp (e.g. `2026-07-21T21:22:57Z`); a UTC time reads as noise.
|
|
210
|
-
- **Scannable layout.** Short lines, a blank line between blocks, one leading emoji per block.
|
|
211
|
-
- **Don't over-Markdown.** Telegram MarkdownV2 requires escaping `_ * [ ] ( ) ~ > # + - = | { } . !`,
|
|
212
|
-
so heavy formatting risks a broken render. A bold/mono code + a bare link + emojis is plenty —
|
|
213
|
-
keep the rest plain prose.
|
|
409
|
+
```
|
|
410
|
+
visa agent grant-card <agentId> --ceiling <usd> --per-transaction <usd> --wait
|
|
411
|
+
visa agent grant-wallet <agentId> --ceiling <usd> --per-transaction <usd> --wait
|
|
412
|
+
```
|
|
214
413
|
|
|
215
|
-
|
|
414
|
+
A policy refusal or timeout means **nothing was signed**: report it and stop; do not retry
|
|
415
|
+
with a different command or a reconstructed URL. Never raise a human-approved limit
|
|
416
|
+
yourself.
|
|
216
417
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- `subway_inbox` — FREE. Read recent inbound messages (`limit`, `clear` to drain).
|
|
312
|
-
- `subway_find` — resolve a handle to its `.visa` peer.
|
|
313
|
-
|
|
314
|
-
**Gated (be honest with the user):** mesh messaging is live only on an **RC/dev build**
|
|
315
|
-
with `SUBWAY_MESH=visa` **and a reachable relay** (`SUBWAY_RELAY_MULTIADDR`, a Visa Crypto
|
|
316
|
-
Labs deployment). On a stable build the `subway_*` tools aren't exposed; without a relay
|
|
317
|
-
they no-op (`subway_register` reports the binding is "ready for admission once a relay is
|
|
318
|
-
up"). If a user asks to message another `.visa` agent and the mesh isn't wired, say so
|
|
319
|
-
plainly and stop — do not improvise another transport.
|
|
320
|
-
|
|
321
|
-
- **Real-merchant card checkout (experimental, opt-in)** — `pay_merchant` fills and pays an
|
|
322
|
-
ordinary merchant web checkout with a **Verified Agent card credential**: a one-shot
|
|
323
|
-
network-token cryptogram minted **on this device** (non-custodial) — not a stored card,
|
|
324
|
-
not x402, not server-side spend controls. Two steps: `review` (free; returns merchant +
|
|
325
|
-
exact amount as a `reviewId`) then `pay` (requires `confirm: "PAY <reviewId>"` + a passkey,
|
|
326
|
-
and CHARGES). Prerequisites — the tool errors clearly if any is missing:
|
|
327
|
-
1. **`checkout_agent_access`** flag on your account (email-keyed; an admin grants it via
|
|
328
|
-
`PUT /v1/admin/users/<your-enroll-email>/feature-flags/checkout_agent_access` or the
|
|
329
|
-
admin panel). Distinct from the RC/GitHub allowlist.
|
|
330
|
-
2. **`CHECKOUT_AGENT_ALLOW_SUBMIT=1`** in the MCP server's env. This is the submit opt-in:
|
|
331
|
-
WITHOUT it the agent fills the checkout form but **refuses to press the pay button** (the
|
|
332
|
-
default safe posture — `submit:false`), so a checkout silently never completes. Set it on
|
|
333
|
-
the `visa-cli` MCP server entry (e.g. OpenClaw `mcp.servers["visa-cli"].env`, Hermes
|
|
334
|
-
`mcp_servers.visa-cli.env`, or `claude mcp add … -e CHECKOUT_AGENT_ALLOW_SUBMIT=1`). The
|
|
335
|
-
`visa-cli checkout … --submit` CLI flag sets the same opt-in.
|
|
336
|
-
3. An enrolled agent credential (`enroll_agent`) and a `~/.visa-mcp/contact.json`. This file
|
|
337
|
-
supplies the **cardholder name** the credential is minted with AND the billing details
|
|
338
|
-
filled into the merchant form. If it is missing, or `fullName` is empty/whitespace, the
|
|
339
|
-
checkout dies at the final step with `cardholder name is required` (after mandate
|
|
340
|
-
approval — an expensive late failure). **Before the first checkout, ASK the user for
|
|
341
|
-
these fields and write the file yourself** (0600), with a REAL non-blank `fullName`:
|
|
342
|
-
|
|
343
|
-
```jsonc
|
|
344
|
-
// ~/.visa-mcp/contact.json (chmod 600)
|
|
345
|
-
{
|
|
346
|
-
"fullName": "Ada Lovelace", // REQUIRED, non-blank — the cardholder name
|
|
347
|
-
"email": "ada@example.com", // REQUIRED — used by the approval ceremony
|
|
348
|
-
"addressLine1": "1 Analytical Way",
|
|
349
|
-
"addressLine2": "",
|
|
350
|
-
"city": "London",
|
|
351
|
-
"state": "CA", // 2-letter region where applicable
|
|
352
|
-
"postalCode": "94105",
|
|
353
|
-
"country": "US", // 2-letter ISO
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
Exact keys only (`fullName` | `firstName`+`lastName`, `email`, `addressLine1/2`, `city`,
|
|
358
|
-
`state`, `postalCode`, `country`) — the engine reads these names verbatim. Never invent a
|
|
359
|
-
name; if the user won't provide one, stop and say checkout needs a real cardholder name.
|
|
360
|
-
|
|
361
|
-
**RC/preview builds only**, opt-in, never paired-and-go.
|
|
362
|
-
|
|
363
|
-
If a tool you expect isn't visible, the MCP server isn't mounted (or the v4 wallet runtime
|
|
364
|
-
isn't bundled in this build) — go back to "Getting set up".
|
|
365
|
-
|
|
366
|
-
## Important
|
|
367
|
-
|
|
368
|
-
- **The confirmation code is display-only.** Show it; never ask for it; never accept it as
|
|
369
|
-
input. It lets the human verify the page matches the flow you started.
|
|
370
|
-
- **Never read, print, log, or echo** the credential file or the enrollment pending file,
|
|
371
|
-
or the `browserUrl` query values beyond the single presentation to the user. The link
|
|
372
|
-
carries only a hash + a public key — safe in history — but treat it as one-time.
|
|
373
|
-
- Do not run API-key setup, card enrollment, or a balance top-up as a substitute for
|
|
374
|
-
pairing. Once paired, use the `visa-cli` skill's v4 wallet commands to pay.
|
|
375
|
-
|
|
376
|
-
## Limits
|
|
377
|
-
|
|
378
|
-
| Limit | Value |
|
|
379
|
-
| ------------------------------ | ------------------------------------------------------------ |
|
|
380
|
-
| Hand-off validity | 15 minutes from `pair_agent_start` |
|
|
381
|
-
| In-flight hand-offs per device | 1 (a new start replaces the prior) |
|
|
382
|
-
| Claim | single-shot server-side; once claimed the entry is destroyed |
|
|
383
|
-
| Confirmation code | 6 chars, no ambiguous glyphs; display-only |
|
|
384
|
-
|
|
385
|
-
## Errors
|
|
386
|
-
|
|
387
|
-
All errors are JSON with a non-zero exit code; `enroll-claim` tags them with `status`.
|
|
388
|
-
|
|
389
|
-
| status / symptom | Cause | Recovery |
|
|
390
|
-
| ------------------------------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
391
|
-
| `error: verify-web URL not available in stable builds` | Stable CLI build, surface not public | Set `VISA_VERIFY_WEB_URL` or use a preview build. Do not proceed otherwise. |
|
|
392
|
-
| `error: RC build requires access` | The RC employee gate rejected the bootstrap | Upgrade the RC (`npm install -g @visa/cli@rc`) or set `VISA_RC_CODE`, then retry the hand-off. |
|
|
393
|
-
| `no_pending` | No hand-off in flight (never started, or already claimed/expired) | Start fresh with `pair_agent_start`. |
|
|
394
|
-
| `not_ready`, `likelyExpired: false` | User hasn't finished the mobile flow | Wait, tell the user, poll again. Bounded polling only. |
|
|
395
|
-
| `not_ready`, `likelyExpired: true` | 15-minute window elapsed | Start over with `pair_agent_start`. |
|
|
396
|
-
| `confirm_mismatch` | The claim didn't match THIS device's secret | STOP. Show the re-derived `confirmCode`. If the user did not just finish the flow, someone else may hold their link — start over. Never retry blindly. |
|
|
397
|
-
| `error` (network / malformed) | Transport or server error | Surface the message. Poll once more; if it persists, start over. |
|
|
418
|
+
## Spending, once a rail is live
|
|
419
|
+
|
|
420
|
+
Wallet: set the policy with `wallet_policy_set` (per-transaction / daily / session USD
|
|
421
|
+
caps plus optional network and merchant allow/deny lists that refuse an x402 payment
|
|
422
|
+
BEFORE it is signed), then `wallet_pay`. The served wallet tools are `wallet_discover`
|
|
423
|
+
(search the public x402 Bazaar), `wallet_probe` (read a challenge without paying),
|
|
424
|
+
`wallet_pay` / `wallet_directory_pay` (pay, policy-enforced), `wallet_history` /
|
|
425
|
+
`wallet_reconcile` (local ledger + resolve `reconciling` holds), `wallet_fund` (funding
|
|
426
|
+
address + faucet), and `wallet_export` (export key material — dangerous). All spending is
|
|
427
|
+
gated by the owner-approved local policy caps.
|
|
428
|
+
|
|
429
|
+
Card: `start_card_mandate`, then `pay_merchant`. The first card purchase asks the owner
|
|
430
|
+
for a spending mandate within the budget they already approved.
|
|
431
|
+
|
|
432
|
+
## Optional `.visa` mesh binding (separate from setup)
|
|
433
|
+
|
|
434
|
+
Pairing does not register a `.visa` name, TAP key, or Subway peer. If the operator has
|
|
435
|
+
separately enabled and registered those channel bindings, the mounted `visa-cli` MCP server
|
|
436
|
+
may expose `subway_register`, `subway_send`, `subway_inbox`, and `subway_find`.
|
|
437
|
+
|
|
438
|
+
Mesh messaging is available only on a compatible RC/dev build with `SUBWAY_MESH=visa` and
|
|
439
|
+
a reachable `SUBWAY_RELAY_MULTIADDR`. If those tools or the relay are unavailable, report
|
|
440
|
+
that clearly and stop. Do not imply that connecting an agent granted directory or
|
|
441
|
+
messaging authority, and do not improvise another transport.
|
|
442
|
+
|
|
443
|
+
## Optional agent mailbox (separate from setup)
|
|
444
|
+
|
|
445
|
+
Connecting an agent does not provision an email address or inbox. If the agent needs a
|
|
446
|
+
mailbox — e.g. to receive a merchant's account-signup or one-time-code email —
|
|
447
|
+
connect one explicitly, from the connected runtime, with the raw CLI:
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
visa agent mail-connect <agentId>
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
This is CLI-only; no pairing step or MCP tool connects a mailbox. It requires an
|
|
454
|
+
already-connected stable-agent identity on this runtime — it reads the local agent
|
|
455
|
+
record and proves the Ed25519 identity to the service. It issues the stable
|
|
456
|
+
agent mailbox if one does not exist, then stores an inbox-scoped credential in an
|
|
457
|
+
owner-only `0600` runtime file so this runtime can read that one inbox.
|
|
458
|
+
|
|
459
|
+
Be honest about scope. A mailbox grants an email address and the ability to read
|
|
460
|
+
that inbox — nothing more. It is **not** identity, a wallet, spend authority, a
|
|
461
|
+
card, or a `.visa` name, and it never authorizes a payment. Do not claim setup
|
|
462
|
+
set up mail. Once connected, the MCP `wallet_mail_status`, `wallet_mail_read`,
|
|
463
|
+
and `wallet_mail_await_otp` tools read the inbox (e.g. to await a sender-checked
|
|
464
|
+
one-time code); without the scoped credential those reads fail closed. Keep the
|
|
465
|
+
org-wide AgentMail key off the runtime — provisioning happens only through
|
|
466
|
+
`mail-connect` under operator control.
|
|
467
|
+
|
|
468
|
+
## Optional checkout profile (separate from setup)
|
|
469
|
+
|
|
470
|
+
The experimental `pay_merchant` flow also needs a local `~/.visa-mcp/contact.json` file
|
|
471
|
+
once card authority exists and `checkout_agent_access` is enabled. Collect every value
|
|
472
|
+
from the human before the first review; never infer or invent identity or address data.
|
|
473
|
+
Write the file with mode `0600`.
|
|
474
|
+
|
|
475
|
+
Create and inspect this profile through the `checkout_profile` MCP tool whenever the
|
|
476
|
+
payment flow runs through MCP. Do not shell `visa agent preflight` as a substitute unless
|
|
477
|
+
the shell has the exact same `HOME` and `VISA_CLI_HOME` as the MCP subprocess. A profile
|
|
478
|
+
found under another root is owner PII, not a migration candidate: never scan, copy, or
|
|
479
|
+
auto-adopt it. If the roots drifted, keep the root holding the paired identity and have the
|
|
480
|
+
owner save the profile again through `checkout_profile` in that runtime.
|
|
481
|
+
|
|
482
|
+
```jsonc
|
|
483
|
+
{
|
|
484
|
+
"fullName": "Ada Lovelace",
|
|
485
|
+
"email": "ada@example.com",
|
|
486
|
+
"addressLine1": "1 Analytical Way",
|
|
487
|
+
"addressLine2": "",
|
|
488
|
+
"city": "San Francisco",
|
|
489
|
+
"state": "CA",
|
|
490
|
+
"postalCode": "94105",
|
|
491
|
+
"country": "US",
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
`fullName` must be non-blank; `firstName` plus `lastName` is also accepted. The engine reads
|
|
496
|
+
the exact keys `fullName`, `firstName`, `lastName`, `email`, `addressLine1`, `addressLine2`,
|
|
497
|
+
`city`, `state`, `postalCode`, and `country`. The profile supplies checkout/cardholder and
|
|
498
|
+
billing data only. Its `email` value is not the account's verified owner email, an agent
|
|
499
|
+
mailbox, key proof, recovery factor, or permission to spend.
|
|
500
|
+
|
|
501
|
+
## Security rules
|
|
502
|
+
|
|
503
|
+
- Never read, print, log, paste, or transmit the private Ed25519 JWK or any local claim
|
|
504
|
+
token.
|
|
505
|
+
- Never read or echo local pending files. Present only the URL returned by the tool.
|
|
506
|
+
- Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
|
|
507
|
+
persistence.
|
|
508
|
+
- Never fetch or submit the review URL on the human's behalf. They review and approve it
|
|
509
|
+
in their own browser.
|
|
510
|
+
- Never invent a secondary path when a call fails. Preserve the local state, surface the
|
|
511
|
+
error, and resume through `setup_status` / `setup_resume`.
|
|
398
512
|
|
|
399
513
|
## Further docs
|
|
400
514
|
|
|
401
|
-
- `
|
|
402
|
-
- `
|
|
515
|
+
- `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
|
|
516
|
+
- `docs/agents/ARCHITECTURE.md` — where setup sits in the v4 request paths.
|
|
517
|
+
- `visacli.sh/agents` — product-facing agent documentation.
|