@tangle-network/agent-runtime 0.21.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { AgentEvalError, KnowledgeReadinessReport, ControlEvalResult, KnowledgeRequirement } from '@tangle-network/agent-eval';
2
2
  export { AgentEvalError, AgentEvalErrorCode, ConfigError, ControlBudget, ControlDecision, ControlEvalResult, ControlRunResult, ControlStep, DataAcquisitionPlan, JudgeError, KnowledgeReadinessReport, KnowledgeRequirement, NotFoundError, RunRecord, ValidationError } from '@tangle-network/agent-eval';
3
- import { a as AgentBackendInput, b as AgentExecutionBackend, c as AgentBackendContext, R as RuntimeStreamEvent, K as KnowledgeReadinessDecision, d as RunAgentTaskOptions, e as AgentTaskRunResult, f as RunAgentTaskStreamOptions, g as AgentRuntimeEvent, h as AgentTaskStatus, i as RuntimeSessionStore, j as RuntimeSession } from './types-DvJIha6w.js';
4
- export { k as AgentAdapter, l as AgentKnowledgeProvider, m as AgentRuntimeEventSink, n as AgentTaskContext, A as AgentTaskSpec } from './types-DvJIha6w.js';
5
- export { R as RuntimeRunHandle, a as RuntimeRunPersistenceAdapter, b as RuntimeRunRow, s as startRuntimeRun } from './runtime-run-B2j-hvBj.js';
3
+ import { a as AgentBackendInput, b as AgentExecutionBackend, O as OpenAIChatTool, c as OpenAIChatToolChoice, d as AgentBackendContext, R as RuntimeStreamEvent, K as KnowledgeReadinessDecision, e as RunAgentTaskOptions, f as AgentTaskRunResult, g as RunAgentTaskStreamOptions, h as AgentRuntimeEvent, i as AgentTaskStatus, j as RuntimeSessionStore, k as RuntimeSession } from './types-BFgFD_sl.js';
4
+ export { l as AgentAdapter, m as AgentKnowledgeProvider, n as AgentRuntimeEventSink, o as AgentTaskContext, A as AgentTaskSpec, B as BackendErrorDetail } from './types-BFgFD_sl.js';
5
+ export { R as RuntimeRunHandle, a as RuntimeRunPersistenceAdapter, b as RuntimeRunRow, s as startRuntimeRun } from './runtime-run-D5ItCKl_.js';
6
6
 
7
7
  /**
8
8
  * @stable
@@ -68,11 +68,59 @@ interface BackendRetryPolicy {
68
68
  */
69
69
  requestTimeoutMs?: number;
70
70
  }
71
+ /**
72
+ * @stable
73
+ *
74
+ * OpenAI-compat streaming backend. Routes `runAgentTaskStream` through any
75
+ * `POST /chat/completions` endpoint that speaks OpenAI's SSE protocol —
76
+ * Tangle Router, OpenAI direct, OpenRouter, Groq, DeepSeek, Together. The
77
+ * router also fronts Anthropic models in Anthropic-native SSE shape; this
78
+ * backend handles both.
79
+ *
80
+ * ### Tool calls
81
+ *
82
+ * Pass `tools` (and optionally `toolChoice`) to forward an OpenAI Chat
83
+ * Completions `tools[]` array on every request. Streamed `tool_call` chunks
84
+ * are buffered until the model finalizes them (either `finish_reason:
85
+ * 'tool_calls'` for OpenAI shape or a `content_block_stop` for Anthropic
86
+ * `tool_use` blocks proxied through the router), then emitted as a single
87
+ * `tool_call` RuntimeStreamEvent with the assembled `args`.
88
+ *
89
+ * The backend does NOT execute tools — it surfaces calls for the caller's
90
+ * own dispatcher (typically the product's MCP / sandbox runtime) to fulfill
91
+ * and feed back as a subsequent `messages` turn. This keeps the transport
92
+ * thin and lets the agent host own tool dispatch policy.
93
+ *
94
+ * ### Fail-loud errors
95
+ *
96
+ * Non-success HTTP responses (4xx/5xx) and exhausted retry budgets throw
97
+ * `BackendTransportError` from inside the `stream()` generator. The runtime
98
+ * catches the throw, yields a `backend_error` with a typed `error` field
99
+ * (`kind`, `status`, truncated `body`) and a terminal `final` event with
100
+ * `status: 'failed'` carrying the same detail. Consumers MUST map
101
+ * `final.error` onto their `RunRecord.error` — silently treating an empty
102
+ * `finalText` as "agent produced nothing" hides credit exhaustion, auth
103
+ * failure, and upstream outages.
104
+ */
71
105
  declare function createOpenAICompatibleBackend<TInput extends AgentBackendInput = AgentBackendInput>(options: {
72
106
  apiKey: string;
73
107
  baseUrl: string;
74
108
  model: string;
75
109
  kind?: string;
110
+ /**
111
+ * OpenAI Chat Completions `tools[]` definitions surfaced to the model on
112
+ * every request. Omit to send a tool-free request (existing behavior).
113
+ * The runtime makes no assumption about the dispatcher — calls stream out
114
+ * as `tool_call` events and the caller is responsible for executing them
115
+ * and feeding `tool_result` messages back on a follow-up turn.
116
+ */
117
+ tools?: ReadonlyArray<OpenAIChatTool>;
118
+ /**
119
+ * OpenAI Chat Completions `tool_choice`. Default `undefined` (request
120
+ * omits the field; provider falls back to its own default — typically
121
+ * `'auto'`).
122
+ */
123
+ toolChoice?: OpenAIChatToolChoice;
76
124
  fetchImpl?: typeof fetch;
77
125
  retry?: BackendRetryPolicy;
78
126
  }): AgentExecutionBackend<TInput>;
