@prestyj/ai 4.6.4 → 4.11.4
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 +248 -68
- 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 +247 -68
- 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 {
|
|
@@ -751,16 +813,17 @@ function toAnthropicThinking(level, maxTokens, model) {
|
|
|
751
813
|
outputConfig: { effort }
|
|
752
814
|
};
|
|
753
815
|
}
|
|
816
|
+
const VISIBLE_FLOOR = 1024;
|
|
754
817
|
const effectiveLevel = level === "xhigh" || level === "max" ? "high" : level;
|
|
755
818
|
const budgetMap = {
|
|
756
|
-
low: Math.max(1024, Math.floor(maxTokens * 0.
|
|
757
|
-
medium: Math.max(2048, Math.floor(maxTokens * 0.
|
|
758
|
-
high: Math.max(4096, maxTokens)
|
|
819
|
+
low: Math.max(1024, Math.floor(maxTokens * 0.2)),
|
|
820
|
+
medium: Math.max(2048, Math.floor(maxTokens * 0.45)),
|
|
821
|
+
high: Math.max(4096, Math.floor(maxTokens * 0.8))
|
|
759
822
|
};
|
|
760
|
-
const budget = budgetMap[effectiveLevel];
|
|
823
|
+
const budget = Math.max(0, Math.min(budgetMap[effectiveLevel], maxTokens - VISIBLE_FLOOR));
|
|
761
824
|
return {
|
|
762
825
|
thinking: { type: "enabled", budget_tokens: budget },
|
|
763
|
-
maxTokens
|
|
826
|
+
maxTokens
|
|
764
827
|
};
|
|
765
828
|
}
|
|
766
829
|
function remapToolCallId(id, idMap) {
|
|
@@ -972,10 +1035,10 @@ function createClient(options) {
|
|
|
972
1035
|
...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
|
|
973
1036
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
974
1037
|
...options.fetch ? { fetch: options.fetch } : {},
|
|
975
|
-
//
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
maxRetries:
|
|
1038
|
+
// Disable SDK retries — the agent loop has its own stall/overload retry
|
|
1039
|
+
// logic that surfaces errors properly. SDK retries on 429s can cause
|
|
1040
|
+
// multi-minute hangs when the provider stops responding mid-retry.
|
|
1041
|
+
maxRetries: 0,
|
|
979
1042
|
...isOAuth ? {
|
|
980
1043
|
defaultHeaders: {
|
|
981
1044
|
// Anthropic's OAuth edge validates the claude-cli version. Callers
|
|
@@ -988,7 +1051,7 @@ function createClient(options) {
|
|
|
988
1051
|
});
|
|
989
1052
|
}
|
|
990
1053
|
function streamAnthropic(options) {
|
|
991
|
-
return new StreamResult(runStream(options));
|
|
1054
|
+
return new StreamResult(runStream(options), options.signal);
|
|
992
1055
|
}
|
|
993
1056
|
async function* runStream(options) {
|
|
994
1057
|
const client = createClient(options);
|
|
@@ -1083,7 +1146,6 @@ async function* runStream(options) {
|
|
|
1083
1146
|
throw toError(err);
|
|
1084
1147
|
}
|
|
1085
1148
|
}
|
|
1086
|
-
const stream2 = client.messages.stream(params, requestOptions);
|
|
1087
1149
|
const contentParts = [];
|
|
1088
1150
|
const blocks = /* @__PURE__ */ new Map();
|
|
1089
1151
|
let inputTokens = 0;
|
|
@@ -1092,8 +1154,14 @@ async function* runStream(options) {
|
|
|
1092
1154
|
let cacheWrite;
|
|
1093
1155
|
let stopReason = null;
|
|
1094
1156
|
const keepalive = { type: "keepalive" };
|
|
1157
|
+
let receivedAnyEvent = false;
|
|
1095
1158
|
try {
|
|
1159
|
+
const stream2 = await client.messages.create(
|
|
1160
|
+
params,
|
|
1161
|
+
requestOptions
|
|
1162
|
+
);
|
|
1096
1163
|
for await (const event of stream2) {
|
|
1164
|
+
receivedAnyEvent = true;
|
|
1097
1165
|
switch (event.type) {
|
|
1098
1166
|
case "message_start": {
|
|
1099
1167
|
const usage = event.message.usage;
|
|
@@ -1130,7 +1198,7 @@ async function* runStream(options) {
|
|
|
1130
1198
|
accum.toolId = block.id;
|
|
1131
1199
|
accum.toolName = block.name;
|
|
1132
1200
|
accum.input = block.input;
|
|
1133
|
-
} else if (block.type
|
|
1201
|
+
} else if (block.type !== "text" && block.type !== "thinking") {
|
|
1134
1202
|
accum.raw = block;
|
|
1135
1203
|
}
|
|
1136
1204
|
blocks.set(idx, accum);
|
|
@@ -1227,8 +1295,7 @@ async function* runStream(options) {
|
|
|
1227
1295
|
contentParts.push({ type: "raw", data: accum.raw });
|
|
1228
1296
|
yield keepalive;
|
|
1229
1297
|
} else {
|
|
1230
|
-
const
|
|
1231
|
-
const rawBlock = msg?.content[event.index];
|
|
1298
|
+
const rawBlock = accum.raw;
|
|
1232
1299
|
if (rawBlock) {
|
|
1233
1300
|
const blockType = rawBlock.type;
|
|
1234
1301
|
if (blockType === "web_search_tool_result") {
|
|
@@ -1274,6 +1341,11 @@ async function* runStream(options) {
|
|
|
1274
1341
|
} catch (err) {
|
|
1275
1342
|
throw toError(err);
|
|
1276
1343
|
}
|
|
1344
|
+
if (!receivedAnyEvent) {
|
|
1345
|
+
throw new ProviderError("anthropic", "Stream ended without producing any events.", {
|
|
1346
|
+
statusCode: 504
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1277
1349
|
const normalizedStop = normalizeAnthropicStopReason(stopReason);
|
|
1278
1350
|
const response = {
|
|
1279
1351
|
message: {
|
|
@@ -1565,7 +1637,7 @@ function createClient2(options) {
|
|
|
1565
1637
|
});
|
|
1566
1638
|
}
|
|
1567
1639
|
function streamOpenAI(options) {
|
|
1568
|
-
return new StreamResult(runStream2(options));
|
|
1640
|
+
return new StreamResult(runStream2(options), options.signal);
|
|
1569
1641
|
}
|
|
1570
1642
|
async function* runStream2(options) {
|
|
1571
1643
|
const providerName = options.provider ?? "openai";
|
|
@@ -1657,51 +1729,62 @@ async function* runStream2(options) {
|
|
|
1657
1729
|
let outputTokens = 0;
|
|
1658
1730
|
let cacheRead = 0;
|
|
1659
1731
|
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 };
|
|
1732
|
+
let receivedAnyChunk = false;
|
|
1733
|
+
try {
|
|
1734
|
+
for await (const chunk of stream2) {
|
|
1735
|
+
receivedAnyChunk = true;
|
|
1736
|
+
const choice = chunk.choices?.[0];
|
|
1737
|
+
if (chunk.usage) {
|
|
1738
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
|
|
1675
1739
|
}
|
|
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);
|
|
1740
|
+
if (!choice) continue;
|
|
1741
|
+
if (choice.finish_reason) {
|
|
1742
|
+
finishReason = choice.finish_reason;
|
|
1743
|
+
}
|
|
1744
|
+
const delta = choice.delta;
|
|
1745
|
+
const reasoningContent = delta.reasoning_content;
|
|
1746
|
+
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1747
|
+
thinkingAccum += reasoningContent;
|
|
1748
|
+
if (options.thinking) {
|
|
1749
|
+
yield { type: "thinking_delta", text: reasoningContent };
|
|
1691
1750
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1751
|
+
}
|
|
1752
|
+
if (delta.content) {
|
|
1753
|
+
textAccum += delta.content;
|
|
1754
|
+
yield { type: "text_delta", text: delta.content };
|
|
1755
|
+
}
|
|
1756
|
+
if (delta.tool_calls) {
|
|
1757
|
+
for (const tc of delta.tool_calls) {
|
|
1758
|
+
let accum = toolCallAccum.get(tc.index);
|
|
1759
|
+
if (!accum) {
|
|
1760
|
+
accum = {
|
|
1761
|
+
id: tc.id ?? "",
|
|
1762
|
+
name: tc.function?.name ?? "",
|
|
1763
|
+
argsJson: ""
|
|
1764
|
+
};
|
|
1765
|
+
toolCallAccum.set(tc.index, accum);
|
|
1766
|
+
}
|
|
1767
|
+
if (tc.id) accum.id = tc.id;
|
|
1768
|
+
if (tc.function?.name) accum.name = tc.function.name;
|
|
1769
|
+
if (tc.function?.arguments) {
|
|
1770
|
+
accum.argsJson += tc.function.arguments;
|
|
1771
|
+
yield {
|
|
1772
|
+
type: "toolcall_delta",
|
|
1773
|
+
id: accum.id,
|
|
1774
|
+
name: accum.name,
|
|
1775
|
+
argsJson: tc.function.arguments
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1702
1778
|
}
|
|
1703
1779
|
}
|
|
1704
1780
|
}
|
|
1781
|
+
} catch (err) {
|
|
1782
|
+
throw toError2(err, providerName);
|
|
1783
|
+
}
|
|
1784
|
+
if (!receivedAnyChunk) {
|
|
1785
|
+
throw new ProviderError(providerName, "Stream ended without producing any chunks.", {
|
|
1786
|
+
statusCode: 504
|
|
1787
|
+
});
|
|
1705
1788
|
}
|
|
1706
1789
|
if (thinkingAccum) {
|
|
1707
1790
|
contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
@@ -1944,7 +2027,7 @@ function isVisibleOutputItem(itemType) {
|
|
|
1944
2027
|
return itemType === "message";
|
|
1945
2028
|
}
|
|
1946
2029
|
function streamOpenAICodex(options) {
|
|
1947
|
-
return new StreamResult(runStream3(options));
|
|
2030
|
+
return new StreamResult(runStream3(options), options.signal);
|
|
1948
2031
|
}
|
|
1949
2032
|
async function* runStream3(options) {
|
|
1950
2033
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
@@ -2274,10 +2357,16 @@ async function* parseSSE(body) {
|
|
|
2274
2357
|
}
|
|
2275
2358
|
}
|
|
2276
2359
|
function remapCodexId(id, idMap) {
|
|
2277
|
-
if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
|
|
2278
2360
|
const existing = idMap.get(id);
|
|
2279
2361
|
if (existing) return existing;
|
|
2280
|
-
const
|
|
2362
|
+
const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
|
|
2363
|
+
const sanitized = withPrefix.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
2364
|
+
let mapped = sanitized;
|
|
2365
|
+
let suffix = 2;
|
|
2366
|
+
const used = new Set(idMap.values());
|
|
2367
|
+
while (used.has(mapped)) {
|
|
2368
|
+
mapped = `${sanitized}_${suffix++}`;
|
|
2369
|
+
}
|
|
2281
2370
|
idMap.set(id, mapped);
|
|
2282
2371
|
return mapped;
|
|
2283
2372
|
}
|
|
@@ -2800,7 +2889,7 @@ async function fetchCodeAssistWithRetry(plan, options) {
|
|
|
2800
2889
|
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2801
2890
|
}
|
|
2802
2891
|
function streamGemini(options) {
|
|
2803
|
-
return new StreamResult(runStream4(options));
|
|
2892
|
+
return new StreamResult(runStream4(options), options.signal);
|
|
2804
2893
|
}
|
|
2805
2894
|
async function* runStream4(options) {
|
|
2806
2895
|
const useStreaming = options.streaming !== false;
|
|
@@ -3020,6 +3109,96 @@ function messagesContainVideo(messages) {
|
|
|
3020
3109
|
return false;
|
|
3021
3110
|
}
|
|
3022
3111
|
|
|
3112
|
+
// src/error-classification.ts
|
|
3113
|
+
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
3114
|
+
/context_length_exceeded/i,
|
|
3115
|
+
/context length exceeded/i,
|
|
3116
|
+
/context window/i,
|
|
3117
|
+
// OpenAI Codex / Responses
|
|
3118
|
+
/maximum context length/i,
|
|
3119
|
+
// OpenAI / OpenRouter / Mistral
|
|
3120
|
+
/prompt is too long/i,
|
|
3121
|
+
// Anthropic
|
|
3122
|
+
/request_too_large/i,
|
|
3123
|
+
// Anthropic HTTP 413
|
|
3124
|
+
/input is too long/i,
|
|
3125
|
+
// Bedrock
|
|
3126
|
+
/input token count.*exceeds the maximum/i,
|
|
3127
|
+
// Gemini
|
|
3128
|
+
/maximum prompt length/i,
|
|
3129
|
+
// xAI / Grok
|
|
3130
|
+
/reduce the length of the messages/i,
|
|
3131
|
+
// Groq
|
|
3132
|
+
/too large for model/i,
|
|
3133
|
+
// Mistral
|
|
3134
|
+
/token limit/i
|
|
3135
|
+
// generic
|
|
3136
|
+
];
|
|
3137
|
+
var RATE_LIMIT_PATTERNS = [
|
|
3138
|
+
/rate[ _-]?limit/i,
|
|
3139
|
+
/\b429\b/,
|
|
3140
|
+
/too many requests/i,
|
|
3141
|
+
/tokens per minute/i,
|
|
3142
|
+
/requests per minute/i
|
|
3143
|
+
];
|
|
3144
|
+
var PROVIDER_TRANSIENT_PATTERNS = [
|
|
3145
|
+
/\b5\d\d\b/,
|
|
3146
|
+
/api_error/i,
|
|
3147
|
+
/server_error/i,
|
|
3148
|
+
/internal server error/i,
|
|
3149
|
+
/bad gateway/i,
|
|
3150
|
+
/service unavailable/i,
|
|
3151
|
+
/gateway timeout/i,
|
|
3152
|
+
/overloaded/i,
|
|
3153
|
+
/\b529\b/
|
|
3154
|
+
];
|
|
3155
|
+
var BILLING_PATTERNS = [
|
|
3156
|
+
/payment required/i,
|
|
3157
|
+
/\b402\b/,
|
|
3158
|
+
/quota_exceeded/i,
|
|
3159
|
+
// underscore variant not in isHardBillingMessage
|
|
3160
|
+
/credit balance/i
|
|
3161
|
+
];
|
|
3162
|
+
var AUTH_PATTERNS = [
|
|
3163
|
+
/invalid[ _]api[ _]key/i,
|
|
3164
|
+
/unauthorized/i,
|
|
3165
|
+
/\b401\b/,
|
|
3166
|
+
/authentication[ _]failed/i,
|
|
3167
|
+
/please run \/login/i
|
|
3168
|
+
// Anthropic Claude Code-style hint
|
|
3169
|
+
];
|
|
3170
|
+
function matchesAny(message, patterns) {
|
|
3171
|
+
return patterns.some((p) => p.test(message));
|
|
3172
|
+
}
|
|
3173
|
+
function classifyProviderError(message) {
|
|
3174
|
+
if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
3175
|
+
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.
|
|
3176
|
+
|
|
3177
|
+
Original: ${message}`;
|
|
3178
|
+
}
|
|
3179
|
+
if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
|
|
3180
|
+
return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
|
|
3181
|
+
|
|
3182
|
+
Original: ${message}`;
|
|
3183
|
+
}
|
|
3184
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
3185
|
+
return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
|
|
3186
|
+
|
|
3187
|
+
Original: ${message}`;
|
|
3188
|
+
}
|
|
3189
|
+
if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
|
|
3190
|
+
return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
|
|
3191
|
+
|
|
3192
|
+
Original: ${message}`;
|
|
3193
|
+
}
|
|
3194
|
+
if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
|
|
3195
|
+
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.
|
|
3196
|
+
|
|
3197
|
+
Original: ${message}`;
|
|
3198
|
+
}
|
|
3199
|
+
return message;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3023
3202
|
// src/providers/palsu.ts
|
|
3024
3203
|
function palsuText(text) {
|
|
3025
3204
|
return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
|
|
@@ -3166,7 +3345,7 @@ function registerPalsuProvider(config) {
|
|
|
3166
3345
|
const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
|
|
3167
3346
|
return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
|
|
3168
3347
|
})();
|
|
3169
|
-
return new StreamResult(gen);
|
|
3348
|
+
return new StreamResult(gen, options.signal);
|
|
3170
3349
|
}
|
|
3171
3350
|
});
|
|
3172
3351
|
return handle;
|
|
@@ -3177,6 +3356,7 @@ function registerPalsuProvider(config) {
|
|
|
3177
3356
|
EventStream,
|
|
3178
3357
|
ProviderError,
|
|
3179
3358
|
StreamResult,
|
|
3359
|
+
classifyProviderError,
|
|
3180
3360
|
formatError,
|
|
3181
3361
|
formatErrorForDisplay,
|
|
3182
3362
|
isHardBillingMessage,
|