@moonshot-ai/kimi-code 0.23.5 → 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 +756 -556
  2. package/dist-web/assets/{CodeBlockNode-KR0ZBibg.js → CodeBlockNode-B5sM5iYb.js} +1 -1
  3. package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-Bl31Ov6p.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-CXVZw3LD.js → Tooltip-BzkYFuS-.js} +1 -1
  7. package/dist-web/assets/{arc-D-rA9VJS.js → arc-hZy2ybJx.js} +1 -1
  8. package/dist-web/assets/{architectureDiagram-3BPJPVTR-DCwhZ0-3.js → architectureDiagram-3BPJPVTR-Dme2VOsk.js} +1 -1
  9. package/dist-web/assets/{blockDiagram-GPEHLZMM-7zi2-mzu.js → blockDiagram-GPEHLZMM-DPYxkkxc.js} +1 -1
  10. package/dist-web/assets/{c4Diagram-AAUBKEIU-BUhBYlbI.js → c4Diagram-AAUBKEIU-BaBOqczU.js} +1 -1
  11. package/dist-web/assets/channel--SU3IV4L.js +1 -0
  12. package/dist-web/assets/{chunk-2J33WTMH-DXfD8jVe.js → chunk-2J33WTMH-D12pYaz9.js} +1 -1
  13. package/dist-web/assets/{chunk-4BX2VUAB-RnAXB6Jd.js → chunk-4BX2VUAB-BXPJT0AD.js} +1 -1
  14. package/dist-web/assets/{chunk-55IACEB6-CzyvmHuE.js → chunk-55IACEB6-BTJjSIMx.js} +1 -1
  15. package/dist-web/assets/{chunk-727SXJPM-BwBvbidm.js → chunk-727SXJPM-BorazSkv.js} +1 -1
  16. package/dist-web/assets/{chunk-AQP2D5EJ-DHeLjRn1.js → chunk-AQP2D5EJ-3eB1WiGE.js} +1 -1
  17. package/dist-web/assets/{chunk-FMBD7UC4-Bm646OfX.js → chunk-FMBD7UC4-BN6eq3TW.js} +1 -1
  18. package/dist-web/assets/{chunk-ND2GUHAM-D3VSc8VX.js → chunk-ND2GUHAM-CzHkIi39.js} +1 -1
  19. package/dist-web/assets/{chunk-QZHKN3VN-BvZn7Sbo.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-DdsbNmIL.js → cose-bilkent-S5V4N54A-CO12pcZF.js} +1 -1
  23. package/dist-web/assets/{dagre-BM42HDAG--3VOObN-.js → dagre-BM42HDAG-BHHr0pRl.js} +1 -1
  24. package/dist-web/assets/{diagram-2AECGRRQ-DeWvFgbz.js → diagram-2AECGRRQ-OR0kYM7N.js} +1 -1
  25. package/dist-web/assets/{diagram-5GNKFQAL-DthtQYgS.js → diagram-5GNKFQAL-BuWjKSqL.js} +1 -1
  26. package/dist-web/assets/{diagram-KO2AKTUF-Bt4BtRLc.js → diagram-KO2AKTUF-VPe8ZEiS.js} +1 -1
  27. package/dist-web/assets/{diagram-LMA3HP47-7N499o4j.js → diagram-LMA3HP47-BvTXlHih.js} +1 -1
  28. package/dist-web/assets/{diagram-OG6HWLK6-Br3CxOQX.js → diagram-OG6HWLK6-goJtZNF9.js} +1 -1
  29. package/dist-web/assets/{erDiagram-TEJ5UH35-BrhRvGW3.js → erDiagram-TEJ5UH35-DRnuyJ1R.js} +1 -1
  30. package/dist-web/assets/{flowDiagram-I6XJVG4X-D-u112mz.js → flowDiagram-I6XJVG4X-CWAtDH7Q.js} +1 -1
  31. package/dist-web/assets/{ganttDiagram-6RSMTGT7-DpXOgY93.js → ganttDiagram-6RSMTGT7-DpNy6QeS.js} +1 -1
  32. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-BzV-RpYW.js → gitGraphDiagram-PVQCEYII-D7ekpdjQ.js} +1 -1
  33. package/dist-web/assets/{index-DrI3jjno.js → index-BPhahMpb.js} +1 -1
  34. package/dist-web/assets/index-DZAkQfN3.js +322 -0
  35. package/dist-web/assets/{index-DEjpFBq9.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-UVo4gjYR.js → index10-B4kCqL2q.js} +1 -1
  38. package/dist-web/assets/{index11-m1oYWviR.js → index11-CdYjVQZ3.js} +1 -1
  39. package/dist-web/assets/{index5-B45mMYRO.js → index5-CuGXaX2S.js} +1 -1
  40. package/dist-web/assets/{index6-DvvrfRua.js → index6-Bu6r1Fdb.js} +1 -1
  41. package/dist-web/assets/{index7-BUH5Peml.js → index7-BBWbs1u4.js} +1 -1
  42. package/dist-web/assets/{index8-1BPQUZEF.js → index8-B1e6fpQC.js} +2 -2
  43. package/dist-web/assets/{infoDiagram-5YYISTIA-Cu7fADeQ.js → infoDiagram-5YYISTIA-DWJMi_CT.js} +1 -1
  44. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-CVvnTORb.js → ishikawaDiagram-YF4QCWOH-DKnaDM-v.js} +1 -1
  45. package/dist-web/assets/{journeyDiagram-JHISSGLW-C-69S6wV.js → journeyDiagram-JHISSGLW-CuTF_v7K.js} +1 -1
  46. package/dist-web/assets/{kanban-definition-UN3LZRKU-pwEooP5T.js → kanban-definition-UN3LZRKU-7ZkgOrqm.js} +1 -1
  47. package/dist-web/assets/{linear-BQ3ylJG8.js → linear-BZuIfHuU.js} +1 -1
  48. package/dist-web/assets/{mermaid.core-DcE0EhAy.js → mermaid.core-BU-yVv7K.js} +4 -4
  49. package/dist-web/assets/{mindmap-definition-RKZ34NQL-DKQ2AZ3h.js → mindmap-definition-RKZ34NQL-DME_lWCk.js} +1 -1
  50. package/dist-web/assets/{pieDiagram-4H26LBE5-CHb1kYTj.js → pieDiagram-4H26LBE5-Co1VItYc.js} +1 -1
  51. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-DzFGGLJt.js → quadrantDiagram-W4KKPZXB-DpswDnET.js} +1 -1
  52. package/dist-web/assets/{requirementDiagram-4Y6WPE33-Bk-5_Exk.js → requirementDiagram-4Y6WPE33-BeRdWb9K.js} +1 -1
  53. package/dist-web/assets/{sankeyDiagram-5OEKKPKP-CSxfPrmb.js → sankeyDiagram-5OEKKPKP-_DYbW6pt.js} +1 -1
  54. package/dist-web/assets/{sequenceDiagram-3UESZ5HK-DHFbmk49.js → sequenceDiagram-3UESZ5HK-DHiijecn.js} +1 -1
  55. package/dist-web/assets/{stateDiagram-AJRCARHV-DR8SEStc.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-O5r5mf9f.js → timeline-definition-PNZ67QCA-CUBRS-qH.js} +1 -1
  58. package/dist-web/assets/{vennDiagram-CIIHVFJN-BRtD6E2o.js → vennDiagram-CIIHVFJN-C98FTG2Y.js} +1 -1
  59. package/dist-web/assets/{vue.runtime.esm-bundler-ClW1ucng.js → vue.runtime.esm-bundler-DPzO-Tma.js} +1 -1
  60. package/dist-web/assets/{wardley-L42UT6IY-BqzYPkrr.js → wardley-L42UT6IY-Bmyzb8p9.js} +1 -1
  61. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-BXjVelBc.js → wardleyDiagram-YWT4CUSO-CxFNYY1a.js} +1 -1
  62. package/dist-web/assets/{xychartDiagram-2RQKCTM6-Cc97QEmc.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-DU67euCs.js +0 -13
  67. package/dist-web/assets/channel-B7bRl8z3.js +0 -1
  68. package/dist-web/assets/classDiagram-4FO5ZUOK-CxRebqWF.js +0 -1
  69. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-CxRebqWF.js +0 -1
  70. package/dist-web/assets/index-6oRZtoED.js +0 -322
  71. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-Cp9aas86.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
@@ -55117,7 +55117,7 @@ function catalogModelToCapability(model) {
55117
55117
  thinking: Boolean(model.reasoning),
55118
55118
  tool_use: model.tool_call ?? true,
55119
55119
  max_context_tokens: context,
55120
- select_tools: model.select_tools === true
55120
+ dynamically_loaded_tools: model.dynamically_loaded_tools === true
55121
55121
  }
55122
55122
  };
