@visa/cli 4.1.0-rc.21 → 4.1.0-rc.211
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 +200 -226
- package/dist/checkout-engine/adapters/generic.d.ts +69 -0
- package/dist/checkout-engine/adapters/generic.js +383 -58
- package/dist/checkout-engine/adapters/index.d.ts +4 -1
- package/dist/checkout-engine/adapters/index.js +10 -3
- package/dist/checkout-engine/adapters/shopify.d.ts +55 -0
- package/dist/checkout-engine/adapters/shopify.js +514 -0
- package/dist/checkout-engine/amount.d.ts +15 -0
- package/dist/checkout-engine/amount.js +72 -0
- package/dist/checkout-engine/cli-engine.d.ts +264 -4
- package/dist/checkout-engine/cli-engine.js +803 -43
- package/dist/checkout-engine/confirmed-merchants.d.ts +31 -0
- package/dist/checkout-engine/confirmed-merchants.js +165 -0
- package/dist/checkout-engine/detect.d.ts +1 -1
- package/dist/checkout-engine/detect.js +26 -0
- package/dist/checkout-engine/evidence.d.ts +4 -1
- package/dist/checkout-engine/evidence.js +51 -6
- package/dist/checkout-engine/executor.d.ts +47 -4
- package/dist/checkout-engine/executor.js +418 -131
- package/dist/checkout-engine/hosted-approval.d.ts +124 -7
- package/dist/checkout-engine/hosted-approval.js +381 -54
- package/dist/checkout-engine/index.d.ts +8 -2
- package/dist/checkout-engine/index.js +7 -1
- package/dist/checkout-engine/instrument.d.ts +7 -0
- package/dist/checkout-engine/instrument.js +4 -0
- package/dist/checkout-engine/known-merchants.d.ts +10 -0
- package/dist/checkout-engine/known-merchants.js +38 -0
- package/dist/checkout-engine/live-fill-approval.d.ts +5 -20
- package/dist/checkout-engine/live-fill-approval.js +20 -51
- package/dist/checkout-engine/mandate/card-mandate.d.ts +121 -0
- package/dist/checkout-engine/mandate/card-mandate.js +226 -0
- package/dist/checkout-engine/mandate/mandate-ledger.d.ts +174 -0
- package/dist/checkout-engine/mandate/mandate-ledger.js +410 -0
- package/dist/checkout-engine/outcome.d.ts +2 -2
- package/dist/checkout-engine/outcome.js +36 -1
- package/dist/checkout-engine/owner-only-file.d.ts +9 -0
- package/dist/checkout-engine/owner-only-file.js +20 -1
- package/dist/checkout-engine/receipt-dir.d.ts +6 -0
- package/dist/checkout-engine/receipt-dir.js +8 -0
- package/dist/checkout-engine/receipt.d.ts +42 -2
- package/dist/checkout-engine/receipt.js +43 -14
- package/dist/checkout-engine/trace-handles.d.ts +8 -0
- package/dist/checkout-engine/trace-handles.js +12 -0
- package/dist/checkout-engine/types.d.ts +28 -2
- package/dist/checkout-engine/unresolved-charges.d.ts +34 -0
- package/dist/checkout-engine/unresolved-charges.js +125 -0
- package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +53 -1
- package/dist/checkout-engine/vgs-gateway/server-mint-client.js +78 -10
- package/dist/checkout-engine/vgs-live-instrument.d.ts +38 -35
- package/dist/checkout-engine/vgs-live-instrument.js +51 -74
- package/dist/checkout-engine/vic-confirmation.d.ts +18 -0
- package/dist/checkout-engine/vic-confirmation.js +9 -3
- package/dist/checkout-engine/web-bot-auth.d.ts +92 -0
- package/dist/checkout-engine/web-bot-auth.js +159 -0
- package/dist/cli.js +772 -496
- package/dist/mcp-apps/ucp-checkout.html +280 -0
- package/dist/mcp-server/index.js +637 -175
- package/dist/skills/pair-visa-agent/RUNTIMES.md +93 -0
- package/dist/skills/pair-visa-agent/SKILL.md +479 -221
- package/dist/subway-direct.mjs +1 -0
- package/install.ps1 +5 -43
- package/install.sh +5 -37
- package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
- package/package.json +33 -27
- package/server.json +4 -4
- package/dist/checkout-engine/inline-target.d.ts +0 -13
- package/dist/checkout-engine/inline-target.js +0 -37
- package/dist/checkout-engine/pay-args.d.ts +0 -14
- package/dist/checkout-engine/pay-args.js +0 -44
- package/dist/checkout-engine/pay.d.ts +0 -1
- package/dist/checkout-engine/pay.js +0 -13
- package/dist/checkout-engine/repo-env.d.ts +0 -11
- package/dist/checkout-engine/repo-env.js +0 -23
- package/dist/checkout-engine/run-live-fill.d.ts +0 -1
- package/dist/checkout-engine/run-live-fill.js +0 -443
- package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
- package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pair-visa-agent
|
|
3
|
-
description: 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, the returned public confirmation code, and the full public request-key fingerprint to the human (or claim a Console handoff code directly without local owner login), 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.
|
|
9
|
+
version: '0.7.1'
|
|
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,89 @@ metadata:
|
|
|
27
27
|
- visa-cli
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
# Pair a Visa CLI v4
|
|
30
|
+
# Pair a Visa CLI v4 agent identity
|
|
31
|
+
|
|
32
|
+
Pair this runtime's locally generated identity with a human-approved Visa agent. Pairing
|
|
33
|
+
protocol v2 has one ceremony and three equivalent ways to drive it:
|
|
34
|
+
|
|
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.
|
|
39
|
+
|
|
40
|
+
Use the first surface available. Do not mix this flow with older pairing or login flows;
|
|
41
|
+
all three surfaces above wrap the same v2 enrollment ceremony and local pending state.
|
|
42
|
+
|
|
43
|
+
**Console handoff codes are a separate, compound ceremony.** When the human brings a
|
|
44
|
+
one-time code minted by the Console's Create-agent flow, claim it instead of pairing:
|
|
45
|
+
the code carries server-held, pre-approved terms and resolves identity + spending in one
|
|
46
|
+
run (card auto-activates from the mint consent; wallet still needs one browser approval).
|
|
47
|
+
The MCP tool `agent_handoff_claim` exists only on `@visa/cli` **4.1.0-rc.159 and newer**
|
|
48
|
+
— on an older CLI it is absent from the served tool list and the only path is shelling
|
|
49
|
+
`visa agent handoff-claim <code> --format json`, a different integration with different
|
|
50
|
+
failure modes (the approval URL and verification code arrive mid-run as a structured
|
|
51
|
+
stderr frame). Never assume the tool from documentation alone: check the served list.
|
|
52
|
+
|
|
53
|
+
## What pairing establishes
|
|
54
|
+
|
|
55
|
+
Pairing activates an agent identity on this runtime:
|
|
56
|
+
|
|
57
|
+
- `agentId` is the server-assigned, stable identifier for the agent.
|
|
58
|
+
- The runtime creates and retains the private Ed25519 identity key. It sends only the
|
|
59
|
+
public JWK to the service.
|
|
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.
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
through a mobile-web enrollment link. This is the device-link flow: the credential
|
|
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.
|
|
70
|
+
## Getting this skill
|
|
37
71
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
server (see Runtimes). Use whichever pairing surface your runtime exposes; they all wrap
|
|
41
|
-
the same two `visa` commands:
|
|
72
|
+
The skill ships inside the public `@visa/cli` npm package. No clone of the private
|
|
73
|
+
monorepo is required:
|
|
42
74
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
```sh
|
|
76
|
+
npm install -g @visa/cli@rc
|
|
77
|
+
visa agent skill
|
|
78
|
+
```
|
|
46
79
|
|
|
47
|
-
|
|
48
|
-
|
|
80
|
+
`visa agent skill` auto-detects OpenClaw, Hermes, Claude Code, and Codex, falling back to
|
|
81
|
+
the project-local `./.agents/skills`. Pass `--runtime <name>` or `--dir <path>` to choose a
|
|
82
|
+
target, `--force` to overwrite, or `--print` to read without writing. Reload or restart
|
|
83
|
+
the agent runtime after installation so it registers the skill.
|
|
49
84
|
|
|
50
|
-
|
|
85
|
+
Access remains enforced by the Visa service. Installing the public package or skill does
|
|
86
|
+
not authorize an account to pair.
|
|
51
87
|
|
|
52
|
-
|
|
53
|
-
(private) monorepo is required, so any agent box that can reach npm can install it:
|
|
88
|
+
OpenClaw users also receive the skill with the `@visa/visa-cli-openclaw` plugin.
|
|
54
89
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
90
|
+
## Getting set up
|
|
91
|
+
|
|
92
|
+
1. **Install the prerelease CLI.** Run `npm install -g @visa/cli@rc`, or run the bundled
|
|
93
|
+
idempotent provisioner `node scripts/setup.mjs`. The `@latest` tag may not yet expose
|
|
94
|
+
the v4 agent commands. Installation puts `visa` and `visa-cli` on `PATH` and includes
|
|
95
|
+
`@visa/cli/dist/mcp-server/index.js`.
|
|
96
|
+
2. **Mount the MCP server when the runtime supports MCP.**
|
|
97
|
+
- **OpenClaw:** installing `@visa/visa-cli-openclaw` auto-mounts the server by writing
|
|
98
|
+
`mcp.servers["visa-cli"]` in `~/.openclaw/openclaw.json`.
|
|
99
|
+
- **Hermes or another supported runtime:** run `visa-cli connect hermes` or
|
|
100
|
+
`visa-cli connect <runtime>`. For Hermes, the entry is written under `mcp_servers`
|
|
101
|
+
in `~/.hermes/config.yaml`.
|
|
102
|
+
- **No MCP integration:** use the raw `visa agent …` commands.
|
|
103
|
+
3. **Sign the owner in.** Pairing starts are owner-bound — see "Sign in first" below. On a
|
|
104
|
+
fresh runtime using direct pairing, this step is required before any pairing command will succeed. When claiming a Console handoff code, skip sign-in and claim the handoff directly.
|
|
105
|
+
4. **Pair.** Follow the core flow below.
|
|
106
|
+
|
|
107
|
+
## MCP mounting examples
|
|
59
108
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
the agent — "set up my Visa agent to pay" — to run the pairing flow below. (Installing a
|
|
65
|
-
skill does not run it; the agent activates it on a matching prompt.)
|
|
66
|
-
|
|
67
|
-
Access is gated server-side by the employee allowlist + Turnkey, so shipping the playbook
|
|
68
|
-
over public npm exposes no capability — only allowlisted accounts can actually pair.
|
|
69
|
-
|
|
70
|
-
(OpenClaw users also get it auto-bundled with the `@visa/visa-cli-openclaw` plugin. The
|
|
71
|
-
`npx skills add` open-standard path applies once the skill is published to a public repo.)
|
|
72
|
-
|
|
73
|
-
## Getting set up (install → mount → pair → transact)
|
|
74
|
-
|
|
75
|
-
The whole flow, top to bottom:
|
|
76
|
-
|
|
77
|
-
1. **Install the CLI (prerelease)** — `npm install -g @visa/cli@rc`, **or** run the bundled
|
|
78
|
-
provisioner `node scripts/setup.mjs` (idempotent — installs `@visa/cli@rc` only if `visa`
|
|
79
|
-
is missing; this is how **Hermes / Claude Code / any runtime** self-provisions, and what
|
|
80
|
-
OpenClaw runs automatically via the manifest). You **must** use the `@rc` tag: the
|
|
81
|
-
`@latest` tag (4.0.x) predates the `visa agent` commands and reports "does not expose
|
|
82
|
-
agent enroll". This puts both `visa` and `visa-cli` on PATH and ships the bundled MCP
|
|
83
|
-
server (`@visa/cli/dist/mcp-server/index.js`). (Once 4.1.0 is promoted to `latest`, bare
|
|
84
|
-
`@visa/cli` will work.)
|
|
85
|
-
2. **Mount the MCP server** — one mount gives this agent the _entire_ v4 toolset:
|
|
86
|
-
- **OpenClaw:** installing the `@visa/visa-cli-openclaw` plugin **auto-mounts** the server
|
|
87
|
-
(its postinstall writes `mcp.servers["visa-cli"]` into `~/.openclaw/openclaw.json`).
|
|
88
|
-
Nothing to do by hand.
|
|
89
|
-
- **Hermes / anything else:** run `visa-cli install hermes` (or `visa-cli install <runtime>`).
|
|
90
|
-
This writes the server entry idempotently into the runtime's config (Hermes →
|
|
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`):
|
|
109
|
+
Both runtimes use the same server entrypoint. Replace `<npm root -g>` with the output of
|
|
110
|
+
`npm root -g`.
|
|
111
|
+
|
|
112
|
+
OpenClaw (`~/.openclaw/openclaw.json`):
|
|
104
113
|
|
|
105
114
|
```json
|
|
106
115
|
{
|
|
@@ -115,185 +124,434 @@ server so the runtime exposes the tools (both use the same server entrypoint —
|
|
|
115
124
|
}
|
|
116
125
|
```
|
|
117
126
|
|
|
118
|
-
|
|
127
|
+
Hermes (`~/.hermes/config.yaml`). **Hermes passes ONLY this `env:` map to the MCP
|
|
128
|
+
subprocess — it does NOT inherit the gateway environment.** Omitting a required variable
|
|
129
|
+
(an RC access code, the right `HOME`, `PATH`) makes the server exit on every start while
|
|
130
|
+
`agent_capabilities` — which reads on-disk grant state, not live tool registration — can
|
|
131
|
+
still report rails as available. Always set the map explicitly:
|
|
119
132
|
|
|
120
133
|
```yaml
|
|
121
134
|
mcp_servers:
|
|
122
135
|
visa-cli:
|
|
123
136
|
command: node
|
|
124
137
|
args: ['<npm root -g>/@visa/cli/dist/mcp-server/index.js']
|
|
138
|
+
# Hermes does NOT inherit the gateway env. This map is the entire
|
|
139
|
+
# subprocess environment; omit VISA_RC_CODE and the server exits on boot.
|
|
140
|
+
env:
|
|
141
|
+
HOME: /home/<user> # the home that holds this runtime's .visa-cli state
|
|
142
|
+
VISA_RC_CODE: <access code>
|
|
143
|
+
PATH: /usr/local/bin:/usr/bin:/bin
|
|
125
144
|
```
|
|
126
145
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
Hermes also loads skills **per profile** from `~/.hermes/profiles/<profile>/skills/`, not
|
|
147
|
+
from `~/.hermes/skills/`. `visa agent skill --runtime hermes` resolves this automatically:
|
|
148
|
+
it targets the single profile when exactly one exists (or the one named by
|
|
149
|
+
`HERMES_PROFILE`), and **fails loudly** on a multi-profile box instead of planting into
|
|
150
|
+
the flat dir nothing reads — pass `--dir ~/.hermes/profiles/<profile>/skills` to choose.
|
|
151
|
+
|
|
152
|
+
**Hermes sanitizes MCP server names when registering tools.** A server declared
|
|
153
|
+
`visa-cli` in `mcp_servers:` registers its tools as `mcp__visa_cli__<tool>` — with an
|
|
154
|
+
UNDERSCORE, not the declared hyphen. Anything that hardcodes `mcp__visa-cli__<tool>` gets
|
|
155
|
+
`unknown tool` on every call while looking correct in review. Read tool names off the
|
|
156
|
+
live registry; never derive them from the config key.
|
|
157
|
+
|
|
158
|
+
In Hermes, `hermes claw migrate` can also import this skill and MCP configuration from an
|
|
159
|
+
existing OpenClaw installation. See `RUNTIMES.md` for the complete runtime map.
|
|
160
|
+
|
|
161
|
+
## JSON and exit-code contract
|
|
162
|
+
|
|
163
|
+
Prefer structured output and parse it; never scrape prose.
|
|
164
|
+
|
|
165
|
+
- The OpenClaw and MCP tools return structured objects directly.
|
|
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):
|
|
184
|
+
|
|
185
|
+
- [ ] Call `agent_login` (MCP, default action `"start"`) or run
|
|
186
|
+
`visa agent login --format json` (CLI). The result carries a sign-in `browserUrl`
|
|
187
|
+
and a short 6-character confirmation code.
|
|
188
|
+
- [ ] Relay **both** to the human in your reply — the bare URL on its own line, and the
|
|
189
|
+
code. You are very often not in a terminal they can see; the chat message is the
|
|
190
|
+
only place these values reach them.
|
|
191
|
+
- [ ] The human opens the link, signs in (Google or email), and **types the confirmation
|
|
192
|
+
code into the sign-in page** — into the browser, never back to you in chat.
|
|
193
|
+
- [ ] Claim the session: `agent_login {"action":"claim"}` (the CLI command polls on its
|
|
194
|
+
own). Once claimed, the session token is stored locally and pairing can start.
|
|
195
|
+
|
|
196
|
+
If a pairing command returns "An owner profile is required before pairing" or
|
|
197
|
+
`{"code":"session_required"}`, that is this ordering rule — not a fault. Run `agent_login`,
|
|
198
|
+
drive the sign-in above to a claimed session, then retry the same pairing command once.
|
|
199
|
+
|
|
200
|
+
The account that signs in here is the owner the pairing binds to. The same person must
|
|
201
|
+
approve the pairing link in the next step from that same signed-in browser session;
|
|
202
|
+
a different account on the review page fails closed.
|
|
203
|
+
|
|
204
|
+
## Fastest path — one shot (`visa agent pair`)
|
|
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:
|
|
130
209
|
|
|
131
|
-
|
|
210
|
+
```
|
|
211
|
+
visa agent pair --format json
|
|
212
|
+
```
|
|
132
213
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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:
|
|
138
318
|
|
|
139
|
-
|
|
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):
|
|
140
327
|
|
|
141
|
-
|
|
328
|
+
```
|
|
329
|
+
visa agent connect --authority <card|wallet|both> --ceiling <usd> --per-transaction <usd> --wait
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Use the stable `agentId` from pairing or `visa agent list` — never invent or alter the id.
|
|
333
|
+
A policy refusal or timeout means **nothing was signed**: report it and stop; do not retry
|
|
334
|
+
with a different command or a reconstructed URL.
|
|
335
|
+
|
|
336
|
+
Once a wallet is delegated and a policy is set, these are the served wallet tools this agent
|
|
337
|
+
can actually call: `wallet_discover` (search the public x402 Bazaar), `wallet_probe` (read a
|
|
338
|
+
challenge without paying), `wallet_pay` / `wallet_directory_pay` (pay, policy-enforced),
|
|
339
|
+
`wallet_history` / `wallet_reconcile` (local ledger + resolve `reconciling` holds),
|
|
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.
|
|
342
|
+
|
|
343
|
+
## Cancelling pending ceremonies
|
|
344
|
+
|
|
345
|
+
If a pairing or spending ceremony was started mistakenly, needs updated caps/rails, or the human chooses to abandon it, cancel it explicitly rather than letting it sit or waiting for its TTL:
|
|
346
|
+
|
|
347
|
+
1. **Cancelling identity pairing:**
|
|
348
|
+
- **MCP:** `enroll_agent { action: "cancel", pairingId: "<pairingId>" }` or `agent_pairing_cancel { pairingId: "<pairingId>" }`.
|
|
349
|
+
- **CLI:** `visa agent cancel <pairingId>`.
|
|
350
|
+
- Requires the immutable `pairingId` from the start step. Clears local pending state and releases the ceremony server-side. Replays return typed `already_terminal`.
|
|
142
351
|
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
352
|
+
2. **Cancelling spending-grant (connect) ceremonies:**
|
|
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.
|
|
147
356
|
|
|
148
|
-
|
|
149
|
-
this skill). The returned fields and the poll semantics are identical across all of them.
|
|
357
|
+
## Core flow
|
|
150
358
|
|
|
151
|
-
|
|
359
|
+
- [ ] Establish the owner session ("Sign in first" above) if this runtime does not have
|
|
360
|
+
one yet.
|
|
361
|
+
- [ ] Start the pairing with `pair_agent_start`, `enroll_agent` action `start`, or
|
|
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.
|
|
152
368
|
|
|
153
|
-
|
|
154
|
-
any prior. If the user says they already started enrolling, go to Step 3 first.
|
|
369
|
+
### Completion is `activated` — nothing else
|
|
155
370
|
|
|
156
|
-
|
|
157
|
-
|
|
371
|
+
Only a poll result of `activated` means this runtime is paired. Until you have one, do not
|
|
372
|
+
tell the human the agent is connected, paired, enrolled, set up, ready, or good to go, and
|
|
373
|
+
do not move on to spending, mail, or mesh steps that assume an identity.
|
|
158
374
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
375
|
+
`start` returning successfully is not completion. It returns a stable `agentId` and a
|
|
376
|
+
`requestKeyFingerprint` **before any human has approved anything** — those are review
|
|
377
|
+
values for the browser comparison, not evidence of pairing. Reporting an `agentId` as
|
|
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.
|
|
162
380
|
|
|
163
|
-
If
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
NOT how v4 pairing authenticates, and it dead-ends on the same gate. Recover by either
|
|
167
|
-
upgrading to a current RC (`npm install -g @visa/cli@rc` — recent RCs exempt `agent enroll`
|
|
168
|
-
/ `agent enroll-claim` from this gate) or, if the operator has the code, setting the
|
|
169
|
-
`VISA_RC_CODE` env var on the host. Then retry the hand-off. Never substitute `setup` for
|
|
170
|
-
pairing.
|
|
381
|
+
If you cannot reach `activated`, say plainly what state you did reach and what the human
|
|
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.
|
|
171
384
|
|
|
172
|
-
###
|
|
385
|
+
### 1. Start
|
|
173
386
|
|
|
174
|
-
|
|
387
|
+
Start once. If there is already a pending pairing, poll it before creating another.
|
|
175
388
|
|
|
176
|
-
|
|
389
|
+
The result provides `browserUrl`, stable `agentId`, `requestKeyFingerprint`, and — on newer
|
|
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.
|
|
177
394
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
parsing. Add a short "👉 Tap to open on your phone" cue so it reads as an action.
|
|
183
|
-
- **Emphasize the confirm code.** Show `confirmCode` in **bold** or `monospace` — both are
|
|
184
|
-
Telegram-safe (mono needs no escaping). It's read/compared, never clicked.
|
|
185
|
-
- **Relative expiry.** "expires in about 15 minutes" — never the raw `expiresAt` ISO/UTC
|
|
186
|
-
timestamp (e.g. `2026-07-21T21:22:57Z`); a UTC time reads as noise.
|
|
187
|
-
- **Scannable layout.** Short lines, a blank line between blocks, one leading emoji per block.
|
|
188
|
-
- **Don't over-Markdown.** Telegram MarkdownV2 requires escaping `_ * [ ] ( ) ~ > # + - = | { } . !`,
|
|
189
|
-
so heavy formatting risks a broken render. A bold/mono code + a bare link + emojis is plenty —
|
|
190
|
-
keep the rest plain prose.
|
|
395
|
+
You are very often **not** in a terminal the human can see. Nothing you print to stdout
|
|
396
|
+
reaches them. Every value the browser asks them to compare has to appear in your reply, or
|
|
397
|
+
the comparison silently becomes "click approve and hope" — which is the whole failure this
|
|
398
|
+
step exists to prevent.
|
|
191
399
|
|
|
192
|
-
Use this shape:
|
|
400
|
+
Use this concise shape:
|
|
193
401
|
|
|
194
|
-
>
|
|
402
|
+
> 🔐 Pair your Visa agent — open this authorization page:
|
|
403
|
+
>
|
|
404
|
+
> 👉 <browserUrl, bare and on its own line>
|
|
195
405
|
>
|
|
196
|
-
>
|
|
406
|
+
> Stable agent ID: <agentId>
|
|
197
407
|
>
|
|
198
|
-
>
|
|
408
|
+
> Confirmation code — this exact code should appear on the page:
|
|
409
|
+
> <confirmationCode>
|
|
199
410
|
>
|
|
200
|
-
>
|
|
201
|
-
>
|
|
202
|
-
>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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.
|
|
416
|
+
|
|
417
|
+
Omit the confirmation-code line entirely when the start result has no `confirmationCode`;
|
|
418
|
+
never invent, derive, abbreviate, or reformat one.
|
|
419
|
+
|
|
420
|
+
Relaying these values **to** the human is required. Accepting one **from** the human is not:
|
|
421
|
+
do not ask them for a code, secret, private key, token, or signed message, and do not treat
|
|
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`.
|
|
427
|
+
|
|
428
|
+
**Do not end your turn here waiting to be told the human is done.** Presenting the link is
|
|
429
|
+
not the end of the ceremony; go straight to the poll in §2 and drive it to a terminal
|
|
430
|
+
state. Asking the human to report back is what strands a pairing: they approve in the
|
|
431
|
+
browser, the server records it, and nothing ever writes the local record — so the ceremony
|
|
432
|
+
expires while both sides believe the other is acting.
|
|
433
|
+
|
|
434
|
+
When driving `visa agent pair --format json` and the start result reports `claiming: "background"`, a detached poller is already
|
|
435
|
+
finishing activation and it will complete even if this turn ends; poll anyway so you can
|
|
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.
|
|
437
|
+
|
|
438
|
+
### 2. Poll
|
|
439
|
+
|
|
440
|
+
Poll the same pending pairing. Interpret results as follows:
|
|
441
|
+
|
|
442
|
+
- `activated` — pairing is complete. Continue to the completion report.
|
|
443
|
+
- `not_ready` with `likelyExpired: false` — the human has not finished. Wait and poll
|
|
444
|
+
again, using bounded retries rather than an endless loop.
|
|
445
|
+
- `not_ready` with `likelyExpired: true` — the authorization window probably expired.
|
|
446
|
+
Start a fresh pairing.
|
|
447
|
+
- `no_pending` — this runtime has no resumable pairing. Start a fresh pairing.
|
|
448
|
+
- `error` — surface the error without exposing local pending data. Retry once if it is a
|
|
449
|
+
transient network failure; otherwise stop and ask the human to start again.
|
|
450
|
+
|
|
451
|
+
### 3. Report completion
|
|
452
|
+
|
|
453
|
+
On `activated`, report the display name when present and the stable `agentId`. You may
|
|
454
|
+
also report `identityKeyJkt` as the current key fingerprint, but label it clearly as a
|
|
455
|
+
rotatable key identifier.
|
|
456
|
+
|
|
457
|
+
Use precise completion language:
|
|
458
|
+
|
|
459
|
+
> Visa agent <displayName> is paired to this runtime. Stable agent ID: <agentId>.
|
|
460
|
+
|
|
461
|
+
Do not claim that activation configured payments, email, tap bindings, or any other
|
|
462
|
+
product capability. Those are separate follow-up configuration flows.
|
|
463
|
+
|
|
464
|
+
## Interruption and replay safety
|
|
465
|
+
|
|
466
|
+
The runtime persists its v2 pending record before sending the signed claim. If a request
|
|
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.
|
|
470
|
+
|
|
471
|
+
The pending record remains until the activated agent record has been written durably.
|
|
472
|
+
Therefore:
|
|
473
|
+
|
|
474
|
+
- Poll before starting over.
|
|
475
|
+
- Do not delete or edit pending files to fix a transient failure.
|
|
476
|
+
- Do not regenerate keys for an existing pairing.
|
|
477
|
+
- Do not copy pending state between runtimes.
|
|
478
|
+
|
|
479
|
+
## Optional `.visa` mesh binding (separate from pairing)
|
|
480
|
+
|
|
481
|
+
Pairing does not register a `.visa` name, TAP key, or Subway peer. If the operator has
|
|
482
|
+
separately enabled and registered those channel bindings, the mounted `visa-cli` MCP server
|
|
483
|
+
may expose `subway_register`, `subway_send`, `subway_inbox`, and `subway_find`.
|
|
484
|
+
|
|
485
|
+
Mesh messaging is available only on a compatible RC/dev build with `SUBWAY_MESH=visa` and
|
|
486
|
+
a reachable `SUBWAY_RELAY_MULTIADDR`. If those tools or the relay are unavailable, report
|
|
487
|
+
that clearly and stop. Do not imply that identity pairing alone granted directory or
|
|
488
|
+
messaging authority, and do not improvise another transport.
|
|
489
|
+
|
|
490
|
+
## Optional agent mailbox (separate from pairing)
|
|
491
|
+
|
|
492
|
+
Pairing does not provision an email address or inbox. If the agent needs a
|
|
493
|
+
mailbox — e.g. to receive a merchant's account-signup or one-time-code email —
|
|
494
|
+
connect one explicitly, from the paired runtime, with the raw CLI:
|
|
495
|
+
|
|
496
|
+
```
|
|
497
|
+
visa agent mail-connect <agentId>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
This is CLI-only; no pairing step or MCP tool connects a mailbox. It requires an
|
|
501
|
+
already-paired stable-agent identity on this runtime — it reads the local agent
|
|
502
|
+
record and proves the Ed25519 identity to the service. It issues the stable
|
|
503
|
+
agent mailbox if one does not exist, then stores an inbox-scoped credential in an
|
|
504
|
+
owner-only `0600` runtime file so this runtime can read that one inbox.
|
|
505
|
+
|
|
506
|
+
Be honest about scope. A mailbox grants an email address and the ability to read
|
|
507
|
+
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 pairing
|
|
509
|
+
set up mail. Once connected, the MCP `wallet_mail_status`, `wallet_mail_read`,
|
|
510
|
+
and `wallet_mail_await_otp` tools read the inbox (e.g. to await a sender-checked
|
|
511
|
+
one-time code); without the scoped credential those reads fail closed. Keep the
|
|
512
|
+
org-wide AgentMail key off the runtime — provisioning happens only through
|
|
513
|
+
`mail-connect` under operator control.
|
|
514
|
+
|
|
515
|
+
## Optional checkout profile (separate from pairing)
|
|
516
|
+
|
|
517
|
+
Pairing does not enable card checkout. If the operator has separately provisioned card
|
|
518
|
+
authority and enabled `checkout_agent_access`, the experimental `pay_merchant` flow also needs a local
|
|
519
|
+
`~/.visa-mcp/contact.json` file. Collect every value from the human before the first review;
|
|
520
|
+
never infer or invent identity or address data. Write the file with mode `0600`.
|
|
521
|
+
|
|
522
|
+
```jsonc
|
|
523
|
+
{
|
|
524
|
+
"fullName": "Ada Lovelace",
|
|
525
|
+
"email": "ada@example.com",
|
|
526
|
+
"addressLine1": "1 Analytical Way",
|
|
527
|
+
"addressLine2": "",
|
|
528
|
+
"city": "San Francisco",
|
|
529
|
+
"state": "CA",
|
|
530
|
+
"postalCode": "94105",
|
|
531
|
+
"country": "US",
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
`fullName` must be non-blank; `firstName` plus `lastName` is also accepted. The engine reads
|
|
536
|
+
the exact keys `fullName`, `firstName`, `lastName`, `email`, `addressLine1`, `addressLine2`,
|
|
537
|
+
`city`, `state`, `postalCode`, and `country`. The profile supplies checkout/cardholder and
|
|
538
|
+
billing data only. Its `email` value is not the account's verified owner email, an agent
|
|
539
|
+
mailbox, key proof, recovery factor, or permission to spend.
|
|
540
|
+
|
|
541
|
+
## Security rules
|
|
542
|
+
|
|
543
|
+
- Never read, print, log, paste, or transmit the private Ed25519 JWK or local claim token.
|
|
544
|
+
- Never read or echo the pending pairing file. Present only the authorization URL returned
|
|
545
|
+
by the supported command or tool.
|
|
546
|
+
- Never treat `identityKeyJkt` as the stable identity. Use `agentId` for references and
|
|
547
|
+
persistence.
|
|
548
|
+
- Never fetch the authorization URL on the human's behalf. The human reviews and approves
|
|
549
|
+
it in their browser.
|
|
550
|
+
- Never invent a secondary pairing path when polling fails. Preserve the pending state,
|
|
551
|
+
surface the error, and retry or restart through the same canonical enrollment flow.
|
|
295
552
|
|
|
296
553
|
## Further docs
|
|
297
554
|
|
|
298
|
-
- `
|
|
299
|
-
- `
|
|
555
|
+
- `RUNTIMES.md` — OpenClaw, Hermes, and raw CLI setup.
|
|
556
|
+
- `docs/agents/ARCHITECTURE.md` — where enrollment sits in the v4 request paths.
|
|
557
|
+
- `visacli.sh/agents` — product-facing agent documentation.
|