@@ -220,6 +268,30 @@ declare function deriveExecutionId(input: {
220
268
  * importing the runtime.
221
269
  */
222
270
 
271
+ /**
272
+ * @stable
273
+ *
274
+ * A backend transport call (HTTP, gRPC, sidecar IPC) failed with a non-success
275
+ * status. Distinct from `JudgeError` (which is structural / unrecoverable)
276
+ * because backend failures are sometimes retryable and consumers may want to
277
+ * branch on the upstream status code.
278
+ */
279
+ declare class BackendTransportError extends AgentEvalError {
280
+ readonly backend: string;
281
+ readonly status?: number;
282
+ /**
283
+ * Truncated upstream response body (≤2 KiB) when available. Diagnostic
284
+ * only — surfaces in `backend_error.error.body` and `final.error.body`
285
+ * so operators can see "free_tier_limit", "invalid_api_key", etc. without
286
+ * cracking the log line open.
287
+ */
288
+ readonly body?: string;
289
+ constructor(backend: string, message: string, options?: {
290
+ cause?: unknown;
291
+ status?: number;
292
+ body?: string;
293
+ });
294
+ }
223
295
  /**
224
296
  * @stable
225
297
  *
@@ -504,4 +576,4 @@ declare function readinessServerSentEvent(report: KnowledgeReadinessReport, opti
504
576
  /** @stable */
505
577
  declare function runtimeStreamServerSentEvent(event: RuntimeStreamEvent, options?: RuntimeTelemetryOptions & ServerSentEventOptions): string;
506
578
 
507
- export { AgentBackendContext, AgentBackendInput, AgentExecutionBackend, AgentRuntimeEvent, AgentTaskRunResult, AgentTaskStatus, type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, DEFAULT_ROUTER_BASE_URL, InMemoryRuntimeSessionStore, type ModelInfo, type ResolvedChatModel, type RouterEnv, type RunChatTurnInput, type RuntimeEventCollector, RuntimeRunStateError, RuntimeSessionStore, RuntimeStreamEvent, type RuntimeStreamEventCollector, type RuntimeTelemetryOptions, type SanitizedKnowledgeReadinessReport, cleanModelId, createIterableBackend, createOpenAICompatibleBackend, createRuntimeEventCollector, createRuntimeStreamEventCollector, createSandboxPromptBackend, decideKnowledgeReadiness, deriveExecutionId, getModels, handleChatTurn, readinessServerSentEvent, resolveChatModel, resolveRouterBaseUrl, runAgentTask, runAgentTaskStream, runtimeStreamServerSentEvent, sanitizeAgentRuntimeEvent, sanitizeKnowledgeReadinessReport, sanitizeRuntimeStreamEvent, validateChatModelId };
579
+ export { AgentBackendContext, AgentBackendInput, AgentExecutionBackend, AgentRuntimeEvent, AgentTaskRunResult, AgentTaskStatus, BackendTransportError, type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, DEFAULT_ROUTER_BASE_URL, InMemoryRuntimeSessionStore, type ModelInfo, OpenAIChatTool, OpenAIChatToolChoice, type ResolvedChatModel, type RouterEnv, type RunChatTurnInput, type RuntimeEventCollector, RuntimeRunStateError, RuntimeSessionStore, RuntimeStreamEvent, type RuntimeStreamEventCollector, type RuntimeTelemetryOptions, type SanitizedKnowledgeReadinessReport, cleanModelId, createIterableBackend, createOpenAICompatibleBackend, createRuntimeEventCollector, createRuntimeStreamEventCollector, createSandboxPromptBackend, decideKnowledgeReadiness, deriveExecutionId, getModels, handleChatTurn, readinessServerSentEvent, resolveChatModel, resolveRouterBaseUrl, runAgentTask, runAgentTaskStream, runtimeStreamServerSentEvent, sanitizeAgentRuntimeEvent, sanitizeKnowledgeReadinessReport, sanitizeRuntimeStreamEvent, validateChatModelId };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  RuntimeRunStateError,
8
8
  SessionMismatchError,
9
9
  ValidationError
10
- } from "./chunk-RZAOYKCO.js";
10
+ } from "./chunk-XZYF3YJN.js";
11
11
  import "./chunk-DGUM43GV.js";
