kfc-code-cli 0.0.1-alpha.10 → 0.0.1-alpha.13
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 +1329 -634
- package/package.json +2 -2
package/dist/main.mjs
CHANGED
|
@@ -1026,14 +1026,7 @@ var BaseContextState = class {
|
|
|
1026
1026
|
this.journalWriter = opts.journalWriter;
|
|
1027
1027
|
this.projector = opts.projector ?? new DefaultConversationProjector();
|
|
1028
1028
|
this.currentTurnId = opts.currentTurnId;
|
|
1029
|
-
this.memory = new ContextStateMemory(
|
|
1030
|
-
initialModel: opts.initialModel,
|
|
1031
|
-
...opts.initialSystemPrompt !== void 0 ? { initialSystemPrompt: opts.initialSystemPrompt } : {},
|
|
1032
|
-
...opts.initialActiveTools !== void 0 ? { initialActiveTools: opts.initialActiveTools } : {},
|
|
1033
|
-
initialHistory: opts.initialHistory,
|
|
1034
|
-
...opts.initialTokenCount !== void 0 ? { initialTokenCount: opts.initialTokenCount } : {},
|
|
1035
|
-
...opts.initialThinkingLevel !== void 0 ? { initialThinkingLevel: opts.initialThinkingLevel } : {}
|
|
1036
|
-
});
|
|
1029
|
+
this.memory = new ContextStateMemory(opts);
|
|
1037
1030
|
}
|
|
1038
1031
|
get model() {
|
|
1039
1032
|
return this.memory.model;
|
|
@@ -1061,8 +1054,12 @@ var BaseContextState = class {
|
|
|
1061
1054
|
activeTools: this.memory.activeTools
|
|
1062
1055
|
});
|
|
1063
1056
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1057
|
+
async applySteerMessages() {
|
|
1058
|
+
const steers = this.memory.drainSteerMessages();
|
|
1059
|
+
if (steers.length === 0) return false;
|
|
1060
|
+
this.assertNotBroken();
|
|
1061
|
+
for (const steer of steers) await this.appendUserMessage(steer);
|
|
1062
|
+
return true;
|
|
1066
1063
|
}
|
|
1067
1064
|
pushSteer(input) {
|
|
1068
1065
|
this.memory.pushSteer(input);
|
|
@@ -1109,10 +1106,6 @@ var BaseContextState = class {
|
|
|
1109
1106
|
await this.journalWriter.append(buildToolCallRecord(input, data));
|
|
1110
1107
|
this.memory.appendOpenStepToolCall(input.stepUuid, toolCallDataToKosongToolCall(input.data));
|
|
1111
1108
|
}
|
|
1112
|
-
async addUserMessages(steers) {
|
|
1113
|
-
this.assertNotBroken();
|
|
1114
|
-
for (const steer of steers) await this.appendUserMessage(steer);
|
|
1115
|
-
}
|
|
1116
1109
|
async appendNotification(data) {
|
|
1117
1110
|
this.assertNotBroken();
|
|
1118
1111
|
await this.journalWriter.append(buildNotificationRecord(data));
|
|
@@ -1160,32 +1153,13 @@ var BaseContextState = class {
|
|
|
1160
1153
|
this.memory.resetToSummary(summary);
|
|
1161
1154
|
}
|
|
1162
1155
|
};
|
|
1163
|
-
var WiredContextState = class extends BaseContextState {
|
|
1164
|
-
constructor(opts) {
|
|
1165
|
-
super({
|
|
1166
|
-
journalWriter: opts.journalWriter,
|
|
1167
|
-
initialModel: opts.initialModel,
|
|
1168
|
-
...opts.initialSystemPrompt !== void 0 ? { initialSystemPrompt: opts.initialSystemPrompt } : {},
|
|
1169
|
-
...opts.initialActiveTools !== void 0 ? { initialActiveTools: opts.initialActiveTools } : {},
|
|
1170
|
-
currentTurnId: opts.currentTurnId,
|
|
1171
|
-
projector: opts.projector,
|
|
1172
|
-
initialHistory: opts.initialHistory,
|
|
1173
|
-
...opts.initialTokenCount !== void 0 ? { initialTokenCount: opts.initialTokenCount } : {},
|
|
1174
|
-
...opts.initialThinkingLevel !== void 0 ? { initialThinkingLevel: opts.initialThinkingLevel } : {}
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
1156
|
+
var WiredContextState = class extends BaseContextState {};
|
|
1178
1157
|
var InMemoryContextState = class extends BaseContextState {
|
|
1179
1158
|
constructor(opts) {
|
|
1180
1159
|
super({
|
|
1181
|
-
|
|
1182
|
-
initialModel: opts.initialModel,
|
|
1183
|
-
...opts.initialSystemPrompt !== void 0 ? { initialSystemPrompt: opts.initialSystemPrompt } : {},
|
|
1184
|
-
...opts.initialActiveTools !== void 0 ? { initialActiveTools: opts.initialActiveTools } : {},
|
|
1160
|
+
...opts,
|
|
1185
1161
|
currentTurnId: opts.currentTurnId ?? (() => "embedded"),
|
|
1186
|
-
|
|
1187
|
-
initialHistory: opts.initialHistory,
|
|
1188
|
-
...opts.initialThinkingLevel !== void 0 ? { initialThinkingLevel: opts.initialThinkingLevel } : {}
|
|
1162
|
+
journalWriter: new NoopJournalWriter()
|
|
1189
1163
|
});
|
|
1190
1164
|
}
|
|
1191
1165
|
};
|
|
@@ -2226,6 +2200,7 @@ function zodToSchema(schema) {
|
|
|
2226
2200
|
}
|
|
2227
2201
|
}
|
|
2228
2202
|
function buildLLMVisibleTools(tools, activeTools) {
|
|
2203
|
+
if (tools === void 0) return [];
|
|
2229
2204
|
return (activeTools === void 0 ? tools : tools.filter((t) => activeTools.includes(t.name))).map((t) => ({
|
|
2230
2205
|
name: t.name,
|
|
2231
2206
|
description: t.description,
|
|
@@ -2558,7 +2533,7 @@ async function executePendingCalls(step, pending, deferred) {
|
|
|
2558
2533
|
for (const callback of postContentCallbacks) await callback();
|
|
2559
2534
|
}
|
|
2560
2535
|
function findTool(tools, name) {
|
|
2561
|
-
return tools
|
|
2536
|
+
return tools?.find((tool) => tool.name === name);
|
|
2562
2537
|
}
|
|
2563
2538
|
function emitToolResultEvent(emit, toolCallId, output, isError) {
|
|
2564
2539
|
emit({
|
|
@@ -2640,10 +2615,7 @@ async function executeSoulStep(deps) {
|
|
|
2640
2615
|
type: "step.begin",
|
|
2641
2616
|
step: currentStep
|
|
2642
2617
|
});
|
|
2643
|
-
const drained = context.drainSteerMessages();
|
|
2644
|
-
if (drained.length > 0) await context.addUserMessages(drained);
|
|
2645
2618
|
signal.throwIfAborted();
|
|
2646
|
-
context.beforeStep?.();
|
|
2647
2619
|
const model = overrides?.model ?? context.model;
|
|
2648
2620
|
const visibleTools = buildLLMVisibleTools(config.tools, overrides?.activeTools);
|
|
2649
2621
|
const messages = context.buildMessages();
|
|
@@ -2763,7 +2735,7 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2763
2735
|
});
|
|
2764
2736
|
if (stepResult.stopReason === "tool_use") continue;
|
|
2765
2737
|
stopReason = stepResult.stopReason;
|
|
2766
|
-
break;
|
|
2738
|
+
if (!(await config.beforeTurnCompletes?.())?.continue) break;
|
|
2767
2739
|
}
|
|
2768
2740
|
} catch (error) {
|
|
2769
2741
|
if (isAbortError$3(error) || signal.aborted) {
|
|
@@ -3253,6 +3225,25 @@ var SoulRegistry = class {
|
|
|
3253
3225
|
}
|
|
3254
3226
|
};
|
|
3255
3227
|
//#endregion
|
|
3228
|
+
//#region ../../packages/kimi-core/src/soul/types.ts
|
|
3229
|
+
function mergeSoulConfig(target, source) {
|
|
3230
|
+
if (source.tools) target.tools = target.tools ? [...target.tools, ...source.tools] : source.tools;
|
|
3231
|
+
if (source.maxSteps !== void 0) target.maxSteps = source.maxSteps;
|
|
3232
|
+
if (source.beforeToolCall) target.beforeToolCall = mergeCallback(target.beforeToolCall, source.beforeToolCall);
|
|
3233
|
+
if (source.afterToolCall) target.afterToolCall = mergeCallback(target.afterToolCall, source.afterToolCall);
|
|
3234
|
+
if (source.beforeStep) target.beforeStep = mergeCallback(target.beforeStep, source.beforeStep);
|
|
3235
|
+
if (source.afterStep) target.afterStep = mergeCallback(target.afterStep, source.afterStep);
|
|
3236
|
+
if (source.compactionConfig !== void 0) target.compactionConfig = source.compactionConfig;
|
|
3237
|
+
if (source.contextWindow !== void 0) target.contextWindow = source.contextWindow;
|
|
3238
|
+
function mergeCallback(f1, f2) {
|
|
3239
|
+
if (!f1) return f2;
|
|
3240
|
+
if (!f2) return f1;
|
|
3241
|
+
return (async (...args) => {
|
|
3242
|
+
return await f1(...args) ?? await f2(...args);
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
//#endregion
|
|
3256
3247
|
//#region ../../packages/kimi-core/src/soul-plus/subagent/git-context.ts
|
|
3257
3248
|
/**
|
|
3258
3249
|
* Git context collection for explore subagents.
|
|
@@ -3476,7 +3467,7 @@ async function runSubagentTurn(deps, agentId, request, signal) {
|
|
|
3476
3467
|
agent_id: agentId,
|
|
3477
3468
|
agent_name: request.agentName,
|
|
3478
3469
|
parent_tool_call_id: request.parentToolCallId,
|
|
3479
|
-
|
|
3470
|
+
parent_tool_call_uuid: request.parentToolCallUuid,
|
|
3480
3471
|
...request.parentAgentId !== void 0 && request.parentAgentId !== "" && request.parentAgentId !== "agent_main" ? { parent_agent_id: request.parentAgentId } : {},
|
|
3481
3472
|
run_in_background: request.runInBackground ?? false
|
|
3482
3473
|
}
|
|
@@ -3782,9 +3773,9 @@ var ContextStateToolTranscriptRecorder = class {
|
|
|
3782
3773
|
tool_call_id: input.toolCall.id,
|
|
3783
3774
|
tool_name: input.toolCall.name,
|
|
3784
3775
|
args: input.args,
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3776
|
+
activity_description: input.display?.activityDescription,
|
|
3777
|
+
user_facing_name: input.display?.userFacingName,
|
|
3778
|
+
input_display: input.display?.inputDisplay
|
|
3788
3779
|
}
|
|
3789
3780
|
});
|
|
3790
3781
|
return { wireUuid };
|
|
@@ -4455,7 +4446,7 @@ var DefaultToolCallUseCase = class {
|
|
|
4455
4446
|
approvalRuntime: this.deps.policy.approvalRuntime,
|
|
4456
4447
|
approvalSource,
|
|
4457
4448
|
turnId: context.turnId,
|
|
4458
|
-
|
|
4449
|
+
approvalTimeoutMs: context.approvalTimeoutMs
|
|
4459
4450
|
});
|
|
4460
4451
|
return async (btcCtx, signal) => {
|
|
4461
4452
|
const hookResult = await this.deps.policy.hookEngine.executeHooks("PreToolUse", {
|
|
@@ -4543,7 +4534,7 @@ function approvalSourceForActor(context) {
|
|
|
4543
4534
|
if (context.actor.kind === "subagent") return {
|
|
4544
4535
|
kind: "subagent",
|
|
4545
4536
|
agent_id: context.actor.agentId,
|
|
4546
|
-
|
|
4537
|
+
subagent_type: context.actor.subagentType
|
|
4547
4538
|
};
|
|
4548
4539
|
return {
|
|
4549
4540
|
kind: "soul",
|
|
@@ -5617,7 +5608,7 @@ async function requestEnterPlanModeApproval(approvalRuntime, turnState, toolCall
|
|
|
5617
5608
|
detail: reason
|
|
5618
5609
|
},
|
|
5619
5610
|
source: approvalSourceForPlanMode(turnState),
|
|
5620
|
-
|
|
5611
|
+
turnId: turnState.getCurrentTurnId()
|
|
5621
5612
|
}, signal);
|
|
5622
5613
|
return {
|
|
5623
5614
|
approved: response.approved,
|
|
@@ -7440,6 +7431,15 @@ function mergeInPlace(target, source) {
|
|
|
7440
7431
|
}
|
|
7441
7432
|
return false;
|
|
7442
7433
|
}
|
|
7434
|
+
/**
|
|
7435
|
+
* Extract the concatenated text from a message's content parts.
|
|
7436
|
+
*
|
|
7437
|
+
* @param message The message to extract text from.
|
|
7438
|
+
* @param sep Separator between text parts. Defaults to empty string.
|
|
7439
|
+
*/
|
|
7440
|
+
function extractText(message, sep = "") {
|
|
7441
|
+
return message.content.filter((part) => part.type === "text").map((part) => part.text).join(sep);
|
|
7442
|
+
}
|
|
7443
7443
|
//#endregion
|
|
7444
7444
|
//#region ../../packages/kosong/src/capability.ts
|
|
7445
7445
|
/**
|
|
@@ -7447,7 +7447,7 @@ function mergeInPlace(target, source) {
|
|
|
7447
7447
|
* given model. Frozen so accidental mutation at one call site cannot leak
|
|
7448
7448
|
* into another.
|
|
7449
7449
|
*/
|
|
7450
|
-
const UNKNOWN_CAPABILITY
|
|
7450
|
+
const UNKNOWN_CAPABILITY = Object.freeze({
|
|
7451
7451
|
image_in: false,
|
|
7452
7452
|
video_in: false,
|
|
7453
7453
|
audio_in: false,
|
|
@@ -7460,16 +7460,47 @@ const UNKNOWN_CAPABILITY$1 = Object.freeze({
|
|
|
7460
7460
|
/**
|
|
7461
7461
|
* Base error for all chat provider errors.
|
|
7462
7462
|
*/
|
|
7463
|
-
var ChatProviderError
|
|
7463
|
+
var ChatProviderError = class extends Error {
|
|
7464
7464
|
constructor(message) {
|
|
7465
7465
|
super(message);
|
|
7466
7466
|
this.name = "ChatProviderError";
|
|
7467
7467
|
}
|
|
7468
7468
|
};
|
|
7469
7469
|
/**
|
|
7470
|
+
* Network-level connection failure.
|
|
7471
|
+
*/
|
|
7472
|
+
var APIConnectionError$3 = class extends ChatProviderError {
|
|
7473
|
+
constructor(message) {
|
|
7474
|
+
super(message);
|
|
7475
|
+
this.name = "APIConnectionError";
|
|
7476
|
+
}
|
|
7477
|
+
};
|
|
7478
|
+
/**
|
|
7479
|
+
* Request timed out.
|
|
7480
|
+
*/
|
|
7481
|
+
var APITimeoutError = class extends ChatProviderError {
|
|
7482
|
+
constructor(message) {
|
|
7483
|
+
super(message);
|
|
7484
|
+
this.name = "APITimeoutError";
|
|
7485
|
+
}
|
|
7486
|
+
};
|
|
7487
|
+
/**
|
|
7488
|
+
* HTTP status error from the API.
|
|
7489
|
+
*/
|
|
7490
|
+
var APIStatusError = class extends ChatProviderError {
|
|
7491
|
+
statusCode;
|
|
7492
|
+
requestId;
|
|
7493
|
+
constructor(statusCode, message, requestId) {
|
|
7494
|
+
super(message);
|
|
7495
|
+
this.name = "APIStatusError";
|
|
7496
|
+
this.statusCode = statusCode;
|
|
7497
|
+
this.requestId = requestId ?? null;
|
|
7498
|
+
}
|
|
7499
|
+
};
|
|
7500
|
+
/**
|
|
7470
7501
|
* The API returned an empty response (no content, no tool calls).
|
|
7471
7502
|
*/
|
|
7472
|
-
var APIEmptyResponseError = class extends ChatProviderError
|
|
7503
|
+
var APIEmptyResponseError = class extends ChatProviderError {
|
|
7473
7504
|
constructor(message) {
|
|
7474
7505
|
super(message);
|
|
7475
7506
|
this.name = "APIEmptyResponseError";
|
|
@@ -13462,7 +13493,7 @@ var APIError$2 = class APIError$2 extends OpenAIError {
|
|
|
13462
13493
|
return "(no status code or body)";
|
|
13463
13494
|
}
|
|
13464
13495
|
static generate(status, errorResponse, message, headers) {
|
|
13465
|
-
if (!status || !headers) return new APIConnectionError$
|
|
13496
|
+
if (!status || !headers) return new APIConnectionError$2({
|
|
13466
13497
|
message,
|
|
13467
13498
|
cause: castToError$2(errorResponse)
|
|
13468
13499
|
});
|
|
@@ -13483,13 +13514,13 @@ var APIUserAbortError$2 = class extends APIError$2 {
|
|
|
13483
13514
|
super(void 0, void 0, message || "Request was aborted.", void 0);
|
|
13484
13515
|
}
|
|
13485
13516
|
};
|
|
13486
|
-
var APIConnectionError$
|
|
13517
|
+
var APIConnectionError$2 = class extends APIError$2 {
|
|
13487
13518
|
constructor({ message, cause }) {
|
|
13488
13519
|
super(void 0, void 0, message || "Connection error.", void 0);
|
|
13489
13520
|
if (cause) this.cause = cause;
|
|
13490
13521
|
}
|
|
13491
13522
|
};
|
|
13492
|
-
var APIConnectionTimeoutError$2 = class extends APIConnectionError$
|
|
13523
|
+
var APIConnectionTimeoutError$2 = class extends APIConnectionError$2 {
|
|
13493
13524
|
constructor({ message } = {}) {
|
|
13494
13525
|
super({ message: message ?? "Request timed out." });
|
|
13495
13526
|
}
|
|
@@ -19915,7 +19946,7 @@ var OpenAI = class {
|
|
|
19915
19946
|
}));
|
|
19916
19947
|
if (response instanceof OAuthError$1 || response instanceof SubjectTokenProviderError) throw response;
|
|
19917
19948
|
if (isTimeout) throw new APIConnectionTimeoutError$2();
|
|
19918
|
-
throw new APIConnectionError$
|
|
19949
|
+
throw new APIConnectionError$2({ cause: response });
|
|
19919
19950
|
}
|
|
19920
19951
|
const responseInfo = `[${requestLogID}${retryLogStr}${[...response.headers.entries()].filter(([name]) => name === "x-request-id").map(([name, value]) => ", " + name + ": " + JSON.stringify(value)).join("")}] ${req.method} ${url} ${response.ok ? "succeeded" : "failed"} with status ${response.status} in ${headersTime - startTime}ms`;
|
|
19921
19952
|
if (!response.ok) {
|
|
@@ -20155,7 +20186,7 @@ OpenAI.OpenAI = _a$3;
|
|
|
20155
20186
|
OpenAI.DEFAULT_TIMEOUT = 6e5;
|
|
20156
20187
|
OpenAI.OpenAIError = OpenAIError;
|
|
20157
20188
|
OpenAI.APIError = APIError$2;
|
|
20158
|
-
OpenAI.APIConnectionError = APIConnectionError$
|
|
20189
|
+
OpenAI.APIConnectionError = APIConnectionError$2;
|
|
20159
20190
|
OpenAI.APIConnectionTimeoutError = APIConnectionTimeoutError$2;
|
|
20160
20191
|
OpenAI.APIUserAbortError = APIUserAbortError$2;
|
|
20161
20192
|
OpenAI.NotFoundError = NotFoundError$2;
|
|
@@ -20191,6 +20222,187 @@ OpenAI.Containers = Containers;
|
|
|
20191
20222
|
OpenAI.Skills = Skills$1;
|
|
20192
20223
|
OpenAI.Videos = Videos;
|
|
20193
20224
|
//#endregion
|
|
20225
|
+
//#region ../../packages/kosong/src/providers/openai-common.ts
|
|
20226
|
+
/**
|
|
20227
|
+
* Convert a kosong `ContentPart` to OpenAI-compatible content part.
|
|
20228
|
+
* Returns `null` for think parts (handled separately as reasoning_content).
|
|
20229
|
+
*/
|
|
20230
|
+
function convertContentPart(part) {
|
|
20231
|
+
switch (part.type) {
|
|
20232
|
+
case "text": return {
|
|
20233
|
+
type: "text",
|
|
20234
|
+
text: part.text
|
|
20235
|
+
};
|
|
20236
|
+
case "think": return null;
|
|
20237
|
+
case "image_url": return {
|
|
20238
|
+
type: "image_url",
|
|
20239
|
+
image_url: part.imageUrl.id === void 0 ? { url: part.imageUrl.url } : {
|
|
20240
|
+
url: part.imageUrl.url,
|
|
20241
|
+
id: part.imageUrl.id
|
|
20242
|
+
}
|
|
20243
|
+
};
|
|
20244
|
+
case "audio_url": return {
|
|
20245
|
+
type: "audio_url",
|
|
20246
|
+
audio_url: part.audioUrl.id === void 0 ? { url: part.audioUrl.url } : {
|
|
20247
|
+
url: part.audioUrl.url,
|
|
20248
|
+
id: part.audioUrl.id
|
|
20249
|
+
}
|
|
20250
|
+
};
|
|
20251
|
+
case "video_url": return {
|
|
20252
|
+
type: "video_url",
|
|
20253
|
+
video_url: part.videoUrl.id === void 0 ? { url: part.videoUrl.url } : {
|
|
20254
|
+
url: part.videoUrl.url,
|
|
20255
|
+
id: part.videoUrl.id
|
|
20256
|
+
}
|
|
20257
|
+
};
|
|
20258
|
+
default: throw new Error(`Unknown content part type: ${part.type}`);
|
|
20259
|
+
}
|
|
20260
|
+
}
|
|
20261
|
+
/**
|
|
20262
|
+
* Convert a kosong `Tool` to OpenAI tool format.
|
|
20263
|
+
*/
|
|
20264
|
+
function toolToOpenAI(tool) {
|
|
20265
|
+
return {
|
|
20266
|
+
type: "function",
|
|
20267
|
+
function: {
|
|
20268
|
+
name: tool.name,
|
|
20269
|
+
description: tool.description,
|
|
20270
|
+
parameters: tool.parameters
|
|
20271
|
+
}
|
|
20272
|
+
};
|
|
20273
|
+
}
|
|
20274
|
+
const NETWORK_RE$1 = /network|connection|connect|disconnect/i;
|
|
20275
|
+
const TIMEOUT_RE$1 = /timed?\s*out|timeout|deadline/i;
|
|
20276
|
+
function classifyBaseApiError(message) {
|
|
20277
|
+
if (TIMEOUT_RE$1.test(message)) return new APITimeoutError(message);
|
|
20278
|
+
if (NETWORK_RE$1.test(message)) return new APIConnectionError$3(message);
|
|
20279
|
+
return new ChatProviderError(`Error: ${message}`);
|
|
20280
|
+
}
|
|
20281
|
+
/**
|
|
20282
|
+
* Convert an OpenAI SDK error (or raw Error) to a kosong `ChatProviderError`.
|
|
20283
|
+
*/
|
|
20284
|
+
function convertOpenAIError(error) {
|
|
20285
|
+
if (error instanceof APIConnectionTimeoutError$2) return new APITimeoutError(error.message);
|
|
20286
|
+
if (error instanceof APIConnectionError$2) return new APIConnectionError$3(error.message);
|
|
20287
|
+
if (error instanceof APIError$2 && typeof error.status === "number") {
|
|
20288
|
+
const reqId = error.requestID ?? null;
|
|
20289
|
+
return new APIStatusError(error.status, error.message, reqId);
|
|
20290
|
+
}
|
|
20291
|
+
if (error instanceof APIError$2 && error.constructor === APIError$2 && error.error === void 0) return classifyBaseApiError(error.message);
|
|
20292
|
+
if (error instanceof OpenAIError) return new ChatProviderError(`Error: ${error.message}`);
|
|
20293
|
+
if (error instanceof Error) return new ChatProviderError(`Error: ${error.message}`);
|
|
20294
|
+
return new ChatProviderError(`Error: ${String(error)}`);
|
|
20295
|
+
}
|
|
20296
|
+
/**
|
|
20297
|
+
* Type guard: narrow a tool call union to the function-type variant.
|
|
20298
|
+
* Works with OpenAI SDK's `ChatCompletionMessageToolCall` as well as
|
|
20299
|
+
* any object carrying `{ type: string }`.
|
|
20300
|
+
*/
|
|
20301
|
+
function isFunctionToolCall(tc) {
|
|
20302
|
+
return tc.type === "function";
|
|
20303
|
+
}
|
|
20304
|
+
/**
|
|
20305
|
+
* Map kosong `ThinkingEffort` to OpenAI `reasoning_effort` string.
|
|
20306
|
+
*/
|
|
20307
|
+
function thinkingEffortToReasoningEffort(effort) {
|
|
20308
|
+
switch (effort) {
|
|
20309
|
+
case "off": return;
|
|
20310
|
+
case "low": return "low";
|
|
20311
|
+
case "medium": return "medium";
|
|
20312
|
+
case "high": return "high";
|
|
20313
|
+
default: throw new Error(`Unknown thinking effort: ${String(effort)}`);
|
|
20314
|
+
}
|
|
20315
|
+
}
|
|
20316
|
+
/**
|
|
20317
|
+
* Map OpenAI `reasoning_effort` string back to kosong `ThinkingEffort`.
|
|
20318
|
+
*/
|
|
20319
|
+
function reasoningEffortToThinkingEffort(reasoning) {
|
|
20320
|
+
if (reasoning === void 0 || reasoning === null) return null;
|
|
20321
|
+
switch (reasoning) {
|
|
20322
|
+
case "low":
|
|
20323
|
+
case "minimal": return "low";
|
|
20324
|
+
case "medium": return "medium";
|
|
20325
|
+
case "high":
|
|
20326
|
+
case "xhigh": return "high";
|
|
20327
|
+
case "none": return "off";
|
|
20328
|
+
default: return "off";
|
|
20329
|
+
}
|
|
20330
|
+
}
|
|
20331
|
+
/**
|
|
20332
|
+
* Extract `TokenUsage` from an OpenAI-compatible usage object.
|
|
20333
|
+
*/
|
|
20334
|
+
function extractUsage(usage) {
|
|
20335
|
+
if (usage === null || usage === void 0 || typeof usage !== "object") return null;
|
|
20336
|
+
const u = usage;
|
|
20337
|
+
const promptTokens = typeof u["prompt_tokens"] === "number" ? u["prompt_tokens"] : 0;
|
|
20338
|
+
const completionTokens = typeof u["completion_tokens"] === "number" ? u["completion_tokens"] : 0;
|
|
20339
|
+
let cached = 0;
|
|
20340
|
+
if (typeof u["cached_tokens"] === "number") cached = u["cached_tokens"];
|
|
20341
|
+
else if (typeof u["prompt_tokens_details"] === "object" && u["prompt_tokens_details"] !== null) {
|
|
20342
|
+
const details = u["prompt_tokens_details"];
|
|
20343
|
+
if (typeof details["cached_tokens"] === "number") cached = details["cached_tokens"];
|
|
20344
|
+
}
|
|
20345
|
+
return {
|
|
20346
|
+
inputOther: promptTokens - cached,
|
|
20347
|
+
output: completionTokens,
|
|
20348
|
+
inputCacheRead: cached,
|
|
20349
|
+
inputCacheCreation: 0
|
|
20350
|
+
};
|
|
20351
|
+
}
|
|
20352
|
+
/**
|
|
20353
|
+
* Normalize an OpenAI Chat Completions–style `finish_reason` string to the
|
|
20354
|
+
* unified {@link FinishReason} enum.
|
|
20355
|
+
*
|
|
20356
|
+
* Used by both the Kimi and OpenAI Legacy adapters because they share the
|
|
20357
|
+
* Chat Completions wire format. Returns `{ finishReason: null,
|
|
20358
|
+
* rawFinishReason: null }` when the upstream value is missing or `null` so
|
|
20359
|
+
* callers can treat "no signal" uniformly.
|
|
20360
|
+
*
|
|
20361
|
+
* Mapping:
|
|
20362
|
+
* - `'stop'` → `'completed'`
|
|
20363
|
+
* - `'tool_calls'` → `'tool_calls'`
|
|
20364
|
+
* - `'function_call'` → `'tool_calls'` (legacy alias)
|
|
20365
|
+
* - `'length'` → `'truncated'`
|
|
20366
|
+
* - `'content_filter'` → `'filtered'`
|
|
20367
|
+
* - any other non-null string → `'other'`
|
|
20368
|
+
*/
|
|
20369
|
+
function normalizeOpenAIFinishReason(raw) {
|
|
20370
|
+
if (raw === null || raw === void 0) return {
|
|
20371
|
+
finishReason: null,
|
|
20372
|
+
rawFinishReason: null
|
|
20373
|
+
};
|
|
20374
|
+
switch (raw) {
|
|
20375
|
+
case "stop": return {
|
|
20376
|
+
finishReason: "completed",
|
|
20377
|
+
rawFinishReason: raw
|
|
20378
|
+
};
|
|
20379
|
+
case "tool_calls":
|
|
20380
|
+
case "function_call": return {
|
|
20381
|
+
finishReason: "tool_calls",
|
|
20382
|
+
rawFinishReason: raw
|
|
20383
|
+
};
|
|
20384
|
+
case "length": return {
|
|
20385
|
+
finishReason: "truncated",
|
|
20386
|
+
rawFinishReason: raw
|
|
20387
|
+
};
|
|
20388
|
+
case "content_filter": return {
|
|
20389
|
+
finishReason: "filtered",
|
|
20390
|
+
rawFinishReason: raw
|
|
20391
|
+
};
|
|
20392
|
+
default: return {
|
|
20393
|
+
finishReason: "other",
|
|
20394
|
+
rawFinishReason: raw
|
|
20395
|
+
};
|
|
20396
|
+
}
|
|
20397
|
+
}
|
|
20398
|
+
/**
|
|
20399
|
+
* Convert tool-role message content according to the chosen strategy.
|
|
20400
|
+
*/
|
|
20401
|
+
function convertToolMessageContent(message, conversion) {
|
|
20402
|
+
if (conversion === "extract_text") return extractText(message);
|
|
20403
|
+
return message.content.map((p) => convertContentPart(p)).filter((p) => p !== null);
|
|
20404
|
+
}
|
|
20405
|
+
//#endregion
|
|
20194
20406
|
//#region ../../packages/kimi-core/src/hooks/execution-pipeline.ts
|
|
20195
20407
|
const ALLOW = Object.freeze({
|
|
20196
20408
|
blockAction: false,
|
|
@@ -20560,7 +20772,7 @@ var TurnManager = class {
|
|
|
20560
20772
|
if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return { error: "agent_busy" };
|
|
20561
20773
|
const input = req.data.input;
|
|
20562
20774
|
const capability = ((this.runtimeSlot?.current())?.runtime ?? this.runtime).kosong.getCapability?.(this.deps.contextState.model);
|
|
20563
|
-
if (capability !== void 0 && capability !== UNKNOWN_CAPABILITY
|
|
20775
|
+
if (capability !== void 0 && capability !== UNKNOWN_CAPABILITY) {
|
|
20564
20776
|
let inputContainsImage = false;
|
|
20565
20777
|
let inputContainsVideo = false;
|
|
20566
20778
|
for (const part of input.parts ?? []) if (part.type === "image_url") inputContainsImage = true;
|
|
@@ -20699,7 +20911,15 @@ var TurnManager = class {
|
|
|
20699
20911
|
kind: "soul",
|
|
20700
20912
|
agent_id: this.agentId
|
|
20701
20913
|
};
|
|
20702
|
-
|
|
20914
|
+
const soulConfig = {
|
|
20915
|
+
beforeStep: async () => {
|
|
20916
|
+
await this.deps.contextState.applySteerMessages();
|
|
20917
|
+
this.deps.contextState.beforeStep?.();
|
|
20918
|
+
},
|
|
20919
|
+
beforeTurnCompletes: async () => {
|
|
20920
|
+
return { continue: await this.deps.contextState.applySteerMessages() };
|
|
20921
|
+
}
|
|
20922
|
+
};
|
|
20703
20923
|
const scope = this.deps.toolExecutionScopeFactory?.create({
|
|
20704
20924
|
sessionId: this.sessionId,
|
|
20705
20925
|
agentId: this.agentId,
|
|
@@ -20711,27 +20931,20 @@ var TurnManager = class {
|
|
|
20711
20931
|
const runtimeForTurn = scope?.wrapRuntime(baseRuntime) ?? baseRuntime;
|
|
20712
20932
|
if (scope !== void 0) {
|
|
20713
20933
|
this.activeToolExecutionScope = scope;
|
|
20714
|
-
|
|
20934
|
+
mergeSoulConfig(soulConfig, scope.buildSoulConfig({
|
|
20715
20935
|
tools: this.deps.tools,
|
|
20716
20936
|
turnId,
|
|
20717
20937
|
permissionRules: () => mergeTurnRules(this.sessionRules, turnOverrides),
|
|
20718
20938
|
permissionMode: () => this.permissionMode,
|
|
20719
20939
|
approvalSource
|
|
20720
|
-
});
|
|
20721
|
-
|
|
20722
|
-
tools: [...scoped.tools],
|
|
20723
|
-
beforeToolCall: scoped.beforeToolCall,
|
|
20724
|
-
afterToolCall: scoped.afterToolCall
|
|
20725
|
-
};
|
|
20726
|
-
} else soulConfig = { tools: [...this.deps.tools] };
|
|
20940
|
+
}));
|
|
20941
|
+
} else mergeSoulConfig(soulConfig, { tools: this.deps.tools });
|
|
20727
20942
|
const compactionConfig = this.runtimeSlot !== void 0 ? runtimeBundle?.compactionConfig : this.compactionConfig;
|
|
20728
|
-
if (compactionConfig !== void 0) soulConfig
|
|
20729
|
-
...soulConfig,
|
|
20943
|
+
if (compactionConfig !== void 0) mergeSoulConfig(soulConfig, {
|
|
20730
20944
|
compactionConfig,
|
|
20731
20945
|
contextWindow: compactionConfig.maxContextSize
|
|
20732
|
-
};
|
|
20733
|
-
if (this.deps.hookEngine !== void 0) soulConfig
|
|
20734
|
-
...soulConfig,
|
|
20946
|
+
});
|
|
20947
|
+
if (this.deps.hookEngine !== void 0) mergeSoulConfig(soulConfig, {
|
|
20735
20948
|
beforeStep: async (ctx, signal) => {
|
|
20736
20949
|
const result = await this.executionHooks.run({
|
|
20737
20950
|
event: "PreStep",
|
|
@@ -20755,7 +20968,7 @@ var TurnManager = class {
|
|
|
20755
20968
|
stopReason: ctx.stopReason
|
|
20756
20969
|
}, signal);
|
|
20757
20970
|
}
|
|
20758
|
-
};
|
|
20971
|
+
});
|
|
20759
20972
|
const runPromise = this.runTurn(turnId, soulConfig, runtimeForTurn, controller.signal);
|
|
20760
20973
|
runPromise.catch(() => {});
|
|
20761
20974
|
this.deps.lifecycle.registerTurn(turnId, controller, runPromise);
|
|
@@ -24250,7 +24463,7 @@ var EditTool = class {
|
|
|
24250
24463
|
};
|
|
24251
24464
|
//#endregion
|
|
24252
24465
|
//#region ../../packages/kimi-core/src/tools/builtin/shell/bash.ts
|
|
24253
|
-
const DEFAULT_TIMEOUT_MS$
|
|
24466
|
+
const DEFAULT_TIMEOUT_MS$2 = 6e4;
|
|
24254
24467
|
const MAX_TIMEOUT_MS = 300 * 1e3;
|
|
24255
24468
|
const MAX_BACKGROUND_TIMEOUT_MS = 1440 * 60 * 1e3;
|
|
24256
24469
|
const SIGTERM_GRACE_MS$1 = 5e3;
|
|
@@ -24264,7 +24477,7 @@ If \`run_in_background=true\`, the command will be started as a background task
|
|
|
24264
24477
|
|
|
24265
24478
|
**Guidelines for safety and security:**
|
|
24266
24479
|
- Each shell tool call will be executed in a fresh shell environment. The shell variables, current working directory changes, and the shell history is not preserved between calls.
|
|
24267
|
-
- The tool call will return after the command is finished. You shall not use this tool to execute an interactive command or a command that may run forever. For possibly long-running commands, set the \`timeout\` argument in milliseconds. The default is ${String(DEFAULT_TIMEOUT_MS$
|
|
24480
|
+
- The tool call will return after the command is finished. You shall not use this tool to execute an interactive command or a command that may run forever. For possibly long-running commands, set the \`timeout\` argument in milliseconds. The default is ${String(DEFAULT_TIMEOUT_MS$2)}ms; foreground commands allow up to ${String(MAX_TIMEOUT_MS)}ms, and background commands allow up to ${String(MAX_BACKGROUND_TIMEOUT_MS)}ms.
|
|
24268
24481
|
- Avoid using \`..\` to access files or directories outside of the working directory.
|
|
24269
24482
|
- Avoid modifying files outside of the working directory unless explicitly instructed to do so.
|
|
24270
24483
|
- Never run commands that require superuser privileges unless explicitly instructed to do so.
|
|
@@ -24287,7 +24500,7 @@ If \`run_in_background=true\`, the command will be started as a background task
|
|
|
24287
24500
|
|
|
24288
24501
|
**Guidelines for safety and security:**
|
|
24289
24502
|
- Every tool call starts a fresh PowerShell session. Environment variables, \`cd\` changes, and command history do not persist between calls.
|
|
24290
|
-
- Do not launch interactive programs or anything that is expected to block indefinitely; ensure each command finishes promptly. Provide a \`timeout\` argument in milliseconds for potentially long runs. The default is ${String(DEFAULT_TIMEOUT_MS$
|
|
24503
|
+
- Do not launch interactive programs or anything that is expected to block indefinitely; ensure each command finishes promptly. Provide a \`timeout\` argument in milliseconds for potentially long runs. The default is ${String(DEFAULT_TIMEOUT_MS$2)}ms; foreground commands allow up to ${String(MAX_TIMEOUT_MS)}ms, and background commands allow up to ${String(MAX_BACKGROUND_TIMEOUT_MS)}ms.
|
|
24291
24504
|
- Avoid using \`..\` to leave the working directory, and never touch files outside that directory unless explicitly instructed.
|
|
24292
24505
|
- Never attempt commands that require elevated (Administrator) privileges unless explicitly authorized.
|
|
24293
24506
|
|
|
@@ -24394,7 +24607,7 @@ var ShellTool = class {
|
|
|
24394
24607
|
};
|
|
24395
24608
|
return this.executeInBackground(args);
|
|
24396
24609
|
}
|
|
24397
|
-
const timeoutMs = Math.min(args.timeout !== void 0 ? args.timeout : DEFAULT_TIMEOUT_MS$
|
|
24610
|
+
const timeoutMs = Math.min(args.timeout !== void 0 ? args.timeout : DEFAULT_TIMEOUT_MS$2, MAX_TIMEOUT_MS);
|
|
24398
24611
|
let proc;
|
|
24399
24612
|
try {
|
|
24400
24613
|
const effectiveCwd = args.cwd ?? this.cwd;
|
|
@@ -24497,7 +24710,7 @@ var ShellTool = class {
|
|
|
24497
24710
|
isError: true,
|
|
24498
24711
|
content: "description is required when run_in_background is true."
|
|
24499
24712
|
};
|
|
24500
|
-
const timeoutMs = Math.min(args.timeout !== void 0 ? args.timeout : DEFAULT_TIMEOUT_MS$
|
|
24713
|
+
const timeoutMs = Math.min(args.timeout !== void 0 ? args.timeout : DEFAULT_TIMEOUT_MS$2, MAX_BACKGROUND_TIMEOUT_MS);
|
|
24501
24714
|
let proc;
|
|
24502
24715
|
try {
|
|
24503
24716
|
const effectiveCwd = args.cwd ?? this.cwd;
|
|
@@ -28911,7 +29124,7 @@ Error: ${cause instanceof Error ? cause.message : typeof cause === "string" ? ca
|
|
|
28911
29124
|
}
|
|
28912
29125
|
//#endregion
|
|
28913
29126
|
//#region ../../packages/kimi-core/src/tools/builtin/file/grep.ts
|
|
28914
|
-
const DEFAULT_TIMEOUT_MS = 2e4;
|
|
29127
|
+
const DEFAULT_TIMEOUT_MS$1 = 2e4;
|
|
28915
29128
|
const SIGTERM_GRACE_MS = 5e3;
|
|
28916
29129
|
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
28917
29130
|
const CONTENT_LINE_RE = /^(.*?)([:-])(\d+)\2/;
|
|
@@ -28997,7 +29210,7 @@ var GrepTool = class {
|
|
|
28997
29210
|
const timeoutHandle = setTimeout(() => {
|
|
28998
29211
|
timedOut = true;
|
|
28999
29212
|
killProc();
|
|
29000
|
-
}, DEFAULT_TIMEOUT_MS);
|
|
29213
|
+
}, DEFAULT_TIMEOUT_MS$1);
|
|
29001
29214
|
let exitCode = 0;
|
|
29002
29215
|
let stdoutText = "";
|
|
29003
29216
|
let stderrText = "";
|
|
@@ -29050,7 +29263,7 @@ var GrepTool = class {
|
|
|
29050
29263
|
messages.push(`Results truncated to ${String(headLimit ?? 0)} lines (total: ${String(total)}). Use offset=${String(nextOffset)} to see more.`);
|
|
29051
29264
|
}
|
|
29052
29265
|
if (bufferTruncated) messages.push(`[stdout truncated at ${String(MAX_OUTPUT_BYTES)} bytes]`);
|
|
29053
|
-
if (timedOut) messages.push(`Grep timed out after ${String(DEFAULT_TIMEOUT_MS / 1e3)}s; partial results`);
|
|
29266
|
+
if (timedOut) messages.push(`Grep timed out after ${String(DEFAULT_TIMEOUT_MS$1 / 1e3)}s; partial results`);
|
|
29054
29267
|
const contentBody = limited.join("\n");
|
|
29055
29268
|
const combined = messages.length > 0 ? contentBody === "" ? messages.join("\n") : `${contentBody}\n${messages.join("\n")}` : contentBody;
|
|
29056
29269
|
if (mode === "files_with_matches") return {
|
|
@@ -30662,12 +30875,12 @@ var DefaultSessionApplicationService = class {
|
|
|
30662
30875
|
workspaceDir: this.deps.workspaceDir,
|
|
30663
30876
|
compactionProvider,
|
|
30664
30877
|
compactionConfig,
|
|
30665
|
-
|
|
30666
|
-
|
|
30878
|
+
approvalRuntime: this.deps.approvalRuntime,
|
|
30879
|
+
approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
|
|
30667
30880
|
hookEngine,
|
|
30668
30881
|
skillManager: this.deps.skillManager,
|
|
30669
|
-
|
|
30670
|
-
|
|
30882
|
+
agentTypeRegistry: this.deps.agentTypeRegistry,
|
|
30883
|
+
questionRuntime: this.deps.questionRuntimeProvider?.({ sessionId }),
|
|
30671
30884
|
logger: this.deps.logger
|
|
30672
30885
|
});
|
|
30673
30886
|
this.deps.sessionLifecycle?.onSessionCreated?.(managed);
|
|
@@ -30707,12 +30920,12 @@ var DefaultSessionApplicationService = class {
|
|
|
30707
30920
|
eventBus,
|
|
30708
30921
|
compactionProvider,
|
|
30709
30922
|
compactionConfig,
|
|
30710
|
-
|
|
30711
|
-
|
|
30923
|
+
approvalRuntime: this.deps.approvalRuntime,
|
|
30924
|
+
approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
|
|
30712
30925
|
hookEngine,
|
|
30713
30926
|
skillManager: this.deps.skillManager,
|
|
30714
|
-
|
|
30715
|
-
|
|
30927
|
+
agentTypeRegistry: this.deps.agentTypeRegistry,
|
|
30928
|
+
questionRuntime: this.deps.questionRuntimeProvider?.({ sessionId }),
|
|
30716
30929
|
logger: this.deps.logger
|
|
30717
30930
|
});
|
|
30718
30931
|
const runtimeBundleProvider = this.deps.runtimeBundleProvider;
|
|
@@ -30777,7 +30990,7 @@ var DefaultSessionApplicationService = class {
|
|
|
30777
30990
|
turn_count: turnCount,
|
|
30778
30991
|
last_turn_id: lastTurnId,
|
|
30779
30992
|
status,
|
|
30780
|
-
|
|
30993
|
+
plan_path: planPath
|
|
30781
30994
|
};
|
|
30782
30995
|
}
|
|
30783
30996
|
getStatus(sessionId) {
|
|
@@ -32772,6 +32985,12 @@ function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
|
32772
32985
|
content,
|
|
32773
32986
|
toolCalls: openStep.toolCalls
|
|
32774
32987
|
});
|
|
32988
|
+
for (const tr of openStep.toolResults) messages.push({
|
|
32989
|
+
role: "tool",
|
|
32990
|
+
content: tr.content,
|
|
32991
|
+
toolCalls: [],
|
|
32992
|
+
toolCallId: tr.toolCallId
|
|
32993
|
+
});
|
|
32775
32994
|
openStep = null;
|
|
32776
32995
|
}
|
|
32777
32996
|
for (const r of records) {
|
|
@@ -32795,18 +33014,25 @@ function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
|
32795
33014
|
break;
|
|
32796
33015
|
}
|
|
32797
33016
|
case "tool_result": {
|
|
32798
|
-
flushOpenStep();
|
|
32799
33017
|
const pendingTodo = pendingTodoWrites.get(r.tool_call_id);
|
|
32800
33018
|
if (pendingTodo !== void 0) {
|
|
32801
33019
|
if (r.is_error !== true) todos = pendingTodo.map((todo) => ({ ...todo }));
|
|
32802
33020
|
pendingTodoWrites.delete(r.tool_call_id);
|
|
32803
33021
|
}
|
|
33022
|
+
const toolContent = isContentPartArray(r.output) ? r.output.map(cloneContentPart) : [{
|
|
33023
|
+
type: "text",
|
|
33024
|
+
text: typeof r.output === "string" ? r.output : JSON.stringify(r.output)
|
|
33025
|
+
}];
|
|
33026
|
+
if (openStep !== null && !openStep.hasStepEnd) {
|
|
33027
|
+
openStep.toolResults.push({
|
|
33028
|
+
toolCallId: r.tool_call_id,
|
|
33029
|
+
content: toolContent
|
|
33030
|
+
});
|
|
33031
|
+
break;
|
|
33032
|
+
}
|
|
32804
33033
|
messages.push({
|
|
32805
33034
|
role: "tool",
|
|
32806
|
-
content:
|
|
32807
|
-
type: "text",
|
|
32808
|
-
text: typeof r.output === "string" ? r.output : JSON.stringify(r.output)
|
|
32809
|
-
}],
|
|
33035
|
+
content: toolContent,
|
|
32810
33036
|
toolCalls: [],
|
|
32811
33037
|
toolCallId: r.tool_call_id
|
|
32812
33038
|
});
|
|
@@ -32846,6 +33072,7 @@ function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
|
32846
33072
|
textChunks: [],
|
|
32847
33073
|
thinkChunks: [],
|
|
32848
33074
|
toolCalls: [],
|
|
33075
|
+
toolResults: [],
|
|
32849
33076
|
hasStepEnd: false
|
|
32850
33077
|
};
|
|
32851
33078
|
break;
|
|
@@ -32857,6 +33084,7 @@ function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
|
32857
33084
|
textChunks: [],
|
|
32858
33085
|
thinkChunks: [],
|
|
32859
33086
|
toolCalls: [],
|
|
33087
|
+
toolResults: [],
|
|
32860
33088
|
hasStepEnd: false
|
|
32861
33089
|
};
|
|
32862
33090
|
}
|
|
@@ -32874,6 +33102,7 @@ function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
|
32874
33102
|
textChunks: [],
|
|
32875
33103
|
thinkChunks: [],
|
|
32876
33104
|
toolCalls: [],
|
|
33105
|
+
toolResults: [],
|
|
32877
33106
|
hasStepEnd: false
|
|
32878
33107
|
};
|
|
32879
33108
|
}
|
|
@@ -32961,7 +33190,7 @@ function normalizeSessionTimestampSeconds(value) {
|
|
|
32961
33190
|
async function readSessionInfo(paths, sessionId) {
|
|
32962
33191
|
const statePath = paths.statePath(sessionId);
|
|
32963
33192
|
const cache = new StateCache(statePath);
|
|
32964
|
-
const [state, lastActivity] = await Promise.all([cache.read(), stat(statePath).then((st) => Math.floor(st.mtimeMs / 1e3), () =>
|
|
33193
|
+
const [state, lastActivity] = await Promise.all([cache.read(), stat(statePath).then((st) => Math.floor(st.mtimeMs / 1e3), () => void 0)]);
|
|
32965
33194
|
if (state !== null) return {
|
|
32966
33195
|
session_id: state.session_id,
|
|
32967
33196
|
created_at: state.created_at,
|
|
@@ -33287,13 +33516,13 @@ var SessionManager = class {
|
|
|
33287
33516
|
runtime: runtimeBundle.runtime,
|
|
33288
33517
|
eventBus,
|
|
33289
33518
|
tools: sessionTools,
|
|
33290
|
-
|
|
33519
|
+
enabledToolNames: options.enabledToolNames,
|
|
33291
33520
|
lifecycleStateMachine,
|
|
33292
33521
|
producer: this.producer,
|
|
33293
33522
|
onShellDeliver: options.onShellDeliver,
|
|
33294
33523
|
skillManager: options.skillManager,
|
|
33295
|
-
|
|
33296
|
-
|
|
33524
|
+
questionRuntime: options.questionRuntime,
|
|
33525
|
+
compactionConfig: runtimeBundle.compactionConfig,
|
|
33297
33526
|
compactionProvider: runtimeBundle.compactionProvider,
|
|
33298
33527
|
journalCapability: options.journalCapability ?? createWiredJournalCapability({
|
|
33299
33528
|
sessionDir,
|
|
@@ -33302,13 +33531,13 @@ var SessionManager = class {
|
|
|
33302
33531
|
}),
|
|
33303
33532
|
approvalRuntime,
|
|
33304
33533
|
hookEngine: options.hookEngine,
|
|
33305
|
-
|
|
33534
|
+
toolExecutionScopeFactory: options.toolExecutionScopeFactory,
|
|
33306
33535
|
sessionRules: options.sessionRules,
|
|
33307
33536
|
permissionMode: options.permissionMode,
|
|
33308
33537
|
stateCache,
|
|
33309
33538
|
initialMeta,
|
|
33310
33539
|
pathConfig: this.paths,
|
|
33311
|
-
|
|
33540
|
+
approvalStateStore: options.approvalStateStore,
|
|
33312
33541
|
...options.agentTypeRegistry !== void 0 ? {
|
|
33313
33542
|
subagentStore: new SubagentStore(sessionDir),
|
|
33314
33543
|
agentTypeRegistry: options.agentTypeRegistry,
|
|
@@ -33425,7 +33654,7 @@ var SessionManager = class {
|
|
|
33425
33654
|
model: projected.model,
|
|
33426
33655
|
runtime: options.runtime,
|
|
33427
33656
|
compactionProvider: options.compactionProvider,
|
|
33428
|
-
|
|
33657
|
+
compactionConfig: options.compactionConfig
|
|
33429
33658
|
});
|
|
33430
33659
|
const runtimeBundle = runtimeSlot.current();
|
|
33431
33660
|
await stateCache.write({
|
|
@@ -33457,10 +33686,7 @@ var SessionManager = class {
|
|
|
33457
33686
|
subagentStore = new SubagentStore(sessionDir);
|
|
33458
33687
|
lostSubagentIds = await cleanupStaleSubagents(subagentStore);
|
|
33459
33688
|
}
|
|
33460
|
-
const approvalRuntime = this.buildApprovalRuntime(sessionId, sessionJournal, stateCache,
|
|
33461
|
-
...options.approvalRuntime !== void 0 ? { approvalRuntime: options.approvalRuntime } : {},
|
|
33462
|
-
...options.approvalRuntimeFactory !== void 0 ? { approvalRuntimeFactory: options.approvalRuntimeFactory } : {}
|
|
33463
|
-
});
|
|
33689
|
+
const approvalRuntime = this.buildApprovalRuntime(sessionId, sessionJournal, stateCache, options);
|
|
33464
33690
|
const soulPlus = new SoulPlus({
|
|
33465
33691
|
sessionId,
|
|
33466
33692
|
contextState,
|
|
@@ -33775,7 +34001,7 @@ var SessionManager = class {
|
|
|
33775
34001
|
turn_count: meta?.turn_count ?? existing.turn_count ?? 0,
|
|
33776
34002
|
plan_slug: meta?.plan_slug,
|
|
33777
34003
|
thinking_level: currentThinkingLevel,
|
|
33778
|
-
|
|
34004
|
+
plan_mode: currentPlanMode,
|
|
33779
34005
|
todos: cloneTodoItems(currentTodos),
|
|
33780
34006
|
last_exit_code: "clean"
|
|
33781
34007
|
} : {
|
|
@@ -41668,12 +41894,9 @@ const KimiConfigSchema = z.object({
|
|
|
41668
41894
|
defaultThinking: z.boolean().optional(),
|
|
41669
41895
|
defaultYolo: z.boolean().optional(),
|
|
41670
41896
|
defaultPlanMode: z.boolean().optional(),
|
|
41671
|
-
defaultEditor: z.string().optional(),
|
|
41672
|
-
theme: z.string().optional(),
|
|
41673
41897
|
hooks: z.array(z.unknown()).optional(),
|
|
41674
41898
|
services: ServicesConfigSchema.optional(),
|
|
41675
41899
|
mergeAllAvailableSkills: z.boolean().optional(),
|
|
41676
|
-
showThinkingStream: z.boolean().optional(),
|
|
41677
41900
|
loopControl: LoopControlSchema.optional(),
|
|
41678
41901
|
raw: z.record(z.string(), z.unknown()).optional()
|
|
41679
41902
|
});
|
|
@@ -41871,11 +42094,8 @@ function configToTomlData(config) {
|
|
|
41871
42094
|
setDefined(out, "default_thinking", config.defaultThinking);
|
|
41872
42095
|
setDefined(out, "default_yolo", config.defaultYolo);
|
|
41873
42096
|
setDefined(out, "default_plan_mode", config.defaultPlanMode);
|
|
41874
|
-
setDefined(out, "default_editor", config.defaultEditor);
|
|
41875
|
-
setDefined(out, "theme", config.theme);
|
|
41876
42097
|
setDefined(out, "hooks", config.hooks);
|
|
41877
42098
|
setDefined(out, "merge_all_available_skills", config.mergeAllAvailableSkills);
|
|
41878
|
-
setDefined(out, "show_thinking_stream", config.showThinkingStream);
|
|
41879
42099
|
const rawProviders = cloneRecord(out["providers"]);
|
|
41880
42100
|
out["providers"] = Object.fromEntries(Object.entries(config.providers).map(([name, provider]) => [name, providerToToml(provider, rawProviders[name])]));
|
|
41881
42101
|
if (config.models !== void 0) {
|
|
@@ -41890,63 +42110,6 @@ function configToTomlData(config) {
|
|
|
41890
42110
|
return out;
|
|
41891
42111
|
}
|
|
41892
42112
|
//#endregion
|
|
41893
|
-
//#region ../../packages/kosong/dist/capability-6GoTY4Ii.mjs
|
|
41894
|
-
/**
|
|
41895
|
-
* Shared read-only default returned when a provider has not catalogued a
|
|
41896
|
-
* given model. Frozen so accidental mutation at one call site cannot leak
|
|
41897
|
-
* into another.
|
|
41898
|
-
*/
|
|
41899
|
-
const UNKNOWN_CAPABILITY = Object.freeze({
|
|
41900
|
-
image_in: false,
|
|
41901
|
-
video_in: false,
|
|
41902
|
-
audio_in: false,
|
|
41903
|
-
thinking: false,
|
|
41904
|
-
tool_use: false,
|
|
41905
|
-
max_context_tokens: 0
|
|
41906
|
-
});
|
|
41907
|
-
//#endregion
|
|
41908
|
-
//#region ../../packages/kosong/dist/errors-DYOwJhxZ.mjs
|
|
41909
|
-
/**
|
|
41910
|
-
* Base error for all chat provider errors.
|
|
41911
|
-
*/
|
|
41912
|
-
var ChatProviderError = class extends Error {
|
|
41913
|
-
constructor(message) {
|
|
41914
|
-
super(message);
|
|
41915
|
-
this.name = "ChatProviderError";
|
|
41916
|
-
}
|
|
41917
|
-
};
|
|
41918
|
-
/**
|
|
41919
|
-
* Network-level connection failure.
|
|
41920
|
-
*/
|
|
41921
|
-
var APIConnectionError$2 = class extends ChatProviderError {
|
|
41922
|
-
constructor(message) {
|
|
41923
|
-
super(message);
|
|
41924
|
-
this.name = "APIConnectionError";
|
|
41925
|
-
}
|
|
41926
|
-
};
|
|
41927
|
-
/**
|
|
41928
|
-
* Request timed out.
|
|
41929
|
-
*/
|
|
41930
|
-
var APITimeoutError = class extends ChatProviderError {
|
|
41931
|
-
constructor(message) {
|
|
41932
|
-
super(message);
|
|
41933
|
-
this.name = "APITimeoutError";
|
|
41934
|
-
}
|
|
41935
|
-
};
|
|
41936
|
-
/**
|
|
41937
|
-
* HTTP status error from the API.
|
|
41938
|
-
*/
|
|
41939
|
-
var APIStatusError = class extends ChatProviderError {
|
|
41940
|
-
statusCode;
|
|
41941
|
-
requestId;
|
|
41942
|
-
constructor(statusCode, message, requestId) {
|
|
41943
|
-
super(message);
|
|
41944
|
-
this.name = "APIStatusError";
|
|
41945
|
-
this.statusCode = statusCode;
|
|
41946
|
-
this.requestId = requestId ?? null;
|
|
41947
|
-
}
|
|
41948
|
-
};
|
|
41949
|
-
//#endregion
|
|
41950
42113
|
//#region ../../node_modules/.pnpm/@anthropic-ai+sdk@0.88.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/internal/tslib.mjs
|
|
41951
42114
|
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
41952
42115
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
@@ -47091,7 +47254,7 @@ Anthropic.Messages = Messages;
|
|
|
47091
47254
|
Anthropic.Models = Models$1;
|
|
47092
47255
|
Anthropic.Beta = Beta;
|
|
47093
47256
|
//#endregion
|
|
47094
|
-
//#region ../../packages/kosong/
|
|
47257
|
+
//#region ../../packages/kosong/src/providers/anthropic.ts
|
|
47095
47258
|
/**
|
|
47096
47259
|
* Normalize an Anthropic `stop_reason` string to the unified
|
|
47097
47260
|
* {@link FinishReason} enum.
|
|
@@ -47277,7 +47440,7 @@ function convertMessage$3(message) {
|
|
|
47277
47440
|
}
|
|
47278
47441
|
function convertAnthropicError(error) {
|
|
47279
47442
|
if (error instanceof APIConnectionTimeoutError$1) return new APITimeoutError(error.message);
|
|
47280
|
-
if (error instanceof APIConnectionError$1) return new APIConnectionError$
|
|
47443
|
+
if (error instanceof APIConnectionError$1) return new APIConnectionError$3(error.message);
|
|
47281
47444
|
if (error instanceof APIError$1 && typeof error.status === "number") {
|
|
47282
47445
|
const reqId = error.requestID ?? null;
|
|
47283
47446
|
return new APIStatusError(error.status, error.message, reqId);
|
|
@@ -75826,7 +75989,7 @@ function getApiKeyFromEnv() {
|
|
|
75826
75989
|
return envGoogleApiKey || envGeminiApiKey || void 0;
|
|
75827
75990
|
}
|
|
75828
75991
|
//#endregion
|
|
75829
|
-
//#region ../../packages/kosong/
|
|
75992
|
+
//#region ../../packages/kosong/src/providers/google-genai.ts
|
|
75830
75993
|
/**
|
|
75831
75994
|
* Normalize a Google GenAI (Gemini) `finishReason` value to the unified
|
|
75832
75995
|
* {@link FinishReason} enum.
|
|
@@ -76214,8 +76377,8 @@ var GoogleGenAIStreamedMessage = class {
|
|
|
76214
76377
|
}
|
|
76215
76378
|
}
|
|
76216
76379
|
};
|
|
76217
|
-
const NETWORK_RE
|
|
76218
|
-
const TIMEOUT_RE
|
|
76380
|
+
const NETWORK_RE = /network|connection|connect|disconnect|fetch failed/i;
|
|
76381
|
+
const TIMEOUT_RE = /timed?\s*out|timeout|deadline/i;
|
|
76219
76382
|
/**
|
|
76220
76383
|
* Convert a Google GenAI SDK error (or raw Error) to a kosong `ChatProviderError`.
|
|
76221
76384
|
*/
|
|
@@ -76223,8 +76386,8 @@ function convertGoogleGenAIError(error) {
|
|
|
76223
76386
|
if (error instanceof ApiError) return new APIStatusError(error.status, error.message);
|
|
76224
76387
|
if (error instanceof Error) {
|
|
76225
76388
|
const msg = error.message;
|
|
76226
|
-
if (TIMEOUT_RE
|
|
76227
|
-
if (NETWORK_RE
|
|
76389
|
+
if (TIMEOUT_RE.test(msg)) return new APITimeoutError(msg);
|
|
76390
|
+
if (NETWORK_RE.test(msg) || error instanceof TypeError && msg.includes("fetch")) return new APIConnectionError$3(msg);
|
|
76228
76391
|
const statusCode = error.code;
|
|
76229
76392
|
if (typeof statusCode === "number") return new APIStatusError(statusCode, msg);
|
|
76230
76393
|
return new ChatProviderError(`GoogleGenAI error: ${msg}`);
|
|
@@ -76388,197 +76551,7 @@ var GoogleGenAIChatProvider = class {
|
|
|
76388
76551
|
}
|
|
76389
76552
|
};
|
|
76390
76553
|
//#endregion
|
|
76391
|
-
//#region ../../packages/kosong/
|
|
76392
|
-
/**
|
|
76393
|
-
* Extract the concatenated text from a message's content parts.
|
|
76394
|
-
*
|
|
76395
|
-
* @param message The message to extract text from.
|
|
76396
|
-
* @param sep Separator between text parts. Defaults to empty string.
|
|
76397
|
-
*/
|
|
76398
|
-
function extractText(message, sep = "") {
|
|
76399
|
-
return message.content.filter((part) => part.type === "text").map((part) => part.text).join(sep);
|
|
76400
|
-
}
|
|
76401
|
-
/**
|
|
76402
|
-
* Convert a kosong `ContentPart` to OpenAI-compatible content part.
|
|
76403
|
-
* Returns `null` for think parts (handled separately as reasoning_content).
|
|
76404
|
-
*/
|
|
76405
|
-
function convertContentPart(part) {
|
|
76406
|
-
switch (part.type) {
|
|
76407
|
-
case "text": return {
|
|
76408
|
-
type: "text",
|
|
76409
|
-
text: part.text
|
|
76410
|
-
};
|
|
76411
|
-
case "think": return null;
|
|
76412
|
-
case "image_url": return {
|
|
76413
|
-
type: "image_url",
|
|
76414
|
-
image_url: part.imageUrl.id === void 0 ? { url: part.imageUrl.url } : {
|
|
76415
|
-
url: part.imageUrl.url,
|
|
76416
|
-
id: part.imageUrl.id
|
|
76417
|
-
}
|
|
76418
|
-
};
|
|
76419
|
-
case "audio_url": return {
|
|
76420
|
-
type: "audio_url",
|
|
76421
|
-
audio_url: part.audioUrl.id === void 0 ? { url: part.audioUrl.url } : {
|
|
76422
|
-
url: part.audioUrl.url,
|
|
76423
|
-
id: part.audioUrl.id
|
|
76424
|
-
}
|
|
76425
|
-
};
|
|
76426
|
-
case "video_url": return {
|
|
76427
|
-
type: "video_url",
|
|
76428
|
-
video_url: part.videoUrl.id === void 0 ? { url: part.videoUrl.url } : {
|
|
76429
|
-
url: part.videoUrl.url,
|
|
76430
|
-
id: part.videoUrl.id
|
|
76431
|
-
}
|
|
76432
|
-
};
|
|
76433
|
-
default: throw new Error(`Unknown content part type: ${part.type}`);
|
|
76434
|
-
}
|
|
76435
|
-
}
|
|
76436
|
-
/**
|
|
76437
|
-
* Convert a kosong `Tool` to OpenAI tool format.
|
|
76438
|
-
*/
|
|
76439
|
-
function toolToOpenAI(tool) {
|
|
76440
|
-
return {
|
|
76441
|
-
type: "function",
|
|
76442
|
-
function: {
|
|
76443
|
-
name: tool.name,
|
|
76444
|
-
description: tool.description,
|
|
76445
|
-
parameters: tool.parameters
|
|
76446
|
-
}
|
|
76447
|
-
};
|
|
76448
|
-
}
|
|
76449
|
-
const NETWORK_RE = /network|connection|connect|disconnect/i;
|
|
76450
|
-
const TIMEOUT_RE = /timed?\s*out|timeout|deadline/i;
|
|
76451
|
-
function classifyBaseApiError(message) {
|
|
76452
|
-
if (TIMEOUT_RE.test(message)) return new APITimeoutError(message);
|
|
76453
|
-
if (NETWORK_RE.test(message)) return new APIConnectionError$2(message);
|
|
76454
|
-
return new ChatProviderError(`Error: ${message}`);
|
|
76455
|
-
}
|
|
76456
|
-
/**
|
|
76457
|
-
* Convert an OpenAI SDK error (or raw Error) to a kosong `ChatProviderError`.
|
|
76458
|
-
*/
|
|
76459
|
-
function convertOpenAIError(error) {
|
|
76460
|
-
if (error instanceof APIConnectionTimeoutError$2) return new APITimeoutError(error.message);
|
|
76461
|
-
if (error instanceof APIConnectionError$3) return new APIConnectionError$2(error.message);
|
|
76462
|
-
if (error instanceof APIError$2 && typeof error.status === "number") {
|
|
76463
|
-
const reqId = error.requestID ?? null;
|
|
76464
|
-
return new APIStatusError(error.status, error.message, reqId);
|
|
76465
|
-
}
|
|
76466
|
-
if (error instanceof APIError$2 && error.constructor === APIError$2 && error.error === void 0) return classifyBaseApiError(error.message);
|
|
76467
|
-
if (error instanceof OpenAIError) return new ChatProviderError(`Error: ${error.message}`);
|
|
76468
|
-
if (error instanceof Error) return new ChatProviderError(`Error: ${error.message}`);
|
|
76469
|
-
return new ChatProviderError(`Error: ${String(error)}`);
|
|
76470
|
-
}
|
|
76471
|
-
/**
|
|
76472
|
-
* Type guard: narrow a tool call union to the function-type variant.
|
|
76473
|
-
* Works with OpenAI SDK's `ChatCompletionMessageToolCall` as well as
|
|
76474
|
-
* any object carrying `{ type: string }`.
|
|
76475
|
-
*/
|
|
76476
|
-
function isFunctionToolCall(tc) {
|
|
76477
|
-
return tc.type === "function";
|
|
76478
|
-
}
|
|
76479
|
-
/**
|
|
76480
|
-
* Map kosong `ThinkingEffort` to OpenAI `reasoning_effort` string.
|
|
76481
|
-
*/
|
|
76482
|
-
function thinkingEffortToReasoningEffort(effort) {
|
|
76483
|
-
switch (effort) {
|
|
76484
|
-
case "off": return;
|
|
76485
|
-
case "low": return "low";
|
|
76486
|
-
case "medium": return "medium";
|
|
76487
|
-
case "high": return "high";
|
|
76488
|
-
default: throw new Error(`Unknown thinking effort: ${String(effort)}`);
|
|
76489
|
-
}
|
|
76490
|
-
}
|
|
76491
|
-
/**
|
|
76492
|
-
* Map OpenAI `reasoning_effort` string back to kosong `ThinkingEffort`.
|
|
76493
|
-
*/
|
|
76494
|
-
function reasoningEffortToThinkingEffort(reasoning) {
|
|
76495
|
-
if (reasoning === void 0 || reasoning === null) return null;
|
|
76496
|
-
switch (reasoning) {
|
|
76497
|
-
case "low":
|
|
76498
|
-
case "minimal": return "low";
|
|
76499
|
-
case "medium": return "medium";
|
|
76500
|
-
case "high":
|
|
76501
|
-
case "xhigh": return "high";
|
|
76502
|
-
case "none": return "off";
|
|
76503
|
-
default: return "off";
|
|
76504
|
-
}
|
|
76505
|
-
}
|
|
76506
|
-
/**
|
|
76507
|
-
* Extract `TokenUsage` from an OpenAI-compatible usage object.
|
|
76508
|
-
*/
|
|
76509
|
-
function extractUsage(usage) {
|
|
76510
|
-
if (usage === null || usage === void 0 || typeof usage !== "object") return null;
|
|
76511
|
-
const u = usage;
|
|
76512
|
-
const promptTokens = typeof u["prompt_tokens"] === "number" ? u["prompt_tokens"] : 0;
|
|
76513
|
-
const completionTokens = typeof u["completion_tokens"] === "number" ? u["completion_tokens"] : 0;
|
|
76514
|
-
let cached = 0;
|
|
76515
|
-
if (typeof u["cached_tokens"] === "number") cached = u["cached_tokens"];
|
|
76516
|
-
else if (typeof u["prompt_tokens_details"] === "object" && u["prompt_tokens_details"] !== null) {
|
|
76517
|
-
const details = u["prompt_tokens_details"];
|
|
76518
|
-
if (typeof details["cached_tokens"] === "number") cached = details["cached_tokens"];
|
|
76519
|
-
}
|
|
76520
|
-
return {
|
|
76521
|
-
inputOther: promptTokens - cached,
|
|
76522
|
-
output: completionTokens,
|
|
76523
|
-
inputCacheRead: cached,
|
|
76524
|
-
inputCacheCreation: 0
|
|
76525
|
-
};
|
|
76526
|
-
}
|
|
76527
|
-
/**
|
|
76528
|
-
* Normalize an OpenAI Chat Completions–style `finish_reason` string to the
|
|
76529
|
-
* unified {@link FinishReason} enum.
|
|
76530
|
-
*
|
|
76531
|
-
* Used by both the Kimi and OpenAI Legacy adapters because they share the
|
|
76532
|
-
* Chat Completions wire format. Returns `{ finishReason: null,
|
|
76533
|
-
* rawFinishReason: null }` when the upstream value is missing or `null` so
|
|
76534
|
-
* callers can treat "no signal" uniformly.
|
|
76535
|
-
*
|
|
76536
|
-
* Mapping:
|
|
76537
|
-
* - `'stop'` → `'completed'`
|
|
76538
|
-
* - `'tool_calls'` → `'tool_calls'`
|
|
76539
|
-
* - `'function_call'` → `'tool_calls'` (legacy alias)
|
|
76540
|
-
* - `'length'` → `'truncated'`
|
|
76541
|
-
* - `'content_filter'` → `'filtered'`
|
|
76542
|
-
* - any other non-null string → `'other'`
|
|
76543
|
-
*/
|
|
76544
|
-
function normalizeOpenAIFinishReason(raw) {
|
|
76545
|
-
if (raw === null || raw === void 0) return {
|
|
76546
|
-
finishReason: null,
|
|
76547
|
-
rawFinishReason: null
|
|
76548
|
-
};
|
|
76549
|
-
switch (raw) {
|
|
76550
|
-
case "stop": return {
|
|
76551
|
-
finishReason: "completed",
|
|
76552
|
-
rawFinishReason: raw
|
|
76553
|
-
};
|
|
76554
|
-
case "tool_calls":
|
|
76555
|
-
case "function_call": return {
|
|
76556
|
-
finishReason: "tool_calls",
|
|
76557
|
-
rawFinishReason: raw
|
|
76558
|
-
};
|
|
76559
|
-
case "length": return {
|
|
76560
|
-
finishReason: "truncated",
|
|
76561
|
-
rawFinishReason: raw
|
|
76562
|
-
};
|
|
76563
|
-
case "content_filter": return {
|
|
76564
|
-
finishReason: "filtered",
|
|
76565
|
-
rawFinishReason: raw
|
|
76566
|
-
};
|
|
76567
|
-
default: return {
|
|
76568
|
-
finishReason: "other",
|
|
76569
|
-
rawFinishReason: raw
|
|
76570
|
-
};
|
|
76571
|
-
}
|
|
76572
|
-
}
|
|
76573
|
-
/**
|
|
76574
|
-
* Convert tool-role message content according to the chosen strategy.
|
|
76575
|
-
*/
|
|
76576
|
-
function convertToolMessageContent(message, conversion) {
|
|
76577
|
-
if (conversion === "extract_text") return extractText(message);
|
|
76578
|
-
return message.content.map((p) => convertContentPart(p)).filter((p) => p !== null);
|
|
76579
|
-
}
|
|
76580
|
-
//#endregion
|
|
76581
|
-
//#region ../../packages/kosong/dist/providers/kimi.mjs
|
|
76554
|
+
//#region ../../packages/kosong/src/providers/kimi-files.ts
|
|
76582
76555
|
/**
|
|
76583
76556
|
* Kimi-specific file upload client.
|
|
76584
76557
|
*
|
|
@@ -76668,6 +76641,8 @@ function guessMimeTypeFromExt(filename) {
|
|
|
76668
76641
|
if (dot < 0) return void 0;
|
|
76669
76642
|
return EXT_TO_MIME[filename.slice(dot + 1).toLowerCase()];
|
|
76670
76643
|
}
|
|
76644
|
+
//#endregion
|
|
76645
|
+
//#region ../../packages/kosong/src/providers/kimi.ts
|
|
76671
76646
|
function convertStreamToolCall$1(toolCall, bufferedByIndex) {
|
|
76672
76647
|
if (!toolCall.function) return [];
|
|
76673
76648
|
const streamIndex = toolCall.index;
|
|
@@ -77007,7 +76982,7 @@ var KimiChatProvider = class {
|
|
|
77007
76982
|
}
|
|
77008
76983
|
};
|
|
77009
76984
|
//#endregion
|
|
77010
|
-
//#region ../../packages/kosong/
|
|
76985
|
+
//#region ../../packages/kosong/src/providers/openai-legacy.ts
|
|
77011
76986
|
function convertStreamToolCall(toolCall, bufferedByIndex) {
|
|
77012
76987
|
if (!toolCall.function) return [];
|
|
77013
76988
|
const streamIndex = toolCall.index;
|
|
@@ -77311,7 +77286,7 @@ var OpenAILegacyChatProvider = class {
|
|
|
77311
77286
|
}
|
|
77312
77287
|
};
|
|
77313
77288
|
//#endregion
|
|
77314
|
-
//#region ../../packages/kosong/
|
|
77289
|
+
//#region ../../packages/kosong/src/providers/openai-responses.ts
|
|
77315
77290
|
/**
|
|
77316
77291
|
* Normalize the Responses API status / incomplete_details into the unified
|
|
77317
77292
|
* {@link FinishReason} enum.
|
|
@@ -78125,7 +78100,7 @@ var DeferredOAuthChatProvider = class DeferredOAuthChatProvider {
|
|
|
78125
78100
|
return createProvider(this.options.providerName, {
|
|
78126
78101
|
...this.options.providerConfig,
|
|
78127
78102
|
apiKey: "__oauth_token_placeholder__"
|
|
78128
|
-
}, this.options.modelOverride, this.options.defaultHeaders).getCapability?.(model) ?? UNKNOWN_CAPABILITY
|
|
78103
|
+
}, this.options.modelOverride, this.options.defaultHeaders).getCapability?.(model) ?? UNKNOWN_CAPABILITY;
|
|
78129
78104
|
}
|
|
78130
78105
|
async materializeProvider() {
|
|
78131
78106
|
const accessToken = await this.options.oauthResolver(this.options.providerName);
|
|
@@ -94703,8 +94678,7 @@ async function startCoreWireServer(options) {
|
|
|
94703
94678
|
},
|
|
94704
94679
|
modelsProvider: options.modelsProvider,
|
|
94705
94680
|
configProvider: options.configProvider,
|
|
94706
|
-
logger: options.logger
|
|
94707
|
-
skillManager: options.skillManager
|
|
94681
|
+
logger: options.logger
|
|
94708
94682
|
}).getEventFilter;
|
|
94709
94683
|
options.transport.onMessage = (frame) => {
|
|
94710
94684
|
(async () => {
|
|
@@ -94900,12 +94874,6 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
94900
94874
|
get defaultPlanMode() {
|
|
94901
94875
|
return effectiveConfig.planMode ?? effectiveConfig.defaultPlanMode ?? false;
|
|
94902
94876
|
},
|
|
94903
|
-
get theme() {
|
|
94904
|
-
return effectiveConfig.theme === "light" ? "light" : "dark";
|
|
94905
|
-
},
|
|
94906
|
-
get defaultEditor() {
|
|
94907
|
-
return effectiveConfig.defaultEditor ?? "";
|
|
94908
|
-
},
|
|
94909
94877
|
get availableModels() {
|
|
94910
94878
|
return effectiveConfig.models ?? {};
|
|
94911
94879
|
},
|
|
@@ -95106,6 +95074,90 @@ async function createKimiAgent() {
|
|
|
95106
95074
|
};
|
|
95107
95075
|
}
|
|
95108
95076
|
//#endregion
|
|
95077
|
+
//#region src/tui/config.ts
|
|
95078
|
+
/**
|
|
95079
|
+
* TUI-owned configuration.
|
|
95080
|
+
*
|
|
95081
|
+
* Agent/runtime settings live in core's `config.toml`; this file owns only
|
|
95082
|
+
* terminal UI preferences for the kimi-code client.
|
|
95083
|
+
*/
|
|
95084
|
+
const INVALID_TUI_CONFIG_MESSAGE = "Invalid TUI config in ~/.kimi-code/tui.toml; using defaults.";
|
|
95085
|
+
const TuiThemeSchema = z.enum([
|
|
95086
|
+
"dark",
|
|
95087
|
+
"light",
|
|
95088
|
+
"auto"
|
|
95089
|
+
]);
|
|
95090
|
+
const TuiConfigFileSchema = z.object({
|
|
95091
|
+
theme: TuiThemeSchema.optional(),
|
|
95092
|
+
editor: z.object({ command: z.string().optional() }).optional()
|
|
95093
|
+
});
|
|
95094
|
+
const TuiConfigSchema = z.object({
|
|
95095
|
+
theme: TuiThemeSchema,
|
|
95096
|
+
editorCommand: z.string().nullable()
|
|
95097
|
+
});
|
|
95098
|
+
const DEFAULT_TUI_CONFIG = TuiConfigSchema.parse({
|
|
95099
|
+
theme: "auto",
|
|
95100
|
+
editorCommand: null
|
|
95101
|
+
});
|
|
95102
|
+
/**
|
|
95103
|
+
* Thrown by `loadTuiConfig` when the on-disk TOML cannot be parsed.
|
|
95104
|
+
* Carries `fallback` so the caller can recover without re-running the
|
|
95105
|
+
* I/O, and use `message` (== `INVALID_TUI_CONFIG_MESSAGE`) as a
|
|
95106
|
+
* user-facing notice.
|
|
95107
|
+
*/
|
|
95108
|
+
var TuiConfigParseError = class extends Error {
|
|
95109
|
+
name = "TuiConfigParseError";
|
|
95110
|
+
fallback;
|
|
95111
|
+
constructor(fallback) {
|
|
95112
|
+
super(INVALID_TUI_CONFIG_MESSAGE);
|
|
95113
|
+
this.fallback = fallback;
|
|
95114
|
+
}
|
|
95115
|
+
};
|
|
95116
|
+
function getTuiConfigPath() {
|
|
95117
|
+
return join(getDataDir(), "tui.toml");
|
|
95118
|
+
}
|
|
95119
|
+
async function loadTuiConfig(filePath = getTuiConfigPath()) {
|
|
95120
|
+
if (!existsSync(filePath)) {
|
|
95121
|
+
await saveTuiConfig(DEFAULT_TUI_CONFIG, filePath);
|
|
95122
|
+
return DEFAULT_TUI_CONFIG;
|
|
95123
|
+
}
|
|
95124
|
+
try {
|
|
95125
|
+
return parseTuiConfig(await readFile(filePath, "utf-8"));
|
|
95126
|
+
} catch {
|
|
95127
|
+
throw new TuiConfigParseError(DEFAULT_TUI_CONFIG);
|
|
95128
|
+
}
|
|
95129
|
+
}
|
|
95130
|
+
function parseTuiConfig(tomlText) {
|
|
95131
|
+
if (tomlText.trim().length === 0) return DEFAULT_TUI_CONFIG;
|
|
95132
|
+
const raw = parse$1(tomlText);
|
|
95133
|
+
return normalizeTuiConfig(TuiConfigFileSchema.parse(raw));
|
|
95134
|
+
}
|
|
95135
|
+
async function saveTuiConfig(config, filePath = getTuiConfigPath()) {
|
|
95136
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
95137
|
+
await writeFile(filePath, renderTuiConfig(config), "utf-8");
|
|
95138
|
+
}
|
|
95139
|
+
function normalizeTuiConfig(config) {
|
|
95140
|
+
const command = config.editor?.command?.trim();
|
|
95141
|
+
return TuiConfigSchema.parse({
|
|
95142
|
+
theme: config.theme ?? DEFAULT_TUI_CONFIG.theme,
|
|
95143
|
+
editorCommand: command === void 0 || command.length === 0 ? null : command
|
|
95144
|
+
});
|
|
95145
|
+
}
|
|
95146
|
+
function renderTuiConfig(config) {
|
|
95147
|
+
return `# ~/.kimi-code/tui.toml
|
|
95148
|
+
# Terminal UI preferences for kimi-code.
|
|
95149
|
+
# Agent/runtime settings stay in ~/.kimi-code/config.toml.
|
|
95150
|
+
|
|
95151
|
+
theme = "${config.theme}" # "auto" | "dark" | "light"
|
|
95152
|
+
|
|
95153
|
+
[editor]
|
|
95154
|
+
command = "${escapeTomlBasicString(config.editorCommand ?? "")}" # Empty uses $VISUAL / $EDITOR
|
|
95155
|
+
`;
|
|
95156
|
+
}
|
|
95157
|
+
function escapeTomlBasicString(value) {
|
|
95158
|
+
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\b", "\\b").replaceAll(" ", "\\t").replaceAll("\n", "\\n").replaceAll("\f", "\\f").replaceAll("\r", "\\r");
|
|
95159
|
+
}
|
|
95160
|
+
//#endregion
|
|
95109
95161
|
//#region src/utils/persistence.ts
|
|
95110
95162
|
/**
|
|
95111
95163
|
* Small persistence helpers for CLI-owned data files.
|
|
@@ -95269,7 +95321,7 @@ var ImageThumbnail = class extends Container {
|
|
|
95269
95321
|
super();
|
|
95270
95322
|
const caps = getCapabilities();
|
|
95271
95323
|
if (!(caps.images === "kitty" || caps.images === "iterm2")) {
|
|
95272
|
-
this.addChild(new Text(chalk.
|
|
95324
|
+
this.addChild(new Text(chalk.hex(colors.accent)(` ${attachment.placeholder}`), 0, 0));
|
|
95273
95325
|
return;
|
|
95274
95326
|
}
|
|
95275
95327
|
const image = new Image(Buffer.from(attachment.bytes).toString("base64"), attachment.mime, { fallbackColor: (s) => chalk.hex(colors.textDim)(s) }, {
|
|
@@ -95289,10 +95341,12 @@ const INDENT$1 = " ";
|
|
|
95289
95341
|
var AssistantMessageComponent = class {
|
|
95290
95342
|
contentContainer;
|
|
95291
95343
|
markdownTheme;
|
|
95344
|
+
bulletColor;
|
|
95292
95345
|
lastText = "";
|
|
95293
95346
|
showBullet;
|
|
95294
|
-
constructor(markdownTheme, showBullet = true) {
|
|
95347
|
+
constructor(markdownTheme, colors, showBullet = true) {
|
|
95295
95348
|
this.markdownTheme = markdownTheme;
|
|
95349
|
+
this.bulletColor = colors.roleAssistant;
|
|
95296
95350
|
this.showBullet = showBullet;
|
|
95297
95351
|
this.contentContainer = new Container();
|
|
95298
95352
|
}
|
|
@@ -95315,7 +95369,7 @@ var AssistantMessageComponent = class {
|
|
|
95315
95369
|
const contentLines = this.contentContainer.render(contentWidth);
|
|
95316
95370
|
const lines = [""];
|
|
95317
95371
|
for (let i = 0; i < contentLines.length; i++) {
|
|
95318
|
-
const p = i === 0 && this.showBullet ? chalk.
|
|
95372
|
+
const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(BULLET$1) : INDENT$1;
|
|
95319
95373
|
lines.push(p + contentLines[i]);
|
|
95320
95374
|
}
|
|
95321
95375
|
return lines;
|
|
@@ -95343,7 +95397,7 @@ var SkillActivationComponent = class extends Container {
|
|
|
95343
95397
|
constructor(name, args, colors) {
|
|
95344
95398
|
super();
|
|
95345
95399
|
this.addChild(new Spacer(1));
|
|
95346
|
-
const head = chalk.hex(colors.primary).bold("▶ Activated skill: ") + chalk.hex(colors.
|
|
95400
|
+
const head = chalk.hex(colors.primary).bold("▶ Activated skill: ") + chalk.hex(colors.roleUser).bold(name);
|
|
95347
95401
|
this.addChild(new Text(head, 0, 0));
|
|
95348
95402
|
const trimmed = args?.trim() ?? "";
|
|
95349
95403
|
if (trimmed.length > 0) {
|
|
@@ -95381,7 +95435,7 @@ var ThinkingComponent = class {
|
|
|
95381
95435
|
spinnerInterval;
|
|
95382
95436
|
constructor(text, colors, showMarker = true, mode = "finalized", ui) {
|
|
95383
95437
|
this.text = text;
|
|
95384
|
-
this.color = colors.
|
|
95438
|
+
this.color = colors.roleThinking;
|
|
95385
95439
|
this.showMarker = showMarker;
|
|
95386
95440
|
this.mode = mode;
|
|
95387
95441
|
this.ui = ui;
|
|
@@ -95494,6 +95548,16 @@ function highlightLines(code, lang) {
|
|
|
95494
95548
|
* Reuses the diff algorithm from approval/DiffPreview.tsx, but outputs
|
|
95495
95549
|
* formatted text lines instead of React elements.
|
|
95496
95550
|
*/
|
|
95551
|
+
function makeDiffStyles(colors) {
|
|
95552
|
+
return {
|
|
95553
|
+
add: (s) => chalk.hex(colors.diffAdded)(s),
|
|
95554
|
+
del: (s) => chalk.hex(colors.diffRemoved)(s),
|
|
95555
|
+
addBold: (s) => chalk.bold.hex(colors.diffAddedStrong)(s),
|
|
95556
|
+
delBold: (s) => chalk.bold.hex(colors.diffRemovedStrong)(s),
|
|
95557
|
+
gutter: (s) => chalk.hex(colors.diffGutter)(s),
|
|
95558
|
+
meta: (s) => chalk.hex(colors.diffMeta)(s)
|
|
95559
|
+
};
|
|
95560
|
+
}
|
|
95497
95561
|
function computeDiffLines(oldText, newText, oldStart = 1, newStart = 1, isIncomplete = false) {
|
|
95498
95562
|
const oldLines = oldText ? oldText.split("\n") : [];
|
|
95499
95563
|
const newLines = newText ? newText.split("\n") : [];
|
|
@@ -95582,10 +95646,10 @@ function buildClusters(diffLines, contextLines) {
|
|
|
95582
95646
|
removedCount: removed
|
|
95583
95647
|
};
|
|
95584
95648
|
}
|
|
95585
|
-
function formatDiffRow(line) {
|
|
95586
|
-
const gutter =
|
|
95587
|
-
if (line.kind === "add") return gutter +
|
|
95588
|
-
if (line.kind === "delete") return gutter +
|
|
95649
|
+
function formatDiffRow(line, s) {
|
|
95650
|
+
const gutter = s.gutter(String(line.lineNum).padStart(4) + " ");
|
|
95651
|
+
if (line.kind === "add") return gutter + s.add("+ " + line.code);
|
|
95652
|
+
if (line.kind === "delete") return gutter + s.del("- " + line.code);
|
|
95589
95653
|
return gutter + " " + line.code;
|
|
95590
95654
|
}
|
|
95591
95655
|
/**
|
|
@@ -95597,15 +95661,16 @@ function formatDiffRow(line) {
|
|
|
95597
95661
|
* Used by Edit's call preview where we want to show *what changed*
|
|
95598
95662
|
* with enough context to read the change, but not the whole file.
|
|
95599
95663
|
*/
|
|
95600
|
-
function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
|
|
95664
|
+
function renderDiffLinesClustered(oldText, newText, path, colors, opts = {}) {
|
|
95665
|
+
const s = makeDiffStyles(colors);
|
|
95601
95666
|
const contextLines = opts.contextLines ?? 3;
|
|
95602
95667
|
const maxLines = opts.maxLines;
|
|
95603
95668
|
const diffLines = computeDiffLines(oldText, newText, 1, 1, opts.isIncomplete ?? false);
|
|
95604
95669
|
const { clusters, changedCount, addedCount, removedCount } = buildClusters(diffLines, contextLines);
|
|
95605
95670
|
const output = [];
|
|
95606
95671
|
let header = "";
|
|
95607
|
-
if (addedCount > 0) header +=
|
|
95608
|
-
if (removedCount > 0) header +=
|
|
95672
|
+
if (addedCount > 0) header += s.addBold(`+${String(addedCount)} `);
|
|
95673
|
+
if (removedCount > 0) header += s.delBold(`-${String(removedCount)} `);
|
|
95609
95674
|
header += path;
|
|
95610
95675
|
output.push(header);
|
|
95611
95676
|
if (clusters.length === 0) return output;
|
|
@@ -95626,7 +95691,7 @@ function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
|
|
|
95626
95691
|
truncated = true;
|
|
95627
95692
|
break;
|
|
95628
95693
|
}
|
|
95629
|
-
output.push(
|
|
95694
|
+
output.push(s.meta(` … ${String(gap)} unchanged line${gap > 1 ? "s" : ""} …`));
|
|
95630
95695
|
body++;
|
|
95631
95696
|
}
|
|
95632
95697
|
}
|
|
@@ -95636,7 +95701,7 @@ function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
|
|
|
95636
95701
|
break outer;
|
|
95637
95702
|
}
|
|
95638
95703
|
const line = diffLines[i];
|
|
95639
|
-
output.push(formatDiffRow(line));
|
|
95704
|
+
output.push(formatDiffRow(line, s));
|
|
95640
95705
|
body++;
|
|
95641
95706
|
if (line.kind !== "context") shownChanges++;
|
|
95642
95707
|
prevEnd = i;
|
|
@@ -95644,7 +95709,7 @@ function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
|
|
|
95644
95709
|
}
|
|
95645
95710
|
if (truncated) {
|
|
95646
95711
|
const hidden = changedCount - shownChanges;
|
|
95647
|
-
if (hidden > 0) output.push(
|
|
95712
|
+
if (hidden > 0) output.push(s.meta(` … ${String(hidden)} more change${hidden > 1 ? "s" : ""} hidden (ctrl+o to expand)`));
|
|
95648
95713
|
}
|
|
95649
95714
|
return output;
|
|
95650
95715
|
}
|
|
@@ -95652,12 +95717,16 @@ function renderDiffLinesClustered(oldText, newText, path, opts = {}) {
|
|
|
95652
95717
|
//#region src/tui/components/panels/plan-box.ts
|
|
95653
95718
|
const LEFT_MARGIN$1 = 2;
|
|
95654
95719
|
const SIDE_PADDING$1 = 1;
|
|
95720
|
+
const TITLE_PREFIX = " plan: ";
|
|
95721
|
+
const TITLE_SUFFIX = " ";
|
|
95722
|
+
const ELLIPSIS_PREFIX = "…/";
|
|
95655
95723
|
var PlanBoxComponent = class {
|
|
95656
95724
|
markdown;
|
|
95657
95725
|
cachedWidth;
|
|
95658
95726
|
cachedLines;
|
|
95659
|
-
constructor(plan, markdownTheme, borderHex) {
|
|
95727
|
+
constructor(plan, markdownTheme, borderHex, planPath) {
|
|
95660
95728
|
this.borderHex = borderHex;
|
|
95729
|
+
this.planPath = planPath;
|
|
95661
95730
|
this.markdown = new Markdown(plan.trim(), 0, 0, markdownTheme);
|
|
95662
95731
|
}
|
|
95663
95732
|
invalidate() {
|
|
@@ -95671,8 +95740,8 @@ var PlanBoxComponent = class {
|
|
|
95671
95740
|
const contentWidth = Math.max(1, horzLen - 2 * SIDE_PADDING$1);
|
|
95672
95741
|
const paint = (s) => chalk.hex(this.borderHex)(s);
|
|
95673
95742
|
const indent = " ".repeat(LEFT_MARGIN$1);
|
|
95674
|
-
const title =
|
|
95675
|
-
const trailingDashLen = Math.max(0, horzLen -
|
|
95743
|
+
const title = this.buildTitle(horzLen);
|
|
95744
|
+
const trailingDashLen = Math.max(0, horzLen - title.length);
|
|
95676
95745
|
const top = indent + paint("┌") + paint(title) + paint("─".repeat(trailingDashLen)) + paint("┐");
|
|
95677
95746
|
const bottom = indent + paint("└" + "─".repeat(horzLen) + "┘");
|
|
95678
95747
|
const rawLines = this.markdown.render(contentWidth);
|
|
@@ -95686,7 +95755,28 @@ var PlanBoxComponent = class {
|
|
|
95686
95755
|
this.cachedLines = lines;
|
|
95687
95756
|
return lines;
|
|
95688
95757
|
}
|
|
95758
|
+
buildTitle(horzLen) {
|
|
95759
|
+
const fallback = " plan ";
|
|
95760
|
+
const path = this.planPath;
|
|
95761
|
+
if (path === void 0 || path.length === 0) return fallback;
|
|
95762
|
+
const pathBudget = horzLen - 1 - 8;
|
|
95763
|
+
if (pathBudget < (path.split("/").pop() ?? path).length) return fallback;
|
|
95764
|
+
return TITLE_PREFIX + (path.length <= pathBudget ? path : truncatePathHead(path, pathBudget)) + TITLE_SUFFIX;
|
|
95765
|
+
}
|
|
95689
95766
|
};
|
|
95767
|
+
function truncatePathHead(path, budget) {
|
|
95768
|
+
if (budget <= 2) return path.slice(-budget);
|
|
95769
|
+
const segments = path.split("/");
|
|
95770
|
+
let acc = segments.at(-1) ?? "";
|
|
95771
|
+
for (let i = segments.length - 2; i >= 0; i -= 1) {
|
|
95772
|
+
const candidate = (segments[i] ?? "") + "/" + acc;
|
|
95773
|
+
if (2 + candidate.length > budget) break;
|
|
95774
|
+
acc = candidate;
|
|
95775
|
+
}
|
|
95776
|
+
if (acc.length + 2 <= budget) return ELLIPSIS_PREFIX + acc;
|
|
95777
|
+
const tailBudget = budget - 2;
|
|
95778
|
+
return ELLIPSIS_PREFIX + acc.slice(-tailBudget);
|
|
95779
|
+
}
|
|
95690
95780
|
//#endregion
|
|
95691
95781
|
//#region src/tui/components/messages/tool-renderers/types.ts
|
|
95692
95782
|
function strArg(args, ...keys) {
|
|
@@ -95697,6 +95787,121 @@ function strArg(args, ...keys) {
|
|
|
95697
95787
|
return "";
|
|
95698
95788
|
}
|
|
95699
95789
|
//#endregion
|
|
95790
|
+
//#region src/tui/components/messages/tool-renderers/truncated.ts
|
|
95791
|
+
const renderTruncated = (_toolCall, result, ctx) => {
|
|
95792
|
+
if (!result.output) return [];
|
|
95793
|
+
const tint = result.is_error ? chalk.hex(ctx.colors.error) : chalk.dim;
|
|
95794
|
+
const lines = result.output.split("\n");
|
|
95795
|
+
if (ctx.expanded) return [new Text(tint(result.output), 2, 0)];
|
|
95796
|
+
const shown = lines.slice(0, 3);
|
|
95797
|
+
const remaining = lines.length - shown.length;
|
|
95798
|
+
const out = [new Text(tint(shown.join("\n")), 2, 0)];
|
|
95799
|
+
if (remaining > 0) out.push(new Text(chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`), 2, 0));
|
|
95800
|
+
return out;
|
|
95801
|
+
};
|
|
95802
|
+
//#endregion
|
|
95803
|
+
//#region src/tui/components/messages/tool-renderers/media.ts
|
|
95804
|
+
const PATH_TAG_RE = /^<(image|video)\s+path="([^"]+)">$/;
|
|
95805
|
+
const ORIGINAL_SIZE_RE = /original size\s+(\d+x\d+px)/;
|
|
95806
|
+
const DATA_URL_RE = /^data:([^;]+);base64,(.*)$/s;
|
|
95807
|
+
function bytesFromBase64(b64) {
|
|
95808
|
+
const len = b64.length;
|
|
95809
|
+
if (len === 0) return 0;
|
|
95810
|
+
const padding = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
|
|
95811
|
+
return Math.floor(len * 3 / 4) - padding;
|
|
95812
|
+
}
|
|
95813
|
+
function parseReadMediaOutput(output) {
|
|
95814
|
+
let parsed;
|
|
95815
|
+
try {
|
|
95816
|
+
parsed = JSON.parse(output);
|
|
95817
|
+
} catch {
|
|
95818
|
+
return null;
|
|
95819
|
+
}
|
|
95820
|
+
if (!Array.isArray(parsed)) return null;
|
|
95821
|
+
let kind;
|
|
95822
|
+
let path;
|
|
95823
|
+
let mimeType;
|
|
95824
|
+
let bytes;
|
|
95825
|
+
let url;
|
|
95826
|
+
let originalSize;
|
|
95827
|
+
let foundMedia = false;
|
|
95828
|
+
for (const raw of parsed) {
|
|
95829
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
95830
|
+
const part = raw;
|
|
95831
|
+
const type = part["type"];
|
|
95832
|
+
if (type === "text" && typeof part["text"] === "string") {
|
|
95833
|
+
const text = part["text"];
|
|
95834
|
+
const tag = PATH_TAG_RE.exec(text);
|
|
95835
|
+
if (tag) {
|
|
95836
|
+
kind = tag[1];
|
|
95837
|
+
path = tag[2];
|
|
95838
|
+
continue;
|
|
95839
|
+
}
|
|
95840
|
+
const size = ORIGINAL_SIZE_RE.exec(text);
|
|
95841
|
+
if (size) originalSize = size[1];
|
|
95842
|
+
continue;
|
|
95843
|
+
}
|
|
95844
|
+
if (type === "image_url" || type === "video_url") {
|
|
95845
|
+
foundMedia = true;
|
|
95846
|
+
kind = type === "image_url" ? "image" : "video";
|
|
95847
|
+
const holder = part[type === "image_url" ? "imageUrl" : "videoUrl"];
|
|
95848
|
+
if (typeof holder === "object" && holder !== null) {
|
|
95849
|
+
const u = holder["url"];
|
|
95850
|
+
if (typeof u === "string") {
|
|
95851
|
+
const data = DATA_URL_RE.exec(u);
|
|
95852
|
+
if (data && data[1] !== void 0 && data[2] !== void 0) {
|
|
95853
|
+
mimeType = data[1];
|
|
95854
|
+
bytes = bytesFromBase64(data[2]);
|
|
95855
|
+
} else url = u;
|
|
95856
|
+
}
|
|
95857
|
+
}
|
|
95858
|
+
}
|
|
95859
|
+
}
|
|
95860
|
+
if (!foundMedia || kind === void 0) return null;
|
|
95861
|
+
const summary = { kind };
|
|
95862
|
+
if (path !== void 0) summary.path = path;
|
|
95863
|
+
if (mimeType !== void 0) summary.mimeType = mimeType;
|
|
95864
|
+
if (bytes !== void 0) summary.bytes = bytes;
|
|
95865
|
+
if (url !== void 0) summary.url = url;
|
|
95866
|
+
if (originalSize !== void 0) summary.originalSize = originalSize;
|
|
95867
|
+
return summary;
|
|
95868
|
+
}
|
|
95869
|
+
function formatBytes$1(bytes) {
|
|
95870
|
+
if (bytes < 1024) return `${String(bytes)} B`;
|
|
95871
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
95872
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
95873
|
+
}
|
|
95874
|
+
function metaSegments(summary) {
|
|
95875
|
+
const segs = [];
|
|
95876
|
+
if (summary.mimeType !== void 0) segs.push(summary.mimeType);
|
|
95877
|
+
if (summary.bytes !== void 0) segs.push(formatBytes$1(summary.bytes));
|
|
95878
|
+
if (summary.originalSize !== void 0) segs.push(summary.originalSize);
|
|
95879
|
+
return segs;
|
|
95880
|
+
}
|
|
95881
|
+
const readMediaChip = (_toolCall, result) => {
|
|
95882
|
+
if (result.is_error) return "";
|
|
95883
|
+
const summary = parseReadMediaOutput(result.output);
|
|
95884
|
+
if (summary === null) return "";
|
|
95885
|
+
const meta = metaSegments(summary);
|
|
95886
|
+
if (meta.length === 0) return summary.url !== void 0 ? `${summary.kind} · uploaded` : summary.kind;
|
|
95887
|
+
return `${summary.kind} (${meta.join(", ")})`;
|
|
95888
|
+
};
|
|
95889
|
+
const readMediaSummary = (toolCall, result, ctx) => {
|
|
95890
|
+
if (result.is_error) return renderTruncated(toolCall, result, ctx);
|
|
95891
|
+
const summary = parseReadMediaOutput(result.output);
|
|
95892
|
+
if (summary === null) return renderTruncated(toolCall, result, ctx);
|
|
95893
|
+
if (!ctx.expanded) return [];
|
|
95894
|
+
const dim = chalk.dim;
|
|
95895
|
+
const out = [];
|
|
95896
|
+
if (summary.path !== void 0) out.push(new Text(` ${dim(summary.path)}`, 0, 0));
|
|
95897
|
+
const meta = metaSegments(summary);
|
|
95898
|
+
const tail = [summary.kind];
|
|
95899
|
+
if (meta.length > 0) tail.push(meta.join(", "));
|
|
95900
|
+
if (summary.url !== void 0) tail.push(summary.url);
|
|
95901
|
+
out.push(new Text(` ${dim(tail.join(" · "))}`, 0, 0));
|
|
95902
|
+
return out;
|
|
95903
|
+
};
|
|
95904
|
+
//#endregion
|
|
95700
95905
|
//#region src/tui/components/messages/tool-renderers/chip.ts
|
|
95701
95906
|
function countNonEmptyLines(text) {
|
|
95702
95907
|
if (text.length === 0) return 0;
|
|
@@ -95772,6 +95977,7 @@ const REGISTRY = {
|
|
|
95772
95977
|
Edit: editChip,
|
|
95773
95978
|
Write: writeChip,
|
|
95774
95979
|
Read: readChip,
|
|
95980
|
+
ReadMediaFile: readMediaChip,
|
|
95775
95981
|
Grep: grepChip,
|
|
95776
95982
|
Glob: globChip,
|
|
95777
95983
|
FetchURL: fetchChip,
|
|
@@ -95781,19 +95987,6 @@ function pickChip(toolName) {
|
|
|
95781
95987
|
return REGISTRY[toolName];
|
|
95782
95988
|
}
|
|
95783
95989
|
//#endregion
|
|
95784
|
-
//#region src/tui/components/messages/tool-renderers/truncated.ts
|
|
95785
|
-
const renderTruncated = (_toolCall, result, ctx) => {
|
|
95786
|
-
if (!result.output) return [];
|
|
95787
|
-
const tint = result.is_error ? chalk.hex(ctx.colors.error) : chalk.dim;
|
|
95788
|
-
const lines = result.output.split("\n");
|
|
95789
|
-
if (ctx.expanded) return [new Text(tint(result.output), 2, 0)];
|
|
95790
|
-
const shown = lines.slice(0, 3);
|
|
95791
|
-
const remaining = lines.length - shown.length;
|
|
95792
|
-
const out = [new Text(tint(shown.join("\n")), 2, 0)];
|
|
95793
|
-
if (remaining > 0) out.push(new Text(chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`), 2, 0));
|
|
95794
|
-
return out;
|
|
95795
|
-
};
|
|
95796
|
-
//#endregion
|
|
95797
95990
|
//#region src/tui/components/messages/tool-renderers/summary.ts
|
|
95798
95991
|
const GLANCE_SAMPLES = 3;
|
|
95799
95992
|
function withGlance(glance) {
|
|
@@ -95859,6 +96052,7 @@ const globSummary = withGlance(globGlance);
|
|
|
95859
96052
|
function pickResultRenderer(toolName) {
|
|
95860
96053
|
switch (toolName) {
|
|
95861
96054
|
case "Read": return readSummary;
|
|
96055
|
+
case "ReadMediaFile": return readMediaSummary;
|
|
95862
96056
|
case "Grep": return grepSummary;
|
|
95863
96057
|
case "Glob": return globSummary;
|
|
95864
96058
|
case "FetchURL": return fetchSummary;
|
|
@@ -95887,6 +96081,42 @@ function extractApprovedPlan(output) {
|
|
|
95887
96081
|
if (markerIndex < 0) return "";
|
|
95888
96082
|
return output.slice(markerIndex + 17).trim();
|
|
95889
96083
|
}
|
|
96084
|
+
const REJECT_PREFIX = "User rejected the plan.";
|
|
96085
|
+
const REJECT_FEEDBACK_PREFIX = "User rejected the plan. Feedback:";
|
|
96086
|
+
const APPROVED_OPTION_RE = /^User approved option "([^"]+)"\./;
|
|
96087
|
+
const PLAN_SAVED_TO_RE = /\nPlan saved to: ([^\n]+)\n/;
|
|
96088
|
+
/**
|
|
96089
|
+
* 解析 ExitPlanMode 工具结果的 content 字符串,识别审批结局 + plan 路径。
|
|
96090
|
+
* core 侧字符串模板见 `packages/kimi-core/src/tools/builtin/planning/exit-plan-mode.ts`:
|
|
96091
|
+
* - 通过:以 'Exited plan mode.' 或 'User approved option "<label>".' 开头,
|
|
96092
|
+
* plan-file 模式还会附带一行 'Plan saved to: <path>'。
|
|
96093
|
+
* - 拒绝:以 'User rejected the plan.' 开头;带 feedback 的形式是
|
|
96094
|
+
* 'User rejected the plan. Feedback:\n\n<text>'。
|
|
96095
|
+
* 这是字符串协议而非结构化字段,未来若 core 把结局结构化进 wire 层应优先切过去。
|
|
96096
|
+
*/
|
|
96097
|
+
function interpretExitPlanModeOutcome(output) {
|
|
96098
|
+
if (output.startsWith(REJECT_PREFIX)) {
|
|
96099
|
+
if (output.startsWith(REJECT_FEEDBACK_PREFIX)) return {
|
|
96100
|
+
kind: "rejected",
|
|
96101
|
+
feedback: output.slice(33).trimStart()
|
|
96102
|
+
};
|
|
96103
|
+
return { kind: "rejected" };
|
|
96104
|
+
}
|
|
96105
|
+
const path = PLAN_SAVED_TO_RE.exec(output)?.[1]?.trim();
|
|
96106
|
+
const optionMatch = APPROVED_OPTION_RE.exec(output);
|
|
96107
|
+
if (optionMatch !== null) return path !== void 0 && path.length > 0 ? {
|
|
96108
|
+
kind: "approved",
|
|
96109
|
+
chosen: optionMatch[1],
|
|
96110
|
+
path
|
|
96111
|
+
} : {
|
|
96112
|
+
kind: "approved",
|
|
96113
|
+
chosen: optionMatch[1]
|
|
96114
|
+
};
|
|
96115
|
+
return path !== void 0 && path.length > 0 ? {
|
|
96116
|
+
kind: "approved",
|
|
96117
|
+
path
|
|
96118
|
+
} : { kind: "approved" };
|
|
96119
|
+
}
|
|
95890
96120
|
const STREAMING_FIELD_RE$1 = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
|
|
95891
96121
|
function unescapeJsonString$1(s) {
|
|
95892
96122
|
return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
|
|
@@ -95981,6 +96211,12 @@ function parseArgsPreview(value) {
|
|
|
95981
96211
|
}
|
|
95982
96212
|
return result;
|
|
95983
96213
|
}
|
|
96214
|
+
const PATH_KEYS = new Set(["path", "file_path"]);
|
|
96215
|
+
function truncateArgValue(key, value) {
|
|
96216
|
+
if (value.length <= MAX_ARG_LENGTH) return value;
|
|
96217
|
+
if (PATH_KEYS.has(key)) return "…" + value.slice(value.length - (MAX_ARG_LENGTH - 1));
|
|
96218
|
+
return value.slice(0, MAX_ARG_LENGTH - 3) + "...";
|
|
96219
|
+
}
|
|
95984
96220
|
function extractKeyArgument(toolName, args) {
|
|
95985
96221
|
const candidates = {
|
|
95986
96222
|
Bash: ["command"],
|
|
@@ -95995,10 +96231,7 @@ function extractKeyArgument(toolName, args) {
|
|
|
95995
96231
|
}[toolName] ?? Object.keys(args);
|
|
95996
96232
|
for (const key of candidates) {
|
|
95997
96233
|
const val = args[key];
|
|
95998
|
-
if (typeof val === "string" && val.length > 0)
|
|
95999
|
-
const firstLine = val.split("\n")[0] ?? val;
|
|
96000
|
-
return firstLine.length <= MAX_ARG_LENGTH ? firstLine : firstLine.slice(0, MAX_ARG_LENGTH - 3) + "...";
|
|
96001
|
-
}
|
|
96234
|
+
if (typeof val === "string" && val.length > 0) return truncateArgValue(key, val.split("\n")[0] ?? val);
|
|
96002
96235
|
}
|
|
96003
96236
|
return null;
|
|
96004
96237
|
}
|
|
@@ -96009,6 +96242,14 @@ var ToolCallComponent = class extends Container {
|
|
|
96009
96242
|
colors;
|
|
96010
96243
|
ui;
|
|
96011
96244
|
markdownTheme;
|
|
96245
|
+
planPath;
|
|
96246
|
+
/**
|
|
96247
|
+
* 退化用的 plan body:当 LLM 走 plan-file 模式、`args.plan` 为空时,
|
|
96248
|
+
* stream-ops 会通过 `setPlanInfo` 把从 `client.getPlan()` 拉到的内容
|
|
96249
|
+
* 喂进来,让 plan 框在审批等待阶段就能渲染(也避免拒绝/修订时
|
|
96250
|
+
* `## Approved Plan:` 标记缺失导致 plan body 永远不出现)。
|
|
96251
|
+
*/
|
|
96252
|
+
currentPlan;
|
|
96012
96253
|
headerText;
|
|
96013
96254
|
callPreviewEndIndex = 0;
|
|
96014
96255
|
subagentAgentId;
|
|
@@ -96053,6 +96294,26 @@ var ToolCallComponent = class extends Container {
|
|
|
96053
96294
|
this.rebuildBody();
|
|
96054
96295
|
this.ui?.requestRender();
|
|
96055
96296
|
}
|
|
96297
|
+
/**
|
|
96298
|
+
* 异步注入 plan body / path。仅 ExitPlanMode 工具卡使用:当 LLM 走
|
|
96299
|
+
* plan-file 模式时 `args.plan` 为空,stream-ops 会从 `client.getPlan()`
|
|
96300
|
+
* 拉到 plan 文本后调用本方法,触发 plan 框渲染。
|
|
96301
|
+
*/
|
|
96302
|
+
setPlanInfo(info) {
|
|
96303
|
+
if (this.toolCall.name !== "ExitPlanMode") return;
|
|
96304
|
+
let changed = false;
|
|
96305
|
+
if (info.plan !== void 0 && info.plan.length > 0 && this.currentPlan !== info.plan) {
|
|
96306
|
+
this.currentPlan = info.plan;
|
|
96307
|
+
changed = true;
|
|
96308
|
+
}
|
|
96309
|
+
if (info.path !== void 0 && info.path.length > 0 && this.planPath !== info.path) {
|
|
96310
|
+
this.planPath = info.path;
|
|
96311
|
+
changed = true;
|
|
96312
|
+
}
|
|
96313
|
+
if (!changed) return;
|
|
96314
|
+
this.rebuildBody();
|
|
96315
|
+
this.ui?.requestRender();
|
|
96316
|
+
}
|
|
96056
96317
|
applySubagentReplay(subagent) {
|
|
96057
96318
|
if (subagent === void 0) return;
|
|
96058
96319
|
this.subagentAgentId = subagent.id;
|
|
@@ -96135,8 +96396,17 @@ var ToolCallComponent = class extends Container {
|
|
|
96135
96396
|
const isError = result?.is_error ?? false;
|
|
96136
96397
|
let bullet;
|
|
96137
96398
|
if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)("⏺ ");
|
|
96138
|
-
else bullet = chalk.
|
|
96139
|
-
if (toolCall.name === "ExitPlanMode")
|
|
96399
|
+
else bullet = chalk.hex(colors.roleAssistant)("⏺ ");
|
|
96400
|
+
if (toolCall.name === "ExitPlanMode") {
|
|
96401
|
+
const label = chalk.hex(colors.primary).bold("Current plan");
|
|
96402
|
+
if (!isFinished || result === void 0 || result.is_error === true) return label;
|
|
96403
|
+
const outcome = interpretExitPlanModeOutcome(result.output);
|
|
96404
|
+
if (outcome.kind === "approved") {
|
|
96405
|
+
const chipText = outcome.chosen !== void 0 && outcome.chosen.length > 0 ? `Approved: ${outcome.chosen}` : "Approved";
|
|
96406
|
+
return `${label}${chalk.hex(colors.success)(` · ${chipText}`)}`;
|
|
96407
|
+
}
|
|
96408
|
+
return `${label}${chalk.hex(colors.error)(" · Rejected")}`;
|
|
96409
|
+
}
|
|
96140
96410
|
if (toolCall.name === "AskUserQuestion") {
|
|
96141
96411
|
const label = isFinished ? isError ? "Could not collect your input" : "Collected your answers" : "Waiting for your input";
|
|
96142
96412
|
const tone = isError ? chalk.hex(colors.error) : chalk.hex(colors.primary);
|
|
@@ -96226,7 +96496,7 @@ var ToolCallComponent = class extends Container {
|
|
|
96226
96496
|
const oldStr = str(this.toolCall.args["old_string"]);
|
|
96227
96497
|
const newStr = str(this.toolCall.args["new_string"]);
|
|
96228
96498
|
if (oldStr.length === 0 && newStr.length === 0) return;
|
|
96229
|
-
const lines = renderDiffLinesClustered(oldStr, newStr, str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"]), {
|
|
96499
|
+
const lines = renderDiffLinesClustered(oldStr, newStr, str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"]), this.colors, {
|
|
96230
96500
|
contextLines: 3,
|
|
96231
96501
|
...shouldCap ? { maxLines: CALL_PREVIEW_LINES } : {}
|
|
96232
96502
|
});
|
|
@@ -96259,7 +96529,7 @@ var ToolCallComponent = class extends Container {
|
|
|
96259
96529
|
const oldStr = extractPartialStringField(streamText, "old_string") ?? "";
|
|
96260
96530
|
const newStr = extractPartialStringField(streamText, "new_string") ?? "";
|
|
96261
96531
|
if (oldStr.length === 0 && newStr.length === 0) return;
|
|
96262
|
-
const lines = renderDiffLinesClustered(oldStr, newStr, extractPartialStringField(streamText, "file_path") ?? extractPartialStringField(streamText, "path") ?? "", {
|
|
96532
|
+
const lines = renderDiffLinesClustered(oldStr, newStr, extractPartialStringField(streamText, "file_path") ?? extractPartialStringField(streamText, "path") ?? "", this.colors, {
|
|
96263
96533
|
contextLines: 3,
|
|
96264
96534
|
isIncomplete: true
|
|
96265
96535
|
});
|
|
@@ -96274,26 +96544,44 @@ var ToolCallComponent = class extends Container {
|
|
|
96274
96544
|
const prefix = i === 0 ? "$ " : " ";
|
|
96275
96545
|
this.addChild(new Text(chalk.dim(prefix + line), 2, 0));
|
|
96276
96546
|
}
|
|
96277
|
-
return;
|
|
96278
96547
|
}
|
|
96279
96548
|
}
|
|
96280
96549
|
buildPlanPreview() {
|
|
96281
|
-
const plan =
|
|
96282
|
-
if (plan.length === 0
|
|
96283
|
-
|
|
96284
|
-
|
|
96285
|
-
renderPlanFromResult(result) {
|
|
96286
|
-
const plan = extractApprovedPlan(result.output);
|
|
96287
|
-
if (plan.length === 0) return false;
|
|
96288
|
-
if (this.markdownTheme !== void 0) this.addChild(new PlanBoxComponent(plan, this.markdownTheme, this.colors.success));
|
|
96550
|
+
const plan = this.resolvePlanForPreview();
|
|
96551
|
+
if (plan.length === 0) return;
|
|
96552
|
+
const path = this.resolvePlanPath();
|
|
96553
|
+
if (this.markdownTheme !== void 0) this.addChild(new PlanBoxComponent(plan, this.markdownTheme, this.colors.success, path));
|
|
96289
96554
|
else this.addChild(new Text(chalk.dim(plan), 2, 0));
|
|
96290
|
-
|
|
96555
|
+
}
|
|
96556
|
+
resolvePlanForPreview() {
|
|
96557
|
+
const inlinePlan = str(this.toolCall.args["plan"]);
|
|
96558
|
+
if (inlinePlan.length > 0) return inlinePlan;
|
|
96559
|
+
if (this.result !== void 0 && !this.result.is_error) {
|
|
96560
|
+
const approved = extractApprovedPlan(this.result.output);
|
|
96561
|
+
if (approved.length > 0) return approved;
|
|
96562
|
+
}
|
|
96563
|
+
return this.currentPlan ?? "";
|
|
96564
|
+
}
|
|
96565
|
+
resolvePlanPath() {
|
|
96566
|
+
if (this.result !== void 0 && !this.result.is_error) {
|
|
96567
|
+
const fromResult = interpretExitPlanModeOutcome(this.result.output).path;
|
|
96568
|
+
if (fromResult !== void 0 && fromResult.length > 0) return fromResult;
|
|
96569
|
+
}
|
|
96570
|
+
return this.planPath;
|
|
96291
96571
|
}
|
|
96292
96572
|
buildContent() {
|
|
96293
96573
|
const { result } = this;
|
|
96294
96574
|
if (result === void 0 || !result.output) return;
|
|
96295
96575
|
if (this.toolCall.name === "ExitPlanMode" && !result.is_error) {
|
|
96296
|
-
|
|
96576
|
+
const outcome = interpretExitPlanModeOutcome(result.output);
|
|
96577
|
+
if (outcome.kind === "rejected" && outcome.feedback !== void 0) {
|
|
96578
|
+
const trimmed = outcome.feedback.trim();
|
|
96579
|
+
if (trimmed.length > 0) {
|
|
96580
|
+
const labelTone = chalk.hex(this.colors.warning).bold;
|
|
96581
|
+
this.addChild(new Text(labelTone("↪ Suggestion"), 2, 0));
|
|
96582
|
+
for (const line of trimmed.split("\n")) this.addChild(new Text(line, 4, 0));
|
|
96583
|
+
}
|
|
96584
|
+
}
|
|
96297
96585
|
return;
|
|
96298
96586
|
}
|
|
96299
96587
|
if (this.toolCall.name === "SetTodoList" && !result.is_error) return;
|
|
@@ -96344,7 +96632,7 @@ var UserMessageComponent = class extends Container {
|
|
|
96344
96632
|
constructor(text, colors) {
|
|
96345
96633
|
super();
|
|
96346
96634
|
this.addChild(new Spacer(1));
|
|
96347
|
-
this.addChild(new Text(chalk.hex(colors.
|
|
96635
|
+
this.addChild(new Text(chalk.hex(colors.roleUser).bold("✨ " + text), 0, 0));
|
|
96348
96636
|
}
|
|
96349
96637
|
};
|
|
96350
96638
|
//#endregion
|
|
@@ -96367,14 +96655,16 @@ var WelcomeComponent = class {
|
|
|
96367
96655
|
const textWidth = Math.max(4, innerWidth - logoWidth - 2);
|
|
96368
96656
|
const rightRow0 = truncateToWidth(chalk.bold.hex(this.colors.primary)("Welcome to Kimi Code!"), textWidth, "…");
|
|
96369
96657
|
const isLoggedOut = !this.state.model;
|
|
96370
|
-
const
|
|
96658
|
+
const dim = chalk.hex(this.colors.textDim);
|
|
96659
|
+
const labelStyle = chalk.bold.hex(this.colors.textDim);
|
|
96660
|
+
const rightRow1 = truncateToWidth(dim(isLoggedOut ? "Run /login to sign in." : "Send /help for help information."), textWidth, "…");
|
|
96371
96661
|
const headerLines = [primary(logo[0].padEnd(logoWidth)) + gap + rightRow0, primary(logo[1].padEnd(logoWidth)) + gap + rightRow1];
|
|
96372
|
-
const modelValue = isLoggedOut ? chalk.
|
|
96662
|
+
const modelValue = isLoggedOut ? chalk.hex(this.colors.warning)("(not signed in — run /login)") : this.state.model;
|
|
96373
96663
|
const infoLines = [
|
|
96374
|
-
|
|
96375
|
-
|
|
96376
|
-
|
|
96377
|
-
|
|
96664
|
+
labelStyle("Directory: ") + this.state.workDir,
|
|
96665
|
+
labelStyle("Session: ") + this.state.sessionId,
|
|
96666
|
+
labelStyle("Model: ") + modelValue,
|
|
96667
|
+
labelStyle("Version: ") + this.state.version
|
|
96378
96668
|
];
|
|
96379
96669
|
const contentLines = [
|
|
96380
96670
|
...headerLines,
|
|
@@ -96540,32 +96830,32 @@ function createTranscriptComponent(state, entry) {
|
|
|
96540
96830
|
if (state.toolOutputExpanded) tc.setExpanded(true);
|
|
96541
96831
|
return tc;
|
|
96542
96832
|
}
|
|
96543
|
-
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, entry.color);
|
|
96544
|
-
case "status": return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, entry.color);
|
|
96833
|
+
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, state.colors, entry.color);
|
|
96834
|
+
case "status": return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, state.colors, entry.color);
|
|
96545
96835
|
default: return null;
|
|
96546
96836
|
}
|
|
96547
96837
|
}
|
|
96548
96838
|
function createAssistantEntry(state, content) {
|
|
96549
|
-
const component = new AssistantMessageComponent(state.markdownTheme);
|
|
96839
|
+
const component = new AssistantMessageComponent(state.markdownTheme, state.colors);
|
|
96550
96840
|
component.updateContent(content);
|
|
96551
96841
|
return component;
|
|
96552
96842
|
}
|
|
96553
|
-
function paintStatus(content, color) {
|
|
96554
|
-
if (color === void 0) return chalk.
|
|
96843
|
+
function paintStatus(content, colors, color) {
|
|
96844
|
+
if (color === void 0) return chalk.hex(colors.textDim)(content);
|
|
96555
96845
|
if (color.startsWith("#")) return chalk.hex(color)(content);
|
|
96556
96846
|
const fn = chalk[color];
|
|
96557
96847
|
return typeof fn === "function" ? fn(content) : content;
|
|
96558
96848
|
}
|
|
96559
|
-
function createStatusEntry(content, color) {
|
|
96849
|
+
function createStatusEntry(content, colors, color) {
|
|
96560
96850
|
const container = new Container();
|
|
96561
|
-
const styled = paintStatus(content, color);
|
|
96851
|
+
const styled = paintStatus(content, colors, color);
|
|
96562
96852
|
container.addChild(new Text(` ${styled}`, 0, 0));
|
|
96563
96853
|
return container;
|
|
96564
96854
|
}
|
|
96565
96855
|
function createNoticeEntry(title, detail, colors) {
|
|
96566
96856
|
const container = new Container();
|
|
96567
96857
|
container.addChild(new Spacer(1));
|
|
96568
|
-
container.addChild(new Text(` ${chalk.
|
|
96858
|
+
container.addChild(new Text(` ${chalk.hex(colors.textStrong)(title)}`, 0, 0));
|
|
96569
96859
|
if (detail !== void 0 && detail.length > 0) container.addChild(new Text(` ${chalk.hex(colors.textDim)(detail)}`, 0, 0));
|
|
96570
96860
|
return container;
|
|
96571
96861
|
}
|
|
@@ -96673,21 +96963,243 @@ function buildImagePart(att) {
|
|
|
96673
96963
|
};
|
|
96674
96964
|
}
|
|
96675
96965
|
//#endregion
|
|
96966
|
+
//#region src/tui/theme/colors.ts
|
|
96967
|
+
/**
|
|
96968
|
+
* Color palette definitions for dark and light themes.
|
|
96969
|
+
*
|
|
96970
|
+
* Two layers:
|
|
96971
|
+
* - private `dark` / `light` raw palettes — unsemantic constants reused
|
|
96972
|
+
* across multiple semantic tokens to avoid hex literal duplication.
|
|
96973
|
+
* - exported `darkColors` / `lightColors` — the semantic `ColorPalette`
|
|
96974
|
+
* consumed by every UI component via chalk.hex(...).
|
|
96975
|
+
*
|
|
96976
|
+
* Light palette values are tuned for ≥ 4.5:1 contrast against #FFFFFF
|
|
96977
|
+
* for text tokens and ≥ 3:1 for chrome (border / large text), matching
|
|
96978
|
+
* WCAG AA. See plan in `~/.claude/plans/kimi-code-tui-zippy-spindle.md`.
|
|
96979
|
+
*/
|
|
96980
|
+
const dark = {
|
|
96981
|
+
blue400: "#4FA8FF",
|
|
96982
|
+
cyan400: "#5BC0BE",
|
|
96983
|
+
gray50: "#F5F5F5",
|
|
96984
|
+
gray100: "#E0E0E0",
|
|
96985
|
+
gray500: "#888888",
|
|
96986
|
+
gray600: "#6B6B6B",
|
|
96987
|
+
gray800: "#3A3A3A",
|
|
96988
|
+
green400: "#4EC87E",
|
|
96989
|
+
green300: "#7AD99B",
|
|
96990
|
+
red400: "#E85454",
|
|
96991
|
+
red300: "#F08585",
|
|
96992
|
+
amber400: "#E8A838",
|
|
96993
|
+
orange300: "#FFCB6B"
|
|
96994
|
+
};
|
|
96995
|
+
const light = {
|
|
96996
|
+
blue600: "#1565C0",
|
|
96997
|
+
cyan700: "#00838F",
|
|
96998
|
+
gray900: "#1A1A1A",
|
|
96999
|
+
gray700: "#454545",
|
|
97000
|
+
gray600: "#5F5F5F",
|
|
97001
|
+
gray500: "#737373",
|
|
97002
|
+
gray400: "#9CA3AF",
|
|
97003
|
+
green700: "#0E7A38",
|
|
97004
|
+
red700: "#B91C1C",
|
|
97005
|
+
amber800: "#92660A",
|
|
97006
|
+
orange700: "#9A4A00"
|
|
97007
|
+
};
|
|
97008
|
+
const darkColors = {
|
|
97009
|
+
primary: dark.blue400,
|
|
97010
|
+
accent: dark.cyan400,
|
|
97011
|
+
text: dark.gray100,
|
|
97012
|
+
textStrong: dark.gray50,
|
|
97013
|
+
textDim: dark.gray500,
|
|
97014
|
+
textMuted: dark.gray600,
|
|
97015
|
+
border: dark.gray800,
|
|
97016
|
+
borderFocus: dark.amber400,
|
|
97017
|
+
success: dark.green400,
|
|
97018
|
+
warning: dark.amber400,
|
|
97019
|
+
error: dark.red400,
|
|
97020
|
+
diffAdded: dark.green400,
|
|
97021
|
+
diffRemoved: dark.red400,
|
|
97022
|
+
diffAddedStrong: dark.green300,
|
|
97023
|
+
diffRemovedStrong: dark.red300,
|
|
97024
|
+
diffGutter: dark.gray600,
|
|
97025
|
+
diffMeta: dark.gray500,
|
|
97026
|
+
roleUser: dark.orange300,
|
|
97027
|
+
roleAssistant: dark.gray100,
|
|
97028
|
+
roleThinking: dark.gray500,
|
|
97029
|
+
roleTool: dark.amber400,
|
|
97030
|
+
status: dark.gray500
|
|
97031
|
+
};
|
|
97032
|
+
const lightColors = {
|
|
97033
|
+
primary: light.blue600,
|
|
97034
|
+
accent: light.cyan700,
|
|
97035
|
+
text: light.gray900,
|
|
97036
|
+
textStrong: light.gray900,
|
|
97037
|
+
textDim: light.gray700,
|
|
97038
|
+
textMuted: light.gray600,
|
|
97039
|
+
border: light.gray400,
|
|
97040
|
+
borderFocus: light.amber800,
|
|
97041
|
+
success: light.green700,
|
|
97042
|
+
warning: light.amber800,
|
|
97043
|
+
error: light.red700,
|
|
97044
|
+
diffAdded: light.green700,
|
|
97045
|
+
diffRemoved: light.red700,
|
|
97046
|
+
diffAddedStrong: light.green700,
|
|
97047
|
+
diffRemovedStrong: light.red700,
|
|
97048
|
+
diffGutter: light.gray500,
|
|
97049
|
+
diffMeta: light.gray600,
|
|
97050
|
+
roleUser: light.orange700,
|
|
97051
|
+
roleAssistant: light.gray900,
|
|
97052
|
+
roleThinking: light.gray700,
|
|
97053
|
+
roleTool: light.amber800,
|
|
97054
|
+
status: light.gray700
|
|
97055
|
+
};
|
|
97056
|
+
function getColorPalette(theme) {
|
|
97057
|
+
return theme === "dark" ? darkColors : lightColors;
|
|
97058
|
+
}
|
|
97059
|
+
//#endregion
|
|
97060
|
+
//#region src/tui/theme/detect.ts
|
|
97061
|
+
const DEFAULT_TIMEOUT_MS = 250;
|
|
97062
|
+
const OSC11_QUERY = "\x1B]11;?\x07";
|
|
97063
|
+
const OSC11_RESPONSE = /\]11;rgb:([0-9a-f]{1,4})\/([0-9a-f]{1,4})\/([0-9a-f]{1,4})/i;
|
|
97064
|
+
async function detectTerminalTheme(opts = {}) {
|
|
97065
|
+
if (!isInteractiveTerminal()) return "dark";
|
|
97066
|
+
if (isColorOptOut()) return "dark";
|
|
97067
|
+
const fromOsc = await queryOsc11({ timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS });
|
|
97068
|
+
if (fromOsc !== null) return fromOsc;
|
|
97069
|
+
const fromColorFgBg = parseColorFgBg(process.env["COLORFGBG"]);
|
|
97070
|
+
if (fromColorFgBg !== null) return fromColorFgBg;
|
|
97071
|
+
return "dark";
|
|
97072
|
+
}
|
|
97073
|
+
function isInteractiveTerminal() {
|
|
97074
|
+
return (process.stdin.isTTY ?? false) && (process.stdout.isTTY ?? false);
|
|
97075
|
+
}
|
|
97076
|
+
function isColorOptOut() {
|
|
97077
|
+
const env = process.env;
|
|
97078
|
+
if (env["NO_COLOR"] !== void 0 && env["NO_COLOR"] !== "") return true;
|
|
97079
|
+
if (env["FORCE_COLOR"] === "0") return true;
|
|
97080
|
+
if (env["CI"] !== void 0 && env["CI"] !== "" && env["CI"] !== "0") return true;
|
|
97081
|
+
return false;
|
|
97082
|
+
}
|
|
97083
|
+
async function queryOsc11(opts) {
|
|
97084
|
+
const stdin = process.stdin;
|
|
97085
|
+
if (typeof stdin.setRawMode !== "function") return null;
|
|
97086
|
+
if (process.stdin.listenerCount("data") > 0) return null;
|
|
97087
|
+
const wasRaw = stdin.isRaw === true;
|
|
97088
|
+
let buffer = "";
|
|
97089
|
+
let listener = null;
|
|
97090
|
+
let timer = null;
|
|
97091
|
+
try {
|
|
97092
|
+
if (!wasRaw) stdin.setRawMode(true);
|
|
97093
|
+
return await new Promise((resolve) => {
|
|
97094
|
+
listener = (chunk) => {
|
|
97095
|
+
buffer += chunk.toString("utf8");
|
|
97096
|
+
const match = OSC11_RESPONSE.exec(buffer);
|
|
97097
|
+
if (match === null) return;
|
|
97098
|
+
const [, r, g, b] = match;
|
|
97099
|
+
if (r === void 0 || g === void 0 || b === void 0) return;
|
|
97100
|
+
resolve(themeFromHexChannels(r, g, b));
|
|
97101
|
+
};
|
|
97102
|
+
stdin.on("data", listener);
|
|
97103
|
+
timer = setTimeout(() => {
|
|
97104
|
+
resolve(null);
|
|
97105
|
+
}, opts.timeoutMs);
|
|
97106
|
+
try {
|
|
97107
|
+
process.stdout.write(OSC11_QUERY);
|
|
97108
|
+
} catch {
|
|
97109
|
+
resolve(null);
|
|
97110
|
+
}
|
|
97111
|
+
});
|
|
97112
|
+
} catch {
|
|
97113
|
+
return null;
|
|
97114
|
+
} finally {
|
|
97115
|
+
if (timer !== null) clearTimeout(timer);
|
|
97116
|
+
if (listener !== null) stdin.off("data", listener);
|
|
97117
|
+
if (!wasRaw) try {
|
|
97118
|
+
stdin.setRawMode(false);
|
|
97119
|
+
} catch {}
|
|
97120
|
+
}
|
|
97121
|
+
}
|
|
97122
|
+
function themeFromHexChannels(rHex, gHex, bHex) {
|
|
97123
|
+
const r = normalizeChannel(rHex);
|
|
97124
|
+
const g = normalizeChannel(gHex);
|
|
97125
|
+
const b = normalizeChannel(bHex);
|
|
97126
|
+
return .2126 * r + .7152 * g + .0722 * b > .5 ? "light" : "dark";
|
|
97127
|
+
}
|
|
97128
|
+
function normalizeChannel(hex) {
|
|
97129
|
+
const max = (1 << hex.length * 4) - 1;
|
|
97130
|
+
const value = parseInt(hex, 16);
|
|
97131
|
+
return Number.isFinite(value) ? value / max : 0;
|
|
97132
|
+
}
|
|
97133
|
+
/**
|
|
97134
|
+
* COLORFGBG is `"fg;bg"` (sometimes `"fg;default;bg"`). The last token is
|
|
97135
|
+
* the background ANSI 16-color index; 0–6 and 8 are dark, the rest light.
|
|
97136
|
+
*/
|
|
97137
|
+
function parseColorFgBg(value) {
|
|
97138
|
+
if (value === void 0 || value === "") return null;
|
|
97139
|
+
const bgRaw = value.split(";").at(-1);
|
|
97140
|
+
if (bgRaw === void 0) return null;
|
|
97141
|
+
const bg = parseInt(bgRaw, 10);
|
|
97142
|
+
if (!Number.isInteger(bg)) return null;
|
|
97143
|
+
return new Set([
|
|
97144
|
+
0,
|
|
97145
|
+
1,
|
|
97146
|
+
2,
|
|
97147
|
+
3,
|
|
97148
|
+
4,
|
|
97149
|
+
5,
|
|
97150
|
+
6,
|
|
97151
|
+
8
|
|
97152
|
+
]).has(bg) ? "dark" : "light";
|
|
97153
|
+
}
|
|
97154
|
+
//#endregion
|
|
97155
|
+
//#region src/tui/theme/styles.ts
|
|
97156
|
+
/**
|
|
97157
|
+
* Theme-aware style helpers built on chalk. Components hold a reference
|
|
97158
|
+
* to a `ThemeStyles` instance via `state.styles` and never reach into
|
|
97159
|
+
* raw chalk color names — that keeps theme switches consistent and lets
|
|
97160
|
+
* every visual token route through `ColorPalette`.
|
|
97161
|
+
*/
|
|
97162
|
+
function createThemeStyles(colors) {
|
|
97163
|
+
return {
|
|
97164
|
+
colors,
|
|
97165
|
+
primary: (s) => chalk.hex(colors.primary)(s),
|
|
97166
|
+
accent: (s) => chalk.hex(colors.accent)(s),
|
|
97167
|
+
dim: (s) => chalk.hex(colors.textDim)(s),
|
|
97168
|
+
muted: (s) => chalk.hex(colors.textMuted)(s),
|
|
97169
|
+
text: (s) => chalk.hex(colors.text)(s),
|
|
97170
|
+
strong: (s) => chalk.hex(colors.textStrong)(s),
|
|
97171
|
+
error: (s) => chalk.hex(colors.error)(s),
|
|
97172
|
+
warning: (s) => chalk.hex(colors.warning)(s),
|
|
97173
|
+
success: (s) => chalk.hex(colors.success)(s),
|
|
97174
|
+
label: (s) => chalk.bold.hex(colors.textDim)(s),
|
|
97175
|
+
value: (s) => chalk.hex(colors.text)(s),
|
|
97176
|
+
diffAdd: (s) => chalk.hex(colors.diffAdded)(s),
|
|
97177
|
+
diffDel: (s) => chalk.hex(colors.diffRemoved)(s),
|
|
97178
|
+
diffAddBold: (s) => chalk.bold.hex(colors.diffAddedStrong)(s),
|
|
97179
|
+
diffDelBold: (s) => chalk.bold.hex(colors.diffRemovedStrong)(s),
|
|
97180
|
+
diffGutter: (s) => chalk.hex(colors.diffGutter)(s),
|
|
97181
|
+
diffMeta: (s) => chalk.hex(colors.diffMeta)(s)
|
|
97182
|
+
};
|
|
97183
|
+
}
|
|
97184
|
+
//#endregion
|
|
96676
97185
|
//#region src/tui/theme/pi-tui-theme.ts
|
|
96677
|
-
const HEADING_HASH_PREFIX = /^((?:\
|
|
97186
|
+
const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
96678
97187
|
function createMarkdownTheme(colors) {
|
|
96679
97188
|
const stripHash = (text) => text.replace(HEADING_HASH_PREFIX, "$1");
|
|
97189
|
+
const muted = chalk.hex(colors.textMuted);
|
|
97190
|
+
const dim = chalk.hex(colors.textDim);
|
|
97191
|
+
const border = chalk.hex(colors.border);
|
|
96680
97192
|
return {
|
|
96681
97193
|
heading: (text) => chalk.bold.hex(colors.text)(stripHash(text)),
|
|
96682
97194
|
link: (text) => chalk.hex(colors.primary)(text),
|
|
96683
|
-
linkUrl: (text) =>
|
|
97195
|
+
linkUrl: (text) => muted(text),
|
|
96684
97196
|
code: (text) => chalk.hex(colors.primary)(text),
|
|
96685
97197
|
codeBlock: (text) => text,
|
|
96686
|
-
codeBlockBorder: (text) =>
|
|
96687
|
-
quote: (text) =>
|
|
96688
|
-
quoteBorder: (text) =>
|
|
96689
|
-
hr: (text) =>
|
|
96690
|
-
listBullet: (text) => chalk.
|
|
97198
|
+
codeBlockBorder: (text) => border(text),
|
|
97199
|
+
quote: (text) => dim(text),
|
|
97200
|
+
quoteBorder: (text) => dim(text),
|
|
97201
|
+
hr: (text) => border(text),
|
|
97202
|
+
listBullet: (text) => chalk.hex(colors.roleAssistant)(text.replace(/^-/, "•")),
|
|
96691
97203
|
bold: (text) => chalk.bold(text),
|
|
96692
97204
|
italic: (text) => chalk.italic(text),
|
|
96693
97205
|
strikethrough: (text) => chalk.strikethrough(text),
|
|
@@ -96705,18 +97217,39 @@ function createMarkdownTheme(colors) {
|
|
|
96705
97217
|
};
|
|
96706
97218
|
}
|
|
96707
97219
|
function createEditorTheme(colors) {
|
|
97220
|
+
const muted = chalk.hex(colors.textMuted);
|
|
96708
97221
|
return {
|
|
96709
97222
|
borderColor: (s) => chalk.hex(colors.border)(s),
|
|
96710
97223
|
selectList: {
|
|
96711
97224
|
selectedPrefix: (s) => chalk.hex(colors.primary)(s),
|
|
96712
97225
|
selectedText: (s) => chalk.hex(colors.primary)(s),
|
|
96713
|
-
description: (s) =>
|
|
96714
|
-
scrollInfo: (s) =>
|
|
96715
|
-
noMatch: (s) =>
|
|
97226
|
+
description: (s) => muted(s),
|
|
97227
|
+
scrollInfo: (s) => muted(s),
|
|
97228
|
+
noMatch: (s) => muted(s)
|
|
96716
97229
|
}
|
|
96717
97230
|
};
|
|
96718
97231
|
}
|
|
96719
97232
|
//#endregion
|
|
97233
|
+
//#region src/tui/theme/index.ts
|
|
97234
|
+
/**
|
|
97235
|
+
* Resolve a user preference to a concrete palette key. `'auto'` triggers
|
|
97236
|
+
* terminal background detection (OSC 11 with COLORFGBG / dark fallback);
|
|
97237
|
+
* explicit choices pass through.
|
|
97238
|
+
*/
|
|
97239
|
+
async function resolveTheme(theme) {
|
|
97240
|
+
if (theme === "auto") return detectTerminalTheme();
|
|
97241
|
+
return theme;
|
|
97242
|
+
}
|
|
97243
|
+
/**
|
|
97244
|
+
* Synchronous fallback used by paths that cannot wait on terminal probes
|
|
97245
|
+
* (initial state construction, in-TUI theme switches). `'auto'` collapses
|
|
97246
|
+
* to `'dark'`; explicit choices pass through.
|
|
97247
|
+
*/
|
|
97248
|
+
function resolveThemeSync(theme) {
|
|
97249
|
+
if (theme === "auto") return "dark";
|
|
97250
|
+
return theme;
|
|
97251
|
+
}
|
|
97252
|
+
//#endregion
|
|
96720
97253
|
//#region src/tui/core/ui-ops.ts
|
|
96721
97254
|
function isExpandable(obj) {
|
|
96722
97255
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
@@ -96792,15 +97325,34 @@ function clearTranscriptAndRedraw(state) {
|
|
|
96792
97325
|
}
|
|
96793
97326
|
/** Use primary color for slash commands or while plan mode is active. */
|
|
96794
97327
|
function updateEditorBorderHighlight(state, text) {
|
|
96795
|
-
const editorTheme = createEditorTheme(state.colors);
|
|
96796
97328
|
const trimmed = (text ?? state.editor.getText()).trimStart();
|
|
96797
97329
|
if (state.appState.planMode || trimmed.startsWith("/")) {
|
|
96798
97330
|
const primary = state.colors.primary;
|
|
96799
97331
|
state.editor.borderColor = (s) => chalk.hex(primary)(s);
|
|
96800
|
-
} else state.editor.borderColor = editorTheme.borderColor;
|
|
97332
|
+
} else state.editor.borderColor = state.editorTheme.borderColor;
|
|
96801
97333
|
state.editor.slashHighlightHex = state.colors.primary;
|
|
96802
97334
|
state.ui.requestRender();
|
|
96803
97335
|
}
|
|
97336
|
+
/**
|
|
97337
|
+
* Apply a theme preference. `resolved` lets callers pre-compute the
|
|
97338
|
+
* concrete palette (e.g. async OSC 11 detection done before re-entering
|
|
97339
|
+
* the TUI); when omitted we fall back to the synchronous rule
|
|
97340
|
+
* (`'auto'` → `'dark'`).
|
|
97341
|
+
*/
|
|
97342
|
+
function applyTheme(state, theme, hooks, resolved) {
|
|
97343
|
+
const resolvedTheme = resolved ?? resolveThemeSync(theme);
|
|
97344
|
+
Object.assign(state.colors, getColorPalette(resolvedTheme));
|
|
97345
|
+
state.appState.theme = theme;
|
|
97346
|
+
state.resolvedTheme = resolvedTheme;
|
|
97347
|
+
state.styles = createThemeStyles(state.colors);
|
|
97348
|
+
state.markdownTheme = createMarkdownTheme(state.colors);
|
|
97349
|
+
state.editorTheme = createEditorTheme(state.colors);
|
|
97350
|
+
updateEditorBorderHighlight(state);
|
|
97351
|
+
hooks.syncFooter();
|
|
97352
|
+
hooks.refreshActivityPane();
|
|
97353
|
+
hooks.refreshQueuePane();
|
|
97354
|
+
state.ui.requestRender(true);
|
|
97355
|
+
}
|
|
96804
97356
|
//#endregion
|
|
96805
97357
|
//#region src/tui/handlers/subagent.ts
|
|
96806
97358
|
function handleSubagentSourceEvent(ectx, payload) {
|
|
@@ -97273,6 +97825,31 @@ const shellCommands = [
|
|
|
97273
97825
|
async execute() {
|
|
97274
97826
|
return ok$1("__show_usage__");
|
|
97275
97827
|
}
|
|
97828
|
+
},
|
|
97829
|
+
{
|
|
97830
|
+
name: "editor",
|
|
97831
|
+
aliases: [],
|
|
97832
|
+
description: "Set the external editor for Ctrl-G",
|
|
97833
|
+
mode: "both",
|
|
97834
|
+
priority: 60,
|
|
97835
|
+
async execute(args, _ctx) {
|
|
97836
|
+
const trimmed = args.trim();
|
|
97837
|
+
if (trimmed.length === 0) return ok$1("__show_editor_picker__");
|
|
97838
|
+
return ok$1(`__set_editor__:${trimmed}`);
|
|
97839
|
+
}
|
|
97840
|
+
},
|
|
97841
|
+
{
|
|
97842
|
+
name: "theme",
|
|
97843
|
+
aliases: [],
|
|
97844
|
+
description: "Set the terminal UI theme",
|
|
97845
|
+
mode: "both",
|
|
97846
|
+
priority: 60,
|
|
97847
|
+
async execute(args) {
|
|
97848
|
+
const trimmed = args.trim();
|
|
97849
|
+
if (trimmed.length === 0) return ok$1("__show_theme_picker__");
|
|
97850
|
+
if (trimmed === "dark" || trimmed === "light" || trimmed === "auto") return ok$1(`__set_theme__:${trimmed}`);
|
|
97851
|
+
return ok$1(`Unknown theme: ${trimmed}`);
|
|
97852
|
+
}
|
|
97276
97853
|
}
|
|
97277
97854
|
];
|
|
97278
97855
|
//#endregion
|
|
@@ -98063,49 +98640,6 @@ var QuestionController = class extends ReverseRpcController {
|
|
|
98063
98640
|
}
|
|
98064
98641
|
};
|
|
98065
98642
|
//#endregion
|
|
98066
|
-
//#region src/tui/theme/colors.ts
|
|
98067
|
-
const darkColors = {
|
|
98068
|
-
primary: "#4FA8FF",
|
|
98069
|
-
primaryDim: "#2563EB",
|
|
98070
|
-
text: "#E0E0E0",
|
|
98071
|
-
textDim: "#888888",
|
|
98072
|
-
textMuted: "#555555",
|
|
98073
|
-
success: "#4EC87E",
|
|
98074
|
-
warning: "#E8A838",
|
|
98075
|
-
error: "#E85454",
|
|
98076
|
-
info: "#4FA8FF",
|
|
98077
|
-
border: "#444444",
|
|
98078
|
-
prompt: "#4FA8FF",
|
|
98079
|
-
spinner: "#4FA8FF",
|
|
98080
|
-
user: "#FFCB6B",
|
|
98081
|
-
assistant: "#E0E0E0",
|
|
98082
|
-
thinking: "#888888",
|
|
98083
|
-
toolCall: "#E8A838",
|
|
98084
|
-
status: "#888888"
|
|
98085
|
-
};
|
|
98086
|
-
const lightColors = {
|
|
98087
|
-
primary: "#1783ff",
|
|
98088
|
-
primaryDim: "#6ba7e8",
|
|
98089
|
-
text: "#1A1A1A",
|
|
98090
|
-
textDim: "#666666",
|
|
98091
|
-
textMuted: "#999999",
|
|
98092
|
-
success: "#16A34A",
|
|
98093
|
-
warning: "#CA8A04",
|
|
98094
|
-
error: "#DC2626",
|
|
98095
|
-
info: "#1783ff",
|
|
98096
|
-
border: "#CCCCCC",
|
|
98097
|
-
prompt: "#1783ff",
|
|
98098
|
-
spinner: "#1783ff",
|
|
98099
|
-
user: "#B45309",
|
|
98100
|
-
assistant: "#1A1A1A",
|
|
98101
|
-
thinking: "#666666",
|
|
98102
|
-
toolCall: "#CA8A04",
|
|
98103
|
-
status: "#666666"
|
|
98104
|
-
};
|
|
98105
|
-
function getColorPalette(theme) {
|
|
98106
|
-
return theme === "dark" ? darkColors : lightColors;
|
|
98107
|
-
}
|
|
98108
|
-
//#endregion
|
|
98109
98643
|
//#region src/tui/state.ts
|
|
98110
98644
|
const INITIAL_LIVE_PANE = {
|
|
98111
98645
|
mode: "idle",
|
|
@@ -98122,7 +98656,9 @@ const INITIAL_LIVE_PANE = {
|
|
|
98122
98656
|
*/
|
|
98123
98657
|
function createTUIState(options) {
|
|
98124
98658
|
const initialAppState = options.initialAppState;
|
|
98125
|
-
const
|
|
98659
|
+
const resolvedTheme = options.resolvedTheme ?? resolveThemeSync(initialAppState.theme);
|
|
98660
|
+
const colors = { ...getColorPalette(resolvedTheme) };
|
|
98661
|
+
const styles = createThemeStyles(colors);
|
|
98126
98662
|
const markdownTheme = createMarkdownTheme(colors);
|
|
98127
98663
|
const editorTheme = createEditorTheme(colors);
|
|
98128
98664
|
const terminal = new ProcessTerminal();
|
|
@@ -98155,10 +98691,13 @@ function createTUIState(options) {
|
|
|
98155
98691
|
footer,
|
|
98156
98692
|
editor,
|
|
98157
98693
|
colors,
|
|
98694
|
+
styles,
|
|
98158
98695
|
markdownTheme,
|
|
98696
|
+
editorTheme,
|
|
98697
|
+
resolvedTheme,
|
|
98159
98698
|
appState: { ...initialAppState },
|
|
98160
98699
|
startupState: "pending",
|
|
98161
|
-
startupNotice:
|
|
98700
|
+
startupNotice: options.startup.startupNotice,
|
|
98162
98701
|
livePane: { ...INITIAL_LIVE_PANE },
|
|
98163
98702
|
transcriptEntries: [],
|
|
98164
98703
|
toasts: [],
|
|
@@ -98333,6 +98872,9 @@ function dequeueFirst(state) {
|
|
|
98333
98872
|
state.queuedMessages = state.queuedMessages.slice(1);
|
|
98334
98873
|
return first.text;
|
|
98335
98874
|
}
|
|
98875
|
+
function clearQueue(state) {
|
|
98876
|
+
state.queuedMessages.length = 0;
|
|
98877
|
+
}
|
|
98336
98878
|
//#endregion
|
|
98337
98879
|
//#region src/tui/actions/wire-ops.ts
|
|
98338
98880
|
/** Immediately send a message through the wire client (no queue). */
|
|
@@ -98463,21 +99005,21 @@ function sendMessage(state, addEntry, input, options) {
|
|
|
98463
99005
|
*/
|
|
98464
99006
|
function steerMessage(state, addEntry, input) {
|
|
98465
99007
|
if (state.appState.isCompacting) {
|
|
98466
|
-
enqueueMessage(state,
|
|
99008
|
+
for (const part of input) enqueueMessage(state, part);
|
|
98467
99009
|
return;
|
|
98468
99010
|
}
|
|
98469
99011
|
if (!state.appState.isStreaming) {
|
|
98470
|
-
sendMessageInternal(state, addEntry,
|
|
99012
|
+
for (const part of input) sendMessageInternal(state, addEntry, part);
|
|
98471
99013
|
return;
|
|
98472
99014
|
}
|
|
98473
|
-
addEntry({
|
|
99015
|
+
for (const part of input) addEntry({
|
|
98474
99016
|
id: nextTranscriptId(),
|
|
98475
99017
|
kind: "user",
|
|
98476
99018
|
turnId: state.currentTurnId,
|
|
98477
99019
|
renderMode: "plain",
|
|
98478
|
-
content:
|
|
99020
|
+
content: part
|
|
98479
99021
|
});
|
|
98480
|
-
state.client.steer(state.appState.sessionId, input).catch((error) => {
|
|
99022
|
+
state.client.steer(state.appState.sessionId, input.join("\n\n")).catch((error) => {
|
|
98481
99023
|
const message = error instanceof Error ? error.message : String(error);
|
|
98482
99024
|
addEntry({
|
|
98483
99025
|
id: nextTranscriptId(),
|
|
@@ -98817,10 +99359,7 @@ function userPartToText(part) {
|
|
|
98817
99359
|
}
|
|
98818
99360
|
function outputToText(output) {
|
|
98819
99361
|
if (typeof output === "string") return output;
|
|
98820
|
-
if (Array.isArray(output)) return
|
|
98821
|
-
if (isObject(part) && part["type"] === "text") return stringValue(part["text"]) ?? "";
|
|
98822
|
-
return JSON.stringify(part);
|
|
98823
|
-
}).join("");
|
|
99362
|
+
if (Array.isArray(output)) return JSON.stringify(output);
|
|
98824
99363
|
return output === void 0 ? "" : JSON.stringify(output);
|
|
98825
99364
|
}
|
|
98826
99365
|
function subToolKey(agentId, toolCallId) {
|
|
@@ -99047,7 +99586,7 @@ var CompactionComponent = class extends Container {
|
|
|
99047
99586
|
}
|
|
99048
99587
|
buildHeader() {
|
|
99049
99588
|
if (this.done) return `${chalk.hex(this.colors.success)("⏺ ")}${chalk.hex(this.colors.success).bold("Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? chalk.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}`;
|
|
99050
|
-
return `${this.blinkOn ? chalk.
|
|
99589
|
+
return `${this.blinkOn ? chalk.hex(this.colors.roleAssistant)("⏺ ") : " "}${chalk.hex(this.colors.primary).bold("Compacting context...")}`;
|
|
99051
99590
|
}
|
|
99052
99591
|
startBlink() {
|
|
99053
99592
|
this.blinkTimer = setInterval(() => {
|
|
@@ -99064,6 +99603,15 @@ var CompactionComponent = class extends Container {
|
|
|
99064
99603
|
}
|
|
99065
99604
|
};
|
|
99066
99605
|
//#endregion
|
|
99606
|
+
//#region src/tui/actions/plan-ops.ts
|
|
99607
|
+
async function refreshPlanFromCore(state) {
|
|
99608
|
+
try {
|
|
99609
|
+
return await state.client.getPlan(state.appState.sessionId);
|
|
99610
|
+
} catch {
|
|
99611
|
+
return {};
|
|
99612
|
+
}
|
|
99613
|
+
}
|
|
99614
|
+
//#endregion
|
|
99067
99615
|
//#region src/tui/actions/stream-ops.ts
|
|
99068
99616
|
/**
|
|
99069
99617
|
* Streaming / tool-call / compaction render hooks.
|
|
@@ -99073,7 +99621,7 @@ var CompactionComponent = class extends Container {
|
|
|
99073
99621
|
* on `state` is touched — state-ops.ts owns AppState / LivePane.
|
|
99074
99622
|
*/
|
|
99075
99623
|
function onStreamingTextStart(state) {
|
|
99076
|
-
state.streamingComponent = new AssistantMessageComponent(state.markdownTheme);
|
|
99624
|
+
state.streamingComponent = new AssistantMessageComponent(state.markdownTheme, state.colors);
|
|
99077
99625
|
state.transcriptContainer.addChild(state.streamingComponent);
|
|
99078
99626
|
state.ui.requestRender();
|
|
99079
99627
|
}
|
|
@@ -99106,6 +99654,10 @@ function onToolCallStart(state, toolCall) {
|
|
|
99106
99654
|
state.pendingToolComponents.set(toolCall.id, tc);
|
|
99107
99655
|
state.transcriptContainer.addChild(tc);
|
|
99108
99656
|
state.ui.requestRender();
|
|
99657
|
+
if (toolCall.name === "ExitPlanMode" && typeof toolCall.args["plan"] !== "string") (async () => {
|
|
99658
|
+
const snapshot = await refreshPlanFromCore(state);
|
|
99659
|
+
tc.setPlanInfo(snapshot);
|
|
99660
|
+
})();
|
|
99109
99661
|
}
|
|
99110
99662
|
function onToolCallEnd(state, toolCallId, result) {
|
|
99111
99663
|
const matchedCall = state.activeToolCalls.get(toolCallId);
|
|
@@ -99148,6 +99700,126 @@ function endCompaction(state, tokensBefore, tokensAfter) {
|
|
|
99148
99700
|
state.ui.requestRender();
|
|
99149
99701
|
}
|
|
99150
99702
|
//#endregion
|
|
99703
|
+
//#region src/tui/components/dialogs/choice-picker.ts
|
|
99704
|
+
/**
|
|
99705
|
+
* ChoicePicker — modal single-select list for slash commands that ask
|
|
99706
|
+
* the user to pick from a small set of preset values.
|
|
99707
|
+
*
|
|
99708
|
+
* Mirrors SessionPickerComponent's container-replacement pattern: host
|
|
99709
|
+
* calls `showChoicePicker(...)` which clears the editor container,
|
|
99710
|
+
* addChild(picker), setFocus(picker); the picker invokes `onSelect` or
|
|
99711
|
+
* `onCancel`, and the host tears it down.
|
|
99712
|
+
*/
|
|
99713
|
+
const CURRENT_MARK = "← current";
|
|
99714
|
+
var ChoicePickerComponent = class extends Container {
|
|
99715
|
+
focused = false;
|
|
99716
|
+
opts;
|
|
99717
|
+
selectedIndex;
|
|
99718
|
+
constructor(opts) {
|
|
99719
|
+
super();
|
|
99720
|
+
this.opts = opts;
|
|
99721
|
+
const currentIdx = opts.options.findIndex((o) => o.value === opts.currentValue);
|
|
99722
|
+
this.selectedIndex = Math.max(currentIdx, 0);
|
|
99723
|
+
}
|
|
99724
|
+
handleInput(data) {
|
|
99725
|
+
if (matchesKey(data, Key.escape)) {
|
|
99726
|
+
this.opts.onCancel();
|
|
99727
|
+
return;
|
|
99728
|
+
}
|
|
99729
|
+
if (matchesKey(data, Key.up)) {
|
|
99730
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
99731
|
+
return;
|
|
99732
|
+
}
|
|
99733
|
+
if (matchesKey(data, Key.down)) {
|
|
99734
|
+
this.selectedIndex = Math.min(this.opts.options.length - 1, this.selectedIndex + 1);
|
|
99735
|
+
return;
|
|
99736
|
+
}
|
|
99737
|
+
if (matchesKey(data, Key.enter)) {
|
|
99738
|
+
const chosen = this.opts.options[this.selectedIndex];
|
|
99739
|
+
if (chosen !== void 0) this.opts.onSelect(chosen.value);
|
|
99740
|
+
return;
|
|
99741
|
+
}
|
|
99742
|
+
}
|
|
99743
|
+
render(width) {
|
|
99744
|
+
const { colors } = this.opts;
|
|
99745
|
+
const hint = this.opts.hint ?? "↑↓ navigate · Enter select · Esc cancel";
|
|
99746
|
+
const lines = [
|
|
99747
|
+
chalk.hex(colors.primary)("─".repeat(width)),
|
|
99748
|
+
chalk.hex(colors.primary).bold(` ${this.opts.title}`),
|
|
99749
|
+
chalk.hex(colors.textMuted)(` ${hint}`),
|
|
99750
|
+
""
|
|
99751
|
+
];
|
|
99752
|
+
for (let i = 0; i < this.opts.options.length; i++) {
|
|
99753
|
+
const opt = this.opts.options[i];
|
|
99754
|
+
const isSelected = i === this.selectedIndex;
|
|
99755
|
+
const isCurrent = opt.value === this.opts.currentValue;
|
|
99756
|
+
const pointer = isSelected ? "❯" : " ";
|
|
99757
|
+
const labelStyle = isSelected ? chalk.hex(colors.primary).bold : chalk.hex(colors.text);
|
|
99758
|
+
let line = chalk.hex(isSelected ? colors.primary : colors.textDim)(` ${pointer} `);
|
|
99759
|
+
line += labelStyle(opt.label);
|
|
99760
|
+
if (isCurrent) line += " " + chalk.hex(colors.success)(CURRENT_MARK);
|
|
99761
|
+
lines.push(line);
|
|
99762
|
+
}
|
|
99763
|
+
lines.push("");
|
|
99764
|
+
lines.push(chalk.hex(colors.primary)("─".repeat(width)));
|
|
99765
|
+
return lines;
|
|
99766
|
+
}
|
|
99767
|
+
};
|
|
99768
|
+
//#endregion
|
|
99769
|
+
//#region src/tui/panels/theme-picker.ts
|
|
99770
|
+
const THEME_OPTIONS = [
|
|
99771
|
+
{
|
|
99772
|
+
value: "auto",
|
|
99773
|
+
label: "Auto (match terminal)"
|
|
99774
|
+
},
|
|
99775
|
+
{
|
|
99776
|
+
value: "dark",
|
|
99777
|
+
label: "Dark"
|
|
99778
|
+
},
|
|
99779
|
+
{
|
|
99780
|
+
value: "light",
|
|
99781
|
+
label: "Light"
|
|
99782
|
+
}
|
|
99783
|
+
];
|
|
99784
|
+
function isTheme(value) {
|
|
99785
|
+
return value === "dark" || value === "light" || value === "auto";
|
|
99786
|
+
}
|
|
99787
|
+
function showThemePicker(state, hooks) {
|
|
99788
|
+
mountPanel(state, new ChoicePickerComponent({
|
|
99789
|
+
title: "Select theme",
|
|
99790
|
+
hint: "↑↓ navigate · Enter select · Esc cancel",
|
|
99791
|
+
options: [...THEME_OPTIONS],
|
|
99792
|
+
currentValue: state.appState.theme,
|
|
99793
|
+
colors: state.colors,
|
|
99794
|
+
onSelect: (value) => {
|
|
99795
|
+
closeThemePicker(state);
|
|
99796
|
+
if (isTheme(value)) applyThemeChoice(state, value, hooks);
|
|
99797
|
+
},
|
|
99798
|
+
onCancel: () => closeThemePicker(state)
|
|
99799
|
+
}));
|
|
99800
|
+
}
|
|
99801
|
+
function closeThemePicker(state) {
|
|
99802
|
+
unmountPanel(state);
|
|
99803
|
+
}
|
|
99804
|
+
async function applyThemeChoice(state, theme, hooks) {
|
|
99805
|
+
if (theme === state.appState.theme) {
|
|
99806
|
+
emitStatus(state, `Theme unchanged: "${theme}".`);
|
|
99807
|
+
return;
|
|
99808
|
+
}
|
|
99809
|
+
try {
|
|
99810
|
+
await saveTuiConfig({
|
|
99811
|
+
theme,
|
|
99812
|
+
editorCommand: state.appState.editorCommand
|
|
99813
|
+
});
|
|
99814
|
+
} catch (error) {
|
|
99815
|
+
emitStatus(state, `Failed to save theme: ${error instanceof Error ? error.message : String(error)}`, state.colors.error);
|
|
99816
|
+
return;
|
|
99817
|
+
}
|
|
99818
|
+
const resolved = await resolveTheme(theme);
|
|
99819
|
+
applyTheme(state, theme, hooks, resolved);
|
|
99820
|
+
emitStatus(state, `Theme set to "${theme}"${theme === "auto" ? ` (detected: ${resolved})` : ""}.`);
|
|
99821
|
+
}
|
|
99822
|
+
//#endregion
|
|
99151
99823
|
//#region src/tui/commands/skill-commands.ts
|
|
99152
99824
|
const SKILL_ACTIVATION_SENTINEL_PREFIX = "__activate_skill__:";
|
|
99153
99825
|
async function fetchSkills(client, sessionId) {
|
|
@@ -99216,6 +99888,27 @@ async function tryDispatchSkill(client, sessionId, name, args) {
|
|
|
99216
99888
|
}
|
|
99217
99889
|
//#endregion
|
|
99218
99890
|
//#region src/tui/commands/dispatch.ts
|
|
99891
|
+
/**
|
|
99892
|
+
* Slash-command dispatcher.
|
|
99893
|
+
*
|
|
99894
|
+
* 入口 `dispatchSlashCommand(input, state, buildCtx)` 替代旧
|
|
99895
|
+
* `KimiTUI.executeSlashCommand`。职责:
|
|
99896
|
+
*
|
|
99897
|
+
* 1. 通过 `parseSlashInput` 拆 `/name args`。
|
|
99898
|
+
* 2. 走 `state.registry.find(name)` 定位命令定义。
|
|
99899
|
+
* - 未命中:交给 `tryDispatchSkill` 做 skill 兜底。
|
|
99900
|
+
* - 命中:调用命令 `execute(args, ctx)` 拿到 `SlashCommandResult`。
|
|
99901
|
+
* 3. 解释 result:
|
|
99902
|
+
* - `type: 'exit'` → 调 ctx.stop。
|
|
99903
|
+
* - `type: 'reload'` → 调 ctx.performReload(action)。
|
|
99904
|
+
* - `type: 'ok'` + 哨兵字符串 `__show_help__ / __show_sessions__ /
|
|
99905
|
+
* __show_editor_picker__ / __show_theme_picker__ /
|
|
99906
|
+
* __show_model_picker__ / __show_usage__ /
|
|
99907
|
+
* __show_model_picker__:<alias> / __set_editor__:<cmd> /
|
|
99908
|
+
* __set_theme__:<theme> / __send_as_message__:<text>` →
|
|
99909
|
+
* 分发到 `ctx.showXxx / ctx.sendAsMessage`。
|
|
99910
|
+
* - 其它:把 `message` append 进 transcript。
|
|
99911
|
+
*/
|
|
99219
99912
|
async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
99220
99913
|
const parsed = parseSlashInput(input);
|
|
99221
99914
|
if (!parsed) return false;
|
|
@@ -99276,6 +99969,10 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
99276
99969
|
ctx.showEditorPicker();
|
|
99277
99970
|
return true;
|
|
99278
99971
|
}
|
|
99972
|
+
if (result.message === "__show_theme_picker__") {
|
|
99973
|
+
ctx.showThemePicker();
|
|
99974
|
+
return true;
|
|
99975
|
+
}
|
|
99279
99976
|
if (result.message === "__show_model_picker__") {
|
|
99280
99977
|
ctx.showModelPicker();
|
|
99281
99978
|
return true;
|
|
@@ -99289,6 +99986,18 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
99289
99986
|
ctx.showThinkingPicker(alias);
|
|
99290
99987
|
return true;
|
|
99291
99988
|
}
|
|
99989
|
+
if (result.message.startsWith("__set_editor__:")) {
|
|
99990
|
+
const command = result.message.slice(15);
|
|
99991
|
+
await ctx.setEditorCommand(command);
|
|
99992
|
+
return true;
|
|
99993
|
+
}
|
|
99994
|
+
if (result.message.startsWith("__set_theme__:")) {
|
|
99995
|
+
const theme = result.message.slice(14);
|
|
99996
|
+
if (isTheme(theme)) {
|
|
99997
|
+
await ctx.setTheme(theme);
|
|
99998
|
+
return true;
|
|
99999
|
+
}
|
|
100000
|
+
}
|
|
99292
100001
|
if (result.message.startsWith("__send_as_message__:")) {
|
|
99293
100002
|
const msg = result.message.slice(20);
|
|
99294
100003
|
ctx.sendAsMessage(msg);
|
|
@@ -99667,72 +100376,6 @@ function dispatchEvent(event, ectx, sendQueued) {
|
|
|
99667
100376
|
}
|
|
99668
100377
|
}
|
|
99669
100378
|
//#endregion
|
|
99670
|
-
//#region src/tui/components/dialogs/choice-picker.ts
|
|
99671
|
-
/**
|
|
99672
|
-
* ChoicePicker — modal single-select list for slash commands that ask
|
|
99673
|
-
* the user to pick from a small set of preset values.
|
|
99674
|
-
*
|
|
99675
|
-
* Mirrors SessionPickerComponent's container-replacement pattern: host
|
|
99676
|
-
* calls `showChoicePicker(...)` which clears the editor container,
|
|
99677
|
-
* addChild(picker), setFocus(picker); the picker invokes `onSelect` or
|
|
99678
|
-
* `onCancel`, and the host tears it down.
|
|
99679
|
-
*/
|
|
99680
|
-
const CURRENT_MARK = "← current";
|
|
99681
|
-
var ChoicePickerComponent = class extends Container {
|
|
99682
|
-
focused = false;
|
|
99683
|
-
opts;
|
|
99684
|
-
selectedIndex;
|
|
99685
|
-
constructor(opts) {
|
|
99686
|
-
super();
|
|
99687
|
-
this.opts = opts;
|
|
99688
|
-
const currentIdx = opts.options.findIndex((o) => o.value === opts.currentValue);
|
|
99689
|
-
this.selectedIndex = Math.max(currentIdx, 0);
|
|
99690
|
-
}
|
|
99691
|
-
handleInput(data) {
|
|
99692
|
-
if (matchesKey(data, Key.escape)) {
|
|
99693
|
-
this.opts.onCancel();
|
|
99694
|
-
return;
|
|
99695
|
-
}
|
|
99696
|
-
if (matchesKey(data, Key.up)) {
|
|
99697
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
99698
|
-
return;
|
|
99699
|
-
}
|
|
99700
|
-
if (matchesKey(data, Key.down)) {
|
|
99701
|
-
this.selectedIndex = Math.min(this.opts.options.length - 1, this.selectedIndex + 1);
|
|
99702
|
-
return;
|
|
99703
|
-
}
|
|
99704
|
-
if (matchesKey(data, Key.enter)) {
|
|
99705
|
-
const chosen = this.opts.options[this.selectedIndex];
|
|
99706
|
-
if (chosen !== void 0) this.opts.onSelect(chosen.value);
|
|
99707
|
-
return;
|
|
99708
|
-
}
|
|
99709
|
-
}
|
|
99710
|
-
render(width) {
|
|
99711
|
-
const { colors } = this.opts;
|
|
99712
|
-
const hint = this.opts.hint ?? "↑↓ navigate · Enter select · Esc cancel";
|
|
99713
|
-
const lines = [
|
|
99714
|
-
chalk.hex(colors.primary)("─".repeat(width)),
|
|
99715
|
-
chalk.hex(colors.primary).bold(` ${this.opts.title}`),
|
|
99716
|
-
chalk.hex(colors.textMuted)(` ${hint}`),
|
|
99717
|
-
""
|
|
99718
|
-
];
|
|
99719
|
-
for (let i = 0; i < this.opts.options.length; i++) {
|
|
99720
|
-
const opt = this.opts.options[i];
|
|
99721
|
-
const isSelected = i === this.selectedIndex;
|
|
99722
|
-
const isCurrent = opt.value === this.opts.currentValue;
|
|
99723
|
-
const pointer = isSelected ? "❯" : " ";
|
|
99724
|
-
const labelStyle = isSelected ? chalk.hex(colors.primary).bold : chalk.hex(colors.text);
|
|
99725
|
-
let line = chalk.hex(isSelected ? colors.primary : colors.textDim)(` ${pointer} `);
|
|
99726
|
-
line += labelStyle(opt.label);
|
|
99727
|
-
if (isCurrent) line += " " + chalk.hex(colors.success)(CURRENT_MARK);
|
|
99728
|
-
lines.push(line);
|
|
99729
|
-
}
|
|
99730
|
-
lines.push("");
|
|
99731
|
-
lines.push(chalk.hex(colors.primary)("─".repeat(width)));
|
|
99732
|
-
return lines;
|
|
99733
|
-
}
|
|
99734
|
-
};
|
|
99735
|
-
//#endregion
|
|
99736
100379
|
//#region src/tui/panels/editor-picker.ts
|
|
99737
100380
|
/**
|
|
99738
100381
|
* `/editor` modal — picks an external editor command.
|
|
@@ -99777,12 +100420,22 @@ function showEditorPicker(state, hooks) {
|
|
|
99777
100420
|
function closeEditorPicker(state) {
|
|
99778
100421
|
unmountPanel(state);
|
|
99779
100422
|
}
|
|
99780
|
-
function applyEditorChoice(state, value, hooks) {
|
|
99781
|
-
if (value === (state.appState.editorCommand ?? "")) {
|
|
100423
|
+
async function applyEditorChoice(state, value, hooks) {
|
|
100424
|
+
if (value === (state.appState.editorCommand ?? "") && value.length > 0) {
|
|
99782
100425
|
emitStatus(state, `Editor unchanged: ${value.length > 0 ? value : "auto-detect"}`);
|
|
99783
100426
|
return;
|
|
99784
100427
|
}
|
|
99785
|
-
|
|
100428
|
+
const editorCommand = value.length > 0 ? value : null;
|
|
100429
|
+
try {
|
|
100430
|
+
await saveTuiConfig({
|
|
100431
|
+
theme: state.appState.theme,
|
|
100432
|
+
editorCommand
|
|
100433
|
+
});
|
|
100434
|
+
} catch (error) {
|
|
100435
|
+
emitStatus(state, `Failed to save editor: ${error instanceof Error ? error.message : String(error)}`, state.colors.error);
|
|
100436
|
+
return;
|
|
100437
|
+
}
|
|
100438
|
+
setState(state, { editorCommand }, hooks);
|
|
99786
100439
|
emitStatus(state, value.length > 0 ? `Editor set to "${value}".` : "Editor set to auto-detect ($VISUAL / $EDITOR).");
|
|
99787
100440
|
}
|
|
99788
100441
|
//#endregion
|
|
@@ -100357,7 +101010,7 @@ var MoonLoader = class extends Text {
|
|
|
100357
101010
|
this.ui = ui;
|
|
100358
101011
|
this.frames = style === "moon" ? MOON_PHASES : BRAILLE_FRAMES;
|
|
100359
101012
|
this.interval = style === "moon" ? MOON_INTERVAL : BRAILLE_INTERVAL;
|
|
100360
|
-
|
|
101013
|
+
this.colorFn = colorFn;
|
|
100361
101014
|
this.label = label;
|
|
100362
101015
|
this.start();
|
|
100363
101016
|
}
|
|
@@ -100476,9 +101129,11 @@ function updateQueueDisplay(state) {
|
|
|
100476
101129
|
state.queueContainer.clear();
|
|
100477
101130
|
const queued = state.queuedMessages;
|
|
100478
101131
|
if (queued.length === 0) return;
|
|
100479
|
-
|
|
101132
|
+
const accent = chalk.hex(state.colors.accent);
|
|
101133
|
+
const dim = chalk.hex(state.colors.textDim);
|
|
101134
|
+
for (const item of queued) state.queueContainer.addChild(new Text(accent(` ❯ ${item.text}`), 0, 0));
|
|
100480
101135
|
const hint = state.appState.isCompacting && !state.appState.isStreaming ? " ↑ to edit · will send after compaction" : " ↑ to edit · ctrl-s to steer immediately";
|
|
100481
|
-
state.queueContainer.addChild(new Text(
|
|
101136
|
+
state.queueContainer.addChild(new Text(dim(hint), 0, 0));
|
|
100482
101137
|
}
|
|
100483
101138
|
//#endregion
|
|
100484
101139
|
//#region src/tui/reverse-rpc/approval/adapter.ts
|
|
@@ -100816,9 +101471,18 @@ function truncateOneLine(text, max) {
|
|
|
100816
101471
|
}
|
|
100817
101472
|
const DIFF_SUMMARY_MAX_LINES = 10;
|
|
100818
101473
|
const CONTENT_SUMMARY_MAX_LINES = 10;
|
|
100819
|
-
function
|
|
101474
|
+
function makeBlockStyles(colors) {
|
|
101475
|
+
return {
|
|
101476
|
+
strong: (s) => chalk.hex(colors.textStrong)(s),
|
|
101477
|
+
dim: (s) => chalk.hex(colors.textDim)(s),
|
|
101478
|
+
accent: (s) => chalk.hex(colors.accent)(s),
|
|
101479
|
+
gutter: (s) => chalk.hex(colors.diffGutter)(s),
|
|
101480
|
+
errorBold: (s) => chalk.bold.hex(colors.error)(s)
|
|
101481
|
+
};
|
|
101482
|
+
}
|
|
101483
|
+
function renderDisplayBlock(block, expanded, s, colors) {
|
|
100820
101484
|
switch (block.type) {
|
|
100821
|
-
case "diff": return renderDiffLinesClustered(block.old_text, block.new_text, block.path, {
|
|
101485
|
+
case "diff": return renderDiffLinesClustered(block.old_text, block.new_text, block.path, colors, {
|
|
100822
101486
|
contextLines: 3,
|
|
100823
101487
|
...expanded ? {} : { maxLines: DIFF_SUMMARY_MAX_LINES }
|
|
100824
101488
|
});
|
|
@@ -100827,42 +101491,42 @@ function renderDisplayBlock(block, expanded) {
|
|
|
100827
101491
|
const allLines = highlightLines(block.content, lang);
|
|
100828
101492
|
const cap = expanded ? allLines.length : CONTENT_SUMMARY_MAX_LINES;
|
|
100829
101493
|
const shown = allLines.slice(0, cap);
|
|
100830
|
-
const lines = [
|
|
100831
|
-
for (const [i, line] of shown.entries()) lines.push(
|
|
101494
|
+
const lines = [s.strong(block.path)];
|
|
101495
|
+
for (const [i, line] of shown.entries()) lines.push(s.gutter(String(i + 1).padStart(4) + " ") + line);
|
|
100832
101496
|
const remaining = allLines.length - shown.length;
|
|
100833
|
-
if (remaining > 0) lines.push(
|
|
101497
|
+
if (remaining > 0) lines.push(s.dim(` … ${String(remaining)} more line${remaining > 1 ? "s" : ""} hidden (ctrl+o to expand)`));
|
|
100834
101498
|
return lines;
|
|
100835
101499
|
}
|
|
100836
101500
|
case "shell": {
|
|
100837
101501
|
const lines = [];
|
|
100838
|
-
if (block.cwd !== void 0 && block.cwd.length > 0) lines.push(
|
|
100839
|
-
if (block.danger !== void 0) lines.push(
|
|
101502
|
+
if (block.cwd !== void 0 && block.cwd.length > 0) lines.push(s.dim(`cwd: ${block.cwd}`));
|
|
101503
|
+
if (block.danger !== void 0) lines.push(s.errorBold(`⚠ potentially destructive: ${block.danger}`));
|
|
100840
101504
|
(block.command.length > 0 ? block.command.split("\n") : [""]).forEach((cmdLine, idx) => {
|
|
100841
|
-
const prefix = idx === 0 ?
|
|
100842
|
-
lines.push(`${prefix} ${
|
|
101505
|
+
const prefix = idx === 0 ? s.accent("$") : s.dim("·");
|
|
101506
|
+
lines.push(`${prefix} ${s.strong(cmdLine)}`);
|
|
100843
101507
|
});
|
|
100844
|
-
if (block.description !== void 0 && block.description.length > 0) lines.push(` ${
|
|
101508
|
+
if (block.description !== void 0 && block.description.length > 0) lines.push(` ${s.dim(block.description)}`);
|
|
100845
101509
|
return lines;
|
|
100846
101510
|
}
|
|
100847
101511
|
case "file_op": {
|
|
100848
|
-
const lines = [`${
|
|
100849
|
-
if (block.detail !== void 0 && block.detail.length > 0) lines.push(
|
|
101512
|
+
const lines = [`${s.accent(block.operation.padEnd(5))} ${s.strong(block.path)}`];
|
|
101513
|
+
if (block.detail !== void 0 && block.detail.length > 0) lines.push(s.dim(block.detail));
|
|
100850
101514
|
return lines;
|
|
100851
101515
|
}
|
|
100852
|
-
case "url_fetch": return [`${
|
|
101516
|
+
case "url_fetch": return [`${s.accent((block.method ?? "GET").toUpperCase().padEnd(5))} ${s.strong(block.url)}`];
|
|
100853
101517
|
case "search": {
|
|
100854
|
-
const lines = [`${
|
|
100855
|
-
if (block.scope !== void 0 && block.scope.length > 0) lines.push(
|
|
101518
|
+
const lines = [`${s.accent("search")} ${s.strong(block.query)}`];
|
|
101519
|
+
if (block.scope !== void 0 && block.scope.length > 0) lines.push(s.dim(`scope: ${block.scope}`));
|
|
100856
101520
|
return lines;
|
|
100857
101521
|
}
|
|
100858
101522
|
case "invocation": {
|
|
100859
|
-
const lines = [`${
|
|
100860
|
-
if (block.description !== void 0 && block.description.length > 0) lines.push(
|
|
101523
|
+
const lines = [`${s.accent(block.kind.padEnd(5))} ${s.strong(block.name)}`];
|
|
101524
|
+
if (block.description !== void 0 && block.description.length > 0) lines.push(s.dim(truncateOneLine(block.description, 200)));
|
|
100861
101525
|
return lines;
|
|
100862
101526
|
}
|
|
100863
|
-
case "brief": return block.text ? block.text.split("\n").map((line) => line.length > 0 ?
|
|
100864
|
-
case "background_task": return [
|
|
100865
|
-
case "todo": return block.items.map((item) =>
|
|
101527
|
+
case "brief": return block.text ? block.text.split("\n").map((line) => line.length > 0 ? s.strong(line) : "") : [];
|
|
101528
|
+
case "background_task": return [s.strong(`${block.status} ${block.kind} task ${block.task_id}: ${block.description}`)];
|
|
101529
|
+
case "todo": return block.items.map((item) => s.strong(`- [${item.status}] ${item.title}`));
|
|
100866
101530
|
default: return [];
|
|
100867
101531
|
}
|
|
100868
101532
|
}
|
|
@@ -100879,14 +101543,13 @@ function isDuplicateBriefBlock(block, description) {
|
|
|
100879
101543
|
if (blockLines.length <= 1) return false;
|
|
100880
101544
|
return normalizeApprovalText(blockLines.slice(1).join("\n")) === normalizedDescription;
|
|
100881
101545
|
}
|
|
100882
|
-
const borderColor = chalk.yellow;
|
|
100883
|
-
const selectColor = chalk.cyan;
|
|
100884
101546
|
function headerFor(toolName) {
|
|
100885
101547
|
switch (toolName) {
|
|
100886
101548
|
case "Bash": return "Run this command?";
|
|
100887
101549
|
case "Write": return "Write this file?";
|
|
100888
101550
|
case "Edit": return "Apply these edits?";
|
|
100889
101551
|
case "TaskStop": return "Stop this task?";
|
|
101552
|
+
case "ExitPlanMode": return "Ready to build with this plan?";
|
|
100890
101553
|
default: return `Approve ${toolName}?`;
|
|
100891
101554
|
}
|
|
100892
101555
|
}
|
|
@@ -100898,10 +101561,12 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
100898
101561
|
expanded = false;
|
|
100899
101562
|
onResponse;
|
|
100900
101563
|
request;
|
|
100901
|
-
|
|
101564
|
+
colors;
|
|
101565
|
+
constructor(request, onResponse, colors) {
|
|
100902
101566
|
super();
|
|
100903
101567
|
this.request = request;
|
|
100904
101568
|
this.onResponse = onResponse;
|
|
101569
|
+
this.colors = colors;
|
|
100905
101570
|
this.feedbackInput.onSubmit = (value) => {
|
|
100906
101571
|
this.submit(this.selectedIndex, value);
|
|
100907
101572
|
};
|
|
@@ -100972,21 +101637,27 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
100972
101637
|
this.ensureValidSelection();
|
|
100973
101638
|
this.feedbackInput.focused = this.focused && this.feedbackMode;
|
|
100974
101639
|
const { data } = this.request;
|
|
101640
|
+
const blockStyles = makeBlockStyles(this.colors);
|
|
101641
|
+
const borderColor = chalk.hex(this.colors.borderFocus);
|
|
101642
|
+
const borderColorBold = chalk.bold.hex(this.colors.borderFocus);
|
|
101643
|
+
const selectColorBold = chalk.bold.hex(this.colors.accent);
|
|
101644
|
+
const dim = chalk.hex(this.colors.textDim);
|
|
101645
|
+
const strong = chalk.hex(this.colors.textStrong);
|
|
100975
101646
|
const horizontalBar = borderColor("─".repeat(width));
|
|
100976
101647
|
const indent = (s) => ` ${s}`;
|
|
100977
101648
|
const title = headerFor(data.tool_name);
|
|
100978
|
-
const lines = [horizontalBar, indent(`${
|
|
101649
|
+
const lines = [horizontalBar, indent(`${borderColorBold("▶")} ${borderColorBold(title)}`)];
|
|
100979
101650
|
const visibleBlocks = data.display.filter((block) => !isDuplicateBriefBlock(block, data.description)).slice(0, 5);
|
|
100980
101651
|
const hasExpandable = visibleBlocks.some((block) => block.type === "diff" || block.type === "file_content");
|
|
100981
101652
|
if (visibleBlocks.length > 0) {
|
|
100982
101653
|
lines.push("");
|
|
100983
101654
|
for (const block of visibleBlocks) {
|
|
100984
|
-
const blockLines = renderDisplayBlock(block, this.expanded);
|
|
101655
|
+
const blockLines = renderDisplayBlock(block, this.expanded, blockStyles, this.colors);
|
|
100985
101656
|
for (const line of blockLines) lines.push(indent(line));
|
|
100986
101657
|
}
|
|
100987
101658
|
} else if (data.description) {
|
|
100988
101659
|
lines.push("");
|
|
100989
|
-
for (const descLine of data.description.split("\n")) lines.push(indent(
|
|
101660
|
+
for (const descLine of data.description.split("\n")) lines.push(indent(dim(descLine)));
|
|
100990
101661
|
}
|
|
100991
101662
|
lines.push("");
|
|
100992
101663
|
for (let idx = 0; idx < data.choices.length; idx++) {
|
|
@@ -100996,14 +101667,14 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
100996
101667
|
const num = idx + 1;
|
|
100997
101668
|
const labelWithNum = `${String(num)}. ${option.label}`;
|
|
100998
101669
|
if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(this.renderInlineFeedbackLine(width - 2, labelWithNum)));
|
|
100999
|
-
else if (isSelected) lines.push(indent(`${
|
|
101000
|
-
else lines.push(indent(
|
|
101670
|
+
else if (isSelected) lines.push(indent(`${selectColorBold("▶")} ${selectColorBold(labelWithNum)}`));
|
|
101671
|
+
else lines.push(indent(strong(` ${labelWithNum}`)));
|
|
101001
101672
|
}
|
|
101002
101673
|
lines.push("");
|
|
101003
|
-
if (this.feedbackMode) lines.push(indent(
|
|
101674
|
+
if (this.feedbackMode) lines.push(indent(dim("Type feedback · ↵ submit.")));
|
|
101004
101675
|
else {
|
|
101005
101676
|
const expandHint = hasExpandable ? ` · ctrl+o ${this.expanded ? "collapse" : "expand"}` : "";
|
|
101006
|
-
lines.push(indent(
|
|
101677
|
+
lines.push(indent(dim(`↑/↓ select · ${buildNumericHint(data.choices.length)} choose · ↵ confirm${expandHint}`)));
|
|
101007
101678
|
}
|
|
101008
101679
|
lines.push(horizontalBar);
|
|
101009
101680
|
return lines.map((line) => truncateToWidth(line, width));
|
|
@@ -101023,7 +101694,8 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
101023
101694
|
if (this.selectedIndex < 0 || this.selectedIndex >= count) this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, count - 1));
|
|
101024
101695
|
}
|
|
101025
101696
|
renderInlineFeedbackLine(width, labelWithNum) {
|
|
101026
|
-
const
|
|
101697
|
+
const selectColorBold = chalk.bold.hex(this.colors.accent);
|
|
101698
|
+
const prefix = `${selectColorBold("▶")} ${selectColorBold(labelWithNum)} `;
|
|
101027
101699
|
const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
|
|
101028
101700
|
const inputLine = this.feedbackInput.render(inputWidth)[0] ?? "> ";
|
|
101029
101701
|
return prefix + (inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine);
|
|
@@ -101044,7 +101716,7 @@ function showApprovalPanel(state, payload) {
|
|
|
101044
101716
|
updateActivityPane(state);
|
|
101045
101717
|
mountPanel(state, new ApprovalPanelComponent({ data: payload }, (response) => {
|
|
101046
101718
|
state.approvalController.respond(adaptPanelResponse(response));
|
|
101047
|
-
}));
|
|
101719
|
+
}, state.colors));
|
|
101048
101720
|
}
|
|
101049
101721
|
function hideApprovalPanel(state) {
|
|
101050
101722
|
state.livePane.pendingApproval = null;
|
|
@@ -101497,7 +102169,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
101497
102169
|
if (question === void 0) continue;
|
|
101498
102170
|
const label = question.header !== void 0 && question.header.length > 0 ? question.header : `Q${String(i + 1)}`;
|
|
101499
102171
|
if (i === this.currentTab) tabs.push(active(` ${label} `));
|
|
101500
|
-
else if (this.isAnswered(i)) tabs.push(chalk.
|
|
102172
|
+
else if (this.isAnswered(i)) tabs.push(chalk.hex(this.colors.success)(`(✓) ${label}`));
|
|
101501
102173
|
else tabs.push(dim(`(○) ${label}`));
|
|
101502
102174
|
}
|
|
101503
102175
|
const submitLabel = "Submit";
|
|
@@ -102258,12 +102930,17 @@ function setupEditor(state, cb) {
|
|
|
102258
102930
|
editor.onCtrlS = () => {
|
|
102259
102931
|
if (!state.appState.isStreaming || state.appState.isCompacting) return;
|
|
102260
102932
|
const text = editor.getText().trim();
|
|
102261
|
-
|
|
102933
|
+
const queuedTexts = state.queuedMessages.map((m) => m.text);
|
|
102934
|
+
clearQueue(state);
|
|
102935
|
+
const parts = [];
|
|
102936
|
+
for (const q of queuedTexts) {
|
|
102937
|
+
const trimmed = q.trim();
|
|
102938
|
+
if (trimmed.length > 0) parts.push(trimmed);
|
|
102939
|
+
}
|
|
102940
|
+
if (text.length > 0) parts.push(text);
|
|
102941
|
+
if (parts.length > 0) {
|
|
102262
102942
|
editor.setText("");
|
|
102263
|
-
cb.onSteerFromInput(
|
|
102264
|
-
} else {
|
|
102265
|
-
const first = cb.onSteerDequeueFirst();
|
|
102266
|
-
if (first !== void 0) cb.onSteerFromInput(first);
|
|
102943
|
+
cb.onSteerFromInput(parts);
|
|
102267
102944
|
}
|
|
102268
102945
|
cb.onAfterQueueMutation();
|
|
102269
102946
|
state.ui.requestRender();
|
|
@@ -102403,7 +103080,8 @@ var KimiTUI = class {
|
|
|
102403
103080
|
this.state = createTUIState({
|
|
102404
103081
|
client,
|
|
102405
103082
|
initialAppState: initialState,
|
|
102406
|
-
startup: options.startup
|
|
103083
|
+
startup: options.startup,
|
|
103084
|
+
...options.resolvedTheme !== void 0 ? { resolvedTheme: options.resolvedTheme } : {}
|
|
102407
103085
|
});
|
|
102408
103086
|
this.stateHooks = {
|
|
102409
103087
|
syncFooter: () => this.state.footer.setState(this.state.appState),
|
|
@@ -102453,14 +103131,14 @@ var KimiTUI = class {
|
|
|
102453
103131
|
this.state.ui.setFocus(this.state.editor);
|
|
102454
103132
|
this.state.ui.start();
|
|
102455
103133
|
attachFooterFeed(this.state, buildFooterFeedHandlers(this.state));
|
|
102456
|
-
if (this.state.startupState === "picker") {
|
|
102457
|
-
bootstrapFromPicker(this.state, this.buildSessionBootstrapHooks());
|
|
102458
|
-
return;
|
|
102459
|
-
}
|
|
102460
103134
|
if (this.state.startupNotice !== void 0) {
|
|
102461
103135
|
emitMuted(this.state, this.state.startupNotice);
|
|
102462
103136
|
this.state.startupNotice = void 0;
|
|
102463
103137
|
}
|
|
103138
|
+
if (this.state.startupState === "picker") {
|
|
103139
|
+
bootstrapFromPicker(this.state, this.buildSessionBootstrapHooks());
|
|
103140
|
+
return;
|
|
103141
|
+
}
|
|
102464
103142
|
if (shouldReplayHistory) await hydrateTranscriptFromReplay(this.state, this.stateHooks, this.state.appState.sessionId);
|
|
102465
103143
|
this.startWireSubscription();
|
|
102466
103144
|
fetchSessions(this.state);
|
|
@@ -102625,11 +103303,14 @@ var KimiTUI = class {
|
|
|
102625
103303
|
showHelpPanel: () => showHelpPanel(this.state),
|
|
102626
103304
|
showSessionPicker: () => showSessionPicker(this.state, this.buildSessionHooks()),
|
|
102627
103305
|
showEditorPicker: () => showEditorPicker(this.state, this.stateHooks),
|
|
103306
|
+
showThemePicker: () => showThemePicker(this.state, this.stateHooks),
|
|
102628
103307
|
showModelPicker: () => showModelPicker(this.state, this.stateHooks),
|
|
102629
103308
|
showThinkingPicker: (alias) => showThinkingPicker(this.state, alias, this.stateHooks),
|
|
102630
103309
|
showUsage: () => {
|
|
102631
103310
|
showUsage(this.state);
|
|
102632
103311
|
},
|
|
103312
|
+
setEditorCommand: (command) => applyEditorChoice(this.state, command, this.stateHooks),
|
|
103313
|
+
setTheme: (theme) => applyThemeChoice(this.state, theme, this.stateHooks),
|
|
102633
103314
|
sendAsMessage: (text) => sendMessage(this.state, (e) => this.addEntry(e), text),
|
|
102634
103315
|
activateSkill: (name, args, fullPrompt) => sendSkillActivation(this.state, (e) => this.addEntry(e), name, args, fullPrompt),
|
|
102635
103316
|
performReload: (action) => performReload(this.state, action, this.buildInputHooks())
|
|
@@ -102663,6 +103344,16 @@ function toInitialSessionIntent(opts) {
|
|
|
102663
103344
|
}
|
|
102664
103345
|
async function runShell(opts, version) {
|
|
102665
103346
|
const ctx = await createKimiAgent();
|
|
103347
|
+
let tuiConfig;
|
|
103348
|
+
let configWarning;
|
|
103349
|
+
try {
|
|
103350
|
+
tuiConfig = await loadTuiConfig();
|
|
103351
|
+
} catch (error) {
|
|
103352
|
+
if (!(error instanceof TuiConfigParseError)) throw error;
|
|
103353
|
+
tuiConfig = error.fallback;
|
|
103354
|
+
configWarning = error.message;
|
|
103355
|
+
}
|
|
103356
|
+
const resolvedTheme = tuiConfig.theme === "auto" ? await detectTerminalTheme() : tuiConfig.theme;
|
|
102666
103357
|
const workDir = process.cwd();
|
|
102667
103358
|
const initialState = {
|
|
102668
103359
|
model: ctx.model,
|
|
@@ -102679,18 +103370,22 @@ async function runShell(opts, version) {
|
|
|
102679
103370
|
isReplaying: false,
|
|
102680
103371
|
streamingPhase: "idle",
|
|
102681
103372
|
streamingStartTime: 0,
|
|
102682
|
-
theme:
|
|
103373
|
+
theme: tuiConfig.theme,
|
|
102683
103374
|
version,
|
|
102684
|
-
editorCommand:
|
|
103375
|
+
editorCommand: tuiConfig.editorCommand,
|
|
102685
103376
|
availableModels: ctx.availableModels,
|
|
102686
103377
|
sessionTitle: null
|
|
102687
103378
|
};
|
|
102688
|
-
const tui = new KimiTUI(ctx.client, initialState, {
|
|
102689
|
-
|
|
102690
|
-
|
|
102691
|
-
|
|
102692
|
-
|
|
102693
|
-
|
|
103379
|
+
const tui = new KimiTUI(ctx.client, initialState, {
|
|
103380
|
+
startup: {
|
|
103381
|
+
initialSession: toInitialSessionIntent(opts),
|
|
103382
|
+
requestedPlanMode: opts.plan,
|
|
103383
|
+
requestedYolo: opts.yolo,
|
|
103384
|
+
startupNotice: configWarning,
|
|
103385
|
+
syncSessionRuntime: ctx.syncSessionRuntime
|
|
103386
|
+
},
|
|
103387
|
+
resolvedTheme
|
|
103388
|
+
});
|
|
102694
103389
|
tui.onExit = async () => {
|
|
102695
103390
|
const sessionId = tui.getCurrentSessionId();
|
|
102696
103391
|
const hasContent = tui.hasSessionContent();
|