@moonshot-ai/kimi-code 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +824 -175
- package/dist/{src-CeUdnMA4.mjs → src-D5Ej2mZZ.mjs} +3 -3
- package/package.json +5 -5
package/dist/main.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import * as fs$10 from "node:fs";
|
|
|
22
22
|
import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
23
23
|
import * as path$10 from "node:path";
|
|
24
24
|
import path$1, { basename as basename$1, dirname as dirname$2, extname, isAbsolute as isAbsolute$1, join as join$1, posix, relative as relative$1, resolve as resolve$1, sep, win32 } from "node:path";
|
|
25
|
+
import { Blob as Blob$1, Buffer as Buffer$2, File as File$1 } from "node:buffer";
|
|
25
26
|
import jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink, utimes, writeFile as writeFile$1 } from "node:fs/promises";
|
|
26
27
|
import { z } from "zod";
|
|
27
28
|
import { execFile, execFileSync, execSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -40,14 +41,13 @@ import { parse as parse$2, stringify } from "smol-toml";
|
|
|
40
41
|
import "zod/v3";
|
|
41
42
|
import * as z4mini from "zod/v4-mini";
|
|
42
43
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
43
|
-
import { Buffer as Buffer$2 } from "node:buffer";
|
|
44
44
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
45
45
|
import process$1 from "node:process";
|
|
46
|
-
import { CURSOR_MARKER, CombinedAutocompleteProvider, Container, Editor, Image, Input, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, getCapabilities, isKeyRelease, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
46
|
+
import { CURSOR_MARKER, CombinedAutocompleteProvider, Container, Editor, Image, Input, Key, Markdown, ProcessTerminal, SelectList, Spacer, TUI, Text, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, isKeyRelease, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
47
47
|
import chalk, { chalkStderr } from "chalk";
|
|
48
48
|
import { Command, Option } from "commander";
|
|
49
49
|
import { createInterface } from "node:readline/promises";
|
|
50
|
-
import { gt, valid } from "semver";
|
|
50
|
+
import { gt, gte, valid } from "semver";
|
|
51
51
|
import { highlight, supportsLanguage } from "cli-highlight";
|
|
52
52
|
import { clearLine, cursorTo, emitKeypressEvents, moveCursor } from "node:readline";
|
|
53
53
|
//#region \0rolldown/runtime.js
|
|
@@ -8742,6 +8742,10 @@ function isToolResultOnly(message) {
|
|
|
8742
8742
|
if (!Array.isArray(content) || content.length === 0) return false;
|
|
8743
8743
|
return content.every((block) => block.type === "tool_result");
|
|
8744
8744
|
}
|
|
8745
|
+
const OMITTED_MEDIA_PLACEHOLDER = {
|
|
8746
|
+
audio_url: "(audio omitted: not supported by this provider)",
|
|
8747
|
+
video_url: "(video omitted: not supported by this provider)"
|
|
8748
|
+
};
|
|
8745
8749
|
const SUPPORTED_B64_MEDIA_TYPES = new Set([
|
|
8746
8750
|
"image/png",
|
|
8747
8751
|
"image/jpeg",
|
|
@@ -8787,6 +8791,14 @@ function toolResultToBlock(toolCallId, content) {
|
|
|
8787
8791
|
text: part.text
|
|
8788
8792
|
});
|
|
8789
8793
|
} else if (part.type === "image_url") blocks.push(imageUrlPartToAnthropic(part.imageUrl.url));
|
|
8794
|
+
else if (part.type === "audio_url" || part.type === "video_url") {
|
|
8795
|
+
const placeholder = OMITTED_MEDIA_PLACEHOLDER[part.type];
|
|
8796
|
+
const last = blocks.at(-1);
|
|
8797
|
+
if (!(last?.type === "text" && last.text === placeholder)) blocks.push({
|
|
8798
|
+
type: "text",
|
|
8799
|
+
text: placeholder
|
|
8800
|
+
});
|
|
8801
|
+
}
|
|
8790
8802
|
return {
|
|
8791
8803
|
type: "tool_result",
|
|
8792
8804
|
tool_use_id: toolCallId,
|
|
@@ -8825,6 +8837,13 @@ function convertMessage$3(message, model) {
|
|
|
8825
8837
|
type: "thinking",
|
|
8826
8838
|
thinking: part.think
|
|
8827
8839
|
});
|
|
8840
|
+
} else if (part.type === "audio_url" || part.type === "video_url") {
|
|
8841
|
+
const placeholder = OMITTED_MEDIA_PLACEHOLDER[part.type];
|
|
8842
|
+
const last = blocks.at(-1);
|
|
8843
|
+
if (!(last?.type === "text" && last.text === placeholder)) blocks.push({
|
|
8844
|
+
type: "text",
|
|
8845
|
+
text: placeholder
|
|
8846
|
+
});
|
|
8828
8847
|
}
|
|
8829
8848
|
if (message.toolCalls.length > 0) for (const tc of message.toolCalls) {
|
|
8830
8849
|
let toolInput = {};
|
|
@@ -10319,7 +10338,7 @@ var require_gaxios = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
10319
10338
|
}
|
|
10320
10339
|
static async #getFetch() {
|
|
10321
10340
|
const hasWindow = typeof window !== "undefined" && !!window;
|
|
10322
|
-
this.#fetch ||= hasWindow ? window.fetch : (await import("./src-
|
|
10341
|
+
this.#fetch ||= hasWindow ? window.fetch : (await import("./src-D5Ej2mZZ.mjs")).default;
|
|
10323
10342
|
return this.#fetch;
|
|
10324
10343
|
}
|
|
10325
10344
|
/**
|
|
@@ -45333,6 +45352,16 @@ function normalizeOpenAIFinishReason(raw) {
|
|
|
45333
45352
|
}
|
|
45334
45353
|
}
|
|
45335
45354
|
/**
|
|
45355
|
+
* Shared wording for tool-result media that cannot live inside the tool
|
|
45356
|
+
* message itself and is reattached as a follow-up user message instead.
|
|
45357
|
+
*/
|
|
45358
|
+
const TOOL_RESULT_MEDIA_PROMPT = "Attached media from tool result:";
|
|
45359
|
+
const TOOL_RESULT_MEDIA_PLACEHOLDER = "(see attached media)";
|
|
45360
|
+
/** A content part that is neither plain text nor reasoning. */
|
|
45361
|
+
function isMediaPart(part) {
|
|
45362
|
+
return part.type !== "text" && part.type !== "think";
|
|
45363
|
+
}
|
|
45364
|
+
/**
|
|
45336
45365
|
* Convert tool-role message content according to the chosen strategy.
|
|
45337
45366
|
*/
|
|
45338
45367
|
function convertToolMessageContent(message, conversion) {
|
|
@@ -45389,14 +45418,11 @@ var KimiFiles = class {
|
|
|
45389
45418
|
const mimeType = guessMimeTypeFromExt(filename);
|
|
45390
45419
|
if (mimeType === void 0 || !mimeType.startsWith("video/")) throw new ChatProviderError(`KimiFiles.uploadVideo: file extension does not indicate a video type: ${filename}`);
|
|
45391
45420
|
const data = await fs$10.promises.readFile(input);
|
|
45392
|
-
|
|
45393
|
-
file = new File([blob], filename, { type: mimeType });
|
|
45421
|
+
file = new File$1([new Blob$1([new Uint8Array(data)], { type: mimeType })], filename, { type: mimeType });
|
|
45394
45422
|
} else {
|
|
45395
45423
|
if (!input.mimeType.startsWith("video/")) throw new ChatProviderError(`Expected a video mime type, got ${input.mimeType}`);
|
|
45396
45424
|
const filename = input.filename ?? guessFilename(input.mimeType);
|
|
45397
|
-
|
|
45398
|
-
const blob = new Blob([bytes], { type: input.mimeType });
|
|
45399
|
-
file = new File([blob], filename, { type: input.mimeType });
|
|
45425
|
+
file = new File$1([new Blob$1([input.data instanceof Uint8Array ? input.data : new Uint8Array(input.data)], { type: input.mimeType })], filename, { type: input.mimeType });
|
|
45400
45426
|
}
|
|
45401
45427
|
let uploaded;
|
|
45402
45428
|
try {
|
|
@@ -45840,8 +45866,6 @@ const OPENAI_CHAT_TOOL_CALL_ID_POLICY = {
|
|
|
45840
45866
|
normalize: (id) => sanitizeToolCallId(id, 64),
|
|
45841
45867
|
maxLength: 64
|
|
45842
45868
|
};
|
|
45843
|
-
const TOOL_RESULT_IMAGE_PROMPT = "Attached image(s) from tool result:";
|
|
45844
|
-
const TOOL_RESULT_IMAGE_PLACEHOLDER = "(see attached image)";
|
|
45845
45869
|
function extractReasoningContent(source, explicitKey) {
|
|
45846
45870
|
if (typeof source !== "object" || source === null) return void 0;
|
|
45847
45871
|
const record = source;
|
|
@@ -45898,45 +45922,46 @@ function convertMessage$1(message, reasoningKey, toolMessageConversion) {
|
|
|
45898
45922
|
if (reasoningContent) result[reasoningKey ?? DEFAULT_OUTBOUND_REASONING_KEY] = reasoningContent;
|
|
45899
45923
|
return result;
|
|
45900
45924
|
}
|
|
45925
|
+
const OMITTED_AUDIO_PLACEHOLDER$1 = "(audio omitted: not supported by this provider)";
|
|
45926
|
+
const OMITTED_VIDEO_PLACEHOLDER$1 = "(video omitted: not supported by this provider)";
|
|
45901
45927
|
function convertToolMessageContentForChat(message, conversion) {
|
|
45902
45928
|
const content = convertToolMessageContent(message, conversion);
|
|
45903
|
-
if (typeof content
|
|
45904
|
-
|
|
45929
|
+
if (typeof content !== "string") return content;
|
|
45930
|
+
const lines = content.length > 0 ? [content] : [];
|
|
45931
|
+
if (message.content.some((part) => part.type === "audio_url")) lines.push(OMITTED_AUDIO_PLACEHOLDER$1);
|
|
45932
|
+
if (message.content.some((part) => part.type === "video_url")) lines.push(OMITTED_VIDEO_PLACEHOLDER$1);
|
|
45933
|
+
if (lines.length === 0 && message.content.some((part) => part.type === "image_url")) return TOOL_RESULT_MEDIA_PLACEHOLDER;
|
|
45934
|
+
return lines.join("\n");
|
|
45905
45935
|
}
|
|
45906
45936
|
function toolResultImageParts(message) {
|
|
45907
45937
|
const images = [];
|
|
45908
45938
|
for (const part of message.content) {
|
|
45909
45939
|
if (part.type !== "image_url") continue;
|
|
45910
|
-
|
|
45911
|
-
|
|
45912
|
-
image_url: part.imageUrl.id === void 0 ? { url: part.imageUrl.url } : {
|
|
45913
|
-
url: part.imageUrl.url,
|
|
45914
|
-
id: part.imageUrl.id
|
|
45915
|
-
}
|
|
45916
|
-
});
|
|
45940
|
+
const converted = convertContentPart(part);
|
|
45941
|
+
if (converted !== null) images.push(converted);
|
|
45917
45942
|
}
|
|
45918
45943
|
return images;
|
|
45919
45944
|
}
|
|
45920
|
-
function
|
|
45921
|
-
if (
|
|
45945
|
+
function appendToolResultMediaMessage(messages, pendingToolResultMedia) {
|
|
45946
|
+
if (pendingToolResultMedia.length === 0) return;
|
|
45922
45947
|
messages.push({
|
|
45923
45948
|
role: "user",
|
|
45924
45949
|
content: [{
|
|
45925
45950
|
type: "text",
|
|
45926
|
-
text:
|
|
45927
|
-
}, ...
|
|
45951
|
+
text: TOOL_RESULT_MEDIA_PROMPT
|
|
45952
|
+
}, ...pendingToolResultMedia]
|
|
45928
45953
|
});
|
|
45929
|
-
|
|
45954
|
+
pendingToolResultMedia.length = 0;
|
|
45930
45955
|
}
|
|
45931
|
-
function convertHistoryMessages(history, reasoningKey, toolMessageConversion) {
|
|
45956
|
+
function convertHistoryMessages$1(history, reasoningKey, toolMessageConversion) {
|
|
45932
45957
|
const messages = [];
|
|
45933
|
-
const
|
|
45958
|
+
const pendingToolResultMedia = [];
|
|
45934
45959
|
for (const msg of history) {
|
|
45935
|
-
if (msg.role !== "tool")
|
|
45960
|
+
if (msg.role !== "tool") appendToolResultMediaMessage(messages, pendingToolResultMedia);
|
|
45936
45961
|
messages.push(convertMessage$1(msg, reasoningKey, toolMessageConversion));
|
|
45937
|
-
if (msg.role === "tool")
|
|
45962
|
+
if (msg.role === "tool") pendingToolResultMedia.push(...toolResultImageParts(msg));
|
|
45938
45963
|
}
|
|
45939
|
-
|
|
45964
|
+
appendToolResultMediaMessage(messages, pendingToolResultMedia);
|
|
45940
45965
|
return messages;
|
|
45941
45966
|
}
|
|
45942
45967
|
var OpenAILegacyStreamedMessage = class {
|
|
@@ -46074,7 +46099,7 @@ var OpenAILegacyChatProvider = class {
|
|
|
46074
46099
|
content: systemPrompt
|
|
46075
46100
|
});
|
|
46076
46101
|
const normalizedHistory = normalizeToolCallIdsForProvider(history, OPENAI_CHAT_TOOL_CALL_ID_POLICY);
|
|
46077
|
-
messages.push(...convertHistoryMessages(normalizedHistory, this._reasoningKey, this._toolMessageConversion));
|
|
46102
|
+
messages.push(...convertHistoryMessages$1(normalizedHistory, this._reasoningKey, this._toolMessageConversion));
|
|
46078
46103
|
const kwargs = normalizeGenerationKwargs(this._model, this._generationKwargs);
|
|
46079
46104
|
let reasoningEffort = this._reasoningEffort;
|
|
46080
46105
|
if (reasoningEffort === void 0 && kwargs["reasoning_effort"] === void 0) {
|
|
@@ -46310,6 +46335,8 @@ function formatResponsesFailedResponse(response) {
|
|
|
46310
46335
|
const reason = incompleteDetails === void 0 ? void 0 : readStringField(incompleteDetails, "reason");
|
|
46311
46336
|
return reason === void 0 ? "Unknown error (no error details in response)" : `incomplete: ${reason}`;
|
|
46312
46337
|
}
|
|
46338
|
+
const OMITTED_AUDIO_PLACEHOLDER = "(audio omitted: unsupported audio format)";
|
|
46339
|
+
const OMITTED_VIDEO_PLACEHOLDER = "(video omitted: not supported by this provider)";
|
|
46313
46340
|
function contentPartsToInputItems(parts) {
|
|
46314
46341
|
const items = [];
|
|
46315
46342
|
for (const part of parts) switch (part.type) {
|
|
@@ -46328,11 +46355,19 @@ function contentPartsToInputItems(parts) {
|
|
|
46328
46355
|
break;
|
|
46329
46356
|
case "audio_url": {
|
|
46330
46357
|
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
46331
|
-
|
|
46358
|
+
items.push(mapped ?? {
|
|
46359
|
+
type: "input_text",
|
|
46360
|
+
text: OMITTED_AUDIO_PLACEHOLDER
|
|
46361
|
+
});
|
|
46332
46362
|
break;
|
|
46333
46363
|
}
|
|
46334
|
-
case "
|
|
46335
|
-
|
|
46364
|
+
case "video_url":
|
|
46365
|
+
items.push({
|
|
46366
|
+
type: "input_text",
|
|
46367
|
+
text: OMITTED_VIDEO_PLACEHOLDER
|
|
46368
|
+
});
|
|
46369
|
+
break;
|
|
46370
|
+
case "think": break;
|
|
46336
46371
|
}
|
|
46337
46372
|
return items;
|
|
46338
46373
|
}
|
|
@@ -46362,11 +46397,19 @@ function messageContentToFunctionOutputItems(content) {
|
|
|
46362
46397
|
break;
|
|
46363
46398
|
case "audio_url": {
|
|
46364
46399
|
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
46365
|
-
|
|
46400
|
+
items.push(mapped ?? {
|
|
46401
|
+
type: "input_text",
|
|
46402
|
+
text: OMITTED_AUDIO_PLACEHOLDER
|
|
46403
|
+
});
|
|
46366
46404
|
break;
|
|
46367
46405
|
}
|
|
46368
|
-
case "
|
|
46369
|
-
|
|
46406
|
+
case "video_url":
|
|
46407
|
+
items.push({
|
|
46408
|
+
type: "input_text",
|
|
46409
|
+
text: OMITTED_VIDEO_PLACEHOLDER
|
|
46410
|
+
});
|
|
46411
|
+
break;
|
|
46412
|
+
case "think": break;
|
|
46370
46413
|
}
|
|
46371
46414
|
return items;
|
|
46372
46415
|
}
|
|
@@ -46399,11 +46442,19 @@ function mapAudioUrlToInputItem(url) {
|
|
|
46399
46442
|
function convertMessage(message, modelName, toolMessageConversion) {
|
|
46400
46443
|
let role = message.role;
|
|
46401
46444
|
if (usesOpenAIResponsesDeveloperRole(modelName) && role === "system") role = "developer";
|
|
46402
|
-
if (role === "tool")
|
|
46403
|
-
|
|
46404
|
-
output
|
|
46405
|
-
|
|
46406
|
-
|
|
46445
|
+
if (role === "tool") {
|
|
46446
|
+
const callId = message.toolCallId ?? "";
|
|
46447
|
+
let output;
|
|
46448
|
+
if (toolMessageConversion === "extract_text") {
|
|
46449
|
+
const text = extractText(message);
|
|
46450
|
+
output = text.length === 0 && message.content.some(isMediaPart) ? TOOL_RESULT_MEDIA_PLACEHOLDER : text;
|
|
46451
|
+
} else output = messageContentToFunctionOutputItems(message.content);
|
|
46452
|
+
return [{
|
|
46453
|
+
call_id: callId,
|
|
46454
|
+
output,
|
|
46455
|
+
type: "function_call_output"
|
|
46456
|
+
}];
|
|
46457
|
+
}
|
|
46407
46458
|
const result = [];
|
|
46408
46459
|
if (message.content.length > 0) {
|
|
46409
46460
|
const pendingParts = [];
|
|
@@ -46474,6 +46525,35 @@ function convertTool(tool) {
|
|
|
46474
46525
|
strict: false
|
|
46475
46526
|
};
|
|
46476
46527
|
}
|
|
46528
|
+
/**
|
|
46529
|
+
* Convert the history, buffering tool-result media when `extract_text`
|
|
46530
|
+
* flattens tool outputs to plain strings. The buffered media items are
|
|
46531
|
+
* reattached as a single user message after each run of consecutive tool
|
|
46532
|
+
* messages — mirroring the OpenAI Chat Completions provider.
|
|
46533
|
+
*/
|
|
46534
|
+
function convertHistoryMessages(history, modelName, toolMessageConversion) {
|
|
46535
|
+
const input = [];
|
|
46536
|
+
const pendingToolResultMedia = [];
|
|
46537
|
+
const flushPendingMedia = () => {
|
|
46538
|
+
if (pendingToolResultMedia.length === 0) return;
|
|
46539
|
+
input.push({
|
|
46540
|
+
type: "message",
|
|
46541
|
+
role: "user",
|
|
46542
|
+
content: [{
|
|
46543
|
+
type: "input_text",
|
|
46544
|
+
text: TOOL_RESULT_MEDIA_PROMPT
|
|
46545
|
+
}, ...pendingToolResultMedia]
|
|
46546
|
+
});
|
|
46547
|
+
pendingToolResultMedia.length = 0;
|
|
46548
|
+
};
|
|
46549
|
+
for (const msg of history) {
|
|
46550
|
+
if (msg.role !== "tool") flushPendingMedia();
|
|
46551
|
+
input.push(...convertMessage(msg, modelName, toolMessageConversion));
|
|
46552
|
+
if (msg.role === "tool" && toolMessageConversion === "extract_text") pendingToolResultMedia.push(...messageContentToFunctionOutputItems(msg.content.filter(isMediaPart)));
|
|
46553
|
+
}
|
|
46554
|
+
flushPendingMedia();
|
|
46555
|
+
return input;
|
|
46556
|
+
}
|
|
46477
46557
|
var OpenAIResponsesStreamedMessage = class {
|
|
46478
46558
|
_id = null;
|
|
46479
46559
|
_usage = null;
|
|
@@ -46735,16 +46815,8 @@ var OpenAIResponsesChatProvider = class {
|
|
|
46735
46815
|
}
|
|
46736
46816
|
async generate(systemPrompt, tools, history, options) {
|
|
46737
46817
|
const input = [];
|
|
46738
|
-
if (systemPrompt) {
|
|
46739
|
-
const sysItem = {
|
|
46740
|
-
role: "system",
|
|
46741
|
-
content: systemPrompt
|
|
46742
|
-
};
|
|
46743
|
-
if (usesOpenAIResponsesDeveloperRole(this._model)) sysItem["role"] = "developer";
|
|
46744
|
-
input.push(sysItem);
|
|
46745
|
-
}
|
|
46746
46818
|
const normalizedHistory = normalizeToolCallIdsForProvider(history, OPENAI_RESPONSES_TOOL_CALL_ID_POLICY);
|
|
46747
|
-
|
|
46819
|
+
input.push(...convertHistoryMessages(normalizedHistory, this._model, this._toolMessageConversion));
|
|
46748
46820
|
const kwargs = { ...this._generationKwargs };
|
|
46749
46821
|
const reasoningEffort = kwargs["reasoning_effort"];
|
|
46750
46822
|
delete kwargs["reasoning_effort"];
|
|
@@ -46766,6 +46838,7 @@ var OpenAIResponsesChatProvider = class {
|
|
|
46766
46838
|
stream: this._stream,
|
|
46767
46839
|
...kwargs
|
|
46768
46840
|
};
|
|
46841
|
+
if (systemPrompt) createParams["instructions"] = systemPrompt;
|
|
46769
46842
|
if (!("responses" in client) || typeof client.responses?.create !== "function") throw new Error("OpenAI SDK version does not support Responses API. Upgrade to >=4.x with responses support.");
|
|
46770
46843
|
return new OpenAIResponsesStreamedMessage(await client.responses.create(createParams, options?.signal ? { signal: options.signal } : void 0), this._stream);
|
|
46771
46844
|
} catch (error) {
|
|
@@ -47221,7 +47294,7 @@ const REDACTED_KEYS = new Set([
|
|
|
47221
47294
|
"bearer"
|
|
47222
47295
|
]);
|
|
47223
47296
|
const SAFE_KEY_RE = /^[\w.-]+$/;
|
|
47224
|
-
const ELLIPSIS$
|
|
47297
|
+
const ELLIPSIS$9 = "…";
|
|
47225
47298
|
const TRUNCATED_TAIL = ` …truncated`;
|
|
47226
47299
|
const REDACTED = "[REDACTED]";
|
|
47227
47300
|
const RAW_SECRET_PATTERNS = [
|
|
@@ -47260,7 +47333,7 @@ function redactCtx(ctx) {
|
|
|
47260
47333
|
return walk(ctx, 0);
|
|
47261
47334
|
}
|
|
47262
47335
|
function truncate$2(value, max) {
|
|
47263
|
-
return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$
|
|
47336
|
+
return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$9;
|
|
47264
47337
|
}
|
|
47265
47338
|
function serializeValue(raw) {
|
|
47266
47339
|
if (typeof raw === "string") return redactString(raw);
|
|
@@ -47450,7 +47523,6 @@ var RotatingFileSink = class {
|
|
|
47450
47523
|
pending = [];
|
|
47451
47524
|
dropped = 0;
|
|
47452
47525
|
closed = false;
|
|
47453
|
-
failed = false;
|
|
47454
47526
|
lastStderrNotice = 0;
|
|
47455
47527
|
currentBytes = -1;
|
|
47456
47528
|
directorySynced = false;
|
|
@@ -47507,7 +47579,6 @@ var RotatingFileSink = class {
|
|
|
47507
47579
|
await syncDir(dirname(this.options.path));
|
|
47508
47580
|
this.directorySynced = true;
|
|
47509
47581
|
}
|
|
47510
|
-
this.failed = false;
|
|
47511
47582
|
return true;
|
|
47512
47583
|
} catch (error) {
|
|
47513
47584
|
this.noteFailure(error);
|
|
@@ -47591,7 +47662,6 @@ var RotatingFileSink = class {
|
|
|
47591
47662
|
return line;
|
|
47592
47663
|
}
|
|
47593
47664
|
noteFailure(error) {
|
|
47594
|
-
this.failed = true;
|
|
47595
47665
|
const now = Date.now();
|
|
47596
47666
|
if (now - this.lastStderrNotice < STDERR_NOTICE_INTERVAL_MS) return;
|
|
47597
47667
|
this.lastStderrNotice = now;
|
|
@@ -49156,8 +49226,8 @@ function buildBackgroundTaskNotificationBody(info) {
|
|
|
49156
49226
|
//#endregion
|
|
49157
49227
|
//#region ../../packages/agent-core/src/utils/abort.ts
|
|
49158
49228
|
var import_retry = /* @__PURE__ */ __toESM(require_retry$4(), 1);
|
|
49159
|
-
function abortError$1() {
|
|
49160
|
-
const error =
|
|
49229
|
+
function abortError$1(message = "Aborted") {
|
|
49230
|
+
const error = new Error(message);
|
|
49161
49231
|
error.name = "AbortError";
|
|
49162
49232
|
return error;
|
|
49163
49233
|
}
|
|
@@ -56376,8 +56446,12 @@ var ConfigState = class {
|
|
|
56376
56446
|
return this._modelAlias;
|
|
56377
56447
|
}
|
|
56378
56448
|
get thinkingLevel() {
|
|
56449
|
+
if (this._thinkingLevel === "off" && this.alwaysThinkingModel) return resolveThinkingEffort("on", this.agent.kimiConfig?.thinking);
|
|
56379
56450
|
return this._thinkingLevel;
|
|
56380
56451
|
}
|
|
56452
|
+
get alwaysThinkingModel() {
|
|
56453
|
+
return this.tryResolvedProviderConfig()?.alwaysThinking === true;
|
|
56454
|
+
}
|
|
56381
56455
|
get profileName() {
|
|
56382
56456
|
return this._profileName;
|
|
56383
56457
|
}
|
|
@@ -58024,6 +58098,31 @@ var InjectionManager = class {
|
|
|
58024
58098
|
}
|
|
58025
58099
|
};
|
|
58026
58100
|
//#endregion
|
|
58101
|
+
//#region ../../packages/agent-core/src/agent/permission/policies/agent-swarm-exclusive-deny.ts
|
|
58102
|
+
var AgentSwarmExclusiveDenyPermissionPolicy = class {
|
|
58103
|
+
name = "agent-swarm-exclusive-deny";
|
|
58104
|
+
evaluate(context) {
|
|
58105
|
+
const toolCalls = context.toolCalls;
|
|
58106
|
+
const agentSwarmCount = toolCalls.filter((toolCall) => toolCall.name === "AgentSwarm").length;
|
|
58107
|
+
if (agentSwarmCount === 0) return;
|
|
58108
|
+
if (agentSwarmCount === 1 && toolCalls.length === 1) return;
|
|
58109
|
+
return {
|
|
58110
|
+
kind: "deny",
|
|
58111
|
+
message: agentSwarmCount > 1 ? multipleAgentSwarmDeniedMessage(toolCalls.length > agentSwarmCount) : mixedAgentSwarmDeniedMessage(),
|
|
58112
|
+
reason: {
|
|
58113
|
+
agent_swarm_tool_calls: agentSwarmCount,
|
|
58114
|
+
tool_calls: toolCalls.length
|
|
58115
|
+
}
|
|
58116
|
+
};
|
|
58117
|
+
}
|
|
58118
|
+
};
|
|
58119
|
+
function multipleAgentSwarmDeniedMessage(hasOtherToolCalls) {
|
|
58120
|
+
return `AgentSwarm must be called one swarm at a time. Multiple AgentSwarm calls are not forbidden, but issue them sequentially: call one AgentSwarm, wait for its result, then call the next; or merge the work into a single AgentSwarm when one swarm can cover it.${hasOtherToolCalls ? " AgentSwarm also must not be combined with other tools in the same response." : ""}`;
|
|
58121
|
+
}
|
|
58122
|
+
function mixedAgentSwarmDeniedMessage() {
|
|
58123
|
+
return "AgentSwarm must be the only tool call in a model response. Retry with a single AgentSwarm call by itself, then call any other tools after it returns.";
|
|
58124
|
+
}
|
|
58125
|
+
//#endregion
|
|
58027
58126
|
//#region ../../packages/agent-core/src/agent/permission/policies/auto-mode-approve.ts
|
|
58028
58127
|
var AutoModeApprovePermissionPolicy = class {
|
|
58029
58128
|
agent;
|
|
@@ -58466,11 +58565,7 @@ function parseGitDir(content, markerParent) {
|
|
|
58466
58565
|
//#endregion
|
|
58467
58566
|
//#region ../../packages/agent-core/src/agent/permission/policies/file-access-ask.ts
|
|
58468
58567
|
var SensitiveFileAccessAskPermissionPolicy = class {
|
|
58469
|
-
agent;
|
|
58470
58568
|
name = "sensitive-file-access-ask";
|
|
58471
|
-
constructor(agent) {
|
|
58472
|
-
this.agent = agent;
|
|
58473
|
-
}
|
|
58474
58569
|
evaluate(context) {
|
|
58475
58570
|
const access = fileAccesses(context).find((fileAccess) => isSensitiveFile(fileAccess.path));
|
|
58476
58571
|
if (access === void 0) return;
|
|
@@ -60720,6 +60815,7 @@ var YoloModeApprovePermissionPolicy = class {
|
|
|
60720
60815
|
function createPermissionDecisionPolicies(agent) {
|
|
60721
60816
|
return [
|
|
60722
60817
|
new PreToolCallHookPermissionPolicy(agent),
|
|
60818
|
+
new AgentSwarmExclusiveDenyPermissionPolicy(),
|
|
60723
60819
|
new AutoModeAskUserQuestionDenyPermissionPolicy(agent),
|
|
60724
60820
|
new PlanModeGuardDenyPermissionPolicy(agent),
|
|
60725
60821
|
new UserConfiguredDenyPermissionPolicy(agent),
|
|
@@ -60729,7 +60825,7 @@ function createPermissionDecisionPolicies(agent) {
|
|
|
60729
60825
|
new UserConfiguredAllowPermissionPolicy(agent),
|
|
60730
60826
|
new ExitPlanModeReviewAskPermissionPolicy(agent),
|
|
60731
60827
|
new PlanModeToolApprovePermissionPolicy(agent),
|
|
60732
|
-
new SensitiveFileAccessAskPermissionPolicy(
|
|
60828
|
+
new SensitiveFileAccessAskPermissionPolicy(),
|
|
60733
60829
|
new GitControlPathAccessAskPermissionPolicy(agent),
|
|
60734
60830
|
new YoloModeApprovePermissionPolicy(agent),
|
|
60735
60831
|
new SwarmModeAgentSwarmApprovePermissionPolicy(agent),
|
|
@@ -62006,12 +62102,16 @@ var AgentRecords = class {
|
|
|
62006
62102
|
//#region ../../packages/agent-core/src/agent/replay/index.ts
|
|
62007
62103
|
var ReplayBuilder = class {
|
|
62008
62104
|
agent;
|
|
62105
|
+
captureLiveRecords = false;
|
|
62009
62106
|
records = [];
|
|
62010
62107
|
constructor(agent) {
|
|
62011
62108
|
this.agent = agent;
|
|
62012
62109
|
}
|
|
62013
62110
|
push(record) {
|
|
62014
|
-
if (this.agent.records.restoring) this.records.push(
|
|
62111
|
+
if (this.captureLiveRecords || this.agent.records.restoring) this.records.push({
|
|
62112
|
+
...record,
|
|
62113
|
+
time: this.agent.records.restoring?.time ?? Date.now()
|
|
62114
|
+
});
|
|
62015
62115
|
}
|
|
62016
62116
|
removeLastMessages(removedMessages) {
|
|
62017
62117
|
if (removedMessages.size === 0) return;
|
|
@@ -65487,7 +65587,7 @@ function isUserActivatableSkillType(type) {
|
|
|
65487
65587
|
return isInlineSkillType(type) || type === "flow";
|
|
65488
65588
|
}
|
|
65489
65589
|
function isSupportedSkillType(type) {
|
|
65490
|
-
return isUserActivatableSkillType(type);
|
|
65590
|
+
return isUserActivatableSkillType(type) || type === "reference";
|
|
65491
65591
|
}
|
|
65492
65592
|
function summarizeSkill(skill) {
|
|
65493
65593
|
return {
|
|
@@ -65698,7 +65798,7 @@ function isRecord$11(value) {
|
|
|
65698
65798
|
}
|
|
65699
65799
|
//#endregion
|
|
65700
65800
|
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.md
|
|
65701
|
-
var custom_theme_default = "---\nname: custom-theme\ndescription: Create or edit a kimi-code custom color theme — a JSON file under
|
|
65801
|
+
var custom_theme_default = "---\nname: custom-theme\ndescription: Create or edit a kimi-code custom color theme — a JSON file under the resolved KIMI_CODE_HOME data directory that recolors the TUI. Use when the user wants their own theme, asks for a specific palette or mood, or wants to tweak an existing custom theme's colors.\n---\n\n# Create a kimi-code custom theme (custom-theme)\n\nHelp the user design, write, and apply a custom color theme for the kimi-code TUI. A theme is a single JSON file; the TUI ships with `dark`, `light`, and `auto`, and any file the user adds becomes selectable alongside them.\n\n## Rules of engagement\n\n- **Never write a theme until the user has explicitly clarified what they want.** This skill may only run after the user has confirmed light vs dark, the style or mood, any specific colors they care about, and the intended filename. If any of these are missing, ask before creating files.\n- **Never assume the data directory is `~/.kimi-code`.** Always resolve `$KIMI_CODE_HOME` first with the Bash command below.\n- **Never edit a live theme file in place.** Always create a `.json.new` candidate, validate it, back up the old file, and then `mv` it into place.\n- **Never overwrite an existing theme without reading it first.** Read, back up, then overwrite only after the user confirms.\n\n## Where a theme lives\n\nThe kimi-code runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Theme files live inside the `themes/` subdirectory of that data directory.\n\nBefore doing anything, resolve the actual data root with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved data root — **never assume `~/.kimi-code`**. Theme files live at `<KIMI_CODE_HOME>/themes/<name>.json`. Create the `themes/` directory if it doesn't exist.\n\n## What a theme is\n\n- A theme lives at `<KIMI_CODE_HOME>/themes/<name>.json`.\n- **The filename is the theme name**: `ember.json` shows up in the `/theme` picker as `Custom: ember`.\n- Shape:\n\n ```json\n {\n \"name\": \"ember\",\n \"displayName\": \"Ember\",\n \"colors\": {\n \"primary\": \"#83A598\",\n \"accent\": \"#FE8019\"\n }\n }\n ```\n\n - `name` (required), `displayName` (optional), `base` (optional: `\"dark\"` default, or `\"light\"`), `colors` (each value a 6-digit hex `#RRGGBB`).\n- **Partial themes are fine**: any token you leave out falls back to the **base** palette (`dark` by default; set `\"base\": \"light\"` for a light theme), so you can recolor just a few tokens or all of them.\n\n## Source of truth: the docs token reference\n\nBefore choosing colors, use **FetchURL** to fetch the official custom-theme docs as the authoritative list of tokens and what each controls:\n\n```\nhttps://moonshotai.github.io/kimi-code/en/customization/themes.html\n```\n\nOnly set tokens from this set — unknown keys are silently ignored at load. If FetchURL is unavailable or the fetch fails, fall back to the embedded reference below (it mirrors the same tokens) and tell the user you're working from the built-in list rather than the live docs.\n\n## Color tokens (what each controls)\n\n| Token | Controls |\n| --- | --- |\n| `primary` | The most-used color: links, inline code, the selected item in nearly every dialog, the focused editor border, plan/\"running\" badges, spinners |\n| `accent` | Secondary highlight: approval `▶` prefix, device-code box, image placeholder, BTW / queue panes, registry import |\n| `text` | Body text: dialog bodies, todo titles, footer model label, Markdown headings, assistant/tool message bullets, list bullets |\n| `textStrong` | Emphasized / bold text: input dialogs, status messages |\n| `textDim` | Secondary, dimmed text (the most widely used dim shade): thinking, hints, descriptions, completed todos, Markdown quotes, footer status bar |\n| `textMuted` | Faintest text: counters, scroll info, descriptions, Markdown link URLs, code-block borders |\n| `border` | Pane and editor borders, Markdown horizontal rule |\n| `borderFocus` | Focus / attention border (currently only the approval panel) |\n| `success` | Success state: `✓`, \"enabled\", completed |\n| `warning` | Warning state: auto/yolo badges, stale markers, plan-mode hint |\n| `error` | Error state: error messages, failed tool output |\n| `diffAdded` | Diff added lines |\n| `diffRemoved` | Diff removed lines |\n| `diffAddedStrong` | Diff intra-line changed words, added (bold) |\n| `diffRemovedStrong` | Diff intra-line changed words, removed (bold) |\n| `diffGutter` | Diff line-number gutter |\n| `diffMeta` | Diff meta / hunk headers |\n| `roleUser` | User message bullet and text, skill-activation name (the one role color with its own hue) |\n\n## Workflow\n\n1. **Ask the user what they want first — before choosing any colors.** Clarify, in one short exchange:\n - **Light or dark?** A light theme (dark text on a light background) or a dark theme (light text on a dark background). This sets the whole direction, so settle it first. For a light theme, set `\"base\": \"light\"` so the tokens you leave out inherit the light palette instead of dark.\n - **What style / mood?** e.g. warm vs cool, vivid vs muted, high vs low contrast, a named vibe (\"nord\", \"solarized\", \"sunset\"), or a base to start from (an existing theme, or `dark` / `light`).\n - **Any specific colors?** Whether they have exact hex values to anchor on (a brand color, a preferred `primary`, etc.).\n\n For the discrete choices (light vs dark, a few style options), prefer **AskUserQuestion** if it is available. If you are running in **auto mode** and `AskUserQuestion` is unavailable, ask the same question as a plain-text message with clear numbered or bulleted options, and wait for the user's reply. Don't start picking colors until you at least know light-vs-dark and the rough style.\n\n2. **Resolve the actual theme directory and current theme(s).**\n - Resolve the data root by checking `echo \"$KIMI_CODE_HOME\"`; if empty, use `echo \"$HOME/.kimi-code\"`. Use `<root>/themes` for every subsequent step.\n - If tweaking an existing custom theme, **Read** `<KIMI_CODE_HOME>/themes/<name>.json` first — never overwrite a theme you haven't read.\n - Starting fresh: build a `colors` object from the token table. You can `ls <KIMI_CODE_HOME>/themes/` and Read one of the user's existing themes as a reference for the format.\n\n3. **Pick a starting point and choose colors deliberately.**\n - Every value is a 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Keep contrast usable against the user's terminal background: don't let `text` / `textDim` sit too close to the background, and keep `success` / `warning` / `error` clearly distinguishable from each other.\n - `primary` is the most-seen color (links, selection, focus) — make it readable and distinct from `text`.\n - `roleUser` is the one role color meant to stand on its own — give it a distinct hue.\n\n4. **Create a candidate file; never edit the live theme in place.**\n - Use Bash to create a candidate. If the target theme already exists, copy it verbatim: `cp <name>.json <name>.json.new` (inside `<KIMI_CODE_HOME>/themes/`). If it doesn't exist, use **Write** to create a minimal skeleton named `<name>.json.new`.\n - Use **Edit** on the candidate to change only the intended keys. Keep every existing entry, comment, and formatting intact.\n\n5. **Validate the candidate before overwriting.**\n - Read the candidate with **Read** to visually confirm it is well-formed JSON and that every `colors` value is a full 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Invalid hex values are silently skipped at load (they fall back to the base palette), but fix them so the theme renders as intended.\n\n6. **Back up and overwrite.**\n - Back up the old file first — **always** create a new timestamped backup and never overwrite an existing backup: `cp <name>.json \"<name>.json.$(date +%Y%m%d-%H%M%S).bak\"`.\n - If the target didn't exist, skip the backup.\n - Overwrite with the candidate: `mv <name>.json.new <name>.json`.\n\n7. **Tell the user how to apply it** (next section).\n\n## Applying the theme\n\n- The `/theme` picker re-scans the themes directory every time it opens, so a newly added file shows up **without restarting** — tell the user to run `/theme` and choose `Custom: <name>`.\n- Or set it in `tui.toml`: `theme = \"<name>\"`.\n- **Editing the active theme**: changes to the theme that's *currently in use* are not auto-reloaded. Tell the user to run **`/reload-tui`** (or switch to another theme and back). Re-selecting the **same** theme in `/theme` is a no-op (\"Theme unchanged\").\n\n## Don'ts\n\n- **Don't start creating or editing a theme until the user has clarified light/dark, style/mood, any specific colors, and the filename.** If anything is unclear, ask — don't guess.\n- Don't invent token names — only use the documented set; unknown keys are silently ignored.\n- Don't write 3-digit hex or named colors — use full `#RRGGBB`.\n- Never edit the live theme file in place; work through a candidate and validate before `mv`.\n- Before overwriting an existing theme file, **read it and back it up** so the user can recover.\n- Don't tell the user to restart the app to apply a theme — `/theme` or `/reload-tui` is enough.\n";
|
|
65702
65802
|
//#endregion
|
|
65703
65803
|
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.ts
|
|
65704
65804
|
const PSEUDO_PATH$3 = "builtin://custom-theme";
|
|
@@ -65742,7 +65842,7 @@ const IMPORT_FROM_CC_CODEX_SKILL = {
|
|
|
65742
65842
|
};
|
|
65743
65843
|
//#endregion
|
|
65744
65844
|
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.md
|
|
65745
|
-
var mcp_config_default = "---\nname: mcp-config\ndescription: Configure MCP servers and handle MCP OAuth login.\n---\n\n# Interactive MCP server configuration\n\nThe user invoked this skill through `/mcp-config` or `/skill:mcp-config`.\nEither they want to log into an MCP server that asked for OAuth, or they\nwant to edit the `mcp.json` that lists MCP servers. The work is small and\nlocal — handle it on this turn yourself, no agents or planning todos.\n\nPick the flow from the user's message and your tool list:\n\n- An `mcp__<server>__authenticate` tool is in your list, the user says\n \"log in\" / \"auth\" / \"sign in\", they invoke `/mcp-config login\n <server>`, or they quote a `needs-auth` status → **Login**.\n- Add / edit / remove / list of an `mcp.json` entry → **Config edit**.\n- Bare `/mcp-config` with no `authenticate` tool in your list →\n **Config edit**. If there were a pending login, the authenticate tool\n would be in your list.\n\n## Login\n\nEach MCP server in `needs-auth` exposes one `mcp__<server>__authenticate`\ntool. Call it for the server the user means — its own description owns\nthe OAuth UX (printing the URL, blocking on the callback, reconnecting on\nsuccess). Surface its output verbatim, including the authorization URL\nunchanged; the URL contains state and PKCE parameters that break if\nedited.\n\nIf the user named a server that has no authenticate tool, say so in one\nsentence and stop — do **not** fall into config edit. They're trying to\nlog in to a server that isn't currently waiting for login; quietly\nrewriting `mcp.json` would be the wrong fix. If multiple authenticate\ntools exist and the user didn't name one, ask which.\n\n## Config edit\n\nConfig lives in three files; on key collision, later entries in this\nprecedence order override earlier ones
|
|
65845
|
+
var mcp_config_default = "---\nname: mcp-config\ndescription: Configure MCP servers and handle MCP OAuth login.\n---\n\n# Interactive MCP server configuration\n\nThe user invoked this skill through `/mcp-config` or `/skill:mcp-config`.\nEither they want to log into an MCP server that asked for OAuth, or they\nwant to edit the `mcp.json` that lists MCP servers. The work is small and\nlocal — handle it on this turn yourself, no agents or planning todos.\n\nPick the flow from the user's message and your tool list:\n\n- An `mcp__<server>__authenticate` tool is in your list, the user says\n \"log in\" / \"auth\" / \"sign in\", they invoke `/mcp-config login\n <server>`, or they quote a `needs-auth` status → **Login**.\n- Add / edit / remove / list of an `mcp.json` entry → **Config edit**.\n- Bare `/mcp-config` with no `authenticate` tool in your list →\n **Config edit**. If there were a pending login, the authenticate tool\n would be in your list.\n\n## Login\n\nEach MCP server in `needs-auth` exposes one `mcp__<server>__authenticate`\ntool. Call it for the server the user means — its own description owns\nthe OAuth UX (printing the URL, blocking on the callback, reconnecting on\nsuccess). Surface its output verbatim, including the authorization URL\nunchanged; the URL contains state and PKCE parameters that break if\nedited.\n\nIf the user named a server that has no authenticate tool, say so in one\nsentence and stop — do **not** fall into config edit. They're trying to\nlog in to a server that isn't currently waiting for login; quietly\nrewriting `mcp.json` would be the wrong fix. If multiple authenticate\ntools exist and the user didn't name one, ask which.\n\n## Config edit\n\nConfig lives in three files; on key collision, later entries in this\nprecedence order override earlier ones.\n\nThe kimi-code runtime resolves the user-global directory as `KIMI_CODE_HOME`\nfirst, falling back to `~/.kimi-code`. Before touching the user-global file,\nresolve the actual directory with Bash so you don't read or write the wrong\none. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code`\nwhen it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the\nrest of this skill, `<KIMI_CODE_HOME>` means that resolved data root —\n**never assume `~/.kimi-code`**.\n\n- User-global: `<KIMI_CODE_HOME>/mcp.json`. Use for servers you want\n everywhere.\n- Project-root: `<project root>/.mcp.json`, where project root is found\n by walking up from `<cwd>` to the nearest `.git`. Use for\n Claude-compatible, repo-shared, or cross-agent servers.\n- Project-local: `<cwd>/.kimi-code/mcp.json`. Use for Kimi-specific\n overrides in the current working directory.\n\nMention once that project-root and project-local stdio entries spawn\ncommands at session start, so they should only live in trusted repos.\n\nAll three files wrap their entries the same way:\n\n```json\n{ \"mcpServers\": { \"<name>\": { /* entry */ } } }\n```\n\nA minimal stdio entry needs `command` (+ optional `args`, `env`, `cwd`).\nFor project-root `.mcp.json`, stdio entries run from the project root by\ndefault; relative `cwd` values are resolved against the directory that\ncontains `.mcp.json`.\nA minimal http entry needs `url`; add `bearerTokenEnvVar: \"ENV_NAME\"` for\nservers that authenticate with a static bearer token from the\nenvironment. Servers that use OAuth take no token field — the login flow\nabove handles them. `transport` is inferred from `command` vs `url`, so\nomit it. For less common fields (`enabled`, `startupTimeoutMs`,\n`toolTimeoutMs`, `enabledTools`, `disabledTools`, `headers`) the source of\ntruth is `McpServerStdioConfigSchema` / `McpServerHttpConfigSchema` in\n`packages/agent-core/src/config/schema.ts`.\n\nIf the user only wants to **see** what's configured, read all three files,\nshow a merged view with enough source-path context to inspect or remove a\nserver from the file that actually declared it, and stop — no scope\nprompt, no write.\n\nFor changes, the flow is:\n\n1. **Pick a scope.** Infer it from the user's words when you can\n (global / everywhere / all projects → user-global; root / repo /\n shared / cross-agent / Claude / `.mcp.json` → project-root; cwd /\n current directory / Kimi-specific / `.kimi-code` → project-local). When\n the request is genuinely scope-less, use one `AskUserQuestion` to ask\n user-global vs project-root vs project-local, defaulting to\n user-global. Use plain text for every other question — `AskUserQuestion`\n is a poor fit for free-form input. If the user dismisses the scope\n question, stop; you can't safely guess where they wanted the change.\n2. **Read and announce.** Read the target file (a missing or empty file\n is fine; you'll create `{ \"mcpServers\": {} }`). If JSON parsing fails,\n surface the error verbatim and stop — silently overwriting a broken\n file could destroy work. Then show the user the target path, what's\n currently in it, and the entry you're about to write or delete. This\n is for transparency, not a confirmation gate — the Edit/Write\n permission prompt is the real gate, and your message is what gives\n the user context when that prompt appears. In yolo / afk modes there\n is no prompt, which is those modes' explicit contract.\n3. **Write and tell them how to reload MCP servers.** Preserve unrelated\n entries and the `mcpServers` wrapper. MCP servers load at session\n start, so tell the user to start a new session (for example `/new`) or\n restart `kimi-code` for the change to take effect.\n\n## Secrets\n\nDon't store secrets (tokens, keys, passwords) as literals in\n`mcp.json` — it's a plain config file on disk. http servers should use\n`bearerTokenEnvVar` to reference an env var instead; if a stdio entry\nmust inline one in `env`, warn the user before writing.\n";
|
|
65746
65846
|
//#endregion
|
|
65747
65847
|
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.ts
|
|
65748
65848
|
const PSEUDO_PATH$1 = "builtin://mcp-config";
|
|
@@ -65799,7 +65899,7 @@ const SUB_SKILL_REVIEW = makeBuiltin(SKILL_default$1, "sub-skill.review", "built
|
|
|
65799
65899
|
const SUB_SKILL_CONSOLIDATE = makeBuiltin(SKILL_default$2, "sub-skill.consolidate", "builtin://sub-skill/consolidate", { disableModelInvocation: true });
|
|
65800
65900
|
//#endregion
|
|
65801
65901
|
//#region ../../packages/agent-core/src/skill/builtin/update-config.md
|
|
65802
|
-
var update_config_default = "---\nname: update-config\ndescription: Inspect or edit kimi-code's own config — `config.toml` (model, provider, permission, hooks) and `tui.toml` (theme, editor, notifications, auto-update). Use when the user asks what a setting does or wants to change one.\n---\n\n# Configure kimi-code (update-config)\n\nHelp the user inspect, change, and validate kimi-code's configuration files. The files are **TOML** with **snake_case** keys.\n\n## The two config files\n\nkimi-code has two TOML config files, both under
|
|
65902
|
+
var update_config_default = "---\nname: update-config\ndescription: Inspect or edit kimi-code's own config — `config.toml` (model, provider, permission, hooks) and `tui.toml` (theme, editor, notifications, auto-update). Use when the user asks what a setting does or wants to change one.\n---\n\n# Configure kimi-code (update-config)\n\nHelp the user inspect, change, and validate kimi-code's configuration files. The files are **TOML** with **snake_case** keys.\n\n## The two config files\n\nkimi-code has two TOML config files, both under `<KIMI_CODE_HOME>/`, both snake_case, but with different ownership — decide which one the user means before doing anything.\n\nThe runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Before doing anything, resolve the actual directory with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved root — **never assume `~/.kimi-code`**.\n\n- **`config.toml`** — agent / runtime settings: `default_model`, `providers`, `models`, `thinking`, `permission`, `hooks`, `loop_control`, etc.\n- **`tui.toml`** — terminal-UI / client preferences: `theme`, `[editor].command`, `[notifications]`, `[upgrade].auto_install` (auto-update). These can usually also be changed with the interactive commands `/config`, `/theme`, `/editor`, which is easier — prefer pointing the user at those.\n\nThe \"read → copy → Edit → validate → back up → overwrite\" flow below applies to both files; only **which reload command applies** differs (see Capability 4).\n\n## Prerequisite 1: the official docs are the single source of truth\n\nBefore touching any config, use **FetchURL** to fetch the official config docs as the one authoritative reference for fields (key names, types, allowed values, owning section):\n\n```\nhttps://moonshotai.github.io/kimi-code/en/configuration/config-files.html\n```\n\n- Use the **snake_case key names and sections exactly as documented** — don't invent them, don't guess camelCase.\n- If FetchURL is unavailable or the fetch fails, tell the user plainly that you can't reach the online docs, and ask them to paste the relevant section or confirm whether to proceed from what you already know. **Never edit blindly without an authoritative reference.**\n\n## Prerequisite 2: read the target file before any change\n\nBefore any modification, use **Read** on the target config file (decide whether it's `config.toml` or `tui.toml` per the above):\n\n- Location: `<KIMI_CODE_HOME>/config.toml` or `<KIMI_CODE_HOME>/tui.toml`. For other scopes/files, defer to the official docs.\n- A missing or empty file is fine — you'll create a minimal skeleton later.\n- If the file exists but **fails to parse as TOML**, report the error verbatim and **stop** — never overwrite a broken file in place (it could destroy the user's existing config).\n\n---\n\n## Capability 1: explain configuration (read-only, no file changes)\n\nWhen the user asks \"what config is there\", \"what does this setting do\", or \"how do I use it\":\n\n1. Fetch the official docs (Prerequisite 1).\n2. Read the current `config.toml` (Prerequisite 2).\n3. Answer against both: list the relevant sections / keys, what each is for, **current value vs default**, and the allowed-value range; say which file and section each lives in.\n4. Present it as a compact grouped list or table. **Stay read-only — write no files.**\n\n## Capability 2: make changes for the user (copy → Edit → validate → back up → overwrite)\n\nDon't edit the target file in place, and **don't rewrite it from scratch** — instead copy it, Edit the copy, and keep the original out of any broken state the whole time:\n\n1. **Clarify intent**: which key, what value, and which file (`config.toml` or `tui.toml`). Ask in one line if ambiguous; for discrete choices (e.g. scope) AskUserQuestion is fine, but use plain questions for free-form input.\n2. **Read the target file** (Prerequisite 2): Read it to understand the current state and confirm it parses.\n3. **Copy out a candidate (do not create from scratch)**: use **Bash** to copy the target verbatim — `cp config.toml config-new.toml` (same directory, `-new` suffix; for tui.toml, `cp tui.toml tui-new.toml`). **Leave the original untouched for now.**\n - Only when the target doesn't exist (nothing to copy) should you use **Write** to create a minimal skeleton candidate (e.g. just the comment line `# <KIMI_CODE_HOME>/config.toml`).\n4. **Edit the candidate**: use the **Edit** tool on the candidate to **change/add only the target key** — never rewrite the whole file. That way every existing section, entry, comment, and bit of formatting stays exactly as-is; only what should change changes. The candidate is identical to the original, so use the content you read in step 2 to locate the Edit anchor. Check the change against the official docs (key / section / value type / allowed values, snake_case).\n5. **Validate the candidate** (see Capability 3, via `kimi doctor`). **If anything fails, keep Editing the candidate and re-validate, looping until it all passes.**\n6. **Back up and overwrite** (only after validation fully passes):\n - **Back up the old file — always create a new timestamped backup, keep all of them, never overwrite an existing backup.** Copy this exactly with **Bash** (for config.toml): `cp config.toml \"config.toml.$(date +%Y%m%d-%H%M%S).bak\"`; for tui.toml: `cp tui.toml \"tui.toml.$(date +%Y%m%d-%H%M%S).bak\"`. Skip the backup only if the target didn't exist.\n - Overwrite with the candidate: `mv config-new.toml config.toml`.\n - If reload errors after the overwrite, the user can recover from **the most recent timestamped backup**.\n7. Tell the user how to apply it (see Capability 4).\n\n## Capability 3: validate the candidate file (must pass before overwrite)\n\nUse **`kimi doctor`** to validate the candidate you wrote — it doesn't start the TUI and doesn't modify any file; it runs kimi's own parser + schema (syntax and schema together), so it's the authoritative check. Pick the subcommand by which file you changed, and pass the **candidate** path explicitly:\n\n- changed `config.toml` → `kimi doctor config <config-new.toml path>`\n- changed `tui.toml` → `kimi doctor tui <tui-new.toml path>`\n\nWhen a path is passed explicitly the file must exist (your candidate does, so that's fine). **Exit code 0 = pass (valid or skipped); non-zero = a specified file is missing or the config is invalid** — show the output verbatim, fix the candidate, and re-run, looping until it's 0.\n\nThen do two checks `kimi doctor` can't:\n\n1. **Cross-check values against the official docs** (single source of truth): are the key / section / enum values as documented, and snake_case? doctor guarantees \"schema-valid\", but \"valid yet not what the user wanted\" (e.g. a misspelled model alias) needs the docs.\n2. **Completeness**: every existing entry is still present (the candidate fully replaces the target — a dropped line is a deletion).\n\n> To also check whether the currently **active** config is OK overall, run `kimi doctor` with no path (it checks the default `config.toml` + `tui.toml`, showing a missing one as skipped).\n\n## Capability 4: tell the user how to apply changes\n\nOnce local validation passes, tell the user how to make the change take effect — **the reload command depends on which file you changed**:\n\n- changed **`config.toml`** → run **`/reload`** in the TUI (reloads the session and applies `config.toml`; it also reloads `tui.toml`).\n- changed **`tui.toml`** → run **`/reload-tui`** (reloads only `tui.toml`, lighter); `/reload` works too (reloads both).\n- changed both → a single **`/reload`** covers it.\n\nNote: `/reload` is available **only when idle** — if a reply is streaming, press Esc / Ctrl-C to stop first. `kimi doctor` already validated the schema before the overwrite, so reload should apply cleanly; if it still errors, follow the message to fix it or recover from the most recent timestamped backup. If you don't want to reload now, the **next new session** picks it up automatically.\n\n## Don'ts\n\n- **Always back up before overwriting**, with a **timestamped name and all history kept** — don't skip the backup, don't keep only a single `.bak`, don't overwrite an old backup.\n- Don't drop unrelated entries (the candidate fully replaces the target — a dropped line is a deletion).\n- When you can't reach the docs / have no authoritative reference, don't edit by guessing.\n";
|
|
65803
65903
|
//#endregion
|
|
65804
65904
|
//#region ../../packages/agent-core/src/skill/builtin/update-config.ts
|
|
65805
65905
|
const PSEUDO_PATH = "builtin://update-config";
|
|
@@ -73815,6 +73915,10 @@ function abortedToolOutput(toolName, signal) {
|
|
|
73815
73915
|
}
|
|
73816
73916
|
async function runToolCallBatch(step, response) {
|
|
73817
73917
|
if (response.toolCalls.length === 0) return { stopTurn: false };
|
|
73918
|
+
const batchStep = {
|
|
73919
|
+
...step,
|
|
73920
|
+
toolCalls: response.toolCalls
|
|
73921
|
+
};
|
|
73818
73922
|
const calls = response.toolCalls.map((toolCall) => preflightToolCall(step.tools, toolCall));
|
|
73819
73923
|
const scheduler = new ToolScheduler();
|
|
73820
73924
|
const pendingResults = [];
|
|
@@ -73822,19 +73926,19 @@ async function runToolCallBatch(step, response) {
|
|
|
73822
73926
|
try {
|
|
73823
73927
|
for (let index = 0; index < calls.length; index += 1) {
|
|
73824
73928
|
const call = calls[index];
|
|
73825
|
-
const prepared = await prepareToolCall(
|
|
73929
|
+
const prepared = await prepareToolCall(batchStep, call);
|
|
73826
73930
|
pendingResults.push(scheduler.add(prepared.task));
|
|
73827
73931
|
if (prepared.stopBatchAfterThis === true) {
|
|
73828
73932
|
stopTurn = true;
|
|
73829
73933
|
for (const skippedCall of calls.slice(index + 1)) {
|
|
73830
|
-
const skippedTask = await prepareSkippedToolCall(
|
|
73934
|
+
const skippedTask = await prepareSkippedToolCall(batchStep, skippedCall);
|
|
73831
73935
|
pendingResults.push(scheduler.add(skippedTask));
|
|
73832
73936
|
}
|
|
73833
73937
|
break;
|
|
73834
73938
|
}
|
|
73835
73939
|
}
|
|
73836
73940
|
for (const pendingResult of pendingResults) {
|
|
73837
|
-
const result = await finalizePendingToolResult(
|
|
73941
|
+
const result = await finalizePendingToolResult(batchStep, await pendingResult);
|
|
73838
73942
|
if (result.stopTurn === true) stopTurn = true;
|
|
73839
73943
|
await step.dispatchEvent({
|
|
73840
73944
|
type: "tool.result",
|
|
@@ -73989,6 +74093,7 @@ async function runPrepareToolExecutionHook(step, call) {
|
|
|
73989
74093
|
try {
|
|
73990
74094
|
hookResult = await hooks.prepareToolExecution({
|
|
73991
74095
|
toolCall,
|
|
74096
|
+
toolCalls: step.toolCalls,
|
|
73992
74097
|
tool: call.tool,
|
|
73993
74098
|
args,
|
|
73994
74099
|
turnId,
|
|
@@ -74031,6 +74136,7 @@ async function runAuthorizeToolExecutionHook(step, call, args, execution) {
|
|
|
74031
74136
|
try {
|
|
74032
74137
|
return await hooks.authorizeToolExecution({
|
|
74033
74138
|
toolCall: call.toolCall,
|
|
74139
|
+
toolCalls: step.toolCalls,
|
|
74034
74140
|
tool: call.tool,
|
|
74035
74141
|
args,
|
|
74036
74142
|
execution,
|
|
@@ -74086,6 +74192,7 @@ async function finalizePendingToolResult(step, pendingResult) {
|
|
|
74086
74192
|
try {
|
|
74087
74193
|
const effectiveResult = coerceToolResult(await hooks.finalizeToolResult({
|
|
74088
74194
|
toolCall: pendingResult.toolCall,
|
|
74195
|
+
toolCalls: step.toolCalls,
|
|
74089
74196
|
args: pendingResult.args,
|
|
74090
74197
|
result: pendingResult.result,
|
|
74091
74198
|
turnId,
|
|
@@ -81732,7 +81839,7 @@ function buildSubagentDescriptions(subagents) {
|
|
|
81732
81839
|
}
|
|
81733
81840
|
//#endregion
|
|
81734
81841
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.md
|
|
81735
|
-
var agent_swarm_default = "Launch multiple subagents from one prompt template, existing agent resumes, or both.\n\nUse AgentSwarm when many subagents should run the same kind of task over different inputs. The placeholder is exactly `{{item}}`. For example, with `prompt_template` set to `Review {{item}} for likely regressions.` and `items` set to `[\"src/a.ts\", \"src/b.ts\"]`, AgentSwarm launches two new subagents with those two concrete prompts.\n\nUse `resume_agent_ids` to continue subagents that already exist from earlier work, such as ones that failed or timed out: map each agent id to the prompt for that resumed subagent (usually `continue` if no extra information is needed). You may combine `resume_agent_ids` with `items` in the same call to resume existing subagents and launch new ones. Do not duplicate resumed work in `items`.\n\nUse enough subagents to keep the work focused and parallel. AgentSwarm supports up to 128 subagents, and launches are queued automatically, so it is safe to split large tasks into many clear, independent items.\n";
|
|
81842
|
+
var agent_swarm_default = "Launch multiple subagents from one prompt template, existing agent resumes, or both.\n\nUse AgentSwarm when many subagents should run the same kind of task over different inputs. The placeholder is exactly `{{item}}`. For example, with `prompt_template` set to `Review {{item}} for likely regressions.` and `items` set to `[\"src/a.ts\", \"src/b.ts\"]`, AgentSwarm launches two new subagents with those two concrete prompts.\n\nUse `resume_agent_ids` to continue subagents that already exist from earlier work, such as ones that failed or timed out: map each agent id to the prompt for that resumed subagent (usually `continue` if no extra information is needed). You may combine `resume_agent_ids` with `items` in the same call to resume existing subagents and launch new ones. Do not duplicate resumed work in `items`.\n\nUse enough subagents to keep the work focused and parallel. AgentSwarm supports up to 128 subagents, and launches are queued automatically, so it is safe to split large tasks into many clear, independent items.\n\nIf `AgentSwarm` is called, that call must be the only tool call in the response.\n";
|
|
81736
81843
|
//#endregion
|
|
81737
81844
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.ts
|
|
81738
81845
|
const DEFAULT_SUBAGENT_TYPE = "coder";
|
|
@@ -90238,7 +90345,8 @@ var BashTool = class {
|
|
|
90238
90345
|
}, timeoutMs);
|
|
90239
90346
|
try {
|
|
90240
90347
|
const builder = new ToolResultBuilder();
|
|
90241
|
-
const
|
|
90348
|
+
const isTerminating = () => timedOut || aborted || killed;
|
|
90349
|
+
const [, exitCode] = await Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder, isTerminating), readStreamIntoBuilder(proc.stderr, builder, isTerminating)]), proc.wait()]);
|
|
90242
90350
|
if (timedOut) {
|
|
90243
90351
|
const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
|
|
90244
90352
|
return builder.error(`Command killed by timeout (${timeoutLabel})`, { brief: `Killed by timeout (${timeoutLabel})` });
|
|
@@ -90301,7 +90409,7 @@ var BashTool = class {
|
|
|
90301
90409
|
const info = backgroundManager.getTask(taskId);
|
|
90302
90410
|
if (info && info.status === "running") backgroundManager.stop(taskId, "Timed out");
|
|
90303
90411
|
})();
|
|
90304
|
-
}, timeoutMs);
|
|
90412
|
+
}, timeoutMs).unref?.();
|
|
90305
90413
|
const status = backgroundManager.getTask(taskId).status;
|
|
90306
90414
|
const builder = new ToolResultBuilder();
|
|
90307
90415
|
builder.write(`task_id: ${taskId}\npid: ${String(proc.pid)}\ndescription: ${args.description.trim()}\nstatus: ${status}\nautomatic_notification: true\nnext_step: You will be automatically notified when it completes.
|
|
@@ -90311,14 +90419,21 @@ human_shell_hint: Tell the human to run /tasks to open the interactive backgroun
|
|
|
90311
90419
|
return builder.ok("Background task started", { brief: `Started ${taskId}` });
|
|
90312
90420
|
}
|
|
90313
90421
|
};
|
|
90314
|
-
async function readStreamIntoBuilder(stream, builder) {
|
|
90422
|
+
async function readStreamIntoBuilder(stream, builder, suppressPrematureClose) {
|
|
90315
90423
|
const decoder = new StringDecoder("utf8");
|
|
90316
|
-
|
|
90317
|
-
|
|
90318
|
-
|
|
90424
|
+
try {
|
|
90425
|
+
for await (const chunk of stream) {
|
|
90426
|
+
const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
90427
|
+
builder.write(decoder.write(buf));
|
|
90428
|
+
}
|
|
90429
|
+
} catch (error) {
|
|
90430
|
+
if (!isPrematureCloseError(error) || suppressPrematureClose?.() !== true) throw error;
|
|
90319
90431
|
}
|
|
90320
90432
|
builder.write(decoder.end());
|
|
90321
90433
|
}
|
|
90434
|
+
function isPrematureCloseError(error) {
|
|
90435
|
+
return error instanceof Error && error.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
90436
|
+
}
|
|
90322
90437
|
function shellQuote(s) {
|
|
90323
90438
|
return `'${s.replaceAll("'", "'\\''")}'`;
|
|
90324
90439
|
}
|
|
@@ -121101,6 +121216,32 @@ var McpConnectionManager = class {
|
|
|
121101
121216
|
this.initialLoad = initialLoad;
|
|
121102
121217
|
return initialLoad;
|
|
121103
121218
|
}
|
|
121219
|
+
async connect(name, config) {
|
|
121220
|
+
const previous = this.entries.get(name);
|
|
121221
|
+
if (previous !== void 0) await this.closeClient(previous);
|
|
121222
|
+
const disabled = config.enabled === false;
|
|
121223
|
+
const entry = {
|
|
121224
|
+
name,
|
|
121225
|
+
config,
|
|
121226
|
+
attemptId: 0,
|
|
121227
|
+
status: disabled ? "disabled" : "pending"
|
|
121228
|
+
};
|
|
121229
|
+
this.entries.set(name, entry);
|
|
121230
|
+
this.emit(entry);
|
|
121231
|
+
if (!disabled) await this.connectOne(entry, this.beginConnectAttempt(entry));
|
|
121232
|
+
}
|
|
121233
|
+
async remove(name) {
|
|
121234
|
+
const entry = this.entries.get(name);
|
|
121235
|
+
if (entry === void 0) return false;
|
|
121236
|
+
await this.closeClient(entry);
|
|
121237
|
+
entry.status = "disabled";
|
|
121238
|
+
entry.tools = void 0;
|
|
121239
|
+
entry.enabledNames = void 0;
|
|
121240
|
+
entry.error = void 0;
|
|
121241
|
+
this.emit(entry);
|
|
121242
|
+
this.entries.delete(name);
|
|
121243
|
+
return true;
|
|
121244
|
+
}
|
|
121104
121245
|
waitForInitialLoad(signal) {
|
|
121105
121246
|
signal?.throwIfAborted();
|
|
121106
121247
|
if (signal === void 0) return this.initialLoad;
|
|
@@ -121450,6 +121591,20 @@ function mergeCallerMcpServers(base, callerServers) {
|
|
|
121450
121591
|
//#endregion
|
|
121451
121592
|
//#region ../../packages/agent-core/src/session/index.ts
|
|
121452
121593
|
const BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV = "KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT";
|
|
121594
|
+
const ACTIVE_TURN_CLOSE_TIMEOUT_MS = 8e3;
|
|
121595
|
+
async function waitForSettlementOrTimeout(promise, timeoutMs) {
|
|
121596
|
+
let timeout;
|
|
121597
|
+
try {
|
|
121598
|
+
return await Promise.race([promise.then(() => true, () => true), new Promise((resolve) => {
|
|
121599
|
+
timeout = setTimeout(() => {
|
|
121600
|
+
resolve(false);
|
|
121601
|
+
}, timeoutMs);
|
|
121602
|
+
timeout.unref?.();
|
|
121603
|
+
})]);
|
|
121604
|
+
} finally {
|
|
121605
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
121606
|
+
}
|
|
121607
|
+
}
|
|
121453
121608
|
var Session$1 = class {
|
|
121454
121609
|
options;
|
|
121455
121610
|
rpc;
|
|
@@ -121541,6 +121696,7 @@ var Session$1 = class {
|
|
|
121541
121696
|
async close() {
|
|
121542
121697
|
try {
|
|
121543
121698
|
await Promise.allSettled(Array.from(this.readyAgents(), async (agent) => agent.cron?.stop()));
|
|
121699
|
+
await this.cancelActiveTurnsOnClose();
|
|
121544
121700
|
await this.stopBackgroundTasksOnExit();
|
|
121545
121701
|
await this.flushMetadata();
|
|
121546
121702
|
await this.triggerSessionEnd("exit");
|
|
@@ -121564,12 +121720,46 @@ var Session$1 = class {
|
|
|
121564
121720
|
}
|
|
121565
121721
|
}
|
|
121566
121722
|
}
|
|
121723
|
+
async cancelActiveTurnsOnClose() {
|
|
121724
|
+
const backgroundAgentIds = this.activeBackgroundAgentIds();
|
|
121725
|
+
const cancellations = [];
|
|
121726
|
+
for (const [agentId, entry] of this.agents) {
|
|
121727
|
+
if (!(entry instanceof Agent$1) || backgroundAgentIds.has(agentId)) continue;
|
|
121728
|
+
cancellations.push(this.cancelAgentTurnOnClose(entry));
|
|
121729
|
+
}
|
|
121730
|
+
await Promise.allSettled(cancellations);
|
|
121731
|
+
}
|
|
121732
|
+
activeBackgroundAgentIds() {
|
|
121733
|
+
const agentIds = /* @__PURE__ */ new Set();
|
|
121734
|
+
for (const agent of this.readyAgents()) for (const task of agent.background.list(true)) if (task.kind === "agent" && task.agentId !== void 0) agentIds.add(task.agentId);
|
|
121735
|
+
return agentIds;
|
|
121736
|
+
}
|
|
121737
|
+
async cancelAgentTurnOnClose(agent) {
|
|
121738
|
+
if (!agent.turn.hasActiveTurn) return;
|
|
121739
|
+
let waitForTurn;
|
|
121740
|
+
try {
|
|
121741
|
+
waitForTurn = agent.turn.waitForCurrentTurn();
|
|
121742
|
+
} catch (error) {
|
|
121743
|
+
this.log.debug("active turn wait unavailable during session close", {
|
|
121744
|
+
agentType: agent.type,
|
|
121745
|
+
agentHomedir: agent.homedir,
|
|
121746
|
+
error
|
|
121747
|
+
});
|
|
121748
|
+
return;
|
|
121749
|
+
}
|
|
121750
|
+
agent.turn.cancel(void 0, abortError$1("Session closed"));
|
|
121751
|
+
if (!await waitForSettlementOrTimeout(waitForTurn, ACTIVE_TURN_CLOSE_TIMEOUT_MS)) this.log.warn("timed out waiting for active turn to cancel during session close", {
|
|
121752
|
+
agentType: agent.type,
|
|
121753
|
+
agentHomedir: agent.homedir,
|
|
121754
|
+
timeoutMs: ACTIVE_TURN_CLOSE_TIMEOUT_MS
|
|
121755
|
+
});
|
|
121756
|
+
}
|
|
121567
121757
|
async stopBackgroundTasksOnExit() {
|
|
121568
121758
|
if (resolveConfigValue({
|
|
121569
121759
|
env: process.env,
|
|
121570
121760
|
envKey: BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV,
|
|
121571
121761
|
configValue: this.options.background?.keepAliveOnExit,
|
|
121572
|
-
defaultValue:
|
|
121762
|
+
defaultValue: false,
|
|
121573
121763
|
parseEnv: parseBooleanEnv
|
|
121574
121764
|
})) return;
|
|
121575
121765
|
await Promise.all(Array.from(this.readyAgents(), (agent) => agent.background.stopAll("Session closed")));
|
|
@@ -136951,6 +137141,7 @@ var ProviderManager = class {
|
|
|
136951
137141
|
providerName,
|
|
136952
137142
|
provider,
|
|
136953
137143
|
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
137144
|
+
alwaysThinking: (alias.capabilities ?? []).some((c) => c.trim().toLowerCase() === "always_thinking"),
|
|
136954
137145
|
maxOutputSize: alias.maxOutputSize
|
|
136955
137146
|
};
|
|
136956
137147
|
}
|
|
@@ -138335,9 +138526,11 @@ var LocalKaos = class LocalKaos {
|
|
|
138335
138526
|
name = "local";
|
|
138336
138527
|
osEnv;
|
|
138337
138528
|
_cwd;
|
|
138338
|
-
|
|
138529
|
+
_envLayers;
|
|
138530
|
+
constructor(osEnv, cwd, envLayers = []) {
|
|
138339
138531
|
this._cwd = normalize(cwd ?? process.cwd());
|
|
138340
138532
|
this.osEnv = osEnv;
|
|
138533
|
+
this._envLayers = envLayers;
|
|
138341
138534
|
}
|
|
138342
138535
|
/**
|
|
138343
138536
|
* Construct a fresh `LocalKaos` after probing the host environment.
|
|
@@ -138350,7 +138543,10 @@ var LocalKaos = class LocalKaos {
|
|
|
138350
138543
|
return new LocalKaos(await detectEnvironmentFromNode());
|
|
138351
138544
|
}
|
|
138352
138545
|
withCwd(cwd) {
|
|
138353
|
-
return new LocalKaos(this.osEnv, cwd);
|
|
138546
|
+
return new LocalKaos(this.osEnv, cwd, this._envLayers);
|
|
138547
|
+
}
|
|
138548
|
+
withEnv(env) {
|
|
138549
|
+
return new LocalKaos(this.osEnv, this._cwd, [...this._envLayers, env]);
|
|
138354
138550
|
}
|
|
138355
138551
|
_resolvePath(path) {
|
|
138356
138552
|
if (isAbsolute(path)) return normalize(path);
|
|
@@ -138543,7 +138739,8 @@ var LocalKaos = class LocalKaos {
|
|
|
138543
138739
|
"pipe",
|
|
138544
138740
|
"pipe"
|
|
138545
138741
|
],
|
|
138546
|
-
detached: !isWindows
|
|
138742
|
+
detached: !isWindows,
|
|
138743
|
+
env: this._buildExecEnv()
|
|
138547
138744
|
});
|
|
138548
138745
|
await waitForSpawn(child);
|
|
138549
138746
|
return new LocalProcess(child);
|
|
@@ -138559,11 +138756,20 @@ var LocalKaos = class LocalKaos {
|
|
|
138559
138756
|
"pipe"
|
|
138560
138757
|
],
|
|
138561
138758
|
detached: !isWindows,
|
|
138562
|
-
env
|
|
138759
|
+
env: this._buildExecEnv(env)
|
|
138563
138760
|
});
|
|
138564
138761
|
await waitForSpawn(child);
|
|
138565
138762
|
return new LocalProcess(child);
|
|
138566
138763
|
}
|
|
138764
|
+
_buildExecEnv(invocationEnv) {
|
|
138765
|
+
if (this._envLayers.length === 0) return invocationEnv;
|
|
138766
|
+
const merged = {
|
|
138767
|
+
...process.env,
|
|
138768
|
+
...invocationEnv
|
|
138769
|
+
};
|
|
138770
|
+
for (const layer of this._envLayers) Object.assign(merged, layer);
|
|
138771
|
+
return merged;
|
|
138772
|
+
}
|
|
138567
138773
|
};
|
|
138568
138774
|
function waitForSpawn(child) {
|
|
138569
138775
|
return new Promise((resolve, reject) => {
|
|
@@ -139649,8 +139855,8 @@ var KimiHarness = class {
|
|
|
139649
139855
|
get interactiveAgentId() {
|
|
139650
139856
|
return this.rpc.interactiveAgentId;
|
|
139651
139857
|
}
|
|
139652
|
-
|
|
139653
|
-
this.rpc.
|
|
139858
|
+
withInteractiveAgent(agentId, fn) {
|
|
139859
|
+
return this.rpc.withInteractiveAgent(agentId, fn);
|
|
139654
139860
|
}
|
|
139655
139861
|
track(event, properties) {
|
|
139656
139862
|
this.telemetry.track(event, properties);
|
|
@@ -142102,7 +142308,19 @@ function managedModelKey(modelId) {
|
|
|
142102
142308
|
}
|
|
142103
142309
|
function capabilitiesForModel$1(model) {
|
|
142104
142310
|
const caps = /* @__PURE__ */ new Set();
|
|
142105
|
-
|
|
142311
|
+
switch (model.supportsThinkingType) {
|
|
142312
|
+
case "only":
|
|
142313
|
+
caps.add("thinking");
|
|
142314
|
+
caps.add("always_thinking");
|
|
142315
|
+
break;
|
|
142316
|
+
case "both":
|
|
142317
|
+
caps.add("thinking");
|
|
142318
|
+
break;
|
|
142319
|
+
case "no": break;
|
|
142320
|
+
case void 0:
|
|
142321
|
+
if (model.supportsReasoning) caps.add("thinking");
|
|
142322
|
+
break;
|
|
142323
|
+
}
|
|
142106
142324
|
if (model.supportsImageIn) caps.add("image_in");
|
|
142107
142325
|
if (model.supportsVideoIn) caps.add("video_in");
|
|
142108
142326
|
if (model.supportsToolUse ?? true) caps.add("tool_use");
|
|
@@ -142245,9 +142463,13 @@ function toModelInfo$1(item) {
|
|
|
142245
142463
|
supportsImageIn: Boolean(item["supports_image_in"]),
|
|
142246
142464
|
supportsVideoIn: Boolean(item["supports_video_in"]),
|
|
142247
142465
|
supportsToolUse,
|
|
142466
|
+
supportsThinkingType: parseSupportsThinkingType(item["supports_thinking_type"]),
|
|
142248
142467
|
displayName: normalizedDisplayName
|
|
142249
142468
|
};
|
|
142250
142469
|
}
|
|
142470
|
+
function parseSupportsThinkingType(value) {
|
|
142471
|
+
return value === "only" || value === "no" || value === "both" ? value : void 0;
|
|
142472
|
+
}
|
|
142251
142473
|
async function fetchManagedKimiCodeModels(options) {
|
|
142252
142474
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
142253
142475
|
const baseUrl = defaultBaseUrl(options.baseUrl);
|
|
@@ -142336,6 +142558,11 @@ function applyManagedKimiCodeLogoutConfig(config) {
|
|
|
142336
142558
|
if (Object.keys(config.services).length === 0) config.services = void 0;
|
|
142337
142559
|
}
|
|
142338
142560
|
}
|
|
142561
|
+
function forcedThinking(model, fallback) {
|
|
142562
|
+
if (model?.supportsThinkingType === "only") return true;
|
|
142563
|
+
if (model?.supportsThinkingType === "no") return false;
|
|
142564
|
+
return fallback;
|
|
142565
|
+
}
|
|
142339
142566
|
function selectDefaultModel(config, models, options) {
|
|
142340
142567
|
const firstModel = models[0];
|
|
142341
142568
|
if (firstModel === void 0) throw new Error("No models available for Kimi Code.");
|
|
@@ -142346,12 +142573,12 @@ function selectDefaultModel(config, models, options) {
|
|
|
142346
142573
|
const preservedModel = managedModels.get(currentDefault);
|
|
142347
142574
|
return {
|
|
142348
142575
|
modelKey: currentDefault,
|
|
142349
|
-
thinking: config.defaultThinking ?? preservedModel?.supportsReasoning ?? false
|
|
142576
|
+
thinking: forcedThinking(preservedModel, config.defaultThinking ?? preservedModel?.supportsReasoning ?? false)
|
|
142350
142577
|
};
|
|
142351
142578
|
}
|
|
142352
142579
|
return {
|
|
142353
142580
|
modelKey: managedModelKey(firstModel.id),
|
|
142354
|
-
thinking: config.defaultThinking ?? firstModel.supportsReasoning
|
|
142581
|
+
thinking: forcedThinking(firstModel, config.defaultThinking ?? firstModel.supportsReasoning)
|
|
142355
142582
|
};
|
|
142356
142583
|
}
|
|
142357
142584
|
function canPreserveDefaultModel(existingModels, defaultModel, managedModels) {
|
|
@@ -142463,12 +142690,25 @@ function toModelInfo(item) {
|
|
|
142463
142690
|
supportsImageIn: Boolean(item["supports_image_in"]),
|
|
142464
142691
|
supportsVideoIn: Boolean(item["supports_video_in"]),
|
|
142465
142692
|
supportsToolUse,
|
|
142693
|
+
supportsThinkingType: parseSupportsThinkingType(item["supports_thinking_type"]),
|
|
142466
142694
|
displayName: normalizedDisplayName
|
|
142467
142695
|
};
|
|
142468
142696
|
}
|
|
142469
142697
|
function capabilitiesForModel(model) {
|
|
142470
142698
|
const caps = /* @__PURE__ */ new Set();
|
|
142471
|
-
|
|
142699
|
+
switch (model.supportsThinkingType) {
|
|
142700
|
+
case "only":
|
|
142701
|
+
caps.add("thinking");
|
|
142702
|
+
caps.add("always_thinking");
|
|
142703
|
+
break;
|
|
142704
|
+
case "both":
|
|
142705
|
+
caps.add("thinking");
|
|
142706
|
+
break;
|
|
142707
|
+
case "no": break;
|
|
142708
|
+
case void 0:
|
|
142709
|
+
if (model.supportsReasoning) caps.add("thinking");
|
|
142710
|
+
break;
|
|
142711
|
+
}
|
|
142472
142712
|
if (model.supportsImageIn) caps.add("image_in");
|
|
142473
142713
|
if (model.supportsVideoIn) caps.add("video_in");
|
|
142474
142714
|
if (model.supportsToolUse ?? true) caps.add("tool_use");
|
|
@@ -143100,10 +143340,16 @@ var KimiAuthFacade = class {
|
|
|
143100
143340
|
//#region ../../packages/node-sdk/src/rpc.ts
|
|
143101
143341
|
const MAIN_AGENT_ID$2 = "main";
|
|
143102
143342
|
var SDKRpcClientBase = class {
|
|
143103
|
-
|
|
143343
|
+
interactiveAgentScope = new AsyncLocalStorage();
|
|
143104
143344
|
eventListeners = /* @__PURE__ */ new Set();
|
|
143105
143345
|
approvalHandlers = /* @__PURE__ */ new Map();
|
|
143106
143346
|
questionHandlers = /* @__PURE__ */ new Map();
|
|
143347
|
+
get interactiveAgentId() {
|
|
143348
|
+
return this.interactiveAgentScope.getStore() ?? MAIN_AGENT_ID$2;
|
|
143349
|
+
}
|
|
143350
|
+
withInteractiveAgent(agentId, fn) {
|
|
143351
|
+
return this.interactiveAgentScope.run(agentId, fn);
|
|
143352
|
+
}
|
|
143107
143353
|
async createSession(input) {
|
|
143108
143354
|
const rpc = await this.getRpc();
|
|
143109
143355
|
const { planMode, ...coreInput } = input;
|
|
@@ -144401,6 +144647,7 @@ const FEEDBACK_VERSION_PREFIX = "kimi-code-";
|
|
|
144401
144647
|
const FEEDBACK_TELEMETRY_EVENT = "feedback_submitted";
|
|
144402
144648
|
const KIMI_CODE_CDN_BASE = "https://code.kimi.com/kimi-code";
|
|
144403
144649
|
const KIMI_CODE_CDN_LATEST_URL = `${KIMI_CODE_CDN_BASE}/latest`;
|
|
144650
|
+
const KIMI_CODE_TIPS_BANNER_URL = "https://cdn.kimi.com/kimi-code-tips/tips.json";
|
|
144404
144651
|
const KIMI_CODE_PLUGIN_MARKETPLACE_URL = `${KIMI_CODE_CDN_BASE}/plugins/marketplace.json`;
|
|
144405
144652
|
const KIMI_CODE_PLUGIN_MARKETPLACE_URL_ENV = "KIMI_CODE_PLUGIN_MARKETPLACE_URL";
|
|
144406
144653
|
const KIMI_CODE_INSTALL_SH_URL = `${KIMI_CODE_CDN_BASE}/install.sh`;
|
|
@@ -150542,6 +150789,9 @@ var AcpKaos = class AcpKaos {
|
|
|
150542
150789
|
withCwd(cwd) {
|
|
150543
150790
|
return new AcpKaos(this.conn, this.sessionId, this.inner.withCwd(cwd));
|
|
150544
150791
|
}
|
|
150792
|
+
withEnv(env) {
|
|
150793
|
+
return new AcpKaos(this.conn, this.sessionId, this.inner.withEnv(env));
|
|
150794
|
+
}
|
|
150545
150795
|
stat(path, options) {
|
|
150546
150796
|
return this.inner.stat(path, options);
|
|
150547
150797
|
}
|
|
@@ -150562,13 +150812,14 @@ var AcpKaos = class AcpKaos {
|
|
|
150562
150812
|
* should be routed through a non-ACP Kaos.
|
|
150563
150813
|
*/
|
|
150564
150814
|
async readText(path, _options) {
|
|
150815
|
+
const rpcPath = this.toClientPath(path);
|
|
150565
150816
|
try {
|
|
150566
150817
|
return (await this.conn.readTextFile({
|
|
150567
150818
|
sessionId: this.sessionId,
|
|
150568
|
-
path
|
|
150819
|
+
path: rpcPath
|
|
150569
150820
|
})).content;
|
|
150570
150821
|
} catch (err) {
|
|
150571
|
-
throw wrapKaosError(`acp: readTextFile failed for ${
|
|
150822
|
+
throw wrapKaosError(`acp: readTextFile failed for ${rpcPath}`, err);
|
|
150572
150823
|
}
|
|
150573
150824
|
}
|
|
150574
150825
|
/**
|
|
@@ -150646,16 +150897,21 @@ var AcpKaos = class AcpKaos {
|
|
|
150646
150897
|
return data.byteLength;
|
|
150647
150898
|
}
|
|
150648
150899
|
async acpWrite(path, content) {
|
|
150900
|
+
const rpcPath = this.toClientPath(path);
|
|
150649
150901
|
try {
|
|
150650
150902
|
await this.conn.writeTextFile({
|
|
150651
150903
|
sessionId: this.sessionId,
|
|
150652
|
-
path,
|
|
150904
|
+
path: rpcPath,
|
|
150653
150905
|
content
|
|
150654
150906
|
});
|
|
150655
150907
|
} catch (err) {
|
|
150656
|
-
throw wrapKaosError(`acp: writeTextFile failed for ${
|
|
150908
|
+
throw wrapKaosError(`acp: writeTextFile failed for ${rpcPath}`, err);
|
|
150657
150909
|
}
|
|
150658
150910
|
}
|
|
150911
|
+
toClientPath(path) {
|
|
150912
|
+
if (this.inner.pathClass() !== "win32") return path;
|
|
150913
|
+
return path.replaceAll("/", "\\");
|
|
150914
|
+
}
|
|
150659
150915
|
exec(...args) {
|
|
150660
150916
|
return this.inner.exec(...args);
|
|
150661
150917
|
}
|
|
@@ -151702,13 +151958,24 @@ function acpModeToToggles(id) {
|
|
|
151702
151958
|
*/
|
|
151703
151959
|
const TOGGLEABLE_THINKING_MODELS = new Set(["kimi-for-coding", "kimi-code"]);
|
|
151704
151960
|
function deriveThinkingSupported(alias) {
|
|
151705
|
-
|
|
151961
|
+
const declared = alias.capabilities ?? [];
|
|
151962
|
+
if (declared.includes("thinking") || declared.includes("always_thinking")) return true;
|
|
151706
151963
|
const lower = alias.model.toLowerCase();
|
|
151707
151964
|
if (lower.includes("thinking") || lower.includes("reason")) return true;
|
|
151708
151965
|
if (TOGGLEABLE_THINKING_MODELS.has(alias.model)) return true;
|
|
151709
151966
|
return false;
|
|
151710
151967
|
}
|
|
151711
151968
|
/**
|
|
151969
|
+
* Whether the alias declares the 'always_thinking' capability — the model
|
|
151970
|
+
* cannot run with thinking disabled, so the ACP toggle must lock to on.
|
|
151971
|
+
* Deliberately capability-only: the name heuristics above keep feeding
|
|
151972
|
+
* `thinkingSupported`, but only an explicit (server-derived) declaration
|
|
151973
|
+
* may remove the off option from the client.
|
|
151974
|
+
*/
|
|
151975
|
+
function deriveAlwaysThinking(alias) {
|
|
151976
|
+
return (alias.capabilities ?? []).includes("always_thinking");
|
|
151977
|
+
}
|
|
151978
|
+
/**
|
|
151712
151979
|
* Project `harness.getConfig().models` into a flat catalog. Returns an
|
|
151713
151980
|
* empty array when the harness has no models configured, when
|
|
151714
151981
|
* `getConfig` is missing on the harness (partial test stubs), or when
|
|
@@ -151729,7 +151996,8 @@ async function listModelsFromHarness(harness) {
|
|
|
151729
151996
|
for (const [id, alias] of Object.entries(models)) out.push({
|
|
151730
151997
|
id,
|
|
151731
151998
|
name: alias.displayName ?? alias.model ?? id,
|
|
151732
|
-
thinkingSupported: deriveThinkingSupported(alias)
|
|
151999
|
+
thinkingSupported: deriveThinkingSupported(alias),
|
|
152000
|
+
alwaysThinking: deriveAlwaysThinking(alias)
|
|
151733
152001
|
});
|
|
151734
152002
|
return out;
|
|
151735
152003
|
}
|
|
@@ -151788,8 +152056,26 @@ function buildModelOption(models, currentBaseModelId) {
|
|
|
151788
152056
|
* currently-selected model has `thinkingSupported === false`, the
|
|
151789
152057
|
* snapshot omits it entirely (dynamic visibility), so the client never
|
|
151790
152058
|
* shows a toggle that wouldn't do anything.
|
|
152059
|
+
*
|
|
152060
|
+
* `alwaysThinking` models (declared `always_thinking` capability — the
|
|
152061
|
+
* runtime cannot disable thinking) collapse the select to a single
|
|
152062
|
+
* locked `on` entry: the state stays visible to the client, but there
|
|
152063
|
+
* is no off option to pick. ACP has no "disabled entry" concept, so
|
|
152064
|
+
* omitting `off` is the wire-level equivalent of the TUI's greyed-out
|
|
152065
|
+
* `Off (Unsupported)` segment.
|
|
151791
152066
|
*/
|
|
151792
|
-
function buildThinkingOption(enabled) {
|
|
152067
|
+
function buildThinkingOption(enabled, alwaysThinking = false) {
|
|
152068
|
+
if (alwaysThinking) return {
|
|
152069
|
+
type: "select",
|
|
152070
|
+
id: "thinking",
|
|
152071
|
+
name: "Thinking",
|
|
152072
|
+
category: "thought_level",
|
|
152073
|
+
currentValue: "on",
|
|
152074
|
+
options: [{
|
|
152075
|
+
value: "on",
|
|
152076
|
+
name: "Thinking On"
|
|
152077
|
+
}]
|
|
152078
|
+
};
|
|
151793
152079
|
return {
|
|
151794
152080
|
type: "select",
|
|
151795
152081
|
id: "thinking",
|
|
@@ -151856,9 +152142,11 @@ function buildModeOption(currentModeId) {
|
|
|
151856
152142
|
*/
|
|
151857
152143
|
async function buildSessionConfigOptions(harness, currentBaseModelId, currentThinkingEnabled, currentModeId) {
|
|
151858
152144
|
const models = await listModelsFromHarness(harness);
|
|
151859
|
-
const
|
|
152145
|
+
const currentModelEntry = models.find((m) => m.id === currentBaseModelId);
|
|
152146
|
+
const showThinking = currentModelEntry?.thinkingSupported === true;
|
|
152147
|
+
const alwaysThinking = currentModelEntry?.alwaysThinking === true;
|
|
151860
152148
|
const out = [buildModelOption(models, currentBaseModelId)];
|
|
151861
|
-
if (showThinking) out.push(buildThinkingOption(currentThinkingEnabled));
|
|
152149
|
+
if (showThinking) out.push(buildThinkingOption(alwaysThinking || currentThinkingEnabled, alwaysThinking));
|
|
151862
152150
|
out.push(buildModeOption(currentModeId));
|
|
151863
152151
|
return out;
|
|
151864
152152
|
}
|
|
@@ -152204,11 +152492,25 @@ var AcpSession = class {
|
|
|
152204
152492
|
* carries a fresh snapshot.
|
|
152205
152493
|
*/
|
|
152206
152494
|
async setThinking(enabled) {
|
|
152495
|
+
if (!enabled && await this.currentModelAlwaysThinking()) {
|
|
152496
|
+
this.currentThinkingEnabledInternal = true;
|
|
152497
|
+
await this.emitConfigOptionUpdate();
|
|
152498
|
+
return;
|
|
152499
|
+
}
|
|
152207
152500
|
if (typeof this.session.setThinking === "function") await this.session.setThinking(enabled ? THINKING_ON_LEVEL : THINKING_OFF_LEVEL);
|
|
152208
152501
|
this.currentThinkingEnabledInternal = enabled;
|
|
152209
152502
|
await this.emitConfigOptionUpdate();
|
|
152210
152503
|
}
|
|
152211
152504
|
/**
|
|
152505
|
+
* Whether the currently-selected model declares 'always_thinking'.
|
|
152506
|
+
* Harness-less adapter unit tests resolve to false — the agent-core
|
|
152507
|
+
* runtime clamp still protects the actual request in that case.
|
|
152508
|
+
*/
|
|
152509
|
+
async currentModelAlwaysThinking() {
|
|
152510
|
+
if (!this.harness) return false;
|
|
152511
|
+
return (await listModelsFromHarness(this.harness)).find((m) => m.id === this.currentModelIdInternal)?.alwaysThinking === true;
|
|
152512
|
+
}
|
|
152513
|
+
/**
|
|
152212
152514
|
* Forward an ACP `session/set_mode` request to the underlying SDK
|
|
152213
152515
|
* session.
|
|
152214
152516
|
*
|
|
@@ -155413,7 +155715,6 @@ const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
|
|
|
155413
155715
|
const NO_ACTIVE_SESSION_MESSAGE = "No active session. Send /login to login.";
|
|
155414
155716
|
const CTRL_D_HINT = "Press Ctrl+D again to exit";
|
|
155415
155717
|
const CTRL_C_HINT = "Press Ctrl+C again to exit";
|
|
155416
|
-
const MAIN_AGENT_ID = "main";
|
|
155417
155718
|
const OAUTH_LOGIN_REQUIRED_STARTUP_NOTICE = "OAuth login expired. Send /login to login.";
|
|
155418
155719
|
const EXIT_CONFIRM_WINDOW_MS = 1500;
|
|
155419
155720
|
function isManagedUsageProvider(providerKey) {
|
|
@@ -155774,7 +156075,7 @@ var SearchableList = class {
|
|
|
155774
156075
|
* addChild(picker), setFocus(picker); the picker invokes `onSelect` or
|
|
155775
156076
|
* `onCancel`, and the host tears it down.
|
|
155776
156077
|
*/
|
|
155777
|
-
function wrapDescription(text, width) {
|
|
156078
|
+
function wrapDescription$1(text, width) {
|
|
155778
156079
|
const maxWidth = Math.max(1, width);
|
|
155779
156080
|
const words = text.trim().split(/\s+/).filter((word) => word.length > 0);
|
|
155780
156081
|
const lines = [];
|
|
@@ -155859,7 +156160,7 @@ var ChoicePickerComponent = class extends Container {
|
|
|
155859
156160
|
lines.push(line);
|
|
155860
156161
|
if (opt.description !== void 0 && opt.description.length > 0) {
|
|
155861
156162
|
const descriptionWidth = Math.max(1, width - 4);
|
|
155862
|
-
for (const descLine of wrapDescription(opt.description, descriptionWidth)) lines.push(currentTheme.fg("textMuted", ` ${descLine}`));
|
|
156163
|
+
for (const descLine of wrapDescription$1(opt.description, descriptionWidth)) lines.push(currentTheme.fg("textMuted", ` ${descLine}`));
|
|
155863
156164
|
}
|
|
155864
156165
|
}
|
|
155865
156166
|
lines.push("");
|
|
@@ -156130,9 +156431,10 @@ var ModelSelectorComponent = class extends Container {
|
|
|
156130
156431
|
}
|
|
156131
156432
|
renderThinkingControl(choice) {
|
|
156132
156433
|
const segment = (label, active) => active ? currentTheme.boldFg("primary", `[ ${label} ]`) : currentTheme.fg("text", ` ${label} `);
|
|
156434
|
+
const unavailable = (label) => currentTheme.fg("textMuted", ` ${label} (Unsupported) `);
|
|
156133
156435
|
const availability = thinkingAvailability(choice.model);
|
|
156134
|
-
if (availability === "always-on") return ` ${segment("
|
|
156135
|
-
if (availability === "unsupported") return ` ${
|
|
156436
|
+
if (availability === "always-on") return ` ${segment("On", true)} ${unavailable("Off")}`;
|
|
156437
|
+
if (availability === "unsupported") return ` ${unavailable("On")} ${segment("Off", true)}`;
|
|
156136
156438
|
const draft = this.draftFor(choice);
|
|
156137
156439
|
return ` ${segment("On", draft)} ${segment("Off", !draft)}`;
|
|
156138
156440
|
}
|
|
@@ -156651,7 +156953,7 @@ var EditorSelectorComponent = class extends ChoicePickerComponent {
|
|
|
156651
156953
|
};
|
|
156652
156954
|
//#endregion
|
|
156653
156955
|
//#region src/tui/components/dialogs/experiments-selector.ts
|
|
156654
|
-
const ELLIPSIS$
|
|
156956
|
+
const ELLIPSIS$8 = "…";
|
|
156655
156957
|
var ExperimentsSelectorComponent = class extends Container {
|
|
156656
156958
|
focused = false;
|
|
156657
156959
|
opts;
|
|
@@ -156710,7 +157012,7 @@ var ExperimentsSelectorComponent = class extends Container {
|
|
|
156710
157012
|
else if (view.page.end < view.items.length) lines.push(currentTheme.fg("textMuted", ` ▼ ${String(view.items.length - view.page.end)} more`));
|
|
156711
157013
|
lines.push(this.renderApplyButton());
|
|
156712
157014
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
156713
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
157015
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$8));
|
|
156714
157016
|
}
|
|
156715
157017
|
toggleDraft(feature) {
|
|
156716
157018
|
if (isLocked(feature)) return;
|
|
@@ -156783,7 +157085,7 @@ function wrapText(text, width) {
|
|
|
156783
157085
|
continue;
|
|
156784
157086
|
}
|
|
156785
157087
|
if (current.length > 0) lines.push(current);
|
|
156786
|
-
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$
|
|
157088
|
+
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$8);
|
|
156787
157089
|
}
|
|
156788
157090
|
if (current.length > 0) lines.push(current);
|
|
156789
157091
|
return lines;
|
|
@@ -158154,7 +158456,7 @@ var GoalStartPermissionPromptComponent = class extends StartPermissionPromptComp
|
|
|
158154
158456
|
//#region src/tui/components/dialogs/goal-queue-manager.ts
|
|
158155
158457
|
const MAX_GOAL_OBJECTIVE_LENGTH$2 = 4e3;
|
|
158156
158458
|
const MAX_EDIT_INPUT_LINES = 8;
|
|
158157
|
-
const ELLIPSIS$
|
|
158459
|
+
const ELLIPSIS$7 = "…";
|
|
158158
158460
|
const BRACKET_PASTE_START$1 = "\x1B[200~";
|
|
158159
158461
|
const BRACKET_PASTE_END$1 = "\x1B[201~";
|
|
158160
158462
|
const SHIFT_ENTER_LEGACY = "\x1B\r";
|
|
@@ -158244,7 +158546,7 @@ var GoalQueueManagerComponent = class extends Container {
|
|
|
158244
158546
|
}
|
|
158245
158547
|
lines.push("");
|
|
158246
158548
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
158247
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
158549
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$7));
|
|
158248
158550
|
}
|
|
158249
158551
|
renderGoal(goal, index, selected, width) {
|
|
158250
158552
|
const moving = goal.id === this.movingGoalId;
|
|
@@ -158255,7 +158557,7 @@ var GoalQueueManagerComponent = class extends Container {
|
|
|
158255
158557
|
const labelWidth = visibleWidth(labelPrefix);
|
|
158256
158558
|
const stateWidth = visibleWidth(stateLabel);
|
|
158257
158559
|
const objectiveWidth = Math.max(1, width - 5 - labelWidth - stateWidth);
|
|
158258
|
-
const objective = truncateToWidth(formatListObjective(goal.objective), objectiveWidth, ELLIPSIS$
|
|
158560
|
+
const objective = truncateToWidth(formatListObjective(goal.objective), objectiveWidth, ELLIPSIS$7);
|
|
158259
158561
|
let line = prefix + (selected ? (text) => currentTheme.boldFg("primary", text) : (text) => currentTheme.fg("text", text))(labelPrefix + objective);
|
|
158260
158562
|
if (moving) line += currentTheme.fg("success", stateLabel);
|
|
158261
158563
|
return line;
|
|
@@ -158326,10 +158628,10 @@ var GoalQueueEditDialogComponent = class extends Container {
|
|
|
158326
158628
|
const innerWidth = Math.max(10, safeWidth - 4);
|
|
158327
158629
|
const pad = " ";
|
|
158328
158630
|
const border = (s) => currentTheme.fg("primary", s);
|
|
158329
|
-
const title = truncateToWidth(currentTheme.boldFg("textStrong", "Edit upcoming goal"), innerWidth, ELLIPSIS$
|
|
158330
|
-
const subtitle = truncateToWidth(currentTheme.fg(this.error === void 0 ? "textDim" : "warning", this.error ?? "Update the queued objective."), innerWidth, ELLIPSIS$
|
|
158631
|
+
const title = truncateToWidth(currentTheme.boldFg("textStrong", "Edit upcoming goal"), innerWidth, ELLIPSIS$7);
|
|
158632
|
+
const subtitle = truncateToWidth(currentTheme.fg(this.error === void 0 ? "textDim" : "warning", this.error ?? "Update the queued objective."), innerWidth, ELLIPSIS$7);
|
|
158331
158633
|
const inputLines = this.input.render(innerWidth);
|
|
158332
|
-
const footer = truncateToWidth(currentTheme.fg("textDim", "Enter submit · Shift-Enter/Ctrl-J newline · Esc cancel"), innerWidth, ELLIPSIS$
|
|
158634
|
+
const footer = truncateToWidth(currentTheme.fg("textDim", "Enter submit · Shift-Enter/Ctrl-J newline · Esc cancel"), innerWidth, ELLIPSIS$7);
|
|
158333
158635
|
const contentLines = [
|
|
158334
158636
|
title,
|
|
158335
158637
|
"",
|
|
@@ -158435,14 +158737,14 @@ var MultilineGoalInput = class {
|
|
|
158435
158737
|
const cursor = this.cursorLocation();
|
|
158436
158738
|
const range = visibleLineRange(logicalLines.length, cursor.line);
|
|
158437
158739
|
const rendered = [];
|
|
158438
|
-
if (range.start > 0) rendered.push(padInputLine(` ${ELLIPSIS$
|
|
158740
|
+
if (range.start > 0) rendered.push(padInputLine(` ${ELLIPSIS$7} ${String(range.start)} previous`, safeWidth));
|
|
158439
158741
|
for (let lineIndex = range.start; lineIndex < range.end; lineIndex++) {
|
|
158440
158742
|
const line = logicalLines[lineIndex] ?? "";
|
|
158441
158743
|
const prefix = lineIndex === 0 ? "> " : " ";
|
|
158442
158744
|
rendered.push(lineIndex === cursor.line ? renderCursorLine(line, cursor.column, prefix, safeWidth, this.focused) : renderTextLine(line, prefix, safeWidth));
|
|
158443
158745
|
}
|
|
158444
158746
|
const remaining = logicalLines.length - range.end;
|
|
158445
|
-
if (remaining > 0) rendered.push(padInputLine(` ${ELLIPSIS$
|
|
158747
|
+
if (remaining > 0) rendered.push(padInputLine(` ${ELLIPSIS$7} ${String(remaining)} more`, safeWidth));
|
|
158446
158748
|
return rendered;
|
|
158447
158749
|
}
|
|
158448
158750
|
insert(text) {
|
|
@@ -158582,7 +158884,7 @@ function visibleLineRange(totalLines, cursorLine) {
|
|
|
158582
158884
|
}
|
|
158583
158885
|
function renderTextLine(line, prefix, width) {
|
|
158584
158886
|
const prefixWidth = visibleWidth(prefix);
|
|
158585
|
-
return padInputLine(prefix + truncateToWidth(line, Math.max(1, width - prefixWidth), ELLIPSIS$
|
|
158887
|
+
return padInputLine(prefix + truncateToWidth(line, Math.max(1, width - prefixWidth), ELLIPSIS$7), width);
|
|
158586
158888
|
}
|
|
158587
158889
|
function renderCursorLine(line, column, prefix, width, focused) {
|
|
158588
158890
|
const prefixWidth = visibleWidth(prefix);
|
|
@@ -160150,7 +160452,7 @@ const OVERVIEW_PLUGIN_PREFIX = "plugin:";
|
|
|
160150
160452
|
const MCP_SERVER_PREFIX = "mcp:";
|
|
160151
160453
|
const REMOVE_CONFIRM_CANCEL = "cancel";
|
|
160152
160454
|
const REMOVE_CONFIRM_REMOVE = "remove";
|
|
160153
|
-
const ELLIPSIS$
|
|
160455
|
+
const ELLIPSIS$6 = "…";
|
|
160154
160456
|
var PluginsOverviewSelectorComponent = class extends Container {
|
|
160155
160457
|
focused = false;
|
|
160156
160458
|
opts;
|
|
@@ -160243,7 +160545,7 @@ var PluginsOverviewSelectorComponent = class extends Container {
|
|
|
160243
160545
|
for (let i = 0; i < actionItems.length; i++) lines.push(...this.renderItem(actionItems[i], pluginItems.length + i, width));
|
|
160244
160546
|
lines.push("");
|
|
160245
160547
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160246
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160548
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160247
160549
|
}
|
|
160248
160550
|
renderItem(item, index, width) {
|
|
160249
160551
|
const selected = index === this.selectedIndex;
|
|
@@ -160315,7 +160617,7 @@ var PluginMarketplaceSelectorComponent = class extends Container {
|
|
|
160315
160617
|
for (let i = 0; i < actions.length; i++) lines.push(...this.renderItem(actions[i], entries.length + i, width));
|
|
160316
160618
|
lines.push("");
|
|
160317
160619
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160318
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160620
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160319
160621
|
}
|
|
160320
160622
|
renderItem(item, index, width) {
|
|
160321
160623
|
const selected = index === this.selectedIndex;
|
|
@@ -160394,7 +160696,7 @@ var PluginMcpSelectorComponent = class extends Container {
|
|
|
160394
160696
|
for (let i = 0; i < actionItems.length; i++) lines.push(...this.renderItem(actionItems[i], serverItems.length + i, width));
|
|
160395
160697
|
lines.push("");
|
|
160396
160698
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160397
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160699
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160398
160700
|
}
|
|
160399
160701
|
renderItem(item, index, width) {
|
|
160400
160702
|
const selected = index === this.selectedIndex;
|
|
@@ -160574,7 +160876,7 @@ function wrapOverviewDescription(text, width) {
|
|
|
160574
160876
|
continue;
|
|
160575
160877
|
}
|
|
160576
160878
|
if (current.length > 0) lines.push(current);
|
|
160577
|
-
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$
|
|
160879
|
+
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$6);
|
|
160578
160880
|
}
|
|
160579
160881
|
if (current.length > 0) lines.push(current);
|
|
160580
160882
|
return lines;
|
|
@@ -161065,15 +161367,23 @@ function applyRuntimeConfig(host, config) {
|
|
|
161065
161367
|
}
|
|
161066
161368
|
//#endregion
|
|
161067
161369
|
//#region src/tui/components/dialogs/swarm-start-permission-prompt.ts
|
|
161068
|
-
const OPTIONS = [
|
|
161069
|
-
|
|
161070
|
-
|
|
161071
|
-
|
|
161072
|
-
|
|
161073
|
-
|
|
161074
|
-
|
|
161075
|
-
|
|
161076
|
-
|
|
161370
|
+
const OPTIONS = [
|
|
161371
|
+
{
|
|
161372
|
+
value: "auto",
|
|
161373
|
+
label: "Switch to Auto and start",
|
|
161374
|
+
description: "Best for swarm tasks. Tools are approved automatically, and questions are skipped."
|
|
161375
|
+
},
|
|
161376
|
+
{
|
|
161377
|
+
value: "yolo",
|
|
161378
|
+
label: "Switch to YOLO and start",
|
|
161379
|
+
description: "Tools and plan changes are approved automatically. Kimi Code may still ask you questions."
|
|
161380
|
+
},
|
|
161381
|
+
{
|
|
161382
|
+
value: "manual",
|
|
161383
|
+
label: "Start in Manual",
|
|
161384
|
+
description: "Keep approvals on. Kimi Code may stop and wait for you during the swarm task."
|
|
161385
|
+
}
|
|
161386
|
+
];
|
|
161077
161387
|
const NOTICE_LINES = [
|
|
161078
161388
|
"Manual mode asks you before Kimi Code runs commands, edits files, or takes other risky actions.",
|
|
161079
161389
|
"Manual mode can block swarm work while agents are running.",
|
|
@@ -161151,7 +161461,7 @@ function showSwarmStartPermissionPrompt(host, commandText, cancelStatus, onSelec
|
|
|
161151
161461
|
}));
|
|
161152
161462
|
}
|
|
161153
161463
|
async function startSwarmWithPermission(host, prompt, choice) {
|
|
161154
|
-
if (choice === "auto") {
|
|
161464
|
+
if (choice === "auto" || choice === "yolo") {
|
|
161155
161465
|
if (!await setPermissionForSwarm(host, choice)) return;
|
|
161156
161466
|
}
|
|
161157
161467
|
await startSwarmTask(host, prompt);
|
|
@@ -161182,7 +161492,7 @@ async function applySwarmMode(host, enabled, commandText) {
|
|
|
161182
161492
|
}
|
|
161183
161493
|
if (enabled && host.state.appState.permissionMode === "manual") {
|
|
161184
161494
|
showSwarmStartPermissionPrompt(host, commandText, "Swarm mode not enabled.", async (choice) => {
|
|
161185
|
-
if (choice === "auto" && !await setPermissionForSwarm(host, choice)) return;
|
|
161495
|
+
if ((choice === "auto" || choice === "yolo") && !await setPermissionForSwarm(host, choice)) return;
|
|
161186
161496
|
if (!await setSwarmMode(host, true, "manual")) return;
|
|
161187
161497
|
renderSwarmModeMarker(host, "active");
|
|
161188
161498
|
});
|
|
@@ -166809,15 +167119,20 @@ function installPromptTerminationCleanup(promptProcess, cleanup) {
|
|
|
166809
167119
|
};
|
|
166810
167120
|
const onSigint = () => exitAfterCleanup("SIGINT");
|
|
166811
167121
|
const onSigterm = () => exitAfterCleanup("SIGTERM");
|
|
167122
|
+
const onSighup = () => exitAfterCleanup("SIGHUP");
|
|
166812
167123
|
promptProcess.once("SIGINT", onSigint);
|
|
166813
167124
|
promptProcess.once("SIGTERM", onSigterm);
|
|
167125
|
+
promptProcess.once("SIGHUP", onSighup);
|
|
166814
167126
|
return () => {
|
|
166815
167127
|
promptProcess.off("SIGINT", onSigint);
|
|
166816
167128
|
promptProcess.off("SIGTERM", onSigterm);
|
|
167129
|
+
promptProcess.off("SIGHUP", onSighup);
|
|
166817
167130
|
};
|
|
166818
167131
|
}
|
|
166819
167132
|
function signalExitCode(signal) {
|
|
166820
|
-
|
|
167133
|
+
if (signal === "SIGINT") return 130;
|
|
167134
|
+
if (signal === "SIGHUP") return 129;
|
|
167135
|
+
return 143;
|
|
166821
167136
|
}
|
|
166822
167137
|
function runPromptTurn(session, prompt, outputFormat, stdout, stderr) {
|
|
166823
167138
|
let activeTurnId;
|
|
@@ -167783,16 +168098,16 @@ function buildNumericHint(count) {
|
|
|
167783
168098
|
* emulator's "snap to bottom on stdout" reflex, causing flicker and an
|
|
167784
168099
|
* unscrollable history pane.
|
|
167785
168100
|
*/
|
|
167786
|
-
const ELLIPSIS$
|
|
168101
|
+
const ELLIPSIS$5 = "…";
|
|
167787
168102
|
function padToWidth$2(line, width) {
|
|
167788
168103
|
const w = visibleWidth(line);
|
|
167789
168104
|
if (w === width) return line;
|
|
167790
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
168105
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$5);
|
|
167791
168106
|
return line + " ".repeat(width - w);
|
|
167792
168107
|
}
|
|
167793
168108
|
function fitExactly$2(line, width) {
|
|
167794
168109
|
let s = line;
|
|
167795
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
168110
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$5);
|
|
167796
168111
|
return padToWidth$2(s, width);
|
|
167797
168112
|
}
|
|
167798
168113
|
var ApprovalPreviewViewer = class extends Container {
|
|
@@ -168649,7 +168964,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
168649
168964
|
/**
|
|
168650
168965
|
* SessionPicker — pi-tui version of the session selection dialog.
|
|
168651
168966
|
*/
|
|
168652
|
-
const ELLIPSIS$
|
|
168967
|
+
const ELLIPSIS$4 = "…";
|
|
168653
168968
|
function formatRelativeTime$1(ts) {
|
|
168654
168969
|
if (!Number.isFinite(ts) || ts <= 0) return "";
|
|
168655
168970
|
const diffSec = Math.floor(Math.max(0, Date.now() - ts) / 1e3);
|
|
@@ -168669,7 +168984,7 @@ function homeAlias(path) {
|
|
|
168669
168984
|
function truncatePathLeft(path, maxWidth) {
|
|
168670
168985
|
if (maxWidth <= 0) return "";
|
|
168671
168986
|
if (visibleWidth(path) <= maxWidth) return path;
|
|
168672
|
-
if (maxWidth === 1) return ELLIPSIS$
|
|
168987
|
+
if (maxWidth === 1) return ELLIPSIS$4;
|
|
168673
168988
|
const segments = [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(path)].map((s) => s.segment);
|
|
168674
168989
|
let used = 0;
|
|
168675
168990
|
const budget = maxWidth - 1;
|
|
@@ -168682,7 +168997,7 @@ function truncatePathLeft(path, maxWidth) {
|
|
|
168682
168997
|
used += w;
|
|
168683
168998
|
i--;
|
|
168684
168999
|
}
|
|
168685
|
-
return ELLIPSIS$
|
|
169000
|
+
return ELLIPSIS$4 + segments.slice(i + 1).join("");
|
|
168686
169001
|
}
|
|
168687
169002
|
function singleLine$1(text) {
|
|
168688
169003
|
return text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -168734,26 +169049,26 @@ var SessionPickerComponent = class extends Container {
|
|
|
168734
169049
|
if (matchesKey(data, Key.down)) this.selectedIndex = Math.min(this.sessions.length - 1, this.selectedIndex + 1);
|
|
168735
169050
|
}
|
|
168736
169051
|
render(width) {
|
|
168737
|
-
return this.renderLines(width).map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
169052
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, width, ELLIPSIS$4));
|
|
168738
169053
|
}
|
|
168739
169054
|
renderLines(width) {
|
|
168740
169055
|
const lines = [currentTheme.fg("primary", "─".repeat(width))];
|
|
168741
169056
|
if (this.loading) {
|
|
168742
|
-
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$
|
|
168743
|
-
lines.push(currentTheme.fg("textMuted", truncateToWidth("Loading sessions...", width, ELLIPSIS$
|
|
169057
|
+
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$4)));
|
|
169058
|
+
lines.push(currentTheme.fg("textMuted", truncateToWidth("Loading sessions...", width, ELLIPSIS$4)));
|
|
168744
169059
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168745
169060
|
return lines;
|
|
168746
169061
|
}
|
|
168747
169062
|
if (this.sessions.length === 0) {
|
|
168748
|
-
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$
|
|
168749
|
-
lines.push(currentTheme.fg("textMuted", truncateToWidth("No sessions found. Press Escape to close.", width, ELLIPSIS$
|
|
169063
|
+
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$4)));
|
|
169064
|
+
lines.push(currentTheme.fg("textMuted", truncateToWidth("No sessions found. Press Escape to close.", width, ELLIPSIS$4)));
|
|
168750
169065
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168751
169066
|
return lines;
|
|
168752
169067
|
}
|
|
168753
169068
|
const headerLabel = "Sessions ";
|
|
168754
169069
|
const headerHint = "↑↓ navigate · Enter select · Esc cancel";
|
|
168755
169070
|
const labelWidth = visibleWidth(headerLabel);
|
|
168756
|
-
const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$
|
|
169071
|
+
const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$4);
|
|
168757
169072
|
lines.push(currentTheme.boldFg("primary", headerLabel) + currentTheme.fg("textMuted", shownHint));
|
|
168758
169073
|
lines.push("");
|
|
168759
169074
|
const visibleStart = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisibleSessions / 2), Math.max(0, this.sessions.length - this.maxVisibleSessions)));
|
|
@@ -168768,7 +169083,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168768
169083
|
if (this.sessions.length > visibleSessions.length) {
|
|
168769
169084
|
lines.push("");
|
|
168770
169085
|
const footer = `Showing ${String(visibleStart + 1)}-${String(visibleStart + visibleSessions.length)} of ${String(this.sessions.length)} sessions`;
|
|
168771
|
-
lines.push(currentTheme.fg("textMuted", truncateToWidth(footer, width, ELLIPSIS$
|
|
169086
|
+
lines.push(currentTheme.fg("textMuted", truncateToWidth(footer, width, ELLIPSIS$4)));
|
|
168772
169087
|
}
|
|
168773
169088
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168774
169089
|
return lines;
|
|
@@ -168789,7 +169104,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168789
169104
|
const trailingWidth = visibleWidth(trailingParts.length > 0 ? " " + trailingParts.join(" ") : "");
|
|
168790
169105
|
const headerPrefixWidth = visibleWidth(pointer) + 1;
|
|
168791
169106
|
const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
|
|
168792
|
-
const shownTitle = truncateToWidth(singleLine$1(titleSource), titleBudget, ELLIPSIS$
|
|
169107
|
+
const shownTitle = truncateToWidth(singleLine$1(titleSource), titleBudget, ELLIPSIS$4);
|
|
168793
169108
|
let header = currentTheme.fg(isSelected ? "primary" : "textDim", pointer + " ");
|
|
168794
169109
|
header += titleStyle(shownTitle);
|
|
168795
169110
|
if (time.length > 0) header += " " + currentTheme.fg("textDim", time);
|
|
@@ -168804,7 +169119,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168804
169119
|
const dirWidth = visibleWidth(aliasedDir);
|
|
168805
169120
|
if (idLineWidth + metaGapWidth + dirWidth <= width) card.push(indent + currentTheme.fg("textMuted", fullId) + currentTheme.fg("textDim", metaGap) + currentTheme.fg("textMuted", aliasedDir));
|
|
168806
169121
|
else {
|
|
168807
|
-
card.push(indent + currentTheme.fg("textMuted", truncateToWidth(fullId, Math.max(idWidth, width - indentWidth), ELLIPSIS$
|
|
169122
|
+
card.push(indent + currentTheme.fg("textMuted", truncateToWidth(fullId, Math.max(idWidth, width - indentWidth), ELLIPSIS$4)));
|
|
168808
169123
|
const dir = truncatePathLeft(aliasedDir, Math.max(8, width - indentWidth));
|
|
168809
169124
|
card.push(indent + currentTheme.fg("textMuted", dir));
|
|
168810
169125
|
}
|
|
@@ -168813,7 +169128,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168813
169128
|
const promptMarker = "› ";
|
|
168814
169129
|
const promptMarkerWidth = visibleWidth(promptMarker);
|
|
168815
169130
|
const promptBudget = Math.max(8, width - indentWidth - promptMarkerWidth);
|
|
168816
|
-
const promptText = truncateToWidth(singleLine$1(rawPrompt), promptBudget, ELLIPSIS$
|
|
169131
|
+
const promptText = truncateToWidth(singleLine$1(rawPrompt), promptBudget, ELLIPSIS$4);
|
|
168817
169132
|
const promptLine = indent + currentTheme.fg("textDim", promptMarker + promptText);
|
|
168818
169133
|
card.push(promptLine);
|
|
168819
169134
|
}
|
|
@@ -168910,7 +169225,7 @@ function restoreProviderAliases(config, aliases) {
|
|
|
168910
169225
|
function restoreDefaultSelection(config, defaultModel, defaultThinking) {
|
|
168911
169226
|
if (defaultModel === void 0 || config.models?.[defaultModel] === void 0) return;
|
|
168912
169227
|
config.defaultModel = defaultModel;
|
|
168913
|
-
config.defaultThinking = defaultThinking;
|
|
169228
|
+
config.defaultThinking = (config.models[defaultModel]?.capabilities ?? []).includes("always_thinking") ? true : defaultThinking;
|
|
168914
169229
|
}
|
|
168915
169230
|
function clampDanglingDefault(config) {
|
|
168916
169231
|
if (config.defaultModel !== void 0 && config.models?.[config.defaultModel] === void 0) {
|
|
@@ -169539,13 +169854,7 @@ var BtwPanelController = class {
|
|
|
169539
169854
|
return panel.isRunning() || panel.isEmpty();
|
|
169540
169855
|
}
|
|
169541
169856
|
withInteractiveAgent(agentId, fn) {
|
|
169542
|
-
|
|
169543
|
-
this.host.harness.interactiveAgentId = agentId;
|
|
169544
|
-
try {
|
|
169545
|
-
return fn();
|
|
169546
|
-
} finally {
|
|
169547
|
-
this.host.harness.interactiveAgentId = previousAgentId;
|
|
169548
|
-
}
|
|
169857
|
+
return this.host.harness.withInteractiveAgent(agentId, fn);
|
|
169549
169858
|
}
|
|
169550
169859
|
};
|
|
169551
169860
|
function formatBtwTurnEnd(event) {
|
|
@@ -173658,7 +173967,7 @@ var StreamingUIController = class {
|
|
|
173658
173967
|
* TasksBrowserApp. Snapshot view (no live tail) — content is fetched
|
|
173659
173968
|
* once when the viewer opens.
|
|
173660
173969
|
*/
|
|
173661
|
-
const ELLIPSIS$
|
|
173970
|
+
const ELLIPSIS$3 = "…";
|
|
173662
173971
|
const STATUS_LABEL$1 = {
|
|
173663
173972
|
running: "running",
|
|
173664
173973
|
completed: "completed",
|
|
@@ -173680,12 +173989,12 @@ function statusColor$1(status) {
|
|
|
173680
173989
|
function padToWidth$1(line, width) {
|
|
173681
173990
|
const w = visibleWidth(line);
|
|
173682
173991
|
if (w === width) return line;
|
|
173683
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
173992
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$3);
|
|
173684
173993
|
return line + " ".repeat(width - w);
|
|
173685
173994
|
}
|
|
173686
173995
|
function fitExactly$1(line, width) {
|
|
173687
173996
|
let s = line;
|
|
173688
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
173997
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$3);
|
|
173689
173998
|
return padToWidth$1(s, width);
|
|
173690
173999
|
}
|
|
173691
174000
|
var TaskOutputViewer = class extends Container {
|
|
@@ -173844,7 +174153,7 @@ var TaskOutputViewer = class extends Container {
|
|
|
173844
174153
|
* Data (tasks list, tail output) flows in via `setProps`; user actions
|
|
173845
174154
|
* fire the `on*` callbacks back to the controller.
|
|
173846
174155
|
*/
|
|
173847
|
-
const ELLIPSIS$
|
|
174156
|
+
const ELLIPSIS$2 = "…";
|
|
173848
174157
|
const STATUS_LABEL = {
|
|
173849
174158
|
running: "running",
|
|
173850
174159
|
completed: "completed",
|
|
@@ -173892,13 +174201,13 @@ function singleLine(text) {
|
|
|
173892
174201
|
function padToWidth(line, width) {
|
|
173893
174202
|
const w = visibleWidth(line);
|
|
173894
174203
|
if (w === width) return line;
|
|
173895
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
174204
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$2);
|
|
173896
174205
|
return line + " ".repeat(width - w);
|
|
173897
174206
|
}
|
|
173898
174207
|
/** Fit `line` into exactly `width` columns, even after CJK-edge truncation. */
|
|
173899
174208
|
function fitExactly(line, width) {
|
|
173900
174209
|
let s = line;
|
|
173901
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
174210
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$2);
|
|
173902
174211
|
return padToWidth(s, width);
|
|
173903
174212
|
}
|
|
173904
174213
|
function visibleTasks(tasks, filter) {
|
|
@@ -174159,7 +174468,7 @@ var TasksBrowserApp = class extends Container {
|
|
|
174159
174468
|
const prefixWidth = visibleWidth(prefix);
|
|
174160
174469
|
const descBudget = Math.max(0, innerWidth - prefixWidth - 1);
|
|
174161
174470
|
if (descBudget < 4) return fitExactly(prefix, innerWidth);
|
|
174162
|
-
const desc = truncateToWidth(singleLine(task.description) || (task.kind === "process" ? singleLine(task.command) : "") || "(no description)", descBudget, ELLIPSIS$
|
|
174471
|
+
const desc = truncateToWidth(singleLine(task.description) || (task.kind === "process" ? singleLine(task.command) : "") || "(no description)", descBudget, ELLIPSIS$2);
|
|
174163
174472
|
return fitExactly(`${prefix} ${currentTheme.fg("text", desc)}`, innerWidth);
|
|
174164
174473
|
}
|
|
174165
174474
|
adjustScroll(visibleRows) {
|
|
@@ -174556,13 +174865,23 @@ const MAX_FALLBACK_SUGGESTIONS = 50;
|
|
|
174556
174865
|
* completer. This wrapper also keeps Kimi-specific slash-command guards.
|
|
174557
174866
|
*/
|
|
174558
174867
|
var FileMentionProvider = class {
|
|
174868
|
+
slashCommands;
|
|
174559
174869
|
workDir;
|
|
174560
174870
|
fdPath;
|
|
174561
174871
|
inner;
|
|
174562
174872
|
constructor(slashCommands, workDir, fdPath) {
|
|
174873
|
+
this.slashCommands = slashCommands;
|
|
174563
174874
|
this.workDir = workDir;
|
|
174564
174875
|
this.fdPath = fdPath;
|
|
174565
|
-
|
|
174876
|
+
const expanded = [];
|
|
174877
|
+
for (const cmd of slashCommands) {
|
|
174878
|
+
expanded.push(cmd);
|
|
174879
|
+
for (const alias of cmd.aliases ?? []) expanded.push({
|
|
174880
|
+
...cmd,
|
|
174881
|
+
name: alias
|
|
174882
|
+
});
|
|
174883
|
+
}
|
|
174884
|
+
this.inner = new CombinedAutocompleteProvider(expanded, workDir, fdPath);
|
|
174566
174885
|
}
|
|
174567
174886
|
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
174568
174887
|
const currentLine = lines[cursorLine] ?? "";
|
|
@@ -174578,6 +174897,46 @@ var FileMentionProvider = class {
|
|
|
174578
174897
|
return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
174579
174898
|
}
|
|
174580
174899
|
}
|
|
174900
|
+
if (!options.force && textBeforeCursor.startsWith("/")) {
|
|
174901
|
+
if (textBeforeCursor.indexOf(" ") === -1) {
|
|
174902
|
+
const tokens = textBeforeCursor.slice(1).trim().split(/\s+/).filter((t) => t.length > 0);
|
|
174903
|
+
const matches = [];
|
|
174904
|
+
for (const cmd of this.slashCommands) {
|
|
174905
|
+
const nameScore = scoreTokens(tokens, cmd.name);
|
|
174906
|
+
if (nameScore !== null) {
|
|
174907
|
+
matches.push({
|
|
174908
|
+
cmd,
|
|
174909
|
+
score: nameScore,
|
|
174910
|
+
viaAlias: false,
|
|
174911
|
+
label: cmd.name
|
|
174912
|
+
});
|
|
174913
|
+
continue;
|
|
174914
|
+
}
|
|
174915
|
+
const aliases = cmd.aliases ?? [];
|
|
174916
|
+
let bestAliasScore = null;
|
|
174917
|
+
for (const alias of aliases) {
|
|
174918
|
+
const aliasScore = scoreTokens(tokens, alias);
|
|
174919
|
+
if (aliasScore !== null && (bestAliasScore === null || aliasScore < bestAliasScore)) bestAliasScore = aliasScore;
|
|
174920
|
+
}
|
|
174921
|
+
if (bestAliasScore !== null) matches.push({
|
|
174922
|
+
cmd,
|
|
174923
|
+
score: bestAliasScore,
|
|
174924
|
+
viaAlias: true,
|
|
174925
|
+
label: `${cmd.name} (${aliases.join(", ")})`
|
|
174926
|
+
});
|
|
174927
|
+
}
|
|
174928
|
+
matches.sort((a, b) => a.score - b.score || Number(a.viaAlias) - Number(b.viaAlias));
|
|
174929
|
+
if (matches.length === 0) return null;
|
|
174930
|
+
return {
|
|
174931
|
+
items: matches.map((m) => ({
|
|
174932
|
+
value: m.cmd.name,
|
|
174933
|
+
label: m.label,
|
|
174934
|
+
description: formatSlashCommandDescription(m.cmd)
|
|
174935
|
+
})),
|
|
174936
|
+
prefix: textBeforeCursor
|
|
174937
|
+
};
|
|
174938
|
+
}
|
|
174939
|
+
}
|
|
174581
174940
|
try {
|
|
174582
174941
|
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
174583
174942
|
} catch {
|
|
@@ -174694,6 +175053,28 @@ function shouldSuppressSlashArgumentCompletion(textBeforeCursor, textAfterCursor
|
|
|
174694
175053
|
if (!textBeforeCursor.includes(" ")) return false;
|
|
174695
175054
|
return textAfterCursor.trimStart().length > 0;
|
|
174696
175055
|
}
|
|
175056
|
+
/**
|
|
175057
|
+
* All tokens must fuzzy-match `text`; returns the summed score, or null when
|
|
175058
|
+
* any token misses. An empty token list matches everything with score 0.
|
|
175059
|
+
* Mirrors pi-tui fuzzyFilter's token semantics — keep in sync if it changes.
|
|
175060
|
+
*/
|
|
175061
|
+
function scoreTokens(tokens, text) {
|
|
175062
|
+
let score = 0;
|
|
175063
|
+
for (const token of tokens) {
|
|
175064
|
+
const m = fuzzyMatch(token, text);
|
|
175065
|
+
if (!m.matches) return null;
|
|
175066
|
+
score += m.score;
|
|
175067
|
+
}
|
|
175068
|
+
return score;
|
|
175069
|
+
}
|
|
175070
|
+
/**
|
|
175071
|
+
* Mirrors CombinedAutocompleteProvider's description rendering so the
|
|
175072
|
+
* intercepted name completion keeps showing the argument hint.
|
|
175073
|
+
*/
|
|
175074
|
+
function formatSlashCommandDescription(cmd) {
|
|
175075
|
+
const desc = cmd.description ?? "";
|
|
175076
|
+
return (cmd.argumentHint ? desc ? `${cmd.argumentHint} — ${desc}` : cmd.argumentHint : desc) || void 0;
|
|
175077
|
+
}
|
|
174697
175078
|
//#endregion
|
|
174698
175079
|
//#region src/tui/components/panes/activity-pane.ts
|
|
174699
175080
|
var ActivityPaneComponent = class extends Container {
|
|
@@ -174714,7 +175095,7 @@ var ActivityPaneComponent = class extends Container {
|
|
|
174714
175095
|
};
|
|
174715
175096
|
//#endregion
|
|
174716
175097
|
//#region src/tui/components/panes/queue-pane.ts
|
|
174717
|
-
const ELLIPSIS = "…";
|
|
175098
|
+
const ELLIPSIS$1 = "…";
|
|
174718
175099
|
var QueuePaneComponent = class extends Container {
|
|
174719
175100
|
messages;
|
|
174720
175101
|
hint;
|
|
@@ -174730,10 +175111,10 @@ var QueuePaneComponent = class extends Container {
|
|
|
174730
175111
|
for (const item of this.messages) {
|
|
174731
175112
|
const singleLine = item.text.replaceAll(/\s+/g, " ").trim();
|
|
174732
175113
|
const prefix = ` ❯ `;
|
|
174733
|
-
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS);
|
|
175114
|
+
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS$1);
|
|
174734
175115
|
lines.push(accent(prefix + truncated));
|
|
174735
175116
|
}
|
|
174736
|
-
if (this.hint !== void 0) lines.push(dim(truncateToWidth(this.hint, width, ELLIPSIS)));
|
|
175117
|
+
if (this.hint !== void 0) lines.push(dim(truncateToWidth(this.hint, width, ELLIPSIS$1)));
|
|
174737
175118
|
return lines;
|
|
174738
175119
|
}
|
|
174739
175120
|
};
|
|
@@ -175157,6 +175538,144 @@ function createApprovalRequestHandler(controller, onResponse) {
|
|
|
175157
175538
|
};
|
|
175158
175539
|
}
|
|
175159
175540
|
//#endregion
|
|
175541
|
+
//#region src/tui/banner/banner-provider.ts
|
|
175542
|
+
function normalizeTag(value) {
|
|
175543
|
+
if (typeof value !== "string") return null;
|
|
175544
|
+
const trimmed = value.trim();
|
|
175545
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
175546
|
+
}
|
|
175547
|
+
function normalizeText(value) {
|
|
175548
|
+
if (typeof value !== "string") return null;
|
|
175549
|
+
const trimmed = value.trim();
|
|
175550
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
175551
|
+
}
|
|
175552
|
+
function normalizeUtcDate(value) {
|
|
175553
|
+
if (value.endsWith("Z")) return value;
|
|
175554
|
+
if (/[+-]\d{2}:\d{2}$/.test(value)) return value;
|
|
175555
|
+
return `${value}Z`;
|
|
175556
|
+
}
|
|
175557
|
+
function parseDate(value) {
|
|
175558
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
175559
|
+
const normalized = normalizeUtcDate(value);
|
|
175560
|
+
const date = new Date(normalized);
|
|
175561
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
175562
|
+
}
|
|
175563
|
+
function isWithinWindow(start, end, now) {
|
|
175564
|
+
if (start !== null && now < start) return false;
|
|
175565
|
+
if (end !== null && now > end) return false;
|
|
175566
|
+
return true;
|
|
175567
|
+
}
|
|
175568
|
+
function meetsMinVersion(minVersion, clientVersion) {
|
|
175569
|
+
if (minVersion === void 0 || minVersion === null) return true;
|
|
175570
|
+
if (typeof minVersion !== "string" || minVersion.length === 0) return true;
|
|
175571
|
+
const min = valid(minVersion);
|
|
175572
|
+
const current = valid(clientVersion);
|
|
175573
|
+
if (min === null || current === null) return false;
|
|
175574
|
+
return gte(current, min);
|
|
175575
|
+
}
|
|
175576
|
+
function pickActiveBanner(json, clientVersion, now) {
|
|
175577
|
+
if (json.banner_enabled !== true) return null;
|
|
175578
|
+
if (!meetsMinVersion(json.banner_min_version, clientVersion)) return null;
|
|
175579
|
+
if (!isWithinWindow(parseDate(json.banner_start_time), parseDate(json.banner_end_time), now)) return null;
|
|
175580
|
+
const mainText = normalizeText(json.banner_maintext);
|
|
175581
|
+
if (mainText === null) return null;
|
|
175582
|
+
return {
|
|
175583
|
+
tag: normalizeTag(json.banner_title),
|
|
175584
|
+
mainText,
|
|
175585
|
+
subText: normalizeText(json.banner_subtext)
|
|
175586
|
+
};
|
|
175587
|
+
}
|
|
175588
|
+
function pickFallbackBanner(json, clientVersion, now, random) {
|
|
175589
|
+
if (json.banner_fallback_enabled !== true) return null;
|
|
175590
|
+
const list = Array.isArray(json.banner_fallback_list) ? json.banner_fallback_list : [];
|
|
175591
|
+
const candidates = [];
|
|
175592
|
+
for (const raw of list) {
|
|
175593
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
175594
|
+
const item = raw;
|
|
175595
|
+
if (item.enabled !== true) continue;
|
|
175596
|
+
if (!meetsMinVersion(item.banner_min_version, clientVersion)) continue;
|
|
175597
|
+
const mainText = normalizeText(item.banner_maintext);
|
|
175598
|
+
if (mainText === null) continue;
|
|
175599
|
+
candidates.push({
|
|
175600
|
+
tag: normalizeTag(item.banner_title),
|
|
175601
|
+
mainText,
|
|
175602
|
+
subText: normalizeText(item.banner_subtext)
|
|
175603
|
+
});
|
|
175604
|
+
}
|
|
175605
|
+
if (candidates.length === 0) return null;
|
|
175606
|
+
return candidates[Math.floor(random() * candidates.length)];
|
|
175607
|
+
}
|
|
175608
|
+
function selectBannerState(json, clientVersion, now, random) {
|
|
175609
|
+
const typed = typeof json === "object" && json !== null ? json : {};
|
|
175610
|
+
return pickActiveBanner(typed, clientVersion, now) ?? pickFallbackBanner(typed, clientVersion, now, random);
|
|
175611
|
+
}
|
|
175612
|
+
var BannerProvider = class {
|
|
175613
|
+
clientVersion;
|
|
175614
|
+
url;
|
|
175615
|
+
constructor(clientVersion, url = KIMI_CODE_TIPS_BANNER_URL) {
|
|
175616
|
+
this.clientVersion = clientVersion;
|
|
175617
|
+
this.url = url;
|
|
175618
|
+
}
|
|
175619
|
+
async load(fetchImpl = fetch) {
|
|
175620
|
+
try {
|
|
175621
|
+
const controller = new AbortController();
|
|
175622
|
+
const timeout = setTimeout(() => {
|
|
175623
|
+
controller.abort();
|
|
175624
|
+
}, 3e3);
|
|
175625
|
+
const response = await fetchImpl(this.url, { signal: controller.signal });
|
|
175626
|
+
clearTimeout(timeout);
|
|
175627
|
+
if (!response.ok) return null;
|
|
175628
|
+
return selectBannerState(await response.json(), this.clientVersion, /* @__PURE__ */ new Date(), Math.random);
|
|
175629
|
+
} catch {
|
|
175630
|
+
return null;
|
|
175631
|
+
}
|
|
175632
|
+
}
|
|
175633
|
+
};
|
|
175634
|
+
//#endregion
|
|
175635
|
+
//#region src/tui/components/chrome/banner.ts
|
|
175636
|
+
const PREFIX_STAR = "✦";
|
|
175637
|
+
const PADDING = " ";
|
|
175638
|
+
var BannerComponent = class {
|
|
175639
|
+
state;
|
|
175640
|
+
constructor(state) {
|
|
175641
|
+
this.state = state;
|
|
175642
|
+
}
|
|
175643
|
+
invalidate() {}
|
|
175644
|
+
render(width) {
|
|
175645
|
+
const main = (s) => currentTheme.boldFg("textStrong", s);
|
|
175646
|
+
const dim = (s) => currentTheme.fg("textDim", s);
|
|
175647
|
+
if (width < 1) return [""];
|
|
175648
|
+
const tagText = this.state.tag ?? "";
|
|
175649
|
+
const tagLabel = tagText.length > 0 ? `${PREFIX_STAR} ${tagText}` : "";
|
|
175650
|
+
const tagStyled = tagLabel.length > 0 ? currentTheme.boldFg("primary", tagLabel) : "";
|
|
175651
|
+
const tagDisplay = tagStyled.length > 0 ? tagStyled + PADDING : "";
|
|
175652
|
+
const tagWidth = visibleWidth(tagDisplay);
|
|
175653
|
+
const showTag = tagWidth > 0 && tagWidth < width;
|
|
175654
|
+
const bodyIndent = showTag ? " ".repeat(tagWidth) : "";
|
|
175655
|
+
const descIndent = showTag ? " ".repeat(visibleWidth(PREFIX_STAR + PADDING)) : "";
|
|
175656
|
+
const bodyContentWidth = width - (showTag ? tagWidth : 0);
|
|
175657
|
+
const descContentWidth = width - (showTag ? visibleWidth(PREFIX_STAR + PADDING) : 0);
|
|
175658
|
+
if (bodyContentWidth <= 0) return [""];
|
|
175659
|
+
const mainSegments = this.state.mainText.split("\n");
|
|
175660
|
+
const subSegments = this.state.subText ? this.state.subText.split("\n") : [];
|
|
175661
|
+
const result = [];
|
|
175662
|
+
for (let i = 0; i < mainSegments.length; i++) {
|
|
175663
|
+
const wrapped = wrapTextWithAnsi(mainSegments[i], bodyContentWidth);
|
|
175664
|
+
for (let j = 0; j < wrapped.length; j++) {
|
|
175665
|
+
const boldLine = main(wrapped[j]);
|
|
175666
|
+
if (i === 0 && j === 0 && showTag) result.push(tagDisplay + boldLine);
|
|
175667
|
+
else result.push(bodyIndent + boldLine);
|
|
175668
|
+
}
|
|
175669
|
+
}
|
|
175670
|
+
for (const sub of subSegments) {
|
|
175671
|
+
const wrapped = wrapTextWithAnsi(sub, descContentWidth <= 0 ? bodyContentWidth : descContentWidth);
|
|
175672
|
+
for (const line of wrapped) result.push(descIndent + dim(line));
|
|
175673
|
+
}
|
|
175674
|
+
result.push("");
|
|
175675
|
+
return result;
|
|
175676
|
+
}
|
|
175677
|
+
};
|
|
175678
|
+
//#endregion
|
|
175160
175679
|
//#region src/tui/reverse-rpc/modal-coordinator.ts
|
|
175161
175680
|
var ReverseRpcModalCoordinator = class {
|
|
175162
175681
|
hooks;
|
|
@@ -176028,6 +176547,107 @@ function styleTitle(title, status, colors) {
|
|
|
176028
176547
|
}
|
|
176029
176548
|
}
|
|
176030
176549
|
//#endregion
|
|
176550
|
+
//#region src/tui/components/editor/wrapping-select-list.ts
|
|
176551
|
+
const DEFAULT_PRIMARY_COLUMN_WIDTH = 32;
|
|
176552
|
+
const PRIMARY_COLUMN_GAP = 2;
|
|
176553
|
+
const MIN_DESCRIPTION_WIDTH = 10;
|
|
176554
|
+
const DESCRIPTION_MAX_LINES = 2;
|
|
176555
|
+
const ELLIPSIS = "…";
|
|
176556
|
+
const ELLIPSIS_WIDTH = visibleWidth(ELLIPSIS);
|
|
176557
|
+
const TRAILING_ANSI_RESET = /(?:\u001B\[0m)+$/;
|
|
176558
|
+
function truncatePlainToWidth(text, maxWidth) {
|
|
176559
|
+
return truncateToWidth(text, maxWidth, "").replace(TRAILING_ANSI_RESET, "");
|
|
176560
|
+
}
|
|
176561
|
+
/**
|
|
176562
|
+
* SelectList that wraps item descriptions onto up to two lines instead of
|
|
176563
|
+
* truncating them to one. Long command / skill descriptions stay readable;
|
|
176564
|
+
* anything past the second line is ellipsized.
|
|
176565
|
+
*
|
|
176566
|
+
* Only `render` is replaced — selection, filtering, and key handling stay in
|
|
176567
|
+
* pi-tui. pi-tui keeps the row state private, so the renderer reads it
|
|
176568
|
+
* through a cast, the same idiom CustomEditor uses for autocomplete
|
|
176569
|
+
* internals.
|
|
176570
|
+
*/
|
|
176571
|
+
var WrappingSelectList = class extends SelectList {
|
|
176572
|
+
render(width) {
|
|
176573
|
+
const { filteredItems, selectedIndex, maxVisible, theme } = this.internals();
|
|
176574
|
+
if (filteredItems.length === 0) return [theme.noMatch(" No matching commands")];
|
|
176575
|
+
const primaryColumnWidth = this.primaryColumnWidth();
|
|
176576
|
+
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), filteredItems.length - maxVisible));
|
|
176577
|
+
const endIndex = Math.min(startIndex + maxVisible, filteredItems.length);
|
|
176578
|
+
const lines = [];
|
|
176579
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
176580
|
+
const item = filteredItems[i];
|
|
176581
|
+
if (!item) continue;
|
|
176582
|
+
lines.push(...this.renderItemLines(item, i === selectedIndex, width, primaryColumnWidth));
|
|
176583
|
+
}
|
|
176584
|
+
if (startIndex > 0 || endIndex < filteredItems.length) {
|
|
176585
|
+
const scrollText = ` (${selectedIndex + 1}/${filteredItems.length})`;
|
|
176586
|
+
lines.push(theme.scrollInfo(truncatePlainToWidth(scrollText, width - 2)));
|
|
176587
|
+
}
|
|
176588
|
+
return lines;
|
|
176589
|
+
}
|
|
176590
|
+
renderItemLines(item, isSelected, width, primaryColumnWidth) {
|
|
176591
|
+
const { theme } = this.internals();
|
|
176592
|
+
const prefix = isSelected ? "→ " : " ";
|
|
176593
|
+
const prefixWidth = visibleWidth(prefix);
|
|
176594
|
+
const description = item.description ? item.description.replaceAll(/[\r\n]+/g, " ").trim() : void 0;
|
|
176595
|
+
if (description && width > 40) {
|
|
176596
|
+
const effectivePrimaryColumnWidth = Math.max(1, Math.min(primaryColumnWidth, width - prefixWidth - 4));
|
|
176597
|
+
const maxPrimaryWidth = Math.max(1, effectivePrimaryColumnWidth - PRIMARY_COLUMN_GAP);
|
|
176598
|
+
const truncatedValue = this.truncatePrimaryValue(item, isSelected, maxPrimaryWidth, effectivePrimaryColumnWidth);
|
|
176599
|
+
const truncatedValueWidth = visibleWidth(truncatedValue);
|
|
176600
|
+
const spacing = " ".repeat(Math.max(1, effectivePrimaryColumnWidth - truncatedValueWidth));
|
|
176601
|
+
const descriptionStart = prefixWidth + truncatedValueWidth + spacing.length;
|
|
176602
|
+
const remainingWidth = width - descriptionStart - 2;
|
|
176603
|
+
if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
|
|
176604
|
+
const descriptionLines = wrapDescription(description, remainingWidth);
|
|
176605
|
+
const indent = " ".repeat(descriptionStart);
|
|
176606
|
+
if (isSelected) return descriptionLines.map((line, index) => theme.selectedText(index === 0 ? `${prefix}${truncatedValue}${spacing}${line}` : indent + line));
|
|
176607
|
+
return descriptionLines.map((line, index) => index === 0 ? prefix + truncatedValue + theme.description(spacing + line) : theme.description(indent + line));
|
|
176608
|
+
}
|
|
176609
|
+
}
|
|
176610
|
+
const maxWidth = width - prefixWidth - 2;
|
|
176611
|
+
const truncatedValue = this.truncatePrimaryValue(item, isSelected, maxWidth, maxWidth);
|
|
176612
|
+
return [isSelected ? theme.selectedText(`${prefix}${truncatedValue}`) : prefix + truncatedValue];
|
|
176613
|
+
}
|
|
176614
|
+
truncatePrimaryValue(item, isSelected, maxWidth, columnWidth) {
|
|
176615
|
+
const { layout } = this.internals();
|
|
176616
|
+
const displayValue = item.label || item.value;
|
|
176617
|
+
return truncatePlainToWidth(layout.truncatePrimary ? layout.truncatePrimary({
|
|
176618
|
+
text: displayValue,
|
|
176619
|
+
maxWidth,
|
|
176620
|
+
columnWidth,
|
|
176621
|
+
item,
|
|
176622
|
+
isSelected
|
|
176623
|
+
}) : displayValue, maxWidth);
|
|
176624
|
+
}
|
|
176625
|
+
primaryColumnWidth() {
|
|
176626
|
+
const { filteredItems, layout } = this.internals();
|
|
176627
|
+
const rawMin = layout.minPrimaryColumnWidth ?? layout.maxPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;
|
|
176628
|
+
const rawMax = layout.maxPrimaryColumnWidth ?? layout.minPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;
|
|
176629
|
+
const min = Math.max(1, Math.min(rawMin, rawMax));
|
|
176630
|
+
const max = Math.max(1, Math.max(rawMin, rawMax));
|
|
176631
|
+
const widest = filteredItems.reduce((acc, item) => Math.max(acc, visibleWidth(item.label || item.value) + PRIMARY_COLUMN_GAP), 0);
|
|
176632
|
+
return Math.max(min, Math.min(widest, max));
|
|
176633
|
+
}
|
|
176634
|
+
internals() {
|
|
176635
|
+
return this;
|
|
176636
|
+
}
|
|
176637
|
+
};
|
|
176638
|
+
/**
|
|
176639
|
+
* Wrap `text` to at most DESCRIPTION_MAX_LINES lines of `width` columns.
|
|
176640
|
+
* When the text needs more lines, the last visible line is rebuilt from the
|
|
176641
|
+
* remaining text and ellipsized.
|
|
176642
|
+
*/
|
|
176643
|
+
function wrapDescription(text, width) {
|
|
176644
|
+
const wrapped = wrapTextWithAnsi(text, width);
|
|
176645
|
+
if (wrapped.length <= DESCRIPTION_MAX_LINES) return wrapped;
|
|
176646
|
+
const kept = wrapped.slice(0, DESCRIPTION_MAX_LINES - 1);
|
|
176647
|
+
const clipped = truncatePlainToWidth(wrapped.slice(DESCRIPTION_MAX_LINES - 1).join(" "), width - ELLIPSIS_WIDTH).trimEnd();
|
|
176648
|
+
return [...kept, `${clipped}${ELLIPSIS}`];
|
|
176649
|
+
}
|
|
176650
|
+
//#endregion
|
|
176031
176651
|
//#region src/tui/components/editor/custom-editor.ts
|
|
176032
176652
|
/**
|
|
176033
176653
|
* Custom editor extending pi-tui Editor with app-level keybindings.
|
|
@@ -176040,6 +176660,10 @@ const KITTY_CSI_U = /^\u001B\[(\d+);(\d+)((?::\d+)*)u$/;
|
|
|
176040
176660
|
const CAPS_LOCK_BIT = 64;
|
|
176041
176661
|
const CTRL_BIT = 4;
|
|
176042
176662
|
const SHIFT_BIT = 1;
|
|
176663
|
+
const SLASH_COMMAND_SELECT_LIST_LAYOUT = {
|
|
176664
|
+
minPrimaryColumnWidth: 12,
|
|
176665
|
+
maxPrimaryColumnWidth: 32
|
|
176666
|
+
};
|
|
176043
176667
|
/**
|
|
176044
176668
|
* Workaround for a pi-tui bug that surfaces when Kitty keyboard protocol
|
|
176045
176669
|
* is active AND caps_lock is on. In that state terminals emit, e.g.,
|
|
@@ -176125,7 +176749,12 @@ var CustomEditor = class extends Editor {
|
|
|
176125
176749
|
consumingPaste = false;
|
|
176126
176750
|
consumeBuffer = "";
|
|
176127
176751
|
constructor(tui) {
|
|
176128
|
-
|
|
176752
|
+
const theme = createEditorTheme();
|
|
176753
|
+
super(tui, theme, { paddingX: 4 });
|
|
176754
|
+
this.createAutocompleteList = (prefix, items) => {
|
|
176755
|
+
if (prefix.startsWith("/")) return new WrappingSelectList(items, this.getAutocompleteMaxVisible(), theme.selectList, SLASH_COMMAND_SELECT_LIST_LAYOUT);
|
|
176756
|
+
return new SelectList(items, this.getAutocompleteMaxVisible(), theme.selectList);
|
|
176757
|
+
};
|
|
176129
176758
|
}
|
|
176130
176759
|
expandPasteMarkerAtCursor() {
|
|
176131
176760
|
const { line, col } = this.getCursor();
|
|
@@ -176756,7 +177385,8 @@ function createInitialAppState(input) {
|
|
|
176756
177385
|
availableProviders: {},
|
|
176757
177386
|
sessionTitle: null,
|
|
176758
177387
|
goal: null,
|
|
176759
|
-
mcpServersSummary: null
|
|
177388
|
+
mcpServersSummary: null,
|
|
177389
|
+
banner: void 0
|
|
176760
177390
|
};
|
|
176761
177391
|
}
|
|
176762
177392
|
var KimiTUI = class {
|
|
@@ -176853,6 +177483,7 @@ var KimiTUI = class {
|
|
|
176853
177483
|
const completer = cmd.completeArgs;
|
|
176854
177484
|
return {
|
|
176855
177485
|
name: cmd.name,
|
|
177486
|
+
aliases: cmd.aliases,
|
|
176856
177487
|
description: cmd.description,
|
|
176857
177488
|
...cmd.argumentHint !== void 0 ? { argumentHint: cmd.argumentHint } : {},
|
|
176858
177489
|
...completer !== void 0 ? { getArgumentCompletions: (prefix) => completer(prefix) } : {}
|
|
@@ -176921,10 +177552,28 @@ var KimiTUI = class {
|
|
|
176921
177552
|
throw error;
|
|
176922
177553
|
}
|
|
176923
177554
|
}
|
|
177555
|
+
async loadBanner() {
|
|
177556
|
+
const provider = new BannerProvider(this.state.appState.version);
|
|
177557
|
+
this.state.appState.banner = await provider.load();
|
|
177558
|
+
if (this.state.appState.banner !== null) {
|
|
177559
|
+
this.renderBanner();
|
|
177560
|
+
this.state.ui.requestRender();
|
|
177561
|
+
}
|
|
177562
|
+
}
|
|
177563
|
+
renderBanner() {
|
|
177564
|
+
if (this.state.appState.banner === null || this.state.appState.banner === void 0) return;
|
|
177565
|
+
if (this.state.transcriptContainer.children.some((child) => child instanceof BannerComponent)) return;
|
|
177566
|
+
const welcomeIndex = this.state.transcriptContainer.children.findIndex((child) => child instanceof WelcomeComponent);
|
|
177567
|
+
const banner = new BannerComponent(this.state.appState.banner);
|
|
177568
|
+
if (welcomeIndex >= 0) this.state.transcriptContainer.children.splice(welcomeIndex + 1, 0, banner);
|
|
177569
|
+
else this.state.transcriptContainer.children.unshift(banner);
|
|
177570
|
+
this.state.transcriptContainer.invalidate();
|
|
177571
|
+
}
|
|
176924
177572
|
async initMainTui() {
|
|
176925
177573
|
const shouldReplayHistory = await this.init();
|
|
176926
177574
|
this.mountFooter();
|
|
176927
177575
|
this.renderWelcome();
|
|
177576
|
+
this.loadBanner();
|
|
176928
177577
|
this.setupAutocomplete();
|
|
176929
177578
|
this.loadPersistedInputHistory();
|
|
176930
177579
|
this.state.editorContainer.clear();
|
|
@@ -177224,10 +177873,11 @@ var KimiTUI = class {
|
|
|
177224
177873
|
this.showError(message);
|
|
177225
177874
|
}
|
|
177226
177875
|
sendQueuedMessage(session, item) {
|
|
177227
|
-
this.harness.
|
|
177228
|
-
|
|
177229
|
-
|
|
177230
|
-
|
|
177876
|
+
this.harness.withInteractiveAgent(item.agentId ?? "main", () => {
|
|
177877
|
+
this.sendMessageInternal(session, item.text, {
|
|
177878
|
+
parts: item.parts,
|
|
177879
|
+
imageAttachmentIds: item.imageAttachmentIds
|
|
177880
|
+
});
|
|
177231
177881
|
});
|
|
177232
177882
|
}
|
|
177233
177883
|
requestQueuedGoalPromotion() {
|
|
@@ -177439,7 +178089,6 @@ var KimiTUI = class {
|
|
|
177439
178089
|
this.streamingUI.discardPending();
|
|
177440
178090
|
this.state.queuedMessages = [];
|
|
177441
178091
|
this.state.swarmModeEntry = void 0;
|
|
177442
|
-
this.harness.interactiveAgentId = MAIN_AGENT_ID;
|
|
177443
178092
|
this.streamingUI.resetToolCallState();
|
|
177444
178093
|
this.streamingUI.resetToolUi();
|
|
177445
178094
|
this.sessionEventHandler.resetRuntimeState();
|