@cydm/pie 2.7.13 → 2.7.15

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/cli.js CHANGED
@@ -29,10 +29,7 @@ import {
29
29
  migrateConfigFromAgentDir,
30
30
  setYoloGrantListener,
31
31
  truncateHeadTail
32
- } from "./chunks/chunk-D7RMNFVM.js";
33
- import {
34
- resolveCliProjectRoot
35
- } from "./chunks/chunk-WLFOBECN.js";
32
+ } from "./chunks/chunk-CLEPSTWH.js";
36
33
  import {
37
34
  AGENTS_CONTEXT_FILE_NAME,
38
35
  AgentSessionController,
@@ -94,8 +91,8 @@ import {
94
91
  shouldPreserveExecutionStateForUserText,
95
92
  supersedeExecutionState,
96
93
  verifyPreparedCompactionCommit
97
- } from "./chunks/chunk-CIFC6QVB.js";
98
- import "./chunks/chunk-HGUYS5SF.js";
94
+ } from "./chunks/chunk-C5UTDL3S.js";
95
+ import "./chunks/chunk-TDHVB2FH.js";
99
96
  import {
100
97
  Deref,
101
98
  Errors,
@@ -118,7 +115,7 @@ import {
118
115
  sortToolModelCandidatesByCapability,
119
116
  streamSimple,
120
117
  type_exports
121
- } from "./chunks/chunk-R46YPPTU.js";
118
+ } from "./chunks/chunk-NM2JLPJ6.js";
122
119
  import {
123
120
  IsArray,
124
121
  IsBigInt,
@@ -133,6 +130,12 @@ import {
133
130
  TypeBoxError,
134
131
  TypeSystemPolicy
135
132
  } from "./chunks/chunk-XXGYUYXI.js";
133
+ import {
134
+ readImageFormat
135
+ } from "./chunks/chunk-F7EBZTZO.js";
136
+ import {
137
+ resolveCliProjectRoot
138
+ } from "./chunks/chunk-WLFOBECN.js";
136
139
  import {
137
140
  __commonJS,
138
141
  __require,
@@ -34273,7 +34276,7 @@ var require_taggerscript = __commonJS({
34273
34276
  begin: /%[_a-zA-Z0-9:]*/,
34274
34277
  end: "%"
34275
34278
  };
34276
- const ESCAPE_SEQUENCE = {
34279
+ const ESCAPE_SEQUENCE2 = {
34277
34280
  className: "symbol",
34278
34281
  begin: /\\./
34279
34282
  };
@@ -34283,7 +34286,7 @@ var require_taggerscript = __commonJS({
34283
34286
  COMMENT,
34284
34287
  FUNCTION,
34285
34288
  VARIABLE,
34286
- ESCAPE_SEQUENCE
34289
+ ESCAPE_SEQUENCE2
34287
34290
  ]
34288
34291
  };
34289
34292
  }
@@ -56533,6 +56536,15 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
56533
56536
  sendUserMessage(content) {
56534
56537
  actions?.sendUserMessage(content);
56535
56538
  },
56539
+ ...actions?.attachLocalFile ? {
56540
+ attachLocalFile: (path34, displayName) => actions.attachLocalFile(path34, displayName)
56541
+ } : {},
56542
+ ...actions?.beginUserInput ? {
56543
+ beginUserInput: (isCurrent) => actions.beginUserInput(isCurrent)
56544
+ } : {},
56545
+ ...actions?.attachLocalFileAndSubmit ? {
56546
+ attachLocalFileAndSubmit: (path34, displayName, submitText) => actions.attachLocalFileAndSubmit(path34, displayName, submitText)
56547
+ } : {},
56536
56548
  setPlanMode(enabled, options) {
56537
56549
  actions?.setPlanMode(enabled, options);
56538
56550
  },
@@ -57136,9 +57148,6 @@ function parseExtensionFields(raw) {
57136
57148
  return requested;
57137
57149
  }
57138
57150
 
57139
- // src/interactive-mode.ts
57140
- import * as os8 from "os";
57141
-
57142
57151
  // ../../packages/tui/src/fuzzy.ts
57143
57152
  function fuzzyMatch(query, text) {
57144
57153
  const queryLower = query.toLowerCase();
@@ -57357,6 +57366,41 @@ function graphemeWidth(segment) {
57357
57366
  }
57358
57367
  return width;
57359
57368
  }
57369
+ var ESCAPE_SEQUENCE = /\x1b\[[0-?]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[PX^_][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[ -\/]*[0-~]?/g;
57370
+ var CONTROL_CHARS = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g;
57371
+ var HAS_CONTROL_CHARS = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/;
57372
+ var SANITIZE_SEQUENCE = new RegExp(`${ESCAPE_SEQUENCE.source}|${CONTROL_CHARS.source}`, "g");
57373
+ function stripEscapeSequences(text) {
57374
+ return text.replace(ESCAPE_SEQUENCE, "");
57375
+ }
57376
+ var SGR_SEQUENCE = /^\x1b\[[0-9;:]*m$/;
57377
+ var OSC8_SEQUENCE = /^\x1b\]8;[^;\x07\x1b]*;[^\x07\x1b]*(?:\x07|\x1b\\)$/;
57378
+ var KITTY_IMAGE_SEQUENCE = /^\x1b_G[^\x07\x1b]*(?:\x07|\x1b\\)$/;
57379
+ var ITERM2_IMAGE_SEQUENCE = /^\x1b\]1337;[^\x07\x1b]*(?:\x07|\x1b\\)$/;
57380
+ var IMAGE_CURSOR_UP = /^\x1b\[\d*A$/;
57381
+ var CURSOR_MARKER_SEQUENCE = "\x1B_pi:c\x07";
57382
+ function sanitizeTerminalText(text, options = {}) {
57383
+ const needsLayout = options.normalizeLayout === true && /[\r\n\t]/.test(text);
57384
+ if (!HAS_CONTROL_CHARS.test(text) && !needsLayout) return text;
57385
+ const normalize2 = (segment) => options.normalizeLayout ? segment.replace(/\r\n?/g, " ").replace(/\n/g, " ").replace(/\t/g, " ") : segment;
57386
+ let out = "";
57387
+ let cursor = 0;
57388
+ SANITIZE_SEQUENCE.lastIndex = 0;
57389
+ for (let match = SANITIZE_SEQUENCE.exec(text); match; match = SANITIZE_SEQUENCE.exec(text)) {
57390
+ out += normalize2(text.slice(cursor, match.index));
57391
+ const sequence = match[0];
57392
+ if (SGR_SEQUENCE.test(sequence) || OSC8_SEQUENCE.test(sequence)) {
57393
+ out += sequence;
57394
+ } else if (options.preserveCursorMarker === true && sequence === CURSOR_MARKER_SEQUENCE) {
57395
+ out += sequence;
57396
+ } else if (options.preserveImages && (KITTY_IMAGE_SEQUENCE.test(sequence) || ITERM2_IMAGE_SEQUENCE.test(sequence) || IMAGE_CURSOR_UP.test(sequence))) {
57397
+ out += sequence;
57398
+ }
57399
+ cursor = match.index + sequence.length;
57400
+ }
57401
+ out += normalize2(text.slice(cursor));
57402
+ return out;
57403
+ }
57360
57404
  function visibleWidth(str2) {
57361
57405
  if (str2.length === 0) {
57362
57406
  return 0;
@@ -57381,9 +57425,7 @@ function visibleWidth(str2) {
57381
57425
  clean = clean.replace(/\t/g, " ");
57382
57426
  }
57383
57427
  if (clean.includes("\x1B")) {
57384
- clean = clean.replace(/\x1b\[[0-9;]*[mGKHJ]/g, "");
57385
- clean = clean.replace(/\x1b\]8;;[^\x07]*\x07/g, "");
57386
- clean = clean.replace(/\x1b_[^\x07\x1b]*(?:\x07|\x1b\\)/g, "");
57428
+ clean = stripEscapeSequences(clean);
57387
57429
  }
57388
57430
  let width = 0;
57389
57431
  for (const { segment } of segmenter.segment(clean)) {
@@ -58831,7 +58873,7 @@ var Container = class {
58831
58873
  function isFocusable(component) {
58832
58874
  return component !== null && "focused" in component;
58833
58875
  }
58834
- var CURSOR_MARKER = "\x1B_pi:c\x07";
58876
+ var CURSOR_MARKER = CURSOR_MARKER_SEQUENCE;
58835
58877
 
58836
58878
  // ../../packages/tui/src/focus-manager.ts
58837
58879
  var FocusManager = class {
@@ -59166,6 +59208,50 @@ function findChangedRange(previousLines, newLines) {
59166
59208
  return { firstChanged, lastChanged, appendedLines, appendStart };
59167
59209
  }
59168
59210
 
59211
+ // ../../packages/tui/src/render-line-cache.ts
59212
+ function prepareRenderLines(rawLines, previous, width) {
59213
+ let lines = rawLines;
59214
+ const reusedFinal = new Uint8Array(rawLines.length);
59215
+ let reusedLineCount = 0;
59216
+ const canReuse = previous !== null && previous.width === width;
59217
+ for (let index = 0; index < rawLines.length; index++) {
59218
+ const rawLine = rawLines[index];
59219
+ if (canReuse && index < previous.rawLines.length && index < previous.processedLines.length && !rawLine.includes(CURSOR_MARKER) && rawLine === previous.rawLines[index]) {
59220
+ reusedFinal[index] = 1;
59221
+ reusedLineCount++;
59222
+ const processed = previous.processedLines[index];
59223
+ if (processed !== rawLine) {
59224
+ if (lines === rawLines) lines = rawLines.slice();
59225
+ lines[index] = processed;
59226
+ }
59227
+ continue;
59228
+ }
59229
+ const image = isImageLine(rawLine);
59230
+ const clean = sanitizeTerminalText(rawLine, {
59231
+ preserveImages: image,
59232
+ normalizeLayout: true,
59233
+ preserveCursorMarker: true
59234
+ });
59235
+ if (clean !== rawLine) {
59236
+ if (lines === rawLines) lines = rawLines.slice();
59237
+ lines[index] = clean;
59238
+ }
59239
+ }
59240
+ return { lines, reusedFinal, reusedLineCount };
59241
+ }
59242
+ function truncatePreparedLines(prepared, width, onOverflow) {
59243
+ let lines = prepared.lines;
59244
+ for (let index = 0; index < lines.length; index++) {
59245
+ if (prepared.reusedFinal[index]) continue;
59246
+ const line = lines[index];
59247
+ if (isImageLine(line) || visibleWidth(line) <= width) continue;
59248
+ onOverflow(index, line, lines);
59249
+ if (lines === prepared.lines) lines = prepared.lines.slice();
59250
+ lines[index] = truncateToWidth(line, width);
59251
+ }
59252
+ return lines;
59253
+ }
59254
+
59169
59255
  // ../../packages/tui/src/terminal-protocol.ts
59170
59256
  var TerminalProtocol = class {
59171
59257
  constructor(terminal, onCellSizeChanged) {
@@ -59226,6 +59312,8 @@ function lineForWrite(line) {
59226
59312
  var TUI = class extends Container {
59227
59313
  terminal;
59228
59314
  previousLines = [];
59315
+ /** Raw lines paired with previousLines after a successful frame commit. */
59316
+ previousRawLines = null;
59229
59317
  previousWidth = 0;
59230
59318
  focusManager;
59231
59319
  inputRouter = new InputRouter();
@@ -59233,6 +59321,29 @@ var TUI = class extends Container {
59233
59321
  terminalProtocol;
59234
59322
  /** Global callback for debug key (Shift+Ctrl+D). Called before input is forwarded to focused component. */
59235
59323
  onDebug;
59324
+ reportOverflowed = false;
59325
+ /** Record the first over-wide render of this process (evidence for the
59326
+ * component bug), then stay quiet. The line is truncated, not fatal. */
59327
+ reportOverflowOnce(lineIndex, line, width, lines) {
59328
+ if (this.reportOverflowed) return;
59329
+ this.reportOverflowed = true;
59330
+ if (process.env.PIE_CRASH_LOG_DISABLED === "1") return;
59331
+ try {
59332
+ const crashLogPath = path9.join(os3.homedir(), ".pie", "pie-crash.log");
59333
+ const crashData = [
59334
+ `Over-wide render at ${(/* @__PURE__ */ new Date()).toISOString()} (truncated, not fatal)`,
59335
+ `Terminal width: ${width}`,
59336
+ `Line ${lineIndex} visible width: ${visibleWidth(line)}`,
59337
+ "",
59338
+ "=== All rendered lines (control sequences sanitized) ===",
59339
+ ...lines.map((candidate, index) => `[${index}] (w=${visibleWidth(candidate)}) ${candidate}`),
59340
+ ""
59341
+ ].join("\n");
59342
+ fs10.mkdirSync(path9.dirname(crashLogPath), { recursive: true });
59343
+ fs10.writeFileSync(crashLogPath, crashData);
59344
+ } catch {
59345
+ }
59346
+ }
59236
59347
  renderRequested = false;
59237
59348
  cursorRow = 0;
59238
59349
  // Logical cursor row (end of rendered content)
@@ -59344,6 +59455,7 @@ var TUI = class extends Container {
59344
59455
  requestRender(force = false) {
59345
59456
  if (force) {
59346
59457
  this.previousLines = [];
59458
+ this.previousRawLines = null;
59347
59459
  this.previousWidth = -1;
59348
59460
  this.cursorRow = 0;
59349
59461
  this.hardwareCursorRow = 0;
@@ -59397,11 +59509,30 @@ var TUI = class extends Container {
59397
59509
  if (this.overlayManager.hasAny) {
59398
59510
  newLines = this.overlayManager.composite(newLines, width, height, this.maxLinesRendered);
59399
59511
  }
59400
- const cursorPos = extractCursorPosition(newLines, height);
59512
+ const rawLines = newLines;
59513
+ const previousFrame = this.previousRawLines === null ? null : {
59514
+ rawLines: this.previousRawLines,
59515
+ processedLines: this.previousLines,
59516
+ width: this.previousWidth
59517
+ };
59518
+ const prepared = prepareRenderLines(rawLines, previousFrame, width);
59519
+ newLines = prepared.lines;
59520
+ let cursorPos = extractCursorPosition(newLines, height);
59401
59521
  if (cursorPos) {
59402
59522
  newLines = newLines.slice();
59403
59523
  newLines[cursorPos.row] = cursorPos.strippedLine;
59404
59524
  }
59525
+ prepared.lines = newLines;
59526
+ newLines = truncatePreparedLines(prepared, width, (lineIndex, line, lines) => {
59527
+ this.reportOverflowOnce(lineIndex, line, width, lines);
59528
+ });
59529
+ if (cursorPos) {
59530
+ const emitted = newLines[cursorPos.row];
59531
+ if (emitted !== void 0) {
59532
+ const maxCol = Math.max(0, Math.min(visibleWidth(emitted), width - 1));
59533
+ if (cursorPos.col > maxCol) cursorPos = { ...cursorPos, col: maxCol };
59534
+ }
59535
+ }
59405
59536
  const widthChanged = this.previousWidth !== 0 && this.previousWidth !== width;
59406
59537
  const fullRender = (clear) => {
59407
59538
  this.fullRedrawCount += 1;
@@ -59423,6 +59554,7 @@ var TUI = class extends Container {
59423
59554
  this.previousViewportTop = Math.max(0, this.maxLinesRendered - height);
59424
59555
  this.positionHardwareCursor(cursorPos, newLines.length);
59425
59556
  this.previousLines = newLines;
59557
+ this.previousRawLines = rawLines;
59426
59558
  this.previousWidth = width;
59427
59559
  };
59428
59560
  const debugRedraw = process.env.PIE_DEBUG_REDRAW === "1";
@@ -59452,6 +59584,9 @@ var TUI = class extends Container {
59452
59584
  const { firstChanged, lastChanged, appendStart } = findChangedRange(this.previousLines, newLines);
59453
59585
  if (firstChanged === -1) {
59454
59586
  this.positionHardwareCursor(cursorPos, newLines.length);
59587
+ this.previousLines = newLines;
59588
+ this.previousRawLines = rawLines;
59589
+ this.previousWidth = width;
59455
59590
  this.previousViewportTop = Math.max(0, this.maxLinesRendered - height);
59456
59591
  return;
59457
59592
  }
@@ -59486,6 +59621,7 @@ var TUI = class extends Container {
59486
59621
  }
59487
59622
  this.positionHardwareCursor(cursorPos, newLines.length);
59488
59623
  this.previousLines = newLines;
59624
+ this.previousRawLines = rawLines;
59489
59625
  this.previousWidth = width;
59490
59626
  this.previousViewportTop = Math.max(0, this.maxLinesRendered - height);
59491
59627
  return;
@@ -59522,33 +59658,7 @@ var TUI = class extends Container {
59522
59658
  for (let i = firstChanged; i <= renderEnd; i++) {
59523
59659
  if (i > firstChanged) buffer += "\r\n";
59524
59660
  buffer += "\x1B[2K";
59525
- const line = newLines[i];
59526
- const isImage = isImageLine(line);
59527
- if (!isImage && visibleWidth(line) > width) {
59528
- const crashLogPath = path9.join(os3.homedir(), ".pie", "pie-crash.log");
59529
- const crashData = [
59530
- `Crash at ${(/* @__PURE__ */ new Date()).toISOString()}`,
59531
- `Terminal width: ${width}`,
59532
- `Line ${i} visible width: ${visibleWidth(line)}`,
59533
- "",
59534
- "=== All rendered lines ===",
59535
- ...newLines.map((l, idx) => `[${idx}] (w=${visibleWidth(l)}) ${l}`),
59536
- ""
59537
- ].join("\n");
59538
- fs10.mkdirSync(path9.dirname(crashLogPath), { recursive: true });
59539
- fs10.writeFileSync(crashLogPath, crashData);
59540
- this.stop();
59541
- const errorMsg = [
59542
- `Rendered line ${i} exceeds terminal width (${visibleWidth(line)} > ${width}).`,
59543
- "",
59544
- "This is likely caused by a custom TUI component not truncating its output.",
59545
- "Use visibleWidth() to measure and truncateToWidth() to truncate lines.",
59546
- "",
59547
- `Debug log written to: ${crashLogPath}`
59548
- ].join("\n");
59549
- throw new Error(errorMsg);
59550
- }
59551
- buffer += lineForWrite(line);
59661
+ buffer += lineForWrite(newLines[i]);
59552
59662
  }
59553
59663
  let finalCursorRow = renderEnd;
59554
59664
  if (this.previousLines.length > newLines.length) {
@@ -59599,6 +59709,7 @@ var TUI = class extends Container {
59599
59709
  this.previousViewportTop = Math.max(0, this.maxLinesRendered - height);
59600
59710
  this.positionHardwareCursor(cursorPos, newLines.length);
59601
59711
  this.previousLines = newLines;
59712
+ this.previousRawLines = rawLines;
59602
59713
  this.previousWidth = width;
59603
59714
  }
59604
59715
  /**
@@ -64637,6 +64748,15 @@ var ExtensionRunner = class {
64637
64748
  setActiveTools: (toolNames) => this.actions.setActiveTools(toolNames),
64638
64749
  getActiveTools: () => this.actions.getActiveTools(),
64639
64750
  sendUserMessage: (content) => this.actions.sendUserMessage(content),
64751
+ ...this.actions.attachLocalFile ? {
64752
+ attachLocalFile: (path34, displayName) => this.actions.attachLocalFile(path34, displayName)
64753
+ } : {},
64754
+ ...this.actions.beginUserInput ? {
64755
+ beginUserInput: (isCurrent) => this.actions.beginUserInput(isCurrent)
64756
+ } : {},
64757
+ ...this.actions.attachLocalFileAndSubmit ? {
64758
+ attachLocalFileAndSubmit: (path34, displayName, submitText) => this.actions.attachLocalFileAndSubmit(path34, displayName, submitText)
64759
+ } : {},
64640
64760
  setPlanMode: (enabled, options) => this.actions.setPlanMode(enabled, options),
64641
64761
  setExecutionReminder: (text) => this.actions.setExecutionReminder(text),
64642
64762
  getExecutionState: () => this.actions.getExecutionState(),
@@ -66470,6 +66590,11 @@ var theme = new Proxy({}, {
66470
66590
  });
66471
66591
  function setGlobalTheme(t) {
66472
66592
  globalThis[THEME_KEY] = t;
66593
+ themeEpoch++;
66594
+ }
66595
+ var themeEpoch = 1;
66596
+ function getThemeEpoch() {
66597
+ return themeEpoch;
66473
66598
  }
66474
66599
  var currentThemeName;
66475
66600
  var themeWatcher;
@@ -67016,9 +67141,6 @@ function validateSkillName(name) {
67016
67141
  return { valid: true };
67017
67142
  }
67018
67143
 
67019
- // src/interactive-mode.ts
67020
- import * as path30 from "path";
67021
-
67022
67144
  // src/capabilities/read-file-understanding.ts
67023
67145
  import * as path21 from "node:path";
67024
67146
  import * as fs22 from "node:fs/promises";
@@ -68160,6 +68282,20 @@ var FileCache = class _FileCache {
68160
68282
  getByHash(hash) {
68161
68283
  return this.index.files[hash];
68162
68284
  }
68285
+ /** Repair historical image MIME labels using the cached bytes. Content
68286
+ * deduplication must not keep an old .png-derived label when the same
68287
+ * Feishu JPEG is staged again with the corrected .jpg name. The hash,
68288
+ * bytes and platform upload records stay unchanged. */
68289
+ refreshImageMimeType(hash) {
68290
+ const entry = this.index.files[hash];
68291
+ if (!entry || !entry.mimeType.startsWith("image/")) return;
68292
+ const localPath = this.getLocalPath(hash);
68293
+ if (!localPath) return;
68294
+ const detected = readImageFormat(localPath);
68295
+ if (detected.ext === ".bin" || detected.mime === entry.mimeType) return;
68296
+ entry.mimeType = detected.mime;
68297
+ this.saveIndex();
68298
+ }
68163
68299
  getByFileId(fileId, platform2) {
68164
68300
  for (const entry of Object.values(this.index.files)) {
68165
68301
  const platformRecord = entry.platforms[platform2];
@@ -69263,6 +69399,7 @@ var FileManager = class {
69263
69399
  if (!cached) {
69264
69400
  await this.fileCache.storeFile(resolvedPath, sourceName, mimeType);
69265
69401
  }
69402
+ this.fileCache.refreshImageMimeType(hash);
69266
69403
  this.fileCache.rememberPathHint(resolvedPath, hash, sourceName);
69267
69404
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69268
69405
  const entry = this.fileCache.getByHash(hash);
@@ -70681,20 +70818,27 @@ var ExecutionStateManager = class {
70681
70818
  // src/session-trace.ts
70682
70819
  import * as fs23 from "node:fs";
70683
70820
  import * as path23 from "node:path";
70684
- function saveTraceFileBestEffort(tracePath, content) {
70685
- const tempPath = `${tracePath}.${process.pid}.${Date.now()}.tmp`;
70821
+ var traceTempSequence = 0;
70822
+ async function saveTraceFileBestEffort(tracePath, content) {
70823
+ const tempPath = `${tracePath}.${process.pid}.${Date.now()}.${traceTempSequence++}.tmp`;
70686
70824
  try {
70687
- fs23.writeFileSync(tempPath, content, "utf8");
70825
+ await fs23.promises.writeFile(tempPath, content, "utf8");
70688
70826
  try {
70689
- fs23.renameSync(tempPath, tracePath);
70827
+ await fs23.promises.rename(tempPath, tracePath);
70690
70828
  return;
70691
70829
  } catch {
70830
+ const stagedPath = `${tracePath}.${process.pid}.${Date.now()}.staged`;
70692
70831
  try {
70693
- fs23.copyFileSync(tempPath, tracePath);
70832
+ await fs23.promises.copyFile(tempPath, stagedPath);
70833
+ await fs23.promises.rename(stagedPath, tracePath);
70694
70834
  } catch {
70835
+ try {
70836
+ await fs23.promises.unlink(stagedPath);
70837
+ } catch {
70838
+ }
70695
70839
  } finally {
70696
70840
  try {
70697
- fs23.unlinkSync(tempPath);
70841
+ await fs23.promises.unlink(tempPath);
70698
70842
  } catch {
70699
70843
  }
70700
70844
  }
@@ -70715,10 +70859,11 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
70715
70859
  return null;
70716
70860
  }
70717
70861
  },
70718
- saveTrace: (trace) => {
70719
- const tracePath = path23.join(sessionsDir, `${trace.sessionId}.trace.json`);
70720
- saveTraceFileBestEffort(tracePath, JSON.stringify(trace, null, 2));
70721
- }
70862
+ saveTraceJson: (json, sessionId) => {
70863
+ const tracePath = path23.join(sessionsDir, `${sessionId}.trace.json`);
70864
+ return saveTraceFileBestEffort(tracePath, json);
70865
+ },
70866
+ saveTrace: () => void 0
70722
70867
  });
70723
70868
  this.sessionsDir = sessionsDir;
70724
70869
  this.sessionManager = sessionManager;
@@ -72127,10 +72272,30 @@ var AssistantMessageComponent = class extends Container {
72127
72272
  calmMode = false;
72128
72273
  markdownTheme;
72129
72274
  lastMessage;
72275
+ streaming;
72276
+ highlightCache = /* @__PURE__ */ new Map();
72277
+ usedHighlightKeys = /* @__PURE__ */ new Set();
72278
+ pruneHighlightCache = false;
72130
72279
  constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme()) {
72131
72280
  super();
72132
72281
  this.hideThinkingBlock = hideThinkingBlock;
72133
- this.markdownTheme = markdownTheme;
72282
+ this.streaming = !message;
72283
+ const highlightCode2 = markdownTheme.highlightCode;
72284
+ this.markdownTheme = highlightCode2 ? {
72285
+ ...markdownTheme,
72286
+ // Cache keys carry the theme epoch: highlighted lines embed theme
72287
+ // colors, so a theme swap must miss instead of serving stale colors,
72288
+ // even when a holder (calm-mode held component) skipped invalidate().
72289
+ highlightCode: (code, lang) => {
72290
+ const key = `${getThemeEpoch()}\0${lang ?? ""}\0${code}`;
72291
+ this.usedHighlightKeys.add(key);
72292
+ const cached = this.highlightCache.get(key);
72293
+ if (cached) return cached;
72294
+ const lines = highlightCode2(code, lang);
72295
+ this.highlightCache.set(key, lines);
72296
+ return lines;
72297
+ }
72298
+ } : markdownTheme;
72134
72299
  this.contentContainer = new Container();
72135
72300
  this.addChild(this.contentContainer);
72136
72301
  if (message) {
@@ -72138,6 +72303,8 @@ var AssistantMessageComponent = class extends Container {
72138
72303
  }
72139
72304
  }
72140
72305
  invalidate() {
72306
+ this.highlightCache.clear();
72307
+ this.usedHighlightKeys.clear();
72141
72308
  super.invalidate();
72142
72309
  if (this.lastMessage) {
72143
72310
  this.updateContent(this.lastMessage);
@@ -72160,8 +72327,35 @@ var AssistantMessageComponent = class extends Container {
72160
72327
  isCalmMode() {
72161
72328
  return this.calmMode;
72162
72329
  }
72330
+ /** The final frame may reuse streamed highlights; afterwards only the
72331
+ * Markdown line cache is needed, so release the duplicate code strings. */
72332
+ finalizeStreaming() {
72333
+ this.streaming = false;
72334
+ }
72335
+ render(width) {
72336
+ this.usedHighlightKeys.clear();
72337
+ try {
72338
+ const lines = super.render(width);
72339
+ if (!this.streaming) {
72340
+ this.highlightCache.clear();
72341
+ } else if (this.pruneHighlightCache) {
72342
+ for (const key of this.highlightCache.keys()) {
72343
+ if (!this.usedHighlightKeys.has(key)) this.highlightCache.delete(key);
72344
+ }
72345
+ }
72346
+ this.pruneHighlightCache = false;
72347
+ return lines;
72348
+ } catch (error) {
72349
+ this.highlightCache.clear();
72350
+ this.pruneHighlightCache = false;
72351
+ throw error;
72352
+ } finally {
72353
+ this.usedHighlightKeys.clear();
72354
+ }
72355
+ }
72163
72356
  updateContent(message) {
72164
72357
  this.lastMessage = message;
72358
+ this.pruneHighlightCache = true;
72165
72359
  this.contentContainer.clear();
72166
72360
  const hasTextContent = message.content.some((c) => c.type === "text" && c.text.trim());
72167
72361
  const hasThinkingContent = message.content.some((c) => c.type === "thinking" && c.thinking.trim());
@@ -73426,6 +73620,7 @@ function renderChatHistory(host, messages) {
73426
73620
  content: contentBlocks,
73427
73621
  ...stopReason ? { stopReason, errorMessage: assistantMsg.errorMessage } : {}
73428
73622
  });
73623
+ msgComponent.finalizeStreaming();
73429
73624
  host.chatContainer.addChild(msgComponent);
73430
73625
  }
73431
73626
  if (Array.isArray(assistantMsg.content) && !host.calmMode) {
@@ -74011,11 +74206,21 @@ function shutdownInteractiveHost(host, idleTimeoutMs = 5e3) {
74011
74206
  } finally {
74012
74207
  clearTimeout(timer);
74013
74208
  }
74209
+ try {
74210
+ await host.sessionTrace?.flushAsync();
74211
+ } catch (error) {
74212
+ errors.push(error);
74213
+ }
74014
74214
  try {
74015
74215
  await host.extensionRunner?.dispose("shutdown");
74016
74216
  } catch (error) {
74017
74217
  errors.push(error);
74018
74218
  }
74219
+ try {
74220
+ await host.sessionTrace?.flushAsync();
74221
+ } catch (error) {
74222
+ errors.push(error);
74223
+ }
74019
74224
  if (errors.length) throw new AggregateError(errors, "Interactive shutdown failed.");
74020
74225
  })().then(resolve7, reject);
74021
74226
  return shutdown;
@@ -74233,6 +74438,7 @@ function bindInteractiveRuntimeEvents(host) {
74233
74438
  }
74234
74439
  host.isProcessing = false;
74235
74440
  revealHeldCalmStreamingComponent(host);
74441
+ host.streamingComponent?.finalizeStreaming?.();
74236
74442
  host.streamingComponent = void 0;
74237
74443
  removeCalmToolLines(host.chatContainer, host.pendingTools);
74238
74444
  host.pendingTools.clear();
@@ -74617,6 +74823,7 @@ function handleMessageEnd(host, event) {
74617
74823
  content: assistantDisplayBlocks(message.content)
74618
74824
  });
74619
74825
  }
74826
+ streaming?.finalizeStreaming?.();
74620
74827
  host.streamingMessageHadToolCalls = false;
74621
74828
  host.streamingComponent = void 0;
74622
74829
  host.footer.setState({ lifecycleEvent: formatFooterLifecycleEvent(event) });
@@ -76051,10 +76258,12 @@ function aggregateToolStats(turns) {
76051
76258
  }
76052
76259
  function buildToolStatRows(turns) {
76053
76260
  const stats = aggregateToolStats(turns);
76261
+ const omitted = turns.reduce((sum, turn) => sum + (turn.toolCallsOmitted ?? 0), 0);
76262
+ const partialRow = omitted > 0 ? [{ id: "tools:omitted", label: "tool statistics are partial", estTokens: 0, note: `${omitted} earlier calls omitted` }] : [];
76054
76263
  if (stats.length === 0) {
76055
- return [{ id: "tools:none", label: "no tool calls in recent turns", estTokens: 0 }];
76264
+ return partialRow.length > 0 ? partialRow : [{ id: "tools:none", label: "no tool calls in recent turns", estTokens: 0 }];
76056
76265
  }
76057
- return stats.map((stat2) => ({
76266
+ return [...partialRow, ...stats.map((stat2) => ({
76058
76267
  id: `toolstat:${stat2.name}`,
76059
76268
  label: stat2.name,
76060
76269
  estTokens: 0,
@@ -76066,7 +76275,7 @@ function buildToolStatRows(turns) {
76066
76275
  rowKind: "plain",
76067
76276
  rows: collectCalls(turns, stat2.name)
76068
76277
  }
76069
- }));
76278
+ }))];
76070
76279
  }
76071
76280
  function collectCalls(turns, toolName) {
76072
76281
  const rows = [];
@@ -76109,7 +76318,7 @@ function buildTurnRows(turns) {
76109
76318
  label: `#${turn.turnIndex} ${turn.state}`,
76110
76319
  estTokens: 0,
76111
76320
  valueText: duration,
76112
- note: `${turn.toolCalls.length} tool calls`,
76321
+ note: `${turn.toolCalls.length} tool calls${turn.toolCallsOmitted ? ` (+${turn.toolCallsOmitted} omitted)` : ""}`,
76113
76322
  detail: turn.userText?.slice(0, 80) || void 0,
76114
76323
  marker: turn.state === "failed" ? "\u2716" : void 0,
76115
76324
  drill: {
@@ -76135,6 +76344,11 @@ ${turn.assistantText.slice(0, 2e3)}`);
76135
76344
  if (turn.errorMessage) parts.push(`
76136
76345
  # error
76137
76346
  ${turn.errorMessage}`);
76347
+ if (turn.toolCallsOmitted) {
76348
+ parts.push(`
76349
+ # tool calls
76350
+ (${turn.toolCallsOmitted} earlier calls omitted from the retention window)`);
76351
+ }
76138
76352
  if (turn.toolCalls.length > 0) {
76139
76353
  parts.push(
76140
76354
  `
@@ -76252,10 +76466,14 @@ function buildTraceOverviewText(snapshot, theme2) {
76252
76466
  lines.push(t.fg("dim", " no retries, compactions, or guard events"));
76253
76467
  }
76254
76468
  }
76255
- const stats = aggregateToolStats([...snapshot.recentTurns, ...snapshot.activeTurn ? [snapshot.activeTurn] : []]);
76469
+ const allTurns = [...snapshot.recentTurns, ...snapshot.activeTurn ? [snapshot.activeTurn] : []];
76470
+ const stats = aggregateToolStats(allTurns);
76471
+ const omitted = allTurns.reduce((sum, turn) => sum + (turn.toolCallsOmitted ?? 0), 0);
76256
76472
  if (stats.length > 0) {
76257
76473
  const top = stats.slice(0, 3).map((s) => `${s.name} ${formatDurationMs(s.totalMs)} (${s.calls})`);
76258
- lines.push(t.fg("dim", ` tools: ${top.join(" \xB7 ")}`));
76474
+ lines.push(t.fg("dim", ` tools: ${top.join(" \xB7 ")}${omitted > 0 ? ` (+${omitted} earlier calls omitted)` : ""}`));
76475
+ } else if (omitted > 0) {
76476
+ lines.push(t.fg("dim", ` tools: (+${omitted} earlier calls omitted)`));
76259
76477
  }
76260
76478
  lines.push(t.fg("dim", " /debugs/trace \xB7 Enter to drill, Esc to go back"));
76261
76479
  return lines.join("\n");
@@ -76565,6 +76783,7 @@ function handleInterrupt(host) {
76565
76783
  host.setSubmitLock(false);
76566
76784
  host.isInterrupting = false;
76567
76785
  revealHeldCalmStreamingComponent(host);
76786
+ host.streamingComponent?.finalizeStreaming?.();
76568
76787
  host.streamingComponent = void 0;
76569
76788
  host.footer.setState({ status: "ready" });
76570
76789
  host.safeRequestRender();
@@ -77094,10 +77313,7 @@ function registerBuiltinCommands(host) {
77094
77313
  });
77095
77314
  commandTree.register(["settings", "quit"], {
77096
77315
  description: "Exit Pie",
77097
- handler: () => {
77098
- host.ui.stop();
77099
- process.exit(0);
77100
- }
77316
+ handler: () => host.cleanupAndExit()
77101
77317
  });
77102
77318
  }
77103
77319
 
@@ -78889,7 +79105,23 @@ var MentionHandler = class {
78889
79105
  * behave identically.
78890
79106
  */
78891
79107
  async attachLocalFile(path34, displayName, pendingMention, isCurrent) {
78892
- await this.attachFileFromPath(path34, { sourceName: displayName, queryToken: pendingMention, isCurrent });
79108
+ return this.attachFileFromPath(path34, { sourceName: displayName, queryToken: pendingMention, isCurrent });
79109
+ }
79110
+ /** Prepare a message-owned mention without reading or mutating the editor.
79111
+ * Errors propagate to the caller; no caption-only submission on failure. */
79112
+ async prepareLocalFile(filePath, displayName, isCurrent = () => true) {
79113
+ if (!isCurrent()) return void 0;
79114
+ const sourceName = displayName ?? path28.basename(filePath);
79115
+ const result = await this.prepareFile(filePath, sourceName);
79116
+ if (!isCurrent() || !result.success) return void 0;
79117
+ return this.formatMention(result.name, result.fileId);
79118
+ }
79119
+ prepareFile(filePath, sourceName, onProgress) {
79120
+ const mimeType = FileCache.getMimeType(filePath);
79121
+ const isMedia = mimeType.startsWith("image/") || mimeType.startsWith("video/");
79122
+ if (!isMedia || !this.fileManager.hasAdapter()) return this.fileManager.stageLocal(filePath, { sourceName });
79123
+ if (mimeType.startsWith("video/")) return this.fileManager.uploadVideo(filePath, { sourceName, onProgress });
79124
+ return this.fileManager.uploadPic(filePath, { sourceName, onProgress });
78893
79125
  }
78894
79126
  /** Reserve a real queue-replaceable token before asynchronous clipboard IO. */
78895
79127
  beginPendingAttachment(name) {
@@ -78942,29 +79174,22 @@ var MentionHandler = class {
78942
79174
  this.formatUploadProgress(sourceName, progress, stage)
78943
79175
  );
78944
79176
  };
78945
- const result = shouldStageLocally ? await this.fileManager.stageLocal(path34, { sourceName }) : mimeType.startsWith("video/") ? await this.fileManager.uploadVideo(path34, {
78946
- sourceName,
78947
- onProgress
78948
- }) : mimeType.startsWith("image/") ? await this.fileManager.uploadPic(path34, {
78949
- sourceName,
78950
- onProgress
78951
- }) : await this.fileManager.upload(path34, {
78952
- sourceName,
78953
- onProgress
78954
- });
78955
- if (options.isCurrent && !options.isCurrent()) return;
79177
+ const result = await this.prepareFile(path34, sourceName, onProgress);
79178
+ if (options.isCurrent && !options.isCurrent()) return false;
78956
79179
  if (result.success) {
78957
79180
  const mention = this.formatMention(result.name, result.fileId);
78958
79181
  this.onInsertMention(mention, pendingMention);
78959
79182
  this.clearPendingStatus(pendingStatusKey);
78960
79183
  this.onStatus?.(shouldStageLocally ? `Added local file ${result.name}` : `Added ${result.name}`);
79184
+ return true;
78961
79185
  } else {
78962
79186
  this.onInsertMention("", pendingMention);
78963
79187
  this.clearPendingStatus(pendingStatusKey);
78964
79188
  this.onError?.("File upload failed. Please try again.");
79189
+ return false;
78965
79190
  }
78966
79191
  } catch (err) {
78967
- if (options.isCurrent && !options.isCurrent()) return;
79192
+ if (options.isCurrent && !options.isCurrent()) return false;
78968
79193
  this.onInsertMention("", pendingMention);
78969
79194
  this.clearPendingStatus(pendingStatusKey);
78970
79195
  const errorMessage5 = err instanceof Error ? err.message : String(err);
@@ -78979,6 +79204,7 @@ var MentionHandler = class {
78979
79204
  } else {
78980
79205
  this.onError?.(`Upload failed: ${errorMessage5}`);
78981
79206
  }
79207
+ return false;
78982
79208
  } finally {
78983
79209
  this.clearPendingStatus(pendingStatusKey);
78984
79210
  this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
@@ -79746,13 +79972,22 @@ function setupInteractiveKeybindings(host) {
79746
79972
  host.handleFollowUp();
79747
79973
  return { consume: true };
79748
79974
  }
79975
+ const shortcuts = host.getExtensionShortcuts?.();
79976
+ if (shortcuts && shortcuts.size > 0 && mainEditorOwnsInput(host)) {
79977
+ for (const shortcut of shortcuts.values()) {
79978
+ if (matchesKey(data, shortcut.key)) {
79979
+ void shortcut.handler();
79980
+ return { consume: true };
79981
+ }
79982
+ }
79983
+ }
79749
79984
  return { consume: false };
79750
79985
  });
79751
79986
  }
79752
79987
 
79753
79988
  // src/clipboard/read-clipboard-image.ts
79754
79989
  import { spawn as spawn2 } from "node:child_process";
79755
- import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync18, fchmodSync, mkdtempSync, openSync as openSync4, readFileSync as readFileSync17, rmdirSync, statSync as statSync12, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "node:fs";
79990
+ import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync18, fchmodSync, mkdtempSync, openSync as openSync4, readFileSync as readFileSync17, rmdirSync, statSync as statSync12, unlinkSync as unlinkSync7, writeFileSync as writeFileSync11 } from "node:fs";
79756
79991
  import { tmpdir } from "node:os";
79757
79992
  import { join as join20 } from "node:path";
79758
79993
  var MAX_IMAGE_BYTES = 50 * 1024 * 1024;
@@ -79843,7 +80078,7 @@ var TempImageResources = class {
79843
80078
  cleanup = () => {
79844
80079
  for (const path34 of this.files) {
79845
80080
  try {
79846
- unlinkSync8(path34);
80081
+ unlinkSync7(path34);
79847
80082
  this.files.delete(path34);
79848
80083
  } catch (err) {
79849
80084
  if (err.code === "ENOENT") this.files.delete(path34);
@@ -80061,7 +80296,7 @@ function writeImageBytes(bytes, declaredMime, mkTempPath) {
80061
80296
  const mimeType = detectMimeFromMagic(bytes) ?? declaredMime;
80062
80297
  const targetPath = mkTempPath(extensionForMime(mimeType));
80063
80298
  try {
80064
- writeFileSync12(targetPath, bytes, { flag: "r+", mode: 384 });
80299
+ writeFileSync11(targetPath, bytes, { flag: "r+", mode: 384 });
80065
80300
  } catch (err) {
80066
80301
  return { ok: false, kind: "failed", message: `Failed to write temp image: ${errorMessage4(err)}` };
80067
80302
  }
@@ -80132,7 +80367,7 @@ function formatBytes2(bytes) {
80132
80367
  }
80133
80368
 
80134
80369
  // src/input/image-paste.ts
80135
- import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync13, rmSync as rmSync3 } from "node:fs";
80370
+ import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync12, rmSync as rmSync3 } from "node:fs";
80136
80371
  import { tmpdir as tmpdir2 } from "node:os";
80137
80372
  import { join as join21 } from "node:path";
80138
80373
  var pastingHosts = /* @__PURE__ */ new WeakSet();
@@ -80161,7 +80396,7 @@ function terminalResultToClipboardResult(result) {
80161
80396
  try {
80162
80397
  dir = mkdtempSync2(join21(tmpdir2(), "pie-osc5522-"));
80163
80398
  const path34 = join21(dir, formatPastedImageName(/* @__PURE__ */ new Date(), ext));
80164
- writeFileSync13(path34, result.bytes);
80399
+ writeFileSync12(path34, result.bytes);
80165
80400
  return {
80166
80401
  ok: true,
80167
80402
  path: path34,
@@ -80935,6 +81170,7 @@ function setupUI(host) {
80935
81170
  contextWindow: host.options.model?.contextWindow ?? 0
80936
81171
  });
80937
81172
  onThemeChange(() => {
81173
+ host.streamingComponent?.invalidate();
80938
81174
  host.ui.invalidate();
80939
81175
  host.ui.requestRender();
80940
81176
  });
@@ -81492,6 +81728,7 @@ async function recoverHardStalledTurn(host, stalledMs) {
81492
81728
  host.isProcessing = false;
81493
81729
  host.setSubmitLock(false);
81494
81730
  revealHeldCalmStreamingComponent(host);
81731
+ host.streamingComponent?.finalizeStreaming?.();
81495
81732
  host.streamingComponent = void 0;
81496
81733
  removeCalmToolLines(host.chatContainer, host.pendingTools);
81497
81734
  host.pendingTools.clear();
@@ -81839,6 +82076,11 @@ function createInteractiveAgentSessionController(host) {
81839
82076
  lastShownCompactionError = null;
81840
82077
  host.setControllerBusy(false);
81841
82078
  } else if (event.type === "turn_cancelled") {
82079
+ host.emitExtensionEvent({
82080
+ type: "turn:cancelled",
82081
+ turnIndex: host._semanticTurnIndex,
82082
+ reason: event.reason
82083
+ });
81842
82084
  const outcome = host.executionState.abort();
81843
82085
  host.logDebug("execution:turn_cancelled", {
81844
82086
  turnIndex: host._semanticTurnIndex,
@@ -82747,6 +82989,51 @@ function summarizeToolNames(tools) {
82747
82989
  return tools.map((tool) => tool.name).join(", ");
82748
82990
  }
82749
82991
 
82992
+ // src/extensions/user-input-scope.ts
82993
+ function beginUserInput(host, extensionIsCurrent = () => true) {
82994
+ const session = host.sessionManager.getActiveSession();
82995
+ const controller = host.agentSessionController;
82996
+ const runner = host.extensionRunner;
82997
+ const handler = host.attachmentMentionHandler?.();
82998
+ if (!session || !controller || !runner || !handler) return void 0;
82999
+ let retired = false;
83000
+ let used = false;
83001
+ const unsubscribe = controller.subscribe((event) => {
83002
+ if (event.type === "session_invalidated") retired = true;
83003
+ });
83004
+ const dispose = runner.trackUnregisterHandle(() => {
83005
+ retired = true;
83006
+ unsubscribe();
83007
+ });
83008
+ const isCurrent = () => !retired && host.sessionManager.getActiveSession() === session && host.agentSessionController === controller && host.extensionRunner === runner && host.attachmentMentionHandler?.() === handler && extensionIsCurrent();
83009
+ return {
83010
+ isCurrent,
83011
+ dispose,
83012
+ async submit(text, files = []) {
83013
+ if (used || !isCurrent() || !text.trim() && !files.length) return false;
83014
+ used = true;
83015
+ try {
83016
+ const mentions = [];
83017
+ for (const file of files) {
83018
+ if (!isCurrent()) return false;
83019
+ const mention = await handler.prepareLocalFile(file.path, file.displayName, isCurrent);
83020
+ if (!mention || !isCurrent()) return false;
83021
+ mentions.push(mention.trim());
83022
+ }
83023
+ let content = [text.trim(), ...mentions].filter(Boolean).join("\n");
83024
+ const decision = await runner.emitTerminalInput({ type: "terminal:input", text: content });
83025
+ if (!isCurrent() || decision.action === "consume") return false;
83026
+ if (decision.action === "transform") content = decision.text.trim();
83027
+ if (!content || mentions.some((mention) => !content.includes(mention))) return false;
83028
+ host.queueMessage(content, "followUp", { clearEditor: false, deliverWhileStreaming: false });
83029
+ return true;
83030
+ } catch {
83031
+ return false;
83032
+ }
83033
+ }
83034
+ };
83035
+ }
83036
+
82750
83037
  // src/extensions/interactive-extension-loader.ts
82751
83038
  async function loadInteractiveExtensions(host) {
82752
83039
  const extensionActions = {
@@ -82768,6 +83055,21 @@ async function loadInteractiveExtensions(host) {
82768
83055
  void host.editor.onSubmit(content);
82769
83056
  }
82770
83057
  },
83058
+ attachLocalFile: async (path34, displayName) => {
83059
+ const handler = host.attachmentMentionHandler?.();
83060
+ if (!handler) return false;
83061
+ return handler.attachLocalFile(path34, displayName);
83062
+ },
83063
+ beginUserInput: (isCurrent) => beginUserInput(host, isCurrent),
83064
+ attachLocalFileAndSubmit: async (path34, displayName, submitText) => {
83065
+ const scope = beginUserInput(host);
83066
+ if (!scope) return false;
83067
+ try {
83068
+ return await scope.submit(submitText, [{ path: path34, displayName }]);
83069
+ } finally {
83070
+ scope.dispose();
83071
+ }
83072
+ },
82771
83073
  setPlanMode: (enabled, options) => {
82772
83074
  const shouldEmitTransition = host.planModeEnabled !== enabled || options?.forcePromptInjection;
82773
83075
  if (shouldEmitTransition) {
@@ -82936,6 +83238,74 @@ async function loadInteractiveExtensions(host) {
82936
83238
  }
82937
83239
  }
82938
83240
 
83241
+ // src/extensions/interactive-runtime-reload.ts
83242
+ import { AsyncLocalStorage } from "async_hooks";
83243
+ import * as os8 from "os";
83244
+ import * as path30 from "path";
83245
+ var runtimeReloadContext = new AsyncLocalStorage();
83246
+ var activeReloadTokens = /* @__PURE__ */ new WeakMap();
83247
+ async function reloadInteractiveRuntime(host) {
83248
+ if (host.isProcessing) {
83249
+ throw new Error("Cannot reload while a response is being processed. Wait for the turn to finish or abort it first.");
83250
+ }
83251
+ const ancestors = runtimeReloadContext.getStore();
83252
+ const activeToken = ancestors ? activeReloadTokens.get(host) : void 0;
83253
+ if (activeToken !== void 0 && ancestors?.get(host) === activeToken) {
83254
+ throw new Error("Cannot reenter runtime reload from its own extension callbacks.");
83255
+ }
83256
+ if (host.runtimeReloadPromise) return host.runtimeReloadPromise;
83257
+ const token = {};
83258
+ const context = new Map(ancestors ?? []);
83259
+ context.set(host, token);
83260
+ const pending = Promise.resolve().then(() => runtimeReloadContext.run(context, async () => {
83261
+ const acquiredReloadLock = !host.submitLock;
83262
+ if (acquiredReloadLock) host.submitLock = true;
83263
+ activeReloadTokens.set(host, token);
83264
+ try {
83265
+ await host.reloadRuntimeWhileLocked();
83266
+ } finally {
83267
+ if (activeReloadTokens.get(host) === token) activeReloadTokens.delete(host);
83268
+ if (acquiredReloadLock) host.submitLock = false;
83269
+ }
83270
+ }));
83271
+ host.runtimeReloadPromise = pending;
83272
+ try {
83273
+ await pending;
83274
+ } finally {
83275
+ if (host.runtimeReloadPromise === pending) host.runtimeReloadPromise = void 0;
83276
+ }
83277
+ }
83278
+ async function reloadInteractiveRuntimeWhileLocked(host) {
83279
+ const builtinDir = path30.join(getBuiltinDir(), "skills");
83280
+ const userDir = path30.join(os8.homedir(), ".pie", "skills");
83281
+ const projectDir = path30.join(host.options.cwd, ".pie", "skills");
83282
+ const { skills, diagnostics } = loadSkills({
83283
+ dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: host.options.cwd })
83284
+ });
83285
+ host.skills = skills;
83286
+ host.skillsSection = formatSkillsForPrompt(skills);
83287
+ for (const diag of diagnostics.filter((entry) => entry.type === "collision")) {
83288
+ host.chatContainer.addChild(new Text(theme.fg("dim", `[reload] ${diag.message}`), 1, 0));
83289
+ }
83290
+ host.allTools = [...host.options.allTools];
83291
+ host.originalTools = [...host.options.allTools];
83292
+ host.pristineTools = [...host.options.allTools];
83293
+ host.agent.state.tools = [...host.options.allTools];
83294
+ resetCliHostEnvelopeRegistry();
83295
+ const previousRunner = host.extensionRunner;
83296
+ host.extensionRunner = void 0;
83297
+ try {
83298
+ await previousRunner?.dispose("reload");
83299
+ } finally {
83300
+ host.extensionWidgets?.clearAll();
83301
+ }
83302
+ await host.loadExtensions();
83303
+ host.applyRuntimeToolMode();
83304
+ host.noteRuntimeModeChanged("extension_reload");
83305
+ await host.syncSessionScopedState();
83306
+ host.ui.requestRender();
83307
+ }
83308
+
82939
83309
  // src/interactive-mode.ts
82940
83310
  var InteractiveMode = class {
82941
83311
  agent;
@@ -83302,7 +83672,7 @@ var InteractiveMode = class {
83302
83672
  async init() {
83303
83673
  const activeSession = this.sessionManager.getActiveSession();
83304
83674
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
83305
- const { Agent } = await import("./chunks/src-5JJZZ5H7.js");
83675
+ const { Agent } = await import("./chunks/src-IY6TEIL7.js");
83306
83676
  this.agent = new Agent({
83307
83677
  initialState: {
83308
83678
  systemPrompt: this.buildSystemPrompt(),
@@ -83398,51 +83768,11 @@ var InteractiveMode = class {
83398
83768
  });
83399
83769
  await loadInteractiveExtensions(this.extensionLoaderHost);
83400
83770
  }
83401
- /**
83402
- * Full runtime reload (extension API `ctx.reload()`): re-discover skills
83403
- * and extensions from disk and rebuild the tool surface and system prompt.
83404
- * Rejects while a turn is processing. Listener lifecycle contract: the old
83405
- * extension instances are dropped entirely (handlers, tools, commands),
83406
- * the tool surface is reset to the pristine composition baseline, and the
83407
- * fresh ExtensionRunner re-wraps from that baseline — no stale handlers
83408
- * and no double-wrapped tools survive a reload.
83409
- */
83410
83771
  async reloadRuntime() {
83411
- if (this.isProcessing) {
83412
- throw new Error("Cannot reload while a response is being processed. Wait for the turn to finish or abort it first.");
83413
- }
83414
- const acquiredReloadLock = !this.submitLock;
83415
- if (acquiredReloadLock) this.submitLock = true;
83416
- try {
83417
- await this.reloadRuntimeWhileLocked();
83418
- } finally {
83419
- if (acquiredReloadLock) this.submitLock = false;
83420
- }
83772
+ return reloadInteractiveRuntime(this);
83421
83773
  }
83422
83774
  async reloadRuntimeWhileLocked() {
83423
- const builtinDir = path30.join(getBuiltinDir(), "skills");
83424
- const userDir = path30.join(os8.homedir(), ".pie", "skills");
83425
- const projectDir = path30.join(this.options.cwd, ".pie", "skills");
83426
- const { skills, diagnostics } = loadSkills({
83427
- dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: this.options.cwd })
83428
- });
83429
- this.skills = skills;
83430
- this.skillsSection = formatSkillsForPrompt(skills);
83431
- for (const diag of diagnostics.filter((entry) => entry.type === "collision")) {
83432
- this.chatContainer.addChild(new Text(theme.fg("dim", `[reload] ${diag.message}`), 1, 0));
83433
- }
83434
- this.allTools = [...this.options.allTools];
83435
- this.originalTools = [...this.options.allTools];
83436
- this.pristineTools = [...this.options.allTools];
83437
- this.agent.state.tools = [...this.options.allTools];
83438
- resetCliHostEnvelopeRegistry();
83439
- await this.extensionRunner?.dispose("reload");
83440
- this.extensionWidgets?.clearAll();
83441
- await this.loadExtensions();
83442
- this.applyRuntimeToolMode();
83443
- this.noteRuntimeModeChanged("extension_reload");
83444
- await this.syncSessionScopedState();
83445
- this.ui.requestRender();
83775
+ return reloadInteractiveRuntimeWhileLocked(this);
83446
83776
  }
83447
83777
  calculateContextUsage() {
83448
83778
  return calculateContextUsage(this.contextUiHost);
@@ -83669,9 +83999,41 @@ var InteractiveMode = class {
83669
83999
  return interactiveCommands.some((cmd) => commandPath.startsWith(cmd));
83670
84000
  }
83671
84001
  toggleToolsExpansion() {
84002
+ if (this.calmMode) {
84003
+ this.setCalmMode(false);
84004
+ if (this.toolOutputExpanded) toggleToolsExpansion(this.uiHelperHost);
84005
+ this.options.settingsManager.setToolOutputExpanded(this.toolOutputExpanded);
84006
+ return;
84007
+ }
84008
+ if (this.toolOutputExpanded) {
84009
+ this.setCalmMode(true);
84010
+ return;
84011
+ }
83672
84012
  toggleToolsExpansion(this.uiHelperHost);
83673
84013
  this.options.settingsManager.setToolOutputExpanded(this.toolOutputExpanded);
83674
84014
  }
84015
+ /** Aggregated extension shortcut registrations for the keybinding
84016
+ * dispatcher (duck-typed onto InteractiveKeybindingsHost). Handlers are
84017
+ * bound to the extension runner context here. */
84018
+ getExtensionShortcuts() {
84019
+ const merged = /* @__PURE__ */ new Map();
84020
+ for (const extension of this.extensions) {
84021
+ for (const [key, shortcut] of extension.shortcuts) {
84022
+ if (merged.has(key)) continue;
84023
+ const context = this.extensionRunner?.createContext();
84024
+ merged.set(key, {
84025
+ key,
84026
+ handler: () => shortcut.handler(context)
84027
+ });
84028
+ }
84029
+ }
84030
+ return merged;
84031
+ }
84032
+ /** Live mention handler for extension-driven attachments (duck-typed onto
84033
+ * InteractiveExtensionLoaderHost); undefined while attachments init. */
84034
+ attachmentMentionHandler() {
84035
+ return this.mentionHandler;
84036
+ }
83675
84037
  showSelector(create) {
83676
84038
  return showSelector(this.uiHelperHost, create);
83677
84039
  }
@@ -84194,7 +84556,7 @@ async function runPrintMode(params) {
84194
84556
  `);
84195
84557
  console.log("Assistant: ");
84196
84558
  }
84197
- const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
84559
+ const { Agent: AgentClass } = await import("./chunks/src-IY6TEIL7.js");
84198
84560
  let agent;
84199
84561
  const printModeExtensions = await loadPrintModeExtensions({
84200
84562
  cwd: runtime.cwd,
@@ -84383,7 +84745,7 @@ async function runPrintMode(params) {
84383
84745
  if (!params.rawOutput && !params.jsonOutput) console.log("");
84384
84746
  agentSessionController.dispose();
84385
84747
  await runtime.sessionManager.save();
84386
- sessionTrace.flush();
84748
+ await sessionTrace.flushAsync();
84387
84749
  } finally {
84388
84750
  await printModeExtensions.runner.dispose().catch((error) => {
84389
84751
  console.error("Extension cleanup failed:", error);
@@ -85089,7 +85451,7 @@ async function runJsonEventsMode(params) {
85089
85451
  const sideTrace = trace;
85090
85452
  const sideApiKey = runtime.apiKey;
85091
85453
  const snapshot = structuredClone(parent.state.messages);
85092
- const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
85454
+ const { Agent: AgentClass } = await import("./chunks/src-IY6TEIL7.js");
85093
85455
  const sideAgent = new AgentClass({
85094
85456
  initialState: {
85095
85457
  systemPrompt: parent.state.systemPrompt,
@@ -85398,7 +85760,7 @@ async function runJsonEventsMode(params) {
85398
85760
  await runtime?.sessionManager.save();
85399
85761
  } catch {
85400
85762
  }
85401
- trace?.flush();
85763
+ await trace?.flushAsync();
85402
85764
  trace = void 0;
85403
85765
  cleanupBashOutputArtifacts();
85404
85766
  }
@@ -85453,7 +85815,7 @@ async function runJsonEventsMode(params) {
85453
85815
  if (runtime.initialModel.unconfigured || controller) {
85454
85816
  return;
85455
85817
  }
85456
- const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
85818
+ const { Agent: AgentClass } = await import("./chunks/src-IY6TEIL7.js");
85457
85819
  if (runtimeClosing) return;
85458
85820
  const compositionRegistry = createCliHostEnvelopeRegistry();
85459
85821
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, envelopeRegistry: compositionRegistry, getAgent: () => agent });
@@ -85820,7 +86182,7 @@ async function runJsonEventsMode(params) {
85820
86182
  if (activeTurn === turn) emitSettled(turn);
85821
86183
  } finally {
85822
86184
  clearInterval(progressTimer);
85823
- trace?.flush();
86185
+ await trace?.flushAsync();
85824
86186
  await activeRuntime?.sessionManager.save();
85825
86187
  if (activeTurn === turn) activeTurn = null;
85826
86188
  }
@@ -86141,7 +86503,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
86141
86503
  }
86142
86504
  async function createCliTestStreamFnFromEnv() {
86143
86505
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
86144
- return (await import("./chunks/test-stream-ZBTSPU7J.js")).createTestStreamFnFromSequenceEnv();
86506
+ return (await import("./chunks/test-stream-EXSL5TIO.js")).createTestStreamFnFromSequenceEnv();
86145
86507
  }
86146
86508
  function resolveJsonEventsDetailFlag() {
86147
86509
  for (let index = 0; index < process.argv.length; index += 1) {