@kernelonpanic/kitcode 1.3.3 → 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 +47 -8
- package/README.ru.md +47 -8
- package/dist/index.js +1746 -539
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -75,6 +75,11 @@ 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),
|
|
80
|
+
// Manual override for the model's context window. Unset means the window
|
|
81
|
+
// comes from the provider catalogue or the endpoint's model list.
|
|
82
|
+
contextWindow: z.number().int().min(1e3).max(1e7).optional(),
|
|
78
83
|
maxTokens: z.number().int().positive().max(2e5).default(64e3),
|
|
79
84
|
budget: budgetSchema.default({
|
|
80
85
|
maxTokensPerTurn: 1e7,
|
|
@@ -1396,15 +1401,39 @@ async function clearProjectMemory(workspace) {
|
|
|
1396
1401
|
}
|
|
1397
1402
|
|
|
1398
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");
|
|
1399
1405
|
function resolveEffort(effort, messages) {
|
|
1400
1406
|
if (effort !== "auto") return effort;
|
|
1401
1407
|
const latest = messages.findLast((message) => message.role === "user" && message.content.some((block) => block.type === "text"));
|
|
1402
1408
|
const text = latest?.content.filter((block) => block.type === "text").map((block) => block.text).join("\n") ?? "";
|
|
1403
|
-
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;
|
|
1404
1428
|
}
|
|
1405
1429
|
function openAiEffort(model, effort) {
|
|
1406
|
-
const id = model.split("/").at(-1) ?? model;
|
|
1407
|
-
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
|
+
}
|
|
1408
1437
|
if (/^(?:o[134](?:-|$)|gpt-5(?:-(?:mini|nano|\d{4})|$)|gpt-5\.1(?:-|$))/.test(id)) {
|
|
1409
1438
|
return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
1410
1439
|
}
|
|
@@ -1487,9 +1516,10 @@ async function loadSession(id) {
|
|
|
1487
1516
|
async function listSessions(limit = 20) {
|
|
1488
1517
|
const files = await filesByRecency();
|
|
1489
1518
|
const summaries = [];
|
|
1490
|
-
for (const file of files
|
|
1519
|
+
for (const file of files) {
|
|
1520
|
+
if (summaries.length >= limit) break;
|
|
1491
1521
|
const state = readState(await readFile4(file, "utf8").catch(() => ""));
|
|
1492
|
-
if (state) {
|
|
1522
|
+
if (state && state.messages.length > 0) {
|
|
1493
1523
|
summaries.push({
|
|
1494
1524
|
id: state.id,
|
|
1495
1525
|
title: state.title,
|
|
@@ -1846,6 +1876,17 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1846
1876
|
hooks.onEvent({ type: "turn_end", stopReason: "max_tokens" });
|
|
1847
1877
|
return messages;
|
|
1848
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
|
+
}
|
|
1849
1890
|
const request = async () => {
|
|
1850
1891
|
if (signal.aborted) return { content: [], stopReason: "aborted" };
|
|
1851
1892
|
const budgetDecision = cfg.budget?.beforeRequest({
|
|
@@ -1920,13 +1961,19 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1920
1961
|
let outcome;
|
|
1921
1962
|
let text = "";
|
|
1922
1963
|
let thinking = "";
|
|
1964
|
+
let outputTokens = 0;
|
|
1965
|
+
const startedAt = Date.now();
|
|
1966
|
+
let firstTokenAt;
|
|
1923
1967
|
for await (const event of streamWithRetry(cfg.provider, request, cfg.modelRef, signal)) {
|
|
1924
1968
|
switch (event.type) {
|
|
1925
1969
|
case "text_delta":
|
|
1970
|
+
firstTokenAt ??= Date.now();
|
|
1926
1971
|
text += event.text;
|
|
1927
1972
|
hooks.onEvent({ type: "text_delta", text: event.text });
|
|
1928
1973
|
break;
|
|
1929
1974
|
case "thinking_delta":
|
|
1975
|
+
firstTokenAt ??= Date.now();
|
|
1976
|
+
if (!cfg.thinking) break;
|
|
1930
1977
|
thinking += event.text;
|
|
1931
1978
|
hooks.onEvent({ type: "thinking_delta", text: event.text });
|
|
1932
1979
|
break;
|
|
@@ -1934,6 +1981,7 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1934
1981
|
cfg.budget?.record(cfg.modelRef, event.usage);
|
|
1935
1982
|
cfg.usage.record(cfg.modelRef, event.usage);
|
|
1936
1983
|
hooks.onEvent({ type: "usage", model: cfg.modelRef, usage: event.usage });
|
|
1984
|
+
outputTokens = event.usage.output;
|
|
1937
1985
|
break;
|
|
1938
1986
|
case "rate_limits":
|
|
1939
1987
|
hooks.onEvent({ type: "rate_limits", model: cfg.modelRef, limits: event.limits });
|
|
@@ -1945,7 +1993,13 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
1945
1993
|
break;
|
|
1946
1994
|
}
|
|
1947
1995
|
}
|
|
1948
|
-
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
|
+
}
|
|
1949
2003
|
if (signal.aborted) {
|
|
1950
2004
|
const content = [];
|
|
1951
2005
|
if (thinking) content.push({ type: "thinking", text: thinking });
|
|
@@ -2066,23 +2120,21 @@ async function runToolCalls(cfg, content, hooks, signal) {
|
|
|
2066
2120
|
const operation = () => runOneCall(cfg, call, hooks, signal);
|
|
2067
2121
|
return call.name !== "task" && cfg.executionLane ? cfg.executionLane.run(operation) : operation();
|
|
2068
2122
|
};
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
results.push(result.value);
|
|
2085
|
-
}
|
|
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());
|
|
2086
2138
|
}
|
|
2087
2139
|
return results;
|
|
2088
2140
|
}
|
|
@@ -2099,6 +2151,7 @@ async function runOneCall(cfg, call, hooks, signal) {
|
|
|
2099
2151
|
requestPermission: (request) => hooks.requestPermission(request),
|
|
2100
2152
|
checkpoint: cfg.checkpoint,
|
|
2101
2153
|
onEvent: (event) => hooks.onEvent(event),
|
|
2154
|
+
onProgress: (text) => hooks.onEvent({ type: "tool_progress", id: call.id, text }),
|
|
2102
2155
|
confirm: async (question) => await hooks.requestPermission({
|
|
2103
2156
|
toolName: tool.name,
|
|
2104
2157
|
summary: question,
|
|
@@ -2409,13 +2462,13 @@ function beginCheckpoint(options) {
|
|
|
2409
2462
|
async function undoLatestCheckpoint(options) {
|
|
2410
2463
|
assertSessionId2(options.sessionId);
|
|
2411
2464
|
const root = workspaceRoot(options.cwd);
|
|
2412
|
-
const
|
|
2413
|
-
const file = await newestCheckpoint(sessionDir);
|
|
2465
|
+
const storageDir = options.storageDir ?? checkpointsDir;
|
|
2414
2466
|
const empty = { found: false, restored: [], removed: [], conflicts: [], failed: [] };
|
|
2467
|
+
const file = await newestCheckpoint(path6.join(storageDir, options.sessionId)) ?? await newestWorkspaceCheckpoint(storageDir, root);
|
|
2415
2468
|
if (!file) return empty;
|
|
2416
2469
|
const checkpoint = await readCheckpoint(file);
|
|
2417
|
-
if (checkpoint.
|
|
2418
|
-
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.");
|
|
2419
2472
|
}
|
|
2420
2473
|
const result = { ...empty, found: true };
|
|
2421
2474
|
const retry = [];
|
|
@@ -2585,6 +2638,27 @@ function decodeSnapshot(snapshot) {
|
|
|
2585
2638
|
function validBase64(value) {
|
|
2586
2639
|
return value.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(value);
|
|
2587
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
|
+
}
|
|
2588
2662
|
async function newestCheckpoint(sessionDir) {
|
|
2589
2663
|
let names;
|
|
2590
2664
|
try {
|
|
@@ -2623,6 +2697,7 @@ var DEFAULT_TIMEOUT = 12e4;
|
|
|
2623
2697
|
var MAX_TIMEOUT = 6e5;
|
|
2624
2698
|
var MAX_OUTPUT = 1e5;
|
|
2625
2699
|
var MAX_COMMAND_CHARS = 2e4;
|
|
2700
|
+
var PROGRESS_INTERVAL_MS = 250;
|
|
2626
2701
|
var bashTool = {
|
|
2627
2702
|
name: "bash",
|
|
2628
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.",
|
|
@@ -2686,6 +2761,19 @@ var bashTool = {
|
|
|
2686
2761
|
windowsHide: true
|
|
2687
2762
|
});
|
|
2688
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
|
+
};
|
|
2689
2777
|
let captured = 0;
|
|
2690
2778
|
let truncated = false;
|
|
2691
2779
|
const collect = (buf) => {
|
|
@@ -2696,6 +2784,7 @@ var bashTool = {
|
|
|
2696
2784
|
} catch {
|
|
2697
2785
|
text = buf.toString();
|
|
2698
2786
|
}
|
|
2787
|
+
progress(text);
|
|
2699
2788
|
if (text.length > MAX_OUTPUT - captured) {
|
|
2700
2789
|
chunks.push(text.slice(0, MAX_OUTPUT - captured));
|
|
2701
2790
|
truncated = true;
|
|
@@ -2909,7 +2998,7 @@ async function isFile2(file) {
|
|
|
2909
2998
|
|
|
2910
2999
|
// src/core/attachments.ts
|
|
2911
3000
|
import { execFile } from "child_process";
|
|
2912
|
-
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";
|
|
2913
3002
|
import os2 from "os";
|
|
2914
3003
|
import path8 from "path";
|
|
2915
3004
|
import { fileURLToPath } from "url";
|
|
@@ -3004,14 +3093,15 @@ async function loadClipboardImage(platform = process.platform, runner = runClipb
|
|
|
3004
3093
|
if (data.length > MAX_IMAGE_BYTES) {
|
|
3005
3094
|
throw new Error(`Clipboard image is larger than ${formatBytes(MAX_IMAGE_BYTES)}.`);
|
|
3006
3095
|
}
|
|
3096
|
+
const fitted = await fitImage(data, mediaType, platform);
|
|
3007
3097
|
return {
|
|
3008
3098
|
type: "image",
|
|
3009
|
-
mediaType,
|
|
3010
|
-
data: data.toString("base64"),
|
|
3011
|
-
name: clipboardImageName(mediaType)
|
|
3099
|
+
mediaType: fitted.mediaType,
|
|
3100
|
+
data: fitted.data.toString("base64"),
|
|
3101
|
+
name: clipboardImageName(fitted.mediaType)
|
|
3012
3102
|
};
|
|
3013
3103
|
} catch (error) {
|
|
3014
|
-
if (error instanceof Error &&
|
|
3104
|
+
if (error instanceof Error && /^(?:Clipboard image|Image) is larger/.test(error.message)) {
|
|
3015
3105
|
throw error;
|
|
3016
3106
|
}
|
|
3017
3107
|
}
|
|
@@ -3028,10 +3118,11 @@ async function loadResolvedAttachment(resolved, size) {
|
|
|
3028
3118
|
const data = await readFile7(resolved);
|
|
3029
3119
|
const mediaType = detectImage(data);
|
|
3030
3120
|
if (mediaType) {
|
|
3121
|
+
const fitted = await fitImage(data, mediaType);
|
|
3031
3122
|
return {
|
|
3032
3123
|
path: resolved,
|
|
3033
|
-
bytes: data.length,
|
|
3034
|
-
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 }
|
|
3035
3126
|
};
|
|
3036
3127
|
}
|
|
3037
3128
|
if (data.length > MAX_TEXT_BYTES) {
|
|
@@ -3221,6 +3312,85 @@ try {
|
|
|
3221
3312
|
$image.Dispose()
|
|
3222
3313
|
}
|
|
3223
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
|
+
`;
|
|
3224
3394
|
function textMime(file) {
|
|
3225
3395
|
const extension = path8.extname(file).toLowerCase();
|
|
3226
3396
|
const known = {
|
|
@@ -3249,13 +3419,27 @@ function normalizeInputPath(value) {
|
|
|
3249
3419
|
return value.slice(1, -1);
|
|
3250
3420
|
}
|
|
3251
3421
|
}
|
|
3422
|
+
if (WINDOWS_PATH.test(value)) return value.replace(/\\ /g, " ");
|
|
3252
3423
|
return value.replace(/\\([\\ "'()\[\]&;#$!])/g, (_match, character) => character);
|
|
3253
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
|
+
}
|
|
3254
3438
|
function safeName(value) {
|
|
3255
3439
|
return value.replace(/[\r\n\0-\x1f\x7f]/g, "").slice(0, 200) || "attachment";
|
|
3256
3440
|
}
|
|
3257
3441
|
function formatBytes(value) {
|
|
3258
|
-
return `${
|
|
3442
|
+
return `${(value / 1024 / 1024).toFixed(1).replace(/\.0$/, "")} MB`;
|
|
3259
3443
|
}
|
|
3260
3444
|
|
|
3261
3445
|
// src/core/compact.ts
|
|
@@ -3271,11 +3455,15 @@ var MAX_FILE_BLOCK_CHARS = 2e3;
|
|
|
3271
3455
|
var MAX_TOOL_RESULT_CHARS = 1200;
|
|
3272
3456
|
var MAX_TOOL_ERROR_CHARS = 2e3;
|
|
3273
3457
|
var MAX_TOOL_INPUT_CHARS = 800;
|
|
3274
|
-
var SINGLE_MAX_TOKENS =
|
|
3275
|
-
var CHUNK_MAX_TOKENS =
|
|
3276
|
-
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;
|
|
3277
3465
|
var CHUNK_CONCURRENCY = 3;
|
|
3278
|
-
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.`;
|
|
3279
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.";
|
|
3280
3468
|
async function compactHistory(options) {
|
|
3281
3469
|
const cut = compactCutIndex(options.history);
|
|
@@ -3348,7 +3536,7 @@ async function compactHistory(options) {
|
|
|
3348
3536
|
const prefix = [
|
|
3349
3537
|
{
|
|
3350
3538
|
role: "user",
|
|
3351
|
-
content: [{ type: "text", text:
|
|
3539
|
+
content: [{ type: "text", text: `${SUMMARY_MARKER}
|
|
3352
3540
|
${summary2}` }]
|
|
3353
3541
|
},
|
|
3354
3542
|
{
|
|
@@ -3356,14 +3544,70 @@ ${summary2}` }]
|
|
|
3356
3544
|
content: [{ type: "text", text: "Earlier context loaded. I will continue from this summary." }]
|
|
3357
3545
|
}
|
|
3358
3546
|
];
|
|
3547
|
+
const kept = clearOldToolResults(options.history.slice(cut));
|
|
3359
3548
|
return {
|
|
3360
|
-
history: [...prefix, ...
|
|
3549
|
+
history: [...prefix, ...withoutThinkingReplay(kept.history)],
|
|
3361
3550
|
compacted: true,
|
|
3362
3551
|
removedMessages: cut,
|
|
3552
|
+
clearedToolResults: kept.cleared,
|
|
3363
3553
|
usage,
|
|
3364
3554
|
rateLimits
|
|
3365
3555
|
};
|
|
3366
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
|
+
}
|
|
3367
3611
|
function shouldAutoCompact(used, window) {
|
|
3368
3612
|
return window !== null && window > 0 && used / window >= 0.8;
|
|
3369
3613
|
}
|
|
@@ -3407,7 +3651,7 @@ function estimateCompactBudget(history, maxTokens = MERGE_MAX_TOKENS) {
|
|
|
3407
3651
|
function isConversationUser(message) {
|
|
3408
3652
|
if (message.role !== "user") return false;
|
|
3409
3653
|
return message.content.some(
|
|
3410
|
-
(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)
|
|
3411
3655
|
);
|
|
3412
3656
|
}
|
|
3413
3657
|
function chunkRenderedMessages(messages, targetChars, overlapMessages, overlapMaxChars) {
|
|
@@ -3613,6 +3857,9 @@ ${blocks.join("\n")}`] : [];
|
|
|
3613
3857
|
function renderBlock(block, role) {
|
|
3614
3858
|
switch (block.type) {
|
|
3615
3859
|
case "text":
|
|
3860
|
+
if (role === "user" && block.text.startsWith(SUMMARY_MARKER)) {
|
|
3861
|
+
return truncate(block.text, MAX_PREVIOUS_SUMMARY_CHARS);
|
|
3862
|
+
}
|
|
3616
3863
|
return truncate(
|
|
3617
3864
|
block.text,
|
|
3618
3865
|
role === "user" ? MAX_USER_TEXT_CHARS : MAX_ASSISTANT_TEXT_CHARS
|
|
@@ -3689,7 +3936,7 @@ function buildSystemPrompt(args) {
|
|
|
3689
3936
|
|
|
3690
3937
|
// src/core/subagent.ts
|
|
3691
3938
|
var TASK_TOOL_NAME = "task";
|
|
3692
|
-
var MAX_SUBAGENT_STEPS =
|
|
3939
|
+
var MAX_SUBAGENT_STEPS = 30;
|
|
3693
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.";
|
|
3694
3941
|
var SUBAGENT_CANCELLED = "The subagent was cancelled before it produced anything.";
|
|
3695
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.`;
|
|
@@ -3704,12 +3951,12 @@ var SUBAGENT_SYSTEM = [
|
|
|
3704
3951
|
].join("\n");
|
|
3705
3952
|
function createSubagentRunner(makeConfig, tools) {
|
|
3706
3953
|
const permissionLane = createExecutionLane();
|
|
3707
|
-
const executionLane = createExecutionLane();
|
|
3708
|
-
const modelRequestLane = createExecutionLane();
|
|
3709
3954
|
return {
|
|
3710
3955
|
async run(request) {
|
|
3711
3956
|
if (request.signal.aborted) return SUBAGENT_CANCELLED;
|
|
3712
3957
|
let steps = 0;
|
|
3958
|
+
const executionLane = createExecutionLane();
|
|
3959
|
+
const modelRequestLane = createExecutionLane();
|
|
3713
3960
|
const base = makeConfig(SUBAGENT_SYSTEM, tools);
|
|
3714
3961
|
const cfg = {
|
|
3715
3962
|
...base,
|
|
@@ -3721,9 +3968,12 @@ function createSubagentRunner(makeConfig, tools) {
|
|
|
3721
3968
|
kind: base.provider.kind,
|
|
3722
3969
|
listModels: () => base.provider.listModels(),
|
|
3723
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.
|
|
3724
3975
|
stream: (chat) => base.provider.stream(steps === MAX_SUBAGENT_STEPS ? {
|
|
3725
3976
|
...chat,
|
|
3726
|
-
tools: [],
|
|
3727
3977
|
system: `${chat.system}
|
|
3728
3978
|
|
|
3729
3979
|
This is your last model call. Do not call tools. Return your findings now, clearly stating any unfinished work.`
|
|
@@ -3854,9 +4104,10 @@ function formatUsageBreakdown(tracker) {
|
|
|
3854
4104
|
lines.push("", `total ${formatUsageCompact(tracker)}`);
|
|
3855
4105
|
return lines.join("\n");
|
|
3856
4106
|
}
|
|
3857
|
-
function tokens(
|
|
3858
|
-
if (
|
|
3859
|
-
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"];
|
|
3860
4111
|
return `${value.toFixed(1).replace(/\.0$/, "")}${suffix}`;
|
|
3861
4112
|
}
|
|
3862
4113
|
function dollars(amount) {
|
|
@@ -3940,7 +4191,7 @@ function clip(value) {
|
|
|
3940
4191
|
// package.json
|
|
3941
4192
|
var package_default = {
|
|
3942
4193
|
name: "@kernelonpanic/kitcode",
|
|
3943
|
-
version: "1.
|
|
4194
|
+
version: "1.4.1",
|
|
3944
4195
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
3945
4196
|
type: "module",
|
|
3946
4197
|
license: "MIT",
|
|
@@ -3973,6 +4224,7 @@ var package_default = {
|
|
|
3973
4224
|
dependencies: {
|
|
3974
4225
|
"@anthropic-ai/sdk": "^0.115.0",
|
|
3975
4226
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
4227
|
+
build: "^0.1.4",
|
|
3976
4228
|
chalk: "^6.0.0",
|
|
3977
4229
|
commander: "^15.0.0",
|
|
3978
4230
|
"fast-glob": "^3.3.3",
|
|
@@ -4008,7 +4260,7 @@ var package_default = {
|
|
|
4008
4260
|
|
|
4009
4261
|
// src/version.ts
|
|
4010
4262
|
var KITCODE_VERSION = package_default.version;
|
|
4011
|
-
var KITCODE_COMMIT = true ? "
|
|
4263
|
+
var KITCODE_COMMIT = true ? "be753d255e7675a850d0ca1ee64f790f68d3fd93" : "development";
|
|
4012
4264
|
|
|
4013
4265
|
// src/mcp/client.ts
|
|
4014
4266
|
var clientInfo = { name: "kitcode", version: KITCODE_VERSION };
|
|
@@ -4393,7 +4645,7 @@ async function limitedResponseText(response, limit) {
|
|
|
4393
4645
|
}
|
|
4394
4646
|
|
|
4395
4647
|
// src/providers/catalog.ts
|
|
4396
|
-
import { chmod as chmod4, readFile as readFile8, writeFile as
|
|
4648
|
+
import { chmod as chmod4, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
4397
4649
|
import path9 from "path";
|
|
4398
4650
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
4399
4651
|
var CACHE_VERSION = 3;
|
|
@@ -4455,7 +4707,7 @@ async function writeCache(file, models) {
|
|
|
4455
4707
|
const payload = { version: CACHE_VERSION, fetchedAt: Date.now(), models };
|
|
4456
4708
|
try {
|
|
4457
4709
|
await ensureDir(path9.dirname(file));
|
|
4458
|
-
await
|
|
4710
|
+
await writeFile7(file, JSON.stringify(payload), { encoding: "utf8", mode: 384 });
|
|
4459
4711
|
await chmod4(file, 384);
|
|
4460
4712
|
} catch {
|
|
4461
4713
|
return;
|
|
@@ -4603,8 +4855,11 @@ async function* streamTurn(client, providerId, apiKey2, req) {
|
|
|
4603
4855
|
messages: toMessageParams(req.messages),
|
|
4604
4856
|
tools: toToolParams(req.tools)
|
|
4605
4857
|
};
|
|
4606
|
-
if (req.thinking)
|
|
4607
|
-
|
|
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);
|
|
4608
4863
|
if (effort) params.output_config = { effort };
|
|
4609
4864
|
let thinking = "";
|
|
4610
4865
|
let text = "";
|
|
@@ -4683,6 +4938,11 @@ function toMessageParams(messages) {
|
|
|
4683
4938
|
const content = message.content.flatMap(toBlockParams);
|
|
4684
4939
|
if (content.length > 0) params.push({ role: message.role, content });
|
|
4685
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
|
+
}
|
|
4686
4946
|
return params;
|
|
4687
4947
|
}
|
|
4688
4948
|
function toBlockParams(block) {
|
|
@@ -4752,6 +5012,15 @@ function toUsage(usage) {
|
|
|
4752
5012
|
cacheRead: usage.cache_read_input_tokens ?? 0
|
|
4753
5013
|
};
|
|
4754
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
|
+
}
|
|
4755
5024
|
function toRefusal(details) {
|
|
4756
5025
|
return { category: details?.category ?? null, explanation: details?.explanation ?? null };
|
|
4757
5026
|
}
|
|
@@ -4784,6 +5053,57 @@ function createOpenAiProvider(args) {
|
|
|
4784
5053
|
};
|
|
4785
5054
|
}
|
|
4786
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) {
|
|
4787
5107
|
let text = "";
|
|
4788
5108
|
let thinking = "";
|
|
4789
5109
|
let finishReason = null;
|
|
@@ -4793,13 +5113,13 @@ async function* streamTurn2(client, providerId, apiKey2, req) {
|
|
|
4793
5113
|
let recognised = 0;
|
|
4794
5114
|
const calls = /* @__PURE__ */ new Map();
|
|
4795
5115
|
const capture = captureResponseHead();
|
|
4796
|
-
const effort = openAiEffort(req.model, resolveEffort(req.effort, req.messages));
|
|
4797
5116
|
try {
|
|
4798
5117
|
const stream = await client.withOptions({ fetch: capture.fetch }).chat.completions.create(
|
|
4799
5118
|
{
|
|
4800
5119
|
model: req.model,
|
|
4801
|
-
|
|
5120
|
+
[tokenField]: req.maxTokens,
|
|
4802
5121
|
...effort ? { reasoning_effort: effort } : {},
|
|
5122
|
+
...thinkingOff,
|
|
4803
5123
|
messages: toChatMessages(req.system, req.messages),
|
|
4804
5124
|
...req.tools.length > 0 ? { tools: toChatTools(req.tools) } : {},
|
|
4805
5125
|
stream: true,
|
|
@@ -4989,14 +5309,22 @@ function toUsage2(usage) {
|
|
|
4989
5309
|
return normalizeOpenAiUsage(usage);
|
|
4990
5310
|
}
|
|
4991
5311
|
function normalizeOpenAiUsage(usage) {
|
|
4992
|
-
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));
|
|
4993
5318
|
return {
|
|
4994
|
-
input:
|
|
4995
|
-
output: usage.completion_tokens
|
|
4996
|
-
cacheWrite
|
|
5319
|
+
input: prompt - cacheRead - cacheWrite,
|
|
5320
|
+
output: count(usage.completion_tokens),
|
|
5321
|
+
cacheWrite,
|
|
4997
5322
|
cacheRead
|
|
4998
5323
|
};
|
|
4999
5324
|
}
|
|
5325
|
+
function count(value) {
|
|
5326
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
5327
|
+
}
|
|
5000
5328
|
|
|
5001
5329
|
// src/providers/registry.ts
|
|
5002
5330
|
function createRegistry(config, auth) {
|
|
@@ -5045,6 +5373,7 @@ var PLAN_REFUSAL = "Plan mode is on: this tool changes state and is unavailable.
|
|
|
5045
5373
|
function createPermissionEngine(configPermissions) {
|
|
5046
5374
|
const sessionGrants = /* @__PURE__ */ new Set();
|
|
5047
5375
|
let bypassEnabled = false;
|
|
5376
|
+
let bypassUnlocked = false;
|
|
5048
5377
|
let current = "normal";
|
|
5049
5378
|
const resolve3 = (tool, requested) => configPermissions[tool.name] ?? requested ?? tool.defaultPermission;
|
|
5050
5379
|
return {
|
|
@@ -5071,9 +5400,12 @@ function createPermissionEngine(configPermissions) {
|
|
|
5071
5400
|
bypass: {
|
|
5072
5401
|
enable() {
|
|
5073
5402
|
bypassEnabled = true;
|
|
5403
|
+
bypassUnlocked = true;
|
|
5404
|
+
current = "normal";
|
|
5074
5405
|
},
|
|
5075
5406
|
disable() {
|
|
5076
5407
|
bypassEnabled = false;
|
|
5408
|
+
bypassUnlocked = false;
|
|
5077
5409
|
},
|
|
5078
5410
|
isEnabled() {
|
|
5079
5411
|
return bypassEnabled;
|
|
@@ -5085,7 +5417,18 @@ function createPermissionEngine(configPermissions) {
|
|
|
5085
5417
|
current = mode;
|
|
5086
5418
|
},
|
|
5087
5419
|
cycle() {
|
|
5088
|
-
|
|
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;
|
|
5089
5432
|
return current;
|
|
5090
5433
|
}
|
|
5091
5434
|
}
|
|
@@ -5153,7 +5496,7 @@ import { readFile as readFile9, stat as stat6 } from "fs/promises";
|
|
|
5153
5496
|
|
|
5154
5497
|
// src/tools/safe-write.ts
|
|
5155
5498
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5156
|
-
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";
|
|
5157
5500
|
import { basename as basename2, dirname as dirname2, join as join2 } from "path";
|
|
5158
5501
|
var UnsafeFileChangeError = class extends Error {
|
|
5159
5502
|
constructor(message) {
|
|
@@ -5216,7 +5559,7 @@ async function atomicWriteSafeFile(file, data, snapshot) {
|
|
|
5216
5559
|
await rename5(temp, file);
|
|
5217
5560
|
} catch (error) {
|
|
5218
5561
|
await handle?.close().catch(() => void 0);
|
|
5219
|
-
await
|
|
5562
|
+
await rm5(temp, { force: true }).catch(() => void 0);
|
|
5220
5563
|
throw error;
|
|
5221
5564
|
}
|
|
5222
5565
|
}
|
|
@@ -5279,12 +5622,12 @@ async function lstatMaybe(file) {
|
|
|
5279
5622
|
var MAX_FILE_BYTES2 = 5e6;
|
|
5280
5623
|
function countOccurrences(haystack, needle) {
|
|
5281
5624
|
if (needle === "") return 0;
|
|
5282
|
-
let
|
|
5625
|
+
let count2 = 0;
|
|
5283
5626
|
let from = 0;
|
|
5284
5627
|
for (; ; ) {
|
|
5285
5628
|
const at = haystack.indexOf(needle, from);
|
|
5286
|
-
if (at === -1) return
|
|
5287
|
-
|
|
5629
|
+
if (at === -1) return count2;
|
|
5630
|
+
count2 += 1;
|
|
5288
5631
|
from = at + needle.length;
|
|
5289
5632
|
}
|
|
5290
5633
|
}
|
|
@@ -5343,16 +5686,16 @@ var editTool = {
|
|
|
5343
5686
|
return { content: `Cannot edit ${path15}: it is a binary file, not text.`, isError: true };
|
|
5344
5687
|
}
|
|
5345
5688
|
const before = beforeBuffer.toString("utf8");
|
|
5346
|
-
const
|
|
5347
|
-
if (
|
|
5689
|
+
const count2 = countOccurrences(before, oldString);
|
|
5690
|
+
if (count2 === 0) {
|
|
5348
5691
|
return {
|
|
5349
5692
|
content: `oldString was not found in ${path15}. Read the file again and match the text exactly, including whitespace.`,
|
|
5350
5693
|
isError: true
|
|
5351
5694
|
};
|
|
5352
5695
|
}
|
|
5353
|
-
if (
|
|
5696
|
+
if (count2 > 1 && !replaceAll) {
|
|
5354
5697
|
return {
|
|
5355
|
-
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.`,
|
|
5356
5699
|
isError: true
|
|
5357
5700
|
};
|
|
5358
5701
|
}
|
|
@@ -5376,14 +5719,14 @@ var editTool = {
|
|
|
5376
5719
|
return { content: `Failed to write ${path15}: ${error.message}`, isError: true };
|
|
5377
5720
|
}
|
|
5378
5721
|
return {
|
|
5379
|
-
content: `Edited ${path15} (${
|
|
5722
|
+
content: `Edited ${path15} (${count2} ${count2 === 1 ? "replacement" : "replacements"})`,
|
|
5380
5723
|
display: { kind: "diff", path: path15, before, after }
|
|
5381
5724
|
};
|
|
5382
5725
|
}
|
|
5383
5726
|
};
|
|
5384
5727
|
function replacement(before, oldString, newString, replaceAll) {
|
|
5385
|
-
const
|
|
5386
|
-
if (
|
|
5728
|
+
const count2 = countOccurrences(before, oldString);
|
|
5729
|
+
if (count2 === 0 || count2 > 1 && !replaceAll) return null;
|
|
5387
5730
|
if (replaceAll) return before.split(oldString).join(newString);
|
|
5388
5731
|
const at = before.indexOf(oldString);
|
|
5389
5732
|
return before.slice(0, at) + newString + before.slice(at + oldString.length);
|
|
@@ -5815,7 +6158,7 @@ function createToolRegistry(tools) {
|
|
|
5815
6158
|
}
|
|
5816
6159
|
|
|
5817
6160
|
// src/prompts/library.ts
|
|
5818
|
-
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";
|
|
5819
6162
|
import path11 from "path";
|
|
5820
6163
|
async function savePrompt(input) {
|
|
5821
6164
|
const slug = slugify(input.name);
|
|
@@ -5829,7 +6172,7 @@ async function savePrompt(input) {
|
|
|
5829
6172
|
};
|
|
5830
6173
|
await ensureDir(promptsDir);
|
|
5831
6174
|
const file = path11.join(promptsDir, `${slug}.md`);
|
|
5832
|
-
await
|
|
6175
|
+
await writeFile8(file, serialize(prompt), {
|
|
5833
6176
|
encoding: "utf8",
|
|
5834
6177
|
mode: 384
|
|
5835
6178
|
});
|
|
@@ -6372,7 +6715,10 @@ function createTaskTool(runner) {
|
|
|
6372
6715
|
required: ["description", "prompt"],
|
|
6373
6716
|
additionalProperties: false
|
|
6374
6717
|
},
|
|
6375
|
-
|
|
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,
|
|
6376
6722
|
summarize(input) {
|
|
6377
6723
|
return `task(${brief(field(input, "description"))})`;
|
|
6378
6724
|
},
|
|
@@ -6546,6 +6892,7 @@ async function readJsonBounded(response) {
|
|
|
6546
6892
|
|
|
6547
6893
|
// src/app/runtime.ts
|
|
6548
6894
|
var PREFERRED2 = ["claude-opus-5", "claude-sonnet-5", "gpt-5", "claude-opus-4-8"];
|
|
6895
|
+
var MID_TURN_TRIM_RATIO = 0.8;
|
|
6549
6896
|
async function boot(options) {
|
|
6550
6897
|
const loadedConfig = await loadRuntimeConfig(options.cwd);
|
|
6551
6898
|
const config = loadedConfig.config;
|
|
@@ -6619,6 +6966,7 @@ async function boot(options) {
|
|
|
6619
6966
|
const notifyContext = () => {
|
|
6620
6967
|
for (const listener of contextListeners) listener();
|
|
6621
6968
|
};
|
|
6969
|
+
const resolveContextWindow = (ref, discovered) => config.contextWindow ?? discovered ?? knownContextWindow(ref);
|
|
6622
6970
|
const knownContextWindow = (ref) => {
|
|
6623
6971
|
const parsed = parseModelRef(ref);
|
|
6624
6972
|
if (!parsed) return null;
|
|
@@ -6635,27 +6983,27 @@ async function boot(options) {
|
|
|
6635
6983
|
modelRef = ref;
|
|
6636
6984
|
if (latestRateLimits?.model !== ref) latestRateLimits = void 0;
|
|
6637
6985
|
contextUsage = restoredContext?.model === ref ? restoredContext : void 0;
|
|
6638
|
-
modelContextWindow =
|
|
6986
|
+
modelContextWindow = resolveContextWindow(ref, discoveredWindow);
|
|
6639
6987
|
notifyContext();
|
|
6640
6988
|
};
|
|
6641
|
-
modelContextWindow =
|
|
6989
|
+
modelContextWindow = resolveContextWindow(modelRef);
|
|
6642
6990
|
async function refreshModelContextWindow() {
|
|
6643
6991
|
const targetRef = modelRef;
|
|
6644
6992
|
const parsed = parseModelRef(targetRef);
|
|
6645
6993
|
if (!parsed) {
|
|
6646
6994
|
if (modelRef === targetRef) {
|
|
6647
|
-
modelContextWindow = null;
|
|
6995
|
+
modelContextWindow = config.contextWindow ?? null;
|
|
6648
6996
|
notifyContext();
|
|
6649
6997
|
}
|
|
6650
6998
|
return;
|
|
6651
6999
|
}
|
|
6652
|
-
let next =
|
|
7000
|
+
let next = resolveContextWindow(targetRef);
|
|
6653
7001
|
try {
|
|
6654
7002
|
const provider = registry.get(parsed.provider);
|
|
6655
7003
|
const models = await loadModels(provider);
|
|
6656
7004
|
rememberModels(parsed.provider, models);
|
|
6657
7005
|
const found = models.find((m) => m.id === parsed.model);
|
|
6658
|
-
next = found?.contextWindow
|
|
7006
|
+
next = resolveContextWindow(targetRef, found?.contextWindow);
|
|
6659
7007
|
} catch {
|
|
6660
7008
|
}
|
|
6661
7009
|
if (modelRef === targetRef) {
|
|
@@ -6678,9 +7026,16 @@ async function boot(options) {
|
|
|
6678
7026
|
effort: config.effort,
|
|
6679
7027
|
thinking: config.thinking,
|
|
6680
7028
|
budget: activeBudget,
|
|
6681
|
-
checkpoint: activeCheckpoint
|
|
7029
|
+
checkpoint: activeCheckpoint,
|
|
7030
|
+
trimContext: (messages) => trimForWindow(resolved.provider.kind, messages)
|
|
6682
7031
|
};
|
|
6683
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
|
+
};
|
|
6684
7039
|
let activeBudget;
|
|
6685
7040
|
let activeCheckpoint;
|
|
6686
7041
|
let activeAgentCount = 0;
|
|
@@ -6727,8 +7082,22 @@ async function boot(options) {
|
|
|
6727
7082
|
return save;
|
|
6728
7083
|
};
|
|
6729
7084
|
const compactWithBudget = async (history, signal, budget) => {
|
|
7085
|
+
const tokensBefore = estimateHistoryTokens(history);
|
|
6730
7086
|
if (compactCutIndex(history) <= 0) {
|
|
6731
|
-
|
|
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
|
+
};
|
|
6732
7101
|
}
|
|
6733
7102
|
const resolved = registry.resolve(modelRef);
|
|
6734
7103
|
const estimate = estimateCompactBudget(history, config.maxTokens);
|
|
@@ -6756,7 +7125,7 @@ async function boot(options) {
|
|
|
6756
7125
|
session.context = void 0;
|
|
6757
7126
|
notifyContext();
|
|
6758
7127
|
}
|
|
6759
|
-
return result;
|
|
7128
|
+
return { ...result, tokensBefore, tokensAfter: estimateHistoryTokens(result.history) };
|
|
6760
7129
|
};
|
|
6761
7130
|
const runtime = {
|
|
6762
7131
|
cwd: options.cwd,
|
|
@@ -6983,26 +7352,50 @@ async function boot(options) {
|
|
|
6983
7352
|
effortDescription(history) {
|
|
6984
7353
|
if (!modelRef) return "unavailable";
|
|
6985
7354
|
const { provider, modelId } = registry.resolve(modelRef);
|
|
6986
|
-
const
|
|
6987
|
-
|
|
6988
|
-
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`;
|
|
6989
7357
|
},
|
|
6990
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
|
+
},
|
|
6991
7368
|
async setEffort(effort) {
|
|
6992
7369
|
await persistConfig((draft) => {
|
|
6993
7370
|
draft.effort = effort;
|
|
6994
7371
|
});
|
|
6995
7372
|
},
|
|
7373
|
+
getBell: () => config.bell,
|
|
7374
|
+
async setBell(enabled) {
|
|
7375
|
+
await persistConfig((draft) => {
|
|
7376
|
+
draft.bell = enabled;
|
|
7377
|
+
});
|
|
7378
|
+
},
|
|
6996
7379
|
getThinking: () => config.thinking,
|
|
6997
7380
|
async setThinking(enabled) {
|
|
6998
7381
|
await persistConfig((draft) => {
|
|
6999
7382
|
draft.thinking = enabled;
|
|
7000
7383
|
});
|
|
7001
7384
|
},
|
|
7385
|
+
getContextWindowOverride: () => config.contextWindow,
|
|
7386
|
+
async setContextWindowOverride(tokens2) {
|
|
7387
|
+
await persistConfig((draft) => {
|
|
7388
|
+
if (tokens2 === void 0) delete draft.contextWindow;
|
|
7389
|
+
else draft.contextWindow = tokens2;
|
|
7390
|
+
});
|
|
7391
|
+
modelContextWindow = config.contextWindow ?? resolveContextWindow(modelRef);
|
|
7392
|
+
notifyContext();
|
|
7393
|
+
void refreshModelContextWindow();
|
|
7394
|
+
},
|
|
7002
7395
|
getMaxTokensPerTurn: () => config.budget.maxTokensPerTurn,
|
|
7003
|
-
async setMaxTokensPerTurn(
|
|
7396
|
+
async setMaxTokensPerTurn(tokens2) {
|
|
7004
7397
|
await persistConfig((draft) => {
|
|
7005
|
-
draft.budget.maxTokensPerTurn =
|
|
7398
|
+
draft.budget.maxTokensPerTurn = tokens2;
|
|
7006
7399
|
draft.budget.maxCostUsdPerTurn = 0;
|
|
7007
7400
|
});
|
|
7008
7401
|
},
|
|
@@ -7257,7 +7650,7 @@ ${lines.join("\n")}` : null;
|
|
|
7257
7650
|
hooks.onEvent({
|
|
7258
7651
|
type: "notice",
|
|
7259
7652
|
level: "info",
|
|
7260
|
-
text:
|
|
7653
|
+
text: describeCompaction(result)
|
|
7261
7654
|
});
|
|
7262
7655
|
}
|
|
7263
7656
|
} catch (error) {
|
|
@@ -7276,10 +7669,6 @@ ${lines.join("\n")}` : null;
|
|
|
7276
7669
|
const trackedHooks = {
|
|
7277
7670
|
...hooks,
|
|
7278
7671
|
onEvent(event) {
|
|
7279
|
-
if (event.type === "turn_start") {
|
|
7280
|
-
contextUsage = void 0;
|
|
7281
|
-
notifyContext();
|
|
7282
|
-
}
|
|
7283
7672
|
if (event.type === "usage") {
|
|
7284
7673
|
contextUsage = { model: requestModelRef, usage: event.usage };
|
|
7285
7674
|
notifyContext();
|
|
@@ -7333,6 +7722,7 @@ ${lines.join("\n")}` : null;
|
|
|
7333
7722
|
context: contextUsage
|
|
7334
7723
|
};
|
|
7335
7724
|
session = snapshot;
|
|
7725
|
+
if (history.length === 0) return;
|
|
7336
7726
|
const save = persistQueue.catch(() => void 0).then(() => saveSession(snapshot));
|
|
7337
7727
|
persistQueue = save;
|
|
7338
7728
|
await save;
|
|
@@ -7370,6 +7760,13 @@ function preferredDetectedModel(detected) {
|
|
|
7370
7760
|
}
|
|
7371
7761
|
return candidates[0];
|
|
7372
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
|
+
}
|
|
7373
7770
|
|
|
7374
7771
|
// src/app/ask.ts
|
|
7375
7772
|
var RU_YES = /* @__PURE__ */ new Set(["y", "\u043D"]);
|
|
@@ -7556,8 +7953,8 @@ function validateKey(value) {
|
|
|
7556
7953
|
import { render } from "ink";
|
|
7557
7954
|
|
|
7558
7955
|
// src/ui/App.tsx
|
|
7559
|
-
import { Box as
|
|
7560
|
-
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";
|
|
7561
7958
|
|
|
7562
7959
|
// src/ui/terminal-size.ts
|
|
7563
7960
|
import { useStdout } from "ink";
|
|
@@ -7631,37 +8028,74 @@ function stdioServer(name, command, args) {
|
|
|
7631
8028
|
return parsed.success ? { ok: true, name, config: parsed.data } : { ok: false, error: "usage" };
|
|
7632
8029
|
}
|
|
7633
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
|
+
|
|
7634
8065
|
// src/ui/commands.ts
|
|
8066
|
+
var BUSY_SAFE = { runsWhileBusy: true };
|
|
7635
8067
|
var COMMANDS = [
|
|
7636
|
-
{ name: "model" },
|
|
8068
|
+
{ name: "model", ...BUSY_SAFE },
|
|
7637
8069
|
{ name: "login" },
|
|
7638
8070
|
{ name: "provider" },
|
|
7639
8071
|
{ name: "logout", args: "[provider]" },
|
|
7640
8072
|
{ name: "key", args: "[provider]" },
|
|
7641
8073
|
{ name: "refresh", args: "[provider]" },
|
|
7642
|
-
{ name: "effort" },
|
|
7643
|
-
{ name: "thinking" },
|
|
7644
|
-
{ name: "
|
|
7645
|
-
{ name: "
|
|
7646
|
-
{ name: "
|
|
8074
|
+
{ name: "effort", ...BUSY_SAFE },
|
|
8075
|
+
{ name: "thinking", ...BUSY_SAFE },
|
|
8076
|
+
{ name: "bell", args: "[on|off]", ...BUSY_SAFE },
|
|
8077
|
+
{ name: "context", args: "[tokens|auto]", ...BUSY_SAFE },
|
|
8078
|
+
{ name: "budget", args: "[tokens]", ...BUSY_SAFE },
|
|
8079
|
+
{ name: "theme", args: "[color]", ...BUSY_SAFE },
|
|
8080
|
+
{ name: "lang", ...BUSY_SAFE },
|
|
7647
8081
|
{ name: "prompt", args: "[save <name>|delete <name>]" },
|
|
7648
8082
|
{ name: "skills", args: "[install <source>]" },
|
|
7649
8083
|
{ name: "bypass" },
|
|
7650
|
-
{ name: "usage" },
|
|
8084
|
+
{ name: "usage", ...BUSY_SAFE },
|
|
7651
8085
|
{ name: "mcp", args: "[add|list|delete|enable|disable]" },
|
|
7652
8086
|
{ name: "attach", args: "<path|clipboard|clear>" },
|
|
7653
8087
|
{ name: "memory", args: "[show|set <text>|clear]" },
|
|
7654
8088
|
{ name: "compact" },
|
|
7655
8089
|
{ name: "update" },
|
|
7656
|
-
{ name: "checker" },
|
|
8090
|
+
{ name: "checker", ...BUSY_SAFE },
|
|
7657
8091
|
{ name: "sessions", args: "[list|rename|delete [all]|export]" },
|
|
7658
|
-
{ name: "config" },
|
|
8092
|
+
{ name: "config", ...BUSY_SAFE },
|
|
7659
8093
|
{ name: "resume" },
|
|
7660
8094
|
{ name: "undo" },
|
|
7661
8095
|
{ name: "clear" },
|
|
7662
|
-
{ name: "
|
|
7663
|
-
{ name: "
|
|
7664
|
-
{ name: "
|
|
8096
|
+
{ name: "subagents", ...BUSY_SAFE },
|
|
8097
|
+
{ name: "help", ...BUSY_SAFE },
|
|
8098
|
+
{ name: "exit", ...BUSY_SAFE }
|
|
7665
8099
|
];
|
|
7666
8100
|
var SUBCOMMANDS = [
|
|
7667
8101
|
{ name: "mcp add", args: "<name> <url|-- command>" },
|
|
@@ -7675,6 +8109,10 @@ var SUBCOMMANDS = [
|
|
|
7675
8109
|
{ name: "sessions delete all" },
|
|
7676
8110
|
{ name: "sessions export", args: "[id] [path]" }
|
|
7677
8111
|
];
|
|
8112
|
+
function commandRunsWhileBusy(name) {
|
|
8113
|
+
if (name === "quit") return true;
|
|
8114
|
+
return COMMANDS.some((command) => command.name === name && command.runsWhileBusy === true);
|
|
8115
|
+
}
|
|
7678
8116
|
function matchCommands(line) {
|
|
7679
8117
|
if (!line.startsWith("/")) return [];
|
|
7680
8118
|
const token = line.slice(1);
|
|
@@ -7712,17 +8150,23 @@ function isSubsequence2(needle, haystack) {
|
|
|
7712
8150
|
return needle.length === 0;
|
|
7713
8151
|
}
|
|
7714
8152
|
function editDistance(a, b) {
|
|
8153
|
+
let beforePrevious = [];
|
|
7715
8154
|
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
7716
8155
|
for (let i = 1; i <= a.length; i += 1) {
|
|
7717
8156
|
const current = [i];
|
|
7718
8157
|
for (let j = 1; j <= b.length; j += 1) {
|
|
7719
8158
|
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
7720
|
-
|
|
8159
|
+
let best = Math.min(
|
|
7721
8160
|
(current[j - 1] ?? 0) + 1,
|
|
7722
8161
|
(previous[j] ?? 0) + 1,
|
|
7723
8162
|
(previous[j - 1] ?? 0) + cost
|
|
7724
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;
|
|
7725
8168
|
}
|
|
8169
|
+
beforePrevious = previous;
|
|
7726
8170
|
previous = current;
|
|
7727
8171
|
}
|
|
7728
8172
|
return previous[b.length] ?? 0;
|
|
@@ -7783,11 +8227,19 @@ var PRESETS = {
|
|
|
7783
8227
|
white: "#e5e7eb"
|
|
7784
8228
|
};
|
|
7785
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
|
+
}
|
|
7786
8238
|
function resolveAccent(value) {
|
|
7787
8239
|
if (!value) return DEFAULT_ACCENT;
|
|
7788
|
-
const
|
|
7789
|
-
if (
|
|
7790
|
-
return
|
|
8240
|
+
const parsed = parseAccent(value);
|
|
8241
|
+
if (!parsed) return DEFAULT_ACCENT;
|
|
8242
|
+
return PRESETS[parsed] ?? parsed;
|
|
7791
8243
|
}
|
|
7792
8244
|
function makeTheme(accent) {
|
|
7793
8245
|
return {
|
|
@@ -7851,10 +8303,12 @@ var en = {
|
|
|
7851
8303
|
placeholder: "ask, or / for commands",
|
|
7852
8304
|
working: "working\u2026",
|
|
7853
8305
|
suggestHelp: "\u2191\u2193 move \xB7 tab complete \xB7 enter run",
|
|
7854
|
-
more: (
|
|
8306
|
+
more: (count2) => `\u2026 ${count2} more`,
|
|
7855
8307
|
allowOnce: "allow once",
|
|
7856
8308
|
always: "always",
|
|
7857
8309
|
deny: "deny",
|
|
8310
|
+
dangerousCommand: "risky command",
|
|
8311
|
+
permissionKeys: "\u2191/\u2193 choose \xB7 enter confirm \xB7 y/a/n \xB7 esc deny",
|
|
7858
8312
|
yes: "yes",
|
|
7859
8313
|
noDefault: "no (default)",
|
|
7860
8314
|
noMatches: "no matches",
|
|
@@ -7869,7 +8323,7 @@ var en = {
|
|
|
7869
8323
|
titleModelRefresh: "Reload models for a provider",
|
|
7870
8324
|
titleSessions: "Resume a session",
|
|
7871
8325
|
titleSessionAction: "Session action",
|
|
7872
|
-
resumed: (id,
|
|
8326
|
+
resumed: (id, count2) => `Resumed ${id} \u2014 ${count2} messages restored`,
|
|
7873
8327
|
sessionsEmpty: "No saved sessions yet.",
|
|
7874
8328
|
providerSet: (id, model) => `Provider: ${id} \xB7 model ${model}`,
|
|
7875
8329
|
providersEmpty: "No providers configured. Use /login to add one.",
|
|
@@ -7877,11 +8331,15 @@ var en = {
|
|
|
7877
8331
|
providerBalance: (balances) => `provider balance: ${balances}`,
|
|
7878
8332
|
providerKeyRemaining: (balances) => `API key limit remaining: ${balances}`,
|
|
7879
8333
|
noThink: "no-think",
|
|
7880
|
-
mcpFailed: (
|
|
8334
|
+
mcpFailed: (count2) => `mcp:${count2} failed`,
|
|
7881
8335
|
escCancel: "esc to cancel",
|
|
8336
|
+
escClear: "esc clears the input \xB7 esc again cancels the answer",
|
|
7882
8337
|
escHelp: "esc \u2014 cancel the running turn",
|
|
7883
8338
|
cancelled: "Cancelled.",
|
|
7884
|
-
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",
|
|
7885
8343
|
configAt: (path15) => `config: ${path15}`,
|
|
7886
8344
|
skillsEmpty: "No skills installed. Drop a folder with a SKILL.md into ~/.kitcode/skills or ./.kitcode/skills",
|
|
7887
8345
|
skillsInstallUsage: "Usage: /skills install <github-url|npm-package|local-path>",
|
|
@@ -7900,7 +8358,7 @@ var en = {
|
|
|
7900
8358
|
mcpDeleted: (name) => `MCP "${name}" disconnected and removed.`,
|
|
7901
8359
|
mcpDeleteFailed: (name, error) => `Could not remove MCP "${name}": ${error}`,
|
|
7902
8360
|
mcpNotFound: (name) => `MCP server "${name}" was not found.`,
|
|
7903
|
-
mcpTools: (
|
|
8361
|
+
mcpTools: (count2) => `${count2} tools`,
|
|
7904
8362
|
mcpEnabled: (name, tools) => `MCP "${name}" enabled \xB7 ${tools} tools`,
|
|
7905
8363
|
mcpDisabled: (name) => `MCP "${name}" disabled.`,
|
|
7906
8364
|
mcpToggleFailed: (name, error) => `Could not change MCP "${name}": ${error}`,
|
|
@@ -7910,14 +8368,24 @@ var en = {
|
|
|
7910
8368
|
error: "error",
|
|
7911
8369
|
disabled: "disabled"
|
|
7912
8370
|
},
|
|
7913
|
-
|
|
7914
|
-
|
|
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",
|
|
7915
8378
|
attachUsage: "Usage: /attach <image-or-text-file> \xB7 /attach clipboard \xB7 /attach clear",
|
|
7916
8379
|
attachmentAdded: (name) => `Attached for the next message: ${name}`,
|
|
7917
8380
|
attachmentsCleared: "Pending attachments cleared.",
|
|
7918
8381
|
compactNothing: "Not enough older conversation to compact yet.",
|
|
7919
8382
|
compactStarting: "Compacting context\u2026",
|
|
7920
|
-
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 ") + ".",
|
|
7921
8389
|
sessionRenameUsage: "Type the title after the command: /sessions rename <id> <title>",
|
|
7922
8390
|
sessionRenamed: (title) => `Session renamed to "${title}".`,
|
|
7923
8391
|
sessionDeleteAsk: "The saved chat file will be deleted. This cannot be undone.",
|
|
@@ -7926,7 +8394,7 @@ var en = {
|
|
|
7926
8394
|
sessionDeleteAllAskBody: "All KitCode sessions from every workspace will be permanently deleted.",
|
|
7927
8395
|
sessionDeleteAllFinalAsk: "Final confirmation: delete all chats?",
|
|
7928
8396
|
sessionDeleteAllFinalBody: "This cannot be undone. Press y only if you want to erase the complete session history.",
|
|
7929
|
-
sessionsDeletedAll: (
|
|
8397
|
+
sessionsDeletedAll: (count2) => `All saved chats deleted \xB7 ${count2} sessions removed.`,
|
|
7930
8398
|
sessionsDeleteAllFailed: (deleted, failed) => `Session cleanup was incomplete \xB7 ${deleted} removed, ${failed} failed.`,
|
|
7931
8399
|
sessionExported: (path15) => `Session exported to ${path15}`,
|
|
7932
8400
|
sessionActionResume: "resume",
|
|
@@ -7939,14 +8407,18 @@ ${url}`,
|
|
|
7939
8407
|
updateCurrent: (version) => `KitCode ${version} is up to date.`,
|
|
7940
8408
|
updateFailed: (reason) => `Could not check for updates: ${reason}.`,
|
|
7941
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",
|
|
7942
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.",
|
|
7943
8415
|
effortSet: (value) => `Effort: ${value}`,
|
|
7944
|
-
budgetCurrent: (
|
|
7945
|
-
budgetSet: (
|
|
8416
|
+
budgetCurrent: (tokens2) => `Token budget: ${tokens2.toLocaleString()} per turn`,
|
|
8417
|
+
budgetSet: (tokens2) => `Token budget set to ${tokens2.toLocaleString()} per turn`,
|
|
7946
8418
|
budgetUnlimited: "Token budget: unlimited (0). Set /budget <tokens> to restore a limit.",
|
|
7947
8419
|
budgetInvalid: "Invalid budget: must be between 1,000 and 10,000,000",
|
|
7948
8420
|
modelSet: (value) => `Model: ${value}`,
|
|
7949
|
-
modelsRefreshed: (provider,
|
|
8421
|
+
modelsRefreshed: (provider, count2) => `${provider}: ${count2} models reloaded from the endpoint.`,
|
|
7950
8422
|
noModels: "No models available. Use /login to add a provider.",
|
|
7951
8423
|
bypassOff: "Approval prompts are back on.",
|
|
7952
8424
|
bypassAsk1: "Disable approval prompts for this session?",
|
|
@@ -7973,10 +8445,17 @@ ${url}`,
|
|
|
7973
8445
|
promptsEmpty: "No saved prompts. Use /prompt save <name>.",
|
|
7974
8446
|
unknownCommand: (name) => `Unknown command /${name}. Try /help`,
|
|
7975
8447
|
didYouMean: (typed, guess) => `Unknown command /${typed}. Did you mean /${guess}?`,
|
|
7976
|
-
undoNothing: "Nothing to undo in this
|
|
8448
|
+
undoNothing: "Nothing to undo: no file changes made by kitcode in this folder.",
|
|
7977
8449
|
undoResult: (restored, removed) => `Undo complete: ${restored} restored, ${removed} newly created removed.`,
|
|
7978
8450
|
undoConflicts: (paths) => `Kept newer changes in: ${paths.join(", ")}`,
|
|
7979
8451
|
undoFailed: (failures) => `Could not restore: ${failures.join("; ")}`,
|
|
8452
|
+
titleContext: "Context window",
|
|
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).`,
|
|
8456
|
+
contextUnknown: "Context window: unknown. Set it with /context 256k, /context 512k or /context 1m.",
|
|
8457
|
+
contextInvalid: "Usage: /context 256k | 512k | 1m | <tokens> | auto",
|
|
8458
|
+
contextCurrentLabel: "current",
|
|
7980
8459
|
titleAccent: "Accent colour",
|
|
7981
8460
|
titleEffort: "Reasoning depth",
|
|
7982
8461
|
titleModel: "Model",
|
|
@@ -7986,7 +8465,7 @@ ${url}`,
|
|
|
7986
8465
|
titleMcpDelete: "Remove an MCP server",
|
|
7987
8466
|
titleMcpEnable: "Enable an MCP server",
|
|
7988
8467
|
titleMcpDisable: "Disable an MCP server",
|
|
7989
|
-
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" },
|
|
7990
8469
|
modeChanged: (label) => `Mode: ${label} (shift+tab to cycle)`,
|
|
7991
8470
|
cmd: {
|
|
7992
8471
|
model: "switch model",
|
|
@@ -7997,6 +8476,8 @@ ${url}`,
|
|
|
7997
8476
|
refresh: "reload the model list for a provider",
|
|
7998
8477
|
effort: "set reasoning depth",
|
|
7999
8478
|
thinking: "toggle reasoning",
|
|
8479
|
+
bell: "toggle the sound for finished answers and permission prompts",
|
|
8480
|
+
context: "set or show the model context window",
|
|
8000
8481
|
budget: "set token budget per turn",
|
|
8001
8482
|
theme: "change the accent colour",
|
|
8002
8483
|
lang: "change the interface language",
|
|
@@ -8029,7 +8510,7 @@ ${url}`,
|
|
|
8029
8510
|
clear: "start a fresh session",
|
|
8030
8511
|
help: "list commands",
|
|
8031
8512
|
exit: "quit",
|
|
8032
|
-
subagents: "
|
|
8513
|
+
subagents: "open a sub-agent and watch its chat live"
|
|
8033
8514
|
}
|
|
8034
8515
|
};
|
|
8035
8516
|
var ru = {
|
|
@@ -8043,10 +8524,12 @@ var ru = {
|
|
|
8043
8524
|
placeholder: "\u0441\u043F\u0440\u043E\u0441\u0438, \u0438\u043B\u0438 / \u0434\u043B\u044F \u043A\u043E\u043C\u0430\u043D\u0434",
|
|
8044
8525
|
working: "\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u2026",
|
|
8045
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",
|
|
8046
|
-
more: (
|
|
8527
|
+
more: (count2) => `\u2026 \u0435\u0449\u0451 ${count2}`,
|
|
8047
8528
|
allowOnce: "\u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043E\u0434\u0438\u043D \u0440\u0430\u0437",
|
|
8048
8529
|
always: "\u0432\u0441\u0435\u0433\u0434\u0430",
|
|
8049
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",
|
|
8050
8533
|
yes: "\u0434\u0430",
|
|
8051
8534
|
noDefault: "\u043D\u0435\u0442 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E)",
|
|
8052
8535
|
noMatches: "\u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E",
|
|
@@ -8061,7 +8544,7 @@ var ru = {
|
|
|
8061
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",
|
|
8062
8545
|
titleSessions: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
8063
8546
|
titleSessionAction: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439",
|
|
8064
|
-
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}`,
|
|
8065
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.",
|
|
8066
8549
|
providerSet: (id, model) => `\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440: ${id} \xB7 \u043C\u043E\u0434\u0435\u043B\u044C ${model}`,
|
|
8067
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.",
|
|
@@ -8069,11 +8552,15 @@ var ru = {
|
|
|
8069
8552
|
providerBalance: (balances) => `\u0431\u0430\u043B\u0430\u043D\u0441 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430: ${balances}`,
|
|
8070
8553
|
providerKeyRemaining: (balances) => `\u043E\u0441\u0442\u0430\u0442\u043E\u043A \u043B\u0438\u043C\u0438\u0442\u0430 API-\u043A\u043B\u044E\u0447\u0430: ${balances}`,
|
|
8071
8554
|
noThink: "\u0431\u0435\u0437 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
8072
|
-
mcpFailed: (
|
|
8555
|
+
mcpFailed: (count2) => `mcp: ${count2} \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439`,
|
|
8073
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",
|
|
8074
8558
|
escHelp: "esc \u2014 \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u0445\u043E\u0434",
|
|
8075
8559
|
cancelled: "\u041E\u0442\u043C\u0435\u043D\u0435\u043D\u043E.",
|
|
8076
|
-
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",
|
|
8077
8564
|
configAt: (path15) => `\u043A\u043E\u043D\u0444\u0438\u0433: ${path15}`,
|
|
8078
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",
|
|
8079
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>",
|
|
@@ -8092,7 +8579,7 @@ var ru = {
|
|
|
8092
8579
|
mcpDeleted: (name) => `MCP \xAB${name}\xBB \u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D \u0438 \u0443\u0434\u0430\u043B\u0451\u043D.`,
|
|
8093
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}`,
|
|
8094
8581
|
mcpNotFound: (name) => `MCP-\u0441\u0435\u0440\u0432\u0435\u0440 \xAB${name}\xBB \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.`,
|
|
8095
|
-
mcpTools: (
|
|
8582
|
+
mcpTools: (count2) => `\u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432: ${count2}`,
|
|
8096
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}`,
|
|
8097
8584
|
mcpDisabled: (name) => `MCP \xAB${name}\xBB \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D.`,
|
|
8098
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}`,
|
|
@@ -8102,14 +8589,24 @@ var ru = {
|
|
|
8102
8589
|
error: "\u043E\u0448\u0438\u0431\u043A\u0430",
|
|
8103
8590
|
disabled: "\u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D"
|
|
8104
8591
|
},
|
|
8105
|
-
|
|
8106
|
-
|
|
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",
|
|
8107
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",
|
|
8108
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}`,
|
|
8109
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.",
|
|
8110
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.",
|
|
8111
8603
|
compactStarting: "\u0421\u0436\u0430\u0442\u0438\u0435 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430\u2026",
|
|
8112
|
-
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 ") + ".",
|
|
8113
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>",
|
|
8114
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.`,
|
|
8115
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.",
|
|
@@ -8118,7 +8615,7 @@ var ru = {
|
|
|
8118
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.",
|
|
8119
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?",
|
|
8120
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.",
|
|
8121
|
-
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}.`,
|
|
8122
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}.`,
|
|
8123
8620
|
sessionExported: (path15) => `\u0421\u0435\u0441\u0441\u0438\u044F \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0430: ${path15}`,
|
|
8124
8621
|
sessionActionResume: "\u043E\u0442\u043A\u0440\u044B\u0442\u044C",
|
|
@@ -8131,14 +8628,18 @@ ${url}`,
|
|
|
8131
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}.`,
|
|
8132
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}.`,
|
|
8133
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",
|
|
8134
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.",
|
|
8135
8636
|
effortSet: (value) => `\u0413\u043B\u0443\u0431\u0438\u043D\u0430: ${value}`,
|
|
8136
|
-
budgetCurrent: (
|
|
8137
|
-
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`,
|
|
8138
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.",
|
|
8139
8640
|
budgetInvalid: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043B\u0438\u043C\u0438\u0442: \u043E\u0442 1 000 \u0434\u043E 10 000 000",
|
|
8140
8641
|
modelSet: (value) => `\u041C\u043E\u0434\u0435\u043B\u044C: ${value}`,
|
|
8141
|
-
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}.`,
|
|
8142
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.",
|
|
8143
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.",
|
|
8144
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?",
|
|
@@ -8165,10 +8666,17 @@ ${url}`,
|
|
|
8165
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>.",
|
|
8166
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`,
|
|
8167
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}?`,
|
|
8168
|
-
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.",
|
|
8169
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}.`,
|
|
8170
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(", ")}`,
|
|
8171
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("; ")}`,
|
|
8673
|
+
titleContext: "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430",
|
|
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).`,
|
|
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.",
|
|
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",
|
|
8679
|
+
contextCurrentLabel: "\u0442\u0435\u043A\u0443\u0449\u0435\u0435",
|
|
8172
8680
|
titleAccent: "\u0426\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
|
|
8173
8681
|
titleEffort: "\u0413\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
8174
8682
|
titleModel: "\u041C\u043E\u0434\u0435\u043B\u044C",
|
|
@@ -8178,7 +8686,7 @@ ${url}`,
|
|
|
8178
8686
|
titleMcpDelete: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8179
8687
|
titleMcpEnable: "\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8180
8688
|
titleMcpDisable: "\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C MCP-\u0441\u0435\u0440\u0432\u0435\u0440",
|
|
8181
|
-
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" },
|
|
8182
8690
|
modeChanged: (label) => `\u0420\u0435\u0436\u0438\u043C: ${label} (shift+tab \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C)`,
|
|
8183
8691
|
cmd: {
|
|
8184
8692
|
model: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C",
|
|
@@ -8189,6 +8697,8 @@ ${url}`,
|
|
|
8189
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",
|
|
8190
8698
|
effort: "\u0433\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
8191
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",
|
|
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",
|
|
8192
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)",
|
|
8193
8703
|
theme: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u0446\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
|
|
8194
8704
|
lang: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u044F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430",
|
|
@@ -8220,7 +8730,7 @@ ${url}`,
|
|
|
8220
8730
|
clear: "\u043D\u0430\u0447\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
8221
8731
|
help: "\u0441\u043F\u0438\u0441\u043E\u043A \u043A\u043E\u043C\u0430\u043D\u0434",
|
|
8222
8732
|
exit: "\u0432\u044B\u0445\u043E\u0434",
|
|
8223
|
-
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"
|
|
8224
8734
|
}
|
|
8225
8735
|
};
|
|
8226
8736
|
var DICTIONARIES = { en, ru };
|
|
@@ -8431,9 +8941,11 @@ function Onboarding({ onSubmit }) {
|
|
|
8431
8941
|
|
|
8432
8942
|
// src/ui/components/PermissionPrompt.tsx
|
|
8433
8943
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
8944
|
+
import { useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
8434
8945
|
|
|
8435
8946
|
// src/ui/components/Diff.tsx
|
|
8436
8947
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
8948
|
+
import stringWidth from "string-width";
|
|
8437
8949
|
|
|
8438
8950
|
// src/ui/diff.ts
|
|
8439
8951
|
var MAX_RENDER_DIFF_CHARS = 5e5;
|
|
@@ -8460,28 +8972,54 @@ function diffBody(before, after) {
|
|
|
8460
8972
|
let added = 0;
|
|
8461
8973
|
let removed = 0;
|
|
8462
8974
|
for (let i = Math.max(0, start - 2); i < start; i += 1) {
|
|
8463
|
-
hunk.push({ kind: "ctx", text: a[i] ?? "" });
|
|
8975
|
+
hunk.push({ kind: "ctx", text: a[i] ?? "", line: i + 1 });
|
|
8464
8976
|
}
|
|
8977
|
+
const dels = [];
|
|
8465
8978
|
for (let i = start; i < endA; i += 1) {
|
|
8466
|
-
|
|
8979
|
+
dels.push({ kind: "del", text: a[i] ?? "", line: i + 1 });
|
|
8467
8980
|
removed += 1;
|
|
8468
8981
|
}
|
|
8982
|
+
const adds = [];
|
|
8469
8983
|
for (let i = start; i < endB; i += 1) {
|
|
8470
|
-
|
|
8984
|
+
adds.push({ kind: "add", text: b[i] ?? "", line: i + 1 });
|
|
8471
8985
|
added += 1;
|
|
8472
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);
|
|
8473
8998
|
for (let i = endA; i < Math.min(a.length, endA + 2); i += 1) {
|
|
8474
|
-
hunk.push({ kind: "ctx", text: a[i] ?? "" });
|
|
8999
|
+
hunk.push({ kind: "ctx", text: a[i] ?? "", line: i + 1 });
|
|
8475
9000
|
}
|
|
8476
9001
|
return { added, removed, hunk };
|
|
8477
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
|
+
}
|
|
8478
9012
|
function truncate2(text, width) {
|
|
8479
9013
|
return text.length <= width ? text : `${text.slice(0, width - 1)}\u2026`;
|
|
8480
9014
|
}
|
|
8481
9015
|
|
|
8482
9016
|
// src/ui/components/Diff.tsx
|
|
8483
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
8484
|
-
|
|
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 }) {
|
|
8485
9023
|
if (!canRenderDiff(before, after)) {
|
|
8486
9024
|
return /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
8487
9025
|
"Diff preview omitted: it exceeds ",
|
|
@@ -8490,77 +9028,152 @@ function Diff({ before, after }) {
|
|
|
8490
9028
|
] });
|
|
8491
9029
|
}
|
|
8492
9030
|
const { hunk, hidden } = diffLines(sanitizeTerminalText(before), sanitizeTerminalText(after));
|
|
8493
|
-
return /* @__PURE__ */ jsx5(DiffHunk, { lines: hunk, hidden });
|
|
9031
|
+
return /* @__PURE__ */ jsx5(DiffHunk, { lines: hunk, hidden, indent });
|
|
8494
9032
|
}
|
|
8495
|
-
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);
|
|
8496
9037
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
8497
|
-
lines.map((line, index) =>
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
dimColor:
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
]
|
|
8506
|
-
}
|
|
8507
|
-
|
|
8508
|
-
|
|
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
|
+
}),
|
|
8509
9067
|
hidden > 0 && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
8510
|
-
"
|
|
9068
|
+
" ".repeat(gutter),
|
|
9069
|
+
" \u2026 ",
|
|
8511
9070
|
hidden,
|
|
8512
9071
|
" more lines"
|
|
8513
9072
|
] })
|
|
8514
9073
|
] });
|
|
8515
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
|
+
}
|
|
8516
9084
|
|
|
8517
9085
|
// src/ui/components/PermissionPrompt.tsx
|
|
8518
|
-
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";
|
|
8519
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
|
+
}
|
|
8520
9104
|
function PermissionPrompt({ request, onDecide }) {
|
|
8521
9105
|
const theme = useTheme();
|
|
8522
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]);
|
|
8523
9120
|
useTerminalInput((char, key) => {
|
|
8524
9121
|
const c = normalizeHotkey(char);
|
|
8525
|
-
if (
|
|
8526
|
-
else if (
|
|
8527
|
-
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
|
+
}
|
|
8528
9131
|
});
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
] }),
|
|
8535
|
-
request.display?.kind === "text" && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: previewText(request.display.text) }),
|
|
8536
|
-
request.display?.kind === "list" && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8537
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: sanitizeTerminalText(request.display.title) }),
|
|
8538
|
-
request.display.items.slice(0, 100).map((item, index) => /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8539
|
-
"\u2022 ",
|
|
8540
|
-
previewText(item, 1e3)
|
|
8541
|
-
] }, 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)
|
|
8542
9137
|
] }),
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
request.allowAlways !== false && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
8551
|
-
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "a" }),
|
|
8552
|
-
" ",
|
|
8553
|
-
strings.always,
|
|
8554
|
-
" (",
|
|
8555
|
-
sanitizeTerminalText(request.toolName),
|
|
8556
|
-
") \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 })
|
|
8557
9145
|
] }),
|
|
8558
|
-
/* @__PURE__ */ jsx6(Text6, {
|
|
8559
|
-
" ",
|
|
8560
|
-
|
|
8561
|
-
|
|
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
|
+
] })
|
|
8562
9171
|
] });
|
|
8563
9172
|
}
|
|
9173
|
+
function commandOf(input) {
|
|
9174
|
+
const command = input?.command;
|
|
9175
|
+
return typeof command === "string" && command.trim() !== "" ? command : void 0;
|
|
9176
|
+
}
|
|
8564
9177
|
function printInput(input) {
|
|
8565
9178
|
try {
|
|
8566
9179
|
return previewText(JSON.stringify(input, null, 2));
|
|
@@ -8576,15 +9189,15 @@ function previewText(value, limit = MAX_PREVIEW_CHARS) {
|
|
|
8576
9189
|
|
|
8577
9190
|
// src/ui/components/Picker.tsx
|
|
8578
9191
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
8579
|
-
import { useMemo as useMemo2, useState as
|
|
9192
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
8580
9193
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8581
9194
|
var WINDOW = 10;
|
|
8582
9195
|
function Picker({ title, items, onSelect, onCancel }) {
|
|
8583
9196
|
const theme = useTheme();
|
|
8584
9197
|
const { columns } = useTerminalSize();
|
|
8585
9198
|
const strings = useStrings();
|
|
8586
|
-
const [query, setQuery] =
|
|
8587
|
-
const [cursor, setCursor] =
|
|
9199
|
+
const [query, setQuery] = useState4("");
|
|
9200
|
+
const [cursor, setCursor] = useState4(0);
|
|
8588
9201
|
const matches = useMemo2(() => {
|
|
8589
9202
|
const needle = query.toLowerCase();
|
|
8590
9203
|
if (needle === "") return items;
|
|
@@ -8636,7 +9249,8 @@ function Picker({ title, items, onSelect, onCancel }) {
|
|
|
8636
9249
|
|
|
8637
9250
|
// src/ui/components/PromptInput.tsx
|
|
8638
9251
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
8639
|
-
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";
|
|
8640
9254
|
|
|
8641
9255
|
// src/ui/history.ts
|
|
8642
9256
|
function moveInputHistory(history, index, value, draft, direction) {
|
|
@@ -8677,6 +9291,10 @@ var CAT_FRAMES = [
|
|
|
8677
9291
|
"/\u1420\uFF61\uA788\uFF61\u141F\\ \u223E"
|
|
8678
9292
|
];
|
|
8679
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" };
|
|
8680
9298
|
var PromptInput = memo(function PromptInput2({
|
|
8681
9299
|
value,
|
|
8682
9300
|
onChange,
|
|
@@ -8688,37 +9306,48 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8688
9306
|
hint,
|
|
8689
9307
|
history,
|
|
8690
9308
|
attachments = [],
|
|
8691
|
-
onListFiles
|
|
9309
|
+
onListFiles,
|
|
9310
|
+
mode = "normal",
|
|
9311
|
+
bypass = false,
|
|
9312
|
+
queued = [],
|
|
9313
|
+
onNavigateQueue
|
|
8692
9314
|
}) {
|
|
8693
9315
|
const theme = useTheme();
|
|
8694
|
-
const { columns
|
|
9316
|
+
const { columns } = useTerminalSize();
|
|
8695
9317
|
const panelWidth = Math.max(2, columns - 1);
|
|
8696
|
-
const inputRows =
|
|
9318
|
+
const inputRows = MAX_INPUT_ROWS;
|
|
8697
9319
|
const strings = useStrings();
|
|
8698
|
-
const [catFrame, setCatFrame] =
|
|
8699
|
-
|
|
9320
|
+
const [catFrame, setCatFrame] = useState5(0);
|
|
9321
|
+
useEffect2(() => {
|
|
8700
9322
|
if (!disabled) return;
|
|
8701
9323
|
const timer = setInterval(() => setCatFrame((frame) => (frame + 1) % CAT_FRAMES.length), CAT_FRAME_MS);
|
|
8702
9324
|
return () => clearInterval(timer);
|
|
8703
9325
|
}, [disabled]);
|
|
8704
|
-
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 : "";
|
|
8705
9329
|
const fullTitle = disabled ? `KitCode ${CAT_FRAMES[catFrame]}` : "KitCode";
|
|
8706
9330
|
const panelTitle = fullTitle.slice(0, Math.max(0, panelWidth - 5));
|
|
8707
9331
|
const safeValue = sanitizeTerminalText(value);
|
|
8708
|
-
const [selectionCursor, setSelectionCursor] =
|
|
8709
|
-
const [inputCursor, setInputCursor] =
|
|
8710
|
-
const [historyIndex, setHistoryIndex] =
|
|
8711
|
-
const historyDraft =
|
|
8712
|
-
const pendingValue =
|
|
8713
|
-
const valueRef =
|
|
8714
|
-
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);
|
|
8715
9339
|
valueRef.current = safeValue;
|
|
8716
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} ` : "";
|
|
8717
9346
|
const commandSuggestions = matchCommands(safeValue);
|
|
8718
9347
|
const mention = onListFiles ? mentionSpan(safeValue, inputCursor) : null;
|
|
8719
|
-
const [files, setFiles] =
|
|
9348
|
+
const [files, setFiles] = useState5([]);
|
|
8720
9349
|
const mentionQuery = mention?.query ?? null;
|
|
8721
|
-
|
|
9350
|
+
useEffect2(() => {
|
|
8722
9351
|
if (mentionQuery === null || !onListFiles) {
|
|
8723
9352
|
setFiles([]);
|
|
8724
9353
|
return;
|
|
@@ -8736,10 +9365,10 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8736
9365
|
const open3 = suggestions.length > 0 || showFiles;
|
|
8737
9366
|
const optionCount = showFiles ? files.length : suggestions.length;
|
|
8738
9367
|
const active2 = Math.min(selectionCursor, Math.max(0, optionCount - 1));
|
|
8739
|
-
|
|
9368
|
+
useEffect2(() => {
|
|
8740
9369
|
setSelectionCursor(0);
|
|
8741
9370
|
}, [safeValue]);
|
|
8742
|
-
|
|
9371
|
+
useEffect2(() => {
|
|
8743
9372
|
const length = characters2(safeValue).length;
|
|
8744
9373
|
if (pendingValue.current === safeValue) {
|
|
8745
9374
|
pendingValue.current = null;
|
|
@@ -8759,28 +9388,39 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8759
9388
|
historyDraft.current = next;
|
|
8760
9389
|
onChange(next);
|
|
8761
9390
|
};
|
|
9391
|
+
const pastes = useRef3(/* @__PURE__ */ new Map());
|
|
9392
|
+
const pasteCounter = useRef3(0);
|
|
8762
9393
|
const submit = (next) => {
|
|
8763
9394
|
setHistoryIndex(null);
|
|
8764
9395
|
historyDraft.current = "";
|
|
8765
|
-
|
|
9396
|
+
const expanded = expandPastes(next, pastes.current);
|
|
9397
|
+
pastes.current = /* @__PURE__ */ new Map();
|
|
9398
|
+
onSubmit(expanded);
|
|
8766
9399
|
};
|
|
8767
9400
|
const insertPastedText = (pasted) => {
|
|
8768
9401
|
const inserted = characters2(sanitizeTerminalText(pasted));
|
|
8769
9402
|
if (inserted.length === 0) return;
|
|
8770
9403
|
const valueCharacters = characters2(valueRef.current);
|
|
8771
9404
|
const cursor = clamp2(cursorRef.current, 0, valueCharacters.length);
|
|
8772
|
-
valueCharacters.
|
|
8773
|
-
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);
|
|
8774
9414
|
};
|
|
8775
9415
|
useTerminalPaste((pasted) => {
|
|
8776
9416
|
const safePaste = sanitizeTerminalText(pasted);
|
|
8777
9417
|
if (onPastePath && looksLikeAttachmentPath(safePaste)) {
|
|
8778
9418
|
void onPastePath(safePaste).then((consumed) => {
|
|
8779
|
-
if (!consumed) insertPastedText(safePaste);
|
|
9419
|
+
if (!consumed) insertPastedText(collapsePaste(safePaste));
|
|
8780
9420
|
});
|
|
8781
9421
|
return;
|
|
8782
9422
|
}
|
|
8783
|
-
insertPastedText(safePaste);
|
|
9423
|
+
insertPastedText(collapsePaste(safePaste));
|
|
8784
9424
|
});
|
|
8785
9425
|
useTerminalInput((input, key) => {
|
|
8786
9426
|
if (onPasteImage && (key.ctrl || key.meta || key.super) && (input.toLowerCase() === "v" || input === "")) {
|
|
@@ -8813,6 +9453,13 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8813
9453
|
if (chosen) submit(`/${chosen.name}`);
|
|
8814
9454
|
return;
|
|
8815
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
|
+
}
|
|
8816
9463
|
if (!open3 && (key.upArrow || key.downArrow)) {
|
|
8817
9464
|
const moved = moveInputHistory(
|
|
8818
9465
|
history,
|
|
@@ -8867,6 +9514,21 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8867
9514
|
const visible = suggestions.slice(start, start + WINDOW2);
|
|
8868
9515
|
const fileStart = Math.max(0, Math.min(active2 - WINDOW2 + 2, files.length - WINDOW2));
|
|
8869
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
|
+
] }),
|
|
8870
9532
|
/* @__PURE__ */ jsxs8(Box8, { width: "100%", flexShrink: 0, children: [
|
|
8871
9533
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256D" }) }),
|
|
8872
9534
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsxs8(Text8, { color: panelColor, wrap: "truncate-end", children: [
|
|
@@ -8892,12 +9554,8 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8892
9554
|
/* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256E" }) })
|
|
8893
9555
|
] }),
|
|
8894
9556
|
/* @__PURE__ */ jsxs8(Box8, { width: "100%", paddingX: 1, maxHeight: inputRows, overflowY: "hidden", children: [
|
|
8895
|
-
/* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u203A " }),
|
|
8896
|
-
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(EditableText, {
|
|
8897
|
-
pending && pending > 0 ? /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
8898
|
-
" \xB7 ",
|
|
8899
|
-
strings.queued(pending)
|
|
8900
|
-
] }) : 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 }) })
|
|
8901
9559
|
] }),
|
|
8902
9560
|
attachments.length > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
8903
9561
|
" ",
|
|
@@ -8921,7 +9579,8 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8921
9579
|
borderRight: false
|
|
8922
9580
|
}
|
|
8923
9581
|
),
|
|
8924
|
-
/* @__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" }) })
|
|
8925
9584
|
] }),
|
|
8926
9585
|
disabled && hint && /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, children: [
|
|
8927
9586
|
" ",
|
|
@@ -8958,25 +9617,81 @@ var PromptInput = memo(function PromptInput2({
|
|
|
8958
9617
|
] })
|
|
8959
9618
|
] });
|
|
8960
9619
|
});
|
|
8961
|
-
function
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
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) {
|
|
8969
9675
|
const placeholderCharacters = characters2(sanitizeTerminalText(placeholder));
|
|
8970
9676
|
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
8971
9677
|
/* @__PURE__ */ jsx8(Text8, { inverse: true, children: placeholderCharacters[0] ?? " " }),
|
|
8972
9678
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: placeholderCharacters.slice(1).join("") })
|
|
8973
9679
|
] });
|
|
8974
9680
|
}
|
|
8975
|
-
return /* @__PURE__ */
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
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);
|
|
8980
9695
|
}
|
|
8981
9696
|
function nextCursor(cursor, length, key) {
|
|
8982
9697
|
if (key.home) return 0;
|
|
@@ -8994,7 +9709,6 @@ function clamp2(value, min, max) {
|
|
|
8994
9709
|
|
|
8995
9710
|
// src/ui/components/StatusBar.tsx
|
|
8996
9711
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
8997
|
-
import { Fragment as Fragment2 } from "react";
|
|
8998
9712
|
|
|
8999
9713
|
// src/ui/time.ts
|
|
9000
9714
|
function formatDuration(ms) {
|
|
@@ -9010,32 +9724,31 @@ function formatDuration(ms) {
|
|
|
9010
9724
|
// src/ui/components/StatusBar.tsx
|
|
9011
9725
|
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
9012
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
|
+
}
|
|
9013
9733
|
function StatusBar({ status }) {
|
|
9014
9734
|
const theme = useTheme();
|
|
9015
9735
|
const strings = useStrings();
|
|
9016
|
-
const primary = [];
|
|
9017
9736
|
const details = [];
|
|
9018
|
-
|
|
9019
|
-
|
|
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"));
|
|
9020
9739
|
if (!status.thinking) {
|
|
9021
9740
|
details.push(
|
|
9022
9741
|
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: strings.noThink }, "think")
|
|
9023
9742
|
);
|
|
9024
9743
|
}
|
|
9025
|
-
if (status.
|
|
9026
|
-
const modeColor = status.mode === "accept" ? "yellow" : status.mode === "plan" ? "blue" : theme.accent;
|
|
9027
|
-
details.push(
|
|
9028
|
-
/* @__PURE__ */ jsx9(Text9, { color: modeColor, children: strings.modeLabel[status.mode] ?? status.mode }, "mode")
|
|
9029
|
-
);
|
|
9030
|
-
}
|
|
9031
|
-
if (status.usage) {
|
|
9744
|
+
if (status.usage && status.usage.totalTokens > 0) {
|
|
9032
9745
|
const u = status.usage;
|
|
9033
9746
|
details.push(
|
|
9034
9747
|
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9035
9748
|
/* @__PURE__ */ jsx9(Text9, { color: "white", bold: true, children: u.input }),
|
|
9036
|
-
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "
|
|
9749
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2191 " }),
|
|
9037
9750
|
/* @__PURE__ */ jsx9(Text9, { color: theme.ok, children: u.output }),
|
|
9038
|
-
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "
|
|
9751
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2193" }),
|
|
9039
9752
|
u.cache && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
9040
9753
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " (" }),
|
|
9041
9754
|
/* @__PURE__ */ jsxs9(Text9, { color: "gray", children: [
|
|
@@ -9051,6 +9764,15 @@ function StatusBar({ status }) {
|
|
|
9051
9764
|
] }, "usage")
|
|
9052
9765
|
);
|
|
9053
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
|
+
}
|
|
9054
9776
|
details.push(
|
|
9055
9777
|
/* @__PURE__ */ jsx9(
|
|
9056
9778
|
Text9,
|
|
@@ -9062,14 +9784,14 @@ function StatusBar({ status }) {
|
|
|
9062
9784
|
"elapsed"
|
|
9063
9785
|
)
|
|
9064
9786
|
);
|
|
9065
|
-
if (status.
|
|
9066
|
-
details.push(
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
);
|
|
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"));
|
|
9073
9795
|
}
|
|
9074
9796
|
return /* @__PURE__ */ jsxs9(
|
|
9075
9797
|
Box9,
|
|
@@ -9089,59 +9811,28 @@ function StatusBar({ status }) {
|
|
|
9089
9811
|
flexDirection: "column",
|
|
9090
9812
|
children: [
|
|
9091
9813
|
/* @__PURE__ */ jsxs9(Box9, { width: "100%", alignItems: "flex-start", children: [
|
|
9092
|
-
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, flexShrink: 1, flexWrap: "wrap", children: /* @__PURE__ */
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
marginLeft: 2,
|
|
9098
|
-
flexDirection: "column",
|
|
9099
|
-
alignItems: "flex-end",
|
|
9100
|
-
children: [
|
|
9101
|
-
/* @__PURE__ */ jsx9(ContextMeter, { context: status.context }),
|
|
9102
|
-
(status.mcp.connected > 0 || status.mcp.failed > 0) && /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9103
|
-
status.mcp.connected > 0 && /* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
9104
|
-
"mcp:",
|
|
9105
|
-
status.mcp.connected
|
|
9106
|
-
] }),
|
|
9107
|
-
status.mcp.connected > 0 && status.mcp.failed > 0 && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }),
|
|
9108
|
-
status.mcp.failed > 0 && /* @__PURE__ */ jsx9(Text9, { color: theme.error, children: strings.mcpFailed(status.mcp.failed) })
|
|
9109
|
-
] }),
|
|
9110
|
-
status.activeAgents > 0 && /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: strings.activeAgents(status.activeAgents) })
|
|
9111
|
-
]
|
|
9112
|
-
}
|
|
9113
|
-
)
|
|
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 }) })
|
|
9114
9819
|
] }),
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
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
|
+
] })
|
|
9119
9827
|
]
|
|
9120
9828
|
}
|
|
9121
9829
|
);
|
|
9122
9830
|
}
|
|
9123
|
-
function Segments({ items }) {
|
|
9124
|
-
return items.flatMap((item, index) => {
|
|
9125
|
-
const elements = [];
|
|
9126
|
-
if (index > 0) {
|
|
9127
|
-
elements.push(
|
|
9128
|
-
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }) }, `sep-${index}`)
|
|
9129
|
-
);
|
|
9130
|
-
}
|
|
9131
|
-
elements.push(/* @__PURE__ */ jsx9(Fragment2, { children: item }, `item-${index}`));
|
|
9132
|
-
return elements;
|
|
9133
|
-
});
|
|
9134
|
-
}
|
|
9135
9831
|
function shortModel(ref) {
|
|
9136
9832
|
const safe = sanitizeTerminalText(ref);
|
|
9137
9833
|
const slash = safe.lastIndexOf("/");
|
|
9138
9834
|
return slash === -1 ? safe : safe.slice(slash + 1);
|
|
9139
9835
|
}
|
|
9140
|
-
function ModelBadge({ ref: modelRef }) {
|
|
9141
|
-
const theme = useTheme();
|
|
9142
|
-
const name = shortModel(modelRef);
|
|
9143
|
-
return /* @__PURE__ */ jsx9(Box9, { borderColor: theme.accent, borderStyle: "round", paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.accent, bold: true, children: name }) });
|
|
9144
|
-
}
|
|
9145
9836
|
var EFFORT_STYLES = {
|
|
9146
9837
|
auto: { label: "auto", color: "cyan" },
|
|
9147
9838
|
max: { label: "max", color: "red", bold: true },
|
|
@@ -9150,36 +9841,30 @@ var EFFORT_STYLES = {
|
|
|
9150
9841
|
medium: { label: "medium", color: "blue" },
|
|
9151
9842
|
low: { label: "low", color: "gray", dim: true }
|
|
9152
9843
|
};
|
|
9153
|
-
function EffortLabel({ effort }) {
|
|
9844
|
+
function EffortLabel({ effort, effective }) {
|
|
9154
9845
|
const style = EFFORT_STYLES[effort];
|
|
9155
|
-
|
|
9156
|
-
Text9,
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
}
|
|
9165
|
-
|
|
9166
|
-
if (count < 1e3) return String(count);
|
|
9167
|
-
const [value, suffix] = count < 1e6 ? [count / 1e3, "k"] : [count / 1e6, "M"];
|
|
9168
|
-
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
|
+
] });
|
|
9169
9857
|
}
|
|
9170
9858
|
function ContextMeter({
|
|
9171
9859
|
context
|
|
9172
9860
|
}) {
|
|
9173
9861
|
const theme = useTheme();
|
|
9174
|
-
const { window: win,
|
|
9175
|
-
|
|
9862
|
+
const { window: win, exact } = context;
|
|
9863
|
+
const used = exact ? context.used : 0;
|
|
9864
|
+
if (win === null || win <= 0) {
|
|
9176
9865
|
return /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9177
9866
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "ctx " }),
|
|
9178
|
-
/* @__PURE__ */ jsx9(Text9, { color: "gray", children:
|
|
9179
|
-
win && win > 0 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
9180
|
-
"/",
|
|
9181
|
-
tokens2(win)
|
|
9182
|
-
] })
|
|
9867
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: tokens(used) })
|
|
9183
9868
|
] });
|
|
9184
9869
|
}
|
|
9185
9870
|
const pct = Math.min(Math.max(used / win, 0), 1);
|
|
@@ -9197,107 +9882,306 @@ function ContextMeter({
|
|
|
9197
9882
|
/* @__PURE__ */ jsx9(Text9, { color, children: "\u258C" }),
|
|
9198
9883
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
9199
9884
|
" ",
|
|
9200
|
-
|
|
9885
|
+
tokens(used),
|
|
9201
9886
|
"/",
|
|
9202
|
-
|
|
9887
|
+
tokens(win)
|
|
9203
9888
|
] })
|
|
9204
9889
|
] });
|
|
9205
9890
|
}
|
|
9206
9891
|
|
|
9207
|
-
// src/ui/components/
|
|
9208
|
-
import { Box as
|
|
9209
|
-
import {
|
|
9210
|
-
function interactiveViewportRows(rows) {
|
|
9211
|
-
if (!Number.isFinite(rows)) return 22;
|
|
9212
|
-
return Math.max(1, Math.floor(rows) - 2);
|
|
9213
|
-
}
|
|
9214
|
-
var INTERACTIVE_CHROME_ROWS = 14;
|
|
9215
|
-
function liveTranscriptRows(rows) {
|
|
9216
|
-
return Math.max(1, interactiveViewportRows(rows) - INTERACTIVE_CHROME_ROWS);
|
|
9217
|
-
}
|
|
9218
|
-
function TerminalViewport({ children, rows }) {
|
|
9219
|
-
const { columns } = useTerminalSize();
|
|
9220
|
-
return /* @__PURE__ */ jsx10(
|
|
9221
|
-
Box10,
|
|
9222
|
-
{
|
|
9223
|
-
width: columns,
|
|
9224
|
-
maxWidth: "100%",
|
|
9225
|
-
flexDirection: "column",
|
|
9226
|
-
maxHeight: interactiveViewportRows(rows),
|
|
9227
|
-
overflowY: "hidden",
|
|
9228
|
-
children
|
|
9229
|
-
}
|
|
9230
|
-
);
|
|
9231
|
-
}
|
|
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";
|
|
9232
9895
|
|
|
9233
9896
|
// src/ui/components/Transcript.tsx
|
|
9234
|
-
import { Box as
|
|
9235
|
-
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";
|
|
9236
9899
|
import Spinner2 from "ink-spinner";
|
|
9237
|
-
import
|
|
9900
|
+
import stringWidth4 from "string-width";
|
|
9238
9901
|
|
|
9239
9902
|
// src/ui/markdown.tsx
|
|
9240
|
-
import { Box as
|
|
9241
|
-
import { createContext as createContext3, Fragment as Fragment4, useContext as useContext3, useMemo as
|
|
9242
|
-
import
|
|
9243
|
-
|
|
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";
|
|
9244
10101
|
var DimContext = createContext3(false);
|
|
9245
|
-
function Text10({ dimColor, color, ...props }) {
|
|
10102
|
+
function Text10({ dimColor, color, bold, ...props }) {
|
|
9246
10103
|
const dim = useContext3(DimContext);
|
|
9247
|
-
return /* @__PURE__ */
|
|
10104
|
+
return /* @__PURE__ */ jsx10(
|
|
10105
|
+
InkText,
|
|
10106
|
+
{
|
|
10107
|
+
...props,
|
|
10108
|
+
color: dim ? void 0 : color,
|
|
10109
|
+
bold: dim ? void 0 : bold,
|
|
10110
|
+
dimColor: dimColor ?? dim
|
|
10111
|
+
}
|
|
10112
|
+
);
|
|
9248
10113
|
}
|
|
9249
10114
|
function Markdown({ children, dim = false }) {
|
|
9250
|
-
const blocks =
|
|
9251
|
-
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)) }) });
|
|
9252
10117
|
}
|
|
9253
10118
|
function BlockView({ block }) {
|
|
9254
10119
|
switch (block.type) {
|
|
9255
10120
|
case "heading": {
|
|
9256
|
-
return /* @__PURE__ */
|
|
10121
|
+
return /* @__PURE__ */ jsx10(Box10, { marginTop: block.level === 1 ? 1 : 0, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(block.text ?? "") }) });
|
|
9257
10122
|
}
|
|
9258
10123
|
case "blockquote":
|
|
9259
|
-
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)) });
|
|
9260
10125
|
case "ul":
|
|
9261
|
-
return /* @__PURE__ */
|
|
9262
|
-
/* @__PURE__ */
|
|
9263
|
-
/* @__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 }) })
|
|
9264
10129
|
] }, i)) });
|
|
9265
10130
|
case "ol":
|
|
9266
|
-
return /* @__PURE__ */
|
|
9267
|
-
/* @__PURE__ */
|
|
9268
|
-
/* @__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 }) })
|
|
9269
10134
|
] }, i)) });
|
|
9270
10135
|
case "paragraph":
|
|
9271
10136
|
default: {
|
|
9272
|
-
const content = /* @__PURE__ */
|
|
10137
|
+
const content = /* @__PURE__ */ jsx10(Text10, { children: inline(block.text ?? "") });
|
|
9273
10138
|
if (!block.children?.length) return content;
|
|
9274
|
-
return /* @__PURE__ */ jsxs10(
|
|
10139
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
9275
10140
|
content,
|
|
9276
|
-
/* @__PURE__ */
|
|
10141
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: block.children.map((child, index) => /* @__PURE__ */ jsx10(BlockView, { block: child }, index)) })
|
|
9277
10142
|
] });
|
|
9278
10143
|
}
|
|
9279
10144
|
case "table":
|
|
9280
|
-
return /* @__PURE__ */
|
|
10145
|
+
return /* @__PURE__ */ jsx10(TableView, { block });
|
|
9281
10146
|
case "code":
|
|
9282
|
-
return /* @__PURE__ */
|
|
10147
|
+
return /* @__PURE__ */ jsx10(CodeBlock, { code: block.text ?? "", lang: block.lang });
|
|
9283
10148
|
case "hr":
|
|
9284
|
-
return /* @__PURE__ */
|
|
10149
|
+
return /* @__PURE__ */ jsx10(Box10, { height: 1 });
|
|
9285
10150
|
}
|
|
9286
10151
|
}
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
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)) });
|
|
9290
10174
|
}
|
|
9291
10175
|
function TableView({ block }) {
|
|
9292
10176
|
const { columns } = useTerminalSize();
|
|
9293
10177
|
const headers = block.headers ?? [];
|
|
9294
10178
|
const rows = block.rows ?? [];
|
|
9295
10179
|
if (headers.length === 0 && rows.length === 0) {
|
|
9296
|
-
return /* @__PURE__ */
|
|
10180
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
9297
10181
|
}
|
|
9298
10182
|
const colCount = Math.max(headers.length, ...rows.map((r) => r?.length ?? 0));
|
|
9299
10183
|
if (colCount === 0) {
|
|
9300
|
-
return /* @__PURE__ */
|
|
10184
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
9301
10185
|
}
|
|
9302
10186
|
const allRows = [headers, ...rows].map(
|
|
9303
10187
|
(row) => Array.from({ length: colCount }, (_, index) => inlineDisplayText(row[index] ?? ""))
|
|
@@ -9306,7 +10190,7 @@ function TableView({ block }) {
|
|
|
9306
10190
|
for (const row of allRows) {
|
|
9307
10191
|
for (let i = 0; i < colCount; i++) {
|
|
9308
10192
|
const cell = row[i] ?? "";
|
|
9309
|
-
naturalWidths[i] = Math.max(naturalWidths[i] ?? 1,
|
|
10193
|
+
naturalWidths[i] = Math.max(naturalWidths[i] ?? 1, stringWidth3(cell));
|
|
9310
10194
|
}
|
|
9311
10195
|
}
|
|
9312
10196
|
const colWidths = fitColumnWidths(naturalWidths, Math.max(1, columns));
|
|
@@ -9322,7 +10206,7 @@ function TableView({ block }) {
|
|
|
9322
10206
|
const align = block.colAligns?.[i] ?? "left";
|
|
9323
10207
|
const width = colWidths[i] ?? 1;
|
|
9324
10208
|
const fitted = truncateToWidth(cell, width);
|
|
9325
|
-
const visualWidth =
|
|
10209
|
+
const visualWidth = stringWidth3(fitted);
|
|
9326
10210
|
const totalPad = Math.max(0, width - visualWidth);
|
|
9327
10211
|
let padded;
|
|
9328
10212
|
if (align === "right") {
|
|
@@ -9336,13 +10220,13 @@ function TableView({ block }) {
|
|
|
9336
10220
|
cells.push(padded);
|
|
9337
10221
|
}
|
|
9338
10222
|
lines.push(
|
|
9339
|
-
/* @__PURE__ */
|
|
10223
|
+
/* @__PURE__ */ jsx10(Text10, { bold: rowIdx === 0, wrap: "truncate-end", children: cells.join(" \u2502 ") }, `row-${rowIdx}`)
|
|
9340
10224
|
);
|
|
9341
10225
|
if (rowIdx === 0) {
|
|
9342
|
-
lines.push(/* @__PURE__ */
|
|
10226
|
+
lines.push(/* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "truncate-end", children: separator }, `sep-${rowIdx}`));
|
|
9343
10227
|
}
|
|
9344
10228
|
});
|
|
9345
|
-
return /* @__PURE__ */
|
|
10229
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: lines });
|
|
9346
10230
|
}
|
|
9347
10231
|
function fitColumnWidths(natural, maxLineWidth) {
|
|
9348
10232
|
const widths = natural.map((width) => Math.max(1, width));
|
|
@@ -9365,11 +10249,11 @@ function fitColumnWidths(natural, maxLineWidth) {
|
|
|
9365
10249
|
}
|
|
9366
10250
|
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
9367
10251
|
function truncateToWidth(text, maxWidth) {
|
|
9368
|
-
if (
|
|
10252
|
+
if (stringWidth3(text) <= maxWidth) return text;
|
|
9369
10253
|
if (maxWidth <= 1) return "\u2026";
|
|
9370
10254
|
let result = "";
|
|
9371
10255
|
for (const { segment } of graphemeSegmenter.segment(text)) {
|
|
9372
|
-
if (
|
|
10256
|
+
if (stringWidth3(result + segment) > maxWidth - 1) break;
|
|
9373
10257
|
result += segment;
|
|
9374
10258
|
}
|
|
9375
10259
|
return `${result}\u2026`;
|
|
@@ -9730,7 +10614,7 @@ function inline(text, depth = 0) {
|
|
|
9730
10614
|
handler: (m) => {
|
|
9731
10615
|
const fragmentKey = key++;
|
|
9732
10616
|
return /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
9733
|
-
/* @__PURE__ */
|
|
10617
|
+
/* @__PURE__ */ jsx10(Text10, { color: "cyan", underline: true, children: inline(m[2], depth + 1) }),
|
|
9734
10618
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
|
|
9735
10619
|
"(",
|
|
9736
10620
|
restoreInlineEscapes(m[3]),
|
|
@@ -9741,31 +10625,31 @@ function inline(text, depth = 0) {
|
|
|
9741
10625
|
},
|
|
9742
10626
|
{
|
|
9743
10627
|
re: /(`+)([^`]+?)\1/,
|
|
9744
|
-
handler: (m) => /* @__PURE__ */
|
|
10628
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: restoreInlineEscapes(m[2]) }, key++)
|
|
9745
10629
|
},
|
|
9746
10630
|
{
|
|
9747
10631
|
re: /(\*\*\*|___)(?=\S)(.*?\S)\1/,
|
|
9748
|
-
handler: (m) => /* @__PURE__ */
|
|
10632
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9749
10633
|
},
|
|
9750
10634
|
{
|
|
9751
10635
|
re: /(\*\*)(?=\S)(.*?\S)\1/,
|
|
9752
|
-
handler: (m) => /* @__PURE__ */
|
|
10636
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
9753
10637
|
},
|
|
9754
10638
|
{
|
|
9755
10639
|
re: /(__)(?=\S)(.*?\S)\1/,
|
|
9756
|
-
handler: (m) => /* @__PURE__ */
|
|
10640
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
9757
10641
|
},
|
|
9758
10642
|
{
|
|
9759
10643
|
re: /(~~)(?=\S)(.*?\S)\1/,
|
|
9760
|
-
handler: (m) => /* @__PURE__ */
|
|
10644
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { strikethrough: true, children: inline(m[2], depth + 1) }, key++)
|
|
9761
10645
|
},
|
|
9762
10646
|
{
|
|
9763
10647
|
re: /(\*)(?=\S)(.*?\S)\1/,
|
|
9764
|
-
handler: (m) => /* @__PURE__ */
|
|
10648
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9765
10649
|
},
|
|
9766
10650
|
{
|
|
9767
10651
|
re: /(?<!\w)(_)(?=\S)(.*?\S)\1(?!\w)/,
|
|
9768
|
-
handler: (m) => /* @__PURE__ */
|
|
10652
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
9769
10653
|
}
|
|
9770
10654
|
];
|
|
9771
10655
|
while (rest.length > 0) {
|
|
@@ -9789,7 +10673,7 @@ function inline(text, depth = 0) {
|
|
|
9789
10673
|
}
|
|
9790
10674
|
|
|
9791
10675
|
// src/ui/components/Transcript.tsx
|
|
9792
|
-
import { Fragment as Fragment5, jsx as
|
|
10676
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
9793
10677
|
var HEADER = { kind: "header" };
|
|
9794
10678
|
function assistantThinkingForFrame(bubble, frozenThinking) {
|
|
9795
10679
|
return bubble.streaming && bubble.text !== "" ? frozenThinking ?? bubble.thinking : bubble.thinking;
|
|
@@ -9806,7 +10690,7 @@ var Transcript = memo2(function Transcript2({
|
|
|
9806
10690
|
}) {
|
|
9807
10691
|
const liveAt = firstMutableBubbleIndex(bubbles);
|
|
9808
10692
|
const stableCount = liveAt === -1 ? bubbles.length : liveAt;
|
|
9809
|
-
const stableRef =
|
|
10693
|
+
const stableRef = useRef4([]);
|
|
9810
10694
|
if (stableRef.current.length < stableCount) {
|
|
9811
10695
|
stableRef.current = [
|
|
9812
10696
|
...stableRef.current,
|
|
@@ -9820,7 +10704,7 @@ var Transcript = memo2(function Transcript2({
|
|
|
9820
10704
|
const agents = live.filter((bubble) => bubble.kind === "subagent");
|
|
9821
10705
|
const groupedAgents = agents.length > 1;
|
|
9822
10706
|
return /* @__PURE__ */ jsxs11(
|
|
9823
|
-
|
|
10707
|
+
Box11,
|
|
9824
10708
|
{
|
|
9825
10709
|
flexDirection: "column",
|
|
9826
10710
|
marginBottom: 1,
|
|
@@ -9828,9 +10712,9 @@ var Transcript = memo2(function Transcript2({
|
|
|
9828
10712
|
minHeight: 0,
|
|
9829
10713
|
overflowY: "hidden",
|
|
9830
10714
|
children: [
|
|
9831
|
-
/* @__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) }),
|
|
9832
10716
|
/* @__PURE__ */ jsxs11(
|
|
9833
|
-
|
|
10717
|
+
Box11,
|
|
9834
10718
|
{
|
|
9835
10719
|
flexDirection: "column",
|
|
9836
10720
|
flexShrink: 1,
|
|
@@ -9839,8 +10723,8 @@ var Transcript = memo2(function Transcript2({
|
|
|
9839
10723
|
overflowY: "hidden",
|
|
9840
10724
|
justifyContent: "flex-end",
|
|
9841
10725
|
children: [
|
|
9842
|
-
live.filter((bubble) => !groupedAgents || bubble.kind !== "subagent" && !(bubble.kind === "tool" && bubble.name === "task")).map((bubble) => /* @__PURE__ */
|
|
9843
|
-
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 })
|
|
9844
10728
|
]
|
|
9845
10729
|
}
|
|
9846
10730
|
)
|
|
@@ -9854,22 +10738,22 @@ var BubbleView = memo2(function BubbleView2({
|
|
|
9854
10738
|
}) {
|
|
9855
10739
|
const theme = useTheme();
|
|
9856
10740
|
if (bubble.kind === "user") {
|
|
9857
|
-
return /* @__PURE__ */
|
|
10741
|
+
return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.accent, bold: true, children: [
|
|
9858
10742
|
"\u203A ",
|
|
9859
10743
|
bubble.text
|
|
9860
10744
|
] }) });
|
|
9861
10745
|
}
|
|
9862
10746
|
if (bubble.kind === "notice") {
|
|
9863
10747
|
const color = bubble.level === "error" ? theme.error : bubble.level === "warn" ? theme.warn : theme.accent;
|
|
9864
|
-
return /* @__PURE__ */
|
|
10748
|
+
return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color, children: bubble.text }) });
|
|
9865
10749
|
}
|
|
9866
10750
|
if (bubble.kind === "assistant") {
|
|
9867
|
-
return /* @__PURE__ */
|
|
10751
|
+
return /* @__PURE__ */ jsx11(AssistantView, { bubble, maxRows });
|
|
9868
10752
|
}
|
|
9869
10753
|
if (bubble.kind === "subagent") {
|
|
9870
|
-
return /* @__PURE__ */
|
|
10754
|
+
return /* @__PURE__ */ jsx11(SubagentView, { bubble, maxRows });
|
|
9871
10755
|
}
|
|
9872
|
-
return /* @__PURE__ */
|
|
10756
|
+
return /* @__PURE__ */ jsx11(ToolView, { bubble });
|
|
9873
10757
|
});
|
|
9874
10758
|
function splitThinkingPrefix(text, streaming) {
|
|
9875
10759
|
const opening = /^\s*<think(?:ing)?>/i.exec(text);
|
|
@@ -9885,7 +10769,7 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
9885
10769
|
const tagged = splitThinkingPrefix(source.text, source.streaming);
|
|
9886
10770
|
const bubble = { ...source, text: tagged.text, thinking: [source.thinking, tagged.thinking].filter(Boolean).join("\n") };
|
|
9887
10771
|
const { columns } = useTerminalSize();
|
|
9888
|
-
const frozenThinking =
|
|
10772
|
+
const frozenThinking = useRef4(void 0);
|
|
9889
10773
|
const answering = bubble.streaming && bubble.text !== "";
|
|
9890
10774
|
if (!answering) frozenThinking.current = void 0;
|
|
9891
10775
|
if (answering && frozenThinking.current === void 0) {
|
|
@@ -9900,11 +10784,11 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
9900
10784
|
const frameThinking = thinkingBudget === void 0 ? visibleThinking.trim() : clipTextToRows(visibleThinking.trim(), thinkingBudget, columns);
|
|
9901
10785
|
const usedThinkingRows = frameThinking === "" ? 0 : frameThinking.split("\n").length;
|
|
9902
10786
|
const frameText = liveBudget === void 0 ? bubble.text : clipTextToRows(bubble.text, Math.max(1, liveBudget - usedThinkingRows), columns);
|
|
9903
|
-
return /* @__PURE__ */ jsxs11(
|
|
9904
|
-
frameThinking !== "" && /* @__PURE__ */
|
|
9905
|
-
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 }),
|
|
9906
10790
|
bubble.streaming && bubble.text === "" && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
9907
|
-
/* @__PURE__ */
|
|
10791
|
+
/* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
|
|
9908
10792
|
" thinking"
|
|
9909
10793
|
] })
|
|
9910
10794
|
] });
|
|
@@ -9923,7 +10807,7 @@ function wrapVisualLine(line, columns) {
|
|
|
9923
10807
|
let current = "";
|
|
9924
10808
|
let currentWidth = 0;
|
|
9925
10809
|
for (const character of line) {
|
|
9926
|
-
const width = Math.max(1,
|
|
10810
|
+
const width = Math.max(1, stringWidth4(character));
|
|
9927
10811
|
if (current !== "" && currentWidth + width > columns) {
|
|
9928
10812
|
rows.push(current);
|
|
9929
10813
|
current = "";
|
|
@@ -9940,23 +10824,23 @@ function ToolView({ bubble }) {
|
|
|
9940
10824
|
const mark = bubble.state === "running" ? "\u25CC" : bubble.state === "ok" ? "\u25CF" : "\u2717";
|
|
9941
10825
|
const color = bubble.state === "error" ? theme.error : bubble.state === "ok" ? theme.ok : theme.warn;
|
|
9942
10826
|
const display = bubble.display;
|
|
9943
|
-
const diff =
|
|
10827
|
+
const diff = useMemo5(
|
|
9944
10828
|
() => display?.kind === "diff" ? diffLines(display.before, display.after) : null,
|
|
9945
10829
|
[display]
|
|
9946
10830
|
);
|
|
9947
|
-
if (!diff) return /* @__PURE__ */
|
|
10831
|
+
if (!diff) return /* @__PURE__ */ jsx11(NonDiffToolView, { bubble, mark, color });
|
|
9948
10832
|
const hasChanges = diff.added > 0 || diff.removed > 0;
|
|
9949
|
-
return /* @__PURE__ */ jsxs11(
|
|
10833
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
9950
10834
|
/* @__PURE__ */ jsxs11(Text11, { color, children: [
|
|
9951
10835
|
mark,
|
|
9952
10836
|
" ",
|
|
9953
10837
|
bubble.summary,
|
|
9954
10838
|
hasChanges && /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
9955
|
-
diff.added > 0 && /* @__PURE__ */
|
|
9956
|
-
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}` })
|
|
9957
10841
|
] })
|
|
9958
10842
|
] }),
|
|
9959
|
-
/* @__PURE__ */
|
|
10843
|
+
/* @__PURE__ */ jsx11(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsx11(DiffHunk, { lines: diff.hunk, hidden: diff.hidden }) })
|
|
9960
10844
|
] });
|
|
9961
10845
|
}
|
|
9962
10846
|
function NonDiffToolView({
|
|
@@ -9964,13 +10848,32 @@ function NonDiffToolView({
|
|
|
9964
10848
|
mark,
|
|
9965
10849
|
color
|
|
9966
10850
|
}) {
|
|
9967
|
-
|
|
10851
|
+
const running = bubble.state === "running";
|
|
10852
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
9968
10853
|
/* @__PURE__ */ jsxs11(Text11, { color, children: [
|
|
9969
10854
|
mark,
|
|
9970
10855
|
" ",
|
|
9971
|
-
bubble.summary
|
|
10856
|
+
bubble.summary,
|
|
10857
|
+
running && bubble.startedAt !== void 0 && /* @__PURE__ */ jsx11(Elapsed, { since: bubble.startedAt })
|
|
9972
10858
|
] }),
|
|
9973
|
-
|
|
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)
|
|
9974
10877
|
] });
|
|
9975
10878
|
}
|
|
9976
10879
|
function previewLines(content) {
|
|
@@ -9982,7 +10885,7 @@ var VISIBLE_SUBAGENTS = 3;
|
|
|
9982
10885
|
function SubagentSummary({ agents }) {
|
|
9983
10886
|
const theme = useTheme();
|
|
9984
10887
|
const running = agents.filter((agent) => agent.state === "running");
|
|
9985
|
-
return /* @__PURE__ */ jsxs11(
|
|
10888
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
9986
10889
|
/* @__PURE__ */ jsxs11(Text11, { color: theme.accent, wrap: "truncate-end", children: [
|
|
9987
10890
|
"Subagents: ",
|
|
9988
10891
|
running.length,
|
|
@@ -9999,7 +10902,7 @@ function SubagentSummary({ agents }) {
|
|
|
9999
10902
|
progress.replace(/\s+/g, " ").trim() || "waiting"
|
|
10000
10903
|
] }, agent.id);
|
|
10001
10904
|
}),
|
|
10002
|
-
/* @__PURE__ */
|
|
10905
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "/subagents \xB7 open a sub-agent chat" })
|
|
10003
10906
|
] });
|
|
10004
10907
|
}
|
|
10005
10908
|
function SubagentView({ bubble, maxRows }) {
|
|
@@ -10009,16 +10912,99 @@ function SubagentView({ bubble, maxRows }) {
|
|
|
10009
10912
|
const color = bubble.state === "running" ? theme.warn : theme.ok;
|
|
10010
10913
|
const latest = bubble.bubbles.at(-1);
|
|
10011
10914
|
const progress = latest?.kind === "assistant" ? sanitizeThinkingText(latest.text || latest.thinking, latest.streaming) : latest?.kind === "tool" ? latest.summary : "";
|
|
10012
|
-
return /* @__PURE__ */ jsxs11(
|
|
10915
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
10013
10916
|
/* @__PURE__ */ jsxs11(Text11, { color, bold: true, wrap: "truncate-end", children: [
|
|
10014
10917
|
mark,
|
|
10015
10918
|
" subagent: ",
|
|
10016
10919
|
bubble.description
|
|
10017
10920
|
] }),
|
|
10018
|
-
/* @__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 })
|
|
10922
|
+
] });
|
|
10923
|
+
}
|
|
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
|
+
] })
|
|
10019
10980
|
] });
|
|
10020
10981
|
}
|
|
10021
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
|
+
|
|
10022
11008
|
// src/ui/transcript.ts
|
|
10023
11009
|
function emptyTranscript() {
|
|
10024
11010
|
return { bubbles: [], seq: 0 };
|
|
@@ -10059,11 +11045,19 @@ function applyEvent(state, event) {
|
|
|
10059
11045
|
name: sanitizeTerminalText(event.name),
|
|
10060
11046
|
summary: sanitizeTerminalText(event.summary),
|
|
10061
11047
|
state: "running",
|
|
10062
|
-
content: ""
|
|
11048
|
+
content: "",
|
|
11049
|
+
startedAt: Date.now()
|
|
10063
11050
|
}
|
|
10064
11051
|
],
|
|
10065
11052
|
seq: state.seq + 1
|
|
10066
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
|
+
};
|
|
10067
11061
|
case "tool_end":
|
|
10068
11062
|
return {
|
|
10069
11063
|
...state,
|
|
@@ -10120,6 +11114,7 @@ function applyEvent(state, event) {
|
|
|
10120
11114
|
case "turn_start":
|
|
10121
11115
|
case "usage":
|
|
10122
11116
|
case "rate_limits":
|
|
11117
|
+
case "speed":
|
|
10123
11118
|
return state;
|
|
10124
11119
|
}
|
|
10125
11120
|
}
|
|
@@ -10256,10 +11251,25 @@ function sanitizeDisplay(display) {
|
|
|
10256
11251
|
}
|
|
10257
11252
|
|
|
10258
11253
|
// src/ui/App.tsx
|
|
10259
|
-
import { jsx as
|
|
11254
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
10260
11255
|
var EFFORTS = ["auto", "low", "medium", "high", "xhigh", "max"];
|
|
10261
11256
|
var STREAM_FRAME_MS = 50;
|
|
11257
|
+
var CUSTOM_ACCENT = "__custom__";
|
|
11258
|
+
var BELL_AFTER_MS = 15e3;
|
|
11259
|
+
var SUBAGENT_CHAT_EXTRA_ROWS = 4;
|
|
10262
11260
|
var MAX_ATTACHMENTS = 8;
|
|
11261
|
+
var CONTEXT_PRESETS = [256e3, 512e3, 1e6];
|
|
11262
|
+
function formatContextTokens(tokens2) {
|
|
11263
|
+
if (tokens2 >= 1e6) return `${tokens2 / 1e6}M`;
|
|
11264
|
+
return `${Math.round(tokens2 / 1e3)}k`;
|
|
11265
|
+
}
|
|
11266
|
+
function parseContextTokens(input) {
|
|
11267
|
+
const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(input.trim());
|
|
11268
|
+
if (!match) return null;
|
|
11269
|
+
const scale = match[2]?.toLowerCase() === "m" ? 1e6 : match[2]?.toLowerCase() === "k" ? 1e3 : 1;
|
|
11270
|
+
const tokens2 = Math.round(Number(match[1]) * scale);
|
|
11271
|
+
return tokens2 >= 1e3 && tokens2 <= 1e7 ? tokens2 : null;
|
|
11272
|
+
}
|
|
10263
11273
|
function App({
|
|
10264
11274
|
runtime,
|
|
10265
11275
|
initialHistory,
|
|
@@ -10268,40 +11278,53 @@ function App({
|
|
|
10268
11278
|
const { exit, suspendTerminal } = useApp();
|
|
10269
11279
|
const { stdout } = useStdout2();
|
|
10270
11280
|
const { rows } = useTerminalSize();
|
|
10271
|
-
const [transcript, setTranscript] =
|
|
11281
|
+
const [transcript, setTranscript] = useState8(
|
|
10272
11282
|
() => warnings.reduce((state, text) => pushNotice(state, "warn", text), fromHistory(initialHistory))
|
|
10273
11283
|
);
|
|
10274
|
-
const [transcriptRevision, setTranscriptRevision] =
|
|
10275
|
-
const
|
|
10276
|
-
|
|
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(
|
|
10277
11289
|
() => inputHistoryFromMessages(initialHistory)
|
|
10278
11290
|
);
|
|
10279
|
-
const [busy, setBusy] =
|
|
10280
|
-
const busyRef =
|
|
10281
|
-
const [pendingCount, setPendingCount] =
|
|
10282
|
-
const queueRef =
|
|
10283
|
-
const slashQueueRef =
|
|
10284
|
-
const slashPendingRef =
|
|
10285
|
-
const drainQueueRef =
|
|
10286
|
-
const [attachments, setAttachments] =
|
|
10287
|
-
const attachmentsRef =
|
|
10288
|
-
const attachmentGeneration =
|
|
10289
|
-
const automaticAttachmentTask =
|
|
10290
|
-
const clipboardPasteTask =
|
|
10291
|
-
const [overlay, setOverlay] =
|
|
10292
|
-
const [accent, setAccent] =
|
|
10293
|
-
const [lang, setLang] =
|
|
10294
|
-
const [setup, setSetup] =
|
|
10295
|
-
const [, forceRender] =
|
|
10296
|
-
const history =
|
|
10297
|
-
const abort =
|
|
10298
|
-
const sessionStart =
|
|
10299
|
-
const turns =
|
|
10300
|
-
const [turnStart, setTurnStart] =
|
|
10301
|
-
const [, tick] =
|
|
10302
|
-
const [context, setContext] =
|
|
10303
|
-
const
|
|
10304
|
-
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]);
|
|
10305
11328
|
const clearScreen = useCallback(() => {
|
|
10306
11329
|
if (stdout.isTTY) stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
10307
11330
|
}, [stdout]);
|
|
@@ -10328,6 +11351,7 @@ function App({
|
|
|
10328
11351
|
}, []);
|
|
10329
11352
|
const queueTranscriptEvent = useCallback(
|
|
10330
11353
|
(event) => {
|
|
11354
|
+
recordSpeed(speedMeter.current, event);
|
|
10331
11355
|
transcriptEvents.current.push(event);
|
|
10332
11356
|
if (event.type !== "text_delta" && event.type !== "thinking_delta") {
|
|
10333
11357
|
flushTranscriptEvents();
|
|
@@ -10337,24 +11361,24 @@ function App({
|
|
|
10337
11361
|
},
|
|
10338
11362
|
[flushTranscriptEvents]
|
|
10339
11363
|
);
|
|
10340
|
-
|
|
11364
|
+
useEffect4(
|
|
10341
11365
|
() => () => {
|
|
10342
11366
|
if (transcriptTimer.current !== null) clearTimeout(transcriptTimer.current);
|
|
10343
11367
|
transcriptEvents.current = [];
|
|
10344
11368
|
},
|
|
10345
11369
|
[]
|
|
10346
11370
|
);
|
|
10347
|
-
|
|
11371
|
+
useEffect4(
|
|
10348
11372
|
() => runtime.subscribeContext(() => setContext(runtime.modelContext())),
|
|
10349
11373
|
[runtime]
|
|
10350
11374
|
);
|
|
10351
|
-
|
|
11375
|
+
useEffect4(() => {
|
|
10352
11376
|
if (turnStart === null) return;
|
|
10353
11377
|
const timer = setInterval(() => tick((n) => n + 1), 1e3);
|
|
10354
11378
|
return () => clearInterval(timer);
|
|
10355
11379
|
}, [turnStart]);
|
|
10356
|
-
const theme =
|
|
10357
|
-
const strings =
|
|
11380
|
+
const theme = useMemo6(() => makeTheme(accent), [accent]);
|
|
11381
|
+
const strings = useMemo6(() => stringsFor(lang), [lang]);
|
|
10358
11382
|
const notice = useCallback(
|
|
10359
11383
|
(level, text) => {
|
|
10360
11384
|
flushTranscriptEvents();
|
|
@@ -10362,7 +11386,7 @@ function App({
|
|
|
10362
11386
|
},
|
|
10363
11387
|
[flushTranscriptEvents]
|
|
10364
11388
|
);
|
|
10365
|
-
|
|
11389
|
+
useEffect4(() => {
|
|
10366
11390
|
const check = runtime.startupUpdateCheck();
|
|
10367
11391
|
let active2 = true;
|
|
10368
11392
|
void check.then((result) => {
|
|
@@ -10402,7 +11426,8 @@ function App({
|
|
|
10402
11426
|
abort.current = controller;
|
|
10403
11427
|
busyRef.current = true;
|
|
10404
11428
|
setBusy(true);
|
|
10405
|
-
|
|
11429
|
+
const startedAt = Date.now();
|
|
11430
|
+
setTurnStart(startedAt);
|
|
10406
11431
|
turns.current += 1;
|
|
10407
11432
|
const hooks = {
|
|
10408
11433
|
onEvent: queueTranscriptEvent,
|
|
@@ -10425,7 +11450,11 @@ function App({
|
|
|
10425
11450
|
return;
|
|
10426
11451
|
}
|
|
10427
11452
|
controller.signal.addEventListener("abort", cancel, { once: true });
|
|
10428
|
-
|
|
11453
|
+
bell();
|
|
11454
|
+
setOverlay((current) => {
|
|
11455
|
+
if (current.kind === "picker") current.select("");
|
|
11456
|
+
return { kind: "permission", request, resolve: finish };
|
|
11457
|
+
});
|
|
10429
11458
|
})
|
|
10430
11459
|
};
|
|
10431
11460
|
try {
|
|
@@ -10439,6 +11468,7 @@ function App({
|
|
|
10439
11468
|
} catch (error) {
|
|
10440
11469
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
10441
11470
|
} finally {
|
|
11471
|
+
if (!controller.signal.aborted && Date.now() - startedAt >= BELL_AFTER_MS) bell();
|
|
10442
11472
|
flushTranscriptEvents();
|
|
10443
11473
|
abort.current = null;
|
|
10444
11474
|
busyRef.current = false;
|
|
@@ -10446,9 +11476,14 @@ function App({
|
|
|
10446
11476
|
setTurnStart(null);
|
|
10447
11477
|
drainQueueRef.current();
|
|
10448
11478
|
}
|
|
10449
|
-
}, [flushTranscriptEvents, notice, queueTranscriptEvent, runtime]);
|
|
11479
|
+
}, [bell, flushTranscriptEvents, notice, queueTranscriptEvent, runtime]);
|
|
10450
11480
|
const applyAccent = useCallback(
|
|
10451
|
-
async (
|
|
11481
|
+
async (requested) => {
|
|
11482
|
+
const value = parseAccent(requested);
|
|
11483
|
+
if (!value) {
|
|
11484
|
+
notice("warn", strings.accentInvalid(requested));
|
|
11485
|
+
return;
|
|
11486
|
+
}
|
|
10452
11487
|
await runtime.setAccent(value);
|
|
10453
11488
|
setAccent(value);
|
|
10454
11489
|
notice("info", strings.accentSet(value, resolveAccent(value)));
|
|
@@ -10652,6 +11687,10 @@ function App({
|
|
|
10652
11687
|
notice("info", strings.undoNothing);
|
|
10653
11688
|
return;
|
|
10654
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
|
+
}
|
|
10655
11694
|
const lines = [strings.undoResult(result.restored.length, result.removed.length)];
|
|
10656
11695
|
if (result.conflicts.length > 0) {
|
|
10657
11696
|
lines.push(strings.undoConflicts(result.conflicts));
|
|
@@ -10776,8 +11815,8 @@ ${strings.modelSet(result.nextModel)}` : ""}`;
|
|
|
10776
11815
|
return;
|
|
10777
11816
|
}
|
|
10778
11817
|
try {
|
|
10779
|
-
const
|
|
10780
|
-
notice("info", strings.modelsRefreshed(providerId,
|
|
11818
|
+
const count2 = await runtime.refreshProviderModels(providerId);
|
|
11819
|
+
notice("info", strings.modelsRefreshed(providerId, count2));
|
|
10781
11820
|
} catch (error) {
|
|
10782
11821
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
10783
11822
|
}
|
|
@@ -11077,7 +12116,7 @@ ${strings.mcpAddUsage}`
|
|
|
11077
12116
|
history.current = result.history;
|
|
11078
12117
|
setPromptHistory(inputHistoryFromMessages(result.history));
|
|
11079
12118
|
setTranscript(
|
|
11080
|
-
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))
|
|
11081
12120
|
);
|
|
11082
12121
|
setTranscriptRevision((revision) => revision + 1);
|
|
11083
12122
|
await runtime.persist(result.history);
|
|
@@ -11096,17 +12135,27 @@ ${strings.mcpAddUsage}`
|
|
|
11096
12135
|
return;
|
|
11097
12136
|
}
|
|
11098
12137
|
case "subagents": {
|
|
11099
|
-
const agents =
|
|
12138
|
+
const agents = transcriptRef.current.bubbles.filter(
|
|
12139
|
+
(bubble) => bubble.kind === "subagent"
|
|
12140
|
+
);
|
|
11100
12141
|
if (agents.length === 0) {
|
|
11101
|
-
notice("info", strings.
|
|
12142
|
+
notice("info", strings.noSubagents);
|
|
11102
12143
|
return;
|
|
11103
12144
|
}
|
|
11104
|
-
const
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
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 });
|
|
11110
12159
|
return;
|
|
11111
12160
|
}
|
|
11112
12161
|
case "checker":
|
|
@@ -11124,9 +12173,21 @@ ${strings.mcpAddUsage}`
|
|
|
11124
12173
|
}
|
|
11125
12174
|
const choice = await pick(
|
|
11126
12175
|
strings.titleAccent,
|
|
11127
|
-
|
|
12176
|
+
[
|
|
12177
|
+
...Object.entries(PRESETS).map(([name, hex]) => ({ key: name, label: name, hint: hex })),
|
|
12178
|
+
{ key: CUSTOM_ACCENT, label: strings.customAccent, hint: "#rrggbb" }
|
|
12179
|
+
]
|
|
11128
12180
|
);
|
|
11129
|
-
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));
|
|
11130
12191
|
return;
|
|
11131
12192
|
}
|
|
11132
12193
|
case "thinking": {
|
|
@@ -11163,16 +12224,16 @@ ${strings.mcpAddUsage}`
|
|
|
11163
12224
|
case "budget": {
|
|
11164
12225
|
const current = runtime.getMaxTokensPerTurn();
|
|
11165
12226
|
if (rest[0]) {
|
|
11166
|
-
const
|
|
11167
|
-
if (
|
|
12227
|
+
const tokens2 = Number(rest[0]);
|
|
12228
|
+
if (tokens2 === 0) {
|
|
11168
12229
|
await runtime.setMaxTokensPerTurn(0);
|
|
11169
12230
|
notice("info", strings.budgetUnlimited);
|
|
11170
12231
|
forceRender((n) => n + 1);
|
|
11171
|
-
} else if (!Number.isInteger(
|
|
12232
|
+
} else if (!Number.isInteger(tokens2) || tokens2 < 1e3 || tokens2 > 1e7) {
|
|
11172
12233
|
notice("warn", strings.budgetInvalid);
|
|
11173
12234
|
} else {
|
|
11174
|
-
await runtime.setMaxTokensPerTurn(
|
|
11175
|
-
notice("info", strings.budgetSet(
|
|
12235
|
+
await runtime.setMaxTokensPerTurn(tokens2);
|
|
12236
|
+
notice("info", strings.budgetSet(tokens2));
|
|
11176
12237
|
forceRender((n) => n + 1);
|
|
11177
12238
|
}
|
|
11178
12239
|
} else {
|
|
@@ -11180,6 +12241,57 @@ ${strings.mcpAddUsage}`
|
|
|
11180
12241
|
}
|
|
11181
12242
|
return;
|
|
11182
12243
|
}
|
|
12244
|
+
case "context": {
|
|
12245
|
+
const override = runtime.getContextWindowOverride();
|
|
12246
|
+
const current = runtime.modelContext();
|
|
12247
|
+
const requested = rest[0]?.toLowerCase();
|
|
12248
|
+
if (requested !== void 0 && requested !== "") {
|
|
12249
|
+
if (requested === "auto") {
|
|
12250
|
+
await runtime.setContextWindowOverride(void 0);
|
|
12251
|
+
notice("info", strings.contextAuto(runtime.modelContext().window));
|
|
12252
|
+
forceRender((n) => n + 1);
|
|
12253
|
+
return;
|
|
12254
|
+
}
|
|
12255
|
+
const tokens2 = parseContextTokens(requested);
|
|
12256
|
+
if (tokens2 === null) {
|
|
12257
|
+
notice("warn", strings.contextInvalid);
|
|
12258
|
+
return;
|
|
12259
|
+
}
|
|
12260
|
+
await runtime.setContextWindowOverride(tokens2);
|
|
12261
|
+
notice("info", strings.contextSet(tokens2));
|
|
12262
|
+
forceRender((n) => n + 1);
|
|
12263
|
+
return;
|
|
12264
|
+
}
|
|
12265
|
+
const effective = override ?? current.window;
|
|
12266
|
+
notice("info", effective === null ? strings.contextUnknown : strings.contextCurrent(effective));
|
|
12267
|
+
const choice = await pick(
|
|
12268
|
+
strings.titleContext,
|
|
12269
|
+
CONTEXT_PRESETS.map((preset) => ({
|
|
12270
|
+
key: String(preset),
|
|
12271
|
+
label: formatContextTokens(preset),
|
|
12272
|
+
hint: current.window === preset ? strings.contextCurrentLabel : void 0
|
|
12273
|
+
})).concat([
|
|
12274
|
+
{
|
|
12275
|
+
key: "auto",
|
|
12276
|
+
label: "auto",
|
|
12277
|
+
hint: override === void 0 ? strings.contextCurrentLabel : void 0
|
|
12278
|
+
}
|
|
12279
|
+
])
|
|
12280
|
+
);
|
|
12281
|
+
if (!choice) return;
|
|
12282
|
+
const chosen = CONTEXT_PRESETS.find((preset) => String(preset) === choice);
|
|
12283
|
+
if (choice === "auto") {
|
|
12284
|
+
await runtime.setContextWindowOverride(void 0);
|
|
12285
|
+
notice("info", strings.contextAuto(runtime.modelContext().window));
|
|
12286
|
+
} else if (chosen !== void 0) {
|
|
12287
|
+
await runtime.setContextWindowOverride(chosen);
|
|
12288
|
+
notice("info", strings.contextSet(chosen));
|
|
12289
|
+
} else {
|
|
12290
|
+
return;
|
|
12291
|
+
}
|
|
12292
|
+
forceRender((n) => n + 1);
|
|
12293
|
+
return;
|
|
12294
|
+
}
|
|
11183
12295
|
case "model": {
|
|
11184
12296
|
const items = await runtime.listModelItems();
|
|
11185
12297
|
if (items.length === 0) {
|
|
@@ -11329,7 +12441,7 @@ Rename the file if you want a different name.`);
|
|
|
11329
12441
|
return;
|
|
11330
12442
|
}
|
|
11331
12443
|
setTranscript((state) => pushUser(state, userDisplay(item.text, item.content)));
|
|
11332
|
-
history.current = [...history.current, { role: "user", content: item.content }];
|
|
12444
|
+
history.current = [...history.current, { role: "user", content: withUndoNote(item.content) }];
|
|
11333
12445
|
void runAgent();
|
|
11334
12446
|
},
|
|
11335
12447
|
[runAgent, runSlash]
|
|
@@ -11346,6 +12458,49 @@ Rename the file if you want a different name.`);
|
|
|
11346
12458
|
queueMicrotask(() => enqueueNext(next));
|
|
11347
12459
|
}, [enqueueNext]);
|
|
11348
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
|
+
};
|
|
11349
12504
|
const tryQueueAutomaticAttachment = useCallback(
|
|
11350
12505
|
(requestedPath) => {
|
|
11351
12506
|
if (!looksLikeAttachmentPath(requestedPath)) return Promise.resolve(false);
|
|
@@ -11356,19 +12511,30 @@ Rename the file if you want a different name.`);
|
|
|
11356
12511
|
notice("warn", `At most ${MAX_ATTACHMENTS} attachments can be queued for one message.`);
|
|
11357
12512
|
return Promise.resolve(true);
|
|
11358
12513
|
}
|
|
11359
|
-
const
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
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
|
+
}
|
|
11365
12535
|
}
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
}).catch(() => {
|
|
11369
|
-
if (automaticAttachmentTask.current !== task) return true;
|
|
11370
|
-
return false;
|
|
11371
|
-
});
|
|
12536
|
+
return added > 0;
|
|
12537
|
+
})();
|
|
11372
12538
|
automaticAttachmentTask.current = task;
|
|
11373
12539
|
void task.finally(() => {
|
|
11374
12540
|
if (automaticAttachmentTask.current === task) automaticAttachmentTask.current = null;
|
|
@@ -11420,12 +12586,11 @@ Rename the file if you want a different name.`);
|
|
|
11420
12586
|
const submitSlash = () => {
|
|
11421
12587
|
if (!detachedInput) setInput("");
|
|
11422
12588
|
const command = text.slice(1).trim().split(/\s+/)[0]?.toLowerCase();
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
queueRef.current = [...queueRef.current, { kind: "command", line: text }];
|
|
11426
|
-
setPendingCount(queueRef.current.length);
|
|
12589
|
+
if (busyRef.current && !commandRunsWhileBusy(command) || slashPendingRef.current > 0) {
|
|
12590
|
+
enqueue({ kind: "command", line: text });
|
|
11427
12591
|
return;
|
|
11428
12592
|
}
|
|
12593
|
+
checkedOut.current = null;
|
|
11429
12594
|
runSlash(text);
|
|
11430
12595
|
};
|
|
11431
12596
|
if (isKnownSlashCommand(text)) {
|
|
@@ -11463,14 +12628,14 @@ Rename the file if you want a different name.`);
|
|
|
11463
12628
|
...mentioned
|
|
11464
12629
|
];
|
|
11465
12630
|
replaceAttachments([]);
|
|
11466
|
-
const item = { kind: "message", text, content };
|
|
12631
|
+
const item = { kind: "message", text, content, attachments: queuedAttachments };
|
|
11467
12632
|
if (busyRef.current || slashPendingRef.current > 0) {
|
|
11468
|
-
|
|
11469
|
-
setPendingCount(queueRef.current.length);
|
|
12633
|
+
enqueue(item);
|
|
11470
12634
|
return;
|
|
11471
12635
|
}
|
|
12636
|
+
checkedOut.current = null;
|
|
11472
12637
|
setTranscript((state) => pushUser(state, userDisplay(text, content)));
|
|
11473
|
-
history.current = [...history.current, { role: "user", content }];
|
|
12638
|
+
history.current = [...history.current, { role: "user", content: withUndoNote(content) }];
|
|
11474
12639
|
void runAgent();
|
|
11475
12640
|
},
|
|
11476
12641
|
[replaceAttachments, runAgent, runSlash, tryQueueAutomaticAttachment]
|
|
@@ -11483,14 +12648,25 @@ Rename the file if you want a different name.`);
|
|
|
11483
12648
|
return;
|
|
11484
12649
|
}
|
|
11485
12650
|
if (!key.escape) return;
|
|
11486
|
-
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")) {
|
|
11487
12663
|
if (!abort.current.signal.aborted) {
|
|
11488
12664
|
abort.current.abort();
|
|
11489
12665
|
notice("warn", strings.cancelled);
|
|
11490
12666
|
}
|
|
11491
12667
|
return;
|
|
11492
12668
|
}
|
|
11493
|
-
if (
|
|
12669
|
+
if (hasDraft) {
|
|
11494
12670
|
attachmentGeneration.current += 1;
|
|
11495
12671
|
setInput("");
|
|
11496
12672
|
replaceAttachments([]);
|
|
@@ -11515,10 +12691,10 @@ Rename the file if you want a different name.`);
|
|
|
11515
12691
|
}
|
|
11516
12692
|
setInput("");
|
|
11517
12693
|
});
|
|
11518
|
-
const shell = (children) => /* @__PURE__ */
|
|
12694
|
+
const shell = (children) => /* @__PURE__ */ jsx14(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx14(StringsContext.Provider, { value: strings, children }) });
|
|
11519
12695
|
if (lang === void 0) {
|
|
11520
12696
|
return shell(
|
|
11521
|
-
/* @__PURE__ */
|
|
12697
|
+
/* @__PURE__ */ jsx14(
|
|
11522
12698
|
LanguagePicker,
|
|
11523
12699
|
{
|
|
11524
12700
|
onPick: (chosen) => {
|
|
@@ -11531,7 +12707,7 @@ Rename the file if you want a different name.`);
|
|
|
11531
12707
|
}
|
|
11532
12708
|
if (setup) {
|
|
11533
12709
|
return shell(
|
|
11534
|
-
/* @__PURE__ */
|
|
12710
|
+
/* @__PURE__ */ jsx14(
|
|
11535
12711
|
Onboarding,
|
|
11536
12712
|
{
|
|
11537
12713
|
onSubmit: async (url, key) => {
|
|
@@ -11546,17 +12722,17 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11546
12722
|
);
|
|
11547
12723
|
}
|
|
11548
12724
|
return shell(
|
|
11549
|
-
/* @__PURE__ */
|
|
11550
|
-
/* @__PURE__ */
|
|
12725
|
+
/* @__PURE__ */ jsxs13(TerminalViewport, { rows, children: [
|
|
12726
|
+
/* @__PURE__ */ jsx14(
|
|
11551
12727
|
Transcript,
|
|
11552
12728
|
{
|
|
11553
12729
|
bubbles: transcript.bubbles,
|
|
11554
12730
|
workspace: runtime.cwd,
|
|
11555
|
-
maxLiveRows: liveTranscriptRows(rows)
|
|
12731
|
+
maxLiveRows: overlay.kind === "subagent" ? 0 : liveTranscriptRows(rows)
|
|
11556
12732
|
},
|
|
11557
12733
|
transcriptRevision
|
|
11558
12734
|
),
|
|
11559
|
-
overlay.kind === "permission" && /* @__PURE__ */
|
|
12735
|
+
overlay.kind === "permission" && /* @__PURE__ */ jsx14(
|
|
11560
12736
|
PermissionPrompt,
|
|
11561
12737
|
{
|
|
11562
12738
|
request: overlay.request,
|
|
@@ -11566,7 +12742,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11566
12742
|
}
|
|
11567
12743
|
}
|
|
11568
12744
|
),
|
|
11569
|
-
overlay.kind === "picker" && /* @__PURE__ */
|
|
12745
|
+
overlay.kind === "picker" && /* @__PURE__ */ jsx14(
|
|
11570
12746
|
Picker,
|
|
11571
12747
|
{
|
|
11572
12748
|
title: overlay.title,
|
|
@@ -11581,7 +12757,17 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11581
12757
|
}
|
|
11582
12758
|
}
|
|
11583
12759
|
),
|
|
11584
|
-
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(
|
|
11585
12771
|
Confirm,
|
|
11586
12772
|
{
|
|
11587
12773
|
title: overlay.title,
|
|
@@ -11593,7 +12779,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11593
12779
|
}
|
|
11594
12780
|
}
|
|
11595
12781
|
),
|
|
11596
|
-
overlay.kind === "textinput" && /* @__PURE__ */
|
|
12782
|
+
overlay.kind === "textinput" && /* @__PURE__ */ jsx14(
|
|
11597
12783
|
TextInputOverlay,
|
|
11598
12784
|
{
|
|
11599
12785
|
title: overlay.title,
|
|
@@ -11608,7 +12794,7 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11608
12794
|
}
|
|
11609
12795
|
}
|
|
11610
12796
|
),
|
|
11611
|
-
overlay.kind === "none" && /* @__PURE__ */
|
|
12797
|
+
overlay.kind === "none" && /* @__PURE__ */ jsx14(
|
|
11612
12798
|
PromptInput,
|
|
11613
12799
|
{
|
|
11614
12800
|
value: input,
|
|
@@ -11619,7 +12805,11 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11619
12805
|
onListFiles: listWorkspaceFiles,
|
|
11620
12806
|
disabled: busy,
|
|
11621
12807
|
pending: pendingCount,
|
|
11622
|
-
|
|
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(),
|
|
11623
12813
|
history: promptHistory,
|
|
11624
12814
|
attachments: attachments.flatMap((block) => {
|
|
11625
12815
|
const label = attachmentLabel(block);
|
|
@@ -11627,21 +12817,23 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
11627
12817
|
})
|
|
11628
12818
|
}
|
|
11629
12819
|
),
|
|
11630
|
-
/* @__PURE__ */
|
|
12820
|
+
/* @__PURE__ */ jsx14(
|
|
11631
12821
|
StatusBar,
|
|
11632
12822
|
{
|
|
11633
12823
|
status: {
|
|
11634
12824
|
modelRef: runtime.getModelRef() || strings.noModel,
|
|
11635
12825
|
effort: runtime.getEffort(),
|
|
12826
|
+
effectiveEffort: runtime.getEffectiveEffort(history.current),
|
|
11636
12827
|
thinking: runtime.getThinking(),
|
|
11637
12828
|
usage: runtime.usageParts(),
|
|
12829
|
+
speed: readSpeed(speedMeter.current, busy),
|
|
11638
12830
|
mcp: runtime.mcpSummary(),
|
|
11639
12831
|
mode: runtime.getMode(),
|
|
11640
12832
|
bypass: runtime.isBypassEnabled(),
|
|
11641
12833
|
busy,
|
|
11642
12834
|
sessionMs: Date.now() - sessionStart.current,
|
|
11643
12835
|
turnMs: turnStart === null ? null : Date.now() - turnStart,
|
|
11644
|
-
|
|
12836
|
+
agents: runtime.activeAgentsList().map((agent) => agent.description),
|
|
11645
12837
|
context
|
|
11646
12838
|
}
|
|
11647
12839
|
}
|
|
@@ -11655,17 +12847,17 @@ function TextInputOverlay({
|
|
|
11655
12847
|
onAnswer,
|
|
11656
12848
|
onCancel
|
|
11657
12849
|
}) {
|
|
11658
|
-
const [value, setValue] =
|
|
12850
|
+
const [value, setValue] = useState8("");
|
|
11659
12851
|
const theme = useTheme();
|
|
11660
12852
|
const submit = useCallback(() => {
|
|
11661
12853
|
onAnswer(value || null);
|
|
11662
12854
|
}, [value, onAnswer]);
|
|
11663
|
-
|
|
12855
|
+
useInput3((_input, key) => {
|
|
11664
12856
|
if (key.escape) onCancel();
|
|
11665
12857
|
});
|
|
11666
|
-
return /* @__PURE__ */
|
|
11667
|
-
/* @__PURE__ */
|
|
11668
|
-
/* @__PURE__ */
|
|
12858
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
12859
|
+
/* @__PURE__ */ jsx14(Text13, { dimColor: true, children: title }),
|
|
12860
|
+
/* @__PURE__ */ jsx14(
|
|
11669
12861
|
TextInput2,
|
|
11670
12862
|
{
|
|
11671
12863
|
value,
|
|
@@ -11674,9 +12866,24 @@ function TextInputOverlay({
|
|
|
11674
12866
|
mask: mask ? "\u2022" : void 0
|
|
11675
12867
|
}
|
|
11676
12868
|
),
|
|
11677
|
-
/* @__PURE__ */
|
|
12869
|
+
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, color: theme.accent, children: "enter submit \xB7 esc cancel" }) })
|
|
11678
12870
|
] });
|
|
11679
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
|
+
}
|
|
11680
12887
|
function isKnownSlashCommand(line) {
|
|
11681
12888
|
if (!line.startsWith("/")) return false;
|
|
11682
12889
|
const [name = ""] = line.slice(1).trim().toLowerCase().split(/\s+/);
|
|
@@ -11747,7 +12954,7 @@ function unquoteArg(value) {
|
|
|
11747
12954
|
}
|
|
11748
12955
|
|
|
11749
12956
|
// src/app/tui.tsx
|
|
11750
|
-
import { jsx as
|
|
12957
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
11751
12958
|
function clearTerminal() {
|
|
11752
12959
|
if (process.stdout.isTTY) process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
11753
12960
|
}
|
|
@@ -11767,7 +12974,7 @@ async function startTui(options) {
|
|
|
11767
12974
|
});
|
|
11768
12975
|
clearTerminal();
|
|
11769
12976
|
const instance = render(
|
|
11770
|
-
/* @__PURE__ */
|
|
12977
|
+
/* @__PURE__ */ jsx15(App, { runtime, initialHistory: history, warnings }),
|
|
11771
12978
|
TUI_RENDER_OPTIONS
|
|
11772
12979
|
);
|
|
11773
12980
|
try {
|