@visa/cli 4.1.0-rc.29 → 4.1.0-rc.290

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.
Files changed (94) hide show
  1. package/README.md +310 -46
  2. package/dist/checkout-engine/adapters/generic.d.ts +69 -0
  3. package/dist/checkout-engine/adapters/generic.js +383 -58
  4. package/dist/checkout-engine/adapters/index.d.ts +4 -1
  5. package/dist/checkout-engine/adapters/index.js +10 -3
  6. package/dist/checkout-engine/adapters/shopify.d.ts +98 -0
  7. package/dist/checkout-engine/adapters/shopify.js +744 -0
  8. package/dist/checkout-engine/amount.d.ts +17 -0
  9. package/dist/checkout-engine/amount.js +72 -0
  10. package/dist/checkout-engine/browser-launch.d.ts +9 -4
  11. package/dist/checkout-engine/browser-launch.js +19 -4
  12. package/dist/checkout-engine/browserbase-browser.d.ts +24 -0
  13. package/dist/checkout-engine/browserbase-browser.js +186 -0
  14. package/dist/checkout-engine/cli-engine.d.ts +241 -32
  15. package/dist/checkout-engine/cli-engine.js +960 -222
  16. package/dist/checkout-engine/confirmed-merchants.d.ts +31 -0
  17. package/dist/checkout-engine/confirmed-merchants.js +165 -0
  18. package/dist/checkout-engine/detect.d.ts +1 -1
  19. package/dist/checkout-engine/detect.js +6 -0
  20. package/dist/checkout-engine/evidence.d.ts +1 -1
  21. package/dist/checkout-engine/executor.d.ts +92 -4
  22. package/dist/checkout-engine/executor.js +688 -157
  23. package/dist/checkout-engine/hosted-approval.d.ts +69 -9
  24. package/dist/checkout-engine/hosted-approval.js +211 -21
  25. package/dist/checkout-engine/index.d.ts +9 -3
  26. package/dist/checkout-engine/index.js +7 -2
  27. package/dist/checkout-engine/instrument.d.ts +6 -0
  28. package/dist/checkout-engine/known-merchants.d.ts +10 -0
  29. package/dist/checkout-engine/known-merchants.js +38 -0
  30. package/dist/checkout-engine/live-fill-approval.d.ts +5 -11
  31. package/dist/checkout-engine/live-fill-approval.js +20 -34
  32. package/dist/checkout-engine/mandate/card-mandate.d.ts +6 -2
  33. package/dist/checkout-engine/mandate/card-mandate.js +10 -5
  34. package/dist/checkout-engine/mandate/mandate-ledger.d.ts +63 -23
  35. package/dist/checkout-engine/mandate/mandate-ledger.js +124 -17
  36. package/dist/checkout-engine/mandate.d.ts +8 -0
  37. package/dist/checkout-engine/mandate.js +44 -9
  38. package/dist/checkout-engine/receipt-dir.d.ts +6 -0
  39. package/dist/checkout-engine/receipt-dir.js +8 -0
  40. package/dist/checkout-engine/receipt.d.ts +56 -2
  41. package/dist/checkout-engine/receipt.js +55 -16
  42. package/dist/checkout-engine/shopify-primary-domain.d.ts +25 -0
  43. package/dist/checkout-engine/shopify-primary-domain.js +96 -0
  44. package/dist/checkout-engine/trace-handles.d.ts +8 -0
  45. package/dist/checkout-engine/trace-handles.js +12 -0
  46. package/dist/checkout-engine/types.d.ts +15 -2
  47. package/dist/checkout-engine/unresolved-charges.d.ts +34 -0
  48. package/dist/checkout-engine/unresolved-charges.js +134 -0
  49. package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +78 -7
  50. package/dist/checkout-engine/vgs-gateway/server-mint-client.js +342 -27
  51. package/dist/checkout-engine/vgs-live-instrument.d.ts +11 -35
  52. package/dist/checkout-engine/vgs-live-instrument.js +14 -74
  53. package/dist/checkout-engine/vic-confirmation.d.ts +18 -0
  54. package/dist/checkout-engine/vic-confirmation.js +9 -3
  55. package/dist/checkout-engine/web-bot-auth.d.ts +98 -0
  56. package/dist/checkout-engine/web-bot-auth.js +218 -0
  57. package/dist/cli.js +935 -389
  58. package/dist/managed-runtime/resolve-and-update.mjs +268 -0
  59. package/dist/managed-runtime/runtime-readiness.mjs +126 -0
  60. package/dist/managed-runtime/update-and-restart.mjs +1079 -0
  61. package/dist/mcp-apps/ucp-checkout.html +280 -0
  62. package/dist/mcp-server/index.js +762 -257
  63. package/dist/merchant-ucp-mcp/index.js +6 -0
  64. package/dist/skills/pair-visa-agent/RUNTIMES.md +122 -79
  65. package/dist/skills/pair-visa-agent/SKILL.md +434 -318
  66. package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +407 -0
  67. package/dist/skills/pair-visa-agent/scripts/setup.mjs +310 -30
  68. package/dist/skills/visa-shopify-checkout/SKILL.md +122 -0
  69. package/dist/skills/visa-shopify-checkout/references/evidence-and-states.md +37 -0
  70. package/dist/skills/visa-ucp-shopping/SKILL.md +86 -0
  71. package/dist/subway-direct.mjs +1 -0
  72. package/install.ps1 +7 -6
  73. package/install.sh +3 -3
  74. package/native/bin/darwin-arm64/visa-runtime-signer +0 -0
  75. package/native/bin/darwin-x64/visa-runtime-signer +0 -0
  76. package/native/bin/linux-arm64/visa-runtime-signer +0 -0
  77. package/native/bin/linux-x64/visa-runtime-signer +0 -0
  78. package/native/bin/win32-arm64/visa-runtime-signer.exe +0 -0
  79. package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
  80. package/native/bin/win32-x64/visa-runtime-signer.exe +0 -0
  81. package/package.json +33 -29
  82. package/server.json +4 -4
  83. package/dist/checkout-engine/inline-target.d.ts +0 -13
  84. package/dist/checkout-engine/inline-target.js +0 -37
  85. package/dist/checkout-engine/pay-args.d.ts +0 -14
  86. package/dist/checkout-engine/pay-args.js +0 -44
  87. package/dist/checkout-engine/pay.d.ts +0 -1
  88. package/dist/checkout-engine/pay.js +0 -13
  89. package/dist/checkout-engine/repo-env.d.ts +0 -11
  90. package/dist/checkout-engine/repo-env.js +0 -23
  91. package/dist/checkout-engine/run-live-fill.d.ts +0 -1
  92. package/dist/checkout-engine/run-live-fill.js +0 -493
  93. package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
  94. package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
