@absol-labs/agent 0.3.2 → 0.4.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.
@@ -0,0 +1,336 @@
1
+ import { canonicalJson, deliveryReceiptSchema, signDeliveryReceipt, } from "@absol-labs/shared";
2
+ import { getHttpProviderClaimParamsFromProof, getProviderHashRequirementsFromSpec, verifyProof, } from "@reclaimprotocol/js-sdk";
3
+ import { keccak256, stringToBytes, } from "viem";
4
+ import { z } from "zod";
5
+ import { asErrorMessage, buildDeliveryContextEnvelopeV2, defaultReclaimClientFactory, deliveryContextEnvelopeV2Schema, parseReclaimProofEnv, ResponseProofUnavailableError, ResponseProofVerificationError, toRequestSpec, } from "./reclaim.js";
6
+ /**
7
+ * T2 consumer zkTLS delivery envelope (issue #24, metrik-protocol#67 DECISION).
8
+ *
9
+ * This is the BUYER-side counterpart to `streamproof-oracle`'s T2 accrue-on-proof
10
+ * ingest (`src/delivery/proof-ingest.ts`). Given the stream identity, an
11
+ * oracle-issued nonce (`GET /delivery/nonce`), and the paid-route request spec,
12
+ * it: (1) proves the paid route via a pluggable zkTLS attestor with the nonce
13
+ * bound into the request and the Metrik v2 envelope bound into
14
+ * `claimData.context`; (2) signs a `DeliveryReceipt` (`@absol-labs/shared`)
15
+ * committing to that proof; and (3) returns the exact `POST /delivery/proof`
16
+ * request body the oracle's `deliveryProofRequestSchema` expects.
17
+ *
18
+ * The attestor is pluggable (`DeliveryProofAttestor`) specifically so this can
19
+ * be tested WITHOUT a live Reclaim App Secret (agent#22 is the human-intervention
20
+ * issue for that credential) - the default production attestor
21
+ * (`createReclaimT2AttestorFromEnv`) still requires it.
22
+ */
23
+ const bytes32Hex = /^0x[0-9a-fA-F]{64}$/;
24
+ const evmAddress = /^0x[0-9a-fA-F]{40}$/;
25
+ const hexSignature = /^0x[0-9a-fA-F]+$/;
26
+ export class PaidRouteNonceInjectionError extends Error {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = "PaidRouteNonceInjectionError";
30
+ }
31
+ }
32
+ /** Injects the oracle-issued nonce into the paid-route request (query or body only). */
33
+ function injectNonce(spec, nonce) {
34
+ if (spec.nonceInjection.in === "query") {
35
+ let url;
36
+ try {
37
+ url = new URL(spec.url);
38
+ }
39
+ catch (cause) {
40
+ throw new PaidRouteNonceInjectionError(`paid route url is not a valid absolute URL: ${asErrorMessage(cause)}`);
41
+ }
42
+ url.searchParams.set(spec.nonceInjection.name, nonce);
43
+ return { url: url.toString(), body: spec.body };
44
+ }
45
+ const placeholder = spec.nonceInjection.placeholder;
46
+ const body = spec.body ?? "";
47
+ if (!body.includes(placeholder)) {
48
+ throw new PaidRouteNonceInjectionError(`paid route body does not contain the nonce placeholder "${placeholder}"`);
49
+ }
50
+ return { url: spec.url, body: body.split(placeholder).join(nonce) };
51
+ }
52
+ const reclaimT2UrlSchema = z.string().url();
53
+ /**
54
+ * Production `DeliveryProofAttestor`: drives the real Reclaim zkFetch + proof
55
+ * verification pipeline (mirrors `ReclaimConsumerProofService` in `reclaim.ts`,
56
+ * but binds the FLAT T2 v2 envelope directly as `context` instead of the v1
57
+ * `{contextAddress, contextMessage}` wrapper).
58
+ */
59
+ export class ReclaimDeliveryProofAttestor {
60
+ client;
61
+ config;
62
+ verifyProofImpl;
63
+ constructor(config, options = {}) {
64
+ this.config = {
65
+ ...config,
66
+ logs: config.logs ?? false,
67
+ };
68
+ this.client = (options.createClient ?? defaultReclaimClientFactory)(this.config.applicationId, this.config.applicationSecret, this.config.logs ?? false);
69
+ this.verifyProofImpl = options.verifyProof ?? verifyProof;
70
+ }
71
+ async proveDeliveryResponse(input) {
72
+ reclaimT2UrlSchema.parse(input.url);
73
+ if (input.responseMatches.length === 0) {
74
+ throw new Error("at least one response match is required");
75
+ }
76
+ const context = deliveryContextEnvelopeV2Schema.parse(input.context);
77
+ const parsed = { ...input, context };
78
+ const method = parsed.method ?? "GET";
79
+ const proof = await this.client
80
+ .zkFetch(parsed.url, {
81
+ method,
82
+ ...(parsed.body === undefined ? {} : { body: parsed.body }),
83
+ ...(parsed.headers === undefined ? {} : { headers: parsed.headers }),
84
+ ...(parsed.geoLocation === undefined
85
+ ? {}
86
+ : { geoLocation: parsed.geoLocation }),
87
+ context: parsed.context,
88
+ ...(parsed.useTee === undefined ? {} : { useTee: parsed.useTee }),
89
+ ...(parsed.zkEngine === undefined
90
+ ? {}
91
+ : { zkEngine: parsed.zkEngine }),
92
+ }, {
93
+ ...(parsed.secretHeaders === undefined
94
+ ? {}
95
+ : { headers: parsed.secretHeaders }),
96
+ responseMatches: parsed.responseMatches,
97
+ ...(parsed.responseRedactions === undefined
98
+ ? {}
99
+ : { responseRedactions: parsed.responseRedactions }),
100
+ ...(parsed.cookie === undefined ? {} : { cookieStr: parsed.cookie }),
101
+ ...(parsed.secretParamValues === undefined
102
+ ? {}
103
+ : { paramValues: parsed.secretParamValues }),
104
+ })
105
+ .catch((error) => {
106
+ throw new ResponseProofUnavailableError(`failed to generate T2 zkTLS delivery proof: ${asErrorMessage(error)}`);
107
+ });
108
+ if (proof === undefined) {
109
+ throw new ResponseProofUnavailableError("reclaim returned no zkTLS delivery proof");
110
+ }
111
+ const verification = await this.verifyProofImpl(proof, {
112
+ ...getProviderHashRequirementsFromSpec({
113
+ requests: [toRequestSpec(parsed, method)],
114
+ }),
115
+ ...(parsed.useTee === true
116
+ ? { teeAttestation: { appSecret: this.config.applicationSecret } }
117
+ : {}),
118
+ });
119
+ if (!verification.isVerified) {
120
+ throw new ResponseProofVerificationError(`T2 zkTLS delivery proof verification failed: ${asErrorMessage(verification.error)}`);
121
+ }
122
+ const trusted = verification.data[0];
123
+ if (trusted === undefined) {
124
+ throw new ResponseProofVerificationError("T2 zkTLS delivery proof verification returned no trusted data");
125
+ }
126
+ const request = getHttpProviderClaimParamsFromProof(proof);
127
+ return {
128
+ proof,
129
+ request: {
130
+ url: request.url,
131
+ method: request.method,
132
+ body: request.body === "" ? null : (request.body ?? null),
133
+ responseMatches: request.responseMatches.map((match) => ({
134
+ value: match.value,
135
+ type: match.type,
136
+ invert: match.invert,
137
+ isOptional: match.isOptional,
138
+ })),
139
+ responseRedactions: request.responseRedactions.map((redaction) => ({
140
+ regex: redaction.regex,
141
+ jsonPath: redaction.jsonPath,
142
+ xPath: redaction.xPath,
143
+ hash: redaction.hash,
144
+ })),
145
+ },
146
+ verification: {
147
+ context: trusted.context,
148
+ extractedParameters: trusted.extractedParameters,
149
+ publicData: verification.publicData[0] ?? null,
150
+ isTeeAttestationVerified: verification.isTeeAttestationVerified ?? false,
151
+ isAttestorTeeAttestationVerified: verification.isAttestorTeeAttestationVerified ?? false,
152
+ },
153
+ };
154
+ }
155
+ }
156
+ /** `null` when `RECLAIM_APP_ID`/`RECLAIM_APP_SECRET` are not configured (see agent#22). */
157
+ export function createReclaimT2AttestorFromEnv(env = process.env) {
158
+ const parsed = parseReclaimProofEnv(env);
159
+ if (parsed === null)
160
+ return null;
161
+ return new ReclaimDeliveryProofAttestor(parsed);
162
+ }
163
+ const wireDeliveryReceiptSchema = z
164
+ .object({
165
+ streamId: z.union([
166
+ z.string().regex(bytes32Hex),
167
+ z.string().regex(/^\d+$/),
168
+ ]),
169
+ serviceRef: z
170
+ .string()
171
+ .regex(bytes32Hex, "must be a 32-byte hex serviceRef"),
172
+ operator: z.string().regex(evmAddress, "must be an EVM address"),
173
+ intervalIndex: z
174
+ .string()
175
+ .regex(/^\d+$/, "must be a non-negative integer string"),
176
+ nonce: z.string().regex(bytes32Hex, "must be a 32-byte hex nonce"),
177
+ reclaimProofId: z
178
+ .string()
179
+ .regex(bytes32Hex, "must be a 32-byte hex identifier"),
180
+ reclaimOwner: z.string().regex(evmAddress, "must be an EVM address"),
181
+ reclaimTimestamp: z
182
+ .string()
183
+ .regex(/^\d+$/, "must be a non-negative integer string"),
184
+ reclaimEpoch: z
185
+ .string()
186
+ .regex(/^\d+$/, "must be a non-negative integer string"),
187
+ canonicalRequestHash: z
188
+ .string()
189
+ .regex(bytes32Hex, "must be a 32-byte hex hash"),
190
+ canonicalBodyHash: z
191
+ .string()
192
+ .regex(bytes32Hex, "must be a 32-byte hex hash"),
193
+ })
194
+ .strict();
195
+ const reclaimProviderClaimDataSchema = z
196
+ .object({
197
+ provider: z.string().min(1),
198
+ parameters: z.string().min(1),
199
+ context: z.string(),
200
+ owner: z.string().regex(evmAddress, "must be an EVM address"),
201
+ timestampS: z.number().int().nonnegative(),
202
+ epoch: z.number().int().nonnegative(),
203
+ identifier: z
204
+ .string()
205
+ .regex(bytes32Hex, "must be a 32-byte hex identifier"),
206
+ })
207
+ .strict();
208
+ const reclaimProofWireSchema = z
209
+ .object({
210
+ identifier: z
211
+ .string()
212
+ .regex(bytes32Hex, "must be a 32-byte hex identifier"),
213
+ claimData: reclaimProviderClaimDataSchema,
214
+ signatures: z.array(z.string().regex(hexSignature)).min(1),
215
+ })
216
+ .strict();
217
+ /**
218
+ * `POST /delivery/proof` request body - mirrors the oracle's own
219
+ * `deliveryProofRequestSchema` (`streamproof-oracle/src/delivery/dto.ts`)
220
+ * field-for-field. Kept as a local copy (same convention the oracle itself
221
+ * uses for `@absol-labs/shared` shapes) rather than an import, since the two
222
+ * repos do not share a package for this DTO.
223
+ */
224
+ export const deliveryProofRequestBodySchema = z
225
+ .object({
226
+ receipt: wireDeliveryReceiptSchema,
227
+ signature: z.string().regex(hexSignature, "must be a hex signature"),
228
+ proof: reclaimProofWireSchema,
229
+ })
230
+ .strict();
231
+ /** Converts a bigint-typed `DeliveryReceipt` to the oracle's decimal-string wire shape. */
232
+ export function toWireDeliveryReceipt(receipt) {
233
+ return {
234
+ streamId: receipt.streamId.toString(),
235
+ serviceRef: receipt.serviceRef,
236
+ operator: receipt.operator,
237
+ intervalIndex: receipt.intervalIndex.toString(),
238
+ nonce: receipt.nonce,
239
+ reclaimProofId: receipt.reclaimProofId,
240
+ reclaimOwner: receipt.reclaimOwner,
241
+ reclaimTimestamp: receipt.reclaimTimestamp.toString(),
242
+ reclaimEpoch: receipt.reclaimEpoch.toString(),
243
+ canonicalRequestHash: receipt.canonicalRequestHash,
244
+ canonicalBodyHash: receipt.canonicalBodyHash,
245
+ };
246
+ }
247
+ /** Deterministic keccak256 over the canonical (sorted-key) JSON of `value`. */
248
+ function hashCanonical(value) {
249
+ return keccak256(stringToBytes(canonicalJson(value)));
250
+ }
251
+ export class NoDeliveryAttestorConfiguredError extends Error {
252
+ constructor() {
253
+ super("no zkTLS delivery attestor configured: set RECLAIM_APP_ID/RECLAIM_APP_SECRET " +
254
+ "(see agent#22) or pass an explicit `attestor`");
255
+ this.name = "NoDeliveryAttestorConfiguredError";
256
+ }
257
+ }
258
+ /**
259
+ * Produces a complete T2 delivery proof envelope: proves the paid route with
260
+ * the oracle-issued nonce bound into both the request and the Reclaim
261
+ * `claimData.context`, signs a `DeliveryReceipt` committing to that proof, and
262
+ * returns the exact body for `POST /delivery/proof`.
263
+ */
264
+ export async function buildT2DeliveryProofSubmission(input) {
265
+ const issuedAt = input.issuedAt ?? Math.floor(Date.now() / 1000);
266
+ if (input.intervalIndex > BigInt(Number.MAX_SAFE_INTEGER)) {
267
+ throw new RangeError("intervalIndex exceeds Number.MAX_SAFE_INTEGER");
268
+ }
269
+ const envelope = buildDeliveryContextEnvelopeV2({
270
+ streamId: input.streamId,
271
+ operator: input.operator,
272
+ serviceRef: input.serviceRef,
273
+ ...(input.mandateId === undefined ? {} : { mandateId: input.mandateId }),
274
+ nonce: input.nonce,
275
+ intervalIndex: Number(input.intervalIndex),
276
+ issuedAt,
277
+ });
278
+ const injected = injectNonce(input.request, input.nonce);
279
+ const attestor = input.attestor ?? createReclaimT2AttestorFromEnv();
280
+ if (attestor === null) {
281
+ throw new NoDeliveryAttestorConfiguredError();
282
+ }
283
+ const proved = await attestor.proveDeliveryResponse({
284
+ url: injected.url,
285
+ method: input.request.method,
286
+ body: injected.body,
287
+ headers: input.request.headers,
288
+ secretHeaders: input.request.secretHeaders,
289
+ responseMatches: input.request.responseMatches,
290
+ responseRedactions: input.request.responseRedactions,
291
+ cookie: input.request.cookie,
292
+ secretParamValues: input.request.secretParamValues,
293
+ geoLocation: input.request.geoLocation,
294
+ useTee: input.request.useTee,
295
+ zkEngine: input.request.zkEngine,
296
+ context: envelope,
297
+ });
298
+ const { proof } = proved;
299
+ // Deterministically bind BOTH the exact proven request (method/url/body, post
300
+ // nonce-injection) and the exact PROVEN (redacted/extracted) response content
301
+ // into the receipt, so the receipt is verifiable dispute evidence even though
302
+ // the oracle itself does not re-derive these hashes today.
303
+ const canonicalRequestHash = hashCanonical({
304
+ method: proved.request.method,
305
+ url: proved.request.url,
306
+ body: proved.request.body,
307
+ });
308
+ const canonicalBodyHash = hashCanonical({
309
+ extractedParameters: proved.verification.extractedParameters,
310
+ });
311
+ const receipt = deliveryReceiptSchema.parse({
312
+ streamId: BigInt(input.streamId),
313
+ serviceRef: input.serviceRef,
314
+ operator: input.operator,
315
+ intervalIndex: input.intervalIndex,
316
+ nonce: input.nonce,
317
+ reclaimProofId: proof.identifier,
318
+ reclaimOwner: proof.claimData.owner,
319
+ reclaimTimestamp: BigInt(proof.claimData.timestampS),
320
+ reclaimEpoch: BigInt(proof.claimData.epoch),
321
+ canonicalRequestHash,
322
+ canonicalBodyHash,
323
+ });
324
+ const signature = await signDeliveryReceipt(receipt, input.domain, input.buyer);
325
+ const body = deliveryProofRequestBodySchema.parse({
326
+ receipt: toWireDeliveryReceipt(receipt),
327
+ signature,
328
+ proof: {
329
+ identifier: proof.identifier,
330
+ claimData: proof.claimData,
331
+ signatures: proof.signatures,
332
+ },
333
+ });
334
+ return { body, receipt, signature, proof, envelope };
335
+ }
336
+ //# sourceMappingURL=t2-delivery-proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"t2-delivery-proof.js","sourceRoot":"","sources":["../../src/zktls/t2-delivery-proof.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absol-labs/agent",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Metrik agent layer: x402 verified-streaming payments, an MCP server, framework tools, and spend mandates so AI agents can hire and pay verified services safely.",
5
5
  "license": "MIT",
