@link-assistant/agent 0.27.0 → 0.27.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 +47 -5
- package/package.json +41 -42
- package/src/cli/argv.ts +1 -1
- package/src/cli/cmd/run.ts +7 -1
- package/src/cli/continuous-mode.js +38 -12
- package/src/cli/event-handler.js +3 -1
- package/src/cli/model-config.js +20 -4
- package/src/cli/run-options.js +144 -78
- package/src/config/config.ts +306 -105
- package/src/config/defaults.ts +13 -5
- package/src/config/file-config.ts +16 -0
- package/src/file/ripgrep.ts +38 -5
- package/src/file/time.ts +86 -9
- package/src/index.js +39 -33
- package/src/mcp/index.ts +370 -106
- package/src/permission/index.ts +22 -0
- package/src/project/instance.ts +5 -1
- package/src/project/project.ts +35 -2
- package/src/provider/free-tier-error.ts +1 -1
- package/src/provider/kilo-deprecation.ts +30 -0
- package/src/provider/kilo.ts +25 -7
- package/src/provider/provider.ts +53 -22
- package/src/provider/retry-fetch.ts +5 -0
- package/src/provider/transform.ts +139 -17
- package/src/session/compaction.ts +18 -2
- package/src/session/index.ts +30 -3
- package/src/session/message-v2.ts +318 -32
- package/src/session/processor.ts +274 -140
- package/src/session/prompt.ts +154 -128
- package/src/session/retry.ts +26 -13
- package/src/session/summary.ts +4 -1
- package/src/tool/bash.ts +232 -45
- package/src/tool/bash.txt +1 -1
- package/src/tool/codesearch.ts +15 -78
- package/src/tool/edit.ts +111 -30
- package/src/tool/glob.ts +2 -1
- package/src/tool/grep.ts +175 -57
- package/src/tool/ls.ts +2 -1
- package/src/tool/multiedit.ts +3 -1
- package/src/tool/read.ts +169 -41
- package/src/tool/task.ts +26 -18
- package/src/tool/tool.ts +4 -1
- package/src/tool/webfetch.ts +68 -56
- package/src/tool/webfetch.txt +4 -4
- package/src/tool/websearch.ts +20 -75
- package/src/tool/websearch.txt +2 -0
- package/src/tool/write.ts +23 -6
- package/src/util/bom.ts +32 -0
- package/src/util/exa-mcp.ts +82 -0
- package/src/util/legacy-error-names.ts +39 -0
- package/src/util/read-bounded.ts +32 -0
- package/src/util/sse-usage-extractor.ts +67 -13
- package/src/util/timeout.ts +3 -4
- package/src/util/verbose-fetch.ts +40 -1
|
@@ -18,6 +18,7 @@ import { Snapshot } from '../snapshot';
|
|
|
18
18
|
import { fn } from '../util/fn';
|
|
19
19
|
import { Storage } from '../storage/storage';
|
|
20
20
|
import { isUnreachableNetworkError } from '../util/network-error';
|
|
21
|
+
import { STATUS_CODES } from 'node:http';
|
|
21
22
|
|
|
22
23
|
export namespace MessageV2 {
|
|
23
24
|
export const OutputLengthError = NamedError.create(
|
|
@@ -78,6 +79,96 @@ export namespace MessageV2 {
|
|
|
78
79
|
);
|
|
79
80
|
export type TimeoutError = z.infer<typeof TimeoutError.Schema>;
|
|
80
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The provider asked us to wait (retry-after) longer than the configured
|
|
84
|
+
* retry timeout, so the request is not retried.
|
|
85
|
+
* See: https://github.com/link-assistant/agent/issues/157
|
|
86
|
+
*/
|
|
87
|
+
export const RetryTimeoutExceededError = NamedError.create(
|
|
88
|
+
'RetryTimeoutExceededError',
|
|
89
|
+
z.object({
|
|
90
|
+
message: z.string(),
|
|
91
|
+
isRetryable: z.literal(false),
|
|
92
|
+
retryAfterMs: z.number(),
|
|
93
|
+
maxTimeoutMs: z.number(),
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
export type RetryTimeoutExceededError = z.infer<
|
|
97
|
+
typeof RetryTimeoutExceededError.Schema
|
|
98
|
+
>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The request exceeds the model's context window. Not retried as is: the
|
|
102
|
+
* session is compacted and the turn continues (upstream #14707).
|
|
103
|
+
*/
|
|
104
|
+
export const ContextOverflowError = NamedError.create(
|
|
105
|
+
'ContextOverflowError',
|
|
106
|
+
z.object({
|
|
107
|
+
message: z.string(),
|
|
108
|
+
responseBody: z.string().optional(),
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
export type ContextOverflowError = z.infer<
|
|
112
|
+
typeof ContextOverflowError.Schema
|
|
113
|
+
>;
|
|
114
|
+
|
|
115
|
+
// Provider wordings for "prompt exceeds the context window", from upstream
|
|
116
|
+
// packages/ai/src/provider-error.ts.
|
|
117
|
+
const OVERFLOW_PATTERNS = [
|
|
118
|
+
/prompt is too long/i,
|
|
119
|
+
/input is too long for (?:the )?(?:requested|selected) model/i,
|
|
120
|
+
/too large for the model'?s configured input limit/i,
|
|
121
|
+
/exceeded this model context window limit/i,
|
|
122
|
+
/exceeds the context window/i,
|
|
123
|
+
/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
|
|
124
|
+
/input token count.*exceeds the maximum/i,
|
|
125
|
+
/number of input tokens exceeds maximum length/i,
|
|
126
|
+
/tokens in request more than max tokens allowed/i,
|
|
127
|
+
/maximum prompt length is \d+/i,
|
|
128
|
+
/reduce the length of the messages/i,
|
|
129
|
+
/requested input length \d+ exceeds maximum input length/i,
|
|
130
|
+
/maximum context length is \d+ tokens/i,
|
|
131
|
+
/exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
|
|
132
|
+
/input(?: \(\d+ tokens\))? is longer than the model'?s context length/i,
|
|
133
|
+
/exceeds the limit of \d+/i,
|
|
134
|
+
/exceeds the available context size/i,
|
|
135
|
+
/greater than the context length/i,
|
|
136
|
+
/`inputs` tokens \+ `max_new_tokens` must be <= \d+/i,
|
|
137
|
+
/context window exceeds limit/i,
|
|
138
|
+
/exceeded model token limit/i,
|
|
139
|
+
/context[_ ]length[_ ]exceeded/i,
|
|
140
|
+
/context length is only \d+ tokens/i,
|
|
141
|
+
/input length.*exceeds.*context length/i,
|
|
142
|
+
/prompt (?:too long|exceeds max length|超长)/i,
|
|
143
|
+
/too large for model with \d+ maximum context length/i,
|
|
144
|
+
/prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
|
|
145
|
+
/model_context_window_exceeded/i,
|
|
146
|
+
/range of input length should be/i,
|
|
147
|
+
/too many tokens/i,
|
|
148
|
+
/token limit exceeded/i,
|
|
149
|
+
/request_too_large/i,
|
|
150
|
+
/request entity too large/i,
|
|
151
|
+
/payload too large/i,
|
|
152
|
+
/request too large/i,
|
|
153
|
+
];
|
|
154
|
+
// Rate limits also talk about "too many tokens"; they are not overflow.
|
|
155
|
+
const OVERFLOW_EXCLUDE_PATTERNS = [
|
|
156
|
+
/^(throttling error|service unavailable):/i,
|
|
157
|
+
/rate limit/i,
|
|
158
|
+
/too many requests/i,
|
|
159
|
+
/max[_ ]tokens must be less than/i,
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
export function isContextOverflow(message: string | undefined) {
|
|
163
|
+
if (!message) return false;
|
|
164
|
+
if (OVERFLOW_EXCLUDE_PATTERNS.some((pattern) => pattern.test(message)))
|
|
165
|
+
return false;
|
|
166
|
+
return (
|
|
167
|
+
OVERFLOW_PATTERNS.some((pattern) => pattern.test(message)) ||
|
|
168
|
+
/^4(?:00|13)\s*(status code)?\s*\(no body\)/i.test(message)
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
81
172
|
const PartBase = z.object({
|
|
82
173
|
id: z.string(),
|
|
83
174
|
sessionID: z.string(),
|
|
@@ -458,6 +549,11 @@ export namespace MessageV2 {
|
|
|
458
549
|
OutputLengthError.Schema,
|
|
459
550
|
AbortedError.Schema,
|
|
460
551
|
APIError.Schema,
|
|
552
|
+
ContextOverflowError.Schema,
|
|
553
|
+
// Retry-only errors are stored once their retries are used up.
|
|
554
|
+
SocketConnectionError.Schema,
|
|
555
|
+
TimeoutError.Schema,
|
|
556
|
+
RetryTimeoutExceededError.Schema,
|
|
461
557
|
])
|
|
462
558
|
.optional(),
|
|
463
559
|
parentID: z.string(),
|
|
@@ -669,11 +765,18 @@ export namespace MessageV2 {
|
|
|
669
765
|
throw new Error('unknown message type');
|
|
670
766
|
}
|
|
671
767
|
|
|
768
|
+
/**
|
|
769
|
+
* @param target the model the messages are sent to. Assistant turns from a
|
|
770
|
+
* different model drop their provider metadata and replay reasoning as text,
|
|
771
|
+
* because signatures and item ids are only valid for the model that produced
|
|
772
|
+
* them (upstream 021e42c0bb1).
|
|
773
|
+
*/
|
|
672
774
|
export async function toModelMessage(
|
|
673
775
|
input: {
|
|
674
776
|
info: Info;
|
|
675
777
|
parts: Part[];
|
|
676
|
-
}[]
|
|
778
|
+
}[],
|
|
779
|
+
target?: { providerID: string; modelID: string }
|
|
677
780
|
): Promise<ModelMessage[]> {
|
|
678
781
|
const result: UIMessage[] = [];
|
|
679
782
|
|
|
@@ -728,12 +831,24 @@ export namespace MessageV2 {
|
|
|
728
831
|
parts: [],
|
|
729
832
|
};
|
|
730
833
|
result.push(assistantMessage);
|
|
834
|
+
const differentModel =
|
|
835
|
+
target !== undefined &&
|
|
836
|
+
(target.providerID !== msg.info.providerID ||
|
|
837
|
+
target.modelID !== msg.info.modelID);
|
|
838
|
+
const metadata = <T>(value: T) => (differentModel ? undefined : value);
|
|
839
|
+
// Anthropic needs the empty separator between signed thinking blocks
|
|
840
|
+
// kept, so send ' ' rather than '' (upstream #21370).
|
|
841
|
+
const hasSignedReasoning = msg.parts.some(
|
|
842
|
+
(part) =>
|
|
843
|
+
part.type === 'reasoning' &&
|
|
844
|
+
part.metadata?.anthropic?.signature != null
|
|
845
|
+
);
|
|
731
846
|
for (const part of msg.parts) {
|
|
732
847
|
if (part.type === 'text')
|
|
733
848
|
assistantMessage.parts.push({
|
|
734
849
|
type: 'text',
|
|
735
|
-
text: part.text,
|
|
736
|
-
providerMetadata: part.metadata,
|
|
850
|
+
text: part.text === '' && hasSignedReasoning ? ' ' : part.text,
|
|
851
|
+
providerMetadata: metadata(part.metadata),
|
|
737
852
|
});
|
|
738
853
|
if (part.type === 'step-start')
|
|
739
854
|
assistantMessage.parts.push({
|
|
@@ -772,7 +887,7 @@ export namespace MessageV2 {
|
|
|
772
887
|
output: part.state.time.compacted
|
|
773
888
|
? '[Old tool result content cleared]'
|
|
774
889
|
: part.state.output,
|
|
775
|
-
callProviderMetadata: part.metadata,
|
|
890
|
+
callProviderMetadata: metadata(part.metadata),
|
|
776
891
|
});
|
|
777
892
|
}
|
|
778
893
|
if (part.state.status === 'error')
|
|
@@ -782,10 +897,15 @@ export namespace MessageV2 {
|
|
|
782
897
|
toolCallId: part.callID,
|
|
783
898
|
input: part.state.input,
|
|
784
899
|
errorText: part.state.error,
|
|
785
|
-
callProviderMetadata: part.metadata,
|
|
900
|
+
callProviderMetadata: metadata(part.metadata),
|
|
786
901
|
});
|
|
787
902
|
}
|
|
788
903
|
if (part.type === 'reasoning') {
|
|
904
|
+
if (differentModel) {
|
|
905
|
+
if (part.text.trim().length > 0)
|
|
906
|
+
assistantMessage.parts.push({ type: 'text', text: part.text });
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
789
909
|
assistantMessage.parts.push({
|
|
790
910
|
type: 'reasoning',
|
|
791
911
|
text: part.text,
|
|
@@ -796,7 +916,13 @@ export namespace MessageV2 {
|
|
|
796
916
|
}
|
|
797
917
|
}
|
|
798
918
|
|
|
799
|
-
|
|
919
|
+
// A message whose parts all convert to nothing would become
|
|
920
|
+
// `content: []`, which Anthropic and Bedrock reject (upstream #4811).
|
|
921
|
+
return await convertToModelMessages(
|
|
922
|
+
result.filter((msg) =>
|
|
923
|
+
msg.parts.some((part) => part.type !== 'step-start')
|
|
924
|
+
)
|
|
925
|
+
);
|
|
800
926
|
}
|
|
801
927
|
|
|
802
928
|
export const stream = fn(
|
|
@@ -861,6 +987,30 @@ export namespace MessageV2 {
|
|
|
861
987
|
return result;
|
|
862
988
|
}
|
|
863
989
|
|
|
990
|
+
// Stream-error codes whose retryability the HTTP status gets wrong
|
|
991
|
+
// (upstream #23069).
|
|
992
|
+
const NON_RETRYABLE_PROVIDER_CODES = new Set([
|
|
993
|
+
'insufficient_quota',
|
|
994
|
+
'usage_not_included',
|
|
995
|
+
'invalid_prompt',
|
|
996
|
+
]);
|
|
997
|
+
const RETRYABLE_PROVIDER_CODES = new Set([
|
|
998
|
+
'server_error',
|
|
999
|
+
'server_is_overloaded',
|
|
1000
|
+
'overloaded_error',
|
|
1001
|
+
'api_error',
|
|
1002
|
+
'rate_limit_error',
|
|
1003
|
+
]);
|
|
1004
|
+
|
|
1005
|
+
function parseJsonObject(input: unknown) {
|
|
1006
|
+
if (typeof input !== 'string') return;
|
|
1007
|
+
try {
|
|
1008
|
+
const parsed = JSON.parse(input);
|
|
1009
|
+
if (parsed && typeof parsed === 'object')
|
|
1010
|
+
return parsed as Record<string, any>;
|
|
1011
|
+
} catch {}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
864
1014
|
function normalizeProviderErrorObject(e: unknown) {
|
|
865
1015
|
if (!e || typeof e !== 'object') return;
|
|
866
1016
|
|
|
@@ -869,21 +1019,36 @@ export namespace MessageV2 {
|
|
|
869
1019
|
root['error'] && typeof root['error'] === 'object'
|
|
870
1020
|
? (root['error'] as Record<string, unknown>)
|
|
871
1021
|
: root;
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
candidate['
|
|
876
|
-
|
|
877
|
-
root['statusCode']
|
|
878
|
-
root['status']
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1022
|
+
// A numeric status must win over a string `code` such as "server_error",
|
|
1023
|
+
// otherwise the object is not recognised at all (upstream #23069).
|
|
1024
|
+
const statusCode = [
|
|
1025
|
+
candidate['statusCode'],
|
|
1026
|
+
candidate['status'],
|
|
1027
|
+
root['statusCode'],
|
|
1028
|
+
root['status'],
|
|
1029
|
+
candidate['code'],
|
|
1030
|
+
root['code'],
|
|
1031
|
+
]
|
|
1032
|
+
.map((value) =>
|
|
1033
|
+
typeof value === 'number'
|
|
1034
|
+
? value
|
|
1035
|
+
: typeof value === 'string' && /^\d+$/.test(value)
|
|
1036
|
+
? Number.parseInt(value, 10)
|
|
1037
|
+
: undefined
|
|
1038
|
+
)
|
|
1039
|
+
.find((value) => value !== undefined);
|
|
1040
|
+
const code = [
|
|
1041
|
+
candidate['code'],
|
|
1042
|
+
root['code'],
|
|
1043
|
+
candidate['type'],
|
|
1044
|
+
root['type'],
|
|
1045
|
+
].find(
|
|
1046
|
+
(value): value is string =>
|
|
1047
|
+
typeof value === 'string' && value !== 'error' && !/^\d+$/.test(value)
|
|
1048
|
+
);
|
|
1049
|
+
|
|
1050
|
+
if (statusCode === undefined && (code === undefined || e instanceof Error))
|
|
1051
|
+
return;
|
|
887
1052
|
|
|
888
1053
|
let responseBody: string | undefined;
|
|
889
1054
|
try {
|
|
@@ -897,17 +1062,85 @@ export namespace MessageV2 {
|
|
|
897
1062
|
? candidate['message']
|
|
898
1063
|
: typeof root['message'] === 'string'
|
|
899
1064
|
? root['message']
|
|
900
|
-
:
|
|
1065
|
+
: statusCode !== undefined
|
|
1066
|
+
? `Provider returned HTTP ${statusCode}`
|
|
1067
|
+
: code!;
|
|
1068
|
+
|
|
1069
|
+
if (code === 'context_length_exceeded' || isContextOverflow(message)) {
|
|
1070
|
+
return { overflow: true as const, message, statusCode, responseBody };
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const sdkRetryable = [root['isRetryable'], candidate['isRetryable']].find(
|
|
1074
|
+
(value): value is boolean => typeof value === 'boolean'
|
|
1075
|
+
);
|
|
1076
|
+
const isRetryable =
|
|
1077
|
+
code && NON_RETRYABLE_PROVIDER_CODES.has(code)
|
|
1078
|
+
? false
|
|
1079
|
+
: code && RETRYABLE_PROVIDER_CODES.has(code)
|
|
1080
|
+
? true
|
|
1081
|
+
: (sdkRetryable ??
|
|
1082
|
+
(statusCode === 429 ||
|
|
1083
|
+
(statusCode !== undefined && statusCode >= 500)));
|
|
901
1084
|
|
|
902
1085
|
return {
|
|
1086
|
+
overflow: false as const,
|
|
903
1087
|
message,
|
|
904
1088
|
statusCode,
|
|
905
|
-
isRetryable
|
|
906
|
-
statusCode === 429 || (statusCode >= 500 && statusCode < 600),
|
|
1089
|
+
isRetryable,
|
|
907
1090
|
responseBody,
|
|
908
1091
|
};
|
|
909
1092
|
}
|
|
910
1093
|
|
|
1094
|
+
// Adds the provider's explanation to a bare status text and keeps HTML
|
|
1095
|
+
// gateway pages out of the message (upstream #13530, #14096).
|
|
1096
|
+
function apiErrorMessage(e: APICallError) {
|
|
1097
|
+
const message = e.message;
|
|
1098
|
+
const body = e.responseBody;
|
|
1099
|
+
const statusText = e.statusCode ? STATUS_CODES[e.statusCode] : undefined;
|
|
1100
|
+
if (message === '') return (body || statusText || 'Unknown error').trim();
|
|
1101
|
+
if (!body || (statusText && message !== statusText)) return message;
|
|
1102
|
+
|
|
1103
|
+
const json = parseJsonObject(body);
|
|
1104
|
+
if (json) {
|
|
1105
|
+
const detail = [json.message, json.error?.message, json.error].find(
|
|
1106
|
+
(value): value is string => typeof value === 'string' && value !== ''
|
|
1107
|
+
);
|
|
1108
|
+
if (detail) return `${message}: ${detail}`.trim();
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
if (/^\s*<!doctype|^\s*<html/i.test(body)) {
|
|
1112
|
+
if (e.statusCode === 401)
|
|
1113
|
+
return 'Unauthorized: request was blocked by a gateway or proxy. Your authentication token may be missing or expired - try `agent auth login` to re-authenticate.';
|
|
1114
|
+
if (e.statusCode === 403)
|
|
1115
|
+
return 'Forbidden: request was blocked by a gateway or proxy. You may not have permission to access this resource - check your account and provider settings.';
|
|
1116
|
+
return message;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
return `${message}: ${body}`.trim();
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Plain errors that are really rate limits or overload (upstream retry.ts).
|
|
1123
|
+
function isRateLimitMessage(message: string) {
|
|
1124
|
+
const lower = message.toLowerCase();
|
|
1125
|
+
if (
|
|
1126
|
+
lower.includes('rate increased too quickly') ||
|
|
1127
|
+
lower.includes('rate limit') ||
|
|
1128
|
+
lower.includes('too many requests')
|
|
1129
|
+
)
|
|
1130
|
+
return true;
|
|
1131
|
+
const json = parseJsonObject(message);
|
|
1132
|
+
if (!json) return false;
|
|
1133
|
+
const code = typeof json.code === 'string' ? json.code.toLowerCase() : '';
|
|
1134
|
+
if (json.type === 'error' && json.error?.type === 'too_many_requests')
|
|
1135
|
+
return true;
|
|
1136
|
+
if (code.includes('exhausted') || code.includes('unavailable')) return true;
|
|
1137
|
+
return (
|
|
1138
|
+
json.type === 'error' &&
|
|
1139
|
+
typeof json.error?.code === 'string' &&
|
|
1140
|
+
json.error.code.includes('rate_limit')
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
911
1144
|
export function fromError(e: unknown, ctx: { providerID: string }) {
|
|
912
1145
|
const providerError = APICallError.isInstance(e)
|
|
913
1146
|
? undefined
|
|
@@ -953,23 +1186,53 @@ export namespace MessageV2 {
|
|
|
953
1186
|
},
|
|
954
1187
|
{ cause: e }
|
|
955
1188
|
).toObject();
|
|
956
|
-
case APICallError.isInstance(e):
|
|
1189
|
+
case APICallError.isInstance(e): {
|
|
1190
|
+
const message = apiErrorMessage(e);
|
|
1191
|
+
const body = parseJsonObject(e.responseBody);
|
|
1192
|
+
if (
|
|
1193
|
+
isContextOverflow(message) ||
|
|
1194
|
+
e.statusCode === 413 ||
|
|
1195
|
+
body?.error?.code === 'context_length_exceeded'
|
|
1196
|
+
) {
|
|
1197
|
+
return new MessageV2.ContextOverflowError(
|
|
1198
|
+
{ message, responseBody: e.responseBody },
|
|
1199
|
+
{ cause: e }
|
|
1200
|
+
).toObject();
|
|
1201
|
+
}
|
|
1202
|
+
const status = e.statusCode;
|
|
957
1203
|
return new MessageV2.APIError(
|
|
958
1204
|
{
|
|
959
|
-
message
|
|
960
|
-
statusCode:
|
|
1205
|
+
message,
|
|
1206
|
+
statusCode: status,
|
|
961
1207
|
// A refused connection / unresolvable host is not transient: the
|
|
962
1208
|
// AI SDK marks it retryable, but retrying it for the 7-day global
|
|
963
|
-
// budget makes the CLI hang forever (#290).
|
|
964
|
-
|
|
1209
|
+
// budget makes the CLI hang forever (#290). Any 5xx is transient
|
|
1210
|
+
// even when the SDK says otherwise (upstream #22508), and OpenAI
|
|
1211
|
+
// returns 404 for models that do exist (upstream #13579).
|
|
1212
|
+
isRetryable: isUnreachableNetworkError(e)
|
|
1213
|
+
? false
|
|
1214
|
+
: e.isRetryable ||
|
|
1215
|
+
(status !== undefined && status >= 500) ||
|
|
1216
|
+
(ctx.providerID.startsWith('openai') && status === 404),
|
|
965
1217
|
responseHeaders: e.responseHeaders,
|
|
966
1218
|
responseBody: e.responseBody,
|
|
967
1219
|
},
|
|
968
1220
|
{ cause: e }
|
|
969
1221
|
).toObject();
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1222
|
+
}
|
|
1223
|
+
case providerError !== undefined: {
|
|
1224
|
+
const normalized = providerError!;
|
|
1225
|
+
if (normalized.overflow === true) {
|
|
1226
|
+
return new MessageV2.ContextOverflowError(
|
|
1227
|
+
{
|
|
1228
|
+
message: normalized.message,
|
|
1229
|
+
responseBody: normalized.responseBody,
|
|
1230
|
+
},
|
|
1231
|
+
{ cause: e }
|
|
1232
|
+
).toObject();
|
|
1233
|
+
}
|
|
1234
|
+
const { overflow: _, ...data } = normalized;
|
|
1235
|
+
return new MessageV2.APIError(data, { cause: e }).toObject();
|
|
973
1236
|
}
|
|
974
1237
|
case e instanceof Error: {
|
|
975
1238
|
const message = e.message || e.toString();
|
|
@@ -996,6 +1259,29 @@ export namespace MessageV2 {
|
|
|
996
1259
|
{ cause: e }
|
|
997
1260
|
).toObject();
|
|
998
1261
|
}
|
|
1262
|
+
// Bun throws ZlibError when a gzip body is cut off mid-transfer
|
|
1263
|
+
// (upstream #18064).
|
|
1264
|
+
if (
|
|
1265
|
+
e.name === 'ZlibError' ||
|
|
1266
|
+
String((e as { code?: unknown }).code ?? '').startsWith('Z_')
|
|
1267
|
+
) {
|
|
1268
|
+
return new MessageV2.APIError(
|
|
1269
|
+
{ message, isRetryable: true },
|
|
1270
|
+
{ cause: e }
|
|
1271
|
+
).toObject();
|
|
1272
|
+
}
|
|
1273
|
+
if (isContextOverflow(message)) {
|
|
1274
|
+
return new MessageV2.ContextOverflowError(
|
|
1275
|
+
{ message },
|
|
1276
|
+
{ cause: e }
|
|
1277
|
+
).toObject();
|
|
1278
|
+
}
|
|
1279
|
+
if (isRateLimitMessage(message)) {
|
|
1280
|
+
return new MessageV2.APIError(
|
|
1281
|
+
{ message, isRetryable: true },
|
|
1282
|
+
{ cause: e }
|
|
1283
|
+
).toObject();
|
|
1284
|
+
}
|
|
999
1285
|
return new NamedError.Unknown(
|
|
1000
1286
|
{ message: e.toString() },
|
|
1001
1287
|
{ cause: e }
|