@absol-labs/agent 0.5.0 → 0.6.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.
@@ -1,7 +1,11 @@
1
1
  import {
2
2
  deriveServiceRef,
3
3
  recoverServiceBindingSigner,
4
+ recoverServiceDescriptorSigner,
4
5
  serviceBindingSchema,
6
+ serviceDescriptorSchema,
7
+ type SignedServiceBinding,
8
+ type SignedServiceDescriptor,
5
9
  } from "@absol-labs/shared";
6
10
  import { isHex } from "viem";
7
11
  import { z } from "zod";
@@ -35,9 +39,14 @@ export interface ServiceListing {
35
39
  readonly serviceRef: `0x${string}`;
36
40
  readonly operator: `0x${string}`;
37
41
  readonly publicUrl: string;
42
+ readonly access: "public" | "gated";
43
+ /** Signed invocation origin; gated listings fail closed if this is absent. */
44
+ readonly accessUrl: string;
45
+ /** The schema-validated and cryptographically verified signed record. */
46
+ readonly signed: SignedServiceBinding | SignedServiceDescriptor;
38
47
  /** Unsigned informational category from the listings row (may be absent). */
39
48
  readonly category: string | null;
40
- /** Optional signed operator-supplied target metadata (e.g. resolver hints). */
49
+ /** Optional unsigned operator-supplied resolver hints; never used for invocation. */
41
50
  readonly targetMetadata?: Record<string, string>;
42
51
  }
43
52
 
@@ -76,14 +85,16 @@ export const DEFAULT_METRIK_REGISTRY_URL =
76
85
  const DEFAULT_TIMEOUT_MS = 5_000;
77
86
 
78
87
  /**
79
- * `serviceRef` of the built-in verified demo listing. Kept as a named constant so
88
+ * `serviceRef` of the built-in verified live-data listing. Kept as a named constant so
80
89
  * callers can recognise (and dedupe against) the seeded row.
81
90
  */
82
- export const DEMO_SEED_SERVICE_REF =
83
- "0x84ef78c5ac9fc084422dc2d3e1ef19623bb05d0d81c097d4da49f9c47ec3ed12" as const;
91
+ export const LIVE_DATA_SEED_SERVICE_REF =
92
+ "0x79e57358e98a35273c1cd9d5f1bf9ae1e0af8705db432eb706459dc4f88b6fe0" as const;
93
+ /** @deprecated Use {@link LIVE_DATA_SEED_SERVICE_REF}. */
94
+ export const DEMO_SEED_SERVICE_REF = LIVE_DATA_SEED_SERVICE_REF;
84
95
 
85
96
  /**
86
- * A REAL, operator-signed demo listing row embedded so `discoverServices()` returns
97
+ * A real, operator-signed live-data listing row embedded so `discoverServices()` returns
87
98
  * something hireable with ZERO configuration (no Supabase anon key, no registry URL).
88
99
  *
89
100
  * This is PUBLIC data — a signed {@link https://github.com/Absol-Labs | Metrik}
@@ -94,23 +105,115 @@ export const DEMO_SEED_SERVICE_REF =
94
105
  * with any field here and the seed silently drops itself, identically to a bad
95
106
  * registry row. The `category` column is unsigned informational metadata only.
96
107
  */
