@buoy-gg/agent-core 7.0.34 → 7.0.36

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 (181) hide show
  1. package/README.md +10 -7
  2. package/lib/commonjs/blocks/receipts.js +83 -24
  3. package/lib/commonjs/blocks/receipts.js.map +1 -1
  4. package/lib/commonjs/blocks/runId.js +31 -0
  5. package/lib/commonjs/blocks/runId.js.map +1 -0
  6. package/lib/commonjs/blocks/types.js +23 -2
  7. package/lib/commonjs/blocks/types.js.map +1 -1
  8. package/lib/commonjs/blocks/uiTool.js +426 -12
  9. package/lib/commonjs/blocks/uiTool.js.map +1 -1
  10. package/lib/commonjs/catalog/catalog.g.js +121 -43
  11. package/lib/commonjs/catalog/catalog.g.js.map +1 -1
  12. package/lib/commonjs/catalog/catalog.source.json +121 -45
  13. package/lib/commonjs/catalog/catalog.types.g.js +44 -0
  14. package/lib/commonjs/catalog/catalog.types.g.js.map +1 -0
  15. package/lib/commonjs/catalog/normalizeParams.js +333 -0
  16. package/lib/commonjs/catalog/normalizeParams.js.map +1 -0
  17. package/lib/commonjs/catalog/signature.js +68 -0
  18. package/lib/commonjs/catalog/signature.js.map +1 -0
  19. package/lib/commonjs/catalog/snapshotReads.js +13 -6
  20. package/lib/commonjs/catalog/snapshotReads.js.map +1 -1
  21. package/lib/commonjs/catalog/toProviderTools.js +41 -5
  22. package/lib/commonjs/catalog/toProviderTools.js.map +1 -1
  23. package/lib/commonjs/context/buildContextPack.js +367 -19
  24. package/lib/commonjs/context/buildContextPack.js.map +1 -1
  25. package/lib/commonjs/effects/digest.js +34 -0
  26. package/lib/commonjs/effects/digest.js.map +1 -0
  27. package/lib/commonjs/effects/ledger.js +85 -1
  28. package/lib/commonjs/effects/ledger.js.map +1 -1
  29. package/lib/commonjs/engine/askGate.js +287 -0
  30. package/lib/commonjs/engine/askGate.js.map +1 -0
  31. package/lib/commonjs/engine/effectFor.js +82 -1
  32. package/lib/commonjs/engine/effectFor.js.map +1 -1
  33. package/lib/commonjs/engine/historyBudget.js +260 -0
  34. package/lib/commonjs/engine/historyBudget.js.map +1 -0
  35. package/lib/commonjs/engine/runAgentTurn.js +958 -88
  36. package/lib/commonjs/engine/runAgentTurn.js.map +1 -1
  37. package/lib/commonjs/engine/systemPrompt.js +72 -8
  38. package/lib/commonjs/engine/systemPrompt.js.map +1 -1
  39. package/lib/commonjs/engine/textToolCalls.js +276 -0
  40. package/lib/commonjs/engine/textToolCalls.js.map +1 -0
  41. package/lib/commonjs/index.js +81 -1
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/policy/labels.js +19 -3
  44. package/lib/commonjs/policy/labels.js.map +1 -1
  45. package/lib/commonjs/policy/policy.js +6 -1
  46. package/lib/commonjs/policy/policy.js.map +1 -1
  47. package/lib/commonjs/policy/redact.js +42 -5
  48. package/lib/commonjs/policy/redact.js.map +1 -1
  49. package/lib/commonjs/providers/anthropic.js +340 -174
  50. package/lib/commonjs/providers/anthropic.js.map +1 -1
  51. package/lib/commonjs/providers/openai.js +229 -103
  52. package/lib/commonjs/providers/openai.js.map +1 -1
  53. package/lib/commonjs/providers/sse.js +164 -29
  54. package/lib/commonjs/providers/sse.js.map +1 -1
  55. package/lib/commonjs/providers/streamTimer.js +123 -0
  56. package/lib/commonjs/providers/streamTimer.js.map +1 -0
  57. package/lib/commonjs/providers/transport.js +107 -0
  58. package/lib/commonjs/providers/transport.js.map +1 -0
  59. package/lib/commonjs/providers/xhrStream.js +209 -0
  60. package/lib/commonjs/providers/xhrStream.js.map +1 -0
  61. package/lib/commonjs/session.js +245 -52
  62. package/lib/commonjs/session.js.map +1 -1
  63. package/lib/module/blocks/receipts.js +83 -24
  64. package/lib/module/blocks/receipts.js.map +1 -1
  65. package/lib/module/blocks/runId.js +26 -0
  66. package/lib/module/blocks/runId.js.map +1 -0
  67. package/lib/module/blocks/types.js +23 -2
  68. package/lib/module/blocks/types.js.map +1 -1
  69. package/lib/module/blocks/uiTool.js +424 -12
  70. package/lib/module/blocks/uiTool.js.map +1 -1
  71. package/lib/module/catalog/catalog.g.js +121 -43
  72. package/lib/module/catalog/catalog.g.js.map +1 -1
  73. package/lib/module/catalog/catalog.source.json +121 -45
  74. package/lib/module/catalog/catalog.types.g.js +40 -0
  75. package/lib/module/catalog/catalog.types.g.js.map +1 -0
  76. package/lib/module/catalog/normalizeParams.js +327 -0
  77. package/lib/module/catalog/normalizeParams.js.map +1 -0
  78. package/lib/module/catalog/signature.js +63 -0
  79. package/lib/module/catalog/signature.js.map +1 -0
  80. package/lib/module/catalog/snapshotReads.js +13 -6
  81. package/lib/module/catalog/snapshotReads.js.map +1 -1
  82. package/lib/module/catalog/toProviderTools.js +41 -5
  83. package/lib/module/catalog/toProviderTools.js.map +1 -1
  84. package/lib/module/context/buildContextPack.js +366 -19
  85. package/lib/module/context/buildContextPack.js.map +1 -1
  86. package/lib/module/effects/digest.js +29 -0
  87. package/lib/module/effects/digest.js.map +1 -0
  88. package/lib/module/effects/ledger.js +85 -1
  89. package/lib/module/effects/ledger.js.map +1 -1
  90. package/lib/module/engine/askGate.js +280 -0
  91. package/lib/module/engine/askGate.js.map +1 -0
  92. package/lib/module/engine/effectFor.js +83 -1
  93. package/lib/module/engine/effectFor.js.map +1 -1
  94. package/lib/module/engine/historyBudget.js +251 -0
  95. package/lib/module/engine/historyBudget.js.map +1 -0
  96. package/lib/module/engine/runAgentTurn.js +954 -88
  97. package/lib/module/engine/runAgentTurn.js.map +1 -1
  98. package/lib/module/engine/systemPrompt.js +71 -8
  99. package/lib/module/engine/systemPrompt.js.map +1 -1
  100. package/lib/module/engine/textToolCalls.js +270 -0
  101. package/lib/module/engine/textToolCalls.js.map +1 -0
  102. package/lib/module/index.js +7 -4
  103. package/lib/module/index.js.map +1 -1
  104. package/lib/module/policy/labels.js +19 -3
  105. package/lib/module/policy/labels.js.map +1 -1
  106. package/lib/module/policy/policy.js +6 -1
  107. package/lib/module/policy/policy.js.map +1 -1
  108. package/lib/module/policy/redact.js +42 -5
  109. package/lib/module/policy/redact.js.map +1 -1
  110. package/lib/module/providers/anthropic.js +341 -175
  111. package/lib/module/providers/anthropic.js.map +1 -1
  112. package/lib/module/providers/openai.js +230 -104
  113. package/lib/module/providers/openai.js.map +1 -1
  114. package/lib/module/providers/sse.js +160 -29
  115. package/lib/module/providers/sse.js.map +1 -1
  116. package/lib/module/providers/streamTimer.js +118 -0
  117. package/lib/module/providers/streamTimer.js.map +1 -0
  118. package/lib/module/providers/transport.js +103 -0
  119. package/lib/module/providers/transport.js.map +1 -0
  120. package/lib/module/providers/xhrStream.js +204 -0
  121. package/lib/module/providers/xhrStream.js.map +1 -0
  122. package/lib/module/session.js +228 -54
  123. package/lib/module/session.js.map +1 -1
  124. package/lib/typescript/blocks/receipts.d.ts +5 -10
  125. package/lib/typescript/blocks/receipts.d.ts.map +1 -1
  126. package/lib/typescript/blocks/runId.d.ts +20 -0
  127. package/lib/typescript/blocks/runId.d.ts.map +1 -0
  128. package/lib/typescript/blocks/types.d.ts +23 -2
  129. package/lib/typescript/blocks/types.d.ts.map +1 -1
  130. package/lib/typescript/blocks/uiTool.d.ts +11 -2
  131. package/lib/typescript/blocks/uiTool.d.ts.map +1 -1
  132. package/lib/typescript/catalog/catalog.g.d.ts.map +1 -1
  133. package/lib/typescript/catalog/catalog.types.g.d.ts +40 -0
  134. package/lib/typescript/catalog/catalog.types.g.d.ts.map +1 -0
  135. package/lib/typescript/catalog/normalizeParams.d.ts +43 -0
  136. package/lib/typescript/catalog/normalizeParams.d.ts.map +1 -0
  137. package/lib/typescript/catalog/signature.d.ts +29 -0
  138. package/lib/typescript/catalog/signature.d.ts.map +1 -0
  139. package/lib/typescript/catalog/snapshotReads.d.ts.map +1 -1
  140. package/lib/typescript/catalog/toProviderTools.d.ts +28 -15
  141. package/lib/typescript/catalog/toProviderTools.d.ts.map +1 -1
  142. package/lib/typescript/context/buildContextPack.d.ts +24 -2
  143. package/lib/typescript/context/buildContextPack.d.ts.map +1 -1
  144. package/lib/typescript/effects/digest.d.ts +10 -0
  145. package/lib/typescript/effects/digest.d.ts.map +1 -0
  146. package/lib/typescript/effects/ledger.d.ts +55 -0
  147. package/lib/typescript/effects/ledger.d.ts.map +1 -1
  148. package/lib/typescript/engine/askGate.d.ts +29 -0
  149. package/lib/typescript/engine/askGate.d.ts.map +1 -0
  150. package/lib/typescript/engine/effectFor.d.ts +6 -1
  151. package/lib/typescript/engine/effectFor.d.ts.map +1 -1
  152. package/lib/typescript/engine/historyBudget.d.ts +92 -0
  153. package/lib/typescript/engine/historyBudget.d.ts.map +1 -0
  154. package/lib/typescript/engine/runAgentTurn.d.ts +115 -1
  155. package/lib/typescript/engine/runAgentTurn.d.ts.map +1 -1
  156. package/lib/typescript/engine/systemPrompt.d.ts +41 -0
  157. package/lib/typescript/engine/systemPrompt.d.ts.map +1 -1
  158. package/lib/typescript/engine/textToolCalls.d.ts +58 -0
  159. package/lib/typescript/engine/textToolCalls.d.ts.map +1 -0
  160. package/lib/typescript/index.d.ts +8 -5
  161. package/lib/typescript/index.d.ts.map +1 -1
  162. package/lib/typescript/policy/labels.d.ts.map +1 -1
  163. package/lib/typescript/policy/policy.d.ts.map +1 -1
  164. package/lib/typescript/policy/redact.d.ts.map +1 -1
  165. package/lib/typescript/providers/anthropic.d.ts +5 -0
  166. package/lib/typescript/providers/anthropic.d.ts.map +1 -1
  167. package/lib/typescript/providers/openai.d.ts +0 -16
  168. package/lib/typescript/providers/openai.d.ts.map +1 -1
  169. package/lib/typescript/providers/sse.d.ts +82 -1
  170. package/lib/typescript/providers/sse.d.ts.map +1 -1
  171. package/lib/typescript/providers/streamTimer.d.ts +59 -0
  172. package/lib/typescript/providers/streamTimer.d.ts.map +1 -0
  173. package/lib/typescript/providers/transport.d.ts +40 -0
  174. package/lib/typescript/providers/transport.d.ts.map +1 -0
  175. package/lib/typescript/providers/types.d.ts +116 -3
  176. package/lib/typescript/providers/types.d.ts.map +1 -1
  177. package/lib/typescript/providers/xhrStream.d.ts +43 -0
  178. package/lib/typescript/providers/xhrStream.d.ts.map +1 -0
  179. package/lib/typescript/session.d.ts +63 -2
  180. package/lib/typescript/session.d.ts.map +1 -1
  181. package/package.json +2 -2
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.createOpenAIProvider = createOpenAIProvider;
7
7
  var _sse = require("./sse");
