@moonshot-ai/kimi-code 0.22.3 → 0.23.0

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 (72) hide show
  1. package/dist/main.mjs +1802 -730
  2. package/dist-web/assets/{CodeBlockNode--K4eRmNh.js → CodeBlockNode-Bi8jIiGS.js} +1 -1
  3. package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-8z4SytTj.js → CodeBlockShell.vue_vue_type_style_index_0_lang-B0EX64Fy.js} +1 -1
  4. package/dist-web/assets/DesignSystemView-CxzrbiS1.css +1 -0
  5. package/dist-web/assets/DesignSystemView-uZVLgoX6.js +12 -0
  6. package/dist-web/assets/{Tooltip-sFOhW5c-.js → Tooltip-BvXZgTKH.js} +1 -1
  7. package/dist-web/assets/{arc-b1f-JaXG.js → arc-Dq8eiFQP.js} +1 -1
  8. package/dist-web/assets/{architectureDiagram-3BPJPVTR-DRV073hL.js → architectureDiagram-3BPJPVTR-b6nIVGZD.js} +1 -1
  9. package/dist-web/assets/{blockDiagram-GPEHLZMM-BG0TxS08.js → blockDiagram-GPEHLZMM-B3B2WOkj.js} +1 -1
  10. package/dist-web/assets/{c4Diagram-AAUBKEIU-CsocakY-.js → c4Diagram-AAUBKEIU-BKyN3yLu.js} +1 -1
  11. package/dist-web/assets/channel-DYPbvBMu.js +1 -0
  12. package/dist-web/assets/{chunk-2J33WTMH-D7mBRhm2.js → chunk-2J33WTMH-HN_s9nzs.js} +1 -1
  13. package/dist-web/assets/{chunk-4BX2VUAB-BMOSnPC4.js → chunk-4BX2VUAB-fxHwG6FR.js} +1 -1
  14. package/dist-web/assets/{chunk-55IACEB6-DH7ZU8ec.js → chunk-55IACEB6-D-XDLZ_9.js} +1 -1
  15. package/dist-web/assets/{chunk-727SXJPM-CaYu9Tne.js → chunk-727SXJPM-BdVZmbdu.js} +1 -1
  16. package/dist-web/assets/{chunk-AQP2D5EJ-Dd2oKOPL.js → chunk-AQP2D5EJ-C0q5GUyy.js} +1 -1
  17. package/dist-web/assets/{chunk-FMBD7UC4-JdHZqUnV.js → chunk-FMBD7UC4-BHvs9p4D.js} +1 -1
  18. package/dist-web/assets/{chunk-ND2GUHAM-DBm1Ahcp.js → chunk-ND2GUHAM-BCuwZrYk.js} +1 -1
  19. package/dist-web/assets/{chunk-QZHKN3VN-DmqufK50.js → chunk-QZHKN3VN-sdMQprxY.js} +1 -1
  20. package/dist-web/assets/classDiagram-4FO5ZUOK-7hR3h9FG.js +1 -0
  21. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-7hR3h9FG.js +1 -0
  22. package/dist-web/assets/{cose-bilkent-S5V4N54A-HNWFCNh9.js → cose-bilkent-S5V4N54A-DAKVyg0y.js} +1 -1
  23. package/dist-web/assets/{dagre-BM42HDAG-Dv6WU7Ym.js → dagre-BM42HDAG-fE0QBWG7.js} +1 -1
  24. package/dist-web/assets/{diagram-2AECGRRQ-DlQ3gFah.js → diagram-2AECGRRQ-DhTfW7BA.js} +1 -1
  25. package/dist-web/assets/{diagram-5GNKFQAL-DdXhz5jN.js → diagram-5GNKFQAL-BFxW-PQU.js} +1 -1
  26. package/dist-web/assets/{diagram-KO2AKTUF-CbAHWTiw.js → diagram-KO2AKTUF-D29F_M5j.js} +1 -1
  27. package/dist-web/assets/{diagram-LMA3HP47-DhASU77a.js → diagram-LMA3HP47-DVsX1Zqb.js} +1 -1
  28. package/dist-web/assets/{diagram-OG6HWLK6-vyegCtBh.js → diagram-OG6HWLK6-C1xkLsBo.js} +1 -1
  29. package/dist-web/assets/{erDiagram-TEJ5UH35-u41T2VU1.js → erDiagram-TEJ5UH35-RExHqUHF.js} +1 -1
  30. package/dist-web/assets/{flowDiagram-I6XJVG4X-BlZR3M4a.js → flowDiagram-I6XJVG4X-Bb2CxfqX.js} +1 -1
  31. package/dist-web/assets/{ganttDiagram-6RSMTGT7-CBIULxGb.js → ganttDiagram-6RSMTGT7-CPgclO8f.js} +1 -1
  32. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-C6SGJJUo.js → gitGraphDiagram-PVQCEYII-CkbhTuQx.js} +1 -1
  33. package/dist-web/assets/index-BBqKjyg_.css +1 -0
  34. package/dist-web/assets/{index-BNJo44oI.js → index-C20P6k3r.js} +1 -1
  35. package/dist-web/assets/{index-D3WSVtYV.js → index-CvZ8g4D9.js} +2 -2
  36. package/dist-web/assets/index-SziNj6Ky.js +292 -0
  37. package/dist-web/assets/{index10-CizaPNSY.js → index10-C34WriXe.js} +1 -1
  38. package/dist-web/assets/{index11-C3vBSelg.js → index11-DnWluvv0.js} +1 -1
  39. package/dist-web/assets/{index5-DCmdgd-c.js → index5-DMfYcwho.js} +1 -1
  40. package/dist-web/assets/{index6-DeBB3wcO.js → index6-CM7_ALPI.js} +1 -1
  41. package/dist-web/assets/{index7-D0RS__NS.js → index7-DOz39QJX.js} +1 -1
  42. package/dist-web/assets/{index8-ByeMRBS0.js → index8-DDNiq5_W.js} +2 -2
  43. package/dist-web/assets/{infoDiagram-5YYISTIA-BEQeL8qo.js → infoDiagram-5YYISTIA-DhTeo1nz.js} +1 -1
  44. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-BGEkrGOU.js → ishikawaDiagram-YF4QCWOH-C6zNOyfV.js} +1 -1
  45. package/dist-web/assets/{journeyDiagram-JHISSGLW-DAEyZppi.js → journeyDiagram-JHISSGLW-GqHLo20m.js} +1 -1
  46. package/dist-web/assets/{kanban-definition-UN3LZRKU-6nYYByea.js → kanban-definition-UN3LZRKU-COLCFNmp.js} +1 -1
  47. package/dist-web/assets/{linear-RPDuZEc_.js → linear-BNWcliWU.js} +1 -1
  48. package/dist-web/assets/{mermaid.core-CmwZMEVS.js → mermaid.core-Dvwdy-Wb.js} +4 -4
  49. package/dist-web/assets/{mindmap-definition-RKZ34NQL-MLtqKWd1.js → mindmap-definition-RKZ34NQL-MmqWhMt1.js} +1 -1
  50. package/dist-web/assets/{pieDiagram-4H26LBE5-Bfifo7At.js → pieDiagram-4H26LBE5-D2HeVweL.js} +1 -1
  51. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-DZRwxAP7.js → quadrantDiagram-W4KKPZXB-D0Gnhh0w.js} +1 -1
  52. package/dist-web/assets/{requirementDiagram-4Y6WPE33-lazNkbiM.js → requirementDiagram-4Y6WPE33-DV_rvSe0.js} +1 -1
  53. package/dist-web/assets/{sankeyDiagram-5OEKKPKP-D4yxPAzq.js → sankeyDiagram-5OEKKPKP-CIOHPqwt.js} +1 -1
  54. package/dist-web/assets/{sequenceDiagram-3UESZ5HK-D7Cjzr6g.js → sequenceDiagram-3UESZ5HK-OMPW2dQZ.js} +1 -1
  55. package/dist-web/assets/{stateDiagram-AJRCARHV-0z8M0BXQ.js → stateDiagram-AJRCARHV-DGC2N7WX.js} +1 -1
  56. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BWIlZ-Ms.js +1 -0
  57. package/dist-web/assets/{timeline-definition-PNZ67QCA-Cfxia7fX.js → timeline-definition-PNZ67QCA-DVD8syC3.js} +1 -1
  58. package/dist-web/assets/{vennDiagram-CIIHVFJN-CBtQvJlD.js → vennDiagram-CIIHVFJN-DjolQSRq.js} +1 -1
  59. package/dist-web/assets/{vue.runtime.esm-bundler-BJW6XsQa.js → vue.runtime.esm-bundler-DOh9SYil.js} +1 -1
  60. package/dist-web/assets/{wardley-L42UT6IY-lhlQsxcl.js → wardley-L42UT6IY-4aEVZF_y.js} +1 -1
  61. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-CNRYkXLH.js → wardleyDiagram-YWT4CUSO-CMJ7T5Da.js} +1 -1
  62. package/dist-web/assets/{xychartDiagram-2RQKCTM6-mUzDW-MM.js → xychartDiagram-2RQKCTM6-SWdAUpzG.js} +1 -1
  63. package/dist-web/index.html +2 -2
  64. package/package.json +8 -8
  65. package/dist-web/assets/DesignSystemView-BskvdBnn.css +0 -1
  66. package/dist-web/assets/DesignSystemView-gstQZ6Dp.js +0 -12
  67. package/dist-web/assets/channel-Cwe7TdNc.js +0 -1
  68. package/dist-web/assets/classDiagram-4FO5ZUOK-CQ4_AXuT.js +0 -1
  69. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-CQ4_AXuT.js +0 -1
  70. package/dist-web/assets/index-CK7yaL8y.js +0 -279
  71. package/dist-web/assets/index-D0u_059j.css +0 -1
  72. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-lF1PY-XM.js +0 -1
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import { createRequire } from "node:module";
7
7
  import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
8
8
  import * as fs$16 from "node:fs";
