@kernelonpanic/kitcode 1.4.3 → 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 +24 -3
- package/README.ru.md +24 -3
- package/dist/index.js +569 -225
- 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(),
|
|
@@ -81,6 +82,14 @@ var configSchema = z.object({
|
|
|
81
82
|
// Manual override for the model's context window. Unset means the window
|
|
82
83
|
// comes from the provider catalogue or the endpoint's model list.
|
|
83
84
|
contextWindow: z.number().int().min(1e3).max(1e7).optional(),
|
|
85
|
+
// Extra instructions appended to the built-in system prompt on every turn.
|
|
86
|
+
systemPrompt: z.string().max(MAX_SYSTEM_PROMPT_CHARS).optional(),
|
|
87
|
+
// When false, systemPrompt is kept in the config but not sent to the model.
|
|
88
|
+
systemPromptEnabled: z.boolean().default(true),
|
|
89
|
+
// Hard cap on model calls in one turn. Higher values let large refactors
|
|
90
|
+
// finish; 0 disables the cap entirely (use with care — a runaway loop will
|
|
91
|
+
// keep burning tokens until you press Esc).
|
|
92
|
+
maxTurnSteps: z.number().int().min(0).max(1e3).default(256),
|
|
84
93
|
maxTokens: z.number().int().positive().max(2e5).default(64e3),
|
|
85
94
|
budget: budgetSchema.default({
|
|
86
95
|
maxTokensPerTurn: 1e7,
|
|
@@ -1268,8 +1277,40 @@ function isSubsequence(needle, haystack) {
|
|
|
1268
1277
|
return needle.length === 0;
|
|
1269
1278
|
}
|
|
1270
1279
|
|
|
1271
|
-
// src/tools/
|
|
1280
|
+
// src/tools/kill-tree.ts
|
|
1272
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";
|
|
1273
1314
|
import { StringDecoder } from "string_decoder";
|
|
1274
1315
|
var MAX_JOBS = 8;
|
|
1275
1316
|
var MAX_JOB_OUTPUT = 1e5;
|
|
@@ -1285,7 +1326,7 @@ function startJob(command, cwd) {
|
|
|
1285
1326
|
installExitCleanup();
|
|
1286
1327
|
counter += 1;
|
|
1287
1328
|
const id = `bg-${counter}`;
|
|
1288
|
-
const child =
|
|
1329
|
+
const child = spawn2(command, {
|
|
1289
1330
|
shell: true,
|
|
1290
1331
|
cwd,
|
|
1291
1332
|
detached: process.platform !== "win32",
|
|
@@ -1391,22 +1432,6 @@ function summary(job) {
|
|
|
1391
1432
|
signal: job.signal
|
|
1392
1433
|
};
|
|
1393
1434
|
}
|
|
1394
|
-
function killTree(child) {
|
|
1395
|
-
if (child.pid == null) return;
|
|
1396
|
-
if (process.platform === "win32") {
|
|
1397
|
-
try {
|
|
1398
|
-
spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], { stdio: "ignore" });
|
|
1399
|
-
} catch {
|
|
1400
|
-
child.kill("SIGKILL");
|
|
1401
|
-
}
|
|
1402
|
-
return;
|
|
1403
|
-
}
|
|
1404
|
-
try {
|
|
1405
|
-
process.kill(-child.pid, "SIGKILL");
|
|
1406
|
-
} catch {
|
|
1407
|
-
child.kill("SIGKILL");
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
1435
|
|
|
1411
1436
|
// src/core/memory.ts
|
|
1412
1437
|
import { createHash } from "crypto";
|
|
@@ -1884,11 +1909,116 @@ function validSessionId(id) {
|
|
|
1884
1909
|
return /^[A-Za-z0-9_-]{1,240}$/.test(id);
|
|
1885
1910
|
}
|
|
1886
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
|
+
|
|
1887
2019
|
// src/core/agent.ts
|
|
1888
2020
|
var MAX_PAUSE_RESUMES = 5;
|
|
1889
|
-
var
|
|
1890
|
-
var INITIAL_BACKOFF_MS = 1e3;
|
|
1891
|
-
var MAX_TURN_STEPS = 64;
|
|
2021
|
+
var DEFAULT_MAX_TURN_STEPS = 256;
|
|
1892
2022
|
function createExecutionLane() {
|
|
1893
2023
|
let tail2 = Promise.resolve();
|
|
1894
2024
|
return {
|
|
@@ -1900,6 +2030,8 @@ function createExecutionLane() {
|
|
|
1900
2030
|
};
|
|
1901
2031
|
}
|
|
1902
2032
|
async function runTurn(cfg, history, hooks, signal) {
|
|
2033
|
+
const maxTurnSteps = cfg.maxTurnSteps ?? DEFAULT_MAX_TURN_STEPS;
|
|
2034
|
+
const hasStepCap = maxTurnSteps > 0;
|
|
1903
2035
|
const permissionLane = cfg.permissionLane ?? createExecutionLane();
|
|
1904
2036
|
const originalHooks = hooks;
|
|
1905
2037
|
hooks = {
|
|
@@ -1914,11 +2046,12 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1914
2046
|
hooks.onEvent({ type: "turn_end", stopReason: "aborted" });
|
|
1915
2047
|
return sanitizeHistory(messages);
|
|
1916
2048
|
}
|
|
1917
|
-
|
|
2049
|
+
steps += 1;
|
|
2050
|
+
if (hasStepCap && steps > maxTurnSteps) {
|
|
1918
2051
|
hooks.onEvent({
|
|
1919
2052
|
type: "notice",
|
|
1920
2053
|
level: "warn",
|
|
1921
|
-
text: `Stopped after ${
|
|
2054
|
+
text: `Stopped after ${maxTurnSteps} model calls without finishing, to avoid burning tokens in a loop. Ask again with a narrower request.`
|
|
1922
2055
|
});
|
|
1923
2056
|
hooks.onEvent({ type: "turn_end", stopReason: "max_tokens" });
|
|
1924
2057
|
return messages;
|
|
@@ -1935,6 +2068,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1935
2068
|
}
|
|
1936
2069
|
}
|
|
1937
2070
|
const request = async () => {
|
|
2071
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1938
2072
|
if (signal.aborted) return { content: [], stopReason: "aborted" };
|
|
1939
2073
|
const budgetDecision = cfg.budget?.beforeRequest({
|
|
1940
2074
|
modelRef: cfg.modelRef,
|
|
@@ -1958,6 +2092,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1958
2092
|
const outcome = await (cfg.modelRequestLane?.run(request) ?? request());
|
|
1959
2093
|
if (!outcome) return messages;
|
|
1960
2094
|
messages.push({ role: "assistant", content: outcome.content });
|
|
2095
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1961
2096
|
if (outcome.stopReason === "aborted") {
|
|
1962
2097
|
hooks.onEvent({ type: "turn_end", stopReason: "aborted" });
|
|
1963
2098
|
return sanitizeHistory(messages);
|
|
@@ -1965,6 +2100,7 @@ async function runTurn(cfg, history, hooks, signal) {
|
|
|
1965
2100
|
if (outcome.stopReason === "tool_use") {
|
|
1966
2101
|
const results = await runToolCalls(cfg, outcome.content, hooks, signal);
|
|
1967
2102
|
if (results.length > 0) messages.push({ role: "user", content: results });
|
|
2103
|
+
hooks.onHistory?.(sanitizeHistory(messages));
|
|
1968
2104
|
if (signal.aborted) {
|
|
1969
2105
|
hooks.onEvent({ type: "turn_end", stopReason: "aborted" });
|
|
1970
2106
|
return sanitizeHistory(messages);
|
|
@@ -2011,7 +2147,7 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2011
2147
|
let outputTokens = 0;
|
|
2012
2148
|
const startedAt = Date.now();
|
|
2013
2149
|
let firstTokenAt;
|
|
2014
|
-
for await (const event of streamWithRetry(cfg.provider, request,
|
|
2150
|
+
for await (const event of streamWithRetry(cfg.provider, request, signal)) {
|
|
2015
2151
|
switch (event.type) {
|
|
2016
2152
|
case "text_delta":
|
|
2017
2153
|
firstTokenAt ??= Date.now();
|
|
@@ -2033,6 +2169,13 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2033
2169
|
case "rate_limits":
|
|
2034
2170
|
hooks.onEvent({ type: "rate_limits", model: cfg.modelRef, limits: event.limits });
|
|
2035
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;
|
|
2036
2179
|
case "tool_call":
|
|
2037
2180
|
break;
|
|
2038
2181
|
case "done":
|
|
@@ -2057,96 +2200,6 @@ async function consumeStream(cfg, messages, hooks, signal, maxTokens) {
|
|
|
2057
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.`
|
|
2058
2201
|
);
|
|
2059
2202
|
}
|
|
2060
|
-
async function* streamWithRetry(provider, request, modelRef, signal) {
|
|
2061
|
-
let lastError;
|
|
2062
|
-
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
2063
|
-
if (signal.aborted) return;
|
|
2064
|
-
try {
|
|
2065
|
-
const stream = provider.stream(request)[Symbol.asyncIterator]();
|
|
2066
|
-
let cancel;
|
|
2067
|
-
let cancelTimer;
|
|
2068
|
-
const cancelled = new Promise((resolve3) => {
|
|
2069
|
-
cancel = () => {
|
|
2070
|
-
cancelTimer ??= setTimeout(() => resolve3({ done: true, value: void 0 }), 100);
|
|
2071
|
-
};
|
|
2072
|
-
signal.addEventListener("abort", cancel, { once: true });
|
|
2073
|
-
if (signal.aborted) cancel();
|
|
2074
|
-
});
|
|
2075
|
-
try {
|
|
2076
|
-
for (; ; ) {
|
|
2077
|
-
const next = await Promise.race([stream.next(), cancelled]);
|
|
2078
|
-
if (next.done) break;
|
|
2079
|
-
if (!signal.aborted || next.value.type === "usage" || next.value.type === "done") {
|
|
2080
|
-
yield next.value;
|
|
2081
|
-
}
|
|
2082
|
-
if (next.value.type === "done") break;
|
|
2083
|
-
}
|
|
2084
|
-
} finally {
|
|
2085
|
-
signal.removeEventListener("abort", cancel);
|
|
2086
|
-
if (cancelTimer !== void 0) clearTimeout(cancelTimer);
|
|
2087
|
-
void stream.return?.().catch(() => void 0);
|
|
2088
|
-
}
|
|
2089
|
-
return;
|
|
2090
|
-
} catch (error) {
|
|
2091
|
-
lastError = error;
|
|
2092
|
-
if (attempt >= MAX_RETRIES2) break;
|
|
2093
|
-
const waitMs = retryBackoffMs(error, attempt);
|
|
2094
|
-
if (waitMs > 0) {
|
|
2095
|
-
try {
|
|
2096
|
-
await sleep(waitMs, signal);
|
|
2097
|
-
} catch (error2) {
|
|
2098
|
-
if (signal.aborted) return;
|
|
2099
|
-
throw error2;
|
|
2100
|
-
}
|
|
2101
|
-
continue;
|
|
2102
|
-
}
|
|
2103
|
-
break;
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
if (lastError) throw lastError;
|
|
2107
|
-
}
|
|
2108
|
-
function retryBackoffMs(error, attempt) {
|
|
2109
|
-
if (!isRateLimitError(error)) return 0;
|
|
2110
|
-
const serverWait = retryAfterFromError(error);
|
|
2111
|
-
if (serverWait > 0) return Math.min(serverWait, 6e4);
|
|
2112
|
-
return Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, 3e4);
|
|
2113
|
-
}
|
|
2114
|
-
function isRateLimitError(error) {
|
|
2115
|
-
if (!(error instanceof ProviderError)) return false;
|
|
2116
|
-
return error.status === 429;
|
|
2117
|
-
}
|
|
2118
|
-
function retryAfterFromError(error) {
|
|
2119
|
-
const cause = error?.cause;
|
|
2120
|
-
const headers = cause?.headers;
|
|
2121
|
-
if (headers && typeof headers.get === "function") {
|
|
2122
|
-
const ms = Number(headers.get("retry-after-ms"));
|
|
2123
|
-
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
2124
|
-
const raw = headers.get("retry-after");
|
|
2125
|
-
if (raw) {
|
|
2126
|
-
const s = Number(raw);
|
|
2127
|
-
if (Number.isFinite(s) && s > 0) return s * 1e3;
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
return 0;
|
|
2131
|
-
}
|
|
2132
|
-
function sleep(ms, signal) {
|
|
2133
|
-
return new Promise((resolve3, reject) => {
|
|
2134
|
-
if (signal.aborted) {
|
|
2135
|
-
reject(signal.reason ?? new Error("Aborted"));
|
|
2136
|
-
return;
|
|
2137
|
-
}
|
|
2138
|
-
const finish = () => {
|
|
2139
|
-
signal.removeEventListener("abort", onAbort);
|
|
2140
|
-
resolve3();
|
|
2141
|
-
};
|
|
2142
|
-
const onAbort = () => {
|
|
2143
|
-
clearTimeout(id);
|
|
2144
|
-
reject(signal.reason ?? new Error("Aborted"));
|
|
2145
|
-
};
|
|
2146
|
-
const id = setTimeout(finish, ms);
|
|
2147
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
2203
|
var CHARS_PER_TOKEN = 4;
|
|
2151
2204
|
function estimateRequestTokens(cfg, messages) {
|
|
2152
2205
|
let characters3 = cfg.system.length;
|
|
@@ -2173,8 +2226,10 @@ async function runToolCalls(cfg, content, hooks, signal) {
|
|
|
2173
2226
|
for (const call of calls) if (call.name !== "task") out.push(await run(call));
|
|
2174
2227
|
return out;
|
|
2175
2228
|
})();
|
|
2176
|
-
const [settledOthers] = await Promise.
|
|
2177
|
-
|
|
2229
|
+
const [[settledOthers], settledTasks] = await Promise.all([
|
|
2230
|
+
Promise.allSettled([others]),
|
|
2231
|
+
Promise.allSettled(tasks)
|
|
2232
|
+
]);
|
|
2178
2233
|
for (const result of [settledOthers, ...settledTasks]) {
|
|
2179
2234
|
if (result.status === "rejected") throw result.reason;
|
|
2180
2235
|
}
|
|
@@ -2752,7 +2807,7 @@ import { readFile as readFile6, stat as stat4 } from "fs/promises";
|
|
|
2752
2807
|
import path7 from "path";
|
|
2753
2808
|
|
|
2754
2809
|
// src/tools/bash.ts
|
|
2755
|
-
import { spawn as
|
|
2810
|
+
import { spawn as spawn3 } from "child_process";
|
|
2756
2811
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
2757
2812
|
var DEFAULT_TIMEOUT = 12e4;
|
|
2758
2813
|
var MAX_TIMEOUT = 6e5;
|
|
@@ -2814,7 +2869,7 @@ var bashTool = {
|
|
|
2814
2869
|
});
|
|
2815
2870
|
}
|
|
2816
2871
|
return new Promise((settle) => {
|
|
2817
|
-
const child =
|
|
2872
|
+
const child = spawn3(command, {
|
|
2818
2873
|
shell: true,
|
|
2819
2874
|
cwd: ctx.cwd,
|
|
2820
2875
|
detached: process.platform !== "win32",
|
|
@@ -2854,31 +2909,15 @@ var bashTool = {
|
|
|
2854
2909
|
};
|
|
2855
2910
|
child.stdout.on("data", collector());
|
|
2856
2911
|
child.stderr.on("data", collector());
|
|
2857
|
-
const killTree2 = () => {
|
|
2858
|
-
if (child.pid == null) return;
|
|
2859
|
-
if (process.platform === "win32") {
|
|
2860
|
-
try {
|
|
2861
|
-
spawn2("taskkill", ["/pid", String(child.pid), "/t", "/f"], { stdio: "ignore" });
|
|
2862
|
-
} catch {
|
|
2863
|
-
child.kill("SIGKILL");
|
|
2864
|
-
}
|
|
2865
|
-
return;
|
|
2866
|
-
}
|
|
2867
|
-
try {
|
|
2868
|
-
process.kill(-child.pid, "SIGKILL");
|
|
2869
|
-
} catch {
|
|
2870
|
-
child.kill("SIGKILL");
|
|
2871
|
-
}
|
|
2872
|
-
};
|
|
2873
2912
|
let timedOut = false;
|
|
2874
2913
|
let aborted = false;
|
|
2875
2914
|
const timer = setTimeout(() => {
|
|
2876
2915
|
timedOut = true;
|
|
2877
|
-
|
|
2916
|
+
killTree(child);
|
|
2878
2917
|
}, timeout);
|
|
2879
2918
|
const onAbort = () => {
|
|
2880
2919
|
aborted = true;
|
|
2881
|
-
|
|
2920
|
+
killTree(child);
|
|
2882
2921
|
};
|
|
2883
2922
|
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
2884
2923
|
const finish = (result) => {
|
|
@@ -4021,26 +4060,31 @@ async function summarizeText(provider, model, maxTokens, signal, source, systemP
|
|
|
4021
4060
|
if (signal.aborted) throw signal.reason ?? new Error("Context compaction was cancelled.");
|
|
4022
4061
|
let summary2 = "";
|
|
4023
4062
|
let finalText2 = "";
|
|
4063
|
+
let stopReason;
|
|
4024
4064
|
let usage;
|
|
4025
4065
|
let rateLimits;
|
|
4026
|
-
const stream = provider
|
|
4066
|
+
const stream = streamWithRetry(provider, {
|
|
4027
4067
|
model,
|
|
4028
4068
|
system: systemPrompt,
|
|
4029
4069
|
messages: [{ role: "user", content: [{ type: "text", text: source }] }],
|
|
4030
4070
|
tools: [],
|
|
4031
4071
|
maxTokens: Math.max(1, Math.floor(Math.min(maxOutputTokens, maxTokens))),
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
});
|
|
4072
|
+
compaction: true
|
|
4073
|
+
}, signal);
|
|
4035
4074
|
for await (const event of stream) {
|
|
4036
4075
|
if (event.type === "text_delta") summary2 += event.text;
|
|
4037
4076
|
else if (event.type === "usage") usage = event.usage;
|
|
4038
4077
|
else if (event.type === "rate_limits") rateLimits = event.limits;
|
|
4039
4078
|
else if (event.type === "done") {
|
|
4079
|
+
stopReason = event.stopReason;
|
|
4040
4080
|
finalText2 = event.content.filter((block) => block.type === "text").map((block) => block.type === "text" ? block.text : "").join("");
|
|
4041
4081
|
}
|
|
4042
4082
|
}
|
|
4043
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.");
|
|
4044
4088
|
return { text: (finalText2 || summary2).trim(), usage, rateLimits };
|
|
4045
4089
|
}
|
|
4046
4090
|
function mergeUsage(usages) {
|
|
@@ -4138,6 +4182,9 @@ function buildSystemPrompt(args) {
|
|
|
4138
4182
|
if (args.memory?.trim()) {
|
|
4139
4183
|
lines.push("", "Project notes saved in earlier conversations. Treat these as potentially stale context; verify claims against current files and follow current user instructions.", args.memory);
|
|
4140
4184
|
}
|
|
4185
|
+
if (args.systemPrompt?.trim()) {
|
|
4186
|
+
lines.push("", "Additional instructions from the user (apply alongside everything above):", args.systemPrompt.trim());
|
|
4187
|
+
}
|
|
4141
4188
|
return lines.join("\n");
|
|
4142
4189
|
}
|
|
4143
4190
|
|
|
@@ -4160,8 +4207,13 @@ function createSubagentRunner(makeConfig, tools) {
|
|
|
4160
4207
|
const permissionLane = createExecutionLane();
|
|
4161
4208
|
return {
|
|
4162
4209
|
async run(request) {
|
|
4163
|
-
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 = "";
|
|
4164
4215
|
let steps = 0;
|
|
4216
|
+
let finalCall = false;
|
|
4165
4217
|
const executionLane = createExecutionLane();
|
|
4166
4218
|
const modelRequestLane = createExecutionLane();
|
|
4167
4219
|
const base = makeConfig(SUBAGENT_SYSTEM, tools);
|
|
@@ -4175,11 +4227,12 @@ function createSubagentRunner(makeConfig, tools) {
|
|
|
4175
4227
|
kind: base.provider.kind,
|
|
4176
4228
|
listModels: () => base.provider.listModels(),
|
|
4177
4229
|
knownModels: () => base.provider.knownModels(),
|
|
4178
|
-
//
|
|
4230
|
+
// The final model call keeps the tool schemas (the history holds
|
|
4179
4231
|
// tool_use/tool_result blocks, and providers reject those without
|
|
4180
|
-
// tools
|
|
4181
|
-
//
|
|
4182
|
-
|
|
4232
|
+
// tools), but the system prompt forbids more calls and tools.get
|
|
4233
|
+
// returns a stopped stub: a call made anyway ends the turn with a
|
|
4234
|
+
// tool_result, instead of aborting mid-request and losing the text.
|
|
4235
|
+
stream: (chat) => base.provider.stream(finalCall ? {
|
|
4183
4236
|
...chat,
|
|
4184
4237
|
system: `${chat.system}
|
|
4185
4238
|
|
|
@@ -4187,7 +4240,20 @@ This is your last model call. Do not call tools. Return your findings now, clear
|
|
|
4187
4240
|
} : chat)
|
|
4188
4241
|
},
|
|
4189
4242
|
tools: {
|
|
4190
|
-
get: (name) =>
|
|
4243
|
+
get: (name) => {
|
|
4244
|
+
if (name === TASK_TOOL_NAME) return void 0;
|
|
4245
|
+
const tool = base.tools.get(name);
|
|
4246
|
+
if (!tool) return void 0;
|
|
4247
|
+
if (!finalCall) return tool;
|
|
4248
|
+
return {
|
|
4249
|
+
...tool,
|
|
4250
|
+
permission: () => "allow",
|
|
4251
|
+
execute: () => Promise.resolve({
|
|
4252
|
+
content: "Tool budget exhausted: this was your last model call. Write the final answer now from what you already gathered.",
|
|
4253
|
+
isError: true
|
|
4254
|
+
})
|
|
4255
|
+
};
|
|
4256
|
+
},
|
|
4191
4257
|
schemas: () => base.tools.schemas().filter((schema) => schema.name !== TASK_TOOL_NAME)
|
|
4192
4258
|
},
|
|
4193
4259
|
system: `${SUBAGENT_SYSTEM}
|
|
@@ -4198,7 +4264,12 @@ Working directory: ${base.cwd}`
|
|
|
4198
4264
|
const signal = AbortSignal.any([request.signal, limit.signal]);
|
|
4199
4265
|
const hooks = {
|
|
4200
4266
|
onEvent(event) {
|
|
4201
|
-
if (event.type === "
|
|
4267
|
+
if (event.type === "turn_end") stopReason = event.stopReason;
|
|
4268
|
+
if (event.type === "turn_start") {
|
|
4269
|
+
++steps;
|
|
4270
|
+
if (steps > MAX_SUBAGENT_STEPS + 1) limit.abort();
|
|
4271
|
+
else if (steps >= MAX_SUBAGENT_STEPS) finalCall = true;
|
|
4272
|
+
}
|
|
4202
4273
|
if (event.type === "tool_start") request.onProgress(brief(event.summary, 80));
|
|
4203
4274
|
if (event.type === "subagent_start" || event.type === "subagent_end" || event.type === "subagent_event") return;
|
|
4204
4275
|
request.onEvent?.(event);
|
|
@@ -4218,11 +4289,22 @@ Working directory: ${base.cwd}`
|
|
|
4218
4289
|
{ role: "user", content: [{ type: "text", text: request.prompt }] }
|
|
4219
4290
|
];
|
|
4220
4291
|
const text = finalText(await runTurn(cfg, history, hooks, signal));
|
|
4221
|
-
if (!limit.signal.aborted)
|
|
4222
|
-
|
|
4223
|
-
|
|
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}
|
|
4224
4299
|
|
|
4225
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;
|
|
4226
4308
|
}
|
|
4227
4309
|
};
|
|
4228
4310
|
}
|
|
@@ -4413,7 +4495,7 @@ function clip(value) {
|
|
|
4413
4495
|
// package.json
|
|
4414
4496
|
var package_default = {
|
|
4415
4497
|
name: "@kernelonpanic/kitcode",
|
|
4416
|
-
version: "1.4.
|
|
4498
|
+
version: "1.4.6",
|
|
4417
4499
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
4418
4500
|
type: "module",
|
|
4419
4501
|
license: "MIT",
|
|
@@ -4481,7 +4563,7 @@ var package_default = {
|
|
|
4481
4563
|
|
|
4482
4564
|
// src/version.ts
|
|
4483
4565
|
var KITCODE_VERSION = package_default.version;
|
|
4484
|
-
var KITCODE_COMMIT = true ? "
|
|
4566
|
+
var KITCODE_COMMIT = true ? "98093a63cea2a1e99b9477191baf72c1839c9c27" : "development";
|
|
4485
4567
|
function kitcodeUserAgent(sdkUserAgent2) {
|
|
4486
4568
|
return `KitCode/${KITCODE_VERSION} ${sdkUserAgent2}`.trim();
|
|
4487
4569
|
}
|
|
@@ -5127,12 +5209,12 @@ async function* streamOnce(client, providerId, apiKey2, req, thinkingOn) {
|
|
|
5127
5209
|
messages: toMessageParams(req.messages),
|
|
5128
5210
|
tools: toToolParams(req.tools)
|
|
5129
5211
|
};
|
|
5130
|
-
if (thinkingOn) {
|
|
5212
|
+
if (thinkingOn && !req.compaction) {
|
|
5131
5213
|
const thinking2 = thinkingParam(req.model, req.maxTokens);
|
|
5132
5214
|
if (thinking2) params.thinking = thinking2;
|
|
5133
5215
|
}
|
|
5134
5216
|
const effort = effectiveEffort("anthropic", req.model, req.effort, req.messages);
|
|
5135
|
-
if (effort) params.output_config = { effort };
|
|
5217
|
+
if (effort && !req.compaction) params.output_config = { effort };
|
|
5136
5218
|
let thinking = "";
|
|
5137
5219
|
let text = "";
|
|
5138
5220
|
let events = 0;
|
|
@@ -5398,8 +5480,8 @@ async function* streamOnce2(client, providerId, apiKey2, req, effort, thinkingOf
|
|
|
5398
5480
|
{
|
|
5399
5481
|
model: req.model,
|
|
5400
5482
|
[tokenField]: req.maxTokens,
|
|
5401
|
-
...effort ? { reasoning_effort: effort } : {},
|
|
5402
|
-
...thinkingOff,
|
|
5483
|
+
...effort && !req.compaction ? { reasoning_effort: effort } : {},
|
|
5484
|
+
...req.compaction ? {} : thinkingOff,
|
|
5403
5485
|
messages: toChatMessages(req.system, req.messages),
|
|
5404
5486
|
...req.tools.length > 0 ? { tools: toChatTools(req.tools) } : {},
|
|
5405
5487
|
stream: true,
|
|
@@ -7061,8 +7143,14 @@ function createTaskTool(runner) {
|
|
|
7061
7143
|
const subagentId = `sa${randomBytes3(4).toString("hex")}`;
|
|
7062
7144
|
const description = field(input, "description") || "subagent";
|
|
7063
7145
|
ctx.onEvent?.({ type: "subagent_start", id: subagentId, description });
|
|
7146
|
+
let status = "done";
|
|
7147
|
+
let reason;
|
|
7064
7148
|
try {
|
|
7065
|
-
|
|
7149
|
+
let content = await runner.run({
|
|
7150
|
+
onComplete: (outcome, detail) => {
|
|
7151
|
+
status = outcome;
|
|
7152
|
+
reason = detail;
|
|
7153
|
+
},
|
|
7066
7154
|
prompt: instruction,
|
|
7067
7155
|
description,
|
|
7068
7156
|
signal: ctx.signal,
|
|
@@ -7071,10 +7159,19 @@ function createTaskTool(runner) {
|
|
|
7071
7159
|
confirm: ctx.confirm,
|
|
7072
7160
|
requestPermission: ctx.requestPermission
|
|
7073
7161
|
});
|
|
7074
|
-
ctx.
|
|
7075
|
-
|
|
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 };
|
|
7076
7172
|
return {
|
|
7077
7173
|
content,
|
|
7174
|
+
isError,
|
|
7078
7175
|
display: {
|
|
7079
7176
|
kind: "list",
|
|
7080
7177
|
title: `task ${brief(field(input, "description"), 40)}`,
|
|
@@ -7082,9 +7179,11 @@ function createTaskTool(runner) {
|
|
|
7082
7179
|
}
|
|
7083
7180
|
};
|
|
7084
7181
|
} catch (error) {
|
|
7085
|
-
const
|
|
7086
|
-
|
|
7087
|
-
|
|
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 };
|
|
7088
7187
|
}
|
|
7089
7188
|
}
|
|
7090
7189
|
};
|
|
@@ -7292,7 +7391,8 @@ async function boot(options) {
|
|
|
7292
7391
|
cwd: options.cwd,
|
|
7293
7392
|
toolNames: tools.list().map((tool) => tool.name),
|
|
7294
7393
|
skills: skillCatalogue,
|
|
7295
|
-
memory: projectMemory
|
|
7394
|
+
memory: projectMemory,
|
|
7395
|
+
systemPrompt: config.systemPromptEnabled ? config.systemPrompt : void 0
|
|
7296
7396
|
});
|
|
7297
7397
|
let modelRef = session.model || config.model || "";
|
|
7298
7398
|
if (options.modelRef) modelRef = options.modelRef;
|
|
@@ -7362,6 +7462,7 @@ async function boot(options) {
|
|
|
7362
7462
|
maxTokens: config.maxTokens,
|
|
7363
7463
|
effort: config.effort,
|
|
7364
7464
|
thinking: config.thinking,
|
|
7465
|
+
maxTurnSteps: config.maxTurnSteps,
|
|
7365
7466
|
budget: activeBudget,
|
|
7366
7467
|
checkpoint: activeCheckpoint,
|
|
7367
7468
|
trimContext: (messages) => trimForWindow(resolved.provider.kind, messages)
|
|
@@ -7742,6 +7843,34 @@ async function boot(options) {
|
|
|
7742
7843
|
draft.lang = lang;
|
|
7743
7844
|
});
|
|
7744
7845
|
},
|
|
7846
|
+
getSystemPrompt: () => config.systemPrompt ?? "",
|
|
7847
|
+
isSystemPromptEnabled: () => config.systemPromptEnabled && config.systemPrompt !== void 0 && config.systemPrompt.trim() !== "",
|
|
7848
|
+
async setSystemPrompt(text) {
|
|
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
|
+
}
|
|
7853
|
+
await persistConfig((draft) => {
|
|
7854
|
+
if (trimmed === "") delete draft.systemPrompt;
|
|
7855
|
+
else draft.systemPrompt = trimmed;
|
|
7856
|
+
});
|
|
7857
|
+
},
|
|
7858
|
+
async setSystemPromptEnabled(enabled) {
|
|
7859
|
+
await persistConfig((draft) => {
|
|
7860
|
+
draft.systemPromptEnabled = enabled;
|
|
7861
|
+
});
|
|
7862
|
+
},
|
|
7863
|
+
async loadSystemPromptFile(requestedPath) {
|
|
7864
|
+
const { readFile: readFile15 } = await import("fs/promises");
|
|
7865
|
+
const resolved = path15.resolve(options.cwd, requestedPath);
|
|
7866
|
+
return readFile15(resolved, "utf8");
|
|
7867
|
+
},
|
|
7868
|
+
getMaxTurnSteps: () => config.maxTurnSteps,
|
|
7869
|
+
async setMaxTurnSteps(steps) {
|
|
7870
|
+
await persistConfig((draft) => {
|
|
7871
|
+
draft.maxTurnSteps = steps;
|
|
7872
|
+
});
|
|
7873
|
+
},
|
|
7745
7874
|
getAccent: () => config.theme.accent,
|
|
7746
7875
|
async setAccent(value) {
|
|
7747
7876
|
await persistConfig((draft) => {
|
|
@@ -8508,6 +8637,8 @@ var COMMANDS = [
|
|
|
8508
8637
|
{ name: "mcp", args: "[add|list|delete|enable|disable]" },
|
|
8509
8638
|
{ name: "attach", args: "<path|clipboard|clear>" },
|
|
8510
8639
|
{ name: "memory", args: "[show|set <text>|clear]" },
|
|
8640
|
+
{ name: "systemp", args: "[set <text>|clear]", ...BUSY_SAFE },
|
|
8641
|
+
{ name: "steps", args: "[count]", ...BUSY_SAFE },
|
|
8511
8642
|
{ name: "compact" },
|
|
8512
8643
|
{ name: "update" },
|
|
8513
8644
|
{ name: "checker", ...BUSY_SAFE },
|
|
@@ -8516,7 +8647,7 @@ var COMMANDS = [
|
|
|
8516
8647
|
{ name: "resume" },
|
|
8517
8648
|
{ name: "undo" },
|
|
8518
8649
|
{ name: "clear" },
|
|
8519
|
-
{ name: "subagents", ...BUSY_SAFE },
|
|
8650
|
+
{ name: "subagents", args: "[clear]", ...BUSY_SAFE },
|
|
8520
8651
|
{ name: "help", ...BUSY_SAFE },
|
|
8521
8652
|
{ name: "exit", ...BUSY_SAFE }
|
|
8522
8653
|
];
|
|
@@ -8803,6 +8934,13 @@ var en = {
|
|
|
8803
8934
|
subagentsTitle: "Open a sub-agent chat",
|
|
8804
8935
|
subagentRunning: "running",
|
|
8805
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.",
|
|
8806
8944
|
subagentWaiting: "waiting for the first step\u2026",
|
|
8807
8945
|
subagentBack: "esc back",
|
|
8808
8946
|
attachUsage: "Usage: /attach <image-or-text-file> \xB7 /attach clipboard \xB7 /attach clear",
|
|
@@ -8881,6 +9019,18 @@ ${url}`,
|
|
|
8881
9019
|
memoryEmpty: "Project memory is empty.",
|
|
8882
9020
|
memoryClearAsk: "Clear project memory?",
|
|
8883
9021
|
memoryReplaceAsk: "Replace all project memory notes with this text?",
|
|
9022
|
+
systemPromptTitle: "System prompt addition",
|
|
9023
|
+
systemPromptUsage: "/systemp [show] \xB7 /systemp set <text> \xB7 /systemp clear",
|
|
9024
|
+
systemPromptEmpty: "No extra system prompt. Set one with /systemp set <text>.",
|
|
9025
|
+
systemPromptSet: "System prompt addition saved.",
|
|
9026
|
+
systemPromptCleared: "System prompt addition cleared.",
|
|
9027
|
+
systemPromptEnabled: "System prompt enabled.",
|
|
9028
|
+
systemPromptDisabled: "System prompt disabled (kept in config).",
|
|
9029
|
+
systemPromptTooLong: (max) => `System prompt must be at most ${max} characters.`,
|
|
9030
|
+
stepsCurrent: (n) => n === 0 ? "Model calls per turn: unlimited" : `Model calls per turn: ${n}`,
|
|
9031
|
+
stepsSet: (n) => `Model calls per turn set to ${n}`,
|
|
9032
|
+
stepsUnlimited: "Model calls per turn: unlimited (0). The turn budget still applies.",
|
|
9033
|
+
stepsInvalid: "Usage: /steps <0-1000> (0 = unlimited)",
|
|
8884
9034
|
promptDeleted: (name) => `Deleted prompt "${name}"`,
|
|
8885
9035
|
promptNotFound: (name) => `Prompt "${name}" not found.`,
|
|
8886
9036
|
promptsEmpty: "No saved prompts. Use /prompt save <name>.",
|
|
@@ -8923,6 +9073,8 @@ ${url}`,
|
|
|
8923
9073
|
theme: "change the accent colour",
|
|
8924
9074
|
lang: "change the interface language",
|
|
8925
9075
|
memory: "view, replace or clear project memory",
|
|
9076
|
+
systemp: "view, set, load from file, or toggle the extra system prompt",
|
|
9077
|
+
steps: "cap model calls per turn",
|
|
8926
9078
|
prompt: "insert or save a prompt",
|
|
8927
9079
|
"prompt save": "save the current or recent text as a prompt",
|
|
8928
9080
|
"prompt delete": "delete a saved prompt",
|
|
@@ -9040,6 +9192,13 @@ var ru = {
|
|
|
9040
9192
|
subagentsTitle: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 \u0441\u0443\u0431-\u0430\u0433\u0435\u043D\u0442\u0430",
|
|
9041
9193
|
subagentRunning: "\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442",
|
|
9042
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.",
|
|
9043
9202
|
subagentWaiting: "\u0436\u0434\u0451\u043C \u043F\u0435\u0440\u0432\u044B\u0439 \u0448\u0430\u0433\u2026",
|
|
9044
9203
|
subagentBack: "esc \u2014 \u043D\u0430\u0437\u0430\u0434",
|
|
9045
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",
|
|
@@ -9118,6 +9277,18 @@ ${url}`,
|
|
|
9118
9277
|
memoryEmpty: "\u041F\u0430\u043C\u044F\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0443\u0441\u0442\u0430.",
|
|
9119
9278
|
memoryClearAsk: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430?",
|
|
9120
9279
|
memoryReplaceAsk: "\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u0430\u043C\u0435\u0442\u043A\u0438 \u043F\u0430\u043C\u044F\u0442\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u044D\u0442\u0438\u043C \u0442\u0435\u043A\u0441\u0442\u043E\u043C?",
|
|
9280
|
+
systemPromptTitle: "\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",
|
|
9281
|
+
systemPromptUsage: "/systemp [show] \xB7 /systemp set <\u0442\u0435\u043A\u0441\u0442> \xB7 /systemp clear",
|
|
9282
|
+
systemPromptEmpty: "\u0414\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u043A \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u043D\u0435\u0442. \u0417\u0430\u0434\u0430\u0439 \u0447\u0435\u0440\u0435\u0437 /systemp set <\u0442\u0435\u043A\u0441\u0442>.",
|
|
9283
|
+
systemPromptSet: "\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 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E.",
|
|
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.",
|
|
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.",
|
|
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.`,
|
|
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}`,
|
|
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}`,
|
|
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.",
|
|
9291
|
+
stepsInvalid: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /steps <0-1000> (0 = \u0431\u0435\u0437 \u043B\u0438\u043C\u0438\u0442\u0430)",
|
|
9121
9292
|
promptDeleted: (name) => `\u041F\u0440\u043E\u043C\u0442 \xAB${name}\xBB \u0443\u0434\u0430\u043B\u0451\u043D`,
|
|
9122
9293
|
promptNotFound: (name) => `\u041F\u0440\u043E\u043C\u0442 \xAB${name}\xBB \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.`,
|
|
9123
9294
|
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>.",
|
|
@@ -9160,6 +9331,8 @@ ${url}`,
|
|
|
9160
9331
|
theme: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u0446\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
|
|
9161
9332
|
lang: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u044F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430",
|
|
9162
9333
|
memory: "\u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0437\u0430\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u043B\u0438 \u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430",
|
|
9334
|
+
systemp: "\u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0437\u0430\u0434\u0430\u0442\u044C, \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u0438\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0434\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",
|
|
9335
|
+
steps: "\u043B\u0438\u043C\u0438\u0442 \u0432\u044B\u0437\u043E\u0432\u043E\u0432 \u043C\u043E\u0434\u0435\u043B\u0438 \u0437\u0430 \u0445\u043E\u0434",
|
|
9163
9336
|
prompt: "\u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u043B\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0440\u043E\u043C\u0442",
|
|
9164
9337
|
skills: "\u0441\u043F\u0438\u0441\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044B\u0445 \u0441\u043A\u0438\u043B\u043B\u043E\u0432",
|
|
9165
9338
|
"prompt delete": "\u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0439 \u043F\u0440\u043E\u043C\u0442",
|
|
@@ -9205,6 +9378,7 @@ function Confirm({ title, body, danger, onAnswer }) {
|
|
|
9205
9378
|
const theme = useTheme();
|
|
9206
9379
|
const strings = useStrings();
|
|
9207
9380
|
useTerminalInput((char, key) => {
|
|
9381
|
+
if (key.ctrl || key.meta || key.super) return;
|
|
9208
9382
|
const c = normalizeHotkey(char);
|
|
9209
9383
|
if (c === "y") onAnswer(true);
|
|
9210
9384
|
else if (c === "n" || key.escape || key.return) onAnswer(false);
|
|
@@ -9338,18 +9512,30 @@ function LanguagePicker({ onPick }) {
|
|
|
9338
9512
|
}
|
|
9339
9513
|
|
|
9340
9514
|
// src/ui/components/Onboarding.tsx
|
|
9341
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
9515
|
+
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
9342
9516
|
import Spinner from "ink-spinner";
|
|
9343
9517
|
import TextInput from "ink-text-input";
|
|
9344
9518
|
import { useState as useState2 } from "react";
|
|
9345
9519
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
9346
|
-
function Onboarding({
|
|
9520
|
+
function Onboarding({
|
|
9521
|
+
onSubmit,
|
|
9522
|
+
onCancel
|
|
9523
|
+
}) {
|
|
9347
9524
|
const theme = useTheme();
|
|
9348
9525
|
const strings = useStrings();
|
|
9349
9526
|
const [step, setStep] = useState2("url");
|
|
9350
9527
|
const [url, setUrl] = useState2("");
|
|
9351
9528
|
const [key, setKey] = useState2("");
|
|
9352
9529
|
const [error, setError] = useState2("");
|
|
9530
|
+
useInput2((_input, inputKey) => {
|
|
9531
|
+
if (!inputKey.escape || step === "working") return;
|
|
9532
|
+
if (step === "key") {
|
|
9533
|
+
setError("");
|
|
9534
|
+
setStep("url");
|
|
9535
|
+
} else {
|
|
9536
|
+
onCancel?.();
|
|
9537
|
+
}
|
|
9538
|
+
});
|
|
9353
9539
|
const submitKey = async (value) => {
|
|
9354
9540
|
if (value.trim() === "") return;
|
|
9355
9541
|
setStep("working");
|
|
@@ -9575,6 +9761,7 @@ function PermissionPrompt({ request, onDecide }) {
|
|
|
9575
9761
|
setSelected(dangerous ? options.length - 1 : 0);
|
|
9576
9762
|
}, [request]);
|
|
9577
9763
|
useTerminalInput((char, key) => {
|
|
9764
|
+
if (key.ctrl || key.meta || key.super) return;
|
|
9578
9765
|
const c = normalizeHotkey(char);
|
|
9579
9766
|
if (key.upArrow) setSelected((index) => (index - 1 + options.length) % options.length);
|
|
9580
9767
|
else if (key.downArrow || key.tab) setSelected((index) => (index + 1) % options.length);
|
|
@@ -10355,13 +10542,30 @@ function ContextMeter({
|
|
|
10355
10542
|
}
|
|
10356
10543
|
|
|
10357
10544
|
// src/ui/components/SubagentChat.tsx
|
|
10358
|
-
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
|
|
10359
10564
|
import { useState as useState7 } from "react";
|
|
10360
10565
|
|
|
10361
10566
|
// src/ui/components/Transcript.tsx
|
|
10362
10567
|
import { Box as Box11, Static, Text as Text11 } from "ink";
|
|
10363
10568
|
import { memo as memo2, useEffect as useEffect3, useMemo as useMemo5, useRef as useRef4, useState as useState6 } from "react";
|
|
10364
|
-
import Spinner2 from "ink-spinner";
|
|
10365
10569
|
import stringWidth4 from "string-width";
|
|
10366
10570
|
|
|
10367
10571
|
// src/ui/markdown.tsx
|
|
@@ -11251,17 +11455,17 @@ function AssistantView({ bubble: source, maxRows }) {
|
|
|
11251
11455
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
11252
11456
|
frameThinking !== "" && /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", maxHeight: thinkingBudget, overflowY: "hidden", children: /* @__PURE__ */ jsx11(Markdown, { dim: true, children: frameThinking }) }),
|
|
11253
11457
|
bubble.streaming ? /* @__PURE__ */ jsx11(Text11, { children: frameText }) : /* @__PURE__ */ jsx11(Markdown, { children: bubble.text }),
|
|
11254
|
-
bubble.streaming && bubble.text === "" && /* @__PURE__ */
|
|
11255
|
-
/* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
|
|
11256
|
-
" thinking"
|
|
11257
|
-
] })
|
|
11458
|
+
bubble.streaming && bubble.text === "" && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "thinking\u2026" })
|
|
11258
11459
|
] });
|
|
11259
11460
|
}
|
|
11260
11461
|
function clipTextToRows(text, maxRows, columns) {
|
|
11261
11462
|
if (text === "" || maxRows <= 0) return "";
|
|
11262
11463
|
const width = Number.isFinite(columns) ? Math.max(1, Math.floor(columns)) : 80;
|
|
11263
|
-
const
|
|
11264
|
-
|
|
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");
|
|
11265
11469
|
const tail2 = rows.slice(-Math.max(1, maxRows - 1));
|
|
11266
11470
|
return maxRows === 1 ? tail2.at(-1) ?? "" : ["\u2026", ...tail2].join("\n");
|
|
11267
11471
|
}
|
|
@@ -11364,6 +11568,13 @@ function previewLines(content) {
|
|
|
11364
11568
|
return [...lines.slice(0, 6), `\u2026 ${lines.length - 6} more lines`];
|
|
11365
11569
|
}
|
|
11366
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
|
+
}
|
|
11367
11578
|
function SubagentSummary({ agents }) {
|
|
11368
11579
|
const theme = useTheme();
|
|
11369
11580
|
const running = agents.filter((agent) => agent.state === "running");
|
|
@@ -11372,12 +11583,14 @@ function SubagentSummary({ agents }) {
|
|
|
11372
11583
|
"Subagents: ",
|
|
11373
11584
|
running.length,
|
|
11374
11585
|
" running \xB7 ",
|
|
11375
|
-
agents.
|
|
11376
|
-
" done"
|
|
11586
|
+
agents.filter((agent) => agent.state === "done").length,
|
|
11587
|
+
" done / ",
|
|
11588
|
+
agents.filter((agent) => agent.state !== "running" && agent.state !== "done").length,
|
|
11589
|
+
" stopped"
|
|
11377
11590
|
] }),
|
|
11378
11591
|
running.slice(0, VISIBLE_SUBAGENTS).map((agent) => {
|
|
11379
11592
|
const latest = agent.bubbles.at(-1);
|
|
11380
|
-
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";
|
|
11381
11594
|
return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, wrap: "truncate-end", children: [
|
|
11382
11595
|
agent.description,
|
|
11383
11596
|
" \xB7 ",
|
|
@@ -11389,23 +11602,28 @@ function SubagentSummary({ agents }) {
|
|
|
11389
11602
|
}
|
|
11390
11603
|
function SubagentView({ bubble, maxRows }) {
|
|
11391
11604
|
const theme = useTheme();
|
|
11605
|
+
const strings = useStrings();
|
|
11392
11606
|
const { columns } = useTerminalSize();
|
|
11393
11607
|
const mark = bubble.state === "running" ? "\u25CC" : "\u25CF";
|
|
11394
|
-
const color = bubble.state === "
|
|
11608
|
+
const color = bubble.state === "error" ? theme.error : bubble.state === "done" ? theme.ok : theme.warn;
|
|
11395
11609
|
const latest = bubble.bubbles.at(-1);
|
|
11396
|
-
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 : "";
|
|
11397
11611
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
|
|
11398
11612
|
/* @__PURE__ */ jsxs11(Text11, { color, bold: true, wrap: "truncate-end", children: [
|
|
11399
11613
|
mark,
|
|
11400
11614
|
" subagent: ",
|
|
11401
|
-
bubble.description
|
|
11615
|
+
bubble.description,
|
|
11616
|
+
" - ",
|
|
11617
|
+
subagentStatusLabel(bubble.state, strings)
|
|
11402
11618
|
] }),
|
|
11619
|
+
bubble.reason && /* @__PURE__ */ jsx11(Text11, { color: theme.warn, children: bubble.reason }),
|
|
11403
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 })
|
|
11404
11621
|
] });
|
|
11405
11622
|
}
|
|
11406
11623
|
|
|
11407
11624
|
// src/ui/components/SubagentChat.tsx
|
|
11408
11625
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
11626
|
+
var MAX_SHOWN_BUBBLES = 24;
|
|
11409
11627
|
function SubagentChat({
|
|
11410
11628
|
bubble,
|
|
11411
11629
|
rows,
|
|
@@ -11415,7 +11633,7 @@ function SubagentChat({
|
|
|
11415
11633
|
const strings = useStrings();
|
|
11416
11634
|
const [offset, setOffset] = useState7(0);
|
|
11417
11635
|
const total = bubble?.bubbles.length ?? 0;
|
|
11418
|
-
|
|
11636
|
+
useTerminalInput((_input, key) => {
|
|
11419
11637
|
if (key.escape) onClose();
|
|
11420
11638
|
else if (key.upArrow) setOffset((value) => Math.min(Math.max(0, total - 1), value + 1));
|
|
11421
11639
|
else if (key.downArrow) setOffset((value) => Math.max(0, value - 1));
|
|
@@ -11427,18 +11645,19 @@ function SubagentChat({
|
|
|
11427
11645
|
] });
|
|
11428
11646
|
}
|
|
11429
11647
|
const running = bubble.state === "running";
|
|
11430
|
-
const shown = bubble.bubbles.slice(0, total - offset);
|
|
11431
|
-
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));
|
|
11432
11650
|
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", minWidth: 0, children: [
|
|
11433
|
-
/* @__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: [
|
|
11434
11652
|
running ? "\u25CC" : "\u25CF",
|
|
11435
11653
|
" ",
|
|
11436
11654
|
bubble.description,
|
|
11437
11655
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, bold: false, children: [
|
|
11438
11656
|
" \xB7 ",
|
|
11439
|
-
|
|
11657
|
+
subagentStatusLabel(bubble.state, strings)
|
|
11440
11658
|
] })
|
|
11441
11659
|
] }),
|
|
11660
|
+
bubble.reason && /* @__PURE__ */ jsx12(Text12, { color: theme.warn, wrap: "truncate-end", children: bubble.reason }),
|
|
11442
11661
|
/* @__PURE__ */ jsxs12(
|
|
11443
11662
|
Box12,
|
|
11444
11663
|
{
|
|
@@ -11464,25 +11683,44 @@ function SubagentChat({
|
|
|
11464
11683
|
|
|
11465
11684
|
// src/ui/components/TerminalViewport.tsx
|
|
11466
11685
|
import { Box as Box13 } from "ink";
|
|
11467
|
-
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
11468
|
-
var
|
|
11686
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
11687
|
+
var CHROME_ROWS = 16;
|
|
11469
11688
|
var MAX_LIVE_ROWS = 11;
|
|
11470
11689
|
var MIN_LIVE_ROWS = 4;
|
|
11471
|
-
|
|
11690
|
+
var BOTTOM_SLACK_ROWS = 3;
|
|
11691
|
+
function liveTranscriptRows(rows, chromeRows) {
|
|
11472
11692
|
if (!Number.isFinite(rows)) return MAX_LIVE_ROWS;
|
|
11473
|
-
|
|
11693
|
+
const chrome = chromeRows ?? CHROME_ROWS;
|
|
11694
|
+
const budget = Math.floor(rows) - chrome - BOTTOM_SLACK_ROWS - 1;
|
|
11695
|
+
return Math.max(MIN_LIVE_ROWS, Math.min(MAX_LIVE_ROWS, budget));
|
|
11474
11696
|
}
|
|
11475
11697
|
function TerminalViewport({ children, rows }) {
|
|
11476
11698
|
const { columns } = useTerminalSize();
|
|
11477
|
-
|
|
11699
|
+
const liveRows = liveTranscriptRows(rows);
|
|
11700
|
+
const items = Array.isArray(children) ? children : [children];
|
|
11701
|
+
const [transcript, ...chromeChildren] = items;
|
|
11702
|
+
return /* @__PURE__ */ jsxs13(
|
|
11478
11703
|
Box13,
|
|
11479
11704
|
{
|
|
11480
|
-
width: columns,
|
|
11705
|
+
width: columns - 1,
|
|
11481
11706
|
maxWidth: "100%",
|
|
11482
11707
|
flexDirection: "column",
|
|
11483
|
-
maxHeight:
|
|
11708
|
+
maxHeight: liveRows + CHROME_ROWS,
|
|
11484
11709
|
overflowY: "hidden",
|
|
11485
|
-
children
|
|
11710
|
+
children: [
|
|
11711
|
+
/* @__PURE__ */ jsx13(
|
|
11712
|
+
Box13,
|
|
11713
|
+
{
|
|
11714
|
+
flexDirection: "column",
|
|
11715
|
+
flexShrink: 1,
|
|
11716
|
+
minHeight: 0,
|
|
11717
|
+
maxHeight: liveRows,
|
|
11718
|
+
overflowY: "hidden",
|
|
11719
|
+
children: transcript
|
|
11720
|
+
}
|
|
11721
|
+
),
|
|
11722
|
+
/* @__PURE__ */ jsx13(Box13, { flexDirection: "column", flexShrink: 0, children: chromeChildren })
|
|
11723
|
+
]
|
|
11486
11724
|
}
|
|
11487
11725
|
);
|
|
11488
11726
|
}
|
|
@@ -11571,7 +11809,7 @@ function applyEvent(state, event) {
|
|
|
11571
11809
|
return {
|
|
11572
11810
|
...state,
|
|
11573
11811
|
bubbles: state.bubbles.map(
|
|
11574
|
-
(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
|
|
11575
11813
|
)
|
|
11576
11814
|
};
|
|
11577
11815
|
case "subagent_event": {
|
|
@@ -11603,6 +11841,10 @@ function applyEvent(state, event) {
|
|
|
11603
11841
|
function applyEvents(state, events) {
|
|
11604
11842
|
return coalesceDeltas(events).reduce(applyEvent, state);
|
|
11605
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
|
+
}
|
|
11606
11848
|
function coalesceDeltas(events) {
|
|
11607
11849
|
const out = [];
|
|
11608
11850
|
for (const event of events) {
|
|
@@ -11740,7 +11982,7 @@ function safeSummary(summarize, name, input) {
|
|
|
11740
11982
|
}
|
|
11741
11983
|
|
|
11742
11984
|
// src/ui/App.tsx
|
|
11743
|
-
import { jsx as jsx14, jsxs as
|
|
11985
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
11744
11986
|
var EFFORTS = ["auto", "low", "medium", "high", "xhigh", "max"];
|
|
11745
11987
|
var STREAM_FRAME_MS = 50;
|
|
11746
11988
|
var CUSTOM_ACCENT = "__custom__";
|
|
@@ -11797,6 +12039,8 @@ function App({
|
|
|
11797
12039
|
const [, forceRender] = useState8(0);
|
|
11798
12040
|
const history = useRef5(initialHistory);
|
|
11799
12041
|
const abort = useRef5(null);
|
|
12042
|
+
const exitArmedAt = useRef5(null);
|
|
12043
|
+
const [hiddenSubagents, setHiddenSubagents] = useState8(() => /* @__PURE__ */ new Set());
|
|
11800
12044
|
const sessionStart = useRef5(Date.now());
|
|
11801
12045
|
const turns = useRef5(0);
|
|
11802
12046
|
const [turnStart, setTurnStart] = useState8(null);
|
|
@@ -11848,7 +12092,7 @@ function App({
|
|
|
11848
12092
|
(event) => {
|
|
11849
12093
|
recordSpeed(speedMeter.current, event);
|
|
11850
12094
|
transcriptEvents.current.push(event);
|
|
11851
|
-
if (event
|
|
12095
|
+
if (!isStreamDelta(event)) {
|
|
11852
12096
|
flushTranscriptEvents();
|
|
11853
12097
|
return;
|
|
11854
12098
|
}
|
|
@@ -11926,6 +12170,9 @@ function App({
|
|
|
11926
12170
|
turns.current += 1;
|
|
11927
12171
|
const hooks = {
|
|
11928
12172
|
onEvent: queueTranscriptEvent,
|
|
12173
|
+
onHistory: (messages) => {
|
|
12174
|
+
history.current = messages;
|
|
12175
|
+
},
|
|
11929
12176
|
requestPermission: (request) => new Promise((resolve3) => {
|
|
11930
12177
|
let settled = false;
|
|
11931
12178
|
const finish = (decision) => {
|
|
@@ -11959,10 +12206,12 @@ function App({
|
|
|
11959
12206
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
11960
12207
|
}
|
|
11961
12208
|
history.current = await runtime.run(history.current, hooks, controller.signal);
|
|
11962
|
-
void runtime.persist(history.current).catch((error) => notice("error", error instanceof Error ? error.message : String(error)));
|
|
11963
12209
|
} catch (error) {
|
|
11964
12210
|
notice("error", error instanceof Error ? error.message : String(error));
|
|
11965
12211
|
} finally {
|
|
12212
|
+
await runtime.persist(history.current).catch(
|
|
12213
|
+
(error) => notice("error", error instanceof Error ? error.message : String(error))
|
|
12214
|
+
);
|
|
11966
12215
|
if (!controller.signal.aborted && Date.now() - startedAt >= BELL_AFTER_MS) bell();
|
|
11967
12216
|
flushTranscriptEvents();
|
|
11968
12217
|
abort.current = null;
|
|
@@ -12022,6 +12271,7 @@ function App({
|
|
|
12022
12271
|
});
|
|
12023
12272
|
sessionStart.current = Date.now();
|
|
12024
12273
|
turns.current = 0;
|
|
12274
|
+
speedMeter.current = createSpeedMeter();
|
|
12025
12275
|
void runtime.persist([]).catch(
|
|
12026
12276
|
(error) => notice("error", error instanceof Error ? error.message : String(error))
|
|
12027
12277
|
);
|
|
@@ -12665,8 +12915,24 @@ ${strings.mcpAddUsage}`
|
|
|
12665
12915
|
return;
|
|
12666
12916
|
}
|
|
12667
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
|
+
}
|
|
12668
12934
|
const agents = transcriptRef.current.bubbles.filter(
|
|
12669
|
-
(bubble) => bubble.kind === "subagent"
|
|
12935
|
+
(bubble) => bubble.kind === "subagent" && !hiddenSubagents.has(bubble.id)
|
|
12670
12936
|
);
|
|
12671
12937
|
if (agents.length === 0) {
|
|
12672
12938
|
notice("info", strings.noSubagents);
|
|
@@ -12679,13 +12945,14 @@ ${strings.mcpAddUsage}`
|
|
|
12679
12945
|
const running = ordered.filter((agent) => agent.state === "running");
|
|
12680
12946
|
const id = running.length === 1 && rawRest === "" ? running[0].id : await pick(
|
|
12681
12947
|
strings.subagentsTitle,
|
|
12682
|
-
ordered.slice(0, 20).map((agent) => ({
|
|
12948
|
+
[...ordered.slice(0, 20).map((agent) => ({
|
|
12683
12949
|
key: agent.id,
|
|
12684
12950
|
label: agent.description || "subagent",
|
|
12685
|
-
hint: agent.state
|
|
12686
|
-
}))
|
|
12951
|
+
hint: subagentStatusLabel(agent.state, strings)
|
|
12952
|
+
})), ...ordered.some((agent) => agent.state !== "running") ? [{ key: "__clear_finished__", label: strings.subagentsClear, hint: "/subagents clear" }] : []]
|
|
12687
12953
|
);
|
|
12688
|
-
if (id
|
|
12954
|
+
if (id === "__clear_finished__") clearFinished();
|
|
12955
|
+
else if (id) setOverlay({ kind: "subagent", id });
|
|
12689
12956
|
return;
|
|
12690
12957
|
}
|
|
12691
12958
|
case "checker":
|
|
@@ -12758,6 +13025,61 @@ ${strings.mcpAddUsage}`
|
|
|
12758
13025
|
notice("info", runtime.readMemory() || strings.memoryEmpty);
|
|
12759
13026
|
return;
|
|
12760
13027
|
}
|
|
13028
|
+
case "systemp": {
|
|
13029
|
+
const sub = rest[0]?.toLowerCase();
|
|
13030
|
+
if (sub === "set" || sub === "load") {
|
|
13031
|
+
const argument = rawRest.replace(/^\S+\s*/, "").trim();
|
|
13032
|
+
if (!argument && sub === "load") {
|
|
13033
|
+
notice("warn", strings.systemPromptUsage);
|
|
13034
|
+
return;
|
|
13035
|
+
}
|
|
13036
|
+
try {
|
|
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
|
+
}
|
|
13044
|
+
await runtime.setSystemPrompt(text);
|
|
13045
|
+
notice("info", strings.systemPromptSet);
|
|
13046
|
+
} catch (error) {
|
|
13047
|
+
notice("error", error instanceof Error ? error.message : String(error));
|
|
13048
|
+
}
|
|
13049
|
+
return;
|
|
13050
|
+
}
|
|
13051
|
+
if (sub === "clear") {
|
|
13052
|
+
if (!runtime.getSystemPrompt()) {
|
|
13053
|
+
notice("info", strings.systemPromptEmpty);
|
|
13054
|
+
return;
|
|
13055
|
+
}
|
|
13056
|
+
await runtime.setSystemPrompt("");
|
|
13057
|
+
notice("info", strings.systemPromptCleared);
|
|
13058
|
+
return;
|
|
13059
|
+
}
|
|
13060
|
+
if (sub === "on") {
|
|
13061
|
+
await runtime.setSystemPromptEnabled(true);
|
|
13062
|
+
notice("info", strings.systemPromptEnabled);
|
|
13063
|
+
return;
|
|
13064
|
+
}
|
|
13065
|
+
if (sub === "off") {
|
|
13066
|
+
await runtime.setSystemPromptEnabled(false);
|
|
13067
|
+
notice("info", strings.systemPromptDisabled);
|
|
13068
|
+
return;
|
|
13069
|
+
}
|
|
13070
|
+
if (sub !== void 0 && sub !== "" && sub !== "show") {
|
|
13071
|
+
notice("warn", strings.systemPromptUsage);
|
|
13072
|
+
return;
|
|
13073
|
+
}
|
|
13074
|
+
const current = runtime.getSystemPrompt();
|
|
13075
|
+
if (!current) {
|
|
13076
|
+
notice("info", strings.systemPromptEmpty);
|
|
13077
|
+
return;
|
|
13078
|
+
}
|
|
13079
|
+
const enabled = runtime.isSystemPromptEnabled();
|
|
13080
|
+
notice("info", (enabled ? "" : "[disabled] ") + current);
|
|
13081
|
+
return;
|
|
13082
|
+
}
|
|
12761
13083
|
case "effort": {
|
|
12762
13084
|
notice("info", runtime.effortDescription(history.current));
|
|
12763
13085
|
const choice = EFFORTS.includes(rawRest) ? rawRest : await pick(
|
|
@@ -12770,6 +13092,21 @@ ${strings.mcpAddUsage}`
|
|
|
12770
13092
|
forceRender((n) => n + 1);
|
|
12771
13093
|
return;
|
|
12772
13094
|
}
|
|
13095
|
+
case "steps": {
|
|
13096
|
+
const current = runtime.getMaxTurnSteps();
|
|
13097
|
+
if (!rest[0]) {
|
|
13098
|
+
notice("info", strings.stepsCurrent(current));
|
|
13099
|
+
return;
|
|
13100
|
+
}
|
|
13101
|
+
const parsed = Number(rest[0]);
|
|
13102
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 1e3) {
|
|
13103
|
+
notice("warn", strings.stepsInvalid);
|
|
13104
|
+
return;
|
|
13105
|
+
}
|
|
13106
|
+
await runtime.setMaxTurnSteps(parsed);
|
|
13107
|
+
notice("info", parsed === 0 ? strings.stepsUnlimited : strings.stepsSet(parsed));
|
|
13108
|
+
return;
|
|
13109
|
+
}
|
|
12773
13110
|
case "budget": {
|
|
12774
13111
|
const current = runtime.getMaxTokensPerTurn();
|
|
12775
13112
|
if (rest[0]) {
|
|
@@ -12954,7 +13291,7 @@ ${strings.mcpAddUsage}`
|
|
|
12954
13291
|
}
|
|
12955
13292
|
}
|
|
12956
13293
|
},
|
|
12957
|
-
[applyAccent, appendAttachment, ask2, exit, notice, pick, replaceAttachments, runtime, strings]
|
|
13294
|
+
[applyAccent, appendAttachment, ask2, exit, notice, pick, replaceAttachments, runtime, strings, hiddenSubagents, flushTranscriptEvents]
|
|
12958
13295
|
);
|
|
12959
13296
|
const runSlash = useCallback(
|
|
12960
13297
|
(line) => {
|
|
@@ -13123,6 +13460,7 @@ ${strings.mcpAddUsage}`
|
|
|
13123
13460
|
}
|
|
13124
13461
|
const text = raw.trim();
|
|
13125
13462
|
const submitSlash = () => {
|
|
13463
|
+
setPromptHistory((state) => appendInputHistory(state, text));
|
|
13126
13464
|
if (!detachedInput) setInput("");
|
|
13127
13465
|
const command = text.slice(1).trim().split(/\s+/)[0]?.toLowerCase();
|
|
13128
13466
|
if (busyRef.current && !commandRunsWhileBusy(command) || slashPendingRef.current > 0) {
|
|
@@ -13184,6 +13522,18 @@ ${strings.mcpAddUsage}`
|
|
|
13184
13522
|
[replaceAttachments, runAgent, runSlash, tryQueueAutomaticAttachment]
|
|
13185
13523
|
);
|
|
13186
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;
|
|
13187
13537
|
if (key.tab && key.shift) {
|
|
13188
13538
|
const next = runtime.cycleMode();
|
|
13189
13539
|
notice("info", strings.modeChanged(strings.modeLabel[next] ?? next));
|
|
@@ -13191,7 +13541,7 @@ ${strings.mcpAddUsage}`
|
|
|
13191
13541
|
return;
|
|
13192
13542
|
}
|
|
13193
13543
|
if (!key.escape) return;
|
|
13194
|
-
if (overlay.kind
|
|
13544
|
+
if (overlay.kind !== "none") return;
|
|
13195
13545
|
if (checkedOut.current && overlay.kind === "none") {
|
|
13196
13546
|
if (busyRef.current || slashPendingRef.current > 0) {
|
|
13197
13547
|
putBackCheckedOut(input);
|
|
@@ -13217,21 +13567,6 @@ ${strings.mcpAddUsage}`
|
|
|
13217
13567
|
if (clipboardPasteTask.current) clipboardPasteTask.current = null;
|
|
13218
13568
|
return;
|
|
13219
13569
|
}
|
|
13220
|
-
if (overlay.kind === "permission") {
|
|
13221
|
-
overlay.resolve("deny");
|
|
13222
|
-
setOverlay({ kind: "none" });
|
|
13223
|
-
return;
|
|
13224
|
-
}
|
|
13225
|
-
if (overlay.kind === "confirm") {
|
|
13226
|
-
overlay.resolve(false);
|
|
13227
|
-
setOverlay({ kind: "none" });
|
|
13228
|
-
return;
|
|
13229
|
-
}
|
|
13230
|
-
if (overlay.kind === "picker") {
|
|
13231
|
-
overlay.select("");
|
|
13232
|
-
setOverlay({ kind: "none" });
|
|
13233
|
-
return;
|
|
13234
|
-
}
|
|
13235
13570
|
setInput("");
|
|
13236
13571
|
});
|
|
13237
13572
|
const shell = (children) => /* @__PURE__ */ jsx14(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx14(StringsContext.Provider, { value: strings, children }) });
|
|
@@ -13259,13 +13594,16 @@ ${strings.mcpAddUsage}`
|
|
|
13259
13594
|
notice("info", `${summary2}
|
|
13260
13595
|
${strings.configAt(runtime.configPath())}`);
|
|
13261
13596
|
return summary2;
|
|
13597
|
+
},
|
|
13598
|
+
onCancel: () => {
|
|
13599
|
+
if (!runtime.needsSetup()) setSetup(false);
|
|
13262
13600
|
}
|
|
13263
13601
|
}
|
|
13264
13602
|
)
|
|
13265
13603
|
);
|
|
13266
13604
|
}
|
|
13267
13605
|
return shell(
|
|
13268
|
-
/* @__PURE__ */
|
|
13606
|
+
/* @__PURE__ */ jsxs14(TerminalViewport, { rows, children: [
|
|
13269
13607
|
/* @__PURE__ */ jsx14(
|
|
13270
13608
|
Transcript,
|
|
13271
13609
|
{
|
|
@@ -13398,7 +13736,7 @@ function TextInputOverlay({
|
|
|
13398
13736
|
useInput3((_input, key) => {
|
|
13399
13737
|
if (key.escape) onCancel();
|
|
13400
13738
|
});
|
|
13401
|
-
return /* @__PURE__ */
|
|
13739
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
13402
13740
|
/* @__PURE__ */ jsx14(Text13, { dimColor: true, children: title }),
|
|
13403
13741
|
/* @__PURE__ */ jsx14(
|
|
13404
13742
|
TextInput2,
|
|
@@ -13518,10 +13856,16 @@ function clearTerminal() {
|
|
|
13518
13856
|
if (process.stdout.isTTY) process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
13519
13857
|
}
|
|
13520
13858
|
var TUI_RENDER_OPTIONS = {
|
|
13521
|
-
//
|
|
13522
|
-
//
|
|
13523
|
-
|
|
13524
|
-
|
|
13859
|
+
// Incremental rendering is off: Ink's incremental diff races with its own
|
|
13860
|
+
// <Static> commits and leaves stale rows behind (the prompt frame appears
|
|
13861
|
+
// twice). Standard rendering clears the whole frame each time — a little
|
|
13862
|
+
// more work, but no stale lines.
|
|
13863
|
+
incrementalRendering: false,
|
|
13864
|
+
exitOnCtrlC: false,
|
|
13865
|
+
// Throttling defers writes; when a bubble closes into <Static> mid-stream,
|
|
13866
|
+
// the deferred frame and the static write race and leave stale rows behind.
|
|
13867
|
+
// Unthrottled renders cost a little CPU but keep every frame consistent.
|
|
13868
|
+
maxFps: 0
|
|
13525
13869
|
};
|
|
13526
13870
|
async function startTui(options) {
|
|
13527
13871
|
const { runtime, history, warnings, shutdown } = await boot({
|