kfc-code-cli 0.0.1-alpha.3 → 0.0.1-alpha.5
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 +1161 -619
- package/package.json +2 -2
package/dist/main.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { writeFile as writeFile$1 } from "fs/promises";
|
|
|
33
33
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
34
34
|
import "yazl";
|
|
35
35
|
import pino from "pino";
|
|
36
|
-
import { CombinedAutocompleteProvider, Container, Editor, Image, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, fuzzyFilter, fuzzyMatch, getCapabilities, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
36
|
+
import { CombinedAutocompleteProvider, Container, Editor, Image, Input, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, fuzzyFilter, fuzzyMatch, getCapabilities, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
37
37
|
import chalk from "chalk";
|
|
38
38
|
import { highlight } from "cli-highlight";
|
|
39
39
|
import { gt, valid } from "semver";
|
|
@@ -1026,26 +1026,12 @@ var BaseContextState = class {
|
|
|
1026
1026
|
this._systemPrompt = event.new_prompt;
|
|
1027
1027
|
return;
|
|
1028
1028
|
case "model_changed":
|
|
1029
|
-
await this.journalWriter.append({
|
|
1030
|
-
type: "model_changed",
|
|
1031
|
-
old_model: event.old_model,
|
|
1032
|
-
new_model: event.new_model
|
|
1033
|
-
});
|
|
1034
1029
|
this._model = event.new_model;
|
|
1035
1030
|
return;
|
|
1036
1031
|
case "thinking_changed":
|
|
1037
|
-
await this.journalWriter.append({
|
|
1038
|
-
type: "thinking_changed",
|
|
1039
|
-
level: event.level
|
|
1040
|
-
});
|
|
1041
1032
|
this._thinkingLevel = event.level;
|
|
1042
1033
|
return;
|
|
1043
|
-
case "plan_mode_changed":
|
|
1044
|
-
await this.journalWriter.append({
|
|
1045
|
-
type: "plan_mode_changed",
|
|
1046
|
-
enabled: event.enabled
|
|
1047
|
-
});
|
|
1048
|
-
return;
|
|
1034
|
+
case "plan_mode_changed": return;
|
|
1049
1035
|
case "tools_changed":
|
|
1050
1036
|
await this.journalWriter.append({
|
|
1051
1037
|
type: "tools_changed",
|
|
@@ -1146,9 +1132,6 @@ var WiredSessionJournalImpl = class {
|
|
|
1146
1132
|
async appendTeamMail(data) {
|
|
1147
1133
|
await this.journalWriter.append(data);
|
|
1148
1134
|
}
|
|
1149
|
-
async appendPermissionModeChanged(data) {
|
|
1150
|
-
await this.journalWriter.append(data);
|
|
1151
|
-
}
|
|
1152
1135
|
async appendToolDenied(data) {
|
|
1153
1136
|
await this.journalWriter.append(data);
|
|
1154
1137
|
}
|
|
@@ -1164,9 +1147,6 @@ var WiredSessionJournalImpl = class {
|
|
|
1164
1147
|
async appendOwnershipChanged(data) {
|
|
1165
1148
|
await this.journalWriter.append(data);
|
|
1166
1149
|
}
|
|
1167
|
-
async appendSessionMetaChanged(data) {
|
|
1168
|
-
await this.journalWriter.append(data);
|
|
1169
|
-
}
|
|
1170
1150
|
};
|
|
1171
1151
|
//#endregion
|
|
1172
1152
|
//#region ../../packages/kimi-core/src/storage/schema/records.ts
|
|
@@ -1284,25 +1264,6 @@ const _rawSystemPromptChangedRecordSchema = z.object({
|
|
|
1284
1264
|
time: z.number(),
|
|
1285
1265
|
new_prompt: z.string()
|
|
1286
1266
|
});
|
|
1287
|
-
const _rawModelChangedRecordSchema = z.object({
|
|
1288
|
-
type: z.literal("model_changed"),
|
|
1289
|
-
seq: z.number(),
|
|
1290
|
-
time: z.number(),
|
|
1291
|
-
old_model: z.string(),
|
|
1292
|
-
new_model: z.string()
|
|
1293
|
-
});
|
|
1294
|
-
const _rawThinkingChangedRecordSchema = z.object({
|
|
1295
|
-
type: z.literal("thinking_changed"),
|
|
1296
|
-
seq: z.number(),
|
|
1297
|
-
time: z.number(),
|
|
1298
|
-
level: z.string()
|
|
1299
|
-
});
|
|
1300
|
-
const _rawPlanModeChangedRecordSchema = z.object({
|
|
1301
|
-
type: z.literal("plan_mode_changed"),
|
|
1302
|
-
seq: z.number(),
|
|
1303
|
-
time: z.number(),
|
|
1304
|
-
enabled: z.boolean()
|
|
1305
|
-
});
|
|
1306
1267
|
const _rawToolsChangedRecordSchema = z.object({
|
|
1307
1268
|
type: z.literal("tools_changed"),
|
|
1308
1269
|
seq: z.number(),
|
|
@@ -1359,17 +1320,6 @@ const _rawNotificationRecordSchema = z.object({
|
|
|
1359
1320
|
envelope_id: z.string().optional()
|
|
1360
1321
|
})
|
|
1361
1322
|
});
|
|
1362
|
-
const _rawPermissionModeChangedRecordSchema = z.object({
|
|
1363
|
-
type: z.literal("permission_mode_changed"),
|
|
1364
|
-
seq: z.number(),
|
|
1365
|
-
time: z.number(),
|
|
1366
|
-
turn_id: z.string().optional(),
|
|
1367
|
-
data: z.object({
|
|
1368
|
-
from: z.string(),
|
|
1369
|
-
to: z.string(),
|
|
1370
|
-
reason: z.string()
|
|
1371
|
-
})
|
|
1372
|
-
});
|
|
1373
1323
|
const _rawToolDeniedRecordSchema = z.object({
|
|
1374
1324
|
type: z.literal("tool_denied"),
|
|
1375
1325
|
seq: z.number(),
|
|
@@ -1611,25 +1561,6 @@ const _rawOwnershipChangedRecordSchema = z.object({
|
|
|
1611
1561
|
old_owner: z.string().nullable(),
|
|
1612
1562
|
new_owner: z.string()
|
|
1613
1563
|
});
|
|
1614
|
-
const _rawSessionMetaChangedRecordSchema = z.object({
|
|
1615
|
-
type: z.literal("session_meta_changed"),
|
|
1616
|
-
seq: z.number(),
|
|
1617
|
-
time: z.number(),
|
|
1618
|
-
patch: z.object({
|
|
1619
|
-
title: z.string().optional(),
|
|
1620
|
-
tags: z.array(z.string()).optional(),
|
|
1621
|
-
description: z.string().optional(),
|
|
1622
|
-
archived: z.boolean().optional(),
|
|
1623
|
-
color: z.string().optional(),
|
|
1624
|
-
plan_slug: z.string().optional()
|
|
1625
|
-
}),
|
|
1626
|
-
source: z.enum([
|
|
1627
|
-
"user",
|
|
1628
|
-
"auto",
|
|
1629
|
-
"system"
|
|
1630
|
-
]),
|
|
1631
|
-
reason: z.string().optional()
|
|
1632
|
-
});
|
|
1633
1564
|
const _rawContextEditRecordSchema = z.object({
|
|
1634
1565
|
type: z.literal("context_edit"),
|
|
1635
1566
|
seq: z.number(),
|
|
@@ -1662,15 +1593,15 @@ const _sessionInitializedCommonShape = {
|
|
|
1662
1593
|
seq: z.number(),
|
|
1663
1594
|
time: z.number(),
|
|
1664
1595
|
system_prompt: z.string(),
|
|
1665
|
-
model: z.string(),
|
|
1666
1596
|
active_tools: z.array(z.string()),
|
|
1597
|
+
model: z.string().optional(),
|
|
1667
1598
|
permission_mode: z.enum([
|
|
1668
1599
|
"default",
|
|
1669
1600
|
"acceptEdits",
|
|
1670
1601
|
"bypassPermissions"
|
|
1671
|
-
]),
|
|
1672
|
-
plan_mode: z.boolean(),
|
|
1673
|
-
workspace_dir: z.string(),
|
|
1602
|
+
]).optional(),
|
|
1603
|
+
plan_mode: z.boolean().optional(),
|
|
1604
|
+
workspace_dir: z.string().optional(),
|
|
1674
1605
|
thinking_level: z.string().optional()
|
|
1675
1606
|
};
|
|
1676
1607
|
const _rawSessionInitializedMainSchema = z.object({
|
|
@@ -1707,13 +1638,9 @@ const WireRecordSchema = z.discriminatedUnion("type", [
|
|
|
1707
1638
|
_rawToolResultRecordSchema,
|
|
1708
1639
|
_rawCompactionRecordSchema,
|
|
1709
1640
|
_rawSystemPromptChangedRecordSchema,
|
|
1710
|
-
_rawModelChangedRecordSchema,
|
|
1711
|
-
_rawThinkingChangedRecordSchema,
|
|
1712
|
-
_rawPlanModeChangedRecordSchema,
|
|
1713
1641
|
_rawToolsChangedRecordSchema,
|
|
1714
1642
|
_rawSystemReminderRecordSchema,
|
|
1715
1643
|
_rawNotificationRecordSchema,
|
|
1716
|
-
_rawPermissionModeChangedRecordSchema,
|
|
1717
1644
|
_rawToolDeniedRecordSchema,
|
|
1718
1645
|
_rawStepBeginRecordSchema,
|
|
1719
1646
|
_rawStepEndRecordSchema,
|
|
@@ -1730,7 +1657,6 @@ const WireRecordSchema = z.discriminatedUnion("type", [
|
|
|
1730
1657
|
_rawOwnershipChangedRecordSchema,
|
|
1731
1658
|
_rawContextEditRecordSchema,
|
|
1732
1659
|
_rawContextClearedRecordSchema,
|
|
1733
|
-
_rawSessionMetaChangedRecordSchema,
|
|
1734
1660
|
_rawSessionInitializedRecordSchema
|
|
1735
1661
|
]);
|
|
1736
1662
|
//#endregion
|
|
@@ -1849,13 +1775,9 @@ const KNOWN_RECORD_TYPES = new Set([
|
|
|
1849
1775
|
"tool_result",
|
|
1850
1776
|
"compaction",
|
|
1851
1777
|
"system_prompt_changed",
|
|
1852
|
-
"model_changed",
|
|
1853
|
-
"thinking_changed",
|
|
1854
|
-
"plan_mode_changed",
|
|
1855
1778
|
"tools_changed",
|
|
1856
1779
|
"system_reminder",
|
|
1857
1780
|
"notification",
|
|
1858
|
-
"permission_mode_changed",
|
|
1859
1781
|
"tool_denied",
|
|
1860
1782
|
"step_begin",
|
|
1861
1783
|
"step_end",
|
|
@@ -1871,8 +1793,7 @@ const KNOWN_RECORD_TYPES = new Set([
|
|
|
1871
1793
|
"subagent_failed",
|
|
1872
1794
|
"ownership_changed",
|
|
1873
1795
|
"context_edit",
|
|
1874
|
-
"context_cleared"
|
|
1875
|
-
"session_meta_changed"
|
|
1796
|
+
"context_cleared"
|
|
1876
1797
|
]);
|
|
1877
1798
|
//#endregion
|
|
1878
1799
|
//#region ../../packages/kimi-core/src/storage/journal/rotation.ts
|
|
@@ -2286,6 +2207,15 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2286
2207
|
if (steps >= maxSteps) throw new MaxStepsExceededError(maxSteps);
|
|
2287
2208
|
steps += 1;
|
|
2288
2209
|
const currentStep = steps;
|
|
2210
|
+
const turnId = context.currentTurnId?.() ?? UNKNOWN_TURN_ID;
|
|
2211
|
+
if (config.beforeStep !== void 0) {
|
|
2212
|
+
const beforeStep = await config.beforeStep({
|
|
2213
|
+
turnId,
|
|
2214
|
+
stepNumber: currentStep,
|
|
2215
|
+
context
|
|
2216
|
+
}, signal);
|
|
2217
|
+
if (beforeStep?.block === true) throw new Error(beforeStep.reason ?? `Step ${String(currentStep)} was blocked`);
|
|
2218
|
+
}
|
|
2289
2219
|
safeEmit(sink, {
|
|
2290
2220
|
type: "step.begin",
|
|
2291
2221
|
step: currentStep
|
|
@@ -2298,7 +2228,6 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2298
2228
|
const visibleTools = buildLLMVisibleTools(config.tools, overrides?.activeTools);
|
|
2299
2229
|
const messages = context.buildMessages();
|
|
2300
2230
|
const stepUuid = randomUUID();
|
|
2301
|
-
const turnId = context.currentTurnId?.() ?? UNKNOWN_TURN_ID;
|
|
2302
2231
|
const toolCallByProviderId = /* @__PURE__ */ new Map();
|
|
2303
2232
|
const prefetchedResults = /* @__PURE__ */ new Map();
|
|
2304
2233
|
await context.appendStepBegin({
|
|
@@ -2420,6 +2349,14 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2420
2349
|
step: currentStep
|
|
2421
2350
|
});
|
|
2422
2351
|
const sr = response.stopReason ?? "end_turn";
|
|
2352
|
+
if (config.afterStep !== void 0) try {
|
|
2353
|
+
await config.afterStep({
|
|
2354
|
+
turnId,
|
|
2355
|
+
stepNumber: currentStep,
|
|
2356
|
+
context,
|
|
2357
|
+
stopReason: sr
|
|
2358
|
+
}, signal);
|
|
2359
|
+
} catch {}
|
|
2423
2360
|
if (sr === "tool_use") continue;
|
|
2424
2361
|
stopReason = sr;
|
|
2425
2362
|
break;
|
|
@@ -2929,21 +2866,26 @@ function hookDedupeKey(hook) {
|
|
|
2929
2866
|
/**
|
|
2930
2867
|
* Extracts the string fed to a hook's matcher regex. Event-dependent:
|
|
2931
2868
|
*
|
|
2869
|
+
* - `PreTurn` / `UserPromptSubmit` — the prompt text itself.
|
|
2870
|
+
* - `PostTurn` / `Stop` — the turn reason (`done` / `cancelled` /
|
|
2871
|
+
* `error`), so hooks can filter e.g. `/^error$/`.
|
|
2872
|
+
* - `PreStep` / `PostStep` — the step number as a string.
|
|
2932
2873
|
* - `PreToolUse` / `PostToolUse` / `OnToolFailure` — tool name
|
|
2933
2874
|
* (mirrors Python's `matcher_value=toolCall.name` contract).
|
|
2934
|
-
* - `UserPromptSubmit` — the prompt text itself (Python parity).
|
|
2935
|
-
* - `Stop` — the turn reason (`done` / `cancelled` / `error`), so
|
|
2936
|
-
* hooks can filter e.g. `/^error$/`.
|
|
2937
2875
|
* - `Notification` — the notification type string, so a single hook
|
|
2938
2876
|
* can subscribe to an entire notification class via regex.
|
|
2939
2877
|
*/
|
|
2940
2878
|
function extractMatcherValue(input) {
|
|
2941
2879
|
switch (input.event) {
|
|
2880
|
+
case "PreTurn":
|
|
2881
|
+
case "UserPromptSubmit": return input.prompt;
|
|
2882
|
+
case "PostTurn":
|
|
2883
|
+
case "Stop": return input.reason;
|
|
2884
|
+
case "PreStep":
|
|
2885
|
+
case "PostStep": return String(input.stepNumber);
|
|
2942
2886
|
case "PreToolUse":
|
|
2943
2887
|
case "PostToolUse":
|
|
2944
2888
|
case "OnToolFailure": return input.toolCall.name;
|
|
2945
|
-
case "UserPromptSubmit": return input.prompt;
|
|
2946
|
-
case "Stop": return input.reason;
|
|
2947
2889
|
case "Notification": return input.notificationType;
|
|
2948
2890
|
case "StopFailure": return input.error;
|
|
2949
2891
|
case "SubagentStart":
|
|
@@ -2953,6 +2895,7 @@ function extractMatcherValue(input) {
|
|
|
2953
2895
|
case "PreCompact":
|
|
2954
2896
|
case "PostCompact": return "";
|
|
2955
2897
|
}
|
|
2898
|
+
return input;
|
|
2956
2899
|
}
|
|
2957
2900
|
const AgentToolInputSchema = z.preprocess((input) => {
|
|
2958
2901
|
if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
|
|
@@ -3252,7 +3195,7 @@ var EnterPlanModeTool = class {
|
|
|
3252
3195
|
isError: true,
|
|
3253
3196
|
content: "Plan mode is already active. Use ExitPlanMode when the plan is ready."
|
|
3254
3197
|
};
|
|
3255
|
-
if (this.deps.isYoloMode()) {
|
|
3198
|
+
if (this.deps.isYoloMode() || this.deps.shouldAutoApprove?.() === true) {
|
|
3256
3199
|
try {
|
|
3257
3200
|
await this.deps.setPlanMode(true);
|
|
3258
3201
|
} catch (error) {
|
|
@@ -3588,15 +3531,10 @@ var CompactionOrchestrator = class {
|
|
|
3588
3531
|
const summary = await (this.deps.runtimeSlot?.current().compactionProvider ?? this.deps.compactionProvider).run(messages, signal, customInstruction !== void 0 ? { userInstructions: customInstruction } : void 0);
|
|
3589
3532
|
signal.throwIfAborted();
|
|
3590
3533
|
const baselineInit = await this.deps.journalCapability.readSessionInitialized();
|
|
3591
|
-
const runtime = this.deps.runtimeStateProvider();
|
|
3592
3534
|
const cs = this.deps.contextState;
|
|
3593
3535
|
const sessionInitialized = applyRuntimeOverlay(baselineInit, {
|
|
3594
3536
|
system_prompt: cs.systemPrompt,
|
|
3595
|
-
|
|
3596
|
-
active_tools: [...cs.activeTools],
|
|
3597
|
-
permission_mode: runtime.permissionMode,
|
|
3598
|
-
plan_mode: runtime.planMode,
|
|
3599
|
-
thinking_level: runtime.thinkingLevel
|
|
3537
|
+
active_tools: [...cs.activeTools]
|
|
3600
3538
|
});
|
|
3601
3539
|
await this.deps.journalWriter.flush();
|
|
3602
3540
|
const rotateResult = await this.deps.journalCapability.rotate({
|
|
@@ -3663,16 +3601,15 @@ var CompactionOrchestrator = class {
|
|
|
3663
3601
|
};
|
|
3664
3602
|
/**
|
|
3665
3603
|
* Phase 23 fix — overlay the runtime-mutable subset of a baseline
|
|
3666
|
-
* `session_initialized` with the live ContextState
|
|
3604
|
+
* `session_initialized` with the live wire-owned ContextState baseline.
|
|
3667
3605
|
*
|
|
3668
3606
|
* Identity-class fields (`type`, `seq`, `time`, `agent_type`, `session_id`,
|
|
3669
|
-
* `agent_id`, parent lineage
|
|
3607
|
+
* `agent_id`, parent lineage) are preserved verbatim
|
|
3670
3608
|
* because they cannot legally mutate at runtime — see the discriminated-union
|
|
3671
|
-
* shape in `wire-record.ts`. The mutable fields
|
|
3672
|
-
* `
|
|
3673
|
-
*
|
|
3674
|
-
*
|
|
3675
|
-
* mutable: `session.setThinking` (Phase 24 24b) changes it at runtime.
|
|
3609
|
+
* shape in `wire-record.ts`. The mutable wire-owned fields
|
|
3610
|
+
* (`system_prompt`, `active_tools`) are overwritten so the post-rotate
|
|
3611
|
+
* baseline reflects the compaction-time snapshot rather than the original
|
|
3612
|
+
* startup config. Runtime/session fields live in state.json.
|
|
3676
3613
|
*
|
|
3677
3614
|
* The generic preserves the discriminated-union narrowing so each branch
|
|
3678
3615
|
* (main / sub / independent) returns its own concrete type.
|
|
@@ -3681,11 +3618,7 @@ function applyRuntimeOverlay(baseline, overlay) {
|
|
|
3681
3618
|
return {
|
|
3682
3619
|
...baseline,
|
|
3683
3620
|
system_prompt: overlay.system_prompt,
|
|
3684
|
-
|
|
3685
|
-
active_tools: [...overlay.active_tools],
|
|
3686
|
-
permission_mode: overlay.permission_mode,
|
|
3687
|
-
plan_mode: overlay.plan_mode,
|
|
3688
|
-
...overlay.thinking_level !== void 0 ? { thinking_level: overlay.thinking_level } : {}
|
|
3621
|
+
active_tools: [...overlay.active_tools]
|
|
3689
3622
|
};
|
|
3690
3623
|
}
|
|
3691
3624
|
/**
|
|
@@ -4449,11 +4382,13 @@ var DefaultSessionControl = class {
|
|
|
4449
4382
|
contextState;
|
|
4450
4383
|
sessionJournal;
|
|
4451
4384
|
setPlanModeOverride;
|
|
4385
|
+
setYoloOverride;
|
|
4452
4386
|
constructor(deps) {
|
|
4453
4387
|
this.turnManager = deps.turnManager;
|
|
4454
4388
|
this.contextState = deps.contextState;
|
|
4455
4389
|
this.sessionJournal = deps.sessionJournal;
|
|
4456
4390
|
this.setPlanModeOverride = deps.setPlanModeOverride;
|
|
4391
|
+
this.setYoloOverride = deps.setYoloOverride;
|
|
4457
4392
|
}
|
|
4458
4393
|
async compact(customInstruction) {
|
|
4459
4394
|
await this.turnManager.triggerCompaction(customInstruction);
|
|
@@ -4478,18 +4413,15 @@ var DefaultSessionControl = class {
|
|
|
4478
4413
|
this.turnManager.setPlanMode(enabled);
|
|
4479
4414
|
}
|
|
4480
4415
|
async setYolo(enabled) {
|
|
4416
|
+
if (this.setYoloOverride !== void 0) {
|
|
4417
|
+
await this.setYoloOverride(enabled);
|
|
4418
|
+
return;
|
|
4419
|
+
}
|
|
4481
4420
|
const previousMode = this.turnManager.getPermissionMode();
|
|
4482
4421
|
const newMode = enabled ? "bypassPermissions" : "default";
|
|
4483
4422
|
if (previousMode === newMode) return;
|
|
4484
4423
|
this.turnManager.setPermissionMode(newMode);
|
|
4485
|
-
|
|
4486
|
-
type: "permission_mode_changed",
|
|
4487
|
-
data: {
|
|
4488
|
-
from: previousMode,
|
|
4489
|
-
to: newMode,
|
|
4490
|
-
reason: enabled ? "/yolo on" : "/yolo off"
|
|
4491
|
-
}
|
|
4492
|
-
});
|
|
4424
|
+
this.sessionJournal;
|
|
4493
4425
|
}
|
|
4494
4426
|
};
|
|
4495
4427
|
//#endregion
|
|
@@ -4591,19 +4523,16 @@ var SessionMetaService = class {
|
|
|
4591
4523
|
}
|
|
4592
4524
|
}
|
|
4593
4525
|
async applyPatch(patch, source, reason) {
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
if (patch.
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
if (patch.color !== void 0) this.meta.color = patch.color;
|
|
4605
|
-
if (patch.plan_slug !== void 0) this.meta.plan_slug = patch.plan_slug;
|
|
4606
|
-
this.meta.last_updated = Date.now();
|
|
4526
|
+
const next = cloneMeta(this.meta);
|
|
4527
|
+
if (patch.title !== void 0) next.title = patch.title;
|
|
4528
|
+
if (patch.tags !== void 0) next.tags = [...patch.tags];
|
|
4529
|
+
if (patch.description !== void 0) next.description = patch.description;
|
|
4530
|
+
if (patch.archived !== void 0) next.archived = patch.archived;
|
|
4531
|
+
if (patch.color !== void 0) next.color = patch.color;
|
|
4532
|
+
if (patch.plan_slug !== void 0) next.plan_slug = patch.plan_slug;
|
|
4533
|
+
next.last_updated = Date.now();
|
|
4534
|
+
await this.flushStateJson(next);
|
|
4535
|
+
this.meta = next;
|
|
4607
4536
|
this.deps.eventBus.emit({
|
|
4608
4537
|
type: "session_meta.changed",
|
|
4609
4538
|
data: {
|
|
@@ -4636,7 +4565,7 @@ var SessionMetaService = class {
|
|
|
4636
4565
|
if (this.flushTimer !== null) return;
|
|
4637
4566
|
this.flushTimer = setTimeout(() => {
|
|
4638
4567
|
this.flushTimer = null;
|
|
4639
|
-
this.flushStateJson().catch(() => {});
|
|
4568
|
+
this.flushStateJson(this.meta).catch(() => {});
|
|
4640
4569
|
}, this.flushDebounceMs);
|
|
4641
4570
|
this.flushTimer.unref?.();
|
|
4642
4571
|
}
|
|
@@ -4651,25 +4580,27 @@ var SessionMetaService = class {
|
|
|
4651
4580
|
* because createSession / closeSession / plan_mode / yolo all need
|
|
4652
4581
|
* to migrate off direct state.json writes first.
|
|
4653
4582
|
*/
|
|
4654
|
-
async flushStateJson() {
|
|
4655
|
-
|
|
4656
|
-
...
|
|
4657
|
-
session_id:
|
|
4658
|
-
created_at:
|
|
4659
|
-
updated_at:
|
|
4583
|
+
async flushStateJson(meta = this.meta) {
|
|
4584
|
+
await this.deps.stateCache.update((existing) => ({
|
|
4585
|
+
...existing ?? {
|
|
4586
|
+
session_id: meta.session_id,
|
|
4587
|
+
created_at: meta.created_at,
|
|
4588
|
+
updated_at: meta.last_updated
|
|
4660
4589
|
},
|
|
4661
|
-
session_id:
|
|
4662
|
-
created_at:
|
|
4663
|
-
updated_at:
|
|
4664
|
-
...
|
|
4665
|
-
...
|
|
4666
|
-
...
|
|
4667
|
-
...
|
|
4668
|
-
...
|
|
4669
|
-
...
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4590
|
+
session_id: meta.session_id,
|
|
4591
|
+
created_at: meta.created_at,
|
|
4592
|
+
updated_at: meta.last_updated,
|
|
4593
|
+
...meta.title !== void 0 ? { custom_title: meta.title } : {},
|
|
4594
|
+
...meta.tags !== void 0 ? { tags: [...meta.tags] } : {},
|
|
4595
|
+
...meta.description !== void 0 ? { description: meta.description } : {},
|
|
4596
|
+
...meta.archived !== void 0 ? { archived: meta.archived } : {},
|
|
4597
|
+
...meta.color !== void 0 ? { color: meta.color } : {},
|
|
4598
|
+
...meta.last_model !== void 0 ? { model: meta.last_model } : {},
|
|
4599
|
+
turn_count: meta.turn_count,
|
|
4600
|
+
...meta.plan_slug !== void 0 ? { plan_slug: meta.plan_slug } : {},
|
|
4601
|
+
...meta.producer !== void 0 ? { producer: { ...meta.producer } } : {},
|
|
4602
|
+
last_exit_code: "dirty"
|
|
4603
|
+
}));
|
|
4673
4604
|
}
|
|
4674
4605
|
};
|
|
4675
4606
|
function cloneMeta(meta) {
|
|
@@ -5258,11 +5189,7 @@ async function runSubagentTurn(deps, agentId, request, signal) {
|
|
|
5258
5189
|
parent_tool_call_id: request.parentToolCallId,
|
|
5259
5190
|
run_in_background: request.runInBackground ?? false,
|
|
5260
5191
|
system_prompt: childSystemPrompt ?? "",
|
|
5261
|
-
|
|
5262
|
-
active_tools: childActiveTools,
|
|
5263
|
-
permission_mode: "default",
|
|
5264
|
-
plan_mode: false,
|
|
5265
|
-
workspace_dir: deps.workDir ?? process.cwd()
|
|
5192
|
+
active_tools: childActiveTools
|
|
5266
5193
|
};
|
|
5267
5194
|
await childJournalWriter.append(subInitInput);
|
|
5268
5195
|
childContext = new WiredContextState({
|
|
@@ -19848,6 +19775,49 @@ OpenAI.Containers = Containers;
|
|
|
19848
19775
|
OpenAI.Skills = Skills$1;
|
|
19849
19776
|
OpenAI.Videos = Videos;
|
|
19850
19777
|
//#endregion
|
|
19778
|
+
//#region ../../packages/kimi-core/src/hooks/execution-pipeline.ts
|
|
19779
|
+
const ALLOW = Object.freeze({
|
|
19780
|
+
blockAction: false,
|
|
19781
|
+
additionalContext: []
|
|
19782
|
+
});
|
|
19783
|
+
var ExecutionHookBlockedError = class extends Error {
|
|
19784
|
+
event;
|
|
19785
|
+
reason;
|
|
19786
|
+
constructor(event, reason) {
|
|
19787
|
+
super(reason !== void 0 ? `${event} hook blocked: ${reason}` : `${event} hook blocked`);
|
|
19788
|
+
this.name = "ExecutionHookBlockedError";
|
|
19789
|
+
this.event = event;
|
|
19790
|
+
this.reason = reason;
|
|
19791
|
+
}
|
|
19792
|
+
};
|
|
19793
|
+
/**
|
|
19794
|
+
* Awaited execution-hook adapter.
|
|
19795
|
+
*
|
|
19796
|
+
* This sits on the execution side of the architecture, separate from the
|
|
19797
|
+
* wire-event publication pipeline. Pre hooks can block real work before it
|
|
19798
|
+
* happens; post hooks can still run in order without being able to rewrite
|
|
19799
|
+
* already-durable facts unless their caller explicitly interprets a result.
|
|
19800
|
+
*/
|
|
19801
|
+
var ExecutionHookPipeline = class {
|
|
19802
|
+
hookEngine;
|
|
19803
|
+
constructor(options) {
|
|
19804
|
+
this.hookEngine = options.hookEngine;
|
|
19805
|
+
}
|
|
19806
|
+
async run(input, signal) {
|
|
19807
|
+
if (this.hookEngine === void 0) return ALLOW;
|
|
19808
|
+
return this.hookEngine.executeHooks(input.event, input, signal);
|
|
19809
|
+
}
|
|
19810
|
+
async runBlocking(input, signal) {
|
|
19811
|
+
const result = await this.run(input, signal);
|
|
19812
|
+
if (result.blockAction) throw new ExecutionHookBlockedError(input.event, result.reason);
|
|
19813
|
+
return result;
|
|
19814
|
+
}
|
|
19815
|
+
dispatch(input) {
|
|
19816
|
+
const controller = new AbortController();
|
|
19817
|
+
this.run(input, controller.signal).catch(() => {});
|
|
19818
|
+
}
|
|
19819
|
+
};
|
|
19820
|
+
//#endregion
|
|
19851
19821
|
//#region ../../packages/kimi-core/src/soul-plus/capability-check.ts
|
|
19852
19822
|
function checkLLMCapabilities(opts) {
|
|
19853
19823
|
if (opts.model === "") return;
|
|
@@ -19944,7 +19914,10 @@ var TurnManager = class {
|
|
|
19944
19914
|
runtimeSlot;
|
|
19945
19915
|
compactionConfig;
|
|
19946
19916
|
sessionId;
|
|
19917
|
+
executionHooks;
|
|
19947
19918
|
activeToolExecutionScope;
|
|
19919
|
+
pendingLaunchController;
|
|
19920
|
+
pendingLaunchDrain;
|
|
19948
19921
|
/**
|
|
19949
19922
|
* Phase 18 A.13 — terminal reason per turn id for callers that
|
|
19950
19923
|
* observe the turn lifecycle out-of-band (after the `end` event
|
|
@@ -19972,6 +19945,7 @@ var TurnManager = class {
|
|
|
19972
19945
|
this.runtimeSlot = deps.runtimeSlot;
|
|
19973
19946
|
this.compactionConfig = deps.compactionConfig;
|
|
19974
19947
|
this.sessionId = deps.sessionId ?? "unknown";
|
|
19948
|
+
this.executionHooks = new ExecutionHookPipeline({ hookEngine: deps.hookEngine });
|
|
19975
19949
|
}
|
|
19976
19950
|
setPermissionMode(mode) {
|
|
19977
19951
|
this.permissionMode = mode;
|
|
@@ -20154,6 +20128,13 @@ var TurnManager = class {
|
|
|
20154
20128
|
};
|
|
20155
20129
|
const scope = this.activeToolExecutionScope;
|
|
20156
20130
|
this.deps.approvalRuntime?.cancelBySource(source);
|
|
20131
|
+
if (this.pendingLaunchTurnId === turnId && this.pendingLaunchController !== void 0) {
|
|
20132
|
+
this.pendingLaunchController.abort();
|
|
20133
|
+
try {
|
|
20134
|
+
await this.pendingLaunchDrain;
|
|
20135
|
+
} catch {}
|
|
20136
|
+
return;
|
|
20137
|
+
}
|
|
20157
20138
|
scope?.discardStreaming?.("aborted");
|
|
20158
20139
|
await this.deps.lifecycle.cancelTurn(turnId);
|
|
20159
20140
|
scope?.drainPrefetched?.();
|
|
@@ -20178,8 +20159,16 @@ var TurnManager = class {
|
|
|
20178
20159
|
if (mismatch !== void 0) throw mismatch;
|
|
20179
20160
|
}
|
|
20180
20161
|
const turnId = this.deps.lifecycle.allocateTurnId();
|
|
20162
|
+
const pendingLaunchController = new AbortController();
|
|
20181
20163
|
this.pendingLaunchTurnId = turnId;
|
|
20164
|
+
this.pendingLaunchController = pendingLaunchController;
|
|
20165
|
+
let releasePendingLaunch;
|
|
20166
|
+
this.pendingLaunchDrain = new Promise((resolve) => {
|
|
20167
|
+
releasePendingLaunch = resolve;
|
|
20168
|
+
});
|
|
20182
20169
|
try {
|
|
20170
|
+
await this.executePreTurnHook(turnId, input, pendingLaunchController.signal);
|
|
20171
|
+
pendingLaunchController.signal.throwIfAborted();
|
|
20183
20172
|
await this.deps.sessionJournal.appendTurnBegin({
|
|
20184
20173
|
type: "turn_begin",
|
|
20185
20174
|
turn_id: turnId,
|
|
@@ -20210,17 +20199,23 @@ var TurnManager = class {
|
|
|
20210
20199
|
};
|
|
20211
20200
|
this.launchTurn(turnId, trigger);
|
|
20212
20201
|
this.pendingLaunchTurnId = void 0;
|
|
20202
|
+
this.pendingLaunchController = void 0;
|
|
20203
|
+
this.pendingLaunchDrain = void 0;
|
|
20204
|
+
releasePendingLaunch();
|
|
20213
20205
|
return {
|
|
20214
20206
|
turn_id: turnId,
|
|
20215
20207
|
status: "started"
|
|
20216
20208
|
};
|
|
20217
20209
|
} catch (error) {
|
|
20218
20210
|
this.pendingLaunchTurnId = void 0;
|
|
20211
|
+
this.pendingLaunchController = void 0;
|
|
20212
|
+
this.pendingLaunchDrain = void 0;
|
|
20213
|
+
releasePendingLaunch();
|
|
20219
20214
|
throw error;
|
|
20220
20215
|
}
|
|
20221
20216
|
}
|
|
20222
20217
|
async handleCancel(req) {
|
|
20223
|
-
const requestedId = req.data.turn_id ?? this.
|
|
20218
|
+
const requestedId = req.data.turn_id ?? this.getCurrentTurnId();
|
|
20224
20219
|
if (requestedId === void 0) return { ok: true };
|
|
20225
20220
|
await this.abortTurn(requestedId, "dispatch-cancel");
|
|
20226
20221
|
return { ok: true };
|
|
@@ -20258,16 +20253,27 @@ var TurnManager = class {
|
|
|
20258
20253
|
const parsed = Number.parseInt(match[1] ?? "1", 10);
|
|
20259
20254
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
|
|
20260
20255
|
}
|
|
20256
|
+
async executePreTurnHook(turnId, input, signal) {
|
|
20257
|
+
try {
|
|
20258
|
+
await this.executionHooks.runBlocking({
|
|
20259
|
+
event: "PreTurn",
|
|
20260
|
+
sessionId: this.sessionId,
|
|
20261
|
+
turnId,
|
|
20262
|
+
agentId: this.agentId,
|
|
20263
|
+
prompt: input.text,
|
|
20264
|
+
inputKind: "user"
|
|
20265
|
+
}, signal);
|
|
20266
|
+
} catch (error) {
|
|
20267
|
+
if (error instanceof ExecutionHookBlockedError) throw error;
|
|
20268
|
+
}
|
|
20269
|
+
}
|
|
20261
20270
|
/**
|
|
20262
|
-
* Fire-and-forget dispatch
|
|
20271
|
+
* Fire-and-forget dispatch for legacy lifecycle hook events. Tool-scoped
|
|
20263
20272
|
* events go through the actor-local `ToolExecutionScope`; this helper
|
|
20264
|
-
* only covers
|
|
20273
|
+
* only covers compatibility events TurnManager owns.
|
|
20265
20274
|
*/
|
|
20266
20275
|
dispatchLifecycleHook(input) {
|
|
20267
|
-
|
|
20268
|
-
if (engine === void 0) return;
|
|
20269
|
-
const controller = new AbortController();
|
|
20270
|
-
engine.executeHooks(input.event, input, controller.signal).catch(() => {});
|
|
20276
|
+
this.executionHooks.dispatch(input);
|
|
20271
20277
|
}
|
|
20272
20278
|
launchTurn(turnId, trigger) {
|
|
20273
20279
|
const controller = this.deps.soulRegistry.getOrCreate("main").abortController;
|
|
@@ -20312,6 +20318,32 @@ var TurnManager = class {
|
|
|
20312
20318
|
compactionConfig,
|
|
20313
20319
|
contextWindow: compactionConfig.maxContextSize
|
|
20314
20320
|
};
|
|
20321
|
+
if (this.deps.hookEngine !== void 0) soulConfig = {
|
|
20322
|
+
...soulConfig,
|
|
20323
|
+
beforeStep: async (ctx, signal) => {
|
|
20324
|
+
const result = await this.executionHooks.run({
|
|
20325
|
+
event: "PreStep",
|
|
20326
|
+
sessionId: this.sessionId,
|
|
20327
|
+
turnId: ctx.turnId,
|
|
20328
|
+
stepNumber: ctx.stepNumber,
|
|
20329
|
+
agentId: this.agentId
|
|
20330
|
+
}, signal);
|
|
20331
|
+
if (result.blockAction) return {
|
|
20332
|
+
block: true,
|
|
20333
|
+
...result.reason !== void 0 ? { reason: result.reason } : {}
|
|
20334
|
+
};
|
|
20335
|
+
},
|
|
20336
|
+
afterStep: async (ctx, signal) => {
|
|
20337
|
+
await this.executionHooks.run({
|
|
20338
|
+
event: "PostStep",
|
|
20339
|
+
sessionId: this.sessionId,
|
|
20340
|
+
turnId: ctx.turnId,
|
|
20341
|
+
stepNumber: ctx.stepNumber,
|
|
20342
|
+
agentId: this.agentId,
|
|
20343
|
+
stopReason: ctx.stopReason
|
|
20344
|
+
}, signal);
|
|
20345
|
+
}
|
|
20346
|
+
};
|
|
20315
20347
|
const input = trigger.input;
|
|
20316
20348
|
const runPromise = this.runTurn(turnId, input, soulConfig, runtimeForTurn, controller.signal);
|
|
20317
20349
|
runPromise.catch(() => {});
|
|
@@ -20418,6 +20450,16 @@ var TurnManager = class {
|
|
|
20418
20450
|
...result.usage.cache_write !== void 0 ? { cache_write_tokens: result.usage.cache_write } : {}
|
|
20419
20451
|
};
|
|
20420
20452
|
await this.deps.sessionJournal.appendTurnEnd(turnEnd);
|
|
20453
|
+
try {
|
|
20454
|
+
await this.executionHooks.run({
|
|
20455
|
+
event: "PostTurn",
|
|
20456
|
+
sessionId: this.sessionId,
|
|
20457
|
+
turnId,
|
|
20458
|
+
agentId: this.agentId,
|
|
20459
|
+
reason,
|
|
20460
|
+
success: reason === "done"
|
|
20461
|
+
}, new AbortController().signal);
|
|
20462
|
+
} catch {}
|
|
20421
20463
|
this.dispatchLifecycleHook({
|
|
20422
20464
|
event: "Stop",
|
|
20423
20465
|
sessionId: this.sessionId,
|
|
@@ -20670,6 +20712,7 @@ var SoulPlus = class {
|
|
|
20670
20712
|
hostToolNames;
|
|
20671
20713
|
planController;
|
|
20672
20714
|
setPlanModeImpl;
|
|
20715
|
+
stateCache;
|
|
20673
20716
|
/**
|
|
20674
20717
|
* Phase 18 A.3–A.6 — lazy `SessionControlHandler` owned by the
|
|
20675
20718
|
* facade. External hosts (SessionManager) still construct their own
|
|
@@ -20681,6 +20724,7 @@ var SoulPlus = class {
|
|
|
20681
20724
|
sessionControlInstance;
|
|
20682
20725
|
constructor(deps) {
|
|
20683
20726
|
this.sessionId = deps.sessionId;
|
|
20727
|
+
this.stateCache = deps.stateCache;
|
|
20684
20728
|
const eventBus = deps.eventBus;
|
|
20685
20729
|
const toolRegistry = [...deps.tools];
|
|
20686
20730
|
const enabledToolNames = deps.enabledToolNames !== void 0 ? new Set(deps.enabledToolNames) : void 0;
|
|
@@ -20703,7 +20747,6 @@ var SoulPlus = class {
|
|
|
20703
20747
|
const approvalRuntime = deps.approvalRuntime;
|
|
20704
20748
|
const sessionMeta = deps.stateCache !== void 0 && deps.initialMeta !== void 0 ? new SessionMetaService({
|
|
20705
20749
|
sessionId: deps.sessionId,
|
|
20706
|
-
sessionJournal,
|
|
20707
20750
|
eventBus,
|
|
20708
20751
|
stateCache: deps.stateCache,
|
|
20709
20752
|
initialMeta: deps.initialMeta
|
|
@@ -20838,6 +20881,19 @@ var SoulPlus = class {
|
|
|
20838
20881
|
const setPlanMode = async (enabled) => {
|
|
20839
20882
|
if (enabled) {
|
|
20840
20883
|
await planController.ensurePlanFilePath();
|
|
20884
|
+
await deps.stateCache?.update((current) => {
|
|
20885
|
+
const now = Date.now();
|
|
20886
|
+
return {
|
|
20887
|
+
...current ?? {
|
|
20888
|
+
session_id: deps.sessionId,
|
|
20889
|
+
created_at: now,
|
|
20890
|
+
updated_at: now
|
|
20891
|
+
},
|
|
20892
|
+
plan_mode: true,
|
|
20893
|
+
updated_at: now,
|
|
20894
|
+
last_exit_code: "dirty"
|
|
20895
|
+
};
|
|
20896
|
+
});
|
|
20841
20897
|
await contextState.applyConfigChange({
|
|
20842
20898
|
type: "plan_mode_changed",
|
|
20843
20899
|
enabled
|
|
@@ -20845,6 +20901,19 @@ var SoulPlus = class {
|
|
|
20845
20901
|
turnManager.setPlanMode(enabled);
|
|
20846
20902
|
return;
|
|
20847
20903
|
}
|
|
20904
|
+
await deps.stateCache?.update((current) => {
|
|
20905
|
+
const now = Date.now();
|
|
20906
|
+
return {
|
|
20907
|
+
...current ?? {
|
|
20908
|
+
session_id: deps.sessionId,
|
|
20909
|
+
created_at: now,
|
|
20910
|
+
updated_at: now
|
|
20911
|
+
},
|
|
20912
|
+
plan_mode: false,
|
|
20913
|
+
updated_at: now,
|
|
20914
|
+
last_exit_code: "dirty"
|
|
20915
|
+
};
|
|
20916
|
+
});
|
|
20848
20917
|
await contextState.applyConfigChange({
|
|
20849
20918
|
type: "plan_mode_changed",
|
|
20850
20919
|
enabled
|
|
@@ -20862,6 +20931,7 @@ var SoulPlus = class {
|
|
|
20862
20931
|
isPlanModeActive: () => turnManager.getPlanMode(),
|
|
20863
20932
|
setPlanMode,
|
|
20864
20933
|
isYoloMode: () => turnManager.getPermissionMode() === "bypassPermissions",
|
|
20934
|
+
shouldAutoApprove: () => true,
|
|
20865
20935
|
requestApproval: enterPlanRequestApproval,
|
|
20866
20936
|
getPlanFilePath: () => planController.getPlanFilePath()
|
|
20867
20937
|
}));
|
|
@@ -21098,19 +21168,49 @@ var SoulPlus = class {
|
|
|
21098
21168
|
turnManager: this.components.turnManager,
|
|
21099
21169
|
contextState: this.journal.contextState,
|
|
21100
21170
|
sessionJournal: this.journal.sessionJournal,
|
|
21101
|
-
setPlanModeOverride: (enabled) => this.setPlanMode(enabled)
|
|
21171
|
+
setPlanModeOverride: (enabled) => this.setPlanMode(enabled),
|
|
21172
|
+
setYoloOverride: async (enabled) => {
|
|
21173
|
+
const permissionMode = enabled ? "bypassPermissions" : "default";
|
|
21174
|
+
await this.stateCache?.update((current) => {
|
|
21175
|
+
const now = Date.now();
|
|
21176
|
+
return {
|
|
21177
|
+
...current ?? {
|
|
21178
|
+
session_id: this.sessionId,
|
|
21179
|
+
created_at: now,
|
|
21180
|
+
updated_at: now
|
|
21181
|
+
},
|
|
21182
|
+
permission_mode: permissionMode,
|
|
21183
|
+
updated_at: now,
|
|
21184
|
+
last_exit_code: "dirty"
|
|
21185
|
+
};
|
|
21186
|
+
});
|
|
21187
|
+
this.components.turnManager.setPermissionMode(permissionMode);
|
|
21188
|
+
}
|
|
21102
21189
|
});
|
|
21103
21190
|
return this.sessionControlInstance;
|
|
21104
21191
|
}
|
|
21105
21192
|
/**
|
|
21106
21193
|
* Phase 18 A.3 — programmatic model change. Applies the config
|
|
21107
|
-
* change event to ContextState
|
|
21194
|
+
* change event to ContextState, persists state.json, and emits a
|
|
21108
21195
|
* fresh `status.update` so downstream observers pick up the new
|
|
21109
21196
|
* model without waiting for the next turn boundary.
|
|
21110
21197
|
*/
|
|
21111
21198
|
async setModel(model) {
|
|
21112
21199
|
const oldModel = this.journal.contextState.model;
|
|
21113
21200
|
if (oldModel === model) return;
|
|
21201
|
+
await this.stateCache?.update((current) => {
|
|
21202
|
+
const now = Date.now();
|
|
21203
|
+
return {
|
|
21204
|
+
...current ?? {
|
|
21205
|
+
session_id: this.sessionId,
|
|
21206
|
+
created_at: now,
|
|
21207
|
+
updated_at: now
|
|
21208
|
+
},
|
|
21209
|
+
model,
|
|
21210
|
+
updated_at: now,
|
|
21211
|
+
last_exit_code: "dirty"
|
|
21212
|
+
};
|
|
21213
|
+
});
|
|
21114
21214
|
await this.journal.contextState.applyConfigChange({
|
|
21115
21215
|
type: "model_changed",
|
|
21116
21216
|
old_model: oldModel,
|
|
@@ -21127,8 +21227,7 @@ var SoulPlus = class {
|
|
|
21127
21227
|
}
|
|
21128
21228
|
/**
|
|
21129
21229
|
* Phase 18 A.6 / Phase 21 §A — programmatic thinking-level change.
|
|
21130
|
-
*
|
|
21131
|
-
* ContextState.applyConfigChange) and emits two SoulEvents:
|
|
21230
|
+
* Persists state.json, updates ContextState, and emits two SoulEvents:
|
|
21132
21231
|
* 1. `thinking.changed` — distinct event the wire bridge maps to a
|
|
21133
21232
|
* `thinking.changed` wire event with the bridge's per-session
|
|
21134
21233
|
* `seq` counter (replaces the old `seq: 0` direct send from
|
|
@@ -21137,6 +21236,19 @@ var SoulPlus = class {
|
|
|
21137
21236
|
* can repaint without subscribing to the dedicated event.
|
|
21138
21237
|
*/
|
|
21139
21238
|
async setThinking(level) {
|
|
21239
|
+
await this.stateCache?.update((current) => {
|
|
21240
|
+
const now = Date.now();
|
|
21241
|
+
return {
|
|
21242
|
+
...current ?? {
|
|
21243
|
+
session_id: this.sessionId,
|
|
21244
|
+
created_at: now,
|
|
21245
|
+
updated_at: now
|
|
21246
|
+
},
|
|
21247
|
+
thinking_level: level,
|
|
21248
|
+
updated_at: now,
|
|
21249
|
+
last_exit_code: "dirty"
|
|
21250
|
+
};
|
|
21251
|
+
});
|
|
21140
21252
|
await this.journal.contextState.applyConfigChange({
|
|
21141
21253
|
type: "thinking_changed",
|
|
21142
21254
|
level
|
|
@@ -21288,17 +21400,35 @@ var KosongAdapter = class {
|
|
|
21288
21400
|
}));
|
|
21289
21401
|
const onDelta = params.onDelta;
|
|
21290
21402
|
const onThinkDelta = params.onThinkDelta;
|
|
21403
|
+
const onToolCallPart = params.onToolCallPart;
|
|
21291
21404
|
const onAtomicPart = params.onAtomicPart;
|
|
21292
|
-
const needMessagePart = onDelta !== void 0 || onThinkDelta !== void 0;
|
|
21405
|
+
const needMessagePart = onDelta !== void 0 || onThinkDelta !== void 0 || onToolCallPart !== void 0;
|
|
21406
|
+
const toolCallsByStreamIndex = /* @__PURE__ */ new Map();
|
|
21407
|
+
let lastToolCall;
|
|
21293
21408
|
let result;
|
|
21294
21409
|
try {
|
|
21295
21410
|
result = await generate$1(activeProvider, params.systemPrompt, kosongTools, params.messages, needMessagePart ? { onMessagePart: async (part) => {
|
|
21296
21411
|
if (part.type === "text" && onDelta !== void 0) onDelta(part.text);
|
|
21297
21412
|
else if (part.type === "think" && onThinkDelta !== void 0) onThinkDelta(part.think);
|
|
21413
|
+
else if (part.type === "function") {
|
|
21414
|
+
lastToolCall = {
|
|
21415
|
+
id: part.id,
|
|
21416
|
+
name: part.function.name
|
|
21417
|
+
};
|
|
21418
|
+
if (part._streamIndex !== void 0) toolCallsByStreamIndex.set(part._streamIndex, lastToolCall);
|
|
21419
|
+
} else if (isToolCallPart(part) && onToolCallPart !== void 0) {
|
|
21420
|
+
const target = part.index !== void 0 ? toolCallsByStreamIndex.get(part.index) : lastToolCall;
|
|
21421
|
+
if (target !== void 0) onToolCallPart({
|
|
21422
|
+
type: "tool_call_part",
|
|
21423
|
+
tool_call_id: target.id,
|
|
21424
|
+
name: target.name,
|
|
21425
|
+
...part.argumentsPart !== null ? { arguments_chunk: part.argumentsPart } : {}
|
|
21426
|
+
});
|
|
21427
|
+
}
|
|
21298
21428
|
} } : void 0, { signal: params.signal });
|
|
21299
|
-
} catch (
|
|
21300
|
-
if (isContextOverflowProviderError(
|
|
21301
|
-
throw
|
|
21429
|
+
} catch (error) {
|
|
21430
|
+
if (isContextOverflowProviderError(error)) throw new ContextOverflowError(extractMessage(error));
|
|
21431
|
+
throw error;
|
|
21302
21432
|
}
|
|
21303
21433
|
if (onAtomicPart !== void 0) {
|
|
21304
21434
|
for (const part of result.message.content) if (part.type === "text" || part.type === "think") await onAtomicPart({
|
|
@@ -29313,6 +29443,28 @@ var SetTodoListTool = class {
|
|
|
29313
29443
|
}));
|
|
29314
29444
|
}
|
|
29315
29445
|
};
|
|
29446
|
+
//#endregion
|
|
29447
|
+
//#region ../../packages/kimi-core/src/hooks/supported-events.ts
|
|
29448
|
+
const SUPPORTED_HOOK_EVENTS = [
|
|
29449
|
+
"PreTurn",
|
|
29450
|
+
"PostTurn",
|
|
29451
|
+
"PreStep",
|
|
29452
|
+
"PostStep",
|
|
29453
|
+
"PreToolUse",
|
|
29454
|
+
"PostToolUse",
|
|
29455
|
+
"OnToolFailure",
|
|
29456
|
+
"UserPromptSubmit",
|
|
29457
|
+
"Stop",
|
|
29458
|
+
"StopFailure",
|
|
29459
|
+
"Notification",
|
|
29460
|
+
"SubagentStart",
|
|
29461
|
+
"SubagentStop",
|
|
29462
|
+
"SessionStart",
|
|
29463
|
+
"SessionEnd",
|
|
29464
|
+
"PreCompact",
|
|
29465
|
+
"PostCompact"
|
|
29466
|
+
];
|
|
29467
|
+
new Set(SUPPORTED_HOOK_EVENTS);
|
|
29316
29468
|
const _rawWireErrorSchema = z.object({
|
|
29317
29469
|
code: z.number(),
|
|
29318
29470
|
message: z.string(),
|
|
@@ -29505,188 +29657,332 @@ function createWireEvent(options) {
|
|
|
29505
29657
|
};
|
|
29506
29658
|
}
|
|
29507
29659
|
//#endregion
|
|
29660
|
+
//#region ../../packages/kimi-core/src/wire-protocol/event-pipeline.ts
|
|
29661
|
+
/**
|
|
29662
|
+
* WireEventPipeline is the ordered, awaited publication path from a translated
|
|
29663
|
+
* `WireEventDraft` to one or more event consumers.
|
|
29664
|
+
*
|
|
29665
|
+
* `enqueue()` intentionally returns void: upstream Soul/SoulPlus emitters must
|
|
29666
|
+
* not be back-pressured by transport, telemetry, or debug consumers. The
|
|
29667
|
+
* pipeline drains in the background and awaits each middleware/consumer
|
|
29668
|
+
* internally to preserve per-session event ordering.
|
|
29669
|
+
*/
|
|
29670
|
+
var WireEventPipeline = class {
|
|
29671
|
+
sessionId;
|
|
29672
|
+
getEventFilter;
|
|
29673
|
+
middlewares;
|
|
29674
|
+
consumers;
|
|
29675
|
+
queue = [];
|
|
29676
|
+
idleWaiters = [];
|
|
29677
|
+
draining = false;
|
|
29678
|
+
seq = 0;
|
|
29679
|
+
constructor(options) {
|
|
29680
|
+
this.sessionId = options.sessionId;
|
|
29681
|
+
this.getEventFilter = options.getEventFilter;
|
|
29682
|
+
this.consumers = options.consumers ?? [createTransportWireEventConsumer({
|
|
29683
|
+
transport: options.transport,
|
|
29684
|
+
codec: options.codec
|
|
29685
|
+
})];
|
|
29686
|
+
this.middlewares = [
|
|
29687
|
+
createWireEventFilterMiddleware(this.getEventFilter),
|
|
29688
|
+
...options.middlewares ?? [],
|
|
29689
|
+
this.createFrameMiddleware(),
|
|
29690
|
+
createWireEventConsumerMiddleware(this.consumers)
|
|
29691
|
+
];
|
|
29692
|
+
}
|
|
29693
|
+
enqueue(draft) {
|
|
29694
|
+
this.queue.push(draft);
|
|
29695
|
+
if (!this.draining) this.drain();
|
|
29696
|
+
}
|
|
29697
|
+
/**
|
|
29698
|
+
* Test / controlled-host helper: resolves once the queue that is currently
|
|
29699
|
+
* known to the pipeline has drained. Normal production callers should use
|
|
29700
|
+
* fire-and-forget `enqueue()`.
|
|
29701
|
+
*/
|
|
29702
|
+
async flush() {
|
|
29703
|
+
if (!this.draining && this.queue.length === 0) return;
|
|
29704
|
+
await new Promise((resolve) => {
|
|
29705
|
+
this.idleWaiters.push(resolve);
|
|
29706
|
+
});
|
|
29707
|
+
}
|
|
29708
|
+
async drain() {
|
|
29709
|
+
this.draining = true;
|
|
29710
|
+
let restart = false;
|
|
29711
|
+
try {
|
|
29712
|
+
while (this.queue.length > 0) {
|
|
29713
|
+
const draft = this.queue.shift();
|
|
29714
|
+
if (draft === void 0) continue;
|
|
29715
|
+
await this.processOne(draft);
|
|
29716
|
+
}
|
|
29717
|
+
} finally {
|
|
29718
|
+
this.draining = false;
|
|
29719
|
+
if (this.queue.length > 0) restart = true;
|
|
29720
|
+
else {
|
|
29721
|
+
const waiters = this.idleWaiters.splice(0);
|
|
29722
|
+
for (const resolve of waiters) resolve();
|
|
29723
|
+
}
|
|
29724
|
+
}
|
|
29725
|
+
if (restart) this.drain();
|
|
29726
|
+
}
|
|
29727
|
+
async processOne(draft) {
|
|
29728
|
+
const ctx = {
|
|
29729
|
+
sessionId: this.sessionId,
|
|
29730
|
+
draft
|
|
29731
|
+
};
|
|
29732
|
+
try {
|
|
29733
|
+
await composeWireEventMiddlewares(this.middlewares)(ctx);
|
|
29734
|
+
} catch {}
|
|
29735
|
+
}
|
|
29736
|
+
createFrameMiddleware() {
|
|
29737
|
+
return async (ctx, next) => {
|
|
29738
|
+
ctx.event = createWireEvent({
|
|
29739
|
+
method: ctx.draft.method,
|
|
29740
|
+
sessionId: ctx.sessionId,
|
|
29741
|
+
seq: this.seq++,
|
|
29742
|
+
...ctx.draft.requestId !== void 0 ? { requestId: ctx.draft.requestId } : {},
|
|
29743
|
+
...ctx.draft.turnId !== void 0 ? { turnId: ctx.draft.turnId } : {},
|
|
29744
|
+
agentType: ctx.draft.agentType ?? "main",
|
|
29745
|
+
...ctx.draft.source !== void 0 ? { source: ctx.draft.source } : {},
|
|
29746
|
+
data: ctx.draft.data
|
|
29747
|
+
});
|
|
29748
|
+
await next();
|
|
29749
|
+
};
|
|
29750
|
+
}
|
|
29751
|
+
};
|
|
29752
|
+
function createWireEventFilterMiddleware(getEventFilter) {
|
|
29753
|
+
return async (ctx, next) => {
|
|
29754
|
+
if (getEventFilter !== void 0) {
|
|
29755
|
+
const filter = getEventFilter();
|
|
29756
|
+
if (filter !== void 0 && !filter.has(ctx.draft.method)) return;
|
|
29757
|
+
}
|
|
29758
|
+
await next();
|
|
29759
|
+
};
|
|
29760
|
+
}
|
|
29761
|
+
function createTransportWireEventConsumer(options) {
|
|
29762
|
+
const codec = options.codec ?? new WireCodec();
|
|
29763
|
+
return {
|
|
29764
|
+
name: "transport",
|
|
29765
|
+
async consume(event) {
|
|
29766
|
+
try {
|
|
29767
|
+
await options.transport.send(codec.encode(event));
|
|
29768
|
+
} catch {}
|
|
29769
|
+
}
|
|
29770
|
+
};
|
|
29771
|
+
}
|
|
29772
|
+
function createWireEventConsumerMiddleware(consumers) {
|
|
29773
|
+
return async (ctx) => {
|
|
29774
|
+
if (ctx.event === void 0) return;
|
|
29775
|
+
const snapshot = [...consumers];
|
|
29776
|
+
for (const consumer of snapshot) try {
|
|
29777
|
+
await consumer.consume(ctx.event, {
|
|
29778
|
+
sessionId: ctx.sessionId,
|
|
29779
|
+
draft: ctx.draft
|
|
29780
|
+
});
|
|
29781
|
+
} catch {}
|
|
29782
|
+
};
|
|
29783
|
+
}
|
|
29784
|
+
function composeWireEventMiddlewares(middlewares) {
|
|
29785
|
+
return async (ctx) => {
|
|
29786
|
+
let index = -1;
|
|
29787
|
+
const dispatch = async (i) => {
|
|
29788
|
+
if (i <= index) throw new Error("wire event middleware called next() multiple times");
|
|
29789
|
+
index = i;
|
|
29790
|
+
const middleware = middlewares[i];
|
|
29791
|
+
if (middleware === void 0) return;
|
|
29792
|
+
await middleware(ctx, () => dispatch(i + 1));
|
|
29793
|
+
};
|
|
29794
|
+
await dispatch(0);
|
|
29795
|
+
};
|
|
29796
|
+
}
|
|
29797
|
+
//#endregion
|
|
29798
|
+
//#region ../../packages/kimi-core/src/wire-protocol/event-translators.ts
|
|
29799
|
+
function draft(event, ctx, method, data) {
|
|
29800
|
+
return {
|
|
29801
|
+
method,
|
|
29802
|
+
data,
|
|
29803
|
+
...ctx.currentTurnId !== void 0 ? { turnId: ctx.currentTurnId } : {},
|
|
29804
|
+
...event.source !== void 0 ? { source: event.source } : {}
|
|
29805
|
+
};
|
|
29806
|
+
}
|
|
29807
|
+
const BUS_EVENT_TRANSLATORS = {
|
|
29808
|
+
"step.begin": (event, ctx) => {
|
|
29809
|
+
return draft(event, ctx, "step.begin", { step: event.step });
|
|
29810
|
+
},
|
|
29811
|
+
"step.end": (event, ctx) => {
|
|
29812
|
+
return draft(event, ctx, "step.end", { step: event.step });
|
|
29813
|
+
},
|
|
29814
|
+
"step.interrupted": (event, ctx) => {
|
|
29815
|
+
return draft(event, ctx, "step.interrupted", {
|
|
29816
|
+
step: event.step,
|
|
29817
|
+
reason: event.reason
|
|
29818
|
+
});
|
|
29819
|
+
},
|
|
29820
|
+
"content.delta": (event, ctx) => {
|
|
29821
|
+
return draft(event, ctx, "content.delta", {
|
|
29822
|
+
type: "text",
|
|
29823
|
+
text: event.delta
|
|
29824
|
+
});
|
|
29825
|
+
},
|
|
29826
|
+
"thinking.delta": (event, ctx) => {
|
|
29827
|
+
return draft(event, ctx, "content.delta", {
|
|
29828
|
+
type: "thinking",
|
|
29829
|
+
thinking: event.delta
|
|
29830
|
+
});
|
|
29831
|
+
},
|
|
29832
|
+
tool_call_part: (event, ctx) => {
|
|
29833
|
+
return draft(event, ctx, "tool.call.delta", {
|
|
29834
|
+
tool_call_id: event.tool_call_id,
|
|
29835
|
+
...event.name !== void 0 ? { name: event.name } : {},
|
|
29836
|
+
arguments_part: event.arguments_chunk ?? null
|
|
29837
|
+
});
|
|
29838
|
+
},
|
|
29839
|
+
"tool.call": (event, ctx) => {
|
|
29840
|
+
return draft(event, ctx, "tool.call", {
|
|
29841
|
+
id: event.toolCallId,
|
|
29842
|
+
name: event.name,
|
|
29843
|
+
args: event.args
|
|
29844
|
+
});
|
|
29845
|
+
},
|
|
29846
|
+
"tool.progress": (event, ctx) => draft(event, ctx, "tool.progress", {
|
|
29847
|
+
id: event.toolCallId,
|
|
29848
|
+
update: event.update
|
|
29849
|
+
}),
|
|
29850
|
+
"tool.result": (event, ctx) => {
|
|
29851
|
+
return draft(event, ctx, "tool.result", {
|
|
29852
|
+
tool_call_id: event.toolCallId,
|
|
29853
|
+
output: event.output,
|
|
29854
|
+
...event.isError !== void 0 ? { is_error: event.isError } : {}
|
|
29855
|
+
});
|
|
29856
|
+
},
|
|
29857
|
+
"compaction.begin": (event, ctx) => {
|
|
29858
|
+
return draft(event, ctx, "compaction.begin", {});
|
|
29859
|
+
},
|
|
29860
|
+
"compaction.end": (event, ctx) => {
|
|
29861
|
+
return draft(event, ctx, "compaction.end", {
|
|
29862
|
+
...event.tokensBefore !== void 0 ? { tokens_before: event.tokensBefore } : {},
|
|
29863
|
+
...event.tokensAfter !== void 0 ? { tokens_after: event.tokensAfter } : {}
|
|
29864
|
+
});
|
|
29865
|
+
},
|
|
29866
|
+
"session.error": (event, ctx) => {
|
|
29867
|
+
return draft(event, ctx, "session.error", {
|
|
29868
|
+
error: event.error,
|
|
29869
|
+
...event.error_type !== void 0 ? { error_type: event.error_type } : {},
|
|
29870
|
+
...event.retry_after_ms !== void 0 ? { retry_after_ms: event.retry_after_ms } : {},
|
|
29871
|
+
...event.details !== void 0 ? { details: event.details } : {}
|
|
29872
|
+
});
|
|
29873
|
+
},
|
|
29874
|
+
"hook.triggered": (event, ctx) => draft(event, ctx, "hook.triggered", {
|
|
29875
|
+
event: event.event,
|
|
29876
|
+
matchers: event.matchers,
|
|
29877
|
+
matched_count: event.matched_count
|
|
29878
|
+
}),
|
|
29879
|
+
"hook.resolved": (event, ctx) => draft(event, ctx, "hook.resolved", {
|
|
29880
|
+
hook_id: event.hook_id,
|
|
29881
|
+
outcome: event.outcome
|
|
29882
|
+
}),
|
|
29883
|
+
"status.update": (event, ctx) => draft(event, ctx, "status.update", event.data),
|
|
29884
|
+
"session_meta.changed": (event, ctx) => draft(event, ctx, "session_meta.changed", event.data),
|
|
29885
|
+
"thinking.changed": (event, ctx) => draft(event, ctx, "thinking.changed", { level: event.level }),
|
|
29886
|
+
"skill.invoked": (event, ctx) => draft(event, ctx, "skill.invoked", event.data),
|
|
29887
|
+
"skill.completed": (event, ctx) => draft(event, ctx, "skill.completed", event.data),
|
|
29888
|
+
"mcp.loading": (event, ctx) => draft(event, ctx, "mcp.loading", event.data),
|
|
29889
|
+
"mcp.connected": (event, ctx) => draft(event, ctx, "mcp.connected", event.data),
|
|
29890
|
+
"mcp.disconnected": (event, ctx) => draft(event, ctx, "mcp.disconnected", event.data),
|
|
29891
|
+
"mcp.error": (event, ctx) => draft(event, ctx, "mcp.error", event.data),
|
|
29892
|
+
"mcp.tools_changed": (event, ctx) => draft(event, ctx, "mcp.tools_changed", event.data),
|
|
29893
|
+
"mcp.resources_changed": (event, ctx) => draft(event, ctx, "mcp.resources_changed", event.data),
|
|
29894
|
+
"mcp.auth_required": (event, ctx) => draft(event, ctx, "mcp.auth_required", event.data),
|
|
29895
|
+
"status.update.mcp_status": (event, ctx) => draft(event, ctx, "status.update.mcp_status", event.data),
|
|
29896
|
+
"subagent.spawned": (event, ctx) => draft(event, ctx, "subagent.spawned", event.data),
|
|
29897
|
+
"subagent.completed": (event, ctx) => draft(event, ctx, "subagent.completed", event.data),
|
|
29898
|
+
"subagent.failed": (event, ctx) => draft(event, ctx, "subagent.failed", event.data),
|
|
29899
|
+
"model.changed": () => {},
|
|
29900
|
+
"turn.end": () => {}
|
|
29901
|
+
};
|
|
29902
|
+
function translateBusEvent(event, currentTurnId) {
|
|
29903
|
+
const translator = BUS_EVENT_TRANSLATORS[event.type];
|
|
29904
|
+
return translator(event, currentTurnId !== void 0 ? { currentTurnId } : {});
|
|
29905
|
+
}
|
|
29906
|
+
function translateNotificationEvent(notif) {
|
|
29907
|
+
return {
|
|
29908
|
+
method: "notification",
|
|
29909
|
+
data: notif
|
|
29910
|
+
};
|
|
29911
|
+
}
|
|
29912
|
+
function translateTurnLifecycleEvent(event, currentTurnId) {
|
|
29913
|
+
if (event.kind === "begin") return {
|
|
29914
|
+
draft: {
|
|
29915
|
+
method: "turn.begin",
|
|
29916
|
+
data: {
|
|
29917
|
+
turn_id: event.turnId,
|
|
29918
|
+
user_input: event.userInputParts ?? event.userInput,
|
|
29919
|
+
input_kind: event.inputKind
|
|
29920
|
+
},
|
|
29921
|
+
turnId: event.turnId
|
|
29922
|
+
},
|
|
29923
|
+
nextTurnId: event.turnId
|
|
29924
|
+
};
|
|
29925
|
+
return {
|
|
29926
|
+
draft: {
|
|
29927
|
+
method: "turn.end",
|
|
29928
|
+
data: {
|
|
29929
|
+
turn_id: event.turnId,
|
|
29930
|
+
reason: event.reason,
|
|
29931
|
+
success: event.success,
|
|
29932
|
+
...event.usage !== void 0 ? { usage: {
|
|
29933
|
+
input_tokens: event.usage.input,
|
|
29934
|
+
output_tokens: event.usage.output,
|
|
29935
|
+
...event.usage.cache_read !== void 0 ? { cache_read_tokens: event.usage.cache_read } : {},
|
|
29936
|
+
...event.usage.cache_write !== void 0 ? { cache_write_tokens: event.usage.cache_write } : {}
|
|
29937
|
+
} } : {}
|
|
29938
|
+
},
|
|
29939
|
+
turnId: event.turnId
|
|
29940
|
+
},
|
|
29941
|
+
...currentTurnId === event.turnId && currentTurnId !== void 0 ? {} : currentTurnId !== void 0 ? { nextTurnId: currentTurnId } : {}
|
|
29942
|
+
};
|
|
29943
|
+
}
|
|
29944
|
+
//#endregion
|
|
29508
29945
|
//#region ../../packages/kimi-core/src/wire-protocol/event-bridge.ts
|
|
29509
29946
|
/**
|
|
29510
|
-
* WireEventBridge — SoulEvent + TurnLifecycleEvent →
|
|
29947
|
+
* WireEventBridge — SoulEvent + TurnLifecycleEvent → WireEventDraft adapter
|
|
29511
29948
|
* (Phase 17 §A.1).
|
|
29512
29949
|
*
|
|
29513
|
-
* Subscribes to
|
|
29514
|
-
* and forwards translated
|
|
29515
|
-
* Each bridge instance owns its own
|
|
29516
|
-
* pointer
|
|
29517
|
-
* Phase 10 test helper).
|
|
29950
|
+
* Subscribes to three input channels (SessionEventBus + notification channel
|
|
29951
|
+
* + TurnLifecycleTracker) and forwards translated drafts through a
|
|
29952
|
+
* per-session WireEventPipeline. Each bridge instance owns its own
|
|
29953
|
+
* `currentTurnId` pointer; the pipeline owns the per-session wire `seq`.
|
|
29518
29954
|
*
|
|
29519
29955
|
* Invariants (§5.0):
|
|
29520
|
-
* - L4: listeners return `void`; the bridge never awaits
|
|
29956
|
+
* - L4: listeners return `void`; the bridge never awaits pipeline drain
|
|
29521
29957
|
* - L5: listener does not touch wire.jsonl — translation is write-only
|
|
29522
29958
|
* toward the transport
|
|
29523
29959
|
*/
|
|
29524
|
-
const codec = new WireCodec();
|
|
29525
29960
|
function installWireEventBridge(opts) {
|
|
29526
29961
|
const { server, eventBus, addTurnLifecycleListener, sessionId, getEventFilter } = opts;
|
|
29527
|
-
let seq = 0;
|
|
29528
29962
|
let currentTurnId;
|
|
29529
|
-
const
|
|
29530
|
-
|
|
29531
|
-
|
|
29532
|
-
|
|
29533
|
-
|
|
29534
|
-
const frame = codec.encode(createWireEvent({
|
|
29535
|
-
method,
|
|
29536
|
-
sessionId,
|
|
29537
|
-
seq: seq++,
|
|
29538
|
-
...turnId !== void 0 ? { turnId } : {},
|
|
29539
|
-
agentType: "main",
|
|
29540
|
-
...source !== void 0 ? { source } : {},
|
|
29541
|
-
data
|
|
29542
|
-
}));
|
|
29543
|
-
server.send(frame).catch(() => {});
|
|
29544
|
-
};
|
|
29963
|
+
const pipeline = new WireEventPipeline({
|
|
29964
|
+
sessionId,
|
|
29965
|
+
transport: server,
|
|
29966
|
+
...getEventFilter !== void 0 ? { getEventFilter } : {}
|
|
29967
|
+
});
|
|
29545
29968
|
const soulListener = (event) => {
|
|
29546
|
-
|
|
29547
|
-
|
|
29548
|
-
|
|
29549
|
-
|
|
29550
|
-
|
|
29551
|
-
sendWire("step.end", { step: event.step }, currentTurnId, event.source);
|
|
29552
|
-
return;
|
|
29553
|
-
case "step.interrupted":
|
|
29554
|
-
sendWire("step.interrupted", {
|
|
29555
|
-
step: event.step,
|
|
29556
|
-
reason: event.reason
|
|
29557
|
-
}, currentTurnId, event.source);
|
|
29558
|
-
return;
|
|
29559
|
-
case "content.delta":
|
|
29560
|
-
sendWire("content.delta", {
|
|
29561
|
-
type: "text",
|
|
29562
|
-
text: event.delta
|
|
29563
|
-
}, currentTurnId, event.source);
|
|
29564
|
-
return;
|
|
29565
|
-
case "thinking.delta":
|
|
29566
|
-
sendWire("content.delta", {
|
|
29567
|
-
type: "thinking",
|
|
29568
|
-
thinking: event.delta
|
|
29569
|
-
}, currentTurnId, event.source);
|
|
29570
|
-
return;
|
|
29571
|
-
case "tool_call_part":
|
|
29572
|
-
sendWire("content.delta", {
|
|
29573
|
-
type: "tool_call_part",
|
|
29574
|
-
tool_call_id: event.tool_call_id,
|
|
29575
|
-
...event.name !== void 0 ? { name: event.name } : {},
|
|
29576
|
-
...event.arguments_chunk !== void 0 ? { arguments_chunk: event.arguments_chunk } : {}
|
|
29577
|
-
}, currentTurnId, event.source);
|
|
29578
|
-
return;
|
|
29579
|
-
case "tool.call":
|
|
29580
|
-
sendWire("tool.call", {
|
|
29581
|
-
id: event.toolCallId,
|
|
29582
|
-
name: event.name,
|
|
29583
|
-
args: event.args
|
|
29584
|
-
}, currentTurnId, event.source);
|
|
29585
|
-
return;
|
|
29586
|
-
case "tool.progress":
|
|
29587
|
-
sendWire("tool.progress", {
|
|
29588
|
-
id: event.toolCallId,
|
|
29589
|
-
update: event.update
|
|
29590
|
-
}, currentTurnId, event.source);
|
|
29591
|
-
return;
|
|
29592
|
-
case "tool.result":
|
|
29593
|
-
sendWire("tool.result", {
|
|
29594
|
-
tool_call_id: event.toolCallId,
|
|
29595
|
-
output: event.output,
|
|
29596
|
-
...event.isError !== void 0 ? { is_error: event.isError } : {}
|
|
29597
|
-
}, currentTurnId, event.source);
|
|
29598
|
-
return;
|
|
29599
|
-
case "compaction.begin":
|
|
29600
|
-
sendWire("compaction.begin", {}, currentTurnId, event.source);
|
|
29601
|
-
return;
|
|
29602
|
-
case "compaction.end":
|
|
29603
|
-
sendWire("compaction.end", {
|
|
29604
|
-
...event.tokensBefore !== void 0 ? { tokens_before: event.tokensBefore } : {},
|
|
29605
|
-
...event.tokensAfter !== void 0 ? { tokens_after: event.tokensAfter } : {}
|
|
29606
|
-
}, currentTurnId, event.source);
|
|
29607
|
-
return;
|
|
29608
|
-
case "session.error":
|
|
29609
|
-
sendWire("session.error", {
|
|
29610
|
-
error: event.error,
|
|
29611
|
-
...event.error_type !== void 0 ? { error_type: event.error_type } : {},
|
|
29612
|
-
...event.retry_after_ms !== void 0 ? { retry_after_ms: event.retry_after_ms } : {},
|
|
29613
|
-
...event.details !== void 0 ? { details: event.details } : {}
|
|
29614
|
-
}, currentTurnId, event.source);
|
|
29615
|
-
return;
|
|
29616
|
-
case "hook.triggered":
|
|
29617
|
-
sendWire("hook.triggered", {
|
|
29618
|
-
event: event.event,
|
|
29619
|
-
matchers: event.matchers,
|
|
29620
|
-
matched_count: event.matched_count
|
|
29621
|
-
}, currentTurnId, event.source);
|
|
29622
|
-
return;
|
|
29623
|
-
case "hook.resolved":
|
|
29624
|
-
sendWire("hook.resolved", {
|
|
29625
|
-
hook_id: event.hook_id,
|
|
29626
|
-
outcome: event.outcome
|
|
29627
|
-
}, currentTurnId, event.source);
|
|
29628
|
-
return;
|
|
29629
|
-
case "status.update":
|
|
29630
|
-
sendWire("status.update", event.data, currentTurnId, event.source);
|
|
29631
|
-
return;
|
|
29632
|
-
case "session_meta.changed":
|
|
29633
|
-
sendWire("session_meta.changed", event.data, currentTurnId, event.source);
|
|
29634
|
-
return;
|
|
29635
|
-
case "thinking.changed":
|
|
29636
|
-
sendWire("thinking.changed", { level: event.level }, currentTurnId, event.source);
|
|
29637
|
-
return;
|
|
29638
|
-
case "skill.invoked":
|
|
29639
|
-
sendWire("skill.invoked", event.data, currentTurnId, event.source);
|
|
29640
|
-
return;
|
|
29641
|
-
case "skill.completed":
|
|
29642
|
-
sendWire("skill.completed", event.data, currentTurnId, event.source);
|
|
29643
|
-
return;
|
|
29644
|
-
case "mcp.loading":
|
|
29645
|
-
case "mcp.connected":
|
|
29646
|
-
case "mcp.disconnected":
|
|
29647
|
-
case "mcp.error":
|
|
29648
|
-
case "mcp.tools_changed":
|
|
29649
|
-
case "mcp.resources_changed":
|
|
29650
|
-
case "mcp.auth_required":
|
|
29651
|
-
case "status.update.mcp_status":
|
|
29652
|
-
sendWire(event.type, event.data, currentTurnId, event.source);
|
|
29653
|
-
return;
|
|
29654
|
-
case "subagent.spawned":
|
|
29655
|
-
case "subagent.completed":
|
|
29656
|
-
case "subagent.failed":
|
|
29657
|
-
sendWire(event.type, event.data, currentTurnId, event.source);
|
|
29658
|
-
return;
|
|
29659
|
-
default:
|
|
29660
|
-
}
|
|
29969
|
+
const draft = translateBusEvent(event, currentTurnId);
|
|
29970
|
+
if (draft !== void 0) pipeline.enqueue(draft);
|
|
29971
|
+
};
|
|
29972
|
+
const notificationListener = (notif) => {
|
|
29973
|
+
pipeline.enqueue(translateNotificationEvent(notif));
|
|
29661
29974
|
};
|
|
29662
29975
|
const turnListener = (event) => {
|
|
29663
|
-
|
|
29664
|
-
|
|
29665
|
-
|
|
29666
|
-
turn_id: event.turnId,
|
|
29667
|
-
user_input: event.userInputParts ?? event.userInput,
|
|
29668
|
-
input_kind: event.inputKind
|
|
29669
|
-
}, event.turnId);
|
|
29670
|
-
return;
|
|
29671
|
-
}
|
|
29672
|
-
const turnId = event.turnId;
|
|
29673
|
-
sendWire("turn.end", {
|
|
29674
|
-
turn_id: turnId,
|
|
29675
|
-
reason: event.reason,
|
|
29676
|
-
success: event.success,
|
|
29677
|
-
...event.usage !== void 0 ? { usage: {
|
|
29678
|
-
input_tokens: event.usage.input,
|
|
29679
|
-
output_tokens: event.usage.output,
|
|
29680
|
-
...event.usage.cache_read !== void 0 ? { cache_read_tokens: event.usage.cache_read } : {},
|
|
29681
|
-
...event.usage.cache_write !== void 0 ? { cache_write_tokens: event.usage.cache_write } : {}
|
|
29682
|
-
} } : {}
|
|
29683
|
-
}, turnId);
|
|
29684
|
-
if (currentTurnId === turnId) currentTurnId = void 0;
|
|
29976
|
+
const translated = translateTurnLifecycleEvent(event, currentTurnId);
|
|
29977
|
+
currentTurnId = translated.nextTurnId;
|
|
29978
|
+
pipeline.enqueue(translated.draft);
|
|
29685
29979
|
};
|
|
29686
29980
|
const unsubTurn = addTurnLifecycleListener(turnListener);
|
|
29687
29981
|
eventBus.on(soulListener);
|
|
29982
|
+
eventBus.subscribeNotifications(notificationListener);
|
|
29688
29983
|
return { dispose() {
|
|
29689
29984
|
eventBus.off(soulListener);
|
|
29985
|
+
eventBus.unsubscribeNotifications(notificationListener);
|
|
29690
29986
|
unsubTurn();
|
|
29691
29987
|
} };
|
|
29692
29988
|
}
|
|
@@ -30116,6 +30412,48 @@ function errorMessage(error) {
|
|
|
30116
30412
|
return error instanceof Error ? error.message : String(error);
|
|
30117
30413
|
}
|
|
30118
30414
|
//#endregion
|
|
30415
|
+
//#region ../../packages/kimi-core/src/session/state-cache.ts
|
|
30416
|
+
/**
|
|
30417
|
+
* StateCache — state.json derived cache (§9续).
|
|
30418
|
+
*
|
|
30419
|
+
* Reads / writes `state.json` in a session directory. Used by
|
|
30420
|
+
* SessionManager to persist session metadata and runtime state (model,
|
|
30421
|
+
* permission mode, plan mode, last turn index) outside the transcript.
|
|
30422
|
+
*
|
|
30423
|
+
* Writes go through `atomicWrite` (write-tmp-fsync-rename, Decision #104)
|
|
30424
|
+
* so a crash mid-write never leaves a half-truncated state.json that
|
|
30425
|
+
* subsequent reads would treat as "missing". Read-modify-write callers
|
|
30426
|
+
* still need their own concurrency guard for "merge then write" races
|
|
30427
|
+
* across processes — see SessionManager.renameSession.
|
|
30428
|
+
*/
|
|
30429
|
+
var StateCache = class {
|
|
30430
|
+
writeQueue = Promise.resolve();
|
|
30431
|
+
constructor(statePath) {
|
|
30432
|
+
this.statePath = statePath;
|
|
30433
|
+
}
|
|
30434
|
+
async read() {
|
|
30435
|
+
try {
|
|
30436
|
+
const raw = await readFile(this.statePath, "utf-8");
|
|
30437
|
+
if (raw.length === 0) return null;
|
|
30438
|
+
return JSON.parse(raw);
|
|
30439
|
+
} catch {
|
|
30440
|
+
return null;
|
|
30441
|
+
}
|
|
30442
|
+
}
|
|
30443
|
+
async write(state) {
|
|
30444
|
+
await atomicWrite(this.statePath, JSON.stringify(state, null, 2));
|
|
30445
|
+
}
|
|
30446
|
+
async update(mutator) {
|
|
30447
|
+
const nextWrite = this.writeQueue.then(async () => {
|
|
30448
|
+
const next = mutator(await this.read());
|
|
30449
|
+
await this.write(next);
|
|
30450
|
+
return next;
|
|
30451
|
+
});
|
|
30452
|
+
this.writeQueue = nextWrite.then(() => {}, () => {});
|
|
30453
|
+
return nextWrite;
|
|
30454
|
+
}
|
|
30455
|
+
};
|
|
30456
|
+
//#endregion
|
|
30119
30457
|
//#region ../../packages/kimi-core/src/session/session-application-service.ts
|
|
30120
30458
|
function withCompactionConfigFallback(bundle, fallback) {
|
|
30121
30459
|
if (bundle.compactionConfig !== void 0 || fallback === void 0) return bundle;
|
|
@@ -30192,7 +30530,7 @@ var DefaultSessionApplicationService = class {
|
|
|
30192
30530
|
eventBus,
|
|
30193
30531
|
...hookEngine !== void 0 ? { hookEngine } : {}
|
|
30194
30532
|
});
|
|
30195
|
-
const initialModel = this.deps.defaultModelProvider();
|
|
30533
|
+
const initialModel = (await new StateCache(this.deps.pathConfig.statePath(sessionId)).read())?.model ?? this.deps.defaultModelProvider();
|
|
30196
30534
|
const runtimeBundle = await this.deps.runtimeBundleProvider?.({
|
|
30197
30535
|
sessionId,
|
|
30198
30536
|
model: initialModel
|
|
@@ -30268,22 +30606,13 @@ var DefaultSessionApplicationService = class {
|
|
|
30268
30606
|
}
|
|
30269
30607
|
async resumeSnapshot(sessionId) {
|
|
30270
30608
|
const managed = this.getManaged(sessionId);
|
|
30271
|
-
|
|
30272
|
-
|
|
30273
|
-
|
|
30274
|
-
const bodyLines = (await readFile(this.deps.pathConfig.wirePath(sessionId), "utf8")).split("\n").filter((line) => line.length > 0).slice(1);
|
|
30275
|
-
for (const line of bodyLines) try {
|
|
30276
|
-
const rec = JSON.parse(line);
|
|
30277
|
-
if (rec.type === "turn_begin") {
|
|
30278
|
-
turnCount += 1;
|
|
30279
|
-
if (rec.turn_id !== void 0) lastTurnId = rec.turn_id;
|
|
30280
|
-
}
|
|
30281
|
-
} catch {}
|
|
30282
|
-
} catch {}
|
|
30609
|
+
const state = await managed.stateCache.read();
|
|
30610
|
+
const turnCount = state?.turn_count ?? 0;
|
|
30611
|
+
const lastTurnId = state?.last_turn_id;
|
|
30283
30612
|
managed.eventBus.emit({
|
|
30284
30613
|
type: "status.update",
|
|
30285
30614
|
data: {
|
|
30286
|
-
model:
|
|
30615
|
+
model: managed.contextState.model,
|
|
30287
30616
|
plan_mode: managed.soulPlus.getTurnManager().getPlanMode(),
|
|
30288
30617
|
yolo: managed.soulPlus.getTurnManager().getPermissionMode() === "bypassPermissions"
|
|
30289
30618
|
}
|
|
@@ -30349,7 +30678,21 @@ var DefaultSessionApplicationService = class {
|
|
|
30349
30678
|
}
|
|
30350
30679
|
async setYolo(sessionId, enabled) {
|
|
30351
30680
|
const managed = this.getManaged(sessionId);
|
|
30352
|
-
|
|
30681
|
+
const permissionMode = enabled ? "bypassPermissions" : "default";
|
|
30682
|
+
await managed.stateCache.update((current) => {
|
|
30683
|
+
const now = Date.now();
|
|
30684
|
+
return {
|
|
30685
|
+
...current ?? {
|
|
30686
|
+
session_id: sessionId,
|
|
30687
|
+
created_at: now,
|
|
30688
|
+
updated_at: now
|
|
30689
|
+
},
|
|
30690
|
+
permission_mode: permissionMode,
|
|
30691
|
+
updated_at: now,
|
|
30692
|
+
last_exit_code: "dirty"
|
|
30693
|
+
};
|
|
30694
|
+
});
|
|
30695
|
+
managed.soulPlus.getTurnManager().setPermissionMode(permissionMode);
|
|
30353
30696
|
if (this.deps.approvalStateStore !== void 0) await this.deps.approvalStateStore.setYolo(enabled);
|
|
30354
30697
|
managed.soulPlus.getTurnManager().emitStatusUpdate({
|
|
30355
30698
|
input: 0,
|
|
@@ -30384,6 +30727,19 @@ var DefaultSessionApplicationService = class {
|
|
|
30384
30727
|
compactionConfig
|
|
30385
30728
|
});
|
|
30386
30729
|
}
|
|
30730
|
+
await managed.stateCache.update((current) => {
|
|
30731
|
+
const now = Date.now();
|
|
30732
|
+
return {
|
|
30733
|
+
...current ?? {
|
|
30734
|
+
session_id: sessionId,
|
|
30735
|
+
created_at: now,
|
|
30736
|
+
updated_at: now
|
|
30737
|
+
},
|
|
30738
|
+
model,
|
|
30739
|
+
updated_at: now,
|
|
30740
|
+
last_exit_code: "dirty"
|
|
30741
|
+
};
|
|
30742
|
+
});
|
|
30387
30743
|
await managed.soulPlus.setModel(model);
|
|
30388
30744
|
}
|
|
30389
30745
|
async setSystemPrompt(sessionId, prompt) {
|
|
@@ -30393,7 +30749,21 @@ var DefaultSessionApplicationService = class {
|
|
|
30393
30749
|
});
|
|
30394
30750
|
}
|
|
30395
30751
|
async setThinking(sessionId, level) {
|
|
30396
|
-
|
|
30752
|
+
const managed = this.getManaged(sessionId);
|
|
30753
|
+
await managed.stateCache.update((current) => {
|
|
30754
|
+
const now = Date.now();
|
|
30755
|
+
return {
|
|
30756
|
+
...current ?? {
|
|
30757
|
+
session_id: sessionId,
|
|
30758
|
+
created_at: now,
|
|
30759
|
+
updated_at: now
|
|
30760
|
+
},
|
|
30761
|
+
thinking_level: level,
|
|
30762
|
+
updated_at: now,
|
|
30763
|
+
last_exit_code: "dirty"
|
|
30764
|
+
};
|
|
30765
|
+
});
|
|
30766
|
+
await managed.soulPlus.setThinking(level);
|
|
30397
30767
|
}
|
|
30398
30768
|
async addSystemReminder(sessionId, content) {
|
|
30399
30769
|
await this.getManaged(sessionId).soulPlus.addSystemReminder(content);
|
|
@@ -30448,7 +30818,7 @@ var ChangeListenerRegistry = class {
|
|
|
30448
30818
|
}
|
|
30449
30819
|
};
|
|
30450
30820
|
/**
|
|
30451
|
-
* Production adapter that reads / writes `auto_approve_actions` + `
|
|
30821
|
+
* Production adapter that reads / writes `auto_approve_actions` + `permission_mode`
|
|
30452
30822
|
* on the session's `state.json` file via the existing `StateCache`
|
|
30453
30823
|
* service. The rest of the state.json fields are preserved on write —
|
|
30454
30824
|
* we only rewrite the approval-state fields.
|
|
@@ -30476,7 +30846,7 @@ var SessionStateApprovalStateStore = class {
|
|
|
30476
30846
|
const raw = state?.auto_approve_actions;
|
|
30477
30847
|
const actions = raw === void 0 ? /* @__PURE__ */ new Set() : new Set(raw);
|
|
30478
30848
|
this.cachedActions = new Set(actions);
|
|
30479
|
-
this.cachedYolo = state
|
|
30849
|
+
this.cachedYolo = permissionModeFromState(state) === "bypassPermissions";
|
|
30480
30850
|
return actions;
|
|
30481
30851
|
}
|
|
30482
30852
|
async save(actions) {
|
|
@@ -30487,7 +30857,7 @@ var SessionStateApprovalStateStore = class {
|
|
|
30487
30857
|
async getYolo() {
|
|
30488
30858
|
if (this.cachedYolo !== void 0) return this.cachedYolo;
|
|
30489
30859
|
const state = await this.stateCache.read();
|
|
30490
|
-
this.cachedYolo = state
|
|
30860
|
+
this.cachedYolo = permissionModeFromState(state) === "bypassPermissions";
|
|
30491
30861
|
this.cachedActions = new Set(state?.auto_approve_actions ?? []);
|
|
30492
30862
|
return this.cachedYolo;
|
|
30493
30863
|
}
|
|
@@ -30505,7 +30875,7 @@ var SessionStateApprovalStateStore = class {
|
|
|
30505
30875
|
const current = await this.stateCache.read();
|
|
30506
30876
|
const now = this.now();
|
|
30507
30877
|
const nextActions = patch.actions !== void 0 ? [...patch.actions] : current?.auto_approve_actions !== void 0 ? [...current.auto_approve_actions] : void 0;
|
|
30508
|
-
const
|
|
30878
|
+
const nextMode = patch.yolo !== void 0 ? patch.yolo ? "bypassPermissions" : "default" : permissionModeFromState(current);
|
|
30509
30879
|
const next = {
|
|
30510
30880
|
...current ? {
|
|
30511
30881
|
...current,
|
|
@@ -30516,7 +30886,7 @@ var SessionStateApprovalStateStore = class {
|
|
|
30516
30886
|
updated_at: now
|
|
30517
30887
|
},
|
|
30518
30888
|
...nextActions !== void 0 ? { auto_approve_actions: nextActions } : {},
|
|
30519
|
-
|
|
30889
|
+
permission_mode: nextMode
|
|
30520
30890
|
};
|
|
30521
30891
|
await this.stateCache.write(next);
|
|
30522
30892
|
}
|
|
@@ -30527,6 +30897,11 @@ var SessionStateApprovalStateStore = class {
|
|
|
30527
30897
|
});
|
|
30528
30898
|
}
|
|
30529
30899
|
};
|
|
30900
|
+
function permissionModeFromState(state) {
|
|
30901
|
+
const mode = state?.permission_mode;
|
|
30902
|
+
if (mode === "acceptEdits" || mode === "bypassPermissions") return mode;
|
|
30903
|
+
return "default";
|
|
30904
|
+
}
|
|
30530
30905
|
//#endregion
|
|
30531
30906
|
//#region ../../packages/kimi-core/src/wire-protocol/reverse-rpc.ts
|
|
30532
30907
|
/**
|
|
@@ -30686,6 +31061,10 @@ function buildHookRequestData(subscriptionId, input) {
|
|
|
30686
31061
|
base["tool_call_id"] = input.toolCall.id;
|
|
30687
31062
|
base["args"] = input.toolCall.args;
|
|
30688
31063
|
}
|
|
31064
|
+
if (input.event === "PreTurn" || input.event === "UserPromptSubmit") base["prompt"] = input.prompt;
|
|
31065
|
+
if (input.event === "PostTurn" || input.event === "Stop") base["reason"] = input.reason;
|
|
31066
|
+
if (input.event === "PreStep" || input.event === "PostStep") base["step_number"] = input.stepNumber;
|
|
31067
|
+
if (input.event === "PostStep") base["stop_reason"] = input.stopReason;
|
|
30689
31068
|
return base;
|
|
30690
31069
|
}
|
|
30691
31070
|
//#endregion
|
|
@@ -30728,21 +31107,6 @@ function buildHookRequestData(subscriptionId, input) {
|
|
|
30728
31107
|
* - `registerDefaultWireHandlers` returns a handle so the host can
|
|
30729
31108
|
* plug per-session state (event filter for now) into the bridge.
|
|
30730
31109
|
*/
|
|
30731
|
-
const HOOK_SUPPORTED_EVENTS = [
|
|
30732
|
-
"PreToolUse",
|
|
30733
|
-
"PostToolUse",
|
|
30734
|
-
"OnToolFailure",
|
|
30735
|
-
"UserPromptSubmit",
|
|
30736
|
-
"Stop",
|
|
30737
|
-
"StopFailure",
|
|
30738
|
-
"Notification",
|
|
30739
|
-
"SubagentStart",
|
|
30740
|
-
"SubagentStop",
|
|
30741
|
-
"SessionStart",
|
|
30742
|
-
"SessionEnd",
|
|
30743
|
-
"PreCompact",
|
|
30744
|
-
"PostCompact"
|
|
30745
|
-
];
|
|
30746
31110
|
const SUPPORTED_WIRE_EVENTS = [
|
|
30747
31111
|
"turn.begin",
|
|
30748
31112
|
"turn.end",
|
|
@@ -30981,7 +31345,7 @@ function registerDefaultWireHandlers(deps) {
|
|
|
30981
31345
|
events: [...SUPPORTED_WIRE_EVENTS],
|
|
30982
31346
|
methods: [...SUPPORTED_WIRE_METHODS],
|
|
30983
31347
|
hooks: {
|
|
30984
|
-
supported_events:
|
|
31348
|
+
supported_events: SUPPORTED_HOOK_EVENTS,
|
|
30985
31349
|
configured: initialHooks.map((h) => ({
|
|
30986
31350
|
event: h.event,
|
|
30987
31351
|
...typeof h.matcher === "string" && h.matcher.length > 0 ? { matcher: h.matcher } : {}
|
|
@@ -32063,12 +32427,6 @@ async function repairJournal(options) {
|
|
|
32063
32427
|
}
|
|
32064
32428
|
//#endregion
|
|
32065
32429
|
//#region ../../packages/kimi-core/src/session/replay-projector.ts
|
|
32066
|
-
/** Whitelist of valid PermissionMode values for safe runtime validation. */
|
|
32067
|
-
const VALID_PERMISSION_MODES = new Set([
|
|
32068
|
-
"default",
|
|
32069
|
-
"acceptEdits",
|
|
32070
|
-
"bypassPermissions"
|
|
32071
|
-
]);
|
|
32072
32430
|
function isContentPart(value) {
|
|
32073
32431
|
if (typeof value !== "object" || value === null) return false;
|
|
32074
32432
|
const part = value;
|
|
@@ -32084,26 +32442,26 @@ function cloneContentPart(part) {
|
|
|
32084
32442
|
* Project replayed WireRecords into the initial state needed to hydrate a
|
|
32085
32443
|
* resumed `WiredContextState`.
|
|
32086
32444
|
*
|
|
32087
|
-
*
|
|
32088
|
-
*
|
|
32089
|
-
* plan_mode
|
|
32090
|
-
*
|
|
32445
|
+
* `sessionInitialized` is the truth source for wire-owned startup context
|
|
32446
|
+
* (system_prompt / active_tools). Runtime/session config (model,
|
|
32447
|
+
* permission_mode, plan_mode, workspace_dir, thinking_level) comes from
|
|
32448
|
+
* `runtimeBaseline`, which is built from state.json by SessionManager.
|
|
32091
32449
|
*
|
|
32092
32450
|
* @param records — ordered records from `replayWire().records`
|
|
32093
32451
|
* @param sessionInitialized — the line-2 baseline from `replayWire().sessionInitialized`
|
|
32094
32452
|
* @returns Projected state suitable for `WiredContextState` constructor.
|
|
32095
32453
|
*/
|
|
32096
|
-
function projectReplayState(records, sessionInitialized) {
|
|
32454
|
+
function projectReplayState(records, sessionInitialized, runtimeBaseline) {
|
|
32097
32455
|
let messages = [];
|
|
32098
|
-
let model = sessionInitialized.model;
|
|
32456
|
+
let model = runtimeBaseline?.model ?? sessionInitialized.model ?? "";
|
|
32099
32457
|
let systemPrompt = sessionInitialized.system_prompt;
|
|
32100
32458
|
const activeTools = new Set(sessionInitialized.active_tools);
|
|
32101
32459
|
let lastSeq = 0;
|
|
32102
|
-
let permissionMode = sessionInitialized.permission_mode;
|
|
32460
|
+
let permissionMode = runtimeBaseline?.permissionMode ?? sessionInitialized.permission_mode ?? "default";
|
|
32103
32461
|
let tokenCount = 0;
|
|
32104
|
-
let planMode = sessionInitialized.plan_mode;
|
|
32105
|
-
let thinkingLevel = sessionInitialized.thinking_level;
|
|
32106
|
-
const workspaceDir = sessionInitialized.workspace_dir;
|
|
32462
|
+
let planMode = runtimeBaseline?.planMode ?? sessionInitialized.plan_mode ?? false;
|
|
32463
|
+
let thinkingLevel = runtimeBaseline?.thinkingLevel ?? sessionInitialized.thinking_level;
|
|
32464
|
+
const workspaceDir = runtimeBaseline?.workspaceDir ?? sessionInitialized.workspace_dir ?? process.cwd();
|
|
32107
32465
|
const sessionMetaPatch = { turn_count: 0 };
|
|
32108
32466
|
let todos = [];
|
|
32109
32467
|
const pendingTodoWrites = /* @__PURE__ */ new Map();
|
|
@@ -32184,21 +32542,9 @@ function projectReplayState(records, sessionInitialized) {
|
|
|
32184
32542
|
}];
|
|
32185
32543
|
tokenCount = r.post_compact_tokens;
|
|
32186
32544
|
break;
|
|
32187
|
-
case "model_changed":
|
|
32188
|
-
model = r.new_model;
|
|
32189
|
-
sessionMetaPatch.last_model = r.new_model;
|
|
32190
|
-
break;
|
|
32191
32545
|
case "turn_begin":
|
|
32192
32546
|
sessionMetaPatch.turn_count += 1;
|
|
32193
32547
|
break;
|
|
32194
|
-
case "session_meta_changed":
|
|
32195
|
-
if (r.patch.title !== void 0) sessionMetaPatch.title = r.patch.title;
|
|
32196
|
-
if (r.patch.tags !== void 0) sessionMetaPatch.tags = [...r.patch.tags];
|
|
32197
|
-
if (r.patch.description !== void 0) sessionMetaPatch.description = r.patch.description;
|
|
32198
|
-
if (r.patch.archived !== void 0) sessionMetaPatch.archived = r.patch.archived;
|
|
32199
|
-
if (r.patch.color !== void 0) sessionMetaPatch.color = r.patch.color;
|
|
32200
|
-
if (r.patch.plan_slug !== void 0) sessionMetaPatch.plan_slug = r.patch.plan_slug;
|
|
32201
|
-
break;
|
|
32202
32548
|
case "system_prompt_changed":
|
|
32203
32549
|
systemPrompt = r.new_prompt;
|
|
32204
32550
|
break;
|
|
@@ -32209,15 +32555,6 @@ function projectReplayState(records, sessionInitialized) {
|
|
|
32209
32555
|
} else if (r.operation === "register") for (const t of r.tools) activeTools.add(t);
|
|
32210
32556
|
else if (r.operation === "remove") for (const t of r.tools) activeTools.delete(t);
|
|
32211
32557
|
break;
|
|
32212
|
-
case "permission_mode_changed":
|
|
32213
|
-
if (VALID_PERMISSION_MODES.has(r.data.to)) permissionMode = r.data.to;
|
|
32214
|
-
break;
|
|
32215
|
-
case "plan_mode_changed":
|
|
32216
|
-
planMode = r.enabled;
|
|
32217
|
-
break;
|
|
32218
|
-
case "thinking_changed":
|
|
32219
|
-
thinkingLevel = r.level;
|
|
32220
|
-
break;
|
|
32221
32558
|
case "context_cleared":
|
|
32222
32559
|
flushOpenStep();
|
|
32223
32560
|
messages = [];
|
|
@@ -32362,38 +32699,6 @@ var SessionTodoStore = class {
|
|
|
32362
32699
|
}
|
|
32363
32700
|
};
|
|
32364
32701
|
//#endregion
|
|
32365
|
-
//#region ../../packages/kimi-core/src/session/state-cache.ts
|
|
32366
|
-
/**
|
|
32367
|
-
* StateCache — state.json derived cache (§9续).
|
|
32368
|
-
*
|
|
32369
|
-
* Reads / writes `state.json` in a session directory. Used by
|
|
32370
|
-
* SessionManager to persist quick-access session metadata (model,
|
|
32371
|
-
* status, last turn timestamp) without replaying the full wire.jsonl.
|
|
32372
|
-
*
|
|
32373
|
-
* Writes go through `atomicWrite` (write-tmp-fsync-rename, Decision #104)
|
|
32374
|
-
* so a crash mid-write never leaves a half-truncated state.json that
|
|
32375
|
-
* subsequent reads would treat as "missing". Read-modify-write callers
|
|
32376
|
-
* still need their own concurrency guard for "merge then write" races
|
|
32377
|
-
* across processes — see SessionManager.renameSession.
|
|
32378
|
-
*/
|
|
32379
|
-
var StateCache = class {
|
|
32380
|
-
constructor(statePath) {
|
|
32381
|
-
this.statePath = statePath;
|
|
32382
|
-
}
|
|
32383
|
-
async read() {
|
|
32384
|
-
try {
|
|
32385
|
-
const raw = await readFile(this.statePath, "utf-8");
|
|
32386
|
-
if (raw.length === 0) return null;
|
|
32387
|
-
return JSON.parse(raw);
|
|
32388
|
-
} catch {
|
|
32389
|
-
return null;
|
|
32390
|
-
}
|
|
32391
|
-
}
|
|
32392
|
-
async write(state) {
|
|
32393
|
-
await atomicWrite(this.statePath, JSON.stringify(state, null, 2));
|
|
32394
|
-
}
|
|
32395
|
-
};
|
|
32396
|
-
//#endregion
|
|
32397
32702
|
//#region ../../packages/kimi-core/src/session/usage-aggregator.ts
|
|
32398
32703
|
/**
|
|
32399
32704
|
* Token usage aggregator — replays a wire.jsonl and sums usage from
|
|
@@ -32553,6 +32858,17 @@ function normalizeRuntimeSlot(slot, fallback) {
|
|
|
32553
32858
|
});
|
|
32554
32859
|
return runtimeSlot;
|
|
32555
32860
|
}
|
|
32861
|
+
function normalizePermissionMode(value) {
|
|
32862
|
+
if (value === "acceptEdits" || value === "bypassPermissions") return value;
|
|
32863
|
+
return "default";
|
|
32864
|
+
}
|
|
32865
|
+
function createFallbackState(sessionId, now) {
|
|
32866
|
+
return {
|
|
32867
|
+
session_id: sessionId,
|
|
32868
|
+
created_at: now,
|
|
32869
|
+
updated_at: now
|
|
32870
|
+
};
|
|
32871
|
+
}
|
|
32556
32872
|
var SessionManager = class {
|
|
32557
32873
|
paths;
|
|
32558
32874
|
producer;
|
|
@@ -32590,6 +32906,29 @@ var SessionManager = class {
|
|
|
32590
32906
|
this.stateWriteMutex.set(sessionId, next.then(() => {}, () => {}));
|
|
32591
32907
|
return next;
|
|
32592
32908
|
}
|
|
32909
|
+
attachStateTracker(sessionId, stateCache, turnManager) {
|
|
32910
|
+
return turnManager.addTurnLifecycleListener((event) => {
|
|
32911
|
+
const now = Date.now();
|
|
32912
|
+
stateCache.update((current) => {
|
|
32913
|
+
const base = current ?? createFallbackState(sessionId, now);
|
|
32914
|
+
if (event.kind === "begin") return {
|
|
32915
|
+
...base,
|
|
32916
|
+
last_turn_id: event.turnId,
|
|
32917
|
+
last_turn_time: now,
|
|
32918
|
+
updated_at: now,
|
|
32919
|
+
last_exit_code: "dirty"
|
|
32920
|
+
};
|
|
32921
|
+
return {
|
|
32922
|
+
...base,
|
|
32923
|
+
turn_count: (base.turn_count ?? 0) + 1,
|
|
32924
|
+
last_turn_id: event.turnId,
|
|
32925
|
+
last_turn_time: now,
|
|
32926
|
+
updated_at: now,
|
|
32927
|
+
last_exit_code: "dirty"
|
|
32928
|
+
};
|
|
32929
|
+
}).catch(() => {});
|
|
32930
|
+
});
|
|
32931
|
+
}
|
|
32593
32932
|
async createSession(options) {
|
|
32594
32933
|
const sessionId = options.sessionId ?? `ses_${randomUUID().replaceAll("-", "").slice(0, 12)}`;
|
|
32595
32934
|
if (this.sessions.has(sessionId)) throw new Error(`Session already exists: ${sessionId}`);
|
|
@@ -32623,11 +32962,7 @@ var SessionManager = class {
|
|
|
32623
32962
|
agent_type: "main",
|
|
32624
32963
|
session_id: sessionId,
|
|
32625
32964
|
system_prompt: options.systemPrompt ?? "",
|
|
32626
|
-
|
|
32627
|
-
active_tools: sessionTools.map((t) => t.name),
|
|
32628
|
-
permission_mode: options.permissionMode ?? "default",
|
|
32629
|
-
plan_mode: false,
|
|
32630
|
-
workspace_dir: options.workspaceDir
|
|
32965
|
+
active_tools: sessionTools.map((t) => t.name)
|
|
32631
32966
|
};
|
|
32632
32967
|
await journalWriter.append(mainInitInput);
|
|
32633
32968
|
const sessionJournal = new WiredSessionJournalImpl(journalWriter);
|
|
@@ -32643,10 +32978,12 @@ var SessionManager = class {
|
|
|
32643
32978
|
await stateCache.write({
|
|
32644
32979
|
session_id: sessionId,
|
|
32645
32980
|
model: options.model,
|
|
32646
|
-
status: "active",
|
|
32647
32981
|
created_at: now,
|
|
32648
32982
|
updated_at: now,
|
|
32649
32983
|
workspace_dir: options.workspaceDir,
|
|
32984
|
+
permission_mode: options.permissionMode ?? "default",
|
|
32985
|
+
plan_mode: false,
|
|
32986
|
+
turn_count: 0,
|
|
32650
32987
|
last_exit_code: "dirty",
|
|
32651
32988
|
producer: this.producer,
|
|
32652
32989
|
todos: []
|
|
@@ -32703,6 +33040,7 @@ var SessionManager = class {
|
|
|
32703
33040
|
...options.logger !== void 0 ? { logger: options.logger } : {}
|
|
32704
33041
|
});
|
|
32705
33042
|
turnManagerRef = soulPlus.getTurnManager();
|
|
33043
|
+
const disposeStateTracking = this.attachStateTracker(sessionId, stateCache, turnManagerRef);
|
|
32706
33044
|
await soulPlus.init();
|
|
32707
33045
|
if (options.permissionMode !== void 0) turnManagerRef.setPermissionMode(options.permissionMode);
|
|
32708
33046
|
const managed = {
|
|
@@ -32712,7 +33050,20 @@ var SessionManager = class {
|
|
|
32712
33050
|
turnManager: turnManagerRef,
|
|
32713
33051
|
contextState,
|
|
32714
33052
|
sessionJournal,
|
|
32715
|
-
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled)
|
|
33053
|
+
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled),
|
|
33054
|
+
setYoloOverride: async (enabled) => {
|
|
33055
|
+
const permissionMode = enabled ? "bypassPermissions" : "default";
|
|
33056
|
+
await stateCache.update((current) => {
|
|
33057
|
+
const now = Date.now();
|
|
33058
|
+
return {
|
|
33059
|
+
...current ?? createFallbackState(sessionId, now),
|
|
33060
|
+
permission_mode: permissionMode,
|
|
33061
|
+
updated_at: now,
|
|
33062
|
+
last_exit_code: "dirty"
|
|
33063
|
+
};
|
|
33064
|
+
});
|
|
33065
|
+
turnManagerRef.setPermissionMode(permissionMode);
|
|
33066
|
+
}
|
|
32716
33067
|
}),
|
|
32717
33068
|
contextState,
|
|
32718
33069
|
eventBus,
|
|
@@ -32721,6 +33072,7 @@ var SessionManager = class {
|
|
|
32721
33072
|
journalWriter,
|
|
32722
33073
|
runtimeSlot,
|
|
32723
33074
|
lifecycleStateMachine,
|
|
33075
|
+
disposeStateTracking,
|
|
32724
33076
|
todoStore
|
|
32725
33077
|
};
|
|
32726
33078
|
this.sessions.set(sessionId, managed);
|
|
@@ -32743,7 +33095,18 @@ var SessionManager = class {
|
|
|
32743
33095
|
throw new Error(`Failed to replay session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
32744
33096
|
}
|
|
32745
33097
|
if (replayResult.sessionInitialized.agent_type !== "main") throw new MalformedWireError("agent-type-mismatch", `resumeSession expected agent_type='main' on wire line 2 for "${sessionId}", got agent_type='${replayResult.sessionInitialized.agent_type}'`);
|
|
32746
|
-
const
|
|
33098
|
+
const stateCache = new StateCache(this.paths.statePath(sessionId));
|
|
33099
|
+
const stateData = await stateCache.read();
|
|
33100
|
+
if (stateData === null) throw new Error(`resumeSession: state.json missing or unreadable for session "${sessionId}"`);
|
|
33101
|
+
if (stateData.model === void 0 || stateData.model.length === 0) throw new Error(`resumeSession: state.json missing model for session "${sessionId}"`);
|
|
33102
|
+
const statePermissionMode = normalizePermissionMode(stateData.permission_mode);
|
|
33103
|
+
const projected = projectReplayState(replayResult.records, replayResult.sessionInitialized, {
|
|
33104
|
+
model: stateData.model,
|
|
33105
|
+
permissionMode: statePermissionMode,
|
|
33106
|
+
planMode: stateData.plan_mode ?? false,
|
|
33107
|
+
workspaceDir: stateData.workspace_dir ?? process.cwd(),
|
|
33108
|
+
...stateData.thinking_level !== void 0 ? { thinkingLevel: stateData.thinking_level } : {}
|
|
33109
|
+
});
|
|
32747
33110
|
const lifecycleStateMachine = new SessionLifecycleStateMachine();
|
|
32748
33111
|
const lifecycleGate = new SoulLifecycleGate(lifecycleStateMachine);
|
|
32749
33112
|
let contextStateRef;
|
|
@@ -32778,10 +33141,7 @@ var SessionManager = class {
|
|
|
32778
33141
|
sessionJournal,
|
|
32779
33142
|
currentTurnId: () => turnManagerRef?.getCurrentTurnId() ?? "recovery"
|
|
32780
33143
|
});
|
|
32781
|
-
const
|
|
32782
|
-
const stateData = await stateCache.read();
|
|
32783
|
-
const isClean = stateData?.last_exit_code === "clean";
|
|
32784
|
-
const initialTodos = isClean ? cloneTodos(stateData?.todos ?? projected.todos) : cloneTodos(projected.todos);
|
|
33144
|
+
const initialTodos = stateData.last_exit_code === "clean" ? cloneTodos(stateData.todos ?? projected.todos) : cloneTodos(projected.todos);
|
|
32785
33145
|
const todoStore = new SessionTodoStore(stateCache, initialTodos);
|
|
32786
33146
|
const sessionTools = bindTodoStore(options.tools, todoStore);
|
|
32787
33147
|
const runtimeSlot = normalizeRuntimeSlot(options.runtimeSlot, {
|
|
@@ -32791,32 +33151,25 @@ var SessionManager = class {
|
|
|
32791
33151
|
...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {}
|
|
32792
33152
|
});
|
|
32793
33153
|
const runtimeBundle = runtimeSlot.current();
|
|
32794
|
-
|
|
33154
|
+
await stateCache.write({
|
|
32795
33155
|
...stateData,
|
|
32796
|
-
status: "active",
|
|
32797
33156
|
updated_at: Date.now(),
|
|
32798
33157
|
todos: initialTodos,
|
|
32799
33158
|
last_exit_code: "dirty"
|
|
32800
33159
|
});
|
|
32801
|
-
const replayedMeta = projected.sessionMetaPatch;
|
|
32802
33160
|
const now = Date.now();
|
|
32803
|
-
const pickTitle = isClean ? stateData?.custom_title ?? replayedMeta.title : replayedMeta.title ?? stateData?.custom_title;
|
|
32804
|
-
const pickTags = isClean ? stateData?.tags ?? replayedMeta.tags : replayedMeta.tags ?? stateData?.tags;
|
|
32805
|
-
const pickDescription = isClean ? stateData?.description ?? replayedMeta.description : replayedMeta.description ?? stateData?.description;
|
|
32806
|
-
const pickArchived = isClean ? stateData?.archived ?? replayedMeta.archived : replayedMeta.archived ?? stateData?.archived;
|
|
32807
|
-
const pickLastModel = isClean ? stateData?.model ?? replayedMeta.last_model : replayedMeta.last_model ?? stateData?.model;
|
|
32808
|
-
const pickPlanSlug = isClean ? stateData?.plan_slug ?? replayedMeta.plan_slug : replayedMeta.plan_slug ?? stateData?.plan_slug;
|
|
32809
33161
|
const initialMeta = {
|
|
32810
33162
|
session_id: sessionId,
|
|
32811
|
-
created_at: stateData
|
|
32812
|
-
turn_count:
|
|
32813
|
-
last_updated: stateData
|
|
32814
|
-
...
|
|
32815
|
-
...
|
|
32816
|
-
...
|
|
32817
|
-
...
|
|
32818
|
-
...
|
|
32819
|
-
|
|
33163
|
+
created_at: stateData.created_at ?? now,
|
|
33164
|
+
turn_count: stateData.turn_count ?? 0,
|
|
33165
|
+
last_updated: stateData.updated_at ?? now,
|
|
33166
|
+
...stateData.custom_title !== void 0 ? { title: stateData.custom_title } : {},
|
|
33167
|
+
...stateData.tags !== void 0 ? { tags: [...stateData.tags] } : {},
|
|
33168
|
+
...stateData.description !== void 0 ? { description: stateData.description } : {},
|
|
33169
|
+
...stateData.archived !== void 0 ? { archived: stateData.archived } : {},
|
|
33170
|
+
...stateData.color !== void 0 ? { color: stateData.color } : {},
|
|
33171
|
+
last_model: stateData.model,
|
|
33172
|
+
...stateData.plan_slug !== void 0 ? { plan_slug: stateData.plan_slug } : {},
|
|
32820
33173
|
producer: replayResult.producer,
|
|
32821
33174
|
last_exit_code: "dirty"
|
|
32822
33175
|
};
|
|
@@ -32865,11 +33218,12 @@ var SessionManager = class {
|
|
|
32865
33218
|
subagentStore,
|
|
32866
33219
|
agentTypeRegistry: options.agentTypeRegistry,
|
|
32867
33220
|
sessionDir,
|
|
32868
|
-
workDir: stateData
|
|
33221
|
+
workDir: stateData.workspace_dir ?? process.cwd()
|
|
32869
33222
|
} : {},
|
|
32870
33223
|
...options.logger !== void 0 ? { logger: options.logger } : {}
|
|
32871
33224
|
});
|
|
32872
33225
|
turnManagerRef = soulPlus.getTurnManager();
|
|
33226
|
+
const disposeStateTracking = this.attachStateTracker(sessionId, stateCache, turnManagerRef);
|
|
32873
33227
|
await soulPlus.init();
|
|
32874
33228
|
turnManagerRef.setPermissionMode(effectivePermissionMode);
|
|
32875
33229
|
if (projected.thinkingLevel !== void 0) turnManagerRef.setThinkingLevel(projected.thinkingLevel);
|
|
@@ -32896,7 +33250,20 @@ var SessionManager = class {
|
|
|
32896
33250
|
turnManager: turnManagerRef,
|
|
32897
33251
|
contextState,
|
|
32898
33252
|
sessionJournal,
|
|
32899
|
-
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled)
|
|
33253
|
+
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled),
|
|
33254
|
+
setYoloOverride: async (enabled) => {
|
|
33255
|
+
const permissionMode = enabled ? "bypassPermissions" : "default";
|
|
33256
|
+
await stateCache.update((current) => {
|
|
33257
|
+
const now = Date.now();
|
|
33258
|
+
return {
|
|
33259
|
+
...current ?? createFallbackState(sessionId, now),
|
|
33260
|
+
permission_mode: permissionMode,
|
|
33261
|
+
updated_at: now,
|
|
33262
|
+
last_exit_code: "dirty"
|
|
33263
|
+
};
|
|
33264
|
+
});
|
|
33265
|
+
turnManagerRef.setPermissionMode(permissionMode);
|
|
33266
|
+
}
|
|
32900
33267
|
}),
|
|
32901
33268
|
contextState,
|
|
32902
33269
|
eventBus,
|
|
@@ -32905,6 +33272,7 @@ var SessionManager = class {
|
|
|
32905
33272
|
journalWriter,
|
|
32906
33273
|
runtimeSlot,
|
|
32907
33274
|
lifecycleStateMachine,
|
|
33275
|
+
disposeStateTracking,
|
|
32908
33276
|
todoStore
|
|
32909
33277
|
};
|
|
32910
33278
|
this.sessions.set(sessionId, managed);
|
|
@@ -32926,7 +33294,6 @@ var SessionManager = class {
|
|
|
32926
33294
|
session_id: state.session_id,
|
|
32927
33295
|
created_at: state.created_at,
|
|
32928
33296
|
...state.model !== void 0 ? { model: state.model } : {},
|
|
32929
|
-
...state.status !== void 0 ? { status: state.status } : {},
|
|
32930
33297
|
...state.workspace_dir !== void 0 ? { workspace_dir: state.workspace_dir } : {},
|
|
32931
33298
|
...state.custom_title !== void 0 ? { title: state.custom_title } : {},
|
|
32932
33299
|
...lastActivity !== void 0 ? { last_activity: lastActivity } : {},
|
|
@@ -32970,7 +33337,8 @@ var SessionManager = class {
|
|
|
32970
33337
|
await cache.write({
|
|
32971
33338
|
...existing,
|
|
32972
33339
|
custom_title: trimmed,
|
|
32973
|
-
updated_at: Date.now()
|
|
33340
|
+
updated_at: Date.now(),
|
|
33341
|
+
last_exit_code: "dirty"
|
|
32974
33342
|
});
|
|
32975
33343
|
this.usageAggregator.invalidate(this.paths.wirePath(sessionId));
|
|
32976
33344
|
});
|
|
@@ -32995,7 +33363,8 @@ var SessionManager = class {
|
|
|
32995
33363
|
await cache.write({
|
|
32996
33364
|
...existing,
|
|
32997
33365
|
tags: next,
|
|
32998
|
-
updated_at: Date.now()
|
|
33366
|
+
updated_at: Date.now(),
|
|
33367
|
+
last_exit_code: "dirty"
|
|
32999
33368
|
});
|
|
33000
33369
|
this.usageAggregator.invalidate(this.paths.wirePath(sessionId));
|
|
33001
33370
|
});
|
|
@@ -33079,7 +33448,29 @@ var SessionManager = class {
|
|
|
33079
33448
|
keepUpToLine = turnBeginIndices[totalTurns - nTurnsBack];
|
|
33080
33449
|
newTurnCount = totalTurns - nTurnsBack;
|
|
33081
33450
|
}
|
|
33082
|
-
|
|
33451
|
+
const kept = lines.slice(0, keepUpToLine);
|
|
33452
|
+
await atomicWrite(wirePath, `${kept.join("\n")}\n`);
|
|
33453
|
+
let lastTurnId;
|
|
33454
|
+
let lastTurnTime;
|
|
33455
|
+
for (let i = kept.length - 1; i >= 0; i--) try {
|
|
33456
|
+
const rec = JSON.parse(kept[i] ?? "");
|
|
33457
|
+
if (rec.type === "turn_begin" && rec.turn_id !== void 0) {
|
|
33458
|
+
lastTurnId = rec.turn_id;
|
|
33459
|
+
if (typeof rec.time === "number") lastTurnTime = rec.time;
|
|
33460
|
+
break;
|
|
33461
|
+
}
|
|
33462
|
+
} catch {}
|
|
33463
|
+
await new StateCache(this.paths.statePath(sessionId)).update((current) => {
|
|
33464
|
+
const now = Date.now();
|
|
33465
|
+
return {
|
|
33466
|
+
...current ?? createFallbackState(sessionId, now),
|
|
33467
|
+
turn_count: newTurnCount,
|
|
33468
|
+
last_turn_id: lastTurnId,
|
|
33469
|
+
last_turn_time: lastTurnTime,
|
|
33470
|
+
updated_at: now,
|
|
33471
|
+
last_exit_code: "dirty"
|
|
33472
|
+
};
|
|
33473
|
+
});
|
|
33083
33474
|
this.usageAggregator.invalidate(wirePath);
|
|
33084
33475
|
return { new_turn_count: newTurnCount };
|
|
33085
33476
|
}
|
|
@@ -33091,18 +33482,8 @@ var SessionManager = class {
|
|
|
33091
33482
|
async getSessionStatus(sessionId) {
|
|
33092
33483
|
const live = this.sessions.get(sessionId);
|
|
33093
33484
|
if (live !== void 0) return live.lifecycleStateMachine.state;
|
|
33094
|
-
|
|
33095
|
-
|
|
33096
|
-
const validStatuses = new Set([
|
|
33097
|
-
"idle",
|
|
33098
|
-
"active",
|
|
33099
|
-
"completing",
|
|
33100
|
-
"compacting",
|
|
33101
|
-
"destroying",
|
|
33102
|
-
"closed"
|
|
33103
|
-
]);
|
|
33104
|
-
if (state.status !== void 0 && validStatuses.has(state.status)) return state.status;
|
|
33105
|
-
return "idle";
|
|
33485
|
+
if (await new StateCache(this.paths.statePath(sessionId)).read() === null) throw new Error(`getSessionStatus: session "${sessionId}" not found`);
|
|
33486
|
+
return "closed";
|
|
33106
33487
|
}
|
|
33107
33488
|
/**
|
|
33108
33489
|
* Return aggregated token usage for a session.
|
|
@@ -33141,33 +33522,43 @@ var SessionManager = class {
|
|
|
33141
33522
|
await managed.todoStore?.flushPending();
|
|
33142
33523
|
const existing = await managed.stateCache.read();
|
|
33143
33524
|
const now = Date.now();
|
|
33144
|
-
const
|
|
33525
|
+
const turnManager = managed.soulPlus.getTurnManager();
|
|
33526
|
+
const currentPlanMode = turnManager.getPlanMode();
|
|
33527
|
+
const currentPermissionMode = turnManager.getPermissionMode();
|
|
33528
|
+
const currentThinkingLevel = turnManager.getThinkingLevel();
|
|
33145
33529
|
const currentTodos = managed.todoStore?.getTodos() ?? [];
|
|
33146
33530
|
const meta = sessionMetaService?.get();
|
|
33147
33531
|
const stateToFlush = existing ? {
|
|
33148
33532
|
...existing,
|
|
33149
|
-
status: "closed",
|
|
33150
33533
|
updated_at: now,
|
|
33534
|
+
model: managed.contextState.model,
|
|
33535
|
+
permission_mode: currentPermissionMode,
|
|
33151
33536
|
...meta?.title !== void 0 ? { custom_title: meta.title } : {},
|
|
33152
33537
|
...meta?.tags !== void 0 ? { tags: [...meta.tags] } : {},
|
|
33153
33538
|
...meta?.description !== void 0 ? { description: meta.description } : {},
|
|
33154
33539
|
...meta?.archived !== void 0 ? { archived: meta.archived } : {},
|
|
33155
|
-
...meta?.
|
|
33540
|
+
...meta?.color !== void 0 ? { color: meta.color } : {},
|
|
33541
|
+
turn_count: meta?.turn_count ?? existing.turn_count ?? 0,
|
|
33156
33542
|
...meta?.plan_slug !== void 0 ? { plan_slug: meta.plan_slug } : {},
|
|
33543
|
+
...currentThinkingLevel !== void 0 ? { thinking_level: currentThinkingLevel } : {},
|
|
33157
33544
|
...currentPlanMode ? { plan_mode: true } : { plan_mode: false },
|
|
33158
33545
|
todos: cloneTodos(currentTodos),
|
|
33159
33546
|
last_exit_code: "clean"
|
|
33160
33547
|
} : {
|
|
33161
33548
|
session_id: sessionId,
|
|
33162
|
-
status: "closed",
|
|
33163
33549
|
created_at: now,
|
|
33164
33550
|
updated_at: now,
|
|
33551
|
+
model: managed.contextState.model,
|
|
33552
|
+
permission_mode: currentPermissionMode,
|
|
33165
33553
|
plan_mode: currentPlanMode,
|
|
33554
|
+
turn_count: meta?.turn_count ?? 0,
|
|
33555
|
+
...currentThinkingLevel !== void 0 ? { thinking_level: currentThinkingLevel } : {},
|
|
33166
33556
|
todos: cloneTodos(currentTodos),
|
|
33167
33557
|
last_exit_code: "clean"
|
|
33168
33558
|
};
|
|
33169
33559
|
await managed.stateCache.write(stateToFlush);
|
|
33170
33560
|
sessionMetaService?.dispose();
|
|
33561
|
+
managed.disposeStateTracking?.();
|
|
33171
33562
|
await managed.journalWriter.close();
|
|
33172
33563
|
this.sessions.delete(sessionId);
|
|
33173
33564
|
});
|
|
@@ -94290,7 +94681,7 @@ function shellQuote(path) {
|
|
|
94290
94681
|
}
|
|
94291
94682
|
//#endregion
|
|
94292
94683
|
//#region src/tui/components/messages/assistant-message.ts
|
|
94293
|
-
const BULLET$1 = "
|
|
94684
|
+
const BULLET$1 = "⏺ ";
|
|
94294
94685
|
const INDENT$2 = " ";
|
|
94295
94686
|
var AssistantMessageComponent = class {
|
|
94296
94687
|
contentContainer;
|
|
@@ -94363,7 +94754,7 @@ var ImageThumbnail = class extends Container {
|
|
|
94363
94754
|
};
|
|
94364
94755
|
//#endregion
|
|
94365
94756
|
//#region src/tui/components/messages/thinking.ts
|
|
94366
|
-
const BULLET = "
|
|
94757
|
+
const BULLET = "⏺ ";
|
|
94367
94758
|
const INDENT$1 = " ";
|
|
94368
94759
|
const PREVIEW_LINES$1 = 3;
|
|
94369
94760
|
var ThinkingComponent = class {
|
|
@@ -94563,6 +94954,19 @@ function extractApprovedPlan(output) {
|
|
|
94563
94954
|
if (markerIndex < 0) return "";
|
|
94564
94955
|
return output.slice(markerIndex + 17).trim();
|
|
94565
94956
|
}
|
|
94957
|
+
function compactPreview(value) {
|
|
94958
|
+
const oneLine = value.replaceAll(/\s+/g, " ").trim();
|
|
94959
|
+
if (oneLine.length <= 160) return oneLine;
|
|
94960
|
+
return oneLine.slice(0, 157) + "...";
|
|
94961
|
+
}
|
|
94962
|
+
function parseArgsPreview(value) {
|
|
94963
|
+
if (value.trim().length === 0) return {};
|
|
94964
|
+
try {
|
|
94965
|
+
const parsed = JSON.parse(value);
|
|
94966
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
|
|
94967
|
+
} catch {}
|
|
94968
|
+
return { arguments: value };
|
|
94969
|
+
}
|
|
94566
94970
|
function extractKeyArgument(toolName, args) {
|
|
94567
94971
|
const candidates = {
|
|
94568
94972
|
Shell: ["command"],
|
|
@@ -94650,6 +95054,12 @@ var ToolCallComponent = class extends Container {
|
|
|
94650
95054
|
this.headerText.setText(this.buildHeader());
|
|
94651
95055
|
this.rebuildContent();
|
|
94652
95056
|
}
|
|
95057
|
+
updateToolCall(toolCall) {
|
|
95058
|
+
this.toolCall = toolCall;
|
|
95059
|
+
this.headerText.setText(this.buildHeader());
|
|
95060
|
+
this.rebuildBody();
|
|
95061
|
+
this.ui?.requestRender();
|
|
95062
|
+
}
|
|
94653
95063
|
applySubagentReplay(subagent) {
|
|
94654
95064
|
if (subagent === void 0) return;
|
|
94655
95065
|
this.subagentAgentId = subagent.id;
|
|
@@ -94688,9 +95098,23 @@ var ToolCallComponent = class extends Container {
|
|
|
94688
95098
|
this.ui?.requestRender();
|
|
94689
95099
|
}
|
|
94690
95100
|
appendSubToolCall(call) {
|
|
95101
|
+
const existing = this.ongoingSubCalls.get(call.id);
|
|
94691
95102
|
this.ongoingSubCalls.set(call.id, {
|
|
94692
95103
|
name: call.name,
|
|
94693
|
-
args: call.args
|
|
95104
|
+
args: call.args,
|
|
95105
|
+
...existing?.streamingArguments !== void 0 ? { streamingArguments: existing.streamingArguments } : {}
|
|
95106
|
+
});
|
|
95107
|
+
this.rebuildContent();
|
|
95108
|
+
this.ui?.requestRender();
|
|
95109
|
+
}
|
|
95110
|
+
appendSubToolCallDelta(delta) {
|
|
95111
|
+
const existing = this.ongoingSubCalls.get(delta.id);
|
|
95112
|
+
const nextArgsText = `${existing?.streamingArguments ?? ""}${delta.argumentsPart ?? ""}`;
|
|
95113
|
+
const parsed = parseArgsPreview(nextArgsText);
|
|
95114
|
+
this.ongoingSubCalls.set(delta.id, {
|
|
95115
|
+
name: delta.name ?? existing?.name ?? "Tool",
|
|
95116
|
+
args: parsed,
|
|
95117
|
+
streamingArguments: nextArgsText
|
|
94694
95118
|
});
|
|
94695
95119
|
this.rebuildContent();
|
|
94696
95120
|
this.ui?.requestRender();
|
|
@@ -94717,8 +95141,8 @@ var ToolCallComponent = class extends Container {
|
|
|
94717
95141
|
const isFinished = result !== void 0;
|
|
94718
95142
|
const isError = result?.is_error ?? false;
|
|
94719
95143
|
let bullet;
|
|
94720
|
-
if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)("
|
|
94721
|
-
else bullet = this.blinkOn ? chalk.white("
|
|
95144
|
+
if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)("⏺ ");
|
|
95145
|
+
else bullet = this.blinkOn ? chalk.white("⏺ ") : " ";
|
|
94722
95146
|
if (toolCall.name === "ExitPlanMode") return chalk.hex(colors.primary).bold("Current plan");
|
|
94723
95147
|
if (toolCall.name === "AskUserQuestion") {
|
|
94724
95148
|
const label = isFinished ? isError ? "Could not collect your input" : "Collected your answers" : "Waiting for your input";
|
|
@@ -94736,6 +95160,13 @@ var ToolCallComponent = class extends Container {
|
|
|
94736
95160
|
this.buildContent();
|
|
94737
95161
|
this.buildSubagentBlock();
|
|
94738
95162
|
}
|
|
95163
|
+
rebuildBody() {
|
|
95164
|
+
while (this.children.length > 2) this.children.pop();
|
|
95165
|
+
this.buildCallPreview();
|
|
95166
|
+
this.callPreviewEndIndex = this.children.length;
|
|
95167
|
+
this.buildContent();
|
|
95168
|
+
this.buildSubagentBlock();
|
|
95169
|
+
}
|
|
94739
95170
|
buildSubagentBlock() {
|
|
94740
95171
|
if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0) return;
|
|
94741
95172
|
const dim = chalk.dim;
|
|
@@ -94757,6 +95188,7 @@ var ToolCallComponent = class extends Container {
|
|
|
94757
95188
|
const nameCol = chalk.hex(this.colors.primary)(call.name);
|
|
94758
95189
|
const argCol = keyArg ? dim(` (${keyArg})`) : "";
|
|
94759
95190
|
this.addChild(new Text(` ${dim("…")} Using ${nameCol}${argCol}`, 0, 0));
|
|
95191
|
+
if (call.streamingArguments !== void 0 && call.streamingArguments.length > 0) this.addChild(new Text(` ${dim(compactPreview(call.streamingArguments))}`, 0, 0));
|
|
94760
95192
|
}
|
|
94761
95193
|
if (this.subagentText.length > 0) {
|
|
94762
95194
|
const tailLines = this.subagentText.split("\n").slice(-3);
|
|
@@ -94773,15 +95205,19 @@ var ToolCallComponent = class extends Container {
|
|
|
94773
95205
|
this.buildPlanPreview();
|
|
94774
95206
|
return;
|
|
94775
95207
|
}
|
|
95208
|
+
if (this.toolCall.streamingArguments !== void 0) {
|
|
95209
|
+
this.addChild(new Text(chalk.dim(compactPreview(this.toolCall.streamingArguments)), 2, 0));
|
|
95210
|
+
return;
|
|
95211
|
+
}
|
|
94776
95212
|
if (name === "Write" || name === "WriteFile") {
|
|
94777
95213
|
const content = str(this.toolCall.args["content"]);
|
|
94778
95214
|
if (content.length === 0) return;
|
|
94779
95215
|
const allLines = highlightLines(content, langFromPath(str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"])));
|
|
94780
95216
|
const shown = allLines.slice(0, CALL_PREVIEW_LINES);
|
|
94781
95217
|
const remaining = allLines.length - shown.length;
|
|
94782
|
-
for (
|
|
95218
|
+
for (const [i, line] of shown.entries()) {
|
|
94783
95219
|
const lineNum = chalk.dim(String(i + 1).padStart(4) + " ");
|
|
94784
|
-
this.addChild(new Text(lineNum +
|
|
95220
|
+
this.addChild(new Text(lineNum + line, 2, 0));
|
|
94785
95221
|
}
|
|
94786
95222
|
if (remaining > 0) this.addChild(new Text(chalk.dim(`... (${String(remaining)} more lines, ${String(allLines.length)} total)`), 2, 0));
|
|
94787
95223
|
} else if (name === "Edit" || name === "EditFile") {
|
|
@@ -94815,15 +95251,14 @@ var ToolCallComponent = class extends Container {
|
|
|
94815
95251
|
return;
|
|
94816
95252
|
}
|
|
94817
95253
|
if (this.toolCall.name === "SetTodoList" && !result.is_error) return;
|
|
94818
|
-
if (this.toolCall.name === "AskUserQuestion" && !result.is_error)
|
|
94819
|
-
|
|
94820
|
-
}
|
|
95254
|
+
if (this.toolCall.name === "AskUserQuestion" && !result.is_error && this.renderAskUserQuestionResult(result.output)) return;
|
|
95255
|
+
const tint = result.is_error ? chalk.hex(this.colors.error) : chalk.dim;
|
|
94821
95256
|
const lines = result.output.split("\n");
|
|
94822
|
-
if (this.expanded) this.addChild(new Text(
|
|
95257
|
+
if (this.expanded) this.addChild(new Text(tint(result.output), 2, 0));
|
|
94823
95258
|
else {
|
|
94824
95259
|
const shown = lines.slice(0, PREVIEW_LINES);
|
|
94825
95260
|
const remaining = lines.length - shown.length;
|
|
94826
|
-
this.addChild(new Text(
|
|
95261
|
+
this.addChild(new Text(tint(shown.join("\n")), 2, 0));
|
|
94827
95262
|
if (remaining > 0) this.addChild(new Text(chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`), 2, 0));
|
|
94828
95263
|
}
|
|
94829
95264
|
}
|
|
@@ -94889,12 +95324,14 @@ var WelcomeComponent = class {
|
|
|
94889
95324
|
const gap = " ";
|
|
94890
95325
|
const textWidth = Math.max(4, innerWidth - logoWidth - 2);
|
|
94891
95326
|
const rightRow0 = truncateToWidth(chalk.bold.hex(this.colors.primary)("Welcome to Kimi Code!"), textWidth, "…");
|
|
94892
|
-
const
|
|
95327
|
+
const isLoggedOut = !this.state.model;
|
|
95328
|
+
const rightRow1 = truncateToWidth(chalk.dim(isLoggedOut ? "Run /login to sign in." : "Send /help for help information."), textWidth, "…");
|
|
94893
95329
|
const headerLines = [primary(logo[0].padEnd(logoWidth)) + gap + rightRow0, primary(logo[1].padEnd(logoWidth)) + gap + rightRow1];
|
|
95330
|
+
const modelValue = isLoggedOut ? chalk.yellow("(not signed in — run /login)") : this.state.model;
|
|
94894
95331
|
const infoLines = [
|
|
94895
95332
|
chalk.dim.bold("Directory: ") + this.state.workDir,
|
|
94896
95333
|
chalk.dim.bold("Session: ") + this.state.sessionId,
|
|
94897
|
-
chalk.dim.bold("Model: ") +
|
|
95334
|
+
chalk.dim.bold("Model: ") + modelValue,
|
|
94898
95335
|
chalk.dim.bold("Version: ") + this.state.version
|
|
94899
95336
|
];
|
|
94900
95337
|
const contentLines = [
|
|
@@ -95060,8 +95497,8 @@ function createTranscriptComponent(state, entry) {
|
|
|
95060
95497
|
if (state.toolOutputExpanded) tc.setExpanded(true);
|
|
95061
95498
|
return tc;
|
|
95062
95499
|
}
|
|
95063
|
-
return createStatusEntry(entry.content, entry.color);
|
|
95064
|
-
case "status": return createStatusEntry(entry.content, entry.color);
|
|
95500
|
+
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, entry.color);
|
|
95501
|
+
case "status": return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, entry.color);
|
|
95065
95502
|
default: return null;
|
|
95066
95503
|
}
|
|
95067
95504
|
}
|
|
@@ -95082,6 +95519,13 @@ function createStatusEntry(content, color) {
|
|
|
95082
95519
|
container.addChild(new Text(` ${styled}`, 0, 0));
|
|
95083
95520
|
return container;
|
|
95084
95521
|
}
|
|
95522
|
+
function createNoticeEntry(title, detail, colors) {
|
|
95523
|
+
const container = new Container();
|
|
95524
|
+
container.addChild(new Spacer(1));
|
|
95525
|
+
container.addChild(new Text(` ${chalk.white(title)}`, 0, 0));
|
|
95526
|
+
if (detail !== void 0 && detail.length > 0) container.addChild(new Text(` ${chalk.hex(colors.textDim)(detail)}`, 0, 0));
|
|
95527
|
+
return container;
|
|
95528
|
+
}
|
|
95085
95529
|
/**
|
|
95086
95530
|
* Push a new entry onto the transcript and render the matching
|
|
95087
95531
|
* component. Equivalent to the old `KimiTUI.addTranscriptEntry`.
|
|
@@ -95113,6 +95557,16 @@ function emitStatus(state, message, color) {
|
|
|
95113
95557
|
...color !== void 0 ? { color } : {}
|
|
95114
95558
|
});
|
|
95115
95559
|
}
|
|
95560
|
+
function emitNotice(state, title, detail) {
|
|
95561
|
+
appendTranscriptEntry(state, {
|
|
95562
|
+
id: nextTranscriptId(),
|
|
95563
|
+
kind: "status",
|
|
95564
|
+
turnId: state.currentTurnId,
|
|
95565
|
+
renderMode: "notice",
|
|
95566
|
+
content: title,
|
|
95567
|
+
...detail !== void 0 ? { detail } : {}
|
|
95568
|
+
});
|
|
95569
|
+
}
|
|
95116
95570
|
/** Red. */
|
|
95117
95571
|
function emitError(state, message) {
|
|
95118
95572
|
emitStatus(state, message, state.colors.error);
|
|
@@ -95125,10 +95579,6 @@ function emitSuccess(state, message) {
|
|
|
95125
95579
|
function emitMuted(state, message) {
|
|
95126
95580
|
emitStatus(state, message, state.colors.textDim);
|
|
95127
95581
|
}
|
|
95128
|
-
/** Primary (brand) color. */
|
|
95129
|
-
function emitPrimary(state, message) {
|
|
95130
|
-
emitStatus(state, message, state.colors.primary);
|
|
95131
|
-
}
|
|
95132
95582
|
//#endregion
|
|
95133
95583
|
//#region src/tui/input/image-placeholder.ts
|
|
95134
95584
|
const PLACEHOLDER_REGEX = /\[image #(\d+) \((\d+)×(\d+)\)\]/g;
|
|
@@ -95200,6 +95650,15 @@ function handleSubagentSourceEvent(ectx, payload) {
|
|
|
95200
95650
|
});
|
|
95201
95651
|
return;
|
|
95202
95652
|
}
|
|
95653
|
+
if (payload.method === "tool.call.delta") {
|
|
95654
|
+
const d = payload.data;
|
|
95655
|
+
if (typeof d.tool_call_id === "string") tc.appendSubToolCallDelta({
|
|
95656
|
+
id: `${payload.source_id}:${d.tool_call_id}`,
|
|
95657
|
+
...typeof d.name === "string" ? { name: d.name } : {},
|
|
95658
|
+
argumentsPart: typeof d.arguments_part === "string" ? d.arguments_part : null
|
|
95659
|
+
});
|
|
95660
|
+
return;
|
|
95661
|
+
}
|
|
95203
95662
|
if (payload.method === "tool.result") {
|
|
95204
95663
|
const d = payload.data;
|
|
95205
95664
|
if (typeof d.tool_call_id === "string") tc.finishSubToolCall({
|
|
@@ -95494,6 +95953,10 @@ function ok$1(message) {
|
|
|
95494
95953
|
};
|
|
95495
95954
|
return { type: "ok" };
|
|
95496
95955
|
}
|
|
95956
|
+
function showNotice(ctx, title, detail) {
|
|
95957
|
+
ctx.showNotice(title, detail);
|
|
95958
|
+
return ok$1();
|
|
95959
|
+
}
|
|
95497
95960
|
const shellCommands = [
|
|
95498
95961
|
{
|
|
95499
95962
|
name: "exit",
|
|
@@ -95575,7 +96038,8 @@ const shellCommands = [
|
|
|
95575
96038
|
else enabled = !ctx.appState.yolo;
|
|
95576
96039
|
await ctx.client.setYolo(ctx.appState.sessionId, enabled);
|
|
95577
96040
|
ctx.setAppState({ yolo: enabled });
|
|
95578
|
-
return
|
|
96041
|
+
if (enabled) return showNotice(ctx, "YOLO mode: ON", "All actions will be approved automatically. Use with caution.");
|
|
96042
|
+
return showNotice(ctx, "YOLO mode: OFF");
|
|
95579
96043
|
}
|
|
95580
96044
|
},
|
|
95581
96045
|
{
|
|
@@ -95600,8 +96064,9 @@ const shellCommands = [
|
|
|
95600
96064
|
else enabled = !ctx.appState.planMode;
|
|
95601
96065
|
const res = await ctx.client.setPlanMode(ctx.appState.sessionId, enabled);
|
|
95602
96066
|
ctx.setAppState({ planMode: enabled });
|
|
95603
|
-
if (enabled && res.plan_path !== void 0) return
|
|
95604
|
-
|
|
96067
|
+
if (enabled && res.plan_path !== void 0) return showNotice(ctx, "Plan mode: ON", `Plan will be created here: ${res.plan_path}`);
|
|
96068
|
+
if (enabled) return showNotice(ctx, "Plan mode: ON");
|
|
96069
|
+
return showNotice(ctx, "Plan mode: OFF");
|
|
95605
96070
|
}
|
|
95606
96071
|
},
|
|
95607
96072
|
{
|
|
@@ -95739,7 +96204,7 @@ var SlashCommandRegistry = class {
|
|
|
95739
96204
|
* 再注册当前 session 下的可用 skills。
|
|
95740
96205
|
*/
|
|
95741
96206
|
setSkillCommands(defs) {
|
|
95742
|
-
for (const [name, def] of
|
|
96207
|
+
for (const [name, def] of this.commands) if (name.startsWith("skill:")) {
|
|
95743
96208
|
this.commands.delete(name);
|
|
95744
96209
|
this.lookup.delete(name);
|
|
95745
96210
|
for (const alias of def.aliases) this.lookup.delete(alias);
|
|
@@ -96540,6 +97005,7 @@ function createTUIState(options) {
|
|
|
96540
97005
|
assistantStreamActive: false,
|
|
96541
97006
|
thinkingDraft: "",
|
|
96542
97007
|
activeToolCalls: /* @__PURE__ */ new Map(),
|
|
97008
|
+
streamingToolCallArguments: /* @__PURE__ */ new Map(),
|
|
96543
97009
|
toastTimers: /* @__PURE__ */ new Map(),
|
|
96544
97010
|
pendingExit: null,
|
|
96545
97011
|
queuedMessages: [],
|
|
@@ -96701,6 +97167,7 @@ function sendMessageInternal(state, addEntry, input, options) {
|
|
|
96701
97167
|
state.assistantStreamActive = false;
|
|
96702
97168
|
state.thinkingDraft = "";
|
|
96703
97169
|
state.activeToolCalls.clear();
|
|
97170
|
+
state.streamingToolCallArguments.clear();
|
|
96704
97171
|
state.livePane = {
|
|
96705
97172
|
...state.livePane,
|
|
96706
97173
|
mode: "waiting",
|
|
@@ -96829,7 +97296,9 @@ async function openExternalEditor(state) {
|
|
|
96829
97296
|
state.externalEditorRunning = true;
|
|
96830
97297
|
const seed = state.editor.getExpandedText?.() ?? state.editor.getText();
|
|
96831
97298
|
state.ui.stop();
|
|
96832
|
-
await new Promise((resolve) =>
|
|
97299
|
+
await new Promise((resolve) => {
|
|
97300
|
+
setImmediate(resolve);
|
|
97301
|
+
});
|
|
96833
97302
|
try {
|
|
96834
97303
|
const result = await editInExternalEditor(seed, cmd);
|
|
96835
97304
|
if (result !== void 0) state.editor.setText(result.replaceAll("\r\n", "\n").replace(/\n$/, ""));
|
|
@@ -96853,11 +97322,11 @@ async function togglePlanMode(state, hooks) {
|
|
|
96853
97322
|
return;
|
|
96854
97323
|
}
|
|
96855
97324
|
setState(state, { planMode: enabled }, hooks);
|
|
96856
|
-
if (enabled
|
|
96857
|
-
|
|
97325
|
+
if (enabled) {
|
|
97326
|
+
emitNotice(state, "Plan mode: ON", planPath !== void 0 ? `Plan will be created here: ${planPath}` : void 0);
|
|
96858
97327
|
return;
|
|
96859
97328
|
}
|
|
96860
|
-
|
|
97329
|
+
emitNotice(state, "Plan mode: OFF");
|
|
96861
97330
|
}
|
|
96862
97331
|
async function performReload(state, action, hooks) {
|
|
96863
97332
|
if (state.appState.isStreaming) {
|
|
@@ -97319,6 +97788,7 @@ function releaseSessionSideEffects(state) {
|
|
|
97319
97788
|
state.queuedMessages = [];
|
|
97320
97789
|
state.queueIdCounter = 0;
|
|
97321
97790
|
state.activeToolCalls.clear();
|
|
97791
|
+
state.streamingToolCallArguments?.clear();
|
|
97322
97792
|
state.currentTurnId = void 0;
|
|
97323
97793
|
state.assistantDraft = "";
|
|
97324
97794
|
state.assistantStreamActive = false;
|
|
@@ -97370,8 +97840,8 @@ var CompactionComponent = class extends Container {
|
|
|
97370
97840
|
this.stopBlink();
|
|
97371
97841
|
}
|
|
97372
97842
|
buildHeader() {
|
|
97373
|
-
if (this.done) return `${chalk.hex(this.colors.success)("
|
|
97374
|
-
return `${this.blinkOn ? chalk.white("
|
|
97843
|
+
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)`) : ""}`;
|
|
97844
|
+
return `${this.blinkOn ? chalk.white("⏺ ") : " "}${chalk.hex(this.colors.primary).bold("Compacting context...")}`;
|
|
97375
97845
|
}
|
|
97376
97846
|
startBlink() {
|
|
97377
97847
|
this.blinkTimer = setInterval(() => {
|
|
@@ -97510,7 +97980,8 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
97510
97980
|
client: ctx.client,
|
|
97511
97981
|
appState: ctx.appState,
|
|
97512
97982
|
setAppState: ctx.setAppState,
|
|
97513
|
-
showStatus: ctx.showStatus
|
|
97983
|
+
showStatus: ctx.showStatus,
|
|
97984
|
+
showNotice: ctx.showNotice
|
|
97514
97985
|
};
|
|
97515
97986
|
let result;
|
|
97516
97987
|
try {
|
|
@@ -97577,6 +98048,7 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
97577
98048
|
//#endregion
|
|
97578
98049
|
//#region src/tui/handlers/turn-lifecycle.ts
|
|
97579
98050
|
function handleTurnBegin(ectx, _data) {
|
|
98051
|
+
ectx.state.streamingToolCallArguments.clear();
|
|
97580
98052
|
ectx.patchLivePane({
|
|
97581
98053
|
mode: "waiting",
|
|
97582
98054
|
thinkingText: "",
|
|
@@ -97594,6 +98066,7 @@ function handleTurnBegin(ectx, _data) {
|
|
|
97594
98066
|
function handleTurnEnd(ectx, _data, sendQueued) {
|
|
97595
98067
|
const todos = ectx.state.todoPanel.getTodos();
|
|
97596
98068
|
if (todos.length > 0 && todos.every((t) => t.status === "done")) ectx.setTodoList([]);
|
|
98069
|
+
ectx.state.streamingToolCallArguments.clear();
|
|
97597
98070
|
finalizeTurn(ectx, sendQueued);
|
|
97598
98071
|
}
|
|
97599
98072
|
function handleStepBegin(ectx) {
|
|
@@ -97645,24 +98118,18 @@ function handleContentDelta(ectx, data) {
|
|
|
97645
98118
|
streamingPhase: "composing",
|
|
97646
98119
|
streamingStartTime: Date.now()
|
|
97647
98120
|
});
|
|
97648
|
-
return;
|
|
97649
|
-
}
|
|
97650
|
-
if (data.type === "tool_call_part") {
|
|
97651
|
-
if (ectx.state.thinkingDraft.length > 0) flushThinkingToTranscript(ectx, "idle");
|
|
97652
|
-
ectx.patchLivePane({
|
|
97653
|
-
mode: "idle",
|
|
97654
|
-
pendingToolCall: null,
|
|
97655
|
-
pendingApproval: null,
|
|
97656
|
-
pendingQuestion: null
|
|
97657
|
-
});
|
|
97658
|
-
ectx.setAppState({
|
|
97659
|
-
streamingPhase: "composing",
|
|
97660
|
-
streamingStartTime: Date.now()
|
|
97661
|
-
});
|
|
97662
98121
|
}
|
|
97663
98122
|
}
|
|
97664
98123
|
//#endregion
|
|
97665
98124
|
//#region src/tui/handlers/tool.ts
|
|
98125
|
+
function parseStreamingArgs(argumentsText) {
|
|
98126
|
+
if (argumentsText.trim().length === 0) return {};
|
|
98127
|
+
try {
|
|
98128
|
+
const parsed = JSON.parse(argumentsText);
|
|
98129
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
|
|
98130
|
+
} catch {}
|
|
98131
|
+
return { arguments: argumentsText };
|
|
98132
|
+
}
|
|
97666
98133
|
function isTodoItemShape(value) {
|
|
97667
98134
|
if (typeof value !== "object" || value === null) return false;
|
|
97668
98135
|
const rec = value;
|
|
@@ -97676,15 +98143,53 @@ function handleToolCall(ectx, data) {
|
|
|
97676
98143
|
args: data.args,
|
|
97677
98144
|
description: data.description
|
|
97678
98145
|
};
|
|
98146
|
+
const existing = ectx.state.activeToolCalls.get(data.id);
|
|
97679
98147
|
ectx.state.activeToolCalls.set(data.id, toolCall);
|
|
97680
|
-
|
|
97681
|
-
ectx.
|
|
98148
|
+
ectx.state.streamingToolCallArguments.delete(data.id);
|
|
98149
|
+
const existingComponent = ectx.state.pendingToolComponents.get(data.id);
|
|
98150
|
+
if (existingComponent !== void 0) existingComponent.updateToolCall(toolCall);
|
|
98151
|
+
else if (existing === void 0) {
|
|
98152
|
+
flushTurnBuffers(ectx, "tool");
|
|
98153
|
+
ectx.onToolCallStart(toolCall);
|
|
98154
|
+
}
|
|
98155
|
+
ectx.patchLivePane({
|
|
98156
|
+
mode: "tool",
|
|
98157
|
+
pendingToolCall: toolCall,
|
|
98158
|
+
pendingApproval: null,
|
|
98159
|
+
pendingQuestion: null
|
|
98160
|
+
});
|
|
98161
|
+
}
|
|
98162
|
+
function handleToolCallDelta(ectx, data) {
|
|
98163
|
+
if (typeof data.tool_call_id !== "string" || data.tool_call_id.length === 0) return;
|
|
98164
|
+
const id = data.tool_call_id;
|
|
98165
|
+
const existing = ectx.state.streamingToolCallArguments.get(id);
|
|
98166
|
+
const argumentsText = `${existing?.argumentsText ?? ""}${data.arguments_part ?? ""}`;
|
|
98167
|
+
const name = data.name ?? existing?.name ?? ectx.state.activeToolCalls.get(id)?.name ?? "Tool";
|
|
98168
|
+
ectx.state.streamingToolCallArguments.set(id, {
|
|
98169
|
+
name,
|
|
98170
|
+
argumentsText
|
|
98171
|
+
});
|
|
98172
|
+
const toolCall = {
|
|
98173
|
+
id,
|
|
98174
|
+
name,
|
|
98175
|
+
args: parseStreamingArgs(argumentsText),
|
|
98176
|
+
streamingArguments: argumentsText
|
|
98177
|
+
};
|
|
98178
|
+
ectx.state.activeToolCalls.set(id, toolCall);
|
|
98179
|
+
if (ectx.state.thinkingDraft.length > 0) flushTurnBuffers(ectx, "tool");
|
|
98180
|
+
const existingComponent = ectx.state.pendingToolComponents.get(id);
|
|
98181
|
+
if (existingComponent !== void 0) existingComponent.updateToolCall(toolCall);
|
|
98182
|
+
else ectx.onToolCallStart(toolCall);
|
|
97682
98183
|
ectx.patchLivePane({
|
|
97683
98184
|
mode: "tool",
|
|
97684
98185
|
pendingToolCall: toolCall,
|
|
97685
98186
|
pendingApproval: null,
|
|
97686
98187
|
pendingQuestion: null
|
|
97687
98188
|
});
|
|
98189
|
+
ectx.setAppState({
|
|
98190
|
+
streamingPhase: "composing",
|
|
98191
|
+
streamingStartTime: Date.now()
|
|
98192
|
+
});
|
|
97688
98193
|
}
|
|
97689
98194
|
function handleToolResult(ectx, data) {
|
|
97690
98195
|
const matchedCall = ectx.state.activeToolCalls.get(data.tool_call_id);
|
|
@@ -97698,7 +98203,7 @@ function handleToolResult(ectx, data) {
|
|
|
97698
98203
|
if (matchedCall.name === "SetTodoList" && !data.is_error) {
|
|
97699
98204
|
const rawTodos = matchedCall.args.todos;
|
|
97700
98205
|
if (Array.isArray(rawTodos)) {
|
|
97701
|
-
const sanitized = rawTodos.filter(isTodoItemShape).map((t) => ({
|
|
98206
|
+
const sanitized = rawTodos.filter((todo) => isTodoItemShape(todo)).map((t) => ({
|
|
97702
98207
|
title: t.title,
|
|
97703
98208
|
status: t.status
|
|
97704
98209
|
}));
|
|
@@ -97707,6 +98212,7 @@ function handleToolResult(ectx, data) {
|
|
|
97707
98212
|
}
|
|
97708
98213
|
}
|
|
97709
98214
|
ectx.state.activeToolCalls.delete(data.tool_call_id);
|
|
98215
|
+
ectx.state.streamingToolCallArguments.delete(data.tool_call_id);
|
|
97710
98216
|
ectx.patchLivePane({
|
|
97711
98217
|
mode: "waiting",
|
|
97712
98218
|
pendingToolCall: null
|
|
@@ -97823,6 +98329,9 @@ function dispatchEvent(event, ectx, sendQueued) {
|
|
|
97823
98329
|
case "tool.call":
|
|
97824
98330
|
handleToolCall(ectx, event.data);
|
|
97825
98331
|
break;
|
|
98332
|
+
case "tool.call.delta":
|
|
98333
|
+
handleToolCallDelta(ectx, event.data);
|
|
98334
|
+
break;
|
|
97826
98335
|
case "tool.result":
|
|
97827
98336
|
handleToolResult(ectx, event.data);
|
|
97828
98337
|
break;
|
|
@@ -99092,7 +99601,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99092
99601
|
focused = false;
|
|
99093
99602
|
selectedIndex = 0;
|
|
99094
99603
|
feedbackMode = false;
|
|
99095
|
-
|
|
99604
|
+
feedbackInput = new Input();
|
|
99096
99605
|
expanded = false;
|
|
99097
99606
|
onResponse;
|
|
99098
99607
|
request;
|
|
@@ -99100,6 +99609,13 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99100
99609
|
super();
|
|
99101
99610
|
this.request = request;
|
|
99102
99611
|
this.onResponse = onResponse;
|
|
99612
|
+
this.feedbackInput.onSubmit = (value) => {
|
|
99613
|
+
this.submit(this.selectedIndex, value);
|
|
99614
|
+
};
|
|
99615
|
+
this.feedbackInput.onEscape = () => {
|
|
99616
|
+
this.feedbackMode = false;
|
|
99617
|
+
this.feedbackInput.setValue("");
|
|
99618
|
+
};
|
|
99103
99619
|
}
|
|
99104
99620
|
submit(index, feedback = "") {
|
|
99105
99621
|
const option = this.choiceAt(index);
|
|
@@ -99120,21 +99636,16 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99120
99636
|
}
|
|
99121
99637
|
onToggleToolExpand;
|
|
99122
99638
|
handleInput(data) {
|
|
99639
|
+
if (matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrl("d"))) {
|
|
99640
|
+
this.onResponse({ response: "rejected" });
|
|
99641
|
+
return;
|
|
99642
|
+
}
|
|
99123
99643
|
if (matchesKey(data, Key.ctrl("o"))) {
|
|
99124
99644
|
this.expanded = !this.expanded;
|
|
99125
99645
|
this.onToggleToolExpand?.();
|
|
99126
99646
|
return;
|
|
99127
99647
|
}
|
|
99128
99648
|
if (this.feedbackMode) {
|
|
99129
|
-
if (matchesKey(data, Key.enter)) {
|
|
99130
|
-
this.submit(this.selectedIndex, this.feedbackText);
|
|
99131
|
-
return;
|
|
99132
|
-
}
|
|
99133
|
-
if (matchesKey(data, Key.escape)) {
|
|
99134
|
-
this.feedbackMode = false;
|
|
99135
|
-
this.feedbackText = "";
|
|
99136
|
-
return;
|
|
99137
|
-
}
|
|
99138
99649
|
if (matchesKey(data, Key.up)) {
|
|
99139
99650
|
this.feedbackMode = false;
|
|
99140
99651
|
this.selectedIndex = (this.selectedIndex - 1 + this.choiceCount()) % this.choiceCount();
|
|
@@ -99145,16 +99656,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99145
99656
|
this.selectedIndex = (this.selectedIndex + 1) % this.choiceCount();
|
|
99146
99657
|
return;
|
|
99147
99658
|
}
|
|
99148
|
-
|
|
99149
|
-
this.feedbackText = this.feedbackText.slice(0, -1);
|
|
99150
|
-
return;
|
|
99151
|
-
}
|
|
99152
|
-
const printable = decodeKittyPrintable(data);
|
|
99153
|
-
if (printable !== void 0) {
|
|
99154
|
-
this.feedbackText += printable;
|
|
99155
|
-
return;
|
|
99156
|
-
}
|
|
99157
|
-
if (data.length > 0 && !data.startsWith("\x1B")) this.feedbackText += data;
|
|
99659
|
+
this.feedbackInput.handleInput(data);
|
|
99158
99660
|
return;
|
|
99159
99661
|
}
|
|
99160
99662
|
if (this.choiceCount() === 0) return;
|
|
@@ -99177,6 +99679,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99177
99679
|
render(width) {
|
|
99178
99680
|
this.clear();
|
|
99179
99681
|
this.ensureValidSelection();
|
|
99682
|
+
this.feedbackInput.focused = this.focused && this.feedbackMode;
|
|
99180
99683
|
const { data } = this.request;
|
|
99181
99684
|
const horizontalBar = borderColor("─".repeat(width));
|
|
99182
99685
|
const indent = (s) => ` ${s}`;
|
|
@@ -99201,12 +99704,12 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99201
99704
|
const isSelected = idx === this.selectedIndex;
|
|
99202
99705
|
const num = idx + 1;
|
|
99203
99706
|
const labelWithNum = `${String(num)}. ${option.label}`;
|
|
99204
|
-
if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(
|
|
99707
|
+
if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(this.renderInlineFeedbackLine(width - 2, labelWithNum)));
|
|
99205
99708
|
else if (isSelected) lines.push(indent(`${selectColor.bold("▶")} ${selectColor.bold(labelWithNum)}`));
|
|
99206
99709
|
else lines.push(indent(chalk.gray(` ${labelWithNum}`)));
|
|
99207
99710
|
}
|
|
99208
99711
|
lines.push("");
|
|
99209
|
-
if (this.feedbackMode) lines.push(indent(chalk.dim("Type feedback
|
|
99712
|
+
if (this.feedbackMode) lines.push(indent(chalk.dim("Type feedback · ↵ submit.")));
|
|
99210
99713
|
else {
|
|
99211
99714
|
const expandHint = hasDiff ? ` · ctrl+o ${this.expanded ? "collapse" : "expand"}` : "";
|
|
99212
99715
|
lines.push(indent(chalk.dim(`↑/↓ select · ${buildNumericHint(data.choices.length)} choose · ↵ confirm${expandHint}`)));
|
|
@@ -99228,6 +99731,16 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
99228
99731
|
}
|
|
99229
99732
|
if (this.selectedIndex < 0 || this.selectedIndex >= count) this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, count - 1));
|
|
99230
99733
|
}
|
|
99734
|
+
renderInlineFeedbackLine(width, labelWithNum) {
|
|
99735
|
+
const prefix = `${selectColor.bold("▶")} ${selectColor.bold(labelWithNum)} `;
|
|
99736
|
+
const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
|
|
99737
|
+
const inputLine = this.feedbackInput.render(inputWidth)[0] ?? "> ";
|
|
99738
|
+
return prefix + (inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine);
|
|
99739
|
+
}
|
|
99740
|
+
invalidate() {
|
|
99741
|
+
super.invalidate();
|
|
99742
|
+
this.feedbackInput.invalidate();
|
|
99743
|
+
}
|
|
99231
99744
|
};
|
|
99232
99745
|
function buildNumericHint(count) {
|
|
99233
99746
|
if (count <= 0) return "↵";
|
|
@@ -99314,6 +99827,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99314
99827
|
colors;
|
|
99315
99828
|
onAnswer;
|
|
99316
99829
|
maxVisibleOptions;
|
|
99830
|
+
otherInput = new Input();
|
|
99317
99831
|
currentTab = 0;
|
|
99318
99832
|
submitActionIdx = 0;
|
|
99319
99833
|
editingOther = false;
|
|
@@ -99336,6 +99850,9 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99336
99850
|
this.onAnswer = onAnswer;
|
|
99337
99851
|
this.colors = colors;
|
|
99338
99852
|
this.maxVisibleOptions = maxVisibleOptions;
|
|
99853
|
+
this.otherInput.onSubmit = (value) => {
|
|
99854
|
+
this.commitOtherInput(value);
|
|
99855
|
+
};
|
|
99339
99856
|
const total = request.data.questions.length;
|
|
99340
99857
|
this.cursors = Array.from({ length: total }, () => 0);
|
|
99341
99858
|
this.singleSelections = Array.from({ length: total }, () => void 0);
|
|
@@ -99349,6 +99866,10 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99349
99866
|
this.onAnswer([]);
|
|
99350
99867
|
return;
|
|
99351
99868
|
}
|
|
99869
|
+
if (matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrl("d"))) {
|
|
99870
|
+
this.onAnswer([]);
|
|
99871
|
+
return;
|
|
99872
|
+
}
|
|
99352
99873
|
if (this.isEditingOther()) {
|
|
99353
99874
|
this.handleOtherInput(data);
|
|
99354
99875
|
return;
|
|
@@ -99390,58 +99911,32 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99390
99911
|
this.activateQuestionOption(numIdx);
|
|
99391
99912
|
return;
|
|
99392
99913
|
}
|
|
99393
|
-
if (printable === " " || matchesKey(data, Key.space))
|
|
99394
|
-
if (question.multi_select) this.activateQuestionOption(this.currentCursor());
|
|
99395
|
-
return;
|
|
99396
|
-
}
|
|
99914
|
+
if ((printable === " " || matchesKey(data, Key.space)) && question.multi_select) this.activateQuestionOption(this.currentCursor());
|
|
99397
99915
|
}
|
|
99398
99916
|
handleOtherInput(data) {
|
|
99399
99917
|
const questionIdx = this.currentQuestionIndex();
|
|
99400
99918
|
if (questionIdx === void 0) return;
|
|
99401
|
-
if (matchesKey(data, Key.
|
|
99402
|
-
this.
|
|
99403
|
-
this.gotoTab(this.currentTab - 1);
|
|
99404
|
-
return;
|
|
99405
|
-
}
|
|
99406
|
-
if (matchesKey(data, Key.right) || matchesKey(data, Key.tab)) {
|
|
99919
|
+
if (matchesKey(data, Key.tab)) {
|
|
99920
|
+
this.syncOtherDraft(questionIdx);
|
|
99407
99921
|
this.editingOther = false;
|
|
99408
99922
|
this.gotoTab(this.currentTab + 1);
|
|
99409
99923
|
return;
|
|
99410
99924
|
}
|
|
99411
99925
|
if (matchesKey(data, Key.up)) {
|
|
99926
|
+
this.syncOtherDraft(questionIdx);
|
|
99412
99927
|
this.editingOther = false;
|
|
99413
99928
|
this.moveQuestionCursor(-1);
|
|
99414
99929
|
return;
|
|
99415
99930
|
}
|
|
99416
99931
|
if (matchesKey(data, Key.down)) {
|
|
99932
|
+
this.syncOtherDraft(questionIdx);
|
|
99417
99933
|
this.editingOther = false;
|
|
99418
99934
|
this.moveQuestionCursor(1);
|
|
99419
99935
|
return;
|
|
99420
99936
|
}
|
|
99421
|
-
|
|
99422
|
-
|
|
99423
|
-
|
|
99424
|
-
}
|
|
99425
|
-
if (matchesKey(data, Key.backspace)) {
|
|
99426
|
-
this.otherDrafts[questionIdx] = this.otherDrafts[questionIdx]?.slice(0, -1) ?? "";
|
|
99427
|
-
this.reviewMessage = void 0;
|
|
99428
|
-
return;
|
|
99429
|
-
}
|
|
99430
|
-
const printable = decodeKittyPrintable(data);
|
|
99431
|
-
if (printable !== void 0) {
|
|
99432
|
-
this.otherDrafts[questionIdx] = (this.otherDrafts[questionIdx] ?? "") + printable;
|
|
99433
|
-
this.reviewMessage = void 0;
|
|
99434
|
-
return;
|
|
99435
|
-
}
|
|
99436
|
-
if (data === " " || matchesKey(data, Key.space)) {
|
|
99437
|
-
this.otherDrafts[questionIdx] = (this.otherDrafts[questionIdx] ?? "") + " ";
|
|
99438
|
-
this.reviewMessage = void 0;
|
|
99439
|
-
return;
|
|
99440
|
-
}
|
|
99441
|
-
if (data.length > 0 && !data.startsWith("\x1B")) {
|
|
99442
|
-
this.otherDrafts[questionIdx] = (this.otherDrafts[questionIdx] ?? "") + data;
|
|
99443
|
-
this.reviewMessage = void 0;
|
|
99444
|
-
}
|
|
99937
|
+
this.otherInput.handleInput(data);
|
|
99938
|
+
this.syncOtherDraft(questionIdx);
|
|
99939
|
+
this.reviewMessage = void 0;
|
|
99445
99940
|
}
|
|
99446
99941
|
handleSubmitInput(data) {
|
|
99447
99942
|
if (matchesKey(data, Key.up)) {
|
|
@@ -99475,7 +99970,6 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99475
99970
|
if (printable === "2") {
|
|
99476
99971
|
this.submitActionIdx = 1;
|
|
99477
99972
|
this.executeSubmitAction(1);
|
|
99478
|
-
return;
|
|
99479
99973
|
}
|
|
99480
99974
|
}
|
|
99481
99975
|
gotoTab(target) {
|
|
@@ -99524,15 +100018,17 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99524
100018
|
enterOtherInput(questionIdx) {
|
|
99525
100019
|
this.cursors[questionIdx] = this.otherOptionIndex(questionIdx);
|
|
99526
100020
|
this.editingOther = true;
|
|
100021
|
+
this.otherInput.setValue(this.otherDraftValue(questionIdx));
|
|
99527
100022
|
this.reviewMessage = void 0;
|
|
99528
100023
|
}
|
|
99529
|
-
commitOtherInput() {
|
|
100024
|
+
commitOtherInput(rawValue) {
|
|
99530
100025
|
const questionIdx = this.currentQuestionIndex();
|
|
99531
100026
|
if (questionIdx === void 0) return;
|
|
99532
100027
|
const question = this.request.data.questions[questionIdx];
|
|
99533
100028
|
if (question === void 0) return;
|
|
99534
|
-
const value = this.
|
|
100029
|
+
const value = (rawValue ?? this.otherInput.getValue()).trim();
|
|
99535
100030
|
if (value.length === 0) return;
|
|
100031
|
+
this.otherInput.setValue(value);
|
|
99536
100032
|
this.otherDrafts[questionIdx] = value;
|
|
99537
100033
|
this.committedOtherValues[questionIdx] = value;
|
|
99538
100034
|
if (question.multi_select) this.multiSelections[questionIdx]?.add(this.otherOptionIndex(questionIdx));
|
|
@@ -99599,6 +100095,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99599
100095
|
this.onAnswer(out);
|
|
99600
100096
|
}
|
|
99601
100097
|
render(width) {
|
|
100098
|
+
this.otherInput.focused = this.focused && this.isEditingOther();
|
|
99602
100099
|
return this.isSubmitTab() ? this.renderSubmitTab(width) : this.renderQuestionTab(width);
|
|
99603
100100
|
}
|
|
99604
100101
|
renderQuestionTab(width) {
|
|
@@ -99611,10 +100108,11 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99611
100108
|
const dim = chalk.hex(colors.textDim);
|
|
99612
100109
|
const success = chalk.hex(colors.success);
|
|
99613
100110
|
const renderWidth = Math.max(1, width);
|
|
99614
|
-
const lines = [
|
|
99615
|
-
|
|
99616
|
-
|
|
99617
|
-
|
|
100111
|
+
const lines = [
|
|
100112
|
+
accent("─".repeat(renderWidth)),
|
|
100113
|
+
accent.bold(" question"),
|
|
100114
|
+
""
|
|
100115
|
+
];
|
|
99618
100116
|
this.pushTabs(lines);
|
|
99619
100117
|
lines.push("");
|
|
99620
100118
|
lines.push(accent(` ? ${question.question}`));
|
|
@@ -99635,10 +100133,15 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99635
100133
|
const singleSelection = this.singleSelections[questionIdx];
|
|
99636
100134
|
for (let i = visibleStart; i < visibleEnd; i++) {
|
|
99637
100135
|
const option = options[i];
|
|
100136
|
+
if (option === void 0) continue;
|
|
99638
100137
|
const num = i + 1;
|
|
99639
100138
|
const isCursor = i === cursor;
|
|
99640
100139
|
const isOther = option.kind === "other";
|
|
99641
100140
|
const isSelected = question.multi_select ? multiSet.has(i) : singleSelection === i;
|
|
100141
|
+
if (this.isEditingOther() && isCursor && isOther) {
|
|
100142
|
+
lines.push(this.renderEditingOtherLine(renderWidth, questionIdx, option, num, isSelected));
|
|
100143
|
+
continue;
|
|
100144
|
+
}
|
|
99642
100145
|
const label = this.renderOptionLabel(questionIdx, option, isCursor);
|
|
99643
100146
|
let line;
|
|
99644
100147
|
if (question.multi_select) {
|
|
@@ -99666,10 +100169,11 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99666
100169
|
const text = chalk.hex(colors.text);
|
|
99667
100170
|
const warning = chalk.hex(colors.warning);
|
|
99668
100171
|
const renderWidth = Math.max(1, width);
|
|
99669
|
-
const lines = [
|
|
99670
|
-
|
|
99671
|
-
|
|
99672
|
-
|
|
100172
|
+
const lines = [
|
|
100173
|
+
accent("─".repeat(renderWidth)),
|
|
100174
|
+
accent.bold(" question"),
|
|
100175
|
+
""
|
|
100176
|
+
];
|
|
99673
100177
|
this.pushTabs(lines);
|
|
99674
100178
|
lines.push("");
|
|
99675
100179
|
lines.push(text.bold(` ${REVIEW_TITLE}`));
|
|
@@ -99678,6 +100182,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99678
100182
|
lines.push("");
|
|
99679
100183
|
for (let i = 0; i < this.request.data.questions.length; i++) {
|
|
99680
100184
|
const question = this.request.data.questions[i];
|
|
100185
|
+
if (question === void 0) continue;
|
|
99681
100186
|
const answer = this.answers[i];
|
|
99682
100187
|
lines.push(` ${dim("Q")} ${question.question}`);
|
|
99683
100188
|
if (answer !== void 0 && answer.length > 0) lines.push(` ${accent("→")} ${text(answer)}`);
|
|
@@ -99688,6 +100193,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99688
100193
|
lines.push("");
|
|
99689
100194
|
for (let i = 0; i < SUBMIT_ACTIONS.length; i++) {
|
|
99690
100195
|
const label = SUBMIT_ACTIONS[i];
|
|
100196
|
+
if (label === void 0) continue;
|
|
99691
100197
|
const num = i + 1;
|
|
99692
100198
|
if (i === this.submitActionIdx) lines.push(accent(` → [${String(num)}] ${label}`));
|
|
99693
100199
|
else lines.push(dim(` [${String(num)}] ${label}`));
|
|
@@ -99703,6 +100209,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99703
100209
|
const tabs = [];
|
|
99704
100210
|
for (let i = 0; i < this.request.data.questions.length; i++) {
|
|
99705
100211
|
const question = this.request.data.questions[i];
|
|
100212
|
+
if (question === void 0) continue;
|
|
99706
100213
|
const label = question.header !== void 0 && question.header.length > 0 ? question.header : `Q${String(i + 1)}`;
|
|
99707
100214
|
if (i === this.currentTab) tabs.push(active(` ${label} `));
|
|
99708
100215
|
else if (this.isAnswered(i)) tabs.push(chalk.green(`(✓) ${label}`));
|
|
@@ -99714,14 +100221,17 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99714
100221
|
lines.push(` ${tabs.join(" ")}`);
|
|
99715
100222
|
}
|
|
99716
100223
|
buildQuestionHint(dim, questionIdx) {
|
|
99717
|
-
if (this.isEditingOther()) {
|
|
99718
|
-
|
|
99719
|
-
|
|
99720
|
-
|
|
99721
|
-
|
|
99722
|
-
}
|
|
100224
|
+
if (this.isEditingOther()) return dim(` ${[
|
|
100225
|
+
"type answer",
|
|
100226
|
+
"↵ save",
|
|
100227
|
+
...this.totalTabs() > 1 ? ["tab switch"] : [],
|
|
100228
|
+
"esc dismiss"
|
|
100229
|
+
].join(" ")}`);
|
|
99723
100230
|
const optionCount = Math.min(this.displayOptions(questionIdx).length, NUMBER_KEYS.length);
|
|
99724
|
-
const
|
|
100231
|
+
const numberHint = optionCount <= 1 ? "1" : `1-${String(optionCount)}`;
|
|
100232
|
+
const question = this.request.data.questions[questionIdx];
|
|
100233
|
+
if (question === void 0) return dim(" esc dismiss");
|
|
100234
|
+
const parts = ["▲/▼ select", `${numberHint} / ↵ ${question.multi_select ? "toggle" : "choose"}`];
|
|
99725
100235
|
if (this.totalTabs() > 1) parts.push("←/→/tab switch");
|
|
99726
100236
|
parts.push("esc dismiss");
|
|
99727
100237
|
return dim(` ${parts.join(" ")}`);
|
|
@@ -99783,11 +100293,33 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99783
100293
|
}
|
|
99784
100294
|
renderOptionLabel(questionIdx, option, isCursor) {
|
|
99785
100295
|
if (option.kind !== "other") return option.label;
|
|
99786
|
-
const value = this.
|
|
100296
|
+
const value = this.otherDraftValue(questionIdx);
|
|
99787
100297
|
if (this.isEditingOther() && isCursor) return `${option.label}: ${value ?? ""}█`;
|
|
99788
100298
|
if (value !== void 0 && value.length > 0) return `${option.label}: ${value}`;
|
|
99789
100299
|
return option.label;
|
|
99790
100300
|
}
|
|
100301
|
+
renderEditingOtherLine(width, questionIdx, option, num, isSelected) {
|
|
100302
|
+
const question = this.request.data.questions[questionIdx];
|
|
100303
|
+
if (question === void 0) return option.label;
|
|
100304
|
+
let prefix;
|
|
100305
|
+
if (question.multi_select) {
|
|
100306
|
+
const body = ` [${isSelected ? "✓" : " "}] ${option.label}: `;
|
|
100307
|
+
prefix = isSelected ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary)(body);
|
|
100308
|
+
} else {
|
|
100309
|
+
const body = ` → [${String(num)}] ${option.label}: `;
|
|
100310
|
+
prefix = isSelected && this.isAnswered(questionIdx) ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary)(body);
|
|
100311
|
+
}
|
|
100312
|
+
const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
|
|
100313
|
+
const inputLine = this.otherInput.render(inputWidth)[0] ?? "> ";
|
|
100314
|
+
const inlineInput = inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine;
|
|
100315
|
+
return prefix + inlineInput;
|
|
100316
|
+
}
|
|
100317
|
+
otherDraftValue(questionIdx) {
|
|
100318
|
+
return this.otherDrafts[questionIdx] || this.committedOtherValues[questionIdx] || "";
|
|
100319
|
+
}
|
|
100320
|
+
syncOtherDraft(questionIdx) {
|
|
100321
|
+
this.otherDrafts[questionIdx] = this.otherInput.getValue();
|
|
100322
|
+
}
|
|
99791
100323
|
isAnswered(questionIdx) {
|
|
99792
100324
|
const answer = this.answers[questionIdx];
|
|
99793
100325
|
return answer !== void 0 && answer.length > 0;
|
|
@@ -99796,6 +100328,10 @@ var QuestionDialogComponent = class extends Container {
|
|
|
99796
100328
|
for (let i = 0; i < this.request.data.questions.length; i++) if (!this.isAnswered(i)) return true;
|
|
99797
100329
|
return false;
|
|
99798
100330
|
}
|
|
100331
|
+
invalidate() {
|
|
100332
|
+
super.invalidate();
|
|
100333
|
+
this.otherInput.invalidate();
|
|
100334
|
+
}
|
|
99799
100335
|
};
|
|
99800
100336
|
//#endregion
|
|
99801
100337
|
//#region src/tui/reverse-rpc/question/ui.ts
|
|
@@ -100804,14 +101340,18 @@ var KimiTUI = class {
|
|
|
100804
101340
|
this.stateHooks.syncFooter();
|
|
100805
101341
|
this.stateHooks.refreshActivityPane();
|
|
100806
101342
|
},
|
|
100807
|
-
showStatus: (message) => {
|
|
101343
|
+
showStatus: (message, color) => {
|
|
100808
101344
|
addTranscriptEntry(this.state, {
|
|
100809
101345
|
id: `status-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
100810
101346
|
kind: "status",
|
|
100811
101347
|
renderMode: "plain",
|
|
100812
|
-
content: message
|
|
101348
|
+
content: message,
|
|
101349
|
+
...color !== void 0 ? { color } : {}
|
|
100813
101350
|
});
|
|
100814
101351
|
},
|
|
101352
|
+
showNotice: (title, detail) => {
|
|
101353
|
+
emitNotice(this.state, title, detail);
|
|
101354
|
+
},
|
|
100815
101355
|
stop: () => void this.stop(),
|
|
100816
101356
|
showHelpPanel: () => showHelpPanel(this.state),
|
|
100817
101357
|
showSessionPicker: () => showSessionPicker(this.state, this.buildSessionHooks()),
|
|
@@ -100942,6 +101482,7 @@ const MUTED = "#999999";
|
|
|
100942
101482
|
const TEXT_DIM = "#6B7280";
|
|
100943
101483
|
const INSTALL_HINT = "Install update now";
|
|
100944
101484
|
const SKIP_HINT = "Continue with current version";
|
|
101485
|
+
const PRODUCT_NAME = "Kimi Code";
|
|
100945
101486
|
function createInstallPromptChoices(target) {
|
|
100946
101487
|
return [{
|
|
100947
101488
|
value: "install",
|
|
@@ -100953,7 +101494,7 @@ function createInstallPromptChoices(target) {
|
|
|
100953
101494
|
}
|
|
100954
101495
|
function getDefaultInstallPromptSelection(choices) {
|
|
100955
101496
|
const installIndex = choices.findIndex((choice) => choice.value === "install");
|
|
100956
|
-
return installIndex
|
|
101497
|
+
return Math.max(installIndex, 0);
|
|
100957
101498
|
}
|
|
100958
101499
|
function moveInstallPromptSelection(currentIndex, direction, choiceCount) {
|
|
100959
101500
|
if (direction === "up") return Math.max(0, currentIndex - 1);
|
|
@@ -100967,7 +101508,7 @@ function renderInstallPrompt(options, choices, selectedIndex) {
|
|
|
100967
101508
|
const command = chalk.hex(PRIMARY)(options.installCommand);
|
|
100968
101509
|
const lines = [
|
|
100969
101510
|
chalk.hex(PRIMARY).bold("Kimi Code Update Available"),
|
|
100970
|
-
chalk.hex(MUTED)(`${
|
|
101511
|
+
chalk.hex(MUTED)(`${PRODUCT_NAME} has a newer release ready.`),
|
|
100971
101512
|
"",
|
|
100972
101513
|
`${label("Current")} ${currentVersion}`,
|
|
100973
101514
|
`${label("Target ")} ${targetVersion} ${channel}`,
|
|
@@ -100978,6 +101519,7 @@ function renderInstallPrompt(options, choices, selectedIndex) {
|
|
|
100978
101519
|
];
|
|
100979
101520
|
for (let i = 0; i < choices.length; i++) {
|
|
100980
101521
|
const choice = choices[i];
|
|
101522
|
+
if (choice === void 0) continue;
|
|
100981
101523
|
const isSelected = i === selectedIndex;
|
|
100982
101524
|
const content = ` ${isSelected ? "❯" : " "} ${choice.label}`;
|
|
100983
101525
|
if (isSelected) {
|