@@ -1,13 +1,14 @@
1
1
  import { type Browser } from 'playwright-core';
2
- import { prepareCheckout as realPrepareCheckout, submitApprovedCheckout as realSubmitApprovedCheckout, type PreparedCheckoutSessionStore } from './executor.js';
3
- import { runHostedApproval as realRunHostedApproval } from './hosted-approval.js';
2
+ import { prepareCheckout as realPrepareCheckout, submitApprovedCheckout as realSubmitApprovedCheckout, type CheckoutMode, type CheckoutOutcome, type CheckoutFailureCode, type CheckoutResult, type PreparedCheckoutSessionStore } from './executor.js';
3
+ import type { MandateRefusalCode } from './mandate.js';
4
+ import { claimMandatePickup as realClaimMandatePickup, runHostedApproval as realRunHostedApproval } from './hosted-approval.js';
4
5
  import { type VgsCheckoutTarget } from './vgs-live-instrument.js';
5
- import { serverFetchCryptogram } from './vgs-gateway/server-mint-client.js';
6
+ import { ServerIntentError, serverFetchCryptogram, serverPostConfirmation } from './vgs-gateway/server-mint-client.js';
6
7
  import { type CardMandateFacts } from './mandate/card-mandate.js';
7
8
  import { MandateLedger } from './mandate/mandate-ledger.js';
8
9
  import { writeReceipt as realWriteReceipt } from './receipt.js';
9
10
  import { reportVicOutcome as realReportVicOutcome, type VicConfirmationReport } from './vic-confirmation.js';
