@kernelonpanic/kitcode 1.4.5 → 1.4.6
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 +2 -1
- package/README.ru.md +2 -1
- package/dist/index.js +356 -213
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,6 +69,7 @@ var diagnosticsSchema = z.object({
|
|
|
69
69
|
autoRun: z.boolean().default(true),
|
|
70
70
|
commands: z.array(z.string().trim().min(1).max(2e4)).max(8).default([])
|
|
71
71
|
});
|
|
72
|
+
var MAX_SYSTEM_PROMPT_CHARS = 2e4;
|
|
72
73
|
var configSchema = z.object({
|
|
73
74
|
version: z.literal(1).default(1),
|
|
74
75
|
model: z.string().optional(),
|
|
@@ -82,7 +83,7 @@ var configSchema = z.object({
|
|
|
82
83
|
// comes from the provider catalogue or the endpoint's model list.
|
|
83
84
|
contextWindow: z.number().int().min(1e3).max(1e7).optional(),
|
|
84
85
|
// Extra instructions appended to the built-in system prompt on every turn.
|
|
85
|
-
systemPrompt: z.string().max(
|
|
86
|
+
systemPrompt: z.string().max(MAX_SYSTEM_PROMPT_CHARS).optional(),
|
|
86
87
|
// When false, systemPrompt is kept in the config but not sent to the model.
|
|
87
88
|
systemPromptEnabled: z.boolean().default(true),
|
|
88
89
|
// Hard cap on model calls in one turn. Higher values let large refactors
|
|
@@ -1276,8 +1277,40 @@ function isSubsequence(needle, haystack) {
|
|
|
1276
1277
|
return needle.length === 0;
|
|
1277
1278
|
}
|
|
1278
1279
|
|
|
1279
|
-
// src/tools/
|
|
1280
|
+
// src/tools/kill-tree.ts
|
|
1280
1281
|
import { spawn } from "child_process";
|
|
1282
|
+
function killTree(child) {
|
|
1283
|
+
if (child.pid == null) return;
|
|
1284
|
+
const fallback = () => {
|
|
1285
|
+
try {
|
|
1286
|
+
child.kill("SIGKILL");
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
if (process.platform === "win32") {
|
|
1291
|
+
try {
|
|
1292
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
1293
|
+
stdio: "ignore",
|
|
1294
|
+
windowsHide: true
|
|
1295
|
+
});
|
|
1296
|
+
killer.once("error", fallback);
|
|
1297
|
+
killer.once("close", (code) => {
|
|
1298
|
+
if (code !== 0) fallback();
|
|
1299
|
+
});
|
|
1300
|
+
} catch {
|
|
1301
|
+
fallback();
|
|
1302
|
+
}
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
process.kill(-child.pid, "SIGKILL");
|
|
1307
|
+
} catch {
|
|
1308
|
+
fallback();
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/tools/jobs.ts
|
|
1313
|
+
import { spawn as spawn2 } from "child_process";
|
|
1281
1314
|
import { StringDecoder } from "string_decoder";
|
|
1282
1315
|
var MAX_JOBS = 8;
|
|
1283
1316
|
var MAX_JOB_OUTPUT = 1e5;
|
|
@@ -1293,7 +1326,7 @@ function startJob(command, cwd) {
|
|
|
1293
1326
|
installExitCleanup();
|
|
1294
1327
|
counter += 1;
|
|
1295
1328
|
const id = `bg-${counter}`;
|
|
1296
|
-
const child =
|
|
1329
|
+
const child = spawn2(command, {
|
|
1297
1330
|
shell: true,
|
|
1298
1331
|
cwd,
|
|
1299
1332
|
detached: process.platform !== "win32",
|
|
@@ -1399,22 +1432,6 @@ function summary(job) {
|
|
|
1399
1432
|
signal: job.signal
|
|
1400
1433
|
};
|
|
1401
1434
|
}
|
|
1402
|
-
function killTree(child) {
|
|
1403
|
-
if (child.pid == null) return;
|
|
1404
|
-
if (process.platform === "win32") {
|
|
1405
|
-
try {
|
|
1406
|
-
spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], { stdio: "ignore" });
|
|
1407
|
-
} catch {
|
|
1408
|
-
child.kill("SIGKILL");
|
|
1409
|
-
}
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
try {
|
|
1413
|
-
process.kill(-child.pid, "SIGKILL");
|
|
1414
|
-
} catch {
|
|
1415
|
-
child.kill("SIGKILL");
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
1435
|
|
|
1419
1436
|
// src/core/memory.ts
|
|
1420
1437
|
import { createHash } from "crypto";
|
|
@@ -1892,10 +1909,115 @@ function validSessionId(id) {
|
|
|
1892
1909
|
return /^[A-Za-z0-9_-]{1,240}$/.test(id);
|
|
1893
1910
|
}
|
|
1894
1911
|
|
|
1912
|
+
// src/core/retry.ts
|
|
1913
|
+
var MAX_RETRIES2 = 9;
|
|
1914
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
1915
|
+
async function* streamWithRetry(provider, request, signal) {
|
|
1916
|
+
request = { ...request, signal };
|
|
1917
|
+
let lastError;
|
|
1918
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
1919
|
+
if (signal.aborted) return;
|
|
1920
|
+
let emittedOutput = false;
|
|
1921
|
+
try {
|
|
1922
|
+
const stream = provider.stream(request)[Symbol.asyncIterator]();
|
|
1923
|
+
let cancel;
|
|
1924
|
+
let cancelTimer;
|
|
1925
|
+
const cancelled = new Promise((resolve3) => {
|
|
1926
|
+
cancel = () => {
|
|
1927
|
+
cancelTimer ??= setTimeout(() => resolve3({ done: true, value: void 0 }), 100);
|
|
1928
|
+
};
|
|
1929
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
1930
|
+
if (signal.aborted) cancel();
|
|
1931
|
+
});
|
|
1932
|
+
try {
|
|
1933
|
+
for (; ; ) {
|
|
1934
|
+
const next = await Promise.race([stream.next(), cancelled]);
|
|
1935
|
+
if (next.done) break;
|
|
1936
|
+
if (!signal.aborted || next.value.type === "usage" || next.value.type === "done") {
|
|
1937
|
+
if (next.value.type !== "rate_limits" && next.value.type !== "retrying") emittedOutput = true;
|
|
1938
|
+
yield next.value;
|
|
1939
|
+
}
|
|
1940
|
+
if (next.value.type === "done") break;
|
|
1941
|
+
}
|
|
1942
|
+
} finally {
|
|
1943
|
+
signal.removeEventListener("abort", cancel);
|
|
1944
|
+
if (cancelTimer !== void 0) clearTimeout(cancelTimer);
|
|
1945
|
+
void stream.return?.().catch(() => void 0);
|
|
1946
|
+
}
|
|
1947
|
+
return;
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
if (signal.aborted) return;
|
|
1950
|
+
if (emittedOutput) throw error;
|
|
1951
|
+
lastError = error;
|
|
1952
|
+
if (attempt >= MAX_RETRIES2) break;
|
|
1953
|
+
const waitMs = retryBackoffMs(error, attempt);
|
|
1954
|
+
if (waitMs > 0) {
|
|
1955
|
+
yield {
|
|
1956
|
+
type: "retrying",
|
|
1957
|
+
attempt: attempt + 2,
|
|
1958
|
+
waitMs,
|
|
1959
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1960
|
+
};
|
|
1961
|
+
try {
|
|
1962
|
+
await sleep(waitMs, signal);
|
|
1963
|
+
} catch (error2) {
|
|
1964
|
+
if (signal.aborted) return;
|
|
1965
|
+
throw error2;
|
|
1966
|
+
}
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
break;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
if (lastError) throw lastError;
|
|
1973
|
+
}
|
|
1974
|
+
function retryBackoffMs(error, attempt) {
|
|
1975
|
+
if (!isRateLimitError(error)) return 0;
|
|
1976
|
+
const serverWait = retryAfterFromError(error);
|
|
1977
|
+
if (serverWait > 0) return Math.min(serverWait, 6e4);
|
|
1978
|
+
return Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, 3e4);
|
|
1979
|
+
}
|
|
1980
|
+
function isRateLimitError(error) {
|
|
1981
|
+
if (!(error instanceof ProviderError)) return false;
|
|
1982
|
+
return error.status === 429;
|
|
1983
|
+
}
|
|
1984
|
+
function retryAfterFromError(error) {
|
|
1985
|
+
const cause = error?.cause;
|
|
1986
|
+
const headers = cause?.headers;
|
|
1987
|
+
if (headers && typeof headers.get === "function") {
|
|
1988
|
+
const ms = Number(headers.get("retry-after-ms"));
|
|
1989
|
+
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
1990
|
+
const raw = headers.get("retry-after");
|
|
1991
|
+
if (raw) {
|
|
1992
|
+
const s = Number(raw);
|
|
1993
|
+
if (Number.isFinite(s) && s > 0) return s * 1e3;
|
|
1994
|
+
const date = Date.parse(raw);
|
|
1995
|
+
if (!Number.isFinite(s) && Number.isFinite(date)) return Math.max(0, date - Date.now());
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return 0;
|
|
1999
|
+
}
|
|
2000
|
+
function sleep(ms, signal) {
|
|
2001
|
+
return new Promise((resolve3, reject) => {
|
|
2002
|
+
if (signal.aborted) {
|
|
2003
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
const finish = () => {
|
|
2007
|
+
signal.removeEventListener("abort", onAbort);
|
|
2008
|
+
resolve3();
|
|
2009
|
+
};
|
|
2010
|
+
const onAbort = () => {
|
|
2011
|
+
clearTimeout(id);
|
|
2012
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
2013
|
+
};
|
|
2014
|
+
const id = setTimeout(finish, ms);
|
|
2015
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
|
|
1895
2019
|
// src/core/agent.ts
|
|
1896
2020
|
var MAX_PAUSE_RESUMES = 5;
|
|
1897
|
-
var MAX_RETRIES2 = 3;
|
|
1898
|
-
var INITIAL_BACKOFF_MS = 1e3;
|
|
1899
2021
|
var DEFAULT_MAX_TURN_STEPS = 256;
|
|
1900
2022
|
function createExecutionLane() {
|
|
1901
2023
|
let tail2 = Promise.resolve();
|
|
@@ -1946,6 +2068,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1946
2068
|
}
|
|
1947
2069
|
}
|
|
1948
2070
|
const request = async () => {
|
|
2071
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1949
2072
|
if (signal.aborted) return { content: [], stopReason: "aborted" };
|
|
1950
2073
|
const budgetDecision = cfg.budget?.beforeRequest({
|
|
1951
2074
|
modelRef: cfg.modelRef,
|
|
@@ -1969,6 +2092,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1969
2092
|
const outcome = await (cfg.modelRequestLane?.run(request) ?? request());
|
|
1970
2093
|
if (!outcome) return messages;
|
|
1971
2094
|
messages.push({ role: "assistant", content: outcome.content });
|
|
2095
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1972
2096
|
if (outcome.stopReason === "aborted") {
|
|
1973
2097
|
hooks.onEvent({ type: "turn_end", stopReason: "aborted" });
|
|
1974
2098
|
return sanitizeHistory(messages);
|
|
@@ -1976,6 +2100,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1976
2100
|
if (outcome.stopReason === "tool_use") {
|
|
1977
2101
|
const results = await runToolCalls(cfg, outcome.content, hooks, signal);
|
|
1978
2102
|
if (results.length > 0) messages.push({ role: "user", content: results });
|
|
2103
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1979
2104
|
if (signal.aborted) {
|
|
1980
2105
|
hooks.onEvent({ type: "turn_end", stopReason: "aborted" });
|
|
1981
2106
|
return sanitizeHistory(messages);
|
|
@@ -2022,7 +2147,7 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2022
2147
|
let outputTokens = 0;
|
|
2023
2148
|
const startedAt = Date.now();
|
|
2024
2149
|
let firstTokenAt;
|
|
2025
|
-
for await (const event of streamWithRetry(cfg.provider, request,
|
|
2150
|
+
for await (const event of streamWithRetry(cfg.provider, request, signal)) {
|
|
2026
2151
|
switch (event.type) {
|
|
2027
2152
|
case "text_delta":
|
|
2028
2153
|
firstTokenAt ??= Date.now();
|
|
@@ -2044,6 +2169,13 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2044
2169
|
case "rate_limits":
|
|
2045
2170
|
hooks.onEvent({ type: "rate_limits", model: cfg.modelRef, limits: event.limits });
|
|
2046
2171
|
break;
|
|
2172
|
+
case "retrying":
|
|
2173
|
+
hooks.onEvent({
|
|
2174
|
+
type: "notice",
|
|
2175
|
+
level: "warn",
|
|
2176
|
+
text: `Rate limited; retrying the same request in ${Math.ceil(event.waitMs / 1e3)}s (attempt ${event.attempt}). The conversation keeps its progress.`
|
|
2177
|
+
});
|
|
2178
|
+
break;
|
|
2047
2179
|
case "tool_call":
|
|
2048
2180
|
break;
|
|
2049
2181
|
case "done":
|
|
@@ -2068,96 +2200,6 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2068
2200
|
`"${cfg.provider.id}" ended the stream without completing the turn \u2014 nothing was generated. The endpoint answered, but not with a usable ${cfg.provider.kind} response.`
|
|
2069
2201
|
);
|
|
2070
2202
|
}
|
|
2071
|
-
async function* streamWithRetry(provider, request, modelRef, signal) {
|
|
2072
|
-
let lastError;
|
|
2073
|
-
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
2074
|
-
if (signal.aborted) return;
|
|
2075
|
-
try {
|
|
2076
|
-
const stream = provider.stream(request)[Symbol.asyncIterator]();
|
|
2077
|
-
let cancel;
|
|
2078
|
-
let cancelTimer;
|
|
2079
|
-
const cancelled = new Promise((resolve3) => {
|
|
2080
|
-
cancel = () => {
|
|
2081
|
-
cancelTimer ??= setTimeout(() => resolve3({ done: true, value: void 0 }), 100);
|
|
2082
|
-
};
|
|
2083
|
-
signal.addEventListener("abort", cancel, { once: true });
|
|
2084
|
-
if (signal.aborted) cancel();
|
|
2085
|
-
});
|
|
2086
|
-
try {
|
|
2087
|
-
for (; ; ) {
|
|
2088
|
-
const next = await Promise.race([stream.next(), cancelled]);
|
|
2089
|
-
if (next.done) break;
|
|
2090
|
-
if (!signal.aborted || next.value.type === "usage" || next.value.type === "done") {
|
|
2091
|
-
yield next.value;
|
|
2092
|
-
}
|
|
2093
|
-
if (next.value.type === "done") break;
|
|
2094
|
-
}
|
|
2095
|
-
} finally {
|
|
2096
|
-
signal.removeEventListener("abort", cancel);
|
|
2097
|
-
if (cancelTimer !== void 0) clearTimeout(cancelTimer);
|
|
2098
|
-
void stream.return?.().catch(() => void 0);
|
|
2099
|
-
}
|
|
2100
|
-
return;
|
|
2101
|
-
} catch (error) {
|
|
2102
|
-
lastError = error;
|
|
2103
|
-
if (attempt >= MAX_RETRIES2) break;
|
|
2104
|
-
const waitMs = retryBackoffMs(error, attempt);
|
|
2105
|
-
if (waitMs > 0) {
|
|
2106
|
-
try {
|
|
2107
|
-
await sleep(waitMs, signal);
|
|
2108
|
-
} catch (error2) {
|
|
2109
|
-
if (signal.aborted) return;
|
|
2110
|
-
throw error2;
|
|
2111
|
-
}
|
|
2112
|
-
continue;
|
|
2113
|
-
}
|
|
2114
|
-
break;
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
if (lastError) throw lastError;
|
|
2118
|
-
}
|
|
2119
|
-
function retryBackoffMs(error, attempt) {
|
|
2120
|
-
if (!isRateLimitError(error)) return 0;
|
|
2121
|
-
const serverWait = retryAfterFromError(error);
|
|
2122
|
-
if (serverWait > 0) return Math.min(serverWait, 6e4);
|
|
2123
|
-
return Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, 3e4);
|
|
2124
|
-
}
|
|
2125
|
-
function isRateLimitError(error) {
|
|
2126
|
-
if (!(error instanceof ProviderError)) return false;
|
|
2127
|
-
return error.status === 429;
|
|
2128
|
-
}
|
|
2129
|
-
function retryAfterFromError(error) {
|
|
2130
|
-
const cause = error?.cause;
|
|
2131
|
-
const headers = cause?.headers;
|
|
2132
|
-
if (headers && typeof headers.get === "function") {
|
|
2133
|
-
const ms = Number(headers.get("retry-after-ms"));
|
|
2134
|
-
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
2135
|
-
const raw = headers.get("retry-after");
|
|
2136
|
-
if (raw) {
|
|
2137
|
-
const s = Number(raw);
|
|
2138
|
-
if (Number.isFinite(s) && s > 0) return s * 1e3;
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
return 0;
|
|
2142
|
-
}
|
|
2143
|
-
function sleep(ms, signal) {
|
|
2144
|
-
return new Promise((resolve3, reject) => {
|
|
2145
|
-
if (signal.aborted) {
|
|
2146
|
-
reject(signal.reason ?? new Error("Aborted"));
|
|
2147
|
-
return;
|
|
2148
|
-
}
|
|
2149
|
-
const finish = () => {
|
|
2150
|
-
signal.removeEventListener("abort", onAbort);
|
|
2151
|
-
resolve3();
|
|
2152
|
-
};
|
|
2153
|
-
const onAbort = () => {
|
|
2154
|
-
clearTimeout(id);
|
|
2155
|
-
reject(signal.reason ?? new Error("Aborted"));
|
|
2156
|
-
};
|
|
2157
|
-
const id = setTimeout(finish, ms);
|
|
2158
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2159
|
-
});
|
|
2160
|
-
}
|
|
2161
2203
|
var CHARS_PER_TOKEN = 4;
|
|
2162
2204
|
function estimateRequestTokens(cfg, messages) {
|
|
2163
2205
|
let characters3 = cfg.system.length;
|
|
@@ -2184,8 +2226,10 @@ async function runToolCalls(cfg, content, hooks, signal) {
|
|
|
2184
2226
|
for (const call of calls) if (call.name !== "task") out.push(await run(call));
|
|
2185
2227
|
return out;
|
|
2186
2228
|
})();
|
|
2187
|
-
const [settledOthers] = await Promise.
|
|
2188
|
-
|
|
2229
|
+
const [[settledOthers], settledTasks] = await Promise.all([
|
|
2230
|
+
Promise.allSettled([others]),
|
|
2231
|
+
Promise.allSettled(tasks)
|
|
2232
|
+
]);
|
|
2189
2233
|
for (const result of [settledOthers, ...settledTasks]) {
|
|
2190
2234
|
if (result.status === "rejected") throw result.reason;
|
|
2191
2235
|
}
|
|
@@ -2763,7 +2807,7 @@ import { readFile as readFile6, stat as stat4 } from "fs/promises";
|
|
|
2763
2807
|
import path7 from "path";
|
|
2764
2808
|
|
|
2765
2809
|
// src/tools/bash.ts
|
|
2766
|
-
import { spawn as
|
|
2810
|
+
import { spawn as spawn3 } from "child_process";
|
|
2767
2811
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
2768
2812
|
var DEFAULT_TIMEOUT = 12e4;
|
|
2769
2813
|
var MAX_TIMEOUT = 6e5;
|
|
@@ -2825,7 +2869,7 @@ var bashTool = {
|
|
|
2825
2869
|
});
|
|
2826
2870
|
}
|
|
2827
2871
|
return new Promise((settle) => {
|
|
2828
|
-
const child =
|
|
2872
|
+
const child = spawn3(command, {
|
|
2829
2873
|
shell: true,
|
|
2830
2874
|
cwd: ctx.cwd,
|
|
2831
2875
|
detached: process.platform !== "win32",
|
|
@@ -2865,31 +2909,15 @@ var bashTool = {
|
|
|
2865
2909
|
};
|
|
2866
2910
|
child.stdout.on("data", collector());
|
|
2867
2911
|
child.stderr.on("data", collector());
|
|
2868
|
-
const killTree2 = () => {
|
|
2869
|
-
if (child.pid == null) return;
|
|
2870
|
-
if (process.platform === "win32") {
|
|
2871
|
-
try {
|
|
2872
|
-
spawn2("taskkill", ["/pid", String(child.pid), "/t", "/f"], { stdio: "ignore" });
|
|
2873
|
-
} catch {
|
|
2874
|
-
child.kill("SIGKILL");
|
|
2875
|
-
}
|
|
2876
|
-
return;
|
|
2877
|
-
}
|
|
2878
|
-
try {
|
|
2879
|
-
process.kill(-child.pid, "SIGKILL");
|
|
2880
|
-
} catch {
|
|
2881
|
-
child.kill("SIGKILL");
|
|
2882
|
-
}
|
|
2883
|
-
};
|
|
2884
2912
|
let timedOut = false;
|
|
2885
2913
|
let aborted = false;
|
|
2886
2914
|
const timer = setTimeout(() => {
|
|
2887
2915
|
timedOut = true;
|
|
2888
|
-
|
|
2916
|
+
killTree(child);
|
|
2889
2917
|
}, timeout);
|
|
2890
2918
|
const onAbort = () => {
|
|
2891
2919
|
aborted = true;
|
|
2892
|
-
|
|
2920
|
+
killTree(child);
|
|
2893
2921
|
};
|
|
2894
2922
|
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
2895
2923
|
const finish = (result) => {
|
|
@@ -4032,26 +4060,31 @@ async function summarizeText(provider, model, maxTokens, signal, source, systemP
|
|
|
4032
4060
|
if (signal.aborted) throw signal.reason ?? new Error("Context compaction was cancelled.");
|
|
4033
4061
|
let summary2 = "";
|
|
4034
4062
|
let finalText2 = "";
|
|
4063
|
+
let stopReason;
|
|
4035
4064
|
let usage;
|
|
4036
4065
|
let rateLimits;
|
|
4037
|
-
const stream = provider
|
|
4066
|
+
const stream = streamWithRetry(provider, {
|
|
4038
4067
|
model,
|
|
4039
4068
|
system: systemPrompt,
|
|
4040
4069
|
messages: [{ role: "user", content: [{ type: "text", text: source }] }],
|
|
4041
4070
|
tools: [],
|
|
4042
4071
|
maxTokens: Math.max(1, Math.floor(Math.min(maxOutputTokens, maxTokens))),
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
});
|
|
4072
|
+
compaction: true
|
|
4073
|
+
}, signal);
|
|
4046
4074
|
for await (const event of stream) {
|
|
4047
4075
|
if (event.type === "text_delta") summary2 += event.text;
|
|
4048
4076
|
else if (event.type === "usage") usage = event.usage;
|
|
4049
4077
|
else if (event.type === "rate_limits") rateLimits = event.limits;
|
|
4050
4078
|
else if (event.type === "done") {
|
|
4079
|
+
stopReason = event.stopReason;
|
|
4051
4080
|
finalText2 = event.content.filter((block) => block.type === "text").map((block) => block.type === "text" ? block.text : "").join("");
|
|
4052
4081
|
}
|
|
4053
4082
|
}
|
|
4054
4083
|
if (signal.aborted) throw signal.reason ?? new Error("Context compaction was cancelled.");
|
|
4084
|
+
if (stopReason !== "end_turn") {
|
|
4085
|
+
throw new Error(`Context summary is incomplete (${stopReason ?? "stream ended without a final response"}); original history was kept.`);
|
|
4086
|
+
}
|
|
4087
|
+
if (!(finalText2 || summary2).trim()) throw new Error("The provider returned an empty context summary.");
|
|
4055
4088
|
return { text: (finalText2 || summary2).trim(), usage, rateLimits };
|
|
4056
4089
|
}
|
|
4057
4090
|
function mergeUsage(usages) {
|
|
@@ -4174,7 +4207,11 @@ function createSubagentRunner(makeConfig, tools) {
|
|
|
4174
4207
|
const permissionLane = createExecutionLane();
|
|
4175
4208
|
return {
|
|
4176
4209
|
async run(request) {
|
|
4177
|
-
if (request.signal.aborted)
|
|
4210
|
+
if (request.signal.aborted) {
|
|
4211
|
+
request.onComplete?.("cancelled", "Cancelled by the user.");
|
|
4212
|
+
return SUBAGENT_CANCELLED;
|
|
4213
|
+
}
|
|
4214
|
+
let stopReason = "";
|
|
4178
4215
|
let steps = 0;
|
|
4179
4216
|
let finalCall = false;
|
|
4180
4217
|
const executionLane = createExecutionLane();
|
|
@@ -4227,6 +4264,7 @@ Working directory: ${base.cwd}`
|
|
|
4227
4264
|
const signal = AbortSignal.any([request.signal, limit.signal]);
|
|
4228
4265
|
const hooks = {
|
|
4229
4266
|
onEvent(event) {
|
|
4267
|
+
if (event.type === "turn_end") stopReason = event.stopReason;
|
|
4230
4268
|
if (event.type === "turn_start") {
|
|
4231
4269
|
++steps;
|
|
4232
4270
|
if (steps > MAX_SUBAGENT_STEPS + 1) limit.abort();
|
|
@@ -4251,11 +4289,22 @@ Working directory: ${base.cwd}`
|
|
|
4251
4289
|
{ role: "user", content: [{ type: "text", text: request.prompt }] }
|
|
4252
4290
|
];
|
|
4253
4291
|
const text = finalText(await runTurn(cfg, history, hooks, signal));
|
|
4254
|
-
if (!limit.signal.aborted)
|
|
4255
|
-
|
|
4256
|
-
|
|
4292
|
+
if (request.signal.aborted || stopReason === "aborted" && !limit.signal.aborted) {
|
|
4293
|
+
request.onComplete?.("cancelled", "Cancelled before the task finished; any output is partial.");
|
|
4294
|
+
return text === SUBAGENT_NO_ANSWER ? SUBAGENT_CANCELLED : text;
|
|
4295
|
+
}
|
|
4296
|
+
if (limit.signal.aborted || finalCall && (stopReason === "tool_use" || text === SUBAGENT_NO_ANSWER)) {
|
|
4297
|
+
request.onComplete?.("incomplete", STEP_LIMIT_NOTE);
|
|
4298
|
+
return text === SUBAGENT_NO_ANSWER ? STEP_LIMIT_NOTE : `${text}
|
|
4257
4299
|
|
|
4258
4300
|
${STEP_LIMIT_NOTE}`;
|
|
4301
|
+
}
|
|
4302
|
+
if (text === SUBAGENT_NO_ANSWER || stopReason === "max_tokens" || stopReason === "refusal" || stopReason === "pause_turn") {
|
|
4303
|
+
request.onComplete?.("incomplete", text === SUBAGENT_NO_ANSWER ? SUBAGENT_NO_ANSWER : `Task stopped (${stopReason}); output may be partial.`);
|
|
4304
|
+
} else {
|
|
4305
|
+
request.onComplete?.("done");
|
|
4306
|
+
}
|
|
4307
|
+
return text;
|
|
4259
4308
|
}
|
|
4260
4309
|
};
|
|
4261
4310
|
}
|
|
@@ -4446,7 +4495,7 @@ function clip(value) {
|
|
|
4446
4495
|
// package.json
|
|
4447
4496
|
var package_default = {
|
|
4448
4497
|
name: "@kernelonpanic/kitcode",
|
|
4449
|
-
version: "1.4.
|
|
4498
|
+
version: "1.4.6",
|
|
4450
4499
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
4451
4500
|
type: "module",
|
|
4452
4501
|
license: "MIT",
|
|
@@ -4514,7 +4563,7 @@ var package_default = {
|
|
|
4514
4563
|
|
|
4515
4564
|
// src/version.ts
|
|
4516
4565
|
var KITCODE_VERSION = package_default.version;
|
|
4517
|
-
var KITCODE_COMMIT = true ? "
|
|
4566
|
+
var KITCODE_COMMIT = true ? "98093a63cea2a1e99b9477191baf72c1839c9c27" : "development";
|
|
4518
4567
|
function kitcodeUserAgent(sdkUserAgent2) {
|
|
4519
4568
|
return `KitCode/${KITCODE_VERSION} ${sdkUserAgent2}`.trim();
|
|
4520
4569
|
}
|
|
@@ -5160,12 +5209,12 @@ async function* streamOnce(client, providerId, apiKey2, req, thinkingOn) {
|
|
|
5160
5209
|
messages: toMessageParams(req.messages),
|
|
5161
5210
|
tools: toToolParams(req.tools)
|
|
5162
5211
|
};
|
|
5163
|
-
if (thinkingOn) {
|
|
5212
|
+
if (thinkingOn && !req.compaction) {
|
|
5164
5213
|
const thinking2 = thinkingParam(req.model, req.maxTokens);
|
|
5165
5214
|
if (thinking2) params.thinking = thinking2;
|
|
5166
5215
|
}
|
|
5167
5216
|
const effort = effectiveEffort("anthropic", req.model, req.effort, req.messages);
|
|
5168
|
-
if (effort) params.output_config = { effort };
|
|
5217
|
+
if (effort && !req.compaction) params.output_config = { effort };
|
|
5169
5218
|
let thinking = "";
|
|
5170
5219
|
let text = "";
|
|
5171
5220
|
let events = 0;
|
|
@@ -5431,8 +5480,8 @@ async function* streamOnce2(client, providerId, apiKey2, req, effort, thinkingOf
|
|
|
5431
5480
|
{
|
|
5432
5481
|
model: req.model,
|
|
5433
5482
|
[tokenField]: req.maxTokens,
|
|
5434
|
-
...effort ? { reasoning_effort: effort } : {},
|
|
5435
|
-
...thinkingOff,
|
|
5483
|
+
...effort && !req.compaction ? { reasoning_effort: effort } : {},
|
|
5484
|
+
...req.compaction ? {} : thinkingOff,
|
|
5436
5485
|
messages: toChatMessages(req.system, req.messages),
|
|
5437
5486
|
...req.tools.length > 0 ? { tools: toChatTools(req.tools) } : {},
|
|
5438
5487
|
stream: true,
|
|
@@ -7094,8 +7143,14 @@ function createTaskTool(runner) {
|
|
|
7094
7143
|
const subagentId = `sa${randomBytes3(4).toString("hex")}`;
|
|
7095
7144
|
const description = field(input, "description") || "subagent";
|
|
7096
7145
|
ctx.onEvent?.({ type: "subagent_start", id: subagentId, description });
|
|
7146
|
+
let status = "done";
|
|
7147
|
+
let reason;
|
|
7097
7148
|
try {
|
|
7098
|
-
|
|
7149
|
+
let content = await runner.run({
|
|
7150
|
+
onComplete: (outcome, detail) => {
|
|
7151
|
+
status = outcome;
|
|
7152
|
+
reason = detail;
|
|
7153
|
+
},
|
|
7099
7154
|
prompt: instruction,
|
|
7100
7155
|
description,
|
|
7101
7156
|
signal: ctx.signal,
|
|
@@ -7104,10 +7159,19 @@ function createTaskTool(runner) {
|
|
|
7104
7159
|
confirm: ctx.confirm,
|
|
7105
7160
|
requestPermission: ctx.requestPermission
|
|
7106
7161
|
});
|
|
7107
|
-
ctx.
|
|
7108
|
-
|
|
7162
|
+
if (ctx.signal.aborted) {
|
|
7163
|
+
status = "cancelled";
|
|
7164
|
+
reason = "Cancelled by the user; any output is partial.";
|
|
7165
|
+
}
|
|
7166
|
+
if (status !== "done" && reason) content = `${reason}
|
|
7167
|
+
|
|
7168
|
+
${content}`;
|
|
7169
|
+
ctx.onEvent?.({ type: "subagent_end", id: subagentId, result: content, status, reason });
|
|
7170
|
+
const isError = status !== "done";
|
|
7171
|
+
if (progress.length === 0) return { content, isError };
|
|
7109
7172
|
return {
|
|
7110
7173
|
content,
|
|
7174
|
+
isError,
|
|
7111
7175
|
display: {
|
|
7112
7176
|
kind: "list",
|
|
7113
7177
|
title: `task ${brief(field(input, "description"), 40)}`,
|
|
@@ -7115,9 +7179,11 @@ function createTaskTool(runner) {
|
|
|
7115
7179
|
}
|
|
7116
7180
|
};
|
|
7117
7181
|
} catch (error) {
|
|
7118
|
-
const
|
|
7119
|
-
|
|
7120
|
-
|
|
7182
|
+
const reason2 = error instanceof Error ? error.message : String(error);
|
|
7183
|
+
const cancelled = ctx.signal.aborted;
|
|
7184
|
+
const content = cancelled ? `The subagent was cancelled: ${reason2}` : `The subagent failed: ${reason2}`;
|
|
7185
|
+
ctx.onEvent?.({ type: "subagent_end", id: subagentId, result: content, status: cancelled ? "cancelled" : "error", reason: reason2 });
|
|
7186
|
+
return { content, isError: true };
|
|
7121
7187
|
}
|
|
7122
7188
|
}
|
|
7123
7189
|
};
|
|
@@ -7781,6 +7847,9 @@ async function boot(options) {
|
|
|
7781
7847
|
isSystemPromptEnabled: () => config.systemPromptEnabled && config.systemPrompt !== void 0 && config.systemPrompt.trim() !== "",
|
|
7782
7848
|
async setSystemPrompt(text) {
|
|
7783
7849
|
const trimmed = text.trim();
|
|
7850
|
+
if (trimmed.length > MAX_SYSTEM_PROMPT_CHARS) {
|
|
7851
|
+
throw new Error(`System prompt must be at most ${MAX_SYSTEM_PROMPT_CHARS.toLocaleString("en-US")} characters.`);
|
|
7852
|
+
}
|
|
7784
7853
|
await persistConfig((draft) => {
|
|
7785
7854
|
if (trimmed === "") delete draft.systemPrompt;
|
|
7786
7855
|
else draft.systemPrompt = trimmed;
|
|
@@ -8382,7 +8451,7 @@ function validateKey(value) {
|
|
|
8382
8451
|
import { render } from "ink";
|
|
8383
8452
|
|
|
8384
8453
|
// src/ui/App.tsx
|
|
8385
|
-
import { Box as Box14, Text as Text13, useApp, useInput as
|
|
8454
|
+
import { Box as Box14, Text as Text13, useApp, useInput as useInput3, useStdout as useStdout2 } from "ink";
|
|
8386
8455
|
import { useCallback, useEffect as useEffect4, useMemo as useMemo6, useRef as useRef5, useState as useState8 } from "react";
|
|
8387
8456
|
|
|
8388
8457
|
// src/ui/terminal-size.ts
|
|
@@ -8578,7 +8647,7 @@ var COMMANDS = [
|
|
|
8578
8647
|
{ name: "resume" },
|
|
8579
8648
|
{ name: "undo" },
|
|
8580
8649
|
{ name: "clear" },
|
|
8581
|
-
{ name: "subagents", ...BUSY_SAFE },
|
|
8650
|
+
{ name: "subagents", args: "[clear]", ...BUSY_SAFE },
|
|
8582
8651
|
{ name: "help", ...BUSY_SAFE },
|
|
8583
8652
|
{ name: "exit", ...BUSY_SAFE }
|
|
8584
8653
|
];
|
|
@@ -8865,6 +8934,13 @@ var en = {
|
|
|
8865
8934
|
subagentsTitle: "Open a sub-agent chat",
|
|
8866
8935
|
subagentRunning: "running",
|
|
8867
8936
|
subagentDone: "done",
|
|
8937
|
+
subagentCancelled: "cancelled",
|
|
8938
|
+
subagentError: "error",
|
|
8939
|
+
subagentIncomplete: "incomplete",
|
|
8940
|
+
subagentsCleared: "Finished subagents cleared from the list.",
|
|
8941
|
+
noFinishedSubagents: "No finished subagents to clear.",
|
|
8942
|
+
subagentsClear: "Clear finished subagents",
|
|
8943
|
+
confirmExit: "Press Ctrl+C again within 3 seconds to exit. Any other key cancels.",
|
|
8868
8944
|
subagentWaiting: "waiting for the first step\u2026",
|
|
8869
8945
|
subagentBack: "esc back",
|
|
8870
8946
|
attachUsage: "Usage: /attach <image-or-text-file> \xB7 /attach clipboard \xB7 /attach clear",
|
|
@@ -8950,6 +9026,7 @@ ${url}`,
|
|
|
8950
9026
|
systemPromptCleared: "System prompt addition cleared.",
|
|
8951
9027
|
systemPromptEnabled: "System prompt enabled.",
|
|
8952
9028
|
systemPromptDisabled: "System prompt disabled (kept in config).",
|
|
9029
|
+
systemPromptTooLong: (max) => `System prompt must be at most ${max} characters.`,
|
|
8953
9030
|
stepsCurrent: (n) => n === 0 ? "Model calls per turn: unlimited" : `Model calls per turn: ${n}`,
|
|
8954
9031
|
stepsSet: (n) => `Model calls per turn set to ${n}`,
|
|
8955
9032
|
stepsUnlimited: "Model calls per turn: unlimited (0). The turn budget still applies.",
|
|
@@ -9115,6 +9192,13 @@ var ru = {
|
|
|
9115
9192
|
subagentsTitle: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 \u0441\u0443\u0431-\u0430\u0433\u0435\u043D\u0442\u0430",
|
|
9116
9193
|
subagentRunning: "\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442",
|
|
9117
9194
|
subagentDone: "\u0433\u043E\u0442\u043E\u0432\u043E",
|
|
9195
|
+
subagentCancelled: "\u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E",
|
|
9196
|
+
subagentError: "\u043E\u0448\u0438\u0431\u043A\u0430",
|
|
9197
|
+
subagentIncomplete: "\u043D\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E",
|
|
9198
|
+
subagentsCleared: "\u0417\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0435 \u0441\u0443\u0431\u0430\u0433\u0435\u043D\u0442\u044B \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430.",
|
|
9199
|
+
noFinishedSubagents: "\u041D\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0445 \u0441\u0443\u0431\u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0434\u043B\u044F \u043E\u0447\u0438\u0441\u0442\u043A\u0438.",
|
|
9200
|
+
subagentsClear: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0445 \u0441\u0443\u0431\u0430\u0433\u0435\u043D\u0442\u043E\u0432",
|
|
9201
|
+
confirmExit: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 Ctrl+C \u0435\u0449\u0451 \u0440\u0430\u0437 \u0432 \u0442\u0435\u0447\u0435\u043D\u0438\u0435 3 \u0441\u0435\u043A\u0443\u043D\u0434 \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430. \u041B\u044E\u0431\u0430\u044F \u0434\u0440\u0443\u0433\u0430\u044F \u043A\u043B\u0430\u0432\u0438\u0448\u0430 \u043E\u0442\u043C\u0435\u043D\u044F\u0435\u0442 \u0432\u044B\u0445\u043E\u0434.",
|
|
9118
9202
|
subagentWaiting: "\u0436\u0434\u0451\u043C \u043F\u0435\u0440\u0432\u044B\u0439 \u0448\u0430\u0433\u2026",
|
|
9119
9203
|
subagentBack: "esc \u2014 \u043D\u0430\u0437\u0430\u0434",
|
|
9120
9204
|
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",
|
|
@@ -9200,6 +9284,7 @@ ${url}`,
|
|
|
9200
9284
|
systemPromptCleared: "\u0414\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u043A \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0443\u0434\u0430\u043B\u0435\u043D\u043E.",
|
|
9201
9285
|
systemPromptEnabled: "\u0414\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u043A \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E.",
|
|
9202
9286
|
systemPromptDisabled: "\u0414\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u043A \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E (\u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0435).",
|
|
9287
|
+
systemPromptTooLong: (max) => `\u0414\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u043A \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0434\u043B\u0438\u043D\u043D\u0435\u0435 ${max} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432.`,
|
|
9203
9288
|
stepsCurrent: (n) => n === 0 ? "\u0412\u044B\u0437\u043E\u0432\u043E\u0432 \u043C\u043E\u0434\u0435\u043B\u0438 \u0437\u0430 \u0445\u043E\u0434: \u0431\u0435\u0437 \u043B\u0438\u043C\u0438\u0442\u0430" : `\u0412\u044B\u0437\u043E\u0432\u043E\u0432 \u043C\u043E\u0434\u0435\u043B\u0438 \u0437\u0430 \u0445\u043E\u0434: ${n}`,
|
|
9204
9289
|
stepsSet: (n) => `\u0412\u044B\u0437\u043E\u0432\u043E\u0432 \u043C\u043E\u0434\u0435\u043B\u0438 \u0437\u0430 \u0445\u043E\u0434 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E: ${n}`,
|
|
9205
9290
|
stepsUnlimited: "\u0412\u044B\u0437\u043E\u0432\u043E\u0432 \u043C\u043E\u0434\u0435\u043B\u0438 \u0437\u0430 \u0445\u043E\u0434: \u0431\u0435\u0437 \u043B\u0438\u043C\u0438\u0442\u0430 (0). \u0411\u044E\u0434\u0436\u0435\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0432\u0441\u0451 \u0435\u0449\u0451 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442.",
|
|
@@ -9293,6 +9378,7 @@ function Confirm({ title, body, danger, onAnswer }) {
|
|
|
9293
9378
|
const theme = useTheme();
|
|
9294
9379
|
const strings = useStrings();
|
|
9295
9380
|
useTerminalInput((char, key) => {
|
|
9381
|
+
if (key.ctrl || key.meta || key.super) return;
|
|
9296
9382
|
const c = normalizeHotkey(char);
|
|
9297
9383
|
if (c === "y") onAnswer(true);
|
|
9298
9384
|
else if (c === "n" || key.escape || key.return) onAnswer(false);
|
|
@@ -9675,6 +9761,7 @@ function PermissionPrompt({ request, onDecide }) {
|
|
|
9675
9761
|
setSelected(dangerous ? options.length - 1 : 0);
|
|
9676
9762
|
}, [request]);
|
|
9677
9763
|
useTerminalInput((char, key) => {
|
|
9764
|
+
if (key.ctrl || key.meta || key.super) return;
|
|
9678
9765
|
const c = normalizeHotkey(char);
|
|
9679
9766
|
if (key.upArrow) setSelected((index) => (index - 1 + options.length) % options.length);
|
|
9680
9767
|
else if (key.downArrow || key.tab) setSelected((index) => (index + 1) % options.length);
|
|
@@ -10455,7 +10542,25 @@ function ContextMeter({
|
|
|
10455
10542
|
}
|
|
10456
10543
|
|
|
10457
10544
|
// src/ui/components/SubagentChat.tsx
|
|
10458
|
-
import { Box as Box12, Text as Text12
|
|
10545
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
10546
|
+
|
|
10547
|
+
// src/ui/subagent-status.ts
|
|
10548
|
+
function subagentStatusLabel(state, strings) {
|
|
10549
|
+
switch (state) {
|
|
10550
|
+
case "running":
|
|
10551
|
+
return strings.subagentRunning;
|
|
10552
|
+
case "done":
|
|
10553
|
+
return strings.subagentDone;
|
|
10554
|
+
case "cancelled":
|
|
10555
|
+
return strings.subagentCancelled;
|
|
10556
|
+
case "error":
|
|
10557
|
+
return strings.subagentError;
|
|
10558
|
+
case "incomplete":
|
|
10559
|
+
return strings.subagentIncomplete;
|
|
10560
|
+
}
|
|
10561
|
+
}
|
|
10562
|
+
|
|
10563
|
+
// src/ui/components/SubagentChat.tsx
|
|
10459
10564
|
import { useState as useState7 } from "react";
|
|
10460
10565
|
|
|
10461
10566
|
// src/ui/components/Transcript.tsx
|
|
@@ -11356,8 +11461,11 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
11356
11461
|
function clipTextToRows(text, maxRows, columns) {
|
|
11357
11462
|
if (text === "" || maxRows <= 0) return "";
|
|
11358
11463
|
const width = Number.isFinite(columns) ? Math.max(1, Math.floor(columns)) : 80;
|
|
11359
|
-
const
|
|
11360
|
-
|
|
11464
|
+
const bound = (maxRows + 1) * width;
|
|
11465
|
+
const sliced = text.length > bound;
|
|
11466
|
+
const source = sliced ? text.slice(text.length - bound) : text;
|
|
11467
|
+
const rows = source.replace(/\r\n?/g, "\n").split("\n").flatMap((line) => wrapVisualLine(line, width));
|
|
11468
|
+
if (!sliced && rows.length <= maxRows) return rows.join("\n");
|
|
11361
11469
|
const tail2 = rows.slice(-Math.max(1, maxRows - 1));
|
|
11362
11470
|
return maxRows === 1 ? tail2.at(-1) ?? "" : ["\u2026", ...tail2].join("\n");
|
|
11363
11471
|
}
|
|
@@ -11460,6 +11568,13 @@ function previewLines(content) {
|
|
|
11460
11568
|
return [...lines.slice(0, 6), `\u2026 ${lines.length - 6} more lines`];
|
|
11461
11569
|
}
|
|
11462
11570
|
var VISIBLE_SUBAGENTS = 3;
|
|
11571
|
+
var PROGRESS_TAIL_CHARS = 4e3;
|
|
11572
|
+
function progressTail(text) {
|
|
11573
|
+
if (text.length <= PROGRESS_TAIL_CHARS) return text;
|
|
11574
|
+
const cut = text.slice(text.length - PROGRESS_TAIL_CHARS);
|
|
11575
|
+
const newline = cut.indexOf("\n");
|
|
11576
|
+
return newline === -1 ? cut : cut.slice(newline + 1);
|
|
11577
|
+
}
|
|
11463
11578
|
function SubagentSummary({ agents }) {
|
|
11464
11579
|
const theme = useTheme();
|
|
11465
11580
|
const running = agents.filter((agent) => agent.state === "running");
|
|
@@ -11468,12 +11583,14 @@ function SubagentSummary({ agents }) {
|
|
|
11468
11583
|
"Subagents: ",
|
|
11469
11584
|
running.length,
|
|
11470
11585
|
" running \xB7 ",
|
|
11471
|
-
agents.
|
|
11472
|
-
" done"
|
|
11586
|
+
agents.filter((agent) => agent.state === "done").length,
|
|
11587
|
+
" done / ",
|
|
11588
|
+
agents.filter((agent) => agent.state !== "running" && agent.state !== "done").length,
|
|
11589
|
+
" stopped"
|
|
11473
11590
|
] }),
|
|
11474
11591
|
running.slice(0, VISIBLE_SUBAGENTS).map((agent) => {
|
|
11475
11592
|
const latest = agent.bubbles.at(-1);
|
|
11476
|
-
const progress = latest?.kind === "tool" ? latest.summary : latest?.kind === "assistant" ? sanitizeThinkingText(latest.text || latest.thinking, latest.streaming) : "waiting";
|
|
11593
|
+
const progress = latest?.kind === "tool" ? latest.summary : latest?.kind === "assistant" ? sanitizeThinkingText(progressTail(latest.text || latest.thinking), latest.streaming) : "waiting";
|
|
11477
11594
|
return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, wrap: "truncate-end", children: [
|
|
11478
11595
|
agent.description,
|
|
11479
11596
|
" \xB7 ",
|
|
@@ -11485,23 +11602,28 @@ function SubagentSummary({ agents }) {
|
|
|
11485
11602
|
}
|
|
11486
11603
|
function SubagentView({ bubble, maxRows }) {
|
|
11487
11604
|
const theme = useTheme();
|
|
11605
|
+
const strings = useStrings();
|
|
11488
11606
|
const { columns } = useTerminalSize();
|
|
11489
11607
|
const mark = bubble.state === "running" ? "\u25CC" : "\u25CF";
|
|
11490
|
-
const color = bubble.state === "
|
|
11608
|
+
const color = bubble.state === "error" ? theme.error : bubble.state === "done" ? theme.ok : theme.warn;
|
|
11491
11609
|
const latest = bubble.bubbles.at(-1);
|
|
11492
|
-
const progress = latest?.kind === "assistant" ? sanitizeThinkingText(latest.text || latest.thinking, latest.streaming) : latest?.kind === "tool" ? latest.summary : "";
|
|
11610
|
+
const progress = latest?.kind === "assistant" ? sanitizeThinkingText(progressTail(latest.text || latest.thinking), latest.streaming) : latest?.kind === "tool" ? latest.summary : "";
|
|
11493
11611
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
11494
11612
|
/* @__PURE__ */ jsxs11(Text11, { color, bold: true, wrap: "truncate-end", children: [
|
|
11495
11613
|
mark,
|
|
11496
11614
|
" subagent: ",
|
|
11497
|
-
bubble.description
|
|
11615
|
+
bubble.description,
|
|
11616
|
+
" - ",
|
|
11617
|
+
subagentStatusLabel(bubble.state, strings)
|
|
11498
11618
|
] }),
|
|
11619
|
+
bubble.reason && /* @__PURE__ */ jsx11(Text11, { color: theme.warn, children: bubble.reason }),
|
|
11499
11620
|
/* @__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 })
|
|
11500
11621
|
] });
|
|
11501
11622
|
}
|
|
11502
11623
|
|
|
11503
11624
|
// src/ui/components/SubagentChat.tsx
|
|
11504
11625
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
11626
|
+
var MAX_SHOWN_BUBBLES = 24;
|
|
11505
11627
|
function SubagentChat({
|
|
11506
11628
|
bubble,
|
|
11507
11629
|
rows,
|
|
@@ -11511,7 +11633,7 @@ function SubagentChat({
|
|
|
11511
11633
|
const strings = useStrings();
|
|
11512
11634
|
const [offset, setOffset] = useState7(0);
|
|
11513
11635
|
const total = bubble?.bubbles.length ?? 0;
|
|
11514
|
-
|
|
11636
|
+
useTerminalInput((_input, key) => {
|
|
11515
11637
|
if (key.escape) onClose();
|
|
11516
11638
|
else if (key.upArrow) setOffset((value) => Math.min(Math.max(0, total - 1), value + 1));
|
|
11517
11639
|
else if (key.downArrow) setOffset((value) => Math.max(0, value - 1));
|
|
@@ -11523,18 +11645,19 @@ function SubagentChat({
|
|
|
11523
11645
|
] });
|
|
11524
11646
|
}
|
|
11525
11647
|
const running = bubble.state === "running";
|
|
11526
|
-
const shown = bubble.bubbles.slice(0, total - offset);
|
|
11527
|
-
const bodyRows = Math.max(3, rows - 2);
|
|
11648
|
+
const shown = bubble.bubbles.slice(Math.max(0, total - offset - MAX_SHOWN_BUBBLES), total - offset);
|
|
11649
|
+
const bodyRows = Math.max(3, rows - (bubble.reason ? 3 : 2));
|
|
11528
11650
|
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", minWidth: 0, children: [
|
|
11529
|
-
/* @__PURE__ */ jsxs12(Text12, { color: running ? theme.warn : theme.ok, bold: true, wrap: "truncate-end", children: [
|
|
11651
|
+
/* @__PURE__ */ jsxs12(Text12, { color: running || bubble.state === "cancelled" || bubble.state === "incomplete" ? theme.warn : bubble.state === "error" ? theme.error : theme.ok, bold: true, wrap: "truncate-end", children: [
|
|
11530
11652
|
running ? "\u25CC" : "\u25CF",
|
|
11531
11653
|
" ",
|
|
11532
11654
|
bubble.description,
|
|
11533
11655
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, bold: false, children: [
|
|
11534
11656
|
" \xB7 ",
|
|
11535
|
-
|
|
11657
|
+
subagentStatusLabel(bubble.state, strings)
|
|
11536
11658
|
] })
|
|
11537
11659
|
] }),
|
|
11660
|
+
bubble.reason && /* @__PURE__ */ jsx12(Text12, { color: theme.warn, wrap: "truncate-end", children: bubble.reason }),
|
|
11538
11661
|
/* @__PURE__ */ jsxs12(
|
|
11539
11662
|
Box12,
|
|
11540
11663
|
{
|
|
@@ -11686,7 +11809,7 @@ function applyEvent(state, event) {
|
|
|
11686
11809
|
return {
|
|
11687
11810
|
...state,
|
|
11688
11811
|
bubbles: state.bubbles.map(
|
|
11689
|
-
(bubble) => bubble.kind === "subagent" && bubble.id === event.id ? { ...bubble, state: "done", result: sanitizeTerminalText(event.result) } : bubble
|
|
11812
|
+
(bubble) => bubble.kind === "subagent" && bubble.id === event.id ? { ...bubble, state: event.status ?? "done", reason: event.reason ? sanitizeTerminalText(event.reason) : void 0, result: sanitizeTerminalText(event.result) } : bubble
|
|
11690
11813
|
)
|
|
11691
11814
|
};
|
|
11692
11815
|
case "subagent_event": {
|
|
@@ -11718,6 +11841,10 @@ function applyEvent(state, event) {
|
|
|
11718
11841
|
function applyEvents(state, events) {
|
|
11719
11842
|
return coalesceDeltas(events).reduce(applyEvent, state);
|
|
11720
11843
|
}
|
|
11844
|
+
function isStreamDelta(event) {
|
|
11845
|
+
if (event.type === "text_delta" || event.type === "thinking_delta") return true;
|
|
11846
|
+
return event.type === "subagent_event" && isStreamDelta(event.event);
|
|
11847
|
+
}
|
|
11721
11848
|
function coalesceDeltas(events) {
|
|
11722
11849
|
const out = [];
|
|
11723
11850
|
for (const event of events) {
|
|
@@ -11912,6 +12039,8 @@ function App({
|
|
|
11912
12039
|
const [, forceRender] = useState8(0);
|
|
11913
12040
|
const history = useRef5(initialHistory);
|
|
11914
12041
|
const abort = useRef5(null);
|
|
12042
|
+
const exitArmedAt = useRef5(null);
|
|
12043
|
+
const [hiddenSubagents, setHiddenSubagents] = useState8(() => /* @__PURE__ */ new Set());
|
|
11915
12044
|
const sessionStart = useRef5(Date.now());
|
|
11916
12045
|
const turns = useRef5(0);
|
|
11917
12046
|
const [turnStart, setTurnStart] = useState8(null);
|
|
@@ -11963,7 +12092,7 @@ function App({
|
|
|
11963
12092
|
(event) => {
|
|
11964
12093
|
recordSpeed(speedMeter.current, event);
|
|
11965
12094
|
transcriptEvents.current.push(event);
|
|
11966
|
-
if (event
|
|
12095
|
+
if (!isStreamDelta(event)) {
|
|
11967
12096
|
flushTranscriptEvents();
|
|
11968
12097
|
return;
|
|
11969
12098
|
}
|
|
@@ -12041,6 +12170,9 @@ function App({
|
|
|
12041
12170
|
turns.current += 1;
|
|
12042
12171
|
const hooks = {
|
|
12043
12172
|
onEvent: queueTranscriptEvent,
|
|
12173
|
+
onHistory: (messages) => {
|
|
12174
|
+
history.current = messages;
|
|
12175
|
+
},
|
|
12044
12176
|
requestPermission: (request) => new Promise((resolve3) => {
|
|
12045
12177
|
let settled = false;
|
|
12046
12178
|
const finish = (decision) => {
|
|
@@ -12074,10 +12206,12 @@ function App({
|
|
|
12074
12206
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
12075
12207
|
}
|
|
12076
12208
|
history.current = await runtime.run(history.current, hooks, controller.signal);
|
|
12077
|
-
void runtime.persist(history.current).catch((error) => notice("error", error instanceof Error ? error.message : String(error)));
|
|
12078
12209
|
} catch (error) {
|
|
12079
12210
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
12080
12211
|
} finally {
|
|
12212
|
+
await runtime.persist(history.current).catch(
|
|
12213
|
+
(error) => notice("error", error instanceof Error ? error.message : String(error))
|
|
12214
|
+
);
|
|
12081
12215
|
if (!controller.signal.aborted && Date.now() - startedAt >= BELL_AFTER_MS) bell();
|
|
12082
12216
|
flushTranscriptEvents();
|
|
12083
12217
|
abort.current = null;
|
|
@@ -12781,8 +12915,24 @@ ${strings.mcpAddUsage}`
|
|
|
12781
12915
|
return;
|
|
12782
12916
|
}
|
|
12783
12917
|
case "subagents": {
|
|
12918
|
+
flushTranscriptEvents();
|
|
12919
|
+
const clearFinished = () => {
|
|
12920
|
+
const finished = transcriptRef.current.bubbles.filter(
|
|
12921
|
+
(bubble) => bubble.kind === "subagent" && bubble.state !== "running" && !hiddenSubagents.has(bubble.id)
|
|
12922
|
+
);
|
|
12923
|
+
if (finished.length === 0) {
|
|
12924
|
+
notice("info", strings.noFinishedSubagents);
|
|
12925
|
+
return;
|
|
12926
|
+
}
|
|
12927
|
+
setHiddenSubagents((previous) => /* @__PURE__ */ new Set([...previous, ...finished.map((bubble) => bubble.id)]));
|
|
12928
|
+
notice("info", strings.subagentsCleared);
|
|
12929
|
+
};
|
|
12930
|
+
if (rawRest === "clear") {
|
|
12931
|
+
clearFinished();
|
|
12932
|
+
return;
|
|
12933
|
+
}
|
|
12784
12934
|
const agents = transcriptRef.current.bubbles.filter(
|
|
12785
|
-
(bubble) => bubble.kind === "subagent"
|
|
12935
|
+
(bubble) => bubble.kind === "subagent" && !hiddenSubagents.has(bubble.id)
|
|
12786
12936
|
);
|
|
12787
12937
|
if (agents.length === 0) {
|
|
12788
12938
|
notice("info", strings.noSubagents);
|
|
@@ -12795,13 +12945,14 @@ ${strings.mcpAddUsage}`
|
|
|
12795
12945
|
const running = ordered.filter((agent) => agent.state === "running");
|
|
12796
12946
|
const id = running.length === 1 && rawRest === "" ? running[0].id : await pick(
|
|
12797
12947
|
strings.subagentsTitle,
|
|
12798
|
-
ordered.slice(0, 20).map((agent) => ({
|
|
12948
|
+
[...ordered.slice(0, 20).map((agent) => ({
|
|
12799
12949
|
key: agent.id,
|
|
12800
12950
|
label: agent.description || "subagent",
|
|
12801
|
-
hint: agent.state
|
|
12802
|
-
}))
|
|
12951
|
+
hint: subagentStatusLabel(agent.state, strings)
|
|
12952
|
+
})), ...ordered.some((agent) => agent.state !== "running") ? [{ key: "__clear_finished__", label: strings.subagentsClear, hint: "/subagents clear" }] : []]
|
|
12803
12953
|
);
|
|
12804
|
-
if (id
|
|
12954
|
+
if (id === "__clear_finished__") clearFinished();
|
|
12955
|
+
else if (id) setOverlay({ kind: "subagent", id });
|
|
12805
12956
|
return;
|
|
12806
12957
|
}
|
|
12807
12958
|
case "checker":
|
|
@@ -12876,27 +13027,20 @@ ${strings.mcpAddUsage}`
|
|
|
12876
13027
|
}
|
|
12877
13028
|
case "systemp": {
|
|
12878
13029
|
const sub = rest[0]?.toLowerCase();
|
|
12879
|
-
if (sub === "set") {
|
|
12880
|
-
const
|
|
12881
|
-
if (!
|
|
12882
|
-
const typed = await promptInput(strings.systemPromptTitle);
|
|
12883
|
-
if (!typed || typed.trim() === "") return;
|
|
12884
|
-
await runtime.setSystemPrompt(typed);
|
|
12885
|
-
notice("info", strings.systemPromptSet);
|
|
12886
|
-
return;
|
|
12887
|
-
}
|
|
12888
|
-
await runtime.setSystemPrompt(text);
|
|
12889
|
-
notice("info", strings.systemPromptSet);
|
|
12890
|
-
return;
|
|
12891
|
-
}
|
|
12892
|
-
if (sub === "load") {
|
|
12893
|
-
const filePath = rawRest.replace(/^\S+\s*/, "").trim();
|
|
12894
|
-
if (!filePath) {
|
|
13030
|
+
if (sub === "set" || sub === "load") {
|
|
13031
|
+
const argument = rawRest.replace(/^\S+\s*/, "").trim();
|
|
13032
|
+
if (!argument && sub === "load") {
|
|
12895
13033
|
notice("warn", strings.systemPromptUsage);
|
|
12896
13034
|
return;
|
|
12897
13035
|
}
|
|
12898
13036
|
try {
|
|
12899
|
-
const text =
|
|
13037
|
+
const text = sub === "set" ? argument || // No inline text: fall back to a full-screen input prompt.
|
|
13038
|
+
(await promptInput(strings.systemPromptTitle) ?? "").trim() : await runtime.loadSystemPromptFile(argument);
|
|
13039
|
+
if (!text) return;
|
|
13040
|
+
if (text.length > MAX_SYSTEM_PROMPT_CHARS) {
|
|
13041
|
+
notice("error", strings.systemPromptTooLong(MAX_SYSTEM_PROMPT_CHARS.toLocaleString(lang ?? "en")));
|
|
13042
|
+
return;
|
|
13043
|
+
}
|
|
12900
13044
|
await runtime.setSystemPrompt(text);
|
|
12901
13045
|
notice("info", strings.systemPromptSet);
|
|
12902
13046
|
} catch (error) {
|
|
@@ -13147,7 +13291,7 @@ ${strings.mcpAddUsage}`
|
|
|
13147
13291
|
}
|
|
13148
13292
|
}
|
|
13149
13293
|
},
|
|
13150
|
-
[applyAccent, appendAttachment, ask2, exit, notice, pick, replaceAttachments, runtime, strings]
|
|
13294
|
+
[applyAccent, appendAttachment, ask2, exit, notice, pick, replaceAttachments, runtime, strings, hiddenSubagents, flushTranscriptEvents]
|
|
13151
13295
|
);
|
|
13152
13296
|
const runSlash = useCallback(
|
|
13153
13297
|
(line) => {
|
|
@@ -13316,6 +13460,7 @@ ${strings.mcpAddUsage}`
|
|
|
13316
13460
|
}
|
|
13317
13461
|
const text = raw.trim();
|
|
13318
13462
|
const submitSlash = () => {
|
|
13463
|
+
setPromptHistory((state) => appendInputHistory(state, text));
|
|
13319
13464
|
if (!detachedInput) setInput("");
|
|
13320
13465
|
const command = text.slice(1).trim().split(/\s+/)[0]?.toLowerCase();
|
|
13321
13466
|
if (busyRef.current && !commandRunsWhileBusy(command) || slashPendingRef.current > 0) {
|
|
@@ -13377,6 +13522,18 @@ ${strings.mcpAddUsage}`
|
|
|
13377
13522
|
[replaceAttachments, runAgent, runSlash, tryQueueAutomaticAttachment]
|
|
13378
13523
|
);
|
|
13379
13524
|
useTerminalInput((_char, key) => {
|
|
13525
|
+
if (key.ctrl && _char === "c") {
|
|
13526
|
+
const now = Date.now();
|
|
13527
|
+
if (exitArmedAt.current !== null && now - exitArmedAt.current < 3e3) {
|
|
13528
|
+
abort.current?.abort();
|
|
13529
|
+
exit();
|
|
13530
|
+
} else {
|
|
13531
|
+
exitArmedAt.current = now;
|
|
13532
|
+
notice("warn", strings.confirmExit);
|
|
13533
|
+
}
|
|
13534
|
+
return;
|
|
13535
|
+
}
|
|
13536
|
+
exitArmedAt.current = null;
|
|
13380
13537
|
if (key.tab && key.shift) {
|
|
13381
13538
|
const next = runtime.cycleMode();
|
|
13382
13539
|
notice("info", strings.modeChanged(strings.modeLabel[next] ?? next));
|
|
@@ -13384,7 +13541,7 @@ ${strings.mcpAddUsage}`
|
|
|
13384
13541
|
return;
|
|
13385
13542
|
}
|
|
13386
13543
|
if (!key.escape) return;
|
|
13387
|
-
if (overlay.kind
|
|
13544
|
+
if (overlay.kind !== "none") return;
|
|
13388
13545
|
if (checkedOut.current && overlay.kind === "none") {
|
|
13389
13546
|
if (busyRef.current || slashPendingRef.current > 0) {
|
|
13390
13547
|
putBackCheckedOut(input);
|
|
@@ -13410,21 +13567,6 @@ ${strings.mcpAddUsage}`
|
|
|
13410
13567
|
if (clipboardPasteTask.current) clipboardPasteTask.current = null;
|
|
13411
13568
|
return;
|
|
13412
13569
|
}
|
|
13413
|
-
if (overlay.kind === "permission") {
|
|
13414
|
-
overlay.resolve("deny");
|
|
13415
|
-
setOverlay({ kind: "none" });
|
|
13416
|
-
return;
|
|
13417
|
-
}
|
|
13418
|
-
if (overlay.kind === "confirm") {
|
|
13419
|
-
overlay.resolve(false);
|
|
13420
|
-
setOverlay({ kind: "none" });
|
|
13421
|
-
return;
|
|
13422
|
-
}
|
|
13423
|
-
if (overlay.kind === "picker") {
|
|
13424
|
-
overlay.select("");
|
|
13425
|
-
setOverlay({ kind: "none" });
|
|
13426
|
-
return;
|
|
13427
|
-
}
|
|
13428
13570
|
setInput("");
|
|
13429
13571
|
});
|
|
13430
13572
|
const shell = (children) => /* @__PURE__ */ jsx14(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx14(StringsContext.Provider, { value: strings, children }) });
|
|
@@ -13591,7 +13733,7 @@ function TextInputOverlay({
|
|
|
13591
13733
|
const submit = useCallback(() => {
|
|
13592
13734
|
onAnswer(value || null);
|
|
13593
13735
|
}, [value, onAnswer]);
|
|
13594
|
-
|
|
13736
|
+
useInput3((_input, key) => {
|
|
13595
13737
|
if (key.escape) onCancel();
|
|
13596
13738
|
});
|
|
13597
13739
|
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
@@ -13719,6 +13861,7 @@ var TUI_RENDER_OPTIONS = {
|
|
|
13719
13861
|
// twice). Standard rendering clears the whole frame each time — a little
|
|
13720
13862
|
// more work, but no stale lines.
|
|
13721
13863
|
incrementalRendering: false,
|
|
13864
|
+
exitOnCtrlC: false,
|
|
13722
13865
|
// Throttling defers writes; when a bubble closes into <Static> mid-stream,
|
|
13723
13866
|
// the deferred frame and the static write race and leave stale rows behind.
|
|
13724
13867
|
// Unthrottled renders cost a little CPU but keep every frame consistent.
|