@nylorun/agents 0.8.0-beta → 0.10.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 (60) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/README.md +371 -166
  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 +64 -0
  10. package/dist/action-handler.js +283 -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/builder.d.ts +20 -0
  18. package/dist/builder.js +26 -0
  19. package/dist/client.d.ts +3 -230
  20. package/dist/client.js +2 -335
  21. package/dist/connection.d.ts +0 -1
  22. package/dist/connection.js +3 -9
  23. package/dist/delivery-token.d.ts +57 -0
  24. package/dist/delivery-token.js +150 -0
  25. package/dist/execute-action.d.ts +6 -0
  26. package/dist/execute-action.js +24 -0
  27. package/dist/http.d.ts +31 -1
  28. package/dist/http.js +74 -14
  29. package/dist/index.d.ts +13 -9
  30. package/dist/index.js +5 -4
  31. package/dist/mcp/index.d.ts +2 -2
  32. package/dist/mcp/index.js +1 -1
  33. package/dist/sandbox/client.d.ts +4 -5
  34. package/dist/sandbox/client.js +4 -8
  35. package/dist/sandbox/index.d.ts +0 -2
  36. package/dist/sandbox/index.js +0 -1
  37. package/dist/served-definitions.d.ts +20 -0
  38. package/dist/served-definitions.js +100 -0
  39. package/dist/session-client.d.ts +282 -0
  40. package/dist/session-client.js +375 -0
  41. package/dist/sse.js +12 -0
  42. package/dist/studio-embed.d.ts +9 -0
  43. package/dist/studio-embed.js +8 -0
  44. package/dist/token-endpoint.d.ts +23 -0
  45. package/dist/token-endpoint.js +30 -0
  46. package/package.json +15 -8
  47. package/dist/ag-ui/history.d.ts +0 -7
  48. package/dist/ag-ui/history.js +0 -60
  49. package/dist/ag-ui/sse.d.ts +0 -9
  50. package/dist/ag-ui/sse.js +0 -7
  51. package/dist/ag-ui/translate.d.ts +0 -38
  52. package/dist/ag-ui/translate.js +0 -222
  53. package/dist/derived-credentials.d.ts +0 -5
  54. package/dist/derived-credentials.js +0 -17
  55. package/dist/executor.d.ts +0 -20
  56. package/dist/executor.js +0 -353
  57. package/dist/mcp/mcp.d.ts +0 -30
  58. package/dist/mcp/mcp.js +0 -80
  59. package/dist/sandbox/sandbox.d.ts +0 -27
  60. package/dist/sandbox/sandbox.js +0 -92
@@ -1,29 +1,21 @@
1
1
  /**
2
- * An AG-UI endpoint over the Tenant's agents, as web-standard `Request` → `Response`
3
- * handlers. The host authenticates people (`subject`); the handler maps each AG-UI thread to
4
- * one session per subject, agent and thread, and streams the session's transcript events as
5
- * AG-UI events. It never trusts a session id from the client, and it calls the Runtime as the
6
- * subject (`client.as`), so the Runtime itself keeps one subject out of another's sessions.
2
+ * AG-UI for the Tenant's agents, served from the app's own server as web-standard
3
+ * `Request` → `Response` handlers. The host signs people in (`subject`); the handler forwards
4
+ * each request to the Runtime's AG-UI endpoint (Host feature `ag-ui-endpoint`) acting for that
5
+ * person, so the Runtime keeps one person out of another's threads and does all the protocol
6
+ * work. The browser never sees the Tenant key, the Runtime's URL or the Tenant id.
7
+ *
8
+ * The same threads are reachable directly from a browser with a subject token: each thread is
9
+ * one session per subject, agent and thread on both paths.
7
10
  */
8
- import { createHash, randomUUID } from "node:crypto";
9
- import { EventType } from "@ag-ui/core";
10
- import { RunAgentInputSchema } from "@ag-ui/core/schemas";
11
- import { parseSubjectHeaders, } from "@nylorun/core/contracts";
11
+ import { parseSubjectHeaders } from "@nylorun/core/contracts";
12
12
  import { createClient, } from "../client.js";
