kfc-code-cli 0.0.1-alpha.11 → 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 +1203 -612
- 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) {
|
|
@@ -95991,6 +96081,42 @@ function extractApprovedPlan(output) {
|
|
|
95991
96081
|
if (markerIndex < 0) return "";
|
|
95992
96082
|
return output.slice(markerIndex + 17).trim();
|
|
95993
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
|
+
}
|
|
95994
96120
|
const STREAMING_FIELD_RE$1 = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
|
|
95995
96121
|
function unescapeJsonString$1(s) {
|
|
95996
96122
|
return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
|
|
@@ -96116,6 +96242,14 @@ var ToolCallComponent = class extends Container {
|
|
|
96116
96242
|
colors;
|
|
96117
96243
|
ui;
|
|
96118
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;
|
|
96119
96253
|
headerText;
|
|
96120
96254
|
callPreviewEndIndex = 0;
|
|
96121
96255
|
subagentAgentId;
|
|
@@ -96160,6 +96294,26 @@ var ToolCallComponent = class extends Container {
|
|
|
96160
96294
|
this.rebuildBody();
|
|
96161
96295
|
this.ui?.requestRender();
|
|
96162
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
|
+
}
|
|
96163
96317
|
applySubagentReplay(subagent) {
|
|
96164
96318
|
if (subagent === void 0) return;
|
|
96165
96319
|
this.subagentAgentId = subagent.id;
|
|
@@ -96242,8 +96396,17 @@ var ToolCallComponent = class extends Container {
|
|
|
96242
96396
|
const isError = result?.is_error ?? false;
|
|
96243
96397
|
let bullet;
|
|
96244
96398
|
if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)("⏺ ");
|
|
96245
|
-
else bullet = chalk.
|
|
96246
|
-
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
|
+
}
|
|
96247
96410
|
if (toolCall.name === "AskUserQuestion") {
|
|
96248
96411
|
const label = isFinished ? isError ? "Could not collect your input" : "Collected your answers" : "Waiting for your input";
|
|
96249
96412
|
const tone = isError ? chalk.hex(colors.error) : chalk.hex(colors.primary);
|
|
@@ -96333,7 +96496,7 @@ var ToolCallComponent = class extends Container {
|
|
|
96333
96496
|
const oldStr = str(this.toolCall.args["old_string"]);
|
|
96334
96497
|
const newStr = str(this.toolCall.args["new_string"]);
|
|
96335
96498
|
if (oldStr.length === 0 && newStr.length === 0) return;
|
|
96336
|
-
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, {
|
|
96337
96500
|
contextLines: 3,
|
|
96338
96501
|
...shouldCap ? { maxLines: CALL_PREVIEW_LINES } : {}
|
|
96339
96502
|
});
|
|
@@ -96366,7 +96529,7 @@ var ToolCallComponent = class extends Container {
|
|
|
96366
96529
|
const oldStr = extractPartialStringField(streamText, "old_string") ?? "";
|
|
96367
96530
|
const newStr = extractPartialStringField(streamText, "new_string") ?? "";
|
|
96368
96531
|
if (oldStr.length === 0 && newStr.length === 0) return;
|
|
96369
|
-
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, {
|
|
96370
96533
|
contextLines: 3,
|
|
96371
96534
|
isIncomplete: true
|
|
96372
96535
|
});
|
|
@@ -96384,22 +96547,41 @@ var ToolCallComponent = class extends Container {
|
|
|
96384
96547
|
}
|
|
96385
96548
|
}
|
|
96386
96549
|
buildPlanPreview() {
|
|
96387
|
-
const plan =
|
|
96388
|
-
if (plan.length === 0
|
|
96389
|
-
|
|
96390
|
-
|
|
96391
|
-
renderPlanFromResult(result) {
|
|
96392
|
-
const plan = extractApprovedPlan(result.output);
|
|
96393
|
-
if (plan.length === 0) return false;
|
|
96394
|
-
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));
|
|
96395
96554
|
else this.addChild(new Text(chalk.dim(plan), 2, 0));
|
|
96396
|
-
|
|
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;
|
|
96397
96571
|
}
|
|
96398
96572
|
buildContent() {
|
|
96399
96573
|
const { result } = this;
|
|
96400
96574
|
if (result === void 0 || !result.output) return;
|
|
96401
96575
|
if (this.toolCall.name === "ExitPlanMode" && !result.is_error) {
|
|
96402
|
-
|
|
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
|
+
}
|
|
96403
96585
|
return;
|
|
96404
96586
|
}
|
|
96405
96587
|
if (this.toolCall.name === "SetTodoList" && !result.is_error) return;
|
|
@@ -96450,7 +96632,7 @@ var UserMessageComponent = class extends Container {
|
|
|
96450
96632
|
constructor(text, colors) {
|
|
96451
96633
|
super();
|
|
96452
96634
|
this.addChild(new Spacer(1));
|
|
96453
|
-
this.addChild(new Text(chalk.hex(colors.
|
|
96635
|
+
this.addChild(new Text(chalk.hex(colors.roleUser).bold("✨ " + text), 0, 0));
|
|
96454
96636
|
}
|
|
96455
96637
|
};
|
|
96456
96638
|
//#endregion
|
|
@@ -96473,14 +96655,16 @@ var WelcomeComponent = class {
|
|
|
96473
96655
|
const textWidth = Math.max(4, innerWidth - logoWidth - 2);
|
|
96474
96656
|
const rightRow0 = truncateToWidth(chalk.bold.hex(this.colors.primary)("Welcome to Kimi Code!"), textWidth, "…");
|
|
96475
96657
|
const isLoggedOut = !this.state.model;
|
|
96476
|
-
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, "…");
|
|
96477
96661
|
const headerLines = [primary(logo[0].padEnd(logoWidth)) + gap + rightRow0, primary(logo[1].padEnd(logoWidth)) + gap + rightRow1];
|
|
96478
|
-
const modelValue = isLoggedOut ? chalk.
|
|
96662
|
+
const modelValue = isLoggedOut ? chalk.hex(this.colors.warning)("(not signed in — run /login)") : this.state.model;
|
|
96479
96663
|
const infoLines = [
|
|
96480
|
-
|
|
96481
|
-
|
|
96482
|
-
|
|
96483
|
-
|
|
96664
|
+
labelStyle("Directory: ") + this.state.workDir,
|
|
96665
|
+
labelStyle("Session: ") + this.state.sessionId,
|
|
96666
|
+
labelStyle("Model: ") + modelValue,
|
|
96667
|
+
labelStyle("Version: ") + this.state.version
|
|
96484
96668
|
];
|
|
96485
96669
|
const contentLines = [
|
|
96486
96670
|
...headerLines,
|
|
@@ -96646,32 +96830,32 @@ function createTranscriptComponent(state, entry) {
|
|
|
96646
96830
|
if (state.toolOutputExpanded) tc.setExpanded(true);
|
|
96647
96831
|
return tc;
|
|
96648
96832
|
}
|
|
96649
|
-
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, entry.color);
|
|
96650
|
-
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);
|
|
96651
96835
|
default: return null;
|
|
96652
96836
|
}
|
|
96653
96837
|
}
|
|
96654
96838
|
function createAssistantEntry(state, content) {
|
|
96655
|
-
const component = new AssistantMessageComponent(state.markdownTheme);
|
|
96839
|
+
const component = new AssistantMessageComponent(state.markdownTheme, state.colors);
|
|
96656
96840
|
component.updateContent(content);
|
|
96657
96841
|
return component;
|
|
96658
96842
|
}
|
|
96659
|
-
function paintStatus(content, color) {
|
|
96660
|
-
if (color === void 0) return chalk.
|
|
96843
|
+
function paintStatus(content, colors, color) {
|
|
96844
|
+
if (color === void 0) return chalk.hex(colors.textDim)(content);
|
|
96661
96845
|
if (color.startsWith("#")) return chalk.hex(color)(content);
|
|
96662
96846
|
const fn = chalk[color];
|
|
96663
96847
|
return typeof fn === "function" ? fn(content) : content;
|
|
96664
96848
|
}
|
|
96665
|
-
function createStatusEntry(content, color) {
|
|
96849
|
+
function createStatusEntry(content, colors, color) {
|
|
96666
96850
|
const container = new Container();
|
|
96667
|
-
const styled = paintStatus(content, color);
|
|
96851
|
+
const styled = paintStatus(content, colors, color);
|
|
96668
96852
|
container.addChild(new Text(` ${styled}`, 0, 0));
|
|
96669
96853
|
return container;
|
|
96670
96854
|
}
|
|
96671
96855
|
function createNoticeEntry(title, detail, colors) {
|
|
96672
96856
|
const container = new Container();
|
|
96673
96857
|
container.addChild(new Spacer(1));
|
|
96674
|
-
container.addChild(new Text(` ${chalk.
|
|
96858
|
+
container.addChild(new Text(` ${chalk.hex(colors.textStrong)(title)}`, 0, 0));
|
|
96675
96859
|
if (detail !== void 0 && detail.length > 0) container.addChild(new Text(` ${chalk.hex(colors.textDim)(detail)}`, 0, 0));
|
|
96676
96860
|
return container;
|
|
96677
96861
|
}
|
|
@@ -96779,21 +96963,243 @@ function buildImagePart(att) {
|
|
|
96779
96963
|
};
|
|
96780
96964
|
}
|
|
96781
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
|
|
96782
97185
|
//#region src/tui/theme/pi-tui-theme.ts
|
|
96783
|
-
const HEADING_HASH_PREFIX = /^((?:\
|
|
97186
|
+
const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
96784
97187
|
function createMarkdownTheme(colors) {
|
|
96785
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);
|
|
96786
97192
|
return {
|
|
96787
97193
|
heading: (text) => chalk.bold.hex(colors.text)(stripHash(text)),
|
|
96788
97194
|
link: (text) => chalk.hex(colors.primary)(text),
|
|
96789
|
-
linkUrl: (text) =>
|
|
97195
|
+
linkUrl: (text) => muted(text),
|
|
96790
97196
|
code: (text) => chalk.hex(colors.primary)(text),
|
|
96791
97197
|
codeBlock: (text) => text,
|
|
96792
|
-
codeBlockBorder: (text) =>
|
|
96793
|
-
quote: (text) =>
|
|
96794
|
-
quoteBorder: (text) =>
|
|
96795
|
-
hr: (text) =>
|
|
96796
|
-
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(/^-/, "•")),
|
|
96797
97203
|
bold: (text) => chalk.bold(text),
|
|
96798
97204
|
italic: (text) => chalk.italic(text),
|
|
96799
97205
|
strikethrough: (text) => chalk.strikethrough(text),
|
|
@@ -96811,18 +97217,39 @@ function createMarkdownTheme(colors) {
|
|
|
96811
97217
|
};
|
|
96812
97218
|
}
|
|
96813
97219
|
function createEditorTheme(colors) {
|
|
97220
|
+
const muted = chalk.hex(colors.textMuted);
|
|
96814
97221
|
return {
|
|
96815
97222
|
borderColor: (s) => chalk.hex(colors.border)(s),
|
|
96816
97223
|
selectList: {
|
|
96817
97224
|
selectedPrefix: (s) => chalk.hex(colors.primary)(s),
|
|
96818
97225
|
selectedText: (s) => chalk.hex(colors.primary)(s),
|
|
96819
|
-
description: (s) =>
|
|
96820
|
-
scrollInfo: (s) =>
|
|
96821
|
-
noMatch: (s) =>
|
|
97226
|
+
description: (s) => muted(s),
|
|
97227
|
+
scrollInfo: (s) => muted(s),
|
|
97228
|
+
noMatch: (s) => muted(s)
|
|
96822
97229
|
}
|
|
96823
97230
|
};
|
|
96824
97231
|
}
|
|
96825
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
|
|
96826
97253
|
//#region src/tui/core/ui-ops.ts
|
|
96827
97254
|
function isExpandable(obj) {
|
|
96828
97255
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
@@ -96898,15 +97325,34 @@ function clearTranscriptAndRedraw(state) {
|
|
|
96898
97325
|
}
|
|
96899
97326
|
/** Use primary color for slash commands or while plan mode is active. */
|
|
96900
97327
|
function updateEditorBorderHighlight(state, text) {
|
|
96901
|
-
const editorTheme = createEditorTheme(state.colors);
|
|
96902
97328
|
const trimmed = (text ?? state.editor.getText()).trimStart();
|
|
96903
97329
|
if (state.appState.planMode || trimmed.startsWith("/")) {
|
|
96904
97330
|
const primary = state.colors.primary;
|
|
96905
97331
|
state.editor.borderColor = (s) => chalk.hex(primary)(s);
|
|
96906
|
-
} else state.editor.borderColor = editorTheme.borderColor;
|
|
97332
|
+
} else state.editor.borderColor = state.editorTheme.borderColor;
|
|
96907
97333
|
state.editor.slashHighlightHex = state.colors.primary;
|
|
96908
97334
|
state.ui.requestRender();
|
|
96909
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
|
+
}
|
|
96910
97356
|
//#endregion
|
|
96911
97357
|
//#region src/tui/handlers/subagent.ts
|
|
96912
97358
|
function handleSubagentSourceEvent(ectx, payload) {
|
|
@@ -97379,6 +97825,31 @@ const shellCommands = [
|
|
|
97379
97825
|
async execute() {
|
|
97380
97826
|
return ok$1("__show_usage__");
|
|
97381
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
|
+
}
|
|
97382
97853
|
}
|
|
97383
97854
|
];
|
|
97384
97855
|
//#endregion
|
|
@@ -98169,49 +98640,6 @@ var QuestionController = class extends ReverseRpcController {
|
|
|
98169
98640
|
}
|
|
98170
98641
|
};
|
|
98171
98642
|
//#endregion
|
|
98172
|
-
//#region src/tui/theme/colors.ts
|
|
98173
|
-
const darkColors = {
|
|
98174
|
-
primary: "#4FA8FF",
|
|
98175
|
-
primaryDim: "#2563EB",
|
|
98176
|
-
text: "#E0E0E0",
|
|
98177
|
-
textDim: "#888888",
|
|
98178
|
-
textMuted: "#555555",
|
|
98179
|
-
success: "#4EC87E",
|
|
98180
|
-
warning: "#E8A838",
|
|
98181
|
-
error: "#E85454",
|
|
98182
|
-
info: "#4FA8FF",
|
|
98183
|
-
border: "#444444",
|
|
98184
|
-
prompt: "#4FA8FF",
|
|
98185
|
-
spinner: "#4FA8FF",
|
|
98186
|
-
user: "#FFCB6B",
|
|
98187
|
-
assistant: "#E0E0E0",
|
|
98188
|
-
thinking: "#888888",
|
|
98189
|
-
toolCall: "#E8A838",
|
|
98190
|
-
status: "#888888"
|
|
98191
|
-
};
|
|
98192
|
-
const lightColors = {
|
|
98193
|
-
primary: "#1783ff",
|
|
98194
|
-
primaryDim: "#6ba7e8",
|
|
98195
|
-
text: "#1A1A1A",
|
|
98196
|
-
textDim: "#666666",
|
|
98197
|
-
textMuted: "#999999",
|
|
98198
|
-
success: "#16A34A",
|
|
98199
|
-
warning: "#CA8A04",
|
|
98200
|
-
error: "#DC2626",
|
|
98201
|
-
info: "#1783ff",
|
|
98202
|
-
border: "#CCCCCC",
|
|
98203
|
-
prompt: "#1783ff",
|
|
98204
|
-
spinner: "#1783ff",
|
|
98205
|
-
user: "#B45309",
|
|
98206
|
-
assistant: "#1A1A1A",
|
|
98207
|
-
thinking: "#666666",
|
|
98208
|
-
toolCall: "#CA8A04",
|
|
98209
|
-
status: "#666666"
|
|
98210
|
-
};
|
|
98211
|
-
function getColorPalette(theme) {
|
|
98212
|
-
return theme === "dark" ? darkColors : lightColors;
|
|
98213
|
-
}
|
|
98214
|
-
//#endregion
|
|
98215
98643
|
//#region src/tui/state.ts
|
|
98216
98644
|
const INITIAL_LIVE_PANE = {
|
|
98217
98645
|
mode: "idle",
|
|
@@ -98228,7 +98656,9 @@ const INITIAL_LIVE_PANE = {
|
|
|
98228
98656
|
*/
|
|
98229
98657
|
function createTUIState(options) {
|
|
98230
98658
|
const initialAppState = options.initialAppState;
|
|
98231
|
-
const
|
|
98659
|
+
const resolvedTheme = options.resolvedTheme ?? resolveThemeSync(initialAppState.theme);
|
|
98660
|
+
const colors = { ...getColorPalette(resolvedTheme) };
|
|
98661
|
+
const styles = createThemeStyles(colors);
|
|
98232
98662
|
const markdownTheme = createMarkdownTheme(colors);
|
|
98233
98663
|
const editorTheme = createEditorTheme(colors);
|
|
98234
98664
|
const terminal = new ProcessTerminal();
|
|
@@ -98261,10 +98691,13 @@ function createTUIState(options) {
|
|
|
98261
98691
|
footer,
|
|
98262
98692
|
editor,
|
|
98263
98693
|
colors,
|
|
98694
|
+
styles,
|
|
98264
98695
|
markdownTheme,
|
|
98696
|
+
editorTheme,
|
|
98697
|
+
resolvedTheme,
|
|
98265
98698
|
appState: { ...initialAppState },
|
|
98266
98699
|
startupState: "pending",
|
|
98267
|
-
startupNotice:
|
|
98700
|
+
startupNotice: options.startup.startupNotice,
|
|
98268
98701
|
livePane: { ...INITIAL_LIVE_PANE },
|
|
98269
98702
|
transcriptEntries: [],
|
|
98270
98703
|
toasts: [],
|
|
@@ -98439,6 +98872,9 @@ function dequeueFirst(state) {
|
|
|
98439
98872
|
state.queuedMessages = state.queuedMessages.slice(1);
|
|
98440
98873
|
return first.text;
|
|
98441
98874
|
}
|
|
98875
|
+
function clearQueue(state) {
|
|
98876
|
+
state.queuedMessages.length = 0;
|
|
98877
|
+
}
|
|
98442
98878
|
//#endregion
|
|
98443
98879
|
//#region src/tui/actions/wire-ops.ts
|
|
98444
98880
|
/** Immediately send a message through the wire client (no queue). */
|
|
@@ -98569,21 +99005,21 @@ function sendMessage(state, addEntry, input, options) {
|
|
|
98569
99005
|
*/
|
|
98570
99006
|
function steerMessage(state, addEntry, input) {
|
|
98571
99007
|
if (state.appState.isCompacting) {
|
|
98572
|
-
enqueueMessage(state,
|
|
99008
|
+
for (const part of input) enqueueMessage(state, part);
|
|
98573
99009
|
return;
|
|
98574
99010
|
}
|
|
98575
99011
|
if (!state.appState.isStreaming) {
|
|
98576
|
-
sendMessageInternal(state, addEntry,
|
|
99012
|
+
for (const part of input) sendMessageInternal(state, addEntry, part);
|
|
98577
99013
|
return;
|
|
98578
99014
|
}
|
|
98579
|
-
addEntry({
|
|
99015
|
+
for (const part of input) addEntry({
|
|
98580
99016
|
id: nextTranscriptId(),
|
|
98581
99017
|
kind: "user",
|
|
98582
99018
|
turnId: state.currentTurnId,
|
|
98583
99019
|
renderMode: "plain",
|
|
98584
|
-
content:
|
|
99020
|
+
content: part
|
|
98585
99021
|
});
|
|
98586
|
-
state.client.steer(state.appState.sessionId, input).catch((error) => {
|
|
99022
|
+
state.client.steer(state.appState.sessionId, input.join("\n\n")).catch((error) => {
|
|
98587
99023
|
const message = error instanceof Error ? error.message : String(error);
|
|
98588
99024
|
addEntry({
|
|
98589
99025
|
id: nextTranscriptId(),
|
|
@@ -99150,7 +99586,7 @@ var CompactionComponent = class extends Container {
|
|
|
99150
99586
|
}
|
|
99151
99587
|
buildHeader() {
|
|
99152
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)`) : ""}`;
|
|
99153
|
-
return `${this.blinkOn ? chalk.
|
|
99589
|
+
return `${this.blinkOn ? chalk.hex(this.colors.roleAssistant)("⏺ ") : " "}${chalk.hex(this.colors.primary).bold("Compacting context...")}`;
|
|
99154
99590
|
}
|
|
99155
99591
|
startBlink() {
|
|
99156
99592
|
this.blinkTimer = setInterval(() => {
|
|
@@ -99167,6 +99603,15 @@ var CompactionComponent = class extends Container {
|
|
|
99167
99603
|
}
|
|
99168
99604
|
};
|
|
99169
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
|
|
99170
99615
|
//#region src/tui/actions/stream-ops.ts
|
|
99171
99616
|
/**
|
|
99172
99617
|
* Streaming / tool-call / compaction render hooks.
|
|
@@ -99176,7 +99621,7 @@ var CompactionComponent = class extends Container {
|
|
|
99176
99621
|
* on `state` is touched — state-ops.ts owns AppState / LivePane.
|
|
99177
99622
|
*/
|
|
99178
99623
|
function onStreamingTextStart(state) {
|
|
99179
|
-
state.streamingComponent = new AssistantMessageComponent(state.markdownTheme);
|
|
99624
|
+
state.streamingComponent = new AssistantMessageComponent(state.markdownTheme, state.colors);
|
|
99180
99625
|
state.transcriptContainer.addChild(state.streamingComponent);
|
|
99181
99626
|
state.ui.requestRender();
|
|
99182
99627
|
}
|
|
@@ -99209,6 +99654,10 @@ function onToolCallStart(state, toolCall) {
|
|
|
99209
99654
|
state.pendingToolComponents.set(toolCall.id, tc);
|
|
99210
99655
|
state.transcriptContainer.addChild(tc);
|
|
99211
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
|
+
})();
|
|
99212
99661
|
}
|
|
99213
99662
|
function onToolCallEnd(state, toolCallId, result) {
|
|
99214
99663
|
const matchedCall = state.activeToolCalls.get(toolCallId);
|
|
@@ -99251,6 +99700,126 @@ function endCompaction(state, tokensBefore, tokensAfter) {
|
|
|
99251
99700
|
state.ui.requestRender();
|
|
99252
99701
|
}
|
|
99253
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
|
|
99254
99823
|
//#region src/tui/commands/skill-commands.ts
|
|
99255
99824
|
const SKILL_ACTIVATION_SENTINEL_PREFIX = "__activate_skill__:";
|
|
99256
99825
|
async function fetchSkills(client, sessionId) {
|
|
@@ -99319,6 +99888,27 @@ async function tryDispatchSkill(client, sessionId, name, args) {
|
|
|
99319
99888
|
}
|
|
99320
99889
|
//#endregion
|
|
99321
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
|
+
*/
|
|
99322
99912
|
async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
99323
99913
|
const parsed = parseSlashInput(input);
|
|
99324
99914
|
if (!parsed) return false;
|
|
@@ -99379,6 +99969,10 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
99379
99969
|
ctx.showEditorPicker();
|
|
99380
99970
|
return true;
|
|
99381
99971
|
}
|
|
99972
|
+
if (result.message === "__show_theme_picker__") {
|
|
99973
|
+
ctx.showThemePicker();
|
|
99974
|
+
return true;
|
|
99975
|
+
}
|
|
99382
99976
|
if (result.message === "__show_model_picker__") {
|
|
99383
99977
|
ctx.showModelPicker();
|
|
99384
99978
|
return true;
|
|
@@ -99392,6 +99986,18 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
99392
99986
|
ctx.showThinkingPicker(alias);
|
|
99393
99987
|
return true;
|
|
99394
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
|
+
}
|
|
99395
100001
|
if (result.message.startsWith("__send_as_message__:")) {
|
|
99396
100002
|
const msg = result.message.slice(20);
|
|
99397
100003
|
ctx.sendAsMessage(msg);
|
|
@@ -99770,72 +100376,6 @@ function dispatchEvent(event, ectx, sendQueued) {
|
|
|
99770
100376
|
}
|
|
99771
100377
|
}
|
|
99772
100378
|
//#endregion
|
|
99773
|
-
//#region src/tui/components/dialogs/choice-picker.ts
|
|
99774
|
-
/**
|
|
99775
|
-
* ChoicePicker — modal single-select list for slash commands that ask
|
|
99776
|
-
* the user to pick from a small set of preset values.
|
|
99777
|
-
*
|
|
99778
|
-
* Mirrors SessionPickerComponent's container-replacement pattern: host
|
|
99779
|
-
* calls `showChoicePicker(...)` which clears the editor container,
|
|
99780
|
-
* addChild(picker), setFocus(picker); the picker invokes `onSelect` or
|
|
99781
|
-
* `onCancel`, and the host tears it down.
|
|
99782
|
-
*/
|
|
99783
|
-
const CURRENT_MARK = "← current";
|
|
99784
|
-
var ChoicePickerComponent = class extends Container {
|
|
99785
|
-
focused = false;
|
|
99786
|
-
opts;
|
|
99787
|
-
selectedIndex;
|
|
99788
|
-
constructor(opts) {
|
|
99789
|
-
super();
|
|
99790
|
-
this.opts = opts;
|
|
99791
|
-
const currentIdx = opts.options.findIndex((o) => o.value === opts.currentValue);
|
|
99792
|
-
this.selectedIndex = Math.max(currentIdx, 0);
|
|
99793
|
-
}
|
|
99794
|
-
handleInput(data) {
|
|
99795
|
-
if (matchesKey(data, Key.escape)) {
|
|
99796
|
-
this.opts.onCancel();
|
|
99797
|
-
return;
|
|
99798
|
-
}
|
|
99799
|
-
if (matchesKey(data, Key.up)) {
|
|
99800
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
99801
|
-
return;
|
|
99802
|
-
}
|
|
99803
|
-
if (matchesKey(data, Key.down)) {
|
|
99804
|
-
this.selectedIndex = Math.min(this.opts.options.length - 1, this.selectedIndex + 1);
|
|
99805
|
-
return;
|
|
99806
|
-
}
|
|
99807
|
-
if (matchesKey(data, Key.enter)) {
|
|
99808
|
-
const chosen = this.opts.options[this.selectedIndex];
|
|
99809
|
-
if (chosen !== void 0) this.opts.onSelect(chosen.value);
|
|
99810
|
-
return;
|
|
99811
|
-
}
|
|
99812
|
-
}
|
|
99813
|
-
render(width) {
|
|
99814
|
-
const { colors } = this.opts;
|
|
99815
|
-
const hint = this.opts.hint ?? "↑↓ navigate · Enter select · Esc cancel";
|
|
99816
|
-
const lines = [
|
|
99817
|
-
chalk.hex(colors.primary)("─".repeat(width)),
|
|
99818
|
-
chalk.hex(colors.primary).bold(` ${this.opts.title}`),
|
|
99819
|
-
chalk.hex(colors.textMuted)(` ${hint}`),
|
|
99820
|
-
""
|
|
99821
|
-
];
|
|
99822
|
-
for (let i = 0; i < this.opts.options.length; i++) {
|
|
99823
|
-
const opt = this.opts.options[i];
|
|
99824
|
-
const isSelected = i === this.selectedIndex;
|
|
99825
|
-
const isCurrent = opt.value === this.opts.currentValue;
|
|
99826
|
-
const pointer = isSelected ? "❯" : " ";
|
|
99827
|
-
const labelStyle = isSelected ? chalk.hex(colors.primary).bold : chalk.hex(colors.text);
|
|
99828
|
-
let line = chalk.hex(isSelected ? colors.primary : colors.textDim)(` ${pointer} `);
|
|
99829
|
-
line += labelStyle(opt.label);
|
|
99830
|
-
if (isCurrent) line += " " + chalk.hex(colors.success)(CURRENT_MARK);
|
|
99831
|
-
lines.push(line);
|
|
99832
|
-
}
|
|
99833
|
-
lines.push("");
|
|
99834
|
-
lines.push(chalk.hex(colors.primary)("─".repeat(width)));
|
|
99835
|
-
return lines;
|
|
99836
|
-
}
|
|
99837
|
-
};
|
|
99838
|
-
//#endregion
|
|
99839
100379
|
//#region src/tui/panels/editor-picker.ts
|
|
99840
100380
|
/**
|
|
99841
100381
|
* `/editor` modal — picks an external editor command.
|
|
@@ -99880,12 +100420,22 @@ function showEditorPicker(state, hooks) {
|
|
|
99880
100420
|
function closeEditorPicker(state) {
|
|
99881
100421
|
unmountPanel(state);
|
|
99882
100422
|
}
|
|
99883
|
-
function applyEditorChoice(state, value, hooks) {
|
|
99884
|
-
if (value === (state.appState.editorCommand ?? "")) {
|
|
100423
|
+
async function applyEditorChoice(state, value, hooks) {
|
|
100424
|
+
if (value === (state.appState.editorCommand ?? "") && value.length > 0) {
|
|
99885
100425
|
emitStatus(state, `Editor unchanged: ${value.length > 0 ? value : "auto-detect"}`);
|
|
99886
100426
|
return;
|
|
99887
100427
|
}
|
|
99888
|
-
|
|
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);
|
|
99889
100439
|
emitStatus(state, value.length > 0 ? `Editor set to "${value}".` : "Editor set to auto-detect ($VISUAL / $EDITOR).");
|
|
99890
100440
|
}
|
|
99891
100441
|
//#endregion
|
|
@@ -100460,7 +101010,7 @@ var MoonLoader = class extends Text {
|
|
|
100460
101010
|
this.ui = ui;
|
|
100461
101011
|
this.frames = style === "moon" ? MOON_PHASES : BRAILLE_FRAMES;
|
|
100462
101012
|
this.interval = style === "moon" ? MOON_INTERVAL : BRAILLE_INTERVAL;
|
|
100463
|
-
|
|
101013
|
+
this.colorFn = colorFn;
|
|
100464
101014
|
this.label = label;
|
|
100465
101015
|
this.start();
|
|
100466
101016
|
}
|
|
@@ -100579,9 +101129,11 @@ function updateQueueDisplay(state) {
|
|
|
100579
101129
|
state.queueContainer.clear();
|
|
100580
101130
|
const queued = state.queuedMessages;
|
|
100581
101131
|
if (queued.length === 0) return;
|
|
100582
|
-
|
|
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));
|
|
100583
101135
|
const hint = state.appState.isCompacting && !state.appState.isStreaming ? " ↑ to edit · will send after compaction" : " ↑ to edit · ctrl-s to steer immediately";
|
|
100584
|
-
state.queueContainer.addChild(new Text(
|
|
101136
|
+
state.queueContainer.addChild(new Text(dim(hint), 0, 0));
|
|
100585
101137
|
}
|
|
100586
101138
|
//#endregion
|
|
100587
101139
|
//#region src/tui/reverse-rpc/approval/adapter.ts
|
|
@@ -100919,9 +101471,18 @@ function truncateOneLine(text, max) {
|
|
|
100919
101471
|
}
|
|
100920
101472
|
const DIFF_SUMMARY_MAX_LINES = 10;
|
|
100921
101473
|
const CONTENT_SUMMARY_MAX_LINES = 10;
|
|
100922
|
-
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) {
|
|
100923
101484
|
switch (block.type) {
|
|
100924
|
-
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, {
|
|
100925
101486
|
contextLines: 3,
|
|
100926
101487
|
...expanded ? {} : { maxLines: DIFF_SUMMARY_MAX_LINES }
|
|
100927
101488
|
});
|
|
@@ -100930,42 +101491,42 @@ function renderDisplayBlock(block, expanded) {
|
|
|
100930
101491
|
const allLines = highlightLines(block.content, lang);
|
|
100931
101492
|
const cap = expanded ? allLines.length : CONTENT_SUMMARY_MAX_LINES;
|
|
100932
101493
|
const shown = allLines.slice(0, cap);
|
|
100933
|
-
const lines = [
|
|
100934
|
-
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);
|
|
100935
101496
|
const remaining = allLines.length - shown.length;
|
|
100936
|
-
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)`));
|
|
100937
101498
|
return lines;
|
|
100938
101499
|
}
|
|
100939
101500
|
case "shell": {
|
|
100940
101501
|
const lines = [];
|
|
100941
|
-
if (block.cwd !== void 0 && block.cwd.length > 0) lines.push(
|
|
100942
|
-
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}`));
|
|
100943
101504
|
(block.command.length > 0 ? block.command.split("\n") : [""]).forEach((cmdLine, idx) => {
|
|
100944
|
-
const prefix = idx === 0 ?
|
|
100945
|
-
lines.push(`${prefix} ${
|
|
101505
|
+
const prefix = idx === 0 ? s.accent("$") : s.dim("·");
|
|
101506
|
+
lines.push(`${prefix} ${s.strong(cmdLine)}`);
|
|
100946
101507
|
});
|
|
100947
|
-
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)}`);
|
|
100948
101509
|
return lines;
|
|
100949
101510
|
}
|
|
100950
101511
|
case "file_op": {
|
|
100951
|
-
const lines = [`${
|
|
100952
|
-
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));
|
|
100953
101514
|
return lines;
|
|
100954
101515
|
}
|
|
100955
|
-
case "url_fetch": return [`${
|
|
101516
|
+
case "url_fetch": return [`${s.accent((block.method ?? "GET").toUpperCase().padEnd(5))} ${s.strong(block.url)}`];
|
|
100956
101517
|
case "search": {
|
|
100957
|
-
const lines = [`${
|
|
100958
|
-
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}`));
|
|
100959
101520
|
return lines;
|
|
100960
101521
|
}
|
|
100961
101522
|
case "invocation": {
|
|
100962
|
-
const lines = [`${
|
|
100963
|
-
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)));
|
|
100964
101525
|
return lines;
|
|
100965
101526
|
}
|
|
100966
|
-
case "brief": return block.text ? block.text.split("\n").map((line) => line.length > 0 ?
|
|
100967
|
-
case "background_task": return [
|
|
100968
|
-
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}`));
|
|
100969
101530
|
default: return [];
|
|
100970
101531
|
}
|
|
100971
101532
|
}
|
|
@@ -100982,8 +101543,6 @@ function isDuplicateBriefBlock(block, description) {
|
|
|
100982
101543
|
if (blockLines.length <= 1) return false;
|
|
100983
101544
|
return normalizeApprovalText(blockLines.slice(1).join("\n")) === normalizedDescription;
|
|
100984
101545
|
}
|
|
100985
|
-
const borderColor = chalk.yellow;
|
|
100986
|
-
const selectColor = chalk.cyan;
|
|
100987
101546
|
function headerFor(toolName) {
|
|
100988
101547
|
switch (toolName) {
|
|
100989
101548
|
case "Bash": return "Run this command?";
|
|
@@ -101002,10 +101561,12 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
101002
101561
|
expanded = false;
|
|
101003
101562
|
onResponse;
|
|
101004
101563
|
request;
|
|
101005
|
-
|
|
101564
|
+
colors;
|
|
101565
|
+
constructor(request, onResponse, colors) {
|
|
101006
101566
|
super();
|
|
101007
101567
|
this.request = request;
|
|
101008
101568
|
this.onResponse = onResponse;
|
|
101569
|
+
this.colors = colors;
|
|
101009
101570
|
this.feedbackInput.onSubmit = (value) => {
|
|
101010
101571
|
this.submit(this.selectedIndex, value);
|
|
101011
101572
|
};
|
|
@@ -101076,21 +101637,27 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
101076
101637
|
this.ensureValidSelection();
|
|
101077
101638
|
this.feedbackInput.focused = this.focused && this.feedbackMode;
|
|
101078
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);
|
|
101079
101646
|
const horizontalBar = borderColor("─".repeat(width));
|
|
101080
101647
|
const indent = (s) => ` ${s}`;
|
|
101081
101648
|
const title = headerFor(data.tool_name);
|
|
101082
|
-
const lines = [horizontalBar, indent(`${
|
|
101649
|
+
const lines = [horizontalBar, indent(`${borderColorBold("▶")} ${borderColorBold(title)}`)];
|
|
101083
101650
|
const visibleBlocks = data.display.filter((block) => !isDuplicateBriefBlock(block, data.description)).slice(0, 5);
|
|
101084
101651
|
const hasExpandable = visibleBlocks.some((block) => block.type === "diff" || block.type === "file_content");
|
|
101085
101652
|
if (visibleBlocks.length > 0) {
|
|
101086
101653
|
lines.push("");
|
|
101087
101654
|
for (const block of visibleBlocks) {
|
|
101088
|
-
const blockLines = renderDisplayBlock(block, this.expanded);
|
|
101655
|
+
const blockLines = renderDisplayBlock(block, this.expanded, blockStyles, this.colors);
|
|
101089
101656
|
for (const line of blockLines) lines.push(indent(line));
|
|
101090
101657
|
}
|
|
101091
101658
|
} else if (data.description) {
|
|
101092
101659
|
lines.push("");
|
|
101093
|
-
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)));
|
|
101094
101661
|
}
|
|
101095
101662
|
lines.push("");
|
|
101096
101663
|
for (let idx = 0; idx < data.choices.length; idx++) {
|
|
@@ -101100,14 +101667,14 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
101100
101667
|
const num = idx + 1;
|
|
101101
101668
|
const labelWithNum = `${String(num)}. ${option.label}`;
|
|
101102
101669
|
if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(this.renderInlineFeedbackLine(width - 2, labelWithNum)));
|
|
101103
|
-
else if (isSelected) lines.push(indent(`${
|
|
101104
|
-
else lines.push(indent(
|
|
101670
|
+
else if (isSelected) lines.push(indent(`${selectColorBold("▶")} ${selectColorBold(labelWithNum)}`));
|
|
101671
|
+
else lines.push(indent(strong(` ${labelWithNum}`)));
|
|
101105
101672
|
}
|
|
101106
101673
|
lines.push("");
|
|
101107
|
-
if (this.feedbackMode) lines.push(indent(
|
|
101674
|
+
if (this.feedbackMode) lines.push(indent(dim("Type feedback · ↵ submit.")));
|
|
101108
101675
|
else {
|
|
101109
101676
|
const expandHint = hasExpandable ? ` · ctrl+o ${this.expanded ? "collapse" : "expand"}` : "";
|
|
101110
|
-
lines.push(indent(
|
|
101677
|
+
lines.push(indent(dim(`↑/↓ select · ${buildNumericHint(data.choices.length)} choose · ↵ confirm${expandHint}`)));
|
|
101111
101678
|
}
|
|
101112
101679
|
lines.push(horizontalBar);
|
|
101113
101680
|
return lines.map((line) => truncateToWidth(line, width));
|
|
@@ -101127,7 +101694,8 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
101127
101694
|
if (this.selectedIndex < 0 || this.selectedIndex >= count) this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, count - 1));
|
|
101128
101695
|
}
|
|
101129
101696
|
renderInlineFeedbackLine(width, labelWithNum) {
|
|
101130
|
-
const
|
|
101697
|
+
const selectColorBold = chalk.bold.hex(this.colors.accent);
|
|
101698
|
+
const prefix = `${selectColorBold("▶")} ${selectColorBold(labelWithNum)} `;
|
|
101131
101699
|
const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
|
|
101132
101700
|
const inputLine = this.feedbackInput.render(inputWidth)[0] ?? "> ";
|
|
101133
101701
|
return prefix + (inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine);
|
|
@@ -101148,7 +101716,7 @@ function showApprovalPanel(state, payload) {
|
|
|
101148
101716
|
updateActivityPane(state);
|
|
101149
101717
|
mountPanel(state, new ApprovalPanelComponent({ data: payload }, (response) => {
|
|
101150
101718
|
state.approvalController.respond(adaptPanelResponse(response));
|
|
101151
|
-
}));
|
|
101719
|
+
}, state.colors));
|
|
101152
101720
|
}
|
|
101153
101721
|
function hideApprovalPanel(state) {
|
|
101154
101722
|
state.livePane.pendingApproval = null;
|
|
@@ -101601,7 +102169,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
101601
102169
|
if (question === void 0) continue;
|
|
101602
102170
|
const label = question.header !== void 0 && question.header.length > 0 ? question.header : `Q${String(i + 1)}`;
|
|
101603
102171
|
if (i === this.currentTab) tabs.push(active(` ${label} `));
|
|
101604
|
-
else if (this.isAnswered(i)) tabs.push(chalk.
|
|
102172
|
+
else if (this.isAnswered(i)) tabs.push(chalk.hex(this.colors.success)(`(✓) ${label}`));
|
|
101605
102173
|
else tabs.push(dim(`(○) ${label}`));
|
|
101606
102174
|
}
|
|
101607
102175
|
const submitLabel = "Submit";
|
|
@@ -102362,12 +102930,17 @@ function setupEditor(state, cb) {
|
|
|
102362
102930
|
editor.onCtrlS = () => {
|
|
102363
102931
|
if (!state.appState.isStreaming || state.appState.isCompacting) return;
|
|
102364
102932
|
const text = editor.getText().trim();
|
|
102365
|
-
|
|
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) {
|
|
102366
102942
|
editor.setText("");
|
|
102367
|
-
cb.onSteerFromInput(
|
|
102368
|
-
} else {
|
|
102369
|
-
const first = cb.onSteerDequeueFirst();
|
|
102370
|
-
if (first !== void 0) cb.onSteerFromInput(first);
|
|
102943
|
+
cb.onSteerFromInput(parts);
|
|
102371
102944
|
}
|
|
102372
102945
|
cb.onAfterQueueMutation();
|
|
102373
102946
|
state.ui.requestRender();
|
|
@@ -102507,7 +103080,8 @@ var KimiTUI = class {
|
|
|
102507
103080
|
this.state = createTUIState({
|
|
102508
103081
|
client,
|
|
102509
103082
|
initialAppState: initialState,
|
|
102510
|
-
startup: options.startup
|
|
103083
|
+
startup: options.startup,
|
|
103084
|
+
...options.resolvedTheme !== void 0 ? { resolvedTheme: options.resolvedTheme } : {}
|
|
102511
103085
|
});
|
|
102512
103086
|
this.stateHooks = {
|
|
102513
103087
|
syncFooter: () => this.state.footer.setState(this.state.appState),
|
|
@@ -102557,14 +103131,14 @@ var KimiTUI = class {
|
|
|
102557
103131
|
this.state.ui.setFocus(this.state.editor);
|
|
102558
103132
|
this.state.ui.start();
|
|
102559
103133
|
attachFooterFeed(this.state, buildFooterFeedHandlers(this.state));
|
|
102560
|
-
if (this.state.startupState === "picker") {
|
|
102561
|
-
bootstrapFromPicker(this.state, this.buildSessionBootstrapHooks());
|
|
102562
|
-
return;
|
|
102563
|
-
}
|
|
102564
103134
|
if (this.state.startupNotice !== void 0) {
|
|
102565
103135
|
emitMuted(this.state, this.state.startupNotice);
|
|
102566
103136
|
this.state.startupNotice = void 0;
|
|
102567
103137
|
}
|
|
103138
|
+
if (this.state.startupState === "picker") {
|
|
103139
|
+
bootstrapFromPicker(this.state, this.buildSessionBootstrapHooks());
|
|
103140
|
+
return;
|
|
103141
|
+
}
|
|
102568
103142
|
if (shouldReplayHistory) await hydrateTranscriptFromReplay(this.state, this.stateHooks, this.state.appState.sessionId);
|
|
102569
103143
|
this.startWireSubscription();
|
|
102570
103144
|
fetchSessions(this.state);
|
|
@@ -102729,11 +103303,14 @@ var KimiTUI = class {
|
|
|
102729
103303
|
showHelpPanel: () => showHelpPanel(this.state),
|
|
102730
103304
|
showSessionPicker: () => showSessionPicker(this.state, this.buildSessionHooks()),
|
|
102731
103305
|
showEditorPicker: () => showEditorPicker(this.state, this.stateHooks),
|
|
103306
|
+
showThemePicker: () => showThemePicker(this.state, this.stateHooks),
|
|
102732
103307
|
showModelPicker: () => showModelPicker(this.state, this.stateHooks),
|
|
102733
103308
|
showThinkingPicker: (alias) => showThinkingPicker(this.state, alias, this.stateHooks),
|
|
102734
103309
|
showUsage: () => {
|
|
102735
103310
|
showUsage(this.state);
|
|
102736
103311
|
},
|
|
103312
|
+
setEditorCommand: (command) => applyEditorChoice(this.state, command, this.stateHooks),
|
|
103313
|
+
setTheme: (theme) => applyThemeChoice(this.state, theme, this.stateHooks),
|
|
102737
103314
|
sendAsMessage: (text) => sendMessage(this.state, (e) => this.addEntry(e), text),
|
|
102738
103315
|
activateSkill: (name, args, fullPrompt) => sendSkillActivation(this.state, (e) => this.addEntry(e), name, args, fullPrompt),
|
|
102739
103316
|
performReload: (action) => performReload(this.state, action, this.buildInputHooks())
|
|
@@ -102767,6 +103344,16 @@ function toInitialSessionIntent(opts) {
|
|
|
102767
103344
|
}
|
|
102768
103345
|
async function runShell(opts, version) {
|
|
102769
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;
|
|
102770
103357
|
const workDir = process.cwd();
|
|
102771
103358
|
const initialState = {
|
|
102772
103359
|
model: ctx.model,
|
|
@@ -102783,18 +103370,22 @@ async function runShell(opts, version) {
|
|
|
102783
103370
|
isReplaying: false,
|
|
102784
103371
|
streamingPhase: "idle",
|
|
102785
103372
|
streamingStartTime: 0,
|
|
102786
|
-
theme:
|
|
103373
|
+
theme: tuiConfig.theme,
|
|
102787
103374
|
version,
|
|
102788
|
-
editorCommand:
|
|
103375
|
+
editorCommand: tuiConfig.editorCommand,
|
|
102789
103376
|
availableModels: ctx.availableModels,
|
|
102790
103377
|
sessionTitle: null
|
|
102791
103378
|
};
|
|
102792
|
-
const tui = new KimiTUI(ctx.client, initialState, {
|
|
102793
|
-
|
|
102794
|
-
|
|
102795
|
-
|
|
102796
|
-
|
|
102797
|
-
|
|
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
|
+
});
|
|
102798
103389
|
tui.onExit = async () => {
|
|
102799
103390
|
const sessionId = tui.getCurrentSessionId();
|
|
102800
103391
|
const hasContent = tui.hasSessionContent();
|