@moonshot-ai/kimi-code 0.13.1 → 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 +852 -155
- 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 {
|
|
@@ -45872,7 +45898,7 @@ function convertMessage$1(message, reasoningKey, toolMessageConversion) {
|
|
|
45872
45898
|
const result = { role: message.role };
|
|
45873
45899
|
if (message.role === "tool") {
|
|
45874
45900
|
const effectiveConversion = message.content.some((p) => p.type !== "text" && p.type !== "think") ? "extract_text" : toolMessageConversion;
|
|
45875
|
-
if (effectiveConversion !== null) result.content =
|
|
45901
|
+
if (effectiveConversion !== null) result.content = convertToolMessageContentForChat(message, effectiveConversion);
|
|
45876
45902
|
else {
|
|
45877
45903
|
const firstPart = nonThinkParts[0];
|
|
45878
45904
|
if (nonThinkParts.length === 1 && firstPart?.type === "text") result.content = firstPart.text;
|
|
@@ -45896,6 +45922,48 @@ function convertMessage$1(message, reasoningKey, toolMessageConversion) {
|
|
|
45896
45922
|
if (reasoningContent) result[reasoningKey ?? DEFAULT_OUTBOUND_REASONING_KEY] = reasoningContent;
|
|
45897
45923
|
return result;
|
|
45898
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)";
|
|
45927
|
+
function convertToolMessageContentForChat(message, conversion) {
|
|
45928
|
+
const content = convertToolMessageContent(message, conversion);
|
|
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");
|
|
45935
|
+
}
|
|
45936
|
+
function toolResultImageParts(message) {
|
|
45937
|
+
const images = [];
|
|
45938
|
+
for (const part of message.content) {
|
|
45939
|
+
if (part.type !== "image_url") continue;
|
|
45940
|
+
const converted = convertContentPart(part);
|
|
45941
|
+
if (converted !== null) images.push(converted);
|
|
45942
|
+
}
|
|
45943
|
+
return images;
|
|
45944
|
+
}
|
|
45945
|
+
function appendToolResultMediaMessage(messages, pendingToolResultMedia) {
|
|
45946
|
+
if (pendingToolResultMedia.length === 0) return;
|
|
45947
|
+
messages.push({
|
|
45948
|
+
role: "user",
|
|
45949
|
+
content: [{
|
|
45950
|
+
type: "text",
|
|
45951
|
+
text: TOOL_RESULT_MEDIA_PROMPT
|
|
45952
|
+
}, ...pendingToolResultMedia]
|
|
45953
|
+
});
|
|
45954
|
+
pendingToolResultMedia.length = 0;
|
|
45955
|
+
}
|
|
45956
|
+
function convertHistoryMessages$1(history, reasoningKey, toolMessageConversion) {
|
|
45957
|
+
const messages = [];
|
|
45958
|
+
const pendingToolResultMedia = [];
|
|
45959
|
+
for (const msg of history) {
|
|
45960
|
+
if (msg.role !== "tool") appendToolResultMediaMessage(messages, pendingToolResultMedia);
|
|
45961
|
+
messages.push(convertMessage$1(msg, reasoningKey, toolMessageConversion));
|
|
45962
|
+
if (msg.role === "tool") pendingToolResultMedia.push(...toolResultImageParts(msg));
|
|
45963
|
+
}
|
|
45964
|
+
appendToolResultMediaMessage(messages, pendingToolResultMedia);
|
|
45965
|
+
return messages;
|
|
45966
|
+
}
|
|
45899
45967
|
var OpenAILegacyStreamedMessage = class {
|
|
45900
45968
|
_id = null;
|
|
45901
45969
|
_usage = null;
|
|
@@ -46031,7 +46099,7 @@ var OpenAILegacyChatProvider = class {
|
|
|
46031
46099
|
content: systemPrompt
|
|
46032
46100
|
});
|
|
46033
46101
|
const normalizedHistory = normalizeToolCallIdsForProvider(history, OPENAI_CHAT_TOOL_CALL_ID_POLICY);
|
|
46034
|
-
|
|
46102
|
+
messages.push(...convertHistoryMessages$1(normalizedHistory, this._reasoningKey, this._toolMessageConversion));
|
|
46035
46103
|
const kwargs = normalizeGenerationKwargs(this._model, this._generationKwargs);
|
|
46036
46104
|
let reasoningEffort = this._reasoningEffort;
|
|
46037
46105
|
if (reasoningEffort === void 0 && kwargs["reasoning_effort"] === void 0) {
|
|
@@ -46267,6 +46335,8 @@ function formatResponsesFailedResponse(response) {
|
|
|
46267
46335
|
const reason = incompleteDetails === void 0 ? void 0 : readStringField(incompleteDetails, "reason");
|
|
46268
46336
|
return reason === void 0 ? "Unknown error (no error details in response)" : `incomplete: ${reason}`;
|
|
46269
46337
|
}
|
|
46338
|
+
const OMITTED_AUDIO_PLACEHOLDER = "(audio omitted: unsupported audio format)";
|
|
46339
|
+
const OMITTED_VIDEO_PLACEHOLDER = "(video omitted: not supported by this provider)";
|
|
46270
46340
|
function contentPartsToInputItems(parts) {
|
|
46271
46341
|
const items = [];
|
|
46272
46342
|
for (const part of parts) switch (part.type) {
|
|
@@ -46285,11 +46355,19 @@ function contentPartsToInputItems(parts) {
|
|
|
46285
46355
|
break;
|
|
46286
46356
|
case "audio_url": {
|
|
46287
46357
|
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
46288
|
-
|
|
46358
|
+
items.push(mapped ?? {
|
|
46359
|
+
type: "input_text",
|
|
46360
|
+
text: OMITTED_AUDIO_PLACEHOLDER
|
|
46361
|
+
});
|
|
46289
46362
|
break;
|
|
46290
46363
|
}
|
|
46291
|
-
case "
|
|
46292
|
-
|
|
46364
|
+
case "video_url":
|
|
46365
|
+
items.push({
|
|
46366
|
+
type: "input_text",
|
|
46367
|
+
text: OMITTED_VIDEO_PLACEHOLDER
|
|
46368
|
+
});
|
|
46369
|
+
break;
|
|
46370
|
+
case "think": break;
|
|
46293
46371
|
}
|
|
46294
46372
|
return items;
|
|
46295
46373
|
}
|
|
@@ -46319,11 +46397,19 @@ function messageContentToFunctionOutputItems(content) {
|
|
|
46319
46397
|
break;
|
|
46320
46398
|
case "audio_url": {
|
|
46321
46399
|
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
46322
|
-
|
|
46400
|
+
items.push(mapped ?? {
|
|
46401
|
+
type: "input_text",
|
|
46402
|
+
text: OMITTED_AUDIO_PLACEHOLDER
|
|
46403
|
+
});
|
|
46323
46404
|
break;
|
|
46324
46405
|
}
|
|
46325
|
-
case "
|
|
46326
|
-
|
|
46406
|
+
case "video_url":
|
|
46407
|
+
items.push({
|
|
46408
|
+
type: "input_text",
|
|
46409
|
+
text: OMITTED_VIDEO_PLACEHOLDER
|
|
46410
|
+
});
|
|
46411
|
+
break;
|
|
46412
|
+
case "think": break;
|
|
46327
46413
|
}
|
|
46328
46414
|
return items;
|
|
46329
46415
|
}
|
|
@@ -46356,11 +46442,19 @@ function mapAudioUrlToInputItem(url) {
|
|
|
46356
46442
|
function convertMessage(message, modelName, toolMessageConversion) {
|
|
46357
46443
|
let role = message.role;
|
|
46358
46444
|
if (usesOpenAIResponsesDeveloperRole(modelName) && role === "system") role = "developer";
|
|
46359
|
-
if (role === "tool")
|
|
46360
|
-
|
|
46361
|
-
output
|
|
46362
|
-
|
|
46363
|
-
|
|
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
|
+
}
|
|
46364
46458
|
const result = [];
|
|
46365
46459
|
if (message.content.length > 0) {
|
|
46366
46460
|
const pendingParts = [];
|
|
@@ -46431,6 +46525,35 @@ function convertTool(tool) {
|
|
|
46431
46525
|
strict: false
|
|
46432
46526
|
};
|
|
46433
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
|
+
}
|
|
46434
46557
|
var OpenAIResponsesStreamedMessage = class {
|
|
46435
46558
|
_id = null;
|
|
46436
46559
|
_usage = null;
|
|
@@ -46692,16 +46815,8 @@ var OpenAIResponsesChatProvider = class {
|
|
|
46692
46815
|
}
|
|
46693
46816
|
async generate(systemPrompt, tools, history, options) {
|
|
46694
46817
|
const input = [];
|
|
46695
|
-
if (systemPrompt) {
|
|
46696
|
-
const sysItem = {
|
|
46697
|
-
role: "system",
|
|
46698
|
-
content: systemPrompt
|
|
46699
|
-
};
|
|
46700
|
-
if (usesOpenAIResponsesDeveloperRole(this._model)) sysItem["role"] = "developer";
|
|
46701
|
-
input.push(sysItem);
|
|
46702
|
-
}
|
|
46703
46818
|
const normalizedHistory = normalizeToolCallIdsForProvider(history, OPENAI_RESPONSES_TOOL_CALL_ID_POLICY);
|
|
46704
|
-
|
|
46819
|
+
input.push(...convertHistoryMessages(normalizedHistory, this._model, this._toolMessageConversion));
|
|
46705
46820
|
const kwargs = { ...this._generationKwargs };
|
|
46706
46821
|
const reasoningEffort = kwargs["reasoning_effort"];
|
|
46707
46822
|
delete kwargs["reasoning_effort"];
|
|
@@ -46723,6 +46838,7 @@ var OpenAIResponsesChatProvider = class {
|
|
|
46723
46838
|
stream: this._stream,
|
|
46724
46839
|
...kwargs
|
|
46725
46840
|
};
|
|
46841
|
+
if (systemPrompt) createParams["instructions"] = systemPrompt;
|
|
46726
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.");
|
|
46727
46843
|
return new OpenAIResponsesStreamedMessage(await client.responses.create(createParams, options?.signal ? { signal: options.signal } : void 0), this._stream);
|
|
46728
46844
|
} catch (error) {
|
|
@@ -47178,7 +47294,7 @@ const REDACTED_KEYS = new Set([
|
|
|
47178
47294
|
"bearer"
|
|
47179
47295
|
]);
|
|
47180
47296
|
const SAFE_KEY_RE = /^[\w.-]+$/;
|
|
47181
|
-
const ELLIPSIS$
|
|
47297
|
+
const ELLIPSIS$9 = "…";
|
|
47182
47298
|
const TRUNCATED_TAIL = ` …truncated`;
|
|
47183
47299
|
const REDACTED = "[REDACTED]";
|
|
47184
47300
|
const RAW_SECRET_PATTERNS = [
|
|
@@ -47217,7 +47333,7 @@ function redactCtx(ctx) {
|
|
|
47217
47333
|
return walk(ctx, 0);
|
|
47218
47334
|
}
|
|
47219
47335
|
function truncate$2(value, max) {
|
|
47220
|
-
return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$
|
|
47336
|
+
return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$9;
|
|
47221
47337
|
}
|
|
47222
47338
|
function serializeValue(raw) {
|
|
47223
47339
|
if (typeof raw === "string") return redactString(raw);
|
|
@@ -47407,7 +47523,6 @@ var RotatingFileSink = class {
|
|
|
47407
47523
|
pending = [];
|
|
47408
47524
|
dropped = 0;
|
|
47409
47525
|
closed = false;
|
|
47410
|
-
failed = false;
|
|
47411
47526
|
lastStderrNotice = 0;
|
|
47412
47527
|
currentBytes = -1;
|
|
47413
47528
|
directorySynced = false;
|
|
@@ -47464,7 +47579,6 @@ var RotatingFileSink = class {
|
|
|
47464
47579
|
await syncDir(dirname(this.options.path));
|
|
47465
47580
|
this.directorySynced = true;
|
|
47466
47581
|
}
|
|
47467
|
-
this.failed = false;
|
|
47468
47582
|
return true;
|
|
47469
47583
|
} catch (error) {
|
|
47470
47584
|
this.noteFailure(error);
|
|
@@ -47548,7 +47662,6 @@ var RotatingFileSink = class {
|
|
|
47548
47662
|
return line;
|
|
47549
47663
|
}
|
|
47550
47664
|
noteFailure(error) {
|
|
47551
|
-
this.failed = true;
|
|
47552
47665
|
const now = Date.now();
|
|
47553
47666
|
if (now - this.lastStderrNotice < STDERR_NOTICE_INTERVAL_MS) return;
|
|
47554
47667
|
this.lastStderrNotice = now;
|
|
@@ -49113,8 +49226,8 @@ function buildBackgroundTaskNotificationBody(info) {
|
|
|
49113
49226
|
//#endregion
|
|
49114
49227
|
//#region ../../packages/agent-core/src/utils/abort.ts
|
|
49115
49228
|
var import_retry = /* @__PURE__ */ __toESM(require_retry$4(), 1);
|
|
49116
|
-
function abortError$1() {
|
|
49117
|
-
const error =
|
|
49229
|
+
function abortError$1(message = "Aborted") {
|
|
49230
|
+
const error = new Error(message);
|
|
49118
49231
|
error.name = "AbortError";
|
|
49119
49232
|
return error;
|
|
49120
49233
|
}
|
|
@@ -56333,8 +56446,12 @@ var ConfigState = class {
|
|
|
56333
56446
|
return this._modelAlias;
|
|
56334
56447
|
}
|
|
56335
56448
|
get thinkingLevel() {
|
|
56449
|
+
if (this._thinkingLevel === "off" && this.alwaysThinkingModel) return resolveThinkingEffort("on", this.agent.kimiConfig?.thinking);
|
|
56336
56450
|
return this._thinkingLevel;
|
|
56337
56451
|
}
|
|
56452
|
+
get alwaysThinkingModel() {
|
|
56453
|
+
return this.tryResolvedProviderConfig()?.alwaysThinking === true;
|
|
56454
|
+
}
|
|
56338
56455
|
get profileName() {
|
|
56339
56456
|
return this._profileName;
|
|
56340
56457
|
}
|
|
@@ -57477,6 +57594,7 @@ const HOOK_EVENT_TYPES = [
|
|
|
57477
57594
|
"UserPromptSubmit",
|
|
57478
57595
|
"Stop",
|
|
57479
57596
|
"StopFailure",
|
|
57597
|
+
"Interrupt",
|
|
57480
57598
|
"SessionStart",
|
|
57481
57599
|
"SessionEnd",
|
|
57482
57600
|
"SubagentStart",
|
|
@@ -57980,6 +58098,31 @@ var InjectionManager = class {
|
|
|
57980
58098
|
}
|
|
57981
58099
|
};
|
|
57982
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
|
|
57983
58126
|
//#region ../../packages/agent-core/src/agent/permission/policies/auto-mode-approve.ts
|
|
57984
58127
|
var AutoModeApprovePermissionPolicy = class {
|
|
57985
58128
|
agent;
|
|
@@ -58422,11 +58565,7 @@ function parseGitDir(content, markerParent) {
|
|
|
58422
58565
|
//#endregion
|
|
58423
58566
|
//#region ../../packages/agent-core/src/agent/permission/policies/file-access-ask.ts
|
|
58424
58567
|
var SensitiveFileAccessAskPermissionPolicy = class {
|
|
58425
|
-
agent;
|
|
58426
58568
|
name = "sensitive-file-access-ask";
|
|
58427
|
-
constructor(agent) {
|
|
58428
|
-
this.agent = agent;
|
|
58429
|
-
}
|
|
58430
58569
|
evaluate(context) {
|
|
58431
58570
|
const access = fileAccesses(context).find((fileAccess) => isSensitiveFile(fileAccess.path));
|
|
58432
58571
|
if (access === void 0) return;
|
|
@@ -60676,6 +60815,7 @@ var YoloModeApprovePermissionPolicy = class {
|
|
|
60676
60815
|
function createPermissionDecisionPolicies(agent) {
|
|
60677
60816
|
return [
|
|
60678
60817
|
new PreToolCallHookPermissionPolicy(agent),
|
|
60818
|
+
new AgentSwarmExclusiveDenyPermissionPolicy(),
|
|
60679
60819
|
new AutoModeAskUserQuestionDenyPermissionPolicy(agent),
|
|
60680
60820
|
new PlanModeGuardDenyPermissionPolicy(agent),
|
|
60681
60821
|
new UserConfiguredDenyPermissionPolicy(agent),
|
|
@@ -60685,7 +60825,7 @@ function createPermissionDecisionPolicies(agent) {
|
|
|
60685
60825
|
new UserConfiguredAllowPermissionPolicy(agent),
|
|
60686
60826
|
new ExitPlanModeReviewAskPermissionPolicy(agent),
|
|
60687
60827
|
new PlanModeToolApprovePermissionPolicy(agent),
|
|
60688
|
-
new SensitiveFileAccessAskPermissionPolicy(
|
|
60828
|
+
new SensitiveFileAccessAskPermissionPolicy(),
|
|
60689
60829
|
new GitControlPathAccessAskPermissionPolicy(agent),
|
|
60690
60830
|
new YoloModeApprovePermissionPolicy(agent),
|
|
60691
60831
|
new SwarmModeAgentSwarmApprovePermissionPolicy(agent),
|
|
@@ -61962,12 +62102,16 @@ var AgentRecords = class {
|
|
|
61962
62102
|
//#region ../../packages/agent-core/src/agent/replay/index.ts
|
|
61963
62103
|
var ReplayBuilder = class {
|
|
61964
62104
|
agent;
|
|
62105
|
+
captureLiveRecords = false;
|
|
61965
62106
|
records = [];
|
|
61966
62107
|
constructor(agent) {
|
|
61967
62108
|
this.agent = agent;
|
|
61968
62109
|
}
|
|
61969
62110
|
push(record) {
|
|
61970
|
-
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
|
+
});
|
|
61971
62115
|
}
|
|
61972
62116
|
removeLastMessages(removedMessages) {
|
|
61973
62117
|
if (removedMessages.size === 0) return;
|
|
@@ -65443,7 +65587,7 @@ function isUserActivatableSkillType(type) {
|
|
|
65443
65587
|
return isInlineSkillType(type) || type === "flow";
|
|
65444
65588
|
}
|
|
65445
65589
|
function isSupportedSkillType(type) {
|
|
65446
|
-
return isUserActivatableSkillType(type);
|
|
65590
|
+
return isUserActivatableSkillType(type) || type === "reference";
|
|
65447
65591
|
}
|
|
65448
65592
|
function summarizeSkill(skill) {
|
|
65449
65593
|
return {
|
|
@@ -65654,7 +65798,7 @@ function isRecord$11(value) {
|
|
|
65654
65798
|
}
|
|
65655
65799
|
//#endregion
|
|
65656
65800
|
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.md
|
|
65657
|
-
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";
|
|
65658
65802
|
//#endregion
|
|
65659
65803
|
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.ts
|
|
65660
65804
|
const PSEUDO_PATH$3 = "builtin://custom-theme";
|
|
@@ -65698,7 +65842,7 @@ const IMPORT_FROM_CC_CODEX_SKILL = {
|
|
|
65698
65842
|
};
|
|
65699
65843
|
//#endregion
|
|
65700
65844
|
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.md
|
|
65701
|
-
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";
|
|
65702
65846
|
//#endregion
|
|
65703
65847
|
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.ts
|
|
65704
65848
|
const PSEUDO_PATH$1 = "builtin://mcp-config";
|
|
@@ -65755,7 +65899,7 @@ const SUB_SKILL_REVIEW = makeBuiltin(SKILL_default$1, "sub-skill.review", "built
|
|
|
65755
65899
|
const SUB_SKILL_CONSOLIDATE = makeBuiltin(SKILL_default$2, "sub-skill.consolidate", "builtin://sub-skill/consolidate", { disableModelInvocation: true });
|
|
65756
65900
|
//#endregion
|
|
65757
65901
|
//#region ../../packages/agent-core/src/skill/builtin/update-config.md
|
|
65758
|
-
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";
|
|
65759
65903
|
//#endregion
|
|
65760
65904
|
//#region ../../packages/agent-core/src/skill/builtin/update-config.ts
|
|
65761
65905
|
const PSEUDO_PATH = "builtin://update-config";
|
|
@@ -73771,6 +73915,10 @@ function abortedToolOutput(toolName, signal) {
|
|
|
73771
73915
|
}
|
|
73772
73916
|
async function runToolCallBatch(step, response) {
|
|
73773
73917
|
if (response.toolCalls.length === 0) return { stopTurn: false };
|
|
73918
|
+
const batchStep = {
|
|
73919
|
+
...step,
|
|
73920
|
+
toolCalls: response.toolCalls
|
|
73921
|
+
};
|
|
73774
73922
|
const calls = response.toolCalls.map((toolCall) => preflightToolCall(step.tools, toolCall));
|
|
73775
73923
|
const scheduler = new ToolScheduler();
|
|
73776
73924
|
const pendingResults = [];
|
|
@@ -73778,19 +73926,19 @@ async function runToolCallBatch(step, response) {
|
|
|
73778
73926
|
try {
|
|
73779
73927
|
for (let index = 0; index < calls.length; index += 1) {
|
|
73780
73928
|
const call = calls[index];
|
|
73781
|
-
const prepared = await prepareToolCall(
|
|
73929
|
+
const prepared = await prepareToolCall(batchStep, call);
|
|
73782
73930
|
pendingResults.push(scheduler.add(prepared.task));
|
|
73783
73931
|
if (prepared.stopBatchAfterThis === true) {
|
|
73784
73932
|
stopTurn = true;
|
|
73785
73933
|
for (const skippedCall of calls.slice(index + 1)) {
|
|
73786
|
-
const skippedTask = await prepareSkippedToolCall(
|
|
73934
|
+
const skippedTask = await prepareSkippedToolCall(batchStep, skippedCall);
|
|
73787
73935
|
pendingResults.push(scheduler.add(skippedTask));
|
|
73788
73936
|
}
|
|
73789
73937
|
break;
|
|
73790
73938
|
}
|
|
73791
73939
|
}
|
|
73792
73940
|
for (const pendingResult of pendingResults) {
|
|
73793
|
-
const result = await finalizePendingToolResult(
|
|
73941
|
+
const result = await finalizePendingToolResult(batchStep, await pendingResult);
|
|
73794
73942
|
if (result.stopTurn === true) stopTurn = true;
|
|
73795
73943
|
await step.dispatchEvent({
|
|
73796
73944
|
type: "tool.result",
|
|
@@ -73945,6 +74093,7 @@ async function runPrepareToolExecutionHook(step, call) {
|
|
|
73945
74093
|
try {
|
|
73946
74094
|
hookResult = await hooks.prepareToolExecution({
|
|
73947
74095
|
toolCall,
|
|
74096
|
+
toolCalls: step.toolCalls,
|
|
73948
74097
|
tool: call.tool,
|
|
73949
74098
|
args,
|
|
73950
74099
|
turnId,
|
|
@@ -73987,6 +74136,7 @@ async function runAuthorizeToolExecutionHook(step, call, args, execution) {
|
|
|
73987
74136
|
try {
|
|
73988
74137
|
return await hooks.authorizeToolExecution({
|
|
73989
74138
|
toolCall: call.toolCall,
|
|
74139
|
+
toolCalls: step.toolCalls,
|
|
73990
74140
|
tool: call.tool,
|
|
73991
74141
|
args,
|
|
73992
74142
|
execution,
|
|
@@ -74042,6 +74192,7 @@ async function finalizePendingToolResult(step, pendingResult) {
|
|
|
74042
74192
|
try {
|
|
74043
74193
|
const effectiveResult = coerceToolResult(await hooks.finalizeToolResult({
|
|
74044
74194
|
toolCall: pendingResult.toolCall,
|
|
74195
|
+
toolCalls: step.toolCalls,
|
|
74045
74196
|
args: pendingResult.args,
|
|
74046
74197
|
result: pendingResult.result,
|
|
74047
74198
|
turnId,
|
|
@@ -81688,7 +81839,7 @@ function buildSubagentDescriptions(subagents) {
|
|
|
81688
81839
|
}
|
|
81689
81840
|
//#endregion
|
|
81690
81841
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.md
|
|
81691
|
-
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";
|
|
81692
81843
|
//#endregion
|
|
81693
81844
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.ts
|
|
81694
81845
|
const DEFAULT_SUBAGENT_TYPE = "coder";
|
|
@@ -89216,6 +89367,10 @@ var TurnFlow = class {
|
|
|
89216
89367
|
}
|
|
89217
89368
|
}
|
|
89218
89369
|
if (this.currentId === turnId) this.agent.usage.endTurn();
|
|
89370
|
+
if (ended.reason === "cancelled" && isUserCancellation(signal.reason)) this.agent.hooks?.fireAndForgetTrigger("Interrupt", { inputData: {
|
|
89371
|
+
turnId,
|
|
89372
|
+
reason: "cancelled"
|
|
89373
|
+
} });
|
|
89219
89374
|
this.agent.emitEvent(ended);
|
|
89220
89375
|
if (standalone && this.currentId === turnId) this.activeTurn = null;
|
|
89221
89376
|
if (this.agent.swarmMode.shouldAutoExit) this.agent.swarmMode.exit();
|
|
@@ -90190,7 +90345,8 @@ var BashTool = class {
|
|
|
90190
90345
|
}, timeoutMs);
|
|
90191
90346
|
try {
|
|
90192
90347
|
const builder = new ToolResultBuilder();
|
|
90193
|
-
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()]);
|
|
90194
90350
|
if (timedOut) {
|
|
90195
90351
|
const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
|
|
90196
90352
|
return builder.error(`Command killed by timeout (${timeoutLabel})`, { brief: `Killed by timeout (${timeoutLabel})` });
|
|
@@ -90253,7 +90409,7 @@ var BashTool = class {
|
|
|
90253
90409
|
const info = backgroundManager.getTask(taskId);
|
|
90254
90410
|
if (info && info.status === "running") backgroundManager.stop(taskId, "Timed out");
|
|
90255
90411
|
})();
|
|
90256
|
-
}, timeoutMs);
|
|
90412
|
+
}, timeoutMs).unref?.();
|
|
90257
90413
|
const status = backgroundManager.getTask(taskId).status;
|
|
90258
90414
|
const builder = new ToolResultBuilder();
|
|
90259
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.
|
|
@@ -90263,14 +90419,21 @@ human_shell_hint: Tell the human to run /tasks to open the interactive backgroun
|
|
|
90263
90419
|
return builder.ok("Background task started", { brief: `Started ${taskId}` });
|
|
90264
90420
|
}
|
|
90265
90421
|
};
|
|
90266
|
-
async function readStreamIntoBuilder(stream, builder) {
|
|
90422
|
+
async function readStreamIntoBuilder(stream, builder, suppressPrematureClose) {
|
|
90267
90423
|
const decoder = new StringDecoder("utf8");
|
|
90268
|
-
|
|
90269
|
-
|
|
90270
|
-
|
|
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;
|
|
90271
90431
|
}
|
|
90272
90432
|
builder.write(decoder.end());
|
|
90273
90433
|
}
|
|
90434
|
+
function isPrematureCloseError(error) {
|
|
90435
|
+
return error instanceof Error && error.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
90436
|
+
}
|
|
90274
90437
|
function shellQuote(s) {
|
|
90275
90438
|
return `'${s.replaceAll("'", "'\\''")}'`;
|
|
90276
90439
|
}
|
|
@@ -121053,6 +121216,32 @@ var McpConnectionManager = class {
|
|
|
121053
121216
|
this.initialLoad = initialLoad;
|
|
121054
121217
|
return initialLoad;
|
|
121055
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
|
+
}
|
|
121056
121245
|
waitForInitialLoad(signal) {
|
|
121057
121246
|
signal?.throwIfAborted();
|
|
121058
121247
|
if (signal === void 0) return this.initialLoad;
|
|
@@ -121402,6 +121591,20 @@ function mergeCallerMcpServers(base, callerServers) {
|
|
|
121402
121591
|
//#endregion
|
|
121403
121592
|
//#region ../../packages/agent-core/src/session/index.ts
|
|
121404
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
|
+
}
|
|
121405
121608
|
var Session$1 = class {
|
|
121406
121609
|
options;
|
|
121407
121610
|
rpc;
|
|
@@ -121493,6 +121696,7 @@ var Session$1 = class {
|
|
|
121493
121696
|
async close() {
|
|
121494
121697
|
try {
|
|
121495
121698
|
await Promise.allSettled(Array.from(this.readyAgents(), async (agent) => agent.cron?.stop()));
|
|
121699
|
+
await this.cancelActiveTurnsOnClose();
|
|
121496
121700
|
await this.stopBackgroundTasksOnExit();
|
|
121497
121701
|
await this.flushMetadata();
|
|
121498
121702
|
await this.triggerSessionEnd("exit");
|
|
@@ -121516,12 +121720,46 @@ var Session$1 = class {
|
|
|
121516
121720
|
}
|
|
121517
121721
|
}
|
|
121518
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
|
+
}
|
|
121519
121757
|
async stopBackgroundTasksOnExit() {
|
|
121520
121758
|
if (resolveConfigValue({
|
|
121521
121759
|
env: process.env,
|
|
121522
121760
|
envKey: BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV,
|
|
121523
121761
|
configValue: this.options.background?.keepAliveOnExit,
|
|
121524
|
-
defaultValue:
|
|
121762
|
+
defaultValue: false,
|
|
121525
121763
|
parseEnv: parseBooleanEnv
|
|
121526
121764
|
})) return;
|
|
121527
121765
|
await Promise.all(Array.from(this.readyAgents(), (agent) => agent.background.stopAll("Session closed")));
|
|
@@ -136903,6 +137141,7 @@ var ProviderManager = class {
|
|
|
136903
137141
|
providerName,
|
|
136904
137142
|
provider,
|
|
136905
137143
|
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
137144
|
+
alwaysThinking: (alias.capabilities ?? []).some((c) => c.trim().toLowerCase() === "always_thinking"),
|
|
136906
137145
|
maxOutputSize: alias.maxOutputSize
|
|
136907
137146
|
};
|
|
136908
137147
|
}
|
|
@@ -138287,9 +138526,11 @@ var LocalKaos = class LocalKaos {
|
|
|
138287
138526
|
name = "local";
|
|
138288
138527
|
osEnv;
|
|
138289
138528
|
_cwd;
|
|
138290
|
-
|
|
138529
|
+
_envLayers;
|
|
138530
|
+
constructor(osEnv, cwd, envLayers = []) {
|
|
138291
138531
|
this._cwd = normalize(cwd ?? process.cwd());
|
|
138292
138532
|
this.osEnv = osEnv;
|
|
138533
|
+
this._envLayers = envLayers;
|
|
138293
138534
|
}
|
|
138294
138535
|
/**
|
|
138295
138536
|
* Construct a fresh `LocalKaos` after probing the host environment.
|
|
@@ -138302,7 +138543,10 @@ var LocalKaos = class LocalKaos {
|
|
|
138302
138543
|
return new LocalKaos(await detectEnvironmentFromNode());
|
|
138303
138544
|
}
|
|
138304
138545
|
withCwd(cwd) {
|
|
138305
|
-
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]);
|
|
138306
138550
|
}
|
|
138307
138551
|
_resolvePath(path) {
|
|
138308
138552
|
if (isAbsolute(path)) return normalize(path);
|
|
@@ -138495,7 +138739,8 @@ var LocalKaos = class LocalKaos {
|
|
|
138495
138739
|
"pipe",
|
|
138496
138740
|
"pipe"
|
|
138497
138741
|
],
|
|
138498
|
-
detached: !isWindows
|
|
138742
|
+
detached: !isWindows,
|
|
138743
|
+
env: this._buildExecEnv()
|
|
138499
138744
|
});
|
|
138500
138745
|
await waitForSpawn(child);
|
|
138501
138746
|
return new LocalProcess(child);
|
|
@@ -138511,11 +138756,20 @@ var LocalKaos = class LocalKaos {
|
|
|
138511
138756
|
"pipe"
|
|
138512
138757
|
],
|
|
138513
138758
|
detached: !isWindows,
|
|
138514
|
-
env
|
|
138759
|
+
env: this._buildExecEnv(env)
|
|
138515
138760
|
});
|
|
138516
138761
|
await waitForSpawn(child);
|
|
138517
138762
|
return new LocalProcess(child);
|
|
138518
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
|
+
}
|
|
138519
138773
|
};
|
|
138520
138774
|
function waitForSpawn(child) {
|
|
138521
138775
|
return new Promise((resolve, reject) => {
|
|
@@ -139601,8 +139855,8 @@ var KimiHarness = class {
|
|
|
139601
139855
|
get interactiveAgentId() {
|
|
139602
139856
|
return this.rpc.interactiveAgentId;
|
|
139603
139857
|
}
|
|
139604
|
-
|
|
139605
|
-
this.rpc.
|
|
139858
|
+
withInteractiveAgent(agentId, fn) {
|
|
139859
|
+
return this.rpc.withInteractiveAgent(agentId, fn);
|
|
139606
139860
|
}
|
|
139607
139861
|
track(event, properties) {
|
|
139608
139862
|
this.telemetry.track(event, properties);
|
|
@@ -142054,7 +142308,19 @@ function managedModelKey(modelId) {
|
|
|
142054
142308
|
}
|
|
142055
142309
|
function capabilitiesForModel$1(model) {
|
|
142056
142310
|
const caps = /* @__PURE__ */ new Set();
|
|
142057
|
-
|
|
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
|
+
}
|
|
142058
142324
|
if (model.supportsImageIn) caps.add("image_in");
|
|
142059
142325
|
if (model.supportsVideoIn) caps.add("video_in");
|
|
142060
142326
|
if (model.supportsToolUse ?? true) caps.add("tool_use");
|
|
@@ -142197,9 +142463,13 @@ function toModelInfo$1(item) {
|
|
|
142197
142463
|
supportsImageIn: Boolean(item["supports_image_in"]),
|
|
142198
142464
|
supportsVideoIn: Boolean(item["supports_video_in"]),
|
|
142199
142465
|
supportsToolUse,
|
|
142466
|
+
supportsThinkingType: parseSupportsThinkingType(item["supports_thinking_type"]),
|
|
142200
142467
|
displayName: normalizedDisplayName
|
|
142201
142468
|
};
|
|
142202
142469
|
}
|
|
142470
|
+
function parseSupportsThinkingType(value) {
|
|
142471
|
+
return value === "only" || value === "no" || value === "both" ? value : void 0;
|
|
142472
|
+
}
|
|
142203
142473
|
async function fetchManagedKimiCodeModels(options) {
|
|
142204
142474
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
142205
142475
|
const baseUrl = defaultBaseUrl(options.baseUrl);
|
|
@@ -142288,6 +142558,11 @@ function applyManagedKimiCodeLogoutConfig(config) {
|
|
|
142288
142558
|
if (Object.keys(config.services).length === 0) config.services = void 0;
|
|
142289
142559
|
}
|
|
142290
142560
|
}
|
|
142561
|
+
function forcedThinking(model, fallback) {
|
|
142562
|
+
if (model?.supportsThinkingType === "only") return true;
|
|
142563
|
+
if (model?.supportsThinkingType === "no") return false;
|
|
142564
|
+
return fallback;
|
|
142565
|
+
}
|
|
142291
142566
|
function selectDefaultModel(config, models, options) {
|
|
142292
142567
|
const firstModel = models[0];
|
|
142293
142568
|
if (firstModel === void 0) throw new Error("No models available for Kimi Code.");
|
|
@@ -142298,12 +142573,12 @@ function selectDefaultModel(config, models, options) {
|
|
|
142298
142573
|
const preservedModel = managedModels.get(currentDefault);
|
|
142299
142574
|
return {
|
|
142300
142575
|
modelKey: currentDefault,
|
|
142301
|
-
thinking: config.defaultThinking ?? preservedModel?.supportsReasoning ?? false
|
|
142576
|
+
thinking: forcedThinking(preservedModel, config.defaultThinking ?? preservedModel?.supportsReasoning ?? false)
|
|
142302
142577
|
};
|
|
142303
142578
|
}
|
|
142304
142579
|
return {
|
|
142305
142580
|
modelKey: managedModelKey(firstModel.id),
|
|
142306
|
-
thinking: config.defaultThinking ?? firstModel.supportsReasoning
|
|
142581
|
+
thinking: forcedThinking(firstModel, config.defaultThinking ?? firstModel.supportsReasoning)
|
|
142307
142582
|
};
|
|
142308
142583
|
}
|
|
142309
142584
|
function canPreserveDefaultModel(existingModels, defaultModel, managedModels) {
|
|
@@ -142415,12 +142690,25 @@ function toModelInfo(item) {
|
|
|
142415
142690
|
supportsImageIn: Boolean(item["supports_image_in"]),
|
|
142416
142691
|
supportsVideoIn: Boolean(item["supports_video_in"]),
|
|
142417
142692
|
supportsToolUse,
|
|
142693
|
+
supportsThinkingType: parseSupportsThinkingType(item["supports_thinking_type"]),
|
|
142418
142694
|
displayName: normalizedDisplayName
|
|
142419
142695
|
};
|
|
142420
142696
|
}
|
|
142421
142697
|
function capabilitiesForModel(model) {
|
|
142422
142698
|
const caps = /* @__PURE__ */ new Set();
|
|
142423
|
-
|
|
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
|
+
}
|
|
142424
142712
|
if (model.supportsImageIn) caps.add("image_in");
|
|
142425
142713
|
if (model.supportsVideoIn) caps.add("video_in");
|
|
142426
142714
|
if (model.supportsToolUse ?? true) caps.add("tool_use");
|
|
@@ -143052,10 +143340,16 @@ var KimiAuthFacade = class {
|
|
|
143052
143340
|
//#region ../../packages/node-sdk/src/rpc.ts
|
|
143053
143341
|
const MAIN_AGENT_ID$2 = "main";
|
|
143054
143342
|
var SDKRpcClientBase = class {
|
|
143055
|
-
|
|
143343
|
+
interactiveAgentScope = new AsyncLocalStorage();
|
|
143056
143344
|
eventListeners = /* @__PURE__ */ new Set();
|
|
143057
143345
|
approvalHandlers = /* @__PURE__ */ new Map();
|
|
143058
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
|
+
}
|
|
143059
143353
|
async createSession(input) {
|
|
143060
143354
|
const rpc = await this.getRpc();
|
|
143061
143355
|
const { planMode, ...coreInput } = input;
|
|
@@ -144353,6 +144647,7 @@ const FEEDBACK_VERSION_PREFIX = "kimi-code-";
|
|
|
144353
144647
|
const FEEDBACK_TELEMETRY_EVENT = "feedback_submitted";
|
|
144354
144648
|
const KIMI_CODE_CDN_BASE = "https://code.kimi.com/kimi-code";
|
|
144355
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";
|
|
144356
144651
|
const KIMI_CODE_PLUGIN_MARKETPLACE_URL = `${KIMI_CODE_CDN_BASE}/plugins/marketplace.json`;
|
|
144357
144652
|
const KIMI_CODE_PLUGIN_MARKETPLACE_URL_ENV = "KIMI_CODE_PLUGIN_MARKETPLACE_URL";
|
|
144358
144653
|
const KIMI_CODE_INSTALL_SH_URL = `${KIMI_CODE_CDN_BASE}/install.sh`;
|
|
@@ -150494,6 +150789,9 @@ var AcpKaos = class AcpKaos {
|
|
|
150494
150789
|
withCwd(cwd) {
|
|
150495
150790
|
return new AcpKaos(this.conn, this.sessionId, this.inner.withCwd(cwd));
|
|
150496
150791
|
}
|
|
150792
|
+
withEnv(env) {
|
|
150793
|
+
return new AcpKaos(this.conn, this.sessionId, this.inner.withEnv(env));
|
|
150794
|
+
}
|
|
150497
150795
|
stat(path, options) {
|
|
150498
150796
|
return this.inner.stat(path, options);
|
|
150499
150797
|
}
|
|
@@ -150514,13 +150812,14 @@ var AcpKaos = class AcpKaos {
|
|
|
150514
150812
|
* should be routed through a non-ACP Kaos.
|
|
150515
150813
|
*/
|
|
150516
150814
|
async readText(path, _options) {
|
|
150815
|
+
const rpcPath = this.toClientPath(path);
|
|
150517
150816
|
try {
|
|
150518
150817
|
return (await this.conn.readTextFile({
|
|
150519
150818
|
sessionId: this.sessionId,
|
|
150520
|
-
path
|
|
150819
|
+
path: rpcPath
|
|
150521
150820
|
})).content;
|
|
150522
150821
|
} catch (err) {
|
|
150523
|
-
throw wrapKaosError(`acp: readTextFile failed for ${
|
|
150822
|
+
throw wrapKaosError(`acp: readTextFile failed for ${rpcPath}`, err);
|
|
150524
150823
|
}
|
|
150525
150824
|
}
|
|
150526
150825
|
/**
|
|
@@ -150598,16 +150897,21 @@ var AcpKaos = class AcpKaos {
|
|
|
150598
150897
|
return data.byteLength;
|
|
150599
150898
|
}
|
|
150600
150899
|
async acpWrite(path, content) {
|
|
150900
|
+
const rpcPath = this.toClientPath(path);
|
|
150601
150901
|
try {
|
|
150602
150902
|
await this.conn.writeTextFile({
|
|
150603
150903
|
sessionId: this.sessionId,
|
|
150604
|
-
path,
|
|
150904
|
+
path: rpcPath,
|
|
150605
150905
|
content
|
|
150606
150906
|
});
|
|
150607
150907
|
} catch (err) {
|
|
150608
|
-
throw wrapKaosError(`acp: writeTextFile failed for ${
|
|
150908
|
+
throw wrapKaosError(`acp: writeTextFile failed for ${rpcPath}`, err);
|
|
150609
150909
|
}
|
|
150610
150910
|
}
|
|
150911
|
+
toClientPath(path) {
|
|
150912
|
+
if (this.inner.pathClass() !== "win32") return path;
|
|
150913
|
+
return path.replaceAll("/", "\\");
|
|
150914
|
+
}
|
|
150611
150915
|
exec(...args) {
|
|
150612
150916
|
return this.inner.exec(...args);
|
|
150613
150917
|
}
|
|
@@ -151654,13 +151958,24 @@ function acpModeToToggles(id) {
|
|
|
151654
151958
|
*/
|
|
151655
151959
|
const TOGGLEABLE_THINKING_MODELS = new Set(["kimi-for-coding", "kimi-code"]);
|
|
151656
151960
|
function deriveThinkingSupported(alias) {
|
|
151657
|
-
|
|
151961
|
+
const declared = alias.capabilities ?? [];
|
|
151962
|
+
if (declared.includes("thinking") || declared.includes("always_thinking")) return true;
|
|
151658
151963
|
const lower = alias.model.toLowerCase();
|
|
151659
151964
|
if (lower.includes("thinking") || lower.includes("reason")) return true;
|
|
151660
151965
|
if (TOGGLEABLE_THINKING_MODELS.has(alias.model)) return true;
|
|
151661
151966
|
return false;
|
|
151662
151967
|
}
|
|
151663
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
|
+
/**
|
|
151664
151979
|
* Project `harness.getConfig().models` into a flat catalog. Returns an
|
|
151665
151980
|
* empty array when the harness has no models configured, when
|
|
151666
151981
|
* `getConfig` is missing on the harness (partial test stubs), or when
|
|
@@ -151681,7 +151996,8 @@ async function listModelsFromHarness(harness) {
|
|
|
151681
151996
|
for (const [id, alias] of Object.entries(models)) out.push({
|
|
151682
151997
|
id,
|
|
151683
151998
|
name: alias.displayName ?? alias.model ?? id,
|
|
151684
|
-
thinkingSupported: deriveThinkingSupported(alias)
|
|
151999
|
+
thinkingSupported: deriveThinkingSupported(alias),
|
|
152000
|
+
alwaysThinking: deriveAlwaysThinking(alias)
|
|
151685
152001
|
});
|
|
151686
152002
|
return out;
|
|
151687
152003
|
}
|
|
@@ -151740,8 +152056,26 @@ function buildModelOption(models, currentBaseModelId) {
|
|
|
151740
152056
|
* currently-selected model has `thinkingSupported === false`, the
|
|
151741
152057
|
* snapshot omits it entirely (dynamic visibility), so the client never
|
|
151742
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.
|
|
151743
152066
|
*/
|
|
151744
|
-
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
|
+
};
|
|
151745
152079
|
return {
|
|
151746
152080
|
type: "select",
|
|
151747
152081
|
id: "thinking",
|
|
@@ -151808,9 +152142,11 @@ function buildModeOption(currentModeId) {
|
|
|
151808
152142
|
*/
|
|
151809
152143
|
async function buildSessionConfigOptions(harness, currentBaseModelId, currentThinkingEnabled, currentModeId) {
|
|
151810
152144
|
const models = await listModelsFromHarness(harness);
|
|
151811
|
-
const
|
|
152145
|
+
const currentModelEntry = models.find((m) => m.id === currentBaseModelId);
|
|
152146
|
+
const showThinking = currentModelEntry?.thinkingSupported === true;
|
|
152147
|
+
const alwaysThinking = currentModelEntry?.alwaysThinking === true;
|
|
151812
152148
|
const out = [buildModelOption(models, currentBaseModelId)];
|
|
151813
|
-
if (showThinking) out.push(buildThinkingOption(currentThinkingEnabled));
|
|
152149
|
+
if (showThinking) out.push(buildThinkingOption(alwaysThinking || currentThinkingEnabled, alwaysThinking));
|
|
151814
152150
|
out.push(buildModeOption(currentModeId));
|
|
151815
152151
|
return out;
|
|
151816
152152
|
}
|
|
@@ -152156,11 +152492,25 @@ var AcpSession = class {
|
|
|
152156
152492
|
* carries a fresh snapshot.
|
|
152157
152493
|
*/
|
|
152158
152494
|
async setThinking(enabled) {
|
|
152495
|
+
if (!enabled && await this.currentModelAlwaysThinking()) {
|
|
152496
|
+
this.currentThinkingEnabledInternal = true;
|
|
152497
|
+
await this.emitConfigOptionUpdate();
|
|
152498
|
+
return;
|
|
152499
|
+
}
|
|
152159
152500
|
if (typeof this.session.setThinking === "function") await this.session.setThinking(enabled ? THINKING_ON_LEVEL : THINKING_OFF_LEVEL);
|
|
152160
152501
|
this.currentThinkingEnabledInternal = enabled;
|
|
152161
152502
|
await this.emitConfigOptionUpdate();
|
|
152162
152503
|
}
|
|
152163
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
|
+
/**
|
|
152164
152514
|
* Forward an ACP `session/set_mode` request to the underlying SDK
|
|
152165
152515
|
* session.
|
|
152166
152516
|
*
|
|
@@ -155365,7 +155715,6 @@ const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
|
|
|
155365
155715
|
const NO_ACTIVE_SESSION_MESSAGE = "No active session. Send /login to login.";
|
|
155366
155716
|
const CTRL_D_HINT = "Press Ctrl+D again to exit";
|
|
155367
155717
|
const CTRL_C_HINT = "Press Ctrl+C again to exit";
|
|
155368
|
-
const MAIN_AGENT_ID = "main";
|
|
155369
155718
|
const OAUTH_LOGIN_REQUIRED_STARTUP_NOTICE = "OAuth login expired. Send /login to login.";
|
|
155370
155719
|
const EXIT_CONFIRM_WINDOW_MS = 1500;
|
|
155371
155720
|
function isManagedUsageProvider(providerKey) {
|
|
@@ -155726,7 +156075,7 @@ var SearchableList = class {
|
|
|
155726
156075
|
* addChild(picker), setFocus(picker); the picker invokes `onSelect` or
|
|
155727
156076
|
* `onCancel`, and the host tears it down.
|
|
155728
156077
|
*/
|
|
155729
|
-
function wrapDescription(text, width) {
|
|
156078
|
+
function wrapDescription$1(text, width) {
|
|
155730
156079
|
const maxWidth = Math.max(1, width);
|
|
155731
156080
|
const words = text.trim().split(/\s+/).filter((word) => word.length > 0);
|
|
155732
156081
|
const lines = [];
|
|
@@ -155811,7 +156160,7 @@ var ChoicePickerComponent = class extends Container {
|
|
|
155811
156160
|
lines.push(line);
|
|
155812
156161
|
if (opt.description !== void 0 && opt.description.length > 0) {
|
|
155813
156162
|
const descriptionWidth = Math.max(1, width - 4);
|
|
155814
|
-
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}`));
|
|
155815
156164
|
}
|
|
155816
156165
|
}
|
|
155817
156166
|
lines.push("");
|
|
@@ -156082,9 +156431,10 @@ var ModelSelectorComponent = class extends Container {
|
|
|
156082
156431
|
}
|
|
156083
156432
|
renderThinkingControl(choice) {
|
|
156084
156433
|
const segment = (label, active) => active ? currentTheme.boldFg("primary", `[ ${label} ]`) : currentTheme.fg("text", ` ${label} `);
|
|
156434
|
+
const unavailable = (label) => currentTheme.fg("textMuted", ` ${label} (Unsupported) `);
|
|
156085
156435
|
const availability = thinkingAvailability(choice.model);
|
|
156086
|
-
if (availability === "always-on") return ` ${segment("
|
|
156087
|
-
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)}`;
|
|
156088
156438
|
const draft = this.draftFor(choice);
|
|
156089
156439
|
return ` ${segment("On", draft)} ${segment("Off", !draft)}`;
|
|
156090
156440
|
}
|
|
@@ -156603,7 +156953,7 @@ var EditorSelectorComponent = class extends ChoicePickerComponent {
|
|
|
156603
156953
|
};
|
|
156604
156954
|
//#endregion
|
|
156605
156955
|
//#region src/tui/components/dialogs/experiments-selector.ts
|
|
156606
|
-
const ELLIPSIS$
|
|
156956
|
+
const ELLIPSIS$8 = "…";
|
|
156607
156957
|
var ExperimentsSelectorComponent = class extends Container {
|
|
156608
156958
|
focused = false;
|
|
156609
156959
|
opts;
|
|
@@ -156662,7 +157012,7 @@ var ExperimentsSelectorComponent = class extends Container {
|
|
|
156662
157012
|
else if (view.page.end < view.items.length) lines.push(currentTheme.fg("textMuted", ` ▼ ${String(view.items.length - view.page.end)} more`));
|
|
156663
157013
|
lines.push(this.renderApplyButton());
|
|
156664
157014
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
156665
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
157015
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$8));
|
|
156666
157016
|
}
|
|
156667
157017
|
toggleDraft(feature) {
|
|
156668
157018
|
if (isLocked(feature)) return;
|
|
@@ -156735,7 +157085,7 @@ function wrapText(text, width) {
|
|
|
156735
157085
|
continue;
|
|
156736
157086
|
}
|
|
156737
157087
|
if (current.length > 0) lines.push(current);
|
|
156738
|
-
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$
|
|
157088
|
+
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$8);
|
|
156739
157089
|
}
|
|
156740
157090
|
if (current.length > 0) lines.push(current);
|
|
156741
157091
|
return lines;
|
|
@@ -158106,7 +158456,7 @@ var GoalStartPermissionPromptComponent = class extends StartPermissionPromptComp
|
|
|
158106
158456
|
//#region src/tui/components/dialogs/goal-queue-manager.ts
|
|
158107
158457
|
const MAX_GOAL_OBJECTIVE_LENGTH$2 = 4e3;
|
|
158108
158458
|
const MAX_EDIT_INPUT_LINES = 8;
|
|
158109
|
-
const ELLIPSIS$
|
|
158459
|
+
const ELLIPSIS$7 = "…";
|
|
158110
158460
|
const BRACKET_PASTE_START$1 = "\x1B[200~";
|
|
158111
158461
|
const BRACKET_PASTE_END$1 = "\x1B[201~";
|
|
158112
158462
|
const SHIFT_ENTER_LEGACY = "\x1B\r";
|
|
@@ -158196,7 +158546,7 @@ var GoalQueueManagerComponent = class extends Container {
|
|
|
158196
158546
|
}
|
|
158197
158547
|
lines.push("");
|
|
158198
158548
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
158199
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
158549
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$7));
|
|
158200
158550
|
}
|
|
158201
158551
|
renderGoal(goal, index, selected, width) {
|
|
158202
158552
|
const moving = goal.id === this.movingGoalId;
|
|
@@ -158207,7 +158557,7 @@ var GoalQueueManagerComponent = class extends Container {
|
|
|
158207
158557
|
const labelWidth = visibleWidth(labelPrefix);
|
|
158208
158558
|
const stateWidth = visibleWidth(stateLabel);
|
|
158209
158559
|
const objectiveWidth = Math.max(1, width - 5 - labelWidth - stateWidth);
|
|
158210
|
-
const objective = truncateToWidth(formatListObjective(goal.objective), objectiveWidth, ELLIPSIS$
|
|
158560
|
+
const objective = truncateToWidth(formatListObjective(goal.objective), objectiveWidth, ELLIPSIS$7);
|
|
158211
158561
|
let line = prefix + (selected ? (text) => currentTheme.boldFg("primary", text) : (text) => currentTheme.fg("text", text))(labelPrefix + objective);
|
|
158212
158562
|
if (moving) line += currentTheme.fg("success", stateLabel);
|
|
158213
158563
|
return line;
|
|
@@ -158278,10 +158628,10 @@ var GoalQueueEditDialogComponent = class extends Container {
|
|
|
158278
158628
|
const innerWidth = Math.max(10, safeWidth - 4);
|
|
158279
158629
|
const pad = " ";
|
|
158280
158630
|
const border = (s) => currentTheme.fg("primary", s);
|
|
158281
|
-
const title = truncateToWidth(currentTheme.boldFg("textStrong", "Edit upcoming goal"), innerWidth, ELLIPSIS$
|
|
158282
|
-
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);
|
|
158283
158633
|
const inputLines = this.input.render(innerWidth);
|
|
158284
|
-
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);
|
|
158285
158635
|
const contentLines = [
|
|
158286
158636
|
title,
|
|
158287
158637
|
"",
|
|
@@ -158387,14 +158737,14 @@ var MultilineGoalInput = class {
|
|
|
158387
158737
|
const cursor = this.cursorLocation();
|
|
158388
158738
|
const range = visibleLineRange(logicalLines.length, cursor.line);
|
|
158389
158739
|
const rendered = [];
|
|
158390
|
-
if (range.start > 0) rendered.push(padInputLine(` ${ELLIPSIS$
|
|
158740
|
+
if (range.start > 0) rendered.push(padInputLine(` ${ELLIPSIS$7} ${String(range.start)} previous`, safeWidth));
|
|
158391
158741
|
for (let lineIndex = range.start; lineIndex < range.end; lineIndex++) {
|
|
158392
158742
|
const line = logicalLines[lineIndex] ?? "";
|
|
158393
158743
|
const prefix = lineIndex === 0 ? "> " : " ";
|
|
158394
158744
|
rendered.push(lineIndex === cursor.line ? renderCursorLine(line, cursor.column, prefix, safeWidth, this.focused) : renderTextLine(line, prefix, safeWidth));
|
|
158395
158745
|
}
|
|
158396
158746
|
const remaining = logicalLines.length - range.end;
|
|
158397
|
-
if (remaining > 0) rendered.push(padInputLine(` ${ELLIPSIS$
|
|
158747
|
+
if (remaining > 0) rendered.push(padInputLine(` ${ELLIPSIS$7} ${String(remaining)} more`, safeWidth));
|
|
158398
158748
|
return rendered;
|
|
158399
158749
|
}
|
|
158400
158750
|
insert(text) {
|
|
@@ -158534,7 +158884,7 @@ function visibleLineRange(totalLines, cursorLine) {
|
|
|
158534
158884
|
}
|
|
158535
158885
|
function renderTextLine(line, prefix, width) {
|
|
158536
158886
|
const prefixWidth = visibleWidth(prefix);
|
|
158537
|
-
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);
|
|
158538
158888
|
}
|
|
158539
158889
|
function renderCursorLine(line, column, prefix, width, focused) {
|
|
158540
158890
|
const prefixWidth = visibleWidth(prefix);
|
|
@@ -160102,7 +160452,7 @@ const OVERVIEW_PLUGIN_PREFIX = "plugin:";
|
|
|
160102
160452
|
const MCP_SERVER_PREFIX = "mcp:";
|
|
160103
160453
|
const REMOVE_CONFIRM_CANCEL = "cancel";
|
|
160104
160454
|
const REMOVE_CONFIRM_REMOVE = "remove";
|
|
160105
|
-
const ELLIPSIS$
|
|
160455
|
+
const ELLIPSIS$6 = "…";
|
|
160106
160456
|
var PluginsOverviewSelectorComponent = class extends Container {
|
|
160107
160457
|
focused = false;
|
|
160108
160458
|
opts;
|
|
@@ -160195,7 +160545,7 @@ var PluginsOverviewSelectorComponent = class extends Container {
|
|
|
160195
160545
|
for (let i = 0; i < actionItems.length; i++) lines.push(...this.renderItem(actionItems[i], pluginItems.length + i, width));
|
|
160196
160546
|
lines.push("");
|
|
160197
160547
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160198
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160548
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160199
160549
|
}
|
|
160200
160550
|
renderItem(item, index, width) {
|
|
160201
160551
|
const selected = index === this.selectedIndex;
|
|
@@ -160267,7 +160617,7 @@ var PluginMarketplaceSelectorComponent = class extends Container {
|
|
|
160267
160617
|
for (let i = 0; i < actions.length; i++) lines.push(...this.renderItem(actions[i], entries.length + i, width));
|
|
160268
160618
|
lines.push("");
|
|
160269
160619
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160270
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160620
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160271
160621
|
}
|
|
160272
160622
|
renderItem(item, index, width) {
|
|
160273
160623
|
const selected = index === this.selectedIndex;
|
|
@@ -160346,7 +160696,7 @@ var PluginMcpSelectorComponent = class extends Container {
|
|
|
160346
160696
|
for (let i = 0; i < actionItems.length; i++) lines.push(...this.renderItem(actionItems[i], serverItems.length + i, width));
|
|
160347
160697
|
lines.push("");
|
|
160348
160698
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
160349
|
-
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
160699
|
+
return lines.map((line) => truncateToWidth(line, width, ELLIPSIS$6));
|
|
160350
160700
|
}
|
|
160351
160701
|
renderItem(item, index, width) {
|
|
160352
160702
|
const selected = index === this.selectedIndex;
|
|
@@ -160526,7 +160876,7 @@ function wrapOverviewDescription(text, width) {
|
|
|
160526
160876
|
continue;
|
|
160527
160877
|
}
|
|
160528
160878
|
if (current.length > 0) lines.push(current);
|
|
160529
|
-
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$
|
|
160879
|
+
current = visibleWidth(word) <= maxWidth ? word : truncateToWidth(word, maxWidth, ELLIPSIS$6);
|
|
160530
160880
|
}
|
|
160531
160881
|
if (current.length > 0) lines.push(current);
|
|
160532
160882
|
return lines;
|
|
@@ -161017,15 +161367,23 @@ function applyRuntimeConfig(host, config) {
|
|
|
161017
161367
|
}
|
|
161018
161368
|
//#endregion
|
|
161019
161369
|
//#region src/tui/components/dialogs/swarm-start-permission-prompt.ts
|
|
161020
|
-
const OPTIONS = [
|
|
161021
|
-
|
|
161022
|
-
|
|
161023
|
-
|
|
161024
|
-
|
|
161025
|
-
|
|
161026
|
-
|
|
161027
|
-
|
|
161028
|
-
|
|
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
|
+
];
|
|
161029
161387
|
const NOTICE_LINES = [
|
|
161030
161388
|
"Manual mode asks you before Kimi Code runs commands, edits files, or takes other risky actions.",
|
|
161031
161389
|
"Manual mode can block swarm work while agents are running.",
|
|
@@ -161103,7 +161461,7 @@ function showSwarmStartPermissionPrompt(host, commandText, cancelStatus, onSelec
|
|
|
161103
161461
|
}));
|
|
161104
161462
|
}
|
|
161105
161463
|
async function startSwarmWithPermission(host, prompt, choice) {
|
|
161106
|
-
if (choice === "auto") {
|
|
161464
|
+
if (choice === "auto" || choice === "yolo") {
|
|
161107
161465
|
if (!await setPermissionForSwarm(host, choice)) return;
|
|
161108
161466
|
}
|
|
161109
161467
|
await startSwarmTask(host, prompt);
|
|
@@ -161134,7 +161492,7 @@ async function applySwarmMode(host, enabled, commandText) {
|
|
|
161134
161492
|
}
|
|
161135
161493
|
if (enabled && host.state.appState.permissionMode === "manual") {
|
|
161136
161494
|
showSwarmStartPermissionPrompt(host, commandText, "Swarm mode not enabled.", async (choice) => {
|
|
161137
|
-
if (choice === "auto" && !await setPermissionForSwarm(host, choice)) return;
|
|
161495
|
+
if ((choice === "auto" || choice === "yolo") && !await setPermissionForSwarm(host, choice)) return;
|
|
161138
161496
|
if (!await setSwarmMode(host, true, "manual")) return;
|
|
161139
161497
|
renderSwarmModeMarker(host, "active");
|
|
161140
161498
|
});
|
|
@@ -166761,15 +167119,20 @@ function installPromptTerminationCleanup(promptProcess, cleanup) {
|
|
|
166761
167119
|
};
|
|
166762
167120
|
const onSigint = () => exitAfterCleanup("SIGINT");
|
|
166763
167121
|
const onSigterm = () => exitAfterCleanup("SIGTERM");
|
|
167122
|
+
const onSighup = () => exitAfterCleanup("SIGHUP");
|
|
166764
167123
|
promptProcess.once("SIGINT", onSigint);
|
|
166765
167124
|
promptProcess.once("SIGTERM", onSigterm);
|
|
167125
|
+
promptProcess.once("SIGHUP", onSighup);
|
|
166766
167126
|
return () => {
|
|
166767
167127
|
promptProcess.off("SIGINT", onSigint);
|
|
166768
167128
|
promptProcess.off("SIGTERM", onSigterm);
|
|
167129
|
+
promptProcess.off("SIGHUP", onSighup);
|
|
166769
167130
|
};
|
|
166770
167131
|
}
|
|
166771
167132
|
function signalExitCode(signal) {
|
|
166772
|
-
|
|
167133
|
+
if (signal === "SIGINT") return 130;
|
|
167134
|
+
if (signal === "SIGHUP") return 129;
|
|
167135
|
+
return 143;
|
|
166773
167136
|
}
|
|
166774
167137
|
function runPromptTurn(session, prompt, outputFormat, stdout, stderr) {
|
|
166775
167138
|
let activeTurnId;
|
|
@@ -167735,16 +168098,16 @@ function buildNumericHint(count) {
|
|
|
167735
168098
|
* emulator's "snap to bottom on stdout" reflex, causing flicker and an
|
|
167736
168099
|
* unscrollable history pane.
|
|
167737
168100
|
*/
|
|
167738
|
-
const ELLIPSIS$
|
|
168101
|
+
const ELLIPSIS$5 = "…";
|
|
167739
168102
|
function padToWidth$2(line, width) {
|
|
167740
168103
|
const w = visibleWidth(line);
|
|
167741
168104
|
if (w === width) return line;
|
|
167742
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
168105
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$5);
|
|
167743
168106
|
return line + " ".repeat(width - w);
|
|
167744
168107
|
}
|
|
167745
168108
|
function fitExactly$2(line, width) {
|
|
167746
168109
|
let s = line;
|
|
167747
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
168110
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$5);
|
|
167748
168111
|
return padToWidth$2(s, width);
|
|
167749
168112
|
}
|
|
167750
168113
|
var ApprovalPreviewViewer = class extends Container {
|
|
@@ -168601,7 +168964,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
168601
168964
|
/**
|
|
168602
168965
|
* SessionPicker — pi-tui version of the session selection dialog.
|
|
168603
168966
|
*/
|
|
168604
|
-
const ELLIPSIS$
|
|
168967
|
+
const ELLIPSIS$4 = "…";
|
|
168605
168968
|
function formatRelativeTime$1(ts) {
|
|
168606
168969
|
if (!Number.isFinite(ts) || ts <= 0) return "";
|
|
168607
168970
|
const diffSec = Math.floor(Math.max(0, Date.now() - ts) / 1e3);
|
|
@@ -168621,7 +168984,7 @@ function homeAlias(path) {
|
|
|
168621
168984
|
function truncatePathLeft(path, maxWidth) {
|
|
168622
168985
|
if (maxWidth <= 0) return "";
|
|
168623
168986
|
if (visibleWidth(path) <= maxWidth) return path;
|
|
168624
|
-
if (maxWidth === 1) return ELLIPSIS$
|
|
168987
|
+
if (maxWidth === 1) return ELLIPSIS$4;
|
|
168625
168988
|
const segments = [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(path)].map((s) => s.segment);
|
|
168626
168989
|
let used = 0;
|
|
168627
168990
|
const budget = maxWidth - 1;
|
|
@@ -168634,7 +168997,7 @@ function truncatePathLeft(path, maxWidth) {
|
|
|
168634
168997
|
used += w;
|
|
168635
168998
|
i--;
|
|
168636
168999
|
}
|
|
168637
|
-
return ELLIPSIS$
|
|
169000
|
+
return ELLIPSIS$4 + segments.slice(i + 1).join("");
|
|
168638
169001
|
}
|
|
168639
169002
|
function singleLine$1(text) {
|
|
168640
169003
|
return text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -168686,26 +169049,26 @@ var SessionPickerComponent = class extends Container {
|
|
|
168686
169049
|
if (matchesKey(data, Key.down)) this.selectedIndex = Math.min(this.sessions.length - 1, this.selectedIndex + 1);
|
|
168687
169050
|
}
|
|
168688
169051
|
render(width) {
|
|
168689
|
-
return this.renderLines(width).map((line) => truncateToWidth(line, width, ELLIPSIS$
|
|
169052
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, width, ELLIPSIS$4));
|
|
168690
169053
|
}
|
|
168691
169054
|
renderLines(width) {
|
|
168692
169055
|
const lines = [currentTheme.fg("primary", "─".repeat(width))];
|
|
168693
169056
|
if (this.loading) {
|
|
168694
|
-
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$
|
|
168695
|
-
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)));
|
|
168696
169059
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168697
169060
|
return lines;
|
|
168698
169061
|
}
|
|
168699
169062
|
if (this.sessions.length === 0) {
|
|
168700
|
-
lines.push(currentTheme.boldFg("primary", truncateToWidth("Sessions", width, ELLIPSIS$
|
|
168701
|
-
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)));
|
|
168702
169065
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168703
169066
|
return lines;
|
|
168704
169067
|
}
|
|
168705
169068
|
const headerLabel = "Sessions ";
|
|
168706
169069
|
const headerHint = "↑↓ navigate · Enter select · Esc cancel";
|
|
168707
169070
|
const labelWidth = visibleWidth(headerLabel);
|
|
168708
|
-
const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$
|
|
169071
|
+
const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$4);
|
|
168709
169072
|
lines.push(currentTheme.boldFg("primary", headerLabel) + currentTheme.fg("textMuted", shownHint));
|
|
168710
169073
|
lines.push("");
|
|
168711
169074
|
const visibleStart = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisibleSessions / 2), Math.max(0, this.sessions.length - this.maxVisibleSessions)));
|
|
@@ -168720,7 +169083,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168720
169083
|
if (this.sessions.length > visibleSessions.length) {
|
|
168721
169084
|
lines.push("");
|
|
168722
169085
|
const footer = `Showing ${String(visibleStart + 1)}-${String(visibleStart + visibleSessions.length)} of ${String(this.sessions.length)} sessions`;
|
|
168723
|
-
lines.push(currentTheme.fg("textMuted", truncateToWidth(footer, width, ELLIPSIS$
|
|
169086
|
+
lines.push(currentTheme.fg("textMuted", truncateToWidth(footer, width, ELLIPSIS$4)));
|
|
168724
169087
|
}
|
|
168725
169088
|
lines.push(currentTheme.fg("primary", "─".repeat(width)));
|
|
168726
169089
|
return lines;
|
|
@@ -168741,7 +169104,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168741
169104
|
const trailingWidth = visibleWidth(trailingParts.length > 0 ? " " + trailingParts.join(" ") : "");
|
|
168742
169105
|
const headerPrefixWidth = visibleWidth(pointer) + 1;
|
|
168743
169106
|
const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
|
|
168744
|
-
const shownTitle = truncateToWidth(singleLine$1(titleSource), titleBudget, ELLIPSIS$
|
|
169107
|
+
const shownTitle = truncateToWidth(singleLine$1(titleSource), titleBudget, ELLIPSIS$4);
|
|
168745
169108
|
let header = currentTheme.fg(isSelected ? "primary" : "textDim", pointer + " ");
|
|
168746
169109
|
header += titleStyle(shownTitle);
|
|
168747
169110
|
if (time.length > 0) header += " " + currentTheme.fg("textDim", time);
|
|
@@ -168756,7 +169119,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168756
169119
|
const dirWidth = visibleWidth(aliasedDir);
|
|
168757
169120
|
if (idLineWidth + metaGapWidth + dirWidth <= width) card.push(indent + currentTheme.fg("textMuted", fullId) + currentTheme.fg("textDim", metaGap) + currentTheme.fg("textMuted", aliasedDir));
|
|
168758
169121
|
else {
|
|
168759
|
-
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)));
|
|
168760
169123
|
const dir = truncatePathLeft(aliasedDir, Math.max(8, width - indentWidth));
|
|
168761
169124
|
card.push(indent + currentTheme.fg("textMuted", dir));
|
|
168762
169125
|
}
|
|
@@ -168765,7 +169128,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
168765
169128
|
const promptMarker = "› ";
|
|
168766
169129
|
const promptMarkerWidth = visibleWidth(promptMarker);
|
|
168767
169130
|
const promptBudget = Math.max(8, width - indentWidth - promptMarkerWidth);
|
|
168768
|
-
const promptText = truncateToWidth(singleLine$1(rawPrompt), promptBudget, ELLIPSIS$
|
|
169131
|
+
const promptText = truncateToWidth(singleLine$1(rawPrompt), promptBudget, ELLIPSIS$4);
|
|
168769
169132
|
const promptLine = indent + currentTheme.fg("textDim", promptMarker + promptText);
|
|
168770
169133
|
card.push(promptLine);
|
|
168771
169134
|
}
|
|
@@ -168862,7 +169225,7 @@ function restoreProviderAliases(config, aliases) {
|
|
|
168862
169225
|
function restoreDefaultSelection(config, defaultModel, defaultThinking) {
|
|
168863
169226
|
if (defaultModel === void 0 || config.models?.[defaultModel] === void 0) return;
|
|
168864
169227
|
config.defaultModel = defaultModel;
|
|
168865
|
-
config.defaultThinking = defaultThinking;
|
|
169228
|
+
config.defaultThinking = (config.models[defaultModel]?.capabilities ?? []).includes("always_thinking") ? true : defaultThinking;
|
|
168866
169229
|
}
|
|
168867
169230
|
function clampDanglingDefault(config) {
|
|
168868
169231
|
if (config.defaultModel !== void 0 && config.models?.[config.defaultModel] === void 0) {
|
|
@@ -169491,13 +169854,7 @@ var BtwPanelController = class {
|
|
|
169491
169854
|
return panel.isRunning() || panel.isEmpty();
|
|
169492
169855
|
}
|
|
169493
169856
|
withInteractiveAgent(agentId, fn) {
|
|
169494
|
-
|
|
169495
|
-
this.host.harness.interactiveAgentId = agentId;
|
|
169496
|
-
try {
|
|
169497
|
-
return fn();
|
|
169498
|
-
} finally {
|
|
169499
|
-
this.host.harness.interactiveAgentId = previousAgentId;
|
|
169500
|
-
}
|
|
169857
|
+
return this.host.harness.withInteractiveAgent(agentId, fn);
|
|
169501
169858
|
}
|
|
169502
169859
|
};
|
|
169503
169860
|
function formatBtwTurnEnd(event) {
|
|
@@ -173610,7 +173967,7 @@ var StreamingUIController = class {
|
|
|
173610
173967
|
* TasksBrowserApp. Snapshot view (no live tail) — content is fetched
|
|
173611
173968
|
* once when the viewer opens.
|
|
173612
173969
|
*/
|
|
173613
|
-
const ELLIPSIS$
|
|
173970
|
+
const ELLIPSIS$3 = "…";
|
|
173614
173971
|
const STATUS_LABEL$1 = {
|
|
173615
173972
|
running: "running",
|
|
173616
173973
|
completed: "completed",
|
|
@@ -173632,12 +173989,12 @@ function statusColor$1(status) {
|
|
|
173632
173989
|
function padToWidth$1(line, width) {
|
|
173633
173990
|
const w = visibleWidth(line);
|
|
173634
173991
|
if (w === width) return line;
|
|
173635
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
173992
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$3);
|
|
173636
173993
|
return line + " ".repeat(width - w);
|
|
173637
173994
|
}
|
|
173638
173995
|
function fitExactly$1(line, width) {
|
|
173639
173996
|
let s = line;
|
|
173640
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
173997
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$3);
|
|
173641
173998
|
return padToWidth$1(s, width);
|
|
173642
173999
|
}
|
|
173643
174000
|
var TaskOutputViewer = class extends Container {
|
|
@@ -173796,7 +174153,7 @@ var TaskOutputViewer = class extends Container {
|
|
|
173796
174153
|
* Data (tasks list, tail output) flows in via `setProps`; user actions
|
|
173797
174154
|
* fire the `on*` callbacks back to the controller.
|
|
173798
174155
|
*/
|
|
173799
|
-
const ELLIPSIS$
|
|
174156
|
+
const ELLIPSIS$2 = "…";
|
|
173800
174157
|
const STATUS_LABEL = {
|
|
173801
174158
|
running: "running",
|
|
173802
174159
|
completed: "completed",
|
|
@@ -173844,13 +174201,13 @@ function singleLine(text) {
|
|
|
173844
174201
|
function padToWidth(line, width) {
|
|
173845
174202
|
const w = visibleWidth(line);
|
|
173846
174203
|
if (w === width) return line;
|
|
173847
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
174204
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$2);
|
|
173848
174205
|
return line + " ".repeat(width - w);
|
|
173849
174206
|
}
|
|
173850
174207
|
/** Fit `line` into exactly `width` columns, even after CJK-edge truncation. */
|
|
173851
174208
|
function fitExactly(line, width) {
|
|
173852
174209
|
let s = line;
|
|
173853
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
174210
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$2);
|
|
173854
174211
|
return padToWidth(s, width);
|
|
173855
174212
|
}
|
|
173856
174213
|
function visibleTasks(tasks, filter) {
|
|
@@ -174111,7 +174468,7 @@ var TasksBrowserApp = class extends Container {
|
|
|
174111
174468
|
const prefixWidth = visibleWidth(prefix);
|
|
174112
174469
|
const descBudget = Math.max(0, innerWidth - prefixWidth - 1);
|
|
174113
174470
|
if (descBudget < 4) return fitExactly(prefix, innerWidth);
|
|
174114
|
-
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);
|
|
174115
174472
|
return fitExactly(`${prefix} ${currentTheme.fg("text", desc)}`, innerWidth);
|
|
174116
174473
|
}
|
|
174117
174474
|
adjustScroll(visibleRows) {
|
|
@@ -174508,13 +174865,23 @@ const MAX_FALLBACK_SUGGESTIONS = 50;
|
|
|
174508
174865
|
* completer. This wrapper also keeps Kimi-specific slash-command guards.
|
|
174509
174866
|
*/
|
|
174510
174867
|
var FileMentionProvider = class {
|
|
174868
|
+
slashCommands;
|
|
174511
174869
|
workDir;
|
|
174512
174870
|
fdPath;
|
|
174513
174871
|
inner;
|
|
174514
174872
|
constructor(slashCommands, workDir, fdPath) {
|
|
174873
|
+
this.slashCommands = slashCommands;
|
|
174515
174874
|
this.workDir = workDir;
|
|
174516
174875
|
this.fdPath = fdPath;
|
|
174517
|
-
|
|
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);
|
|
174518
174885
|
}
|
|
174519
174886
|
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
174520
174887
|
const currentLine = lines[cursorLine] ?? "";
|
|
@@ -174530,6 +174897,46 @@ var FileMentionProvider = class {
|
|
|
174530
174897
|
return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
174531
174898
|
}
|
|
174532
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
|
+
}
|
|
174533
174940
|
try {
|
|
174534
174941
|
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
174535
174942
|
} catch {
|
|
@@ -174646,6 +175053,28 @@ function shouldSuppressSlashArgumentCompletion(textBeforeCursor, textAfterCursor
|
|
|
174646
175053
|
if (!textBeforeCursor.includes(" ")) return false;
|
|
174647
175054
|
return textAfterCursor.trimStart().length > 0;
|
|
174648
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
|
+
}
|
|
174649
175078
|
//#endregion
|
|
174650
175079
|
//#region src/tui/components/panes/activity-pane.ts
|
|
174651
175080
|
var ActivityPaneComponent = class extends Container {
|
|
@@ -174666,7 +175095,7 @@ var ActivityPaneComponent = class extends Container {
|
|
|
174666
175095
|
};
|
|
174667
175096
|
//#endregion
|
|
174668
175097
|
//#region src/tui/components/panes/queue-pane.ts
|
|
174669
|
-
const ELLIPSIS = "…";
|
|
175098
|
+
const ELLIPSIS$1 = "…";
|
|
174670
175099
|
var QueuePaneComponent = class extends Container {
|
|
174671
175100
|
messages;
|
|
174672
175101
|
hint;
|
|
@@ -174682,10 +175111,10 @@ var QueuePaneComponent = class extends Container {
|
|
|
174682
175111
|
for (const item of this.messages) {
|
|
174683
175112
|
const singleLine = item.text.replaceAll(/\s+/g, " ").trim();
|
|
174684
175113
|
const prefix = ` ❯ `;
|
|
174685
|
-
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS);
|
|
175114
|
+
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS$1);
|
|
174686
175115
|
lines.push(accent(prefix + truncated));
|
|
174687
175116
|
}
|
|
174688
|
-
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)));
|
|
174689
175118
|
return lines;
|
|
174690
175119
|
}
|
|
174691
175120
|
};
|
|
@@ -175109,6 +175538,144 @@ function createApprovalRequestHandler(controller, onResponse) {
|
|
|
175109
175538
|
};
|
|
175110
175539
|
}
|
|
175111
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
|
|
175112
175679
|
//#region src/tui/reverse-rpc/modal-coordinator.ts
|
|
175113
175680
|
var ReverseRpcModalCoordinator = class {
|
|
175114
175681
|
hooks;
|
|
@@ -175980,6 +176547,107 @@ function styleTitle(title, status, colors) {
|
|
|
175980
176547
|
}
|
|
175981
176548
|
}
|
|
175982
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
|
|
175983
176651
|
//#region src/tui/components/editor/custom-editor.ts
|
|
175984
176652
|
/**
|
|
175985
176653
|
* Custom editor extending pi-tui Editor with app-level keybindings.
|
|
@@ -175992,6 +176660,10 @@ const KITTY_CSI_U = /^\u001B\[(\d+);(\d+)((?::\d+)*)u$/;
|
|
|
175992
176660
|
const CAPS_LOCK_BIT = 64;
|
|
175993
176661
|
const CTRL_BIT = 4;
|
|
175994
176662
|
const SHIFT_BIT = 1;
|
|
176663
|
+
const SLASH_COMMAND_SELECT_LIST_LAYOUT = {
|
|
176664
|
+
minPrimaryColumnWidth: 12,
|
|
176665
|
+
maxPrimaryColumnWidth: 32
|
|
176666
|
+
};
|
|
175995
176667
|
/**
|
|
175996
176668
|
* Workaround for a pi-tui bug that surfaces when Kitty keyboard protocol
|
|
175997
176669
|
* is active AND caps_lock is on. In that state terminals emit, e.g.,
|
|
@@ -176077,7 +176749,12 @@ var CustomEditor = class extends Editor {
|
|
|
176077
176749
|
consumingPaste = false;
|
|
176078
176750
|
consumeBuffer = "";
|
|
176079
176751
|
constructor(tui) {
|
|
176080
|
-
|
|
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
|
+
};
|
|
176081
176758
|
}
|
|
176082
176759
|
expandPasteMarkerAtCursor() {
|
|
176083
176760
|
const { line, col } = this.getCursor();
|
|
@@ -176708,7 +177385,8 @@ function createInitialAppState(input) {
|
|
|
176708
177385
|
availableProviders: {},
|
|
176709
177386
|
sessionTitle: null,
|
|
176710
177387
|
goal: null,
|
|
176711
|
-
mcpServersSummary: null
|
|
177388
|
+
mcpServersSummary: null,
|
|
177389
|
+
banner: void 0
|
|
176712
177390
|
};
|
|
176713
177391
|
}
|
|
176714
177392
|
var KimiTUI = class {
|
|
@@ -176805,6 +177483,7 @@ var KimiTUI = class {
|
|
|
176805
177483
|
const completer = cmd.completeArgs;
|
|
176806
177484
|
return {
|
|
176807
177485
|
name: cmd.name,
|
|
177486
|
+
aliases: cmd.aliases,
|
|
176808
177487
|
description: cmd.description,
|
|
176809
177488
|
...cmd.argumentHint !== void 0 ? { argumentHint: cmd.argumentHint } : {},
|
|
176810
177489
|
...completer !== void 0 ? { getArgumentCompletions: (prefix) => completer(prefix) } : {}
|
|
@@ -176873,10 +177552,28 @@ var KimiTUI = class {
|
|
|
176873
177552
|
throw error;
|
|
176874
177553
|
}
|
|
176875
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
|
+
}
|
|
176876
177572
|
async initMainTui() {
|
|
176877
177573
|
const shouldReplayHistory = await this.init();
|
|
176878
177574
|
this.mountFooter();
|
|
176879
177575
|
this.renderWelcome();
|
|
177576
|
+
this.loadBanner();
|
|
176880
177577
|
this.setupAutocomplete();
|
|
176881
177578
|
this.loadPersistedInputHistory();
|
|
176882
177579
|
this.state.editorContainer.clear();
|
|
@@ -177176,10 +177873,11 @@ var KimiTUI = class {
|
|
|
177176
177873
|
this.showError(message);
|
|
177177
177874
|
}
|
|
177178
177875
|
sendQueuedMessage(session, item) {
|
|
177179
|
-
this.harness.
|
|
177180
|
-
|
|
177181
|
-
|
|
177182
|
-
|
|
177876
|
+
this.harness.withInteractiveAgent(item.agentId ?? "main", () => {
|
|
177877
|
+
this.sendMessageInternal(session, item.text, {
|
|
177878
|
+
parts: item.parts,
|
|
177879
|
+
imageAttachmentIds: item.imageAttachmentIds
|
|
177880
|
+
});
|
|
177183
177881
|
});
|
|
177184
177882
|
}
|
|
177185
177883
|
requestQueuedGoalPromotion() {
|
|
@@ -177391,7 +178089,6 @@ var KimiTUI = class {
|
|
|
177391
178089
|
this.streamingUI.discardPending();
|
|
177392
178090
|
this.state.queuedMessages = [];
|
|
177393
178091
|
this.state.swarmModeEntry = void 0;
|
|
177394
|
-
this.harness.interactiveAgentId = MAIN_AGENT_ID;
|
|
177395
178092
|
this.streamingUI.resetToolCallState();
|
|
177396
178093
|
this.streamingUI.resetToolUi();
|
|
177397
178094
|
this.sessionEventHandler.resetRuntimeState();
|