scream-code 0.16.2 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-V7s7I7Nr.mjs → app-C0OLgL_b.mjs} +177 -256
- package/dist/{dispatch-BRkUiHzo.mjs → dispatch-C7ujqj4p.mjs} +1 -1
- package/dist/{dispatch-D0mYcf1X.mjs → dispatch-La1sAZPF.mjs} +726 -533
- package/dist/main.mjs +1 -1
- package/dist/{text-input-dialog-CzJKBIoN.mjs → text-input-dialog--J3ngWFv.mjs} +36 -18
- package/dist/{text-input-dialog-Dp2TUSjQ.mjs → text-input-dialog-CNhL-3wm.mjs} +1 -1
- package/package.json +1 -1
|
@@ -4,9 +4,9 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
|
|
7
|
-
import { C as join$1, D as resolve$
|
|
7
|
+
import { C as join$1, D as resolve$2, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize$1, x as dirname$2, y as KnowledgeStore } from "./src-tDEINaMV.mjs";
|
|
8
8
|
import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
|
|
9
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog
|
|
9
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog--J3ngWFv.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
12
12
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -50779,7 +50779,7 @@ function canonicalizePath(path, cwd, pathClass = DEFAULT_PATH_CLASS) {
|
|
|
50779
50779
|
const normalizedPath = normalizeUserPath(path, pathClass);
|
|
50780
50780
|
if (pathClass === "win32" && isWin32DriveRelative(normalizedPath)) throw new PathSecurityError("PATH_INVALID", path, normalizedPath, `"${path}" is a drive-relative Windows path. Use an absolute path like C:\\path or a path relative to the working directory.`);
|
|
50781
50781
|
if (!isAbsolute$1(normalizedPath) && !isAbsolute$1(cwd)) throw new PathSecurityError("PATH_INVALID", path, normalizedPath, `Cannot resolve "${path}" against non-absolute cwd "${cwd}".`);
|
|
50782
|
-
return normalize$1(isAbsolute$1(normalizedPath) ? normalizedPath : resolve$
|
|
50782
|
+
return normalize$1(isAbsolute$1(normalizedPath) ? normalizedPath : resolve$2(cwd, normalizedPath));
|
|
50783
50783
|
}
|
|
50784
50784
|
/**
|
|
50785
50785
|
* True iff `candidate` is `base` itself or a descendant of it, compared
|
|
@@ -58452,7 +58452,7 @@ function dump$1(input, options) {
|
|
|
58452
58452
|
return "";
|
|
58453
58453
|
}
|
|
58454
58454
|
var dumper = { dump: dump$1 };
|
|
58455
|
-
var load = loader.load;
|
|
58455
|
+
var load$2 = loader.load;
|
|
58456
58456
|
loader.loadAll;
|
|
58457
58457
|
dumper.dump;
|
|
58458
58458
|
//#endregion
|
|
@@ -60001,7 +60001,7 @@ function parseFrontmatter(text) {
|
|
|
60001
60001
|
};
|
|
60002
60002
|
try {
|
|
60003
60003
|
return {
|
|
60004
|
-
data: load(yamlText) ?? {},
|
|
60004
|
+
data: load$2(yamlText) ?? {},
|
|
60005
60005
|
body
|
|
60006
60006
|
};
|
|
60007
60007
|
} catch (error) {
|
|
@@ -60025,7 +60025,7 @@ function parseSkillText(options) {
|
|
|
60025
60025
|
const name = nonEmptyString$1(metadata.name);
|
|
60026
60026
|
const description = nonEmptyString$1(metadata.description);
|
|
60027
60027
|
if (isDirectorySkill && (name === void 0 || description === void 0)) throw new SkillParseError(`Missing required frontmatter field ${name === void 0 ? "\"name\"" : "\"description\""} in ${options.skillMdPath}`);
|
|
60028
|
-
const skillPath = resolve$
|
|
60028
|
+
const skillPath = resolve$2(options.skillMdPath);
|
|
60029
60029
|
const content = parsed.body.trim();
|
|
60030
60030
|
return {
|
|
60031
60031
|
name: name ?? options.skillDirName,
|
|
@@ -60407,7 +60407,7 @@ async function defaultIsFile(p) {
|
|
|
60407
60407
|
}
|
|
60408
60408
|
}
|
|
60409
60409
|
async function findProjectRoot$2(workDir) {
|
|
60410
|
-
const start = resolve$
|
|
60410
|
+
const start = resolve$2(workDir);
|
|
60411
60411
|
let current = start;
|
|
60412
60412
|
while (true) {
|
|
60413
60413
|
if (await exists(join$1(current, ".git"))) return current;
|
|
@@ -60428,7 +60428,7 @@ function resolveConfiguredDir(dir, projectRoot, userHomeDir) {
|
|
|
60428
60428
|
if (dir === "~") return userHomeDir;
|
|
60429
60429
|
if (dir.startsWith("~/")) return join$1(userHomeDir, dir.slice(2));
|
|
60430
60430
|
if (isAbsolute$1(dir)) return dir;
|
|
60431
|
-
return resolve$
|
|
60431
|
+
return resolve$2(projectRoot, dir);
|
|
60432
60432
|
}
|
|
60433
60433
|
function isWithin$1(child, parent) {
|
|
60434
60434
|
const rel = relative$1(parent, child);
|
|
@@ -78140,7 +78140,7 @@ const McpJsonFileSchema = z.object({ mcpServers: z.record(z.string(), McpServerC
|
|
|
78140
78140
|
/** Maximum number of parent directories to walk when discovering mcp.json. */
|
|
78141
78141
|
const MAX_PARENT_WALK = 20;
|
|
78142
78142
|
function resolveMcpJsonPaths(input) {
|
|
78143
|
-
const cwd = resolve$
|
|
78143
|
+
const cwd = resolve$2(input.cwd);
|
|
78144
78144
|
return {
|
|
78145
78145
|
user: join$1(resolveScreamHome(input.homeDir), "mcp.json"),
|
|
78146
78146
|
project: join$1(cwd, ".scream-code", "mcp.json"),
|
|
@@ -99461,7 +99461,7 @@ function finalizeRawAgentProfileSource(content, profilePath, sources) {
|
|
|
99461
99461
|
function parseAgentProfileYaml(content, profilePath) {
|
|
99462
99462
|
let parsed;
|
|
99463
99463
|
try {
|
|
99464
|
-
parsed = load(content);
|
|
99464
|
+
parsed = load$2(content);
|
|
99465
99465
|
} catch (error) {
|
|
99466
99466
|
throw new Error(`Invalid agent profile YAML at ${profilePath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
99467
99467
|
}
|
|
@@ -108049,7 +108049,7 @@ var Session$1 = class {
|
|
|
108049
108049
|
parents: true,
|
|
108050
108050
|
existOk: true
|
|
108051
108051
|
});
|
|
108052
|
-
await this.options.jian.
|
|
108052
|
+
await this.options.jian.writeTextAtomic(this.metadataPath, text);
|
|
108053
108053
|
};
|
|
108054
108054
|
this.writeMetadataPromise = this.writeMetadataPromise.then(() => write(), () => write());
|
|
108055
108055
|
return this.writeMetadataPromise;
|
|
@@ -123105,7 +123105,7 @@ async function exportSessionDirectory(input) {
|
|
|
123105
123105
|
installSource: input.request.installSource,
|
|
123106
123106
|
shellEnv: input.request.shellEnv
|
|
123107
123107
|
});
|
|
123108
|
-
const outputPath = input.request.outputPath !== void 0 ? resolve$
|
|
123108
|
+
const outputPath = input.request.outputPath !== void 0 ? resolve$2(input.request.outputPath) : resolve$2(`${input.summary.id}.zip`);
|
|
123109
123109
|
return {
|
|
123110
123110
|
zipPath: outputPath,
|
|
123111
123111
|
entries: await writeExportZip({
|
|
@@ -123633,7 +123633,7 @@ async function readSessionIndex(homeDir, sessionsDir) {
|
|
|
123633
123633
|
if (trimmed === "") continue;
|
|
123634
123634
|
const entry = parseIndexLine(trimmed);
|
|
123635
123635
|
if (entry === void 0) continue;
|
|
123636
|
-
const sessionDir = resolve$
|
|
123636
|
+
const sessionDir = resolve$2(entry.sessionDir);
|
|
123637
123637
|
if (!isAbsolute$1(entry.sessionDir)) continue;
|
|
123638
123638
|
if (!isAbsolute$1(entry.workDir)) continue;
|
|
123639
123639
|
if (!isPathInside(sessionsDir, sessionDir)) continue;
|
|
@@ -123641,7 +123641,7 @@ async function readSessionIndex(homeDir, sessionsDir) {
|
|
|
123641
123641
|
result.set(entry.sessionId, {
|
|
123642
123642
|
sessionId: entry.sessionId,
|
|
123643
123643
|
sessionDir,
|
|
123644
|
-
workDir: resolve$
|
|
123644
|
+
workDir: resolve$2(entry.workDir)
|
|
123645
123645
|
});
|
|
123646
123646
|
}
|
|
123647
123647
|
return result;
|
|
@@ -123682,7 +123682,7 @@ function parseIndexLine(line) {
|
|
|
123682
123682
|
}
|
|
123683
123683
|
}
|
|
123684
123684
|
function isPathInside(parent, child) {
|
|
123685
|
-
const rel = relative$1(resolve$
|
|
123685
|
+
const rel = relative$1(resolve$2(parent), resolve$2(child));
|
|
123686
123686
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute$1(rel);
|
|
123687
123687
|
}
|
|
123688
123688
|
//#endregion
|
|
@@ -123697,7 +123697,7 @@ function slugifyWorkDirName(name) {
|
|
|
123697
123697
|
const WORKDIR_KEY_PREFIX = "wd_";
|
|
123698
123698
|
const HASH_LENGTH = 12;
|
|
123699
123699
|
function normalizeWorkDir(workDir) {
|
|
123700
|
-
const resolved = resolve$
|
|
123700
|
+
const resolved = resolve$2(workDir);
|
|
123701
123701
|
let dir = resolved;
|
|
123702
123702
|
while (dir !== dirname$2(dir)) {
|
|
123703
123703
|
if (existsSync(join$1(dir, ".git")) || existsSync(join$1(dir, "package.json"))) return dir;
|
|
@@ -124628,6 +124628,7 @@ var LocalProcess = class {
|
|
|
124628
124628
|
return Promise.resolve();
|
|
124629
124629
|
}
|
|
124630
124630
|
};
|
|
124631
|
+
let atomicWriteCounter = 0;
|
|
124631
124632
|
/**
|
|
124632
124633
|
* A JIAN implementation that directly interacts with the local filesystem.
|
|
124633
124634
|
*
|
|
@@ -124876,6 +124877,14 @@ var LocalJian = class LocalJian {
|
|
|
124876
124877
|
else await writeFile(resolved, data, encoding);
|
|
124877
124878
|
return data.length;
|
|
124878
124879
|
}
|
|
124880
|
+
async writeTextAtomic(path, data, options) {
|
|
124881
|
+
const resolved = this._resolvePath(path);
|
|
124882
|
+
const encoding = options?.encoding ?? "utf-8";
|
|
124883
|
+
const tmpPath = `${resolved}.${process.pid}.${Date.now().toString(36)}.${(atomicWriteCounter++).toString(36)}.tmp`;
|
|
124884
|
+
await writeFile(tmpPath, data, encoding);
|
|
124885
|
+
await rename(tmpPath, resolved);
|
|
124886
|
+
return data.length;
|
|
124887
|
+
}
|
|
124879
124888
|
async mkdir(path, options) {
|
|
124880
124889
|
const resolved = this._resolvePath(path);
|
|
124881
124890
|
const parents = options?.parents ?? false;
|
|
@@ -127996,6 +128005,20 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
127996
128005
|
priority: 178,
|
|
127997
128006
|
availability: "always"
|
|
127998
128007
|
},
|
|
128008
|
+
{
|
|
128009
|
+
name: "blockrows",
|
|
128010
|
+
aliases: ["rows"],
|
|
128011
|
+
description: "registry.blockrows_desc",
|
|
128012
|
+
priority: 179,
|
|
128013
|
+
availability: "always"
|
|
128014
|
+
},
|
|
128015
|
+
{
|
|
128016
|
+
name: "codebg",
|
|
128017
|
+
aliases: ["codeblock"],
|
|
128018
|
+
description: "registry.codebg_desc",
|
|
128019
|
+
priority: 180,
|
|
128020
|
+
availability: "always"
|
|
128021
|
+
},
|
|
127999
128022
|
{
|
|
128000
128023
|
name: "like",
|
|
128001
128024
|
aliases: [],
|
|
@@ -128578,6 +128601,7 @@ var ApiKeyInputDialogComponent = class extends Container {
|
|
|
128578
128601
|
const STATUS_BULLET = "■ ";
|
|
128579
128602
|
const USER_MESSAGE_BULLET = "■ ";
|
|
128580
128603
|
const FAILURE_MARK = "✗ ";
|
|
128604
|
+
const DONE_MARK = "✓ ";
|
|
128581
128605
|
const INTERJECTION_BULLET = "▸ ";
|
|
128582
128606
|
//#endregion
|
|
128583
128607
|
//#region src/tui/utils/printable-key.ts
|
|
@@ -131783,8 +131807,9 @@ const darkColors = {
|
|
|
131783
131807
|
mdLink: "#56B6C2",
|
|
131784
131808
|
mdCodeBlock: "#9CDCFE",
|
|
131785
131809
|
mdCodeBlockBorder: "#5C6370",
|
|
131786
|
-
mdCodeBlockBg: "#
|
|
131810
|
+
mdCodeBlockBg: "#16191f",
|
|
131787
131811
|
mdQuote: "#7F848E",
|
|
131812
|
+
mdHeading: "#FFC35C",
|
|
131788
131813
|
border: dark.gray700,
|
|
131789
131814
|
borderFocus: dark.yellowGreenLight,
|
|
131790
131815
|
success: dark.yellowGreen,
|
|
@@ -131817,8 +131842,9 @@ const lightColors = {
|
|
|
131817
131842
|
mdLink: "#007A8A",
|
|
131818
131843
|
mdCodeBlock: "#1565C0",
|
|
131819
131844
|
mdCodeBlockBorder: "#848484",
|
|
131820
|
-
mdCodeBlockBg: "#
|
|
131845
|
+
mdCodeBlockBg: "#f7f9fb",
|
|
131821
131846
|
mdQuote: "#616161",
|
|
131847
|
+
mdHeading: "#8F6400",
|
|
131822
131848
|
border: light.gray500,
|
|
131823
131849
|
borderFocus: light.yellowGreen700,
|
|
131824
131850
|
success: light.yellowGreen700,
|
|
@@ -131886,6 +131912,113 @@ function createThemeStyles(colors) {
|
|
|
131886
131912
|
};
|
|
131887
131913
|
}
|
|
131888
131914
|
//#endregion
|
|
131915
|
+
//#region src/tui/utils/ui-preferences.ts
|
|
131916
|
+
/**
|
|
131917
|
+
* TUI-local UI preferences persisted to `<dataDir>/ui-preferences.json`.
|
|
131918
|
+
* Distinct from agent/SDK config files (which are owned by core) — this file
|
|
131919
|
+
* stores lightweight view-state knobs owned by the TUI itself, e.g. whether
|
|
131920
|
+
* the empty-session provider hint has been dismissed by the user.
|
|
131921
|
+
*/
|
|
131922
|
+
const EMPTY = {};
|
|
131923
|
+
function getUiPreferencesPath() {
|
|
131924
|
+
return join(getDataDir(), SCREAM_CODE_UI_PREFERENCES_FILE_NAME);
|
|
131925
|
+
}
|
|
131926
|
+
function readUiPreferences() {
|
|
131927
|
+
try {
|
|
131928
|
+
const file = getUiPreferencesPath();
|
|
131929
|
+
if (!existsSync(file)) return { ...EMPTY };
|
|
131930
|
+
const raw = readFileSync(file, "utf8");
|
|
131931
|
+
const parsed = JSON.parse(raw);
|
|
131932
|
+
return {
|
|
131933
|
+
...EMPTY,
|
|
131934
|
+
...parsed
|
|
131935
|
+
};
|
|
131936
|
+
} catch {
|
|
131937
|
+
return { ...EMPTY };
|
|
131938
|
+
}
|
|
131939
|
+
}
|
|
131940
|
+
function writeUiPreferences(prefs) {
|
|
131941
|
+
try {
|
|
131942
|
+
const file = getUiPreferencesPath();
|
|
131943
|
+
mkdirSync(dirname$1(file), { recursive: true });
|
|
131944
|
+
writeFileSync(file, JSON.stringify(prefs, null, 2), "utf8");
|
|
131945
|
+
} catch {}
|
|
131946
|
+
}
|
|
131947
|
+
function isEmptySessionHintDismissed() {
|
|
131948
|
+
return readUiPreferences().emptySessionHintDismissed === true;
|
|
131949
|
+
}
|
|
131950
|
+
/** Toggle the empty-session hint on/off (Ctrl+B). Returns the new state:
|
|
131951
|
+
* true = hidden, false = shown. Persisted immediately. */
|
|
131952
|
+
function toggleEmptySessionHint() {
|
|
131953
|
+
const prefs = readUiPreferences();
|
|
131954
|
+
const dismissed = prefs.emptySessionHintDismissed !== true;
|
|
131955
|
+
prefs.emptySessionHintDismissed = dismissed;
|
|
131956
|
+
writeUiPreferences(prefs);
|
|
131957
|
+
return dismissed;
|
|
131958
|
+
}
|
|
131959
|
+
/** Whether the per-turn elapsed marker is enabled (default on). */
|
|
131960
|
+
function isTurnElapsedEnabled() {
|
|
131961
|
+
return readUiPreferences().turnElapsedEnabled !== false;
|
|
131962
|
+
}
|
|
131963
|
+
/** Toggle the per-turn elapsed marker via /snaptimer. Returns the new state:
|
|
131964
|
+
* true = shown, false = hidden. Persisted immediately. */
|
|
131965
|
+
function toggleTurnElapsed() {
|
|
131966
|
+
const prefs = readUiPreferences();
|
|
131967
|
+
const enabled = prefs.turnElapsedEnabled !== false;
|
|
131968
|
+
prefs.turnElapsedEnabled = !enabled;
|
|
131969
|
+
writeUiPreferences(prefs);
|
|
131970
|
+
return !enabled;
|
|
131971
|
+
}
|
|
131972
|
+
/** Row budgets of the activity block as stored, without clamping (see
|
|
131973
|
+
* `utils/activity-lines.ts`, which owns the ranges and defaults). */
|
|
131974
|
+
function readActivityLinePrefs() {
|
|
131975
|
+
const prefs = readUiPreferences();
|
|
131976
|
+
return {
|
|
131977
|
+
activityCollapsedLines: prefs.activityCollapsedLines,
|
|
131978
|
+
activityExpandedToolLines: prefs.activityExpandedToolLines,
|
|
131979
|
+
activityExpandedThinkingLines: prefs.activityExpandedThinkingLines
|
|
131980
|
+
};
|
|
131981
|
+
}
|
|
131982
|
+
/** Persist one activity row budget. Persisted immediately. */
|
|
131983
|
+
function writeActivityLinePref(key, value) {
|
|
131984
|
+
const prefs = readUiPreferences();
|
|
131985
|
+
prefs[key] = value;
|
|
131986
|
+
writeUiPreferences(prefs);
|
|
131987
|
+
}
|
|
131988
|
+
/**
|
|
131989
|
+
* Whether fenced code blocks render as a background panel (default on).
|
|
131990
|
+
*
|
|
131991
|
+
* Cached: the markdown renderer asks once per code line, and reading the file
|
|
131992
|
+
* each time would hit the disk on every frame. `/codebg` updates the cache.
|
|
131993
|
+
*/
|
|
131994
|
+
let codeBlockPanelCache;
|
|
131995
|
+
function isCodeBlockPanelEnabled() {
|
|
131996
|
+
codeBlockPanelCache ??= readUiPreferences().codeBlockPanelEnabled !== false;
|
|
131997
|
+
return codeBlockPanelCache;
|
|
131998
|
+
}
|
|
131999
|
+
/** Toggle the code-block panel via /codebg. Returns the new state. */
|
|
132000
|
+
function toggleCodeBlockPanel() {
|
|
132001
|
+
const prefs = readUiPreferences();
|
|
132002
|
+
const enabled = prefs.codeBlockPanelEnabled !== false;
|
|
132003
|
+
prefs.codeBlockPanelEnabled = !enabled;
|
|
132004
|
+
writeUiPreferences(prefs);
|
|
132005
|
+
codeBlockPanelCache = !enabled;
|
|
132006
|
+
return !enabled;
|
|
132007
|
+
}
|
|
132008
|
+
/** Whether user messages render with a highlight background block (default on). */
|
|
132009
|
+
function isUserMessageHighlightEnabled() {
|
|
132010
|
+
return readUiPreferences().userMessageHighlightEnabled !== false;
|
|
132011
|
+
}
|
|
132012
|
+
/** Toggle the user-message highlight block via /hl. Returns the new state:
|
|
132013
|
+
* true = highlighted, false = default (no background). Persisted immediately. */
|
|
132014
|
+
function toggleUserMessageHighlight() {
|
|
132015
|
+
const prefs = readUiPreferences();
|
|
132016
|
+
const enabled = prefs.userMessageHighlightEnabled !== false;
|
|
132017
|
+
prefs.userMessageHighlightEnabled = !enabled;
|
|
132018
|
+
writeUiPreferences(prefs);
|
|
132019
|
+
return !enabled;
|
|
132020
|
+
}
|
|
132021
|
+
//#endregion
|
|
131889
132022
|
//#region src/tui/theme/pi-tui-theme.ts
|
|
131890
132023
|
const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
131891
132024
|
/**
|
|
@@ -131895,16 +132028,16 @@ const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
|
131895
132028
|
* its DEFAULT_THEME.
|
|
131896
132029
|
*/
|
|
131897
132030
|
/**
|
|
131898
|
-
* Markdown code-block highlight theme: green
|
|
131899
|
-
* function, built_in
|
|
131900
|
-
* comments → textDim
|
|
131901
|
-
* code-highlight-theme.ts on purpose
|
|
132031
|
+
* Markdown code-block highlight theme: the fluorescent brand green is the
|
|
132032
|
+
* only green (keyword, function, built_in, diff additions); strings take the
|
|
132033
|
+
* heading amber; numbers → warning; comments → textDim. Kept distinct from
|
|
132034
|
+
* the shared preview theme in code-highlight-theme.ts on purpose.
|
|
131902
132035
|
* primary hue, while file-preview panels use the classic blue/red/yellow
|
|
131903
132036
|
* scheme mapped to the same palette. Both follow the active theme.
|
|
131904
132037
|
*/
|
|
131905
132038
|
function createMarkdownCodeHighlightTheme(colors) {
|
|
131906
132039
|
const keyword = chalk.hex(colors.primary);
|
|
131907
|
-
const str = chalk.hex(colors.
|
|
132040
|
+
const str = chalk.hex(colors.mdHeading);
|
|
131908
132041
|
const comment = chalk.hex(colors.textDim);
|
|
131909
132042
|
const num = chalk.hex(colors.warning);
|
|
131910
132043
|
const fn = chalk.hex(colors.primary);
|
|
@@ -131943,7 +132076,7 @@ function createMarkdownCodeHighlightTheme(colors) {
|
|
|
131943
132076
|
formula: text,
|
|
131944
132077
|
link: chalk.hex(colors.mdLink),
|
|
131945
132078
|
quote: chalk.hex(colors.mdQuote),
|
|
131946
|
-
addition:
|
|
132079
|
+
addition: keyword,
|
|
131947
132080
|
deletion: chalk.hex(colors.diffRemoved),
|
|
131948
132081
|
default: text
|
|
131949
132082
|
};
|
|
@@ -131954,7 +132087,7 @@ function createMarkdownTheme(colors) {
|
|
|
131954
132087
|
const border = chalk.hex(colors.border);
|
|
131955
132088
|
const codeTheme = createMarkdownCodeHighlightTheme(colors);
|
|
131956
132089
|
return {
|
|
131957
|
-
heading: (text) => chalk.bold.hex(colors.
|
|
132090
|
+
heading: (text) => chalk.bold.hex(colors.mdHeading)(stripHash(text)),
|
|
131958
132091
|
link: (text) => chalk.hex(colors.mdLink)(text),
|
|
131959
132092
|
linkUrl: (text) => muted(text),
|
|
131960
132093
|
code: (text) => chalk.hex(colors.primary)(text),
|
|
@@ -131962,8 +132095,6 @@ function createMarkdownTheme(colors) {
|
|
|
131962
132095
|
codeBlockBorder: () => null,
|
|
131963
132096
|
codeBlockIndent: " ",
|
|
131964
132097
|
codeBlockLine: (line, { index, lang, width }) => {
|
|
131965
|
-
const panel = chalk.bgHex(colors.mdCodeBlockBg);
|
|
131966
|
-
const labelPaint = chalk.bgHex(colors.mdCodeBlockBg).hex(colors.mdCodeBlock);
|
|
131967
132098
|
const content = visibleWidth(line) > width - 1 ? truncateToWidth(line, Math.max(1, width - 1), "…") : line;
|
|
131968
132099
|
const contentCells = visibleWidth(content);
|
|
131969
132100
|
const rawLabel = index === 0 && lang !== void 0 && lang.length > 0 ? ` ${lang}` : "";
|
|
@@ -131971,8 +132102,11 @@ function createMarkdownTheme(colors) {
|
|
|
131971
132102
|
const showLabel = labelCells > 0 && 1 + contentCells + labelCells + 1 <= width;
|
|
131972
132103
|
const filler = showLabel ? Math.max(0, width - contentCells - labelCells - 2) : 0;
|
|
131973
132104
|
const padCells = showLabel ? 1 : Math.max(0, width - contentCells - 1);
|
|
132105
|
+
const panelled = isCodeBlockPanelEnabled();
|
|
132106
|
+
const paint = panelled ? chalk.bgHex(colors.mdCodeBlockBg) : (text) => text;
|
|
132107
|
+
const labelPaint = panelled ? chalk.bgHex(colors.mdCodeBlockBg).hex(colors.mdCodeBlock) : chalk.hex(colors.mdCodeBlock);
|
|
131974
132108
|
const label = showLabel ? chalk.italic(labelPaint(rawLabel)) : "";
|
|
131975
|
-
return `${
|
|
132109
|
+
return `${paint(" ")}${paint(content)}${paint(" ".repeat(filler))}${label}${paint(" ".repeat(padCells))}`;
|
|
131976
132110
|
},
|
|
131977
132111
|
quote: (text) => chalk.hex(colors.mdQuote)(text),
|
|
131978
132112
|
quoteBorder: (text) => chalk.hex(colors.mdQuote)(text),
|
|
@@ -132441,7 +132575,7 @@ async function loadManagedUsageReport(host) {
|
|
|
132441
132575
|
* not hammer the vendor endpoint.
|
|
132442
132576
|
*/
|
|
132443
132577
|
const BALANCE_CACHE_MS = 6e4;
|
|
132444
|
-
const cache = /* @__PURE__ */ new Map();
|
|
132578
|
+
const cache$1 = /* @__PURE__ */ new Map();
|
|
132445
132579
|
/** Monotonic id so an out-of-order lookup can never overwrite a newer one. */
|
|
132446
132580
|
let latestRequestId = 0;
|
|
132447
132581
|
function resolveApiKey(providerName) {
|
|
@@ -132484,10 +132618,10 @@ async function loadBalance(providerName) {
|
|
|
132484
132618
|
async function getProviderBalanceForModel(model) {
|
|
132485
132619
|
const providerName = model.split("/")[0] ?? "";
|
|
132486
132620
|
if (providerName.length === 0) return null;
|
|
132487
|
-
const cached = cache.get(providerName);
|
|
132621
|
+
const cached = cache$1.get(providerName);
|
|
132488
132622
|
if (cached !== void 0 && Date.now() - cached.at < BALANCE_CACHE_MS) return cached.balance;
|
|
132489
132623
|
const balance = await loadBalance(providerName);
|
|
132490
|
-
cache.set(providerName, {
|
|
132624
|
+
cache$1.set(providerName, {
|
|
132491
132625
|
balance,
|
|
132492
132626
|
at: Date.now()
|
|
132493
132627
|
});
|
|
@@ -133931,7 +134065,7 @@ async function guidedGoalSetup(host) {
|
|
|
133931
134065
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
133932
134066
|
return;
|
|
133933
134067
|
}
|
|
133934
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
134068
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-CNhL-3wm.mjs");
|
|
133935
134069
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
133936
134070
|
title: t("goal.setup_title_initial"),
|
|
133937
134071
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -133952,7 +134086,7 @@ async function guidedGoalSetup(host) {
|
|
|
133952
134086
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
133953
134087
|
}
|
|
133954
134088
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
133955
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
134089
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-CNhL-3wm.mjs");
|
|
133956
134090
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
133957
134091
|
title: t("goal.wizard_title", { objective }),
|
|
133958
134092
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -134618,7 +134752,7 @@ var WrappedLine = class {
|
|
|
134618
134752
|
};
|
|
134619
134753
|
//#endregion
|
|
134620
134754
|
//#region src/tui/components/messages/agent-group.ts
|
|
134621
|
-
const THROTTLE_MS$
|
|
134755
|
+
const THROTTLE_MS$1 = 200;
|
|
134622
134756
|
var AgentGroupComponent = class extends Container {
|
|
134623
134757
|
colors;
|
|
134624
134758
|
ui;
|
|
@@ -134679,7 +134813,7 @@ var AgentGroupComponent = class extends Container {
|
|
|
134679
134813
|
this.throttleTimer = setTimeout(() => {
|
|
134680
134814
|
this.throttleTimer = null;
|
|
134681
134815
|
this.flushRender();
|
|
134682
|
-
}, THROTTLE_MS$
|
|
134816
|
+
}, THROTTLE_MS$1);
|
|
134683
134817
|
}
|
|
134684
134818
|
/**
|
|
134685
134819
|
* Compares each child's current phase with the phase captured at the last
|
|
@@ -134801,6 +134935,139 @@ function formatTokens$1(n) {
|
|
|
134801
134935
|
return `${String(n)} tok`;
|
|
134802
134936
|
}
|
|
134803
134937
|
//#endregion
|
|
134938
|
+
//#region src/tui/components/messages/background-agent-status.ts
|
|
134939
|
+
/**
|
|
134940
|
+
* Builds the bullet + text of a background task notice. Shared by the standalone
|
|
134941
|
+
* card and the activity block row so a notice looks the same in both places.
|
|
134942
|
+
*/
|
|
134943
|
+
function renderBackgroundStatus(data, colors, maxCells) {
|
|
134944
|
+
const tone = data.phase === "started" ? colors.primary : data.phase === "completed" ? colors.success : colors.error;
|
|
134945
|
+
const bullet = data.phase === "completed" ? chalk.hex(colors.success)(DONE_MARK) : data.phase === "failed" ? chalk.hex(colors.error)(FAILURE_MARK) : chalk.hex(colors.textDim)(`${BRAILLE_SPINNER_FRAMES[0] ?? "⠋"} `);
|
|
134946
|
+
const hasDetail = data.detail !== void 0 && data.detail.length > 0;
|
|
134947
|
+
const plain = hasDetail ? `${data.headline} (${data.detail ?? ""})` : data.headline;
|
|
134948
|
+
if (maxCells !== void 0 && visibleWidth(plain) > maxCells) return {
|
|
134949
|
+
bullet,
|
|
134950
|
+
text: chalk.hex(tone)(truncateToWidth(plain, maxCells, "…"))
|
|
134951
|
+
};
|
|
134952
|
+
return {
|
|
134953
|
+
bullet,
|
|
134954
|
+
text: chalk.hex(tone)(data.headline) + (hasDetail ? chalk.hex(colors.textDim)(` (${data.detail ?? ""})`) : "")
|
|
134955
|
+
};
|
|
134956
|
+
}
|
|
134957
|
+
var BackgroundAgentStatusComponent = class {
|
|
134958
|
+
data;
|
|
134959
|
+
colors;
|
|
134960
|
+
bullet;
|
|
134961
|
+
textComponent;
|
|
134962
|
+
cachedWidth;
|
|
134963
|
+
cachedLines;
|
|
134964
|
+
constructor(data, colors) {
|
|
134965
|
+
this.data = data;
|
|
134966
|
+
this.colors = colors;
|
|
134967
|
+
const view = renderBackgroundStatus(data, colors);
|
|
134968
|
+
this.bullet = view.bullet;
|
|
134969
|
+
this.textComponent = new Text(view.text, 0, 0);
|
|
134970
|
+
}
|
|
134971
|
+
invalidate() {
|
|
134972
|
+
this.cachedWidth = void 0;
|
|
134973
|
+
this.cachedLines = void 0;
|
|
134974
|
+
}
|
|
134975
|
+
render(width) {
|
|
134976
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
134977
|
+
const contentWidth = Math.max(1, width - 2);
|
|
134978
|
+
const lines = ["", ...this.textComponent.render(contentWidth).map((line, index) => (index === 0 ? this.bullet : " ") + line)];
|
|
134979
|
+
this.cachedWidth = width;
|
|
134980
|
+
this.cachedLines = lines;
|
|
134981
|
+
return lines;
|
|
134982
|
+
}
|
|
134983
|
+
};
|
|
134984
|
+
//#endregion
|
|
134985
|
+
//#region src/tui/utils/activity-lines.ts
|
|
134986
|
+
/**
|
|
134987
|
+
* Row budgets of the activity block.
|
|
134988
|
+
*
|
|
134989
|
+
* The defaults are the constants in `constant/rendering.ts`; `/blockrows` writes
|
|
134990
|
+
* overrides into ui-preferences.json so the block can be tuned without a rebuild.
|
|
134991
|
+
* Values are cached in memory because a block reads them on every rebuild — and
|
|
134992
|
+
* rebuilds happen on every throttled streaming tick — while the file only changes
|
|
134993
|
+
* when the user runs the command.
|
|
134994
|
+
*
|
|
134995
|
+
* Anything out of range (hand-edited file, stale value, non-number) falls back to
|
|
134996
|
+
* the default rather than changing how much content a row may occupy.
|
|
134997
|
+
*/
|
|
134998
|
+
const ACTIVITY_LINE_SETTINGS = [
|
|
134999
|
+
{
|
|
135000
|
+
key: "activityCollapsedLines",
|
|
135001
|
+
label: "blockrows.item_collapsed",
|
|
135002
|
+
values: [
|
|
135003
|
+
3,
|
|
135004
|
+
5,
|
|
135005
|
+
8
|
|
135006
|
+
],
|
|
135007
|
+
fallback: 3
|
|
135008
|
+
},
|
|
135009
|
+
{
|
|
135010
|
+
key: "activityExpandedToolLines",
|
|
135011
|
+
label: "blockrows.item_tool",
|
|
135012
|
+
values: [
|
|
135013
|
+
5,
|
|
135014
|
+
10,
|
|
135015
|
+
15,
|
|
135016
|
+
20,
|
|
135017
|
+
30
|
|
135018
|
+
],
|
|
135019
|
+
fallback: 15
|
|
135020
|
+
},
|
|
135021
|
+
{
|
|
135022
|
+
key: "activityExpandedThinkingLines",
|
|
135023
|
+
label: "blockrows.item_thinking",
|
|
135024
|
+
values: [
|
|
135025
|
+
5,
|
|
135026
|
+
10,
|
|
135027
|
+
15,
|
|
135028
|
+
20
|
|
135029
|
+
],
|
|
135030
|
+
fallback: 10
|
|
135031
|
+
}
|
|
135032
|
+
];
|
|
135033
|
+
let cache;
|
|
135034
|
+
function isActivityLineKey(value) {
|
|
135035
|
+
return ACTIVITY_LINE_SETTINGS.some((setting) => setting.key === value);
|
|
135036
|
+
}
|
|
135037
|
+
function findActivityLineSetting(key) {
|
|
135038
|
+
return ACTIVITY_LINE_SETTINGS.find((candidate) => candidate.key === key) ?? ACTIVITY_LINE_SETTINGS[0];
|
|
135039
|
+
}
|
|
135040
|
+
function resolve$1(stored, setting) {
|
|
135041
|
+
if (typeof stored !== "number" || !Number.isFinite(stored)) return setting.fallback;
|
|
135042
|
+
return setting.values.includes(stored) ? stored : setting.fallback;
|
|
135043
|
+
}
|
|
135044
|
+
function load() {
|
|
135045
|
+
const stored = readActivityLinePrefs();
|
|
135046
|
+
const settings = ACTIVITY_LINE_SETTINGS;
|
|
135047
|
+
return {
|
|
135048
|
+
collapsed: resolve$1(stored.activityCollapsedLines, settings[0]),
|
|
135049
|
+
expandedTool: resolve$1(stored.activityExpandedToolLines, settings[1]),
|
|
135050
|
+
expandedThinking: resolve$1(stored.activityExpandedThinkingLines, settings[2])
|
|
135051
|
+
};
|
|
135052
|
+
}
|
|
135053
|
+
/** Current row budgets. Cached: call `refreshActivityLines` after a change. */
|
|
135054
|
+
function getActivityLines() {
|
|
135055
|
+
cache ??= load();
|
|
135056
|
+
return cache;
|
|
135057
|
+
}
|
|
135058
|
+
/** Value currently in effect for one budget (never undefined). */
|
|
135059
|
+
function getActivityLineValue(key) {
|
|
135060
|
+
const lines = getActivityLines();
|
|
135061
|
+
if (key === "activityCollapsedLines") return lines.collapsed;
|
|
135062
|
+
if (key === "activityExpandedToolLines") return lines.expandedTool;
|
|
135063
|
+
return lines.expandedThinking;
|
|
135064
|
+
}
|
|
135065
|
+
/** Persist one budget and drop the cache so the next render picks it up. */
|
|
135066
|
+
function setActivityLineValue(key, value) {
|
|
135067
|
+
writeActivityLinePref(key, value);
|
|
135068
|
+
cache = void 0;
|
|
135069
|
+
}
|
|
135070
|
+
//#endregion
|
|
134804
135071
|
//#region src/tui/utils/speed-tracker.ts
|
|
134805
135072
|
/**
|
|
134806
135073
|
* Streaming-speed gauge for the live thinking indicator.
|
|
@@ -134957,7 +135224,9 @@ const BRANCH_LAST = " └─ ";
|
|
|
134957
135224
|
const BRANCH_PIPE = " │ ";
|
|
134958
135225
|
const TREE_PIPE_ROW = " │";
|
|
134959
135226
|
const THINKING_BODY_PREFIX = " ";
|
|
134960
|
-
|
|
135227
|
+
/** Cells of a background task notice bullet (`⠋ ` / `✓ ` / `✗ `). */
|
|
135228
|
+
const NOTICE_BULLET_WIDTH = 2;
|
|
135229
|
+
const THROTTLE_MS = 200;
|
|
134961
135230
|
/** Rates below this are noise: the live badge stays hidden until tokens flow. */
|
|
134962
135231
|
const MIN_RATE = .05;
|
|
134963
135232
|
const SEPARATOR = " · ";
|
|
@@ -134974,9 +135243,9 @@ const CARD_MARKERS = [
|
|
|
134974
135243
|
const CARD_MARKER_AT_START_RE = new RegExp(`^(?:\\u001b\\[[0-9;]*m)*(?:${CARD_MARKERS.join("|")}) `);
|
|
134975
135244
|
const CARD_MARKER_CHAR_RE = new RegExp(`^(?:\\u001b\\[[0-9;]*m)*(${CARD_MARKERS.join("|")}) `);
|
|
134976
135245
|
const CARD_INDENT_RE = /^(\u001B\[[0-9;]*m)*( {2})/;
|
|
134977
|
-
/**
|
|
134978
|
-
function
|
|
134979
|
-
return text.trim().
|
|
135246
|
+
/** Non-empty, trimmed lines of one reasoning run. */
|
|
135247
|
+
function thinkingLines(text) {
|
|
135248
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
134980
135249
|
}
|
|
134981
135250
|
/**
|
|
134982
135251
|
* Drops the card's own two-cell status marker so a row prefix can take its
|
|
@@ -135119,10 +135388,8 @@ function condenseRowLabel(header, toolName, args, budget) {
|
|
|
135119
135388
|
var ActivityGroupComponent = class extends Container {
|
|
135120
135389
|
colors;
|
|
135121
135390
|
ui;
|
|
135122
|
-
|
|
135391
|
+
segments = [];
|
|
135123
135392
|
steps = /* @__PURE__ */ new Set();
|
|
135124
|
-
thinkingText = "";
|
|
135125
|
-
thinkingLive = false;
|
|
135126
135393
|
running = true;
|
|
135127
135394
|
expanded = false;
|
|
135128
135395
|
headerText;
|
|
@@ -135132,9 +135399,8 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135132
135399
|
spinnerFrame = 0;
|
|
135133
135400
|
lastSpinnerTickAt = 0;
|
|
135134
135401
|
renderedWidth;
|
|
135135
|
-
/**
|
|
135136
|
-
|
|
135137
|
-
thinkingTokenCache;
|
|
135402
|
+
/** Reasoning text already shown when a notice interrupted the newest run. */
|
|
135403
|
+
interruptedThinking;
|
|
135138
135404
|
constructor(colors, ui) {
|
|
135139
135405
|
super();
|
|
135140
135406
|
this.colors = colors;
|
|
@@ -135148,7 +135414,7 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135148
135414
|
}
|
|
135149
135415
|
/** True when the group owns neither reasoning nor tool calls. */
|
|
135150
135416
|
isEmpty() {
|
|
135151
|
-
return this.
|
|
135417
|
+
return this.segments.every((segment) => segment.kind === "thinking" && segment.text.trim().length === 0);
|
|
135152
135418
|
}
|
|
135153
135419
|
isExpanded() {
|
|
135154
135420
|
return this.expanded;
|
|
@@ -135158,8 +135424,9 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135158
135424
|
* card is a no-op.
|
|
135159
135425
|
*/
|
|
135160
135426
|
attachTool(tc, step) {
|
|
135161
|
-
if (this.
|
|
135162
|
-
this.
|
|
135427
|
+
if (this.segments.some((segment) => segment.kind === "tool" && segment.tc === tc)) return;
|
|
135428
|
+
this.segments.push({
|
|
135429
|
+
kind: "tool",
|
|
135163
135430
|
tc,
|
|
135164
135431
|
step
|
|
135165
135432
|
});
|
|
@@ -135170,18 +135437,55 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135170
135437
|
});
|
|
135171
135438
|
this.flushNow();
|
|
135172
135439
|
}
|
|
135173
|
-
/**
|
|
135174
|
-
|
|
135175
|
-
|
|
135176
|
-
|
|
135177
|
-
|
|
135178
|
-
|
|
135179
|
-
|
|
135180
|
-
|
|
135440
|
+
/** Adds a background task notice as the newest step of the timeline. */
|
|
135441
|
+
attachNotice(data) {
|
|
135442
|
+
const last = this.segments.at(-1);
|
|
135443
|
+
if (last !== void 0 && last.kind === "thinking" && last.live) {
|
|
135444
|
+
last.live = false;
|
|
135445
|
+
this.interruptedThinking = {
|
|
135446
|
+
text: last.text,
|
|
135447
|
+
length: last.text.length
|
|
135448
|
+
};
|
|
135449
|
+
}
|
|
135450
|
+
this.segments.push({
|
|
135451
|
+
kind: "notice",
|
|
135452
|
+
data
|
|
135453
|
+
});
|
|
135454
|
+
this.flushNow();
|
|
135455
|
+
}
|
|
135456
|
+
/**
|
|
135457
|
+
* Appends to the reasoning run streaming right now, opening a new step of the
|
|
135458
|
+
* timeline when the last step is a tool call or a reasoning run that already
|
|
135459
|
+
* ended. `live` drives the streaming rate in the header.
|
|
135460
|
+
*/
|
|
135461
|
+
appendThinking(text, live) {
|
|
135462
|
+
const interrupted = this.interruptedThinking;
|
|
135463
|
+
const split = interrupted !== void 0 && text.startsWith(interrupted.text);
|
|
135464
|
+
if (interrupted !== void 0 && !split) this.interruptedThinking = void 0;
|
|
135465
|
+
const body = split && interrupted !== void 0 ? text.slice(interrupted.length) : text;
|
|
135466
|
+
const last = this.segments.at(-1);
|
|
135467
|
+
if (last !== void 0 && last.kind === "thinking" && (last.live || last.text.trim().length === 0)) {
|
|
135468
|
+
if (last.text === body && last.live === live) return;
|
|
135469
|
+
const structureChanged = body.length === 0 !== (last.text.length === 0);
|
|
135470
|
+
last.text = body;
|
|
135471
|
+
last.live = live;
|
|
135472
|
+
if (structureChanged) this.flushNow();
|
|
135473
|
+
else this.scheduleFlush();
|
|
135474
|
+
return;
|
|
135475
|
+
}
|
|
135476
|
+
if (body.trim().length === 0) return;
|
|
135477
|
+
this.segments.push({
|
|
135478
|
+
kind: "thinking",
|
|
135479
|
+
text: body,
|
|
135480
|
+
live
|
|
135481
|
+
});
|
|
135482
|
+
this.flushNow();
|
|
135181
135483
|
}
|
|
135182
135484
|
endThinking() {
|
|
135183
|
-
|
|
135184
|
-
|
|
135485
|
+
this.interruptedThinking = void 0;
|
|
135486
|
+
const last = this.segments.at(-1);
|
|
135487
|
+
if (last === void 0 || last.kind !== "thinking" || !last.live) return;
|
|
135488
|
+
last.live = false;
|
|
135185
135489
|
this.flushNow();
|
|
135186
135490
|
}
|
|
135187
135491
|
/** Marks the producing turn as finished: the header stops spinning. */
|
|
@@ -135189,13 +135493,18 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135189
135493
|
if (this.running === running) return;
|
|
135190
135494
|
this.running = running;
|
|
135191
135495
|
if (running) this.startSpinner();
|
|
135192
|
-
else
|
|
135496
|
+
else {
|
|
135497
|
+
this.stopSpinner();
|
|
135498
|
+
this.interruptedThinking = void 0;
|
|
135499
|
+
const last = this.segments.at(-1);
|
|
135500
|
+
if (last !== void 0 && last.kind === "thinking") last.live = false;
|
|
135501
|
+
}
|
|
135193
135502
|
this.flushNow();
|
|
135194
135503
|
}
|
|
135195
135504
|
setExpanded(expanded) {
|
|
135196
135505
|
if (this.expanded === expanded) return;
|
|
135197
135506
|
this.expanded = expanded;
|
|
135198
|
-
for (const
|
|
135507
|
+
for (const segment of this.segments) if (segment.kind === "tool") segment.tc.setExpanded(expanded);
|
|
135199
135508
|
this.flushNow();
|
|
135200
135509
|
}
|
|
135201
135510
|
/** Repaints after a borrowed card changed (results land via setResult). */
|
|
@@ -135224,7 +135533,7 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135224
135533
|
*/
|
|
135225
135534
|
invalidate() {
|
|
135226
135535
|
this.renderedWidth = void 0;
|
|
135227
|
-
for (const
|
|
135536
|
+
for (const segment of this.segments) if (segment.kind === "tool") segment.tc.invalidate();
|
|
135228
135537
|
super.invalidate();
|
|
135229
135538
|
}
|
|
135230
135539
|
scheduleFlush() {
|
|
@@ -135233,7 +135542,7 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135233
135542
|
this.throttleTimer = null;
|
|
135234
135543
|
this.rebuild(this.renderedWidth ?? 80);
|
|
135235
135544
|
this.ui?.requestRender();
|
|
135236
|
-
}, THROTTLE_MS
|
|
135545
|
+
}, THROTTLE_MS);
|
|
135237
135546
|
}
|
|
135238
135547
|
flushNow() {
|
|
135239
135548
|
if (this.throttleTimer !== null) {
|
|
@@ -135251,21 +135560,48 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135251
135560
|
if (this.expanded) this.buildExpandedRows(width);
|
|
135252
135561
|
else this.buildCollapsedRows(width);
|
|
135253
135562
|
}
|
|
135563
|
+
/**
|
|
135564
|
+
* Aggregate diff of the group's finished, successful file mutations
|
|
135565
|
+
* (Edit/Write), shown in the header. Read-only tools contribute nothing;
|
|
135566
|
+
* failed attempts changed nothing, so only successful calls count.
|
|
135567
|
+
*/
|
|
135568
|
+
diffTotals() {
|
|
135569
|
+
let added = 0;
|
|
135570
|
+
let removed = 0;
|
|
135571
|
+
let seen = false;
|
|
135572
|
+
for (const segment of this.segments) {
|
|
135573
|
+
if (segment.kind !== "tool") continue;
|
|
135574
|
+
const contribution = segment.tc.diffContribution();
|
|
135575
|
+
if (contribution === void 0) continue;
|
|
135576
|
+
seen = true;
|
|
135577
|
+
added += contribution.added;
|
|
135578
|
+
removed += contribution.removed;
|
|
135579
|
+
}
|
|
135580
|
+
return seen ? {
|
|
135581
|
+
added,
|
|
135582
|
+
removed
|
|
135583
|
+
} : void 0;
|
|
135584
|
+
}
|
|
135254
135585
|
buildHeader(width) {
|
|
135255
135586
|
const colors = this.colors;
|
|
135256
135587
|
const frame = BRAILLE_SPINNER_FRAMES[this.spinnerFrame] ?? BRAILLE_SPINNER_FRAMES[0];
|
|
135257
135588
|
const marker = this.running ? chalk.hex(colors.roleThinking)(`${frame} `) : chalk.hex(colors.success)(STATUS_BULLET);
|
|
135258
135589
|
const label = this.running ? t("activitygroup.running") : t("activitygroup.done");
|
|
135259
135590
|
const parts = [];
|
|
135591
|
+
const toolCount = this.toolCount();
|
|
135260
135592
|
if (this.steps.size > 0) parts.push(t("activitygroup.steps", { count: String(this.steps.size) }));
|
|
135261
|
-
if (
|
|
135262
|
-
parts.push(t("activitygroup.tokens", { tok: this.formatTokens(this.blockTokens()) }));
|
|
135593
|
+
if (toolCount > 0) parts.push(t("activitygroup.tools", { count: String(toolCount) }));
|
|
135263
135594
|
const stats = chalk.dim(SEPARATOR + parts.join(SEPARATOR));
|
|
135264
|
-
const
|
|
135595
|
+
const diff = this.diffTotals();
|
|
135596
|
+
const diffPart = diff ? SEPARATOR + t("activitygroup.diff", {
|
|
135597
|
+
added: chalk.hex(colors.diffAdded)(`+${diff.added}`),
|
|
135598
|
+
removed: chalk.hex(colors.diffRemoved)(`-${diff.removed}`)
|
|
135599
|
+
}) : "";
|
|
135600
|
+
const speed = this.liveThinking() ? getSharedSpeedTracker().getSpeed() : 0;
|
|
135265
135601
|
const rate = speed > MIN_RATE ? chalk.hex(lerpHex(colors.textDim, colors.primary, easeSpeedRatio(speed / 200)))(t("activitygroup.rate", { rate: speed.toFixed(1) })) : chalk.dim(t("activitygroup.rate", { rate: "-" }));
|
|
135266
135602
|
const hint = chalk.dim(this.expanded ? t("activitygroup.hint_collapse") : t("activitygroup.hint_expand"));
|
|
135267
135603
|
const head = `${marker}${chalk.hex(colors.primary).bold(label)}${stats}`;
|
|
135268
|
-
const withRate = `${head}${rate}`;
|
|
135604
|
+
const withRate = `${head}${diffPart}${rate}`;
|
|
135269
135605
|
const full = `${withRate}${hint}`;
|
|
135270
135606
|
if (visibleWidth(full) <= width) return full;
|
|
135271
135607
|
if (visibleWidth(withRate) <= width) return withRate;
|
|
@@ -135274,68 +135610,126 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135274
135610
|
if (visibleWidth(bare) <= width) return bare;
|
|
135275
135611
|
return chalk.hex(colors.primary).bold(truncateToWidth(label, Math.max(1, width - 2), "…"));
|
|
135276
135612
|
}
|
|
135277
|
-
/**
|
|
135613
|
+
/**
|
|
135614
|
+
* Collapsed state: the newest steps of the timeline, kept in chronological
|
|
135615
|
+
* order so the block still reads top-down. Reasoning runs and tool calls share
|
|
135616
|
+
* the same row budget — by default the newest step plus the reasoning run that
|
|
135617
|
+
* led to it.
|
|
135618
|
+
*/
|
|
135278
135619
|
buildCollapsedRows(width) {
|
|
135279
|
-
const
|
|
135280
|
-
const
|
|
135281
|
-
|
|
135282
|
-
const
|
|
135283
|
-
|
|
135284
|
-
|
|
135285
|
-
|
|
135286
|
-
|
|
135620
|
+
const budget = Math.max(1, getActivityLines().collapsed - 1);
|
|
135621
|
+
const shown = this.segments.slice(Math.max(0, this.segments.length - budget));
|
|
135622
|
+
shown.forEach((segment, index) => {
|
|
135623
|
+
const isLast = index === shown.length - 1;
|
|
135624
|
+
if (segment.kind === "tool") {
|
|
135625
|
+
const row = this.toolRow(segment.tc, width, 3);
|
|
135626
|
+
this.bodyContainer.addChild(new Text(`${isLast ? BRANCH_LAST : BRANCH_FIRST}${this.statusGlyph(row)} ${row.header}`, 0, 0));
|
|
135627
|
+
return;
|
|
135628
|
+
}
|
|
135629
|
+
if (segment.kind === "notice") {
|
|
135630
|
+
this.bodyContainer.addChild(this.noticeRow(segment.data, width, isLast));
|
|
135631
|
+
return;
|
|
135632
|
+
}
|
|
135633
|
+
this.bodyContainer.addChild(this.thinkingSummaryRow(segment, width, isLast));
|
|
135634
|
+
});
|
|
135287
135635
|
}
|
|
135288
|
-
/**
|
|
135636
|
+
/**
|
|
135637
|
+
* Expanded state: the whole timeline in order. Rows are budgeted for the block
|
|
135638
|
+
* as a whole, so a turn with dozens of steps cannot expand into hundreds of
|
|
135639
|
+
* rows; whatever does not fit is summarised in one row at the end.
|
|
135640
|
+
*/
|
|
135289
135641
|
buildExpandedRows(width) {
|
|
135290
|
-
const colors = this.colors;
|
|
135291
|
-
const hasThinking = this.thinkingText.trim().length > 0;
|
|
135292
135642
|
this.bodyContainer.addChild(new Text(TREE_PIPE_ROW, 0, 0));
|
|
135293
|
-
const toolRows = this.tools.map((entry) => ({ row: this.toolRow(entry.tc, width, 15) }));
|
|
135294
135643
|
let used = 1;
|
|
135295
|
-
let
|
|
135296
|
-
let
|
|
135297
|
-
|
|
135298
|
-
const
|
|
135299
|
-
|
|
135300
|
-
|
|
135301
|
-
|
|
135644
|
+
let hidden = 0;
|
|
135645
|
+
for (let index = 0; index < this.segments.length; index += 1) {
|
|
135646
|
+
const segment = this.segments[index];
|
|
135647
|
+
const isLastStep = index === this.segments.length - 1;
|
|
135648
|
+
const rows = this.segmentRows(segment, width, isLastStep);
|
|
135649
|
+
const budget = 60 - (isLastStep ? 0 : 1);
|
|
135650
|
+
if (index > 0 && used + rows.cost > budget) {
|
|
135651
|
+
hidden = this.segments.length - index;
|
|
135652
|
+
break;
|
|
135302
135653
|
}
|
|
135303
|
-
const
|
|
135304
|
-
|
|
135654
|
+
for (const component of rows.components) this.bodyContainer.addChild(component);
|
|
135655
|
+
used += rows.cost;
|
|
135656
|
+
}
|
|
135657
|
+
if (hidden > 0) {
|
|
135658
|
+
const summary = t("activitygroup.segments_hidden", { count: String(hidden) });
|
|
135659
|
+
this.bodyContainer.addChild(new Text(`${BRANCH_PIPE}${chalk.dim(summary)}`, 0, 0));
|
|
135660
|
+
}
|
|
135661
|
+
}
|
|
135662
|
+
/** Renders one step of the timeline: its branch row plus its own body rows. */
|
|
135663
|
+
segmentRows(segment, width, isLast) {
|
|
135664
|
+
if (segment.kind === "tool") {
|
|
135665
|
+
const row = this.toolRow(segment.tc, width, getActivityLines().expandedTool);
|
|
135666
|
+
const components = [new Text(`${isLast ? BRANCH_LAST : BRANCH_FIRST}${this.statusGlyph(row)} ${row.header}`, 0, 0)];
|
|
135305
135667
|
const continuation = isLast ? THINKING_BODY_PREFIX : BRANCH_PIPE;
|
|
135306
|
-
for (const line of row.body)
|
|
135307
|
-
|
|
135308
|
-
|
|
135309
|
-
|
|
135310
|
-
|
|
135311
|
-
|
|
135312
|
-
|
|
135313
|
-
this.
|
|
135314
|
-
|
|
135315
|
-
}
|
|
135316
|
-
|
|
135317
|
-
|
|
135318
|
-
|
|
135319
|
-
|
|
135320
|
-
|
|
135321
|
-
|
|
135322
|
-
|
|
135323
|
-
|
|
135324
|
-
|
|
135325
|
-
|
|
135326
|
-
|
|
135668
|
+
for (const line of row.body) components.push(new Text(`${continuation}${line}`, 0, 0));
|
|
135669
|
+
return {
|
|
135670
|
+
components,
|
|
135671
|
+
cost: 1 + row.body.length
|
|
135672
|
+
};
|
|
135673
|
+
}
|
|
135674
|
+
if (segment.kind === "notice") return {
|
|
135675
|
+
components: [this.noticeRow(segment.data, width, isLast)],
|
|
135676
|
+
cost: 1
|
|
135677
|
+
};
|
|
135678
|
+
return this.thinkingSegmentRows(segment, width, isLast);
|
|
135679
|
+
}
|
|
135680
|
+
/** One background task notice, kept to a single row. */
|
|
135681
|
+
noticeRow(data, width, isLast) {
|
|
135682
|
+
const prefix = isLast ? BRANCH_LAST : BRANCH_FIRST;
|
|
135683
|
+
const view = renderBackgroundStatus(data, this.colors, Math.max(1, width - visibleWidth(prefix) - NOTICE_BULLET_WIDTH));
|
|
135684
|
+
return new Text(`${prefix}${view.bullet}${view.text}`, 0, 0);
|
|
135685
|
+
}
|
|
135686
|
+
/**
|
|
135687
|
+
* One reasoning run: a label row plus as many of its lines as its own budget
|
|
135688
|
+
* allows. Lines keep their order; the oldest end of a live run (or the newest
|
|
135689
|
+
* end of a finished one) is dropped until the budget fits.
|
|
135690
|
+
*/
|
|
135691
|
+
thinkingSegmentRows(segment, width, isLast) {
|
|
135692
|
+
const colors = this.colors;
|
|
135693
|
+
const lines = thinkingLines(segment.text);
|
|
135694
|
+
if (lines.length === 0) return {
|
|
135695
|
+
components: [],
|
|
135696
|
+
cost: 0
|
|
135697
|
+
};
|
|
135698
|
+
const continuation = isLast ? THINKING_BODY_PREFIX : BRANCH_PIPE;
|
|
135699
|
+
const components = [new Text(`${isLast ? BRANCH_LAST : BRANCH_FIRST}${chalk.hex(colors.roleThinking)(t("activitygroup.thinking_label"))}`, 0, 0)];
|
|
135700
|
+
let cost = 1;
|
|
135701
|
+
const budget = getActivityLines().expandedThinking;
|
|
135702
|
+
const inner = Math.max(1, width - visibleWidth(continuation));
|
|
135703
|
+
const height = (line) => new WrappedLine(continuation, continuation, line).render(inner).length;
|
|
135704
|
+
let selected = segment.live ? lines.slice(-budget) : lines.slice(0, budget);
|
|
135705
|
+
while (selected.length > 1 && selected.reduce((sum, line) => sum + height(line), 0) > budget) selected = segment.live ? selected.slice(1) : selected.slice(0, -1);
|
|
135327
135706
|
const first = selected[0];
|
|
135328
|
-
if (selected.length === 1 && first !== void 0 && height(first) >
|
|
135707
|
+
if (selected.length === 1 && first !== void 0 && height(first) > budget) selected = [truncateToWidth(first, inner * budget, "…")];
|
|
135329
135708
|
for (const line of selected) {
|
|
135330
|
-
|
|
135331
|
-
|
|
135332
|
-
used += height(line);
|
|
135709
|
+
components.push(new WrappedLine(continuation, continuation, chalk.hex(colors.roleThinking)(line)));
|
|
135710
|
+
cost += height(line);
|
|
135333
135711
|
}
|
|
135334
|
-
const
|
|
135335
|
-
if (
|
|
135336
|
-
const hint =
|
|
135337
|
-
|
|
135712
|
+
const hiddenLines = lines.length - selected.length;
|
|
135713
|
+
if (hiddenLines > 0) {
|
|
135714
|
+
const hint = segment.live ? t("activitygroup.thinking_earlier", { count: String(hiddenLines) }) : t("activitygroup.thinking_more", { count: String(hiddenLines) });
|
|
135715
|
+
components.push(new Text(`${continuation}${chalk.dim(hint)}`, 0, 0));
|
|
135716
|
+
cost += 1;
|
|
135338
135717
|
}
|
|
135718
|
+
return {
|
|
135719
|
+
components,
|
|
135720
|
+
cost
|
|
135721
|
+
};
|
|
135722
|
+
}
|
|
135723
|
+
/** Number of tool calls on the timeline. */
|
|
135724
|
+
toolCount() {
|
|
135725
|
+
let count = 0;
|
|
135726
|
+
for (const segment of this.segments) if (segment.kind === "tool") count += 1;
|
|
135727
|
+
return count;
|
|
135728
|
+
}
|
|
135729
|
+
/** True while the newest step of the timeline is a reasoning run still streaming. */
|
|
135730
|
+
liveThinking() {
|
|
135731
|
+
const last = this.segments.at(-1);
|
|
135732
|
+
return last !== void 0 && last.kind === "thinking" && last.live;
|
|
135339
135733
|
}
|
|
135340
135734
|
/**
|
|
135341
135735
|
* Renders one borrowed card at the width left over by the row prefix and
|
|
@@ -135373,63 +135767,18 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135373
135767
|
}
|
|
135374
135768
|
return chalk.hex(this.colors.success)("✓");
|
|
135375
135769
|
}
|
|
135376
|
-
thinkingLines() {
|
|
135377
|
-
return this.thinkingText.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
135378
|
-
}
|
|
135379
135770
|
/**
|
|
135380
|
-
* Collapsed reasoning row: the first
|
|
135381
|
-
*
|
|
135382
|
-
*
|
|
135771
|
+
* Collapsed reasoning row: the run's first line clipped to whatever space the
|
|
135772
|
+
* row can still hold at this width, so a narrow terminal cannot wrap the block
|
|
135773
|
+
* past its height. The token estimate lives in the block header only.
|
|
135383
135774
|
*/
|
|
135384
|
-
|
|
135385
|
-
const first =
|
|
135775
|
+
thinkingSummaryRow(segment, width, isLast) {
|
|
135776
|
+
const first = thinkingLines(segment.text)[0] ?? "";
|
|
135386
135777
|
const label = t("activitygroup.thinking_summary", { summary: "" });
|
|
135387
|
-
const
|
|
135388
|
-
const cells = Math.max(1,
|
|
135778
|
+
const used = visibleWidth(isLast ? BRANCH_LAST : BRANCH_FIRST) + visibleWidth(label);
|
|
135779
|
+
const cells = Math.max(1, width - used - 1);
|
|
135389
135780
|
const summary = chalk.hex(this.colors.roleThinking)(t("activitygroup.thinking_summary", { summary: truncateToWidth(first, cells, "…") }));
|
|
135390
|
-
return `${BRANCH_LAST
|
|
135391
|
-
}
|
|
135392
|
-
/**
|
|
135393
|
-
* Token estimate for the whole block: the reasoning text plus every result the
|
|
135394
|
-
* block owns, i.e. how much material this stretch of work moved through.
|
|
135395
|
-
*/
|
|
135396
|
-
blockTokens() {
|
|
135397
|
-
let tokens = this.thinkingTokens();
|
|
135398
|
-
for (const entry of this.tools) tokens += this.toolTokens(entry.tc);
|
|
135399
|
-
return tokens;
|
|
135400
|
-
}
|
|
135401
|
-
/** Result tokens of one card, counted once per distinct result text. */
|
|
135402
|
-
toolTokens(tc) {
|
|
135403
|
-
const output = tc.resultView?.output ?? "";
|
|
135404
|
-
const cached = this.toolTokenCache.get(tc);
|
|
135405
|
-
if (cached !== void 0 && cached.output === output) return cached.tokens;
|
|
135406
|
-
const tokens = countTokens(output);
|
|
135407
|
-
this.toolTokenCache.set(tc, {
|
|
135408
|
-
output,
|
|
135409
|
-
tokens
|
|
135410
|
-
});
|
|
135411
|
-
return tokens;
|
|
135412
|
-
}
|
|
135413
|
-
/**
|
|
135414
|
-
* Token estimate for the block's reasoning. Character-based and script-aware
|
|
135415
|
-
* (CJK ≈ one token per char, Latin ≈ a quarter), using the same estimator as
|
|
135416
|
-
* the streaming speed gauge so both numbers agree. Empty reasoning counts as
|
|
135417
|
-
* zero: the estimator's floor of one token is for per-delta rates.
|
|
135418
|
-
*/
|
|
135419
|
-
thinkingTokens() {
|
|
135420
|
-
const cached = this.thinkingTokenCache;
|
|
135421
|
-
if (cached !== void 0 && cached.text === this.thinkingText) return cached.tokens;
|
|
135422
|
-
const tokens = countTokens(this.thinkingText);
|
|
135423
|
-
this.thinkingTokenCache = {
|
|
135424
|
-
text: this.thinkingText,
|
|
135425
|
-
tokens
|
|
135426
|
-
};
|
|
135427
|
-
return tokens;
|
|
135428
|
-
}
|
|
135429
|
-
formatTokens(tokens) {
|
|
135430
|
-
if (tokens < 1e3) return String(tokens);
|
|
135431
|
-
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}K`;
|
|
135432
|
-
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
135781
|
+
return new Text(`${isLast ? BRANCH_LAST : BRANCH_FIRST}${summary}`, 0, 0);
|
|
135433
135782
|
}
|
|
135434
135783
|
scheduleSpinnerTick(delayMs) {
|
|
135435
135784
|
if (this.ui === void 0) return;
|
|
@@ -135442,7 +135791,7 @@ var ActivityGroupComponent = class extends Container {
|
|
|
135442
135791
|
this.spinnerFrame = (this.spinnerFrame + steps) % BRAILLE_SPINNER_FRAMES.length;
|
|
135443
135792
|
this.lastSpinnerTickAt += steps * 80;
|
|
135444
135793
|
const width = this.renderedWidth ?? 80;
|
|
135445
|
-
if (!this.expanded && this.
|
|
135794
|
+
if (!this.expanded && this.segments.some((segment) => segment.kind === "tool" && segment.tc.resultView === void 0)) this.rebuild(width);
|
|
135446
135795
|
else this.headerText.setText(this.buildHeader(width));
|
|
135447
135796
|
this.ui?.requestRender();
|
|
135448
135797
|
}
|
|
@@ -135666,244 +136015,6 @@ var AssistantMessageComponent = class {
|
|
|
135666
136015
|
}
|
|
135667
136016
|
};
|
|
135668
136017
|
//#endregion
|
|
135669
|
-
//#region src/tui/components/messages/background-agent-status.ts
|
|
135670
|
-
var BackgroundAgentStatusComponent = class {
|
|
135671
|
-
data;
|
|
135672
|
-
colors;
|
|
135673
|
-
bullet;
|
|
135674
|
-
textComponent;
|
|
135675
|
-
cachedWidth;
|
|
135676
|
-
cachedLines;
|
|
135677
|
-
constructor(data, colors) {
|
|
135678
|
-
this.data = data;
|
|
135679
|
-
this.colors = colors;
|
|
135680
|
-
const tone = data.phase === "started" ? colors.primary : data.phase === "completed" ? colors.success : colors.error;
|
|
135681
|
-
this.bullet = data.phase === "failed" ? chalk.hex(tone)(FAILURE_MARK) : chalk.hex(tone)(STATUS_BULLET);
|
|
135682
|
-
const text = chalk.hex(tone)(data.headline) + (data.detail !== void 0 && data.detail.length > 0 ? chalk.hex(colors.textDim)(` (${data.detail})`) : "");
|
|
135683
|
-
this.textComponent = new Text(text, 0, 0);
|
|
135684
|
-
}
|
|
135685
|
-
invalidate() {
|
|
135686
|
-
this.cachedWidth = void 0;
|
|
135687
|
-
this.cachedLines = void 0;
|
|
135688
|
-
}
|
|
135689
|
-
render(width) {
|
|
135690
|
-
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
135691
|
-
const contentWidth = Math.max(1, width - 2);
|
|
135692
|
-
const lines = ["", ...this.textComponent.render(contentWidth).map((line, index) => (index === 0 ? this.bullet : " ") + line)];
|
|
135693
|
-
this.cachedWidth = width;
|
|
135694
|
-
this.cachedLines = lines;
|
|
135695
|
-
return lines;
|
|
135696
|
-
}
|
|
135697
|
-
};
|
|
135698
|
-
//#endregion
|
|
135699
|
-
//#region src/tui/components/messages/read-group.ts
|
|
135700
|
-
const THROTTLE_MS = 200;
|
|
135701
|
-
const CONFLICT_MARKER_RE = /^(<<<<<<<|=======|>>>>>>>)/;
|
|
135702
|
-
function detectConflictMarkers(contentLines) {
|
|
135703
|
-
for (const raw of contentLines) {
|
|
135704
|
-
const tabIdx = raw.indexOf(" ");
|
|
135705
|
-
const line = tabIdx >= 0 ? raw.slice(tabIdx + 1) : raw;
|
|
135706
|
-
if (CONFLICT_MARKER_RE.test(line)) return true;
|
|
135707
|
-
}
|
|
135708
|
-
return false;
|
|
135709
|
-
}
|
|
135710
|
-
function parseReadGroupOutput(output) {
|
|
135711
|
-
const results = [];
|
|
135712
|
-
const lines = output.split("\n");
|
|
135713
|
-
let currentPath;
|
|
135714
|
-
let currentLines = [];
|
|
135715
|
-
const finishSection = (path, contentLines) => {
|
|
135716
|
-
if (path === void 0) return;
|
|
135717
|
-
const text = contentLines.join("\n");
|
|
135718
|
-
if (text.trimStart().startsWith("[ERROR]")) {
|
|
135719
|
-
results.push({
|
|
135720
|
-
filePath: path,
|
|
135721
|
-
lines: 0,
|
|
135722
|
-
failed: true
|
|
135723
|
-
});
|
|
135724
|
-
return;
|
|
135725
|
-
}
|
|
135726
|
-
const systemMatch = text.match(/<system>([\s\S]*?)<\/system>\s*$/);
|
|
135727
|
-
let lineCount = 0;
|
|
135728
|
-
if (systemMatch !== null) {
|
|
135729
|
-
const systemBody = systemMatch[1];
|
|
135730
|
-
if (systemBody !== void 0) {
|
|
135731
|
-
const lineMatch = systemBody.match(/(\d+)\s+lines?\s+read\s+from\s+file/);
|
|
135732
|
-
if (lineMatch !== null) {
|
|
135733
|
-
const count = lineMatch[1];
|
|
135734
|
-
if (count !== void 0) lineCount = parseInt(count, 10);
|
|
135735
|
-
}
|
|
135736
|
-
}
|
|
135737
|
-
}
|
|
135738
|
-
const hasConflicts = detectConflictMarkers(contentLines);
|
|
135739
|
-
results.push({
|
|
135740
|
-
filePath: path,
|
|
135741
|
-
lines: lineCount,
|
|
135742
|
-
failed: false,
|
|
135743
|
-
hasConflicts
|
|
135744
|
-
});
|
|
135745
|
-
};
|
|
135746
|
-
for (const line of lines) {
|
|
135747
|
-
const match = line.match(/^---\s+(.+?)\s+---$/);
|
|
135748
|
-
if (match !== null) {
|
|
135749
|
-
finishSection(currentPath, currentLines);
|
|
135750
|
-
currentPath = match[1];
|
|
135751
|
-
currentLines = [];
|
|
135752
|
-
} else if (currentPath !== void 0) currentLines.push(line);
|
|
135753
|
-
}
|
|
135754
|
-
finishSection(currentPath, currentLines);
|
|
135755
|
-
return results;
|
|
135756
|
-
}
|
|
135757
|
-
var ReadGroupComponent = class extends Container {
|
|
135758
|
-
colors;
|
|
135759
|
-
ui;
|
|
135760
|
-
entries = [];
|
|
135761
|
-
directResults;
|
|
135762
|
-
headerText;
|
|
135763
|
-
bodyContainer;
|
|
135764
|
-
throttleTimer = null;
|
|
135765
|
-
lastFlushPhases = /* @__PURE__ */ new Map();
|
|
135766
|
-
constructor(colors, ui) {
|
|
135767
|
-
super();
|
|
135768
|
-
this.colors = colors;
|
|
135769
|
-
this.ui = ui;
|
|
135770
|
-
this.addChild(new Spacer(1));
|
|
135771
|
-
this.headerText = new Text("", 0, 0);
|
|
135772
|
-
this.addChild(this.headerText);
|
|
135773
|
-
this.bodyContainer = new Container();
|
|
135774
|
-
this.addChild(this.bodyContainer);
|
|
135775
|
-
}
|
|
135776
|
-
size() {
|
|
135777
|
-
return this.directResults?.length ?? this.entries.length;
|
|
135778
|
-
}
|
|
135779
|
-
setResults(results) {
|
|
135780
|
-
this.directResults = results;
|
|
135781
|
-
this.flushRender();
|
|
135782
|
-
}
|
|
135783
|
-
/**
|
|
135784
|
-
* Borrows a standalone `ToolCallComponent` into the group as a hidden state
|
|
135785
|
-
* container. Snapshot changes trigger throttled refreshes. Re-attaching the
|
|
135786
|
-
* same toolCallId is a no-op.
|
|
135787
|
-
*/
|
|
135788
|
-
attach(toolCallId, tc) {
|
|
135789
|
-
if (this.entries.some((e) => e.toolCallId === toolCallId)) return;
|
|
135790
|
-
this.entries.push({
|
|
135791
|
-
toolCallId,
|
|
135792
|
-
tc
|
|
135793
|
-
});
|
|
135794
|
-
tc.setSnapshotListener(() => {
|
|
135795
|
-
this.scheduleRender();
|
|
135796
|
-
});
|
|
135797
|
-
this.flushRender();
|
|
135798
|
-
}
|
|
135799
|
-
/**
|
|
135800
|
-
* The pending -> done/failed transition is the important visible change, so
|
|
135801
|
-
* it refreshes immediately. Other changes are throttled.
|
|
135802
|
-
*/
|
|
135803
|
-
scheduleRender() {
|
|
135804
|
-
if (this.detectPhaseTransition()) {
|
|
135805
|
-
this.flushRender();
|
|
135806
|
-
return;
|
|
135807
|
-
}
|
|
135808
|
-
if (this.throttleTimer !== null) return;
|
|
135809
|
-
this.throttleTimer = setTimeout(() => {
|
|
135810
|
-
this.throttleTimer = null;
|
|
135811
|
-
this.flushRender();
|
|
135812
|
-
}, THROTTLE_MS);
|
|
135813
|
-
}
|
|
135814
|
-
detectPhaseTransition() {
|
|
135815
|
-
for (const e of this.entries) {
|
|
135816
|
-
const phase = e.tc.getReadSnapshot().phase;
|
|
135817
|
-
if (this.lastFlushPhases.get(e.toolCallId) !== phase) return true;
|
|
135818
|
-
}
|
|
135819
|
-
return false;
|
|
135820
|
-
}
|
|
135821
|
-
flushRender() {
|
|
135822
|
-
if (this.throttleTimer !== null) {
|
|
135823
|
-
clearTimeout(this.throttleTimer);
|
|
135824
|
-
this.throttleTimer = null;
|
|
135825
|
-
}
|
|
135826
|
-
if (this.directResults !== void 0) {
|
|
135827
|
-
this.renderDirectResults();
|
|
135828
|
-
return;
|
|
135829
|
-
}
|
|
135830
|
-
const snapshots = this.entries.map((e) => e.tc.getReadSnapshot());
|
|
135831
|
-
let pending = 0;
|
|
135832
|
-
let failed = 0;
|
|
135833
|
-
let totalLines = 0;
|
|
135834
|
-
for (const snap of snapshots) if (snap.phase === "pending") pending += 1;
|
|
135835
|
-
else if (snap.phase === "failed") failed += 1;
|
|
135836
|
-
else totalLines += snap.lines;
|
|
135837
|
-
this.headerText.setText(this.buildHeader(snapshots.length, pending, failed, totalLines));
|
|
135838
|
-
this.bodyContainer.clear();
|
|
135839
|
-
const visibleSnapshots = snapshots.filter((snap) => snap.filePath !== void 0 && snap.filePath.length > 0);
|
|
135840
|
-
visibleSnapshots.forEach((snap, idx) => {
|
|
135841
|
-
const isLast = idx === visibleSnapshots.length - 1;
|
|
135842
|
-
this.addBodyBranch(snap.filePath ?? "", isLast, this.buildBodyTail(snap));
|
|
135843
|
-
});
|
|
135844
|
-
this.lastFlushPhases.clear();
|
|
135845
|
-
this.entries.forEach((entry, i) => {
|
|
135846
|
-
const snap = snapshots[i];
|
|
135847
|
-
if (snap !== void 0) this.lastFlushPhases.set(entry.toolCallId, snap.phase);
|
|
135848
|
-
});
|
|
135849
|
-
this.invalidate();
|
|
135850
|
-
this.ui?.requestRender();
|
|
135851
|
-
}
|
|
135852
|
-
renderDirectResults() {
|
|
135853
|
-
const results = this.directResults ?? [];
|
|
135854
|
-
let failed = 0;
|
|
135855
|
-
let totalLines = 0;
|
|
135856
|
-
for (const r of results) if (r.failed) failed += 1;
|
|
135857
|
-
else totalLines += r.lines;
|
|
135858
|
-
this.headerText.setText(this.buildHeader(results.length, 0, failed, totalLines));
|
|
135859
|
-
this.bodyContainer.clear();
|
|
135860
|
-
results.forEach((r, idx) => {
|
|
135861
|
-
const isLast = idx === results.length - 1;
|
|
135862
|
-
this.addBodyBranch(r.filePath, isLast, this.buildDirectBodyTail(r));
|
|
135863
|
-
});
|
|
135864
|
-
this.invalidate();
|
|
135865
|
-
this.ui?.requestRender();
|
|
135866
|
-
}
|
|
135867
|
-
buildHeader(total, pending, failed, totalLines) {
|
|
135868
|
-
const colors = this.colors;
|
|
135869
|
-
const dim = chalk.dim;
|
|
135870
|
-
if (pending > 0) return `${chalk.hex(colors.roleAssistant)(STATUS_BULLET)}${chalk.hex(colors.primary).bold(t("readgroup.reading", { count: total }))}`;
|
|
135871
|
-
if (failed === total) return `${chalk.hex(colors.error)("✗ ")}${chalk.hex(colors.error).bold(t("readgroup.read", { count: total }))}${chalk.hex(colors.error)(t("readgroup.failed_suffix"))}`;
|
|
135872
|
-
return `${chalk.hex(colors.success)(STATUS_BULLET)}${chalk.hex(colors.primary).bold(t("readgroup.read", { count: total }))}${dim(t("readgroup.lines_suffix", { count: totalLines }))}${failed > 0 ? chalk.hex(colors.error)(t("readgroup.failed_count", { count: failed })) : ""}`;
|
|
135873
|
-
}
|
|
135874
|
-
/**
|
|
135875
|
-
* Renders one tree-branch body line with a hanging-indent continuation,
|
|
135876
|
-
* so a path longer than the viewport keeps its branch connector instead
|
|
135877
|
-
* of breaking the tree.
|
|
135878
|
-
*/
|
|
135879
|
-
addBodyBranch(path, isLast, tail) {
|
|
135880
|
-
const prefix = ` ${isLast ? "└─" : "├─"} `;
|
|
135881
|
-
const continuation = isLast ? " " : " │ ";
|
|
135882
|
-
this.bodyContainer.addChild(new WrappedLine(prefix, continuation, `${chalk.hex(this.colors.text)(path)}${tail}`));
|
|
135883
|
-
}
|
|
135884
|
-
buildBodyTail(snap) {
|
|
135885
|
-
const dim = chalk.dim;
|
|
135886
|
-
if (snap.phase === "pending") return dim(t("readgroup.reading_suffix"));
|
|
135887
|
-
if (snap.phase === "failed") return chalk.hex(this.colors.error)(t("readgroup.failed_suffix"));
|
|
135888
|
-
return dim(t("readgroup.lines_suffix", { count: snap.lines }));
|
|
135889
|
-
}
|
|
135890
|
-
buildDirectBodyTail(result) {
|
|
135891
|
-
const dim = chalk.dim;
|
|
135892
|
-
if (result.failed) return chalk.hex(this.colors.error)(t("readgroup.failed_suffix"));
|
|
135893
|
-
let tail = dim(t("readgroup.lines_suffix", { count: result.lines }));
|
|
135894
|
-
if (result.hasConflicts) tail = `${tail}${chalk.hex(this.colors.warning)(` ⚠ ${t("readgroup.conflict_suffix")}`)}`;
|
|
135895
|
-
return tail;
|
|
135896
|
-
}
|
|
135897
|
-
/** Releases throttle timers so destroyed components cannot refresh later. */
|
|
135898
|
-
dispose() {
|
|
135899
|
-
if (this.throttleTimer !== null) {
|
|
135900
|
-
clearTimeout(this.throttleTimer);
|
|
135901
|
-
this.throttleTimer = null;
|
|
135902
|
-
}
|
|
135903
|
-
for (const e of this.entries) e.tc.setSnapshotListener(void 0);
|
|
135904
|
-
}
|
|
135905
|
-
};
|
|
135906
|
-
//#endregion
|
|
135907
136018
|
//#region src/tui/components/messages/skill-activation.ts
|
|
135908
136019
|
/**
|
|
135909
136020
|
* Skill activation card.
|
|
@@ -137058,6 +137169,76 @@ const readMediaSummary = (toolCall, result, ctx) => {
|
|
|
137058
137169
|
return out;
|
|
137059
137170
|
};
|
|
137060
137171
|
//#endregion
|
|
137172
|
+
//#region src/tui/components/messages/tool-renderers/read-group-list.ts
|
|
137173
|
+
const CONFLICT_MARKER_RE = /^(<<<<<<<|=======|>>>>>>>)/;
|
|
137174
|
+
function detectConflictMarkers(contentLines) {
|
|
137175
|
+
for (const raw of contentLines) {
|
|
137176
|
+
const tabIdx = raw.indexOf(" ");
|
|
137177
|
+
const line = tabIdx >= 0 ? raw.slice(tabIdx + 1) : raw;
|
|
137178
|
+
if (CONFLICT_MARKER_RE.test(line)) return true;
|
|
137179
|
+
}
|
|
137180
|
+
return false;
|
|
137181
|
+
}
|
|
137182
|
+
/** Parses the `--- path ---` sections of the batch reader's output. */
|
|
137183
|
+
function parseReadGroupOutput(output) {
|
|
137184
|
+
const results = [];
|
|
137185
|
+
const lines = output.split("\n");
|
|
137186
|
+
let currentPath;
|
|
137187
|
+
let currentLines = [];
|
|
137188
|
+
const finishSection = (path, contentLines) => {
|
|
137189
|
+
if (path === void 0) return;
|
|
137190
|
+
const text = contentLines.join("\n");
|
|
137191
|
+
if (text.trimStart().startsWith("[ERROR]")) {
|
|
137192
|
+
results.push({
|
|
137193
|
+
filePath: path,
|
|
137194
|
+
lines: 0,
|
|
137195
|
+
failed: true,
|
|
137196
|
+
hasConflicts: false
|
|
137197
|
+
});
|
|
137198
|
+
return;
|
|
137199
|
+
}
|
|
137200
|
+
let lineCount = 0;
|
|
137201
|
+
for (const match of text.matchAll(/(\d+)\s+lines?\s+read\s+from\s+file/g)) {
|
|
137202
|
+
const count = match[1];
|
|
137203
|
+
if (count !== void 0) lineCount = parseInt(count, 10);
|
|
137204
|
+
}
|
|
137205
|
+
const hasConflicts = detectConflictMarkers(contentLines);
|
|
137206
|
+
results.push({
|
|
137207
|
+
filePath: path,
|
|
137208
|
+
lines: lineCount,
|
|
137209
|
+
failed: false,
|
|
137210
|
+
hasConflicts
|
|
137211
|
+
});
|
|
137212
|
+
};
|
|
137213
|
+
for (const line of lines) {
|
|
137214
|
+
const match = line.match(/^---\s+(.+?)\s+---$/);
|
|
137215
|
+
if (match !== null) {
|
|
137216
|
+
finishSection(currentPath, currentLines);
|
|
137217
|
+
currentPath = match[1];
|
|
137218
|
+
currentLines = [];
|
|
137219
|
+
} else if (currentPath !== void 0) currentLines.push(line);
|
|
137220
|
+
}
|
|
137221
|
+
finishSection(currentPath, currentLines);
|
|
137222
|
+
return results;
|
|
137223
|
+
}
|
|
137224
|
+
const readGroupListSummary = (toolCall, result, ctx) => {
|
|
137225
|
+
if (result.is_error) return renderTruncated(toolCall, result, ctx);
|
|
137226
|
+
if (!ctx.expanded) return [];
|
|
137227
|
+
const entries = parseReadGroupOutput(result.output);
|
|
137228
|
+
if (entries.length === 0) return renderTruncated(toolCall, result, ctx);
|
|
137229
|
+
const shown = entries.slice(0, 15);
|
|
137230
|
+
const rows = shown.map((entry) => {
|
|
137231
|
+
const path = chalk.hex(ctx.colors.text)(entry.filePath);
|
|
137232
|
+
if (entry.failed) return `${path}${chalk.hex(ctx.colors.error)(t("readgroup.failed_suffix"))}`;
|
|
137233
|
+
let tail = chalk.dim(t("readgroup.lines_suffix", { count: String(entry.lines) }));
|
|
137234
|
+
if (entry.hasConflicts) tail += chalk.hex(ctx.colors.warning)(` ⚠ ${t("readgroup.conflict_suffix")}`);
|
|
137235
|
+
return `${path}${tail}`;
|
|
137236
|
+
});
|
|
137237
|
+
const remaining = entries.length - shown.length;
|
|
137238
|
+
if (remaining > 0) rows.push(chalk.dim(t("readgroup.more_files", { count: String(remaining) })));
|
|
137239
|
+
return [new Text(rows.map((row) => ` ${row}`).join("\n"), 0, 0)];
|
|
137240
|
+
};
|
|
137241
|
+
//#endregion
|
|
137061
137242
|
//#region src/tui/components/messages/tool-renderers/chip.ts
|
|
137062
137243
|
/**
|
|
137063
137244
|
* Header chip providers — produce a short "stat" suffix appended to the
|
|
@@ -137120,6 +137301,18 @@ const editChip = (toolCall) => {
|
|
|
137120
137301
|
};
|
|
137121
137302
|
const writeChip = (toolCall) => formatWriteChip(computeWriteStats(toolCall.args));
|
|
137122
137303
|
const readChip = (_toolCall, result) => pluralize(countNonEmptyLines(result.output), "line");
|
|
137304
|
+
const readGroupChip = (_toolCall, result) => {
|
|
137305
|
+
const entries = parseReadGroupOutput(result.output);
|
|
137306
|
+
if (entries.length === 0) return "";
|
|
137307
|
+
const failed = entries.filter((entry) => entry.failed).length;
|
|
137308
|
+
const files = pluralize(entries.length, "file");
|
|
137309
|
+
if (failed === entries.length) return `${files} · failed`;
|
|
137310
|
+
const lines = entries.reduce((sum, entry) => entry.failed ? sum : sum + entry.lines, 0);
|
|
137311
|
+
const parts = [files];
|
|
137312
|
+
if (lines > 0) parts.push(pluralize(lines, "line"));
|
|
137313
|
+
if (failed > 0) parts.push(`${String(failed)} failed`);
|
|
137314
|
+
return parts.join(" · ");
|
|
137315
|
+
};
|
|
137123
137316
|
const grepChip = (_toolCall, result) => {
|
|
137124
137317
|
const matches = countNonEmptyLines(result.output);
|
|
137125
137318
|
if (matches === 0) return "no matches";
|
|
@@ -137142,6 +137335,7 @@ const REGISTRY = {
|
|
|
137142
137335
|
Edit: editChip,
|
|
137143
137336
|
Write: writeChip,
|
|
137144
137337
|
Read: readChip,
|
|
137338
|
+
ReadGroup: readGroupChip,
|
|
137145
137339
|
ReadMediaFile: readMediaChip,
|
|
137146
137340
|
Grep: grepChip,
|
|
137147
137341
|
Glob: globChip,
|
|
@@ -137397,6 +137591,7 @@ const globSummary = withGlance(globGlance);
|
|
|
137397
137591
|
function pickResultRenderer(toolName) {
|
|
137398
137592
|
switch (toolName) {
|
|
137399
137593
|
case "Read": return readSummary;
|
|
137594
|
+
case "ReadGroup": return readGroupListSummary;
|
|
137400
137595
|
case "ReadMediaFile": return readMediaSummary;
|
|
137401
137596
|
case "Grep": return grepSummary;
|
|
137402
137597
|
case "Glob": return globSummary;
|
|
@@ -137741,12 +137936,12 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
137741
137936
|
writeStreamLang;
|
|
137742
137937
|
static WRITE_STREAM_TAIL_RAW_CHARS = 4096;
|
|
137743
137938
|
/**
|
|
137744
|
-
* Registered by a group container (`AgentGroupComponent
|
|
137745
|
-
*
|
|
137746
|
-
*
|
|
137747
|
-
*
|
|
137748
|
-
*
|
|
137749
|
-
*
|
|
137939
|
+
* Registered by a group container (`AgentGroupComponent`, the activity block)
|
|
137940
|
+
* when this component is borrowed as a hidden state container. Any state
|
|
137941
|
+
* change (subagent meta, phase, sub-tool, result, etc.) triggers a throttled
|
|
137942
|
+
* group re-render. `undefined` means no group is subscribed and standalone
|
|
137943
|
+
* rendering is unaffected. A ToolCallComponent can only belong to one group at
|
|
137944
|
+
* a time, so one listener slot is enough.
|
|
137750
137945
|
*/
|
|
137751
137946
|
onSnapshotChange;
|
|
137752
137947
|
static HEADER_END_INDEX = 2;
|
|
@@ -137808,6 +138003,28 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
137808
138003
|
this.rebuildBody();
|
|
137809
138004
|
this.notifySnapshotChange();
|
|
137810
138005
|
}
|
|
138006
|
+
/**
|
|
138007
|
+
* File-mutation diff of this call (Edit additions/deletions, Write
|
|
138008
|
+
* additions), for the group-level diff stat in the activity block header.
|
|
138009
|
+
* Undefined for non-mutating tools, unfinished calls, and failed calls: a
|
|
138010
|
+
* failure changed nothing, so it must not count towards the group total.
|
|
138011
|
+
*/
|
|
138012
|
+
diffContribution() {
|
|
138013
|
+
if (this.result === void 0 || this.result.is_error) return void 0;
|
|
138014
|
+
if (this.toolCall.name === "Edit") {
|
|
138015
|
+
const stats = computeEditStats(this.toolCall.args);
|
|
138016
|
+
if (stats.added === 0 && stats.removed === 0) return void 0;
|
|
138017
|
+
return stats;
|
|
138018
|
+
}
|
|
138019
|
+
if (this.toolCall.name === "Write") {
|
|
138020
|
+
const stats = computeWriteStats(this.toolCall.args);
|
|
138021
|
+
if (stats.lines === 0) return void 0;
|
|
138022
|
+
return {
|
|
138023
|
+
added: stats.lines,
|
|
138024
|
+
removed: 0
|
|
138025
|
+
};
|
|
138026
|
+
}
|
|
138027
|
+
}
|
|
137811
138028
|
updateToolCall(toolCall) {
|
|
137812
138029
|
this.toolCall = toolCall;
|
|
137813
138030
|
this.syncStreamingProgressTimer();
|
|
@@ -137922,10 +138139,10 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
137922
138139
|
this.ui?.requestRender();
|
|
137923
138140
|
}
|
|
137924
138141
|
/**
|
|
137925
|
-
* Lets group containers (
|
|
137926
|
-
* state changes. Registration immediately calls back so the group
|
|
137927
|
-
* the current
|
|
137928
|
-
*
|
|
138142
|
+
* Lets group containers (the agent group, the activity block) subscribe to
|
|
138143
|
+
* this card's state changes. Registration immediately calls back so the group
|
|
138144
|
+
* receives the current state without separately calling
|
|
138145
|
+
* getSubagentSnapshot. Pass `undefined` to unsubscribe.
|
|
137929
138146
|
*/
|
|
137930
138147
|
setSnapshotListener(cb) {
|
|
137931
138148
|
this.onSnapshotChange = cb;
|
|
@@ -137952,35 +138169,6 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
137952
138169
|
latestActivity
|
|
137953
138170
|
};
|
|
137954
138171
|
}
|
|
137955
|
-
/**
|
|
137956
|
-
* Used by `ReadGroupComponent` to sum line counts across same-step Read
|
|
137957
|
-
* cards. `lines` matches the single-card chip
|
|
137958
|
-
* (`pluralize(countNonEmptyLines(...), 'line')`) so group and card counts do
|
|
137959
|
-
* not drift.
|
|
137960
|
-
*/
|
|
137961
|
-
getReadSnapshot() {
|
|
137962
|
-
const args = this.toolCall.args;
|
|
137963
|
-
const filePathRaw = args["file_path"] ?? args["path"];
|
|
137964
|
-
const filePath = typeof filePathRaw === "string" ? makeWorkspaceRelativePath(filePathRaw, this.workspaceDir) : void 0;
|
|
137965
|
-
if (this.result === void 0) return {
|
|
137966
|
-
toolCallId: this.toolCall.id,
|
|
137967
|
-
filePath,
|
|
137968
|
-
phase: "pending",
|
|
137969
|
-
lines: 0
|
|
137970
|
-
};
|
|
137971
|
-
if (this.result.is_error === true) return {
|
|
137972
|
-
toolCallId: this.toolCall.id,
|
|
137973
|
-
filePath,
|
|
137974
|
-
phase: "failed",
|
|
137975
|
-
lines: 0
|
|
137976
|
-
};
|
|
137977
|
-
return {
|
|
137978
|
-
toolCallId: this.toolCall.id,
|
|
137979
|
-
filePath,
|
|
137980
|
-
phase: "done",
|
|
137981
|
-
lines: countNonEmptyLines(this.result.output)
|
|
137982
|
-
};
|
|
137983
|
-
}
|
|
137984
138172
|
get toolCallView() {
|
|
137985
138173
|
return this.toolCall;
|
|
137986
138174
|
}
|
|
@@ -138791,77 +138979,6 @@ function formatActivityLine(verb, toolName, args, workspaceDir) {
|
|
|
138791
138979
|
return keyArg ? `${verb} ${toolName} (${keyArg})` : `${verb} ${toolName}`;
|
|
138792
138980
|
}
|
|
138793
138981
|
//#endregion
|
|
138794
|
-
//#region src/tui/utils/ui-preferences.ts
|
|
138795
|
-
/**
|
|
138796
|
-
* TUI-local UI preferences persisted to `<dataDir>/ui-preferences.json`.
|
|
138797
|
-
* Distinct from agent/SDK config files (which are owned by core) — this file
|
|
138798
|
-
* stores lightweight view-state knobs owned by the TUI itself, e.g. whether
|
|
138799
|
-
* the empty-session provider hint has been dismissed by the user.
|
|
138800
|
-
*/
|
|
138801
|
-
const EMPTY = {};
|
|
138802
|
-
function getUiPreferencesPath() {
|
|
138803
|
-
return join(getDataDir(), SCREAM_CODE_UI_PREFERENCES_FILE_NAME);
|
|
138804
|
-
}
|
|
138805
|
-
function readUiPreferences() {
|
|
138806
|
-
try {
|
|
138807
|
-
const file = getUiPreferencesPath();
|
|
138808
|
-
if (!existsSync(file)) return { ...EMPTY };
|
|
138809
|
-
const raw = readFileSync(file, "utf8");
|
|
138810
|
-
const parsed = JSON.parse(raw);
|
|
138811
|
-
return {
|
|
138812
|
-
...EMPTY,
|
|
138813
|
-
...parsed
|
|
138814
|
-
};
|
|
138815
|
-
} catch {
|
|
138816
|
-
return { ...EMPTY };
|
|
138817
|
-
}
|
|
138818
|
-
}
|
|
138819
|
-
function writeUiPreferences(prefs) {
|
|
138820
|
-
try {
|
|
138821
|
-
const file = getUiPreferencesPath();
|
|
138822
|
-
mkdirSync(dirname$1(file), { recursive: true });
|
|
138823
|
-
writeFileSync(file, JSON.stringify(prefs, null, 2), "utf8");
|
|
138824
|
-
} catch {}
|
|
138825
|
-
}
|
|
138826
|
-
function isEmptySessionHintDismissed() {
|
|
138827
|
-
return readUiPreferences().emptySessionHintDismissed === true;
|
|
138828
|
-
}
|
|
138829
|
-
/** Toggle the empty-session hint on/off (Ctrl+B). Returns the new state:
|
|
138830
|
-
* true = hidden, false = shown. Persisted immediately. */
|
|
138831
|
-
function toggleEmptySessionHint() {
|
|
138832
|
-
const prefs = readUiPreferences();
|
|
138833
|
-
const dismissed = prefs.emptySessionHintDismissed !== true;
|
|
138834
|
-
prefs.emptySessionHintDismissed = dismissed;
|
|
138835
|
-
writeUiPreferences(prefs);
|
|
138836
|
-
return dismissed;
|
|
138837
|
-
}
|
|
138838
|
-
/** Whether the per-turn elapsed marker is enabled (default on). */
|
|
138839
|
-
function isTurnElapsedEnabled() {
|
|
138840
|
-
return readUiPreferences().turnElapsedEnabled !== false;
|
|
138841
|
-
}
|
|
138842
|
-
/** Toggle the per-turn elapsed marker via /snaptimer. Returns the new state:
|
|
138843
|
-
* true = shown, false = hidden. Persisted immediately. */
|
|
138844
|
-
function toggleTurnElapsed() {
|
|
138845
|
-
const prefs = readUiPreferences();
|
|
138846
|
-
const enabled = prefs.turnElapsedEnabled !== false;
|
|
138847
|
-
prefs.turnElapsedEnabled = !enabled;
|
|
138848
|
-
writeUiPreferences(prefs);
|
|
138849
|
-
return !enabled;
|
|
138850
|
-
}
|
|
138851
|
-
/** Whether user messages render with a highlight background block (default on). */
|
|
138852
|
-
function isUserMessageHighlightEnabled() {
|
|
138853
|
-
return readUiPreferences().userMessageHighlightEnabled !== false;
|
|
138854
|
-
}
|
|
138855
|
-
/** Toggle the user-message highlight block via /hl. Returns the new state:
|
|
138856
|
-
* true = highlighted, false = default (no background). Persisted immediately. */
|
|
138857
|
-
function toggleUserMessageHighlight() {
|
|
138858
|
-
const prefs = readUiPreferences();
|
|
138859
|
-
const enabled = prefs.userMessageHighlightEnabled !== false;
|
|
138860
|
-
prefs.userMessageHighlightEnabled = !enabled;
|
|
138861
|
-
writeUiPreferences(prefs);
|
|
138862
|
-
return !enabled;
|
|
138863
|
-
}
|
|
138864
|
-
//#endregion
|
|
138865
138982
|
//#region src/tui/components/messages/user-message.ts
|
|
138866
138983
|
var UserMessageComponent = class {
|
|
138867
138984
|
color;
|
|
@@ -139052,7 +139169,7 @@ function isRevokeAnchorComponent(child) {
|
|
|
139052
139169
|
return child instanceof UserMessageComponent || child instanceof SkillActivationComponent;
|
|
139053
139170
|
}
|
|
139054
139171
|
function isRevokeContextComponent(child) {
|
|
139055
|
-
return child instanceof UserMessageComponent || child instanceof AssistantMessageComponent || child instanceof ThinkingComponent || child instanceof ToolCallComponent || child instanceof ActivityGroupComponent || child instanceof AgentGroupComponent || child instanceof
|
|
139172
|
+
return child instanceof UserMessageComponent || child instanceof AssistantMessageComponent || child instanceof ThinkingComponent || child instanceof ToolCallComponent || child instanceof ActivityGroupComponent || child instanceof AgentGroupComponent || child instanceof SkillActivationComponent || child instanceof BackgroundAgentStatusComponent || child instanceof StatusMessageComponent || child instanceof CompactionComponent;
|
|
139056
139173
|
}
|
|
139057
139174
|
function renderWelcome(host) {
|
|
139058
139175
|
if (host.state.transcriptContainer.children.some((child) => child instanceof WelcomeComponent)) return;
|
|
@@ -141021,18 +141138,6 @@ function isExpandable(obj) {
|
|
|
141021
141138
|
function isPlanExpandable(obj) {
|
|
141022
141139
|
return typeof obj === "object" && obj !== null && "setPlanExpanded" in obj && typeof obj.setPlanExpanded === "function";
|
|
141023
141140
|
}
|
|
141024
|
-
/**
|
|
141025
|
-
* Reads a component's current expansion state. Components that only accept
|
|
141026
|
-
* `setExpanded` (or that return something other than a boolean) yield
|
|
141027
|
-
* undefined, so callers fall back to their own last value instead of guessing.
|
|
141028
|
-
*/
|
|
141029
|
-
function readExpanded(obj) {
|
|
141030
|
-
if (typeof obj !== "object" || obj === null || !("isExpanded" in obj)) return void 0;
|
|
141031
|
-
const reader = obj.isExpanded;
|
|
141032
|
-
if (typeof reader !== "function") return void 0;
|
|
141033
|
-
const value = reader.call(obj);
|
|
141034
|
-
return typeof value === "boolean" ? value : void 0;
|
|
141035
|
-
}
|
|
141036
141141
|
function hasDispose(value) {
|
|
141037
141142
|
return typeof value === "object" && value !== null && "dispose" in value && typeof value.dispose === "function";
|
|
141038
141143
|
}
|
|
@@ -141602,6 +141707,88 @@ async function handleHighlightCommand(host, _args) {
|
|
|
141602
141707
|
host.state.ui.requestRender();
|
|
141603
141708
|
}
|
|
141604
141709
|
//#endregion
|
|
141710
|
+
//#region src/tui/utils/transcript-repaint.ts
|
|
141711
|
+
function repaintTranscript(host) {
|
|
141712
|
+
for (const child of host.state.transcriptContainer.children) {
|
|
141713
|
+
const repaintable = child;
|
|
141714
|
+
if (typeof repaintable.invalidate === "function") repaintable.invalidate();
|
|
141715
|
+
}
|
|
141716
|
+
host.state.ui.requestRender();
|
|
141717
|
+
}
|
|
141718
|
+
//#endregion
|
|
141719
|
+
//#region src/tui/commands/blockrows.ts
|
|
141720
|
+
/**
|
|
141721
|
+
* /blockrows — Tune the activity block's row budgets: how tall a collapsed block
|
|
141722
|
+
* is, how much of a tool result and of a reasoning run an expanded block shows.
|
|
141723
|
+
*
|
|
141724
|
+
* Two steps: pick the budget, then pick the value. Choices are persisted to
|
|
141725
|
+
* ui-preferences.json (so they survive restarts) and applied to the transcript
|
|
141726
|
+
* that is already on screen, not just to blocks created afterwards.
|
|
141727
|
+
*/
|
|
141728
|
+
function showSettingPicker(host) {
|
|
141729
|
+
host.mountEditorReplacement(new ChoicePickerComponent({
|
|
141730
|
+
title: t("blockrows.title"),
|
|
141731
|
+
hint: t("blockrows.hint"),
|
|
141732
|
+
colors: host.state.theme.colors,
|
|
141733
|
+
options: ACTIVITY_LINE_SETTINGS.map((setting) => ({
|
|
141734
|
+
value: setting.key,
|
|
141735
|
+
label: t(setting.label),
|
|
141736
|
+
description: t("blockrows.current", { value: String(getActivityLineValue(setting.key)) })
|
|
141737
|
+
})),
|
|
141738
|
+
onSelect: (value) => {
|
|
141739
|
+
if (isActivityLineKey(value)) showValuePicker(host, value);
|
|
141740
|
+
},
|
|
141741
|
+
onCancel: () => {
|
|
141742
|
+
host.restoreEditor();
|
|
141743
|
+
}
|
|
141744
|
+
}));
|
|
141745
|
+
}
|
|
141746
|
+
function showValuePicker(host, key) {
|
|
141747
|
+
const setting = findActivityLineSetting(key);
|
|
141748
|
+
host.mountEditorReplacement(new ChoicePickerComponent({
|
|
141749
|
+
title: t(setting.label),
|
|
141750
|
+
hint: t("blockrows.value_hint"),
|
|
141751
|
+
currentValue: String(getActivityLineValue(key)),
|
|
141752
|
+
colors: host.state.theme.colors,
|
|
141753
|
+
options: setting.values.map((value) => ({
|
|
141754
|
+
value: String(value),
|
|
141755
|
+
label: String(value)
|
|
141756
|
+
})),
|
|
141757
|
+
onSelect: (value) => {
|
|
141758
|
+
const parsed = Number(value);
|
|
141759
|
+
if (!Number.isFinite(parsed)) return;
|
|
141760
|
+
setActivityLineValue(key, parsed);
|
|
141761
|
+
host.restoreEditor();
|
|
141762
|
+
host.showStatus(t("blockrows.saved", {
|
|
141763
|
+
label: t(setting.label),
|
|
141764
|
+
value: String(parsed)
|
|
141765
|
+
}), host.state.theme.colors.success);
|
|
141766
|
+
repaintTranscript(host);
|
|
141767
|
+
},
|
|
141768
|
+
onCancel: () => {
|
|
141769
|
+
showSettingPicker(host);
|
|
141770
|
+
}
|
|
141771
|
+
}));
|
|
141772
|
+
}
|
|
141773
|
+
async function handleBlockRowsCommand(host, _args) {
|
|
141774
|
+
showSettingPicker(host);
|
|
141775
|
+
}
|
|
141776
|
+
//#endregion
|
|
141777
|
+
//#region src/tui/commands/codebg.ts
|
|
141778
|
+
/**
|
|
141779
|
+
* /codebg — Toggle the background panel behind fenced code blocks.
|
|
141780
|
+
*
|
|
141781
|
+
* On (default): each code line is painted as a full-width band, which separates
|
|
141782
|
+
* code from prose at a glance. Off: the band disappears and code is set apart by
|
|
141783
|
+
* its own colour, the one-cell gutter and the language label only.
|
|
141784
|
+
* Persisted to ui-preferences.json, so it survives restarts.
|
|
141785
|
+
*/
|
|
141786
|
+
async function handleCodeBgCommand(host, _args) {
|
|
141787
|
+
const enabled = toggleCodeBlockPanel();
|
|
141788
|
+
host.showStatus(enabled ? t("codebg.enabled") : t("codebg.disabled"), enabled ? host.state.theme.colors.success : host.state.theme.colors.textDim);
|
|
141789
|
+
repaintTranscript(host);
|
|
141790
|
+
}
|
|
141791
|
+
//#endregion
|
|
141605
141792
|
//#region src/tui/commands/like.ts
|
|
141606
141793
|
function promptTextInput$1(host, title, opts) {
|
|
141607
141794
|
const { promise, resolve } = Promise.withResolvers();
|
|
@@ -144446,6 +144633,12 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
144446
144633
|
case "hl":
|
|
144447
144634
|
await handleHighlightCommand(host, args);
|
|
144448
144635
|
return;
|
|
144636
|
+
case "blockrows":
|
|
144637
|
+
await handleBlockRowsCommand(host, args);
|
|
144638
|
+
return;
|
|
144639
|
+
case "codebg":
|
|
144640
|
+
await handleCodeBgCommand(host, args);
|
|
144641
|
+
return;
|
|
144449
144642
|
case "mcp":
|
|
144450
144643
|
await handleMcpCommand(host, args);
|
|
144451
144644
|
return;
|
|
@@ -144579,4 +144772,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
144579
144772
|
}
|
|
144580
144773
|
}
|
|
144581
144774
|
//#endregion
|
|
144582
|
-
export {
|
|
144775
|
+
export { handleExportMdCommand as $, readSessionIndex as $n, SubagentSlots as $t, langFromPath as A, startManualEmbeddingDownload as An, toggleEmptySessionHint as At, charsForTokenBudget as B, getLogDir as Bn, OSC11_QUERY as Bt, getDaemonInstructions as C, buildSkillSlashCommands as Cn, showStatusReport as Ct, renderDiffLines as D, setExperimentalFlags as Dn, createMarkdownTheme as Dt, ToolCallComponent as E, isExperimentalFlagEnabled as En, createEditorTheme as Et, replaceTabs as F, loadTuiConfig as Fn, parseOsc11BackgroundTheme as Ft, WelcomeComponent as G, PRODUCT_NAME as Gn, TERMINAL_FOCUS_IN as Gt, getSharedSpeedTracker as H, CLI_COMMAND_NAME as Hn, OSC11_RESPONSE_PREFIX as Ht, sanitizeShellOutput as I, saveTuiConfig as In, DISABLE_TERMINAL_FOCUS_REPORTING as It, resetBreathingClock as J, saveCatalogCache as Jn, TERMINAL_THEME_LIGHT as Jt, BREATHE_CYCLE_MS as K, DEFAULT_CATALOG_URL as Kn, TERMINAL_FOCUS_OUT as Kt, SkillActivationComponent as L, detectShellEnvironment as Ln, DISABLE_TERMINAL_THEME_REPORTING as Lt, ThinkingComponent as M, PULSE_WAVE_FRAMES as Mn, contrastTextHex as Mt, NoticeMessageComponent as N, TuiConfigParseError as Nn, getColorPalette as Nt, renderDiffLinesClustered as O, getKnowledgeStore as On, isEmptySessionHintDismissed as Ot, StatusMessageComponent as P, TuiLikePreferencesSchema as Pn, detectTerminalTheme as Pt, handleExportDebugZipCommand as Q, appendSessionIndexEntry as Qn, lerpGradient as Qt, AssistantMessageComponent as R, getDataDir as Rn, ENABLE_TERMINAL_FOCUS_REPORTING as Rt, handleCcCommand as S, getNoActiveSessionMessage as Sn, clearInfoPanelState as St, UserMessageComponent as T, sortSlashCommands as Tn, resolveThemeSync as Tt, BackgroundAgentStatusComponent as U, CLI_UI_MODE as Un, OSC11_RESPONSE_PREFIX_NO_ESC as Ut, estimateTokens as V, detectInstallSource as Vn, OSC11_RESPONSE as Vt, AgentGroupComponent as W, CLI_USER_AGENT_PRODUCT as Wn, QUERY_TERMINAL_THEME as Wt, clearGoalState as X, encodeWorkDirKey as Xn, isStreaming as Xt, CompactionComponent as Y, ScreamHarness as Yn, isBusy as Yt, refineGoal as Z, normalizeWorkDir as Zn, FooterComponent as Zt, selectUpdateTarget as _, SESSION_TIPS as _n, showModelPicker as _t, handleExtensionCommand as a, STATUS_BULLET as an, log as ar, changeThinkingLevel as at, appendJsonlLine as b, getCtrlDHint as bn, refreshProviderBalance as bt, hasDispose as c, formatErrorMessage as cn, isOrphanedToolCallError as cr, handleBotCommand as ct, MoonLoader as d, serializeToolResultOutput as dn, handleFusionPlanCommand as dt, handleTraceCommand as en, removeSessionIndexEntry as er, handleForkCommand as et, formatMemoryMemoForInjection as f, stringValue as fn, handleModelCommand as ft, handleUpdateCommand as g, MAIN_AGENT_ID$1 as gn, handleYoloCommand as gt, handleMcpCommand as h, EXIT_CONFIRM_WINDOW_MS as hn, handleWolfpackCommand as ht, buildRoleAdditionalText as i, printableChar as in, flushDiagnosticLogs as ir, toTerminalHyperlink as it, CachedContainer as j, PIXEL_PULSE_FRAMES as jn, createThemeStyles as jt, highlightLines as k, isEmbeddingModelCached as kn, isTurnElapsedEnabled as kt, isExpandable as l, isTodoItemShape as ln, ErrorCodes as lr, handleCompactCommand as lt, handleChannelCommand as m, EMPTY_SESSION_HINT_URL as mn, handleThemeCommand as mt, clearEvalPanelState as n, handleConnectCommand as nn, resolveScreamHome as nr, handleTitleCommand as nt, handleSkillCommand as o, appendStreamingArgsPreview as on, resolveGlobalLogPath as or, getModelCycleLevel as ot, handleMemoryCommand as p, truncateErrorMessage as pn, handlePlanCommand as pt, getBreathingFrame as q, fetchCatalog as qn, TERMINAL_THEME_DARK as qt, openUrl as r, handleLogoutCommand as rn, MemoryMemoStore as rr, isInternalMessage as rt, disposeChildren as s, argsRecord as sn, isScreamError as sr, handleAutoCommand as st, dispatchInput as t, handleSearchCommand as tn, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as tr, handleInitCommand as tt, isPlanExpandable as u, parseStreamingArgs as un, SCREAM_ERROR_INFO as ur, handleEditorCommand as ut, refreshUpdateCache as v, TIP_ROTATION_INTERVAL_MS as vn, showPermissionPicker as vt, handleRevokeCommand as w, BUILTIN_SLASH_COMMANDS as wn, showUsage as wt, readJsonlFile as x, getLlmNotSetMessage as xn, supportsBalance as xt, readUpdateCache as y, getCtrlCHint as yn, showSettingsSelector as yt, ActivityGroupComponent as z, getInputHistoryFile as zn, ENABLE_TERMINAL_THEME_REPORTING as zt };
|