@visa/cli 4.1.0-rc.36 → 4.1.0-rc.38
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 +79 -36
- package/dist/cli.js +372 -335
- package/dist/mcp-server/index.js +297 -260
- package/dist/skills/pair-visa-agent/RUNTIMES.md +92 -79
- package/dist/skills/pair-visa-agent/SKILL.md +224 -312
- package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
- package/package.json +2 -2
- package/server.json +3 -3
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pair-visa-agent
|
|
3
|
-
description: Pair a Visa CLI v4 agent identity to this
|
|
4
|
-
compatibility: Needs the `visa` CLI (`npm i -g @visa/cli@rc`) — run `node scripts/setup.mjs` to
|
|
3
|
+
description: Pair a Visa CLI v4 agent identity to this runtime. Present one authorization link 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.
|
|
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.
|
|
9
|
+
version: '0.6.5'
|
|
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,77 @@ metadata:
|
|
|
27
27
|
- visa-cli
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
# Pair a Visa CLI v4
|
|
30
|
+
# Pair a Visa CLI v4 agent identity
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
pairs back **automatically** to the terminal that started it. **No confirmation code is
|
|
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
|
+
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:
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- **`visa agent enroll` / `visa agent enroll-claim`** — the raw CLI, always available if `visa` is on PATH. The universal fallback.
|
|
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.
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
## What pairing establishes
|
|
44
|
+
|
|
45
|
+
Pairing activates an agent identity on this runtime:
|
|
46
|
+
|
|
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.
|
|
49
59
|
|
|
50
60
|
## Getting this skill
|
|
51
61
|
|
|
52
|
-
The skill ships
|
|
53
|
-
|
|
62
|
+
The skill ships inside the public `@visa/cli` npm package. No clone of the private
|
|
63
|
+
monorepo is required:
|
|
54
64
|
|
|
65
|
+
```sh
|
|
66
|
+
npm install -g @visa/cli@rc
|
|
67
|
+
visa agent skill
|
|
55
68
|
```
|
|
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
69
|
|
|
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
|
-
`~/.hermes/config.yaml` under `mcp_servers`). See Runtimes below for the exact shape.
|
|
92
|
-
- **No runtime integration?** The raw `visa agent …` CLI still works — the universal fallback.
|
|
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`):
|
|
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.
|
|
74
|
+
|
|
75
|
+
Access remains enforced by the Visa service. Installing the public package or skill does
|
|
76
|
+
not authorize an account to pair.
|
|
77
|
+
|
|
78
|
+
OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
|
|
79
|
+
|
|
80
|
+
## Getting set up
|
|
81
|
+
|
|
82
|
+
1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
|
|
83
|
+
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
|
|
85
|
+
`@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.
|
|
94
|
+
|
|
95
|
+
## MCP mounting examples
|
|
96
|
+
|
|
97
|
+
Both runtimes use the same server entrypoint. Replace `<npm root -g>` with the output of
|
|
98
|
+
`npm root -g`.
|
|
99
|
+
|
|
100
|
+
OpenClaw (`~/.openclaw/openclaw.json`):
|
|
104
101
|
|
|
105
102
|
```json
|
|
106
103
|
{
|
|
@@ -115,7 +112,7 @@ server so the runtime exposes the tools (both use the same server entrypoint —
|
|
|
115
112
|
}
|
|
116
113
|
```
|
|
117
114
|
|
|
118
|
-
|
|
115
|
+
Hermes (`~/.hermes/config.yaml`):
|
|
119
116
|
|
|
120
117
|
```yaml
|
|
121
118
|
mcp_servers:
|
|
@@ -124,17 +121,22 @@ mcp_servers:
|
|
|
124
121
|
args: ['<npm root -g>/@visa/cli/dist/mcp-server/index.js']
|
|
125
122
|
```
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
|
|
125
|
+
existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
|
|
126
|
+
|
|
127
|
+
## JSON and exit-code contract
|
|
130
128
|
|
|
131
|
-
|
|
129
|
+
Prefer structured output and parse it; never scrape prose.
|
|
132
130
|
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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.
|
|
135
|
+
|
|
136
|
+
The start result includes protocol version `2`, `pairingId`, stable `agentId`, full
|
|
137
|
+
`requestKeyFingerprint`, `browserUrl`, and expiry information. The activation result
|
|
138
|
+
includes protocol version `2`, `pairingId`, stable `agentId`, the display name, and
|
|
139
|
+
`identityKeyJkt`.
|
|
138
140
|
|
|
139
141
|
## Fastest path — one shot (`visa agent pair`)
|
|
140
142
|
|
|
@@ -145,259 +147,169 @@ from a single call, so the user types nothing after their initial request:
|
|
|
145
147
|
visa agent pair --format json
|
|
146
148
|
```
|
|
147
149
|
|
|
148
|
-
It returns `browserUrl`
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
It returns `browserUrl`, `pairingId`, stable `agentId`, and the full public
|
|
151
|
+
`requestKeyFingerprint` **immediately**. It starts a detached activation process only
|
|
152
|
+
after the pending identity and private key are durable. Present the link and fingerprint
|
|
153
|
+
for exact browser comparison. When `claiming` is `background`, activation completes after
|
|
154
|
+
approval without another command; if it is `manual`, run the returned `recoveryCommand`
|
|
155
|
+
after approval. Use the step-by-step flow below when `pair` is unavailable or when driving
|
|
156
|
+
the plugin/MCP tools.
|
|
157
|
+
|
|
158
|
+
## Returning & already-connected — do NOT re-pair
|
|
159
|
+
|
|
160
|
+
If `pair`/`enroll` returns **`already_connected`** (or `alreadyConnected: true`), this
|
|
161
|
+
device is **already set up** as an agent — a fresh pairing is neither needed nor
|
|
162
|
+
possible (re-pairing an existing identity silently dead-ends). Do this instead:
|
|
163
|
+
|
|
164
|
+
1. **Tell the user plainly:** "This device is already connected as `<name>.visa`."
|
|
165
|
+
Read the name from the response. Do **not** start another hand-off. `--new` /
|
|
166
|
+
`force:true` exists only if they explicitly want a _second, separate_ agent.
|
|
167
|
+
2. **Report status honestly — "connected" is five separate things**, not one. Never
|
|
168
|
+
imply the agent can spend just because it's connected:
|
|
169
|
+
- **Identity** — connected (`.visa` name bound to _this user's_ account). ✓ once `already_connected`.
|
|
170
|
+
- **Spending** — a _separate human approval_. If `keyBound`/spend isn't set, say:
|
|
171
|
+
"To let me pay, approve spending in your **account settings → agent card → Spend**."
|
|
172
|
+
You **cannot** self-grant it; never tell the user to run `visa agent add-rail`
|
|
173
|
+
(it refuses identity-only agents).
|
|
174
|
+
- **Mesh (`.visa` messaging)** — with `SUBWAY_MESH=visa`, a returning device now
|
|
175
|
+
registers on `pair`; if `meshRegistered` is false, `visa register <name>` joins it.
|
|
176
|
+
- **Trusted (TAP)** — follows spend/provisioning; don't promise it before then.
|
|
177
|
+
3. **Scope everything to the user.** The identity is bound to the account the user
|
|
178
|
+
signed in with (their email); the wallet + spend limits are theirs. Speak in terms
|
|
179
|
+
of "your agent / your account / the limits you approved," never a shared identity.
|
|
180
|
+
|
|
181
|
+
The paid wallet keeps working regardless of the account's spend flag — see "What you
|
|
182
|
+
can do once paired." Missing **spend** blocks _checkout mandates_, not local x402 pay.
|
|
153
183
|
|
|
154
184
|
## Core flow
|
|
155
185
|
|
|
156
|
-
|
|
186
|
+
- [ ] Start the pairing with `pair_agent_start`, `enroll_agent` action `start`, or
|
|
187
|
+
`visa agent enroll --format json`.
|
|
188
|
+
- [ ] Present the authorization URL, stable agent ID, and full public fingerprint to the human.
|
|
189
|
+
- [ ] Poll with `pair_agent_poll`, `enroll_agent` action `claim`, or
|
|
190
|
+
`visa agent enroll-claim --format json`.
|
|
191
|
+
- [ ] Report the stable `agentId` and that the identity is paired on this runtime.
|
|
157
192
|
|
|
158
|
-
|
|
159
|
-
- [ ] Step 2 — Present the link AND the confirmation code to the user
|
|
160
|
-
- [ ] Step 3 — Poll until paired (`pair_agent_poll` / `visa agent enroll-claim --format json`) — auto-pairs, no code entry
|
|
161
|
-
- [ ] Step 4 — Confirm the identity is bound to this device
|
|
193
|
+
### 1. Start
|
|
162
194
|
|
|
163
|
-
|
|
164
|
-
this skill). The returned fields and the poll semantics are identical across all of them.
|
|
195
|
+
Start once. If there is already a pending pairing, poll it before creating another.
|
|
165
196
|
|
|
166
|
-
|
|
197
|
+
The result provides `browserUrl`, stable `agentId`, and `requestKeyFingerprint`. Show the
|
|
198
|
+
URL as a bare, tappable value on its own line. Do not decorate it as a Markdown link or put
|
|
199
|
+
it in a code span; chat clients reliably recognize the bare URL. Show the complete
|
|
200
|
+
fingerprint without truncation and tell the human to approve only when every character
|
|
201
|
+
matches the browser review page.
|
|
167
202
|
|
|
168
|
-
|
|
169
|
-
→ phone link + confirm code → `enroll-claim`. It provisions identity + card + x402 wallet
|
|
170
|
-
in one phone tap, needs no terminal, and never asks you to relay a secret.
|
|
203
|
+
Use this concise shape:
|
|
171
204
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
205
|
+
> 🔐 Pair your Visa agent — open this authorization page:
|
|
206
|
+
>
|
|
207
|
+
> 👉 <browserUrl, bare and on its own line>
|
|
208
|
+
>
|
|
209
|
+
> Stable agent ID: <agentId>
|
|
210
|
+
>
|
|
211
|
+
> Public request-key fingerprint — compare every character in the browser:
|
|
212
|
+
> <requestKeyFingerprint, complete and untruncated>
|
|
213
|
+
>
|
|
214
|
+
> Approve only if the fingerprint matches, then tell me when it is finished. The link
|
|
215
|
+
> expires shortly.
|
|
178
216
|
|
|
179
|
-
|
|
217
|
+
Do not ask the human for a code, secret, private key, token, or signed message. The URL,
|
|
218
|
+
stable agent ID, and public fingerprint are review values, not claim credentials.
|
|
180
219
|
|
|
181
|
-
|
|
182
|
-
any prior. If the user says they already started enrolling, go to Step 3 first.
|
|
220
|
+
### 2. Poll
|
|
183
221
|
|
|
184
|
-
|
|
185
|
-
`browserUrl`, `confirmCode`, `expiresAt`.
|
|
222
|
+
Poll the same pending pairing. Interpret results as follows:
|
|
186
223
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
224
|
+
- `activated` — pairing is complete. Continue to the completion report.
|
|
225
|
+
- `not_ready` with `likelyExpired: false` — the human has not finished. Wait and poll
|
|
226
|
+
again, using bounded retries rather than an endless loop.
|
|
227
|
+
- `not_ready` with `likelyExpired: true` — the authorization window probably expired.
|
|
228
|
+
Start a fresh pairing.
|
|
229
|
+
- `no_pending` — this runtime has no resumable pairing. Start a fresh pairing.
|
|
230
|
+
- `error` — surface the error without exposing local pending data. Retry once if it is a
|
|
231
|
+
transient network failure; otherwise stop and ask the human to start again.
|
|
190
232
|
|
|
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.
|
|
233
|
+
### 3. Report completion
|
|
194
234
|
|
|
195
|
-
|
|
235
|
+
On `activated`, report the display name when present and the stable `agentId`. You may
|
|
236
|
+
also report `identityKeyJkt` as the current key fingerprint, but label it clearly as a
|
|
237
|
+
rotatable key identifier.
|
|
196
238
|
|
|
197
|
-
|
|
239
|
+
Use precise completion language:
|
|
198
240
|
|
|
199
|
-
|
|
241
|
+
> Visa agent <displayName> is paired to this runtime. Stable agent ID: <agentId>.
|
|
200
242
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
a bare URL (one tap opens the phone's browser); a code span is unclickable, and the long
|
|
204
|
-
pairing query string (`?cli=…&cliPk=…`, with underscores) breaks Telegram MarkdownV2 link
|
|
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.
|
|
243
|
+
Do not claim that activation configured payments, email, tap bindings, or any other
|
|
244
|
+
product capability. Those are separate follow-up configuration flows.
|
|
214
245
|
|
|
215
|
-
|
|
246
|
+
## Interruption and replay safety
|
|
216
247
|
|
|
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
|
-
with any x402 URL the user names (the wallet pays any endpoint, no directory needed).
|
|
280
|
-
2. **`wallet_probe`** — preview a fresh x402 challenge for a discovered listing **or any
|
|
281
|
-
x402 URL** (free, no spend, needs no session). Confirm network (`eip155:8453` / Base),
|
|
282
|
-
asset (Base USDC), and that the price is at or below the user's ceiling.
|
|
283
|
-
3. **`wallet_pay`** (arbitrary URL) or **`wallet_directory_pay`** (a directory listing) —
|
|
284
|
-
a bounded payment settled directly from the delegated wallet over x402, enforcing the
|
|
285
|
-
on-device policy and journaling a receipt. Always pass a hard `max` ceiling.
|
|
286
|
-
|
|
287
|
-
Bounded by the on-device wallet policy; never touches credits, cards, or server-side spend
|
|
288
|
-
controls. These `wallet_*` tools are **default-on in the supported build**. CLI equivalents
|
|
289
|
-
(`visa find` / `inspect` / `pay`) exist, but prefer the MCP tools — and note `visa find` is
|
|
290
|
-
session-gated (it can report `Not logged in`), whereas `wallet_probe` / `wallet_pay` work on
|
|
291
|
-
any x402 URL directly, so use those when discovery is unavailable.
|
|
292
|
-
|
|
293
|
-
**NEVER — to find or pay an x402 service — fall back to any of:** `npx awal` or any
|
|
294
|
-
"bazaar"/third-party discovery client; `curl` or hand-built EIP-3009 signatures / another
|
|
295
|
-
payment client; reading a merchant's OpenAPI / `/docs` to guess an endpoint and pay it
|
|
296
|
-
blind; or any retired catalog/direct-execution surface. If the wallet tools cannot find
|
|
297
|
-
or pay something, report that to the user
|
|
298
|
-
with what you tried and stop — do not improvise another payment path. `wallet_probe` +
|
|
299
|
-
`wallet_pay` already settle ANY x402 endpoint the user gives you.
|
|
300
|
-
|
|
301
|
-
- **Message other agents on the `.visa` mesh (Subway)** — once paired, your agent's Visa
|
|
302
|
-
identity **doubles as its Subway mesh identity** (admission reuses the same Visa-signed
|
|
303
|
-
device-pairing + TAP binding, so a paired, TAP-registered `.visa` agent is already
|
|
304
|
-
admitted — no extra key, no separate install; the Subway SDK is bundled into the mounted
|
|
305
|
-
`visa-cli` MCP server). The tools:
|
|
306
|
-
- `subway_register` — FREE. Claim your handle → `<name>.visa` on the mesh (reuses your
|
|
307
|
-
agent identity). Returns the mesh name + peer id.
|
|
308
|
-
- `subway_send` — FREE. Send a direct **signed** message to another agent: `to` (their
|
|
309
|
-
handle, e.g. `"dee"` → `dee.visa`) + `text`. Reaches other `.visa` mesh peers only.
|
|
310
|
-
(A Telegram bridge is planned — #6119 — but not built yet; do not tell a user they can
|
|
311
|
-
message a Telegram contact through `subway_send`.)
|
|
312
|
-
- `subway_inbox` — FREE. Read recent inbound messages (`limit`, `clear` to drain).
|
|
313
|
-
- `subway_find` — resolve a handle to its `.visa` peer.
|
|
314
|
-
|
|
315
|
-
**Gated (be honest with the user):** mesh messaging is live only on an **RC/dev build**
|
|
316
|
-
with `SUBWAY_MESH=visa` **and a reachable relay** (`SUBWAY_RELAY_MULTIADDR`, a Visa Crypto
|
|
317
|
-
Labs deployment). On a stable build the `subway_*` tools aren't exposed; without a relay
|
|
318
|
-
they no-op (`subway_register` reports the binding is "ready for admission once a relay is
|
|
319
|
-
up"). If a user asks to message another `.visa` agent and the mesh isn't wired, say so
|
|
320
|
-
plainly and stop — do not improvise another transport.
|
|
321
|
-
|
|
322
|
-
- **Real-merchant card checkout (experimental, opt-in)** — `pay_merchant` fills and pays an
|
|
323
|
-
ordinary merchant web checkout with a **Verified Agent card credential**: a one-shot
|
|
324
|
-
network-token cryptogram minted **on this device** (non-custodial) — not a stored card,
|
|
325
|
-
not x402, not server-side spend controls. Two steps: `review` (free; returns merchant +
|
|
326
|
-
exact amount as a `reviewId`) then `pay` (requires `confirm: "PAY <reviewId>"` + a passkey,
|
|
327
|
-
and CHARGES). Prerequisites — the tool errors clearly if any is missing:
|
|
328
|
-
1. **`checkout_agent_access`** flag on your account (email-keyed; an admin grants it via
|
|
329
|
-
`PUT /v1/admin/users/<your-enroll-email>/feature-flags/checkout_agent_access` or the
|
|
330
|
-
admin panel). Distinct from the RC/GitHub allowlist.
|
|
331
|
-
2. **`CHECKOUT_AGENT_ALLOW_SUBMIT=1`** in the MCP server's env. This is the submit opt-in:
|
|
332
|
-
WITHOUT it the agent fills the checkout form but **refuses to press the pay button** (the
|
|
333
|
-
default safe posture — `submit:false`), so a checkout silently never completes. Set it on
|
|
334
|
-
the `visa-cli` MCP server entry (e.g. OpenClaw `mcp.servers["visa-cli"].env`, Hermes
|
|
335
|
-
`mcp_servers.visa-cli.env`, or `claude mcp add … -e CHECKOUT_AGENT_ALLOW_SUBMIT=1`). The
|
|
336
|
-
`visa-cli checkout … --submit` CLI flag sets the same opt-in.
|
|
337
|
-
3. An enrolled agent credential (`enroll_agent`) and a `~/.visa-mcp/contact.json`. This file
|
|
338
|
-
supplies the **cardholder name** the credential is minted with AND the billing details
|
|
339
|
-
filled into the merchant form. If it is missing, or `fullName` is empty/whitespace, the
|
|
340
|
-
checkout dies at the final step with `cardholder name is required` (after mandate
|
|
341
|
-
approval — an expensive late failure). **Before the first checkout, ASK the user for
|
|
342
|
-
these fields and write the file yourself** (0600), with a REAL non-blank `fullName`:
|
|
343
|
-
|
|
344
|
-
```jsonc
|
|
345
|
-
// ~/.visa-mcp/contact.json (chmod 600)
|
|
346
|
-
{
|
|
347
|
-
"fullName": "Ada Lovelace", // REQUIRED, non-blank — the cardholder name
|
|
348
|
-
"email": "ada@example.com", // REQUIRED — used by the approval ceremony
|
|
349
|
-
"addressLine1": "1 Analytical Way",
|
|
350
|
-
"addressLine2": "",
|
|
351
|
-
"city": "London",
|
|
352
|
-
"state": "CA", // 2-letter region where applicable
|
|
353
|
-
"postalCode": "94105",
|
|
354
|
-
"country": "US", // 2-letter ISO
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
Exact keys only (`fullName` | `firstName`+`lastName`, `email`, `addressLine1/2`, `city`,
|
|
359
|
-
`state`, `postalCode`, `country`) — the engine reads these names verbatim. Never invent a
|
|
360
|
-
name; if the user won't provide one, stop and say checkout needs a real cardholder name.
|
|
361
|
-
|
|
362
|
-
**RC/preview builds only**, opt-in, never paired-and-go.
|
|
363
|
-
|
|
364
|
-
If a tool you expect isn't visible, the MCP server isn't mounted (or the v4 wallet runtime
|
|
365
|
-
isn't bundled in this build) — go back to "Getting set up".
|
|
366
|
-
|
|
367
|
-
## Important
|
|
368
|
-
|
|
369
|
-
- **The confirmation code is display-only.** Show it; never ask for it; never accept it as
|
|
370
|
-
input. It lets the human verify the page matches the flow you started.
|
|
371
|
-
- **Never read, print, log, or echo** the credential file or the enrollment pending file,
|
|
372
|
-
or the `browserUrl` query values beyond the single presentation to the user. The link
|
|
373
|
-
carries only a hash + a public key — safe in history — but treat it as one-time.
|
|
374
|
-
- Do not run API-key setup, card enrollment, or a balance top-up as a substitute for
|
|
375
|
-
pairing. Once paired, use the `visa-cli` skill's v4 wallet commands to pay.
|
|
376
|
-
|
|
377
|
-
## Limits
|
|
378
|
-
|
|
379
|
-
| Limit | Value |
|
|
380
|
-
| ------------------------------ | ------------------------------------------------------------ |
|
|
381
|
-
| Hand-off validity | 15 minutes from `pair_agent_start` |
|
|
382
|
-
| In-flight hand-offs per device | 1 (a new start replaces the prior) |
|
|
383
|
-
| Claim | single-shot server-side; once claimed the entry is destroyed |
|
|
384
|
-
| Confirmation code | 6 chars, no ambiguous glyphs; display-only |
|
|
385
|
-
|
|
386
|
-
## Errors
|
|
387
|
-
|
|
388
|
-
All errors are JSON with a non-zero exit code; `enroll-claim` tags them with `status`.
|
|
389
|
-
|
|
390
|
-
| status / symptom | Cause | Recovery |
|
|
391
|
-
| ------------------------------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
392
|
-
| `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. |
|
|
393
|
-
| `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. |
|
|
394
|
-
| `no_pending` | No hand-off in flight (never started, or already claimed/expired) | Start fresh with `pair_agent_start`. |
|
|
395
|
-
| `not_ready`, `likelyExpired: false` | User hasn't finished the mobile flow | Wait, tell the user, poll again. Bounded polling only. |
|
|
396
|
-
| `not_ready`, `likelyExpired: true` | 15-minute window elapsed | Start over with `pair_agent_start`. |
|
|
397
|
-
| `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. |
|
|
398
|
-
| `error` (network / malformed) | Transport or server error | Surface the message. Poll once more; if it persists, start over. |
|
|
248
|
+
The runtime persists its v2 pending record before sending the signed claim. If a request
|
|
249
|
+
or response is interrupted, the next poll resumes the exact same `pairingId`, `agentId`,
|
|
250
|
+
Ed25519 identity, and claim material. This makes an identical retry safe and avoids
|
|
251
|
+
creating a second identity because a response was lost.
|
|
252
|
+
|
|
253
|
+
The pending record remains until the activated agent record has been written durably.
|
|
254
|
+
Therefore:
|
|
255
|
+
|
|
256
|
+
- Poll before starting over.
|
|
257
|
+
- Do not delete or edit pending files to fix a transient failure.
|
|
258
|
+
- Do not regenerate keys for an existing pairing.
|
|
259
|
+
- Do not copy pending state between runtimes.
|
|
260
|
+
|
|
261
|
+
## Optional `.visa` mesh binding (separate from pairing)
|
|
262
|
+
|
|
263
|
+
Pairing does not register a `.visa` name, TAP key, or Subway peer. If the operator has
|
|
264
|
+
separately enabled and registered those channel bindings, the mounted `visa-cli` MCP server
|
|
265
|
+
may expose `subway_register`, `subway_send`, `subway_inbox`, and `subway_find`.
|
|
266
|
+
|
|
267
|
+
Mesh messaging is available only on a compatible RC/dev build with `SUBWAY_MESH=visa` and
|
|
268
|
+
a reachable `SUBWAY_RELAY_MULTIADDR`. If those tools or the relay are unavailable, report
|
|
269
|
+
that clearly and stop. Do not imply that identity pairing alone granted directory or
|
|
270
|
+
messaging authority, and do not improvise another transport.
|
|
271
|
+
|
|
272
|
+
## Optional checkout profile (separate from pairing)
|
|
273
|
+
|
|
274
|
+
Pairing does not enable card checkout. If the operator has separately provisioned card
|
|
275
|
+
authority, enabled `checkout_agent_access`, and opted the MCP server into submission with
|
|
276
|
+
`CHECKOUT_AGENT_ALLOW_SUBMIT=1`, the experimental `pay_merchant` flow also needs a local
|
|
277
|
+
`~/.visa-mcp/contact.json` file. Collect every value from the human before the first review;
|
|
278
|
+
never infer or invent identity or address data. Write the file with mode `0600`.
|
|
279
|
+
|
|
280
|
+
```jsonc
|
|
281
|
+
{
|
|
282
|
+
"fullName": "Ada Lovelace",
|
|
283
|
+
"email": "ada@example.com",
|
|
284
|
+
"addressLine1": "1 Analytical Way",
|
|
285
|
+
"addressLine2": "",
|
|
286
|
+
"city": "San Francisco",
|
|
287
|
+
"state": "CA",
|
|
288
|
+
"postalCode": "94105",
|
|
289
|
+
"country": "US",
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`fullName` must be non-blank; `firstName` plus `lastName` is also accepted. The engine reads
|
|
294
|
+
the exact keys `fullName`, `firstName`, `lastName`, `email`, `addressLine1`, `addressLine2`,
|
|
295
|
+
`city`, `state`, `postalCode`, and `country`. The profile supplies checkout/cardholder and
|
|
296
|
+
billing data only. Its `email` value is not the account's verified owner email, an agent
|
|
297
|
+
mailbox, key proof, recovery factor, or permission to spend.
|
|
298
|
+
|
|
299
|
+
## Security rules
|
|
300
|
+
|
|
301
|
+
- Never read, print, log, paste, or transmit the private Ed25519 JWK or local claim token.
|
|
302
|
+
- Never read or echo the pending pairing file. Present only the authorization URL returned
|
|
303
|
+
by the supported command or tool.
|
|
304
|
+
- Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
|
|
305
|
+
persistence.
|
|
306
|
+
- Never fetch the authorization URL on the human's behalf. The human reviews and approves
|
|
307
|
+
it in their browser.
|
|
308
|
+
- Never invent a secondary pairing path when polling fails. Preserve the pending state,
|
|
309
|
+
surface the error, and retry or restart through the same canonical enrollment flow.
|
|
399
310
|
|
|
400
311
|
## Further docs
|
|
401
312
|
|
|
402
|
-
- `
|
|
403
|
-
- `
|
|
313
|
+
- `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
|
|
314
|
+
- `docs/agents/ARCHITECTURE.md` — where enrollment sits in the v4 request paths.
|
|
315
|
+
- `visacli.sh/agents` — product-facing agent documentation.
|