@moonshot-ai/kimi-code 0.23.4 → 0.23.6
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 +1274 -663
- package/dist-web/assets/{CodeBlockNode-DI6gCVXa.js → CodeBlockNode-B5sM5iYb.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-DJ0XaWcQ.js} +1 -1
- package/dist-web/assets/DesignSystemView-CrA0fNau.js +13 -0
- package/dist-web/assets/DesignSystemView-prdQJClv.css +1 -0
- package/dist-web/assets/{Tooltip-B3YtzPNZ.js → Tooltip-BzkYFuS-.js} +1 -1
- package/dist-web/assets/{arc-bQMfCHSI.js → arc-hZy2ybJx.js} +1 -1
- package/dist-web/assets/{architectureDiagram-3BPJPVTR-sZkP4A5D.js → architectureDiagram-3BPJPVTR-Dme2VOsk.js} +1 -1
- package/dist-web/assets/{blockDiagram-GPEHLZMM-Cnf-tp-z.js → blockDiagram-GPEHLZMM-DPYxkkxc.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-D1Ej05NX.js → c4Diagram-AAUBKEIU-BaBOqczU.js} +1 -1
- package/dist-web/assets/channel--SU3IV4L.js +1 -0
- package/dist-web/assets/{chunk-2J33WTMH-BhnDBJIi.js → chunk-2J33WTMH-D12pYaz9.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-CiOLp8_3.js → chunk-4BX2VUAB-BXPJT0AD.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-aUOZP4x-.js → chunk-55IACEB6-BTJjSIMx.js} +1 -1
- package/dist-web/assets/{chunk-727SXJPM-CHY14gq8.js → chunk-727SXJPM-BorazSkv.js} +1 -1
- package/dist-web/assets/{chunk-AQP2D5EJ-BKQr3gru.js → chunk-AQP2D5EJ-3eB1WiGE.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-BNnO5ZRK.js → chunk-FMBD7UC4-BN6eq3TW.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-CzE2E83w.js → chunk-ND2GUHAM-CzHkIi39.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-BVjSv3p6.js → chunk-QZHKN3VN-CAAkvUsl.js} +1 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-Cev__UDN.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-Cev__UDN.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-DaSWvU-5.js → cose-bilkent-S5V4N54A-CO12pcZF.js} +1 -1
- package/dist-web/assets/{dagre-BM42HDAG-VuVwg9z6.js → dagre-BM42HDAG-BHHr0pRl.js} +1 -1
- package/dist-web/assets/{diagram-2AECGRRQ-BI5_WXHg.js → diagram-2AECGRRQ-OR0kYM7N.js} +1 -1
- package/dist-web/assets/{diagram-5GNKFQAL-3M89yso9.js → diagram-5GNKFQAL-BuWjKSqL.js} +1 -1
- package/dist-web/assets/{diagram-KO2AKTUF-C9Qx0fJd.js → diagram-KO2AKTUF-VPe8ZEiS.js} +1 -1
- package/dist-web/assets/{diagram-LMA3HP47-yJ-bCZyw.js → diagram-LMA3HP47-BvTXlHih.js} +1 -1
- package/dist-web/assets/{diagram-OG6HWLK6-CZnqkQin.js → diagram-OG6HWLK6-goJtZNF9.js} +1 -1
- package/dist-web/assets/{erDiagram-TEJ5UH35-BQucaPqh.js → erDiagram-TEJ5UH35-DRnuyJ1R.js} +1 -1
- package/dist-web/assets/{flowDiagram-I6XJVG4X-CNpuVBgN.js → flowDiagram-I6XJVG4X-CWAtDH7Q.js} +1 -1
- package/dist-web/assets/{ganttDiagram-6RSMTGT7-KIhg0NCT.js → ganttDiagram-6RSMTGT7-DpNy6QeS.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-C4YXqCii.js → gitGraphDiagram-PVQCEYII-D7ekpdjQ.js} +1 -1
- package/dist-web/assets/{index-v28kS3Hg.js → index-BPhahMpb.js} +1 -1
- package/dist-web/assets/index-DZAkQfN3.js +322 -0
- package/dist-web/assets/{index-CL0UgQBD.js → index-De7eXGys.js} +2 -2
- package/dist-web/assets/{index-5DXn785n.css → index-u0HjHtha.css} +1 -1
- package/dist-web/assets/{index10-RB_ZIgWs.js → index10-B4kCqL2q.js} +1 -1
- package/dist-web/assets/{index11-DyrslmWY.js → index11-CdYjVQZ3.js} +1 -1
- package/dist-web/assets/{index5-Bn5298gF.js → index5-CuGXaX2S.js} +1 -1
- package/dist-web/assets/{index6-Di9uDNmo.js → index6-Bu6r1Fdb.js} +1 -1
- package/dist-web/assets/{index7-CL9Ie_DH.js → index7-BBWbs1u4.js} +1 -1
- package/dist-web/assets/{index8-CHlcChT0.js → index8-B1e6fpQC.js} +2 -2
- package/dist-web/assets/{infoDiagram-5YYISTIA-DTw-WQIn.js → infoDiagram-5YYISTIA-DWJMi_CT.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-BUmTsigt.js → ishikawaDiagram-YF4QCWOH-DKnaDM-v.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-B8yfgocJ.js → journeyDiagram-JHISSGLW-CuTF_v7K.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-BUcF33Xd.js → kanban-definition-UN3LZRKU-7ZkgOrqm.js} +1 -1
- package/dist-web/assets/{linear-DY3q771w.js → linear-BZuIfHuU.js} +1 -1
- package/dist-web/assets/{mermaid.core-D1ZdIpxL.js → mermaid.core-BU-yVv7K.js} +4 -4
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-ltEULHzt.js → mindmap-definition-RKZ34NQL-DME_lWCk.js} +1 -1
- package/dist-web/assets/{pieDiagram-4H26LBE5-iCUA44fk.js → pieDiagram-4H26LBE5-Co1VItYc.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-Zrit1Fzo.js → quadrantDiagram-W4KKPZXB-DpswDnET.js} +1 -1
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-n610iR9M.js → requirementDiagram-4Y6WPE33-BeRdWb9K.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-5OEKKPKP-RQEY2tBl.js → sankeyDiagram-5OEKKPKP-_DYbW6pt.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-3UESZ5HK-QrJ3Ysl6.js → sequenceDiagram-3UESZ5HK-DHiijecn.js} +1 -1
- package/dist-web/assets/{stateDiagram-AJRCARHV-D0mG4Tjg.js → stateDiagram-AJRCARHV-DCZ1rX02.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-Cw0BsNEk.js +1 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-DacB6B1v.js → timeline-definition-PNZ67QCA-CUBRS-qH.js} +1 -1
- package/dist-web/assets/{vennDiagram-CIIHVFJN-CXO9WlfO.js → vennDiagram-CIIHVFJN-C98FTG2Y.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-Dj6KWBND.js → vue.runtime.esm-bundler-DPzO-Tma.js} +1 -1
- package/dist-web/assets/{wardley-L42UT6IY-BbMWJCmQ.js → wardley-L42UT6IY-Bmyzb8p9.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-BIcvmjmV.js → wardleyDiagram-YWT4CUSO-CxFNYY1a.js} +1 -1
- package/dist-web/assets/{xychartDiagram-2RQKCTM6-CX9xzSCM.js → xychartDiagram-2RQKCTM6-ySyVEQA-.js} +1 -1
- package/dist-web/index.html +2 -2
- package/package.json +5 -5
- package/dist-web/assets/DesignSystemView-B0VQ8shl.css +0 -1
- package/dist-web/assets/DesignSystemView-DQSXggUP.js +0 -13
- 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/index-CBO7u0oz.js +0 -322
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-CctN1EOf.js +0 -1
package/dist/main.mjs
CHANGED
|
@@ -1063,7 +1063,7 @@ var init_provider$1 = __esmMin((() => {}));
|
|
|
1063
1063
|
function isUnknownCapability(capability) {
|
|
1064
1064
|
if (capability === UNKNOWN_CAPABILITY) return true;
|
|
1065
1065
|
if (capability[UNKNOWN_CAPABILITY_MARKER] === true) return true;
|
|
1066
|
-
return !capability.image_in && !capability.video_in && !capability.audio_in && !capability.thinking && !capability.tool_use && capability.
|
|
1066
|
+
return !capability.image_in && !capability.video_in && !capability.audio_in && !capability.thinking && !capability.tool_use && capability.dynamically_loaded_tools !== true && capability.max_context_tokens === 0;
|
|
1067
1067
|
}
|
|
1068
1068
|
var UNKNOWN_CAPABILITY_MARKER, UNKNOWN_CAPABILITY;
|
|
1069
1069
|
var init_capability = __esmMin((() => {
|
|
@@ -1075,7 +1075,7 @@ var init_capability = __esmMin((() => {
|
|
|
1075
1075
|
thinking: false,
|
|
1076
1076
|
tool_use: false,
|
|
1077
1077
|
max_context_tokens: 0,
|
|
1078
|
-
|
|
1078
|
+
dynamically_loaded_tools: false
|
|
1079
1079
|
}, UNKNOWN_CAPABILITY_MARKER, { value: true }));
|
|
1080
1080
|
}));
|
|
1081
1081
|
//#endregion
|
|
@@ -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 {
|
|
@@ -55051,7 +55117,7 @@ function catalogModelToCapability(model) {
|
|
|
55051
55117
|
thinking: Boolean(model.reasoning),
|
|
55052
55118
|
tool_use: model.tool_call ?? true,
|
|
55053
55119
|
max_context_tokens: context,
|
|
55054
|
-
|
|
55120
|
+
dynamically_loaded_tools: model.dynamically_loaded_tools === true
|
|
55055
55121
|
}
|
|
55056
55122
|
};
|
|
55057
55123
|
}
|
|
@@ -61941,7 +62007,7 @@ function isValidPermissionPattern(pattern) {
|
|
|
61941
62007
|
return false;
|
|
61942
62008
|
}
|
|
61943
62009
|
}
|
|
61944
|
-
var ProviderTypeSchema, OAuthRefSchema, StringRecordSchema, ProviderConfigSchema, ModelAliasBaseSchema, ModelAliasOverrideSchema, ModelAliasSchema, ThinkingConfigSchema, PermissionModeSchema, PermissionRuleDecisionSchema, PermissionRuleScopeSchema, PermissionRuleSchema, PermissionConfigSchema, LoopControlSchema, BackgroundConfigSchema, ImageConfigSchema, ModelCatalogConfigSchema, ExperimentalConfigSchema, HookDefSchema, MoonshotServiceConfigSchema, ServicesConfigSchema, McpServerCommonFields, McpServerStdioConfigSchema, McpServerHttpConfigSchema, McpServerSseConfigSchema, McpServerConfigDiscriminatedSchema, McpServerConfigSchema, KimiConfigSchema, ProviderConfigPatchSchema, ModelAliasPatchSchema, ThinkingConfigPatchSchema, PermissionConfigPatchSchema, LoopControlPatchSchema, BackgroundConfigPatchSchema, ImageConfigPatchSchema, ModelCatalogConfigPatchSchema, ExperimentalConfigPatchSchema, MoonshotServiceConfigPatchSchema, ServicesConfigPatchSchema, KimiConfigPatchSchema;
|
|
62010
|
+
var ProviderTypeSchema, OAuthRefSchema, StringRecordSchema, ProviderConfigSchema, ModelAliasBaseSchema, ModelAliasOverrideSchema, ModelAliasSchema, ThinkingConfigSchema, PermissionModeSchema, PermissionRuleDecisionSchema, PermissionRuleScopeSchema, PermissionRuleSchema, PermissionConfigSchema, LoopControlSchema, BackgroundConfigSchema, SubagentConfigSchema, ImageConfigSchema, ModelCatalogConfigSchema, ExperimentalConfigSchema, HookDefSchema, MoonshotServiceConfigSchema, ServicesConfigSchema, McpServerCommonFields, McpServerStdioConfigSchema, McpServerHttpConfigSchema, McpServerSseConfigSchema, McpServerConfigDiscriminatedSchema, McpServerConfigSchema, KimiConfigSchema, ProviderConfigPatchSchema, ModelAliasPatchSchema, ThinkingConfigPatchSchema, PermissionConfigPatchSchema, LoopControlPatchSchema, BackgroundConfigPatchSchema, SubagentConfigPatchSchema, ImageConfigPatchSchema, ModelCatalogConfigPatchSchema, ExperimentalConfigPatchSchema, MoonshotServiceConfigPatchSchema, ServicesConfigPatchSchema, KimiConfigPatchSchema;
|
|
61945
62011
|
var init_schema = __esmMin((() => {
|
|
61946
62012
|
init_types$18();
|
|
61947
62013
|
init_matches_rule();
|
|
@@ -62031,8 +62097,15 @@ var init_schema = __esmMin((() => {
|
|
|
62031
62097
|
maxRunningTasks: number$1().int().min(1).optional(),
|
|
62032
62098
|
keepAliveOnExit: boolean$1().optional(),
|
|
62033
62099
|
killGracePeriodMs: number$1().int().min(0).optional(),
|
|
62034
|
-
printWaitCeilingS: number$1().int().min(1).optional()
|
|
62100
|
+
printWaitCeilingS: number$1().int().min(1).optional(),
|
|
62101
|
+
printBackgroundMode: _enum([
|
|
62102
|
+
"exit",
|
|
62103
|
+
"drain",
|
|
62104
|
+
"steer"
|
|
62105
|
+
]).optional(),
|
|
62106
|
+
printMaxTurns: number$1().int().min(1).optional()
|
|
62035
62107
|
});
|
|
62108
|
+
SubagentConfigSchema = object({ timeoutMs: number$1().int().min(1).optional() });
|
|
62036
62109
|
ImageConfigSchema = object({
|
|
62037
62110
|
/**
|
|
62038
62111
|
* Longest-edge ceiling (px) applied when compressing images for the model.
|
|
@@ -62138,6 +62211,7 @@ var init_schema = __esmMin((() => {
|
|
|
62138
62211
|
extraSkillDirs: array(string()).optional(),
|
|
62139
62212
|
loopControl: LoopControlSchema.optional(),
|
|
62140
62213
|
background: BackgroundConfigSchema.optional(),
|
|
62214
|
+
subagent: SubagentConfigSchema.optional(),
|
|
62141
62215
|
image: ImageConfigSchema.optional(),
|
|
62142
62216
|
modelCatalog: ModelCatalogConfigSchema.optional(),
|
|
62143
62217
|
experimental: ExperimentalConfigSchema.optional(),
|
|
@@ -62150,6 +62224,7 @@ var init_schema = __esmMin((() => {
|
|
|
62150
62224
|
PermissionConfigPatchSchema = PermissionConfigSchema.partial();
|
|
62151
62225
|
LoopControlPatchSchema = LoopControlSchema.partial();
|
|
62152
62226
|
BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
|
|
62227
|
+
SubagentConfigPatchSchema = SubagentConfigSchema.partial();
|
|
62153
62228
|
ImageConfigPatchSchema = ImageConfigSchema.partial();
|
|
62154
62229
|
ModelCatalogConfigPatchSchema = ModelCatalogConfigSchema.partial();
|
|
62155
62230
|
ExperimentalConfigPatchSchema = ExperimentalConfigSchema;
|
|
@@ -62175,6 +62250,7 @@ var init_schema = __esmMin((() => {
|
|
|
62175
62250
|
extraSkillDirs: array(string()).optional(),
|
|
62176
62251
|
loopControl: LoopControlPatchSchema.optional(),
|
|
62177
62252
|
background: BackgroundConfigPatchSchema.optional(),
|
|
62253
|
+
subagent: SubagentConfigPatchSchema.optional(),
|
|
62178
62254
|
image: ImageConfigPatchSchema.optional(),
|
|
62179
62255
|
modelCatalog: ModelCatalogConfigPatchSchema.optional(),
|
|
62180
62256
|
experimental: ExperimentalConfigPatchSchema.optional(),
|
|
@@ -63678,6 +63754,7 @@ function transformTomlData(data) {
|
|
|
63678
63754
|
else if (targetKey === "background" && isPlainObject$2(value)) result[targetKey] = transformPlainObject(value);
|
|
63679
63755
|
else if (targetKey === "image" && isPlainObject$2(value)) result[targetKey] = transformPlainObject(value);
|
|
63680
63756
|
else if (targetKey === "experimental" && isPlainObject$2(value)) result[targetKey] = cloneRecord$2(value);
|
|
63757
|
+
else if (targetKey === "subagent" && isPlainObject$2(value)) result[targetKey] = transformPlainObject(value);
|
|
63681
63758
|
else if (!isPlainObject$2(value)) result[targetKey] = value;
|
|
63682
63759
|
}
|
|
63683
63760
|
return result;
|
|
@@ -63788,6 +63865,7 @@ function configToTomlData(config) {
|
|
|
63788
63865
|
setSection(out, "services", config.services, servicesToToml);
|
|
63789
63866
|
setSection(out, "loop_control", config.loopControl, loopControlToToml);
|
|
63790
63867
|
setSection(out, "background", config.background, backgroundToToml);
|
|
63868
|
+
setSection(out, "subagent", config.subagent, subagentToToml);
|
|
63791
63869
|
setSection(out, "image", config.image, imageToToml);
|
|
63792
63870
|
setSection(out, "experimental", config.experimental, experimentalToToml);
|
|
63793
63871
|
setSection(out, "permission", config.permission, permissionToToml);
|
|
@@ -63879,6 +63957,11 @@ function backgroundToToml(background, rawBackground) {
|
|
|
63879
63957
|
for (const [key, value] of Object.entries(background)) setDefined(out, camelToSnake$3(key), value);
|
|
63880
63958
|
return out;
|
|
63881
63959
|
}
|
|
63960
|
+
function subagentToToml(subagent, rawSubagent) {
|
|
63961
|
+
const out = cloneRecord$2(rawSubagent);
|
|
63962
|
+
for (const [key, value] of Object.entries(subagent)) setDefined(out, camelToSnake$3(key), value);
|
|
63963
|
+
return out;
|
|
63964
|
+
}
|
|
63882
63965
|
function imageToToml(image, rawImage) {
|
|
63883
63966
|
const out = cloneRecord$2(rawImage);
|
|
63884
63967
|
for (const [key, value] of Object.entries(image)) setDefined(out, camelToSnake$3(key), value);
|
|
@@ -68564,7 +68647,7 @@ var init_registry$1 = __esmMin((() => {
|
|
|
68564
68647
|
FLAG_DEFINITIONS = [{
|
|
68565
68648
|
id: "tool-select",
|
|
68566
68649
|
title: "Tool select (progressive tool disclosure)",
|
|
68567
|
-
description: "Keep MCP tool schemas out of the immutable top-level tools[]; the model loads them on demand via the select_tools tool. Only takes effect on models whose capability catalog declares
|
|
68650
|
+
description: "Keep MCP tool schemas out of the immutable top-level tools[]; the model loads them on demand via the select_tools tool. Only takes effect on models whose capability catalog declares dynamically loaded tools.",
|
|
68568
68651
|
env: "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT",
|
|
68569
68652
|
default: false,
|
|
68570
68653
|
surface: "core"
|
|
@@ -69080,6 +69163,174 @@ var init_file_type$1 = __esmMin((() => {
|
|
|
69080
69163
|
});
|
|
69081
69164
|
}));
|
|
69082
69165
|
//#endregion
|
|
69166
|
+
//#region ../../packages/agent-core/src/tools/support/image-format-policy.ts
|
|
69167
|
+
/**
|
|
69168
|
+
* Lowercase, drop MIME parameters, and apply the `image/jpg` alias. Parameter
|
|
69169
|
+
* stripping keeps a declared media type like `image/jpeg; charset=utf-8`
|
|
69170
|
+
* consistent with a data-URL MIME token (which the parser already clips at
|
|
69171
|
+
* the first `;`), so an accepted image with parameters is treated exactly
|
|
69172
|
+
* like the bare form instead of being misread as unsupported.
|
|
69173
|
+
*/
|
|
69174
|
+
function normalizeImageMime(mimeType) {
|
|
69175
|
+
const lower = mimeType.trim().toLowerCase();
|
|
69176
|
+
const semi = lower.indexOf(";");
|
|
69177
|
+
const base = (semi === -1 ? lower : lower.slice(0, semi)).trim();
|
|
69178
|
+
return base === "image/jpg" ? "image/jpeg" : base;
|
|
69179
|
+
}
|
|
69180
|
+
/**
|
|
69181
|
+
* Decode just the prefix of a base64 payload needed for magic-byte
|
|
69182
|
+
* sniffing, without allocating the full image. `Buffer.from` never throws
|
|
69183
|
+
* on malformed base64 — it decodes what it can.
|
|
69184
|
+
*/
|
|
69185
|
+
function decodeBase64Prefix(base64) {
|
|
69186
|
+
return Buffer.from(base64.slice(0, BASE64_SNIFF_CHARS), "base64");
|
|
69187
|
+
}
|
|
69188
|
+
/**
|
|
69189
|
+
* The MIME an image should be judged by: the sniffed bytes when the magic
|
|
69190
|
+
* header is recognized (bytes are authoritative — a mislabeled image, e.g.
|
|
69191
|
+
* AVIF bytes an MCP image search tool labels `image/png`, is gated on what
|
|
69192
|
+
* it IS, because the provider decodes bytes not labels), else the declared
|
|
69193
|
+
* MIME. A header recognized as a non-image container also wins, so a video
|
|
69194
|
+
* file hiding in an image part is refused instead of trusted.
|
|
69195
|
+
*/
|
|
69196
|
+
function resolveEffectiveImageMime(declaredMime, header) {
|
|
69197
|
+
const sniffed = sniffMediaFromMagic(header);
|
|
69198
|
+
return sniffed !== null ? sniffed.mimeType : declaredMime;
|
|
69199
|
+
}
|
|
69200
|
+
/**
|
|
69201
|
+
* Whether a non-data image URL points at a format providers reject, judged
|
|
69202
|
+
* by its path extension. A best-effort heuristic for the case where there
|
|
69203
|
+
* are no bytes to sniff (a remote http(s) image): catches the common
|
|
69204
|
+
* search-tool direct link ending in `.avif`. Query string and fragment are
|
|
69205
|
+
* ignored; the match is case-insensitive. URLs without an extension, or
|
|
69206
|
+
* whose extension lies, fall through to the provider — and to the 400
|
|
69207
|
+
* recovery — unchanged.
|
|
69208
|
+
*
|
|
69209
|
+
* Returns the unsupported MIME when the extension is known and not in the
|
|
69210
|
+
* accepted set (so the notice names the right format), else null.
|
|
69211
|
+
*/
|
|
69212
|
+
function unsupportedImageMimeFromUrl(url) {
|
|
69213
|
+
let path = url;
|
|
69214
|
+
const query = path.indexOf("?");
|
|
69215
|
+
if (query !== -1) path = path.slice(0, query);
|
|
69216
|
+
const hash = path.indexOf("#");
|
|
69217
|
+
if (hash !== -1) path = path.slice(0, hash);
|
|
69218
|
+
const dot = path.lastIndexOf(".");
|
|
69219
|
+
if (dot === -1) return null;
|
|
69220
|
+
const ext = path.slice(dot).toLowerCase();
|
|
69221
|
+
const mime = ext === ".svg" ? "image/svg+xml" : IMAGE_MIME_BY_SUFFIX[ext];
|
|
69222
|
+
if (mime === void 0 || isModelAcceptedImageMime(mime)) return null;
|
|
69223
|
+
return mime;
|
|
69224
|
+
}
|
|
69225
|
+
/**
|
|
69226
|
+
* Parse an image `data:` URL into its MIME and base64 payload. The MIME is
|
|
69227
|
+
* returned raw — callers decide via {@link isModelAcceptedImageMime} and
|
|
69228
|
+
* forward {@link normalizeImageMime}. MIME parameters are tolerated and
|
|
69229
|
+
* ignored (`data:image/avif;charset=utf-8;base64,…`), so a parameter-bearing
|
|
69230
|
+
* URL cannot slip past the format gate. The scheme and `base64` marker are
|
|
69231
|
+
* matched case-insensitively (RFC 2045 encoding names are case-insensitive),
|
|
69232
|
+
* so an uppercase `;BASE64,` cannot slip past either — and since callers
|
|
69233
|
+
* rebuild to the canonical URL, the marker comes back out lowercase.
|
|
69234
|
+
* Returns null for non-data URLs (e.g. a remote http(s) image — see the
|
|
69235
|
+
* scope note in the module header).
|
|
69236
|
+
*/
|
|
69237
|
+
function parseImageDataUrl(url) {
|
|
69238
|
+
const match = /^data:([^;,]+)(?:;[^;,]+)*?;base64,(.*)$/is.exec(url);
|
|
69239
|
+
if (match === null) return null;
|
|
69240
|
+
return {
|
|
69241
|
+
mimeType: match[1],
|
|
69242
|
+
base64: match[2]
|
|
69243
|
+
};
|
|
69244
|
+
}
|
|
69245
|
+
/**
|
|
69246
|
+
* Whether a URL claims to be a `data:` URL (the scheme is case-insensitive).
|
|
69247
|
+
* Used to distinguish "failed to parse a data URL" (malformed — guaranteed
|
|
69248
|
+
* to fail at the provider) from "not a data URL" (a remote http(s) image
|
|
69249
|
+
* the provider fetches).
|
|
69250
|
+
*/
|
|
69251
|
+
function isDataUrl(url) {
|
|
69252
|
+
return url.toLowerCase().startsWith("data:");
|
|
69253
|
+
}
|
|
69254
|
+
/**
|
|
69255
|
+
* Whether an image with this MIME may be sent to the model. Only the closed
|
|
69256
|
+
* accepted set passes; everything else must be refused at the entry point —
|
|
69257
|
+
* once an unsupported `image_url` lands in the session history, every later
|
|
69258
|
+
* request in the session is rejected by the provider.
|
|
69259
|
+
*/
|
|
69260
|
+
function isModelAcceptedImageMime(mimeType) {
|
|
69261
|
+
return MODEL_ACCEPTED_IMAGE_MIMES.has(normalizeImageMime(mimeType));
|
|
69262
|
+
}
|
|
69263
|
+
/**
|
|
69264
|
+
* Refusal for an unsupported image that has a readable file path, with a
|
|
69265
|
+
* conversion command matching the execution environment (`kaos.osEnv.osKind`
|
|
69266
|
+
* — where Bash actually runs, so SSH/container sessions get the right command
|
|
69267
|
+
* too). The model can run the command through Bash (under the normal
|
|
69268
|
+
* permission flow) and read the converted file.
|
|
69269
|
+
*
|
|
69270
|
+
* macOS converts with the built-in `sips`; Linux and Windows have no built-in
|
|
69271
|
+
* decoder for these formats, so the guidance names ImageMagick (plus the
|
|
69272
|
+
* format's dedicated Linux decoder when one exists, e.g. heif-convert).
|
|
69273
|
+
*/
|
|
69274
|
+
function buildImageConversionGuidance(path, mimeType, osKind) {
|
|
69275
|
+
const converted = path.replace(/\.[^./\\]+$/, "") + ".jpg";
|
|
69276
|
+
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)]);
|
|
69277
|
+
}
|
|
69278
|
+
function imageConversionCommand(path, converted, osKind, format) {
|
|
69279
|
+
const magick = `magick "${path}" "${converted}"`;
|
|
69280
|
+
const linuxDecoder = format?.linuxDecoder;
|
|
69281
|
+
switch (osKind) {
|
|
69282
|
+
case "macOS": return `On macOS: sips -s format jpeg "${path}" --out "${converted}"`;
|
|
69283
|
+
case "Linux": return linuxDecoder === void 0 ? `On Linux, with ImageMagick: ${magick}` : `On Linux: ${linuxDecoder.command} "${path}" "${converted}" (package ${linuxDecoder.packageName}), or with ImageMagick: ${magick}`;
|
|
69284
|
+
case "Windows": return `On Windows, with ImageMagick: ${magick} (install it first if missing: winget install ImageMagick.ImageMagick)`;
|
|
69285
|
+
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)`;
|
|
69286
|
+
}
|
|
69287
|
+
}
|
|
69288
|
+
/**
|
|
69289
|
+
* Short notice standing in for an unsupported image where there is no file
|
|
69290
|
+
* path to point at (MCP tool results, prompt uploads): the image part is
|
|
69291
|
+
* dropped and this text replaces it, so the model knows what happened and
|
|
69292
|
+
* the session history stays free of formats the provider rejects.
|
|
69293
|
+
*/
|
|
69294
|
+
function buildUnsupportedImageNotice(mimeType, name) {
|
|
69295
|
+
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.]`;
|
|
69296
|
+
}
|
|
69297
|
+
/**
|
|
69298
|
+
* Notice standing in for an image part whose `data:` URL could not be parsed
|
|
69299
|
+
* at all (missing `;base64,` separator, empty MIME, …): the provider is
|
|
69300
|
+
* guaranteed to reject it, so it is dropped at ingestion instead of being
|
|
69301
|
+
* left to poison the session and trigger the media-stripped resend on every
|
|
69302
|
+
* later turn. The URL is truncated — a malformed payload can be huge.
|
|
69303
|
+
*/
|
|
69304
|
+
function buildMalformedImageNotice(url) {
|
|
69305
|
+
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.]`;
|
|
69306
|
+
}
|
|
69307
|
+
var MODEL_ACCEPTED_IMAGE_MIMES, ACCEPTED_FORMATS_TEXT, UNSUPPORTED_IMAGE_FORMATS, BASE64_SNIFF_CHARS;
|
|
69308
|
+
var init_image_format_policy = __esmMin((() => {
|
|
69309
|
+
init_file_type$1();
|
|
69310
|
+
MODEL_ACCEPTED_IMAGE_MIMES = new Set([
|
|
69311
|
+
"image/png",
|
|
69312
|
+
"image/jpeg",
|
|
69313
|
+
"image/gif",
|
|
69314
|
+
"image/webp"
|
|
69315
|
+
]);
|
|
69316
|
+
ACCEPTED_FORMATS_TEXT = "PNG, JPEG, GIF, and WebP";
|
|
69317
|
+
UNSUPPORTED_IMAGE_FORMATS = Object.freeze({
|
|
69318
|
+
"image/avif": {},
|
|
69319
|
+
"image/heic": { linuxDecoder: {
|
|
69320
|
+
command: "heif-convert",
|
|
69321
|
+
packageName: "libheif-examples"
|
|
69322
|
+
} },
|
|
69323
|
+
"image/heif": { linuxDecoder: {
|
|
69324
|
+
command: "heif-convert",
|
|
69325
|
+
packageName: "libheif-examples"
|
|
69326
|
+
} },
|
|
69327
|
+
"image/bmp": {},
|
|
69328
|
+
"image/tiff": {},
|
|
69329
|
+
"image/x-icon": {}
|
|
69330
|
+
});
|
|
69331
|
+
BASE64_SNIFF_CHARS = 48;
|
|
69332
|
+
}));
|
|
69333
|
+
//#endregion
|
|
69083
69334
|
//#region ../../node_modules/.pnpm/@jsquash+webp@1.5.0/node_modules/@jsquash/webp/codec/dec/webp_dec.js
|
|
69084
69335
|
var Module;
|
|
69085
69336
|
var init_webp_dec = __esmMin((() => {
|
|
@@ -111969,7 +112220,7 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
|
|
|
111969
112220
|
const maxEdge = options.maxEdge ?? resolveMaxImageEdgePx();
|
|
111970
112221
|
const byteBudget = options.byteBudget ?? 3932160;
|
|
111971
112222
|
const maxDecodeBytes = options.maxDecodeBytes ?? MAX_DECODE_BYTES;
|
|
111972
|
-
const normalizedMime =
|
|
112223
|
+
const normalizedMime = normalizeImageMime(mimeType);
|
|
111973
112224
|
const dims = sniffImageDimensions(bytes);
|
|
111974
112225
|
const passthrough = () => ({
|
|
111975
112226
|
data: bytes,
|
|
@@ -112031,7 +112282,10 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
|
|
|
112031
112282
|
/**
|
|
112032
112283
|
* Convenience wrapper for call sites that already hold base64 (MCP results,
|
|
112033
112284
|
* data URLs). Decodes, compresses, and re-encodes to base64. Best effort:
|
|
112034
|
-
* returns the original base64 unchanged on any failure
|
|
112285
|
+
* returns the original base64 unchanged on any failure — including formats it
|
|
112286
|
+
* cannot re-encode, so callers must refuse MIME types outside the
|
|
112287
|
+
* provider-accepted set (`isModelAcceptedImageMime`) before building an
|
|
112288
|
+
* image part from the result.
|
|
112035
112289
|
*/
|
|
112036
112290
|
async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
112037
112291
|
const startedAt = Date.now();
|
|
@@ -112052,7 +112306,7 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112052
112306
|
reportCompressEvent(options.telemetry, {
|
|
112053
112307
|
outcome: "passthrough_guard",
|
|
112054
112308
|
startedAt,
|
|
112055
|
-
inputMime:
|
|
112309
|
+
inputMime: normalizeImageMime(mimeType),
|
|
112056
112310
|
exifTransposed: false,
|
|
112057
112311
|
result
|
|
112058
112312
|
});
|
|
@@ -112076,7 +112330,7 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112076
112330
|
reportCompressEvent(options.telemetry, {
|
|
112077
112331
|
outcome: "passthrough_error",
|
|
112078
112332
|
startedAt,
|
|
112079
|
-
inputMime:
|
|
112333
|
+
inputMime: normalizeImageMime(mimeType),
|
|
112080
112334
|
exifTransposed: false,
|
|
112081
112335
|
result
|
|
112082
112336
|
});
|
|
@@ -112107,6 +112361,76 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112107
112361
|
};
|
|
112108
112362
|
}
|
|
112109
112363
|
/**
|
|
112364
|
+
* Enforce the provider-accepted image format set (see ./image-format-policy)
|
|
112365
|
+
* on a content-part list. Inline `data:` image parts whose MIME is outside
|
|
112366
|
+
* the accepted set are dropped and replaced with a text notice, so one
|
|
112367
|
+
* unsupported image cannot poison the session history. Accepted images are
|
|
112368
|
+
* forwarded only as the byte-exact canonical data URL: an alias
|
|
112369
|
+
* (`image/jpg`), case/whitespace variants, or MIME parameters
|
|
112370
|
+
* (`image/jpeg;charset=utf-8`) all rebuild to the bare canonical form,
|
|
112371
|
+
* because strict provider whitelists exact-match the full header. Remote
|
|
112372
|
+
* (non-data) image URLs and non-image parts pass through — a URL carries no
|
|
112373
|
+
* bytes to inspect.
|
|
112374
|
+
*
|
|
112375
|
+
* The BYTES are authoritative, not the declared MIME: the header of each
|
|
112376
|
+
* inline image is sniffed, and a mismatch (e.g. AVIF bytes an MCP image
|
|
112377
|
+
* search tool labels `image/png`) is gated on what the image IS — the
|
|
112378
|
+
* provider decodes bytes, not labels. When the sniffer doesn't recognize
|
|
112379
|
+
* the bytes (corrupt image, exotic container), the declared MIME stands
|
|
112380
|
+
* and the 400-recovery path remains the backstop.
|
|
112381
|
+
*
|
|
112382
|
+
* This is the format gate shared by every ingestion point; run it BEFORE
|
|
112383
|
+
* compression so unsupported bytes are never decoded.
|
|
112384
|
+
*/
|
|
112385
|
+
function gateImageFormatParts(parts) {
|
|
112386
|
+
const out = [];
|
|
112387
|
+
for (const part of parts) {
|
|
112388
|
+
if (part.type === "image_url") {
|
|
112389
|
+
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
112390
|
+
if (parsed === null) {
|
|
112391
|
+
if (isDataUrl(part.imageUrl.url)) {
|
|
112392
|
+
out.push({
|
|
112393
|
+
type: "text",
|
|
112394
|
+
text: buildMalformedImageNotice(part.imageUrl.url)
|
|
112395
|
+
});
|
|
112396
|
+
continue;
|
|
112397
|
+
}
|
|
112398
|
+
const extMime = unsupportedImageMimeFromUrl(part.imageUrl.url);
|
|
112399
|
+
if (extMime !== null) {
|
|
112400
|
+
out.push({
|
|
112401
|
+
type: "text",
|
|
112402
|
+
text: buildUnsupportedImageNotice(extMime, part.imageUrl.url)
|
|
112403
|
+
});
|
|
112404
|
+
continue;
|
|
112405
|
+
}
|
|
112406
|
+
out.push(part);
|
|
112407
|
+
continue;
|
|
112408
|
+
}
|
|
112409
|
+
const effectiveMime = resolveEffectiveImageMime(parsed.mimeType, decodeBase64Prefix(parsed.base64));
|
|
112410
|
+
if (!isModelAcceptedImageMime(effectiveMime)) {
|
|
112411
|
+
out.push({
|
|
112412
|
+
type: "text",
|
|
112413
|
+
text: buildUnsupportedImageNotice(effectiveMime)
|
|
112414
|
+
});
|
|
112415
|
+
continue;
|
|
112416
|
+
}
|
|
112417
|
+
const canonicalUrl = `data:${normalizeImageMime(effectiveMime)};base64,${parsed.base64}`;
|
|
112418
|
+
if (part.imageUrl.url !== canonicalUrl) {
|
|
112419
|
+
out.push({
|
|
112420
|
+
type: "image_url",
|
|
112421
|
+
imageUrl: {
|
|
112422
|
+
...part.imageUrl,
|
|
112423
|
+
url: canonicalUrl
|
|
112424
|
+
}
|
|
112425
|
+
});
|
|
112426
|
+
continue;
|
|
112427
|
+
}
|
|
112428
|
+
}
|
|
112429
|
+
out.push(part);
|
|
112430
|
+
}
|
|
112431
|
+
return out;
|
|
112432
|
+
}
|
|
112433
|
+
/**
|
|
112110
112434
|
* Compress any inline base64 image parts in a content-part list — used by
|
|
112111
112435
|
* the MCP tool-result path (prompt ingestion compresses per image with
|
|
112112
112436
|
* {@link compressBase64ForModel} while constructing the part). Image parts
|
|
@@ -112114,6 +112438,13 @@ async function compressBase64ForModel(base64, mimeType, options = {}) {
|
|
|
112114
112438
|
* through, as are non-image parts. Best effort: a part that fails to
|
|
112115
112439
|
* compress is left unchanged.
|
|
112116
112440
|
*
|
|
112441
|
+
* The format gate ({@link gateImageFormatParts}) runs first: parts whose
|
|
112442
|
+
* MIME is outside the provider-accepted set are never forwarded — the part
|
|
112443
|
+
* is dropped and a text notice stands in, so one unsupported image cannot
|
|
112444
|
+
* poison the session history. This is the MCP funnel's enforcement point —
|
|
112445
|
+
* MCP servers can return any `image/*` MIME (e.g. AVIF from an image search
|
|
112446
|
+
* tool).
|
|
112447
|
+
*
|
|
112117
112448
|
* With `annotate` set, every image that was actually re-encoded gets a
|
|
112118
112449
|
* caption in {@link CompressedContentParts.captions} so the model knows it
|
|
112119
112450
|
* is looking at a downsampled copy. `annotate.persistOriginal` additionally
|
|
@@ -112125,9 +112456,9 @@ async function compressImageContentParts(parts, options = {}) {
|
|
|
112125
112456
|
const { annotate, ...compressOptions } = options;
|
|
112126
112457
|
const out = [];
|
|
112127
112458
|
const captions = [];
|
|
112128
|
-
for (const part of parts) {
|
|
112459
|
+
for (const part of gateImageFormatParts(parts)) {
|
|
112129
112460
|
if (part.type === "image_url") {
|
|
112130
|
-
const parsed = parseImageDataUrl
|
|
112461
|
+
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
112131
112462
|
if (parsed !== null) {
|
|
112132
112463
|
const result = await compressBase64ForModel(parsed.base64, parsed.mimeType, compressOptions);
|
|
112133
112464
|
if (result.changed) {
|
|
@@ -112189,7 +112520,7 @@ async function cropImageForModel(bytes, mimeType, region, options = {}) {
|
|
|
112189
112520
|
const maxEdge = options.maxEdge ?? resolveMaxImageEdgePx();
|
|
112190
112521
|
const byteBudget = options.byteBudget ?? 3932160;
|
|
112191
112522
|
const maxDecodeBytes = options.maxDecodeBytes ?? MAX_DECODE_BYTES;
|
|
112192
|
-
const normalizedMime =
|
|
112523
|
+
const normalizedMime = normalizeImageMime(mimeType);
|
|
112193
112524
|
const fail = (errorKind, error) => {
|
|
112194
112525
|
reportCropEvent(options.telemetry, {
|
|
112195
112526
|
startedAt,
|
|
@@ -112339,14 +112670,6 @@ function formatByteSize$2(bytes) {
|
|
|
112339
112670
|
if (bytes < 1024 * 1024) return `${String(Math.round(bytes / 1024))} KB`;
|
|
112340
112671
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
112341
112672
|
}
|
|
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
112673
|
/**
|
|
112351
112674
|
* Decode `bytes` into a jimp image. PNG/JPEG decode through jimp itself
|
|
112352
112675
|
* (which applies EXIF orientation); WebP decodes through the bundled wasm
|
|
@@ -112455,10 +112778,6 @@ function fitWithinEdge(image, edge) {
|
|
|
112455
112778
|
});
|
|
112456
112779
|
return true;
|
|
112457
112780
|
}
|
|
112458
|
-
function normalizeMime(mimeType) {
|
|
112459
|
-
const lower = mimeType.trim().toLowerCase();
|
|
112460
|
-
return lower === "image/jpg" ? "image/jpeg" : lower;
|
|
112461
|
-
}
|
|
112462
112781
|
/**
|
|
112463
112782
|
* Emit the `image_compress` event. Properties are all numeric/enum — never
|
|
112464
112783
|
* paths or content — and a throwing client is swallowed so telemetry can
|
|
@@ -112471,7 +112790,7 @@ function reportCompressEvent(telemetry, input) {
|
|
|
112471
112790
|
source: telemetry.source,
|
|
112472
112791
|
outcome: input.outcome,
|
|
112473
112792
|
input_mime: input.inputMime,
|
|
112474
|
-
output_mime:
|
|
112793
|
+
output_mime: normalizeImageMime(input.result.mimeType),
|
|
112475
112794
|
original_bytes: input.result.originalByteLength,
|
|
112476
112795
|
final_bytes: input.result.finalByteLength,
|
|
112477
112796
|
original_width: input.result.originalWidth,
|
|
@@ -112508,6 +112827,7 @@ function reportCropEvent(telemetry, input) {
|
|
|
112508
112827
|
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
112828
|
var init_image_compress = __esmMin((() => {
|
|
112510
112829
|
init_file_type$1();
|
|
112830
|
+
init_image_format_policy();
|
|
112511
112831
|
init_webp_decode();
|
|
112512
112832
|
MAX_IMAGE_EDGE_ENV = "KIMI_IMAGE_MAX_EDGE_PX";
|
|
112513
112833
|
IMAGE_BYTE_BUDGET = 3.75 * 1024 * 1024;
|
|
@@ -120915,10 +121235,10 @@ function isLoadableToolsAnnouncement(message) {
|
|
|
120915
121235
|
* Shape a history for a consumer that must not see dynamic-tool protocol
|
|
120916
121236
|
* context: drop the loadable-tools announcements and strip `message.tools`
|
|
120917
121237
|
* (dropping the message entirely when nothing else remains). Two callers:
|
|
120918
|
-
* - projection for a model without the
|
|
120919
|
-
* model switch — the canonical history keeps its shape, only
|
|
120920
|
-
* view changes; announcements would be noise and even
|
|
120921
|
-
* select_tools tool the model does not have);
|
|
121238
|
+
* - projection for a model without the dynamically-loaded-tools capability
|
|
121239
|
+
* (mid-session model switch — the canonical history keeps its shape, only
|
|
121240
|
+
* the outgoing view changes; announcements would be noise and even
|
|
121241
|
+
* reference a select_tools tool the model does not have);
|
|
120922
121242
|
* - the compaction summarizer input (schemas and announcements are protocol
|
|
120923
121243
|
* context, not conversation — summarizing them wastes tokens and risks
|
|
120924
121244
|
* leaking schema text into the summary).
|
|
@@ -121073,7 +121393,7 @@ async function chatWithRetry(input) {
|
|
|
121073
121393
|
logRequestFailure(input, error, attempt, maxAttempts);
|
|
121074
121394
|
throw error;
|
|
121075
121395
|
}
|
|
121076
|
-
const delayMs = delays[attempt - 1] ?? 0;
|
|
121396
|
+
const delayMs = readRetryAfterMs(error) ?? delays[attempt - 1] ?? 0;
|
|
121077
121397
|
input.params.signal.throwIfAborted();
|
|
121078
121398
|
input.dispatchEvent({
|
|
121079
121399
|
type: "step.retrying",
|
|
@@ -121113,13 +121433,24 @@ function paramsForAttempt(input, attempt, maxAttempts) {
|
|
|
121113
121433
|
};
|
|
121114
121434
|
}
|
|
121115
121435
|
function retryBackoffDelays(maxAttempts) {
|
|
121116
|
-
|
|
121117
|
-
|
|
121118
|
-
|
|
121119
|
-
|
|
121120
|
-
|
|
121121
|
-
|
|
121122
|
-
|
|
121436
|
+
const count = Math.max(maxAttempts - 1, 0);
|
|
121437
|
+
const delays = [];
|
|
121438
|
+
for (let i = 0; i < count; i += 1) {
|
|
121439
|
+
const base = Math.min(BASE_DELAY_MS * Math.pow(RETRY_FACTOR, i), MAX_DELAY_MS);
|
|
121440
|
+
delays.push(base + Math.random() * JITTER_FACTOR * base);
|
|
121441
|
+
}
|
|
121442
|
+
return delays;
|
|
121443
|
+
}
|
|
121444
|
+
/**
|
|
121445
|
+
* Server-requested backoff carried on a kosong `APIStatusError` (parsed from
|
|
121446
|
+
* the `retry-after` response header). When present and positive it overrides
|
|
121447
|
+
* the computed backoff — a server `Retry-After` directive takes precedence
|
|
121448
|
+
* over the local exponential delay.
|
|
121449
|
+
*/
|
|
121450
|
+
function readRetryAfterMs(error) {
|
|
121451
|
+
if (typeof error !== "object" || error === null) return null;
|
|
121452
|
+
const value = error.retryAfterMs;
|
|
121453
|
+
return typeof value === "number" && value > 0 ? value : null;
|
|
121123
121454
|
}
|
|
121124
121455
|
async function sleepForRetry(delayMs, signal) {
|
|
121125
121456
|
signal.throwIfAborted();
|
|
@@ -121137,15 +121468,15 @@ function maybeStatusCode(error) {
|
|
|
121137
121468
|
const statusCode = error.statusCode;
|
|
121138
121469
|
return typeof statusCode === "number" ? statusCode : void 0;
|
|
121139
121470
|
}
|
|
121140
|
-
var
|
|
121471
|
+
var BASE_DELAY_MS, MAX_DELAY_MS, RETRY_FACTOR, JITTER_FACTOR;
|
|
121141
121472
|
var init_retry = __esmMin((() => {
|
|
121142
121473
|
init_dist$4();
|
|
121143
|
-
import_retry$1 = /* @__PURE__ */ __toESM(require_retry$4(), 1);
|
|
121144
121474
|
init_abort();
|
|
121145
121475
|
init_errors$4();
|
|
121146
|
-
|
|
121147
|
-
|
|
121476
|
+
BASE_DELAY_MS = 500;
|
|
121477
|
+
MAX_DELAY_MS = 32e3;
|
|
121148
121478
|
RETRY_FACTOR = 2;
|
|
121479
|
+
JITTER_FACTOR = .25;
|
|
121149
121480
|
}));
|
|
121150
121481
|
//#endregion
|
|
121151
121482
|
//#region ../../packages/agent-core/src/tools/support/input-schema.ts
|
|
@@ -121988,7 +122319,7 @@ var init_full = __esmMin((() => {
|
|
|
121988
122319
|
summary = extractCompactionSummary(response);
|
|
121989
122320
|
break;
|
|
121990
122321
|
} catch (error) {
|
|
121991
|
-
if (error instanceof APIRequestTooLargeError && !mediaStripAttempted) {
|
|
122322
|
+
if ((error instanceof APIRequestTooLargeError || isImageFormatError(error)) && !mediaStripAttempted) {
|
|
121992
122323
|
mediaStripAttempted = true;
|
|
121993
122324
|
const stripped = replaceMediaPartsWithMarkers(historyForModel);
|
|
121994
122325
|
if (stripped !== historyForModel) {
|
|
@@ -123268,6 +123599,21 @@ var init_manager$2 = __esmMin((() => {
|
|
|
123268
123599
|
return this.scheduler.getNextFireForTask(taskId);
|
|
123269
123600
|
}
|
|
123270
123601
|
/**
|
|
123602
|
+
* Enumerate every scheduled task with its post-jitter next fire time.
|
|
123603
|
+
* Unlike the CronList tool (which renders for the model), this returns
|
|
123604
|
+
* structured data for host applications polling for pending work.
|
|
123605
|
+
*/
|
|
123606
|
+
listTaskSnapshots() {
|
|
123607
|
+
return this.store.list().map((task) => ({
|
|
123608
|
+
id: task.id,
|
|
123609
|
+
cron: task.cron,
|
|
123610
|
+
recurring: task.recurring !== false,
|
|
123611
|
+
createdAt: task.createdAt,
|
|
123612
|
+
lastFiredAt: task.lastFiredAt,
|
|
123613
|
+
nextFireAt: this.scheduler.getNextFireForTask(task.id)
|
|
123614
|
+
}));
|
|
123615
|
+
}
|
|
123616
|
+
/**
|
|
123271
123617
|
* Stale judgment.
|
|
123272
123618
|
*
|
|
123273
123619
|
* - `KIMI_CRON_NO_STALE=1` short-circuits to false (bench).
|
|
@@ -124015,15 +124361,17 @@ function isDegradableMediaPart(part) {
|
|
|
124015
124361
|
/**
|
|
124016
124362
|
* Replace all but the `keepRecent` most recent media parts with deterministic
|
|
124017
124363
|
* 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
|
-
|
|
124364
|
+
* the provider rejected as too large (HTTP 413 on accumulated base64 media)
|
|
124365
|
+
* and — with `keepRecent = 0` and `MEDIA_STRIPPED_PLACEHOLDERS` — the resend
|
|
124366
|
+
* after an image-format rejection, where the poisoned image could be anywhere
|
|
124367
|
+
* and only a full strip guarantees a clean request. A purely read-side
|
|
124368
|
+
* transform — the underlying history is left untouched — that trades pixels
|
|
124369
|
+
* for deliverability while the surrounding text (including ReadMediaFile's
|
|
124370
|
+
* `<image path="...">` wrapper) survives, so the model can re-read any file
|
|
124371
|
+
* it still needs. Untouched messages are returned by reference, and when
|
|
124372
|
+
* nothing needs degrading the input array itself is returned.
|
|
124373
|
+
*/
|
|
124374
|
+
function degradeOlderMediaParts(messages, keepRecent, placeholders = MEDIA_DEGRADED_PLACEHOLDERS) {
|
|
124027
124375
|
const mediaCount = messages.reduce((count, message) => count + message.content.filter(isDegradableMediaPart).length, 0);
|
|
124028
124376
|
let toDegrade = Math.max(0, mediaCount - keepRecent);
|
|
124029
124377
|
if (toDegrade === 0) return messages;
|
|
@@ -124034,7 +124382,7 @@ function degradeOlderMediaParts(messages, keepRecent) {
|
|
|
124034
124382
|
toDegrade -= 1;
|
|
124035
124383
|
return {
|
|
124036
124384
|
type: "text",
|
|
124037
|
-
text:
|
|
124385
|
+
text: placeholders[part.type]
|
|
124038
124386
|
};
|
|
124039
124387
|
});
|
|
124040
124388
|
return {
|
|
@@ -124043,7 +124391,7 @@ function degradeOlderMediaParts(messages, keepRecent) {
|
|
|
124043
124391
|
};
|
|
124044
124392
|
});
|
|
124045
124393
|
}
|
|
124046
|
-
var SYNTHETIC_TOOL_RESULT_TEXT, MEDIA_DEGRADED_PLACEHOLDERS;
|
|
124394
|
+
var SYNTHETIC_TOOL_RESULT_TEXT, MEDIA_DEGRADED_PLACEHOLDERS, MEDIA_STRIPPED_PLACEHOLDERS;
|
|
124047
124395
|
var init_projector = __esmMin((() => {
|
|
124048
124396
|
init_errors$8();
|
|
124049
124397
|
init_tool_result_render();
|
|
@@ -124053,6 +124401,11 @@ var init_projector = __esmMin((() => {
|
|
|
124053
124401
|
audio_url: "[audio omitted: dropped to fit the provider request size limit; re-read the file to hear it]",
|
|
124054
124402
|
video_url: "[video omitted: dropped to fit the provider request size limit; re-read the file to view it]"
|
|
124055
124403
|
};
|
|
124404
|
+
MEDIA_STRIPPED_PLACEHOLDERS = {
|
|
124405
|
+
image_url: "[image omitted: the provider rejected this image; re-read the file for conversion instructions]",
|
|
124406
|
+
audio_url: "[audio omitted: dropped along with a rejected image; re-read the file to hear it]",
|
|
124407
|
+
video_url: "[video omitted: dropped along with a rejected image; re-read the file to view it]"
|
|
124408
|
+
};
|
|
124056
124409
|
}));
|
|
124057
124410
|
//#endregion
|
|
124058
124411
|
//#region ../../packages/agent-core/src/agent/context/types.ts
|
|
@@ -124452,6 +124805,9 @@ var init_context$2 = __esmMin((() => {
|
|
|
124452
124805
|
get mediaDegradedMessages() {
|
|
124453
124806
|
return degradeOlderMediaParts(this.messages, 2);
|
|
124454
124807
|
}
|
|
124808
|
+
get mediaStrippedMessages() {
|
|
124809
|
+
return degradeOlderMediaParts(this.messages, 0, MEDIA_STRIPPED_PLACEHOLDERS);
|
|
124810
|
+
}
|
|
124455
124811
|
useProjectedHistoryFrom(source) {
|
|
124456
124812
|
this.clear();
|
|
124457
124813
|
this.pushHistory(...trimTrailingOpenToolExchange(source.project(source.history)));
|
|
@@ -137278,7 +137634,7 @@ var init_tool_call = __esmMin((() => {
|
|
|
137278
137634
|
* does not lose model usage that was already spent.
|
|
137279
137635
|
*/
|
|
137280
137636
|
async function executeLoopStep(deps) {
|
|
137281
|
-
const { turnId, signal, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, dispatchEvent, llm, tools, buildTools, describeMissingTool, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
|
|
137637
|
+
const { turnId, signal, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, buildMessagesMediaStripped, dispatchEvent, llm, tools, buildTools, describeMissingTool, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
|
|
137282
137638
|
if (hooks?.beforeStep !== void 0) {
|
|
137283
137639
|
const beforeStep = await hooks.beforeStep({
|
|
137284
137640
|
turnId,
|
|
@@ -137333,6 +137689,7 @@ async function executeLoopStep(deps) {
|
|
|
137333
137689
|
};
|
|
137334
137690
|
let response;
|
|
137335
137691
|
let mediaDegradedResendUsed = false;
|
|
137692
|
+
let mediaStrippedResendUsed = false;
|
|
137336
137693
|
try {
|
|
137337
137694
|
response = await chatWithRetry({
|
|
137338
137695
|
...retryInput,
|
|
@@ -137367,6 +137724,34 @@ async function executeLoopStep(deps) {
|
|
|
137367
137724
|
}
|
|
137368
137725
|
mediaDegradedResendUsed = true;
|
|
137369
137726
|
log?.info("recovered after media-degraded resend", { turnStep: `${turnId}.${String(currentStep)}` });
|
|
137727
|
+
} else if (buildMessagesMediaStripped !== void 0 && isImageFormatError(error)) {
|
|
137728
|
+
signal.throwIfAborted();
|
|
137729
|
+
log?.warn("provider rejected an image in the request; resending with all media stripped", {
|
|
137730
|
+
turnStep: `${turnId}.${String(currentStep)}`,
|
|
137731
|
+
model: llm.modelName
|
|
137732
|
+
});
|
|
137733
|
+
const strippedMessages = await buildMessagesMediaStripped();
|
|
137734
|
+
signal.throwIfAborted();
|
|
137735
|
+
try {
|
|
137736
|
+
response = await chatWithRetry({
|
|
137737
|
+
...retryInput,
|
|
137738
|
+
params: {
|
|
137739
|
+
...chatParams,
|
|
137740
|
+
messages: strippedMessages,
|
|
137741
|
+
requestLogFields: { projection: "media-stripped" }
|
|
137742
|
+
}
|
|
137743
|
+
});
|
|
137744
|
+
} catch (strippedError) {
|
|
137745
|
+
log?.error("media-stripped resend still rejected by provider", {
|
|
137746
|
+
turnStep: `${turnId}.${String(currentStep)}`,
|
|
137747
|
+
model: llm.modelName,
|
|
137748
|
+
originalError: errorMessage$5(error),
|
|
137749
|
+
strippedError: errorMessage$5(strippedError)
|
|
137750
|
+
});
|
|
137751
|
+
throw strippedError;
|
|
137752
|
+
}
|
|
137753
|
+
mediaStrippedResendUsed = true;
|
|
137754
|
+
log?.info("recovered after media-stripped resend", { turnStep: `${turnId}.${String(currentStep)}` });
|
|
137370
137755
|
} else if (buildMessagesStrict !== void 0 && isRecoverableRequestStructureError(error)) {
|
|
137371
137756
|
signal.throwIfAborted();
|
|
137372
137757
|
log?.warn("provider rejected request structure; resending with strict projection", {
|
|
@@ -137436,7 +137821,8 @@ async function executeLoopStep(deps) {
|
|
|
137436
137821
|
return {
|
|
137437
137822
|
usage,
|
|
137438
137823
|
stopReason: stopTurnAfterStep && effectiveStopReason === "tool_use" ? "end_turn" : effectiveStopReason,
|
|
137439
|
-
mediaDegradedResendUsed
|
|
137824
|
+
mediaDegradedResendUsed,
|
|
137825
|
+
mediaStrippedResendUsed
|
|
137440
137826
|
};
|
|
137441
137827
|
}
|
|
137442
137828
|
/**
|
|
@@ -137535,12 +137921,13 @@ var init_turn_step = __esmMin((() => {
|
|
|
137535
137921
|
//#endregion
|
|
137536
137922
|
//#region ../../packages/agent-core/src/loop/run-turn.ts
|
|
137537
137923
|
async function runTurn(input) {
|
|
137538
|
-
const { turnId, signal, llm, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, dispatchEvent, tools, buildTools, describeMissingTool, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
|
|
137924
|
+
const { turnId, signal, llm, buildMessages, buildMessagesStrict, buildMessagesMediaDegraded, buildMessagesMediaStripped, dispatchEvent, tools, buildTools, describeMissingTool, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
|
|
137539
137925
|
let usage = emptyUsage();
|
|
137540
137926
|
let steps = 0;
|
|
137541
137927
|
let stopReason = "end_turn";
|
|
137542
137928
|
let activeStep;
|
|
137543
137929
|
let mediaDegradedActive = false;
|
|
137930
|
+
let mediaStrippedActive = false;
|
|
137544
137931
|
const recordStepUsage = async (stepUsage) => {
|
|
137545
137932
|
usage = addUsage$1(usage, stepUsage);
|
|
137546
137933
|
return hostRecordStepUsage?.(stepUsage);
|
|
@@ -137554,9 +137941,10 @@ async function runTurn(input) {
|
|
|
137554
137941
|
const stepResult = await executeLoopStep({
|
|
137555
137942
|
turnId,
|
|
137556
137943
|
signal,
|
|
137557
|
-
buildMessages: mediaDegradedActive && buildMessagesMediaDegraded !== void 0 ? buildMessagesMediaDegraded : buildMessages,
|
|
137944
|
+
buildMessages: mediaStrippedActive && buildMessagesMediaStripped !== void 0 ? buildMessagesMediaStripped : mediaDegradedActive && buildMessagesMediaDegraded !== void 0 ? buildMessagesMediaDegraded : buildMessages,
|
|
137558
137945
|
buildMessagesStrict,
|
|
137559
137946
|
buildMessagesMediaDegraded,
|
|
137947
|
+
buildMessagesMediaStripped,
|
|
137560
137948
|
dispatchEvent,
|
|
137561
137949
|
llm,
|
|
137562
137950
|
tools,
|
|
@@ -137570,6 +137958,7 @@ async function runTurn(input) {
|
|
|
137570
137958
|
});
|
|
137571
137959
|
activeStep = void 0;
|
|
137572
137960
|
mediaDegradedActive = mediaDegradedActive || stepResult.mediaDegradedResendUsed === true;
|
|
137961
|
+
mediaStrippedActive = mediaStrippedActive || stepResult.mediaStrippedResendUsed === true;
|
|
137573
137962
|
if (stepResult.stopReason === "tool_use") continue;
|
|
137574
137963
|
const terminalStopReason = stepResult.stopReason;
|
|
137575
137964
|
stopReason = terminalStopReason;
|
|
@@ -143796,10 +144185,16 @@ function convertMCPContentBlock(block) {
|
|
|
143796
144185
|
}
|
|
143797
144186
|
if (block.type === "resource_link" && typeof block.uri === "string") {
|
|
143798
144187
|
const mimeType = block.mimeType ?? "application/octet-stream";
|
|
143799
|
-
if (mimeType.startsWith("image/"))
|
|
143800
|
-
|
|
143801
|
-
|
|
143802
|
-
|
|
144188
|
+
if (mimeType.startsWith("image/")) {
|
|
144189
|
+
if (!isModelAcceptedImageMime(mimeType)) return {
|
|
144190
|
+
type: "text",
|
|
144191
|
+
text: buildUnsupportedImageNotice(mimeType, block.uri)
|
|
144192
|
+
};
|
|
144193
|
+
return {
|
|
144194
|
+
type: "image_url",
|
|
144195
|
+
imageUrl: { url: block.uri }
|
|
144196
|
+
};
|
|
144197
|
+
}
|
|
143803
144198
|
if (mimeType.startsWith("audio/")) return {
|
|
143804
144199
|
type: "audio_url",
|
|
143805
144200
|
audioUrl: { url: block.uri }
|
|
@@ -143980,6 +144375,7 @@ function collapseSingleText(parts) {
|
|
|
143980
144375
|
var MCP_MAX_OUTPUT_CHARS, MCP_OUTPUT_TRUNCATED_TEXT, MCP_MAX_BINARY_PART_BYTES, MCP_MAX_BINARY_PART_CHARS;
|
|
143981
144376
|
var init_output = __esmMin((() => {
|
|
143982
144377
|
init_image_compress();
|
|
144378
|
+
init_image_format_policy();
|
|
143983
144379
|
init_image_originals();
|
|
143984
144380
|
MCP_MAX_OUTPUT_CHARS = 1e5;
|
|
143985
144381
|
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.]`;
|
|
@@ -143987,479 +144383,6 @@ var init_output = __esmMin((() => {
|
|
|
143987
144383
|
MCP_MAX_BINARY_PART_CHARS = Math.ceil(MCP_MAX_BINARY_PART_BYTES * 4 / 3);
|
|
143988
144384
|
}));
|
|
143989
144385
|
//#endregion
|
|
143990
|
-
//#region ../../packages/agent-core/src/agent/llm-request-logger.ts
|
|
143991
|
-
function splitGenerateOptions(options) {
|
|
143992
|
-
if (options === void 0) return {
|
|
143993
|
-
requestLogFields: void 0,
|
|
143994
|
-
generateOptions: void 0
|
|
143995
|
-
};
|
|
143996
|
-
const { requestLogFields, ...generateOptions } = options;
|
|
143997
|
-
return {
|
|
143998
|
-
requestLogFields,
|
|
143999
|
-
generateOptions
|
|
144000
|
-
};
|
|
144001
|
-
}
|
|
144002
|
-
function toolSignature(tools) {
|
|
144003
|
-
return tools.map(({ name, description, parameters }) => ({
|
|
144004
|
-
name,
|
|
144005
|
-
description,
|
|
144006
|
-
parameters
|
|
144007
|
-
}));
|
|
144008
|
-
}
|
|
144009
|
-
function fingerprint(content) {
|
|
144010
|
-
return createHash("sha256").update(content).digest("hex");
|
|
144011
|
-
}
|
|
144012
|
-
var LlmRequestLogger;
|
|
144013
|
-
var init_llm_request_logger = __esmMin((() => {
|
|
144014
|
-
LlmRequestLogger = class {
|
|
144015
|
-
log;
|
|
144016
|
-
lastConfigLogSignature;
|
|
144017
|
-
constructor(log) {
|
|
144018
|
-
this.log = log;
|
|
144019
|
-
}
|
|
144020
|
-
logRequest(input) {
|
|
144021
|
-
const { provider, modelAlias, systemPrompt, tools, messages, fields } = input;
|
|
144022
|
-
const requestLogFields = fields ?? {};
|
|
144023
|
-
const wireTools = tools.filter((tool) => tool.deferred !== true);
|
|
144024
|
-
const config = {
|
|
144025
|
-
provider: provider.name,
|
|
144026
|
-
model: provider.modelName,
|
|
144027
|
-
modelAlias,
|
|
144028
|
-
thinkingEffort: provider.thinkingEffort ?? void 0,
|
|
144029
|
-
systemPromptChars: systemPrompt.length,
|
|
144030
|
-
toolCount: wireTools.length
|
|
144031
|
-
};
|
|
144032
|
-
const signature = JSON.stringify({
|
|
144033
|
-
...config,
|
|
144034
|
-
systemPromptHash: fingerprint(systemPrompt),
|
|
144035
|
-
toolsHash: fingerprint(JSON.stringify(toolSignature(wireTools)))
|
|
144036
|
-
});
|
|
144037
|
-
if (signature !== this.lastConfigLogSignature) {
|
|
144038
|
-
this.lastConfigLogSignature = signature;
|
|
144039
|
-
this.log.info("llm config", {
|
|
144040
|
-
...requestLogFields,
|
|
144041
|
-
...config
|
|
144042
|
-
});
|
|
144043
|
-
}
|
|
144044
|
-
const partialMessageCount = messages.filter((message) => message.partial === true).length;
|
|
144045
|
-
const requestFields = { ...requestLogFields };
|
|
144046
|
-
if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
|
|
144047
|
-
this.log.info("llm request", requestFields);
|
|
144048
|
-
}
|
|
144049
|
-
};
|
|
144050
|
-
}));
|
|
144051
|
-
//#endregion
|
|
144052
|
-
//#region ../../packages/agent-core/src/tools/background/task-output.md?raw
|
|
144053
|
-
var task_output_default;
|
|
144054
|
-
var init_task_output$1 = __esmMin((() => {
|
|
144055
|
-
task_output_default = "Retrieve output from a running or completed background task.\n\nUse this after `Bash(run_in_background=true)` or `Agent(run_in_background=true)` when you need to inspect progress or explicitly wait for completion.\n\nGuidelines:\n- Prefer relying on automatic completion notifications. Use this tool only when you need task output before the automatic notification arrives.\n- Do not use TaskOutput to wait for a result you need before continuing — if your next step depends on the task's result, run that task in the foreground instead. TaskOutput is for a deliberate progress check you will act on without blocking, not a way to sit and wait for a background task you just launched.\n- By default this tool is non-blocking and returns a current status/output snapshot.\n- Use block=true only when you intentionally want to wait for completion or timeout.\n- This tool returns structured task metadata, a fixed-size output preview, and an output_path for the full log.\n- For a terminal task, the metadata also explains why it ended. A shell command that runs to completion reports `status: completed` on a zero exit, or `status: failed` with its non-zero `exit_code` — judge that failure from the `exit_code`, because a plain command failure carries no `stop_reason` and no `terminal_reason`. `terminal_reason` is a categorical label emitted only when the end is not an ordinary exit: `timed_out` when the deadline aborted it, `stopped` when it was explicitly stopped, or `failed` when it errored without producing an exit code; the `stopped` and `failed` cases also carry a human-readable `stop_reason`. A task that finished on its own with a clean exit carries neither `stop_reason` nor `terminal_reason`.\n- The full, never-truncated log is always available at output_path; use the `Read` tool with that path to page through it, whether or not the preview was truncated.\n- This tool works with the generic background task system and should remain the primary read path for future task types, not just bash.\n";
|
|
144056
|
-
}));
|
|
144057
|
-
//#endregion
|
|
144058
|
-
//#region ../../packages/agent-core/src/tools/background/task-output.ts
|
|
144059
|
-
function retrievalStatus(status, block) {
|
|
144060
|
-
if (isBackgroundTaskTerminal(status)) return "success";
|
|
144061
|
-
return block ? "timeout" : "not_ready";
|
|
144062
|
-
}
|
|
144063
|
-
function terminalReason(info) {
|
|
144064
|
-
if (info.status === "timed_out") return "timed_out";
|
|
144065
|
-
if (info.status === "killed" && info.stopReason !== void 0) return "stopped";
|
|
144066
|
-
if (info.status === "failed" && info.stopReason !== void 0) return "failed";
|
|
144067
|
-
}
|
|
144068
|
-
function fullOutputHint(output) {
|
|
144069
|
-
if (!output.fullOutputAvailable || output.outputPath === void 0) return void 0;
|
|
144070
|
-
if (output.truncated) return `Only the last ${String(OUTPUT_PREVIEW_BYTES)} bytes are shown above. Use the Read tool with the output_path to page through the full log (parameters: path, line_offset, n_lines; read about ${String(PAGING_HINT_LINES)} lines per page).`;
|
|
144071
|
-
return `The preview above is the complete output. Use the Read tool with the output_path if you need to re-read the full log later (parameters: path, line_offset, n_lines; read about ${String(PAGING_HINT_LINES)} lines per page).`;
|
|
144072
|
-
}
|
|
144073
|
-
var OUTPUT_PREVIEW_BYTES, PAGING_HINT_LINES, TaskOutputInputSchema, TaskOutputTool;
|
|
144074
|
-
var init_task_output = __esmMin((() => {
|
|
144075
|
-
init_zod$1();
|
|
144076
|
-
init_background();
|
|
144077
|
-
init_input_schema();
|
|
144078
|
-
init_rule_match();
|
|
144079
|
-
init_format();
|
|
144080
|
-
init_task_output$1();
|
|
144081
|
-
OUTPUT_PREVIEW_BYTES = 32 * 1024;
|
|
144082
|
-
PAGING_HINT_LINES = 300;
|
|
144083
|
-
TaskOutputInputSchema = object({
|
|
144084
|
-
task_id: string().describe("The background task ID to inspect."),
|
|
144085
|
-
block: boolean$1().default(false).describe("Whether to wait for the task to finish before returning.").optional(),
|
|
144086
|
-
timeout: number$1().int().min(0).max(3600).default(30).describe("Maximum number of seconds to wait when block=true.").optional()
|
|
144087
|
-
});
|
|
144088
|
-
TaskOutputTool = class {
|
|
144089
|
-
manager;
|
|
144090
|
-
name = "TaskOutput";
|
|
144091
|
-
description = task_output_default;
|
|
144092
|
-
parameters = toInputJsonSchema(TaskOutputInputSchema);
|
|
144093
|
-
constructor(manager) {
|
|
144094
|
-
this.manager = manager;
|
|
144095
|
-
}
|
|
144096
|
-
resolveExecution(args) {
|
|
144097
|
-
return {
|
|
144098
|
-
description: `Reading output of task ${args.task_id}`,
|
|
144099
|
-
approvalRule: this.name,
|
|
144100
|
-
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
|
|
144101
|
-
execute: () => this.execute(args)
|
|
144102
|
-
};
|
|
144103
|
-
}
|
|
144104
|
-
async execute(args) {
|
|
144105
|
-
const info = this.manager.getTask(args.task_id);
|
|
144106
|
-
if (!info) return {
|
|
144107
|
-
isError: true,
|
|
144108
|
-
output: `Task not found: ${args.task_id}`
|
|
144109
|
-
};
|
|
144110
|
-
if (args.block && !isBackgroundTaskTerminal(info.status)) await this.manager.wait(args.task_id, (args.timeout ?? 30) * 1e3);
|
|
144111
|
-
const current = this.manager.getTask(args.task_id);
|
|
144112
|
-
if (!current) return {
|
|
144113
|
-
isError: true,
|
|
144114
|
-
output: `Task not found: ${args.task_id}`
|
|
144115
|
-
};
|
|
144116
|
-
const output = await this.manager.getOutputSnapshot(args.task_id, OUTPUT_PREVIEW_BYTES);
|
|
144117
|
-
const lines = [formatPlainObject({
|
|
144118
|
-
retrievalStatus: retrievalStatus(current.status, args.block),
|
|
144119
|
-
...current,
|
|
144120
|
-
outputPath: output.outputPath,
|
|
144121
|
-
terminalReason: terminalReason(current),
|
|
144122
|
-
outputSizeBytes: output.outputSizeBytes,
|
|
144123
|
-
outputPreviewBytes: output.previewBytes,
|
|
144124
|
-
outputTruncated: output.truncated,
|
|
144125
|
-
fullOutputAvailable: output.fullOutputAvailable,
|
|
144126
|
-
fullOutputTool: output.fullOutputAvailable && output.outputPath !== void 0 ? "Read" : void 0,
|
|
144127
|
-
fullOutputHint: fullOutputHint(output)
|
|
144128
|
-
}), ""];
|
|
144129
|
-
if (output.truncated) lines.push(output.fullOutputAvailable && output.outputPath !== void 0 ? `[Truncated. Full output: ${output.outputPath}]` : "[Truncated. No persisted full log is available for this task.]");
|
|
144130
|
-
lines.push("[output]", output.preview || "[no output available]");
|
|
144131
|
-
return {
|
|
144132
|
-
output: lines.join("\n"),
|
|
144133
|
-
isError: false,
|
|
144134
|
-
message: "Task snapshot retrieved."
|
|
144135
|
-
};
|
|
144136
|
-
}
|
|
144137
|
-
};
|
|
144138
|
-
}));
|
|
144139
|
-
//#endregion
|
|
144140
|
-
//#region ../../packages/agent-core/src/tools/background/task-stop.md?raw
|
|
144141
|
-
var task_stop_default;
|
|
144142
|
-
var init_task_stop$1 = __esmMin((() => {
|
|
144143
|
-
task_stop_default = "Stop a running background task.\n\nOnly use this when a task must genuinely be cancelled — for a task that is\nfinishing normally, wait for its completion notification or inspect it with\n`TaskOutput` instead of stopping it.\n\nGuidelines:\n- This is a general-purpose stop capability for any background task. It is not\n a bash-specific kill.\n- Stopping a task is destructive: it may leave partial side effects behind.\n Use it with care.\n- If the task has already finished, this tool simply returns its current\n status.\n";
|
|
144144
|
-
}));
|
|
144145
|
-
//#endregion
|
|
144146
|
-
//#region ../../packages/agent-core/src/tools/background/task-stop.ts
|
|
144147
|
-
function terminalStopReason(reason) {
|
|
144148
|
-
const trimmed = reason?.trim();
|
|
144149
|
-
return trimmed === void 0 || trimmed.length === 0 ? "Task already in terminal state" : trimmed;
|
|
144150
|
-
}
|
|
144151
|
-
var TaskStopInputSchema, TaskStopTool;
|
|
144152
|
-
var init_task_stop = __esmMin((() => {
|
|
144153
|
-
init_zod$1();
|
|
144154
|
-
init_background();
|
|
144155
|
-
init_input_schema();
|
|
144156
|
-
init_rule_match();
|
|
144157
|
-
init_task_stop$1();
|
|
144158
|
-
TaskStopInputSchema = object({
|
|
144159
|
-
task_id: string().describe("The background task ID to stop."),
|
|
144160
|
-
reason: string().default("Stopped by TaskStop").describe("Short reason recorded when the task is stopped.").optional()
|
|
144161
|
-
});
|
|
144162
|
-
TaskStopTool = class {
|
|
144163
|
-
manager;
|
|
144164
|
-
name = "TaskStop";
|
|
144165
|
-
description = task_stop_default;
|
|
144166
|
-
parameters = toInputJsonSchema(TaskStopInputSchema);
|
|
144167
|
-
constructor(manager) {
|
|
144168
|
-
this.manager = manager;
|
|
144169
|
-
}
|
|
144170
|
-
resolveExecution(args) {
|
|
144171
|
-
return {
|
|
144172
|
-
description: `Stopping task ${args.task_id}`,
|
|
144173
|
-
approvalRule: this.name,
|
|
144174
|
-
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
|
|
144175
|
-
execute: async () => {
|
|
144176
|
-
const info = this.manager.getTask(args.task_id);
|
|
144177
|
-
if (!info) return {
|
|
144178
|
-
isError: true,
|
|
144179
|
-
output: `Task not found: ${args.task_id}`
|
|
144180
|
-
};
|
|
144181
|
-
const trimmedReason = args.reason?.trim();
|
|
144182
|
-
const reason = trimmedReason === void 0 || trimmedReason.length === 0 ? "Stopped by TaskStop" : trimmedReason;
|
|
144183
|
-
if (isBackgroundTaskTerminal(info.status)) return {
|
|
144184
|
-
output: `task_id: ${info.taskId}\nstatus: ${info.status}\nreason: ${terminalStopReason(info.stopReason)}`,
|
|
144185
|
-
isError: false
|
|
144186
|
-
};
|
|
144187
|
-
await this.manager.suppressTerminalNotification(args.task_id);
|
|
144188
|
-
const result = await this.manager.stop(args.task_id, reason);
|
|
144189
|
-
if (!result) return {
|
|
144190
|
-
isError: true,
|
|
144191
|
-
output: `Failed to stop task: ${args.task_id}`
|
|
144192
|
-
};
|
|
144193
|
-
return {
|
|
144194
|
-
output: `task_id: ${result.taskId}\nstatus: ${result.status}\nreason: ${result.stopReason ?? reason}`,
|
|
144195
|
-
isError: false
|
|
144196
|
-
};
|
|
144197
|
-
}
|
|
144198
|
-
};
|
|
144199
|
-
}
|
|
144200
|
-
};
|
|
144201
|
-
}));
|
|
144202
|
-
//#endregion
|
|
144203
|
-
//#region ../../packages/agent-core/src/tools/cron/time-format.ts
|
|
144204
|
-
/**
|
|
144205
|
-
* Render cron-facing timestamps in local wall time with an explicit
|
|
144206
|
-
* numeric offset. Cron expressions are evaluated in local time, so the
|
|
144207
|
-
* tool output should preserve that mental model while remaining
|
|
144208
|
-
* unambiguous and parseable as ISO 8601.
|
|
144209
|
-
*/
|
|
144210
|
-
function formatLocalIsoWithOffset(ms) {
|
|
144211
|
-
const date = new Date(ms);
|
|
144212
|
-
const offsetMin = -date.getTimezoneOffset();
|
|
144213
|
-
const sign = offsetMin >= 0 ? "+" : "-";
|
|
144214
|
-
const absOffset = Math.abs(offsetMin);
|
|
144215
|
-
const offset = `${sign}${pad(Math.floor(absOffset / 60))}:${pad(absOffset % 60)}`;
|
|
144216
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${String(date.getMilliseconds()).padStart(3, "0")}${offset}`;
|
|
144217
|
-
}
|
|
144218
|
-
function pad(n) {
|
|
144219
|
-
return String(n).padStart(2, "0");
|
|
144220
|
-
}
|
|
144221
|
-
var init_time_format = __esmMin((() => {}));
|
|
144222
|
-
//#endregion
|
|
144223
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-create.md?raw
|
|
144224
|
-
var cron_create_default;
|
|
144225
|
-
var init_cron_create$1 = __esmMin((() => {
|
|
144226
|
-
cron_create_default = "Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.\n\nUses standard 5-field cron in the user's local timezone: minute hour day-of-month month day-of-week. `0 9 * * *` means 9am local — no timezone conversion needed.\n\n## One-shot tasks (recurring: false)\n\nFor \"remind me at X\" or \"at <time>, do Y\" requests — fire once then auto-delete.\nPin minute/hour/day-of-month/month to specific values:\n \"remind me at 2:30pm today to check the deploy\" → cron: \"30 14 <today_dom> <today_month> *\", recurring: false\n \"tomorrow morning, run the smoke test\" → cron: \"57 8 <tomorrow_dom> <tomorrow_month> *\", recurring: false\n\nOne-shots are best for near-term reminders. A task only fires while its session is still alive (see Session lifetime below), so favor near times — within hours or a few days — rather than scheduling weeks or months ahead.\n\n## Recurring jobs (recurring: true, the default)\n\nFor \"every N minutes\" / \"every hour\" / \"weekdays at 9am\" requests:\n \"*/5 * * * *\" (every 5 min), \"0 * * * *\" (hourly), \"0 9 * * 1-5\" (weekdays at 9am local)\n\n## Avoid the :00 and :30 minute marks when the task allows it\n\nEvery user who asks for \"9am\" gets `0 9`, and every user who asks for \"hourly\" gets `0 *` — which means requests from across the planet land on the API at the same instant. When the user's request is approximate, pick a minute that is NOT 0 or 30:\n \"every morning around 9\" → \"57 8 * * *\" or \"3 9 * * *\" (not \"0 9 * * *\")\n \"hourly\" → \"7 * * * *\" (not \"0 * * * *\")\n \"in an hour or so, remind me to...\" → pick whatever minute you land on, don't round\n\nOnly use minute 0 or 30 when the user names that exact time and clearly means it (\"at 9:00 sharp\", \"at half past\", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.\n\n## Coalesce semantics\n\nFires are delivered only while the session is idle: a fire that comes due during an active turn is held and delivered at the next idle moment, never injected mid-turn.\n\nIf the scheduler slept past multiple ideal fire times (laptop closed, long-running turn, etc.), only **one** fire is delivered when it wakes up. The origin carries `coalescedCount` showing how many ideal fires were collapsed into this single delivery. You should treat `coalescedCount > 1` as \"I missed some checks; only the latest state matters\" rather than running the prompt that many times.\n\n## Cron-fire envelope\n\nWhen a cron task fires, the prompt you scheduled is re-injected wrapped in an XML envelope that exposes the fire context:\n\n```\n<cron-fire jobId=\"...\" cron=\"...\" recurring=\"true|false\" coalescedCount=\"N\" stale=\"true|false\">\n<prompt>\nyour original prompt text, verbatim\n</prompt>\n</cron-fire>\n```\n\nThe envelope is parseable. Use `coalescedCount > 1` to know multiple ideal fires were collapsed into a single delivery (treat as \"only the latest state matters\"), and `stale=\"true\"` as a cue that the task is past its 7-day threshold.\n\n## 7-day stale behavior\n\nRecurring tasks that have been alive for more than 7 days fire one\nfinal time with `stale: true` on the envelope, and the system then\nauto-deletes the task. The flag is the model's notice that this is\nthe last delivery. If the schedule is still wanted, call `CronCreate`\nagain with the same `cron` and `prompt` — that resets `createdAt` and\nstarts a fresh 7-day window. One-shot tasks are never marked stale.\n\n## Jitter behavior\n\nAnti-herd jitter is applied deterministically per task id:\n - Recurring: ideal fire time is shifted **forward** by an offset ≤ min(10% of the cron period, 15 minutes). A `*/5 * * * *` task can drift up to 30s; a `0 9 * * *` task can drift up to 15 minutes.\n - One-shot: only when the ideal fire lands on `:00` or `:30` of the hour, the fire is pulled **earlier** by ≤ 90 seconds. Other minutes pass through unchanged.\n\n## One-shot vs recurring — when to pick which\n\nUse `recurring: false` for \"remind me at X\" style requests, single deadlines, \"in N minutes do Y\", and any task that should not repeat. Use `recurring: true` for periodic polling (CI status, build watchers, scheduled reports), workday rituals, and anything the user explicitly described as recurring.\n\n## Session lifetime\n\nCron tasks live in the current kimi CLI session. When you exit, they\nare persisted under the session homedir; the next `kimi resume` of the\nsame session reloads them and the scheduler resumes from each task's\n`createdAt`. Fire times that fell during the offline window are\ncollapsed into a single delivery via `coalescedCount` (and recurring\ntasks past their 7-day window arrive with `stale: true` as their final\ndelivery).\n\nTasks do **not** carry over into a brand-new session — they are scoped\nto the resumed session id, not to the working directory.\n\n## Limits\n\nA session holds at most 50 live cron tasks; creating one beyond that is rejected. (The `prompt` body is also capped — see its parameter description.) Expressions that never fire within the next 5 years (e.g. `0 0 31 2 *`, an impossible date) are rejected at create time.\n\n## Returned fields\n\n`id` (8-hex), `cron` (the normalized expression), `humanSchedule` (English summary), `recurring`,\n`nextFireAt` (local ISO timestamp with numeric offset, or null). `id` is needed by `CronDelete`.\n\n## Tell the user how to cancel or modify\n\nAfter successfully creating a task, proactively tell the user how they can cancel or modify it later. Users have no direct `/cron` command or self-service UI to manage reminders themselves; they must ask the model to make changes (e.g. \"cancel my 9am reminder\" or \"change my daily check to 10am\"). Include the task `id` in your message so the user can reference it.\n";
|
|
144227
|
-
}));
|
|
144228
|
-
//#endregion
|
|
144229
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-create.ts
|
|
144230
|
-
function formatOutput(o) {
|
|
144231
|
-
return [
|
|
144232
|
-
`id: ${o.id}`,
|
|
144233
|
-
`cron: ${o.cron}`,
|
|
144234
|
-
`humanSchedule: ${o.humanSchedule}`,
|
|
144235
|
-
`recurring: ${String(o.recurring)}`,
|
|
144236
|
-
`nextFireAt: ${o.nextFireAt === null ? "null" : formatLocalIsoWithOffset(o.nextFireAt)}`
|
|
144237
|
-
].join("\n");
|
|
144238
|
-
}
|
|
144239
|
-
var MAX_PROMPT_BYTES, ONE_SHOT_MAX_FUTURE_MS, CronCreateInputSchema, CronCreateTool;
|
|
144240
|
-
var init_cron_create = __esmMin((() => {
|
|
144241
|
-
init_zod$1();
|
|
144242
|
-
init_input_schema();
|
|
144243
|
-
init_rule_match();
|
|
144244
|
-
init_cron_expr();
|
|
144245
|
-
init_jitter();
|
|
144246
|
-
init_time_format();
|
|
144247
|
-
init_cron_create$1();
|
|
144248
|
-
MAX_PROMPT_BYTES = 8 * 1024;
|
|
144249
|
-
ONE_SHOT_MAX_FUTURE_MS = 350 * 24 * 60 * 60 * 1e3;
|
|
144250
|
-
CronCreateInputSchema = object({
|
|
144251
|
-
cron: string().describe("5-field cron expression in local time: \"M H DoM Mon DoW\" (e.g. \"*/5 * * * *\" = every 5 minutes; \"30 14 28 2 *\" = Feb 28 at 2:30pm local — a pinned date like this repeats yearly unless you also pass recurring: false)."),
|
|
144252
|
-
prompt: string().min(1).max(MAX_PROMPT_BYTES).describe("The prompt to enqueue at each fire time. Limited to 8 KiB (UTF-8)."),
|
|
144253
|
-
recurring: boolean$1().optional().default(true).describe("true (default) = fire on every cron match until deleted or auto-expired after 7 days. false = fire once at the next match, then auto-delete. Use false for \"remind me at X\" one-shot requests with pinned minute/hour/dom/month.")
|
|
144254
|
-
});
|
|
144255
|
-
CronCreateTool = class {
|
|
144256
|
-
manager;
|
|
144257
|
-
name = "CronCreate";
|
|
144258
|
-
description = cron_create_default;
|
|
144259
|
-
parameters = toInputJsonSchema(CronCreateInputSchema);
|
|
144260
|
-
constructor(manager) {
|
|
144261
|
-
this.manager = manager;
|
|
144262
|
-
}
|
|
144263
|
-
resolveExecution(args) {
|
|
144264
|
-
if (process.env["KIMI_DISABLE_CRON"] === "1") return {
|
|
144265
|
-
isError: true,
|
|
144266
|
-
output: "Cron scheduling is disabled (KIMI_DISABLE_CRON=1)."
|
|
144267
|
-
};
|
|
144268
|
-
const normalizedCron = args.cron.trim().split(/\s+/).join(" ");
|
|
144269
|
-
let parsed;
|
|
144270
|
-
try {
|
|
144271
|
-
parsed = parseCronExpression(normalizedCron);
|
|
144272
|
-
} catch (err) {
|
|
144273
|
-
return {
|
|
144274
|
-
isError: true,
|
|
144275
|
-
output: `Invalid cron expression: ${err instanceof Error ? err.message : String(err)}`
|
|
144276
|
-
};
|
|
144277
|
-
}
|
|
144278
|
-
const nowAtPrepare = this.manager.clocks.wallNow();
|
|
144279
|
-
if (!hasFireWithinYears(parsed, 5, nowAtPrepare)) return {
|
|
144280
|
-
isError: true,
|
|
144281
|
-
output: `Cron expression ${JSON.stringify(normalizedCron)} has no fire within 5 years; refusing to schedule.`
|
|
144282
|
-
};
|
|
144283
|
-
if (this.manager.store.list().length >= 50) return {
|
|
144284
|
-
isError: true,
|
|
144285
|
-
output: `Cron job cap reached (max ${String(50)} per session).`
|
|
144286
|
-
};
|
|
144287
|
-
const byteLen = Buffer.byteLength(args.prompt, "utf8");
|
|
144288
|
-
if (byteLen > MAX_PROMPT_BYTES) return {
|
|
144289
|
-
isError: true,
|
|
144290
|
-
output: `Prompt exceeds ${String(MAX_PROMPT_BYTES)} bytes (got ${String(byteLen)}).`
|
|
144291
|
-
};
|
|
144292
|
-
const recurring = args.recurring !== false;
|
|
144293
|
-
if (!recurring) {
|
|
144294
|
-
const firstFire = computeNextCronRun(parsed, nowAtPrepare);
|
|
144295
|
-
if (firstFire !== null && firstFire - nowAtPrepare > ONE_SHOT_MAX_FUTURE_MS) return {
|
|
144296
|
-
isError: true,
|
|
144297
|
-
output: `One-shot cron ${JSON.stringify(normalizedCron)} would not fire until ${formatLocalIsoWithOffset(firstFire)} (more than a year out). If you meant "today" or a near date, the pinned day/month has already passed this year — pick a future date or use wildcards.`
|
|
144298
|
-
};
|
|
144299
|
-
}
|
|
144300
|
-
return {
|
|
144301
|
-
description: recurring ? `Scheduling cron ${normalizedCron}` : `Scheduling one-shot ${normalizedCron}`,
|
|
144302
|
-
approvalRule: literalRulePattern(this.name, JSON.stringify({
|
|
144303
|
-
cron: normalizedCron,
|
|
144304
|
-
prompt: args.prompt,
|
|
144305
|
-
recurring
|
|
144306
|
-
})),
|
|
144307
|
-
execute: async () => {
|
|
144308
|
-
const nowMs = this.manager.clocks.wallNow();
|
|
144309
|
-
if (this.manager.store.list().length >= 50) return {
|
|
144310
|
-
isError: true,
|
|
144311
|
-
output: `Cron job cap reached (max ${String(50)} per session).`
|
|
144312
|
-
};
|
|
144313
|
-
const task = this.manager.addTask({
|
|
144314
|
-
cron: normalizedCron,
|
|
144315
|
-
prompt: args.prompt,
|
|
144316
|
-
recurring
|
|
144317
|
-
});
|
|
144318
|
-
const ideal = computeNextCronRun(parsed, nowMs);
|
|
144319
|
-
const nextFireAt = ideal === null ? null : recurring ? jitteredNextCronRunMs(task, parsed, ideal) : oneShotJitteredNextCronRunMs(task, ideal);
|
|
144320
|
-
const humanSchedule = cronToHuman(parsed);
|
|
144321
|
-
this.manager.emitScheduled(task);
|
|
144322
|
-
return {
|
|
144323
|
-
output: formatOutput({
|
|
144324
|
-
id: task.id,
|
|
144325
|
-
cron: normalizedCron,
|
|
144326
|
-
humanSchedule,
|
|
144327
|
-
recurring,
|
|
144328
|
-
nextFireAt
|
|
144329
|
-
}),
|
|
144330
|
-
isError: false,
|
|
144331
|
-
message: `Scheduled cron ${task.id}`
|
|
144332
|
-
};
|
|
144333
|
-
}
|
|
144334
|
-
};
|
|
144335
|
-
}
|
|
144336
|
-
};
|
|
144337
|
-
}));
|
|
144338
|
-
//#endregion
|
|
144339
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-delete.md?raw
|
|
144340
|
-
var cron_delete_default;
|
|
144341
|
-
var init_cron_delete$1 = __esmMin((() => {
|
|
144342
|
-
cron_delete_default = "Cancel a scheduled cron job by id.\n\nUse this tool to remove a cron task previously scheduled with\n`CronCreate`. The `id` is the 8-hex value returned by `CronCreate`, or\nshown in the `id:` column of `CronList` — quote it verbatim, no\nprefix.\n\nBehaviour by task kind:\n\n- **Recurring task** (`recurring: true`): stops all future fires\n immediately. The scheduler picks up the deletion on its next tick.\n- **One-shot task** (`recurring: false`): cancels the pending fire if\n it has not happened yet. One-shots that have already fired\n auto-delete themselves, so calling `CronDelete` on a fired one-shot\n returns \"no cron job with id ...\".\n\nNot-found is reported as an error (not a silent no-op) so you can\ncorrect yourself — typically by calling `CronList` to see which ids\nare actually live, rather than re-trying with the same stale id.\n\nRefresh pattern (use when you want a stale recurring schedule to\ncontinue):\n\nStale recurring tasks are auto-deleted by the system after their final\nfire — there is nothing for `CronDelete` to remove at that point. To\nkeep the schedule running, just call `CronCreate` with the same `cron`\nand `prompt`. Use `CronList`'s `prompt` field to recall the original\ntext after a context compaction.\n\n`CronDelete` remains the right call when you want to cancel a task\nthat is still live (recurring not yet stale, or a one-shot still\npending).\n\nGuidelines:\n\n- Users have no direct `/cron` command or self-service UI to delete\n tasks themselves; they must ask the model to cancel a reminder.\n When deleting on behalf of a user, confirm the action and report\n the result plainly.\n- Cron deletion is irreversible — there is no undo. If you delete the\n wrong task, you must re-create it with `CronCreate`.\n- If the model is unsure which id is current (e.g. after a context\n compaction), call `CronList` first rather than guessing.\n";
|
|
144343
|
-
}));
|
|
144344
|
-
//#endregion
|
|
144345
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-delete.ts
|
|
144346
|
-
var ID_PATTERN, CronDeleteInputSchema, CronDeleteTool;
|
|
144347
|
-
var init_cron_delete = __esmMin((() => {
|
|
144348
|
-
init_zod$1();
|
|
144349
|
-
init_input_schema();
|
|
144350
|
-
init_cron_delete$1();
|
|
144351
|
-
ID_PATTERN = /^[0-9a-f]{8}$/;
|
|
144352
|
-
CronDeleteInputSchema = object({ id: string().describe("The 8-hex cron job id returned by CronCreate / CronList.") });
|
|
144353
|
-
CronDeleteTool = class {
|
|
144354
|
-
manager;
|
|
144355
|
-
name = "CronDelete";
|
|
144356
|
-
description = cron_delete_default;
|
|
144357
|
-
parameters = toInputJsonSchema(CronDeleteInputSchema);
|
|
144358
|
-
constructor(manager) {
|
|
144359
|
-
this.manager = manager;
|
|
144360
|
-
}
|
|
144361
|
-
resolveExecution(args) {
|
|
144362
|
-
if (!ID_PATTERN.test(args.id)) return {
|
|
144363
|
-
isError: true,
|
|
144364
|
-
output: `Invalid cron job id ${JSON.stringify(args.id)} — must be 8 lowercase hex characters.`
|
|
144365
|
-
};
|
|
144366
|
-
return {
|
|
144367
|
-
description: `Deleting cron ${args.id}`,
|
|
144368
|
-
approvalRule: this.name,
|
|
144369
|
-
execute: async () => {
|
|
144370
|
-
if (this.manager.removeTasks([args.id]).length === 0) return {
|
|
144371
|
-
isError: true,
|
|
144372
|
-
output: `No cron job with id ${args.id}.`
|
|
144373
|
-
};
|
|
144374
|
-
this.manager.emitDeleted(args.id);
|
|
144375
|
-
return {
|
|
144376
|
-
output: `Deleted cron job ${args.id}.`,
|
|
144377
|
-
isError: false
|
|
144378
|
-
};
|
|
144379
|
-
}
|
|
144380
|
-
};
|
|
144381
|
-
}
|
|
144382
|
-
};
|
|
144383
|
-
}));
|
|
144384
|
-
//#endregion
|
|
144385
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-list.md?raw
|
|
144386
|
-
var cron_list_default;
|
|
144387
|
-
var init_cron_list$1 = __esmMin((() => {
|
|
144388
|
-
cron_list_default = "List all cron jobs currently scheduled in this session.\n\nUse this tool to see every pending cron task — both recurring jobs and\none-shot reminders — that you (or the user) have scheduled with\n`CronCreate`. The output is the entry point for inspecting scheduled\nwork: it returns a stable id, the original cron expression, a human\nrendering, the next post-jitter fire time, the recurring flag, the\ntask's age in days, and a stale indicator.\n\nEach record carries:\n\n- `id` — the 8-hex task id. Pass this to `CronDelete` to remove the\n task, or quote it in user-facing messages when asking for\n confirmation.\n- `cron` — the verbatim 5-field cron expression as scheduled.\n- `humanSchedule` — plain-English rendering (e.g. `every 5 minutes`).\n- `prompt` — the scheduled prompt text, JSON-encoded so embedded\n newlines stay on one line. Truncated to 200 UTF-8 bytes with\n `…(truncated)` if longer. Use this to recall what a task is for\n after a context compaction, and as the source for the\n `CronCreate` refresh ritual.\n- `nextFireAt` — local ISO timestamp with an explicit numeric offset\n for the next fire **after jitter has been applied**. The actual fire\n may land slightly before or after a round `:00` / `:30` minute mark\n due to herd-avoidance jitter; this is the value the scheduler will\n compare against, so it reflects what will really happen. `null` if\n the expression has no fire in the next 5 years (should not happen\n for tasks created through `CronCreate`, which validates).\n- `recurring` — `true` for cadenced jobs, `false` for one-shots.\n- `ageDays` — `(now - createdAt) / day`, two decimal places. Useful\n when deciding whether a long-running cron is still relevant.\n- `stale` — `true` when a recurring task is older than 7 days. The\n system **auto-deletes the task after this fire** to bound session\n lifetime; the `stale: true` flag is the model's notice that this is\n the final delivery. To resume the same schedule, call `CronCreate`\n again with the original `cron` and `prompt` (the `prompt` row above\n carries it for exactly this purpose). One-shots are never marked\n stale — they fire at most once by construction.\n\nGuidelines:\n\n- This tool is read-only and never mutates state, so it is always\n safe to call (including in plan mode).\n- Users cannot directly manage cron tasks themselves; if they want to\n cancel or modify a schedule, route the request through the model\n (i.e. call `CronDelete` or `CronCreate` on their behalf).\n- The empty case returns `cron_jobs: 0\\nNo cron jobs scheduled.`. Cron\n tasks survive a `kimi resume` of the same session but do not bleed\n into new sessions.\n- After a context compaction, or whenever you are unsure which cron\n jobs are live, call this tool to re-enumerate them rather than\n guessing ids from earlier in the conversation.\n- Records are separated by a line containing just `---`, in the\n insertion order they were scheduled.\n";
|
|
144389
|
-
}));
|
|
144390
|
-
//#endregion
|
|
144391
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-list.ts
|
|
144392
|
-
function previewPrompt(prompt) {
|
|
144393
|
-
const buf = Buffer.from(prompt, "utf8");
|
|
144394
|
-
if (buf.byteLength <= PROMPT_PREVIEW_BYTES) return prompt;
|
|
144395
|
-
let end = PROMPT_PREVIEW_BYTES;
|
|
144396
|
-
while (end > 0 && (buf[end] & 192) === 128) end--;
|
|
144397
|
-
return `${buf.subarray(0, end).toString("utf8")}…(truncated)`;
|
|
144398
|
-
}
|
|
144399
|
-
var CronListInputSchema, MS_PER_DAY, PROMPT_PREVIEW_BYTES, CronListTool;
|
|
144400
|
-
var init_cron_list = __esmMin((() => {
|
|
144401
|
-
init_zod$1();
|
|
144402
|
-
init_input_schema();
|
|
144403
|
-
init_cron_expr();
|
|
144404
|
-
init_time_format();
|
|
144405
|
-
init_cron_list$1();
|
|
144406
|
-
CronListInputSchema = object({}).strict();
|
|
144407
|
-
MS_PER_DAY = 1440 * 60 * 1e3;
|
|
144408
|
-
PROMPT_PREVIEW_BYTES = 200;
|
|
144409
|
-
CronListTool = class {
|
|
144410
|
-
manager;
|
|
144411
|
-
name = "CronList";
|
|
144412
|
-
description = cron_list_default;
|
|
144413
|
-
parameters = toInputJsonSchema(CronListInputSchema);
|
|
144414
|
-
constructor(manager) {
|
|
144415
|
-
this.manager = manager;
|
|
144416
|
-
}
|
|
144417
|
-
resolveExecution(_args) {
|
|
144418
|
-
return {
|
|
144419
|
-
description: "Listing scheduled cron jobs",
|
|
144420
|
-
approvalRule: this.name,
|
|
144421
|
-
execute: async () => {
|
|
144422
|
-
const tasks = this.manager.store.list();
|
|
144423
|
-
const nowMs = this.manager.clocks.wallNow();
|
|
144424
|
-
const records = tasks.map((t) => this.renderRecord(t, nowMs));
|
|
144425
|
-
const header = `cron_jobs: ${String(tasks.length)}`;
|
|
144426
|
-
if (records.length === 0) return {
|
|
144427
|
-
output: `${header}\nNo cron jobs scheduled.`,
|
|
144428
|
-
isError: false
|
|
144429
|
-
};
|
|
144430
|
-
return {
|
|
144431
|
-
output: `${header}\n${records.join("\n---\n")}`,
|
|
144432
|
-
isError: false
|
|
144433
|
-
};
|
|
144434
|
-
}
|
|
144435
|
-
};
|
|
144436
|
-
}
|
|
144437
|
-
renderRecord(task, nowMs) {
|
|
144438
|
-
const recurring = task.recurring !== false;
|
|
144439
|
-
const ageMs = nowMs - task.createdAt;
|
|
144440
|
-
const ageDays = Number.isFinite(ageMs) ? ageMs / MS_PER_DAY : 0;
|
|
144441
|
-
const stale = this.manager.isStale(task);
|
|
144442
|
-
let humanSchedule = task.cron;
|
|
144443
|
-
let nextFireAtIso = "null";
|
|
144444
|
-
try {
|
|
144445
|
-
humanSchedule = cronToHuman(parseCronExpression(task.cron));
|
|
144446
|
-
const nextFireMs = this.manager.getNextFireForTask(task.id);
|
|
144447
|
-
if (nextFireMs !== null) nextFireAtIso = formatLocalIsoWithOffset(nextFireMs);
|
|
144448
|
-
} catch {}
|
|
144449
|
-
return [
|
|
144450
|
-
`id: ${task.id}`,
|
|
144451
|
-
`cron: ${task.cron}`,
|
|
144452
|
-
`humanSchedule: ${humanSchedule}`,
|
|
144453
|
-
`prompt: ${JSON.stringify(previewPrompt(task.prompt))}`,
|
|
144454
|
-
`nextFireAt: ${nextFireAtIso}`,
|
|
144455
|
-
`recurring: ${String(recurring)}`,
|
|
144456
|
-
`ageDays: ${ageDays.toFixed(2)}`,
|
|
144457
|
-
`stale: ${String(stale)}`
|
|
144458
|
-
].join("\n");
|
|
144459
|
-
}
|
|
144460
|
-
};
|
|
144461
|
-
}));
|
|
144462
|
-
//#endregion
|
|
144463
144386
|
//#region ../../packages/agent-core/src/agent/permission/policies/deny-all.ts
|
|
144464
144387
|
var DenyAllPermissionPolicy;
|
|
144465
144388
|
var init_deny_all = __esmMin((() => {
|
|
@@ -145119,6 +145042,38 @@ var init_summary_continuation = __esmMin((() => {
|
|
|
145119
145042
|
}));
|
|
145120
145043
|
//#endregion
|
|
145121
145044
|
//#region ../../packages/agent-core/src/session/subagent-host.ts
|
|
145045
|
+
/**
|
|
145046
|
+
* Resolve the effective subagent per-task timeout. Precedence:
|
|
145047
|
+
* `KIMI_SUBAGENT_TIMEOUT_MS` (positive integer ms) → `configMs` →
|
|
145048
|
+
* `DEFAULT_SUBAGENT_TIMEOUT_MS` (30 min). Set a large value to effectively
|
|
145049
|
+
* disable the cap. The value feeds the background-task manager's per-task
|
|
145050
|
+
* timeout, so it governs foreground and background subagents (and AgentSwarm).
|
|
145051
|
+
*/
|
|
145052
|
+
function resolveSubagentTimeoutMs(configMs) {
|
|
145053
|
+
const raw = process.env[SUBAGENT_TIMEOUT_ENV];
|
|
145054
|
+
if (raw !== void 0 && raw.trim().length > 0) {
|
|
145055
|
+
const parsed = Number(raw);
|
|
145056
|
+
if (Number.isInteger(parsed) && parsed >= 1) return parsed;
|
|
145057
|
+
}
|
|
145058
|
+
if (configMs !== void 0 && Number.isInteger(configMs) && configMs >= 1) return configMs;
|
|
145059
|
+
return DEFAULT_SUBAGENT_TIMEOUT_MS;
|
|
145060
|
+
}
|
|
145061
|
+
/** Human-readable duration for the subagent timeout message. */
|
|
145062
|
+
function formatSubagentTimeoutDescription(ms) {
|
|
145063
|
+
if (ms % (3600 * 1e3) === 0) {
|
|
145064
|
+
const h = ms / (3600 * 1e3);
|
|
145065
|
+
return `${h} hour${h === 1 ? "" : "s"}`;
|
|
145066
|
+
}
|
|
145067
|
+
if (ms % (60 * 1e3) === 0) {
|
|
145068
|
+
const m = ms / (60 * 1e3);
|
|
145069
|
+
return `${m} minute${m === 1 ? "" : "s"}`;
|
|
145070
|
+
}
|
|
145071
|
+
if (ms % 1e3 === 0) {
|
|
145072
|
+
const s = ms / 1e3;
|
|
145073
|
+
return `${s} second${s === 1 ? "" : "s"}`;
|
|
145074
|
+
}
|
|
145075
|
+
return `${ms} ms`;
|
|
145076
|
+
}
|
|
145122
145077
|
async function runChildTurnToCompletion(child, signal) {
|
|
145123
145078
|
const completion = await child.turn.waitForCurrentTurn(signal);
|
|
145124
145079
|
const turnEnded = completion.event;
|
|
@@ -145146,7 +145101,7 @@ function shouldSuppressQueuedAttemptFailureEvent(options, error) {
|
|
|
145146
145101
|
if (isProviderRateLimitError(error)) return true;
|
|
145147
145102
|
return isAbortError$3(error) || options.signal.aborted;
|
|
145148
145103
|
}
|
|
145149
|
-
var DEFAULT_SUBAGENT_TIMEOUT_MS,
|
|
145104
|
+
var DEFAULT_SUBAGENT_TIMEOUT_MS, SUBAGENT_TIMEOUT_ENV, SUMMARY_MIN_LENGTH, SUMMARY_CONTINUATION_ATTEMPTS, HOOK_TEXT_PREVIEW_LENGTH, SUBAGENT_MAX_TOKENS_ERROR, TOOL_CALL_DISABLED_MESSAGE, SUBAGENT_PROMPT_ORIGIN, SIDE_QUESTION_SYSTEM_REMINDER, SessionSubagentHost;
|
|
145150
145105
|
var init_subagent_host = __esmMin((() => {
|
|
145151
145106
|
init_src$4();
|
|
145152
145107
|
init_errors$8();
|
|
@@ -145158,8 +145113,8 @@ var init_subagent_host = __esmMin((() => {
|
|
|
145158
145113
|
init_git_context();
|
|
145159
145114
|
init_subagent_batch();
|
|
145160
145115
|
init_summary_continuation();
|
|
145161
|
-
DEFAULT_SUBAGENT_TIMEOUT_MS =
|
|
145162
|
-
|
|
145116
|
+
DEFAULT_SUBAGENT_TIMEOUT_MS = 7200 * 1e3;
|
|
145117
|
+
SUBAGENT_TIMEOUT_ENV = "KIMI_SUBAGENT_TIMEOUT_MS";
|
|
145163
145118
|
SUMMARY_MIN_LENGTH = 200;
|
|
145164
145119
|
SUMMARY_CONTINUATION_ATTEMPTS = 1;
|
|
145165
145120
|
HOOK_TEXT_PREVIEW_LENGTH = 500;
|
|
@@ -145463,6 +145418,479 @@ IMPORTANT:
|
|
|
145463
145418
|
};
|
|
145464
145419
|
}));
|
|
145465
145420
|
//#endregion
|
|
145421
|
+
//#region ../../packages/agent-core/src/agent/llm-request-logger.ts
|
|
145422
|
+
function splitGenerateOptions(options) {
|
|
145423
|
+
if (options === void 0) return {
|
|
145424
|
+
requestLogFields: void 0,
|
|
145425
|
+
generateOptions: void 0
|
|
145426
|
+
};
|
|
145427
|
+
const { requestLogFields, ...generateOptions } = options;
|
|
145428
|
+
return {
|
|
145429
|
+
requestLogFields,
|
|
145430
|
+
generateOptions
|
|
145431
|
+
};
|
|
145432
|
+
}
|
|
145433
|
+
function toolSignature(tools) {
|
|
145434
|
+
return tools.map(({ name, description, parameters }) => ({
|
|
145435
|
+
name,
|
|
145436
|
+
description,
|
|
145437
|
+
parameters
|
|
145438
|
+
}));
|
|
145439
|
+
}
|
|
145440
|
+
function fingerprint(content) {
|
|
145441
|
+
return createHash("sha256").update(content).digest("hex");
|
|
145442
|
+
}
|
|
145443
|
+
var LlmRequestLogger;
|
|
145444
|
+
var init_llm_request_logger = __esmMin((() => {
|
|
145445
|
+
LlmRequestLogger = class {
|
|
145446
|
+
log;
|
|
145447
|
+
lastConfigLogSignature;
|
|
145448
|
+
constructor(log) {
|
|
145449
|
+
this.log = log;
|
|
145450
|
+
}
|
|
145451
|
+
logRequest(input) {
|
|
145452
|
+
const { provider, modelAlias, systemPrompt, tools, messages, fields } = input;
|
|
145453
|
+
const requestLogFields = fields ?? {};
|
|
145454
|
+
const wireTools = tools.filter((tool) => tool.deferred !== true);
|
|
145455
|
+
const config = {
|
|
145456
|
+
provider: provider.name,
|
|
145457
|
+
model: provider.modelName,
|
|
145458
|
+
modelAlias,
|
|
145459
|
+
thinkingEffort: provider.thinkingEffort ?? void 0,
|
|
145460
|
+
systemPromptChars: systemPrompt.length,
|
|
145461
|
+
toolCount: wireTools.length
|
|
145462
|
+
};
|
|
145463
|
+
const signature = JSON.stringify({
|
|
145464
|
+
...config,
|
|
145465
|
+
systemPromptHash: fingerprint(systemPrompt),
|
|
145466
|
+
toolsHash: fingerprint(JSON.stringify(toolSignature(wireTools)))
|
|
145467
|
+
});
|
|
145468
|
+
if (signature !== this.lastConfigLogSignature) {
|
|
145469
|
+
this.lastConfigLogSignature = signature;
|
|
145470
|
+
this.log.info("llm config", {
|
|
145471
|
+
...requestLogFields,
|
|
145472
|
+
...config
|
|
145473
|
+
});
|
|
145474
|
+
}
|
|
145475
|
+
const partialMessageCount = messages.filter((message) => message.partial === true).length;
|
|
145476
|
+
const requestFields = { ...requestLogFields };
|
|
145477
|
+
if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
|
|
145478
|
+
this.log.info("llm request", requestFields);
|
|
145479
|
+
}
|
|
145480
|
+
};
|
|
145481
|
+
}));
|
|
145482
|
+
//#endregion
|
|
145483
|
+
//#region ../../packages/agent-core/src/tools/background/task-output.md?raw
|
|
145484
|
+
var task_output_default;
|
|
145485
|
+
var init_task_output$1 = __esmMin((() => {
|
|
145486
|
+
task_output_default = "Retrieve output from a running or completed background task.\n\nUse this after `Bash(run_in_background=true)` or `Agent(run_in_background=true)` when you need to inspect progress or explicitly wait for completion.\n\nGuidelines:\n- Prefer relying on automatic completion notifications. Use this tool only when you need task output before the automatic notification arrives.\n- Do not use TaskOutput to wait for a result you need before continuing — if your next step depends on the task's result, run that task in the foreground instead. TaskOutput is for a deliberate progress check you will act on without blocking, not a way to sit and wait for a background task you just launched.\n- By default this tool is non-blocking and returns a current status/output snapshot.\n- Use block=true only when you intentionally want to wait for completion or timeout.\n- This tool returns structured task metadata, a fixed-size output preview, and an output_path for the full log.\n- For a terminal task, the metadata also explains why it ended. A shell command that runs to completion reports `status: completed` on a zero exit, or `status: failed` with its non-zero `exit_code` — judge that failure from the `exit_code`, because a plain command failure carries no `stop_reason` and no `terminal_reason`. `terminal_reason` is a categorical label emitted only when the end is not an ordinary exit: `timed_out` when the deadline aborted it, `stopped` when it was explicitly stopped, or `failed` when it errored without producing an exit code; the `stopped` and `failed` cases also carry a human-readable `stop_reason`. A task that finished on its own with a clean exit carries neither `stop_reason` nor `terminal_reason`.\n- The full, never-truncated log is always available at output_path; use the `Read` tool with that path to page through it, whether or not the preview was truncated.\n- This tool works with the generic background task system and should remain the primary read path for future task types, not just bash.\n";
|
|
145487
|
+
}));
|
|
145488
|
+
//#endregion
|
|
145489
|
+
//#region ../../packages/agent-core/src/tools/background/task-output.ts
|
|
145490
|
+
function retrievalStatus(status, block) {
|
|
145491
|
+
if (isBackgroundTaskTerminal(status)) return "success";
|
|
145492
|
+
return block ? "timeout" : "not_ready";
|
|
145493
|
+
}
|
|
145494
|
+
function terminalReason(info) {
|
|
145495
|
+
if (info.status === "timed_out") return "timed_out";
|
|
145496
|
+
if (info.status === "killed" && info.stopReason !== void 0) return "stopped";
|
|
145497
|
+
if (info.status === "failed" && info.stopReason !== void 0) return "failed";
|
|
145498
|
+
}
|
|
145499
|
+
function fullOutputHint(output) {
|
|
145500
|
+
if (!output.fullOutputAvailable || output.outputPath === void 0) return void 0;
|
|
145501
|
+
if (output.truncated) return `Only the last ${String(OUTPUT_PREVIEW_BYTES)} bytes are shown above. Use the Read tool with the output_path to page through the full log (parameters: path, line_offset, n_lines; read about ${String(PAGING_HINT_LINES)} lines per page).`;
|
|
145502
|
+
return `The preview above is the complete output. Use the Read tool with the output_path if you need to re-read the full log later (parameters: path, line_offset, n_lines; read about ${String(PAGING_HINT_LINES)} lines per page).`;
|
|
145503
|
+
}
|
|
145504
|
+
var OUTPUT_PREVIEW_BYTES, PAGING_HINT_LINES, TaskOutputInputSchema, TaskOutputTool;
|
|
145505
|
+
var init_task_output = __esmMin((() => {
|
|
145506
|
+
init_zod$1();
|
|
145507
|
+
init_background();
|
|
145508
|
+
init_input_schema();
|
|
145509
|
+
init_rule_match();
|
|
145510
|
+
init_format();
|
|
145511
|
+
init_task_output$1();
|
|
145512
|
+
OUTPUT_PREVIEW_BYTES = 32 * 1024;
|
|
145513
|
+
PAGING_HINT_LINES = 300;
|
|
145514
|
+
TaskOutputInputSchema = object({
|
|
145515
|
+
task_id: string().describe("The background task ID to inspect."),
|
|
145516
|
+
block: boolean$1().default(false).describe("Whether to wait for the task to finish before returning.").optional(),
|
|
145517
|
+
timeout: number$1().int().min(0).max(3600).default(30).describe("Maximum number of seconds to wait when block=true.").optional()
|
|
145518
|
+
});
|
|
145519
|
+
TaskOutputTool = class {
|
|
145520
|
+
manager;
|
|
145521
|
+
name = "TaskOutput";
|
|
145522
|
+
description = task_output_default;
|
|
145523
|
+
parameters = toInputJsonSchema(TaskOutputInputSchema);
|
|
145524
|
+
constructor(manager) {
|
|
145525
|
+
this.manager = manager;
|
|
145526
|
+
}
|
|
145527
|
+
resolveExecution(args) {
|
|
145528
|
+
return {
|
|
145529
|
+
description: `Reading output of task ${args.task_id}`,
|
|
145530
|
+
approvalRule: this.name,
|
|
145531
|
+
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
|
|
145532
|
+
execute: () => this.execute(args)
|
|
145533
|
+
};
|
|
145534
|
+
}
|
|
145535
|
+
async execute(args) {
|
|
145536
|
+
const info = this.manager.getTask(args.task_id);
|
|
145537
|
+
if (!info) return {
|
|
145538
|
+
isError: true,
|
|
145539
|
+
output: `Task not found: ${args.task_id}`
|
|
145540
|
+
};
|
|
145541
|
+
if (args.block && !isBackgroundTaskTerminal(info.status)) await this.manager.wait(args.task_id, (args.timeout ?? 30) * 1e3);
|
|
145542
|
+
const current = this.manager.getTask(args.task_id);
|
|
145543
|
+
if (!current) return {
|
|
145544
|
+
isError: true,
|
|
145545
|
+
output: `Task not found: ${args.task_id}`
|
|
145546
|
+
};
|
|
145547
|
+
const output = await this.manager.getOutputSnapshot(args.task_id, OUTPUT_PREVIEW_BYTES);
|
|
145548
|
+
const lines = [formatPlainObject({
|
|
145549
|
+
retrievalStatus: retrievalStatus(current.status, args.block),
|
|
145550
|
+
...current,
|
|
145551
|
+
outputPath: output.outputPath,
|
|
145552
|
+
terminalReason: terminalReason(current),
|
|
145553
|
+
outputSizeBytes: output.outputSizeBytes,
|
|
145554
|
+
outputPreviewBytes: output.previewBytes,
|
|
145555
|
+
outputTruncated: output.truncated,
|
|
145556
|
+
fullOutputAvailable: output.fullOutputAvailable,
|
|
145557
|
+
fullOutputTool: output.fullOutputAvailable && output.outputPath !== void 0 ? "Read" : void 0,
|
|
145558
|
+
fullOutputHint: fullOutputHint(output)
|
|
145559
|
+
}), ""];
|
|
145560
|
+
if (output.truncated) lines.push(output.fullOutputAvailable && output.outputPath !== void 0 ? `[Truncated. Full output: ${output.outputPath}]` : "[Truncated. No persisted full log is available for this task.]");
|
|
145561
|
+
lines.push("[output]", output.preview || "[no output available]");
|
|
145562
|
+
return {
|
|
145563
|
+
output: lines.join("\n"),
|
|
145564
|
+
isError: false,
|
|
145565
|
+
message: "Task snapshot retrieved."
|
|
145566
|
+
};
|
|
145567
|
+
}
|
|
145568
|
+
};
|
|
145569
|
+
}));
|
|
145570
|
+
//#endregion
|
|
145571
|
+
//#region ../../packages/agent-core/src/tools/background/task-stop.md?raw
|
|
145572
|
+
var task_stop_default;
|
|
145573
|
+
var init_task_stop$1 = __esmMin((() => {
|
|
145574
|
+
task_stop_default = "Stop a running background task.\n\nOnly use this when a task must genuinely be cancelled — for a task that is\nfinishing normally, wait for its completion notification or inspect it with\n`TaskOutput` instead of stopping it.\n\nGuidelines:\n- This is a general-purpose stop capability for any background task. It is not\n a bash-specific kill.\n- Stopping a task is destructive: it may leave partial side effects behind.\n Use it with care.\n- If the task has already finished, this tool simply returns its current\n status.\n";
|
|
145575
|
+
}));
|
|
145576
|
+
//#endregion
|
|
145577
|
+
//#region ../../packages/agent-core/src/tools/background/task-stop.ts
|
|
145578
|
+
function terminalStopReason(reason) {
|
|
145579
|
+
const trimmed = reason?.trim();
|
|
145580
|
+
return trimmed === void 0 || trimmed.length === 0 ? "Task already in terminal state" : trimmed;
|
|
145581
|
+
}
|
|
145582
|
+
var TaskStopInputSchema, TaskStopTool;
|
|
145583
|
+
var init_task_stop = __esmMin((() => {
|
|
145584
|
+
init_zod$1();
|
|
145585
|
+
init_background();
|
|
145586
|
+
init_input_schema();
|
|
145587
|
+
init_rule_match();
|
|
145588
|
+
init_task_stop$1();
|
|
145589
|
+
TaskStopInputSchema = object({
|
|
145590
|
+
task_id: string().describe("The background task ID to stop."),
|
|
145591
|
+
reason: string().default("Stopped by TaskStop").describe("Short reason recorded when the task is stopped.").optional()
|
|
145592
|
+
});
|
|
145593
|
+
TaskStopTool = class {
|
|
145594
|
+
manager;
|
|
145595
|
+
name = "TaskStop";
|
|
145596
|
+
description = task_stop_default;
|
|
145597
|
+
parameters = toInputJsonSchema(TaskStopInputSchema);
|
|
145598
|
+
constructor(manager) {
|
|
145599
|
+
this.manager = manager;
|
|
145600
|
+
}
|
|
145601
|
+
resolveExecution(args) {
|
|
145602
|
+
return {
|
|
145603
|
+
description: `Stopping task ${args.task_id}`,
|
|
145604
|
+
approvalRule: this.name,
|
|
145605
|
+
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
|
|
145606
|
+
execute: async () => {
|
|
145607
|
+
const info = this.manager.getTask(args.task_id);
|
|
145608
|
+
if (!info) return {
|
|
145609
|
+
isError: true,
|
|
145610
|
+
output: `Task not found: ${args.task_id}`
|
|
145611
|
+
};
|
|
145612
|
+
const trimmedReason = args.reason?.trim();
|
|
145613
|
+
const reason = trimmedReason === void 0 || trimmedReason.length === 0 ? "Stopped by TaskStop" : trimmedReason;
|
|
145614
|
+
if (isBackgroundTaskTerminal(info.status)) return {
|
|
145615
|
+
output: `task_id: ${info.taskId}\nstatus: ${info.status}\nreason: ${terminalStopReason(info.stopReason)}`,
|
|
145616
|
+
isError: false
|
|
145617
|
+
};
|
|
145618
|
+
await this.manager.suppressTerminalNotification(args.task_id);
|
|
145619
|
+
const result = await this.manager.stop(args.task_id, reason);
|
|
145620
|
+
if (!result) return {
|
|
145621
|
+
isError: true,
|
|
145622
|
+
output: `Failed to stop task: ${args.task_id}`
|
|
145623
|
+
};
|
|
145624
|
+
return {
|
|
145625
|
+
output: `task_id: ${result.taskId}\nstatus: ${result.status}\nreason: ${result.stopReason ?? reason}`,
|
|
145626
|
+
isError: false
|
|
145627
|
+
};
|
|
145628
|
+
}
|
|
145629
|
+
};
|
|
145630
|
+
}
|
|
145631
|
+
};
|
|
145632
|
+
}));
|
|
145633
|
+
//#endregion
|
|
145634
|
+
//#region ../../packages/agent-core/src/tools/cron/time-format.ts
|
|
145635
|
+
/**
|
|
145636
|
+
* Render cron-facing timestamps in local wall time with an explicit
|
|
145637
|
+
* numeric offset. Cron expressions are evaluated in local time, so the
|
|
145638
|
+
* tool output should preserve that mental model while remaining
|
|
145639
|
+
* unambiguous and parseable as ISO 8601.
|
|
145640
|
+
*/
|
|
145641
|
+
function formatLocalIsoWithOffset(ms) {
|
|
145642
|
+
const date = new Date(ms);
|
|
145643
|
+
const offsetMin = -date.getTimezoneOffset();
|
|
145644
|
+
const sign = offsetMin >= 0 ? "+" : "-";
|
|
145645
|
+
const absOffset = Math.abs(offsetMin);
|
|
145646
|
+
const offset = `${sign}${pad(Math.floor(absOffset / 60))}:${pad(absOffset % 60)}`;
|
|
145647
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${String(date.getMilliseconds()).padStart(3, "0")}${offset}`;
|
|
145648
|
+
}
|
|
145649
|
+
function pad(n) {
|
|
145650
|
+
return String(n).padStart(2, "0");
|
|
145651
|
+
}
|
|
145652
|
+
var init_time_format = __esmMin((() => {}));
|
|
145653
|
+
//#endregion
|
|
145654
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-create.md?raw
|
|
145655
|
+
var cron_create_default;
|
|
145656
|
+
var init_cron_create$1 = __esmMin((() => {
|
|
145657
|
+
cron_create_default = "Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.\n\nUses standard 5-field cron in the user's local timezone: minute hour day-of-month month day-of-week. `0 9 * * *` means 9am local — no timezone conversion needed.\n\n## One-shot tasks (recurring: false)\n\nFor \"remind me at X\" or \"at <time>, do Y\" requests — fire once then auto-delete.\nPin minute/hour/day-of-month/month to specific values:\n \"remind me at 2:30pm today to check the deploy\" → cron: \"30 14 <today_dom> <today_month> *\", recurring: false\n \"tomorrow morning, run the smoke test\" → cron: \"57 8 <tomorrow_dom> <tomorrow_month> *\", recurring: false\n\nOne-shots are best for near-term reminders. A task only fires while its session is still alive (see Session lifetime below), so favor near times — within hours or a few days — rather than scheduling weeks or months ahead.\n\n## Recurring jobs (recurring: true, the default)\n\nFor \"every N minutes\" / \"every hour\" / \"weekdays at 9am\" requests:\n \"*/5 * * * *\" (every 5 min), \"0 * * * *\" (hourly), \"0 9 * * 1-5\" (weekdays at 9am local)\n\n## Avoid the :00 and :30 minute marks when the task allows it\n\nEvery user who asks for \"9am\" gets `0 9`, and every user who asks for \"hourly\" gets `0 *` — which means requests from across the planet land on the API at the same instant. When the user's request is approximate, pick a minute that is NOT 0 or 30:\n \"every morning around 9\" → \"57 8 * * *\" or \"3 9 * * *\" (not \"0 9 * * *\")\n \"hourly\" → \"7 * * * *\" (not \"0 * * * *\")\n \"in an hour or so, remind me to...\" → pick whatever minute you land on, don't round\n\nOnly use minute 0 or 30 when the user names that exact time and clearly means it (\"at 9:00 sharp\", \"at half past\", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.\n\n## Coalesce semantics\n\nFires are delivered only while the session is idle: a fire that comes due during an active turn is held and delivered at the next idle moment, never injected mid-turn.\n\nIf the scheduler slept past multiple ideal fire times (laptop closed, long-running turn, etc.), only **one** fire is delivered when it wakes up. The origin carries `coalescedCount` showing how many ideal fires were collapsed into this single delivery. You should treat `coalescedCount > 1` as \"I missed some checks; only the latest state matters\" rather than running the prompt that many times.\n\n## Cron-fire envelope\n\nWhen a cron task fires, the prompt you scheduled is re-injected wrapped in an XML envelope that exposes the fire context:\n\n```\n<cron-fire jobId=\"...\" cron=\"...\" recurring=\"true|false\" coalescedCount=\"N\" stale=\"true|false\">\n<prompt>\nyour original prompt text, verbatim\n</prompt>\n</cron-fire>\n```\n\nThe envelope is parseable. Use `coalescedCount > 1` to know multiple ideal fires were collapsed into a single delivery (treat as \"only the latest state matters\"), and `stale=\"true\"` as a cue that the task is past its 7-day threshold.\n\n## 7-day stale behavior\n\nRecurring tasks that have been alive for more than 7 days fire one\nfinal time with `stale: true` on the envelope, and the system then\nauto-deletes the task. The flag is the model's notice that this is\nthe last delivery. If the schedule is still wanted, call `CronCreate`\nagain with the same `cron` and `prompt` — that resets `createdAt` and\nstarts a fresh 7-day window. One-shot tasks are never marked stale.\n\n## Jitter behavior\n\nAnti-herd jitter is applied deterministically per task id:\n - Recurring: ideal fire time is shifted **forward** by an offset ≤ min(10% of the cron period, 15 minutes). A `*/5 * * * *` task can drift up to 30s; a `0 9 * * *` task can drift up to 15 minutes.\n - One-shot: only when the ideal fire lands on `:00` or `:30` of the hour, the fire is pulled **earlier** by ≤ 90 seconds. Other minutes pass through unchanged.\n\n## One-shot vs recurring — when to pick which\n\nUse `recurring: false` for \"remind me at X\" style requests, single deadlines, \"in N minutes do Y\", and any task that should not repeat. Use `recurring: true` for periodic polling (CI status, build watchers, scheduled reports), workday rituals, and anything the user explicitly described as recurring.\n\n## Session lifetime\n\nCron tasks live in the current kimi CLI session. When you exit, they\nare persisted under the session homedir; the next `kimi resume` of the\nsame session reloads them and the scheduler resumes from each task's\n`createdAt`. Fire times that fell during the offline window are\ncollapsed into a single delivery via `coalescedCount` (and recurring\ntasks past their 7-day window arrive with `stale: true` as their final\ndelivery).\n\nTasks do **not** carry over into a brand-new session — they are scoped\nto the resumed session id, not to the working directory.\n\n## Limits\n\nA session holds at most 50 live cron tasks; creating one beyond that is rejected. (The `prompt` body is also capped — see its parameter description.) Expressions that never fire within the next 5 years (e.g. `0 0 31 2 *`, an impossible date) are rejected at create time.\n\n## Returned fields\n\n`id` (8-hex), `cron` (the normalized expression), `humanSchedule` (English summary), `recurring`,\n`nextFireAt` (local ISO timestamp with numeric offset, or null). `id` is needed by `CronDelete`.\n\n## Tell the user how to cancel or modify\n\nAfter successfully creating a task, proactively tell the user how they can cancel or modify it later. Users have no direct `/cron` command or self-service UI to manage reminders themselves; they must ask the model to make changes (e.g. \"cancel my 9am reminder\" or \"change my daily check to 10am\"). Include the task `id` in your message so the user can reference it.\n";
|
|
145658
|
+
}));
|
|
145659
|
+
//#endregion
|
|
145660
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-create.ts
|
|
145661
|
+
function formatOutput(o) {
|
|
145662
|
+
return [
|
|
145663
|
+
`id: ${o.id}`,
|
|
145664
|
+
`cron: ${o.cron}`,
|
|
145665
|
+
`humanSchedule: ${o.humanSchedule}`,
|
|
145666
|
+
`recurring: ${String(o.recurring)}`,
|
|
145667
|
+
`nextFireAt: ${o.nextFireAt === null ? "null" : formatLocalIsoWithOffset(o.nextFireAt)}`
|
|
145668
|
+
].join("\n");
|
|
145669
|
+
}
|
|
145670
|
+
var MAX_PROMPT_BYTES, ONE_SHOT_MAX_FUTURE_MS, CronCreateInputSchema, CronCreateTool;
|
|
145671
|
+
var init_cron_create = __esmMin((() => {
|
|
145672
|
+
init_zod$1();
|
|
145673
|
+
init_input_schema();
|
|
145674
|
+
init_rule_match();
|
|
145675
|
+
init_cron_expr();
|
|
145676
|
+
init_jitter();
|
|
145677
|
+
init_time_format();
|
|
145678
|
+
init_cron_create$1();
|
|
145679
|
+
MAX_PROMPT_BYTES = 8 * 1024;
|
|
145680
|
+
ONE_SHOT_MAX_FUTURE_MS = 350 * 24 * 60 * 60 * 1e3;
|
|
145681
|
+
CronCreateInputSchema = object({
|
|
145682
|
+
cron: string().describe("5-field cron expression in local time: \"M H DoM Mon DoW\" (e.g. \"*/5 * * * *\" = every 5 minutes; \"30 14 28 2 *\" = Feb 28 at 2:30pm local — a pinned date like this repeats yearly unless you also pass recurring: false)."),
|
|
145683
|
+
prompt: string().min(1).max(MAX_PROMPT_BYTES).describe("The prompt to enqueue at each fire time. Limited to 8 KiB (UTF-8)."),
|
|
145684
|
+
recurring: boolean$1().optional().default(true).describe("true (default) = fire on every cron match until deleted or auto-expired after 7 days. false = fire once at the next match, then auto-delete. Use false for \"remind me at X\" one-shot requests with pinned minute/hour/dom/month.")
|
|
145685
|
+
});
|
|
145686
|
+
CronCreateTool = class {
|
|
145687
|
+
manager;
|
|
145688
|
+
name = "CronCreate";
|
|
145689
|
+
description = cron_create_default;
|
|
145690
|
+
parameters = toInputJsonSchema(CronCreateInputSchema);
|
|
145691
|
+
constructor(manager) {
|
|
145692
|
+
this.manager = manager;
|
|
145693
|
+
}
|
|
145694
|
+
resolveExecution(args) {
|
|
145695
|
+
if (process.env["KIMI_DISABLE_CRON"] === "1") return {
|
|
145696
|
+
isError: true,
|
|
145697
|
+
output: "Cron scheduling is disabled (KIMI_DISABLE_CRON=1)."
|
|
145698
|
+
};
|
|
145699
|
+
const normalizedCron = args.cron.trim().split(/\s+/).join(" ");
|
|
145700
|
+
let parsed;
|
|
145701
|
+
try {
|
|
145702
|
+
parsed = parseCronExpression(normalizedCron);
|
|
145703
|
+
} catch (err) {
|
|
145704
|
+
return {
|
|
145705
|
+
isError: true,
|
|
145706
|
+
output: `Invalid cron expression: ${err instanceof Error ? err.message : String(err)}`
|
|
145707
|
+
};
|
|
145708
|
+
}
|
|
145709
|
+
const nowAtPrepare = this.manager.clocks.wallNow();
|
|
145710
|
+
if (!hasFireWithinYears(parsed, 5, nowAtPrepare)) return {
|
|
145711
|
+
isError: true,
|
|
145712
|
+
output: `Cron expression ${JSON.stringify(normalizedCron)} has no fire within 5 years; refusing to schedule.`
|
|
145713
|
+
};
|
|
145714
|
+
if (this.manager.store.list().length >= 50) return {
|
|
145715
|
+
isError: true,
|
|
145716
|
+
output: `Cron job cap reached (max ${String(50)} per session).`
|
|
145717
|
+
};
|
|
145718
|
+
const byteLen = Buffer.byteLength(args.prompt, "utf8");
|
|
145719
|
+
if (byteLen > MAX_PROMPT_BYTES) return {
|
|
145720
|
+
isError: true,
|
|
145721
|
+
output: `Prompt exceeds ${String(MAX_PROMPT_BYTES)} bytes (got ${String(byteLen)}).`
|
|
145722
|
+
};
|
|
145723
|
+
const recurring = args.recurring !== false;
|
|
145724
|
+
if (!recurring) {
|
|
145725
|
+
const firstFire = computeNextCronRun(parsed, nowAtPrepare);
|
|
145726
|
+
if (firstFire !== null && firstFire - nowAtPrepare > ONE_SHOT_MAX_FUTURE_MS) return {
|
|
145727
|
+
isError: true,
|
|
145728
|
+
output: `One-shot cron ${JSON.stringify(normalizedCron)} would not fire until ${formatLocalIsoWithOffset(firstFire)} (more than a year out). If you meant "today" or a near date, the pinned day/month has already passed this year — pick a future date or use wildcards.`
|
|
145729
|
+
};
|
|
145730
|
+
}
|
|
145731
|
+
return {
|
|
145732
|
+
description: recurring ? `Scheduling cron ${normalizedCron}` : `Scheduling one-shot ${normalizedCron}`,
|
|
145733
|
+
approvalRule: literalRulePattern(this.name, JSON.stringify({
|
|
145734
|
+
cron: normalizedCron,
|
|
145735
|
+
prompt: args.prompt,
|
|
145736
|
+
recurring
|
|
145737
|
+
})),
|
|
145738
|
+
execute: async () => {
|
|
145739
|
+
const nowMs = this.manager.clocks.wallNow();
|
|
145740
|
+
if (this.manager.store.list().length >= 50) return {
|
|
145741
|
+
isError: true,
|
|
145742
|
+
output: `Cron job cap reached (max ${String(50)} per session).`
|
|
145743
|
+
};
|
|
145744
|
+
const task = this.manager.addTask({
|
|
145745
|
+
cron: normalizedCron,
|
|
145746
|
+
prompt: args.prompt,
|
|
145747
|
+
recurring
|
|
145748
|
+
});
|
|
145749
|
+
const ideal = computeNextCronRun(parsed, nowMs);
|
|
145750
|
+
const nextFireAt = ideal === null ? null : recurring ? jitteredNextCronRunMs(task, parsed, ideal) : oneShotJitteredNextCronRunMs(task, ideal);
|
|
145751
|
+
const humanSchedule = cronToHuman(parsed);
|
|
145752
|
+
this.manager.emitScheduled(task);
|
|
145753
|
+
return {
|
|
145754
|
+
output: formatOutput({
|
|
145755
|
+
id: task.id,
|
|
145756
|
+
cron: normalizedCron,
|
|
145757
|
+
humanSchedule,
|
|
145758
|
+
recurring,
|
|
145759
|
+
nextFireAt
|
|
145760
|
+
}),
|
|
145761
|
+
isError: false,
|
|
145762
|
+
message: `Scheduled cron ${task.id}`
|
|
145763
|
+
};
|
|
145764
|
+
}
|
|
145765
|
+
};
|
|
145766
|
+
}
|
|
145767
|
+
};
|
|
145768
|
+
}));
|
|
145769
|
+
//#endregion
|
|
145770
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-delete.md?raw
|
|
145771
|
+
var cron_delete_default;
|
|
145772
|
+
var init_cron_delete$1 = __esmMin((() => {
|
|
145773
|
+
cron_delete_default = "Cancel a scheduled cron job by id.\n\nUse this tool to remove a cron task previously scheduled with\n`CronCreate`. The `id` is the 8-hex value returned by `CronCreate`, or\nshown in the `id:` column of `CronList` — quote it verbatim, no\nprefix.\n\nBehaviour by task kind:\n\n- **Recurring task** (`recurring: true`): stops all future fires\n immediately. The scheduler picks up the deletion on its next tick.\n- **One-shot task** (`recurring: false`): cancels the pending fire if\n it has not happened yet. One-shots that have already fired\n auto-delete themselves, so calling `CronDelete` on a fired one-shot\n returns \"no cron job with id ...\".\n\nNot-found is reported as an error (not a silent no-op) so you can\ncorrect yourself — typically by calling `CronList` to see which ids\nare actually live, rather than re-trying with the same stale id.\n\nRefresh pattern (use when you want a stale recurring schedule to\ncontinue):\n\nStale recurring tasks are auto-deleted by the system after their final\nfire — there is nothing for `CronDelete` to remove at that point. To\nkeep the schedule running, just call `CronCreate` with the same `cron`\nand `prompt`. Use `CronList`'s `prompt` field to recall the original\ntext after a context compaction.\n\n`CronDelete` remains the right call when you want to cancel a task\nthat is still live (recurring not yet stale, or a one-shot still\npending).\n\nGuidelines:\n\n- Users have no direct `/cron` command or self-service UI to delete\n tasks themselves; they must ask the model to cancel a reminder.\n When deleting on behalf of a user, confirm the action and report\n the result plainly.\n- Cron deletion is irreversible — there is no undo. If you delete the\n wrong task, you must re-create it with `CronCreate`.\n- If the model is unsure which id is current (e.g. after a context\n compaction), call `CronList` first rather than guessing.\n";
|
|
145774
|
+
}));
|
|
145775
|
+
//#endregion
|
|
145776
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-delete.ts
|
|
145777
|
+
var ID_PATTERN, CronDeleteInputSchema, CronDeleteTool;
|
|
145778
|
+
var init_cron_delete = __esmMin((() => {
|
|
145779
|
+
init_zod$1();
|
|
145780
|
+
init_input_schema();
|
|
145781
|
+
init_cron_delete$1();
|
|
145782
|
+
ID_PATTERN = /^[0-9a-f]{8}$/;
|
|
145783
|
+
CronDeleteInputSchema = object({ id: string().describe("The 8-hex cron job id returned by CronCreate / CronList.") });
|
|
145784
|
+
CronDeleteTool = class {
|
|
145785
|
+
manager;
|
|
145786
|
+
name = "CronDelete";
|
|
145787
|
+
description = cron_delete_default;
|
|
145788
|
+
parameters = toInputJsonSchema(CronDeleteInputSchema);
|
|
145789
|
+
constructor(manager) {
|
|
145790
|
+
this.manager = manager;
|
|
145791
|
+
}
|
|
145792
|
+
resolveExecution(args) {
|
|
145793
|
+
if (!ID_PATTERN.test(args.id)) return {
|
|
145794
|
+
isError: true,
|
|
145795
|
+
output: `Invalid cron job id ${JSON.stringify(args.id)} — must be 8 lowercase hex characters.`
|
|
145796
|
+
};
|
|
145797
|
+
return {
|
|
145798
|
+
description: `Deleting cron ${args.id}`,
|
|
145799
|
+
approvalRule: this.name,
|
|
145800
|
+
execute: async () => {
|
|
145801
|
+
if (this.manager.removeTasks([args.id]).length === 0) return {
|
|
145802
|
+
isError: true,
|
|
145803
|
+
output: `No cron job with id ${args.id}.`
|
|
145804
|
+
};
|
|
145805
|
+
this.manager.emitDeleted(args.id);
|
|
145806
|
+
return {
|
|
145807
|
+
output: `Deleted cron job ${args.id}.`,
|
|
145808
|
+
isError: false
|
|
145809
|
+
};
|
|
145810
|
+
}
|
|
145811
|
+
};
|
|
145812
|
+
}
|
|
145813
|
+
};
|
|
145814
|
+
}));
|
|
145815
|
+
//#endregion
|
|
145816
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-list.md?raw
|
|
145817
|
+
var cron_list_default;
|
|
145818
|
+
var init_cron_list$1 = __esmMin((() => {
|
|
145819
|
+
cron_list_default = "List all cron jobs currently scheduled in this session.\n\nUse this tool to see every pending cron task — both recurring jobs and\none-shot reminders — that you (or the user) have scheduled with\n`CronCreate`. The output is the entry point for inspecting scheduled\nwork: it returns a stable id, the original cron expression, a human\nrendering, the next post-jitter fire time, the recurring flag, the\ntask's age in days, and a stale indicator.\n\nEach record carries:\n\n- `id` — the 8-hex task id. Pass this to `CronDelete` to remove the\n task, or quote it in user-facing messages when asking for\n confirmation.\n- `cron` — the verbatim 5-field cron expression as scheduled.\n- `humanSchedule` — plain-English rendering (e.g. `every 5 minutes`).\n- `prompt` — the scheduled prompt text, JSON-encoded so embedded\n newlines stay on one line. Truncated to 200 UTF-8 bytes with\n `…(truncated)` if longer. Use this to recall what a task is for\n after a context compaction, and as the source for the\n `CronCreate` refresh ritual.\n- `nextFireAt` — local ISO timestamp with an explicit numeric offset\n for the next fire **after jitter has been applied**. The actual fire\n may land slightly before or after a round `:00` / `:30` minute mark\n due to herd-avoidance jitter; this is the value the scheduler will\n compare against, so it reflects what will really happen. `null` if\n the expression has no fire in the next 5 years (should not happen\n for tasks created through `CronCreate`, which validates).\n- `recurring` — `true` for cadenced jobs, `false` for one-shots.\n- `ageDays` — `(now - createdAt) / day`, two decimal places. Useful\n when deciding whether a long-running cron is still relevant.\n- `stale` — `true` when a recurring task is older than 7 days. The\n system **auto-deletes the task after this fire** to bound session\n lifetime; the `stale: true` flag is the model's notice that this is\n the final delivery. To resume the same schedule, call `CronCreate`\n again with the original `cron` and `prompt` (the `prompt` row above\n carries it for exactly this purpose). One-shots are never marked\n stale — they fire at most once by construction.\n\nGuidelines:\n\n- This tool is read-only and never mutates state, so it is always\n safe to call (including in plan mode).\n- Users cannot directly manage cron tasks themselves; if they want to\n cancel or modify a schedule, route the request through the model\n (i.e. call `CronDelete` or `CronCreate` on their behalf).\n- The empty case returns `cron_jobs: 0\\nNo cron jobs scheduled.`. Cron\n tasks survive a `kimi resume` of the same session but do not bleed\n into new sessions.\n- After a context compaction, or whenever you are unsure which cron\n jobs are live, call this tool to re-enumerate them rather than\n guessing ids from earlier in the conversation.\n- Records are separated by a line containing just `---`, in the\n insertion order they were scheduled.\n";
|
|
145820
|
+
}));
|
|
145821
|
+
//#endregion
|
|
145822
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-list.ts
|
|
145823
|
+
function previewPrompt(prompt) {
|
|
145824
|
+
const buf = Buffer.from(prompt, "utf8");
|
|
145825
|
+
if (buf.byteLength <= PROMPT_PREVIEW_BYTES) return prompt;
|
|
145826
|
+
let end = PROMPT_PREVIEW_BYTES;
|
|
145827
|
+
while (end > 0 && (buf[end] & 192) === 128) end--;
|
|
145828
|
+
return `${buf.subarray(0, end).toString("utf8")}…(truncated)`;
|
|
145829
|
+
}
|
|
145830
|
+
var CronListInputSchema, MS_PER_DAY, PROMPT_PREVIEW_BYTES, CronListTool;
|
|
145831
|
+
var init_cron_list = __esmMin((() => {
|
|
145832
|
+
init_zod$1();
|
|
145833
|
+
init_input_schema();
|
|
145834
|
+
init_cron_expr();
|
|
145835
|
+
init_time_format();
|
|
145836
|
+
init_cron_list$1();
|
|
145837
|
+
CronListInputSchema = object({}).strict();
|
|
145838
|
+
MS_PER_DAY = 1440 * 60 * 1e3;
|
|
145839
|
+
PROMPT_PREVIEW_BYTES = 200;
|
|
145840
|
+
CronListTool = class {
|
|
145841
|
+
manager;
|
|
145842
|
+
name = "CronList";
|
|
145843
|
+
description = cron_list_default;
|
|
145844
|
+
parameters = toInputJsonSchema(CronListInputSchema);
|
|
145845
|
+
constructor(manager) {
|
|
145846
|
+
this.manager = manager;
|
|
145847
|
+
}
|
|
145848
|
+
resolveExecution(_args) {
|
|
145849
|
+
return {
|
|
145850
|
+
description: "Listing scheduled cron jobs",
|
|
145851
|
+
approvalRule: this.name,
|
|
145852
|
+
execute: async () => {
|
|
145853
|
+
const tasks = this.manager.store.list();
|
|
145854
|
+
const nowMs = this.manager.clocks.wallNow();
|
|
145855
|
+
const records = tasks.map((t) => this.renderRecord(t, nowMs));
|
|
145856
|
+
const header = `cron_jobs: ${String(tasks.length)}`;
|
|
145857
|
+
if (records.length === 0) return {
|
|
145858
|
+
output: `${header}\nNo cron jobs scheduled.`,
|
|
145859
|
+
isError: false
|
|
145860
|
+
};
|
|
145861
|
+
return {
|
|
145862
|
+
output: `${header}\n${records.join("\n---\n")}`,
|
|
145863
|
+
isError: false
|
|
145864
|
+
};
|
|
145865
|
+
}
|
|
145866
|
+
};
|
|
145867
|
+
}
|
|
145868
|
+
renderRecord(task, nowMs) {
|
|
145869
|
+
const recurring = task.recurring !== false;
|
|
145870
|
+
const ageMs = nowMs - task.createdAt;
|
|
145871
|
+
const ageDays = Number.isFinite(ageMs) ? ageMs / MS_PER_DAY : 0;
|
|
145872
|
+
const stale = this.manager.isStale(task);
|
|
145873
|
+
let humanSchedule = task.cron;
|
|
145874
|
+
let nextFireAtIso = "null";
|
|
145875
|
+
try {
|
|
145876
|
+
humanSchedule = cronToHuman(parseCronExpression(task.cron));
|
|
145877
|
+
const nextFireMs = this.manager.getNextFireForTask(task.id);
|
|
145878
|
+
if (nextFireMs !== null) nextFireAtIso = formatLocalIsoWithOffset(nextFireMs);
|
|
145879
|
+
} catch {}
|
|
145880
|
+
return [
|
|
145881
|
+
`id: ${task.id}`,
|
|
145882
|
+
`cron: ${task.cron}`,
|
|
145883
|
+
`humanSchedule: ${humanSchedule}`,
|
|
145884
|
+
`prompt: ${JSON.stringify(previewPrompt(task.prompt))}`,
|
|
145885
|
+
`nextFireAt: ${nextFireAtIso}`,
|
|
145886
|
+
`recurring: ${String(recurring)}`,
|
|
145887
|
+
`ageDays: ${ageDays.toFixed(2)}`,
|
|
145888
|
+
`stale: ${String(stale)}`
|
|
145889
|
+
].join("\n");
|
|
145890
|
+
}
|
|
145891
|
+
};
|
|
145892
|
+
}));
|
|
145893
|
+
//#endregion
|
|
145466
145894
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-disabled.md?raw
|
|
145467
145895
|
var agent_background_disabled_default;
|
|
145468
145896
|
var init_agent_background_disabled = __esmMin((() => {
|
|
@@ -145580,6 +146008,7 @@ var init_agent$1 = __esmMin((() => {
|
|
|
145580
146008
|
this.backgroundManager = backgroundManager;
|
|
145581
146009
|
const log = options?.log;
|
|
145582
146010
|
this.allowBackground = options?.allowBackground ?? true;
|
|
146011
|
+
this.subagentTimeoutMs = options?.subagentTimeoutMs;
|
|
145583
146012
|
const typeLines = buildSubagentDescriptions(subagents);
|
|
145584
146013
|
const baseDescription = `${agent_default}\n\n${this.allowBackground ? agent_background_enabled_default : agent_background_disabled_default}`;
|
|
145585
146014
|
this.description = typeLines ? `${baseDescription}\n\nAvailable agent types (pass via subagent_type):\n${typeLines}` : baseDescription;
|
|
@@ -145587,6 +146016,7 @@ var init_agent$1 = __esmMin((() => {
|
|
|
145587
146016
|
}
|
|
145588
146017
|
log;
|
|
145589
146018
|
allowBackground;
|
|
146019
|
+
subagentTimeoutMs;
|
|
145590
146020
|
async resolveExecution(args) {
|
|
145591
146021
|
let profileName = args.subagent_type?.length ? args.subagent_type : "coder";
|
|
145592
146022
|
const resumeAgentId = args.resume?.trim();
|
|
@@ -145654,7 +146084,7 @@ var init_agent$1 = __esmMin((() => {
|
|
|
145654
146084
|
try {
|
|
145655
146085
|
taskId = this.backgroundManager.registerTask(new AgentBackgroundTask(handle, args.description, this.subagentHost, controller), {
|
|
145656
146086
|
detached: runInBackground,
|
|
145657
|
-
timeoutMs:
|
|
146087
|
+
timeoutMs: this.subagentTimeoutMs ?? 72e5,
|
|
145658
146088
|
signal: runInBackground ? void 0 : signal
|
|
145659
146089
|
});
|
|
145660
146090
|
signal.removeEventListener("abort", abortBeforeRegister);
|
|
@@ -145688,7 +146118,7 @@ var init_agent$1 = __esmMin((() => {
|
|
|
145688
146118
|
if (info?.status === "completed") return { output: formatForegroundAgentSuccess(handle, await this.backgroundManager.readOutput(taskId)) };
|
|
145689
146119
|
const timedOut = info?.status === "timed_out";
|
|
145690
146120
|
return {
|
|
145691
|
-
output: formatForegroundAgentFailure(handle, timedOut ? `Agent timed out after ${
|
|
146121
|
+
output: formatForegroundAgentFailure(handle, timedOut ? `Agent timed out after ${formatSubagentTimeoutDescription(this.subagentTimeoutMs ?? 72e5)}.` : info?.stopReason === "Interrupted by user" ? USER_INTERRUPTED_SUBAGENT_MESSAGE : info?.stopReason !== void 0 ? info.stopReason : "The subagent was stopped before it finished.", timedOut),
|
|
145692
146122
|
isError: true
|
|
145693
146123
|
};
|
|
145694
146124
|
}
|
|
@@ -145802,12 +146232,14 @@ var init_agent_swarm = __esmMin((() => {
|
|
|
145802
146232
|
AgentSwarmTool = class {
|
|
145803
146233
|
subagentHost;
|
|
145804
146234
|
swarmMode;
|
|
146235
|
+
subagentTimeoutMs;
|
|
145805
146236
|
name = "AgentSwarm";
|
|
145806
146237
|
description = agent_swarm_default;
|
|
145807
146238
|
parameters = toInputJsonSchema(AgentSwarmToolInputSchema);
|
|
145808
|
-
constructor(subagentHost, swarmMode) {
|
|
146239
|
+
constructor(subagentHost, swarmMode, subagentTimeoutMs) {
|
|
145809
146240
|
this.subagentHost = subagentHost;
|
|
145810
146241
|
this.swarmMode = swarmMode;
|
|
146242
|
+
this.subagentTimeoutMs = subagentTimeoutMs;
|
|
145811
146243
|
}
|
|
145812
146244
|
resolveExecution(args) {
|
|
145813
146245
|
const agentCount = (args.items?.length ?? 0) + Object.keys(args.resume_agent_ids ?? {}).length;
|
|
@@ -145848,7 +146280,7 @@ var init_agent_swarm = __esmMin((() => {
|
|
|
145848
146280
|
runInBackground: false,
|
|
145849
146281
|
swarmItem: spec.item,
|
|
145850
146282
|
signal,
|
|
145851
|
-
timeout:
|
|
146283
|
+
timeout: this.subagentTimeoutMs ?? 72e5
|
|
145852
146284
|
};
|
|
145853
146285
|
if (spec.kind === "resume") return {
|
|
145854
146286
|
...common,
|
|
@@ -145871,7 +146303,7 @@ var init_agent_swarm = __esmMin((() => {
|
|
|
145871
146303
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md?raw
|
|
145872
146304
|
var ask_user_default;
|
|
145873
146305
|
var init_ask_user$1 = __esmMin((() => {
|
|
145874
|
-
ask_user_default = "Use this tool when you need to ask the user questions with structured options during execution. This allows you to:\n1. Collect user preferences or requirements before proceeding\n2. Resolve ambiguous or underspecified instructions\n3. Let the user decide between implementation approaches as you work\n4. Present concrete options when multiple valid directions exist\n\n**When NOT to use:**\n- When you can infer the answer from context — be decisive and proceed\n- Trivial decisions that don't materially affect the outcome\n\nOverusing this tool interrupts the user's flow. Only use it when the user's input genuinely changes your next action.\n\n**Usage notes:**\n- Users always have an \"Other\" option for custom input — don't create one yourself\n- Use multi_select to allow multiple answers to be selected for a question\n- Keep option labels concise (1-5 words), use descriptions for trade-offs and details\n- Each question should have 2-4 meaningful, distinct options\n- Question texts must be unique across the call, and option labels must be unique within each question\n- You can ask 1-4 questions at a time; group related questions to minimize interruptions\n- If you recommend a specific option, list it first and append \"(Recommended)\" to its label\n- The result is JSON with an `answers` object keyed by question text; each value is the chosen option's label (comma-separated labels for multi_select, or the user's own words if they picked \"Other\"); if `answers` is empty and a `note` says the user dismissed it, they
|
|
146306
|
+
ask_user_default = "Use this tool when you need to ask the user questions with structured options during execution. This allows you to:\n1. Collect user preferences or requirements before proceeding\n2. Resolve ambiguous or underspecified instructions\n3. Let the user decide between implementation approaches as you work\n4. Present concrete options when multiple valid directions exist\n\n**When NOT to use:**\n- When you can infer the answer from context — be decisive and proceed\n- Trivial decisions that don't materially affect the outcome\n\nOverusing this tool interrupts the user's flow. Only use it when the user's input genuinely changes your next action.\n\n**Usage notes:**\n- Users always have an \"Other\" option for custom input — don't create one yourself\n- Use multi_select to allow multiple answers to be selected for a question\n- Keep option labels concise (1-5 words), use descriptions for trade-offs and details\n- Each question should have 2-4 meaningful, distinct options\n- Question texts must be unique across the call, and option labels must be unique within each question\n- You can ask 1-4 questions at a time; group related questions to minimize interruptions\n- If you recommend a specific option, list it first and append \"(Recommended)\" to its label\n- The result is JSON with an `answers` object keyed by question text; each value is the chosen option's label (comma-separated labels for multi_select, or the user's own words if they picked \"Other\"); if `answers` is empty and a `note` says the user dismissed it, they chose not to answer — do not treat this as selecting the recommended option; decide based on context and do not re-ask the same question\n";
|
|
145875
146307
|
}));
|
|
145876
146308
|
//#endregion
|
|
145877
146309
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.ts
|
|
@@ -152113,25 +152545,6 @@ function buildMediaNote(input) {
|
|
|
152113
152545
|
parts.push("If you generate or edit images or videos via commands or scripts, read the result back immediately before continuing.");
|
|
152114
152546
|
return `<system>${parts.join(" ")}</system>`;
|
|
152115
152547
|
}
|
|
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
152548
|
var MAX_MEDIA_MEGABYTES, MAX_MEDIA_BYTES, ReadMediaFileInputSchema, ReadMediaFileTool;
|
|
152136
152549
|
var init_read_media = __esmMin((() => {
|
|
152137
152550
|
init_zod$1();
|
|
@@ -152140,6 +152553,7 @@ var init_read_media = __esmMin((() => {
|
|
|
152140
152553
|
init_path_access();
|
|
152141
152554
|
init_file_type$1();
|
|
152142
152555
|
init_image_compress();
|
|
152556
|
+
init_image_format_policy();
|
|
152143
152557
|
init_image_limits();
|
|
152144
152558
|
init_input_schema();
|
|
152145
152559
|
init_rule_match();
|
|
@@ -152225,9 +152639,9 @@ var init_read_media = __esmMin((() => {
|
|
|
152225
152639
|
isError: true,
|
|
152226
152640
|
output: "The current model does not support image input. Tell the user to use a model with image input capability."
|
|
152227
152641
|
};
|
|
152228
|
-
if (fileType.
|
|
152642
|
+
if (fileType.kind === "image" && !isModelAcceptedImageMime(fileType.mimeType)) return {
|
|
152229
152643
|
isError: true,
|
|
152230
|
-
output:
|
|
152644
|
+
output: buildImageConversionGuidance(args.path, fileType.mimeType, this.kaos.osEnv.osKind)
|
|
152231
152645
|
};
|
|
152232
152646
|
if (fileType.kind === "video" && !this.capabilities.video_in) return {
|
|
152233
152647
|
isError: true,
|
|
@@ -153599,6 +154013,7 @@ var init_tool$1 = __esmMin((() => {
|
|
|
153599
154013
|
init_output();
|
|
153600
154014
|
init_tool_naming();
|
|
153601
154015
|
init_profile();
|
|
154016
|
+
init_subagent_host();
|
|
153602
154017
|
init_skill$4();
|
|
153603
154018
|
init_llm_request_logger();
|
|
153604
154019
|
init_builtin();
|
|
@@ -154155,9 +154570,10 @@ var init_tool$1 = __esmMin((() => {
|
|
|
154155
154570
|
this.agent.skills?.registry.listInvocableSkills().length && new SkillTool(this.agent),
|
|
154156
154571
|
this.agent.subagentHost && new AgentTool(this.agent.subagentHost, background, DEFAULT_AGENT_PROFILES["agent"]?.subagents, {
|
|
154157
154572
|
allowBackground,
|
|
154158
|
-
log: this.agent.log
|
|
154573
|
+
log: this.agent.log,
|
|
154574
|
+
subagentTimeoutMs: resolveSubagentTimeoutMs(this.agent.kimiConfig?.subagent?.timeoutMs)
|
|
154159
154575
|
}),
|
|
154160
|
-
this.agent.subagentHost && new AgentSwarmTool(this.agent.subagentHost, this.agent.swarmMode),
|
|
154576
|
+
this.agent.subagentHost && new AgentSwarmTool(this.agent.subagentHost, this.agent.swarmMode, resolveSubagentTimeoutMs(this.agent.kimiConfig?.subagent?.timeoutMs)),
|
|
154161
154577
|
toolServices?.webSearcher && new WebSearchTool(toolServices.webSearcher),
|
|
154162
154578
|
toolServices?.urlFetcher && new FetchURLTool(toolServices.urlFetcher)
|
|
154163
154579
|
].filter((tool) => !!tool).map((tool) => [tool.name, tool]));
|
|
@@ -154723,6 +155139,7 @@ var init_turn = __esmMin((() => {
|
|
|
154723
155139
|
init_errors$8();
|
|
154724
155140
|
init_errors$4();
|
|
154725
155141
|
init_loop();
|
|
155142
|
+
init_image_compress();
|
|
154726
155143
|
init_abort();
|
|
154727
155144
|
init_context$2();
|
|
154728
155145
|
init_hooks();
|
|
@@ -154792,27 +155209,29 @@ var init_turn = __esmMin((() => {
|
|
|
154792
155209
|
return this.agent.homedir ? basename$2(this.agent.homedir) : this.agent.type;
|
|
154793
155210
|
}
|
|
154794
155211
|
prompt(input, origin = USER_PROMPT_ORIGIN) {
|
|
155212
|
+
const gated = gateImageFormatParts(input);
|
|
154795
155213
|
this.agent.records.logRecord({
|
|
154796
155214
|
type: "turn.prompt",
|
|
154797
|
-
input,
|
|
155215
|
+
input: gated,
|
|
154798
155216
|
origin
|
|
154799
155217
|
});
|
|
154800
|
-
return this.launch(
|
|
155218
|
+
return this.launch(gated, origin);
|
|
154801
155219
|
}
|
|
154802
155220
|
steer(input, origin = USER_PROMPT_ORIGIN) {
|
|
155221
|
+
const gated = gateImageFormatParts(input);
|
|
154803
155222
|
this.agent.records.logRecord({
|
|
154804
155223
|
type: "turn.steer",
|
|
154805
|
-
input,
|
|
155224
|
+
input: gated,
|
|
154806
155225
|
origin
|
|
154807
155226
|
});
|
|
154808
155227
|
if (this.activeTurn || this.agent.fullCompaction.isCompacting) {
|
|
154809
155228
|
this.steerBuffer.push({
|
|
154810
|
-
input,
|
|
155229
|
+
input: gated,
|
|
154811
155230
|
origin
|
|
154812
155231
|
});
|
|
154813
155232
|
return null;
|
|
154814
155233
|
}
|
|
154815
|
-
return this.launch(
|
|
155234
|
+
return this.launch(gated, origin);
|
|
154816
155235
|
}
|
|
154817
155236
|
retry(trigger) {
|
|
154818
155237
|
return this.prompt([], {
|
|
@@ -155245,6 +155664,7 @@ var init_turn = __esmMin((() => {
|
|
|
155245
155664
|
buildMessages: () => this.agent.context.messages,
|
|
155246
155665
|
buildMessagesStrict: () => this.agent.context.strictMessages,
|
|
155247
155666
|
buildMessagesMediaDegraded: () => this.agent.context.mediaDegradedMessages,
|
|
155667
|
+
buildMessagesMediaStripped: () => this.agent.context.mediaStrippedMessages,
|
|
155248
155668
|
dispatchEvent: this.buildDispatchEvent(turnId),
|
|
155249
155669
|
buildTools: () => this.agent.tools.loopTools,
|
|
155250
155670
|
describeMissingTool: (name) => this.agent.tools.missingToolMessage(name),
|
|
@@ -156018,17 +156438,18 @@ var init_agent = __esmMin((() => {
|
|
|
156018
156438
|
}
|
|
156019
156439
|
/**
|
|
156020
156440
|
* Single decision point for select_tools progressive disclosure. All three
|
|
156021
|
-
* gates must be open: the model
|
|
156022
|
-
*
|
|
156023
|
-
*
|
|
156024
|
-
*
|
|
156025
|
-
*
|
|
156026
|
-
*
|
|
156027
|
-
*
|
|
156441
|
+
* gates must be open: the model has the `dynamically_loaded_tools`
|
|
156442
|
+
* capability (message-level tool declarations), the model declares
|
|
156443
|
+
* `tool_use` (a model without tool use loading tools dynamically is a
|
|
156444
|
+
* contradiction), and the `tool-select` experimental flag is on. Every
|
|
156445
|
+
* consumer — top-level tools[] convergence, select_tools registration,
|
|
156446
|
+
* manifest announcements, projection shaping — reads this instead of
|
|
156447
|
+
* re-deriving the conditions, so degradation is lossless: any closed gate
|
|
156448
|
+
* reproduces the inline behavior byte-for-byte.
|
|
156028
156449
|
*/
|
|
156029
156450
|
get toolSelectEnabled() {
|
|
156030
156451
|
const capability = this.config.modelCapabilities;
|
|
156031
|
-
return capability.
|
|
156452
|
+
return capability.dynamically_loaded_tools === true && capability.tool_use && this.experimentalFlags.enabled("tool-select");
|
|
156032
156453
|
}
|
|
156033
156454
|
get generate() {
|
|
156034
156455
|
return async (provider, systemPrompt, tools, history, callbacks, options) => {
|
|
@@ -156260,6 +156681,7 @@ var init_agent = __esmMin((() => {
|
|
|
156260
156681
|
pauseGoal: () => this.goal.pauseGoal(),
|
|
156261
156682
|
resumeGoal: () => this.goal.resumeGoal(),
|
|
156262
156683
|
cancelGoal: () => this.goal.cancelGoal(),
|
|
156684
|
+
getCronTasks: () => ({ tasks: this.cron?.listTaskSnapshots() ?? [] }),
|
|
156263
156685
|
getBackgroundOutput: (payload) => this.background.readOutput(payload.taskId, payload.tail),
|
|
156264
156686
|
getContext: () => this.context.data(),
|
|
156265
156687
|
getConfig: () => this.config.data(),
|
|
@@ -187111,6 +187533,8 @@ var init_session$1 = __esmMin((() => {
|
|
|
187111
187533
|
};
|
|
187112
187534
|
writeMetadataPromise = Promise.resolve();
|
|
187113
187535
|
agentsMdWarning;
|
|
187536
|
+
printSteerDeadline;
|
|
187537
|
+
printSteerTurns = 0;
|
|
187114
187538
|
constructor(options) {
|
|
187115
187539
|
this.options = options;
|
|
187116
187540
|
this.logHandle = options.id === void 0 ? void 0 : getRootLogger().attachSession({
|
|
@@ -187297,23 +187721,18 @@ var init_session$1 = __esmMin((() => {
|
|
|
187297
187721
|
* Wait for all still-running background tasks (across every agent) to reach a
|
|
187298
187722
|
* terminal state before a `kimi -p` (print) run exits.
|
|
187299
187723
|
*
|
|
187300
|
-
*
|
|
187301
|
-
*
|
|
187302
|
-
*
|
|
187303
|
-
*
|
|
187724
|
+
* Only runs when the resolved print background mode is `'drain'` (see
|
|
187725
|
+
* `resolvePrintBackgroundMode`): `print_background_mode = "drain"`, or the
|
|
187726
|
+
* legacy `keep_alive_on_exit = true` fallback. In every other mode it returns
|
|
187727
|
+
* immediately. The wait is bounded by `background.print_wait_ceiling_s`
|
|
187728
|
+
* (default 3600s) so a wedged task cannot keep the process alive forever.
|
|
187304
187729
|
*
|
|
187305
187730
|
* Terminal notifications are suppressed for each task while we wait, so a task
|
|
187306
187731
|
* completing cannot `turn.steer` the (already finished) main agent into launching
|
|
187307
|
-
* a new turn.
|
|
187732
|
+
* a new turn. (This is exactly what `'steer'` mode avoids by never calling here.)
|
|
187308
187733
|
*/
|
|
187309
187734
|
async waitForBackgroundTasksOnPrint() {
|
|
187310
|
-
if (
|
|
187311
|
-
env: process.env,
|
|
187312
|
-
envKey: BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV,
|
|
187313
|
-
configValue: this.options.background?.keepAliveOnExit,
|
|
187314
|
-
defaultValue: false,
|
|
187315
|
-
parseEnv: parseBooleanEnv
|
|
187316
|
-
})) return;
|
|
187735
|
+
if (this.resolvePrintBackgroundMode() !== "drain") return;
|
|
187317
187736
|
const timeoutMs = (this.options.background?.printWaitCeilingS ?? 3600) * 1e3;
|
|
187318
187737
|
const deadline = Date.now() + timeoutMs;
|
|
187319
187738
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -187349,6 +187768,67 @@ var init_session$1 = __esmMin((() => {
|
|
|
187349
187768
|
});
|
|
187350
187769
|
}
|
|
187351
187770
|
}
|
|
187771
|
+
/**
|
|
187772
|
+
* Resolve the effective print-mode (`kimi -p`) background-task policy.
|
|
187773
|
+
*
|
|
187774
|
+
* `background.print_background_mode` is authoritative when set. Otherwise we
|
|
187775
|
+
* fall back to the legacy `background.keep_alive_on_exit` mapping so existing
|
|
187776
|
+
* configs keep their behavior: `keep_alive_on_exit = true` ⇒ `'drain'`
|
|
187777
|
+
* (suppress + drain background tasks before exit), otherwise `'exit'`.
|
|
187778
|
+
*/
|
|
187779
|
+
resolvePrintBackgroundMode() {
|
|
187780
|
+
const configured = this.options.background?.printBackgroundMode;
|
|
187781
|
+
if (configured !== void 0) return configured;
|
|
187782
|
+
return resolveConfigValue({
|
|
187783
|
+
env: process.env,
|
|
187784
|
+
envKey: BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV,
|
|
187785
|
+
configValue: this.options.background?.keepAliveOnExit,
|
|
187786
|
+
defaultValue: false,
|
|
187787
|
+
parseEnv: parseBooleanEnv
|
|
187788
|
+
}) ? "drain" : "exit";
|
|
187789
|
+
}
|
|
187790
|
+
countActiveBackgroundTasks() {
|
|
187791
|
+
let count = 0;
|
|
187792
|
+
for (const agent of this.readyAgents()) count += agent.background.list(true).length;
|
|
187793
|
+
return count;
|
|
187794
|
+
}
|
|
187795
|
+
/**
|
|
187796
|
+
* Decide what the `kimi -p` driver should do after the main agent's turn ends
|
|
187797
|
+
* with `reason === 'completed'`. Returns `'finish'` when the run may exit, or
|
|
187798
|
+
* `'continue'` when the driver must stay alive so a background-task completion
|
|
187799
|
+
* can `turn.steer` the main agent into a new turn.
|
|
187800
|
+
*
|
|
187801
|
+
* - 'exit' : finish immediately (default).
|
|
187802
|
+
* - 'drain' : suppress + drain background tasks, then finish (legacy
|
|
187803
|
+
* `keep_alive_on_exit = true` behavior).
|
|
187804
|
+
* - 'steer' : while background tasks are still pending, return 'continue' so
|
|
187805
|
+
* completions steer new main turns; finish once quiescent, or when
|
|
187806
|
+
* the wall-clock ceiling (`print_wait_ceiling_s`) or the turn cap
|
|
187807
|
+
* (`print_max_turns`) is reached.
|
|
187808
|
+
*/
|
|
187809
|
+
async handlePrintMainTurnCompleted() {
|
|
187810
|
+
const mode = this.resolvePrintBackgroundMode();
|
|
187811
|
+
if (mode === "exit") return "finish";
|
|
187812
|
+
if (mode === "drain") {
|
|
187813
|
+
await this.waitForBackgroundTasksOnPrint();
|
|
187814
|
+
return "finish";
|
|
187815
|
+
}
|
|
187816
|
+
const ceilingS = this.options.background?.printWaitCeilingS ?? 3600;
|
|
187817
|
+
const maxTurns = this.options.background?.printMaxTurns ?? 50;
|
|
187818
|
+
const now = Date.now();
|
|
187819
|
+
this.printSteerDeadline ??= now + ceilingS * 1e3;
|
|
187820
|
+
this.printSteerTurns += 1;
|
|
187821
|
+
if (now >= this.printSteerDeadline) {
|
|
187822
|
+
this.log.warn("print steer ceiling reached, finishing", { ceilingS });
|
|
187823
|
+
return "finish";
|
|
187824
|
+
}
|
|
187825
|
+
if (this.printSteerTurns > maxTurns) {
|
|
187826
|
+
this.log.warn("print steer max turns reached, finishing", { maxTurns });
|
|
187827
|
+
return "finish";
|
|
187828
|
+
}
|
|
187829
|
+
if (this.countActiveBackgroundTasks() > 0) return "continue";
|
|
187830
|
+
return "finish";
|
|
187831
|
+
}
|
|
187352
187832
|
async createAgent(config, options = {}) {
|
|
187353
187833
|
await this.skillsReady;
|
|
187354
187834
|
const type = config.type ?? "main";
|
|
@@ -205850,7 +206330,9 @@ var init_model_alias_merge = __esmMin((() => {
|
|
|
205850
206330
|
"model",
|
|
205851
206331
|
"maxContextSize",
|
|
205852
206332
|
"capabilities",
|
|
205853
|
-
"displayName"
|
|
206333
|
+
"displayName",
|
|
206334
|
+
"supportEfforts",
|
|
206335
|
+
"defaultEffort"
|
|
205854
206336
|
]);
|
|
205855
206337
|
}));
|
|
205856
206338
|
//#endregion
|
|
@@ -206578,6 +207060,10 @@ function toModelEntry(value) {
|
|
|
206578
207060
|
}
|
|
206579
207061
|
if (typeof value["tool_call"] === "boolean") entry.tool_call = value["tool_call"];
|
|
206580
207062
|
if (typeof value["reasoning"] === "boolean") entry.reasoning = value["reasoning"];
|
|
207063
|
+
const supportEfforts = toStringArrayOrUndefined(value["support_efforts"]);
|
|
207064
|
+
if (supportEfforts !== void 0) entry.support_efforts = supportEfforts;
|
|
207065
|
+
const defaultEffort = value["default_effort"];
|
|
207066
|
+
if (typeof defaultEffort === "string" && defaultEffort.length > 0) entry.default_effort = defaultEffort;
|
|
206581
207067
|
const modalities = value["modalities"];
|
|
206582
207068
|
if (isRecord$10(modalities)) {
|
|
206583
207069
|
const input = toStringArrayOrUndefined(modalities["input"]);
|
|
@@ -206651,7 +207137,7 @@ async function fetchCustomRegistry(source, fetchImpl = fetch, signal) {
|
|
|
206651
207137
|
function capabilitiesFromCustomEntry(model) {
|
|
206652
207138
|
const caps = /* @__PURE__ */ new Set();
|
|
206653
207139
|
if (model.tool_call === true) caps.add("tool_use");
|
|
206654
|
-
if (model.reasoning === true) caps.add("thinking");
|
|
207140
|
+
if (model.reasoning === true || (model.support_efforts?.length ?? 0) > 0) caps.add("thinking");
|
|
206655
207141
|
if (model.modalities?.input?.includes("image") === true) caps.add("image_in");
|
|
206656
207142
|
if (model.modalities?.input?.includes("video") === true) caps.add("video_in");
|
|
206657
207143
|
if (model.modalities?.output?.includes("image") === true) caps.add("image_out");
|
|
@@ -206659,7 +207145,7 @@ function capabilitiesFromCustomEntry(model) {
|
|
|
206659
207145
|
return [...caps];
|
|
206660
207146
|
}
|
|
206661
207147
|
function hasRichCapabilityHints(model) {
|
|
206662
|
-
return typeof model.tool_call === "boolean" || typeof model.reasoning === "boolean" || model.modalities !== void 0;
|
|
207148
|
+
return typeof model.tool_call === "boolean" || typeof model.reasoning === "boolean" || model.modalities !== void 0 || model.support_efforts !== void 0;
|
|
206663
207149
|
}
|
|
206664
207150
|
function resolveMaxContextSize(model) {
|
|
206665
207151
|
const context = model.limit?.context;
|
|
@@ -206701,7 +207187,9 @@ function applyCustomRegistryProvider(config, entry, source) {
|
|
|
206701
207187
|
model: model.id,
|
|
206702
207188
|
maxContextSize,
|
|
206703
207189
|
capabilities,
|
|
206704
|
-
displayName
|
|
207190
|
+
displayName,
|
|
207191
|
+
...model.support_efforts !== void 0 ? { supportEfforts: model.support_efforts } : {},
|
|
207192
|
+
...model.default_effort !== void 0 ? { defaultEffort: model.default_effort } : {}
|
|
206705
207193
|
}, CUSTOM_REGISTRY_MODEL_FIELDS);
|
|
206706
207194
|
}
|
|
206707
207195
|
config.models = existingModels;
|
|
@@ -207450,7 +207938,7 @@ function resolveModelCapabilities(alias, provider) {
|
|
|
207450
207938
|
thinking: declared.has("thinking") || declared.has("always_thinking") || detected.thinking,
|
|
207451
207939
|
tool_use: declared.has("tool_use") || detected.tool_use,
|
|
207452
207940
|
max_context_tokens: alias.maxContextSize,
|
|
207453
|
-
|
|
207941
|
+
dynamically_loaded_tools: declared.has("dynamically_loaded_tools") || detected.dynamically_loaded_tools === true
|
|
207454
207942
|
};
|
|
207455
207943
|
}
|
|
207456
207944
|
function toKosongProviderConfig(provider, model, modelProtocol, kimiRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey, adaptiveThinking, betaApi, supportEfforts) {
|
|
@@ -207793,6 +208281,9 @@ var init_rpc$1 = __esmMin((() => {
|
|
|
207793
208281
|
waitForBackgroundTasksOnPrint(_payload) {
|
|
207794
208282
|
return this.session.waitForBackgroundTasksOnPrint();
|
|
207795
208283
|
}
|
|
208284
|
+
handlePrintMainTurnCompleted(_payload) {
|
|
208285
|
+
return this.session.handlePrintMainTurnCompleted();
|
|
208286
|
+
}
|
|
207796
208287
|
addAdditionalDir(payload) {
|
|
207797
208288
|
return this.session.addAdditionalDir(payload.path, payload.persist);
|
|
207798
208289
|
}
|
|
@@ -207895,6 +208386,9 @@ var init_rpc$1 = __esmMin((() => {
|
|
|
207895
208386
|
async cancelGoal({ agentId, ...payload }) {
|
|
207896
208387
|
return (await this.getAgent(agentId)).cancelGoal(payload);
|
|
207897
208388
|
}
|
|
208389
|
+
async getCronTasks({ agentId, ...payload }) {
|
|
208390
|
+
return (await this.getAgent(agentId)).getCronTasks(payload);
|
|
208391
|
+
}
|
|
207898
208392
|
async getBackgroundOutput({ agentId, ...payload }) {
|
|
207899
208393
|
return (await this.getAgent(agentId)).getBackgroundOutput(payload);
|
|
207900
208394
|
}
|
|
@@ -209687,6 +210181,9 @@ var init_core_impl = __esmMin((() => {
|
|
|
209687
210181
|
waitForBackgroundTasksOnPrint({ sessionId, ...payload }) {
|
|
209688
210182
|
return this.sessionApi(sessionId).waitForBackgroundTasksOnPrint(payload);
|
|
209689
210183
|
}
|
|
210184
|
+
handlePrintMainTurnCompleted({ sessionId, ...payload }) {
|
|
210185
|
+
return this.sessionApi(sessionId).handlePrintMainTurnCompleted(payload);
|
|
210186
|
+
}
|
|
209690
210187
|
addAdditionalDir({ sessionId, ...payload }) {
|
|
209691
210188
|
return this.requireSession(sessionId).addAdditionalDir(payload.path, payload.persist);
|
|
209692
210189
|
}
|
|
@@ -209708,6 +210205,9 @@ var init_core_impl = __esmMin((() => {
|
|
|
209708
210205
|
cancelGoal({ sessionId, ...payload }) {
|
|
209709
210206
|
return Promise.resolve(this.sessionApi(sessionId).cancelGoal(payload));
|
|
209710
210207
|
}
|
|
210208
|
+
getCronTasks({ sessionId, ...payload }) {
|
|
210209
|
+
return Promise.resolve(this.sessionApi(sessionId).getCronTasks(payload));
|
|
210210
|
+
}
|
|
209711
210211
|
async installPlugin(payload) {
|
|
209712
210212
|
await this.pluginsReady;
|
|
209713
210213
|
this.assertPluginsLoaded();
|
|
@@ -216461,25 +216961,29 @@ function mapContentPart(part) {
|
|
|
216461
216961
|
* Build the protocol-shaped `Message.content[]` for one ContextMessage.
|
|
216462
216962
|
*
|
|
216463
216963
|
* Order:
|
|
216464
|
-
* 1. For `tool` role: emit a SINGLE `tool_result` part.
|
|
216465
|
-
*
|
|
216466
|
-
*
|
|
216964
|
+
* 1. For `tool` role: emit a SINGLE `tool_result` part. Plain-text results
|
|
216965
|
+
* keep the historical flattened-text output (most tool messages emit a
|
|
216966
|
+
* single text); a result that carries media parts (image/video/audio —
|
|
216967
|
+
* e.g. ReadMediaFile) passes the raw kosong content-part array through
|
|
216968
|
+
* instead, the same shape the live `tool.result` event stream carries,
|
|
216969
|
+
* so REST consumers can still render the media. `is_error` is taken
|
|
216970
|
+
* from `ContextMessage.isError`.
|
|
216467
216971
|
* 2. For other roles: emit each content part mapped per `mapContentPart`,
|
|
216468
216972
|
* THEN append one `tool_use` part per `ToolCall` (assistant only).
|
|
216469
216973
|
*/
|
|
216470
216974
|
function buildProtocolContent(msg) {
|
|
216471
216975
|
if (msg.role === "tool") {
|
|
216472
216976
|
if (msg.toolCallId === void 0) return msg.content.map((p) => mapContentPart(p));
|
|
216473
|
-
const
|
|
216977
|
+
const output = msg.content.some((p) => p.type === "image_url" || p.type === "video_url" || p.type === "audio_url") ? msg.content : msg.content.map((p) => p.type === "text" ? p.text : "").join("");
|
|
216474
216978
|
return [msg.isError === true ? {
|
|
216475
216979
|
type: "tool_result",
|
|
216476
216980
|
tool_call_id: msg.toolCallId,
|
|
216477
|
-
output
|
|
216981
|
+
output,
|
|
216478
216982
|
is_error: true
|
|
216479
216983
|
} : {
|
|
216480
216984
|
type: "tool_result",
|
|
216481
216985
|
tool_call_id: msg.toolCallId,
|
|
216482
|
-
output
|
|
216986
|
+
output
|
|
216483
216987
|
}];
|
|
216484
216988
|
}
|
|
216485
216989
|
const base = msg.content.map((p) => mapContentPart(p));
|
|
@@ -218888,6 +219392,7 @@ var init_src = __esmMin((() => {
|
|
|
218888
219392
|
init_context$2();
|
|
218889
219393
|
init_tool_result_render();
|
|
218890
219394
|
init_image_compress();
|
|
219395
|
+
init_image_format_policy();
|
|
218891
219396
|
init_image_limits();
|
|
218892
219397
|
init_image_originals();
|
|
218893
219398
|
init_provider_manager();
|
|
@@ -219170,14 +219675,27 @@ var Session = class {
|
|
|
219170
219675
|
/**
|
|
219171
219676
|
* Block until every still-running background task (across all agents in this
|
|
219172
219677
|
* session) reaches a terminal state. Used by `kimi -p` after the main agent's
|
|
219173
|
-
* turn finishes when
|
|
219174
|
-
*
|
|
219175
|
-
*
|
|
219678
|
+
* turn finishes when the resolved print background mode is `'drain'`
|
|
219679
|
+
* (`print_background_mode = "drain"`, or the legacy `keep_alive_on_exit = true`
|
|
219680
|
+
* fallback), so background subagents get a chance to complete before the process
|
|
219681
|
+
* exits. No-op in other modes. Bounded by `background.print_wait_ceiling_s`.
|
|
219176
219682
|
*/
|
|
219177
219683
|
async waitForBackgroundTasksOnPrint() {
|
|
219178
219684
|
this.ensureOpen();
|
|
219179
219685
|
await this.rpc.waitForBackgroundTasksOnPrint({ sessionId: this.id });
|
|
219180
219686
|
}
|
|
219687
|
+
/**
|
|
219688
|
+
* Used by `kimi -p` after the main agent's turn ends with `reason ===
|
|
219689
|
+
* 'completed'`. Returns `'finish'` when the run may exit, or `'continue'` when
|
|
219690
|
+
* the caller must keep the session alive so a background-task completion can
|
|
219691
|
+
* steer the main agent into a new turn. Policy is selected by
|
|
219692
|
+
* `background.print_background_mode` (`'exit' | 'drain' | 'steer'`); when unset
|
|
219693
|
+
* it falls back to the legacy `keep_alive_on_exit` mapping (`true ⇒ 'drain'`).
|
|
219694
|
+
*/
|
|
219695
|
+
async handlePrintMainTurnCompleted() {
|
|
219696
|
+
this.ensureOpen();
|
|
219697
|
+
return this.rpc.handlePrintMainTurnCompleted({ sessionId: this.id });
|
|
219698
|
+
}
|
|
219181
219699
|
async createGoal(input) {
|
|
219182
219700
|
this.ensureOpen();
|
|
219183
219701
|
return this.rpc.createGoal({
|
|
@@ -219201,6 +219719,15 @@ var Session = class {
|
|
|
219201
219719
|
this.ensureOpen();
|
|
219202
219720
|
return this.rpc.cancelGoal({ sessionId: this.id });
|
|
219203
219721
|
}
|
|
219722
|
+
/**
|
|
219723
|
+
* Enumerate the cron tasks scheduled in this session. Hosts running a
|
|
219724
|
+
* bounded session lifetime (e.g. `kimi -p`) poll this to decide whether
|
|
219725
|
+
* pending scheduled work still needs the process alive.
|
|
219726
|
+
*/
|
|
219727
|
+
async getCronTasks() {
|
|
219728
|
+
this.ensureOpen();
|
|
219729
|
+
return this.rpc.getCronTasks({ sessionId: this.id });
|
|
219730
|
+
}
|
|
219204
219731
|
async listMcpServers() {
|
|
219205
219732
|
this.ensureOpen();
|
|
219206
219733
|
return this.rpc.listMcpServers({ sessionId: this.id });
|
|
@@ -220063,6 +220590,9 @@ var SDKRpcClientBase = class {
|
|
|
220063
220590
|
async waitForBackgroundTasksOnPrint(input) {
|
|
220064
220591
|
return (await this.getRpc()).waitForBackgroundTasksOnPrint({ sessionId: input.sessionId });
|
|
220065
220592
|
}
|
|
220593
|
+
async handlePrintMainTurnCompleted(input) {
|
|
220594
|
+
return (await this.getRpc()).handlePrintMainTurnCompleted({ sessionId: input.sessionId });
|
|
220595
|
+
}
|
|
220066
220596
|
async createGoal(input) {
|
|
220067
220597
|
return (await this.getRpc()).createGoal({
|
|
220068
220598
|
sessionId: input.sessionId,
|
|
@@ -220095,6 +220625,12 @@ var SDKRpcClientBase = class {
|
|
|
220095
220625
|
agentId: this.interactiveAgentId
|
|
220096
220626
|
});
|
|
220097
220627
|
}
|
|
220628
|
+
async getCronTasks(input) {
|
|
220629
|
+
return (await this.getRpc()).getCronTasks({
|
|
220630
|
+
sessionId: input.sessionId,
|
|
220631
|
+
agentId: this.interactiveAgentId
|
|
220632
|
+
});
|
|
220633
|
+
}
|
|
220098
220634
|
async listMcpServers(input) {
|
|
220099
220635
|
return (await this.getRpc()).listMcpServers({ sessionId: input.sessionId });
|
|
220100
220636
|
}
|
|
@@ -220423,7 +220959,7 @@ function capabilityToStrings(capability) {
|
|
|
220423
220959
|
if (capability.audio_in) caps.push("audio_in");
|
|
220424
220960
|
if (capability.thinking) caps.push("thinking");
|
|
220425
220961
|
if (capability.tool_use) caps.push("tool_use");
|
|
220426
|
-
if (capability.
|
|
220962
|
+
if (capability.dynamically_loaded_tools === true) caps.push("dynamically_loaded_tools");
|
|
220427
220963
|
return caps.length > 0 ? caps : void 0;
|
|
220428
220964
|
}
|
|
220429
220965
|
/** Builds a kimi-code model alias from a normalized catalog model. */
|
|
@@ -240329,6 +240865,9 @@ function isHideOutputMarker(value) {
|
|
|
240329
240865
|
* Convert an array of ACP {@link ContentBlock}s into the SDK's
|
|
240330
240866
|
* {@link PromptPart} array.
|
|
240331
240867
|
*
|
|
240868
|
+
* Image parts are built from the client-declared MIME verbatim; run the
|
|
240869
|
+
* result through {@link compressPromptImageParts} before submitting so
|
|
240870
|
+
* unsupported formats are dropped and MIME aliases canonicalized.
|
|
240332
240871
|
*/
|
|
240333
240872
|
function acpBlocksToPromptParts(blocks) {
|
|
240334
240873
|
const out = [];
|
|
@@ -240394,6 +240933,13 @@ function acpBlocksToPromptParts(blocks) {
|
|
|
240394
240933
|
* server's upload-time step. Best effort: a part that cannot be compressed is
|
|
240395
240934
|
* passed through unchanged.
|
|
240396
240935
|
*
|
|
240936
|
+
* The format gate (`gateImageFormatParts`) runs first: parts whose MIME is
|
|
240937
|
+
* outside the provider-accepted set are never forwarded — the part is
|
|
240938
|
+
* dropped and a text notice stands in, so one unsupported image cannot
|
|
240939
|
+
* poison the session history; accepted MIME aliases (`image/jpg`,
|
|
240940
|
+
* case/whitespace variants) are rewritten to the canonical form strict
|
|
240941
|
+
* provider whitelists require.
|
|
240942
|
+
*
|
|
240397
240943
|
* Compression is never silent: a re-encoded image gains a caption text part
|
|
240398
240944
|
* immediately before it stating what the original was, and the original bytes
|
|
240399
240945
|
* are persisted (into `originalsDir` — typically the session's
|
|
@@ -240402,7 +240948,7 @@ function acpBlocksToPromptParts(blocks) {
|
|
|
240402
240948
|
*/
|
|
240403
240949
|
async function compressPromptImageParts(parts, options = {}) {
|
|
240404
240950
|
const out = [];
|
|
240405
|
-
for (const part of parts) {
|
|
240951
|
+
for (const part of gateImageFormatParts(parts)) {
|
|
240406
240952
|
if (part.type === "image_url") {
|
|
240407
240953
|
const parsed = parseImageDataUrl(part.imageUrl.url);
|
|
240408
240954
|
if (parsed !== null) {
|
|
@@ -240448,14 +240994,6 @@ async function compressPromptImageParts(parts, options = {}) {
|
|
|
240448
240994
|
}
|
|
240449
240995
|
return out;
|
|
240450
240996
|
}
|
|
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
240997
|
/**
|
|
240460
240998
|
* Minimum-viable XML-attribute escaping for prompt-embedded resource
|
|
240461
240999
|
* wrappers. The output is consumed by an LLM, not parsed by a canonical
|
|
@@ -278086,7 +278624,17 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278086
278624
|
const content = [];
|
|
278087
278625
|
for (const part of body.content) {
|
|
278088
278626
|
if (part.type === "image" && part.source.kind === "base64") {
|
|
278089
|
-
const
|
|
278627
|
+
const effectiveMime = resolveEffectiveImageMime(part.source.media_type, decodeBase64Prefix(part.source.data));
|
|
278628
|
+
if (!isModelAcceptedImageMime(effectiveMime)) {
|
|
278629
|
+
content.push({
|
|
278630
|
+
type: "text",
|
|
278631
|
+
text: buildUnsupportedImageNotice(effectiveMime)
|
|
278632
|
+
});
|
|
278633
|
+
changed = true;
|
|
278634
|
+
continue;
|
|
278635
|
+
}
|
|
278636
|
+
const canonicalMime = normalizeImageMime(effectiveMime);
|
|
278637
|
+
const compressed = await compressBase64ForModel(part.source.data, canonicalMime, {
|
|
278090
278638
|
maxEdge: options.maxImageEdgePx,
|
|
278091
278639
|
telemetry: telemetryFor("prompt_inline")
|
|
278092
278640
|
});
|
|
@@ -278120,9 +278668,31 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278120
278668
|
}
|
|
278121
278669
|
});
|
|
278122
278670
|
changed = true;
|
|
278671
|
+
} else if (canonicalMime !== part.source.media_type) {
|
|
278672
|
+
content.push({
|
|
278673
|
+
...part,
|
|
278674
|
+
source: {
|
|
278675
|
+
...part.source,
|
|
278676
|
+
media_type: canonicalMime
|
|
278677
|
+
}
|
|
278678
|
+
});
|
|
278679
|
+
changed = true;
|
|
278123
278680
|
} else content.push(part);
|
|
278124
278681
|
continue;
|
|
278125
278682
|
}
|
|
278683
|
+
if (part.type === "image" && part.source.kind === "url") {
|
|
278684
|
+
const extMime = unsupportedImageMimeFromUrl(part.source.url);
|
|
278685
|
+
if (extMime !== null) {
|
|
278686
|
+
content.push({
|
|
278687
|
+
type: "text",
|
|
278688
|
+
text: buildUnsupportedImageNotice(extMime, part.source.url)
|
|
278689
|
+
});
|
|
278690
|
+
changed = true;
|
|
278691
|
+
continue;
|
|
278692
|
+
}
|
|
278693
|
+
content.push(part);
|
|
278694
|
+
continue;
|
|
278695
|
+
}
|
|
278126
278696
|
if (part.type !== "image" && part.type !== "video" || part.source.kind !== "file") {
|
|
278127
278697
|
content.push(part);
|
|
278128
278698
|
continue;
|
|
@@ -278142,6 +278712,16 @@ async function resolvePromptMediaFiles(body, store, cacheDir, options = {}) {
|
|
|
278142
278712
|
let mediaType = file.meta.media_type;
|
|
278143
278713
|
let bytes = data;
|
|
278144
278714
|
if (part.type === "image") {
|
|
278715
|
+
mediaType = resolveEffectiveImageMime(mediaType, data);
|
|
278716
|
+
if (!isModelAcceptedImageMime(mediaType)) {
|
|
278717
|
+
content.push({
|
|
278718
|
+
type: "text",
|
|
278719
|
+
text: buildUnsupportedImageNotice(mediaType, file.meta.name)
|
|
278720
|
+
});
|
|
278721
|
+
changed = true;
|
|
278722
|
+
continue;
|
|
278723
|
+
}
|
|
278724
|
+
mediaType = normalizeImageMime(mediaType);
|
|
278145
278725
|
const compressed = await compressImageForModel(data, mediaType, {
|
|
278146
278726
|
maxEdge: options.maxImageEdgePx,
|
|
278147
278727
|
telemetry: telemetryFor("prompt_file")
|
|
@@ -303284,7 +303864,7 @@ const REMOVE_CONFIRM_REMOVE = "remove";
|
|
|
303284
303864
|
const INSTALL_TRUST_EXIT = "exit";
|
|
303285
303865
|
const INSTALL_TRUST_TRUST = "trust";
|
|
303286
303866
|
const ELLIPSIS$6 = "…";
|
|
303287
|
-
const WEB_BRIDGE_URL = "https://www.kimi.com/features/webbridge";
|
|
303867
|
+
const WEB_BRIDGE_URL = "https://www.kimi.com/features/webbridge#local-agent";
|
|
303288
303868
|
const WEB_BRIDGE_ENTRY = {
|
|
303289
303869
|
id: "kimi-webbridge",
|
|
303290
303870
|
displayName: "Kimi WebBridge",
|
|
@@ -370450,7 +371030,7 @@ async function runHeadlessGoal(session, goal, model, outputFormat, stdout, stder
|
|
|
370450
371030
|
if (event.type === "goal.updated" && event.agentId === "main" && event.change?.kind === "completion" && event.snapshot !== null) completedSnapshot = event.snapshot;
|
|
370451
371031
|
});
|
|
370452
371032
|
try {
|
|
370453
|
-
await runPromptTurn(session, goal.objective, outputFormat, stdout, stderr
|
|
371033
|
+
await runPromptTurn(session, goal.objective, outputFormat, stdout, stderr);
|
|
370454
371034
|
} finally {
|
|
370455
371035
|
unsubscribeGoalEvents();
|
|
370456
371036
|
const snapshot = completedSnapshot ?? (await session.getGoal()).goal;
|
|
@@ -370577,17 +371157,26 @@ function signalExitCode(signal) {
|
|
|
370577
371157
|
if (signal === "SIGHUP") return 129;
|
|
370578
371158
|
return 143;
|
|
370579
371159
|
}
|
|
370580
|
-
function runPromptTurn(session, prompt, outputFormat, stdout, stderr
|
|
371160
|
+
function runPromptTurn(session, prompt, outputFormat, stdout, stderr) {
|
|
370581
371161
|
let activeTurnId;
|
|
370582
371162
|
let activeAgentId;
|
|
370583
|
-
let latestStartedTurnId;
|
|
370584
371163
|
const outputWriter = outputFormat === "stream-json" ? new PromptJsonWriter(stdout) : new PromptTranscriptWriter(stdout, stderr);
|
|
370585
371164
|
let settled = false;
|
|
370586
371165
|
let unsubscribe;
|
|
371166
|
+
let keepAliveTimer;
|
|
371167
|
+
const holdEventLoop = () => {
|
|
371168
|
+
keepAliveTimer ??= setInterval(() => {}, 6e4);
|
|
371169
|
+
};
|
|
371170
|
+
const releaseEventLoop = () => {
|
|
371171
|
+
if (keepAliveTimer === void 0) return;
|
|
371172
|
+
clearInterval(keepAliveTimer);
|
|
371173
|
+
keepAliveTimer = void 0;
|
|
371174
|
+
};
|
|
370587
371175
|
return new Promise((resolve, reject) => {
|
|
370588
371176
|
const finish = (error) => {
|
|
370589
371177
|
if (settled) return;
|
|
370590
371178
|
settled = true;
|
|
371179
|
+
releaseEventLoop();
|
|
370591
371180
|
unsubscribe?.();
|
|
370592
371181
|
outputWriter.finish();
|
|
370593
371182
|
if (error !== void 0) {
|
|
@@ -370596,21 +371185,39 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370596
371185
|
}
|
|
370597
371186
|
resolve();
|
|
370598
371187
|
};
|
|
371188
|
+
const evaluateRunCompletion = async () => {
|
|
371189
|
+
try {
|
|
371190
|
+
const { goal } = await session.getGoal();
|
|
371191
|
+
if (settled || activeTurnId !== void 0) return;
|
|
371192
|
+
if (goal?.status === "active") {
|
|
371193
|
+
holdEventLoop();
|
|
371194
|
+
return;
|
|
371195
|
+
}
|
|
371196
|
+
const { tasks } = await session.getCronTasks();
|
|
371197
|
+
if (settled || activeTurnId !== void 0) return;
|
|
371198
|
+
if (tasks.some((task) => task.nextFireAt !== null)) {
|
|
371199
|
+
holdEventLoop();
|
|
371200
|
+
return;
|
|
371201
|
+
}
|
|
371202
|
+
await finishCompletedTurn();
|
|
371203
|
+
} catch (error) {
|
|
371204
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
371205
|
+
}
|
|
371206
|
+
};
|
|
370599
371207
|
unsubscribe = session.onEvent((event) => {
|
|
370600
371208
|
if (event.type === "error") {
|
|
370601
371209
|
if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
|
|
370602
371210
|
finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
|
|
370603
371211
|
return;
|
|
370604
371212
|
}
|
|
370605
|
-
if (event.type === "turn.started"
|
|
371213
|
+
if (event.type === "turn.started") {
|
|
370606
371214
|
if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
|
|
370607
371215
|
activeTurnId = event.turnId;
|
|
370608
371216
|
activeAgentId = event.agentId;
|
|
370609
|
-
latestStartedTurnId = event.turnId;
|
|
370610
371217
|
return;
|
|
370611
371218
|
}
|
|
370612
|
-
if (
|
|
370613
|
-
|
|
371219
|
+
if (event.type === "goal.updated" && event.agentId === PROMPT_MAIN_AGENT_ID && activeTurnId === void 0 && event.snapshot !== null && event.snapshot.status !== "active") {
|
|
371220
|
+
evaluateRunCompletion();
|
|
370614
371221
|
return;
|
|
370615
371222
|
}
|
|
370616
371223
|
if (activeTurnId === void 0 || activeAgentId === void 0 || !hasTurnId(event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
|
|
@@ -370621,6 +371228,7 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370621
371228
|
return;
|
|
370622
371229
|
case "turn.step.retrying":
|
|
370623
371230
|
outputWriter.discardAssistant();
|
|
371231
|
+
outputWriter.writeRetrying(event);
|
|
370624
371232
|
return;
|
|
370625
371233
|
case "assistant.delta":
|
|
370626
371234
|
outputWriter.writeAssistantDelta(event.delta);
|
|
@@ -370646,23 +371254,9 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370646
371254
|
case "turn.ended":
|
|
370647
371255
|
if (event.reason === "completed") {
|
|
370648
371256
|
outputWriter.flushAssistant();
|
|
370649
|
-
|
|
370650
|
-
|
|
370651
|
-
|
|
370652
|
-
activeAgentId = void 0;
|
|
370653
|
-
(async () => {
|
|
370654
|
-
try {
|
|
370655
|
-
const { goal } = await session.getGoal();
|
|
370656
|
-
if (activeTurnId !== void 0 || latestStartedTurnId !== completedTurnId) return;
|
|
370657
|
-
if (goal?.status === "active") return;
|
|
370658
|
-
await finishCompletedTurn();
|
|
370659
|
-
} catch (error) {
|
|
370660
|
-
finish(error instanceof Error ? error : new Error(String(error)));
|
|
370661
|
-
}
|
|
370662
|
-
})();
|
|
370663
|
-
return;
|
|
370664
|
-
}
|
|
370665
|
-
finishCompletedTurn();
|
|
371257
|
+
activeTurnId = void 0;
|
|
371258
|
+
activeAgentId = void 0;
|
|
371259
|
+
evaluateRunCompletion();
|
|
370666
371260
|
return;
|
|
370667
371261
|
}
|
|
370668
371262
|
finish(new Error(formatTurnEndedFailure(event)));
|
|
@@ -370685,7 +371279,6 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370685
371279
|
case "subagent.started":
|
|
370686
371280
|
case "subagent.suspended":
|
|
370687
371281
|
case "tool.list.updated":
|
|
370688
|
-
case "turn.started":
|
|
370689
371282
|
case "turn.step.completed":
|
|
370690
371283
|
case "warning": return;
|
|
370691
371284
|
}
|
|
@@ -370696,9 +371289,12 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
|
|
|
370696
371289
|
async function finishCompletedTurn() {
|
|
370697
371290
|
outputWriter.flushAssistant();
|
|
370698
371291
|
try {
|
|
370699
|
-
await session.
|
|
371292
|
+
if (await session.handlePrintMainTurnCompleted() === "continue") {
|
|
371293
|
+
holdEventLoop();
|
|
371294
|
+
return;
|
|
371295
|
+
}
|
|
370700
371296
|
} catch (error) {
|
|
370701
|
-
log.warn("
|
|
371297
|
+
log.warn("handlePrintMainTurnCompleted failed", { error });
|
|
370702
371298
|
}
|
|
370703
371299
|
finish();
|
|
370704
371300
|
}
|
|
@@ -370727,6 +371323,7 @@ var PromptTranscriptWriter = class {
|
|
|
370727
371323
|
writeToolCall() {}
|
|
370728
371324
|
writeToolCallDelta() {}
|
|
370729
371325
|
writeToolResult() {}
|
|
371326
|
+
writeRetrying() {}
|
|
370730
371327
|
flushAssistant() {
|
|
370731
371328
|
this.assistantWriter.finish();
|
|
370732
371329
|
}
|
|
@@ -370813,6 +371410,20 @@ var PromptJsonWriter = class {
|
|
|
370813
371410
|
this.assistantText = "";
|
|
370814
371411
|
this.toolCalls.length = 0;
|
|
370815
371412
|
}
|
|
371413
|
+
writeRetrying(event) {
|
|
371414
|
+
const message = {
|
|
371415
|
+
role: "meta",
|
|
371416
|
+
type: "turn.step.retrying",
|
|
371417
|
+
failed_attempt: event.failedAttempt,
|
|
371418
|
+
next_attempt: event.nextAttempt,
|
|
371419
|
+
max_attempts: event.maxAttempts,
|
|
371420
|
+
delay_ms: event.delayMs,
|
|
371421
|
+
error_name: event.errorName,
|
|
371422
|
+
error_message: event.errorMessage,
|
|
371423
|
+
status_code: event.statusCode
|
|
371424
|
+
};
|
|
371425
|
+
this.writeJsonLine(message);
|
|
371426
|
+
}
|
|
370816
371427
|
finish() {
|
|
370817
371428
|
this.flushAssistant();
|
|
370818
371429
|
}
|