@visa/cli 5.0.0-rc.409 → 5.0.0-rc.412
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 +166 -761
- package/bin/visa-tool.js +6 -0
- package/dist/cli.js +128 -177
- package/dist/enrollment-finisher.js +67 -13
- package/dist/managed-runtime/runtime-readiness.mjs +0 -44
- package/dist/managed-runtime/update-and-restart.mjs +36 -120
- package/dist/mcp-server/index.js +339 -627
- package/dist/tool-call.js +64 -0
- package/package.json +5 -7
- package/server.json +2 -2
- package/dist/skills/pair-visa-agent/RUNTIMES.md +0 -117
- package/dist/skills/pair-visa-agent/SKILL.md +0 -517
- package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +0 -422
- package/dist/skills/pair-visa-agent/scripts/setup.mjs +0 -328
|
@@ -1,517 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pair-visa-agent
|
|
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
|
-
allowed-tools: Bash(visa:*) Bash(visa-cli:*) Bash(node:*) Bash(npm:*) Bash(npx:*)
|
|
6
|
-
metadata:
|
|
7
|
-
author: visa
|
|
8
|
-
homepage: https://visacli.sh/agents
|
|
9
|
-
version: '0.9.0'
|
|
10
|
-
# OpenClaw-namespaced extension (agentskills.io keeps `metadata` free-form, so
|
|
11
|
-
# non-standard runtime config lives here — `user-invocable` is not a standard
|
|
12
|
-
# top-level field). OpenClaw auto-installs `install[]` when `requires.bins` are
|
|
13
|
-
# missing; other runtimes (Hermes, Claude Code, …) self-provision via the
|
|
14
|
-
# bundled `scripts/setup.mjs` (see compatibility + Getting set up). @rc pinned
|
|
15
|
-
# until 4.1.0 is promoted to latest.
|
|
16
|
-
openclaw:
|
|
17
|
-
user-invocable: true
|
|
18
|
-
emoji: '🔐'
|
|
19
|
-
requires:
|
|
20
|
-
bins:
|
|
21
|
-
- visa
|
|
22
|
-
install:
|
|
23
|
-
- kind: node
|
|
24
|
-
package: '@visa/cli@rc'
|
|
25
|
-
bins:
|
|
26
|
-
- visa
|
|
27
|
-
- visa-cli
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
# Set up a Visa agent
|
|
31
|
-
|
|
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.
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
- **You have the Visa MCP server mounted:** call `agent_enroll` once and give the owner
|
|
41
|
-
`browserUrl` and `confirmCode` exactly as returned. After their one approval this computer
|
|
42
|
-
finishes in the background. Poll `get_status`: `pairing.setup.state` goes `finishing` →
|
|
43
|
-
`connected`, and `pairing.setup.owed` names any owner step still left (call `agent_enroll`
|
|
44
|
-
once to hand it over). Do not call `agent_enroll` while it is `finishing`.
|
|
45
|
-
- **The owner is at a terminal:** they run the command below.
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
# the human runs this:
|
|
49
|
-
visa connect
|
|
50
|
-
|
|
51
|
-
→ it prints a URL and a short code
|
|
52
|
-
→ the human opens the URL and enters the code at /agent/enroll/protected-agent
|
|
53
|
-
→ the human approves the device and its spending limits (one approval)
|
|
54
|
-
→ the command finishes; `get_status` now reports pairing.paired: true
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The installed CLI selects its own service origins from its release channel. Never ask
|
|
58
|
-
for, guess, or construct a Visa URL.
|
|
59
|
-
|
|
60
|
-
Running the same command again resumes an interrupted enrolment; `--restart` replaces an
|
|
61
|
-
unclaimed request with a fresh code. The local finisher can time out after about 15 minutes; this does not prove server expiry: when `pairing.setup.state` reads `expired`, call `agent_enroll` once without
|
|
62
|
-
`restart` (terminal: `visa connect`): it resumes the original request if the server still
|
|
63
|
-
accepts it, or answers `enrollment_expired` for an already-bound request the server no longer accepts. Only then call it with `restart: true` (terminal:
|
|
64
|
-
`visa connect --restart`) and relay the new link and code. `--ceiling <usd>` and `--per-transaction <usd>`
|
|
65
|
-
propose limits the owner confirms on the approval page.
|
|
66
|
-
|
|
67
|
-
## The tools the Visa MCP server serves
|
|
68
|
-
|
|
69
|
-
The Visa MCP server serves exactly these tools: `get_status`, `agent_enroll`,
|
|
70
|
-
`discover`, `pay` and `history`, plus the card preview's
|
|
71
|
-
`purchase_create`, `purchase_retrieve` and `purchase_cancel` where the build lists
|
|
72
|
-
them. Call no other Visa tool name, whatever an older guide, a memory or another
|
|
73
|
-
agent says: every earlier setup, login, pairing, capability and wallet tool is gone.
|
|
74
|
-
Read the live tool list if in doubt.
|
|
75
|
-
|
|
76
|
-
## The older ceremonies were deleted — do not call them
|
|
77
|
-
|
|
78
|
-
The whole `visa setup` and `visa agent` command groups are gone too — including the
|
|
79
|
-
retired `agent pair`, `agent create`, `agent verify`, `agent enroll-claim`,
|
|
80
|
-
`agent claim`, `agent pairing-resume`, `agent connect`, `agent handoff-claim` and
|
|
81
|
-
every `agent grant-*` verb.
|
|
82
|
-
|
|
83
|
-
**`visa connect` is the LIVE door and is not on that list.** It is the
|
|
84
|
-
top-level command, not the retired `visa agent connect` subcommand: one is the
|
|
85
|
-
one door, the other is a ceremony that no longer exists.
|
|
86
|
-
|
|
87
|
-
A retired command, or an older build still answering a retired tool name, returns
|
|
88
|
-
one refusal:
|
|
89
|
-
|
|
90
|
-
```json
|
|
91
|
-
{
|
|
92
|
-
"code": "legacy_door_removed",
|
|
93
|
-
"kind": "caller",
|
|
94
|
-
"fix": "visa connect (shell) or agent_enroll (MCP)"
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
That code means the door no longer exists. **Relay the replacement and stop.** Do not
|
|
99
|
-
retry it, do not try a variant spelling, and do not report it to the human as an outage:
|
|
100
|
-
nothing was signed, paired, or paid.
|
|
101
|
-
|
|
102
|
-
## What "set up" means here
|
|
103
|
-
|
|
104
|
-
Each completed protected enrollment mints a **new** agent: a new server-assigned agent,
|
|
105
|
-
a new device-held Ed25519 identity key, and the wallet limits the owner approved.
|
|
106
|
-
|
|
107
|
-
- The runtime keeps the private Ed25519 key and sends only the public JWK.
|
|
108
|
-
- The limits are the ones the owner actually approved on the page. Nothing you asked for
|
|
109
|
-
is granted until they approve it.
|
|
110
|
-
- An email address, a `.visa` mesh name, and TAP bindings remain separate, later
|
|
111
|
-
configuration. Do not infer them from a finished enrolment.
|
|
112
|
-
|
|
113
|
-
**Where the `agentId` comes from — read this before you quote one.** Do not invent it and
|
|
114
|
-
do not read it out of the command's terminal output, which you often cannot see. Read it
|
|
115
|
-
from `get_status` once the enrolment finishes. Never substitute a
|
|
116
|
-
correlation id, a confirmation code, or an origin for an `agentId`.
|
|
117
|
-
|
|
118
|
-
**Re-running the command does not repair an agent that already exists** — it creates a
|
|
119
|
-
second one. To fix a capability an existing agent is missing, see "Already set up — do NOT
|
|
120
|
-
enrol again" near the end.
|
|
121
|
-
|
|
122
|
-
## Getting this skill
|
|
123
|
-
|
|
124
|
-
The skill ships inside the public `@visa/cli` npm package. No clone of the private
|
|
125
|
-
monorepo is required:
|
|
126
|
-
|
|
127
|
-
```sh
|
|
128
|
-
npm install -g @visa/cli@rc
|
|
129
|
-
visa connect
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
`visa connect` auto-detects the runtime it is running in. It plants this skill into that
|
|
133
|
-
runtime's own skills directory (OpenClaw, Hermes, Claude Code and Codex each read their
|
|
134
|
-
own), mounts the Visa MCP server, signs the owner in, enrols this machine's agent and
|
|
135
|
-
renews its device lease — skipping whatever is already done. Pass a client explicitly
|
|
136
|
-
(`visa connect codex`) when auto-detection is not what you want, and `--restart` to
|
|
137
|
-
replace an edited skill. Reload or restart the agent runtime afterwards so it registers
|
|
138
|
-
the skill.
|
|
139
|
-
|
|
140
|
-
Access remains enforced by the Visa service. Installing the public package or skill does
|
|
141
|
-
not authorize an account to pair.
|
|
142
|
-
|
|
143
|
-
OpenClaw users receive the skill via `visa connect openclaw` (or through the `@visa/visa-cli-openclaw` plugin when running from source).
|
|
144
|
-
|
|
145
|
-
## Getting set up
|
|
146
|
-
|
|
147
|
-
1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
|
|
148
|
-
idempotent provisioner `node scripts/setup.mjs`. The `@latest` tag may not yet expose
|
|
149
|
-
the v4 setup tools. Installation puts `visa` and `visa-cli` on `PATH` and includes
|
|
150
|
-
`@visa/cli/dist/mcp-server/index.js`.
|
|
151
|
-
2. **Run `visa connect <client>`.** One command: it plants the skill, writes the MCP
|
|
152
|
-
entry, signs the owner in, enrols this machine's agent and renews its device lease.
|
|
153
|
-
- **OpenClaw:** `visa connect openclaw` writes `mcp.servers["visa-cli"]` in
|
|
154
|
-
`~/.openclaw/openclaw.json`. (Installing `@visa/visa-cli-openclaw` from
|
|
155
|
-
source/tarball also auto-mounts the server).
|
|
156
|
-
- **Hermes:** `visa connect hermes` writes the entry under `mcp_servers` in
|
|
157
|
-
`~/.hermes/config.yaml`.
|
|
158
|
-
- **No MCP integration:** everything below still works — connecting is a shell
|
|
159
|
-
command, and `--format json` is available on every command.
|
|
160
|
-
3. **Check what is owed.** `get_status` reports the owner, the agent, its limits, which
|
|
161
|
-
clients are mounted, and one `next:` line when something is blocking.
|
|
162
|
-
|
|
163
|
-
## MCP mounting examples
|
|
164
|
-
|
|
165
|
-
Both runtimes use the same server entrypoint. Replace `<npm root -g>` with the output of
|
|
166
|
-
`npm root -g`.
|
|
167
|
-
|
|
168
|
-
OpenClaw (`~/.openclaw/openclaw.json`):
|
|
169
|
-
|
|
170
|
-
```json
|
|
171
|
-
{
|
|
172
|
-
"mcp": {
|
|
173
|
-
"servers": {
|
|
174
|
-
"visa-cli": {
|
|
175
|
-
"command": "node",
|
|
176
|
-
"args": ["<npm root -g>/@visa/cli/dist/mcp-server/index.js"]
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Hermes (`~/.hermes/config.yaml`). **Hermes passes ONLY this `env:` map to the MCP
|
|
184
|
-
subprocess — it does NOT inherit the gateway environment.** Omitting a required variable
|
|
185
|
-
(an RC access code, the right `HOME`, `PATH`) makes the server exit on every start, so no
|
|
186
|
-
Visa tool registers at all, while the terminal — which reads on-disk state with the
|
|
187
|
-
gateway's own environment — can still look healthy. Always set the map explicitly:
|
|
188
|
-
|
|
189
|
-
```yaml
|
|
190
|
-
mcp_servers:
|
|
191
|
-
visa-cli:
|
|
192
|
-
command: node
|
|
193
|
-
args: ['<npm root -g>/@visa/cli/dist/mcp-server/index.js']
|
|
194
|
-
# Hermes does NOT inherit the gateway env. This map is the entire
|
|
195
|
-
# subprocess environment; omit VISA_RC_CODE and the server exits on boot.
|
|
196
|
-
env:
|
|
197
|
-
HOME: /home/<user> # the home that holds this runtime's .visa-cli state
|
|
198
|
-
VISA_RC_CODE: <access code>
|
|
199
|
-
PATH: /usr/local/bin:/usr/bin:/bin
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
Hermes also loads skills **per profile** from `~/.hermes/profiles/<profile>/skills/`, not
|
|
203
|
-
from `~/.hermes/skills/`. `visa connect hermes` resolves this automatically: it targets
|
|
204
|
-
the single profile when exactly one exists (or the one named by `HERMES_PROFILE`), and
|
|
205
|
-
**fails loudly** on a multi-profile box instead of planting into the flat dir nothing
|
|
206
|
-
reads — set `HERMES_PROFILE` to choose.
|
|
207
|
-
|
|
208
|
-
**Hermes sanitizes MCP server names when registering tools.** A server declared
|
|
209
|
-
`visa-cli` in `mcp_servers:` registers its tools as `mcp__visa_cli__<tool>` — with an
|
|
210
|
-
UNDERSCORE, not the declared hyphen. Anything that hardcodes `mcp__visa-cli__<tool>` gets
|
|
211
|
-
`unknown tool` on every call while looking correct in review. Read tool names off the
|
|
212
|
-
live registry; never derive them from the config key.
|
|
213
|
-
|
|
214
|
-
In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
|
|
215
|
-
existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
|
|
216
|
-
|
|
217
|
-
## Sign in first — the wallet is owner-bound
|
|
218
|
-
|
|
219
|
-
The USDC wallet is delegated out of the owner's own wallet, so this runtime needs a live
|
|
220
|
-
owner session. The one door takes care of the order: `agent_enroll` (and `visa connect`)
|
|
221
|
-
signs the owner in first whenever the session is missing or has lapsed, and only then
|
|
222
|
-
starts the enrolment.
|
|
223
|
-
|
|
224
|
-
- [ ] Call `agent_enroll` (MCP) or run `visa connect --format json` (CLI). When a sign-in
|
|
225
|
-
is owed, the result carries a sign-in `browserUrl` and a short 6-character
|
|
226
|
-
`confirmCode`.
|
|
227
|
-
- [ ] Relay **both** to the human in your reply — the bare URL on its own line, and the
|
|
228
|
-
code. You are very often not in a terminal they can see; the chat message is the
|
|
229
|
-
only place these values reach them.
|
|
230
|
-
- [ ] The human opens the link, signs in (Google or email), and **types the confirmation
|
|
231
|
-
code into the sign-in page** — into the browser, never back to you in chat.
|
|
232
|
-
- [ ] Call `agent_enroll` again once they have (the CLI command polls on its own). It
|
|
233
|
-
picks up the approved sign-in on this device and moves on to the enrolment link.
|
|
234
|
-
This second call belongs to the sign-in leg only: nothing in the background picks
|
|
235
|
-
up a sign-in. The background finish starts after the enrolment link, so once the
|
|
236
|
-
owner approves that, do not call while `pairing.setup.state` is `finishing`.
|
|
237
|
-
|
|
238
|
-
Signing in establishes the OWNER's session on this device. It creates no agent and grants
|
|
239
|
-
no spending authority — those come from the enrolment and the approval the owner gives in
|
|
240
|
-
their browser.
|
|
241
|
-
|
|
242
|
-
If something reports `{"code":"session_required"}` or "Not logged in", that is this
|
|
243
|
-
ordering rule, not a fault: call `agent_enroll` (or run `visa connect`), drive the sign-in
|
|
244
|
-
above, then continue.
|
|
245
|
-
|
|
246
|
-
The account that signs in is the owner the enrolment binds to, and the same account must
|
|
247
|
-
be signed in on the approval page. A different account there fails closed.
|
|
248
|
-
|
|
249
|
-
## Core flow
|
|
250
|
-
|
|
251
|
-
- [ ] **Do not ask for service origins.** The installed CLI selects its Auth and web
|
|
252
|
-
origins from the release channel; Authority is private behind Auth.
|
|
253
|
-
- [ ] **Sign the owner in** (above), so the wallet leg has a session to bind to.
|
|
254
|
-
- [ ] **Start the one enrollment.** Call `agent_enroll` and relay its returned link and
|
|
255
|
-
code, or give the owner `visa connect` in a code block they can copy.
|
|
256
|
-
Never fall back to a retired setup tool.
|
|
257
|
-
- [ ] **Relay what the entrance returns.** Show an MCP-returned URL as a bare,
|
|
258
|
-
tappable value. For the terminal flow, the owner follows the URL and code
|
|
259
|
-
printed in their own terminal. The code goes into the browser at
|
|
260
|
-
`/agent/enroll/protected-agent`; it is not authority in chat.
|
|
261
|
-
- [ ] **Tell them what they are approving**: this device, and the spending limits. One
|
|
262
|
-
approval covers all of it.
|
|
263
|
-
- [ ] **Confirm from a tool, not from their word.** Poll `get_status` until
|
|
264
|
-
`pairing.paired` is `true`; the same `get_status` says what is actually live.
|
|
265
|
-
|
|
266
|
-
### Relaying values, and what you must never accept
|
|
267
|
-
|
|
268
|
-
Relaying values **to** the human is required. Accepting one **from** them as authority is
|
|
269
|
-
not: never ask them for a secret, private key, token, or signed message, and never treat
|
|
270
|
-
anything they type back as approval. The URL and the enrolment code are review values they
|
|
271
|
-
check against their own authenticated browser session — they cannot approve anything and
|
|
272
|
-
cannot spend.
|
|
273
|
-
|
|
274
|
-
Show any URL as a bare, tappable value on its own line. Do not decorate it as a Markdown
|
|
275
|
-
link or put it in a code span; chat clients reliably recognise the bare URL. Never
|
|
276
|
-
construct, shorten, or reformat a Visa URL, and never open one "for them" in place of
|
|
277
|
-
showing it.
|
|
278
|
-
|
|
279
|
-
You are very often **not** in a terminal the human can see. Nothing the command prints to
|
|
280
|
-
their stdout reaches you unless they tell you, and nothing you print reaches them unless it
|
|
281
|
-
is in your reply.
|
|
282
|
-
|
|
283
|
-
### Completion is what the tools say — nothing else
|
|
284
|
-
|
|
285
|
-
Do not tell the human the agent is connected, paired, set up, ready, or good to go until
|
|
286
|
-
`get_status` reports `pairing.paired: true`. The enrolment command printing a URL is not a
|
|
287
|
-
result: it means an approval is still open in their browser.
|
|
288
|
-
|
|
289
|
-
If you cannot get there, say plainly what state you did reach and what the human should do
|
|
290
|
-
next. An honest "the approval page is open — I'm waiting for you to approve the device and
|
|
291
|
-
its limits" is correct; "you're all set" without a paired agent is not.
|
|
292
|
-
|
|
293
|
-
On success, report the agent and what is actually live:
|
|
294
|
-
|
|
295
|
-
> Your Visa agent is set up. <Report only the capabilities the live map confirms.>
|
|
296
|
-
|
|
297
|
-
Read the rails from `get_status`, never from what was requested.
|
|
298
|
-
|
|
299
|
-
## Interruption and resume
|
|
300
|
-
|
|
301
|
-
The runtime persists the pending enrolment before it starts, so an interrupted run is safe
|
|
302
|
-
to repeat: **the same command again** resumes it, with the same request and the same
|
|
303
|
-
device key.
|
|
304
|
-
|
|
305
|
-
- Same command, no flags changed — resumes and reprints the URL and code.
|
|
306
|
-
- `--restart` — replaces an unclaimed request with a fresh terminal code. Use it only when
|
|
307
|
-
the previous code is genuinely unusable; it is not a retry button.
|
|
308
|
-
- `--wait` — keeps the command polling until the owner has approved, instead of returning
|
|
309
|
-
after printing the link.
|
|
310
|
-
|
|
311
|
-
Do not tell the human to run a second, different enrolment because the first went quiet.
|
|
312
|
-
Two enrolments mean two agents, two identities, and a confused owner. Do not delete or edit
|
|
313
|
-
local pending files to fix a transient failure, and never copy pending state between
|
|
314
|
-
runtimes.
|
|
315
|
-
|
|
316
|
-
## Management commands (an agent that already exists)
|
|
317
|
-
|
|
318
|
-
```
|
|
319
|
-
get_status {} # owner, agent, limits, mounts, every readiness gate
|
|
320
|
-
history {} # this agent's payments
|
|
321
|
-
# Caps are the owner's, in the Visa Console: /agent/enroll/limits?agent=<agent-id>
|
|
322
|
-
# Spend across every rail is the Console's Activity feed.
|
|
323
|
-
visa disconnect --revoke # drop this machine's spending authority
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
`get_status` is the one call that answers "can it spend, and what is missing" — it
|
|
327
|
-
carries the readiness gates the old `spendability` and `preflight` commands each answered
|
|
328
|
-
a fragment of. Pausing, resuming, renaming and revoking an agent are owner actions in the
|
|
329
|
-
Visa Console, not terminal commands.
|
|
330
|
-
|
|
331
|
-
None of these creates an identity. Prefer structured output and parse it; never scrape
|
|
332
|
-
prose.
|
|
333
|
-
|
|
334
|
-
## Already set up — do NOT enrol again
|
|
335
|
-
|
|
336
|
-
If this device already holds an agent, a fresh enrolment is not needed and creates a
|
|
337
|
-
_second, separate_ agent. Do this instead:
|
|
338
|
-
|
|
339
|
-
1. **Tell the user plainly:** "This device is already set up as `<name>`." Read the name
|
|
340
|
-
from `get_status`. Start another enrolment only if they
|
|
341
|
-
explicitly want a second agent.
|
|
342
|
-
2. **Report status honestly — "set up" is several separate things.** Never imply the agent
|
|
343
|
-
can spend just because it exists. Read it live from `get_status` rather than guessing
|
|
344
|
-
from prose: it reports pairing, account and version, and answers "can it spend, and
|
|
345
|
-
what is missing".
|
|
346
|
-
- **Identity** — bound to _this user's_ account, on _this device_.
|
|
347
|
-
- **Spending** — the limits the owner approved in the browser. You **cannot** self-grant
|
|
348
|
-
either rail or self-mint a wallet: the wallet arrives only by the owner's delegation.
|
|
349
|
-
- **Trusted (TAP)** — follows spend/provisioning; don't promise it before then.
|
|
350
|
-
3. **Scope everything to the user.** The identity is bound to the account they signed in
|
|
351
|
-
with; the wallet and limits are theirs. Speak in terms of "your agent / your account /
|
|
352
|
-
the limits you approved", never a shared identity.
|
|
353
|
-
|
|
354
|
-
## An existing agent is missing a capability
|
|
355
|
-
|
|
356
|
-
There is no rail-add ceremony any more: `visa agent grant-card` / `grant-wallet` /
|
|
357
|
-
`grant-activate` / `grant-claim` were deleted, and calling one returns
|
|
358
|
-
`legacy_door_removed`.
|
|
359
|
-
|
|
360
|
-
What to do instead:
|
|
361
|
-
|
|
362
|
-
- [ ] **Diagnose first.** `get_status` says
|
|
363
|
-
exactly what is missing. Do not start anything before you know which of identity,
|
|
364
|
-
wallet delegation, card authority or funding is absent.
|
|
365
|
-
- [ ] **If the device's identity custody is broken**, `visa connect` repairs it in place
|
|
366
|
-
without minting a new agent — it renews the device lease and skips every step that
|
|
367
|
-
is already done.
|
|
368
|
-
- [ ] **If the OWNER never approved that authority**, only they can add it. Say so, name
|
|
369
|
-
what is missing, and stop. Follow the returned Payments or limits action for this agent. Do not use
|
|
370
|
-
enrollment or restart to work around a payment refusal.
|
|
371
|
-
|
|
372
|
-
A policy refusal is a stop: follow its returned remedy. Never raise a human-approved limit yourself. A timeout leaves the payment outcome unknown; use the recovery table below.
|
|
373
|
-
|
|
374
|
-
## Talking to the owner
|
|
375
|
-
|
|
376
|
-
<!-- recovery-guidance: replyStyle -->
|
|
377
|
-
|
|
378
|
-
Keep each owner message to a line or two, result first. Amounts in dollars; for an approval, the price and the link. Skip preamble, announcing next steps, recaps, how Visa works and waiting. No ids or hashes unless something failed; then one id. End with the result and one total, no per-call costs.
|
|
379
|
-
<!-- /recovery-guidance -->
|
|
380
|
-
|
|
381
|
-
## Spending, once a rail is live
|
|
382
|
-
|
|
383
|
-
Wallet: `discover` searches with `query`, or probes a URL/listing id with `target`
|
|
384
|
-
without paying (target probes require wallet setup), `pay` pays it with a mandatory `max` ceiling, and `history` reads this agent's
|
|
385
|
-
payments. The limits are the ones the owner approved; the owner changes them in the Visa
|
|
386
|
-
Console, never you. Funding is the owner's, in the Console.
|
|
387
|
-
|
|
388
|
-
Card: Visa-operated browser checkout is retired; another enrollment will not repair it.
|
|
389
|
-
`pay` is the wallet door: it refuses a merchant checkout URL with `card_checkout_not_on_pay`.
|
|
390
|
-
On local builds that list `purchase_create`,
|
|
391
|
-
`purchase_retrieve` and `purchase_cancel`, the protected card preview uses the
|
|
392
|
-
host's checkout tools and the existing eligible card and agent identity. Follow
|
|
393
|
-
`next`: relay the single owner setup or limits URL, let the owner act, then repeat
|
|
394
|
-
the same purchase call. Never ask for funding-card secrets in chat. Use an already returned `purchase.card` under the owner’s disclosure consent; otherwise follow the explicit retrieval action. Explicit
|
|
395
|
-
`include:"card"` may put the issued purchase credential in model/chat history,
|
|
396
|
-
under the owner's disclosure consent. A saved card or tester flag alone does
|
|
397
|
-
not establish eligibility or merchant credential compatibility. UCP discovery
|
|
398
|
-
and preparation remain separate from payment execution; a `not_ready` handoff
|
|
399
|
-
is not permission to pay or to revive the retired browser flow.
|
|
400
|
-
|
|
401
|
-
## Payment outcomes and recovery
|
|
402
|
-
|
|
403
|
-
<!-- recovery-guidance: ownerAction, timeout, identity -->
|
|
404
|
-
|
|
405
|
-
Follow the returned code, stage, remedy and owner action. The specific nextAction takes precedence over kind. Platform needs human action or diagnosis. Show the exact approval link once; continue the same payment only when nextAction says so. On denial or expiry, follow its stop or recovery action. Escalate unclassified errors. A timeout does not prove nothing was signed or paid. Keep the same agent and purchase identity: wallet `economicIntentId` and exact arguments, card `resumeId`/`purchaseId`, or an app’s `idempotencyKey`/`draftId`. Never retry uncertainty under a new identity.
|
|
406
|
-
<!-- /recovery-guidance -->
|
|
407
|
-
|
|
408
|
-
<!-- recovery-guidance: localOutcomes -->
|
|
409
|
-
|
|
410
|
-
| Returned code or outcome | Action |
|
|
411
|
-
| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
412
|
-
| `pending_activation` | Owner finishes the existing connection in the Console. |
|
|
413
|
-
| `spending_limit_required`, `grant_expired` | Owner sets or renews this agent’s limits; no re-pair. |
|
|
414
|
-
| `agent_paused`, `agent_revoked` | Owner resumes the paused agent; a revoked agent cannot pay again. Do not bypass the refusal; reconnecting does not bypass it. |
|
|
415
|
-
| `device_key_replaced` | Follow the returned connection action for this computer. |
|
|
416
|
-
| `owner_approval_required`, `wallet_owner_approval_required` | Show the exact returned approval link, then continue the same purchase after approval. |
|
|
417
|
-
| `owner_declined` | Stop; never retry automatically. |
|
|
418
|
-
| `owner_signature_unconfirmed`, `outcome_unknown`, `unreachable`, `malformed_response` | Read/reconcile the original request. Preserve its identity and token; do not create a replacement. |
|
|
419
|
-
| `cancellation_pending` | Retrieve the same purchase; liability is unresolved, not released. |
|
|
420
|
-
| `delivered` or `reconciling` | Check the recorded financial outcome; delivery is not settlement proof. Never pay the item again. |
|
|
421
|
-
|
|
422
|
-
<!-- /recovery-guidance -->
|
|
423
|
-
|
|
424
|
-
<!-- recovery-guidance: approval -->
|
|
425
|
-
|
|
426
|
-
Read the actual approval mode. In an app, `authority: owner_autonomy` can pay within the owner’s limits. `owner_approval_required` needs the returned owner link. A connected app does not need local `agent_enroll` or `visa connect`.
|
|
427
|
-
<!-- /recovery-guidance -->
|
|
428
|
-
|
|
429
|
-
For agents on the owner’s computer:
|
|
430
|
-
|
|
431
|
-
<!-- recovery-guidance: localOutput -->
|
|
432
|
-
|
|
433
|
-
Save output in an owner-controlled local file; re-read the local saved result.
|
|
434
|
-
<!-- /recovery-guidance -->
|
|
435
|
-
|
|
436
|
-
For apps:
|
|
437
|
-
|
|
438
|
-
<!-- recovery-guidance: missingOutput, noRefetch -->
|
|
439
|
-
|
|
440
|
-
If payment is confirmed but the result is missing (“Paid; result not received. Do not pay again”), tell the owner in one line that it was paid and nothing came back, with the original purchase id. Never ask the merchant again for a purchased result, even for GET requests.
|
|
441
|
-
<!-- /recovery-guidance -->
|
|
442
|
-
|
|
443
|
-
## Retired mesh integration
|
|
444
|
-
|
|
445
|
-
Subway messaging and `visa register` are retired. Pairing grants no directory or
|
|
446
|
-
messaging authority. Do not attempt mesh registration or improvise another transport.
|
|
447
|
-
|
|
448
|
-
## Optional agent mailbox (separate from setup)
|
|
449
|
-
|
|
450
|
-
Connecting an agent does not provision an email address or inbox, and no Visa tool
|
|
451
|
-
connects or reads one. There is no terminal command for this either. Do not claim setup
|
|
452
|
-
set up mail, and do not improvise a mailbox. A mailbox would grant an email address and
|
|
453
|
-
nothing more: it never authorizes a payment.
|
|
454
|
-
|
|
455
|
-
## Optional checkout profile (separate from setup)
|
|
456
|
-
|
|
457
|
-
Do not collect a checkout profile to revive Visa browser checkout: it
|
|
458
|
-
is retired. On local builds that list `purchase_create`, `purchase_retrieve` and
|
|
459
|
-
`purchase_cancel`, the protected card preview reuses this identity and verified
|
|
460
|
-
card. The host supplies checkout and billing details, owns browser submission,
|
|
461
|
-
and follows the returned `next` action. Fixed protocol fields and `requestKey`
|
|
462
|
-
may be omitted; retain the same checkout reference and terms on retry. Explicit
|
|
463
|
-
`include:"card"` returns issued purchase credentials under owner consent and may
|
|
464
|
-
put them in model/chat history. Never request original funding-card details in
|
|
465
|
-
chat, bypass eligibility, or retry an uncertain merchant submission. Existing
|
|
466
|
-
`~/.visa-mcp/contact.json` files use the format below; preserve them with mode `0600`.
|
|
467
|
-
Never infer or invent identity or address data.
|
|
468
|
-
|
|
469
|
-
No Visa tool writes this profile; the host passes checkout and billing details to
|
|
470
|
-
`purchase_create` itself. Any shell that reads an existing profile must have the exact
|
|
471
|
-
same `HOME` and `VISA_CLI_HOME` as the MCP subprocess. A profile found under another root
|
|
472
|
-
is owner PII, not a migration candidate: never scan, copy, or auto-adopt it. If the roots
|
|
473
|
-
drifted, keep the root holding the paired identity.
|
|
474
|
-
|
|
475
|
-
```jsonc
|
|
476
|
-
{
|
|
477
|
-
"fullName": "Ada Lovelace",
|
|
478
|
-
"email": "ada@example.com",
|
|
479
|
-
"addressLine1": "1 Analytical Way",
|
|
480
|
-
"addressLine2": "",
|
|
481
|
-
"city": "San Francisco",
|
|
482
|
-
"state": "CA",
|
|
483
|
-
"postalCode": "94105",
|
|
484
|
-
"country": "US",
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
`fullName` must be non-blank; `firstName` plus `lastName` is also accepted. The engine reads
|
|
489
|
-
the exact keys `fullName`, `firstName`, `lastName`, `email`, `addressLine1`, `addressLine2`,
|
|
490
|
-
`city`, `state`, `postalCode`, and `country`. The profile supplies checkout/cardholder and
|
|
491
|
-
billing data only. Its `email` value is not the account's verified owner email, an agent
|
|
492
|
-
mailbox, key proof, recovery factor, or permission to spend.
|
|
493
|
-
|
|
494
|
-
## Security rules
|
|
495
|
-
|
|
496
|
-
- Never read, print, log, paste, or transmit the private Ed25519 JWK or any local claim
|
|
497
|
-
token.
|
|
498
|
-
- Never read or echo local pending files. Present only the URL returned by the tool.
|
|
499
|
-
- Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
|
|
500
|
-
persistence.
|
|
501
|
-
- Never fetch or submit the review URL on the human's behalf. They review and approve it
|
|
502
|
-
in their own browser.
|
|
503
|
-
- Never invent a secondary path when a call fails. Preserve the local state, surface the
|
|
504
|
-
error, and resume through the same protected entrance: call `agent_enroll` again, or
|
|
505
|
-
repeat the same `visa connect` command.
|
|
506
|
-
- Never call a retired door to "check whether it still works". `legacy_door_removed` is a
|
|
507
|
-
final answer, not a transient failure.
|
|
508
|
-
|
|
509
|
-
## Further docs
|
|
510
|
-
|
|
511
|
-
- `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
|
|
512
|
-
- `docs/agents/ARCHITECTURE.md` — where enrolment sits in the v4 request paths.
|
|
513
|
-
- `visacli.sh/agents` — product-facing agent documentation.
|
|
514
|
-
|
|
515
|
-
### Resuming after a local setup timeout
|
|
516
|
-
|
|
517
|
-
A local timeout is not proof of server expiry. After a plain resume, relay any new `browserUrl` and `confirmCode` returned: an unbound request the server has definitively refused can be replaced automatically. `enrollment_expired` means an already-bound request needs an explicit restart. A transient or ambiguous failure preserves the same request; do not use `restart: true` merely because a request timed out.
|