kfc-code-cli 0.0.1-alpha.1 → 0.0.1-alpha.2
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/README.md +13 -0
- package/dist/main.mjs +1856 -632
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import path, { basename, dirname, extname, isAbsolute, join, normalize, posix, r
|
|
|
10
10
|
import { finished, pipeline } from "node:stream/promises";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { ZodError, z } from "zod";
|
|
13
|
-
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
13
|
+
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
14
14
|
import { execFile, execSync, spawn, spawnSync } from "node:child_process";
|
|
15
15
|
import { createInterface } from "node:readline";
|
|
16
16
|
import * as nodeOs from "node:os";
|
|
@@ -34,7 +34,7 @@ import { writeFile as writeFile$1 } from "fs/promises";
|
|
|
34
34
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
35
35
|
import { ZipFile } from "yazl";
|
|
36
36
|
import pino from "pino";
|
|
37
|
-
import { CombinedAutocompleteProvider, Container, Editor, Image, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, fuzzyFilter, fuzzyMatch, getCapabilities, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
37
|
+
import { CombinedAutocompleteProvider, Container, Editor, Image, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, fuzzyFilter, fuzzyMatch, getCapabilities, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
38
38
|
import chalk from "chalk";
|
|
39
39
|
import { highlight } from "cli-highlight";
|
|
40
40
|
//#region ../../packages/kimi-core/src/storage/fs/durability.ts
|
|
@@ -904,7 +904,7 @@ var BaseContextState = class {
|
|
|
904
904
|
...input.finishReason !== void 0 ? { finish_reason: input.finishReason } : {}
|
|
905
905
|
});
|
|
906
906
|
this.openSteps.delete(input.uuid);
|
|
907
|
-
if (input.usage !== void 0) this._tokenCountWithPending
|
|
907
|
+
if (input.usage !== void 0) this._tokenCountWithPending = input.usage.input_tokens + input.usage.output_tokens;
|
|
908
908
|
}
|
|
909
909
|
async appendContentPart(input) {
|
|
910
910
|
this.assertNotBroken();
|
|
@@ -2092,6 +2092,19 @@ async function atomicWrite(filePath, content, _syncOverride) {
|
|
|
2092
2092
|
}
|
|
2093
2093
|
}
|
|
2094
2094
|
//#endregion
|
|
2095
|
+
//#region ../../packages/kimi-core/src/session/session-runtime-slot.ts
|
|
2096
|
+
function createSessionRuntimeSlot(initial) {
|
|
2097
|
+
let current = initial;
|
|
2098
|
+
return {
|
|
2099
|
+
current() {
|
|
2100
|
+
return current;
|
|
2101
|
+
},
|
|
2102
|
+
replace(next) {
|
|
2103
|
+
current = next;
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
//#endregion
|
|
2095
2108
|
//#region ../../packages/kimi-core/src/soul/adapters.ts
|
|
2096
2109
|
/**
|
|
2097
2110
|
* Widen `ToolCall.args` (`unknown`) into the `Record<string, unknown>`
|
|
@@ -2176,11 +2189,16 @@ function adaptToolResult(r) {
|
|
|
2176
2189
|
if (r.isError === true) payload.isError = true;
|
|
2177
2190
|
return payload;
|
|
2178
2191
|
}
|
|
2192
|
+
const DEFAULT_RESERVED_CONTEXT_SIZE$1 = 5e4;
|
|
2193
|
+
const DEFAULT_RESERVED_CONTEXT_FRACTION$1 = .25;
|
|
2194
|
+
function defaultReservedContextSize$1(maxContextSize) {
|
|
2195
|
+
return Math.max(0, Math.min(DEFAULT_RESERVED_CONTEXT_SIZE$1, Math.floor(maxContextSize * DEFAULT_RESERVED_CONTEXT_FRACTION$1)));
|
|
2196
|
+
}
|
|
2179
2197
|
/** Returns true when either trigger condition fires. */
|
|
2180
2198
|
function shouldCompact(context, config) {
|
|
2181
2199
|
if (config === void 0) return false;
|
|
2182
2200
|
const triggerRatio = config.triggerRatio ?? .85;
|
|
2183
|
-
const reservedContextSize = config.reservedContextSize ??
|
|
2201
|
+
const reservedContextSize = config.reservedContextSize ?? defaultReservedContextSize$1(config.maxContextSize);
|
|
2184
2202
|
const tokens = context.tokenCountWithPending;
|
|
2185
2203
|
if (tokens >= config.maxContextSize * triggerRatio) return true;
|
|
2186
2204
|
if (tokens + reservedContextSize >= config.maxContextSize) return true;
|
|
@@ -2736,6 +2754,206 @@ async function executePendingCalls(step, pending, deferred) {
|
|
|
2736
2754
|
await context.appendToolResult(toolCallByProviderId.get(toolCall.id), toolCall.id, adapted);
|
|
2737
2755
|
}
|
|
2738
2756
|
}
|
|
2757
|
+
//#endregion
|
|
2758
|
+
//#region ../../packages/kimi-core/src/hooks/engine.ts
|
|
2759
|
+
/**
|
|
2760
|
+
* Synthesises a stable id for `hook.resolved` emissions. Hooks are
|
|
2761
|
+
* registered without an intrinsic id; we derive one from
|
|
2762
|
+
* `event:type:matcher`, suffixed with the hook's position inside
|
|
2763
|
+
* `this.hooks` (its registration order). The `registrationIndex` is
|
|
2764
|
+
* stable across multiple `executeHooks` calls — using the per-call
|
|
2765
|
+
* `settled[]` index instead would hand the same id to different hooks
|
|
2766
|
+
* across different dispatches, breaking client-side correlation.
|
|
2767
|
+
*/
|
|
2768
|
+
function hookId(hook, registrationIndex) {
|
|
2769
|
+
return `${hook.event}:${hook.type}:${hook.matcher ?? ""}:${registrationIndex}`;
|
|
2770
|
+
}
|
|
2771
|
+
var HookEngine = class {
|
|
2772
|
+
deps;
|
|
2773
|
+
/**
|
|
2774
|
+
* Mutable executor registry. Seeded from `deps.executors` at
|
|
2775
|
+
* construction so the constructor arg can stay `ReadonlyMap`-typed
|
|
2776
|
+
* while `registerExecutor` still lets callers install additional
|
|
2777
|
+
* executors (e.g. `WireHookExecutor`) after the engine is wired.
|
|
2778
|
+
*/
|
|
2779
|
+
executors;
|
|
2780
|
+
hooks = [];
|
|
2781
|
+
/**
|
|
2782
|
+
* Phase 18 L3-2 — invalid-regex warn dedupe. Each distinct invalid
|
|
2783
|
+
* matcher fires `onInvalidMatcher` once per engine instance so a
|
|
2784
|
+
* misconfigured block-action hook doesn't flood logs on every
|
|
2785
|
+
* tool call.
|
|
2786
|
+
*/
|
|
2787
|
+
warnedInvalidMatchers = /* @__PURE__ */ new Set();
|
|
2788
|
+
constructor(deps) {
|
|
2789
|
+
this.deps = deps;
|
|
2790
|
+
this.executors = new Map(deps.executors);
|
|
2791
|
+
}
|
|
2792
|
+
register(hook) {
|
|
2793
|
+
this.hooks.push(hook);
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Phase 18 L2-4 — install / replace an executor for a given `type`
|
|
2797
|
+
* label at runtime. Used by the wire layer to bolt a
|
|
2798
|
+
* `WireHookExecutor` onto an engine that was constructed with only
|
|
2799
|
+
* the `command` executor. Replacing an existing entry is silent;
|
|
2800
|
+
* callers wanting a conflict check can probe `hasExecutor(type)`.
|
|
2801
|
+
*/
|
|
2802
|
+
registerExecutor(type, executor) {
|
|
2803
|
+
this.executors.set(type, executor);
|
|
2804
|
+
}
|
|
2805
|
+
/** Returns true when an executor has been registered under `type`. */
|
|
2806
|
+
hasExecutor(type) {
|
|
2807
|
+
return this.executors.has(type);
|
|
2808
|
+
}
|
|
2809
|
+
unregister(hook) {
|
|
2810
|
+
const idx = this.hooks.indexOf(hook);
|
|
2811
|
+
if (idx !== -1) this.hooks.splice(idx, 1);
|
|
2812
|
+
}
|
|
2813
|
+
list(event) {
|
|
2814
|
+
if (event === void 0) return [...this.hooks];
|
|
2815
|
+
return this.hooks.filter((h) => h.event === event);
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Pre-filters hooks by both `event` and `matcher` regex against the
|
|
2819
|
+
* target value (tool name for tool-scoped events). Exported-ish via
|
|
2820
|
+
* `executeHooks` — v2 §9-C.3 requires "getMatchingHooks(event, input)
|
|
2821
|
+
* then concurrent execute" ordering.
|
|
2822
|
+
*/
|
|
2823
|
+
getMatchingHooks(event, input) {
|
|
2824
|
+
const matcherValue = extractMatcherValue(input);
|
|
2825
|
+
return this.hooks.filter((h) => {
|
|
2826
|
+
if (h.event !== event) return false;
|
|
2827
|
+
return this.matchesTarget(h, matcherValue);
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
matchesTarget(hook, value) {
|
|
2831
|
+
const matcher = hook.matcher;
|
|
2832
|
+
if (matcher === void 0 || matcher === "") return true;
|
|
2833
|
+
let re;
|
|
2834
|
+
try {
|
|
2835
|
+
re = new RegExp(matcher);
|
|
2836
|
+
} catch {
|
|
2837
|
+
if (!this.warnedInvalidMatchers.has(matcher)) {
|
|
2838
|
+
this.warnedInvalidMatchers.add(matcher);
|
|
2839
|
+
this.deps.onInvalidMatcher?.(hook, matcher);
|
|
2840
|
+
}
|
|
2841
|
+
return false;
|
|
2842
|
+
}
|
|
2843
|
+
return re.test(value);
|
|
2844
|
+
}
|
|
2845
|
+
async executeHooks(event, input, signal) {
|
|
2846
|
+
const deduped = dedupeByCommand(this.getMatchingHooks(event, input));
|
|
2847
|
+
if (deduped.length === 0) {
|
|
2848
|
+
this.deps.sink?.emit({
|
|
2849
|
+
type: "hook.triggered",
|
|
2850
|
+
event,
|
|
2851
|
+
matchers: [],
|
|
2852
|
+
matched_count: 0
|
|
2853
|
+
});
|
|
2854
|
+
return {
|
|
2855
|
+
blockAction: false,
|
|
2856
|
+
additionalContext: []
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
this.deps.sink?.emit({
|
|
2860
|
+
type: "hook.triggered",
|
|
2861
|
+
event,
|
|
2862
|
+
matchers: deduped.map((h) => h.matcher ?? ""),
|
|
2863
|
+
matched_count: deduped.length
|
|
2864
|
+
});
|
|
2865
|
+
const settled = await Promise.allSettled(deduped.map((hook) => {
|
|
2866
|
+
const executor = this.executors.get(hook.type);
|
|
2867
|
+
if (executor === void 0) return Promise.resolve(void 0);
|
|
2868
|
+
return executor.execute(hook, input, signal);
|
|
2869
|
+
}));
|
|
2870
|
+
let blockAction = false;
|
|
2871
|
+
let reason;
|
|
2872
|
+
const additionalContext = [];
|
|
2873
|
+
for (const [i, result] of settled.entries()) {
|
|
2874
|
+
const hook = deduped[i];
|
|
2875
|
+
const registrationIndex = hook !== void 0 ? this.hooks.indexOf(hook) : -1;
|
|
2876
|
+
if (result.status === "rejected") {
|
|
2877
|
+
if (hook !== void 0) {
|
|
2878
|
+
this.deps.onExecutorError?.(hook, result.reason instanceof Error ? result.reason : new Error(String(result.reason)));
|
|
2879
|
+
this.deps.sink?.emit({
|
|
2880
|
+
type: "hook.resolved",
|
|
2881
|
+
hook_id: hookId(hook, registrationIndex),
|
|
2882
|
+
outcome: "error"
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
continue;
|
|
2886
|
+
}
|
|
2887
|
+
const value = result.value;
|
|
2888
|
+
if (hook !== void 0) {
|
|
2889
|
+
const outcome = value?.ok === false ? "error" : value?.blockAction === true ? "blocked" : "ok";
|
|
2890
|
+
this.deps.sink?.emit({
|
|
2891
|
+
type: "hook.resolved",
|
|
2892
|
+
hook_id: hookId(hook, registrationIndex),
|
|
2893
|
+
outcome
|
|
2894
|
+
});
|
|
2895
|
+
}
|
|
2896
|
+
if (value === void 0) continue;
|
|
2897
|
+
if (value.blockAction) {
|
|
2898
|
+
blockAction = true;
|
|
2899
|
+
if (value.reason !== void 0) reason = value.reason;
|
|
2900
|
+
}
|
|
2901
|
+
if (value.additionalContext !== void 0) additionalContext.push(value.additionalContext);
|
|
2902
|
+
}
|
|
2903
|
+
return {
|
|
2904
|
+
blockAction,
|
|
2905
|
+
reason,
|
|
2906
|
+
additionalContext
|
|
2907
|
+
};
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
/**
|
|
2911
|
+
* Collapse hook configs that share the same `command` string. `wire` hooks
|
|
2912
|
+
* (no `command` field) key on their subscription id so different wire
|
|
2913
|
+
* subscriptions still run in parallel; identical subscriptions collapse
|
|
2914
|
+
* like identical commands.
|
|
2915
|
+
*/
|
|
2916
|
+
function dedupeByCommand(hooks) {
|
|
2917
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2918
|
+
for (const hook of hooks) {
|
|
2919
|
+
const key = hookDedupeKey(hook);
|
|
2920
|
+
if (!seen.has(key)) seen.set(key, hook);
|
|
2921
|
+
}
|
|
2922
|
+
return [...seen.values()];
|
|
2923
|
+
}
|
|
2924
|
+
function hookDedupeKey(hook) {
|
|
2925
|
+
if (hook.type === "command") return `command:${hook.command}`;
|
|
2926
|
+
if (hook.type === "wire") return `wire:${hook.subscriptionId}`;
|
|
2927
|
+
return `unknown:${JSON.stringify(hook)}`;
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Extracts the string fed to a hook's matcher regex. Event-dependent:
|
|
2931
|
+
*
|
|
2932
|
+
* - `PreToolUse` / `PostToolUse` / `OnToolFailure` — tool name
|
|
2933
|
+
* (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
|
+
* - `Notification` — the notification type string, so a single hook
|
|
2938
|
+
* can subscribe to an entire notification class via regex.
|
|
2939
|
+
*/
|
|
2940
|
+
function extractMatcherValue(input) {
|
|
2941
|
+
switch (input.event) {
|
|
2942
|
+
case "PreToolUse":
|
|
2943
|
+
case "PostToolUse":
|
|
2944
|
+
case "OnToolFailure": return input.toolCall.name;
|
|
2945
|
+
case "UserPromptSubmit": return input.prompt;
|
|
2946
|
+
case "Stop": return input.reason;
|
|
2947
|
+
case "Notification": return input.notificationType;
|
|
2948
|
+
case "StopFailure": return input.error;
|
|
2949
|
+
case "SubagentStart":
|
|
2950
|
+
case "SubagentStop": return input.agentName;
|
|
2951
|
+
case "SessionStart":
|
|
2952
|
+
case "SessionEnd":
|
|
2953
|
+
case "PreCompact":
|
|
2954
|
+
case "PostCompact": return "";
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2739
2957
|
const AgentToolInputSchema = z.preprocess((input) => {
|
|
2740
2958
|
if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
|
|
2741
2959
|
const record = input;
|
|
@@ -3043,7 +3261,7 @@ var EnterPlanModeTool = class {
|
|
|
3043
3261
|
content: `Failed to enter plan mode: ${error instanceof Error ? error.message : "Failed to enter plan mode."}`
|
|
3044
3262
|
};
|
|
3045
3263
|
}
|
|
3046
|
-
return { content:
|
|
3264
|
+
return { content: enteredPlanModeMessage(this.deps.getPlanFilePath?.() ?? null) };
|
|
3047
3265
|
}
|
|
3048
3266
|
if (this.deps.requestApproval === void 0) return {
|
|
3049
3267
|
isError: true,
|
|
@@ -3067,18 +3285,32 @@ var EnterPlanModeTool = class {
|
|
|
3067
3285
|
content: `Failed to enter plan mode: ${error instanceof Error ? error.message : "Failed to enter plan mode."}`
|
|
3068
3286
|
};
|
|
3069
3287
|
}
|
|
3070
|
-
return { content:
|
|
3288
|
+
return { content: enteredPlanModeMessage(this.deps.getPlanFilePath?.() ?? null) };
|
|
3071
3289
|
}
|
|
3072
3290
|
};
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3291
|
+
function enteredPlanModeMessage(planPath) {
|
|
3292
|
+
if (planPath === null) return [
|
|
3293
|
+
"Plan mode is now active. Your workflow:",
|
|
3294
|
+
"",
|
|
3295
|
+
"1. Use read-only tools (Read, Grep, Glob) to investigate the codebase.",
|
|
3296
|
+
"2. Design a concrete, step-by-step plan.",
|
|
3297
|
+
"3. When the plan is ready, call ExitPlanMode with the full plan text in the `plan` parameter.",
|
|
3298
|
+
"",
|
|
3299
|
+
"Do NOT use Write or Edit while plan mode is active in this host; no plan file path is available."
|
|
3300
|
+
].join("\n");
|
|
3301
|
+
return [
|
|
3302
|
+
"Plan mode is now active. Your workflow:",
|
|
3303
|
+
"",
|
|
3304
|
+
`Plan file: ${planPath}`,
|
|
3305
|
+
"",
|
|
3306
|
+
"1. Use read-only tools (Read, Grep, Glob) to investigate the codebase.",
|
|
3307
|
+
"2. Design a concrete, step-by-step plan.",
|
|
3308
|
+
"3. Write the plan to the plan file with Write or Edit.",
|
|
3309
|
+
"4. When the plan is ready, call ExitPlanMode for user approval.",
|
|
3310
|
+
"",
|
|
3311
|
+
"Do NOT edit files other than the plan file while plan mode is active."
|
|
3312
|
+
].join("\n");
|
|
3313
|
+
}
|
|
3082
3314
|
//#endregion
|
|
3083
3315
|
//#region ../../packages/kimi-core/src/tools/builtin/planning/exit-plan-mode.ts
|
|
3084
3316
|
/**
|
|
@@ -3109,7 +3341,7 @@ const ENTERED_PLAN_MODE_MESSAGE = [
|
|
|
3109
3341
|
* simulate a rejection.
|
|
3110
3342
|
*/
|
|
3111
3343
|
const ExitPlanModeInputSchema = z.object({
|
|
3112
|
-
plan: z.string().min(1).optional().describe("
|
|
3344
|
+
plan: z.string().min(1).optional().describe("Legacy inline fallback only. In the default CLI runtime, write the plan file first; the host reads that file and ignores this field."),
|
|
3113
3345
|
options: z.array(z.object({
|
|
3114
3346
|
label: z.string().min(1).max(80),
|
|
3115
3347
|
description: z.string()
|
|
@@ -3122,7 +3354,8 @@ const DESCRIPTION$4 = `Use this tool when you are in plan mode and are ready to
|
|
|
3122
3354
|
|
|
3123
3355
|
**Usage notes:**
|
|
3124
3356
|
- Only call this tool after you have fully investigated the request and designed a concrete plan.
|
|
3125
|
-
-
|
|
3357
|
+
- In the default CLI runtime, write the plan to the current plan file first; this tool reads the file and presents it for approval.
|
|
3358
|
+
- Only pass \`plan\` when running in a host that has no plan-file support.
|
|
3126
3359
|
- When presenting multiple approaches, pass 2-3 concrete options and do not use reserved labels like "Approve", "Reject", "Reject and Exit", or "Revise"; the host adds those controls.
|
|
3127
3360
|
- Do NOT call this tool to ask clarifying questions (use AskUserQuestion for that). Call ExitPlanMode only when the plan is genuinely final.`;
|
|
3128
3361
|
var ExitPlanModeTool = class {
|
|
@@ -3223,13 +3456,19 @@ var ExitPlanModeTool = class {
|
|
|
3223
3456
|
}
|
|
3224
3457
|
};
|
|
3225
3458
|
}
|
|
3226
|
-
if (source === null || source.plan.trim().length === 0)
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3459
|
+
if (source === null || source.plan.trim().length === 0) {
|
|
3460
|
+
if (args.plan !== void 0 && args.plan.trim().length > 0) return {
|
|
3461
|
+
ok: true,
|
|
3462
|
+
plan: args.plan
|
|
3463
|
+
};
|
|
3464
|
+
return {
|
|
3465
|
+
ok: false,
|
|
3466
|
+
error: {
|
|
3467
|
+
isError: true,
|
|
3468
|
+
content: source?.path !== void 0 ? `No plan file found. Write your plan to ${source.path} first, then call ExitPlanMode.` : "No plan file found. Write your plan first, then call ExitPlanMode."
|
|
3469
|
+
}
|
|
3470
|
+
};
|
|
3471
|
+
}
|
|
3233
3472
|
return {
|
|
3234
3473
|
ok: true,
|
|
3235
3474
|
plan: source.plan,
|
|
@@ -3253,6 +3492,44 @@ function formatPlanForOutput(plan, path) {
|
|
|
3253
3492
|
return `Plan mode deactivated. All tools are now available.\n${path !== void 0 ? `Plan saved to: ${path}\n\n` : ""}## Approved Plan:\n${plan}`;
|
|
3254
3493
|
}
|
|
3255
3494
|
//#endregion
|
|
3495
|
+
//#region ../../packages/kimi-core/src/soul-plus/approval/approval-runtime.ts
|
|
3496
|
+
/**
|
|
3497
|
+
* Thrown when a cross-process / team forwarding hook is invoked before
|
|
3498
|
+
* the TeamDaemon wiring lands (Slice 2.6+). Callers should treat this as
|
|
3499
|
+
* an unrecoverable misconfiguration in Slice 2.3.
|
|
3500
|
+
*/
|
|
3501
|
+
var NotImplementedError = class extends Error {
|
|
3502
|
+
constructor(feature) {
|
|
3503
|
+
super(`${feature} not implemented in Slice 2.3 (deferred to TeamDaemon)`);
|
|
3504
|
+
this.name = "NotImplementedError";
|
|
3505
|
+
}
|
|
3506
|
+
};
|
|
3507
|
+
/**
|
|
3508
|
+
* Default stub used when no wire/UI integration is required (e.g. unit
|
|
3509
|
+
* tests of unrelated layers, or embedder harnesses that bypass approvals
|
|
3510
|
+
* entirely via their own `beforeToolCall`). Every request is immediately
|
|
3511
|
+
* approved and no records are written.
|
|
3512
|
+
*
|
|
3513
|
+
* For the real implementation see `./wired-approval-runtime.ts`.
|
|
3514
|
+
*/
|
|
3515
|
+
var AlwaysAllowApprovalRuntime = class {
|
|
3516
|
+
async request(_req, _signal) {
|
|
3517
|
+
return {
|
|
3518
|
+
approved: true,
|
|
3519
|
+
response: "approved"
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
async recoverPendingOnStartup() {}
|
|
3523
|
+
resolve(_requestId, _response) {}
|
|
3524
|
+
cancelBySource(_source) {}
|
|
3525
|
+
async ingestRemoteRequest(_data) {
|
|
3526
|
+
throw new NotImplementedError("ApprovalRuntime.ingestRemoteRequest");
|
|
3527
|
+
}
|
|
3528
|
+
resolveRemote(_data) {
|
|
3529
|
+
throw new NotImplementedError("ApprovalRuntime.resolveRemote");
|
|
3530
|
+
}
|
|
3531
|
+
};
|
|
3532
|
+
//#endregion
|
|
3256
3533
|
//#region ../../packages/kimi-core/src/soul-plus/compaction/tokens.ts
|
|
3257
3534
|
/**
|
|
3258
3535
|
* Token-estimation helper used by `CompactionOrchestrator` to seed
|
|
@@ -3308,7 +3585,7 @@ var CompactionOrchestrator = class {
|
|
|
3308
3585
|
signal.throwIfAborted();
|
|
3309
3586
|
const messages = this.deps.contextState.buildMessages();
|
|
3310
3587
|
const preCompactTokens = this.deps.contextState.tokenCountWithPending;
|
|
3311
|
-
const summary = await this.deps.compactionProvider.run(messages, signal, customInstruction !== void 0 ? { userInstructions: customInstruction } : void 0);
|
|
3588
|
+
const summary = await (this.deps.runtimeSlot?.current().compactionProvider ?? this.deps.compactionProvider).run(messages, signal, customInstruction !== void 0 ? { userInstructions: customInstruction } : void 0);
|
|
3312
3589
|
signal.throwIfAborted();
|
|
3313
3590
|
const baselineInit = await this.deps.journalCapability.readSessionInitialized();
|
|
3314
3591
|
const runtime = this.deps.runtimeStateProvider();
|
|
@@ -3537,6 +3814,375 @@ var SoulLifecycleGate = class {
|
|
|
3537
3814
|
}
|
|
3538
3815
|
};
|
|
3539
3816
|
//#endregion
|
|
3817
|
+
//#region ../../packages/kimi-core/src/soul-plus/plan/plan-slugs.ts
|
|
3818
|
+
/**
|
|
3819
|
+
* Plan-file slug generator — Phase 18 §D.1.
|
|
3820
|
+
*
|
|
3821
|
+
* Ports Python `kimi_cli/tools/plan/heroes.py`: each session gets a
|
|
3822
|
+
* human-readable slug composed of three Marvel/DC hero names joined by
|
|
3823
|
+
* `-`. The slug is cached per `sessionId` so repeated lookups are
|
|
3824
|
+
* idempotent. When the randomly-assembled slug collides with the
|
|
3825
|
+
* caller-supplied `existingSlugs` set 20 times in a row, we append the
|
|
3826
|
+
* first 8 chars of `sessionId` as a tiebreaker (matches the Python
|
|
3827
|
+
* fallback path `{slug}-{session_id[:8]}`).
|
|
3828
|
+
*
|
|
3829
|
+
* Note: uses `crypto.randomInt` for uniform secure randomness, matching
|
|
3830
|
+
* Python `secrets.choice`.
|
|
3831
|
+
*/
|
|
3832
|
+
const HERO_NAMES = [
|
|
3833
|
+
"iron-man",
|
|
3834
|
+
"spider-man",
|
|
3835
|
+
"captain-america",
|
|
3836
|
+
"thor",
|
|
3837
|
+
"hulk",
|
|
3838
|
+
"black-widow",
|
|
3839
|
+
"hawkeye",
|
|
3840
|
+
"black-panther",
|
|
3841
|
+
"doctor-strange",
|
|
3842
|
+
"scarlet-witch",
|
|
3843
|
+
"vision",
|
|
3844
|
+
"falcon",
|
|
3845
|
+
"war-machine",
|
|
3846
|
+
"ant-man",
|
|
3847
|
+
"wasp",
|
|
3848
|
+
"captain-marvel",
|
|
3849
|
+
"gamora",
|
|
3850
|
+
"star-lord",
|
|
3851
|
+
"groot",
|
|
3852
|
+
"rocket",
|
|
3853
|
+
"drax",
|
|
3854
|
+
"mantis",
|
|
3855
|
+
"nebula",
|
|
3856
|
+
"shang-chi",
|
|
3857
|
+
"moon-knight",
|
|
3858
|
+
"ms-marvel",
|
|
3859
|
+
"she-hulk",
|
|
3860
|
+
"echo",
|
|
3861
|
+
"wolverine",
|
|
3862
|
+
"cyclops",
|
|
3863
|
+
"storm",
|
|
3864
|
+
"jean-grey",
|
|
3865
|
+
"rogue",
|
|
3866
|
+
"beast",
|
|
3867
|
+
"nightcrawler",
|
|
3868
|
+
"colossus",
|
|
3869
|
+
"shadowcat",
|
|
3870
|
+
"jubilee",
|
|
3871
|
+
"cable",
|
|
3872
|
+
"deadpool",
|
|
3873
|
+
"bishop",
|
|
3874
|
+
"magik",
|
|
3875
|
+
"iceman",
|
|
3876
|
+
"archangel",
|
|
3877
|
+
"psylocke",
|
|
3878
|
+
"dazzler",
|
|
3879
|
+
"forge",
|
|
3880
|
+
"havok",
|
|
3881
|
+
"polaris",
|
|
3882
|
+
"emma-frost",
|
|
3883
|
+
"namor",
|
|
3884
|
+
"silver-surfer",
|
|
3885
|
+
"adam-warlock",
|
|
3886
|
+
"nova",
|
|
3887
|
+
"quasar",
|
|
3888
|
+
"sentry",
|
|
3889
|
+
"blue-marvel",
|
|
3890
|
+
"spectrum",
|
|
3891
|
+
"squirrel-girl",
|
|
3892
|
+
"cloak",
|
|
3893
|
+
"dagger",
|
|
3894
|
+
"punisher",
|
|
3895
|
+
"elektra",
|
|
3896
|
+
"luke-cage",
|
|
3897
|
+
"iron-fist",
|
|
3898
|
+
"jessica-jones",
|
|
3899
|
+
"daredevil",
|
|
3900
|
+
"blade",
|
|
3901
|
+
"ghost-rider",
|
|
3902
|
+
"morbius",
|
|
3903
|
+
"venom",
|
|
3904
|
+
"carnage",
|
|
3905
|
+
"silk",
|
|
3906
|
+
"spider-gwen",
|
|
3907
|
+
"miles-morales",
|
|
3908
|
+
"america-chavez",
|
|
3909
|
+
"kate-bishop",
|
|
3910
|
+
"yelena-belova",
|
|
3911
|
+
"white-tiger",
|
|
3912
|
+
"moon-girl",
|
|
3913
|
+
"devil-dinosaur",
|
|
3914
|
+
"amadeus-cho",
|
|
3915
|
+
"riri-williams",
|
|
3916
|
+
"kamala-khan",
|
|
3917
|
+
"sam-alexander",
|
|
3918
|
+
"nova-prime",
|
|
3919
|
+
"medusa",
|
|
3920
|
+
"black-bolt",
|
|
3921
|
+
"crystal",
|
|
3922
|
+
"karnak",
|
|
3923
|
+
"gorgon",
|
|
3924
|
+
"lockjaw",
|
|
3925
|
+
"quake",
|
|
3926
|
+
"mockingbird",
|
|
3927
|
+
"bobbi-morse",
|
|
3928
|
+
"maria-hill",
|
|
3929
|
+
"nick-fury",
|
|
3930
|
+
"phil-coulson",
|
|
3931
|
+
"winter-soldier",
|
|
3932
|
+
"us-agent",
|
|
3933
|
+
"patriot",
|
|
3934
|
+
"speed",
|
|
3935
|
+
"wiccan",
|
|
3936
|
+
"hulkling",
|
|
3937
|
+
"stature",
|
|
3938
|
+
"yellowjacket",
|
|
3939
|
+
"tigra",
|
|
3940
|
+
"hellcat",
|
|
3941
|
+
"valkyrie",
|
|
3942
|
+
"sif",
|
|
3943
|
+
"beta-ray-bill",
|
|
3944
|
+
"hercules",
|
|
3945
|
+
"wonder-man",
|
|
3946
|
+
"taskmaster",
|
|
3947
|
+
"domino",
|
|
3948
|
+
"cannonball",
|
|
3949
|
+
"sunspot",
|
|
3950
|
+
"wolfsbane",
|
|
3951
|
+
"warpath",
|
|
3952
|
+
"multiple-man",
|
|
3953
|
+
"banshee",
|
|
3954
|
+
"siryn",
|
|
3955
|
+
"monet",
|
|
3956
|
+
"rictor",
|
|
3957
|
+
"shatterstar",
|
|
3958
|
+
"longshot",
|
|
3959
|
+
"daken",
|
|
3960
|
+
"x-23",
|
|
3961
|
+
"fantomex",
|
|
3962
|
+
"batman",
|
|
3963
|
+
"superman",
|
|
3964
|
+
"wonder-woman",
|
|
3965
|
+
"flash",
|
|
3966
|
+
"aquaman",
|
|
3967
|
+
"green-lantern",
|
|
3968
|
+
"martian-manhunter",
|
|
3969
|
+
"cyborg",
|
|
3970
|
+
"hawkgirl",
|
|
3971
|
+
"green-arrow",
|
|
3972
|
+
"black-canary",
|
|
3973
|
+
"zatanna",
|
|
3974
|
+
"constantine",
|
|
3975
|
+
"shazam",
|
|
3976
|
+
"blue-beetle",
|
|
3977
|
+
"booster-gold",
|
|
3978
|
+
"firestorm",
|
|
3979
|
+
"atom",
|
|
3980
|
+
"hawkman",
|
|
3981
|
+
"plastic-man",
|
|
3982
|
+
"red-tornado",
|
|
3983
|
+
"starfire",
|
|
3984
|
+
"raven",
|
|
3985
|
+
"beast-boy",
|
|
3986
|
+
"robin",
|
|
3987
|
+
"nightwing",
|
|
3988
|
+
"batgirl",
|
|
3989
|
+
"batwoman",
|
|
3990
|
+
"red-hood",
|
|
3991
|
+
"signal",
|
|
3992
|
+
"orphan",
|
|
3993
|
+
"spoiler",
|
|
3994
|
+
"catwoman",
|
|
3995
|
+
"huntress",
|
|
3996
|
+
"supergirl",
|
|
3997
|
+
"superboy",
|
|
3998
|
+
"power-girl",
|
|
3999
|
+
"steel",
|
|
4000
|
+
"stargirl",
|
|
4001
|
+
"wildcat",
|
|
4002
|
+
"doctor-fate",
|
|
4003
|
+
"mister-terrific",
|
|
4004
|
+
"hourman",
|
|
4005
|
+
"sandman",
|
|
4006
|
+
"spectre",
|
|
4007
|
+
"phantom-stranger",
|
|
4008
|
+
"swamp-thing",
|
|
4009
|
+
"animal-man",
|
|
4010
|
+
"deadman",
|
|
4011
|
+
"vixen",
|
|
4012
|
+
"black-lightning",
|
|
4013
|
+
"static",
|
|
4014
|
+
"icon",
|
|
4015
|
+
"rocket-dc",
|
|
4016
|
+
"captain-atom",
|
|
4017
|
+
"fire",
|
|
4018
|
+
"ice",
|
|
4019
|
+
"elongated-man",
|
|
4020
|
+
"metamorpho",
|
|
4021
|
+
"black-hawk",
|
|
4022
|
+
"crimson-avenger",
|
|
4023
|
+
"doctor-mid-nite",
|
|
4024
|
+
"jakeem-thunder",
|
|
4025
|
+
"mister-miracle",
|
|
4026
|
+
"big-barda",
|
|
4027
|
+
"orion",
|
|
4028
|
+
"lightray",
|
|
4029
|
+
"forager",
|
|
4030
|
+
"killer-frost",
|
|
4031
|
+
"jessica-cruz",
|
|
4032
|
+
"simon-baz",
|
|
4033
|
+
"john-stewart",
|
|
4034
|
+
"guy-gardner",
|
|
4035
|
+
"kyle-rayner",
|
|
4036
|
+
"hal-jordan",
|
|
4037
|
+
"wally-west",
|
|
4038
|
+
"barry-allen",
|
|
4039
|
+
"jay-garrick",
|
|
4040
|
+
"impulse",
|
|
4041
|
+
"kid-flash",
|
|
4042
|
+
"donna-troy",
|
|
4043
|
+
"tempest",
|
|
4044
|
+
"aqualad",
|
|
4045
|
+
"miss-martian",
|
|
4046
|
+
"terra",
|
|
4047
|
+
"jericho",
|
|
4048
|
+
"ravager",
|
|
4049
|
+
"red-star",
|
|
4050
|
+
"pantha",
|
|
4051
|
+
"argent",
|
|
4052
|
+
"damage",
|
|
4053
|
+
"jade",
|
|
4054
|
+
"obsidian",
|
|
4055
|
+
"cyclone",
|
|
4056
|
+
"atom-smasher",
|
|
4057
|
+
"maxima",
|
|
4058
|
+
"starman",
|
|
4059
|
+
"liberty-belle",
|
|
4060
|
+
"dove",
|
|
4061
|
+
"hawk",
|
|
4062
|
+
"blue-devil",
|
|
4063
|
+
"creeper",
|
|
4064
|
+
"ragman",
|
|
4065
|
+
"thunder"
|
|
4066
|
+
];
|
|
4067
|
+
const MAX_ATTEMPTS = 20;
|
|
4068
|
+
const slugCache = /* @__PURE__ */ new Map();
|
|
4069
|
+
function pickHero() {
|
|
4070
|
+
return HERO_NAMES[randomInt(HERO_NAMES.length)] ?? HERO_NAMES[0] ?? "kimi";
|
|
4071
|
+
}
|
|
4072
|
+
function assembleSlug() {
|
|
4073
|
+
return `${pickHero()}-${pickHero()}-${pickHero()}`;
|
|
4074
|
+
}
|
|
4075
|
+
/**
|
|
4076
|
+
* TODO(Slice 18-3): callers must scan `<KIMI_CODE_HOME>/plans/` for
|
|
4077
|
+
* previously-assigned slugs and pass them as `existingSlugs`. The
|
|
4078
|
+
* current call sites pass a literal empty `Set()` as a placeholder,
|
|
4079
|
+
* which means collision avoidance is only effective within a single
|
|
4080
|
+
* process (via `slugCache`) — two sessions that start after a process
|
|
4081
|
+
* restart can still collide. The plans directory scan is Slice 18-3
|
|
4082
|
+
* scope because it depends on PlanFileManager wiring in the host.
|
|
4083
|
+
*/
|
|
4084
|
+
function generatePlanSlug(sessionId, existingSlugs) {
|
|
4085
|
+
const cached = slugCache.get(sessionId);
|
|
4086
|
+
if (cached !== void 0) return cached;
|
|
4087
|
+
let slug = "";
|
|
4088
|
+
let collided = true;
|
|
4089
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
4090
|
+
slug = assembleSlug();
|
|
4091
|
+
if (!existingSlugs.has(slug)) {
|
|
4092
|
+
collided = false;
|
|
4093
|
+
break;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (collided) slug = `${slug}-${sessionId.slice(0, 8)}`;
|
|
4097
|
+
slugCache.set(sessionId, slug);
|
|
4098
|
+
return slug;
|
|
4099
|
+
}
|
|
4100
|
+
//#endregion
|
|
4101
|
+
//#region ../../packages/kimi-core/src/soul-plus/plan/plan-session-controller.ts
|
|
4102
|
+
/**
|
|
4103
|
+
* Session-scoped owner for the plan artifact.
|
|
4104
|
+
*
|
|
4105
|
+
* Plan mode is not just a boolean: it needs a stable markdown file under
|
|
4106
|
+
* `<KIMI_CODE_HOME>/plans/`, a persisted slug for crash/restart recovery,
|
|
4107
|
+
* and one write surface shared by ExitPlanMode, /plan, and Write/Edit gates.
|
|
4108
|
+
*/
|
|
4109
|
+
var PlanSessionController = class {
|
|
4110
|
+
constructor(deps) {
|
|
4111
|
+
this.deps = deps;
|
|
4112
|
+
}
|
|
4113
|
+
isAvailable() {
|
|
4114
|
+
return this.deps.paths !== void 0 && this.deps.sessionMeta !== void 0;
|
|
4115
|
+
}
|
|
4116
|
+
getPlanFilePath() {
|
|
4117
|
+
const paths = this.deps.paths;
|
|
4118
|
+
const slug = this.getActiveSlug();
|
|
4119
|
+
if (paths === void 0 || slug === null) return null;
|
|
4120
|
+
return join(paths.home, "plans", `${slug}.md`);
|
|
4121
|
+
}
|
|
4122
|
+
async ensurePlanFilePath(source = "system", reason = "plan mode activated") {
|
|
4123
|
+
const paths = this.deps.paths;
|
|
4124
|
+
const sessionMeta = this.deps.sessionMeta;
|
|
4125
|
+
if (paths === void 0 || sessionMeta === void 0) return null;
|
|
4126
|
+
const active = this.getActiveSlug();
|
|
4127
|
+
if (active !== null) return join(paths.home, "plans", `${active}.md`);
|
|
4128
|
+
const slug = generatePlanSlug(`${this.deps.sessionId}:${randomUUID()}`, await readExistingPlanSlugs(paths));
|
|
4129
|
+
await sessionMeta.setPlanSlug(slug, source, reason);
|
|
4130
|
+
return join(paths.home, "plans", `${slug}.md`);
|
|
4131
|
+
}
|
|
4132
|
+
async readCurrentPlan() {
|
|
4133
|
+
const path = this.getPlanFilePath();
|
|
4134
|
+
if (path === null) return null;
|
|
4135
|
+
try {
|
|
4136
|
+
return {
|
|
4137
|
+
plan: await readFile(path, "utf8"),
|
|
4138
|
+
path
|
|
4139
|
+
};
|
|
4140
|
+
} catch (error) {
|
|
4141
|
+
if (isEnoent(error)) return {
|
|
4142
|
+
plan: "",
|
|
4143
|
+
path
|
|
4144
|
+
};
|
|
4145
|
+
throw error;
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
async writeCurrentPlan(content) {
|
|
4149
|
+
const path = await this.ensurePlanFilePath();
|
|
4150
|
+
if (path === null) throw new Error("Plan file is unavailable for this session.");
|
|
4151
|
+
await mkdir(dirname(path), { recursive: true });
|
|
4152
|
+
await atomicWrite(path, content);
|
|
4153
|
+
return Buffer.byteLength(content, "utf8");
|
|
4154
|
+
}
|
|
4155
|
+
async clearCurrentPlan() {
|
|
4156
|
+
const path = this.getPlanFilePath();
|
|
4157
|
+
if (path === null) return null;
|
|
4158
|
+
await rm(path, { force: true });
|
|
4159
|
+
return path;
|
|
4160
|
+
}
|
|
4161
|
+
async deactivatePlanMode() {
|
|
4162
|
+
const sessionMeta = this.deps.sessionMeta;
|
|
4163
|
+
if (sessionMeta === void 0 || this.getActiveSlug() === null) return;
|
|
4164
|
+
await sessionMeta.clearPlanSlug("system", "plan mode deactivated");
|
|
4165
|
+
}
|
|
4166
|
+
getActiveSlug() {
|
|
4167
|
+
const raw = this.deps.sessionMeta?.get().plan_slug;
|
|
4168
|
+
if (typeof raw !== "string") return null;
|
|
4169
|
+
const trimmed = raw.trim();
|
|
4170
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
4171
|
+
}
|
|
4172
|
+
};
|
|
4173
|
+
async function readExistingPlanSlugs(paths) {
|
|
4174
|
+
try {
|
|
4175
|
+
const entries = await readdir(join(paths.home, "plans"), { withFileTypes: true });
|
|
4176
|
+
return new Set(entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => basename(entry.name, ".md")));
|
|
4177
|
+
} catch (error) {
|
|
4178
|
+
if (isEnoent(error)) return /* @__PURE__ */ new Set();
|
|
4179
|
+
throw error;
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
function isEnoent(error) {
|
|
4183
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
4184
|
+
}
|
|
4185
|
+
//#endregion
|
|
3540
4186
|
//#region ../../packages/kimi-core/src/utils/logger.ts
|
|
3541
4187
|
const noopLogger$3 = {
|
|
3542
4188
|
debug: () => {},
|
|
@@ -3802,10 +4448,12 @@ var DefaultSessionControl = class {
|
|
|
3802
4448
|
turnManager;
|
|
3803
4449
|
contextState;
|
|
3804
4450
|
sessionJournal;
|
|
4451
|
+
setPlanModeOverride;
|
|
3805
4452
|
constructor(deps) {
|
|
3806
4453
|
this.turnManager = deps.turnManager;
|
|
3807
4454
|
this.contextState = deps.contextState;
|
|
3808
4455
|
this.sessionJournal = deps.sessionJournal;
|
|
4456
|
+
this.setPlanModeOverride = deps.setPlanModeOverride;
|
|
3809
4457
|
}
|
|
3810
4458
|
async compact(customInstruction) {
|
|
3811
4459
|
await this.turnManager.triggerCompaction(customInstruction);
|
|
@@ -3819,6 +4467,10 @@ var DefaultSessionControl = class {
|
|
|
3819
4467
|
}
|
|
3820
4468
|
}
|
|
3821
4469
|
async setPlanMode(enabled) {
|
|
4470
|
+
if (this.setPlanModeOverride !== void 0) {
|
|
4471
|
+
await this.setPlanModeOverride(enabled);
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
3822
4474
|
await this.contextState.applyConfigChange({
|
|
3823
4475
|
type: "plan_mode_changed",
|
|
3824
4476
|
enabled
|
|
@@ -3893,6 +4545,10 @@ var SessionMetaService = class {
|
|
|
3893
4545
|
if (this.meta.plan_slug === trimmed) return;
|
|
3894
4546
|
await this.applyPatch({ plan_slug: trimmed }, source, reason);
|
|
3895
4547
|
};
|
|
4548
|
+
clearPlanSlug = async (source, reason) => {
|
|
4549
|
+
if (this.meta.plan_slug === void 0 || this.meta.plan_slug.length === 0) return;
|
|
4550
|
+
await this.applyPatch({ plan_slug: "" }, source, reason);
|
|
4551
|
+
};
|
|
3896
4552
|
/**
|
|
3897
4553
|
* Overwrite the in-memory view with replay-derived fields (dirty-exit
|
|
3898
4554
|
* path). Does not emit any event and does not write wire — the caller
|
|
@@ -4078,6 +4734,39 @@ function escapeXml(input) {
|
|
|
4078
4734
|
//#endregion
|
|
4079
4735
|
//#region ../../packages/kimi-core/src/soul-plus/errors.ts
|
|
4080
4736
|
/**
|
|
4737
|
+
* Business-error sentinel classes — Phase 18 A.11 / A.12 / A.13
|
|
4738
|
+
* (v2 §3.1 wire error code table).
|
|
4739
|
+
*
|
|
4740
|
+
* Lives in `src/` rather than the test harness so production paths
|
|
4741
|
+
* (KosongAdapter implementations, wire server) can throw these
|
|
4742
|
+
* directly; the wire layer maps them onto the canonical JSON-RPC
|
|
4743
|
+
* style error codes:
|
|
4744
|
+
*
|
|
4745
|
+
* -32001 LLM not configured (missing `default_model`)
|
|
4746
|
+
* -32002 LLM capability mismatch (image_in / video_in / audio_in
|
|
4747
|
+
* rejected by the selected model)
|
|
4748
|
+
* -32003 Provider-level failure (network, rate-limit, 5xx, etc.)
|
|
4749
|
+
*
|
|
4750
|
+
* These classes extend `Error` so `instanceof` works across module
|
|
4751
|
+
* boundaries; the wire mapper in `classifyBusinessError` drops back
|
|
4752
|
+
* to a `/provider|backend|upstream/i` string heuristic when callers
|
|
4753
|
+
* throw vanilla `Error`s (e.g. fixture adapters that haven't been
|
|
4754
|
+
* migrated to `ProviderError`).
|
|
4755
|
+
*/
|
|
4756
|
+
/**
|
|
4757
|
+
* Phase 18 A.11 — no default LLM configured. Surfaced when a
|
|
4758
|
+
* session is created without a `model` AND config has no
|
|
4759
|
+
* `default_model` fallback. Host code can throw this at any point
|
|
4760
|
+
* the missing-model is observed; the wire layer maps it to code
|
|
4761
|
+
* -32001.
|
|
4762
|
+
*/
|
|
4763
|
+
var LLMNotSetError = class extends Error {
|
|
4764
|
+
constructor(message = "No LLM configured") {
|
|
4765
|
+
super(message);
|
|
4766
|
+
this.name = "LLMNotSetError";
|
|
4767
|
+
}
|
|
4768
|
+
};
|
|
4769
|
+
/**
|
|
4081
4770
|
* Phase 18 A.12 — the selected LLM does not accept the input
|
|
4082
4771
|
* modality the client supplied (image / video / audio). Thrown by
|
|
4083
4772
|
* the wire prompt handler before the turn is armed so the client
|
|
@@ -4503,7 +5192,7 @@ var StubChildLifecycle = class {
|
|
|
4503
5192
|
* `SoulRegistry.runSubagentTurn`.
|
|
4504
5193
|
*/
|
|
4505
5194
|
async function runSubagentTurn(deps, agentId, request, signal) {
|
|
4506
|
-
const { store, typeRegistry, parentTools, parentRuntime, parentEventBus, parentSessionJournal, parentModel, sessionDir, pathConfig, sessionId, parentSessionId, toolExecutionScopeFactory } = deps;
|
|
5195
|
+
const { store, typeRegistry, parentTools, parentRuntime, runtimeSlot, parentEventBus, parentSessionJournal, parentModel, sessionDir, pathConfig, sessionId, parentSessionId, toolExecutionScopeFactory } = deps;
|
|
4507
5196
|
const typeDef = typeRegistry.resolve(request.agentName);
|
|
4508
5197
|
const childTools = typeRegistry.resolveToolSet(request.agentName, parentTools);
|
|
4509
5198
|
await store.createInstance({
|
|
@@ -4588,7 +5277,7 @@ async function runSubagentTurn(deps, agentId, request, signal) {
|
|
|
4588
5277
|
...childSystemPrompt !== void 0 ? { initialSystemPrompt: childSystemPrompt } : {}
|
|
4589
5278
|
});
|
|
4590
5279
|
const childTurnId = `${agentId}_turn`;
|
|
4591
|
-
const baseChildRuntime = { kosong: parentRuntime.kosong };
|
|
5280
|
+
const baseChildRuntime = { kosong: runtimeSlot?.current().runtime.kosong ?? parentRuntime.kosong };
|
|
4592
5281
|
const contentCollector = [];
|
|
4593
5282
|
let baseSink;
|
|
4594
5283
|
if (parentEventBus !== void 0 && childJournalWriter !== void 0) {
|
|
@@ -5353,6 +6042,138 @@ function buildBeforeToolCall(options) {
|
|
|
5353
6042
|
};
|
|
5354
6043
|
}
|
|
5355
6044
|
//#endregion
|
|
6045
|
+
//#region ../../packages/kimi-core/src/soul-plus/plan/plan-mode-checker.ts
|
|
6046
|
+
/**
|
|
6047
|
+
* Shared error message surface used by Write / Edit so the LLM always
|
|
6048
|
+
* sees a consistent "here's the exit" hint regardless of which tool
|
|
6049
|
+
* tripped the block.
|
|
6050
|
+
*/
|
|
6051
|
+
function planModeWriteBlockMessage(planPath) {
|
|
6052
|
+
return `Plan mode is active. You may only write to the current plan file: ${planPath ?? "(no plan file selected yet)"}. Call ExitPlanMode to exit plan mode before editing other files.`;
|
|
6053
|
+
}
|
|
6054
|
+
/**
|
|
6055
|
+
* Shared error message surface for Bash mutation blocks.
|
|
6056
|
+
*/
|
|
6057
|
+
function planModeBashBlockMessage() {
|
|
6058
|
+
return "Plan mode is active. This command would mutate the filesystem; plan mode forbids that. Call ExitPlanMode to exit plan mode before running mutation commands.";
|
|
6059
|
+
}
|
|
6060
|
+
/**
|
|
6061
|
+
* Classify a shell command as "mutation" vs read-only. Used exclusively
|
|
6062
|
+
* by the plan-mode hard block — the detector is conservative: the tests
|
|
6063
|
+
* pin seven concrete cases (rm, redirect `>`, `>>`, sed -i, git commit,
|
|
6064
|
+
* plus `ls` / `cat` that must NOT trip the gate). The detector leans
|
|
6065
|
+
* toward under-blocking (unknown-first-word = allow) so plan-mode
|
|
6066
|
+
* doesn't interfere with legitimate read-only explorations.
|
|
6067
|
+
*
|
|
6068
|
+
* This deliberately does NOT attempt to be a full shell parser. Known
|
|
6069
|
+
* and accepted false positives:
|
|
6070
|
+
* - Quoted strings containing `>` are blocked (e.g.
|
|
6071
|
+
* `echo "hello > world"`) because the regex does not track quote
|
|
6072
|
+
* state. Plan-mode callers can work around this by avoiding
|
|
6073
|
+
* redirect-like substrings in quoted output.
|
|
6074
|
+
* - `&>` / `&>>` (bash stderr+stdout combined redirect) is *not*
|
|
6075
|
+
* detected; those are legitimate file writes but the policy's
|
|
6076
|
+
* 7-case test pin does not cover them.
|
|
6077
|
+
*
|
|
6078
|
+
* Stricter parsing is out of scope for Phase 18; hosts that need
|
|
6079
|
+
* tighter policy should wrap or replace this helper.
|
|
6080
|
+
*/
|
|
6081
|
+
function isMutatingBashCommand(command) {
|
|
6082
|
+
const trimmed = command.trim();
|
|
6083
|
+
if (trimmed.length === 0) return false;
|
|
6084
|
+
if (/(^|[^&0-9])>{1,2}\s*(?!&)\S/.test(trimmed)) return true;
|
|
6085
|
+
const tokens = trimmed.split(/\s+/);
|
|
6086
|
+
const firstToken = tokens[0] ?? "";
|
|
6087
|
+
if (new Set([
|
|
6088
|
+
"rm",
|
|
6089
|
+
"rmdir",
|
|
6090
|
+
"mv",
|
|
6091
|
+
"cp",
|
|
6092
|
+
"mkdir",
|
|
6093
|
+
"touch",
|
|
6094
|
+
"chmod",
|
|
6095
|
+
"chown",
|
|
6096
|
+
"ln",
|
|
6097
|
+
"tee",
|
|
6098
|
+
"dd",
|
|
6099
|
+
"truncate",
|
|
6100
|
+
"shred",
|
|
6101
|
+
"patch"
|
|
6102
|
+
]).has(firstToken)) return true;
|
|
6103
|
+
if (firstToken === "sed" && (/\s-i(\b|\s|=)/.test(` ${trimmed}`) || trimmed.includes("--in-place"))) return true;
|
|
6104
|
+
if (firstToken === "git") {
|
|
6105
|
+
const gitMutating = new Set([
|
|
6106
|
+
"commit",
|
|
6107
|
+
"push",
|
|
6108
|
+
"reset",
|
|
6109
|
+
"checkout",
|
|
6110
|
+
"rebase",
|
|
6111
|
+
"merge",
|
|
6112
|
+
"tag",
|
|
6113
|
+
"stash",
|
|
6114
|
+
"cherry-pick",
|
|
6115
|
+
"revert",
|
|
6116
|
+
"pull",
|
|
6117
|
+
"add",
|
|
6118
|
+
"rm",
|
|
6119
|
+
"mv",
|
|
6120
|
+
"clone",
|
|
6121
|
+
"init",
|
|
6122
|
+
"restore",
|
|
6123
|
+
"apply",
|
|
6124
|
+
"clean",
|
|
6125
|
+
"gc",
|
|
6126
|
+
"prune"
|
|
6127
|
+
]);
|
|
6128
|
+
const sub = tokens[1];
|
|
6129
|
+
if (sub !== void 0 && gitMutating.has(sub)) return true;
|
|
6130
|
+
if (sub === "branch" && /\s-[dD](\b|\s)/.test(trimmed)) return true;
|
|
6131
|
+
}
|
|
6132
|
+
if (new Set([
|
|
6133
|
+
"npm",
|
|
6134
|
+
"yarn",
|
|
6135
|
+
"pnpm",
|
|
6136
|
+
"pip",
|
|
6137
|
+
"pip3",
|
|
6138
|
+
"poetry",
|
|
6139
|
+
"cargo",
|
|
6140
|
+
"gem",
|
|
6141
|
+
"uv"
|
|
6142
|
+
]).has(firstToken)) {
|
|
6143
|
+
const sub = tokens[1] ?? "";
|
|
6144
|
+
if (new Set([
|
|
6145
|
+
"install",
|
|
6146
|
+
"i",
|
|
6147
|
+
"add",
|
|
6148
|
+
"remove",
|
|
6149
|
+
"rm",
|
|
6150
|
+
"uninstall",
|
|
6151
|
+
"update",
|
|
6152
|
+
"upgrade",
|
|
6153
|
+
"publish",
|
|
6154
|
+
"link",
|
|
6155
|
+
"unlink"
|
|
6156
|
+
]).has(sub)) return true;
|
|
6157
|
+
}
|
|
6158
|
+
return false;
|
|
6159
|
+
}
|
|
6160
|
+
//#endregion
|
|
6161
|
+
//#region ../../packages/kimi-core/src/soul-plus/plan/plan-tool-call-policy.ts
|
|
6162
|
+
function inspectPlanModeToolCall(toolName, args, planMode) {
|
|
6163
|
+
if (planMode === void 0 || !planMode.isActive()) return { kind: "none" };
|
|
6164
|
+
if (toolName !== "Write" && toolName !== "Edit") return { kind: "none" };
|
|
6165
|
+
if (!isRecord$9(args) || typeof args["path"] !== "string") return { kind: "none" };
|
|
6166
|
+
const planPath = planMode.controller.getPlanFilePath();
|
|
6167
|
+
if (planPath !== null && resolve(args["path"]) === resolve(planPath)) return { kind: "allow_plan_file" };
|
|
6168
|
+
return {
|
|
6169
|
+
kind: "block",
|
|
6170
|
+
reason: planModeWriteBlockMessage(planPath)
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
6173
|
+
function isRecord$9(value) {
|
|
6174
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6175
|
+
}
|
|
6176
|
+
//#endregion
|
|
5356
6177
|
//#region ../../packages/kimi-core/src/soul-plus/tool-call/use-case.ts
|
|
5357
6178
|
var DefaultToolCallUseCase = class {
|
|
5358
6179
|
constructor(deps) {
|
|
@@ -5384,8 +6205,15 @@ var DefaultToolCallUseCase = class {
|
|
|
5384
6205
|
block: true,
|
|
5385
6206
|
reason: hookResult.reason
|
|
5386
6207
|
};
|
|
5387
|
-
const
|
|
5388
|
-
if (
|
|
6208
|
+
const planModeDecision = inspectPlanModeToolCall(btcCtx.toolCall.name, btcCtx.args, this.deps.planMode);
|
|
6209
|
+
if (planModeDecision.kind === "block") return {
|
|
6210
|
+
block: true,
|
|
6211
|
+
reason: planModeDecision.reason
|
|
6212
|
+
};
|
|
6213
|
+
if (planModeDecision.kind !== "allow_plan_file") {
|
|
6214
|
+
const permissionResult = await permissionClosure(btcCtx, signal);
|
|
6215
|
+
if (permissionResult?.block === true) return permissionResult;
|
|
6216
|
+
}
|
|
5389
6217
|
const display = context.toolsByName?.get(btcCtx.toolCall.name)?.display;
|
|
5390
6218
|
const activityDescription = display?.getActivityDescription?.(btcCtx.args);
|
|
5391
6219
|
const userFacingName = display?.getUserFacingName?.(btcCtx.args);
|
|
@@ -5404,7 +6232,6 @@ var DefaultToolCallUseCase = class {
|
|
|
5404
6232
|
}
|
|
5405
6233
|
});
|
|
5406
6234
|
if (wireUuid !== void 0) btcCtx.toolCallByProviderId?.set(btcCtx.toolCall.id, wireUuid);
|
|
5407
|
-
return permissionResult;
|
|
5408
6235
|
};
|
|
5409
6236
|
}
|
|
5410
6237
|
buildAfterToolCall(context, toolsByName) {
|
|
@@ -5528,10 +6355,12 @@ var DefaultToolExecutionScopeFactory = class {
|
|
|
5528
6355
|
const transcript = new ContextStateToolTranscriptRecorder();
|
|
5529
6356
|
const resultPolicy = this.deps.resultPolicy ?? new ArchiveToolResultPolicy();
|
|
5530
6357
|
const streaming = new ToolStreamingPrefetchScope();
|
|
6358
|
+
const planMode = this.deps.planMode;
|
|
5531
6359
|
const useCase = new DefaultToolCallUseCase({
|
|
5532
6360
|
policy: this.deps.policy,
|
|
5533
6361
|
transcript,
|
|
5534
6362
|
resultPolicy,
|
|
6363
|
+
...planMode !== void 0 ? { planMode } : {},
|
|
5535
6364
|
consumeToolOutcome: (toolCallId) => {
|
|
5536
6365
|
const outcome = outcomes.get(toolCallId);
|
|
5537
6366
|
outcomes.delete(toolCallId);
|
|
@@ -5545,6 +6374,8 @@ var DefaultToolExecutionScopeFactory = class {
|
|
|
5545
6374
|
inputSchema: inner.inputSchema,
|
|
5546
6375
|
async execute(toolCallId, args, signal, onUpdate, ctx) {
|
|
5547
6376
|
try {
|
|
6377
|
+
const planModeResult = await maybeExecutePlanFileTool(inner.name, args, planMode);
|
|
6378
|
+
if (planModeResult !== void 0) return planModeResult;
|
|
5548
6379
|
return await inner.execute(toolCallId, args, signal, onUpdate, ctx);
|
|
5549
6380
|
} catch (error) {
|
|
5550
6381
|
if (isAbortLike(error, signal)) outcomes.set(toolCallId, { kind: "abort" });
|
|
@@ -5600,6 +6431,19 @@ var DefaultToolExecutionScopeFactory = class {
|
|
|
5600
6431
|
};
|
|
5601
6432
|
}
|
|
5602
6433
|
};
|
|
6434
|
+
function createPlanModeOnlyToolExecutionScopeFactory(deps) {
|
|
6435
|
+
return { create(actor) {
|
|
6436
|
+
return {
|
|
6437
|
+
actor,
|
|
6438
|
+
buildSoulConfig({ tools }) {
|
|
6439
|
+
return { tools: tools.map((tool) => wrapPlanModeTool(tool, deps.planMode)) };
|
|
6440
|
+
},
|
|
6441
|
+
wrapRuntime(runtime) {
|
|
6442
|
+
return runtime;
|
|
6443
|
+
}
|
|
6444
|
+
};
|
|
6445
|
+
} };
|
|
6446
|
+
}
|
|
5603
6447
|
/**
|
|
5604
6448
|
* Explicit test/embedder escape hatch matching the historical
|
|
5605
6449
|
* `runSubagentTurn` behavior: no hooks, no approval, no permission rule walk,
|
|
@@ -5635,6 +6479,109 @@ function createWalOnlyToolExecutionScopeFactory() {
|
|
|
5635
6479
|
};
|
|
5636
6480
|
} };
|
|
5637
6481
|
}
|
|
6482
|
+
function wrapPlanModeTool(inner, planMode) {
|
|
6483
|
+
const wrapped = {
|
|
6484
|
+
name: inner.name,
|
|
6485
|
+
description: inner.description,
|
|
6486
|
+
inputSchema: inner.inputSchema,
|
|
6487
|
+
async execute(toolCallId, args, signal, onUpdate, ctx) {
|
|
6488
|
+
const planModeResult = await maybeExecutePlanFileTool(inner.name, args, planMode);
|
|
6489
|
+
if (planModeResult !== void 0) return planModeResult;
|
|
6490
|
+
return inner.execute(toolCallId, args, signal, onUpdate, ctx);
|
|
6491
|
+
}
|
|
6492
|
+
};
|
|
6493
|
+
copyToolMetadata(inner, wrapped);
|
|
6494
|
+
return wrapped;
|
|
6495
|
+
}
|
|
6496
|
+
function copyToolMetadata(inner, wrapped) {
|
|
6497
|
+
if (inner.maxResultSizeChars !== void 0) wrapped.maxResultSizeChars = inner.maxResultSizeChars;
|
|
6498
|
+
if (inner.display !== void 0) wrapped.display = inner.display;
|
|
6499
|
+
if (inner.isConcurrencySafe !== void 0) wrapped.isConcurrencySafe = inner.isConcurrencySafe.bind(inner);
|
|
6500
|
+
}
|
|
6501
|
+
async function maybeExecutePlanFileTool(toolName, args, planMode) {
|
|
6502
|
+
if (!isRecord$8(args) || typeof args["path"] !== "string") return void 0;
|
|
6503
|
+
const decision = inspectPlanModeToolCall(toolName, args, planMode);
|
|
6504
|
+
if (decision.kind === "none") return void 0;
|
|
6505
|
+
if (decision.kind === "block") return {
|
|
6506
|
+
isError: true,
|
|
6507
|
+
content: decision.reason
|
|
6508
|
+
};
|
|
6509
|
+
if (planMode === void 0) return void 0;
|
|
6510
|
+
const inputPath = args["path"];
|
|
6511
|
+
if (toolName === "Write") {
|
|
6512
|
+
const content = args["content"];
|
|
6513
|
+
if (typeof content !== "string") return void 0;
|
|
6514
|
+
const bytesWritten = await planMode.controller.writeCurrentPlan(content);
|
|
6515
|
+
return {
|
|
6516
|
+
content: `Wrote ${String(bytesWritten)} bytes to ${inputPath}`,
|
|
6517
|
+
output: { bytesWritten }
|
|
6518
|
+
};
|
|
6519
|
+
}
|
|
6520
|
+
const oldString = args["old_string"];
|
|
6521
|
+
const newString = args["new_string"];
|
|
6522
|
+
if (typeof oldString !== "string" || typeof newString !== "string") return void 0;
|
|
6523
|
+
const current = await planMode.controller.readCurrentPlan();
|
|
6524
|
+
if (current === null || current.plan.length === 0) return {
|
|
6525
|
+
isError: true,
|
|
6526
|
+
content: "The current plan file does not exist yet. Use Write to create it before calling Edit."
|
|
6527
|
+
};
|
|
6528
|
+
const replaceAll = args["replace_all"] === true;
|
|
6529
|
+
const replacement = replacePlanText(current.plan, oldString, newString, replaceAll, inputPath);
|
|
6530
|
+
if (!replacement.ok) return replacement.error;
|
|
6531
|
+
await planMode.controller.writeCurrentPlan(replacement.content);
|
|
6532
|
+
return {
|
|
6533
|
+
content: `Replaced ${String(replacement.replacementCount)} occurrence${replacement.replacementCount === 1 ? "" : "s"} in ${inputPath}`,
|
|
6534
|
+
output: { replacementCount: replacement.replacementCount }
|
|
6535
|
+
};
|
|
6536
|
+
}
|
|
6537
|
+
function replacePlanText(content, oldString, newString, replaceAll, path) {
|
|
6538
|
+
if (!replaceAll) {
|
|
6539
|
+
let count = 0;
|
|
6540
|
+
let pos = 0;
|
|
6541
|
+
while (pos < content.length) {
|
|
6542
|
+
const idx = content.indexOf(oldString, pos);
|
|
6543
|
+
if (idx === -1) break;
|
|
6544
|
+
count++;
|
|
6545
|
+
pos = idx + oldString.length;
|
|
6546
|
+
}
|
|
6547
|
+
if (count === 0) return {
|
|
6548
|
+
ok: false,
|
|
6549
|
+
error: {
|
|
6550
|
+
isError: true,
|
|
6551
|
+
content: `old_string not found in ${path}`
|
|
6552
|
+
}
|
|
6553
|
+
};
|
|
6554
|
+
if (count > 1) return {
|
|
6555
|
+
ok: false,
|
|
6556
|
+
error: {
|
|
6557
|
+
isError: true,
|
|
6558
|
+
content: `old_string is not unique in ${path} (found ${String(count)} occurrences)`
|
|
6559
|
+
}
|
|
6560
|
+
};
|
|
6561
|
+
return {
|
|
6562
|
+
ok: true,
|
|
6563
|
+
content: content.replace(oldString, newString),
|
|
6564
|
+
replacementCount: 1
|
|
6565
|
+
};
|
|
6566
|
+
}
|
|
6567
|
+
const parts = content.split(oldString);
|
|
6568
|
+
const replacementCount = parts.length - 1;
|
|
6569
|
+
if (replacementCount === 0) return {
|
|
6570
|
+
ok: false,
|
|
6571
|
+
error: {
|
|
6572
|
+
isError: true,
|
|
6573
|
+
content: `old_string not found in ${path}`
|
|
6574
|
+
}
|
|
6575
|
+
};
|
|
6576
|
+
return {
|
|
6577
|
+
ok: true,
|
|
6578
|
+
content: parts.join(newString),
|
|
6579
|
+
replacementCount
|
|
6580
|
+
};
|
|
6581
|
+
}
|
|
6582
|
+
function isRecord$8(value) {
|
|
6583
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6584
|
+
}
|
|
5638
6585
|
function isAbortLike(error, signal) {
|
|
5639
6586
|
if (signal.aborted) return true;
|
|
5640
6587
|
if (error instanceof Error && error.name === "AbortError") return true;
|
|
@@ -5788,6 +6735,7 @@ function withPlanFileFooter(body, planFilePath) {
|
|
|
5788
6735
|
return `${body}\n\nPlan file: ${planFilePath}`;
|
|
5789
6736
|
}
|
|
5790
6737
|
function fullReminder(planFilePath) {
|
|
6738
|
+
if (planFilePath === void 0 || planFilePath.length === 0) return inlineFullReminder();
|
|
5791
6739
|
return withPlanFileFooter([
|
|
5792
6740
|
"Plan mode is active. You MUST NOT make any edits (with the exception of the current plan file), run non-readonly tools, or otherwise make changes to the system. This supersedes any other instructions you have received.",
|
|
5793
6741
|
"",
|
|
@@ -5811,6 +6759,7 @@ function fullReminder(planFilePath) {
|
|
|
5811
6759
|
].join("\n"), planFilePath);
|
|
5812
6760
|
}
|
|
5813
6761
|
function sparseReminder(planFilePath) {
|
|
6762
|
+
if (planFilePath === void 0 || planFilePath.length === 0) return inlineSparseReminder();
|
|
5814
6763
|
return withPlanFileFooter([
|
|
5815
6764
|
"Plan mode still active (see full instructions earlier).",
|
|
5816
6765
|
"Read-only except the current plan file.",
|
|
@@ -5822,6 +6771,7 @@ function sparseReminder(planFilePath) {
|
|
|
5822
6771
|
].join(" "), planFilePath);
|
|
5823
6772
|
}
|
|
5824
6773
|
function reentryReminder(planFilePath) {
|
|
6774
|
+
if (planFilePath === void 0 || planFilePath.length === 0) return inlineReentryReminder();
|
|
5825
6775
|
return withPlanFileFooter([
|
|
5826
6776
|
"Plan mode is active. You MUST NOT make any edits (with the exception of the current plan file), run non-readonly tools, or otherwise make changes to the system. This supersedes any other instructions you have received.",
|
|
5827
6777
|
"",
|
|
@@ -5838,6 +6788,50 @@ function reentryReminder(planFilePath) {
|
|
|
5838
6788
|
"Your turn must end with either AskUserQuestion (to clarify requirements) or ExitPlanMode (to request plan approval)."
|
|
5839
6789
|
].join("\n"), planFilePath);
|
|
5840
6790
|
}
|
|
6791
|
+
function inlineFullReminder() {
|
|
6792
|
+
return [
|
|
6793
|
+
"Plan mode is active. You MUST NOT make any edits, run non-readonly tools, or otherwise make changes to the system. This supersedes any other instructions you have received.",
|
|
6794
|
+
"",
|
|
6795
|
+
"Workflow:",
|
|
6796
|
+
" 1. Understand — explore the codebase with Glob, Grep, Read.",
|
|
6797
|
+
" 2. Design — converge on the best approach; consider trade-offs but aim for a single recommendation.",
|
|
6798
|
+
" 3. Review — re-read key files to verify understanding.",
|
|
6799
|
+
" 4. Exit — call ExitPlanMode with the complete plan text in the `plan` parameter.",
|
|
6800
|
+
"",
|
|
6801
|
+
"## Handling multiple approaches",
|
|
6802
|
+
"Keep it focused: at most 2-3 meaningfully different approaches. Do NOT pad with minor variations — if one approach is clearly superior, just propose that one.",
|
|
6803
|
+
"When the best approach depends on user preferences, constraints, or context you don't have, use AskUserQuestion to clarify first.",
|
|
6804
|
+
"When you do include multiple approaches in the plan, you MUST pass them as the `options` parameter when calling ExitPlanMode, so the user can select which approach to execute at approval time.",
|
|
6805
|
+
"",
|
|
6806
|
+
"AskUserQuestion is for clarifying missing requirements or user preferences that affect the plan.",
|
|
6807
|
+
"Never ask about plan approval via text or AskUserQuestion.",
|
|
6808
|
+
"Your turn must end with either AskUserQuestion (to clarify requirements or preferences) or ExitPlanMode (to request plan approval). Do NOT end your turn any other way."
|
|
6809
|
+
].join("\n");
|
|
6810
|
+
}
|
|
6811
|
+
function inlineSparseReminder() {
|
|
6812
|
+
return [
|
|
6813
|
+
"Plan mode still active (see full instructions earlier).",
|
|
6814
|
+
"Read-only; no plan file path is available in this host.",
|
|
6815
|
+
"Call ExitPlanMode with the complete plan text in the `plan` parameter when ready.",
|
|
6816
|
+
"Use AskUserQuestion to clarify user preferences when it helps you write a better plan.",
|
|
6817
|
+
"If the plan has multiple approaches, pass options to ExitPlanMode so the user can choose.",
|
|
6818
|
+
"End turns with AskUserQuestion (for clarifications) or ExitPlanMode (for approval)."
|
|
6819
|
+
].join(" ");
|
|
6820
|
+
}
|
|
6821
|
+
function inlineReentryReminder() {
|
|
6822
|
+
return [
|
|
6823
|
+
"Plan mode is active. You MUST NOT make any edits, run non-readonly tools, or otherwise make changes to the system. This supersedes any other instructions you have received.",
|
|
6824
|
+
"",
|
|
6825
|
+
"## Re-entering Plan Mode",
|
|
6826
|
+
"No plan file path is available in this host.",
|
|
6827
|
+
"Before proceeding:",
|
|
6828
|
+
" 1. Re-evaluate the user request and any existing conversation context.",
|
|
6829
|
+
" 2. Use AskUserQuestion to clarify missing requirements or user preferences that affect the plan.",
|
|
6830
|
+
" 3. Call ExitPlanMode with the complete revised plan text in the `plan` parameter.",
|
|
6831
|
+
"",
|
|
6832
|
+
"Your turn must end with either AskUserQuestion (to clarify requirements) or ExitPlanMode (to request plan approval)."
|
|
6833
|
+
].join("\n");
|
|
6834
|
+
}
|
|
5841
6835
|
/**
|
|
5842
6836
|
* Yolo-mode reminder ported from Python `yolo_mode.py`. One-shot per
|
|
5843
6837
|
* activation: the first turn after `permissionMode` flips to
|
|
@@ -18949,6 +19943,9 @@ var TurnManager = class {
|
|
|
18949
19943
|
pendingTurnOverrides;
|
|
18950
19944
|
planMode;
|
|
18951
19945
|
thinkingLevel;
|
|
19946
|
+
runtime;
|
|
19947
|
+
runtimeSlot;
|
|
19948
|
+
compactionConfig;
|
|
18952
19949
|
sessionId;
|
|
18953
19950
|
activeToolExecutionScope;
|
|
18954
19951
|
/**
|
|
@@ -18974,6 +19971,9 @@ var TurnManager = class {
|
|
|
18974
19971
|
this.sessionRules = [...deps.sessionRules ?? []];
|
|
18975
19972
|
this.permissionMode = deps.permissionMode ?? "default";
|
|
18976
19973
|
this.planMode = deps.planMode ?? false;
|
|
19974
|
+
this.runtime = deps.runtime;
|
|
19975
|
+
this.runtimeSlot = deps.runtimeSlot;
|
|
19976
|
+
this.compactionConfig = deps.compactionConfig;
|
|
18977
19977
|
this.sessionId = deps.sessionId ?? "unknown";
|
|
18978
19978
|
}
|
|
18979
19979
|
setPermissionMode(mode) {
|
|
@@ -18998,6 +19998,26 @@ var TurnManager = class {
|
|
|
18998
19998
|
getThinkingLevel() {
|
|
18999
19999
|
return this.thinkingLevel;
|
|
19000
20000
|
}
|
|
20001
|
+
setCompactionConfig(config) {
|
|
20002
|
+
if (this.runtimeSlot !== void 0) {
|
|
20003
|
+
const current = this.runtimeSlot.current();
|
|
20004
|
+
this.runtimeSlot.replace({
|
|
20005
|
+
...current,
|
|
20006
|
+
...config !== void 0 ? { compactionConfig: config } : { compactionConfig: void 0 }
|
|
20007
|
+
});
|
|
20008
|
+
}
|
|
20009
|
+
this.compactionConfig = config;
|
|
20010
|
+
}
|
|
20011
|
+
setRuntime(runtime) {
|
|
20012
|
+
if (this.runtimeSlot !== void 0) {
|
|
20013
|
+
const current = this.runtimeSlot.current();
|
|
20014
|
+
this.runtimeSlot.replace({
|
|
20015
|
+
...current,
|
|
20016
|
+
runtime
|
|
20017
|
+
});
|
|
20018
|
+
}
|
|
20019
|
+
this.runtime = runtime;
|
|
20020
|
+
}
|
|
19001
20021
|
getAgentId() {
|
|
19002
20022
|
return this.agentId;
|
|
19003
20023
|
}
|
|
@@ -19145,7 +20165,7 @@ var TurnManager = class {
|
|
|
19145
20165
|
async handlePrompt(req) {
|
|
19146
20166
|
if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return { error: "agent_busy" };
|
|
19147
20167
|
const input = req.data.input;
|
|
19148
|
-
const capability = this.
|
|
20168
|
+
const capability = ((this.runtimeSlot?.current())?.runtime ?? this.runtime).kosong.getCapability?.(this.deps.contextState.model);
|
|
19149
20169
|
if (capability !== void 0 && capability !== UNKNOWN_CAPABILITY$1) {
|
|
19150
20170
|
let inputContainsImage = false;
|
|
19151
20171
|
let inputContainsVideo = false;
|
|
@@ -19212,16 +20232,28 @@ var TurnManager = class {
|
|
|
19212
20232
|
this.deps.contextState.pushSteer(req.data.input);
|
|
19213
20233
|
return { ok: true };
|
|
19214
20234
|
}
|
|
19215
|
-
drainDynamicInjectionsIntoContext(turnId) {
|
|
20235
|
+
async drainDynamicInjectionsIntoContext(turnId) {
|
|
19216
20236
|
const manager = this.deps.dynamicInjectionManager;
|
|
19217
20237
|
if (manager === void 0) return;
|
|
20238
|
+
const planFilePath = this.deps.planFilePathProvider?.();
|
|
19218
20239
|
const ctx = {
|
|
19219
20240
|
planMode: this.planMode,
|
|
19220
20241
|
permissionMode: this.permissionMode,
|
|
19221
20242
|
turnNumber: this.extractTurnNumber(turnId),
|
|
19222
|
-
history: this.deps.contextState.getHistory()
|
|
19223
|
-
|
|
19224
|
-
|
|
20243
|
+
history: this.deps.contextState.getHistory(),
|
|
20244
|
+
...planFilePath !== void 0 ? { planFilePath } : {}
|
|
20245
|
+
};
|
|
20246
|
+
const contextBackedWrites = [];
|
|
20247
|
+
const injections = manager.computeInjections(ctx, { appendSystemReminder: (data) => {
|
|
20248
|
+
const write = this.deps.contextState.appendSystemReminder(data);
|
|
20249
|
+
contextBackedWrites.push(write);
|
|
20250
|
+
return write;
|
|
20251
|
+
} });
|
|
20252
|
+
await Promise.all(contextBackedWrites);
|
|
20253
|
+
for (const injection of injections) {
|
|
20254
|
+
if (injection.kind !== "system_reminder") continue;
|
|
20255
|
+
await this.deps.contextState.appendSystemReminder({ content: typeof injection.content === "string" ? injection.content : JSON.stringify(injection.content) });
|
|
20256
|
+
}
|
|
19225
20257
|
}
|
|
19226
20258
|
extractTurnNumber(turnId) {
|
|
19227
20259
|
const match = /^turn_(\d+)$/.exec(turnId);
|
|
@@ -19259,7 +20291,9 @@ var TurnManager = class {
|
|
|
19259
20291
|
kind: this.agentType === "sub" ? "subagent" : this.agentType === "independent" ? "independent" : "main",
|
|
19260
20292
|
...this.subagentType !== void 0 ? { subagentType: this.subagentType } : {}
|
|
19261
20293
|
});
|
|
19262
|
-
const
|
|
20294
|
+
const runtimeBundle = this.runtimeSlot?.current();
|
|
20295
|
+
const baseRuntime = runtimeBundle?.runtime ?? this.runtime;
|
|
20296
|
+
const runtimeForTurn = scope?.wrapRuntime(baseRuntime) ?? baseRuntime;
|
|
19263
20297
|
if (scope !== void 0) {
|
|
19264
20298
|
this.activeToolExecutionScope = scope;
|
|
19265
20299
|
const scoped = scope.buildSoulConfig({
|
|
@@ -19275,10 +20309,11 @@ var TurnManager = class {
|
|
|
19275
20309
|
...scoped.afterToolCall !== void 0 ? { afterToolCall: scoped.afterToolCall } : {}
|
|
19276
20310
|
};
|
|
19277
20311
|
} else soulConfig = { tools: [...this.deps.tools] };
|
|
19278
|
-
|
|
20312
|
+
const compactionConfig = this.runtimeSlot !== void 0 ? runtimeBundle?.compactionConfig : this.compactionConfig;
|
|
20313
|
+
if (compactionConfig !== void 0) soulConfig = {
|
|
19279
20314
|
...soulConfig,
|
|
19280
|
-
compactionConfig
|
|
19281
|
-
contextWindow:
|
|
20315
|
+
compactionConfig,
|
|
20316
|
+
contextWindow: compactionConfig.maxContextSize
|
|
19282
20317
|
};
|
|
19283
20318
|
const input = trigger.input;
|
|
19284
20319
|
const runPromise = this.runTurn(turnId, input, soulConfig, runtimeForTurn, controller.signal);
|
|
@@ -19287,8 +20322,7 @@ var TurnManager = class {
|
|
|
19287
20322
|
return turnId;
|
|
19288
20323
|
}
|
|
19289
20324
|
async runTurn(turnId, input, soulConfig, runtime, signal) {
|
|
19290
|
-
this.drainDynamicInjectionsIntoContext(turnId);
|
|
19291
|
-
await Promise.resolve();
|
|
20325
|
+
await this.drainDynamicInjectionsIntoContext(turnId);
|
|
19292
20326
|
let result;
|
|
19293
20327
|
let reason;
|
|
19294
20328
|
try {
|
|
@@ -19433,7 +20467,7 @@ var TurnManager = class {
|
|
|
19433
20467
|
*/
|
|
19434
20468
|
emitStatusUpdate(tokenUsage) {
|
|
19435
20469
|
const used = this.deps.contextState.tokenCountWithPending;
|
|
19436
|
-
const total = this.
|
|
20470
|
+
const total = (this.runtimeSlot !== void 0 ? this.runtimeSlot.current().compactionConfig : this.compactionConfig)?.maxContextSize ?? DEFAULT_CONTEXT_WINDOW;
|
|
19437
20471
|
const data = {
|
|
19438
20472
|
context_usage: {
|
|
19439
20473
|
used,
|
|
@@ -19477,6 +20511,101 @@ var WakeQueueScheduler = class {
|
|
|
19477
20511
|
};
|
|
19478
20512
|
//#endregion
|
|
19479
20513
|
//#region ../../packages/kimi-core/src/soul-plus/soul-plus.ts
|
|
20514
|
+
const PLAN_APPROVAL_APPROVE = "Approve";
|
|
20515
|
+
const PLAN_APPROVAL_REJECT = "Reject";
|
|
20516
|
+
const PLAN_APPROVAL_REJECT_AND_EXIT = "Reject and Exit";
|
|
20517
|
+
const PLAN_APPROVAL_REVISE = "Revise";
|
|
20518
|
+
function isRecord$7(value) {
|
|
20519
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20520
|
+
}
|
|
20521
|
+
function parseQuestionAnswerLabels(raw) {
|
|
20522
|
+
const trimmed = raw.trim();
|
|
20523
|
+
if (trimmed.length === 0) return [];
|
|
20524
|
+
try {
|
|
20525
|
+
const parsed = JSON.parse(trimmed);
|
|
20526
|
+
const answers = isRecord$7(parsed) && isRecord$7(parsed["answers"]) ? parsed["answers"] : void 0;
|
|
20527
|
+
if (answers !== void 0) return Object.values(answers).filter((value) => typeof value === "string" && value.length > 0);
|
|
20528
|
+
} catch {}
|
|
20529
|
+
return [trimmed];
|
|
20530
|
+
}
|
|
20531
|
+
function pushPlanApprovalOption(options, option) {
|
|
20532
|
+
if (options.some((item) => item.label === option.label)) return;
|
|
20533
|
+
options.push(option);
|
|
20534
|
+
}
|
|
20535
|
+
function planApprovalQuestionOptions(args) {
|
|
20536
|
+
const options = args.options !== void 0 && args.options.length >= 2 ? [...args.options] : [{
|
|
20537
|
+
label: PLAN_APPROVAL_APPROVE,
|
|
20538
|
+
description: "Exit plan mode and start execution"
|
|
20539
|
+
}];
|
|
20540
|
+
pushPlanApprovalOption(options, {
|
|
20541
|
+
label: PLAN_APPROVAL_REJECT,
|
|
20542
|
+
description: "Reject and stay in plan mode"
|
|
20543
|
+
});
|
|
20544
|
+
pushPlanApprovalOption(options, {
|
|
20545
|
+
label: PLAN_APPROVAL_REJECT_AND_EXIT,
|
|
20546
|
+
description: "Reject and exit plan mode"
|
|
20547
|
+
});
|
|
20548
|
+
return options;
|
|
20549
|
+
}
|
|
20550
|
+
async function requestEnterPlanModeQuestion(questionRuntime, toolCallId, args, signal) {
|
|
20551
|
+
const reasonSuffix = args.reason ? `\n\nReason: ${args.reason}` : "";
|
|
20552
|
+
return { approved: parseQuestionAnswerLabels((await questionRuntime.askQuestion({
|
|
20553
|
+
toolCallId,
|
|
20554
|
+
questions: [{
|
|
20555
|
+
header: "Plan Mode",
|
|
20556
|
+
question: `Enter plan mode? In plan mode I'll investigate and design a plan before making changes.${reasonSuffix}`,
|
|
20557
|
+
options: [{
|
|
20558
|
+
label: "Yes",
|
|
20559
|
+
description: "Enter plan mode"
|
|
20560
|
+
}, {
|
|
20561
|
+
label: "No",
|
|
20562
|
+
description: "Proceed without planning"
|
|
20563
|
+
}]
|
|
20564
|
+
}],
|
|
20565
|
+
signal
|
|
20566
|
+
})).answer)[0] === "Yes" };
|
|
20567
|
+
}
|
|
20568
|
+
async function requestPlanApprovalQuestion(questionRuntime, args, signal) {
|
|
20569
|
+
const labels = new Set((args.options ?? []).map((option) => option.label));
|
|
20570
|
+
const choice = parseQuestionAnswerLabels((await questionRuntime.askQuestion({
|
|
20571
|
+
toolCallId: `exit_plan_approval_${Date.now().toString(36)}`,
|
|
20572
|
+
questions: [{
|
|
20573
|
+
header: "Plan",
|
|
20574
|
+
question: "Approve this plan?",
|
|
20575
|
+
body: args.plan,
|
|
20576
|
+
options: planApprovalQuestionOptions(args),
|
|
20577
|
+
otherLabel: PLAN_APPROVAL_REVISE,
|
|
20578
|
+
otherDescription: "Stay in plan mode and provide feedback"
|
|
20579
|
+
}],
|
|
20580
|
+
signal
|
|
20581
|
+
})).answer)[0];
|
|
20582
|
+
if (choice === void 0) return {
|
|
20583
|
+
approved: false,
|
|
20584
|
+
chosenLabel: PLAN_APPROVAL_REVISE,
|
|
20585
|
+
feedback: "User dismissed without choosing. Plan mode remains active."
|
|
20586
|
+
};
|
|
20587
|
+
if (choice === PLAN_APPROVAL_REJECT_AND_EXIT) return { approved: true };
|
|
20588
|
+
if (choice === PLAN_APPROVAL_REJECT) return {
|
|
20589
|
+
approved: false,
|
|
20590
|
+
chosenLabel: PLAN_APPROVAL_REVISE,
|
|
20591
|
+
feedback: "Plan rejected by user. Stay in plan mode and wait for the user before revising."
|
|
20592
|
+
};
|
|
20593
|
+
if (choice === PLAN_APPROVAL_REVISE) return {
|
|
20594
|
+
approved: false,
|
|
20595
|
+
chosenLabel: PLAN_APPROVAL_REVISE,
|
|
20596
|
+
feedback: "User requested a plan revision."
|
|
20597
|
+
};
|
|
20598
|
+
if (choice === PLAN_APPROVAL_APPROVE) return { approved: true };
|
|
20599
|
+
if (labels.has(choice)) return {
|
|
20600
|
+
approved: true,
|
|
20601
|
+
chosenLabel: choice
|
|
20602
|
+
};
|
|
20603
|
+
return {
|
|
20604
|
+
approved: false,
|
|
20605
|
+
chosenLabel: PLAN_APPROVAL_REVISE,
|
|
20606
|
+
feedback: `User selected "${choice}". Stay in plan mode and revise before proceeding.`
|
|
20607
|
+
};
|
|
20608
|
+
}
|
|
19480
20609
|
async function requestPlanApproval(approvalRuntime, turnManager, toolCallId, args, signal) {
|
|
19481
20610
|
const response = await approvalRuntime.request({
|
|
19482
20611
|
toolCallId,
|
|
@@ -19542,6 +20671,8 @@ var SoulPlus = class {
|
|
|
19542
20671
|
components;
|
|
19543
20672
|
infra;
|
|
19544
20673
|
hostToolNames;
|
|
20674
|
+
planController;
|
|
20675
|
+
setPlanModeImpl;
|
|
19545
20676
|
/**
|
|
19546
20677
|
* Phase 18 A.3–A.6 — lazy `SessionControlHandler` owned by the
|
|
19547
20678
|
* facade. External hosts (SessionManager) still construct their own
|
|
@@ -19565,14 +20696,33 @@ var SoulPlus = class {
|
|
|
19565
20696
|
const sessionJournal = deps.sessionJournal;
|
|
19566
20697
|
const journalWriter = contextState.journalWriter;
|
|
19567
20698
|
const journalCapability = deps.journalCapability;
|
|
19568
|
-
const
|
|
19569
|
-
|
|
20699
|
+
const runtimeSlot = deps.runtimeSlot ?? createSessionRuntimeSlot({
|
|
20700
|
+
model: contextState.model,
|
|
20701
|
+
runtime: deps.runtime,
|
|
20702
|
+
compactionProvider: deps.compactionProvider,
|
|
20703
|
+
...deps.compactionConfig !== void 0 ? { compactionConfig: deps.compactionConfig } : {}
|
|
20704
|
+
});
|
|
20705
|
+
const runtime = runtimeSlot.current().runtime;
|
|
19570
20706
|
const approvalRuntime = deps.approvalRuntime;
|
|
20707
|
+
const sessionMeta = deps.stateCache !== void 0 && deps.initialMeta !== void 0 ? new SessionMetaService({
|
|
20708
|
+
sessionId: deps.sessionId,
|
|
20709
|
+
sessionJournal,
|
|
20710
|
+
eventBus,
|
|
20711
|
+
stateCache: deps.stateCache,
|
|
20712
|
+
initialMeta: deps.initialMeta
|
|
20713
|
+
}) : void 0;
|
|
20714
|
+
const planController = new PlanSessionController({
|
|
20715
|
+
sessionId: deps.sessionId,
|
|
20716
|
+
...deps.pathConfig !== void 0 ? { paths: deps.pathConfig } : {},
|
|
20717
|
+
...sessionMeta !== void 0 ? { sessionMeta } : {}
|
|
20718
|
+
});
|
|
20719
|
+
this.planController = planController;
|
|
19571
20720
|
const compactionProvider = deps.compactionProvider;
|
|
19572
20721
|
let turnManagerRef;
|
|
19573
20722
|
const compaction = new CompactionOrchestrator({
|
|
19574
20723
|
contextState,
|
|
19575
20724
|
compactionProvider,
|
|
20725
|
+
runtimeSlot,
|
|
19576
20726
|
lifecycleStateMachine: stateMachine,
|
|
19577
20727
|
journalCapability,
|
|
19578
20728
|
sink: eventBus,
|
|
@@ -19592,7 +20742,11 @@ var SoulPlus = class {
|
|
|
19592
20742
|
return turnManagerRef.getCurrentTurnId();
|
|
19593
20743
|
}
|
|
19594
20744
|
});
|
|
19595
|
-
const
|
|
20745
|
+
const effectiveHookEngine = deps.hookEngine ?? (approvalRuntime !== void 0 ? new HookEngine({
|
|
20746
|
+
executors: /* @__PURE__ */ new Map(),
|
|
20747
|
+
sink: eventBus
|
|
20748
|
+
}) : void 0);
|
|
20749
|
+
const toolExecutionScopeFactory = deps.toolExecutionScopeFactory ?? (approvalRuntime !== void 0 && effectiveHookEngine !== void 0 ? new DefaultToolExecutionScopeFactory({
|
|
19596
20750
|
policy: {
|
|
19597
20751
|
getPermissionMode: () => {
|
|
19598
20752
|
if (turnManagerRef === void 0) return "default";
|
|
@@ -19607,10 +20761,17 @@ var SoulPlus = class {
|
|
|
19607
20761
|
turnManagerRef.addSessionRule(rule);
|
|
19608
20762
|
},
|
|
19609
20763
|
approvalRuntime,
|
|
19610
|
-
hookEngine:
|
|
20764
|
+
hookEngine: effectiveHookEngine
|
|
19611
20765
|
},
|
|
19612
|
-
resultPolicy: new ArchiveToolResultPolicy(deps.pathConfig !== void 0 ? { pathConfig: deps.pathConfig } : {})
|
|
19613
|
-
|
|
20766
|
+
resultPolicy: new ArchiveToolResultPolicy(deps.pathConfig !== void 0 ? { pathConfig: deps.pathConfig } : {}),
|
|
20767
|
+
planMode: {
|
|
20768
|
+
isActive: () => turnManagerRef?.getPlanMode() ?? false,
|
|
20769
|
+
controller: planController
|
|
20770
|
+
}
|
|
20771
|
+
}) : planController.isAvailable() ? createPlanModeOnlyToolExecutionScopeFactory({ planMode: {
|
|
20772
|
+
isActive: () => turnManagerRef?.getPlanMode() ?? false,
|
|
20773
|
+
controller: planController
|
|
20774
|
+
} }) : void 0);
|
|
19614
20775
|
const hasSubagentInfra = deps.subagentStore !== void 0 && deps.agentTypeRegistry !== void 0;
|
|
19615
20776
|
const soulRegistry = new SoulRegistry({
|
|
19616
20777
|
createHandle: (key, agentDepth) => ({
|
|
@@ -19625,7 +20786,8 @@ var SoulPlus = class {
|
|
|
19625
20786
|
store: deps.subagentStore,
|
|
19626
20787
|
typeRegistry: deps.agentTypeRegistry,
|
|
19627
20788
|
parentTools: deps.tools,
|
|
19628
|
-
|
|
20789
|
+
runtimeSlot,
|
|
20790
|
+
parentRuntime: runtimeSlot.current().runtime,
|
|
19629
20791
|
parentEventBus: eventBus,
|
|
19630
20792
|
sessionDir: deps.sessionDir ?? "",
|
|
19631
20793
|
parentModel: contextState.model,
|
|
@@ -19657,6 +20819,7 @@ var SoulPlus = class {
|
|
|
19657
20819
|
contextState,
|
|
19658
20820
|
sessionJournal,
|
|
19659
20821
|
runtime,
|
|
20822
|
+
runtimeSlot,
|
|
19660
20823
|
sink: eventBus,
|
|
19661
20824
|
lifecycleStateMachine: stateMachine,
|
|
19662
20825
|
soulRegistry,
|
|
@@ -19666,6 +20829,7 @@ var SoulPlus = class {
|
|
|
19666
20829
|
wakeScheduler,
|
|
19667
20830
|
dynamicInjectionManager,
|
|
19668
20831
|
sessionId: deps.sessionId,
|
|
20832
|
+
planFilePathProvider: () => planController.getPlanFilePath() ?? void 0,
|
|
19669
20833
|
...deps.sessionRules !== void 0 ? { sessionRules: deps.sessionRules } : {},
|
|
19670
20834
|
...deps.permissionMode !== void 0 ? { permissionMode: deps.permissionMode } : {},
|
|
19671
20835
|
...toolExecutionScopeFactory !== void 0 ? { toolExecutionScopeFactory } : {},
|
|
@@ -19674,25 +20838,41 @@ var SoulPlus = class {
|
|
|
19674
20838
|
...deps.compactionConfig !== void 0 ? { compactionConfig: deps.compactionConfig } : {}
|
|
19675
20839
|
});
|
|
19676
20840
|
turnManagerRef = turnManager;
|
|
19677
|
-
|
|
19678
|
-
|
|
19679
|
-
|
|
20841
|
+
const setPlanMode = async (enabled) => {
|
|
20842
|
+
if (enabled) {
|
|
20843
|
+
await planController.ensurePlanFilePath();
|
|
19680
20844
|
await contextState.applyConfigChange({
|
|
19681
20845
|
type: "plan_mode_changed",
|
|
19682
20846
|
enabled
|
|
19683
20847
|
});
|
|
19684
20848
|
turnManager.setPlanMode(enabled);
|
|
19685
|
-
|
|
20849
|
+
return;
|
|
20850
|
+
}
|
|
20851
|
+
await contextState.applyConfigChange({
|
|
20852
|
+
type: "plan_mode_changed",
|
|
20853
|
+
enabled
|
|
20854
|
+
});
|
|
20855
|
+
turnManager.setPlanMode(enabled);
|
|
20856
|
+
await planController.deactivatePlanMode();
|
|
20857
|
+
};
|
|
20858
|
+
this.setPlanModeImpl = setPlanMode;
|
|
20859
|
+
if (deps.questionRuntime !== void 0) {
|
|
20860
|
+
const questionRuntime = deps.questionRuntime;
|
|
20861
|
+
const planApprovalRuntime = approvalRuntime !== void 0 && !(approvalRuntime instanceof AlwaysAllowApprovalRuntime) ? approvalRuntime : void 0;
|
|
20862
|
+
const enterPlanRequestApproval = planApprovalRuntime !== void 0 ? (toolCallId, args, signal) => requestEnterPlanModeApproval(planApprovalRuntime, turnManager, toolCallId, args, signal) : (toolCallId, args, signal) => requestEnterPlanModeQuestion(questionRuntime, toolCallId, args, signal);
|
|
20863
|
+
const exitPlanRequestApproval = planApprovalRuntime !== void 0 ? (toolCallId, args, signal) => requestPlanApproval(planApprovalRuntime, turnManager, toolCallId, args, signal) : (_toolCallId, args, signal) => requestPlanApprovalQuestion(questionRuntime, args, signal);
|
|
19686
20864
|
if (isToolEnabled("EnterPlanMode")) toolRegistry.push(new EnterPlanModeTool({
|
|
19687
20865
|
isPlanModeActive: () => turnManager.getPlanMode(),
|
|
19688
20866
|
setPlanMode,
|
|
19689
20867
|
isYoloMode: () => turnManager.getPermissionMode() === "bypassPermissions",
|
|
19690
|
-
|
|
20868
|
+
requestApproval: enterPlanRequestApproval,
|
|
20869
|
+
getPlanFilePath: () => planController.getPlanFilePath()
|
|
19691
20870
|
}));
|
|
19692
20871
|
if (isToolEnabled("ExitPlanMode")) toolRegistry.push(new ExitPlanModeTool({
|
|
19693
20872
|
isPlanModeActive: () => turnManager.getPlanMode(),
|
|
19694
20873
|
setPlanMode,
|
|
19695
|
-
|
|
20874
|
+
requestApproval: exitPlanRequestApproval,
|
|
20875
|
+
...planController.isAvailable() ? { readPlan: () => planController.readCurrentPlan() } : {}
|
|
19696
20876
|
}));
|
|
19697
20877
|
if (isToolEnabled("AskUserQuestion")) toolRegistry.push(new AskUserQuestionTool(questionRuntime, () => turnManager.getPermissionMode()));
|
|
19698
20878
|
}
|
|
@@ -19703,13 +20883,6 @@ var SoulPlus = class {
|
|
|
19703
20883
|
...deps.onShellDeliver !== void 0 ? { onShellDeliver: deps.onShellDeliver } : {},
|
|
19704
20884
|
...deps.logger !== void 0 ? { logger: deps.logger } : {}
|
|
19705
20885
|
});
|
|
19706
|
-
const sessionMeta = deps.stateCache !== void 0 && deps.initialMeta !== void 0 ? new SessionMetaService({
|
|
19707
|
-
sessionId: deps.sessionId,
|
|
19708
|
-
sessionJournal,
|
|
19709
|
-
eventBus,
|
|
19710
|
-
stateCache: deps.stateCache,
|
|
19711
|
-
initialMeta: deps.initialMeta
|
|
19712
|
-
}) : void 0;
|
|
19713
20886
|
this.lifecycle = {
|
|
19714
20887
|
stateMachine,
|
|
19715
20888
|
gate
|
|
@@ -19737,7 +20910,7 @@ var SoulPlus = class {
|
|
|
19737
20910
|
eventBus,
|
|
19738
20911
|
toolRegistry,
|
|
19739
20912
|
permissionRules,
|
|
19740
|
-
hookEngine:
|
|
20913
|
+
hookEngine: effectiveHookEngine
|
|
19741
20914
|
};
|
|
19742
20915
|
if (deps.approvalStateStore !== void 0) deps.approvalStateStore.onChanged((snapshot) => {
|
|
19743
20916
|
turnManager.setPermissionMode(snapshot.yolo ? "bypassPermissions" : "default");
|
|
@@ -19881,6 +21054,21 @@ var SoulPlus = class {
|
|
|
19881
21054
|
tryGetSessionMeta() {
|
|
19882
21055
|
return this.services.sessionMeta;
|
|
19883
21056
|
}
|
|
21057
|
+
async setPlanMode(enabled) {
|
|
21058
|
+
await this.setPlanModeImpl(enabled);
|
|
21059
|
+
}
|
|
21060
|
+
getPlanFilePath() {
|
|
21061
|
+
return this.planController.getPlanFilePath();
|
|
21062
|
+
}
|
|
21063
|
+
ensurePlanFilePath() {
|
|
21064
|
+
return this.planController.ensurePlanFilePath();
|
|
21065
|
+
}
|
|
21066
|
+
readCurrentPlan() {
|
|
21067
|
+
return this.planController.readCurrentPlan();
|
|
21068
|
+
}
|
|
21069
|
+
clearCurrentPlan() {
|
|
21070
|
+
return this.planController.clearCurrentPlan();
|
|
21071
|
+
}
|
|
19884
21072
|
/**
|
|
19885
21073
|
* Activate a skill by name (Slice 2.5 inline mode). Reads the
|
|
19886
21074
|
* SKILL.md body from the registered `SkillDefinition`, interpolates
|
|
@@ -19912,7 +21100,8 @@ var SoulPlus = class {
|
|
|
19912
21100
|
this.sessionControlInstance = new DefaultSessionControl({
|
|
19913
21101
|
turnManager: this.components.turnManager,
|
|
19914
21102
|
contextState: this.journal.contextState,
|
|
19915
|
-
sessionJournal: this.journal.sessionJournal
|
|
21103
|
+
sessionJournal: this.journal.sessionJournal,
|
|
21104
|
+
setPlanModeOverride: (enabled) => this.setPlanMode(enabled)
|
|
19916
21105
|
});
|
|
19917
21106
|
return this.sessionControlInstance;
|
|
19918
21107
|
}
|
|
@@ -20098,7 +21287,7 @@ var KosongAdapter = class {
|
|
|
20098
21287
|
const kosongTools = params.tools.map((t) => ({
|
|
20099
21288
|
name: t.name,
|
|
20100
21289
|
description: t.description,
|
|
20101
|
-
parameters: isRecord$
|
|
21290
|
+
parameters: isRecord$6(t.input_schema) ? t.input_schema : { type: "object" }
|
|
20102
21291
|
}));
|
|
20103
21292
|
const onDelta = params.onDelta;
|
|
20104
21293
|
const onThinkDelta = params.onThinkDelta;
|
|
@@ -20196,7 +21385,7 @@ function extractMessage(err) {
|
|
|
20196
21385
|
function createKosongAdapter(options) {
|
|
20197
21386
|
return new KosongAdapter(options);
|
|
20198
21387
|
}
|
|
20199
|
-
function isRecord$
|
|
21388
|
+
function isRecord$6(value) {
|
|
20200
21389
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20201
21390
|
}
|
|
20202
21391
|
function parseToolArgs(raw) {
|
|
@@ -20483,44 +21672,6 @@ function safeDispatch(fn, arg, onError) {
|
|
|
20483
21672
|
});
|
|
20484
21673
|
}
|
|
20485
21674
|
//#endregion
|
|
20486
|
-
//#region ../../packages/kimi-core/src/soul-plus/approval/approval-runtime.ts
|
|
20487
|
-
/**
|
|
20488
|
-
* Thrown when a cross-process / team forwarding hook is invoked before
|
|
20489
|
-
* the TeamDaemon wiring lands (Slice 2.6+). Callers should treat this as
|
|
20490
|
-
* an unrecoverable misconfiguration in Slice 2.3.
|
|
20491
|
-
*/
|
|
20492
|
-
var NotImplementedError = class extends Error {
|
|
20493
|
-
constructor(feature) {
|
|
20494
|
-
super(`${feature} not implemented in Slice 2.3 (deferred to TeamDaemon)`);
|
|
20495
|
-
this.name = "NotImplementedError";
|
|
20496
|
-
}
|
|
20497
|
-
};
|
|
20498
|
-
/**
|
|
20499
|
-
* Default stub used when no wire/UI integration is required (e.g. unit
|
|
20500
|
-
* tests of unrelated layers, or embedder harnesses that bypass approvals
|
|
20501
|
-
* entirely via their own `beforeToolCall`). Every request is immediately
|
|
20502
|
-
* approved and no records are written.
|
|
20503
|
-
*
|
|
20504
|
-
* For the real implementation see `./wired-approval-runtime.ts`.
|
|
20505
|
-
*/
|
|
20506
|
-
var AlwaysAllowApprovalRuntime = class {
|
|
20507
|
-
async request(_req, _signal) {
|
|
20508
|
-
return {
|
|
20509
|
-
approved: true,
|
|
20510
|
-
response: "approved"
|
|
20511
|
-
};
|
|
20512
|
-
}
|
|
20513
|
-
async recoverPendingOnStartup() {}
|
|
20514
|
-
resolve(_requestId, _response) {}
|
|
20515
|
-
cancelBySource(_source) {}
|
|
20516
|
-
async ingestRemoteRequest(_data) {
|
|
20517
|
-
throw new NotImplementedError("ApprovalRuntime.ingestRemoteRequest");
|
|
20518
|
-
}
|
|
20519
|
-
resolveRemote(_data) {
|
|
20520
|
-
throw new NotImplementedError("ApprovalRuntime.resolveRemote");
|
|
20521
|
-
}
|
|
20522
|
-
};
|
|
20523
|
-
//#endregion
|
|
20524
21675
|
//#region ../../packages/kimi-core/src/soul-plus/approval/wired-approval-runtime.ts
|
|
20525
21676
|
/**
|
|
20526
21677
|
* WiredApprovalRuntime — the production `ApprovalRuntime` (v2 §9-G).
|
|
@@ -22798,122 +23949,6 @@ var ReadTool = class {
|
|
|
22798
23949
|
}
|
|
22799
23950
|
};
|
|
22800
23951
|
//#endregion
|
|
22801
|
-
//#region ../../packages/kimi-core/src/soul-plus/plan/plan-mode-checker.ts
|
|
22802
|
-
/**
|
|
22803
|
-
* Shared error message surface used by Write / Edit so the LLM always
|
|
22804
|
-
* sees a consistent "here's the exit" hint regardless of which tool
|
|
22805
|
-
* tripped the block.
|
|
22806
|
-
*/
|
|
22807
|
-
function planModeWriteBlockMessage(planPath) {
|
|
22808
|
-
return `Plan mode is active. You may only write to the current plan file: ${planPath ?? "(no plan file selected yet)"}. Call ExitPlanMode to exit plan mode before editing other files.`;
|
|
22809
|
-
}
|
|
22810
|
-
/**
|
|
22811
|
-
* Shared error message surface for Bash mutation blocks.
|
|
22812
|
-
*/
|
|
22813
|
-
function planModeBashBlockMessage() {
|
|
22814
|
-
return "Plan mode is active. This command would mutate the filesystem; plan mode forbids that. Call ExitPlanMode to exit plan mode before running mutation commands.";
|
|
22815
|
-
}
|
|
22816
|
-
/**
|
|
22817
|
-
* Classify a shell command as "mutation" vs read-only. Used exclusively
|
|
22818
|
-
* by the plan-mode hard block — the detector is conservative: the tests
|
|
22819
|
-
* pin seven concrete cases (rm, redirect `>`, `>>`, sed -i, git commit,
|
|
22820
|
-
* plus `ls` / `cat` that must NOT trip the gate). The detector leans
|
|
22821
|
-
* toward under-blocking (unknown-first-word = allow) so plan-mode
|
|
22822
|
-
* doesn't interfere with legitimate read-only explorations.
|
|
22823
|
-
*
|
|
22824
|
-
* This deliberately does NOT attempt to be a full shell parser. Known
|
|
22825
|
-
* and accepted false positives:
|
|
22826
|
-
* - Quoted strings containing `>` are blocked (e.g.
|
|
22827
|
-
* `echo "hello > world"`) because the regex does not track quote
|
|
22828
|
-
* state. Plan-mode callers can work around this by avoiding
|
|
22829
|
-
* redirect-like substrings in quoted output.
|
|
22830
|
-
* - `&>` / `&>>` (bash stderr+stdout combined redirect) is *not*
|
|
22831
|
-
* detected; those are legitimate file writes but the policy's
|
|
22832
|
-
* 7-case test pin does not cover them.
|
|
22833
|
-
*
|
|
22834
|
-
* Stricter parsing is out of scope for Phase 18; hosts that need
|
|
22835
|
-
* tighter policy should wrap or replace this helper.
|
|
22836
|
-
*/
|
|
22837
|
-
function isMutatingBashCommand(command) {
|
|
22838
|
-
const trimmed = command.trim();
|
|
22839
|
-
if (trimmed.length === 0) return false;
|
|
22840
|
-
if (/(^|[^&0-9])>{1,2}\s*(?!&)\S/.test(trimmed)) return true;
|
|
22841
|
-
const tokens = trimmed.split(/\s+/);
|
|
22842
|
-
const firstToken = tokens[0] ?? "";
|
|
22843
|
-
if (new Set([
|
|
22844
|
-
"rm",
|
|
22845
|
-
"rmdir",
|
|
22846
|
-
"mv",
|
|
22847
|
-
"cp",
|
|
22848
|
-
"mkdir",
|
|
22849
|
-
"touch",
|
|
22850
|
-
"chmod",
|
|
22851
|
-
"chown",
|
|
22852
|
-
"ln",
|
|
22853
|
-
"tee",
|
|
22854
|
-
"dd",
|
|
22855
|
-
"truncate",
|
|
22856
|
-
"shred",
|
|
22857
|
-
"patch"
|
|
22858
|
-
]).has(firstToken)) return true;
|
|
22859
|
-
if (firstToken === "sed" && (/\s-i(\b|\s|=)/.test(` ${trimmed}`) || trimmed.includes("--in-place"))) return true;
|
|
22860
|
-
if (firstToken === "git") {
|
|
22861
|
-
const gitMutating = new Set([
|
|
22862
|
-
"commit",
|
|
22863
|
-
"push",
|
|
22864
|
-
"reset",
|
|
22865
|
-
"checkout",
|
|
22866
|
-
"rebase",
|
|
22867
|
-
"merge",
|
|
22868
|
-
"tag",
|
|
22869
|
-
"stash",
|
|
22870
|
-
"cherry-pick",
|
|
22871
|
-
"revert",
|
|
22872
|
-
"pull",
|
|
22873
|
-
"add",
|
|
22874
|
-
"rm",
|
|
22875
|
-
"mv",
|
|
22876
|
-
"clone",
|
|
22877
|
-
"init",
|
|
22878
|
-
"restore",
|
|
22879
|
-
"apply",
|
|
22880
|
-
"clean",
|
|
22881
|
-
"gc",
|
|
22882
|
-
"prune"
|
|
22883
|
-
]);
|
|
22884
|
-
const sub = tokens[1];
|
|
22885
|
-
if (sub !== void 0 && gitMutating.has(sub)) return true;
|
|
22886
|
-
if (sub === "branch" && /\s-[dD](\b|\s)/.test(trimmed)) return true;
|
|
22887
|
-
}
|
|
22888
|
-
if (new Set([
|
|
22889
|
-
"npm",
|
|
22890
|
-
"yarn",
|
|
22891
|
-
"pnpm",
|
|
22892
|
-
"pip",
|
|
22893
|
-
"pip3",
|
|
22894
|
-
"poetry",
|
|
22895
|
-
"cargo",
|
|
22896
|
-
"gem",
|
|
22897
|
-
"uv"
|
|
22898
|
-
]).has(firstToken)) {
|
|
22899
|
-
const sub = tokens[1] ?? "";
|
|
22900
|
-
if (new Set([
|
|
22901
|
-
"install",
|
|
22902
|
-
"i",
|
|
22903
|
-
"add",
|
|
22904
|
-
"remove",
|
|
22905
|
-
"rm",
|
|
22906
|
-
"uninstall",
|
|
22907
|
-
"update",
|
|
22908
|
-
"upgrade",
|
|
22909
|
-
"publish",
|
|
22910
|
-
"link",
|
|
22911
|
-
"unlink"
|
|
22912
|
-
]).has(sub)) return true;
|
|
22913
|
-
}
|
|
22914
|
-
return false;
|
|
22915
|
-
}
|
|
22916
|
-
//#endregion
|
|
22917
23952
|
//#region ../../packages/kimi-core/src/tools/builtin/file/write.ts
|
|
22918
23953
|
/**
|
|
22919
23954
|
* WriteTool — overwrite-write a file (§9-F / Appendix E.2).
|
|
@@ -28384,206 +29419,6 @@ var SetTodoListTool = class {
|
|
|
28384
29419
|
}));
|
|
28385
29420
|
}
|
|
28386
29421
|
};
|
|
28387
|
-
//#endregion
|
|
28388
|
-
//#region ../../packages/kimi-core/src/hooks/engine.ts
|
|
28389
|
-
/**
|
|
28390
|
-
* Synthesises a stable id for `hook.resolved` emissions. Hooks are
|
|
28391
|
-
* registered without an intrinsic id; we derive one from
|
|
28392
|
-
* `event:type:matcher`, suffixed with the hook's position inside
|
|
28393
|
-
* `this.hooks` (its registration order). The `registrationIndex` is
|
|
28394
|
-
* stable across multiple `executeHooks` calls — using the per-call
|
|
28395
|
-
* `settled[]` index instead would hand the same id to different hooks
|
|
28396
|
-
* across different dispatches, breaking client-side correlation.
|
|
28397
|
-
*/
|
|
28398
|
-
function hookId(hook, registrationIndex) {
|
|
28399
|
-
return `${hook.event}:${hook.type}:${hook.matcher ?? ""}:${registrationIndex}`;
|
|
28400
|
-
}
|
|
28401
|
-
var HookEngine = class {
|
|
28402
|
-
deps;
|
|
28403
|
-
/**
|
|
28404
|
-
* Mutable executor registry. Seeded from `deps.executors` at
|
|
28405
|
-
* construction so the constructor arg can stay `ReadonlyMap`-typed
|
|
28406
|
-
* while `registerExecutor` still lets callers install additional
|
|
28407
|
-
* executors (e.g. `WireHookExecutor`) after the engine is wired.
|
|
28408
|
-
*/
|
|
28409
|
-
executors;
|
|
28410
|
-
hooks = [];
|
|
28411
|
-
/**
|
|
28412
|
-
* Phase 18 L3-2 — invalid-regex warn dedupe. Each distinct invalid
|
|
28413
|
-
* matcher fires `onInvalidMatcher` once per engine instance so a
|
|
28414
|
-
* misconfigured block-action hook doesn't flood logs on every
|
|
28415
|
-
* tool call.
|
|
28416
|
-
*/
|
|
28417
|
-
warnedInvalidMatchers = /* @__PURE__ */ new Set();
|
|
28418
|
-
constructor(deps) {
|
|
28419
|
-
this.deps = deps;
|
|
28420
|
-
this.executors = new Map(deps.executors);
|
|
28421
|
-
}
|
|
28422
|
-
register(hook) {
|
|
28423
|
-
this.hooks.push(hook);
|
|
28424
|
-
}
|
|
28425
|
-
/**
|
|
28426
|
-
* Phase 18 L2-4 — install / replace an executor for a given `type`
|
|
28427
|
-
* label at runtime. Used by the wire layer to bolt a
|
|
28428
|
-
* `WireHookExecutor` onto an engine that was constructed with only
|
|
28429
|
-
* the `command` executor. Replacing an existing entry is silent;
|
|
28430
|
-
* callers wanting a conflict check can probe `hasExecutor(type)`.
|
|
28431
|
-
*/
|
|
28432
|
-
registerExecutor(type, executor) {
|
|
28433
|
-
this.executors.set(type, executor);
|
|
28434
|
-
}
|
|
28435
|
-
/** Returns true when an executor has been registered under `type`. */
|
|
28436
|
-
hasExecutor(type) {
|
|
28437
|
-
return this.executors.has(type);
|
|
28438
|
-
}
|
|
28439
|
-
unregister(hook) {
|
|
28440
|
-
const idx = this.hooks.indexOf(hook);
|
|
28441
|
-
if (idx !== -1) this.hooks.splice(idx, 1);
|
|
28442
|
-
}
|
|
28443
|
-
list(event) {
|
|
28444
|
-
if (event === void 0) return [...this.hooks];
|
|
28445
|
-
return this.hooks.filter((h) => h.event === event);
|
|
28446
|
-
}
|
|
28447
|
-
/**
|
|
28448
|
-
* Pre-filters hooks by both `event` and `matcher` regex against the
|
|
28449
|
-
* target value (tool name for tool-scoped events). Exported-ish via
|
|
28450
|
-
* `executeHooks` — v2 §9-C.3 requires "getMatchingHooks(event, input)
|
|
28451
|
-
* then concurrent execute" ordering.
|
|
28452
|
-
*/
|
|
28453
|
-
getMatchingHooks(event, input) {
|
|
28454
|
-
const matcherValue = extractMatcherValue(input);
|
|
28455
|
-
return this.hooks.filter((h) => {
|
|
28456
|
-
if (h.event !== event) return false;
|
|
28457
|
-
return this.matchesTarget(h, matcherValue);
|
|
28458
|
-
});
|
|
28459
|
-
}
|
|
28460
|
-
matchesTarget(hook, value) {
|
|
28461
|
-
const matcher = hook.matcher;
|
|
28462
|
-
if (matcher === void 0 || matcher === "") return true;
|
|
28463
|
-
let re;
|
|
28464
|
-
try {
|
|
28465
|
-
re = new RegExp(matcher);
|
|
28466
|
-
} catch {
|
|
28467
|
-
if (!this.warnedInvalidMatchers.has(matcher)) {
|
|
28468
|
-
this.warnedInvalidMatchers.add(matcher);
|
|
28469
|
-
this.deps.onInvalidMatcher?.(hook, matcher);
|
|
28470
|
-
}
|
|
28471
|
-
return false;
|
|
28472
|
-
}
|
|
28473
|
-
return re.test(value);
|
|
28474
|
-
}
|
|
28475
|
-
async executeHooks(event, input, signal) {
|
|
28476
|
-
const deduped = dedupeByCommand(this.getMatchingHooks(event, input));
|
|
28477
|
-
if (deduped.length === 0) {
|
|
28478
|
-
this.deps.sink?.emit({
|
|
28479
|
-
type: "hook.triggered",
|
|
28480
|
-
event,
|
|
28481
|
-
matchers: [],
|
|
28482
|
-
matched_count: 0
|
|
28483
|
-
});
|
|
28484
|
-
return {
|
|
28485
|
-
blockAction: false,
|
|
28486
|
-
additionalContext: []
|
|
28487
|
-
};
|
|
28488
|
-
}
|
|
28489
|
-
this.deps.sink?.emit({
|
|
28490
|
-
type: "hook.triggered",
|
|
28491
|
-
event,
|
|
28492
|
-
matchers: deduped.map((h) => h.matcher ?? ""),
|
|
28493
|
-
matched_count: deduped.length
|
|
28494
|
-
});
|
|
28495
|
-
const settled = await Promise.allSettled(deduped.map((hook) => {
|
|
28496
|
-
const executor = this.executors.get(hook.type);
|
|
28497
|
-
if (executor === void 0) return Promise.resolve(void 0);
|
|
28498
|
-
return executor.execute(hook, input, signal);
|
|
28499
|
-
}));
|
|
28500
|
-
let blockAction = false;
|
|
28501
|
-
let reason;
|
|
28502
|
-
const additionalContext = [];
|
|
28503
|
-
for (const [i, result] of settled.entries()) {
|
|
28504
|
-
const hook = deduped[i];
|
|
28505
|
-
const registrationIndex = hook !== void 0 ? this.hooks.indexOf(hook) : -1;
|
|
28506
|
-
if (result.status === "rejected") {
|
|
28507
|
-
if (hook !== void 0) {
|
|
28508
|
-
this.deps.onExecutorError?.(hook, result.reason instanceof Error ? result.reason : new Error(String(result.reason)));
|
|
28509
|
-
this.deps.sink?.emit({
|
|
28510
|
-
type: "hook.resolved",
|
|
28511
|
-
hook_id: hookId(hook, registrationIndex),
|
|
28512
|
-
outcome: "error"
|
|
28513
|
-
});
|
|
28514
|
-
}
|
|
28515
|
-
continue;
|
|
28516
|
-
}
|
|
28517
|
-
const value = result.value;
|
|
28518
|
-
if (hook !== void 0) {
|
|
28519
|
-
const outcome = value?.ok === false ? "error" : value?.blockAction === true ? "blocked" : "ok";
|
|
28520
|
-
this.deps.sink?.emit({
|
|
28521
|
-
type: "hook.resolved",
|
|
28522
|
-
hook_id: hookId(hook, registrationIndex),
|
|
28523
|
-
outcome
|
|
28524
|
-
});
|
|
28525
|
-
}
|
|
28526
|
-
if (value === void 0) continue;
|
|
28527
|
-
if (value.blockAction) {
|
|
28528
|
-
blockAction = true;
|
|
28529
|
-
if (value.reason !== void 0) reason = value.reason;
|
|
28530
|
-
}
|
|
28531
|
-
if (value.additionalContext !== void 0) additionalContext.push(value.additionalContext);
|
|
28532
|
-
}
|
|
28533
|
-
return {
|
|
28534
|
-
blockAction,
|
|
28535
|
-
reason,
|
|
28536
|
-
additionalContext
|
|
28537
|
-
};
|
|
28538
|
-
}
|
|
28539
|
-
};
|
|
28540
|
-
/**
|
|
28541
|
-
* Collapse hook configs that share the same `command` string. `wire` hooks
|
|
28542
|
-
* (no `command` field) key on their subscription id so different wire
|
|
28543
|
-
* subscriptions still run in parallel; identical subscriptions collapse
|
|
28544
|
-
* like identical commands.
|
|
28545
|
-
*/
|
|
28546
|
-
function dedupeByCommand(hooks) {
|
|
28547
|
-
const seen = /* @__PURE__ */ new Map();
|
|
28548
|
-
for (const hook of hooks) {
|
|
28549
|
-
const key = hookDedupeKey(hook);
|
|
28550
|
-
if (!seen.has(key)) seen.set(key, hook);
|
|
28551
|
-
}
|
|
28552
|
-
return [...seen.values()];
|
|
28553
|
-
}
|
|
28554
|
-
function hookDedupeKey(hook) {
|
|
28555
|
-
if (hook.type === "command") return `command:${hook.command}`;
|
|
28556
|
-
if (hook.type === "wire") return `wire:${hook.subscriptionId}`;
|
|
28557
|
-
return `unknown:${JSON.stringify(hook)}`;
|
|
28558
|
-
}
|
|
28559
|
-
/**
|
|
28560
|
-
* Extracts the string fed to a hook's matcher regex. Event-dependent:
|
|
28561
|
-
*
|
|
28562
|
-
* - `PreToolUse` / `PostToolUse` / `OnToolFailure` — tool name
|
|
28563
|
-
* (mirrors Python's `matcher_value=toolCall.name` contract).
|
|
28564
|
-
* - `UserPromptSubmit` — the prompt text itself (Python parity).
|
|
28565
|
-
* - `Stop` — the turn reason (`done` / `cancelled` / `error`), so
|
|
28566
|
-
* hooks can filter e.g. `/^error$/`.
|
|
28567
|
-
* - `Notification` — the notification type string, so a single hook
|
|
28568
|
-
* can subscribe to an entire notification class via regex.
|
|
28569
|
-
*/
|
|
28570
|
-
function extractMatcherValue(input) {
|
|
28571
|
-
switch (input.event) {
|
|
28572
|
-
case "PreToolUse":
|
|
28573
|
-
case "PostToolUse":
|
|
28574
|
-
case "OnToolFailure": return input.toolCall.name;
|
|
28575
|
-
case "UserPromptSubmit": return input.prompt;
|
|
28576
|
-
case "Stop": return input.reason;
|
|
28577
|
-
case "Notification": return input.notificationType;
|
|
28578
|
-
case "StopFailure": return input.error;
|
|
28579
|
-
case "SubagentStart":
|
|
28580
|
-
case "SubagentStop": return input.agentName;
|
|
28581
|
-
case "SessionStart":
|
|
28582
|
-
case "SessionEnd":
|
|
28583
|
-
case "PreCompact":
|
|
28584
|
-
case "PostCompact": return "";
|
|
28585
|
-
}
|
|
28586
|
-
}
|
|
28587
29422
|
const _rawWireErrorSchema = z.object({
|
|
28588
29423
|
code: z.number(),
|
|
28589
29424
|
message: z.string(),
|
|
@@ -29388,6 +30223,13 @@ function errorMessage(error) {
|
|
|
29388
30223
|
}
|
|
29389
30224
|
//#endregion
|
|
29390
30225
|
//#region ../../packages/kimi-core/src/session/session-application-service.ts
|
|
30226
|
+
function withCompactionConfigFallback(bundle, fallback) {
|
|
30227
|
+
if (bundle.compactionConfig !== void 0 || fallback === void 0) return bundle;
|
|
30228
|
+
return {
|
|
30229
|
+
...bundle,
|
|
30230
|
+
compactionConfig: fallback
|
|
30231
|
+
};
|
|
30232
|
+
}
|
|
29391
30233
|
var DefaultSessionApplicationService = class {
|
|
29392
30234
|
constructor(deps) {
|
|
29393
30235
|
this.deps = deps;
|
|
@@ -29404,18 +30246,33 @@ var DefaultSessionApplicationService = class {
|
|
|
29404
30246
|
eventBus,
|
|
29405
30247
|
...hookEngine !== void 0 ? { hookEngine } : {}
|
|
29406
30248
|
});
|
|
29407
|
-
const systemPrompt = input.systemPrompt
|
|
30249
|
+
const systemPrompt = input.systemPrompt ?? this.deps.defaultSystemPromptProvider?.();
|
|
29408
30250
|
const enabledToolNames = this.deps.enabledToolNamesProvider?.();
|
|
30251
|
+
const model = input.model ?? this.deps.defaultModelProvider();
|
|
30252
|
+
const runtimeBundle = await this.deps.runtimeBundleProvider?.({
|
|
30253
|
+
sessionId,
|
|
30254
|
+
model
|
|
30255
|
+
});
|
|
30256
|
+
const compactionConfig = runtimeBundle?.compactionConfig ?? this.deps.compactionConfigProvider?.({
|
|
30257
|
+
sessionId,
|
|
30258
|
+
model
|
|
30259
|
+
});
|
|
30260
|
+
const runtimeSlotBundle = runtimeBundle !== void 0 ? withCompactionConfigFallback(runtimeBundle, compactionConfig) : void 0;
|
|
30261
|
+
const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
|
|
30262
|
+
const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
|
|
30263
|
+
const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
|
|
29409
30264
|
const managed = await this.deps.sessionManager.createSession({
|
|
29410
30265
|
sessionId,
|
|
29411
|
-
runtime
|
|
30266
|
+
runtime,
|
|
30267
|
+
...runtimeSlot !== void 0 ? { runtimeSlot } : {},
|
|
29412
30268
|
tools: this.deps.toolsProvider(),
|
|
29413
30269
|
...enabledToolNames !== void 0 ? { enabledToolNames } : {},
|
|
29414
|
-
model
|
|
30270
|
+
model,
|
|
29415
30271
|
...systemPrompt !== void 0 ? { systemPrompt } : {},
|
|
29416
30272
|
eventBus,
|
|
29417
30273
|
workspaceDir: this.deps.workspaceDir,
|
|
29418
|
-
compactionProvider
|
|
30274
|
+
compactionProvider,
|
|
30275
|
+
...compactionConfig !== void 0 ? { compactionConfig } : {},
|
|
29419
30276
|
...this.deps.approvalRuntime !== void 0 ? { approvalRuntime: this.deps.approvalRuntime } : {},
|
|
29420
30277
|
...this.deps.approvalRuntimeFactory !== void 0 ? { approvalRuntimeFactory: this.deps.approvalRuntimeFactory } : {},
|
|
29421
30278
|
...hookEngine !== void 0 ? { hookEngine } : {},
|
|
@@ -29441,11 +30298,26 @@ var DefaultSessionApplicationService = class {
|
|
|
29441
30298
|
eventBus,
|
|
29442
30299
|
...hookEngine !== void 0 ? { hookEngine } : {}
|
|
29443
30300
|
});
|
|
30301
|
+
const initialModel = this.deps.defaultModelProvider();
|
|
30302
|
+
const runtimeBundle = await this.deps.runtimeBundleProvider?.({
|
|
30303
|
+
sessionId,
|
|
30304
|
+
model: initialModel
|
|
30305
|
+
});
|
|
30306
|
+
const compactionConfig = runtimeBundle?.compactionConfig ?? this.deps.compactionConfigProvider?.({
|
|
30307
|
+
sessionId,
|
|
30308
|
+
model: initialModel
|
|
30309
|
+
});
|
|
30310
|
+
const runtimeSlotBundle = runtimeBundle !== void 0 ? withCompactionConfigFallback(runtimeBundle, compactionConfig) : void 0;
|
|
30311
|
+
const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
|
|
30312
|
+
const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
|
|
30313
|
+
const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
|
|
29444
30314
|
const managed = await this.deps.sessionManager.resumeSession(sessionId, {
|
|
29445
|
-
runtime
|
|
30315
|
+
runtime,
|
|
30316
|
+
...runtimeSlot !== void 0 ? { runtimeSlot } : {},
|
|
29446
30317
|
tools: this.deps.toolsProvider(),
|
|
29447
30318
|
eventBus,
|
|
29448
|
-
compactionProvider
|
|
30319
|
+
compactionProvider,
|
|
30320
|
+
...compactionConfig !== void 0 ? { compactionConfig } : {},
|
|
29449
30321
|
...this.deps.approvalRuntime !== void 0 ? { approvalRuntime: this.deps.approvalRuntime } : {},
|
|
29450
30322
|
...this.deps.approvalRuntimeFactory !== void 0 ? { approvalRuntimeFactory: this.deps.approvalRuntimeFactory } : {},
|
|
29451
30323
|
...hookEngine !== void 0 ? { hookEngine } : {},
|
|
@@ -29454,6 +30326,21 @@ var DefaultSessionApplicationService = class {
|
|
|
29454
30326
|
...this.deps.questionRuntimeProvider !== void 0 ? { questionRuntime: this.deps.questionRuntimeProvider({ sessionId }) } : {},
|
|
29455
30327
|
...this.deps.logger !== void 0 ? { logger: this.deps.logger } : {}
|
|
29456
30328
|
});
|
|
30329
|
+
const runtimeBundleProvider = this.deps.runtimeBundleProvider;
|
|
30330
|
+
if (runtimeSlot !== void 0 && runtimeBundleProvider !== void 0 && managed.contextState.model !== runtimeSlot.current().model) {
|
|
30331
|
+
const next = await runtimeBundleProvider({
|
|
30332
|
+
sessionId,
|
|
30333
|
+
model: managed.contextState.model
|
|
30334
|
+
});
|
|
30335
|
+
const nextConfig = next.compactionConfig ?? this.deps.compactionConfigProvider?.({
|
|
30336
|
+
sessionId,
|
|
30337
|
+
model: managed.contextState.model
|
|
30338
|
+
});
|
|
30339
|
+
runtimeSlot.replace(withCompactionConfigFallback(next, nextConfig));
|
|
30340
|
+
} else if (runtimeSlot === void 0) managed.soulPlus.getTurnManager().setCompactionConfig(this.deps.compactionConfigProvider?.({
|
|
30341
|
+
sessionId,
|
|
30342
|
+
model: managed.contextState.model
|
|
30343
|
+
}));
|
|
29457
30344
|
this.deps.sessionLifecycle?.onSessionCreated?.(managed);
|
|
29458
30345
|
return {
|
|
29459
30346
|
sessionId: managed.sessionId,
|
|
@@ -29557,6 +30444,15 @@ var DefaultSessionApplicationService = class {
|
|
|
29557
30444
|
async setPlanMode(sessionId, enabled) {
|
|
29558
30445
|
await this.getManaged(sessionId).sessionControl.setPlanMode(enabled);
|
|
29559
30446
|
}
|
|
30447
|
+
getPlanFilePath(sessionId) {
|
|
30448
|
+
return this.getManaged(sessionId).soulPlus.getPlanFilePath();
|
|
30449
|
+
}
|
|
30450
|
+
readCurrentPlan(sessionId) {
|
|
30451
|
+
return this.getManaged(sessionId).soulPlus.readCurrentPlan();
|
|
30452
|
+
}
|
|
30453
|
+
clearCurrentPlan(sessionId) {
|
|
30454
|
+
return this.getManaged(sessionId).soulPlus.clearCurrentPlan();
|
|
30455
|
+
}
|
|
29560
30456
|
async setYolo(sessionId, enabled) {
|
|
29561
30457
|
const managed = this.getManaged(sessionId);
|
|
29562
30458
|
await managed.sessionControl.setYolo(enabled);
|
|
@@ -29568,9 +30464,31 @@ var DefaultSessionApplicationService = class {
|
|
|
29568
30464
|
}
|
|
29569
30465
|
async setModel(sessionId, model) {
|
|
29570
30466
|
const managed = this.getManaged(sessionId);
|
|
29571
|
-
|
|
30467
|
+
const compactionConfig = this.deps.compactionConfigProvider?.({
|
|
30468
|
+
sessionId,
|
|
30469
|
+
model
|
|
30470
|
+
});
|
|
30471
|
+
if (this.deps.runtimeBundleProvider !== void 0) {
|
|
30472
|
+
const next = await this.deps.runtimeBundleProvider({
|
|
30473
|
+
sessionId,
|
|
30474
|
+
model
|
|
30475
|
+
});
|
|
30476
|
+
managed.runtimeSlot.replace(withCompactionConfigFallback(next, compactionConfig));
|
|
30477
|
+
} else if (this.deps.rebuildRuntimeForModel !== void 0) {
|
|
29572
30478
|
await this.deps.rebuildRuntimeForModel(sessionId, model);
|
|
29573
|
-
|
|
30479
|
+
managed.runtimeSlot.replace({
|
|
30480
|
+
model,
|
|
30481
|
+
runtime: this.deps.runtimeProvider(),
|
|
30482
|
+
compactionProvider: this.deps.compactionProviderProvider(),
|
|
30483
|
+
compactionConfig
|
|
30484
|
+
});
|
|
30485
|
+
} else {
|
|
30486
|
+
const current = managed.runtimeSlot.current();
|
|
30487
|
+
managed.runtimeSlot.replace({
|
|
30488
|
+
...current,
|
|
30489
|
+
model,
|
|
30490
|
+
compactionConfig
|
|
30491
|
+
});
|
|
29574
30492
|
}
|
|
29575
30493
|
await managed.soulPlus.setModel(model);
|
|
29576
30494
|
}
|
|
@@ -30012,6 +30930,8 @@ const SUPPORTED_WIRE_METHODS = [
|
|
|
30012
30930
|
"session.listSkills",
|
|
30013
30931
|
"session.activateSkill",
|
|
30014
30932
|
"session.setPlanMode",
|
|
30933
|
+
"session.getPlan",
|
|
30934
|
+
"session.clearPlan",
|
|
30015
30935
|
"session.setYolo",
|
|
30016
30936
|
"session.setModel",
|
|
30017
30937
|
"session.setThinking",
|
|
@@ -30132,6 +31052,8 @@ function registerDefaultWireHandlers(deps) {
|
|
|
30132
31052
|
defaultModelProvider: () => deps.defaultModelProvider?.() ?? defaultModel,
|
|
30133
31053
|
...deps.defaultSystemPromptProvider !== void 0 ? { defaultSystemPromptProvider: deps.defaultSystemPromptProvider } : {},
|
|
30134
31054
|
compactionProviderProvider: () => deps.compactionProviderProvider?.() ?? compactionProvider,
|
|
31055
|
+
...deps.compactionConfigProvider !== void 0 ? { compactionConfigProvider: deps.compactionConfigProvider } : {},
|
|
31056
|
+
...deps.runtimeBundleProvider !== void 0 ? { runtimeBundleProvider: deps.runtimeBundleProvider } : {},
|
|
30135
31057
|
eventBusProvider: () => eventBusProvider?.() ?? eventBus,
|
|
30136
31058
|
...approvalRuntime !== void 0 ? { approvalRuntime } : {},
|
|
30137
31059
|
...approvalRuntimeFactory !== void 0 ? { approvalRuntimeFactory } : {},
|
|
@@ -30288,8 +31210,16 @@ function registerDefaultWireHandlers(deps) {
|
|
|
30288
31210
|
data: { ok: true }
|
|
30289
31211
|
});
|
|
30290
31212
|
});
|
|
30291
|
-
router.registerMethod("session.prompt", "conversation", async (msg) => {
|
|
31213
|
+
router.registerMethod("session.prompt", "conversation", async (msg, _transport, session) => {
|
|
30292
31214
|
const payload = SessionPromptSchema.parse(msg.data ?? {});
|
|
31215
|
+
if (session?.contextState.model === "") return createWireResponse({
|
|
31216
|
+
requestId: msg.id,
|
|
31217
|
+
sessionId: msg.session_id,
|
|
31218
|
+
error: {
|
|
31219
|
+
code: -32001,
|
|
31220
|
+
message: "No LLM configured. Run /login to authenticate."
|
|
31221
|
+
}
|
|
31222
|
+
});
|
|
30293
31223
|
let inputText;
|
|
30294
31224
|
let inputParts;
|
|
30295
31225
|
if (typeof payload.input === "string") {
|
|
@@ -30527,10 +31457,39 @@ function registerDefaultWireHandlers(deps) {
|
|
|
30527
31457
|
router.registerMethod("session.setPlanMode", "config", async (msg, _t, session) => {
|
|
30528
31458
|
const payload = z.object({ enabled: z.boolean() }).parse(msg.data ?? {});
|
|
30529
31459
|
await sessionApplication.setPlanMode(msg.session_id, payload.enabled);
|
|
31460
|
+
const planPath = sessionApplication.getPlanFilePath(msg.session_id);
|
|
30530
31461
|
return createWireResponse({
|
|
30531
31462
|
requestId: msg.id,
|
|
30532
31463
|
sessionId: msg.session_id,
|
|
30533
|
-
data: {
|
|
31464
|
+
data: {
|
|
31465
|
+
ok: true,
|
|
31466
|
+
...planPath !== null ? { plan_path: planPath } : {}
|
|
31467
|
+
}
|
|
31468
|
+
});
|
|
31469
|
+
});
|
|
31470
|
+
router.registerMethod("session.getPlan", "config", async (msg) => {
|
|
31471
|
+
const current = await sessionApplication.readCurrentPlan(msg.session_id);
|
|
31472
|
+
return createWireResponse({
|
|
31473
|
+
requestId: msg.id,
|
|
31474
|
+
sessionId: msg.session_id,
|
|
31475
|
+
data: {
|
|
31476
|
+
ok: true,
|
|
31477
|
+
...current !== null ? {
|
|
31478
|
+
plan: current.plan,
|
|
31479
|
+
path: current.path
|
|
31480
|
+
} : {}
|
|
31481
|
+
}
|
|
31482
|
+
});
|
|
31483
|
+
});
|
|
31484
|
+
router.registerMethod("session.clearPlan", "config", async (msg) => {
|
|
31485
|
+
const path = await sessionApplication.clearCurrentPlan(msg.session_id);
|
|
31486
|
+
return createWireResponse({
|
|
31487
|
+
requestId: msg.id,
|
|
31488
|
+
sessionId: msg.session_id,
|
|
31489
|
+
data: {
|
|
31490
|
+
ok: true,
|
|
31491
|
+
...path !== null ? { path } : {}
|
|
31492
|
+
}
|
|
30534
31493
|
});
|
|
30535
31494
|
});
|
|
30536
31495
|
router.registerMethod("session.setYolo", "config", async (msg, _t, session) => {
|
|
@@ -30740,7 +31699,10 @@ function createWireQuestionRuntime(reverse, sessionId) {
|
|
|
30740
31699
|
questions: req.questions.map((q) => ({
|
|
30741
31700
|
question: q.question,
|
|
30742
31701
|
...q.header !== void 0 ? { header: q.header } : {},
|
|
31702
|
+
...q.body !== void 0 ? { body: q.body } : {},
|
|
30743
31703
|
multi_select: q.multiSelect ?? false,
|
|
31704
|
+
...q.otherLabel !== void 0 ? { other_label: q.otherLabel } : {},
|
|
31705
|
+
...q.otherDescription !== void 0 ? { other_description: q.otherDescription } : {},
|
|
30744
31706
|
options: q.options.map((o) => ({
|
|
30745
31707
|
label: o.label,
|
|
30746
31708
|
...o.description !== void 0 ? { description: o.description } : {}
|
|
@@ -31421,7 +32383,7 @@ function projectReplayState(records, sessionInitialized) {
|
|
|
31421
32383
|
case "step_end":
|
|
31422
32384
|
if (openStep !== null && openStep.stepUuid === r.uuid) {
|
|
31423
32385
|
openStep.hasStepEnd = true;
|
|
31424
|
-
if (r.usage !== void 0) tokenCount
|
|
32386
|
+
if (r.usage !== void 0) tokenCount = r.usage.input_tokens + r.usage.output_tokens;
|
|
31425
32387
|
}
|
|
31426
32388
|
flushOpenStep();
|
|
31427
32389
|
break;
|
|
@@ -31688,6 +32650,15 @@ function bindTodoStore(tools, todoStore) {
|
|
|
31688
32650
|
return tool;
|
|
31689
32651
|
});
|
|
31690
32652
|
}
|
|
32653
|
+
function normalizeRuntimeSlot(slot, fallback) {
|
|
32654
|
+
const runtimeSlot = slot ?? createSessionRuntimeSlot(fallback);
|
|
32655
|
+
const current = runtimeSlot.current();
|
|
32656
|
+
if (current.compactionConfig === void 0 && fallback.compactionConfig !== void 0) runtimeSlot.replace({
|
|
32657
|
+
...current,
|
|
32658
|
+
compactionConfig: fallback.compactionConfig
|
|
32659
|
+
});
|
|
32660
|
+
return runtimeSlot;
|
|
32661
|
+
}
|
|
31691
32662
|
var SessionManager = class {
|
|
31692
32663
|
paths;
|
|
31693
32664
|
producer;
|
|
@@ -31746,6 +32717,13 @@ var SessionManager = class {
|
|
|
31746
32717
|
const stateCache = new StateCache(this.paths.statePath(sessionId));
|
|
31747
32718
|
const todoStore = new SessionTodoStore(stateCache, []);
|
|
31748
32719
|
const sessionTools = bindTodoStore(options.tools, todoStore);
|
|
32720
|
+
const runtimeSlot = normalizeRuntimeSlot(options.runtimeSlot, {
|
|
32721
|
+
model: options.model,
|
|
32722
|
+
runtime: options.runtime,
|
|
32723
|
+
compactionProvider: options.compactionProvider,
|
|
32724
|
+
...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {}
|
|
32725
|
+
});
|
|
32726
|
+
const runtimeBundle = runtimeSlot.current();
|
|
31749
32727
|
const mainInitInput = {
|
|
31750
32728
|
type: "session_initialized",
|
|
31751
32729
|
agent_type: "main",
|
|
@@ -31796,7 +32774,8 @@ var SessionManager = class {
|
|
|
31796
32774
|
sessionId,
|
|
31797
32775
|
contextState,
|
|
31798
32776
|
sessionJournal,
|
|
31799
|
-
|
|
32777
|
+
runtimeSlot,
|
|
32778
|
+
runtime: runtimeBundle.runtime,
|
|
31800
32779
|
eventBus,
|
|
31801
32780
|
tools: sessionTools,
|
|
31802
32781
|
...options.enabledToolNames !== void 0 ? { enabledToolNames: options.enabledToolNames } : {},
|
|
@@ -31805,8 +32784,8 @@ var SessionManager = class {
|
|
|
31805
32784
|
...options.onShellDeliver !== void 0 ? { onShellDeliver: options.onShellDeliver } : {},
|
|
31806
32785
|
...options.skillManager !== void 0 ? { skillManager: options.skillManager } : {},
|
|
31807
32786
|
...options.questionRuntime !== void 0 ? { questionRuntime: options.questionRuntime } : {},
|
|
31808
|
-
...
|
|
31809
|
-
compactionProvider:
|
|
32787
|
+
...runtimeBundle.compactionConfig !== void 0 ? { compactionConfig: runtimeBundle.compactionConfig } : {},
|
|
32788
|
+
compactionProvider: runtimeBundle.compactionProvider,
|
|
31810
32789
|
journalCapability: options.journalCapability ?? createWiredJournalCapability({
|
|
31811
32790
|
sessionDir,
|
|
31812
32791
|
journalWriter,
|
|
@@ -31838,13 +32817,15 @@ var SessionManager = class {
|
|
|
31838
32817
|
sessionControl: new DefaultSessionControl({
|
|
31839
32818
|
turnManager: turnManagerRef,
|
|
31840
32819
|
contextState,
|
|
31841
|
-
sessionJournal
|
|
32820
|
+
sessionJournal,
|
|
32821
|
+
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled)
|
|
31842
32822
|
}),
|
|
31843
32823
|
contextState,
|
|
31844
32824
|
eventBus,
|
|
31845
32825
|
stateCache,
|
|
31846
32826
|
sessionJournal,
|
|
31847
32827
|
journalWriter,
|
|
32828
|
+
runtimeSlot,
|
|
31848
32829
|
lifecycleStateMachine,
|
|
31849
32830
|
todoStore
|
|
31850
32831
|
};
|
|
@@ -31909,6 +32890,13 @@ var SessionManager = class {
|
|
|
31909
32890
|
const initialTodos = isClean ? cloneTodos(stateData?.todos ?? projected.todos) : cloneTodos(projected.todos);
|
|
31910
32891
|
const todoStore = new SessionTodoStore(stateCache, initialTodos);
|
|
31911
32892
|
const sessionTools = bindTodoStore(options.tools, todoStore);
|
|
32893
|
+
const runtimeSlot = normalizeRuntimeSlot(options.runtimeSlot, {
|
|
32894
|
+
model: projected.model,
|
|
32895
|
+
runtime: options.runtime,
|
|
32896
|
+
compactionProvider: options.compactionProvider,
|
|
32897
|
+
...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {}
|
|
32898
|
+
});
|
|
32899
|
+
const runtimeBundle = runtimeSlot.current();
|
|
31912
32900
|
if (stateData !== null) await stateCache.write({
|
|
31913
32901
|
...stateData,
|
|
31914
32902
|
status: "active",
|
|
@@ -31923,6 +32911,7 @@ var SessionManager = class {
|
|
|
31923
32911
|
const pickDescription = isClean ? stateData?.description ?? replayedMeta.description : replayedMeta.description ?? stateData?.description;
|
|
31924
32912
|
const pickArchived = isClean ? stateData?.archived ?? replayedMeta.archived : replayedMeta.archived ?? stateData?.archived;
|
|
31925
32913
|
const pickLastModel = isClean ? stateData?.model ?? replayedMeta.last_model : replayedMeta.last_model ?? stateData?.model;
|
|
32914
|
+
const pickPlanSlug = isClean ? stateData?.plan_slug ?? replayedMeta.plan_slug : replayedMeta.plan_slug ?? stateData?.plan_slug;
|
|
31926
32915
|
const initialMeta = {
|
|
31927
32916
|
session_id: sessionId,
|
|
31928
32917
|
created_at: stateData?.created_at ?? now,
|
|
@@ -31933,6 +32922,7 @@ var SessionManager = class {
|
|
|
31933
32922
|
...pickDescription !== void 0 ? { description: pickDescription } : {},
|
|
31934
32923
|
...pickArchived !== void 0 ? { archived: pickArchived } : {},
|
|
31935
32924
|
...pickLastModel !== void 0 ? { last_model: pickLastModel } : {},
|
|
32925
|
+
...pickPlanSlug !== void 0 ? { plan_slug: pickPlanSlug } : {},
|
|
31936
32926
|
producer: replayResult.producer,
|
|
31937
32927
|
last_exit_code: "dirty"
|
|
31938
32928
|
};
|
|
@@ -31951,7 +32941,8 @@ var SessionManager = class {
|
|
|
31951
32941
|
sessionId,
|
|
31952
32942
|
contextState,
|
|
31953
32943
|
sessionJournal,
|
|
31954
|
-
|
|
32944
|
+
runtimeSlot,
|
|
32945
|
+
runtime: runtimeBundle.runtime,
|
|
31955
32946
|
eventBus,
|
|
31956
32947
|
tools: sessionTools,
|
|
31957
32948
|
...options.enabledToolNames !== void 0 ? { enabledToolNames: options.enabledToolNames } : {},
|
|
@@ -31960,8 +32951,8 @@ var SessionManager = class {
|
|
|
31960
32951
|
...options.onShellDeliver !== void 0 ? { onShellDeliver: options.onShellDeliver } : {},
|
|
31961
32952
|
...options.skillManager !== void 0 ? { skillManager: options.skillManager } : {},
|
|
31962
32953
|
...options.questionRuntime !== void 0 ? { questionRuntime: options.questionRuntime } : {},
|
|
31963
|
-
...
|
|
31964
|
-
compactionProvider:
|
|
32954
|
+
...runtimeBundle.compactionConfig !== void 0 ? { compactionConfig: runtimeBundle.compactionConfig } : {},
|
|
32955
|
+
compactionProvider: runtimeBundle.compactionProvider,
|
|
31965
32956
|
journalCapability: options.journalCapability ?? createWiredJournalCapability({
|
|
31966
32957
|
sessionDir,
|
|
31967
32958
|
journalWriter,
|
|
@@ -31988,7 +32979,10 @@ var SessionManager = class {
|
|
|
31988
32979
|
await soulPlus.init();
|
|
31989
32980
|
turnManagerRef.setPermissionMode(effectivePermissionMode);
|
|
31990
32981
|
if (projected.thinkingLevel !== void 0) turnManagerRef.setThinkingLevel(projected.thinkingLevel);
|
|
31991
|
-
if (projected.planMode)
|
|
32982
|
+
if (projected.planMode) {
|
|
32983
|
+
await soulPlus.ensurePlanFilePath();
|
|
32984
|
+
turnManagerRef.setPlanMode(true);
|
|
32985
|
+
}
|
|
31992
32986
|
const notificationRecords = replayResult.records.filter((r) => r.type === "notification");
|
|
31993
32987
|
if (notificationRecords.length > 0) soulPlus.getNotificationManager().primeDedupeIndex(notificationRecords);
|
|
31994
32988
|
for (const agentId of lostSubagentIds) await soulPlus.emitNotification({
|
|
@@ -32007,13 +33001,15 @@ var SessionManager = class {
|
|
|
32007
33001
|
sessionControl: new DefaultSessionControl({
|
|
32008
33002
|
turnManager: turnManagerRef,
|
|
32009
33003
|
contextState,
|
|
32010
|
-
sessionJournal
|
|
33004
|
+
sessionJournal,
|
|
33005
|
+
setPlanModeOverride: (enabled) => soulPlus.setPlanMode(enabled)
|
|
32011
33006
|
}),
|
|
32012
33007
|
contextState,
|
|
32013
33008
|
eventBus,
|
|
32014
33009
|
stateCache,
|
|
32015
33010
|
sessionJournal,
|
|
32016
33011
|
journalWriter,
|
|
33012
|
+
runtimeSlot,
|
|
32017
33013
|
lifecycleStateMachine,
|
|
32018
33014
|
todoStore
|
|
32019
33015
|
};
|
|
@@ -32263,6 +33259,7 @@ var SessionManager = class {
|
|
|
32263
33259
|
...meta?.description !== void 0 ? { description: meta.description } : {},
|
|
32264
33260
|
...meta?.archived !== void 0 ? { archived: meta.archived } : {},
|
|
32265
33261
|
...meta?.last_model !== void 0 ? { model: meta.last_model } : {},
|
|
33262
|
+
...meta?.plan_slug !== void 0 ? { plan_slug: meta.plan_slug } : {},
|
|
32266
33263
|
...currentPlanMode ? { plan_mode: true } : { plan_mode: false },
|
|
32267
33264
|
todos: cloneTodos(currentTodos),
|
|
32268
33265
|
last_exit_code: "clean"
|
|
@@ -39213,7 +40210,7 @@ async function loadAgentBlock(filePath, visited = /* @__PURE__ */ new Set()) {
|
|
|
39213
40210
|
}
|
|
39214
40211
|
async function readAgentBlock(absPath) {
|
|
39215
40212
|
const record = asRecord$1(load$2(await readFile(absPath, "utf-8")), `agent yaml root (${absPath})`);
|
|
39216
|
-
if (isRecord$
|
|
40213
|
+
if (isRecord$5(record["agent"])) throw new Error(`Invalid agent YAML: ${absPath} uses an unsupported 'agent:' wrapper. Use TS-native root fields.`);
|
|
39217
40214
|
return normalizeNativeAgent(record, absPath);
|
|
39218
40215
|
}
|
|
39219
40216
|
function normalizeNativeAgent(raw, source) {
|
|
@@ -39348,11 +40345,11 @@ async function getBundledAgentYamlPath() {
|
|
|
39348
40345
|
} catch {}
|
|
39349
40346
|
throw new Error(`getBundledAgentYamlPath: unable to locate bundled agent.yaml. Checked: ${candidates.join(", ")}`);
|
|
39350
40347
|
}
|
|
39351
|
-
function isRecord$
|
|
40348
|
+
function isRecord$5(value) {
|
|
39352
40349
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39353
40350
|
}
|
|
39354
40351
|
function asRecord$1(value, label) {
|
|
39355
|
-
if (!isRecord$
|
|
40352
|
+
if (!isRecord$5(value)) throw new Error(`Invalid agent YAML: ${label} must be a mapping`);
|
|
39356
40353
|
return value;
|
|
39357
40354
|
}
|
|
39358
40355
|
function asOptionalRecord(value, label) {
|
|
@@ -40653,6 +41650,10 @@ const ThinkingConfigSchema = z.object({
|
|
|
40653
41650
|
]).optional(),
|
|
40654
41651
|
effort: z.string().optional()
|
|
40655
41652
|
});
|
|
41653
|
+
const LoopControlSchema = z.object({
|
|
41654
|
+
reservedContextSize: z.number().int().min(0).optional(),
|
|
41655
|
+
compactionTriggerRatio: z.number().min(.5).max(.99).optional()
|
|
41656
|
+
});
|
|
40656
41657
|
const MoonshotServiceConfigSchema = z.object({
|
|
40657
41658
|
baseUrl: z.string().optional(),
|
|
40658
41659
|
apiKey: z.string().optional(),
|
|
@@ -40680,6 +41681,7 @@ const KimiConfigSchema = z.object({
|
|
|
40680
41681
|
services: ServicesConfigSchema.optional(),
|
|
40681
41682
|
mergeAllAvailableSkills: z.boolean().optional(),
|
|
40682
41683
|
showThinkingStream: z.boolean().optional(),
|
|
41684
|
+
loopControl: LoopControlSchema.optional(),
|
|
40683
41685
|
raw: z.record(z.string(), z.unknown()).optional()
|
|
40684
41686
|
});
|
|
40685
41687
|
//#endregion
|
|
@@ -40861,6 +41863,12 @@ function serviceToToml(service, rawService) {
|
|
|
40861
41863
|
if (service?.customHeaders !== void 0) out["custom_headers"] = service.customHeaders;
|
|
40862
41864
|
return out;
|
|
40863
41865
|
}
|
|
41866
|
+
function loopControlToToml(loopControl, rawLoopControl) {
|
|
41867
|
+
const out = cloneRecord(rawLoopControl);
|
|
41868
|
+
setDefined(out, "reserved_context_size", loopControl.reservedContextSize);
|
|
41869
|
+
setDefined(out, "compaction_trigger_ratio", loopControl.compactionTriggerRatio);
|
|
41870
|
+
return out;
|
|
41871
|
+
}
|
|
40864
41872
|
function configToTomlData(config) {
|
|
40865
41873
|
const out = cloneRecord(config.raw);
|
|
40866
41874
|
setDefined(out, "default_provider", config.defaultProvider);
|
|
@@ -40885,6 +41893,7 @@ function configToTomlData(config) {
|
|
|
40885
41893
|
if (config.services?.moonshotSearch !== void 0) services["moonshot_search"] = serviceToToml(config.services.moonshotSearch, services["moonshot_search"]);
|
|
40886
41894
|
if (config.services?.moonshotFetch !== void 0) services["moonshot_fetch"] = serviceToToml(config.services.moonshotFetch, services["moonshot_fetch"]);
|
|
40887
41895
|
if (Object.keys(services).length > 0) out["services"] = services;
|
|
41896
|
+
if (config.loopControl !== void 0) out["loop_control"] = loopControlToToml(config.loopControl, out["loop_control"]);
|
|
40888
41897
|
return out;
|
|
40889
41898
|
}
|
|
40890
41899
|
//#endregion
|
|
@@ -77282,7 +78291,7 @@ function createVideoUploader(provider) {
|
|
|
77282
78291
|
}
|
|
77283
78292
|
function resolveDefaultSoulPlusDefaultModel(options) {
|
|
77284
78293
|
const defaultModel = options.defaultModel ?? options.modelAlias ?? options.kimiConfig?.defaultModel;
|
|
77285
|
-
if (defaultModel === void 0 || defaultModel.length === 0)
|
|
78294
|
+
if (defaultModel === void 0 || defaultModel.length === 0) return "";
|
|
77286
78295
|
return defaultModel;
|
|
77287
78296
|
}
|
|
77288
78297
|
function getDefaultSoulPlusMaxContextSize(options, defaultModel) {
|
|
@@ -77298,6 +78307,12 @@ async function createDefaultSoulPlusRuntimeBundle(options) {
|
|
|
77298
78307
|
maxContextSize
|
|
77299
78308
|
};
|
|
77300
78309
|
if (options.runtime !== void 0 || options.compactionProvider !== void 0) throw new Error("createDefaultSoulPlusWireClient: runtime and compactionProvider must be provided together");
|
|
78310
|
+
if (defaultModel.length === 0) return {
|
|
78311
|
+
runtime: createRuntime({ kosong: new UnconfiguredKosongAdapter() }),
|
|
78312
|
+
compactionProvider: new UnconfiguredCompactionProvider(),
|
|
78313
|
+
defaultModel,
|
|
78314
|
+
maxContextSize
|
|
78315
|
+
};
|
|
77301
78316
|
if (options.kimiConfig === void 0) throw new Error("createDefaultSoulPlusWireClient: provide either runtime+compactionProvider or kimiConfig");
|
|
77302
78317
|
const provider = await createProviderFromConfig(options.kimiConfig, defaultModel, {
|
|
77303
78318
|
...options.oauthResolver !== void 0 ? { oauthResolver: options.oauthResolver } : {},
|
|
@@ -77316,6 +78331,17 @@ async function createDefaultSoulPlusRuntimeBundle(options) {
|
|
|
77316
78331
|
...videoUploader !== void 0 ? { videoUploader } : {}
|
|
77317
78332
|
};
|
|
77318
78333
|
}
|
|
78334
|
+
const LLM_NOT_CONFIGURED_MESSAGE = "No LLM configured. Run /login to authenticate.";
|
|
78335
|
+
var UnconfiguredKosongAdapter = class {
|
|
78336
|
+
async chat(_params) {
|
|
78337
|
+
throw new LLMNotSetError(LLM_NOT_CONFIGURED_MESSAGE);
|
|
78338
|
+
}
|
|
78339
|
+
};
|
|
78340
|
+
var UnconfiguredCompactionProvider = class {
|
|
78341
|
+
async run() {
|
|
78342
|
+
throw new LLMNotSetError(LLM_NOT_CONFIGURED_MESSAGE);
|
|
78343
|
+
}
|
|
78344
|
+
};
|
|
77319
78345
|
//#endregion
|
|
77320
78346
|
//#region ../../packages/kaos/src/errors.ts
|
|
77321
78347
|
/**
|
|
@@ -90347,7 +91373,7 @@ function tokenFromWire(wire) {
|
|
|
90347
91373
|
* Load semantics: missing file → undefined. Corrupt JSON / wrong shape →
|
|
90348
91374
|
* undefined (never throws). Callers treat undefined as "no token stored".
|
|
90349
91375
|
*/
|
|
90350
|
-
function isRecord$
|
|
91376
|
+
function isRecord$4(value) {
|
|
90351
91377
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
90352
91378
|
}
|
|
90353
91379
|
var FileTokenStorage = class {
|
|
@@ -90383,7 +91409,7 @@ var FileTokenStorage = class {
|
|
|
90383
91409
|
} catch {
|
|
90384
91410
|
return;
|
|
90385
91411
|
}
|
|
90386
|
-
if (!isRecord$
|
|
91412
|
+
if (!isRecord$4(parsed)) return void 0;
|
|
90387
91413
|
return tokenFromWire(parsed);
|
|
90388
91414
|
}
|
|
90389
91415
|
async save(name, token) {
|
|
@@ -90522,7 +91548,7 @@ const RETRYABLE_STATUSES = new Set([
|
|
|
90522
91548
|
503,
|
|
90523
91549
|
504
|
|
90524
91550
|
]);
|
|
90525
|
-
function isRecord$
|
|
91551
|
+
function isRecord$3(value) {
|
|
90526
91552
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
90527
91553
|
}
|
|
90528
91554
|
function pickErrorDetail(data) {
|
|
@@ -90576,7 +91602,7 @@ async function postForm(url, params, deviceHeaders, options) {
|
|
|
90576
91602
|
const text = await response.text();
|
|
90577
91603
|
if (text.length > 0) {
|
|
90578
91604
|
const parsed = JSON.parse(text);
|
|
90579
|
-
if (isRecord$
|
|
91605
|
+
if (isRecord$3(parsed)) data = parsed;
|
|
90580
91606
|
}
|
|
90581
91607
|
} catch {}
|
|
90582
91608
|
return {
|
|
@@ -92211,9 +93237,9 @@ function parseManagedUsagePayload(payload) {
|
|
|
92211
93237
|
const item = rawLimits[idx];
|
|
92212
93238
|
if (!item || typeof item !== "object") continue;
|
|
92213
93239
|
const detailRaw = item["detail"];
|
|
92214
|
-
const detail = isRecord$
|
|
93240
|
+
const detail = isRecord$2(detailRaw) ? detailRaw : item;
|
|
92215
93241
|
const windowRaw = item["window"];
|
|
92216
|
-
const row = toUsageRow(detail, limitLabel(item, detail, isRecord$
|
|
93242
|
+
const row = toUsageRow(detail, limitLabel(item, detail, isRecord$2(windowRaw) ? windowRaw : {}, idx));
|
|
92217
93243
|
if (row !== null) limits.push(row);
|
|
92218
93244
|
}
|
|
92219
93245
|
return {
|
|
@@ -92222,7 +93248,7 @@ function parseManagedUsagePayload(payload) {
|
|
|
92222
93248
|
};
|
|
92223
93249
|
}
|
|
92224
93250
|
function toUsageRow(raw, defaultLabel) {
|
|
92225
|
-
if (!isRecord$
|
|
93251
|
+
if (!isRecord$2(raw)) return null;
|
|
92226
93252
|
const limit = toInt(raw["limit"]);
|
|
92227
93253
|
let used = toInt(raw["used"]);
|
|
92228
93254
|
if (used === null) {
|
|
@@ -92316,7 +93342,7 @@ function toInt(value) {
|
|
|
92316
93342
|
}
|
|
92317
93343
|
return null;
|
|
92318
93344
|
}
|
|
92319
|
-
function isRecord$
|
|
93345
|
+
function isRecord$2(v) {
|
|
92320
93346
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
92321
93347
|
}
|
|
92322
93348
|
async function fetchManagedUsage(url, accessToken, opts = {}) {
|
|
@@ -92382,11 +93408,11 @@ function capabilitiesForModel(model) {
|
|
|
92382
93408
|
function defaultBaseUrl(baseUrl) {
|
|
92383
93409
|
return (baseUrl ?? kimiCodeBaseUrl()).replace(/\/+$/, "");
|
|
92384
93410
|
}
|
|
92385
|
-
function isRecord(value) {
|
|
93411
|
+
function isRecord$1(value) {
|
|
92386
93412
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
92387
93413
|
}
|
|
92388
93414
|
function toModelInfo(item) {
|
|
92389
|
-
if (!isRecord(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
|
|
93415
|
+
if (!isRecord$1(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
|
|
92390
93416
|
const displayName = item["display_name"];
|
|
92391
93417
|
return {
|
|
92392
93418
|
id: item["id"],
|
|
@@ -92406,7 +93432,7 @@ async function fetchManagedKimiCodeModels(options) {
|
|
|
92406
93432
|
} });
|
|
92407
93433
|
if (!response.ok) throw new Error(`Failed to list Kimi Code models (HTTP ${response.status}).`);
|
|
92408
93434
|
const payload = await response.json();
|
|
92409
|
-
if (!isRecord(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
|
|
93435
|
+
if (!isRecord$1(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
|
|
92410
93436
|
return payload["data"].map((item) => toModelInfo(item)).filter((item) => item !== void 0);
|
|
92411
93437
|
}
|
|
92412
93438
|
function applyManagedKimiCodeConfig(config, options) {
|
|
@@ -92730,6 +93756,7 @@ var DefaultSoulPlusOAuthServiceImpl = class {
|
|
|
92730
93756
|
this.oauthResolver = async (providerName) => this.managerFor(providerName).ensureFresh();
|
|
92731
93757
|
}
|
|
92732
93758
|
prepareRequiredManagers() {
|
|
93759
|
+
if (this.options.forceManagedKimiCodeOAuth === true) this.managerFor(KIMI_CODE_PROVIDER_NAME);
|
|
92733
93760
|
const selected = this.resolveSelectedProvider();
|
|
92734
93761
|
if (selected.selectedProviderNeedsOAuth && selected.providerName !== "managed:kimi-code") throw new Error(`OAuth provider "${selected.providerName}" is not yet supported. Only managed:kimi-code device-code OAuth is wired for the default SoulPlus host.`);
|
|
92735
93762
|
if (selected.providerName === "managed:kimi-code" && selected.selectedProviderNeedsOAuth) this.managerFor(KIMI_CODE_PROVIDER_NAME);
|
|
@@ -92757,6 +93784,7 @@ var DefaultSoulPlusOAuthServiceImpl = class {
|
|
|
92757
93784
|
pathConfig: this.options.pathConfig,
|
|
92758
93785
|
accessToken
|
|
92759
93786
|
});
|
|
93787
|
+
await this.options.onConfigProvisioned?.(loadConfig({ pathConfig: this.options.pathConfig }));
|
|
92760
93788
|
return {
|
|
92761
93789
|
provider_name: name,
|
|
92762
93790
|
ok: true
|
|
@@ -93330,6 +94358,12 @@ var KimiWireClient = class {
|
|
|
93330
94358
|
setPlanMode(sessionId, enabled) {
|
|
93331
94359
|
return this.request("session.setPlanMode", { enabled }, { sessionId });
|
|
93332
94360
|
}
|
|
94361
|
+
getPlan(sessionId) {
|
|
94362
|
+
return this.request("session.getPlan", {}, { sessionId });
|
|
94363
|
+
}
|
|
94364
|
+
clearPlan(sessionId) {
|
|
94365
|
+
return this.request("session.clearPlan", {}, { sessionId });
|
|
94366
|
+
}
|
|
93333
94367
|
setYolo(sessionId, enabled) {
|
|
93334
94368
|
return this.request("session.setYolo", { enabled }, { sessionId });
|
|
93335
94369
|
}
|
|
@@ -93547,6 +94581,8 @@ async function startCoreWireServer(options) {
|
|
|
93547
94581
|
...options.runtimeProvider !== void 0 ? { runtimeProvider: options.runtimeProvider } : {},
|
|
93548
94582
|
...options.defaultModelProvider !== void 0 ? { defaultModelProvider: options.defaultModelProvider } : {},
|
|
93549
94583
|
...options.compactionProviderProvider !== void 0 ? { compactionProviderProvider: options.compactionProviderProvider } : {},
|
|
94584
|
+
...options.compactionConfigProvider !== void 0 ? { compactionConfigProvider: options.compactionConfigProvider } : {},
|
|
94585
|
+
...options.runtimeBundleProvider !== void 0 ? { runtimeBundleProvider: options.runtimeBundleProvider } : {},
|
|
93550
94586
|
...options.rebuildRuntimeForModel !== void 0 ? { rebuildRuntimeForModel: options.rebuildRuntimeForModel } : {},
|
|
93551
94587
|
...options.defaultSystemPromptProvider !== void 0 ? { defaultSystemPromptProvider: options.defaultSystemPromptProvider } : {},
|
|
93552
94588
|
pathConfig: options.pathConfig,
|
|
@@ -93630,38 +94666,71 @@ async function createInProcessWireClient(options) {
|
|
|
93630
94666
|
}
|
|
93631
94667
|
//#endregion
|
|
93632
94668
|
//#region ../../packages/kimi-sdk/src/default-soul-plus-wire-client.ts
|
|
94669
|
+
const DEFAULT_RESERVED_CONTEXT_SIZE = 5e4;
|
|
94670
|
+
const DEFAULT_RESERVED_CONTEXT_FRACTION = .25;
|
|
93633
94671
|
async function createDefaultSoulPlusWireClient(options) {
|
|
93634
94672
|
const pathConfig = options.pathConfig ?? new PathConfig();
|
|
93635
|
-
const
|
|
94673
|
+
const loadHostConfig = () => options.kimiConfig ?? loadConfig({
|
|
93636
94674
|
pathConfig,
|
|
93637
94675
|
workspaceDir: options.workspaceDir
|
|
93638
94676
|
});
|
|
93639
|
-
const
|
|
93640
|
-
|
|
93641
|
-
|
|
93642
|
-
|
|
93643
|
-
|
|
93644
|
-
|
|
93645
|
-
|
|
93646
|
-
|
|
93647
|
-
|
|
93648
|
-
|
|
93649
|
-
|
|
94677
|
+
const resolveHostConfig = (config) => {
|
|
94678
|
+
const requestedDefaultModel = resolveDefaultSoulPlusDefaultModel({
|
|
94679
|
+
...options,
|
|
94680
|
+
kimiConfig: config
|
|
94681
|
+
});
|
|
94682
|
+
const effective = resolveEffectiveConfig(config, { requestedModel: requestedDefaultModel });
|
|
94683
|
+
return {
|
|
94684
|
+
effectiveConfig: effective,
|
|
94685
|
+
defaultModel: resolveDefaultSoulPlusDefaultModel({
|
|
94686
|
+
...options,
|
|
94687
|
+
kimiConfig: effective,
|
|
94688
|
+
defaultModel: requestedDefaultModel
|
|
94689
|
+
})
|
|
94690
|
+
};
|
|
94691
|
+
};
|
|
94692
|
+
let { effectiveConfig, defaultModel } = resolveHostConfig(loadHostConfig());
|
|
94693
|
+
let authService;
|
|
94694
|
+
let activeVideoUploader;
|
|
94695
|
+
let activeDefaultModel = defaultModel;
|
|
94696
|
+
let activeMaxContextSize = 2e5;
|
|
94697
|
+
const buildRuntimeBundle = async (model) => {
|
|
94698
|
+
const next = await createDefaultSoulPlusRuntimeBundle({
|
|
94699
|
+
...options,
|
|
94700
|
+
kimiConfig: effectiveConfig,
|
|
94701
|
+
defaultModel: model,
|
|
94702
|
+
oauthResolver: authService.oauthResolver,
|
|
94703
|
+
deferOAuth: true,
|
|
94704
|
+
tokenRefresher: authService
|
|
94705
|
+
});
|
|
94706
|
+
activeVideoUploader = next.videoUploader;
|
|
94707
|
+
return {
|
|
94708
|
+
model: next.defaultModel,
|
|
94709
|
+
runtime: next.runtime,
|
|
94710
|
+
compactionProvider: next.compactionProvider,
|
|
94711
|
+
compactionConfig: buildCompactionConfig(effectiveConfig, next.defaultModel, next.maxContextSize)
|
|
94712
|
+
};
|
|
94713
|
+
};
|
|
94714
|
+
const reloadConfigFromDisk = async () => {
|
|
94715
|
+
if (options.kimiConfig !== void 0) return;
|
|
94716
|
+
const resolved = resolveHostConfig(loadHostConfig());
|
|
94717
|
+
effectiveConfig = resolved.effectiveConfig;
|
|
94718
|
+
defaultModel = resolved.defaultModel;
|
|
94719
|
+
activeDefaultModel = defaultModel;
|
|
94720
|
+
activeMaxContextSize = getDefaultSoulPlusMaxContextSize({
|
|
94721
|
+
kimiConfig: effectiveConfig,
|
|
94722
|
+
defaultModel
|
|
94723
|
+
}, defaultModel);
|
|
94724
|
+
};
|
|
94725
|
+
authService = createDefaultSoulPlusOAuthService({
|
|
93650
94726
|
kimiConfig: effectiveConfig,
|
|
93651
94727
|
modelAlias: defaultModel,
|
|
93652
|
-
pathConfig
|
|
93653
|
-
|
|
93654
|
-
|
|
93655
|
-
...options,
|
|
93656
|
-
kimiConfig: effectiveConfig,
|
|
93657
|
-
defaultModel,
|
|
93658
|
-
oauthResolver: authService.oauthResolver,
|
|
93659
|
-
deferOAuth: true,
|
|
93660
|
-
tokenRefresher: authService
|
|
94728
|
+
pathConfig,
|
|
94729
|
+
forceManagedKimiCodeOAuth: defaultModel.length === 0,
|
|
94730
|
+
onConfigProvisioned: reloadConfigFromDisk
|
|
93661
94731
|
});
|
|
93662
|
-
|
|
93663
|
-
|
|
93664
|
-
let activeDefaultModel = defaultModel;
|
|
94732
|
+
const initialRuntimeBundle = await buildRuntimeBundle(defaultModel);
|
|
94733
|
+
activeMaxContextSize = initialRuntimeBundle.compactionConfig?.maxContextSize ?? 2e5;
|
|
93665
94734
|
const defaults = await prepareDefaultSoulPlusSessionDefaults({
|
|
93666
94735
|
workspaceDir: options.workspaceDir,
|
|
93667
94736
|
pathConfig
|
|
@@ -93679,41 +94748,27 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
93679
94748
|
enabledToolNames: defaults.enabledToolNames,
|
|
93680
94749
|
webSearchProvider: webProviders.webSearchProvider,
|
|
93681
94750
|
urlFetcher: webProviders.urlFetcher,
|
|
93682
|
-
...
|
|
94751
|
+
...activeVideoUploader !== void 0 ? { videoUploader: activeVideoUploader } : {}
|
|
93683
94752
|
});
|
|
93684
94753
|
const backgroundManager = builtin.backgroundManager;
|
|
93685
94754
|
const handle = await createInProcessWireClient({
|
|
93686
94755
|
pathConfig,
|
|
93687
|
-
runtime:
|
|
93688
|
-
|
|
93689
|
-
|
|
93690
|
-
compactionProviderProvider: () => activeCompactionProvider,
|
|
94756
|
+
runtime: initialRuntimeBundle.runtime,
|
|
94757
|
+
compactionProvider: initialRuntimeBundle.compactionProvider,
|
|
94758
|
+
runtimeBundleProvider: ({ model }) => buildRuntimeBundle(model),
|
|
93691
94759
|
workspaceDir: options.workspaceDir,
|
|
93692
94760
|
defaultModel: activeDefaultModel,
|
|
93693
94761
|
defaultModelProvider: () => activeDefaultModel,
|
|
93694
94762
|
enabledToolNames: defaults.enabledToolNames,
|
|
93695
94763
|
enableWiredApprovals: true,
|
|
93696
|
-
rebuildRuntimeForModel: async (_sessionId, model) => {
|
|
93697
|
-
const next = await createDefaultSoulPlusRuntimeBundle({
|
|
93698
|
-
...options,
|
|
93699
|
-
kimiConfig: effectiveConfig,
|
|
93700
|
-
defaultModel: model,
|
|
93701
|
-
oauthResolver: authService.oauthResolver,
|
|
93702
|
-
deferOAuth: true,
|
|
93703
|
-
tokenRefresher: authService
|
|
93704
|
-
});
|
|
93705
|
-
activeRuntime = next.runtime;
|
|
93706
|
-
activeCompactionProvider = next.compactionProvider;
|
|
93707
|
-
activeDefaultModel = next.defaultModel;
|
|
93708
|
-
},
|
|
93709
94764
|
defaultSystemPromptProvider: () => defaults.systemPrompt,
|
|
93710
94765
|
tools: builtin.tools,
|
|
93711
94766
|
skillManager: defaults.skillManager,
|
|
93712
94767
|
...defaults.agentTypeRegistry !== void 0 ? { agentTypeRegistry: defaults.agentTypeRegistry } : {},
|
|
93713
94768
|
authService,
|
|
93714
94769
|
modelsProvider: () => ({
|
|
93715
|
-
models: Object.keys(
|
|
93716
|
-
default_model:
|
|
94770
|
+
models: Object.keys(effectiveConfig.models ?? {}),
|
|
94771
|
+
...activeDefaultModel.length > 0 ? { default_model: activeDefaultModel } : {}
|
|
93717
94772
|
}),
|
|
93718
94773
|
configProvider: () => effectiveConfig,
|
|
93719
94774
|
...options.logger !== void 0 ? { logger: options.logger } : {},
|
|
@@ -93726,14 +94781,30 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
93726
94781
|
await handle.dispose();
|
|
93727
94782
|
},
|
|
93728
94783
|
workspaceDir: options.workspaceDir,
|
|
93729
|
-
defaultModel
|
|
93730
|
-
|
|
93731
|
-
|
|
93732
|
-
|
|
93733
|
-
|
|
93734
|
-
|
|
93735
|
-
|
|
93736
|
-
|
|
94784
|
+
get defaultModel() {
|
|
94785
|
+
return activeDefaultModel;
|
|
94786
|
+
},
|
|
94787
|
+
get maxContextSize() {
|
|
94788
|
+
return activeMaxContextSize;
|
|
94789
|
+
},
|
|
94790
|
+
get defaultThinking() {
|
|
94791
|
+
return effectiveConfig.defaultThinking ?? false;
|
|
94792
|
+
},
|
|
94793
|
+
get defaultYolo() {
|
|
94794
|
+
return effectiveConfig.yolo ?? effectiveConfig.defaultYolo ?? false;
|
|
94795
|
+
},
|
|
94796
|
+
get defaultPlanMode() {
|
|
94797
|
+
return effectiveConfig.planMode ?? effectiveConfig.defaultPlanMode ?? false;
|
|
94798
|
+
},
|
|
94799
|
+
get theme() {
|
|
94800
|
+
return effectiveConfig.theme === "light" ? "light" : "dark";
|
|
94801
|
+
},
|
|
94802
|
+
get defaultEditor() {
|
|
94803
|
+
return effectiveConfig.defaultEditor ?? "";
|
|
94804
|
+
},
|
|
94805
|
+
get availableModels() {
|
|
94806
|
+
return effectiveConfig.models ?? {};
|
|
94807
|
+
},
|
|
93737
94808
|
async syncSessionRuntime(sessionId, syncOptions = {}) {
|
|
93738
94809
|
if (syncOptions.plan === true) await handle.client.setPlanMode(sessionId, true);
|
|
93739
94810
|
if (syncOptions.yolo === true) await handle.client.setYolo(sessionId, true);
|
|
@@ -93745,6 +94816,21 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
93745
94816
|
}
|
|
93746
94817
|
};
|
|
93747
94818
|
}
|
|
94819
|
+
function buildCompactionConfig(config, model, maxContextSize) {
|
|
94820
|
+
const loop = config.loopControl;
|
|
94821
|
+
const resolvedMaxContextSize = config.models?.[model] !== void 0 ? getDefaultSoulPlusMaxContextSize({
|
|
94822
|
+
kimiConfig: config,
|
|
94823
|
+
defaultModel: model
|
|
94824
|
+
}, model) : maxContextSize;
|
|
94825
|
+
return {
|
|
94826
|
+
maxContextSize: resolvedMaxContextSize,
|
|
94827
|
+
...loop?.compactionTriggerRatio !== void 0 ? { triggerRatio: loop.compactionTriggerRatio } : {},
|
|
94828
|
+
reservedContextSize: loop?.reservedContextSize ?? defaultReservedContextSize(resolvedMaxContextSize)
|
|
94829
|
+
};
|
|
94830
|
+
}
|
|
94831
|
+
function defaultReservedContextSize(maxContextSize) {
|
|
94832
|
+
return Math.max(0, Math.min(DEFAULT_RESERVED_CONTEXT_SIZE, Math.floor(maxContextSize * DEFAULT_RESERVED_CONTEXT_FRACTION)));
|
|
94833
|
+
}
|
|
93748
94834
|
function inferUserAgent(defaultHeaders) {
|
|
93749
94835
|
return defaultHeaders?.["User-Agent"] ?? "KimiCLI/unknown";
|
|
93750
94836
|
}
|
|
@@ -94504,6 +95590,49 @@ async function createKimiAgent() {
|
|
|
94504
95590
|
};
|
|
94505
95591
|
}
|
|
94506
95592
|
//#endregion
|
|
95593
|
+
//#region src/utils/history/input-history.ts
|
|
95594
|
+
/**
|
|
95595
|
+
* User input history persistence — JSONL file with `{"content": "..."}` per line.
|
|
95596
|
+
*
|
|
95597
|
+
* Mirrors the Python implementation in `kimi_cli/ui/shell/prompt.py`:
|
|
95598
|
+
* - One JSON object per line (`_HistoryEntry { content }`)
|
|
95599
|
+
* - Append-only writes
|
|
95600
|
+
* - Skip empty entries
|
|
95601
|
+
* - Skip when same as last entry (consecutive deduplication)
|
|
95602
|
+
* - Tolerate corrupt lines: log + skip, do not abort load
|
|
95603
|
+
*/
|
|
95604
|
+
async function loadInputHistory(file) {
|
|
95605
|
+
let raw;
|
|
95606
|
+
try {
|
|
95607
|
+
raw = await readFile(file, "utf-8");
|
|
95608
|
+
} catch (err) {
|
|
95609
|
+
if (err.code === "ENOENT") return [];
|
|
95610
|
+
throw err;
|
|
95611
|
+
}
|
|
95612
|
+
const entries = [];
|
|
95613
|
+
for (const line of raw.split("\n")) {
|
|
95614
|
+
const trimmed = line.trim();
|
|
95615
|
+
if (trimmed.length === 0) continue;
|
|
95616
|
+
try {
|
|
95617
|
+
const parsed = JSON.parse(trimmed);
|
|
95618
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.content === "string") entries.push({ content: parsed.content });
|
|
95619
|
+
} catch {}
|
|
95620
|
+
}
|
|
95621
|
+
return entries;
|
|
95622
|
+
}
|
|
95623
|
+
/**
|
|
95624
|
+
* Append an entry to the history file. Returns true if written, false if
|
|
95625
|
+
* skipped (empty or equal to `lastContent`).
|
|
95626
|
+
*/
|
|
95627
|
+
async function appendInputHistory(file, text, lastContent) {
|
|
95628
|
+
const content = text.trim();
|
|
95629
|
+
if (content.length === 0) return false;
|
|
95630
|
+
if (content === lastContent) return false;
|
|
95631
|
+
await mkdir(dirname(file), { recursive: true });
|
|
95632
|
+
await appendFile(file, `${JSON.stringify({ content })}\n`, "utf-8");
|
|
95633
|
+
return true;
|
|
95634
|
+
}
|
|
95635
|
+
//#endregion
|
|
94507
95636
|
//#region src/utils/process/external-editor.ts
|
|
94508
95637
|
/**
|
|
94509
95638
|
* External-editor helper — spawn $VISUAL / $EDITOR (or a configured
|
|
@@ -94552,99 +95681,6 @@ function shellQuote(path) {
|
|
|
94552
95681
|
return `'${path.replace(/'/g, "'\\''")}'`;
|
|
94553
95682
|
}
|
|
94554
95683
|
//#endregion
|
|
94555
|
-
//#region src/utils/history/input-history.ts
|
|
94556
|
-
/**
|
|
94557
|
-
* User input history persistence — JSONL file with `{"content": "..."}` per line.
|
|
94558
|
-
*
|
|
94559
|
-
* Mirrors the Python implementation in `kimi_cli/ui/shell/prompt.py`:
|
|
94560
|
-
* - One JSON object per line (`_HistoryEntry { content }`)
|
|
94561
|
-
* - Append-only writes
|
|
94562
|
-
* - Skip empty entries
|
|
94563
|
-
* - Skip when same as last entry (consecutive deduplication)
|
|
94564
|
-
* - Tolerate corrupt lines: log + skip, do not abort load
|
|
94565
|
-
*/
|
|
94566
|
-
async function loadInputHistory(file) {
|
|
94567
|
-
let raw;
|
|
94568
|
-
try {
|
|
94569
|
-
raw = await readFile(file, "utf-8");
|
|
94570
|
-
} catch (err) {
|
|
94571
|
-
if (err.code === "ENOENT") return [];
|
|
94572
|
-
throw err;
|
|
94573
|
-
}
|
|
94574
|
-
const entries = [];
|
|
94575
|
-
for (const line of raw.split("\n")) {
|
|
94576
|
-
const trimmed = line.trim();
|
|
94577
|
-
if (trimmed.length === 0) continue;
|
|
94578
|
-
try {
|
|
94579
|
-
const parsed = JSON.parse(trimmed);
|
|
94580
|
-
if (typeof parsed === "object" && parsed !== null && typeof parsed.content === "string") entries.push({ content: parsed.content });
|
|
94581
|
-
} catch {}
|
|
94582
|
-
}
|
|
94583
|
-
return entries;
|
|
94584
|
-
}
|
|
94585
|
-
/**
|
|
94586
|
-
* Append an entry to the history file. Returns true if written, false if
|
|
94587
|
-
* skipped (empty or equal to `lastContent`).
|
|
94588
|
-
*/
|
|
94589
|
-
async function appendInputHistory(file, text, lastContent) {
|
|
94590
|
-
const content = text.trim();
|
|
94591
|
-
if (content.length === 0) return false;
|
|
94592
|
-
if (content === lastContent) return false;
|
|
94593
|
-
await mkdir(dirname(file), { recursive: true });
|
|
94594
|
-
await appendFile(file, `${JSON.stringify({ content })}\n`, "utf-8");
|
|
94595
|
-
return true;
|
|
94596
|
-
}
|
|
94597
|
-
//#endregion
|
|
94598
|
-
//#region src/tui/input/image-placeholder.ts
|
|
94599
|
-
const PLACEHOLDER_REGEX = /\[image #(\d+) \((\d+)×(\d+)\)\]/g;
|
|
94600
|
-
function extractImageAttachments(text, store) {
|
|
94601
|
-
const parts = [];
|
|
94602
|
-
const cleanedSegments = [];
|
|
94603
|
-
const attachmentIds = [];
|
|
94604
|
-
let cursor = 0;
|
|
94605
|
-
let hasImages = false;
|
|
94606
|
-
PLACEHOLDER_REGEX.lastIndex = 0;
|
|
94607
|
-
let match;
|
|
94608
|
-
while ((match = PLACEHOLDER_REGEX.exec(text)) !== null) {
|
|
94609
|
-
const [literal, idStr] = match;
|
|
94610
|
-
if (idStr === void 0) continue;
|
|
94611
|
-
const id = Number.parseInt(idStr, 10);
|
|
94612
|
-
const attachment = store.get(id);
|
|
94613
|
-
if (attachment === void 0) continue;
|
|
94614
|
-
const before = text.slice(cursor, match.index);
|
|
94615
|
-
pushText(parts, before);
|
|
94616
|
-
cleanedSegments.push(before);
|
|
94617
|
-
parts.push(buildImagePart(attachment));
|
|
94618
|
-
attachmentIds.push(id);
|
|
94619
|
-
hasImages = true;
|
|
94620
|
-
cursor = match.index + literal.length;
|
|
94621
|
-
}
|
|
94622
|
-
const tail = text.slice(cursor);
|
|
94623
|
-
pushText(parts, tail);
|
|
94624
|
-
cleanedSegments.push(tail);
|
|
94625
|
-
return {
|
|
94626
|
-
parts: hasImages ? parts : [],
|
|
94627
|
-
hasImages,
|
|
94628
|
-
cleanedText: cleanedSegments.join("").replaceAll(/\s+/g, " ").trim(),
|
|
94629
|
-
attachmentIds
|
|
94630
|
-
};
|
|
94631
|
-
}
|
|
94632
|
-
function pushText(parts, segment) {
|
|
94633
|
-
if (segment.length === 0) return;
|
|
94634
|
-
if (segment.trim().length === 0) return;
|
|
94635
|
-
parts.push({
|
|
94636
|
-
type: "text",
|
|
94637
|
-
text: segment
|
|
94638
|
-
});
|
|
94639
|
-
}
|
|
94640
|
-
function buildImagePart(att) {
|
|
94641
|
-
const base64 = Buffer.from(att.bytes).toString("base64");
|
|
94642
|
-
return {
|
|
94643
|
-
type: "image_url",
|
|
94644
|
-
image_url: { url: `data:${att.mime};base64,${base64}` }
|
|
94645
|
-
};
|
|
94646
|
-
}
|
|
94647
|
-
//#endregion
|
|
94648
95684
|
//#region src/tui/components/messages/assistant-message.ts
|
|
94649
95685
|
const BULLET$1 = "● ";
|
|
94650
95686
|
const INDENT$2 = " ";
|
|
@@ -95483,6 +96519,56 @@ function emitPrimary(state, message) {
|
|
|
95483
96519
|
emitStatus(state, message, state.colors.primary);
|
|
95484
96520
|
}
|
|
95485
96521
|
//#endregion
|
|
96522
|
+
//#region src/tui/input/image-placeholder.ts
|
|
96523
|
+
const PLACEHOLDER_REGEX = /\[image #(\d+) \((\d+)×(\d+)\)\]/g;
|
|
96524
|
+
function extractImageAttachments(text, store) {
|
|
96525
|
+
const parts = [];
|
|
96526
|
+
const cleanedSegments = [];
|
|
96527
|
+
const attachmentIds = [];
|
|
96528
|
+
let cursor = 0;
|
|
96529
|
+
let hasImages = false;
|
|
96530
|
+
PLACEHOLDER_REGEX.lastIndex = 0;
|
|
96531
|
+
let match;
|
|
96532
|
+
while ((match = PLACEHOLDER_REGEX.exec(text)) !== null) {
|
|
96533
|
+
const [literal, idStr] = match;
|
|
96534
|
+
if (idStr === void 0) continue;
|
|
96535
|
+
const id = Number.parseInt(idStr, 10);
|
|
96536
|
+
const attachment = store.get(id);
|
|
96537
|
+
if (attachment === void 0) continue;
|
|
96538
|
+
const before = text.slice(cursor, match.index);
|
|
96539
|
+
pushText(parts, before);
|
|
96540
|
+
cleanedSegments.push(before);
|
|
96541
|
+
parts.push(buildImagePart(attachment));
|
|
96542
|
+
attachmentIds.push(id);
|
|
96543
|
+
hasImages = true;
|
|
96544
|
+
cursor = match.index + literal.length;
|
|
96545
|
+
}
|
|
96546
|
+
const tail = text.slice(cursor);
|
|
96547
|
+
pushText(parts, tail);
|
|
96548
|
+
cleanedSegments.push(tail);
|
|
96549
|
+
return {
|
|
96550
|
+
parts: hasImages ? parts : [],
|
|
96551
|
+
hasImages,
|
|
96552
|
+
cleanedText: cleanedSegments.join("").replaceAll(/\s+/g, " ").trim(),
|
|
96553
|
+
attachmentIds
|
|
96554
|
+
};
|
|
96555
|
+
}
|
|
96556
|
+
function pushText(parts, segment) {
|
|
96557
|
+
if (segment.length === 0) return;
|
|
96558
|
+
if (segment.trim().length === 0) return;
|
|
96559
|
+
parts.push({
|
|
96560
|
+
type: "text",
|
|
96561
|
+
text: segment
|
|
96562
|
+
});
|
|
96563
|
+
}
|
|
96564
|
+
function buildImagePart(att) {
|
|
96565
|
+
const base64 = Buffer.from(att.bytes).toString("base64");
|
|
96566
|
+
return {
|
|
96567
|
+
type: "image_url",
|
|
96568
|
+
image_url: { url: `data:${att.mime};base64,${base64}` }
|
|
96569
|
+
};
|
|
96570
|
+
}
|
|
96571
|
+
//#endregion
|
|
95486
96572
|
//#region src/utils/git/git-ls-files.ts
|
|
95487
96573
|
/**
|
|
95488
96574
|
* Git-aware file listing + relevance signals with a short-TTL cache.
|
|
@@ -95679,6 +96765,38 @@ function openUrl(url) {
|
|
|
95679
96765
|
]] : ["xdg-open", [url]];
|
|
95680
96766
|
execFile(args[0], args[1], () => {});
|
|
95681
96767
|
}
|
|
96768
|
+
function isRecord(value) {
|
|
96769
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96770
|
+
}
|
|
96771
|
+
function readAvailableModels(config) {
|
|
96772
|
+
if (!isRecord(config) || !isRecord(config["models"])) return {};
|
|
96773
|
+
const out = {};
|
|
96774
|
+
for (const [alias, raw] of Object.entries(config["models"])) {
|
|
96775
|
+
if (!isRecord(raw) || typeof raw["provider"] !== "string" || typeof raw["model"] !== "string") continue;
|
|
96776
|
+
out[alias] = {
|
|
96777
|
+
provider: raw["provider"],
|
|
96778
|
+
model: raw["model"],
|
|
96779
|
+
...typeof raw["maxContextSize"] === "number" ? { maxContextSize: raw["maxContextSize"] } : {},
|
|
96780
|
+
...Array.isArray(raw["capabilities"]) ? { capabilities: raw["capabilities"].filter((v) => typeof v === "string") } : {}
|
|
96781
|
+
};
|
|
96782
|
+
}
|
|
96783
|
+
return out;
|
|
96784
|
+
}
|
|
96785
|
+
async function refreshConfigAfterLogin(client, ctx) {
|
|
96786
|
+
const [modelsResponse, configResponse] = await Promise.all([client.getModels(), client.getConfig()]);
|
|
96787
|
+
const config = isRecord(configResponse.config) ? configResponse.config : {};
|
|
96788
|
+
const availableModels = readAvailableModels(config);
|
|
96789
|
+
const defaultModel = modelsResponse.default_model ?? (typeof config["defaultModel"] === "string" ? config["defaultModel"] : void 0);
|
|
96790
|
+
const selected = defaultModel !== void 0 ? availableModels[defaultModel] : void 0;
|
|
96791
|
+
const patch = {
|
|
96792
|
+
availableModels,
|
|
96793
|
+
...defaultModel !== void 0 ? { model: defaultModel } : {},
|
|
96794
|
+
...selected?.maxContextSize !== void 0 ? { maxContextTokens: selected.maxContextSize } : {},
|
|
96795
|
+
...typeof config["defaultThinking"] === "boolean" ? { thinking: config["defaultThinking"] } : {}
|
|
96796
|
+
};
|
|
96797
|
+
if (defaultModel !== void 0 && typeof ctx.appState.sessionId === "string" && ctx.appState.sessionId.length > 0) await client.setModel(ctx.appState.sessionId, defaultModel);
|
|
96798
|
+
ctx.setAppState(patch);
|
|
96799
|
+
}
|
|
95682
96800
|
function createAuthCommands(deps = {}) {
|
|
95683
96801
|
const providerName = deps.defaultProviderName ?? "managed:kimi-code";
|
|
95684
96802
|
return [{
|
|
@@ -95708,6 +96826,11 @@ function createAuthCommands(deps = {}) {
|
|
|
95708
96826
|
});
|
|
95709
96827
|
try {
|
|
95710
96828
|
await deps.client.authLogin(providerName);
|
|
96829
|
+
try {
|
|
96830
|
+
await refreshConfigAfterLogin(deps.client, ctx);
|
|
96831
|
+
} catch (refreshError) {
|
|
96832
|
+
return ok$2(`Login successful, but failed to refresh config: ${refreshError instanceof Error ? refreshError.message : String(refreshError)}`);
|
|
96833
|
+
}
|
|
95711
96834
|
return {
|
|
95712
96835
|
type: "ok",
|
|
95713
96836
|
message: "✓ Login successful!",
|
|
@@ -95814,8 +96937,8 @@ const shellCommands = [
|
|
|
95814
96937
|
const newTitle = trimmed.slice(0, 200);
|
|
95815
96938
|
try {
|
|
95816
96939
|
await ctx.client.rename(ctx.appState.sessionId, newTitle);
|
|
95817
|
-
} catch (
|
|
95818
|
-
return ok$1(`Failed to set title: ${
|
|
96940
|
+
} catch (error) {
|
|
96941
|
+
return ok$1(`Failed to set title: ${error instanceof Error ? error.message : String(error)}`);
|
|
95819
96942
|
}
|
|
95820
96943
|
return ok$1(`Session title set to: ${newTitle}`);
|
|
95821
96944
|
}
|
|
@@ -95841,12 +96964,23 @@ const shellCommands = [
|
|
|
95841
96964
|
description: "Toggle plan mode",
|
|
95842
96965
|
mode: "both",
|
|
95843
96966
|
async execute(args, ctx) {
|
|
96967
|
+
const subcmd = args.trim().toLowerCase();
|
|
96968
|
+
if (subcmd === "view") {
|
|
96969
|
+
const res = await ctx.client.getPlan(ctx.appState.sessionId);
|
|
96970
|
+
if (res.plan !== void 0 && res.plan.length > 0) return ok$1(res.plan);
|
|
96971
|
+
return ok$1("No plan file found for this session.");
|
|
96972
|
+
}
|
|
96973
|
+
if (subcmd === "clear") {
|
|
96974
|
+
await ctx.client.clearPlan(ctx.appState.sessionId);
|
|
96975
|
+
return ok$1("Plan cleared.");
|
|
96976
|
+
}
|
|
95844
96977
|
let enabled;
|
|
95845
|
-
if (
|
|
95846
|
-
else if (
|
|
96978
|
+
if (subcmd === "on") enabled = true;
|
|
96979
|
+
else if (subcmd === "off") enabled = false;
|
|
95847
96980
|
else enabled = !ctx.appState.planMode;
|
|
95848
|
-
await ctx.client.setPlanMode(ctx.appState.sessionId, enabled);
|
|
96981
|
+
const res = await ctx.client.setPlanMode(ctx.appState.sessionId, enabled);
|
|
95849
96982
|
ctx.setAppState({ planMode: enabled });
|
|
96983
|
+
if (enabled && res.plan_path !== void 0) return ok$1(`Plan mode ON. Write your plan to: ${res.plan_path}`);
|
|
95850
96984
|
return ok$1(`Plan mode: ${enabled ? "on" : "off"}`);
|
|
95851
96985
|
}
|
|
95852
96986
|
},
|
|
@@ -96008,6 +97142,40 @@ function createDefaultRegistry(authDeps) {
|
|
|
96008
97142
|
* Custom editor extending pi-tui Editor with app-level keybindings.
|
|
96009
97143
|
*/
|
|
96010
97144
|
const ANSI_SGR = /\x1b\[[0-9;]*m/g;
|
|
97145
|
+
const KITTY_CSI_U = /^\x1b\[(\d+);(\d+)((?::\d+)*)u$/;
|
|
97146
|
+
const CAPS_LOCK_BIT = 64;
|
|
97147
|
+
const CTRL_BIT = 4;
|
|
97148
|
+
const SHIFT_BIT = 1;
|
|
97149
|
+
/**
|
|
97150
|
+
* Workaround for a pi-tui bug that surfaces when Kitty keyboard protocol
|
|
97151
|
+
* is active AND caps_lock is on. In that state terminals emit, e.g.,
|
|
97152
|
+
* `ESC[68;69u` for ctrl+d (codepoint=68=`D`, modifier=ctrl|caps_lock).
|
|
97153
|
+
* pi-tui's `matchesKittySequence` masks `caps_lock` out of the *modifier*
|
|
97154
|
+
* but leaves the *codepoint* capitalised, so `matchesKey(data, "ctrl+d")`
|
|
97155
|
+
* (which expects codepoint=100=`d`) fails and every ctrl-shortcut is
|
|
97156
|
+
* silently dropped.
|
|
97157
|
+
*
|
|
97158
|
+
* We rewrite the sequence back to its unlocked form before dispatching,
|
|
97159
|
+
* but only when ctrl is held and shift is not — i.e. exactly the
|
|
97160
|
+
* `ctrl+<letter>` case. Plain uppercase (caps_lock only, no ctrl) and
|
|
97161
|
+
* explicit ctrl+shift+<letter> are left alone.
|
|
97162
|
+
*/
|
|
97163
|
+
function normalizeCapsLockedCtrl(data) {
|
|
97164
|
+
const m = data.match(KITTY_CSI_U);
|
|
97165
|
+
if (m === null) return data;
|
|
97166
|
+
const codepoint = Number(m[1]);
|
|
97167
|
+
const modifierPlus1 = Number(m[2]);
|
|
97168
|
+
const tail = m[3] ?? "";
|
|
97169
|
+
if (!Number.isFinite(codepoint) || !Number.isFinite(modifierPlus1)) return data;
|
|
97170
|
+
const modifier = modifierPlus1 - 1;
|
|
97171
|
+
if ((modifier & CAPS_LOCK_BIT) === 0) return data;
|
|
97172
|
+
if ((modifier & CTRL_BIT) === 0) return data;
|
|
97173
|
+
if ((modifier & SHIFT_BIT) !== 0) return data;
|
|
97174
|
+
if (codepoint < 65 || codepoint > 90) return data;
|
|
97175
|
+
const loweredCodepoint = codepoint + 32;
|
|
97176
|
+
const strippedModifier = (modifier & ~CAPS_LOCK_BIT) + 1;
|
|
97177
|
+
return `\x1b[${String(loweredCodepoint)};${String(strippedModifier)}${tail}u`;
|
|
97178
|
+
}
|
|
96011
97179
|
/** Convert a visible-char index (ANSI-stripped) back to an index into the raw ANSI-bearing string. */
|
|
96012
97180
|
function mapVisibleIdxToRaw(line, visibleIdx) {
|
|
96013
97181
|
let visibleCount = 0;
|
|
@@ -96073,51 +97241,52 @@ var CustomEditor = class extends Editor {
|
|
|
96073
97241
|
return lines;
|
|
96074
97242
|
}
|
|
96075
97243
|
handleInput(data) {
|
|
96076
|
-
|
|
97244
|
+
const normalized = normalizeCapsLockedCtrl(data);
|
|
97245
|
+
if (matchesKey(normalized, process.platform === "win32" ? "alt+v" : Key.ctrl("v")) && this.onPasteImage !== void 0) {
|
|
96077
97246
|
const handler = this.onPasteImage;
|
|
96078
97247
|
handler().then((handled) => {
|
|
96079
|
-
if (!handled) super.handleInput.call(this,
|
|
97248
|
+
if (!handled) super.handleInput.call(this, normalized);
|
|
96080
97249
|
});
|
|
96081
97250
|
return;
|
|
96082
97251
|
}
|
|
96083
|
-
if (matchesKey(
|
|
97252
|
+
if (matchesKey(normalized, Key.ctrl("d"))) {
|
|
96084
97253
|
if (this.getText().length === 0) {
|
|
96085
97254
|
this.onCtrlD?.();
|
|
96086
97255
|
return;
|
|
96087
97256
|
}
|
|
96088
97257
|
}
|
|
96089
|
-
if (matchesKey(
|
|
97258
|
+
if (matchesKey(normalized, Key.ctrl("c"))) {
|
|
96090
97259
|
this.onCtrlC?.();
|
|
96091
97260
|
return;
|
|
96092
97261
|
}
|
|
96093
|
-
if (matchesKey(
|
|
97262
|
+
if (matchesKey(normalized, Key.ctrl("g"))) {
|
|
96094
97263
|
this.onOpenExternalEditor?.();
|
|
96095
97264
|
return;
|
|
96096
97265
|
}
|
|
96097
|
-
if (matchesKey(
|
|
97266
|
+
if (matchesKey(normalized, Key.ctrl("o"))) {
|
|
96098
97267
|
this.onToggleToolExpand?.();
|
|
96099
97268
|
return;
|
|
96100
97269
|
}
|
|
96101
|
-
if (matchesKey(
|
|
97270
|
+
if (matchesKey(normalized, Key.ctrl("s"))) {
|
|
96102
97271
|
this.onCtrlS?.();
|
|
96103
97272
|
return;
|
|
96104
97273
|
}
|
|
96105
|
-
if (matchesKey(
|
|
97274
|
+
if (matchesKey(normalized, "shift+tab")) {
|
|
96106
97275
|
this.onShiftTab?.();
|
|
96107
97276
|
return;
|
|
96108
97277
|
}
|
|
96109
|
-
if (matchesKey(
|
|
97278
|
+
if (matchesKey(normalized, Key.up)) {
|
|
96110
97279
|
if (this.getText().length === 0 && this.onUpArrowEmpty) {
|
|
96111
97280
|
if (this.onUpArrowEmpty()) return;
|
|
96112
97281
|
}
|
|
96113
97282
|
}
|
|
96114
|
-
if (matchesKey(
|
|
97283
|
+
if (matchesKey(normalized, Key.escape)) {
|
|
96115
97284
|
if (!this.isShowingAutocomplete()) {
|
|
96116
97285
|
this.onEscape?.();
|
|
96117
97286
|
return;
|
|
96118
97287
|
}
|
|
96119
97288
|
}
|
|
96120
|
-
super.handleInput(
|
|
97289
|
+
super.handleInput(normalized);
|
|
96121
97290
|
}
|
|
96122
97291
|
};
|
|
96123
97292
|
/**
|
|
@@ -96972,6 +98141,10 @@ function cancelStream(state) {
|
|
|
96972
98141
|
}
|
|
96973
98142
|
//#endregion
|
|
96974
98143
|
//#region src/tui/actions/input-ops.ts
|
|
98144
|
+
/**
|
|
98145
|
+
* User input orchestration: submission / history / external editor /
|
|
98146
|
+
* plan-mode toggle / reload.
|
|
98147
|
+
*/
|
|
96975
98148
|
function handleUserInput(state, text, hooks, onSlash) {
|
|
96976
98149
|
if (text.trim().length === 0) return;
|
|
96977
98150
|
if (state.appState.isReplaying) {
|
|
@@ -97017,9 +98190,9 @@ async function openExternalEditor(state) {
|
|
|
97017
98190
|
await new Promise((resolve) => setImmediate(resolve));
|
|
97018
98191
|
try {
|
|
97019
98192
|
const result = await editInExternalEditor(seed, cmd);
|
|
97020
|
-
if (result !== void 0) state.editor.setText(result.
|
|
97021
|
-
} catch (
|
|
97022
|
-
emitError(state, `External editor failed: ${
|
|
98193
|
+
if (result !== void 0) state.editor.setText(result.replaceAll("\r\n", "\n").replace(/\n$/, ""));
|
|
98194
|
+
} catch (error) {
|
|
98195
|
+
emitError(state, `External editor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
97023
98196
|
} finally {
|
|
97024
98197
|
if (typeof process.stdin.pause === "function") process.stdin.pause();
|
|
97025
98198
|
state.ui.start();
|
|
@@ -97030,13 +98203,18 @@ async function openExternalEditor(state) {
|
|
|
97030
98203
|
}
|
|
97031
98204
|
async function togglePlanMode(state, hooks) {
|
|
97032
98205
|
const enabled = !state.appState.planMode;
|
|
98206
|
+
let planPath;
|
|
97033
98207
|
try {
|
|
97034
|
-
await state.client.setPlanMode(state.appState.sessionId, enabled);
|
|
97035
|
-
} catch (
|
|
97036
|
-
emitError(state, `Failed to toggle plan mode: ${
|
|
98208
|
+
planPath = (await state.client.setPlanMode(state.appState.sessionId, enabled)).plan_path;
|
|
98209
|
+
} catch (error) {
|
|
98210
|
+
emitError(state, `Failed to toggle plan mode: ${error instanceof Error ? error.message : String(error)}`);
|
|
97037
98211
|
return;
|
|
97038
98212
|
}
|
|
97039
98213
|
setState(state, { planMode: enabled }, hooks);
|
|
98214
|
+
if (enabled && planPath !== void 0) {
|
|
98215
|
+
emitPrimary(state, `Plan mode: ON. Write your plan to: ${planPath}`);
|
|
98216
|
+
return;
|
|
98217
|
+
}
|
|
97040
98218
|
emitPrimary(state, `Plan mode: ${enabled ? "ON" : "OFF"}`);
|
|
97041
98219
|
}
|
|
97042
98220
|
async function performReload(state, action, hooks) {
|
|
@@ -97825,6 +99003,20 @@ function handleContentDelta(ectx, data) {
|
|
|
97825
99003
|
streamingPhase: "composing",
|
|
97826
99004
|
streamingStartTime: Date.now()
|
|
97827
99005
|
});
|
|
99006
|
+
return;
|
|
99007
|
+
}
|
|
99008
|
+
if (data.type === "tool_call_part") {
|
|
99009
|
+
if (ectx.state.thinkingDraft.length > 0) flushThinkingToTranscript(ectx, "idle");
|
|
99010
|
+
ectx.patchLivePane({
|
|
99011
|
+
mode: "idle",
|
|
99012
|
+
pendingToolCall: null,
|
|
99013
|
+
pendingApproval: null,
|
|
99014
|
+
pendingQuestion: null
|
|
99015
|
+
});
|
|
99016
|
+
ectx.setAppState({
|
|
99017
|
+
streamingPhase: "composing",
|
|
99018
|
+
streamingStartTime: Date.now()
|
|
99019
|
+
});
|
|
97828
99020
|
}
|
|
97829
99021
|
}
|
|
97830
99022
|
//#endregion
|
|
@@ -97874,7 +99066,7 @@ function handleToolResult(ectx, data) {
|
|
|
97874
99066
|
}
|
|
97875
99067
|
ectx.state.activeToolCalls.delete(data.tool_call_id);
|
|
97876
99068
|
ectx.patchLivePane({
|
|
97877
|
-
mode: "
|
|
99069
|
+
mode: "waiting",
|
|
97878
99070
|
pendingToolCall: null
|
|
97879
99071
|
});
|
|
97880
99072
|
}
|
|
@@ -98299,6 +99491,12 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
98299
99491
|
this.rebuildOptions();
|
|
98300
99492
|
return;
|
|
98301
99493
|
}
|
|
99494
|
+
const printable = decodeKittyPrintable(data);
|
|
99495
|
+
if (printable !== void 0) {
|
|
99496
|
+
this.feedbackText += printable;
|
|
99497
|
+
this.rebuildOptions();
|
|
99498
|
+
return;
|
|
99499
|
+
}
|
|
98302
99500
|
if (data.length > 0 && !data.startsWith("\x1B")) {
|
|
98303
99501
|
this.feedbackText += data;
|
|
98304
99502
|
this.rebuildOptions();
|
|
@@ -98320,7 +99518,8 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
98320
99518
|
this.selectAndSubmit(this.selectedIndex);
|
|
98321
99519
|
return;
|
|
98322
99520
|
}
|
|
98323
|
-
const
|
|
99521
|
+
const printable = decodeKittyPrintable(data) ?? data;
|
|
99522
|
+
const numericIndex = Number(printable) - 1;
|
|
98324
99523
|
if (Number.isInteger(numericIndex) && numericIndex >= 0 && numericIndex < this.choiceCount()) this.selectAndSubmit(numericIndex);
|
|
98325
99524
|
}
|
|
98326
99525
|
render(width) {
|
|
@@ -98530,13 +99729,14 @@ var QuestionDialogComponent = class extends Container {
|
|
|
98530
99729
|
this.activateQuestionOption(this.currentCursor());
|
|
98531
99730
|
return;
|
|
98532
99731
|
}
|
|
98533
|
-
const
|
|
99732
|
+
const printable = decodeKittyPrintable(data) ?? data;
|
|
99733
|
+
const numIdx = NUMBER_KEYS.indexOf(printable);
|
|
98534
99734
|
if (numIdx >= 0 && numIdx < optionCount) {
|
|
98535
99735
|
this.cursors[questionIdx] = numIdx;
|
|
98536
99736
|
this.activateQuestionOption(numIdx);
|
|
98537
99737
|
return;
|
|
98538
99738
|
}
|
|
98539
|
-
if (
|
|
99739
|
+
if (printable === " " || matchesKey(data, Key.space)) {
|
|
98540
99740
|
if (question.multi_select) this.activateQuestionOption(this.currentCursor());
|
|
98541
99741
|
return;
|
|
98542
99742
|
}
|
|
@@ -98573,6 +99773,12 @@ var QuestionDialogComponent = class extends Container {
|
|
|
98573
99773
|
this.reviewMessage = void 0;
|
|
98574
99774
|
return;
|
|
98575
99775
|
}
|
|
99776
|
+
const printable = decodeKittyPrintable(data);
|
|
99777
|
+
if (printable !== void 0) {
|
|
99778
|
+
this.otherDrafts[questionIdx] = (this.otherDrafts[questionIdx] ?? "") + printable;
|
|
99779
|
+
this.reviewMessage = void 0;
|
|
99780
|
+
return;
|
|
99781
|
+
}
|
|
98576
99782
|
if (data === " " || matchesKey(data, Key.space)) {
|
|
98577
99783
|
this.otherDrafts[questionIdx] = (this.otherDrafts[questionIdx] ?? "") + " ";
|
|
98578
99784
|
this.reviewMessage = void 0;
|
|
@@ -98606,12 +99812,13 @@ var QuestionDialogComponent = class extends Container {
|
|
|
98606
99812
|
this.executeSubmitAction(this.submitActionIdx);
|
|
98607
99813
|
return;
|
|
98608
99814
|
}
|
|
98609
|
-
|
|
99815
|
+
const printable = decodeKittyPrintable(data) ?? data;
|
|
99816
|
+
if (printable === "1") {
|
|
98610
99817
|
this.submitActionIdx = 0;
|
|
98611
99818
|
this.executeSubmitAction(0);
|
|
98612
99819
|
return;
|
|
98613
99820
|
}
|
|
98614
|
-
if (
|
|
99821
|
+
if (printable === "2") {
|
|
98615
99822
|
this.submitActionIdx = 1;
|
|
98616
99823
|
this.executeSubmitAction(1);
|
|
98617
99824
|
return;
|
|
@@ -99155,7 +100362,8 @@ var HelpPanelComponent = class extends Container {
|
|
|
99155
100362
|
this.opts = opts;
|
|
99156
100363
|
}
|
|
99157
100364
|
handleInput(data) {
|
|
99158
|
-
|
|
100365
|
+
const printable = decodeKittyPrintable(data) ?? data;
|
|
100366
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.enter) || printable === "q" || printable === "Q") {
|
|
99159
100367
|
this.opts.onClose();
|
|
99160
100368
|
return;
|
|
99161
100369
|
}
|
|
@@ -99714,6 +100922,14 @@ var MoonLoader = class extends Text {
|
|
|
99714
100922
|
this.intervalId = null;
|
|
99715
100923
|
}
|
|
99716
100924
|
}
|
|
100925
|
+
setLabel(label) {
|
|
100926
|
+
this.label = label;
|
|
100927
|
+
this.updateDisplay();
|
|
100928
|
+
}
|
|
100929
|
+
setColorFn(colorFn) {
|
|
100930
|
+
this.colorFn = colorFn;
|
|
100931
|
+
this.updateDisplay();
|
|
100932
|
+
}
|
|
99717
100933
|
updateDisplay() {
|
|
99718
100934
|
const frame = this.frames[this.currentFrame];
|
|
99719
100935
|
const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
|
|
@@ -99768,6 +100984,10 @@ function updateActivityPane(state) {
|
|
|
99768
100984
|
case "composing":
|
|
99769
100985
|
stopLoader(state);
|
|
99770
100986
|
if (!state.phaseSpinner) state.phaseSpinner = new MoonLoader(state.ui, "braille", (s) => chalk.hex(state.colors.primary)(s), "working...");
|
|
100987
|
+
else {
|
|
100988
|
+
state.phaseSpinner.setLabel("working...");
|
|
100989
|
+
state.phaseSpinner.setColorFn((s) => chalk.hex(state.colors.primary)(s));
|
|
100990
|
+
}
|
|
99771
100991
|
state.activityContainer.addChild(state.phaseSpinner);
|
|
99772
100992
|
break;
|
|
99773
100993
|
case "tool":
|
|
@@ -100710,6 +101930,9 @@ var KimiTUI = class {
|
|
|
100710
101930
|
getCurrentSessionId() {
|
|
100711
101931
|
return this.state.appState.sessionId;
|
|
100712
101932
|
}
|
|
101933
|
+
hasSessionContent() {
|
|
101934
|
+
return this.state.transcriptEntries.length > 0;
|
|
101935
|
+
}
|
|
100713
101936
|
startWireSubscription() {
|
|
100714
101937
|
const ectx = buildEventCtx(this.state, this.stateHooks, this.streamCallbacks, (entry) => this.addEntry(entry));
|
|
100715
101938
|
const sendQueued = (text) => {
|
|
@@ -100792,9 +102015,6 @@ var KimiTUI = class {
|
|
|
100792
102015
|
//#region src/cli/run-shell.ts
|
|
100793
102016
|
/**
|
|
100794
102017
|
* Shell runner —— 把 `AgentContext` 接进 `KimiTUI`。
|
|
100795
|
-
*
|
|
100796
|
-
* 新 wire 架构下不再有 "resume this session: kimi -r" 的提示(方案明确
|
|
100797
|
-
* 不支持 session 重新打开)。
|
|
100798
102018
|
*/
|
|
100799
102019
|
function toInitialSessionIntent(opts) {
|
|
100800
102020
|
if (opts.session === "") return { kind: "picker" };
|
|
@@ -100836,7 +102056,11 @@ async function runShell(opts, version) {
|
|
|
100836
102056
|
syncSessionRuntime: ctx.syncSessionRuntime
|
|
100837
102057
|
} });
|
|
100838
102058
|
tui.onExit = async () => {
|
|
102059
|
+
const sessionId = tui.getCurrentSessionId();
|
|
102060
|
+
const hasContent = tui.hasSessionContent();
|
|
100839
102061
|
await ctx.dispose();
|
|
102062
|
+
process.stdout.write("Bye!\n");
|
|
102063
|
+
if (sessionId !== "" && hasContent) process.stderr.write(`\nTo resume this session: kimi -r ${sessionId}\n`);
|
|
100840
102064
|
process.exit(0);
|
|
100841
102065
|
};
|
|
100842
102066
|
try {
|