@tangle-network/agent-runtime 0.126.0 → 0.131.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.
Files changed (146) hide show
  1. package/README.md +70 -20
  2. package/dist/{activation-DhWJ3p8N.js → activation-BCzMOTaV.js} +3 -3
  3. package/dist/{activation-DhWJ3p8N.js.map → activation-BCzMOTaV.js.map} +1 -1
  4. package/dist/agent.d.ts +2 -3
  5. package/dist/agent.js +4 -5
  6. package/dist/agent.js.map +1 -1
  7. package/dist/{analyst-loop-DvSciOfB.js → analyst-loop-BE8cDs5Q.js} +2 -2
  8. package/dist/{analyst-loop-DvSciOfB.js.map → analyst-loop-BE8cDs5Q.js.map} +1 -1
  9. package/dist/analyst-loop.d.ts +1 -1
  10. package/dist/analyst-loop.js +1 -1
  11. package/dist/authoring-CvHwo1oW.js +163 -0
  12. package/dist/authoring-CvHwo1oW.js.map +1 -0
  13. package/dist/candidate-execution/index.js +4 -4
  14. package/dist/{candidate-execution-BFpq-Xi6.js → candidate-execution-BNxKr-Bu.js} +5 -5
  15. package/dist/{candidate-execution-BFpq-Xi6.js.map → candidate-execution-BNxKr-Bu.js.map} +1 -1
  16. package/dist/{conversation-BpLQZGPH.js → conversation-DNtxaJ1Z.js} +113 -31
  17. package/dist/conversation-DNtxaJ1Z.js.map +1 -0
  18. package/dist/conversation.d.ts +2 -2
  19. package/dist/conversation.js +2 -2
  20. package/dist/environment-provider-CxvSd1W6.d.ts +86 -0
  21. package/dist/{environment-provider-DqFS6FSZ.js → environment-provider-Dyg8DtLK.js} +8 -4
  22. package/dist/environment-provider-Dyg8DtLK.js.map +1 -0
  23. package/dist/environment-provider.d.ts +1 -1
  24. package/dist/environment-provider.js +1 -1
  25. package/dist/graph-BJTxGOFB.js +471 -0
  26. package/dist/graph-BJTxGOFB.js.map +1 -0
  27. package/dist/{improvement-cycle-IJgCbKWQ.js → improvement-cycle-Csp38cWg.js} +133 -224
  28. package/dist/improvement-cycle-Csp38cWg.js.map +1 -0
  29. package/dist/{index-EdjCQBV9.d.ts → index-CoO7atyo.d.ts} +640 -1184
  30. package/dist/{index-DIV33AF5.d.ts → index-DwGtu9nc.d.ts} +7 -9
  31. package/dist/{index-Efjb3nrQ.d.ts → index-qYHpsmG2.d.ts} +36 -18
  32. package/dist/index.d.ts +353 -11
  33. package/dist/index.js +111 -354
  34. package/dist/index.js.map +1 -1
  35. package/dist/intelligence.d.ts +6 -6
  36. package/dist/intelligence.js +9 -8
  37. package/dist/intelligence.js.map +1 -1
  38. package/dist/kernel.d.ts +7 -5
  39. package/dist/kernel.js +13 -9
  40. package/dist/{knowledge-EnuEqm_Y.js → knowledge-ce0_uKCl.js} +19 -17
  41. package/dist/knowledge-ce0_uKCl.js.map +1 -0
  42. package/dist/knowledge.d.ts +1 -1
  43. package/dist/knowledge.js +1 -1
  44. package/dist/{loop-runner-bin-Bo29_fiD.d.ts → loop-runner-bin-BwgZ8m_7.d.ts} +6 -3
  45. package/dist/{loop-runner-bin-qwT_4F5I.js → loop-runner-bin-DSbuDDqM.js} +5 -27
  46. package/dist/loop-runner-bin-DSbuDDqM.js.map +1 -0
  47. package/dist/loop-runner-bin.d.ts +1 -1
  48. package/dist/loop-runner-bin.js +1 -1
  49. package/dist/materialization-COJ1UYQ-.js +272 -0
  50. package/dist/materialization-COJ1UYQ-.js.map +1 -0
  51. package/dist/mcp/bin.js +39 -47
  52. package/dist/mcp/bin.js.map +1 -1
  53. package/dist/mcp/index.d.ts +24 -30
  54. package/dist/mcp/index.js +66 -83
  55. package/dist/mcp/index.js.map +1 -1
  56. package/dist/mcp/memory-bin.js +1 -1
  57. package/dist/{memory-server-DL6cE2Ag.js → memory-server-5HEJH672.js} +2 -2
  58. package/dist/{memory-server-DL6cE2Ag.js.map → memory-server-5HEJH672.js.map} +1 -1
  59. package/dist/model-policy-CqziaqS1.js +232 -0
  60. package/dist/model-policy-CqziaqS1.js.map +1 -0
  61. package/dist/{openai-tools-B68JaOCx.d.ts → openai-tools-D3oMyVGl.d.ts} +2 -2
  62. package/dist/{openai-tools-Bp1KSkP6.js → openai-tools-ru75mLjq.js} +2 -2
  63. package/dist/openai-tools-ru75mLjq.js.map +1 -0
  64. package/dist/{prepare--8EvLqCr.js → prepare-DYWjVcPx.js} +169 -153
  65. package/dist/prepare-DYWjVcPx.js.map +1 -0
  66. package/dist/primeintellect/index.d.ts +7 -6
  67. package/dist/primeintellect/index.js +9 -11
  68. package/dist/primeintellect/index.js.map +1 -1
  69. package/dist/profiles.d.ts +21 -174
  70. package/dist/profiles.js +67 -276
  71. package/dist/profiles.js.map +1 -1
  72. package/dist/{protected-model-port-CXVfOUu_.js → protected-model-port-48ALLGxT.js} +2 -2
  73. package/dist/{protected-model-port-CXVfOUu_.js.map → protected-model-port-48ALLGxT.js.map} +1 -1
  74. package/dist/{redact-PQzmE1Jn.d.ts → redact-9_Gf-8m3.d.ts} +58 -69
  75. package/dist/{researcher-CoVqNhfI.js → researcher-Skz5-Uc8.js} +50 -26
  76. package/dist/researcher-Skz5-Uc8.js.map +1 -0
  77. package/dist/{run-layout-C2FGmZ3v.js → run-layout-CeJsEAom.js} +2 -2
  78. package/dist/{run-layout-C2FGmZ3v.js.map → run-layout-CeJsEAom.js.map} +1 -1
  79. package/dist/runtime-D-QfLbSd.d.ts +893 -0
  80. package/dist/{runtime-BzXz7OjS.js → runtime-hiAABiTk.js} +329 -854
  81. package/dist/runtime-hiAABiTk.js.map +1 -0
  82. package/dist/{sandbox-events-Yhd1GYWl.js → sandbox-events-CRDwc5WN.js} +42 -5
  83. package/dist/sandbox-events-CRDwc5WN.js.map +1 -0
  84. package/dist/snapshot-CXiiuHhL.js +21 -0
  85. package/dist/snapshot-CXiiuHhL.js.map +1 -0
  86. package/dist/{spawn-journal-DsZKDqeh.js → spawn-journal-saHQzqYi.js} +15 -21
  87. package/dist/spawn-journal-saHQzqYi.js.map +1 -0
  88. package/dist/stream-agent-turn-C852AgMT.d.ts +128 -0
  89. package/dist/stream-agent-turn-rYgaOLO0.js +910 -0
  90. package/dist/stream-agent-turn-rYgaOLO0.js.map +1 -0
  91. package/dist/{structural-rollout-zY0oqQzO.js → structural-rollout-3uxVGcg2.js} +459 -235
  92. package/dist/structural-rollout-3uxVGcg2.js.map +1 -0
  93. package/dist/{supervise-Ds8FtyI9.js → supervise-iPN27pO0.js} +932 -4771
  94. package/dist/supervise-iPN27pO0.js.map +1 -0
  95. package/dist/{supervisor-DpjO0Gmy.js → supervisor-CV6Jh28D.js} +7439 -2897
  96. package/dist/supervisor-CV6Jh28D.js.map +1 -0
  97. package/dist/testing.d.ts +3 -1
  98. package/dist/testing.js +271 -221
  99. package/dist/testing.js.map +1 -1
  100. package/dist/{top-app-5unxqovu.js → top-app-G4M18b6i.js} +3 -3
  101. package/dist/{top-app-5unxqovu.js.map → top-app-G4M18b6i.js.map} +1 -1
  102. package/dist/tui/bin.js +1 -1
  103. package/dist/tui/index.js +1 -1
  104. package/dist/{environment-provider-PM9PeW_J.d.ts → types-C6Q-J0Dt.d.ts} +57 -114
  105. package/dist/{types-DnNGJ5Gz.d.ts → types-ebIY0dMG.d.ts} +556 -30
  106. package/dist/{util-MVgdwuIS.js → util-Bw6srryQ.js} +3 -2
  107. package/dist/{util-MVgdwuIS.js.map → util-Bw6srryQ.js.map} +1 -1
  108. package/dist/{workspace-archive-BQxvkypI.js → workspace-archive-aOfJ47ms.js} +3 -3
  109. package/dist/{workspace-archive-BQxvkypI.js.map → workspace-archive-aOfJ47ms.js.map} +1 -1
  110. package/package.json +12 -15
  111. package/skills/agent-graphs/IMPROVE.md +58 -0
  112. package/skills/agent-graphs/SKILL.md +139 -0
  113. package/skills/agent-graphs/cases/artifact-mission-release-notes.json +10 -0
  114. package/skills/agent-graphs/cases/audited-single-writer.json +9 -0
  115. package/skills/agent-graphs/cases/cap-as-stop-mistake.json +8 -0
  116. package/skills/agent-graphs/cases/mission-in-deliverable.json +8 -0
  117. package/skills/agent-graphs/cases/review-pipeline.json +13 -0
  118. package/skills/agent-graphs/cases/runtime-discovered-fanout.json +8 -0
  119. package/skills/agent-graphs/cases/single-agent-suffices.json +7 -0
  120. package/skills/agent-graphs/cases/steer-heavy-drafting.json +9 -0
  121. package/skills/agent-graphs/cases/unmeasured-harness.json +7 -0
  122. package/skills/agent-graphs/generations/gen1-baseline.json +248 -0
  123. package/skills/agent-graphs/generations/gen2.json +375 -0
  124. package/skills/agent-graphs/generations/gen3.json +702 -0
  125. package/skills/build-with-agent-runtime/SKILL.md +1 -0
  126. package/dist/backends-CiOCyRHb.js +0 -743
  127. package/dist/backends-CiOCyRHb.js.map +0 -1
  128. package/dist/conversation-BpLQZGPH.js.map +0 -1
  129. package/dist/environment-provider-DqFS6FSZ.js.map +0 -1
  130. package/dist/improvement-cycle-IJgCbKWQ.js.map +0 -1
  131. package/dist/index-D_M4d1_B.d.ts +0 -545
  132. package/dist/knowledge-EnuEqm_Y.js.map +0 -1
  133. package/dist/local-harness-BIajef4A.d.ts +0 -465
  134. package/dist/loop-runner-bin-qwT_4F5I.js.map +0 -1
  135. package/dist/model-resolution-Btd9iIKV.js +0 -98
  136. package/dist/model-resolution-Btd9iIKV.js.map +0 -1
  137. package/dist/openai-tools-Bp1KSkP6.js.map +0 -1
  138. package/dist/prepare--8EvLqCr.js.map +0 -1
  139. package/dist/researcher-CoVqNhfI.js.map +0 -1
  140. package/dist/runtime-BzXz7OjS.js.map +0 -1
  141. package/dist/sandbox-events-Yhd1GYWl.js.map +0 -1
  142. package/dist/spawn-journal-DsZKDqeh.js.map +0 -1
  143. package/dist/structural-rollout-zY0oqQzO.js.map +0 -1
  144. package/dist/supervise-Ds8FtyI9.js.map +0 -1
  145. package/dist/supervisor-DpjO0Gmy.js.map +0 -1
  146. package/dist/types-C9j4qg6l.d.ts +0 -500
