@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.
Files changed (71) hide show
  1. package/dist/main.mjs +1274 -663
  2. package/dist-web/assets/{CodeBlockNode-DI6gCVXa.js → CodeBlockNode-B5sM5iYb.js} +1 -1
  3. 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
  4. package/dist-web/assets/DesignSystemView-CrA0fNau.js +13 -0
  5. package/dist-web/assets/DesignSystemView-prdQJClv.css +1 -0
  6. package/dist-web/assets/{Tooltip-B3YtzPNZ.js → Tooltip-BzkYFuS-.js} +1 -1
  7. package/dist-web/assets/{arc-bQMfCHSI.js → arc-hZy2ybJx.js} +1 -1
  8. package/dist-web/assets/{architectureDiagram-3BPJPVTR-sZkP4A5D.js → architectureDiagram-3BPJPVTR-Dme2VOsk.js} +1 -1
  9. package/dist-web/assets/{blockDiagram-GPEHLZMM-Cnf-tp-z.js → blockDiagram-GPEHLZMM-DPYxkkxc.js} +1 -1
  10. package/dist-web/assets/{c4Diagram-AAUBKEIU-D1Ej05NX.js → c4Diagram-AAUBKEIU-BaBOqczU.js} +1 -1
  11. package/dist-web/assets/channel--SU3IV4L.js +1 -0
  12. package/dist-web/assets/{chunk-2J33WTMH-BhnDBJIi.js → chunk-2J33WTMH-D12pYaz9.js} +1 -1
  13. package/dist-web/assets/{chunk-4BX2VUAB-CiOLp8_3.js → chunk-4BX2VUAB-BXPJT0AD.js} +1 -1
  14. package/dist-web/assets/{chunk-55IACEB6-aUOZP4x-.js → chunk-55IACEB6-BTJjSIMx.js} +1 -1
  15. package/dist-web/assets/{chunk-727SXJPM-CHY14gq8.js → chunk-727SXJPM-BorazSkv.js} +1 -1
  16. package/dist-web/assets/{chunk-AQP2D5EJ-BKQr3gru.js → chunk-AQP2D5EJ-3eB1WiGE.js} +1 -1
  17. package/dist-web/assets/{chunk-FMBD7UC4-BNnO5ZRK.js → chunk-FMBD7UC4-BN6eq3TW.js} +1 -1
  18. package/dist-web/assets/{chunk-ND2GUHAM-CzE2E83w.js → chunk-ND2GUHAM-CzHkIi39.js} +1 -1
  19. package/dist-web/assets/{chunk-QZHKN3VN-BVjSv3p6.js → chunk-QZHKN3VN-CAAkvUsl.js} +1 -1
  20. package/dist-web/assets/classDiagram-4FO5ZUOK-Cev__UDN.js +1 -0
  21. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-Cev__UDN.js +1 -0
  22. package/dist-web/assets/{cose-bilkent-S5V4N54A-DaSWvU-5.js → cose-bilkent-S5V4N54A-CO12pcZF.js} +1 -1
  23. package/dist-web/assets/{dagre-BM42HDAG-VuVwg9z6.js → dagre-BM42HDAG-BHHr0pRl.js} +1 -1
  24. package/dist-web/assets/{diagram-2AECGRRQ-BI5_WXHg.js → diagram-2AECGRRQ-OR0kYM7N.js} +1 -1
  25. package/dist-web/assets/{diagram-5GNKFQAL-3M89yso9.js → diagram-5GNKFQAL-BuWjKSqL.js} +1 -1
  26. package/dist-web/assets/{diagram-KO2AKTUF-C9Qx0fJd.js → diagram-KO2AKTUF-VPe8ZEiS.js} +1 -1
  27. package/dist-web/assets/{diagram-LMA3HP47-yJ-bCZyw.js → diagram-LMA3HP47-BvTXlHih.js} +1 -1
  28. package/dist-web/assets/{diagram-OG6HWLK6-CZnqkQin.js → diagram-OG6HWLK6-goJtZNF9.js} +1 -1
  29. package/dist-web/assets/{erDiagram-TEJ5UH35-BQucaPqh.js → erDiagram-TEJ5UH35-DRnuyJ1R.js} +1 -1
  30. package/dist-web/assets/{flowDiagram-I6XJVG4X-CNpuVBgN.js → flowDiagram-I6XJVG4X-CWAtDH7Q.js} +1 -1
  31. package/dist-web/assets/{ganttDiagram-6RSMTGT7-KIhg0NCT.js → ganttDiagram-6RSMTGT7-DpNy6QeS.js} +1 -1
  32. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-C4YXqCii.js → gitGraphDiagram-PVQCEYII-D7ekpdjQ.js} +1 -1
  33. package/dist-web/assets/{index-v28kS3Hg.js → index-BPhahMpb.js} +1 -1
  34. package/dist-web/assets/index-DZAkQfN3.js +322 -0
  35. package/dist-web/assets/{index-CL0UgQBD.js → index-De7eXGys.js} +2 -2
  36. package/dist-web/assets/{index-5DXn785n.css → index-u0HjHtha.css} +1 -1
  37. package/dist-web/assets/{index10-RB_ZIgWs.js → index10-B4kCqL2q.js} +1 -1
  38. package/dist-web/assets/{index11-DyrslmWY.js → index11-CdYjVQZ3.js} +1 -1
  39. package/dist-web/assets/{index5-Bn5298gF.js → index5-CuGXaX2S.js} +1 -1
  40. package/dist-web/assets/{index6-Di9uDNmo.js → index6-Bu6r1Fdb.js} +1 -1
  41. package/dist-web/assets/{index7-CL9Ie_DH.js → index7-BBWbs1u4.js} +1 -1
  42. package/dist-web/assets/{index8-CHlcChT0.js → index8-B1e6fpQC.js} +2 -2
  43. package/dist-web/assets/{infoDiagram-5YYISTIA-DTw-WQIn.js → infoDiagram-5YYISTIA-DWJMi_CT.js} +1 -1
  44. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-BUmTsigt.js → ishikawaDiagram-YF4QCWOH-DKnaDM-v.js} +1 -1
  45. package/dist-web/assets/{journeyDiagram-JHISSGLW-B8yfgocJ.js → journeyDiagram-JHISSGLW-CuTF_v7K.js} +1 -1
  46. package/dist-web/assets/{kanban-definition-UN3LZRKU-BUcF33Xd.js → kanban-definition-UN3LZRKU-7ZkgOrqm.js} +1 -1
  47. package/dist-web/assets/{linear-DY3q771w.js → linear-BZuIfHuU.js} +1 -1
  48. package/dist-web/assets/{mermaid.core-D1ZdIpxL.js → mermaid.core-BU-yVv7K.js} +4 -4
  49. package/dist-web/assets/{mindmap-definition-RKZ34NQL-ltEULHzt.js → mindmap-definition-RKZ34NQL-DME_lWCk.js} +1 -1
  50. package/dist-web/assets/{pieDiagram-4H26LBE5-iCUA44fk.js → pieDiagram-4H26LBE5-Co1VItYc.js} +1 -1
  51. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-Zrit1Fzo.js → quadrantDiagram-W4KKPZXB-DpswDnET.js} +1 -1
  52. package/dist-web/assets/{requirementDiagram-4Y6WPE33-n610iR9M.js → requirementDiagram-4Y6WPE33-BeRdWb9K.js} +1 -1
  53. package/dist-web/assets/{sankeyDiagram-5OEKKPKP-RQEY2tBl.js → sankeyDiagram-5OEKKPKP-_DYbW6pt.js} +1 -1
  54. package/dist-web/assets/{sequenceDiagram-3UESZ5HK-QrJ3Ysl6.js → sequenceDiagram-3UESZ5HK-DHiijecn.js} +1 -1
  55. package/dist-web/assets/{stateDiagram-AJRCARHV-D0mG4Tjg.js → stateDiagram-AJRCARHV-DCZ1rX02.js} +1 -1
  56. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-Cw0BsNEk.js +1 -0
  57. package/dist-web/assets/{timeline-definition-PNZ67QCA-DacB6B1v.js → timeline-definition-PNZ67QCA-CUBRS-qH.js} +1 -1
  58. package/dist-web/assets/{vennDiagram-CIIHVFJN-CXO9WlfO.js → vennDiagram-CIIHVFJN-C98FTG2Y.js} +1 -1
  59. package/dist-web/assets/{vue.runtime.esm-bundler-Dj6KWBND.js → vue.runtime.esm-bundler-DPzO-Tma.js} +1 -1
  60. package/dist-web/assets/{wardley-L42UT6IY-BbMWJCmQ.js → wardley-L42UT6IY-Bmyzb8p9.js} +1 -1
  61. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-BIcvmjmV.js → wardleyDiagram-YWT4CUSO-CxFNYY1a.js} +1 -1
  62. package/dist-web/assets/{xychartDiagram-2RQKCTM6-CX9xzSCM.js → xychartDiagram-2RQKCTM6-ySyVEQA-.js} +1 -1
  63. package/dist-web/index.html +2 -2
  64. package/package.json +5 -5
  65. package/dist-web/assets/DesignSystemView-B0VQ8shl.css +0 -1
  66. package/dist-web/assets/DesignSystemView-DQSXggUP.js +0 -13
  67. package/dist-web/assets/channel-UBfbidLp.js +0 -1
  68. package/dist-web/assets/classDiagram-4FO5ZUOK-B6r9FPXR.js +0 -1
  69. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-B6r9FPXR.js +0 -1
  70. package/dist-web/assets/index-CBO7u0oz.js +0 -322
  71. 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.select_tools !== true && capability.max_context_tokens === 0;
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
- select_tools: false
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
- return error instanceof APIStatusError && [
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
- constructor(statusCode, message, requestId) {
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
- select_tools: model.select_tools === true
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 select_tools.",
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 = normalizeMime(mimeType);
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: normalizeMime(mimeType),
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: normalizeMime(mimeType),
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$1(part.imageUrl.url);
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 = normalizeMime(mimeType);
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: normalizeMime(input.result.mimeType),
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 `select_tools` capability (mid-session
120919
- * model switch — the canonical history keeps its shape, only the outgoing
120920
- * view changes; announcements would be noise and even reference a
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
- return import_retry$1.timeouts({
121117
- retries: Math.max(maxAttempts - 1, 0),
121118
- minTimeout: RETRY_MIN_TIMEOUT_MS,
121119
- maxTimeout: RETRY_MAX_TIMEOUT_MS,
121120
- factor: RETRY_FACTOR,
121121
- randomize: true
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 import_retry$1, RETRY_MIN_TIMEOUT_MS, RETRY_MAX_TIMEOUT_MS, RETRY_FACTOR;
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
- RETRY_MIN_TIMEOUT_MS = 300;
121147
- RETRY_MAX_TIMEOUT_MS = 5e3;
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
- * a purely read-side transform — the underlying history is left untouched —
124020
- * that trades old pixels for bytes while the surrounding text (including
124021
- * ReadMediaFile's `<image path="...">` wrapper) survives, so the model can
124022
- * re-read any file it still needs. Untouched messages are returned by
124023
- * reference, and when nothing needs degrading the input array itself is
124024
- * returned.
124025
- */
124026
- function degradeOlderMediaParts(messages, keepRecent) {
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: MEDIA_DEGRADED_PLACEHOLDERS[part.type]
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/")) return {
143800
- type: "image_url",
143801
- imageUrl: { url: block.uri }
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, DEFAULT_SUBAGENT_TIMEOUT_DESCRIPTION, 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;
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 = 1800 * 1e3;
145162
- DEFAULT_SUBAGENT_TIMEOUT_DESCRIPTION = "30 minutes";
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: DEFAULT_SUBAGENT_TIMEOUT_MS,
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 ${DEFAULT_SUBAGENT_TIMEOUT_DESCRIPTION}.` : info?.stopReason === "Interrupted by user" ? USER_INTERRUPTED_SUBAGENT_MESSAGE : info?.stopReason !== void 0 ? info.stopReason : "The subagent was stopped before it finished.", timedOut),
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: DEFAULT_SUBAGENT_TIMEOUT_MS
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 declined to answer — proceed with your best judgment and do not re-ask the same question\n";
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.mimeType === "image/heic" || fileType.mimeType === "image/heif") return {
152642
+ if (fileType.kind === "image" && !isModelAcceptedImageMime(fileType.mimeType)) return {
152229
152643
  isError: true,
152230
- output: buildHeicConversionGuidance(args.path, fileType.mimeType, this.kaos.osEnv.osKind)
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(input, origin);
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(input, origin);
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 declares the `select_tools` capability, the
156022
- * model declares `tool_use` (a model without tool use registering
156023
- * select_tools is a contradiction), and the `tool-select` experimental flag
156024
- * is on. Every consumer — top-level tools[] convergence, select_tools
156025
- * registration, manifest announcements, projection shaping — reads this
156026
- * instead of re-deriving the conditions, so degradation is lossless: any
156027
- * closed gate reproduces the inline behavior byte-for-byte.
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.select_tools === true && capability.tool_use && this.experimentalFlags.enabled("tool-select");
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
- * Gated by `background.keep_alive_on_exit`: when it is not `true`, this returns
187301
- * immediately so print mode keeps its default single-turn semantics. The wait is
187302
- * bounded by `background.print_wait_ceiling_s` (default 3600s) so a wedged task
187303
- * cannot keep the process alive forever.
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 (!resolveConfigValue({
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
- select_tools: declared.has("select_tools") || detected.select_tools === true
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. The output is the
216465
- * flattened text of the kosong message's content parts (most tool
216466
- * messages emit a single text). `is_error` is taken from `ContextMessage.isError`.
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 flattenedOutput = msg.content.map((p) => p.type === "text" ? p.text : "").join("");
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: flattenedOutput,
216981
+ output,
216478
216982
  is_error: true
216479
216983
  } : {
216480
216984
  type: "tool_result",
216481
216985
  tool_call_id: msg.toolCallId,
216482
- output: flattenedOutput
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 `background.keep_alive_on_exit` is `true`, so background
219174
- * subagents get a chance to complete before the process exits. No-op when
219175
- * `keep_alive_on_exit` is not enabled. Bounded by `background.print_wait_ceiling_s`.
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.select_tools === true) caps.push("select_tools");
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 compressed = await compressBase64ForModel(part.source.data, part.source.media_type, {
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, true);
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, waitForGoalTerminal = false) {
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" && activeTurnId === void 0) {
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 (waitForGoalTerminal && event.type === "goal.updated" && event.agentId === PROMPT_MAIN_AGENT_ID && activeTurnId === void 0 && event.snapshot !== null && event.snapshot.status !== "active") {
370613
- finishCompletedTurn();
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
- if (waitForGoalTerminal) {
370650
- const completedTurnId = event.turnId;
370651
- activeTurnId = void 0;
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.waitForBackgroundTasksOnPrint();
371292
+ if (await session.handlePrintMainTurnCompleted() === "continue") {
371293
+ holdEventLoop();
371294
+ return;
371295
+ }
370700
371296
  } catch (error) {
370701
- log.warn("waitForBackgroundTasksOnPrint failed", { error });
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
  }