10
- import type { Contact } from './types.js';
11
+ import type { Contact, OtpResolver } from './types.js';
11
12
  /**
12
13
  * A card-mandate draw failed transiently (retryable) rather than definitively.
13
14
  * Gateway 5xx, "server cryptogram not completed / try again", and network
@@ -16,15 +17,45 @@ import type { Contact } from './types.js';
16
17
  * is classified by its underlying gateway error. Exported for tests.
17
18
  */
18
19
  export declare function isTransientDrawFailure(err: unknown): boolean;
20
+ /**
21
+ * A verdict refusal may be wrapped by drawFromMandate after its local
22
+ * reservation is released. Walk the cause chain so the original auth status +
23
+ * reasons still decide whether the mandate is permanently disabled.
24
+ */
25
+ export declare function classifyCardDrawVerdictFailure(err: unknown): {
26
+ transient: boolean;
27
+ grantCapacity: boolean;
28
+ reasons: string[];
29
+ } | null;
19
30
  export type CliReviewInput = {
20
31
  url: string;
32
+ checkoutRoute: 'guest-card';
21
33
  amount: string;
22
34
  currency: string;
23
35
  credentialPath: string;
36
+ /** See {@link CardInstrumentSource}. */
37
+ cardTokenId?: string;
38
+ /** Exact request-key identity selected by the caller. */
39
+ agentJkt?: string;
40
+ /** Owner-facing selected-agent label for the compact local receipt. */
41
+ agentName?: string;
42
+ /** Safe display suffix derived from the selected card grant label. */
43
+ cardLast4?: string;
24
44
  contact: Contact;
25
45
  approvalBaseUrl: string;
46
+ /**
47
+ * #8047: agent-authored purchase narrative captured at review time and
48
+ * replayed into the card draw's Transaction Intent Journal at pay time.
49
+ */
50
+ intentNarrative?: CardIntentNarrative;
26
51
  merchantName?: string;
27
52
  merchantCountryCode?: string;
53
+ trustedMerchantIdentity?: Readonly<{
54
+ handoffId: string;
55
+ checkoutId: string;
56
+ allowedOrigins: readonly string[];
57
+ expiresAt: string;
58
+ }>;
28
59
  };
