@cubos/agent-sdk 0.0.1136563

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.
package/dist/index.js ADDED
@@ -0,0 +1,1826 @@
1
+ // src/errors.ts
2
+ class AgentError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "AgentError";
6
+ }
7
+ }
8
+
9
+ class AgentApiError extends AgentError {
10
+ status;
11
+ requestId;
12
+ body;
13
+ constructor(message, status, requestId = null, body = "") {
14
+ super(message);
15
+ this.name = "AgentApiError";
16
+ this.status = status;
17
+ this.requestId = requestId;
18
+ this.body = body;
19
+ }
20
+ json() {
21
+ try {
22
+ return JSON.parse(this.body);
23
+ } catch {
24
+ return;
25
+ }
26
+ }
27
+ get isAuthError() {
28
+ return this.status === 401 || this.status === 403;
29
+ }
30
+ get isNotFound() {
31
+ return this.status === 404;
32
+ }
33
+ get isConflict() {
34
+ return this.status === 409;
35
+ }
36
+ get isRetryable() {
37
+ return this.status === 429 || this.status >= 500;
38
+ }
39
+ }
40
+
41
+ class AgentConfigError extends AgentError {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = "AgentConfigError";
45
+ }
46
+ }
47
+
48
+ class AgentNetworkError extends AgentError {
49
+ cause;
50
+ timedOut;
51
+ constructor(message, cause, timedOut = false) {
52
+ super(message);
53
+ this.name = "AgentNetworkError";
54
+ this.cause = cause;
55
+ this.timedOut = timedOut;
56
+ }
57
+ }
58
+ var DEFAULT_MESSAGES = {
59
+ 400: "Invalid request.",
60
+ 401: "Not authenticated.",
61
+ 403: "Not allowed.",
62
+ 404: "Not found.",
63
+ 409: "Conflicts with the current state.",
64
+ 413: "Payload too large.",
65
+ 429: "Rate limited."
66
+ };
67
+ async function raiseForStatus(res, fallback) {
68
+ if (res.ok)
69
+ return;
70
+ let detail = "";
71
+ try {
72
+ detail = await res.text();
73
+ } catch {
74
+ detail = "";
75
+ }
76
+ const base = DEFAULT_MESSAGES[res.status] ?? fallback;
77
+ throw new AgentApiError(detail ? `${base} (${detail.slice(0, 500)})` : base, res.status, res.headers.get("x-request-id"), detail);
78
+ }
79
+
80
+ // src/sse.ts
81
+ var MAX_BACKOFF_MS = 30000;
82
+
83
+ class Fatal extends Error {
84
+ cause;
85
+ constructor(cause) {
86
+ super("fatal stream error");
87
+ this.cause = cause;
88
+ }
89
+ }
90
+ async function readSse(opts) {
91
+ const doFetch = opts.fetchImpl ?? globalThis.fetch;
92
+ let lastEventId = opts.lastEventId;
93
+ let attempt = 0;
94
+ while (!opts.signal.aborted) {
95
+ let madeProgress = false;
96
+ try {
97
+ const headers = {
98
+ ...await opts.headers?.(),
99
+ Accept: "text/event-stream"
100
+ };
101
+ if (lastEventId !== undefined)
102
+ headers["Last-Event-ID"] = lastEventId;
103
+ const res = await doFetch(opts.url, { headers, signal: opts.signal });
104
+ if (opts.signal.aborted)
105
+ return;
106
+ if (res.ok && res.body)
107
+ opts.onOpen?.();
108
+ if (!res.ok || !res.body) {
109
+ if (res.status >= 400 && res.status < 500) {
110
+ await raiseForStatus(res, `Could not open ${opts.url}.`).catch((err) => {
111
+ throw new Fatal(err);
112
+ });
113
+ return;
114
+ }
115
+ throw new Error(`stream open failed with HTTP ${res.status}`);
116
+ }
117
+ for await (const frame of frames(res.body, opts.signal)) {
118
+ const parsed = parseFrame(frame, opts.event);
119
+ if (parsed === null)
120
+ continue;
121
+ if (parsed.id !== null)
122
+ lastEventId = parsed.id;
123
+ opts.onEvent(parsed.data, parsed.event);
124
+ madeProgress = true;
125
+ }
126
+ } catch (err) {
127
+ if (opts.signal.aborted)
128
+ return;
129
+ if (err instanceof Fatal)
130
+ throw err.cause;
131
+ opts.onError?.(err);
132
+ }
133
+ if (opts.signal.aborted)
134
+ return;
135
+ if (madeProgress)
136
+ attempt = 0;
137
+ const delayMs = Math.min(MAX_BACKOFF_MS, 1000 * 2 ** attempt);
138
+ attempt += 1;
139
+ await sleep(delayMs, opts.signal);
140
+ }
141
+ }
142
+ async function* frames(body, signal) {
143
+ const reader = body.getReader();
144
+ const decoder = new TextDecoder;
145
+ let buffer = "";
146
+ try {
147
+ while (!signal.aborted) {
148
+ const { value, done } = await reader.read();
149
+ if (done)
150
+ return;
151
+ buffer += decoder.decode(value, { stream: true });
152
+ for (;; ) {
153
+ const sep = buffer.indexOf(`
154
+
155
+ `);
156
+ if (sep === -1)
157
+ break;
158
+ yield buffer.slice(0, sep);
159
+ buffer = buffer.slice(sep + 2);
160
+ }
161
+ }
162
+ } finally {
163
+ reader.cancel().catch(() => {});
164
+ }
165
+ }
166
+ function parseFrame(frame, expectedEvent) {
167
+ let dataLine = null;
168
+ let id = null;
169
+ let eventName = "message";
170
+ for (const line of frame.split(`
171
+ `)) {
172
+ if (line.startsWith(":"))
173
+ continue;
174
+ if (line.startsWith("data:"))
175
+ dataLine = line.slice(5).trimStart();
176
+ else if (line.startsWith("event:"))
177
+ eventName = line.slice(6).trim();
178
+ else if (line.startsWith("id:"))
179
+ id = line.slice(3).trim();
180
+ }
181
+ const wanted = typeof expectedEvent === "string" ? eventName === expectedEvent : expectedEvent.includes(eventName);
182
+ if (!wanted || dataLine === null)
183
+ return null;
184
+ try {
185
+ return { data: JSON.parse(dataLine), id, event: eventName };
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+ function sleep(ms, signal) {
191
+ return new Promise((resolve) => {
192
+ const onAbort = () => {
193
+ clearTimeout(timer);
194
+ resolve();
195
+ };
196
+ const timer = setTimeout(() => {
197
+ signal.removeEventListener("abort", onAbort);
198
+ resolve();
199
+ }, ms);
200
+ signal.addEventListener("abort", onAbort, { once: true });
201
+ });
202
+ }
203
+
204
+ // src/http.ts
205
+ var DEFAULT_TIMEOUT_MS = 30000;
206
+ var DEFAULT_MAX_RETRIES = 2;
207
+ var MAX_RETRY_WAIT_MS = 20000;
208
+ function parseRetryAfter(header) {
209
+ if (!header)
210
+ return null;
211
+ const seconds = Number(header.trim());
212
+ if (Number.isFinite(seconds))
213
+ return Math.max(0, seconds * 1000);
214
+ const date = Date.parse(header);
215
+ if (Number.isNaN(date))
216
+ return null;
217
+ return Math.max(0, date - Date.now());
218
+ }
219
+ function sleep2(ms, signal) {
220
+ return new Promise((resolve, reject) => {
221
+ if (signal?.aborted) {
222
+ reject(signal.reason ?? new Error("Aborted"));
223
+ return;
224
+ }
225
+ const timer = setTimeout(() => {
226
+ signal?.removeEventListener("abort", onAbort);
227
+ resolve();
228
+ }, ms);
229
+ function onAbort() {
230
+ clearTimeout(timer);
231
+ reject(signal?.reason ?? new Error("Aborted"));
232
+ }
233
+ signal?.addEventListener("abort", onAbort, { once: true });
234
+ });
235
+ }
236
+ function buildQuery(query) {
237
+ if (!query)
238
+ return "";
239
+ const search = new URLSearchParams;
240
+ for (const [key, value] of Object.entries(query)) {
241
+ if (value !== undefined)
242
+ search.set(key, String(value));
243
+ }
244
+ const qs = search.toString();
245
+ return qs ? `?${qs}` : "";
246
+ }
247
+ function normalizeBaseUrl(baseUrl) {
248
+ if (typeof baseUrl !== "string") {
249
+ throw new AgentConfigError("baseUrl is required, e.g. https://agent.acme.com");
250
+ }
251
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
252
+ if (trimmed === "")
253
+ return "";
254
+ if (!/^https?:\/\//i.test(trimmed)) {
255
+ throw new AgentConfigError(`baseUrl must start with http:// or https:// (got ${JSON.stringify(baseUrl)}).`);
256
+ }
257
+ try {
258
+ new URL(trimmed);
259
+ } catch {
260
+ throw new AgentConfigError(`baseUrl is not a valid URL (got ${JSON.stringify(baseUrl)}).`);
261
+ }
262
+ return trimmed;
263
+ }
264
+ function combineSignals(caller, timeout) {
265
+ if (!caller)
266
+ return timeout;
267
+ const anyOf = AbortSignal.any;
268
+ if (typeof anyOf === "function")
269
+ return anyOf([caller, timeout]);
270
+ const controller = new AbortController;
271
+ const abort = () => controller.abort();
272
+ if (caller.aborted || timeout.aborted)
273
+ controller.abort();
274
+ caller.addEventListener("abort", abort, { once: true });
275
+ timeout.addEventListener("abort", abort, { once: true });
276
+ return controller.signal;
277
+ }
278
+ function createTransport(baseUrl, auth, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_TIMEOUT_MS, maxRetries = DEFAULT_MAX_RETRIES) {
279
+ const root = normalizeBaseUrl(baseUrl);
280
+ async function authHeader(forceRefresh) {
281
+ if (!("apiKey" in auth))
282
+ return `Bearer ${await auth.getToken({ forceRefresh })}`;
283
+ const key = typeof auth.apiKey === "function" ? await auth.apiKey() : auth.apiKey;
284
+ return `Bearer ${key}`;
285
+ }
286
+ async function send(method, path, opts, retry) {
287
+ const headers = { Authorization: await authHeader(retry) };
288
+ const isForm = typeof FormData !== "undefined" && opts.body instanceof FormData;
289
+ let body;
290
+ if (isForm) {
291
+ body = opts.body;
292
+ } else if (opts.body !== undefined) {
293
+ body = JSON.stringify(opts.body);
294
+ headers["Content-Type"] = "application/json";
295
+ }
296
+ const timer = timeoutMs > 0 && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(timeoutMs) : undefined;
297
+ const signal = timer ? combineSignals(opts.signal, timer) : opts.signal;
298
+ const url = `${root}${path}${buildQuery(opts.query)}`;
299
+ try {
300
+ return await fetchImpl(url, { method, headers, body, signal });
301
+ } catch (err) {
302
+ if (opts.signal?.aborted)
303
+ throw err;
304
+ if (timer?.aborted) {
305
+ throw new AgentNetworkError(`Request to ${url} timed out after ${timeoutMs}ms.`, err, true);
306
+ }
307
+ throw new AgentNetworkError(`Could not reach ${url}. Check the base URL, that the server is running, and CORS.`, err);
308
+ }
309
+ }
310
+ async function once(method, path, opts = {}) {
311
+ let attempt = 0;
312
+ for (;; ) {
313
+ let res = await send(method, path, opts, false);
314
+ if (res.status === 401 && "getToken" in auth) {
315
+ res = await send(method, path, opts, true);
316
+ }
317
+ if (res.status !== 429 || attempt >= maxRetries)
318
+ return res;
319
+ const waitMs = parseRetryAfter(res.headers.get("retry-after"));
320
+ if (waitMs === null || waitMs > MAX_RETRY_WAIT_MS)
321
+ return res;
322
+ attempt += 1;
323
+ await sleep2(waitMs, opts.signal);
324
+ }
325
+ }
326
+ return {
327
+ fetchImpl,
328
+ url: (path, query) => `${root}${path}${buildQuery(query)}`,
329
+ async streamHeaders() {
330
+ return { Authorization: await authHeader(false) };
331
+ },
332
+ fetchRaw: once,
333
+ async request(method, path, opts = {}) {
334
+ const res = await once(method, path, opts);
335
+ await raiseForStatus(res, `${method} ${path} failed.`);
336
+ if (opts.raw || res.status === 204)
337
+ return;
338
+ return await res.json();
339
+ }
340
+ };
341
+ }
342
+
343
+ // src/admin/paths.ts
344
+ var enc = encodeURIComponent;
345
+ var tenantPath = (tenantSlug) => `/api/v1/tenants/${enc(tenantSlug)}`;
346
+ function withoutNoopRename(input, currentSlug) {
347
+ if (input.slug && input.slug === currentSlug)
348
+ return { ...input, slug: undefined };
349
+ return input;
350
+ }
351
+
352
+ // src/admin/agents.ts
353
+ function agentsApi(t, tenantSlug) {
354
+ const base = `${tenantPath(tenantSlug)}/agents`;
355
+ const agent = (slug) => `${base}/${enc(slug)}`;
356
+ return {
357
+ list: (signal) => t.request("GET", base, { signal }),
358
+ get: (slug, signal) => t.request("GET", agent(slug), { signal }),
359
+ create: (input) => t.request("POST", base, { body: input }),
360
+ update: (currentSlug, input) => t.request("PATCH", agent(currentSlug), {
361
+ body: withoutNoopRename(input, currentSlug)
362
+ }),
363
+ delete: (slug) => t.request("DELETE", agent(slug), { raw: true }),
364
+ countTokens: (slug, input, signal) => t.request("POST", `${agent(slug)}/count-tokens`, {
365
+ body: input,
366
+ signal
367
+ }),
368
+ listMcps: (agentSlug, signal) => t.request("GET", `${agent(agentSlug)}/mcps`, { signal }),
369
+ addMcp: (agentSlug, mcpSlug) => t.request("PUT", `${agent(agentSlug)}/mcps/${enc(mcpSlug)}`, { raw: true }),
370
+ removeMcp: (agentSlug, mcpSlug) => t.request("DELETE", `${agent(agentSlug)}/mcps/${enc(mcpSlug)}`, { raw: true }),
371
+ updateMcpTools: (agentSlug, mcpSlug, enabledTools) => t.request("PATCH", `${agent(agentSlug)}/mcps/${enc(mcpSlug)}`, {
372
+ body: { enabled_tools: enabledTools },
373
+ raw: true
374
+ }),
375
+ replaceMcpRoles: (agentSlug, mcpSlug, roleSlugs) => t.request("PUT", `${agent(agentSlug)}/mcps/${enc(mcpSlug)}/roles`, {
376
+ body: { role_slugs: roleSlugs },
377
+ raw: true
378
+ }),
379
+ updateMcpRoleTools: (agentSlug, mcpSlug, roleSlug, enabledTools) => t.request("PATCH", `${agent(agentSlug)}/mcps/${enc(mcpSlug)}/roles/${enc(roleSlug)}`, {
380
+ body: { enabled_tools: enabledTools },
381
+ raw: true
382
+ }),
383
+ listSkills: (agentSlug, signal) => t.request("GET", `${agent(agentSlug)}/skills`, { signal }),
384
+ addSkill: (agentSlug, skillSlug) => t.request("PUT", `${agent(agentSlug)}/skills/${enc(skillSlug)}`, { raw: true }),
385
+ removeSkill: (agentSlug, skillSlug) => t.request("DELETE", `${agent(agentSlug)}/skills/${enc(skillSlug)}`, { raw: true }),
386
+ replaceSkillRoles: (agentSlug, skillSlug, roleSlugs) => t.request("PUT", `${agent(agentSlug)}/skills/${enc(skillSlug)}/roles`, {
387
+ body: { role_slugs: roleSlugs },
388
+ raw: true
389
+ }),
390
+ listTaskTemplates: (agentSlug, signal) => t.request("GET", `${agent(agentSlug)}/task-templates`, {
391
+ signal
392
+ }),
393
+ addTaskTemplate: (agentSlug, templateSlug) => t.request("PUT", `${agent(agentSlug)}/task-templates/${enc(templateSlug)}`, {
394
+ raw: true
395
+ }),
396
+ removeTaskTemplate: (agentSlug, templateSlug) => t.request("DELETE", `${agent(agentSlug)}/task-templates/${enc(templateSlug)}`, {
397
+ raw: true
398
+ }),
399
+ replaceTaskTemplateRoles: (agentSlug, templateSlug, roleSlugs) => t.request("PUT", `${agent(agentSlug)}/task-templates/${enc(templateSlug)}/roles`, {
400
+ body: { role_slugs: roleSlugs },
401
+ raw: true
402
+ }),
403
+ listAutoCallTools: (agentSlug, signal) => t.request("GET", `${agent(agentSlug)}/auto-call-tools`, {
404
+ signal
405
+ }),
406
+ putAutoCallTools: (agentSlug, tools) => t.request("PUT", `${agent(agentSlug)}/auto-call-tools`, {
407
+ body: { tools },
408
+ raw: true
409
+ })
410
+ };
411
+ }
412
+
413
+ // src/admin/ai-providers.ts
414
+ function aiProvidersApi(t, tenantSlug) {
415
+ const base = `${tenantPath(tenantSlug)}/ai-providers`;
416
+ const provider = (slug) => `${base}/${enc(slug)}`;
417
+ return {
418
+ list: (signal) => t.request("GET", base, { signal }),
419
+ get: (slug, signal) => t.request("GET", provider(slug), { signal }),
420
+ create: (input) => t.request("POST", base, { body: input }),
421
+ update: (currentSlug, input) => t.request("PATCH", provider(currentSlug), {
422
+ body: withoutNoopRename(input, currentSlug)
423
+ }),
424
+ delete: (slug) => t.request("DELETE", provider(slug), { raw: true }),
425
+ listModels: (slug, signal) => t.request("GET", `${provider(slug)}/models`, { signal }),
426
+ refreshModels: (slug) => t.request("POST", `${provider(slug)}/models/refresh`),
427
+ updateModel: (slug, modelId, input) => t.request("PATCH", `${provider(slug)}/models/${enc(modelId)}`, {
428
+ body: input,
429
+ raw: true
430
+ })
431
+ };
432
+ }
433
+
434
+ // src/admin/channels.ts
435
+ function channelsApi(t, tenantSlug) {
436
+ const base = `${tenantPath(tenantSlug)}/channels`;
437
+ const channel = (slug) => `${base}/${enc(slug)}`;
438
+ return {
439
+ list: (signal) => t.request("GET", base, { signal }),
440
+ get: (slug, signal) => t.request("GET", channel(slug), { signal }),
441
+ create: (input) => t.request("POST", base, { body: input }),
442
+ update: (slug, input) => t.request("PATCH", channel(slug), { body: input }),
443
+ delete: (slug) => t.request("DELETE", channel(slug), { raw: true }),
444
+ rotateSecret: (slug) => t.request("POST", `${channel(slug)}/rotate-secret`),
445
+ listComponentLibraries: (slug, signal) => t.request("GET", `${channel(slug)}/component-libraries`, {
446
+ signal
447
+ }),
448
+ setComponentLibraries: (slug, libraries) => t.request("PUT", `${channel(slug)}/component-libraries`, {
449
+ body: { libraries }
450
+ }),
451
+ listDeadLetters: (slug, signal) => t.request("GET", `${channel(slug)}/dead-letters`, { signal }),
452
+ pairWhatsapp: (slug, input) => t.request("POST", `${channel(slug)}/whatsapp/pair`, {
453
+ body: input
454
+ })
455
+ };
456
+ }
457
+
458
+ // src/client-tools.ts
459
+ var DEFAULT_CLAIM_TTL_SECONDS = 70;
460
+ var SUBMIT_ATTEMPTS = 5;
461
+ function serveClientTools(t, conversationPath, conversationId, options) {
462
+ const controller = new AbortController;
463
+ const stop = () => controller.abort();
464
+ if (options.signal) {
465
+ if (options.signal.aborted)
466
+ stop();
467
+ else
468
+ options.signal.addEventListener("abort", stop, { once: true });
469
+ }
470
+ const claimant = options.claimant ?? `sdk-${randomId()}`;
471
+ const ttl = options.claimTtlSeconds ?? DEFAULT_CLAIM_TTL_SECONDS;
472
+ let basePath;
473
+ const resolveBase = async () => {
474
+ basePath ??= await conversationPath();
475
+ return basePath;
476
+ };
477
+ const inFlight = new Set;
478
+ const running = new Set;
479
+ const track = (p) => {
480
+ running.add(p);
481
+ p.finally(() => running.delete(p)).catch(() => {});
482
+ };
483
+ const done = (async () => {
484
+ try {
485
+ await run();
486
+ } catch (err) {
487
+ stop();
488
+ while (running.size > 0)
489
+ await Promise.allSettled([...running]);
490
+ throw err;
491
+ }
492
+ })();
493
+ async function run() {
494
+ const base = await resolveBase();
495
+ if (options.declare !== false) {
496
+ await t.request("PUT", `${base}/client-tools`, {
497
+ body: { tools: declarations(options.tools) },
498
+ signal: controller.signal
499
+ });
500
+ }
501
+ if (options.watch === false) {
502
+ track(guard(drain));
503
+ await stopped();
504
+ while (running.size > 0)
505
+ await Promise.allSettled([...running]);
506
+ return;
507
+ }
508
+ await readSse({
509
+ url: t.url(`${base}/events/stream`),
510
+ event: "conversation_event",
511
+ headers: () => t.streamHeaders(),
512
+ fetchImpl: t.fetchImpl,
513
+ signal: controller.signal,
514
+ onError: options.onError,
515
+ onOpen: () => {
516
+ track(guard(drain));
517
+ },
518
+ onEvent: (ev) => {
519
+ if (ev.event_type !== "client_tool_call")
520
+ return;
521
+ track(guard(drain));
522
+ }
523
+ });
524
+ while (running.size > 0)
525
+ await Promise.allSettled([...running]);
526
+ }
527
+ async function drain() {
528
+ if (controller.signal.aborted)
529
+ return;
530
+ const base = await resolveBase();
531
+ const calls = await t.request("GET", `${base}/client-tool-calls`, {
532
+ signal: controller.signal
533
+ });
534
+ for (const call of calls) {
535
+ if (inFlight.has(call.tool_call_id))
536
+ continue;
537
+ const tool = options.tools[call.tool_name];
538
+ if (!tool)
539
+ continue;
540
+ inFlight.add(call.tool_call_id);
541
+ track(guard(() => execute(call, tool)));
542
+ }
543
+ }
544
+ async function execute(call, tool) {
545
+ const id = call.tool_call_id;
546
+ const base = await resolveBase();
547
+ try {
548
+ if (!await acquireClaim(call))
549
+ return;
550
+ const keepAlive = setInterval(() => {
551
+ t.request("POST", `${base}/client-tool-calls/${id}/claim`, {
552
+ body: { claimant, ttl_seconds: ttl }
553
+ }).catch(() => {});
554
+ }, Math.max(1000, ttl * 1000 / 2));
555
+ let body;
556
+ try {
557
+ const result = await tool.handler(call.arguments, {
558
+ toolCallId: id,
559
+ conversationId,
560
+ signal: controller.signal
561
+ });
562
+ body = { result: result === undefined ? null : result, claimant };
563
+ } catch (err) {
564
+ if (controller.signal.aborted) {
565
+ clearInterval(keepAlive);
566
+ await t.request("DELETE", `${base}/client-tool-calls/${id}/claim`, {
567
+ query: { claimant },
568
+ raw: true
569
+ }).catch(() => {});
570
+ return;
571
+ }
572
+ options.onError?.(err);
573
+ body = { error: errorText(err), claimant };
574
+ } finally {
575
+ clearInterval(keepAlive);
576
+ }
577
+ await submitWithRetry(id, body);
578
+ } finally {
579
+ inFlight.delete(id);
580
+ }
581
+ }
582
+ async function acquireClaim(call) {
583
+ const base = await resolveBase();
584
+ const deadline = Date.parse(call.deadline_at);
585
+ while (!controller.signal.aborted) {
586
+ try {
587
+ await t.request("POST", `${base}/client-tool-calls/${call.tool_call_id}/claim`, { body: { claimant, ttl_seconds: ttl }, signal: controller.signal });
588
+ return true;
589
+ } catch (err) {
590
+ if (!(err instanceof AgentApiError))
591
+ throw err;
592
+ if (err.status === 410 || err.status === 404)
593
+ return false;
594
+ if (err.status !== 409)
595
+ throw err;
596
+ const held = err.json()?.claim_expires_at;
597
+ const until = held ? Date.parse(held) : Date.now() + ttl * 1000;
598
+ const wakeAt = Number.isFinite(deadline) ? Math.min(until, deadline) : until;
599
+ await sleep3(Math.max(250, wakeAt - Date.now()), controller.signal);
600
+ }
601
+ }
602
+ return false;
603
+ }
604
+ async function submitWithRetry(id, body) {
605
+ const base = await resolveBase();
606
+ for (let attempt = 0;; attempt++) {
607
+ try {
608
+ await t.request("POST", `${base}/client-tool-calls/${id}/result`, {
609
+ body,
610
+ raw: true
611
+ });
612
+ return;
613
+ } catch (err) {
614
+ const status = err instanceof AgentApiError ? err.status : 0;
615
+ if (status === 410)
616
+ return;
617
+ if (status >= 400 && status < 500)
618
+ throw err;
619
+ if (attempt >= SUBMIT_ATTEMPTS - 1 || controller.signal.aborted)
620
+ throw err;
621
+ options.onError?.(err);
622
+ await sleep3(Math.min(8000, 250 * 2 ** attempt), controller.signal);
623
+ }
624
+ }
625
+ }
626
+ async function guard(fn) {
627
+ try {
628
+ await fn();
629
+ } catch (err) {
630
+ if (!controller.signal.aborted)
631
+ options.onError?.(err);
632
+ }
633
+ }
634
+ function stopped() {
635
+ if (controller.signal.aborted)
636
+ return Promise.resolve();
637
+ return new Promise((resolve) => {
638
+ controller.signal.addEventListener("abort", () => resolve(), { once: true });
639
+ });
640
+ }
641
+ return {
642
+ stop,
643
+ poke: () => {
644
+ if (!controller.signal.aborted)
645
+ track(guard(drain));
646
+ },
647
+ done
648
+ };
649
+ }
650
+ function declarations(tools) {
651
+ return Object.entries(tools).map(([name, tool]) => ({
652
+ name,
653
+ description: tool.description ?? "",
654
+ input_schema: tool.inputSchema ?? { type: "object" },
655
+ output_schema: tool.outputSchema,
656
+ read_only_hint: tool.readOnlyHint,
657
+ destructive_hint: tool.destructiveHint,
658
+ idempotent_hint: tool.idempotentHint,
659
+ timeout_seconds: tool.timeoutSeconds
660
+ }));
661
+ }
662
+ function errorText(err) {
663
+ if (err instanceof Error)
664
+ return err.message;
665
+ return String(err);
666
+ }
667
+ function sleep3(ms, signal) {
668
+ return new Promise((resolve) => {
669
+ const onAbort = () => {
670
+ clearTimeout(timer);
671
+ resolve();
672
+ };
673
+ const timer = setTimeout(() => {
674
+ signal.removeEventListener("abort", onAbort);
675
+ resolve();
676
+ }, ms);
677
+ signal.addEventListener("abort", onAbort, { once: true });
678
+ });
679
+ }
680
+ function randomId() {
681
+ return Math.random().toString(36).slice(2, 10);
682
+ }
683
+
684
+ // src/admin/client-tools.ts
685
+ function clientToolsApi(t, tenantSlug) {
686
+ const base = (convId) => `${tenantPath(tenantSlug)}/conversations/${enc(convId)}`;
687
+ const tools = (convId) => `${base(convId)}/client-tools`;
688
+ const calls = (convId) => `${base(convId)}/client-tool-calls`;
689
+ return {
690
+ serve: (convId, options) => serveClientTools(t, async () => base(convId), convId, options),
691
+ list: (convId, signal) => t.request("GET", tools(convId), { signal }),
692
+ replace: (convId, input) => t.request("PUT", tools(convId), { body: input }),
693
+ listCalls: (convId, signal) => t.request("GET", calls(convId), { signal }),
694
+ claim: (convId, toolCallId, input) => t.request("POST", `${calls(convId)}/${enc(toolCallId)}/claim`, {
695
+ body: input
696
+ }),
697
+ releaseClaim: (convId, toolCallId, claimant) => t.request("DELETE", `${calls(convId)}/${enc(toolCallId)}/claim`, {
698
+ query: { claimant },
699
+ raw: true
700
+ }),
701
+ submitResult: (convId, toolCallId, input) => t.request("POST", `${calls(convId)}/${enc(toolCallId)}/result`, {
702
+ body: input,
703
+ raw: true
704
+ })
705
+ };
706
+ }
707
+
708
+ // src/admin/component-libraries.ts
709
+ function componentLibrariesApi(t, tenantSlug) {
710
+ const base = `${tenantPath(tenantSlug)}/component-libraries`;
711
+ const library = (slug) => `${base}/${enc(slug)}`;
712
+ return {
713
+ list: (signal) => t.request("GET", base, { signal }),
714
+ get: (slug, signal) => t.request("GET", library(slug), { signal }),
715
+ create: (input) => t.request("POST", base, { body: input }),
716
+ update: (currentSlug, input) => t.request("PATCH", library(currentSlug), {
717
+ body: withoutNoopRename(input, currentSlug)
718
+ }),
719
+ delete: (slug) => t.request("DELETE", library(slug), { raw: true })
720
+ };
721
+ }
722
+
723
+ // src/admin/conversations.ts
724
+ function conversationsAdminApi(t, tenantSlug) {
725
+ const base = `${tenantPath(tenantSlug)}/conversations`;
726
+ const conv = (id) => `${base}/${enc(id)}`;
727
+ const sendImages = (id, images, caption) => {
728
+ const form = new FormData;
729
+ for (const [i, entry] of images.entries()) {
730
+ form.append("file", entry.image, entry.filename ?? `image-${i + 1}.png`);
731
+ form.append("label", entry.label ?? "");
732
+ }
733
+ if (caption)
734
+ form.append("caption", caption);
735
+ return t.request("POST", `${conv(id)}/user_message/image`, {
736
+ body: form
737
+ });
738
+ };
739
+ return {
740
+ list: (query = {}, signal) => t.request("GET", base, { query, signal }),
741
+ get: (id, signal) => t.request("GET", conv(id), { signal }),
742
+ create: (input) => t.request("POST", base, { body: input }),
743
+ update: (id, input) => t.request("PATCH", conv(id), { body: input }),
744
+ archive: (id) => t.request("POST", `${conv(id)}/archive`),
745
+ retryNow: (id) => t.request("POST", `${conv(id)}/retry-now`),
746
+ listEvents: (id, query = {}, signal) => t.request("GET", `${conv(id)}/events`, { query, signal }),
747
+ sendUserMessage: (id, content) => t.request("POST", `${conv(id)}/user_message`, {
748
+ body: { content }
749
+ }),
750
+ sendUserAudio: (id, audio, filename = "recording.webm") => {
751
+ const form = new FormData;
752
+ form.append("audio", audio, filename);
753
+ return t.request("POST", `${conv(id)}/user_message/audio`, {
754
+ body: form
755
+ });
756
+ },
757
+ sendUserImage: (id, image, opts = {}) => sendImages(id, [{ image, filename: opts.filename, label: opts.label }], opts.caption),
758
+ sendUserImages: (id, images, opts = {}) => sendImages(id, images, opts.caption),
759
+ steer: (id, content) => t.request("POST", `${conv(id)}/steer`, {
760
+ body: { content }
761
+ }),
762
+ listSubConversations: (id, signal) => t.request("GET", `${conv(id)}/sub-conversations`, {
763
+ signal
764
+ }),
765
+ getSummary: (id, summaryId, signal) => t.request("GET", `${conv(id)}/summaries/${enc(summaryId)}`, { signal }),
766
+ listKnowledgeBases: (id, signal) => t.request("GET", `${conv(id)}/knowledge-bases`, {
767
+ signal
768
+ }),
769
+ attachKnowledgeBase: (id, kbSlug) => t.request("PUT", `${conv(id)}/knowledge-bases/${enc(kbSlug)}`, { raw: true }),
770
+ detachKnowledgeBase: (id, kbSlug) => t.request("DELETE", `${conv(id)}/knowledge-bases/${enc(kbSlug)}`, { raw: true }),
771
+ listSkills: (id, signal) => t.request("GET", `${conv(id)}/skills`, { signal }),
772
+ addSkill: (id, skillSlug) => t.request("PUT", `${conv(id)}/skills/${enc(skillSlug)}`, { raw: true }),
773
+ removeSkill: (id, skillSlug) => t.request("DELETE", `${conv(id)}/skills/${enc(skillSlug)}`, { raw: true }),
774
+ workspaceDir: (id, atSeq, path, signal) => t.request("GET", `${conv(id)}/workspace/dir`, {
775
+ query: { at_seq: atSeq, path },
776
+ signal
777
+ }),
778
+ workspaceFile: (id, query) => t.fetchRaw("GET", `${conv(id)}/workspace/file`, { query }),
779
+ workspaceWrite: (id, path, file, filename) => {
780
+ const form = new FormData;
781
+ form.append("file", file, filename ?? path.split("/").pop() ?? "upload");
782
+ return t.request("PUT", `${conv(id)}/workspace/file`, {
783
+ query: { path },
784
+ body: form
785
+ });
786
+ },
787
+ workspaceWriteMany: (id, files) => {
788
+ const form = new FormData;
789
+ for (const [i, entry] of files.entries()) {
790
+ form.append("file", entry.file, entry.filename ?? `file-${i + 1}`);
791
+ form.append("path", entry.path);
792
+ }
793
+ return t.request("POST", `${conv(id)}/workspace/files`, { body: form });
794
+ },
795
+ workspaceDelete: (id, path) => t.request("DELETE", `${conv(id)}/workspace/file`, {
796
+ query: { path }
797
+ }),
798
+ workspaceMove: (id, from, to) => t.request("POST", `${conv(id)}/workspace/move`, {
799
+ body: { from, to }
800
+ }),
801
+ eventAttachment: (id, eventId, attachmentId) => t.fetchRaw("GET", `${conv(id)}/events/${eventId}/attachment`, {
802
+ query: { attachment_id: attachmentId }
803
+ })
804
+ };
805
+ }
806
+
807
+ // src/admin/global.ts
808
+ function tenantsApi(t) {
809
+ return {
810
+ list: (signal) => t.request("GET", "/api/v1/tenants", { signal }),
811
+ create: (input) => t.request("POST", "/api/v1/tenants", { body: input }),
812
+ update: (currentSlug, input) => t.request("PATCH", `/api/v1/tenants/${enc(currentSlug)}`, {
813
+ body: withoutNoopRename(input, currentSlug)
814
+ }),
815
+ delete: (slug) => t.request("DELETE", `/api/v1/tenants/${enc(slug)}`, { raw: true })
816
+ };
817
+ }
818
+ function apiKeysApi(t) {
819
+ const base = "/api/v1/api_keys";
820
+ return {
821
+ list: (signal) => t.request("GET", base, { signal }),
822
+ get: (id, signal) => t.request("GET", `${base}/${enc(id)}`, { signal }),
823
+ create: (input) => t.request("POST", base, { body: input }),
824
+ update: (id, input) => t.request("PATCH", `${base}/${enc(id)}`, { body: input }),
825
+ delete: (id) => t.request("DELETE", `${base}/${enc(id)}`, { raw: true }),
826
+ rotate: (id) => t.request("POST", `${base}/${enc(id)}/rotate`),
827
+ addGrant: (id, input) => t.request("POST", `${base}/${enc(id)}/grants`, { body: input }),
828
+ removeGrant: (id, grantId) => t.request("DELETE", `${base}/${enc(id)}/grants/${enc(grantId)}`, { raw: true })
829
+ };
830
+ }
831
+ function sharedProvidersApi(t) {
832
+ const base = "/api/v1/ai-providers/shared";
833
+ return {
834
+ create: (input) => t.request("POST", base, { body: input }),
835
+ update: (currentSlug, input) => t.request("PATCH", `${base}/${enc(currentSlug)}`, {
836
+ body: withoutNoopRename(input, currentSlug)
837
+ }),
838
+ delete: (slug) => t.request("DELETE", `${base}/${enc(slug)}`, { raw: true }),
839
+ refreshModels: (slug) => t.request("POST", `${base}/${enc(slug)}/models/refresh`),
840
+ updateModel: (slug, modelId, input) => t.request("PATCH", `${base}/${enc(slug)}/models/${enc(modelId)}`, {
841
+ body: input,
842
+ raw: true
843
+ })
844
+ };
845
+ }
846
+
847
+ // src/admin/knowledge-bases.ts
848
+ function knowledgeBasesApi(t, tenantSlug) {
849
+ const base = `${tenantPath(tenantSlug)}/knowledge-bases`;
850
+ const kb = (slug) => `${base}/${enc(slug)}`;
851
+ return {
852
+ list: (signal) => t.request("GET", base, { signal }),
853
+ get: (slug, signal) => t.request("GET", kb(slug), { signal }),
854
+ create: (input) => t.request("POST", base, { body: input }),
855
+ update: (currentSlug, input) => t.request("PATCH", kb(currentSlug), {
856
+ body: withoutNoopRename(input, currentSlug)
857
+ }),
858
+ delete: (slug) => t.request("DELETE", kb(slug), { raw: true }),
859
+ requestCuration: (slug) => t.request("POST", `${kb(slug)}/curate`, { raw: true }),
860
+ listSources: (slug, signal) => t.request("GET", `${kb(slug)}/sources`, { signal }),
861
+ createTextSource: (slug, input) => t.request("POST", `${kb(slug)}/sources/text`, {
862
+ body: input
863
+ }),
864
+ uploadSource: (slug, file, title) => {
865
+ const form = new FormData;
866
+ form.append("file", file);
867
+ if (title?.trim())
868
+ form.append("title", title.trim());
869
+ return t.request("POST", `${kb(slug)}/sources`, {
870
+ body: form
871
+ });
872
+ },
873
+ retractSource: (slug, sourceId) => t.request("DELETE", `${kb(slug)}/sources/${enc(sourceId)}`, { raw: true }),
874
+ downloadSource: (slug, sourceId) => t.fetchRaw("GET", `${kb(slug)}/sources/${enc(sourceId)}/content`),
875
+ listPages: (slug, signal) => t.request("GET", `${kb(slug)}/pages`, { signal }),
876
+ getPage: (slug, pageSlug, signal) => t.request("GET", `${kb(slug)}/pages/${enc(pageSlug)}`, {
877
+ signal
878
+ }),
879
+ updatePage: (slug, pageSlug, input) => t.request("PUT", `${kb(slug)}/pages/${enc(pageSlug)}`, {
880
+ body: input
881
+ })
882
+ };
883
+ }
884
+
885
+ // src/admin/mcps.ts
886
+ function mcpsApi(t, tenantSlug) {
887
+ const base = `${tenantPath(tenantSlug)}/mcps`;
888
+ const mcp = (slug) => `${base}/${enc(slug)}`;
889
+ return {
890
+ list: (signal) => t.request("GET", base, { signal }),
891
+ get: (slug, signal) => t.request("GET", mcp(slug), { signal }),
892
+ create: (input) => t.request("POST", base, { body: input }),
893
+ update: (currentSlug, input) => t.request("PATCH", mcp(currentSlug), {
894
+ body: withoutNoopRename(input, currentSlug)
895
+ }),
896
+ delete: (slug) => t.request("DELETE", mcp(slug), { raw: true }),
897
+ refresh: (slug) => t.request("POST", `${mcp(slug)}/refresh`),
898
+ updateTool: (slug, toolName, enabled) => t.request("PATCH", `${mcp(slug)}/tools/${enc(toolName)}`, {
899
+ body: { enabled }
900
+ }),
901
+ getSkill: (slug, skillSlug, signal) => t.request("GET", `${mcp(slug)}/skills/${enc(skillSlug)}`, { signal }),
902
+ createSkill: (slug, input) => t.request("POST", `${mcp(slug)}/skills`, { body: input }),
903
+ updateSkill: (slug, currentSkillSlug, input) => t.request("PATCH", `${mcp(slug)}/skills/${enc(currentSkillSlug)}`, {
904
+ body: withoutNoopRename(input, currentSkillSlug)
905
+ }),
906
+ deleteSkill: (slug, skillSlug) => t.request("DELETE", `${mcp(slug)}/skills/${enc(skillSlug)}`, { raw: true })
907
+ };
908
+ }
909
+
910
+ // src/admin/skills.ts
911
+ function skillsApi(t, tenantSlug) {
912
+ const base = `${tenantPath(tenantSlug)}/skills`;
913
+ const skill = (slug) => `${base}/${enc(slug)}`;
914
+ return {
915
+ list: (signal) => t.request("GET", base, { signal }),
916
+ get: (slug, signal) => t.request("GET", skill(slug), { signal }),
917
+ create: (input) => t.request("POST", base, { body: input }),
918
+ update: (currentSlug, input) => t.request("PATCH", skill(currentSlug), {
919
+ body: withoutNoopRename(input, currentSlug)
920
+ }),
921
+ delete: (slug) => t.request("DELETE", skill(slug), { raw: true })
922
+ };
923
+ }
924
+
925
+ // src/admin/task-templates.ts
926
+ function taskTemplatesApi(t, tenantSlug) {
927
+ const base = `${tenantPath(tenantSlug)}/task-templates`;
928
+ const template = (slug) => `${base}/${enc(slug)}`;
929
+ return {
930
+ list: (signal) => t.request("GET", base, { signal }),
931
+ get: (slug, signal) => t.request("GET", template(slug), { signal }),
932
+ create: (input) => t.request("POST", base, { body: input }),
933
+ update: (currentSlug, input) => t.request("PATCH", template(currentSlug), {
934
+ body: withoutNoopRename(input, currentSlug)
935
+ }),
936
+ delete: (slug) => t.request("DELETE", template(slug), { raw: true }),
937
+ createSecret: (slug) => t.request("POST", `${template(slug)}/secret`),
938
+ revokeSecret: (slug) => t.request("DELETE", `${template(slug)}/secret`, { raw: true }),
939
+ trigger: (slug, input) => t.fetchRaw("POST", `${template(slug)}/trigger`, { body: input })
940
+ };
941
+ }
942
+ function scheduledRunsApi(t, tenantSlug) {
943
+ const base = `${tenantPath(tenantSlug)}/scheduled-runs`;
944
+ return {
945
+ list: (query = {}, signal) => t.request("GET", base, { query, signal }),
946
+ get: (id, signal) => t.request("GET", `${base}/${enc(id)}`, { signal }),
947
+ update: (id, input) => t.request("PATCH", `${base}/${enc(id)}`, { body: input }),
948
+ cancel: (id) => t.request("DELETE", `${base}/${enc(id)}`, { raw: true })
949
+ };
950
+ }
951
+ function backgroundTasksApi(t, tenantSlug) {
952
+ return {
953
+ spawn: (input) => t.request("POST", `${tenantPath(tenantSlug)}/background-tasks`, { body: input })
954
+ };
955
+ }
956
+
957
+ // src/admin/users.ts
958
+ function usersApi(t, tenantSlug) {
959
+ const base = `${tenantPath(tenantSlug)}/users`;
960
+ const user = (id) => `${base}/${enc(id)}`;
961
+ return {
962
+ list: (query = {}, signal) => t.request("GET", base, { query, signal }),
963
+ get: (id, signal) => t.request("GET", user(id), { signal }),
964
+ create: (input) => t.request("POST", base, { body: input }),
965
+ update: (id, input) => t.request("PATCH", user(id), { body: input }),
966
+ delete: (id) => t.request("DELETE", user(id), { raw: true }),
967
+ approve: (id, input) => t.request("POST", `${user(id)}/approve`, { body: input }),
968
+ block: (id) => t.request("POST", `${user(id)}/block`),
969
+ unblock: (id) => t.request("POST", `${user(id)}/unblock`),
970
+ merge: (id, input) => t.request("POST", `${user(id)}/merge`, { body: input }),
971
+ attachRole: (id, roleSlug) => t.request("PUT", `${user(id)}/roles/${enc(roleSlug)}`, { raw: true }),
972
+ detachRole: (id, roleSlug) => t.request("DELETE", `${user(id)}/roles/${enc(roleSlug)}`, { raw: true }),
973
+ addIdentity: (id, input) => t.request("POST", `${user(id)}/identities`, { body: input }),
974
+ deleteIdentity: (id, identityId) => t.request("DELETE", `${user(id)}/identities/${enc(identityId)}`, { raw: true }),
975
+ listKnowledgeBases: (id, signal) => t.request("GET", `${user(id)}/knowledge-bases`, {
976
+ signal
977
+ }),
978
+ attachKnowledgeBase: (id, kbSlug) => t.request("PUT", `${user(id)}/knowledge-bases/${enc(kbSlug)}`, { raw: true }),
979
+ detachKnowledgeBase: (id, kbSlug) => t.request("DELETE", `${user(id)}/knowledge-bases/${enc(kbSlug)}`, { raw: true }),
980
+ getMemory: (id, signal) => t.request("GET", `${user(id)}/memory`, { signal }),
981
+ listMemoryObservations: (id, signal) => t.request("GET", `${user(id)}/memory/observations`, {
982
+ signal
983
+ }),
984
+ createMemoryObservation: (id, input) => t.request("POST", `${user(id)}/memory/observations`, {
985
+ body: input
986
+ }),
987
+ deleteMemoryObservation: (id, observationId) => t.request("DELETE", `${user(id)}/memory/observations/${enc(observationId)}`, {
988
+ raw: true
989
+ }),
990
+ updateMemoryProfile: (id, input) => t.request("PATCH", `${user(id)}/memory/profile`, {
991
+ body: input
992
+ })
993
+ };
994
+ }
995
+ function userRolesApi(t, tenantSlug) {
996
+ const base = `${tenantPath(tenantSlug)}/user-roles`;
997
+ const role = (slug) => `${base}/${enc(slug)}`;
998
+ return {
999
+ list: (signal) => t.request("GET", base, { signal }),
1000
+ get: (slug, signal) => t.request("GET", role(slug), { signal }),
1001
+ create: (input) => t.request("POST", base, { body: input }),
1002
+ update: (slug, input) => t.request("PATCH", role(slug), { body: input }),
1003
+ delete: (slug) => t.request("DELETE", role(slug), { raw: true })
1004
+ };
1005
+ }
1006
+ function userTokensApi(t, tenantSlug) {
1007
+ return {
1008
+ create: (input) => t.request("POST", `${tenantPath(tenantSlug)}/user-tokens`, {
1009
+ body: input
1010
+ })
1011
+ };
1012
+ }
1013
+
1014
+ // src/admin/index.ts
1015
+ function tenantScope(t, slug) {
1016
+ return {
1017
+ slug,
1018
+ agents: agentsApi(t, slug),
1019
+ aiProviders: aiProvidersApi(t, slug),
1020
+ mcps: mcpsApi(t, slug),
1021
+ skills: skillsApi(t, slug),
1022
+ componentLibraries: componentLibrariesApi(t, slug),
1023
+ channels: channelsApi(t, slug),
1024
+ users: usersApi(t, slug),
1025
+ userRoles: userRolesApi(t, slug),
1026
+ userTokens: userTokensApi(t, slug),
1027
+ knowledgeBases: knowledgeBasesApi(t, slug),
1028
+ taskTemplates: taskTemplatesApi(t, slug),
1029
+ scheduledRuns: scheduledRunsApi(t, slug),
1030
+ backgroundTasks: backgroundTasksApi(t, slug),
1031
+ conversations: conversationsAdminApi(t, slug),
1032
+ clientTools: clientToolsApi(t, slug)
1033
+ };
1034
+ }
1035
+ function createAdminClient(options) {
1036
+ const t = createTransport(options.baseUrl, { apiKey: options.apiKey }, options.fetch, options.timeoutMs, options.maxRetries);
1037
+ const scopes = new Map;
1038
+ return {
1039
+ raw: t,
1040
+ health: (signal) => t.request("GET", "/api/v1/health", { signal }),
1041
+ me: (signal) => t.request("GET", "/api/v1/me", { signal }),
1042
+ tenants: tenantsApi(t),
1043
+ apiKeys: apiKeysApi(t),
1044
+ sharedProviders: sharedProvidersApi(t),
1045
+ tenant(slug) {
1046
+ let scope = scopes.get(slug);
1047
+ if (!scope) {
1048
+ scope = tenantScope(t, slug);
1049
+ scopes.set(slug, scope);
1050
+ }
1051
+ return scope;
1052
+ }
1053
+ };
1054
+ }
1055
+ // src/cache.ts
1056
+ var CACHE_VERSION = 3;
1057
+
1058
+ class MemoryConversationCache {
1059
+ #entries = new Map;
1060
+ #max;
1061
+ constructor(options = {}) {
1062
+ this.#max = Math.max(1, options.maxConversations ?? 20);
1063
+ }
1064
+ read(key) {
1065
+ const entry = this.#entries.get(key);
1066
+ if (entry === undefined)
1067
+ return Promise.resolve(null);
1068
+ this.#entries.delete(key);
1069
+ this.#entries.set(key, entry);
1070
+ return Promise.resolve(entry);
1071
+ }
1072
+ write(key, entry) {
1073
+ this.#entries.delete(key);
1074
+ this.#entries.set(key, entry);
1075
+ while (this.#entries.size > this.#max) {
1076
+ const oldest = this.#entries.keys().next();
1077
+ if (oldest.done)
1078
+ break;
1079
+ this.#entries.delete(oldest.value);
1080
+ }
1081
+ return Promise.resolve();
1082
+ }
1083
+ clear(key) {
1084
+ if (key === undefined)
1085
+ this.#entries.clear();
1086
+ else
1087
+ this.#entries.delete(key);
1088
+ return Promise.resolve();
1089
+ }
1090
+ get size() {
1091
+ return this.#entries.size;
1092
+ }
1093
+ }
1094
+ function trimCached(entry, limit) {
1095
+ if (entry.messages.length <= limit)
1096
+ return entry;
1097
+ const messages = entry.messages.slice(entry.messages.length - limit);
1098
+ const head = messages[0];
1099
+ const oldestSeq = head ? head.seq : entry.oldestSeq;
1100
+ return {
1101
+ ...entry,
1102
+ messages,
1103
+ ...entry.toolActivity === undefined ? {} : {
1104
+ toolActivity: entry.toolActivity.filter((a) => oldestSeq === null || a.seq >= oldestSeq)
1105
+ },
1106
+ ...entry.plans === undefined ? {} : { plans: entry.plans.filter((p) => oldestSeq === null || p.seq >= oldestSeq) },
1107
+ oldestSeq,
1108
+ hasOlder: true
1109
+ };
1110
+ }
1111
+ // src/mapping.ts
1112
+ var TRANSCRIPTION = Symbol("cubos.transcription");
1113
+ var SOURCE_EVENT = Symbol("cubos.sourceEvent");
1114
+ function toConversation(w) {
1115
+ return {
1116
+ id: w.id,
1117
+ title: w.title ?? w.generated_title ?? null,
1118
+ titleIsGenerated: w.title === null && w.generated_title !== null,
1119
+ agentSlug: w.agent?.slug ?? null,
1120
+ lastActivityAt: w.last_activity_at,
1121
+ archived: w.archived_at !== null,
1122
+ isProcessing: w.is_processing,
1123
+ hasPendingTurn: w.has_pending_turn,
1124
+ createdAt: w.created_at,
1125
+ updatedAt: w.updated_at
1126
+ };
1127
+ }
1128
+ function toTurnStatus(w) {
1129
+ return { isProcessing: w.is_processing, hasPendingTurn: w.has_pending_turn };
1130
+ }
1131
+ function toMessage(w) {
1132
+ if (w.tentative || w.discarded_at !== null)
1133
+ return null;
1134
+ if (w.type === "media_transcription" && w.content !== null) {
1135
+ const message = {
1136
+ id: w.id,
1137
+ role: "user",
1138
+ content: w.content,
1139
+ attachments: [],
1140
+ seq: w.seq,
1141
+ at: w.created_at
1142
+ };
1143
+ const hidden = message;
1144
+ hidden[TRANSCRIPTION] = true;
1145
+ hidden[SOURCE_EVENT] = sourceEventId(w.result);
1146
+ return message;
1147
+ }
1148
+ const role = w.type === "user_message" ? "user" : w.type === "agent_message" ? "agent" : null;
1149
+ if (role === null || w.content === null)
1150
+ return null;
1151
+ const attachments = toAttachments(w);
1152
+ if (role === "user" && w.content === "" && attachments.length === 0)
1153
+ return null;
1154
+ return {
1155
+ id: w.id,
1156
+ role,
1157
+ content: w.content,
1158
+ attachments,
1159
+ seq: w.seq,
1160
+ at: w.created_at,
1161
+ ...w.blocks === undefined ? {} : { blocks: w.blocks }
1162
+ };
1163
+ }
1164
+ function toAttachments(w) {
1165
+ const out = [];
1166
+ for (const a of w.attachments ?? []) {
1167
+ if (a.kind !== "image" && a.kind !== "audio")
1168
+ continue;
1169
+ out.push({
1170
+ id: a.id,
1171
+ kind: a.kind,
1172
+ mimeType: a.mime_type,
1173
+ bytes: a.bytes,
1174
+ label: a.label
1175
+ });
1176
+ }
1177
+ return out;
1178
+ }
1179
+ function hasAudio(message) {
1180
+ return message.attachments.some((a) => a.kind === "audio");
1181
+ }
1182
+ function sourceEventId(result) {
1183
+ const id = result?.source_event_id;
1184
+ return typeof id === "string" ? id : null;
1185
+ }
1186
+ function mergeVoiceMessages(messages) {
1187
+ const out = [];
1188
+ const awaitingText = [];
1189
+ const byId = new Map;
1190
+ for (const message of messages) {
1191
+ const hidden = message;
1192
+ if (hidden[TRANSCRIPTION] !== true) {
1193
+ const copy = { ...message };
1194
+ byId.set(copy.id, copy);
1195
+ if (hasAudio(copy) && copy.transcribed !== true) {
1196
+ copy.transcribed = false;
1197
+ awaitingText.push(copy);
1198
+ }
1199
+ out.push(copy);
1200
+ continue;
1201
+ }
1202
+ const sourceId = hidden[SOURCE_EVENT];
1203
+ const source = typeof sourceId === "string" ? byId.get(sourceId) : undefined;
1204
+ if (source) {
1205
+ if (hasAudio(source)) {
1206
+ source.content = message.content;
1207
+ source.transcribed = true;
1208
+ }
1209
+ continue;
1210
+ }
1211
+ const clip = awaitingText.shift();
1212
+ if (clip) {
1213
+ clip.content = message.content;
1214
+ clip.transcribed = true;
1215
+ continue;
1216
+ }
1217
+ out.push(message);
1218
+ }
1219
+ return out;
1220
+ }
1221
+ var TODO_STATUS = {
1222
+ pending: "pending",
1223
+ in_progress: "in_progress",
1224
+ done: "completed",
1225
+ cancelled: "completed"
1226
+ };
1227
+ function toTodos(w) {
1228
+ if (w.type !== "todo_update" || w.tentative || w.discarded_at !== null)
1229
+ return null;
1230
+ if (w.content === null)
1231
+ return null;
1232
+ let payload;
1233
+ try {
1234
+ payload = JSON.parse(w.content);
1235
+ } catch {
1236
+ return null;
1237
+ }
1238
+ const raw = payload?.items;
1239
+ if (!Array.isArray(raw))
1240
+ return null;
1241
+ const todos = [];
1242
+ for (const item of raw) {
1243
+ if (typeof item !== "object" || item === null)
1244
+ continue;
1245
+ const { text, status } = item;
1246
+ if (typeof text !== "string")
1247
+ continue;
1248
+ todos.push({
1249
+ title: text,
1250
+ status: (typeof status === "string" ? TODO_STATUS[status] : undefined) ?? "pending"
1251
+ });
1252
+ }
1253
+ return todos;
1254
+ }
1255
+ function toPlanSnapshot(w) {
1256
+ const todos = toTodos(w);
1257
+ return todos === null ? null : { todos, seq: w.seq };
1258
+ }
1259
+ function toTurnDone(w) {
1260
+ if (w.type !== "turn_done" || w.tentative || w.discarded_at !== null)
1261
+ return null;
1262
+ return w.seq;
1263
+ }
1264
+ function toClientToolCall(w) {
1265
+ if (w.type !== "client_tool_call" || w.tentative || w.discarded_at !== null)
1266
+ return null;
1267
+ if (!w.tool_call_id || !w.tool_name)
1268
+ return null;
1269
+ return { toolCallId: w.tool_call_id, toolName: w.tool_name };
1270
+ }
1271
+ function toToolActivity(w) {
1272
+ if (w.discarded_at !== null)
1273
+ return null;
1274
+ if (!w.tool_call_id || !w.tool_name)
1275
+ return null;
1276
+ if (w.type === "client_tool_call") {
1277
+ const args = toArguments(w.content);
1278
+ return {
1279
+ toolCallId: w.tool_call_id,
1280
+ toolName: w.tool_name,
1281
+ ...args === undefined ? {} : { arguments: args },
1282
+ status: "running",
1283
+ seq: w.seq,
1284
+ at: w.created_at
1285
+ };
1286
+ }
1287
+ if (w.type === "tool_result") {
1288
+ const failed = typeof w.error === "string" && w.error !== "";
1289
+ return {
1290
+ toolCallId: w.tool_call_id,
1291
+ toolName: w.tool_name,
1292
+ status: failed ? "error" : "ok",
1293
+ ...w.result === undefined || w.result === null ? {} : { result: w.result },
1294
+ ...failed ? { error: w.error } : {},
1295
+ ...typeof w.duration_ms === "number" ? { durationMs: w.duration_ms } : {},
1296
+ seq: w.seq,
1297
+ at: w.created_at
1298
+ };
1299
+ }
1300
+ return null;
1301
+ }
1302
+ function toArguments(content) {
1303
+ if (content === null || content === "")
1304
+ return;
1305
+ try {
1306
+ const parsed = JSON.parse(content);
1307
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
1308
+ return;
1309
+ return parsed;
1310
+ } catch {
1311
+ return;
1312
+ }
1313
+ }
1314
+ function toToolCallArguments(w) {
1315
+ if (w.type !== "llm_call" || w.discarded_at !== null)
1316
+ return [];
1317
+ const messages = w.llm_call_data?.response?.messages;
1318
+ if (!Array.isArray(messages))
1319
+ return [];
1320
+ const out = [];
1321
+ for (const m of messages) {
1322
+ if (m.type !== "tool_call" || typeof m.tool_call_id !== "string")
1323
+ continue;
1324
+ const args = m.arguments;
1325
+ if (typeof args !== "object" || args === null || Array.isArray(args))
1326
+ continue;
1327
+ out.push({ toolCallId: m.tool_call_id, arguments: args });
1328
+ }
1329
+ return out;
1330
+ }
1331
+ function withToolArguments(activity, known) {
1332
+ if (activity.arguments !== undefined)
1333
+ return activity;
1334
+ const args = known.get(activity.toolCallId);
1335
+ return args === undefined ? activity : { ...activity, arguments: args };
1336
+ }
1337
+ function mergeToolActivities(activities) {
1338
+ const byId = new Map;
1339
+ for (const incoming of activities) {
1340
+ const held = byId.get(incoming.toolCallId);
1341
+ byId.set(incoming.toolCallId, held === undefined ? incoming : fold(held, incoming));
1342
+ }
1343
+ return [...byId.values()].sort((a, b) => a.seq - b.seq);
1344
+ }
1345
+ function fold(a, b) {
1346
+ const withArgs = a.arguments !== undefined ? a : b;
1347
+ const outcome = a.status !== "running" ? a : b.status !== "running" ? b : null;
1348
+ const earliest = a.seq <= b.seq ? a : b;
1349
+ return {
1350
+ toolCallId: a.toolCallId,
1351
+ toolName: a.toolName,
1352
+ ...withArgs.arguments === undefined ? {} : { arguments: withArgs.arguments },
1353
+ status: outcome?.status ?? "running",
1354
+ ...outcome?.result === undefined ? {} : { result: outcome.result },
1355
+ ...outcome?.error === undefined ? {} : { error: outcome.error },
1356
+ ...outcome?.durationMs === undefined ? {} : { durationMs: outcome.durationMs },
1357
+ seq: earliest.seq,
1358
+ at: earliest.at
1359
+ };
1360
+ }
1361
+
1362
+ // src/client.ts
1363
+ var MAX_IMAGES_PER_MESSAGE = 10;
1364
+
1365
+ class AgentClient {
1366
+ #transport;
1367
+ #cache;
1368
+ #cacheLimit;
1369
+ #tenant;
1370
+ #identity = null;
1371
+ constructor(options) {
1372
+ const getToken = "getToken" in options ? options.getToken : () => options.token;
1373
+ this.#transport = createTransport(options.baseUrl, { getToken }, options.fetch, options.timeoutMs, options.maxRetries);
1374
+ this.#tenant = options.tenant;
1375
+ this.#cache = options.cache === undefined ? new MemoryConversationCache : options.cache;
1376
+ this.#cacheLimit = Math.max(1, options.cacheMessageLimit ?? 300);
1377
+ }
1378
+ me() {
1379
+ this.#identity ??= this.#fetchIdentity();
1380
+ return this.#identity;
1381
+ }
1382
+ refreshIdentity() {
1383
+ this.#identity = this.#fetchIdentity();
1384
+ return this.#identity;
1385
+ }
1386
+ async#fetchIdentity() {
1387
+ const raw = await this.#transport.request("GET", "/api/v1/me");
1388
+ if (raw.user_id === null) {
1389
+ throw new AgentApiError("This token is an api_key, not an end-user token. Mint one with POST /api/v1/tenants/{slug}/user-tokens and keep the api_key on your server.", 403);
1390
+ }
1391
+ const tenantSlug = this.#tenant ?? raw.tenants[0]?.slug;
1392
+ if (!tenantSlug) {
1393
+ throw new AgentApiError("Token resolves to no tenant.", 403);
1394
+ }
1395
+ this.#tenant = tenantSlug;
1396
+ return {
1397
+ userId: raw.user_id,
1398
+ displayName: raw.display_name,
1399
+ tenantSlug,
1400
+ agentSlugs: raw.agent_slugs
1401
+ };
1402
+ }
1403
+ async#base() {
1404
+ if (this.#tenant === undefined)
1405
+ await this.me();
1406
+ return `/api/v1/tenants/${encodeURIComponent(this.#tenant)}/conversations`;
1407
+ }
1408
+ async listConversations(opts = {}) {
1409
+ const query = { origin: "interactive", limit: opts.limit, before: opts.before };
1410
+ const rows = await this.#transport.request("GET", `${await this.#base()}`, {
1411
+ query,
1412
+ signal: opts.signal
1413
+ });
1414
+ const items = rows.map(toConversation);
1415
+ const last = rows.at(-1);
1416
+ const exhausted = opts.limit !== undefined && rows.length < opts.limit;
1417
+ return {
1418
+ items,
1419
+ nextCursor: last && !exhausted ? `${last.last_activity_at}|${last.id}` : null
1420
+ };
1421
+ }
1422
+ async* iterateConversations(opts = {}) {
1423
+ const limit = opts.pageSize ?? 50;
1424
+ let before;
1425
+ for (;; ) {
1426
+ const page = await this.listConversations({ limit, before, signal: opts.signal });
1427
+ for (const conversation of page.items)
1428
+ yield conversation;
1429
+ if (page.nextCursor === null)
1430
+ return;
1431
+ before = page.nextCursor;
1432
+ }
1433
+ }
1434
+ async* iterateMessages(id, opts = {}) {
1435
+ const limit = opts.pageSize ?? 50;
1436
+ let before;
1437
+ for (;; ) {
1438
+ const page = await this.#messagePage(id, { limit, before, signal: opts.signal });
1439
+ for (let i = page.messages.length - 1;i >= 0; i--)
1440
+ yield page.messages[i];
1441
+ if (page.eventCount < limit || page.oldestSeq === null)
1442
+ return;
1443
+ before = page.oldestSeq;
1444
+ }
1445
+ }
1446
+ async createConversation(opts = {}) {
1447
+ let agentSlug = opts.agentSlug;
1448
+ if (agentSlug === undefined) {
1449
+ const { agentSlugs } = await this.me();
1450
+ if (agentSlugs.length !== 1) {
1451
+ throw new AgentApiError(agentSlugs.length === 0 ? "This token was minted without any agent, so it can't start a conversation." : `This token covers ${agentSlugs.length} agents — pass agentSlug to pick one.`, 400);
1452
+ }
1453
+ agentSlug = agentSlugs[0];
1454
+ }
1455
+ const row = await this.#transport.request("POST", await this.#base(), {
1456
+ body: {
1457
+ agent_slug: agentSlug,
1458
+ title: opts.title,
1459
+ metadata: opts.metadata,
1460
+ ...opts.componentLibraries === undefined ? {} : { component_libraries: opts.componentLibraries }
1461
+ },
1462
+ signal: opts.signal
1463
+ });
1464
+ return toConversation(row);
1465
+ }
1466
+ async setComponentLibraries(id, libraries, signal) {
1467
+ return await this.#transport.request("PUT", `${await this.#base()}/${id}/component-libraries`, { body: { libraries }, signal });
1468
+ }
1469
+ async setContext(id, context, signal) {
1470
+ await this.#transport.request("PUT", `${await this.#base()}/${id}/context`, {
1471
+ body: { context },
1472
+ signal
1473
+ });
1474
+ }
1475
+ async listComponentLibraries(id, signal) {
1476
+ return await this.#transport.request("GET", `${await this.#base()}/${id}/component-libraries`, { signal });
1477
+ }
1478
+ async getConversation(id, signal) {
1479
+ const row = await this.#transport.request("GET", `${await this.#base()}/${id}`, { signal });
1480
+ return toConversation(row);
1481
+ }
1482
+ async renameConversation(id, title, signal) {
1483
+ const row = await this.#transport.request("PATCH", `${await this.#base()}/${id}`, { body: { title }, signal });
1484
+ return toConversation(row);
1485
+ }
1486
+ async archiveConversation(id, signal) {
1487
+ const row = await this.#transport.request("POST", `${await this.#base()}/${id}/archive`, { signal });
1488
+ return toConversation(row);
1489
+ }
1490
+ async#messagePage(id, opts) {
1491
+ const rows = await this.#transport.request("GET", `${await this.#base()}/${id}/events`, { query: { before: opts.before, limit: opts.limit }, signal: opts.signal });
1492
+ const messages = mergeVoiceMessages(rows.map(toMessage).filter((m) => m !== null));
1493
+ const argsByCall = new Map;
1494
+ for (const row of rows) {
1495
+ for (const call of toToolCallArguments(row))
1496
+ argsByCall.set(call.toolCallId, call.arguments);
1497
+ }
1498
+ const toolActivity = mergeToolActivities(rows.map(toToolActivity).filter((a) => a !== null)).map((a) => withToolArguments(a, argsByCall));
1499
+ const plans = rows.map(toPlanSnapshot).filter((p) => p !== null);
1500
+ const turnDones = rows.map(toTurnDone).filter((n) => n !== null);
1501
+ const lastTurnDoneSeq = turnDones.length === 0 ? null : Math.max(...turnDones);
1502
+ let oldestSeq = null;
1503
+ let latestChangeSeq = null;
1504
+ for (const row of rows) {
1505
+ if (oldestSeq === null || row.seq < oldestSeq)
1506
+ oldestSeq = row.seq;
1507
+ if (latestChangeSeq === null || row.change_seq > latestChangeSeq) {
1508
+ latestChangeSeq = row.change_seq;
1509
+ }
1510
+ }
1511
+ return {
1512
+ messages,
1513
+ toolActivity,
1514
+ plans,
1515
+ lastTurnDoneSeq,
1516
+ oldestSeq,
1517
+ latestChangeSeq,
1518
+ eventCount: rows.length,
1519
+ events: rows
1520
+ };
1521
+ }
1522
+ async listMessages(id, opts = {}) {
1523
+ return (await this.#messagePage(id, opts)).messages;
1524
+ }
1525
+ async loadHistory(id, opts = {}) {
1526
+ const key = await this.#cacheKey(id);
1527
+ if (key !== null && this.#cache) {
1528
+ try {
1529
+ const entry = await this.#cache.read(key);
1530
+ if (entry && entry.version === CACHE_VERSION && entry.messages.length > 0) {
1531
+ return {
1532
+ messages: entry.messages,
1533
+ toolActivity: entry.toolActivity ?? [],
1534
+ plans: entry.plans ?? [],
1535
+ lastTurnDoneSeq: entry.lastTurnDoneSeq ?? null,
1536
+ oldestSeq: entry.oldestSeq,
1537
+ latestChangeSeq: entry.latestChangeSeq,
1538
+ hasOlder: entry.hasOlder,
1539
+ fromCache: true
1540
+ };
1541
+ }
1542
+ } catch {}
1543
+ }
1544
+ const page = await this.listMessagesPage(id, {
1545
+ limit: opts.pageSize,
1546
+ signal: opts.signal
1547
+ });
1548
+ await this.saveHistory(id, page);
1549
+ return { ...page, fromCache: false };
1550
+ }
1551
+ async saveHistory(id, state) {
1552
+ if (!this.#cache || state.latestChangeSeq === null || state.messages.length === 0)
1553
+ return;
1554
+ const key = await this.#cacheKey(id);
1555
+ if (key === null)
1556
+ return;
1557
+ const entry = trimCached({
1558
+ version: CACHE_VERSION,
1559
+ messages: state.messages,
1560
+ ...state.toolActivity === undefined ? {} : { toolActivity: state.toolActivity },
1561
+ ...state.plans === undefined ? {} : { plans: state.plans },
1562
+ ...state.lastTurnDoneSeq === undefined ? {} : { lastTurnDoneSeq: state.lastTurnDoneSeq },
1563
+ latestChangeSeq: state.latestChangeSeq,
1564
+ oldestSeq: state.oldestSeq,
1565
+ hasOlder: state.hasOlder
1566
+ }, this.#cacheLimit);
1567
+ try {
1568
+ await this.#cache.write(key, entry);
1569
+ } catch {}
1570
+ }
1571
+ async forgetHistory(id) {
1572
+ if (!this.#cache)
1573
+ return;
1574
+ if (id === undefined) {
1575
+ await this.#cache.clear();
1576
+ return;
1577
+ }
1578
+ const key = await this.#cacheKey(id);
1579
+ if (key !== null)
1580
+ await this.#cache.clear(key);
1581
+ }
1582
+ async#cacheKey(id) {
1583
+ if (!this.#cache)
1584
+ return null;
1585
+ try {
1586
+ const identity = await this.me();
1587
+ return `${identity.tenantSlug}\x00${identity.userId}\x00${id}`;
1588
+ } catch {
1589
+ return null;
1590
+ }
1591
+ }
1592
+ async listEventsPage(id, opts = {}) {
1593
+ const limit = opts.limit ?? 50;
1594
+ const page = await this.#messagePage(id, { ...opts, limit });
1595
+ return {
1596
+ events: page.events,
1597
+ oldestSeq: page.oldestSeq,
1598
+ latestChangeSeq: page.latestChangeSeq,
1599
+ hasOlder: page.eventCount === limit
1600
+ };
1601
+ }
1602
+ async listMessagesPage(id, opts = {}) {
1603
+ const limit = opts.limit ?? 50;
1604
+ const page = await this.#messagePage(id, { ...opts, limit });
1605
+ return {
1606
+ messages: page.messages,
1607
+ toolActivity: page.toolActivity,
1608
+ plans: page.plans,
1609
+ lastTurnDoneSeq: page.lastTurnDoneSeq,
1610
+ oldestSeq: page.oldestSeq,
1611
+ latestChangeSeq: page.latestChangeSeq,
1612
+ hasOlder: page.eventCount >= limit
1613
+ };
1614
+ }
1615
+ async sendMessage(id, content, signal) {
1616
+ await this.#transport.request("POST", `${await this.#base()}/${id}/user_message`, {
1617
+ body: { content },
1618
+ signal
1619
+ });
1620
+ }
1621
+ async sendImage(id, image, opts = {}) {
1622
+ await this.sendImages(id, [{ image, filename: opts.filename, label: opts.label }], {
1623
+ caption: opts.caption,
1624
+ signal: opts.signal
1625
+ });
1626
+ }
1627
+ async sendImages(id, images, opts = {}) {
1628
+ if (images.length === 0) {
1629
+ throw new AgentApiError("sendImages needs at least one image.", 400);
1630
+ }
1631
+ if (images.length > MAX_IMAGES_PER_MESSAGE) {
1632
+ throw new AgentApiError(`A message carries at most ${MAX_IMAGES_PER_MESSAGE} images (got ${images.length}).`, 400);
1633
+ }
1634
+ const form = new FormData;
1635
+ for (const [i, entry] of images.entries()) {
1636
+ form.append("file", entry.image, entry.filename ?? `image-${i + 1}.png`);
1637
+ form.append("label", entry.label ?? "");
1638
+ }
1639
+ if (opts.caption)
1640
+ form.append("caption", opts.caption);
1641
+ await this.#transport.request("POST", `${await this.#base()}/${id}/user_message/image`, { body: form, signal: opts.signal });
1642
+ }
1643
+ async sendAudio(id, audio, opts = {}) {
1644
+ const form = new FormData;
1645
+ form.append("audio", audio, opts.filename ?? "recording.webm");
1646
+ await this.#transport.request("POST", `${await this.#base()}/${id}/user_message/audio`, { body: form, signal: opts.signal });
1647
+ }
1648
+ async fetchAttachment(conversationId, messageId, attachmentId, signal) {
1649
+ const res = await this.#transport.fetchRaw("GET", `${await this.#base()}/${conversationId}/events/${messageId}/attachment`, { query: { attachment_id: attachmentId }, signal });
1650
+ await raiseForStatus(res, "Could not fetch the attachment.");
1651
+ return await res.blob();
1652
+ }
1653
+ async listFiles(id, opts = {}) {
1654
+ const raw = await this.#transport.request("GET", `${await this.#base()}/${id}/workspace/dir`, {
1655
+ query: { path: opts.path, at_seq: opts.atSeq },
1656
+ signal: opts.signal
1657
+ });
1658
+ return {
1659
+ path: raw.path,
1660
+ rootEventId: raw.root_event_id,
1661
+ rootSeq: raw.root_seq,
1662
+ entries: raw.entries.map((e) => ({
1663
+ name: e.name,
1664
+ kind: e.kind,
1665
+ size: e.size,
1666
+ mime: e.mime,
1667
+ sha256: e.sha256,
1668
+ exec: e.exec,
1669
+ symlinkTarget: e.symlink_target
1670
+ }))
1671
+ };
1672
+ }
1673
+ async readFile(id, path, opts = {}) {
1674
+ const res = await this.#transport.fetchRaw("GET", `${await this.#base()}/${id}/workspace/file`, { query: { path, at_seq: opts.atSeq }, signal: opts.signal });
1675
+ await raiseForStatus(res, "Could not read the file.");
1676
+ return await res.blob();
1677
+ }
1678
+ async writeFile(id, path, file, opts = {}) {
1679
+ const form = new FormData;
1680
+ form.append("file", file, opts.filename ?? path.split("/").pop() ?? "upload");
1681
+ await this.#transport.request("PUT", `${await this.#base()}/${id}/workspace/file`, {
1682
+ query: { path },
1683
+ body: form,
1684
+ signal: opts.signal
1685
+ });
1686
+ }
1687
+ async writeFiles(id, files, opts = {}) {
1688
+ if (files.length === 0) {
1689
+ throw new AgentApiError("writeFiles needs at least one file.", 400);
1690
+ }
1691
+ const form = new FormData;
1692
+ for (const [i, entry] of files.entries()) {
1693
+ form.append("file", entry.file, entry.filename ?? `file-${i + 1}`);
1694
+ form.append("path", entry.path);
1695
+ }
1696
+ await this.#transport.request("POST", `${await this.#base()}/${id}/workspace/files`, {
1697
+ body: form,
1698
+ signal: opts.signal
1699
+ });
1700
+ }
1701
+ async deleteFile(id, path, signal) {
1702
+ await this.#transport.request("DELETE", `${await this.#base()}/${id}/workspace/file`, {
1703
+ query: { path },
1704
+ signal
1705
+ });
1706
+ }
1707
+ async moveFile(id, from, to, signal) {
1708
+ await this.#transport.request("POST", `${await this.#base()}/${id}/workspace/move`, {
1709
+ body: { from, to },
1710
+ signal
1711
+ });
1712
+ }
1713
+ async setClientTools(id, tools, signal) {
1714
+ await this.#transport.request("PUT", `${await this.#base()}/${id}/client-tools`, {
1715
+ body: { tools: declarations(tools) },
1716
+ signal
1717
+ });
1718
+ }
1719
+ serveClientTools(id, options) {
1720
+ return serveClientTools(this.#transport, async () => `${await this.#base()}/${encodeURIComponent(id)}`, id, options);
1721
+ }
1722
+ async steer(id, content, signal) {
1723
+ await this.#transport.request("POST", `${await this.#base()}/${id}/steer`, {
1724
+ body: { content },
1725
+ signal
1726
+ });
1727
+ }
1728
+ subscribe(id, handlers, opts = {}) {
1729
+ const controller = new AbortController;
1730
+ const streamedArgs = new Map;
1731
+ this.#base().then((base) => {
1732
+ if (controller.signal.aborted)
1733
+ return;
1734
+ if (handlers.onMessage || handlers.onEvent || handlers.onTodos || handlers.onClientToolCall || handlers.onToolActivity || handlers.onTurnDone || handlers.onActivity || handlers.onOpen) {
1735
+ readSse({
1736
+ url: this.#transport.url(`${base}/${id}/events/stream`),
1737
+ event: ["conversation_event", "conversation_status"],
1738
+ lastEventId: opts.since === undefined ? undefined : String(opts.since),
1739
+ headers: () => this.#transport.streamHeaders(),
1740
+ fetchImpl: this.#transport.fetchImpl,
1741
+ signal: controller.signal,
1742
+ onError: handlers.onError,
1743
+ onOpen: handlers.onOpen,
1744
+ onEvent: (frame, name) => {
1745
+ if (name === "conversation_status") {
1746
+ handlers.onActivity?.(toTurnStatus(frame));
1747
+ return;
1748
+ }
1749
+ const raw = frame;
1750
+ handlers.onEvent?.(raw);
1751
+ const message = toMessage(raw);
1752
+ if (message)
1753
+ handlers.onMessage?.(message);
1754
+ const todos = toTodos(raw);
1755
+ if (todos)
1756
+ handlers.onTodos?.(todos, raw.seq);
1757
+ const turnDone = toTurnDone(raw);
1758
+ if (turnDone !== null)
1759
+ handlers.onTurnDone?.(turnDone);
1760
+ const call = toClientToolCall(raw);
1761
+ if (call)
1762
+ handlers.onClientToolCall?.(call);
1763
+ for (const call2 of toToolCallArguments(raw)) {
1764
+ streamedArgs.set(call2.toolCallId, call2.arguments);
1765
+ }
1766
+ const activity = toToolActivity(raw);
1767
+ if (activity)
1768
+ handlers.onToolActivity?.(withToolArguments(activity, streamedArgs));
1769
+ handlers.onCursor?.(raw.change_seq);
1770
+ }
1771
+ }).catch((err) => handlers.onError?.(err));
1772
+ }
1773
+ if (handlers.onConversation) {
1774
+ readSse({
1775
+ url: this.#transport.url(`${base}/${id}/meta/stream`),
1776
+ event: "conversation_meta",
1777
+ headers: () => this.#transport.streamHeaders(),
1778
+ fetchImpl: this.#transport.fetchImpl,
1779
+ signal: controller.signal,
1780
+ onError: handlers.onError,
1781
+ onEvent: (raw) => handlers.onConversation?.(toConversation(raw))
1782
+ }).catch((err) => handlers.onError?.(err));
1783
+ }
1784
+ });
1785
+ return { close: () => controller.abort() };
1786
+ }
1787
+ subscribeToConversations(handlers) {
1788
+ const controller = new AbortController;
1789
+ this.#base().then((base) => {
1790
+ if (controller.signal.aborted)
1791
+ return;
1792
+ readSse({
1793
+ url: this.#transport.url(`${base}/stream`, { origin: "interactive" }),
1794
+ event: "conversation",
1795
+ headers: () => this.#transport.streamHeaders(),
1796
+ fetchImpl: this.#transport.fetchImpl,
1797
+ signal: controller.signal,
1798
+ onError: handlers.onError,
1799
+ onEvent: (raw) => handlers.onConversation(toConversation(raw))
1800
+ }).catch((err) => handlers.onError?.(err));
1801
+ });
1802
+ return { close: () => controller.abort() };
1803
+ }
1804
+ }
1805
+ function createUserClient(options) {
1806
+ return new AgentClient(options);
1807
+ }
1808
+ export {
1809
+ serveClientTools,
1810
+ readSse,
1811
+ mergeVoiceMessages,
1812
+ mergeToolActivities,
1813
+ createUserClient,
1814
+ createAdminClient,
1815
+ MemoryConversationCache,
1816
+ DEFAULT_TIMEOUT_MS,
1817
+ DEFAULT_MAX_RETRIES,
1818
+ AgentNetworkError,
1819
+ AgentError,
1820
+ AgentConfigError,
1821
+ AgentClient,
1822
+ AgentApiError
1823
+ };
1824
+
1825
+ //# debugId=135CEC176CA70F7564756E2164756E21
1826
+ //# sourceMappingURL=index.js.map