@moonshot-ai/kimi-code 0.23.4 → 0.23.5
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/dist/main.mjs +518 -107
- package/dist-web/assets/{CodeBlockNode-DI6gCVXa.js → CodeBlockNode-KR0ZBibg.js} +1 -1
- package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-BeZI5g5_.js → CodeBlockShell.vue_vue_type_style_index_0_lang-Bl31Ov6p.js} +1 -1
- package/dist-web/assets/{DesignSystemView-DQSXggUP.js → DesignSystemView-DU67euCs.js} +1 -1
- package/dist-web/assets/{Tooltip-B3YtzPNZ.js → Tooltip-CXVZw3LD.js} +1 -1
- package/dist-web/assets/{arc-bQMfCHSI.js → arc-D-rA9VJS.js} +1 -1
- package/dist-web/assets/{architectureDiagram-3BPJPVTR-sZkP4A5D.js → architectureDiagram-3BPJPVTR-DCwhZ0-3.js} +1 -1
- package/dist-web/assets/{blockDiagram-GPEHLZMM-Cnf-tp-z.js → blockDiagram-GPEHLZMM-7zi2-mzu.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-D1Ej05NX.js → c4Diagram-AAUBKEIU-BUhBYlbI.js} +1 -1
- package/dist-web/assets/channel-B7bRl8z3.js +1 -0
- package/dist-web/assets/{chunk-2J33WTMH-BhnDBJIi.js → chunk-2J33WTMH-DXfD8jVe.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-CiOLp8_3.js → chunk-4BX2VUAB-RnAXB6Jd.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-aUOZP4x-.js → chunk-55IACEB6-CzyvmHuE.js} +1 -1
- package/dist-web/assets/{chunk-727SXJPM-CHY14gq8.js → chunk-727SXJPM-BwBvbidm.js} +1 -1
- package/dist-web/assets/{chunk-AQP2D5EJ-BKQr3gru.js → chunk-AQP2D5EJ-DHeLjRn1.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-BNnO5ZRK.js → chunk-FMBD7UC4-Bm646OfX.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-CzE2E83w.js → chunk-ND2GUHAM-D3VSc8VX.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-BVjSv3p6.js → chunk-QZHKN3VN-BvZn7Sbo.js} +1 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-CxRebqWF.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-CxRebqWF.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-DaSWvU-5.js → cose-bilkent-S5V4N54A-DdsbNmIL.js} +1 -1
- package/dist-web/assets/{dagre-BM42HDAG-VuVwg9z6.js → dagre-BM42HDAG--3VOObN-.js} +1 -1
- package/dist-web/assets/{diagram-2AECGRRQ-BI5_WXHg.js → diagram-2AECGRRQ-DeWvFgbz.js} +1 -1
- package/dist-web/assets/{diagram-5GNKFQAL-3M89yso9.js → diagram-5GNKFQAL-DthtQYgS.js} +1 -1
- package/dist-web/assets/{diagram-KO2AKTUF-C9Qx0fJd.js → diagram-KO2AKTUF-Bt4BtRLc.js} +1 -1
- package/dist-web/assets/{diagram-LMA3HP47-yJ-bCZyw.js → diagram-LMA3HP47-7N499o4j.js} +1 -1
- package/dist-web/assets/{diagram-OG6HWLK6-CZnqkQin.js → diagram-OG6HWLK6-Br3CxOQX.js} +1 -1
- package/dist-web/assets/{erDiagram-TEJ5UH35-BQucaPqh.js → erDiagram-TEJ5UH35-BrhRvGW3.js} +1 -1
- package/dist-web/assets/{flowDiagram-I6XJVG4X-CNpuVBgN.js → flowDiagram-I6XJVG4X-D-u112mz.js} +1 -1
- package/dist-web/assets/{ganttDiagram-6RSMTGT7-KIhg0NCT.js → ganttDiagram-6RSMTGT7-DpXOgY93.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-C4YXqCii.js → gitGraphDiagram-PVQCEYII-BzV-RpYW.js} +1 -1
- package/dist-web/assets/{index-CBO7u0oz.js → index-6oRZtoED.js} +26 -26
- package/dist-web/assets/{index-CL0UgQBD.js → index-DEjpFBq9.js} +2 -2
- package/dist-web/assets/{index-v28kS3Hg.js → index-DrI3jjno.js} +1 -1
- package/dist-web/assets/{index10-RB_ZIgWs.js → index10-UVo4gjYR.js} +1 -1
- package/dist-web/assets/{index11-DyrslmWY.js → index11-m1oYWviR.js} +1 -1
- package/dist-web/assets/{index5-Bn5298gF.js → index5-B45mMYRO.js} +1 -1
- package/dist-web/assets/{index6-Di9uDNmo.js → index6-DvvrfRua.js} +1 -1
- package/dist-web/assets/{index7-CL9Ie_DH.js → index7-BUH5Peml.js} +1 -1
- package/dist-web/assets/{index8-CHlcChT0.js → index8-1BPQUZEF.js} +2 -2
- package/dist-web/assets/{infoDiagram-5YYISTIA-DTw-WQIn.js → infoDiagram-5YYISTIA-Cu7fADeQ.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-BUmTsigt.js → ishikawaDiagram-YF4QCWOH-CVvnTORb.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-B8yfgocJ.js → journeyDiagram-JHISSGLW-C-69S6wV.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-BUcF33Xd.js → kanban-definition-UN3LZRKU-pwEooP5T.js} +1 -1
- package/dist-web/assets/{linear-DY3q771w.js → linear-BQ3ylJG8.js} +1 -1
- package/dist-web/assets/{mermaid.core-D1ZdIpxL.js → mermaid.core-DcE0EhAy.js} +4 -4
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-ltEULHzt.js → mindmap-definition-RKZ34NQL-DKQ2AZ3h.js} +1 -1
- package/dist-web/assets/{pieDiagram-4H26LBE5-iCUA44fk.js → pieDiagram-4H26LBE5-CHb1kYTj.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-Zrit1Fzo.js → quadrantDiagram-W4KKPZXB-DzFGGLJt.js} +1 -1
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-n610iR9M.js → requirementDiagram-4Y6WPE33-Bk-5_Exk.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-5OEKKPKP-RQEY2tBl.js → sankeyDiagram-5OEKKPKP-CSxfPrmb.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-3UESZ5HK-QrJ3Ysl6.js → sequenceDiagram-3UESZ5HK-DHFbmk49.js} +1 -1
- package/dist-web/assets/{stateDiagram-AJRCARHV-D0mG4Tjg.js → stateDiagram-AJRCARHV-DR8SEStc.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-Cp9aas86.js +1 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-DacB6B1v.js → timeline-definition-PNZ67QCA-O5r5mf9f.js} +1 -1
- package/dist-web/assets/{vennDiagram-CIIHVFJN-CXO9WlfO.js → vennDiagram-CIIHVFJN-BRtD6E2o.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-Dj6KWBND.js → vue.runtime.esm-bundler-ClW1ucng.js} +1 -1
- package/dist-web/assets/{wardley-L42UT6IY-BbMWJCmQ.js → wardley-L42UT6IY-BqzYPkrr.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-BIcvmjmV.js → wardleyDiagram-YWT4CUSO-BXjVelBc.js} +1 -1
- package/dist-web/assets/{xychartDiagram-2RQKCTM6-CX9xzSCM.js → xychartDiagram-2RQKCTM6-Cc97QEmc.js} +1 -1
- package/dist-web/index.html +1 -1
- package/package.json +6 -6
- package/dist-web/assets/channel-UBfbidLp.js +0 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-B6r9FPXR.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-B6r9FPXR.js +0 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-CctN1EOf.js +0 -1
package/dist/main.mjs
CHANGED
|
@@ -1083,13 +1083,42 @@ var init_capability = __esmMin((() => {
|
|
|
1083
1083
|
function isRetryableGenerateError(error) {
|
|
1084
1084
|
if (error instanceof APIConnectionError$3 || error instanceof APITimeoutError) return true;
|
|
1085
1085
|
if (error instanceof APIEmptyResponseError) return true;
|
|
1086
|
-
|
|
1086
|
+
if (error instanceof APIStatusError) return [
|
|
1087
|
+
408,
|
|
1088
|
+
409,
|
|
1087
1089
|
429,
|
|
1088
1090
|
500,
|
|
1089
1091
|
502,
|
|
1090
1092
|
503,
|
|
1091
|
-
504
|
|
1093
|
+
504,
|
|
1094
|
+
529
|
|
1092
1095
|
].includes(error.statusCode);
|
|
1096
|
+
return error instanceof ChatProviderError && !isImageFormatError(error);
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Whether the provider rejected an IMAGE in the request because of its
|
|
1100
|
+
* FORMAT or DATA — an unsupported media type or undecodable image bytes.
|
|
1101
|
+
* The rejection is deterministic for a given history (the same image is
|
|
1102
|
+
* re-sent on every request, so the session would fail every turn), and the
|
|
1103
|
+
* only recovery is to resend once with all media stripped (see the
|
|
1104
|
+
* media-stripped resend in the agent loop). Body-size (413), context
|
|
1105
|
+
* overflow, image count/size limits, image-input-disabled rejections, and
|
|
1106
|
+
* non-image (audio/video) media rejections are excluded — the first two
|
|
1107
|
+
* have their own recoveries, and the rest are not fixed by stripping media.
|
|
1108
|
+
*/
|
|
1109
|
+
function isImageFormatError(error) {
|
|
1110
|
+
if (error instanceof APIStatusError) {
|
|
1111
|
+
if (error instanceof APIContextOverflowError) return false;
|
|
1112
|
+
if (error instanceof APIRequestTooLargeError) return false;
|
|
1113
|
+
if (error.statusCode !== 400) return false;
|
|
1114
|
+
const lowerMessage = error.message.toLowerCase();
|
|
1115
|
+
return IMAGE_FORMAT_STATUS_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage)) || MEDIA_TYPE_FIELD_PATTERN.test(lowerMessage) && lowerMessage.includes("image");
|
|
1116
|
+
}
|
|
1117
|
+
if (error instanceof ChatProviderError) {
|
|
1118
|
+
const lowerMessage = error.message.toLowerCase();
|
|
1119
|
+
return IMAGE_FORMAT_PROVIDER_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
|
|
1120
|
+
}
|
|
1121
|
+
return false;
|
|
1093
1122
|
}
|
|
1094
1123
|
/**
|
|
1095
1124
|
* Classify a raw (non-SDK) error message into the right transport-layer
|
|
@@ -1107,11 +1136,25 @@ function classifyBaseApiError(message) {
|
|
|
1107
1136
|
function isContextOverflowErrorCode(code) {
|
|
1108
1137
|
return code === "context_length_exceeded";
|
|
1109
1138
|
}
|
|
1110
|
-
function normalizeAPIStatusError(statusCode, message, requestId) {
|
|
1111
|
-
if (statusCode === 429) return new APIProviderRateLimitError(message, requestId);
|
|
1112
|
-
if (isContextOverflowStatusError(statusCode, message)) return new APIContextOverflowError(statusCode, message, requestId);
|
|
1113
|
-
if (isRequestTooLargeStatusError(statusCode, message)) return new APIRequestTooLargeError(statusCode, message, requestId);
|
|
1114
|
-
return new APIStatusError(statusCode, message, requestId);
|
|
1139
|
+
function normalizeAPIStatusError(statusCode, message, requestId, retryAfterMs) {
|
|
1140
|
+
if (statusCode === 429) return new APIProviderRateLimitError(message, requestId, retryAfterMs);
|
|
1141
|
+
if (isContextOverflowStatusError(statusCode, message)) return new APIContextOverflowError(statusCode, message, requestId, retryAfterMs);
|
|
1142
|
+
if (isRequestTooLargeStatusError(statusCode, message)) return new APIRequestTooLargeError(statusCode, message, requestId, retryAfterMs);
|
|
1143
|
+
return new APIStatusError(statusCode, message, requestId, retryAfterMs);
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Parse a `retry-after` response header into milliseconds. Only integer
|
|
1147
|
+
* seconds is honored; an HTTP-date (or any non-integer / missing value)
|
|
1148
|
+
* returns null and the caller falls back to its computed backoff. Shared by
|
|
1149
|
+
* the provider error converters so every backend honors the same server
|
|
1150
|
+
* backoff directive.
|
|
1151
|
+
*/
|
|
1152
|
+
function parseRetryAfterMs(headers) {
|
|
1153
|
+
const raw = headers !== null && typeof headers === "object" && typeof headers.get === "function" ? headers.get("retry-after") : null;
|
|
1154
|
+
if (raw === null || raw === void 0) return null;
|
|
1155
|
+
const seconds = Number.parseInt(raw, 10);
|
|
1156
|
+
if (!Number.isFinite(seconds) || seconds < 0) return null;
|
|
1157
|
+
return seconds * 1e3;
|
|
1115
1158
|
}
|
|
1116
1159
|
function isContextOverflowStatusError(statusCode, message) {
|
|
1117
1160
|
if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422) return false;
|
|
@@ -1163,7 +1206,7 @@ function getStatusCode(error) {
|
|
|
1163
1206
|
function errorMessage$6(error) {
|
|
1164
1207
|
return error instanceof Error ? error.message : String(error);
|
|
1165
1208
|
}
|
|
1166
|
-
var ChatProviderError, APIConnectionError$3, APITimeoutError, APIStatusError, APIContextOverflowError, APIRequestTooLargeError, APIProviderRateLimitError, APIEmptyResponseError, NETWORK_RE$1, TIMEOUT_RE$1, CONTEXT_OVERFLOW_MESSAGE_PATTERNS, PROVIDER_RATE_LIMIT_MESSAGE_PATTERNS, REQUEST_TOO_LARGE_MESSAGE_PATTERNS, TOOL_EXCHANGE_ADJACENCY_MESSAGE_PATTERNS, STRUCTURAL_REQUEST_MESSAGE_PATTERNS;
|
|
1209
|
+
var ChatProviderError, APIConnectionError$3, APITimeoutError, APIStatusError, APIContextOverflowError, APIRequestTooLargeError, APIProviderRateLimitError, APIEmptyResponseError, IMAGE_FORMAT_PROVIDER_MESSAGE_PATTERNS, IMAGE_FORMAT_STATUS_MESSAGE_PATTERNS, MEDIA_TYPE_FIELD_PATTERN, NETWORK_RE$1, TIMEOUT_RE$1, CONTEXT_OVERFLOW_MESSAGE_PATTERNS, PROVIDER_RATE_LIMIT_MESSAGE_PATTERNS, REQUEST_TOO_LARGE_MESSAGE_PATTERNS, TOOL_EXCHANGE_ADJACENCY_MESSAGE_PATTERNS, STRUCTURAL_REQUEST_MESSAGE_PATTERNS;
|
|
1167
1210
|
var init_errors$12 = __esmMin((() => {
|
|
1168
1211
|
ChatProviderError = class extends Error {
|
|
1169
1212
|
constructor(message) {
|
|
@@ -1186,28 +1229,36 @@ var init_errors$12 = __esmMin((() => {
|
|
|
1186
1229
|
APIStatusError = class extends ChatProviderError {
|
|
1187
1230
|
statusCode;
|
|
1188
1231
|
requestId;
|
|
1189
|
-
|
|
1232
|
+
/**
|
|
1233
|
+
* Server-requested backoff from the `retry-after` response header, in
|
|
1234
|
+
* milliseconds. When present, the retry loop honors it instead of its own
|
|
1235
|
+
* computed backoff — a server `Retry-After` directive overrides the local
|
|
1236
|
+
* exponential delay.
|
|
1237
|
+
*/
|
|
1238
|
+
retryAfterMs;
|
|
1239
|
+
constructor(statusCode, message, requestId, retryAfterMs) {
|
|
1190
1240
|
super(message);
|
|
1191
1241
|
this.name = "APIStatusError";
|
|
1192
1242
|
this.statusCode = statusCode;
|
|
1193
1243
|
this.requestId = requestId ?? null;
|
|
1244
|
+
this.retryAfterMs = retryAfterMs ?? null;
|
|
1194
1245
|
}
|
|
1195
1246
|
};
|
|
1196
1247
|
APIContextOverflowError = class extends APIStatusError {
|
|
1197
|
-
constructor(statusCode, message, requestId) {
|
|
1198
|
-
super(statusCode, message, requestId);
|
|
1248
|
+
constructor(statusCode, message, requestId, retryAfterMs) {
|
|
1249
|
+
super(statusCode, message, requestId, retryAfterMs);
|
|
1199
1250
|
this.name = "APIContextOverflowError";
|
|
1200
1251
|
}
|
|
1201
1252
|
};
|
|
1202
1253
|
APIRequestTooLargeError = class extends APIStatusError {
|
|
1203
|
-
constructor(statusCode, message, requestId) {
|
|
1204
|
-
super(statusCode, message, requestId);
|
|
1254
|
+
constructor(statusCode, message, requestId, retryAfterMs) {
|
|
1255
|
+
super(statusCode, message, requestId, retryAfterMs);
|
|
1205
1256
|
this.name = "APIRequestTooLargeError";
|
|
1206
1257
|
}
|
|
1207
1258
|
};
|
|
1208
1259
|
APIProviderRateLimitError = class extends APIStatusError {
|
|
1209
|
-
constructor(message, requestId) {
|
|
1210
|
-
super(429, message, requestId);
|
|
1260
|
+
constructor(message, requestId, retryAfterMs) {
|
|
1261
|
+
super(429, message, requestId, retryAfterMs);
|
|
1211
1262
|
this.name = "APIProviderRateLimitError";
|
|
1212
1263
|
}
|
|
1213
1264
|
};
|
|
@@ -1221,6 +1272,16 @@ var init_errors$12 = __esmMin((() => {
|
|
|
1221
1272
|
this.rawFinishReason = options.rawFinishReason ?? null;
|
|
1222
1273
|
}
|
|
1223
1274
|
};
|
|
1275
|
+
IMAGE_FORMAT_PROVIDER_MESSAGE_PATTERNS = [/unsupported media type for base64 image/, /invalid data url for image/];
|
|
1276
|
+
IMAGE_FORMAT_STATUS_MESSAGE_PATTERNS = [
|
|
1277
|
+
/unsupported image (?:url|format|type)/,
|
|
1278
|
+
/does not represent a valid image/,
|
|
1279
|
+
/could not (?:process|decode) (?:the |input )?image/,
|
|
1280
|
+
/unable to process (?:the |input )?image/,
|
|
1281
|
+
/failed to decode (?:the )?image/,
|
|
1282
|
+
/invalid image(?: data| type| format)?/
|
|
1283
|
+
];
|
|
1284
|
+
MEDIA_TYPE_FIELD_PATTERN = /(?:media|mime)_?type/;
|
|
1224
1285
|
NETWORK_RE$1 = /network|connection|connect|disconnect|terminated/i;
|
|
1225
1286
|
TIMEOUT_RE$1 = /timed?\s*out|timeout|deadline/i;
|
|
1226
1287
|
CONTEXT_OVERFLOW_MESSAGE_PATTERNS = [
|
|
@@ -9608,7 +9669,7 @@ function convertAnthropicError(error) {
|
|
|
9608
9669
|
if (error instanceof APIConnectionError$2) return new APIConnectionError$3(error.message);
|
|
9609
9670
|
if (error instanceof APIError$2 && typeof error.status === "number") {
|
|
9610
9671
|
const reqId = error.requestID ?? null;
|
|
9611
|
-
return normalizeAPIStatusError(error.status, error.message, reqId);
|
|
9672
|
+
return normalizeAPIStatusError(error.status, error.message, reqId, parseRetryAfterMs(error.headers));
|
|
9612
9673
|
}
|
|
9613
9674
|
if (error instanceof AnthropicError) return new ChatProviderError(`Anthropic error: ${error.message}`);
|
|
9614
9675
|
if (error instanceof Error) return classifyBaseApiError(error.message);
|
|
@@ -53199,7 +53260,7 @@ function convertOpenAIError(error) {
|
|
|
53199
53260
|
if (error instanceof APIConnectionError) return new APIConnectionError$3(error.message);
|
|
53200
53261
|
if (error instanceof APIError && typeof error.status === "number") {
|
|
53201
53262
|
const reqId = error.requestID ?? null;
|
|
53202
|
-
return normalizeAPIStatusError(error.status, error.message, reqId);
|
|
53263
|
+
return normalizeAPIStatusError(error.status, error.message, reqId, parseRetryAfterMs(error.headers));
|
|
53203
53264
|
}
|
|
53204
53265
|
if (error instanceof APIError && error.constructor === APIError && error.error === void 0) return classifyBaseApiError(error.message);
|
|
53205
53266
|
if (error instanceof OpenAIError) return new ChatProviderError(`Error: ${error.message}`);
|
|
@@ -54315,10 +54376,14 @@ function functionCallId(callId) {
|
|
|
54315
54376
|
function formatResponsesErrorEvent(code, message, param) {
|
|
54316
54377
|
return `${code ?? "unknown"}: ${message}${param === null ? "" : ` (param: ${param})`}`;
|
|
54317
54378
|
}
|
|
54379
|
+
function readEmbeddedStatusCode(message) {
|
|
54380
|
+
const match = EMBEDDED_STATUS_CODE_RE.exec(message);
|
|
54381
|
+
return match === null ? void 0 : Number(match[1]);
|
|
54382
|
+
}
|
|
54318
54383
|
function errorFromOpenAIResponsesEvent(prefix, code, message, param) {
|
|
54319
54384
|
const fullMessage = `${prefix}: ${formatResponsesErrorEvent(code, message, param)}`;
|
|
54320
54385
|
if (isContextOverflowErrorCode(code)) return new APIContextOverflowError(400, fullMessage);
|
|
54321
|
-
if (code === "rate_limit_exceeded") return new APIProviderRateLimitError(fullMessage);
|
|
54386
|
+
if (code === "rate_limit_exceeded" || readEmbeddedStatusCode(message) === 429) return new APIProviderRateLimitError(fullMessage);
|
|
54322
54387
|
return new ChatProviderError(fullMessage);
|
|
54323
54388
|
}
|
|
54324
54389
|
function parseNestedGatewayStreamError(message) {
|
|
@@ -54589,7 +54654,7 @@ function convertHistoryMessages(history, modelName, toolMessageConversion) {
|
|
|
54589
54654
|
flushPendingMedia();
|
|
54590
54655
|
return input;
|
|
54591
54656
|
}
|
|
54592
|
-
var OPENAI_RESPONSES_TOOL_CALL_ID_POLICY, OMITTED_AUDIO_PLACEHOLDER, OMITTED_VIDEO_PLACEHOLDER, OpenAIResponsesStreamedMessage, OpenAIResponsesChatProvider;
|
|
54657
|
+
var OPENAI_RESPONSES_TOOL_CALL_ID_POLICY, EMBEDDED_STATUS_CODE_RE, OMITTED_AUDIO_PLACEHOLDER, OMITTED_VIDEO_PLACEHOLDER, OpenAIResponsesStreamedMessage, OpenAIResponsesChatProvider;
|
|
54593
54658
|
var init_openai_responses = __esmMin((() => {
|
|
54594
54659
|
init_errors$12();
|
|
54595
54660
|
init_message$3();
|
|
@@ -54602,6 +54667,7 @@ var init_openai_responses = __esmMin((() => {
|
|
|
54602
54667
|
normalize: (id) => sanitizeOpenAIResponsesCallId(id, 64),
|
|
54603
54668
|
maxLength: 64
|
|
54604
54669
|
};
|
|
54670
|
+
EMBEDDED_STATUS_CODE_RE = /\bstatus_code\s*[:=]\s*(\d{3})\b/;
|
|
54605
54671
|
OMITTED_AUDIO_PLACEHOLDER = "(audio omitted: unsupported audio format)";
|
|
54606
54672
|
OMITTED_VIDEO_PLACEHOLDER = "(video omitted: not supported by this provider)";
|
|
54607
54673
|
OpenAIResponsesStreamedMessage = class {
|
|
@@ -69080,6 +69146,174 @@ var init_file_type$1 = __esmMin((() => {
|
|
|
69080
69146
|
});
|
|
69081
69147
|
}));
|
|
69082
69148
|
//#endregion
|
|
69149
|
+
//#region ../../packages/agent-core/src/tools/support/image-format-policy.ts
|
|
69150
|
+
/**
|
|
69151
|
+
* Lowercase, drop MIME parameters, and apply the `image/jpg` alias. Parameter
|
|
69152
|
+
* stripping keeps a declared media type like `image/jpeg; charset=utf-8`
|
|
69153
|
+
* consistent with a data-URL MIME token (which the parser already clips at
|
|
69154
|
+
* the first `;`), so an accepted image with parameters is treated exactly
|
|
69155
|
+
* like the bare form instead of being misread as unsupported.
|
|
69156
|
+
*/
|
|
69157
|
+
function normalizeImageMime(mimeType) {
|
|
69158
|
+
const lower = mimeType.trim().toLowerCase();
|
|
69159
|
+
const semi = lower.indexOf(";");
|
|
69160
|
+
const base = (semi === -1 ? lower : lower.slice(0, semi)).trim();
|
|
69161
|
+
return base === "image/jpg" ? "image/jpeg" : base;
|
|
69162
|
+
}
|
|
69163
|
+
/**
|
|
69164
|
+
* Decode just the prefix of a base64 payload needed for magic-byte
|
|
69165
|
+
* sniffing, without allocating the full image. `Buffer.from` never throws
|
|
69166
|
+
* on malformed base64 — it decodes what it can.
|
|
69167
|
+
*/
|
|
69168
|
+
function decodeBase64Prefix(base64) {
|
|
69169
|
+
return Buffer.from(base64.slice(0, BASE64_SNIFF_CHARS), "base64");
|
|
69170
|
+
}
|
|
69171
|
+
/**
|
|
69172
|
+
* The MIME an image should be judged by: the sniffed bytes when the magic
|
|
69173
|
+
* header is recognized (bytes are authoritative — a mislabeled image, e.g.
|
|
69174
|
+
* AVIF bytes an MCP image search tool labels `image/png`, is gated on what
|
|
69175
|
+
* it IS, because the provider decodes bytes not labels), else the declared
|
|
69176
|
+
* MIME. A header recognized as a non-image container also wins, so a video
|
|
69177
|
+
* file hiding in an image part is refused instead of trusted.
|
|
69178
|
+
*/
|
|
69179
|
+
function resolveEffectiveImageMime(declaredMime, header) {
|
|
69180
|
+
const sniffed = sniffMediaFromMagic(header);
|
|
69181
|
+
return sniffed !== null ? sniffed.mimeType : declaredMime;
|
|
69182
|
+
}
|
|
69183
|
+
/**
|
|
69184
|
+
* Whether a non-data image URL points at a format providers reject, judged
|
|
69185
|
+
* by its path extension. A best-effort heuristic for the case where there
|
|
69186
|
+
* are no bytes to sniff (a remote http(s) image): catches the common
|
|
69187
|
+
* search-tool direct link ending in `.avif`. Query string and fragment are
|
|
69188
|
+
* ignored; the match is case-insensitive. URLs without an extension, or
|
|
69189
|
+
* whose extension lies, fall through to the provider — and to the 400
|
|
69190
|
+
* recovery — unchanged.
|
|
69191
|
+
*
|
|
69192
|
+
* Returns the unsupported MIME when the extension is known and not in the
|
|
69193
|
+
* accepted set (so the notice names the right format), else null.
|
|
69194
|
+
*/
|
|
69195
|
+
function unsupportedImageMimeFromUrl(url) {
|
|
69196
|
+
let path = url;
|
|
69197
|
+
const query = path.indexOf("?");
|
|
69198
|
+
if (query !== -1) path = path.slice(0, query);
|
|
69199
|
+
const hash = path.indexOf("#");
|
|
69200
|
+
if (hash !== -1) path = path.slice(0, hash);
|
|
69201
|
+
const dot = path.lastIndexOf(".");
|
|
69202
|
+
if (dot === -1) return null;
|
|
69203
|
+
const ext = path.slice(dot).toLowerCase();
|
|
69204
|
+
const mime = ext === ".svg" ? "image/svg+xml" : IMAGE_MIME_BY_SUFFIX[ext];
|
|
69205
|
+
if (mime === void 0 || isModelAcceptedImageMime(mime)) return null;
|
|
69206
|
+
return mime;
|
|
69207
|
+
}
|
|
69208
|
+
/**
|
|
69209
|
+
* Parse an image `data:` URL into its MIME and base64 payload. The MIME is
|
|
69210
|
+
* returned raw — callers decide via {@link isModelAcceptedImageMime} and
|
|
69211
|
+
* forward {@link normalizeImageMime}. MIME parameters are tolerated and
|
|
69212
|
+
* ignored (`data:image/avif;charset=utf-8;base64,…`), so a parameter-bearing
|
|
69213
|
+
* URL cannot slip past the format gate. The scheme and `base64` marker are
|
|
69214
|
+
* matched case-insensitively (RFC 2045 encoding names are case-insensitive),
|
|
69215
|
+
* so an uppercase `;BASE64,` cannot slip past either — and since callers
|
|
69216
|
+
* rebuild to the canonical URL, the marker comes back out lowercase.
|
|
69217
|
+
* Returns null for non-data URLs (e.g. a remote http(s) image — see the
|
|
69218
|
+
* scope note in the module header).
|
|
69219
|
+
*/
|
|
69220
|
+
function parseImageDataUrl(url) {
|
|
69221
|
+
const match = /^data:([^;,]+)(?:;[^;,]+)*?;base64,(.*)$/is.exec(url);
|
|
69222
|
+
if (match === null) return null;
|
|
69223
|
+
return {
|
|
69224
|
+
mimeType: match[1],
|
|
69225
|
+
base64: match[2]
|
|
69226
|
+
};
|
|
69227
|
+
}
|
|
69228
|
+
/**
|
|
69229
|
+
* Whether a URL claims to be a `data:` URL (the scheme is case-insensitive).
|
|
69230
|
+
* Used to distinguish "failed to parse a data URL" (malformed — guaranteed
|
|
69231
|
+
* to fail at the provider) from "not a data URL" (a remote http(s) image
|
|
69232
|
+
* the provider fetches).
|
|
69233
|
+
*/
|
|
69234
|
+
function isDataUrl(url) {
|
|
69235
|
+
return url.toLowerCase().startsWith("data:");
|
|
69236
|
+
}
|
|
69237
|
+
/**
|
|
69238
|
+
* Whether an image with this MIME may be sent to the model. Only the closed
|
|
69239
|
+
* accepted set passes; everything else must be refused at the entry point —
|
|
69240
|
+
* once an unsupported `image_url` lands in the session history, every later
|
|
69241
|
+
* request in the session is rejected by the provider.
|
|
69242
|
+
*/
|
|
69243
|
+
function isModelAcceptedImageMime(mimeType) {
|
|
69244
|
+
return MODEL_ACCEPTED_IMAGE_MIMES.has(normalizeImageMime(mimeType));
|
|
69245
|
+
}
|
|
69246
|
+
/**
|
|
69247
|
+
* Refusal for an unsupported image that has a readable file path, with a
|
|
69248
|
+
* conversion command matching the execution environment (`kaos.osEnv.osKind`
|
|
69249
|
+
* — where Bash actually runs, so SSH/container sessions get the right command
|
|
69250
|
+
* too). The model can run the command through Bash (under the normal
|
|
69251
|
+
* permission flow) and read the converted file.
|
|
69252
|
+
*
|
|
69253
|
+
* macOS converts with the built-in `sips`; Linux and Windows have no built-in
|
|
69254
|
+
* decoder for these formats, so the guidance names ImageMagick (plus the
|
|
69255
|
+
* format's dedicated Linux decoder when one exists, e.g. heif-convert).
|
|
69256
|
+
*/
|
|
69257
|
+
function buildImageConversionGuidance(path, mimeType, osKind) {
|
|
69258
|
+
const converted = path.replace(/\.[^./\\]+$/, "") + ".jpg";
|
|
69259
|
+
return `"${path}" is an ${mimeType} image, which the provider does not accept. Convert it to JPEG first, then read the converted file. ` + imageConversionCommand(path, converted, osKind, UNSUPPORTED_IMAGE_FORMATS[normalizeImageMime(mimeType)]);
|
|
69260
|
+
}
|
|
69261
|
+
function imageConversionCommand(path, converted, osKind, format) {
|
|
69262
|
+
const magick = `magick "${path}" "${converted}"`;
|
|
69263
|
+
const linuxDecoder = format?.linuxDecoder;
|
|
69264
|
+
switch (osKind) {
|
|
69265
|
+
case "macOS": return `On macOS: sips -s format jpeg "${path}" --out "${converted}"`;
|
|
69266
|
+
case "Linux": return linuxDecoder === void 0 ? `On Linux, with ImageMagick: ${magick}` : `On Linux: ${linuxDecoder.command} "${path}" "${converted}" (package ${linuxDecoder.packageName}), or with ImageMagick: ${magick}`;
|
|
69267
|
+
case "Windows": return `On Windows, with ImageMagick: ${magick} (install it first if missing: winget install ImageMagick.ImageMagick)`;
|
|
69268
|
+
default: return `Options: sips -s format jpeg "${path}" --out "${converted}" (macOS)` + (linuxDecoder === void 0 ? "" : `, ${linuxDecoder.command} "${path}" "${converted}" (Linux, package ${linuxDecoder.packageName})`) + `, or ${magick} (ImageMagick)`;
|
|
69269
|
+
}
|
|
69270
|
+
}
|
|
69271
|
+
/**
|
|
69272
|
+
* Short notice standing in for an unsupported image where there is no file
|
|
69273
|
+
* path to point at (MCP tool results, prompt uploads): the image part is
|
|
69274
|
+
* dropped and this text replaces it, so the model knows what happened and
|
|
69275
|
+
* the session history stays free of formats the provider rejects.
|
|
69276
|
+
*/
|
|
69277
|
+
function buildUnsupportedImageNotice(mimeType, name) {
|
|
69278
|
+
return `[Image omitted: ${name === void 0 || name.length === 0 ? `unsupported image format ${mimeType}` : `"${name}" uses unsupported image format ${mimeType}`}. Model providers accept only ${ACCEPTED_FORMATS_TEXT} — convert it to PNG or JPEG and try again.]`;
|
|
69279
|
+
}
|
|
69280
|
+
/**
|
|
69281
|
+
* Notice standing in for an image part whose `data:` URL could not be parsed
|
|
69282
|
+
* at all (missing `;base64,` separator, empty MIME, …): the provider is
|
|
69283
|
+
* guaranteed to reject it, so it is dropped at ingestion instead of being
|
|
69284
|
+
* left to poison the session and trigger the media-stripped resend on every
|
|
69285
|
+
* later turn. The URL is truncated — a malformed payload can be huge.
|
|
69286
|
+
*/
|
|
69287
|
+
function buildMalformedImageNotice(url) {
|
|
69288
|
+
return `[Image omitted: "${url.length > 80 ? `${url.slice(0, 80)}…` : url}" is not a valid data URL (its header or payload could not be parsed). Re-encode the image as PNG or JPEG and try again.]`;
|
|
69289
|
+
}
|
|
69290
|
+
var MODEL_ACCEPTED_IMAGE_MIMES, ACCEPTED_FORMATS_TEXT, UNSUPPORTED_IMAGE_FORMATS, BASE64_SNIFF_CHARS;
|
|
69291
|
+
var init_image_format_policy = __esmMin((() => {
|
|
69292
|
+
init_file_type$1();
|
|
69293
|
+
MODEL_ACCEPTED_IMAGE_MIMES = new Set([
|
|
69294
|
+
"image/png",
|
|
69295
|
+
"image/jpeg",
|
|
69296
|
+
"image/gif",
|
|
69297
|
+
"image/webp"
|
|
69298
|
+
]);
|
|
69299
|
+
ACCEPTED_FORMATS_TEXT = "PNG, JPEG, GIF, and WebP";
|
|
69300
|
+
UNSUPPORTED_IMAGE_FORMATS = Object.freeze({
|
|
69301
|
+
"image/avif": {},
|
|
69302
|
+
"image/heic": { linuxDecoder: {
|
|
69303
|
+
command: "heif-convert",
|
|
69304
|
+
packageName: "libheif-examples"
|
|
69305
|
+
} },
|
|
69306
|
+
"image/heif": { linuxDecoder: {
|
|
69307
|
+
command: "heif-convert",
|
|
69308
|
+
packageName: "libheif-examples"
|
|
69309
|
+
} },
|
|
69310
|
+
"image/bmp": {},
|
|
69311
|
+
"image/tiff": {},
|
|
69312
|
+
"image/x-icon": {}
|
|
69313
|
+
});
|
|
69314
|
+
BASE64_SNIFF_CHARS = 48;
|
|
69315
|
+
}));
|
|
69316
|
+
//#endregion
|
|
69083
69317
|
//#region ../../node_modules/.pnpm/@jsquash+webp@1.5.0/node_modules/@jsquash/webp/codec/dec/webp_dec.js
|
|
69084
69318
|
var Module;
|
|
69085
69319
|
var init_webp_dec = __esmMin((() => {
|
|
@@ -111969,7 +112203,7 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
|
|
|
111969
112203
|
const maxEdge = options.maxEdge ?? resolveMaxImageEdgePx();
|
|
111970
112204
|
const byteBudget = options.byteBudget ?? 3932160;
|
|
111971
112205
|
const maxDecodeBytes = options.maxDecodeBytes ?? MAX_DECODE_BYTES;
|
|
111972
|
-
const normalizedMime =
|
|
112206
|
+
const normalizedMime = normalizeImageMime(mimeType);
|
|
111973
112207
|
const dims = sniffImageDimensions(bytes);
|
|
111974
112208
|
const passthrough = () => ({
|
|
111975
112209
|
data: bytes,
|
|
@@ -112031,7 +112265,10 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
|
|
|
112031
112265
|
/**
|
|
112032
112266
|
* Convenience wrapper for call sites that already hold base64 (MCP results,
|
|
112033
112267
|
* data URLs). Decodes, compresses, and re-encodes to base64. Best effort:
|
|
112034
|
-
* returns the original base64 unchanged on any failure
|
|
112268
|
+
* returns the original base64 unchanged on any failure — including formats it
|
|
112269
|
+
* cannot re-encode, so callers must refuse MIME types outside the
|
|
112270
|
+
* provider-accepted set (`isModelAcceptedImageMime`) before building an
|
|
112271
|
+
* image part from the result.
|
|
112035
112272
|
*/
|
|
112036
112273
|
async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
112037
112274
|
const startedAt = Date.now();
|
|
@@ -112052,7 +112289,7 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112052
112289
|
reportCompressEvent(options.telemetry, {
|
|
112053
112290
|
outcome: "passthrough_guard",
|
|
112054
112291
|
startedAt,
|
|
112055
|
-
inputMime:
|
|
112292
|
+
inputMime: normalizeImageMime(mimeType),
|
|
112056
112293
|
exifTransposed: false,
|
|
112057
112294
|
result
|
|
112058
112295
|
});
|
|
@@ -112076,7 +112313,7 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112076
112313
|
reportCompressEvent(options.telemetry, {
|
|
112077
112314
|
outcome: "passthrough_error",
|
|
112078
112315
|
startedAt,
|
|
112079
|
-
inputMime:
|
|
112316
|
+
inputMime: normalizeImageMime(mimeType),
|
|
112080
112317
|
exifTransposed: false,
|
|
112081
112318
|
result
|
|
112082
112319
|
});
|
|
@@ -112107,6 +112344,76 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112107
112344
|
};
|
|
112108
112345
|
}
|
|
112109
112346
|
/**
|
|
112347
|
+
* Enforce the provider-accepted image format set (see ./image-format-policy)
|
|
112348
|
+
* on a content-part list. Inline `data:` image parts whose MIME is outside
|
|
112349
|
+
* the accepted set are dropped and replaced with a text notice, so one
|
|
112350
|
+
* unsupported image cannot poison the session history. Accepted images are
|
|
112351
|
+
* forwarded only as the byte-exact canonical data URL: an alias
|
|
112352
|
+
* (`image/jpg`), case/whitespace variants, or MIME parameters
|
|
112353
|
+
* (`image/jpeg;charset=utf-8`) all rebuild to the bare canonical form,
|
|
112354
|
+
* because strict provider whitelists exact-match the full header. Remote
|
|
112355
|
+
* (non-data) image URLs and non-image parts pass through — a URL carries no
|
|
112356
|
+
* bytes to inspect.
|
|
112357
|
+
*
|
|
112358
|
+
* The BYTES are authoritative, not the declared MIME: the header of each
|
|
112359
|
+
* inline image is sniffed, and a mismatch (e.g. AVIF bytes an MCP image
|
|
112360
|
+
* search tool labels `image/png`) is gated on what the image IS — the
|
|
112361
|
+
* provider decodes bytes, not labels. When the sniffer doesn't recognize
|
|
112362
|
+
* the bytes (corrupt image, exotic container), the declared MIME stands
|
|
112363
|
+
* and the 400-recovery path remains the backstop.
|
|
112364
|
+
*
|
|
112365
|
+
* This is the format gate shared by every ingestion point; run it BEFORE
|
|
112366
|
+
* compression so unsupported bytes are never decoded.
|
|
112367
|
+
*/
|
|
112368
|
+
function gateImageFormatParts(parts) {
|
|
112369
|
+
const out = [];
|
|
112370
|
+
for (const part of parts) {
|
|
112371
|
+
if (part.type === "image_url") {
|
|
112372
|
+
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
112373
|
+
if (parsed === null) {
|
|
112374
|
+
if (isDataUrl(part.imageUrl.url)) {
|
|
112375
|
+
out.push({
|
|
112376
|
+
type: "text",
|
|
112377
|
+
text: buildMalformedImageNotice(part.imageUrl.url)
|
|
112378
|
+
});
|
|
112379
|
+
continue;
|
|
112380
|
+
}
|
|
112381
|
+
const extMime = unsupportedImageMimeFromUrl(part.imageUrl.url);
|
|
112382
|
+
if (extMime !== null) {
|
|
112383
|
+
out.push({
|
|
112384
|
+
type: "text",
|
|
112385
|
+
text: buildUnsupportedImageNotice(extMime, part.imageUrl.url)
|
|
112386
|
+
});
|
|
112387
|
+
continue;
|
|
112388
|
+
}
|
|
112389
|
+
out.push(part);
|
|
112390
|
+
continue;
|
|
112391
|
+
}
|
|
112392
|
+
const effectiveMime = resolveEffectiveImageMime(parsed.mimeType, decodeBase64Prefix(parsed.base64));
|
|
112393
|
+
if (!isModelAcceptedImageMime(effectiveMime)) {
|
|
112394
|
+
out.push({
|
|
112395
|
+
type: "text",
|
|
112396
|
+
text: buildUnsupportedImageNotice(effectiveMime)
|
|
112397
|
+
});
|
|
112398
|
+
continue;
|
|
112399
|
+
}
|
|
112400
|
+
const canonicalUrl = `data:${normalizeImageMime(effectiveMime)};base64,${parsed.base64}`;
|
|
112401
|
+
if (part.imageUrl.url !== canonicalUrl) {
|
|
112402
|
+
out.push({
|
|
112403
|
+
type: "image_url",
|
|
112404
|
+
imageUrl: {
|
|
112405
|
+
...part.imageUrl,
|
|
112406
|
+
url: canonicalUrl
|
|
112407
|
+
}
|
|
112408
|
+
});
|
|
112409
|
+
continue;
|
|
112410
|
+
}
|
|
112411
|
+
}
|
|
112412
|
+
out.push(part);
|
|
112413
|
+
}
|
|
112414
|
+
return out;
|
|
112415
|
+
}
|
|
112416
|
+
/**
|
|
112110
112417
|
* Compress any inline base64 image parts in a content-part list — used by
|
|
112111
112418
|
* the MCP tool-result path (prompt ingestion compresses per image with
|
|
112112
112419
|
* {@link compressBase64ForModel} while constructing the part). Image parts
|
|
@@ -112114,6 +112421,13 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112114
112421
|
* through, as are non-image parts. Best effort: a part that fails to
|
|
112115
112422
|
* compress is left unchanged.
|
|
112116
112423
|
*
|
|
112424
|
+
* The format gate ({@link gateImageFormatParts}) runs first: parts whose
|
|
112425
|
+
* MIME is outside the provider-accepted set are never forwarded — the part
|
|
112426
|
+
* is dropped and a text notice stands in, so one unsupported image cannot
|
|
112427
|
+
* poison the session history. This is the MCP funnel's enforcement point —
|
|
112428
|
+
* MCP servers can return any `image/*` MIME (e.g. AVIF from an image search
|
|
112429
|
+
* tool).
|
|
112430
|
+
*
|
|
112117
112431
|
* With `annotate` set, every image that was actually re-encoded gets a
|
|
112118
112432
|
* caption in {@link CompressedContentParts.captions} so the model knows it
|
|
112119
112433
|
* is looking at a downsampled copy. `annotate.persistOriginal` additionally
|
|
@@ -112125,9 +112439,9 @@ async function compressImageContentParts(parts, options = {}) {
|
|
|
112125
112439
|
const { annotate, ...compressOptions } = options;
|
|
112126
112440
|
const out = [];
|
|
112127
112441
|
const captions = [];
|
|
112128
|
-
for (const part of parts) {
|
|
112442
|
+
for (const part of gateImageFormatParts(parts)) {
|
|
112129
112443
|
if (part.type === "image_url") {
|
|
112130
|
-
const parsed = parseImageDataUrl
|
|
112444
|
+
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
112131
112445
|
if (parsed !== null) {
|
|
112132
112446
|
const result = await compressBase64ForModel(parsed.base64, parsed.mimeType, compressOptions);
|
|
112133
112447
|
if (result.changed) {
|
|
@@ -112189,7 +112503,7 @@ async function cropImageForModel(bytes, mimeType, region, options = {}) {
|
|
|
112189
112503
|
const maxEdge = options.maxEdge ?? resolveMaxImageEdgePx();
|
|
112190
112504
|
const byteBudget = options.byteBudget ?? 3932160;
|
|
112191
112505
|
const maxDecodeBytes = options.maxDecodeBytes ?? MAX_DECODE_BYTES;
|
|
112192
|
-
const normalizedMime =
|
|
112506
|
+
const normalizedMime = normalizeImageMime(mimeType);
|
|
112193
112507
|
const fail = (errorKind, error) => {
|
|
112194
112508
|
reportCropEvent(options.telemetry, {
|
|
112195
112509
|
startedAt,
|
|
@@ -112339,14 +112653,6 @@ function formatByteSize$2(bytes) {
|
|
|
112339
112653
|
if (bytes < 1024 * 1024) return `${String(Math.round(bytes / 1024))} KB`;
|
|
112340
112654
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
112341
112655
|
}
|
|
112342
|
-
function parseImageDataUrl$1(url) {
|
|
112343
|
-
const match = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
112344
|
-
if (match === null) return null;
|
|
112345
|
-
return {
|
|
112346
|
-
mimeType: match[1],
|
|
112347
|
-
base64: match[2]
|
|
112348
|
-
};
|
|
112349
|
-
}
|
|
112350
112656
|
/**
|
|
112351
112657
|
* Decode `bytes` into a jimp image. PNG/JPEG decode through jimp itself
|
|
112352
112658
|
* (which applies EXIF orientation); WebP decodes through the bundled wasm
|
|
@@ -112455,10 +112761,6 @@ function fitWithinEdge(image, edge) {
|
|
|
112455
112761
|
});
|
|
112456
112762
|
return true;
|
|
112457
112763
|
}
|
|
112458
|
-
function normalizeMime(mimeType) {
|
|
112459
|
-
const lower = mimeType.trim().toLowerCase();
|
|
112460
|
-
return lower === "image/jpg" ? "image/jpeg" : lower;
|
|
112461
|
-
}
|
|
112462
112764
|
/**
|
|
112463
112765
|
* Emit the `image_compress` event. Properties are all numeric/enum — never
|
|
112464
112766
|
* paths or content — and a throwing client is swallowed so telemetry can
|
|
@@ -112471,7 +112773,7 @@ function reportCompressEvent(telemetry, input) {
|
|
|
112471
112773
|
source: telemetry.source,
|
|
112472
112774
|
outcome: input.outcome,
|
|
112473
112775
|
input_mime: input.inputMime,
|
|
112474
|
-
output_mime:
|
|
112776
|
+
output_mime: normalizeImageMime(input.result.mimeType),
|
|
112475
112777
|
original_bytes: input.result.originalByteLength,
|
|
112476
112778
|
final_bytes: input.result.finalByteLength,
|
|
112477
112779
|
original_width: input.result.originalWidth,
|
|
@@ -112508,6 +112810,7 @@ function reportCropEvent(telemetry, input) {
|
|
|
112508
112810
|
var MAX_IMAGE_EDGE_ENV, IMAGE_BYTE_BUDGET, READ_IMAGE_BYTE_BUDGET_ENV, JPEG_QUALITY_STEPS, FALLBACK_EDGES_PX, PNG_RESCALE_FLOOR_PX, MAX_DECODE_PIXELS, MAX_DECODE_BYTES, RECODABLE_MIME, CAPTION_OPENING, CAPTION_PATTERN;
|
|
112509
112811
|
var init_image_compress = __esmMin((() => {
|
|
112510
112812
|
init_file_type$1();
|
|
112813
|
+
init_image_format_policy();
|
|
112511
112814
|
init_webp_decode();
|
|
112512
112815
|
MAX_IMAGE_EDGE_ENV = "KIMI_IMAGE_MAX_EDGE_PX";
|
|
112513
112816
|
IMAGE_BYTE_BUDGET = 3.75 * 1024 * 1024;
|
|
@@ -121073,7 +121376,7 @@ async function chatWithRetry(input) {
|
|
|
121073
121376
|
logRequestFailure(input, error, attempt, maxAttempts);
|
|
121074
121377
|
throw error;
|
|
121075
121378
|
}
|
|
121076
|
-
const delayMs = delays[attempt - 1] ?? 0;
|
|
121379
|
+
const delayMs = readRetryAfterMs(error) ?? delays[attempt - 1] ?? 0;
|
|
121077
121380
|
input.params.signal.throwIfAborted();
|
|
121078
121381
|
input.dispatchEvent({
|
|
121079
121382
|
type: "step.retrying",
|
|
@@ -121113,13 +121416,24 @@ function paramsForAttempt(input, attempt, maxAttempts) {
|
|
|
121113
121416
|
};
|
|
121114
121417
|
}
|
|
121115
121418
|
function retryBackoffDelays(maxAttempts) {
|
|
121116
|
-
|
|
121117
|
-
|
|
121118
|
-
|
|
121119
|
-
|
|
121120
|
-
|
|
121121
|
-
|
|
121122
|
-
|
|
121419
|
+
const count = Math.max(maxAttempts - 1, 0);
|
|
121420
|
+
const delays = [];
|
|
121421
|
+
for (let i = 0; i < count; i += 1) {
|
|
121422
|
+
const base = Math.min(BASE_DELAY_MS * Math.pow(RETRY_FACTOR, i), MAX_DELAY_MS);
|
|
121423
|
+
delays.push(base + Math.random() * JITTER_FACTOR * base);
|
|
121424
|
+
}
|
|
121425
|
+
return delays;
|
|
121426
|
+
}
|
|
121427
|
+
/**
|
|
121428
|
+
* Server-requested backoff carried on a kosong `APIStatusError` (parsed from
|
|
121429
|
+
* the `retry-after` response header). When present and positive it overrides
|
|
121430
|
+
* the computed backoff — a server `Retry-After` directive takes precedence
|
|
121431
|
+
* over the local exponential delay.
|
|
121432
|
+
*/
|
|
121433
|
+
function readRetryAfterMs(error) {
|
|
121434
|
+
if (typeof error !== "object" || error === null) return null;
|
|
121435
|
+
const value = error.retryAfterMs;
|
|
121436
|
+
return typeof value === "number" && value > 0 ? value : null;
|
|
121123
121437
|
}
|
|
121124
121438
|
async function sleepForRetry(delayMs, signal) {
|
|
121125
121439
|
signal.throwIfAborted();
|
|
@@ -121137,15 +121451,15 @@ function maybeStatusCode(error) {
|
|
|
121137
121451
|
const statusCode = error.statusCode;
|
|
121138
121452
|
return typeof statusCode === "number" ? statusCode : void 0;
|
|
121139
121453
|
}
|
|
121140
|
-
var
|
|
121454
|
+
var BASE_DELAY_MS, MAX_DELAY_MS, RETRY_FACTOR, JITTER_FACTOR;
|
|
121141
121455
|
var init_retry = __esmMin((() => {
|
|
121142
121456
|
init_dist$4();
|
|
121143
|
-
import_retry$1 = /* @__PURE__ */ __toESM(require_retry$4(), 1);
|
|
121144
121457
|
init_abort();
|
|
121145
121458
|
init_errors$4();
|
|
121146
|
-
|
|
121147
|
-
|
|
121459
|
+
BASE_DELAY_MS = 500;
|
|
121460
|
+
MAX_DELAY_MS = 32e3;
|
|
121148
121461
|
RETRY_FACTOR = 2;
|
|
121462
|
+
JITTER_FACTOR = .25;
|
|
121149
121463
|
}));
|
|
121150
121464
|
//#endregion
|
|
121151
121465
|
//#region ../../packages/agent-core/src/tools/support/input-schema.ts
|
|
@@ -121988,7 +122302,7 @@ var init_full = __esmMin((() => {
|
|
|
121988
122302
|
summary = extractCompactionSummary(response);
|
|
121989
122303
|
break;
|
|
121990
122304
|
} catch (error) {
|
|
121991
|
-
if (error instanceof APIRequestTooLargeError && !mediaStripAttempted) {
|
|
122305
|
+
if ((error instanceof APIRequestTooLargeError || isImageFormatError(error)) && !mediaStripAttempted) {
|
|
121992
122306
|
mediaStripAttempted = true;
|
|
121993
122307
|
const stripped = replaceMediaPartsWithMarkers(historyForModel);
|
|
121994
122308
|
if (stripped !== historyForModel) {
|
|
@@ -124015,15 +124329,17 @@ function isDegradableMediaPart(part) {
|
|
|
124015
124329
|
/**
|
|
124016
124330
|
* Replace all but the `keepRecent` most recent media parts with deterministic
|
|
124017
124331
|
* text markers. This is the media-degraded projection used to resend a request
|
|
124018
|
-
* the provider rejected as too large (HTTP 413 on accumulated base64 media)
|
|
124019
|
-
*
|
|
124020
|
-
*
|
|
124021
|
-
*
|
|
124022
|
-
*
|
|
124023
|
-
*
|
|
124024
|
-
*
|
|
124025
|
-
|
|
124026
|
-
|
|
124332
|
+
* the provider rejected as too large (HTTP 413 on accumulated base64 media)
|
|
124333
|
+
* and — with `keepRecent = 0` and `MEDIA_STRIPPED_PLACEHOLDERS` — the resend
|
|
124334
|
+
* after an image-format rejection, where the poisoned image could be anywhere
|
|
124335
|
+
* and only a full strip guarantees a clean request. A purely read-side
|
|
124336
|
+
* transform — the underlying history is left untouched — that trades pixels
|
|
124337
|
+
* for deliverability while the surrounding text (including ReadMediaFile's
|
|
124338
|
+
* `<image path="...">` wrapper) survives, so the model can re-read any file
|
|
124339
|
+
* it still needs. Untouched messages are returned by reference, and when
|
|
124340
|
+
* nothing needs degrading the input array itself is returned.
|
|
124341
|
+
*/
|
|
124342
|
+
function degradeOlderMediaParts(messages, keepRecent, placeholders = MEDIA_DEGRADED_PLACEHOLDERS) {
|
|
124027
124343
|
const mediaCount = messages.reduce((count, message) => count + message.content.filter(isDegradableMediaPart).length, 0);
|
|
124028
124344
|
let toDegrade = Math.max(0, mediaCount - keepRecent);
|
|
124029
124345
|
if (toDegrade === 0) return messages;
|
|
@@ -124034,7 +124350,7 @@ function degradeOlderMediaParts(messages, keepRecent) {
|
|
|
124034
124350
|
toDegrade -= 1;
|
|
124035
124351
|
return {
|
|
124036
124352
|
type: "text",
|
|
124037
|
-
text:
|
|
124353
|
+
text: placeholders[part.type]
|
|
124038
124354
|
};
|
|
124039
124355
|
});
|
|
124040
124356
|
return {
|
|
@@ -124043,7 +124359,7 @@ function degradeOlderMediaParts(messages, keepRecent) {
|
|
|
124043
124359
|
};
|
|
124044
124360
|
});
|
|
124045
124361
|
}
|
|
124046
|
-
var SYNTHETIC_TOOL_RESULT_TEXT, MEDIA_DEGRADED_PLACEHOLDERS;
|
|
124362
|
+
var SYNTHETIC_TOOL_RESULT_TEXT, MEDIA_DEGRADED_PLACEHOLDERS, MEDIA_STRIPPED_PLACEHOLDERS;
|
|
124047
124363
|
var init_projector = __esmMin((() => {
|
|
124048
124364
|
init_errors$8();
|
|
124049
124365
|
init_tool_result_render();
|
|
@@ -124053,6 +124369,11 @@ var init_projector = __esmMin((() => {
|
|
|
124053
124369
|
audio_url: "[audio omitted: dropped to fit the provider request size limit; re-read the file to hear it]",
|
|
124054
124370
|
video_url: "[video omitted: dropped to fit the provider request size limit; re-read the file to view it]"
|
|
124055
124371
|
};
|
|
124372
|
+
MEDIA_STRIPPED_PLACEHOLDERS = {
|
|
124373
|
+
image_url: "[image omitted: the provider rejected this image; re-read the file for conversion instructions]",
|
|
124374
|
+
audio_url: "[audio omitted: dropped along with a rejected image; re-read the file to hear it]",
|
|
124375
|
+
video_url: "[video omitted: dropped along with a rejected image; re-read the file to view it]"
|
|
124376
|
+
};
|
|
124056
124377
|
}));
|
|
124057
124378
|
//#endregion
|
|
124058
124379
|
//#region ../../packages/agent-core/src/agent/context/types.ts
|
|
@@ -124452,6 +124773,9 @@ var init_context$2 = __esmMin((() => {
|
|
|
124452
124773
|
get mediaDegradedMessages() {
|
|
124453
124774
|
return degradeOlderMediaParts(this.messages, 2);
|
|
124454
124775
|
}
|
|
124776
|
+
get mediaStrippedMessages() {
|
|
124777
|
+
return degradeOlderMediaParts(this.messages, 0, MEDIA_STRIPPED_PLACEHOLDERS);
|
|
124778
|
+
}
|
|
124455
124779
|
useProjectedHistoryFrom(source) {
|
|
124456
124780
|
this.clear();
|
|
124457
124781
|
this.pushHistory(...trimTrailingOpenToolExchange(source.project(source.history)));
|
|
@@ -137278,7 +137602,7 @@ var init_tool_call = __esmMin((() => {
|
|
|
137278
137602
|
* does not lose model usage that was already spent.
|
|
137279
137603
|
*/
|
|
137280
137604
|
async function executeLoopStep(deps) {
|
|
137281
|
-
const { turnId, signal, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, dispatchEvent, llm, tools, buildTools, describeMissingTool, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
|
|
137605
|
+
const { turnId, signal, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, buildMessagesMediaStripped, dispatchEvent, llm, tools, buildTools, describeMissingTool, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
|
|
137282
137606
|
if (hooks?.beforeStep !== void 0) {
|
|
137283
137607
|
const beforeStep = await hooks.beforeStep({
|
|
137284
137608
|
turnId,
|
|
@@ -137333,6 +137657,7 @@ async function executeLoopStep(deps) {
|
|
|
137333
137657
|
};
|
|
137334
137658
|
let response;
|
|
137335
137659
|
let mediaDegradedResendUsed = false;
|
|
137660
|
+
let mediaStrippedResendUsed = false;
|
|
137336
137661
|
try {
|
|
137337
137662
|
response = await chatWithRetry({
|
|
137338
137663
|
...retryInput,
|
|
@@ -137367,6 +137692,34 @@ async function executeLoopStep(deps) {
|
|
|
137367
137692
|
}
|
|
137368
137693
|
mediaDegradedResendUsed = true;
|
|
137369
137694
|
log?.info("recovered after media-degraded resend", { turnStep: `${turnId}.${String(currentStep)}` });
|
|
137695
|
+
} else if (buildMessagesMediaStripped !== void 0 && isImageFormatError(error)) {
|
|
137696
|
+
signal.throwIfAborted();
|
|
137697
|
+
log?.warn("provider rejected an image in the request; resending with all media stripped", {
|
|
137698
|
+
turnStep: `${turnId}.${String(currentStep)}`,
|
|
137699
|
+
model: llm.modelName
|
|
137700
|
+
});
|
|
137701
|
+
const strippedMessages = await buildMessagesMediaStripped();
|
|
137702
|
+
signal.throwIfAborted();
|
|
137703
|
+
try {
|
|
137704
|
+
response = await chatWithRetry({
|
|
137705
|
+
...retryInput,
|
|
137706
|
+
params: {
|
|
137707
|
+
...chatParams,
|
|
137708
|
+
messages: strippedMessages,
|
|
137709
|
+
requestLogFields: { projection: "media-stripped" }
|
|
137710
|
+
}
|
|
137711
|
+
});
|
|
137712
|
+
} catch (strippedError) {
|
|
137713
|
+
log?.error("media-stripped resend still rejected by provider", {
|
|
137714
|
+
turnStep: `${turnId}.${String(currentStep)}`,
|
|
137715
|
+
model: llm.modelName,
|
|
137716
|
+
originalError: errorMessage$5(error),
|
|
137717
|
+
strippedError: errorMessage$5(strippedError)
|
|
137718
|
+
});
|
|
137719
|
+
throw strippedError;
|
|
137720
|
+
}
|
|
137721
|
+
mediaStrippedResendUsed = true;
|
|
137722
|
+
log?.info("recovered after media-stripped resend", { turnStep: `${turnId}.${String(currentStep)}` });
|
|
137370
137723
|
} else if (buildMessagesStrict !== void 0 && isRecoverableRequestStructureError(error)) {
|
|
137371
137724
|
signal.throwIfAborted();
|
|
137372
137725
|
log?.warn("provider rejected request structure; resending with strict projection", {
|
|
@@ -137436,7 +137789,8 @@ async function executeLoopStep(deps) {
|
|
|
137436
137789
|
return {
|
|
137437
137790
|
usage,
|
|
137438
137791
|
stopReason: stopTurnAfterStep && effectiveStopReason === "tool_use" ? "end_turn" : effectiveStopReason,
|
|
137439
|
-
mediaDegradedResendUsed
|
|
137792
|
+
mediaDegradedResendUsed,
|
|
137793
|
+
mediaStrippedResendUsed
|
|
137440
137794
|
};
|
|
137441
137795
|
}
|
|
137442
137796
|
/**
|
|
@@ -137535,12 +137889,13 @@ var init_turn_step = __esmMin((() => {
|
|
|
137535
137889
|
//#endregion
|
|
137536
137890
|
//#region ../../packages/agent-core/src/loop/run-turn.ts
|
|
137537
137891
|
async function runTurn(input) {
|
|
137538
|
-
const { turnId, signal, llm, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, dispatchEvent, tools, buildTools, describeMissingTool, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
|
|
137892
|
+
const { turnId, signal, llm, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, buildMessagesMediaStripped, dispatchEvent, tools, buildTools, describeMissingTool, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
|
|
137539
137893
|
let usage = emptyUsage();
|
|
137540
137894
|
let steps = 0;
|
|
137541
137895
|
let stopReason = "end_turn";
|
|
137542
137896
|
let activeStep;
|
|
137543
137897
|
let mediaDegradedActive = false;
|
|
137898
|
+
let mediaStrippedActive = false;
|
|
137544
137899
|
const recordStepUsage = async (stepUsage) => {
|
|
137545
137900
|
usage = addUsage$1(usage, stepUsage);
|
|
137546
137901
|
return hostRecordStepUsage?.(stepUsage);
|
|
@@ -137554,9 +137909,10 @@ async function runTurn(input) {
|
|
|
137554
137909
|
const stepResult = await executeLoopStep({
|
|
137555
137910
|
turnId,
|
|
137556
137911
|
signal,
|
|
137557
|
-
buildMessages: mediaDegradedActive && buildMessagesMediaDegraded !== void 0 ? buildMessagesMediaDegraded : buildMessages,
|
|
137912
|
+
buildMessages: mediaStrippedActive && buildMessagesMediaStripped !== void 0 ? buildMessagesMediaStripped : mediaDegradedActive && buildMessagesMediaDegraded !== void 0 ? buildMessagesMediaDegraded : buildMessages,
|
|
137558
137913
|
buildMessagesStrict,
|
|
137559
137914
|
buildMessagesMediaDegraded,
|
|
137915
|
+
buildMessagesMediaStripped,
|
|
137560
137916
|
dispatchEvent,
|
|
137561
137917
|
llm,
|
|
137562
137918
|
tools,
|
|
@@ -137570,6 +137926,7 @@ async function runTurn(input) {
|
|
|
137570
137926
|
});
|
|
137571
137927
|
activeStep = void 0;
|
|
137572
137928
|
mediaDegradedActive = mediaDegradedActive || stepResult.mediaDegradedResendUsed === true;
|
|
137929
|
+
mediaStrippedActive = mediaStrippedActive || stepResult.mediaStrippedResendUsed === true;
|
|
137573
137930
|
if (stepResult.stopReason === "tool_use") continue;
|
|
137574
137931
|
const terminalStopReason = stepResult.stopReason;
|
|
137575
137932
|
stopReason = terminalStopReason;
|
|
@@ -143796,10 +144153,16 @@ function convertMCPContentBlock(block) {
|
|
|
143796
144153
|
}
|
|
143797
144154
|
if (block.type === "resource_link" && typeof block.uri === "string") {
|
|
143798
144155
|
const mimeType = block.mimeType ?? "application/octet-stream";
|
|
143799
|
-
if (mimeType.startsWith("image/"))
|
|
143800
|
-
|
|
143801
|
-
|
|
143802
|
-
|
|
144156
|
+
if (mimeType.startsWith("image/")) {
|
|
144157
|
+
if (!isModelAcceptedImageMime(mimeType)) return {
|
|
144158
|
+
type: "text",
|
|
144159
|
+
text: buildUnsupportedImageNotice(mimeType, block.uri)
|
|
144160
|
+
};
|
|
144161
|
+
return {
|
|
144162
|
+
type: "image_url",
|
|
144163
|
+
imageUrl: { url: block.uri }
|
|
144164
|
+
};
|
|
144165
|
+
}
|
|
143803
144166
|
if (mimeType.startsWith("audio/")) return {
|
|
143804
144167
|
type: "audio_url",
|
|
143805
144168
|
audioUrl: { url: block.uri }
|
|
@@ -143980,6 +144343,7 @@ function collapseSingleText(parts) {
|
|
|
143980
144343
|
var MCP_MAX_OUTPUT_CHARS, MCP_OUTPUT_TRUNCATED_TEXT, MCP_MAX_BINARY_PART_BYTES, MCP_MAX_BINARY_PART_CHARS;
|
|
143981
144344
|
var init_output = __esmMin((() => {
|
|
143982
144345
|
init_image_compress();
|
|
144346
|
+
init_image_format_policy();
|
|
143983
144347
|
init_image_originals();
|
|
143984
144348
|
MCP_MAX_OUTPUT_CHARS = 1e5;
|
|
143985
144349
|
MCP_OUTPUT_TRUNCATED_TEXT = `\n\n[Output truncated: exceeded ${String(MCP_MAX_OUTPUT_CHARS)} character limit. Use pagination or more specific queries to get remaining content.]`;
|
|
@@ -152113,25 +152477,6 @@ function buildMediaNote(input) {
|
|
|
152113
152477
|
parts.push("If you generate or edit images or videos via commands or scripts, read the result back immediately before continuing.");
|
|
152114
152478
|
return `<system>${parts.join(" ")}</system>`;
|
|
152115
152479
|
}
|
|
152116
|
-
/**
|
|
152117
|
-
* Refusal message for HEIC/HEIF with a conversion command matching the
|
|
152118
|
-
* execution environment (`kaos.osEnv.osKind` — where Bash actually runs, so
|
|
152119
|
-
* SSH/container sessions get the right command too). macOS converts with the
|
|
152120
|
-
* built-in `sips`; Linux and Windows have no built-in HEIC decoder, so the
|
|
152121
|
-
* guidance names the common tools and how to get them.
|
|
152122
|
-
*/
|
|
152123
|
-
function buildHeicConversionGuidance(path, mimeType, osKind) {
|
|
152124
|
-
const converted = path.replace(/\.[^./\\]+$/, "") + ".jpg";
|
|
152125
|
-
return `"${path}" is a ${mimeType} image, which the provider does not accept. Convert it to JPEG first, then read the converted file. ` + heicConversionCommand(path, converted, osKind);
|
|
152126
|
-
}
|
|
152127
|
-
function heicConversionCommand(path, converted, osKind) {
|
|
152128
|
-
switch (osKind) {
|
|
152129
|
-
case "macOS": return `On macOS: sips -s format jpeg "${path}" --out "${converted}"`;
|
|
152130
|
-
case "Linux": return `On Linux: heif-convert "${path}" "${converted}" (package libheif-examples), or with ImageMagick: magick "${path}" "${converted}"`;
|
|
152131
|
-
case "Windows": return `On Windows, with ImageMagick: magick "${path}" "${converted}" (install it first if missing: winget install ImageMagick.ImageMagick)`;
|
|
152132
|
-
default: return `Options: sips -s format jpeg "${path}" --out "${converted}" (macOS), heif-convert "${path}" "${converted}" (Linux, package libheif-examples), or magick "${path}" "${converted}" (ImageMagick)`;
|
|
152133
|
-
}
|
|
152134
|
-
}
|
|
152135
152480
|
var MAX_MEDIA_MEGABYTES, MAX_MEDIA_BYTES, ReadMediaFileInputSchema, ReadMediaFileTool;
|
|
152136
152481
|
var init_read_media = __esmMin((() => {
|
|
152137
152482
|
init_zod$1();
|
|
@@ -152140,6 +152485,7 @@ var init_read_media = __esmMin((() => {
|
|
|
152140
152485
|
init_path_access();
|
|
152141
152486
|
init_file_type$1();
|
|
152142
152487
|
init_image_compress();
|
|
152488
|
+
init_image_format_policy();
|
|
152143
152489
|
init_image_limits();
|
|
152144
152490
|
init_input_schema();
|
|
152145
152491
|
init_rule_match();
|
|
@@ -152225,9 +152571,9 @@ var init_read_media = __esmMin((() => {
|
|
|
152225
152571
|
isError: true,
|
|
152226
152572
|
output: "The current model does not support image input. Tell the user to use a model with image input capability."
|
|
152227
152573
|
};
|
|
152228
|
-
if (fileType.
|
|
152574
|
+
if (fileType.kind === "image" && !isModelAcceptedImageMime(fileType.mimeType)) return {
|
|
152229
152575
|
isError: true,
|
|
152230
|
-
output:
|
|
152576
|
+
output: buildImageConversionGuidance(args.path, fileType.mimeType, this.kaos.osEnv.osKind)
|
|
152231
152577
|
};
|
|
152232
152578
|
if (fileType.kind === "video" && !this.capabilities.video_in) return {
|
|
152233
152579
|
isError: true,
|
|
@@ -154723,6 +155069,7 @@ var init_turn = __esmMin((() => {
|
|
|
154723
155069
|
init_errors$8();
|
|
154724
155070
|
init_errors$4();
|
|
154725
155071
|
init_loop();
|
|
155072
|
+
init_image_compress();
|
|
154726
155073
|
init_abort();
|
|
154727
155074
|
init_context$2();
|
|
154728
155075
|
init_hooks();
|
|
@@ -154792,27 +155139,29 @@ var init_turn = __esmMin((() => {
|
|
|
154792
155139
|
return this.agent.homedir ? basename$2(this.agent.homedir) : this.agent.type;
|
|
154793
155140
|
}
|
|
154794
155141
|
prompt(input, origin = USER_PROMPT_ORIGIN) {
|
|
155142
|
+
const gated = gateImageFormatParts(input);
|
|
154795
155143
|
this.agent.records.logRecord({
|
|
154796
155144
|
type: "turn.prompt",
|
|
154797
|
-
input,
|
|
155145
|
+
input: gated,
|
|
154798
155146
|
origin
|
|
154799
155147
|
});
|
|
154800
|
-
return this.launch(
|
|
155148
|
+
return this.launch(gated, origin);
|
|
154801
155149
|
}
|
|
154802
155150
|
steer(input, origin = USER_PROMPT_ORIGIN) {
|
|
155151
|
+
const gated = gateImageFormatParts(input);
|
|
154803
155152
|
this.agent.records.logRecord({
|
|
154804
155153
|
type: "turn.steer",
|
|
154805
|
-
input,
|
|
155154
|
+
input: gated,
|
|
154806
155155
|
origin
|
|
154807
155156
|
});
|
|
154808
155157
|
if (this.activeTurn || this.agent.fullCompaction.isCompacting) {
|
|
154809
155158
|
this.steerBuffer.push({
|
|
154810
|
-
input,
|
|
155159
|
+
input: gated,
|
|
154811
155160
|
origin
|
|
154812
155161
|
});
|
|
154813
155162
|
return null;
|
|
154814
155163
|
}
|
|
154815
|
-
return this.launch(
|
|
155164
|
+
return this.launch(gated, origin);
|
|
154816
155165
|
}
|
|
154817
155166
|
retry(trigger) {
|
|
154818
155167
|
return this.prompt([], {
|
|
@@ -155245,6 +155594,7 @@ var init_turn = __esmMin((() => {
|
|
|
155245
155594
|
buildMessages: () => this.agent.context.messages,
|
|
155246
155595
|
buildMessagesStrict: () => this.agent.context.strictMessages,
|
|
155247
155596
|
buildMessagesMediaDegraded: () => this.agent.context.mediaDegradedMessages,
|
|
155597
|
+
buildMessagesMediaStripped: () => this.agent.context.mediaStrippedMessages,
|
|
155248
155598
|
dispatchEvent: this.buildDispatchEvent(turnId),
|
|
155249
155599
|
buildTools: () => this.agent.tools.loopTools,
|
|
155250
155600
|
describeMissingTool: (name) => this.agent.tools.missingToolMessage(name),
|
|
@@ -218888,6 +219238,7 @@ var init_src = __esmMin((() => {
|
|
|
218888
219238
|
init_context$2();
|
|
218889
219239
|
init_tool_result_render();
|
|
218890
219240
|
init_image_compress();
|
|
219241
|
+
init_image_format_policy();
|
|
218891
219242
|
init_image_limits();
|
|
218892
219243
|
init_image_originals();
|
|
218893
219244
|
init_provider_manager();
|
|
@@ -240329,6 +240680,9 @@ function isHideOutputMarker(value) {
|
|
|
240329
240680
|
* Convert an array of ACP {@link ContentBlock}s into the SDK's
|
|
240330
240681
|
* {@link PromptPart} array.
|
|
240331
240682
|
*
|
|
240683
|
+
* Image parts are built from the client-declared MIME verbatim; run the
|
|
240684
|
+
* result through {@link compressPromptImageParts} before submitting so
|
|
240685
|
+
* unsupported formats are dropped and MIME aliases canonicalized.
|
|
240332
240686
|
*/
|
|
240333
240687
|
function acpBlocksToPromptParts(blocks) {
|
|
240334
240688
|
const out = [];
|
|
@@ -240394,6 +240748,13 @@ function acpBlocksToPromptParts(blocks) {
|
|
|
240394
240748
|
* server's upload-time step. Best effort: a part that cannot be compressed is
|
|
240395
240749
|
* passed through unchanged.
|
|
240396
240750
|
*
|
|
240751
|
+
* The format gate (`gateImageFormatParts`) runs first: parts whose MIME is
|
|
240752
|
+
* outside the provider-accepted set are never forwarded — the part is
|
|
240753
|
+
* dropped and a text notice stands in, so one unsupported image cannot
|
|
240754
|
+
* poison the session history; accepted MIME aliases (`image/jpg`,
|
|
240755
|
+
* case/whitespace variants) are rewritten to the canonical form strict
|
|
240756
|
+
* provider whitelists require.
|
|
240757
|
+
*
|
|
240397
240758
|
* Compression is never silent: a re-encoded image gains a caption text part
|
|
240398
240759
|
* immediately before it stating what the original was, and the original bytes
|
|
240399
240760
|
* are persisted (into `originalsDir` — typically the session's
|
|
@@ -240402,7 +240763,7 @@ function acpBlocksToPromptParts(blocks) {
|
|
|
240402
240763
|
*/
|
|
240403
240764
|
async function compressPromptImageParts(parts, options = {}) {
|
|
240404
240765
|
const out = [];
|
|
240405
|
-
for (const part of parts) {
|
|
240766
|
+
for (const part of gateImageFormatParts(parts)) {
|
|
240406
240767
|
if (part.type === "image_url") {
|
|
240407
240768
|
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
240408
240769
|
if (parsed !== null) {
|
|
@@ -240448,14 +240809,6 @@ async function compressPromptImageParts(parts, options = {}) {
|
|
|
240448
240809
|
}
|
|
240449
240810
|
return out;
|
|
240450
240811
|
}
|
|
240451
|
-
function parseImageDataUrl(url) {
|
|
240452
|
-
const match = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
240453
|
-
if (match === null) return null;
|
|
240454
|
-
return {
|
|
240455
|
-
mimeType: match[1],
|
|
240456
|
-
base64: match[2]
|
|
240457
|
-
};
|
|
240458
|
-
}
|
|
240459
240812
|
/**
|
|
240460
240813
|
* Minimum-viable XML-attribute escaping for prompt-embedded resource
|
|
240461
240814
|
* wrappers. The output is consumed by an LLM, not parsed by a canonical
|
|
@@ -278086,7 +278439,17 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278086
278439
|
const content = [];
|
|
278087
278440
|
for (const part of body.content) {
|
|
278088
278441
|
if (part.type === "image" && part.source.kind === "base64") {
|
|
278089
|
-
const
|
|
278442
|
+
const effectiveMime = resolveEffectiveImageMime(part.source.media_type, decodeBase64Prefix(part.source.data));
|
|
278443
|
+
if (!isModelAcceptedImageMime(effectiveMime)) {
|
|
278444
|
+
content.push({
|
|
278445
|
+
type: "text",
|
|
278446
|
+
text: buildUnsupportedImageNotice(effectiveMime)
|
|
278447
|
+
});
|
|
278448
|
+
changed = true;
|
|
278449
|
+
continue;
|
|
278450
|
+
}
|
|
278451
|
+
const canonicalMime = normalizeImageMime(effectiveMime);
|
|
278452
|
+
const compressed = await compressBase64ForModel(part.source.data, canonicalMime, {
|
|
278090
278453
|
maxEdge: options.maxImageEdgePx,
|
|
278091
278454
|
telemetry: telemetryFor("prompt_inline")
|
|
278092
278455
|
});
|
|
@@ -278120,9 +278483,31 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278120
278483
|
}
|
|
278121
278484
|
});
|
|
278122
278485
|
changed = true;
|
|
278486
|
+
} else if (canonicalMime !== part.source.media_type) {
|
|
278487
|
+
content.push({
|
|
278488
|
+
...part,
|
|
278489
|
+
source: {
|
|
278490
|
+
...part.source,
|
|
278491
|
+
media_type: canonicalMime
|
|
278492
|
+
}
|
|
278493
|
+
});
|
|
278494
|
+
changed = true;
|
|
278123
278495
|
} else content.push(part);
|
|
278124
278496
|
continue;
|
|
278125
278497
|
}
|
|
278498
|
+
if (part.type === "image" && part.source.kind === "url") {
|
|
278499
|
+
const extMime = unsupportedImageMimeFromUrl(part.source.url);
|
|
278500
|
+
if (extMime !== null) {
|
|
278501
|
+
content.push({
|
|
278502
|
+
type: "text",
|
|
278503
|
+
text: buildUnsupportedImageNotice(extMime, part.source.url)
|
|
278504
|
+
});
|
|
278505
|
+
changed = true;
|
|
278506
|
+
continue;
|
|
278507
|
+
}
|
|
278508
|
+
content.push(part);
|
|
278509
|
+
continue;
|
|
278510
|
+
}
|
|
278126
278511
|
if (part.type !== "image" && part.type !== "video" || part.source.kind !== "file") {
|
|
278127
278512
|
content.push(part);
|
|
278128
278513
|
continue;
|
|
@@ -278142,6 +278527,16 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278142
278527
|
let mediaType = file.meta.media_type;
|
|
278143
278528
|
let bytes = data;
|
|
278144
278529
|
if (part.type === "image") {
|
|
278530
|
+
mediaType = resolveEffectiveImageMime(mediaType, data);
|
|
278531
|
+
if (!isModelAcceptedImageMime(mediaType)) {
|
|
278532
|
+
content.push({
|
|
278533
|
+
type: "text",
|
|
278534
|
+
text: buildUnsupportedImageNotice(mediaType, file.meta.name)
|
|
278535
|
+
});
|
|
278536
|
+
changed = true;
|
|
278537
|
+
continue;
|
|
278538
|
+
}
|
|
278539
|
+
mediaType = normalizeImageMime(mediaType);
|
|
278145
278540
|
const compressed = await compressImageForModel(data, mediaType, {
|
|
278146
278541
|
maxEdge: options.maxImageEdgePx,
|
|
278147
278542
|
telemetry: telemetryFor("prompt_file")
|
|
@@ -370621,6 +371016,7 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370621
371016
|
return;
|
|
370622
371017
|
case "turn.step.retrying":
|
|
370623
371018
|
outputWriter.discardAssistant();
|
|
371019
|
+
outputWriter.writeRetrying(event);
|
|
370624
371020
|
return;
|
|
370625
371021
|
case "assistant.delta":
|
|
370626
371022
|
outputWriter.writeAssistantDelta(event.delta);
|
|
@@ -370727,6 +371123,7 @@ var PromptTranscriptWriter = class {
|
|
|
370727
371123
|
writeToolCall() {}
|
|
370728
371124
|
writeToolCallDelta() {}
|
|
370729
371125
|
writeToolResult() {}
|
|
371126
|
+
writeRetrying() {}
|
|
370730
371127
|
flushAssistant() {
|
|
370731
371128
|
this.assistantWriter.finish();
|
|
370732
371129
|
}
|
|
@@ -370813,6 +371210,20 @@ var PromptJsonWriter = class {
|
|
|
370813
371210
|
this.assistantText = "";
|
|
370814
371211
|
this.toolCalls.length = 0;
|
|
370815
371212
|
}
|
|
371213
|
+
writeRetrying(event) {
|
|
371214
|
+
const message = {
|
|
371215
|
+
role: "meta",
|
|
371216
|
+
type: "turn.step.retrying",
|
|
371217
|
+
failed_attempt: event.failedAttempt,
|
|
371218
|
+
next_attempt: event.nextAttempt,
|
|
371219
|
+
max_attempts: event.maxAttempts,
|
|
371220
|
+
delay_ms: event.delayMs,
|
|
371221
|
+
error_name: event.errorName,
|
|
371222
|
+
error_message: event.errorMessage,
|
|
371223
|
+
status_code: event.statusCode
|
|
371224
|
+
};
|
|
371225
|
+
this.writeJsonLine(message);
|
|
371226
|
+
}
|
|
370816
371227
|
finish() {
|
|
370817
371228
|
this.flushAssistant();
|
|
370818
371229
|
}
|