55123
55123
  }
@@ -62007,7 +62007,7 @@ function isValidPermissionPattern(pattern) {
62007
62007
  return false;
62008
62008
  }
62009
62009
  }
62010
- 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;
62011
62011
  var init_schema = __esmMin((() => {
62012
62012
  init_types$18();
62013
62013
  init_matches_rule();
@@ -62097,8 +62097,15 @@ var init_schema = __esmMin((() => {
62097
62097
  maxRunningTasks: number$1().int().min(1).optional(),
62098
62098
  keepAliveOnExit: boolean$1().optional(),
62099
62099
  killGracePeriodMs: number$1().int().min(0).optional(),
62100
- 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()
62101
62107
  });
62108
+ SubagentConfigSchema = object({ timeoutMs: number$1().int().min(1).optional() });
62102
62109
  ImageConfigSchema = object({
62103
62110
  /**
62104
62111
  * Longest-edge ceiling (px) applied when compressing images for the model.
@@ -62204,6 +62211,7 @@ var init_schema = __esmMin((() => {
62204
62211
  extraSkillDirs: array(string()).optional(),
62205
62212
  loopControl: LoopControlSchema.optional(),
62206
62213
  background: BackgroundConfigSchema.optional(),
62214
+ subagent: SubagentConfigSchema.optional(),
62207
62215
  image: ImageConfigSchema.optional(),
62208
62216
  modelCatalog: ModelCatalogConfigSchema.optional(),
62209
62217
  experimental: ExperimentalConfigSchema.optional(),
@@ -62216,6 +62224,7 @@ var init_schema = __esmMin((() => {
62216
62224
  PermissionConfigPatchSchema = PermissionConfigSchema.partial();
62217
62225
  LoopControlPatchSchema = LoopControlSchema.partial();
62218
62226
  BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
62227
+ SubagentConfigPatchSchema = SubagentConfigSchema.partial();
62219
62228
  ImageConfigPatchSchema = ImageConfigSchema.partial();
62220
62229
  ModelCatalogConfigPatchSchema = ModelCatalogConfigSchema.partial();
62221
62230
  ExperimentalConfigPatchSchema = ExperimentalConfigSchema;
@@ -62241,6 +62250,7 @@ var init_schema = __esmMin((() => {
62241
62250
  extraSkillDirs: array(string()).optional(),
62242
62251
  loopControl: LoopControlPatchSchema.optional(),
62243
62252
  background: BackgroundConfigPatchSchema.optional(),
62253
+ subagent: SubagentConfigPatchSchema.optional(),
62244
62254
  image: ImageConfigPatchSchema.optional(),
62245
62255
  modelCatalog: ModelCatalogConfigPatchSchema.optional(),
62246
62256
  experimental: ExperimentalConfigPatchSchema.optional(),
@@ -63744,6 +63754,7 @@ function transformTomlData(data) {
63744
63754
  else if (targetKey === "background" && isPlainObject$2(value)) result[targetKey] = transformPlainObject(value);
63745
63755
  else if (targetKey === "image" && isPlainObject$2(value)) result[targetKey] = transformPlainObject(value);
63746
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);
63747
63758
  else if (!isPlainObject$2(value)) result[targetKey] = value;
63748
63759
  }
63749
63760
  return result;
@@ -63854,6 +63865,7 @@ function configToTomlData(config) {
63854
63865
  setSection(out, "services", config.services, servicesToToml);
63855
63866
  setSection(out, "loop_control", config.loopControl, loopControlToToml);
63856
63867
  setSection(out, "background", config.background, backgroundToToml);
63868
+ setSection(out, "subagent", config.subagent, subagentToToml);
63857
63869
  setSection(out, "image", config.image, imageToToml);
63858
63870
  setSection(out, "experimental", config.experimental, experimentalToToml);
63859
63871
  setSection(out, "permission", config.permission, permissionToToml);
@@ -63945,6 +63957,11 @@ function backgroundToToml(background, rawBackground) {
63945
63957
  for (const [key, value] of Object.entries(background)) setDefined(out, camelToSnake$3(key), value);
63946
63958
  return out;
63947
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
+ }
63948
63965
  function imageToToml(image, rawImage) {
63949
63966
  const out = cloneRecord$2(rawImage);
63950
63967
  for (const [key, value] of Object.entries(image)) setDefined(out, camelToSnake$3(key), value);
@@ -68630,7 +68647,7 @@ var init_registry$1 = __esmMin((() => {
68630
68647
  FLAG_DEFINITIONS = [{
68631
68648
  id: "tool-select",
68632
68649
  title: "Tool select (progressive tool disclosure)",
68633
- 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.",
68634
68651
  env: "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT",
68635
68652
  default: false,
68636
68653
  surface: "core"
@@ -121218,10 +121235,10 @@ function isLoadableToolsAnnouncement(message) {
121218
121235
  * Shape a history for a consumer that must not see dynamic-tool protocol
121219
121236
  * context: drop the loadable-tools announcements and strip `message.tools`
121220
121237
  * (dropping the message entirely when nothing else remains). Two callers:
121221
- * - projection for a model without the `select_tools` capability (mid-session
121222
- * model switch — the canonical history keeps its shape, only the outgoing
121223
- * view changes; announcements would be noise and even reference a
121224
- * 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);
121225
121242
  * - the compaction summarizer input (schemas and announcements are protocol
121226
121243
  * context, not conversation — summarizing them wastes tokens and risks
121227
121244
  * leaking schema text into the summary).
@@ -123582,6 +123599,21 @@ var init_manager$2 = __esmMin((() => {
123582
123599
  return this.scheduler.getNextFireForTask(taskId);
123583
123600
  }
123584
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
+ /**
123585
123617
  * Stale judgment.
123586
123618
  *
123587
123619
  * - `KIMI_CRON_NO_STALE=1` short-circuits to false (bench).
@@ -144351,479 +144383,6 @@ var init_output = __esmMin((() => {
144351
144383
  MCP_MAX_BINARY_PART_CHARS = Math.ceil(MCP_MAX_BINARY_PART_BYTES * 4 / 3);
144352
144384
  }));
144353
144385
  //#endregion
144354
- //#region ../../packages/agent-core/src/agent/llm-request-logger.ts
144355
- function splitGenerateOptions(options) {
144356
- if (options === void 0) return {
144357
- requestLogFields: void 0,
144358
- generateOptions: void 0
144359
- };
144360
- const { requestLogFields, ...generateOptions } = options;
144361
- return {
144362
- requestLogFields,
144363
- generateOptions
144364
- };
144365
- }
144366
- function toolSignature(tools) {
144367
- return tools.map(({ name, description, parameters }) => ({
144368
- name,
144369
- description,
144370
- parameters
144371
- }));
144372
- }
144373
- function fingerprint(content) {
144374
- return createHash("sha256").update(content).digest("hex");
144375
- }
144376
- var LlmRequestLogger;
144377
- var init_llm_request_logger = __esmMin((() => {
144378
- LlmRequestLogger = class {
144379
- log;
144380
- lastConfigLogSignature;
144381
- constructor(log) {
144382
- this.log = log;
144383
- }
144384
- logRequest(input) {
144385
- const { provider, modelAlias, systemPrompt, tools, messages, fields } = input;
144386
- const requestLogFields = fields ?? {};
144387
- const wireTools = tools.filter((tool) => tool.deferred !== true);
144388
- const config = {
144389
- provider: provider.name,
144390
- model: provider.modelName,
144391
- modelAlias,
144392
- thinkingEffort: provider.thinkingEffort ?? void 0,
144393
- systemPromptChars: systemPrompt.length,
144394
- toolCount: wireTools.length
144395
- };
144396
- const signature = JSON.stringify({
144397
- ...config,
144398
- systemPromptHash: fingerprint(systemPrompt),
144399
- toolsHash: fingerprint(JSON.stringify(toolSignature(wireTools)))
144400
- });
144401
- if (signature !== this.lastConfigLogSignature) {
144402
- this.lastConfigLogSignature = signature;
144403
- this.log.info("llm config", {
144404
- ...requestLogFields,
144405
- ...config
144406
- });
144407
- }
144408
- const partialMessageCount = messages.filter((message) => message.partial === true).length;
144409
- const requestFields = { ...requestLogFields };
144410
- if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
144411
- this.log.info("llm request", requestFields);
144412
- }
144413
- };
144414
- }));
144415
- //#endregion
144416
- //#region ../../packages/agent-core/src/tools/background/task-output.md?raw
144417
- var task_output_default;
144418
- var init_task_output$1 = __esmMin((() => {
144419
- 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";
144420
- }));
144421
- //#endregion
144422
- //#region ../../packages/agent-core/src/tools/background/task-output.ts
144423
- function retrievalStatus(status, block) {
144424
- if (isBackgroundTaskTerminal(status)) return "success";
144425
- return block ? "timeout" : "not_ready";
144426
- }
144427
- function terminalReason(info) {
144428
- if (info.status === "timed_out") return "timed_out";
144429
- if (info.status === "killed" && info.stopReason !== void 0) return "stopped";
144430
- if (info.status === "failed" && info.stopReason !== void 0) return "failed";
144431
- }
144432
- function fullOutputHint(output) {
144433
- if (!output.fullOutputAvailable || output.outputPath === void 0) return void 0;
144434
- 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).`;
144435
- 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).`;
144436
- }
144437
- var OUTPUT_PREVIEW_BYTES, PAGING_HINT_LINES, TaskOutputInputSchema, TaskOutputTool;
144438
- var init_task_output = __esmMin((() => {
144439
- init_zod$1();
144440
- init_background();
144441
- init_input_schema();
144442
- init_rule_match();
144443
- init_format();
144444
- init_task_output$1();
144445
- OUTPUT_PREVIEW_BYTES = 32 * 1024;
144446
- PAGING_HINT_LINES = 300;
144447
- TaskOutputInputSchema = object({
144448
- task_id: string().describe("The background task ID to inspect."),
144449
- block: boolean$1().default(false).describe("Whether to wait for the task to finish before returning.").optional(),
144450
- timeout: number$1().int().min(0).max(3600).default(30).describe("Maximum number of seconds to wait when block=true.").optional()
144451
- });
144452
- TaskOutputTool = class {
144453
- manager;
144454
- name = "TaskOutput";
144455
- description = task_output_default;
144456
- parameters = toInputJsonSchema(TaskOutputInputSchema);
144457
- constructor(manager) {
144458
- this.manager = manager;
144459
- }
144460
- resolveExecution(args) {
144461
- return {
144462
- description: `Reading output of task ${args.task_id}`,
144463
- approvalRule: this.name,
144464
- matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
144465
- execute: () => this.execute(args)
144466
- };
144467
- }
144468
- async execute(args) {
144469
- const info = this.manager.getTask(args.task_id);
144470
- if (!info) return {
144471
- isError: true,
144472
- output: `Task not found: ${args.task_id}`
144473
- };
144474
- if (args.block && !isBackgroundTaskTerminal(info.status)) await this.manager.wait(args.task_id, (args.timeout ?? 30) * 1e3);
144475
- const current = this.manager.getTask(args.task_id);
144476
- if (!current) return {
144477
- isError: true,
144478
- output: `Task not found: ${args.task_id}`
144479
- };
144480
- const output = await this.manager.getOutputSnapshot(args.task_id, OUTPUT_PREVIEW_BYTES);
144481
- const lines = [formatPlainObject({
144482
- retrievalStatus: retrievalStatus(current.status, args.block),
144483
- ...current,
144484
- outputPath: output.outputPath,
144485
- terminalReason: terminalReason(current),
144486
- outputSizeBytes: output.outputSizeBytes,
144487
- outputPreviewBytes: output.previewBytes,
144488
- outputTruncated: output.truncated,
144489
- fullOutputAvailable: output.fullOutputAvailable,
144490
- fullOutputTool: output.fullOutputAvailable && output.outputPath !== void 0 ? "Read" : void 0,
144491
- fullOutputHint: fullOutputHint(output)
144492
- }), ""];
144493
- 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.]");
144494
- lines.push("[output]", output.preview || "[no output available]");
144495
- return {
144496
- output: lines.join("\n"),
144497
- isError: false,
144498
- message: "Task snapshot retrieved."
144499
- };
144500
- }
144501
- };
144502
- }));
144503
- //#endregion
144504
- //#region ../../packages/agent-core/src/tools/background/task-stop.md?raw
144505
- var task_stop_default;
144506
- var init_task_stop$1 = __esmMin((() => {
144507
- 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";
144508
- }));
144509
- //#endregion
144510
- //#region ../../packages/agent-core/src/tools/background/task-stop.ts
144511
- function terminalStopReason(reason) {
144512
- const trimmed = reason?.trim();
144513
- return trimmed === void 0 || trimmed.length === 0 ? "Task already in terminal state" : trimmed;
144514
- }
144515
- var TaskStopInputSchema, TaskStopTool;
144516
- var init_task_stop = __esmMin((() => {
144517
- init_zod$1();
144518
- init_background();
144519
- init_input_schema();
144520
- init_rule_match();
144521
- init_task_stop$1();
144522
- TaskStopInputSchema = object({
144523
- task_id: string().describe("The background task ID to stop."),
144524
- reason: string().default("Stopped by TaskStop").describe("Short reason recorded when the task is stopped.").optional()
144525
- });
144526
- TaskStopTool = class {
144527
- manager;
144528
- name = "TaskStop";
144529
- description = task_stop_default;
144530
- parameters = toInputJsonSchema(TaskStopInputSchema);
144531
- constructor(manager) {
144532
- this.manager = manager;
144533
- }
144534
- resolveExecution(args) {
144535
- return {
144536
- description: `Stopping task ${args.task_id}`,
144537
- approvalRule: this.name,
144538
- matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.task_id),
144539
- execute: async () => {
144540
- const info = this.manager.getTask(args.task_id);
144541
- if (!info) return {
144542
- isError: true,
144543
- output: `Task not found: ${args.task_id}`
144544
- };
144545
- const trimmedReason = args.reason?.trim();
144546
- const reason = trimmedReason === void 0 || trimmedReason.length === 0 ? "Stopped by TaskStop" : trimmedReason;
144547
- if (isBackgroundTaskTerminal(info.status)) return {
144548
- output: `task_id: ${info.taskId}\nstatus: ${info.status}\nreason: ${terminalStopReason(info.stopReason)}`,
144549
- isError: false
144550
- };
144551
- await this.manager.suppressTerminalNotification(args.task_id);
144552
- const result = await this.manager.stop(args.task_id, reason);
144553
- if (!result) return {
144554
- isError: true,
144555
- output: `Failed to stop task: ${args.task_id}`
144556
- };
144557
- return {
144558
- output: `task_id: ${result.taskId}\nstatus: ${result.status}\nreason: ${result.stopReason ?? reason}`,
144559
- isError: false
144560
- };
144561
- }
144562
- };
144563
- }
144564
- };
144565
- }));
144566
- //#endregion
144567
- //#region ../../packages/agent-core/src/tools/cron/time-format.ts
144568
- /**
144569
- * Render cron-facing timestamps in local wall time with an explicit
144570
- * numeric offset. Cron expressions are evaluated in local time, so the
144571
- * tool output should preserve that mental model while remaining
144572
- * unambiguous and parseable as ISO 8601.
144573
- */
144574
- function formatLocalIsoWithOffset(ms) {
144575
- const date = new Date(ms);
144576
- const offsetMin = -date.getTimezoneOffset();
144577
- const sign = offsetMin >= 0 ? "+" : "-";
144578
- const absOffset = Math.abs(offsetMin);
144579
- const offset = `${sign}${pad(Math.floor(absOffset / 60))}:${pad(absOffset % 60)}`;
144580
- 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}`;
144581
- }
144582
- function pad(n) {
144583
- return String(n).padStart(2, "0");
144584
- }
144585
- var init_time_format = __esmMin((() => {}));
144586
- //#endregion
144587
- //#region ../../packages/agent-core/src/tools/cron/cron-create.md?raw
144588
- var cron_create_default;
144589
- var init_cron_create$1 = __esmMin((() => {
144590
- 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";
144591
- }));
144592
- //#endregion
144593
- //#region ../../packages/agent-core/src/tools/cron/cron-create.ts
144594
- function formatOutput(o) {
144595
- return [
144596
- `id: ${o.id}`,
144597
- `cron: ${o.cron}`,
144598
- `humanSchedule: ${o.humanSchedule}`,
144599
- `recurring: ${String(o.recurring)}`,
144600
- `nextFireAt: ${o.nextFireAt === null ? "null" : formatLocalIsoWithOffset(o.nextFireAt)}`
144601
- ].join("\n");
144602
- }
144603
- var MAX_PROMPT_BYTES, ONE_SHOT_MAX_FUTURE_MS, CronCreateInputSchema, CronCreateTool;
144604
- var init_cron_create = __esmMin((() => {
144605
- init_zod$1();
144606
- init_input_schema();
144607
- init_rule_match();
144608
- init_cron_expr();
144609
- init_jitter();
144610
- init_time_format();
144611
- init_cron_create$1();
144612
- MAX_PROMPT_BYTES = 8 * 1024;
144613
- ONE_SHOT_MAX_FUTURE_MS = 350 * 24 * 60 * 60 * 1e3;
144614
- CronCreateInputSchema = object({
144615
- 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)."),
144616
- prompt: string().min(1).max(MAX_PROMPT_BYTES).describe("The prompt to enqueue at each fire time. Limited to 8 KiB (UTF-8)."),
144617
- 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.")
144618
- });
144619
- CronCreateTool = class {
144620
- manager;
144621
- name = "CronCreate";
144622
- description = cron_create_default;
144623
- parameters = toInputJsonSchema(CronCreateInputSchema);
144624
- constructor(manager) {
144625
- this.manager = manager;
144626
- }
144627
- resolveExecution(args) {
144628
- if (process.env["KIMI_DISABLE_CRON"] === "1") return {
144629
- isError: true,
144630
- output: "Cron scheduling is disabled (KIMI_DISABLE_CRON=1)."
144631
- };
144632
- const normalizedCron = args.cron.trim().split(/\s+/).join(" ");
144633
- let parsed;
144634
- try {
144635
- parsed = parseCronExpression(normalizedCron);
144636
- } catch (err) {
144637
- return {
144638
- isError: true,
144639
- output: `Invalid cron expression: ${err instanceof Error ? err.message : String(err)}`
144640
- };
144641
- }
144642
- const nowAtPrepare = this.manager.clocks.wallNow();
144643
- if (!hasFireWithinYears(parsed, 5, nowAtPrepare)) return {
144644
- isError: true,
144645
- output: `Cron expression ${JSON.stringify(normalizedCron)} has no fire within 5 years; refusing to schedule.`
144646
- };
144647
- if (this.manager.store.list().length >= 50) return {
144648
- isError: true,
144649
- output: `Cron job cap reached (max ${String(50)} per session).`
144650
- };
144651
- const byteLen = Buffer.byteLength(args.prompt, "utf8");
144652
- if (byteLen > MAX_PROMPT_BYTES) return {
144653
- isError: true,
144654
- output: `Prompt exceeds ${String(MAX_PROMPT_BYTES)} bytes (got ${String(byteLen)}).`
144655
- };
144656
- const recurring = args.recurring !== false;
144657
- if (!recurring) {
144658
- const firstFire = computeNextCronRun(parsed, nowAtPrepare);
144659
- if (firstFire !== null && firstFire - nowAtPrepare > ONE_SHOT_MAX_FUTURE_MS) return {
144660
- isError: true,
144661
- 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.`
144662
- };
144663
- }
144664
- return {
144665
- description: recurring ? `Scheduling cron ${normalizedCron}` : `Scheduling one-shot ${normalizedCron}`,
144666
- approvalRule: literalRulePattern(this.name, JSON.stringify({
144667
- cron: normalizedCron,
144668
- prompt: args.prompt,
144669
- recurring
144670
- })),
144671
- execute: async () => {
144672
- const nowMs = this.manager.clocks.wallNow();
144673
- if (this.manager.store.list().length >= 50) return {
144674
- isError: true,
144675
- output: `Cron job cap reached (max ${String(50)} per session).`
144676
- };
144677
- const task = this.manager.addTask({
144678
- cron: normalizedCron,
144679
- prompt: args.prompt,
144680
- recurring
144681
- });
144682
- const ideal = computeNextCronRun(parsed, nowMs);
144683
- const nextFireAt = ideal === null ? null : recurring ? jitteredNextCronRunMs(task, parsed, ideal) : oneShotJitteredNextCronRunMs(task, ideal);
144684
- const humanSchedule = cronToHuman(parsed);
144685
- this.manager.emitScheduled(task);
144686
- return {
144687
- output: formatOutput({
144688
- id: task.id,
144689
- cron: normalizedCron,
144690
- humanSchedule,
144691
- recurring,
144692
- nextFireAt
144693
- }),
144694
- isError: false,
144695
- message: `Scheduled cron ${task.id}`
144696
- };
144697
- }
144698
- };
144699
- }
144700
- };
144701
- }));
144702
- //#endregion
144703
- //#region ../../packages/agent-core/src/tools/cron/cron-delete.md?raw
144704
- var cron_delete_default;
144705
- var init_cron_delete$1 = __esmMin((() => {
144706
- 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";
144707
- }));
144708
- //#endregion
144709
- //#region ../../packages/agent-core/src/tools/cron/cron-delete.ts
144710
- var ID_PATTERN, CronDeleteInputSchema, CronDeleteTool;
144711
- var init_cron_delete = __esmMin((() => {
144712
- init_zod$1();
144713
- init_input_schema();
144714
- init_cron_delete$1();
144715
- ID_PATTERN = /^[0-9a-f]{8}$/;
144716
- CronDeleteInputSchema = object({ id: string().describe("The 8-hex cron job id returned by CronCreate / CronList.") });
144717
- CronDeleteTool = class {
144718
- manager;
144719
- name = "CronDelete";
144720
- description = cron_delete_default;
144721
- parameters = toInputJsonSchema(CronDeleteInputSchema);
144722
- constructor(manager) {
144723
- this.manager = manager;
144724
- }
144725
- resolveExecution(args) {
144726
- if (!ID_PATTERN.test(args.id)) return {
144727
- isError: true,
144728
- output: `Invalid cron job id ${JSON.stringify(args.id)} — must be 8 lowercase hex characters.`
144729
- };
144730
- return {
144731
- description: `Deleting cron ${args.id}`,
144732
- approvalRule: this.name,
144733
- execute: async () => {
144734
- if (this.manager.removeTasks([args.id]).length === 0) return {
144735
- isError: true,
144736
- output: `No cron job with id ${args.id}.`
144737
- };
144738
- this.manager.emitDeleted(args.id);
144739
- return {
144740
- output: `Deleted cron job ${args.id}.`,
144741
- isError: false
144742
- };
144743
- }
144744
- };
144745
- }
144746
- };
144747
- }));
144748
- //#endregion
144749
- //#region ../../packages/agent-core/src/tools/cron/cron-list.md?raw
144750
- var cron_list_default;
144751
- var init_cron_list$1 = __esmMin((() => {
144752
- 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";
144753
- }));
144754
- //#endregion
144755
- //#region ../../packages/agent-core/src/tools/cron/cron-list.ts
144756
- function previewPrompt(prompt) {
144757
- const buf = Buffer.from(prompt, "utf8");
144758
- if (buf.byteLength <= PROMPT_PREVIEW_BYTES) return prompt;
144759
- let end = PROMPT_PREVIEW_BYTES;
144760
- while (end > 0 && (buf[end] & 192) === 128) end--;
144761
- return `${buf.subarray(0, end).toString("utf8")}…(truncated)`;
144762
- }
144763
- var CronListInputSchema, MS_PER_DAY, PROMPT_PREVIEW_BYTES, CronListTool;
144764
- var init_cron_list = __esmMin((() => {
144765
- init_zod$1();
144766
- init_input_schema();
144767
- init_cron_expr();
144768
- init_time_format();
144769
- init_cron_list$1();
144770
- CronListInputSchema = object({}).strict();
144771
- MS_PER_DAY = 1440 * 60 * 1e3;
144772
- PROMPT_PREVIEW_BYTES = 200;
144773
- CronListTool = class {
144774
- manager;
144775
- name = "CronList";
144776
- description = cron_list_default;
144777
- parameters = toInputJsonSchema(CronListInputSchema);
144778
- constructor(manager) {
144779
- this.manager = manager;
144780
- }
144781
- resolveExecution(_args) {
144782
- return {
144783
- description: "Listing scheduled cron jobs",
144784
- approvalRule: this.name,
144785
- execute: async () => {
144786
- const tasks = this.manager.store.list();
144787
- const nowMs = this.manager.clocks.wallNow();
144788
- const records = tasks.map((t) => this.renderRecord(t, nowMs));
144789
- const header = `cron_jobs: ${String(tasks.length)}`;
144790
- if (records.length === 0) return {
144791
- output: `${header}\nNo cron jobs scheduled.`,
144792
- isError: false
144793
- };
144794
- return {
144795
- output: `${header}\n${records.join("\n---\n")}`,
144796
- isError: false
144797
- };
144798
- }
144799
- };
144800
- }
144801
- renderRecord(task, nowMs) {
144802
- const recurring = task.recurring !== false;
144803
- const ageMs = nowMs - task.createdAt;
144804
- const ageDays = Number.isFinite(ageMs) ? ageMs / MS_PER_DAY : 0;
144805
- const stale = this.manager.isStale(task);
144806
- let humanSchedule = task.cron;
144807
- let nextFireAtIso = "null";
144808
- try {
144809
- humanSchedule = cronToHuman(parseCronExpression(task.cron));
144810
- const nextFireMs = this.manager.getNextFireForTask(task.id);
144811
- if (nextFireMs !== null) nextFireAtIso = formatLocalIsoWithOffset(nextFireMs);
144812
- } catch {}
144813
- return [
144814
- `id: ${task.id}`,
144815
- `cron: ${task.cron}`,
144816
- `humanSchedule: ${humanSchedule}`,
144817
- `prompt: ${JSON.stringify(previewPrompt(task.prompt))}`,
144818
- `nextFireAt: ${nextFireAtIso}`,
144819
- `recurring: ${String(recurring)}`,
144820
- `ageDays: ${ageDays.toFixed(2)}`,
144821
- `stale: ${String(stale)}`
144822
- ].join("\n");
144823
- }
144824
- };
144825
- }));
144826
- //#endregion
144827
144386
  //#region ../../packages/agent-core/src/agent/permission/policies/deny-all.ts
