@prestyj/agent 5.7.0 → 5.9.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.cjs +120 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -7
- package/dist/index.d.ts +32 -7
- package/dist/index.js +120 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -154,7 +154,7 @@ function classifyOverload(err) {
|
|
|
154
154
|
if (statusCode === 529 || msg.includes("overloaded") || msg.includes("529")) {
|
|
155
155
|
return "overloaded";
|
|
156
156
|
}
|
|
157
|
-
if (statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504 || msg.includes("api_error") || msg.includes("server_error") || msg.includes("internal server error") || msg.includes("bad gateway") || msg.includes("service unavailable") || msg.includes("gateway timeout")) {
|
|
157
|
+
if (statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504 || statusCode === 507 || msg.includes("api_error") || msg.includes("server_error") || msg.includes("internal server error") || msg.includes("bad gateway") || msg.includes("service unavailable") || msg.includes("gateway timeout") || msg.includes("exceeded request buffer limit while retrying upstream")) {
|
|
158
158
|
return "provider_error";
|
|
159
159
|
}
|
|
160
160
|
if (isOpaqueProviderMessage(err.message)) {
|
|
@@ -215,7 +215,11 @@ function createAbortError() {
|
|
|
215
215
|
}
|
|
216
216
|
function abortablePromise(promise, signal) {
|
|
217
217
|
if (!signal) return promise;
|
|
218
|
-
if (signal.aborted)
|
|
218
|
+
if (signal.aborted) {
|
|
219
|
+
promise.catch(() => {
|
|
220
|
+
});
|
|
221
|
+
return Promise.reject(createAbortError());
|
|
222
|
+
}
|
|
219
223
|
return new Promise((resolve, reject) => {
|
|
220
224
|
let settled = false;
|
|
221
225
|
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
@@ -260,6 +264,8 @@ async function* agentLoop(messages, options) {
|
|
|
260
264
|
const maxContinuations = options.maxContinuations ?? 5;
|
|
261
265
|
let toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
|
|
262
266
|
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
267
|
+
let latestProviderUsage;
|
|
268
|
+
let usageAnchorIndex;
|
|
263
269
|
let turn = 0;
|
|
264
270
|
let hitMaxTurns = false;
|
|
265
271
|
let firstTurn = true;
|
|
@@ -269,6 +275,7 @@ async function* agentLoop(messages, options) {
|
|
|
269
275
|
let overloadRetries = 0;
|
|
270
276
|
let emptyResponseRetries = 0;
|
|
271
277
|
let stallRetries = 0;
|
|
278
|
+
let runawayToolcallRetries = 0;
|
|
272
279
|
let overflowCompactionAttempts = 0;
|
|
273
280
|
let toolResultTruncationAttempted = false;
|
|
274
281
|
const invalidToolArgumentCounts = /* @__PURE__ */ new Map();
|
|
@@ -277,11 +284,16 @@ async function* agentLoop(messages, options) {
|
|
|
277
284
|
const MAX_OVERLOAD_RETRIES = 10;
|
|
278
285
|
const MAX_EMPTY_RESPONSE_RETRIES = 2;
|
|
279
286
|
const MAX_STALL_RETRIES = 10;
|
|
287
|
+
const MAX_RUNAWAY_TOOLCALL_RETRIES = 2;
|
|
288
|
+
const RUNAWAY_TOOLCALL_RETRY_DELAY_MS = 1e3;
|
|
280
289
|
const MAX_OVERFLOW_COMPACTIONS = 2;
|
|
281
290
|
const STALL_RETRIES_BEFORE_NON_STREAMING = 2;
|
|
282
291
|
const STALL_DELAY_MS = 1e3;
|
|
283
292
|
const MIN_PARTIAL_PRESERVE_CHARS = 200;
|
|
284
293
|
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.]";
|
|
294
|
+
const MAX_OUTPUT_CONTINUATIONS = 2;
|
|
295
|
+
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.]";
|
|
296
|
+
let maxTokensContinuations = 0;
|
|
285
297
|
const OVERLOAD_BASE_DELAY_MS = 2e3;
|
|
286
298
|
const OVERLOAD_MAX_DELAY_MS = 3e4;
|
|
287
299
|
const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
|
|
@@ -336,7 +348,11 @@ async function* agentLoop(messages, options) {
|
|
|
336
348
|
firstTurn = false;
|
|
337
349
|
if (options.transformContext) {
|
|
338
350
|
diag("transform_start");
|
|
339
|
-
const
|
|
351
|
+
const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
|
|
352
|
+
const transformed = await options.transformContext(messages, {
|
|
353
|
+
usage: latestProviderUsage,
|
|
354
|
+
pendingMessages
|
|
355
|
+
});
|
|
340
356
|
if (transformed !== messages) {
|
|
341
357
|
diag("transform_compacted", {
|
|
342
358
|
before: messages.length,
|
|
@@ -344,6 +360,8 @@ async function* agentLoop(messages, options) {
|
|
|
344
360
|
});
|
|
345
361
|
messages.length = 0;
|
|
346
362
|
messages.push(...transformed);
|
|
363
|
+
latestProviderUsage = void 0;
|
|
364
|
+
usageAnchorIndex = void 0;
|
|
347
365
|
}
|
|
348
366
|
diag("transform_end");
|
|
349
367
|
}
|
|
@@ -604,10 +622,17 @@ async function* agentLoop(messages, options) {
|
|
|
604
622
|
...overflowDetails
|
|
605
623
|
});
|
|
606
624
|
try {
|
|
607
|
-
const
|
|
625
|
+
const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
|
|
626
|
+
const compacted = await options.transformContext(messages, {
|
|
627
|
+
force: true,
|
|
628
|
+
usage: latestProviderUsage,
|
|
629
|
+
pendingMessages
|
|
630
|
+
});
|
|
608
631
|
if (compacted !== messages && compacted.length < messages.length) {
|
|
609
632
|
messages.length = 0;
|
|
610
633
|
messages.push(...compacted);
|
|
634
|
+
latestProviderUsage = void 0;
|
|
635
|
+
usageAnchorIndex = void 0;
|
|
611
636
|
diag("overflow_compact_success", {
|
|
612
637
|
attempt: overflowCompactionAttempts,
|
|
613
638
|
messages: messages.length,
|
|
@@ -671,11 +696,33 @@ async function* agentLoop(messages, options) {
|
|
|
671
696
|
provider: options.provider,
|
|
672
697
|
model: options.model
|
|
673
698
|
});
|
|
699
|
+
if (runawayToolcallRetries < MAX_RUNAWAY_TOOLCALL_RETRIES) {
|
|
700
|
+
runawayToolcallRetries++;
|
|
701
|
+
const delayMs = RUNAWAY_TOOLCALL_RETRY_DELAY_MS * runawayToolcallRetries;
|
|
702
|
+
diag("retry", {
|
|
703
|
+
reason: "runaway_toolcall",
|
|
704
|
+
attempt: runawayToolcallRetries,
|
|
705
|
+
maxAttempts: MAX_RUNAWAY_TOOLCALL_RETRIES,
|
|
706
|
+
delayMs,
|
|
707
|
+
...runawayDetected
|
|
708
|
+
});
|
|
709
|
+
yield {
|
|
710
|
+
type: "retry",
|
|
711
|
+
reason: "runaway_toolcall",
|
|
712
|
+
attempt: runawayToolcallRetries,
|
|
713
|
+
maxAttempts: MAX_RUNAWAY_TOOLCALL_RETRIES,
|
|
714
|
+
delayMs,
|
|
715
|
+
silent: true
|
|
716
|
+
};
|
|
717
|
+
await abortableSleep(delayMs, options.signal);
|
|
718
|
+
turn--;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
674
721
|
const detail = runawayDetected.kind === "chars" ? `${(runawayDetected.chars / 1024).toFixed(0)} KB of tool-call arguments` : `${runawayDetected.events} tool-call delta events`;
|
|
675
722
|
yield {
|
|
676
723
|
type: "error",
|
|
677
724
|
error: new Error(
|
|
678
|
-
`The model
|
|
725
|
+
`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.`
|
|
679
726
|
)
|
|
680
727
|
};
|
|
681
728
|
break;
|
|
@@ -776,6 +823,7 @@ async function* agentLoop(messages, options) {
|
|
|
776
823
|
}
|
|
777
824
|
overloadRetries = 0;
|
|
778
825
|
stallRetries = 0;
|
|
826
|
+
runawayToolcallRetries = 0;
|
|
779
827
|
const contentArr = Array.isArray(response.message.content) ? response.message.content : null;
|
|
780
828
|
const hasActionableContent = response.message.content !== "" && contentArr !== null && contentArr.some(
|
|
781
829
|
(p) => p.type === "text" || p.type === "tool_call" || p.type === "server_tool_call"
|
|
@@ -813,6 +861,8 @@ async function* agentLoop(messages, options) {
|
|
|
813
861
|
totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
|
|
814
862
|
}
|
|
815
863
|
messages.push(response.message);
|
|
864
|
+
latestProviderUsage = response.usage;
|
|
865
|
+
usageAnchorIndex = messages.length - 1;
|
|
816
866
|
const completedAt = Date.now();
|
|
817
867
|
const outputTokensPerSecond = providerDurationMs > 0 && response.usage.outputTokens > 0 ? response.usage.outputTokens / (providerDurationMs / 1e3) : void 0;
|
|
818
868
|
const timing = {
|
|
@@ -845,6 +895,27 @@ async function* agentLoop(messages, options) {
|
|
|
845
895
|
consecutivePauses = 0;
|
|
846
896
|
const allToolCalls = extractToolCalls(response.message.content);
|
|
847
897
|
if (response.stopReason !== "tool_use" && allToolCalls.length === 0) {
|
|
898
|
+
if (response.stopReason === "max_tokens") {
|
|
899
|
+
if (maxTokensContinuations < MAX_OUTPUT_CONTINUATIONS) {
|
|
900
|
+
maxTokensContinuations++;
|
|
901
|
+
diag("max_tokens_continuation", {
|
|
902
|
+
attempt: maxTokensContinuations,
|
|
903
|
+
maxAttempts: MAX_OUTPUT_CONTINUATIONS,
|
|
904
|
+
provider: options.provider,
|
|
905
|
+
model: options.model
|
|
906
|
+
});
|
|
907
|
+
yield { type: "truncated", reason: "max_tokens", continued: true };
|
|
908
|
+
messages.push({ role: "user", content: MAX_TOKENS_CONTINUATION_PROMPT });
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
yield { type: "truncated", reason: "max_tokens", continued: false };
|
|
912
|
+
} else if (response.stopReason === "refusal" || response.stopReason === "error") {
|
|
913
|
+
yield {
|
|
914
|
+
type: "truncated",
|
|
915
|
+
reason: response.stopReason === "refusal" ? "refusal" : "provider_error",
|
|
916
|
+
continued: false
|
|
917
|
+
};
|
|
918
|
+
}
|
|
848
919
|
if (options.getSteeringMessages) {
|
|
849
920
|
const steering = await options.getSteeringMessages();
|
|
850
921
|
if (steering && steering.length > 0) {
|
|
@@ -900,6 +971,7 @@ async function* agentLoop(messages, options) {
|
|
|
900
971
|
const executionOptions = {
|
|
901
972
|
signal: options.signal,
|
|
902
973
|
maxToolResultChars: options.maxToolResultChars,
|
|
974
|
+
maxTurnToolResultChars: options.maxTurnToolResultChars,
|
|
903
975
|
toolMap,
|
|
904
976
|
invalidToolArgumentCounts,
|
|
905
977
|
markFatalToolArgumentError
|
|
@@ -1139,6 +1211,7 @@ async function* executeToolCallsMixed(toolCalls, initialToolResults, options) {
|
|
|
1139
1211
|
}
|
|
1140
1212
|
const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
|
|
1141
1213
|
capToolResults(toolResults, options.maxToolResultChars);
|
|
1214
|
+
capTurnToolResults(toolResults, options.maxTurnToolResultChars);
|
|
1142
1215
|
return { toolResults, aborted };
|
|
1143
1216
|
}
|
|
1144
1217
|
async function* executeToolCallsParallel(toolCalls, initialToolResults, options) {
|
|
@@ -1178,6 +1251,7 @@ async function* executeToolCallsParallel(toolCalls, initialToolResults, options)
|
|
|
1178
1251
|
}
|
|
1179
1252
|
const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
|
|
1180
1253
|
capToolResults(toolResults, options.maxToolResultChars);
|
|
1254
|
+
capTurnToolResults(toolResults, options.maxTurnToolResultChars);
|
|
1181
1255
|
return { toolResults, aborted };
|
|
1182
1256
|
}
|
|
1183
1257
|
function buildToolResults(initialToolResults, toolCalls, resultsById) {
|
|
@@ -1208,16 +1282,56 @@ function capToolResults(toolResults, maxToolResultChars) {
|
|
|
1208
1282
|
const max = Math.min(maxToolResultChars, hardMax);
|
|
1209
1283
|
for (const toolResult of toolResults) {
|
|
1210
1284
|
if (typeof toolResult.content !== "string" || toolResult.content.length <= max) continue;
|
|
1285
|
+
const originalChars = toolResult.content.length;
|
|
1211
1286
|
const headChars = Math.floor(max * 0.7);
|
|
1212
1287
|
const tailChars = max - headChars;
|
|
1213
1288
|
const head = toolResult.content.slice(0, headChars);
|
|
1214
1289
|
const tail = toolResult.content.slice(-tailChars);
|
|
1215
|
-
const omitted =
|
|
1290
|
+
const omitted = originalChars - headChars - tailChars;
|
|
1216
1291
|
toolResult.content = head + `
|
|
1217
1292
|
|
|
1218
1293
|
[... ${omitted} characters omitted ...]
|
|
1219
1294
|
|
|
1220
1295
|
` + tail;
|
|
1296
|
+
toolResult.capped = {
|
|
1297
|
+
originalChars,
|
|
1298
|
+
keptChars: toolResult.content.length,
|
|
1299
|
+
scope: "per-result"
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function capTurnToolResults(toolResults, maxTurnToolResultChars) {
|
|
1304
|
+
if (!maxTurnToolResultChars) return;
|
|
1305
|
+
const textResults = toolResults.filter(
|
|
1306
|
+
(toolResult) => typeof toolResult.content === "string"
|
|
1307
|
+
);
|
|
1308
|
+
const total = textResults.reduce((sum, toolResult) => sum + toolResult.content.length, 0);
|
|
1309
|
+
if (total <= maxTurnToolResultChars) return;
|
|
1310
|
+
const bySize = [...textResults].sort((a, b) => a.content.length - b.content.length);
|
|
1311
|
+
let remaining = maxTurnToolResultChars;
|
|
1312
|
+
let left = bySize.length;
|
|
1313
|
+
for (const toolResult of bySize) {
|
|
1314
|
+
const fairShare = Math.floor(remaining / left);
|
|
1315
|
+
left--;
|
|
1316
|
+
if (toolResult.content.length <= fairShare) {
|
|
1317
|
+
remaining -= toolResult.content.length;
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
remaining -= fairShare;
|
|
1321
|
+
const originalChars = toolResult.content.length;
|
|
1322
|
+
const headChars = Math.floor(fairShare * 0.7);
|
|
1323
|
+
const tailChars = fairShare - headChars;
|
|
1324
|
+
const omitted = originalChars - fairShare;
|
|
1325
|
+
toolResult.content = toolResult.content.slice(0, headChars) + `
|
|
1326
|
+
|
|
1327
|
+
[... ${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 ...]
|
|
1328
|
+
|
|
1329
|
+
` + (tailChars > 0 ? toolResult.content.slice(-tailChars) : "");
|
|
1330
|
+
toolResult.capped = {
|
|
1331
|
+
originalChars: toolResult.capped?.originalChars ?? originalChars,
|
|
1332
|
+
keptChars: toolResult.content.length,
|
|
1333
|
+
scope: "per-turn"
|
|
1334
|
+
};
|
|
1221
1335
|
}
|
|
1222
1336
|
}
|
|
1223
1337
|
function normalizeToolResult(raw) {
|