13
- import { IncompatibleRuntimeError, RuntimeError } from "../http.js";
14
- import { messagesFromEvents } from "./history.js";
15
- import { SSE_CONTENT_TYPE, SSE_HEARTBEAT, sseFrame } from "./sse.js";
16
- import { RunTranslator } from "./translate.js";
17
- const HEARTBEAT_MS = 15_000;
18
- /** Host features the handler needs: the chat transcript, and acting for each subject. */
19
- const REQUIRED_FEATURES = ["transcript-events", "subject-headers"];
13
+ import { IncompatibleRuntimeError } from "../http.js";
14
+ /** The Host feature the handler forwards to. */
15
+ const REQUIRED_FEATURES = ["ag-ui-endpoint"];
20
16
  const DEFAULT_SCOPES = ["sessions:own"];
21
- const TERMINAL = new Set([
22
- "turn.completed",
23
- "turn.paused",
24
- "turn.failed",
25
- "turn.cancelled",
26
- ]);
17
+ /** Response headers passed back to the browser; nothing else of the Runtime's. */
18
+ const PASSED_HEADERS = ["content-type", "cache-control", "x-accel-buffering"];
27
19
  class Problem extends Error {
28
20
  status;
29
21
  code;
@@ -36,49 +28,28 @@ class Problem extends Error {
36
28
  function problem(status, message, code) {
37
29
  return Response.json({ error: message, ...(code ? { code } : {}) }, { status });
38
30
  }
31
+ /** The Runtime's refusal, as the browser may see it: no Runtime details. */
32
+ function refusal(status) {
33
+ if (status === 404)
34
+ return new Problem(404, "Not found");
35
+ if (status === 400)
36
+ return new Problem(400, "The request was rejected", "invalid_request");
37
+ if (status === 409)
38
+ return new Problem(409, "The agent is busy or waiting for an answer to an open interrupt", "session_busy");
39
+ return new Problem(502, "The agent service failed", "runtime_error");
40
+ }
39
41
  function problemOf(error) {
40
42
  if (error instanceof Problem)
41
43
  return error;
42
- if (error instanceof RuntimeError) {
43
- if (error.status === 404)
44
- return new Problem(404, "Not found");
45
- // The Runtime refused what the client sent (e.g. a cursor it cannot read).
46
- if (error.status === 400)
47
- return new Problem(400, "The request was rejected", "invalid_request");
48
- if (error.status === 409)
49
- return new Problem(409, "The agent is busy or waiting for an answer to an open interrupt", "session_busy");
50
- return new Problem(502, "The agent service failed", "runtime_error");
51
- }
52
44
  if (error instanceof IncompatibleRuntimeError)
53
45
  return new Problem(502, error.message, "runtime_incompatible");
54
- return new Problem(500, "Internal error");
55
- }
56
- /** A session per subject, agent and thread; one subject can never name another's session. */
57
- export function sessionIdFor(subject, agentId, threadId) {
58
- return createHash("sha256")
59
- .update(`${subject}\u0000${agentId}\u0000${threadId}`)
60
- .digest("hex")
61
- .slice(0, 32);
62
- }
63
- /** The trailing user message: the Runtime keeps the transcript, AG-UI resends all of it. */
64
- function lastUserMessage(messages) {
65
- const last = messages.at(-1);
66
- if (!last || last.role !== "user")
67
- throw new Problem(400, "The run's last message must be a user message");
68
- const text = typeof last.content === "string"
69
- ? last.content
70
- : last.content
71
- .filter((part) => part.type === "text")
72
- .map((part) => part.text)
73
- .join("\n");
74
- if (!text.trim())
75
- throw new Problem(400, "The user message has no text");
76
- return { id: last.id, text };
46
+ return new Problem(502, "The agent service failed", "runtime_error");
77
47
  }
78
48
  function normalizeBase(basePath = "/") {
79
49
  const trimmed = basePath.replace(/\/+$/, "");
80
50
  return trimmed === "" ? "" : trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
81
51
  }
52
+ const segment = (value) => encodeURIComponent(value);
82
53
  export function createAgUiHandler(options) {
83
54
  const allowed = new Set(options.agents.map((agent) => (typeof agent === "string" ? agent : agent.id)));
84
55
  const base = normalizeBase(options.basePath);
@@ -87,7 +58,7 @@ export function createAgUiHandler(options) {
87
58
  if (!checked.ok)
88
59
  throw new TypeError(`createAgUiHandler: ${checked.message}`);
89
60
  let ready;
90
- /** The client, once the Runtime is known to have every required feature. Retries after a failure. */
61
+ /** The client, once the Runtime is known to serve AG-UI. Retries after a failure. */
91
62
  function connected() {
92
63
  ready ??= (async () => {
93
64
  const client = await (options.client ?? createClient());
@@ -102,253 +73,91 @@ export function createAgUiHandler(options) {
102
73
  });
103
74
  return ready;
104
75
  }
105
- /** The client acting for `subject`: the Runtime limits it to the subject's own sessions. */
106
- async function clientFor(subject) {
107
- const client = await connected();
108
- try {
109
- return client.as(subject, { scopes });
110
- }
111
- catch (error) {
112
- // The host's `subject` returned something the Runtime cannot name (see `as`).
113
- throw new Problem(500, error.message, "subject_invalid");
114
- }
115
- }
116
76
  async function subjectOf(request) {
117
77
  const subject = await options.subject(request);
118
78
  if (!subject)
119
79
  throw new Problem(401, "Sign in required");
120
80
  return subject;
121
81
  }
122
- function assertAgent(agentId) {
123
- if (!allowed.has(agentId))
124
- throw new Problem(404, "Not found");
125
- }
126
- async function sessionFor(request, agentId, threadId) {
127
- assertAgent(agentId);
128
- const subject = await subjectOf(request);
129
- const client = await clientFor(subject);
130
- return {
131
- client,
132
- subject,
133
- session: client.session(sessionIdFor(subject, agentId, threadId)),
134
- };
135
- }
136
- async function submit(session, input) {
137
- if (input.resume?.length) {
138
- let cursor = null;
139
- // The interaction's kind is on the pending view, not in its id.
140
- const pending = await session.pending();
141
- const waits = Array.isArray(pending) ? pending : [];
142
- for (const entry of input.resume) {
143
- const wait = waits.find((w) => w?.interaction?.id === entry.interruptId);
144
- const key = `${input.runId}:${entry.interruptId}`;
145
- const payload = entry.payload;
146
- const accepted = (wait?.interaction?.kind ?? "approval") === "approval"
147
- ? await session.approve(entry.interruptId, entry.status === "resolved" &&
148
- (payload === true ||
149
- (typeof payload === "object" && payload?.approved === true)), { idempotencyKey: key })
150
- : entry.status === "cancelled"
151
- ? await session.cancel({ idempotencyKey: key, reason: "interrupt cancelled" })
152
- : await session.respond(entry.interruptId, (entry.payload ?? null), {
153
- idempotencyKey: key,
154
- });
155
- cursor ??= accepted.cursor;
156
- }
157
- return cursor;
158
- }
159
- const message = lastUserMessage(input.messages);
160
- // The AG-UI message id is the idempotency key: a retried run replays the same turn,
161
- // and history gives the message back its client id.
162
- const accepted = await session.input(message.text, {
163
- idempotencyKey: message.id,
164
- });
165
- return accepted.cursor;
166
- }
167
- /** Streams translated events until the run ends. Closing the response stops the read only. */
168
- function stream(request, translator, source, preface = []) {
169
- const reading = new AbortController();
170
- request.signal.addEventListener("abort", () => reading.abort(), { once: true });
171
- const bytes = new TextEncoder();
172
- const body = new ReadableStream({
173
- async start(controller) {
174
- const write = (text) => {
175
- try {
176
- controller.enqueue(bytes.encode(text));
177
- }
178
- catch {
179
- reading.abort();
180
- }
181
- };
182
- const heartbeat = setInterval(() => write(SSE_HEARTBEAT), HEARTBEAT_MS);
183
- try {
184
- write(sseFrame(translator.started()));
185
- for (const event of preface)
186
- write(sseFrame(event));
187
- if (source)
188
- for await (const event of source(reading.signal)) {
189
- const step = translator.translate(event);
190
- step.events.forEach((out, index) => write(sseFrame(out, index === step.events.length - 1 ? event.cursor : undefined)));
191
- if (step.finished || reading.signal.aborted)
192
- break;
193
- }
194
- }
195
- catch (error) {
196
- if (!reading.signal.aborted) {
197
- const failure = problemOf(error);
198
- write(sseFrame({
199
- type: EventType.RUN_ERROR,
200
- message: failure.message,
201
- ...(failure.code ? { code: failure.code } : {}),
202
- }));
203
- }
204
- }
205
- finally {
206
- clearInterval(heartbeat);
207
- reading.abort();
208
- try {
209
- controller.close();
210
- }
211
- catch {
212
- /* the client is gone */
213
- }
214
- }
215
- },
216
- cancel() {
217
- reading.abort();
218
- },
219
- });
220
- return new Response(body, {
221
- headers: {
222
- "content-type": SSE_CONTENT_TYPE,
223
- "cache-control": "no-cache, no-transform",
224
- "x-accel-buffering": "no",
225
- },
226
- });
227
- }
228
- async function run(request, agentId) {
82
+ /** Forwards to the Runtime acting for `subject`, and passes the answer back. */
83
+ async function forward(request, subject, path, init) {
84
+ const client = await connected();
85
+ let person;
229
86
  try {
230
- assertAgent(agentId);
231
- const subject = await subjectOf(request);
232
- const parsed = RunAgentInputSchema.safeParse(await request.json().catch(() => undefined));
233
- if (!parsed.success)
234
- throw new Problem(400, "Invalid RunAgentInput");
235
- const input = parsed.data;
236
- // Nylorun tools run in the Runtime or the app's executor; the browser runs none.
237
- if (input.tools?.length)
238
- throw new Problem(400, "Frontend tools are not supported");
239
- const client = await clientFor(subject);
240
- const extra = (await options.session?.(subject, agentId)) ?? {};
241
- const translator = new RunTranslator(input.threadId, input.runId);
242
- let session;
243
- try {
244
- // The host's parameters first: they never replace the session's identity.
245
- session = await client.createSession({
246
- ...extra,
247
- id: sessionIdFor(subject, agentId, input.threadId),
248
- agentId,
249
- ownerUserId: subject,
250
- });
251
- }
252
- catch (error) {
253
- if (error instanceof RuntimeError && error.status === 409)
254
- throw new Problem(409, "The thread's session was opened with other parameters", "session_conflict");
255
- throw error;
256
- }
257
- let cursor;
258
- try {
259
- cursor = await submit(session, input);
260
- }
261
- catch (error) {
262
- const failure = problemOf(error);
263
- // A busy or paused session is a run that could not start, not a transport failure.
264
- if (failure.status !== 409)
265
- throw failure;
266
- return stream(request, translator, undefined, [
267
- {
268
- type: EventType.RUN_ERROR,
269
- message: failure.message,
270
- code: failure.code,
271
- },
272
- ]);
273
- }
274
- return stream(request, translator, (signal) => session.observe({ ...(cursor ? { cursor } : {}), signal }));
87
+ person = client.as(subject, { scopes });
275
88
  }
276
89
  catch (error) {
277
- const failure = problemOf(error);
278
- return problem(failure.status, failure.message, failure.code);
90
+ // The host's `subject` returned something the Runtime cannot name (see `as`).
91
+ throw new Problem(500, error.message, "subject_invalid");
279
92
  }
280
- }
281
- async function history(request, agentId, threadId) {
282
- try {
283
- const { session } = await sessionFor(request, agentId, threadId);
284
- const items = [];
285
- let cursor;
286
- try {
287
- for (;;) {
288
- const page = await session.history({ ...(cursor ? { cursor } : {}) });
289
- items.push(...page.items);
290
- if (!page.cursor || page.cursor === cursor || page.items.length === 0)
291
- break;
292
- cursor = page.cursor;
293
- }
294
- }
295
- catch (error) {
296
- // A thread that never ran has no session yet.
297
- if (error instanceof RuntimeError && error.status === 404)
298
- return Response.json([]);
299
- throw error;
300
- }
301
- return Response.json(messagesFromEvents(items));
93
+ const response = await person.transport.forward(`/v1/ag-ui/agents${path}`, {
94
+ ...init,
95
+ signal: request.signal,
96
+ });
97
+ if (!response.ok) {
98
+ await response.body?.cancel().catch(() => { });
99
+ throw refusal(response.status);
302
100
  }
303
- catch (error) {
304
- const failure = problemOf(error);
305
- return problem(failure.status, failure.message, failure.code);
101
+ const headers = new Headers();
102
+ for (const name of PASSED_HEADERS) {
103
+ const value = response.headers.get(name);
104
+ if (value !== null)
105
+ headers.set(name, value);
306
106
  }
107
+ return new Response(response.body, { status: response.status, headers });
307
108
  }
308
- async function reattach(request, agentId, threadId) {
109
+ async function run(request, agentId) {
309
110
  try {
310
- const { session } = await sessionFor(request, agentId, threadId);
311
- const url = new URL(request.url);
312
- const cursor = request.headers.get("last-event-id") ?? url.searchParams.get("cursor");
313
- if (!cursor)
314
- throw new Problem(400, "Last-Event-ID or ?cursor= is required");
315
- const view = await session.inspect();
316
- const runId = url.searchParams.get("runId") ??
317
- view.activeTurnId ??
318
- (typeof view.lastTurnId === "string" ? view.lastTurnId : randomUUID());
319
- const translator = new RunTranslator(threadId, runId, { reattached: true });
320
- if (view.activeTurnId)
321
- return stream(request, translator, (signal) => session.observe({ cursor, signal }));
322
- // No turn is running: send what the client missed, if the run ended after the cursor.
323
- const missed = [];
324
- let from = cursor;
325
- for (;;) {
326
- const page = await session.history({ cursor: from });
327
- missed.push(...page.items);
328
- if (!page.cursor || page.cursor === from || page.items.length === 0)
329
- break;
330
- from = page.cursor;
331
- }
332
- if (!missed.some((event) => TERMINAL.has(event.type)))
333
- return new Response(null, { status: 204 });
334
- return stream(request, translator, () => iterate(missed));
111
+ if (!allowed.has(agentId))
112
+ throw new Problem(404, "Not found");
113
+ const subject = await subjectOf(request);
114
+ const body = (await request.json().catch(() => undefined));
115
+ if (!body || typeof body !== "object" || Array.isArray(body))
116
+ throw new Problem(400, "Invalid RunAgentInput", "invalid_request");
117
+ // Only the host names the session's options; whatever the browser sent is replaced.
118
+ const forwarded = body.forwardedProps && typeof body.forwardedProps === "object"
119
+ ? { ...body.forwardedProps }
120
+ : {};
121
+ delete forwarded.nylorun;
122
+ const extra = (await options.session?.(subject, agentId)) ?? {};
123
+ if (Object.keys(extra).length > 0)
124
+ forwarded.nylorun = { session: extra };
125
+ return await forward(request, subject, `/${segment(agentId)}`, {
126
+ method: "POST",
127
+ headers: { accept: "text/event-stream" },
128
+ body: JSON.stringify({ ...body, forwardedProps: forwarded }),
129
+ });
335
130
  }
336
131
  catch (error) {
337
132
  const failure = problemOf(error);
338
133
  return problem(failure.status, failure.message, failure.code);
339
134
  }
340
135
  }
341
- async function cancel(request, agentId, threadId) {
136
+ async function thread(request, agentId, threadId, action, init) {
342
137
  try {
343
- const { session } = await sessionFor(request, agentId, threadId);
344
- await session.cancel({ idempotencyKey: `cancel:${randomUUID()}` });
345
- return new Response(null, { status: 204 });
138
+ if (!allowed.has(agentId))
139
+ throw new Problem(404, "Not found");
140
+ const subject = await subjectOf(request);
141
+ const query = new URL(request.url).search;
142
+ return await forward(request, subject, `/${segment(agentId)}/threads/${segment(threadId)}/${action}${query}`, init);
346
143
  }
347
144
  catch (error) {
348
145
  const failure = problemOf(error);
349
146
  return problem(failure.status, failure.message, failure.code);
350
147
  }
351
148
  }
149
+ const history = (request, agentId, threadId) => thread(request, agentId, threadId, "messages", { method: "GET" });
150
+ const reattach = (request, agentId, threadId) => {
151
+ const cursor = request.headers.get("last-event-id");
152
+ return thread(request, agentId, threadId, "events", {
153
+ method: "GET",
154
+ headers: {
155
+ accept: "text/event-stream",
156
+ ...(cursor ? { "last-event-id": cursor } : {}),
157
+ },
158
+ });
159
+ };
160
+ const cancel = (request, agentId, threadId) => thread(request, agentId, threadId, "cancel", { method: "POST" });
352
161
  async function route(request) {
353
162
  const path = new URL(request.url).pathname;
354
163
  if (path !== base && !path.startsWith(`${base}/`))
@@ -359,12 +168,12 @@ export function createAgUiHandler(options) {
359
168
  .slice(base.length)
360
169
  .split("/")
361
170
  .slice(1)
362
- .map((segment) => decodeURIComponent(segment));
171
+ .map((part) => decodeURIComponent(part));
363
172
  }
364
173
  catch {
365
174
  return problem(404, "Not found");
366
175
  }
367
- if (segments.some((segment) => segment === ""))
176
+ if (segments.some((part) => part === ""))
368
177
  return problem(404, "Not found");
369
178
  const [agentId, threads, threadId, action] = segments;
370
179
  const method = request.method.toUpperCase();
@@ -392,6 +201,3 @@ export function createAgUiHandler(options) {
392
201
  }
393
202
  return { fetch: route, run, history, reattach, cancel };
394
203
  }
395
- async function* iterate(items) {
396
- yield* items;
397
- }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * `@nylorun/agents/ag-ui`: serve the Tenant's agents to AG-UI clients from the app's own
3
- * server. The only entry point that loads `@ag-ui/core`.
3
+ * server. The handler forwards to the Runtime's AG-UI endpoint for each signed-in person; it
4
+ * loads no AG-UI package itself.
4
5
  */
5
6
  export { createAgUiHandler } from "./handler.js";
6
7
  export type { AgUiHandler, AgUiHandlerOptions, AgUiSessionOptions, } from "./handler.js";
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * `@nylorun/agents/ag-ui`: serve the Tenant's agents to AG-UI clients from the app's own
3
- * server. The only entry point that loads `@ag-ui/core`.
3
+ * server. The handler forwards to the Runtime's AG-UI endpoint for each signed-in person; it
4
+ * loads no AG-UI package itself.
4
5
  */
5
6
  export { createAgUiHandler } from "./handler.js";
6
7
  export { toNodeListener } from "./node.js";
@@ -0,0 +1,97 @@
1
+ import type { CredentialSelection, VaultInfo } from "@nylorun/core/contracts";
2
+ import type { TokenSource } from "./http.js";
3
+ import { AgentsClient, type SessionClient } from "./session-client.js";
4
+ /** What the token callback returns: the token, or the app server's `{ token, expiresAt }`. */
5
+ export type TokenReply = string | {
6
+ token: string;
7
+ expiresAt?: string;
8
+ };
9
+ export interface BrowserClientOptions {
10
+ /** The Runtime's URL, as the page reaches it. */
11
+ url: string;
12
+ /** The Tenant's publishable key for this app (`nr_pub_…`). */
13
+ publishableKey: string;
14
+ /** Returns a subject token for the signed-in person, usually from your app server. */
15
+ token(signal?: AbortSignal): Promise<TokenReply>;
16
+ fetch?: typeof fetch;
17
+ }
18
+ interface Claims {
19
+ sub?: string;
20
+ exp?: number;
21
+ }
22
+ /** The token's claims, read without verifying (the Runtime verifies). */
23
+ export declare function readClaims(token: string): Claims;
24
+ /** A token kept in memory, fetched on demand, one fetch at a time. */
25
+ export declare class CachedToken implements TokenSource {
26
+ private readonly fetchToken;
27
+ private readonly now;
28
+ private current;
29
+ private pending;
30
+ constructor(fetchToken: (signal?: AbortSignal) => Promise<TokenReply>, now?: () => number);
31
+ get(signal?: AbortSignal): Promise<string>;
32
+ invalidate(): void;
33
+ /** The subject of the current token, fetching one if needed. */
34
+ subject(signal?: AbortSignal): Promise<string>;
35
+ }
36
+ /** The signed-in person's view of the Runtime: their sessions and vaults, the agents they may use. */
37
+ export declare class BrowserClient {
38
+ /** The underlying Tenant API client, for anything this class does not wrap. */
39
+ readonly client: AgentsClient;
40
+ readonly url: string;
41
+ readonly publishableKey: string;
42
+ private readonly tokens;
43
+ private readonly fetcher;
44
+ constructor(options: BrowserClientOptions);
45
+ /** The person the tokens are for. */
46
+ subject(signal?: AbortSignal): Promise<string>;
47
+ /** A current token, e.g. for a request this client does not make itself. */
48
+ token(signal?: AbortSignal): Promise<string>;
49
+ /** The agents the person may use: id, name and description. */
50
+ listAgents(signal?: AbortSignal): Promise<{
51
+ agents: {
52
+ agentId: string;
53
+ name?: string;
54
+ description?: string;
55
+ }[];
56
+ }>;
57
+ listSessions(agentId?: string, signal?: AbortSignal): Promise<{
58
+ sessions: import("./session-client.js").SessionView[];
59
+ }>;
60
+ session(sessionId: string): SessionClient;
61
+ /** A session of the person's own; the owner is always the token's subject. */
62
+ createSession(options: {
63
+ id?: string;
64
+ agentId: string;
65
+ vaultIds?: readonly string[];
66
+ credentialSelections?: readonly CredentialSelection[];
67
+ }): Promise<SessionClient>;
68
+ createVault(options: {
69
+ name: string;
70
+ idempotencyKey: string;
71
+ metadata?: Record<string, string>;
72
+ }): Promise<VaultInfo>;
73
+ listVaults(signal?: AbortSignal): Promise<{
74
+ vaults: VaultInfo[];
75
+ }>;
76
+ /** `fetch` with this client's credentials, retrying once after `401 token_expired`. */
77
+ private send;
78
+ /**
79
+ * For `@ag-ui/client`'s `HttpAgent`: `new HttpAgent({ url, fetch })`. The `fetch` adds the
80
+ * publishable key and a current token, and when the Runtime ends a run's stream because the
81
+ * token expired or was revoked, it reattaches with a new token from the last event, so the
82
+ * agent sees one run.
83
+ */
84
+ agUi(agentId: string): {
85
+ url: string;
86
+ fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
87
+ };
88
+ /** The thread's messages, for `HttpAgent`'s `initialMessages`. */
89
+ agUiHistory(agentId: string, threadId: string, signal?: AbortSignal): Promise<unknown[]>;
90
+ /**
91
+ * Passes a run's frames through; on `nylorun.stream_closed` before the run finished, fetches
92
+ * the rest from the last event id and continues without repeating `RUN_STARTED`.
93
+ */
94
+ private stitch;
95
+ }
96
+ export declare function createBrowserClient(options: BrowserClientOptions): BrowserClient;
97
+ export {};