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.
@@ -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$1, 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";
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-CzJKBIoN.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--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$1(cwd, normalizedPath));
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$1(options.skillMdPath);
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$1(workDir);
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$1(projectRoot, dir);
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$1(input.cwd);
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.writeText(this.metadataPath, text);
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$1(input.request.outputPath) : resolve$1(`${input.summary.id}.zip`);
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$1(entry.sessionDir);
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$1(entry.workDir)
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$1(parent), resolve$1(child));
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$1(workDir);
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: "#1e232b",
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: "#f2f4f8",
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-dominant mapping (keyword,
131899
- * function, built_in primary; strings success; numbers → warning;
131900
- * comments → textDim). Kept distinct from the shared preview theme in
131901
- * code-highlight-theme.ts on purpose — markdown code blocks use the green
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.success);
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: chalk.hex(colors.diffAdded),
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.text)(stripHash(text)),
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 `${panel(" ")}${panel(content)}${panel(" ".repeat(filler))}${label}${panel(" ".repeat(padCells))}`;
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-Dp2TUSjQ.mjs");
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-Dp2TUSjQ.mjs");
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$2 = 200;
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$2);
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
- const THROTTLE_MS$1 = 200;
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
- /** Token count for a text block; empty text is zero, unlike the rate estimator. */
134978
- function countTokens(text) {
134979
- return text.trim().length === 0 ? 0 : estimateTokens(text);
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
- tools = [];
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
- /** Token estimates per borrowed card, keyed by the result text they counted. */
135136
- toolTokenCache = /* @__PURE__ */ new WeakMap();
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.tools.length === 0 && this.thinkingText.trim().length === 0;
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.tools.some((entry) => entry.tc === tc)) return;
135162
- this.tools.push({
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
- /** Updates the reasoning text; `live` drives the streaming rate in the header. */
135174
- setThinking(text, live) {
135175
- if (text === this.thinkingText && live === this.thinkingLive) return;
135176
- const structureChanged = text.length === 0 !== (this.thinkingText.length === 0);
135177
- this.thinkingText = text;
135178
- this.thinkingLive = live;
135179
- if (structureChanged) this.flushNow();
135180
- else this.scheduleFlush();
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
- if (!this.thinkingLive) return;
135184
- this.thinkingLive = false;
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 this.stopSpinner();
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 entry of this.tools) entry.tc.setExpanded(expanded);
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 entry of this.tools) entry.tc.invalidate();
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$1);
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 (this.tools.length > 0) parts.push(t("activitygroup.tools", { count: String(this.tools.length) }));
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 speed = this.thinkingLive ? getSharedSpeedTracker().getSpeed() : 0;
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
- /** Collapsed state: at most three rows, matching the documented preview. */
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 hasThinking = this.thinkingText.trim().length > 0;
135280
- const latest = this.tools.at(-1);
135281
- if (latest !== void 0) {
135282
- const prefix = hasThinking ? BRANCH_PIPE : BRANCH_LAST;
135283
- const row = this.toolRow(latest.tc, width, 3);
135284
- this.bodyContainer.addChild(new Text(`${prefix}${this.statusGlyph(row)} ${row.header}`, 0, 0));
135285
- }
135286
- if (hasThinking) this.bodyContainer.addChild(new Text(this.thinkingRow(width), 0, 0));
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
- /** Expanded state: one branch per tool plus a reasoning excerpt. */
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 hiddenTools = 0;
135296
- let shownTools = 0;
135297
- toolRows.forEach(({ row }, index) => {
135298
- const cost = 1 + row.body.length;
135299
- if (shownTools > 0 && used + cost > 60) {
135300
- hiddenTools += 1;
135301
- return;
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 isLast = index === this.tools.length - 1 && !hasThinking && hiddenTools === 0;
135304
- this.bodyContainer.addChild(new Text(`${isLast ? BRANCH_LAST : BRANCH_FIRST}${this.statusGlyph(row)} ${row.header}`, 0, 0));
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) this.bodyContainer.addChild(new Text(`${continuation}${line}`, 0, 0));
135307
- used += cost;
135308
- shownTools += 1;
135309
- });
135310
- if (hiddenTools > 0) {
135311
- const prefix = hasThinking ? BRANCH_FIRST : BRANCH_LAST;
135312
- const summary = t("activitygroup.tools_hidden", { count: String(hiddenTools) });
135313
- this.bodyContainer.addChild(new Text(`${prefix}${chalk.dim(summary)}`, 0, 0));
135314
- used += 1;
135315
- }
135316
- if (!hasThinking) return;
135317
- const thinkingLines = this.thinkingLines();
135318
- let excerptBudget = Math.min(10, Math.max(0, 60 - used - 1));
135319
- if (excerptBudget < 10 && thinkingLines.length > excerptBudget) excerptBudget -= 1;
135320
- if (excerptBudget <= 0) return;
135321
- this.bodyContainer.addChild(new Text(`${BRANCH_LAST} ${chalk.dim("·")} ${chalk.hex(colors.roleThinking)(t("activitygroup.thinking_label"))}`, 0, 0));
135322
- const lines = thinkingLines;
135323
- const inner = Math.max(1, width - visibleWidth(THINKING_BODY_PREFIX));
135324
- const height = (line) => new WrappedLine(THINKING_BODY_PREFIX, THINKING_BODY_PREFIX, line).render(inner).length;
135325
- let selected = this.thinkingLive ? lines.slice(-10) : lines.slice(0, 10);
135326
- while (selected.length > 1 && selected.reduce((sum, line) => sum + height(line), 0) > excerptBudget) selected = this.thinkingLive ? selected.slice(1) : selected.slice(0, -1);
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) > excerptBudget) selected = [truncateToWidth(first, inner * excerptBudget, "…")];
135707
+ if (selected.length === 1 && first !== void 0 && height(first) > budget) selected = [truncateToWidth(first, inner * budget, "…")];
135329
135708
  for (const line of selected) {
135330
- const row = new WrappedLine(THINKING_BODY_PREFIX, THINKING_BODY_PREFIX, chalk.hex(colors.roleThinking)(line));
135331
- this.bodyContainer.addChild(row);
135332
- used += height(line);
135709
+ components.push(new WrappedLine(continuation, continuation, chalk.hex(colors.roleThinking)(line)));
135710
+ cost += height(line);
135333
135711
  }
