@prestyj/ai 4.6.4 → 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/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 {
|
|
@@ -919,10 +980,10 @@ function createClient(options) {
|
|
|
919
980
|
...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
|
|
920
981
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
921
982
|
...options.fetch ? { fetch: options.fetch } : {},
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
maxRetries:
|
|
983
|
+
// Disable SDK retries — the agent loop has its own stall/overload retry
|
|
984
|
+
// logic that surfaces errors properly. SDK retries on 429s can cause
|
|
985
|
+
// multi-minute hangs when the provider stops responding mid-retry.
|
|
986
|
+
maxRetries: 0,
|
|
926
987
|
...isOAuth ? {
|
|
927
988
|
defaultHeaders: {
|
|
928
989
|
// Anthropic's OAuth edge validates the claude-cli version. Callers
|
|
@@ -935,7 +996,7 @@ function createClient(options) {
|
|
|
935
996
|
});
|
|
936
997
|
}
|
|
937
998
|
function streamAnthropic(options) {
|
|
938
|
-
return new StreamResult(runStream(options));
|
|
999
|
+
return new StreamResult(runStream(options), options.signal);
|
|
939
1000
|
}
|
|
940
1001
|
async function* runStream(options) {
|
|
941
1002
|
const client = createClient(options);
|
|
@@ -1030,7 +1091,6 @@ async function* runStream(options) {
|
|
|
1030
1091
|
throw toError(err);
|
|
1031
1092
|
}
|
|
1032
1093
|
}
|
|
1033
|
-
const stream2 = client.messages.stream(params, requestOptions);
|
|
1034
1094
|
const contentParts = [];
|
|
1035
1095
|
const blocks = /* @__PURE__ */ new Map();
|
|
1036
1096
|
let inputTokens = 0;
|
|
@@ -1039,8 +1099,14 @@ async function* runStream(options) {
|
|
|
1039
1099
|
let cacheWrite;
|
|
1040
1100
|
let stopReason = null;
|
|
1041
1101
|
const keepalive = { type: "keepalive" };
|
|
1102
|
+
let receivedAnyEvent = false;
|
|
1042
1103
|
try {
|
|
1104
|
+
const stream2 = await client.messages.create(
|
|
1105
|
+
params,
|
|
1106
|
+
requestOptions
|
|
1107
|
+
);
|
|
1043
1108
|
for await (const event of stream2) {
|
|
1109
|
+
receivedAnyEvent = true;
|
|
1044
1110
|
switch (event.type) {
|
|
1045
1111
|
case "message_start": {
|
|
1046
1112
|
const usage = event.message.usage;
|
|
@@ -1077,7 +1143,7 @@ async function* runStream(options) {
|
|
|
1077
1143
|
accum.toolId = block.id;
|
|
1078
1144
|
accum.toolName = block.name;
|
|
1079
1145
|
accum.input = block.input;
|
|
1080
|
-
} else if (block.type
|
|
1146
|
+
} else if (block.type !== "text" && block.type !== "thinking") {
|
|
1081
1147
|
accum.raw = block;
|
|
1082
1148
|
}
|
|
1083
1149
|
blocks.set(idx, accum);
|
|
@@ -1174,8 +1240,7 @@ async function* runStream(options) {
|
|
|
1174
1240
|
contentParts.push({ type: "raw", data: accum.raw });
|
|
1175
1241
|
yield keepalive;
|
|
1176
1242
|
} else {
|
|
1177
|
-
const
|
|
1178
|
-
const rawBlock = msg?.content[event.index];
|
|
1243
|
+
const rawBlock = accum.raw;
|
|
1179
1244
|
if (rawBlock) {
|
|
1180
1245
|
const blockType = rawBlock.type;
|
|
1181
1246
|
if (blockType === "web_search_tool_result") {
|
|
@@ -1221,6 +1286,11 @@ async function* runStream(options) {
|
|
|
1221
1286
|
} catch (err) {
|
|
1222
1287
|
throw toError(err);
|
|
1223
1288
|
}
|
|
1289
|
+
if (!receivedAnyEvent) {
|
|
1290
|
+
throw new ProviderError("anthropic", "Stream ended without producing any events.", {
|
|
1291
|
+
statusCode: 504
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1224
1294
|
const normalizedStop = normalizeAnthropicStopReason(stopReason);
|
|
1225
1295
|
const response = {
|
|
1226
1296
|
message: {
|
|
@@ -1512,7 +1582,7 @@ function createClient2(options) {
|
|
|
1512
1582
|
});
|
|
1513
1583
|
}
|
|
1514
1584
|
function streamOpenAI(options) {
|
|
1515
|
-
return new StreamResult(runStream2(options));
|
|
1585
|
+
return new StreamResult(runStream2(options), options.signal);
|
|
1516
1586
|
}
|
|
1517
1587
|
async function* runStream2(options) {
|
|
1518
1588
|
const providerName = options.provider ?? "openai";
|
|
@@ -1604,51 +1674,62 @@ async function* runStream2(options) {
|
|
|
1604
1674
|
let outputTokens = 0;
|
|
1605
1675
|
let cacheRead = 0;
|
|
1606
1676
|
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 };
|
|
1677
|
+
let receivedAnyChunk = false;
|
|
1678
|
+
try {
|
|
1679
|
+
for await (const chunk of stream2) {
|
|
1680
|
+
receivedAnyChunk = true;
|
|
1681
|
+
const choice = chunk.choices?.[0];
|
|
1682
|
+
if (chunk.usage) {
|
|
1683
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
|
|
1622
1684
|
}
|
|
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);
|
|
1685
|
+
if (!choice) continue;
|
|
1686
|
+
if (choice.finish_reason) {
|
|
1687
|
+
finishReason = choice.finish_reason;
|
|
1688
|
+
}
|
|
1689
|
+
const delta = choice.delta;
|
|
1690
|
+
const reasoningContent = delta.reasoning_content;
|
|
1691
|
+
if (typeof reasoningContent === "string" && reasoningContent) {
|
|
1692
|
+
thinkingAccum += reasoningContent;
|
|
1693
|
+
if (options.thinking) {
|
|
1694
|
+
yield { type: "thinking_delta", text: reasoningContent };
|
|
1638
1695
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1696
|
+
}
|
|
1697
|
+
if (delta.content) {
|
|
1698
|
+
textAccum += delta.content;
|
|
1699
|
+
yield { type: "text_delta", text: delta.content };
|
|
1700
|
+
}
|
|
1701
|
+
if (delta.tool_calls) {
|
|
1702
|
+
for (const tc of delta.tool_calls) {
|
|
1703
|
+
let accum = toolCallAccum.get(tc.index);
|
|
1704
|
+
if (!accum) {
|
|
1705
|
+
accum = {
|
|
1706
|
+
id: tc.id ?? "",
|
|
1707
|
+
name: tc.function?.name ?? "",
|
|
1708
|
+
argsJson: ""
|
|
1709
|
+
};
|
|
1710
|
+
toolCallAccum.set(tc.index, accum);
|
|
1711
|
+
}
|
|
1712
|
+
if (tc.id) accum.id = tc.id;
|
|
1713
|
+
if (tc.function?.name) accum.name = tc.function.name;
|
|
1714
|
+
if (tc.function?.arguments) {
|
|
1715
|
+
accum.argsJson += tc.function.arguments;
|
|
1716
|
+
yield {
|
|
1717
|
+
type: "toolcall_delta",
|
|
1718
|
+
id: accum.id,
|
|
1719
|
+
name: accum.name,
|
|
1720
|
+
argsJson: tc.function.arguments
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1649
1723
|
}
|
|
1650
1724
|
}
|
|
1651
1725
|
}
|
|
1726
|
+
} catch (err) {
|
|
1727
|
+
throw toError2(err, providerName);
|
|
1728
|
+
}
|
|
1729
|
+
if (!receivedAnyChunk) {
|
|
1730
|
+
throw new ProviderError(providerName, "Stream ended without producing any chunks.", {
|
|
1731
|
+
statusCode: 504
|
|
1732
|
+
});
|
|
1652
1733
|
}
|
|
1653
1734
|
if (thinkingAccum) {
|
|
1654
1735
|
contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
@@ -1891,7 +1972,7 @@ function isVisibleOutputItem(itemType) {
|
|
|
1891
1972
|
return itemType === "message";
|
|
1892
1973
|
}
|
|
1893
1974
|
function streamOpenAICodex(options) {
|
|
1894
|
-
return new StreamResult(runStream3(options));
|
|
1975
|
+
return new StreamResult(runStream3(options), options.signal);
|
|
1895
1976
|
}
|
|
1896
1977
|
async function* runStream3(options) {
|
|
1897
1978
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
@@ -2221,10 +2302,16 @@ async function* parseSSE(body) {
|
|
|
2221
2302
|
}
|
|
2222
2303
|
}
|
|
2223
2304
|
function remapCodexId(id, idMap) {
|
|
2224
|
-
if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
|
|
2225
2305
|
const existing = idMap.get(id);
|
|
2226
2306
|
if (existing) return existing;
|
|
2227
|
-
const
|
|
2307
|
+
const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
|
|
2308
|
+
const sanitized = withPrefix.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
2309
|
+
let mapped = sanitized;
|
|
2310
|
+
let suffix = 2;
|
|
2311
|
+
const used = new Set(idMap.values());
|
|
2312
|
+
while (used.has(mapped)) {
|
|
2313
|
+
mapped = `${sanitized}_${suffix++}`;
|
|
2314
|
+
}
|
|
2228
2315
|
idMap.set(id, mapped);
|
|
2229
2316
|
return mapped;
|
|
2230
2317
|
}
|
|
@@ -2747,7 +2834,7 @@ async function fetchCodeAssistWithRetry(plan, options) {
|
|
|
2747
2834
|
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2748
2835
|
}
|
|
2749
2836
|
function streamGemini(options) {
|
|
2750
|
-
return new StreamResult(runStream4(options));
|
|
2837
|
+
return new StreamResult(runStream4(options), options.signal);
|
|
2751
2838
|
}
|
|
2752
2839
|
async function* runStream4(options) {
|
|
2753
2840
|
const useStreaming = options.streaming !== false;
|
|
@@ -2967,6 +3054,96 @@ function messagesContainVideo(messages) {
|
|
|
2967
3054
|
return false;
|
|
2968
3055
|
}
|
|
2969
3056
|
|
|
3057
|
+
// src/error-classification.ts
|
|
3058
|
+
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
3059
|
+
/context_length_exceeded/i,
|
|
3060
|
+
/context length exceeded/i,
|
|
3061
|
+
/context window/i,
|
|
3062
|
+
// OpenAI Codex / Responses
|
|
3063
|
+
/maximum context length/i,
|
|
3064
|
+
// OpenAI / OpenRouter / Mistral
|
|
3065
|
+
/prompt is too long/i,
|
|
3066
|
+
// Anthropic
|
|
3067
|
+
/request_too_large/i,
|
|
3068
|
+
// Anthropic HTTP 413
|
|
3069
|
+
/input is too long/i,
|
|
3070
|
+
// Bedrock
|
|
3071
|
+
/input token count.*exceeds the maximum/i,
|
|
3072
|
+
// Gemini
|
|
3073
|
+
/maximum prompt length/i,
|
|
3074
|
+
// xAI / Grok
|
|
3075
|
+
/reduce the length of the messages/i,
|
|
3076
|
+
// Groq
|
|
3077
|
+
/too large for model/i,
|
|
3078
|
+
// Mistral
|
|
3079
|
+
/token limit/i
|
|
3080
|
+
// generic
|
|
3081
|
+
];
|
|
3082
|
+
var RATE_LIMIT_PATTERNS = [
|
|
3083
|
+
/rate[ _-]?limit/i,
|
|
3084
|
+
/\b429\b/,
|
|
3085
|
+
/too many requests/i,
|
|
3086
|
+
/tokens per minute/i,
|
|
3087
|
+
/requests per minute/i
|
|
3088
|
+
];
|
|
3089
|
+
var PROVIDER_TRANSIENT_PATTERNS = [
|
|
3090
|
+
/\b5\d\d\b/,
|
|
3091
|
+
/api_error/i,
|
|
3092
|
+
/server_error/i,
|
|
3093
|
+
/internal server error/i,
|
|
3094
|
+
/bad gateway/i,
|
|
3095
|
+
/service unavailable/i,
|
|
3096
|
+
/gateway timeout/i,
|
|
3097
|
+
/overloaded/i,
|
|
3098
|
+
/\b529\b/
|
|
3099
|
+
];
|
|
3100
|
+
var BILLING_PATTERNS = [
|
|
3101
|
+
/payment required/i,
|
|
3102
|
+
/\b402\b/,
|
|
3103
|
+
/quota_exceeded/i,
|
|
3104
|
+
// underscore variant not in isHardBillingMessage
|
|
3105
|
+
/credit balance/i
|
|
3106
|
+
];
|
|
3107
|
+
var AUTH_PATTERNS = [
|
|
3108
|
+
/invalid[ _]api[ _]key/i,
|
|
3109
|
+
/unauthorized/i,
|
|
3110
|
+
/\b401\b/,
|
|
3111
|
+
/authentication[ _]failed/i,
|
|
3112
|
+
/please run \/login/i
|
|
3113
|
+
// Anthropic Claude Code-style hint
|
|
3114
|
+
];
|
|
3115
|
+
function matchesAny(message, patterns) {
|
|
3116
|
+
return patterns.some((p) => p.test(message));
|
|
3117
|
+
}
|
|
3118
|
+
function classifyProviderError(message) {
|
|
3119
|
+
if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
3120
|
+
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.
|
|
3121
|
+
|
|
3122
|
+
Original: ${message}`;
|
|
3123
|
+
}
|
|
3124
|
+
if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
|
|
3125
|
+
return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
|
|
3126
|
+
|
|
3127
|
+
Original: ${message}`;
|
|
3128
|
+
}
|
|
3129
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
3130
|
+
return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
|
|
3131
|
+
|
|
3132
|
+
Original: ${message}`;
|
|
3133
|
+
}
|
|
3134
|
+
if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
|
|
3135
|
+
return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
|
|
3136
|
+
|
|
3137
|
+
Original: ${message}`;
|
|
3138
|
+
}
|
|
3139
|
+
if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
|
|
3140
|
+
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.
|
|
3141
|
+
|
|
3142
|
+
Original: ${message}`;
|
|
3143
|
+
}
|
|
3144
|
+
return message;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
2970
3147
|
// src/providers/palsu.ts
|
|
2971
3148
|
function palsuText(text) {
|
|
2972
3149
|
return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
|
|
@@ -3113,7 +3290,7 @@ function registerPalsuProvider(config) {
|
|
|
3113
3290
|
const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
|
|
3114
3291
|
return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
|
|
3115
3292
|
})();
|
|
3116
|
-
return new StreamResult(gen);
|
|
3293
|
+
return new StreamResult(gen, options.signal);
|
|
3117
3294
|
}
|
|
3118
3295
|
});
|
|
3119
3296
|
return handle;
|
|
@@ -3123,6 +3300,7 @@ export {
|
|
|
3123
3300
|
EventStream,
|
|
3124
3301
|
ProviderError,
|
|
3125
3302
|
StreamResult,
|
|
3303
|
+
classifyProviderError,
|
|
3126
3304
|
formatError,
|
|
3127
3305
|
formatErrorForDisplay,
|
|
3128
3306
|
isHardBillingMessage,
|