97
- export const DEMO_SEED_LISTING = {
98
- service_ref: DEMO_SEED_SERVICE_REF,
99
- operator: "0x28ea4eF61ac4cca3ed6a64dBb5b2D4be1aDC9814",
100
- public_url: "https://metrik-demo-service.vercel.app",
101
- category: "demo",
108
+ export const LIVE_DATA_SEED_LISTING = {
109
+ service_ref: LIVE_DATA_SEED_SERVICE_REF,
110
+ operator: "0xB3e162711920dFD8933609019Dc35C73cC1a09F0",
111
+ public_url: "https://livedata.137.23.50.249.sslip.io",
112
+ category: "live-data",
102
113
  signed_binding: {
103
- serviceRef: DEMO_SEED_SERVICE_REF,
104
- binding: {
105
- version: 2,
106
- operator: "0x28ea4eF61ac4cca3ed6a64dBb5b2D4be1aDC9814",
107
- publicUrl: "https://metrik-demo-service.vercel.app",
108
- issuedAt: 1_787_500_000,
114
+ serviceRef: LIVE_DATA_SEED_SERVICE_REF,
115
+ descriptor: {
116
+ version: 3,
117
+ operator: "0xB3e162711920dFD8933609019Dc35C73cC1a09F0",
118
+ publicUrl: "https://livedata.137.23.50.249.sslip.io",
119
+ issuedAt: 1_787_772_057,
120
+ interface: {
121
+ baseUrl: "https://livedata.137.23.50.249.sslip.io",
122
+ healthPath: "/health",
123
+ endpoints: [
124
+ {
125
+ name: "health",
126
+ path: "/health",
127
+ method: "GET",
128
+ description: "Liveness probe.",
129
+ },
130
+ {
131
+ name: "readiness",
132
+ path: "/readiness",
133
+ method: "GET",
134
+ description:
135
+ "Non-value dependency readiness backed by the same real BTC cache policy.",
136
+ },
137
+ {
138
+ name: "price",
139
+ path: "/price",
140
+ method: "GET",
141
+ description:
142
+ "Real live crypto spot price (?symbol=BTC|ETH|SOL) from CoinGecko's public API.",
143
+ },
144
+ ],
145
+ authModel: "caller-auth",
146
+ example: {
147
+ endpoint: "price",
148
+ request: { symbol: "BTC" },
149
+ response: { symbol: "BTC", source: "coingecko-public-api" },
150
+ description:
151
+ "Real, nondeterministic live price — no re-execution guarantee.",
152
+ },
153
+ expectedStatuses: [200],
154
+ },
155
+ verification: {
156
+ canaries: [
157
+ {
158
+ path: "/readiness",
159
+ method: "GET",
160
+ matcher: {
161
+ type: "json-field-equals",
162
+ field: "source",
163
+ expected: "coingecko-public-api",
164
+ },
165
+ nonce: {
166
+ inject: { in: "query", name: "nonce" },
167
+ echo: { from: "json-path", path: "nonce" },
168
+ },
169
+ },
170
+ {
171
+ path: "/canary",
172
+ method: "GET",
173
+ matcher: {
174
+ type: "includes",
175
+ expected: "metrik-canary:live-data:",
176
+ },
177
+ nonce: {
178
+ inject: { in: "query", name: "nonce" },
179
+ echo: { from: "json-path", path: "nonce" },
180
+ },
181
+ },
182
+ ],
183
+ schema: {
184
+ path: "/readiness?nonce=metrik-schema",
185
+ required: [
186
+ { pointer: "ready", type: "boolean" },
187
+ { pointer: "source", type: "string", nonEmpty: true },
188
+ { pointer: "fetchedAt", type: "string", nonEmpty: true },
189
+ { pointer: "priceAgeMs", type: "number" },
190
+ { pointer: "freshness", type: "string", nonEmpty: true },
191
+ ],
192
+ },
193
+ sla: { maxLatencyMs: 5000, freshnessSeconds: 60 },
194
+ },
195
+ commercial: {
196
+ minRatePerSecond: "100",
197
+ maxRatePerSecond: "10000",
198
+ currency: "USDC",
199
+ slaDescription:
200
+ "Failed or unproven intervals do not advance verified cumulative entitlement; the stream remains active until buyer close or expiry.",
201
+ refundPolicy:
202
+ "The buyer reclaims unspent escrow using the latest checkpoint after close/expiry, or the unverified escape path after its grace window.",
203
+ },
204
+ access: "gated",
205
+ callerAuth: {
206
+ mechanism: "on-chain",
207
+ role: "buyer",
208
+ accessUrl: "https://gateway.137.23.50.249.sslip.io",
209
+ },
109
210
  },
110
211
  signature:
111
- "0x17917e96836a63238d0abe70c8c55081c4d694ad43311cdb3ea91dad349827fa6480657be2362e0b7f0524a0ee4ba0c48068255c3423a965392724874d244cd21b",
212
+ "0x2f4d2784eb40b1542517bc19f12534fb2e97da4ade5a3ff56193fcfb97fbef921309f77af3445e336cb07a4379011b080bba700033a098f672b0b2f096197f161b",
112
213
  },
113
214
  } as const;
215
+ /** @deprecated Use {@link LIVE_DATA_SEED_LISTING}. */
216
+ export const DEMO_SEED_LISTING = LIVE_DATA_SEED_LISTING;
114
217
 
115
218
  const signature65HexSchema = z
116
219
  .string()
@@ -135,12 +238,25 @@ const signedBindingRecordSchema = z.object({
135
238
  targetMetadata: z.record(z.string()).optional(),
136
239
  });
137
240
 
241
+ const signedDescriptorRecordSchema = z.object({
242
+ serviceRef: bytes32HexSchema,
243
+ descriptor: serviceDescriptorSchema,
244
+ signature: signature65HexSchema,
245
+ targetMetadata: z.record(z.string()).optional(),
246
+ });
247
+
138
248
  /** A Supabase `listings` row. Only `signed_binding` is trusted; `category` is
139
249
  * carried through as unsigned informational metadata. Unknown columns are ignored. */
140
250
  const listingRowSchema = z
141
251
  .object({
252
+ service_ref: bytes32HexSchema.optional(),
253
+ operator: z.string().optional(),
254
+ public_url: z.string().optional(),
142
255
  category: z.string().nullish(),
143
- signed_binding: signedBindingRecordSchema,
256
+ signed_binding: z.union([
257
+ signedBindingRecordSchema,
258
+ signedDescriptorRecordSchema,
259
+ ]),
144
260
  })
145
261
  .passthrough();
146
262
 
@@ -167,13 +283,13 @@ export async function discoverServices(
167
283
 
168
284
  // `registryUnavailable` = the registry could not be queried at all (no fetch /
169
285
  // network error / non-200). It never throws into the hire flow; instead it
170
- // degrades to the built-in verified demo seed (when enabled) so a cold user
286
+ // degrades to the built-in verified live-data seed (when enabled) so a cold user
171
287
  // still discovers a hireable service.
172
288
  let rows: unknown[] = [];
173
289
  let registryUnavailable = false;
174
290
  if (typeof fetchImpl !== "function") {
175
291
  logger.warn(
176
- "metrik discovery: no fetch implementation available; falling back to demo seed only",
292
+ "metrik discovery: no fetch implementation available; falling back to live-data seed only",
177
293
  );
178
294
  registryUnavailable = true;
179
295
  } else {
@@ -182,9 +298,9 @@ export async function discoverServices(
182
298
  } catch (error) {
183
299
  // Fail-safe / buyer-favouring: a down, unreachable, or 401 registry (the
184
300
  // Supabase default rejects credential-free reads) must never throw into the
185
- // agent's hire flow — it degrades to the demo seed.
301
+ // agent's hire flow — it degrades to the live-data seed.
186
302
  logger.warn(
187
- `metrik discovery: listings registry unavailable (${errorMessage(error)}); falling back to demo seed only`,
303
+ `metrik discovery: listings registry unavailable (${errorMessage(error)}); falling back to live-data seed only`,
188
304
  );
189
305
  registryUnavailable = true;
190
306
  }
@@ -210,21 +326,21 @@ export async function discoverServices(
210
326
  }
211
327
  }
212
328
 
213
- // Append the built-in verified demo listing when the registry did not already
329
+ // Append the built-in verified live-data listing when the registry did not already
214
330
  // surface it. Runs through the SAME fail-closed verifyRow path, so it is
215
331
  // verified exactly like a registry row and can never be a trust bypass.
216
332
  if (includeDemoSeed && !seedAlreadyPresent) {
217
- const seed = await verifyRow(DEMO_SEED_LISTING, "demo-seed", logger);
333
+ const seed = await verifyRow(DEMO_SEED_LISTING, "live-data-seed", logger);
218
334
  if (seed === null) {
219
335
  // Defensive: only reachable if the embedded constant were corrupted.
220
336
  logger.warn(
221
- "metrik discovery: built-in demo seed failed fail-closed verification; not seeding",
337
+ "metrik discovery: built-in live-data seed failed fail-closed verification; not seeding",
222
338
  );
223
339
  } else if (opts.category === undefined || seed.category === opts.category) {
224
340
  logger.warn(
225
341
  registryUnavailable
226
- ? "metrik discovery: registry unavailable — serving the built-in verified demo listing"
227
- : "metrik discovery: registry did not include the demo service — appending the built-in verified demo listing",
342
+ ? "metrik discovery: registry unavailable — serving the built-in verified live-data listing"
343
+ : "metrik discovery: registry did not include live data — appending the built-in verified listing",
228
344
  );
229
345
  listings.push(seed);
230
346
  }
@@ -284,26 +400,59 @@ async function verifyRow(
284
400
  }
285
401
  const { signed_binding: record, category } = parsed.data;
286
402
 
403
+ const signedIdentity =
404
+ "descriptor" in record
405
+ ? {
406
+ operator: record.descriptor.operator,
407
+ publicUrl: record.descriptor.publicUrl,
408
+ }
409
+ : {
410
+ operator: record.binding.operator,
411
+ publicUrl: record.binding.publicUrl,
412
+ };
413
+ if (
414
+ (parsed.data.service_ref !== undefined &&
415
+ !eqHex(parsed.data.service_ref, record.serviceRef)) ||
416
+ (parsed.data.operator !== undefined &&
417
+ !eqHex(parsed.data.operator, signedIdentity.operator)) ||
418
+ (parsed.data.public_url !== undefined &&
419
+ parsed.data.public_url !== signedIdentity.publicUrl)
420
+ ) {
421
+ logger.warn(
422
+ `metrik discovery: row ${index} dropped — flat identity columns disagree with signed record`,
423
+ );
424
+ return null;
425
+ }
426
+
287
427
  let signer: `0x${string}` | null;
288
428
  try {
289
- signer = (await recoverServiceBindingSigner(
290
- record.binding,
291
- record.signature as `0x${string}`,
292
- )) as `0x${string}` | null;
429
+ signer = (
430
+ "descriptor" in record
431
+ ? await recoverServiceDescriptorSigner(
432
+ record.descriptor,
433
+ record.signature as `0x${string}`,
434
+ )
435
+ : await recoverServiceBindingSigner(
436
+ record.binding,
437
+ record.signature as `0x${string}`,
438
+ )
439
+ ) as `0x${string}` | null;
293
440
  } catch (error) {
294
441
  logger.warn(
295
442
  `metrik discovery: row ${index} dropped — signature recovery threw (${errorMessage(error)})`,
296
443
  );
297
444
  return null;
298
445
  }
299
- if (signer === null || !eqHex(signer, record.binding.operator)) {
446
+ if (signer === null || !eqHex(signer, signedIdentity.operator)) {
300
447
  logger.warn(
301
448
  `metrik discovery: row ${index} dropped — signature does not recover to operator (recovered ${signer ?? "null"})`,
302
449
  );
303
450
  return null;
304
451
  }
305
452
 
306
- const derivedRef = deriveServiceRef(record.binding);
453
+ const derivedRef = deriveServiceRef(
454
+ "descriptor" in record ? record.descriptor : record.binding,
455
+ );
307
456
  if (!eqHex(derivedRef, record.serviceRef)) {
308
457
  logger.warn(
309
458
  `metrik discovery: row ${index} dropped — serviceRef ${record.serviceRef} does not match derived ${derivedRef}`,
@@ -311,10 +460,31 @@ async function verifyRow(
311
460
  return null;
312
461
  }
313
462
 
463
+ const access =
464
+ "descriptor" in record ? (record.descriptor.access ?? "public") : "public";
465
+ const accessUrl =
466
+ "descriptor" in record
467
+ ? access === "gated"
468
+ ? record.descriptor.callerAuth?.accessUrl
469
+ : (record.descriptor.interface?.baseUrl ?? record.descriptor.publicUrl)
470
+ : record.binding.publicUrl;
471
+ if (
472
+ accessUrl === undefined ||
473
+ (access === "gated" && !isHttpsUrl(accessUrl))
474
+ ) {
475
+ logger.warn(
476
+ `metrik discovery: row ${index} dropped — gated descriptor has no signed HTTPS callerAuth.accessUrl`,
477
+ );
478
+ return null;
479
+ }
480
+
314
481
  return {
315
482
  serviceRef: derivedRef,
316
- operator: record.binding.operator as `0x${string}`,
317
- publicUrl: record.binding.publicUrl,
483
+ operator: signedIdentity.operator as `0x${string}`,
484
+ publicUrl: signedIdentity.publicUrl,
485
+ access,
486
+ accessUrl,
487
+ signed: record as SignedServiceBinding | SignedServiceDescriptor,
318
488
  category: category ?? null,
319
489
  ...(record.targetMetadata === undefined
320
490
  ? {}
@@ -322,6 +492,14 @@ async function verifyRow(
322
492
  };
323
493
  }
324
494
 
495
+ function isHttpsUrl(value: string): boolean {
496
+ try {
497
+ return new URL(value).protocol === "https:";
498
+ } catch {
499
+ return false;
500
+ }
501
+ }
502
+
325
503
  function eqHex(a: string, b: string): boolean {
326
504
  return a.toLowerCase() === b.toLowerCase();
327
505
  }
@@ -290,7 +290,7 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
290
290
  }
291
291
  const lines = listings.map(
292
292
  (listing) =>
293
- `- ${listing.serviceRef} operator=${listing.operator} url=${listing.publicUrl}${listing.category ? ` category=${listing.category}` : ""}`,
293
+ `- ${listing.serviceRef} operator=${listing.operator} verificationUrl=${listing.publicUrl} access=${listing.access} accessUrl=${listing.accessUrl}${listing.category ? ` category=${listing.category}` : ""}`,
294
294
  );
295
295
  return (
296
296
  `Discovered ${listings.length} verified Metrik service${listings.length === 1 ? "" : "s"} ` +
@@ -72,7 +72,7 @@ export function renderCrewAiVerifiedStreamPythonExample(
72
72
  "",
73
73
  "buyer_agent = Agent(",
74
74
  ' role="Verified Compute Buyer",',
75
- ' goal="Hire a verified service and stop work when the stream pauses.",',
75
+ ' goal="Hire a verified service and close it if delivery is no longer verified.",',
76
76
  ' backstory="An operator that only pays for oracle-verified delivery.",',
77
77
  " mcps=[metrik_mcp],",
78
78
  ")",
@@ -145,6 +145,8 @@ export interface CallerAuthGatewayConfig {
145
145
  readonly rpcUrl?: string;
146
146
  /** The service this gateway fronts. Every request's stream AND capability must match it. */
147
147
  readonly serviceRef: Hex;
148
+ /** Additional service refs accepted during signed-descriptor migrations. */
149
+ readonly serviceRefs?: readonly Hex[];
148
150
  /** Chain id for EIP-712 domain separation. Default `84532` (Base Sepolia). */
149
151
  readonly chainId?: number;
150
152
  /** Capability header name. Default {@link CAPABILITY_HEADER_NAME}. */
@@ -176,18 +178,18 @@ export class CallerAuthGateway {
176
178
  private readonly nonceCache: NonceReplayCache;
177
179
  private readonly headerName: string;
178
180
  private readonly domain: InvocationCapabilityDomainInput;
179
- private readonly serviceRef: Hex;
181
+ private readonly serviceRefs: ReadonlySet<string>;
180
182
  private readonly now: () => number;
181
183
 
182
184
  constructor(config: CallerAuthGatewayConfig) {
183
185
  if (!isAddress(config.escrowAddress)) {
184
186
  throw new Error("escrowAddress must be an EVM address");
185
187
  }
186
- if (
187
- !isHex(config.serviceRef, { strict: true }) ||
188
- config.serviceRef.length !== 66
189
- ) {
190
- throw new Error("serviceRef must be a 32-byte hex value");
188
+ const serviceRefs = [config.serviceRef, ...(config.serviceRefs ?? [])];
189
+ for (const serviceRef of serviceRefs) {
190
+ if (!isHex(serviceRef, { strict: true }) || serviceRef.length !== 66) {
191
+ throw new Error("every serviceRef must be a 32-byte hex value");
192
+ }
191
193
  }
192
194
  if (config.streamReader === undefined && config.rpcUrl === undefined) {
193
195
  throw new Error(
@@ -195,7 +197,7 @@ export class CallerAuthGateway {
195
197
  );
196
198
  }
197
199
 
198
- this.serviceRef = config.serviceRef;
200
+ this.serviceRefs = new Set(serviceRefs.map((value) => value.toLowerCase()));
199
201
  this.headerName = (
200
202
  config.headerName ?? CAPABILITY_HEADER_NAME
201
203
  ).toLowerCase();
@@ -260,7 +262,7 @@ export class CallerAuthGateway {
260
262
  return deny("path-mismatch", 403, capability);
261
263
  }
262
264
 
263
- if (capability.serviceRef.toLowerCase() !== this.serviceRef.toLowerCase()) {
265
+ if (!this.serviceRefs.has(capability.serviceRef.toLowerCase())) {
264
266
  return deny("service-ref-mismatch", 403, capability);
265
267
  }
266
268
 
@@ -280,7 +282,9 @@ export class CallerAuthGateway {
280
282
  if (stream.buyer.toLowerCase() !== capability.buyer.toLowerCase()) {
281
283
  return deny("buyer-mismatch", 403, capability);
282
284
  }
283
- if (stream.serviceRef.toLowerCase() !== this.serviceRef.toLowerCase()) {
285
+ if (
286
+ stream.serviceRef.toLowerCase() !== capability.serviceRef.toLowerCase()
287
+ ) {
284
288
  return deny("service-ref-mismatch", 403, capability);
285
289
  }
286
290
  if (stream.status !== "active") {
@@ -232,6 +232,7 @@ export interface CallerAuthGatewayEnvConfig {
232
232
  readonly escrowAddress: Address;
233
233
  readonly rpcUrl: string;
234
234
  readonly serviceRef: Hex;
235
+ readonly serviceRefs: readonly Hex[];
235
236
  readonly chainId: number;
236
237
  readonly upstreamUrl: string;
237
238
  readonly headerName?: string;
@@ -242,7 +243,8 @@ export interface CallerAuthGatewayEnvConfig {
242
243
  * - `METRIK_GATEWAY_ESCROW_ADDRESS` (required) - the `StreamEscrowV2` this
243
244
  * gateway checks streams against.
244
245
  * - `METRIK_GATEWAY_RPC_URL` (required) - RPC used for the read-only stream check.
245
- * - `METRIK_GATEWAY_SERVICE_REF` (required) - bytes32 `serviceRef` this gateway fronts.
246
+ * - `METRIK_GATEWAY_SERVICE_REF` (required) - primary bytes32 service ref.
247
+ * - `METRIK_GATEWAY_SERVICE_REFS` (optional) - comma-separated migration aliases.
246
248
  * - `METRIK_GATEWAY_UPSTREAM_URL` (required) - the real service to proxy to on pass.
247
249
  * - `METRIK_GATEWAY_CHAIN_ID` (optional) - default `84532` (Base Sepolia).
248
250
  * - `METRIK_GATEWAY_HEADER_NAME` (optional) - default `x-metrik-capability`.
@@ -263,6 +265,17 @@ export function parseCallerAuthGatewayEnvConfig(
263
265
  if (!isHex(serviceRef, { strict: true }) || serviceRef.length !== 66) {
264
266
  throw new Error("METRIK_GATEWAY_SERVICE_REF must be a 32-byte hex value");
265
267
  }
268
+ const serviceRefs = (env.METRIK_GATEWAY_SERVICE_REFS ?? "")
269
+ .split(",")
270
+ .map((value) => value.trim())
271
+ .filter((value) => value.length > 0);
272
+ for (const candidate of serviceRefs) {
273
+ if (!isHex(candidate, { strict: true }) || candidate.length !== 66) {
274
+ throw new Error(
275
+ "METRIK_GATEWAY_SERVICE_REFS must contain only 32-byte hex values",
276
+ );
277
+ }
278
+ }
266
279
  const upstreamUrl = requireEnv(env, "METRIK_GATEWAY_UPSTREAM_URL");
267
280
  // eslint-disable-next-line no-new -- validates the URL is well-formed.
268
281
  new URL(upstreamUrl);
@@ -288,6 +301,9 @@ export function parseCallerAuthGatewayEnvConfig(
288
301
  escrowAddress,
289
302
  rpcUrl,
290
303
  serviceRef,
304
+ serviceRefs: [
305
+ ...new Set(serviceRefs.map((value) => value.toLowerCase())),
306
+ ] as Hex[],
291
307
  chainId,
292
308
  upstreamUrl,
293
309
  ...(headerName === undefined ? {} : { headerName }),
@@ -311,6 +327,7 @@ export async function startCallerAuthGatewayServerFromEnv(
311
327
  escrowAddress: config.escrowAddress,
312
328
  rpcUrl: config.rpcUrl,
313
329
  serviceRef: config.serviceRef,
330
+ serviceRefs: config.serviceRefs,
314
331
  chainId: config.chainId,
315
332
  ...(config.headerName === undefined
316
333
  ? {}
package/src/index.ts CHANGED
@@ -49,6 +49,8 @@ export {
49
49
  DEFAULT_METRIK_REGISTRY_URL,
50
50
  DEMO_SEED_LISTING,
51
51
  DEMO_SEED_SERVICE_REF,
52
+ LIVE_DATA_SEED_LISTING,
53
+ LIVE_DATA_SEED_SERVICE_REF,
52
54
  discoverServices,
53
55
  type DiscoverServicesOptions,
54
56
  type ServiceListing,
@@ -219,6 +221,9 @@ export {
219
221
 
220
222
  export {
221
223
  InvokeBuyerMismatchError,
224
+ InvokeAccessUrlError,
225
+ InvokePathError,
226
+ InvokeServiceRefMismatchError,
222
227
  InvokeStreamExpiredError,
223
228
  InvokeStreamNotActiveError,
224
229
  capabilityFor,
package/src/mcp/server.ts CHANGED
@@ -923,6 +923,8 @@ function serializeServiceListing(
923
923
  serviceRef: listing.serviceRef,
924
924
  operator: listing.operator,
925
925
  publicUrl: listing.publicUrl,
926
+ access: listing.access,
927
+ accessUrl: listing.accessUrl,
926
928
  category: listing.category,
927
929
  ...(listing.targetMetadata === undefined
928
930
  ? {}