8
+ var _streamTimer = require("./streamTimer");
9
+ var _transport = require("./transport");
8
10
  /**
9
11
  * OpenAI chat-completions protocol.
10
12
  *
@@ -20,6 +22,19 @@ var _sse = require("./sse");
20
22
  * is the common shape for an internal LLM proxy. Note Anthropic's own
21
23
  * OpenAI-compatibility endpoint is explicitly not production-ready and silently
22
24
  * ignores several fields — use the native Anthropic adapter for Anthropic.
25
+ *
26
+ * THE 1024-CHAR TRAP. Azure caps a function description at 1024 chars, so
27
+ * this adapter trims to fit. The catalog renders a tool's summary FIRST and
28
+ * its `Actions:` list LAST, and every tool's full description is longer than
29
+ * the cap — so trimming `t.description` silently removed every action line
30
+ * for 23 of the 24 tools — the 24th kept one line of sixteen — measured
31
+ * against the catalog as it is. A model on this protocol (api.openai.com, Azure,
32
+ * OpenRouter, fal — every row of the model-floor sweep) saw a summary cut
33
+ * mid-sentence and a bare `action` enum: no per-action summary, no
34
+ * [DESTRUCTIVE] / [changes state] tag, no "prefer X over Y" guidance, and a
35
+ * catalog wording change could not move its score. So the description is
36
+ * the SUMMARY only, and the `Actions:` block goes into
37
+ * `parameters.properties.action.description`, which nothing caps.
23
38
  */