9
- import Vt, { appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
9
+ import Vt, { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
10
10
  import * as path$15 from "node:path";
11
11
  import path, { basename, dirname, extname, isAbsolute, join, normalize, posix, relative, resolve, sep, win32 } from "node:path";
12
12
  import { spawn } from "child_process";
@@ -36,7 +36,7 @@ import { finished, pipeline as pipeline$1 } from "node:stream/promises";
36
36
  import * as ks from "zlib";
37
37
  import qr from "zlib";
38
38
  import * as nodeOs from "node:os";
39
- import os, { arch, homedir as homedir$1, hostname, networkInterfaces, platform, release, tmpdir, type as type$1, userInfo } from "node:os";
39
+ import os, { arch, cpus, freemem, homedir as homedir$1, hostname, loadavg, networkInterfaces, platform, release, tmpdir, totalmem, type as type$1, userInfo } from "node:os";
40
40
  import fs, { access, appendFile, chmod, copyFile, cp, lstat as lstat$1, mkdir, mkdtemp, open as open$1, readFile, readdir as readdir$1, realpath as realpath$1, rename, rm, stat as stat$2, unlink, utimes, writeFile as writeFile$1 } from "node:fs/promises";
41
41
  import { execFile, execFileSync, execSync, spawn as spawn$1, spawnSync } from "node:child_process";
42
42
  import * as posixPath from "node:path/posix";
@@ -967,6 +967,18 @@ function isContentPart(part) {
967
967
  const t = part.type;
968
968
  return t === "text" || t === "think" || t === "image_url" || t === "audio_url" || t === "video_url";
969
969
  }
970
+ /**
971
+ * True for a message whose only payload is `tools` — the dynamic tool-loading
972
+ * primitive (see {@link Message.tools}). Message-level tool declarations are a
973
+ * Kimi wire feature; every other provider must skip such a message entirely:
974
+ * their explicit field construction already keeps the `tools` field off the
975
+ * wire, but the leftover empty message would be rejected (OpenAI: system
976
+ * message without content) or serialized as a garbage `<system></system>`
977
+ * turn (Anthropic/Google system-to-user wrapping).
978
+ */
979
+ function isToolDeclarationOnlyMessage(message) {
980
+ return message.tools !== void 0 && message.tools.length > 0 && message.content.length === 0 && message.toolCalls.length === 0;
981
+ }
970
982
  /** Check if a streamed part is a ToolCall. */
971
983
  function isToolCall(part) {
972
984
  return part.type === "function";
@@ -1051,7 +1063,7 @@ var init_provider$1 = __esmMin((() => {}));
1051
1063
  function isUnknownCapability(capability) {
1052
1064
  if (capability === UNKNOWN_CAPABILITY) return true;
1053
1065
  if (capability[UNKNOWN_CAPABILITY_MARKER] === true) return true;
1054
- return !capability.image_in && !capability.video_in && !capability.audio_in && !capability.thinking && !capability.tool_use && capability.max_context_tokens === 0;
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;
1055
1067
  }
1056
1068
  var UNKNOWN_CAPABILITY_MARKER, UNKNOWN_CAPABILITY;
1057
1069
  var init_capability = __esmMin((() => {
@@ -1062,7 +1074,8 @@ var init_capability = __esmMin((() => {
1062
1074
  audio_in: false,
1063
1075
  thinking: false,
1064
1076
  tool_use: false,
1065
- max_context_tokens: 0
1077
+ max_context_tokens: 0,
1078
+ select_tools: false
1066
1079
  }, UNKNOWN_CAPABILITY_MARKER, { value: true }));
1067
1080
  }));
1068
1081
  //#endregion
@@ -9573,6 +9586,7 @@ function convertAnthropicError(error) {
9573
9586
  var INTERLEAVED_THINKING_BETA, OPUS_VERSION_RE, ADAPTIVE_MIN_VERSION, ANTHROPIC_TOOL_CALL_ID_POLICY, CEILING_BY_FAMILY_VERSION, FALLBACK_MAX_TOKENS, FAMILY_FIRST_RE, VERSION_FIRST_RE, BARE_FAMILY_RE, CACHE_CONTROL, CACHEABLE_TYPES, OMITTED_MEDIA_PLACEHOLDER, SUPPORTED_B64_MEDIA_TYPES, SUPPORTED_B64_VIDEO_TYPES, AnthropicStreamedMessage, AnthropicChatProvider;
9574
9587
  var init_anthropic = __esmMin((() => {
9575
9588
  init_errors$12();
9589
+ init_message$3();
9576
9590
  init_sdk();
9577
9591
  init_merge_user_messages();
9578
9592
  init_request_auth();
@@ -9885,7 +9899,7 @@ var init_anthropic = __esmMin((() => {
9885
9899
  text: systemPrompt,
9886
9900
  cache_control: CACHE_CONTROL
9887
9901
  }] : void 0;
9888
- const messages = mergeConsecutiveUserMessages(normalizeToolCallIdsForProvider(history, ANTHROPIC_TOOL_CALL_ID_POLICY).map((msg) => convertMessage$3(msg, this._model)), {
9902
+ const messages = mergeConsecutiveUserMessages(normalizeToolCallIdsForProvider(history.filter((msg) => !isToolDeclarationOnlyMessage(msg)), ANTHROPIC_TOOL_CALL_ID_POLICY).map((msg) => convertMessage$3(msg, this._model)), {
9889
9903
  isUser: (message) => message.role === "user",
9890
9904
  isToolResultOnly,
9891
9905
  merge: (last, next) => ({
@@ -44811,10 +44825,10 @@ function normalizeGoogleGenAIFinishReason(raw) {
44811
44825
  }
44812
44826
  }
44813
44827
  function toolToGoogleGenAI(tool) {
44814
- return { function_declarations: [{
44828
+ return { functionDeclarations: [{
44815
44829
  name: tool.name,
44816
44830
  description: tool.description,
44817
- parameters_json_schema: tool.parameters
44831
+ parametersJsonSchema: tool.parameters
44818
44832
  }] };
44819
44833
  }
44820
44834
  function toolCallIdToName(toolCallId, toolNameById) {
@@ -44900,11 +44914,11 @@ function messageToGoogleGenAI(message) {
44900
44914
  if (error instanceof ChatProviderError) throw error;
44901
44915
  throw new ChatProviderError("Tool call arguments must be valid JSON.");
44902
44916
  }
44903
- const functionCallPart = { function_call: {
44917
+ const functionCallPart = { functionCall: {
44904
44918
  name: toolCall.name,
44905
44919
  args
44906
44920
  } };
44907
- if (toolCall.extras && "thought_signature_b64" in toolCall.extras) functionCallPart["thought_signature"] = toolCall.extras["thought_signature_b64"];
44921
+ if (toolCall.extras && "thought_signature_b64" in toolCall.extras) functionCallPart["thoughtSignature"] = toolCall.extras["thought_signature_b64"];
44908
44922
  parts.push(functionCallPart);
44909
44923
  }
44910
44924
  return {
@@ -44941,7 +44955,7 @@ function toolMessageToFunctionResponseParts(message, toolNameById) {
44941
44955
  break;
44942
44956
  case "think": break;
44943
44957
  }
44944
- return [{ function_response: {
44958
+ return [{ functionResponse: {
44945
44959
  name: toolCallIdToName(message.toolCallId, toolNameById),
44946
44960
  response: { output: textOutput },
44947
44961
  parts: []
@@ -44954,6 +44968,10 @@ function messagesToGoogleGenAIContents(messages) {
44954
44968
  while (i < messages.length) {
44955
44969
  const message = messages[i];
44956
44970
  if (message === void 0) break;
44971
+ if (isToolDeclarationOnlyMessage(message)) {
44972
+ i += 1;
44973
+ continue;
44974
+ }
44957
44975
  if (message.role === "system") {
44958
44976
  const text = message.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
44959
44977
  if (text.length > 0) contents.push({
@@ -45021,7 +45039,7 @@ function messagesToGoogleGenAIContents(messages) {
45021
45039
  }
45022
45040
  return mergeConsecutiveUserMessages(contents, {
45023
45041
  isUser: (content) => content.role === "user",
45024
- isToolResultOnly: (content) => content.parts.length > 0 && content.parts.every((part) => part.function_response !== void 0),
45042
+ isToolResultOnly: (content) => content.parts.length > 0 && content.parts.every((part) => part.functionResponse !== void 0),
45025
45043
  merge: (last, next) => ({
45026
45044
  ...last,
45027
45045
  parts: [...last.parts, ...next.parts]
@@ -45046,6 +45064,7 @@ function convertGoogleGenAIError(error) {
45046
45064
  var GoogleGenAIStreamedMessage, NETWORK_RE, TIMEOUT_RE, GoogleGenAIChatProvider;
45047
45065
  var init_google_genai = __esmMin((() => {
45048
45066
  init_errors$12();
45067
+ init_message$3();
45049
45068
  init_node$6();
45050
45069
  init_merge_user_messages();
45051
45070
  init_request_auth();
@@ -45218,19 +45237,19 @@ var init_google_genai = __esmMin((() => {
45218
45237
  return this._model;
45219
45238
  }
45220
45239
  get thinkingEffort() {
45221
- const thinkingConfig = this._generationKwargs.thinking_config;
45240
+ const thinkingConfig = this._generationKwargs.thinkingConfig;
45222
45241
  if (thinkingConfig === void 0) return null;
45223
- if (thinkingConfig.thinking_level !== void 0) switch (thinkingConfig.thinking_level) {
45224
- case "MINIMAL": return thinkingConfig.include_thoughts === false ? "off" : "low";
45242
+ if (thinkingConfig.thinkingLevel !== void 0) switch (thinkingConfig.thinkingLevel) {
45243
+ case "MINIMAL": return thinkingConfig.includeThoughts === false ? "off" : "low";
45225
45244
  case "LOW": return "low";
45226
45245
  case "MEDIUM": return "medium";
45227
45246
  case "HIGH": return "high";
45228
45247
  default: return null;
45229
45248
  }
45230
- if (thinkingConfig.thinking_budget !== void 0) {
45231
- if (thinkingConfig.thinking_budget === 0) return "off";
45232
- if (thinkingConfig.thinking_budget <= 1024) return "low";
45233
- if (thinkingConfig.thinking_budget <= 4096) return "medium";
45249
+ if (thinkingConfig.thinkingBudget !== void 0) {
45250
+ if (thinkingConfig.thinkingBudget === 0) return "off";
45251
+ if (thinkingConfig.thinkingBudget <= 1024) return "low";
45252
+ if (thinkingConfig.thinkingBudget <= 4096) return "medium";
45234
45253
  return "high";
45235
45254
  }
45236
45255
  return null;
@@ -45246,7 +45265,7 @@ var init_google_genai = __esmMin((() => {
45246
45265
  const contents = messagesToGoogleGenAIContents(history);
45247
45266
  const config = {
45248
45267
  ...this._generationKwargs,
45249
- system_instruction: systemPrompt,
45268
+ systemInstruction: systemPrompt,
45250
45269
  ...tools.length > 0 ? { tools: tools.map((t) => toolToGoogleGenAI(t)) } : {}
45251
45270
  };
45252
45271
  try {
@@ -45274,45 +45293,45 @@ var init_google_genai = __esmMin((() => {
45274
45293
  });
45275
45294
  }
45276
45295
  withThinking(effort) {
45277
- const thinkingConfig = { include_thoughts: true };
45296
+ const thinkingConfig = { includeThoughts: true };
45278
45297
  if (this._model.includes("gemini-3")) switch (effort) {
45279
45298
  case "off":
45280
- thinkingConfig.thinking_level = "MINIMAL";
45281
- thinkingConfig.include_thoughts = false;
45299
+ thinkingConfig.thinkingLevel = "MINIMAL";
45300
+ thinkingConfig.includeThoughts = false;
45282
45301
  break;
45283
45302
  case "low":
45284
- thinkingConfig.thinking_level = "LOW";
45303
+ thinkingConfig.thinkingLevel = "LOW";
45285
45304
  break;
45286
45305
  case "medium":
45287
- thinkingConfig.thinking_level = "MEDIUM";
45306
+ thinkingConfig.thinkingLevel = "MEDIUM";
45288
45307
  break;
45289
45308
  case "high":
45290
45309
  case "xhigh":
45291
45310
  case "max":
45292
- thinkingConfig.thinking_level = "HIGH";
45311
+ thinkingConfig.thinkingLevel = "HIGH";
45293
45312
  break;
45294
45313
  }
45295
45314
  else switch (effort) {
45296
45315
  case "off":
45297
- thinkingConfig.thinking_budget = 0;
45298
- thinkingConfig.include_thoughts = false;
45316
+ thinkingConfig.thinkingBudget = 0;
45317
+ thinkingConfig.includeThoughts = false;
45299
45318
  break;
45300
45319
  case "low":
45301
- thinkingConfig.thinking_budget = 1024;
45302
- thinkingConfig.include_thoughts = true;
45320
+ thinkingConfig.thinkingBudget = 1024;
45321
+ thinkingConfig.includeThoughts = true;
45303
45322
  break;
45304
45323
  case "medium":
45305
- thinkingConfig.thinking_budget = 4096;
45306
- thinkingConfig.include_thoughts = true;
45324
+ thinkingConfig.thinkingBudget = 4096;
45325
+ thinkingConfig.includeThoughts = true;
45307
45326
  break;
45308
45327
  case "high":
45309
45328
  case "xhigh":
45310
45329
  case "max":
45311
- thinkingConfig.thinking_budget = 32e3;
45312
- thinkingConfig.include_thoughts = true;
45330
+ thinkingConfig.thinkingBudget = 32e3;
45331
+ thinkingConfig.includeThoughts = true;
45313
45332
  break;
45314
45333
  }
45315
- return this.withGenerationKwargs({ thinking_config: thinkingConfig });
45334
+ return this.withGenerationKwargs({ thinkingConfig });
45316
45335
  }
45317
45336
  withGenerationKwargs(kwargs) {
45318
45337
  const clone = this._clone();
@@ -45323,7 +45342,7 @@ var init_google_genai = __esmMin((() => {
45323
45342
  return clone;
45324
45343
  }
45325
45344
  withMaxCompletionTokens(maxCompletionTokens) {
45326
- return this.withGenerationKwargs({ max_output_tokens: maxCompletionTokens });
45345
+ return this.withGenerationKwargs({ maxOutputTokens: maxCompletionTokens });
45327
45346
  }
45328
45347
  _clone() {
45329
45348
  const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
@@ -53449,6 +53468,7 @@ function convertMessage$2(message) {
53449
53468
  });
53450
53469
  if (message.toolCallId !== void 0) result.tool_call_id = message.toolCallId;
53451
53470
  if (reasoningContent) result.reasoning_content = reasoningContent;
53471
+ if (message.tools !== void 0 && message.tools.length > 0) result.tools = message.tools.map((tool) => convertTool$1(tool));
53452
53472
  return result;
53453
53473
  }
53454
53474
  function convertTool$1(tool) {
@@ -53828,6 +53848,7 @@ function convertHistoryMessages$1(history, reasoningKey, toolMessageConversion)
53828
53848
  const messages = [];
53829
53849
  const pendingToolResultMedia = [];
53830
53850
  for (const msg of history) {
53851
+ if (isToolDeclarationOnlyMessage(msg)) continue;
53831
53852
  if (msg.role !== "tool") appendToolResultMediaMessage(messages, pendingToolResultMedia);
53832
53853
  messages.push(convertMessage$1(msg, reasoningKey, toolMessageConversion));
53833
53854
  if (msg.role === "tool") pendingToolResultMedia.push(...toolResultImageParts(msg));
@@ -53837,6 +53858,7 @@ function convertHistoryMessages$1(history, reasoningKey, toolMessageConversion)
53837
53858
  }
53838
53859
  var KNOWN_REASONING_KEYS, DEFAULT_OUTBOUND_REASONING_KEY, CHAT_COMPLETIONS_MAX_OUTPUT_TOKENS_CEILING, OPENAI_CHAT_TOOL_CALL_ID_POLICY, OMITTED_AUDIO_PLACEHOLDER$1, OMITTED_VIDEO_PLACEHOLDER$1, OpenAILegacyStreamedMessage, OpenAILegacyChatProvider;
53839
53860
  var init_openai_legacy = __esmMin((() => {
53861
+ init_message$3();
53840
53862
  init_openai();
53841
53863
  init_openai_common();
53842
53864
  init_chat_completions_stream();
@@ -54435,6 +54457,7 @@ function convertHistoryMessages(history, modelName, toolMessageConversion) {
54435
54457
  pendingToolResultMedia.length = 0;
54436
54458
  };
54437
54459
  for (const msg of history) {
54460
+ if (isToolDeclarationOnlyMessage(msg)) continue;
54438
54461
  if (msg.role !== "tool") flushPendingMedia();
54439
54462
  input.push(...convertMessage(msg, modelName, toolMessageConversion));
54440
54463
  if (msg.role === "tool" && toolMessageConversion === "extract_text") pendingToolResultMedia.push(...messageContentToFunctionOutputItems(msg.content.filter(isMediaPart)));
@@ -54895,7 +54918,8 @@ function catalogModelToCapability(model) {
54895
54918
  audio_in: inputs.includes("audio"),
54896
54919
  thinking: Boolean(model.reasoning),
54897
54920
  tool_use: model.tool_call ?? true,
54898
- max_context_tokens: context
54921
+ max_context_tokens: context,
54922
+ select_tools: model.select_tools === true
54899
54923
  }
54900
54924
  };
54901
54925
  }
@@ -54956,8 +54980,9 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
54956
54980
  let pendingPart = null;
54957
54981
  const toolCallIndexMap = /* @__PURE__ */ new Map();
54958
54982
  if (options?.signal?.aborted) throwAbortError();
54983
+ const wireTools = tools.some((tool) => tool.deferred === true) ? tools.filter((tool) => tool.deferred !== true) : tools;
54959
54984
  options?.onRequestStart?.();
54960
- const stream = await provider.generate(systemPrompt, tools, history, options);
54985
+ const stream = await provider.generate(systemPrompt, wireTools, history, options);
54961
54986
  await throwIfAborted$1(options?.signal, stream);
54962
54987
  let serverDecodeMs = 0;
54963
54988
  let clientConsumeMs = 0;
@@ -61843,7 +61868,8 @@ var init_schema = __esmMin((() => {
61843
61868
  ModelAliasSchema = ModelAliasBaseSchema.extend({ overrides: ModelAliasOverrideSchema.optional() });
61844
61869
  ThinkingConfigSchema = object({
61845
61870
  enabled: boolean$1().optional(),
61846
- effort: string().optional()
61871
+ effort: string().optional(),
61872
+ keep: string().optional()
61847
61873
  });
61848
61874
  PermissionModeSchema = _enum([
61849
61875
  "yolo",
@@ -68383,7 +68409,14 @@ var init_types$15 = __esmMin((() => {}));
68383
68409
  //#region ../../packages/agent-core/src/flags/registry.ts
68384
68410
  var FLAG_DEFINITIONS;
68385
68411
  var init_registry$1 = __esmMin((() => {
68386
- FLAG_DEFINITIONS = [];
68412
+ FLAG_DEFINITIONS = [{
68413
+ id: "tool-select",
68414
+ title: "Tool select (progressive tool disclosure)",
68415
+ 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.",
68416
+ env: "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT",
68417
+ default: false,
68418
+ surface: "core"
68419
+ }];
68387
68420
  })), FlagResolver;
68388
68421
  var init_resolver = __esmMin((() => {
68389
68422
  init_resolve$1();
@@ -76785,6 +76818,102 @@ var init_background = __esmMin((() => {
76785
76818
  };
76786
76819
  }));
76787
76820
  //#endregion
76821
+ //#region ../../packages/agent-core/src/agent/context/dynamic-tools.ts
76822
+ /** True for a message that loads tool definitions (`message.tools` present). */
76823
+ function isDynamicToolSchemaMessage(message) {
76824
+ return message.tools !== void 0 && message.tools.length > 0;
76825
+ }
76826
+ /** True for a `<tools_added>/<tools_removed>` announcement reminder. */
76827
+ function isLoadableToolsAnnouncement(message) {
76828
+ return message.origin?.kind === "system_trigger" && message.origin.name === "loadable-tools";
76829
+ }
76830
+ /**
76831
+ * Shape a history for a consumer that must not see dynamic-tool protocol
76832
+ * context: drop the loadable-tools announcements and strip `message.tools`
76833
+ * (dropping the message entirely when nothing else remains). Two callers:
76834
+ * - projection for a model without the `select_tools` capability (mid-session
76835
+ * model switch — the canonical history keeps its shape, only the outgoing
76836
+ * view changes; announcements would be noise and even reference a
76837
+ * select_tools tool the model does not have);
76838
+ * - the compaction summarizer input (schemas and announcements are protocol
76839
+ * context, not conversation — summarizing them wastes tokens and risks
76840
+ * leaking schema text into the summary).
76841
+ * Returns the input array unchanged when there is nothing to strip, so the
76842
+ * common no-dynamic-tools path costs one scan and no allocation.
76843
+ */
76844
+ function stripDynamicToolContext(history) {
76845
+ if (!history.some((m) => isDynamicToolSchemaMessage(m) || isLoadableToolsAnnouncement(m))) return history;
76846
+ const out = [];
76847
+ for (const message of history) {
76848
+ if (isLoadableToolsAnnouncement(message)) continue;
76849
+ if (isDynamicToolSchemaMessage(message)) {
76850
+ const { tools: _tools, ...rest } = message;
76851
+ if (rest.content.length === 0 && rest.toolCalls.length === 0) continue;
76852
+ out.push(rest);
76853
+ continue;
76854
+ }
76855
+ out.push(message);
76856
+ }
76857
+ return out;
76858
+ }
76859
+ /** Union of tool names loaded by dynamic tool schema messages in `history`. */
76860
+ function collectLoadedDynamicToolNames(history) {
76861
+ const names = /* @__PURE__ */ new Set();
76862
+ for (const message of history) {
76863
+ if (message.tools === void 0) continue;
76864
+ for (const tool of message.tools) names.add(tool.name);
76865
+ }
76866
+ return names;
76867
+ }
76868
+ /**
76869
+ * Fold every loadable-tools announcement in `history`, in order, into the
76870
+ * currently-announced name set (`tools_removed` deletes, then `tools_added`
76871
+ * adds — last wins). The announcements are the context's own record of what
76872
+ * the model has been told is loadable; there is deliberately no separate
76873
+ * persisted ledger, so undo/compaction/resume all self-heal by re-folding.
76874
+ */
76875
+ function foldAnnouncedToolNames(history) {
76876
+ const announced = /* @__PURE__ */ new Set();
76877
+ for (const message of history) {
76878
+ if (!isLoadableToolsAnnouncement(message)) continue;
76879
+ const text = message.content.map((part) => part.type === "text" ? part.text : "").join("");
76880
+ for (const name of matchToolNameBlocks(text, TOOLS_REMOVED_BLOCK)) announced.delete(name);
76881
+ for (const name of matchToolNameBlocks(text, TOOLS_ADDED_BLOCK)) announced.add(name);
76882
+ }
76883
+ return announced;
76884
+ }
76885
+ function matchToolNameBlocks(text, pattern) {
76886
+ const names = [];
76887
+ pattern.lastIndex = 0;
76888
+ for (const match of text.matchAll(pattern)) {
76889
+ const body = match[1] ?? "";
76890
+ for (const line of body.split("\n")) {
76891
+ const name = line.trim();
76892
+ if (name.length > 0) names.push(name);
76893
+ }
76894
+ }
76895
+ return names;
76896
+ }
76897
+ /**
76898
+ * Render one diff announcement. Only the blocks with content are emitted; the
76899
+ * guidance sentence never contains a literal block tag, so `foldAnnouncedToolNames`
76900
+ * can anchor on the tags without tripping over prose.
76901
+ */
76902
+ function renderLoadableToolsAnnouncement(added, removed) {
76903
+ const sections = [];
76904
+ if (added.length > 0) sections.push(`<tools_added>\n${added.join("\n")}\n</tools_added>`);
76905
+ if (removed.length > 0) sections.push(`<tools_removed>\n${removed.join("\n")}\n</tools_removed>`);
76906
+ sections.push("Use the select_tools tool with exact names to load full tool definitions before calling them. Names listed as removed are no longer loadable — do not select them. Fold all announcements in this conversation in order to get the current list.");
76907
+ return sections.join("\n\n");
76908
+ }
76909
+ var DYNAMIC_TOOL_SCHEMA_VARIANT, LOADABLE_TOOLS_TRIGGER, TOOLS_ADDED_BLOCK, TOOLS_REMOVED_BLOCK;
76910
+ var init_dynamic_tools = __esmMin((() => {
76911
+ DYNAMIC_TOOL_SCHEMA_VARIANT = "dynamic_tool_schema";
76912
+ LOADABLE_TOOLS_TRIGGER = "loadable-tools";
76913
+ TOOLS_ADDED_BLOCK = /<tools_added>\n?([\s\S]*?)\n?<\/tools_added>/g;
76914
+ TOOLS_REMOVED_BLOCK = /<tools_removed>\n?([\s\S]*?)\n?<\/tools_removed>/g;
76915
+ }));
76916
+ //#endregion
76788
76917
  //#region ../../packages/agent-core/src/utils/abort.ts
76789
76918
  function abortError$1(message = "Aborted") {
76790
76919
  const error = new Error(message);
@@ -77089,6 +77218,7 @@ function estimateTokensForMessage(message) {
77089
77218
  total += estimateTokens$1(call.name);
77090
77219
  total += estimateTokens$1(JSON.stringify(call.arguments));
77091
77220
  }
77221
+ if (message.tools !== void 0) total += estimateTokensForTools(message.tools);
77092
77222
  messageTokenEstimateCache.set(message, total);
77093
77223
  return total;
77094
77224
  }
@@ -77492,6 +77622,7 @@ var DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS, OVERFLOW_CONTEXT_SAFETY_RATIO, OVE
77492
77622
  var init_full = __esmMin((() => {
77493
77623
  init_errors$8();
77494
77624
  init_src$4();
77625
+ init_dynamic_tools();
77495
77626
  init_errors$5();
77496
77627
  init_retry();
77497
77628
  init_todo_list$1();
@@ -77600,7 +77731,7 @@ var init_full = __esmMin((() => {
77600
77731
  return this.agent.context.tokenCountWithPending;
77601
77732
  }
77602
77733
  estimateRequestTokens(messages) {
77603
- return estimateTokens$1(this.agent.config.systemPrompt) + estimateTokensForTools(this.agent.tools.loopTools) + estimateTokensForMessages(messages);
77734
+ return estimateTokens$1(this.agent.config.systemPrompt) + estimateTokensForTools(this.agent.tools.loopTools.filter((t) => t.deferred !== true)) + estimateTokensForMessages(messages);
77604
77735
  }
77605
77736
  resetForTurn() {
77606
77737
  this.compactionCountInTurn = 0;
@@ -77665,6 +77796,7 @@ var init_full = __esmMin((() => {
77665
77796
  this.agent.log.error("failed to refresh system prompt after compaction", { error });
77666
77797
  }
77667
77798
  await this.agent.injection.injectAfterCompaction();
77799
+ this.lastCompactedTokenCount = this.tokenCountWithPending;
77668
77800
  this.markCompleted();
77669
77801
  const { contextSummary: _contextSummary, ...eventResult } = result;
77670
77802
  this.agent.emitEvent({
@@ -77689,6 +77821,57 @@ var init_full = __esmMin((() => {
77689
77821
  buildInstruction(customInstruction) {
77690
77822
  return renderPrompt(compaction_instruction_default, { customInstruction: customInstruction?.trim() ?? "" }).trimEnd();
77691
77823
  }
77824
+ /**
77825
+ * Keep-all rebuild (Phase 1): after compaction folded the history — and the
77826
+ * dynamic tool schema messages with it — append ONE merged schema message so
77827
+ * the model keeps calling its loaded tools without re-selecting. Schemas are
77828
+ * read from the live registry, never copied from the old history, so a
77829
+ * schema that changed since load self-heals here. Names whose server is
77830
+ * currently disconnected have no registry schema and are not rebuilt (the
77831
+ * model re-selects after reconnect); names that survived into the
77832
+ * post-compaction history (none under today's users+summary rebuild, but
77833
+ * guarded) are not duplicated. The message goes through the normal
77834
+ * injection-origin append, so estimation and records pick it up as usual.
77835
+ *
77836
+ * Budget guard: the rebuilt floor (users + summary + schemas) is the one
77837
+ * part of the post-compaction context that compaction itself can never
77838
+ * shrink — if it lands inside the auto-compaction trigger band, every
77839
+ * following step re-compacts and rebuilds in a loop. Admit schemas (in name
77840
+ * order) only while the projected context stays within HALF the compaction
77841
+ * trigger, so normal turn content still fits before the next compaction;
77842
+ * anything dropped is simply re-selectable on demand, the same degradation
77843
+ * as a disconnected server.
77844
+ */
77845
+ rebuildDynamicToolSchemas(activeBefore) {
77846
+ if (!this.agent.toolSelectEnabled) return;
77847
+ if (activeBefore.size === 0) return;
77848
+ const surviving = collectLoadedDynamicToolNames(this.agent.context.history);
77849
+ const candidates = [...activeBefore].filter((name) => !surviving.has(name)).toSorted((a, b) => a.localeCompare(b)).map((name) => this.agent.tools.getMcpToolSchema(name)).filter((tool) => tool !== void 0);
77850
+ if (candidates.length === 0) return;
77851
+ const tools = [];
77852
+ let projected = this.tokenCountWithPending;
77853
+ for (const tool of candidates) {
77854
+ const toolTokens = estimateTokensForTools([tool]);
77855
+ if (this.strategy.shouldCompact((projected + toolTokens) * 2)) break;
77856
+ tools.push(tool);
77857
+ projected += toolTokens;
77858
+ }
77859
+ if (tools.length < candidates.length) this.agent.log.info("trimmed dynamic tool schema rebuild to stay clear of the compaction trigger", {
77860
+ kept: tools.length,
77861
+ dropped: candidates.slice(tools.length).map((tool) => tool.name)
77862
+ });
77863
+ if (tools.length === 0) return;
77864
+ this.agent.context.appendMessage({
77865
+ role: "system",
77866
+ content: [],
77867
+ toolCalls: [],
77868
+ tools,
77869
+ origin: {
77870
+ kind: "injection",
77871
+ variant: DYNAMIC_TOOL_SCHEMA_VARIANT
77872
+ }
77873
+ });
77874
+ }
77692
77875
  postProcessSummary(summary) {
77693
77876
  const todos = this.agent.tools.storeData()["todo"] ?? [];
77694
77877
  if (todos.length === 0) return summary;
@@ -77699,6 +77882,7 @@ var init_full = __esmMin((() => {
77699
77882
  const startedAt = Date.now();
77700
77883
  const originalHistory = [...this.agent.context.history];
77701
77884
  const tokensBefore = estimateTokensForMessages(originalHistory);
77885
+ const activeDynamicToolsBefore = new Set(this.agent.tools.loadedDynamicToolNames());
77702
77886
  let retryCount = 0;
77703
77887
  try {
77704
77888
  await this.triggerPreCompactHook(data, tokensBefore, signal);
@@ -77718,7 +77902,7 @@ var init_full = __esmMin((() => {
77718
77902
  const delays = retryBackoffDelays(5);
77719
77903
  let usage = null;
77720
77904
  let summary;
77721
- let historyForModel = originalHistory;
77905
+ let historyForModel = stripDynamicToolContext(originalHistory);
77722
77906
  let droppedCount = 0;
77723
77907
  let overflowShrinkCount = 0;
77724
77908
  let emptyOrTruncatedShrinkCount = 0;
@@ -77780,6 +77964,7 @@ var init_full = __esmMin((() => {
77780
77964
  tokensBefore,
77781
77965
  droppedCount: droppedCount === 0 ? void 0 : droppedCount
77782
77966
  });
77967
+ this.rebuildDynamicToolSchemas(activeDynamicToolsBefore);
77783
77968
  this.agent.telemetry.track("compaction_finished", {
77784
77969
  source: data.source,
77785
77970
  tokens_before: result.tokensBefore,
@@ -77795,7 +77980,7 @@ var init_full = __esmMin((() => {
77795
77980
  output_tokens: usage.output
77796
77981
  }
77797
77982
  });
77798
- this.lastCompactedTokenCount = result.tokensAfter;
77983
+ this.lastCompactedTokenCount = this.tokenCountWithPending;
77799
77984
  return result;
77800
77985
  } catch (error) {
77801
77986
  if (isAbortError$3(error)) return void 0;
@@ -79222,23 +79407,64 @@ function applyKimiEnvThinkingEffort(provider, thinkingEffort, env = process.env)
79222
79407
  return provider.withExtraBody({ thinking: { effort } });
79223
79408
  }
79224
79409
  /**
79225
- * Apply the Moonshot preserved-thinking passthrough (`KIMI_MODEL_THINKING_KEEP`
79226
- * -> `thinking.keep`) to a chat provider. Applied in `ConfigState.provider` after
79227
- * `withThinking`, and only while thinking is on — otherwise the API would
79228
- * receive a `thinking.keep` with no accompanying `thinking.type` it honors.
79229
- * (Compaction uses a raw provider with thinking off, so it correctly skips this.)
79410
+ * Parse a single keep source (env var or config field). A blank value is
79411
+ * "unspecified" and falls through to the next source; an off-value explicitly
79412
+ * disables Preserved Thinking (short-circuits, no fallback); anything else is
79413
+ * forwarded verbatim (e.g. "all").
79414
+ */
79415
+ function parseKeepValue(raw) {
79416
+ const trimmed = raw?.trim();
79417
+ if (trimmed === void 0 || trimmed.length === 0) return { specified: false };
79418
+ if (KEEP_OFF_VALUES.has(trimmed.toLowerCase())) return {
79419
+ specified: true,
79420
+ value: void 0
79421
+ };
79422
+ return {
79423
+ specified: true,
79424
+ value: trimmed
79425
+ };
79426
+ }
79427
+ /**
79428
+ * Resolve the Moonshot Preserved Thinking passthrough (`thinking.keep`) with
79429
+ * precedence env (`KIMI_MODEL_THINKING_KEEP`) > config (`thinking.keep`) >
79430
+ * default `"all"`. Only meaningful while thinking is on — otherwise the API
79431
+ * would receive a `thinking.keep` with no accompanying `thinking.type` it
79432
+ * honors. (Compaction uses a raw provider with thinking off, so it correctly
79433
+ * resolves to `undefined`.)
79230
79434
  *
79231
- * Non-Kimi providers — and an unset/blank value — are returned unchanged.
79435
+ * Returns `undefined` when Preserved Thinking should be disabled.
79436
+ */
79437
+ function resolveThinkingKeep(env, configKeep, thinkingEffort) {
79438
+ if (thinkingEffort === "off") return void 0;
79439
+ const fromEnv = parseKeepValue(env["KIMI_MODEL_THINKING_KEEP"]);
79440
+ if (fromEnv.specified) return fromEnv.value;
79441
+ const fromConfig = parseKeepValue(configKeep);
79442
+ if (fromConfig.specified) return fromConfig.value;
79443
+ return "all";
79444
+ }
79445
+ /**
79446
+ * Apply the Moonshot Preserved Thinking passthrough to a chat provider. See
79447
+ * `resolveThinkingKeep` for precedence. Non-Kimi providers are returned
79448
+ * unchanged.
79232
79449
  */
79233
- function applyKimiEnvThinkingKeep(provider, thinkingEffort, env = process.env) {
79450
+ function applyKimiEnvThinkingKeep(provider, thinkingEffort, env = process.env, configKeep) {
79234
79451
  if (!(provider instanceof KimiChatProvider)) return provider;
79235
- const keep = env["KIMI_MODEL_THINKING_KEEP"]?.trim();
79236
- if (keep === void 0 || keep.length === 0 || thinkingEffort === "off") return provider;
79452
+ const keep = resolveThinkingKeep(env, configKeep, thinkingEffort);
79453
+ if (keep === void 0) return provider;
79237
79454
  return provider.withExtraBody({ thinking: { keep } });
79238
79455
  }
79456
+ var KEEP_OFF_VALUES;
79239
79457
  var init_kimi_env_params = __esmMin((() => {
79240
79458
  init_src$4();
79241
79459
  init_resolve$1();
79460
+ KEEP_OFF_VALUES = new Set([
79461
+ "0",
79462
+ "false",
79463
+ "no",
79464
+ "off",
79465
+ "none",
79466
+ "null"
79467
+ ]);
79242
79468
  }));
79243
79469
  //#endregion
79244
79470
  //#region ../../packages/agent-core/src/agent/config/thinking.ts
@@ -79364,7 +79590,7 @@ var init_config$3 = __esmMin((() => {
79364
79590
  return provider;
79365
79591
  }
79366
79592
  get provider() {
79367
- return applyKimiEnvThinkingKeep(applyKimiEnvThinkingEffort(applyKimiEnvSamplingParams(createProvider(this.providerConfig).withThinking(this.thinkingEffort)), this.thinkingEffort), this.thinkingEffort);
79593
+ return applyKimiEnvThinkingKeep(applyKimiEnvThinkingEffort(applyKimiEnvSamplingParams(createProvider(this.providerConfig).withThinking(this.thinkingEffort)), this.thinkingEffort), this.thinkingEffort, void 0, this.agent.kimiConfig?.thinking?.keep);
79368
79594
  }
79369
79595
  get model() {
79370
79596
  if (this._modelAlias === void 0) throw new KimiError(ErrorCodes.MODEL_NOT_CONFIGURED, "Model not set");
@@ -122000,6 +122226,7 @@ function prepareMessageForProjection(message, onAnomaly) {
122000
122226
  content
122001
122227
  };
122002
122228
  if (next.role === "tool" && next.content.length === 0) throw new KimiError(ErrorCodes.REQUEST_INVALID, "Tool result message content cannot be empty after removing empty text blocks.", { details: { toolCallId: next.toolCallId } });
122229
+ if (next.tools !== void 0 && next.tools.length > 0) return next;
122003
122230
  return next.content.length === 0 && next.toolCalls.length === 0 ? null : next;
122004
122231
  }
122005
122232
  function canMergeUserMessage(message) {
@@ -122029,7 +122256,8 @@ function stripContextMetadata(message) {
122029
122256
  content: message.content.map((p) => ({ ...p })),
122030
122257
  toolCalls: message.toolCalls.map((tc) => ({ ...tc })),
122031
122258
  toolCallId: message.toolCallId,
122032
- partial: message.partial
122259
+ partial: message.partial,
122260
+ tools: message.tools?.map((tool) => ({ ...tool }))
122033
122261
  };
122034
122262
  }
122035
122263
  function trimTrailingOpenToolExchange(history) {
@@ -122120,8 +122348,10 @@ var init_context$2 = __esmMin((() => {
122120
122348
  init_xml_escape();
122121
122349
  init_compaction();
122122
122350
  init_projector();
122351
+ init_dynamic_tools();
122123
122352
  init_types$8();
122124
122353
  init_types$8();
122354
+ init_dynamic_tools();
122125
122355
  TOOL_ERROR_STATUS$2 = "<system>ERROR: Tool execution failed.</system>";
122126
122356
  TOOL_EMPTY_STATUS$2 = "<system>Tool output is empty.</system>";
122127
122357
  TOOL_EMPTY_ERROR_STATUS$2 = "<system>ERROR: Tool execution failed. Tool output is empty.</system>";
@@ -122259,6 +122489,7 @@ var init_context$2 = __esmMin((() => {
122259
122489
  this._lastAssistantAt = null;
122260
122490
  this.agent.microCompaction.reset();
122261
122491
  this.agent.injection.onContextClear();
122492
+ this.agent.tools.onContextCleared();
122262
122493
  this.agent.emitStatusUpdated();
122263
122494
  }
122264
122495
  undo(count) {
@@ -122375,6 +122606,7 @@ var init_context$2 = __esmMin((() => {
122375
122606
  this.tokenCountCoveredMessageCount = this._history.length;
122376
122607
  this.agent.microCompaction.reset();
122377
122608
  this.agent.injection.onContextCompacted();
122609
+ this.agent.tools.onContextCompacted();
122378
122610
  this.agent.emitStatusUpdated();
122379
122611
  return result;
122380
122612
  }
@@ -122395,8 +122627,9 @@ var init_context$2 = __esmMin((() => {
122395
122627
  return this._history;
122396
122628
  }
122397
122629
  project(messages, options) {
122630
+ const shaped = this.agent.toolSelectEnabled ? messages : stripDynamicToolContext(messages);
122398
122631
  const anomalies = [];
122399
- const result = project(this.agent.microCompaction.compact(messages), {
122632
+ const result = project(this.agent.microCompaction.compact(shaped), {
122400
122633
  ...options,
122401
122634
  onAnomaly: (anomaly) => {
122402
122635
  anomalies.push(anomaly);
@@ -122555,7 +122788,8 @@ var init_context$2 = __esmMin((() => {
122555
122788
  type: "function",
122556
122789
  id: event.toolCallId,
122557
122790
  name: event.name,
122558
- arguments: event.args === void 0 ? null : JSON.stringify(event.args)
122791
+ arguments: event.args === void 0 ? null : JSON.stringify(event.args),
122792
+ extras: event.extras
122559
122793
  });
122560
122794
  this.pendingToolResultIds.add(event.toolCallId);
122561
122795
  return;
@@ -124231,6 +124465,37 @@ var init_todo_list = __esmMin((() => {
124231
124465
  };
124232
124466
  }));
124233
124467
  //#endregion
124468
+ //#region ../../packages/agent-core/src/agent/injection/tools-diff.ts
124469
+ var ToolsDiffInjector;
124470
+ var init_tools_diff = __esmMin((() => {
124471
+ init_dynamic_tools();
124472
+ ToolsDiffInjector = class {
124473
+ agent;
124474
+ constructor(agent) {
124475
+ this.agent = agent;
124476
+ }
124477
+ /**
124478
+ * Recompute the loadable set, fold the announced set from history, and
124479
+ * append one diff announcement iff they differ. Most turns append nothing,
124480
+ * keeping the prompt cache warm; the first announcement after session start
124481
+ * (or after compaction folded the history) is naturally the full list.
124482
+ */
124483
+ inject() {
124484
+ if (!this.agent.toolSelectEnabled) return;
124485
+ const loadable = this.agent.tools.loadableDynamicToolNames();
124486
+ const loadableSet = new Set(loadable);
124487
+ const announced = foldAnnouncedToolNames(this.agent.context.history);
124488
+ const added = loadable.filter((name) => !announced.has(name));
124489
+ const removed = [...announced].filter((name) => !loadableSet.has(name)).toSorted((a, b) => a.localeCompare(b));
124490
+ if (added.length === 0 && removed.length === 0) return;
124491
+ this.agent.context.appendSystemReminder(renderLoadableToolsAnnouncement(added, removed), {
124492
+ kind: "system_trigger",
124493
+ name: LOADABLE_TOOLS_TRIGGER
124494
+ });
124495
+ }
124496
+ };
124497
+ }));
124498
+ //#endregion
124234
124499
  //#region ../../packages/agent-core/src/agent/injection/manager.ts
124235
124500
  var ACTIVE_BACKGROUND_TASK_GUIDANCE, InjectionManager;
124236
124501
  var init_manager$1 = __esmMin((() => {
@@ -124240,11 +124505,13 @@ var init_manager$1 = __esmMin((() => {
124240
124505
  init_plugin_session_start();
124241
124506
  init_plan_mode();
124242
124507
  init_todo_list();
124508
+ init_tools_diff();
124243
124509
  ACTIVE_BACKGROUND_TASK_GUIDANCE = "The conversation was compacted, so the earlier messages that started these background tasks are gone — but the tasks are still running from before. Do not start duplicates. Use TaskOutput to fetch a task’s result, TaskList to list them, and TaskStop to cancel one.";
124244
124510
  InjectionManager = class {
124245
124511
  agent;
124246
124512
  injectors;
124247
124513
  goalInjector;
124514
+ toolsDiffInjector;
124248
124515
  constructor(agent) {
124249
124516
  this.agent = agent;
124250
124517
  this.injectors = [
@@ -124254,6 +124521,7 @@ var init_manager$1 = __esmMin((() => {
124254
124521
  new PermissionModeInjector(agent)
124255
124522
  ];
124256
124523
  this.goalInjector = agent.type === "main" ? new GoalInjector(agent) : null;
124524
+ this.toolsDiffInjector = new ToolsDiffInjector(agent);
124257
124525
  }
124258
124526
  async inject() {
124259
124527
  for (const injector of this.injectors) await injector.inject();
@@ -124266,8 +124534,17 @@ var init_manager$1 = __esmMin((() => {
124266
124534
  async injectGoal() {
124267
124535
  await this.activeGoalInjector()?.inject();
124268
124536
  }
124537
+ /**
124538
+ * Appends a loadable-tools diff announcement when the loadable set changed.
124539
+ * Boundary cadence (turn start + post-compaction); no-op when the disclosure
124540
+ * gate is closed or nothing changed.
124541
+ */
124542
+ injectToolsDiff() {
124543
+ this.toolsDiffInjector.inject();
124544
+ }
124269
124545
  async injectAfterCompaction() {
124270
124546
  await this.injectGoal();
124547
+ this.injectToolsDiff();
124271
124548
  this.injectActiveBackgroundTasks();
124272
124549
  await this.inject();
124273
124550
  }
@@ -124396,7 +124673,8 @@ var init_default_tool_approve = __esmMin((() => {
124396
124673
  "Skill",
124397
124674
  "GetGoal",
124398
124675
  "SetGoalBudget",
124399
- "UpdateGoal"
124676
+ "UpdateGoal",
124677
+ "select_tools"
124400
124678
  ]);
124401
124679
  DefaultToolApprovePermissionPolicy = class {
124402
124680
  name = "default-tool-approve";
@@ -134788,7 +135066,7 @@ function preflightToolCall(step, toolCall) {
134788
135066
  toolCall,
134789
135067
  toolName,
134790
135068
  args: parsedArgs.data,
134791
- output: `Tool "${toolName}" not found`
135069
+ output: step.describeMissingTool?.(toolName) ?? `Tool "${toolName}" not found`
134792
135070
  };
134793
135071
  if (parsedArgs.parseFailed) step.log?.debug("tool args JSON parse failed", {
134794
135072
  toolName,
@@ -135157,7 +135435,8 @@ async function dispatchToolCall(step, call, args, displayFields) {
135157
135435
  name: toolName,
135158
135436
  args,
135159
135437
  description: displayFields?.description,
135160
- display: displayFields?.display
135438
+ display: displayFields?.display,
135439
+ extras: toolCall.extras
135161
135440
  });
135162
135441
  }
135163
135442
  var GRACE_TIMEOUT_MS, TOOL_OUTPUT_EMPTY, TOOL_OUTPUT_NON_TEXT, validators;
@@ -135185,7 +135464,7 @@ var init_tool_call = __esmMin((() => {
135185
135464
  * does not lose model usage that was already spent.
135186
135465
  */
135187
135466
  async function executeLoopStep(deps) {
135188
- const { turnId, signal, buildMessages, buildMessagesStrict, dispatchEvent, llm, tools, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
135467
+ const { turnId, signal, buildMessages, buildMessagesStrict, dispatchEvent, llm, tools, buildTools, describeMissingTool, hooks, log, currentStep, maxRetryAttempts, recordUsage } = deps;
135189
135468
  if (hooks?.beforeStep !== void 0) {
135190
135469
  const beforeStep = await hooks.beforeStep({
135191
135470
  turnId,
@@ -135196,11 +135475,13 @@ async function executeLoopStep(deps) {
135196
135475
  if (beforeStep?.block === true) throw new Error(beforeStep.reason ?? `Step ${String(currentStep)} was blocked`);
135197
135476
  }
135198
135477
  signal.throwIfAborted();
135478
+ const stepTools = buildTools !== void 0 ? buildTools() : tools;
135199
135479
  const messages = await buildMessages();
135200
135480
  signal.throwIfAborted();
135201
135481
  const stepUuid = randomUUID();
135202
135482
  const step = {
135203
- tools,
135483
+ tools: stepTools,
135484
+ describeMissingTool,
135204
135485
  hooks,
135205
135486
  log,
135206
135487
  dispatchEvent,
@@ -135218,7 +135499,7 @@ async function executeLoopStep(deps) {
135218
135499
  });
135219
135500
  const chatParams = {
135220
135501
  messages,
135221
- tools: tools ?? [],
135502
+ tools: stepTools ?? [],
135222
135503
  signal,
135223
135504
  ...createChatStreamingCallbacks({
135224
135505
  dispatchEvent,
@@ -135408,7 +135689,7 @@ var init_turn_step = __esmMin((() => {
135408
135689
  //#endregion
135409
135690
  //#region ../../packages/agent-core/src/loop/run-turn.ts
135410
135691
  async function runTurn(input) {
135411
- const { turnId, signal, llm, buildMessages, buildMessagesStrict, dispatchEvent, tools, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
135692
+ const { turnId, signal, llm, buildMessages, buildMessagesStrict, dispatchEvent, tools, buildTools, describeMissingTool, hooks, log, maxSteps, maxRetryAttempts, recordStepUsage: hostRecordStepUsage } = input;
135412
135693
  let usage = emptyUsage();
135413
135694
  let steps = 0;
135414
135695
  let stopReason = "end_turn";
@@ -135431,6 +135712,8 @@ async function runTurn(input) {
135431
135712
  dispatchEvent,
135432
135713
  llm,
135433
135714
  tools,
135715
+ buildTools,
135716
+ describeMissingTool,
135434
135717
  hooks,
135435
135718
  log,
135436
135719
  currentStep: steps,
@@ -135452,7 +135735,8 @@ async function runTurn(input) {
135452
135735
  }
135453
135736
  } catch (error) {
135454
135737
  if (isAbortError$3(error) || signal.aborted) {
135455
- dispatchEvent(makeInterruptedEvent("aborted", steps, activeStep));
135738
+ const interruptReason = isUserCancellation(signal.reason) || isUserCancellation(error) ? "user_cancelled" : "aborted";
135739
+ dispatchEvent(makeInterruptedEvent("aborted", steps, activeStep, void 0, interruptReason));
135456
135740
  return {
135457
135741
  stopReason: "aborted",
135458
135742
  steps,
@@ -135468,17 +135752,19 @@ async function runTurn(input) {
135468
135752
  usage
135469
135753
  };
135470
135754
  }
135471
- function makeInterruptedEvent(reason, attemptedSteps, activeStep, message) {
135755
+ function makeInterruptedEvent(reason, attemptedSteps, activeStep, message, interruptReason = reason) {
135472
135756
  return {
135473
135757
  type: "turn.interrupted",
135474
135758
  reason,
135475
135759
  attemptedSteps,
135476
135760
  ...activeStep !== void 0 ? { activeStep } : {},
135477
- ...message !== void 0 ? { message } : {}
135761
+ ...message !== void 0 ? { message } : {},
135762
+ interruptReason
135478
135763
  };
135479
135764
  }
135480
135765
  var init_run_turn = __esmMin((() => {
135481
135766
  init_src$4();
135767
+ init_abort();
135482
135768
  init_errors$5();
135483
135769
  init_turn_step();
135484
135770
  }));
@@ -143669,10 +143955,30 @@ var init_agent_swarm = __esmMin((() => {
143669
143955
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md?raw
143670
143956
  var ask_user_default;
143671
143957
  var init_ask_user$1 = __esmMin((() => {
143672
- 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- 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 whose keys identify each answered question; 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";
143958
+ 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";
143673
143959
  }));
143674
143960
  //#endregion
143675
143961
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.ts
143962
+ /**
143963
+ * Answers are keyed by question text with option labels as values, so both
143964
+ * must be unambiguous: question texts unique across the call, option labels
143965
+ * unique within their question. Runtime tool-arg validation is AJV against
143966
+ * the JSON Schema (where zod refinements are unrepresentable), so the
143967
+ * execution path re-runs this check itself.
143968
+ */
143969
+ function questionUniquenessError(questions) {
143970
+ const texts = /* @__PURE__ */ new Set();
143971
+ for (const q of questions) {
143972
+ if (texts.has(q.question)) return `Invalid questions: duplicate question text ${JSON.stringify(q.question)}. ${QUESTION_UNIQUENESS_MESSAGE} Rephrase the duplicates and call the tool again.`;
143973
+ texts.add(q.question);
143974
+ const labels = /* @__PURE__ */ new Set();
143975
+ for (const option of q.options) {
143976
+ if (labels.has(option.label)) return `Invalid questions: duplicate option label ${JSON.stringify(option.label)} in question ${JSON.stringify(q.question)}. ${QUESTION_UNIQUENESS_MESSAGE} Rephrase the duplicates and call the tool again.`;
143977
+ labels.add(option.label);
143978
+ }
143979
+ }
143980
+ return null;
143981
+ }
143676
143982
  function dismissedQuestionResult() {
143677
143983
  return {
143678
143984
  isError: false,
@@ -143707,7 +144013,7 @@ function isQuestionResponse(result) {
143707
144013
  const answers = result.answers;
143708
144014
  return typeof answers === "object" && answers !== null && !Array.isArray(answers);
143709
144015
  }
143710
- var QuestionOptionSchema, QuestionItemSchema, AskUserQuestionInputBaseSchema, AskUserQuestionInputSchemaWithBackground, QUESTION_DISMISSED_MESSAGE, QUESTION_UNSUPPORTED_FAILURE_MESSAGE, AskUserQuestionTool;
144016
+ var QuestionOptionSchema, QuestionItemSchema, QUESTION_UNIQUENESS_MESSAGE, AskUserQuestionInputBaseSchema, AskUserQuestionInputSchemaWithBackground, QUESTION_DISMISSED_MESSAGE, QUESTION_UNSUPPORTED_FAILURE_MESSAGE, AskUserQuestionTool;
143711
144017
  var init_ask_user = __esmMin((() => {
143712
144018
  init_zod$1();
143713
144019
  init_background();
@@ -143716,17 +144022,19 @@ var init_ask_user = __esmMin((() => {
143716
144022
  init_input_schema();
143717
144023
  init_ask_user$1();
143718
144024
  QuestionOptionSchema = object({
143719
- label: string().describe("Concise display text (1-5 words). If recommended, append '(Recommended)'."),
144025
+ label: string().min(1).describe("Concise display text (1-5 words). If recommended, append '(Recommended)'."),
143720
144026
  description: string().default("").describe("Brief explanation of trade-offs or implications.")
143721
144027
  });
143722
144028
  QuestionItemSchema = object({
143723
- question: string().describe("A specific, actionable question. End with '?'."),
144029
+ question: string().min(1).describe("A specific, actionable question. End with '?'."),
143724
144030
  header: string().default("").describe("Short category tag (max 12 chars, e.g. 'Auth', 'Style')."),
143725
144031
  options: array(QuestionOptionSchema).min(2).max(4).describe("2-4 meaningful, distinct options. Do NOT include an 'Other' option — the system adds one automatically."),
143726
144032
  multi_select: boolean$1().default(false).describe("Whether the user can select multiple options.")
143727
144033
  });
144034
+ QUESTION_UNIQUENESS_MESSAGE = "Question texts must be unique across questions, and option labels must be unique within each question.";
143728
144035
  AskUserQuestionInputBaseSchema = object({ questions: array(QuestionItemSchema).min(1).max(4).describe("The questions to ask the user (1-4 questions).") });
143729
- AskUserQuestionInputSchemaWithBackground = AskUserQuestionInputBaseSchema.extend({ background: boolean$1().default(false).describe("Set true to ask in the background and return immediately with a background task_id; you are notified automatically when the user answers — do not poll with TaskOutput while the question is pending.") });
144036
+ AskUserQuestionInputSchemaWithBackground = AskUserQuestionInputBaseSchema.extend({ background: boolean$1().default(false).describe("Set true to ask in the background and return immediately with a background task_id; you are notified automatically when the user answers — do not poll with TaskOutput while the question is pending.") }).refine((data) => questionUniquenessError(data.questions) === null, { message: QUESTION_UNIQUENESS_MESSAGE });
144037
+ AskUserQuestionInputBaseSchema.refine((data) => questionUniquenessError(data.questions) === null, { message: QUESTION_UNIQUENESS_MESSAGE });
143730
144038
  QUESTION_DISMISSED_MESSAGE = "User dismissed the question without answering.";
143731
144039
  QUESTION_UNSUPPORTED_FAILURE_MESSAGE = "The connected client does not support interactive questions. Do NOT call this tool again. Ask the user directly in your text response instead.";
143732
144040
  AskUserQuestionTool = class {
@@ -143747,6 +144055,11 @@ var init_ask_user = __esmMin((() => {
143747
144055
  };
143748
144056
  }
143749
144057
  async execution(args, { toolCallId, signal, turnId }) {
144058
+ const uniquenessError = questionUniquenessError(args.questions);
144059
+ if (uniquenessError !== null) return {
144060
+ isError: true,
144061
+ output: uniquenessError
144062
+ };
143750
144063
  if (args.background === true) return this.executeInBackground(args, {
143751
144064
  toolCallId,
143752
144065
  turnId,
@@ -150799,6 +151112,12 @@ function toolOutputText(output) {
150799
151112
  function interruptedStep(event) {
150800
151113
  return event.activeStep ?? event.attemptedSteps;
150801
151114
  }
151115
+ function telemetryInterruptReason(reason, userCancelled) {
151116
+ if ((reason === "aborted" || reason === "cancelled") && userCancelled) return "user_cancelled";
151117
+ if (reason === "aborted" || reason === "cancelled") return "aborted";
151118
+ if (reason === "failed") return "error";
151119
+ return reason;
151120
+ }
150802
151121
  function classifyApiError(error, summary) {
150803
151122
  const statusCode = apiStatusCode(error) ?? summaryStatusCode(summary);
150804
151123
  if (statusCode !== void 0) {
@@ -151301,7 +151620,10 @@ var init_turn = __esmMin((() => {
151301
151620
  if (standalone && this.currentId === turnId && this.agent.goal.getGoal().goal?.status !== "active") this.activeTurn = null;
151302
151621
  if (this.agent.swarmMode.shouldAutoExit) this.agent.swarmMode.exit();
151303
151622
  if (errorEvent !== void 0) this.agent.emitEvent(errorEvent);
151304
- if (ended.reason !== "completed") this.trackTurnInterrupted(turnId, this.currentStepByTurn.get(turnId) ?? this.currentStep);
151623
+ if (ended.reason !== "completed") {
151624
+ const interruptReason = telemetryInterruptReason(ended.reason, isUserCancellation(signal.reason));
151625
+ this.trackTurnInterrupted(turnId, this.currentStepByTurn.get(turnId) ?? this.currentStep, interruptReason);
151626
+ }
151305
151627
  this.telemetryModeByTurn.delete(turnId);
151306
151628
  this.currentStepByTurn.delete(turnId);
151307
151629
  this.interruptedTelemetryTurnIds.delete(turnId);
@@ -151375,6 +151697,7 @@ var init_turn = __esmMin((() => {
151375
151697
  const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
151376
151698
  await this.agent.mcp?.waitForInitialLoad(signal);
151377
151699
  await this.agent.injection.injectGoal();
151700
+ this.agent.injection.injectToolsDiff();
151378
151701
  while (true) {
151379
151702
  signal.throwIfAborted();
151380
151703
  const model = this.agent.config.model;
@@ -151388,7 +151711,8 @@ var init_turn = __esmMin((() => {
151388
151711
  buildMessages: () => this.agent.context.messages,
151389
151712
  buildMessagesStrict: () => this.agent.context.strictMessages,
151390
151713
  dispatchEvent: this.buildDispatchEvent(turnId),
151391
- tools: this.agent.tools.loopTools,
151714
+ buildTools: () => this.agent.tools.loopTools,
151715
+ describeMissingTool: (name) => this.agent.tools.missingToolMessage(name),
151392
151716
  log: this.agent.log,
151393
151717
  maxSteps: loopControl?.maxStepsPerTurn,
151394
151718
  maxRetryAttempts: loopControl?.maxRetriesPerStep,
@@ -151560,7 +151884,7 @@ var init_turn = __esmMin((() => {
151560
151884
  }
151561
151885
  if (event.type === "turn.interrupted") {
151562
151886
  if (event.reason === "error" && event.activeStep !== void 0) this.stepFailureByTurn.set(turnId, event);
151563
- this.trackTurnInterrupted(turnId, interruptedStep(event));
151887
+ this.trackTurnInterrupted(turnId, interruptedStep(event), event.interruptReason ?? telemetryInterruptReason(event.reason, false));
151564
151888
  return;
151565
151889
  }
151566
151890
  this.trackToolLifecycle(event, turnId);
@@ -151621,12 +151945,13 @@ var init_turn = __esmMin((() => {
151621
151945
  for (const [seenStep, keys] of this.stepToolCallKeys) if (seenStep !== step && keys.has(key)) return true;
151622
151946
  return false;
151623
151947
  }
151624
- trackTurnInterrupted(turnId, atStep) {
151948
+ trackTurnInterrupted(turnId, atStep, interruptReason) {
151625
151949
  if (this.interruptedTelemetryTurnIds.has(turnId)) return;
151626
151950
  this.interruptedTelemetryTurnIds.add(turnId);
151627
151951
  this.agent.telemetry.track("turn_interrupted", {
151628
151952
  mode: this.telemetryModeByTurn.get(turnId) ?? this.telemetryMode(),
151629
151953
  at_step: atStep,
151954
+ interrupt_reason: interruptReason,
151630
151955
  ...this.requestProtocolProps()
151631
151956
  });
151632
151957
  }
@@ -151974,6 +152299,71 @@ var init_exit_plan_mode = __esmMin((() => {
151974
152299
  };
151975
152300
  }));
151976
152301
  //#endregion
152302
+ //#region ../../packages/agent-core/src/tools/builtin/select-tools.ts
152303
+ var SELECT_TOOLS_TOOL_NAME, SelectToolsInputSchema, DESCRIPTION, SelectToolsTool;
152304
+ var init_select_tools = __esmMin((() => {
152305
+ init_zod$1();
152306
+ init_dynamic_tools();
152307
+ init_input_schema();
152308
+ SELECT_TOOLS_TOOL_NAME = "select_tools";
152309
+ SelectToolsInputSchema = object({ names: array(string()).min(1).describe("Exact tool names to load, taken from the latest announced tool list.") }).strict();
152310
+ DESCRIPTION = "Load one or more tools by name so you can call them. All available tool names are listed in the <tools_added>/<tools_removed> announcements in the system context — fold them in order to get the current list. Pass the exact name(s) you need; their full definitions become available immediately, so you can call them directly in your next tool call.";
152311
+ SelectToolsTool = class {
152312
+ agent;
152313
+ name = SELECT_TOOLS_TOOL_NAME;
152314
+ description = DESCRIPTION;
152315
+ parameters = toInputJsonSchema(SelectToolsInputSchema);
152316
+ constructor(agent) {
152317
+ this.agent = agent;
152318
+ }
152319
+ resolveExecution(args) {
152320
+ return {
152321
+ description: `Loading ${args.names.join(", ")}`,
152322
+ approvalRule: this.name,
152323
+ execute: async () => {
152324
+ if (!this.agent.toolSelectEnabled) return {
152325
+ output: "select_tools is not available for the current model.",
152326
+ isError: true
152327
+ };
152328
+ const manager = this.agent.tools;
152329
+ const loadable = new Set(manager.loadableDynamicToolNames());
152330
+ const loaded = manager.loadedDynamicToolNames();
152331
+ const toLoad = [];
152332
+ const alreadyAvailable = [];
152333
+ const unknown = [];
152334
+ for (const name of new Set(args.names)) if (loaded.has(name)) alreadyAvailable.push(name);
152335
+ else if (loadable.has(name)) toLoad.push(name);
152336
+ else unknown.push(name);
152337
+ if (toLoad.length > 0) {
152338
+ toLoad.sort((a, b) => a.localeCompare(b));
152339
+ const tools = toLoad.map((name) => manager.getMcpToolSchema(name)).filter((tool) => tool !== void 0);
152340
+ this.agent.context.appendMessage({
152341
+ role: "system",
152342
+ content: [],
152343
+ toolCalls: [],
152344
+ tools,
152345
+ origin: {
152346
+ kind: "injection",
152347
+ variant: DYNAMIC_TOOL_SCHEMA_VARIANT
152348
+ }
152349
+ });
152350
+ manager.markDynamicToolsLoaded(toLoad);
152351
+ }
152352
+ const lines = [];
152353
+ if (toLoad.length > 0) lines.push(`Loaded: ${toLoad.join(", ")}`);
152354
+ if (alreadyAvailable.length > 0) lines.push(`Already available: ${alreadyAvailable.join(", ")}`);
152355
+ for (const name of unknown) lines.push(`Unknown tool: ${name}. Pick from the latest announced tools list.`);
152356
+ const isError = toLoad.length === 0 && alreadyAvailable.length === 0;
152357
+ return isError ? {
152358
+ output: lines.join("\n"),
152359
+ isError
152360
+ } : { output: lines.join("\n") };
152361
+ }
152362
+ };
152363
+ }
152364
+ };
152365
+ }));
152366
+ //#endregion
151977
152367
  //#region ../../packages/agent-core/src/tools/builtin/shell/bash.md?raw
151978
152368
  var bash_default;
151979
152369
  var init_bash$1 = __esmMin((() => {
@@ -152464,6 +152854,7 @@ var init_builtin = __esmMin((() => {
152464
152854
  init_update_goal();
152465
152855
  init_enter_plan_mode();
152466
152856
  init_exit_plan_mode();
152857
+ init_select_tools();
152467
152858
  init_bash();
152468
152859
  init_todo_list$1();
152469
152860
  init_fetch_url();
@@ -152478,6 +152869,7 @@ var import_picomatch, SHELL_FOREGROUND_TIMEOUT_S, ToolManager;
152478
152869
  var init_tool$1 = __esmMin((() => {
152479
152870
  init_dist$4();
152480
152871
  import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
152872
+ init_dynamic_tools();
152481
152873
  init_errors$8();
152482
152874
  init_auth_tool();
152483
152875
  init_output();
@@ -152498,6 +152890,14 @@ var init_tool$1 = __esmMin((() => {
152498
152890
  enabledTools = /* @__PURE__ */ new Set();
152499
152891
  /** Glob patterns (e.g. `mcp__*`, `mcp__github__*`) gating which MCP tools the profile exposes. */
152500
152892
  mcpAccessPatterns = [];
152893
+ /**
152894
+ * Defer-window lead for the loaded-tools ledger: names marked loaded whose
152895
+ * schema message may still sit in the context's deferred queue (an open tool
152896
+ * exchange). The history itself is the source of truth —
152897
+ * `loadedDynamicToolNames()` unions this set with a history scan — so
152898
+ * undo/compaction/resume never need to roll this back.
152899
+ */
152900
+ pendingLoadedDynamicTools = /* @__PURE__ */ new Set();
152501
152901
  store = {};
152502
152902
  mcpToolStatusUnsubscribe;
152503
152903
  /** Abort controllers for in-flight `!` shell commands, keyed by commandId so
@@ -152818,11 +153218,93 @@ var init_tool$1 = __esmMin((() => {
152818
153218
  isMcpToolEnabled(name) {
152819
153219
  return this.mcpAccessPatterns.some((pattern) => import_picomatch.default.isMatch(name, pattern));
152820
153220
  }
153221
+ /**
153222
+ * Whether MCP tools are disclosed progressively: kept out of the top-level
153223
+ * `tools[]` and loaded on demand via select_tools. Reads the agent's single
153224
+ * three-gate decision point.
153225
+ */
153226
+ get progressiveDisclosure() {
153227
+ return this.agent.toolSelectEnabled;
153228
+ }
153229
+ /**
153230
+ * Names the model may select right now: registered MCP tools that pass the
153231
+ * profile's `mcp__*` access patterns, sorted for byte-stable announcements.
153232
+ * In disclosure mode the patterns keep their permission-filter role but stop
153233
+ * feeding the top-level `tools[]`.
153234
+ */
153235
+ loadableDynamicToolNames() {
153236
+ return [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name)).toSorted((a, b) => a.localeCompare(b));
153237
+ }
153238
+ /**
153239
+ * The loaded-tools ledger: every name whose full definition has been
153240
+ * delivered to the conversation via a `tools`-carrying message, plus the
153241
+ * defer-window pending set. History is the single source of truth, so the
153242
+ * ledger survives resume (records replay rebuilds the history), keeps its
153243
+ * state across undo (schema messages have `injection` origin and are not
153244
+ * undone), and self-heals after compaction (the rebuild message re-carries
153245
+ * the schemas).
153246
+ */
153247
+ loadedDynamicToolNames() {
153248
+ const names = collectLoadedDynamicToolNames(this.agent.context.history);
153249
+ for (const name of this.pendingLoadedDynamicTools) names.add(name);
153250
+ return names;
153251
+ }
153252
+ /** Mark names loaded ahead of their schema message landing in history. */
153253
+ markDynamicToolsLoaded(names) {
153254
+ for (const name of names) this.pendingLoadedDynamicTools.add(name);
153255
+ }
153256
+ /**
153257
+ * Context was cleared (`/clear`): every schema message is gone, so the
153258
+ * defer-window lead must not keep reporting its names as loaded — a stale
153259
+ * entry would make select_tools answer "Already available" for a tool whose
153260
+ * definition the model can no longer see.
153261
+ */
153262
+ onContextCleared() {
153263
+ this.pendingLoadedDynamicTools.clear();
153264
+ }
153265
+ /**
153266
+ * Compaction rebuilt the history: from here on the keep-all rebuild message
153267
+ * (which may have trimmed or skipped schemas — budget guard, disconnected
153268
+ * servers) is the sole truth about what is still loaded. A pending entry
153269
+ * surviving past this boundary would report a schema the context no longer
153270
+ * carries as loaded, and re-selecting it would wrongly answer
153271
+ * "Already available" instead of injecting.
153272
+ */
153273
+ onContextCompacted() {
153274
+ this.pendingLoadedDynamicTools.clear();
153275
+ }
153276
+ /**
153277
+ * Plain schema snapshot of a registered MCP tool, read from the live
153278
+ * registry (never from history) at injection time.
153279
+ */
153280
+ getMcpToolSchema(name) {
153281
+ const entry = this.mcpTools.get(name);
153282
+ if (entry === void 0) return void 0;
153283
+ return {
153284
+ name: entry.tool.name,
153285
+ description: entry.tool.description,
153286
+ parameters: entry.tool.parameters
153287
+ };
153288
+ }
153289
+ /**
153290
+ * Disclosure-mode wording for a tool-call preflight miss. A loaded tool
153291
+ * whose server dropped is a different situation from a never-announced name;
153292
+ * telling them apart stops the model from re-selecting a disconnected tool
153293
+ * in a loop or treating a transient disconnect as a permanent removal.
153294
+ */
153295
+ missingToolMessage(name) {
153296
+ if (!this.progressiveDisclosure) return void 0;
153297
+ if (!isMcpToolName(name)) return void 0;
153298
+ const registered = this.mcpTools.has(name) && this.isMcpToolEnabled(name);
153299
+ const loaded = this.loadedDynamicToolNames().has(name);
153300
+ if (registered && !loaded) return `Tool "${name}" is available but not loaded. Call select_tools with ["${name}"] first, then call the tool.`;
153301
+ if (!registered && loaded) return `Tool "${name}" was loaded but its MCP server is currently disconnected. It may become available again when the server reconnects; do not retry immediately.`;
153302
+ }
152821
153303
  *toolInfos() {
152822
153304
  for (const tool of this.builtinTools.values()) yield {
152823
153305
  name: tool.name,
152824
153306
  description: tool.description,
152825
- active: this.enabledTools.has(tool.name),
153307
+ active: this.enabledTools.has(tool.name) || tool.name === "select_tools" && this.agent.toolSelectEnabled,
152826
153308
  source: "builtin"
152827
153309
  };
152828
153310
  for (const tool of this.userTools.values()) yield {
@@ -152863,6 +153345,7 @@ var init_tool$1 = __esmMin((() => {
152863
153345
  (modelCapabilities.image_in || modelCapabilities.video_in) && new ReadMediaFileTool(kaos, workspace, modelCapabilities, videoUploader),
152864
153346
  new EnterPlanModeTool(this.agent),
152865
153347
  new ExitPlanModeTool(this.agent),
153348
+ new SelectToolsTool(this.agent),
152866
153349
  goalToolsEnabled && new CreateGoalTool(this.agent),
152867
153350
  goalToolsEnabled && new GetGoalTool(this.agent),
152868
153351
  goalToolsEnabled && new SetGoalBudgetTool(this.agent),
@@ -152941,9 +153424,24 @@ var init_tool$1 = __esmMin((() => {
152941
153424
  }
152942
153425
  get loopTools() {
152943
153426
  if (this.loopToolsOverride !== void 0) return this.loopToolsOverride;
152944
- const mcpNames = [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name));
153427
+ const disclosure = this.progressiveDisclosure;
153428
+ const enabledMcpNames = [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name));
153429
+ const loadedSet = disclosure ? this.loadedDynamicToolNames() : void 0;
153430
+ const mcpNames = loadedSet === void 0 ? enabledMcpNames : enabledMcpNames.filter((name) => loadedSet.has(name));
153431
+ const selectToolsName = disclosure ? [SELECT_TOOLS_TOOL_NAME] : [];
152945
153432
  const hideGoalMutationTools = this.agent.goal.getGoal().goal === null;
152946
- return uniq([...this.enabledTools, ...mcpNames]).toSorted((a, b) => a.localeCompare(b)).filter((name) => !(hideGoalMutationTools && (name === "SetGoalBudget" || name === "UpdateGoal"))).map((name) => this.userTools.get(name) ?? this.mcpTools.get(name)?.tool ?? this.builtinTools.get(name)).filter((tool) => !!tool);
153433
+ return uniq([
153434
+ ...this.enabledTools,
153435
+ ...selectToolsName,
153436
+ ...mcpNames
153437
+ ]).toSorted((a, b) => a.localeCompare(b)).filter((name) => !(hideGoalMutationTools && (name === "SetGoalBudget" || name === "UpdateGoal"))).filter((name) => disclosure || name !== "select_tools").map((name) => {
153438
+ const tool = this.userTools.get(name) ?? this.mcpTools.get(name)?.tool ?? this.builtinTools.get(name);
153439
+ if (tool === void 0) return void 0;
153440
+ return disclosure && this.mcpTools.has(name) ? {
153441
+ ...tool,
153442
+ deferred: true
153443
+ } : tool;
153444
+ }).filter((tool) => !!tool);
152947
153445
  }
152948
153446
  };
152949
153447
  }));
@@ -153245,18 +153743,19 @@ var init_llm_request_logger = __esmMin((() => {
153245
153743
  logRequest(input) {
153246
153744
  const { provider, modelAlias, systemPrompt, tools, messages, fields } = input;
153247
153745
  const requestLogFields = fields ?? {};
153746
+ const wireTools = tools.filter((tool) => tool.deferred !== true);
153248
153747
  const config = {
153249
153748
  provider: provider.name,
153250
153749
  model: provider.modelName,
153251
153750
  modelAlias,
153252
153751
  thinkingEffort: provider.thinkingEffort ?? void 0,
153253
153752
  systemPromptChars: systemPrompt.length,
153254
- toolCount: tools.length
153753
+ toolCount: wireTools.length
153255
153754
  };
153256
153755
  const signature = JSON.stringify({
153257
153756
  ...config,
153258
153757
  systemPromptHash: fingerprint(systemPrompt),
153259
- toolsHash: fingerprint(JSON.stringify(toolSignature(tools)))
153758
+ toolsHash: fingerprint(JSON.stringify(toolSignature(wireTools)))
153260
153759
  });
153261
153760
  if (signature !== this.lastConfigLogSignature) {
153262
153761
  this.lastConfigLogSignature = signature;
@@ -153420,6 +153919,20 @@ var init_agent = __esmMin((() => {
153420
153919
  this.additionalDirs = normalizeAdditionalDirs(additionalDirs);
153421
153920
  if (this.config.hasProvider) this.tools.initializeBuiltinTools();
153422
153921
  }
153922
+ /**
153923
+ * Single decision point for select_tools progressive disclosure. All three
153924
+ * gates must be open: the model declares the `select_tools` capability, the
153925
+ * model declares `tool_use` (a model without tool use registering
153926
+ * select_tools is a contradiction), and the `tool-select` experimental flag
153927
+ * is on. Every consumer — top-level tools[] convergence, select_tools
153928
+ * registration, manifest announcements, projection shaping — reads this
153929
+ * instead of re-deriving the conditions, so degradation is lossless: any
153930
+ * closed gate reproduces the inline behavior byte-for-byte.
153931
+ */
153932
+ get toolSelectEnabled() {
153933
+ const capability = this.config.modelCapabilities;
153934
+ return capability.select_tools === true && capability.tool_use && this.experimentalFlags.enabled("tool-select");
153935
+ }
153423
153936
  get generate() {
153424
153937
  return async (provider, systemPrompt, tools, history, callbacks, options) => {
153425
153938
  const { requestLogFields, generateOptions } = splitGenerateOptions(options);
@@ -183817,6 +184330,597 @@ var init_mcp$1 = __esmMin((() => {
183817
184330
  init_types$2();
183818
184331
  }));
183819
184332
  //#endregion
184333
+ //#region ../../packages/agent-core/src/session/store/session-index.ts
184334
+ function sessionIndexPath(homeDir) {
184335
+ return join$4(homeDir, "session_index.jsonl");
184336
+ }
184337
+ async function appendSessionIndexEntry(homeDir, entry) {
184338
+ const indexPath = sessionIndexPath(homeDir);
184339
+ const line = `${JSON.stringify(entry)}\n`;
184340
+ const next = (appendQueues.get(homeDir) ?? Promise.resolve()).then(async () => {
184341
+ await mkdir(dirname$3(indexPath), {
184342
+ recursive: true,
184343
+ mode: 448
184344
+ });
184345
+ await appendFile(indexPath, line, "utf-8");
184346
+ });
184347
+ appendQueues.set(homeDir, next.then(() => void 0, () => void 0));
184348
+ return next;
184349
+ }
184350
+ async function readSessionIndex$1(homeDir, sessionsDir) {
184351
+ let raw;
184352
+ try {
184353
+ raw = await readFile(sessionIndexPath(homeDir), "utf-8");
184354
+ } catch {
184355
+ return /* @__PURE__ */ new Map();
184356
+ }
184357
+ const result = /* @__PURE__ */ new Map();
184358
+ for (const line of raw.split(/\r?\n/)) {
184359
+ const trimmed = line.trim();
184360
+ if (trimmed === "") continue;
184361
+ const entry = parseIndexLine$1(trimmed);
184362
+ if (entry === void 0) continue;
184363
+ const sessionDir = resolve$2(entry.sessionDir);
184364
+ if (!isAbsolute$2(entry.sessionDir)) continue;
184365
+ if (!isPathInside$1(sessionsDir, sessionDir)) continue;
184366
+ if (basename$2(sessionDir) !== entry.sessionId) continue;
184367
+ result.set(entry.sessionId, {
184368
+ sessionId: entry.sessionId,
184369
+ sessionDir,
184370
+ workDir: entry.workDir
184371
+ });
184372
+ }
184373
+ return result;
184374
+ }
184375
+ function parseIndexLine$1(line) {
184376
+ try {
184377
+ const parsed = JSON.parse(line);
184378
+ if (typeof parsed !== "object" || parsed === null) return void 0;
184379
+ const entry = parsed;
184380
+ if (typeof entry.sessionId !== "string" || typeof entry.sessionDir !== "string" || typeof entry.workDir !== "string") return;
184381
+ return {
184382
+ sessionId: entry.sessionId,
184383
+ sessionDir: entry.sessionDir,
184384
+ workDir: entry.workDir
184385
+ };
184386
+ } catch {
184387
+ return;
184388
+ }
184389
+ }
184390
+ function isPathInside$1(parent, child) {
184391
+ const rel = relative$1(resolve$2(parent), resolve$2(child));
184392
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute$2(rel);
184393
+ }
184394
+ var appendQueues;
184395
+ var init_session_index = __esmMin((() => {
184396
+ init_dist$7();
184397
+ appendQueues = /* @__PURE__ */ new Map();
184398
+ }));
184399
+ //#endregion
184400
+ //#region ../../packages/agent-core/src/utils/workdir-slug.ts
184401
+ function slugifyWorkDirName(name) {
184402
+ const slug = name.toLowerCase().replaceAll(/[^a-z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, MAX_WORKDIR_SLUG_LENGTH).replaceAll(/^-+|-+$/g, "");
184403
+ return slug === "" || slug === "." || slug === ".." ? "workspace" : slug;
184404
+ }
184405
+ var MAX_WORKDIR_SLUG_LENGTH;
184406
+ var init_workdir_slug = __esmMin((() => {
184407
+ MAX_WORKDIR_SLUG_LENGTH = 40;
184408
+ }));
184409
+ //#endregion
184410
+ //#region ../../packages/agent-core/src/session/store/workdir-key.ts
184411
+ function normalizeWorkDir(workDir) {
184412
+ return resolve$2(workDir);
184413
+ }
184414
+ function encodeWorkDirKey(workDir) {
184415
+ const normalized = normalizeWorkDir(workDir);
184416
+ return `${WORKDIR_KEY_PREFIX}${slugifyWorkDirName(basename$2(normalized))}_${createHash("sha256").update(normalized).digest("hex").slice(0, HASH_LENGTH)}`;
184417
+ }
184418
+ var WORKDIR_KEY_PREFIX, HASH_LENGTH;
184419
+ var init_workdir_key = __esmMin((() => {
184420
+ init_dist$7();
184421
+ init_workdir_slug();
184422
+ WORKDIR_KEY_PREFIX = "wd_";
184423
+ HASH_LENGTH = 12;
184424
+ }));
184425
+ //#endregion
184426
+ //#region ../../packages/agent-core/src/session/store/session-store.ts
184427
+ function metadataFromState(state) {
184428
+ if (state === void 0 || state.custom === void 0) return void 0;
184429
+ return state.custom;
184430
+ }
184431
+ function forkCustomMetadata(source, metadata) {
184432
+ return {
184433
+ ...customMetadataWithoutGoal(source),
184434
+ ...customMetadataWithoutGoal(metadata)
184435
+ };
184436
+ }
184437
+ async function dropForkedSessionFiles(sessionDir) {
184438
+ await Promise.all(FORKED_SESSION_DROPPED_FILES.map((fileName) => rm(join$4(sessionDir, fileName), { force: true })));
184439
+ }
184440
+ async function appendForkedMarkers(state) {
184441
+ const record = {
184442
+ type: "forked",
184443
+ time: Date.now()
184444
+ };
184445
+ const agents = state["agents"];
184446
+ if (!isRecord$11(agents)) return;
184447
+ const paths = /* @__PURE__ */ new Set();
184448
+ for (const agentMeta of Object.values(agents)) {
184449
+ if (!isRecord$11(agentMeta)) continue;
184450
+ const homedir = agentMeta["homedir"];
184451
+ if (typeof homedir !== "string") continue;
184452
+ paths.add(join$4(homedir, "wire.jsonl"));
184453
+ }
184454
+ await Promise.all([...paths].map(async (path) => {
184455
+ const persistence = new FileSystemAgentRecordPersistence(path);
184456
+ persistence.append(record);
184457
+ await persistence.flush();
184458
+ }));
184459
+ }
184460
+ function customMetadataWithoutGoal(value) {
184461
+ if (!isRecord$11(value)) return {};
184462
+ const custom = {};
184463
+ for (const [key, entry] of Object.entries(value)) {
184464
+ if (key === "goal") continue;
184465
+ custom[key] = entry;
184466
+ }
184467
+ return custom;
184468
+ }
184469
+ async function latestAgentWireMtime(sessionDir) {
184470
+ const agentsDir = join$4(sessionDir, "agents");
184471
+ let entries;
184472
+ try {
184473
+ entries = await readdir$1(agentsDir, { withFileTypes: true });
184474
+ } catch {
184475
+ return;
184476
+ }
184477
+ let latest = 0;
184478
+ for (const entry of entries) {
184479
+ if (!entry.isDirectory()) continue;
184480
+ const wireInfo = await statIfExists(join$4(agentsDir, entry.name, "wire.jsonl"));
184481
+ latest = Math.max(latest, wireInfo?.mtimeMs ?? 0);
184482
+ }
184483
+ return latest > 0 ? latest : void 0;
184484
+ }
184485
+ function titleFromState(state) {
184486
+ if (state === void 0) return void 0;
184487
+ if (typeof state.isCustomTitle === "boolean" && typeof state.title === "string") return state.title;
184488
+ if (typeof state.customTitle === "string") return state.customTitle;
184489
+ return typeof state.title === "string" ? state.title : void 0;
184490
+ }
184491
+ async function readOptionalState(sessionDir) {
184492
+ try {
184493
+ const parsed = JSON.parse(await readFile(join$4(sessionDir, "state.json"), "utf-8"));
184494
+ const result = SessionSummaryStateSchema.safeParse(parsed);
184495
+ return result.success ? result.data : void 0;
184496
+ } catch {
184497
+ return;
184498
+ }
184499
+ }
184500
+ function normalizeRequiredWorkDir(workDir) {
184501
+ if (workDir.trim() === "") throw new KimiError(ErrorCodes.REQUEST_WORK_DIR_REQUIRED, "listSessions requires workDir");
184502
+ return normalizeWorkDir(workDir);
184503
+ }
184504
+ function normalizeOptionalSessionId$1(sessionId) {
184505
+ return sessionId === void 0 ? void 0 : sessionId.trim();
184506
+ }
184507
+ function normalizeForkTitle(title, fallback) {
184508
+ if (title !== void 0) {
184509
+ const normalized = title.trim();
184510
+ if (normalized.length === 0) throw new KimiError(ErrorCodes.SESSION_TITLE_EMPTY, "Session title cannot be empty");
184511
+ return normalized;
184512
+ }
184513
+ return typeof fallback === "string" && fallback.trim().length > 0 ? fallback : "New Session";
184514
+ }
184515
+ function rewriteAgentHomedirs(value, sourceDir, targetDir) {
184516
+ if (!isRecord$11(value)) return {};
184517
+ const agents = {};
184518
+ for (const [agentId, agentMeta] of Object.entries(value)) {
184519
+ if (!isRecord$11(agentMeta)) {
184520
+ agents[agentId] = agentMeta;
184521
+ continue;
184522
+ }
184523
+ const homedir = agentMeta["homedir"];
184524
+ agents[agentId] = {
184525
+ ...agentMeta,
184526
+ homedir: typeof homedir === "string" ? remapSessionPath(homedir, sourceDir, targetDir) : homedir
184527
+ };
184528
+ }
184529
+ return agents;
184530
+ }
184531
+ function remapSessionPath(value, sourceDir, targetDir) {
184532
+ const rel = relative$1(sourceDir, value);
184533
+ if (rel === "") return targetDir;
184534
+ if (rel.startsWith("..") || isAbsolute$2(rel)) return value;
184535
+ return join$4(targetDir, rel);
184536
+ }
184537
+ function isRecord$11(value) {
184538
+ return typeof value === "object" && value !== null && !Array.isArray(value);
184539
+ }
184540
+ async function statIfExists(path) {
184541
+ try {
184542
+ return await stat$2(path);
184543
+ } catch {
184544
+ return;
184545
+ }
184546
+ }
184547
+ async function isDirectory(path) {
184548
+ try {
184549
+ return (await stat$2(path)).isDirectory();
184550
+ } catch {
184551
+ return false;
184552
+ }
184553
+ }
184554
+ function timestampOrFallback(value, fallback) {
184555
+ return Number.isFinite(value) && value > 0 ? value : fallback;
184556
+ }
184557
+ function assertSafeSessionId(id) {
184558
+ if (isSafeSessionId(id)) return;
184559
+ throw new KimiError(ErrorCodes.SESSION_ID_INVALID, "Session id contains unsupported path characters");
184560
+ }
184561
+ function isSafeSessionId(id) {
184562
+ return /^[A-Za-z0-9._-]+$/.test(id) && id !== "." && id !== "..";
184563
+ }
184564
+ function compareSessionSummary(a, b) {
184565
+ if (a.updatedAt !== b.updatedAt) return b.updatedAt - a.updatedAt;
184566
+ if (a.createdAt !== b.createdAt) return b.createdAt - a.createdAt;
184567
+ if (a.id < b.id) return -1;
184568
+ if (a.id > b.id) return 1;
184569
+ return 0;
184570
+ }
184571
+ var SessionSummaryStateSchema, FORKED_SESSION_DROPPED_FILES, SessionStore;
184572
+ var init_session_store$1 = __esmMin((() => {
184573
+ init_dist$7();
184574
+ init_zod$1();
184575
+ init_errors$8();
184576
+ init_session_index();
184577
+ init_workdir_key();
184578
+ init_records();
184579
+ SessionSummaryStateSchema = object({
184580
+ archived: boolean$1().optional(),
184581
+ customTitle: string().optional(),
184582
+ isCustomTitle: boolean$1().optional(),
184583
+ lastPrompt: string().optional(),
184584
+ title: string().optional(),
184585
+ workDir: string().optional(),
184586
+ custom: record(string(), unknown()).optional()
184587
+ });
184588
+ FORKED_SESSION_DROPPED_FILES = ["upcoming-goals.json"];
184589
+ SessionStore = class {
184590
+ homeDir;
184591
+ sessionsDir;
184592
+ constructor(homeDir, _options = {}) {
184593
+ this.homeDir = homeDir;
184594
+ this.sessionsDir = join$4(homeDir, "sessions");
184595
+ }
184596
+ sessionDirFor(input) {
184597
+ assertSafeSessionId(input.id);
184598
+ return join$4(this.sessionsDir, encodeWorkDirKey(normalizeWorkDir(input.workDir)), input.id);
184599
+ }
184600
+ async create(input) {
184601
+ assertSafeSessionId(input.id);
184602
+ const workDir = normalizeWorkDir(input.workDir);
184603
+ if (await this.findSessionEntry(input.id) !== void 0) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.id}" already exists`);
184604
+ const dir = this.sessionDirFor({
184605
+ id: input.id,
184606
+ workDir
184607
+ });
184608
+ if (await isDirectory(dir)) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.id}" already exists`);
184609
+ await mkdir(dir, {
184610
+ recursive: true,
184611
+ mode: 448
184612
+ });
184613
+ await appendSessionIndexEntry(this.homeDir, {
184614
+ sessionId: input.id,
184615
+ sessionDir: dir,
184616
+ workDir
184617
+ });
184618
+ return this.summaryFromDir(input.id, dir, workDir);
184619
+ }
184620
+ async fork(input) {
184621
+ const source = await this.findExistingSessionEntry(input.sourceId);
184622
+ assertSafeSessionId(input.targetId);
184623
+ if (await this.findSessionEntry(input.targetId) !== void 0) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.targetId}" already exists`);
184624
+ const targetDir = this.sessionDirFor({
184625
+ id: input.targetId,
184626
+ workDir: source.workDir
184627
+ });
184628
+ if (await isDirectory(targetDir)) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.targetId}" already exists`);
184629
+ await mkdir(dirname$3(targetDir), {
184630
+ recursive: true,
184631
+ mode: 448
184632
+ });
184633
+ try {
184634
+ await cp(source.sessionDir, targetDir, {
184635
+ recursive: true,
184636
+ force: false,
184637
+ errorOnExist: true
184638
+ });
184639
+ await dropForkedSessionFiles(targetDir);
184640
+ await appendForkedMarkers(await this.writeForkedState(input, source.sessionDir, source.workDir, targetDir));
184641
+ const summary = await this.summaryFromDir(input.targetId, targetDir, source.workDir);
184642
+ await appendSessionIndexEntry(this.homeDir, {
184643
+ sessionId: input.targetId,
184644
+ sessionDir: targetDir,
184645
+ workDir: source.workDir
184646
+ });
184647
+ return summary;
184648
+ } catch (error) {
184649
+ await rm(targetDir, {
184650
+ recursive: true,
184651
+ force: true
184652
+ }).catch(() => {});
184653
+ throw error;
184654
+ }
184655
+ }
184656
+ async get(id) {
184657
+ const entry = await this.findExistingSessionEntry(id);
184658
+ return this.summaryFromDir(id, entry.sessionDir, entry.workDir);
184659
+ }
184660
+ async rename(id, title) {
184661
+ const normalized = title.trim();
184662
+ if (normalized.length === 0) throw new KimiError(ErrorCodes.SESSION_TITLE_EMPTY, "Session title cannot be empty");
184663
+ const statePath = join$4((await this.findExistingSessionEntry(id)).sessionDir, "state.json");
184664
+ let parsed;
184665
+ try {
184666
+ parsed = JSON.parse(await readFile(statePath, "utf-8"));
184667
+ } catch (error) {
184668
+ throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${id}" state.json was not found`, { cause: error });
184669
+ }
184670
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${id}" state.json is invalid`);
184671
+ const next = {
184672
+ ...parsed,
184673
+ title: normalized,
184674
+ isCustomTitle: true
184675
+ };
184676
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
184677
+ }
184678
+ async archive(id) {
184679
+ const entry = await this.findExistingSessionEntry(id);
184680
+ const statePath = join$4(entry.sessionDir, "state.json");
184681
+ let parsed;
184682
+ try {
184683
+ parsed = JSON.parse(await readFile(statePath, "utf-8"));
184684
+ } catch (error) {
184685
+ throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${id}" state.json was not found`, { cause: error });
184686
+ }
184687
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${id}" state.json is invalid`);
184688
+ const now = (/* @__PURE__ */ new Date()).toISOString();
184689
+ const next = {
184690
+ ...parsed,
184691
+ archived: true,
184692
+ updatedAt: now
184693
+ };
184694
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
184695
+ return this.summaryFromDir(id, entry.sessionDir, entry.workDir);
184696
+ }
184697
+ async list(options = {}) {
184698
+ const workDir = options.workDir === void 0 ? void 0 : normalizeRequiredWorkDir(options.workDir);
184699
+ const sessionId = normalizeOptionalSessionId$1(options.sessionId);
184700
+ const includeArchive = options.includeArchive === true;
184701
+ if (workDir !== void 0) {
184702
+ if (sessionId !== void 0) {
184703
+ const local = await this.summaryFromWorkDirSession(sessionId, workDir, includeArchive);
184704
+ if (local !== void 0) return [local];
184705
+ return this.listSessionId(sessionId, includeArchive);
184706
+ }
184707
+ return this.listWorkDir(workDir, includeArchive);
184708
+ }
184709
+ if (sessionId !== void 0) return this.listSessionId(sessionId, includeArchive);
184710
+ return this.listAll(includeArchive);
184711
+ }
184712
+ /**
184713
+ * Rebuild the global session index from the session directories on disk.
184714
+ *
184715
+ * The bucket directory name is a one-way hash of the workDir, so the workDir
184716
+ * can only be recovered from each session's self-describing `state.json`
184717
+ * (`workDir`, falling back to `custom.cwd` for older sessions). Sessions that
184718
+ * record no workDir, or whose recorded workDir does not match the bucket they
184719
+ * live in, are left untouched rather than writing a misleading entry.
184720
+ *
184721
+ * The index is append-only and `readSessionIndex` lets later lines override
184722
+ * earlier ones for the same id, so appending a corrected line both adds
184723
+ * missing entries and repairs stale ones. Best-effort: never throws.
184724
+ */
184725
+ async reindex() {
184726
+ const index = await readSessionIndex$1(this.homeDir, this.sessionsDir);
184727
+ let bucketEntries;
184728
+ try {
184729
+ bucketEntries = await readdir$1(this.sessionsDir, { withFileTypes: true });
184730
+ } catch {
184731
+ return {
184732
+ scanned: 0,
184733
+ added: 0,
184734
+ repaired: 0
184735
+ };
184736
+ }
184737
+ let scanned = 0;
184738
+ let added = 0;
184739
+ let repaired = 0;
184740
+ for (const bucket of bucketEntries) {
184741
+ if (!bucket.isDirectory()) continue;
184742
+ const bucketDir = join$4(this.sessionsDir, bucket.name);
184743
+ let sessionEntries;
184744
+ try {
184745
+ sessionEntries = await readdir$1(bucketDir, { withFileTypes: true });
184746
+ } catch {
184747
+ continue;
184748
+ }
184749
+ for (const entry of sessionEntries) {
184750
+ if (!entry.isDirectory()) continue;
184751
+ const id = entry.name;
184752
+ if (!isSafeSessionId(id)) continue;
184753
+ const sessionDir = join$4(bucketDir, id);
184754
+ const workDir = await this.recoverWorkDir(sessionDir);
184755
+ if (workDir === void 0) continue;
184756
+ scanned++;
184757
+ let expectedDir;
184758
+ try {
184759
+ expectedDir = this.sessionDirFor({
184760
+ id,
184761
+ workDir
184762
+ });
184763
+ } catch {
184764
+ continue;
184765
+ }
184766
+ if (resolve$2(sessionDir) !== resolve$2(expectedDir)) continue;
184767
+ const existing = index.get(id);
184768
+ if (existing !== void 0 && resolve$2(existing.sessionDir) === resolve$2(sessionDir) && existing.workDir === workDir) continue;
184769
+ await appendSessionIndexEntry(this.homeDir, {
184770
+ sessionId: id,
184771
+ sessionDir,
184772
+ workDir
184773
+ });
184774
+ index.set(id, {
184775
+ sessionId: id,
184776
+ sessionDir,
184777
+ workDir
184778
+ });
184779
+ if (existing === void 0) added++;
184780
+ else repaired++;
184781
+ }
184782
+ }
184783
+ return {
184784
+ scanned,
184785
+ added,
184786
+ repaired
184787
+ };
184788
+ }
184789
+ async recoverWorkDir(sessionDir) {
184790
+ const state = await readOptionalState(sessionDir);
184791
+ if (state?.workDir !== void 0) try {
184792
+ return normalizeWorkDir(state.workDir);
184793
+ } catch {
184794
+ return;
184795
+ }
184796
+ const legacyCwd = state?.custom?.["cwd"];
184797
+ if (typeof legacyCwd === "string" && legacyCwd.length > 0) try {
184798
+ return normalizeWorkDir(legacyCwd);
184799
+ } catch {
184800
+ return;
184801
+ }
184802
+ }
184803
+ async listWorkDir(workDir, includeArchive) {
184804
+ const bucketDir = join$4(this.sessionsDir, encodeWorkDirKey(workDir));
184805
+ let entries;
184806
+ try {
184807
+ entries = await readdir$1(bucketDir, { withFileTypes: true });
184808
+ } catch {
184809
+ return [];
184810
+ }
184811
+ const sessions = [];
184812
+ for (const entry of entries) {
184813
+ if (!entry.isDirectory()) continue;
184814
+ const id = entry.name;
184815
+ if (!isSafeSessionId(id)) continue;
184816
+ const dir = join$4(bucketDir, id);
184817
+ const summary = await this.summaryFromDir(id, dir, workDir);
184818
+ if (!includeArchive && summary.archived === true) continue;
184819
+ sessions.push(summary);
184820
+ }
184821
+ sessions.sort(compareSessionSummary);
184822
+ return sessions;
184823
+ }
184824
+ async listSessionId(sessionId, includeArchive) {
184825
+ try {
184826
+ const summary = await this.get(sessionId);
184827
+ if (!includeArchive && summary.archived === true) return [];
184828
+ return [summary];
184829
+ } catch (error) {
184830
+ if (error instanceof KimiError && error.code === ErrorCodes.SESSION_NOT_FOUND) return [];
184831
+ throw error;
184832
+ }
184833
+ }
184834
+ async listAll(includeArchive) {
184835
+ const index = await readSessionIndex$1(this.homeDir, this.sessionsDir);
184836
+ const sessions = [];
184837
+ for (const entry of index.values()) {
184838
+ if (!await isDirectory(entry.sessionDir)) continue;
184839
+ const summary = await this.summaryFromDir(entry.sessionId, entry.sessionDir, entry.workDir);
184840
+ if (!includeArchive && summary.archived === true) continue;
184841
+ sessions.push(summary);
184842
+ }
184843
+ sessions.sort(compareSessionSummary);
184844
+ return sessions;
184845
+ }
184846
+ async summaryFromWorkDirSession(sessionId, workDir, includeArchive) {
184847
+ if (!isSafeSessionId(sessionId)) return void 0;
184848
+ const sessionDir = this.sessionDirFor({
184849
+ id: sessionId,
184850
+ workDir
184851
+ });
184852
+ if (!await isDirectory(sessionDir)) return void 0;
184853
+ const summary = await this.summaryFromDir(sessionId, sessionDir, workDir);
184854
+ if (!includeArchive && summary.archived === true) return void 0;
184855
+ return summary;
184856
+ }
184857
+ async assertDirectory(id) {
184858
+ return (await this.findExistingSessionEntry(id)).sessionDir;
184859
+ }
184860
+ async findSessionEntry(id) {
184861
+ if (!isSafeSessionId(id)) return void 0;
184862
+ return (await readSessionIndex$1(this.homeDir, this.sessionsDir)).get(id);
184863
+ }
184864
+ async findExistingSessionEntry(id) {
184865
+ const entry = await this.findSessionEntry(id);
184866
+ if (entry !== void 0 && await isDirectory(entry.sessionDir)) return entry;
184867
+ throw new KimiError(ErrorCodes.SESSION_NOT_FOUND, `Session "${id}" was not found`, { details: { sessionId: id } });
184868
+ }
184869
+ async writeForkedState(input, sourceDir, sourceWorkDir, targetDir) {
184870
+ const statePath = join$4(targetDir, "state.json");
184871
+ let parsed;
184872
+ try {
184873
+ parsed = JSON.parse(await readFile(statePath, "utf-8"));
184874
+ } catch (error) {
184875
+ throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${input.sourceId}" state.json was not found`, { cause: error });
184876
+ }
184877
+ if (!isRecord$11(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${input.sourceId}" state.json is invalid`);
184878
+ const title = normalizeForkTitle(input.title, parsed["title"]);
184879
+ const now = (/* @__PURE__ */ new Date()).toISOString();
184880
+ const next = {
184881
+ ...parsed,
184882
+ createdAt: now,
184883
+ updatedAt: now,
184884
+ workDir: sourceWorkDir,
184885
+ title,
184886
+ isCustomTitle: input.title === void 0 ? parsed["isCustomTitle"] === true : true,
184887
+ forkedFrom: input.sourceId,
184888
+ agents: rewriteAgentHomedirs(parsed["agents"], sourceDir, targetDir),
184889
+ custom: forkCustomMetadata(parsed["custom"], input.metadata)
184890
+ };
184891
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
184892
+ return next;
184893
+ }
184894
+ async summaryFromDir(id, sessionDir, workDir) {
184895
+ const dirStat = await stat$2(sessionDir);
184896
+ const state = await readOptionalState(sessionDir);
184897
+ const [stateInfo, wireInfo, agentsWireMtime] = await Promise.all([
184898
+ statIfExists(join$4(sessionDir, "state.json")),
184899
+ statIfExists(join$4(sessionDir, "wire.jsonl")),
184900
+ latestAgentWireMtime(sessionDir)
184901
+ ]);
184902
+ return {
184903
+ id,
184904
+ workDir: state?.workDir ?? workDir,
184905
+ sessionDir,
184906
+ createdAt: timestampOrFallback(dirStat.birthtimeMs, dirStat.ctimeMs),
184907
+ updatedAt: Math.max(dirStat.mtimeMs, stateInfo?.mtimeMs ?? 0, wireInfo?.mtimeMs ?? 0, agentsWireMtime ?? 0),
184908
+ archived: state?.archived === true,
184909
+ title: titleFromState(state),
184910
+ lastPrompt: state?.lastPrompt,
184911
+ metadata: metadataFromState(state)
184912
+ };
184913
+ }
184914
+ };
184915
+ }));
184916
+ //#endregion
184917
+ //#region ../../packages/agent-core/src/session/store/index.ts
184918
+ var init_store$1 = __esmMin((() => {
184919
+ init_session_store$1();
184920
+ init_session_index();
184921
+ init_workdir_key();
184922
+ }));
184923
+ //#endregion
183820
184924
  //#region ../../packages/agent-core/src/session/index.ts
183821
184925
  async function waitForSettlementOrTimeout(promise, timeoutMs) {
183822
184926
  let timeout;
@@ -183859,6 +184963,7 @@ var init_session$1 = __esmMin((() => {
183859
184963
  init_flags();
183860
184964
  init_abort();
183861
184965
  init_subagent_host();
184966
+ init_store$1();
183862
184967
  BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV = "KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT";
183863
184968
  ACTIVE_TURN_CLOSE_TIMEOUT_MS = 8e3;
183864
184969
  Session$1 = class {
@@ -184993,7 +186098,7 @@ async function writeInstalled(kimiHomeDir, data) {
184993
186098
  await rename(tmp, final);
184994
186099
  }
184995
186100
  var INSTALLED_REL, EMPTY;
184996
- var init_store$1 = __esmMin((() => {
186101
+ var init_store = __esmMin((() => {
184997
186102
  INSTALLED_REL = path.join("plugins", "installed.json");
184998
186103
  EMPTY = {
184999
186104
  version: 1,
@@ -185488,7 +186593,7 @@ var init_manager = __esmMin((() => {
185488
186593
  init_archive();
185489
186594
  init_github_resolver();
185490
186595
  init_manifest$1();
185491
- init_store$1();
186596
+ init_store();
185492
186597
  init_source();
185493
186598
  init_types$1();
185494
186599
  KIMI_NODE_FALLBACK_SUBCOMMAND = "__plugin_run_node";
@@ -185766,7 +186871,7 @@ var init_manager = __esmMin((() => {
185766
186871
  var init_plugin$1 = __esmMin((() => {
185767
186872
  init_types$1();
185768
186873
  init_manifest$1();
185769
- init_store$1();
186874
+ init_store();
185770
186875
  init_manager();
185771
186876
  init_source();
185772
186877
  init_archive();
@@ -200316,7 +201421,7 @@ function tokenFromWire(wire) {
200316
201421
  var init_types = __esmMin((() => {}));
200317
201422
  //#endregion
200318
201423
  //#region ../../packages/oauth/src/utils.ts
200319
- function isRecord$11(value) {
201424
+ function isRecord$10(value) {
200320
201425
  return typeof value === "object" && value !== null && !Array.isArray(value);
200321
201426
  }
200322
201427
  var init_utils = __esmMin((() => {}));
@@ -200372,7 +201477,7 @@ var init_storage = __esmMin((() => {
200372
201477
  } catch {
200373
201478
  return;
200374
201479
  }
200375
- if (!isRecord$11(parsed)) return void 0;
201480
+ if (!isRecord$10(parsed)) return void 0;
200376
201481
  return tokenFromWire(parsed);
200377
201482
  }
200378
201483
  async save(name, token) {
@@ -200426,7 +201531,7 @@ function extractApiErrorMessage(value) {
200426
201531
  }
200427
201532
  return;
200428
201533
  }
200429
- if (!isRecord$11(value)) return void 0;
201534
+ if (!isRecord$10(value)) return void 0;
200430
201535
  for (const key of DIRECT_ERROR_KEYS) {
200431
201536
  const message = stringField$3(value, key);
200432
201537
  if (message !== void 0) return message;
@@ -200434,7 +201539,7 @@ function extractApiErrorMessage(value) {
200434
201539
  const error = value["error"];
200435
201540
  const errorString = nonEmptyString$2(error);
200436
201541
  if (errorString !== void 0) return errorString;
200437
- if (isRecord$11(error)) for (const key of NESTED_ERROR_KEYS) {
201542
+ if (isRecord$10(error)) for (const key of NESTED_ERROR_KEYS) {
200438
201543
  const message = stringField$3(error, key);
200439
201544
  if (message !== void 0) return message;
200440
201545
  }
@@ -200524,7 +201629,7 @@ async function postForm(url, params, deviceHeaders, options) {
200524
201629
  let data = {};
200525
201630
  try {
200526
201631
  const parsed = await response.json();
200527
- if (isRecord$11(parsed)) data = parsed;
201632
+ if (isRecord$10(parsed)) data = parsed;
200528
201633
  } catch {}
200529
201634
  return {
200530
201635
  status,
@@ -202397,9 +203502,9 @@ function parseManagedUsagePayload(payload) {
202397
203502
  const item = rawLimits[idx];
202398
203503
  if (!item || typeof item !== "object") continue;
202399
203504
  const detailRaw = item["detail"];
202400
- const detail = isRecord$11(detailRaw) ? detailRaw : item;
203505
+ const detail = isRecord$10(detailRaw) ? detailRaw : item;
202401
203506
  const windowRaw = item["window"];
202402
- const row = toUsageRow(detail, limitLabel(item, detail, isRecord$11(windowRaw) ? windowRaw : {}, idx));
203507
+ const row = toUsageRow(detail, limitLabel(item, detail, isRecord$10(windowRaw) ? windowRaw : {}, idx));
202403
203508
  if (row !== null) limits.push(row);
202404
203509
  }
202405
203510
  return {
@@ -202408,7 +203513,7 @@ function parseManagedUsagePayload(payload) {
202408
203513
  };
202409
203514
  }
202410
203515
  function toUsageRow(raw, defaultLabel) {
202411
- if (!isRecord$11(raw)) return null;
203516
+ if (!isRecord$10(raw)) return null;
202412
203517
  const limit = toInt(raw["limit"]);
202413
203518
  let used = toInt(raw["used"]);
202414
203519
  if (used === null) {
@@ -202561,8 +203666,8 @@ function userExtras(existing, remoteOwnedFields) {
202561
203666
  return out;
202562
203667
  }
202563
203668
  function mergeRefreshedModelAlias(existing, remote, remoteOwnedFields) {
202564
- const current = isRecord$11(existing) ? existing : {};
202565
- const overrides = cloneOverrides(isRecord$11(current["overrides"]) ? current["overrides"] : void 0);
203669
+ const current = isRecord$10(existing) ? existing : {};
203670
+ const overrides = cloneOverrides(isRecord$10(current["overrides"]) ? current["overrides"] : void 0);
202566
203671
  return {
202567
203672
  ...userExtras(current, remoteOwnedFields),
202568
203673
  ...remote,
@@ -202742,7 +203847,7 @@ function resolveKimiCodeLoginAuth(options) {
202742
203847
  };
202743
203848
  }
202744
203849
  function toModelInfo$1(item) {
202745
- if (!isRecord$11(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
203850
+ if (!isRecord$10(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
202746
203851
  const contextLength = Number(item["context_length"]);
202747
203852
  if (!Number.isInteger(contextLength) || contextLength <= 0) throw new Error(`Kimi Code model "${item["id"]}" must include a positive context_length.`);
202748
203853
  const displayName = item["display_name"];
@@ -202813,7 +203918,7 @@ async function fetchManagedKimiCodeModels(options) {
202813
203918
  throw new Error(message);
202814
203919
  }
202815
203920
  const payload = await response.json();
202816
- if (!isRecord$11(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
203921
+ if (!isRecord$10(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
202817
203922
  return payload["data"].map((item) => toModelInfo$1(item)).filter((item) => item !== void 0);
202818
203923
  }
202819
203924
  function applyManagedKimiCodeConfig(config, options) {
@@ -202836,11 +203941,11 @@ function applyManagedKimiCodeConfig(config, options) {
202836
203941
  oauth
202837
203942
  };
202838
203943
  const upstreamKeys = new Set(options.models.map((m) => managedModelKey(m.id)));
202839
- for (const [key, model] of Object.entries(existingModels)) if (isRecord$11(model) && model["provider"] === "managed:kimi-code" && !upstreamKeys.has(key)) delete existingModels[key];
203944
+ for (const [key, model] of Object.entries(existingModels)) if (isRecord$10(model) && model["provider"] === "managed:kimi-code" && !upstreamKeys.has(key)) delete existingModels[key];
202840
203945
  for (const model of options.models) {
202841
203946
  const capabilities = capabilitiesForModel$1(model);
202842
203947
  const key = managedModelKey(model.id);
202843
- const existing = isRecord$11(existingModels[key]) ? existingModels[key] : {};
203948
+ const existing = isRecord$10(existingModels[key]) ? existingModels[key] : {};
202844
203949
  const supportsAdaptiveThinking = model.protocol === "anthropic" && (capabilities?.includes("thinking") === true || capabilities?.includes("always_thinking") === true);
202845
203950
  existingModels[key] = mergeRefreshedModelAlias(existing, {
202846
203951
  provider: KIMI_CODE_PROVIDER_NAME$1,
@@ -202883,7 +203988,7 @@ function applyManagedKimiCodeLogoutConfig(config) {
202883
203988
  let removedDefaultModel = false;
202884
203989
  const existingModels = config.models ?? {};
202885
203990
  for (const [key, model] of Object.entries(existingModels)) {
202886
- if (!isRecord$11(model) || model["provider"] !== "managed:kimi-code") continue;
203991
+ if (!isRecord$10(model) || model["provider"] !== "managed:kimi-code") continue;
202887
203992
  delete existingModels[key];
202888
203993
  if (config.defaultModel === key) removedDefaultModel = true;
202889
203994
  }
@@ -202922,7 +204027,7 @@ function selectDefaultModel(config, models, options) {
202922
204027
  function canPreserveDefaultModel(existingModels, defaultModel, managedModels) {
202923
204028
  if (managedModels.has(defaultModel)) return true;
202924
204029
  const existing = existingModels[defaultModel];
202925
- return isRecord$11(existing) && existing["provider"] !== "managed:kimi-code";
204030
+ return isRecord$10(existing) && existing["provider"] !== "managed:kimi-code";
202926
204031
  }
202927
204032
  function assertPositiveContextLength(model) {
202928
204033
  if (!Number.isInteger(model.contextLength) || model.contextLength <= 0) throw new Error(`Kimi Code model "${model.id}" must include a positive context_length.`);
@@ -203162,7 +204267,7 @@ function isOpenPlatformId(id) {
203162
204267
  return OPEN_PLATFORMS.some((p) => p.id === id);
203163
204268
  }
203164
204269
  function toModelInfo(item) {
203165
- if (!isRecord$11(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
204270
+ if (!isRecord$10(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
203166
204271
  const contextLength = Number(item["context_length"]);
203167
204272
  if (!Number.isInteger(contextLength) || contextLength <= 0) throw new Error(`Model "${item["id"]}" must include a positive context_length.`);
203168
204273
  const displayName = item["display_name"];
@@ -203213,7 +204318,7 @@ async function fetchOpenPlatformModels(platform, apiKey, fetchImpl = fetch, sign
203213
204318
  });
203214
204319
  if (!res.ok) throw new OpenPlatformApiError(await readApiErrorMessage(res, `Failed to list models (HTTP ${res.status}).`), res.status);
203215
204320
  const payload = await res.json();
203216
- if (!isRecord$11(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${platform.baseUrl}.`);
204321
+ if (!isRecord$10(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${platform.baseUrl}.`);
203217
204322
  return payload["data"].map((item) => toModelInfo(item)).filter((item) => item !== void 0);
203218
204323
  }
203219
204324
  function filterModelsByPrefix(models, platform) {
@@ -203231,10 +204336,10 @@ function applyOpenPlatformConfig(config, options) {
203231
204336
  };
203232
204337
  const existingModels = config.models ?? {};
203233
204338
  const upstreamKeys = new Set(options.models.map((m) => `${providerKey}/${m.id}`));
203234
- for (const [key, model] of Object.entries(existingModels)) if (isRecord$11(model) && model["provider"] === providerKey && !upstreamKeys.has(key)) delete existingModels[key];
204339
+ for (const [key, model] of Object.entries(existingModels)) if (isRecord$10(model) && model["provider"] === providerKey && !upstreamKeys.has(key)) delete existingModels[key];
203235
204340
  for (const model of options.models) {
203236
204341
  const aliasKey = `${providerKey}/${model.id}`;
203237
- existingModels[aliasKey] = mergeRefreshedModelAlias(isRecord$11(existingModels[aliasKey]) ? existingModels[aliasKey] : {}, {
204342
+ existingModels[aliasKey] = mergeRefreshedModelAlias(isRecord$10(existingModels[aliasKey]) ? existingModels[aliasKey] : {}, {
203238
204343
  provider: providerKey,
203239
204344
  model: model.id,
203240
204345
  maxContextSize: model.contextLength,
@@ -203300,14 +204405,14 @@ function toStringArrayOrUndefined(value) {
203300
204405
  return out;
203301
204406
  }
203302
204407
  function toModelEntry(value) {
203303
- if (!isRecord$11(value)) return void 0;
204408
+ if (!isRecord$10(value)) return void 0;
203304
204409
  const id = value["id"];
203305
204410
  if (typeof id !== "string" || id.length === 0) return void 0;
203306
204411
  const entry = { id };
203307
204412
  const name = value["name"];
203308
204413
  if (typeof name === "string" && name.length > 0) entry.name = name;
203309
204414
  const limit = value["limit"];
203310
- if (isRecord$11(limit)) {
204415
+ if (isRecord$10(limit)) {
203311
204416
  const context = limit["context"];
203312
204417
  const output = limit["output"];
203313
204418
  const parsedLimit = {};
@@ -203318,7 +204423,7 @@ function toModelEntry(value) {
203318
204423
  if (typeof value["tool_call"] === "boolean") entry.tool_call = value["tool_call"];
203319
204424
  if (typeof value["reasoning"] === "boolean") entry.reasoning = value["reasoning"];
203320
204425
  const modalities = value["modalities"];
203321
- if (isRecord$11(modalities)) {
204426
+ if (isRecord$10(modalities)) {
203322
204427
  const input = toStringArrayOrUndefined(modalities["input"]);
203323
204428
  const output = toStringArrayOrUndefined(modalities["output"]);
203324
204429
  if (input !== void 0 || output !== void 0) entry.modalities = {
@@ -203329,7 +204434,7 @@ function toModelEntry(value) {
203329
204434
  return entry;
203330
204435
  }
203331
204436
  function toProviderEntry(value) {
203332
- if (!isRecord$11(value)) return void 0;
204437
+ if (!isRecord$10(value)) return void 0;
203333
204438
  const id = value["id"];
203334
204439
  const name = value["name"];
203335
204440
  const api = value["api"];
@@ -203339,7 +204444,7 @@ function toProviderEntry(value) {
203339
204444
  if (typeof name !== "string" || name.length === 0) return void 0;
203340
204445
  if (typeof api !== "string" || api.length === 0) return void 0;
203341
204446
  if (!isAllowedProviderType(type)) return void 0;
203342
- if (!isRecord$11(models)) return void 0;
204447
+ if (!isRecord$10(models)) return void 0;
203343
204448
  const parsedModels = {};
203344
204449
  for (const [key, raw] of Object.entries(models)) {
203345
204450
  const modelEntry = toModelEntry(raw);
@@ -203369,7 +204474,7 @@ async function fetchCustomRegistry(source, fetchImpl = fetch, signal) {
203369
204474
  const response = await fetchImpl(source.url, init);
203370
204475
  if (!response.ok) throw new CustomRegistryApiError(await readApiErrorMessage(response, `Failed to fetch custom registry at ${source.url} (HTTP ${response.status}).`), response.status);
203371
204476
  const payload = await response.json();
203372
- if (!isRecord$11(payload)) throw new Error(`Unexpected custom registry response at ${source.url}: expected a JSON object keyed by provider id.`);
204477
+ if (!isRecord$10(payload)) throw new Error(`Unexpected custom registry response at ${source.url}: expected a JSON object keyed by provider id.`);
203373
204478
  const out = {};
203374
204479
  for (const [key, raw] of Object.entries(payload)) {
203375
204480
  const entry = toProviderEntry(raw);
@@ -203429,13 +204534,13 @@ function applyCustomRegistryProvider(config, entry, source) {
203429
204534
  };
203430
204535
  const existingModels = config.models ?? {};
203431
204536
  const upstreamKeys = new Set(Object.keys(entry.models).map((modelKey) => `${providerKey}/${modelKey}`));
203432
- for (const [key, alias] of Object.entries(existingModels)) if (isRecord$11(alias) && alias["provider"] === providerKey && !upstreamKeys.has(key)) delete existingModels[key];
204537
+ for (const [key, alias] of Object.entries(existingModels)) if (isRecord$10(alias) && alias["provider"] === providerKey && !upstreamKeys.has(key)) delete existingModels[key];
203433
204538
  for (const [modelKey, model] of Object.entries(entry.models)) {
203434
204539
  const aliasKey = `${providerKey}/${modelKey}`;
203435
204540
  const maxContextSize = resolveMaxContextSize(model);
203436
204541
  const capabilities = resolveCapabilities(model);
203437
204542
  const displayName = typeof model.name === "string" && model.name.length > 0 ? model.name : model.id;
203438
- existingModels[aliasKey] = mergeRefreshedModelAlias(isRecord$11(existingModels[aliasKey]) ? existingModels[aliasKey] : {}, {
204543
+ existingModels[aliasKey] = mergeRefreshedModelAlias(isRecord$10(existingModels[aliasKey]) ? existingModels[aliasKey] : {}, {
203439
204544
  provider: providerKey,
203440
204545
  model: model.id,
203441
204546
  maxContextSize,
@@ -203455,7 +204560,7 @@ function removeCustomRegistryProvider(config, providerId) {
203455
204560
  let removedDefault = false;
203456
204561
  const existingModels = config.models ?? {};
203457
204562
  for (const [key, alias] of Object.entries(existingModels)) {
203458
- if (!isRecord$11(alias) || alias["provider"] !== providerId) continue;
204563
+ if (!isRecord$10(alias) || alias["provider"] !== providerId) continue;
203459
204564
  delete existingModels[key];
203460
204565
  if (config.defaultModel === key) removedDefault = true;
203461
204566
  }
@@ -203487,9 +204592,9 @@ function applyCustomRegistryEntries(config, entries, source) {
203487
204592
  const surviving = new Set(Object.values(entries).map((entry) => entry.id));
203488
204593
  for (const [providerId, provider] of Object.entries(config.providers)) {
203489
204594
  if (surviving.has(providerId)) continue;
203490
- if (!isRecord$11(provider)) continue;
204595
+ if (!isRecord$10(provider)) continue;
203491
204596
  const existingSource = provider["source"];
203492
- if (isRecord$11(existingSource) && existingSource["kind"] === "apiJson" && existingSource["url"] === source.url) removeCustomRegistryProvider(config, providerId);
204597
+ if (isRecord$10(existingSource) && existingSource["kind"] === "apiJson" && existingSource["url"] === source.url) removeCustomRegistryProvider(config, providerId);
203493
204598
  }
203494
204599
  for (const entry of Object.values(entries)) {
203495
204600
  if (entry.id in config.providers) removeCustomRegistryProvider(config, entry.id);
@@ -204187,7 +205292,8 @@ function resolveModelCapabilities(alias, provider) {
204187
205292
  audio_in: declared.has("audio_in") || detected.audio_in,
204188
205293
  thinking: declared.has("thinking") || declared.has("always_thinking") || detected.thinking,
204189
205294
  tool_use: declared.has("tool_use") || detected.tool_use,
204190
- max_context_tokens: alias.maxContextSize
205295
+ max_context_tokens: alias.maxContextSize,
205296
+ select_tools: declared.has("select_tools") || detected.select_tools === true
204191
205297
  };
204192
205298
  }
204193
205299
  function toKosongProviderConfig(provider, model, modelProtocol, kimiRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey, adaptiveThinking, betaApi, supportEfforts) {
@@ -204691,498 +205797,6 @@ var init_rpc$1 = __esmMin((() => {
204691
205797
  };
204692
205798
  }));
204693
205799
  //#endregion
204694
- //#region ../../packages/agent-core/src/session/store/session-index.ts
204695
- function sessionIndexPath(homeDir) {
204696
- return join$4(homeDir, "session_index.jsonl");
204697
- }
204698
- async function appendSessionIndexEntry(homeDir, entry) {
204699
- const indexPath = sessionIndexPath(homeDir);
204700
- await mkdir(dirname$3(indexPath), {
204701
- recursive: true,
204702
- mode: 448
204703
- });
204704
- await appendFile(indexPath, `${JSON.stringify(entry)}\n`, "utf-8");
204705
- }
204706
- async function readSessionIndex$1(homeDir, sessionsDir) {
204707
- let raw;
204708
- try {
204709
- raw = await readFile(sessionIndexPath(homeDir), "utf-8");
204710
- } catch {
204711
- return /* @__PURE__ */ new Map();
204712
- }
204713
- const result = /* @__PURE__ */ new Map();
204714
- for (const line of raw.split(/\r?\n/)) {
204715
- const trimmed = line.trim();
204716
- if (trimmed === "") continue;
204717
- const entry = parseIndexLine(trimmed);
204718
- if (entry === void 0) continue;
204719
- const sessionDir = resolve$2(entry.sessionDir);
204720
- if (!isAbsolute$2(entry.sessionDir)) continue;
204721
- if (!isAbsolute$2(entry.workDir)) continue;
204722
- if (!isPathInside(sessionsDir, sessionDir)) continue;
204723
- if (basename$2(sessionDir) !== entry.sessionId) continue;
204724
- result.set(entry.sessionId, {
204725
- sessionId: entry.sessionId,
204726
- sessionDir,
204727
- workDir: resolve$2(entry.workDir)
204728
- });
204729
- }
204730
- return result;
204731
- }
204732
- function parseIndexLine(line) {
204733
- try {
204734
- const parsed = JSON.parse(line);
204735
- if (typeof parsed !== "object" || parsed === null) return void 0;
204736
- const entry = parsed;
204737
- if (typeof entry.sessionId !== "string" || typeof entry.sessionDir !== "string" || typeof entry.workDir !== "string") return;
204738
- return {
204739
- sessionId: entry.sessionId,
204740
- sessionDir: entry.sessionDir,
204741
- workDir: entry.workDir
204742
- };
204743
- } catch {
204744
- return;
204745
- }
204746
- }
204747
- function isPathInside(parent, child) {
204748
- const rel = relative$1(resolve$2(parent), resolve$2(child));
204749
- return rel !== "" && !rel.startsWith("..") && !isAbsolute$2(rel);
204750
- }
204751
- var init_session_index = __esmMin((() => {
204752
- init_dist$7();
204753
- }));
204754
- //#endregion
204755
- //#region ../../packages/agent-core/src/utils/workdir-slug.ts
204756
- function slugifyWorkDirName(name) {
204757
- const slug = name.toLowerCase().replaceAll(/[^a-z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, MAX_WORKDIR_SLUG_LENGTH).replaceAll(/^-+|-+$/g, "");
204758
- return slug === "" || slug === "." || slug === ".." ? "workspace" : slug;
204759
- }
204760
- var MAX_WORKDIR_SLUG_LENGTH;
204761
- var init_workdir_slug = __esmMin((() => {
204762
- MAX_WORKDIR_SLUG_LENGTH = 40;
204763
- }));
204764
- //#endregion
204765
- //#region ../../packages/agent-core/src/session/store/workdir-key.ts
204766
- function normalizeWorkDir(workDir) {
204767
- return resolve$2(workDir);
204768
- }
204769
- function encodeWorkDirKey(workDir) {
204770
- const normalized = normalizeWorkDir(workDir);
204771
- return `${WORKDIR_KEY_PREFIX}${slugifyWorkDirName(basename$2(normalized))}_${createHash("sha256").update(normalized).digest("hex").slice(0, HASH_LENGTH)}`;
204772
- }
204773
- var WORKDIR_KEY_PREFIX, HASH_LENGTH;
204774
- var init_workdir_key = __esmMin((() => {
204775
- init_dist$7();
204776
- init_workdir_slug();
204777
- WORKDIR_KEY_PREFIX = "wd_";
204778
- HASH_LENGTH = 12;
204779
- }));
204780
- //#endregion
204781
- //#region ../../packages/agent-core/src/session/store/session-store.ts
204782
- function metadataFromState(state) {
204783
- if (state === void 0 || state.custom === void 0) return void 0;
204784
- return state.custom;
204785
- }
204786
- function forkCustomMetadata(source, metadata) {
204787
- return {
204788
- ...customMetadataWithoutGoal(source),
204789
- ...customMetadataWithoutGoal(metadata)
204790
- };
204791
- }
204792
- async function dropForkedSessionFiles(sessionDir) {
204793
- await Promise.all(FORKED_SESSION_DROPPED_FILES.map((fileName) => rm(join$4(sessionDir, fileName), { force: true })));
204794
- }
204795
- async function appendForkedMarkers(state) {
204796
- const record = {
204797
- type: "forked",
204798
- time: Date.now()
204799
- };
204800
- const agents = state["agents"];
204801
- if (!isRecord$10(agents)) return;
204802
- const paths = /* @__PURE__ */ new Set();
204803
- for (const agentMeta of Object.values(agents)) {
204804
- if (!isRecord$10(agentMeta)) continue;
204805
- const homedir = agentMeta["homedir"];
204806
- if (typeof homedir !== "string") continue;
204807
- paths.add(join$4(homedir, "wire.jsonl"));
204808
- }
204809
- await Promise.all([...paths].map(async (path) => {
204810
- const persistence = new FileSystemAgentRecordPersistence(path);
204811
- persistence.append(record);
204812
- await persistence.flush();
204813
- }));
204814
- }
204815
- function customMetadataWithoutGoal(value) {
204816
- if (!isRecord$10(value)) return {};
204817
- const custom = {};
204818
- for (const [key, entry] of Object.entries(value)) {
204819
- if (key === "goal") continue;
204820
- custom[key] = entry;
204821
- }
204822
- return custom;
204823
- }
204824
- async function latestAgentWireMtime(sessionDir) {
204825
- const agentsDir = join$4(sessionDir, "agents");
204826
- let entries;
204827
- try {
204828
- entries = await readdir$1(agentsDir, { withFileTypes: true });
204829
- } catch {
204830
- return;
204831
- }
204832
- let latest = 0;
204833
- for (const entry of entries) {
204834
- if (!entry.isDirectory()) continue;
204835
- const wireInfo = await statIfExists(join$4(agentsDir, entry.name, "wire.jsonl"));
204836
- latest = Math.max(latest, wireInfo?.mtimeMs ?? 0);
204837
- }
204838
- return latest > 0 ? latest : void 0;
204839
- }
204840
- function titleFromState(state) {
204841
- if (state === void 0) return void 0;
204842
- if (typeof state.isCustomTitle === "boolean" && typeof state.title === "string") return state.title;
204843
- if (typeof state.customTitle === "string") return state.customTitle;
204844
- return typeof state.title === "string" ? state.title : void 0;
204845
- }
204846
- async function readOptionalState(sessionDir) {
204847
- try {
204848
- const parsed = JSON.parse(await readFile(join$4(sessionDir, "state.json"), "utf-8"));
204849
- const result = SessionSummaryStateSchema.safeParse(parsed);
204850
- return result.success ? result.data : void 0;
204851
- } catch {
204852
- return;
204853
- }
204854
- }
204855
- function normalizeRequiredWorkDir(workDir) {
204856
- if (workDir.trim() === "") throw new KimiError(ErrorCodes.REQUEST_WORK_DIR_REQUIRED, "listSessions requires workDir");
204857
- return normalizeWorkDir(workDir);
204858
- }
204859
- function normalizeOptionalSessionId$1(sessionId) {
204860
- return sessionId === void 0 ? void 0 : sessionId.trim();
204861
- }
204862
- function normalizeForkTitle(title, fallback) {
204863
- if (title !== void 0) {
204864
- const normalized = title.trim();
204865
- if (normalized.length === 0) throw new KimiError(ErrorCodes.SESSION_TITLE_EMPTY, "Session title cannot be empty");
204866
- return normalized;
204867
- }
204868
- return typeof fallback === "string" && fallback.trim().length > 0 ? fallback : "New Session";
204869
- }
204870
- function rewriteAgentHomedirs(value, sourceDir, targetDir) {
204871
- if (!isRecord$10(value)) return {};
204872
- const agents = {};
204873
- for (const [agentId, agentMeta] of Object.entries(value)) {
204874
- if (!isRecord$10(agentMeta)) {
204875
- agents[agentId] = agentMeta;
204876
- continue;
204877
- }
204878
- const homedir = agentMeta["homedir"];
204879
- agents[agentId] = {
204880
- ...agentMeta,
204881
- homedir: typeof homedir === "string" ? remapSessionPath(homedir, sourceDir, targetDir) : homedir
204882
- };
204883
- }
204884
- return agents;
204885
- }
204886
- function remapSessionPath(value, sourceDir, targetDir) {
204887
- const rel = relative$1(sourceDir, value);
204888
- if (rel === "") return targetDir;
204889
- if (rel.startsWith("..") || isAbsolute$2(rel)) return value;
204890
- return join$4(targetDir, rel);
204891
- }
204892
- function isRecord$10(value) {
204893
- return typeof value === "object" && value !== null && !Array.isArray(value);
204894
- }
204895
- async function statIfExists(path) {
204896
- try {
204897
- return await stat$2(path);
204898
- } catch {
204899
- return;
204900
- }
204901
- }
204902
- async function isDirectory(path) {
204903
- try {
204904
- return (await stat$2(path)).isDirectory();
204905
- } catch {
204906
- return false;
204907
- }
204908
- }
204909
- function timestampOrFallback(value, fallback) {
204910
- return Number.isFinite(value) && value > 0 ? value : fallback;
204911
- }
204912
- function assertSafeSessionId(id) {
204913
- if (isSafeSessionId(id)) return;
204914
- throw new KimiError(ErrorCodes.SESSION_ID_INVALID, "Session id contains unsupported path characters");
204915
- }
204916
- function isSafeSessionId(id) {
204917
- return /^[A-Za-z0-9._-]+$/.test(id) && id !== "." && id !== "..";
204918
- }
204919
- function compareSessionSummary(a, b) {
204920
- if (a.updatedAt !== b.updatedAt) return b.updatedAt - a.updatedAt;
204921
- if (a.createdAt !== b.createdAt) return b.createdAt - a.createdAt;
204922
- if (a.id < b.id) return -1;
204923
- if (a.id > b.id) return 1;
204924
- return 0;
204925
- }
204926
- var SessionSummaryStateSchema, FORKED_SESSION_DROPPED_FILES, SessionStore;
204927
- var init_session_store$1 = __esmMin((() => {
204928
- init_dist$7();
204929
- init_zod$1();
204930
- init_errors$8();
204931
- init_session_index();
204932
- init_workdir_key();
204933
- init_records();
204934
- SessionSummaryStateSchema = object({
204935
- archived: boolean$1().optional(),
204936
- customTitle: string().optional(),
204937
- isCustomTitle: boolean$1().optional(),
204938
- lastPrompt: string().optional(),
204939
- title: string().optional(),
204940
- custom: record(string(), unknown()).optional()
204941
- });
204942
- FORKED_SESSION_DROPPED_FILES = ["upcoming-goals.json"];
204943
- SessionStore = class {
204944
- homeDir;
204945
- sessionsDir;
204946
- constructor(homeDir, _options = {}) {
204947
- this.homeDir = homeDir;
204948
- this.sessionsDir = join$4(homeDir, "sessions");
204949
- }
204950
- sessionDirFor(input) {
204951
- assertSafeSessionId(input.id);
204952
- return join$4(this.sessionsDir, encodeWorkDirKey(normalizeWorkDir(input.workDir)), input.id);
204953
- }
204954
- async create(input) {
204955
- assertSafeSessionId(input.id);
204956
- const workDir = normalizeWorkDir(input.workDir);
204957
- if (await this.findSessionEntry(input.id) !== void 0) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.id}" already exists`);
204958
- const dir = this.sessionDirFor({
204959
- id: input.id,
204960
- workDir
204961
- });
204962
- if (await isDirectory(dir)) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.id}" already exists`);
204963
- await mkdir(dir, {
204964
- recursive: true,
204965
- mode: 448
204966
- });
204967
- await appendSessionIndexEntry(this.homeDir, {
204968
- sessionId: input.id,
204969
- sessionDir: dir,
204970
- workDir
204971
- });
204972
- return this.summaryFromDir(input.id, dir, workDir);
204973
- }
204974
- async fork(input) {
204975
- const source = await this.findExistingSessionEntry(input.sourceId);
204976
- assertSafeSessionId(input.targetId);
204977
- if (await this.findSessionEntry(input.targetId) !== void 0) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.targetId}" already exists`);
204978
- const targetDir = this.sessionDirFor({
204979
- id: input.targetId,
204980
- workDir: source.workDir
204981
- });
204982
- if (await isDirectory(targetDir)) throw new KimiError(ErrorCodes.SESSION_ALREADY_EXISTS, `Session "${input.targetId}" already exists`);
204983
- await mkdir(dirname$3(targetDir), {
204984
- recursive: true,
204985
- mode: 448
204986
- });
204987
- try {
204988
- await cp(source.sessionDir, targetDir, {
204989
- recursive: true,
204990
- force: false,
204991
- errorOnExist: true
204992
- });
204993
- await dropForkedSessionFiles(targetDir);
204994
- await appendForkedMarkers(await this.writeForkedState(input, source.sessionDir, targetDir));
204995
- const summary = await this.summaryFromDir(input.targetId, targetDir, source.workDir);
204996
- await appendSessionIndexEntry(this.homeDir, {
204997
- sessionId: input.targetId,
204998
- sessionDir: targetDir,
204999
- workDir: source.workDir
205000
- });
205001
- return summary;
205002
- } catch (error) {
205003
- await rm(targetDir, {
205004
- recursive: true,
205005
- force: true
205006
- }).catch(() => {});
205007
- throw error;
205008
- }
205009
- }
205010
- async get(id) {
205011
- const entry = await this.findExistingSessionEntry(id);
205012
- return this.summaryFromDir(id, entry.sessionDir, entry.workDir);
205013
- }
205014
- async rename(id, title) {
205015
- const normalized = title.trim();
205016
- if (normalized.length === 0) throw new KimiError(ErrorCodes.SESSION_TITLE_EMPTY, "Session title cannot be empty");
205017
- const statePath = join$4((await this.findExistingSessionEntry(id)).sessionDir, "state.json");
205018
- let parsed;
205019
- try {
205020
- parsed = JSON.parse(await readFile(statePath, "utf-8"));
205021
- } catch (error) {
205022
- throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${id}" state.json was not found`, { cause: error });
205023
- }
205024
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${id}" state.json is invalid`);
205025
- const next = {
205026
- ...parsed,
205027
- title: normalized,
205028
- isCustomTitle: true
205029
- };
205030
- await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
205031
- }
205032
- async archive(id) {
205033
- const entry = await this.findExistingSessionEntry(id);
205034
- const statePath = join$4(entry.sessionDir, "state.json");
205035
- let parsed;
205036
- try {
205037
- parsed = JSON.parse(await readFile(statePath, "utf-8"));
205038
- } catch (error) {
205039
- throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${id}" state.json was not found`, { cause: error });
205040
- }
205041
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${id}" state.json is invalid`);
205042
- const now = (/* @__PURE__ */ new Date()).toISOString();
205043
- const next = {
205044
- ...parsed,
205045
- archived: true,
205046
- updatedAt: now
205047
- };
205048
- await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
205049
- return this.summaryFromDir(id, entry.sessionDir, entry.workDir);
205050
- }
205051
- async list(options = {}) {
205052
- const workDir = options.workDir === void 0 ? void 0 : normalizeRequiredWorkDir(options.workDir);
205053
- const sessionId = normalizeOptionalSessionId$1(options.sessionId);
205054
- const includeArchive = options.includeArchive === true;
205055
- if (workDir !== void 0) {
205056
- if (sessionId !== void 0) {
205057
- const local = await this.summaryFromWorkDirSession(sessionId, workDir, includeArchive);
205058
- if (local !== void 0) return [local];
205059
- return this.listSessionId(sessionId, includeArchive);
205060
- }
205061
- return this.listWorkDir(workDir, includeArchive);
205062
- }
205063
- if (sessionId !== void 0) return this.listSessionId(sessionId, includeArchive);
205064
- return this.listAll(includeArchive);
205065
- }
205066
- async listWorkDir(workDir, includeArchive) {
205067
- const bucketDir = join$4(this.sessionsDir, encodeWorkDirKey(workDir));
205068
- let entries;
205069
- try {
205070
- entries = await readdir$1(bucketDir, { withFileTypes: true });
205071
- } catch {
205072
- return [];
205073
- }
205074
- const sessions = [];
205075
- for (const entry of entries) {
205076
- if (!entry.isDirectory()) continue;
205077
- const id = entry.name;
205078
- if (!isSafeSessionId(id)) continue;
205079
- const dir = join$4(bucketDir, id);
205080
- const summary = await this.summaryFromDir(id, dir, workDir);
205081
- if (!includeArchive && summary.archived === true) continue;
205082
- sessions.push(summary);
205083
- }
205084
- sessions.sort(compareSessionSummary);
205085
- return sessions;
205086
- }
205087
- async listSessionId(sessionId, includeArchive) {
205088
- try {
205089
- const summary = await this.get(sessionId);
205090
- if (!includeArchive && summary.archived === true) return [];
205091
- return [summary];
205092
- } catch (error) {
205093
- if (error instanceof KimiError && error.code === ErrorCodes.SESSION_NOT_FOUND) return [];
205094
- throw error;
205095
- }
205096
- }
205097
- async listAll(includeArchive) {
205098
- const index = await readSessionIndex$1(this.homeDir, this.sessionsDir);
205099
- const sessions = [];
205100
- for (const entry of index.values()) {
205101
- if (!await isDirectory(entry.sessionDir)) continue;
205102
- const summary = await this.summaryFromDir(entry.sessionId, entry.sessionDir, entry.workDir);
205103
- if (!includeArchive && summary.archived === true) continue;
205104
- sessions.push(summary);
205105
- }
205106
- sessions.sort(compareSessionSummary);
205107
- return sessions;
205108
- }
205109
- async summaryFromWorkDirSession(sessionId, workDir, includeArchive) {
205110
- if (!isSafeSessionId(sessionId)) return void 0;
205111
- const sessionDir = this.sessionDirFor({
205112
- id: sessionId,
205113
- workDir
205114
- });
205115
- if (!await isDirectory(sessionDir)) return void 0;
205116
- const summary = await this.summaryFromDir(sessionId, sessionDir, workDir);
205117
- if (!includeArchive && summary.archived === true) return void 0;
205118
- return summary;
205119
- }
205120
- async assertDirectory(id) {
205121
- return (await this.findExistingSessionEntry(id)).sessionDir;
205122
- }
205123
- async findSessionEntry(id) {
205124
- if (!isSafeSessionId(id)) return void 0;
205125
- return (await readSessionIndex$1(this.homeDir, this.sessionsDir)).get(id);
205126
- }
205127
- async findExistingSessionEntry(id) {
205128
- const entry = await this.findSessionEntry(id);
205129
- if (entry !== void 0 && await isDirectory(entry.sessionDir)) return entry;
205130
- throw new KimiError(ErrorCodes.SESSION_NOT_FOUND, `Session "${id}" was not found`, { details: { sessionId: id } });
205131
- }
205132
- async writeForkedState(input, sourceDir, targetDir) {
205133
- const statePath = join$4(targetDir, "state.json");
205134
- let parsed;
205135
- try {
205136
- parsed = JSON.parse(await readFile(statePath, "utf-8"));
205137
- } catch (error) {
205138
- throw new KimiError(ErrorCodes.SESSION_STATE_NOT_FOUND, `Session "${input.sourceId}" state.json was not found`, { cause: error });
205139
- }
205140
- if (!isRecord$10(parsed)) throw new KimiError(ErrorCodes.SESSION_STATE_INVALID, `Session "${input.sourceId}" state.json is invalid`);
205141
- const title = normalizeForkTitle(input.title, parsed["title"]);
205142
- const now = (/* @__PURE__ */ new Date()).toISOString();
205143
- const next = {
205144
- ...parsed,
205145
- createdAt: now,
205146
- updatedAt: now,
205147
- title,
205148
- isCustomTitle: input.title === void 0 ? parsed["isCustomTitle"] === true : true,
205149
- forkedFrom: input.sourceId,
205150
- agents: rewriteAgentHomedirs(parsed["agents"], sourceDir, targetDir),
205151
- custom: forkCustomMetadata(parsed["custom"], input.metadata)
205152
- };
205153
- await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
205154
- return next;
205155
- }
205156
- async summaryFromDir(id, sessionDir, workDir) {
205157
- const dirStat = await stat$2(sessionDir);
205158
- const state = await readOptionalState(sessionDir);
205159
- const [stateInfo, wireInfo, agentsWireMtime] = await Promise.all([
205160
- statIfExists(join$4(sessionDir, "state.json")),
205161
- statIfExists(join$4(sessionDir, "wire.jsonl")),
205162
- latestAgentWireMtime(sessionDir)
205163
- ]);
205164
- return {
205165
- id,
205166
- workDir,
205167
- sessionDir,
205168
- createdAt: timestampOrFallback(dirStat.birthtimeMs, dirStat.ctimeMs),
205169
- updatedAt: Math.max(dirStat.mtimeMs, stateInfo?.mtimeMs ?? 0, wireInfo?.mtimeMs ?? 0, agentsWireMtime ?? 0),
205170
- archived: state?.archived === true,
205171
- title: titleFromState(state),
205172
- lastPrompt: state?.lastPrompt,
205173
- metadata: metadataFromState(state)
205174
- };
205175
- }
205176
- };
205177
- }));
205178
- //#endregion
205179
- //#region ../../packages/agent-core/src/session/store/index.ts
205180
- var init_store = __esmMin((() => {
205181
- init_session_store$1();
205182
- init_session_index();
205183
- init_workdir_key();
205184
- }));
205185
- //#endregion
205186
205800
  //#region ../../packages/kaos/src/errors.ts
205187
205801
  var KaosError, KaosFileExistsError, KaosShellNotFoundError;
205188
205802
  var init_errors$1 = __esmMin((() => {
@@ -206432,9 +207046,10 @@ var init_core_impl = __esmMin((() => {
206432
207046
  init_mcp$1();
206433
207047
  init_session$1();
206434
207048
  init_export();
207049
+ init_skill$4();
206435
207050
  init_provider_manager();
206436
207051
  init_rpc$1();
206437
- init_store();
207052
+ init_store$1();
206438
207053
  init_telemetry();
206439
207054
  init_types$3();
206440
207055
  init_src$1();
@@ -206559,6 +207174,7 @@ var init_core_impl = __esmMin((() => {
206559
207174
  ...session.metadata,
206560
207175
  createdAt: new Date(summary.createdAt).toISOString(),
206561
207176
  updatedAt: new Date(summary.updatedAt).toISOString(),
207177
+ workDir,
206562
207178
  ...summary.title !== void 0 ? {
206563
207179
  title: summary.title,
206564
207180
  isCustomTitle: true
@@ -206857,6 +207473,34 @@ var init_core_impl = __esmMin((() => {
206857
207473
  listSkills({ sessionId, ...payload }) {
206858
207474
  return this.sessionApi(sessionId).listSkills(payload);
206859
207475
  }
207476
+ /**
207477
+ * List the skills available for a workspace working directory without
207478
+ * requiring a session. Mirrors `Session.loadSkills` exactly (same roots,
207479
+ * same discovery order, same built-ins) so the result matches what a new
207480
+ * session created in `workDir` would see. Used to populate the composer
207481
+ * skill menu before a session exists.
207482
+ */
207483
+ async listWorkspaceSkills({ workDir }) {
207484
+ const cwd = requiredWorkDir("listWorkspaceSkills", workDir);
207485
+ await this.pluginsReady;
207486
+ const skills = this.resolveSessionSkillConfig(this.reloadProviderManager());
207487
+ const roots = await resolveSkillRoots({
207488
+ paths: {
207489
+ userHomeDir: skills.userHomeDir ?? this.userHomeDir,
207490
+ brandHomeDir: skills.brandHomeDir ?? this.homeDir,
207491
+ workDir: cwd
207492
+ },
207493
+ explicitDirs: skills.explicitDirs,
207494
+ extraDirs: skills.extraDirs,
207495
+ pluginSkillRoots: skills.pluginSkillRoots,
207496
+ mergeAllAvailableSkills: skills.mergeAllAvailableSkills,
207497
+ builtinDir: skills.builtinDir
207498
+ });
207499
+ const registry = new SessionSkillRegistry({});
207500
+ await registry.loadRoots(roots);
207501
+ registerBuiltinSkills(registry);
207502
+ return registry.listSkills().map(summarizeSkill);
207503
+ }
206860
207504
  listPluginCommands({ sessionId, ...payload }) {
206861
207505
  return this.sessionApi(sessionId).listPluginCommands(payload);
206862
207506
  }
@@ -208407,30 +209051,53 @@ function toBrokerRequest(req, params) {
208407
209051
  * Protocol REST response body → in-process SDK `QuestionResponse` (with
208408
209052
  * `answers` flattened to `Record<string, string | true>`).
208409
209053
  *
208410
- * Normalization rules from SCHEMAS §6.4:
208411
- * - single → option_id
208412
- * - multi → option_ids.join(',')
209054
+ * The wire keeps synthesized ids (`q_<idx>` / `opt_<q>_<o>`) so clients can
209055
+ * answer unambiguously, but the flattened record is what the ask-user tool
209056
+ * feeds back to the model — so ids are translated back to text here using
209057
+ * the original broker `request`:
209058
+ * - key → the question's text (falls back to the raw qid
209059
+ * when the request is unavailable or the qid is
209060
+ * unknown — stale client, defensive)
209061
+ * - single → option label
209062
+ * - multi → labels.join(', ')
208413
209063
  * - other → text
208414
- * - multi_with_other → [...option_ids, other_text].join(',')
209064
+ * - multi_with_other → [...labels, other_text].join(', ')
208415
209065
  * - skipped → OMIT entry
209066
+ *
209067
+ * Multi-select joins use `', '` to match what the TUI reverse-RPC path
209068
+ * already emits, so the model sees one format regardless of which client
209069
+ * answered.
209070
+ *
209071
+ * Unknown qids and option ids — including ids that belong to a DIFFERENT
209072
+ * question than the one being answered — are kept verbatim rather than
209073
+ * resolved or dropped: translating a cross-question id would hand the model
209074
+ * a plausible-looking label that was never offered for that question, while
209075
+ * the raw id stays diagnosable.
208416
209076
  */
208417
- function toAgentCoreResponse(resp) {
209077
+ function toAgentCoreResponse(resp, request) {
209078
+ const itemsById = /* @__PURE__ */ new Map();
209079
+ for (const item of request?.questions ?? []) itemsById.set(item.id, item);
208418
209080
  const flattened = {};
208419
- for (const [qid, ans] of Object.entries(resp.answers)) switch (ans.kind) {
208420
- case "single":
208421
- flattened[qid] = ans.option_id;
208422
- break;
208423
- case "multi":
208424
- flattened[qid] = ans.option_ids.join(",");
208425
- break;
208426
- case "other":
208427
- flattened[qid] = ans.text;
208428
- break;
208429
- case "multi_with_other":
208430
- flattened[qid] = [...ans.option_ids, ans.other_text].join(",");
208431
- break;
208432
- case "skipped": break;
208433
- default:
209081
+ for (const [qid, ans] of Object.entries(resp.answers)) {
209082
+ const item = itemsById.get(qid);
209083
+ const key = item?.question ?? qid;
209084
+ const optionText = (id) => item?.options.find((o) => o.id === id)?.label ?? id;
209085
+ switch (ans.kind) {
209086
+ case "single":
209087
+ flattened[key] = optionText(ans.option_id);
209088
+ break;
209089
+ case "multi":
209090
+ flattened[key] = ans.option_ids.map(optionText).join(", ");
209091
+ break;
209092
+ case "other":
209093
+ flattened[key] = ans.text;
209094
+ break;
209095
+ case "multi_with_other":
209096
+ flattened[key] = [...ans.option_ids.map(optionText), ans.other_text].join(", ");
209097
+ break;
209098
+ case "skipped": break;
209099
+ default:
209100
+ }
208434
209101
  }
208435
209102
  const out = { answers: flattened };
208436
209103
  if (resp.method !== void 0) {
@@ -209087,7 +209754,7 @@ function toProtocolSession(summary, meta) {
209087
209754
  var ISessionService, SessionUndoUnavailableError, SessionNotFoundError;
209088
209755
  var init_session = __esmMin((() => {
209089
209756
  init_di();
209090
- init_store();
209757
+ init_store$1();
209091
209758
  init_src$3();
209092
209759
  ISessionService = createDecorator("sessionService");
209093
209760
  SessionUndoUnavailableError = class extends Error {
@@ -212483,7 +213150,7 @@ var WORKSPACE_REGISTRY_FILE, WORKSPACE_REGISTRY_VERSION, WorkspaceRegistryServic
212483
213150
  var init_workspaceRegistryService = __esmMin((() => {
212484
213151
  init_dist$7();
212485
213152
  init_di();
212486
- init_store();
213153
+ init_store$1();
212487
213154
  init_session_index();
212488
213155
  init_environment();
212489
213156
  init_event();
@@ -215595,6 +216262,9 @@ var init_skillService = __esmMin((() => {
215595
216262
  await this._requireLoadedSession(sessionId);
215596
216263
  return (await this.core.rpc.listSkills({ sessionId })).map(toProtocolSkill);
215597
216264
  }
216265
+ async listForWorkDir(workDir) {
216266
+ return (await this.core.rpc.listWorkspaceSkills({ workDir })).map(toProtocolSkill);
216267
+ }
215598
216268
  async activate(sessionId, skillName, args) {
215599
216269
  await this._requireLoadedSession(sessionId);
215600
216270
  try {
@@ -217590,6 +218260,7 @@ function capabilityToStrings(capability) {
217590
218260
  if (capability.audio_in) caps.push("audio_in");
217591
218261
  if (capability.thinking) caps.push("thinking");
217592
218262
  if (capability.tool_use) caps.push("tool_use");
218263
+ if (capability.select_tools === true) caps.push("select_tools");
217593
218264
  return caps.length > 0 ? caps : void 0;
217594
218265
  }
217595
218266
  /** Builds a kimi-code model alias from a normalized catalog model. */
@@ -217648,6 +218319,7 @@ const MAX_QUEUE_SIZE = 1e3;
217648
218319
  var TelemetryClient = class {
217649
218320
  queue = [];
217650
218321
  sink = null;
218322
+ systemMetricsCollector = null;
217651
218323
  deviceId = null;
217652
218324
  sessionId = null;
217653
218325
  disabled = false;
@@ -217658,6 +218330,10 @@ var TelemetryClient = class {
217658
218330
  withContext(input) {
217659
218331
  return new ScopedTelemetryClient(this, input);
217660
218332
  }
218333
+ setSystemMetricsCollector(collector) {
218334
+ if (this.systemMetricsCollector !== null && this.systemMetricsCollector !== collector) this.systemMetricsCollector.stop();
218335
+ this.systemMetricsCollector = collector;
218336
+ }
217661
218337
  attachSink(sink) {
217662
218338
  if (this.sink !== null && this.sink !== sink) {
217663
218339
  this.sink.stopPeriodicFlush();
@@ -217675,6 +218351,8 @@ var TelemetryClient = class {
217675
218351
  disable() {
217676
218352
  this.disabled = true;
217677
218353
  this.queue = [];
218354
+ this.systemMetricsCollector?.stop();
218355
+ this.systemMetricsCollector = null;
217678
218356
  if (this.sink !== null) {
217679
218357
  this.sink.stopPeriodicFlush();
217680
218358
  this.sink.clearBuffer();
@@ -217718,6 +218396,8 @@ var TelemetryClient = class {
217718
218396
  this.sink?.flushSync();
217719
218397
  }
217720
218398
  async shutdown(options = {}) {
218399
+ this.systemMetricsCollector?.stop();
218400
+ this.systemMetricsCollector = null;
217721
218401
  const sink = this.sink;
217722
218402
  if (sink === null) return;
217723
218403
  sink.stopPeriodicFlush();
@@ -217740,6 +218420,8 @@ var TelemetryClient = class {
217740
218420
  }
217741
218421
  resetForTests() {
217742
218422
  this.sink?.stopPeriodicFlush();
218423
+ this.systemMetricsCollector?.stop();
218424
+ this.systemMetricsCollector = null;
217743
218425
  this.queue = [];
217744
218426
  this.sink = null;
217745
218427
  this.deviceId = null;
@@ -217761,6 +218443,9 @@ var ScopedTelemetryClient = class ScopedTelemetryClient extends TelemetryClient
217761
218443
  withContext(input) {
217762
218444
  return new ScopedTelemetryClient(this.parent, mergeContext(this.context, input));
217763
218445
  }
218446
+ setSystemMetricsCollector(collector) {
218447
+ this.parent.setSystemMetricsCollector(collector);
218448
+ }
217764
218449
  attachSink(sink) {
217765
218450
  this.parent.attachSink(sink);
217766
218451
  }
@@ -217908,6 +218593,88 @@ function setPrimitive(target, key, value) {
217908
218593
  if (typeof value === "string" && value.length === 0) return;
217909
218594
  target[key] = value;
217910
218595
  }
218596
+ //#endregion
218597
+ //#region ../../packages/telemetry/src/systemMetrics.ts
218598
+ const DEFAULT_INTERVAL_MS = 3e4;
218599
+ const DEFAULT_WARMUP_SAMPLE_MS = 1500;
218600
+ const SYSTEM_METRICS_EVENT = "system_metrics";
218601
+ var SystemMetricsCollector = class {
218602
+ client;
218603
+ intervalMs;
218604
+ warmupSampleMs;
218605
+ intervalTimer = null;
218606
+ warmupTimer = null;
218607
+ previousCpuUsage = process.cpuUsage();
218608
+ previousHrtime = process.hrtime.bigint();
218609
+ processStartedAtSeconds = Math.floor(Date.now() / 1e3 - process.uptime());
218610
+ constructor(options) {
218611
+ this.client = options.client;
218612
+ this.intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
218613
+ this.warmupSampleMs = options.warmupSampleMs === void 0 ? DEFAULT_WARMUP_SAMPLE_MS : options.warmupSampleMs;
218614
+ }
218615
+ start() {
218616
+ if (this.intervalTimer !== null) return;
218617
+ if (this.warmupSampleMs !== null && this.warmupSampleMs > 0) {
218618
+ this.warmupTimer = setTimeout(() => {
218619
+ this.warmupTimer = null;
218620
+ this.sampleSafely();
218621
+ }, this.warmupSampleMs);
218622
+ this.warmupTimer.unref?.();
218623
+ }
218624
+ this.intervalTimer = setInterval(() => {
218625
+ this.sampleSafely();
218626
+ }, this.intervalMs);
218627
+ this.intervalTimer.unref?.();
218628
+ }
218629
+ stop() {
218630
+ if (this.warmupTimer !== null) {
218631
+ clearTimeout(this.warmupTimer);
218632
+ this.warmupTimer = null;
218633
+ }
218634
+ if (this.intervalTimer !== null) {
218635
+ clearInterval(this.intervalTimer);
218636
+ this.intervalTimer = null;
218637
+ }
218638
+ }
218639
+ sampleSafely() {
218640
+ try {
218641
+ this.sample();
218642
+ } catch {
218643
+ this.stop();
218644
+ }
218645
+ }
218646
+ sample() {
218647
+ const now = process.hrtime.bigint();
218648
+ const elapsedUs = Number(now - this.previousHrtime) / 1e3;
218649
+ const cpu = process.cpuUsage(this.previousCpuUsage);
218650
+ this.previousCpuUsage = process.cpuUsage();
218651
+ this.previousHrtime = now;
218652
+ const mem = process.memoryUsage();
218653
+ const constrainedMemory = getConstrainedMemoryBytes();
218654
+ this.client.track(SYSTEM_METRICS_EVENT, {
218655
+ process_started_at: this.processStartedAtSeconds,
218656
+ process_uptime_ms: Math.round(process.uptime() * 1e3),
218657
+ rss_bytes: mem.rss,
218658
+ heap_used_bytes: mem.heapUsed,
218659
+ heap_total_bytes: mem.heapTotal,
218660
+ external_bytes: mem.external,
218661
+ array_buffers_bytes: mem.arrayBuffers,
218662
+ constrained_memory_bytes: constrainedMemory,
218663
+ cpu_user_us: cpu.user,
218664
+ cpu_system_us: cpu.system,
218665
+ cpu_elapsed_us: Math.round(elapsedUs),
218666
+ load_avg_1m: loadavg()[0],
218667
+ free_mem_bytes: freemem(),
218668
+ total_mem_bytes: totalmem(),
218669
+ cpu_count: cpus().length
218670
+ });
218671
+ }
218672
+ };
218673
+ function getConstrainedMemoryBytes() {
218674
+ if (typeof process.constrainedMemory !== "function") return void 0;
218675
+ const value = process.constrainedMemory();
218676
+ return Number.isFinite(value) ? value : void 0;
218677
+ }
217911
218678
  const SERVER_EVENT_PREFIX = "kfc_";
217912
218679
  const USER_ID_PREFIX = "kfc_device_id_";
217913
218680
  const RETRY_BACKOFFS_MS = [
@@ -218206,6 +218973,9 @@ function initializeTelemetry(options) {
218206
218973
  });
218207
218974
  client.attachSink(sink);
218208
218975
  sink.startPeriodicFlush();
218976
+ const systemMetricsCollector = new SystemMetricsCollector({ client });
218977
+ client.setSystemMetricsCollector(systemMetricsCollector);
218978
+ systemMetricsCollector.start();
218209
218979
  sink.retryDiskEvents().catch(() => {});
218210
218980
  }
218211
218981
  //#endregion
@@ -218602,7 +219372,7 @@ async function classifySessionDir(sessionDir) {
218602
219372
  }
218603
219373
  //#endregion
218604
219374
  //#region ../../packages/migration-legacy/src/sessions/workdir-bucket.ts
218605
- init_store();
219375
+ init_store$1();
218606
219376
  /**
218607
219377
  * Bucket directory name `wd_<slug>_<hash12>` for a workdir path.
218608
219378
  *
@@ -220229,10 +220999,12 @@ async function walkDirectoryWithFd(baseDir, fdPath, query, maxResults, signal) {
220229
220999
  var CombinedAutocompleteProvider = class {
220230
221000
  commands;
220231
221001
  basePath;
221002
+ additionalBasePaths;
220232
221003
  fdPath;
220233
- constructor(commands = [], basePath, fdPath = null) {
221004
+ constructor(commands = [], basePath, fdPath = null, additionalBasePaths = []) {
220234
221005
  this.commands = commands;
220235
221006
  this.basePath = basePath;
221007
+ this.additionalBasePaths = additionalBasePaths.map((p) => toDisplayPath(p));
220236
221008
  this.fdPath = fdPath;
220237
221009
  }
220238
221010
  async getSuggestions(lines, cursorLine, cursorCol, options) {
@@ -220379,25 +221151,41 @@ var CombinedAutocompleteProvider = class {
220379
221151
  } else if (path === "~") return homedir();
220380
221152
  return path;
220381
221153
  }
221154
+ searchRoots() {
221155
+ const seen = /* @__PURE__ */ new Set();
221156
+ const roots = [];
221157
+ for (const root of [this.basePath, ...this.additionalBasePaths]) {
221158
+ const key = toDisplayPath(root);
221159
+ if (seen.has(key)) continue;
221160
+ seen.add(key);
221161
+ roots.push(root);
221162
+ }
221163
+ return roots;
221164
+ }
220382
221165
  resolveScopedFuzzyQuery(rawQuery) {
220383
221166
  const normalizedQuery = toDisplayPath(rawQuery);
220384
221167
  const slashIndex = normalizedQuery.lastIndexOf("/");
220385
221168
  if (slashIndex === -1) return null;
220386
221169
  const displayBase = normalizedQuery.slice(0, slashIndex + 1);
220387
221170
  const query = normalizedQuery.slice(slashIndex + 1);
220388
- let baseDir;
220389
- if (displayBase.startsWith("~/")) baseDir = this.expandHomePath(displayBase);
220390
- else if (displayBase.startsWith("/")) baseDir = displayBase;
220391
- else baseDir = join$1(this.basePath, displayBase);
220392
- try {
220393
- if (!statSync$1(baseDir).isDirectory()) return null;
220394
- } catch {
220395
- return null;
221171
+ if (displayBase.startsWith("~/") || displayBase.startsWith("/")) {
221172
+ const baseDir = displayBase.startsWith("~/") ? this.expandHomePath(displayBase) : displayBase;
221173
+ try {
221174
+ if (!statSync$1(baseDir).isDirectory()) return null;
221175
+ } catch {
221176
+ return null;
221177
+ }
221178
+ return {
221179
+ kind: "absolute",
221180
+ baseDir,
221181
+ displayBase,
221182
+ query
221183
+ };
220396
221184
  }
220397
221185
  return {
220398
- baseDir,
220399
- query,
220400
- displayBase
221186
+ kind: "relative",
221187
+ displayBase,
221188
+ query
220401
221189
  };
220402
221190
  }
220403
221191
  scopedPathForDisplay(displayBase, relativePath) {
@@ -220487,32 +221275,90 @@ var CombinedAutocompleteProvider = class {
220487
221275
  return score;
220488
221276
  }
220489
221277
  async getFuzzyFileSuggestions(query, options) {
220490
- if (!this.fdPath || options.signal.aborted) return [];
221278
+ const fdPath = this.fdPath;
221279
+ if (!fdPath || options.signal.aborted) return [];
220491
221280
  try {
220492
- const scopedQuery = this.resolveScopedFuzzyQuery(query);
220493
- const fdBaseDir = scopedQuery?.baseDir ?? this.basePath;
220494
- const fdQuery = scopedQuery?.query ?? query;
220495
- const entries = await walkDirectoryWithFd(fdBaseDir, this.fdPath, fdQuery, 100, options.signal);
221281
+ const scoped = this.resolveScopedFuzzyQuery(query);
221282
+ const targets = [];
221283
+ const pushFullPathTarget = (root) => {
221284
+ targets.push({
221285
+ baseDir: root,
221286
+ pathRoot: root,
221287
+ displayBase: "",
221288
+ fdQuery: query,
221289
+ isAdditional: root !== this.basePath,
221290
+ absolute: false
221291
+ });
221292
+ };
221293
+ if (scoped?.kind === "absolute") targets.push({
221294
+ baseDir: scoped.baseDir,
221295
+ pathRoot: scoped.baseDir,
221296
+ displayBase: scoped.displayBase,
221297
+ fdQuery: scoped.query,
221298
+ isAdditional: false,
221299
+ absolute: true
221300
+ });
221301
+ else if (scoped?.kind === "relative") for (const root of this.searchRoots()) {
221302
+ const baseDir = join$1(root, scoped.displayBase);
221303
+ let isDir = false;
221304
+ try {
221305
+ isDir = statSync$1(baseDir).isDirectory();
221306
+ } catch {
221307
+ isDir = false;
221308
+ }
221309
+ if (isDir) targets.push({
221310
+ baseDir,
221311
+ pathRoot: root,
221312
+ displayBase: scoped.displayBase,
221313
+ fdQuery: scoped.query,
221314
+ isAdditional: root !== this.basePath,
221315
+ absolute: false
221316
+ });
221317
+ else pushFullPathTarget(root);
221318
+ }
221319
+ else for (const root of this.searchRoots()) pushFullPathTarget(root);
221320
+ if (targets.length === 0) return [];
221321
+ const perRoot = await Promise.all(targets.map(async (target) => {
221322
+ return (await walkDirectoryWithFd(target.baseDir, fdPath, target.fdQuery, 100, options.signal)).map((entry) => ({
221323
+ entry,
221324
+ target
221325
+ }));
221326
+ }));
220496
221327
  if (options.signal.aborted) return [];
220497
- const scoredEntries = entries.map((entry) => ({
220498
- ...entry,
220499
- score: fdQuery ? this.scoreEntry(entry.path, fdQuery, entry.isDirectory) : 1
220500
- })).filter((entry) => entry.score > 0);
220501
- scoredEntries.sort((a, b) => b.score - a.score);
220502
- const topEntries = scoredEntries.slice(0, 20);
221328
+ const bestByAbs = /* @__PURE__ */ new Map();
221329
+ for (const group of perRoot) for (const { entry, target } of group) {
221330
+ const score = target.fdQuery ? this.scoreEntry(entry.path, target.fdQuery, entry.isDirectory) : 1;
221331
+ if (score <= 0) continue;
221332
+ const pathWithoutSlash = entry.isDirectory ? entry.path.slice(0, -1) : entry.path;
221333
+ const absPath = target.absolute ? toDisplayPath(join$1(target.displayBase, pathWithoutSlash)) : toDisplayPath(join$1(target.pathRoot, target.displayBase, pathWithoutSlash));
221334
+ const existing = bestByAbs.get(absPath);
221335
+ if (!existing || score > existing.score) bestByAbs.set(absPath, {
221336
+ path: entry.path,
221337
+ isDirectory: entry.isDirectory,
221338
+ score,
221339
+ target,
221340
+ absPath
221341
+ });
221342
+ }
221343
+ const scored = [...bestByAbs.values()];
221344
+ scored.sort((a, b) => b.score - a.score);
221345
+ const topEntries = scored.slice(0, 20);
220503
221346
  const suggestions = [];
220504
- for (const { path: entryPath, isDirectory } of topEntries) {
220505
- const pathWithoutSlash = isDirectory ? entryPath.slice(0, -1) : entryPath;
220506
- const displayPath = scopedQuery ? this.scopedPathForDisplay(scopedQuery.displayBase, pathWithoutSlash) : pathWithoutSlash;
221347
+ for (const item of topEntries) {
221348
+ const pathWithoutSlash = item.isDirectory ? item.path.slice(0, -1) : item.path;
220507
221349
  const entryName = basename$1(pathWithoutSlash);
220508
- const value = buildCompletionValue(isDirectory ? `${displayPath}/` : displayPath, {
220509
- isDirectory,
221350
+ let displayPath;
221351
+ if (item.target.absolute) displayPath = this.scopedPathForDisplay(item.target.displayBase, pathWithoutSlash);
221352
+ else if (item.target.isAdditional) displayPath = item.absPath;
221353
+ else displayPath = item.target.displayBase ? this.scopedPathForDisplay(item.target.displayBase, pathWithoutSlash) : pathWithoutSlash;
221354
+ const value = buildCompletionValue(item.isDirectory ? `${displayPath}/` : displayPath, {
221355
+ isDirectory: item.isDirectory,
220510
221356
  isAtPrefix: true,
220511
221357
  isQuotedPrefix: options.isQuotedPrefix
220512
221358
  });
220513
221359
  suggestions.push({
220514
221360
  value,
220515
- label: entryName + (isDirectory ? "/" : ""),
221361
+ label: entryName + (item.isDirectory ? "/" : ""),
220516
221362
  description: displayPath
220517
221363
  });
220518
221364
  }
@@ -241057,6 +241903,7 @@ function initializeCliTelemetry(options) {
241057
241903
  version: options.version,
241058
241904
  uiMode: options.uiMode,
241059
241905
  model: options.model ?? options.config.defaultModel,
241906
+ sessionId: options.sessionId,
241060
241907
  getAccessToken: async () => await options.harness.auth.getCachedAccessToken("managed:kimi-code") ?? null
241061
241908
  });
241062
241909
  if (options.bootstrap.firstLaunch) options.harness.track("first_launch");
@@ -275394,6 +276241,15 @@ let QuestionService = class QuestionService extends Disposable {
275394
276241
  isPending(questionId) {
275395
276242
  return this._pending.has(questionId);
275396
276243
  }
276244
+ /**
276245
+ * Protocol request for a still-pending question. The resolve route needs it
276246
+ * to translate wire ids back to question text / option labels before the
276247
+ * response reaches the SDK; must be read BEFORE `resolve()` settles (and
276248
+ * thereby drops) the pending entry.
276249
+ */
276250
+ getPendingRequest(questionId) {
276251
+ return this._pending.get(questionId)?.protocolRequest;
276252
+ }
275397
276253
  listPending(sessionId) {
275398
276254
  return Array.from(this._pending.values()).filter((p) => p.sessionId === sessionId).map((p) => p.protocolRequest);
275399
276255
  }
@@ -275536,7 +276392,7 @@ function registerQuestionsRoutes(app, ix) {
275536
276392
  return;
275537
276393
  }
275538
276394
  const body = bodyParse.data;
275539
- const inProc = toAgentCoreResponse(body);
276395
+ const inProc = toAgentCoreResponse(body, broker.getPendingRequest(questionId));
275540
276396
  broker.resolve(questionId, inProc);
275541
276397
  broker.markResolved(questionId);
275542
276398
  const result = {
@@ -275548,6 +276404,69 @@ function registerQuestionsRoutes(app, ix) {
275548
276404
  app.post(route.path, route.options, route.handler);
275549
276405
  }
275550
276406
  //#endregion
276407
+ //#region ../../packages/server/src/lib/sessionArchive.ts
276408
+ init_src();
276409
+ async function restoreArchivedSession(homeDir, sessionId) {
276410
+ const sessionDir = await findSessionDir$1(homeDir, sessionId);
276411
+ if (sessionDir === void 0) throw new SessionNotFoundError(sessionId);
276412
+ const statePath = join(sessionDir, "state.json");
276413
+ let parsed;
276414
+ try {
276415
+ parsed = JSON.parse(await readFile(statePath, "utf-8"));
276416
+ } catch {
276417
+ throw new SessionNotFoundError(sessionId);
276418
+ }
276419
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new SessionNotFoundError(sessionId);
276420
+ const next = {
276421
+ ...parsed,
276422
+ archived: false,
276423
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
276424
+ };
276425
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
276426
+ }
276427
+ async function findSessionDir$1(homeDir, sessionId) {
276428
+ const indexPath = join(homeDir, "session_index.jsonl");
276429
+ let raw;
276430
+ try {
276431
+ raw = await readFile(indexPath, "utf-8");
276432
+ } catch {
276433
+ return;
276434
+ }
276435
+ const sessionsDir = join(homeDir, "sessions");
276436
+ let found;
276437
+ for (const line of raw.split(/\r?\n/)) {
276438
+ const trimmed = line.trim();
276439
+ if (trimmed === "") continue;
276440
+ const entry = parseIndexLine(trimmed);
276441
+ if (entry === void 0 || entry.sessionId !== sessionId) continue;
276442
+ const sessionDir = resolve(entry.sessionDir);
276443
+ if (!isAbsolute(entry.sessionDir)) continue;
276444
+ if (!isPathInside(sessionsDir, sessionDir)) continue;
276445
+ if (basename(sessionDir) !== entry.sessionId) continue;
276446
+ found = sessionDir;
276447
+ }
276448
+ return found;
276449
+ }
276450
+ function parseIndexLine(line) {
276451
+ try {
276452
+ const parsed = JSON.parse(line);
276453
+ if (typeof parsed !== "object" || parsed === null) return void 0;
276454
+ const entry = parsed;
276455
+ if (typeof entry.sessionId !== "string" || typeof entry.sessionDir !== "string" || typeof entry.workDir !== "string") return;
276456
+ return {
276457
+ sessionId: entry.sessionId,
276458
+ sessionDir: entry.sessionDir,
276459
+ workDir: entry.workDir
276460
+ };
276461
+ } catch {
276462
+ return;
276463
+ }
276464
+ }
276465
+ function isPathInside(parent, child) {
276466
+ const rel = relative(resolve(parent), resolve(child));
276467
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
276468
+ }
276469
+ //#endregion
275551
276470
  //#region ../../packages/server/src/routes/sessions.ts
275552
276471
  init_src$3();
275553
276472
  init_src();
@@ -275564,6 +276483,7 @@ const sessionsListQueryCoercion = object({
275564
276483
  status: sessionStatusSchema.optional(),
275565
276484
  include_archive: booleanQueryParam,
275566
276485
  exclude_empty: booleanQueryParam,
276486
+ archived_only: booleanQueryParam,
275567
276487
  workspace_id: workspaceIdSchema.optional()
275568
276488
  }).superRefine((value, ctx) => {
275569
276489
  if (value.before_id !== void 0 && value.after_id !== void 0) ctx.addIssue({
@@ -275572,6 +276492,12 @@ const sessionsListQueryCoercion = object({
275572
276492
  path: ["before_id"],
275573
276493
  params: { code: ErrorCode$1.VALIDATION_FAILED }
275574
276494
  });
276495
+ if (value.archived_only === true && value.include_archive === true) ctx.addIssue({
276496
+ code: "custom",
276497
+ message: "archived_only and include_archive are mutually exclusive",
276498
+ path: ["archived_only"],
276499
+ params: { code: ErrorCode$1.VALIDATION_FAILED }
276500
+ });
275575
276501
  });
275576
276502
  const sessionChildrenListQueryCoercion = object({
275577
276503
  before_id: string().min(1).optional(),
@@ -275615,6 +276541,47 @@ function headerString(headers, key) {
275615
276541
  const trimmed = raw.trim();
275616
276542
  return trimmed.length === 0 ? void 0 : trimmed;
275617
276543
  }
276544
+ const DEFAULT_SESSION_LIST_PAGE_SIZE = 20;
276545
+ const MAX_SESSION_LIST_PAGE_SIZE = 100;
276546
+ function normalizeSessionListPageSize(cursor) {
276547
+ const requested = cursor.page_size ?? DEFAULT_SESSION_LIST_PAGE_SIZE;
276548
+ return Math.min(Math.max(requested, 1), MAX_SESSION_LIST_PAGE_SIZE);
276549
+ }
276550
+ async function listSessionsWithRouteFilter(fetchPage, baseQuery, cursor, predicate) {
276551
+ const targetSize = normalizeSessionListPageSize(cursor);
276552
+ const forward = cursor.after_id !== void 0 && cursor.before_id === void 0;
276553
+ const matches = [];
276554
+ let beforeId = forward ? void 0 : cursor.before_id;
276555
+ let afterId = forward ? cursor.after_id : void 0;
276556
+ let coreHasMore = true;
276557
+ while (matches.length <= targetSize && coreHasMore) {
276558
+ const page = await fetchPage({
276559
+ ...baseQuery,
276560
+ before_id: beforeId,
276561
+ after_id: afterId,
276562
+ page_size: MAX_SESSION_LIST_PAGE_SIZE
276563
+ });
276564
+ if (page.items.length === 0) break;
276565
+ let hitPivot = false;
276566
+ for (const session of page.items) {
276567
+ if (forward && session.id === afterId) {
276568
+ hitPivot = true;
276569
+ break;
276570
+ }
276571
+ if (predicate(session)) matches.push(session);
276572
+ }
276573
+ coreHasMore = page.has_more && !hitPivot;
276574
+ if (!coreHasMore) break;
276575
+ const nextBeforeId = page.items[page.items.length - 1]?.id;
276576
+ if (nextBeforeId === void 0 || nextBeforeId === beforeId) break;
276577
+ beforeId = nextBeforeId;
276578
+ afterId = void 0;
276579
+ }
276580
+ return {
276581
+ items: matches.slice(0, targetSize),
276582
+ has_more: matches.length > targetSize
276583
+ };
276584
+ }
275618
276585
  function registerSessionsRoutes(app, ix) {
275619
276586
  const createRoute = defineRoute({
275620
276587
  method: "POST",
@@ -275694,15 +276661,12 @@ function registerSessionsRoutes(app, ix) {
275694
276661
  }, async (req, reply) => {
275695
276662
  try {
275696
276663
  const raw = req.query;
275697
- const baseQuery = {
275698
- before_id: raw.before_id,
275699
- after_id: raw.after_id,
275700
- page_size: raw.page_size,
275701
- status: raw.status,
275702
- includeArchive: raw.include_archive,
276664
+ const archivedOnly = raw.archived_only === true;
276665
+ const status = raw.status;
276666
+ let baseQuery = {
276667
+ includeArchive: archivedOnly ? true : raw.include_archive,
275703
276668
  excludeEmpty: raw.exclude_empty
275704
276669
  };
275705
- let query;
275706
276670
  if (raw.workspace_id !== void 0) {
275707
276671
  const registry = ix.invokeFunction((a) => a.get(IWorkspaceRegistry));
275708
276672
  let root;
@@ -275715,12 +276679,23 @@ function registerSessionsRoutes(app, ix) {
275715
276679
  }
275716
276680
  throw err;
275717
276681
  }
275718
- query = {
276682
+ baseQuery = {
275719
276683
  ...baseQuery,
275720
276684
  workDir: root
275721
276685
  };
275722
- } else query = baseQuery;
275723
- const page = await ix.invokeFunction((a) => a.get(ISessionService).list(query));
276686
+ }
276687
+ if (archivedOnly) {
276688
+ const page = await listSessionsWithRouteFilter((query) => ix.invokeFunction((a) => a.get(ISessionService).list(query)), baseQuery, raw, (session) => session.archived === true && (status === void 0 || session.status === status));
276689
+ reply.send(okEnvelope(page, req.id));
276690
+ return;
276691
+ }
276692
+ const page = await ix.invokeFunction((a) => a.get(ISessionService).list({
276693
+ ...baseQuery,
276694
+ before_id: raw.before_id,
276695
+ after_id: raw.after_id,
276696
+ page_size: raw.page_size,
276697
+ status
276698
+ }));
275724
276699
  reply.send(okEnvelope(page, req.id));
275725
276700
  } catch (err) {
275726
276701
  sendMappedError$6(reply, req.id, err);
@@ -275836,7 +276811,8 @@ function registerSessionsRoutes(app, ix) {
275836
276811
  "undo",
275837
276812
  "abort",
275838
276813
  "btw",
275839
- "archive"
276814
+ "archive",
276815
+ "restore"
275840
276816
  ],
275841
276817
  resourceLabel: "session"
275842
276818
  });
@@ -275875,6 +276851,13 @@ function registerSessionsRoutes(app, ix) {
275875
276851
  reply.send(okEnvelope(result, req.id));
275876
276852
  return;
275877
276853
  }
276854
+ if (parsed.action === "restore") {
276855
+ const homeDir = ix.invokeFunction((a) => a.get(IEnvironmentService)).homeDir;
276856
+ await restoreArchivedSession(homeDir, parsed.id);
276857
+ const session = await ix.invokeFunction((a) => a.get(ISessionService).get(parsed.id));
276858
+ reply.send(okEnvelope(session, req.id));
276859
+ return;
276860
+ }
275878
276861
  const body = undoSessionRequestSchema.parse(req.body);
275879
276862
  const result = await ix.invokeFunction((a) => a.get(ISessionService).undo(parsed.id, body));
275880
276863
  reply.send(okEnvelope(result, req.id));
@@ -276073,6 +277056,29 @@ function registerSkillsRoutes(app, ix) {
276073
277056
  }
276074
277057
  });
276075
277058
  app.get(listSkillsRoute.path, listSkillsRoute.options, listSkillsRoute.handler);
277059
+ const listWorkspaceSkillsRoute = defineRoute({
277060
+ method: "GET",
277061
+ path: "/workspaces/{workspace_id}/skills",
277062
+ params: workspaceIdParamSchema,
277063
+ success: { data: listSkillsResponseSchema },
277064
+ errors: { [ErrorCode$1.WORKSPACE_NOT_FOUND]: {} },
277065
+ description: "List the skills available to a workspace (no session required)",
277066
+ tags: ["skills"],
277067
+ operationId: "listWorkspaceSkills"
277068
+ }, async (req, reply) => {
277069
+ try {
277070
+ const { workspace_id } = req.params;
277071
+ const skills = await ix.invokeFunction((a) => {
277072
+ const registry = a.get(IWorkspaceRegistry);
277073
+ const skillService = a.get(ISkillService);
277074
+ return registry.resolveRoot(workspace_id).then((workDir) => skillService.listForWorkDir(workDir));
277075
+ });
277076
+ reply.send(okEnvelope({ skills }, req.id));
277077
+ } catch (err) {
277078
+ sendMappedError$5(reply, req.id, err);
277079
+ }
277080
+ });
277081
+ app.get(listWorkspaceSkillsRoute.path, listWorkspaceSkillsRoute.options, listWorkspaceSkillsRoute.handler);
276076
277082
  const activateSkillRoute = defineRoute({
276077
277083
  method: "POST",
276078
277084
  path: "/sessions/{session_id}/skills/{tail}",
@@ -276119,6 +277125,10 @@ function registerSkillsRoutes(app, ix) {
276119
277125
  * Map a thrown error to the right envelope. See module header for the table.
276120
277126
  */
276121
277127
  function sendMappedError$5(reply, requestId, err) {
277128
+ if (err instanceof WorkspaceNotFoundError) {
277129
+ reply.send(errEnvelope(ErrorCode$1.WORKSPACE_NOT_FOUND, err.message, requestId));
277130
+ return;
277131
+ }
276122
277132
  if (err instanceof SessionNotFoundError) {
276123
277133
  reply.send(errEnvelope(ErrorCode$1.SESSION_NOT_FOUND, err.message, requestId));
276124
277134
  return;
@@ -276217,7 +277227,7 @@ function loadSnapshotConfig(env = process.env) {
276217
277227
  * `turn.started` events fired before the first snapshot request.
276218
277228
  */
276219
277229
  init_src();
276220
- init_store();
277230
+ init_store$1();
276221
277231
  init_decorateParam();
276222
277232
  init_decorate();
276223
277233
  const MAIN_AGENT_ID$1 = "main";
@@ -287643,6 +288653,12 @@ async function startServer(opts) {
287643
288653
  configPath: opts.coreProcessOptions?.configPath
287644
288654
  })
287645
288655
  };
288656
+ try {
288657
+ const stats = await new SessionStore(envService.homeDir).reindex();
288658
+ pinoLogger.info(stats, "session index rebuilt");
288659
+ } catch (error) {
288660
+ pinoLogger.warn({ err: String(error) }, "session index rebuild failed (best-effort)");
288661
+ }
287646
288662
  const tokenStore = await createTokenStore(envService.homeDir);
287647
288663
  const passwordHash = await resolvePasswordHash(process.env);
287648
288664
  const defaultAuth = createAuthTokenService({
@@ -302394,6 +303410,7 @@ const BLINK_INTERVAL = 500;
302394
303410
  var CompactionComponent = class extends Container {
302395
303411
  ui;
302396
303412
  headerText;
303413
+ instructionText;
302397
303414
  instruction;
302398
303415
  tip;
302399
303416
  blinkOn = true;
@@ -302402,6 +303419,9 @@ var CompactionComponent = class extends Container {
302402
303419
  canceled = false;
302403
303420
  tokensBefore;
302404
303421
  tokensAfter;
303422
+ summary;
303423
+ summaryText;
303424
+ expanded = false;
302405
303425
  constructor(ui, instruction, tip) {
302406
303426
  super();
302407
303427
  this.ui = ui;
@@ -302414,23 +303434,35 @@ var CompactionComponent = class extends Container {
302414
303434
  this.startBlink();
302415
303435
  }
302416
303436
  addInstructionChild() {
302417
- if (this.instruction !== void 0) this.addChild(new Text(currentTheme.dim(` ${this.instruction}`), 0, 0));
303437
+ if (this.instruction !== void 0) {
303438
+ this.instructionText = new Text(currentTheme.dim(` ${this.instruction}`), 0, 0);
303439
+ this.addChild(this.instructionText);
303440
+ }
303441
+ }
303442
+ removeInstructionChild() {
303443
+ if (this.instructionText === void 0) return;
303444
+ const index = this.children.indexOf(this.instructionText);
303445
+ if (index !== -1) this.children.splice(index, 1);
303446
+ this.instructionText = void 0;
302418
303447
  }
302419
303448
  invalidate() {
302420
303449
  this.headerText.setText(this.buildHeader());
302421
- if (this.instruction !== void 0) {
302422
- if (this.children.length > 2) this.children.pop();
302423
- this.addInstructionChild();
302424
- }
303450
+ const expanded = this.expanded;
303451
+ this.removeInstructionChild();
303452
+ if (expanded) this.removeSummaryChild();
303453
+ this.addInstructionChild();
303454
+ if (expanded) this.addSummaryChild();
302425
303455
  super.invalidate();
302426
303456
  }
302427
- markDone(tokensBefore, tokensAfter) {
303457
+ markDone(tokensBefore, tokensAfter, summary) {
302428
303458
  if (this.done || this.canceled) return;
302429
303459
  this.done = true;
302430
303460
  this.tokensBefore = tokensBefore;
302431
303461
  this.tokensAfter = tokensAfter;
303462
+ this.summary = summary;
302432
303463
  this.stopBlink();
302433
303464
  this.headerText.setText(this.buildHeader());
303465
+ if (this.expanded) this.addSummaryChild();
302434
303466
  this.ui?.requestRender();
302435
303467
  }
302436
303468
  markCanceled() {
@@ -302440,11 +303472,31 @@ var CompactionComponent = class extends Container {
302440
303472
  this.headerText.setText(this.buildHeader());
302441
303473
  this.ui?.requestRender();
302442
303474
  }
303475
+ setExpanded(expanded) {
303476
+ if (this.expanded === expanded) return;
303477
+ this.expanded = expanded;
303478
+ if (expanded) this.addSummaryChild();
303479
+ else this.removeSummaryChild();
303480
+ this.headerText.setText(this.buildHeader());
303481
+ this.ui?.requestRender();
303482
+ }
303483
+ addSummaryChild() {
303484
+ if (this.summaryText !== void 0 || this.summary === void 0 || this.summary.length === 0) return;
303485
+ const indentedSummary = this.summary.split("\n").map((line) => ` ${line}`).join("\n");
303486
+ this.summaryText = new Text(currentTheme.dim(indentedSummary), 0, 0);
303487
+ this.addChild(this.summaryText);
303488
+ }
303489
+ removeSummaryChild() {
303490
+ if (this.summaryText === void 0) return;
303491
+ const index = this.children.indexOf(this.summaryText);
303492
+ if (index !== -1) this.children.splice(index, 1);
303493
+ this.summaryText = void 0;
303494
+ }
302443
303495
  dispose() {
302444
303496
  this.stopBlink();
302445
303497
  }
302446
303498
  buildHeader() {
302447
- if (this.done) return `${currentTheme.fg("success", STATUS_BULLET)}${currentTheme.boldFg("success", "Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? currentTheme.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}`;
303499
+ if (this.done) return `${currentTheme.fg("success", STATUS_BULLET)}${currentTheme.boldFg("success", "Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? currentTheme.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}${this.summary !== void 0 && this.summary.length > 0 ? currentTheme.dim(` (Ctrl-O to ${this.expanded ? "hide" : "show"} compaction summary)`) : ""}`;
302448
303500
  if (this.canceled) return `${currentTheme.fg("warning", STATUS_BULLET)}${currentTheme.boldFg("warning", "Compaction cancelled")}`;
302449
303501
  return `${this.blinkOn ? currentTheme.fg("text", STATUS_BULLET) : " "}${currentTheme.boldFg("primary", "Compacting context...")}${this.tip ? currentTheme.fg("textDim", ` · Tip: ${this.tip}`) : ""}`;
302450
303502
  }
@@ -364019,27 +365071,6 @@ function computeDiffLines(oldText, newText, oldStart = 1, newStart = 1, isIncomp
364019
365071
  }
364020
365072
  return result;
364021
365073
  }
364022
- function renderDiffLines(oldText, newText, path, isIncomplete = false, oldStart, newStart, maxLines) {
364023
- const s = makeDiffStyles();
364024
- const changedLines = computeDiffLines(oldText, newText, oldStart ?? 1, newStart ?? 1, isIncomplete).filter((l) => l.kind !== "context");
364025
- const added = changedLines.filter((l) => l.kind === "add").length;
364026
- const removed = changedLines.filter((l) => l.kind === "delete").length;
364027
- const output = [];
364028
- let header = "";
364029
- if (added > 0) header += s.addBold(`+${String(added)} `);
364030
- if (removed > 0) header += s.delBold(`-${String(removed)} `);
364031
- header += path;
364032
- output.push(header);
364033
- const shown = maxLines !== void 0 && maxLines >= 0 && changedLines.length > maxLines ? changedLines.slice(0, maxLines) : changedLines;
364034
- for (const line of shown) {
364035
- const marker = line.kind === "add" ? "+" : "-";
364036
- const color = line.kind === "add" ? s.add : s.del;
364037
- output.push(s.gutter(String(line.lineNum).padStart(4) + " ") + color(marker + " " + line.code));
364038
- }
364039
- const hidden = changedLines.length - shown.length;
364040
- if (hidden > 0) output.push(s.meta(` … ${String(hidden)} more change${hidden > 1 ? "s" : ""} hidden (ctrl+o to expand)`));
364041
- return output;
364042
- }
364043
365074
  function buildClusters(diffLines, contextLines) {
364044
365075
  const changeIndices = [];
364045
365076
  let added = 0;
@@ -364103,7 +365134,7 @@ function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
364103
365134
  const s = makeDiffStyles();
364104
365135
  const contextLines = opts.contextLines ?? 3;
364105
365136
  const maxLines = opts.maxLines;
364106
- const diffLines = computeDiffLines(oldText, newText, 1, 1, opts.isIncomplete ?? false);
365137
+ const diffLines = computeDiffLines(oldText, newText, opts.oldStart ?? 1, opts.newStart ?? 1, opts.isIncomplete ?? false);
364107
365138
  const { clusters, changedCount, addedCount, removedCount } = buildClusters(diffLines, contextLines);
364108
365139
  const output = [];
364109
365140
  let header = "";
@@ -364282,7 +365313,8 @@ var TruncatedOutputComponent = class {
364282
365313
  this.expandHint = options.expandHint ?? true;
364283
365314
  this.tail = options.tail ?? false;
364284
365315
  const cleaned = trimTrailingEmptyLines(output.split("\n")).join("\n");
364285
- this.textComponent = new Text(options.isError ? currentTheme.fg("error", cleaned) : currentTheme.dim(cleaned), this.indent, 0);
365316
+ const successColor = options.color ?? "textDim";
365317
+ this.textComponent = new Text(options.isError ? currentTheme.fg("error", cleaned) : currentTheme.fg(successColor, cleaned), this.indent, 0);
364286
365318
  }
364287
365319
  invalidate() {
364288
365320
  this.textComponent.invalidate();
@@ -364325,8 +365357,8 @@ var ShellExecutionComponent = class extends Container {
364325
365357
  const allLines = command.split("\n");
364326
365358
  const lines = previewLines === void 0 ? allLines : allLines.slice(0, previewLines);
364327
365359
  for (const [i, line] of lines.entries()) {
364328
- const prefix = i === 0 ? "$ " : " ";
364329
- this.addChild(new Text(currentTheme.dim(prefix + line), 2, 0));
365360
+ const text = i === 0 ? currentTheme.fg("shellMode", "$ ") + currentTheme.dim(line) : ` ${currentTheme.dim(line)}`;
365361
+ this.addChild(new Text(text, 2, 0));
364330
365362
  }
364331
365363
  }
364332
365364
  addResultPreview(result, expanded, previewLines, tailOutput, expandHint) {
@@ -364336,16 +365368,14 @@ var ShellExecutionComponent = class extends Container {
364336
365368
  isError: result.is_error ?? false,
364337
365369
  maxLines: previewLines,
364338
365370
  tail: tailOutput,
364339
- expandHint
365371
+ expandHint,
365372
+ color: "textMuted"
364340
365373
  }));
364341
365374
  }
364342
365375
  };
364343
- const shellExecutionResultRenderer = (toolCall, result, ctx) => [new ShellExecutionComponent({
364344
- command: typeof toolCall.args["command"] === "string" ? toolCall.args["command"] : "",
365376
+ const shellExecutionResultRenderer = (_toolCall, result, ctx) => [new ShellExecutionComponent({
364345
365377
  result,
364346
- expanded: ctx.expanded,
364347
- showCommand: ctx.expanded,
364348
- commandPreviewLines: void 0
365378
+ expanded: ctx.expanded
364349
365379
  })];
364350
365380
  //#endregion
364351
365381
  //#region src/tui/components/messages/tool-renderers/goal.ts
@@ -365727,6 +366757,13 @@ var ToolCallComponent = class ToolCallComponent extends Container {
365727
366757
  const tone = isError ? "error" : "primary";
365728
366758
  return `${bullet}${currentTheme.boldFg(tone, label)}`;
365729
366759
  }
366760
+ if (toolCall.name === "Bash") {
366761
+ if (isTruncated) return `${bullet}${currentTheme.fg("error", "Truncated")} ${currentTheme.boldFg("primary", "Bash")}`;
366762
+ const label = isFinished ? "Ran a command" : "Running a command";
366763
+ const tone = isError ? "error" : "primary";
366764
+ const chipStr = isFinished && result !== void 0 ? this.buildHeaderChip(result) : "";
366765
+ return `${bullet}${currentTheme.boldFg(tone, label)}${chipStr}`;
366766
+ }
365730
366767
  const goalHeader = buildGoalToolHeader({
365731
366768
  toolCall,
365732
366769
  result,
@@ -366038,7 +367075,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
366038
367075
  this.buildStreamingPreview(this.toolCall.streamingArguments);
366039
367076
  return;
366040
367077
  }
366041
- const shouldCap = this.result !== void 0 && !this.expanded;
367078
+ const shouldCap = !this.expanded;
366042
367079
  if (name === "Write") {
366043
367080
  const content = str(this.toolCall.args["content"]);
366044
367081
  if (content.length === 0) return;
@@ -366060,7 +367097,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
366060
367097
  ...shouldCap ? { maxLines: 10 } : {}
366061
367098
  });
366062
367099
  for (const line of lines) this.addChild(new Text(line, 2, 0));
366063
- } else if (name === "Bash" && this.result === void 0) {
367100
+ } else if (name === "Bash") {
366064
367101
  const command = str(this.toolCall.args["command"]);
366065
367102
  if (command.length === 0) return;
366066
367103
  this.addChild(new ShellExecutionComponent({
@@ -366111,7 +367148,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
366111
367148
  this.addChild(new ShellExecutionComponent({
366112
367149
  command: cmd,
366113
367150
  showCommand: true,
366114
- commandPreviewLines: 10
367151
+ commandPreviewLines: this.expanded ? void 0 : 10
366115
367152
  }));
366116
367153
  }
366117
367154
  }
@@ -367121,7 +368158,8 @@ async function runPrompt(opts, version, io = {}) {
367121
368158
  config,
367122
368159
  version,
367123
368160
  uiMode: PROMPT_UI_MODE,
367124
- model: telemetryModel
368161
+ model: telemetryModel,
368162
+ sessionId: session.id
367125
368163
  });
367126
368164
  setCrashPhase("runtime");
367127
368165
  const outputFormat = opts.outputFormat ?? "text";
@@ -369524,7 +370562,11 @@ function buildBody(block) {
369524
370562
  return buildFileContentBody(block);
369525
370563
  }
369526
370564
  function buildDiffBody(block) {
369527
- const [header = "", ...rest] = renderDiffLines(block.old_text, block.new_text, block.path, false, block.old_start ?? 1, block.new_start ?? 1);
370565
+ const [header = "", ...rest] = renderDiffLinesClustered(block.old_text, block.new_text, block.path, {
370566
+ contextLines: 3,
370567
+ oldStart: block.old_start ?? 1,
370568
+ newStart: block.new_start ?? 1
370569
+ });
369528
370570
  return {
369529
370571
  lines: rest,
369530
370572
  title: stripLeadingSpace(header)
@@ -369563,7 +370605,7 @@ const DEFAULT_KEYBOARD_SHORTCUTS = [
369563
370605
  },
369564
370606
  {
369565
370607
  keys: "Ctrl-O",
369566
- description: "Toggle tool output expansion"
370608
+ description: "Toggle tool output / compaction summary expansion"
369567
370609
  },
369568
370610
  {
369569
370611
  keys: "Ctrl-T",
@@ -370511,13 +371553,13 @@ const MAX_FALLBACK_SUGGESTIONS = 50;
370511
371553
  /**
370512
371554
  * Kimi wrapper around pi-tui's combined autocomplete provider.
370513
371555
  *
370514
- * File / folder mention behavior uses pi-tui's fd-backed provider when fd is
370515
- * available and only the current working directory is involved. While managed fd
370516
- * is downloading, when it is unavailable, or when the session has additional
370517
- * roots, a small filesystem fallback keeps `@` file and folder completion usable
370518
- * across every root. Ordinary path completion is still handled by pi-tui's
370519
- * readdir-backed path completer. This wrapper also keeps Kimi-specific
370520
- * slash-command guards.
371556
+ * File / folder mention behavior uses pi-tui's fd-backed provider whenever fd
371557
+ * is available, fanning out across the working directory and any additional
371558
+ * roots so `@` completion pushes the query down to fd instead of enumerating
371559
+ * every file. A small filesystem fallback is used only while managed fd is
371560
+ * downloading, when it is unavailable, or if fd fails to spawn. Ordinary path
371561
+ * completion is still handled by pi-tui's readdir-backed path completer. This
371562
+ * wrapper also keeps Kimi-specific slash-command guards.
370521
371563
  */
370522
371564
  var FileMentionProvider = class {
370523
371565
  slashCommands;
@@ -370540,14 +371582,14 @@ var FileMentionProvider = class {
370540
371582
  name: alias
370541
371583
  });
370542
371584
  }
370543
- this.inner = new CombinedAutocompleteProvider(expanded, workDir, fdPath);
371585
+ this.inner = new CombinedAutocompleteProvider(expanded, workDir, fdPath, this.additionalDirs);
370544
371586
  }
370545
371587
  async getSuggestions(lines, cursorLine, cursorCol, options) {
370546
371588
  const currentLine = lines[cursorLine] ?? "";
370547
371589
  const textBeforeCursor = currentLine.slice(0, cursorCol);
370548
371590
  const atPrefix = extractAtPrefix(textBeforeCursor);
370549
371591
  if (atPrefix !== null) {
370550
- if (this.fdPath === null || this.additionalDirs.length > 0) return getFsMentionSuggestions(this.workDir, this.additionalDirs, atPrefix, options.signal);
371592
+ if (this.fdPath === null || !isExecutableFd(this.fdPath)) return getFsMentionSuggestions(this.workDir, this.additionalDirs, atPrefix, options.signal);
370551
371593
  try {
370552
371594
  return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
370553
371595
  } catch {
@@ -370625,6 +371667,15 @@ function extractAtPrefix(text) {
370625
371667
  if (text[tokenStart] !== "@") return null;
370626
371668
  return text.slice(tokenStart);
370627
371669
  }
371670
+ function isExecutableFd(fdPath) {
371671
+ if (!fdPath.includes("/") && !fdPath.includes("\\")) return true;
371672
+ try {
371673
+ accessSync(fdPath, constants.X_OK);
371674
+ return true;
371675
+ } catch {
371676
+ return false;
371677
+ }
371678
+ }
370628
371679
  /**
370629
371680
  * Match the `/add-dir` directory completer, which skips every entry whose name
370630
371681
  * starts with `.` (see registry.ts). pi-tui's path completer sets `label` to
@@ -374222,7 +375273,7 @@ var SessionEventHandler = class {
374222
375273
  this.host.streamingUI.beginCompaction(event.instruction);
374223
375274
  }
374224
375275
  handleCompactionEnd(event, sendQueued) {
374225
- this.host.streamingUI.endCompaction(event.result.tokensBefore, event.result.tokensAfter);
375276
+ this.host.streamingUI.endCompaction(event.result.tokensBefore, event.result.tokensAfter, event.result.summary);
374226
375277
  this.finishCompaction(sendQueued);
374227
375278
  }
374228
375279
  handleCompactionCancel(_event, sendQueued) {
@@ -374859,6 +375910,7 @@ var SessionReplayRenderer = class {
374859
375910
  this.host.appendTranscriptEntry({
374860
375911
  ...replayEntry(context, "status", "Compaction complete", "plain"),
374861
375912
  compactionData: {
375913
+ summary: record.result.summary,
374862
375914
  tokensBefore: record.result.tokensBefore,
374863
375915
  tokensAfter: record.result.tokensAfter,
374864
375916
  instruction: record.instruction
@@ -375670,12 +376722,13 @@ var StreamingUIController = class {
375670
376722
  const block = new CompactionComponent(state.ui, instruction, currentWorkingTip()?.text);
375671
376723
  this._activeCompactionBlock = block;
375672
376724
  state.transcriptContainer.addChild(block);
376725
+ if (state.toolOutputExpanded) block.setExpanded(true);
375673
376726
  state.ui.requestRender();
375674
376727
  }
375675
- endCompaction(tokensBefore, tokensAfter) {
376728
+ endCompaction(tokensBefore, tokensAfter, summary) {
375676
376729
  const block = this._activeCompactionBlock;
375677
376730
  if (block === void 0) return;
375678
- block.markDone(tokensBefore, tokensAfter);
376731
+ block.markDone(tokensBefore, tokensAfter, summary);
375679
376732
  this._activeCompactionBlock = void 0;
375680
376733
  this.host.state.ui.requestRender();
375681
376734
  }
@@ -377658,6 +378711,7 @@ var CustomEditor = class extends Editor {
377658
378711
  consumingPaste = false;
377659
378712
  consumeBuffer = "";
377660
378713
  argumentHints = /* @__PURE__ */ new Map();
378714
+ autocompleteWasShowing = false;
377661
378715
  setArgumentHints(hints) {
377662
378716
  this.argumentHints = hints;
377663
378717
  }
@@ -377713,7 +378767,20 @@ var CustomEditor = class extends Editor {
377713
378767
  cancelAutocompleteActivity() {
377714
378768
  this.cancelAutocomplete();
377715
378769
  }
378770
+ requestFullRenderOnAutocompleteClose() {
378771
+ if (isInsideTmux()) return;
378772
+ const { columns, rows } = this.tui.terminal;
378773
+ if (this.tui.render(columns).length < rows) return;
378774
+ this.tui.requestRender(true);
378775
+ }
378776
+ trackAutocompleteCloseForFullRender() {
378777
+ const showing = this.isShowingAutocomplete();
378778
+ const closed = this.autocompleteWasShowing && !showing;
378779
+ this.autocompleteWasShowing = showing;
378780
+ if (closed) queueMicrotask(() => this.requestFullRenderOnAutocompleteClose());
378781
+ }
377716
378782
  render(width) {
378783
+ this.trackAutocompleteCloseForFullRender();
377717
378784
  const lines = super.render(width);
377718
378785
  if (lines.length < 3) return lines;
377719
378786
  const firstContentIdx = 1;
@@ -379675,7 +380742,10 @@ var KimiTUI = class {
379675
380742
  const data = entry.compactionData;
379676
380743
  const block = new CompactionComponent(this.state.ui, data.instruction);
379677
380744
  if (data.result === "cancelled") block.markCanceled();
379678
- else block.markDone(data.tokensBefore, data.tokensAfter);
380745
+ else {
380746
+ block.markDone(data.tokensBefore, data.tokensAfter, data.summary);
380747
+ if (this.state.toolOutputExpanded) block.setExpanded(true);
380748
+ }
379679
380749
  return block;
379680
380750
  }
379681
380751
  switch (entry.kind) {
@@ -380251,7 +381321,9 @@ var KimiTUI = class {
380251
381321
  this.state.editorContainer.clear();
380252
381322
  this.state.editorContainer.addChild(this.state.editor);
380253
381323
  this.state.ui.setFocus(this.state.editor);
380254
- this.state.ui.requestRender();
381324
+ const { columns, rows } = this.state.terminal;
381325
+ const overflowsViewport = this.state.ui.render(columns).length >= rows;
381326
+ this.state.ui.requestRender(!this.state.terminalState.insideTmux && overflowsViewport);
380255
381327
  }
380256
381328
  restoreInputText(text) {
380257
381329
  this.restoreEditor();