@nylorun/agents 0.9.0-beta → 0.11.0-beta

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 (52) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +296 -77
  3. package/dist/a2a/handler.d.ts +59 -0
  4. package/dist/a2a/handler.js +178 -0
  5. package/dist/a2a/index.d.ts +8 -0
  6. package/dist/a2a/index.js +7 -0
  7. package/dist/access.d.ts +101 -0
  8. package/dist/access.js +111 -0
  9. package/dist/action-handler.d.ts +63 -0
  10. package/dist/action-handler.js +279 -0
  11. package/dist/ag-ui/handler.d.ts +12 -4
  12. package/dist/ag-ui/handler.js +87 -281
  13. package/dist/ag-ui/index.d.ts +2 -1
  14. package/dist/ag-ui/index.js +2 -1
  15. package/dist/browser.d.ts +97 -0
  16. package/dist/browser.js +277 -0
  17. package/dist/client.d.ts +3 -230
  18. package/dist/client.js +3 -361
  19. package/dist/connection.d.ts +4 -4
  20. package/dist/connection.js +16 -25
  21. package/dist/delivery-token.d.ts +62 -0
  22. package/dist/delivery-token.js +159 -0
  23. package/dist/execute-action.d.ts +6 -0
  24. package/dist/execute-action.js +24 -0
  25. package/dist/http.d.ts +34 -3
  26. package/dist/http.js +68 -16
  27. package/dist/index.d.ts +10 -8
  28. package/dist/index.js +4 -4
  29. package/dist/sandbox/client.d.ts +4 -5
  30. package/dist/sandbox/client.js +4 -8
  31. package/dist/sandbox/index.d.ts +0 -2
  32. package/dist/sandbox/index.js +0 -1
  33. package/dist/served-definitions.d.ts +20 -0
  34. package/dist/served-definitions.js +100 -0
  35. package/dist/session-client.d.ts +282 -0
  36. package/dist/session-client.js +375 -0
  37. package/dist/sse.js +12 -0
  38. package/dist/studio-embed.d.ts +9 -0
  39. package/dist/studio-embed.js +8 -0
  40. package/dist/token-endpoint.d.ts +23 -0
  41. package/dist/token-endpoint.js +30 -0
  42. package/package.json +15 -8
  43. package/dist/ag-ui/history.d.ts +0 -7
  44. package/dist/ag-ui/history.js +0 -60
  45. package/dist/ag-ui/sse.d.ts +0 -9
  46. package/dist/ag-ui/sse.js +0 -7
  47. package/dist/ag-ui/translate.d.ts +0 -38
  48. package/dist/ag-ui/translate.js +0 -222
  49. package/dist/derived-credentials.d.ts +0 -5
  50. package/dist/derived-credentials.js +0 -17
  51. package/dist/executor.d.ts +0 -20
  52. package/dist/executor.js +0 -417
package/dist/client.js CHANGED
@@ -1,371 +1,13 @@
1
- import { SANDBOX_TOOL_NAMES, delegateOf, isBuiltWorkflow, isSandboxToolName, } from "@nylorun/core/define";
2
- import { SCOPES_HEADER, SUBJECT_HEADER } from "@nylorun/core/compatibility";
3
- import { LiveEventSchema, SessionItemsResponseSchema, parseSubjectHeaders, } from "@nylorun/core/contracts";
1
+ export * from "./session-client.js";
2
+ import { AgentsClient } from "./session-client.js";
4
3
  import { resolveConnection } from "./connection.js";
