@kernelonpanic/kitcode 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/README.ru.md +3 -2
- package/dist/index.js +1635 -534
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,6 +75,8 @@ var configSchema = z.object({
|
|
|
75
75
|
theme: themeSchema.default({ accent: "#a78bfa" }),
|
|
76
76
|
effort: effortSchema.default("xhigh"),
|
|
77
77
|
thinking: z.boolean().default(true),
|
|
78
|
+
// Terminal bell when a long answer finishes or a permission prompt appears.
|
|
79
|
+
bell: z.boolean().default(true),
|
|
78
80
|
// Manual override for the model's context window. Unset means the window
|
|
79
81
|
// comes from the provider catalogue or the endpoint's model list.
|
|
80
82
|
contextWindow: z.number().int().min(1e3).max(1e7).optional(),
|
|
@@ -1399,15 +1401,39 @@ async function clearProjectMemory(workspace) {
|
|
|
1399
1401
|
}
|
|
1400
1402
|
|
|
1401
1403
|
// src/providers/effort.ts
|
|
1404
|
+
var HARD_TASK = new RegExp("(?<!\\p{L})(?:bug|fix|debug|test|refactor|implement|\u0431\u0430\u0433|\u0444\u0438\u043A\u0441|\u0438\u0441\u043F\u0440\u0430\u0432|\u0442\u0435\u0441\u0442|\u0440\u0435\u0444\u0430\u043A\u0442\u043E\u0440|\u0440\u0435\u0430\u043B\u0438\u0437)", "iu");
|
|
1402
1405
|
function resolveEffort(effort, messages) {
|
|
1403
1406
|
if (effort !== "auto") return effort;
|
|
1404
1407
|
const latest = messages.findLast((message) => message.role === "user" && message.content.some((block) => block.type === "text"));
|
|
1405
1408
|
const text = latest?.content.filter((block) => block.type === "text").map((block) => block.text).join("\n") ?? "";
|
|
1406
|
-
return text.length > 500 ||
|
|
1409
|
+
return text.length > 500 || HARD_TASK.test(text) ? "high" : "medium";
|
|
1410
|
+
}
|
|
1411
|
+
var rejected = /* @__PURE__ */ new Set();
|
|
1412
|
+
function markEffortRejected(providerId, model) {
|
|
1413
|
+
rejected.add(`${providerId}/${model}`);
|
|
1414
|
+
}
|
|
1415
|
+
function effectiveEffort(kind, model, effort, messages, providerId) {
|
|
1416
|
+
if (providerId !== void 0 && rejected.has(`${providerId}/${model}`)) return void 0;
|
|
1417
|
+
const requested = resolveEffort(effort, messages);
|
|
1418
|
+
return kind === "openai" ? openAiEffort(model, requested) : anthropicEffort(model, requested);
|
|
1419
|
+
}
|
|
1420
|
+
function anthropicEffort(model, effort) {
|
|
1421
|
+
if (!effort) return void 0;
|
|
1422
|
+
const id = (model.split("/").at(-1) ?? model).replace(/^(?:[a-z]{2}\.)?anthropic\./, "");
|
|
1423
|
+
if (!id.startsWith("claude-")) return void 0;
|
|
1424
|
+
if (/^claude-(?:3|[a-z]+-3|haiku-4|sonnet-4(?:-[0-5])?(?:-\d{8}|@|$)|opus-4(?:-[01])?(?:-\d{8}|@|$))/.test(id)) return void 0;
|
|
1425
|
+
if (/^claude-opus-4-5(?:-|@|$)/.test(id)) return effort === "xhigh" || effort === "max" ? "high" : effort;
|
|
1426
|
+
if (/^claude-(?:opus|sonnet)-4-6(?:-|@|$)/.test(id)) return effort === "xhigh" ? "high" : effort;
|
|
1427
|
+
return effort;
|
|
1407
1428
|
}
|
|
1408
1429
|
function openAiEffort(model, effort) {
|
|
1409
|
-
const id = model.split("/").at(-1) ?? model;
|
|
1410
|
-
if (
|
|
1430
|
+
const id = (model.split("/").at(-1) ?? model).toLowerCase();
|
|
1431
|
+
if (/^(?:gpt-oss|gemini-(?:2\.5|[3-9])|grok-3-mini)/.test(id)) {
|
|
1432
|
+
return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
1433
|
+
}
|
|
1434
|
+
if (!/^(?:o[134](?:-|$)|gpt-5(?:[.-]|$))/.test(id)) {
|
|
1435
|
+
return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
1436
|
+
}
|
|
1411
1437
|
if (/^(?:o[134](?:-|$)|gpt-5(?:-(?:mini|nano|\d{4})|$)|gpt-5\.1(?:-|$))/.test(id)) {
|
|
1412
1438
|
return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
1413
1439
|
}
|
|
@@ -1490,9 +1516,10 @@ async function loadSession(id) {
|
|
|
1490
1516
|
async function listSessions(limit = 20) {
|
|
1491
1517
|
const files = await filesByRecency();
|
|
1492
1518
|
const summaries = [];
|
|
1493
|
-
for (const file of files
|
|
1519
|
+
for (const file of files) {
|
|
1520
|
+
if (summaries.length >= limit) break;
|
|
1494
1521
|
const state = readState(await readFile4(file, "utf8").catch(() => ""));
|
|
1495
|
-
if (state) {
|
|
1522
|
+
if (state && state.messages.length > 0) {
|
|
1496
1523
|
summaries.push({
|
|
1497
1524
|
id: state.id,
|
|
1498
1525
|
title: state.title,
|
|
@@ -1849,6 +1876,17 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1849
1876
|
hooks.onEvent({ type: "turn_end", stopReason: "max_tokens" });
|
|
1850
1877
|
return messages;
|
|
1851
1878
|
}
|
|
1879
|
+
if (steps > 1) {
|
|
1880
|
+
const trimmed = cfg.trimContext?.(messages);
|
|
1881
|
+
if (trimmed) {
|
|
1882
|
+
messages = trimmed;
|
|
1883
|
+
hooks.onEvent({
|
|
1884
|
+
type: "notice",
|
|
1885
|
+
level: "info",
|
|
1886
|
+
text: "Context is nearly full: cleared older tool output to keep going."
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1852
1890
|
const request = async () => {
|
|
1853
1891
|
if (signal.aborted) return { content: [], stopReason: "aborted" };
|
|
1854
1892
|
const budgetDecision = cfg.budget?.beforeRequest({
|
|
@@ -1923,13 +1961,19 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1923
1961
|
let outcome;
|
|
1924
1962
|
let text = "";
|
|
1925
1963
|
let thinking = "";
|
|
1964
|
+
let outputTokens = 0;
|
|
1965
|
+
const startedAt = Date.now();
|
|
1966
|
+
let firstTokenAt;
|
|
1926
1967
|
for await (const event of streamWithRetry(cfg.provider, request, cfg.modelRef, signal)) {
|
|
1927
1968
|
switch (event.type) {
|
|
1928
1969
|
case "text_delta":
|
|
1970
|
+
firstTokenAt ??= Date.now();
|
|
1929
1971
|
text += event.text;
|
|
1930
1972
|
hooks.onEvent({ type: "text_delta", text: event.text });
|
|
1931
1973
|
break;
|
|
1932
1974
|
case "thinking_delta":
|
|
1975
|
+
firstTokenAt ??= Date.now();
|
|
1976
|
+
if (!cfg.thinking) break;
|
|
1933
1977
|
thinking += event.text;
|
|
1934
1978
|
hooks.onEvent({ type: "thinking_delta", text: event.text });
|
|
1935
1979
|
break;
|
|
@@ -1937,6 +1981,7 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1937
1981
|
cfg.budget?.record(cfg.modelRef, event.usage);
|
|
1938
1982
|
cfg.usage.record(cfg.modelRef, event.usage);
|
|
1939
1983
|
hooks.onEvent({ type: "usage", model: cfg.modelRef, usage: event.usage });
|
|
1984
|
+
outputTokens = event.usage.output;
|
|
1940
1985
|
break;
|
|
1941
1986
|
case "rate_limits":
|
|
1942
1987
|
hooks.onEvent({ type: "rate_limits", model: cfg.modelRef, limits: event.limits });
|
|
@@ -1948,7 +1993,13 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1948
1993
|
break;
|
|
1949
1994
|
}
|
|
1950
1995
|
}
|
|
1951
|
-
if (outcome)
|
|
1996
|
+
if (outcome) {
|
|
1997
|
+
const ms = Date.now() - (firstTokenAt ?? startedAt);
|
|
1998
|
+
if (outcome.stopReason !== "aborted" && outputTokens > 0 && ms > 0) {
|
|
1999
|
+
hooks.onEvent({ type: "speed", model: cfg.modelRef, outputTokens, ms });
|
|
2000
|
+
}
|
|
2001
|
+
return outcome;
|
|
2002
|
+
}
|
|
1952
2003
|
if (signal.aborted) {
|
|
1953
2004
|
const content = [];
|
|
1954
2005
|
if (thinking) content.push({ type: "thinking", text: thinking });
|
|
@@ -2069,23 +2120,21 @@ async function runToolCalls(cfg, content, hooks, signal) {
|
|
|
2069
2120
|
const operation = () => runOneCall(cfg, call, hooks, signal);
|
|
2070
2121
|
return call.name !== "task" && cfg.executionLane ? cfg.executionLane.run(operation) : operation();
|
|
2071
2122
|
};
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
results.push(result.value);
|
|
2088
|
-
}
|
|
2123
|
+
const tasks = calls.filter((call) => call.name === "task").map(run);
|
|
2124
|
+
const others = (async () => {
|
|
2125
|
+
const out = [];
|
|
2126
|
+
for (const call of calls) if (call.name !== "task") out.push(await run(call));
|
|
2127
|
+
return out;
|
|
2128
|
+
})();
|
|
2129
|
+
const [settledOthers] = await Promise.allSettled([others]);
|
|
2130
|
+
const settledTasks = await Promise.allSettled(tasks);
|
|
2131
|
+
for (const result of [settledOthers, ...settledTasks]) {
|
|
2132
|
+
if (result.status === "rejected") throw result.reason;
|
|
2133
|
+
}
|
|
2134
|
+
const otherResults = settledOthers.status === "fulfilled" ? settledOthers.value : [];
|
|
2135
|
+
const taskResults = settledTasks.map((result) => result.value);
|
|
2136
|
+
for (const call of calls) {
|
|
2137
|
+
results.push((call.name === "task" ? taskResults : otherResults).shift());
|
|
2089
2138
|
}
|
|
2090
2139
|
return results;
|
|
2091
2140
|
}
|
|
@@ -2102,6 +2151,7 @@ async function runOneCall(cfg, call, hooks, signal) {
|
|
|
2102
2151
|
requestPermission: (request) => hooks.requestPermission(request),
|
|
2103
2152
|
checkpoint: cfg.checkpoint,
|
|
2104
2153
|
onEvent: (event) => hooks.onEvent(event),
|
|
2154
|
+
onProgress: (text) => hooks.onEvent({ type: "tool_progress", id: call.id, text }),
|
|
2105
2155
|
confirm: async (question) => await hooks.requestPermission({
|
|
2106
2156
|
toolName: tool.name,
|
|
2107
2157
|
summary: question,
|
|
@@ -2412,13 +2462,13 @@ function beginCheckpoint(options) {
|
|
|
2412
2462
|
async function undoLatestCheckpoint(options) {
|
|
2413
2463
|
assertSessionId2(options.sessionId);
|
|
2414
2464
|
const root = workspaceRoot(options.cwd);
|
|
2415
|
-
const
|
|
2416
|
-
const file = await newestCheckpoint(sessionDir);
|
|
2465
|
+
const storageDir = options.storageDir ?? checkpointsDir;
|
|
2417
2466
|
const empty = { found: false, restored: [], removed: [], conflicts: [], failed: [] };
|
|
2467
|
+
const file = await newestCheckpoint(path6.join(storageDir, options.sessionId)) ?? await newestWorkspaceCheckpoint(storageDir, root);
|
|
2418
2468
|
if (!file) return empty;
|
|
2419
2469
|
const checkpoint = await readCheckpoint(file);
|
|
2420
|
-
if (checkpoint.
|
|
2421
|
-
throw new Error("The latest undo checkpoint belongs to a different workspace
|
|
2470
|
+
if (checkpoint.cwd !== root) {
|
|
2471
|
+
throw new Error("The latest undo checkpoint belongs to a different workspace.");
|
|
2422
2472
|
}
|
|
2423
2473
|
const result = { ...empty, found: true };
|
|
2424
2474
|
const retry = [];
|
|
@@ -2588,6 +2638,27 @@ function decodeSnapshot(snapshot) {
|
|
|
2588
2638
|
function validBase64(value) {
|
|
2589
2639
|
return value.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(value);
|
|
2590
2640
|
}
|
|
2641
|
+
async function newestWorkspaceCheckpoint(storageDir, root) {
|
|
2642
|
+
let sessions;
|
|
2643
|
+
try {
|
|
2644
|
+
sessions = await readdir2(storageDir);
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
if (error.code === "ENOENT") return null;
|
|
2647
|
+
throw error;
|
|
2648
|
+
}
|
|
2649
|
+
const candidates = [];
|
|
2650
|
+
for (const session of sessions) {
|
|
2651
|
+
if (!/^[A-Za-z0-9_-]{1,240}$/.test(session)) continue;
|
|
2652
|
+
const file = await newestCheckpoint(path6.join(storageDir, session)).catch(() => null);
|
|
2653
|
+
if (file) candidates.push({ name: path6.basename(file), file });
|
|
2654
|
+
}
|
|
2655
|
+
candidates.sort((a, b) => b.name.localeCompare(a.name));
|
|
2656
|
+
for (const candidate of candidates) {
|
|
2657
|
+
const checkpoint = await readCheckpoint(candidate.file).catch(() => null);
|
|
2658
|
+
if (checkpoint?.cwd === root) return candidate.file;
|
|
2659
|
+
}
|
|
2660
|
+
return null;
|
|
2661
|
+
}
|
|
2591
2662
|
async function newestCheckpoint(sessionDir) {
|
|
2592
2663
|
let names;
|
|
2593
2664
|
try {
|
|
@@ -2626,6 +2697,7 @@ var DEFAULT_TIMEOUT = 12e4;
|
|
|
2626
2697
|
var MAX_TIMEOUT = 6e5;
|
|
2627
2698
|
var MAX_OUTPUT = 1e5;
|
|
2628
2699
|
var MAX_COMMAND_CHARS = 2e4;
|
|
2700
|
+
var PROGRESS_INTERVAL_MS = 250;
|
|
2629
2701
|
var bashTool = {
|
|
2630
2702
|
name: "bash",
|
|
2631
2703
|
description: "Run a shell command in the workspace root. stdout and stderr are merged. Avoid commands that wait for interactive input. Set background: true to keep a long command (dev server, test watcher, build) running while you carry on with other work, then collect its output with bash_output.",
|
|
@@ -2689,6 +2761,19 @@ var bashTool = {
|
|
|
2689
2761
|
windowsHide: true
|
|
2690
2762
|
});
|
|
2691
2763
|
const chunks = [];
|
|
2764
|
+
let partial = "";
|
|
2765
|
+
let lastProgress = 0;
|
|
2766
|
+
const progress = (text) => {
|
|
2767
|
+
if (!ctx.onProgress) return;
|
|
2768
|
+
const lines = (partial + text).split(/\r?\n|\r/);
|
|
2769
|
+
partial = lines.at(-1) ?? "";
|
|
2770
|
+
const now = Date.now();
|
|
2771
|
+
if (now - lastProgress < PROGRESS_INTERVAL_MS) return;
|
|
2772
|
+
const latest = [...lines].reverse().find((line) => line.trim() !== "");
|
|
2773
|
+
if (latest === void 0) return;
|
|
2774
|
+
lastProgress = now;
|
|
2775
|
+
ctx.onProgress(latest.trim());
|
|
2776
|
+
};
|
|
2692
2777
|
let captured = 0;
|
|
2693
2778
|
let truncated = false;
|
|
2694
2779
|
const collect = (buf) => {
|
|
@@ -2699,6 +2784,7 @@ var bashTool = {
|
|
|
2699
2784
|
} catch {
|
|
2700
2785
|
text = buf.toString();
|
|
2701
2786
|
}
|
|
2787
|
+
progress(text);
|
|
2702
2788
|
if (text.length > MAX_OUTPUT - captured) {
|
|
2703
2789
|
chunks.push(text.slice(0, MAX_OUTPUT - captured));
|
|
2704
2790
|
truncated = true;
|
|
@@ -2912,7 +2998,7 @@ async function isFile2(file) {
|
|
|
2912
2998
|
|
|
2913
2999
|
// src/core/attachments.ts
|
|
2914
3000
|
import { execFile } from "child_process";
|
|
2915
|
-
import { lstat as lstat2, readdir as readdir3, readFile as readFile7, stat as stat5 } from "fs/promises";
|
|
3001
|
+
import { lstat as lstat2, mkdtemp, readdir as readdir3, readFile as readFile7, rm as rm4, stat as stat5, writeFile as writeFile6 } from "fs/promises";
|
|
2916
3002
|
import os2 from "os";
|
|
2917
3003
|
import path8 from "path";
|
|
2918
3004
|
import { fileURLToPath } from "url";
|
|
@@ -3007,14 +3093,15 @@ async function loadClipboardImage(platform = process.platform, runner = runClipb
|
|
|
3007
3093
|
if (data.length > MAX_IMAGE_BYTES) {
|
|
3008
3094
|
throw new Error(`Clipboard image is larger than ${formatBytes(MAX_IMAGE_BYTES)}.`);
|
|
3009
3095
|
}
|
|
3096
|
+
const fitted = await fitImage(data, mediaType, platform);
|
|
3010
3097
|
return {
|
|
3011
3098
|
type: "image",
|
|
3012
|
-
mediaType,
|
|
3013
|
-
data: data.toString("base64"),
|
|
3014
|
-
name: clipboardImageName(mediaType)
|
|
3099
|
+
mediaType: fitted.mediaType,
|
|
3100
|
+
data: fitted.data.toString("base64"),
|
|
3101
|
+
name: clipboardImageName(fitted.mediaType)
|
|
3015
3102
|
};
|
|
3016
3103
|
} catch (error) {
|
|
3017
|
-
if (error instanceof Error &&
|
|
3104
|
+
if (error instanceof Error && /^(?:Clipboard image|Image) is larger/.test(error.message)) {
|
|
3018
3105
|
throw error;
|
|
3019
3106
|
}
|
|
3020
3107
|
}
|
|
@@ -3031,10 +3118,11 @@ async function loadResolvedAttachment(resolved, size) {
|
|
|
3031
3118
|
const data = await readFile7(resolved);
|
|
3032
3119
|
const mediaType = detectImage(data);
|
|
3033
3120
|
if (mediaType) {
|
|
3121
|
+
const fitted = await fitImage(data, mediaType);
|
|
3034
3122
|
return {
|
|
3035
3123
|
path: resolved,
|
|
3036
|
-
bytes: data.length,
|
|
3037
|
-
block: { type: "image", mediaType, data: data.toString("base64"), name }
|
|
3124
|
+
bytes: fitted.data.length,
|
|
3125
|
+
block: { type: "image", mediaType: fitted.mediaType, data: fitted.data.toString("base64"), name }
|
|
3038
3126
|
};
|
|
3039
3127
|
}
|
|
3040
3128
|
if (data.length > MAX_TEXT_BYTES) {
|
|
@@ -3224,6 +3312,85 @@ try {
|
|
|
3224
3312
|
$image.Dispose()
|
|
3225
3313
|
}
|
|
3226
3314
|
`;
|
|
3315
|
+
var MAX_SENT_IMAGE_BYTES = 37e5;
|
|
3316
|
+
var SHRINK_LONG_EDGE = 2e3;
|
|
3317
|
+
async function fitImage(data, mediaType, platform = process.platform, shrink = shrinkWithSystemTools) {
|
|
3318
|
+
if (data.length <= MAX_SENT_IMAGE_BYTES) return { data, mediaType };
|
|
3319
|
+
const dir = await mkdtemp(path8.join(os2.tmpdir(), "kitcode-image-"));
|
|
3320
|
+
try {
|
|
3321
|
+
const input = path8.join(dir, `in${extensionFor(mediaType)}`);
|
|
3322
|
+
const output = path8.join(dir, "out.jpg");
|
|
3323
|
+
await writeFile6(input, data);
|
|
3324
|
+
try {
|
|
3325
|
+
await shrink(input, output, platform);
|
|
3326
|
+
} catch {
|
|
3327
|
+
throw new Error(
|
|
3328
|
+
`Image is larger than ${formatBytes(MAX_SENT_IMAGE_BYTES)} and could not be scaled down automatically` + (platform === "win32" || platform === "darwin" ? "" : " (install ImageMagick for that)") + ". Save a smaller copy and attach that."
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
const shrunk = await readFile7(output);
|
|
3332
|
+
if (shrunk.length > MAX_SENT_IMAGE_BYTES || detectImage(shrunk) !== "image/jpeg") {
|
|
3333
|
+
throw new Error(`Image is larger than ${formatBytes(MAX_SENT_IMAGE_BYTES)} even after scaling it down.`);
|
|
3334
|
+
}
|
|
3335
|
+
return { data: shrunk, mediaType: "image/jpeg" };
|
|
3336
|
+
} finally {
|
|
3337
|
+
await rm4(dir, { recursive: true, force: true }).catch(() => void 0);
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
async function shrinkWithSystemTools(input, output, platform) {
|
|
3341
|
+
if (platform === "win32") {
|
|
3342
|
+
await runQuiet("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", WINDOWS_SHRINK_SCRIPT], {
|
|
3343
|
+
KITCODE_IMAGE_IN: input,
|
|
3344
|
+
KITCODE_IMAGE_OUT: output,
|
|
3345
|
+
KITCODE_IMAGE_EDGE: String(SHRINK_LONG_EDGE)
|
|
3346
|
+
});
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
if (platform === "darwin") {
|
|
3350
|
+
await runQuiet("sips", ["-Z", String(SHRINK_LONG_EDGE), "-s", "format", "jpeg", "-s", "formatOptions", "85", input, "--out", output]);
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
const args = [input, "-resize", `${SHRINK_LONG_EDGE}x${SHRINK_LONG_EDGE}>`, "-background", "white", "-flatten", "-quality", "85", output];
|
|
3354
|
+
try {
|
|
3355
|
+
await runQuiet("magick", args);
|
|
3356
|
+
} catch {
|
|
3357
|
+
await runQuiet("convert", args);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
function runQuiet(command, args, env) {
|
|
3361
|
+
return new Promise((resolve3, reject) => {
|
|
3362
|
+
execFile(command, args, { timeout: 2e4, windowsHide: true, env: { ...process.env, ...env } }, (error) => {
|
|
3363
|
+
if (error) reject(error);
|
|
3364
|
+
else resolve3();
|
|
3365
|
+
});
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
function extensionFor(mediaType) {
|
|
3369
|
+
return mediaType === "image/jpeg" ? ".jpg" : mediaType === "image/gif" ? ".gif" : mediaType === "image/webp" ? ".webp" : ".png";
|
|
3370
|
+
}
|
|
3371
|
+
var WINDOWS_SHRINK_SCRIPT = String.raw`
|
|
3372
|
+
Add-Type -AssemblyName System.Drawing
|
|
3373
|
+
$source = [System.Drawing.Image]::FromFile($env:KITCODE_IMAGE_IN)
|
|
3374
|
+
try {
|
|
3375
|
+
$edge = [double]$env:KITCODE_IMAGE_EDGE
|
|
3376
|
+
$scale = [Math]::Min(1.0, $edge / [Math]::Max($source.Width, $source.Height))
|
|
3377
|
+
$width = [Math]::Max(1, [int]($source.Width * $scale))
|
|
3378
|
+
$height = [Math]::Max(1, [int]($source.Height * $scale))
|
|
3379
|
+
$bitmap = New-Object System.Drawing.Bitmap $width, $height
|
|
3380
|
+
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
|
3381
|
+
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
|
3382
|
+
$graphics.Clear([System.Drawing.Color]::White)
|
|
3383
|
+
$graphics.DrawImage($source, 0, 0, $width, $height)
|
|
3384
|
+
$codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.MimeType -eq 'image/jpeg' }
|
|
3385
|
+
$parameters = New-Object System.Drawing.Imaging.EncoderParameters 1
|
|
3386
|
+
$parameters.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, [long]85)
|
|
3387
|
+
$bitmap.Save($env:KITCODE_IMAGE_OUT, $codec, $parameters)
|
|
3388
|
+
$graphics.Dispose()
|
|
3389
|
+
$bitmap.Dispose()
|
|
3390
|
+
} finally {
|
|
3391
|
+
$source.Dispose()
|
|
3392
|
+
}
|
|
3393
|
+
`;
|
|
3227
3394
|
function textMime(file) {
|
|
3228
3395
|
const extension = path8.extname(file).toLowerCase();
|
|
3229
3396
|
const known = {
|
|
@@ -3252,13 +3419,27 @@ function normalizeInputPath(value) {
|
|
|
3252
3419
|
return value.slice(1, -1);
|
|
3253
3420
|
}
|
|
3254
3421
|
}
|
|
3422
|
+
if (WINDOWS_PATH.test(value)) return value.replace(/\\ /g, " ");
|
|
3255
3423
|
return value.replace(/\\([\\ "'()\[\]&;#$!])/g, (_match, character) => character);
|
|
3256
3424
|
}
|
|
3425
|
+
var WINDOWS_PATH = /^(?:[A-Za-z]:[\\/]|\\\\)/;
|
|
3426
|
+
function splitAttachmentPaths(value) {
|
|
3427
|
+
const trimmed = value.trim();
|
|
3428
|
+
if (!trimmed || /[\r\n\0]/.test(trimmed)) return null;
|
|
3429
|
+
const tokens2 = [];
|
|
3430
|
+
const pattern = /"([^"]+)"|'([^']+)'|((?:\\ |[^\s"'])+)/g;
|
|
3431
|
+
for (const match of trimmed.matchAll(pattern)) {
|
|
3432
|
+
tokens2.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
3433
|
+
}
|
|
3434
|
+
if (tokens2.length < 2) return null;
|
|
3435
|
+
const absolute = (token) => /^file:\/\//i.test(token) || WINDOWS_PATH.test(token) || token.startsWith("/") || /^~[\\/]/.test(token);
|
|
3436
|
+
return tokens2.every(absolute) ? tokens2 : null;
|
|
3437
|
+
}
|
|
3257
3438
|
function safeName(value) {
|
|
3258
3439
|
return value.replace(/[\r\n\0-\x1f\x7f]/g, "").slice(0, 200) || "attachment";
|
|
3259
3440
|
}
|
|
3260
3441
|
function formatBytes(value) {
|
|
3261
|
-
return `${
|
|
3442
|
+
return `${(value / 1024 / 1024).toFixed(1).replace(/\.0$/, "")} MB`;
|
|
3262
3443
|
}
|
|
3263
3444
|
|
|
3264
3445
|
// src/core/compact.ts
|
|
@@ -3274,11 +3455,15 @@ var MAX_FILE_BLOCK_CHARS = 2e3;
|
|
|
3274
3455
|
var MAX_TOOL_RESULT_CHARS = 1200;
|
|
3275
3456
|
var MAX_TOOL_ERROR_CHARS = 2e3;
|
|
3276
3457
|
var MAX_TOOL_INPUT_CHARS = 800;
|
|
3277
|
-
var SINGLE_MAX_TOKENS =
|
|
3278
|
-
var CHUNK_MAX_TOKENS =
|
|
3279
|
-
var MERGE_MAX_TOKENS =
|
|
3458
|
+
var SINGLE_MAX_TOKENS = 3e3;
|
|
3459
|
+
var CHUNK_MAX_TOKENS = 1200;
|
|
3460
|
+
var MERGE_MAX_TOKENS = 4e3;
|
|
3461
|
+
var MAX_PREVIOUS_SUMMARY_CHARS = 4e4;
|
|
3462
|
+
var SUMMARY_MARKER = "[Earlier conversation summary]";
|
|
3463
|
+
var KEEP_RECENT_TOOL_RESULTS = 6;
|
|
3464
|
+
var CLEARABLE_TOOL_RESULT_CHARS = 300;
|
|
3280
3465
|
var CHUNK_CONCURRENCY = 3;
|
|
3281
|
-
var SYSTEM_SUMMARIZE =
|
|
3466
|
+
var SYSTEM_SUMMARIZE = `You are summarizing a coding conversation so another agent can continue the work. Extract and preserve ONLY: concrete requirements, user preferences, files changed, commands run and their results, errors encountered, bugs found, architectural decisions, and unfinished work. Be terse and factual. Do NOT include greetings, acknowledgments, chain-of-thought, or step-by-step narration. Use these sections: Requirements; Verified facts (with file paths or tool evidence); Changes made; Verification (exact commands, pass/fail/not run, and unresolved error excerpts); Decisions and assumptions; Remaining work. Keep plans and assumptions separate from verified facts. Never turn an intended command into a completed check. When checks conflict, retain the latest result and mark earlier results as superseded. Preserve explicit user corrections and project constraints. Do not retain credentials or secret values. If the input starts with "${SUMMARY_MARKER}", that is the summary of even older work: carry over everything in it that is still relevant and fold the newer events into the same sections. Return only the summary.`;
|
|
3282
3467
|
var SYSTEM_MERGE = "Merge these partial summaries of a coding conversation into one coherent summary. Remove duplicates. Preserve: requirements, files changed, commands run, errors, decisions, and unfinished work. Keep the structured sections and evidence from the source summaries. Separate verified facts, assumptions, and planned work; keep exact verification commands and their latest outcomes. Do not resolve conflicting evidence by guessing. Do not retain credentials or secret values. Be terse. Return only the merged summary.";
|
|
3283
3468
|
async function compactHistory(options) {
|
|
3284
3469
|
const cut = compactCutIndex(options.history);
|
|
@@ -3351,7 +3536,7 @@ async function compactHistory(options) {
|
|
|
3351
3536
|
const prefix = [
|
|
3352
3537
|
{
|
|
3353
3538
|
role: "user",
|
|
3354
|
-
content: [{ type: "text", text:
|
|
3539
|
+
content: [{ type: "text", text: `${SUMMARY_MARKER}
|
|
3355
3540
|
${summary2}` }]
|
|
3356
3541
|
},
|
|
3357
3542
|
{
|
|
@@ -3359,14 +3544,70 @@ ${summary2}` }]
|
|
|
3359
3544
|
content: [{ type: "text", text: "Earlier context loaded. I will continue from this summary." }]
|
|
3360
3545
|
}
|
|
3361
3546
|
];
|
|
3547
|
+
const kept = clearOldToolResults(options.history.slice(cut));
|
|
3362
3548
|
return {
|
|
3363
|
-
history: [...prefix, ...
|
|
3549
|
+
history: [...prefix, ...withoutThinkingReplay(kept.history)],
|
|
3364
3550
|
compacted: true,
|
|
3365
3551
|
removedMessages: cut,
|
|
3552
|
+
clearedToolResults: kept.cleared,
|
|
3366
3553
|
usage,
|
|
3367
3554
|
rateLimits
|
|
3368
3555
|
};
|
|
3369
3556
|
}
|
|
3557
|
+
function clearOldToolResults(history, keepRecent = KEEP_RECENT_TOOL_RESULTS) {
|
|
3558
|
+
let seen = 0;
|
|
3559
|
+
let cleared = 0;
|
|
3560
|
+
const out = [...history];
|
|
3561
|
+
for (let index = out.length - 1; index >= 0; index -= 1) {
|
|
3562
|
+
const message = out[index];
|
|
3563
|
+
if (!message.content.some((block) => block.type === "tool_result")) continue;
|
|
3564
|
+
let changed = false;
|
|
3565
|
+
const content = [...message.content];
|
|
3566
|
+
for (let at = content.length - 1; at >= 0; at -= 1) {
|
|
3567
|
+
const block = content[at];
|
|
3568
|
+
if (block.type !== "tool_result") continue;
|
|
3569
|
+
seen += 1;
|
|
3570
|
+
if (seen <= keepRecent || block.content.length <= CLEARABLE_TOOL_RESULT_CHARS) continue;
|
|
3571
|
+
if (block.content.startsWith(CLEARED_NOTE_PREFIX)) continue;
|
|
3572
|
+
content[at] = {
|
|
3573
|
+
...block,
|
|
3574
|
+
content: `${CLEARED_NOTE_PREFIX} ${block.content.length} characters). Run the tool again if you need it.]`
|
|
3575
|
+
};
|
|
3576
|
+
cleared += 1;
|
|
3577
|
+
changed = true;
|
|
3578
|
+
}
|
|
3579
|
+
if (changed) out[index] = { ...message, content };
|
|
3580
|
+
}
|
|
3581
|
+
return { history: cleared > 0 ? out : history, cleared };
|
|
3582
|
+
}
|
|
3583
|
+
var CLEARED_NOTE_PREFIX = "[Older tool output cleared to free context (";
|
|
3584
|
+
function withoutThinkingReplay(history) {
|
|
3585
|
+
let changed = false;
|
|
3586
|
+
const out = history.map((message) => {
|
|
3587
|
+
if (message.role !== "assistant" || !message.content.some((block) => block.type === "thinking" && block.raw)) {
|
|
3588
|
+
return message;
|
|
3589
|
+
}
|
|
3590
|
+
changed = true;
|
|
3591
|
+
return {
|
|
3592
|
+
...message,
|
|
3593
|
+
content: message.content.map((block) => block.type === "thinking" && block.raw ? { type: "thinking", text: block.text } : block)
|
|
3594
|
+
};
|
|
3595
|
+
});
|
|
3596
|
+
return changed ? out : history;
|
|
3597
|
+
}
|
|
3598
|
+
function estimateHistoryTokens(history) {
|
|
3599
|
+
let chars = 0;
|
|
3600
|
+
for (const message of history) {
|
|
3601
|
+
for (const block of message.content) {
|
|
3602
|
+
if (block.type === "text" || block.type === "thinking") chars += block.text.length;
|
|
3603
|
+
else if (block.type === "tool_result") chars += block.content.length;
|
|
3604
|
+
else if (block.type === "file") chars += block.text.length;
|
|
3605
|
+
else if (block.type === "tool_use") chars += safeJson(block.input).length;
|
|
3606
|
+
else if (block.type === "image") chars += 6e3;
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
return Math.round(chars / 4);
|
|
3610
|
+
}
|
|
3370
3611
|
function shouldAutoCompact(used, window) {
|
|
3371
3612
|
return window !== null && window > 0 && used / window >= 0.8;
|
|
3372
3613
|
}
|
|
@@ -3410,7 +3651,7 @@ function estimateCompactBudget(history, maxTokens = MERGE_MAX_TOKENS) {
|
|
|
3410
3651
|
function isConversationUser(message) {
|
|
3411
3652
|
if (message.role !== "user") return false;
|
|
3412
3653
|
return message.content.some(
|
|
3413
|
-
(block) => block.type === "image" || block.type === "file" || block.type === "text" && !block.text.startsWith(
|
|
3654
|
+
(block) => block.type === "image" || block.type === "file" || block.type === "text" && !block.text.startsWith(SUMMARY_MARKER)
|
|
3414
3655
|
);
|
|
3415
3656
|
}
|
|
3416
3657
|
function chunkRenderedMessages(messages, targetChars, overlapMessages, overlapMaxChars) {
|
|
@@ -3616,6 +3857,9 @@ ${blocks.join("\n")}`] : [];
|
|
|
3616
3857
|
function renderBlock(block, role) {
|
|
3617
3858
|
switch (block.type) {
|
|
3618
3859
|
case "text":
|
|
3860
|
+
if (role === "user" && block.text.startsWith(SUMMARY_MARKER)) {
|
|
3861
|
+
return truncate(block.text, MAX_PREVIOUS_SUMMARY_CHARS);
|
|
3862
|
+
}
|
|
3619
3863
|
return truncate(
|
|
3620
3864
|
block.text,
|
|
3621
3865
|
role === "user" ? MAX_USER_TEXT_CHARS : MAX_ASSISTANT_TEXT_CHARS
|
|
@@ -3692,7 +3936,7 @@ function buildSystemPrompt(args) {
|
|
|
3692
3936
|
|
|
3693
3937
|
// src/core/subagent.ts
|
|
3694
3938
|
var TASK_TOOL_NAME = "task";
|
|
3695
|
-
var MAX_SUBAGENT_STEPS =
|
|
3939
|
+
var MAX_SUBAGENT_STEPS = 30;
|
|
3696
3940
|
var SUBAGENT_NO_ANSWER = "The subagent stopped without writing a final answer \u2014 it was interrupted or ended on a tool call. Nothing was reported back: do the work directly, or delegate it again with a narrower prompt.";
|
|
3697
3941
|
var SUBAGENT_CANCELLED = "The subagent was cancelled before it produced anything.";
|
|
3698
3942
|
var STEP_LIMIT_NOTE = `The subagent was stopped after ${MAX_SUBAGENT_STEPS} model calls without finishing, so anything above is partial. Do the work directly or delegate a narrower piece of it.`;
|
|
@@ -3724,9 +3968,12 @@ function createSubagentRunner(makeConfig, tools) {
|
|
|
3724
3968
|
kind: base.provider.kind,
|
|
3725
3969
|
listModels: () => base.provider.listModels(),
|
|
3726
3970
|
knownModels: () => base.provider.knownModels(),
|
|
3971
|
+
// Keep the tool definitions on the last call: the history holds
|
|
3972
|
+
// tool_use/tool_result blocks, and providers reject those without
|
|
3973
|
+
// tools (Anthropic: 400 "must define tools"). Calls made anyway are
|
|
3974
|
+
// refused by tools.get below.
|
|
3727
3975
|
stream: (chat) => base.provider.stream(steps === MAX_SUBAGENT_STEPS ? {
|
|
3728
3976
|
...chat,
|
|
3729
|
-
tools: [],
|
|
3730
3977
|
system: `${chat.system}
|
|
3731
3978
|
|
|
3732
3979
|
This is your last model call. Do not call tools. Return your findings now, clearly stating any unfinished work.`
|
|
@@ -3857,9 +4104,10 @@ function formatUsageBreakdown(tracker) {
|
|
|
3857
4104
|
lines.push("", `total ${formatUsageCompact(tracker)}`);
|
|
3858
4105
|
return lines.join("\n");
|
|
3859
4106
|
}
|
|
3860
|
-
function tokens(
|
|
3861
|
-
if (
|
|
3862
|
-
const
|
|
4107
|
+
function tokens(count2) {
|
|
4108
|
+
if (count2 < 1e3) return String(count2);
|
|
4109
|
+
const k = Number((count2 / 1e3).toFixed(1));
|
|
4110
|
+
const [value, suffix] = k < 1e3 ? [k, "k"] : [count2 / 1e6, "M"];
|
|
3863
4111
|
return `${value.toFixed(1).replace(/\.0$/, "")}${suffix}`;
|
|
3864
4112
|
}
|
|
3865
4113
|
function dollars(amount) {
|
|
@@ -3943,7 +4191,7 @@ function clip(value) {
|
|
|
3943
4191
|
// package.json
|
|
3944
4192
|
var package_default = {
|
|
3945
4193
|
name: "@kernelonpanic/kitcode",
|
|
3946
|
-
version: "1.4.
|
|
4194
|
+
version: "1.4.1",
|
|
3947
4195
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
3948
4196
|
type: "module",
|
|
3949
4197
|
license: "MIT",
|
|
@@ -4012,7 +4260,7 @@ var package_default = {
|
|
|
4012
4260
|
|
|
4013
4261
|
// src/version.ts
|
|
4014
4262
|
var KITCODE_VERSION = package_default.version;
|
|
4015
|
-
var KITCODE_COMMIT = true ? "
|
|
4263
|
+
var KITCODE_COMMIT = true ? "be753d255e7675a850d0ca1ee64f790f68d3fd93" : "development";
|
|
4016
4264
|
|
|
4017
4265
|
// src/mcp/client.ts
|
|
4018
4266
|
var clientInfo = { name: "kitcode", version: KITCODE_VERSION };
|
|
@@ -4397,7 +4645,7 @@ async function limitedResponseText(response, limit) {
|
|
|
4397
4645
|
}
|
|
4398
4646
|
|
|
4399
4647
|
// src/providers/catalog.ts
|
|
4400
|
-
import { chmod as chmod4, readFile as readFile8, writeFile as
|
|
4648
|
+
import { chmod as chmod4, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
4401
4649
|
import path9 from "path";
|
|
4402
4650
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
4403
4651
|
var CACHE_VERSION = 3;
|
|
@@ -4459,7 +4707,7 @@ async function writeCache(file, models) {
|
|
|
4459
4707
|
const payload = { version: CACHE_VERSION, fetchedAt: Date.now(), models };
|
|
4460
4708
|
try {
|
|
4461
4709
|
await ensureDir(path9.dirname(file));
|
|
4462
|
-
await
|
|
4710
|
+
await writeFile7(file, JSON.stringify(payload), { encoding: "utf8", mode: 384 });
|
|
4463
4711
|
await chmod4(file, 384);
|
|
4464
4712
|
} catch {
|
|
4465
4713
|
return;
|
|
@@ -4607,8 +4855,11 @@ async function* streamTurn(client, providerId, apiKey2, req) {
|
|
|
4607
4855
|
messages: toMessageParams(req.messages),
|
|
4608
4856
|
tools: toToolParams(req.tools)
|
|
4609
4857
|
};
|
|
4610
|
-
if (req.thinking)
|
|
4611
|
-
|
|
4858
|
+
if (req.thinking) {
|
|
4859
|
+
const thinking2 = thinkingParam(req.model, req.maxTokens);
|
|
4860
|
+
if (thinking2) params.thinking = thinking2;
|
|
4861
|
+
}
|
|
4862
|
+
const effort = effectiveEffort("anthropic", req.model, req.effort, req.messages);
|
|
4612
4863
|
if (effort) params.output_config = { effort };
|
|
4613
4864
|
let thinking = "";
|
|
4614
4865
|
let text = "";
|
|
@@ -4687,6 +4938,11 @@ function toMessageParams(messages) {
|
|
|
4687
4938
|
const content = message.content.flatMap(toBlockParams);
|
|
4688
4939
|
if (content.length > 0) params.push({ role: message.role, content });
|
|
4689
4940
|
}
|
|
4941
|
+
const last = params.at(-1)?.content;
|
|
4942
|
+
if (Array.isArray(last)) {
|
|
4943
|
+
const block = last.findLast((candidate) => candidate.type !== "thinking" && candidate.type !== "redacted_thinking");
|
|
4944
|
+
if (block) block.cache_control = { type: "ephemeral" };
|
|
4945
|
+
}
|
|
4690
4946
|
return params;
|
|
4691
4947
|
}
|
|
4692
4948
|
function toBlockParams(block) {
|
|
@@ -4756,6 +5012,15 @@ function toUsage(usage) {
|
|
|
4756
5012
|
cacheRead: usage.cache_read_input_tokens ?? 0
|
|
4757
5013
|
};
|
|
4758
5014
|
}
|
|
5015
|
+
var BUDGET_THINKING = /^claude-(?:3|[a-z]+-3|haiku-4|(?:sonnet|opus)-4(?:-[0-5])?(?:-\d{8}|@|$))/;
|
|
5016
|
+
var THINKING_BUDGET = 16e3;
|
|
5017
|
+
var MIN_THINKING_BUDGET = 1024;
|
|
5018
|
+
function thinkingParam(model, maxTokens) {
|
|
5019
|
+
const id = (model.split("/").at(-1) ?? model).replace(/^(?:[a-z]{2}\.)?anthropic\./, "");
|
|
5020
|
+
if (!BUDGET_THINKING.test(id)) return { type: "adaptive", display: "summarized" };
|
|
5021
|
+
const budget = Math.min(THINKING_BUDGET, maxTokens - 1);
|
|
5022
|
+
return budget >= MIN_THINKING_BUDGET ? { type: "enabled", budget_tokens: budget } : void 0;
|
|
5023
|
+
}
|
|
4759
5024
|
function toRefusal(details) {
|
|
4760
5025
|
return { category: details?.category ?? null, explanation: details?.explanation ?? null };
|
|
4761
5026
|
}
|
|
@@ -4788,6 +5053,57 @@ function createOpenAiProvider(args) {
|
|
|
4788
5053
|
};
|
|
4789
5054
|
}
|
|
4790
5055
|
async function* streamTurn2(client, providerId, apiKey2, req) {
|
|
5056
|
+
let effort = effectiveEffort("openai", req.model, req.effort, req.messages, providerId);
|
|
5057
|
+
const thinkingKey = `${providerId}/${req.model}`;
|
|
5058
|
+
let thinkingOff = req.thinking === false && !thinkingOffRejected.has(thinkingKey) ? thinkingOffParams(req.model) : void 0;
|
|
5059
|
+
let tokenField = completionTokenModels.has(thinkingKey) || usesCompletionTokens(req.model) ? "max_completion_tokens" : "max_tokens";
|
|
5060
|
+
for (; ; ) {
|
|
5061
|
+
let streamed = false;
|
|
5062
|
+
try {
|
|
5063
|
+
for await (const event of streamOnce(client, providerId, apiKey2, req, effort, thinkingOff, tokenField)) {
|
|
5064
|
+
if (event.type !== "rate_limits" && event.type !== "usage") streamed = true;
|
|
5065
|
+
yield event;
|
|
5066
|
+
}
|
|
5067
|
+
return;
|
|
5068
|
+
} catch (error) {
|
|
5069
|
+
if (streamed) throw error;
|
|
5070
|
+
if (tokenField === "max_tokens" && rejectsField(error, /max_completion_tokens/)) {
|
|
5071
|
+
completionTokenModels.add(thinkingKey);
|
|
5072
|
+
tokenField = "max_completion_tokens";
|
|
5073
|
+
continue;
|
|
5074
|
+
}
|
|
5075
|
+
if (effort && rejectsField(error, /reasoning|effort/i)) {
|
|
5076
|
+
markEffortRejected(providerId, req.model);
|
|
5077
|
+
effort = void 0;
|
|
5078
|
+
continue;
|
|
5079
|
+
}
|
|
5080
|
+
if (thinkingOff && rejectsField(error, /thinking/i)) {
|
|
5081
|
+
thinkingOffRejected.add(thinkingKey);
|
|
5082
|
+
thinkingOff = void 0;
|
|
5083
|
+
continue;
|
|
5084
|
+
}
|
|
5085
|
+
throw error;
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
var thinkingOffRejected = /* @__PURE__ */ new Set();
|
|
5090
|
+
var completionTokenModels = /* @__PURE__ */ new Set();
|
|
5091
|
+
function usesCompletionTokens(model) {
|
|
5092
|
+
const id = (model.split("/").at(-1) ?? model).toLowerCase();
|
|
5093
|
+
return /^(?:o[1-9](?:-|$)|gpt-5(?:[.-]|$))/.test(id);
|
|
5094
|
+
}
|
|
5095
|
+
function thinkingOffParams(model) {
|
|
5096
|
+
const id = (model.split("/").at(-1) ?? model).toLowerCase();
|
|
5097
|
+
if (/^(?:glm-4\.[5-9]|glm-[5-9]|kimi-k(?:2\.[5-9]|[3-9]))/.test(id)) return { thinking: { type: "disabled" } };
|
|
5098
|
+
if (/^(?:qwen3|qwq)/.test(id)) return { enable_thinking: false };
|
|
5099
|
+
return void 0;
|
|
5100
|
+
}
|
|
5101
|
+
function rejectsField(error, field2) {
|
|
5102
|
+
if (!(error instanceof ProviderError) || error.status !== 400 && error.status !== 422) return false;
|
|
5103
|
+
const cause = error.cause instanceof Error ? error.cause.message : "";
|
|
5104
|
+
return field2.test(`${error.message} ${cause}`);
|
|
5105
|
+
}
|
|
5106
|
+
async function* streamOnce(client, providerId, apiKey2, req, effort, thinkingOff, tokenField) {
|
|
4791
5107
|
let text = "";
|
|
4792
5108
|
let thinking = "";
|
|
4793
5109
|
let finishReason = null;
|
|
@@ -4797,13 +5113,13 @@ async function* streamTurn2(client, providerId, apiKey2, req) {
|
|
|
4797
5113
|
let recognised = 0;
|
|
4798
5114
|
const calls = /* @__PURE__ */ new Map();
|
|
4799
5115
|
const capture = captureResponseHead();
|
|
4800
|
-
const effort = openAiEffort(req.model, resolveEffort(req.effort, req.messages));
|
|
4801
5116
|
try {
|
|
4802
5117
|
const stream = await client.withOptions({ fetch: capture.fetch }).chat.completions.create(
|
|
4803
5118
|
{
|
|
4804
5119
|
model: req.model,
|
|
4805
|
-
|
|
5120
|
+
[tokenField]: req.maxTokens,
|
|
4806
5121
|
...effort ? { reasoning_effort: effort } : {},
|
|
5122
|
+
...thinkingOff,
|
|
4807
5123
|
messages: toChatMessages(req.system, req.messages),
|
|
4808
5124
|
...req.tools.length > 0 ? { tools: toChatTools(req.tools) } : {},
|
|
4809
5125
|
stream: true,
|
|
@@ -4993,14 +5309,22 @@ function toUsage2(usage) {
|
|
|
4993
5309
|
return normalizeOpenAiUsage(usage);
|
|
4994
5310
|
}
|
|
4995
5311
|
function normalizeOpenAiUsage(usage) {
|
|
4996
|
-
const
|
|
5312
|
+
const prompt = count(usage.prompt_tokens);
|
|
5313
|
+
const cacheRead = Math.min(
|
|
5314
|
+
prompt,
|
|
5315
|
+
count(usage.prompt_tokens_details?.cached_tokens) || count(usage.prompt_cache_hit_tokens) || count(usage.cached_tokens)
|
|
5316
|
+
);
|
|
5317
|
+
const cacheWrite = Math.min(prompt - cacheRead, count(usage.prompt_tokens_details?.cache_write_tokens));
|
|
4997
5318
|
return {
|
|
4998
|
-
input:
|
|
4999
|
-
output: usage.completion_tokens
|
|
5000
|
-
cacheWrite
|
|
5319
|
+
input: prompt - cacheRead - cacheWrite,
|
|
5320
|
+
output: count(usage.completion_tokens),
|
|
5321
|
+
cacheWrite,
|
|
5001
5322
|
cacheRead
|
|
5002
5323
|
};
|
|
5003
5324
|
}
|
|
5325
|
+
function count(value) {
|
|
5326
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
5327
|
+
}
|
|
5004
5328
|
|
|
5005
5329
|
// src/providers/registry.ts
|
|
5006
5330
|
function createRegistry(config, auth) {
|
|
@@ -5049,6 +5373,7 @@ var PLAN_REFUSAL = "Plan mode is on: this tool changes state and is unavailable.
|
|
|
5049
5373
|
function createPermissionEngine(configPermissions) {
|
|
5050
5374
|
const sessionGrants = /* @__PURE__ */ new Set();
|
|
5051
5375
|
let bypassEnabled = false;
|
|
5376
|
+
let bypassUnlocked = false;
|
|
5052
5377
|
let current = "normal";
|
|
5053
5378
|
const resolve3 = (tool, requested) => configPermissions[tool.name] ?? requested ?? tool.defaultPermission;
|
|
5054
5379
|
return {
|
|
@@ -5075,9 +5400,12 @@ function createPermissionEngine(configPermissions) {
|
|
|
5075
5400
|
bypass: {
|
|
5076
5401
|
enable() {
|
|
5077
5402
|
bypassEnabled = true;
|
|
5403
|
+
bypassUnlocked = true;
|
|
5404
|
+
current = "normal";
|
|
5078
5405
|
},
|
|
5079
5406
|
disable() {
|
|
5080
5407
|
bypassEnabled = false;
|
|
5408
|
+
bypassUnlocked = false;
|
|
5081
5409
|
},
|
|
5082
5410
|
isEnabled() {
|
|
5083
5411
|
return bypassEnabled;
|
|
@@ -5089,7 +5417,18 @@ function createPermissionEngine(configPermissions) {
|
|
|
5089
5417
|
current = mode;
|
|
5090
5418
|
},
|
|
5091
5419
|
cycle() {
|
|
5092
|
-
|
|
5420
|
+
if (bypassEnabled) {
|
|
5421
|
+
bypassEnabled = false;
|
|
5422
|
+
current = "normal";
|
|
5423
|
+
return current;
|
|
5424
|
+
}
|
|
5425
|
+
const next = MODES[(MODES.indexOf(current) + 1) % MODES.length] ?? "normal";
|
|
5426
|
+
if (next === "normal" && bypassUnlocked) {
|
|
5427
|
+
bypassEnabled = true;
|
|
5428
|
+
current = "normal";
|
|
5429
|
+
return "bypass";
|
|
5430
|
+
}
|
|
5431
|
+
current = next;
|
|
5093
5432
|
return current;
|
|
5094
5433
|
}
|
|
5095
5434
|
}
|
|
@@ -5157,7 +5496,7 @@ import { readFile as readFile9, stat as stat6 } from "fs/promises";
|
|
|
5157
5496
|
|
|
5158
5497
|
// src/tools/safe-write.ts
|
|
5159
5498
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5160
|
-
import { chmod as chmod5, lstat as lstat3, open, rename as rename5, rm as
|
|
5499
|
+
import { chmod as chmod5, lstat as lstat3, open, rename as rename5, rm as rm5 } from "fs/promises";
|
|
5161
5500
|
import { basename as basename2, dirname as dirname2, join as join2 } from "path";
|
|
5162
5501
|
var UnsafeFileChangeError = class extends Error {
|
|
5163
5502
|
constructor(message) {
|
|
@@ -5220,7 +5559,7 @@ async function atomicWriteSafeFile(file, data, snapshot) {
|
|
|
5220
5559
|
await rename5(temp, file);
|
|
5221
5560
|
} catch (error) {
|
|
5222
5561
|
await handle?.close().catch(() => void 0);
|
|
5223
|
-
await
|
|
5562
|
+
await rm5(temp, { force: true }).catch(() => void 0);
|
|
5224
5563
|
throw error;
|
|
5225
5564
|
}
|
|
5226
5565
|
}
|
|
@@ -5283,12 +5622,12 @@ async function lstatMaybe(file) {
|
|
|
5283
5622
|
var MAX_FILE_BYTES2 = 5e6;
|
|
5284
5623
|
function countOccurrences(haystack, needle) {
|
|
5285
5624
|
if (needle === "") return 0;
|
|
5286
|
-
let
|
|
5625
|
+
let count2 = 0;
|
|
5287
5626
|
let from = 0;
|
|
5288
5627
|
for (; ; ) {
|
|
5289
5628
|
const at = haystack.indexOf(needle, from);
|
|
5290
|
-
if (at === -1) return
|
|
5291
|
-
|
|
5629
|
+
if (at === -1) return count2;
|
|
5630
|
+
count2 += 1;
|
|
5292
5631
|
from = at + needle.length;
|
|
5293
5632
|
}
|
|
5294
5633
|
}
|
|
@@ -5347,16 +5686,16 @@ var editTool = {
|
|
|
5347
5686
|
return { content: `Cannot edit ${path15}: it is a binary file, not text.`, isError: true };
|
|
5348
5687
|
}
|
|
5349
5688
|
const before = beforeBuffer.toString("utf8");
|
|
5350
|
-
const
|
|
5351
|
-
if (
|
|
5689
|
+
const count2 = countOccurrences(before, oldString);
|
|
5690
|
+
if (count2 === 0) {
|
|
5352
5691
|
return {
|
|
5353
5692
|
content: `oldString was not found in ${path15}. Read the file again and match the text exactly, including whitespace.`,
|
|
5354
5693
|
isError: true
|
|
5355
5694
|
};
|
|
5356
5695
|
}
|
|
5357
|
-
if (
|
|
5696
|
+
if (count2 > 1 && !replaceAll) {
|
|
5358
5697
|
return {
|
|
5359
|
-
content: `oldString matches ${
|
|
5698
|
+
content: `oldString matches ${count2} places in ${path15}. Add more surrounding context to identify a single one, or set replaceAll to true.`,
|
|
5360
5699
|
isError: true
|
|
5361
5700
|
};
|
|
5362
5701
|
}
|
|
@@ -5380,14 +5719,14 @@ var editTool = {
|
|
|
5380
5719
|
return { content: `Failed to write ${path15}: ${error.message}`, isError: true };
|
|
5381
5720
|
}
|
|
5382
5721
|
return {
|
|
5383
|
-
content: `Edited ${path15} (${
|
|
5722
|
+
content: `Edited ${path15} (${count2} ${count2 === 1 ? "replacement" : "replacements"})`,
|
|
5384
5723
|
display: { kind: "diff", path: path15, before, after }
|
|
5385
5724
|
};
|
|
5386
5725
|
}
|
|
5387
5726
|
};
|
|
5388
5727
|
function replacement(before, oldString, newString, replaceAll) {
|
|
5389
|
-
const
|
|
5390
|
-
if (
|
|
5728
|
+
const count2 = countOccurrences(before, oldString);
|
|
5729
|
+
if (count2 === 0 || count2 > 1 && !replaceAll) return null;
|
|
5391
5730
|
if (replaceAll) return before.split(oldString).join(newString);
|
|
5392
5731
|
const at = before.indexOf(oldString);
|
|
5393
5732
|
return before.slice(0, at) + newString + before.slice(at + oldString.length);
|
|
@@ -5819,7 +6158,7 @@ function createToolRegistry(tools) {
|
|
|
5819
6158
|
}
|
|
5820
6159
|
|
|
5821
6160
|
// src/prompts/library.ts
|
|
5822
|
-
import { chmod as chmod6, readFile as readFile13, readdir as readdir4, unlink as unlink3, writeFile as
|
|
6161
|
+
import { chmod as chmod6, readFile as readFile13, readdir as readdir4, unlink as unlink3, writeFile as writeFile8 } from "fs/promises";
|
|
5823
6162
|
import path11 from "path";
|
|
5824
6163
|
async function savePrompt(input) {
|
|
5825
6164
|
const slug = slugify(input.name);
|
|
@@ -5833,7 +6172,7 @@ async function savePrompt(input) {
|
|
|
5833
6172
|
};
|
|
5834
6173
|
await ensureDir(promptsDir);
|
|
5835
6174
|
const file = path11.join(promptsDir, `${slug}.md`);
|
|
5836
|
-
await
|
|
6175
|
+
await writeFile8(file, serialize(prompt), {
|
|
5837
6176
|
encoding: "utf8",
|
|
5838
6177
|
mode: 384
|
|
5839
6178
|
});
|
|
@@ -6376,7 +6715,10 @@ function createTaskTool(runner) {
|
|
|
6376
6715
|
required: ["description", "prompt"],
|
|
6377
6716
|
additionalProperties: false
|
|
6378
6717
|
},
|
|
6379
|
-
|
|
6718
|
+
// Starting a subagent changes nothing by itself: each tool call it makes
|
|
6719
|
+
// goes through the same permission and plan-mode checks as the parent.
|
|
6720
|
+
defaultPermission: "allow",
|
|
6721
|
+
readOnly: true,
|
|
6380
6722
|
summarize(input) {
|
|
6381
6723
|
return `task(${brief(field(input, "description"))})`;
|
|
6382
6724
|
},
|
|
@@ -6550,6 +6892,7 @@ async function readJsonBounded(response) {
|
|
|
6550
6892
|
|
|
6551
6893
|
// src/app/runtime.ts
|
|
6552
6894
|
var PREFERRED2 = ["claude-opus-5", "claude-sonnet-5", "gpt-5", "claude-opus-4-8"];
|
|
6895
|
+
var MID_TURN_TRIM_RATIO = 0.8;
|
|
6553
6896
|
async function boot(options) {
|
|
6554
6897
|
const loadedConfig = await loadRuntimeConfig(options.cwd);
|
|
6555
6898
|
const config = loadedConfig.config;
|
|
@@ -6683,9 +7026,16 @@ async function boot(options) {
|
|
|
6683
7026
|
effort: config.effort,
|
|
6684
7027
|
thinking: config.thinking,
|
|
6685
7028
|
budget: activeBudget,
|
|
6686
|
-
checkpoint: activeCheckpoint
|
|
7029
|
+
checkpoint: activeCheckpoint,
|
|
7030
|
+
trimContext: (messages) => trimForWindow(resolved.provider.kind, messages)
|
|
6687
7031
|
};
|
|
6688
7032
|
};
|
|
7033
|
+
const trimForWindow = (kind, messages) => {
|
|
7034
|
+
if (kind !== "openai" || !modelContextWindow) return null;
|
|
7035
|
+
if (estimateHistoryTokens(messages) < modelContextWindow * MID_TURN_TRIM_RATIO) return null;
|
|
7036
|
+
const cleared = clearOldToolResults(messages);
|
|
7037
|
+
return cleared.cleared > 0 ? cleared.history : null;
|
|
7038
|
+
};
|
|
6689
7039
|
let activeBudget;
|
|
6690
7040
|
let activeCheckpoint;
|
|
6691
7041
|
let activeAgentCount = 0;
|
|
@@ -6732,8 +7082,22 @@ async function boot(options) {
|
|
|
6732
7082
|
return save;
|
|
6733
7083
|
};
|
|
6734
7084
|
const compactWithBudget = async (history, signal, budget) => {
|
|
7085
|
+
const tokensBefore = estimateHistoryTokens(history);
|
|
6735
7086
|
if (compactCutIndex(history) <= 0) {
|
|
6736
|
-
|
|
7087
|
+
const cleared = clearOldToolResults(history);
|
|
7088
|
+
if (cleared.cleared === 0) return { history, compacted: false, removedMessages: 0 };
|
|
7089
|
+
const next = withoutThinkingReplay(cleared.history);
|
|
7090
|
+
contextUsage = void 0;
|
|
7091
|
+
session.context = void 0;
|
|
7092
|
+
notifyContext();
|
|
7093
|
+
return {
|
|
7094
|
+
history: next,
|
|
7095
|
+
compacted: true,
|
|
7096
|
+
removedMessages: 0,
|
|
7097
|
+
clearedToolResults: cleared.cleared,
|
|
7098
|
+
tokensBefore,
|
|
7099
|
+
tokensAfter: estimateHistoryTokens(next)
|
|
7100
|
+
};
|
|
6737
7101
|
}
|
|
6738
7102
|
const resolved = registry.resolve(modelRef);
|
|
6739
7103
|
const estimate = estimateCompactBudget(history, config.maxTokens);
|
|
@@ -6761,7 +7125,7 @@ async function boot(options) {
|
|
|
6761
7125
|
session.context = void 0;
|
|
6762
7126
|
notifyContext();
|
|
6763
7127
|
}
|
|
6764
|
-
return result;
|
|
7128
|
+
return { ...result, tokensBefore, tokensAfter: estimateHistoryTokens(result.history) };
|
|
6765
7129
|
};
|
|
6766
7130
|
const runtime = {
|
|
6767
7131
|
cwd: options.cwd,
|
|
@@ -6988,16 +7352,30 @@ async function boot(options) {
|
|
|
6988
7352
|
effortDescription(history) {
|
|
6989
7353
|
if (!modelRef) return "unavailable";
|
|
6990
7354
|
const { provider, modelId } = registry.resolve(modelRef);
|
|
6991
|
-
const
|
|
6992
|
-
|
|
6993
|
-
return effective ? `${config.effort} \u2192 ${effective} (API request)` : `${config.effort} \u2192 not sent: unknown model support`;
|
|
7355
|
+
const effective = effectiveEffort(provider.kind, modelId, config.effort, history, provider.id);
|
|
7356
|
+
return effective ? `${config.effort} \u2192 ${effective} (API request)` : `${config.effort} \u2192 not sent: this model/provider does not accept an effort level`;
|
|
6994
7357
|
},
|
|
6995
7358
|
getEffort: () => config.effort,
|
|
7359
|
+
getEffectiveEffort(history) {
|
|
7360
|
+
if (!modelRef) return null;
|
|
7361
|
+
try {
|
|
7362
|
+
const { provider, modelId } = registry.resolve(modelRef);
|
|
7363
|
+
return effectiveEffort(provider.kind, modelId, config.effort, history, provider.id) ?? null;
|
|
7364
|
+
} catch {
|
|
7365
|
+
return null;
|
|
7366
|
+
}
|
|
7367
|
+
},
|
|
6996
7368
|
async setEffort(effort) {
|
|
6997
7369
|
await persistConfig((draft) => {
|
|
6998
7370
|
draft.effort = effort;
|
|
6999
7371
|
});
|
|
7000
7372
|
},
|
|
7373
|
+
getBell: () => config.bell,
|
|
7374
|
+
async setBell(enabled) {
|
|
7375
|
+
await persistConfig((draft) => {
|
|
7376
|
+
draft.bell = enabled;
|
|
7377
|
+
});
|
|
7378
|
+
},
|
|
7001
7379
|
getThinking: () => config.thinking,
|
|
7002
7380
|
async setThinking(enabled) {
|
|
7003
7381
|
await persistConfig((draft) => {
|
|
@@ -7005,19 +7383,19 @@ async function boot(options) {
|
|
|
7005
7383
|
});
|
|
7006
7384
|
},
|
|
7007
7385
|
getContextWindowOverride: () => config.contextWindow,
|
|
7008
|
-
async setContextWindowOverride(
|
|
7386
|
+
async setContextWindowOverride(tokens2) {
|
|
7009
7387
|
await persistConfig((draft) => {
|
|
7010
|
-
if (
|
|
7011
|
-
else draft.contextWindow =
|
|
7388
|
+
if (tokens2 === void 0) delete draft.contextWindow;
|
|
7389
|
+
else draft.contextWindow = tokens2;
|
|
7012
7390
|
});
|
|
7013
7391
|
modelContextWindow = config.contextWindow ?? resolveContextWindow(modelRef);
|
|
7014
7392
|
notifyContext();
|
|
7015
7393
|
void refreshModelContextWindow();
|
|
7016
7394
|
},
|
|
7017
7395
|
getMaxTokensPerTurn: () => config.budget.maxTokensPerTurn,
|
|
7018
|
-
async setMaxTokensPerTurn(
|
|
7396
|
+
async setMaxTokensPerTurn(tokens2) {
|
|
7019
7397
|
await persistConfig((draft) => {
|
|
7020
|
-
draft.budget.maxTokensPerTurn =
|
|
7398
|
+
draft.budget.maxTokensPerTurn = tokens2;
|
|
7021
7399
|
draft.budget.maxCostUsdPerTurn = 0;
|
|
7022
7400
|
});
|
|
7023
7401
|
},
|
|
@@ -7272,7 +7650,7 @@ ${lines.join("\n")}` : null;
|
|
|
7272
7650
|
hooks.onEvent({
|
|
7273
7651
|
type: "notice",
|
|
7274
7652
|
level: "info",
|
|
7275
|
-
text:
|
|
7653
|
+
text: describeCompaction(result)
|
|
7276
7654
|
});
|
|
7277
7655
|
}
|
|
7278
7656
|
} catch (error) {
|
|
@@ -7291,10 +7669,6 @@ ${lines.join("\n")}` : null;
|
|
|
7291
7669
|
const trackedHooks = {
|
|
7292
7670
|
...hooks,
|
|
7293
7671
|
onEvent(event) {
|
|
7294
|
-
if (event.type === "turn_start") {
|
|
7295
|
-
contextUsage = void 0;
|
|
7296
|
-
notifyContext();
|
|
7297
|
-
}
|
|
7298
7672
|
if (event.type === "usage") {
|
|
7299
7673
|
contextUsage = { model: requestModelRef, usage: event.usage };
|
|
7300
7674
|
notifyContext();
|
|
@@ -7348,6 +7722,7 @@ ${lines.join("\n")}` : null;
|
|
|
7348
7722
|
context: contextUsage
|
|
7349
7723
|
};
|
|
7350
7724
|
session = snapshot;
|
|
7725
|
+
if (history.length === 0) return;
|
|
7351
7726
|
const save = persistQueue.catch(() => void 0).then(() => saveSession(snapshot));
|
|
7352
7727
|
persistQueue = save;
|
|
7353
7728
|
await save;
|
|
@@ -7385,6 +7760,13 @@ function preferredDetectedModel(detected) {
|
|
|
7385
7760
|
}
|
|
7386
7761
|
return candidates[0];
|
|
7387
7762
|
}
|
|
7763
|
+
function describeCompaction(result) {
|
|
7764
|
+
const parts = [];
|
|
7765
|
+
if (result.removedMessages > 0) parts.push(`${result.removedMessages} older messages summarised`);
|
|
7766
|
+
if (result.clearedToolResults) parts.push(`${result.clearedToolResults} old tool outputs cleared`);
|
|
7767
|
+
const size = result.tokensBefore !== void 0 && result.tokensAfter !== void 0 ? ` \xB7 ~${tokens(result.tokensBefore)} \u2192 ~${tokens(result.tokensAfter)} tokens` : "";
|
|
7768
|
+
return `Context compacted: ${parts.join(", ")}${size}.`;
|
|
7769
|
+
}
|
|
7388
7770
|
|
|
7389
7771
|
// src/app/ask.ts
|
|
7390
7772
|
var RU_YES = /* @__PURE__ */ new Set(["y", "\u043D"]);
|
|
@@ -7571,8 +7953,8 @@ function validateKey(value) {
|
|
|
7571
7953
|
import { render } from "ink";
|
|
7572
7954
|
|
|
7573
7955
|
// src/ui/App.tsx
|
|
7574
|
-
import { Box as
|
|
7575
|
-
import { useCallback, useEffect as
|
|
7956
|
+
import { Box as Box14, Text as Text13, useApp, useInput as useInput3, useStdout as useStdout2 } from "ink";
|
|
7957
|
+
import { useCallback, useEffect as useEffect4, useMemo as useMemo6, useRef as useRef5, useState as useState8 } from "react";
|
|
7576
7958
|
|
|
7577
7959
|
// src/ui/terminal-size.ts
|
|
7578
7960
|
import { useStdout } from "ink";
|
|
@@ -7646,6 +8028,40 @@ function stdioServer(name, command, args) {
|
|
|
7646
8028
|
return parsed.success ? { ok: true, name, config: parsed.data } : { ok: false, error: "usage" };
|
|
7647
8029
|
}
|
|
7648
8030
|
|
|
8031
|
+
// src/ui/speed.ts
|
|
8032
|
+
var CHARS_PER_TOKEN = 4;
|
|
8033
|
+
var MIN_LIVE_MS = 1e3;
|
|
8034
|
+
function createSpeedMeter() {
|
|
8035
|
+
return { streamedChars: 0, firstTokenAt: null, last: null };
|
|
8036
|
+
}
|
|
8037
|
+
function recordSpeed(meter, event, now = Date.now()) {
|
|
8038
|
+
switch (event.type) {
|
|
8039
|
+
case "turn_start":
|
|
8040
|
+
meter.streamedChars = 0;
|
|
8041
|
+
meter.firstTokenAt = null;
|
|
8042
|
+
break;
|
|
8043
|
+
case "text_delta":
|
|
8044
|
+
case "thinking_delta":
|
|
8045
|
+
meter.firstTokenAt ??= now;
|
|
8046
|
+
meter.streamedChars += event.text.length;
|
|
8047
|
+
break;
|
|
8048
|
+
case "speed":
|
|
8049
|
+
meter.last = event.outputTokens / (event.ms / 1e3);
|
|
8050
|
+
meter.streamedChars = 0;
|
|
8051
|
+
meter.firstTokenAt = null;
|
|
8052
|
+
break;
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
function readSpeed(meter, busy, now = Date.now()) {
|
|
8056
|
+
if (busy && meter.firstTokenAt !== null && meter.streamedChars > 0) {
|
|
8057
|
+
const ms = now - meter.firstTokenAt;
|
|
8058
|
+
if (ms >= MIN_LIVE_MS) {
|
|
8059
|
+
return { tokensPerSecond: meter.streamedChars / CHARS_PER_TOKEN / (ms / 1e3), live: true };
|
|
8060
|
+
}
|
|
8061
|
+
}
|
|
8062
|
+
return meter.last === null || !Number.isFinite(meter.last) ? null : { tokensPerSecond: meter.last, live: false };
|
|
8063
|
+
}
|
|
8064
|
+
|
|
7649
8065
|
// src/ui/commands.ts
|
|
7650
8066
|
var BUSY_SAFE = { runsWhileBusy: true };
|
|
7651
8067
|
var COMMANDS = [
|
|
@@ -7657,6 +8073,7 @@ var COMMANDS = [
|
|
|
7657
8073
|
{ name: "refresh", args: "[provider]" },
|
|
7658
8074
|
{ name: "effort", ...BUSY_SAFE },
|
|
7659
8075
|
{ name: "thinking", ...BUSY_SAFE },
|
|
8076
|
+
{ name: "bell", args: "[on|off]", ...BUSY_SAFE },
|
|
7660
8077
|
{ name: "context", args: "[tokens|auto]", ...BUSY_SAFE },
|
|
7661
8078
|
{ name: "budget", args: "[tokens]", ...BUSY_SAFE },
|
|
7662
8079
|
{ name: "theme", args: "[color]", ...BUSY_SAFE },
|
|
@@ -7676,9 +8093,9 @@ var COMMANDS = [
|
|
|
7676
8093
|
{ name: "resume" },
|
|
7677
8094
|
{ name: "undo" },
|
|
7678
8095
|
{ name: "clear" },
|
|
8096
|
+
{ name: "subagents", ...BUSY_SAFE },
|
|
7679
8097
|
{ name: "help", ...BUSY_SAFE },
|
|
7680
|
-
{ name: "exit", ...BUSY_SAFE }
|
|
7681
|
-
{ name: "subagents", ...BUSY_SAFE }
|
|
8098
|
+
{ name: "exit", ...BUSY_SAFE }
|
|
7682
8099
|
];
|
|
7683
8100
|
var SUBCOMMANDS = [
|
|
7684
8101
|
{ name: "mcp add", args: "<name> <url|-- command>" },
|
|
@@ -7733,17 +8150,23 @@ function isSubsequence2(needle, haystack) {
|
|
|
7733
8150
|
return needle.length === 0;
|
|
7734
8151
|
}
|
|
7735
8152
|
function editDistance(a, b) {
|
|
8153
|
+
let beforePrevious = [];
|
|
7736
8154
|
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
7737
8155
|
for (let i = 1; i <= a.length; i += 1) {
|
|
7738
8156
|
const current = [i];
|
|
7739
8157
|
for (let j = 1; j <= b.length; j += 1) {
|
|
7740
8158
|
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
7741
|
-
|
|
8159
|
+
let best = Math.min(
|
|
7742
8160
|
(current[j - 1] ?? 0) + 1,
|
|
7743
8161
|
(previous[j] ?? 0) + 1,
|
|
7744
8162
|
(previous[j - 1] ?? 0) + cost
|
|
7745
8163
|
);
|
|
8164
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
8165
|
+
best = Math.min(best, (beforePrevious[j - 2] ?? 0) + 1);
|
|
8166
|
+
}
|
|
8167
|
+
current[j] = best;
|
|
7746
8168
|
}
|
|
8169
|
+
beforePrevious = previous;
|
|
7747
8170
|
previous = current;
|
|
7748
8171
|
}
|
|
7749
8172
|
return previous[b.length] ?? 0;
|
|
@@ -7804,11 +8227,19 @@ var PRESETS = {
|
|
|
7804
8227
|
white: "#e5e7eb"
|
|
7805
8228
|
};
|
|
7806
8229
|
var DEFAULT_ACCENT = PRESETS.purple;
|
|
8230
|
+
function parseAccent(value) {
|
|
8231
|
+
const trimmed = value.trim().toLowerCase();
|
|
8232
|
+
if (Object.hasOwn(PRESETS, trimmed)) return trimmed;
|
|
8233
|
+
const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/.exec(trimmed)?.[1];
|
|
8234
|
+
if (!hex) return void 0;
|
|
8235
|
+
const full = hex.length === 3 ? [...hex].map((digit) => digit + digit).join("") : hex;
|
|
8236
|
+
return `#${full}`;
|
|
8237
|
+
}
|
|
7807
8238
|
function resolveAccent(value) {
|
|
7808
8239
|
if (!value) return DEFAULT_ACCENT;
|
|
7809
|
-
const
|
|
7810
|
-
if (
|
|
7811
|
-
return
|
|
8240
|
+
const parsed = parseAccent(value);
|
|
8241
|
+
if (!parsed) return DEFAULT_ACCENT;
|
|
8242
|
+
return PRESETS[parsed] ?? parsed;
|
|
7812
8243
|
}
|
|
7813
8244
|
function makeTheme(accent) {
|
|
7814
8245
|
return {
|
|
@@ -7872,10 +8303,12 @@ var en = {
|
|
|
7872
8303
|
placeholder: "ask, or / for commands",
|
|
7873
8304
|
working: "working\u2026",
|
|
7874
8305
|
suggestHelp: "\u2191\u2193 move \xB7 tab complete \xB7 enter run",
|
|
7875
|
-
more: (
|
|
8306
|
+
more: (count2) => `\u2026 ${count2} more`,
|
|
7876
8307
|
allowOnce: "allow once",
|
|
7877
8308
|
always: "always",
|
|
7878
8309
|
deny: "deny",
|
|
8310
|
+
dangerousCommand: "risky command",
|
|
8311
|
+
permissionKeys: "\u2191/\u2193 choose \xB7 enter confirm \xB7 y/a/n \xB7 esc deny",
|
|
7879
8312
|
yes: "yes",
|
|
7880
8313
|
noDefault: "no (default)",
|
|
7881
8314
|
noMatches: "no matches",
|
|
@@ -7890,7 +8323,7 @@ var en = {
|
|
|
7890
8323
|
titleModelRefresh: "Reload models for a provider",
|
|
7891
8324
|
titleSessions: "Resume a session",
|
|
7892
8325
|
titleSessionAction: "Session action",
|
|
7893
|
-
resumed: (id,
|
|
8326
|
+
resumed: (id, count2) => `Resumed ${id} \u2014 ${count2} messages restored`,
|
|
7894
8327
|
sessionsEmpty: "No saved sessions yet.",
|
|
7895
8328
|
providerSet: (id, model) => `Provider: ${id} \xB7 model ${model}`,
|
|
7896
8329
|
providersEmpty: "No providers configured. Use /login to add one.",
|
|
@@ -7898,11 +8331,15 @@ var en = {
|
|
|
7898
8331
|
providerBalance: (balances) => `provider balance: ${balances}`,
|
|
7899
8332
|
providerKeyRemaining: (balances) => `API key limit remaining: ${balances}`,
|
|
7900
8333
|
noThink: "no-think",
|
|
7901
|
-
mcpFailed: (
|
|
8334
|
+
mcpFailed: (count2) => `mcp:${count2} failed`,
|
|
7902
8335
|
escCancel: "esc to cancel",
|
|
8336
|
+
escClear: "esc clears the input \xB7 esc again cancels the answer",
|
|
7903
8337
|
escHelp: "esc \u2014 cancel the running turn",
|
|
7904
8338
|
cancelled: "Cancelled.",
|
|
7905
|
-
queued: (
|
|
8339
|
+
queued: (count2) => `queued: ${count2}`,
|
|
8340
|
+
pasteToken: (id, lines, chars) => `[paste #${id} \xB7 ${lines > 1 ? `${lines} lines` : `${chars} chars`}]`,
|
|
8341
|
+
inputRows: (from, to, total) => `lines ${from}\u2013${to} of ${total}`,
|
|
8342
|
+
queueHint: "waiting for the current answer \xB7 \u2191/\u2193 pick one to edit \xB7 enter puts it back in its place",
|
|
7906
8343
|
configAt: (path15) => `config: ${path15}`,
|
|
7907
8344
|
skillsEmpty: "No skills installed. Drop a folder with a SKILL.md into ~/.kitcode/skills or ./.kitcode/skills",
|
|
7908
8345
|
skillsInstallUsage: "Usage: /skills install <github-url|npm-package|local-path>",
|
|
@@ -7921,7 +8358,7 @@ var en = {
|
|
|
7921
8358
|
mcpDeleted: (name) => `MCP "${name}" disconnected and removed.`,
|
|
7922
8359
|
mcpDeleteFailed: (name, error) => `Could not remove MCP "${name}": ${error}`,
|
|
7923
8360
|
mcpNotFound: (name) => `MCP server "${name}" was not found.`,
|
|
7924
|
-
mcpTools: (
|
|
8361
|
+
mcpTools: (count2) => `${count2} tools`,
|
|
7925
8362
|
mcpEnabled: (name, tools) => `MCP "${name}" enabled \xB7 ${tools} tools`,
|
|
7926
8363
|
mcpDisabled: (name) => `MCP "${name}" disabled.`,
|
|
7927
8364
|
mcpToggleFailed: (name, error) => `Could not change MCP "${name}": ${error}`,
|
|
@@ -7931,14 +8368,24 @@ var en = {
|
|
|
7931
8368
|
error: "error",
|
|
7932
8369
|
disabled: "disabled"
|
|
7933
8370
|
},
|
|
7934
|
-
|
|
7935
|
-
|
|
8371
|
+
moreAgents: (count2) => `+${count2} more`,
|
|
8372
|
+
noSubagents: "No sub-agents in this session yet.",
|
|
8373
|
+
subagentsTitle: "Open a sub-agent chat",
|
|
8374
|
+
subagentRunning: "running",
|
|
8375
|
+
subagentDone: "done",
|
|
8376
|
+
subagentWaiting: "waiting for the first step\u2026",
|
|
8377
|
+
subagentBack: "esc back",
|
|
7936
8378
|
attachUsage: "Usage: /attach <image-or-text-file> \xB7 /attach clipboard \xB7 /attach clear",
|
|
7937
8379
|
attachmentAdded: (name) => `Attached for the next message: ${name}`,
|
|
7938
8380
|
attachmentsCleared: "Pending attachments cleared.",
|
|
7939
8381
|
compactNothing: "Not enough older conversation to compact yet.",
|
|
7940
8382
|
compactStarting: "Compacting context\u2026",
|
|
7941
|
-
compactDone: (
|
|
8383
|
+
compactDone: (summarised, cleared, before, after) => [
|
|
8384
|
+
"Context compacted",
|
|
8385
|
+
summarised > 0 ? `${summarised} older messages replaced with a summary` : "",
|
|
8386
|
+
cleared > 0 ? `${cleared} old tool outputs cleared` : "",
|
|
8387
|
+
before !== void 0 && after !== void 0 ? `~${tokens(before)} \u2192 ~${tokens(after)} tokens` : ""
|
|
8388
|
+
].filter(Boolean).join(" \xB7 ") + ".",
|
|
7942
8389
|
sessionRenameUsage: "Type the title after the command: /sessions rename <id> <title>",
|
|
7943
8390
|
sessionRenamed: (title) => `Session renamed to "${title}".`,
|
|
7944
8391
|
sessionDeleteAsk: "The saved chat file will be deleted. This cannot be undone.",
|
|
@@ -7947,7 +8394,7 @@ var en = {
|
|
|
7947
8394
|
sessionDeleteAllAskBody: "All KitCode sessions from every workspace will be permanently deleted.",
|
|
7948
8395
|
sessionDeleteAllFinalAsk: "Final confirmation: delete all chats?",
|
|
7949
8396
|
sessionDeleteAllFinalBody: "This cannot be undone. Press y only if you want to erase the complete session history.",
|
|
7950
|
-
sessionsDeletedAll: (
|
|
8397
|
+
sessionsDeletedAll: (count2) => `All saved chats deleted \xB7 ${count2} sessions removed.`,
|
|
7951
8398
|
sessionsDeleteAllFailed: (deleted, failed) => `Session cleanup was incomplete \xB7 ${deleted} removed, ${failed} failed.`,
|
|
7952
8399
|
sessionExported: (path15) => `Session exported to ${path15}`,
|
|
7953
8400
|
sessionActionResume: "resume",
|
|
@@ -7960,14 +8407,18 @@ ${url}`,
|
|
|
7960
8407
|
updateCurrent: (version) => `KitCode ${version} is up to date.`,
|
|
7961
8408
|
updateFailed: (reason) => `Could not check for updates: ${reason}.`,
|
|
7962
8409
|
accentSet: (name, hex) => `Accent: ${name} (${hex})`,
|
|
8410
|
+
accentInvalid: (value) => `"${value}" is not a colour. Use a preset name or a hex code like #ff8800 or #f80.`,
|
|
8411
|
+
customAccent: "custom colour\u2026",
|
|
8412
|
+
customAccentPrompt: "Hex colour, e.g. #ff8800 or #f80",
|
|
7963
8413
|
reasoning: (on) => `Reasoning ${on ? "on" : "off"}`,
|
|
8414
|
+
bellState: (on) => on ? "Sound on: a beep when a long answer finishes or a permission is needed." : "Sound off.",
|
|
7964
8415
|
effortSet: (value) => `Effort: ${value}`,
|
|
7965
|
-
budgetCurrent: (
|
|
7966
|
-
budgetSet: (
|
|
8416
|
+
budgetCurrent: (tokens2) => `Token budget: ${tokens2.toLocaleString()} per turn`,
|
|
8417
|
+
budgetSet: (tokens2) => `Token budget set to ${tokens2.toLocaleString()} per turn`,
|
|
7967
8418
|
budgetUnlimited: "Token budget: unlimited (0). Set /budget <tokens> to restore a limit.",
|
|
7968
8419
|
budgetInvalid: "Invalid budget: must be between 1,000 and 10,000,000",
|
|
7969
8420
|
modelSet: (value) => `Model: ${value}`,
|
|
7970
|
-
modelsRefreshed: (provider,
|
|
8421
|
+
modelsRefreshed: (provider, count2) => `${provider}: ${count2} models reloaded from the endpoint.`,
|
|
7971
8422
|
noModels: "No models available. Use /login to add a provider.",
|
|
7972
8423
|
bypassOff: "Approval prompts are back on.",
|
|
7973
8424
|
bypassAsk1: "Disable approval prompts for this session?",
|
|
@@ -7994,14 +8445,14 @@ ${url}`,
|
|
|
7994
8445
|
promptsEmpty: "No saved prompts. Use /prompt save <name>.",
|
|
7995
8446
|
unknownCommand: (name) => `Unknown command /${name}. Try /help`,
|
|
7996
8447
|
didYouMean: (typed, guess) => `Unknown command /${typed}. Did you mean /${guess}?`,
|
|
7997
|
-
undoNothing: "Nothing to undo in this
|
|
8448
|
+
undoNothing: "Nothing to undo: no file changes made by kitcode in this folder.",
|
|
7998
8449
|
undoResult: (restored, removed) => `Undo complete: ${restored} restored, ${removed} newly created removed.`,
|
|
7999
8450
|
undoConflicts: (paths) => `Kept newer changes in: ${paths.join(", ")}`,
|
|
8000
8451
|
undoFailed: (failures) => `Could not restore: ${failures.join("; ")}`,
|
|
8001
8452
|
titleContext: "Context window",
|
|
8002
|
-
contextCurrent: (
|
|
8003
|
-
contextSet: (
|
|
8004
|
-
contextAuto: (
|
|
8453
|
+
contextCurrent: (tokens2) => `Context window: ${tokens2.toLocaleString()} tokens`,
|
|
8454
|
+
contextSet: (tokens2) => `Context window set to ${tokens2.toLocaleString()} tokens. Auto-compact triggers at 80%.`,
|
|
8455
|
+
contextAuto: (tokens2) => tokens2 === null ? "Context window: unknown \u2014 using whatever the provider reports." : `Context window: auto (${tokens2.toLocaleString()} tokens from the provider).`,
|
|
8005
8456
|
contextUnknown: "Context window: unknown. Set it with /context 256k, /context 512k or /context 1m.",
|
|
8006
8457
|
contextInvalid: "Usage: /context 256k | 512k | 1m | <tokens> | auto",
|
|
8007
8458
|
contextCurrentLabel: "current",
|
|
@@ -8014,7 +8465,7 @@ ${url}`,
|
|
|
8014
8465
|
titleMcpDelete: "Remove an MCP server",
|
|
8015
8466
|
titleMcpEnable: "Enable an MCP server",
|
|
8016
8467
|
titleMcpDisable: "Disable an MCP server",
|
|
8017
|
-
modeLabel: { normal: "normal", accept: "auto-accept edits", plan: "plan only" },
|
|
8468
|
+
modeLabel: { normal: "normal", accept: "auto-accept edits", plan: "plan only", bypass: "BYPASS \u2014 no permission prompts" },
|
|
8018
8469
|
modeChanged: (label) => `Mode: ${label} (shift+tab to cycle)`,
|
|
8019
8470
|
cmd: {
|
|
8020
8471
|
model: "switch model",
|
|
@@ -8025,6 +8476,7 @@ ${url}`,
|
|
|
8025
8476
|
refresh: "reload the model list for a provider",
|
|
8026
8477
|
effort: "set reasoning depth",
|
|
8027
8478
|
thinking: "toggle reasoning",
|
|
8479
|
+
bell: "toggle the sound for finished answers and permission prompts",
|
|
8028
8480
|
context: "set or show the model context window",
|
|
8029
8481
|
budget: "set token budget per turn",
|
|
8030
8482
|
theme: "change the accent colour",
|
|
@@ -8058,7 +8510,7 @@ ${url}`,
|
|
|
8058
8510
|
clear: "start a fresh session",
|
|
8059
8511
|
help: "list commands",
|
|
8060
8512
|
exit: "quit",
|
|
8061
|
-
subagents: "
|
|
8513
|
+
subagents: "open a sub-agent and watch its chat live"
|
|
8062
8514
|
}
|
|
8063
8515
|
};
|
|
8064
8516
|
var ru = {
|
|
@@ -8072,10 +8524,12 @@ var ru = {
|
|
|
8072
8524
|
placeholder: "\u0441\u043F\u0440\u043E\u0441\u0438, \u0438\u043B\u0438 / \u0434\u043B\u044F \u043A\u043E\u043C\u0430\u043D\u0434",
|
|
8073
8525
|
working: "\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u2026",
|
|
8074
8526
|
suggestHelp: "\u2191\u2193 \u0432\u044B\u0431\u043E\u0440 \xB7 tab \u2014 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u044C \xB7 enter \u2014 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C",
|
|
8075
|
-
more: (
|
|
8527
|
+
more: (count2) => `\u2026 \u0435\u0449\u0451 ${count2}`,
|
|
8076
8528
|
allowOnce: "\u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043E\u0434\u0438\u043D \u0440\u0430\u0437",
|
|
8077
8529
|
always: "\u0432\u0441\u0435\u0433\u0434\u0430",
|
|
8078
8530
|
deny: "\u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442\u044C",
|
|
8531
|
+
dangerousCommand: "\u0440\u0438\u0441\u043A\u043E\u0432\u0430\u043D\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430",
|
|
8532
|
+
permissionKeys: "\u2191/\u2193 \u0432\u044B\u0431\u0440\u0430\u0442\u044C \xB7 enter \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \xB7 y/a/n \xB7 esc \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442\u044C",
|
|
8079
8533
|
yes: "\u0434\u0430",
|
|
8080
8534
|
noDefault: "\u043D\u0435\u0442 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E)",
|
|
8081
8535
|
noMatches: "\u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E",
|
|
@@ -8090,7 +8544,7 @@ var ru = {
|
|
|
8090
8544
|
titleModelRefresh: "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u0438 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430",
|
|
8091
8545
|
titleSessions: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
8092
8546
|
titleSessionAction: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439",
|
|
8093
|
-
resumed: (id,
|
|
8547
|
+
resumed: (id, count2) => `\u0421\u0435\u0441\u0441\u0438\u044F ${id} \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439: ${count2}`,
|
|
8094
8548
|
sessionsEmpty: "\u0421\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442.",
|
|
8095
8549
|
providerSet: (id, model) => `\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440: ${id} \xB7 \u043C\u043E\u0434\u0435\u043B\u044C ${model}`,
|
|
8096
8550
|
providersEmpty: "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432 \u043D\u0435\u0442. \u0414\u043E\u0431\u0430\u0432\u044C \u0447\u0435\u0440\u0435\u0437 /login.",
|
|
@@ -8098,11 +8552,15 @@ var ru = {
|
|
|
8098
8552
|
providerBalance: (balances) => `\u0431\u0430\u043B\u0430\u043D\u0441 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430: ${balances}`,
|
|
8099
8553
|
providerKeyRemaining: (balances) => `\u043E\u0441\u0442\u0430\u0442\u043E\u043A \u043B\u0438\u043C\u0438\u0442\u0430 API-\u043A\u043B\u044E\u0447\u0430: ${balances}`,
|
|
8100
8554
|
noThink: "\u0431\u0435\u0437 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
8101
|
-
mcpFailed: (
|
|
8555
|
+
mcpFailed: (count2) => `mcp: ${count2} \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439`,
|
|
8102
8556
|
escCancel: "esc \u2014 \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C",
|
|
8557
|
+
escClear: "esc \u2014 \u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u043E\u043B\u0435 \xB7 \u0435\u0449\u0451 \u0440\u0430\u0437 esc \u2014 \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043E\u0442\u0432\u0435\u0442",
|
|
8103
8558
|
escHelp: "esc \u2014 \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u0445\u043E\u0434",
|
|
8104
8559
|
cancelled: "\u041E\u0442\u043C\u0435\u043D\u0435\u043D\u043E.",
|
|
8105
|
-
queued: (
|
|
8560
|
+
queued: (count2) => `\u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438: ${count2}`,
|
|
8561
|
+
pasteToken: (id, lines, chars) => `[\u0432\u0441\u0442\u0430\u0432\u043A\u0430 #${id} \xB7 ${lines > 1 ? `${lines} \u0441\u0442\u0440\u043E\u043A` : `${chars} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`}]`,
|
|
8562
|
+
inputRows: (from, to, total) => `\u0441\u0442\u0440\u043E\u043A\u0438 ${from}\u2013${to} \u0438\u0437 ${total}`,
|
|
8563
|
+
queueHint: "\u0436\u0434\u0443\u0442 \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u043E\u0442\u0432\u0435\u0442\u0430 \xB7 \u2191/\u2193 \u2014 \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u043E\u0434\u043D\u043E \u0434\u043B\u044F \u043F\u0440\u0430\u0432\u043A\u0438 \xB7 enter \u0432\u0435\u0440\u043D\u0451\u0442 \u0435\u0433\u043E \u043D\u0430 \u043C\u0435\u0441\u0442\u043E",
|
|
8106
8564
|
configAt: (path15) => `\u043A\u043E\u043D\u0444\u0438\u0433: ${path15}`,
|
|
8107
8565
|
skillsEmpty: "\u0421\u043A\u0438\u043B\u043B\u043E\u0432 \u043D\u0435\u0442. \u041F\u043E\u043B\u043E\u0436\u0438 \u043F\u0430\u043F\u043A\u0443 \u0441 \u0444\u0430\u0439\u043B\u043E\u043C SKILL.md \u0432 ~/.kitcode/skills \u0438\u043B\u0438 ./.kitcode/skills",
|
|
8108
8566
|
skillsInstallUsage: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /skills install <github-url|npm-\u043F\u0430\u043A\u0435\u0442|\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439-\u043F\u0443\u0442\u044C>",
|
|
@@ -8121,7 +8579,7 @@ var ru = {
|
|
|
8121
8579
|
mcpDeleted: (name) => `MCP \xAB${name}\xBB \u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D \u0438 \u0443\u0434\u0430\u043B\u0451\u043D.`,
|
|
8122
8580
|
mcpDeleteFailed: (name, error) => `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u0434\u0430\u043B\u0438\u0442\u044C MCP \xAB${name}\xBB: ${error}`,
|
|
8123
8581
|
mcpNotFound: (name) => `MCP-\u0441\u0435\u0440\u0432\u0435\u0440 \xAB${name}\xBB \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.`,
|
|
8124
|
-
mcpTools: (
|
|
8582
|
+
mcpTools: (count2) => `\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432: ${count2}`,
|
|
8125
8583
|
mcpEnabled: (name, tools) => `MCP \xAB${name}\xBB \u0432\u043A\u043B\u044E\u0447\u0451\u043D \xB7 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432: ${tools}`,
|
|
8126
8584
|
mcpDisabled: (name) => `MCP \xAB${name}\xBB \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D.`,
|
|
8127
8585
|
mcpToggleFailed: (name, error) => `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C MCP \xAB${name}\xBB: ${error}`,
|
|
@@ -8131,14 +8589,24 @@ var ru = {
|
|
|
8131
8589
|
error: "\u043E\u0448\u0438\u0431\u043A\u0430",
|
|
8132
8590
|
disabled: "\u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D"
|
|
8133
8591
|
},
|
|
8134
|
-
|
|
8135
|
-
|
|
8592
|
+
moreAgents: (count2) => `+${count2} \u0435\u0449\u0451`,
|
|
8593
|
+
noSubagents: "\u0412 \u044D\u0442\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0435\u0449\u0451 \u043D\u0435 \u0431\u044B\u043B\u043E \u0441\u0443\u0431-\u0430\u0433\u0435\u043D\u0442\u043E\u0432.",
|
|
8594
|
+
subagentsTitle: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 \u0441\u0443\u0431-\u0430\u0433\u0435\u043D\u0442\u0430",
|
|
8595
|
+
subagentRunning: "\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442",
|
|
8596
|
+
subagentDone: "\u0433\u043E\u0442\u043E\u0432\u043E",
|
|
8597
|
+
subagentWaiting: "\u0436\u0434\u0451\u043C \u043F\u0435\u0440\u0432\u044B\u0439 \u0448\u0430\u0433\u2026",
|
|
8598
|
+
subagentBack: "esc \u2014 \u043D\u0430\u0437\u0430\u0434",
|
|
8136
8599
|
attachUsage: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /attach <\u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0430-\u0438\u043B\u0438-\u0442\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0439-\u0444\u0430\u0439\u043B> \xB7 /attach clipboard \xB7 /attach clear",
|
|
8137
8600
|
attachmentAdded: (name) => `\u041F\u0440\u0438\u043A\u0440\u0435\u043F\u043B\u0435\u043D\u043E \u043A \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E: ${name}`,
|
|
8138
8601
|
attachmentsCleared: "\u041E\u0436\u0438\u0434\u0430\u044E\u0449\u0438\u0435 \u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0443\u0431\u0440\u0430\u043D\u044B.",
|
|
8139
8602
|
compactNothing: "\u041F\u043E\u043A\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0434\u043B\u044F \u0441\u0436\u0430\u0442\u0438\u044F \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430.",
|
|
8140
8603
|
compactStarting: "\u0421\u0436\u0430\u0442\u0438\u0435 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430\u2026",
|
|
8141
|
-
compactDone: (
|
|
8604
|
+
compactDone: (summarised, cleared, before, after) => [
|
|
8605
|
+
"\u041A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0441\u0436\u0430\u0442",
|
|
8606
|
+
summarised > 0 ? `\u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u043C\u0435\u043D\u0435\u043D\u043E \u0441\u0432\u043E\u0434\u043A\u043E\u0439: ${summarised}` : "",
|
|
8607
|
+
cleared > 0 ? `\u043E\u0447\u0438\u0449\u0435\u043D\u043E \u0441\u0442\u0430\u0440\u044B\u0445 \u0432\u044B\u0432\u043E\u0434\u043E\u0432 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432: ${cleared}` : "",
|
|
8608
|
+
before !== void 0 && after !== void 0 ? `~${tokens(before)} \u2192 ~${tokens(after)} \u0442\u043E\u043A\u0435\u043D\u043E\u0432` : ""
|
|
8609
|
+
].filter(Boolean).join(" \xB7 ") + ".",
|
|
8142
8610
|
sessionRenameUsage: "\u0414\u043E\u043F\u0438\u0448\u0438 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435: /sessions rename <id> <\u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435>",
|
|
8143
8611
|
sessionRenamed: (title) => `\u0421\u0435\u0441\u0441\u0438\u044F \u043F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0430 \u0432 \xAB${title}\xBB.`,
|
|
8144
8612
|
sessionDeleteAsk: "\u0424\u0430\u0439\u043B \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u043E\u0433\u043E \u0447\u0430\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0451\u043D \u0431\u0435\u0437 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F.",
|
|
@@ -8147,7 +8615,7 @@ var ru = {
|
|
|
8147
8615
|
sessionDeleteAllAskBody: "\u0411\u0443\u0434\u0443\u0442 \u043D\u0430\u0432\u0441\u0435\u0433\u0434\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0432\u0441\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 KitCode \u0438\u0437 \u0432\u0441\u0435\u0445 \u0440\u0430\u0431\u043E\u0447\u0438\u0445 \u043F\u0430\u043F\u043E\u043A.",
|
|
8148
8616
|
sessionDeleteAllFinalAsk: "\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435: \u0442\u043E\u0447\u043D\u043E \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u0441\u0435 \u0447\u0430\u0442\u044B?",
|
|
8149
8617
|
sessionDeleteAllFinalBody: "\u042D\u0442\u043E \u043D\u0435\u043B\u044C\u0437\u044F \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C. \u041D\u0430\u0436\u0438\u043C\u0430\u0439 y \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0445\u043E\u0447\u0435\u0448\u044C \u0441\u0442\u0435\u0440\u0435\u0442\u044C \u0432\u0441\u044E \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0441\u0435\u0441\u0441\u0438\u0439.",
|
|
8150
|
-
sessionsDeletedAll: (
|
|
8618
|
+
sessionsDeletedAll: (count2) => `\u0412\u0441\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u0447\u0430\u0442\u044B \u0443\u0434\u0430\u043B\u0435\u043D\u044B \xB7 \u0441\u0435\u0441\u0441\u0438\u0439: ${count2}.`,
|
|
8151
8619
|
sessionsDeleteAllFailed: (deleted, failed) => `\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E \u043D\u0435 \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E \xB7 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${deleted}, \u043E\u0448\u0438\u0431\u043E\u043A: ${failed}.`,
|
|
8152
8620
|
sessionExported: (path15) => `\u0421\u0435\u0441\u0441\u0438\u044F \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0430: ${path15}`,
|
|
8153
8621
|
sessionActionResume: "\u043E\u0442\u043A\u0440\u044B\u0442\u044C",
|
|
@@ -8160,14 +8628,18 @@ ${url}`,
|
|
|
8160
8628
|
updateCurrent: (version) => `\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F KitCode ${version}.`,
|
|
8161
8629
|
updateFailed: (reason) => `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F: ${reason}.`,
|
|
8162
8630
|
accentSet: (name, hex) => `\u0426\u0432\u0435\u0442: ${name} (${hex})`,
|
|
8631
|
+
accentInvalid: (value) => `\xAB${value}\xBB \u2014 \u043D\u0435 \u0446\u0432\u0435\u0442. \u0423\u043A\u0430\u0436\u0438 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0438\u043B\u0438 hex-\u043A\u043E\u0434, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 #ff8800 \u0438\u043B\u0438 #f80.`,
|
|
8632
|
+
customAccent: "\u0441\u0432\u043E\u0439 \u0446\u0432\u0435\u0442\u2026",
|
|
8633
|
+
customAccentPrompt: "Hex-\u043A\u043E\u0434 \u0446\u0432\u0435\u0442\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 #ff8800 \u0438\u043B\u0438 #f80",
|
|
8163
8634
|
reasoning: (on) => `\u0420\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u044F ${on ? "\u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B" : "\u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u044B"}`,
|
|
8635
|
+
bellState: (on) => on ? "\u0417\u0432\u0443\u043A \u0432\u043A\u043B\u044E\u0447\u0451\u043D: \u0441\u0438\u0433\u043D\u0430\u043B, \u043A\u043E\u0433\u0434\u0430 \u0434\u043E\u043B\u0433\u0438\u0439 \u043E\u0442\u0432\u0435\u0442 \u0433\u043E\u0442\u043E\u0432 \u0438\u043B\u0438 \u043D\u0443\u0436\u043D\u043E \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435." : "\u0417\u0432\u0443\u043A \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D.",
|
|
8164
8636
|
effortSet: (value) => `\u0413\u043B\u0443\u0431\u0438\u043D\u0430: ${value}`,
|
|
8165
|
-
budgetCurrent: (
|
|
8166
|
-
budgetSet: (
|
|
8637
|
+
budgetCurrent: (tokens2) => `\u041B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432: ${tokens2.toLocaleString()} \u0437\u0430 \u0445\u043E\u0434`,
|
|
8638
|
+
budgetSet: (tokens2) => `\u041B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D: ${tokens2.toLocaleString()} \u0437\u0430 \u0445\u043E\u0434`,
|
|
8167
8639
|
budgetUnlimited: "\u041B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432: \u0431\u0435\u0437\u043B\u0438\u043C\u0438\u0442\u043D\u044B\u0439 (0). \u0412\u0432\u0435\u0434\u0438 /budget <\u0447\u0438\u0441\u043B\u043E> \u0447\u0442\u043E\u0431\u044B \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u043B\u0438\u043C\u0438\u0442.",
|
|
8168
8640
|
budgetInvalid: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043B\u0438\u043C\u0438\u0442: \u043E\u0442 1 000 \u0434\u043E 10 000 000",
|
|
8169
8641
|
modelSet: (value) => `\u041C\u043E\u0434\u0435\u043B\u044C: ${value}`,
|
|
8170
|
-
modelsRefreshed: (provider,
|
|
8642
|
+
modelsRefreshed: (provider, count2) => `${provider}: \u0441 \u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u0430 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E \u043C\u043E\u0434\u0435\u043B\u0435\u0439 \u2014 ${count2}.`,
|
|
8171
8643
|
noModels: "\u041C\u043E\u0434\u0435\u043B\u0435\u0439 \u043D\u0435\u0442. \u0414\u043E\u0431\u0430\u0432\u044C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 /login.",
|
|
8172
8644
|
bypassOff: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0441\u043D\u043E\u0432\u0430 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B.",
|
|
8173
8645
|
bypassAsk1: "\u041E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u043D\u0430 \u044D\u0442\u0443 \u0441\u0435\u0441\u0441\u0438\u044E?",
|
|
@@ -8194,14 +8666,14 @@ ${url}`,
|
|
|
8194
8666
|
promptsEmpty: "\u0421\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u043C\u0442\u043E\u0432 \u043D\u0435\u0442. \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 /prompt save <\u0438\u043C\u044F>.",
|
|
8195
8667
|
unknownCommand: (name) => `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430 /${name}. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439 /help`,
|
|
8196
8668
|
didYouMean: (typed, guess) => `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430 /${typed}. \u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C /${guess}?`,
|
|
8197
|
-
undoNothing: "\
|
|
8669
|
+
undoNothing: "\u041D\u0435\u0447\u0435\u0433\u043E \u043E\u0442\u043A\u0430\u0442\u044B\u0432\u0430\u0442\u044C: kitcode \u0435\u0449\u0451 \u043D\u0435 \u043C\u0435\u043D\u044F\u043B \u0444\u0430\u0439\u043B\u044B \u0432 \u044D\u0442\u043E\u0439 \u043F\u0430\u043F\u043A\u0435.",
|
|
8198
8670
|
undoResult: (restored, removed) => `\u041E\u0442\u043A\u0430\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D: \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${restored}, \u0443\u0434\u0430\u043B\u0435\u043D\u043E \u043D\u043E\u0432\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432 ${removed}.`,
|
|
8199
8671
|
undoConflicts: (paths) => `\u0411\u043E\u043B\u0435\u0435 \u0441\u0432\u0435\u0436\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B \u0432: ${paths.join(", ")}`,
|
|
8200
8672
|
undoFailed: (failures) => `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C: ${failures.join("; ")}`,
|
|
8201
8673
|
titleContext: "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430",
|
|
8202
|
-
contextCurrent: (
|
|
8203
|
-
contextSet: (
|
|
8204
|
-
contextAuto: (
|
|
8674
|
+
contextCurrent: (tokens2) => `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: ${tokens2.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432`,
|
|
8675
|
+
contextSet: (tokens2) => `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: ${tokens2.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432. \u0410\u0432\u0442\u043E\u0441\u0436\u0430\u0442\u0438\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043D\u0430 80%.`,
|
|
8676
|
+
contextAuto: (tokens2) => tokens2 === null ? "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E \u2014 \u0431\u0435\u0440\u0451\u043C \u0442\u043E, \u0447\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440." : `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: \u0430\u0432\u0442\u043E (${tokens2.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430).`,
|
|
8205
8677
|
contextUnknown: "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E. \u0417\u0430\u0434\u0430\u0439 \u0435\u0433\u043E: /context 256k, /context 512k \u0438\u043B\u0438 /context 1m.",
|
|
8206
8678
|
contextInvalid: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /context 256k | 512k | 1m | <\u0442\u043E\u043A\u0435\u043D\u044B> | auto",
|
|
8207
8679
|
contextCurrentLabel: "\u0442\u0435\u043A\u0443\u0449\u0435\u0435",
|
|
@@ -8214,7 +8686,7 @@ ${url}`,
|
|
|
8214
8686
|
titleMcpDelete: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8215
8687
|
titleMcpEnable: "\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8216
8688
|
titleMcpDisable: "\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8217
|
-
modeLabel: { normal: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439", accept: "\u043F\u0440\u0430\u0432\u043A\u0438 \u0431\u0435\u0437 \u0441\u043F\u0440\u043E\u0441\u0430", plan: "\u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043B\u0430\u043D" },
|
|
8689
|
+
modeLabel: { normal: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439", accept: "\u043F\u0440\u0430\u0432\u043A\u0438 \u0431\u0435\u0437 \u0441\u043F\u0440\u043E\u0441\u0430", plan: "\u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043B\u0430\u043D", bypass: "BYPASS \u2014 \u0431\u0435\u0437 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0439" },
|
|
8218
8690
|
modeChanged: (label) => `\u0420\u0435\u0436\u0438\u043C: ${label} (shift+tab \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C)`,
|
|
8219
8691
|
cmd: {
|
|
8220
8692
|
model: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C",
|
|
@@ -8225,6 +8697,7 @@ ${url}`,
|
|
|
8225
8697
|
refresh: "\u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u043C\u043E\u0434\u0435\u043B\u0435\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430",
|
|
8226
8698
|
effort: "\u0433\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
8227
8699
|
thinking: "\u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u044F",
|
|
8700
|
+
bell: "\u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A \u043E \u0433\u043E\u0442\u043E\u0432\u043E\u043C \u043E\u0442\u0432\u0435\u0442\u0435 \u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u0430\u0445 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0439",
|
|
8228
8701
|
context: "\u0437\u0430\u0434\u0430\u0442\u044C \u0438\u043B\u0438 \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u043C\u043E\u0434\u0435\u043B\u0438",
|
|
8229
8702
|
budget: "\u043B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0437\u0430 \u0445\u043E\u0434 (0 = \u0431\u0435\u0437\u043B\u0438\u043C\u0438\u0442)",
|
|
8230
8703
|
theme: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u0446\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
|
|
@@ -8257,7 +8730,7 @@ ${url}`,
|
|
|
8257
8730
|
clear: "\u043D\u0430\u0447\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
8258
8731
|
help: "\u0441\u043F\u0438\u0441\u043E\u043A \u043A\u043E\u043C\u0430\u043D\u0434",
|
|
8259
8732
|
exit: "\u0432\u044B\u0445\u043E\u0434",
|
|
8260
|
-
subagents: "\
|
|
8733
|
+
subagents: "\u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0441\u0443\u0431-\u0430\u0433\u0435\u043D\u0442\u0430 \u0438 \u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0435\u0433\u043E \u0447\u0430\u0442 \u0432\u0436\u0438\u0432\u0443\u044E"
|
|
8261
8734
|
}
|
|
8262
8735
|
};
|
|
8263
8736
|
var DICTIONARIES = { en, ru };
|
|
@@ -8468,9 +8941,11 @@ function Onboarding({ onSubmit }) {
|
|
|
8468
8941
|
|
|
8469
8942
|
// src/ui/components/PermissionPrompt.tsx
|
|
8470
8943
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
8944
|
+
import { useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
8471
8945
|
|
|
8472
8946
|
// src/ui/components/Diff.tsx
|
|
8473
8947
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
8948
|
+
import stringWidth from "string-width";
|
|
8474
8949
|
|
|
8475
8950
|
// src/ui/diff.ts
|
|
8476
8951
|
var MAX_RENDER_DIFF_CHARS = 5e5;
|
|
@@ -8497,28 +8972,54 @@ function diffBody(before, after) {
|
|
|
8497
8972
|
let added = 0;
|
|
8498
8973
|
let removed = 0;
|
|
8499
8974
|
for (let i = Math.max(0, start - 2); i < start; i += 1) {
|
|
8500
|
-
hunk.push({ kind: "ctx", text: a[i] ?? "" });
|
|
8975
|
+
hunk.push({ kind: "ctx", text: a[i] ?? "", line: i + 1 });
|
|
8501
8976
|
}
|
|
8977
|
+
const dels = [];
|
|
8502
8978
|
for (let i = start; i < endA; i += 1) {
|
|
8503
|
-
|
|
8979
|
+
dels.push({ kind: "del", text: a[i] ?? "", line: i + 1 });
|
|
8504
8980
|
removed += 1;
|
|
8505
8981
|
}
|
|
8982
|
+
const adds = [];
|
|
8506
8983
|
for (let i = start; i < endB; i += 1) {
|
|
8507
|
-
|
|
8984
|
+
adds.push({ kind: "add", text: b[i] ?? "", line: i + 1 });
|
|
8508
8985
|
added += 1;
|
|
8509
8986
|
}
|
|
8987
|
+
if (dels.length === adds.length) {
|
|
8988
|
+
dels.forEach((del, index) => {
|
|
8989
|
+
const add = adds[index];
|
|
8990
|
+
const span = changedSpans(del.text, add.text);
|
|
8991
|
+
if (span) {
|
|
8992
|
+
del.changed = span.before;
|
|
8993
|
+
add.changed = span.after;
|
|
8994
|
+
}
|
|
8995
|
+
});
|
|
8996
|
+
}
|
|
8997
|
+
hunk.push(...dels, ...adds);
|
|
8510
8998
|
for (let i = endA; i < Math.min(a.length, endA + 2); i += 1) {
|
|
8511
|
-
hunk.push({ kind: "ctx", text: a[i] ?? "" });
|
|
8999
|
+
hunk.push({ kind: "ctx", text: a[i] ?? "", line: i + 1 });
|
|
8512
9000
|
}
|
|
8513
9001
|
return { added, removed, hunk };
|
|
8514
9002
|
}
|
|
9003
|
+
function changedSpans(before, after) {
|
|
9004
|
+
if (before === after) return null;
|
|
9005
|
+
let head = 0;
|
|
9006
|
+
while (head < before.length && head < after.length && before[head] === after[head]) head += 1;
|
|
9007
|
+
let tail2 = 0;
|
|
9008
|
+
while (tail2 < before.length - head && tail2 < after.length - head && before[before.length - 1 - tail2] === after[after.length - 1 - tail2]) tail2 += 1;
|
|
9009
|
+
if (head + tail2 < Math.min(before.length, after.length) / 3) return null;
|
|
9010
|
+
return { before: [head, before.length - tail2], after: [head, after.length - tail2] };
|
|
9011
|
+
}
|
|
8515
9012
|
function truncate2(text, width) {
|
|
8516
9013
|
return text.length <= width ? text : `${text.slice(0, width - 1)}\u2026`;
|
|
8517
9014
|
}
|
|
8518
9015
|
|
|
8519
9016
|
// src/ui/components/Diff.tsx
|
|
8520
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
8521
|
-
|
|
9017
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
9018
|
+
var STYLE = {
|
|
9019
|
+
add: { sign: "+", fg: "#86efac", bg: "#173822", strong: "#2c6a40" },
|
|
9020
|
+
del: { sign: "-", fg: "#fca5a5", bg: "#3f1c1c", strong: "#83302d" }
|
|
9021
|
+
};
|
|
9022
|
+
function Diff({ before, after, indent }) {
|
|
8522
9023
|
if (!canRenderDiff(before, after)) {
|
|
8523
9024
|
return /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
8524
9025
|
"Diff preview omitted: it exceeds ",
|
|
@@ -8527,77 +9028,152 @@ function Diff({ before, after }) {
|
|
|
8527
9028
|
] });
|
|
8528
9029
|
}
|
|
8529
9030
|
const { hunk, hidden } = diffLines(sanitizeTerminalText(before), sanitizeTerminalText(after));
|
|
8530
|
-
return /* @__PURE__ */ jsx5(DiffHunk, { lines: hunk, hidden });
|
|
9031
|
+
return /* @__PURE__ */ jsx5(DiffHunk, { lines: hunk, hidden, indent });
|
|
8531
9032
|
}
|
|
8532
|
-
function DiffHunk({ lines, hidden }) {
|
|
9033
|
+
function DiffHunk({ lines, hidden, indent = 2 }) {
|
|
9034
|
+
const { columns } = useTerminalSize();
|
|
9035
|
+
const gutter = Math.max(1, ...lines.map((line) => String(line.line ?? "").length));
|
|
9036
|
+
const width = Math.max(10, (Number.isFinite(columns) ? columns : 80) - indent - gutter - 4);
|
|
8533
9037
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
8534
|
-
lines.map((line, index) =>
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
dimColor:
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
]
|
|
8543
|
-
}
|
|
8544
|
-
|
|
8545
|
-
|
|
9038
|
+
lines.map((line, index) => {
|
|
9039
|
+
const text = sanitizeTerminalText(line.text).replace(/\t/g, " ").replace(/\r$/, "");
|
|
9040
|
+
const number = String(line.line ?? "").padStart(gutter);
|
|
9041
|
+
if (line.kind === "ctx") {
|
|
9042
|
+
return /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
9043
|
+
number,
|
|
9044
|
+
" ",
|
|
9045
|
+
clip2(text, width)
|
|
9046
|
+
] }, index);
|
|
9047
|
+
}
|
|
9048
|
+
const style = STYLE[line.kind];
|
|
9049
|
+
const shown = clip2(text, width);
|
|
9050
|
+
const span = line.changed && shown === text && text === line.text ? line.changed : void 0;
|
|
9051
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
9052
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
9053
|
+
number,
|
|
9054
|
+
" "
|
|
9055
|
+
] }),
|
|
9056
|
+
/* @__PURE__ */ jsx5(Box5, { flexGrow: 1, backgroundColor: style.bg, children: /* @__PURE__ */ jsxs5(Text5, { color: style.fg, wrap: "truncate-end", children: [
|
|
9057
|
+
style.sign,
|
|
9058
|
+
" ",
|
|
9059
|
+
span ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
9060
|
+
shown.slice(0, span[0]),
|
|
9061
|
+
/* @__PURE__ */ jsx5(Text5, { backgroundColor: style.strong, bold: true, children: shown.slice(span[0], span[1]) }),
|
|
9062
|
+
shown.slice(span[1])
|
|
9063
|
+
] }) : shown
|
|
9064
|
+
] }) })
|
|
9065
|
+
] }, index);
|
|
9066
|
+
}),
|
|
8546
9067
|
hidden > 0 && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
8547
|
-
"
|
|
9068
|
+
" ".repeat(gutter),
|
|
9069
|
+
" \u2026 ",
|
|
8548
9070
|
hidden,
|
|
8549
9071
|
" more lines"
|
|
8550
9072
|
] })
|
|
8551
9073
|
] });
|
|
8552
9074
|
}
|
|
9075
|
+
function clip2(text, width) {
|
|
9076
|
+
if (stringWidth(text) <= width) return text;
|
|
9077
|
+
let out = "";
|
|
9078
|
+
for (const character of text) {
|
|
9079
|
+
if (stringWidth(out + character) > width - 1) break;
|
|
9080
|
+
out += character;
|
|
9081
|
+
}
|
|
9082
|
+
return `${out}\u2026`;
|
|
9083
|
+
}
|
|
8553
9084
|
|
|
8554
9085
|
// src/ui/components/PermissionPrompt.tsx
|
|
8555
|
-
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
9086
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8556
9087
|
var MAX_PREVIEW_CHARS = 2e4;
|
|
9088
|
+
var ENTER_GRACE_MS = 400;
|
|
9089
|
+
var DANGEROUS = [
|
|
9090
|
+
/\brm\s+(?:-[a-z]*[rf][a-z]*\s+)+/i,
|
|
9091
|
+
/\b(?:rmdir|del|erase|rd)\s+\/[sq]/i,
|
|
9092
|
+
/\bRemove-Item\b.*-Recurse/i,
|
|
9093
|
+
/\bgit\s+(?:push\s+.*(?:--force|-f\b)|reset\s+--hard|clean\s+-[a-z]*f|checkout\s+--\s|branch\s+-D)/i,
|
|
9094
|
+
/\b(?:sudo|doas|runas)\b/i,
|
|
9095
|
+
/\b(?:curl|wget|iwr|Invoke-WebRequest)\b[^|]*\|\s*(?:sh|bash|zsh|iex|Invoke-Expression|python)/i,
|
|
9096
|
+
/\b(?:mkfs|format|diskpart|dd\s+if=)/i,
|
|
9097
|
+
/\bchmod\s+(?:-R\s+)?777\b/i,
|
|
9098
|
+
/>\s*\/dev\/(?:sd|nvme|disk)/i,
|
|
9099
|
+
/\bDROP\s+(?:TABLE|DATABASE)\b/i
|
|
9100
|
+
];
|
|
9101
|
+
function isDangerousCommand(command) {
|
|
9102
|
+
return DANGEROUS.some((pattern) => pattern.test(command));
|
|
9103
|
+
}
|
|
8557
9104
|
function PermissionPrompt({ request, onDecide }) {
|
|
8558
9105
|
const theme = useTheme();
|
|
8559
9106
|
const strings = useStrings();
|
|
9107
|
+
const options = [
|
|
9108
|
+
{ decision: "once", label: strings.allowOnce, key: "y" },
|
|
9109
|
+
...request.allowAlways !== false ? [{ decision: "always", label: `${strings.always} (${sanitizeTerminalText(request.toolName)})`, key: "a" }] : [],
|
|
9110
|
+
{ decision: "deny", label: strings.deny, key: "n" }
|
|
9111
|
+
];
|
|
9112
|
+
const command = commandOf(request.input);
|
|
9113
|
+
const dangerous = command !== void 0 && isDangerousCommand(command);
|
|
9114
|
+
const [selected, setSelected] = useState3(dangerous ? options.length - 1 : 0);
|
|
9115
|
+
const shownAt = useRef2(Date.now());
|
|
9116
|
+
useEffect(() => {
|
|
9117
|
+
shownAt.current = Date.now();
|
|
9118
|
+
setSelected(dangerous ? options.length - 1 : 0);
|
|
9119
|
+
}, [request]);
|
|
8560
9120
|
useTerminalInput((char, key) => {
|
|
8561
9121
|
const c = normalizeHotkey(char);
|
|
8562
|
-
if (
|
|
8563
|
-
else if (
|
|
8564
|
-
else if (
|
|
9122
|
+
if (key.upArrow) setSelected((index) => (index - 1 + options.length) % options.length);
|
|
9123
|
+
else if (key.downArrow || key.tab) setSelected((index) => (index + 1) % options.length);
|
|
9124
|
+
else if (key.escape) onDecide("deny");
|
|
9125
|
+
else if (key.return) {
|
|
9126
|
+
if (Date.now() - shownAt.current >= ENTER_GRACE_MS) onDecide(options[selected].decision);
|
|
9127
|
+
} else {
|
|
9128
|
+
const byKey = options.find((option) => option.key === c);
|
|
9129
|
+
if (byKey) onDecide(byKey.decision);
|
|
9130
|
+
}
|
|
8565
9131
|
});
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
] }),
|
|
8572
|
-
request.display?.kind === "text" && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: previewText(request.display.text) }),
|
|
8573
|
-
request.display?.kind === "list" && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8574
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: sanitizeTerminalText(request.display.title) }),
|
|
8575
|
-
request.display.items.slice(0, 100).map((item, index) => /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8576
|
-
"\u2022 ",
|
|
8577
|
-
previewText(item, 1e3)
|
|
8578
|
-
] }, index))
|
|
9132
|
+
const frame = dangerous ? theme.error : theme.warn;
|
|
9133
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: frame, paddingX: 1, children: [
|
|
9134
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, color: frame, children: [
|
|
9135
|
+
dangerous ? `\u26A0 ${strings.dangerousCommand} \xB7 ` : "",
|
|
9136
|
+
sanitizeTerminalText(request.summary)
|
|
8579
9137
|
] }),
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
request.allowAlways !== false && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
8588
|
-
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "a" }),
|
|
8589
|
-
" ",
|
|
8590
|
-
strings.always,
|
|
8591
|
-
" (",
|
|
8592
|
-
sanitizeTerminalText(request.toolName),
|
|
8593
|
-
") \xB7 "
|
|
9138
|
+
command !== void 0 ? /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: dangerous ? theme.error : void 0, children: [
|
|
9139
|
+
"$ ",
|
|
9140
|
+
previewText(command, 2e3)
|
|
9141
|
+
] }) }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
9142
|
+
request.display?.kind === "diff" && /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
9143
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: sanitizeTerminalText(request.display.path) }),
|
|
9144
|
+
/* @__PURE__ */ jsx6(Diff, { before: request.display.before, after: request.display.after, indent: 4 })
|
|
8594
9145
|
] }),
|
|
8595
|
-
/* @__PURE__ */ jsx6(Text6, {
|
|
8596
|
-
" ",
|
|
8597
|
-
|
|
8598
|
-
|
|
9146
|
+
request.display?.kind === "text" && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: previewText(request.display.text) }),
|
|
9147
|
+
request.display?.kind === "list" && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9148
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: sanitizeTerminalText(request.display.title) }),
|
|
9149
|
+
request.display.items.slice(0, 100).map((item, index) => /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
9150
|
+
"\u2022 ",
|
|
9151
|
+
previewText(item, 1e3)
|
|
9152
|
+
] }, index))
|
|
9153
|
+
] }),
|
|
9154
|
+
!request.display && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: printInput(request.input) })
|
|
9155
|
+
] }),
|
|
9156
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
9157
|
+
options.map((option, index) => {
|
|
9158
|
+
const active2 = index === selected;
|
|
9159
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: active2 ? frame : void 0, bold: active2, dimColor: !active2, children: [
|
|
9160
|
+
active2 ? "\u276F " : " ",
|
|
9161
|
+
option.label,
|
|
9162
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
9163
|
+
" (",
|
|
9164
|
+
option.key,
|
|
9165
|
+
")"
|
|
9166
|
+
] })
|
|
9167
|
+
] }, option.decision);
|
|
9168
|
+
}),
|
|
9169
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: strings.permissionKeys })
|
|
9170
|
+
] })
|
|
8599
9171
|
] });
|
|
8600
9172
|
}
|
|
9173
|
+
function commandOf(input) {
|
|
9174
|
+
const command = input?.command;
|
|
9175
|
+
return typeof command === "string" && command.trim() !== "" ? command : void 0;
|
|
9176
|
+
}
|
|
8601
9177
|
function printInput(input) {
|
|
8602
9178
|
try {
|
|
8603
9179
|
return previewText(JSON.stringify(input, null, 2));
|
|
@@ -8613,15 +9189,15 @@ function previewText(value, limit = MAX_PREVIEW_CHARS) {
|
|
|
8613
9189
|
|
|
8614
9190
|
// src/ui/components/Picker.tsx
|
|
8615
9191
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
8616
|
-
import { useMemo as useMemo2, useState as
|
|
9192
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
8617
9193
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8618
9194
|
var WINDOW = 10;
|
|
8619
9195
|
function Picker({ title, items, onSelect, onCancel }) {
|
|
8620
9196
|
const theme = useTheme();
|
|
8621
9197
|
const { columns } = useTerminalSize();
|
|
8622
9198
|
const strings = useStrings();
|
|
8623
|
-
const [query, setQuery] =
|
|
8624
|
-
const [cursor, setCursor] =
|
|
9199
|
+
const [query, setQuery] = useState4("");
|
|
9200
|
+
const [cursor, setCursor] = useState4(0);
|
|
8625
9201
|
const matches = useMemo2(() => {
|
|
8626
9202
|
const needle = query.toLowerCase();
|
|
8627
9203
|
if (needle === "") return items;
|
|
@@ -8673,7 +9249,8 @@ function Picker({ title, items, onSelect, onCancel }) {
|
|
|
8673
9249
|
|
|
8674
9250
|
// src/ui/components/PromptInput.tsx
|
|
8675
9251
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
8676
|
-
import { memo, useEffect, useRef as
|
|
9252
|
+
import { memo, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef3, useState as useState5 } from "react";
|
|
9253
|
+
import stringWidth2 from "string-width";
|
|
8677
9254
|
|
|
8678
9255
|
// src/ui/history.ts
|
|
8679
9256
|
function moveInputHistory(history, index, value, draft, direction) {
|
|
@@ -8714,6 +9291,10 @@ var CAT_FRAMES = [
|
|
|
8714
9291
|
"/\u1420\uFF61\uA788\uFF61\u141F\\ \u223E"
|
|
8715
9292
|
];
|
|
8716
9293
|
var MAX_INPUT_ROWS = 6;
|
|
9294
|
+
var PASTE_COLLAPSE_CHARS = 1e3;
|
|
9295
|
+
var PASTE_COLLAPSE_LINES = 10;
|
|
9296
|
+
var VISIBLE_QUEUED = 3;
|
|
9297
|
+
var MODE_COLORS = { plan: "#60a5fa", accept: "#fbbf24" };
|
|
8717
9298
|
var PromptInput = memo(function PromptInput2({
|
|
8718
9299
|
value,
|
|
8719
9300
|
onChange,
|
|
@@ -8725,37 +9306,48 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8725
9306
|
hint,
|
|
8726
9307
|
history,
|
|
8727
9308
|
attachments = [],
|
|
8728
|
-
onListFiles
|
|
9309
|
+
onListFiles,
|
|
9310
|
+
mode = "normal",
|
|
9311
|
+
bypass = false,
|
|
9312
|
+
queued = [],
|
|
9313
|
+
onNavigateQueue
|
|
8729
9314
|
}) {
|
|
8730
9315
|
const theme = useTheme();
|
|
8731
9316
|
const { columns } = useTerminalSize();
|
|
8732
9317
|
const panelWidth = Math.max(2, columns - 1);
|
|
8733
9318
|
const inputRows = MAX_INPUT_ROWS;
|
|
8734
9319
|
const strings = useStrings();
|
|
8735
|
-
const [catFrame, setCatFrame] =
|
|
8736
|
-
|
|
9320
|
+
const [catFrame, setCatFrame] = useState5(0);
|
|
9321
|
+
useEffect2(() => {
|
|
8737
9322
|
if (!disabled) return;
|
|
8738
9323
|
const timer = setInterval(() => setCatFrame((frame) => (frame + 1) % CAT_FRAMES.length), CAT_FRAME_MS);
|
|
8739
9324
|
return () => clearInterval(timer);
|
|
8740
9325
|
}, [disabled]);
|
|
8741
|
-
const
|
|
9326
|
+
const modeColor = bypass ? theme.error : mode === "plan" ? MODE_COLORS.plan : mode === "accept" ? MODE_COLORS.accept : void 0;
|
|
9327
|
+
const panelColor = disabled ? theme.warn : modeColor ?? theme.accent;
|
|
9328
|
+
const modeTitle = bypass ? "BYPASS" : mode !== "normal" ? strings.modeLabel[mode] ?? mode : "";
|
|
8742
9329
|
const fullTitle = disabled ? `KitCode ${CAT_FRAMES[catFrame]}` : "KitCode";
|
|
8743
9330
|
const panelTitle = fullTitle.slice(0, Math.max(0, panelWidth - 5));
|
|
8744
9331
|
const safeValue = sanitizeTerminalText(value);
|
|
8745
|
-
const [selectionCursor, setSelectionCursor] =
|
|
8746
|
-
const [inputCursor, setInputCursor] =
|
|
8747
|
-
const [historyIndex, setHistoryIndex] =
|
|
8748
|
-
const historyDraft =
|
|
8749
|
-
const pendingValue =
|
|
8750
|
-
const valueRef =
|
|
8751
|
-
const cursorRef =
|
|
9332
|
+
const [selectionCursor, setSelectionCursor] = useState5(0);
|
|
9333
|
+
const [inputCursor, setInputCursor] = useState5(() => characters2(safeValue).length);
|
|
9334
|
+
const [historyIndex, setHistoryIndex] = useState5(null);
|
|
9335
|
+
const historyDraft = useRef3("");
|
|
9336
|
+
const pendingValue = useRef3(null);
|
|
9337
|
+
const valueRef = useRef3(safeValue);
|
|
9338
|
+
const cursorRef = useRef3(inputCursor);
|
|
8752
9339
|
valueRef.current = safeValue;
|
|
8753
9340
|
cursorRef.current = inputCursor;
|
|
9341
|
+
const valueChars = useMemo3(() => characters2(safeValue), [safeValue]);
|
|
9342
|
+
const inputView = valueChars.length === 0 ? null : inputWindow(valueChars, clamp2(inputCursor, 0, valueChars.length), panelWidth - 4, inputRows);
|
|
9343
|
+
const position = inputView && inputView.total > inputRows ? strings.inputRows(inputView.first + 1, inputView.first + inputView.rows.length, inputView.total) : "";
|
|
9344
|
+
const footerText = [modeTitle && `${modeTitle} \xB7 shift+tab`, position].filter(Boolean).join(" \xB7 ");
|
|
9345
|
+
const footer = footerText ? `\u2500 ${footerText} ` : "";
|
|
8754
9346
|
const commandSuggestions = matchCommands(safeValue);
|
|
8755
9347
|
const mention = onListFiles ? mentionSpan(safeValue, inputCursor) : null;
|
|
8756
|
-
const [files, setFiles] =
|
|
9348
|
+
const [files, setFiles] = useState5([]);
|
|
8757
9349
|
const mentionQuery = mention?.query ?? null;
|
|
8758
|
-
|
|
9350
|
+
useEffect2(() => {
|
|
8759
9351
|
if (mentionQuery === null || !onListFiles) {
|
|
8760
9352
|
setFiles([]);
|
|
8761
9353
|
return;
|
|
@@ -8773,10 +9365,10 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8773
9365
|
const open3 = suggestions.length > 0 || showFiles;
|
|
8774
9366
|
const optionCount = showFiles ? files.length : suggestions.length;
|
|
8775
9367
|
const active2 = Math.min(selectionCursor, Math.max(0, optionCount - 1));
|
|
8776
|
-
|
|
9368
|
+
useEffect2(() => {
|
|
8777
9369
|
setSelectionCursor(0);
|
|
8778
9370
|
}, [safeValue]);
|
|
8779
|
-
|
|
9371
|
+
useEffect2(() => {
|
|
8780
9372
|
const length = characters2(safeValue).length;
|
|
8781
9373
|
if (pendingValue.current === safeValue) {
|
|
8782
9374
|
pendingValue.current = null;
|
|
@@ -8796,28 +9388,39 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8796
9388
|
historyDraft.current = next;
|
|
8797
9389
|
onChange(next);
|
|
8798
9390
|
};
|
|
9391
|
+
const pastes = useRef3(/* @__PURE__ */ new Map());
|
|
9392
|
+
const pasteCounter = useRef3(0);
|
|
8799
9393
|
const submit = (next) => {
|
|
8800
9394
|
setHistoryIndex(null);
|
|
8801
9395
|
historyDraft.current = "";
|
|
8802
|
-
|
|
9396
|
+
const expanded = expandPastes(next, pastes.current);
|
|
9397
|
+
pastes.current = /* @__PURE__ */ new Map();
|
|
9398
|
+
onSubmit(expanded);
|
|
8803
9399
|
};
|
|
8804
9400
|
const insertPastedText = (pasted) => {
|
|
8805
9401
|
const inserted = characters2(sanitizeTerminalText(pasted));
|
|
8806
9402
|
if (inserted.length === 0) return;
|
|
8807
9403
|
const valueCharacters = characters2(valueRef.current);
|
|
8808
9404
|
const cursor = clamp2(cursorRef.current, 0, valueCharacters.length);
|
|
8809
|
-
valueCharacters.
|
|
8810
|
-
change(
|
|
9405
|
+
const next = valueCharacters.slice(0, cursor).concat(inserted, valueCharacters.slice(cursor));
|
|
9406
|
+
change(next.join(""), cursor + inserted.length);
|
|
9407
|
+
};
|
|
9408
|
+
const collapsePaste = (text) => {
|
|
9409
|
+
const lines = text.split("\n").length;
|
|
9410
|
+
if (text.length < PASTE_COLLAPSE_CHARS && lines <= PASTE_COLLAPSE_LINES) return text;
|
|
9411
|
+
pasteCounter.current += 1;
|
|
9412
|
+
pastes.current.set(pasteCounter.current, text);
|
|
9413
|
+
return strings.pasteToken(pasteCounter.current, lines, text.length);
|
|
8811
9414
|
};
|
|
8812
9415
|
useTerminalPaste((pasted) => {
|
|
8813
9416
|
const safePaste = sanitizeTerminalText(pasted);
|
|
8814
9417
|
if (onPastePath && looksLikeAttachmentPath(safePaste)) {
|
|
8815
9418
|
void onPastePath(safePaste).then((consumed) => {
|
|
8816
|
-
if (!consumed) insertPastedText(safePaste);
|
|
9419
|
+
if (!consumed) insertPastedText(collapsePaste(safePaste));
|
|
8817
9420
|
});
|
|
8818
9421
|
return;
|
|
8819
9422
|
}
|
|
8820
|
-
insertPastedText(safePaste);
|
|
9423
|
+
insertPastedText(collapsePaste(safePaste));
|
|
8821
9424
|
});
|
|
8822
9425
|
useTerminalInput((input, key) => {
|
|
8823
9426
|
if (onPasteImage && (key.ctrl || key.meta || key.super) && (input.toLowerCase() === "v" || input === "")) {
|
|
@@ -8850,6 +9453,13 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8850
9453
|
if (chosen) submit(`/${chosen.name}`);
|
|
8851
9454
|
return;
|
|
8852
9455
|
}
|
|
9456
|
+
if (!open3 && (key.upArrow || key.downArrow) && onNavigateQueue) {
|
|
9457
|
+
const next = onNavigateQueue(key.upArrow ? "previous" : "next", safeValue);
|
|
9458
|
+
if (next !== null) {
|
|
9459
|
+
change(next);
|
|
9460
|
+
return;
|
|
9461
|
+
}
|
|
9462
|
+
}
|
|
8853
9463
|
if (!open3 && (key.upArrow || key.downArrow)) {
|
|
8854
9464
|
const moved = moveInputHistory(
|
|
8855
9465
|
history,
|
|
@@ -8904,6 +9514,21 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8904
9514
|
const visible = suggestions.slice(start, start + WINDOW2);
|
|
8905
9515
|
const fileStart = Math.max(0, Math.min(active2 - WINDOW2 + 2, files.length - WINDOW2));
|
|
8906
9516
|
return /* @__PURE__ */ jsxs8(Box8, { width: panelWidth, maxWidth: "100%", flexDirection: "column", marginTop: 1, flexShrink: 0, children: [
|
|
9517
|
+
queued.length > 0 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
9518
|
+
queued.slice(0, VISIBLE_QUEUED).map((text, index) => /* @__PURE__ */ jsxs8(Text8, { dimColor: true, wrap: "truncate-end", children: [
|
|
9519
|
+
" ",
|
|
9520
|
+
"\u23F3 ",
|
|
9521
|
+
sanitizeTerminalText(text).replace(/\s+/g, " ").trim()
|
|
9522
|
+
] }, index)),
|
|
9523
|
+
queued.length > VISIBLE_QUEUED && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
9524
|
+
" ",
|
|
9525
|
+
strings.more(queued.length - VISIBLE_QUEUED)
|
|
9526
|
+
] }),
|
|
9527
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
9528
|
+
" ",
|
|
9529
|
+
strings.queueHint
|
|
9530
|
+
] })
|
|
9531
|
+
] }),
|
|
8907
9532
|
/* @__PURE__ */ jsxs8(Box8, { width: "100%", flexShrink: 0, children: [
|
|
8908
9533
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256D" }) }),
|
|
8909
9534
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsxs8(Text8, { color: panelColor, wrap: "truncate-end", children: [
|
|
@@ -8929,12 +9554,8 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8929
9554
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256E" }) })
|
|
8930
9555
|
] }),
|
|
8931
9556
|
/* @__PURE__ */ jsxs8(Box8, { width: "100%", paddingX: 1, maxHeight: inputRows, overflowY: "hidden", children: [
|
|
8932
|
-
/* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u203A " }),
|
|
8933
|
-
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(EditableText, {
|
|
8934
|
-
pending && pending > 0 ? /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
8935
|
-
" \xB7 ",
|
|
8936
|
-
strings.queued(pending)
|
|
8937
|
-
] }) : null
|
|
9557
|
+
/* @__PURE__ */ jsx8(Text8, { color: panelColor, bold: true, children: "\u203A " }),
|
|
9558
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(EditableText, { window: inputView, placeholder: strings.placeholder }) })
|
|
8938
9559
|
] }),
|
|
8939
9560
|
attachments.length > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
8940
9561
|
" ",
|
|
@@ -8958,7 +9579,8 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8958
9579
|
borderRight: false
|
|
8959
9580
|
}
|
|
8960
9581
|
),
|
|
8961
|
-
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor,
|
|
9582
|
+
footer && /* @__PURE__ */ jsx8(Box8, { flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, bold: true, wrap: "truncate-start", children: footer.slice(0, Math.max(0, panelWidth - 6)) }) }),
|
|
9583
|
+
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: footer ? "\u2500\u256F" : "\u256F" }) })
|
|
8962
9584
|
] }),
|
|
8963
9585
|
disabled && hint && /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, children: [
|
|
8964
9586
|
" ",
|
|
@@ -8995,25 +9617,81 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8995
9617
|
] })
|
|
8996
9618
|
] });
|
|
8997
9619
|
});
|
|
8998
|
-
function
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9620
|
+
function inputWindow(chars, cursor, width, maxRows) {
|
|
9621
|
+
const limit = Math.max(1, width - 1);
|
|
9622
|
+
const rows = [{ chars: [], start: 0 }];
|
|
9623
|
+
let rowWidth = 0;
|
|
9624
|
+
let cursorRow = -1;
|
|
9625
|
+
let cursorCol = 0;
|
|
9626
|
+
for (let index = 0; index <= chars.length; index += 1) {
|
|
9627
|
+
const row = rows[rows.length - 1];
|
|
9628
|
+
if (index === cursor) {
|
|
9629
|
+
cursorRow = rows.length - 1;
|
|
9630
|
+
cursorCol = row.chars.length;
|
|
9631
|
+
}
|
|
9632
|
+
if (index === chars.length) break;
|
|
9633
|
+
const character = chars[index];
|
|
9634
|
+
if (character === "\n") {
|
|
9635
|
+
rows.push({ chars: [], start: index + 1 });
|
|
9636
|
+
rowWidth = 0;
|
|
9637
|
+
continue;
|
|
9638
|
+
}
|
|
9639
|
+
const cells = charWidth(character);
|
|
9640
|
+
if (rowWidth + cells > limit && row.chars.length > 0) {
|
|
9641
|
+
rows.push({ chars: [character], start: index });
|
|
9642
|
+
rowWidth = cells;
|
|
9643
|
+
if (cursorRow === rows.length - 2 && cursorCol === row.chars.length) {
|
|
9644
|
+
cursorRow = rows.length - 1;
|
|
9645
|
+
cursorCol = 0;
|
|
9646
|
+
}
|
|
9647
|
+
continue;
|
|
9648
|
+
}
|
|
9649
|
+
row.chars.push(character);
|
|
9650
|
+
rowWidth += cells;
|
|
9651
|
+
}
|
|
9652
|
+
if (cursorRow === -1) {
|
|
9653
|
+
cursorRow = rows.length - 1;
|
|
9654
|
+
cursorCol = rows[cursorRow].chars.length;
|
|
9655
|
+
}
|
|
9656
|
+
let first2 = Math.max(0, rows.length - maxRows);
|
|
9657
|
+
if (cursorRow < first2) first2 = cursorRow;
|
|
9658
|
+
if (cursorRow >= first2 + maxRows) first2 = cursorRow - maxRows + 1;
|
|
9659
|
+
return {
|
|
9660
|
+
rows: rows.slice(first2, first2 + maxRows).map((row, offset) => ({
|
|
9661
|
+
text: row.chars.join(""),
|
|
9662
|
+
...first2 + offset === cursorRow ? { cursor: cursorCol } : {}
|
|
9663
|
+
})),
|
|
9664
|
+
first: first2,
|
|
9665
|
+
total: rows.length
|
|
9666
|
+
};
|
|
9667
|
+
}
|
|
9668
|
+
function charWidth(character) {
|
|
9669
|
+
const code = character.codePointAt(0) ?? 0;
|
|
9670
|
+
if (code >= 32 && code < 768 || code >= 880 && code < 4352) return 1;
|
|
9671
|
+
return stringWidth2(character);
|
|
9672
|
+
}
|
|
9673
|
+
function EditableText({ window, placeholder }) {
|
|
9674
|
+
if (!window) {
|
|
9006
9675
|
const placeholderCharacters = characters2(sanitizeTerminalText(placeholder));
|
|
9007
9676
|
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9008
9677
|
/* @__PURE__ */ jsx8(Text8, { inverse: true, children: placeholderCharacters[0] ?? " " }),
|
|
9009
9678
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: placeholderCharacters.slice(1).join("") })
|
|
9010
9679
|
] });
|
|
9011
9680
|
}
|
|
9012
|
-
return /* @__PURE__ */
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9681
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: window.rows.map((row, index) => {
|
|
9682
|
+
if (row.cursor === void 0) return /* @__PURE__ */ jsx8(Text8, { children: row.text || " " }, index);
|
|
9683
|
+
const chars = characters2(row.text);
|
|
9684
|
+
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9685
|
+
chars.slice(0, row.cursor).join(""),
|
|
9686
|
+
/* @__PURE__ */ jsx8(Text8, { inverse: true, children: chars[row.cursor] ?? " " }),
|
|
9687
|
+
chars.slice(row.cursor + 1).join("")
|
|
9688
|
+
] }, index);
|
|
9689
|
+
}) });
|
|
9690
|
+
}
|
|
9691
|
+
var PASTE_TOKEN = /\[(?:paste|вставка) #(\d+)[^\]\n]*\]/g;
|
|
9692
|
+
function expandPastes(text, pastes) {
|
|
9693
|
+
if (pastes.size === 0) return text;
|
|
9694
|
+
return text.replace(PASTE_TOKEN, (token, id) => pastes.get(Number(id)) ?? token);
|
|
9017
9695
|
}
|
|
9018
9696
|
function nextCursor(cursor, length, key) {
|
|
9019
9697
|
if (key.home) return 0;
|
|
@@ -9031,7 +9709,6 @@ function clamp2(value, min, max) {
|
|
|
9031
9709
|
|
|
9032
9710
|
// src/ui/components/StatusBar.tsx
|
|
9033
9711
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
9034
|
-
import { Fragment as Fragment2 } from "react";
|
|
9035
9712
|
|
|
9036
9713
|
// src/ui/time.ts
|
|
9037
9714
|
function formatDuration(ms) {
|
|
@@ -9047,32 +9724,31 @@ function formatDuration(ms) {
|
|
|
9047
9724
|
// src/ui/components/StatusBar.tsx
|
|
9048
9725
|
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
9049
9726
|
var BAR_WIDTH = 12;
|
|
9727
|
+
var VISIBLE_AGENTS = 3;
|
|
9728
|
+
var AGENT_LABEL_WIDTH = 28;
|
|
9729
|
+
function shortTask(task) {
|
|
9730
|
+
const label = sanitizeTerminalText(task).replace(/\s+/g, " ").trim() || "subagent";
|
|
9731
|
+
return label.length <= AGENT_LABEL_WIDTH ? label : `${label.slice(0, AGENT_LABEL_WIDTH - 1)}\u2026`;
|
|
9732
|
+
}
|
|
9050
9733
|
function StatusBar({ status }) {
|
|
9051
9734
|
const theme = useTheme();
|
|
9052
9735
|
const strings = useStrings();
|
|
9053
|
-
const primary = [];
|
|
9054
9736
|
const details = [];
|
|
9055
|
-
|
|
9056
|
-
|
|
9737
|
+
details.push(/* @__PURE__ */ jsx9(Text9, { color: theme.accent, bold: true, children: shortModel(status.modelRef) }, "model"));
|
|
9738
|
+
details.push(/* @__PURE__ */ jsx9(EffortLabel, { effort: status.effort, effective: status.effectiveEffort }, "effort"));
|
|
9057
9739
|
if (!status.thinking) {
|
|
9058
9740
|
details.push(
|
|
9059
9741
|
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: strings.noThink }, "think")
|
|
9060
9742
|
);
|
|
9061
9743
|
}
|
|
9062
|
-
if (status.
|
|
9063
|
-
const modeColor = status.mode === "accept" ? "yellow" : status.mode === "plan" ? "blue" : theme.accent;
|
|
9064
|
-
details.push(
|
|
9065
|
-
/* @__PURE__ */ jsx9(Text9, { color: modeColor, children: strings.modeLabel[status.mode] ?? status.mode }, "mode")
|
|
9066
|
-
);
|
|
9067
|
-
}
|
|
9068
|
-
if (status.usage) {
|
|
9744
|
+
if (status.usage && status.usage.totalTokens > 0) {
|
|
9069
9745
|
const u = status.usage;
|
|
9070
9746
|
details.push(
|
|
9071
9747
|
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9072
9748
|
/* @__PURE__ */ jsx9(Text9, { color: "white", bold: true, children: u.input }),
|
|
9073
|
-
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "
|
|
9749
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2191 " }),
|
|
9074
9750
|
/* @__PURE__ */ jsx9(Text9, { color: theme.ok, children: u.output }),
|
|
9075
|
-
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "
|
|
9751
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2193" }),
|
|
9076
9752
|
u.cache && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
9077
9753
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " (" }),
|
|
9078
9754
|
/* @__PURE__ */ jsxs9(Text9, { color: "gray", children: [
|
|
@@ -9088,6 +9764,15 @@ function StatusBar({ status }) {
|
|
|
9088
9764
|
] }, "usage")
|
|
9089
9765
|
);
|
|
9090
9766
|
}
|
|
9767
|
+
if (status.speed) {
|
|
9768
|
+
const rate = status.speed.tokensPerSecond;
|
|
9769
|
+
details.push(
|
|
9770
|
+
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9771
|
+
/* @__PURE__ */ jsx9(Text9, { color: status.speed.live ? theme.accent : "white", bold: !status.speed.live, children: `${status.speed.live ? "~" : ""}${rate < 10 ? rate.toFixed(1) : Math.round(rate)}` }),
|
|
9772
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " tok/s" })
|
|
9773
|
+
] }, "speed")
|
|
9774
|
+
);
|
|
9775
|
+
}
|
|
9091
9776
|
details.push(
|
|
9092
9777
|
/* @__PURE__ */ jsx9(
|
|
9093
9778
|
Text9,
|
|
@@ -9099,14 +9784,14 @@ function StatusBar({ status }) {
|
|
|
9099
9784
|
"elapsed"
|
|
9100
9785
|
)
|
|
9101
9786
|
);
|
|
9102
|
-
if (status.
|
|
9103
|
-
details.push(
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
);
|
|
9787
|
+
if (status.mcp.connected > 0) {
|
|
9788
|
+
details.push(/* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
9789
|
+
"mcp:",
|
|
9790
|
+
status.mcp.connected
|
|
9791
|
+
] }, "mcp"));
|
|
9792
|
+
}
|
|
9793
|
+
if (status.mcp.failed > 0) {
|
|
9794
|
+
details.push(/* @__PURE__ */ jsx9(Text9, { color: theme.error, children: strings.mcpFailed(status.mcp.failed) }, "mcp-failed"));
|
|
9110
9795
|
}
|
|
9111
9796
|
return /* @__PURE__ */ jsxs9(
|
|
9112
9797
|
Box9,
|
|
@@ -9126,59 +9811,28 @@ function StatusBar({ status }) {
|
|
|
9126
9811
|
flexDirection: "column",
|
|
9127
9812
|
children: [
|
|
9128
9813
|
/* @__PURE__ */ jsxs9(Box9, { width: "100%", alignItems: "flex-start", children: [
|
|
9129
|
-
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, flexShrink: 1, flexWrap: "wrap", children: /* @__PURE__ */
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
marginLeft: 2,
|
|
9135
|
-
flexDirection: "column",
|
|
9136
|
-
alignItems: "flex-end",
|
|
9137
|
-
children: [
|
|
9138
|
-
/* @__PURE__ */ jsx9(ContextMeter, { context: status.context }),
|
|
9139
|
-
(status.mcp.connected > 0 || status.mcp.failed > 0) && /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9140
|
-
status.mcp.connected > 0 && /* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
9141
|
-
"mcp:",
|
|
9142
|
-
status.mcp.connected
|
|
9143
|
-
] }),
|
|
9144
|
-
status.mcp.connected > 0 && status.mcp.failed > 0 && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }),
|
|
9145
|
-
status.mcp.failed > 0 && /* @__PURE__ */ jsx9(Text9, { color: theme.error, children: strings.mcpFailed(status.mcp.failed) })
|
|
9146
|
-
] }),
|
|
9147
|
-
status.activeAgents > 0 && /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: strings.activeAgents(status.activeAgents) })
|
|
9148
|
-
]
|
|
9149
|
-
}
|
|
9150
|
-
)
|
|
9814
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, flexShrink: 1, flexWrap: "wrap", children: details.map((item, index) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9815
|
+
index > 0 && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }),
|
|
9816
|
+
item
|
|
9817
|
+
] }, index)) }),
|
|
9818
|
+
/* @__PURE__ */ jsx9(Box9, { flexShrink: 0, marginLeft: 2, children: /* @__PURE__ */ jsx9(ContextMeter, { context: status.context }) })
|
|
9151
9819
|
] }),
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9820
|
+
status.agents.length > 0 && /* @__PURE__ */ jsxs9(Box9, { width: "100%", flexDirection: "column", alignItems: "flex-end", children: [
|
|
9821
|
+
status.agents.slice(0, VISIBLE_AGENTS).map((task, index) => /* @__PURE__ */ jsxs9(Text9, { color: theme.accent, wrap: "truncate-end", children: [
|
|
9822
|
+
"\u25CC ",
|
|
9823
|
+
shortTask(task)
|
|
9824
|
+
] }, index)),
|
|
9825
|
+
status.agents.length > VISIBLE_AGENTS && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: strings.moreAgents(status.agents.length - VISIBLE_AGENTS) })
|
|
9826
|
+
] })
|
|
9156
9827
|
]
|
|
9157
9828
|
}
|
|
9158
9829
|
);
|
|
9159
9830
|
}
|
|
9160
|
-
function Segments({ items }) {
|
|
9161
|
-
return items.flatMap((item, index) => {
|
|
9162
|
-
const elements = [];
|
|
9163
|
-
if (index > 0) {
|
|
9164
|
-
elements.push(
|
|
9165
|
-
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }) }, `sep-${index}`)
|
|
9166
|
-
);
|
|
9167
|
-
}
|
|
9168
|
-
elements.push(/* @__PURE__ */ jsx9(Fragment2, { children: item }, `item-${index}`));
|
|
9169
|
-
return elements;
|
|
9170
|
-
});
|
|
9171
|
-
}
|
|
9172
9831
|
function shortModel(ref) {
|
|
9173
9832
|
const safe = sanitizeTerminalText(ref);
|
|
9174
9833
|
const slash = safe.lastIndexOf("/");
|
|
9175
9834
|
return slash === -1 ? safe : safe.slice(slash + 1);
|
|
9176
9835
|
}
|
|
9177
|
-
function ModelBadge({ ref: modelRef }) {
|
|
9178
|
-
const theme = useTheme();
|
|
9179
|
-
const name = shortModel(modelRef);
|
|
9180
|
-
return /* @__PURE__ */ jsx9(Box9, { borderColor: theme.accent, borderStyle: "round", paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.accent, bold: true, children: name }) });
|
|
9181
|
-
}
|
|
9182
9836
|
var EFFORT_STYLES = {
|
|
9183
9837
|
auto: { label: "auto", color: "cyan" },
|
|
9184
9838
|
max: { label: "max", color: "red", bold: true },
|
|
@@ -9187,36 +9841,30 @@ var EFFORT_STYLES = {
|
|
|
9187
9841
|
medium: { label: "medium", color: "blue" },
|
|
9188
9842
|
low: { label: "low", color: "gray", dim: true }
|
|
9189
9843
|
};
|
|
9190
|
-
function EffortLabel({ effort }) {
|
|
9844
|
+
function EffortLabel({ effort, effective }) {
|
|
9191
9845
|
const style = EFFORT_STYLES[effort];
|
|
9192
|
-
|
|
9193
|
-
Text9,
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
}
|
|
9202
|
-
|
|
9203
|
-
if (count < 1e3) return String(count);
|
|
9204
|
-
const [value, suffix] = count < 1e6 ? [count / 1e3, "k"] : [count / 1e6, "M"];
|
|
9205
|
-
return `${value.toFixed(1).replace(/\.0$/, "")}${suffix}`;
|
|
9846
|
+
if (effective === null) {
|
|
9847
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, strikethrough: true, children: style.label });
|
|
9848
|
+
}
|
|
9849
|
+
const sent = effective !== effort ? EFFORT_STYLES[effective] : void 0;
|
|
9850
|
+
return /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9851
|
+
/* @__PURE__ */ jsx9(Text9, { color: style.color, bold: style.bold, dimColor: style.dim, children: style.label }),
|
|
9852
|
+
sent && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
9853
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: effort === "auto" ? "\xB7" : "\u2192" }),
|
|
9854
|
+
/* @__PURE__ */ jsx9(Text9, { color: sent.color, bold: sent.bold, dimColor: sent.dim, children: sent.label })
|
|
9855
|
+
] })
|
|
9856
|
+
] });
|
|
9206
9857
|
}
|
|
9207
9858
|
function ContextMeter({
|
|
9208
9859
|
context
|
|
9209
9860
|
}) {
|
|
9210
9861
|
const theme = useTheme();
|
|
9211
|
-
const { window: win,
|
|
9212
|
-
|
|
9862
|
+
const { window: win, exact } = context;
|
|
9863
|
+
const used = exact ? context.used : 0;
|
|
9864
|
+
if (win === null || win <= 0) {
|
|
9213
9865
|
return /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9214
9866
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "ctx " }),
|
|
9215
|
-
/* @__PURE__ */ jsx9(Text9, { color: "gray", children:
|
|
9216
|
-
win && win > 0 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
9217
|
-
"/",
|
|
9218
|
-
tokens2(win)
|
|
9219
|
-
] })
|
|
9867
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: tokens(used) })
|
|
9220
9868
|
] });
|
|
9221
9869
|
}
|
|
9222
9870
|
const pct = Math.min(Math.max(used / win, 0), 1);
|
|
@@ -9234,53 +9882,226 @@ function ContextMeter({
|
|
|
9234
9882
|
/* @__PURE__ */ jsx9(Text9, { color, children: "\u258C" }),
|
|
9235
9883
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
9236
9884
|
" ",
|
|
9237
|
-
|
|
9885
|
+
tokens(used),
|
|
9238
9886
|
"/",
|
|
9239
|
-
|
|
9887
|
+
tokens(win)
|
|
9240
9888
|
] })
|
|
9241
9889
|
] });
|
|
9242
9890
|
}
|
|
9243
9891
|
|
|
9244
|
-
// src/ui/components/
|
|
9245
|
-
import { Box as
|
|
9246
|
-
import {
|
|
9247
|
-
var INTERACTIVE_CHROME_ROWS = 14;
|
|
9248
|
-
var MAX_LIVE_ROWS = 8;
|
|
9249
|
-
var MIN_LIVE_ROWS = 4;
|
|
9250
|
-
function liveTranscriptRows(rows) {
|
|
9251
|
-
if (!Number.isFinite(rows)) return MAX_LIVE_ROWS;
|
|
9252
|
-
return Math.max(MIN_LIVE_ROWS, Math.min(MAX_LIVE_ROWS, Math.floor(rows) - INTERACTIVE_CHROME_ROWS - 2));
|
|
9253
|
-
}
|
|
9254
|
-
function TerminalViewport({ children, rows }) {
|
|
9255
|
-
const { columns } = useTerminalSize();
|
|
9256
|
-
return /* @__PURE__ */ jsx10(
|
|
9257
|
-
Box10,
|
|
9258
|
-
{
|
|
9259
|
-
width: columns,
|
|
9260
|
-
maxWidth: "100%",
|
|
9261
|
-
flexDirection: "column",
|
|
9262
|
-
maxHeight: liveTranscriptRows(rows) + INTERACTIVE_CHROME_ROWS,
|
|
9263
|
-
overflowY: "hidden",
|
|
9264
|
-
children
|
|
9265
|
-
}
|
|
9266
|
-
);
|
|
9267
|
-
}
|
|
9892
|
+
// src/ui/components/SubagentChat.tsx
|
|
9893
|
+
import { Box as Box12, Text as Text12, useInput as useInput2 } from "ink";
|
|
9894
|
+
import { useState as useState7 } from "react";
|
|
9268
9895
|
|
|
9269
9896
|
// src/ui/components/Transcript.tsx
|
|
9270
|
-
import { Box as
|
|
9271
|
-
import { memo as memo2, useMemo as
|
|
9897
|
+
import { Box as Box11, Static, Text as Text11 } from "ink";
|
|
9898
|
+
import { memo as memo2, useEffect as useEffect3, useMemo as useMemo5, useRef as useRef4, useState as useState6 } from "react";
|
|
9272
9899
|
import Spinner2 from "ink-spinner";
|
|
9273
|
-
import
|
|
9900
|
+
import stringWidth4 from "string-width";
|
|
9274
9901
|
|
|
9275
9902
|
// src/ui/markdown.tsx
|
|
9276
|
-
import { Box as
|
|
9277
|
-
import { createContext as createContext3, Fragment as Fragment4, useContext as useContext3, useMemo as
|
|
9278
|
-
import
|
|
9279
|
-
|
|
9903
|
+
import { Box as Box10, Text as InkText } from "ink";
|
|
9904
|
+
import { createContext as createContext3, Fragment as Fragment4, useContext as useContext3, useMemo as useMemo4 } from "react";
|
|
9905
|
+
import stringWidth3 from "string-width";
|
|
9906
|
+
|
|
9907
|
+
// src/ui/highlight.ts
|
|
9908
|
+
var words = (list) => new Set(list.split(/\s+/).filter(Boolean));
|
|
9909
|
+
var JS_KEYWORDS = words(`
|
|
9910
|
+
async await break case catch class const continue debugger default delete do else enum export
|
|
9911
|
+
extends finally for from function get if implements import in instanceof interface let new of
|
|
9912
|
+
package private protected public readonly return satisfies set static super switch this throw
|
|
9913
|
+
try type typeof var void while with yield as declare abstract keyof namespace`);
|
|
9914
|
+
var GRAMMARS = {
|
|
9915
|
+
js: { keywords: JS_KEYWORDS, literals: words("true false null undefined NaN Infinity"), lineComment: ["//"], block: ["/*", "*/"], types: true },
|
|
9916
|
+
py: {
|
|
9917
|
+
keywords: words(`and as assert async await break class continue def del elif else except finally for
|
|
9918
|
+
from global if import in is lambda nonlocal not or pass raise return try while with yield match case self`),
|
|
9919
|
+
literals: words("True False None"),
|
|
9920
|
+
lineComment: ["#"],
|
|
9921
|
+
triple: true,
|
|
9922
|
+
types: true
|
|
9923
|
+
},
|
|
9924
|
+
go: {
|
|
9925
|
+
keywords: words(`break case chan const continue default defer else fallthrough for func go goto if import
|
|
9926
|
+
interface map package range return select struct switch type var`),
|
|
9927
|
+
literals: words("true false nil iota"),
|
|
9928
|
+
lineComment: ["//"],
|
|
9929
|
+
block: ["/*", "*/"],
|
|
9930
|
+
types: true
|
|
9931
|
+
},
|
|
9932
|
+
rust: {
|
|
9933
|
+
keywords: words(`as async await break const continue crate dyn else enum extern fn for if impl in let loop
|
|
9934
|
+
match mod move mut pub ref return self Self static struct super trait type unsafe use where while`),
|
|
9935
|
+
literals: words("true false None Some Ok Err"),
|
|
9936
|
+
lineComment: ["//"],
|
|
9937
|
+
block: ["/*", "*/"],
|
|
9938
|
+
types: true
|
|
9939
|
+
},
|
|
9940
|
+
c: {
|
|
9941
|
+
keywords: words(`auto break case char class const continue default delete do double else enum extern float
|
|
9942
|
+
for goto if inline int long namespace new private protected public return short signed sizeof static
|
|
9943
|
+
struct switch template this throw try typedef typename union unsigned using virtual void volatile while
|
|
9944
|
+
boolean byte extends final finally implements import instanceof interface package super synchronized throws var`),
|
|
9945
|
+
literals: words("true false null nullptr NULL"),
|
|
9946
|
+
lineComment: ["//"],
|
|
9947
|
+
block: ["/*", "*/"],
|
|
9948
|
+
types: true
|
|
9949
|
+
},
|
|
9950
|
+
sh: {
|
|
9951
|
+
keywords: words(`if then else elif fi for while until do done case esac in function return export local
|
|
9952
|
+
readonly set unset shift exit echo cd source alias`),
|
|
9953
|
+
literals: words("true false"),
|
|
9954
|
+
lineComment: ["#"]
|
|
9955
|
+
},
|
|
9956
|
+
json: { keywords: /* @__PURE__ */ new Set(), literals: words("true false null"), lineComment: [] },
|
|
9957
|
+
sql: {
|
|
9958
|
+
keywords: words(`select from where insert into values update set delete create table drop alter index join
|
|
9959
|
+
left right inner outer on group by order having limit offset as and or not null is in distinct union all
|
|
9960
|
+
primary key foreign references default SELECT FROM WHERE INSERT INTO VALUES UPDATE SET DELETE CREATE TABLE
|
|
9961
|
+
DROP ALTER INDEX JOIN LEFT RIGHT INNER OUTER ON GROUP BY ORDER HAVING LIMIT OFFSET AS AND OR NOT NULL IS IN
|
|
9962
|
+
DISTINCT UNION ALL PRIMARY KEY FOREIGN REFERENCES DEFAULT`),
|
|
9963
|
+
literals: words("true false TRUE FALSE"),
|
|
9964
|
+
lineComment: ["--"],
|
|
9965
|
+
block: ["/*", "*/"]
|
|
9966
|
+
}
|
|
9967
|
+
};
|
|
9968
|
+
var ALIASES = {
|
|
9969
|
+
js: "js",
|
|
9970
|
+
javascript: "js",
|
|
9971
|
+
jsx: "js",
|
|
9972
|
+
mjs: "js",
|
|
9973
|
+
cjs: "js",
|
|
9974
|
+
ts: "js",
|
|
9975
|
+
typescript: "js",
|
|
9976
|
+
tsx: "js",
|
|
9977
|
+
py: "py",
|
|
9978
|
+
python: "py",
|
|
9979
|
+
go: "go",
|
|
9980
|
+
golang: "go",
|
|
9981
|
+
rs: "rust",
|
|
9982
|
+
rust: "rust",
|
|
9983
|
+
c: "c",
|
|
9984
|
+
h: "c",
|
|
9985
|
+
cpp: "c",
|
|
9986
|
+
"c++": "c",
|
|
9987
|
+
cc: "c",
|
|
9988
|
+
hpp: "c",
|
|
9989
|
+
cs: "c",
|
|
9990
|
+
csharp: "c",
|
|
9991
|
+
java: "c",
|
|
9992
|
+
kotlin: "c",
|
|
9993
|
+
kt: "c",
|
|
9994
|
+
swift: "c",
|
|
9995
|
+
php: "c",
|
|
9996
|
+
sh: "sh",
|
|
9997
|
+
bash: "sh",
|
|
9998
|
+
zsh: "sh",
|
|
9999
|
+
shell: "sh",
|
|
10000
|
+
console: "sh",
|
|
10001
|
+
powershell: "sh",
|
|
10002
|
+
ps1: "sh",
|
|
10003
|
+
ps: "sh",
|
|
10004
|
+
json: "json",
|
|
10005
|
+
jsonc: "json",
|
|
10006
|
+
json5: "json",
|
|
10007
|
+
sql: "sql",
|
|
10008
|
+
postgres: "sql",
|
|
10009
|
+
mysql: "sql",
|
|
10010
|
+
sqlite: "sql"
|
|
10011
|
+
};
|
|
10012
|
+
function grammarFor(lang) {
|
|
10013
|
+
return lang ? ALIASES[lang.toLowerCase()] : void 0;
|
|
10014
|
+
}
|
|
10015
|
+
function highlightCode(code, lang) {
|
|
10016
|
+
const grammar = GRAMMARS[grammarFor(lang) ?? ""];
|
|
10017
|
+
const lines = code.split("\n");
|
|
10018
|
+
if (!grammar) return lines.map((line) => [{ kind: "plain", text: line }]);
|
|
10019
|
+
const state = {};
|
|
10020
|
+
return lines.map((line) => highlightLine(line, grammar, state));
|
|
10021
|
+
}
|
|
10022
|
+
function highlightLine(line, grammar, state) {
|
|
10023
|
+
const tokens2 = [];
|
|
10024
|
+
const push = (kind, text) => {
|
|
10025
|
+
if (text === "") return;
|
|
10026
|
+
const last = tokens2.at(-1);
|
|
10027
|
+
if (last && last.kind === kind) last.text += text;
|
|
10028
|
+
else tokens2.push({ kind, text });
|
|
10029
|
+
};
|
|
10030
|
+
let i = 0;
|
|
10031
|
+
if (state.open) {
|
|
10032
|
+
const end = line.indexOf(state.open.close);
|
|
10033
|
+
if (end === -1) {
|
|
10034
|
+
push(state.open.kind, line);
|
|
10035
|
+
return tokens2;
|
|
10036
|
+
}
|
|
10037
|
+
push(state.open.kind, line.slice(0, end + state.open.close.length));
|
|
10038
|
+
i = end + state.open.close.length;
|
|
10039
|
+
state.open = void 0;
|
|
10040
|
+
}
|
|
10041
|
+
while (i < line.length) {
|
|
10042
|
+
const rest = line.slice(i);
|
|
10043
|
+
const lineComment = grammar.lineComment.find((marker) => rest.startsWith(marker));
|
|
10044
|
+
if (lineComment) {
|
|
10045
|
+
push("comment", rest);
|
|
10046
|
+
break;
|
|
10047
|
+
}
|
|
10048
|
+
if (grammar.block && rest.startsWith(grammar.block[0])) {
|
|
10049
|
+
const end = rest.indexOf(grammar.block[1], grammar.block[0].length);
|
|
10050
|
+
if (end === -1) {
|
|
10051
|
+
push("comment", rest);
|
|
10052
|
+
state.open = { kind: "comment", close: grammar.block[1] };
|
|
10053
|
+
break;
|
|
10054
|
+
}
|
|
10055
|
+
push("comment", rest.slice(0, end + grammar.block[1].length));
|
|
10056
|
+
i += end + grammar.block[1].length;
|
|
10057
|
+
continue;
|
|
10058
|
+
}
|
|
10059
|
+
if (grammar.triple && (rest.startsWith('"""') || rest.startsWith("'''"))) {
|
|
10060
|
+
const quote = rest.slice(0, 3);
|
|
10061
|
+
const end = rest.indexOf(quote, 3);
|
|
10062
|
+
if (end === -1) {
|
|
10063
|
+
push("string", rest);
|
|
10064
|
+
state.open = { kind: "string", close: quote };
|
|
10065
|
+
break;
|
|
10066
|
+
}
|
|
10067
|
+
push("string", rest.slice(0, end + 3));
|
|
10068
|
+
i += end + 3;
|
|
10069
|
+
continue;
|
|
10070
|
+
}
|
|
10071
|
+
const character = line[i];
|
|
10072
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
10073
|
+
let j = i + 1;
|
|
10074
|
+
while (j < line.length && line[j] !== character) j += line[j] === "\\" ? 2 : 1;
|
|
10075
|
+
push("string", line.slice(i, Math.min(line.length, j + 1)));
|
|
10076
|
+
i = j + 1;
|
|
10077
|
+
continue;
|
|
10078
|
+
}
|
|
10079
|
+
const number = /^(?:0x[0-9a-f_]+|\d[\d_]*(?:\.\d+)?(?:e[+-]?\d+)?)/i.exec(rest);
|
|
10080
|
+
if (number && !/[\w$]/.test(line[i - 1] ?? "")) {
|
|
10081
|
+
push("number", number[0]);
|
|
10082
|
+
i += number[0].length;
|
|
10083
|
+
continue;
|
|
10084
|
+
}
|
|
10085
|
+
const word = /^[A-Za-z_$][\w$]*/.exec(rest);
|
|
10086
|
+
if (word) {
|
|
10087
|
+
const text = word[0];
|
|
10088
|
+
const kind = grammar.keywords.has(text) ? "keyword" : grammar.literals.has(text) ? "literal" : grammar.types && /^[A-Z][a-z0-9]/.test(text) ? "type" : "plain";
|
|
10089
|
+
push(kind, text);
|
|
10090
|
+
i += text.length;
|
|
10091
|
+
continue;
|
|
10092
|
+
}
|
|
10093
|
+
push("plain", character);
|
|
10094
|
+
i += 1;
|
|
10095
|
+
}
|
|
10096
|
+
return tokens2;
|
|
10097
|
+
}
|
|
10098
|
+
|
|
10099
|
+
// src/ui/markdown.tsx
|
|
10100
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
9280
10101
|
var DimContext = createContext3(false);
|
|
9281
10102
|
function Text10({ dimColor, color, bold, ...props }) {
|
|
9282
10103
|
const dim = useContext3(DimContext);
|
|
9283
|
-
return /* @__PURE__ */
|
|
10104
|
+
return /* @__PURE__ */ jsx10(
|
|
9284
10105
|
InkText,
|
|
9285
10106
|
{
|
|
9286
10107
|
...props,
|
|
@@ -9291,57 +10112,76 @@ function Text10({ dimColor, color, bold, ...props }) {
|
|
|
9291
10112
|
);
|
|
9292
10113
|
}
|
|
9293
10114
|
function Markdown({ children, dim = false }) {
|
|
9294
|
-
const blocks =
|
|
9295
|
-
return /* @__PURE__ */
|
|
10115
|
+
const blocks = useMemo4(() => extractBlocks(children), [children]);
|
|
10116
|
+
return /* @__PURE__ */ jsx10(DimContext.Provider, { value: dim, children: /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: blocks.map((block, i) => /* @__PURE__ */ jsx10(BlockView, { block }, i)) }) });
|
|
9296
10117
|
}
|
|
9297
10118
|
function BlockView({ block }) {
|
|
9298
10119
|
switch (block.type) {
|
|
9299
10120
|
case "heading": {
|
|
9300
|
-
return /* @__PURE__ */
|
|
10121
|
+
return /* @__PURE__ */ jsx10(Box10, { marginTop: block.level === 1 ? 1 : 0, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(block.text ?? "") }) });
|
|
9301
10122
|
}
|
|
9302
10123
|
case "blockquote":
|
|
9303
|
-
return /* @__PURE__ */
|
|
10124
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.text ?? "").split("\n").map((line, i) => /* @__PURE__ */ jsx10(Text10, { dimColor: true, italic: true, children: inline(line) }, i)) });
|
|
9304
10125
|
case "ul":
|
|
9305
|
-
return /* @__PURE__ */
|
|
9306
|
-
/* @__PURE__ */
|
|
9307
|
-
/* @__PURE__ */
|
|
10126
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
10127
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: item.text?.match(/^[☑☐] /) ? "" : "\u2022 " }),
|
|
10128
|
+
/* @__PURE__ */ jsx10(Box10, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
9308
10129
|
] }, i)) });
|
|
9309
10130
|
case "ol":
|
|
9310
|
-
return /* @__PURE__ */
|
|
9311
|
-
/* @__PURE__ */
|
|
9312
|
-
/* @__PURE__ */
|
|
10131
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
10132
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: `${(block.start ?? 1) + i}. ` }),
|
|
10133
|
+
/* @__PURE__ */ jsx10(Box10, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
9313
10134
|
] }, i)) });
|
|
9314
10135
|
case "paragraph":
|
|
9315
10136
|
default: {
|
|
9316
|
-
const content = /* @__PURE__ */
|
|
10137
|
+
const content = /* @__PURE__ */ jsx10(Text10, { children: inline(block.text ?? "") });
|
|
9317
10138
|
if (!block.children?.length) return content;
|
|
9318
|
-
return /* @__PURE__ */ jsxs10(
|
|
10139
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
9319
10140
|
content,
|
|
9320
|
-
/* @__PURE__ */
|
|
10141
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: block.children.map((child, index) => /* @__PURE__ */ jsx10(BlockView, { block: child }, index)) })
|
|
9321
10142
|
] });
|
|
9322
10143
|
}
|
|
9323
10144
|
case "table":
|
|
9324
|
-
return /* @__PURE__ */
|
|
10145
|
+
return /* @__PURE__ */ jsx10(TableView, { block });
|
|
9325
10146
|
case "code":
|
|
9326
|
-
return /* @__PURE__ */
|
|
10147
|
+
return /* @__PURE__ */ jsx10(CodeBlock, { code: block.text ?? "", lang: block.lang });
|
|
9327
10148
|
case "hr":
|
|
9328
|
-
return /* @__PURE__ */
|
|
10149
|
+
return /* @__PURE__ */ jsx10(Box10, { height: 1 });
|
|
9329
10150
|
}
|
|
9330
10151
|
}
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
10152
|
+
var TOKEN_COLORS = {
|
|
10153
|
+
plain: void 0,
|
|
10154
|
+
keyword: "#c4a7ff",
|
|
10155
|
+
string: "#86efac",
|
|
10156
|
+
comment: void 0,
|
|
10157
|
+
number: "#fbbf24",
|
|
10158
|
+
literal: "#fb923c",
|
|
10159
|
+
type: "#67e8f9"
|
|
10160
|
+
};
|
|
10161
|
+
function CodeBlock({ code, lang }) {
|
|
10162
|
+
const lines = useMemo4(() => highlightCode(code.replace(/\t/g, " "), lang), [code, lang]);
|
|
10163
|
+
const plain = grammarFor(lang) ? void 0 : "cyan";
|
|
10164
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginTop: 1, children: lines.map((tokens2, i) => /* @__PURE__ */ jsx10(Text10, { children: tokens2.map((token, j) => /* @__PURE__ */ jsx10(
|
|
10165
|
+
Text10,
|
|
10166
|
+
{
|
|
10167
|
+
color: TOKEN_COLORS[token.kind] ?? (token.kind === "plain" ? plain : void 0),
|
|
10168
|
+
dimColor: token.kind === "comment" ? true : void 0,
|
|
10169
|
+
italic: token.kind === "comment",
|
|
10170
|
+
children: token.text
|
|
10171
|
+
},
|
|
10172
|
+
j
|
|
10173
|
+
)) }, i)) });
|
|
9334
10174
|
}
|
|
9335
10175
|
function TableView({ block }) {
|
|
9336
10176
|
const { columns } = useTerminalSize();
|
|
9337
10177
|
const headers = block.headers ?? [];
|
|
9338
10178
|
const rows = block.rows ?? [];
|
|
9339
10179
|
if (headers.length === 0 && rows.length === 0) {
|
|
9340
|
-
return /* @__PURE__ */
|
|
10180
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
9341
10181
|
}
|
|
9342
10182
|
const colCount = Math.max(headers.length, ...rows.map((r) => r?.length ?? 0));
|
|
9343
10183
|
if (colCount === 0) {
|
|
9344
|
-
return /* @__PURE__ */
|
|
10184
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
9345
10185
|
}
|
|
9346
10186
|
const allRows = [headers, ...rows].map(
|
|
9347
10187
|
(row) => Array.from({ length: colCount }, (_, index) => inlineDisplayText(row[index] ?? ""))
|
|
@@ -9350,7 +10190,7 @@ function TableView({ block }) {
|
|
|
9350
10190
|
for (const row of allRows) {
|
|
9351
10191
|
for (let i = 0; i < colCount; i++) {
|
|
9352
10192
|
const cell = row[i] ?? "";
|
|
9353
|
-
naturalWidths[i] = Math.max(naturalWidths[i] ?? 1,
|
|
10193
|
+
naturalWidths[i] = Math.max(naturalWidths[i] ?? 1, stringWidth3(cell));
|
|
9354
10194
|
}
|
|
9355
10195
|
}
|
|
9356
10196
|
const colWidths = fitColumnWidths(naturalWidths, Math.max(1, columns));
|
|
@@ -9366,7 +10206,7 @@ function TableView({ block }) {
|
|
|
9366
10206
|
const align = block.colAligns?.[i] ?? "left";
|
|
9367
10207
|
const width = colWidths[i] ?? 1;
|
|
9368
10208
|
const fitted = truncateToWidth(cell, width);
|
|
9369
|
-
const visualWidth =
|
|
10209
|
+
const visualWidth = stringWidth3(fitted);
|
|
9370
10210
|
const totalPad = Math.max(0, width - visualWidth);
|
|
9371
10211
|
let padded;
|
|
9372
10212
|
if (align === "right") {
|
|
@@ -9380,13 +10220,13 @@ function TableView({ block }) {
|
|
|
9380
10220
|
cells.push(padded);
|
|
9381
10221
|
}
|
|
9382
10222
|
lines.push(
|
|
9383
|
-
/* @__PURE__ */
|
|
10223
|
+
/* @__PURE__ */ jsx10(Text10, { bold: rowIdx === 0, wrap: "truncate-end", children: cells.join(" \u2502 ") }, `row-${rowIdx}`)
|
|
9384
10224
|
);
|
|
9385
10225
|
if (rowIdx === 0) {
|
|
9386
|
-
lines.push(/* @__PURE__ */
|
|
10226
|
+
lines.push(/* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "truncate-end", children: separator }, `sep-${rowIdx}`));
|
|
9387
10227
|
}
|
|
9388
10228
|
});
|
|
9389
|
-
return /* @__PURE__ */
|
|
10229
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: lines });
|
|
9390
10230
|
}
|
|
9391
10231
|
function fitColumnWidths(natural, maxLineWidth) {
|
|
9392
10232
|
const widths = natural.map((width) => Math.max(1, width));
|
|
@@ -9409,11 +10249,11 @@ function fitColumnWidths(natural, maxLineWidth) {
|
|
|
9409
10249
|
}
|
|
9410
10250
|
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
9411
10251
|
function truncateToWidth(text, maxWidth) {
|
|
9412
|
-
if (
|
|
10252
|
+
if (stringWidth3(text) <= maxWidth) return text;
|
|
9413
10253
|
if (maxWidth <= 1) return "\u2026";
|
|
9414
10254
|
let result = "";
|
|
9415
10255
|
for (const { segment } of graphemeSegmenter.segment(text)) {
|
|
9416
|
-
if (
|
|
10256
|
+
if (stringWidth3(result + segment) > maxWidth - 1) break;
|
|
9417
10257
|
result += segment;
|
|
9418
10258
|
}
|
|
9419
10259
|
return `${result}\u2026`;
|
|
@@ -9774,7 +10614,7 @@ function inline(text, depth = 0) {
|
|
|
9774
10614
|
handler: (m) => {
|
|
9775
10615
|
const fragmentKey = key++;
|
|
9776
10616
|
return /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
9777
|
-
/* @__PURE__ */
|
|
10617
|
+
/* @__PURE__ */ jsx10(Text10, { color: "cyan", underline: true, children: inline(m[2], depth + 1) }),
|
|
9778
10618
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
|
|
9779
10619
|
"(",
|
|
9780
10620
|
restoreInlineEscapes(m[3]),
|
|
@@ -9785,31 +10625,31 @@ function inline(text, depth = 0) {
|
|
|
9785
10625
|
},
|
|
9786
10626
|
{
|
|
9787
10627
|
re: /(`+)([^`]+?)\1/,
|
|
9788
|
-
handler: (m) => /* @__PURE__ */
|
|
10628
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: restoreInlineEscapes(m[2]) }, key++)
|
|
9789
10629
|
},
|
|
9790
10630
|
{
|
|
9791
10631
|
re: /(\*\*\*|___)(?=\S)(.*?\S)\1/,
|
|
9792
|
-
handler: (m) => /* @__PURE__ */
|
|
10632
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9793
10633
|
},
|
|
9794
10634
|
{
|
|
9795
10635
|
re: /(\*\*)(?=\S)(.*?\S)\1/,
|
|
9796
|
-
handler: (m) => /* @__PURE__ */
|
|
10636
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
9797
10637
|
},
|
|
9798
10638
|
{
|
|
9799
10639
|
re: /(__)(?=\S)(.*?\S)\1/,
|
|
9800
|
-
handler: (m) => /* @__PURE__ */
|
|
10640
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
9801
10641
|
},
|
|
9802
10642
|
{
|
|
9803
10643
|
re: /(~~)(?=\S)(.*?\S)\1/,
|
|
9804
|
-
handler: (m) => /* @__PURE__ */
|
|
10644
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { strikethrough: true, children: inline(m[2], depth + 1) }, key++)
|
|
9805
10645
|
},
|
|
9806
10646
|
{
|
|
9807
10647
|
re: /(\*)(?=\S)(.*?\S)\1/,
|
|
9808
|
-
handler: (m) => /* @__PURE__ */
|
|
10648
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9809
10649
|
},
|
|
9810
10650
|
{
|
|
9811
10651
|
re: /(?<!\w)(_)(?=\S)(.*?\S)\1(?!\w)/,
|
|
9812
|
-
handler: (m) => /* @__PURE__ */
|
|
10652
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9813
10653
|
}
|
|
9814
10654
|
];
|
|
9815
10655
|
while (rest.length > 0) {
|
|
@@ -9833,7 +10673,7 @@ function inline(text, depth = 0) {
|
|
|
9833
10673
|
}
|
|
9834
10674
|
|
|
9835
10675
|
// src/ui/components/Transcript.tsx
|
|
9836
|
-
import { Fragment as Fragment5, jsx as
|
|
10676
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
9837
10677
|
var HEADER = { kind: "header" };
|
|
9838
10678
|
function assistantThinkingForFrame(bubble, frozenThinking) {
|
|
9839
10679
|
return bubble.streaming && bubble.text !== "" ? frozenThinking ?? bubble.thinking : bubble.thinking;
|
|
@@ -9850,7 +10690,7 @@ var Transcript = memo2(function Transcript2({
|
|
|
9850
10690
|
}) {
|
|
9851
10691
|
const liveAt = firstMutableBubbleIndex(bubbles);
|
|
9852
10692
|
const stableCount = liveAt === -1 ? bubbles.length : liveAt;
|
|
9853
|
-
const stableRef =
|
|
10693
|
+
const stableRef = useRef4([]);
|
|
9854
10694
|
if (stableRef.current.length < stableCount) {
|
|
9855
10695
|
stableRef.current = [
|
|
9856
10696
|
...stableRef.current,
|
|
@@ -9864,7 +10704,7 @@ var Transcript = memo2(function Transcript2({
|
|
|
9864
10704
|
const agents = live.filter((bubble) => bubble.kind === "subagent");
|
|
9865
10705
|
const groupedAgents = agents.length > 1;
|
|
9866
10706
|
return /* @__PURE__ */ jsxs11(
|
|
9867
|
-
|
|
10707
|
+
Box11,
|
|
9868
10708
|
{
|
|
9869
10709
|
flexDirection: "column",
|
|
9870
10710
|
marginBottom: 1,
|
|
@@ -9872,9 +10712,9 @@ var Transcript = memo2(function Transcript2({
|
|
|
9872
10712
|
minHeight: 0,
|
|
9873
10713
|
overflowY: "hidden",
|
|
9874
10714
|
children: [
|
|
9875
|
-
/* @__PURE__ */
|
|
10715
|
+
/* @__PURE__ */ jsx11(Static, { items: staticItems, children: (item) => item.kind === "header" ? /* @__PURE__ */ jsx11(Logo, { workspace }, "kitcode-header") : /* @__PURE__ */ jsx11(BubbleView, { bubble: item }, item.id) }),
|
|
9876
10716
|
/* @__PURE__ */ jsxs11(
|
|
9877
|
-
|
|
10717
|
+
Box11,
|
|
9878
10718
|
{
|
|
9879
10719
|
flexDirection: "column",
|
|
9880
10720
|
flexShrink: 1,
|
|
@@ -9883,8 +10723,8 @@ var Transcript = memo2(function Transcript2({
|
|
|
9883
10723
|
overflowY: "hidden",
|
|
9884
10724
|
justifyContent: "flex-end",
|
|
9885
10725
|
children: [
|
|
9886
|
-
live.filter((bubble) => !groupedAgents || bubble.kind !== "subagent" && !(bubble.kind === "tool" && bubble.name === "task")).map((bubble) => /* @__PURE__ */
|
|
9887
|
-
groupedAgents && /* @__PURE__ */
|
|
10726
|
+
live.filter((bubble) => !groupedAgents || bubble.kind !== "subagent" && !(bubble.kind === "tool" && bubble.name === "task")).map((bubble) => /* @__PURE__ */ jsx11(BubbleView, { bubble, maxRows: maxLiveRows }, bubble.id)),
|
|
10727
|
+
groupedAgents && /* @__PURE__ */ jsx11(SubagentSummary, { agents })
|
|
9888
10728
|
]
|
|
9889
10729
|
}
|
|
9890
10730
|
)
|
|
@@ -9898,22 +10738,22 @@ var BubbleView = memo2(function BubbleView2({
|
|
|
9898
10738
|
}) {
|
|
9899
10739
|
const theme = useTheme();
|
|
9900
10740
|
if (bubble.kind === "user") {
|
|
9901
|
-
return /* @__PURE__ */
|
|
10741
|
+
return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.accent, bold: true, children: [
|
|
9902
10742
|
"\u203A ",
|
|
9903
10743
|
bubble.text
|
|
9904
10744
|
] }) });
|
|
9905
10745
|
}
|
|
9906
10746
|
if (bubble.kind === "notice") {
|
|
9907
10747
|
const color = bubble.level === "error" ? theme.error : bubble.level === "warn" ? theme.warn : theme.accent;
|
|
9908
|
-
return /* @__PURE__ */
|
|
10748
|
+
return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color, children: bubble.text }) });
|
|
9909
10749
|
}
|
|
9910
10750
|
if (bubble.kind === "assistant") {
|
|
9911
|
-
return /* @__PURE__ */
|
|
10751
|
+
return /* @__PURE__ */ jsx11(AssistantView, { bubble, maxRows });
|
|
9912
10752
|
}
|
|
9913
10753
|
if (bubble.kind === "subagent") {
|
|
9914
|
-
return /* @__PURE__ */
|
|
10754
|
+
return /* @__PURE__ */ jsx11(SubagentView, { bubble, maxRows });
|
|
9915
10755
|
}
|
|
9916
|
-
return /* @__PURE__ */
|
|
10756
|
+
return /* @__PURE__ */ jsx11(ToolView, { bubble });
|
|
9917
10757
|
});
|
|
9918
10758
|
function splitThinkingPrefix(text, streaming) {
|
|
9919
10759
|
const opening = /^\s*<think(?:ing)?>/i.exec(text);
|
|
@@ -9929,7 +10769,7 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
9929
10769
|
const tagged = splitThinkingPrefix(source.text, source.streaming);
|
|
9930
10770
|
const bubble = { ...source, text: tagged.text, thinking: [source.thinking, tagged.thinking].filter(Boolean).join("\n") };
|
|
9931
10771
|
const { columns } = useTerminalSize();
|
|
9932
|
-
const frozenThinking =
|
|
10772
|
+
const frozenThinking = useRef4(void 0);
|
|
9933
10773
|
const answering = bubble.streaming && bubble.text !== "";
|
|
9934
10774
|
if (!answering) frozenThinking.current = void 0;
|
|
9935
10775
|
if (answering && frozenThinking.current === void 0) {
|
|
@@ -9944,11 +10784,11 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
9944
10784
|
const frameThinking = thinkingBudget === void 0 ? visibleThinking.trim() : clipTextToRows(visibleThinking.trim(), thinkingBudget, columns);
|
|
9945
10785
|
const usedThinkingRows = frameThinking === "" ? 0 : frameThinking.split("\n").length;
|
|
9946
10786
|
const frameText = liveBudget === void 0 ? bubble.text : clipTextToRows(bubble.text, Math.max(1, liveBudget - usedThinkingRows), columns);
|
|
9947
|
-
return /* @__PURE__ */ jsxs11(
|
|
9948
|
-
frameThinking !== "" && /* @__PURE__ */
|
|
9949
|
-
bubble.streaming ? /* @__PURE__ */
|
|
10787
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
10788
|
+
frameThinking !== "" && /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", maxHeight: thinkingBudget, overflowY: "hidden", children: /* @__PURE__ */ jsx11(Markdown, { dim: true, children: frameThinking }) }),
|
|
10789
|
+
bubble.streaming ? /* @__PURE__ */ jsx11(Text11, { children: frameText }) : /* @__PURE__ */ jsx11(Markdown, { children: bubble.text }),
|
|
9950
10790
|
bubble.streaming && bubble.text === "" && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
9951
|
-
/* @__PURE__ */
|
|
10791
|
+
/* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
|
|
9952
10792
|
" thinking"
|
|
9953
10793
|
] })
|
|
9954
10794
|
] });
|
|
@@ -9967,7 +10807,7 @@ function wrapVisualLine(line, columns) {
|
|
|
9967
10807
|
let current = "";
|
|
9968
10808
|
let currentWidth = 0;
|
|
9969
10809
|
for (const character of line) {
|
|
9970
|
-
const width = Math.max(1,
|
|
10810
|
+
const width = Math.max(1, stringWidth4(character));
|
|
9971
10811
|
if (current !== "" && currentWidth + width > columns) {
|
|
9972
10812
|
rows.push(current);
|
|
9973
10813
|
current = "";
|
|
@@ -9984,23 +10824,23 @@ function ToolView({ bubble }) {
|
|
|
9984
10824
|
const mark = bubble.state === "running" ? "\u25CC" : bubble.state === "ok" ? "\u25CF" : "\u2717";
|
|
9985
10825
|
const color = bubble.state === "error" ? theme.error : bubble.state === "ok" ? theme.ok : theme.warn;
|
|
9986
10826
|
const display = bubble.display;
|
|
9987
|
-
const diff =
|
|
10827
|
+
const diff = useMemo5(
|
|
9988
10828
|
() => display?.kind === "diff" ? diffLines(display.before, display.after) : null,
|
|
9989
10829
|
[display]
|
|
9990
10830
|
);
|
|
9991
|
-
if (!diff) return /* @__PURE__ */
|
|
10831
|
+
if (!diff) return /* @__PURE__ */ jsx11(NonDiffToolView, { bubble, mark, color });
|
|
9992
10832
|
const hasChanges = diff.added > 0 || diff.removed > 0;
|
|
9993
|
-
return /* @__PURE__ */ jsxs11(
|
|
10833
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
9994
10834
|
/* @__PURE__ */ jsxs11(Text11, { color, children: [
|
|
9995
10835
|
mark,
|
|
9996
10836
|
" ",
|
|
9997
10837
|
bubble.summary,
|
|
9998
10838
|
hasChanges && /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
9999
|
-
diff.added > 0 && /* @__PURE__ */
|
|
10000
|
-
diff.removed > 0 && /* @__PURE__ */
|
|
10839
|
+
diff.added > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.ok, bold: true, children: ` +${diff.added}` }),
|
|
10840
|
+
diff.removed > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.error, bold: true, children: ` -${diff.removed}` })
|
|
10001
10841
|
] })
|
|
10002
10842
|
] }),
|
|
10003
|
-
/* @__PURE__ */
|
|
10843
|
+
/* @__PURE__ */ jsx11(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsx11(DiffHunk, { lines: diff.hunk, hidden: diff.hidden }) })
|
|
10004
10844
|
] });
|
|
10005
10845
|
}
|
|
10006
10846
|
function NonDiffToolView({
|
|
@@ -10008,13 +10848,32 @@ function NonDiffToolView({
|
|
|
10008
10848
|
mark,
|
|
10009
10849
|
color
|
|
10010
10850
|
}) {
|
|
10011
|
-
|
|
10851
|
+
const running = bubble.state === "running";
|
|
10852
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
10012
10853
|
/* @__PURE__ */ jsxs11(Text11, { color, children: [
|
|
10013
10854
|
mark,
|
|
10014
10855
|
" ",
|
|
10015
|
-
bubble.summary
|
|
10856
|
+
bubble.summary,
|
|
10857
|
+
running && bubble.startedAt !== void 0 && /* @__PURE__ */ jsx11(Elapsed, { since: bubble.startedAt })
|
|
10016
10858
|
] }),
|
|
10017
|
-
|
|
10859
|
+
running && bubble.progress && /* @__PURE__ */ jsx11(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsxs11(Text11, { dimColor: true, wrap: "truncate-end", children: [
|
|
10860
|
+
"\u2514 ",
|
|
10861
|
+
bubble.progress
|
|
10862
|
+
] }) }),
|
|
10863
|
+
!running && bubble.content.trim() !== "" && /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", marginLeft: 2, children: previewLines(bubble.content).map((line, index) => /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: truncate2(line, 110) }, index)) })
|
|
10864
|
+
] });
|
|
10865
|
+
}
|
|
10866
|
+
var ELAPSED_AFTER_MS = 2e3;
|
|
10867
|
+
function Elapsed({ since }) {
|
|
10868
|
+
const [now, setNow] = useState6(() => Date.now());
|
|
10869
|
+
useEffect3(() => {
|
|
10870
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
10871
|
+
return () => clearInterval(timer);
|
|
10872
|
+
}, []);
|
|
10873
|
+
const ms = now - since;
|
|
10874
|
+
return ms < ELAPSED_AFTER_MS ? null : /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
10875
|
+
" \xB7 ",
|
|
10876
|
+
formatDuration(ms)
|
|
10018
10877
|
] });
|
|
10019
10878
|
}
|
|
10020
10879
|
function previewLines(content) {
|
|
@@ -10026,7 +10885,7 @@ var VISIBLE_SUBAGENTS = 3;
|
|
|
10026
10885
|
function SubagentSummary({ agents }) {
|
|
10027
10886
|
const theme = useTheme();
|
|
10028
10887
|
const running = agents.filter((agent) => agent.state === "running");
|
|
10029
|
-
return /* @__PURE__ */ jsxs11(
|
|
10888
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
10030
10889
|
/* @__PURE__ */ jsxs11(Text11, { color: theme.accent, wrap: "truncate-end", children: [
|
|
10031
10890
|
"Subagents: ",
|
|
10032
10891
|
running.length,
|
|
@@ -10043,7 +10902,7 @@ function SubagentSummary({ agents }) {
|
|
|
10043
10902
|
progress.replace(/\s+/g, " ").trim() || "waiting"
|
|
10044
10903
|
] }, agent.id);
|
|
10045
10904
|
}),
|
|
10046
|
-
/* @__PURE__ */
|
|
10905
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "/subagents \xB7 open a sub-agent chat" })
|
|
10047
10906
|
] });
|
|
10048
10907
|
}
|
|
10049
10908
|
function SubagentView({ bubble, maxRows }) {
|
|
@@ -10053,16 +10912,99 @@ function SubagentView({ bubble, maxRows }) {
|
|
|
10053
10912
|
const color = bubble.state === "running" ? theme.warn : theme.ok;
|
|
10054
10913
|
const latest = bubble.bubbles.at(-1);
|
|
10055
10914
|
const progress = latest?.kind === "assistant" ? sanitizeThinkingText(latest.text || latest.thinking, latest.streaming) : latest?.kind === "tool" ? latest.summary : "";
|
|
10056
|
-
return /* @__PURE__ */ jsxs11(
|
|
10915
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
10057
10916
|
/* @__PURE__ */ jsxs11(Text11, { color, bold: true, wrap: "truncate-end", children: [
|
|
10058
10917
|
mark,
|
|
10059
10918
|
" subagent: ",
|
|
10060
10919
|
bubble.description
|
|
10061
10920
|
] }),
|
|
10062
|
-
/* @__PURE__ */
|
|
10921
|
+
/* @__PURE__ */ jsx11(Box11, { marginLeft: 2, flexDirection: "column", minWidth: 0, children: bubble.state === "running" ? /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", maxHeight: Math.max(1, (maxRows ?? 6) - 2), overflowY: "hidden", children: /* @__PURE__ */ jsx11(Markdown, { children: clipTextToRows(progress, Math.max(1, (maxRows ?? 6) - 2), Math.max(1, columns - 2)) }) }) : bubble.result ? /* @__PURE__ */ jsx11(Markdown, { children: sanitizeThinkingText(bubble.result) }) : null })
|
|
10063
10922
|
] });
|
|
10064
10923
|
}
|
|
10065
10924
|
|
|
10925
|
+
// src/ui/components/SubagentChat.tsx
|
|
10926
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
10927
|
+
function SubagentChat({
|
|
10928
|
+
bubble,
|
|
10929
|
+
rows,
|
|
10930
|
+
onClose
|
|
10931
|
+
}) {
|
|
10932
|
+
const theme = useTheme();
|
|
10933
|
+
const strings = useStrings();
|
|
10934
|
+
const [offset, setOffset] = useState7(0);
|
|
10935
|
+
const total = bubble?.bubbles.length ?? 0;
|
|
10936
|
+
useInput2((_input, key) => {
|
|
10937
|
+
if (key.escape) onClose();
|
|
10938
|
+
else if (key.upArrow) setOffset((value) => Math.min(Math.max(0, total - 1), value + 1));
|
|
10939
|
+
else if (key.downArrow) setOffset((value) => Math.max(0, value - 1));
|
|
10940
|
+
});
|
|
10941
|
+
if (!bubble) {
|
|
10942
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
10943
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: strings.noSubagents }),
|
|
10944
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: strings.subagentBack })
|
|
10945
|
+
] });
|
|
10946
|
+
}
|
|
10947
|
+
const running = bubble.state === "running";
|
|
10948
|
+
const shown = bubble.bubbles.slice(0, total - offset);
|
|
10949
|
+
const bodyRows = Math.max(3, rows - 2);
|
|
10950
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", minWidth: 0, children: [
|
|
10951
|
+
/* @__PURE__ */ jsxs12(Text12, { color: running ? theme.warn : theme.ok, bold: true, wrap: "truncate-end", children: [
|
|
10952
|
+
running ? "\u25CC" : "\u25CF",
|
|
10953
|
+
" ",
|
|
10954
|
+
bubble.description,
|
|
10955
|
+
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, bold: false, children: [
|
|
10956
|
+
" \xB7 ",
|
|
10957
|
+
running ? strings.subagentRunning : strings.subagentDone
|
|
10958
|
+
] })
|
|
10959
|
+
] }),
|
|
10960
|
+
/* @__PURE__ */ jsxs12(
|
|
10961
|
+
Box12,
|
|
10962
|
+
{
|
|
10963
|
+
flexDirection: "column",
|
|
10964
|
+
height: bodyRows,
|
|
10965
|
+
overflowY: "hidden",
|
|
10966
|
+
justifyContent: "flex-end",
|
|
10967
|
+
minWidth: 0,
|
|
10968
|
+
children: [
|
|
10969
|
+
shown.length === 0 && !bubble.result && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: strings.subagentWaiting }),
|
|
10970
|
+
shown.map((inner) => /* @__PURE__ */ jsx12(BubbleView, { bubble: inner, maxRows: bodyRows }, inner.id)),
|
|
10971
|
+
offset === 0 && !running && bubble.result && shown.length === 0 && /* @__PURE__ */ jsx12(Text12, { children: bubble.result })
|
|
10972
|
+
]
|
|
10973
|
+
}
|
|
10974
|
+
),
|
|
10975
|
+
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
10976
|
+
strings.subagentBack,
|
|
10977
|
+
" \xB7 \u2191/\u2193",
|
|
10978
|
+
offset > 0 ? ` \xB7 \u2212${offset}` : ""
|
|
10979
|
+
] })
|
|
10980
|
+
] });
|
|
10981
|
+
}
|
|
10982
|
+
|
|
10983
|
+
// src/ui/components/TerminalViewport.tsx
|
|
10984
|
+
import { Box as Box13 } from "ink";
|
|
10985
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
10986
|
+
var INTERACTIVE_CHROME_ROWS = 11;
|
|
10987
|
+
var MAX_LIVE_ROWS = 11;
|
|
10988
|
+
var MIN_LIVE_ROWS = 4;
|
|
10989
|
+
function liveTranscriptRows(rows) {
|
|
10990
|
+
if (!Number.isFinite(rows)) return MAX_LIVE_ROWS;
|
|
10991
|
+
return Math.max(MIN_LIVE_ROWS, Math.min(MAX_LIVE_ROWS, Math.floor(rows) - INTERACTIVE_CHROME_ROWS - 2));
|
|
10992
|
+
}
|
|
10993
|
+
function TerminalViewport({ children, rows }) {
|
|
10994
|
+
const { columns } = useTerminalSize();
|
|
10995
|
+
return /* @__PURE__ */ jsx13(
|
|
10996
|
+
Box13,
|
|
10997
|
+
{
|
|
10998
|
+
width: columns,
|
|
10999
|
+
maxWidth: "100%",
|
|
11000
|
+
flexDirection: "column",
|
|
11001
|
+
maxHeight: liveTranscriptRows(rows) + INTERACTIVE_CHROME_ROWS,
|
|
11002
|
+
overflowY: "hidden",
|
|
11003
|
+
children
|
|
11004
|
+
}
|
|
11005
|
+
);
|
|
11006
|
+
}
|
|
11007
|
+
|
|
10066
11008
|
// src/ui/transcript.ts
|
|
10067
11009
|
function emptyTranscript() {
|
|
10068
11010
|
return { bubbles: [], seq: 0 };
|
|
@@ -10103,11 +11045,19 @@ function applyEvent(state, event) {
|
|
|
10103
11045
|
name: sanitizeTerminalText(event.name),
|
|
10104
11046
|
summary: sanitizeTerminalText(event.summary),
|
|
10105
11047
|
state: "running",
|
|
10106
|
-
content: ""
|
|
11048
|
+
content: "",
|
|
11049
|
+
startedAt: Date.now()
|
|
10107
11050
|
}
|
|
10108
11051
|
],
|
|
10109
11052
|
seq: state.seq + 1
|
|
10110
11053
|
};
|
|
11054
|
+
case "tool_progress":
|
|
11055
|
+
return {
|
|
11056
|
+
...state,
|
|
11057
|
+
bubbles: state.bubbles.map(
|
|
11058
|
+
(bubble) => bubble.kind === "tool" && bubble.id === event.id && bubble.state === "running" ? { ...bubble, progress: sanitizeTerminalText(event.text) } : bubble
|
|
11059
|
+
)
|
|
11060
|
+
};
|
|
10111
11061
|
case "tool_end":
|
|
10112
11062
|
return {
|
|
10113
11063
|
...state,
|
|
@@ -10164,6 +11114,7 @@ function applyEvent(state, event) {
|
|
|
10164
11114
|
case "turn_start":
|
|
10165
11115
|
case "usage":
|
|
10166
11116
|
case "rate_limits":
|
|
11117
|
+
case "speed":
|
|
10167
11118
|
return state;
|
|
10168
11119
|
}
|
|
10169
11120
|
}
|
|
@@ -10300,21 +11251,24 @@ function sanitizeDisplay(display) {
|
|
|
10300
11251
|
}
|
|
10301
11252
|
|
|
10302
11253
|
// src/ui/App.tsx
|
|
10303
|
-
import { jsx as
|
|
11254
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
10304
11255
|
var EFFORTS = ["auto", "low", "medium", "high", "xhigh", "max"];
|
|
10305
11256
|
var STREAM_FRAME_MS = 50;
|
|
11257
|
+
var CUSTOM_ACCENT = "__custom__";
|
|
11258
|
+
var BELL_AFTER_MS = 15e3;
|
|
11259
|
+
var SUBAGENT_CHAT_EXTRA_ROWS = 4;
|
|
10306
11260
|
var MAX_ATTACHMENTS = 8;
|
|
10307
11261
|
var CONTEXT_PRESETS = [256e3, 512e3, 1e6];
|
|
10308
|
-
function formatContextTokens(
|
|
10309
|
-
if (
|
|
10310
|
-
return `${Math.round(
|
|
11262
|
+
function formatContextTokens(tokens2) {
|
|
11263
|
+
if (tokens2 >= 1e6) return `${tokens2 / 1e6}M`;
|
|
11264
|
+
return `${Math.round(tokens2 / 1e3)}k`;
|
|
10311
11265
|
}
|
|
10312
11266
|
function parseContextTokens(input) {
|
|
10313
11267
|
const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(input.trim());
|
|
10314
11268
|
if (!match) return null;
|
|
10315
11269
|
const scale = match[2]?.toLowerCase() === "m" ? 1e6 : match[2]?.toLowerCase() === "k" ? 1e3 : 1;
|
|
10316
|
-
const
|
|
10317
|
-
return
|
|
11270
|
+
const tokens2 = Math.round(Number(match[1]) * scale);
|
|
11271
|
+
return tokens2 >= 1e3 && tokens2 <= 1e7 ? tokens2 : null;
|
|
10318
11272
|
}
|
|
10319
11273
|
function App({
|
|
10320
11274
|
runtime,
|
|
@@ -10324,40 +11278,53 @@ function App({
|
|
|
10324
11278
|
const { exit, suspendTerminal } = useApp();
|
|
10325
11279
|
const { stdout } = useStdout2();
|
|
10326
11280
|
const { rows } = useTerminalSize();
|
|
10327
|
-
const [transcript, setTranscript] =
|
|
11281
|
+
const [transcript, setTranscript] = useState8(
|
|
10328
11282
|
() => warnings.reduce((state, text) => pushNotice(state, "warn", text), fromHistory(initialHistory))
|
|
10329
11283
|
);
|
|
10330
|
-
const [transcriptRevision, setTranscriptRevision] =
|
|
10331
|
-
const
|
|
10332
|
-
|
|
11284
|
+
const [transcriptRevision, setTranscriptRevision] = useState8(0);
|
|
11285
|
+
const transcriptRef = useRef5(transcript);
|
|
11286
|
+
transcriptRef.current = transcript;
|
|
11287
|
+
const [input, setInput] = useState8("");
|
|
11288
|
+
const [promptHistory, setPromptHistory] = useState8(
|
|
10333
11289
|
() => inputHistoryFromMessages(initialHistory)
|
|
10334
11290
|
);
|
|
10335
|
-
const [busy, setBusy] =
|
|
10336
|
-
const busyRef =
|
|
10337
|
-
const [pendingCount, setPendingCount] =
|
|
10338
|
-
const queueRef =
|
|
10339
|
-
const slashQueueRef =
|
|
10340
|
-
const slashPendingRef =
|
|
10341
|
-
const drainQueueRef =
|
|
10342
|
-
const [attachments, setAttachments] =
|
|
10343
|
-
const attachmentsRef =
|
|
10344
|
-
const attachmentGeneration =
|
|
10345
|
-
const automaticAttachmentTask =
|
|
10346
|
-
const clipboardPasteTask =
|
|
10347
|
-
const [overlay, setOverlay] =
|
|
10348
|
-
const [accent, setAccent] =
|
|
10349
|
-
const [lang, setLang] =
|
|
10350
|
-
const [setup, setSetup] =
|
|
10351
|
-
const [, forceRender] =
|
|
10352
|
-
const history =
|
|
10353
|
-
const abort =
|
|
10354
|
-
const sessionStart =
|
|
10355
|
-
const turns =
|
|
10356
|
-
const [turnStart, setTurnStart] =
|
|
10357
|
-
const [, tick] =
|
|
10358
|
-
const [context, setContext] =
|
|
10359
|
-
const
|
|
10360
|
-
const
|
|
11291
|
+
const [busy, setBusy] = useState8(false);
|
|
11292
|
+
const busyRef = useRef5(false);
|
|
11293
|
+
const [pendingCount, setPendingCount] = useState8(0);
|
|
11294
|
+
const queueRef = useRef5([]);
|
|
11295
|
+
const slashQueueRef = useRef5(Promise.resolve());
|
|
11296
|
+
const slashPendingRef = useRef5(0);
|
|
11297
|
+
const drainQueueRef = useRef5(() => void 0);
|
|
11298
|
+
const [attachments, setAttachments] = useState8([]);
|
|
11299
|
+
const attachmentsRef = useRef5([]);
|
|
11300
|
+
const attachmentGeneration = useRef5(0);
|
|
11301
|
+
const automaticAttachmentTask = useRef5(null);
|
|
11302
|
+
const clipboardPasteTask = useRef5(null);
|
|
11303
|
+
const [overlay, setOverlay] = useState8({ kind: "none" });
|
|
11304
|
+
const [accent, setAccent] = useState8(runtime.getAccent());
|
|
11305
|
+
const [lang, setLang] = useState8(runtime.getLang());
|
|
11306
|
+
const [setup, setSetup] = useState8(runtime.needsSetup());
|
|
11307
|
+
const [, forceRender] = useState8(0);
|
|
11308
|
+
const history = useRef5(initialHistory);
|
|
11309
|
+
const abort = useRef5(null);
|
|
11310
|
+
const sessionStart = useRef5(Date.now());
|
|
11311
|
+
const turns = useRef5(0);
|
|
11312
|
+
const [turnStart, setTurnStart] = useState8(null);
|
|
11313
|
+
const [, tick] = useState8(0);
|
|
11314
|
+
const [context, setContext] = useState8(() => runtime.modelContext());
|
|
11315
|
+
const speedMeter = useRef5(createSpeedMeter());
|
|
11316
|
+
const undoNote = useRef5(null);
|
|
11317
|
+
const withUndoNote = (content) => {
|
|
11318
|
+
const note = undoNote.current;
|
|
11319
|
+
if (!note) return content;
|
|
11320
|
+
undoNote.current = null;
|
|
11321
|
+
return [{ type: "text", text: note }, ...content];
|
|
11322
|
+
};
|
|
11323
|
+
const transcriptEvents = useRef5([]);
|
|
11324
|
+
const transcriptTimer = useRef5(null);
|
|
11325
|
+
const bell = useCallback(() => {
|
|
11326
|
+
if (stdout.isTTY && runtime.getBell()) stdout.write("\x07");
|
|
11327
|
+
}, [runtime, stdout]);
|
|
10361
11328
|
const clearScreen = useCallback(() => {
|
|
10362
11329
|
if (stdout.isTTY) stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
10363
11330
|
}, [stdout]);
|
|
@@ -10384,6 +11351,7 @@ function App({
|
|
|
10384
11351
|
}, []);
|
|
10385
11352
|
const queueTranscriptEvent = useCallback(
|
|
10386
11353
|
(event) => {
|
|
11354
|
+
recordSpeed(speedMeter.current, event);
|
|
10387
11355
|
transcriptEvents.current.push(event);
|
|
10388
11356
|
if (event.type !== "text_delta" && event.type !== "thinking_delta") {
|
|
10389
11357
|
flushTranscriptEvents();
|
|
@@ -10393,24 +11361,24 @@ function App({
|
|
|
10393
11361
|
},
|
|
10394
11362
|
[flushTranscriptEvents]
|
|
10395
11363
|
);
|
|
10396
|
-
|
|
11364
|
+
useEffect4(
|
|
10397
11365
|
() => () => {
|
|
10398
11366
|
if (transcriptTimer.current !== null) clearTimeout(transcriptTimer.current);
|
|
10399
11367
|
transcriptEvents.current = [];
|
|
10400
11368
|
},
|
|
10401
11369
|
[]
|
|
10402
11370
|
);
|
|
10403
|
-
|
|
11371
|
+
useEffect4(
|
|
10404
11372
|
() => runtime.subscribeContext(() => setContext(runtime.modelContext())),
|
|
10405
11373
|
[runtime]
|
|
10406
11374
|
);
|
|
10407
|
-
|
|
11375
|
+
useEffect4(() => {
|
|
10408
11376
|
if (turnStart === null) return;
|
|
10409
11377
|
const timer = setInterval(() => tick((n) => n + 1), 1e3);
|
|
10410
11378
|
return () => clearInterval(timer);
|
|
10411
11379
|
}, [turnStart]);
|
|
10412
|
-
const theme =
|
|
10413
|
-
const strings =
|
|
11380
|
+
const theme = useMemo6(() => makeTheme(accent), [accent]);
|
|
11381
|
+
const strings = useMemo6(() => stringsFor(lang), [lang]);
|
|
10414
11382
|
const notice = useCallback(
|
|
10415
11383
|
(level, text) => {
|
|
10416
11384
|
flushTranscriptEvents();
|
|
@@ -10418,7 +11386,7 @@ function App({
|
|
|
10418
11386
|
},
|
|
10419
11387
|
[flushTranscriptEvents]
|
|
10420
11388
|
);
|
|
10421
|
-
|
|
11389
|
+
useEffect4(() => {
|
|
10422
11390
|
const check = runtime.startupUpdateCheck();
|
|
10423
11391
|
let active2 = true;
|
|
10424
11392
|
void check.then((result) => {
|
|
@@ -10458,7 +11426,8 @@ function App({
|
|
|
10458
11426
|
abort.current = controller;
|
|
10459
11427
|
busyRef.current = true;
|
|
10460
11428
|
setBusy(true);
|
|
10461
|
-
|
|
11429
|
+
const startedAt = Date.now();
|
|
11430
|
+
setTurnStart(startedAt);
|
|
10462
11431
|
turns.current += 1;
|
|
10463
11432
|
const hooks = {
|
|
10464
11433
|
onEvent: queueTranscriptEvent,
|
|
@@ -10481,7 +11450,11 @@ function App({
|
|
|
10481
11450
|
return;
|
|
10482
11451
|
}
|
|
10483
11452
|
controller.signal.addEventListener("abort", cancel, { once: true });
|
|
10484
|
-
|
|
11453
|
+
bell();
|
|
11454
|
+
setOverlay((current) => {
|
|
11455
|
+
if (current.kind === "picker") current.select("");
|
|
11456
|
+
return { kind: "permission", request, resolve: finish };
|
|
11457
|
+
});
|
|
10485
11458
|
})
|
|
10486
11459
|
};
|
|
10487
11460
|
try {
|
|
@@ -10495,6 +11468,7 @@ function App({
|
|
|
10495
11468
|
} catch (error) {
|
|
10496
11469
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
10497
11470
|
} finally {
|
|
11471
|
+
if (!controller.signal.aborted && Date.now() - startedAt >= BELL_AFTER_MS) bell();
|
|
10498
11472
|
flushTranscriptEvents();
|
|
10499
11473
|
abort.current = null;
|
|
10500
11474
|
busyRef.current = false;
|
|
@@ -10502,9 +11476,14 @@ function App({
|
|
|
10502
11476
|
setTurnStart(null);
|
|
10503
11477
|
drainQueueRef.current();
|
|
10504
11478
|
}
|
|
10505
|
-
}, [flushTranscriptEvents, notice, queueTranscriptEvent, runtime]);
|
|
11479
|
+
}, [bell, flushTranscriptEvents, notice, queueTranscriptEvent, runtime]);
|
|
10506
11480
|
const applyAccent = useCallback(
|
|
10507
|
-
async (
|
|
11481
|
+
async (requested) => {
|
|
11482
|
+
const value = parseAccent(requested);
|
|
11483
|
+
if (!value) {
|
|
11484
|
+
notice("warn", strings.accentInvalid(requested));
|
|
11485
|
+
return;
|
|
11486
|
+
}
|
|
10508
11487
|
await runtime.setAccent(value);
|
|
10509
11488
|
setAccent(value);
|
|
10510
11489
|
notice("info", strings.accentSet(value, resolveAccent(value)));
|
|
@@ -10708,6 +11687,10 @@ function App({
|
|
|
10708
11687
|
notice("info", strings.undoNothing);
|
|
10709
11688
|
return;
|
|
10710
11689
|
}
|
|
11690
|
+
const reverted = [...result.restored, ...result.removed];
|
|
11691
|
+
if (reverted.length > 0) {
|
|
11692
|
+
undoNote.current = `[kitcode] The user ran /undo. These file changes from the previous turn were reverted and are no longer on disk: ${reverted.join(", ")}. Re-read files before relying on them.`;
|
|
11693
|
+
}
|
|
10711
11694
|
const lines = [strings.undoResult(result.restored.length, result.removed.length)];
|
|
10712
11695
|
if (result.conflicts.length > 0) {
|
|
10713
11696
|
lines.push(strings.undoConflicts(result.conflicts));
|
|
@@ -10832,8 +11815,8 @@ ${strings.modelSet(result.nextModel)}` : ""}`;
|
|
|
10832
11815
|
return;
|
|
10833
11816
|
}
|
|
10834
11817
|
try {
|
|
10835
|
-
const
|
|
10836
|
-
notice("info", strings.modelsRefreshed(providerId,
|
|
11818
|
+
const count2 = await runtime.refreshProviderModels(providerId);
|
|
11819
|
+
notice("info", strings.modelsRefreshed(providerId, count2));
|
|
10837
11820
|
} catch (error) {
|
|
10838
11821
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
10839
11822
|
}
|
|
@@ -11133,7 +12116,7 @@ ${strings.mcpAddUsage}`
|
|
|
11133
12116
|
history.current = result.history;
|
|
11134
12117
|
setPromptHistory(inputHistoryFromMessages(result.history));
|
|
11135
12118
|
setTranscript(
|
|
11136
|
-
pushNotice(fromHistory(result.history), "info", strings.compactDone(result.removedMessages))
|
|
12119
|
+
pushNotice(fromHistory(result.history), "info", strings.compactDone(result.removedMessages, result.clearedToolResults ?? 0, result.tokensBefore, result.tokensAfter))
|
|
11137
12120
|
);
|
|
11138
12121
|
setTranscriptRevision((revision) => revision + 1);
|
|
11139
12122
|
await runtime.persist(result.history);
|
|
@@ -11152,17 +12135,27 @@ ${strings.mcpAddUsage}`
|
|
|
11152
12135
|
return;
|
|
11153
12136
|
}
|
|
11154
12137
|
case "subagents": {
|
|
11155
|
-
const agents =
|
|
12138
|
+
const agents = transcriptRef.current.bubbles.filter(
|
|
12139
|
+
(bubble) => bubble.kind === "subagent"
|
|
12140
|
+
);
|
|
11156
12141
|
if (agents.length === 0) {
|
|
11157
|
-
notice("info", strings.
|
|
12142
|
+
notice("info", strings.noSubagents);
|
|
11158
12143
|
return;
|
|
11159
12144
|
}
|
|
11160
|
-
const
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
12145
|
+
const ordered = [
|
|
12146
|
+
...agents.filter((agent) => agent.state === "running"),
|
|
12147
|
+
...agents.filter((agent) => agent.state !== "running").reverse()
|
|
12148
|
+
];
|
|
12149
|
+
const running = ordered.filter((agent) => agent.state === "running");
|
|
12150
|
+
const id = running.length === 1 && rawRest === "" ? running[0].id : await pick(
|
|
12151
|
+
strings.subagentsTitle,
|
|
12152
|
+
ordered.slice(0, 20).map((agent) => ({
|
|
12153
|
+
key: agent.id,
|
|
12154
|
+
label: agent.description || "subagent",
|
|
12155
|
+
hint: agent.state === "running" ? strings.subagentRunning : strings.subagentDone
|
|
12156
|
+
}))
|
|
12157
|
+
);
|
|
12158
|
+
if (id) setOverlay({ kind: "subagent", id });
|
|
11166
12159
|
return;
|
|
11167
12160
|
}
|
|
11168
12161
|
case "checker":
|
|
@@ -11180,9 +12173,21 @@ ${strings.mcpAddUsage}`
|
|
|
11180
12173
|
}
|
|
11181
12174
|
const choice = await pick(
|
|
11182
12175
|
strings.titleAccent,
|
|
11183
|
-
|
|
12176
|
+
[
|
|
12177
|
+
...Object.entries(PRESETS).map(([name, hex]) => ({ key: name, label: name, hint: hex })),
|
|
12178
|
+
{ key: CUSTOM_ACCENT, label: strings.customAccent, hint: "#rrggbb" }
|
|
12179
|
+
]
|
|
11184
12180
|
);
|
|
11185
|
-
if (choice
|
|
12181
|
+
if (choice === CUSTOM_ACCENT) {
|
|
12182
|
+
const typed = await promptInput(strings.customAccentPrompt);
|
|
12183
|
+
if (typed) await applyAccent(typed);
|
|
12184
|
+
} else if (choice) await applyAccent(choice);
|
|
12185
|
+
return;
|
|
12186
|
+
}
|
|
12187
|
+
case "bell": {
|
|
12188
|
+
const next = rawRest === "on" ? true : rawRest === "off" ? false : !runtime.getBell();
|
|
12189
|
+
await runtime.setBell(next);
|
|
12190
|
+
notice("info", strings.bellState(next));
|
|
11186
12191
|
return;
|
|
11187
12192
|
}
|
|
11188
12193
|
case "thinking": {
|
|
@@ -11219,16 +12224,16 @@ ${strings.mcpAddUsage}`
|
|
|
11219
12224
|
case "budget": {
|
|
11220
12225
|
const current = runtime.getMaxTokensPerTurn();
|
|
11221
12226
|
if (rest[0]) {
|
|
11222
|
-
const
|
|
11223
|
-
if (
|
|
12227
|
+
const tokens2 = Number(rest[0]);
|
|
12228
|
+
if (tokens2 === 0) {
|
|
11224
12229
|
await runtime.setMaxTokensPerTurn(0);
|
|
11225
12230
|
notice("info", strings.budgetUnlimited);
|
|
11226
12231
|
forceRender((n) => n + 1);
|
|
11227
|
-
} else if (!Number.isInteger(
|
|
12232
|
+
} else if (!Number.isInteger(tokens2) || tokens2 < 1e3 || tokens2 > 1e7) {
|
|
11228
12233
|
notice("warn", strings.budgetInvalid);
|
|
11229
12234
|
} else {
|
|
11230
|
-
await runtime.setMaxTokensPerTurn(
|
|
11231
|
-
notice("info", strings.budgetSet(
|
|
12235
|
+
await runtime.setMaxTokensPerTurn(tokens2);
|
|
12236
|
+
notice("info", strings.budgetSet(tokens2));
|
|
11232
12237
|
forceRender((n) => n + 1);
|
|
11233
12238
|
}
|
|
11234
12239
|
} else {
|
|
@@ -11247,13 +12252,13 @@ ${strings.mcpAddUsage}`
|
|
|
11247
12252
|
forceRender((n) => n + 1);
|
|
11248
12253
|
return;
|
|
11249
12254
|
}
|
|
11250
|
-
const
|
|
11251
|
-
if (
|
|
12255
|
+
const tokens2 = parseContextTokens(requested);
|
|
12256
|
+
if (tokens2 === null) {
|
|
11252
12257
|
notice("warn", strings.contextInvalid);
|
|
11253
12258
|
return;
|
|
11254
12259
|
}
|
|
11255
|
-
await runtime.setContextWindowOverride(
|
|
11256
|
-
notice("info", strings.contextSet(
|
|
12260
|
+
await runtime.setContextWindowOverride(tokens2);
|
|
12261
|
+
notice("info", strings.contextSet(tokens2));
|
|
11257
12262
|
forceRender((n) => n + 1);
|
|
11258
12263
|
return;
|
|
11259
12264
|
}
|
|
@@ -11436,7 +12441,7 @@ Rename the file if you want a different name.`);
|
|
|
11436
12441
|
return;
|
|
11437
12442
|
}
|
|
11438
12443
|
setTranscript((state) => pushUser(state, userDisplay(item.text, item.content)));
|
|
11439
|
-
history.current = [...history.current, { role: "user", content: item.content }];
|
|
12444
|
+
history.current = [...history.current, { role: "user", content: withUndoNote(item.content) }];
|
|
11440
12445
|
void runAgent();
|
|
11441
12446
|
},
|
|
11442
12447
|
[runAgent, runSlash]
|
|
@@ -11453,6 +12458,49 @@ Rename the file if you want a different name.`);
|
|
|
11453
12458
|
queueMicrotask(() => enqueueNext(next));
|
|
11454
12459
|
}, [enqueueNext]);
|
|
11455
12460
|
drainQueueRef.current = drainQueue;
|
|
12461
|
+
const checkedOut = useRef5(null);
|
|
12462
|
+
const putBackCheckedOut = (currentValue) => {
|
|
12463
|
+
const out = checkedOut.current;
|
|
12464
|
+
if (!out) return null;
|
|
12465
|
+
checkedOut.current = null;
|
|
12466
|
+
const restored = withEditedText(out.item, currentValue, attachmentsRef.current);
|
|
12467
|
+
replaceAttachments([]);
|
|
12468
|
+
const index = Math.min(out.index, queueRef.current.length);
|
|
12469
|
+
queueRef.current = [...queueRef.current.slice(0, index), restored, ...queueRef.current.slice(index)];
|
|
12470
|
+
return index;
|
|
12471
|
+
};
|
|
12472
|
+
const checkOut = (index) => {
|
|
12473
|
+
const item = queueRef.current[index];
|
|
12474
|
+
queueRef.current = queueRef.current.filter((_, at) => at !== index);
|
|
12475
|
+
checkedOut.current = { index, item };
|
|
12476
|
+
replaceAttachments(item.kind === "message" ? item.attachments : []);
|
|
12477
|
+
return item.kind === "command" ? item.line : item.text;
|
|
12478
|
+
};
|
|
12479
|
+
const navigateQueue = useCallback(
|
|
12480
|
+
(direction, currentValue) => {
|
|
12481
|
+
if (!busyRef.current && slashPendingRef.current === 0) {
|
|
12482
|
+
checkedOut.current = null;
|
|
12483
|
+
return null;
|
|
12484
|
+
}
|
|
12485
|
+
if (!checkedOut.current && (currentValue !== "" || queueRef.current.length === 0)) return null;
|
|
12486
|
+
const from = putBackCheckedOut(currentValue);
|
|
12487
|
+
const length = queueRef.current.length;
|
|
12488
|
+
let value;
|
|
12489
|
+
if (from === null) value = direction === "previous" ? checkOut(length - 1) : "";
|
|
12490
|
+
else if (direction === "previous") value = checkOut(Math.max(0, from - 1));
|
|
12491
|
+
else value = from + 1 < length ? checkOut(from + 1) : "";
|
|
12492
|
+
setPendingCount(queueRef.current.length);
|
|
12493
|
+
return value;
|
|
12494
|
+
},
|
|
12495
|
+
[replaceAttachments]
|
|
12496
|
+
);
|
|
12497
|
+
const enqueue = (item) => {
|
|
12498
|
+
const out = checkedOut.current;
|
|
12499
|
+
checkedOut.current = null;
|
|
12500
|
+
const index = out ? Math.min(out.index, queueRef.current.length) : queueRef.current.length;
|
|
12501
|
+
queueRef.current = [...queueRef.current.slice(0, index), item, ...queueRef.current.slice(index)];
|
|
12502
|
+
setPendingCount(queueRef.current.length);
|
|
12503
|
+
};
|
|
11456
12504
|
const tryQueueAutomaticAttachment = useCallback(
|
|
11457
12505
|
(requestedPath) => {
|
|
11458
12506
|
if (!looksLikeAttachmentPath(requestedPath)) return Promise.resolve(false);
|
|
@@ -11463,19 +12511,30 @@ Rename the file if you want a different name.`);
|
|
|
11463
12511
|
notice("warn", `At most ${MAX_ATTACHMENTS} attachments can be queued for one message.`);
|
|
11464
12512
|
return Promise.resolve(true);
|
|
11465
12513
|
}
|
|
11466
|
-
const
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
12514
|
+
const paths = splitAttachmentPaths(requestedPath) ?? [requestedPath];
|
|
12515
|
+
let task;
|
|
12516
|
+
task = (async () => {
|
|
12517
|
+
let added = 0;
|
|
12518
|
+
for (const candidate of paths) {
|
|
12519
|
+
try {
|
|
12520
|
+
const block = await runtime.loadAutomaticAttachment(candidate);
|
|
12521
|
+
if (automaticAttachmentTask.current !== task) return true;
|
|
12522
|
+
if (!block) continue;
|
|
12523
|
+
if (!appendAttachment(block)) {
|
|
12524
|
+
notice("warn", `At most ${MAX_ATTACHMENTS} attachments can be queued for one message.`);
|
|
12525
|
+
return true;
|
|
12526
|
+
}
|
|
12527
|
+
added += 1;
|
|
12528
|
+
notice("info", strings.attachmentAdded(attachmentLabel(block) ?? "attachment"));
|
|
12529
|
+
} catch (error) {
|
|
12530
|
+
if (automaticAttachmentTask.current !== task) return true;
|
|
12531
|
+
if (error instanceof Error && !error.code) {
|
|
12532
|
+
notice("warn", error.message);
|
|
12533
|
+
}
|
|
12534
|
+
}
|
|
11472
12535
|
}
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
}).catch(() => {
|
|
11476
|
-
if (automaticAttachmentTask.current !== task) return true;
|
|
11477
|
-
return false;
|
|
11478
|
-
});
|
|
12536
|
+
return added > 0;
|
|
12537
|
+
})();
|
|
11479
12538
|
automaticAttachmentTask.current = task;
|
|
11480
12539
|
void task.finally(() => {
|
|
11481
12540
|
if (automaticAttachmentTask.current === task) automaticAttachmentTask.current = null;
|
|
@@ -11528,10 +12587,10 @@ Rename the file if you want a different name.`);
|
|
|
11528
12587
|
if (!detachedInput) setInput("");
|
|
11529
12588
|
const command = text.slice(1).trim().split(/\s+/)[0]?.toLowerCase();
|
|
11530
12589
|
if (busyRef.current && !commandRunsWhileBusy(command) || slashPendingRef.current > 0) {
|
|
11531
|
-
|
|
11532
|
-
setPendingCount(queueRef.current.length);
|
|
12590
|
+
enqueue({ kind: "command", line: text });
|
|
11533
12591
|
return;
|
|
11534
12592
|
}
|
|
12593
|
+
checkedOut.current = null;
|
|
11535
12594
|
runSlash(text);
|
|
11536
12595
|
};
|
|
11537
12596
|
if (isKnownSlashCommand(text)) {
|
|
@@ -11569,14 +12628,14 @@ Rename the file if you want a different name.`);
|
|
|
11569
12628
|
...mentioned
|
|
11570
12629
|
];
|
|
11571
12630
|
replaceAttachments([]);
|
|
11572
|
-
const item = { kind: "message", text, content };
|
|
12631
|
+
const item = { kind: "message", text, content, attachments: queuedAttachments };
|
|
11573
12632
|
if (busyRef.current || slashPendingRef.current > 0) {
|
|
11574
|
-
|
|
11575
|
-
setPendingCount(queueRef.current.length);
|
|
12633
|
+
enqueue(item);
|
|
11576
12634
|
return;
|
|
11577
12635
|
}
|
|
12636
|
+
checkedOut.current = null;
|
|
11578
12637
|
setTranscript((state) => pushUser(state, userDisplay(text, content)));
|
|
11579
|
-
history.current = [...history.current, { role: "user", content }];
|
|
12638
|
+
history.current = [...history.current, { role: "user", content: withUndoNote(content) }];
|
|
11580
12639
|
void runAgent();
|
|
11581
12640
|
},
|
|
11582
12641
|
[replaceAttachments, runAgent, runSlash, tryQueueAutomaticAttachment]
|
|
@@ -11589,14 +12648,25 @@ Rename the file if you want a different name.`);
|
|
|
11589
12648
|
return;
|
|
11590
12649
|
}
|
|
11591
12650
|
if (!key.escape) return;
|
|
11592
|
-
if (
|
|
12651
|
+
if (overlay.kind === "subagent") return;
|
|
12652
|
+
if (checkedOut.current && overlay.kind === "none") {
|
|
12653
|
+
if (busyRef.current || slashPendingRef.current > 0) {
|
|
12654
|
+
putBackCheckedOut(input);
|
|
12655
|
+
setPendingCount(queueRef.current.length);
|
|
12656
|
+
setInput("");
|
|
12657
|
+
return;
|
|
12658
|
+
}
|
|
12659
|
+
checkedOut.current = null;
|
|
12660
|
+
}
|
|
12661
|
+
const hasDraft = input !== "" || attachmentsRef.current.length > 0 || automaticAttachmentTask.current !== null || clipboardPasteTask.current !== null;
|
|
12662
|
+
if (busyRef.current && abort.current && !(hasDraft && overlay.kind === "none")) {
|
|
11593
12663
|
if (!abort.current.signal.aborted) {
|
|
11594
12664
|
abort.current.abort();
|
|
11595
12665
|
notice("warn", strings.cancelled);
|
|
11596
12666
|
}
|
|
11597
12667
|
return;
|
|
11598
12668
|
}
|
|
11599
|
-
if (
|
|
12669
|
+
if (hasDraft) {
|
|
11600
12670
|
attachmentGeneration.current += 1;
|
|
11601
12671
|
setInput("");
|
|
11602
12672
|
replaceAttachments([]);
|
|
@@ -11621,10 +12691,10 @@ Rename the file if you want a different name.`);
|
|
|
11621
12691
|
}
|
|
11622
12692
|
setInput("");
|
|
11623
12693
|
});
|
|
11624
|
-
const shell = (children) => /* @__PURE__ */
|
|
12694
|
+
const shell = (children) => /* @__PURE__ */ jsx14(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx14(StringsContext.Provider, { value: strings, children }) });
|
|
11625
12695
|
if (lang === void 0) {
|
|
11626
12696
|
return shell(
|
|
11627
|
-
/* @__PURE__ */
|
|
12697
|
+
/* @__PURE__ */ jsx14(
|
|
11628
12698
|
LanguagePicker,
|
|
11629
12699
|
{
|
|
11630
12700
|
onPick: (chosen) => {
|
|
@@ -11637,7 +12707,7 @@ Rename the file if you want a different name.`);
|
|
|
11637
12707
|
}
|
|
11638
12708
|
if (setup) {
|
|
11639
12709
|
return shell(
|
|
11640
|
-
/* @__PURE__ */
|
|
12710
|
+
/* @__PURE__ */ jsx14(
|
|
11641
12711
|
Onboarding,
|
|
11642
12712
|
{
|
|
11643
12713
|
onSubmit: async (url, key) => {
|
|
@@ -11652,17 +12722,17 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11652
12722
|
);
|
|
11653
12723
|
}
|
|
11654
12724
|
return shell(
|
|
11655
|
-
/* @__PURE__ */
|
|
11656
|
-
/* @__PURE__ */
|
|
12725
|
+
/* @__PURE__ */ jsxs13(TerminalViewport, { rows, children: [
|
|
12726
|
+
/* @__PURE__ */ jsx14(
|
|
11657
12727
|
Transcript,
|
|
11658
12728
|
{
|
|
11659
12729
|
bubbles: transcript.bubbles,
|
|
11660
12730
|
workspace: runtime.cwd,
|
|
11661
|
-
maxLiveRows: liveTranscriptRows(rows)
|
|
12731
|
+
maxLiveRows: overlay.kind === "subagent" ? 0 : liveTranscriptRows(rows)
|
|
11662
12732
|
},
|
|
11663
12733
|
transcriptRevision
|
|
11664
12734
|
),
|
|
11665
|
-
overlay.kind === "permission" && /* @__PURE__ */
|
|
12735
|
+
overlay.kind === "permission" && /* @__PURE__ */ jsx14(
|
|
11666
12736
|
PermissionPrompt,
|
|
11667
12737
|
{
|
|
11668
12738
|
request: overlay.request,
|
|
@@ -11672,7 +12742,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11672
12742
|
}
|
|
11673
12743
|
}
|
|
11674
12744
|
),
|
|
11675
|
-
overlay.kind === "picker" && /* @__PURE__ */
|
|
12745
|
+
overlay.kind === "picker" && /* @__PURE__ */ jsx14(
|
|
11676
12746
|
Picker,
|
|
11677
12747
|
{
|
|
11678
12748
|
title: overlay.title,
|
|
@@ -11687,7 +12757,17 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11687
12757
|
}
|
|
11688
12758
|
}
|
|
11689
12759
|
),
|
|
11690
|
-
overlay.kind === "
|
|
12760
|
+
overlay.kind === "subagent" && /* @__PURE__ */ jsx14(
|
|
12761
|
+
SubagentChat,
|
|
12762
|
+
{
|
|
12763
|
+
bubble: transcript.bubbles.find(
|
|
12764
|
+
(bubble) => bubble.kind === "subagent" && bubble.id === overlay.id
|
|
12765
|
+
),
|
|
12766
|
+
rows: liveTranscriptRows(rows) + SUBAGENT_CHAT_EXTRA_ROWS,
|
|
12767
|
+
onClose: () => setOverlay({ kind: "none" })
|
|
12768
|
+
}
|
|
12769
|
+
),
|
|
12770
|
+
overlay.kind === "confirm" && /* @__PURE__ */ jsx14(
|
|
11691
12771
|
Confirm,
|
|
11692
12772
|
{
|
|
11693
12773
|
title: overlay.title,
|
|
@@ -11699,7 +12779,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11699
12779
|
}
|
|
11700
12780
|
}
|
|
11701
12781
|
),
|
|
11702
|
-
overlay.kind === "textinput" && /* @__PURE__ */
|
|
12782
|
+
overlay.kind === "textinput" && /* @__PURE__ */ jsx14(
|
|
11703
12783
|
TextInputOverlay,
|
|
11704
12784
|
{
|
|
11705
12785
|
title: overlay.title,
|
|
@@ -11714,7 +12794,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11714
12794
|
}
|
|
11715
12795
|
}
|
|
11716
12796
|
),
|
|
11717
|
-
overlay.kind === "none" && /* @__PURE__ */
|
|
12797
|
+
overlay.kind === "none" && /* @__PURE__ */ jsx14(
|
|
11718
12798
|
PromptInput,
|
|
11719
12799
|
{
|
|
11720
12800
|
value: input,
|
|
@@ -11725,7 +12805,11 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11725
12805
|
onListFiles: listWorkspaceFiles,
|
|
11726
12806
|
disabled: busy,
|
|
11727
12807
|
pending: pendingCount,
|
|
11728
|
-
|
|
12808
|
+
queued: queueRef.current.map((item) => item.kind === "command" ? item.line : item.text || attachmentSummary(item.attachments)),
|
|
12809
|
+
onNavigateQueue: navigateQueue,
|
|
12810
|
+
hint: busy ? input !== "" || attachments.length > 0 ? strings.escClear : strings.escCancel : void 0,
|
|
12811
|
+
mode: runtime.getMode(),
|
|
12812
|
+
bypass: runtime.isBypassEnabled(),
|
|
11729
12813
|
history: promptHistory,
|
|
11730
12814
|
attachments: attachments.flatMap((block) => {
|
|
11731
12815
|
const label = attachmentLabel(block);
|
|
@@ -11733,21 +12817,23 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11733
12817
|
})
|
|
11734
12818
|
}
|
|
11735
12819
|
),
|
|
11736
|
-
/* @__PURE__ */
|
|
12820
|
+
/* @__PURE__ */ jsx14(
|
|
11737
12821
|
StatusBar,
|
|
11738
12822
|
{
|
|
11739
12823
|
status: {
|
|
11740
12824
|
modelRef: runtime.getModelRef() || strings.noModel,
|
|
11741
12825
|
effort: runtime.getEffort(),
|
|
12826
|
+
effectiveEffort: runtime.getEffectiveEffort(history.current),
|
|
11742
12827
|
thinking: runtime.getThinking(),
|
|
11743
12828
|
usage: runtime.usageParts(),
|
|
12829
|
+
speed: readSpeed(speedMeter.current, busy),
|
|
11744
12830
|
mcp: runtime.mcpSummary(),
|
|
11745
12831
|
mode: runtime.getMode(),
|
|
11746
12832
|
bypass: runtime.isBypassEnabled(),
|
|
11747
12833
|
busy,
|
|
11748
12834
|
sessionMs: Date.now() - sessionStart.current,
|
|
11749
12835
|
turnMs: turnStart === null ? null : Date.now() - turnStart,
|
|
11750
|
-
|
|
12836
|
+
agents: runtime.activeAgentsList().map((agent) => agent.description),
|
|
11751
12837
|
context
|
|
11752
12838
|
}
|
|
11753
12839
|
}
|
|
@@ -11761,17 +12847,17 @@ function TextInputOverlay({
|
|
|
11761
12847
|
onAnswer,
|
|
11762
12848
|
onCancel
|
|
11763
12849
|
}) {
|
|
11764
|
-
const [value, setValue] =
|
|
12850
|
+
const [value, setValue] = useState8("");
|
|
11765
12851
|
const theme = useTheme();
|
|
11766
12852
|
const submit = useCallback(() => {
|
|
11767
12853
|
onAnswer(value || null);
|
|
11768
12854
|
}, [value, onAnswer]);
|
|
11769
|
-
|
|
12855
|
+
useInput3((_input, key) => {
|
|
11770
12856
|
if (key.escape) onCancel();
|
|
11771
12857
|
});
|
|
11772
|
-
return /* @__PURE__ */
|
|
11773
|
-
/* @__PURE__ */
|
|
11774
|
-
/* @__PURE__ */
|
|
12858
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
12859
|
+
/* @__PURE__ */ jsx14(Text13, { dimColor: true, children: title }),
|
|
12860
|
+
/* @__PURE__ */ jsx14(
|
|
11775
12861
|
TextInput2,
|
|
11776
12862
|
{
|
|
11777
12863
|
value,
|
|
@@ -11780,9 +12866,24 @@ function TextInputOverlay({
|
|
|
11780
12866
|
mask: mask ? "\u2022" : void 0
|
|
11781
12867
|
}
|
|
11782
12868
|
),
|
|
11783
|
-
/* @__PURE__ */
|
|
12869
|
+
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, color: theme.accent, children: "enter submit \xB7 esc cancel" }) })
|
|
11784
12870
|
] });
|
|
11785
12871
|
}
|
|
12872
|
+
function withEditedText(item, text, attachments) {
|
|
12873
|
+
if (item.kind === "command") {
|
|
12874
|
+
if (text.startsWith("/")) return { kind: "command", line: text };
|
|
12875
|
+
return { kind: "message", text, attachments, content: messageContent(text, attachments, []) };
|
|
12876
|
+
}
|
|
12877
|
+
if (text === item.text && attachments === item.attachments) return item;
|
|
12878
|
+
const mentioned = item.content.filter((block) => block.type !== "text" && !item.attachments.includes(block));
|
|
12879
|
+
return { kind: "message", text, attachments, content: messageContent(text, attachments, mentioned) };
|
|
12880
|
+
}
|
|
12881
|
+
function messageContent(text, attachments, mentioned) {
|
|
12882
|
+
return [...text ? [{ type: "text", text }] : [], ...attachments, ...mentioned];
|
|
12883
|
+
}
|
|
12884
|
+
function attachmentSummary(blocks) {
|
|
12885
|
+
return "\u{1F4CE} " + blocks.map((block) => attachmentLabel(block) ?? "attachment").join(", ");
|
|
12886
|
+
}
|
|
11786
12887
|
function isKnownSlashCommand(line) {
|
|
11787
12888
|
if (!line.startsWith("/")) return false;
|
|
11788
12889
|
const [name = ""] = line.slice(1).trim().toLowerCase().split(/\s+/);
|
|
@@ -11853,7 +12954,7 @@ function unquoteArg(value) {
|
|
|
11853
12954
|
}
|
|
11854
12955
|
|
|
11855
12956
|
// src/app/tui.tsx
|
|
11856
|
-
import { jsx as
|
|
12957
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
11857
12958
|
function clearTerminal() {
|
|
11858
12959
|
if (process.stdout.isTTY) process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
11859
12960
|
}
|
|
@@ -11873,7 +12974,7 @@ async function startTui(options) {
|
|
|
11873
12974
|
});
|
|
11874
12975
|
clearTerminal();
|
|
11875
12976
|
const instance = render(
|
|
11876
|
-
/* @__PURE__ */
|
|
12977
|
+
/* @__PURE__ */ jsx15(App, { runtime, initialHistory: history, warnings }),
|
|
11877
12978
|
TUI_RENDER_OPTIONS
|
|
11878
12979
|
);
|
|
11879
12980
|
try {
|