@better-agent/client 0.1.0-canary.5 → 0.2.0-beta.1

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.mjs CHANGED
@@ -1,643 +1,287 @@
1
- import { a as toModelMessages, i as fromModelMessages, n as createAgentChatController, o as getEventErrorMessage, r as fromConversationItems, s as toAgentClientError } from "./controller-BrBUfjhZ.mjs";
2
- import { Events } from "@better-agent/core/events";
3
- import { BetterAgentError } from "@better-agent/shared/errors";
4
- import { pruneInputByCapabilities } from "@better-agent/core";
1
+ import { a as BetterAgentClientError, i as toAgentMessages, n as createAgentController, o as toBetterAgentClientError, r as fromAgentMessages, t as AgentController } from "./controller-xMlzSCc7.mjs";
5
2
 
6
3
  //#region src/core/sse.ts
7
- const isEventType = (value) => Object.values(Events).includes(value);
8
- const resetFrame = () => ({ data: "" });
9
- const getSseErrorMessage = (data) => {
4
+ const createFrame = () => ({ data: "" });
5
+ const getErrorPayload = (data) => {
6
+ const fallbackMessage = data.trim().length > 0 ? data.trim() : "Stream failed.";
10
7
  try {
11
8
  const parsed = JSON.parse(data);
12
- if (typeof parsed.message === "string" && parsed.message.trim().length > 0) return parsed.message;
13
- } catch {}
14
- return data.trim().length > 0 ? data.trim() : "Stream failed";
15
- };
16
- const parseFrame = (frame, options) => {
17
- if (!frame.data) return null;
18
- const data = frame.data.replace(/\n$/, "");
19
- if (frame.eventName === "error") throw new Error(getSseErrorMessage(data));
20
- try {
21
- const parsed = JSON.parse(data);
22
- if (!(parsed && typeof parsed === "object" && typeof parsed.type === "string")) return null;
23
- if (!isEventType(parsed.type)) return null;
24
- if (typeof frame.id === "number") {
25
- parsed.seq = frame.id;
26
- options.onId?.(frame.id);
27
- }
28
- return parsed;
9
+ if (!parsed || typeof parsed !== "object") return {
10
+ message: fallbackMessage,
11
+ details: parsed
12
+ };
13
+ const messageField = "message" in parsed && typeof parsed.message === "string" && parsed.message.trim().length > 0 ? parsed.message : void 0;
14
+ const detailField = "detail" in parsed && typeof parsed.detail === "string" && parsed.detail.trim().length > 0 ? parsed.detail : void 0;
15
+ const codeField = "code" in parsed && typeof parsed.code === "string" ? parsed.code : void 0;
16
+ const statusField = "status" in parsed && typeof parsed.status === "number" && Number.isFinite(parsed.status) ? parsed.status : void 0;
17
+ return {
18
+ message: messageField ?? detailField ?? fallbackMessage,
19
+ code: codeField,
20
+ status: statusField,
21
+ details: parsed
22
+ };
29
23
  } catch {
30
- return null;
24
+ return { message: fallbackMessage };
31
25
  }
32
26
  };
33
- async function* parseSse(body, options = {}) {
27
+ const toSseError = (data) => {
28
+ const payload = getErrorPayload(data);
29
+ return new BetterAgentClientError(payload.message, {
30
+ code: payload.code,
31
+ status: payload.status,
32
+ details: payload.details
33
+ });
34
+ };
35
+ const parseFrame = (frame) => {
36
+ if (frame.data.length === 0) return null;
37
+ const data = frame.data.replace(/\n$/, "");
38
+ if (frame.event === "error") throw toSseError(data);
39
+ const parsed = JSON.parse(data);
40
+ if (frame.id !== void 0 && parsed && typeof parsed === "object") return {
41
+ ...parsed,
42
+ seq: frame.id
43
+ };
44
+ return parsed;
45
+ };
46
+ async function* parseSse(body) {
34
47
  const reader = body.getReader();
35
48
  const decoder = new TextDecoder();
36
49
  let buffer = "";
37
- let frame = resetFrame();
38
- while (true) {
50
+ let frame = createFrame();
51
+ for (;;) {
39
52
  const { done, value } = await reader.read();
40
53
  if (done) break;
41
54
  buffer += decoder.decode(value, { stream: true });
42
55
  for (;;) {
43
- const nl = buffer.indexOf("\n");
44
- if (nl < 0) break;
45
- const line = buffer.slice(0, nl).replace(/\r$/, "");
46
- buffer = buffer.slice(nl + 1);
56
+ const newlineIndex = buffer.indexOf("\n");
57
+ if (newlineIndex < 0) break;
58
+ const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
59
+ buffer = buffer.slice(newlineIndex + 1);
47
60
  if (line === "") {
48
- const event = parseFrame(frame, options);
49
- frame = resetFrame();
61
+ const event = parseFrame(frame);
62
+ frame = createFrame();
50
63
  if (event) yield event;
51
64
  continue;
52
65
  }
53
66
  if (line.startsWith("event:")) {
54
- frame.eventName = line.slice(6).trim();
67
+ frame.event = line.slice(6).trim();
55
68
  continue;
56
69
  }
57
70
  if (line.startsWith("id:")) {
58
- const idValue = Number(line.slice(3).trim());
59
- frame.id = Number.isFinite(idValue) ? idValue : void 0;
71
+ const id = Number(line.slice(3).trim());
72
+ frame.id = Number.isFinite(id) ? id : void 0;
60
73
  continue;
61
74
  }
62
75
  if (line.startsWith("data:")) frame.data += `${line.slice(5).trim()}\n`;
63
76
  }
64
77
  }
65
- const finalEvent = parseFrame(frame, options);
66
- if (finalEvent) yield finalEvent;
78
+ const event = parseFrame(frame);
79
+ if (event) yield event;
67
80
  }
68
81
 
69
82
  //#endregion
70
- //#region src/core/client/errors.ts
71
- const tryParseJson = (bodyText) => {
72
- try {
73
- return JSON.parse(bodyText);
74
- } catch {
75
- return null;
76
- }
77
- };
78
- const parseErrorBody = (bodyText) => {
79
- let errorCodeFromBody;
80
- let errorMessageFromBody;
81
- let errorTitleFromBody;
82
- let errorStatusFromBody;
83
- let retryableFromBody;
84
- let traceIdFromBody;
85
- let issuesFromBody;
86
- let contextFromBody;
87
- let traceFromBody;
88
- if (bodyText.length > 0) {
89
- const parsed = tryParseJson(bodyText);
90
- if (parsed) {
91
- if (typeof parsed.code === "string") errorCodeFromBody = parsed.code;
92
- else if (typeof parsed.error === "string") errorCodeFromBody = parsed.error;
93
- if (typeof parsed.message === "string") errorMessageFromBody = parsed.message;
94
- else if (typeof parsed.detail === "string") errorMessageFromBody = parsed.detail;
95
- if (typeof parsed.title === "string") errorTitleFromBody = parsed.title;
96
- if (typeof parsed.status === "number") errorStatusFromBody = parsed.status;
97
- if (typeof parsed.retryable === "boolean") retryableFromBody = parsed.retryable;
98
- if (typeof parsed.traceId === "string") traceIdFromBody = parsed.traceId;
99
- if (Array.isArray(parsed.issues)) issuesFromBody = parsed.issues;
100
- if (typeof parsed.context === "object" && parsed.context !== null) contextFromBody = parsed.context;
101
- if (Array.isArray(parsed.trace)) traceFromBody = parsed.trace;
102
- }
103
- }
104
- return {
105
- errorCodeFromBody,
106
- errorMessageFromBody,
107
- errorTitleFromBody,
108
- errorStatusFromBody,
109
- retryableFromBody,
110
- traceIdFromBody,
111
- issuesFromBody,
112
- contextFromBody,
113
- traceFromBody
114
- };
115
- };
116
- const getContentType = (response) => response.headers.get("content-type")?.toLowerCase() ?? "";
117
- const isHtmlResponse = (response) => getContentType(response).includes("text/html");
118
- const truncateForContext = (value, maxLength = 300) => value.length <= maxLength ? value : `${value.slice(0, maxLength)}...`;
119
- const throwRequestError = async (params) => {
120
- const bodyText = (await params.response.text().catch(() => "")).trim();
121
- const { errorCodeFromBody, errorMessageFromBody, errorTitleFromBody, errorStatusFromBody, retryableFromBody, traceIdFromBody, issuesFromBody, contextFromBody, traceFromBody } = parseErrorBody(bodyText);
122
- const fallbackDetail = isHtmlResponse(params.response) ? "Server returned an HTML error page. This usually means a framework or dev-server failure before the API handler ran." : params.response.statusText || "Unknown error";
123
- const detail = errorMessageFromBody ?? (bodyText.length > 0 && !isHtmlResponse(params.response) ? bodyText : void 0) ?? fallbackDetail;
124
- throw Object.assign(BetterAgentError.fromCode(errorCodeFromBody ?? (params.response.status >= 500 || params.response.status === 429 ? "UPSTREAM_FAILED" : "BAD_REQUEST"), detail, {
125
- title: errorTitleFromBody,
126
- status: errorStatusFromBody ?? params.response.status,
127
- retryable: retryableFromBody,
128
- traceId: traceIdFromBody,
129
- context: {
130
- ...params.context,
131
- operation: params.operation,
132
- status: params.response.status,
133
- ...contextFromBody ?? {},
134
- error: errorCodeFromBody,
135
- issues: issuesFromBody,
136
- ...isHtmlResponse(params.response) && bodyText.length > 0 ? {
137
- responseContentType: getContentType(params.response),
138
- responseBodySnippet: truncateForContext(bodyText)
139
- } : {}
140
- },
141
- trace: [...traceFromBody ?? [], { at: params.at }]
142
- }), issuesFromBody ? { issues: issuesFromBody } : {});
143
- };
144
-
145
- //#endregion
146
- //#region src/core/client/request.ts
147
- const mergeHeaders = (...parts) => {
148
- const merged = new Headers();
149
- for (const part of parts) {
150
- if (!part) continue;
151
- new Headers(part).forEach((value, key) => merged.set(key, value));
152
- }
153
- return merged;
154
- };
155
- const prepareRequest = async (advanced, context) => {
156
- if (!advanced?.prepareRequest) return {
157
- url: context.url,
158
- method: context.method,
159
- headers: context.headers,
160
- body: context.body
161
- };
162
- const prepared = await advanced.prepareRequest(context);
163
- if (!prepared) return {
164
- url: context.url,
165
- method: context.method,
166
- headers: context.headers,
167
- body: context.body
168
- };
169
- return {
170
- url: prepared.url ?? context.url,
171
- method: prepared.method ?? context.method,
172
- headers: prepared.headers ?? context.headers,
173
- ...prepared.body !== void 0 ? { body: prepared.body } : context.body !== void 0 ? { body: context.body } : {}
174
- };
175
- };
83
+ //#region src/core/url.ts
84
+ const ABSOLUTE_URL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//;
85
+ const isAbsoluteURL = (url) => ABSOLUTE_URL_PATTERN.test(url);
86
+ async function resolveRequestURL(url) {
87
+ if (isAbsoluteURL(url)) return url;
88
+ if (typeof window !== "undefined") return url;
89
+ throw new Error("Relative Better Agent URLs work only in browser contexts. Use an absolute URL on the server or call the app directly.");
90
+ }
176
91
 
177
92
  //#endregion
178
- //#region src/core/client/tool-submission.ts
179
- const sleep = async (ms) => {
180
- await new Promise((resolve) => setTimeout(resolve, ms));
181
- };
182
- const createToolSubmissionHandlers = (deps) => {
183
- const doFetch = deps.doFetch;
184
- const authHeaders = deps.secret ? { authorization: `Bearer ${deps.secret}` } : void 0;
185
- const submitToolResult = async (params) => {
186
- const payload = JSON.stringify({
187
- runId: params.runId,
188
- toolCallId: params.toolCallId,
189
- ...params.error !== void 0 ? {
190
- status: "error",
191
- error: params.error
192
- } : {
193
- status: "success",
194
- result: params.result
195
- }
196
- });
197
- for (let attempt = 1; attempt <= deps.toolSubmissionMaxAttempts; attempt++) try {
198
- const prepared = await prepareRequest(deps.advanced, {
199
- operation: "tool-result",
200
- url: `${deps.baseURL}/${encodeURIComponent(params.agent)}/run/tool-result`,
201
- method: "POST",
202
- headers: mergeHeaders(authHeaders, { "content-type": "application/json" }, deps.defaultHeaders),
203
- body: payload
204
- });
205
- const res = await doFetch(prepared.url, {
206
- method: prepared.method,
207
- headers: prepared.headers,
208
- body: prepared.body
209
- });
210
- if (res.ok) return;
211
- if (!(res.status >= 500 || res.status === 429) || attempt === deps.toolSubmissionMaxAttempts) await throwRequestError({
212
- response: res,
213
- operation: "tool-result",
214
- at: "client.core.submitToolResult",
215
- context: {
216
- agentName: params.agent,
217
- runId: params.runId,
218
- toolCallId: params.toolCallId
219
- }
220
- });
221
- await sleep(deps.toolSubmissionRetryDelayMs * attempt);
222
- } catch (error) {
223
- const wrapped = error instanceof Error ? error : new Error("Tool result submission failed.", { cause: error });
224
- if (attempt === deps.toolSubmissionMaxAttempts) throw wrapped;
225
- await sleep(deps.toolSubmissionRetryDelayMs * attempt);
226
- }
227
- };
228
- const submitToolApproval = async (params) => {
229
- const payload = JSON.stringify({
230
- runId: params.runId,
231
- toolCallId: params.toolCallId,
232
- decision: params.decision,
233
- ...params.note !== void 0 ? { note: params.note } : {},
234
- ...params.actorId !== void 0 ? { actorId: params.actorId } : {}
235
- });
236
- for (let attempt = 1; attempt <= deps.toolSubmissionMaxAttempts; attempt++) try {
237
- const prepared = await prepareRequest(deps.advanced, {
238
- operation: "tool-approval",
239
- url: `${deps.baseURL}/${encodeURIComponent(params.agent)}/run/tool-approval`,
240
- method: "POST",
241
- headers: mergeHeaders(authHeaders, { "content-type": "application/json" }, deps.defaultHeaders),
242
- body: payload
243
- });
244
- const res = await doFetch(prepared.url, {
245
- method: prepared.method,
246
- headers: prepared.headers,
247
- body: prepared.body
248
- });
249
- if (res.ok) return;
250
- if (!(res.status >= 500 || res.status === 429) || attempt === deps.toolSubmissionMaxAttempts) await throwRequestError({
251
- response: res,
252
- operation: "tool-approval",
253
- at: "client.core.submitToolApproval",
254
- context: {
255
- agentName: params.agent,
256
- runId: params.runId,
257
- toolCallId: params.toolCallId,
258
- decision: params.decision
259
- }
260
- });
261
- await sleep(deps.toolSubmissionRetryDelayMs * attempt);
262
- } catch (error) {
263
- const wrapped = error instanceof Error ? error : new Error("Tool approval submission failed.", { cause: error });
264
- if (attempt === deps.toolSubmissionMaxAttempts) throw wrapped;
265
- await sleep(deps.toolSubmissionRetryDelayMs * attempt);
93
+ //#region src/core/request.ts
94
+ function joinURL(baseURL, path) {
95
+ return `${baseURL.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
96
+ }
97
+ async function resolveHeaders(configHeaders, requestHeaders) {
98
+ const headers = new Headers(typeof configHeaders === "function" ? await configHeaders() : configHeaders);
99
+ if (requestHeaders) for (const [key, value] of new Headers(requestHeaders)) headers.set(key, value);
100
+ return headers;
101
+ }
102
+ async function prepareRequest(config, request) {
103
+ return await config.prepareRequest?.(request) ?? request;
104
+ }
105
+ async function throwRequestError(response) {
106
+ let details;
107
+ let code;
108
+ let message = `Better Agent request failed with ${response.status}.`;
109
+ try {
110
+ const body = await response.json();
111
+ details = body;
112
+ if (body && typeof body === "object") {
113
+ if ("message" in body && typeof body.message === "string") message = body.message;
114
+ else if ("detail" in body && typeof body.detail === "string") message = body.detail;
115
+ if ("code" in body && typeof body.code === "string") code = body.code;
266
116
  }
267
- };
268
- return {
269
- submitToolApproval,
270
- submitToolResult
271
- };
272
- };
117
+ } catch {}
118
+ throw new BetterAgentClientError(message, {
119
+ status: response.status,
120
+ code,
121
+ details
122
+ });
123
+ }
124
+ async function requestJson(config, request, options) {
125
+ const headers = await resolveHeaders(config.headers, options?.headers);
126
+ if (!headers.has("content-type") && request.body !== void 0) headers.set("content-type", "application/json");
127
+ const prepared = await prepareRequest(config, {
128
+ url: request.url,
129
+ method: request.method,
130
+ headers,
131
+ body: request.body === void 0 ? void 0 : JSON.stringify(request.body)
132
+ });
133
+ const url = await resolveRequestURL(prepared.url);
134
+ const response = await (config.fetch ?? fetch)(url, {
135
+ method: prepared.method,
136
+ headers: prepared.headers,
137
+ body: prepared.body,
138
+ credentials: config.credentials,
139
+ signal: options?.signal ?? null
140
+ });
141
+ if (!response.ok) await throwRequestError(response);
142
+ if (response.status === 204) return;
143
+ return response.json();
144
+ }
145
+ async function requestSse(config, request, options) {
146
+ const headers = await resolveHeaders(config.headers, options?.headers);
147
+ headers.set("accept", "text/event-stream");
148
+ if (!headers.has("content-type") && request.body !== void 0) headers.set("content-type", "application/json");
149
+ const prepared = await prepareRequest(config, {
150
+ url: request.url,
151
+ method: request.method,
152
+ headers,
153
+ body: request.body === void 0 ? void 0 : JSON.stringify(request.body)
154
+ });
155
+ const url = await resolveRequestURL(prepared.url);
156
+ const response = await (config.fetch ?? fetch)(url, {
157
+ method: prepared.method,
158
+ headers: prepared.headers,
159
+ body: prepared.body,
160
+ credentials: config.credentials,
161
+ signal: options?.signal ?? null
162
+ });
163
+ if (!response.ok) await throwRequestError(response);
164
+ if (!response.body) return (async function* () {})();
165
+ return parseSse(response.body);
166
+ }
273
167
 
274
168
  //#endregion
275
- //#region src/core/client/index.ts
276
- /**
277
- * Creates a Better Agent client.
278
- *
279
- * @param config Client configuration.
280
- * @returns A typed client.
281
- *
282
- * @example
283
- * ```ts
284
- * import { createClient } from "@better-agent/client";
285
- *
286
- * const client = createClient({
287
- * baseURL: "http://localhost:3000/api",
288
- * secret: "dev_secret",
289
- * });
290
- *
291
- * const result = await client.run("helloAgent", {
292
- * input: "Write one short sentence about TypeScript.",
293
- * });
294
- * ```
295
- *
296
- * @example
297
- * ```ts
298
- * import type ba from "./better-agent/server";
299
- * import { createClient } from "@better-agent/client";
300
- *
301
- * const client = createClient<typeof ba>({
302
- * baseURL: "http://localhost:3000/api",
303
- * secret: "dev_secret",
304
- * toolHandlers: {
305
- * getClientTime: () => ({ now: new Date().toISOString() }),
306
- * },
307
- * });
308
- *
309
- * for await (const event of client.stream("helloAgent", {
310
- * input: "Use tools if needed.",
311
- * })) {
312
- * console.log(event.type);
313
- * }
314
- * ```
315
- */
316
- const createClient = (config) => {
169
+ //#region src/create-client.ts
170
+ function createClient(config) {
317
171
  const baseURL = config.baseURL.replace(/\/$/, "");
318
- const doFetch = config.fetch ?? fetch;
319
- const advanced = config.advanced;
320
- const authHeaders = config.secret ? { authorization: `Bearer ${config.secret}` } : void 0;
321
- const toolSubmissionMaxAttempts = Math.max(1, Math.floor(advanced?.toolSubmissionMaxAttempts ?? 3));
322
- const toolSubmissionRetryDelayMs = Math.max(0, advanced?.toolSubmissionRetryDelayMs ?? 150);
323
- const { submitToolResult, submitToolApproval } = createToolSubmissionHandlers({
324
- advanced,
325
- baseURL,
326
- defaultHeaders: config.headers,
327
- doFetch,
328
- secret: config.secret,
329
- toolSubmissionMaxAttempts,
330
- toolSubmissionRetryDelayMs
331
- });
332
- return {
333
- async run(agent, input, options) {
334
- const request = {
335
- agent,
336
- ...input
337
- };
338
- const prepared = await prepareRequest(advanced, {
339
- operation: "run",
340
- url: `${baseURL}/${encodeURIComponent(String(agent))}/run`,
341
- method: "POST",
342
- headers: mergeHeaders(authHeaders, { "content-type": "application/json" }, config.headers, options?.headers),
343
- body: JSON.stringify(request)
344
- });
345
- const res = await doFetch(prepared.url, {
346
- method: prepared.method,
347
- headers: prepared.headers,
348
- ...prepared.body !== void 0 ? { body: prepared.body } : {},
349
- signal: options?.signal ?? null
350
- });
351
- options?.onResponse?.(res);
352
- if (!res.ok) await throwRequestError({
353
- response: res,
354
- operation: "run",
355
- at: "client.core.run",
356
- context: { agentName: agent }
357
- });
358
- return await res.json();
359
- },
360
- stream(agent, input, options) {
361
- const request = {
362
- agent,
363
- ...input
364
- };
365
- return (async function* () {
366
- const pendingToolCalls = /* @__PURE__ */ new Map();
367
- const toolCallController = new AbortController();
368
- const abortToolCalls = () => {
369
- if (!toolCallController.signal.aborted) toolCallController.abort();
370
- };
371
- const externalSignal = options?.signal;
372
- const onExternalAbort = () => abortToolCalls();
373
- if (externalSignal) if (externalSignal.aborted) abortToolCalls();
374
- else externalSignal.addEventListener("abort", onExternalAbort, { once: true });
375
- const processClientToolCall = async (toolCallKey, runId, toolCallId) => {
376
- const pending = pendingToolCalls.get(toolCallKey);
377
- if (!pending || pending.submitted || !pending.ended || pending.approvalState !== "waiting" && pending.approvalState !== "approved") return;
378
- pending.submitted = true;
379
- let parsedArgs = {};
380
- try {
381
- parsedArgs = pending.argsJson ? JSON.parse(pending.argsJson) : {};
382
- } catch {
383
- await submitToolResult({
384
- agent: String(agent),
385
- runId,
386
- toolCallId,
387
- error: `Invalid client tool arguments for '${pending.toolName}'.`
388
- });
389
- return;
390
- }
391
- const context = {
392
- agent,
393
- runId,
394
- toolCallId,
395
- signal: toolCallController.signal
396
- };
397
- const handler = resolveToolHandler(pending.toolName, options, config.toolHandlers);
398
- if (!handler) {
399
- await submitToolResult({
400
- agent: String(agent),
401
- runId,
402
- toolCallId,
403
- error: `Missing client tool handler for '${pending.toolName}'.`
404
- });
405
- return;
406
- }
407
- try {
408
- const result = await Promise.resolve(handler({
409
- toolName: pending.toolName,
410
- input: parsedArgs,
411
- context
412
- }));
413
- await submitToolResult({
414
- agent: String(agent),
415
- runId,
416
- toolCallId,
417
- result
418
- });
419
- } catch (error) {
420
- await submitToolResult({
421
- agent: String(agent),
422
- runId,
423
- toolCallId,
424
- error: error instanceof Error ? error.message : "Client tool handler failed."
425
- });
426
- }
427
- };
428
- try {
429
- const prepared = await prepareRequest(advanced, {
430
- operation: "stream",
431
- url: `${baseURL}/${encodeURIComponent(String(agent))}/run`,
432
- method: "POST",
433
- headers: mergeHeaders(authHeaders, {
434
- "content-type": "application/json",
435
- accept: "text/event-stream"
436
- }, config.headers, options?.headers),
437
- body: JSON.stringify({
438
- ...request,
439
- stream: true
440
- })
441
- });
442
- const res = await doFetch(prepared.url, {
443
- method: prepared.method,
444
- headers: prepared.headers,
445
- body: prepared.body,
446
- signal: options?.signal ?? null
447
- });
448
- options?.onResponse?.(res);
449
- if (!res.ok) await throwRequestError({
450
- response: res,
451
- operation: "stream",
452
- at: "client.core.stream",
453
- context: { agentName: agent }
454
- });
455
- if (!res.body) return;
456
- for await (const ev of parseSse(res.body)) {
457
- if (ev.type === "TOOL_CALL_START") {
458
- if (ev.toolTarget !== "client") {
459
- yield ev;
460
- continue;
461
- }
462
- if (typeof ev.runId === "string") pendingToolCalls.set(getToolCallKey(ev.runId, ev.toolCallId), {
463
- toolName: ev.toolCallName,
464
- argsJson: "",
465
- ended: false,
466
- submitted: false,
467
- approvalState: "waiting"
468
- });
469
- } else if (ev.type === "TOOL_CALL_ARGS") {
470
- if (typeof ev.runId !== "string") {
471
- yield ev;
472
- continue;
473
- }
474
- const pending = pendingToolCalls.get(getToolCallKey(ev.runId, ev.toolCallId));
475
- if (pending) pending.argsJson += ev.delta;
476
- } else if (ev.type === "TOOL_CALL_END" && typeof ev.runId === "string") {
477
- const toolCallKey = getToolCallKey(ev.runId, ev.toolCallId);
478
- const pending = pendingToolCalls.get(toolCallKey);
479
- if (pending) {
480
- pending.ended = true;
481
- await processClientToolCall(toolCallKey, ev.runId, ev.toolCallId);
482
- }
483
- } else if ((ev.type === "TOOL_APPROVAL_REQUIRED" || ev.type === "TOOL_APPROVAL_UPDATED") && ev.toolTarget === "client" && typeof ev.runId === "string") {
484
- const toolCallKey = getToolCallKey(ev.runId, ev.toolCallId);
485
- const pending = pendingToolCalls.get(toolCallKey);
486
- if (pending) {
487
- pending.approvalState = ev.state === "approved" ? "approved" : ev.state === "requested" ? "requested" : ev.state === "denied" ? "denied" : "expired";
488
- if (pending.approvalState === "denied" || pending.approvalState === "expired") {
489
- pending.submitted = true;
490
- pendingToolCalls.delete(toolCallKey);
491
- } else if (pending.approvalState === "approved") await processClientToolCall(toolCallKey, ev.runId, ev.toolCallId);
492
- }
493
- }
494
- if (ev.type === "RUN_ABORTED" || ev.type === "RUN_ERROR" || ev.type === "RUN_FINISHED") {
495
- for (const [toolCallKey, pending] of pendingToolCalls) if (pending.submitted) pendingToolCalls.delete(toolCallKey);
496
- abortToolCalls();
497
- }
498
- yield ev;
499
- }
500
- } finally {
501
- abortToolCalls();
502
- if (externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
503
- pendingToolCalls.clear();
504
- }
505
- })();
506
- },
507
- resumeStream(agent, input, options) {
508
- return (async function* () {
509
- const searchParams = new URLSearchParams();
510
- searchParams.set("streamId", input.streamId);
511
- const headers = mergeHeaders(authHeaders, { accept: "text/event-stream" }, config.headers, options?.headers);
512
- if (typeof input.afterSeq === "number") headers.set("last-event-id", String(input.afterSeq));
513
- const prepared = await prepareRequest(advanced, {
514
- operation: "resume-stream",
515
- url: `${baseURL}/${encodeURIComponent(String(agent))}/stream-events/resume?${searchParams.toString()}`,
516
- method: "GET",
517
- headers
518
- });
519
- const res = await doFetch(prepared.url, {
520
- method: prepared.method,
521
- headers: prepared.headers,
522
- ...prepared.body !== void 0 ? { body: prepared.body } : {},
523
- signal: options?.signal ?? null
524
- });
525
- options?.onResponse?.(res);
526
- if (res.status === 204) return;
527
- if (!res.ok) await throwRequestError({
528
- response: res,
529
- operation: "resume-stream",
530
- at: "client.core.resumeStream",
531
- context: { agentName: agent }
532
- });
533
- if (!res.body) return;
534
- for await (const ev of parseSse(res.body)) yield ev;
535
- })();
172
+ const withLimit = (url, limit) => {
173
+ if (typeof limit !== "number") return url;
174
+ return `${url}${url.includes("?") ? "&" : "?"}limit=${encodeURIComponent(String(limit))}`;
175
+ };
176
+ const withParam = (url, key, value) => {
177
+ if (value === void 0) return url;
178
+ return `${url}${url.includes("?") ? "&" : "?"}${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
179
+ };
180
+ const runs = {
181
+ async abort(runId, options) {
182
+ await requestJson(config, {
183
+ url: joinURL(baseURL, `/runs/${encodeURIComponent(runId)}/abort`),
184
+ method: "POST"
185
+ }, options);
536
186
  },
537
- resumeConversation(agent, input, options) {
187
+ resumeStream(input, options) {
538
188
  return (async function* () {
539
- const headers = mergeHeaders(authHeaders, { accept: "text/event-stream" }, config.headers, options?.headers);
540
- if (typeof input.afterSeq === "number") headers.set("last-event-id", String(input.afterSeq));
541
- const prepared = await prepareRequest(advanced, {
542
- operation: "resume-conversation",
543
- url: `${baseURL}/${encodeURIComponent(String(agent))}/conversations/${encodeURIComponent(input.conversationId)}/resume`,
544
- method: "GET",
189
+ const headers = new Headers(options?.headers);
190
+ if (typeof input.afterSequence === "number") headers.set("last-event-id", String(input.afterSequence));
191
+ const events = await requestSse(config, {
192
+ url: joinURL(baseURL, `/runs/${encodeURIComponent(input.runId)}/stream`),
193
+ method: "GET"
194
+ }, {
195
+ ...options,
545
196
  headers
546
197
  });
547
- const res = await doFetch(prepared.url, {
548
- method: prepared.method,
549
- headers: prepared.headers,
550
- ...prepared.body !== void 0 ? { body: prepared.body } : {},
551
- signal: options?.signal ?? null
552
- });
553
- options?.onResponse?.(res);
554
- if (res.status === 204) return;
555
- if (!res.ok) await throwRequestError({
556
- response: res,
557
- operation: "resume-conversation",
558
- at: "client.core.resumeConversation",
559
- context: {
560
- agentName: agent,
561
- conversationId: input.conversationId
562
- }
563
- });
564
- if (!res.body) return;
565
- for await (const ev of parseSse(res.body)) yield ev;
198
+ for await (const event of events) yield event;
566
199
  })();
567
- },
568
- async loadConversation(agent, conversationId, options) {
569
- const prepared = await prepareRequest(advanced, {
570
- operation: "load-conversation",
571
- url: `${baseURL}/${encodeURIComponent(String(agent))}/conversations/${encodeURIComponent(conversationId)}`,
572
- method: "GET",
573
- headers: mergeHeaders(authHeaders, config.headers, options?.headers)
574
- });
575
- const res = await doFetch(prepared.url, {
576
- method: prepared.method,
577
- headers: prepared.headers,
578
- ...prepared.body !== void 0 ? { body: prepared.body } : {},
579
- signal: options?.signal ?? null
580
- });
581
- options?.onResponse?.(res);
582
- if (res.status === 204) return null;
583
- if (!res.ok) await throwRequestError({
584
- response: res,
585
- operation: "load-conversation",
586
- at: "client.core.loadConversation",
587
- context: {
588
- agentName: agent,
589
- conversationId
590
- }
591
- });
592
- return await res.json();
593
- },
594
- async abortRun(req, options) {
595
- const prepared = await prepareRequest(advanced, {
596
- operation: "abort-run",
597
- url: `${baseURL}/${encodeURIComponent(String(req.agent))}/runs/${encodeURIComponent(req.runId)}/abort`,
598
- method: "POST",
599
- headers: mergeHeaders(authHeaders, config.headers, options?.headers)
600
- });
601
- const res = await doFetch(prepared.url, {
602
- method: prepared.method,
603
- headers: prepared.headers,
604
- ...prepared.body !== void 0 ? { body: prepared.body } : {},
605
- signal: options?.signal ?? null
606
- });
607
- options?.onResponse?.(res);
608
- if (res.status === 204) return;
609
- if (!res.ok) await throwRequestError({
610
- response: res,
611
- operation: "abort-run",
612
- at: "client.core.abortRun",
613
- context: {
614
- agentName: req.agent,
615
- runId: req.runId
200
+ }
201
+ };
202
+ const agentHandles = /* @__PURE__ */ new Map();
203
+ return {
204
+ agent(agentName) {
205
+ const agentKey = String(agentName);
206
+ const cached = agentHandles.get(agentKey);
207
+ if (cached) return cached;
208
+ const encodedAgentName = encodeURIComponent(agentKey);
209
+ const runURL = joinURL(baseURL, `/${encodedAgentName}/run`);
210
+ const handle = {
211
+ runs,
212
+ run(input, options) {
213
+ return requestJson(config, {
214
+ url: runURL,
215
+ method: "POST",
216
+ body: input
217
+ }, options);
218
+ },
219
+ stream(input, options) {
220
+ return (async function* () {
221
+ const events = await requestSse(config, {
222
+ url: runURL,
223
+ method: "POST",
224
+ body: input
225
+ }, options);
226
+ for await (const event of events) yield event;
227
+ })();
228
+ },
229
+ memory: {
230
+ threads: {
231
+ async list(input, options) {
232
+ return (await requestJson(config, {
233
+ url: withLimit(joinURL(baseURL, `/${encodedAgentName}/threads`), input?.limit),
234
+ method: "GET"
235
+ }, options)).threads;
236
+ },
237
+ create(input, options) {
238
+ return requestJson(config, {
239
+ url: joinURL(baseURL, `/${encodedAgentName}/threads`),
240
+ method: "POST",
241
+ body: input ?? {}
242
+ }, options);
243
+ },
244
+ get(threadId, options) {
245
+ return requestJson(config, {
246
+ url: joinURL(baseURL, `/${encodedAgentName}/threads/${encodeURIComponent(threadId)}`),
247
+ method: "GET"
248
+ }, options);
249
+ },
250
+ update(threadId, input, options) {
251
+ return requestJson(config, {
252
+ url: joinURL(baseURL, `/${encodedAgentName}/threads/${encodeURIComponent(threadId)}`),
253
+ method: "PATCH",
254
+ body: input
255
+ }, options);
256
+ },
257
+ async delete(threadId, options) {
258
+ await requestJson(config, {
259
+ url: joinURL(baseURL, `/${encodedAgentName}/threads/${encodeURIComponent(threadId)}`),
260
+ method: "DELETE"
261
+ }, options);
262
+ },
263
+ runtime(threadId, options) {
264
+ return requestJson(config, {
265
+ url: joinURL(baseURL, `/${encodedAgentName}/threads/${encodeURIComponent(threadId)}/runtime`),
266
+ method: "GET"
267
+ }, options);
268
+ }
269
+ },
270
+ messages: { async list(threadId, input, options) {
271
+ return (await requestJson(config, {
272
+ url: withParam(withLimit(joinURL(baseURL, `/${encodedAgentName}/threads/${encodeURIComponent(threadId)}/messages`), input?.limit), "beforeRunId", input?.beforeRunId),
273
+ method: "GET"
274
+ }, options)).messages;
275
+ } }
616
276
  }
617
- });
618
- },
619
- async submitToolResult(req) {
620
- await submitToolResult({
621
- ...req,
622
- agent: String(req.agent)
623
- });
277
+ };
278
+ agentHandles.set(agentKey, handle);
279
+ return handle;
624
280
  },
625
- async submitToolApproval(req) {
626
- await submitToolApproval({
627
- ...req,
628
- agent: String(req.agent)
629
- });
630
- }
281
+ runs
631
282
  };
632
- };
633
- const getToolCallKey = (runId, toolCallId) => `${runId}:${toolCallId}`;
634
- const resolveToolHandler = (toolName, options, staticToolHandlers) => {
635
- const requestHandler = options?.onToolCall;
636
- const requestToolHandler = (options?.toolHandlers)?.[toolName];
637
- const staticHandler = staticToolHandlers?.[toolName];
638
- return requestHandler ?? (requestToolHandler ? (params) => requestToolHandler(params.input, params.context) : void 0) ?? (staticHandler ? (params) => staticHandler(params.input, params.context) : void 0);
639
- };
283
+ }
640
284
 
641
285
  //#endregion
642
- export { createAgentChatController, createClient, fromConversationItems, fromModelMessages, getEventErrorMessage, pruneInputByCapabilities, toAgentClientError, toModelMessages };
286
+ export { AgentController, BetterAgentClientError, createAgentController, createClient, fromAgentMessages, toAgentMessages, toBetterAgentClientError };
643
287
  //# sourceMappingURL=index.mjs.map