5
- import { Transport, id, segment } from "./http.js";
6
- import { observeSSE } from "./sse.js";
7
- function sessionSandboxOf(transport, sessionId) {
8
- const call = (tool, args) => transport.json(`/v1/sessions/${segment(sessionId)}/sandbox/${segment(tool)}`, "POST", args);
9
- const sandbox = {};
10
- for (const tool of SANDBOX_TOOL_NAMES) {
11
- if (!isSandboxToolName(tool))
12
- continue;
13
- sandbox[tool] = (args) => call(tool, args);
14
- }
15
- return sandbox;
16
- }
17
- function linkedSessionId(event) {
18
- if (event.type !== "node.agent")
19
- return undefined;
20
- const payload = event.payload;
21
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
22
- return undefined;
23
- const sessionId = payload.sessionId;
24
- return typeof sessionId === "string" && sessionId.length > 0
25
- ? sessionId
26
- : undefined;
27
- }
28
- const middlewareClosure = "Hosted definitions support declarative capabilities and before/after hooks, not middleware closures";
29
- /** The agent's declarations, then those of each agent it uses as a tool, keyed `<child>/<capability>`. */
30
- function declarationsOf(agent) {
31
- if (isBuiltWorkflow(agent) || agent.manifest.kind === "workflow")
32
- return [];
33
- const built = agent.build?.() ?? agent;
34
- if (isBuiltWorkflow(built))
35
- return [];
36
- const binding = built.getBinding?.();
37
- if (!binding || !("declarations" in binding))
38
- return [];
39
- const found = (binding.declarations ?? []).map((item) => ({ key: item.id, item }));
40
- for (const tool of binding.tools ?? []) {
41
- const delegate = delegateOf(tool);
42
- const child = delegate?.agent;
43
- for (const item of child?.getBinding().declarations ?? [])
44
- found.push({ key: `${child.id}/${item.id}`, item });
45
- // A flow agent used as a tool: its agents' declarations, keyed `<flow>/<agent>/<capability>`.
46
- const flow = delegate?.workflow;
47
- for (const leaf of Object.values(flow?.getBinding().agents ?? {}))
48
- for (const item of leaf.declarations ?? [])
49
- found.push({ key: `${flow.id}/${leaf.manifest.id}/${item.id}`, item });
50
- }
51
- return found;
52
- }
53
- function pluginRootsOf(agent) {
54
- const roots = {};
55
- for (const { key, item } of declarationsOf(agent))
56
- if (item.pluginRoot)
57
- roots[key] = item.pluginRoot;
58
- return Object.keys(roots).length === 0 ? undefined : roots;
59
- }
60
- /** Closures stay on the live binding. The published manifest cannot name them. */
61
- export function assertNoMiddlewareClosures(agent) {
62
- if (declarationsOf(agent).some(({ item }) => item.hasMiddleware))
63
- throw new Error(middlewareClosure);
64
- }
65
- export class AgentsClient {
66
- transport;
67
- /** The subject this client acts for (`as`), or `undefined` for the principal itself. */
68
- subject;
69
- constructor(destination = {}) {
70
- this.transport = new Transport(destination);
71
- this.subject = undefined;
72
- }
73
- /**
74
- * A client that acts for `subject`: every call sends `Nylorun-Subject` and
75
- * `Nylorun-Scopes`, and the Runtime limits it to the scopes and to the subject's own sessions
76
- * and vaults (Host feature `subject-headers`). For app servers, which authenticate the person
77
- * themselves and hold the key; nothing is minted, cached or refreshed.
78
- */
79
- as(subject, options = {}) {
80
- if (this.subject !== undefined)
81
- throw new Error(`This client already acts for ${this.subject}`);
82
- const scopes = [...(options.scopes ?? ["sessions:own"])].join(" ");
83
- const parsed = parseSubjectHeaders(subject, scopes);
84
- if (!parsed.ok)
85
- throw new TypeError(parsed.message);
86
- const client = Object.create(AgentsClient.prototype);
87
- return Object.assign(client, {
88
- transport: this.transport.withHeaders({
89
- [SUBJECT_HEADER]: subject,
90
- [SCOPES_HEADER]: [...parsed.scopes].join(" "),
91
- }),
92
- subject,
93
- });
94
- }
95
- /** The Runtime's protocol features, including optional ones such as `transcript-events`. */
96
- hostFeatures(options = {}) {
97
- return this.transport.hostFeatures(options.signal);
98
- }
99
- listAgents(options = {}) {
100
- return this.transport.json("/v1/agents", "GET", undefined, options.signal);
101
- }
102
- listSessions(options = {}) {
103
- const query = options.agentId
104
- ? `?agentId=${encodeURIComponent(options.agentId)}`
105
- : "";
106
- return this.transport.json(`/v1/sessions${query}`, "GET", undefined, options.signal);
107
- }
108
- async saveAgent(agent, options) {
109
- if (isBuiltWorkflow(agent) && agent.manifest.workflowSchemaVersion === 2) {
110
- // A v2 workflow embeds its agents: one document, carrying their plugin roots.
111
- const pluginRoots = {};
112
- for (const binding of Object.values(agent.getBinding().agents)) {
113
- const leaf = { id: binding.manifest.id, manifest: binding.manifest, getBinding: () => binding };
114
- assertNoMiddlewareClosures(leaf);
115
- for (const [key, root] of Object.entries(pluginRootsOf(leaf) ?? {}))
116
- pluginRoots[`${leaf.id}/${key}`] = root;
117
- }
118
- return this.transport.json(`/v1/agents/${segment(agent.id)}`, "PUT", {
119
- requestId: options.requestId ?? id(),
120
- manifest: agent.manifest,
121
- implementationVersion: options.implementationVersion,
122
- ...(Object.keys(pluginRoots).length === 0 ? {} : { pluginRoots }),
123
- });
124
- }
125
- if (isBuiltWorkflow(agent)) {
126
- for (const binding of Object.values(agent.getBinding().agents)) {
127
- await this.saveAgent({
128
- id: binding.manifest.id,
129
- manifest: binding.manifest,
130
- getBinding: () => binding,
131
- }, options);
132
- }
133
- return this.transport.json(`/v1/agents/${segment(agent.id)}`, "PUT", {
134
- requestId: options.requestId ?? id(),
135
- manifest: agent.manifest,
136
- implementationVersion: options.implementationVersion,
137
- });
138
- }
139
- assertNoMiddlewareClosures(agent);
140
- const pluginRoots = pluginRootsOf(agent);
141
- return this.transport.json(`/v1/agents/${segment(agent.id)}`, "PUT", {
142
- requestId: options.requestId ?? id(),
143
- manifest: agent.manifest,
144
- implementationVersion: options.implementationVersion,
145
- ...(pluginRoots === undefined ? {} : { pluginRoots }),
146
- });
147
- }
148
- async createSession(options) {
149
- const sessionId = options.id ?? id();
150
- await this.transport.json(`/v1/sessions/${segment(sessionId)}`, "PUT", {
151
- requestId: options.requestId ?? id(),
152
- agentId: options.agentId,
153
- ownerUserId: options.ownerUserId,
154
- ...(options.info ? { info: options.info } : {}),
155
- ...(options.vaultIds ? { vaultIds: options.vaultIds } : {}),
156
- ...(options.credentialSelections
157
- ? { credentialSelections: options.credentialSelections }
158
- : {}),
159
- ...(options.sandbox ? { sandbox: options.sandbox } : {}),
160
- });
161
- return this.session(sessionId);
162
- }
163
- createVault(options) {
164
- return this.transport.json("/v1/vaults", "POST", {
165
- requestId: options.requestId ?? id(),
166
- idempotencyKey: options.idempotencyKey,
167
- name: options.name,
168
- ownerUserId: options.ownerUserId,
169
- ...(options.metadata ? { metadata: options.metadata } : {}),
170
- });
171
- }
172
- listVaults(ownerUserId, signal) {
173
- return this.transport.json(`/v1/vaults?ownerUserId=${encodeURIComponent(ownerUserId)}`, "GET", undefined, signal);
174
- }
175
- getVault(vaultId, signal) {
176
- return this.transport.json(`/v1/vaults/${segment(vaultId)}`, "GET", undefined, signal);
177
- }
178
- deleteVault(vaultId) {
179
- return this.transport.json(`/v1/vaults/${segment(vaultId)}`, "DELETE");
180
- }
181
- createCredential(vaultId, options) {
182
- return this.transport.json(`/v1/vaults/${segment(vaultId)}/credentials`, "POST", {
183
- requestId: options.requestId ?? id(),
184
- idempotencyKey: options.idempotencyKey,
185
- name: options.name,
186
- auth: options.auth,
187
- });
188
- }
189
- listCredentials(vaultId, signal) {
190
- return this.transport.json(`/v1/vaults/${segment(vaultId)}/credentials`, "GET", undefined, signal);
191
- }
192
- getCredential(vaultId, credentialId, signal) {
193
- return this.transport.json(`/v1/vaults/${segment(vaultId)}/credentials/${segment(credentialId)}`, "GET", undefined, signal);
194
- }
195
- rotateCredential(vaultId, credentialId, options) {
196
- return this.transport.json(`/v1/vaults/${segment(vaultId)}/credentials/${segment(credentialId)}`, "POST", {
197
- requestId: options.requestId ?? id(),
198
- idempotencyKey: options.idempotencyKey,
199
- auth: options.auth,
200
- });
201
- }
202
- deleteCredential(vaultId, credentialId) {
203
- return this.transport.json(`/v1/vaults/${segment(vaultId)}/credentials/${segment(credentialId)}`, "DELETE");
204
- }
205
- session(sessionId) {
206
- return new SessionClient(this.transport, sessionId);
207
- }
208
- }
209
- export class SessionClient {
210
- transport;
211
- id;
212
- sandbox;
213
- constructor(transport, id) {
214
- this.transport = transport;
215
- this.id = id;
216
- this.sandbox = sessionSandboxOf(transport, id);
217
- }
218
- get path() {
219
- return `/v1/sessions/${segment(this.id)}`;
220
- }
221
- inspect(signal) {
222
- return this.transport.json(this.path, "GET", undefined, signal);
223
- }
224
- /** Pending waits / interactions from the session inspect view. */
225
- async pending(signal) {
226
- const view = await this.inspect(signal);
227
- return view.waits ?? [];
228
- }
229
- command(command, signal) {
230
- return this.transport.json(`${this.path}/commands`, "POST", command, signal);
231
- }
232
- /**
233
- * Start a turn. Strings become `message.content`; any other JSON value becomes
234
- * `message.data` (workflows.md §5).
235
- */
236
- input(value, options) {
237
- const base = {
238
- type: "message",
239
- requestId: options.requestId ?? id(),
240
- idempotencyKey: options.idempotencyKey,
241
- };
242
- return this.command(typeof value === "string"
243
- ? { ...base, content: value }
244
- : { ...base, data: value }, options.signal);
245
- }
246
- approve(interactionId, approved, options) {
247
- return this.command({
248
- type: "approve",
249
- interactionId,
250
- approved,
251
- requestId: options.requestId ?? id(),
252
- idempotencyKey: options.idempotencyKey,
253
- }, options.signal);
254
- }
255
- respond(interactionId, value, options) {
256
- return this.command({
257
- type: "respond",
258
- interactionId,
259
- value,
260
- requestId: options.requestId ?? id(),
261
- idempotencyKey: options.idempotencyKey,
262
- }, options.signal);
263
- }
264
- cancel(options) {
265
- return this.command({
266
- type: "cancel",
267
- requestId: options.requestId ?? id(),
268
- idempotencyKey: options.idempotencyKey,
269
- ...(options.reason ? { reason: options.reason } : {}),
270
- }, options.signal);
271
- }
272
- /** `agent` narrows to one agent used as a tool: its delegationId or its path. */
273
- async history(options = {}) {
274
- const query = new URLSearchParams();
275
- if (options.cursor)
276
- query.set("cursor", options.cursor);
277
- if (options.agent)
278
- query.set("agent", options.agent);
279
- const search = query.toString();
280
- return SessionItemsResponseSchema.parse(await this.transport.json(`${this.path}/items${search ? `?${search}` : ""}`, "GET", undefined, options.signal));
281
- }
282
- async *observe(options = {}) {
283
- if (!options.follow) {
284
- yield* this.observeOne(this.id, options);
285
- return;
286
- }
287
- yield* this.observeFollowing(options);
288
- }
289
- async *observeOne(sessionId, options) {
290
- for await (const frame of observeSSE(this.transport, `/v1/sessions/${segment(sessionId)}/events`, options)) {
291
- if (frame.event === "heartbeat" || frame.event === "ready")
292
- continue;
293
- yield LiveEventSchema.parse(JSON.parse(frame.data));
294
- }
295
- }
296
- /**
297
- * Own session stream plus linked agent sessions announced in `node.agent`
298
- * events, merged by `createdAt` then arrival order (workflows.md §11).
299
- */
300
- async *observeFollowing(options) {
301
- const signal = options.signal;
302
- const linked = new Set();
303
- const queue = [];
304
- let wait;
305
- let open = 1;
306
- let failed;
307
- const wake = () => {
308
- wait?.();
309
- wait = undefined;
310
- };
311
- const push = (event) => {
312
- queue.push(event);
313
- queue.sort((a, b) => {
314
- const byTime = a.createdAt.localeCompare(b.createdAt);
315
- return byTime !== 0 ? byTime : a.cursor.localeCompare(b.cursor);
316
- });
317
- wake();
318
- };
319
- const pump = async (sessionId, cursor) => {
320
- try {
321
- for await (const event of this.observeOne(sessionId, { cursor, signal })) {
322
- push(event);
323
- const link = linkedSessionId(event);
324
- if (link && link !== this.id && !linked.has(link)) {
325
- linked.add(link);
326
- open += 1;
327
- void pump(link).finally(() => {
328
- open -= 1;
329
- wake();
330
- });
331
- }
332
- }
333
- }
334
- catch (error) {
335
- if (!signal?.aborted)
336
- failed = error;
337
- }
338
- };
339
- void pump(this.id, options.cursor).finally(() => {
340
- open -= 1;
341
- wake();
342
- });
343
- while (!signal?.aborted) {
344
- if (failed)
345
- throw failed;
346
- if (queue.length) {
347
- yield queue.shift();
348
- continue;
349
- }
350
- if (open <= 0)
351
- return;
352
- await new Promise((resolve) => {
353
- wait = resolve;
354
- });
355
- }
356
- }
357
- }
358
4
  export function createClient(destination = {}) {
359
- if (destination.url !== undefined ||
360
- destination.key !== undefined ||
361
- destination.tenant !== undefined) {
5
+ if (destination.url !== undefined || destination.key !== undefined) {
362
6
  return new AgentsClient(destination);
363
7
  }
364
8
  return resolveConnection().then((connection) => new AgentsClient({
365
9
  url: connection.url,
366
10
  key: connection.key,
367
- tenant: connection.tenant,
368
11
  ...(destination.fetch ? { fetch: destination.fetch } : {}),
369
12
  }));
370
13
  }
371
- export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
@@ -1,19 +1,19 @@
1
1
  import type { ErrorCode } from "@nylorun/core/compatibility";
2
2
  export interface ResolvedConnection {
3
3
  url: string;
4
- tenant: string;
5
4
  key: string;
6
- role: "application" | "executor";
7
5
  source: "options" | "environment" | "project-link";
8
6
  }
9
7
  export declare class ConnectionError extends Error {
10
8
  readonly code: ErrorCode;
11
9
  constructor(message: string);
12
10
  }
13
- /** Resolve Tenant API connection: options → environment → project link (D§7.1). */
11
+ /**
12
+ * Resolve Tenant API connection: options → environment → project link (D§7.1). A connection is a
13
+ * Runtime URL and a key: the Runtime serves one Tenant, so nothing names it.
14
+ */
14
15
  export declare function resolveConnection(options?: {
15
16
  url?: string;
16
- tenant?: string;
17
17
  key?: string;
18
18
  cwd?: string;
19
19
  }): Promise<ResolvedConnection>;
@@ -11,8 +11,8 @@ export class ConnectionError extends Error {
11
11
  }
12
12
  function missing(tried) {
13
13
  throw new ConnectionError(`connection_missing: no Runtime connection found (tried ${tried.join(", ")}). ` +
14
- `Start the local stack with "npx nylorun up" and link this project with "npx @nylorun/cli tenant create", ` +
15
- `or set NYLORUN_RUNTIME_URL, NYLORUN_TENANT and NYLORUN_SERVER_KEY.`);
14
+ `Run "npx nylorun start" in this project to start its stack and link it, ` +
15
+ `or set NYLORUN_RUNTIME_URL and NYLORUN_SERVER_KEY.`);
16
16
  }
