@moonshot-ai/kimi-code 0.14.1 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +1205 -756
- package/package.json +4 -4
package/dist/main.mjs
CHANGED
|
@@ -37,7 +37,7 @@ import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
|
37
37
|
import { StringDecoder } from "node:string_decoder";
|
|
38
38
|
import Mi from "assert";
|
|
39
39
|
import so from "node:assert";
|
|
40
|
-
import { parse as parse$2, stringify } from "smol-toml";
|
|
40
|
+
import { TomlError, parse as parse$2, stringify } from "smol-toml";
|
|
41
41
|
import "zod/v3";
|
|
42
42
|
import * as z4mini from "zod/v4-mini";
|
|
43
43
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -54366,7 +54366,7 @@ function applyCompletionBudget(args) {
|
|
|
54366
54366
|
return args.provider.withMaxCompletionTokens(cap);
|
|
54367
54367
|
}
|
|
54368
54368
|
//#endregion
|
|
54369
|
-
//#region ../../packages/agent-core/src/agent/compaction/compaction-instruction.md
|
|
54369
|
+
//#region ../../packages/agent-core/src/agent/compaction/compaction-instruction.md?raw
|
|
54370
54370
|
var compaction_instruction_default = "\n--- This message is a direct task, not part of the above conversation ---\n\nYou are now given a task to compact this conversation context according to specific priorities and output requirements.\n\nOutput text only. DO NOT CALL ANY TOOLS. Calling tools will be rejected and fails the task. You already have all the information you need in the conversation history. You have only one chance.\n\nThe goal of compaction is to keep essential code patterns, technical details, and architectural decisions for continuing development without losing context after the above messages are cleared work.\n\n{{ customInstruction }}\n\n<!-- Compression Priorities (in order) -->\n\n1. **Current Task State**: What is being worked on RIGHT NOW\n2. **Errors & Solutions**: All encountered errors and their resolutions\n3. **Code Evolution**: Final working versions only (remove intermediate attempts)\n4. **System Context**: Project structure, dependencies, environment setup\n5. **Design Decisions**: Architectural choices and their rationale\n6. **TODO Items**: Unfinished tasks and known issues\n\n<!-- Required Output Structure -->\n\n## Current Focus\n\n[What we're working on now]\n\n## Environment\n\n- [Key setup/config points]\n- ...\n\n## Completed Tasks\n\n- [Task]: [Brief outcome]\n- ...\n\n## Active Issues\n\n- [Issue]: [Status/Next steps]\n- ...\n\n## Code State\n\n### [Critical file name]\n\n[Brief description of the file's purpose and current state]\n\n```\n[The latest version of critical code snippets in this file, <20 lines]\n```\n\n### [Critical file name]\n\n- [Useful classes/methods/functions]: [Brief description/usage]\n- ...\n\n<!-- Omit non-critical code, intermediate attempts, and resolved errors -->\n\n## Important Context\n\n- [Any crucial information not covered above]\n- ...\n\n## All User Messages\n\n- [Detailed non tool use user message]\n- ...\n";
|
|
54371
54371
|
//#endregion
|
|
54372
54372
|
//#region ../../packages/agent-core/src/agent/compaction/render-messages.ts
|
|
@@ -54496,7 +54496,7 @@ function closeObjectNodes(value) {
|
|
|
54496
54496
|
for (const child of Object.values(node)) closeObjectNodes(child);
|
|
54497
54497
|
}
|
|
54498
54498
|
//#endregion
|
|
54499
|
-
//#region ../../packages/agent-core/src/tools/builtin/state/todo-list.md
|
|
54499
|
+
//#region ../../packages/agent-core/src/tools/builtin/state/todo-list.md?raw
|
|
54500
54500
|
var todo_list_default = "Use this tool to maintain a structured TODO list as you work through a multi-step task. Use it proactively and often when progress tracking helps the current work. This is especially useful in Plan mode, long-running investigations, and implementation tasks with several tool calls.\n\n**When to use:**\n- Multi-step tasks that span several tool calls\n- Tracking investigation progress across a large codebase search\n- Planning a sequence of edits before making them\n- After receiving new multi-step instructions, capture the requirements as todos\n- Before starting a tracked task, mark exactly one item as `in_progress`\n- Immediately after finishing a tracked task, mark it `done`; do not batch completions at the end\n\n**When NOT to use:**\n- Single-shot answers that complete in one or two tool calls\n- Trivial requests where tracking adds no clarity\n- Purely conversational or informational replies\n\n**Avoid churn:**\n- Do not re-call this tool when nothing meaningful has changed since the last call — update the list only after real progress.\n- When unsure of the current state, call query mode first (omit `todos`) to check the list before deciding what to update.\n- If no available tool can move any task forward, tell the user where you are stuck instead of repeatedly re-ordering the same todos.\n\n**How to use:**\n- Call with `todos: [...]` to replace the full list. Statuses: pending / in_progress / done.\n- Call with no arguments to retrieve the current list without changing it.\n- Call with `todos: []` to clear the list.\n- Keep titles short and actionable (e.g. \"Read session-control.ts\", \"Add planMode flag to TurnManager\").\n- Update statuses as you make progress.\n- When work is underway, keep exactly one task `in_progress`.\n- Only mark a task `done` when it is fully accomplished.\n- Never mark a task `done` if tests are failing, implementation is partial, unresolved errors remain, or required files/dependencies could not be found.\n- If you encounter a blocker, keep the blocked task `in_progress` or add a new pending task describing what must be resolved.\n";
|
|
54501
54501
|
//#endregion
|
|
54502
54502
|
//#region ../../packages/agent-core/src/tools/builtin/state/todo-list.ts
|
|
@@ -54700,7 +54700,13 @@ var FullCompaction = class {
|
|
|
54700
54700
|
if (data.source === "manual") this.compactionCountInTurn = 0;
|
|
54701
54701
|
else this.compactionCountInTurn += 1;
|
|
54702
54702
|
if (this.compactionCountInTurn > this.strategy.maxCompactionPerTurn) return;
|
|
54703
|
-
if (this.agent.records.restoring)
|
|
54703
|
+
if (this.agent.records.restoring) {
|
|
54704
|
+
this.agent.replayBuilder.push({
|
|
54705
|
+
type: "compaction",
|
|
54706
|
+
instruction: data.instruction
|
|
54707
|
+
});
|
|
54708
|
+
return;
|
|
54709
|
+
}
|
|
54704
54710
|
const compactedCount = this.strategy.computeCompactCount(this.agent.context.history, data.source);
|
|
54705
54711
|
if (compactedCount === 0) throw new KimiError(ErrorCodes.COMPACTION_UNABLE, "No prefix that can be compacted in current history.");
|
|
54706
54712
|
this.agent.records.logRecord({
|
|
@@ -54730,6 +54736,7 @@ var FullCompaction = class {
|
|
|
54730
54736
|
this.markCanceled();
|
|
54731
54737
|
}
|
|
54732
54738
|
markCanceled() {
|
|
54739
|
+
this.agent.replayBuilder.patchLast("compaction", { result: "cancelled" });
|
|
54733
54740
|
if (!this.compacting) return;
|
|
54734
54741
|
this.agent.records.logRecord({ type: "full_compaction.cancel" });
|
|
54735
54742
|
this.compacting.abortController.abort();
|
|
@@ -56644,26 +56651,32 @@ var ContextMemory = class {
|
|
|
56644
56651
|
stoppedAtCompaction: stoppedAtBoundary
|
|
56645
56652
|
} });
|
|
56646
56653
|
}
|
|
56647
|
-
applyCompaction(
|
|
56654
|
+
applyCompaction(result) {
|
|
56648
56655
|
this.agent.records.logRecord({
|
|
56649
56656
|
type: "context.apply_compaction",
|
|
56650
|
-
...
|
|
56657
|
+
...result
|
|
56651
56658
|
});
|
|
56659
|
+
this.agent.replayBuilder.patchLast("compaction", { result: {
|
|
56660
|
+
summary: result.summary,
|
|
56661
|
+
compactedCount: result.compactedCount,
|
|
56662
|
+
tokensBefore: result.tokensBefore,
|
|
56663
|
+
tokensAfter: result.tokensAfter
|
|
56664
|
+
} });
|
|
56652
56665
|
this._history = [{
|
|
56653
56666
|
role: "assistant",
|
|
56654
56667
|
content: [{
|
|
56655
56668
|
type: "text",
|
|
56656
|
-
text:
|
|
56669
|
+
text: result.summary
|
|
56657
56670
|
}],
|
|
56658
56671
|
toolCalls: [],
|
|
56659
56672
|
origin: { kind: "compaction_summary" }
|
|
56660
|
-
}, ...this._history.slice(
|
|
56673
|
+
}, ...this._history.slice(result.compactedCount)];
|
|
56661
56674
|
this.openSteps.clear();
|
|
56662
56675
|
this.flushDeferredMessagesIfToolExchangeClosed();
|
|
56663
|
-
this._tokenCount =
|
|
56676
|
+
this._tokenCount = result.tokensAfter;
|
|
56664
56677
|
this.tokenCountCoveredMessageCount = this._history.length;
|
|
56665
56678
|
this.agent.microCompaction.reset();
|
|
56666
|
-
this.agent.injection.onContextCompacted(
|
|
56679
|
+
this.agent.injection.onContextCompacted(result.compactedCount);
|
|
56667
56680
|
this.agent.emitStatusUpdated();
|
|
56668
56681
|
}
|
|
56669
56682
|
data() {
|
|
@@ -62113,6 +62126,12 @@ var ReplayBuilder = class {
|
|
|
62113
62126
|
time: this.agent.records.restoring?.time ?? Date.now()
|
|
62114
62127
|
});
|
|
62115
62128
|
}
|
|
62129
|
+
patchLast(type, patch) {
|
|
62130
|
+
if (this.agent.records.restoring) {
|
|
62131
|
+
const last = this.records.at(-1);
|
|
62132
|
+
if (last && last.type === type) Object.assign(last, patch);
|
|
62133
|
+
}
|
|
62134
|
+
}
|
|
62116
62135
|
removeLastMessages(removedMessages) {
|
|
62117
62136
|
if (removedMessages.size === 0) return;
|
|
62118
62137
|
for (let i = this.records.length - 1; i >= 0; i--) {
|
|
@@ -65596,7 +65615,8 @@ function summarizeSkill(skill) {
|
|
|
65596
65615
|
path: skill.path,
|
|
65597
65616
|
source: skill.source,
|
|
65598
65617
|
type: skill.metadata.type,
|
|
65599
|
-
disableModelInvocation: skill.metadata.disableModelInvocation
|
|
65618
|
+
disableModelInvocation: skill.metadata.disableModelInvocation,
|
|
65619
|
+
isSubSkill: skill.metadata.isSubSkill
|
|
65600
65620
|
};
|
|
65601
65621
|
}
|
|
65602
65622
|
//#endregion
|
|
@@ -65797,7 +65817,7 @@ function isRecord$11(value) {
|
|
|
65797
65817
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65798
65818
|
}
|
|
65799
65819
|
//#endregion
|
|
65800
|
-
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.md
|
|
65820
|
+
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.md?raw
|
|
65801
65821
|
var custom_theme_default = "---\nname: custom-theme\ndescription: Create or edit a kimi-code custom color theme — a JSON file under the resolved KIMI_CODE_HOME data directory that recolors the TUI. Use when the user wants their own theme, asks for a specific palette or mood, or wants to tweak an existing custom theme's colors.\n---\n\n# Create a kimi-code custom theme (custom-theme)\n\nHelp the user design, write, and apply a custom color theme for the kimi-code TUI. A theme is a single JSON file; the TUI ships with `dark`, `light`, and `auto`, and any file the user adds becomes selectable alongside them.\n\n## Rules of engagement\n\n- **Never write a theme until the user has explicitly clarified what they want.** This skill may only run after the user has confirmed light vs dark, the style or mood, any specific colors they care about, and the intended filename. If any of these are missing, ask before creating files.\n- **Never assume the data directory is `~/.kimi-code`.** Always resolve `$KIMI_CODE_HOME` first with the Bash command below.\n- **Never edit a live theme file in place.** Always create a `.json.new` candidate, validate it, back up the old file, and then `mv` it into place.\n- **Never overwrite an existing theme without reading it first.** Read, back up, then overwrite only after the user confirms.\n\n## Where a theme lives\n\nThe kimi-code runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Theme files live inside the `themes/` subdirectory of that data directory.\n\nBefore doing anything, resolve the actual data root with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved data root — **never assume `~/.kimi-code`**. Theme files live at `<KIMI_CODE_HOME>/themes/<name>.json`. Create the `themes/` directory if it doesn't exist.\n\n## What a theme is\n\n- A theme lives at `<KIMI_CODE_HOME>/themes/<name>.json`.\n- **The filename is the theme name**: `ember.json` shows up in the `/theme` picker as `Custom: ember`.\n- Shape:\n\n ```json\n {\n \"name\": \"ember\",\n \"displayName\": \"Ember\",\n \"colors\": {\n \"primary\": \"#83A598\",\n \"accent\": \"#FE8019\"\n }\n }\n ```\n\n - `name` (required), `displayName` (optional), `base` (optional: `\"dark\"` default, or `\"light\"`), `colors` (each value a 6-digit hex `#RRGGBB`).\n- **Partial themes are fine**: any token you leave out falls back to the **base** palette (`dark` by default; set `\"base\": \"light\"` for a light theme), so you can recolor just a few tokens or all of them.\n\n## Source of truth: the docs token reference\n\nBefore choosing colors, use **FetchURL** to fetch the official custom-theme docs as the authoritative list of tokens and what each controls:\n\n```\nhttps://moonshotai.github.io/kimi-code/en/customization/themes.html\n```\n\nOnly set tokens from this set — unknown keys are silently ignored at load. If FetchURL is unavailable or the fetch fails, fall back to the embedded reference below (it mirrors the same tokens) and tell the user you're working from the built-in list rather than the live docs.\n\n## Color tokens (what each controls)\n\n| Token | Controls |\n| --- | --- |\n| `primary` | The most-used color: links, inline code, the selected item in nearly every dialog, the focused editor border, plan/\"running\" badges, spinners |\n| `accent` | Secondary highlight: approval `▶` prefix, device-code box, image placeholder, BTW / queue panes, registry import |\n| `text` | Body text: dialog bodies, todo titles, footer model label, Markdown headings, assistant/tool message bullets, list bullets |\n| `textStrong` | Emphasized / bold text: input dialogs, status messages |\n| `textDim` | Secondary, dimmed text (the most widely used dim shade): thinking, hints, descriptions, completed todos, Markdown quotes, footer status bar |\n| `textMuted` | Faintest text: counters, scroll info, descriptions, Markdown link URLs, code-block borders |\n| `border` | Pane and editor borders, Markdown horizontal rule |\n| `borderFocus` | Focus / attention border (currently only the approval panel) |\n| `success` | Success state: `✓`, \"enabled\", completed |\n| `warning` | Warning state: auto/yolo badges, stale markers, plan-mode hint |\n| `error` | Error state: error messages, failed tool output |\n| `diffAdded` | Diff added lines |\n| `diffRemoved` | Diff removed lines |\n| `diffAddedStrong` | Diff intra-line changed words, added (bold) |\n| `diffRemovedStrong` | Diff intra-line changed words, removed (bold) |\n| `diffGutter` | Diff line-number gutter |\n| `diffMeta` | Diff meta / hunk headers |\n| `roleUser` | User message bullet and text, skill-activation name (the one role color with its own hue) |\n\n## Workflow\n\n1. **Ask the user what they want first — before choosing any colors.** Clarify, in one short exchange:\n - **Light or dark?** A light theme (dark text on a light background) or a dark theme (light text on a dark background). This sets the whole direction, so settle it first. For a light theme, set `\"base\": \"light\"` so the tokens you leave out inherit the light palette instead of dark.\n - **What style / mood?** e.g. warm vs cool, vivid vs muted, high vs low contrast, a named vibe (\"nord\", \"solarized\", \"sunset\"), or a base to start from (an existing theme, or `dark` / `light`).\n - **Any specific colors?** Whether they have exact hex values to anchor on (a brand color, a preferred `primary`, etc.).\n\n For the discrete choices (light vs dark, a few style options), prefer **AskUserQuestion** if it is available. If you are running in **auto mode** and `AskUserQuestion` is unavailable, ask the same question as a plain-text message with clear numbered or bulleted options, and wait for the user's reply. Don't start picking colors until you at least know light-vs-dark and the rough style.\n\n2. **Resolve the actual theme directory and current theme(s).**\n - Resolve the data root by checking `echo \"$KIMI_CODE_HOME\"`; if empty, use `echo \"$HOME/.kimi-code\"`. Use `<root>/themes` for every subsequent step.\n - If tweaking an existing custom theme, **Read** `<KIMI_CODE_HOME>/themes/<name>.json` first — never overwrite a theme you haven't read.\n - Starting fresh: build a `colors` object from the token table. You can `ls <KIMI_CODE_HOME>/themes/` and Read one of the user's existing themes as a reference for the format.\n\n3. **Pick a starting point and choose colors deliberately.**\n - Every value is a 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Keep contrast usable against the user's terminal background: don't let `text` / `textDim` sit too close to the background, and keep `success` / `warning` / `error` clearly distinguishable from each other.\n - `primary` is the most-seen color (links, selection, focus) — make it readable and distinct from `text`.\n - `roleUser` is the one role color meant to stand on its own — give it a distinct hue.\n\n4. **Create a candidate file; never edit the live theme in place.**\n - Use Bash to create a candidate. If the target theme already exists, copy it verbatim: `cp <name>.json <name>.json.new` (inside `<KIMI_CODE_HOME>/themes/`). If it doesn't exist, use **Write** to create a minimal skeleton named `<name>.json.new`.\n - Use **Edit** on the candidate to change only the intended keys. Keep every existing entry, comment, and formatting intact.\n\n5. **Validate the candidate before overwriting.**\n - Read the candidate with **Read** to visually confirm it is well-formed JSON and that every `colors` value is a full 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Invalid hex values are silently skipped at load (they fall back to the base palette), but fix them so the theme renders as intended.\n\n6. **Back up and overwrite.**\n - Back up the old file first — **always** create a new timestamped backup and never overwrite an existing backup: `cp <name>.json \"<name>.json.$(date +%Y%m%d-%H%M%S).bak\"`.\n - If the target didn't exist, skip the backup.\n - Overwrite with the candidate: `mv <name>.json.new <name>.json`.\n\n7. **Tell the user how to apply it** (next section).\n\n## Applying the theme\n\n- The `/theme` picker re-scans the themes directory every time it opens, so a newly added file shows up **without restarting** — tell the user to run `/theme` and choose `Custom: <name>`.\n- Or set it in `tui.toml`: `theme = \"<name>\"`.\n- **Editing the active theme**: changes to the theme that's *currently in use* are not auto-reloaded. Tell the user to run **`/reload-tui`** (or switch to another theme and back). Re-selecting the **same** theme in `/theme` is a no-op (\"Theme unchanged\").\n\n## Don'ts\n\n- **Don't start creating or editing a theme until the user has clarified light/dark, style/mood, any specific colors, and the filename.** If anything is unclear, ask — don't guess.\n- Don't invent token names — only use the documented set; unknown keys are silently ignored.\n- Don't write 3-digit hex or named colors — use full `#RRGGBB`.\n- Never edit the live theme file in place; work through a candidate and validate before `mv`.\n- Before overwriting an existing theme file, **read it and back it up** so the user can recover.\n- Don't tell the user to restart the app to apply a theme — `/theme` or `/reload-tui` is enough.\n";
|
|
65802
65822
|
//#endregion
|
|
65803
65823
|
//#region ../../packages/agent-core/src/skill/builtin/custom-theme.ts
|
|
@@ -65819,7 +65839,7 @@ const CUSTOM_THEME_SKILL = {
|
|
|
65819
65839
|
}
|
|
65820
65840
|
};
|
|
65821
65841
|
//#endregion
|
|
65822
|
-
//#region ../../packages/agent-core/src/skill/builtin/import-from-cc-codex.md
|
|
65842
|
+
//#region ../../packages/agent-core/src/skill/builtin/import-from-cc-codex.md?raw
|
|
65823
65843
|
var import_from_cc_codex_default = "---\nname: import-from-cc-codex\ndescription: Import Claude Code and Codex instructions, skills, and MCP settings into Kimi Code.\ndisable-model-invocation: true\n---\n\n# Import from Claude Code and Codex\n\nThe user invoked `/import-from-cc-codex` (or `/skill:import-from-cc-codex`).\nHelp them migrate selected local Claude Code and Codex assets into Kimi Code.\nThis skill is intentionally conservative: it imports only instructions, skills,\nand MCP server declarations from `.claude` / `.codex` surfaces, with a user\npreview before any write.\n\n## Non-negotiable rules\n\n- Do **not** migrate `.agents` content. Kimi Code already supports `.agents`\n skills and AGENTS files by default.\n- Do **not** migrate Claude custom commands (`.claude/commands/**`). They are\n out of scope for this importer.\n- Do **not** migrate credentials, OAuth tokens, sessions, history, logs, hooks,\n plugins, plugin caches, output styles, or custom agents/subagents.\n- Do **not** run or install anything from the source directories.\n- Do **not** write anything until the user has chosen what to migrate, reviewed\n the final preview, and explicitly confirmed applying it.\n- Only write under Kimi Code targets:\n - User-global: `$KIMI_CODE_HOME` if set, otherwise `~/.kimi-code`.\n - Project instructions/skills: `<project root>/.kimi-code`, where the project\n root is the nearest parent directory containing `.git`; if no `.git` exists,\n use the current working directory.\n - Project-local MCP: `<cwd>/.kimi-code/mcp.json`, because Kimi reads the\n current working directory's Kimi-specific MCP file, not every project-root\n `.kimi-code/mcp.json` from subdirectories.\n- Preserve existing Kimi files. Never overwrite existing skills or replace an\n existing AGENTS.md / mcp.json wholesale.\n\n## Conversation flow\n\n### 1. Ask what to migrate first\n\nBefore reading source files, ask the user which categories to migrate. Use\n`AskUserQuestion` when available; otherwise ask in plain text and stop. Offer a\nmulti-select choice:\n\n- Instructions (`AGENTS.md` / `CLAUDE.md`)\n- Skills\n- MCP settings\n- All of the above\n\nIf the user already gave a preference in the invocation arguments, present it as\nthe default/recommended choice, but still ask for confirmation of the categories.\nIf the user dismisses or refuses the question, stop.\n\n### 2. Scan only the chosen categories\n\nResolve paths explicitly; `~` is the real OS home, and Kimi home follows\n`$KIMI_CODE_HOME` before `~/.kimi-code`.\n\nUser-level sources:\n\n- Claude instructions:\n - `~/.claude/AGENTS.md`\n - `~/.claude/CLAUDE.md`\n- Claude skills:\n - `~/.claude/skills/`\n- Claude MCP candidates:\n - `~/.claude.json` only when MCP is selected. Claude Code stores user-level\n MCP declarations there; do not read it for instruction/skill-only imports.\n- Codex instructions:\n - `~/.codex/AGENTS.md`\n - `~/.codex/CLAUDE.md` if present\n- Codex skills:\n - `~/.codex/skills/`\n- Codex MCP candidates:\n - `~/.codex/config.toml`\n\nProject-level sources, rooted at the project root:\n\n- Claude instructions:\n - `<project root>/.claude/AGENTS.md`\n - `<project root>/.claude/CLAUDE.md`\n- Claude skills:\n - `<project root>/.claude/skills/`\n- Codex instructions:\n - `<project root>/.codex/AGENTS.md`\n - `<project root>/.codex/CLAUDE.md` if present\n- Codex skills:\n - `<project root>/.codex/skills/`\n- Codex MCP candidates:\n - `<project root>/.codex/config.toml`\n\nDo not scan project-root `AGENTS.md`, project-root `CLAUDE.md`, `.agents/**`, or\nproject-root `.mcp.json` in this skill. `AGENTS.md` and `.agents/**` are already\nKimi-readable, and project-root `.mcp.json` is already read by Kimi as a\nClaude-compatible MCP file.\n\n### 3. Build an import plan\n\nCreate a plan with three sections: instructions, skills, and MCP. Include exact\nsource and target paths.\n\n#### Instructions plan\n\nMap user-level instruction sources to:\n\n- `$KIMI_CODE_HOME/AGENTS.md`, or `~/.kimi-code/AGENTS.md` if the env var is not\n set.\n\nMap project-level instruction sources to:\n\n- `<project root>/.kimi-code/AGENTS.md`\n\nAppend imported instruction content as marked blocks. Do not duplicate a block\nthat already exists in the target file.\n\nUse this marker shape:\n\n```md\n<!-- Imported from Claude Code: /absolute/source/path -->\n\n<source content>\n\n<!-- End imported from Claude Code: /absolute/source/path -->\n```\n\nFor Codex, use `Imported from Codex` / `End imported from Codex`.\n\nIf a source file is empty, skip it and report it as skipped. If the target exists\nand cannot be read as UTF-8 text, stop before writing and report the blocker.\n\n#### Skills plan\n\nMap user-level skill sources to:\n\n- `$KIMI_CODE_HOME/skills/`, or `~/.kimi-code/skills/` if the env var is not set.\n\nMap project-level skill sources to:\n\n- `<project root>/.kimi-code/skills/`\n\nRecognize these skill shapes under `.claude/skills/` or `.codex/skills/`:\n\n- Directory bundle: `<skill-name>/SKILL.md`\n- Flat markdown skill: `<skill-name>.md`\n\nCopy the entire directory bundle or flat markdown file. Preserve supporting\nfiles inside bundles. Do not copy hidden directories, `node_modules`, caches, or\nplugin-managed folders.\n\nBefore planning a copy:\n\n- Read a bundle's `SKILL.md` enough to verify that directory skills have\n frontmatter with non-empty `name` and `description`, because Kimi requires\n those fields for directory skills.\n- If the target top-level entry already exists, skip it; do not overwrite.\n- If two source entries would write the same target path, keep the first one in\n this order and report the later one as skipped:\n 1. project Claude\n 2. project Codex\n 3. user Claude\n 4. user Codex\n- Warn when a source skill uses Claude/Codex-specific fields or syntax that Kimi\n may not interpret the same way, such as `allowed-tools`, `disallowed-tools`,\n `context: fork`, `agent`, `hooks`, `paths`, dynamic shell injection with\n ``!`command` ``, or `agents/openai.yaml`. Preserve the file; do not rewrite it\n unless the user explicitly asks.\n\nDo not convert `.claude/commands/*.md`. Commands are out of scope.\n\n#### MCP plan\n\nDo not edit `mcp.json` directly in this import skill. Prepare MCP entries for\nmanual follow-up with `/mcp-config`; that built-in skill is user-invocable only,\nso you must not try to call it through the `Skill` tool.\n\nFor the preview, collect MCP candidates and normalize them into Kimi's MCP shape\nwhen possible:\n\n```json\n{\n \"mcpServers\": {\n \"name\": {\n \"command\": \"...\",\n \"args\": [\"...\"],\n \"env\": { \"KEY\": \"VALUE\" }\n }\n }\n}\n```\n\nClaude user MCP:\n\n- Read `~/.claude.json` only if MCP was selected.\n- Look for a top-level `mcpServers` object.\n- Keep stdio entries with `command`; keep HTTP entries with `url`.\n- Preserve `args`, `env`, `cwd`, `enabled`, `enabledTools`, `disabledTools`,\n `startupTimeoutMs`, `toolTimeoutMs`, `headers`, and `bearerTokenEnvVar` when\n present and valid.\n- Drop unsupported or malformed entries and report why.\n\nCodex MCP:\n\n- Read selected `config.toml` files only if MCP was selected.\n- Look for `[mcp_servers.<name>]` tables.\n- Map Codex fields to Kimi fields:\n - `command` -> `command`\n - `args` -> `args`\n - `env` -> `env`\n - `cwd` -> `cwd`\n - `url` -> `url`\n - `bearer_token_env_var` -> `bearerTokenEnvVar`\n - `enabled` -> `enabled`\n - `enabled_tools` -> `enabledTools`\n - `disabled_tools` -> `disabledTools`\n - `startup_timeout_sec` -> `startupTimeoutMs` in milliseconds\n - `tool_timeout_sec` -> `toolTimeoutMs` in milliseconds\n - `http_headers` -> `headers`\n- Drop unsupported Codex-only fields and report them, especially `required`,\n `default_tools_approval_mode`, `tools.<tool>.approval_mode`,\n `env_vars`, `env_http_headers`, and `experimental_environment`.\n- Do not import project-root `.mcp.json`; Kimi already reads it.\n\nFor each MCP candidate, choose the target scope in the preview:\n\n- User-level source -> user-global MCP target (`$KIMI_CODE_HOME/mcp.json` or\n `~/.kimi-code/mcp.json`).\n- Project-level source -> project-local Kimi MCP target (`<cwd>/.kimi-code/mcp.json`). If `<cwd>` is not the project root, call this out in the preview so the user understands when Kimi will load it.\n\nWarn that stdio MCP entries spawn commands at session start, and the user should\nonly import MCP servers they trust. Warn if an MCP entry contains apparent\nliteral secrets in `env`, `headers`, or token-like fields; prefer env-var\nreferences.\n\nAfter the user confirms applying the final preview, do not write MCP config and\ndo not invoke `mcp-config` programmatically. Instead, finish the non-MCP writes\nand show a copy-pasteable manual follow-up for the user, including:\n\n- the `/mcp-config` command they should run,\n- target scope and target path,\n- the normalized JSON entry or entries to add,\n- collision policy: keep existing Kimi entries on name conflict,\n- the reminder that unrelated entries must be preserved.\n\nMake it clear that MCP import is pending until the user manually runs\n`/mcp-config` with the prepared entries.\n\n### 4. Show the final preview and stop\n\nAfter scanning, show a concise final preview grouped by target file/directory:\n\n- Will append instruction blocks\n- Will copy skill bundles/files\n- Will leave these MCP entries pending for a manual `/mcp-config` follow-up\n- Already present / skipped\n- Warnings and blockers\n\nThen ask for explicit confirmation before writing. Use a clear choice such as:\n\n- Apply import\n- Cancel\n\nIf there are blockers, do not offer apply; explain what must be fixed first.\n\n### 5. Apply only after confirmation\n\nWhen the user confirms:\n\n- Create target directories with private permissions where possible.\n- Append instruction blocks without duplicating existing imported source blocks.\n- Copy skills without overwriting existing target entries.\n- Do not write MCP entries. Show the prepared `/mcp-config` follow-up command\n and mark MCP import as pending user action.\n- Report exactly what changed and what was skipped.\n- Tell the user to start a new session (for example `/new`) or restart Kimi Code\n for newly imported skills, instructions, and MCP servers to be picked up.\n\n## Output style\n\nBe brief but precise. Use absolute paths in previews and summaries. Prefer a\nsmall table or bullet list over long prose. If nothing is found for a selected\ncategory, say so and do not treat it as an error.\n";
|
|
65824
65844
|
//#endregion
|
|
65825
65845
|
//#region ../../packages/agent-core/src/skill/builtin/import-from-cc-codex.ts
|
|
@@ -65841,7 +65861,7 @@ const IMPORT_FROM_CC_CODEX_SKILL = {
|
|
|
65841
65861
|
}
|
|
65842
65862
|
};
|
|
65843
65863
|
//#endregion
|
|
65844
|
-
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.md
|
|
65864
|
+
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.md?raw
|
|
65845
65865
|
var mcp_config_default = "---\nname: mcp-config\ndescription: Configure MCP servers and handle MCP OAuth login.\n---\n\n# Interactive MCP server configuration\n\nThe user invoked this skill through `/mcp-config` or `/skill:mcp-config`.\nEither they want to log into an MCP server that asked for OAuth, or they\nwant to edit the `mcp.json` that lists MCP servers. The work is small and\nlocal — handle it on this turn yourself, no agents or planning todos.\n\nPick the flow from the user's message and your tool list:\n\n- An `mcp__<server>__authenticate` tool is in your list, the user says\n \"log in\" / \"auth\" / \"sign in\", they invoke `/mcp-config login\n <server>`, or they quote a `needs-auth` status → **Login**.\n- Add / edit / remove / list of an `mcp.json` entry → **Config edit**.\n- Bare `/mcp-config` with no `authenticate` tool in your list →\n **Config edit**. If there were a pending login, the authenticate tool\n would be in your list.\n\n## Login\n\nEach MCP server in `needs-auth` exposes one `mcp__<server>__authenticate`\ntool. Call it for the server the user means — its own description owns\nthe OAuth UX (printing the URL, blocking on the callback, reconnecting on\nsuccess). Surface its output verbatim, including the authorization URL\nunchanged; the URL contains state and PKCE parameters that break if\nedited.\n\nIf the user named a server that has no authenticate tool, say so in one\nsentence and stop — do **not** fall into config edit. They're trying to\nlog in to a server that isn't currently waiting for login; quietly\nrewriting `mcp.json` would be the wrong fix. If multiple authenticate\ntools exist and the user didn't name one, ask which.\n\n## Config edit\n\nConfig lives in three files; on key collision, later entries in this\nprecedence order override earlier ones.\n\nThe kimi-code runtime resolves the user-global directory as `KIMI_CODE_HOME`\nfirst, falling back to `~/.kimi-code`. Before touching the user-global file,\nresolve the actual directory with Bash so you don't read or write the wrong\none. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code`\nwhen it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the\nrest of this skill, `<KIMI_CODE_HOME>` means that resolved data root —\n**never assume `~/.kimi-code`**.\n\n- User-global: `<KIMI_CODE_HOME>/mcp.json`. Use for servers you want\n everywhere.\n- Project-root: `<project root>/.mcp.json`, where project root is found\n by walking up from `<cwd>` to the nearest `.git`. Use for\n Claude-compatible, repo-shared, or cross-agent servers.\n- Project-local: `<cwd>/.kimi-code/mcp.json`. Use for Kimi-specific\n overrides in the current working directory.\n\nMention once that project-root and project-local stdio entries spawn\ncommands at session start, so they should only live in trusted repos.\n\nAll three files wrap their entries the same way:\n\n```json\n{ \"mcpServers\": { \"<name>\": { /* entry */ } } }\n```\n\nA minimal stdio entry needs `command` (+ optional `args`, `env`, `cwd`).\nFor project-root `.mcp.json`, stdio entries run from the project root by\ndefault; relative `cwd` values are resolved against the directory that\ncontains `.mcp.json`.\nA minimal http entry needs `url`; add `bearerTokenEnvVar: \"ENV_NAME\"` for\nservers that authenticate with a static bearer token from the\nenvironment. Servers that use OAuth take no token field — the login flow\nabove handles them. `transport` is inferred from `command` vs `url`, so\nomit it. For less common fields (`enabled`, `startupTimeoutMs`,\n`toolTimeoutMs`, `enabledTools`, `disabledTools`, `headers`) the source of\ntruth is `McpServerStdioConfigSchema` / `McpServerHttpConfigSchema` in\n`packages/agent-core/src/config/schema.ts`.\n\nIf the user only wants to **see** what's configured, read all three files,\nshow a merged view with enough source-path context to inspect or remove a\nserver from the file that actually declared it, and stop — no scope\nprompt, no write.\n\nFor changes, the flow is:\n\n1. **Pick a scope.** Infer it from the user's words when you can\n (global / everywhere / all projects → user-global; root / repo /\n shared / cross-agent / Claude / `.mcp.json` → project-root; cwd /\n current directory / Kimi-specific / `.kimi-code` → project-local). When\n the request is genuinely scope-less, use one `AskUserQuestion` to ask\n user-global vs project-root vs project-local, defaulting to\n user-global. Use plain text for every other question — `AskUserQuestion`\n is a poor fit for free-form input. If the user dismisses the scope\n question, stop; you can't safely guess where they wanted the change.\n2. **Read and announce.** Read the target file (a missing or empty file\n is fine; you'll create `{ \"mcpServers\": {} }`). If JSON parsing fails,\n surface the error verbatim and stop — silently overwriting a broken\n file could destroy work. Then show the user the target path, what's\n currently in it, and the entry you're about to write or delete. This\n is for transparency, not a confirmation gate — the Edit/Write\n permission prompt is the real gate, and your message is what gives\n the user context when that prompt appears. In yolo / afk modes there\n is no prompt, which is those modes' explicit contract.\n3. **Write and tell them how to reload MCP servers.** Preserve unrelated\n entries and the `mcpServers` wrapper. MCP servers load at session\n start, so tell the user to start a new session (for example `/new`) or\n restart `kimi-code` for the change to take effect.\n\n## Secrets\n\nDon't store secrets (tokens, keys, passwords) as literals in\n`mcp.json` — it's a plain config file on disk. http servers should use\n`bearerTokenEnvVar` to reference an env var instead; if a stdio entry\nmust inline one in `env`, warn the user before writing.\n";
|
|
65846
65866
|
//#endregion
|
|
65847
65867
|
//#region ../../packages/agent-core/src/skill/builtin/mcp-config.ts
|
|
@@ -65863,13 +65883,13 @@ const MCP_CONFIG_SKILL = {
|
|
|
65863
65883
|
}
|
|
65864
65884
|
};
|
|
65865
65885
|
//#endregion
|
|
65866
|
-
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/consolidate/SKILL.md
|
|
65867
|
-
var SKILL_default$2 = "---\nname:
|
|
65886
|
+
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/consolidate/SKILL.md?raw
|
|
65887
|
+
var SKILL_default$2 = "---\nname: consolidate\ndescription: Apply an approved sub-skill grouping by moving user-specified skills into a parent bundle, with timestamped backups of every modified directory.\ndisable-model-invocation: true\n---\n\n# Consolidate sub-skills (`sub-skill.consolidate`)\n\nExecute the reorganization by moving user-specified skills into a parent bundle, forming a sub-skill hierarchy.\n\n## When to use\n\n- The user has approved a grouping proposal (typically from `sub-skill.review`) and wants to apply it.\n- Migrating standalone skills into a new or existing parent bundle.\n\n## Process\n\n1. **Confirm the plan.** Restate which skills will move and where, and ask the user to confirm **before** making any file changes.\n2. **Back up every original skill directory.** Before moving anything, create a timestamped backup of each skill directory that will be modified.\n - For a skill at `<root>/<skill-name>/SKILL.md`, back up the entire `<skill-name>` directory:\n ```bash\n cp -r <skill-name> \"<skill-name>.$(date +%Y%m%d-%H%M%S).bak\"\n ```\n - Keep all backups; never overwrite an existing backup file.\n3. **Create or update the parent bundle.**\n - If the parent does not exist, create `<parent-name>/SKILL.md` with `has-sub-skill: true` in the frontmatter.\n - If the parent already exists, ensure its frontmatter includes `has-sub-skill: true`.\n4. **Move child skills into the parent.** Move each child skill's entire directory under the parent bundle.\n - Example: `web-search/` → `web-research/web-search/`\n5. **Keep documentation directory alignment.** When moving documentation, references, examples, assets, or other payload directories, align them with the new skill directory layout.\n - Preserve relative links from `SKILL.md` to files such as `references/`, `assets/`, `examples/`, or templates.\n - If a child skill moves from `<root>/<child>/` to `<root>/<parent>/<child>/`, its documentation payload should move with that child unless the approved plan says otherwise.\n - Do not leave documentation in the old location or merge unrelated documentation directories together.\n6. **Verify the result.** List the new directory structure and confirm each moved skill still has a valid `SKILL.md` with required frontmatter (`name` and `description`). Check documentation directory alignment and relative links after the move.\n7. **Report the change.** Summarize what was moved, the new structure, any documentation directories that moved, and where backups are located.\n\n## Don'ts\n\n- **Never move skills without backing up first.**\n- **Never overwrite an existing backup** — always use a fresh timestamped suffix.\n- **Don't drop frontmatter or payload files** during the move; the entire directory must be preserved.\n- **Don't break documentation directory alignment** — references, assets, examples, and templates must stay aligned with the skill directory that uses them.\n- **Don't create deeply nested hierarchies** (3+ levels) unless the user explicitly requests it.\n";
|
|
65868
65888
|
//#endregion
|
|
65869
|
-
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/review/SKILL.md
|
|
65870
|
-
var SKILL_default$1 = "---\nname:
|
|
65889
|
+
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/review/SKILL.md?raw
|
|
65890
|
+
var SKILL_default$1 = "---\nname: review\ndescription: Analyze the available skill set and recommend candidate groups that could be consolidated into sub-skill bundles. Read-only — proposes a plan, does not move files.\ndisable-model-invocation: true\n---\n\n# Review sub-skills (`sub-skill.review`)\n\nAnalyze the current skill inventory and identify candidate groups that could be consolidated into sub-skill bundles. This sub-skill is **read-only**: it produces a proposal for the user to review before any file changes are made by `sub-skill.consolidate`.\n\n## When to use\n\n- The user asks to review, reorganize, or audit the skill inventory.\n- There are many loosely-related skills that might benefit from hierarchical grouping.\n- The user wants to evaluate whether a new skill should be a sub-skill of an existing parent.\n\n## Process\n\n1. **List the current inventory.** Use the skill registry (or scan the configured skill roots) to get a full list of skill names, descriptions, and source scopes.\n2. **Categorize by domain.** Group skills by functional domain — e.g. file operations, web tools, collaboration, observability.\n3. **Detect coupling.** Identify skills that are frequently used together or share similar `whenToUse` conditions.\n4. **Flag granularity issues.** Call out skills that are too fine-grained (e.g. one skill per CLI flag) or too broad to land in any single domain.\n5. **Propose sub-skill structures.** For each candidate group, list:\n - The parent skill name and a one-line description.\n - The children that should move under it.\n - Any documentation, reference, example, asset, or template directories that must move with each child so the final directory layout stays aligned.\n - Whether the parent needs `has-sub-skill: true` (it does, if children should be discovered).\n6. **Output a summary report.** Present findings as a concise grouped list with rationale, and stop. Do **not** edit any file — that's `sub-skill.consolidate`'s job.\n\n## Criteria for a good sub-skill grouping\n\n- **Shared context.** Children operate within the same domain or workflow.\n- **Composable entry point.** The parent gives a natural top-level handle; children handle specifics.\n- **Shallow nesting.** Prefer 2 levels (parent → child). Avoid 3+ unless strictly necessary.\n- **Backward compatibility.** Existing skill names should ideally remain discoverable.\n\n## Example output format\n\n```\nProposed sub-skill: web-research\n - Parent: web-research (has-sub-skill: true)\n - Children:\n - web-search → move under web-research/search\n - fetch-url → move under web-research/fetch\n - Documentation alignment: move each child's references/examples/assets with that child.\n - Rationale: Both deal with online information retrieval and are often chained together.\n```\n";
|
|
65871
65891
|
//#endregion
|
|
65872
|
-
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/SKILL.md
|
|
65892
|
+
//#region ../../packages/agent-core/src/skill/builtin/sub-skill/SKILL.md?raw
|
|
65873
65893
|
var SKILL_default = "---\nname: sub-skill\ndescription: Discover and reorganize the skill inventory into hierarchical sub-skill bundles. Use when the user asks to review, group, or consolidate skills into a parent bundle.\ndisable-model-invocation: true\nhas-sub-skill: true\n---\n\n# Sub-skill\n\nContainer skill for analyzing the local skill inventory and reorganizing it into hierarchical sub-skill bundles (`has-sub-skill: true` parents with children inside).\n\n## When to use\n\n- The user asks to review, reorganize, or consolidate skills.\n- There are many loosely-related skills that might benefit from hierarchical grouping.\n- The user wants to evaluate whether a new skill should be a sub-skill of an existing one.\n\n## Sub-skills\n\n- **`sub-skill.review`** — Analyze the current inventory and propose candidate sub-skill groupings.\n- **`sub-skill.consolidate`** — Apply an approved grouping by moving skills into a parent bundle, with timestamped backups.\n\nThe usual flow is `sub-skill.review` first (read-only proposal), then `sub-skill.consolidate` after the user approves a plan. Never run consolidate without an explicit go-ahead from the user.\n";
|
|
65874
65894
|
//#endregion
|
|
65875
65895
|
//#region ../../packages/agent-core/src/skill/builtin/sub-skill.ts
|
|
@@ -65882,6 +65902,7 @@ function makeBuiltin(body, dirName, pseudoPath, extraMetadata = {}) {
|
|
|
65882
65902
|
});
|
|
65883
65903
|
return {
|
|
65884
65904
|
...parsed,
|
|
65905
|
+
name: dirName,
|
|
65885
65906
|
path: pseudoPath,
|
|
65886
65907
|
dir: pseudoPath,
|
|
65887
65908
|
metadata: {
|
|
@@ -65895,10 +65916,16 @@ const SUB_SKILL_PARENT = makeBuiltin(SKILL_default, "sub-skill", "builtin://sub-
|
|
|
65895
65916
|
disableModelInvocation: true,
|
|
65896
65917
|
"has-sub-skill": true
|
|
65897
65918
|
});
|
|
65898
|
-
const SUB_SKILL_REVIEW = makeBuiltin(SKILL_default$1, "sub-skill.review", "builtin://sub-skill/review", {
|
|
65899
|
-
|
|
65919
|
+
const SUB_SKILL_REVIEW = makeBuiltin(SKILL_default$1, "sub-skill.review", "builtin://sub-skill/review", {
|
|
65920
|
+
disableModelInvocation: true,
|
|
65921
|
+
isSubSkill: true
|
|
65922
|
+
});
|
|
65923
|
+
const SUB_SKILL_CONSOLIDATE = makeBuiltin(SKILL_default$2, "sub-skill.consolidate", "builtin://sub-skill/consolidate", {
|
|
65924
|
+
disableModelInvocation: true,
|
|
65925
|
+
isSubSkill: true
|
|
65926
|
+
});
|
|
65900
65927
|
//#endregion
|
|
65901
|
-
//#region ../../packages/agent-core/src/skill/builtin/update-config.md
|
|
65928
|
+
//#region ../../packages/agent-core/src/skill/builtin/update-config.md?raw
|
|
65902
65929
|
var update_config_default = "---\nname: update-config\ndescription: Inspect or edit kimi-code's own config — `config.toml` (model, provider, permission, hooks) and `tui.toml` (theme, editor, notifications, auto-update). Use when the user asks what a setting does or wants to change one.\n---\n\n# Configure kimi-code (update-config)\n\nHelp the user inspect, change, and validate kimi-code's configuration files. The files are **TOML** with **snake_case** keys.\n\n## The two config files\n\nkimi-code has two TOML config files, both under `<KIMI_CODE_HOME>/`, both snake_case, but with different ownership — decide which one the user means before doing anything.\n\nThe runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Before doing anything, resolve the actual directory with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved root — **never assume `~/.kimi-code`**.\n\n- **`config.toml`** — agent / runtime settings: `default_model`, `providers`, `models`, `thinking`, `permission`, `hooks`, `loop_control`, etc.\n- **`tui.toml`** — terminal-UI / client preferences: `theme`, `[editor].command`, `[notifications]`, `[upgrade].auto_install` (auto-update). These can usually also be changed with the interactive commands `/config`, `/theme`, `/editor`, which is easier — prefer pointing the user at those.\n\nThe \"read → copy → Edit → validate → back up → overwrite\" flow below applies to both files; only **which reload command applies** differs (see Capability 4).\n\n## Prerequisite 1: the official docs are the single source of truth\n\nBefore touching any config, use **FetchURL** to fetch the official config docs as the one authoritative reference for fields (key names, types, allowed values, owning section):\n\n```\nhttps://moonshotai.github.io/kimi-code/en/configuration/config-files.html\n```\n\n- Use the **snake_case key names and sections exactly as documented** — don't invent them, don't guess camelCase.\n- If FetchURL is unavailable or the fetch fails, tell the user plainly that you can't reach the online docs, and ask them to paste the relevant section or confirm whether to proceed from what you already know. **Never edit blindly without an authoritative reference.**\n\n## Prerequisite 2: read the target file before any change\n\nBefore any modification, use **Read** on the target config file (decide whether it's `config.toml` or `tui.toml` per the above):\n\n- Location: `<KIMI_CODE_HOME>/config.toml` or `<KIMI_CODE_HOME>/tui.toml`. For other scopes/files, defer to the official docs.\n- A missing or empty file is fine — you'll create a minimal skeleton later.\n- If the file exists but **fails to parse as TOML**, report the error verbatim and **stop** — never overwrite a broken file in place (it could destroy the user's existing config).\n\n---\n\n## Capability 1: explain configuration (read-only, no file changes)\n\nWhen the user asks \"what config is there\", \"what does this setting do\", or \"how do I use it\":\n\n1. Fetch the official docs (Prerequisite 1).\n2. Read the current `config.toml` (Prerequisite 2).\n3. Answer against both: list the relevant sections / keys, what each is for, **current value vs default**, and the allowed-value range; say which file and section each lives in.\n4. Present it as a compact grouped list or table. **Stay read-only — write no files.**\n\n## Capability 2: make changes for the user (copy → Edit → validate → back up → overwrite)\n\nDon't edit the target file in place, and **don't rewrite it from scratch** — instead copy it, Edit the copy, and keep the original out of any broken state the whole time:\n\n1. **Clarify intent**: which key, what value, and which file (`config.toml` or `tui.toml`). Ask in one line if ambiguous; for discrete choices (e.g. scope) AskUserQuestion is fine, but use plain questions for free-form input.\n2. **Read the target file** (Prerequisite 2): Read it to understand the current state and confirm it parses.\n3. **Copy out a candidate (do not create from scratch)**: use **Bash** to copy the target verbatim — `cp config.toml config-new.toml` (same directory, `-new` suffix; for tui.toml, `cp tui.toml tui-new.toml`). **Leave the original untouched for now.**\n - Only when the target doesn't exist (nothing to copy) should you use **Write** to create a minimal skeleton candidate (e.g. just the comment line `# <KIMI_CODE_HOME>/config.toml`).\n4. **Edit the candidate**: use the **Edit** tool on the candidate to **change/add only the target key** — never rewrite the whole file. That way every existing section, entry, comment, and bit of formatting stays exactly as-is; only what should change changes. The candidate is identical to the original, so use the content you read in step 2 to locate the Edit anchor. Check the change against the official docs (key / section / value type / allowed values, snake_case).\n5. **Validate the candidate** (see Capability 3, via `kimi doctor`). **If anything fails, keep Editing the candidate and re-validate, looping until it all passes.**\n6. **Back up and overwrite** (only after validation fully passes):\n - **Back up the old file — always create a new timestamped backup, keep all of them, never overwrite an existing backup.** Copy this exactly with **Bash** (for config.toml): `cp config.toml \"config.toml.$(date +%Y%m%d-%H%M%S).bak\"`; for tui.toml: `cp tui.toml \"tui.toml.$(date +%Y%m%d-%H%M%S).bak\"`. Skip the backup only if the target didn't exist.\n - Overwrite with the candidate: `mv config-new.toml config.toml`.\n - If reload errors after the overwrite, the user can recover from **the most recent timestamped backup**.\n7. Tell the user how to apply it (see Capability 4).\n\n## Capability 3: validate the candidate file (must pass before overwrite)\n\nUse **`kimi doctor`** to validate the candidate you wrote — it doesn't start the TUI and doesn't modify any file; it runs kimi's own parser + schema (syntax and schema together), so it's the authoritative check. Pick the subcommand by which file you changed, and pass the **candidate** path explicitly:\n\n- changed `config.toml` → `kimi doctor config <config-new.toml path>`\n- changed `tui.toml` → `kimi doctor tui <tui-new.toml path>`\n\nWhen a path is passed explicitly the file must exist (your candidate does, so that's fine). **Exit code 0 = pass (valid or skipped); non-zero = a specified file is missing or the config is invalid** — show the output verbatim, fix the candidate, and re-run, looping until it's 0.\n\nThen do two checks `kimi doctor` can't:\n\n1. **Cross-check values against the official docs** (single source of truth): are the key / section / enum values as documented, and snake_case? doctor guarantees \"schema-valid\", but \"valid yet not what the user wanted\" (e.g. a misspelled model alias) needs the docs.\n2. **Completeness**: every existing entry is still present (the candidate fully replaces the target — a dropped line is a deletion).\n\n> To also check whether the currently **active** config is OK overall, run `kimi doctor` with no path (it checks the default `config.toml` + `tui.toml`, showing a missing one as skipped).\n\n## Capability 4: tell the user how to apply changes\n\nOnce local validation passes, tell the user how to make the change take effect — **the reload command depends on which file you changed**:\n\n- changed **`config.toml`** → run **`/reload`** in the TUI (reloads the session and applies `config.toml`; it also reloads `tui.toml`).\n- changed **`tui.toml`** → run **`/reload-tui`** (reloads only `tui.toml`, lighter); `/reload` works too (reloads both).\n- changed both → a single **`/reload`** covers it.\n\nNote: `/reload` is available **only when idle** — if a reply is streaming, press Esc / Ctrl-C to stop first. `kimi doctor` already validated the schema before the overwrite, so reload should apply cleanly; if it still errors, follow the message to fix it or recover from the most recent timestamped backup. If you don't want to reload now, the **next new session** picks it up automatically.\n\n## Don'ts\n\n- **Always back up before overwriting**, with a **timestamped name and all history kept** — don't skip the backup, don't keep only a single `.bak`, don't overwrite an old backup.\n- Don't drop unrelated entries (the candidate fully replaces the target — a dropped line is a deletion).\n- When you can't reach the docs / have no authoritative reference, don't edit by guessing.\n";
|
|
65903
65930
|
//#endregion
|
|
65904
65931
|
//#region ../../packages/agent-core/src/skill/builtin/update-config.ts
|
|
@@ -65964,7 +65991,7 @@ async function discoverSkills(options) {
|
|
|
65964
65991
|
const warn = options.onWarning ?? (() => {});
|
|
65965
65992
|
const skip = options.onSkippedByPolicy ?? (() => {});
|
|
65966
65993
|
const byName = /* @__PURE__ */ new Map();
|
|
65967
|
-
async function walkSkillDir(dirPath, root, isTopLevel, depth) {
|
|
65994
|
+
async function walkSkillDir(dirPath, root, isTopLevel, depth, subSkillParentName) {
|
|
65968
65995
|
if (depth > MAX_SKILL_SCAN_DEPTH) return;
|
|
65969
65996
|
let entries;
|
|
65970
65997
|
try {
|
|
@@ -65981,7 +66008,7 @@ async function discoverSkills(options) {
|
|
|
65981
66008
|
if (entry === "node_modules" || entry.startsWith(".")) continue;
|
|
65982
66009
|
if (await isDir(entryPath)) subdirs.push(entry);
|
|
65983
66010
|
}
|
|
65984
|
-
const allowedSubSkillBundles = /* @__PURE__ */ new
|
|
66011
|
+
const allowedSubSkillBundles = /* @__PURE__ */ new Map();
|
|
65985
66012
|
for (const entry of directorySkills) {
|
|
65986
66013
|
const skill = await parseAndRegister({
|
|
65987
66014
|
parse,
|
|
@@ -65991,9 +66018,10 @@ async function discoverSkills(options) {
|
|
|
65991
66018
|
root,
|
|
65992
66019
|
onDiscoveredSkill: options.onDiscoveredSkill,
|
|
65993
66020
|
warn,
|
|
65994
|
-
skip
|
|
66021
|
+
skip,
|
|
66022
|
+
subSkillParentName
|
|
65995
66023
|
});
|
|
65996
|
-
if (skill !== void 0 && hasSubSkillEnabled(skill)) allowedSubSkillBundles.
|
|
66024
|
+
if (skill !== void 0 && hasSubSkillEnabled(skill)) allowedSubSkillBundles.set(entry, skill.name);
|
|
65997
66025
|
}
|
|
65998
66026
|
if (isTopLevel) {
|
|
65999
66027
|
if (root.plugin !== void 0) {
|
|
@@ -66033,7 +66061,8 @@ async function discoverSkills(options) {
|
|
|
66033
66061
|
}
|
|
66034
66062
|
for (const entry of subdirs) {
|
|
66035
66063
|
if (directorySkills.has(entry) && !allowedSubSkillBundles.has(entry)) continue;
|
|
66036
|
-
|
|
66064
|
+
const allowedSubSkillParentName = allowedSubSkillBundles.get(entry);
|
|
66065
|
+
await walkSkillDir(path.join(dirPath, entry), root, false, depth + 1, allowedSubSkillParentName ?? subSkillParentName);
|
|
66037
66066
|
}
|
|
66038
66067
|
}
|
|
66039
66068
|
for (const root of options.roots) await walkSkillDir(root.path, root, true, 0);
|
|
@@ -66097,11 +66126,20 @@ async function pushProvidedRoot(out, root, isDir, realpath) {
|
|
|
66097
66126
|
}
|
|
66098
66127
|
async function parseAndRegister(input) {
|
|
66099
66128
|
try {
|
|
66100
|
-
const
|
|
66129
|
+
const parsed = await input.parse({
|
|
66101
66130
|
skillMdPath: input.skillMdPath,
|
|
66102
66131
|
skillDirName: input.skillDirName,
|
|
66103
66132
|
source: input.root.source
|
|
66104
66133
|
});
|
|
66134
|
+
const subSkillParentName = input.subSkillParentName;
|
|
66135
|
+
const skill = subSkillParentName !== void 0 ? {
|
|
66136
|
+
...parsed,
|
|
66137
|
+
name: qualifySubSkillName(subSkillParentName, parsed.name),
|
|
66138
|
+
metadata: {
|
|
66139
|
+
...parsed.metadata,
|
|
66140
|
+
isSubSkill: true
|
|
66141
|
+
}
|
|
66142
|
+
} : parsed;
|
|
66105
66143
|
const discovered = input.root.plugin === void 0 ? skill : {
|
|
66106
66144
|
...skill,
|
|
66107
66145
|
plugin: input.root.plugin
|
|
@@ -66109,7 +66147,7 @@ async function parseAndRegister(input) {
|
|
|
66109
66147
|
input.onDiscoveredSkill?.(discovered);
|
|
66110
66148
|
const key = normalizeSkillName(discovered.name);
|
|
66111
66149
|
if (!input.byName.has(key)) input.byName.set(key, discovered);
|
|
66112
|
-
return
|
|
66150
|
+
return discovered;
|
|
66113
66151
|
} catch (error) {
|
|
66114
66152
|
if (error instanceof UnsupportedSkillTypeError) input.skip({
|
|
66115
66153
|
path: input.skillMdPath,
|
|
@@ -66121,6 +66159,10 @@ async function parseAndRegister(input) {
|
|
|
66121
66159
|
return;
|
|
66122
66160
|
}
|
|
66123
66161
|
}
|
|
66162
|
+
function qualifySubSkillName(parentName, skillName) {
|
|
66163
|
+
if (skillName === parentName || skillName.startsWith(`${parentName}.`)) return skillName;
|
|
66164
|
+
return `${parentName}.${skillName}`;
|
|
66165
|
+
}
|
|
66124
66166
|
function hasSubSkillEnabled(skill) {
|
|
66125
66167
|
const nested = skill.metadata["metadata"];
|
|
66126
66168
|
const nestedFlag = typeof nested === "object" && nested !== null ? nested["has-sub-skill"] === true || nested["hasSubSkill"] === true : false;
|
|
@@ -66249,7 +66291,7 @@ var SkillRegistry = class {
|
|
|
66249
66291
|
}
|
|
66250
66292
|
getModelSkillListing() {
|
|
66251
66293
|
const lines = ["DISREGARD any earlier skill listings. Current available skills:"];
|
|
66252
|
-
const listing = renderGroupedSkills(this.listInvocableSkills(), formatModelSkill);
|
|
66294
|
+
const listing = renderGroupedSkills(this.listInvocableSkills().filter((skill) => skill.metadata.isSubSkill !== true), formatModelSkill);
|
|
66253
66295
|
if (listing.length > 0) lines.push(listing);
|
|
66254
66296
|
return lines.length === 1 ? "" : lines.join("\n");
|
|
66255
66297
|
}
|
|
@@ -66392,10 +66434,10 @@ var SkillManager = class {
|
|
|
66392
66434
|
}
|
|
66393
66435
|
};
|
|
66394
66436
|
//#endregion
|
|
66395
|
-
//#region ../../packages/agent-core/src/agent/swarm/enter-reminder.md
|
|
66437
|
+
//#region ../../packages/agent-core/src/agent/swarm/enter-reminder.md?raw
|
|
66396
66438
|
var enter_reminder_default = "## Swarm Mode\n\nYou are now in \"agent swarm\" mode. The user may send tasks that require a large number of parallel subagents.\n\n## Workflow\n\nYou do not need to use TodoList to record this workflow.\n\n1. First, you may need to do a small amount of exploratory work before deciding how to divide the task across subagents. You may not need subagents during this exploratory phase.\n\n2. After exploring, if you are convinced no subagent is needed to complete the task, tell the user why and wait for further instructions; otherwise, continue with the appropriate delegation.\n\n3. Once you have enough context, do not handle the main work yourself. Use AgentSwarm with a `prompt_template` containing the `{{item}}` placeholder and an `items` array for the requested or appropriate number of subagents, partitioning the problem so each item gives one subagent a distinct part of the work. Pass `subagent_type` when the whole swarm should use a non-default subagent profile.\n\n## Coordination\n\n- Give each subagent a distinct scope of work.\n- Avoid duplicating work across subagents.\n- Avoid assigning conflicting changes or responsibilities to different subagents.\n- Remember that subagents have your full capabilities. Do not overload their prompts with excessive detail; only describe the necessary background and each subagent's specific task.\n- Unless the user explicitly specifies a lower limit, do not try to conserve the number of agents. AgentSwarm supports up to 128 subagents and queues launches automatically, so decompose work as finely as possible while keeping subagent responsibilities non-conflicting; combine tasks only when they are genuinely inseparable. If the subagents only need to read, inspect, or report back without making changes, their scopes may overlap slightly.\n";
|
|
66397
66439
|
//#endregion
|
|
66398
|
-
//#region ../../packages/agent-core/src/agent/swarm/exit-reminder.md
|
|
66440
|
+
//#region ../../packages/agent-core/src/agent/swarm/exit-reminder.md?raw
|
|
66399
66441
|
var exit_reminder_default = "## Swarm Mode Ended\n\nSwarm Mode has ended. You are no longer required to follow the Swarm Mode workflow.\n\nThe user's next request is likely to be a regular request that does not need AgentSwarm. If the request still benefits from parallel subagents, you may call the AgentSwarm tool, but decide from the new request itself rather than the ended Swarm Mode workflow.\n";
|
|
66400
66442
|
//#endregion
|
|
66401
66443
|
//#region ../../packages/agent-core/src/agent/swarm/index.ts
|
|
@@ -80068,16 +80110,16 @@ function normalizeSourcePath(path) {
|
|
|
80068
80110
|
return normalize(path.replaceAll("\\", "/")).replace(/^\.\//, "");
|
|
80069
80111
|
}
|
|
80070
80112
|
//#endregion
|
|
80071
|
-
//#region ../../packages/agent-core/src/profile/default/agent.yaml
|
|
80113
|
+
//#region ../../packages/agent-core/src/profile/default/agent.yaml?raw
|
|
80072
80114
|
var agent_default$1 = "name: agent\ndescription: Default Kimi Code agent\n\nsystemPromptPath: ./system.md\npromptVars:\n roleAdditional: ''\n\ntools:\n - Read\n - Write\n - Edit\n - Grep\n - Glob\n - Bash\n - TaskList\n - TaskOutput\n - TaskStop\n - CronCreate\n - CronList\n - CronDelete\n - ReadMediaFile\n - TodoList\n - Skill\n - WebSearch\n - Agent\n - AgentSwarm\n - FetchURL\n - AskUserQuestion\n - EnterPlanMode\n - ExitPlanMode\n - CreateGoal\n - GetGoal\n - SetGoalBudget\n - UpdateGoal\n - mcp__*\n\nsubagents:\n coder:\n description: Good at general software engineering tasks.\n explore:\n description: Fast codebase exploration with prompt-enforced read-only behavior.\n plan:\n description: Read-only implementation planning and architecture design.\n";
|
|
80073
80115
|
//#endregion
|
|
80074
|
-
//#region ../../packages/agent-core/src/profile/default/coder.yaml
|
|
80116
|
+
//#region ../../packages/agent-core/src/profile/default/coder.yaml?raw
|
|
80075
80117
|
var coder_default = "extends: agent\nname: coder\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\nwhenToUse: |\n Use this agent for non-trivial software engineering work that may require reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - Write\n - Edit\n - WebSearch\n - FetchURL\n - mcp__*\n";
|
|
80076
80118
|
//#endregion
|
|
80077
|
-
//#region ../../packages/agent-core/src/profile/default/explore.yaml
|
|
80119
|
+
//#region ../../packages/agent-core/src/profile/default/explore.yaml?raw
|
|
80078
80120
|
var explore_default = "extends: agent\nname: explore\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n You are a codebase exploration specialist. Your role is EXCLUSIVELY to search, read, and analyze existing code and resources. You do NOT have access to file editing tools.\n\n Your strengths:\n - Rapidly finding files using glob patterns\n - Searching code and text with powerful regex patterns\n - Reading and analyzing file contents\n - Running read-only shell commands (git log, git diff, ls, find, etc.)\n\n Guidelines:\n - Use Glob for broad file pattern matching. Patterns MUST contain a literal anchor (extension or subdirectory); pure wildcards like `*` or `**/*` are rejected by the tool.\n - Use Grep for searching file contents with regex\n - Use Read when you know the specific file path\n - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find)\n - NEVER use Bash for any file creation or modification commands\n - Adapt your search depth based on the thoroughness level specified by the caller\n - Wherever possible, spawn multiple parallel tool calls for grepping and reading files to maximize speed\n\n If the prompt includes a <git-context> block, use it to orient yourself about the repository state before starting your investigation.\n\n You are meant to be a fast agent. Complete the search request efficiently and report your findings clearly in a structured format.\nwhenToUse: |\n Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. \"src/**/*.yaml\"), search code for keywords (e.g. \"database connection\"), or answer questions about the codebase (e.g. \"how does the auth module work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"thorough\" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. Prefer launching multiple explore agents concurrently when investigating independent questions.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - WebSearch\n - FetchURL\n";
|
|
80079
80121
|
//#endregion
|
|
80080
|
-
//#region ../../packages/agent-core/src/profile/default/init.md
|
|
80122
|
+
//#region ../../packages/agent-core/src/profile/default/init.md?raw
|
|
80081
80123
|
var init_default = "You are a software engineering expert with many years of programming experience. Please explore the current project directory to understand the project's architecture and main details.\n\nTask requirements:\n1. Analyze the project structure and identify key configuration files (such as pyproject.toml, package.json, Cargo.toml, etc.).\n2. Understand the project's technology stack, build process and runtime architecture.\n3. Identify how the code is organized and main module divisions.\n4. Discover project-specific development conventions, testing strategies, and deployment processes.\n\nAfter the exploration, you should do a thorough summary of your findings and overwrite it into `AGENTS.md` file in the project root. You need to refer to what is already in the file when you do so.\n\nFor your information, `AGENTS.md` is a file intended to be read by AI coding agents. Expect the reader of this file know nothing about the project.\n\nYou should compose this file according to the actual project content. Do not make any assumptions or generalizations. Ensure the information is accurate and useful. You must use the natural language that is mainly used in the project's comments and documentation.\n\nPopular sections that people usually write in `AGENTS.md` are:\n\n- Project overview\n- Build and test commands\n- Code style guidelines\n- Testing instructions\n- Security considerations\n";
|
|
80082
80124
|
//#endregion
|
|
80083
80125
|
//#region ../../packages/agent-core/src/profile/default.ts
|
|
@@ -80208,7 +80250,7 @@ function formatPlainObject(record) {
|
|
|
80208
80250
|
return Object.entries(record).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${fieldName(key)}: ${formatValue(value)}`).join("\n");
|
|
80209
80251
|
}
|
|
80210
80252
|
//#endregion
|
|
80211
|
-
//#region ../../packages/agent-core/src/tools/background/task-list.md
|
|
80253
|
+
//#region ../../packages/agent-core/src/tools/background/task-list.md?raw
|
|
80212
80254
|
var task_list_default = "List background tasks and their current status.\n\nUse this tool to discover which background tasks exist and where each one\nstands. It is the entry point for inspecting background work: it returns a\ntask ID, status, command, description, and PID for every task it reports,\nplus the exit code and stop reason for tasks that have already finished.\n\nGuidelines:\n\n- After a context compaction, or whenever you are unsure which background\n tasks are running or what their task IDs are, call this tool to\n re-enumerate them instead of guessing a task ID.\n- Prefer the default `active_only=true`, which lists only non-terminal tasks.\n Pass `active_only=false` only when you specifically need to see tasks that\n have already finished. With `active_only=false` the result may also include\n `lost` tasks — tasks left over from a previous process that can no longer be\n inspected or controlled; treat them as already terminated.\n- `limit` caps how many tasks are returned. It accepts a value between 1 and\n 100 and defaults to 20 when omitted.\n- This tool only lists tasks; it does not return their output. Use it first\n to locate the task ID you need, then call `TaskOutput` with that ID to read\n the task's output and details.\n- This tool is read-only and does not change any state, so it is always safe\n to call, including in plan mode.\n";
|
|
80213
80255
|
//#endregion
|
|
80214
80256
|
//#region ../../packages/agent-core/src/tools/background/task-list.ts
|
|
@@ -80249,7 +80291,7 @@ var TaskListTool = class {
|
|
|
80249
80291
|
}
|
|
80250
80292
|
};
|
|
80251
80293
|
//#endregion
|
|
80252
|
-
//#region ../../packages/agent-core/src/tools/background/task-output.md
|
|
80294
|
+
//#region ../../packages/agent-core/src/tools/background/task-output.md?raw
|
|
80253
80295
|
var task_output_default = "Retrieve output from a running or completed background task.\n\nUse this after `Bash(run_in_background=true)` or `Agent(run_in_background=true)` when you need to inspect progress or explicitly wait for completion.\n\nGuidelines:\n- Prefer relying on automatic completion notifications. Use this tool only when you need task output before the automatic notification arrives.\n- By default this tool is non-blocking and returns a current status/output snapshot.\n- Use block=true only when you intentionally want to wait for completion or timeout.\n- This tool returns structured task metadata, a fixed-size output preview, and an output_path for the full log.\n- For a terminal task, the metadata also explains why it ended: `status: timed_out` when a task was aborted by its deadline, and `stop_reason` when the task was explicitly stopped. `terminal_reason` is a categorical label for the same event — its value is `timed_out` or `stopped` — and is emitted alongside the matching status / `stop_reason` field. A task that ended on its own emits neither `stop_reason` nor `terminal_reason`.\n- The full, never-truncated log is always available at output_path; use the `Read` tool with that path to page through it, whether or not the preview was truncated.\n- This tool works with the generic background task system and should remain the primary read path for future task types, not just bash.\n";
|
|
80254
80296
|
//#endregion
|
|
80255
80297
|
//#region ../../packages/agent-core/src/tools/background/task-output.ts
|
|
@@ -80344,7 +80386,7 @@ var TaskOutputTool = class {
|
|
|
80344
80386
|
}
|
|
80345
80387
|
};
|
|
80346
80388
|
//#endregion
|
|
80347
|
-
//#region ../../packages/agent-core/src/tools/background/task-stop.md
|
|
80389
|
+
//#region ../../packages/agent-core/src/tools/background/task-stop.md?raw
|
|
80348
80390
|
var task_stop_default = "Stop a running background task.\n\nOnly use this when a task must genuinely be cancelled — for a task that is\nfinishing normally, wait for its completion notification or inspect it with\n`TaskOutput` instead of stopping it.\n\nGuidelines:\n- This is a general-purpose stop capability for any background task. It is not\n a bash-specific kill.\n- Stopping a task is destructive: it may leave partial side effects behind.\n Use it with care.\n- If the task has already finished, this tool simply returns its current\n status.\n";
|
|
80349
80391
|
//#endregion
|
|
80350
80392
|
//#region ../../packages/agent-core/src/tools/background/task-stop.ts
|
|
@@ -80418,7 +80460,7 @@ function pad(n) {
|
|
|
80418
80460
|
return String(n).padStart(2, "0");
|
|
80419
80461
|
}
|
|
80420
80462
|
//#endregion
|
|
80421
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-create.md
|
|
80463
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-create.md?raw
|
|
80422
80464
|
var cron_create_default = "Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.\n\nUses standard 5-field cron in the user's local timezone: minute hour day-of-month month day-of-week. `0 9 * * *` means 9am local — no timezone conversion needed.\n\n## One-shot tasks (recurring: false)\n\nFor \"remind me at X\" or \"at <time>, do Y\" requests — fire once then auto-delete.\nPin minute/hour/day-of-month/month to specific values:\n \"remind me at 2:30pm today to check the deploy\" → cron: \"30 14 <today_dom> <today_month> *\", recurring: false\n \"tomorrow morning, run the smoke test\" → cron: \"57 8 <tomorrow_dom> <tomorrow_month> *\", recurring: false\n\n## Recurring jobs (recurring: true, the default)\n\nFor \"every N minutes\" / \"every hour\" / \"weekdays at 9am\" requests:\n \"*/5 * * * *\" (every 5 min), \"0 * * * *\" (hourly), \"0 9 * * 1-5\" (weekdays at 9am local)\n\n## Avoid the :00 and :30 minute marks when the task allows it\n\nEvery user who asks for \"9am\" gets `0 9`, and every user who asks for \"hourly\" gets `0 *` — which means requests from across the planet land on the API at the same instant. When the user's request is approximate, pick a minute that is NOT 0 or 30:\n \"every morning around 9\" → \"57 8 * * *\" or \"3 9 * * *\" (not \"0 9 * * *\")\n \"hourly\" → \"7 * * * *\" (not \"0 * * * *\")\n \"in an hour or so, remind me to...\" → pick whatever minute you land on, don't round\n\nOnly use minute 0 or 30 when the user names that exact time and clearly means it (\"at 9:00 sharp\", \"at half past\", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.\n\n## Coalesce semantics\n\nIf the scheduler slept past multiple ideal fire times (laptop closed, long-running turn, etc.), only **one** fire is delivered when it wakes up. The origin carries `coalescedCount` showing how many ideal fires were collapsed into this single delivery. You should treat `coalescedCount > 1` as \"I missed some checks; only the latest state matters\" rather than running the prompt that many times.\n\n## Cron-fire envelope\n\nWhen a cron task fires, the prompt you scheduled is re-injected wrapped in an XML envelope that exposes the fire context:\n\n```\n<cron-fire jobId=\"...\" cron=\"...\" recurring=\"true|false\" coalescedCount=\"N\" stale=\"true|false\">\n<prompt>\nyour original prompt text, verbatim\n</prompt>\n</cron-fire>\n```\n\nThe envelope is parseable. Use `coalescedCount > 1` to know multiple ideal fires were collapsed into a single delivery (treat as \"only the latest state matters\"), and `stale=\"true\"` as a cue that the task is past its 7-day threshold.\n\n## 7-day stale behavior\n\nRecurring tasks that have been alive for more than 7 days fire one\nfinal time with `stale: true` on the envelope, and the system then\nauto-deletes the task. The flag is the model's notice that this is\nthe last delivery. If the schedule is still wanted, call `CronCreate`\nagain with the same `cron` and `prompt` — that resets `createdAt` and\nstarts a fresh 7-day window. One-shot tasks are never marked stale.\n\nBench / acceptance runs can set `KIMI_CRON_NO_STALE=1` to disable the\njudgment entirely.\n\n## Jitter behavior\n\nAnti-herd jitter is applied deterministically per task id:\n - Recurring: ideal fire time is shifted **forward** by an offset ≤ min(10% of the cron period, 15 minutes). A `*/5 * * * *` task can drift up to 30s; a `0 9 * * *` task can drift up to 15 minutes.\n - One-shot: only when the ideal fire lands on `:00` or `:30` of the hour, the fire is pulled **earlier** by ≤ 90 seconds. Other minutes pass through unchanged.\n\nBench / acceptance tests can set `KIMI_CRON_NO_JITTER=1` to disable jitter entirely.\n\n## One-shot vs recurring — when to pick which\n\nUse `recurring: false` for \"remind me at X\" style requests, single deadlines, \"in N minutes do Y\", and any task that should not repeat. Use `recurring: true` for periodic polling (CI status, build watchers, scheduled reports), workday rituals, and anything the user explicitly described as recurring.\n\n## Session lifetime\n\nCron tasks live in the current kimi CLI session. When you exit, they\nare persisted under the session homedir; the next `kimi resume` of the\nsame session reloads them and the scheduler resumes from each task's\n`createdAt`. Fire times that fell during the offline window are\ncollapsed into a single delivery via `coalescedCount` (and recurring\ntasks past their 7-day window arrive with `stale: true` as their final\ndelivery).\n\nTasks do **not** carry over into a brand-new session — they are scoped\nto the resumed session id, not to the working directory.\n\n## Returned fields\n\n`id` (8-hex), `humanSchedule` (English summary), `recurring`,\n`nextFireAt` (local ISO timestamp with numeric offset, or null). `id` is needed by `CronDelete`.\n\n## Tell the user how to cancel or modify\n\nAfter successfully creating a task, proactively tell the user how they can cancel or modify it later. Users have no direct `/cron` command or self-service UI to manage reminders themselves; they must ask the model to make changes (e.g. \"cancel my 9am reminder\" or \"change my daily check to 10am\"). Include the task `id` in your message so the user can reference it.\n";
|
|
80423
80465
|
/**
|
|
80424
80466
|
* Hard ceiling on `prompt` byte length (UTF-8). The zod `.max(...)`
|
|
@@ -80540,7 +80582,7 @@ function formatOutput(o) {
|
|
|
80540
80582
|
].join("\n");
|
|
80541
80583
|
}
|
|
80542
80584
|
//#endregion
|
|
80543
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-delete.md
|
|
80585
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-delete.md?raw
|
|
80544
80586
|
var cron_delete_default = "Cancel a scheduled cron job by id.\n\nUse this tool to remove a cron task previously scheduled with\n`CronCreate`. The `id` is the 8-hex value returned by `CronCreate`, or\nshown in the `id:` column of `CronList` — quote it verbatim, no\nprefix.\n\nBehaviour by task kind:\n\n- **Recurring task** (`recurring: true`): stops all future fires\n immediately. The scheduler picks up the deletion on its next tick.\n- **One-shot task** (`recurring: false`): cancels the pending fire if\n it has not happened yet. One-shots that have already fired\n auto-delete themselves, so calling `CronDelete` on a fired one-shot\n returns \"no cron job with id ...\".\n\nNot-found is reported as an error (not a silent no-op) so you can\ncorrect yourself — typically by calling `CronList` to see which ids\nare actually live, rather than re-trying with the same stale id.\n\nRefresh pattern (use when you want a stale recurring schedule to\ncontinue):\n\nStale recurring tasks are auto-deleted by the system after their final\nfire — there is nothing for `CronDelete` to remove at that point. To\nkeep the schedule running, just call `CronCreate` with the same `cron`\nand `prompt`. Use `CronList`'s `prompt` field to recall the original\ntext after a context compaction.\n\n`CronDelete` remains the right call when you want to cancel a task\nthat is still live (recurring not yet stale, or a one-shot still\npending).\n\nGuidelines:\n\n- Users have no direct `/cron` command or self-service UI to delete\n tasks themselves; they must ask the model to cancel a reminder.\n When deleting on behalf of a user, confirm the action and report\n the result plainly.\n- Cron deletion is irreversible — there is no undo. If you delete the\n wrong task, you must re-create it with `CronCreate`.\n- If the model is unsure which id is current (e.g. after a context\n compaction), call `CronList` first rather than guessing.\n";
|
|
80545
80587
|
//#endregion
|
|
80546
80588
|
//#region ../../packages/agent-core/src/tools/cron/cron-delete.ts
|
|
@@ -80621,7 +80663,7 @@ var CronDeleteTool = class {
|
|
|
80621
80663
|
}
|
|
80622
80664
|
};
|
|
80623
80665
|
//#endregion
|
|
80624
|
-
//#region ../../packages/agent-core/src/tools/cron/cron-list.md
|
|
80666
|
+
//#region ../../packages/agent-core/src/tools/cron/cron-list.md?raw
|
|
80625
80667
|
var cron_list_default = "List all cron jobs currently scheduled in this session.\n\nUse this tool to see every pending cron task — both recurring jobs and\none-shot reminders — that you (or the user) have scheduled with\n`CronCreate`. The output is the entry point for inspecting scheduled\nwork: it returns a stable id, the original cron expression, a human\nrendering, the next post-jitter fire time, the recurring flag, the\ntask's age in days, and a stale indicator.\n\nEach record carries:\n\n- `id` — the 8-hex task id. Pass this to `CronDelete` to remove the\n task, or quote it in user-facing messages when asking for\n confirmation.\n- `cron` — the verbatim 5-field cron expression as scheduled.\n- `humanSchedule` — plain-English rendering (e.g. `every 5 minutes`).\n- `prompt` — the scheduled prompt text, JSON-encoded so embedded\n newlines stay on one line. Truncated to 200 UTF-8 bytes with\n `…(truncated)` if longer. Use this to recall what a task is for\n after a context compaction, and as the source for the\n `CronCreate` refresh ritual.\n- `nextFireAt` — local ISO timestamp with an explicit numeric offset\n for the next fire **after jitter has been applied**. The actual fire\n may land slightly before or after a round `:00` / `:30` minute mark\n due to herd-avoidance jitter; this is the value the scheduler will\n compare against, so it reflects what will really happen. `null` if\n the expression has no fire in the next 5 years (should not happen\n for tasks created through `CronCreate`, which validates).\n- `recurring` — `true` for cadenced jobs, `false` for one-shots.\n- `ageDays` — `(now - createdAt) / day`, two decimal places. Useful\n when deciding whether a long-running cron is still relevant.\n- `stale` — `true` when a recurring task is older than 7 days. The\n system **auto-deletes the task after this fire** to bound session\n lifetime; the `stale: true` flag is the model's notice that this is\n the final delivery. To resume the same schedule, call `CronCreate`\n again with the original `cron` and `prompt` (the `prompt` row above\n carries it for exactly this purpose). One-shots are never marked\n stale — they fire at most once by construction.\n `KIMI_CRON_NO_STALE=1` disables the judgment entirely (bench /\n acceptance runs).\n\nGuidelines:\n\n- This tool is read-only and never mutates state, so it is always\n safe to call (including in plan mode).\n- Users cannot directly manage cron tasks themselves; if they want to\n cancel or modify a schedule, route the request through the model\n (i.e. call `CronDelete` or `CronCreate` on their behalf).\n- The empty case returns `cron_jobs: 0\\nNo cron jobs scheduled.`. Cron\n tasks survive a `kimi resume` of the same session but do not bleed\n into new sessions.\n- After a context compaction, or whenever you are unsure which cron\n jobs are live, call this tool to re-enumerate them rather than\n guessing ids from earlier in the conversation.\n- Records are separated by a line containing just `---`, in the\n insertion order they were scheduled.\n";
|
|
80626
80668
|
//#endregion
|
|
80627
80669
|
//#region ../../packages/agent-core/src/tools/cron/cron-list.ts
|
|
@@ -81277,7 +81319,7 @@ var SubagentBatch = class {
|
|
|
81277
81319
|
}
|
|
81278
81320
|
};
|
|
81279
81321
|
//#endregion
|
|
81280
|
-
//#region ../../packages/agent-core/src/session/summary-continuation.md
|
|
81322
|
+
//#region ../../packages/agent-core/src/session/summary-continuation.md?raw
|
|
81281
81323
|
var summary_continuation_default = "Your previous response was too brief. Please provide a more comprehensive summary that includes:\n\n1. Specific technical details and implementations\n2. Detailed findings and analysis\n3. All important information that the parent agent should know";
|
|
81282
81324
|
//#endregion
|
|
81283
81325
|
//#region ../../packages/agent-core/src/session/subagent-host.ts
|
|
@@ -81611,13 +81653,13 @@ function shouldSuppressQueuedAttemptFailureEvent(options, error) {
|
|
|
81611
81653
|
return isAbortError$3(error) || options.signal.aborted;
|
|
81612
81654
|
}
|
|
81613
81655
|
//#endregion
|
|
81614
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-disabled.md
|
|
81656
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-disabled.md?raw
|
|
81615
81657
|
var agent_background_disabled_default = "Background agent execution is disabled for this agent. Do not set `run_in_background=true`.";
|
|
81616
81658
|
//#endregion
|
|
81617
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-enabled.md
|
|
81659
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-enabled.md?raw
|
|
81618
81660
|
var agent_background_enabled_default = "When `run_in_background=true`, the subagent runs detached from this turn. The completion arrives in a later turn as a synthetic user-role message containing its result — you do not need to poll, sleep, or check on its progress. Continue with other work or respond to the user. Never fabricate or predict what the result will say.\n";
|
|
81619
81661
|
//#endregion
|
|
81620
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent.md
|
|
81662
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent.md?raw
|
|
81621
81663
|
var agent_default = "Launch a subagent to handle a task. The subagent runs as a same-process loop instance with its own context and wire file.\n\nWriting the prompt:\n- The subagent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.\n- Lookups (read this file, run that test): put the exact path or command in the prompt. The subagent should not have to search for things you already know.\n- Investigations (figure out X, find why Y): give the question, not prescribed steps — fixed steps become dead weight when the premise is wrong.\n- Do not delegate understanding. If the task hinges on a file path or line number, find it yourself first and write it into the prompt.\n\nUsage notes:\n- When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance — the resumed agent keeps its prior context.\n- A subagent's result is only visible to you, not to the user. When the user needs to see what a subagent produced, summarize the relevant parts yourself in your own reply.\n- Subagents use a fixed 30-minute timeout. If one times out, resume the same agent instead of starting over.\n\nWhen NOT to use Agent: skip delegation for trivial work you can do directly — reading a file whose path you already know, searching a small known set of files, or any task that takes only a step or two. Delegation has a context-handoff cost; it pays off only when the task is substantial enough to outweigh it.\n\nOnce a subagent is running, leave that scope to it: do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually. Both undo the context savings the delegation was meant to buy.\n";
|
|
81622
81664
|
//#endregion
|
|
81623
81665
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent.ts
|
|
@@ -81838,7 +81880,7 @@ function buildSubagentDescriptions(subagents) {
|
|
|
81838
81880
|
}).join("\n");
|
|
81839
81881
|
}
|
|
81840
81882
|
//#endregion
|
|
81841
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.md
|
|
81883
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.md?raw
|
|
81842
81884
|
var agent_swarm_default = "Launch multiple subagents from one prompt template, existing agent resumes, or both.\n\nUse AgentSwarm when many subagents should run the same kind of task over different inputs. The placeholder is exactly `{{item}}`. For example, with `prompt_template` set to `Review {{item}} for likely regressions.` and `items` set to `[\"src/a.ts\", \"src/b.ts\"]`, AgentSwarm launches two new subagents with those two concrete prompts.\n\nUse `resume_agent_ids` to continue subagents that already exist from earlier work, such as ones that failed or timed out: map each agent id to the prompt for that resumed subagent (usually `continue` if no extra information is needed). You may combine `resume_agent_ids` with `items` in the same call to resume existing subagents and launch new ones. Do not duplicate resumed work in `items`.\n\nUse enough subagents to keep the work focused and parallel. AgentSwarm supports up to 128 subagents, and launches are queued automatically, so it is safe to split large tasks into many clear, independent items.\n\nIf `AgentSwarm` is called, that call must be the only tool call in the response.\n";
|
|
81843
81885
|
//#endregion
|
|
81844
81886
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.ts
|
|
@@ -81999,7 +82041,7 @@ function escapeXmlAttribute(value) {
|
|
|
81999
82041
|
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
82000
82042
|
}
|
|
82001
82043
|
//#endregion
|
|
82002
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md
|
|
82044
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md?raw
|
|
82003
82045
|
var ask_user_default = "Use this tool when you need to ask the user questions with structured options during execution. This allows you to:\n1. Collect user preferences or requirements before proceeding\n2. Resolve ambiguous or underspecified instructions\n3. Let the user decide between implementation approaches as you work\n4. Present concrete options when multiple valid directions exist\n\n**When NOT to use:**\n- When you can infer the answer from context — be decisive and proceed\n- Trivial decisions that don't materially affect the outcome\n\nOverusing this tool interrupts the user's flow. Only use it when the user's input genuinely changes your next action.\n\n**Usage notes:**\n- Users always have an \"Other\" option for custom input — don't create one yourself\n- Use multi_select to allow multiple answers to be selected for a question\n- Keep option labels concise (1-5 words), use descriptions for trade-offs and details\n- Each question should have 2-4 meaningful, distinct options\n- You can ask 1-4 questions at a time; group related questions to minimize interruptions\n- If you recommend a specific option, list it first and append \"(Recommended)\" to its label\n";
|
|
82004
82046
|
//#endregion
|
|
82005
82047
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.ts
|
|
@@ -82161,7 +82203,7 @@ function isQuestionResponse(result) {
|
|
|
82161
82203
|
return typeof answers === "object" && answers !== null && !Array.isArray(answers);
|
|
82162
82204
|
}
|
|
82163
82205
|
//#endregion
|
|
82164
|
-
//#region ../../packages/agent-core/src/tools/builtin/collaboration/skill-tool.md
|
|
82206
|
+
//#region ../../packages/agent-core/src/tools/builtin/collaboration/skill-tool.md?raw
|
|
82165
82207
|
var skill_tool_default = "Invoke a registered skill from the current skill listing. BLOCKING REQUIREMENT: when a skill from the listing matches the user's request, you MUST call this tool (not free-form text). Do NOT call the same skill repeatedly inside one turn — recursive depth is capped at {{ MAX_SKILL_QUERY_DEPTH }}.";
|
|
82166
82208
|
var NestedSkillTooDeepError = class extends Error {
|
|
82167
82209
|
skillName;
|
|
@@ -82289,7 +82331,7 @@ function makeCarriageReturnsVisible(text) {
|
|
|
82289
82331
|
return text.replaceAll("\r", "\\r");
|
|
82290
82332
|
}
|
|
82291
82333
|
//#endregion
|
|
82292
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/edit.md
|
|
82334
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/edit.md?raw
|
|
82293
82335
|
var edit_default = "Perform exact replacements in existing files.\n\n- Edit is mandatory for every incremental change, especially small edits. DO NOT use Write or Bash `sed`.\n- Read the target file before every Edit. DO NOT call Edit from memory, stale context, or a guessed `old_string`.\n- Take `old_string` and `new_string` from the Read output view.\n- Drop the line-number prefix and tab; match only file content.\n- `old_string` must be unique unless `replace_all` is set.\n- If `old_string` is ambiguous, add surrounding context. Use `replace_all` only when every occurrence should change.\n- Multiple Edit calls may run in one response only when they do not target the same file.\n- DO NOT issue consecutive Edit calls on the same file. A previous Edit can invalidate a later Edit's `old_string`, causing `old_string not found`. Read the file again before the next Edit.\n- A write lock serializes same-file edits in response order, but serialization does not make stale `old_string` valid.\n- For pure CRLF files, Read shows LF; use LF in `old_string` and `new_string`, and Edit writes CRLF back.\n- For mixed endings or lone carriage returns, Read shows carriage returns as \\r; include actual \\r escapes in those positions.\n";
|
|
82294
82336
|
//#endregion
|
|
82295
82337
|
//#region ../../packages/agent-core/src/tools/builtin/file/edit.ts
|
|
@@ -82392,7 +82434,7 @@ var EditTool = class {
|
|
|
82392
82434
|
}
|
|
82393
82435
|
};
|
|
82394
82436
|
//#endregion
|
|
82395
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/glob.md
|
|
82437
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/glob.md?raw
|
|
82396
82438
|
var glob_default = "Find files (and optionally directories) by glob pattern, sorted by modification time (most recent first).\n\nGood patterns:\n- `*.ts` — files in the current directory matching an extension\n- `src/**/*.ts` — recursive walk with a subdirectory anchor and extension\n- `**/*.py` — recursive walk from the search root for an extension\n- `*.{ts,tsx}` — brace expansion is supported; expanded into `*.ts` and `*.tsx` before walking\n- `{src,test}/**/*.ts` — cartesian brace expansion is supported too\n\nResults are capped at the first 100 matching paths (walk order, not global modification-time order). If a search would return more, a truncation marker is appended with the count of matches seen so far. Refine the pattern (extension, subdirectory) when 100 is not enough, or call again with a narrower anchor.\n\nLarge-directory caveat — avoid recursing into dependency / build output even with an anchor:\n- `node_modules/**/*.js`, `.venv/**/*.py`, `__pycache__/**`, `target/**` all match technically but\n typically produce thousands of results that truncate at the match cap and waste the caller context.\n Prefer specific subpaths like `node_modules/react/src/**/*.js`.\n";
|
|
82397
82439
|
//#endregion
|
|
82398
82440
|
//#region ../../packages/agent-core/src/tools/builtin/file/glob.ts
|
|
@@ -87119,7 +87161,7 @@ var ToolResultBuilder = class {
|
|
|
87119
87161
|
}
|
|
87120
87162
|
};
|
|
87121
87163
|
//#endregion
|
|
87122
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/grep.md
|
|
87164
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/grep.md?raw
|
|
87123
87165
|
var grep_default = "Search file contents using regular expressions (powered by ripgrep).\n\nUse Grep when the task is to find unknown content or unknown file locations. Do not use shell `grep` or `rg` directly; this tool applies workspace path policy, output limits, and sensitive-file filtering.\nALWAYS use Grep tool instead of running `grep` or `rg` from a shell — direct shell calls bypass workspace policy, output limits, and sensitive-file filtering.\nIf you already know a concrete file path and need to inspect its contents, use Read directly instead.\n\nWrite patterns in ripgrep regex syntax, which differs from POSIX `grep` syntax. For example, braces are special, so escape them as `\\{` to match a literal `{`.\n\nHidden files (dotfiles such as `.gitlab-ci.yml` or `.eslintrc.json`) are searched by default. To also search files excluded by `.gitignore` (such as `node_modules` or build outputs), set `include_ignored` to `true`. Sensitive files (such as `.env`) are always skipped for safety, even when `include_ignored` is `true`.\n";
|
|
87124
87166
|
//#endregion
|
|
87125
87167
|
//#region ../../packages/agent-core/src/tools/builtin/file/grep.ts
|
|
@@ -88107,7 +88149,7 @@ function detectFileType(path, header) {
|
|
|
88107
88149
|
};
|
|
88108
88150
|
}
|
|
88109
88151
|
//#endregion
|
|
88110
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/read.md
|
|
88152
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/read.md?raw
|
|
88111
88153
|
var read_default = "Read a text file from the local filesystem.\n\nIf the user provides a concrete file path to a text file, call Read directly. Do not `Glob`, `ls`, or otherwise pre-check known text file paths; missing or invalid file paths return errors you can handle. Do not use Read for directories; use `ls` via Bash for a known directory, or Glob when you need files/directories matching a pattern. Use `Grep` only when the task is to search for unknown content or locations.\n\nWhen you need several files, prefer to read them in parallel: emit multiple `Read` calls in a single response instead of reading one file per turn.\n\n- Relative paths resolve against the working directory; a path outside the working directory must be absolute.\n- Returns up to {{ MAX_LINES }} lines or {{ MAX_BYTES_KB }} KB per call, whichever comes first; lines longer than {{ MAX_LINE_LENGTH }} chars are truncated mid-line.\n- Page larger files with `line_offset` (1-based start line) and `n_lines`. Omit `n_lines` to read up to the {{ MAX_LINES }}-line cap.\n- Sensitive files (`.env` files, credential stores, SSH keys, and similar secrets) are refused to protect secrets; do not attempt to read them.\n- Only UTF-8 text files can be read. Non-UTF-8 encodings, binary files, and files containing NUL bytes are refused; use `ReadMediaFile` for images or video, and Bash or an MCP tool for other binary formats.\n- Negative line_offset reads from the end of the file (for example, -100 reads the last 100 lines); the absolute value cannot exceed {{ MAX_LINES }}.\n- Output format: `<line-number>\\t<content>` per line.\n- A `<system>...</system>` status block is appended after the file content; it summarizes how much was read (line and byte counts, truncation, line-ending notes) and is not part of the file itself.\n- Pure CRLF files are displayed with LF line endings; `Edit` matches this output and preserves CRLF when writing back.\n- Mixed or lone carriage-return line endings are shown as `\\r` and require exact `Edit.old_string` escapes.\n- After a successful `Edit`/`Write`, do not re-read solely to prove the write landed. When the task depends on an exact file, API, or output shape, inspect the final external contract before finishing.\n";
|
|
88112
88154
|
//#endregion
|
|
88113
88155
|
//#region ../../packages/agent-core/src/tools/builtin/file/read.ts
|
|
@@ -88414,7 +88456,7 @@ var ReadTool = class {
|
|
|
88414
88456
|
}
|
|
88415
88457
|
};
|
|
88416
88458
|
//#endregion
|
|
88417
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/read-media.md
|
|
88459
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/read-media.md?raw
|
|
88418
88460
|
var read_media_default = "Read media content from a file.\n\n**Tips:**\n- Make sure you follow the description of each tool parameter.\n- A `<system>` tag is given before the file content; it summarizes the mime type, byte size and, for images, the original pixel dimensions. When outputting coordinates, give relative coordinates first and compute absolute coordinates from the original image size. After generating or editing media via commands or scripts, read the result back before continuing.\n- The system will notify you when there is anything wrong when reading the file.\n- This tool is a tool that you typically want to use in parallel. Always read multiple files in one response when possible.\n- This tool can only read image or video files. To read text files, use the Read tool. To list directories, use `ls` via Bash for a known directory, or Glob for pattern search.\n- If the file doesn't exist or path is invalid, an error will be returned.\n- The maximum size that can be read is {{ MAX_MEDIA_MEGABYTES }}MB. An error will be returned if the file is larger than this limit.\n- The media content will be returned in a form that you can directly view and understand.\n\n**Capabilities**";
|
|
88419
88461
|
//#endregion
|
|
88420
88462
|
//#region ../../packages/agent-core/src/tools/builtin/file/read-media.ts
|
|
@@ -88576,7 +88618,7 @@ var ReadMediaFileTool = class {
|
|
|
88576
88618
|
}
|
|
88577
88619
|
};
|
|
88578
88620
|
//#endregion
|
|
88579
|
-
//#region ../../packages/agent-core/src/tools/builtin/file/write.md
|
|
88621
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/write.md?raw
|
|
88580
88622
|
var write_default = "Create, append to, or replace a file entirely.\n\n- The parent directory must already exist.\n- Mode defaults to overwrite; append adds content at EOF without adding a newline.\n- Write is NOT ALLOWED for incremental changes to existing files, including trivial, one-line, quick, or cosmetic edits. Use Edit instead.\n- Use Write only when the file does not exist, you intend a complete replacement, or the new contents have little continuity with the old contents.\n- Read before overwriting an existing file.\n- Write ignores the Read/Edit line-number view. NEVER include line prefixes.\n- Write outputs content literally, including supplied line endings: \\n stays LF, \\r\\n stays CRLF.\n- For new content too large for one call, overwrite the first chunk, then append subsequent chunks. Never chunk Write to modify an existing file.\n";
|
|
88581
88623
|
//#endregion
|
|
88582
88624
|
//#region ../../packages/agent-core/src/tools/builtin/file/write.ts
|
|
@@ -88672,7 +88714,7 @@ var WriteTool = class {
|
|
|
88672
88714
|
}
|
|
88673
88715
|
};
|
|
88674
88716
|
//#endregion
|
|
88675
|
-
//#region ../../packages/agent-core/src/tools/builtin/goal/create-goal.md
|
|
88717
|
+
//#region ../../packages/agent-core/src/tools/builtin/goal/create-goal.md?raw
|
|
88676
88718
|
var create_goal_default = "Create a durable, structured goal that the runtime will pursue across multiple turns.\n\nCall `CreateGoal` only when:\n\n- the user explicitly asks you to start a goal or work autonomously toward an outcome, or\n- a host goal-intake prompt asks you to create one.\n\nDo NOT create a goal for greetings, ordinary questions, or vague requests that lack a\nverifiable completion condition. A goal needs a checkable end state.\n\nWhen the request is vague, ask the user for the missing completion criterion before creating\nthe goal. If the user clearly insists after you warn them that the wording is vague or risky,\nrespect that and create the goal.\n\nInclude a `completionCriterion` when the user provides one, or when it can be stated without\ninventing new requirements. Keep `objective` concise; reference long task descriptions by file\npath rather than pasting them.\n\nUse `replace: true` only when the user explicitly wants to abandon the current goal and start a\nnew one.\n";
|
|
88677
88719
|
//#endregion
|
|
88678
88720
|
//#region ../../packages/agent-core/src/tools/builtin/goal/create-goal.ts
|
|
@@ -88706,7 +88748,7 @@ var CreateGoalTool = class {
|
|
|
88706
88748
|
}
|
|
88707
88749
|
};
|
|
88708
88750
|
//#endregion
|
|
88709
|
-
//#region ../../packages/agent-core/src/tools/builtin/goal/get-goal.md
|
|
88751
|
+
//#region ../../packages/agent-core/src/tools/builtin/goal/get-goal.md?raw
|
|
88710
88752
|
var get_goal_default = "Read the current goal: its objective, completion criterion, status, budgets (turns, tokens,\ntime, and how much remains), the latest self-report, and the latest evaluator verdict.\n\nUse `GetGoal` before deciding whether to continue working, report completion, report a blocker,\nor respect a pause. It returns `{ \"goal\": null }` when there is no current goal.\n";
|
|
88711
88753
|
//#endregion
|
|
88712
88754
|
//#region ../../packages/agent-core/src/tools/builtin/goal/get-goal.ts
|
|
@@ -88732,7 +88774,7 @@ var GetGoalTool = class {
|
|
|
88732
88774
|
}
|
|
88733
88775
|
};
|
|
88734
88776
|
//#endregion
|
|
88735
|
-
//#region ../../packages/agent-core/src/tools/builtin/goal/set-goal-budget.md
|
|
88777
|
+
//#region ../../packages/agent-core/src/tools/builtin/goal/set-goal-budget.md?raw
|
|
88736
88778
|
var set_goal_budget_default = "Set a hard budget limit for the current goal.\n\nUse this only when the user clearly gives a runtime limit, such as:\n\n- \"stop after 20 turns\"\n- \"use no more than 500k tokens\"\n- \"finish within 30 minutes\"\n\nDo not invent limits. Do not call this for vague wording such as \"spend some time\" or\n\"try to be quick\".\n\nIf the user gives a compound time, convert it to one supported unit before calling this tool.\nFor example, \"2 hours and 3 minutes\" can be set as `value: 123, unit: \"minutes\"`.\n\nIf the requested budget is not reasonable, do not set it. Tell the user that the requested\nbudget is not reasonable. Examples include a time budget that is too short to act on, such as\n1 millisecond, or too long for an interactive goal run, such as 1 year.\n\nSupported units:\n\n- `turns`\n- `tokens`\n- `milliseconds`\n- `seconds`\n- `minutes`\n- `hours`\n";
|
|
88737
88779
|
//#endregion
|
|
88738
88780
|
//#region ../../packages/agent-core/src/tools/builtin/goal/set-goal-budget.ts
|
|
@@ -89920,7 +89962,7 @@ function formatTokens$3(tokens) {
|
|
|
89920
89962
|
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
89921
89963
|
}
|
|
89922
89964
|
//#endregion
|
|
89923
|
-
//#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.md
|
|
89965
|
+
//#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.md?raw
|
|
89924
89966
|
var update_goal_default = "Set the status of the current goal. This is how you resume, end, or yield an autonomous goal.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is satisfied and any stated validation has passed. The goal ends and a completion summary is recorded.\n- `blocked` — an external condition or required user input prevents progress, or the objective cannot be completed as stated. The goal stops but can be resumed later.\n- `paused` — set the goal aside for now (e.g. to hand control back to the user). It can be resumed later.\n\nIf the goal is active and you do not call this, the goal keeps running: after your turn ends you will be prompted to continue. Call `complete` only when all required work is done, any stated validation has passed, and there is no useful next action. Do not call `complete` after only producing a plan, summary, first pass, or partial result. If you call `blocked`, you will be prompted to explain the blocker in your next message. This tool only records the status.\n";
|
|
89925
89967
|
//#endregion
|
|
89926
89968
|
//#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.ts
|
|
@@ -89981,7 +90023,7 @@ var UpdateGoalTool = class {
|
|
|
89981
90023
|
}
|
|
89982
90024
|
};
|
|
89983
90025
|
//#endregion
|
|
89984
|
-
//#region ../../packages/agent-core/src/tools/builtin/planning/enter-plan-mode.md
|
|
90026
|
+
//#region ../../packages/agent-core/src/tools/builtin/planning/enter-plan-mode.md?raw
|
|
89985
90027
|
var enter_plan_mode_default = "Use this tool proactively when you're about to start a non-trivial implementation task.\nGetting user sign-off on your approach via ExitPlanMode before writing code prevents wasted effort.\n\nUse it when ANY of these conditions apply:\n\n1. New Feature Implementation - e.g. \"Add a caching layer to the API\"\n2. Multiple Valid Approaches - e.g. \"Optimize database queries\" (indexing vs rewrite vs caching)\n3. Code Modifications - e.g. \"Refactor auth module to support OAuth\"\n4. Architectural Decisions - e.g. \"Add WebSocket support\"\n5. Multi-File Changes - involves more than 2-3 files\n6. Unclear Requirements - need exploration to understand scope\n7. User Preferences Matter - if user input would materially change the implementation approach, use EnterPlanMode to structure the decision\n\nPermission mode notes:\n- EnterPlanMode enters plan mode automatically without an approval prompt in all permission modes.\n- In yolo and manual modes, ExitPlanMode still presents the plan to the user for approval.\n- In auto permission mode, do not use AskUserQuestion; make the best decision from available context.\n- In auto permission mode, ExitPlanMode exits plan mode without asking the user.\n- Use EnterPlanMode only when planning itself adds value.\n\nWhen NOT to use:\n- Single-line or few-line fixes (typos, obvious bugs, small tweaks)\n- User gave very specific, detailed instructions\n- Pure research/exploration tasks\n\n## What Happens in Plan Mode\nIn plan mode, you will:\n1. Identify 2-3 key questions about the codebase that are critical to your plan. If you are not confident about the codebase structure or relevant code paths, use `Agent(subagent_type=\"explore\")` to investigate these questions first - this is strongly recommended for non-trivial tasks.\n2. Explore the codebase using Glob, Grep, Read, and other read-only tools for any remaining quick lookups. Use Bash only when needed; Bash follows the normal permission mode and rules.\n3. Design an implementation approach based on your findings\n4. Write your plan to the current plan file with Write or Edit\n5. Present your plan to the user via ExitPlanMode for approval\n";
|
|
89986
90028
|
//#endregion
|
|
89987
90029
|
//#region ../../packages/agent-core/src/tools/builtin/planning/enter-plan-mode.ts
|
|
@@ -90043,7 +90085,7 @@ function enteredPlanModeMessage(planPath) {
|
|
|
90043
90085
|
].join("\n");
|
|
90044
90086
|
}
|
|
90045
90087
|
//#endregion
|
|
90046
|
-
//#region ../../packages/agent-core/src/tools/builtin/planning/exit-plan-mode.md
|
|
90088
|
+
//#region ../../packages/agent-core/src/tools/builtin/planning/exit-plan-mode.md?raw
|
|
90047
90089
|
var exit_plan_mode_default = "Use this tool when you are in plan mode and have finished writing your plan to the plan file and are ready for user approval.\n\n## How This Tool Works\n- You should have already written your plan to the plan file specified in the plan mode reminder.\n- This tool does NOT take the plan content as a parameter - it reads the plan from the file you wrote.\n- The user will see the contents of your plan file when they review it. In auto permission mode, the tool reads the file and exits plan mode without asking the user.\n\n## When to Use\nOnly use this tool for tasks that require planning implementation steps. For research tasks (searching files, reading code, understanding the codebase), do NOT use this tool.\n\n## Multiple Approaches\nIf your plan contains multiple alternative approaches:\n- Pass them via the `options` parameter so the user can choose which approach to execute.\n- Each option should have a concise label and a brief description of trade-offs.\n- If you recommend one option, append \"(Recommended)\" to its label.\n- In yolo and manual modes, the user will see all options alongside Reject and Revise choices.\n- Provide up to 3 options; the host adds the standard rejection and revision controls. When the plan offers a real choice, 2-3 distinct approaches work best.\n- Passing a single option is allowed and is equivalent to a plain plan approval (no approach choice is surfaced to the user).\n- Do NOT use \"Reject\", \"Reject and Exit\", \"Revise\", or \"Approve\" as option labels - these are reserved by the system.\n\n## Before Using\n- In auto permission mode, do NOT use AskUserQuestion; make the best decision from available context.\n- In auto permission mode, this tool exits plan mode without asking the user.\n- In yolo and manual modes, this tool still presents the plan to the user for approval.\n- If auto permission mode is not active and you have unresolved questions, use AskUserQuestion first.\n- If auto permission mode is not active and you have multiple approaches and haven't narrowed down yet, consider using AskUserQuestion first to let the user choose, then write a plan for the chosen approach only.\n- Once your plan is finalized, use THIS tool to request approval.\n- Do NOT use AskUserQuestion to ask \"Is this plan OK?\" or \"Should I proceed?\" - that is exactly what ExitPlanMode does.\n- If rejected, revise based on feedback and call ExitPlanMode again.\n";
|
|
90048
90090
|
//#endregion
|
|
90049
90091
|
//#region ../../packages/agent-core/src/tools/builtin/planning/exit-plan-mode.ts
|
|
@@ -90168,7 +90210,7 @@ function formatPlanForOutput(plan, path) {
|
|
|
90168
90210
|
return `Plan mode deactivated. All tools are now available.\n${path !== void 0 ? `Plan saved to: ${path}\n\n` : ""}## Approved Plan:\n${plan}`;
|
|
90169
90211
|
}
|
|
90170
90212
|
//#endregion
|
|
90171
|
-
//#region ../../packages/agent-core/src/tools/builtin/shell/bash.md
|
|
90213
|
+
//#region ../../packages/agent-core/src/tools/builtin/shell/bash.md?raw
|
|
90172
90214
|
var bash_default = "Execute a `{{ SHELL_NAME }}` command. Use this for shell semantics — pipes, env, processes, git, package managers, build/test runners, anything genuinely interactive or multi-step.\n\n**Translate these to a dedicated tool instead:**\n- `cat` / `head` / `tail` (known path) → `Read`\n- `sed` / `awk` (in-place edit) → `Edit`\n- `echo > file` / `cat <<EOF` → `Write`\n- `find` / recursive `ls` to locate files by name pattern → `Glob` (plain `ls <known-directory>` is fine for listing a directory)\n- `grep` / `rg` (search file contents) → `Grep`\n- `echo` / `printf` (talk to the user) → just output text directly\n\nThe dedicated tools render in the per-tool permission UI and keep raw stdout out of the conversation; that is why they are worth reaching for whenever one fits.\n\n**Output:**\nThe stdout and stderr will be combined and returned as a string. The output may be truncated if it is too long. If the command failed, the output will end with a `Command failed with exit code: N` line stating the non-zero exit code.\n\nIf `run_in_background=true`, the command will be started as a background task and this tool will return a task ID instead of waiting for command completion. When doing that, you must provide a short `description`. Background commands default to a {{ DEFAULT_BACKGROUND_TIMEOUT_S }}s timeout and `timeout` is capped at {{ MAX_BACKGROUND_TIMEOUT_S }}s; set `disable_timeout=true` only when the task should run without a timeout. You will be automatically notified when the task completes. Use `TaskOutput` for a non-blocking status/output snapshot, and only set `block=true` when you explicitly want to wait for completion. Use `TaskStop` only if the task must be cancelled. If a human user wants to inspect background tasks themselves, point them to the `/tasks` command, which opens an interactive panel; it has no subcommands.\n\n**Guidelines for safety and security:**\n- Each shell tool call will be executed in a fresh shell environment. The shell variables, current working directory changes, and the shell history is not preserved between calls.\n- The tool call will return after the command is finished. You shall not use this tool to execute an interactive command or a command that may run forever. For possibly long-running foreground commands, set the `timeout` argument in seconds. Foreground commands default to {{ DEFAULT_TIMEOUT_S }}s and allow up to {{ MAX_TIMEOUT_S }}s.\n- Avoid using `..` to access files or directories outside of the working directory.\n- Avoid modifying files outside of the working directory unless explicitly instructed to do so.\n- Never run commands that require superuser privileges unless explicitly instructed to do so.\n\n**Guidelines for efficiency:**\n- For multiple related commands, use `&&` to chain them in a single call, e.g. `cd /path && ls -la`\n- Use `;` to run commands sequentially regardless of success/failure\n- Use `||` for conditional execution (run second command only if first fails)\n- Use pipe operations (`|`) and redirections (`>`, `>>`) to chain input and output between commands\n- Always quote file paths containing spaces with double quotes (e.g., cd \"/path with spaces/\")\n- Compose multi-step logic in a single call with `if` / `case` / `for` / `while` control flows.\n- Prefer `run_in_background=true` for long-running builds, tests, watchers, or servers when you need the conversation to continue before the command finishes.\n\n**Commands available:**\nThe following common command categories are usually available. Availability still depends on the host, so when in doubt run `which <command>` first to confirm a command exists before relying on it.\n- Navigation and inspection: `ls`, `pwd`, `cd`, `stat`, `file`, `du`, `df`, `tree`\n- File and directory management: `cp`, `mv`, `rm`, `mkdir`, `touch`, `ln`, `chmod`, `chown`\n- Text and data processing: `wc`, `sort`, `uniq`, `cut`, `tr`, `diff`, `xargs`\n- Archives and compression: `tar`, `gzip`, `gunzip`, `zip`, `unzip`\n- Networking and transfer: `curl`, `wget`, `ping`, `ssh`, `scp`\n- Version control: `git`\n- Process and system: `ps`, `kill`, `top`, `env`, `date`, `uname`, `whoami`\n- Language and package toolchains: `node`, `npm`, `pnpm`, `yarn`, `python`, `pip` (use whichever the project actually relies on)\n";
|
|
90173
90215
|
//#endregion
|
|
90174
90216
|
//#region ../../packages/agent-core/src/tools/builtin/shell/bash.ts
|
|
@@ -90257,7 +90299,7 @@ var BashTool = class {
|
|
|
90257
90299
|
},
|
|
90258
90300
|
approvalRule: literalRulePattern(this.name, args.command),
|
|
90259
90301
|
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
|
|
90260
|
-
execute: ({ signal }) => this.execution(args, signal)
|
|
90302
|
+
execute: ({ signal, onUpdate }) => this.execution(args, signal, onUpdate)
|
|
90261
90303
|
};
|
|
90262
90304
|
}
|
|
90263
90305
|
spawn(effectiveCwd, command) {
|
|
@@ -90279,7 +90321,7 @@ var BashTool = class {
|
|
|
90279
90321
|
};
|
|
90280
90322
|
return this.kaos.execWithEnv(shellArgs, mergedEnv);
|
|
90281
90323
|
}
|
|
90282
|
-
async execution(args, signal) {
|
|
90324
|
+
async execution(args, signal, onUpdate) {
|
|
90283
90325
|
if (signal.aborted) return {
|
|
90284
90326
|
isError: true,
|
|
90285
90327
|
output: "Aborted before command started"
|
|
@@ -90346,7 +90388,7 @@ var BashTool = class {
|
|
|
90346
90388
|
try {
|
|
90347
90389
|
const builder = new ToolResultBuilder();
|
|
90348
90390
|
const isTerminating = () => timedOut || aborted || killed;
|
|
90349
|
-
const [, exitCode] = await Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder, isTerminating), readStreamIntoBuilder(proc.stderr, builder, isTerminating)]), proc.wait()]);
|
|
90391
|
+
const [, exitCode] = await Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder, "stdout", onUpdate, isTerminating), readStreamIntoBuilder(proc.stderr, builder, "stderr", onUpdate, isTerminating)]), proc.wait()]);
|
|
90350
90392
|
if (timedOut) {
|
|
90351
90393
|
const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
|
|
90352
90394
|
return builder.error(`Command killed by timeout (${timeoutLabel})`, { brief: `Killed by timeout (${timeoutLabel})` });
|
|
@@ -90419,17 +90461,27 @@ human_shell_hint: Tell the human to run /tasks to open the interactive backgroun
|
|
|
90419
90461
|
return builder.ok("Background task started", { brief: `Started ${taskId}` });
|
|
90420
90462
|
}
|
|
90421
90463
|
};
|
|
90422
|
-
async function readStreamIntoBuilder(stream, builder, suppressPrematureClose) {
|
|
90464
|
+
async function readStreamIntoBuilder(stream, builder, kind, onUpdate, suppressPrematureClose) {
|
|
90423
90465
|
const decoder = new StringDecoder("utf8");
|
|
90424
90466
|
try {
|
|
90425
90467
|
for await (const chunk of stream) {
|
|
90426
90468
|
const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
90427
|
-
|
|
90469
|
+
const text = decoder.write(buf);
|
|
90470
|
+
if (text.length > 0) onUpdate?.({
|
|
90471
|
+
kind,
|
|
90472
|
+
text
|
|
90473
|
+
});
|
|
90474
|
+
builder.write(text);
|
|
90428
90475
|
}
|
|
90429
90476
|
} catch (error) {
|
|
90430
90477
|
if (!isPrematureCloseError(error) || suppressPrematureClose?.() !== true) throw error;
|
|
90431
90478
|
}
|
|
90432
|
-
|
|
90479
|
+
const trailing = decoder.end();
|
|
90480
|
+
if (trailing.length > 0) onUpdate?.({
|
|
90481
|
+
kind,
|
|
90482
|
+
text: trailing
|
|
90483
|
+
});
|
|
90484
|
+
builder.write(trailing);
|
|
90433
90485
|
}
|
|
90434
90486
|
function isPrematureCloseError(error) {
|
|
90435
90487
|
return error instanceof Error && error.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
@@ -90452,7 +90504,7 @@ function rewriteWindowsNullRedirect(command) {
|
|
|
90452
90504
|
return command.replace(WINDOWS_NUL_REDIRECT, "$1/dev/null");
|
|
90453
90505
|
}
|
|
90454
90506
|
//#endregion
|
|
90455
|
-
//#region ../../packages/agent-core/src/tools/builtin/web/fetch-url.md
|
|
90507
|
+
//#region ../../packages/agent-core/src/tools/builtin/web/fetch-url.md?raw
|
|
90456
90508
|
var fetch_url_default = "Fetch content from a URL. Returns the main text content extracted from the page. Use this when you need to read a specific web page.\n\nOnly public `http`/`https` URLs are supported. Requests to private, loopback, or link-local addresses are refused, and responses larger than 10 MiB are rejected.\n";
|
|
90457
90509
|
//#endregion
|
|
90458
90510
|
//#region ../../packages/agent-core/src/tools/builtin/web/fetch-url.ts
|
|
@@ -90525,7 +90577,7 @@ var FetchURLTool = class {
|
|
|
90525
90577
|
}
|
|
90526
90578
|
};
|
|
90527
90579
|
//#endregion
|
|
90528
|
-
//#region ../../packages/agent-core/src/tools/builtin/web/web-search.md
|
|
90580
|
+
//#region ../../packages/agent-core/src/tools/builtin/web/web-search.md?raw
|
|
90529
90581
|
var web_search_default = "Search the web for information. Use this when you need up-to-date information from the internet.\n\nEach result includes its title, URL, snippet, and—when available—a publication date. When `include_content` is enabled, the full page content—when available—is appended after the snippet.\n";
|
|
90530
90582
|
//#endregion
|
|
90531
90583
|
//#region ../../packages/agent-core/src/tools/builtin/web/web-search.ts
|
|
@@ -91958,13 +92010,119 @@ function readConfigFile(filePath) {
|
|
|
91958
92010
|
return parseConfigString(readFileSync(filePath, "utf-8"), filePath);
|
|
91959
92011
|
}
|
|
91960
92012
|
/**
|
|
91961
|
-
*
|
|
91962
|
-
*
|
|
91963
|
-
*
|
|
91964
|
-
*
|
|
92013
|
+
* Strict read for write paths (read-merge-write must never use a salvaged
|
|
92014
|
+
* config as its base, or the rewrite would drop the user's broken-but-fixable
|
|
92015
|
+
* sections). Re-throws validation failures with a short actionable message —
|
|
92016
|
+
* UIs surface it directly — instead of the raw validation details.
|
|
92017
|
+
*/
|
|
92018
|
+
function readConfigFileForUpdate(filePath) {
|
|
92019
|
+
try {
|
|
92020
|
+
return readConfigFile(filePath);
|
|
92021
|
+
} catch (error) {
|
|
92022
|
+
if (error instanceof KimiError && error.code === ErrorCodes.CONFIG_INVALID) throw new KimiError(ErrorCodes.CONFIG_INVALID, `Cannot change settings while ${filePath} is invalid — fix it first (run \`kimi doctor\` for details).`, { cause: error });
|
|
92023
|
+
throw error;
|
|
92024
|
+
}
|
|
92025
|
+
}
|
|
92026
|
+
/**
|
|
92027
|
+
* Lenient variant of `loadRuntimeConfig` that never throws: schema errors
|
|
92028
|
+
* drop only the offending sections (whole entry for `providers`/`models`,
|
|
92029
|
+
* whole top-level section otherwise) and a bad KIMI_MODEL_* env overlay is
|
|
92030
|
+
* skipped, each reported as a warning. A file that cannot be used at all
|
|
92031
|
+
* additionally sets `fileError` so startup can fail fast while mid-run
|
|
92032
|
+
* reloads degrade. Runtime read paths use this; write paths must keep using
|
|
92033
|
+
* the strict readers so a broken file is never silently rewritten.
|
|
91965
92034
|
*/
|
|
91966
|
-
function
|
|
91967
|
-
|
|
92035
|
+
function loadRuntimeConfigSafe(filePath, env = process.env) {
|
|
92036
|
+
const fileWarnings = [];
|
|
92037
|
+
let fileError;
|
|
92038
|
+
let config = getDefaultConfig();
|
|
92039
|
+
let text;
|
|
92040
|
+
try {
|
|
92041
|
+
text = existsSync(filePath) ? readFileSync(filePath, "utf-8") : void 0;
|
|
92042
|
+
} catch (error) {
|
|
92043
|
+
fileError = new KimiError(ErrorCodes.CONFIG_INVALID, `Failed to read ${filePath}: ${describeUnknownError(error)}`, { cause: error });
|
|
92044
|
+
fileWarnings.push(`Failed to read ${filePath}: ${describeUnknownError(error)}.`);
|
|
92045
|
+
}
|
|
92046
|
+
if (text !== void 0 && text.trim().length > 0) {
|
|
92047
|
+
let data;
|
|
92048
|
+
try {
|
|
92049
|
+
data = parse$2(text);
|
|
92050
|
+
} catch (error) {
|
|
92051
|
+
fileError = new KimiError(ErrorCodes.CONFIG_INVALID, `Invalid TOML in ${filePath}: ${describeUnknownError(error)}`, { cause: error });
|
|
92052
|
+
fileWarnings.push(`Invalid TOML in ${filePath}: ${describeTomlSyntaxError(error)}.`);
|
|
92053
|
+
}
|
|
92054
|
+
if (data !== void 0) {
|
|
92055
|
+
const raw = cloneRecord(data);
|
|
92056
|
+
const transformed = transformTomlData(data);
|
|
92057
|
+
transformed["raw"] = raw;
|
|
92058
|
+
const salvaged = salvageConfigData(transformed);
|
|
92059
|
+
if (salvaged.config === void 0) {
|
|
92060
|
+
fileError = new KimiError(ErrorCodes.CONFIG_INVALID, `Invalid configuration in ${filePath}: ${formatConfigValidationError(salvaged.error)}`, { cause: salvaged.error });
|
|
92061
|
+
fileWarnings.push(`Invalid configuration in ${filePath}: ${formatConfigValidationError(salvaged.error)}.`);
|
|
92062
|
+
} else {
|
|
92063
|
+
config = salvaged.config;
|
|
92064
|
+
if (salvaged.dropped.length > 0) fileWarnings.push(`Ignored invalid config in ${filePath}: ${salvaged.dropped.join(", ")}. Run \`kimi doctor\` for details.`);
|
|
92065
|
+
}
|
|
92066
|
+
}
|
|
92067
|
+
}
|
|
92068
|
+
const envWarnings = [];
|
|
92069
|
+
try {
|
|
92070
|
+
config = applyEnvModelConfig(config, env);
|
|
92071
|
+
} catch (error) {
|
|
92072
|
+
envWarnings.push(`Ignoring KIMI_MODEL_* environment overrides: ${describeUnknownError(error)}`);
|
|
92073
|
+
}
|
|
92074
|
+
return {
|
|
92075
|
+
config,
|
|
92076
|
+
fileWarnings,
|
|
92077
|
+
envWarnings,
|
|
92078
|
+
fileError
|
|
92079
|
+
};
|
|
92080
|
+
}
|
|
92081
|
+
/** Sections keyed by user-chosen names where single entries can be dropped. */
|
|
92082
|
+
const ENTRY_KEYED_SECTIONS = new Set(["providers", "models"]);
|
|
92083
|
+
function salvageConfigData(transformed) {
|
|
92084
|
+
const dropped = [];
|
|
92085
|
+
for (;;) {
|
|
92086
|
+
const result = KimiConfigSchema.safeParse(transformed);
|
|
92087
|
+
if (result.success) return {
|
|
92088
|
+
config: result.data,
|
|
92089
|
+
dropped
|
|
92090
|
+
};
|
|
92091
|
+
let deletedAny = false;
|
|
92092
|
+
for (const issue of result.error.issues) {
|
|
92093
|
+
const [section, entry] = issue.path;
|
|
92094
|
+
if (typeof section !== "string" || !(section in transformed)) continue;
|
|
92095
|
+
const sectionValue = transformed[section];
|
|
92096
|
+
if (ENTRY_KEYED_SECTIONS.has(section) && typeof entry === "string" && isPlainObject$1(sectionValue)) {
|
|
92097
|
+
if (entry in sectionValue) {
|
|
92098
|
+
delete sectionValue[entry];
|
|
92099
|
+
dropped.push(`${camelToSnake$2(section)}.${entry}`);
|
|
92100
|
+
deletedAny = true;
|
|
92101
|
+
}
|
|
92102
|
+
continue;
|
|
92103
|
+
}
|
|
92104
|
+
delete transformed[section];
|
|
92105
|
+
dropped.push(camelToSnake$2(section));
|
|
92106
|
+
deletedAny = true;
|
|
92107
|
+
}
|
|
92108
|
+
if (!deletedAny) return {
|
|
92109
|
+
config: void 0,
|
|
92110
|
+
dropped,
|
|
92111
|
+
error: result.error
|
|
92112
|
+
};
|
|
92113
|
+
}
|
|
92114
|
+
}
|
|
92115
|
+
function describeUnknownError(error) {
|
|
92116
|
+
return error instanceof Error ? error.message : String(error);
|
|
92117
|
+
}
|
|
92118
|
+
/**
|
|
92119
|
+
* One-line summary of a smol-toml parse error: first message line plus the
|
|
92120
|
+
* line/column location, without the multi-line code-frame block.
|
|
92121
|
+
*/
|
|
92122
|
+
function describeTomlSyntaxError(error) {
|
|
92123
|
+
const firstLine = describeUnknownError(error).split("\n", 1)[0] ?? "";
|
|
92124
|
+
if (error instanceof TomlError) return `${firstLine} (line ${error.line}, column ${error.column})`;
|
|
92125
|
+
return firstLine;
|
|
91968
92126
|
}
|
|
91969
92127
|
function parseConfigString(tomlText, filePath = "config.toml") {
|
|
91970
92128
|
if (tomlText.trim().length === 0) return getDefaultConfig();
|
|
@@ -121295,7 +121453,7 @@ var McpConnectionManager = class {
|
|
|
121295
121453
|
const startupClient = this.createClient(entry.config, entry.name);
|
|
121296
121454
|
client = startupClient;
|
|
121297
121455
|
entry.client = startupClient;
|
|
121298
|
-
const tools = await withTimeout(this.connectAndDiscoverTools(startupClient), timeoutMs, () => {
|
|
121456
|
+
const tools = await withTimeout$1(this.connectAndDiscoverTools(startupClient), timeoutMs, () => {
|
|
121299
121457
|
this.closeRuntimeClient(startupClient);
|
|
121300
121458
|
});
|
|
121301
121459
|
if (!this.isCurrent(entry, attemptId)) {
|
|
@@ -121449,7 +121607,7 @@ function stderrTail(client) {
|
|
|
121449
121607
|
if (snapshot.length === 0) return void 0;
|
|
121450
121608
|
return snapshot.trimEnd();
|
|
121451
121609
|
}
|
|
121452
|
-
async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
121610
|
+
async function withTimeout$1(promise, timeoutMs, onTimeout) {
|
|
121453
121611
|
let timer;
|
|
121454
121612
|
try {
|
|
121455
121613
|
return await new Promise((resolve, reject) => {
|
|
@@ -138802,6 +138960,7 @@ var KimiCore = class {
|
|
|
138802
138960
|
kaos;
|
|
138803
138961
|
runtime;
|
|
138804
138962
|
config;
|
|
138963
|
+
configWarnings = [];
|
|
138805
138964
|
runtimeOverride;
|
|
138806
138965
|
userHomeDir;
|
|
138807
138966
|
kimiRequestHeaders;
|
|
@@ -138829,7 +138988,11 @@ var KimiCore = class {
|
|
|
138829
138988
|
this.telemetry = options.telemetry ?? noopTelemetryClient;
|
|
138830
138989
|
this.appVersion = options.appVersion;
|
|
138831
138990
|
ensureKimiHome(this.homeDir);
|
|
138832
|
-
|
|
138991
|
+
const loaded = loadRuntimeConfigSafe(this.configPath);
|
|
138992
|
+
if (loaded.fileError !== void 0) throw loaded.fileError;
|
|
138993
|
+
this.config = loaded.config;
|
|
138994
|
+
this.configWarnings = [...loaded.fileWarnings, ...loaded.envWarnings];
|
|
138995
|
+
if (this.configWarnings.length > 0) log.warn("config load degraded", { warnings: this.configWarnings });
|
|
138833
138996
|
this.experimentalFlags = new FlagResolver(process.env, FLAG_DEFINITIONS, this.config.experimental);
|
|
138834
138997
|
this.sessionStore = new SessionStore(this.homeDir);
|
|
138835
138998
|
this.plugins = new PluginManager({ kimiHomeDir: this.homeDir });
|
|
@@ -139038,16 +139201,19 @@ var KimiCore = class {
|
|
|
139038
139201
|
});
|
|
139039
139202
|
}
|
|
139040
139203
|
async getKimiConfig(input) {
|
|
139041
|
-
if (input?.reload) this.
|
|
139204
|
+
if (input?.reload) this.reloadRuntimeConfig();
|
|
139042
139205
|
return this.config;
|
|
139043
139206
|
}
|
|
139207
|
+
async getConfigDiagnostics(_input) {
|
|
139208
|
+
return { warnings: this.configWarnings };
|
|
139209
|
+
}
|
|
139044
139210
|
async setKimiConfig(input) {
|
|
139045
|
-
const config = mergeConfigPatch(
|
|
139211
|
+
const config = mergeConfigPatch(this.readConfigForWrite(), input);
|
|
139046
139212
|
await writeConfigFile(this.configPath, config);
|
|
139047
|
-
return this.
|
|
139213
|
+
return this.reloadRuntimeConfig();
|
|
139048
139214
|
}
|
|
139049
139215
|
async removeKimiProvider(input) {
|
|
139050
|
-
const config =
|
|
139216
|
+
const config = this.readConfigForWrite();
|
|
139051
139217
|
delete config.providers[input.providerId];
|
|
139052
139218
|
let removedDefault = false;
|
|
139053
139219
|
const existingModels = config.models ?? {};
|
|
@@ -139059,7 +139225,7 @@ var KimiCore = class {
|
|
|
139059
139225
|
if (removedDefault) config.defaultModel = void 0;
|
|
139060
139226
|
if (config.defaultProvider === input.providerId) config.defaultProvider = void 0;
|
|
139061
139227
|
await writeConfigFile(this.configPath, config);
|
|
139062
|
-
return this.
|
|
139228
|
+
return this.reloadRuntimeConfig();
|
|
139063
139229
|
}
|
|
139064
139230
|
prompt({ sessionId, ...payload }) {
|
|
139065
139231
|
return this.sessionApi(sessionId).prompt(payload);
|
|
@@ -139319,7 +139485,24 @@ var KimiCore = class {
|
|
|
139319
139485
|
return new SessionAPIImpl(session);
|
|
139320
139486
|
}
|
|
139321
139487
|
reloadProviderManager() {
|
|
139322
|
-
return this.
|
|
139488
|
+
return this.reloadRuntimeConfig();
|
|
139489
|
+
}
|
|
139490
|
+
readConfigForWrite() {
|
|
139491
|
+
return readConfigFileForUpdate(this.configPath);
|
|
139492
|
+
}
|
|
139493
|
+
reloadRuntimeConfig() {
|
|
139494
|
+
const loaded = loadRuntimeConfigSafe(this.configPath);
|
|
139495
|
+
if (loaded.fileWarnings.length > 0) {
|
|
139496
|
+
this.configWarnings = [
|
|
139497
|
+
...loaded.fileWarnings,
|
|
139498
|
+
...loaded.envWarnings,
|
|
139499
|
+
"config.toml has errors; keeping the previously loaded configuration."
|
|
139500
|
+
];
|
|
139501
|
+
log.warn("config reload degraded; keeping previous config", { warnings: loaded.fileWarnings });
|
|
139502
|
+
return this.config;
|
|
139503
|
+
}
|
|
139504
|
+
this.configWarnings = loaded.envWarnings;
|
|
139505
|
+
return this.setRuntimeConfig(loaded.config);
|
|
139323
139506
|
}
|
|
139324
139507
|
setRuntimeConfig(config) {
|
|
139325
139508
|
this.config = config;
|
|
@@ -139982,6 +140165,10 @@ var KimiHarness = class {
|
|
|
139982
140165
|
async getConfig(options = {}) {
|
|
139983
140166
|
return this.rpc.getConfig(options);
|
|
139984
140167
|
}
|
|
140168
|
+
/** Warnings from the most recent config.toml load; empty when the config is fully valid. */
|
|
140169
|
+
async getConfigDiagnostics() {
|
|
140170
|
+
return this.rpc.getConfigDiagnostics();
|
|
140171
|
+
}
|
|
139985
140172
|
async getExperimentalFeatures() {
|
|
139986
140173
|
return this.rpc.getExperimentalFeatures();
|
|
139987
140174
|
}
|
|
@@ -143239,7 +143426,7 @@ var KimiAuthFacade = class {
|
|
|
143239
143426
|
onRefresh: options.onRefresh,
|
|
143240
143427
|
configAdapter: {
|
|
143241
143428
|
configPath: options.configPath,
|
|
143242
|
-
read: () =>
|
|
143429
|
+
read: () => readConfigFileForUpdate(options.configPath),
|
|
143243
143430
|
write: async (config) => {
|
|
143244
143431
|
await writeConfigFile(options.configPath, config);
|
|
143245
143432
|
},
|
|
@@ -143314,7 +143501,7 @@ var KimiAuthFacade = class {
|
|
|
143314
143501
|
};
|
|
143315
143502
|
resolveManagedAuth(providerName) {
|
|
143316
143503
|
const name = providerName ?? "managed:kimi-code";
|
|
143317
|
-
const provider =
|
|
143504
|
+
const provider = loadRuntimeConfigSafe(this.options.configPath).config.providers[name];
|
|
143318
143505
|
return {
|
|
143319
143506
|
oauthRef: provider?.oauth,
|
|
143320
143507
|
baseUrl: provider?.baseUrl
|
|
@@ -143403,6 +143590,9 @@ var SDKRpcClientBase = class {
|
|
|
143403
143590
|
async getConfig(input) {
|
|
143404
143591
|
return (await this.getRpc()).getKimiConfig(input ?? {});
|
|
143405
143592
|
}
|
|
143593
|
+
async getConfigDiagnostics() {
|
|
143594
|
+
return (await this.getRpc()).getConfigDiagnostics({});
|
|
143595
|
+
}
|
|
143406
143596
|
async getExperimentalFeatures() {
|
|
143407
143597
|
return (await this.getRpc()).getExperimentalFeatures({});
|
|
143408
143598
|
}
|
|
@@ -154211,7 +154401,7 @@ function buildSkillSlashCommands(skills) {
|
|
|
154211
154401
|
const commandMap = /* @__PURE__ */ new Map();
|
|
154212
154402
|
return {
|
|
154213
154403
|
commands: [...skills].toSorted(compareSkillSlashCommands).filter(isUserActivatableSkill).map((skill) => {
|
|
154214
|
-
const commandName = skill.source === "builtin" ? skill.name : `skill:${skill.name}`;
|
|
154404
|
+
const commandName = skill.source === "builtin" || skill.isSubSkill === true ? skill.name : `skill:${skill.name}`;
|
|
154215
154405
|
commandMap.set(commandName, skill.name);
|
|
154216
154406
|
return {
|
|
154217
154407
|
name: commandName,
|
|
@@ -154951,8 +155141,9 @@ function registerLoginCommand(parent) {
|
|
|
154951
155141
|
*
|
|
154952
155142
|
* `add` writes the same `source = { kind: 'apiJson', url, apiKey }` blob the
|
|
154953
155143
|
* TUI does; the next launch's `refreshAllProviderModels`
|
|
154954
|
-
* (apps/kimi-code/src/tui/utils/refresh-providers.ts) groups by
|
|
154955
|
-
* and re-fetches the model list, so periodic
|
|
155144
|
+
* (apps/kimi-code/src/tui/utils/refresh-providers.ts) groups by URL, retries
|
|
155145
|
+
* available API-key candidates, and re-fetches the model list, so periodic
|
|
155146
|
+
* refresh is automatic.
|
|
154956
155147
|
*/
|
|
154957
155148
|
async function handleProviderAdd$1(deps, url, opts) {
|
|
154958
155149
|
const apiKey = resolveApiKey(opts.apiKey, deps.env);
|
|
@@ -155176,29 +155367,42 @@ async function loadCatalogOrExit(deps, url) {
|
|
|
155176
155367
|
}
|
|
155177
155368
|
function registerProviderCommand(parent, deps) {
|
|
155178
155369
|
const provider = parent.command("provider").description("Manage LLM providers non-interactively.");
|
|
155370
|
+
const runAction = async (resolved, run) => {
|
|
155371
|
+
try {
|
|
155372
|
+
await run();
|
|
155373
|
+
} catch (error) {
|
|
155374
|
+
resolved.stderr.write(`${errorMessage(error)}\n`);
|
|
155375
|
+
resolved.exit(1);
|
|
155376
|
+
}
|
|
155377
|
+
};
|
|
155179
155378
|
provider.command("add <url>").description("Import every provider listed in a custom registry (api.json).").option("--api-key <key>", "Registry API key. Falls back to KIMI_REGISTRY_API_KEY.").action(async (url, options) => {
|
|
155180
|
-
|
|
155379
|
+
const resolved = resolveDeps(deps);
|
|
155380
|
+
await runAction(resolved, () => handleProviderAdd$1(resolved, url, { apiKey: options.apiKey }));
|
|
155181
155381
|
});
|
|
155182
155382
|
provider.command("remove <providerId>").description("Remove a provider and every model alias that referenced it.").action(async (providerId) => {
|
|
155183
|
-
|
|
155383
|
+
const resolved = resolveDeps(deps);
|
|
155384
|
+
await runAction(resolved, () => handleProviderRemove(resolved, providerId));
|
|
155184
155385
|
});
|
|
155185
155386
|
provider.command("list").description("Show configured providers and their model counts.").option("--json", "Emit the raw providers/models config as JSON.", false).action(async (options) => {
|
|
155186
|
-
|
|
155387
|
+
const resolved = resolveDeps(deps);
|
|
155388
|
+
await runAction(resolved, () => handleProviderList(resolved, { json: options.json === true }));
|
|
155187
155389
|
});
|
|
155188
155390
|
const catalog = provider.command("catalog").description("Discover and import providers from the public models.dev catalog.");
|
|
155189
155391
|
catalog.command("list [providerId]").description("List providers in the catalog, or models when a providerId is given.").option("--filter <substring>", "Case-insensitive id/name substring filter.").option("--url <url>", `Override catalog URL. Defaults to ${DEFAULT_CATALOG_URL}.`).option("--json", "Emit the matching catalog slice as JSON.", false).action(async (providerId, options) => {
|
|
155190
|
-
|
|
155392
|
+
const resolved = resolveDeps(deps);
|
|
155393
|
+
await runAction(resolved, () => handleCatalogList(resolved, providerId, {
|
|
155191
155394
|
json: options.json === true,
|
|
155192
155395
|
...options.filter === void 0 ? {} : { filter: options.filter },
|
|
155193
155396
|
...options.url === void 0 ? {} : { url: options.url }
|
|
155194
|
-
});
|
|
155397
|
+
}));
|
|
155195
155398
|
});
|
|
155196
155399
|
catalog.command("add <providerId>").description("Import a known provider from the catalog by id.").option("--api-key <key>", "API key for the provider. Falls back to KIMI_REGISTRY_API_KEY.").option("--default-model <modelId>", "Mark the imported model as default_model after import.").option("--url <url>", `Override catalog URL. Defaults to ${DEFAULT_CATALOG_URL}.`).action(async (providerId, options) => {
|
|
155197
|
-
|
|
155400
|
+
const resolved = resolveDeps(deps);
|
|
155401
|
+
await runAction(resolved, () => handleCatalogAdd(resolved, providerId, {
|
|
155198
155402
|
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
155199
155403
|
...options.defaultModel === void 0 ? {} : { defaultModel: options.defaultModel },
|
|
155200
155404
|
...options.url === void 0 ? {} : { url: options.url }
|
|
155201
|
-
});
|
|
155405
|
+
}));
|
|
155202
155406
|
});
|
|
155203
155407
|
}
|
|
155204
155408
|
function resolveDeps(overrides = {}) {
|
|
@@ -155292,9 +155496,6 @@ function validateOptions(opts) {
|
|
|
155292
155496
|
if (promptMode && opts.session === "") throw new OptionConflictError("Cannot use --session without an id in prompt mode.");
|
|
155293
155497
|
if (opts.continue && opts.session !== void 0) throw new OptionConflictError("Cannot combine --continue, --session.");
|
|
155294
155498
|
if (opts.yolo && opts.auto) throw new OptionConflictError("Cannot combine --yolo with --auto.");
|
|
155295
|
-
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.yolo) throw new OptionConflictError("Cannot combine --yolo with --continue or --session.");
|
|
155296
|
-
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.auto) throw new OptionConflictError("Cannot combine --auto with --continue or --session.");
|
|
155297
|
-
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.plan) throw new OptionConflictError("Cannot combine --plan with --continue or --session.");
|
|
155298
155499
|
return {
|
|
155299
155500
|
options: opts,
|
|
155300
155501
|
uiMode: promptMode ? "print" : "shell"
|
|
@@ -157877,6 +158078,7 @@ async function loadManagedUsageReport(host) {
|
|
|
157877
158078
|
}
|
|
157878
158079
|
//#endregion
|
|
157879
158080
|
//#region src/tui/commands/config.ts
|
|
158081
|
+
const MODEL_PICKER_REFRESH_TIMEOUT_MS = 2e3;
|
|
157880
158082
|
async function handlePlanCommand(host, args) {
|
|
157881
158083
|
const session = host.session;
|
|
157882
158084
|
if (session === void 0) {
|
|
@@ -158021,8 +158223,9 @@ async function handleThemeCommand(host, args) {
|
|
|
158021
158223
|
}
|
|
158022
158224
|
await applyThemeChoice(host, theme);
|
|
158023
158225
|
}
|
|
158024
|
-
function handleModelCommand(host, args) {
|
|
158226
|
+
async function handleModelCommand(host, args) {
|
|
158025
158227
|
const alias = args.trim();
|
|
158228
|
+
await refreshModelsForPicker(host);
|
|
158026
158229
|
if (alias.length === 0) {
|
|
158027
158230
|
showModelPicker(host);
|
|
158028
158231
|
return;
|
|
@@ -158046,6 +158249,27 @@ function showEditorPicker(host) {
|
|
|
158046
158249
|
}
|
|
158047
158250
|
}));
|
|
158048
158251
|
}
|
|
158252
|
+
async function refreshModelsForPicker(host) {
|
|
158253
|
+
try {
|
|
158254
|
+
const result = await withTimeout(host.authFlow.refreshOAuthProviderModels(), MODEL_PICKER_REFRESH_TIMEOUT_MS);
|
|
158255
|
+
if (result === void 0) return;
|
|
158256
|
+
for (const f of result.failed) host.showStatus(`Skipped refreshing ${f.provider}: ${f.reason}`, "warning");
|
|
158257
|
+
} catch (error) {
|
|
158258
|
+
host.showStatus(`Skipped refreshing models: ${formatErrorMessage$2(error)}`, "warning");
|
|
158259
|
+
}
|
|
158260
|
+
}
|
|
158261
|
+
async function withTimeout(promise, timeoutMs) {
|
|
158262
|
+
let timeout;
|
|
158263
|
+
try {
|
|
158264
|
+
return await Promise.race([promise, new Promise((resolve) => {
|
|
158265
|
+
timeout = setTimeout(() => {
|
|
158266
|
+
resolve(void 0);
|
|
158267
|
+
}, timeoutMs);
|
|
158268
|
+
})]);
|
|
158269
|
+
} finally {
|
|
158270
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
158271
|
+
}
|
|
158272
|
+
}
|
|
158049
158273
|
async function applyEditorChoice(host, value) {
|
|
158050
158274
|
if (value === (host.state.appState.editorCommand ?? "") && value.length > 0) {
|
|
158051
158275
|
host.showStatus(`Editor unchanged: ${value.length > 0 ? value : "auto-detect"}`);
|
|
@@ -160039,10 +160263,14 @@ function buildProviderManagerOptions(host) {
|
|
|
160039
160263
|
providers: host.state.appState.availableProviders,
|
|
160040
160264
|
activeProviderId,
|
|
160041
160265
|
onAdd: () => {
|
|
160042
|
-
handleProviderAdd(host)
|
|
160266
|
+
handleProviderAdd(host).catch((error) => {
|
|
160267
|
+
host.showError(`Add provider failed: ${formatErrorMessage$2(error)}`);
|
|
160268
|
+
});
|
|
160043
160269
|
},
|
|
160044
160270
|
onDeleteSource: (providerIds) => {
|
|
160045
|
-
handleProviderManagerDeleteSource(host, providerIds)
|
|
160271
|
+
handleProviderManagerDeleteSource(host, providerIds).catch((error) => {
|
|
160272
|
+
host.showError(`Remove provider failed: ${formatErrorMessage$2(error)}`);
|
|
160273
|
+
});
|
|
160046
160274
|
},
|
|
160047
160275
|
onClose: () => {
|
|
160048
160276
|
host.restoreEditor();
|
|
@@ -160192,7 +160420,9 @@ async function handleCatalogProviderAdd(host) {
|
|
|
160192
160420
|
initialTabId: providerId,
|
|
160193
160421
|
onSelect: ({ alias, thinking }) => {
|
|
160194
160422
|
host.restoreEditor();
|
|
160195
|
-
setDefaultModel(host, alias, thinking)
|
|
160423
|
+
setDefaultModel(host, alias, thinking).catch((error) => {
|
|
160424
|
+
host.showError(`Set default model failed: ${formatErrorMessage$2(error)}`);
|
|
160425
|
+
});
|
|
160196
160426
|
},
|
|
160197
160427
|
onCancel: () => {
|
|
160198
160428
|
host.restoreEditor();
|
|
@@ -160220,8 +160450,8 @@ async function handleCustomRegistryAddViaDialog(host) {
|
|
|
160220
160450
|
let entries;
|
|
160221
160451
|
try {
|
|
160222
160452
|
entries = await fetchCustomRegistry(source);
|
|
160223
|
-
} catch (
|
|
160224
|
-
host.showError(`Failed to import registry: ${formatErrorMessage$2(
|
|
160453
|
+
} catch (error) {
|
|
160454
|
+
host.showError(`Failed to import registry: ${formatErrorMessage$2(error)}`);
|
|
160225
160455
|
return false;
|
|
160226
160456
|
}
|
|
160227
160457
|
const addedProviderIds = Object.values(entries).map((entry) => entry.id);
|
|
@@ -160233,8 +160463,8 @@ async function handleCustomRegistryAddViaDialog(host) {
|
|
|
160233
160463
|
models: config.models
|
|
160234
160464
|
});
|
|
160235
160465
|
await host.authFlow.refreshConfigAfterLogin();
|
|
160236
|
-
} catch (
|
|
160237
|
-
host.showError(`Failed to apply registry: ${formatErrorMessage$2(
|
|
160466
|
+
} catch (error) {
|
|
160467
|
+
host.showError(`Failed to apply registry: ${formatErrorMessage$2(error)}`);
|
|
160238
160468
|
return false;
|
|
160239
160469
|
}
|
|
160240
160470
|
const count = addedProviderIds.length;
|
|
@@ -160254,7 +160484,9 @@ async function handleCustomRegistryAddViaDialog(host) {
|
|
|
160254
160484
|
initialTabId: firstNewProvider,
|
|
160255
160485
|
onSelect: ({ alias, thinking }) => {
|
|
160256
160486
|
host.restoreEditor();
|
|
160257
|
-
setDefaultModel(host, alias, thinking)
|
|
160487
|
+
setDefaultModel(host, alias, thinking).catch((error) => {
|
|
160488
|
+
host.showError(`Set default model failed: ${formatErrorMessage$2(error)}`);
|
|
160489
|
+
});
|
|
160258
160490
|
},
|
|
160259
160491
|
onCancel: () => {
|
|
160260
160492
|
host.restoreEditor();
|
|
@@ -164546,11 +164778,13 @@ var TruncatedOutputComponent = class {
|
|
|
164546
164778
|
maxLines;
|
|
164547
164779
|
indent;
|
|
164548
164780
|
expandHint;
|
|
164781
|
+
tail;
|
|
164549
164782
|
constructor(output, options) {
|
|
164550
164783
|
this.expanded = options.expanded;
|
|
164551
164784
|
this.maxLines = options.maxLines ?? PREVIEW_LINES;
|
|
164552
164785
|
this.indent = options.indent ?? DEFAULT_INDENT;
|
|
164553
164786
|
this.expandHint = options.expandHint ?? true;
|
|
164787
|
+
this.tail = options.tail ?? false;
|
|
164554
164788
|
const cleaned = trimTrailingEmptyLines(output.split("\n")).join("\n");
|
|
164555
164789
|
this.textComponent = new Text(options.isError ? currentTheme.fg("error", cleaned) : currentTheme.dim(cleaned), this.indent, 0);
|
|
164556
164790
|
}
|
|
@@ -164560,8 +164794,12 @@ var TruncatedOutputComponent = class {
|
|
|
164560
164794
|
render(width) {
|
|
164561
164795
|
const contentLines = this.textComponent.render(width);
|
|
164562
164796
|
if (this.expanded || contentLines.length <= this.maxLines) return contentLines;
|
|
164563
|
-
const shown = contentLines.slice(0, this.maxLines);
|
|
164564
164797
|
const remaining = contentLines.length - this.maxLines;
|
|
164798
|
+
if (this.tail) {
|
|
164799
|
+
const shown = contentLines.slice(contentLines.length - this.maxLines);
|
|
164800
|
+
return [" ".repeat(this.indent) + currentTheme.dim(`... (${String(remaining)} earlier lines)`), ...shown];
|
|
164801
|
+
}
|
|
164802
|
+
const shown = contentLines.slice(0, this.maxLines);
|
|
164565
164803
|
const hint = this.expandHint ? `... (${String(remaining)} more lines, ctrl+o to expand)` : `... (${String(remaining)} more lines)`;
|
|
164566
164804
|
return [...shown, " ".repeat(this.indent) + currentTheme.dim(hint)];
|
|
164567
164805
|
}
|
|
@@ -164579,7 +164817,7 @@ var ShellExecutionComponent = class extends Container {
|
|
|
164579
164817
|
constructor(options) {
|
|
164580
164818
|
super();
|
|
164581
164819
|
if (options.showCommand === true) this.addCommandPreview(options.command ?? "", options.commandPreviewLines);
|
|
164582
|
-
if (options.result !== void 0) this.addResultPreview(options.result, options.expanded ?? false, options.resultPreviewLines ?? PREVIEW_LINES);
|
|
164820
|
+
if (options.result !== void 0) this.addResultPreview(options.result, options.expanded ?? false, options.resultPreviewLines ?? PREVIEW_LINES, options.tailOutput ?? false, options.expandHint ?? true);
|
|
164583
164821
|
}
|
|
164584
164822
|
addCommandPreview(command, previewLines) {
|
|
164585
164823
|
if (command.length === 0) return;
|
|
@@ -164590,12 +164828,14 @@ var ShellExecutionComponent = class extends Container {
|
|
|
164590
164828
|
this.addChild(new Text(currentTheme.dim(prefix + line), 2, 0));
|
|
164591
164829
|
}
|
|
164592
164830
|
}
|
|
164593
|
-
addResultPreview(result, expanded, previewLines) {
|
|
164831
|
+
addResultPreview(result, expanded, previewLines, tailOutput, expandHint) {
|
|
164594
164832
|
if (!result.output) return;
|
|
164595
164833
|
this.addChild(new TruncatedOutputComponent(result.output, {
|
|
164596
164834
|
expanded,
|
|
164597
164835
|
isError: result.is_error ?? false,
|
|
164598
|
-
maxLines: previewLines
|
|
164836
|
+
maxLines: previewLines,
|
|
164837
|
+
tail: tailOutput,
|
|
164838
|
+
expandHint
|
|
164599
164839
|
}));
|
|
164600
164840
|
}
|
|
164601
164841
|
};
|
|
@@ -165045,6 +165285,7 @@ const STREAMING_PROGRESS_INTERVAL_MS = 1e3;
|
|
|
165045
165285
|
const SUBAGENT_ELAPSED_INTERVAL_MS = 1e3;
|
|
165046
165286
|
const PROGRESS_URL_RE = /https?:\/\/\S+/g;
|
|
165047
165287
|
const ABORTED_MARK = "⊘";
|
|
165288
|
+
const MAX_LIVE_OUTPUT_CHARS = 5e4;
|
|
165048
165289
|
function backgroundFailureMessage(status) {
|
|
165049
165290
|
switch (status) {
|
|
165050
165291
|
case "lost": return "Background agent lost (session restarted before completion)";
|
|
@@ -165360,6 +165601,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165360
165601
|
subagentEndedAtMs;
|
|
165361
165602
|
progressLines = [];
|
|
165362
165603
|
static MAX_PROGRESS_LINES = 24;
|
|
165604
|
+
liveOutput = "";
|
|
165363
165605
|
/**
|
|
165364
165606
|
* Registered by a group container (`AgentGroupComponent` or
|
|
165365
165607
|
* `ReadGroupComponent`) when this component is borrowed as a hidden state
|
|
@@ -165382,6 +165624,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165382
165624
|
this.buildCallPreview();
|
|
165383
165625
|
this.callPreviewEndIndex = this.children.length;
|
|
165384
165626
|
this.buildProgressBlock();
|
|
165627
|
+
this.buildLiveOutputBlock();
|
|
165385
165628
|
this.buildContent();
|
|
165386
165629
|
this.buildSubagentBlock();
|
|
165387
165630
|
this.syncStreamingProgressTimer();
|
|
@@ -165400,6 +165643,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165400
165643
|
setResult(result) {
|
|
165401
165644
|
this.result = result;
|
|
165402
165645
|
this.progressLines = [];
|
|
165646
|
+
this.liveOutput = "";
|
|
165403
165647
|
this.finalizeSubagentElapsedIfNeeded();
|
|
165404
165648
|
this.syncStreamingProgressTimer();
|
|
165405
165649
|
this.syncSubagentElapsedTimer();
|
|
@@ -165430,6 +165674,14 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165430
165674
|
this.notifySnapshotChange();
|
|
165431
165675
|
this.ui?.requestRender();
|
|
165432
165676
|
}
|
|
165677
|
+
appendLiveOutput(text) {
|
|
165678
|
+
if (this.result !== void 0 || text.length === 0) return;
|
|
165679
|
+
this.liveOutput += text;
|
|
165680
|
+
if (this.liveOutput.length > MAX_LIVE_OUTPUT_CHARS) this.liveOutput = `[...truncated]\n${this.liveOutput.slice(this.liveOutput.length - MAX_LIVE_OUTPUT_CHARS)}`;
|
|
165681
|
+
this.rebuildContent();
|
|
165682
|
+
this.notifySnapshotChange();
|
|
165683
|
+
this.ui?.requestRender();
|
|
165684
|
+
}
|
|
165433
165685
|
dispose() {
|
|
165434
165686
|
this.stopStreamingProgressTimer();
|
|
165435
165687
|
this.stopSubagentElapsedTimer();
|
|
@@ -165800,6 +166052,20 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165800
166052
|
this.notifySnapshotChange();
|
|
165801
166053
|
this.ui?.requestRender();
|
|
165802
166054
|
}
|
|
166055
|
+
appendSubToolLiveOutput(id, text) {
|
|
166056
|
+
if (text.length === 0) return;
|
|
166057
|
+
const activity = this.subToolActivities.get(id);
|
|
166058
|
+
const ongoing = this.ongoingSubCalls.get(id);
|
|
166059
|
+
if (activity === void 0 && ongoing === void 0) return;
|
|
166060
|
+
const name = activity?.name ?? ongoing?.name ?? "Tool";
|
|
166061
|
+
const args = activity?.args ?? ongoing?.args ?? {};
|
|
166062
|
+
let output = (activity?.output ?? "") + text;
|
|
166063
|
+
if (output.length > MAX_LIVE_OUTPUT_CHARS) output = `[...truncated]\n${output.slice(output.length - MAX_LIVE_OUTPUT_CHARS)}`;
|
|
166064
|
+
this.upsertSubToolActivity(id, name, args, activity?.phase ?? "ongoing", output);
|
|
166065
|
+
this.rebuildContent();
|
|
166066
|
+
this.notifySnapshotChange();
|
|
166067
|
+
this.ui?.requestRender();
|
|
166068
|
+
}
|
|
165803
166069
|
finishSubToolCall(result) {
|
|
165804
166070
|
const ongoing = this.ongoingSubCalls.get(result.tool_call_id);
|
|
165805
166071
|
if (ongoing === void 0) return;
|
|
@@ -165874,6 +166140,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165874
166140
|
rebuildContent() {
|
|
165875
166141
|
while (this.children.length > this.callPreviewEndIndex) this.children.pop();
|
|
165876
166142
|
this.buildProgressBlock();
|
|
166143
|
+
this.buildLiveOutputBlock();
|
|
165877
166144
|
this.buildContent();
|
|
165878
166145
|
this.buildSubagentBlock();
|
|
165879
166146
|
}
|
|
@@ -165882,6 +166149,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165882
166149
|
this.buildCallPreview();
|
|
165883
166150
|
this.callPreviewEndIndex = this.children.length;
|
|
165884
166151
|
this.buildProgressBlock();
|
|
166152
|
+
this.buildLiveOutputBlock();
|
|
165885
166153
|
this.buildContent();
|
|
165886
166154
|
this.buildSubagentBlock();
|
|
165887
166155
|
}
|
|
@@ -165911,6 +166179,21 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
165911
166179
|
this.addChild(new Text(styled, 2, 0));
|
|
165912
166180
|
}
|
|
165913
166181
|
}
|
|
166182
|
+
buildLiveOutputBlock() {
|
|
166183
|
+
if (this.result !== void 0) return;
|
|
166184
|
+
if (this.liveOutput.length === 0) return;
|
|
166185
|
+
this.addChild(new ShellExecutionComponent({
|
|
166186
|
+
result: {
|
|
166187
|
+
tool_call_id: this.toolCall.id,
|
|
166188
|
+
output: this.liveOutput,
|
|
166189
|
+
is_error: false
|
|
166190
|
+
},
|
|
166191
|
+
expanded: this.expanded,
|
|
166192
|
+
resultPreviewLines: 3,
|
|
166193
|
+
tailOutput: true,
|
|
166194
|
+
expandHint: false
|
|
166195
|
+
}));
|
|
166196
|
+
}
|
|
165914
166197
|
buildSubagentBlock() {
|
|
165915
166198
|
if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0 && this.subagentPhase === void 0 && this.backgroundTaskTerminalPhase === void 0) return;
|
|
165916
166199
|
if (this.isSingleSubagentView()) {
|
|
@@ -166060,7 +166343,6 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
166060
166343
|
if (outputLine !== void 0) this.addChild(new PrefixedWrappedLine(` ${currentTheme.fg("text", "└")} `, " ", currentTheme.fg("text", outputLine)));
|
|
166061
166344
|
}
|
|
166062
166345
|
addSubToolOutputPreview(activity) {
|
|
166063
|
-
if (activity.phase === "ongoing") return;
|
|
166064
166346
|
const output = activity.output;
|
|
166065
166347
|
if (output === void 0 || output.trim().length === 0) return;
|
|
166066
166348
|
if (activity.name !== "Bash" && !isGenericToolResult(activity.name)) return;
|
|
@@ -166069,7 +166351,8 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
166069
166351
|
expandHint: false,
|
|
166070
166352
|
isError: activity.phase === "failed",
|
|
166071
166353
|
maxLines: 3,
|
|
166072
|
-
indent: SUBAGENT_SUBTOOL_OUTPUT_INDENT
|
|
166354
|
+
indent: SUBAGENT_SUBTOOL_OUTPUT_INDENT,
|
|
166355
|
+
tail: activity.phase === "ongoing"
|
|
166073
166356
|
}));
|
|
166074
166357
|
}
|
|
166075
166358
|
getRecentSubToolActivities() {
|
|
@@ -166779,7 +167062,7 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
166779
167062
|
await handleThemeCommand(host, args);
|
|
166780
167063
|
return;
|
|
166781
167064
|
case "model":
|
|
166782
|
-
handleModelCommand(host, args);
|
|
167065
|
+
await handleModelCommand(host, args);
|
|
166783
167066
|
return;
|
|
166784
167067
|
case "provider":
|
|
166785
167068
|
await handleProviderCommand(host);
|
|
@@ -166975,6 +167258,7 @@ async function runPrompt(opts, version, io = {}) {
|
|
|
166975
167258
|
try {
|
|
166976
167259
|
await harness.ensureConfigFile();
|
|
166977
167260
|
const config = await harness.getConfig();
|
|
167261
|
+
for (const warning of (await harness.getConfigDiagnostics()).warnings) stderr.write(`Warning: ${warning}\n`);
|
|
166978
167262
|
const { session, resumed, restorePermission, telemetryModel, goalModel } = await resolvePromptSession(harness, opts, workDir, config.defaultModel, stderr, (restorePermission) => {
|
|
166979
167263
|
restorePromptSessionPermission = restorePermission;
|
|
166980
167264
|
});
|
|
@@ -167688,6 +167972,144 @@ function findBinaryRecursively(rootDir, binaryName) {
|
|
|
167688
167972
|
return null;
|
|
167689
167973
|
}
|
|
167690
167974
|
//#endregion
|
|
167975
|
+
//#region src/tui/banner/banner-provider.ts
|
|
167976
|
+
function normalizeTag(value) {
|
|
167977
|
+
if (typeof value !== "string") return null;
|
|
167978
|
+
const trimmed = value.trim();
|
|
167979
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
167980
|
+
}
|
|
167981
|
+
function normalizeText(value) {
|
|
167982
|
+
if (typeof value !== "string") return null;
|
|
167983
|
+
const trimmed = value.trim();
|
|
167984
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
167985
|
+
}
|
|
167986
|
+
function normalizeUtcDate(value) {
|
|
167987
|
+
if (value.endsWith("Z")) return value;
|
|
167988
|
+
if (/[+-]\d{2}:\d{2}$/.test(value)) return value;
|
|
167989
|
+
return `${value}Z`;
|
|
167990
|
+
}
|
|
167991
|
+
function parseDate(value) {
|
|
167992
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
167993
|
+
const normalized = normalizeUtcDate(value);
|
|
167994
|
+
const date = new Date(normalized);
|
|
167995
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
167996
|
+
}
|
|
167997
|
+
function isWithinWindow(start, end, now) {
|
|
167998
|
+
if (start !== null && now < start) return false;
|
|
167999
|
+
if (end !== null && now > end) return false;
|
|
168000
|
+
return true;
|
|
168001
|
+
}
|
|
168002
|
+
function meetsMinVersion(minVersion, clientVersion) {
|
|
168003
|
+
if (minVersion === void 0 || minVersion === null) return true;
|
|
168004
|
+
if (typeof minVersion !== "string" || minVersion.length === 0) return true;
|
|
168005
|
+
const min = valid(minVersion);
|
|
168006
|
+
const current = valid(clientVersion);
|
|
168007
|
+
if (min === null || current === null) return false;
|
|
168008
|
+
return gte(current, min);
|
|
168009
|
+
}
|
|
168010
|
+
function pickActiveBanner(json, clientVersion, now) {
|
|
168011
|
+
if (json.banner_enabled !== true) return null;
|
|
168012
|
+
if (!meetsMinVersion(json.banner_min_version, clientVersion)) return null;
|
|
168013
|
+
if (!isWithinWindow(parseDate(json.banner_start_time), parseDate(json.banner_end_time), now)) return null;
|
|
168014
|
+
const mainText = normalizeText(json.banner_maintext);
|
|
168015
|
+
if (mainText === null) return null;
|
|
168016
|
+
return {
|
|
168017
|
+
tag: normalizeTag(json.banner_title),
|
|
168018
|
+
mainText,
|
|
168019
|
+
subText: normalizeText(json.banner_subtext)
|
|
168020
|
+
};
|
|
168021
|
+
}
|
|
168022
|
+
function pickFallbackBanner(json, clientVersion, now, random) {
|
|
168023
|
+
if (json.banner_fallback_enabled !== true) return null;
|
|
168024
|
+
const list = Array.isArray(json.banner_fallback_list) ? json.banner_fallback_list : [];
|
|
168025
|
+
const candidates = [];
|
|
168026
|
+
for (const raw of list) {
|
|
168027
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
168028
|
+
const item = raw;
|
|
168029
|
+
if (item.enabled !== true) continue;
|
|
168030
|
+
if (!meetsMinVersion(item.banner_min_version, clientVersion)) continue;
|
|
168031
|
+
const mainText = normalizeText(item.banner_maintext);
|
|
168032
|
+
if (mainText === null) continue;
|
|
168033
|
+
candidates.push({
|
|
168034
|
+
tag: normalizeTag(item.banner_title),
|
|
168035
|
+
mainText,
|
|
168036
|
+
subText: normalizeText(item.banner_subtext)
|
|
168037
|
+
});
|
|
168038
|
+
}
|
|
168039
|
+
if (candidates.length === 0) return null;
|
|
168040
|
+
return candidates[Math.floor(random() * candidates.length)];
|
|
168041
|
+
}
|
|
168042
|
+
function selectBannerState(json, clientVersion, now, random) {
|
|
168043
|
+
const typed = typeof json === "object" && json !== null ? json : {};
|
|
168044
|
+
return pickActiveBanner(typed, clientVersion, now) ?? pickFallbackBanner(typed, clientVersion, now, random);
|
|
168045
|
+
}
|
|
168046
|
+
var BannerProvider = class {
|
|
168047
|
+
clientVersion;
|
|
168048
|
+
url;
|
|
168049
|
+
constructor(clientVersion, url = KIMI_CODE_TIPS_BANNER_URL) {
|
|
168050
|
+
this.clientVersion = clientVersion;
|
|
168051
|
+
this.url = url;
|
|
168052
|
+
}
|
|
168053
|
+
async load(fetchImpl = fetch) {
|
|
168054
|
+
try {
|
|
168055
|
+
const controller = new AbortController();
|
|
168056
|
+
const timeout = setTimeout(() => {
|
|
168057
|
+
controller.abort();
|
|
168058
|
+
}, 3e3);
|
|
168059
|
+
const response = await fetchImpl(this.url, { signal: controller.signal });
|
|
168060
|
+
clearTimeout(timeout);
|
|
168061
|
+
if (!response.ok) return null;
|
|
168062
|
+
return selectBannerState(await response.json(), this.clientVersion, /* @__PURE__ */ new Date(), Math.random);
|
|
168063
|
+
} catch {
|
|
168064
|
+
return null;
|
|
168065
|
+
}
|
|
168066
|
+
}
|
|
168067
|
+
};
|
|
168068
|
+
//#endregion
|
|
168069
|
+
//#region src/tui/components/chrome/banner.ts
|
|
168070
|
+
const PREFIX_STAR = "✦";
|
|
168071
|
+
const PADDING = " ";
|
|
168072
|
+
var BannerComponent = class {
|
|
168073
|
+
state;
|
|
168074
|
+
constructor(state) {
|
|
168075
|
+
this.state = state;
|
|
168076
|
+
}
|
|
168077
|
+
invalidate() {}
|
|
168078
|
+
render(width) {
|
|
168079
|
+
const main = (s) => currentTheme.boldFg("textStrong", s);
|
|
168080
|
+
const dim = (s) => currentTheme.fg("textDim", s);
|
|
168081
|
+
if (width < 1) return [""];
|
|
168082
|
+
const tagText = this.state.tag ?? "";
|
|
168083
|
+
const tagLabel = tagText.length > 0 ? `${PREFIX_STAR} ${tagText}` : "";
|
|
168084
|
+
const tagStyled = tagLabel.length > 0 ? currentTheme.boldFg("primary", tagLabel) : "";
|
|
168085
|
+
const tagDisplay = tagStyled.length > 0 ? tagStyled + PADDING : "";
|
|
168086
|
+
const tagWidth = visibleWidth(tagDisplay);
|
|
168087
|
+
const showTag = tagWidth > 0 && tagWidth < width;
|
|
168088
|
+
const bodyIndent = showTag ? " ".repeat(tagWidth) : "";
|
|
168089
|
+
const descIndent = showTag ? " ".repeat(visibleWidth(PREFIX_STAR + PADDING)) : "";
|
|
168090
|
+
const bodyContentWidth = width - (showTag ? tagWidth : 0);
|
|
168091
|
+
const descContentWidth = width - (showTag ? visibleWidth(PREFIX_STAR + PADDING) : 0);
|
|
168092
|
+
if (bodyContentWidth <= 0) return [""];
|
|
168093
|
+
const mainSegments = this.state.mainText.split("\n");
|
|
168094
|
+
const subSegments = this.state.subText ? this.state.subText.split("\n") : [];
|
|
168095
|
+
const result = [];
|
|
168096
|
+
for (let i = 0; i < mainSegments.length; i++) {
|
|
168097
|
+
const wrapped = wrapTextWithAnsi(mainSegments[i], bodyContentWidth);
|
|
168098
|
+
for (let j = 0; j < wrapped.length; j++) {
|
|
168099
|
+
const boldLine = main(wrapped[j]);
|
|
168100
|
+
if (i === 0 && j === 0 && showTag) result.push(tagDisplay + boldLine);
|
|
168101
|
+
else result.push(bodyIndent + boldLine);
|
|
168102
|
+
}
|
|
168103
|
+
}
|
|
168104
|
+
for (const sub of subSegments) {
|
|
168105
|
+
const wrapped = wrapTextWithAnsi(sub, descContentWidth <= 0 ? bodyContentWidth : descContentWidth);
|
|
168106
|
+
for (const line of wrapped) result.push(descIndent + dim(line));
|
|
168107
|
+
}
|
|
168108
|
+
result.push("");
|
|
168109
|
+
return result;
|
|
168110
|
+
}
|
|
168111
|
+
};
|
|
168112
|
+
//#endregion
|
|
167691
168113
|
//#region src/tui/components/chrome/device-code-box.ts
|
|
167692
168114
|
var DeviceCodeBoxComponent = class {
|
|
167693
168115
|
params;
|
|
@@ -169136,6 +169558,447 @@ var SessionPickerComponent = class extends Container {
|
|
|
169136
169558
|
}
|
|
169137
169559
|
};
|
|
169138
169560
|
//#endregion
|
|
169561
|
+
//#region src/tui/components/editor/file-mention-provider.ts
|
|
169562
|
+
const PATH_DELIMITERS = new Set([
|
|
169563
|
+
" ",
|
|
169564
|
+
" ",
|
|
169565
|
+
"\"",
|
|
169566
|
+
"'",
|
|
169567
|
+
"="
|
|
169568
|
+
]);
|
|
169569
|
+
const MAX_FALLBACK_SCAN = 2e3;
|
|
169570
|
+
const MAX_FALLBACK_SUGGESTIONS = 50;
|
|
169571
|
+
/**
|
|
169572
|
+
* Kimi wrapper around pi-tui's combined autocomplete provider.
|
|
169573
|
+
*
|
|
169574
|
+
* File / folder mention behavior uses pi-tui's fd-backed provider when fd is
|
|
169575
|
+
* available. While managed fd is downloading (or when it is unavailable), a
|
|
169576
|
+
* small filesystem fallback keeps basic `@` file and folder completion usable.
|
|
169577
|
+
* Ordinary path completion is still handled by pi-tui's readdir-backed path
|
|
169578
|
+
* completer. This wrapper also keeps Kimi-specific slash-command guards.
|
|
169579
|
+
*/
|
|
169580
|
+
var FileMentionProvider = class {
|
|
169581
|
+
slashCommands;
|
|
169582
|
+
workDir;
|
|
169583
|
+
fdPath;
|
|
169584
|
+
inner;
|
|
169585
|
+
constructor(slashCommands, workDir, fdPath) {
|
|
169586
|
+
this.slashCommands = slashCommands;
|
|
169587
|
+
this.workDir = workDir;
|
|
169588
|
+
this.fdPath = fdPath;
|
|
169589
|
+
const expanded = [];
|
|
169590
|
+
for (const cmd of slashCommands) {
|
|
169591
|
+
expanded.push(cmd);
|
|
169592
|
+
for (const alias of cmd.aliases ?? []) expanded.push({
|
|
169593
|
+
...cmd,
|
|
169594
|
+
name: alias
|
|
169595
|
+
});
|
|
169596
|
+
}
|
|
169597
|
+
this.inner = new CombinedAutocompleteProvider(expanded, workDir, fdPath);
|
|
169598
|
+
}
|
|
169599
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
169600
|
+
const currentLine = lines[cursorLine] ?? "";
|
|
169601
|
+
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
169602
|
+
if (shouldSuppressLeadingWhitespaceSlashPath(textBeforeCursor, options.force)) return null;
|
|
169603
|
+
if (shouldSuppressSlashArgumentCompletion(textBeforeCursor, currentLine.slice(cursorCol), options.force)) return null;
|
|
169604
|
+
const atPrefix = extractAtPrefix(textBeforeCursor);
|
|
169605
|
+
if (atPrefix !== null) {
|
|
169606
|
+
if (this.fdPath === null) return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
169607
|
+
try {
|
|
169608
|
+
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
169609
|
+
} catch {
|
|
169610
|
+
return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
169611
|
+
}
|
|
169612
|
+
}
|
|
169613
|
+
if (!options.force && textBeforeCursor.startsWith("/")) {
|
|
169614
|
+
if (textBeforeCursor.indexOf(" ") === -1) {
|
|
169615
|
+
const tokens = textBeforeCursor.slice(1).trim().split(/\s+/).filter((t) => t.length > 0);
|
|
169616
|
+
const matches = [];
|
|
169617
|
+
for (const cmd of this.slashCommands) {
|
|
169618
|
+
const nameScore = scoreTokens(tokens, cmd.name);
|
|
169619
|
+
if (nameScore !== null) {
|
|
169620
|
+
matches.push({
|
|
169621
|
+
cmd,
|
|
169622
|
+
score: nameScore,
|
|
169623
|
+
viaAlias: false,
|
|
169624
|
+
label: cmd.name
|
|
169625
|
+
});
|
|
169626
|
+
continue;
|
|
169627
|
+
}
|
|
169628
|
+
const aliases = cmd.aliases ?? [];
|
|
169629
|
+
let bestAliasScore = null;
|
|
169630
|
+
for (const alias of aliases) {
|
|
169631
|
+
const aliasScore = scoreTokens(tokens, alias);
|
|
169632
|
+
if (aliasScore !== null && (bestAliasScore === null || aliasScore < bestAliasScore)) bestAliasScore = aliasScore;
|
|
169633
|
+
}
|
|
169634
|
+
if (bestAliasScore !== null) matches.push({
|
|
169635
|
+
cmd,
|
|
169636
|
+
score: bestAliasScore,
|
|
169637
|
+
viaAlias: true,
|
|
169638
|
+
label: `${cmd.name} (${aliases.join(", ")})`
|
|
169639
|
+
});
|
|
169640
|
+
}
|
|
169641
|
+
matches.sort((a, b) => a.score - b.score || Number(a.viaAlias) - Number(b.viaAlias));
|
|
169642
|
+
if (matches.length === 0) return null;
|
|
169643
|
+
return {
|
|
169644
|
+
items: matches.map((m) => ({
|
|
169645
|
+
value: m.cmd.name,
|
|
169646
|
+
label: m.label,
|
|
169647
|
+
description: formatSlashCommandDescription(m.cmd)
|
|
169648
|
+
})),
|
|
169649
|
+
prefix: textBeforeCursor
|
|
169650
|
+
};
|
|
169651
|
+
}
|
|
169652
|
+
}
|
|
169653
|
+
try {
|
|
169654
|
+
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
169655
|
+
} catch {
|
|
169656
|
+
return null;
|
|
169657
|
+
}
|
|
169658
|
+
}
|
|
169659
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
169660
|
+
return this.inner.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
169661
|
+
}
|
|
169662
|
+
};
|
|
169663
|
+
function extractAtPrefix(text) {
|
|
169664
|
+
let tokenStart = 0;
|
|
169665
|
+
for (let i = text.length - 1; i >= 0; i -= 1) if (PATH_DELIMITERS.has(text[i] ?? "")) {
|
|
169666
|
+
tokenStart = i + 1;
|
|
169667
|
+
break;
|
|
169668
|
+
}
|
|
169669
|
+
if (text[tokenStart] !== "@") return null;
|
|
169670
|
+
return text.slice(tokenStart);
|
|
169671
|
+
}
|
|
169672
|
+
function getFsMentionSuggestions(workDir, atPrefix, signal) {
|
|
169673
|
+
if (signal.aborted) return null;
|
|
169674
|
+
const query = atPrefix.slice(1);
|
|
169675
|
+
const candidates = collectFsMentionCandidates(workDir, signal);
|
|
169676
|
+
if (candidates.length === 0 || signal.aborted) return null;
|
|
169677
|
+
const ranked = rankFsMentionCandidates(candidates, query).slice(0, MAX_FALLBACK_SUGGESTIONS);
|
|
169678
|
+
if (ranked.length === 0) return null;
|
|
169679
|
+
return {
|
|
169680
|
+
prefix: atPrefix,
|
|
169681
|
+
items: ranked.map(toMentionItem)
|
|
169682
|
+
};
|
|
169683
|
+
}
|
|
169684
|
+
function collectFsMentionCandidates(workDir, signal) {
|
|
169685
|
+
const result = [];
|
|
169686
|
+
const stack = [""];
|
|
169687
|
+
while (stack.length > 0 && result.length < MAX_FALLBACK_SCAN) {
|
|
169688
|
+
if (signal.aborted) break;
|
|
169689
|
+
const relativeDir = stack.pop() ?? "";
|
|
169690
|
+
const absoluteDir = relativeDir.length === 0 ? workDir : join$1(workDir, relativeDir);
|
|
169691
|
+
let entries;
|
|
169692
|
+
try {
|
|
169693
|
+
entries = readdirSync(absoluteDir, { withFileTypes: true });
|
|
169694
|
+
} catch {
|
|
169695
|
+
continue;
|
|
169696
|
+
}
|
|
169697
|
+
for (const entry of entries) {
|
|
169698
|
+
if (signal.aborted || result.length >= MAX_FALLBACK_SCAN) break;
|
|
169699
|
+
if (entry.name === ".git") continue;
|
|
169700
|
+
const relativePath = normalizePath(relativeDir.length === 0 ? entry.name : join$1(relativeDir, entry.name));
|
|
169701
|
+
const isSymlink = entry.isSymbolicLink();
|
|
169702
|
+
let isDirectory = entry.isDirectory();
|
|
169703
|
+
if (!isDirectory && isSymlink) try {
|
|
169704
|
+
isDirectory = statSync(join$1(workDir, relativePath)).isDirectory();
|
|
169705
|
+
} catch {}
|
|
169706
|
+
result.push({
|
|
169707
|
+
path: relativePath,
|
|
169708
|
+
isDirectory
|
|
169709
|
+
});
|
|
169710
|
+
if (isDirectory && !isSymlink) stack.push(relativePath);
|
|
169711
|
+
}
|
|
169712
|
+
}
|
|
169713
|
+
return result;
|
|
169714
|
+
}
|
|
169715
|
+
function rankFsMentionCandidates(candidates, query) {
|
|
169716
|
+
const lowerQuery = query.toLowerCase();
|
|
169717
|
+
const scored = [];
|
|
169718
|
+
for (const candidate of candidates) {
|
|
169719
|
+
const score = scoreCandidate(candidate, lowerQuery);
|
|
169720
|
+
if (score > 0) scored.push({
|
|
169721
|
+
candidate,
|
|
169722
|
+
score
|
|
169723
|
+
});
|
|
169724
|
+
}
|
|
169725
|
+
scored.sort((a, b) => {
|
|
169726
|
+
if (a.score !== b.score) return b.score - a.score;
|
|
169727
|
+
if (a.candidate.isDirectory !== b.candidate.isDirectory) return a.candidate.isDirectory ? -1 : 1;
|
|
169728
|
+
return a.candidate.path.localeCompare(b.candidate.path);
|
|
169729
|
+
});
|
|
169730
|
+
return scored.map((entry) => entry.candidate);
|
|
169731
|
+
}
|
|
169732
|
+
function scoreCandidate(candidate, lowerQuery) {
|
|
169733
|
+
if (lowerQuery.length === 0) {
|
|
169734
|
+
const depthPenalty = candidate.path.split("/").length - 1;
|
|
169735
|
+
return (candidate.isDirectory ? 120 : 100) - depthPenalty;
|
|
169736
|
+
}
|
|
169737
|
+
const lowerPath = candidate.path.toLowerCase();
|
|
169738
|
+
const lowerBase = basename$1(candidate.path).toLowerCase();
|
|
169739
|
+
let score = 0;
|
|
169740
|
+
if (lowerBase === lowerQuery) score = 100;
|
|
169741
|
+
else if (lowerBase.startsWith(lowerQuery)) score = 80;
|
|
169742
|
+
else if (lowerBase.includes(lowerQuery)) score = 50;
|
|
169743
|
+
else if (lowerPath.includes(lowerQuery)) score = 30;
|
|
169744
|
+
if (candidate.isDirectory && score > 0) score += 10;
|
|
169745
|
+
return score;
|
|
169746
|
+
}
|
|
169747
|
+
function toMentionItem(candidate) {
|
|
169748
|
+
const valuePath = candidate.isDirectory ? `${candidate.path}/` : candidate.path;
|
|
169749
|
+
return {
|
|
169750
|
+
value: valuePath.includes(" ") ? `@"${valuePath}"` : `@${valuePath}`,
|
|
169751
|
+
label: `${basename$1(candidate.path)}${candidate.isDirectory ? "/" : ""}`,
|
|
169752
|
+
description: valuePath
|
|
169753
|
+
};
|
|
169754
|
+
}
|
|
169755
|
+
function normalizePath(path) {
|
|
169756
|
+
return path.replaceAll("\\", "/");
|
|
169757
|
+
}
|
|
169758
|
+
function shouldSuppressLeadingWhitespaceSlashPath(textBeforeCursor, force) {
|
|
169759
|
+
if (force === true) return false;
|
|
169760
|
+
if (textBeforeCursor.startsWith("/")) return false;
|
|
169761
|
+
return textBeforeCursor.trimStart().startsWith("/");
|
|
169762
|
+
}
|
|
169763
|
+
function shouldSuppressSlashArgumentCompletion(textBeforeCursor, textAfterCursor, force) {
|
|
169764
|
+
if (force === true) return false;
|
|
169765
|
+
if (!textBeforeCursor.startsWith("/")) return false;
|
|
169766
|
+
if (!textBeforeCursor.includes(" ")) return false;
|
|
169767
|
+
return textAfterCursor.trimStart().length > 0;
|
|
169768
|
+
}
|
|
169769
|
+
/**
|
|
169770
|
+
* All tokens must fuzzy-match `text`; returns the summed score, or null when
|
|
169771
|
+
* any token misses. An empty token list matches everything with score 0.
|
|
169772
|
+
* Mirrors pi-tui fuzzyFilter's token semantics — keep in sync if it changes.
|
|
169773
|
+
*/
|
|
169774
|
+
function scoreTokens(tokens, text) {
|
|
169775
|
+
let score = 0;
|
|
169776
|
+
for (const token of tokens) {
|
|
169777
|
+
const m = fuzzyMatch(token, text);
|
|
169778
|
+
if (!m.matches) return null;
|
|
169779
|
+
score += m.score;
|
|
169780
|
+
}
|
|
169781
|
+
return score;
|
|
169782
|
+
}
|
|
169783
|
+
/**
|
|
169784
|
+
* Mirrors CombinedAutocompleteProvider's description rendering so the
|
|
169785
|
+
* intercepted name completion keeps showing the argument hint.
|
|
169786
|
+
*/
|
|
169787
|
+
function formatSlashCommandDescription(cmd) {
|
|
169788
|
+
const desc = cmd.description ?? "";
|
|
169789
|
+
return (cmd.argumentHint ? desc ? `${cmd.argumentHint} — ${desc}` : cmd.argumentHint : desc) || void 0;
|
|
169790
|
+
}
|
|
169791
|
+
//#endregion
|
|
169792
|
+
//#region src/tui/components/messages/goal-markers.ts
|
|
169793
|
+
/**
|
|
169794
|
+
* Low-profile transcript markers for the autonomous goal loop.
|
|
169795
|
+
*
|
|
169796
|
+
* Lifecycle changes (paused / resumed / cancelled) and `no_progress` verdicts
|
|
169797
|
+
* render as a single dim line — `◦ Goal paused` — that expands (ctrl+o, shared
|
|
169798
|
+
* with tool output) to show the reason when there is one. Terminal outcomes use
|
|
169799
|
+
* the richer completion card (the `/goal` box), not this marker.
|
|
169800
|
+
*/
|
|
169801
|
+
const HEAD_INDENT = " ";
|
|
169802
|
+
const DETAIL_INDENT = " ";
|
|
169803
|
+
var GoalMarkerComponent = class {
|
|
169804
|
+
headline;
|
|
169805
|
+
detail;
|
|
169806
|
+
accentToken;
|
|
169807
|
+
expanded = false;
|
|
169808
|
+
marker;
|
|
169809
|
+
textToken;
|
|
169810
|
+
expandable;
|
|
169811
|
+
indent;
|
|
169812
|
+
leadingBlank;
|
|
169813
|
+
constructor(headline, detail, accentToken, options = {}) {
|
|
169814
|
+
this.headline = headline;
|
|
169815
|
+
this.detail = detail;
|
|
169816
|
+
this.accentToken = accentToken;
|
|
169817
|
+
this.marker = options.marker ?? "◦";
|
|
169818
|
+
this.textToken = options.textToken ?? "textDim";
|
|
169819
|
+
this.expandable = options.expandable ?? true;
|
|
169820
|
+
this.indent = options.indent ?? HEAD_INDENT;
|
|
169821
|
+
this.leadingBlank = options.leadingBlank ?? false;
|
|
169822
|
+
}
|
|
169823
|
+
invalidate() {}
|
|
169824
|
+
setExpanded(expanded) {
|
|
169825
|
+
this.expanded = expanded;
|
|
169826
|
+
}
|
|
169827
|
+
render(width) {
|
|
169828
|
+
const dot = currentTheme.fg(this.accentToken, this.marker);
|
|
169829
|
+
const head = currentTheme.fg(this.textToken, this.headline);
|
|
169830
|
+
if (!(this.detail !== void 0 && this.detail.length > 0)) return this.clampToWidth([`${this.indent}${dot} ${head}`], width);
|
|
169831
|
+
if (!this.expandable) return this.clampToWidth([`${this.indent}${dot} ${head}`], width);
|
|
169832
|
+
if (!this.expanded) return this.clampToWidth([`${this.indent}${dot} ${head} ${currentTheme.fg("textMuted", "(ctrl+o)")}`], width);
|
|
169833
|
+
const out = [`${this.indent}${dot} ${head}`];
|
|
169834
|
+
const wrapWidth = Math.max(20, width - 4);
|
|
169835
|
+
for (const line of wrap(this.detail, wrapWidth)) out.push(DETAIL_INDENT + currentTheme.fg("textDim", line));
|
|
169836
|
+
return this.clampToWidth(out, width);
|
|
169837
|
+
}
|
|
169838
|
+
clampToWidth(lines, width) {
|
|
169839
|
+
const withBlank = this.withLeadingBlank(lines);
|
|
169840
|
+
if (width <= 0) return withBlank.map(() => "");
|
|
169841
|
+
return withBlank.map((line) => truncateToWidth(line, width));
|
|
169842
|
+
}
|
|
169843
|
+
withLeadingBlank(lines) {
|
|
169844
|
+
return this.leadingBlank ? ["", ...lines] : lines;
|
|
169845
|
+
}
|
|
169846
|
+
};
|
|
169847
|
+
/**
|
|
169848
|
+
* Builds a marker for a lifecycle change (paused / resumed / blocked), or `null`
|
|
169849
|
+
* when the change should be silent (a `completion` change posts its own message,
|
|
169850
|
+
* not a marker). `expanded` seeds the initial ctrl+o state.
|
|
169851
|
+
*/
|
|
169852
|
+
function buildGoalMarker(change, expanded, actor) {
|
|
169853
|
+
const spec = markerSpec(change, actor);
|
|
169854
|
+
if (spec === null) return null;
|
|
169855
|
+
const marker = new GoalMarkerComponent(spec.headline, spec.detail ?? change.reason, spec.accentToken, spec.options);
|
|
169856
|
+
marker.setExpanded(expanded);
|
|
169857
|
+
return marker;
|
|
169858
|
+
}
|
|
169859
|
+
function markerSpec(change, actor) {
|
|
169860
|
+
if (change.kind === "lifecycle") switch (change.status) {
|
|
169861
|
+
case "paused": return prominentMarker(pausedHeadline(change.reason, actor), "warning");
|
|
169862
|
+
case "active": return prominentMarker(resumedHeadline(actor), "primary");
|
|
169863
|
+
case "blocked": return {
|
|
169864
|
+
headline: "Goal blocked",
|
|
169865
|
+
accentToken: "warning"
|
|
169866
|
+
};
|
|
169867
|
+
default: return null;
|
|
169868
|
+
}
|
|
169869
|
+
return null;
|
|
169870
|
+
}
|
|
169871
|
+
function prominentMarker(headline, accentToken) {
|
|
169872
|
+
return {
|
|
169873
|
+
headline,
|
|
169874
|
+
accentToken,
|
|
169875
|
+
detail: void 0,
|
|
169876
|
+
options: {
|
|
169877
|
+
marker: STATUS_BULLET.trimEnd(),
|
|
169878
|
+
textToken: accentToken,
|
|
169879
|
+
expandable: false,
|
|
169880
|
+
indent: "",
|
|
169881
|
+
leadingBlank: true
|
|
169882
|
+
}
|
|
169883
|
+
};
|
|
169884
|
+
}
|
|
169885
|
+
function pausedHeadline(reason, actor) {
|
|
169886
|
+
if (reason === "Paused after interruption") return "Goal paused due to user's interruption";
|
|
169887
|
+
if (actor === "user") return "Goal paused by the user.";
|
|
169888
|
+
if (reason?.startsWith("Paused ") === true) return `Goal ${lowercaseFirst(reason)}`;
|
|
169889
|
+
if (reason !== void 0 && reason.length > 0) return `Goal paused: ${reason}`;
|
|
169890
|
+
if (actor === "model") return "Goal paused by the agent.";
|
|
169891
|
+
return "Goal paused";
|
|
169892
|
+
}
|
|
169893
|
+
function resumedHeadline(actor) {
|
|
169894
|
+
if (actor === "user") return "Goal resumed by the user.";
|
|
169895
|
+
if (actor === "model") return "Goal resumed by the agent.";
|
|
169896
|
+
return "Goal resumed";
|
|
169897
|
+
}
|
|
169898
|
+
function lowercaseFirst(text) {
|
|
169899
|
+
return text.length === 0 ? text : `${text[0].toLowerCase()}${text.slice(1)}`;
|
|
169900
|
+
}
|
|
169901
|
+
function wrap(text, width) {
|
|
169902
|
+
const words = text.replaceAll(/\s+/g, " ").trim().split(" ");
|
|
169903
|
+
const lines = [];
|
|
169904
|
+
let current = "";
|
|
169905
|
+
for (const word of words) {
|
|
169906
|
+
const candidate = current.length === 0 ? word : `${current} ${word}`;
|
|
169907
|
+
if (candidate.length > width && current.length > 0) {
|
|
169908
|
+
lines.push(current);
|
|
169909
|
+
current = word;
|
|
169910
|
+
} else current = candidate;
|
|
169911
|
+
}
|
|
169912
|
+
if (current.length > 0) lines.push(current);
|
|
169913
|
+
return lines.length > 0 ? lines : [""];
|
|
169914
|
+
}
|
|
169915
|
+
//#endregion
|
|
169916
|
+
//#region src/tui/components/messages/status-message.ts
|
|
169917
|
+
var StatusMessageComponent = class extends Container {
|
|
169918
|
+
textComponent;
|
|
169919
|
+
content;
|
|
169920
|
+
color;
|
|
169921
|
+
constructor(content, color) {
|
|
169922
|
+
super();
|
|
169923
|
+
this.content = content;
|
|
169924
|
+
this.color = color;
|
|
169925
|
+
const text = color === void 0 ? currentTheme.fg("textDim", content) : currentTheme.fg(color, content);
|
|
169926
|
+
this.textComponent = new Text(` ${text}`, 0, 0);
|
|
169927
|
+
this.addChild(this.textComponent);
|
|
169928
|
+
}
|
|
169929
|
+
invalidate() {
|
|
169930
|
+
const text = this.color === void 0 ? currentTheme.fg("textDim", this.content) : currentTheme.fg(this.color, this.content);
|
|
169931
|
+
this.textComponent.setText(` ${text}`);
|
|
169932
|
+
super.invalidate();
|
|
169933
|
+
}
|
|
169934
|
+
};
|
|
169935
|
+
var NoticeMessageComponent = class extends Container {
|
|
169936
|
+
titleText;
|
|
169937
|
+
detailText;
|
|
169938
|
+
title;
|
|
169939
|
+
detail;
|
|
169940
|
+
constructor(title, detail) {
|
|
169941
|
+
super();
|
|
169942
|
+
this.title = title;
|
|
169943
|
+
this.detail = detail;
|
|
169944
|
+
this.addChild(new Spacer(1));
|
|
169945
|
+
this.titleText = new Text(` ${currentTheme.fg("textStrong", title)}`, 0, 0);
|
|
169946
|
+
this.addChild(this.titleText);
|
|
169947
|
+
if (detail !== void 0 && detail.length > 0) {
|
|
169948
|
+
this.detailText = new Text(` ${currentTheme.fg("textDim", detail)}`, 0, 0);
|
|
169949
|
+
this.addChild(this.detailText);
|
|
169950
|
+
}
|
|
169951
|
+
}
|
|
169952
|
+
invalidate() {
|
|
169953
|
+
this.titleText.setText(` ${currentTheme.fg("textStrong", this.title)}`);
|
|
169954
|
+
if (this.detailText !== void 0 && this.detail !== void 0) this.detailText.setText(` ${currentTheme.fg("textDim", this.detail)}`);
|
|
169955
|
+
super.invalidate();
|
|
169956
|
+
}
|
|
169957
|
+
};
|
|
169958
|
+
//#endregion
|
|
169959
|
+
//#region src/tui/components/panes/activity-pane.ts
|
|
169960
|
+
var ActivityPaneComponent = class extends Container {
|
|
169961
|
+
constructor(options) {
|
|
169962
|
+
super();
|
|
169963
|
+
if (options.mode === "waiting" || options.mode === "tool") {
|
|
169964
|
+
if (options.spinner !== void 0) {
|
|
169965
|
+
this.addChild(new Spacer(1));
|
|
169966
|
+
this.addChild(options.spinner);
|
|
169967
|
+
}
|
|
169968
|
+
return;
|
|
169969
|
+
}
|
|
169970
|
+
if (options.mode === "composing" && options.spinner !== void 0) {
|
|
169971
|
+
this.addChild(new Spacer(1));
|
|
169972
|
+
this.addChild(options.spinner);
|
|
169973
|
+
}
|
|
169974
|
+
}
|
|
169975
|
+
};
|
|
169976
|
+
//#endregion
|
|
169977
|
+
//#region src/tui/components/panes/queue-pane.ts
|
|
169978
|
+
const ELLIPSIS$3 = "…";
|
|
169979
|
+
var QueuePaneComponent = class extends Container {
|
|
169980
|
+
messages;
|
|
169981
|
+
hint;
|
|
169982
|
+
constructor(options) {
|
|
169983
|
+
super();
|
|
169984
|
+
this.messages = options.messages;
|
|
169985
|
+
if (options.messages.length > 0) this.hint = options.isCompacting && !options.isStreaming ? " ↑ to edit · will send after compaction" : !options.canSteerImmediately ? " ↑ to edit · will send after current task" : " ↑ to edit · ctrl-s to steer immediately";
|
|
169986
|
+
}
|
|
169987
|
+
render(width) {
|
|
169988
|
+
const accent = (text) => currentTheme.fg("accent", text);
|
|
169989
|
+
const dim = (text) => currentTheme.fg("textDim", text);
|
|
169990
|
+
const lines = [];
|
|
169991
|
+
for (const item of this.messages) {
|
|
169992
|
+
const singleLine = item.text.replaceAll(/\s+/g, " ").trim();
|
|
169993
|
+
const prefix = ` ❯ `;
|
|
169994
|
+
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS$3);
|
|
169995
|
+
lines.push(accent(prefix + truncated));
|
|
169996
|
+
}
|
|
169997
|
+
if (this.hint !== void 0) lines.push(dim(truncateToWidth(this.hint, width, ELLIPSIS$3)));
|
|
169998
|
+
return lines;
|
|
169999
|
+
}
|
|
170000
|
+
};
|
|
170001
|
+
//#endregion
|
|
169139
170002
|
//#region src/tui/utils/refresh-providers.ts
|
|
169140
170003
|
function readCustomRegistrySource(provider) {
|
|
169141
170004
|
const source = provider.source;
|
|
@@ -169152,6 +170015,26 @@ function readCustomRegistrySource(provider) {
|
|
|
169152
170015
|
apiKey
|
|
169153
170016
|
};
|
|
169154
170017
|
}
|
|
170018
|
+
function customRegistrySourceKey(source) {
|
|
170019
|
+
return JSON.stringify([source.url]);
|
|
170020
|
+
}
|
|
170021
|
+
function customRegistrySourceCredentialKey(source) {
|
|
170022
|
+
return JSON.stringify([source.url, source.apiKey]);
|
|
170023
|
+
}
|
|
170024
|
+
async function fetchCustomRegistryFromSources(sources) {
|
|
170025
|
+
let lastError;
|
|
170026
|
+
for (const source of sources) try {
|
|
170027
|
+
return {
|
|
170028
|
+
entries: await fetchCustomRegistry(source),
|
|
170029
|
+
source
|
|
170030
|
+
};
|
|
170031
|
+
} catch (error) {
|
|
170032
|
+
lastError = error;
|
|
170033
|
+
}
|
|
170034
|
+
if (lastError instanceof Error) throw lastError;
|
|
170035
|
+
if (typeof lastError === "string") throw new Error(lastError);
|
|
170036
|
+
throw new Error("No custom registry sources configured.");
|
|
170037
|
+
}
|
|
169155
170038
|
function asManaged(config) {
|
|
169156
170039
|
return config;
|
|
169157
170040
|
}
|
|
@@ -169202,6 +170085,12 @@ function providerModelSnapshot(config, providerId, aliasKeys) {
|
|
|
169202
170085
|
function providerModelsEqual(config, nextConfig, providerId, aliasKeys) {
|
|
169203
170086
|
return providerModelSnapshot(config, providerId, aliasKeys) === providerModelSnapshot(nextConfig, providerId, aliasKeys);
|
|
169204
170087
|
}
|
|
170088
|
+
function providerConfigSnapshot(config, providerId) {
|
|
170089
|
+
return JSON.stringify(config.providers[providerId] ?? null);
|
|
170090
|
+
}
|
|
170091
|
+
function providerConfigEqual(config, nextConfig, providerId) {
|
|
170092
|
+
return providerConfigSnapshot(config, providerId) === providerConfigSnapshot(nextConfig, providerId);
|
|
170093
|
+
}
|
|
169205
170094
|
function providerRefreshAliasKeys(config, nextConfig, providerId, aliasPrefix) {
|
|
169206
170095
|
const keys = generatedProviderAliasKeys(config, providerId, aliasPrefix);
|
|
169207
170096
|
for (const key of providerAliasKeys(nextConfig, providerId)) keys.add(key);
|
|
@@ -169233,6 +170122,9 @@ function clampDanglingDefault(config) {
|
|
|
169233
170122
|
config.defaultThinking = void 0;
|
|
169234
170123
|
}
|
|
169235
170124
|
}
|
|
170125
|
+
function clearDefaultThinkingWhenDefaultRemoved(config, previousDefaultModel) {
|
|
170126
|
+
if (previousDefaultModel !== void 0 && config.defaultModel === void 0) config.defaultThinking = void 0;
|
|
170127
|
+
}
|
|
169236
170128
|
function pickDefaultModel(config, providerId, models) {
|
|
169237
170129
|
const firstModel = models[0];
|
|
169238
170130
|
if (firstModel === void 0) return "";
|
|
@@ -169246,10 +170138,11 @@ function pickDefaultModel(config, providerId, models) {
|
|
|
169246
170138
|
}
|
|
169247
170139
|
return firstModel.id;
|
|
169248
170140
|
}
|
|
169249
|
-
async function refreshAllProviderModels(host) {
|
|
170141
|
+
async function refreshAllProviderModels(host, options = {}) {
|
|
169250
170142
|
const changed = [];
|
|
169251
170143
|
const unchanged = [];
|
|
169252
170144
|
const failed = [];
|
|
170145
|
+
const scope = options.scope ?? "all";
|
|
169253
170146
|
let config = await host.getConfig();
|
|
169254
170147
|
const managedProvider = config.providers[KIMI_CODE_PROVIDER_NAME];
|
|
169255
170148
|
if (managedProvider !== void 0 && managedProvider.type === "kimi" && managedProvider.oauth !== void 0) try {
|
|
@@ -169274,6 +170167,7 @@ async function refreshAllProviderModels(host) {
|
|
|
169274
170167
|
restoreProviderAliases(next, preserveUserProviderAliases(config, KIMI_CODE_PROVIDER_NAME, refreshedAliasKeys));
|
|
169275
170168
|
restoreDefaultSelection(next, config.defaultModel, config.defaultThinking);
|
|
169276
170169
|
clampDanglingDefault(next);
|
|
170170
|
+
clearDefaultThinkingWhenDefaultRemoved(next, config.defaultModel);
|
|
169277
170171
|
if (providerModelsEqual(config, next, "managed:kimi-code", refreshedAliasKeys)) unchanged.push(KIMI_CODE_PROVIDER_NAME);
|
|
169278
170172
|
else {
|
|
169279
170173
|
const { added, removed } = computeChanges(collectModelIdsForAliases(config, refreshedAliasKeys), collectModelIdsForAliases(next, refreshedAliasKeys));
|
|
@@ -169298,6 +170192,11 @@ async function refreshAllProviderModels(host) {
|
|
|
169298
170192
|
reason: error instanceof Error ? error.message : String(error)
|
|
169299
170193
|
});
|
|
169300
170194
|
}
|
|
170195
|
+
if (scope === "oauth") return {
|
|
170196
|
+
changed,
|
|
170197
|
+
unchanged,
|
|
170198
|
+
failed
|
|
170199
|
+
};
|
|
169301
170200
|
const openPlatformIds = Object.keys(config.providers).filter((id) => isOpenPlatformId(id));
|
|
169302
170201
|
for (const providerId of openPlatformIds) {
|
|
169303
170202
|
const platform = getOpenPlatformById(providerId);
|
|
@@ -169325,6 +170224,7 @@ async function refreshAllProviderModels(host) {
|
|
|
169325
170224
|
restoreProviderAliases(next, preserveUserProviderAliases(config, providerId, refreshedAliasKeys));
|
|
169326
170225
|
restoreDefaultSelection(next, config.defaultModel, config.defaultThinking);
|
|
169327
170226
|
clampDanglingDefault(next);
|
|
170227
|
+
clearDefaultThinkingWhenDefaultRemoved(next, config.defaultModel);
|
|
169328
170228
|
if (providerModelsEqual(config, next, providerId, refreshedAliasKeys)) unchanged.push(providerId);
|
|
169329
170229
|
else {
|
|
169330
170230
|
const { added, removed } = computeChanges(collectModelIdsForAliases(config, refreshedAliasKeys), collectModelIdsForAliases(next, refreshedAliasKeys));
|
|
@@ -169355,26 +170255,55 @@ async function refreshAllProviderModels(host) {
|
|
|
169355
170255
|
if (isOpenPlatformId(providerId)) continue;
|
|
169356
170256
|
const source = readCustomRegistrySource(providerConfig);
|
|
169357
170257
|
if (source === void 0) continue;
|
|
169358
|
-
const key =
|
|
170258
|
+
const key = customRegistrySourceKey(source);
|
|
170259
|
+
const sourceKey = customRegistrySourceCredentialKey(source);
|
|
169359
170260
|
const entry = customSources.get(key);
|
|
169360
|
-
if (entry !== void 0)
|
|
169361
|
-
|
|
169362
|
-
|
|
170261
|
+
if (entry !== void 0) {
|
|
170262
|
+
if (!entry.sourceKeys.has(sourceKey)) {
|
|
170263
|
+
entry.sources.push(source);
|
|
170264
|
+
entry.sourceKeys.add(sourceKey);
|
|
170265
|
+
}
|
|
170266
|
+
entry.providerIds.push(providerId);
|
|
170267
|
+
} else customSources.set(key, {
|
|
170268
|
+
sources: [source],
|
|
170269
|
+
sourceKeys: new Set([sourceKey]),
|
|
169363
170270
|
providerIds: [providerId]
|
|
169364
170271
|
});
|
|
169365
170272
|
}
|
|
169366
|
-
for (const {
|
|
169367
|
-
const entries = await
|
|
170273
|
+
for (const { sources, providerIds } of customSources.values()) try {
|
|
170274
|
+
const { entries, source } = await fetchCustomRegistryFromSources(sources);
|
|
169368
170275
|
const next = structuredClone(config);
|
|
169369
170276
|
const changedProviders = [];
|
|
169370
|
-
|
|
169371
|
-
|
|
169372
|
-
|
|
170277
|
+
const providersToRemoveBeforeSet = /* @__PURE__ */ new Set();
|
|
170278
|
+
let hasUnreportedConfigChange = false;
|
|
170279
|
+
const remoteEntries = Object.values(entries);
|
|
170280
|
+
const remoteEntriesByProviderId = new Map(remoteEntries.map((entry) => [entry.id, entry]));
|
|
170281
|
+
const providerIdsToSync = new Set(providerIds);
|
|
170282
|
+
for (const entry of remoteEntries) providerIdsToSync.add(entry.id);
|
|
170283
|
+
for (const providerId of providerIdsToSync) {
|
|
170284
|
+
const entry = remoteEntriesByProviderId.get(providerId);
|
|
170285
|
+
if (entry === void 0) {
|
|
170286
|
+
const oldIds = collectModelIdsForAliases(config, providerAliasKeys(config, providerId));
|
|
170287
|
+
removeCustomRegistryProvider(asManaged(next), providerId);
|
|
170288
|
+
changedProviders.push({
|
|
170289
|
+
providerId,
|
|
170290
|
+
providerName: providerId,
|
|
170291
|
+
added: 0,
|
|
170292
|
+
removed: oldIds.size
|
|
170293
|
+
});
|
|
170294
|
+
providersToRemoveBeforeSet.add(providerId);
|
|
170295
|
+
continue;
|
|
170296
|
+
}
|
|
170297
|
+
const existed = config.providers[providerId] !== void 0;
|
|
169373
170298
|
applyCustomRegistryProvider(asManaged(next), entry, source);
|
|
169374
170299
|
const refreshedAliasKeys = providerRefreshAliasKeys(config, next, providerId, `${providerId}/`);
|
|
169375
|
-
restoreProviderAliases(next, preserveUserProviderAliases(config, providerId, refreshedAliasKeys));
|
|
169376
|
-
if (providerModelsEqual(config, next, providerId, refreshedAliasKeys)) unchanged.push(providerId);
|
|
169377
|
-
else {
|
|
170300
|
+
if (existed) restoreProviderAliases(next, preserveUserProviderAliases(config, providerId, refreshedAliasKeys));
|
|
170301
|
+
if (existed && providerModelsEqual(config, next, providerId, refreshedAliasKeys) && providerConfigEqual(config, next, providerId)) unchanged.push(providerId);
|
|
170302
|
+
else if (existed && providerModelsEqual(config, next, providerId, refreshedAliasKeys)) {
|
|
170303
|
+
unchanged.push(providerId);
|
|
170304
|
+
providersToRemoveBeforeSet.add(providerId);
|
|
170305
|
+
hasUnreportedConfigChange = true;
|
|
170306
|
+
} else {
|
|
169378
170307
|
const { added, removed } = computeChanges(collectModelIdsForAliases(config, refreshedAliasKeys), collectModelIdsForAliases(next, refreshedAliasKeys));
|
|
169379
170308
|
changedProviders.push({
|
|
169380
170309
|
providerId,
|
|
@@ -169382,19 +170311,26 @@ async function refreshAllProviderModels(host) {
|
|
|
169382
170311
|
added,
|
|
169383
170312
|
removed
|
|
169384
170313
|
});
|
|
170314
|
+
if (existed) providersToRemoveBeforeSet.add(providerId);
|
|
169385
170315
|
}
|
|
169386
170316
|
}
|
|
169387
|
-
if (changedProviders.length > 0) {
|
|
170317
|
+
if (changedProviders.length > 0 || hasUnreportedConfigChange) {
|
|
169388
170318
|
restoreDefaultSelection(next, config.defaultModel, config.defaultThinking);
|
|
169389
170319
|
clampDanglingDefault(next);
|
|
169390
|
-
|
|
170320
|
+
clearDefaultThinkingWhenDefaultRemoved(next, config.defaultModel);
|
|
170321
|
+
for (const providerId of providersToRemoveBeforeSet) await host.removeProvider(providerId);
|
|
169391
170322
|
config = await host.setConfig({
|
|
169392
170323
|
providers: next.providers,
|
|
169393
170324
|
models: next.models,
|
|
169394
170325
|
defaultModel: next.defaultModel,
|
|
169395
170326
|
defaultThinking: next.defaultThinking
|
|
169396
170327
|
});
|
|
169397
|
-
for (const change of changedProviders) changed.push(
|
|
170328
|
+
for (const change of changedProviders) changed.push({
|
|
170329
|
+
providerId: change.providerId,
|
|
170330
|
+
providerName: change.providerName,
|
|
170331
|
+
added: change.added,
|
|
170332
|
+
removed: change.removed
|
|
170333
|
+
});
|
|
169398
170334
|
}
|
|
169399
170335
|
} catch (error) {
|
|
169400
170336
|
for (const providerId of providerIds) failed.push({
|
|
@@ -169515,6 +170451,12 @@ var AuthFlowController = class {
|
|
|
169515
170451
|
* and returned instead of thrown.
|
|
169516
170452
|
*/
|
|
169517
170453
|
async refreshProviderModels() {
|
|
170454
|
+
return this.refreshProviderModelsWithScope("all");
|
|
170455
|
+
}
|
|
170456
|
+
async refreshOAuthProviderModels() {
|
|
170457
|
+
return this.refreshProviderModelsWithScope("oauth");
|
|
170458
|
+
}
|
|
170459
|
+
async refreshProviderModelsWithScope(scope) {
|
|
169518
170460
|
const { host } = this;
|
|
169519
170461
|
const result = await refreshAllProviderModels({
|
|
169520
170462
|
getConfig: () => host.harness.getConfig({ reload: true }),
|
|
@@ -169523,7 +170465,7 @@ var AuthFlowController = class {
|
|
|
169523
170465
|
resolveOAuthToken: async (providerName, oauthRef) => {
|
|
169524
170466
|
return host.harness.auth.resolveOAuthTokenProvider(providerName, oauthRef).getAccessToken();
|
|
169525
170467
|
}
|
|
169526
|
-
});
|
|
170468
|
+
}, { scope });
|
|
169527
170469
|
if (result.changed.length > 0) await this.refreshAvailableModels();
|
|
169528
170470
|
return result;
|
|
169529
170471
|
}
|
|
@@ -170991,173 +171933,6 @@ var EditorKeyboardController = class {
|
|
|
170991
171933
|
}
|
|
170992
171934
|
};
|
|
170993
171935
|
//#endregion
|
|
170994
|
-
//#region src/tui/components/messages/goal-markers.ts
|
|
170995
|
-
/**
|
|
170996
|
-
* Low-profile transcript markers for the autonomous goal loop.
|
|
170997
|
-
*
|
|
170998
|
-
* Lifecycle changes (paused / resumed / cancelled) and `no_progress` verdicts
|
|
170999
|
-
* render as a single dim line — `◦ Goal paused` — that expands (ctrl+o, shared
|
|
171000
|
-
* with tool output) to show the reason when there is one. Terminal outcomes use
|
|
171001
|
-
* the richer completion card (the `/goal` box), not this marker.
|
|
171002
|
-
*/
|
|
171003
|
-
const HEAD_INDENT = " ";
|
|
171004
|
-
const DETAIL_INDENT = " ";
|
|
171005
|
-
var GoalMarkerComponent = class {
|
|
171006
|
-
headline;
|
|
171007
|
-
detail;
|
|
171008
|
-
accentToken;
|
|
171009
|
-
expanded = false;
|
|
171010
|
-
marker;
|
|
171011
|
-
textToken;
|
|
171012
|
-
expandable;
|
|
171013
|
-
indent;
|
|
171014
|
-
leadingBlank;
|
|
171015
|
-
constructor(headline, detail, accentToken, options = {}) {
|
|
171016
|
-
this.headline = headline;
|
|
171017
|
-
this.detail = detail;
|
|
171018
|
-
this.accentToken = accentToken;
|
|
171019
|
-
this.marker = options.marker ?? "◦";
|
|
171020
|
-
this.textToken = options.textToken ?? "textDim";
|
|
171021
|
-
this.expandable = options.expandable ?? true;
|
|
171022
|
-
this.indent = options.indent ?? HEAD_INDENT;
|
|
171023
|
-
this.leadingBlank = options.leadingBlank ?? false;
|
|
171024
|
-
}
|
|
171025
|
-
invalidate() {}
|
|
171026
|
-
setExpanded(expanded) {
|
|
171027
|
-
this.expanded = expanded;
|
|
171028
|
-
}
|
|
171029
|
-
render(width) {
|
|
171030
|
-
const dot = currentTheme.fg(this.accentToken, this.marker);
|
|
171031
|
-
const head = currentTheme.fg(this.textToken, this.headline);
|
|
171032
|
-
if (!(this.detail !== void 0 && this.detail.length > 0)) return this.clampToWidth([`${this.indent}${dot} ${head}`], width);
|
|
171033
|
-
if (!this.expandable) return this.clampToWidth([`${this.indent}${dot} ${head}`], width);
|
|
171034
|
-
if (!this.expanded) return this.clampToWidth([`${this.indent}${dot} ${head} ${currentTheme.fg("textMuted", "(ctrl+o)")}`], width);
|
|
171035
|
-
const out = [`${this.indent}${dot} ${head}`];
|
|
171036
|
-
const wrapWidth = Math.max(20, width - 4);
|
|
171037
|
-
for (const line of wrap(this.detail, wrapWidth)) out.push(DETAIL_INDENT + currentTheme.fg("textDim", line));
|
|
171038
|
-
return this.clampToWidth(out, width);
|
|
171039
|
-
}
|
|
171040
|
-
clampToWidth(lines, width) {
|
|
171041
|
-
const withBlank = this.withLeadingBlank(lines);
|
|
171042
|
-
if (width <= 0) return withBlank.map(() => "");
|
|
171043
|
-
return withBlank.map((line) => truncateToWidth(line, width));
|
|
171044
|
-
}
|
|
171045
|
-
withLeadingBlank(lines) {
|
|
171046
|
-
return this.leadingBlank ? ["", ...lines] : lines;
|
|
171047
|
-
}
|
|
171048
|
-
};
|
|
171049
|
-
/**
|
|
171050
|
-
* Builds a marker for a lifecycle change (paused / resumed / blocked), or `null`
|
|
171051
|
-
* when the change should be silent (a `completion` change posts its own message,
|
|
171052
|
-
* not a marker). `expanded` seeds the initial ctrl+o state.
|
|
171053
|
-
*/
|
|
171054
|
-
function buildGoalMarker(change, expanded, actor) {
|
|
171055
|
-
const spec = markerSpec(change, actor);
|
|
171056
|
-
if (spec === null) return null;
|
|
171057
|
-
const marker = new GoalMarkerComponent(spec.headline, spec.detail ?? change.reason, spec.accentToken, spec.options);
|
|
171058
|
-
marker.setExpanded(expanded);
|
|
171059
|
-
return marker;
|
|
171060
|
-
}
|
|
171061
|
-
function markerSpec(change, actor) {
|
|
171062
|
-
if (change.kind === "lifecycle") switch (change.status) {
|
|
171063
|
-
case "paused": return prominentMarker(pausedHeadline(change.reason, actor), "warning");
|
|
171064
|
-
case "active": return prominentMarker(resumedHeadline(actor), "primary");
|
|
171065
|
-
case "blocked": return {
|
|
171066
|
-
headline: "Goal blocked",
|
|
171067
|
-
accentToken: "warning"
|
|
171068
|
-
};
|
|
171069
|
-
default: return null;
|
|
171070
|
-
}
|
|
171071
|
-
return null;
|
|
171072
|
-
}
|
|
171073
|
-
function prominentMarker(headline, accentToken) {
|
|
171074
|
-
return {
|
|
171075
|
-
headline,
|
|
171076
|
-
accentToken,
|
|
171077
|
-
detail: void 0,
|
|
171078
|
-
options: {
|
|
171079
|
-
marker: STATUS_BULLET.trimEnd(),
|
|
171080
|
-
textToken: accentToken,
|
|
171081
|
-
expandable: false,
|
|
171082
|
-
indent: "",
|
|
171083
|
-
leadingBlank: true
|
|
171084
|
-
}
|
|
171085
|
-
};
|
|
171086
|
-
}
|
|
171087
|
-
function pausedHeadline(reason, actor) {
|
|
171088
|
-
if (reason === "Paused after interruption") return "Goal paused due to user's interruption";
|
|
171089
|
-
if (actor === "user") return "Goal paused by the user.";
|
|
171090
|
-
if (reason?.startsWith("Paused ") === true) return `Goal ${lowercaseFirst(reason)}`;
|
|
171091
|
-
if (reason !== void 0 && reason.length > 0) return `Goal paused: ${reason}`;
|
|
171092
|
-
if (actor === "model") return "Goal paused by the agent.";
|
|
171093
|
-
return "Goal paused";
|
|
171094
|
-
}
|
|
171095
|
-
function resumedHeadline(actor) {
|
|
171096
|
-
if (actor === "user") return "Goal resumed by the user.";
|
|
171097
|
-
if (actor === "model") return "Goal resumed by the agent.";
|
|
171098
|
-
return "Goal resumed";
|
|
171099
|
-
}
|
|
171100
|
-
function lowercaseFirst(text) {
|
|
171101
|
-
return text.length === 0 ? text : `${text[0].toLowerCase()}${text.slice(1)}`;
|
|
171102
|
-
}
|
|
171103
|
-
function wrap(text, width) {
|
|
171104
|
-
const words = text.replaceAll(/\s+/g, " ").trim().split(" ");
|
|
171105
|
-
const lines = [];
|
|
171106
|
-
let current = "";
|
|
171107
|
-
for (const word of words) {
|
|
171108
|
-
const candidate = current.length === 0 ? word : `${current} ${word}`;
|
|
171109
|
-
if (candidate.length > width && current.length > 0) {
|
|
171110
|
-
lines.push(current);
|
|
171111
|
-
current = word;
|
|
171112
|
-
} else current = candidate;
|
|
171113
|
-
}
|
|
171114
|
-
if (current.length > 0) lines.push(current);
|
|
171115
|
-
return lines.length > 0 ? lines : [""];
|
|
171116
|
-
}
|
|
171117
|
-
//#endregion
|
|
171118
|
-
//#region src/tui/components/messages/status-message.ts
|
|
171119
|
-
var StatusMessageComponent = class extends Container {
|
|
171120
|
-
textComponent;
|
|
171121
|
-
content;
|
|
171122
|
-
color;
|
|
171123
|
-
constructor(content, color) {
|
|
171124
|
-
super();
|
|
171125
|
-
this.content = content;
|
|
171126
|
-
this.color = color;
|
|
171127
|
-
const text = color === void 0 ? currentTheme.fg("textDim", content) : currentTheme.fg(color, content);
|
|
171128
|
-
this.textComponent = new Text(` ${text}`, 0, 0);
|
|
171129
|
-
this.addChild(this.textComponent);
|
|
171130
|
-
}
|
|
171131
|
-
invalidate() {
|
|
171132
|
-
const text = this.color === void 0 ? currentTheme.fg("textDim", this.content) : currentTheme.fg(this.color, this.content);
|
|
171133
|
-
this.textComponent.setText(` ${text}`);
|
|
171134
|
-
super.invalidate();
|
|
171135
|
-
}
|
|
171136
|
-
};
|
|
171137
|
-
var NoticeMessageComponent = class extends Container {
|
|
171138
|
-
titleText;
|
|
171139
|
-
detailText;
|
|
171140
|
-
title;
|
|
171141
|
-
detail;
|
|
171142
|
-
constructor(title, detail) {
|
|
171143
|
-
super();
|
|
171144
|
-
this.title = title;
|
|
171145
|
-
this.detail = detail;
|
|
171146
|
-
this.addChild(new Spacer(1));
|
|
171147
|
-
this.titleText = new Text(` ${currentTheme.fg("textStrong", title)}`, 0, 0);
|
|
171148
|
-
this.addChild(this.titleText);
|
|
171149
|
-
if (detail !== void 0 && detail.length > 0) {
|
|
171150
|
-
this.detailText = new Text(` ${currentTheme.fg("textDim", detail)}`, 0, 0);
|
|
171151
|
-
this.addChild(this.detailText);
|
|
171152
|
-
}
|
|
171153
|
-
}
|
|
171154
|
-
invalidate() {
|
|
171155
|
-
this.titleText.setText(` ${currentTheme.fg("textStrong", this.title)}`);
|
|
171156
|
-
if (this.detailText !== void 0 && this.detail !== void 0) this.detailText.setText(` ${currentTheme.fg("textDim", this.detail)}`);
|
|
171157
|
-
super.invalidate();
|
|
171158
|
-
}
|
|
171159
|
-
};
|
|
171160
|
-
//#endregion
|
|
171161
171936
|
//#region src/tui/utils/goal-completion.ts
|
|
171162
171937
|
/**
|
|
171163
171938
|
* Deterministic goal-completion text rendered by the TUI when the model marks a
|
|
@@ -171437,6 +172212,7 @@ var SubAgentEventHandler = class {
|
|
|
171437
172212
|
name: event.name,
|
|
171438
172213
|
argumentsPart: event.argumentsPart ?? null
|
|
171439
172214
|
});
|
|
172215
|
+
else if (event.type === "tool.progress" && (event.update.kind === "stdout" || event.update.kind === "stderr") && event.update.text !== void 0) toolCall.appendSubToolLiveOutput(`${childAgentId}:${event.toolCallId}`, event.update.text);
|
|
171440
172216
|
else if (event.type === "tool.result") toolCall.finishSubToolCall({
|
|
171441
172217
|
tool_call_id: `${childAgentId}:${event.toolCallId}`,
|
|
171442
172218
|
output: serializeToolResultOutput(event.output),
|
|
@@ -172174,12 +172950,15 @@ var SessionEventHandler = class {
|
|
|
172174
172950
|
streamingUI.scheduleFlush();
|
|
172175
172951
|
}
|
|
172176
172952
|
handleToolProgress(event) {
|
|
172177
|
-
if (event.update.kind !== "status") return;
|
|
172178
172953
|
const text = event.update.text;
|
|
172179
172954
|
if (text === void 0 || text.length === 0) return;
|
|
172180
172955
|
const tc = this.host.streamingUI.getToolComponent(event.toolCallId);
|
|
172181
172956
|
if (tc === void 0) return;
|
|
172182
|
-
|
|
172957
|
+
if (event.update.kind === "status") {
|
|
172958
|
+
tc.appendProgress(text);
|
|
172959
|
+
return;
|
|
172960
|
+
}
|
|
172961
|
+
if (event.update.kind === "stdout" || event.update.kind === "stderr") tc.appendLiveOutput(text);
|
|
172183
172962
|
}
|
|
172184
172963
|
handleToolResult(event) {
|
|
172185
172964
|
const { streamingUI } = this.host;
|
|
@@ -172888,6 +173667,9 @@ var SessionReplayRenderer = class {
|
|
|
172888
173667
|
case "message":
|
|
172889
173668
|
this.renderMessage(context, record.message);
|
|
172890
173669
|
return;
|
|
173670
|
+
case "compaction":
|
|
173671
|
+
this.renderCompaction(context, record);
|
|
173672
|
+
return;
|
|
172891
173673
|
case "goal_updated":
|
|
172892
173674
|
this.renderGoalReplayRecord(context, record);
|
|
172893
173675
|
return;
|
|
@@ -173050,6 +173832,28 @@ var SessionReplayRenderer = class {
|
|
|
173050
173832
|
skillTrigger: skill.trigger
|
|
173051
173833
|
});
|
|
173052
173834
|
}
|
|
173835
|
+
renderCompaction(context, record) {
|
|
173836
|
+
this.flushAssistant(context);
|
|
173837
|
+
if (record.result === void 0) return;
|
|
173838
|
+
if (record.result === "cancelled") {
|
|
173839
|
+
this.host.appendTranscriptEntry({
|
|
173840
|
+
...replayEntry(context, "status", "Compaction cancelled", "plain"),
|
|
173841
|
+
compactionData: {
|
|
173842
|
+
result: "cancelled",
|
|
173843
|
+
instruction: record.instruction
|
|
173844
|
+
}
|
|
173845
|
+
});
|
|
173846
|
+
return;
|
|
173847
|
+
}
|
|
173848
|
+
this.host.appendTranscriptEntry({
|
|
173849
|
+
...replayEntry(context, "status", "Compaction complete", "plain"),
|
|
173850
|
+
compactionData: {
|
|
173851
|
+
tokensBefore: record.result.tokensBefore,
|
|
173852
|
+
tokensAfter: record.result.tokensAfter,
|
|
173853
|
+
instruction: record.instruction
|
|
173854
|
+
}
|
|
173855
|
+
});
|
|
173856
|
+
}
|
|
173053
173857
|
renderGoalReplayRecord(context, record) {
|
|
173054
173858
|
this.flushAssistant(context);
|
|
173055
173859
|
const { change } = record;
|
|
@@ -173320,6 +174124,23 @@ function supportsOsc9Notification(env = process.env) {
|
|
|
173320
174124
|
if (term === "xterm-kitty" || term === "xterm-ghostty") return true;
|
|
173321
174125
|
return false;
|
|
173322
174126
|
}
|
|
174127
|
+
/**
|
|
174128
|
+
* Best-effort detection of ConEmu-style OSC 9;4 progress support, driven
|
|
174129
|
+
* off well-known environment variables like `supportsOsc9Notification`.
|
|
174130
|
+
* The two allow-lists must stay separate: iTerm2 posts a desktop
|
|
174131
|
+
* notification for ANY `OSC 9;<payload>` it receives, so sending the 9;4
|
|
174132
|
+
* progress sequence there pops a "4;3" notification every keepalive tick.
|
|
174133
|
+
* Terminals outside this list simply get no progress reporting, which is
|
|
174134
|
+
* always safe.
|
|
174135
|
+
*/
|
|
174136
|
+
function supportsTerminalProgress(env = process.env) {
|
|
174137
|
+
if ((env["WT_SESSION"] ?? "").length > 0) return true;
|
|
174138
|
+
if (env["ConEmuANSI"] === "ON") return true;
|
|
174139
|
+
const termProgram = env["TERM_PROGRAM"] ?? "";
|
|
174140
|
+
if (termProgram === "ghostty" || termProgram === "WezTerm") return true;
|
|
174141
|
+
if ((env["TERM"] ?? "") === "xterm-ghostty") return true;
|
|
174142
|
+
return false;
|
|
174143
|
+
}
|
|
173323
174144
|
function isInsideTmux(env = process.env) {
|
|
173324
174145
|
return (env["TMUX"] ?? "").length > 0;
|
|
173325
174146
|
}
|
|
@@ -173967,7 +174788,7 @@ var StreamingUIController = class {
|
|
|
173967
174788
|
* TasksBrowserApp. Snapshot view (no live tail) — content is fetched
|
|
173968
174789
|
* once when the viewer opens.
|
|
173969
174790
|
*/
|
|
173970
|
-
const ELLIPSIS$
|
|
174791
|
+
const ELLIPSIS$2 = "…";
|
|
173971
174792
|
const STATUS_LABEL$1 = {
|
|
173972
174793
|
running: "running",
|
|
173973
174794
|
completed: "completed",
|
|
@@ -173989,12 +174810,12 @@ function statusColor$1(status) {
|
|
|
173989
174810
|
function padToWidth$1(line, width) {
|
|
173990
174811
|
const w = visibleWidth(line);
|
|
173991
174812
|
if (w === width) return line;
|
|
173992
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
174813
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$2);
|
|
173993
174814
|
return line + " ".repeat(width - w);
|
|
173994
174815
|
}
|
|
173995
174816
|
function fitExactly$1(line, width) {
|
|
173996
174817
|
let s = line;
|
|
173997
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
174818
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$2);
|
|
173998
174819
|
return padToWidth$1(s, width);
|
|
173999
174820
|
}
|
|
174000
174821
|
var TaskOutputViewer = class extends Container {
|
|
@@ -174153,7 +174974,7 @@ var TaskOutputViewer = class extends Container {
|
|
|
174153
174974
|
* Data (tasks list, tail output) flows in via `setProps`; user actions
|
|
174154
174975
|
* fire the `on*` callbacks back to the controller.
|
|
174155
174976
|
*/
|
|
174156
|
-
const ELLIPSIS$
|
|
174977
|
+
const ELLIPSIS$1 = "…";
|
|
174157
174978
|
const STATUS_LABEL = {
|
|
174158
174979
|
running: "running",
|
|
174159
174980
|
completed: "completed",
|
|
@@ -174201,13 +175022,13 @@ function singleLine(text) {
|
|
|
174201
175022
|
function padToWidth(line, width) {
|
|
174202
175023
|
const w = visibleWidth(line);
|
|
174203
175024
|
if (w === width) return line;
|
|
174204
|
-
if (w > width) return truncateToWidth(line, width, ELLIPSIS$
|
|
175025
|
+
if (w > width) return truncateToWidth(line, width, ELLIPSIS$1);
|
|
174205
175026
|
return line + " ".repeat(width - w);
|
|
174206
175027
|
}
|
|
174207
175028
|
/** Fit `line` into exactly `width` columns, even after CJK-edge truncation. */
|
|
174208
175029
|
function fitExactly(line, width) {
|
|
174209
175030
|
let s = line;
|
|
174210
|
-
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$
|
|
175031
|
+
if (visibleWidth(s) > width) s = truncateToWidth(s, width, ELLIPSIS$1);
|
|
174211
175032
|
return padToWidth(s, width);
|
|
174212
175033
|
}
|
|
174213
175034
|
function visibleTasks(tasks, filter) {
|
|
@@ -174468,7 +175289,7 @@ var TasksBrowserApp = class extends Container {
|
|
|
174468
175289
|
const prefixWidth = visibleWidth(prefix);
|
|
174469
175290
|
const descBudget = Math.max(0, innerWidth - prefixWidth - 1);
|
|
174470
175291
|
if (descBudget < 4) return fitExactly(prefix, innerWidth);
|
|
174471
|
-
const desc = truncateToWidth(singleLine(task.description) || (task.kind === "process" ? singleLine(task.command) : "") || "(no description)", descBudget, ELLIPSIS$
|
|
175292
|
+
const desc = truncateToWidth(singleLine(task.description) || (task.kind === "process" ? singleLine(task.command) : "") || "(no description)", descBudget, ELLIPSIS$1);
|
|
174472
175293
|
return fitExactly(`${prefix} ${currentTheme.fg("text", desc)}`, innerWidth);
|
|
174473
175294
|
}
|
|
174474
175295
|
adjustScroll(visibleRows) {
|
|
@@ -174845,289 +175666,6 @@ var TasksBrowserController = class {
|
|
|
174845
175666
|
}
|
|
174846
175667
|
};
|
|
174847
175668
|
//#endregion
|
|
174848
|
-
//#region src/tui/components/editor/file-mention-provider.ts
|
|
174849
|
-
const PATH_DELIMITERS = new Set([
|
|
174850
|
-
" ",
|
|
174851
|
-
" ",
|
|
174852
|
-
"\"",
|
|
174853
|
-
"'",
|
|
174854
|
-
"="
|
|
174855
|
-
]);
|
|
174856
|
-
const MAX_FALLBACK_SCAN = 2e3;
|
|
174857
|
-
const MAX_FALLBACK_SUGGESTIONS = 50;
|
|
174858
|
-
/**
|
|
174859
|
-
* Kimi wrapper around pi-tui's combined autocomplete provider.
|
|
174860
|
-
*
|
|
174861
|
-
* File / folder mention behavior uses pi-tui's fd-backed provider when fd is
|
|
174862
|
-
* available. While managed fd is downloading (or when it is unavailable), a
|
|
174863
|
-
* small filesystem fallback keeps basic `@` file and folder completion usable.
|
|
174864
|
-
* Ordinary path completion is still handled by pi-tui's readdir-backed path
|
|
174865
|
-
* completer. This wrapper also keeps Kimi-specific slash-command guards.
|
|
174866
|
-
*/
|
|
174867
|
-
var FileMentionProvider = class {
|
|
174868
|
-
slashCommands;
|
|
174869
|
-
workDir;
|
|
174870
|
-
fdPath;
|
|
174871
|
-
inner;
|
|
174872
|
-
constructor(slashCommands, workDir, fdPath) {
|
|
174873
|
-
this.slashCommands = slashCommands;
|
|
174874
|
-
this.workDir = workDir;
|
|
174875
|
-
this.fdPath = fdPath;
|
|
174876
|
-
const expanded = [];
|
|
174877
|
-
for (const cmd of slashCommands) {
|
|
174878
|
-
expanded.push(cmd);
|
|
174879
|
-
for (const alias of cmd.aliases ?? []) expanded.push({
|
|
174880
|
-
...cmd,
|
|
174881
|
-
name: alias
|
|
174882
|
-
});
|
|
174883
|
-
}
|
|
174884
|
-
this.inner = new CombinedAutocompleteProvider(expanded, workDir, fdPath);
|
|
174885
|
-
}
|
|
174886
|
-
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
174887
|
-
const currentLine = lines[cursorLine] ?? "";
|
|
174888
|
-
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
174889
|
-
if (shouldSuppressLeadingWhitespaceSlashPath(textBeforeCursor, options.force)) return null;
|
|
174890
|
-
if (shouldSuppressSlashArgumentCompletion(textBeforeCursor, currentLine.slice(cursorCol), options.force)) return null;
|
|
174891
|
-
const atPrefix = extractAtPrefix(textBeforeCursor);
|
|
174892
|
-
if (atPrefix !== null) {
|
|
174893
|
-
if (this.fdPath === null) return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
174894
|
-
try {
|
|
174895
|
-
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
174896
|
-
} catch {
|
|
174897
|
-
return getFsMentionSuggestions(this.workDir, atPrefix, options.signal);
|
|
174898
|
-
}
|
|
174899
|
-
}
|
|
174900
|
-
if (!options.force && textBeforeCursor.startsWith("/")) {
|
|
174901
|
-
if (textBeforeCursor.indexOf(" ") === -1) {
|
|
174902
|
-
const tokens = textBeforeCursor.slice(1).trim().split(/\s+/).filter((t) => t.length > 0);
|
|
174903
|
-
const matches = [];
|
|
174904
|
-
for (const cmd of this.slashCommands) {
|
|
174905
|
-
const nameScore = scoreTokens(tokens, cmd.name);
|
|
174906
|
-
if (nameScore !== null) {
|
|
174907
|
-
matches.push({
|
|
174908
|
-
cmd,
|
|
174909
|
-
score: nameScore,
|
|
174910
|
-
viaAlias: false,
|
|
174911
|
-
label: cmd.name
|
|
174912
|
-
});
|
|
174913
|
-
continue;
|
|
174914
|
-
}
|
|
174915
|
-
const aliases = cmd.aliases ?? [];
|
|
174916
|
-
let bestAliasScore = null;
|
|
174917
|
-
for (const alias of aliases) {
|
|
174918
|
-
const aliasScore = scoreTokens(tokens, alias);
|
|
174919
|
-
if (aliasScore !== null && (bestAliasScore === null || aliasScore < bestAliasScore)) bestAliasScore = aliasScore;
|
|
174920
|
-
}
|
|
174921
|
-
if (bestAliasScore !== null) matches.push({
|
|
174922
|
-
cmd,
|
|
174923
|
-
score: bestAliasScore,
|
|
174924
|
-
viaAlias: true,
|
|
174925
|
-
label: `${cmd.name} (${aliases.join(", ")})`
|
|
174926
|
-
});
|
|
174927
|
-
}
|
|
174928
|
-
matches.sort((a, b) => a.score - b.score || Number(a.viaAlias) - Number(b.viaAlias));
|
|
174929
|
-
if (matches.length === 0) return null;
|
|
174930
|
-
return {
|
|
174931
|
-
items: matches.map((m) => ({
|
|
174932
|
-
value: m.cmd.name,
|
|
174933
|
-
label: m.label,
|
|
174934
|
-
description: formatSlashCommandDescription(m.cmd)
|
|
174935
|
-
})),
|
|
174936
|
-
prefix: textBeforeCursor
|
|
174937
|
-
};
|
|
174938
|
-
}
|
|
174939
|
-
}
|
|
174940
|
-
try {
|
|
174941
|
-
return await this.inner.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
174942
|
-
} catch {
|
|
174943
|
-
return null;
|
|
174944
|
-
}
|
|
174945
|
-
}
|
|
174946
|
-
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
174947
|
-
return this.inner.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
174948
|
-
}
|
|
174949
|
-
};
|
|
174950
|
-
function extractAtPrefix(text) {
|
|
174951
|
-
let tokenStart = 0;
|
|
174952
|
-
for (let i = text.length - 1; i >= 0; i -= 1) if (PATH_DELIMITERS.has(text[i] ?? "")) {
|
|
174953
|
-
tokenStart = i + 1;
|
|
174954
|
-
break;
|
|
174955
|
-
}
|
|
174956
|
-
if (text[tokenStart] !== "@") return null;
|
|
174957
|
-
return text.slice(tokenStart);
|
|
174958
|
-
}
|
|
174959
|
-
function getFsMentionSuggestions(workDir, atPrefix, signal) {
|
|
174960
|
-
if (signal.aborted) return null;
|
|
174961
|
-
const query = atPrefix.slice(1);
|
|
174962
|
-
const candidates = collectFsMentionCandidates(workDir, signal);
|
|
174963
|
-
if (candidates.length === 0 || signal.aborted) return null;
|
|
174964
|
-
const ranked = rankFsMentionCandidates(candidates, query).slice(0, MAX_FALLBACK_SUGGESTIONS);
|
|
174965
|
-
if (ranked.length === 0) return null;
|
|
174966
|
-
return {
|
|
174967
|
-
prefix: atPrefix,
|
|
174968
|
-
items: ranked.map(toMentionItem)
|
|
174969
|
-
};
|
|
174970
|
-
}
|
|
174971
|
-
function collectFsMentionCandidates(workDir, signal) {
|
|
174972
|
-
const result = [];
|
|
174973
|
-
const stack = [""];
|
|
174974
|
-
while (stack.length > 0 && result.length < MAX_FALLBACK_SCAN) {
|
|
174975
|
-
if (signal.aborted) break;
|
|
174976
|
-
const relativeDir = stack.pop() ?? "";
|
|
174977
|
-
const absoluteDir = relativeDir.length === 0 ? workDir : join$1(workDir, relativeDir);
|
|
174978
|
-
let entries;
|
|
174979
|
-
try {
|
|
174980
|
-
entries = readdirSync(absoluteDir, { withFileTypes: true });
|
|
174981
|
-
} catch {
|
|
174982
|
-
continue;
|
|
174983
|
-
}
|
|
174984
|
-
for (const entry of entries) {
|
|
174985
|
-
if (signal.aborted || result.length >= MAX_FALLBACK_SCAN) break;
|
|
174986
|
-
if (entry.name === ".git") continue;
|
|
174987
|
-
const relativePath = normalizePath(relativeDir.length === 0 ? entry.name : join$1(relativeDir, entry.name));
|
|
174988
|
-
const isSymlink = entry.isSymbolicLink();
|
|
174989
|
-
let isDirectory = entry.isDirectory();
|
|
174990
|
-
if (!isDirectory && isSymlink) try {
|
|
174991
|
-
isDirectory = statSync(join$1(workDir, relativePath)).isDirectory();
|
|
174992
|
-
} catch {}
|
|
174993
|
-
result.push({
|
|
174994
|
-
path: relativePath,
|
|
174995
|
-
isDirectory
|
|
174996
|
-
});
|
|
174997
|
-
if (isDirectory && !isSymlink) stack.push(relativePath);
|
|
174998
|
-
}
|
|
174999
|
-
}
|
|
175000
|
-
return result;
|
|
175001
|
-
}
|
|
175002
|
-
function rankFsMentionCandidates(candidates, query) {
|
|
175003
|
-
const lowerQuery = query.toLowerCase();
|
|
175004
|
-
const scored = [];
|
|
175005
|
-
for (const candidate of candidates) {
|
|
175006
|
-
const score = scoreCandidate(candidate, lowerQuery);
|
|
175007
|
-
if (score > 0) scored.push({
|
|
175008
|
-
candidate,
|
|
175009
|
-
score
|
|
175010
|
-
});
|
|
175011
|
-
}
|
|
175012
|
-
scored.sort((a, b) => {
|
|
175013
|
-
if (a.score !== b.score) return b.score - a.score;
|
|
175014
|
-
if (a.candidate.isDirectory !== b.candidate.isDirectory) return a.candidate.isDirectory ? -1 : 1;
|
|
175015
|
-
return a.candidate.path.localeCompare(b.candidate.path);
|
|
175016
|
-
});
|
|
175017
|
-
return scored.map((entry) => entry.candidate);
|
|
175018
|
-
}
|
|
175019
|
-
function scoreCandidate(candidate, lowerQuery) {
|
|
175020
|
-
if (lowerQuery.length === 0) {
|
|
175021
|
-
const depthPenalty = candidate.path.split("/").length - 1;
|
|
175022
|
-
return (candidate.isDirectory ? 120 : 100) - depthPenalty;
|
|
175023
|
-
}
|
|
175024
|
-
const lowerPath = candidate.path.toLowerCase();
|
|
175025
|
-
const lowerBase = basename$1(candidate.path).toLowerCase();
|
|
175026
|
-
let score = 0;
|
|
175027
|
-
if (lowerBase === lowerQuery) score = 100;
|
|
175028
|
-
else if (lowerBase.startsWith(lowerQuery)) score = 80;
|
|
175029
|
-
else if (lowerBase.includes(lowerQuery)) score = 50;
|
|
175030
|
-
else if (lowerPath.includes(lowerQuery)) score = 30;
|
|
175031
|
-
if (candidate.isDirectory && score > 0) score += 10;
|
|
175032
|
-
return score;
|
|
175033
|
-
}
|
|
175034
|
-
function toMentionItem(candidate) {
|
|
175035
|
-
const valuePath = candidate.isDirectory ? `${candidate.path}/` : candidate.path;
|
|
175036
|
-
return {
|
|
175037
|
-
value: valuePath.includes(" ") ? `@"${valuePath}"` : `@${valuePath}`,
|
|
175038
|
-
label: `${basename$1(candidate.path)}${candidate.isDirectory ? "/" : ""}`,
|
|
175039
|
-
description: valuePath
|
|
175040
|
-
};
|
|
175041
|
-
}
|
|
175042
|
-
function normalizePath(path) {
|
|
175043
|
-
return path.replaceAll("\\", "/");
|
|
175044
|
-
}
|
|
175045
|
-
function shouldSuppressLeadingWhitespaceSlashPath(textBeforeCursor, force) {
|
|
175046
|
-
if (force === true) return false;
|
|
175047
|
-
if (textBeforeCursor.startsWith("/")) return false;
|
|
175048
|
-
return textBeforeCursor.trimStart().startsWith("/");
|
|
175049
|
-
}
|
|
175050
|
-
function shouldSuppressSlashArgumentCompletion(textBeforeCursor, textAfterCursor, force) {
|
|
175051
|
-
if (force === true) return false;
|
|
175052
|
-
if (!textBeforeCursor.startsWith("/")) return false;
|
|
175053
|
-
if (!textBeforeCursor.includes(" ")) return false;
|
|
175054
|
-
return textAfterCursor.trimStart().length > 0;
|
|
175055
|
-
}
|
|
175056
|
-
/**
|
|
175057
|
-
* All tokens must fuzzy-match `text`; returns the summed score, or null when
|
|
175058
|
-
* any token misses. An empty token list matches everything with score 0.
|
|
175059
|
-
* Mirrors pi-tui fuzzyFilter's token semantics — keep in sync if it changes.
|
|
175060
|
-
*/
|
|
175061
|
-
function scoreTokens(tokens, text) {
|
|
175062
|
-
let score = 0;
|
|
175063
|
-
for (const token of tokens) {
|
|
175064
|
-
const m = fuzzyMatch(token, text);
|
|
175065
|
-
if (!m.matches) return null;
|
|
175066
|
-
score += m.score;
|
|
175067
|
-
}
|
|
175068
|
-
return score;
|
|
175069
|
-
}
|
|
175070
|
-
/**
|
|
175071
|
-
* Mirrors CombinedAutocompleteProvider's description rendering so the
|
|
175072
|
-
* intercepted name completion keeps showing the argument hint.
|
|
175073
|
-
*/
|
|
175074
|
-
function formatSlashCommandDescription(cmd) {
|
|
175075
|
-
const desc = cmd.description ?? "";
|
|
175076
|
-
return (cmd.argumentHint ? desc ? `${cmd.argumentHint} — ${desc}` : cmd.argumentHint : desc) || void 0;
|
|
175077
|
-
}
|
|
175078
|
-
//#endregion
|
|
175079
|
-
//#region src/tui/components/panes/activity-pane.ts
|
|
175080
|
-
var ActivityPaneComponent = class extends Container {
|
|
175081
|
-
constructor(options) {
|
|
175082
|
-
super();
|
|
175083
|
-
if (options.mode === "waiting" || options.mode === "tool") {
|
|
175084
|
-
if (options.spinner !== void 0) {
|
|
175085
|
-
this.addChild(new Spacer(1));
|
|
175086
|
-
this.addChild(options.spinner);
|
|
175087
|
-
}
|
|
175088
|
-
return;
|
|
175089
|
-
}
|
|
175090
|
-
if (options.mode === "composing" && options.spinner !== void 0) {
|
|
175091
|
-
this.addChild(new Spacer(1));
|
|
175092
|
-
this.addChild(options.spinner);
|
|
175093
|
-
}
|
|
175094
|
-
}
|
|
175095
|
-
};
|
|
175096
|
-
//#endregion
|
|
175097
|
-
//#region src/tui/components/panes/queue-pane.ts
|
|
175098
|
-
const ELLIPSIS$1 = "…";
|
|
175099
|
-
var QueuePaneComponent = class extends Container {
|
|
175100
|
-
messages;
|
|
175101
|
-
hint;
|
|
175102
|
-
constructor(options) {
|
|
175103
|
-
super();
|
|
175104
|
-
this.messages = options.messages;
|
|
175105
|
-
if (options.messages.length > 0) this.hint = options.isCompacting && !options.isStreaming ? " ↑ to edit · will send after compaction" : !options.canSteerImmediately ? " ↑ to edit · will send after current task" : " ↑ to edit · ctrl-s to steer immediately";
|
|
175106
|
-
}
|
|
175107
|
-
render(width) {
|
|
175108
|
-
const accent = (text) => currentTheme.fg("accent", text);
|
|
175109
|
-
const dim = (text) => currentTheme.fg("textDim", text);
|
|
175110
|
-
const lines = [];
|
|
175111
|
-
for (const item of this.messages) {
|
|
175112
|
-
const singleLine = item.text.replaceAll(/\s+/g, " ").trim();
|
|
175113
|
-
const prefix = ` ❯ `;
|
|
175114
|
-
const truncated = truncateToWidth(singleLine, Math.max(1, width - visibleWidth(prefix)), ELLIPSIS$1);
|
|
175115
|
-
lines.push(accent(prefix + truncated));
|
|
175116
|
-
}
|
|
175117
|
-
if (this.hint !== void 0) lines.push(dim(truncateToWidth(this.hint, width, ELLIPSIS$1)));
|
|
175118
|
-
return lines;
|
|
175119
|
-
}
|
|
175120
|
-
};
|
|
175121
|
-
//#endregion
|
|
175122
|
-
//#region src/tui/utils/startup.ts
|
|
175123
|
-
function combineStartupNotice(existing, next) {
|
|
175124
|
-
if (existing !== void 0 && next !== void 0) return `${existing}\n${next}`;
|
|
175125
|
-
return existing ?? next;
|
|
175126
|
-
}
|
|
175127
|
-
function isOAuthLoginRequiredError(error) {
|
|
175128
|
-
return error.code === OAUTH_LOGIN_REQUIRED_CODE;
|
|
175129
|
-
}
|
|
175130
|
-
//#endregion
|
|
175131
175669
|
//#region src/tui/reverse-rpc/approval/adapter.ts
|
|
175132
175670
|
const DEFAULT_APPROVAL_CHOICES = [
|
|
175133
175671
|
{
|
|
@@ -175538,144 +176076,6 @@ function createApprovalRequestHandler(controller, onResponse) {
|
|
|
175538
176076
|
};
|
|
175539
176077
|
}
|
|
175540
176078
|
//#endregion
|
|
175541
|
-
//#region src/tui/banner/banner-provider.ts
|
|
175542
|
-
function normalizeTag(value) {
|
|
175543
|
-
if (typeof value !== "string") return null;
|
|
175544
|
-
const trimmed = value.trim();
|
|
175545
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
175546
|
-
}
|
|
175547
|
-
function normalizeText(value) {
|
|
175548
|
-
if (typeof value !== "string") return null;
|
|
175549
|
-
const trimmed = value.trim();
|
|
175550
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
175551
|
-
}
|
|
175552
|
-
function normalizeUtcDate(value) {
|
|
175553
|
-
if (value.endsWith("Z")) return value;
|
|
175554
|
-
if (/[+-]\d{2}:\d{2}$/.test(value)) return value;
|
|
175555
|
-
return `${value}Z`;
|
|
175556
|
-
}
|
|
175557
|
-
function parseDate(value) {
|
|
175558
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
175559
|
-
const normalized = normalizeUtcDate(value);
|
|
175560
|
-
const date = new Date(normalized);
|
|
175561
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
175562
|
-
}
|
|
175563
|
-
function isWithinWindow(start, end, now) {
|
|
175564
|
-
if (start !== null && now < start) return false;
|
|
175565
|
-
if (end !== null && now > end) return false;
|
|
175566
|
-
return true;
|
|
175567
|
-
}
|
|
175568
|
-
function meetsMinVersion(minVersion, clientVersion) {
|
|
175569
|
-
if (minVersion === void 0 || minVersion === null) return true;
|
|
175570
|
-
if (typeof minVersion !== "string" || minVersion.length === 0) return true;
|
|
175571
|
-
const min = valid(minVersion);
|
|
175572
|
-
const current = valid(clientVersion);
|
|
175573
|
-
if (min === null || current === null) return false;
|
|
175574
|
-
return gte(current, min);
|
|
175575
|
-
}
|
|
175576
|
-
function pickActiveBanner(json, clientVersion, now) {
|
|
175577
|
-
if (json.banner_enabled !== true) return null;
|
|
175578
|
-
if (!meetsMinVersion(json.banner_min_version, clientVersion)) return null;
|
|
175579
|
-
if (!isWithinWindow(parseDate(json.banner_start_time), parseDate(json.banner_end_time), now)) return null;
|
|
175580
|
-
const mainText = normalizeText(json.banner_maintext);
|
|
175581
|
-
if (mainText === null) return null;
|
|
175582
|
-
return {
|
|
175583
|
-
tag: normalizeTag(json.banner_title),
|
|
175584
|
-
mainText,
|
|
175585
|
-
subText: normalizeText(json.banner_subtext)
|
|
175586
|
-
};
|
|
175587
|
-
}
|
|
175588
|
-
function pickFallbackBanner(json, clientVersion, now, random) {
|
|
175589
|
-
if (json.banner_fallback_enabled !== true) return null;
|
|
175590
|
-
const list = Array.isArray(json.banner_fallback_list) ? json.banner_fallback_list : [];
|
|
175591
|
-
const candidates = [];
|
|
175592
|
-
for (const raw of list) {
|
|
175593
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
175594
|
-
const item = raw;
|
|
175595
|
-
if (item.enabled !== true) continue;
|
|
175596
|
-
if (!meetsMinVersion(item.banner_min_version, clientVersion)) continue;
|
|
175597
|
-
const mainText = normalizeText(item.banner_maintext);
|
|
175598
|
-
if (mainText === null) continue;
|
|
175599
|
-
candidates.push({
|
|
175600
|
-
tag: normalizeTag(item.banner_title),
|
|
175601
|
-
mainText,
|
|
175602
|
-
subText: normalizeText(item.banner_subtext)
|
|
175603
|
-
});
|
|
175604
|
-
}
|
|
175605
|
-
if (candidates.length === 0) return null;
|
|
175606
|
-
return candidates[Math.floor(random() * candidates.length)];
|
|
175607
|
-
}
|
|
175608
|
-
function selectBannerState(json, clientVersion, now, random) {
|
|
175609
|
-
const typed = typeof json === "object" && json !== null ? json : {};
|
|
175610
|
-
return pickActiveBanner(typed, clientVersion, now) ?? pickFallbackBanner(typed, clientVersion, now, random);
|
|
175611
|
-
}
|
|
175612
|
-
var BannerProvider = class {
|
|
175613
|
-
clientVersion;
|
|
175614
|
-
url;
|
|
175615
|
-
constructor(clientVersion, url = KIMI_CODE_TIPS_BANNER_URL) {
|
|
175616
|
-
this.clientVersion = clientVersion;
|
|
175617
|
-
this.url = url;
|
|
175618
|
-
}
|
|
175619
|
-
async load(fetchImpl = fetch) {
|
|
175620
|
-
try {
|
|
175621
|
-
const controller = new AbortController();
|
|
175622
|
-
const timeout = setTimeout(() => {
|
|
175623
|
-
controller.abort();
|
|
175624
|
-
}, 3e3);
|
|
175625
|
-
const response = await fetchImpl(this.url, { signal: controller.signal });
|
|
175626
|
-
clearTimeout(timeout);
|
|
175627
|
-
if (!response.ok) return null;
|
|
175628
|
-
return selectBannerState(await response.json(), this.clientVersion, /* @__PURE__ */ new Date(), Math.random);
|
|
175629
|
-
} catch {
|
|
175630
|
-
return null;
|
|
175631
|
-
}
|
|
175632
|
-
}
|
|
175633
|
-
};
|
|
175634
|
-
//#endregion
|
|
175635
|
-
//#region src/tui/components/chrome/banner.ts
|
|
175636
|
-
const PREFIX_STAR = "✦";
|
|
175637
|
-
const PADDING = " ";
|
|
175638
|
-
var BannerComponent = class {
|
|
175639
|
-
state;
|
|
175640
|
-
constructor(state) {
|
|
175641
|
-
this.state = state;
|
|
175642
|
-
}
|
|
175643
|
-
invalidate() {}
|
|
175644
|
-
render(width) {
|
|
175645
|
-
const main = (s) => currentTheme.boldFg("textStrong", s);
|
|
175646
|
-
const dim = (s) => currentTheme.fg("textDim", s);
|
|
175647
|
-
if (width < 1) return [""];
|
|
175648
|
-
const tagText = this.state.tag ?? "";
|
|
175649
|
-
const tagLabel = tagText.length > 0 ? `${PREFIX_STAR} ${tagText}` : "";
|
|
175650
|
-
const tagStyled = tagLabel.length > 0 ? currentTheme.boldFg("primary", tagLabel) : "";
|
|
175651
|
-
const tagDisplay = tagStyled.length > 0 ? tagStyled + PADDING : "";
|
|
175652
|
-
const tagWidth = visibleWidth(tagDisplay);
|
|
175653
|
-
const showTag = tagWidth > 0 && tagWidth < width;
|
|
175654
|
-
const bodyIndent = showTag ? " ".repeat(tagWidth) : "";
|
|
175655
|
-
const descIndent = showTag ? " ".repeat(visibleWidth(PREFIX_STAR + PADDING)) : "";
|
|
175656
|
-
const bodyContentWidth = width - (showTag ? tagWidth : 0);
|
|
175657
|
-
const descContentWidth = width - (showTag ? visibleWidth(PREFIX_STAR + PADDING) : 0);
|
|
175658
|
-
if (bodyContentWidth <= 0) return [""];
|
|
175659
|
-
const mainSegments = this.state.mainText.split("\n");
|
|
175660
|
-
const subSegments = this.state.subText ? this.state.subText.split("\n") : [];
|
|
175661
|
-
const result = [];
|
|
175662
|
-
for (let i = 0; i < mainSegments.length; i++) {
|
|
175663
|
-
const wrapped = wrapTextWithAnsi(mainSegments[i], bodyContentWidth);
|
|
175664
|
-
for (let j = 0; j < wrapped.length; j++) {
|
|
175665
|
-
const boldLine = main(wrapped[j]);
|
|
175666
|
-
if (i === 0 && j === 0 && showTag) result.push(tagDisplay + boldLine);
|
|
175667
|
-
else result.push(bodyIndent + boldLine);
|
|
175668
|
-
}
|
|
175669
|
-
}
|
|
175670
|
-
for (const sub of subSegments) {
|
|
175671
|
-
const wrapped = wrapTextWithAnsi(sub, descContentWidth <= 0 ? bodyContentWidth : descContentWidth);
|
|
175672
|
-
for (const line of wrapped) result.push(descIndent + dim(line));
|
|
175673
|
-
}
|
|
175674
|
-
result.push("");
|
|
175675
|
-
return result;
|
|
175676
|
-
}
|
|
175677
|
-
};
|
|
175678
|
-
//#endregion
|
|
175679
176079
|
//#region src/tui/reverse-rpc/modal-coordinator.ts
|
|
175680
176080
|
var ReverseRpcModalCoordinator = class {
|
|
175681
176081
|
hooks;
|
|
@@ -175824,13 +176224,6 @@ function adaptQuestionAnswers(event, response) {
|
|
|
175824
176224
|
} : null;
|
|
175825
176225
|
}
|
|
175826
176226
|
//#endregion
|
|
175827
|
-
//#region src/tui/types.ts
|
|
175828
|
-
const INITIAL_LIVE_PANE = {
|
|
175829
|
-
mode: "idle",
|
|
175830
|
-
pendingApproval: null,
|
|
175831
|
-
pendingQuestion: null
|
|
175832
|
-
};
|
|
175833
|
-
//#endregion
|
|
175834
176227
|
//#region src/utils/git/git-status.ts
|
|
175835
176228
|
/**
|
|
175836
176229
|
* Cached git branch + working-tree status for the footer/statusline.
|
|
@@ -176997,11 +177390,19 @@ function createTerminalState() {
|
|
|
176997
177390
|
notificationKeys: /* @__PURE__ */ new Set(),
|
|
176998
177391
|
focused: true,
|
|
176999
177392
|
supportsOsc9: supportsOsc9Notification(),
|
|
177393
|
+
supportsProgress: supportsTerminalProgress(),
|
|
177000
177394
|
insideTmux: isInsideTmux(),
|
|
177001
177395
|
progressActive: false
|
|
177002
177396
|
};
|
|
177003
177397
|
}
|
|
177004
177398
|
//#endregion
|
|
177399
|
+
//#region src/tui/types.ts
|
|
177400
|
+
const INITIAL_LIVE_PANE = {
|
|
177401
|
+
mode: "idle",
|
|
177402
|
+
pendingApproval: null,
|
|
177403
|
+
pendingQuestion: null
|
|
177404
|
+
};
|
|
177405
|
+
//#endregion
|
|
177005
177406
|
//#region src/tui/tui-state.ts
|
|
177006
177407
|
function createTUIState(options) {
|
|
177007
177408
|
const initialAppState = options.initialAppState;
|
|
@@ -177215,6 +177616,15 @@ function sessionRowsForPicker(sessions, currentSessionId, currentSessionHasConte
|
|
|
177215
177616
|
}));
|
|
177216
177617
|
}
|
|
177217
177618
|
//#endregion
|
|
177619
|
+
//#region src/tui/utils/startup.ts
|
|
177620
|
+
function combineStartupNotice(existing, next) {
|
|
177621
|
+
if (existing !== void 0 && next !== void 0) return `${existing}\n${next}`;
|
|
177622
|
+
return existing ?? next;
|
|
177623
|
+
}
|
|
177624
|
+
function isOAuthLoginRequiredError(error) {
|
|
177625
|
+
return error.code === OAUTH_LOGIN_REQUIRED_CODE;
|
|
177626
|
+
}
|
|
177627
|
+
//#endregion
|
|
177218
177628
|
//#region src/tui/utils/terminal-focus.ts
|
|
177219
177629
|
function installTerminalFocusTracking(state) {
|
|
177220
177630
|
state.terminalState.focused = true;
|
|
@@ -177599,10 +178009,8 @@ var KimiTUI = class {
|
|
|
177599
178009
|
try {
|
|
177600
178010
|
const result = await this.authFlow.refreshProviderModels();
|
|
177601
178011
|
for (const c of result.changed) {
|
|
177602
|
-
|
|
177603
|
-
|
|
177604
|
-
if (c.removed > 0) parts.push(`-${String(c.removed)} model${c.removed > 1 ? "s" : ""}`);
|
|
177605
|
-
this.showStatus(parts.join(" · ") + ".");
|
|
178012
|
+
if (c.added <= 0) continue;
|
|
178013
|
+
this.showStatus(`${c.providerName} · +${String(c.added)} model${c.added > 1 ? "s" : ""}.`);
|
|
177606
178014
|
}
|
|
177607
178015
|
for (const f of result.failed) this.showStatus(`Skipped refreshing ${f.provider}: ${f.reason}`, "warning");
|
|
177608
178016
|
} catch {}
|
|
@@ -177617,7 +178025,10 @@ var KimiTUI = class {
|
|
|
177617
178025
|
this.bootstrapFromPicker();
|
|
177618
178026
|
return;
|
|
177619
178027
|
}
|
|
177620
|
-
if (shouldReplayHistory)
|
|
178028
|
+
if (shouldReplayHistory) {
|
|
178029
|
+
await this.sessionReplay.hydrateFromReplay(this.requireSession());
|
|
178030
|
+
this.applyStartupPermissionAndPlanToAppState();
|
|
178031
|
+
}
|
|
177621
178032
|
const resumeState = this.session?.getResumeState();
|
|
177622
178033
|
if (resumeState?.warning !== void 0) this.showStatus(`Warning: ${resumeState.warning}`, "warning");
|
|
177623
178034
|
if (this.session !== void 0) this.sessionEventHandler.startSubscription();
|
|
@@ -177675,7 +178086,10 @@ var KimiTUI = class {
|
|
|
177675
178086
|
}
|
|
177676
178087
|
}
|
|
177677
178088
|
} else session = await this.harness.createSession(createSessionOptions);
|
|
177678
|
-
if (session !== void 0 &&
|
|
178089
|
+
if (session !== void 0 && shouldReplayHistory) {
|
|
178090
|
+
await this.applyStartupModesToResumedSession(session);
|
|
178091
|
+
if (startup.model !== void 0) await session.setModel(startup.model);
|
|
178092
|
+
}
|
|
177679
178093
|
} catch (error) {
|
|
177680
178094
|
if (!isOAuthLoginRequiredError(error)) throw error;
|
|
177681
178095
|
this.authFlow.enterLoginRequiredStartupState();
|
|
@@ -177684,6 +178098,7 @@ var KimiTUI = class {
|
|
|
177684
178098
|
if (session === void 0) throw new Error("Startup session was not initialized.");
|
|
177685
178099
|
await this.setSession(session);
|
|
177686
178100
|
await this.syncRuntimeState(session);
|
|
178101
|
+
this.applyStartupPermissionAndPlanToAppState();
|
|
177687
178102
|
this.state.startupState = "ready";
|
|
177688
178103
|
return shouldReplayHistory;
|
|
177689
178104
|
}
|
|
@@ -178038,6 +178453,20 @@ var KimiTUI = class {
|
|
|
178038
178453
|
goal: goalResult.goal
|
|
178039
178454
|
});
|
|
178040
178455
|
}
|
|
178456
|
+
async applyStartupModesToResumedSession(session) {
|
|
178457
|
+
const { startup } = this.options;
|
|
178458
|
+
if (startup.auto) await session.setPermission("auto");
|
|
178459
|
+
else if (startup.yolo) await session.setPermission("yolo");
|
|
178460
|
+
if (startup.plan) {
|
|
178461
|
+
if (!(await session.getStatus()).planMode) await session.setPlanMode(true);
|
|
178462
|
+
}
|
|
178463
|
+
}
|
|
178464
|
+
applyStartupPermissionAndPlanToAppState() {
|
|
178465
|
+
const { startup } = this.options;
|
|
178466
|
+
if (startup.auto) this.setAppState({ permissionMode: "auto" });
|
|
178467
|
+
else if (startup.yolo) this.setAppState({ permissionMode: "yolo" });
|
|
178468
|
+
if (startup.plan) this.setAppState({ planMode: true });
|
|
178469
|
+
}
|
|
178041
178470
|
async activateRuntime() {
|
|
178042
178471
|
const session = this.requireSession();
|
|
178043
178472
|
await session.setPermission(this.state.appState.permissionMode);
|
|
@@ -178203,12 +178632,21 @@ var KimiTUI = class {
|
|
|
178203
178632
|
this.sessionEventHandler.startSubscription();
|
|
178204
178633
|
this.clearTranscriptAndRedraw();
|
|
178205
178634
|
this.showStatus(`Started a new session (${session.id}).`);
|
|
178635
|
+
this.showConfigWarningsIfAny();
|
|
178636
|
+
}
|
|
178637
|
+
/** Surface config.toml load warnings (degraded or kept-previous config) in the status bar. */
|
|
178638
|
+
async showConfigWarningsIfAny() {
|
|
178639
|
+
try {
|
|
178640
|
+
const { warnings } = await this.harness.getConfigDiagnostics();
|
|
178641
|
+
for (const warning of warnings) this.showStatus(warning, "warning");
|
|
178642
|
+
} catch {}
|
|
178206
178643
|
}
|
|
178207
178644
|
createTranscriptComponent(entry) {
|
|
178208
178645
|
if (entry.compactionData !== void 0) {
|
|
178209
178646
|
const data = entry.compactionData;
|
|
178210
178647
|
const block = new CompactionComponent(this.state.ui, data.instruction);
|
|
178211
|
-
|
|
178648
|
+
if (data.result === "cancelled") block.markCanceled();
|
|
178649
|
+
else block.markDone(data.tokensBefore, data.tokensAfter);
|
|
178212
178650
|
return block;
|
|
178213
178651
|
}
|
|
178214
178652
|
switch (entry.kind) {
|
|
@@ -178475,6 +178913,7 @@ var KimiTUI = class {
|
|
|
178475
178913
|
this.sessionEventHandler.syncAgentSwarmActivitySpinner(spinner);
|
|
178476
178914
|
}
|
|
178477
178915
|
syncTerminalProgress(active) {
|
|
178916
|
+
if (!this.state.terminalState.supportsProgress) return;
|
|
178478
178917
|
if (this.state.terminalState.progressActive === active) return;
|
|
178479
178918
|
this.state.terminal.setProgress(active);
|
|
178480
178919
|
this.state.terminalState.progressActive = active;
|
|
@@ -178554,16 +178993,18 @@ var KimiTUI = class {
|
|
|
178554
178993
|
}
|
|
178555
178994
|
async showSessionPicker() {
|
|
178556
178995
|
await this.fetchSessions();
|
|
178557
|
-
this.mountSessionPicker(() => {
|
|
178996
|
+
this.mountSessionPicker({ onCancel: () => {
|
|
178558
178997
|
this.hideSessionPicker();
|
|
178559
|
-
});
|
|
178998
|
+
} });
|
|
178560
178999
|
}
|
|
178561
179000
|
async bootstrapFromPicker() {
|
|
178562
179001
|
await this.fetchSessions();
|
|
178563
|
-
this.mountSessionPicker(
|
|
178564
|
-
|
|
178565
|
-
|
|
178566
|
-
|
|
179002
|
+
this.mountSessionPicker({
|
|
179003
|
+
applyStartupModes: true,
|
|
179004
|
+
onCancel: () => {
|
|
179005
|
+
this.hideSessionPicker();
|
|
179006
|
+
this.stop();
|
|
179007
|
+
},
|
|
178567
179008
|
onCtrlC: () => {
|
|
178568
179009
|
this.state.editor.onCtrlC?.();
|
|
178569
179010
|
},
|
|
@@ -178577,20 +179018,27 @@ var KimiTUI = class {
|
|
|
178577
179018
|
this.state.activeDialog = null;
|
|
178578
179019
|
this.restoreEditor();
|
|
178579
179020
|
}
|
|
178580
|
-
mountSessionPicker(
|
|
179021
|
+
mountSessionPicker(options) {
|
|
178581
179022
|
this.state.activeDialog = "session-picker";
|
|
178582
179023
|
this.mountEditorReplacement(new SessionPickerComponent({
|
|
178583
179024
|
sessions: this.state.sessions,
|
|
178584
179025
|
loading: this.state.loadingSessions,
|
|
178585
179026
|
currentSessionId: this.state.appState.sessionId,
|
|
178586
179027
|
onSelect: (sessionId) => {
|
|
178587
|
-
this.resumeSession(sessionId).then((switched) => {
|
|
178588
|
-
if (switched)
|
|
179028
|
+
this.resumeSession(sessionId).then(async (switched) => {
|
|
179029
|
+
if (!switched) return;
|
|
179030
|
+
if (options.applyStartupModes === true) {
|
|
179031
|
+
await this.applyStartupModesToResumedSession(this.requireSession());
|
|
179032
|
+
this.applyStartupPermissionAndPlanToAppState();
|
|
179033
|
+
}
|
|
179034
|
+
this.hideSessionPicker();
|
|
179035
|
+
}).catch((error) => {
|
|
179036
|
+
this.showError(`Failed to apply startup flags: ${formatErrorMessage$2(error)}`);
|
|
178589
179037
|
});
|
|
178590
179038
|
},
|
|
178591
|
-
onCancel,
|
|
178592
|
-
onCtrlC:
|
|
178593
|
-
onCtrlD:
|
|
179039
|
+
onCancel: options.onCancel,
|
|
179040
|
+
onCtrlC: options.onCtrlC,
|
|
179041
|
+
onCtrlD: options.onCtrlD
|
|
178594
179042
|
}));
|
|
178595
179043
|
}
|
|
178596
179044
|
showApprovalPanel(payload) {
|
|
@@ -178718,6 +179166,7 @@ async function runShell(opts, version, runOptions = {}) {
|
|
|
178718
179166
|
return;
|
|
178719
179167
|
}
|
|
178720
179168
|
const config = await harness.getConfig();
|
|
179169
|
+
for (const warning of (await harness.getConfigDiagnostics()).warnings) configWarning = combineStartupNotice(configWarning, warning);
|
|
178721
179170
|
const configMs = Date.now() - configStartedAt;
|
|
178722
179171
|
const tui = new KimiTUI(harness, {
|
|
178723
179172
|
cliOptions: opts,
|