12
12
 
13
13
  // src/sessions.ts
@@ -144,14 +144,19 @@ function createOpenAICompatibleBackend(options) {
144
144
  },
145
145
  async *stream(input, context) {
146
146
  const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
147
- const requestBody = JSON.stringify({
147
+ const bodyPayload = {
148
148
  model: options.model,
149
149
  stream: true,
150
150
  stream_options: { include_usage: true },
151
151
  messages: input.messages ?? [
152
152
  { role: "user", content: input.message ?? context.task.intent }
153
153
  ]
154
- });
154
+ };
155
+ if (options.tools && options.tools.length > 0) {
156
+ bodyPayload.tools = options.tools;
157
+ if (options.toolChoice !== void 0) bodyPayload.tool_choice = options.toolChoice;
158
+ }
159
+ const requestBody = JSON.stringify(bodyPayload);
155
160
  let response;
156
161
  let lastStatus = 0;
157
162
  let lastThrown;
@@ -198,14 +203,23 @@ function createOpenAICompatibleBackend(options) {
198
203
  );
199
204
  }
200
205
  if (!response.ok) {
206
+ let body;
207
+ try {
208
+ const raw = await response.text();
209
+ body = raw.length > MAX_ERROR_BODY_BYTES ? `${raw.slice(0, MAX_ERROR_BODY_BYTES)}\u2026` : raw;
210
+ } catch {
211
+ body = void 0;
212
+ }
201
213
  throw new BackendTransportError(kind, `chat backend returned ${lastStatus || "unknown"}`, {
202
- status: lastStatus || 0
214
+ status: lastStatus || 0,
215
+ body
203
216
  });
204
217
  }
205
218
  yield* streamResponseEvents(response, context, options.model);
206
219
  }
207
220
  };
208
221
  }
