@prestyj/agent 4.3.239 → 4.5.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.js CHANGED
@@ -5,7 +5,8 @@ import { EventStream as EventStream2 } from "@prestyj/ai";
5
5
  import { ZodError, prettifyError } from "zod";
6
6
  import {
7
7
  stream,
8
- EventStream
8
+ EventStream,
9
+ isHardBillingMessage
9
10
  } from "@prestyj/ai";
10
11
  var DEFAULT_MAX_TURNS = 300;
11
12
  var _diagFn = null;
@@ -23,6 +24,8 @@ function isAbortError(err) {
23
24
  }
24
25
  function isContextOverflow(err) {
25
26
  if (!(err instanceof Error)) return false;
27
+ const overflowStatus = err.statusCode;
28
+ if (overflowStatus === 402) return false;
26
29
  if (isBillingError(err)) return false;
27
30
  const msg = err.message.toLowerCase();
28
31
  return msg.includes("prompt is too long") || msg.includes("prompt too long") || msg.includes("input is too long") || msg.includes("context_length_exceeded") || msg.includes("context_window_exceeded") || msg.includes("maximum context length") || msg.includes("exceeds model context window") || msg.includes("exceeds the context window") || msg.includes("content_too_large") || msg.includes("request_too_large") || msg.includes("reduce the length") || msg.includes("please shorten") || msg.includes("token") && msg.includes("exceed");
@@ -67,13 +70,21 @@ function extractContextOverflowDetails(err) {
67
70
  }
68
71
  function isBillingError(err) {
69
72
  if (!(err instanceof Error)) return false;
70
- const msg = err.message.toLowerCase();
71
- return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge") || msg.includes("subscription plan") || msg.includes("does not yet include access") || msg.includes("token quota") || msg.includes("exceeded_current_quota_error") || msg.includes("check your account balance");
73
+ const statusCode = err.statusCode;
74
+ if (statusCode === 402) return true;
75
+ return isHardBillingMessage(err.message);
72
76
  }
73
77
  function isUsageLimitError(err) {
74
78
  if (!(err instanceof Error)) return false;
75
79
  return /usage limit reached/i.test(err.message);
76
80
  }
81
+ function serverResetDelayMs(err) {
82
+ if (!(err instanceof Error)) return void 0;
83
+ const resetsAt = err.resetsAt;
84
+ if (typeof resetsAt !== "number" || !Number.isFinite(resetsAt)) return void 0;
85
+ const delayMs = resetsAt * 1e3 - Date.now();
86
+ return delayMs > 0 ? delayMs : void 0;
87
+ }
77
88
  function isToolPairingError(err) {
78
89
  if (!(err instanceof Error)) return false;
79
90
  const msg = err.message.toLowerCase();
@@ -94,6 +105,7 @@ function classifyOverload(err) {
94
105
  const msg = err.message.toLowerCase();
95
106
  const errorWithStatus = err;
96
107
  const statusCode = typeof errorWithStatus.statusCode === "number" ? errorWithStatus.statusCode : void 0;
108
+ if (statusCode === 402) return null;
97
109
  if (statusCode === 429 || msg.includes("rate_limit") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429")) {
98
110
  return "rate_limit";
99
111
  }
@@ -153,23 +165,51 @@ function isTransportFailure(err) {
153
165
  }
154
166
  return false;
155
167
  }
168
+ function createAbortError() {
169
+ return new DOMException("Aborted", "AbortError");
170
+ }
171
+ function abortablePromise(promise, signal) {
172
+ if (!signal) return promise;
173
+ if (signal.aborted) return Promise.reject(createAbortError());
174
+ return new Promise((resolve, reject) => {
175
+ let settled = false;
176
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
177
+ const resolveOnce = (value) => {
178
+ if (settled) return;
179
+ settled = true;
180
+ cleanup();
181
+ resolve(value);
182
+ };
183
+ const rejectOnce = (err) => {
184
+ if (settled) return;
185
+ settled = true;
186
+ cleanup();
187
+ reject(err instanceof Error ? err : new Error(String(err)));
188
+ };
189
+ const onAbort = () => rejectOnce(createAbortError());
190
+ signal.addEventListener("abort", onAbort, { once: true });
191
+ promise.then(resolveOnce, rejectOnce);
192
+ });
193
+ }
156
194
  function abortableSleep(ms, signal) {
157
- if (signal?.aborted) {
158
- return Promise.reject(new DOMException("Aborted", "AbortError"));
159
- }
195
+ if (signal?.aborted) return Promise.reject(createAbortError());
160
196
  return new Promise((resolve, reject) => {
161
- let onAbort = null;
162
197
  const timer = setTimeout(() => {
163
- if (onAbort) signal?.removeEventListener("abort", onAbort);
198
+ signal?.removeEventListener("abort", onAbort);
164
199
  resolve();
165
200
  }, ms);
166
- onAbort = () => {
201
+ const onAbort = () => {
167
202
  clearTimeout(timer);
168
- reject(new DOMException("Aborted", "AbortError"));
203
+ reject(createAbortError());
169
204
  };
170
205
  signal?.addEventListener("abort", onAbort, { once: true });
171
206
  });
172
207
  }
208
+ function closeIterator(iterator) {
209
+ if (!iterator?.return) return;
210
+ Promise.resolve(iterator.return()).catch(() => {
211
+ });
212
+ }
173
213
  async function* agentLoop(messages, options) {
174
214
  const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
175
215
  const maxContinuations = options.maxContinuations ?? 5;
@@ -294,6 +334,7 @@ async function* agentLoop(messages, options) {
294
334
  idleTimedOut = true;
295
335
  streamController.abort();
296
336
  }, hardTimeoutMs);
337
+ let streamIterator = null;
297
338
  try {
298
339
  diag("stream_call", { nonStreaming: useNonStreamingFallback });
299
340
  streamCallStart = Date.now();
@@ -316,9 +357,11 @@ async function* agentLoop(messages, options) {
316
357
  promptCacheKey: options.promptCacheKey,
317
358
  serviceTier: options.serviceTier,
318
359
  supportsImages: options.supportsImages,
360
+ supportsVideo: options.supportsVideo,
319
361
  compaction: options.compaction,
320
362
  clearToolUses: options.clearToolUses,
321
363
  userAgent: options.userAgent,
364
+ defaultHeaders: options.defaultHeaders,
322
365
  // Flip to non-streaming fallback after repeated stream stalls.
323
366
  ...useNonStreamingFallback ? { streaming: false } : {}
324
367
  });
@@ -331,7 +374,11 @@ async function* agentLoop(messages, options) {
331
374
  streamCallStart = Date.now();
332
375
  lastYieldEndTime = Date.now();
333
376
  resetIdleTimer();
334
- for await (const event of result) {
377
+ streamIterator = result[Symbol.asyncIterator]();
378
+ while (true) {
379
+ const next = await abortablePromise(streamIterator.next(), streamController.signal);
380
+ if (next.done) break;
381
+ const event = next.value;
335
382
  const pullTime = Date.now();
336
383
  const consumerLag = pullTime - lastYieldEndTime;
337
384
  if (streamEventCount > 0 && consumerLag > maxConsumerLagMs) {
@@ -428,8 +475,9 @@ async function* agentLoop(messages, options) {
428
475
  maxConsumerLagMs,
429
476
  eventTypes: eventTypeCounts
430
477
  });
431
- response = await result.response;
478
+ response = await abortablePromise(result.response, streamController.signal);
432
479
  } catch (err) {
480
+ if (streamController.signal.aborted) closeIterator(streamIterator);
433
481
  const errMsg = err instanceof Error ? err.message : String(err);
434
482
  diag("stream_error", {
435
483
  error: errMsg.slice(0, 200),
@@ -528,7 +576,8 @@ async function* agentLoop(messages, options) {
528
576
  const overloadKind = classifyOverload(err);
529
577
  if (overloadRetries < MAX_OVERLOAD_RETRIES && overloadKind) {
530
578
  overloadRetries++;
531
- const delayMs = Math.min(
579
+ const serverDelayMs = serverResetDelayMs(err);
580
+ const delayMs = serverDelayMs !== void 0 ? Math.min(serverDelayMs, OVERLOAD_MAX_DELAY_MS) : Math.min(
532
581
  OVERLOAD_BASE_DELAY_MS * 2 ** (overloadRetries - 1),
533
582
  OVERLOAD_MAX_DELAY_MS
534
583
  );
@@ -828,7 +877,10 @@ async function executeSingleToolCall(toolCall, options, pushEvent) {
828
877
  });
829
878
  }
830
879
  };
831
- const raw = await tool.execute(parsed, ctx);
880
+ const raw = await abortablePromise(
881
+ Promise.resolve().then(() => tool.execute(parsed, ctx)),
882
+ ctx.signal
883
+ );
832
884
  const normalized = normalizeToolResult(raw);
833
885
  resultContent = normalized.content;
834
886
  details = normalized.details;