24
39
 
25
40
  function toWireMessages(system, messages) {
@@ -61,132 +76,243 @@ function toWireMessages(system, messages) {
61
76
  }
62
77
  return out;
63
78
  }
79
+
80
+ /**
81
+ * The tool's input schema with the `Actions:` block on the `action`
82
+ * property — the uncapped home for the per-action lines (header note).
83
+ */
84
+ function withActionsBlock(t) {
85
+ const action = t.inputSchema.properties.action;
86
+ return {
87
+ ...t.inputSchema,
88
+ properties: {
89
+ ...t.inputSchema.properties,
90
+ action: {
91
+ ...action,
92
+ description: `Which action to run.\n\n${t.actionsBlock}`
93
+ }
94
+ }
95
+ };
96
+ }
64
97
  function createOpenAIProvider(config) {
98
+ // One per provider, so the `auto` transport decision is made once and not
99
+ // re-learned (and re-buffered) at the start of every turn.
100
+ const transport = (0, _transport.createTransport)(config);
65
101
  return {
66
102
  protocol: "openai",
67
103
  async *send(req) {
68
- const headers = {
69
- "content-type": "application/json",
70
- ...(await (config.headers?.() ?? {}))
104
+ const timer = (0, _streamTimer.createStreamTimer)(req.signal, config);
105
+ const status = {
106
+ truncated: false
71
107
  };
72
- if (config.apiKey && !headers.Authorization && !headers["api-key"]) {
73
- headers.Authorization = `Bearer ${config.apiKey}`;
74
- }
75
- const response = await fetch(config.endpoint, {
76
- method: "POST",
77
- headers,
78
- signal: req.signal,
79
- body: JSON.stringify({
80
- model: req.model,
81
- max_tokens: req.maxTokens,
82
- stream: true,
83
- stream_options: {
84
- include_usage: true
85
- },
86
- messages: toWireMessages(req.system, req.messages),
87
- tools: req.tools.map(t => ({
88
- type: "function",
89
- function: {
90
- name: t.name,
91
- // Azure caps function descriptions at 1024 chars while Anthropic's
92
- // guidance asks for several sentences. Trim rather than 400.
93
- description: t.description.slice(0, 1024),
94
- parameters: t.inputSchema
95
- }
96
- })),
97
- ...config.requestOverrides
98
- })
99
- });
100
- if (!response.ok) {
101
- yield {
102
- type: "error",
103
- message: await (0, _sse.errorBody)(response)
108
+ try {
109
+ const headers = {
110
+ "content-type": "application/json",
111
+ ...(await (config.headers?.() ?? {}))
104
112
  };
105
- return;
106
- }
113
+ if (config.apiKey && !headers.Authorization && !headers["api-key"]) {
114
+ headers.Authorization = `Bearer ${config.apiKey}`;
115
+ }
116
+ const stream = await transport.open({
117
+ headers,
118
+ // The timer's signal, not the caller's: it aborts on the user's Stop
119
+ // AND on either clock running out. See providers/streamTimer.ts.
120
+ signal: timer.signal,
121
+ body: JSON.stringify({
122
+ model: req.model,
123
+ max_tokens: req.maxTokens,
124
+ stream: true,
125
+ stream_options: {
126
+ include_usage: true
127
+ },
128
+ messages: toWireMessages(req.systemVolatile ? `${req.system}\n\n---\n\n${req.systemVolatile}` : req.system, req.messages),
129
+ tools: req.tools.map(t => ({
130
+ type: "function",
131
+ function: {
132
+ name: t.name,
133
+ // Azure caps function descriptions at 1024 chars while Anthropic's
134
+ // guidance asks for several sentences. Trim rather than 400 — but
135
+ // trim the SUMMARY only: the action lines travel on `parameters`
136
+ // (withActionsBlock), where nothing is capped. See the header.
137
+ description: t.summaryDescription.slice(0, 1024),
138
+ parameters: withActionsBlock(t)
139
+ }
140
+ })),
141
+ ...config.requestOverrides
142
+ })
143
+ });
144
+
145
+ // Headers are in: the connect clock stops here and the idle one starts.
146
+ timer.connected();
147
+ if (stream.problem) {
148
+ yield {
149
+ type: "error",
150
+ message: stream.problem,
151
+ kind: "provider"
152
+ };
153
+ return;
154
+ }
107
155
 
108
- // Keyed by the stream's `index`; id and name arrive only on the first
109
- // fragment, arguments accumulate across all of them.
110
- const pending = new Map();
111
- let stopReason;
112
- let usage;
113
- const flush = function* () {
114
- for (const [, slot] of pending) {
115
- let input = {};
156
+ // Keyed by the stream's `index`; id and name arrive only on the first
157
+ // fragment, arguments accumulate across all of them.
158
+ const pending = new Map();
159
+ let stopReason;
160
+ let usage;
161
+ // The served model id, from the first chunk that names one. A gateway
162
+ // resolves `gpt-4.1` to a dated snapshot, and that is the id a cost
163
+ // table or a benchmark has to attribute a drift to.
164
+ let model;
165
+ /** The `[DONE]` sentinel; ends the read. See `sawTerminal` below. */
166
+ let sawDone = false;
167
+ const flush = function* () {
168
+ for (const [, slot] of pending) {
169
+ let input = {};
170
+ try {
171
+ input = slot.args ? JSON.parse(slot.args) : {};
172
+ } catch {
173
+ yield {
174
+ type: "error",
175
+ kind: "provider",
176
+ // Carry the evidence. Without it this error says only THAT the
177
+ // arguments were bad, and diagnosing it needs a live repro of a
178
+ // model that may not be reachable any more — a Sonnet bench run
179
+ // produced 15 of these and left nothing to read. `length` here
180
+ // means the model was cut off mid-argument and `max_tokens` is
181
+ // the fix; anything else is a genuinely malformed stream.
182
+ message: (0, _sse.unparseableMessage)(slot.name, slot.args, stopReason)
183
+ };
184
+ continue;
185
+ }
186
+ yield {
187
+ type: "tool-call",
188
+ call: {
189
+ id: slot.id,
190
+ name: slot.name,
191
+ input
192
+ }
193
+ };
194
+ }
195
+ pending.clear();
196
+ };
197
+ for await (const ev of stream.frames({
198
+ status,
199
+ onFrame: timer.activity,
200
+ signal: timer.signal
201
+ })) {
202
+ if (ev.data === "[DONE]") {
203
+ sawDone = true;
204
+ break;
205
+ }
206
+ let payload;
116
207
  try {
117
- input = slot.args ? JSON.parse(slot.args) : {};
208
+ payload = JSON.parse(ev.data);
118
209
  } catch {
210
+ continue;
211
+ }
212
+ if (payload.error) {
213
+ const err = payload.error;
119
214
  yield {
120
215
  type: "error",
121
- message: `Model sent unparseable arguments for ${slot.name}`
216
+ message: err.message ?? "Provider error",
217
+ kind: "provider"
122
218
  };
123
- continue;
219
+ return;
220
+ }
221
+ if (!model && typeof payload.model === "string" && payload.model) model = payload.model;
222
+ const u = payload.usage;
223
+ if (u) {
224
+ // `prompt_tokens_details.cached_tokens` is this protocol's cache
225
+ // counter. Unlike Anthropic's it is INCLUDED in `prompt_tokens`
226
+ // rather than reported beside it; it is still carried as
227
+ // `cacheRead` so a cost table can price that slice at the
228
+ // discounted rate.
229
+ const details = u.prompt_tokens_details;
230
+ const cached = details?.cached_tokens;
231
+ usage = {
232
+ input: typeof u.prompt_tokens === "number" ? u.prompt_tokens : 0,
233
+ output: typeof u.completion_tokens === "number" ? u.completion_tokens : 0,
234
+ ...(typeof cached === "number" ? {
235
+ cacheRead: cached
236
+ } : {})
237
+ };
238
+ }
239
+ const choice = payload.choices?.[0];
240
+ if (!choice) continue;
241
+ if (choice.finish_reason) stopReason = choice.finish_reason;
242
+ const delta = choice.delta;
243
+ if (typeof delta?.content === "string" && delta.content) {
244
+ yield {
245
+ type: "text",
246
+ delta: delta.content
247
+ };
248
+ }
249
+ const calls = delta?.tool_calls;
250
+ for (const c of calls ?? []) {
251
+ const index = c.index ?? 0;
252
+ const fn = c.function;
253
+ const slot = pending.get(index) ?? {
254
+ id: "",
255
+ name: "",
256
+ args: ""
257
+ };
258
+ if (c.id) slot.id = c.id;
259
+ if (fn?.name) slot.name = fn.name;
260
+ if (typeof fn?.arguments === "string") slot.args += fn.arguments;
261
+ pending.set(index, slot);
124
262
  }
125
- yield {
126
- type: "tool-call",
127
- call: {
128
- id: slot.id,
129
- name: slot.name,
130
- input
131
- }
132
- };
133
- }
134
- pending.clear();
135
- };
136
- for await (const ev of (0, _sse.readSse)(response)) {
137
- if (ev.data === "[DONE]") break;
138
- let payload;
139
- try {
140
- payload = JSON.parse(ev.data);
141
- } catch {
142
- continue;
143
263
  }
144
- if (payload.error) {
145
- const err = payload.error;
264
+
265
+ /**
266
+ * Did the provider actually say it was finished?
267
+ *
268
+ * `[DONE]` is the transport sentinel. `finish_reason` counts too, and is
269
+ * the stronger claim: it is the model's own statement of why it stopped,
270
+ * while `[DONE]` only says the SSE stream closed. Plenty of
271
+ * OpenAI-compatible gateways send one and not the other, and refusing
272
+ * either shape would break working endpoints for no safety gain. NEITHER
273
+ * arriving is the case that matters — bare EOF, which used to be read as
274
+ * success, and which is how a complete-looking `storage.setItem` came out
275
+ * of a cut connection and ran.
276
+ */
277
+ const sawTerminal = sawDone || stopReason !== undefined;
278
+ if (!sawTerminal) {
279
+ // Nothing is flushed: the accumulated arguments may be a whole JSON
280
+ // object and still be half a plan. Text already yielded stays on
281
+ // screen; the engine shows it and offers Continue.
146
282
  yield {
147
283
  type: "error",
148
- message: err.message ?? "Provider error"
284
+ message: (0, _sse.truncatedMessage)(status.truncated),
285
+ kind: "truncated"
149
286
  };
150
287
  return;
151
288
  }
152
- const u = payload.usage;
153
- if (u) {
154
- usage = {
155
- input: u.prompt_tokens ?? 0,
156
- output: u.completion_tokens ?? 0
157
- };
158
- }
159
- const choice = payload.choices?.[0];
160
- if (!choice) continue;
161
- if (choice.finish_reason) stopReason = choice.finish_reason;
162
- const delta = choice.delta;
163
- if (typeof delta?.content === "string" && delta.content) {
289
+ yield* flush();
290
+ yield {
291
+ type: "done",
292
+ outcome: stopReason === "length" ? "output-limited" : "completed",
293
+ stopReason,
294
+ usage,
295
+ model
296
+ };
297
+ } catch (err) {
298
+ // The user's Stop wins the label even if a clock fired in the same
299
+ // tick: they know why the turn ended, and "timed out" would be a lie.
300
+ if (req.signal?.aborted) throw err;
301
+ const message = timer.message();
302
+ if (message) {
164
303
  yield {
165
- type: "text",
166
- delta: delta.content
167
- };
168
- }
169
- const calls = delta?.tool_calls;
170
- for (const c of calls ?? []) {
171
- const index = c.index ?? 0;
172
- const fn = c.function;
173
- const slot = pending.get(index) ?? {
174
- id: "",
175
- name: "",
176
- args: ""
304
+ type: "error",
305
+ message,
306
+ kind: "timeout"
177
307
  };
178
- if (c.id) slot.id = c.id;
179
- if (fn?.name) slot.name = fn.name;
180
- if (typeof fn?.arguments === "string") slot.args += fn.arguments;
181
- pending.set(index, slot);
308
+ return;
182
309
  }
310
+ // A genuine transport failure — DNS, refused connection, TLS. It has
311
+ // always propagated to the failed-turn card, and still does.
312
+ throw err;
313
+ } finally {
314
+ timer.dispose();
183
315
  }
184
- yield* flush();
185
- yield {
186
- type: "done",
187
- stopReason,
188
- usage
189
- };
190
316
  }
191
317
  };
192
318
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_sse","require","toWireMessages","system","messages","out","role","content","m","push","text","toolCalls","length","tool_calls","map","c","id","type","function","name","arguments","JSON","stringify","input","r","results","tool_call_id","toolCallId","createOpenAIProvider","config","protocol","send","req","headers","apiKey","Authorization","response","fetch","endpoint","method","signal","body","model","max_tokens","maxTokens","stream","stream_options","include_usage","tools","t","description","slice","parameters","inputSchema","requestOverrides","ok","message","errorBody","pending","Map","stopReason","usage","flush","slot","args","parse","call","clear","ev","readSse","data","payload","error","err","u","prompt_tokens","output","completion_tokens","choice","choices","finish_reason","delta","calls","index","fn","get","set"],"sourceRoot":"../../../src","sources":["providers/openai.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_sse","require","_streamTimer","_transport","toWireMessages","system","messages","out","role","content","m","push","text","toolCalls","length","tool_calls","map","c","id","type","function","name","arguments","JSON","stringify","input","r","results","tool_call_id","toolCallId","withActionsBlock","t","action","inputSchema","properties","description","actionsBlock","createOpenAIProvider","config","transport","createTransport","protocol","send","req","timer","createStreamTimer","signal","status","truncated","headers","apiKey","Authorization","stream","open","body","model","max_tokens","maxTokens","stream_options","include_usage","systemVolatile","tools","summaryDescription","slice","parameters","requestOverrides","connected","problem","message","kind","pending","Map","stopReason","usage","sawDone","flush","slot","args","parse","unparseableMessage","call","clear","ev","frames","onFrame","activity","data","payload","error","err","u","details","prompt_tokens_details","cached","cached_tokens","prompt_tokens","output","completion_tokens","cacheRead","choice","choices","finish_reason","delta","calls","index","fn","get","set","sawTerminal","undefined","truncatedMessage","outcome","aborted","dispose"],"sourceRoot":"../../../src","sources":["providers/openai.ts"],"mappings":"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","ignoreList":[]}
@@ -4,7 +4,11 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.errorBody = errorBody;
7
+ exports.framesFrom = framesFrom;
8
+ exports.nonStreamProblem = nonStreamProblem;
7
9
  exports.readSse = readSse;
10
+ exports.truncatedMessage = truncatedMessage;
11
+ exports.unparseableMessage = unparseableMessage;
8
12
  /**
9
13
  * SSE reading, with the React Native reality baked in.
10
14
  *
@@ -14,15 +18,24 @@ exports.readSse = readSse;
14
18
  * TV-CLI lanes never had it. So `readSse` degrades to reading the whole body
15
19
  * and replaying it as events rather than failing: a non-streaming answer is a
16
20
  * slower chat, while a hard failure is no chat at all.
21
+ *
22
+ * THE TWO PATHS MUST AGREE. They did not. The buffered path appended "\n\n" to
23
+ * the body before parsing, which turns an unterminated final frame — the exact
24
+ * signature of a cut connection — into a well-formed event. The streaming path
25
+ * discarded that same frame, as the SSE spec requires. So one lane invented a
26
+ * complete answer where the other saw a broken one. Now both discard it and
27
+ * both RECORD it (`SseReadStatus.truncated`), because "was this reply whole?"
28
+ * is a question the engine has to answer before it runs anything.
17
29
  */
18
30
 
19
- function* parseChunk(buffer) {
31
+ function* parseChunk(buffer, onFrame) {
20
32
  let rest = buffer;
21
33
  let idx;
22
34
  // Frames are separated by a blank line. \r\n\r\n is legal too.
23
35
  while ((idx = rest.search(/\r?\n\r?\n/)) !== -1) {
24
36
  const raw = rest.slice(0, idx);
25
37
  rest = rest.slice(idx + (rest[idx] === "\r" ? 4 : 2));
38
+ onFrame?.();
26
39
  let event;
27
40
  const dataLines = [];
28
41
  for (const line of raw.split(/\r?\n/)) {
@@ -36,53 +49,124 @@ function* parseChunk(buffer) {
36
49
  }
37
50
  return rest;
38
51
  }
39
- async function* readSse(response) {
52
+
53
+ /**
54
+ * Drain complete frames out of `buffer`, returning what is left over.
55
+ *
56
+ * Shared by both paths so a framing fix can only ever be made in one place.
57
+ */
58
+ function* drain(buffer, out, onFrame) {
59
+ const gen = parseChunk(buffer, onFrame);
60
+ let next = gen.next();
61
+ while (!next.done) {
62
+ yield next.value;
63
+ next = gen.next();
64
+ }
65
+ out.rest = next.value;
66
+ }
67
+
68
+ /**
69
+ * Turn a stream of TEXT chunks into SSE frames.
70
+ *
71
+ * The one place framing happens, whatever produced the bytes: a `fetch` body
72
+ * reader, an `XMLHttpRequest` progress slice, or a whole body read at once on
73
+ * a runtime that cannot stream. Splitting it out is what lets the XHR
74
+ * transport exist without a second parser to keep in step with this one.
75
+ */
76
+ async function* framesFrom(chunks, options = {}) {
77
+ const {
78
+ status,
79
+ onFrame
80
+ } = options;
81
+ const leftover = {
82
+ rest: ""
83
+ };
84
+ let buffer = "";
85
+ for await (const chunk of chunks) {
86
+ buffer += chunk;
87
+ yield* drain(buffer, leftover, onFrame);
88
+ buffer = leftover.rest;
89
+ }
90
+ if (status && leftover.rest.trim()) status.truncated = true;
91
+ }
92
+ async function* readSse(response, options = {}) {
93
+ const {
94
+ status,
95
+ onFrame,
96
+ signal
97
+ } = options;
40
98
  const body = response.body;
99
+ const leftover = {
100
+ rest: ""
101
+ };
41
102
  if (!body || typeof body.getReader !== "function") {
42
- // No streaming on this platform — read it all, then replay.
103
+ // No streaming on this platform — read it all, then replay. Note there is
104
+ // no "\n\n" here any more: an unterminated tail is discarded exactly as
105
+ // the streaming path discards it (see the header).
43
106
  const text = await response.text();
44
- let rest = "";
45
- const gen = parseChunk(text + "\n\n");
46
- let next = gen.next();
47
- while (!next.done) {
48
- yield next.value;
49
- next = gen.next();
50
- }
51
- rest = next.value;
52
- if (rest.trim()) {
53
- const tail = parseChunk(rest + "\n\n");
54
- let t = tail.next();
55
- while (!t.done) {
56
- yield t.value;
57
- t = tail.next();
58
- }
59
- }
107
+ yield* drain(text, leftover, onFrame);
108
+ if (status && leftover.rest.trim()) status.truncated = true;
60
109
  return;
61
110
  }
62
111
  const reader = body.getReader();
63
112
  const decoder = new TextDecoder();
64
113
  let buffer = "";
114
+ let finished = false;
115
+ /**
116
+ * One promise for the whole read, not one per chunk.
117
+ *
118
+ * It never resolves — only rejects — so racing it is free apart from the
119
+ * `Promise.race` allocation, and an answer of a few hundred frames pays far
120
+ * more for `JSON.parse` than for this.
121
+ */
122
+ const aborted = signal ? new Promise((_resolve, reject) => {
123
+ const fail = () => {
124
+ const err = new Error("Aborted");
125
+ err.name = "AbortError";
126
+ reject(err);
127
+ };
128
+ if (signal.aborted) fail();else signal.addEventListener?.("abort", fail, {
129
+ once: true
130
+ });
131
+ }) : undefined;
132
+ // Nothing awaits this until the race below, and a rejection with no handler
133
+ // is an unhandled rejection on some runtimes. This is the handler.
134
+ aborted?.catch(() => {});
65
135
  try {
66
136
  for (;;) {
137
+ const read = reader.read();
67
138
  const {
68
139
  done,
69
140
  value
70
- } = await reader.read();
141
+ } = aborted ? await Promise.race([read, aborted]) : await read;
71
142
  if (done) break;
72
143
  buffer += decoder.decode(value, {
73
144
  stream: true
74
145
  });
75
- const gen = parseChunk(buffer);
76
- let next = gen.next();
77
- while (!next.done) {
78
- yield next.value;
79
- next = gen.next();
80
- }
81
- buffer = next.value;
146
+ yield* drain(buffer, leftover, onFrame);
147
+ buffer = leftover.rest;
82
148
  }
149
+ // Flush the decoder: a multi-byte character split across the last two
150
+ // chunks is only completed here, and an INCOMPLETE one at EOF is itself
151
+ // evidence the connection was cut.
152
+ buffer += decoder.decode();
153
+ yield* drain(buffer, leftover, onFrame);
154
+ if (status && leftover.rest.trim()) status.truncated = true;
155
+ finished = true;
83
156
  } finally {
84
- // Cancelling on abort keeps the socket from lingering after the user
85
- // closes the sheet mid-answer.
157
+ if (!finished) {
158
+ // The consumer left early — an abort, a timeout, or a `break` in the
159
+ // provider. `releaseLock` alone leaves the socket open; only `cancel`
160
+ // tears the request down, and it must come first because cancelling a
161
+ // locked reader is the only way to reach the underlying stream.
162
+ try {
163
+ // Not awaited: the consumer is already leaving, and a cancel that
164
+ // rejects (an errored stream) must not become an unhandled rejection.
165
+ void Promise.resolve(reader.cancel()).catch(() => {});
166
+ } catch {
167
+ /* already cancelled or errored */
168
+ }
169
+ }
86
170
  try {
87
171
  reader.releaseLock();
88
172
  } catch {
@@ -91,6 +175,33 @@ async function* readSse(response) {
91
175
  }
92
176
  }
93
177
 
178
+ /**
179
+ * A 200 that is not a stream at all.
180
+ *
181
+ * Gateways in front of a model do this: an auth proxy that has lost its
182
+ * upstream serves an HTML error page, a misrouted path serves an SPA shell,
183
+ * and a gateway that ignored `stream: true` serves one JSON object. Every one
184
+ * of those parses to zero SSE frames, so the old code read it as a model that
185
+ * had nothing to say and ended the turn with a blank bubble.
186
+ *
187
+ * Returns a message when the body should be treated as an error, and undefined
188
+ * when it looks like something the SSE reader can work with. Consumes the body
189
+ * only in the failing case.
190
+ */
191
+ async function nonStreamProblem(response) {
192
+ const type = response.headers?.get?.("content-type") ?? "";
193
+ if (!type) return undefined; // No header to judge by — let the parser try.
194
+ const lowered = type.toLowerCase();
195
+ if (lowered.includes("event-stream")) return undefined;
196
+ // A JSON body is either a non-streaming reply or a structured error; both
197
+ // are worth reporting with their own text rather than guessing.
198
+ const detail = (await response.text().catch(() => "")).slice(0, 600);
199
+ if (lowered.includes("json")) {
200
+ return `The endpoint answered with JSON instead of a stream — it may not support "stream": true.${detail ? ` — ${detail}` : ""}`;
201
+ }
202
+ return `The endpoint answered with ${type} instead of an event stream.${detail ? ` — ${detail}` : ""}`;
203
+ }
204
+
94
205
  /** Body text for a failed response, trimmed to something a user can read. */
95
206
  async function errorBody(response) {
96
207
  let detail = "";
@@ -101,4 +212,28 @@ async function errorBody(response) {
101
212
  }
102
213
  return `HTTP ${response.status}${detail ? ` — ${detail}` : ""}`;
103
214
  }
215
+
216
+ /**
217
+ * The message for a stream that stopped before the provider said it was done.
218
+ *
219
+ * `truncated` distinguishes the two shapes: cut mid-frame (a dropped
220
+ * connection) versus a clean frame boundary with no terminal marker (a gateway
221
+ * that closes the socket instead of sending one). The second is a
222
+ * compatibility problem worth naming, because retrying it will not help.
223
+ */
224
+ function truncatedMessage(truncated) {
225
+ return truncated ? "The connection dropped before the answer finished." : "The endpoint closed the stream without saying the answer was finished.";
226
+ }
227
+
228
+ /**
229
+ * The message for arguments that would not parse, with enough in it to act on.
230
+ * `stopReason` "length" is the common cause — a reasoning model spends its
231
+ * budget thinking and the tool call is truncated mid-JSON — and the tail of
232
+ * the raw arguments says immediately whether that is what happened.
233
+ */
234
+ function unparseableMessage(name, args, stopReason) {
235
+ const truncated = stopReason === "length";
236
+ const tail = args.length > 160 ? `…${args.slice(-160)}` : args;
237
+ return `Model sent unparseable arguments for ${name}` + (truncated ? " (cut off at max_tokens — raise it or reduce thinking budget)" : "") + ` [stop: ${stopReason ?? "none"}, ${args.length} chars] ${tail}`;
238
+ }
104
239
  //# sourceMappingURL=sse.js.map