@caupulican/pi-agent-core 0.81.42 → 0.84.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 (92) hide show
  1. package/dist/agent-loop.d.ts +3 -2
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +127 -33
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/agent.d.ts +15 -1
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +9 -38
  8. package/dist/agent.js.map +1 -1
  9. package/dist/compaction/branch-summarization.d.ts +1 -1
  10. package/dist/compaction/branch-summarization.d.ts.map +1 -1
  11. package/dist/compaction/branch-summarization.js +3 -13
  12. package/dist/compaction/branch-summarization.js.map +1 -1
  13. package/dist/compaction/compaction.d.ts +1 -1
  14. package/dist/compaction/compaction.d.ts.map +1 -1
  15. package/dist/compaction/compaction.js +26 -57
  16. package/dist/compaction/compaction.js.map +1 -1
  17. package/dist/compaction/extraction.d.ts.map +1 -1
  18. package/dist/compaction/extraction.js +1 -6
  19. package/dist/compaction/extraction.js.map +1 -1
  20. package/dist/compaction/index.d.ts.map +1 -1
  21. package/dist/compaction/loop.d.ts.map +1 -1
  22. package/dist/compaction/loop.js.map +1 -1
  23. package/dist/compaction/token-budget.d.ts.map +1 -1
  24. package/dist/compaction/token-budget.js +7 -5
  25. package/dist/compaction/token-budget.js.map +1 -1
  26. package/dist/compaction/utils.d.ts +5 -0
  27. package/dist/compaction/utils.d.ts.map +1 -1
  28. package/dist/compaction/utils.js +20 -0
  29. package/dist/compaction/utils.js.map +1 -1
  30. package/dist/compaction/verification.d.ts.map +1 -1
  31. package/dist/compaction/verification.js +6 -5
  32. package/dist/compaction/verification.js.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/messages.d.ts.map +1 -1
  35. package/dist/messages.js.map +1 -1
  36. package/dist/node.d.ts.map +1 -1
  37. package/dist/proxy.d.ts +3 -2
  38. package/dist/proxy.d.ts.map +1 -1
  39. package/dist/proxy.js +88 -56
  40. package/dist/proxy.js.map +1 -1
  41. package/dist/reliability/classifier.d.ts.map +1 -1
  42. package/dist/reliability/classifier.js.map +1 -1
  43. package/dist/reliability/index.d.ts.map +1 -1
  44. package/dist/reliability/node.d.ts.map +1 -1
  45. package/dist/reliability/process-tree.d.ts.map +1 -1
  46. package/dist/reliability/process-tree.js.map +1 -1
  47. package/dist/reliability/provider-signatures.d.ts.map +1 -1
  48. package/dist/reliability/retry-controller.d.ts +1 -1
  49. package/dist/reliability/retry-controller.d.ts.map +1 -1
  50. package/dist/reliability/retry-controller.js +2 -7
  51. package/dist/reliability/retry-controller.js.map +1 -1
  52. package/dist/reliability/retry.d.ts.map +1 -1
  53. package/dist/reliability/retry.js.map +1 -1
  54. package/dist/reliability/watchdogs.d.ts.map +1 -1
  55. package/dist/reliability/watchdogs.js +24 -41
  56. package/dist/reliability/watchdogs.js.map +1 -1
  57. package/dist/session/message-retention.d.ts.map +1 -1
  58. package/dist/session/message-retention.js.map +1 -1
  59. package/dist/session/session-manager.d.ts +1 -1
  60. package/dist/session/session-manager.d.ts.map +1 -1
  61. package/dist/session/session-manager.js +81 -43
  62. package/dist/session/session-manager.js.map +1 -1
  63. package/dist/tool-failure-memory.d.ts.map +1 -1
  64. package/dist/tool-failure-memory.js +1 -1
  65. package/dist/tool-failure-memory.js.map +1 -1
  66. package/dist/types.d.ts +41 -0
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js.map +1 -1
  69. package/dist/usage.d.ts +3 -2
  70. package/dist/usage.d.ts.map +1 -1
  71. package/dist/usage.js +2 -10
  72. package/dist/usage.js.map +1 -1
  73. package/dist/utils/paths.d.ts.map +1 -1
  74. package/dist/utils/paths.js.map +1 -1
  75. package/dist/utils/shell-output.d.ts.map +1 -1
  76. package/dist/utils/shell-output.js.map +1 -1
  77. package/dist/utils/streaming-content.d.ts +23 -0
  78. package/dist/utils/streaming-content.d.ts.map +1 -0
  79. package/dist/utils/streaming-content.js +52 -0
  80. package/dist/utils/streaming-content.js.map +1 -0
  81. package/dist/utils/truncate.d.ts.map +1 -1
  82. package/dist/utils/truncate.js +55 -80
  83. package/dist/utils/truncate.js.map +1 -1
  84. package/dist/uuid.d.ts +1 -1
  85. package/dist/uuid.d.ts.map +1 -1
  86. package/dist/uuid.js +1 -1
  87. package/dist/uuid.js.map +1 -1
  88. package/package.json +7 -7
  89. package/dist/utils/streaming-lines.d.ts +0 -20
  90. package/dist/utils/streaming-lines.d.ts.map +0 -1
  91. package/dist/utils/streaming-lines.js +0 -79
  92. package/dist/utils/streaming-lines.js.map +0 -1
package/dist/proxy.js CHANGED
@@ -3,8 +3,13 @@
3
3
  * The server manages auth and proxies requests to LLM providers.
4
4
  */
5
5
  // Internal import for JSON parsing utility
6
- import { EventStream, parseStreamingJson, } from "@caupulican/pi-ai";
7
- import { StreamingLineDecoder } from "./utils/streaming-lines.js";
6
+ import { EventStream } from "@caupulican/pi-ai/event-stream";
7
+ import { parseStreamingJson } from "@caupulican/pi-ai/json-parse";
8
+ import { StreamingLineDecoder } from "@caupulican/pi-ai/streaming-lines";
9
+ import { createEmptyUsage } from "@caupulican/pi-ai/usage";
10
+ import { GeometricStreamingProjector, StreamingTextBuffer } from "./utils/streaming-content.js";
11
+ const textBuffers = new WeakMap();
12
+ const toolArgumentProjectors = new WeakMap();
8
13
  // Create stream class matching ProxyMessageEventStream