17
17
  function stripTrailingSlash(url) {
18
18
  return url.replace(/\/$/, "");
@@ -37,55 +37,46 @@ async function readProjectLink(startDir) {
37
37
  throw error;
38
38
  }
39
39
  const link = ProjectLinkFileSchema.parse(JSON.parse(linkRaw));
40
+ // Formats 0 and 1 named a Tenant on a multi-Tenant Host of an older Runtime.
41
+ if (link.format < 2)
42
+ throw new ConnectionError(`connection_missing: the Project link at ${linkPath} is for a stack of an older ` +
43
+ `Runtime, before one Tenant per installation. Run "npx nylorun start" in this project ` +
44
+ `to start its own stack and link it again.`);
40
45
  const credentials = ProjectCredentialsFileSchema.parse(JSON.parse(await readFile(join(nylorun, "credentials.json"), "utf8")));
41
46
  return {
42
47
  url: stripTrailingSlash(link.hostUrl),
43
- tenant: link.tenantId,
44
48
  key: credentials.applicationKey,
45
- role: "application",
46
49
  source: "project-link",
47
50
  };
48
51
  }
49
52
  }
50
- /** Resolve Tenant API connection: options → environment → project link (D§7.1). */
53
+ /**
54
+ * Resolve Tenant API connection: options → environment → project link (D§7.1). A connection is a
55
+ * Runtime URL and a key: the Runtime serves one Tenant, so nothing names it.
56
+ */
51
57
  export async function resolveConnection(options) {
52
58
  const tried = [];
53
59
  const optionUrl = options?.url;
54
- const optionTenant = options?.tenant;
55
60
  const optionKey = options?.key;
56
- const anyOption = optionUrl !== undefined ||
57
- optionTenant !== undefined ||
58
- optionKey !== undefined;
59
61
  tried.push("options");
60
- if (anyOption) {
61
- if (optionUrl && optionTenant && optionKey) {
62
+ if (optionUrl !== undefined || optionKey !== undefined) {
63
+ if (optionUrl && optionKey) {
62
64
  return {
63
65
  url: stripTrailingSlash(optionUrl),
64
- tenant: optionTenant,
65
66
  key: optionKey,
66
- role: "application",
67
67
  source: "options",
68
68
  };
69
69
  }
70
70
  missing(tried.concat(["environment", "project-link"]));
71
71
  }
72
72
  const envUrl = env("NYLORUN_RUNTIME_URL");
73
- const envTenant = env("NYLORUN_TENANT");
74
73
  const envServerKey = env("NYLORUN_SERVER_KEY");
75
- const envExecutorKey = env("NYLORUN_EXECUTOR_KEY");
76
- const anyEnv = envUrl !== undefined ||
77
- envTenant !== undefined ||
78
- envServerKey !== undefined ||
79
- envExecutorKey !== undefined;
80
74
  tried.push("environment");
81
- if (anyEnv) {
82
- const key = envExecutorKey ?? envServerKey;
83
- if (envUrl && envTenant && key) {
75
+ if (envUrl !== undefined || envServerKey !== undefined) {
76
+ if (envUrl && envServerKey) {
84
77
  return {
85
78
  url: stripTrailingSlash(envUrl),
86
- tenant: envTenant,
87
- key,
88
- role: envExecutorKey !== undefined ? "executor" : "application",
79
+ key: envServerKey,
89
80
  source: "environment",
90
81
  };
91
82
  }
@@ -0,0 +1,62 @@
1
+ /** The claims of a verified delivery token. */
2
+ export interface DeliveryClaims {
3
+ readonly iss: string;
4
+ readonly aud: string;
5
+ /** The Action id, or `ping`. */
6
+ readonly sub: string;
7
+ /** The agent or workflow the Action belongs to. */
8
+ readonly agt: string;
9
+ /** The delivery's generation; `0` for a ping. */
10
+ readonly gen: number;
11
+ /** base64url SHA-256 of the request body. */
12
+ readonly bdy: string;
13
+ readonly iat: number;
14
+ readonly exp: number;
15
+ readonly jti: string;
16
+ }
17
+ /** Why a delivery was refused; `code` is sent back to the Runtime. */
18
+ export declare class DeliveryVerificationError extends Error {
19
+ readonly code: "signature_missing" | "signature_invalid" | "key_unknown" | "token_expired";
20
+ readonly name = "DeliveryVerificationError";
21
+ constructor(code: "signature_missing" | "signature_invalid" | "key_unknown" | "token_expired", message: string);
22
+ }
23
+ /** Finds the public key for a `kid`, or `undefined` when the Tenant has none. */
24
+ export type KeyLookup = (kid: string) => Promise<CryptoKey | undefined>;
25
+ /**
26
+ * Verifies `token` against `body`. The keys are the installation's, so a valid signature already
27
+ * binds the issuer to its one Tenant: any `urn:nylorun:tenant:<id>` issuer is accepted (and
28
+ * returned in `iss`) unless `tenantId` names the one expected.
29
+ */
30
+ export declare function verifyDeliveryToken(token: string | null, options: {
31
+ keys: KeyLookup;
32
+ /** When set, `iss` must name this Tenant. */
33
+ tenantId?: string;
34
+ body: Uint8Array;
35
+ /** When set, `aud` must equal it: the URL this endpoint was registered with. */
36
+ audience?: string;
37
+ now?: number;
38
+ }): Promise<DeliveryClaims>;
39
+ /** base64url SHA-256 of `body`: the token's `bdy` claim. */
40
+ export declare function bodyHash(body: Uint8Array): Promise<string>;
41
+ /**
42
+ * The Tenant's public keys, fetched from `GET /v1/access/jwks` and cached by `kid`. An
43
+ * unknown `kid` refetches once, at most every `refetchMs`, so forged tokens cannot make the
44
+ * endpoint call the Runtime on every request. Concurrent lookups share one in-flight fetch.
45
+ */
46
+ export declare class JwksCache {
47
+ private readonly source;
48
+ private readonly refetchMs;
49
+ private readonly keys;
50
+ private lastFetch;
51
+ private pending;
52
+ constructor(source: {
53
+ url: string;
54
+ key?: string;
55
+ fetch?: typeof fetch;
56
+ } | {
57
+ keys: readonly JsonWebKey[];
58
+ }, refetchMs?: number);
59
+ readonly lookup: KeyLookup;
60
+ private load;
61
+ private fetchKeys;
62
+ }