222
+ var MAX_ERROR_BODY_BYTES = 2048;
209
223
  function normalizeBackendStreamEvent(event, task, session) {
210
224
  if ("task" in event && event.task && "session" in event && event.session && "timestamp" in event && event.timestamp) {
211
225
  return event;
@@ -315,6 +329,7 @@ async function* streamResponseEvents(response, context, requestedModel) {
315
329
  const decoder = new TextDecoder();
316
330
  let buffer = "";
317
331
  const usage = { saw: false };
332
+ const toolCalls = /* @__PURE__ */ new Map();
318
333
  const startedAt = Date.now();
319
334
  for (; ; ) {
320
335
  const { done, value } = await reader.read();
@@ -325,9 +340,9 @@ async function* streamResponseEvents(response, context, requestedModel) {
325
340
  buffer += decoder.decode().replace(/\r\n/g, "\n");
326
341
  for (const event of drainStreamBuffer(true)) yield event;
327
342
  if (buffer.trim()) {
328
- const event = parseStreamChunk(buffer, context, usage);
329
- if (event) yield event;
343
+ for (const event of parseStreamChunk(buffer, context, usage, toolCalls)) yield event;
330
344
  }
345
+ for (const event of flushPendingToolCalls(toolCalls, context)) yield event;
331
346
  if (usage.saw) {
332
347
  yield {
333
348
  type: "llm_call",
@@ -350,56 +365,124 @@ async function* streamResponseEvents(response, context, requestedModel) {
350
365
  if (sseBoundary >= 0) {
351
366
  const chunk = buffer.slice(0, sseBoundary);
352
367
  buffer = buffer.slice(sseBoundary + 2);
353
- const event = parseStreamChunk(chunk, context, usage);
354
- if (event) yield event;
368
+ for (const event of parseStreamChunk(chunk, context, usage, toolCalls)) yield event;
355
369
  continue;
356
370
  }
357
371
  const newline = buffer.indexOf("\n");
358
372
  if (newline >= 0 && !buffer.slice(0, newline).startsWith("data:")) {
359
373
  const line = buffer.slice(0, newline);
360
374
  buffer = buffer.slice(newline + 1);
361
- const event = parseStreamChunk(line, context, usage);
362
- if (event) yield event;
375
+ for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
363
376
  continue;
364
377
  }
365
378
  if (flush && buffer.trim() && !buffer.trimStart().startsWith("data:")) {
366
379
  const line = buffer;
367
380
  buffer = "";
368
- const event = parseStreamChunk(line, context, usage);
369
- if (event) yield event;
381
+ for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
370
382
  continue;
371
383
  }
372
384
  break;
373
385
  }
374
386
  }
375
387
  }
376
- function parseStreamChunk(chunk, context, usage) {
388
+ function* parseStreamChunk(chunk, context, usage, toolCalls) {
377
389
  const lines = chunk.split(/\r?\n/);
378
390
  const dataLines = lines.filter((line) => line.startsWith("data:"));
379
391
  const data = dataLines.length > 0 ? dataLines.map((line) => line.slice(5).trimStart()).join("\n") : chunk.trim();
380
- if (!data || data === "[DONE]") return void 0;
392
+ if (!data || data === "[DONE]") return;
393
+ let parsed;
381
394
  try {
382
- const parsed = JSON.parse(data);
383
- captureStreamUsage(parsed, usage);
384
- const choices = parsed.choices;
385
- const choice = Array.isArray(choices) ? choices[0] : void 0;
386
- const delta = choice?.delta;
387
- const message = choice?.message;
388
- const text = stringValue(delta?.content) ?? stringValue(message?.content) ?? stringValue(parsed.text);
389
- if (text) {
390
- return {
391
- type: "text_delta",
392
- task: context.task,
393
- session: context.session,
394
- text,
395
- timestamp: nowIso()
396
- };
395
+ parsed = JSON.parse(data);
396
+ } catch {
397
+ yield {
398
+ type: "text_delta",
399
+ task: context.task,
400
+ session: context.session,
401
+ text: data,
402
+ timestamp: nowIso()
403
+ };
404
+ return;
405
+ }
406
+ captureStreamUsage(parsed, usage);
407
+ const choices = parsed.choices;
408
+ const choice = Array.isArray(choices) ? choices[0] : void 0;
409
+ const delta = choice?.delta;
410
+ const message = choice?.message;
411
+ const deltaToolCalls = delta?.tool_calls;
412
+ if (Array.isArray(deltaToolCalls)) {
413
+ for (const tc of deltaToolCalls) {
414
+ if (!tc || typeof tc !== "object") continue;
415
+ const rec = tc;
416
+ const idx = numberValue(rec.index) ?? 0;
417
+ const key = `openai:${idx}`;
418
+ const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
419
+ const id = stringValue(rec.id);
420
+ if (id) acc.id = id;
421
+ const fn = rec.function;
422
+ const name = stringValue(fn?.name);
423
+ if (name) acc.name = name;
424
+ const args = stringValue(fn?.arguments);
425
+ if (args) acc.argsRaw += args;
426
+ toolCalls.set(key, acc);
427
+ }
428
+ }
429
+ const messageToolCalls = message?.tool_calls;
430
+ if (Array.isArray(messageToolCalls)) {
431
+ for (const tc of messageToolCalls) {
432
+ if (!tc || typeof tc !== "object") continue;
433
+ const rec = tc;
434
+ const fn = rec.function;
435
+ const idx = numberValue(rec.index) ?? messageToolCalls.indexOf(tc);
436
+ const key = `openai:${idx}`;
437
+ const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
438
+ const id = stringValue(rec.id);
439
+ if (id) acc.id = id;
440
+ const name = stringValue(fn?.name);
441
+ if (name) acc.name = name;
442
+ const args = stringValue(fn?.arguments);
443
+ if (args) acc.argsRaw += args;
444
+ acc.finalized = true;
445
+ toolCalls.set(key, acc);
397
446
  }
398
- if (stringValue(parsed.type) === "content_block_delta") {
399
- const d = parsed.delta;
447
+ }
448
+ const finishReason = stringValue(choice?.finish_reason);
449
+ if (finishReason === "tool_calls") {
450
+ for (const [key, acc] of toolCalls) {
451
+ if (acc.source === "openai" && !acc.finalized) acc.finalized = true;
452
+ toolCalls.set(key, acc);
453
+ }
454
+ }
455
+ const eventType = stringValue(parsed.type);
456
+ if (eventType === "content_block_start") {
457
+ const block = parsed.content_block;
458
+ if (block && stringValue(block.type) === "tool_use") {
459
+ const idx = numberValue(parsed.index) ?? 0;
460
+ const key = `anthropic:${idx}`;
461
+ toolCalls.set(key, {
462
+ id: stringValue(block.id),
463
+ name: stringValue(block.name),
464
+ argsRaw: "",
465
+ source: "anthropic",
466
+ finalized: false
467
+ });
468
+ }
469
+ }
470
+ if (eventType === "content_block_delta") {
471
+ const d = parsed.delta;
472
+ const dType = stringValue(d?.type);
473
+ if (dType === "input_json_delta") {
474
+ const idx = numberValue(parsed.index) ?? 0;
475
+ const key = `anthropic:${idx}`;
476
+ const acc = toolCalls.get(key);
477
+ if (acc) {
478
+ const partial = stringValue(d?.partial_json) ?? "";
479
+ acc.argsRaw += partial;
480
+ toolCalls.set(key, acc);
481
+ }
482
+ } else {
400
483
  const text2 = stringValue(d?.text);
401
484
  if (text2) {
402
- return {
485
+ yield {
403
486
  type: "text_delta",
404
487
  task: context.task,
405
488
  session: context.session,
@@ -408,17 +491,65 @@ function parseStreamChunk(chunk, context, usage) {
408
491
  };
409
492
  }
410
493
  }
411
- return mapCommonBackendEvent(parsed, context);
412
- } catch {
413
- return {
494
+ }
495
+ if (eventType === "content_block_stop") {
496
+ const idx = numberValue(parsed.index) ?? 0;
497
+ const key = `anthropic:${idx}`;
498
+ const acc = toolCalls.get(key);
499
+ if (acc) {
500
+ acc.finalized = true;
501
+ toolCalls.set(key, acc);
502
+ }
503
+ }
504
+ for (const event of drainFinalizedToolCalls(toolCalls, context)) yield event;
505
+ const text = stringValue(delta?.content) ?? stringValue(message?.content) ?? stringValue(parsed.text);
506
+ if (text) {
507
+ yield {
414
508
  type: "text_delta",
415
509
  task: context.task,
416
510
  session: context.session,
417
- text: data,
511
+ text,
418
512
  timestamp: nowIso()
419
513
  };
514
+ return;
515
+ }
516
+ const mapped = mapCommonBackendEvent(parsed, context);
517
+ if (mapped) yield mapped;
518
+ }
519
+ function* drainFinalizedToolCalls(toolCalls, context) {
520
+ for (const [key, acc] of toolCalls) {
521
+ if (!acc.finalized) continue;
522
+ toolCalls.delete(key);
523
+ yield buildToolCallEvent(acc, context);
524
+ }
525
+ }
526
+ function* flushPendingToolCalls(toolCalls, context) {
527
+ for (const [key, acc] of toolCalls) {
528
+ toolCalls.delete(key);
529
+ yield buildToolCallEvent(acc, context);
420
530
  }
421
531
  }
532
+ function buildToolCallEvent(acc, context) {
533
+ let args = acc.argsRaw;
534
+ if (acc.argsRaw.length > 0) {
535
+ try {
536
+ args = JSON.parse(acc.argsRaw);
537
+ } catch {
538
+ args = acc.argsRaw;
539
+ }
540
+ } else {
541
+ args = {};
542
+ }
543
+ return {
544
+ type: "tool_call",
545
+ task: context.task,
546
+ session: context.session,
547
+ toolName: acc.name ?? "tool",
548
+ toolCallId: acc.id,
549
+ args,
550
+ timestamp: nowIso()
551
+ };
552
+ }
422
553
  function captureStreamUsage(parsed, usage) {
423
554
  const model = stringValue(parsed.model);
424
555
  if (model && !usage.model) usage.model = model;
@@ -888,13 +1019,21 @@ async function* runAgentTaskStream(options) {
888
1019
  } catch (stopErr) {
889
1020
  stopErrorMessage = stopErr instanceof Error ? stopErr.message : String(stopErr);
890
1021
  }
1022
+ const combinedMessage = stopErrorMessage ? `${message}; backend stop failed: ${stopErrorMessage}` : message;
1023
+ const errorDetail = err instanceof BackendTransportError ? {
1024
+ kind: "transport",
1025
+ message: combinedMessage,
1026
+ status: err.status,
1027
+ body: err.body
1028
+ } : { kind: "backend", message: combinedMessage };
891
1029
  const backendError = streamEvent({
892
1030
  type: "backend_error",
893
1031
  task,
894
1032
  session,
895
1033
  backend: options.backend.kind,
896
- message: stopErrorMessage ? `${message}; backend stop failed: ${stopErrorMessage}` : message,
897
- recoverable: !options.signal?.aborted
1034
+ message: combinedMessage,
1035
+ recoverable: !options.signal?.aborted,
1036
+ error: errorDetail
898
1037
  });
899
1038
  await store?.appendEvent?.(session.id, backendError);
900
1039
  yield backendError;
@@ -908,7 +1047,8 @@ async function* runAgentTaskStream(options) {
908
1047
  session,
909
1048
  status,
910
1049
  reason: message,
911
- text: finalText || void 0
1050
+ text: finalText || void 0,
1051
+ error: errorDetail
912
1052
  });
913
1053
  await store?.appendEvent?.(session.id, final);
914
1054
  yield final;
@@ -1289,6 +1429,12 @@ function sanitizeRuntimeStreamEvent(event, options = {}) {
1289
1429
  };
1290
1430
  }
1291
1431
  if (event.type === "final") {
1432
+ const sanitizedError = event.error !== void 0 ? {
1433
+ kind: event.error.kind,
1434
+ message: event.error.message,
1435
+ status: event.error.status,
1436
+ body: options.includeControlPayloads ? event.error.body : void 0
1437
+ } : void 0;
1292
1438
  return {
1293
1439
  type: event.type,
1294
1440
  ...withTask,
@@ -1297,7 +1443,8 @@ function sanitizeRuntimeStreamEvent(event, options = {}) {
1297
1443
  status: event.status,
1298
1444
  reason: event.reason,
1299
1445
  text: options.includeControlPayloads ? event.text : void 0,
1300
- metadata: options.includeMetadata ? event.metadata : void 0
1446
+ metadata: options.includeMetadata ? event.metadata : void 0,
1447
+ ...sanitizedError !== void 0 ? { error: sanitizedError } : {}
1301
1448
  };
1302
1449
  }
1303
1450
  return {
@@ -1423,7 +1570,16 @@ function pickPublicStreamFields(event) {
1423
1570
  if (event.type === "backend_start" || event.type === "backend_end")
1424
1571
  return { backend: event.backend };
1425
1572
  if (event.type === "backend_error") {
1426
- return { backend: event.backend, message: event.message, recoverable: event.recoverable };
1573
+ const sanitizedError = event.error !== void 0 ? {
1574
+ kind: event.error.kind,
1575
+ status: event.error.status
1576
+ } : void 0;
1577
+ return {
1578
+ backend: event.backend,
1579
+ message: event.message,
1580
+ recoverable: event.recoverable,
1581
+ ...sanitizedError !== void 0 ? { error: sanitizedError } : {}
1582
+ };
1427
1583
  }
1428
1584
  if (event.type === "task_end") return { status: event.status, reason: event.reason };
1429
1585
  if (event.type === "text_delta" || event.type === "reasoning_delta") return { text: event.text };
@@ -1511,6 +1667,7 @@ function stripNewlines(value) {
1511
1667
  }
1512
1668
  export {
1513
1669
  AgentEvalError,
1670
+ BackendTransportError,
1514
1671
  ConfigError,
1515
1672
  DEFAULT_ROUTER_BASE_URL,
1516
1673
  InMemoryRuntimeSessionStore,