agentic-wallet-mcp 0.12.3 → 0.13.0
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/CHANGELOG.md +44 -0
- package/README.md +11 -4
- package/dist/server-bundle.cjs +984 -44
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
> Entries for 0.5.0 and earlier were reconstructed from commit history when this file was
|
|
9
9
|
> introduced in 0.6.0, so they summarise each release rather than being exhaustive.
|
|
10
10
|
|
|
11
|
+
## [0.13.0] — 29 September 2026
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`check_policy_decision` — ask whether a specific spend is permitted right now.** The one policy
|
|
16
|
+
question no chain read can answer, because a cap is measured against cumulative spend held
|
|
17
|
+
off-chain. Three outcomes: `permitted`, `refused` and `undetermined`, where undetermined is
|
|
18
|
+
neither a refusal nor permission — it means nothing was evaluated, and every failure produces it,
|
|
19
|
+
so no transport error, bad envelope or unknown verdict can ever yield a permitted answer. There is
|
|
20
|
+
no step-up verdict, because the decision service has none.
|
|
21
|
+
|
|
22
|
+
**A permitted answer reserves the owner's budget for fifteen minutes**, with no way to release it
|
|
23
|
+
early, so the tool tells the agent not to poll it and not to probe amounts — every permitted
|
|
24
|
+
answer along the way reserves again, and the owner's next real payment can be refused by their own
|
|
25
|
+
agent. New optional settings: `POLICY_DECISION_URL` and `POLICY_DECISION_AUTH`. Without a URL the
|
|
26
|
+
tool answers `undetermined` and says so, rather than failing against a guessed host.
|
|
27
|
+
- **`write_policy` / `check_policy_write` — deploy a spending policy on chain, pay-gated over x402.**
|
|
28
|
+
Three phases: a free pre-check, a payment that writes nothing, and a collect that finishes the
|
|
29
|
+
write once the settlement confirms. Two tools rather than one, because the settlement queue can
|
|
30
|
+
outlast a single call. A `202` here is not a failure — between the payment and a confirmed
|
|
31
|
+
settlement, a payment has been made and no policy exists yet, and that window is the normal case;
|
|
32
|
+
`settling` and `submitted` both mean a payment has been made, so the agent is told to call
|
|
33
|
+
`check_policy_write` rather than retry `write_policy`. `submitted` carries a `txHash` and is still
|
|
34
|
+
not a written policy — the block hasn't confirmed it. A `receipt_void` result is the one state
|
|
35
|
+
where paying again is correct.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **The wallet's own follow-up guidance no longer promises the agent will "check again shortly" on
|
|
40
|
+
its own.** QA saw an agent claim it had "set up an automation to check every 60 seconds" after a
|
|
41
|
+
Verified AI Birthcert session came back pending — no such capability exists. Investigation found
|
|
42
|
+
the wallet's own ready-made "Tell the user:" sentences ended the same way, and the new hardening
|
|
43
|
+
text in the tool descriptions had nowhere near the reach of a sentence the model is told to relay
|
|
44
|
+
verbatim. Every such sentence now asks the user to message back, never claims the agent will act
|
|
45
|
+
between messages, and — since a link that has already expired (or is about to) makes "message back
|
|
46
|
+
in a few minutes" actively wrong — the wording now depends on how much time is actually left on the
|
|
47
|
+
verification link.
|
|
48
|
+
- **`request_ai_birthcert_verification` and `subscribe_and_issue` now explicitly instruct the agent
|
|
49
|
+
to ask the human owner for an agent name, rather than inventing one.** The same QA session showed
|
|
50
|
+
an agent silently picking its own agent name in both the Verified and Basic AI Birthcert flows.
|
|
51
|
+
The "ask the user, never invent" rule previously lived only in a skill-layer document that not
|
|
52
|
+
every MCP client loads; it is now in the MCP tool schemas themselves, including at the specific
|
|
53
|
+
field the model fills in.
|
|
54
|
+
|
|
11
55
|
## [0.12.3] — 28 September 2026
|
|
12
56
|
|
|
13
57
|
### Fixed
|
package/README.md
CHANGED
|
@@ -24,14 +24,17 @@ VC issuance → identity proof → pay-per-use).
|
|
|
24
24
|
| `credential_preflight` | **Free.** The first call for any credential — composes the quote, the balances and the spending cap into one answer, before a single application field is collected. Spends nothing, starts nothing. `blockers` lists *every* reason the wallet is not ready, at once; `notChecked` lists what preflight cannot know — agent-name availability is decided by myid at issuance, after payment, so a `ready` result is never a reservation | `{ credential, templateId? }` | `{ credential, ready, fee?, balances, cap?, schema?, blockers, notChecked }` |
|
|
25
25
|
| `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc?, revealAttribute?, issuerKeys? }` | `{ proofResponseHeader, verified, presentationId }` |
|
|
26
26
|
| `pay_and_fetch` | Fetch a URL, auto-pay with x402 (self-pay via Wallet BE) on `402` | `{ url, method?, headers?, body? }` | `{ status, body, paymentMade, amountPaid, amountPaidHuman, asset }` |
|
|
27
|
-
| `subscribe_and_issue` | Reuse a cached VC if still valid, else pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate?, dryRun?, forceReissue? }` | `{ issued, vcId, vc, txHash, fromCache?, schema?, originalPayment?, paymentAttempted?, recovery? }` |
|
|
27
|
+
| `subscribe_and_issue` | Reuse a cached VC if still valid, else pay x402 → MBI issues → return the VC. Replacing a still-valid cached VC needs `forceReissue: true` **and** `confirmReplaceExistingVc` set to the exact `vcId` you were shown first — `forceReissue` alone only shows you the existing VC (`issued: false`), it never pays | `{ templateId, attributes, expirationDate?, dryRun?, forceReissue?, confirmReplaceExistingVc? }` | `{ issued, vcId, vc, txHash, fromCache?, schema?, originalPayment?, paymentAttempted?, recovery?, vcPassImagePaths? }` — `issued: false` with `fromCache: true` means an existing valid VC is being shown, not an error |
|
|
28
28
|
| `create_holder_account` | Onboarding: mint an HSM account (the MCP already auto-creates one at startup if `ZETRIX_ADDRESS` is omitted — see Environment below). Always checks for an existing account first — if one is active for this session, returns `{ alreadyExists: true, existing }` without creating anything; pass `confirmNew: true` (after asking the user) to mint a new one anyway | `{ password, label?, purpose?, confirmNew? }` | `{ created, alreadyExists, existing?, zetrixAddress?, holderDid?, publicKeyHex?, message }` |
|
|
29
29
|
| `get_template_schema` | **Free** read of a VC template's declared attribute schema. Call before `subscribe_and_issue` to learn which attributes it requires | `{ templateId }` | `{ templateId, schema: { required, optional } }` or `{ templateId, error }` |
|
|
30
30
|
| `query_contract` | Read-only query against any Zetrix contract — call an arbitrary method and return its raw result. No signing, no state change | `{ contractAddress, method, params? }` | `{ ok: true, result }` or `{ ok: false, error }` |
|
|
31
31
|
| `get_policy_template_schema` | **Free** read of a POLICY template's declared attribute vocabulary. This is the only vocabulary that means anything on chain — the policy contract validates nothing, so an attribute outside this list deploys cleanly and then enforces nothing at all. Accepts `{ publisher, policyKey }` or `{ templateId }`. A template that cannot be read reports `{ error }` rather than `{ found: false }`, so "no such template" is never confused with "could not look it up" | `{ templateId?, publisher?, policyKey? }` | `{ found, attributes?, error? }` |
|
|
32
32
|
| `get_my_policy` | **Free** read of the spending policies this owner has deployed on chain; defaults to this wallet's own address. Costs 2 + N chain calls and warns above 50 keys. An owner who has never deployed one is a normal absence, **not** an error — the policy contract is created lazily on first write — and a failed lookup keeps its own error state, so "we could not list your policies" is never presented as "you have none" | `{ owner? }` | `{ owner, policies?, warning?, error? }` |
|
|
33
33
|
| `policy_preflight` | **Free** check of a draft policy *before* it is deployed. Two questions: is it well-formed (`blockers` lists every problem at once, so one round of fixes is enough — on a hostile draft with hundreds of faults the list is capped, and it says so on the last line), and — more important — does it MEAN what the user thinks (`interpretation` states in plain words what it actually does). Always show `interpretation`, **including when `ready` is true**: a valid policy can still mean something unintended — a spending cap with no window is a LIFETIME cap, not a monthly one. `notChecked` lists what could not be verified, including whether the policy would be enforced at all | `{ policyKey, attributes, validFromBlock?, validToBlock?, templateId?, publisher? }` | `{ policyKey, ready, declared?, blockers, interpretation, notChecked }` — `declared` is the template's own attribute names, present only when the template resolved; long results are bounded, and anything omitted is stated in `notChecked` rather than dropped silently |
|
|
34
|
-
| `
|
|
34
|
+
| `check_policy_decision` | **Free** — but NOT free of consequence. Ask whether a specific spend is permitted RIGHT NOW: the one policy question no chain read can answer, because a cap is measured against cumulative spend held off-chain. Three outcomes — `permitted`, `refused`, `undetermined`. **`undetermined` is neither a refusal nor permission**: it means nothing was evaluated (service unreachable, ledger stale, or none configured), and it is what every failure produces — no transport error, bad envelope or unknown verdict string can ever yield `permitted`. There is **no step-up verdict**; the decision service answers only allow or deny, so the wallet never reports that a human must approve a spend. **A permitted answer reserves the owner’s budget for 15 minutes**, with no way to release it early — so do not poll it, do not probe amounts (every allow along the way reserves again, and the owner’s next real payment can be refused by their own agent), and **never retry automatically after a timeout**: the call may have succeeded and reserved already. To find an amount that fits, read `remaining` from one answer and compute locally. `requestKey` is **correlation only, not idempotency**: repeating it does not deduplicate and each permitted answer reserves again. While the spend-ledger crawl is off, every decision answers `undetermined` / `EVALUATION_UNAVAILABLE`, which is correct behaviour rather than an outage. `ignored` is surfaced on a permitted answer too — it lists constraints the policy carries that the service could not enforce | `{ ownerAddress, asset, amount, policyKey?, recipientAddress?, method?, payTo?, paymentNonce?, requestKey? }` | `{ outcome, summary, reasonCode?, policyKey?, enforced?, ignored?, remaining?, capacityReturnsAt?, reservationId?, notChecked }` |
|
|
35
|
+
| `write_policy` | **Pays a real fee.** Deploy a spending policy on chain over x402. Three steps: a free pre-check, a payment that writes **nothing**, and a collect that finishes the write once the payment settles. Between the payment and the settlement a payment has been made and no policy exists — **that window is normal, not a failure**. `settling` and `submitted` both mean a payment has been made: pass `paymentReceipt` to `check_policy_write`, never call this again. `submitted` carries a `txHash` and still is **not** a written policy — the block has not confirmed it. `written` is the only state where the policy exists. `already_exists` / `refused` come from the free pre-check, so nothing was paid; `payment_refused` says nothing either way about whether the fee was taken. `receipt_void` is the one state where paying again is right (`payFresh`). The fee goes through the same payment cap as every other paying tool, and the wallet never builds a blob, permit digest or canonical JSON — ms-zetrix signs with the owner’s own key | `{ policyKey, attributes, templateContractAddress, templateId, validFromBlock?, validToBlock?, requestKey?, pollBudgetMs? }` | `{ state, message, paymentReceipt?, policyKey?, txHash?, paid?, payFresh? }` |
|
|
36
|
+
| `check_policy_write` | **Free — never pays, on any path.** Finish a policy write that has already been paid for, from its `paymentReceipt` (or the most recent pending one when omitted). This is the right answer to “did my policy get created?”; calling `write_policy` again is not. A receipt this wallet does not recognise does **not** mean the write failed — the service completes a paid write on its own, and asking to write the same `policyKey` again reports the truth for free before any payment | `{ paymentReceipt?, pollBudgetMs? }` | `{ state, message, paymentReceipt?, policyKey?, txHash?, paid?, payFresh? }` |
|
|
37
|
+
| `request_ai_birthcert_verification` | Start a **Verified** AI Birthcert issuance session with myid (MyDigital ID owner verification) — distinct from `subscribe_and_issue`'s self-declared Basic AI Birthcert. Optional `gasPayer` (`"sponsored"` | `"self"`) overrides the gas payer for this call only — precedence is per-call `gasPayer` > `GAS_PREFERENCE` config > `sponsored` hardcoded default. If a sponsored quote is refused before any money moved, the wallet automatically falls back to paying gas itself; it never falls back once payment is merely pending/indeterminate. `dryRun: true` quotes instead of paying: it stops after the 402 challenge, pays nothing, creates no session, and returns `{ quote }` — the asset, the amount and which side pays gas. Quote and paid request send byte-identical signed bodies. **Checks for an existing Verified AI Birthcert VC before paying**: if this holder already has a still-valid one (found in the local cache, or resolved from a previously-issued session the cache never saw), nothing is paid and no session is started — you get `{ existingVerifiedVc: { vcId, validUntil }, message }` instead; call again with `confirmReplaceExistingVc` set to exactly that `vcId` to replace it anyway. An existing VC that has already expired is replaced automatically, no confirmation needed, and the result carries `replacedExpiredVc: { vcId, validUntil }` naming the one it replaced. It can also refuse with `{ error }` specifically because an already-issued credential exists but could not be confirmed as valid or expired — nothing is paid on that path either; relay `error` as given, which names the concrete next step | `{ agentName, agentPurpose?, evidenceAssuranceLevel?, ownerType?, ownerVerified?, gasPayer?, dryRun?, discardStuckReceiptAndPayFresh?, confirmReplaceExistingVc? }` | `{ sessionId, verificationUrl, expiresAt, expiresIn, expiresInSeconds, message, replacedExpiredVc? }` (`expiresIn` / `expiresInSeconds` are worked out by the wallet from its own clock, so an agent quotes `expiresIn` instead of doing timezone arithmetic itself; `message` carries a "Tell the user:" sentence about following up, worded to match how much time is actually left on the link), `{ quote: { asset, maxAmountRequired, payTo?, gasModel } }`, `{ existingVerifiedVc: { vcId, validUntil? }, message }` (nothing paid, see above), or — instead of a session — `{ settlementPending: true, paymentReceipt, message }` after ~90s, whose meaning depends on which flag (if any) is set below: never assume the payment succeeded from this shape alone. While a merely-stuck receipt (outcome still unresolved) is held this tool can only REPLAY it — it never buys a new credential, which is what stops a second charge. A credential-service refusal is different again: `issuerRejected: true` means the service was reached and refused the request (the settlement is NOT what failed); `paymentInvalid: true` means the service specifically ruled the payment or receipt invalid (this one IS about the payment). Neither tells you whether the fee was taken, in either direction. A **void** receipt (SSIVC status_code 67/68) is different again: the wallet discards it itself, with no confirmation and no flag, so the very next call is an ordinary fresh purchase — see `{ error }` below. `discardStuckReceiptAndPayFresh: "<exact receipt id>"` is the way to start over on a merely-stuck receipt instead: it throws that payment away and pays the fee again, refuses a mismatched id (discarding and paying nothing), refuses a receipt that belongs to a live session, **refuses a receipt that is not stuck yet** (younger than `SETTLEMENT_STUCK_AFTER_MS`, 24h by default — enforced by the wallet, so an agent that mistakes a bare "retry" for consent still cannot cause a second payment; nothing is discarded and nothing is paid), and returns `discardedPaymentReceipt` so the lost payment stays traceable. `{ error }` (`RequestVerificationFailure`) also carries `discardedPaymentReceipt` when this call itself discarded a dead receipt — that confirmed stuck-receipt discard, or an unprompted void one, or **both on one call**; it also rides on `settlementPending` results, not only successes. When both happen (the confirmed discard, then the fresh payment ruled void) the field holds the **void** receipt and the id the user confirmed is named at the end of the `error`/`message` text ("ALSO DISCARDED earlier on this same call") — quote both ids to support, not just the field. Retrying is fine only for an insufficient-funds or payment-cap `{ error }` (nothing was paid); any other `{ error }` (already-settled, receipt not saved, receipt void, outcome unknown) may mean money moved, so get the user's explicit agreement before calling again |
|
|
35
38
|
| `check_ai_birthcert_verification` | **Free** (never pays). Poll the most recently requested Verified AI Birthcert session; on `status: "issued"`, also fetches, verifies, and caches the credential. While the session is still open it replays the stored `verificationUrl` alongside SSIVC's live `expiresAt` and the wallet's own `expiresIn` / `expiresInSeconds` (time left, worked out from the wallet clock, never from the agent's) — SSIVC issues the link only once, at creation, so this store is the only place it survives. That makes *"where is my link?"* answerable without touching the paid tool. No link is returned once `status` is `"issued"`: the link is spent. If a prior payment is still clearing this tool **advances** it — it replays the saved receipt (never a new payment) and returns the live session once it settles, so "check back in a few minutes" genuinely progresses the flow | (none) | `{ status: "pending" | "issued" | "no_session" | "settlement_pending" | "receipt_void", verificationUrl?, expiresAt?, vcId?, vc?, cacheError?, paymentReceipt?, outcomeUnknown?, issuerRejected?, paymentInvalid?, message? }` — `settlement_pending` means a payment was made and is being followed: without `outcomeUnknown`, `issuerRejected` or `paymentInvalid` the message leads `PAYMENT SENT` and covers **two** states that the following clause tells apart — *"still being processed"* is a confirmed queued settlement (check again shortly), while *"has not been confirmed yet"* means the outcome could not be determined at all yet, so it must not be reported as progressing or as succeeded; either way the receipt is saved, nothing is paid again, and checking again later is the next step; with `outcomeUnknown: true` the outcome could not be determined at all and has been unresolved longer than `SETTLEMENT_STUCK_AFTER_MS` (24h default), so it is not coming back on its own — `stuckFor` says how long, the fee was most likely already taken, and quoting `paymentReceipt` to support is the way forward. Starting over via `clear_stuck_payment_receipt` costs a second fee. `issuerRejected: true` is a distinct case: the credential service was reached and refused the request (a `4xx` that is not a settlement verdict — measured live as `status_code 99` "Error retrieving ZVG access token"), so the settlement is NOT what failed; `message` quotes what it said, the receipt is kept, no new payment was made, and it can clear once the service recovers. `paymentInvalid: true` is different again: the service has specifically ruled the payment or receipt invalid — this one IS about the payment. Both say nothing about whether the fee was taken, in either direction. `receipt_void` is terminal and different: SSIVC has ruled the receipt finished (status_code 67 expired / 68 failed), so checking again cannot help. The wallet **discards the dead receipt itself** here — no confirmation needed, because a receipt the server will never honour protects nothing and only blocks the next purchase — and returns it as `discardedPaymentReceipt`. It does NOT mean the fee was refunded, and buying the credential means calling `request_ai_birthcert_verification` again, which pays the fee again |
|
|
36
39
|
| `clear_stuck_payment_receipt` | **Free, last resort, destructive.** Discard a stuck Verified AI Birthcert payment receipt the wallet is holding and refusing to pay past — the supported replacement for deleting `<stateDir>/ssivc-session.json` by hand, which a hosted subscriber cannot do. Two steps by construction: with no argument it clears nothing and returns the receipt id plus a warning; clearing requires echoing that exact id back as `confirmReceiptId`, and a mismatch clears nothing. A receipt younger than `SETTLEMENT_STUCK_AFTER_MS` (24h by default) is refused on both steps. If the settlement completed in between — which is what happens when you follow the advice to call `check_ai_birthcert_verification` first — the receipt now belongs to a **live, paid-for session**, and the id alone no longer clears it: the tool hands back `sessionId` and `verificationUrl` instead, and only `confirmDiscardLiveSession: true` will discard them. The discarded payment is unrecoverable | `{ confirmReceiptId?, confirmDiscardLiveSession? }` | `{ cleared, paymentReceipt?, requiresConfirmation?, sessionId?, verificationUrl?, message?, error? }` |
|
|
37
40
|
| `transfer_token` | **Moves real funds, irreversible.** Send native ZTX or any ZTP20 token to an address. `token` takes `"ZTX"`, a registered symbol (no contract address needed) or a raw ZTP20 address; an unregistered symbol returns `needsTokenAddress: true` and signs nothing. Give `amountHuman` or `amount` — if both, they must agree, which is the cheapest way to catch a 1-vs-1000000 error. Nothing is signed without `confirm: true`, and the wallet never infers it. `outcomeUnknown: true` means the transaction may already be on chain — **do not retry**, check the reported nonce. `policyDenied: true` means the spending policy refused it: a decision, not a failure, so retrying will not help | `{ token, to, amountHuman?, amount?, confirm?, dryRun? }` | `{ sent, txHash?, nonce, token, asset, amount, amountHuman, decimals, fee, needsConfirmation?, needsTokenAddress?, outcomeUnknown?, policyDenied? }` |
|
|
@@ -39,8 +42,10 @@ VC issuance → identity proof → pay-per-use).
|
|
|
39
42
|
> **VCs are cached locally**, keyed by `templateId`, under `~/.agentic-wallet-mcp/vc-cache/`
|
|
40
43
|
> (scoped per network + holder — different identities or networks never share a cache).
|
|
41
44
|
> `subscribe_and_issue` checks the cache before paying: a still-valid cached VC is returned
|
|
42
|
-
> immediately with `fromCache: true` and **no payment made
|
|
43
|
-
>
|
|
45
|
+
> immediately with `fromCache: true` and **no payment made**. `forceReissue: true` on its own does
|
|
46
|
+
> NOT pay — it returns that same existing VC as `{ issued: false, fromCache: true, reason }` instead,
|
|
47
|
+
> so it can be shown to the user first; only with `confirmReplaceExistingVc` also set to the exact
|
|
48
|
+
> `vcId` shown does it actually pay and reissue. `wallet_status`/`prove_identity` fall back to the cache
|
|
44
49
|
> automatically when you don't pass `heldCredentials`/`vc` explicitly — `prove_identity` only
|
|
45
50
|
> auto-selects when there's exactly one valid cached VC; with zero or several, it errors and
|
|
46
51
|
> asks you to pass `vc` explicitly. Explicitly passing `vc`/`heldCredentials` (including `[]`)
|
|
@@ -185,6 +190,8 @@ Node ≥ 18 required (built-in `fetch`).
|
|
|
185
190
|
| `MAX_PAYMENT_AMOUNT` | no** | Per-asset x402 auto-pay cap — JSON `{ "<asset>": "<maxRawUnits>", "*": "<fallback>" }`. `pay_and_fetch`/`subscribe_and_issue` are asset-agnostic: the resource server's 402 challenge may quote the native ZETRIX token (asset code `ZTX`) **or** a ZTP20 token (e.g. `JMYR`) — cap whichever assets you expect. **Either the ticker or the contract address works** — the challenge identifies a ZTP20 token by its contract address, and the wallet resolves a known ticker (e.g. `JMYR`) to it. If both are written for the same asset, the contract address wins. An unrecognised ticker still matches nothing and falls through to `"*"`. e.g. `{"ZTX":"1000000000","ZTX3WeinXtt28YMyr4vUZ14ddTgEMGeuc1e6b":"5000000","*":"0"}`. **Default when unset:** both networks allow exactly the AI Birthcert fee (1 JMYR), keyed to that network's JMYR contract, and refuse everything else. **The cap is per call, not cumulative** — so an unconfigured wallet, mainnet included, can pay that fee once per call with no overall ceiling. Set this explicitly to lock a wallet down. |
|
|
186
191
|
| `ZETRIX_WALLET_STATE_DIR` | no | Where the wallet keeps `account.json` and its VC cache. Defaults to `~/.agentic-wallet-mcp` |
|
|
187
192
|
| `SSIVC_BASE_URL` | no | myid's SSIVC API base URL, for the Verified AI Birthcert flow (`request_ai_birthcert_verification`/`check_ai_birthcert_verification`). **Auto-derived per network** — testnet `https://ssivc-api-uat.myegdev.com/api`, mainnet `https://verifyid-api.zetrix.com/api`. Override only if either changes |
|
|
193
|
+
| `POLICY_DECISION_URL` | no | Base URL of the policy DECISION service, for `check_policy_decision`. **No default**: the endpoint sits behind an authenticated path this wallet has no credential for, so guessing a host would turn "nobody has wired this up" into a connection error. With none configured the tool answers `undetermined` and says why |
|
|
194
|
+
| `POLICY_DECISION_AUTH` | no* | `Authorization` header value for the policy decision service (e.g. `Bearer …`). Without it the service answers 401 and the tool reports that the wallet is not authorised — not that the request was wrong |
|
|
188
195
|
| `AI_BIRTHCERT_VERIFIED_TEMPLATE_ID` | no | The Verified AI Birthcert's on-chain `did:zid:...` template id. Auto-derived per network by default — **the mainnet default is unverified**, so override this explicitly once the mainnet template id is confirmed |
|
|
189
196
|
| `GAS_PREFERENCE` | no | `sponsored` or `self` — the default gas payer for the Verified AI Birthcert flow (`request_ai_birthcert_verification`). **Defaults to `sponsored`; any unrecognised value also falls back to `sponsored`** rather than throwing. Sponsorship only actually engages when the resource server's x402 quote offers a sponsored option (`extra.gasModel: "facilitator"`) — a server that only quotes self-pay behaves unchanged. **Sponsorship is testnet-only today**; override per-call with the tool's `gasPayer` parameter |
|
|
190
197
|
| `MAX_SETTLEMENT_ATTEMPTS` | no | Cap on retry attempts while polling a sponsored payment for settlement after SSIVC returns `202 Accepted` (queued). **Defaults to `20`; a non-positive or unparseable value also falls back to `20`** |
|