@tangle-network/agent-runtime 0.105.0 → 0.106.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.
Files changed (218) hide show
  1. package/README.md +2 -2
  2. package/dist/activation-CRjVfOiy.js +203 -0
  3. package/dist/activation-CRjVfOiy.js.map +1 -0
  4. package/dist/activation-DyWB0K6E.d.ts +98 -0
  5. package/dist/agent.d.ts +216 -234
  6. package/dist/agent.js +615 -547
  7. package/dist/agent.js.map +1 -1
  8. package/dist/analyst-loop-DGdtVENg.js +517 -0
  9. package/dist/analyst-loop-DGdtVENg.js.map +1 -0
  10. package/dist/analyst-loop.d.ts +10 -43
  11. package/dist/analyst-loop.js +2 -11
  12. package/dist/backends-CiOCyRHb.js +743 -0
  13. package/dist/backends-CiOCyRHb.js.map +1 -0
  14. package/dist/candidate-execution/index.d.ts +4 -192
  15. package/dist/candidate-execution/index.js +5 -66
  16. package/dist/candidate-execution-DqMbv1rU.js +488 -0
  17. package/dist/candidate-execution-DqMbv1rU.js.map +1 -0
  18. package/dist/coder-yhVWbdWc.js +40 -0
  19. package/dist/coder-yhVWbdWc.js.map +1 -0
  20. package/dist/conversation-BpLQZGPH.js +1265 -0
  21. package/dist/conversation-BpLQZGPH.js.map +1 -0
  22. package/dist/conversation.d.ts +2 -804
  23. package/dist/conversation.js +2 -55
  24. package/dist/environment-provider-CfLUmKmf.js +1168 -0
  25. package/dist/environment-provider-CfLUmKmf.js.map +1 -0
  26. package/dist/environment-provider-IUGU3epE.d.ts +964 -0
  27. package/dist/environment-provider.d.ts +2 -78
  28. package/dist/environment-provider.js +2 -18
  29. package/dist/errors-DEAvWQPy.js +111 -0
  30. package/dist/errors-DEAvWQPy.js.map +1 -0
  31. package/dist/improvement-adapter-Cr5TBSji.d.ts +166 -0
  32. package/dist/improvement-cycle-C095kS_g.js +2624 -0
  33. package/dist/improvement-cycle-C095kS_g.js.map +1 -0
  34. package/dist/index-BGdxAwfO.d.ts +838 -0
  35. package/dist/index-C-FYUuFG.d.ts +193 -0
  36. package/dist/index-CbNM40xG.d.ts +140 -0
  37. package/dist/index-DruSKs3Z.d.ts +7224 -0
  38. package/dist/index-Efjb3nrQ.d.ts +671 -0
  39. package/dist/index.d.ts +14 -1058
  40. package/dist/index.js +2106 -2082
  41. package/dist/index.js.map +1 -1
  42. package/dist/intelligence.d.ts +768 -889
  43. package/dist/intelligence.js +1466 -1367
  44. package/dist/intelligence.js.map +1 -1
  45. package/dist/{kb-gate-CwHO0vz6.d.ts → kb-gate-C8z2juK8.d.ts} +33 -31
  46. package/dist/kb-gate-DpaSwXVx.js +80 -0
  47. package/dist/kb-gate-DpaSwXVx.js.map +1 -0
  48. package/dist/knowledge-Ku-mPP4f.js +433 -0
  49. package/dist/knowledge-Ku-mPP4f.js.map +1 -0
  50. package/dist/knowledge.d.ts +2 -150
  51. package/dist/knowledge.js +2 -40
  52. package/dist/local-harness-Dh8PJ0ot.d.ts +455 -0
  53. package/dist/loop-runner-bin-BXsFEUcf.d.ts +162 -0
  54. package/dist/loop-runner-bin-CRagyuNb.js +268 -0
  55. package/dist/loop-runner-bin-CRagyuNb.js.map +1 -0
  56. package/dist/loop-runner-bin.d.ts +2 -15
  57. package/dist/loop-runner-bin.js +2 -24
  58. package/dist/loops.d.ts +5 -4164
  59. package/dist/loops.js +8 -419
  60. package/dist/mcp/bin.d.ts +1 -1
  61. package/dist/mcp/bin.js +116 -146
  62. package/dist/mcp/bin.js.map +1 -1
  63. package/dist/mcp/index.d.ts +295 -436
  64. package/dist/mcp/index.js +1242 -1177
  65. package/dist/mcp/index.js.map +1 -1
  66. package/dist/mcp/memory-bin.d.ts +1 -1
  67. package/dist/mcp/memory-bin.js +37 -16
  68. package/dist/mcp/memory-bin.js.map +1 -1
  69. package/dist/memory-server-DL6cE2Ag.js +234 -0
  70. package/dist/memory-server-DL6cE2Ag.js.map +1 -0
  71. package/dist/model-resolution-Btd9iIKV.js +98 -0
  72. package/dist/model-resolution-Btd9iIKV.js.map +1 -0
  73. package/dist/openai-tools-B68JaOCx.d.ts +24 -0
  74. package/dist/openai-tools-CKLy1C7M.js +44 -0
  75. package/dist/openai-tools-CKLy1C7M.js.map +1 -0
  76. package/dist/otel-export-D34IQW1C.js +843 -0
  77. package/dist/otel-export-D34IQW1C.js.map +1 -0
  78. package/dist/platform.d.ts +188 -185
  79. package/dist/platform.js +185 -218
  80. package/dist/platform.js.map +1 -1
  81. package/dist/prepare-B53sPV7M.js +4292 -0
  82. package/dist/prepare-B53sPV7M.js.map +1 -0
  83. package/dist/primeintellect/index.d.ts +149 -145
  84. package/dist/primeintellect/index.js +626 -1003
  85. package/dist/primeintellect/index.js.map +1 -1
  86. package/dist/profiles.d.ts +296 -490
  87. package/dist/profiles.js +722 -749
  88. package/dist/profiles.js.map +1 -1
  89. package/dist/protected-model-port-BLPH5VVv.js +722 -0
  90. package/dist/protected-model-port-BLPH5VVv.js.map +1 -0
  91. package/dist/protected-model-port-BP6Z4eau.d.ts +283 -0
  92. package/dist/protected-redaction--F3v1oo8.js +200 -0
  93. package/dist/protected-redaction--F3v1oo8.js.map +1 -0
  94. package/dist/redact-D-u-rrcn.js +82 -0
  95. package/dist/redact-D-u-rrcn.js.map +1 -0
  96. package/dist/redact-kbIOjfjM.d.ts +452 -0
  97. package/dist/researcher-CoVqNhfI.js +433 -0
  98. package/dist/researcher-CoVqNhfI.js.map +1 -0
  99. package/dist/runtime-5zM5XxLr.js +5408 -0
  100. package/dist/runtime-5zM5XxLr.js.map +1 -0
  101. package/dist/sandbox-events-DeI5xX8P.js +316 -0
  102. package/dist/sandbox-events-DeI5xX8P.js.map +1 -0
  103. package/dist/spawn-journal-CwPvKUTa.js +456 -0
  104. package/dist/spawn-journal-CwPvKUTa.js.map +1 -0
  105. package/dist/structural-rollout-IXUEplky.js +1445 -0
  106. package/dist/structural-rollout-IXUEplky.js.map +1 -0
  107. package/dist/substrate-B0TYNrXn.js +27 -0
  108. package/dist/substrate-B0TYNrXn.js.map +1 -0
  109. package/dist/{substrate-Dyiqmd3o.d.ts → substrate-BcnuSHXm.d.ts} +57 -54
  110. package/dist/supervise-BiRutHS9.js +7321 -0
  111. package/dist/supervise-BiRutHS9.js.map +1 -0
  112. package/dist/supervisor-DTKhF-RV.js +3826 -0
  113. package/dist/supervisor-DTKhF-RV.js.map +1 -0
  114. package/dist/testing.d.ts +4 -3
  115. package/dist/testing.js +2478 -2637
  116. package/dist/testing.js.map +1 -1
  117. package/dist/tool-server-RcWgLIsL.js +141 -0
  118. package/dist/tool-server-RcWgLIsL.js.map +1 -0
  119. package/dist/trace-propagation-CJJC7SVB.js +57 -0
  120. package/dist/trace-propagation-CJJC7SVB.js.map +1 -0
  121. package/dist/types-BaIpb0XT.d.ts +492 -0
  122. package/dist/types-BevOjfTY.d.ts +765 -0
  123. package/dist/types-C9j4qg6l.d.ts +500 -0
  124. package/dist/types-Cja5OTGh.d.ts +190 -0
  125. package/dist/util-MVgdwuIS.js +145 -0
  126. package/dist/util-MVgdwuIS.js.map +1 -0
  127. package/dist/workspace-archive-BosQiotA.js +575 -0
  128. package/dist/workspace-archive-BosQiotA.js.map +1 -0
  129. package/package.json +44 -56
  130. package/skills/build-with-agent-runtime/SKILL.md +1 -0
  131. package/dist/activation-Cm-9MMX1.d.ts +0 -69
  132. package/dist/analyst-loop.js.map +0 -1
  133. package/dist/backends-BO4Jqoe2.d.ts +0 -134
  134. package/dist/candidate-execution/index.js.map +0 -1
  135. package/dist/chunk-2KGAN2HM.js +0 -1256
  136. package/dist/chunk-2KGAN2HM.js.map +0 -1
  137. package/dist/chunk-2VZYRB55.js +0 -834
  138. package/dist/chunk-2VZYRB55.js.map +0 -1
  139. package/dist/chunk-3MDZX7YU.js +0 -125
  140. package/dist/chunk-3MDZX7YU.js.map +0 -1
  141. package/dist/chunk-5IBQAPVB.js +0 -29
  142. package/dist/chunk-5IBQAPVB.js.map +0 -1
  143. package/dist/chunk-5QOB7H74.js +0 -387
  144. package/dist/chunk-5QOB7H74.js.map +0 -1
  145. package/dist/chunk-C5RTIBNZ.js +0 -48
  146. package/dist/chunk-C5RTIBNZ.js.map +0 -1
  147. package/dist/chunk-CTRA64LY.js +0 -680
  148. package/dist/chunk-CTRA64LY.js.map +0 -1
  149. package/dist/chunk-DPEUKJRO.js +0 -27
  150. package/dist/chunk-DPEUKJRO.js.map +0 -1
  151. package/dist/chunk-FD2MBMOH.js +0 -494
  152. package/dist/chunk-FD2MBMOH.js.map +0 -1
  153. package/dist/chunk-H5QPIZNX.js +0 -907
  154. package/dist/chunk-H5QPIZNX.js.map +0 -1
  155. package/dist/chunk-HLKC4UYB.js +0 -3514
  156. package/dist/chunk-HLKC4UYB.js.map +0 -1
  157. package/dist/chunk-HNP72PNU.js +0 -192
  158. package/dist/chunk-HNP72PNU.js.map +0 -1
  159. package/dist/chunk-ISPWRSEC.js +0 -408
  160. package/dist/chunk-ISPWRSEC.js.map +0 -1
  161. package/dist/chunk-KRBFHMV6.js +0 -1228
  162. package/dist/chunk-KRBFHMV6.js.map +0 -1
  163. package/dist/chunk-M22PL4GH.js +0 -171
  164. package/dist/chunk-M22PL4GH.js.map +0 -1
  165. package/dist/chunk-NURIQBJQ.js +0 -72
  166. package/dist/chunk-NURIQBJQ.js.map +0 -1
  167. package/dist/chunk-OPVWXJ2H.js +0 -75
  168. package/dist/chunk-OPVWXJ2H.js.map +0 -1
  169. package/dist/chunk-PZZKQVQV.js +0 -52
  170. package/dist/chunk-PZZKQVQV.js.map +0 -1
  171. package/dist/chunk-Q2JSAVQ3.js +0 -671
  172. package/dist/chunk-Q2JSAVQ3.js.map +0 -1
  173. package/dist/chunk-RDOAVVHY.js +0 -699
  174. package/dist/chunk-RDOAVVHY.js.map +0 -1
  175. package/dist/chunk-SGKPNBXE.js +0 -60
  176. package/dist/chunk-SGKPNBXE.js.map +0 -1
  177. package/dist/chunk-SMQXZGLZ.js +0 -351
  178. package/dist/chunk-SMQXZGLZ.js.map +0 -1
  179. package/dist/chunk-UHEZW5BU.js +0 -2918
  180. package/dist/chunk-UHEZW5BU.js.map +0 -1
  181. package/dist/chunk-UPWGXKXB.js +0 -254
  182. package/dist/chunk-UPWGXKXB.js.map +0 -1
  183. package/dist/chunk-VISA6CI3.js +0 -200
  184. package/dist/chunk-VISA6CI3.js.map +0 -1
  185. package/dist/chunk-WMTCUOQL.js +0 -474
  186. package/dist/chunk-WMTCUOQL.js.map +0 -1
  187. package/dist/chunk-WRTOVNN4.js +0 -4728
  188. package/dist/chunk-WRTOVNN4.js.map +0 -1
  189. package/dist/chunk-WSTRQZYQ.js +0 -5576
  190. package/dist/chunk-WSTRQZYQ.js.map +0 -1
  191. package/dist/chunk-XBG2W2VW.js +0 -6434
  192. package/dist/chunk-XBG2W2VW.js.map +0 -1
  193. package/dist/chunk-YEJR7IXO.js +0 -68
  194. package/dist/chunk-YEJR7IXO.js.map +0 -1
  195. package/dist/chunk-YJZA2BIK.js +0 -1338
  196. package/dist/chunk-YJZA2BIK.js.map +0 -1
  197. package/dist/completion-gate-BMy5LGoP.d.ts +0 -256
  198. package/dist/conversation.js.map +0 -1
  199. package/dist/coordination-BZZSVYpZ.d.ts +0 -2570
  200. package/dist/environment-provider.js.map +0 -1
  201. package/dist/improvement-adapter-HAZz-7vK.d.ts +0 -203
  202. package/dist/knowledge.js.map +0 -1
  203. package/dist/local-harness-BDNcl6jI.d.ts +0 -175
  204. package/dist/loop-runner-bin-68aoM9-7.d.ts +0 -199
  205. package/dist/loop-runner-bin.js.map +0 -1
  206. package/dist/loops.js.map +0 -1
  207. package/dist/openai-tools-fnj6SRVg.d.ts +0 -46
  208. package/dist/protected-model-port-B4t-OQjL.d.ts +0 -271
  209. package/dist/redact-BuFjxBUJ.d.ts +0 -481
  210. package/dist/sanitize-DEbPNtyI.d.ts +0 -328
  211. package/dist/structural-rollout-DEh6CEsa.d.ts +0 -446
  212. package/dist/supervise-4_48wwvy.d.ts +0 -379
  213. package/dist/types-BCoemcXU.d.ts +0 -802
  214. package/dist/types-BwoZWq-i.d.ts +0 -510
  215. package/dist/types-Cbp72qyu.d.ts +0 -491
  216. package/dist/types-CmYCMbFT.d.ts +0 -203
  217. package/dist/types-CvNwMzQt.d.ts +0 -957
  218. package/dist/worktree-fanout-DxhAWr5Z.d.ts +0 -999
@@ -0,0 +1,743 @@
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