@visa/cli 4.1.0-rc.225 → 4.1.0-rc.227
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/dist/cli.js +359 -359
- package/dist/mcp-server/index.js +248 -248
- package/dist/skills/pair-visa-agent/RUNTIMES.md +31 -26
- package/dist/skills/pair-visa-agent/SKILL.md +283 -330
- package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -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 install it if missing — plus network access to the Visa authorization service. Works in OpenClaw, Hermes, or any Agent Skills runtime.
|
|
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
|
|
@@ -27,45 +27,52 @@ metadata:
|
|
|
27
27
|
- visa-cli
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# Connect a Visa agent
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
- **`enroll_agent` with `action: "start"` / `action: "claim"`** — the `visa` MCP server tool.
|
|
37
|
-
- **`visa agent enroll --format json` / `visa agent enroll-claim --format json`** — the raw
|
|
38
|
-
CLI and universal fallback.
|
|
36
|
+
**The whole flow is two tool calls and, for the human, one or two clicks.**
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
+
```
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
stderr frame). Never assume the tool from documentation alone: check the served list.
|
|
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:
|
|
52
55
|
|
|
53
|
-
|
|
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.
|
|
54
61
|
|
|
55
|
-
|
|
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.
|
|
56
72
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- `identityKeyJkt` is the thumbprint of the currently bound Ed25519 public key. It can
|
|
61
|
-
change when that key rotates, so it must never be presented or stored as the stable
|
|
62
|
-
agent identifier.
|
|
63
|
-
- The human receives the authorization URL, stable agent ID, and full public request-key
|
|
64
|
-
fingerprint for exact comparison. Private key material, the local claim token, and signed
|
|
65
|
-
protocol messages stay with the runtime.
|
|
66
|
-
- An `activated` result means **identity paired only**. Payment methods, an email address,
|
|
67
|
-
and tap bindings are separate configuration that may be added later. Do not infer any
|
|
68
|
-
of those capabilities from pairing success.
|
|
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.
|
|
69
76
|
|
|
70
77
|
## Getting this skill
|
|
71
78
|
|
|
@@ -91,7 +98,7 @@ OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
|
|
|
91
98
|
|
|
92
99
|
1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
|
|
93
100
|
idempotent provisioner `node scripts/setup.mjs`. The `@latest` tag may not yet expose
|
|
94
|
-
the v4
|
|
101
|
+
the v4 setup tools. Installation puts `visa` and `visa-cli` on `PATH` and includes
|
|
95
102
|
`@visa/cli/dist/mcp-server/index.js`.
|
|
96
103
|
2. **Mount the MCP server when the runtime supports MCP.**
|
|
97
104
|
- **OpenClaw:** installing `@visa/visa-cli-openclaw` auto-mounts the server by writing
|
|
@@ -99,10 +106,10 @@ OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
|
|
|
99
106
|
- **Hermes or another supported runtime:** run `visa-cli connect hermes` or
|
|
100
107
|
`visa-cli connect <runtime>`. For Hermes, the entry is written under `mcp_servers`
|
|
101
108
|
in `~/.hermes/config.yaml`.
|
|
102
|
-
- **No MCP integration:** use the raw `visa
|
|
103
|
-
3. **Sign the owner in
|
|
104
|
-
|
|
105
|
-
4. **
|
|
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.
|
|
106
113
|
|
|
107
114
|
## MCP mounting examples
|
|
108
115
|
|
|
@@ -158,325 +165,271 @@ live registry; never derive them from the config key.
|
|
|
158
165
|
In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
|
|
159
166
|
existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
|
|
160
167
|
|
|
161
|
-
##
|
|
168
|
+
## Sign in first — the wallet rail is owner-bound
|
|
162
169
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
- Raw CLI commands support `--format json`.
|
|
167
|
-
- `visa agent enroll-claim` exits `0` when activated, `3` when the human has not finished,
|
|
168
|
-
and `1` on a terminal failure.
|
|
169
|
-
|
|
170
|
-
The start result includes protocol version `2`, `pairingId`, stable `agentId`, full
|
|
171
|
-
`requestKeyFingerprint`, `browserUrl`, and expiry information. Newer builds also return
|
|
172
|
-
`confirmationCode`, a short public code the browser review page displays for comparison.
|
|
173
|
-
Present it when the field is present and skip it when it is absent; an older server or CLI
|
|
174
|
-
simply omits it, which is not an error. The activation result includes protocol version
|
|
175
|
-
`2`, `pairingId`, stable `agentId`, the display name, and `identityKeyJkt`.
|
|
176
|
-
|
|
177
|
-
## Sign in first — pairing is owner-bound
|
|
178
|
-
|
|
179
|
-
Pairing starts are authenticated as the owner: the service pins the owner account at
|
|
180
|
-
initiation, and the browser review page rejects any other signed-in account
|
|
181
|
-
(`owner_profile_mismatch`). So on a fresh runtime the ceremony has a mandatory first leg —
|
|
182
|
-
establish the owner session **before** any runtime-initiated pairing command (Console handoff codes
|
|
183
|
-
are owner-pinned by the code itself and do NOT require an active owner session):
|
|
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`:
|
|
184
173
|
|
|
185
174
|
- [ ] Call `agent_login` (MCP, default action `"start"`) or run
|
|
186
175
|
`visa agent login --format json` (CLI). The result carries a sign-in `browserUrl`
|
|
187
|
-
and a short 6-character
|
|
176
|
+
and a short 6-character `confirmCode`.
|
|
188
177
|
- [ ] Relay **both** to the human in your reply — the bare URL on its own line, and the
|
|
189
178
|
code. You are very often not in a terminal they can see; the chat message is the
|
|
190
179
|
only place these values reach them.
|
|
191
180
|
- [ ] The human opens the link, signs in (Google or email), and **types the confirmation
|
|
192
181
|
code into the sign-in page** — into the browser, never back to you in chat.
|
|
193
182
|
- [ ] Claim the session: `agent_login {"action":"claim"}` (the CLI command polls on its
|
|
194
|
-
own). Once claimed, the session token is stored locally
|
|
183
|
+
own). Once claimed, the session token is stored locally.
|
|
195
184
|
|
|
196
|
-
If a
|
|
197
|
-
|
|
198
|
-
|
|
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.
|
|
199
188
|
|
|
200
|
-
The account that signs in
|
|
201
|
-
|
|
202
|
-
|
|
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.
|
|
203
192
|
|
|
204
|
-
##
|
|
205
|
-
|
|
206
|
-
If the `visa` CLI is on PATH, prefer the one-shot command — after the owner session above
|
|
207
|
-
exists, it does the whole flow from a single call, so the user types nothing after their
|
|
208
|
-
initial request:
|
|
209
|
-
|
|
210
|
-
```
|
|
211
|
-
visa agent pair --format json
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
On a runtime with no owner session it fails with "An owner profile is required before
|
|
215
|
-
pairing" — run the sign-in leg above first, then re-run it.
|
|
216
|
-
|
|
217
|
-
It returns `browserUrl`, `pairingId`, stable `agentId`, the full public
|
|
218
|
-
`requestKeyFingerprint`, and — on newer builds — `confirmationCode` **immediately**. It
|
|
219
|
-
starts a detached activation process only after the pending identity and private key are
|
|
220
|
-
durable. Present the link, returned confirmation code, and fingerprint for browser
|
|
221
|
-
comparison; omit only a confirmation code the result did not provide. When driving `visa agent pair --format json` and `claiming` is `background`,
|
|
222
|
-
activation completes after approval without another command; if it is `manual`, run the
|
|
223
|
-
returned `recoveryCommand` after approval. Use the step-by-step flow
|
|
224
|
-
below when `pair` is unavailable or when driving the plugin/MCP tools — `enroll_agent` and `visa agent enroll`
|
|
225
|
-
are explicit two-step flows (`start` returns `state: "needs_owner"` and `nextAction: {tool: "enroll_agent", action: "claim"}`; `claim` completes activation after human approval). There is no detached background poller for `enroll_agent` or `visa agent enroll`.
|
|
226
|
-
|
|
227
|
-
## Returning & already-connected — do NOT re-pair
|
|
228
|
-
|
|
229
|
-
If `pair`/`enroll` returns **`already_connected`** (or `alreadyConnected: true`), this
|
|
230
|
-
device is **already set up** as an agent — a fresh pairing is neither needed nor
|
|
231
|
-
possible (re-pairing an existing identity silently dead-ends). Do this instead:
|
|
232
|
-
|
|
233
|
-
1. **Tell the user plainly:** "This device is already connected as `<name>.visa`."
|
|
234
|
-
Read the name from the response. Do **not** start another hand-off. `--new` /
|
|
235
|
-
`force:true` exists only if they explicitly want a _second, separate_ agent.
|
|
236
|
-
2. **Report status honestly — "connected" is five separate things**, not one. Never
|
|
237
|
-
imply the agent can spend just because it's connected. Don't guess from prose —
|
|
238
|
-
read it live from tools: `agent_capabilities` returns the DERIVED capability map
|
|
239
|
-
(the identity + wallet + mail base plus card/tap/subway availability),
|
|
240
|
-
`get_status` reports enrollment / account / version, and `agent_login` establishes
|
|
241
|
-
or confirms the account session that spending grants require.
|
|
242
|
-
- **Identity** — connected (`.visa` name bound to _this user's_ account). ✓ once `already_connected`.
|
|
243
|
-
- **Spending** — a _separate human approval_, and there are two rails, each
|
|
244
|
-
approved on the **v4 agent dashboard** (`app.visacli.sh/agent/enroll`) — not any
|
|
245
|
-
account-settings page (that legacy surface is retired). The Turnkey
|
|
246
|
-
**wallet:x402** rail (stablecoin) is approved with the owner's **sign-in session**
|
|
247
|
-
(the Google/email auth-proxy) — **no passkey**. The **card:vic** rail is approved
|
|
248
|
-
with a **passkey**. You **cannot** self-grant either; there is no
|
|
249
|
-
`visa agent add-rail` command, and never self-mint a wallet with `wallet_init` on
|
|
250
|
-
mainnet — it throws until the owner's delegation lands.
|
|
251
|
-
- **Mesh (`.visa` messaging)** — with `SUBWAY_MESH=visa`, a returning device now
|
|
252
|
-
registers on `pair`; if `meshRegistered` is false, `visa register <name>` joins it.
|
|
253
|
-
- **Trusted (TAP)** — follows spend/provisioning; don't promise it before then.
|
|
254
|
-
3. **Scope everything to the user.** The identity is bound to the account the user
|
|
255
|
-
signed in with (their email); the wallet + spend limits are theirs. Speak in terms
|
|
256
|
-
of "your agent / your account / the limits you approved," never a shared identity.
|
|
257
|
-
|
|
258
|
-
Missing **spend** blocks _checkout mandates_, not a delegated x402 wallet — but that
|
|
259
|
-
wallet must first be acquired by owner approval (below); it is never self-minted.
|
|
260
|
-
|
|
261
|
-
## What you can do once paired — granting spend authority (card or wallet)
|
|
262
|
-
|
|
263
|
-
Pairing binds **identity only**. To let this agent pay, the human owner must **delegate**
|
|
264
|
-
a spend rail — the runtime never self-mints one. There are two rails, and **one** ceremony
|
|
265
|
-
drives both:
|
|
266
|
-
|
|
267
|
-
- **`wallet:x402`** (Turnkey stablecoin) — owner approves with their **sign-in session, no
|
|
268
|
-
passkey**. On mainnet `wallet_init` throws `WalletCredentialRequiredError` until this
|
|
269
|
-
delegation lands, so never call it as a setup step.
|
|
270
|
-
- **`card:vic`** (Visa card checkout) — owner approves with a **passkey**.
|
|
271
|
-
|
|
272
|
-
The contract is identical for both rails, and it is **one command from you, one approval
|
|
273
|
-
from the owner**. You never hand a command, code, or URL back to the human after they
|
|
274
|
-
approve — you poll to completion yourself:
|
|
275
|
-
|
|
276
|
-
1. **You run one command** naming the rail and caps.
|
|
277
|
-
2. **The owner opens the returned link and approves once** (they may adjust the amount).
|
|
278
|
-
3. **You poll to activation** — the owner does nothing further.
|
|
279
|
-
|
|
280
|
-
- [ ] **Pair** the identity (the flow above). Identity only — no rail yet.
|
|
281
|
-
- [ ] **Establish the owner session once.** Grant creation is an account operation, so this
|
|
282
|
-
runtime needs a live owner session: `agent_login` (MCP) or `visa agent login` (CLI).
|
|
283
|
-
If you just paired, the session from the sign-in-first leg is usually still live —
|
|
284
|
-
no re-login needed. This is a short-lived session established once — **not** a
|
|
285
|
-
re-pair, and **not** something the owner repeats per payment. If `agent_connect`
|
|
286
|
-
later returns `{"code":"session_required"}`, the session lapsed — run `agent_login`
|
|
287
|
-
again.
|
|
288
|
-
- [ ] **Initiate the grant from MCP — no shelling, no invented URLs.** Call `agent_connect`
|
|
289
|
-
with the rail, caps, and exact `agentId` returned by pairing:
|
|
290
|
-
`{"agentId":"<agentId>","rail":"card","ceiling":"<usd>","perTransaction":"<usd>"}`
|
|
291
|
-
(or `"rail":"wallet"`; `agentId` is optional only when exactly one eligible paired agent
|
|
292
|
-
exists on this runtime). It returns `{ url, code, attachId, willGrant, expiresAt }`. Present the
|
|
293
|
-
**bare `url` and `code` exactly as returned** — never construct, shorten, or guess a Visa URL, and
|
|
294
|
-
never open it yourself. The crypto approval happens in the owner's browser and cannot run
|
|
295
|
-
inline in chat. (`setup_agent {"rail":"card"|"wallet"}` returns the same next-step map
|
|
296
|
-
if you need it.)
|
|
297
|
-
- [ ] **Owner approves once** on the **v4 agent dashboard** (`app.visacli.sh/agent/enroll`),
|
|
298
|
-
confirming the caps — **card with a passkey, wallet with their sign-in session**. You
|
|
299
|
-
cannot approve on their behalf.
|
|
300
|
-
- [ ] **Poll to activation from MCP.** Call `agent_connect_poll` (`{"attachId":"<from
|
|
301
|
-
agent_connect>"}`; or resume by `agentId`) — one bounded poll per call. It returns
|
|
302
|
-
`{"ok":false,"state":"...","blockedByKind":"awaiting_human_approval"}` while pending;
|
|
303
|
-
call again until `{"ok":true,"state":"grant_activated","caps":...}` (wallet also
|
|
304
|
-
returns `fundAddress`). Activation registers the delegated signer + caps: wallet writes
|
|
305
|
-
the Turnkey credential (`turnkey.json`); card writes the card pointer. It never spends.
|
|
306
|
-
- [ ] **Confirm the rail is live.** Wallet: `wallet_status` / `visa wallet show`
|
|
307
|
-
(`agent_capabilities.wallet.available`). Card: `agent_capabilities.card.available`.
|
|
308
|
-
Only a delegated credential — not the served tool list — means the rail is usable.
|
|
309
|
-
- [ ] **Then spend against the owner-approved caps.** Wallet: set the policy with
|
|
310
|
-
`wallet_policy_set` (per-transaction / daily / session USD caps + optional network and
|
|
311
|
-
merchant allow/deny lists that refuse an x402 payment BEFORE it is signed), then
|
|
312
|
-
`wallet_pay`. Card: `start_card_mandate` then `pay_merchant`. Never raise a
|
|
313
|
-
human-approved limit yourself.
|
|
314
|
-
|
|
315
|
-
**Raw CLI equivalent (one shot).** If you cannot drive MCP, the same ceremony runs from the
|
|
316
|
-
CLI and `--wait` polls to activation in a single call. Present the URL it prints — don't
|
|
317
|
-
invent one:
|
|
318
|
-
|
|
319
|
-
```
|
|
320
|
-
visa agent grant-card <agentId> --ceiling <usd> --per-transaction <usd> --wait
|
|
321
|
-
visa agent grant-wallet <agentId> --ceiling <usd> --per-transaction <usd> --wait
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Rail selection is the owner's call — wallet only, card only, or both. Newer builds also
|
|
325
|
-
accept one compound command that runs the same ceremony per rail in sequence
|
|
326
|
-
(`both` = card, then wallet, under the same caps — expect one extra approval trip):
|
|
327
|
-
|
|
328
|
-
```
|
|
329
|
-
visa agent connect --authority <card|wallet|both> --ceiling <usd> --per-transaction <usd> --wait
|
|
330
|
-
```
|
|
193
|
+
## Core flow
|
|
331
194
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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.
|
|
335
248
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
`wallet_fund` (funding address + faucet), and `wallet_export` (export key material —
|
|
341
|
-
dangerous). All spending is gated by the owner-approved local policy caps.
|
|
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.
|
|
342
253
|
|
|
343
|
-
|
|
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.**
|
|
344
258
|
|
|
345
|
-
|
|
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".
|
|
346
262
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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.
|
|
351
269
|
|
|
352
|
-
|
|
353
|
-
- **MCP:** `agent_connect_cancel { attachId: "<attachId>" }` or `agent_connect { action: "cancel", attachId: "<attachId>" }`.
|
|
354
|
-
- **CLI:** `visa agent grant-cancel <attachId>`.
|
|
355
|
-
- Requires the immutable `attachId` (`ca_...`). Releases the owner-side hold and purges local pending files so a fresh grant ceremony can be started immediately. Replaying returns `already_terminal` / `noop`. Subsequent polls report `rejected` without resurrecting local state.
|
|
270
|
+
### The human's clicks, and what they are for
|
|
356
271
|
|
|
357
|
-
|
|
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.
|
|
358
278
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
`visa agent enroll --format json`.
|
|
363
|
-
- [ ] Present the authorization URL, stable agent ID, full public fingerprint, and the
|
|
364
|
-
`confirmationCode` when the start result carries one, to the human.
|
|
365
|
-
- [ ] Poll with `pair_agent_poll`, `enroll_agent` action `claim`, or
|
|
366
|
-
`visa agent enroll-claim --format json`.
|
|
367
|
-
- [ ] Report the stable `agentId` and that the identity is paired on this runtime.
|
|
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.
|
|
368
282
|
|
|
369
|
-
### Completion is `
|
|
283
|
+
### Completion is what `readiness` says — nothing else
|
|
370
284
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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.
|
|
374
290
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
though it were a finished pairing is the most likely way to mislead the human here, because
|
|
379
|
-
the number looks like a result.
|
|
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.
|
|
380
294
|
|
|
381
|
-
|
|
382
|
-
should do next. An honest "approved but not yet activated — I'm still polling" is correct;
|
|
383
|
-
"you're all set" without an `activated` result is not.
|
|
295
|
+
On success, report the agent's name and the rails that are actually ready:
|
|
384
296
|
|
|
385
|
-
|
|
297
|
+
> Visa agent <agent.name> is connected. Ready to pay by <card and/or USDC wallet>, within
|
|
298
|
+
> the limits you approved.
|
|
386
299
|
|
|
387
|
-
|
|
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.
|
|
388
302
|
|
|
389
|
-
|
|
390
|
-
builds — `confirmationCode`. Show the URL as a bare, tappable value on its own line. Do not
|
|
391
|
-
decorate it as a Markdown link or put it in a code span; chat clients reliably recognize the
|
|
392
|
-
bare URL. Show the complete fingerprint without truncation and tell the human to approve
|
|
393
|
-
only when every character matches the browser review page.
|
|
303
|
+
## Interruption and resume
|
|
394
304
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
step exists to prevent.
|
|
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.
|
|
399
308
|
|
|
400
|
-
|
|
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.
|
|
401
316
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
>
|
|
406
|
-
> Stable agent ID: <agentId>
|
|
407
|
-
>
|
|
408
|
-
> Confirmation code — this exact code should appear on the page:
|
|
409
|
-
> <confirmationCode>
|
|
410
|
-
>
|
|
411
|
-
> Public request-key fingerprint — compare every character in the browser:
|
|
412
|
-
> <requestKeyFingerprint, complete and untruncated>
|
|
413
|
-
>
|
|
414
|
-
> Approve only if the code and fingerprint both match. The link expires shortly — I'll keep
|
|
415
|
-
> watching and confirm here the moment it activates.
|
|
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.
|
|
416
320
|
|
|
417
|
-
|
|
418
|
-
never invent, derive, abbreviate, or reformat one.
|
|
321
|
+
## Raw CLI equivalent
|
|
419
322
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
anything they type back as approval. The URL, stable agent ID, confirmation code, and public
|
|
423
|
-
fingerprint are review values the human checks against their own authenticated browser
|
|
424
|
-
session. They are not claim credentials, they cannot approve a pairing, and they cannot
|
|
425
|
-
spend. Approval happens only in that browser session, and the only evidence of it is a poll
|
|
426
|
-
result of `activated`.
|
|
323
|
+
If MCP is unavailable, the same ceremony runs from the shell. The name is a positional
|
|
324
|
+
argument:
|
|
427
325
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
+
```
|
|
433
333
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
confirm the outcome. When driving `enroll_agent` or `visa agent enroll`, there is no detached poller: calling `enroll_agent` with `{"action":"claim"}` (or `visa agent enroll-claim`) is required to complete the pairing and write the local credential.
|
|
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.
|
|
437
336
|
|
|
438
|
-
|
|
337
|
+
## The human already created this agent in the Console
|
|
439
338
|
|
|
440
|
-
|
|
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.
|
|
441
342
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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.
|
|
450
383
|
|
|
451
|
-
|
|
384
|
+
## Adding a rail to an agent that already exists
|
|
452
385
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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":
|
|
456
389
|
|
|
457
|
-
|
|
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.
|
|
458
406
|
|
|
459
|
-
|
|
407
|
+
Shell equivalents, which poll to activation in one call:
|
|
460
408
|
|
|
461
|
-
|
|
462
|
-
|
|
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
|
+
```
|
|
463
413
|
|
|
464
|
-
|
|
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.
|
|
465
417
|
|
|
466
|
-
|
|
467
|
-
or response is interrupted, the next poll resumes the exact same `pairingId`, `agentId`,
|
|
468
|
-
Ed25519 identity, and claim material. This makes an identical retry safe and avoids
|
|
469
|
-
creating a second identity because a response was lost.
|
|
418
|
+
## Spending, once a rail is live
|
|
470
419
|
|
|
471
|
-
|
|
472
|
-
|
|
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.
|
|
473
428
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
- Do not regenerate keys for an existing pairing.
|
|
477
|
-
- Do not copy pending state between runtimes.
|
|
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.
|
|
478
431
|
|
|
479
|
-
## Optional `.visa` mesh binding (separate from
|
|
432
|
+
## Optional `.visa` mesh binding (separate from setup)
|
|
480
433
|
|
|
481
434
|
Pairing does not register a `.visa` name, TAP key, or Subway peer. If the operator has
|
|
482
435
|
separately enabled and registered those channel bindings, the mounted `visa-cli` MCP server
|
|
@@ -484,40 +437,40 @@ may expose `subway_register`, `subway_send`, `subway_inbox`, and `subway_find`.
|
|
|
484
437
|
|
|
485
438
|
Mesh messaging is available only on a compatible RC/dev build with `SUBWAY_MESH=visa` and
|
|
486
439
|
a reachable `SUBWAY_RELAY_MULTIADDR`. If those tools or the relay are unavailable, report
|
|
487
|
-
that clearly and stop. Do not imply that
|
|
440
|
+
that clearly and stop. Do not imply that connecting an agent granted directory or
|
|
488
441
|
messaging authority, and do not improvise another transport.
|
|
489
442
|
|
|
490
|
-
## Optional agent mailbox (separate from
|
|
443
|
+
## Optional agent mailbox (separate from setup)
|
|
491
444
|
|
|
492
|
-
|
|
445
|
+
Connecting an agent does not provision an email address or inbox. If the agent needs a
|
|
493
446
|
mailbox — e.g. to receive a merchant's account-signup or one-time-code email —
|
|
494
|
-
connect one explicitly, from the
|
|
447
|
+
connect one explicitly, from the connected runtime, with the raw CLI:
|
|
495
448
|
|
|
496
449
|
```
|
|
497
450
|
visa agent mail-connect <agentId>
|
|
498
451
|
```
|
|
499
452
|
|
|
500
453
|
This is CLI-only; no pairing step or MCP tool connects a mailbox. It requires an
|
|
501
|
-
already-
|
|
454
|
+
already-connected stable-agent identity on this runtime — it reads the local agent
|
|
502
455
|
record and proves the Ed25519 identity to the service. It issues the stable
|
|
503
456
|
agent mailbox if one does not exist, then stores an inbox-scoped credential in an
|
|
504
457
|
owner-only `0600` runtime file so this runtime can read that one inbox.
|
|
505
458
|
|
|
506
459
|
Be honest about scope. A mailbox grants an email address and the ability to read
|
|
507
460
|
that inbox — nothing more. It is **not** identity, a wallet, spend authority, a
|
|
508
|
-
card, or a `.visa` name, and it never authorizes a payment. Do not claim
|
|
461
|
+
card, or a `.visa` name, and it never authorizes a payment. Do not claim setup
|
|
509
462
|
set up mail. Once connected, the MCP `wallet_mail_status`, `wallet_mail_read`,
|
|
510
463
|
and `wallet_mail_await_otp` tools read the inbox (e.g. to await a sender-checked
|
|
511
464
|
one-time code); without the scoped credential those reads fail closed. Keep the
|
|
512
465
|
org-wide AgentMail key off the runtime — provisioning happens only through
|
|
513
466
|
`mail-connect` under operator control.
|
|
514
467
|
|
|
515
|
-
## Optional checkout profile (separate from
|
|
468
|
+
## Optional checkout profile (separate from setup)
|
|
516
469
|
|
|
517
|
-
|
|
518
|
-
authority and
|
|
519
|
-
|
|
520
|
-
|
|
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`.
|
|
521
474
|
|
|
522
475
|
```jsonc
|
|
523
476
|
{
|
|
@@ -540,18 +493,18 @@ mailbox, key proof, recovery factor, or permission to spend.
|
|
|
540
493
|
|
|
541
494
|
## Security rules
|
|
542
495
|
|
|
543
|
-
- Never read, print, log, paste, or transmit the private Ed25519 JWK or local claim
|
|
544
|
-
|
|
545
|
-
|
|
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.
|
|
546
499
|
- Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
|
|
547
500
|
persistence.
|
|
548
|
-
- Never fetch the
|
|
549
|
-
|
|
550
|
-
- Never invent a secondary
|
|
551
|
-
|
|
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 `setup_status` / `setup_resume`.
|
|
552
505
|
|
|
553
506
|
## Further docs
|
|
554
507
|
|
|
555
508
|
- `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
|
|
556
|
-
- `docs/agents/ARCHITECTURE.md` — where
|
|
509
|
+
- `docs/agents/ARCHITECTURE.md` — where setup sits in the v4 request paths.
|
|
557
510
|
- `visacli.sh/agents` — product-facing agent documentation.
|