9
14
  class ProxyMessageEventStream extends EventStream {
10
15
  constructor() {
@@ -62,18 +67,25 @@ export function streamProxy(model, context, options) {
62
67
  api: model.api,
63
68
  provider: model.provider,
64
69
  model: model.id,
65
- usage: {
66
- input: 0,
67
- output: 0,
68
- cacheRead: 0,
69
- cacheWrite: 0,
70
- totalTokens: 0,
71
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
72
- },
70
+ usage: createEmptyUsage(),
73
71
  timestamp: Date.now(),
74
72
  };
75
73
  let reader;
76
74
  let terminalPushed = false;
75
+ const pushProxyLine = (line) => {
76
+ if (!line.startsWith("data: "))
77
+ return;
78
+ const data = line.slice(6).trim();
79
+ if (!data)
80
+ return;
81
+ const proxyEvent = JSON.parse(data);
82
+ const event = processProxyEvent(proxyEvent, partial);
83
+ if (!event)
84
+ return;
85
+ if (event.type === "done" || event.type === "error")
86
+ terminalPushed = true;
87
+ stream.push(event);
88
+ };
77
89
  const abortHandler = () => {
78
90
  if (reader) {
79
91
  reader.cancel("Request aborted by user").catch(() => { });
@@ -120,51 +132,20 @@ export function streamProxy(model, context, options) {
120
132
  throw new Error("Request aborted by user");
121
133
  }
122
134
  for (const line of lines.push(decoder.decode(value, { stream: true }))) {
123
- if (line.startsWith("data: ")) {
124
- const data = line.slice(6).trim();
125
- if (data) {
126
- const proxyEvent = JSON.parse(data);
127
- const event = processProxyEvent(proxyEvent, partial);
128
- if (event) {
129
- if (event.type === "done" || event.type === "error")
130
- terminalPushed = true;
131
- stream.push(event);
132
- }
133
- }
134
- }
135
+ pushProxyLine(line);
135
136
  }
136
137
  }
137
138
  for (const line of lines.push(decoder.decode())) {
138
- if (!line.startsWith("data: "))
139
- continue;
140
- const data = line.slice(6).trim();
141
- if (!data)
142
- continue;
143
- const proxyEvent = JSON.parse(data);
144
- const event = processProxyEvent(proxyEvent, partial);
145
- if (event) {
146
- if (event.type === "done" || event.type === "error")
147
- terminalPushed = true;
148
- stream.push(event);
149
- }
139
+ pushProxyLine(line);
150
140
  }
151
141
  const finalLine = lines.finish();
152
- if (finalLine?.startsWith("data: ")) {
153
- const data = finalLine.slice(6).trim();
154
- if (data) {
155
- const proxyEvent = JSON.parse(data);
156
- const event = processProxyEvent(proxyEvent, partial);
157
- if (event) {
158
- if (event.type === "done" || event.type === "error")
159
- terminalPushed = true;
160
- stream.push(event);
161
- }
162
- }
163
- }
142
+ if (finalLine)
143
+ pushProxyLine(finalLine);
164
144
  if (options.signal?.aborted) {
165
145
  throw new Error("Request aborted by user");
166
146
  }
167
147
  if (!terminalPushed) {
148
+ finalizeStreamingContent(partial);
168
149
  partial.stopReason = "error";
169
150
  partial.errorMessage = "stream ended before terminal event";
170
151
  stream.push({ type: "error", reason: "error", error: partial });
@@ -174,6 +155,7 @@ export function streamProxy(model, context, options) {
174
155
  catch (error) {
175
156
  const errorMessage = error instanceof Error ? error.message : String(error);
176
157
  const reason = options.signal?.aborted ? "aborted" : "error";
158
+ finalizeStreamingContent(partial);
177
159
  partial.stopReason = reason;
178
160
  partial.errorMessage = errorMessage;
179
161
  stream.push({
@@ -199,12 +181,12 @@ function processProxyEvent(proxyEvent, partial) {
199
181
  case "start":
200
182
  return { type: "start", partial };
201
183
  case "text_start":
202
- partial.content[proxyEvent.contentIndex] = { type: "text", text: "" };
184
+ partial.content[proxyEvent.contentIndex] = createStreamingTextContent("text");
203
185
  return { type: "text_start", contentIndex: proxyEvent.contentIndex, partial };
204
186
  case "text_delta": {
205
187
  const content = partial.content[proxyEvent.contentIndex];
206
188
  if (content?.type === "text") {
207
- content.text += proxyEvent.delta;
189
+ textBuffers.get(content)?.append(proxyEvent.delta);
208
190
  return {
209
191
  type: "text_delta",
210
192
  contentIndex: proxyEvent.contentIndex,
@@ -217,6 +199,7 @@ function processProxyEvent(proxyEvent, partial) {
217
199
  case "text_end": {
218
200
  const content = partial.content[proxyEvent.contentIndex];
219
201
  if (content?.type === "text") {
202
+ finalizeStreamingText(content);
220
203
  content.textSignature = proxyEvent.contentSignature;
221
204
  return {
222
205
  type: "text_end",
@@ -228,12 +211,12 @@ function processProxyEvent(proxyEvent, partial) {
228
211
  throw new Error("Received text_end for non-text content");
229
212
  }
230
213
  case "thinking_start":
231
- partial.content[proxyEvent.contentIndex] = { type: "thinking", thinking: "" };
214
+ partial.content[proxyEvent.contentIndex] = createStreamingTextContent("thinking");
232
215
  return { type: "thinking_start", contentIndex: proxyEvent.contentIndex, partial };
233
216
  case "thinking_delta": {
234
217
  const content = partial.content[proxyEvent.contentIndex];
235
218
  if (content?.type === "thinking") {
236
- content.thinking += proxyEvent.delta;
219
+ textBuffers.get(content)?.append(proxyEvent.delta);
237
220
  return {
238
221
  type: "thinking_delta",
239
222
  contentIndex: proxyEvent.contentIndex,
@@ -246,6 +229,7 @@ function processProxyEvent(proxyEvent, partial) {
246
229
  case "thinking_end": {
247
230
  const content = partial.content[proxyEvent.contentIndex];
248
231
  if (content?.type === "thinking") {
232
+ finalizeStreamingText(content);
249
233
  content.thinkingSignature = proxyEvent.contentSignature;
250
234
  return {
251
235
  type: "thinking_end",
@@ -256,21 +240,30 @@ function processProxyEvent(proxyEvent, partial) {
256
240
  }
257
241
  throw new Error("Received thinking_end for non-thinking content");
258
242
  }
259
- case "toolcall_start":
260
- partial.content[proxyEvent.contentIndex] = {
243
+ case "toolcall_start": {
244
+ const toolCall = {
261
245
  type: "toolCall",
262
246
  id: proxyEvent.id,
263
247
  name: proxyEvent.toolName,
264
248
  arguments: {},
265
- partialJson: "",
266
249
  };
250
+ toolArgumentProjectors.set(toolCall, new GeometricStreamingProjector((text) => parseStreamingJson(text)));
251
+ partial.content[proxyEvent.contentIndex] = toolCall;
267
252
  return { type: "toolcall_start", contentIndex: proxyEvent.contentIndex, partial };
253
+ }
268
254
  case "toolcall_delta": {
269
255
  const content = partial.content[proxyEvent.contentIndex];
270
256
  if (content?.type === "toolCall") {
271
- content.partialJson += proxyEvent.delta;
272
- content.arguments = parseStreamingJson(content.partialJson) || {};
257
+ const projected = toolArgumentProjectors.get(content)?.append(proxyEvent.delta);
258
+ if (projected)
259
+ content.arguments = projected;
273
260
  partial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity
261
+ const projectedContent = partial.content[proxyEvent.contentIndex];
262
+ if (projectedContent.type === "toolCall") {
263
+ const projector = toolArgumentProjectors.get(content);
264
+ if (projector)
265
+ toolArgumentProjectors.set(projectedContent, projector);
266
+ }
274
267
  return {
275
268
  type: "toolcall_delta",
276
269
  contentIndex: proxyEvent.contentIndex,
@@ -283,7 +276,8 @@ function processProxyEvent(proxyEvent, partial) {
283
276
  case "toolcall_end": {
284
277
  const content = partial.content[proxyEvent.contentIndex];
285
278
  if (content?.type === "toolCall") {
286
- delete content.partialJson;
279
+ content.arguments = toolArgumentProjectors.get(content)?.finish() ?? content.arguments;
280
+ toolArgumentProjectors.delete(content);
287
281
  return {
288
282
  type: "toolcall_end",
289
283
  contentIndex: proxyEvent.contentIndex,
@@ -294,10 +288,12 @@ function processProxyEvent(proxyEvent, partial) {
294
288
  return undefined;
295
289
  }
296
290
  case "done":
291
+ finalizeStreamingContent(partial);
297
292
  partial.stopReason = proxyEvent.reason;
298
293
  partial.usage = proxyEvent.usage;
299
294
  return { type: "done", reason: proxyEvent.reason, message: partial };
300
295
  case "error":
296
+ finalizeStreamingContent(partial);
301
297
  partial.stopReason = proxyEvent.reason;
302
298
  partial.errorMessage = proxyEvent.errorMessage;
303
299
  partial.usage = proxyEvent.usage;
@@ -309,4 +305,40 @@ function processProxyEvent(proxyEvent, partial) {
309
305
  }
310
306
  }
311
307
  }
308
+ function createStreamingTextContent(type) {
309
+ const buffer = new StreamingTextBuffer();
310
+ const content = type === "text" ? { type, text: "" } : { type, thinking: "" };
311
+ const field = type === "text" ? "text" : "thinking";
312
+ Object.defineProperty(content, field, {
313
+ configurable: true,
314
+ enumerable: true,
315
+ get: () => buffer.materialize(),
316
+ });
317
+ textBuffers.set(content, buffer);
318
+ return content;
319
+ }
320
+ function finalizeStreamingText(content) {
321
+ const buffer = textBuffers.get(content);
322
+ if (!buffer)
323
+ return;
324
+ const field = content.type === "text" ? "text" : "thinking";
325
+ Object.defineProperty(content, field, {
326
+ configurable: true,
327
+ enumerable: true,
328
+ value: buffer.materialize(),
329
+ writable: true,
330
+ });
331
+ textBuffers.delete(content);
332
+ }
333
+ function finalizeStreamingContent(message) {
334
+ for (const content of message.content) {
335
+ if (content.type === "text" || content.type === "thinking") {
336
+ finalizeStreamingText(content);
337
+ }
338
+ else {
339
+ content.arguments = toolArgumentProjectors.get(content)?.finish() ?? content.arguments;
340
+ toolArgumentProjectors.delete(content);
341
+ }
342
+ }
343
+ }
312
344
  //# sourceMappingURL=proxy.js.map
package/dist/proxy.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@caupulican/pi-ai\";\nimport { StreamingLineDecoder } from \"./utils/streaming-lines.ts\";\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"maxTokens\"\n\t| \"interactionMode\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tmaxTokens: options.maxTokens,\n\t\tinteractionMode: options.interactionMode,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\t\tlet terminalPushed = false;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tconst lines = new StreamingLineDecoder(64 * 1024 * 1024);\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tfor (const line of lines.push(decoder.decode(value, { stream: true }))) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalPushed = true;\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const line of lines.push(decoder.decode())) {\n\t\t\t\tif (!line.startsWith(\"data: \")) continue;\n\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\tif (!data) continue;\n\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\tif (event) {\n\t\t\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalPushed = true;\n\t\t\t\t\tstream.push(event);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst finalLine = lines.finish();\n\t\t\tif (finalLine?.startsWith(\"data: \")) {\n\t\t\t\tconst data = finalLine.slice(6).trim();\n\t\t\t\tif (data) {\n\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\tif (event) {\n\t\t\t\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalPushed = true;\n\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (!terminalPushed) {\n\t\t\t\tpartial.stopReason = \"error\";\n\t\t\t\tpartial.errorMessage = \"stream ended before terminal event\";\n\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: partial });\n\t\t\t}\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport { EventStream } from \"@caupulican/pi-ai/event-stream\";\nimport { parseStreamingJson } from \"@caupulican/pi-ai/json-parse\";\nimport { StreamingLineDecoder } from \"@caupulican/pi-ai/streaming-lines\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStopReason,\n\tTextContent,\n\tThinkingContent,\n\tToolCall,\n} from \"@caupulican/pi-ai/types\";\nimport { createEmptyUsage } from \"@caupulican/pi-ai/usage\";\nimport { GeometricStreamingProjector, StreamingTextBuffer } from \"./utils/streaming-content.ts\";\n\nconst textBuffers = new WeakMap<TextContent | ThinkingContent, StreamingTextBuffer>();\nconst toolArgumentProjectors = new WeakMap<ToolCall, GeometricStreamingProjector<Record<string, unknown>>>();\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"maxTokens\"\n\t| \"interactionMode\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tmaxTokens: options.maxTokens,\n\t\tinteractionMode: options.interactionMode,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<Api>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: createEmptyUsage(),\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\t\tlet terminalPushed = false;\n\t\tconst pushProxyLine = (line: string) => {\n\t\t\tif (!line.startsWith(\"data: \")) return;\n\t\t\tconst data = line.slice(6).trim();\n\t\t\tif (!data) return;\n\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\tif (!event) return;\n\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalPushed = true;\n\t\t\tstream.push(event);\n\t\t};\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tconst lines = new StreamingLineDecoder(64 * 1024 * 1024);\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tfor (const line of lines.push(decoder.decode(value, { stream: true }))) {\n\t\t\t\t\tpushProxyLine(line);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const line of lines.push(decoder.decode())) {\n\t\t\t\tpushProxyLine(line);\n\t\t\t}\n\t\t\tconst finalLine = lines.finish();\n\t\t\tif (finalLine) pushProxyLine(finalLine);\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (!terminalPushed) {\n\t\t\t\tfinalizeStreamingContent(partial);\n\t\t\t\tpartial.stopReason = \"error\";\n\t\t\t\tpartial.errorMessage = \"stream ended before terminal event\";\n\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: partial });\n\t\t\t}\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tfinalizeStreamingContent(partial);\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = createStreamingTextContent(\"text\");\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\ttextBuffers.get(content)?.append(proxyEvent.delta);\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tfinalizeStreamingText(content);\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = createStreamingTextContent(\"thinking\");\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\ttextBuffers.get(content)?.append(proxyEvent.delta);\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tfinalizeStreamingText(content);\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\": {\n\t\t\tconst toolCall: ToolCall = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t};\n\t\t\ttoolArgumentProjectors.set(\n\t\t\t\ttoolCall,\n\t\t\t\tnew GeometricStreamingProjector((text) => parseStreamingJson<Record<string, unknown>>(text)),\n\t\t\t);\n\t\t\tpartial.content[proxyEvent.contentIndex] = toolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t}\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tconst projected = toolArgumentProjectors.get(content)?.append(proxyEvent.delta);\n\t\t\t\tif (projected) content.arguments = projected;\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\tconst projectedContent = partial.content[proxyEvent.contentIndex];\n\t\t\t\tif (projectedContent.type === \"toolCall\") {\n\t\t\t\t\tconst projector = toolArgumentProjectors.get(content);\n\t\t\t\t\tif (projector) toolArgumentProjectors.set(projectedContent, projector);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tcontent.arguments = toolArgumentProjectors.get(content)?.finish() ?? content.arguments;\n\t\t\t\ttoolArgumentProjectors.delete(content);\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tfinalizeStreamingContent(partial);\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tfinalizeStreamingContent(partial);\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as { type?: unknown }).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nfunction createStreamingTextContent(type: \"text\"): TextContent;\nfunction createStreamingTextContent(type: \"thinking\"): ThinkingContent;\nfunction createStreamingTextContent(type: \"text\" | \"thinking\"): TextContent | ThinkingContent {\n\tconst buffer = new StreamingTextBuffer();\n\tconst content =\n\t\ttype === \"text\" ? ({ type, text: \"\" } satisfies TextContent) : ({ type, thinking: \"\" } satisfies ThinkingContent);\n\tconst field = type === \"text\" ? \"text\" : \"thinking\";\n\tObject.defineProperty(content, field, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tget: () => buffer.materialize(),\n\t});\n\ttextBuffers.set(content, buffer);\n\treturn content;\n}\n\nfunction finalizeStreamingText(content: TextContent | ThinkingContent): void {\n\tconst buffer = textBuffers.get(content);\n\tif (!buffer) return;\n\tconst field = content.type === \"text\" ? \"text\" : \"thinking\";\n\tObject.defineProperty(content, field, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tvalue: buffer.materialize(),\n\t\twritable: true,\n\t});\n\ttextBuffers.delete(content);\n}\n\nfunction finalizeStreamingContent(message: AssistantMessage): void {\n\tfor (const content of message.content) {\n\t\tif (content.type === \"text\" || content.type === \"thinking\") {\n\t\t\tfinalizeStreamingText(content);\n\t\t} else {\n\t\t\tcontent.arguments = toolArgumentProjectors.get(content)?.finish() ?? content.arguments;\n\t\t\ttoolArgumentProjectors.delete(content);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../src/reliability/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,aAAa,GACtB,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,SAAS,GACT,cAAc,GACd,kBAAkB,GAClB,MAAM,GACN,kBAAkB,GAClB,SAAS,GACT,SAAS,CAAC;AAEb,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CAuD5E","sourcesContent":["/**\n * Pure provider-failure classifier.\n *\n * One classification produces four independent action booleans (hermes-derived design):\n * the retry loop, compaction, credential rotation, and provider failover each read their\n * own flag, so one pipeline can route a 429 to rotation, an overflow to compaction, and a\n * billing error to failover without re-parsing error text at each site.\n *\n * Pattern sources: AgentSession._isRetryableError / _isNonRetryableProviderLimitError\n * (the live, battle-tested regexes), split by reason so each maps to distinct actions.\n * Detection of context overflow stays in @caupulican/pi-ai (needs model state); hosts pass\n * `contextOverflow` in.\n */\n\nimport { PROVIDER_FAILURE_SIGNATURES } from \"./provider-signatures.ts\";\n\nexport type FailureReason =\n\t| \"overloaded\"\n\t| \"rate_limit\"\n\t| \"server_error\"\n\t| \"network\"\n\t| \"stream_stall\"\n\t| \"context_overflow\"\n\t| \"auth\"\n\t| \"billing_or_quota\"\n\t| \"aborted\"\n\t| \"unknown\";\n\nexport interface ClassifiedError {\n\treason: FailureReason;\n\tretryable: boolean;\n\tshouldCompact: boolean;\n\tshouldRotateCredential: boolean;\n\tshouldFallback: boolean;\n\t/** Provider-suggested delay parsed from the message, capped by the retry policy at use site. */\n\tretryAfterMs?: number;\n\tmessage: string;\n}\n\nexport interface ClassifyFailureInput {\n\tmessage: string;\n\t/** Host-computed via pi-ai isContextOverflow(message, contextWindow). */\n\tcontextOverflow?: boolean;\n\t/** True when the failure came from an intentional abort (stopReason \"aborted\"). */\n\taborted?: boolean;\n\t/** Provider id; provider-specific signatures are checked before generic patterns. */\n\tprovider?: string;\n}\n\nconst BILLING_OR_QUOTA =\n\t/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing|usage.?limit(?:s)?\\s*(?:reached|exceeded|hit)|usage_limit_reached|hit your usage limit|hit your ChatGPT usage limit/i;\nconst AUTH = /\\b401\\b|unauthorized|invalid.?api.?key|authentication.?error|forbidden|permission.?denied/i;\nconst RATE_LIMIT = /rate.?limit|too many requests|(?<![A-Za-z0-9])429(?![A-Za-z0-9])/i;\nconst OVERLOADED = /overloaded/i;\nconst STREAM_STALL =\n\t/stream stalled|ended without|stream ended before message_stop|stream ended before a terminal response event|reset before headers/i;\nconst SERVER_ERROR =\n\t/(?<![A-Za-z0-9])(?:500|502|503|504)(?![A-Za-z0-9])|service.?unavailable|server.?error|internal.?error|provider.?returned.?error|upstream.?connect|http2 request did not get a response|ResourceExhausted|retry delay|you can retry your request|try your request again|please retry your request/i;\nconst NETWORK =\n\t/network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|getaddrinfo|ENOTFOUND|EAI_AGAIN|socket hang up|socket connection was closed|timed? out|timeout|terminated/i;\n\nconst RETRY_AFTER_S = /retry.?(?:after|in)\\s+(\\d+(?:\\.\\d+)?)\\s*s\\b|\"retryDelay\"\\s*:\\s*\"(\\d+(?:\\.\\d+)?)s\"/i;\nconst RETRY_AFTER_MS = /retry.?(?:after|in)\\s+(\\d+)\\s*ms\\b/i;\n\nfunction parseRetryAfterMs(message: string): number | undefined {\n\tconst ms = RETRY_AFTER_MS.exec(message);\n\tif (ms) return Number(ms[1]);\n\tconst s = RETRY_AFTER_S.exec(message);\n\tif (s) return Math.round(Number(s[1] ?? s[2]) * 1000);\n\treturn undefined;\n}\n\nexport function classifyFailure(input: ClassifyFailureInput): ClassifiedError {\n\tconst message = input.message;\n\tconst retryAfterMs = parseRetryAfterMs(message);\n\n\tconst base = {\n\t\tretryable: false,\n\t\tshouldCompact: false,\n\t\tshouldRotateCredential: false,\n\t\tshouldFallback: false,\n\t\tmessage,\n\t};\n\n\tconst withRetry = <T extends { reason: FailureReason }>(obj: T): T | (T & { retryAfterMs: number }) =>\n\t\tretryAfterMs !== undefined ? { ...obj, retryAfterMs } : obj;\n\n\tif (input.aborted) return withRetry({ ...base, reason: \"aborted\" });\n\tif (input.contextOverflow) return withRetry({ ...base, reason: \"context_overflow\", shouldCompact: true });\n\tconst providerSignatures = input.provider ? (PROVIDER_FAILURE_SIGNATURES[input.provider] ?? []) : [];\n\tfor (const signature of providerSignatures) {\n\t\tif (signature.pattern.test(message)) {\n\t\t\treturn withRetry({\n\t\t\t\t...base,\n\t\t\t\treason: signature.reason,\n\t\t\t\tshouldFallback: signature.reason === \"billing_or_quota\" || signature.reason === \"auth\",\n\t\t\t\tshouldRotateCredential: signature.reason === \"auth\",\n\t\t\t\tretryable:\n\t\t\t\t\tsignature.reason === \"rate_limit\" ||\n\t\t\t\t\tsignature.reason === \"overloaded\" ||\n\t\t\t\t\tsignature.reason === \"server_error\" ||\n\t\t\t\t\tsignature.reason === \"network\" ||\n\t\t\t\t\tsignature.reason === \"stream_stall\",\n\t\t\t});\n\t\t}\n\t}\n\tif (BILLING_OR_QUOTA.test(message)) return withRetry({ ...base, reason: \"billing_or_quota\", shouldFallback: true });\n\tif (AUTH.test(message))\n\t\treturn withRetry({ ...base, reason: \"auth\", shouldRotateCredential: true, shouldFallback: true });\n\n\tconst isRateLimit = RATE_LIMIT.test(message);\n\tif (isRateLimit || OVERLOADED.test(message)) {\n\t\treturn withRetry({\n\t\t\t...base,\n\t\t\treason: isRateLimit ? \"rate_limit\" : \"overloaded\",\n\t\t\tretryable: true,\n\t\t\tshouldRotateCredential: true,\n\t\t\tshouldFallback: true,\n\t\t});\n\t}\n\tif (STREAM_STALL.test(message))\n\t\treturn withRetry({ ...base, reason: \"stream_stall\", retryable: true, shouldFallback: true });\n\tif (SERVER_ERROR.test(message))\n\t\treturn withRetry({ ...base, reason: \"server_error\", retryable: true, shouldFallback: true });\n\tif (NETWORK.test(message)) return withRetry({ ...base, reason: \"network\", retryable: true, shouldFallback: true });\n\n\treturn withRetry({ ...base, reason: \"unknown\" });\n}\n"]}
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../src/reliability/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,aAAa,GACtB,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,SAAS,GACT,cAAc,GACd,kBAAkB,GAClB,MAAM,GACN,kBAAkB,GAClB,SAAS,GACT,SAAS,CAAC;AAEb,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CAuD5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/reliability/classifier.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure provider-failure classifier.\n *\n * One classification produces four independent action booleans (hermes-derived design):\n * the retry loop, compaction, credential rotation, and provider failover each read their\n * own flag, so one pipeline can route a 429 to rotation, an overflow to compaction, and a\n * billing error to failover without re-parsing error text at each site.\n *\n * Pattern sources: AgentSession._isRetryableError / _isNonRetryableProviderLimitError\n * (the live, battle-tested regexes), split by reason so each maps to distinct actions.\n * Detection of context overflow stays in @caupulican/pi-ai (needs model state); hosts pass\n * `contextOverflow` in.\n */\n\nimport { PROVIDER_FAILURE_SIGNATURES } from \"./provider-signatures.ts\";\n\nexport type FailureReason =\n\t| \"overloaded\"\n\t| \"rate_limit\"\n\t| \"server_error\"\n\t| \"network\"\n\t| \"stream_stall\"\n\t| \"context_overflow\"\n\t| \"auth\"\n\t| \"billing_or_quota\"\n\t| \"aborted\"\n\t| \"unknown\";\n\nexport interface ClassifiedError {\n\treason: FailureReason;\n\tretryable: boolean;\n\tshouldCompact: boolean;\n\tshouldRotateCredential: boolean;\n\tshouldFallback: boolean;\n\t/** Provider-suggested delay parsed from the message, capped by the retry policy at use site. */\n\tretryAfterMs?: number;\n\tmessage: string;\n}\n\nexport interface ClassifyFailureInput {\n\tmessage: string;\n\t/** Host-computed via pi-ai isContextOverflow(message, contextWindow). */\n\tcontextOverflow?: boolean;\n\t/** True when the failure came from an intentional abort (stopReason \"aborted\"). */\n\taborted?: boolean;\n\t/** Provider id; provider-specific signatures are checked before generic patterns. */\n\tprovider?: string;\n}\n\nconst BILLING_OR_QUOTA =\n\t/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing|usage.?limit(?:s)?\\s*(?:reached|exceeded|hit)|usage_limit_reached|hit your usage limit|hit your ChatGPT usage limit/i;\nconst AUTH = /\\b401\\b|unauthorized|invalid.?api.?key|authentication.?error|forbidden|permission.?denied/i;\nconst RATE_LIMIT = /rate.?limit|too many requests|(?<![A-Za-z0-9])429(?![A-Za-z0-9])/i;\nconst OVERLOADED = /overloaded/i;\nconst STREAM_STALL =\n\t/stream stalled|ended without|stream ended before message_stop|stream ended before a terminal response event|reset before headers/i;\nconst SERVER_ERROR =\n\t/(?<![A-Za-z0-9])(?:500|502|503|504)(?![A-Za-z0-9])|service.?unavailable|server.?error|internal.?error|provider.?returned.?error|upstream.?connect|http2 request did not get a response|ResourceExhausted|retry delay|you can retry your request|try your request again|please retry your request/i;\nconst NETWORK =\n\t/network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|getaddrinfo|ENOTFOUND|EAI_AGAIN|socket hang up|socket connection was closed|timed? out|timeout|terminated/i;\n\nconst RETRY_AFTER_S = /retry.?(?:after|in)\\s+(\\d+(?:\\.\\d+)?)\\s*s\\b|\"retryDelay\"\\s*:\\s*\"(\\d+(?:\\.\\d+)?)s\"/i;\nconst RETRY_AFTER_MS = /retry.?(?:after|in)\\s+(\\d+)\\s*ms\\b/i;\n\nfunction parseRetryAfterMs(message: string): number | undefined {\n\tconst ms = RETRY_AFTER_MS.exec(message);\n\tif (ms) return Number(ms[1]);\n\tconst s = RETRY_AFTER_S.exec(message);\n\tif (s) return Math.round(Number(s[1] ?? s[2]) * 1000);\n\treturn undefined;\n}\n\nexport function classifyFailure(input: ClassifyFailureInput): ClassifiedError {\n\tconst message = input.message;\n\tconst retryAfterMs = parseRetryAfterMs(message);\n\n\tconst base = {\n\t\tretryable: false,\n\t\tshouldCompact: false,\n\t\tshouldRotateCredential: false,\n\t\tshouldFallback: false,\n\t\tmessage,\n\t};\n\n\tconst withRetry = <T extends { reason: FailureReason }>(obj: T): T | (T & { retryAfterMs: number }) =>\n\t\tretryAfterMs !== undefined ? { ...obj, retryAfterMs } : obj;\n\n\tif (input.aborted) return withRetry({ ...base, reason: \"aborted\" });\n\tif (input.contextOverflow) return withRetry({ ...base, reason: \"context_overflow\", shouldCompact: true });\n\tconst providerSignatures = input.provider ? (PROVIDER_FAILURE_SIGNATURES[input.provider] ?? []) : [];\n\tfor (const signature of providerSignatures) {\n\t\tif (signature.pattern.test(message)) {\n\t\t\treturn withRetry({\n\t\t\t\t...base,\n\t\t\t\treason: signature.reason,\n\t\t\t\tshouldFallback: signature.reason === \"billing_or_quota\" || signature.reason === \"auth\",\n\t\t\t\tshouldRotateCredential: signature.reason === \"auth\",\n\t\t\t\tretryable:\n\t\t\t\t\tsignature.reason === \"rate_limit\" ||\n\t\t\t\t\tsignature.reason === \"overloaded\" ||\n\t\t\t\t\tsignature.reason === \"server_error\" ||\n\t\t\t\t\tsignature.reason === \"network\" ||\n\t\t\t\t\tsignature.reason === \"stream_stall\",\n\t\t\t});\n\t\t}\n\t}\n\tif (BILLING_OR_QUOTA.test(message)) return withRetry({ ...base, reason: \"billing_or_quota\", shouldFallback: true });\n\tif (AUTH.test(message))\n\t\treturn withRetry({ ...base, reason: \"auth\", shouldRotateCredential: true, shouldFallback: true });\n\n\tconst isRateLimit = RATE_LIMIT.test(message);\n\tif (isRateLimit || OVERLOADED.test(message)) {\n\t\treturn withRetry({\n\t\t\t...base,\n\t\t\treason: isRateLimit ? \"rate_limit\" : \"overloaded\",\n\t\t\tretryable: true,\n\t\t\tshouldRotateCredential: true,\n\t\t\tshouldFallback: true,\n\t\t});\n\t}\n\tif (STREAM_STALL.test(message))\n\t\treturn withRetry({ ...base, reason: \"stream_stall\", retryable: true, shouldFallback: true });\n\tif (SERVER_ERROR.test(message))\n\t\treturn withRetry({ ...base, reason: \"server_error\", retryable: true, shouldFallback: true });\n\tif (NETWORK.test(message)) return withRetry({ ...base, reason: \"network\", retryable: true, shouldFallback: true });\n\n\treturn withRetry({ ...base, reason: \"unknown\" });\n}\n"]}
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/reliability/classifier.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure provider-failure classifier.\n *\n * One classification produces four independent action booleans (hermes-derived design):\n * the retry loop, compaction, credential rotation, and provider failover each read their\n * own flag, so one pipeline can route a 429 to rotation, an overflow to compaction, and a\n * billing error to failover without re-parsing error text at each site.\n *\n * Pattern sources: AgentSession._isRetryableError / _isNonRetryableProviderLimitError\n * (the live, battle-tested regexes), split by reason so each maps to distinct actions.\n * Detection of context overflow stays in @caupulican/pi-ai (needs model state); hosts pass\n * `contextOverflow` in.\n */\n\nimport { PROVIDER_FAILURE_SIGNATURES } from \"./provider-signatures.ts\";\n\nexport type FailureReason =\n\t| \"overloaded\"\n\t| \"rate_limit\"\n\t| \"server_error\"\n\t| \"network\"\n\t| \"stream_stall\"\n\t| \"context_overflow\"\n\t| \"auth\"\n\t| \"billing_or_quota\"\n\t| \"aborted\"\n\t| \"unknown\";\n\nexport interface ClassifiedError {\n\treason: FailureReason;\n\tretryable: boolean;\n\tshouldCompact: boolean;\n\tshouldRotateCredential: boolean;\n\tshouldFallback: boolean;\n\t/** Provider-suggested delay parsed from the message, capped by the retry policy at use site. */\n\tretryAfterMs?: number;\n\tmessage: string;\n}\n\nexport interface ClassifyFailureInput {\n\tmessage: string;\n\t/** Host-computed via pi-ai isContextOverflow(message, contextWindow). */\n\tcontextOverflow?: boolean;\n\t/** True when the failure came from an intentional abort (stopReason \"aborted\"). */\n\taborted?: boolean;\n\t/** Provider id; provider-specific signatures are checked before generic patterns. */\n\tprovider?: string;\n}\n\nconst BILLING_OR_QUOTA =\n\t/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing|usage.?limit(?:s)?\\s*(?:reached|exceeded|hit)|usage_limit_reached|hit your usage limit|hit your ChatGPT usage limit/i;\nconst AUTH = /\\b401\\b|unauthorized|invalid.?api.?key|authentication.?error|forbidden|permission.?denied/i;\nconst RATE_LIMIT = /rate.?limit|too many requests|(?<![A-Za-z0-9])429(?![A-Za-z0-9])/i;\nconst OVERLOADED = /overloaded/i;\nconst STREAM_STALL =\n\t/stream stalled|ended without|stream ended before message_stop|stream ended before a terminal response event|reset before headers/i;\nconst SERVER_ERROR =\n\t/(?<![A-Za-z0-9])(?:500|502|503|504)(?![A-Za-z0-9])|service.?unavailable|server.?error|internal.?error|provider.?returned.?error|upstream.?connect|http2 request did not get a response|ResourceExhausted|retry delay|you can retry your request|try your request again|please retry your request/i;\nconst NETWORK =\n\t/network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|getaddrinfo|ENOTFOUND|EAI_AGAIN|socket hang up|socket connection was closed|timed? out|timeout|terminated/i;\n\nconst RETRY_AFTER_S = /retry.?(?:after|in)\\s+(\\d+(?:\\.\\d+)?)\\s*s\\b|\"retryDelay\"\\s*:\\s*\"(\\d+(?:\\.\\d+)?)s\"/i;\nconst RETRY_AFTER_MS = /retry.?(?:after|in)\\s+(\\d+)\\s*ms\\b/i;\n\nfunction parseRetryAfterMs(message: string): number | undefined {\n\tconst ms = RETRY_AFTER_MS.exec(message);\n\tif (ms) return Number(ms[1]);\n\tconst s = RETRY_AFTER_S.exec(message);\n\tif (s) return Math.round(Number(s[1] ?? s[2]) * 1000);\n\treturn undefined;\n}\n\nexport function classifyFailure(input: ClassifyFailureInput): ClassifiedError {\n\tconst message = input.message;\n\tconst retryAfterMs = parseRetryAfterMs(message);\n\n\tconst base = {\n\t\tretryable: false,\n\t\tshouldCompact: false,\n\t\tshouldRotateCredential: false,\n\t\tshouldFallback: false,\n\t\tmessage,\n\t};\n\n\tconst withRetry = <T extends { reason: FailureReason }>(obj: T): T | (T & { retryAfterMs: number }) =>\n\t\tretryAfterMs !== undefined ? { ...obj, retryAfterMs } : obj;\n\n\tif (input.aborted) return withRetry({ ...base, reason: \"aborted\" });\n\tif (input.contextOverflow) return withRetry({ ...base, reason: \"context_overflow\", shouldCompact: true });\n\tconst providerSignatures = input.provider ? (PROVIDER_FAILURE_SIGNATURES[input.provider] ?? []) : [];\n\tfor (const signature of providerSignatures) {\n\t\tif (signature.pattern.test(message)) {\n\t\t\treturn withRetry({\n\t\t\t\t...base,\n\t\t\t\treason: signature.reason,\n\t\t\t\tshouldFallback: signature.reason === \"billing_or_quota\" || signature.reason === \"auth\",\n\t\t\t\tshouldRotateCredential: signature.reason === \"auth\",\n\t\t\t\tretryable:\n\t\t\t\t\tsignature.reason === \"rate_limit\" ||\n\t\t\t\t\tsignature.reason === \"overloaded\" ||\n\t\t\t\t\tsignature.reason === \"server_error\" ||\n\t\t\t\t\tsignature.reason === \"network\" ||\n\t\t\t\t\tsignature.reason === \"stream_stall\",\n\t\t\t});\n\t\t}\n\t}\n\tif (BILLING_OR_QUOTA.test(message)) return withRetry({ ...base, reason: \"billing_or_quota\", shouldFallback: true });\n\tif (AUTH.test(message))\n\t\treturn withRetry({ ...base, reason: \"auth\", shouldRotateCredential: true, shouldFallback: true });\n\n\tconst isRateLimit = RATE_LIMIT.test(message);\n\tif (isRateLimit || OVERLOADED.test(message)) {\n\t\treturn withRetry({\n\t\t\t...base,\n\t\t\treason: isRateLimit ? \"rate_limit\" : \"overloaded\",\n\t\t\tretryable: true,\n\t\t\tshouldRotateCredential: true,\n\t\t\tshouldFallback: true,\n\t\t});\n\t}\n\tif (STREAM_STALL.test(message))\n\t\treturn withRetry({ ...base, reason: \"stream_stall\", retryable: true, shouldFallback: true });\n\tif (SERVER_ERROR.test(message))\n\t\treturn withRetry({ ...base, reason: \"server_error\", retryable: true, shouldFallback: true });\n\tif (NETWORK.test(message)) return withRetry({ ...base, reason: \"network\", retryable: true, shouldFallback: true });\n\n\treturn withRetry({ ...base, reason: \"unknown\" });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC","sourcesContent":["export * from \"./classifier.ts\";\nexport * from \"./provider-signatures.ts\";\nexport * from \"./retry.ts\";\nexport * from \"./retry-controller.ts\";\nexport * from \"./watchdogs.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/reliability/node.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC","sourcesContent":["export * from \"./process-tree.ts\";\n"]}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/reliability/node.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAIlE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQnD;AAoBD,MAAM,WAAW,eAAe;IAC/B,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEvE,yFAAqF;AACrF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAmE9F;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU7C","sourcesContent":["/**\n * Process-tree kill primitives.\n *\n * WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the\n * process exits. Graceful escalation therefore follows the spawned child's exit\n * event and a one-shot deadline; it never polls PID liveness.\n *\n * Group kill (-pid) requires the target to be a process-group leader (spawned with\n * detached: true). Both functions fall back to single-pid signaling otherwise.\n */\nimport { type ChildProcess, spawnSync } from \"node:child_process\";\n\nconst KILL_ACKNOWLEDGEMENT_MS = 1000;\n\nexport function isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\t// EPERM means it exists but we lack permission — still alive.\n\t\treturn (err as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction signalTree(pid: number, signal: NodeJS.Signals): boolean {\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t\treturn true;\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction isChildTerminal(child: ChildProcess): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null;\n}\n\nexport interface KillTreeOptions {\n\t/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */\n\tgraceMs?: number;\n}\n\nexport type KillTreeOutcome = \"already_dead\" | \"terminated\" | \"killed\";\n\n/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */\nexport function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome> {\n\tconst pid = child.pid;\n\tif (pid === undefined || isChildTerminal(child)) return Promise.resolve(\"already_dead\");\n\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tlet escalated = false;\n\t\tlet escalationTimer: NodeJS.Timeout | undefined;\n\t\tlet acknowledgementTimer: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (escalationTimer) clearTimeout(escalationTimer);\n\t\t\tif (acknowledgementTimer) clearTimeout(acknowledgementTimer);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t};\n\t\tconst settle = (outcome: KillTreeOutcome) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(outcome);\n\t\t};\n\t\tconst onExit = () => settle(escalated ? \"killed\" : \"terminated\");\n\t\tconst onError = () => settle(escalated ? \"killed\" : \"already_dead\");\n\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"error\", onError);\n\t\tif (isChildTerminal(child)) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tescalated = true;\n\t\t\tkillTreeNow(pid);\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!signalTree(pid, \"SIGTERM\")) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\t\tconst graceMs = Math.max(0, opts?.graceMs ?? 5000);\n\t\tescalationTimer = setTimeout(() => {\n\t\t\tescalationTimer = undefined;\n\t\t\tif (isChildTerminal(child)) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tescalated = true;\n\t\t\tif (!signalTree(pid, \"SIGKILL\")) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t}, graceMs);\n\t\tescalationTimer.unref();\n\t});\n}\n\n/** Immediate tree kill (SIGKILL / synchronous taskkill). */\nexport function killTreeNow(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\tspawnSync(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 10_000,\n\t\t\twindowsHide: true,\n\t\t});\n\t} else {\n\t\tsignalTree(pid, \"SIGKILL\");\n\t}\n}\n"]}
1
+ {"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAIlE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQnD;AAoBD,MAAM,WAAW,eAAe;IAC/B,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEvE,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAmE9F;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"process-tree.js","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process-tree kill primitives.\n *\n * WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the\n * process exits. Graceful escalation therefore follows the spawned child's exit\n * event and a one-shot deadline; it never polls PID liveness.\n *\n * Group kill (-pid) requires the target to be a process-group leader (spawned with\n * detached: true). Both functions fall back to single-pid signaling otherwise.\n */\nimport { type ChildProcess, spawnSync } from \"node:child_process\";\n\nconst KILL_ACKNOWLEDGEMENT_MS = 1000;\n\nexport function isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\t// EPERM means it exists but we lack permission — still alive.\n\t\treturn (err as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction signalTree(pid: number, signal: NodeJS.Signals): boolean {\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t\treturn true;\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction isChildTerminal(child: ChildProcess): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null;\n}\n\nexport interface KillTreeOptions {\n\t/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */\n\tgraceMs?: number;\n}\n\nexport type KillTreeOutcome = \"already_dead\" | \"terminated\" | \"killed\";\n\n/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */\nexport function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome> {\n\tconst pid = child.pid;\n\tif (pid === undefined || isChildTerminal(child)) return Promise.resolve(\"already_dead\");\n\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tlet escalated = false;\n\t\tlet escalationTimer: NodeJS.Timeout | undefined;\n\t\tlet acknowledgementTimer: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (escalationTimer) clearTimeout(escalationTimer);\n\t\t\tif (acknowledgementTimer) clearTimeout(acknowledgementTimer);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t};\n\t\tconst settle = (outcome: KillTreeOutcome) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(outcome);\n\t\t};\n\t\tconst onExit = () => settle(escalated ? \"killed\" : \"terminated\");\n\t\tconst onError = () => settle(escalated ? \"killed\" : \"already_dead\");\n\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"error\", onError);\n\t\tif (isChildTerminal(child)) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tescalated = true;\n\t\t\tkillTreeNow(pid);\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!signalTree(pid, \"SIGTERM\")) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\t\tconst graceMs = Math.max(0, opts?.graceMs ?? 5000);\n\t\tescalationTimer = setTimeout(() => {\n\t\t\tescalationTimer = undefined;\n\t\t\tif (isChildTerminal(child)) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tescalated = true;\n\t\t\tif (!signalTree(pid, \"SIGKILL\")) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t}, graceMs);\n\t\tescalationTimer.unref();\n\t});\n}\n\n/** Immediate tree kill (SIGKILL / synchronous taskkill). */\nexport function killTreeNow(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\tspawnSync(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 10_000,\n\t\t\twindowsHide: true,\n\t\t});\n\t} else {\n\t\tsignalTree(pid, \"SIGKILL\");\n\t}\n}\n"]}
1
+ {"version":3,"file":"process-tree.js","sourceRoot":"","sources":["../../src/reliability/process-tree.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process-tree kill primitives.\n *\n * WHY: node's ChildProcess.killed flag is set when a signal is sent, not when the\n * process exits. Graceful escalation therefore follows the spawned child's exit\n * event and a one-shot deadline; it never polls PID liveness.\n *\n * Group kill (-pid) requires the target to be a process-group leader (spawned with\n * detached: true). Both functions fall back to single-pid signaling otherwise.\n */\nimport { type ChildProcess, spawnSync } from \"node:child_process\";\n\nconst KILL_ACKNOWLEDGEMENT_MS = 1000;\n\nexport function isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\t// EPERM means it exists but we lack permission — still alive.\n\t\treturn (err as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction signalTree(pid: number, signal: NodeJS.Signals): boolean {\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t\treturn true;\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction isChildTerminal(child: ChildProcess): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null;\n}\n\nexport interface KillTreeOptions {\n\t/** How long to wait for the child's exit event after SIGTERM before SIGKILL. Default 5000ms. */\n\tgraceMs?: number;\n}\n\nexport type KillTreeOutcome = \"already_dead\" | \"terminated\" | \"killed\";\n\n/** Graceful tree kill: SIGTERM → child exit event or one-shot deadline → SIGKILL. */\nexport function killTree(child: ChildProcess, opts?: KillTreeOptions): Promise<KillTreeOutcome> {\n\tconst pid = child.pid;\n\tif (pid === undefined || isChildTerminal(child)) return Promise.resolve(\"already_dead\");\n\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tlet escalated = false;\n\t\tlet escalationTimer: NodeJS.Timeout | undefined;\n\t\tlet acknowledgementTimer: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (escalationTimer) clearTimeout(escalationTimer);\n\t\t\tif (acknowledgementTimer) clearTimeout(acknowledgementTimer);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t};\n\t\tconst settle = (outcome: KillTreeOutcome) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(outcome);\n\t\t};\n\t\tconst onExit = () => settle(escalated ? \"killed\" : \"terminated\");\n\t\tconst onError = () => settle(escalated ? \"killed\" : \"already_dead\");\n\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"error\", onError);\n\t\tif (isChildTerminal(child)) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tescalated = true;\n\t\t\tkillTreeNow(pid);\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!signalTree(pid, \"SIGTERM\")) {\n\t\t\tsettle(\"already_dead\");\n\t\t\treturn;\n\t\t}\n\t\tconst graceMs = Math.max(0, opts?.graceMs ?? 5000);\n\t\tescalationTimer = setTimeout(() => {\n\t\t\tescalationTimer = undefined;\n\t\t\tif (isChildTerminal(child)) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tescalated = true;\n\t\t\tif (!signalTree(pid, \"SIGKILL\")) {\n\t\t\t\tsettle(\"terminated\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tacknowledgementTimer = setTimeout(() => {\n\t\t\t\tchild.unref();\n\t\t\t\tsettle(\"killed\");\n\t\t\t}, KILL_ACKNOWLEDGEMENT_MS);\n\t\t\tacknowledgementTimer.unref();\n\t\t}, graceMs);\n\t\tescalationTimer.unref();\n\t});\n}\n\n/** Immediate tree kill (SIGKILL / synchronous taskkill). */\nexport function killTreeNow(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\tspawnSync(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 10_000,\n\t\t\twindowsHide: true,\n\t\t});\n\t} else {\n\t\tsignalTree(pid, \"SIGKILL\");\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"provider-signatures.d.ts","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CA+BpF,CAAC","sourcesContent":["import type { FailureReason } from \"./classifier.ts\";\n\nexport interface ProviderSignature {\n\treason: FailureReason;\n\tpattern: RegExp;\n\t/** Evidence citation: sdk package+version+file, corpus capture, or adapter file:line. */\n\tsource: string;\n\t/** True when the SDK-rendered fixture uses a vendor body shape that still awaits corpus confirmation. */\n\tprovisional?: boolean;\n}\n\n/** Provider-thrown-message signatures checked before the generic ladder. */\nexport const PROVIDER_FAILURE_SIGNATURES: Record<string, readonly ProviderSignature[]> = {\n\tanthropic: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /credit balance is too low/i,\n\t\t\tsource: \"sdk:@anthropic-ai/sdk@0.91.1 node_modules/@anthropic-ai/sdk/core/error.js\",\n\t\t},\n\t],\n\tmistral: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:@mistralai/mistralai@2.2.1 node_modules/@mistralai/mistralai/esm/models/errors/sdkerror.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\topenrouter: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:openai@6.26.0 node_modules/openai/core/error.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\t\"openai-codex\": [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /You have hit your ChatGPT usage limit/i,\n\t\t\tsource: \"packages/ai/src/providers/openai-codex-responses.ts:1402\",\n\t\t},\n\t],\n};\n"]}
1
+ {"version":3,"file":"provider-signatures.d.ts","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CA+BpF,CAAC"}
@@ -12,7 +12,7 @@
12
12
  * start event fires, so prompts arriving inside start handlers queue as steering instead of
13
13
  * racing the retry continuation.
14
14
  */
15
- import { type AssistantMessage } from "@caupulican/pi-ai";
15
+ import type { AssistantMessage } from "@caupulican/pi-ai/types";
16
16
  import type { AgentMessage } from "../types.ts";
17
17
  import { type RetryPolicy } from "./retry.ts";
18
18
  /** The slice of an Agent the retry driver reads and mutates: just the live transcript. */
@@ -1 +1 @@
1
- {"version":3,"file":"retry-controller.d.ts","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAuB,KAAK,WAAW,EAAkB,MAAM,YAAY,CAAC;AAEnF,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAED,iGAAiG;AACjG,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAEhD,YACC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,qBAAqB,EACtC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,MAAM,EAM9B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+FAA+F;IAC/F,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,oGAAkG;IAClG,KAAK,IAAI,IAAI,CAEZ;IAED,8EAA8E;IAC9E,KAAK,IAAI,IAAI,CAEZ;IAED;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyD9D;CACD","sourcesContent":["/**\n * Host-agnostic auto-retry driver.\n *\n * Owns the retry attempt counter and the abortable backoff for one agent run. It reads the\n * failure verdict from {@link classifyFailure} (fed a host-computed context-overflow flag), so\n * billing/auth are terminal and overflow routes to compaction — never to a pointless retry.\n * The controller only ever touches `agent.state.messages` (it drops the trailing assistant error\n * before retrying); durable history stays the host session's responsibility.\n *\n * Ported from AgentSession._prepareRetry / _isRetryableError so the exact event ordering and\n * exhaustion semantics carry over: the retry window is marked active (isRetrying) before the\n * start event fires, so prompts arriving inside start handlers queue as steering instead of\n * racing the retry continuation.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"@caupulican/pi-ai\";\nimport type { AgentMessage } from \"../types.ts\";\nimport { classifyFailure } from \"./classifier.ts\";\nimport { computeRetryDelayMs, type RetryPolicy, sleepAbortable } from \"./retry.ts\";\n\n/** The slice of an Agent the retry driver reads and mutates: just the live transcript. */\nexport interface RetryAgent {\n\treadonly state: { messages: AgentMessage[] };\n}\n\nexport interface RetryStartInfo {\n\tattempt: number;\n\tmaxAttempts: number;\n\tdelayMs: number;\n\terrorMessage: string;\n}\n\nexport interface RetryEndInfo {\n\tsuccess: boolean;\n\tattempt: number;\n\tfinalError?: string;\n}\n\nexport interface RetryEvents {\n\tonRetryStart(info: RetryStartInfo): void;\n\tonRetryEnd(info: RetryEndInfo): void;\n}\n\n/** Runtime retry policy: the backoff shape plus the on/off switch the host resolves per call. */\nexport type RetryControllerPolicy = RetryPolicy & { enabled: boolean };\n\nexport class RetryController {\n\tprivate _attempt = 0;\n\tprivate _abortController: AbortController | undefined;\n\tprivate readonly agent: RetryAgent;\n\tprivate readonly getPolicy: () => RetryControllerPolicy;\n\tprivate readonly events: RetryEvents;\n\tprivate readonly getContextWindow: () => number;\n\n\tconstructor(\n\t\tagent: RetryAgent,\n\t\tgetPolicy: () => RetryControllerPolicy,\n\t\tevents: RetryEvents,\n\t\tgetContextWindow: () => number,\n\t) {\n\t\tthis.agent = agent;\n\t\tthis.getPolicy = getPolicy;\n\t\tthis.events = events;\n\t\tthis.getContextWindow = getContextWindow;\n\t}\n\n\t/** Completed retry attempts for the current run (0 when not retrying). */\n\tget attempt(): number {\n\t\treturn this._attempt;\n\t}\n\n\t/** True from the instant onRetryStart fires until the backoff sleep resolves or is aborted. */\n\tget isRetrying(): boolean {\n\t\treturn this._abortController !== undefined;\n\t}\n\n\t/** Clear the attempt counter — the host calls this after a successful turn or a final failure. */\n\treset(): void {\n\t\tthis._attempt = 0;\n\t}\n\n\t/** Cancel an in-progress backoff; the pending prepareRetry resolves false. */\n\tabort(): void {\n\t\tthis._abortController?.abort();\n\t}\n\n\t/**\n\t * Classify `message`; if it is retryable and attempts remain, drop the trailing assistant\n\t * error from agent state, emit the start event, and wait out the backoff (abortable).\n\t * @returns true if the caller should continue the agent, false otherwise.\n\t */\n\tasync prepareRetry(message: AssistantMessage): Promise<boolean> {\n\t\tconst policy = this.getPolicy();\n\t\tif (!policy.enabled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// The classifier is the single source of the retry verdict: context overflow (host-computed\n\t\t// from the live window) routes to compaction, billing/auth are terminal, transient failures retry.\n\t\tconst classified = classifyFailure({\n\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\tcontextOverflow: isContextOverflow(message, this.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t\tif (!classified.retryable) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attempt++;\n\t\tif (this._attempt > policy.maxAttempts) {\n\t\t\t// Preserve the completed attempt count so the host can emit the final failure.\n\t\t\tthis._attempt--;\n\t\t\treturn false;\n\t\t}\n\n\t\tconst delayMs = computeRetryDelayMs(policy, this._attempt, { retryAfterMs: classified.retryAfterMs });\n\n\t\t// The retry window counts as active work from the instant listeners hear about it:\n\t\t// isRetrying must already be true inside onRetryStart handlers so prompts arriving there\n\t\t// queue as steering instead of racing the retry continuation.\n\t\tthis._abortController = new AbortController();\n\n\t\tthis.events.onRetryStart({\n\t\t\tattempt: this._attempt,\n\t\t\tmaxAttempts: policy.maxAttempts,\n\t\t\tdelayMs,\n\t\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t\t});\n\n\t\t// Remove the trailing assistant error from live agent state (the host session keeps it in history).\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\n\t\ttry {\n\t\t\tawait sleepAbortable(delayMs, this._abortController.signal);\n\t\t} catch {\n\t\t\t// Aborted mid-backoff: report the cancellation and reset so the next turn starts clean.\n\t\t\tconst attempt = this._attempt;\n\t\t\tthis._attempt = 0;\n\t\t\tthis.events.onRetryEnd({ success: false, attempt, finalError: \"Retry cancelled\" });\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tthis._abortController = undefined;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"]}
1
+ {"version":3,"file":"retry-controller.d.ts","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAuB,KAAK,WAAW,EAAkB,MAAM,YAAY,CAAC;AAEnF,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAED,iGAAiG;AACjG,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAEhD,YACC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,qBAAqB,EACtC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,MAAM,EAM9B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+FAA+F;IAC/F,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,kGAAkG;IAClG,KAAK,IAAI,IAAI,CAEZ;IAED,8EAA8E;IAC9E,KAAK,IAAI,IAAI,CAEZ;IAED;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyD9D;CACD"}
@@ -12,17 +12,12 @@
12
12
  * start event fires, so prompts arriving inside start handlers queue as steering instead of
13
13
  * racing the retry continuation.
14
14
  */
15
- import { isContextOverflow } from "@caupulican/pi-ai";
15
+ import { isContextOverflow } from "@caupulican/pi-ai/overflow";
16
16
  import { classifyFailure } from "./classifier.js";
17
17
  import { computeRetryDelayMs, sleepAbortable } from "./retry.js";
18
18
  export class RetryController {
19
- _attempt = 0;
20
- _abortController;
21
- agent;
22
- getPolicy;
23
- events;
24
- getContextWindow;
25
19
  constructor(agent, getPolicy, events, getContextWindow) {
20
+ this._attempt = 0;
26
21
  this.agent = agent;
27
22
  this.getPolicy = getPolicy;
28
23
  this.events = events;