@prestyj/agent 5.8.0 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -87,9 +87,22 @@ interface AgentMaxTurnsEvent {
87
87
  totalTurns: number;
88
88
  maxTurns: number;
89
89
  }
90
+ /**
91
+ * Warning signal emitted when a turn ended on a non-clean stop reason —
92
+ * `max_tokens` (output clipped at the model's output-token limit), `refusal`,
93
+ * or a provider-reported `error` stop. Distinguishes a truncated/degraded
94
+ * completion from a clean one so hosts can warn the user instead of silently
95
+ * presenting incomplete output as done.
96
+ */
97
+ interface AgentTruncatedEvent {
98
+ type: "truncated";
99
+ reason: "max_tokens" | "refusal" | "provider_error";
100
+ /** True when the loop injected a continuation and will keep going. */
101
+ continued: boolean;
102
+ }
90
103
  interface AgentRetryEvent {
91
104
  type: "retry";
92
- reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch";
105
+ reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch" | "runaway_toolcall";
93
106
  attempt: number;
94
107
  maxAttempts: number;
95
108
  delayMs: number;
@@ -135,7 +148,7 @@ interface AgentFollowUpMessageEvent {
135
148
  type: "follow_up_message";
136
149
  content: Message["content"];
137
150
  }
138
- type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
151
+ type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentTruncatedEvent | AgentErrorEvent;
139
152
  interface TransformContextOptions {
140
153
  /** Force a transform after the provider reports context overflow. */
141
154
  force?: boolean;
@@ -311,4 +324,13 @@ declare function isBillingError(err: unknown): boolean;
311
324
  declare function isUsageLimitError(err: unknown): boolean;
312
325
  declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
313
326
 
314
- export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
327
+ /**
328
+ * A local inference server (llama.cpp, vLLM, Ollama, LM Studio) can spend
329
+ * minutes prefilling a large prompt before it emits its first token. The
330
+ * first-event watchdog that protects hosted streams turns that into an abort
331
+ * → retry → cold-prefill loop that never converges, so it is disabled for
332
+ * loopback backends.
333
+ */
334
+ declare function isLocalBackendUrl(baseUrl?: string): boolean;
335
+
336
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isLocalBackendUrl, isUsageLimitError, setStreamDiagnostic };
package/dist/index.d.ts CHANGED
@@ -87,9 +87,22 @@ interface AgentMaxTurnsEvent {
87
87
  totalTurns: number;
88
88
  maxTurns: number;
89
89
  }
90
+ /**
91
+ * Warning signal emitted when a turn ended on a non-clean stop reason —
92
+ * `max_tokens` (output clipped at the model's output-token limit), `refusal`,
93
+ * or a provider-reported `error` stop. Distinguishes a truncated/degraded
94
+ * completion from a clean one so hosts can warn the user instead of silently
95
+ * presenting incomplete output as done.
96
+ */
97
+ interface AgentTruncatedEvent {
98
+ type: "truncated";
99
+ reason: "max_tokens" | "refusal" | "provider_error";
100
+ /** True when the loop injected a continuation and will keep going. */
101
+ continued: boolean;
102
+ }
90
103
  interface AgentRetryEvent {
91
104
  type: "retry";
92
- reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch";
105
+ reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch" | "runaway_toolcall";
93
106
  attempt: number;
94
107
  maxAttempts: number;
95
108
  delayMs: number;
@@ -135,7 +148,7 @@ interface AgentFollowUpMessageEvent {
135
148
  type: "follow_up_message";
136
149
  content: Message["content"];
137
150
  }
138
- type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
151
+ type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentTruncatedEvent | AgentErrorEvent;
139
152
  interface TransformContextOptions {
140
153
  /** Force a transform after the provider reports context overflow. */
141
154
  force?: boolean;
@@ -311,4 +324,13 @@ declare function isBillingError(err: unknown): boolean;
311
324
  declare function isUsageLimitError(err: unknown): boolean;
312
325
  declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
313
326
 
314
- export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
327
+ /**
328
+ * A local inference server (llama.cpp, vLLM, Ollama, LM Studio) can spend
329
+ * minutes prefilling a large prompt before it emits its first token. The
330
+ * first-event watchdog that protects hosted streams turns that into an abort
331
+ * → retry → cold-prefill loop that never converges, so it is disabled for
332
+ * loopback backends.
333
+ */
334
+ declare function isLocalBackendUrl(baseUrl?: string): boolean;
335
+
336
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isLocalBackendUrl, isUsageLimitError, setStreamDiagnostic };
package/dist/index.js CHANGED
@@ -10,6 +10,30 @@ import {
10
10
  isHardBillingMessage,
11
11
  redactValue
12
12
  } from "@prestyj/ai";
13
+
14
+ // src/local-backend.ts
15
+ function isLocalBackendUrl(baseUrl) {
16
+ if (!baseUrl) return false;
17
+ let host;
18
+ try {
19
+ host = new URL(baseUrl).hostname.toLowerCase();
20
+ } catch {
21
+ return false;
22
+ }
23
+ if (host === "[::1]" || host === "::1") return true;
24
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
25
+ if (host === "0.0.0.0") return true;
26
+ if (host.endsWith(".local")) return true;
27
+ const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
28
+ if (ipv4) {
29
+ const octets = ipv4.slice(1).map(Number);
30
+ if (octets.some((n) => n > 255)) return false;
31
+ return octets[0] === 127;
32
+ }
33
+ return false;
34
+ }
35
+
36
+ // src/agent-loop.ts
13
37
  var DEFAULT_MAX_TURNS = 300;
14
38
  var _diagFn = null;
15
39
  function setStreamDiagnostic(fn) {
@@ -188,7 +212,11 @@ function createAbortError() {
188
212
  }
189
213
  function abortablePromise(promise, signal) {
190
214
  if (!signal) return promise;
191
- if (signal.aborted) return Promise.reject(createAbortError());
215
+ if (signal.aborted) {
216
+ promise.catch(() => {
217
+ });
218
+ return Promise.reject(createAbortError());
219
+ }
192
220
  return new Promise((resolve, reject) => {
193
221
  let settled = false;
194
222
  const cleanup = () => signal.removeEventListener("abort", onAbort);
@@ -244,6 +272,7 @@ async function* agentLoop(messages, options) {
244
272
  let overloadRetries = 0;
245
273
  let emptyResponseRetries = 0;
246
274
  let stallRetries = 0;
275
+ let runawayToolcallRetries = 0;
247
276
  let overflowCompactionAttempts = 0;
248
277
  let toolResultTruncationAttempted = false;
249
278
  const invalidToolArgumentCounts = /* @__PURE__ */ new Map();
@@ -252,11 +281,16 @@ async function* agentLoop(messages, options) {
252
281
  const MAX_OVERLOAD_RETRIES = 10;
253
282
  const MAX_EMPTY_RESPONSE_RETRIES = 2;
254
283
  const MAX_STALL_RETRIES = 10;
284
+ const MAX_RUNAWAY_TOOLCALL_RETRIES = 2;
285
+ const RUNAWAY_TOOLCALL_RETRY_DELAY_MS = 1e3;
255
286
  const MAX_OVERFLOW_COMPACTIONS = 2;
256
287
  const STALL_RETRIES_BEFORE_NON_STREAMING = 2;
257
288
  const STALL_DELAY_MS = 1e3;
258
289
  const MIN_PARTIAL_PRESERVE_CHARS = 200;
259
290
  const PARTIAL_CONTINUATION_PROMPT = "[Your previous response was cut off by a connection failure. The text above is what was already delivered to the user. Continue exactly from where it stopped \u2014 do not repeat or restart it.]";
291
+ const MAX_OUTPUT_CONTINUATIONS = 2;
292
+ const MAX_TOKENS_CONTINUATION_PROMPT = "[Your previous response hit the output-token limit and was cut off. The text above is what was already delivered to the user. Continue exactly from where it stopped \u2014 do not repeat or restart it.]";
293
+ let maxTokensContinuations = 0;
260
294
  const OVERLOAD_BASE_DELAY_MS = 2e3;
261
295
  const OVERLOAD_MAX_DELAY_MS = 3e4;
262
296
  const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
@@ -266,9 +300,10 @@ async function* agentLoop(messages, options) {
266
300
  const STREAM_THINKING_IDLE_TIMEOUT_MS = 3e5;
267
301
  const STREAM_THINKING_HARD_TIMEOUT_MS = 6e5;
268
302
  const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
269
- const isSakana = options.provider === "sakana";
270
- const firstEventTimeoutMs = isSakana ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
271
- const initialHardTimeoutMs = isSakana ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
303
+ const usesSilentReasoningBudget = options.provider === "sakana" || options.provider === "openai" && options.thinking != null;
304
+ const localBackend = isLocalBackendUrl(options.baseUrl);
305
+ const firstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
306
+ const initialHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
272
307
  const MAX_TOOLCALL_DELTA_CHARS = 1e6;
273
308
  const MAX_TOOLCALL_DELTA_EVENTS = 2e4;
274
309
  let logicalTurnStartedAt = 0;
@@ -296,7 +331,11 @@ async function* agentLoop(messages, options) {
296
331
  messages: messages.length,
297
332
  chars: msgChars,
298
333
  provider: options.provider,
299
- model: options.model
334
+ model: options.model,
335
+ thinking: options.thinking ?? "off",
336
+ firstEventTimeoutMs,
337
+ initialHardTimeoutMs,
338
+ localBackend
300
339
  });
301
340
  }
302
341
  if (firstTurn && options.getSteeringMessages) {
@@ -354,6 +393,7 @@ async function* agentLoop(messages, options) {
354
393
  if (useNonStreamingFallback) return;
355
394
  if (idleTimer) clearTimeout(idleTimer);
356
395
  const timeoutMs = hasReceivedEvent ? STREAM_IDLE_TIMEOUT_MS : hasReceivedThinking ? STREAM_THINKING_IDLE_TIMEOUT_MS : firstEventTimeoutMs;
396
+ if (!Number.isFinite(timeoutMs)) return;
357
397
  idleTimer = setTimeout(() => {
358
398
  diag("idle_timeout_fired", {
359
399
  events: streamEventCount,
@@ -659,11 +699,33 @@ async function* agentLoop(messages, options) {
659
699
  provider: options.provider,
660
700
  model: options.model
661
701
  });
702
+ if (runawayToolcallRetries < MAX_RUNAWAY_TOOLCALL_RETRIES) {
703
+ runawayToolcallRetries++;
704
+ const delayMs = RUNAWAY_TOOLCALL_RETRY_DELAY_MS * runawayToolcallRetries;
705
+ diag("retry", {
706
+ reason: "runaway_toolcall",
707
+ attempt: runawayToolcallRetries,
708
+ maxAttempts: MAX_RUNAWAY_TOOLCALL_RETRIES,
709
+ delayMs,
710
+ ...runawayDetected
711
+ });
712
+ yield {
713
+ type: "retry",
714
+ reason: "runaway_toolcall",
715
+ attempt: runawayToolcallRetries,
716
+ maxAttempts: MAX_RUNAWAY_TOOLCALL_RETRIES,
717
+ delayMs,
718
+ silent: true
719
+ };
720
+ await abortableSleep(delayMs, options.signal);
721
+ turn--;
722
+ continue;
723
+ }
662
724
  const detail = runawayDetected.kind === "chars" ? `${(runawayDetected.chars / 1024).toFixed(0)} KB of tool-call arguments` : `${runawayDetected.events} tool-call delta events`;
663
725
  yield {
664
726
  type: "error",
665
727
  error: new Error(
666
- `The model glitched mid-tool-call and produced ${detail} without closing the call. This is usually an upstream model bug \u2014 try the same request again or switch models. Your conversation is preserved.`
728
+ `The model repeatedly failed to close a tool call after ${MAX_RUNAWAY_TOOLCALL_RETRIES} automatic retries (${detail}). Switch models and retry; your conversation is preserved.`
667
729
  )
668
730
  };
669
731
  break;
@@ -716,15 +778,29 @@ async function* agentLoop(messages, options) {
716
778
  continue;
717
779
  }
718
780
  if (transportFailure) {
781
+ const cause = malformed ? "malformed_stream" : socketDrop ? "socket_drop" : "stream_stall";
719
782
  diag("stall_exhausted", {
720
783
  stallRetries: MAX_STALL_RETRIES,
721
784
  provider: options.provider,
722
- model: options.model
785
+ model: options.model,
786
+ cause,
787
+ nonStreaming: useNonStreamingFallback,
788
+ events: streamEventCount,
789
+ eventTypes: eventTypeCounts,
790
+ lastEventType,
791
+ sinceLastEventMs: Date.now() - lastEventTime,
792
+ attemptDurationMs: Date.now() - streamCallStart,
793
+ maxConsumerLagMs
723
794
  });
724
795
  yield {
725
796
  type: "error",
726
- error: new Error(
727
- `The API provider's stream stalled ${MAX_STALL_RETRIES} times \u2014 the provider may be experiencing capacity issues. Your conversation is preserved. Send another message to retry.`
797
+ error: new EZCoderAIError(
798
+ `The connection to the API provider stopped responding after ${MAX_STALL_RETRIES} automatic retries. Your conversation is preserved.`,
799
+ {
800
+ source: "network",
801
+ hint: "Retry once. If it keeps happening on this device, disable any VPN or proxy and allow EZ Coder through firewall or antivirus web protection.",
802
+ cause: err
803
+ }
728
804
  )
729
805
  };
730
806
  break;
@@ -764,6 +840,7 @@ async function* agentLoop(messages, options) {
764
840
  }
765
841
  overloadRetries = 0;
766
842
  stallRetries = 0;
843
+ runawayToolcallRetries = 0;
767
844
  const contentArr = Array.isArray(response.message.content) ? response.message.content : null;
768
845
  const hasActionableContent = response.message.content !== "" && contentArr !== null && contentArr.some(
769
846
  (p) => p.type === "text" || p.type === "tool_call" || p.type === "server_tool_call"
@@ -835,6 +912,27 @@ async function* agentLoop(messages, options) {
835
912
  consecutivePauses = 0;
836
913
  const allToolCalls = extractToolCalls(response.message.content);
837
914
  if (response.stopReason !== "tool_use" && allToolCalls.length === 0) {
915
+ if (response.stopReason === "max_tokens") {
916
+ if (maxTokensContinuations < MAX_OUTPUT_CONTINUATIONS) {
917
+ maxTokensContinuations++;
918
+ diag("max_tokens_continuation", {
919
+ attempt: maxTokensContinuations,
920
+ maxAttempts: MAX_OUTPUT_CONTINUATIONS,
921
+ provider: options.provider,
922
+ model: options.model
923
+ });
924
+ yield { type: "truncated", reason: "max_tokens", continued: true };
925
+ messages.push({ role: "user", content: MAX_TOKENS_CONTINUATION_PROMPT });
926
+ continue;
927
+ }
928
+ yield { type: "truncated", reason: "max_tokens", continued: false };
929
+ } else if (response.stopReason === "refusal" || response.stopReason === "error") {
930
+ yield {
931
+ type: "truncated",
932
+ reason: response.stopReason === "refusal" ? "refusal" : "provider_error",
933
+ continued: false
934
+ };
935
+ }
838
936
  if (options.getSteeringMessages) {
839
937
  const steering = await options.getSteeringMessages();
840
938
  if (steering && steering.length > 0) {
@@ -1201,16 +1299,22 @@ function capToolResults(toolResults, maxToolResultChars) {
1201
1299
  const max = Math.min(maxToolResultChars, hardMax);
1202
1300
  for (const toolResult of toolResults) {
1203
1301
  if (typeof toolResult.content !== "string" || toolResult.content.length <= max) continue;
1302
+ const originalChars = toolResult.content.length;
1204
1303
  const headChars = Math.floor(max * 0.7);
1205
1304
  const tailChars = max - headChars;
1206
1305
  const head = toolResult.content.slice(0, headChars);
1207
1306
  const tail = toolResult.content.slice(-tailChars);
1208
- const omitted = toolResult.content.length - headChars - tailChars;
1307
+ const omitted = originalChars - headChars - tailChars;
1209
1308
  toolResult.content = head + `
1210
1309
 
1211
1310
  [... ${omitted} characters omitted ...]
1212
1311
 
1213
1312
  ` + tail;
1313
+ toolResult.capped = {
1314
+ originalChars,
1315
+ keptChars: toolResult.content.length,
1316
+ scope: "per-result"
1317
+ };
1214
1318
  }
1215
1319
  }
1216
1320
  function capTurnToolResults(toolResults, maxTurnToolResultChars) {
@@ -1231,14 +1335,20 @@ function capTurnToolResults(toolResults, maxTurnToolResultChars) {
1231
1335
  continue;
1232
1336
  }
1233
1337
  remaining -= fairShare;
1338
+ const originalChars = toolResult.content.length;
1234
1339
  const headChars = Math.floor(fairShare * 0.7);
1235
1340
  const tailChars = fairShare - headChars;
1236
- const omitted = toolResult.content.length - fairShare;
1341
+ const omitted = originalChars - fairShare;
1237
1342
  toolResult.content = toolResult.content.slice(0, headChars) + `
1238
1343
 
1239
1344
  [... ${omitted} characters trimmed: this turn's combined tool results exceeded the per-turn budget. Re-run this call alone with narrower filters or offset/limit if you need the omitted content ...]
1240
1345
 
1241
1346
  ` + (tailChars > 0 ? toolResult.content.slice(-tailChars) : "");
1347
+ toolResult.capped = {
1348
+ originalChars: toolResult.capped?.originalChars ?? originalChars,
1349
+ keptChars: toolResult.content.length,
1350
+ scope: "per-turn"
1351
+ };
1242
1352
  }
1243
1353
  }
1244
1354
  function normalizeToolResult(raw) {
@@ -1509,6 +1619,7 @@ export {
1509
1619
  isAbortError,
1510
1620
  isBillingError,
1511
1621
  isContextOverflow,
1622
+ isLocalBackendUrl,
1512
1623
  isUsageLimitError,
1513
1624
  setStreamDiagnostic
1514
1625
  };