29
60
  export type CliReviewFacts = {
30
61
  reviewId: string;
@@ -34,6 +65,24 @@ export type CliReviewFacts = {
34
65
  submitTargetFingerprint: string;
35
66
  detectedRoles: string[];
36
67
  };
68
+ /**
69
+ * A checkout was inspected successfully but cannot be reviewed safely.
70
+ *
71
+ * The explicit fields survive the CLI's copied-engine boundary structurally,
72
+ * so MCP callers do not have to parse the human-readable message.
73
+ */
74
+ export declare class CheckoutReviewRefusedError extends Error {
75
+ readonly code = "CHECKOUT_REVIEW_REFUSED";
76
+ readonly checkoutOutcome: CheckoutOutcome;
77
+ readonly failureCode?: CheckoutFailureCode;
78
+ /** Bounded mandate/trusted-identity reason (#8669); absent for other outcomes. */
79
+ readonly refusalCode?: MandateRefusalCode;
80
+ readonly requiresAdapter: string[];
81
+ readonly detectedRoles: string[];
82
+ readonly receiptWrite: ReceiptWriteObservation;
83
+ readonly detail?: string;
84
+ constructor(result: CheckoutResult, receiptWrite: ReceiptWriteObservation);
85
+ }
37
86
  export type CliPayInput = CliReviewInput & {
38
87
  reviewId: string;
39
88
  submit: boolean;
@@ -45,36 +94,48 @@ export type CliPayInput = CliReviewInput & {
45
94
  onApprovalUrl?: (url: string) => void;
46
95
  };
47
96
  export type CliReceiptFacts = {
48
- outcome: string;
97
+ outcome: CheckoutOutcome;
49
98
  confirmationRef: string | null;
50
99
  receiptPath: string | null;
51
100
  detail: string | null;
52
101
  vicConfirmation: VicConfirmationReport | null;
53
102
  /**
54
103
  * Which credential path actually ran: `mandate` = tap-free draw against an
55
- * existing card mandate; `fresh-tap` = today's 1:1 hosted-passkey mint; `null`
56
- * = neither ran (a pre-flight refusal, e.g. no prepared review). Transparency,
57
- * never magic — the caller can always see whether a passkey was skipped.
104
+ * existing card mandate; `null` = none ran (a pre-flight refusal, e.g. no
105
+ * prepared review or no covering mandate). Transparency, never magic — the
106
+ * caller can always see whether a passkey was skipped.
58
107
  */
59
- source: 'mandate' | 'fresh-tap' | null;
108
+ source: 'mandate' | null;
60
109
  /** Remaining mandate budget (minor units) after a mandate draw; else null. */
61
110
  remainingMinor: number | null;
111
+ /**
112
+ * Immutable VIC/VGS intent used for server-side receipt correlation. This is
113
+ * an opaque non-credential identifier; it never carries the mint/verdict
114
+ * bearer that authorizes a cryptogram or confirmation. Present only after a
115
+ * card confirmation target exists.
116
+ */
117
+ processorIntentId?: string | null;
118
+ /** True only when the checkout engine recorded a credential-minted step. */
119
+ credentialIssued: boolean;
120
+ /** True only when at least one payment field was successfully filled. */
121
+ credentialDisclosed: boolean;
122
+ /** Privacy-safe local receipt persistence outcome for centralized correlation. */
123
+ receiptWrite?: ReceiptWriteObservation;
124
+ };
125
+ type PayAttempt = {
126
+ fingerprint: string;
127
+ promise: Promise<CliReceiptFacts>;
62
128
  };
63
129
  export type CliStartMandateInput = {
64
- /** The merchant URL for a merchant-scoped mandate. Optional (and ignored) when
65
- * `anyMerchant` is set — a budget mandate is not tied to a merchant. */
66
- url?: string;
130
+ /** Optional local card-capability selector (legacy name or exact request-key JKT). */
131
+ agentRef?: string;
67
132
  ceiling: string;
68
133
  currency: string;
69
134
  credentialPath: string;
135
+ /** See {@link CardInstrumentSource}. */
136
+ cardTokenId?: string;
70
137
  contact: Contact;
71
138
  approvalBaseUrl: string;
72
- merchantName?: string;
73
- merchantCountryCode?: string;
74
- /** ISO 8601 mandate expiry; defaults to now + 24h. */
75
- expiresAt?: string;
76
- /** Max draws the ceiling intent may fulfil. */
77
- maxDraws?: number;
78
139
  /**
79
140
  * Per-purchase cap (decimal string, > 0 and <= ceiling). Registered with the
80
141
  * approval context so the operator reads it as a worst-case term, and carried
@@ -87,11 +148,33 @@ export type CliStartMandateInput = {
87
148
  */
88
149
  intent?: string;
89
150
  /**
90
- * BUDGET mode: create a mandate spendable at ANY merchant (no merchant lock),
91
- * bounded by the ceiling + per-transaction limit. The passkey approval shows
92
- * "spend budget mandate" so the owner consents to the broader scope.
151
+ * Per-call relay of the hosted-approval URL, the moment it is known and
152
+ * BEFORE the (up-to-timeout) wait. Takes precedence over the engine-level
153
+ * `deps.onApprovalUrl`.
154
+ *
155
+ * The engine seam was deps-only, and the MCP surface builds its engine once
156
+ * per process without it — so `start_card_mandate` produced an approval URL,
157
+ * printed it to stderr, and blocked the agent's turn for minutes with no way
158
+ * to tell the human what to open. A per-call hook lets one caller relay the
159
+ * URL without every caller sharing one engine-wide callback.
160
+ */
161
+ onApprovalUrl?: (url: string) => void;
162
+ };
163
+ export type CliClaimMandateInput = {
164
+ /** Optional local card-capability selector (legacy name or exact request-key JKT). */
165
+ agentRef?: string;
166
+ /** Single-use pickup code the owner handed over from the panel. */
167
+ pickupCode: string;
168
+ /**
169
+ * The agent handoff this runtime just redeemed, when the pickup code rode
170
+ * inside its claim code. A dialog-minted budget pins that handoff instead of
171
+ * a key — there was no key yet — so this is what the pin is checked against.
93
172
  */
94
- anyMerchant?: boolean;
173
+ expectedHandoffId?: string;
174
+ credentialPath: string;
175
+ /** See {@link CardInstrumentSource}. */
176
+ cardTokenId?: string;
177
+ approvalBaseUrl: string;
95
178
  };
96
179
  export type CliMandateFacts = CardMandateFacts & {
97
180
  merchantHost: string;
@@ -99,19 +182,86 @@ export type CliMandateFacts = CardMandateFacts & {
99
182
  * True when the mandate minted its ceiling intent but the #5942 register
100
183
  * handshake failed, so `findCovering` will SKIP it and no tap-free draw is
101
184
  * possible. The mandate exists but is not usable — the caller must surface
102
- * this (not report a plain success). Absent/false → registered (or no
103
- * delegated binding was present, so register was intentionally not attempted).
185
+ * this (not report a plain success). Absent/false means registration succeeded;
186
+ * mandate-start now refuses before approval when no capability can register.
104
187
  */
105
188
  registerFailed?: boolean;
189
+ /**
190
+ * The server's refusal reason when {@link registerFailed} is true, verbatim.
191
+ * Carried out so the CLI can distinguish causes that need DIFFERENT operator
192
+ * actions — notably `token_mismatch`, which means the on-device grant record's
193
+ * cached token no longer matches the owner's live agentic token (they
194
+ * re-enrolled a card after the grant) and is fixed by re-running `grant-card`,
195
+ * not by retrying `mandate start`. Without the reason every failure reads as
196
+ * "the auth server was unreachable", which sends the operator in a loop.
197
+ */
198
+ registerFailureReason?: string;
199
+ };
200
+ /**
201
+ * The owner approved a budget but the intent bootstrap did not complete
202
+ * (#8470). `resumable` means the same approval can be resumed without a second
203
+ * passkey ceremony: the server's bootstrap state is at-most-once per signed
204
+ * token, so a resume can only recover an intent that already exists or
205
+ * dispatch once when nothing was ever dispatched — never create a sibling.
206
+ */
207
+ export type CardMandateActivationFacts = {
208
+ phase: 'intent';
209
+ /** `uncertain`: the provider may have created the intent. `not_created`: proven not. */
210
+ outcome: 'uncertain' | 'not_created';
211
+ resumable: boolean;
212
+ status: number | null;
213
+ errorCode: string | null;
214
+ requestId: string | null;
215
+ bootstrapState: string;
216
+ /** Opaque, process-bound handle for {@link CliCheckoutEngine.resumeCardMandate}. */
217
+ resumeToken?: string;
218
+ /** When the approval's bootstrap credential stops being usable. */
219
+ resumeExpiresAt?: string;
220
+ };
221
+ export declare class CardMandateActivationError extends Error {
222
+ readonly facts: CardMandateActivationFacts;
223
+ readonly code = "CARD_MANDATE_ACTIVATION_INCOMPLETE";
224
+ constructor(message: string, facts: CardMandateActivationFacts);
225
+ }
226
+ export type CliResumeMandateInput = {
227
+ resumeToken: string;
228
+ };
229
+ /**
230
+ * Sort a budget-intent route failure into resume semantics. Exported for the
231
+ * regression net; the truth table is the product contract of #8470.
232
+ */
233
+ export declare function classifyServerIntentFailure(err: ServerIntentError): {
234
+ outcome: 'uncertain' | 'not_created';
235
+ resumable: boolean;
106
236
  };
107
237
  type Session = {
108
238
  browser: Browser;
239
+ /** Exact caller URL repeated at pay time; may contain a UCP capability. */
240
+ requestUrl: string;
241
+ checkoutRoute: 'guest-card';
109
242
  target: VgsCheckoutTarget;
110
243
  amountMinor: number;
111
244
  currency: string;
112
245
  contact: Contact;
246
+ agentJkt?: string;
247
+ /** #8047: narrative for this review's journal, carried review -> pay. */
248
+ intentNarrative?: CardIntentNarrative;
113
249
  cleanupTimer: ReturnType<typeof setTimeout>;
114
250
  };
251
+ /**
252
+ * #8047 Transaction Intent Journal narrative — the agent-authored half of the
253
+ * journal. Structural mirror of the CLI card client's `CardIntentNarrative`;
254
+ * the engine only threads it through and never interprets it.
255
+ */
256
+ export interface CardIntentNarrative {
257
+ userRequest: string;
258
+ product: string;
259
+ purpose?: string;
260
+ quantity?: number;
261
+ expectedResult?: string;
262
+ recurring?: boolean;
263
+ userIntentSalt?: string;
264
+ }
115
265
  export interface CardDrawVerdictDraw {
116
266
  tokenId: string;
117
267
  amount: string;
@@ -123,32 +273,39 @@ export interface CardDrawVerdictDraw {
123
273
  export interface CardDrawVerdictCapability {
124
274
  /** Opaque to the engine — passed straight back to {@link CardDrawVerdictSeam.fetchVerdict}. */
125
275
  agentKey: unknown;
276
+ /** Root-signed delegation for a short-lived device key. */
277
+ runtimeCertificate?: string;
126
278
  agentJkt: string;
127
279
  /** Auth origin that minted the binding and hosts the /v4/card/draw* routes. */
128
280
  authBaseUrl: string;
129
281
  }
130
282
  export interface CardDrawVerdictSeam {
131
- loadCapability: () => CardDrawVerdictCapability | null;
283
+ loadCapability: (agentRef?: string) => CardDrawVerdictCapability | null;
132
284
  fetchVerdict: (input: {
133
285
  authBaseUrl: string;
134
286
  agentKey: unknown;
287
+ runtimeCertificate?: string;
135
288
  mandateId: string;
136
289
  drawId: string;
137
290
  draw: CardDrawVerdictDraw;
291
+ /** #8047: narrative the client turns into the draw's journal sidecar. */
292
+ intentNarrative?: CardIntentNarrative;
138
293
  }) => Promise<{
139
294
  verdict: string;
140
295
  remainingCents: number;
141
296
  }>;
142
297
  }
143
298
  export interface CardMandateRegisterSeam {
144
- loadCapability: () => {
299
+ loadCapability: (agentRef?: string) => {
145
300
  agentKey: unknown;
301
+ runtimeCertificate?: string;
146
302
  agentJkt: string;
147
303
  authBaseUrl: string;
148
304
  } | null;
149
305
  register: (input: {
150
306
  authBaseUrl: string;
151
307
  agentKey: unknown;
308
+ runtimeCertificate?: string;
152
309
  mandateId: string;
153
310
  mintToken: string;
154
311
  ceiling: string;
@@ -156,6 +313,12 @@ export interface CardMandateRegisterSeam {
156
313
  }) => Promise<{
157
314
  ok: boolean;
158
315
  reason?: string;
316
+ /**
317
+ * The ceiling the server committed — `min(requested, the owner's live card
318
+ * grant cap)`. Omitted by an older auth that does not report it, in which
319
+ * case the requested ceiling stands.
320
+ */
321
+ approvedCeilingMinor?: number;
159
322
  }>;
160
323
  }
161
324
  export type CliEngineDeps = {
@@ -163,6 +326,8 @@ export type CliEngineDeps = {
163
326
  prepareCheckout?: typeof realPrepareCheckout;
164
327
  submitApprovedCheckout?: typeof realSubmitApprovedCheckout;
165
328
  runHostedApproval?: typeof realRunHostedApproval;
329
+ /** Injectable pickup-code redeem for claimCardMandate — tests pass a fake. */
330
+ claimMandatePickup?: typeof realClaimMandatePickup;
166
331
  /**
167
332
  * Relay the hosted-approval URL to the caller as DATA the moment it is known,
168
333
  * before the (up-to-timeout) poll wait. A headless agent surface wires this to
@@ -172,8 +337,18 @@ export type CliEngineDeps = {
172
337
  onApprovalUrl?: (url: string) => void;
173
338
  reportVicOutcome?: typeof realReportVicOutcome;
174
339
  writeReceipt?: typeof realWriteReceipt;
340
+ /**
341
+ * Privacy-safe receipt persistence lifecycle signal. The event deliberately
342
+ * excludes the receipt body, checkout URL, filesystem path, and raw error.
343
+ * Observer failures are swallowed so logging can never alter checkout state.
344
+ */
345
+ onReceiptWrite?: (event: ReceiptWriteObservation) => void | Promise<void>;
346
+ /** Injectable privacy-safe id source for terminal observations before reviewId exists. */
347
+ createObservationId?: () => string;
175
348
  store?: PreparedCheckoutSessionStore;
176
349
  sessions?: Map<string, Session>;
350
+ /** Exact-review singleflight registry; tests inject a fresh map for isolation. */
351
+ payAttempts?: Map<string, PayAttempt>;
177
352
  ttlMs?: number;
178
353
  /** Owner-only card-mandate ledger — defaults to the ~/.visa-mcp singleton. */
179
354
  ledger?: MandateLedger;
@@ -185,24 +360,58 @@ export type CliEngineDeps = {
185
360
  * markUnhonored path with no network.
186
361
  */
187
362
  serverFetchCryptogram?: typeof serverFetchCryptogram;
363
+ /** Injectable confirmation transport; defaults to verify-web. */
364
+ serverPostConfirmation?: typeof serverPostConfirmation;
188
365
  /**
189
366
  * #5923 delegated card-draw verdict seam (see {@link CardDrawVerdictSeam}).
190
- * Injected by the CLI when the agent holds a mode='card' delegated binding;
191
- * when absent the covering-mandate draw keeps using the bearer mint token (the
192
- * shipped #5917 flow, unchanged).
367
+ * Injected by the CLI when the runtime holds separately provisioned card
368
+ * authority. When absent, a covering-mandate draw fails before cryptogram mint.
193
369
  */
194
370
  cardDrawVerdict?: CardDrawVerdictSeam;
195
371
  /**
196
372
  * #5942 delegated card-mandate register seam (see {@link CardMandateRegisterSeam}).
197
- * Injected by the CLI when the agent holds a mode='card' delegated binding;
198
- * when absent mandate-start skips the register (best-effort — the bearer path
199
- * still works, only the delegated draw needs the register row).
373
+ * Required by mandate-start. When absent, the ceremony is refused before
374
+ * passkey approval because a budget token cannot act as draw authority.
200
375
  */
201
376
  cardMandateRegister?: CardMandateRegisterSeam;
377
+ /**
378
+ * Canonical mailbox OTP reader supplied by the runtime. The checkout engine
379
+ * owns no mailbox credential and never imports an email provider package.
380
+ */
381
+ resolveEmailOtp?: OtpResolver;
382
+ };
383
+ export type ReceiptWriteObservation = {
384
+ event: 'checkout_receipt_write';
385
+ status: 'written' | 'failed';
386
+ mode: CheckoutMode;
387
+ checkoutOutcome: CheckoutOutcome;
388
+ merchantHost: string;
389
+ observationId: string;
390
+ failureCode: string | null;
391
+ errorCode: string | null;
392
+ panRedactions: number | null;
202
393
  };
203
394
  export declare function createCliCheckoutEngine(deps?: CliEngineDeps): {
204
395
  startCardMandate(input: CliStartMandateInput): Promise<CliMandateFacts>;
396
+ resumeCardMandate(input: CliResumeMandateInput): Promise<CliMandateFacts>;
397
+ claimCardMandate(input: CliClaimMandateInput): Promise<CliMandateFacts>;
205
398
  review(input: CliReviewInput): Promise<CliReviewFacts>;
399
+ /**
400
+ * Give up a prepared review without paying it (#7100).
401
+ *
402
+ * The retained browser is what lets a same-process MCP `review` → `pay`
403
+ * draw against the checkout it already inspected. A TERMINAL review-only
404
+ * run has no such second call — that process tells the operator to re-RUN
405
+ * with `--submit`, and the new process cannot reach this one's in-memory
406
+ * session. So the browser sat open for the full TTL, and because a live
407
+ * browser connection keeps the event loop alive, a command that had already
408
+ * succeeded looked hung until Ctrl-C.
409
+ *
410
+ * Idempotent and non-throwing: an unknown or already-released id is a
411
+ * no-op, so it is safe on an error path that may not have prepared anything
412
+ * and safe to call twice.
413
+ */
414
+ releaseReview(reviewId: string): Promise<void>;
206
415
  pay(input: CliPayInput): Promise<CliReceiptFacts>;
207
416
  };
208
417
  export {};