6
6
  "author": "Absol Labs",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@absol-labs/sdk": "^0.4.0",
70
- "@absol-labs/shared": "^0.7.1",
70
+ "@absol-labs/shared": "^0.11.0",
71
71
  "@coinbase/cdp-sdk": "^1.51.2",
72
72
  "@modelcontextprotocol/sdk": "^1.29.0",
73
73
  "@reclaimprotocol/js-sdk": "^5.6.0",
package/src/index.ts CHANGED
@@ -117,9 +117,13 @@ export {
117
117
  ReclaimConsumerProofService,
118
118
  ResponseProofUnavailableError,
119
119
  ResponseProofVerificationError,
120
+ buildDeliveryContextEnvelopeV2,
120
121
  createReclaimConsumerProofServiceFromEnv,
122
+ deliveryContextEnvelopeV2Schema,
121
123
  parseReclaimProofEnv,
122
124
  type ConsumerDeliveryProofService,
125
+ type DeliveryContextEnvelopeV2,
126
+ type DeliveryContextEnvelopeV2Input,
123
127
  type DeliveryProofBinding,
124
128
  type HttpsResponseMatch,
125
129
  type HttpsResponseRedaction,
@@ -132,6 +136,24 @@ export {
132
136
  type VerifyProofFn,
133
137
  } from "./zktls/reclaim.js";
134
138
 
139
+ export {
140
+ NoDeliveryAttestorConfiguredError,
141
+ PaidRouteNonceInjectionError,
142
+ ReclaimDeliveryProofAttestor,
143
+ buildT2DeliveryProofSubmission,
144
+ createReclaimT2AttestorFromEnv,
145
+ deliveryProofRequestBodySchema,
146
+ toWireDeliveryReceipt,
147
+ type BuildT2DeliveryProofInput,
148
+ type BuildT2DeliveryProofResult,
149
+ type DeliveryProofAttestor,
150
+ type DeliveryProofRequestBody,
151
+ type PaidRouteRequestSpec,
152
+ type ProveDeliveryResponseInput,
153
+ type T2NonceInjection,
154
+ type WireDeliveryReceipt,
155
+ } from "./zktls/t2-delivery-proof.js";
156
+
135
157
  // Framework adapters are NOT re-exported from this top-level barrel on purpose.
136
158
  // Some of them (notably the Coinbase AgentKit adapter) pull heavy optional
137
159
  // dependency graphs, so importing "@absol-labs/agent" must stay light and load
@@ -94,10 +94,19 @@ export interface ReclaimClientLike {
94
94
  body?: string;
95
95
  headers?: Record<string, string>;
96
96
  geoLocation?: string;
97
- context?: {
98
- contextAddress: string;
99
- contextMessage: string;
100
- };
97
+ /**
98
+ * Whatever is passed here is canonicalized and stored VERBATIM as the
99
+ * proof's `claimData.context` (see `@reclaimprotocol/attestor-core`'s
100
+ * `_createClaimOnAttestor`, which does `context: canonicalStringify(context)`
101
+ * with no further transformation). The upstream zk-fetch SDK types this
102
+ * narrower (`{contextAddress, contextMessage}`, the L2 v1 convention used
103
+ * by `buildRequestContext` below) but nothing downstream requires that
104
+ * wrapper shape - it is opaque JSON. The T2 delivery-proof path
105
+ * (`t2-delivery-proof.ts`) passes the FLAT `DeliveryContextEnvelopeV2`
106
+ * object directly so the oracle's `deliveryContextEnvelopeSchema` can
107
+ * parse `claimData.context` without an extra unwrap step.
108
+ */
109
+ context?: Record<string, unknown>;
101
110
  useTee?: boolean;
102
111
  zkEngine?: "snarkjs" | "stwo";
103
112
  },
@@ -329,7 +338,7 @@ export function createReclaimConsumerProofServiceFromEnv(
329
338
  return new ReclaimConsumerProofService(parsed);
330
339
  }
331
340
 
332
- function defaultReclaimClientFactory(
341
+ export function defaultReclaimClientFactory(
333
342
  applicationId: string,
334
343
  applicationSecret: string,
335
344
  logs: boolean,
@@ -355,8 +364,79 @@ function buildRequestContext(
355
364
  };
356
365
  }
357
366
 
358
- function toRequestSpec(
359
- input: z.infer<typeof reclaimProofInputSchema>,
367
+ /**
368
+ * The T2 (issue #24 / #85) `metrik-l2-http-response/v2` Metrik delivery-binding
369
+ * envelope, folded VERBATIM (not wrapped in `{contextAddress, contextMessage}`)
370
+ * into a Reclaim proof's `claimData.context`. This mirrors the ORACLE's OWN
371
+ * `deliveryContextEnvelopeSchema` (`streamproof-oracle/src/delivery/context-envelope.ts`)
372
+ * field-for-field and MUST stay byte-compatible with it - the oracle re-parses
373
+ * `claimData.context` through its own copy of this schema and fails closed on
374
+ * any mismatch.
375
+ *
376
+ * Unlike the v1 `buildRequestContext` binding above (used by the x402/MCP
377
+ * consumer-proof-as-signal path, agent#12), every identity field here is
378
+ * REQUIRED: a T2 accrual receipt cannot bind to a partially-specified stream.
379
+ */
380
+ export const deliveryContextEnvelopeV2Schema = z
381
+ .object({
382
+ kind: z.literal("metrik-l2-http-response/v2"),
383
+ streamId: bytes32Schema,
384
+ operator: addressSchema,
385
+ serviceRef: bytes32Schema,
386
+ mandateId: z.string().optional(),
387
+ nonce: bytes32Schema,
388
+ intervalIndex: z.number().int().nonnegative(),
389
+ issuedAt: z.number().int().nonnegative(),
390
+ })
391
+ .strict();
392
+
393
+ export type DeliveryContextEnvelopeV2 = z.infer<
394
+ typeof deliveryContextEnvelopeV2Schema
395
+ >;
396
+
397
+ export interface DeliveryContextEnvelopeV2Input {
398
+ readonly streamId: `0x${string}`;
399
+ readonly operator: `0x${string}`;
400
+ readonly serviceRef: `0x${string}`;
401
+ readonly mandateId?: string;
402
+ readonly nonce: `0x${string}`;
403
+ readonly intervalIndex: number;
404
+ /** Unix seconds. Defaults to `Math.floor(Date.now() / 1000)`. */
405
+ readonly issuedAt?: number;
406
+ }
407
+
408
+ /**
409
+ * Build the flat T2 envelope object to pass as `zkFetch`'s `context` option
410
+ * (see the widened `ReclaimClientLike.zkFetch` doc above - it is stored
411
+ * verbatim, no `{contextAddress, contextMessage}` wrapper). Validated against
412
+ * `deliveryContextEnvelopeV2Schema` before return so a malformed binding fails
413
+ * loudly here rather than silently producing an envelope the oracle rejects.
414
+ */
415
+ export function buildDeliveryContextEnvelopeV2(
416
+ input: DeliveryContextEnvelopeV2Input,
417
+ ): DeliveryContextEnvelopeV2 {
418
+ return deliveryContextEnvelopeV2Schema.parse({
419
+ kind: "metrik-l2-http-response/v2",
420
+ streamId: input.streamId,
421
+ operator: input.operator,
422
+ serviceRef: input.serviceRef,
423
+ ...(input.mandateId === undefined ? {} : { mandateId: input.mandateId }),
424
+ nonce: input.nonce,
425
+ intervalIndex: input.intervalIndex,
426
+ issuedAt: input.issuedAt ?? Math.floor(Date.now() / 1000),
427
+ });
428
+ }
429
+
430
+ /** Structural input `toRequestSpec` needs - satisfied by both the v1 and T2 request shapes. */
431
+ export interface RequestSpecInput {
432
+ readonly url: string;
433
+ readonly body?: string | undefined;
434
+ readonly responseMatches: readonly HttpsResponseMatch[];
435
+ readonly responseRedactions?: readonly HttpsResponseRedaction[] | undefined;
436
+ }
437
+
438
+ export function toRequestSpec(
439
+ input: RequestSpecInput,
360
440
  method: "GET" | "POST" | "PUT",
361
441
  ): RequestSpec {
362
442
  return {
@@ -384,7 +464,7 @@ function toRequestSpec(
384
464
  };
385
465
  }
386
466
 
387
- function asErrorMessage(error: unknown): string {
467
+ export function asErrorMessage(error: unknown): string {
388
468
  return error instanceof Error ? error.message : String(error);
389
469
  }
390
470