@tangle-network/agent-runtime 0.21.0 → 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/README.md +93 -1
- package/dist/agent.d.ts +1 -1
- package/dist/{chunk-EDVCVFQB.js → chunk-724Y4KHY.js} +2 -2
- package/dist/{chunk-Z5LKAYAS.js → chunk-CBQVID7G.js} +2 -2
- package/dist/{chunk-4XA4TWKW.js → chunk-FTWTA6YE.js} +5 -5
- package/dist/{chunk-4XA4TWKW.js.map → chunk-FTWTA6YE.js.map} +1 -1
- package/dist/{chunk-XLWPTPRP.js → chunk-URDSRUPQ.js} +2 -2
- package/dist/{chunk-RZAOYKCO.js → chunk-XZYF3YJN.js} +9 -1
- package/dist/{chunk-RZAOYKCO.js.map → chunk-XZYF3YJN.js.map} +1 -1
- package/dist/index.d.ts +76 -4
- package/dist/index.js +198 -41
- package/dist/index.js.map +1 -1
- package/dist/loops.d.ts +4 -4
- package/dist/loops.js +3 -3
- package/dist/mcp/bin.js +5 -5
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +5 -5
- package/dist/profiles.d.ts +3 -3
- package/dist/profiles.js +3 -3
- package/dist/{runtime-run-B2j-hvBj.d.ts → runtime-run-D5ItCKl_.d.ts} +1 -1
- package/dist/{types-DvJIha6w.d.ts → types-BFgFD_sl.d.ts} +87 -1
- package/dist/{types-Cu-SkGa0.d.ts → types-Ps7-2gJC.d.ts} +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-EDVCVFQB.js.map → chunk-724Y4KHY.js.map} +0 -0
- /package/dist/{chunk-Z5LKAYAS.js.map → chunk-CBQVID7G.js.map} +0 -0
- /package/dist/{chunk-XLWPTPRP.js.map → chunk-URDSRUPQ.js.map} +0 -0
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,
|
|
4
|
-
export {
|
|
5
|
-
export { R as RuntimeRunHandle, a as RuntimeRunPersistenceAdapter, b as RuntimeRunRow, s as startRuntimeRun } from './runtime-run-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
392
|
+
if (!data || data === "[DONE]") return;
|
|
393
|
+
let parsed;
|
|
381
394
|
try {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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,
|