omnius 1.0.89 → 1.0.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3373,8 +3373,9 @@ function hashMismatchMessage(filePath, expectedHash, actualHash) {
3373
3373
  function fileContextHeader(filePath, content, range) {
3374
3374
  const totalLines = content.split("\n").length;
3375
3375
  const hash = contentHash(content);
3376
- const shown = range && (range.offset !== void 0 || range.limit !== void 0) ? ` showing=${range.offset ?? 1}-${range.limit ? (range.offset ?? 1) + range.limit - 1 : "end"}` : "";
3377
- return `[FILE CONTEXT path=${filePath} sha256=${hash} lines=${totalLines}${shown}]`;
3376
+ const shortHash2 = hash.slice(0, 12);
3377
+ const scope = range && (range.offset !== void 0 || range.limit !== void 0) ? `lines ${range.offset ?? 1}-${range.limit ? (range.offset ?? 1) + range.limit - 1 : totalLines} of ${totalLines}` : `${totalLines} lines`;
3378
+ return `[FILE CONTEXT | ${filePath} | ${scope} | sha256:${shortHash2}]`;
3378
3379
  }
3379
3380
  var init_edit_metadata = __esm({
3380
3381
  "packages/execution/dist/tools/edit-metadata.js"() {
@@ -548095,6 +548096,60 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
548095
548096
  fs11.appendFileSync(path12.join(trajDir, `trajectories.jsonl`), JSON.stringify(trajectory) + "\n", "utf-8");
548096
548097
  } catch {
548097
548098
  }
548099
+ let filesEdited;
548100
+ let testsRun;
548101
+ let provenanceAnchors;
548102
+ try {
548103
+ filesEdited = [...this._taskState.modifiedFiles.keys()].slice(0, 32);
548104
+ } catch {
548105
+ }
548106
+ try {
548107
+ const shellCommands = [];
548108
+ const provText = [];
548109
+ for (const entry of toolCallLog) {
548110
+ if (entry.name === "shell" || entry.name === "background_run") {
548111
+ const m2 = entry.argsKey.match(/(?:^|,)command=([^,]*)/);
548112
+ if (m2 && m2[1])
548113
+ shellCommands.push(m2[1]);
548114
+ }
548115
+ if (entry.outputPreview)
548116
+ provText.push(entry.outputPreview);
548117
+ }
548118
+ const testRunners = [
548119
+ /\b(?:pnpm|npm|yarn)\s+(?:run\s+)?(?:test|test:[\w-]+|typecheck|lint)\b[^&|;]*/,
548120
+ /\bvitest\s+(?:run\s+)?[\w\-/.@\s]*/,
548121
+ /\bjest\s+[\w\-/.@\s]*/,
548122
+ /\bpytest\s+[\w\-/.@\s]*/,
548123
+ /\bmocha\s+[\w\-/.@\s]*/,
548124
+ /\bgo\s+test\s+[\w./\s-]+/,
548125
+ /\bcargo\s+test\b[\w\s-]*/
548126
+ ];
548127
+ const tests = /* @__PURE__ */ new Set();
548128
+ for (const cmd of shellCommands) {
548129
+ for (const re of testRunners) {
548130
+ const m2 = cmd.match(re);
548131
+ if (m2) {
548132
+ const label = m2[0].replace(/\s+/g, " ").trim();
548133
+ if (label && label.length <= 80)
548134
+ tests.add(label);
548135
+ }
548136
+ }
548137
+ }
548138
+ if (tests.size > 0)
548139
+ testsRun = [...tests].slice(0, 16);
548140
+ const anchorRe = /(urn:omnius:[^\s"'<>)]+|aiwg-prov:[^\s"'<>)]+)/g;
548141
+ const anchors = /* @__PURE__ */ new Set();
548142
+ for (const text of provText) {
548143
+ for (const m2 of text.matchAll(anchorRe)) {
548144
+ const a2 = m2[0];
548145
+ if (a2.length <= 120)
548146
+ anchors.add(a2);
548147
+ }
548148
+ }
548149
+ if (anchors.size > 0)
548150
+ provenanceAnchors = [...anchors].slice(0, 16);
548151
+ } catch {
548152
+ }
548098
548153
  return {
548099
548154
  completed,
548100
548155
  turns: messages2.filter((m2) => m2.role === "assistant").length,
@@ -548104,7 +548159,10 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
548104
548159
  completionTokens,
548105
548160
  estimatedTokens,
548106
548161
  summary,
548107
- durationMs
548162
+ durationMs,
548163
+ filesEdited,
548164
+ testsRun,
548165
+ provenanceAnchors
548108
548166
  };
548109
548167
  }
548110
548168
  // -------------------------------------------------------------------------
@@ -562057,6 +562115,618 @@ var init_command_registry = __esm({
562057
562115
  }
562058
562116
  });
562059
562117
 
562118
+ // packages/cli/src/tui/text-selection.ts
562119
+ var text_selection_exports = {};
562120
+ __export(text_selection_exports, {
562121
+ SEL_END: () => SEL_END,
562122
+ SEL_START: () => SEL_START,
562123
+ TextSelection: () => TextSelection,
562124
+ computeHeaderButtons: () => computeHeaderButtons,
562125
+ copyText: () => copyText,
562126
+ hitTestHeaderButton: () => hitTestHeaderButton,
562127
+ pasteText: () => pasteText,
562128
+ renderHeaderButtons: () => renderHeaderButtons,
562129
+ setHoveredButton: () => setHoveredButton,
562130
+ setPressedButton: () => setPressedButton,
562131
+ setUpdateBadgeRegion: () => setUpdateBadgeRegion,
562132
+ stripAnsi: () => stripAnsi,
562133
+ visibleLength: () => visibleLength
562134
+ });
562135
+ import { execSync as execSync47 } from "node:child_process";
562136
+ function stripAnsi(s2) {
562137
+ return s2.replace(/\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\)/g, "");
562138
+ }
562139
+ function visibleLength(s2) {
562140
+ return stripAnsi(s2).length;
562141
+ }
562142
+ function copyText(text) {
562143
+ try {
562144
+ const platform7 = process.platform;
562145
+ if (platform7 === "darwin") {
562146
+ execSync47("pbcopy", { input: text, timeout: 3e3 });
562147
+ return true;
562148
+ }
562149
+ if (platform7 === "win32") {
562150
+ execSync47("clip", { input: text, timeout: 3e3 });
562151
+ return true;
562152
+ }
562153
+ for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
562154
+ try {
562155
+ execSync47(tool, { input: text, timeout: 3e3 });
562156
+ return true;
562157
+ } catch {
562158
+ continue;
562159
+ }
562160
+ }
562161
+ if (!_clipboardAutoInstallAttempted) {
562162
+ _clipboardAutoInstallAttempted = true;
562163
+ try {
562164
+ execSync47("which apt-get", { timeout: 2e3, stdio: "pipe" });
562165
+ try {
562166
+ execSync47("sudo -n apt-get install -y xclip 2>/dev/null", { timeout: 15e3, stdio: "pipe" });
562167
+ execSync47("xclip -selection clipboard", { input: text, timeout: 3e3 });
562168
+ return true;
562169
+ } catch {
562170
+ }
562171
+ } catch {
562172
+ }
562173
+ }
562174
+ } catch {
562175
+ }
562176
+ try {
562177
+ const b64 = Buffer.from(text).toString("base64");
562178
+ process.stdout.write(`\x1B]52;c;${b64}\x07`);
562179
+ return true;
562180
+ } catch {
562181
+ }
562182
+ return false;
562183
+ }
562184
+ function pasteText() {
562185
+ try {
562186
+ const platform7 = process.platform;
562187
+ if (platform7 === "darwin") {
562188
+ return execSync47("pbpaste", { timeout: 3e3, encoding: "utf8" }).trimEnd();
562189
+ }
562190
+ if (platform7 === "win32") {
562191
+ return execSync47("powershell -command Get-Clipboard", { timeout: 3e3, encoding: "utf8" }).trimEnd();
562192
+ }
562193
+ for (const tool of [
562194
+ { cmd: "xclip", args: ["-selection", "clipboard", "-o"] },
562195
+ { cmd: "xsel", args: ["--clipboard", "--output"] },
562196
+ { cmd: "wl-paste", args: [] }
562197
+ ]) {
562198
+ try {
562199
+ const result = execSync47(`${tool.cmd} ${tool.args.join(" ")}`, { timeout: 3e3, encoding: "utf8" });
562200
+ return result.trimEnd();
562201
+ } catch {
562202
+ continue;
562203
+ }
562204
+ }
562205
+ } catch {
562206
+ }
562207
+ return null;
562208
+ }
562209
+ function computeHeaderButtons(_termWidth) {
562210
+ return [];
562211
+ }
562212
+ function setHoveredButton(cmd) {
562213
+ _hoveredButtonCmd = cmd;
562214
+ }
562215
+ function setPressedButton(cmd) {
562216
+ _pressedButtonCmd = cmd;
562217
+ }
562218
+ function renderHeaderButtons(_termWidth) {
562219
+ return "";
562220
+ }
562221
+ function setUpdateBadgeRegion(active, startCol, length4) {
562222
+ _updateBadgeActive = active;
562223
+ _updateBadgeCol = startCol;
562224
+ _updateBadgeLen = length4;
562225
+ }
562226
+ function hitTestHeaderButton(row, col, termWidth) {
562227
+ if (_updateBadgeActive && row === 1 && col >= _updateBadgeCol && col < _updateBadgeCol + _updateBadgeLen) {
562228
+ return "/update";
562229
+ }
562230
+ const hdrRow = layout().headerContent;
562231
+ if (row === hdrRow) {
562232
+ if (col <= 3) return "header-prev";
562233
+ if (col >= termWidth - 3) return "header-next";
562234
+ const btnDefs = [
562235
+ { cmd: "/help", label: " help " },
562236
+ { cmd: "/voice", label: " voice " },
562237
+ { cmd: "/model", label: " model " },
562238
+ { cmd: "/cohere", label: " cohere " }
562239
+ ];
562240
+ let btnEnd = termWidth - 4;
562241
+ for (let i2 = btnDefs.length - 1; i2 >= 0; i2--) {
562242
+ const btn = btnDefs[i2];
562243
+ const btnStart = btnEnd - btn.label.length;
562244
+ if (col >= btnStart && col <= btnEnd) return btn.cmd;
562245
+ btnEnd = btnStart - 1;
562246
+ }
562247
+ }
562248
+ return null;
562249
+ }
562250
+ var SEL_BG, SEL_FG, SEL_START, SEL_END, TextSelection, _clipboardAutoInstallAttempted, _hoveredButtonCmd, _pressedButtonCmd, _updateBadgeActive, _updateBadgeCol, _updateBadgeLen;
562251
+ var init_text_selection = __esm({
562252
+ "packages/cli/src/tui/text-selection.ts"() {
562253
+ "use strict";
562254
+ init_layout2();
562255
+ SEL_BG = 37;
562256
+ SEL_FG = 30;
562257
+ SEL_START = `\x1B[${SEL_FG}m\x1B[48;5;${SEL_BG}m`;
562258
+ SEL_END = `\x1B[0m`;
562259
+ TextSelection = class {
562260
+ _selection = null;
562261
+ _active = false;
562262
+ // true while mouse button is held
562263
+ _blockModeArmed = false;
562264
+ // Ctrl+Shift+B pressed, next click starts block select
562265
+ _provider;
562266
+ constructor(provider) {
562267
+ this._provider = provider;
562268
+ }
562269
+ /** Whether a selection currently exists */
562270
+ get hasSelection() {
562271
+ return this._selection !== null;
562272
+ }
562273
+ /** Whether we're actively dragging */
562274
+ get isDragging() {
562275
+ return this._active;
562276
+ }
562277
+ /** Get the current selection range (or null) */
562278
+ get selection() {
562279
+ return this._selection;
562280
+ }
562281
+ /** Arm block selection mode — next click starts rectangular select */
562282
+ armBlockMode() {
562283
+ this._blockModeArmed = true;
562284
+ }
562285
+ /** Clear the current selection */
562286
+ clear() {
562287
+ this._selection = null;
562288
+ this._active = false;
562289
+ this._blockModeArmed = false;
562290
+ }
562291
+ /**
562292
+ * Handle mouse press (button 0, M suffix in SGR).
562293
+ * Starts a new selection from the click position.
562294
+ */
562295
+ onMousePress(row, col) {
562296
+ const mode = this._blockModeArmed ? "block" : "line";
562297
+ this._blockModeArmed = false;
562298
+ this._selection = {
562299
+ anchor: { row, col },
562300
+ current: { row, col },
562301
+ mode
562302
+ };
562303
+ this._active = true;
562304
+ }
562305
+ /**
562306
+ * Handle mouse drag (button 32, M suffix in SGR).
562307
+ * Extends the selection to the current cursor position.
562308
+ */
562309
+ onMouseDrag(row, col) {
562310
+ if (!this._active || !this._selection) return;
562311
+ this._selection.current = { row, col };
562312
+ }
562313
+ /**
562314
+ * Handle mouse release (button 0, m suffix in SGR).
562315
+ * Finalizes the selection.
562316
+ */
562317
+ onMouseRelease(row, col) {
562318
+ if (!this._active || !this._selection) return;
562319
+ this._selection.current = { row, col };
562320
+ this._active = false;
562321
+ if (this._selection.anchor.row === this._selection.current.row && this._selection.anchor.col === this._selection.current.col) {
562322
+ this._selection = null;
562323
+ }
562324
+ }
562325
+ /**
562326
+ * Compute which content buffer indices and column ranges are selected.
562327
+ * Returns an array of { bufferIdx, startCol, endCol } for each selected line.
562328
+ * Columns are 0-based visible character positions.
562329
+ */
562330
+ getSelectedRanges() {
562331
+ if (!this._selection) return [];
562332
+ const { anchor, current, mode } = this._selection;
562333
+ const top = this._provider.getScrollRegionTop();
562334
+ const height = this._provider.getContentHeight();
562335
+ const offset = this._provider.getScrollOffset();
562336
+ const totalLines = this._provider.getContentLines().length;
562337
+ const startIdx = Math.max(0, totalLines - height - offset);
562338
+ const anchorBufIdx = startIdx + (anchor.row - top);
562339
+ const currentBufIdx = startIdx + (current.row - top);
562340
+ const minRow = Math.min(anchorBufIdx, currentBufIdx);
562341
+ const maxRow = Math.max(anchorBufIdx, currentBufIdx);
562342
+ const minCol = Math.min(anchor.col, current.col);
562343
+ const maxCol = Math.max(anchor.col, current.col);
562344
+ const ranges = [];
562345
+ if (mode === "block") {
562346
+ for (let idx = minRow; idx <= maxRow; idx++) {
562347
+ if (idx >= 0 && idx < totalLines) {
562348
+ ranges.push({ bufferIdx: idx, startCol: minCol - 1, endCol: maxCol - 1 });
562349
+ }
562350
+ }
562351
+ } else {
562352
+ const isForward = anchorBufIdx < currentBufIdx || anchorBufIdx === currentBufIdx && anchor.col <= current.col;
562353
+ const startR = isForward ? anchorBufIdx : currentBufIdx;
562354
+ const endR = isForward ? currentBufIdx : anchorBufIdx;
562355
+ const startC = isForward ? anchor.col - 1 : current.col - 1;
562356
+ const endC = isForward ? current.col - 1 : anchor.col - 1;
562357
+ for (let idx = startR; idx <= endR; idx++) {
562358
+ if (idx < 0 || idx >= totalLines) continue;
562359
+ const lineLen = visibleLength(this._provider.getContentLines()[idx] ?? "");
562360
+ if (idx === startR && idx === endR) {
562361
+ ranges.push({ bufferIdx: idx, startCol: startC, endCol: endC });
562362
+ } else if (idx === startR) {
562363
+ ranges.push({ bufferIdx: idx, startCol: startC, endCol: Math.max(lineLen, startC) });
562364
+ } else if (idx === endR) {
562365
+ ranges.push({ bufferIdx: idx, startCol: 0, endCol: endC });
562366
+ } else {
562367
+ ranges.push({ bufferIdx: idx, startCol: 0, endCol: lineLen });
562368
+ }
562369
+ }
562370
+ }
562371
+ return ranges;
562372
+ }
562373
+ /**
562374
+ * Apply selection highlighting to a content line for rendering.
562375
+ * Takes the original ANSI line and returns it with selection highlight applied.
562376
+ *
562377
+ * @param line Original content line (with ANSI codes)
562378
+ * @param startCol 0-based visible start column to highlight
562379
+ * @param endCol 0-based visible end column to highlight (inclusive)
562380
+ * @returns Line with selection highlight overlay
562381
+ */
562382
+ static applyHighlight(line, startCol, endCol) {
562383
+ const plain = stripAnsi(line);
562384
+ if (startCol > plain.length || endCol < 0 || startCol > endCol) return line;
562385
+ const sc = Math.max(0, startCol);
562386
+ const ec = Math.min(plain.length - 1, endCol);
562387
+ let result = "";
562388
+ let visPos = 0;
562389
+ let i2 = 0;
562390
+ let inHighlight = false;
562391
+ while (i2 < line.length) {
562392
+ const escMatch = line.slice(i2).match(/^(\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\))/);
562393
+ if (escMatch) {
562394
+ if (inHighlight) {
562395
+ result += SEL_END + escMatch[0] + SEL_START;
562396
+ } else {
562397
+ result += escMatch[0];
562398
+ }
562399
+ i2 += escMatch[0].length;
562400
+ continue;
562401
+ }
562402
+ if (visPos === sc && !inHighlight) {
562403
+ result += SEL_START;
562404
+ inHighlight = true;
562405
+ }
562406
+ result += line[i2];
562407
+ if (visPos === ec && inHighlight) {
562408
+ result += SEL_END;
562409
+ inHighlight = false;
562410
+ }
562411
+ visPos++;
562412
+ i2++;
562413
+ }
562414
+ if (inHighlight) result += SEL_END;
562415
+ return result;
562416
+ }
562417
+ /**
562418
+ * Get the selected text content (plain text, no ANSI codes).
562419
+ * For block mode, each line is joined with newline.
562420
+ * For line mode, text flows continuously with newlines between lines.
562421
+ */
562422
+ getSelectedText() {
562423
+ const ranges = this.getSelectedRanges();
562424
+ if (ranges.length === 0) return "";
562425
+ const lines = this._provider.getContentLines();
562426
+ const parts = [];
562427
+ for (const { bufferIdx, startCol, endCol } of ranges) {
562428
+ const raw = lines[bufferIdx] ?? "";
562429
+ const plain = stripAnsi(raw);
562430
+ const selected = plain.slice(
562431
+ Math.max(0, startCol),
562432
+ Math.min(plain.length, endCol + 1)
562433
+ );
562434
+ parts.push(selected);
562435
+ }
562436
+ return parts.join("\n");
562437
+ }
562438
+ /**
562439
+ * Copy current selection to system clipboard.
562440
+ * Tries platform commands first, falls back to OSC 52.
562441
+ * Returns true if copy succeeded.
562442
+ */
562443
+ copyToClipboard() {
562444
+ const text = this.getSelectedText();
562445
+ if (!text) return false;
562446
+ return copyText(text);
562447
+ }
562448
+ };
562449
+ _clipboardAutoInstallAttempted = false;
562450
+ _hoveredButtonCmd = null;
562451
+ _pressedButtonCmd = null;
562452
+ _updateBadgeActive = false;
562453
+ _updateBadgeCol = 0;
562454
+ _updateBadgeLen = 0;
562455
+ }
562456
+ });
562457
+
562458
+ // packages/cli/src/tui/task-complete-box.ts
562459
+ var task_complete_box_exports = {};
562460
+ __export(task_complete_box_exports, {
562461
+ buildBoxLines: () => buildBoxLines,
562462
+ deriveTitle: () => deriveTitle,
562463
+ detectProvenanceAnchors: () => detectProvenanceAnchors,
562464
+ detectTestRuns: () => detectTestRuns,
562465
+ renderTaskCompleteBox: () => renderTaskCompleteBox
562466
+ });
562467
+ function deriveTitle(task) {
562468
+ if (!task || !task.trim()) return "Task Complete";
562469
+ const cleaned = task.replace(/```[\s\S]*?```/g, " ").replace(/`[^`]+`/g, " ").replace(/\([^)]*\)/g, " ").replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
562470
+ if (!cleaned) return "Task Complete";
562471
+ const firstSentence = cleaned.split(/(?<=[.!?:])\s/)[0] ?? cleaned;
562472
+ const words = firstSentence.split(/\s+/).filter((w) => w.length > 0);
562473
+ if (words.length === 0) return "Task Complete";
562474
+ if (words.length <= 10) return words.join(" ");
562475
+ return words.slice(0, 10).join(" ") + "…";
562476
+ }
562477
+ function formatDuration2(ms) {
562478
+ if (ms < 1e3) return `${ms}ms`;
562479
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
562480
+ const m2 = Math.floor(ms / 6e4);
562481
+ const s2 = Math.floor(ms % 6e4 / 1e3);
562482
+ return `${m2}m${s2.toString().padStart(2, "0")}s`;
562483
+ }
562484
+ function formatTokenCount(t2) {
562485
+ const n2 = t2.total > 0 ? t2.total : t2.estimated;
562486
+ if (n2 <= 0) return "";
562487
+ if (n2 < 1e3) return `${n2} tok`;
562488
+ if (n2 < 1e6) return `${(n2 / 1e3).toFixed(1)}k tok`;
562489
+ return `${(n2 / 1e6).toFixed(2)}M tok`;
562490
+ }
562491
+ function buildMetricsChip(data) {
562492
+ const parts = [];
562493
+ parts.push(`${data.turns} turn${data.turns === 1 ? "" : "s"}`);
562494
+ parts.push(`${data.toolCalls} call${data.toolCalls === 1 ? "" : "s"}`);
562495
+ if (data.tokens && data.durationMs > 0) {
562496
+ const total = data.tokens.total > 0 ? data.tokens.total : data.tokens.estimated;
562497
+ if (total > 0) {
562498
+ const tps = total / (data.durationMs / 1e3);
562499
+ if (tps >= 1) parts.push(`${tps.toFixed(0)} tps`);
562500
+ }
562501
+ }
562502
+ parts.push(formatDuration2(data.durationMs));
562503
+ if (data.tokens) {
562504
+ const tok = formatTokenCount(data.tokens);
562505
+ if (tok) parts.push(tok);
562506
+ }
562507
+ return parts.join(" · ");
562508
+ }
562509
+ function wrapToWidth(text, width) {
562510
+ if (width <= 0) return [text];
562511
+ const out = [];
562512
+ for (const paragraph of text.split(/\n/)) {
562513
+ if (paragraph.length === 0) {
562514
+ out.push("");
562515
+ continue;
562516
+ }
562517
+ let remaining = paragraph;
562518
+ while (remaining.length > width) {
562519
+ let breakAt = remaining.lastIndexOf(" ", width);
562520
+ if (breakAt <= 0) breakAt = width;
562521
+ out.push(remaining.slice(0, breakAt).trimEnd());
562522
+ remaining = remaining.slice(breakAt).trimStart();
562523
+ }
562524
+ out.push(remaining);
562525
+ }
562526
+ return out;
562527
+ }
562528
+ function wrapListItems(items, width) {
562529
+ if (items.length === 0) return [];
562530
+ const sep4 = " · ";
562531
+ const lines = [];
562532
+ let current = "";
562533
+ for (const item of items) {
562534
+ const candidate = current === "" ? item : current + sep4 + item;
562535
+ if (stripAnsi(candidate).length <= width) {
562536
+ current = candidate;
562537
+ } else {
562538
+ if (current) lines.push(current);
562539
+ if (stripAnsi(item).length > width) {
562540
+ const chunks = wrapToWidth(item, width);
562541
+ lines.push(...chunks.slice(0, -1));
562542
+ current = chunks[chunks.length - 1] ?? "";
562543
+ } else {
562544
+ current = item;
562545
+ }
562546
+ }
562547
+ }
562548
+ if (current) lines.push(current);
562549
+ return lines;
562550
+ }
562551
+ function buildTopBorder(title, metrics2, width) {
562552
+ const inner = Math.max(4, width - 2);
562553
+ const titleVisible = stripAnsi(title);
562554
+ const metricsVisible = stripAnsi(metrics2);
562555
+ const titleChip = ` ${titleVisible} `;
562556
+ const titleSpan = titleChip.length + 2;
562557
+ const metricsChip = metricsVisible ? ` ${metricsVisible} ` : "";
562558
+ const metricsSpan = metricsChip.length > 0 ? metricsChip.length + 2 : 0;
562559
+ let titleSegment;
562560
+ let metricsSegment;
562561
+ let fillerWidth;
562562
+ if (titleSpan + metricsSpan + 4 <= inner) {
562563
+ titleSegment = `${FG_BORDER}[${FG_TITLE}${titleChip}${RESET}${FG_BORDER}]`;
562564
+ metricsSegment = metricsChip ? `${FG_BORDER}[${FG_METRIC}${metricsChip}${RESET}${FG_BORDER}]` : "";
562565
+ fillerWidth = inner - titleSpan - metricsSpan - 2;
562566
+ } else if (titleSpan + 4 <= inner) {
562567
+ titleSegment = `${FG_BORDER}[${FG_TITLE}${titleChip}${RESET}${FG_BORDER}]`;
562568
+ metricsSegment = "";
562569
+ fillerWidth = inner - titleSpan - 2;
562570
+ } else {
562571
+ const room = Math.max(3, inner - 8);
562572
+ const truncated = titleVisible.length > room ? titleVisible.slice(0, Math.max(1, room - 1)) + "…" : titleVisible;
562573
+ titleSegment = `${FG_BORDER}[${FG_TITLE} ${truncated} ${RESET}${FG_BORDER}]`;
562574
+ metricsSegment = "";
562575
+ fillerWidth = Math.max(0, inner - (truncated.length + 4) - 2);
562576
+ }
562577
+ const leadDash = `${FG_BORDER}${BOX_H}`;
562578
+ const trailDash = `${FG_BORDER}${BOX_H}${RESET}`;
562579
+ const filler = fillerWidth > 0 ? `${FG_BORDER}${BOX_H.repeat(fillerWidth)}` : "";
562580
+ return `${FG_BORDER}${BOX_TL}${leadDash}${titleSegment}${filler}${metricsSegment}${trailDash}${FG_BORDER}${BOX_TR}${RESET}`;
562581
+ }
562582
+ function buildBottomBorder(width) {
562583
+ const inner = Math.max(0, width - 2);
562584
+ return `${FG_BORDER}${BOX_BL}${BOX_H.repeat(inner)}${BOX_BR}${RESET}`;
562585
+ }
562586
+ function buildInnerDivider(width) {
562587
+ const inner = Math.max(0, width - 2);
562588
+ return `${FG_BORDER}${BOX_TJ_L}${BOX_H.repeat(inner)}${BOX_TJ_R}${RESET}`;
562589
+ }
562590
+ function buildContentRow(content, width) {
562591
+ const innerWidth = Math.max(1, width - 4);
562592
+ const visible = stripAnsi(content);
562593
+ let padded = content;
562594
+ if (visible.length < innerWidth) {
562595
+ padded = content + " ".repeat(innerWidth - visible.length);
562596
+ } else if (visible.length > innerWidth) {
562597
+ padded = content;
562598
+ }
562599
+ return `${FG_BORDER}${BOX_V}${RESET} ${padded} ${FG_BORDER}${BOX_V}${RESET}`;
562600
+ }
562601
+ function buildEmptyRow(width) {
562602
+ return buildContentRow("", width);
562603
+ }
562604
+ function buildLabeledFooterLines(label, items, width) {
562605
+ if (items.length === 0) return [];
562606
+ const innerWidth = Math.max(8, width - 4);
562607
+ const labelText = `${FG_LABEL}${label}:${RESET} `;
562608
+ const labelVisible = `${label}: `.length;
562609
+ const continuation = " ".repeat(labelVisible);
562610
+ const itemListWidth = Math.max(8, innerWidth - labelVisible);
562611
+ const wrapped = wrapListItems(items, itemListWidth);
562612
+ if (wrapped.length === 0) return [];
562613
+ const lines = [];
562614
+ lines.push(buildContentRow(`${labelText}${wrapped[0]}`, width));
562615
+ for (let i2 = 1; i2 < wrapped.length; i2++) {
562616
+ lines.push(buildContentRow(`${continuation}${wrapped[i2]}`, width));
562617
+ }
562618
+ return lines;
562619
+ }
562620
+ function buildBoxLines(data, width) {
562621
+ const w = Math.max(40, width);
562622
+ const title = deriveTitle(data.task);
562623
+ const metrics2 = buildMetricsChip(data);
562624
+ const lines = [];
562625
+ lines.push(buildTopBorder(`${GREEN}✔${RESET} ${title}`, metrics2, w));
562626
+ const innerWidth = Math.max(1, w - 4);
562627
+ const bodyText = (data.summary ?? "").trim();
562628
+ const hasFooter = (data.testsRun?.length ?? 0) > 0 || (data.filesEdited?.length ?? 0) > 0 || (data.provenanceAnchors?.length ?? 0) > 0;
562629
+ if (bodyText || hasFooter) {
562630
+ lines.push(buildInnerDivider(w));
562631
+ }
562632
+ if (bodyText) {
562633
+ lines.push(buildEmptyRow(w));
562634
+ for (const para of bodyText.split(/\n/)) {
562635
+ const wrapped = wrapToWidth(para, innerWidth);
562636
+ for (const line of wrapped) {
562637
+ lines.push(buildContentRow(line, w));
562638
+ }
562639
+ }
562640
+ lines.push(buildEmptyRow(w));
562641
+ }
562642
+ if (hasFooter) {
562643
+ if (bodyText) lines.push(buildInnerDivider(w));
562644
+ if (data.testsRun?.length) {
562645
+ lines.push(...buildLabeledFooterLines("Tests", data.testsRun, w));
562646
+ }
562647
+ if (data.filesEdited?.length) {
562648
+ lines.push(...buildLabeledFooterLines("Files", data.filesEdited, w));
562649
+ }
562650
+ if (data.provenanceAnchors?.length) {
562651
+ lines.push(...buildLabeledFooterLines("Provenance", data.provenanceAnchors, w));
562652
+ }
562653
+ }
562654
+ lines.push(buildBottomBorder(w));
562655
+ return lines;
562656
+ }
562657
+ function renderTaskCompleteBox(host, data) {
562658
+ const blockId = `task-complete-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
562659
+ const frozen = {
562660
+ task: data.task,
562661
+ summary: data.summary,
562662
+ turns: data.turns,
562663
+ toolCalls: data.toolCalls,
562664
+ durationMs: data.durationMs,
562665
+ tokens: data.tokens ?? null,
562666
+ filesEdited: data.filesEdited ? [...data.filesEdited] : [],
562667
+ testsRun: data.testsRun ? [...data.testsRun] : [],
562668
+ provenanceAnchors: data.provenanceAnchors ? [...data.provenanceAnchors] : []
562669
+ };
562670
+ host.registerDynamicBlock(blockId, (width) => buildBoxLines(frozen, width));
562671
+ host.appendDynamicBlock(blockId);
562672
+ return blockId;
562673
+ }
562674
+ function detectTestRuns(shellCommands) {
562675
+ const out = /* @__PURE__ */ new Set();
562676
+ const runners = [
562677
+ /\b(?:pnpm|npm|yarn)\s+(?:run\s+)?(test|test:[\w-]+|typecheck|lint)\b/,
562678
+ /\bvitest\s+(?:run\s+)?[\w-/.@]*/,
562679
+ /\bjest\s+[\w-/.@]*/,
562680
+ /\bpytest\s+[\w-/.@]*/,
562681
+ /\bmocha\s+[\w-/.@]*/,
562682
+ /\bgo\s+test\s+[\w./]+/,
562683
+ /\bcargo\s+test\b[\w\s-]*/
562684
+ ];
562685
+ for (const cmd of shellCommands) {
562686
+ for (const re of runners) {
562687
+ const m2 = cmd.match(re);
562688
+ if (m2) {
562689
+ const label = m2[0].replace(/\s+/g, " ").trim();
562690
+ if (label.length <= 80) out.add(label);
562691
+ }
562692
+ }
562693
+ }
562694
+ return [...out].slice(0, 16);
562695
+ }
562696
+ function detectProvenanceAnchors(toolOutputs) {
562697
+ const out = /* @__PURE__ */ new Set();
562698
+ const re = /(urn:omnius:[^\s"'<>)]+|aiwg-prov:[^\s"'<>)]+)/g;
562699
+ for (const out_ of toolOutputs) {
562700
+ if (!out_) continue;
562701
+ for (const match of out_.matchAll(re)) {
562702
+ const anchor = match[0];
562703
+ if (anchor.length <= 120) out.add(anchor);
562704
+ }
562705
+ }
562706
+ return [...out].slice(0, 16);
562707
+ }
562708
+ var BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, BOX_TJ_L, BOX_TJ_R, RESET, GREEN, FG_BORDER, FG_TITLE, FG_METRIC, FG_LABEL;
562709
+ var init_task_complete_box = __esm({
562710
+ "packages/cli/src/tui/task-complete-box.ts"() {
562711
+ "use strict";
562712
+ init_text_selection();
562713
+ BOX_TL = "╭";
562714
+ BOX_TR = "╮";
562715
+ BOX_BL = "╰";
562716
+ BOX_BR = "╯";
562717
+ BOX_H = "─";
562718
+ BOX_V = "│";
562719
+ BOX_TJ_L = "├";
562720
+ BOX_TJ_R = "┤";
562721
+ RESET = "\x1B[0m";
562722
+ GREEN = "\x1B[38;5;154m";
562723
+ FG_BORDER = "\x1B[38;5;154m";
562724
+ FG_TITLE = "\x1B[1;38;5;154m";
562725
+ FG_METRIC = "\x1B[38;5;222m";
562726
+ FG_LABEL = "\x1B[38;5;147m";
562727
+ }
562728
+ });
562729
+
562060
562730
  // packages/cli/src/tui/render.ts
562061
562731
  var render_exports = {};
562062
562732
  __export(render_exports, {
@@ -562303,16 +562973,16 @@ function renderToolResult(toolName, success, output, verbose) {
562303
562973
  `);
562304
562974
  return;
562305
562975
  }
562306
- renderCodePreview(output, prefix, maxW, 6);
562976
+ renderCodePreview(output, prefix, maxW, 10);
562307
562977
  return;
562308
562978
  }
562309
562979
  case "shell":
562310
562980
  case "background_run": {
562311
- renderShellOutput(output, success, prefix, maxW, 8);
562981
+ renderShellOutput(output, success, prefix, maxW, 10);
562312
562982
  return;
562313
562983
  }
562314
562984
  case "grep_search": {
562315
- renderShellOutput(output, success, prefix, maxW, 6);
562985
+ renderShellOutput(output, success, prefix, maxW, 10);
562316
562986
  return;
562317
562987
  }
562318
562988
  case "task_complete": {
@@ -562461,18 +563131,40 @@ function highlightToolOutput(line) {
562461
563131
  if (formatted !== line) return formatted;
562462
563132
  return c3.dim(line);
562463
563133
  }
562464
- function renderTaskComplete(summary, turns, toolCalls, durationMs, tokens) {
562465
- const duration = formatDuration2(durationMs);
562466
- const tokenStr = tokens ? ` ${formatTokenCount(tokens)}` : "";
563134
+ function renderTaskComplete(arg1, turns, toolCalls, durationMs, tokens) {
563135
+ const opts = typeof arg1 === "string" ? {
563136
+ summary: arg1,
563137
+ turns: turns ?? 0,
563138
+ toolCalls: toolCalls ?? 0,
563139
+ durationMs: durationMs ?? 0,
563140
+ tokens
563141
+ } : arg1;
563142
+ if (opts.host) {
563143
+ const { renderTaskCompleteBox: renderTaskCompleteBox2 } = (init_task_complete_box(), __toCommonJS(task_complete_box_exports));
563144
+ renderTaskCompleteBox2(opts.host, {
563145
+ task: opts.task ?? "",
563146
+ summary: opts.summary,
563147
+ turns: opts.turns,
563148
+ toolCalls: opts.toolCalls,
563149
+ durationMs: opts.durationMs,
563150
+ tokens: opts.tokens ?? null,
563151
+ filesEdited: opts.filesEdited,
563152
+ testsRun: opts.testsRun,
563153
+ provenanceAnchors: opts.provenanceAnchors
563154
+ });
563155
+ return;
563156
+ }
563157
+ const duration = formatDuration3(opts.durationMs);
563158
+ const tokenStr = opts.tokens ? ` ${formatTokenCount2(opts.tokens)}` : "";
562467
563159
  process.stdout.write(`
562468
- ${c3.green("✔")} ${c3.bold("Task completed")} ${c3.dim(`(${turns} turns, ${toolCalls} tool calls, ${duration})`)}
563160
+ ${c3.green("✔")} ${c3.bold("Task completed")} ${c3.dim(`(${opts.turns} turns, ${opts.toolCalls} tool calls, ${duration})`)}
562469
563161
  `);
562470
563162
  if (tokenStr) {
562471
563163
  process.stdout.write(` ${c3.dim(tokenStr)}
562472
563164
  `);
562473
563165
  }
562474
- if (summary) {
562475
- const formatted = formatMarkdownBlock(wrapTaskCompleteSummary(summary));
563166
+ if (opts.summary) {
563167
+ const formatted = formatMarkdownBlock(wrapTaskCompleteSummary(opts.summary));
562476
563168
  const lines = formatted.split("\n");
562477
563169
  for (const line of lines) {
562478
563170
  process.stdout.write(` ${line}
@@ -562527,8 +563219,8 @@ function hangingIndentForPlainLine(line, width) {
562527
563219
  return capped(line.match(/^\s*/)?.[0].length ?? 0);
562528
563220
  }
562529
563221
  function renderTaskIncomplete(turns, toolCalls, durationMs, tokens) {
562530
- const duration = formatDuration2(durationMs);
562531
- const tokenStr = tokens ? ` ${formatTokenCount(tokens)}` : "";
563222
+ const duration = formatDuration3(durationMs);
563223
+ const tokenStr = tokens ? ` ${formatTokenCount2(tokens)}` : "";
562532
563224
  process.stdout.write(`
562533
563225
  ${c3.yellow("⚠")} ${c3.bold("Task incomplete")} ${c3.dim(`(${turns} turns, ${toolCalls} tool calls, ${duration})`)}
562534
563226
  `);
@@ -562538,7 +563230,7 @@ ${c3.yellow("⚠")} ${c3.bold("Task incomplete")} ${c3.dim(`(${turns} turns, ${t
562538
563230
  }
562539
563231
  process.stdout.write("\n");
562540
563232
  }
562541
- function formatTokenCount(tokens) {
563233
+ function formatTokenCount2(tokens) {
562542
563234
  if (tokens.total > 0) {
562543
563235
  return `Tokens: ${tokens.total.toLocaleString()}`;
562544
563236
  }
@@ -562838,7 +563530,7 @@ function formatToolArgs(toolName, args, verbose) {
562838
563530
  function truncStr(s2, max) {
562839
563531
  return s2.length > max ? s2.slice(0, max) + "..." : s2;
562840
563532
  }
562841
- function formatDuration2(ms) {
563533
+ function formatDuration3(ms) {
562842
563534
  if (ms < 1e3) return `${ms}ms`;
562843
563535
  const totalSecs = ms / 1e3;
562844
563536
  if (totalSecs < 60) return `${totalSecs.toFixed(1)}s`;
@@ -563112,7 +563804,7 @@ var init_render = __esm({
563112
563804
 
563113
563805
  // packages/cli/src/tui/voice-session.ts
563114
563806
  import { createServer as createServer4 } from "node:http";
563115
- import { spawn as spawn23, execSync as execSync47 } from "node:child_process";
563807
+ import { spawn as spawn23, execSync as execSync48 } from "node:child_process";
563116
563808
  import { EventEmitter as EventEmitter6 } from "node:events";
563117
563809
  function generateFrontendHTML() {
563118
563810
  return `<!DOCTYPE html>
@@ -570199,346 +570891,6 @@ var init_system_metrics = __esm({
570199
570891
  }
570200
570892
  });
570201
570893
 
570202
- // packages/cli/src/tui/text-selection.ts
570203
- var text_selection_exports = {};
570204
- __export(text_selection_exports, {
570205
- SEL_END: () => SEL_END,
570206
- SEL_START: () => SEL_START,
570207
- TextSelection: () => TextSelection,
570208
- computeHeaderButtons: () => computeHeaderButtons,
570209
- copyText: () => copyText,
570210
- hitTestHeaderButton: () => hitTestHeaderButton,
570211
- pasteText: () => pasteText,
570212
- renderHeaderButtons: () => renderHeaderButtons,
570213
- setHoveredButton: () => setHoveredButton,
570214
- setPressedButton: () => setPressedButton,
570215
- setUpdateBadgeRegion: () => setUpdateBadgeRegion,
570216
- stripAnsi: () => stripAnsi,
570217
- visibleLength: () => visibleLength
570218
- });
570219
- import { execSync as execSync48 } from "node:child_process";
570220
- function stripAnsi(s2) {
570221
- return s2.replace(/\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\)/g, "");
570222
- }
570223
- function visibleLength(s2) {
570224
- return stripAnsi(s2).length;
570225
- }
570226
- function copyText(text) {
570227
- try {
570228
- const platform7 = process.platform;
570229
- if (platform7 === "darwin") {
570230
- execSync48("pbcopy", { input: text, timeout: 3e3 });
570231
- return true;
570232
- }
570233
- if (platform7 === "win32") {
570234
- execSync48("clip", { input: text, timeout: 3e3 });
570235
- return true;
570236
- }
570237
- for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
570238
- try {
570239
- execSync48(tool, { input: text, timeout: 3e3 });
570240
- return true;
570241
- } catch {
570242
- continue;
570243
- }
570244
- }
570245
- if (!_clipboardAutoInstallAttempted) {
570246
- _clipboardAutoInstallAttempted = true;
570247
- try {
570248
- execSync48("which apt-get", { timeout: 2e3, stdio: "pipe" });
570249
- try {
570250
- execSync48("sudo -n apt-get install -y xclip 2>/dev/null", { timeout: 15e3, stdio: "pipe" });
570251
- execSync48("xclip -selection clipboard", { input: text, timeout: 3e3 });
570252
- return true;
570253
- } catch {
570254
- }
570255
- } catch {
570256
- }
570257
- }
570258
- } catch {
570259
- }
570260
- try {
570261
- const b64 = Buffer.from(text).toString("base64");
570262
- process.stdout.write(`\x1B]52;c;${b64}\x07`);
570263
- return true;
570264
- } catch {
570265
- }
570266
- return false;
570267
- }
570268
- function pasteText() {
570269
- try {
570270
- const platform7 = process.platform;
570271
- if (platform7 === "darwin") {
570272
- return execSync48("pbpaste", { timeout: 3e3, encoding: "utf8" }).trimEnd();
570273
- }
570274
- if (platform7 === "win32") {
570275
- return execSync48("powershell -command Get-Clipboard", { timeout: 3e3, encoding: "utf8" }).trimEnd();
570276
- }
570277
- for (const tool of [
570278
- { cmd: "xclip", args: ["-selection", "clipboard", "-o"] },
570279
- { cmd: "xsel", args: ["--clipboard", "--output"] },
570280
- { cmd: "wl-paste", args: [] }
570281
- ]) {
570282
- try {
570283
- const result = execSync48(`${tool.cmd} ${tool.args.join(" ")}`, { timeout: 3e3, encoding: "utf8" });
570284
- return result.trimEnd();
570285
- } catch {
570286
- continue;
570287
- }
570288
- }
570289
- } catch {
570290
- }
570291
- return null;
570292
- }
570293
- function computeHeaderButtons(_termWidth) {
570294
- return [];
570295
- }
570296
- function setHoveredButton(cmd) {
570297
- _hoveredButtonCmd = cmd;
570298
- }
570299
- function setPressedButton(cmd) {
570300
- _pressedButtonCmd = cmd;
570301
- }
570302
- function renderHeaderButtons(_termWidth) {
570303
- return "";
570304
- }
570305
- function setUpdateBadgeRegion(active, startCol, length4) {
570306
- _updateBadgeActive = active;
570307
- _updateBadgeCol = startCol;
570308
- _updateBadgeLen = length4;
570309
- }
570310
- function hitTestHeaderButton(row, col, termWidth) {
570311
- if (_updateBadgeActive && row === 1 && col >= _updateBadgeCol && col < _updateBadgeCol + _updateBadgeLen) {
570312
- return "/update";
570313
- }
570314
- const hdrRow = layout().headerContent;
570315
- if (row === hdrRow) {
570316
- if (col <= 3) return "header-prev";
570317
- if (col >= termWidth - 3) return "header-next";
570318
- const btnDefs = [
570319
- { cmd: "/help", label: " help " },
570320
- { cmd: "/voice", label: " voice " },
570321
- { cmd: "/model", label: " model " },
570322
- { cmd: "/cohere", label: " cohere " }
570323
- ];
570324
- let btnEnd = termWidth - 4;
570325
- for (let i2 = btnDefs.length - 1; i2 >= 0; i2--) {
570326
- const btn = btnDefs[i2];
570327
- const btnStart = btnEnd - btn.label.length;
570328
- if (col >= btnStart && col <= btnEnd) return btn.cmd;
570329
- btnEnd = btnStart - 1;
570330
- }
570331
- }
570332
- return null;
570333
- }
570334
- var SEL_BG, SEL_FG, SEL_START, SEL_END, TextSelection, _clipboardAutoInstallAttempted, _hoveredButtonCmd, _pressedButtonCmd, _updateBadgeActive, _updateBadgeCol, _updateBadgeLen;
570335
- var init_text_selection = __esm({
570336
- "packages/cli/src/tui/text-selection.ts"() {
570337
- "use strict";
570338
- init_layout2();
570339
- SEL_BG = 37;
570340
- SEL_FG = 30;
570341
- SEL_START = `\x1B[${SEL_FG}m\x1B[48;5;${SEL_BG}m`;
570342
- SEL_END = `\x1B[0m`;
570343
- TextSelection = class {
570344
- _selection = null;
570345
- _active = false;
570346
- // true while mouse button is held
570347
- _blockModeArmed = false;
570348
- // Ctrl+Shift+B pressed, next click starts block select
570349
- _provider;
570350
- constructor(provider) {
570351
- this._provider = provider;
570352
- }
570353
- /** Whether a selection currently exists */
570354
- get hasSelection() {
570355
- return this._selection !== null;
570356
- }
570357
- /** Whether we're actively dragging */
570358
- get isDragging() {
570359
- return this._active;
570360
- }
570361
- /** Get the current selection range (or null) */
570362
- get selection() {
570363
- return this._selection;
570364
- }
570365
- /** Arm block selection mode — next click starts rectangular select */
570366
- armBlockMode() {
570367
- this._blockModeArmed = true;
570368
- }
570369
- /** Clear the current selection */
570370
- clear() {
570371
- this._selection = null;
570372
- this._active = false;
570373
- this._blockModeArmed = false;
570374
- }
570375
- /**
570376
- * Handle mouse press (button 0, M suffix in SGR).
570377
- * Starts a new selection from the click position.
570378
- */
570379
- onMousePress(row, col) {
570380
- const mode = this._blockModeArmed ? "block" : "line";
570381
- this._blockModeArmed = false;
570382
- this._selection = {
570383
- anchor: { row, col },
570384
- current: { row, col },
570385
- mode
570386
- };
570387
- this._active = true;
570388
- }
570389
- /**
570390
- * Handle mouse drag (button 32, M suffix in SGR).
570391
- * Extends the selection to the current cursor position.
570392
- */
570393
- onMouseDrag(row, col) {
570394
- if (!this._active || !this._selection) return;
570395
- this._selection.current = { row, col };
570396
- }
570397
- /**
570398
- * Handle mouse release (button 0, m suffix in SGR).
570399
- * Finalizes the selection.
570400
- */
570401
- onMouseRelease(row, col) {
570402
- if (!this._active || !this._selection) return;
570403
- this._selection.current = { row, col };
570404
- this._active = false;
570405
- if (this._selection.anchor.row === this._selection.current.row && this._selection.anchor.col === this._selection.current.col) {
570406
- this._selection = null;
570407
- }
570408
- }
570409
- /**
570410
- * Compute which content buffer indices and column ranges are selected.
570411
- * Returns an array of { bufferIdx, startCol, endCol } for each selected line.
570412
- * Columns are 0-based visible character positions.
570413
- */
570414
- getSelectedRanges() {
570415
- if (!this._selection) return [];
570416
- const { anchor, current, mode } = this._selection;
570417
- const top = this._provider.getScrollRegionTop();
570418
- const height = this._provider.getContentHeight();
570419
- const offset = this._provider.getScrollOffset();
570420
- const totalLines = this._provider.getContentLines().length;
570421
- const startIdx = Math.max(0, totalLines - height - offset);
570422
- const anchorBufIdx = startIdx + (anchor.row - top);
570423
- const currentBufIdx = startIdx + (current.row - top);
570424
- const minRow = Math.min(anchorBufIdx, currentBufIdx);
570425
- const maxRow = Math.max(anchorBufIdx, currentBufIdx);
570426
- const minCol = Math.min(anchor.col, current.col);
570427
- const maxCol = Math.max(anchor.col, current.col);
570428
- const ranges = [];
570429
- if (mode === "block") {
570430
- for (let idx = minRow; idx <= maxRow; idx++) {
570431
- if (idx >= 0 && idx < totalLines) {
570432
- ranges.push({ bufferIdx: idx, startCol: minCol - 1, endCol: maxCol - 1 });
570433
- }
570434
- }
570435
- } else {
570436
- const isForward = anchorBufIdx < currentBufIdx || anchorBufIdx === currentBufIdx && anchor.col <= current.col;
570437
- const startR = isForward ? anchorBufIdx : currentBufIdx;
570438
- const endR = isForward ? currentBufIdx : anchorBufIdx;
570439
- const startC = isForward ? anchor.col - 1 : current.col - 1;
570440
- const endC = isForward ? current.col - 1 : anchor.col - 1;
570441
- for (let idx = startR; idx <= endR; idx++) {
570442
- if (idx < 0 || idx >= totalLines) continue;
570443
- const lineLen = visibleLength(this._provider.getContentLines()[idx] ?? "");
570444
- if (idx === startR && idx === endR) {
570445
- ranges.push({ bufferIdx: idx, startCol: startC, endCol: endC });
570446
- } else if (idx === startR) {
570447
- ranges.push({ bufferIdx: idx, startCol: startC, endCol: Math.max(lineLen, startC) });
570448
- } else if (idx === endR) {
570449
- ranges.push({ bufferIdx: idx, startCol: 0, endCol: endC });
570450
- } else {
570451
- ranges.push({ bufferIdx: idx, startCol: 0, endCol: lineLen });
570452
- }
570453
- }
570454
- }
570455
- return ranges;
570456
- }
570457
- /**
570458
- * Apply selection highlighting to a content line for rendering.
570459
- * Takes the original ANSI line and returns it with selection highlight applied.
570460
- *
570461
- * @param line Original content line (with ANSI codes)
570462
- * @param startCol 0-based visible start column to highlight
570463
- * @param endCol 0-based visible end column to highlight (inclusive)
570464
- * @returns Line with selection highlight overlay
570465
- */
570466
- static applyHighlight(line, startCol, endCol) {
570467
- const plain = stripAnsi(line);
570468
- if (startCol > plain.length || endCol < 0 || startCol > endCol) return line;
570469
- const sc = Math.max(0, startCol);
570470
- const ec = Math.min(plain.length - 1, endCol);
570471
- let result = "";
570472
- let visPos = 0;
570473
- let i2 = 0;
570474
- let inHighlight = false;
570475
- while (i2 < line.length) {
570476
- const escMatch = line.slice(i2).match(/^(\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\))/);
570477
- if (escMatch) {
570478
- if (inHighlight) {
570479
- result += SEL_END + escMatch[0] + SEL_START;
570480
- } else {
570481
- result += escMatch[0];
570482
- }
570483
- i2 += escMatch[0].length;
570484
- continue;
570485
- }
570486
- if (visPos === sc && !inHighlight) {
570487
- result += SEL_START;
570488
- inHighlight = true;
570489
- }
570490
- result += line[i2];
570491
- if (visPos === ec && inHighlight) {
570492
- result += SEL_END;
570493
- inHighlight = false;
570494
- }
570495
- visPos++;
570496
- i2++;
570497
- }
570498
- if (inHighlight) result += SEL_END;
570499
- return result;
570500
- }
570501
- /**
570502
- * Get the selected text content (plain text, no ANSI codes).
570503
- * For block mode, each line is joined with newline.
570504
- * For line mode, text flows continuously with newlines between lines.
570505
- */
570506
- getSelectedText() {
570507
- const ranges = this.getSelectedRanges();
570508
- if (ranges.length === 0) return "";
570509
- const lines = this._provider.getContentLines();
570510
- const parts = [];
570511
- for (const { bufferIdx, startCol, endCol } of ranges) {
570512
- const raw = lines[bufferIdx] ?? "";
570513
- const plain = stripAnsi(raw);
570514
- const selected = plain.slice(
570515
- Math.max(0, startCol),
570516
- Math.min(plain.length, endCol + 1)
570517
- );
570518
- parts.push(selected);
570519
- }
570520
- return parts.join("\n");
570521
- }
570522
- /**
570523
- * Copy current selection to system clipboard.
570524
- * Tries platform commands first, falls back to OSC 52.
570525
- * Returns true if copy succeeded.
570526
- */
570527
- copyToClipboard() {
570528
- const text = this.getSelectedText();
570529
- if (!text) return false;
570530
- return copyText(text);
570531
- }
570532
- };
570533
- _clipboardAutoInstallAttempted = false;
570534
- _hoveredButtonCmd = null;
570535
- _pressedButtonCmd = null;
570536
- _updateBadgeActive = false;
570537
- _updateBadgeCol = 0;
570538
- _updateBadgeLen = 0;
570539
- }
570540
- });
570541
-
570542
570894
  // packages/cli/src/tui/daemon-registry.ts
570543
570895
  var DaemonRegistry, registry2;
570544
570896
  var init_daemon_registry = __esm({
@@ -570951,16 +571303,16 @@ function buildTodoProgressBar(todos, maxWidth) {
570951
571303
  for (let i2 = 0; i2 < cells; i2++) {
570952
571304
  const t2 = todos[i2];
570953
571305
  if (t2.status === "completed") {
570954
- out += `\x1B[1m${DONE_Y}█${RESET}`;
571306
+ out += `\x1B[1m${DONE_Y}█${RESET2}`;
570955
571307
  } else if (i2 === inIdx) {
570956
- out += `${INPROG}▒${RESET}`;
571308
+ out += `${INPROG}▒${RESET2}`;
570957
571309
  } else if (i2 === nextIdx && inIdx >= 0) {
570958
- out += `${NEXT}▒${RESET}`;
571310
+ out += `${NEXT}▒${RESET2}`;
570959
571311
  } else {
570960
- out += `${PEND}░${RESET}`;
571312
+ out += `${PEND}░${RESET2}`;
570961
571313
  }
570962
571314
  }
570963
- if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET}`;
571315
+ if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET2}`;
570964
571316
  return out;
570965
571317
  }
570966
571318
  function render() {
@@ -570982,7 +571334,7 @@ function render() {
570982
571334
  const total = _lastTodos.length;
570983
571335
  const headerColor = ACCENT;
570984
571336
  const lines = [];
570985
- const headerPrefix = `tasks ${headerColor}${completed}/${total}${RESET} `;
571337
+ const headerPrefix = `tasks ${headerColor}${completed}/${total}${RESET2} `;
570986
571338
  const headerPrefixWidth = visualLen(headerPrefix);
570987
571339
  const maxBarWidth = Math.max(0, cols - 2 - headerPrefixWidth);
570988
571340
  const progressBar = buildTodoProgressBar(_lastTodos, maxBarWidth);
@@ -570994,11 +571346,11 @@ function render() {
570994
571346
  const contentWidth = Math.max(4, cols - 8);
570995
571347
  const contentText = t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
570996
571348
  const truncated = truncate2(contentText, contentWidth);
570997
- lines.push(`${color}${mark}${RESET} ${color}${truncated}${RESET}`);
571349
+ lines.push(`${color}${mark}${RESET2} ${color}${truncated}${RESET2}`);
570998
571350
  }
570999
571351
  if (_lastTodos.length > visible.length) {
571000
571352
  const more = _lastTodos.length - visible.length;
571001
- lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET}`;
571353
+ lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET2}`;
571002
571354
  }
571003
571355
  let out = HIDE + SAVE;
571004
571356
  const newTop = L.tasksTop;
@@ -571015,7 +571367,7 @@ function render() {
571015
571367
  for (let i2 = 0; i2 < lines.length; i2++) {
571016
571368
  const row = L.tasksTop + i2;
571017
571369
  if (row > L.tasksBottom) break;
571018
- out += `\x1B[${row};1H${CLEAR_LINE}${BG}${" ".repeat(cols)}\x1B[${row};2H${lines[i2]}${RESET}`;
571370
+ out += `\x1B[${row};1H${CLEAR_LINE}${BG}${" ".repeat(cols)}\x1B[${row};2H${lines[i2]}${RESET2}`;
571019
571371
  }
571020
571372
  out += RESTORE + SHOW;
571021
571373
  try {
@@ -571043,7 +571395,7 @@ function clearLastPaintedRows() {
571043
571395
  _lastPaintedTop = -1;
571044
571396
  _lastPaintedBottom = -1;
571045
571397
  }
571046
- var chromeWrite, _activeSessionId, _watcher, _lastTodos, _enabled, _redrawScheduled, _onResizeChange, _scopeOverlayActive, _scopeMainViewActive, _scopeNeovimActive, _scopePagerActive, _lastPaintedTop, _lastPaintedBottom, MAX_VISIBLE_ROWS, SAVE, RESTORE, HIDE, SHOW, CLEAR_LINE, RESET, BG, DIM_LABEL, ACCENT, DONE2, PENDING, BLOCKED;
571398
+ var chromeWrite, _activeSessionId, _watcher, _lastTodos, _enabled, _redrawScheduled, _onResizeChange, _scopeOverlayActive, _scopeMainViewActive, _scopeNeovimActive, _scopePagerActive, _lastPaintedTop, _lastPaintedBottom, MAX_VISIBLE_ROWS, SAVE, RESTORE, HIDE, SHOW, CLEAR_LINE, RESET2, BG, DIM_LABEL, ACCENT, DONE2, PENDING, BLOCKED;
571047
571399
  var init_tui_tasks_renderer = __esm({
571048
571400
  "packages/cli/src/tui/tui-tasks-renderer.ts"() {
571049
571401
  "use strict";
@@ -571070,7 +571422,7 @@ var init_tui_tasks_renderer = __esm({
571070
571422
  HIDE = "\x1B[?25l";
571071
571423
  SHOW = "\x1B[?25h";
571072
571424
  CLEAR_LINE = "\x1B[2K";
571073
- RESET = "\x1B[0m";
571425
+ RESET2 = "\x1B[0m";
571074
571426
  BG = tuiBgSeq();
571075
571427
  DIM_LABEL = "";
571076
571428
  ACCENT = "";
@@ -571117,7 +571469,7 @@ function setTerminalTitle(task, version4) {
571117
571469
  const title = task ? `${task.slice(0, 60)} · ${ver}` : ver;
571118
571470
  process.stdout.write(`\x1B]2;${title}\x07`);
571119
571471
  }
571120
- var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, NO_SUB_AGENTS_HEADER_LABEL, BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, _globalFooterLock, RESET2, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
571472
+ var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, NO_SUB_AGENTS_HEADER_LABEL, BOX_TL2, BOX_TR2, BOX_BL2, BOX_BR2, BOX_H2, BOX_V2, _globalFooterLock, RESET3, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
571121
571473
  var init_status_bar = __esm({
571122
571474
  "packages/cli/src/tui/status-bar.ts"() {
571123
571475
  "use strict";
@@ -571296,14 +571648,14 @@ var init_status_bar = __esm({
571296
571648
  TEXT_PRIMARY = tuiTextPrimary() < 0 ? 252 : tuiTextPrimary();
571297
571649
  TEXT_DIM = tuiTextDim();
571298
571650
  NO_SUB_AGENTS_HEADER_LABEL = " no sub-agents ";
571299
- BOX_TL = "╭";
571300
- BOX_TR = "╮";
571301
- BOX_BL = "╰";
571302
- BOX_BR = "╯";
571303
- BOX_H = "─";
571304
- BOX_V = "│";
571651
+ BOX_TL2 = "╭";
571652
+ BOX_TR2 = "╮";
571653
+ BOX_BL2 = "╰";
571654
+ BOX_BR2 = "╯";
571655
+ BOX_H2 = "─";
571656
+ BOX_V2 = "│";
571305
571657
  _globalFooterLock = false;
571306
- RESET2 = "\x1B[0m";
571658
+ RESET3 = "\x1B[0m";
571307
571659
  CURSOR_BLINK_BLOCK = "\x1B[1 q";
571308
571660
  _isWindows = process.platform === "win32";
571309
571661
  StatusBar = class _StatusBar {
@@ -571349,6 +571701,22 @@ var init_status_bar = __esm({
571349
571701
  _contentScrollOffset = 0;
571350
571702
  // 0 = live (bottom), >0 = scrolled back
571351
571703
  _contentMaxLines = 1e4;
571704
+ /**
571705
+ * Dynamic content blocks — width-aware regions that re-render themselves
571706
+ * when the terminal resizes. The renderer registered here is called from
571707
+ * the scrollback reflow path: when a sentinel line of the form
571708
+ * `\x01DYNBLOCK:<id>\x01` is encountered, the registered fn is invoked
571709
+ * with the current terminal width and its returned lines replace the
571710
+ * sentinel in the reflowed output. This is how the Task Complete box
571711
+ * stays geometrically correct under SIGWINCH-driven resize.
571712
+ *
571713
+ * Keep the renderer cheap (pure data → strings) — it runs every full
571714
+ * repaint, including selection updates and scroll events.
571715
+ */
571716
+ _dynamicBlocks = /* @__PURE__ */ new Map();
571717
+ /** Sentinel marker — used both for scrollback storage and reflow detection. */
571718
+ DYNAMIC_BLOCK_MARK_PREFIX = "DYNBLOCK:";
571719
+ DYNAMIC_BLOCK_MARK_SUFFIX = "";
571352
571720
  // Partial-line accumulator for the buffered-write layer. Stream output
571353
571721
  // arrives in chunks (one per syntax-highlighted token) and a single
571354
571722
  // logical line can span many writes. If we naively push each chunk to
@@ -571459,6 +571827,41 @@ var init_status_bar = __esm({
571459
571827
  syncEnd() {
571460
571828
  this.termWrite("\x1B[?2026l");
571461
571829
  }
571830
+ /**
571831
+ * Register (or replace) a dynamic content block. Returns the sentinel
571832
+ * line the caller should push into the scrollback to make the block
571833
+ * appear there. The block's renderer is invoked on every repaint and
571834
+ * given the current terminal width; its returned lines are spliced in
571835
+ * place of the sentinel during reflow. Callers MUST also call
571836
+ * `appendDynamicBlock(id)` to actually place the block in scrollback.
571837
+ *
571838
+ * The sentinel is opaque — never write it directly to stdout; route
571839
+ * through `appendDynamicBlock` which also triggers a repaint so the
571840
+ * block becomes visible immediately.
571841
+ */
571842
+ registerDynamicBlock(id, render2) {
571843
+ this._dynamicBlocks.set(id, render2);
571844
+ return `${this.DYNAMIC_BLOCK_MARK_PREFIX}${id}${this.DYNAMIC_BLOCK_MARK_SUFFIX}`;
571845
+ }
571846
+ /** Unregister a dynamic block. Existing sentinels in scrollback become inert (rendered as empty). */
571847
+ unregisterDynamicBlock(id) {
571848
+ this._dynamicBlocks.delete(id);
571849
+ }
571850
+ /**
571851
+ * Append a previously-registered dynamic block's sentinel to scrollback
571852
+ * and trigger a repaint. The block's renderer fires immediately at the
571853
+ * current terminal width and again on every subsequent SIGWINCH (because
571854
+ * `_handleResizeImmediate` already calls `repaintContent`).
571855
+ */
571856
+ appendDynamicBlock(id) {
571857
+ if (!this._dynamicBlocks.has(id)) return;
571858
+ const sentinel = `${this.DYNAMIC_BLOCK_MARK_PREFIX}${id}${this.DYNAMIC_BLOCK_MARK_SUFFIX}`;
571859
+ this._contentLines.push(sentinel);
571860
+ if (this._contentLines.length > this._contentMaxLines) {
571861
+ this._contentLines.splice(0, this._contentLines.length - this._contentMaxLines);
571862
+ }
571863
+ if (this.active) this.repaintContent();
571864
+ }
571462
571865
  /** Force a complete footer redraw (public wrapper for renderFooterAndPositionInput).
571463
571866
  *
571464
571867
  * IMPORTANT: do NOT call `updateFooterHeight()` here. The inner render
@@ -571595,7 +571998,7 @@ var init_status_bar = __esm({
571595
571998
  const segment = segments[i2];
571596
571999
  if (i2 > 0) {
571597
572000
  separatorOffsets.push(width);
571598
- text += `${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
572001
+ text += `${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
571599
572002
  width += 1;
571600
572003
  }
571601
572004
  text += `\x1B[1;38;5;${TEXT_PRIMARY}m${PANEL_BG_SEQ}${segment}`;
@@ -571777,7 +572180,7 @@ var init_status_bar = __esm({
571777
572180
  const sysSeparatorOffset = sysItems.reduce((sum, item) => sum + item.w, 0);
571778
572181
  this._sysSeparatorOffset = sysSeparatorOffset;
571779
572182
  sysItems.push({
571780
- render: () => `${BOX_FG}│${RESET2}${PANEL_BG_SEQ} `,
572183
+ render: () => `${BOX_FG}│${RESET3}${PANEL_BG_SEQ} `,
571781
572184
  w: 2
571782
572185
  });
571783
572186
  const voiceLabel = this._voiceActive ? ` ${this._voiceModelId || "voice"} ` : " voice ";
@@ -571974,7 +572377,7 @@ var init_status_bar = __esm({
571974
572377
  const hdrRow = layout().headerContent;
571975
572378
  let buf = "\x1B7";
571976
572379
  buf += `\x1B[${hdrRow};1H${PANEL_BG_SEQ}\x1B[2K`;
571977
- buf += `${BOX_FG}│${RESET2}${PANEL_BG_SEQ}`;
572380
+ buf += `${BOX_FG}│${RESET3}${PANEL_BG_SEQ}`;
571978
572381
  if (chrome.showPrev) {
571979
572382
  buf += leftArrow;
571980
572383
  buf += ` `;
@@ -571985,7 +572388,7 @@ var init_status_bar = __esm({
571985
572388
  buf += `\x1B[${hdrRow};${w - 1}H`;
571986
572389
  buf += rightArrow;
571987
572390
  }
571988
- buf += `\x1B[${hdrRow};${w}H${BOX_FG}│${RESET2}${PANEL_BG_SEQ}`;
572391
+ buf += `\x1B[${hdrRow};${w}H${BOX_FG}│${RESET3}${PANEL_BG_SEQ}`;
571989
572392
  buf += "\x1B8";
571990
572393
  this.termWrite(buf);
571991
572394
  }
@@ -573188,11 +573591,11 @@ var init_status_bar = __esm({
573188
573591
  for (let i2 = 0; i2 < inputWrap.lines.length; i2++) {
573189
573592
  const row = pos.inputStartRow + i2;
573190
573593
  const prefix = i2 === 0 ? this.promptText : " ".repeat(this.promptWidth);
573191
- buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${prefix}${inputWrap.lines[i2]}${RESET2}${PANEL_BG_SEQ}`;
573594
+ buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${prefix}${inputWrap.lines[i2]}${RESET3}${PANEL_BG_SEQ}`;
573192
573595
  }
573193
573596
  const boxInnerP = w - 2;
573194
- buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInnerP))}${BOX_BR}${RESET2}${PANEL_BG_SEQ}`;
573195
- buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET2}${PANEL_BG_SEQ}\x1B[?7h\x1B[${pos.inputStartRow};1H`;
573597
+ buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInnerP))}${BOX_BR2}${RESET3}${PANEL_BG_SEQ}`;
573598
+ buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET3}${PANEL_BG_SEQ}\x1B[?7h\x1B[${pos.inputStartRow};1H`;
573196
573599
  this.termWrite(buf);
573197
573600
  if (this._bannerRefresh) this._bannerRefresh();
573198
573601
  } else {
@@ -573393,7 +573796,7 @@ ${CONTENT_BG_SEQ}`);
573393
573796
  process.stdout.write = this._origWrite;
573394
573797
  this._origWrite = null;
573395
573798
  }
573396
- process.stdout.write(RESET2);
573799
+ process.stdout.write(RESET3);
573397
573800
  this._brailleSpinner.setMetrics({ isStreaming: false });
573398
573801
  this.renderFooterAndPositionInput();
573399
573802
  this.parkCursorInInput();
@@ -573522,12 +573925,32 @@ ${CONTENT_BG_SEQ}`);
573522
573925
  reflowContentLines(livePartialLine, width) {
573523
573926
  const maxWidth = Math.max(16, width);
573524
573927
  const source = livePartialLine ? [...this._contentLines, livePartialLine] : this._contentLines;
573525
- return source.flatMap(
573526
- (line, idx) => this.reflowContentLine(line, maxWidth).map((segment) => ({
573928
+ return source.flatMap((line, idx) => {
573929
+ if (line.startsWith(this.DYNAMIC_BLOCK_MARK_PREFIX) && line.endsWith(this.DYNAMIC_BLOCK_MARK_SUFFIX)) {
573930
+ const id = line.slice(
573931
+ this.DYNAMIC_BLOCK_MARK_PREFIX.length,
573932
+ line.length - this.DYNAMIC_BLOCK_MARK_SUFFIX.length
573933
+ );
573934
+ const renderer = this._dynamicBlocks.get(id);
573935
+ if (!renderer) return [];
573936
+ let blockLines;
573937
+ try {
573938
+ blockLines = renderer(maxWidth);
573939
+ } catch {
573940
+ return [];
573941
+ }
573942
+ return blockLines.flatMap(
573943
+ (segment) => this.reflowContentLine(segment, maxWidth).map((s2) => ({
573944
+ line: s2,
573945
+ bufferIdx: idx
573946
+ }))
573947
+ );
573948
+ }
573949
+ return this.reflowContentLine(line, maxWidth).map((segment) => ({
573527
573950
  line: segment,
573528
573951
  bufferIdx: idx
573529
- }))
573530
- );
573952
+ }));
573953
+ });
573531
573954
  }
573532
573955
  reflowContentLine(line, width) {
573533
573956
  const visible = stripAnsi(line);
@@ -573587,7 +574010,7 @@ ${CONTENT_BG_SEQ}`);
573587
574010
  const endRaw = this.rawIndexForVisibleColumn(line, end);
573588
574011
  const activeStyle = this.activeSgrAt(line, startRaw);
573589
574012
  const raw = line.slice(startRaw, endRaw);
573590
- return activeStyle ? `${activeStyle}${raw}${RESET2}` : raw;
574013
+ return activeStyle ? `${activeStyle}${raw}${RESET3}` : raw;
573591
574014
  }
573592
574015
  rawIndexForVisibleColumn(line, target) {
573593
574016
  if (target <= 0) return 0;
@@ -573611,7 +574034,7 @@ ${CONTENT_BG_SEQ}`);
573611
574034
  let match;
573612
574035
  while ((match = sgr.exec(line)) && match.index < rawIndex) {
573613
574036
  const seq = match[0];
573614
- if (seq === RESET2 || /\x1B\[(?:0|39|49)(?:;0)?m/.test(seq)) {
574037
+ if (seq === RESET3 || /\x1B\[(?:0|39|49)(?:;0)?m/.test(seq)) {
573615
574038
  active = "";
573616
574039
  } else {
573617
574040
  active += seq;
@@ -574386,21 +574809,21 @@ ${CONTENT_BG_SEQ}`);
574386
574809
  const inputWrap = this.wrapInput(w);
574387
574810
  let buf = "\x1B[?7l";
574388
574811
  const boxInner = w - 2;
574389
- buf += `\x1B[${pos.inputStartRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_TL}${BOX_H.repeat(Math.max(0, boxInner))}${BOX_TR}${RESET2}${PANEL_BG_SEQ}`;
574812
+ buf += `\x1B[${pos.inputStartRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_TL2}${BOX_H2.repeat(Math.max(0, boxInner))}${BOX_TR2}${RESET3}${PANEL_BG_SEQ}`;
574390
574813
  const Lspacer = layout();
574391
574814
  const spacerRow = pos.inputStartRow - 1;
574392
574815
  const tasksOccupiesSpacer = Lspacer.tasksHeight > 0 && spacerRow >= Lspacer.tasksTop && spacerRow <= Lspacer.tasksBottom;
574393
574816
  if (spacerRow >= this.scrollRegionTop && !tasksOccupiesSpacer) {
574394
- buf += `\x1B[${spacerRow};1H${PANEL_BG_SEQ}\x1B[2K${RESET2}`;
574817
+ buf += `\x1B[${spacerRow};1H${PANEL_BG_SEQ}\x1B[2K${RESET3}`;
574395
574818
  }
574396
574819
  for (let i2 = 0; i2 < inputWrap.lines.length; i2++) {
574397
574820
  const row = pos.inputStartRow + 1 + i2;
574398
574821
  const prefix = i2 === 0 ? this.promptText : " ".repeat(this.promptWidth);
574399
574822
  const lineContent = `${prefix}${inputWrap.lines[i2]}`;
574400
574823
  buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K`;
574401
- buf += `${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}${lineContent}`;
574824
+ buf += `${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}${lineContent}`;
574402
574825
  buf += `${PANEL_BG_SEQ}\x1B[K`;
574403
- buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
574826
+ buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
574404
574827
  }
574405
574828
  const cursorTermRow = pos.inputStartRow + 1 + inputWrap.cursorRow;
574406
574829
  if (this._suggestions.length > 0 && pos.suggestStartRow > 0) {
@@ -574412,17 +574835,17 @@ ${CONTENT_BG_SEQ}`);
574412
574835
  const fg2 = isHighlighted ? `\x1B[1;38;5;${TEXT_PRIMARY}m` : `\x1B[38;5;${TEXT_PRIMARY}m`;
574413
574836
  const slash = isHighlighted ? `\x1B[38;5;245m` : `\x1B[38;5;${TEXT_DIM}m`;
574414
574837
  const marker = isHighlighted ? `\x1B[38;5;${TEXT_PRIMARY}m› ` : " ";
574415
- buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
574838
+ buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
574416
574839
  buf += `${bg} ${marker}${slash}/${fg2}${cmd}`;
574417
574840
  buf += `${PANEL_BG_SEQ}\x1B[K`;
574418
- buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
574841
+ buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
574419
574842
  }
574420
574843
  const suggestBottomRow = pos.suggestStartRow + this._suggestions.length;
574421
- buf += `\x1B[${suggestBottomRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInner))}${BOX_BR}${RESET2}${PANEL_BG_SEQ}`;
574844
+ buf += `\x1B[${suggestBottomRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInner))}${BOX_BR2}${RESET3}${PANEL_BG_SEQ}`;
574422
574845
  } else {
574423
- buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInner))}${BOX_BR}${RESET2}${PANEL_BG_SEQ}`;
574846
+ buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInner))}${BOX_BR2}${RESET3}${PANEL_BG_SEQ}`;
574424
574847
  }
574425
- buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET2}${PANEL_BG_SEQ}`;
574848
+ buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET3}${PANEL_BG_SEQ}`;
574426
574849
  buf += "\x1B[?7h";
574427
574850
  if (this.writeDepth === 0) {
574428
574851
  buf += `\x1B[${cursorTermRow};${inputWrap.cursorCol}H${CURSOR_BLINK_BLOCK}\x1B[?25h`;
@@ -574455,7 +574878,7 @@ ${CONTENT_BG_SEQ}`);
574455
574878
  const pos = this.rowPositions(termRows());
574456
574879
  let buf = "\x1B7\x1B[?7l";
574457
574880
  if (pos.tabBarRow > 0) {
574458
- buf += `\x1B[${pos.tabBarRow};1H${PANEL_BG_SEQ}\x1B[2K${RESET2}`;
574881
+ buf += `\x1B[${pos.tabBarRow};1H${PANEL_BG_SEQ}\x1B[2K${RESET3}`;
574459
574882
  }
574460
574883
  const boxInnerR = w - 2;
574461
574884
  if (this._suggestions.length > 0 && pos.suggestStartRow > 0) {
@@ -574465,14 +574888,14 @@ ${CONTENT_BG_SEQ}`);
574465
574888
  const isHl = si === this._suggestIndex;
574466
574889
  const fg2 = isHl ? `\x1B[1;38;5;${TEXT_PRIMARY}m` : `\x1B[38;5;${TEXT_PRIMARY}m`;
574467
574890
  const marker = isHl ? `\x1B[38;5;${TEXT_PRIMARY}m› ` : " ";
574468
- buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ} ${marker}\x1B[38;5;${TEXT_DIM}m/${fg2}${cmd}`;
574469
- buf += `${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}`;
574891
+ buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ} ${marker}\x1B[38;5;${TEXT_DIM}m/${fg2}${cmd}`;
574892
+ buf += `${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}`;
574470
574893
  }
574471
- buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInnerR))}${BOX_BR}${RESET2}`;
574894
+ buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInnerR))}${BOX_BR2}${RESET3}`;
574472
574895
  } else {
574473
- buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInnerR))}${BOX_BR}${RESET2}`;
574896
+ buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInnerR))}${BOX_BR2}${RESET3}`;
574474
574897
  }
574475
- buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET2}${PANEL_BG_SEQ}\x1B[?7h\x1B8` + // DEC restore cursor
574898
+ buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET3}${PANEL_BG_SEQ}\x1B[?7h\x1B8` + // DEC restore cursor
574476
574899
  (this.writeDepth === 0 ? `${CURSOR_BLINK_BLOCK}\x1B[?25h` : "");
574477
574900
  this.termWrite(buf);
574478
574901
  if (pos.tabBarRow > 0) this.renderAgentTabs();
@@ -574516,12 +574939,12 @@ ${CONTENT_BG_SEQ}`);
574516
574939
  }
574517
574940
  buf += "\x1B[?7l";
574518
574941
  const boxInnerH = w - 2;
574519
- buf += `\x1B[${pos.inputStartRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_TL}${BOX_H.repeat(Math.max(0, boxInnerH))}${BOX_TR}${RESET2}`;
574942
+ buf += `\x1B[${pos.inputStartRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_TL2}${BOX_H2.repeat(Math.max(0, boxInnerH))}${BOX_TR2}${RESET3}`;
574520
574943
  for (let i2 = 0; i2 < inputWrap.lines.length; i2++) {
574521
574944
  const row = pos.inputStartRow + 1 + i2;
574522
574945
  const prefix = i2 === 0 ? this.promptText : " ".repeat(this.promptWidth);
574523
574946
  const lineContent = `${prefix}${inputWrap.lines[i2]}`;
574524
- buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}${lineContent}${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}`;
574947
+ buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}${lineContent}${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}`;
574525
574948
  }
574526
574949
  if (this._suggestions.length > 0 && pos.suggestStartRow > 0) {
574527
574950
  for (let si = 0; si < this._suggestions.length; si++) {
@@ -574531,14 +574954,14 @@ ${CONTENT_BG_SEQ}`);
574531
574954
  const bg = isHl ? `\x1B[48;5;235m` : PANEL_BG_SEQ;
574532
574955
  const fg2 = isHl ? `\x1B[1;38;5;${TEXT_PRIMARY}m` : `\x1B[38;5;${TEXT_PRIMARY}m`;
574533
574956
  const marker = isHl ? `\x1B[38;5;${TEXT_PRIMARY}m› ` : " ";
574534
- buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
574957
+ buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
574535
574958
  buf += `${bg} ${marker}\x1B[38;5;${TEXT_DIM}m/${fg2}${cmd}`;
574536
- buf += `${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}`;
574959
+ buf += `${PANEL_BG_SEQ}\x1B[K\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}`;
574537
574960
  }
574538
574961
  }
574539
574962
  const boxInnerS = w - 2;
574540
- buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL}${BOX_H.repeat(Math.max(0, boxInnerS))}${BOX_BR}${RESET2}`;
574541
- buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET2}`;
574963
+ buf += `\x1B[${pos.bufferRow};1H${PANEL_BG_SEQ}\x1B[2K${BOX_FG}${BOX_BL2}${BOX_H2.repeat(Math.max(0, boxInnerS))}${BOX_BR2}${RESET3}`;
574964
+ buf += `\x1B[${pos.metricsRow};1H${PANEL_BG_SEQ}\x1B[2K${this.buildMetricsLine()}${RESET3}`;
574542
574965
  buf += "\x1B[?7h";
574543
574966
  buf += "\x1B8";
574544
574967
  if (heightDelta > 0) {
@@ -574556,9 +574979,9 @@ ${CONTENT_BG_SEQ}`);
574556
574979
  const prefix = i2 === 0 ? this.promptText : " ".repeat(this.promptWidth);
574557
574980
  const lineContent = `${prefix}${inputWrap.lines[i2]}`;
574558
574981
  buf += `\x1B[${row};1H${PANEL_BG_SEQ}\x1B[2K`;
574559
- buf += `${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}${lineContent}`;
574982
+ buf += `${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}${lineContent}`;
574560
574983
  buf += `${PANEL_BG_SEQ}\x1B[K`;
574561
- buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V}${RESET2}${PANEL_BG_SEQ}`;
574984
+ buf += `\x1B[${row};${w}H${BOX_FG}${BOX_V2}${RESET3}${PANEL_BG_SEQ}`;
574562
574985
  }
574563
574986
  buf += "\x1B[?7h";
574564
574987
  this.termWrite(buf);
@@ -584685,6 +585108,13 @@ function formatImageAsciiContext(preview, label) {
584685
585108
  return `[ASCII preview of image: ${label}]
584686
585109
  ${preview.ascii}`;
584687
585110
  }
585111
+ function hasImageExtension(filePath) {
585112
+ const lower = filePath.toLowerCase();
585113
+ for (const ext of IMAGE_PREVIEW_EXTENSIONS) {
585114
+ if (lower.endsWith(ext)) return true;
585115
+ }
585116
+ return false;
585117
+ }
584688
585118
  function extractSavedImagePath(text, repoRoot) {
584689
585119
  const patterns = [
584690
585120
  /Image generated:\s*([^\n\r]+)/i,
@@ -584700,18 +585130,29 @@ function extractSavedImagePath(text, repoRoot) {
584700
585130
  const match = text.match(pattern);
584701
585131
  if (!match?.[1]) continue;
584702
585132
  const raw = match[1].trim().replace(/\s+\([^)]+\)\s*$/g, "").replace(/^["']|["']$/g, "");
585133
+ if (!hasImageExtension(raw)) continue;
584703
585134
  const candidate = raw.startsWith("/") ? raw : resolve40(repoRoot, raw);
584704
585135
  if (existsSync99(candidate)) return candidate;
584705
585136
  }
584706
585137
  return null;
584707
585138
  }
584708
- var DEFAULT_PIXELS, ANSI_PATTERN, TERMINAL_CELL_ASPECT;
585139
+ var DEFAULT_PIXELS, ANSI_PATTERN, TERMINAL_CELL_ASPECT, IMAGE_PREVIEW_EXTENSIONS;
584709
585140
  var init_image_ascii_preview = __esm({
584710
585141
  "packages/cli/src/tui/image-ascii-preview.ts"() {
584711
585142
  "use strict";
584712
585143
  DEFAULT_PIXELS = " .,:;i1tfLCG08@";
584713
585144
  ANSI_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/g;
584714
585145
  TERMINAL_CELL_ASPECT = 0.52;
585146
+ IMAGE_PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set([
585147
+ ".png",
585148
+ ".jpg",
585149
+ ".jpeg",
585150
+ ".gif",
585151
+ ".webp",
585152
+ ".bmp",
585153
+ ".tiff",
585154
+ ".tif"
585155
+ ]);
584715
585156
  }
584716
585157
  });
584717
585158
 
@@ -649733,13 +650174,24 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
649733
650174
  }
649734
650175
  contentWrite(() => {
649735
650176
  if (result.completed) {
649736
- renderTaskComplete(
649737
- result.summary,
649738
- result.turns,
649739
- result.toolCalls,
649740
- result.durationMs,
649741
- tokens
649742
- );
650177
+ renderTaskComplete({
650178
+ task: effectiveTask,
650179
+ summary: result.summary,
650180
+ turns: result.turns,
650181
+ toolCalls: result.toolCalls,
650182
+ durationMs: result.durationMs,
650183
+ tokens,
650184
+ filesEdited: result.filesEdited,
650185
+ testsRun: result.testsRun,
650186
+ provenanceAnchors: result.provenanceAnchors,
650187
+ // Hand the status bar so the box renders as a resize-aware
650188
+ // dynamic block. When the bar isn't active (tests, headless),
650189
+ // the legacy line-based banner fires instead.
650190
+ host: statusBar?.isActive ? {
650191
+ registerDynamicBlock: (id, render2) => statusBar.registerDynamicBlock(id, render2),
650192
+ appendDynamicBlock: (id) => statusBar.appendDynamicBlock(id)
650193
+ } : void 0
650194
+ });
649743
650195
  if (onComplete)
649744
650196
  onComplete(result.summary, {
649745
650197
  turns: result.turns,