135334
- const hidden = lines.length - selected.length;
135335
- if (hidden > 0) {
135336
- const hint = this.thinkingLive ? t("activitygroup.thinking_earlier", { count: String(hidden) }) : t("activitygroup.thinking_more", { count: String(hidden) });
135337
- this.bodyContainer.addChild(new Text(`${THINKING_BODY_PREFIX}${chalk.dim(hint)}`, 0, 0));
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 reasoning line clipped to whatever space
135381
- * the row can still hold at this width, so a narrow terminal cannot wrap the
135382
- * block into a fourth row. The token estimate lives in the block header only.
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
- thinkingRow(width) {
135385
- const first = this.thinkingLines()[0] ?? "";
135775
+ thinkingSummaryRow(segment, width, isLast) {
135776
+ const first = thinkingLines(segment.text)[0] ?? "";
135386
135777
  const label = t("activitygroup.thinking_summary", { summary: "" });
135387
- const bareFixed = BRANCH_WIDTH + visibleWidth(SEPARATOR) + visibleWidth(label);
135388
- const cells = Math.max(1, Math.min(42, width - bareFixed));
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} ${chalk.dim("·")} ${summary}`;
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.tools.some((entry) => entry.tc.resultView === void 0)) this.rebuild(width);
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` or
137745
- * `ReadGroupComponent`) when this component is borrowed as a hidden state
137746
- * container. Any state change (subagent meta, phase, sub-tool, result, etc.)
137747
- * triggers a throttled group re-render. `undefined` means no group is
137748
- * subscribed and standalone rendering is unaffected. A ToolCallComponent can
137749
- * only belong to one group at a time, so one listener slot is enough.
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 (AgentGroup or ReadGroup) subscribe to this card's
137926
- * state changes. Registration immediately calls back so the group receives
137927
- * the current snapshot without separately calling getSubagentSnapshot or
137928
- * getReadSnapshot. Pass `undefined` to unsubscribe.
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 ReadGroupComponent || child instanceof SkillActivationComponent || child instanceof BackgroundAgentStatusComponent || child instanceof StatusMessageComponent || child instanceof CompactionComponent;
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 { getBreathingFrame as $, encodeWorkDirKey as $n, isStreaming as $t, ToolCallComponent as A, setExperimentalFlags as An, showUsage as At, sanitizeShellOutput as B, detectShellEnvironment as Bn, DISABLE_TERMINAL_THEME_REPORTING as Bt, handleCcCommand as C, getCtrlDHint as Cn, showModelPicker as Ct, isEmptySessionHintDismissed as D, BUILTIN_SLASH_COMMANDS as Dn, supportsBalance as Dt, UserMessageComponent as E, buildSkillSlashCommands as En, refreshProviderBalance as Et, CachedContainer as F, PULSE_WAVE_FRAMES as Fn, contrastTextHex as Ft, AssistantMessageComponent as G, CLI_COMMAND_NAME as Gn, OSC11_RESPONSE_PREFIX as Gt, ReadGroupComponent as H, getInputHistoryFile as Hn, ENABLE_TERMINAL_THEME_REPORTING as Ht, ThinkingComponent as I, TuiConfigParseError as In, getColorPalette as It, estimateTokens as J, PRODUCT_NAME as Jn, TERMINAL_FOCUS_IN as Jt, ActivityGroupComponent as K, CLI_UI_MODE as Kn, OSC11_RESPONSE_PREFIX_NO_ESC as Kt, NoticeMessageComponent as L, TuiLikePreferencesSchema as Ln, detectTerminalTheme as Lt, renderDiffLinesClustered as M, isEmbeddingModelCached as Mn, createEditorTheme as Mt, highlightLines as N, startManualEmbeddingDownload as Nn, createMarkdownTheme as Nt, isTurnElapsedEnabled as O, sortSlashCommands as On, clearInfoPanelState as Ot, langFromPath as P, PIXEL_PULSE_FRAMES as Pn, createThemeStyles as Pt, BREATHE_CYCLE_MS as Q, ScreamHarness as Qn, isBusy as Qt, StatusMessageComponent as R, loadTuiConfig as Rn, parseOsc11BackgroundTheme as Rt, readJsonlFile as S, getCtrlCHint as Sn, handleYoloCommand as St, handleRevokeCommand as T, getNoActiveSessionMessage as Tn, showSettingsSelector as Tt, parseReadGroupOutput as U, getLogDir as Un, OSC11_QUERY as Ut, SkillActivationComponent as V, getDataDir as Vn, ENABLE_TERMINAL_FOCUS_REPORTING as Vt, BackgroundAgentStatusComponent as W, detectInstallSource as Wn, OSC11_RESPONSE as Wt, AgentGroupComponent as X, fetchCatalog as Xn, TERMINAL_THEME_DARK as Xt, getSharedSpeedTracker as Y, DEFAULT_CATALOG_URL as Yn, TERMINAL_FOCUS_OUT as Yt, WelcomeComponent as Z, saveCatalogCache as Zn, TERMINAL_THEME_LIGHT as Zt, handleUpdateCommand as _, EMPTY_SESSION_HINT_URL as _n, handleFusionPlanCommand as _t, handleExtensionCommand as a, handleConnectCommand as an, resolveScreamHome as ar, handleExportMdCommand as at, readUpdateCache as b, SESSION_TIPS as bn, handleThemeCommand as bt, hasDispose as c, STATUS_BULLET as cn, log as cr, handleTitleCommand as ct, readExpanded as d, formatErrorMessage as dn, isOrphanedToolCallError as dr, changeThinkingLevel as dt, FooterComponent as en, normalizeWorkDir as er, resetBreathingClock as et, MoonLoader as f, isTodoItemShape as fn, ErrorCodes as fr, getModelCycleLevel as ft, handleMcpCommand as g, truncateErrorMessage as gn, handleEditorCommand as gt, handleChannelCommand as h, stringValue as hn, handleCompactCommand as ht, buildRoleAdditionalText as i, handleSearchCommand as in, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as ir, handleExportDebugZipCommand as it, renderDiffLines as j, getKnowledgeStore as jn, resolveThemeSync as jt, toggleEmptySessionHint as k, isExperimentalFlagEnabled as kn, showStatusReport as kt, isExpandable as l, appendStreamingArgsPreview as ln, resolveGlobalLogPath as lr, isInternalMessage as lt, handleMemoryCommand as m, serializeToolResultOutput as mn, handleBotCommand as mt, clearEvalPanelState as n, SubagentSlots as nn, readSessionIndex as nr, clearGoalState as nt, handleSkillCommand as o, handleLogoutCommand as on, MemoryMemoStore as or, handleForkCommand as ot, formatMemoryMemoForInjection as p, parseStreamingArgs as pn, SCREAM_ERROR_INFO as pr, handleAutoCommand as pt, charsForTokenBudget as q, CLI_USER_AGENT_PRODUCT as qn, QUERY_TERMINAL_THEME as qt, openUrl as r, handleTraceCommand as rn, removeSessionIndexEntry as rr, refineGoal as rt, disposeChildren as s, printableChar as sn, flushDiagnosticLogs as sr, handleInitCommand as st, dispatchInput as t, lerpGradient as tn, appendSessionIndexEntry as tr, CompactionComponent as tt, isPlanExpandable as u, argsRecord as un, isScreamError as ur, toTerminalHyperlink as ut, selectUpdateTarget as v, EXIT_CONFIRM_WINDOW_MS as vn, handleModelCommand as vt, getDaemonInstructions as w, getLlmNotSetMessage as wn, showPermissionPicker as wt, appendJsonlLine as x, TIP_ROTATION_INTERVAL_MS as xn, handleWolfpackCommand as xt, refreshUpdateCache as y, MAIN_AGENT_ID$1 as yn, handlePlanCommand as yt, replaceTabs as z, saveTuiConfig as zn, DISABLE_TERMINAL_FOCUS_REPORTING as zt };
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 };