@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/dist/index.d.cts
CHANGED
|
@@ -255,8 +255,9 @@ declare class StreamResult implements AsyncIterable<StreamEvent> {
|
|
|
255
255
|
private resolveResponse;
|
|
256
256
|
private rejectResponse;
|
|
257
257
|
private resolveWait;
|
|
258
|
-
constructor(generator: AsyncGenerator<StreamEvent, StreamResponse
|
|
258
|
+
constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>, signal?: AbortSignal);
|
|
259
259
|
private pump;
|
|
260
|
+
private _nextWithAbort;
|
|
260
261
|
[Symbol.asyncIterator](): AsyncIterator<StreamEvent>;
|
|
261
262
|
then<TResult1 = StreamResponse, TResult2 = never>(onfulfilled?: ((value: StreamResponse) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
262
263
|
}
|
|
@@ -414,6 +415,19 @@ declare function formatError(err: unknown): FormattedError;
|
|
|
414
415
|
*/
|
|
415
416
|
declare function formatErrorForDisplay(err: unknown): string;
|
|
416
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Inspect a raw provider error message and tag it with a clearer, actionable
|
|
420
|
+
* prefix so a worker orchestrator can route on intent instead of regexing JSON.
|
|
421
|
+
* Preserves the original message verbatim after the prefix — helpful for
|
|
422
|
+
* debugging.
|
|
423
|
+
*
|
|
424
|
+
* Order matters: context-overflow is checked first because some providers wrap
|
|
425
|
+
* overflow errors in HTTP 429 envelopes; we want the structural meaning, not
|
|
426
|
+
* the transport status. Billing comes before auth/rate-limit because "402
|
|
427
|
+
* Payment Required" must not be mis-routed as a rate-limit retry.
|
|
428
|
+
*/
|
|
429
|
+
declare function classifyProviderError(message: string): string;
|
|
430
|
+
|
|
417
431
|
/**
|
|
418
432
|
* Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
|
|
419
433
|
* setStreamDiagnostic — the host app wires a callback (typically writing to
|
|
@@ -506,4 +520,4 @@ interface PalsuProviderConfig {
|
|
|
506
520
|
*/
|
|
507
521
|
declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
|
|
508
522
|
|
|
509
|
-
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
|
|
523
|
+
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
|
package/dist/index.d.ts
CHANGED
|
@@ -255,8 +255,9 @@ declare class StreamResult implements AsyncIterable<StreamEvent> {
|
|
|
255
255
|
private resolveResponse;
|
|
256
256
|
private rejectResponse;
|
|
257
257
|
private resolveWait;
|
|
258
|
-
constructor(generator: AsyncGenerator<StreamEvent, StreamResponse
|
|
258
|
+
constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>, signal?: AbortSignal);
|
|
259
259
|
private pump;
|
|
260
|
+
private _nextWithAbort;
|
|
260
261
|
[Symbol.asyncIterator](): AsyncIterator<StreamEvent>;
|
|
261
262
|
then<TResult1 = StreamResponse, TResult2 = never>(onfulfilled?: ((value: StreamResponse) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
262
263
|
}
|
|
@@ -414,6 +415,19 @@ declare function formatError(err: unknown): FormattedError;
|
|
|
414
415
|
*/
|
|
415
416
|
declare function formatErrorForDisplay(err: unknown): string;
|
|
416
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Inspect a raw provider error message and tag it with a clearer, actionable
|
|
420
|
+
* prefix so a worker orchestrator can route on intent instead of regexing JSON.
|
|
421
|
+
* Preserves the original message verbatim after the prefix — helpful for
|
|
422
|
+
* debugging.
|
|
423
|
+
*
|
|
424
|
+
* Order matters: context-overflow is checked first because some providers wrap
|
|
425
|
+
* overflow errors in HTTP 429 envelopes; we want the structural meaning, not
|
|
426
|
+
* the transport status. Billing comes before auth/rate-limit because "402
|
|
427
|
+
* Payment Required" must not be mis-routed as a rate-limit retry.
|
|
428
|
+
*/
|
|
429
|
+
declare function classifyProviderError(message: string): string;
|
|
430
|
+
|
|
417
431
|
/**
|
|
418
432
|
* Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
|
|
419
433
|
* setStreamDiagnostic — the host app wires a callback (typically writing to
|
|
@@ -506,4 +520,4 @@ interface PalsuProviderConfig {
|
|
|
506
520
|
*/
|
|
507
521
|
declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
|
|
508
522
|
|
|
509
|
-
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
|
|
523
|
+
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
|
package/dist/index.js
CHANGED
|
@@ -84,10 +84,25 @@ function formatResetTime(resetsAt) {
|
|
|
84
84
|
minute: "2-digit"
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
+
function isMythosAccessError(message) {
|
|
88
|
+
const lower = message.toLowerCase();
|
|
89
|
+
return lower.includes("mythos") && (lower.includes("not_found") || lower.includes("not found") || lower.includes("no access"));
|
|
90
|
+
}
|
|
87
91
|
function formatError(err) {
|
|
88
92
|
if (err instanceof ProviderError) {
|
|
89
93
|
const name = providerDisplayName(err.provider);
|
|
90
94
|
const cleanMessage = cleanProviderMessage(err.message);
|
|
95
|
+
if (isMythosAccessError(cleanMessage)) {
|
|
96
|
+
return {
|
|
97
|
+
headline: "Claude Mythos 5 is invitation-only.",
|
|
98
|
+
source: "provider",
|
|
99
|
+
message: "Your Anthropic account isn't approved for Project Glasswing, so the API reports the model as not found.",
|
|
100
|
+
provider: err.provider,
|
|
101
|
+
statusCode: err.statusCode,
|
|
102
|
+
...err.requestId ? { requestId: err.requestId } : {},
|
|
103
|
+
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."
|
|
104
|
+
};
|
|
105
|
+
}
|
|
91
106
|
if (isUsageLimitError(err)) {
|
|
92
107
|
const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
|
|
93
108
|
return {
|
|
@@ -274,21 +289,21 @@ var StreamResult = class {
|
|
|
274
289
|
resolveResponse;
|
|
275
290
|
rejectResponse;
|
|
276
291
|
resolveWait = null;
|
|
277
|
-
constructor(generator) {
|
|
292
|
+
constructor(generator, signal) {
|
|
278
293
|
this.response = new Promise((resolve, reject) => {
|
|
279
294
|
this.resolveResponse = resolve;
|
|
280
295
|
this.rejectResponse = reject;
|
|
281
296
|
});
|
|
282
|
-
this.pump(generator);
|
|
297
|
+
this.pump(generator, signal);
|
|
283
298
|
}
|
|
284
|
-
async pump(generator) {
|
|
299
|
+
async pump(generator, signal) {
|
|
285
300
|
try {
|
|
286
|
-
let next = await
|
|
301
|
+
let next = await this._nextWithAbort(generator, signal);
|
|
287
302
|
while (!next.done) {
|
|
288
303
|
this.buffer.push(next.value);
|
|
289
304
|
this.resolveWait?.();
|
|
290
305
|
this.resolveWait = null;
|
|
291
|
-
next = await
|
|
306
|
+
next = await this._nextWithAbort(generator, signal);
|
|
292
307
|
}
|
|
293
308
|
this.done = true;
|
|
294
309
|
this.resolveResponse(next.value);
|
|
@@ -303,6 +318,28 @@ var StreamResult = class {
|
|
|
303
318
|
this.resolveWait = null;
|
|
304
319
|
}
|
|
305
320
|
}
|
|
321
|
+
async _nextWithAbort(generator, signal) {
|
|
322
|
+
if (!signal) {
|
|
323
|
+
return generator.next();
|
|
324
|
+
}
|
|
325
|
+
if (signal.aborted) {
|
|
326
|
+
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
327
|
+
}
|
|
328
|
+
let onAbort;
|
|
329
|
+
const abortPromise = new Promise((_, reject) => {
|
|
330
|
+
onAbort = () => {
|
|
331
|
+
generator.return?.(void 0).catch(() => {
|
|
332
|
+
});
|
|
333
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
334
|
+
};
|
|
335
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
336
|
+
});
|
|
337
|
+
try {
|
|
338
|
+
return await Promise.race([generator.next(), abortPromise]);
|
|
339
|
+
} finally {
|
|
340
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
306
343
|
async *[Symbol.asyncIterator]() {
|
|
307
344
|
let index = 0;
|
|
308
345
|
while (true) {
|
|
@@ -399,6 +436,29 @@ function isRawThinking(part) {
|
|
|
399
436
|
const t = part.data.type;
|
|
400
437
|
return t === "thinking" || t === "redacted_thinking";
|
|
401
438
|
}
|
|
439
|
+
var ANTHROPIC_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
440
|
+
"bash_code_execution_tool_result",
|
|
441
|
+
"code_execution_tool_result",
|
|
442
|
+
"connector_text",
|
|
443
|
+
"container_upload",
|
|
444
|
+
"document",
|
|
445
|
+
"image",
|
|
446
|
+
"mid_conv_system",
|
|
447
|
+
"redacted_thinking",
|
|
448
|
+
"search_result",
|
|
449
|
+
"server_tool_use",
|
|
450
|
+
"text",
|
|
451
|
+
"text_editor_code_execution_tool_result",
|
|
452
|
+
"thinking",
|
|
453
|
+
"tool_result",
|
|
454
|
+
"tool_search_tool_result",
|
|
455
|
+
"tool_use",
|
|
456
|
+
"web_fetch_tool_result",
|
|
457
|
+
"web_search_tool_result"
|
|
458
|
+
]);
|
|
459
|
+
function isAnthropicCompatibleRaw(part) {
|
|
460
|
+
return ANTHROPIC_INPUT_BLOCK_TYPES.has(part.data.type);
|
|
461
|
+
}
|
|
402
462
|
function isPositionSensitiveThinking(part) {
|
|
403
463
|
if (part.type === "thinking") return hasValidThinkingSignature(part);
|
|
404
464
|
return isRawThinking(part);
|
|
@@ -425,7 +485,8 @@ function toAnthropicAssistantPart(part, idMap) {
|
|
|
425
485
|
};
|
|
426
486
|
if (part.type === "server_tool_result")
|
|
427
487
|
return part.data;
|
|
428
|
-
if (part.type === "raw")
|
|
488
|
+
if (part.type === "raw")
|
|
489
|
+
return isAnthropicCompatibleRaw(part) ? part.data : null;
|
|
429
490
|
return null;
|
|
430
491
|
}
|
|
431
492
|
function toAnthropicAssistantContent(content, preserveThinking, idMap) {
|
|
@@ -684,12 +745,12 @@ function toAnthropicToolChoice(choice) {
|
|
|
684
745
|
return { type: "tool", name: choice.name };
|
|
685
746
|
}
|
|
686
747
|
function isAdaptiveThinkingModel(model) {
|
|
687
|
-
return /
|
|
748
|
+
return /opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-4[-.]6|fable-5|mythos-5/.test(model);
|
|
688
749
|
}
|
|
689
750
|
function toAnthropicThinking(level, maxTokens, model) {
|
|
690
751
|
if (isAdaptiveThinkingModel(model)) {
|
|
691
752
|
let effort = level;
|
|
692
|
-
if (effort === "xhigh" && !/opus-4-8|opus-4-7
|
|
753
|
+
if (effort === "xhigh" && !/opus-4-8|opus-4-7/.test(model)) {
|
|
693
754
|
effort = "high";
|
|
694
755
|
}
|
|
695
756
|
return {
|
|
@@ -698,16 +759,17 @@ function toAnthropicThinking(level, maxTokens, model) {
|
|
|
698
759
|
outputConfig: { effort }
|
|
699
760
|
};
|
|
700
761
|
}
|
|
762
|
+
const VISIBLE_FLOOR = 1024;
|
|
701
763
|
const effectiveLevel = level === "xhigh" || level === "max" ? "high" : level;
|
|
702
764
|
const budgetMap = {
|
|
703
|
-
low: Math.max(1024, Math.floor(maxTokens * 0.
|
|
704
|
-
medium: Math.max(2048, Math.floor(maxTokens * 0.
|
|
705
|
-
high: Math.max(4096, maxTokens)
|
|
765
|
+
low: Math.max(1024, Math.floor(maxTokens * 0.2)),
|
|
766
|
+
medium: Math.max(2048, Math.floor(maxTokens * 0.45)),
|
|
767
|
+
high: Math.max(4096, Math.floor(maxTokens * 0.8))
|
|
706
768
|
};
|
|
707
|
-
const budget = budgetMap[effectiveLevel];
|
|
769
|
+
const budget = Math.max(0, Math.min(budgetMap[effectiveLevel], maxTokens - VISIBLE_FLOOR));
|
|
708
770
|
return {
|
|
709
771
|
thinking: { type: "enabled", budget_tokens: budget },
|
|
710
|
-
maxTokens
|
|
772
|
+
maxTokens
|
|
711
773
|
};
|
|
712
774
|
}
|
|
713
775
|
function remapToolCallId(id, idMap) {
|
|
@@ -919,10 +981,10 @@ function createClient(options) {
|
|
|
919
981
|
...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
|
|
920
982
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
921
983
|
...options.fetch ? { fetch: options.fetch } : {},
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
maxRetries:
|
|
984
|
+
// Disable SDK retries — the agent loop has its own stall/overload retry
|
|
985
|
+
// logic that surfaces errors properly. SDK retries on 429s can cause
|
|
986
|
+
// multi-minute hangs when the provider stops responding mid-retry.
|
|
987
|
+
maxRetries: 0,
|
|
926
988
|
...isOAuth ? {
|
|
927
989
|
defaultHeaders: {
|
|
928
990
|
// Anthropic's OAuth edge validates the claude-cli version. Callers
|
|
@@ -935,7 +997,7 @@ function createClient(options) {
|
|
|
935
997
|
});
|
|
936
998
|
}
|
|
937
999
|
function streamAnthropic(options) {
|
|
938
|
-
return new StreamResult(runStream(options));
|
|
1000
|
+
return new StreamResult(runStream(options), options.signal);
|
|
939
1001
|
}
|
|
940
1002
|
async function* runStream(options) {
|
|
941
1003
|
const client = createClient(options);
|
|
@@ -1030,7 +1092,6 @@ async function* runStream(options) {
|
|
|
1030
1092
|
throw toError(err);
|
|
1031
1093
|
}
|
|
1032
1094
|
}
|
|
1033
|
-
const stream2 = client.messages.stream(params, requestOptions);
|
|
1034
1095
|
const contentParts = [];
|
|
1035
1096
|
const blocks = /* @__PURE__ */ new Map();
|
|
1036
1097
|
let inputTokens = 0;
|
|
@@ -1039,8 +1100,14 @@ async function* runStream(options) {
|
|
|
1039
1100
|
let cacheWrite;
|
|
1040
1101
|
let stopReason = null;
|
|
1041
1102
|
const keepalive = { type: "keepalive" };
|
|
1103
|
+
let receivedAnyEvent = false;
|
|
1042
1104
|
try {
|
|
1105
|
+
const stream2 = await client.messages.create(
|
|
1106
|
+
params,
|
|
1107
|
+
requestOptions
|
|
1108
|
+
);
|
|
1043
1109
|
for await (const event of stream2) {
|
|
1110
|
+
receivedAnyEvent = true;
|
|
1044
1111
|
switch (event.type) {
|
|
1045
1112
|
case "message_start": {
|
|
1046
1113
|
const usage = event.message.usage;
|
|
@@ -1077,7 +1144,7 @@ async function* runStream(options) {
|
|
|
1077
1144
|
accum.toolId = block.id;
|
|
1078
1145
|
accum.toolName = block.name;
|
|
1079
1146
|
accum.input = block.input;
|
|
1080
|
-
} else if (block.type
|
|
1147
|
+
} else if (block.type !== "text" && block.type !== "thinking") {
|
|
1081
1148
|
accum.raw = block;
|
|
1082
1149
|
}
|
|
1083
1150
|
blocks.set(idx, accum);
|
|
@@ -1174,8 +1241,7 @@ async function* runStream(options) {
|
|
|
1174
1241
|
contentParts.push({ type: "raw", data: accum.raw });
|
|
1175
1242
|
yield keepalive;
|
|
1176
1243
|
} else {
|
|
1177
|
-
const
|
|
1178
|
-
const rawBlock = msg?.content[event.index];
|
|
1244
|
+
const rawBlock = accum.raw;
|
|
1179
1245
|
if (rawBlock) {
|
|
1180
1246
|
const blockType = rawBlock.type;
|
|
1181
1247
|
if (blockType === "web_search_tool_result") {
|
|
@@ -1221,6 +1287,11 @@ async function* runStream(options) {
|
|
|
1221
1287
|
} catch (err) {
|
|
1222
1288
|
throw toError(err);
|
|
1223
1289
|
}
|
|
1290
|
+
if (!receivedAnyEvent) {
|
|
1291
|
+
throw new ProviderError("anthropic", "Stream ended without producing any events.", {
|
|
1292
|
+
statusCode: 504
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1224
1295
|
const normalizedStop = normalizeAnthropicStopReason(stopReason);
|
|
1225
1296
|
const response = {
|
|
1226
1297
|
message: {
|
|
@@ -1512,7 +1583,7 @@ function createClient2(options) {
|
|
|
1512
1583
|
});
|
|
1513
1584
|
}
|
|
1514
1585
|
function streamOpenAI(options) {
|
|
1515
|
-
return new StreamResult(runStream2(options));
|
|
1586
|
+
return new StreamResult(runStream2(options), options.signal);
|
|
1516
1587
|
}
|
|
1517
1588
|
async function* runStream2(options) {
|
|
1518
1589
|
const providerName = options.provider ?? "openai";
|
|
@@ -1604,51 +1675,62 @@ async function* runStream2(options) {
|
|
|
1604
1675
|
let outputTokens = 0;
|
|
1605
1676
|
let cacheRead = 0;
|
|
1606
1677
|
let finishReason = null;
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
finishReason = choice.finish_reason;
|
|
1615
|
-
}
|
|
1616
|
-
const delta = choice.delta;
|
|
1617
|
-
const reasoningContent = delta.reasoning_content;
|
|
1618
|
-
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1619
|
-
thinkingAccum += reasoningContent;
|
|
1620
|
-
if (options.thinking) {
|
|
1621
|
-
yield { type: "thinking_delta", text: reasoningContent };
|
|
1678
|
+
let receivedAnyChunk = false;
|
|
1679
|
+
try {
|
|
1680
|
+
for await (const chunk of stream2) {
|
|
1681
|
+
receivedAnyChunk = true;
|
|
1682
|
+
const choice = chunk.choices?.[0];
|
|
1683
|
+
if (chunk.usage) {
|
|
1684
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
|
|
1622
1685
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
if (
|
|
1632
|
-
|
|
1633
|
-
id: tc.id ?? "",
|
|
1634
|
-
name: tc.function?.name ?? "",
|
|
1635
|
-
argsJson: ""
|
|
1636
|
-
};
|
|
1637
|
-
toolCallAccum.set(tc.index, accum);
|
|
1686
|
+
if (!choice) continue;
|
|
1687
|
+
if (choice.finish_reason) {
|
|
1688
|
+
finishReason = choice.finish_reason;
|
|
1689
|
+
}
|
|
1690
|
+
const delta = choice.delta;
|
|
1691
|
+
const reasoningContent = delta.reasoning_content;
|
|
1692
|
+
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1693
|
+
thinkingAccum += reasoningContent;
|
|
1694
|
+
if (options.thinking) {
|
|
1695
|
+
yield { type: "thinking_delta", text: reasoningContent };
|
|
1638
1696
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1697
|
+
}
|
|
1698
|
+
if (delta.content) {
|
|
1699
|
+
textAccum += delta.content;
|
|
1700
|
+
yield { type: "text_delta", text: delta.content };
|
|
1701
|
+
}
|
|
1702
|
+
if (delta.tool_calls) {
|
|
1703
|
+
for (const tc of delta.tool_calls) {
|
|
1704
|
+
let accum = toolCallAccum.get(tc.index);
|
|
1705
|
+
if (!accum) {
|
|
1706
|
+
accum = {
|
|
1707
|
+
id: tc.id ?? "",
|
|
1708
|
+
name: tc.function?.name ?? "",
|
|
1709
|
+
argsJson: ""
|
|
1710
|
+
};
|
|
1711
|
+
toolCallAccum.set(tc.index, accum);
|
|
1712
|
+
}
|
|
1713
|
+
if (tc.id) accum.id = tc.id;
|
|
1714
|
+
if (tc.function?.name) accum.name = tc.function.name;
|
|
1715
|
+
if (tc.function?.arguments) {
|
|
1716
|
+
accum.argsJson += tc.function.arguments;
|
|
1717
|
+
yield {
|
|
1718
|
+
type: "toolcall_delta",
|
|
1719
|
+
id: accum.id,
|
|
1720
|
+
name: accum.name,
|
|
1721
|
+
argsJson: tc.function.arguments
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1649
1724
|
}
|
|
1650
1725
|
}
|
|
1651
1726
|
}
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
throw toError2(err, providerName);
|
|
1729
|
+
}
|
|
1730
|
+
if (!receivedAnyChunk) {
|
|
1731
|
+
throw new ProviderError(providerName, "Stream ended without producing any chunks.", {
|
|
1732
|
+
statusCode: 504
|
|
1733
|
+
});
|
|
1652
1734
|
}
|
|
1653
1735
|
if (thinkingAccum) {
|
|
1654
1736
|
contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
@@ -1891,7 +1973,7 @@ function isVisibleOutputItem(itemType) {
|
|
|
1891
1973
|
return itemType === "message";
|
|
1892
1974
|
}
|
|
1893
1975
|
function streamOpenAICodex(options) {
|
|
1894
|
-
return new StreamResult(runStream3(options));
|
|
1976
|
+
return new StreamResult(runStream3(options), options.signal);
|
|
1895
1977
|
}
|
|
1896
1978
|
async function* runStream3(options) {
|
|
1897
1979
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
@@ -2221,10 +2303,16 @@ async function* parseSSE(body) {
|
|
|
2221
2303
|
}
|
|
2222
2304
|
}
|
|
2223
2305
|
function remapCodexId(id, idMap) {
|
|
2224
|
-
if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
|
|
2225
2306
|
const existing = idMap.get(id);
|
|
2226
2307
|
if (existing) return existing;
|
|
2227
|
-
const
|
|
2308
|
+
const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
|
|
2309
|
+
const sanitized = withPrefix.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
2310
|
+
let mapped = sanitized;
|
|
2311
|
+
let suffix = 2;
|
|
2312
|
+
const used = new Set(idMap.values());
|
|
2313
|
+
while (used.has(mapped)) {
|
|
2314
|
+
mapped = `${sanitized}_${suffix++}`;
|
|
2315
|
+
}
|
|
2228
2316
|
idMap.set(id, mapped);
|
|
2229
2317
|
return mapped;
|
|
2230
2318
|
}
|
|
@@ -2747,7 +2835,7 @@ async function fetchCodeAssistWithRetry(plan, options) {
|
|
|
2747
2835
|
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2748
2836
|
}
|
|
2749
2837
|
function streamGemini(options) {
|
|
2750
|
-
return new StreamResult(runStream4(options));
|
|
2838
|
+
return new StreamResult(runStream4(options), options.signal);
|
|
2751
2839
|
}
|
|
2752
2840
|
async function* runStream4(options) {
|
|
2753
2841
|
const useStreaming = options.streaming !== false;
|
|
@@ -2967,6 +3055,96 @@ function messagesContainVideo(messages) {
|
|
|
2967
3055
|
return false;
|
|
2968
3056
|
}
|
|
2969
3057
|
|
|
3058
|
+
// src/error-classification.ts
|
|
3059
|
+
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
3060
|
+
/context_length_exceeded/i,
|
|
3061
|
+
/context length exceeded/i,
|
|
3062
|
+
/context window/i,
|
|
3063
|
+
// OpenAI Codex / Responses
|
|
3064
|
+
/maximum context length/i,
|
|
3065
|
+
// OpenAI / OpenRouter / Mistral
|
|
3066
|
+
/prompt is too long/i,
|
|
3067
|
+
// Anthropic
|
|
3068
|
+
/request_too_large/i,
|
|
3069
|
+
// Anthropic HTTP 413
|
|
3070
|
+
/input is too long/i,
|
|
3071
|
+
// Bedrock
|
|
3072
|
+
/input token count.*exceeds the maximum/i,
|
|
3073
|
+
// Gemini
|
|
3074
|
+
/maximum prompt length/i,
|
|
3075
|
+
// xAI / Grok
|
|
3076
|
+
/reduce the length of the messages/i,
|
|
3077
|
+
// Groq
|
|
3078
|
+
/too large for model/i,
|
|
3079
|
+
// Mistral
|
|
3080
|
+
/token limit/i
|
|
3081
|
+
// generic
|
|
3082
|
+
];
|
|
3083
|
+
var RATE_LIMIT_PATTERNS = [
|
|
3084
|
+
/rate[ _-]?limit/i,
|
|
3085
|
+
/\b429\b/,
|
|
3086
|
+
/too many requests/i,
|
|
3087
|
+
/tokens per minute/i,
|
|
3088
|
+
/requests per minute/i
|
|
3089
|
+
];
|
|
3090
|
+
var PROVIDER_TRANSIENT_PATTERNS = [
|
|
3091
|
+
/\b5\d\d\b/,
|
|
3092
|
+
/api_error/i,
|
|
3093
|
+
/server_error/i,
|
|
3094
|
+
/internal server error/i,
|
|
3095
|
+
/bad gateway/i,
|
|
3096
|
+
/service unavailable/i,
|
|
3097
|
+
/gateway timeout/i,
|
|
3098
|
+
/overloaded/i,
|
|
3099
|
+
/\b529\b/
|
|
3100
|
+
];
|
|
3101
|
+
var BILLING_PATTERNS = [
|
|
3102
|
+
/payment required/i,
|
|
3103
|
+
/\b402\b/,
|
|
3104
|
+
/quota_exceeded/i,
|
|
3105
|
+
// underscore variant not in isHardBillingMessage
|
|
3106
|
+
/credit balance/i
|
|
3107
|
+
];
|
|
3108
|
+
var AUTH_PATTERNS = [
|
|
3109
|
+
/invalid[ _]api[ _]key/i,
|
|
3110
|
+
/unauthorized/i,
|
|
3111
|
+
/\b401\b/,
|
|
3112
|
+
/authentication[ _]failed/i,
|
|
3113
|
+
/please run \/login/i
|
|
3114
|
+
// Anthropic Claude Code-style hint
|
|
3115
|
+
];
|
|
3116
|
+
function matchesAny(message, patterns) {
|
|
3117
|
+
return patterns.some((p) => p.test(message));
|
|
3118
|
+
}
|
|
3119
|
+
function classifyProviderError(message) {
|
|
3120
|
+
if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
3121
|
+
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.
|
|
3122
|
+
|
|
3123
|
+
Original: ${message}`;
|
|
3124
|
+
}
|
|
3125
|
+
if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
|
|
3126
|
+
return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
|
|
3127
|
+
|
|
3128
|
+
Original: ${message}`;
|
|
3129
|
+
}
|
|
3130
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
3131
|
+
return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
|
|
3132
|
+
|
|
3133
|
+
Original: ${message}`;
|
|
3134
|
+
}
|
|
3135
|
+
if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
|
|
3136
|
+
return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
|
|
3137
|
+
|
|
3138
|
+
Original: ${message}`;
|
|
3139
|
+
}
|
|
3140
|
+
if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
|
|
3141
|
+
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.
|
|
3142
|
+
|
|
3143
|
+
Original: ${message}`;
|
|
3144
|
+
}
|
|
3145
|
+
return message;
|
|
3146
|
+
}
|
|
3147
|
+
|
|
2970
3148
|
// src/providers/palsu.ts
|
|
2971
3149
|
function palsuText(text) {
|
|
2972
3150
|
return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
|
|
@@ -3113,7 +3291,7 @@ function registerPalsuProvider(config) {
|
|
|
3113
3291
|
const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
|
|
3114
3292
|
return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
|
|
3115
3293
|
})();
|
|
3116
|
-
return new StreamResult(gen);
|
|
3294
|
+
return new StreamResult(gen, options.signal);
|
|
3117
3295
|
}
|
|
3118
3296
|
});
|
|
3119
3297
|
return handle;
|
|
@@ -3123,6 +3301,7 @@ export {
|
|
|
3123
3301
|
EventStream,
|
|
3124
3302
|
ProviderError,
|
|
3125
3303
|
StreamResult,
|
|
3304
|
+
classifyProviderError,
|
|
3126
3305
|
formatError,
|
|
3127
3306
|
formatErrorForDisplay,
|
|
3128
3307
|
isHardBillingMessage,
|