@prestyj/ai 4.6.3 → 4.10.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.
- package/README.md +1 -1
- package/dist/index.cjs +242 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +241 -63
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ Tool parameters are Zod schemas. Converted to JSON Schema at the provider bounda
|
|
|
43
43
|
| `anthropic` | Claude Fable 5, Opus 4.8, Sonnet 4.6, Haiku 4.5 | Extended thinking, prompt caching, server-side compaction |
|
|
44
44
|
| `openai` | GPT-4.1, o3, o4-mini | Supports OAuth (codex endpoint) and API keys |
|
|
45
45
|
| `glm` | GLM-5.1, GLM-4.7 | Z.AI platform, OpenAI-compatible |
|
|
46
|
-
| `moonshot` | Kimi K2.
|
|
46
|
+
| `moonshot` | Kimi K2.7 | Moonshot platform, OpenAI-compatible |
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
EventStream: () => EventStream,
|
|
35
35
|
ProviderError: () => ProviderError,
|
|
36
36
|
StreamResult: () => StreamResult,
|
|
37
|
+
classifyProviderError: () => classifyProviderError,
|
|
37
38
|
formatError: () => formatError,
|
|
38
39
|
formatErrorForDisplay: () => formatErrorForDisplay,
|
|
39
40
|
isHardBillingMessage: () => isHardBillingMessage,
|
|
@@ -137,10 +138,25 @@ function formatResetTime(resetsAt) {
|
|
|
137
138
|
minute: "2-digit"
|
|
138
139
|
});
|
|
139
140
|
}
|
|
141
|
+
function isMythosAccessError(message) {
|
|
142
|
+
const lower = message.toLowerCase();
|
|
143
|
+
return lower.includes("mythos") && (lower.includes("not_found") || lower.includes("not found") || lower.includes("no access"));
|
|
144
|
+
}
|
|
140
145
|
function formatError(err) {
|
|
141
146
|
if (err instanceof ProviderError) {
|
|
142
147
|
const name = providerDisplayName(err.provider);
|
|
143
148
|
const cleanMessage = cleanProviderMessage(err.message);
|
|
149
|
+
if (isMythosAccessError(cleanMessage)) {
|
|
150
|
+
return {
|
|
151
|
+
headline: "Claude Mythos 5 is invitation-only.",
|
|
152
|
+
source: "provider",
|
|
153
|
+
message: "Your Anthropic account isn't approved for Project Glasswing, so the API reports the model as not found.",
|
|
154
|
+
provider: err.provider,
|
|
155
|
+
statusCode: err.statusCode,
|
|
156
|
+
...err.requestId ? { requestId: err.requestId } : {},
|
|
157
|
+
guidance: "Request access via your Anthropic account team (see platform.claude.com/docs/en/about-claude/models/overview), or switch to claude-fable-5 with /model \u2014 same underlying model, generally available."
|
|
158
|
+
};
|
|
159
|
+
}
|
|
144
160
|
if (isUsageLimitError(err)) {
|
|
145
161
|
const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
|
|
146
162
|
return {
|
|
@@ -327,21 +343,21 @@ var StreamResult = class {
|
|
|
327
343
|
resolveResponse;
|
|
328
344
|
rejectResponse;
|
|
329
345
|
resolveWait = null;
|
|
330
|
-
constructor(generator) {
|
|
346
|
+
constructor(generator, signal) {
|
|
331
347
|
this.response = new Promise((resolve, reject) => {
|
|
332
348
|
this.resolveResponse = resolve;
|
|
333
349
|
this.rejectResponse = reject;
|
|
334
350
|
});
|
|
335
|
-
this.pump(generator);
|
|
351
|
+
this.pump(generator, signal);
|
|
336
352
|
}
|
|
337
|
-
async pump(generator) {
|
|
353
|
+
async pump(generator, signal) {
|
|
338
354
|
try {
|
|
339
|
-
let next = await
|
|
355
|
+
let next = await this._nextWithAbort(generator, signal);
|
|
340
356
|
while (!next.done) {
|
|
341
357
|
this.buffer.push(next.value);
|
|
342
358
|
this.resolveWait?.();
|
|
343
359
|
this.resolveWait = null;
|
|
344
|
-
next = await
|
|
360
|
+
next = await this._nextWithAbort(generator, signal);
|
|
345
361
|
}
|
|
346
362
|
this.done = true;
|
|
347
363
|
this.resolveResponse(next.value);
|
|
@@ -356,6 +372,28 @@ var StreamResult = class {
|
|
|
356
372
|
this.resolveWait = null;
|
|
357
373
|
}
|
|
358
374
|
}
|
|
375
|
+
async _nextWithAbort(generator, signal) {
|
|
376
|
+
if (!signal) {
|
|
377
|
+
return generator.next();
|
|
378
|
+
}
|
|
379
|
+
if (signal.aborted) {
|
|
380
|
+
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
381
|
+
}
|
|
382
|
+
let onAbort;
|
|
383
|
+
const abortPromise = new Promise((_, reject) => {
|
|
384
|
+
onAbort = () => {
|
|
385
|
+
generator.return?.(void 0).catch(() => {
|
|
386
|
+
});
|
|
387
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
388
|
+
};
|
|
389
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
390
|
+
});
|
|
391
|
+
try {
|
|
392
|
+
return await Promise.race([generator.next(), abortPromise]);
|
|
393
|
+
} finally {
|
|
394
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
359
397
|
async *[Symbol.asyncIterator]() {
|
|
360
398
|
let index = 0;
|
|
361
399
|
while (true) {
|
|
@@ -452,6 +490,29 @@ function isRawThinking(part) {
|
|
|
452
490
|
const t = part.data.type;
|
|
453
491
|
return t === "thinking" || t === "redacted_thinking";
|
|
454
492
|
}
|
|
493
|
+
var ANTHROPIC_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
494
|
+
"bash_code_execution_tool_result",
|
|
495
|
+
"code_execution_tool_result",
|
|
496
|
+
"connector_text",
|
|
497
|
+
"container_upload",
|
|
498
|
+
"document",
|
|
499
|
+
"image",
|
|
500
|
+
"mid_conv_system",
|
|
501
|
+
"redacted_thinking",
|
|
502
|
+
"search_result",
|
|
503
|
+
"server_tool_use",
|
|
504
|
+
"text",
|
|
505
|
+
"text_editor_code_execution_tool_result",
|
|
506
|
+
"thinking",
|
|
507
|
+
"tool_result",
|
|
508
|
+
"tool_search_tool_result",
|
|
509
|
+
"tool_use",
|
|
510
|
+
"web_fetch_tool_result",
|
|
511
|
+
"web_search_tool_result"
|
|
512
|
+
]);
|
|
513
|
+
function isAnthropicCompatibleRaw(part) {
|
|
514
|
+
return ANTHROPIC_INPUT_BLOCK_TYPES.has(part.data.type);
|
|
515
|
+
}
|
|
455
516
|
function isPositionSensitiveThinking(part) {
|
|
456
517
|
if (part.type === "thinking") return hasValidThinkingSignature(part);
|
|
457
518
|
return isRawThinking(part);
|
|
@@ -478,7 +539,8 @@ function toAnthropicAssistantPart(part, idMap) {
|
|
|
478
539
|
};
|
|
479
540
|
if (part.type === "server_tool_result")
|
|
480
541
|
return part.data;
|
|
481
|
-
if (part.type === "raw")
|
|
542
|
+
if (part.type === "raw")
|
|
543
|
+
return isAnthropicCompatibleRaw(part) ? part.data : null;
|
|
482
544
|
return null;
|
|
483
545
|
}
|
|
484
546
|
function toAnthropicAssistantContent(content, preserveThinking, idMap) {
|
|
@@ -737,12 +799,12 @@ function toAnthropicToolChoice(choice) {
|
|
|
737
799
|
return { type: "tool", name: choice.name };
|
|
738
800
|
}
|
|
739
801
|
function isAdaptiveThinkingModel(model) {
|
|
740
|
-
return /
|
|
802
|
+
return /opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-4[-.]6|fable-5|mythos-5/.test(model);
|
|
741
803
|
}
|
|
742
804
|
function toAnthropicThinking(level, maxTokens, model) {
|
|
743
805
|
if (isAdaptiveThinkingModel(model)) {
|
|
744
806
|
let effort = level;
|
|
745
|
-
if (effort === "xhigh" && !/opus-4-8|opus-4-7
|
|
807
|
+
if (effort === "xhigh" && !/opus-4-8|opus-4-7/.test(model)) {
|
|
746
808
|
effort = "high";
|
|
747
809
|
}
|
|
748
810
|
return {
|
|
@@ -972,10 +1034,10 @@ function createClient(options) {
|
|
|
972
1034
|
...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
|
|
973
1035
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
974
1036
|
...options.fetch ? { fetch: options.fetch } : {},
|
|
975
|
-
//
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
maxRetries:
|
|
1037
|
+
// Disable SDK retries — the agent loop has its own stall/overload retry
|
|
1038
|
+
// logic that surfaces errors properly. SDK retries on 429s can cause
|
|
1039
|
+
// multi-minute hangs when the provider stops responding mid-retry.
|
|
1040
|
+
maxRetries: 0,
|
|
979
1041
|
...isOAuth ? {
|
|
980
1042
|
defaultHeaders: {
|
|
981
1043
|
// Anthropic's OAuth edge validates the claude-cli version. Callers
|
|
@@ -988,7 +1050,7 @@ function createClient(options) {
|
|
|
988
1050
|
});
|
|
989
1051
|
}
|
|
990
1052
|
function streamAnthropic(options) {
|
|
991
|
-
return new StreamResult(runStream(options));
|
|
1053
|
+
return new StreamResult(runStream(options), options.signal);
|
|
992
1054
|
}
|
|
993
1055
|
async function* runStream(options) {
|
|
994
1056
|
const client = createClient(options);
|
|
@@ -1083,7 +1145,6 @@ async function* runStream(options) {
|
|
|
1083
1145
|
throw toError(err);
|
|
1084
1146
|
}
|
|
1085
1147
|
}
|
|
1086
|
-
const stream2 = client.messages.stream(params, requestOptions);
|
|
1087
1148
|
const contentParts = [];
|
|
1088
1149
|
const blocks = /* @__PURE__ */ new Map();
|
|
1089
1150
|
let inputTokens = 0;
|
|
@@ -1092,8 +1153,14 @@ async function* runStream(options) {
|
|
|
1092
1153
|
let cacheWrite;
|
|
1093
1154
|
let stopReason = null;
|
|
1094
1155
|
const keepalive = { type: "keepalive" };
|
|
1156
|
+
let receivedAnyEvent = false;
|
|
1095
1157
|
try {
|
|
1158
|
+
const stream2 = await client.messages.create(
|
|
1159
|
+
params,
|
|
1160
|
+
requestOptions
|
|
1161
|
+
);
|
|
1096
1162
|
for await (const event of stream2) {
|
|
1163
|
+
receivedAnyEvent = true;
|
|
1097
1164
|
switch (event.type) {
|
|
1098
1165
|
case "message_start": {
|
|
1099
1166
|
const usage = event.message.usage;
|
|
@@ -1130,7 +1197,7 @@ async function* runStream(options) {
|
|
|
1130
1197
|
accum.toolId = block.id;
|
|
1131
1198
|
accum.toolName = block.name;
|
|
1132
1199
|
accum.input = block.input;
|
|
1133
|
-
} else if (block.type
|
|
1200
|
+
} else if (block.type !== "text" && block.type !== "thinking") {
|
|
1134
1201
|
accum.raw = block;
|
|
1135
1202
|
}
|
|
1136
1203
|
blocks.set(idx, accum);
|
|
@@ -1227,8 +1294,7 @@ async function* runStream(options) {
|
|
|
1227
1294
|
contentParts.push({ type: "raw", data: accum.raw });
|
|
1228
1295
|
yield keepalive;
|
|
1229
1296
|
} else {
|
|
1230
|
-
const
|
|
1231
|
-
const rawBlock = msg?.content[event.index];
|
|
1297
|
+
const rawBlock = accum.raw;
|
|
1232
1298
|
if (rawBlock) {
|
|
1233
1299
|
const blockType = rawBlock.type;
|
|
1234
1300
|
if (blockType === "web_search_tool_result") {
|
|
@@ -1274,6 +1340,11 @@ async function* runStream(options) {
|
|
|
1274
1340
|
} catch (err) {
|
|
1275
1341
|
throw toError(err);
|
|
1276
1342
|
}
|
|
1343
|
+
if (!receivedAnyEvent) {
|
|
1344
|
+
throw new ProviderError("anthropic", "Stream ended without producing any events.", {
|
|
1345
|
+
statusCode: 504
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1277
1348
|
const normalizedStop = normalizeAnthropicStopReason(stopReason);
|
|
1278
1349
|
const response = {
|
|
1279
1350
|
message: {
|
|
@@ -1565,7 +1636,7 @@ function createClient2(options) {
|
|
|
1565
1636
|
});
|
|
1566
1637
|
}
|
|
1567
1638
|
function streamOpenAI(options) {
|
|
1568
|
-
return new StreamResult(runStream2(options));
|
|
1639
|
+
return new StreamResult(runStream2(options), options.signal);
|
|
1569
1640
|
}
|
|
1570
1641
|
async function* runStream2(options) {
|
|
1571
1642
|
const providerName = options.provider ?? "openai";
|
|
@@ -1657,51 +1728,62 @@ async function* runStream2(options) {
|
|
|
1657
1728
|
let outputTokens = 0;
|
|
1658
1729
|
let cacheRead = 0;
|
|
1659
1730
|
let finishReason = null;
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
finishReason = choice.finish_reason;
|
|
1668
|
-
}
|
|
1669
|
-
const delta = choice.delta;
|
|
1670
|
-
const reasoningContent = delta.reasoning_content;
|
|
1671
|
-
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1672
|
-
thinkingAccum += reasoningContent;
|
|
1673
|
-
if (options.thinking) {
|
|
1674
|
-
yield { type: "thinking_delta", text: reasoningContent };
|
|
1731
|
+
let receivedAnyChunk = false;
|
|
1732
|
+
try {
|
|
1733
|
+
for await (const chunk of stream2) {
|
|
1734
|
+
receivedAnyChunk = true;
|
|
1735
|
+
const choice = chunk.choices?.[0];
|
|
1736
|
+
if (chunk.usage) {
|
|
1737
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
|
|
1675
1738
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
if (
|
|
1685
|
-
|
|
1686
|
-
id: tc.id ?? "",
|
|
1687
|
-
name: tc.function?.name ?? "",
|
|
1688
|
-
argsJson: ""
|
|
1689
|
-
};
|
|
1690
|
-
toolCallAccum.set(tc.index, accum);
|
|
1739
|
+
if (!choice) continue;
|
|
1740
|
+
if (choice.finish_reason) {
|
|
1741
|
+
finishReason = choice.finish_reason;
|
|
1742
|
+
}
|
|
1743
|
+
const delta = choice.delta;
|
|
1744
|
+
const reasoningContent = delta.reasoning_content;
|
|
1745
|
+
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1746
|
+
thinkingAccum += reasoningContent;
|
|
1747
|
+
if (options.thinking) {
|
|
1748
|
+
yield { type: "thinking_delta", text: reasoningContent };
|
|
1691
1749
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1750
|
+
}
|
|
1751
|
+
if (delta.content) {
|
|
1752
|
+
textAccum += delta.content;
|
|
1753
|
+
yield { type: "text_delta", text: delta.content };
|
|
1754
|
+
}
|
|
1755
|
+
if (delta.tool_calls) {
|
|
1756
|
+
for (const tc of delta.tool_calls) {
|
|
1757
|
+
let accum = toolCallAccum.get(tc.index);
|
|
1758
|
+
if (!accum) {
|
|
1759
|
+
accum = {
|
|
1760
|
+
id: tc.id ?? "",
|
|
1761
|
+
name: tc.function?.name ?? "",
|
|
1762
|
+
argsJson: ""
|
|
1763
|
+
};
|
|
1764
|
+
toolCallAccum.set(tc.index, accum);
|
|
1765
|
+
}
|
|
1766
|
+
if (tc.id) accum.id = tc.id;
|
|
1767
|
+
if (tc.function?.name) accum.name = tc.function.name;
|
|
1768
|
+
if (tc.function?.arguments) {
|
|
1769
|
+
accum.argsJson += tc.function.arguments;
|
|
1770
|
+
yield {
|
|
1771
|
+
type: "toolcall_delta",
|
|
1772
|
+
id: accum.id,
|
|
1773
|
+
name: accum.name,
|
|
1774
|
+
argsJson: tc.function.arguments
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1702
1777
|
}
|
|
1703
1778
|
}
|
|
1704
1779
|
}
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
throw toError2(err, providerName);
|
|
1782
|
+
}
|
|
1783
|
+
if (!receivedAnyChunk) {
|
|
1784
|
+
throw new ProviderError(providerName, "Stream ended without producing any chunks.", {
|
|
1785
|
+
statusCode: 504
|
|
1786
|
+
});
|
|
1705
1787
|
}
|
|
1706
1788
|
if (thinkingAccum) {
|
|
1707
1789
|
contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
@@ -1944,7 +2026,7 @@ function isVisibleOutputItem(itemType) {
|
|
|
1944
2026
|
return itemType === "message";
|
|
1945
2027
|
}
|
|
1946
2028
|
function streamOpenAICodex(options) {
|
|
1947
|
-
return new StreamResult(runStream3(options));
|
|
2029
|
+
return new StreamResult(runStream3(options), options.signal);
|
|
1948
2030
|
}
|
|
1949
2031
|
async function* runStream3(options) {
|
|
1950
2032
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
@@ -2274,10 +2356,16 @@ async function* parseSSE(body) {
|
|
|
2274
2356
|
}
|
|
2275
2357
|
}
|
|
2276
2358
|
function remapCodexId(id, idMap) {
|
|
2277
|
-
if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
|
|
2278
2359
|
const existing = idMap.get(id);
|
|
2279
2360
|
if (existing) return existing;
|
|
2280
|
-
const
|
|
2361
|
+
const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
|
|
2362
|
+
const sanitized = withPrefix.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
2363
|
+
let mapped = sanitized;
|
|
2364
|
+
let suffix = 2;
|
|
2365
|
+
const used = new Set(idMap.values());
|
|
2366
|
+
while (used.has(mapped)) {
|
|
2367
|
+
mapped = `${sanitized}_${suffix++}`;
|
|
2368
|
+
}
|
|
2281
2369
|
idMap.set(id, mapped);
|
|
2282
2370
|
return mapped;
|
|
2283
2371
|
}
|
|
@@ -2800,7 +2888,7 @@ async function fetchCodeAssistWithRetry(plan, options) {
|
|
|
2800
2888
|
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2801
2889
|
}
|
|
2802
2890
|
function streamGemini(options) {
|
|
2803
|
-
return new StreamResult(runStream4(options));
|
|
2891
|
+
return new StreamResult(runStream4(options), options.signal);
|
|
2804
2892
|
}
|
|
2805
2893
|
async function* runStream4(options) {
|
|
2806
2894
|
const useStreaming = options.streaming !== false;
|
|
@@ -3020,6 +3108,96 @@ function messagesContainVideo(messages) {
|
|
|
3020
3108
|
return false;
|
|
3021
3109
|
}
|
|
3022
3110
|
|
|
3111
|
+
// src/error-classification.ts
|
|
3112
|
+
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
3113
|
+
/context_length_exceeded/i,
|
|
3114
|
+
/context length exceeded/i,
|
|
3115
|
+
/context window/i,
|
|
3116
|
+
// OpenAI Codex / Responses
|
|
3117
|
+
/maximum context length/i,
|
|
3118
|
+
// OpenAI / OpenRouter / Mistral
|
|
3119
|
+
/prompt is too long/i,
|
|
3120
|
+
// Anthropic
|
|
3121
|
+
/request_too_large/i,
|
|
3122
|
+
// Anthropic HTTP 413
|
|
3123
|
+
/input is too long/i,
|
|
3124
|
+
// Bedrock
|
|
3125
|
+
/input token count.*exceeds the maximum/i,
|
|
3126
|
+
// Gemini
|
|
3127
|
+
/maximum prompt length/i,
|
|
3128
|
+
// xAI / Grok
|
|
3129
|
+
/reduce the length of the messages/i,
|
|
3130
|
+
// Groq
|
|
3131
|
+
/too large for model/i,
|
|
3132
|
+
// Mistral
|
|
3133
|
+
/token limit/i
|
|
3134
|
+
// generic
|
|
3135
|
+
];
|
|
3136
|
+
var RATE_LIMIT_PATTERNS = [
|
|
3137
|
+
/rate[ _-]?limit/i,
|
|
3138
|
+
/\b429\b/,
|
|
3139
|
+
/too many requests/i,
|
|
3140
|
+
/tokens per minute/i,
|
|
3141
|
+
/requests per minute/i
|
|
3142
|
+
];
|
|
3143
|
+
var PROVIDER_TRANSIENT_PATTERNS = [
|
|
3144
|
+
/\b5\d\d\b/,
|
|
3145
|
+
/api_error/i,
|
|
3146
|
+
/server_error/i,
|
|
3147
|
+
/internal server error/i,
|
|
3148
|
+
/bad gateway/i,
|
|
3149
|
+
/service unavailable/i,
|
|
3150
|
+
/gateway timeout/i,
|
|
3151
|
+
/overloaded/i,
|
|
3152
|
+
/\b529\b/
|
|
3153
|
+
];
|
|
3154
|
+
var BILLING_PATTERNS = [
|
|
3155
|
+
/payment required/i,
|
|
3156
|
+
/\b402\b/,
|
|
3157
|
+
/quota_exceeded/i,
|
|
3158
|
+
// underscore variant not in isHardBillingMessage
|
|
3159
|
+
/credit balance/i
|
|
3160
|
+
];
|
|
3161
|
+
var AUTH_PATTERNS = [
|
|
3162
|
+
/invalid[ _]api[ _]key/i,
|
|
3163
|
+
/unauthorized/i,
|
|
3164
|
+
/\b401\b/,
|
|
3165
|
+
/authentication[ _]failed/i,
|
|
3166
|
+
/please run \/login/i
|
|
3167
|
+
// Anthropic Claude Code-style hint
|
|
3168
|
+
];
|
|
3169
|
+
function matchesAny(message, patterns) {
|
|
3170
|
+
return patterns.some((p) => p.test(message));
|
|
3171
|
+
}
|
|
3172
|
+
function classifyProviderError(message) {
|
|
3173
|
+
if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
3174
|
+
return `[context_overflow] Worker context window exceeded \u2014 the conversation is too large to continue. Recovery: call reset_worker(project) to wipe history, then re-prompt with the task. Re-prompting WITHOUT reset will fail the same way.
|
|
3175
|
+
|
|
3176
|
+
Original: ${message}`;
|
|
3177
|
+
}
|
|
3178
|
+
if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
|
|
3179
|
+
return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
|
|
3180
|
+
|
|
3181
|
+
Original: ${message}`;
|
|
3182
|
+
}
|
|
3183
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
3184
|
+
return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
|
|
3185
|
+
|
|
3186
|
+
Original: ${message}`;
|
|
3187
|
+
}
|
|
3188
|
+
if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
|
|
3189
|
+
return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
|
|
3190
|
+
|
|
3191
|
+
Original: ${message}`;
|
|
3192
|
+
}
|
|
3193
|
+
if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
|
|
3194
|
+
return `[provider_transient] Provider server-side/transient error. Recovery: wait briefly, then re-prompt the same worker (no reset needed). If it keeps happening, switch models/providers or check provider status.
|
|
3195
|
+
|
|
3196
|
+
Original: ${message}`;
|
|
3197
|
+
}
|
|
3198
|
+
return message;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3023
3201
|
// src/providers/palsu.ts
|
|
3024
3202
|
function palsuText(text) {
|
|
3025
3203
|
return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
|
|
@@ -3166,7 +3344,7 @@ function registerPalsuProvider(config) {
|
|
|
3166
3344
|
const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
|
|
3167
3345
|
return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
|
|
3168
3346
|
})();
|
|
3169
|
-
return new StreamResult(gen);
|
|
3347
|
+
return new StreamResult(gen, options.signal);
|
|
3170
3348
|
}
|
|
3171
3349
|
});
|
|
3172
3350
|
return handle;
|
|
@@ -3177,6 +3355,7 @@ function registerPalsuProvider(config) {
|
|
|
3177
3355
|
EventStream,
|
|
3178
3356
|
ProviderError,
|
|
3179
3357
|
StreamResult,
|
|
3358
|
+
classifyProviderError,
|
|
3180
3359
|
formatError,
|
|
3181
3360
|
formatErrorForDisplay,
|
|
3182
3361
|
isHardBillingMessage,
|