@@ -1,743 +0,0 @@
1
- import { l as SessionMismatchError, r as BackendTransportError } from "./errors-DEAvWQPy.js";
2
- //#region src/sessions.ts
3
- /**
4
- *
5
- * Session helpers + an in-memory `RuntimeSessionStore` implementation suitable
6
- * for tests, scratch processes, and per-request scratch storage in serverless
7
- * runtimes. Durable stores (D1, postgres, Durable Objects) implement the same
8
- * interface from `./types`.
9
- *
10
- * @stable
11
- */
12
- /** @internal */
13
- function newRuntimeSession(backend, requestedId, metadata) {
14
- const now = nowIso();
15
- return {
16
- id: requestedId || crypto.randomUUID(),
17
- backend,
18
- status: "active",
19
- createdAt: now,
20
- updatedAt: now,
21
- metadata
22
- };
23
- }
24
- /** @internal */
25
- function touchSession(session) {
26
- return {
27
- ...session,
28
- updatedAt: nowIso()
29
- };
30
- }
31
- /** @internal */
32
- function nowIso() {
33
- return (/* @__PURE__ */ new Date()).toISOString();
34
- }
35
- /** Start or resume one backend session and persist its current identity. @internal */
36
- async function startOrResumeRuntimeSession(options) {
37
- const existing = options.resume && options.sessionId ? await options.store?.get(options.sessionId) : void 0;
38
- if (!existing) {
39
- const input = resolveSessionInput(options.input);
40
- const session = options.backend.start ? await options.backend.start(input, {
41
- ...options.context,
42
- requestedSessionId: options.sessionId
43
- }) : newRuntimeSession(options.backend.kind, options.sessionId);
44
- assertSessionBackend(session, options.backend.kind);
45
- options.validateSession?.(session);
46
- await options.store?.put(session);
47
- return {
48
- session,
49
- input,
50
- resumed: false
51
- };
52
- }
53
- assertSessionBackend(existing, options.backend.kind);
54
- const input = resolveSessionInput(options.backend.resume !== void 0 ? options.continuationInput ?? options.input : options.input);
55
- const session = options.backend.resume ? await options.backend.resume(existing, input, options.context) : touchSession({
56
- ...existing,
57
- status: "active"
58
- });
59
- assertSessionBackend(session, options.backend.kind);
60
- options.validateSession?.(session);
61
- await options.store?.put(session);
62
- return {
63
- session,
64
- input,
65
- resumed: true
66
- };
67
- }
68
- function resolveSessionInput(input) {
69
- return typeof input === "function" ? input() : input;
70
- }
71
- function assertSessionBackend(session, backend) {
72
- if (session.backend !== backend) throw new SessionMismatchError(session.backend, backend);
73
- }
74
- /** In-memory `RuntimeSessionStore` for single-process use and tests. @stable */
75
- var InMemoryRuntimeSessionStore = class {
76
- sessions = /* @__PURE__ */ new Map();
77
- events = /* @__PURE__ */ new Map();
78
- get(sessionId) {
79
- return this.sessions.get(sessionId);
80
- }
81
- put(session) {
82
- this.sessions.set(session.id, session);
83
- }
84
- appendEvent(sessionId, event) {
85
- const existing = this.events.get(sessionId) ?? [];
86
- existing.push(event);
87
- this.events.set(sessionId, existing);
88
- }
89
- listEvents(sessionId) {
90
- return [...this.events.get(sessionId) ?? []];
91
- }
92
- };
93
- //#endregion
94
- //#region src/backends.ts
95
- /**
96
- *
97
- * Backend factories for `runAgentTaskStream`. Three shapes ship in core:
98
- *
99
- * - `createIterableBackend` — wrap any custom async iterable into a backend
100
- * - `createSandboxPromptBackend` — sandbox / sidecar `streamPrompt` clients
101
- * - `createOpenAICompatibleBackend` — OpenAI-style chat completions endpoints
102
- *
103
- * Adapters stay thin: domain repos own auth, model selection, and the concrete
104
- * tool surface. The factories handle session creation, stream normalization,
105
- * and graceful end-of-stream signalling.
106
- *
107
- * @stable
108
- */
109
- /** Wrap any custom async-iterable stream into a typed `AgentExecutionBackend`. @stable */
110
- function createIterableBackend(options) {
111
- return options;
112
- }
113
- /** Build an `AgentExecutionBackend` backed by a sandbox/sidecar `streamPrompt` call. @stable */
114
- function createSandboxPromptBackend(options) {
115
- const kind = options.kind ?? "sandbox";
116
- return {
117
- kind,
118
- async start(input, context) {
119
- const box = await options.getBox(input, context);
120
- return newRuntimeSession(kind, options.getSessionId?.(box, input) ?? context.requestedSessionId, { resumable: true });
121
- },
122
- resume(session) {
123
- return touchSession({
124
- ...session,
125
- status: "active"
126
- });
127
- },
128
- async *stream(input, context) {
129
- const box = await options.getBox(input, context);
130
- const message = input.message ?? input.messages?.at(-1)?.content ?? context.task.intent;
131
- for await (const event of options.streamPrompt(box, message, context)) {
132
- const mapped = options.mapEvent?.(event, context) ?? mapCommonBackendEvent(event, context);
133
- if (mapped) yield mapped;
134
- }
135
- }
136
- };
137
- }
138
- const DEFAULT_RETRY_STATUSES = [
139
- 408,
140
- 425,
141
- 429,
142
- 500,
143
- 502,
144
- 503,
145
- 504
146
- ];
147
- function pickRetryDelayMs(attempt, policy) {
148
- const exp = policy.initialBackoffMs * 2 ** (attempt - 1);
149
- const capped = Math.min(exp, policy.maxBackoffMs);
150
- const jitter = capped * policy.jitter * (Math.random() * 2 - 1);
151
- return Math.max(0, Math.round(capped + jitter));
152
- }
153
- /**
154
- * Derive a per-attempt AbortSignal that fires when EITHER the caller's
155
- * signal aborts OR `timeoutMs` elapses. `dispose()` clears the timer so a
156
- * completed attempt doesn't leak a pending timeout. `timeoutMs <= 0`
157
- * disables the deadline (caller signal still propagates).
158
- */
159
- function withTimeout(callerSignal, timeoutMs) {
160
- if (timeoutMs <= 0) return {
161
- signal: callerSignal ?? new AbortController().signal,
162
- dispose: () => void 0
163
- };
164
- const controller = new AbortController();
165
- const timer = setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`request timed out after ${timeoutMs}ms`)), timeoutMs);
166
- if (typeof timer.unref === "function") timer.unref();
167
- const onCallerAbort = () => controller.abort(callerSignal?.reason ?? /* @__PURE__ */ new Error("aborted"));
168
- if (callerSignal) if (callerSignal.aborted) onCallerAbort();
169
- else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
170
- return {
171
- signal: controller.signal,
172
- dispose: () => {
173
- clearTimeout(timer);
174
- callerSignal?.removeEventListener("abort", onCallerAbort);
175
- }
176
- };
177
- }
178
- function sleep(ms, signal) {
179
- return new Promise((resolve, reject) => {
180
- if (signal?.aborted) {
181
- reject(signal.reason ?? /* @__PURE__ */ new Error("aborted"));
182
- return;
183
- }
184
- const t = setTimeout(() => {
185
- signal?.removeEventListener("abort", onAbort);
186
- resolve();
187
- }, ms);
188
- const onAbort = () => {
189
- clearTimeout(t);
190
- reject(signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
191
- };
192
- signal?.addEventListener("abort", onAbort, { once: true });
193
- });
194
- }
195
- /**
196
- *
197
- * OpenAI-compat streaming backend. Routes `runAgentTaskStream` through any
198
- * `POST /chat/completions` endpoint that speaks OpenAI's SSE protocol —
199
- * Tangle Router, OpenAI direct, OpenRouter, Groq, DeepSeek, Together. The
200
- * router also fronts Anthropic models in Anthropic-native SSE shape; this
201
- * backend handles both.
202
- *
203
- * ### Tool calls
204
- *
205
- * Pass `tools` (and optionally `toolChoice`) to forward an OpenAI Chat
206
- * Completions `tools[]` array on every request. Streamed `tool_call` chunks
207
- * are buffered until the model finalizes them (either `finish_reason:
208
- * 'tool_calls'` for OpenAI shape or a `content_block_stop` for Anthropic
209
- * `tool_use` blocks proxied through the router), then emitted as a single
210
- * `tool_call` RuntimeStreamEvent with the assembled `args`.
211
- *
212
- * The backend does NOT execute tools — it surfaces calls for the caller's
213
- * own dispatcher (typically the product's MCP / sandbox runtime) to fulfill
214
- * and feed back as a subsequent `messages` turn. This keeps the transport
215
- * thin and lets the agent host own tool dispatch policy.
216
- *
217
- * ### Fail-loud errors
218
- *
219
- * Non-success HTTP responses (4xx/5xx) and exhausted retry budgets throw
220
- * `BackendTransportError` from inside the `stream()` generator. The runtime
221
- * catches the throw, yields a `backend_error` with a typed `error` field
222
- * (`kind`, `status`, truncated `body`) and a terminal `final` event with
223
- * `status: 'failed'` carrying the same detail. Consumers MUST map
224
- * `final.error` onto their `RunRecord.error` — silently treating an empty
225
- * `finalText` as "agent produced nothing" hides credit exhaustion, auth
226
- * failure, and upstream outages.
227
- *
228
- * @stable
229
- */
230
- function createOpenAICompatibleBackend(options) {
231
- const fetcher = options.fetchImpl ?? fetch;
232
- const kind = options.kind ?? "tcloud";
233
- const retryPolicy = {
234
- maxAttempts: options.retry?.maxAttempts ?? 5,
235
- initialBackoffMs: options.retry?.initialBackoffMs ?? 1e3,
236
- maxBackoffMs: options.retry?.maxBackoffMs ?? 3e4,
237
- jitter: options.retry?.jitter ?? .25,
238
- retryStatuses: options.retry?.retryStatuses ?? DEFAULT_RETRY_STATUSES,
239
- requestTimeoutMs: options.retry?.requestTimeoutMs ?? 12e4
240
- };
241
- return {
242
- kind,
243
- start(_input, context) {
244
- return newRuntimeSession(kind, context.requestedSessionId);
245
- },
246
- async *stream(input, context) {
247
- const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
248
- const bodyPayload = {
249
- model: options.model,
250
- stream: true,
251
- stream_options: { include_usage: true },
252
- messages: input.messages ?? [{
253
- role: "user",
254
- content: input.message ?? context.task.intent
255
- }]
256
- };
257
- if (options.tools && options.tools.length > 0) {
258
- bodyPayload.tools = options.tools;
259
- if (options.toolChoice !== void 0) bodyPayload.tool_choice = options.toolChoice;
260
- }
261
- if (options.responseFormat !== void 0) bodyPayload.response_format = options.responseFormat;
262
- if (options.temperature !== void 0) bodyPayload.temperature = options.temperature;
263
- if (options.maxTokens !== void 0) bodyPayload.max_tokens = options.maxTokens;
264
- const requestBody = JSON.stringify(bodyPayload);
265
- let response;
266
- let lastStatus = 0;
267
- let lastThrown;
268
- for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt++) {
269
- lastThrown = void 0;
270
- const attemptSignal = withTimeout(context.signal, retryPolicy.requestTimeoutMs);
271
- try {
272
- response = await fetcher(url, {
273
- method: "POST",
274
- headers: {
275
- Authorization: `Bearer ${options.apiKey}`,
276
- "Content-Type": "application/json",
277
- ...context.propagatedHeaders ?? {}
278
- },
279
- body: requestBody,
280
- signal: attemptSignal.signal
281
- });
282
- } catch (err) {
283
- attemptSignal.dispose();
284
- if (context.signal?.aborted) throw err;
285
- lastThrown = err;
286
- response = void 0;
287
- if (attempt === retryPolicy.maxAttempts) break;
288
- await sleep(pickRetryDelayMs(attempt, retryPolicy), context.signal);
289
- continue;
290
- }
291
- attemptSignal.dispose();
292
- if (response.ok) break;
293
- lastStatus = response.status;
294
- if (!retryPolicy.retryStatuses.includes(response.status)) break;
295
- if (attempt === retryPolicy.maxAttempts) break;
296
- try {
297
- await response.body?.cancel();
298
- } catch {}
299
- await sleep(pickRetryDelayMs(attempt, retryPolicy), context.signal);
300
- }
301
- if (!response) {
302
- const reason = lastThrown instanceof Error ? lastThrown.message : String(lastThrown);
303
- throw new BackendTransportError(kind, `chat backend unreachable after ${retryPolicy.maxAttempts} attempts: ${reason}`, { status: 0 });
304
- }
305
- if (!response.ok) {
306
- let body;
307
- try {
308
- const raw = await response.text();
309
- body = raw.length > MAX_ERROR_BODY_BYTES ? `${raw.slice(0, MAX_ERROR_BODY_BYTES)}…` : raw;
310
- } catch {
311
- body = void 0;
312
- }
313
- throw new BackendTransportError(kind, `chat backend returned ${lastStatus || "unknown"}`, {
314
- status: lastStatus || 0,
315
- body
316
- });
317
- }
318
- yield* streamResponseEvents(response, context, options.model);
319
- }
320
- };
321
- }
322
- /**
323
- * Cap the captured error body. 2 KiB is enough to carry a JSON error envelope
324
- * with a structured `code`/`message` payload (the router returns ~150 bytes
325
- * for a free-tier denial; OpenAI returns ~300 bytes for invalid auth) without
326
- * letting an HTML error page balloon persisted events.
327
- */
328
- const MAX_ERROR_BODY_BYTES = 2048;
329
- /** @internal */
330
- function normalizeBackendStreamEvent(event, task, session) {
331
- if ("task" in event && event.task && "session" in event && event.session && "timestamp" in event && event.timestamp) return event;
332
- return {
333
- ...event,
334
- task: "task" in event && event.task ? event.task : task,
335
- session: "session" in event && event.session ? event.session : session,
336
- timestamp: "timestamp" in event && event.timestamp ? event.timestamp : nowIso()
337
- };
338
- }
339
- function mapCommonBackendEvent(event, context) {
340
- if (!event || typeof event !== "object") return void 0;
341
- const record = event;
342
- const type = String(record.type ?? "");
343
- const data = record.data && typeof record.data === "object" ? record.data : record;
344
- if (type === "message.part.updated" || type === "text_delta" || type === "delta") {
345
- const part = data.part;
346
- const partText = part !== void 0 && typeof part === "object" && (part.type === "text" || part.type === void 0) ? stringValue(part.text) : void 0;
347
- const text = stringValue(data.text) ?? stringValue(data.delta) ?? stringValue(record.text) ?? partText;
348
- return text ? {
349
- type: "text_delta",
350
- task: context.task,
351
- session: context.session,
352
- text,
353
- timestamp: nowIso()
354
- } : void 0;
355
- }
356
- if (type === "reasoning_delta") {
357
- const text = stringValue(data.text) ?? stringValue(record.text);
358
- return text ? {
359
- type: "reasoning_delta",
360
- task: context.task,
361
- session: context.session,
362
- text,
363
- timestamp: nowIso()
364
- } : void 0;
365
- }
366
- if (type === "tool_call") return {
367
- type: "tool_call",
368
- task: context.task,
369
- session: context.session,
370
- toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
371
- toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
372
- args: data.args ?? data.input ?? record.args,
373
- timestamp: nowIso()
374
- };
375
- if (type === "tool_result") return {
376
- type: "tool_result",
377
- task: context.task,
378
- session: context.session,
379
- toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
380
- toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
381
- result: data.result ?? data.output ?? record.result,
382
- timestamp: nowIso()
383
- };
384
- if (type === "artifact") {
385
- const artifactId = stringValue(data.artifactId) ?? stringValue(data.id) ?? stringValue(record.artifactId);
386
- if (!artifactId) return void 0;
387
- return {
388
- type: "artifact",
389
- task: context.task,
390
- session: context.session,
391
- artifactId,
392
- name: stringValue(data.name) ?? stringValue(record.name),
393
- mimeType: stringValue(data.mimeType) ?? stringValue(record.mimeType),
394
- uri: stringValue(data.uri) ?? stringValue(record.uri),
395
- content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
396
- metadata: data.metadata && typeof data.metadata === "object" ? data.metadata : void 0,
397
- timestamp: nowIso()
398
- };
399
- }
400
- if (type === "proposal_created" || type === "proposal" || type === "filing") {
401
- const proposalId = stringValue(data.proposalId) ?? stringValue(data.id) ?? stringValue(record.proposalId);
402
- if (!proposalId) return void 0;
403
- const status = stringValue(data.status) ?? stringValue(record.status);
404
- return {
405
- type: "proposal_created",
406
- task: context.task,
407
- session: context.session,
408
- proposalId,
409
- title: stringValue(data.title) ?? stringValue(record.title) ?? proposalId,
410
- status: status === "pending" || status === "approved" || status === "rejected" ? status : void 0,
411
- content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
412
- timestamp: nowIso()
413
- };
414
- }
415
- if (type === "result" || type === "final") {
416
- const text = stringValue(data.finalText) ?? stringValue(data.text) ?? stringValue(record.text);
417
- return text ? {
418
- type: "text_delta",
419
- task: context.task,
420
- session: context.session,
421
- text,
422
- timestamp: nowIso()
423
- } : void 0;
424
- }
425
- }
426
- async function* streamResponseEvents(response, context, requestedModel) {
427
- const body = response.body;
428
- if (!body) return;
429
- const reader = body.getReader();
430
- const decoder = new TextDecoder();
431
- let buffer = "";
432
- const usage = { saw: false };
433
- const toolCalls = /* @__PURE__ */ new Map();
434
- const startedAt = Date.now();
435
- for (;;) {
436
- const { done, value } = await reader.read();
437
- if (done) break;
438
- buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
439
- for (const event of drainStreamBuffer(false)) yield event;
440
- }
441
- buffer += decoder.decode().replace(/\r\n/g, "\n");
442
- for (const event of drainStreamBuffer(true)) yield event;
443
- if (buffer.trim()) for (const event of parseStreamChunk(buffer, context, usage, toolCalls)) yield event;
444
- for (const event of flushPendingToolCalls(toolCalls, context)) yield event;
445
- if (usage.saw) yield {
446
- type: "llm_call",
447
- task: context.task,
448
- session: context.session,
449
- model: usage.model ?? requestedModel,
450
- tokensIn: usage.tokensIn,
451
- tokensOut: usage.tokensOut,
452
- latencyMs: Date.now() - startedAt,
453
- finishReason: usage.finishReason,
454
- timestamp: nowIso()
455
- };
456
- function* drainStreamBuffer(flush) {
457
- for (;;) {
458
- const sseBoundary = buffer.indexOf("\n\n");
459
- if (sseBoundary >= 0) {
460
- const chunk = buffer.slice(0, sseBoundary);
461
- buffer = buffer.slice(sseBoundary + 2);
462
- for (const event of parseStreamChunk(chunk, context, usage, toolCalls)) yield event;
463
- continue;
464
- }
465
- const newline = buffer.indexOf("\n");
466
- if (newline >= 0 && !buffer.slice(0, newline).startsWith("data:")) {
467
- const line = buffer.slice(0, newline);
468
- buffer = buffer.slice(newline + 1);
469
- for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
470
- continue;
471
- }
472
- if (flush && buffer.trim() && !buffer.trimStart().startsWith("data:")) {
473
- const line = buffer;
474
- buffer = "";
475
- for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
476
- continue;
477
- }
478
- break;
479
- }
480
- }
481
- }
482
- function* parseStreamChunk(chunk, context, usage, toolCalls) {
483
- const lines = chunk.split(/\r?\n/);
484
- const dataLines = lines.filter((line) => line.startsWith("data:"));
485
- if (dataLines.length === 0 && lines.every((line) => {
486
- const trimmed = line.trim();
487
- return trimmed.length === 0 || trimmed.startsWith(":");
488
- })) return;
489
- const data = dataLines.length > 0 ? dataLines.map((line) => line.slice(5).trimStart()).join("\n") : chunk.trim();
490
- if (!data || data === "[DONE]") return;
491
- let parsed;
492
- try {
493
- parsed = JSON.parse(data);
494
- } catch {
495
- yield {
496
- type: "text_delta",
497
- task: context.task,
498
- session: context.session,
499
- text: data,
500
- timestamp: nowIso()
501
- };
502
- return;
503
- }
504
- captureStreamUsage(parsed, usage);
505
- const choices = parsed.choices;
506
- const choice = Array.isArray(choices) ? choices[0] : void 0;
507
- const delta = choice?.delta;
508
- const message = choice?.message;
509
- const deltaToolCalls = delta?.tool_calls;
510
- if (Array.isArray(deltaToolCalls)) for (const tc of deltaToolCalls) {
511
- if (!tc || typeof tc !== "object") continue;
512
- const rec = tc;
513
- const id = stringValue(rec.id);
514
- const key = deltaToolCallKey(rec, id, toolCalls);
515
- const acc = toolCalls.get(key) ?? {
516
- argsRaw: "",
517
- source: "openai",
518
- finalized: false
519
- };
520
- if (id) acc.id = id;
521
- const fn = rec.function;
522
- const name = stringValue(fn?.name);
523
- if (name) acc.name = name;
524
- const args = stringValue(fn?.arguments);
525
- if (args) acc.argsRaw += args;
526
- toolCalls.set(key, acc);
527
- }
528
- const messageToolCalls = message?.tool_calls;
529
- if (Array.isArray(messageToolCalls)) for (const tc of messageToolCalls) {
530
- if (!tc || typeof tc !== "object") continue;
531
- const rec = tc;
532
- const fn = rec.function;
533
- const key = `openai:${numberValue(rec.index) ?? messageToolCalls.indexOf(tc)}`;
534
- const acc = toolCalls.get(key) ?? {
535
- argsRaw: "",
536
- source: "openai",
537
- finalized: false
538
- };
539
- const id = stringValue(rec.id);
540
- if (id) acc.id = id;
541
- const name = stringValue(fn?.name);
542
- if (name) acc.name = name;
543
- const args = stringValue(fn?.arguments);
544
- if (args) acc.argsRaw += args;
545
- acc.finalized = true;
546
- toolCalls.set(key, acc);
547
- }
548
- if (stringValue(choice?.finish_reason) === "tool_calls") for (const [key, acc] of toolCalls) {
549
- if (acc.source === "openai" && !acc.finalized) acc.finalized = true;
550
- toolCalls.set(key, acc);
551
- }
552
- const eventType = stringValue(parsed.type);
553
- if (eventType === "content_block_start") {
554
- const block = parsed.content_block;
555
- if (block && stringValue(block.type) === "tool_use") {
556
- const key = `anthropic:${numberValue(parsed.index) ?? 0}`;
557
- toolCalls.set(key, {
558
- id: stringValue(block.id),
559
- name: stringValue(block.name),
560
- argsRaw: "",
561
- source: "anthropic",
562
- finalized: false
563
- });
564
- }
565
- }
566
- if (eventType === "content_block_delta") {
567
- const d = parsed.delta;
568
- if (stringValue(d?.type) === "input_json_delta") {
569
- const key = `anthropic:${numberValue(parsed.index) ?? 0}`;
570
- const acc = toolCalls.get(key);
571
- if (acc) {
572
- const partial = stringValue(d?.partial_json) ?? "";
573
- acc.argsRaw += partial;
574
- toolCalls.set(key, acc);
575
- }
576
- } else {
577
- const text = stringValue(d?.text);
578
- if (text) yield {
579
- type: "text_delta",
580
- task: context.task,
581
- session: context.session,
582
- text,
583
- timestamp: nowIso()
584
- };
585
- }
586
- }
587
- if (eventType === "content_block_stop") {
588
- const key = `anthropic:${numberValue(parsed.index) ?? 0}`;
589
- const acc = toolCalls.get(key);
590
- if (acc) {
591
- acc.finalized = true;
592
- toolCalls.set(key, acc);
593
- }
594
- }
595
- for (const event of drainFinalizedToolCalls(toolCalls, context)) yield event;
596
- const text = stringValue(delta?.content) ?? stringValue(message?.content) ?? stringValue(parsed.text);
597
- if (text) {
598
- yield {
599
- type: "text_delta",
600
- task: context.task,
601
- session: context.session,
602
- text,
603
- timestamp: nowIso()
604
- };
605
- return;
606
- }
607
- const mapped = mapCommonBackendEvent(parsed, context);
608
- if (mapped) yield mapped;
609
- }
610
- function* drainFinalizedToolCalls(toolCalls, context) {
611
- for (const [key, acc] of toolCalls) {
612
- if (!acc.finalized) continue;
613
- toolCalls.delete(key);
614
- yield buildToolCallEvent(acc, context);
615
- }
616
- }
617
- function* flushPendingToolCalls(toolCalls, context) {
618
- for (const [key, acc] of toolCalls) {
619
- toolCalls.delete(key);
620
- yield buildToolCallEvent(acc, context);
621
- }
622
- }
623
- /**
624
- * The accumulator key one streamed `delta.tool_calls[]` entry belongs to.
625
- *
626
- * OpenAI's streaming contract puts `index` on every fragment, so fragments of
627
- * the same call concatenate under `openai:<index>`. Some OpenAI-COMPATIBLE
628
- * gateways do not: the Tangle router's Gemini lane emits N COMPLETE tool calls
629
- * inside a single delta, each with a distinct `id` and no `index` at all.
630
- * Defaulting those to index 0 concatenated every call's `arguments` into one
631
- * string, which then failed `JSON.parse` and was surfaced as a raw string — so
632
- * a turn that asked for six deliverables silently produced none.
633
- *
634
- * Resolution order:
635
- * 1. `index` present → spec path, key by index (cross-delta fragments join).
636
- * 2. `id` present → key by id; distinct ids are distinct calls, and a
637
- * repeated id still concatenates correctly.
638
- * 3. neither → an argument-only continuation fragment; append to the
639
- * last-OPENED unfinalized OpenAI entry. `Map` iteration
640
- * is insertion-ordered and `set` on an existing key does
641
- * not reorder, so this is the most recently STARTED call
642
- * still taking arguments — which is the one a gateway
643
- * streaming calls sequentially is continuing.
644
- */
645
- function deltaToolCallKey(rec, id, toolCalls) {
646
- const idx = numberValue(rec.index);
647
- if (idx !== void 0) return `openai:${idx}`;
648
- if (id) return `openai:id:${id}`;
649
- let last;
650
- for (const [key, acc] of toolCalls) if (acc.source === "openai" && !acc.finalized) last = key;
651
- return last ?? "openai:0";
652
- }
653
- function buildToolCallEvent(acc, context) {
654
- let args = acc.argsRaw;
655
- if (acc.argsRaw.length > 0) try {
656
- args = JSON.parse(acc.argsRaw);
657
- } catch {
658
- args = acc.argsRaw;
659
- }
660
- else args = {};
661
- return {
662
- type: "tool_call",
663
- task: context.task,
664
- session: context.session,
665
- toolName: acc.name ?? "tool",
666
- toolCallId: acc.id,
667
- args,
668
- timestamp: nowIso()
669
- };
670
- }
671
- /**
672
- * Accumulate token usage from any SSE chunk shape the router may emit.
673
- *
674
- * - OpenAI: a final chunk before `[DONE]` with `{ usage: { prompt_tokens,
675
- * completion_tokens, total_tokens } }` and (often) empty `choices`. The
676
- * `model` field is on every chunk and the last `choices[0].finish_reason`
677
- * carries the stop reason.
678
- * - Anthropic: `message_start` carries `message.model` and
679
- * `message.usage.input_tokens`. The terminal `message_delta` carries
680
- * `usage.output_tokens` and `delta.stop_reason`.
681
- */
682
- function captureStreamUsage(parsed, usage) {
683
- const model = stringValue(parsed.model);
684
- if (model && !usage.model) usage.model = model;
685
- const openAiUsage = parsed.usage;
686
- if (openAiUsage && typeof openAiUsage === "object") {
687
- const promptTokens = numberValue(openAiUsage.prompt_tokens);
688
- const completionTokens = numberValue(openAiUsage.completion_tokens);
689
- const inputTokens = numberValue(openAiUsage.input_tokens);
690
- const outputTokens = numberValue(openAiUsage.output_tokens);
691
- if (promptTokens !== void 0) {
692
- usage.tokensIn = promptTokens;
693
- usage.saw = true;
694
- } else if (inputTokens !== void 0) {
695
- usage.tokensIn = (usage.tokensIn ?? 0) + inputTokens;
696
- usage.saw = true;
697
- }
698
- if (completionTokens !== void 0) {
699
- usage.tokensOut = completionTokens;
700
- usage.saw = true;
701
- } else if (outputTokens !== void 0) {
702
- usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
703
- usage.saw = true;
704
- }
705
- }
706
- const type = stringValue(parsed.type);
707
- if (type === "message_start") {
708
- const message = parsed.message;
709
- const messageModel = stringValue(message?.model);
710
- if (messageModel && !usage.model) usage.model = messageModel;
711
- const messageUsage = message?.usage;
712
- const inputTokens = numberValue(messageUsage?.input_tokens);
713
- if (inputTokens !== void 0) {
714
- usage.tokensIn = inputTokens;
715
- usage.saw = true;
716
- }
717
- const outputTokens = numberValue(messageUsage?.output_tokens);
718
- if (outputTokens !== void 0) {
719
- usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
720
- usage.saw = true;
721
- }
722
- }
723
- if (type === "message_delta") {
724
- const delta = parsed.delta;
725
- const stopReason = stringValue(delta?.stop_reason);
726
- if (stopReason) usage.finishReason = stopReason;
727
- }
728
- const choices = parsed.choices;
729
- if (Array.isArray(choices)) {
730
- const finishReason = stringValue(choices[0]?.finish_reason);
731
- if (finishReason) usage.finishReason = finishReason;
732
- }
733
- }
734
- function numberValue(value) {
735
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
736
- }
737
- function stringValue(value) {
738
- return typeof value === "string" && value.length > 0 ? value : void 0;
739
- }
740
- //#endregion
741
- export { InMemoryRuntimeSessionStore as a, startOrResumeRuntimeSession as c, normalizeBackendStreamEvent as i, touchSession as l, createOpenAICompatibleBackend as n, newRuntimeSession as o, createSandboxPromptBackend as r, nowIso as s, createIterableBackend as t };
742
-
743
- //# sourceMappingURL=backends-CiOCyRHb.js.map