144828
144387
  var DenyAllPermissionPolicy;
144829
144388
  var init_deny_all = __esmMin((() => {
@@ -145483,6 +145042,38 @@ var init_summary_continuation = __esmMin((() => {
145483
145042
  }));
145484
145043
  //#endregion
145485
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
+ }
145486
145077
  async function runChildTurnToCompletion(child, signal) {
145487
145078
  const completion = await child.turn.waitForCurrentTurn(signal);
145488
145079
  const turnEnded = completion.event;
@@ -145510,7 +145101,7 @@ function shouldSuppressQueuedAttemptFailureEvent(options, error) {
145510
145101
  if (isProviderRateLimitError(error)) return true;
145511
145102
  return isAbortError$3(error) || options.signal.aborted;
145512
145103
  }
145513
- 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;
145514
145105
  var init_subagent_host = __esmMin((() => {
145515
145106
  init_src$4();
145516
145107
  init_errors$8();
@@ -145522,8 +145113,8 @@ var init_subagent_host = __esmMin((() => {
145522
145113
  init_git_context();
145523
145114
  init_subagent_batch();
145524
145115
  init_summary_continuation();
145525
- DEFAULT_SUBAGENT_TIMEOUT_MS = 1800 * 1e3;
145526
- DEFAULT_SUBAGENT_TIMEOUT_DESCRIPTION = "30 minutes";
145116
+ DEFAULT_SUBAGENT_TIMEOUT_MS = 7200 * 1e3;
145117
+ SUBAGENT_TIMEOUT_ENV = "KIMI_SUBAGENT_TIMEOUT_MS";
145527
145118
  SUMMARY_MIN_LENGTH = 200;
145528
145119
  SUMMARY_CONTINUATION_ATTEMPTS = 1;
145529
145120
  HOOK_TEXT_PREVIEW_LENGTH = 500;
@@ -145827,6 +145418,479 @@ IMPORTANT:
145827
145418
  };
145828
145419
  }));
145829
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
145830
145894
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-disabled.md?raw
145831
145895
  var agent_background_disabled_default;
145832
145896
  var init_agent_background_disabled = __esmMin((() => {
@@ -145944,6 +146008,7 @@ var init_agent$1 = __esmMin((() => {
145944
146008
  this.backgroundManager = backgroundManager;
145945
146009
  const log = options?.log;
145946
146010
  this.allowBackground = options?.allowBackground ?? true;
146011
+ this.subagentTimeoutMs = options?.subagentTimeoutMs;
145947
146012
  const typeLines = buildSubagentDescriptions(subagents);
145948
146013
  const baseDescription = `${agent_default}\n\n${this.allowBackground ? agent_background_enabled_default : agent_background_disabled_default}`;
145949
146014
  this.description = typeLines ? `${baseDescription}\n\nAvailable agent types (pass via subagent_type):\n${typeLines}` : baseDescription;
@@ -145951,6 +146016,7 @@ var init_agent$1 = __esmMin((() => {
145951
146016
  }
145952
146017
  log;
145953
146018
  allowBackground;
146019
+ subagentTimeoutMs;
145954
146020
  async resolveExecution(args) {
145955
146021
  let profileName = args.subagent_type?.length ? args.subagent_type : "coder";
145956
146022
  const resumeAgentId = args.resume?.trim();
@@ -146018,7 +146084,7 @@ var init_agent$1 = __esmMin((() => {
146018
146084
  try {
146019
146085
  taskId = this.backgroundManager.registerTask(new AgentBackgroundTask(handle, args.description, this.subagentHost, controller), {
146020
146086
  detached: runInBackground,
146021
- timeoutMs: DEFAULT_SUBAGENT_TIMEOUT_MS,
146087
+ timeoutMs: this.subagentTimeoutMs ?? 72e5,
146022
146088
  signal: runInBackground ? void 0 : signal
146023
146089
  });
146024
146090
  signal.removeEventListener("abort", abortBeforeRegister);
@@ -146052,7 +146118,7 @@ var init_agent$1 = __esmMin((() => {
146052
146118
  if (info?.status === "completed") return { output: formatForegroundAgentSuccess(handle, await this.backgroundManager.readOutput(taskId)) };
146053
146119
  const timedOut = info?.status === "timed_out";
146054
146120
  return {
146055
- 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),
146056
146122
  isError: true
146057
146123
  };
146058
146124
  }
@@ -146166,12 +146232,14 @@ var init_agent_swarm = __esmMin((() => {
146166
146232
  AgentSwarmTool = class {
146167
146233
  subagentHost;
146168
146234
  swarmMode;
146235
+ subagentTimeoutMs;
146169
146236
  name = "AgentSwarm";
146170
146237
  description = agent_swarm_default;
146171
146238
  parameters = toInputJsonSchema(AgentSwarmToolInputSchema);
146172
- constructor(subagentHost, swarmMode) {
146239
+ constructor(subagentHost, swarmMode, subagentTimeoutMs) {
146173
146240
  this.subagentHost = subagentHost;
146174
146241
  this.swarmMode = swarmMode;
146242
+ this.subagentTimeoutMs = subagentTimeoutMs;
146175
146243
  }
146176
146244
  resolveExecution(args) {
146177
146245
  const agentCount = (args.items?.length ?? 0) + Object.keys(args.resume_agent_ids ?? {}).length;
@@ -146212,7 +146280,7 @@ var init_agent_swarm = __esmMin((() => {
146212
146280
  runInBackground: false,
146213
146281
  swarmItem: spec.item,
146214
146282
  signal,
146215
- timeout: DEFAULT_SUBAGENT_TIMEOUT_MS
146283
+ timeout: this.subagentTimeoutMs ?? 72e5
146216
146284
  };
146217
146285
  if (spec.kind === "resume") return {
146218
146286
  ...common,
@@ -146235,7 +146303,7 @@ var init_agent_swarm = __esmMin((() => {
146235
146303
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md?raw
146236
146304
  var ask_user_default;
146237
146305
  var init_ask_user$1 = __esmMin((() => {
146238
- 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";
146239
146307
  }));
146240
146308
  //#endregion
146241
146309
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.ts
@@ -153945,6 +154013,7 @@ var init_tool$1 = __esmMin((() => {
153945
154013
  init_output();
153946
154014
  init_tool_naming();
153947
154015
  init_profile();
154016
+ init_subagent_host();
153948
154017
  init_skill$4();
153949
154018
  init_llm_request_logger();
153950
154019
  init_builtin();
@@ -154501,9 +154570,10 @@ var init_tool$1 = __esmMin((() => {
154501
154570
  this.agent.skills?.registry.listInvocableSkills().length && new SkillTool(this.agent),
154502
154571
  this.agent.subagentHost && new AgentTool(this.agent.subagentHost, background, DEFAULT_AGENT_PROFILES["agent"]?.subagents, {
154503
154572
  allowBackground,
154504
- log: this.agent.log
154573
+ log: this.agent.log,
154574
+ subagentTimeoutMs: resolveSubagentTimeoutMs(this.agent.kimiConfig?.subagent?.timeoutMs)
154505
154575
  }),
154506
- 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)),
154507
154577
  toolServices?.webSearcher && new WebSearchTool(toolServices.webSearcher),
154508
154578
  toolServices?.urlFetcher && new FetchURLTool(toolServices.urlFetcher)
154509
154579
  ].filter((tool) => !!tool).map((tool) => [tool.name, tool]));
@@ -156368,17 +156438,18 @@ var init_agent = __esmMin((() => {
156368
156438
  }
156369
156439
  /**
156370
156440
  * Single decision point for select_tools progressive disclosure. All three
156371
- * gates must be open: the model declares the `select_tools` capability, the
156372
- * model declares `tool_use` (a model without tool use registering
156373
- * select_tools is a contradiction), and the `tool-select` experimental flag
156374
- * is on. Every consumer — top-level tools[] convergence, select_tools
156375
- * registration, manifest announcements, projection shaping — reads this
156376
- * instead of re-deriving the conditions, so degradation is lossless: any
156377
- * 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.
156378
156449
  */
156379
156450
  get toolSelectEnabled() {
156380
156451
  const capability = this.config.modelCapabilities;
156381
- 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");
156382
156453
  }
156383
156454
  get generate() {
156384
156455
  return async (provider, systemPrompt, tools, history, callbacks, options) => {
@@ -156610,6 +156681,7 @@ var init_agent = __esmMin((() => {
156610
156681
  pauseGoal: () => this.goal.pauseGoal(),
156611
156682
  resumeGoal: () => this.goal.resumeGoal(),
156612
156683
  cancelGoal: () => this.goal.cancelGoal(),
156684
+ getCronTasks: () => ({ tasks: this.cron?.listTaskSnapshots() ?? [] }),
156613
156685
  getBackgroundOutput: (payload) => this.background.readOutput(payload.taskId, payload.tail),
156614
156686
  getContext: () => this.context.data(),
156615
156687
  getConfig: () => this.config.data(),
@@ -187461,6 +187533,8 @@ var init_session$1 = __esmMin((() => {
187461
187533
  };
187462
187534
  writeMetadataPromise = Promise.resolve();
187463
187535
  agentsMdWarning;
187536
+ printSteerDeadline;
187537
+ printSteerTurns = 0;
187464
187538
  constructor(options) {
187465
187539
  this.options = options;
187466
187540
  this.logHandle = options.id === void 0 ? void 0 : getRootLogger().attachSession({
@@ -187647,23 +187721,18 @@ var init_session$1 = __esmMin((() => {
187647
187721
  * Wait for all still-running background tasks (across every agent) to reach a
187648
187722
  * terminal state before a `kimi -p` (print) run exits.
187649
187723
  *
187650
- * Gated by `background.keep_alive_on_exit`: when it is not `true`, this returns
187651
- * immediately so print mode keeps its default single-turn semantics. The wait is
187652
- * bounded by `background.print_wait_ceiling_s` (default 3600s) so a wedged task
187653
- * 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.
187654
187729
  *
187655
187730
  * Terminal notifications are suppressed for each task while we wait, so a task
187656
187731
  * completing cannot `turn.steer` the (already finished) main agent into launching
187657
- * a new turn.
187732
+ * a new turn. (This is exactly what `'steer'` mode avoids by never calling here.)
187658
187733
  */
187659
187734
  async waitForBackgroundTasksOnPrint() {
187660
- if (!resolveConfigValue({
187661
- env: process.env,
187662
- envKey: BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV,
187663
- configValue: this.options.background?.keepAliveOnExit,
187664
- defaultValue: false,
187665
- parseEnv: parseBooleanEnv
187666
- })) return;
187735
+ if (this.resolvePrintBackgroundMode() !== "drain") return;
187667
187736
  const timeoutMs = (this.options.background?.printWaitCeilingS ?? 3600) * 1e3;
187668
187737
  const deadline = Date.now() + timeoutMs;
187669
187738
  const seen = /* @__PURE__ */ new Set();
@@ -187699,6 +187768,67 @@ var init_session$1 = __esmMin((() => {
187699
187768
  });
187700
187769
  }
187701
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
+ }
187702
187832
  async createAgent(config, options = {}) {
187703
187833
  await this.skillsReady;
187704
187834
  const type = config.type ?? "main";
@@ -206200,7 +206330,9 @@ var init_model_alias_merge = __esmMin((() => {
206200
206330
  "model",
206201
206331
  "maxContextSize",
206202
206332
  "capabilities",
206203
- "displayName"
206333
+ "displayName",
206334
+ "supportEfforts",
206335
+ "defaultEffort"
206204
206336
  ]);
206205
206337
  }));
206206
206338
  //#endregion
@@ -206928,6 +207060,10 @@ function toModelEntry(value) {
206928
207060
  }
206929
207061
  if (typeof value["tool_call"] === "boolean") entry.tool_call = value["tool_call"];
206930
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;
206931
207067
  const modalities = value["modalities"];
206932
207068
  if (isRecord$10(modalities)) {
206933
207069
  const input = toStringArrayOrUndefined(modalities["input"]);
@@ -207001,7 +207137,7 @@ async function fetchCustomRegistry(source, fetchImpl = fetch, signal) {
207001
207137
  function capabilitiesFromCustomEntry(model) {
207002
207138
  const caps = /* @__PURE__ */ new Set();
207003
207139
  if (model.tool_call === true) caps.add("tool_use");
207004
- if (model.reasoning === true) caps.add("thinking");
207140
+ if (model.reasoning === true || (model.support_efforts?.length ?? 0) > 0) caps.add("thinking");
207005
207141
  if (model.modalities?.input?.includes("image") === true) caps.add("image_in");
207006
207142
  if (model.modalities?.input?.includes("video") === true) caps.add("video_in");
207007
207143
  if (model.modalities?.output?.includes("image") === true) caps.add("image_out");
@@ -207009,7 +207145,7 @@ function capabilitiesFromCustomEntry(model) {
207009
207145
  return [...caps];
207010
207146
  }
207011
207147
  function hasRichCapabilityHints(model) {
207012
- 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;
207013
207149
  }
207014
207150
  function resolveMaxContextSize(model) {
207015
207151
  const context = model.limit?.context;
@@ -207051,7 +207187,9 @@ function applyCustomRegistryProvider(config, entry, source) {
207051
207187
  model: model.id,
207052
207188
  maxContextSize,
207053
207189
  capabilities,
207054
- displayName
207190
+ displayName,
207191
+ ...model.support_efforts !== void 0 ? { supportEfforts: model.support_efforts } : {},
207192
+ ...model.default_effort !== void 0 ? { defaultEffort: model.default_effort } : {}
207055
207193
  }, CUSTOM_REGISTRY_MODEL_FIELDS);
207056
207194
  }
207057
207195
  config.models = existingModels;
@@ -207800,7 +207938,7 @@ function resolveModelCapabilities(alias, provider) {
207800
207938
  thinking: declared.has("thinking") || declared.has("always_thinking") || detected.thinking,
207801
207939
  tool_use: declared.has("tool_use") || detected.tool_use,
207802
207940
  max_context_tokens: alias.maxContextSize,
207803
- select_tools: declared.has("select_tools") || detected.select_tools === true
207941
+ dynamically_loaded_tools: declared.has("dynamically_loaded_tools") || detected.dynamically_loaded_tools === true
207804
207942
  };
207805
207943
  }
207806
207944
  function toKosongProviderConfig(provider, model, modelProtocol, kimiRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey, adaptiveThinking, betaApi, supportEfforts) {
@@ -208143,6 +208281,9 @@ var init_rpc$1 = __esmMin((() => {
208143
208281
  waitForBackgroundTasksOnPrint(_payload) {
208144
208282
  return this.session.waitForBackgroundTasksOnPrint();
208145
208283
  }
208284
+ handlePrintMainTurnCompleted(_payload) {
208285
+ return this.session.handlePrintMainTurnCompleted();
208286
+ }
208146
208287
  addAdditionalDir(payload) {
208147
208288
  return this.session.addAdditionalDir(payload.path, payload.persist);
208148
208289
  }
@@ -208245,6 +208386,9 @@ var init_rpc$1 = __esmMin((() => {
208245
208386
  async cancelGoal({ agentId, ...payload }) {
208246
208387
  return (await this.getAgent(agentId)).cancelGoal(payload);
208247
208388
  }
208389
+ async getCronTasks({ agentId, ...payload }) {
208390
+ return (await this.getAgent(agentId)).getCronTasks(payload);
208391
+ }
208248
208392
  async getBackgroundOutput({ agentId, ...payload }) {
208249
208393
  return (await this.getAgent(agentId)).getBackgroundOutput(payload);
208250
208394
  }
@@ -210037,6 +210181,9 @@ var init_core_impl = __esmMin((() => {
210037
210181
  waitForBackgroundTasksOnPrint({ sessionId, ...payload }) {
210038
210182
  return this.sessionApi(sessionId).waitForBackgroundTasksOnPrint(payload);
210039
210183
  }
210184
+ handlePrintMainTurnCompleted({ sessionId, ...payload }) {
210185
+ return this.sessionApi(sessionId).handlePrintMainTurnCompleted(payload);
210186
+ }
210040
210187
  addAdditionalDir({ sessionId, ...payload }) {
210041
210188
  return this.requireSession(sessionId).addAdditionalDir(payload.path, payload.persist);
210042
210189
  }
@@ -210058,6 +210205,9 @@ var init_core_impl = __esmMin((() => {
210058
210205
  cancelGoal({ sessionId, ...payload }) {
210059
210206
  return Promise.resolve(this.sessionApi(sessionId).cancelGoal(payload));
210060
210207
  }
210208
+ getCronTasks({ sessionId, ...payload }) {
210209
+ return Promise.resolve(this.sessionApi(sessionId).getCronTasks(payload));
210210
+ }
210061
210211
  async installPlugin(payload) {
210062
210212
  await this.pluginsReady;
210063
210213
  this.assertPluginsLoaded();
@@ -216811,25 +216961,29 @@ function mapContentPart(part) {
216811
216961
  * Build the protocol-shaped `Message.content[]` for one ContextMessage.
216812
216962
  *
216813
216963
  * Order:
216814
- * 1. For `tool` role: emit a SINGLE `tool_result` part. The output is the
216815
- * flattened text of the kosong message's content parts (most tool
216816
- * 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`.
216817
216971
  * 2. For other roles: emit each content part mapped per `mapContentPart`,
216818
216972
  * THEN append one `tool_use` part per `ToolCall` (assistant only).
216819
216973
  */
216820
216974
  function buildProtocolContent(msg) {
216821
216975
  if (msg.role === "tool") {
216822
216976
  if (msg.toolCallId === void 0) return msg.content.map((p) => mapContentPart(p));
216823
- 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("");
216824
216978
  return [msg.isError === true ? {
216825
216979
  type: "tool_result",
216826
216980
  tool_call_id: msg.toolCallId,
216827
- output: flattenedOutput,
216981
+ output,
216828
216982
  is_error: true
216829
216983
  } : {
216830
216984
  type: "tool_result",
216831
216985
  tool_call_id: msg.toolCallId,
216832
- output: flattenedOutput
216986
+ output
216833
216987
  }];
216834
216988
  }
216835
216989
  const base = msg.content.map((p) => mapContentPart(p));
@@ -219521,14 +219675,27 @@ var Session = class {
219521
219675
  /**
219522
219676
  * Block until every still-running background task (across all agents in this
219523
219677
  * session) reaches a terminal state. Used by `kimi -p` after the main agent's
219524
- * turn finishes when `background.keep_alive_on_exit` is `true`, so background
219525
- * subagents get a chance to complete before the process exits. No-op when
219526
- * `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`.
219527
219682
  */
219528
219683
  async waitForBackgroundTasksOnPrint() {
219529
219684
  this.ensureOpen();
219530
219685
  await this.rpc.waitForBackgroundTasksOnPrint({ sessionId: this.id });
219531
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
+ }
219532
219699
  async createGoal(input) {
219533
219700
  this.ensureOpen();
219534
219701
  return this.rpc.createGoal({
@@ -219552,6 +219719,15 @@ var Session = class {
219552
219719
  this.ensureOpen();
219553
219720
  return this.rpc.cancelGoal({ sessionId: this.id });
219554
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
+ }
219555
219731
  async listMcpServers() {
219556
219732
  this.ensureOpen();
219557
219733
  return this.rpc.listMcpServers({ sessionId: this.id });
@@ -220414,6 +220590,9 @@ var SDKRpcClientBase = class {
220414
220590
  async waitForBackgroundTasksOnPrint(input) {
220415
220591
  return (await this.getRpc()).waitForBackgroundTasksOnPrint({ sessionId: input.sessionId });
220416
220592
  }
220593
+ async handlePrintMainTurnCompleted(input) {
220594
+ return (await this.getRpc()).handlePrintMainTurnCompleted({ sessionId: input.sessionId });
220595
+ }
220417
220596
  async createGoal(input) {
220418
220597
  return (await this.getRpc()).createGoal({
220419
220598
  sessionId: input.sessionId,
@@ -220446,6 +220625,12 @@ var SDKRpcClientBase = class {
220446
220625
  agentId: this.interactiveAgentId
220447
220626
  });
220448
220627
  }
220628
+ async getCronTasks(input) {
220629
+ return (await this.getRpc()).getCronTasks({
220630
+ sessionId: input.sessionId,
220631
+ agentId: this.interactiveAgentId
220632
+ });
220633
+ }
220449
220634
  async listMcpServers(input) {
220450
220635
  return (await this.getRpc()).listMcpServers({ sessionId: input.sessionId });
220451
220636
  }
@@ -220774,7 +220959,7 @@ function capabilityToStrings(capability) {
220774
220959
  if (capability.audio_in) caps.push("audio_in");
220775
220960
  if (capability.thinking) caps.push("thinking");
220776
220961
  if (capability.tool_use) caps.push("tool_use");
220777
- if (capability.select_tools === true) caps.push("select_tools");
220962
+ if (capability.dynamically_loaded_tools === true) caps.push("dynamically_loaded_tools");
220778
220963
  return caps.length > 0 ? caps : void 0;
220779
220964
  }
220780
220965
  /** Builds a kimi-code model alias from a normalized catalog model. */
@@ -303679,7 +303864,7 @@ const REMOVE_CONFIRM_REMOVE = "remove";
303679
303864
  const INSTALL_TRUST_EXIT = "exit";
303680
303865
  const INSTALL_TRUST_TRUST = "trust";
303681
303866
  const ELLIPSIS$6 = "…";
303682
- const WEB_BRIDGE_URL = "https://www.kimi.com/features/webbridge";
303867
+ const WEB_BRIDGE_URL = "https://www.kimi.com/features/webbridge#local-agent";
303683
303868
  const WEB_BRIDGE_ENTRY = {
303684
303869
  id: "kimi-webbridge",
303685
303870
  displayName: "Kimi WebBridge",
@@ -370845,7 +371030,7 @@ async function runHeadlessGoal(session, goal, model, outputFormat, stdout, stder
370845
371030
  if (event.type === "goal.updated" && event.agentId === "main" && event.change?.kind === "completion" && event.snapshot !== null) completedSnapshot = event.snapshot;
370846
371031
  });
370847
371032
  try {
370848
- await runPromptTurn(session, goal.objective, outputFormat, stdout, stderr, true);
371033
+ await runPromptTurn(session, goal.objective, outputFormat, stdout, stderr);
370849
371034
  } finally {
370850
371035
  unsubscribeGoalEvents();
370851
371036
  const snapshot = completedSnapshot ?? (await session.getGoal()).goal;
@@ -370972,17 +371157,26 @@ function signalExitCode(signal) {
370972
371157
  if (signal === "SIGHUP") return 129;
370973
371158
  return 143;
370974
371159
  }
370975
- function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoalTerminal = false) {
371160
+ function runPromptTurn(session, prompt, outputFormat, stdout, stderr) {
370976
371161
  let activeTurnId;
370977
371162
  let activeAgentId;
370978
- let latestStartedTurnId;
370979
371163
  const outputWriter = outputFormat === "stream-json" ? new PromptJsonWriter(stdout) : new PromptTranscriptWriter(stdout, stderr);
370980
371164
  let settled = false;
370981
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
+ };
370982
371175
  return new Promise((resolve, reject) => {
370983
371176
  const finish = (error) => {
370984
371177
  if (settled) return;
370985
371178
  settled = true;
371179
+ releaseEventLoop();
370986
371180
  unsubscribe?.();
370987
371181
  outputWriter.finish();
370988
371182
  if (error !== void 0) {
@@ -370991,21 +371185,39 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
370991
371185
  }
370992
371186
  resolve();
370993
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
+ };
370994
371207
  unsubscribe = session.onEvent((event) => {
370995
371208
  if (event.type === "error") {
370996
371209
  if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
370997
371210
  finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
370998
371211
  return;
370999
371212
  }
371000
- if (event.type === "turn.started" && activeTurnId === void 0) {
371213
+ if (event.type === "turn.started") {
371001
371214
  if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
371002
371215
  activeTurnId = event.turnId;
371003
371216
  activeAgentId = event.agentId;
371004
- latestStartedTurnId = event.turnId;
371005
371217
  return;
371006
371218
  }
371007
- if (waitForGoalTerminal && event.type === "goal.updated" && event.agentId === PROMPT_MAIN_AGENT_ID && activeTurnId === void 0 && event.snapshot !== null && event.snapshot.status !== "active") {
371008
- 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();
371009
371221
  return;
371010
371222
  }
371011
371223
  if (activeTurnId === void 0 || activeAgentId === void 0 || !hasTurnId(event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
@@ -371042,23 +371254,9 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
371042
371254
  case "turn.ended":
371043
371255
  if (event.reason === "completed") {
371044
371256
  outputWriter.flushAssistant();
371045
- if (waitForGoalTerminal) {
371046
- const completedTurnId = event.turnId;
371047
- activeTurnId = void 0;
371048
- activeAgentId = void 0;
371049
- (async () => {
371050
- try {
371051
- const { goal } = await session.getGoal();
371052
- if (activeTurnId !== void 0 || latestStartedTurnId !== completedTurnId) return;
371053
- if (goal?.status === "active") return;
371054
- await finishCompletedTurn();
371055
- } catch (error) {
371056
- finish(error instanceof Error ? error : new Error(String(error)));
371057
- }
371058
- })();
371059
- return;
371060
- }
371061
- finishCompletedTurn();
371257
+ activeTurnId = void 0;
371258
+ activeAgentId = void 0;
371259
+ evaluateRunCompletion();
371062
371260
  return;
371063
371261
  }
371064
371262
  finish(new Error(formatTurnEndedFailure(event)));
@@ -371081,7 +371279,6 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
371081
371279
  case "subagent.started":
371082
371280
  case "subagent.suspended":
371083
371281
  case "tool.list.updated":
371084
- case "turn.started":
371085
371282
  case "turn.step.completed":
371086
371283
  case "warning": return;
371087
371284
  }
@@ -371092,9 +371289,12 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, waitForGoa
371092
371289
  async function finishCompletedTurn() {
371093
371290
  outputWriter.flushAssistant();
371094
371291
  try {
371095
- await session.waitForBackgroundTasksOnPrint();
371292
+ if (await session.handlePrintMainTurnCompleted() === "continue") {
371293
+ holdEventLoop();
371294
+ return;
371295
+ }
371096
371296
  } catch (error) {
371097
- log.warn("waitForBackgroundTasksOnPrint failed", { error });
371297
+ log.warn("handlePrintMainTurnCompleted failed", { error });
371098
371298
  }
371099
371299
  finish();
371100
371300
  }