@cydm/pie 2.6.4 → 2.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -7,10 +7,11 @@ import {
7
7
  createCliWebResearchHealthStore,
8
8
  formatSize,
9
9
  truncateHeadTail
10
- } from "./chunks/chunk-6QEDYZUR.js";
10
+ } from "./chunks/chunk-OWSXALCG.js";
11
11
  import {
12
12
  AGENTS_CONTEXT_FILE_NAME,
13
13
  AgentSessionController,
14
+ AttentionCoordinator,
14
15
  BUILTIN_TOOL_CAPABILITY_METADATA,
15
16
  ExtensionResourceScope,
16
17
  FileSessionStore,
@@ -46,6 +47,8 @@ import {
46
47
  getAutoCompactTokenLimit,
47
48
  getExtensionEnablementEntry,
48
49
  hasInteractionHandler,
50
+ interactionAttentionReason,
51
+ interactionAttentionResolution,
49
52
  isCompactSummaryMessage,
50
53
  isExtensionEnabled,
51
54
  maybeAdvanceTodoExecutionState,
@@ -60,8 +63,8 @@ import {
60
63
  setExtensionEnabledInSettings,
61
64
  shouldPreserveExecutionStateForUserText,
62
65
  supersedeExecutionState
63
- } from "./chunks/chunk-3AOMWS2Z.js";
64
- import "./chunks/chunk-YEUBQBOB.js";
66
+ } from "./chunks/chunk-H6BAM2N4.js";
67
+ import "./chunks/chunk-KEARZX6F.js";
65
68
  import {
66
69
  Deref,
67
70
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -85,7 +88,7 @@ import {
85
88
  setLogger,
86
89
  sortToolModelCandidatesByCapability,
87
90
  type_exports
88
- } from "./chunks/chunk-OCEYHWVX.js";
91
+ } from "./chunks/chunk-G5B6D35K.js";
89
92
  import {
90
93
  VERSION,
91
94
  getAgentDir,
@@ -53581,20 +53584,98 @@ function filterEnabledTools(tools, settingsManager) {
53581
53584
  const disabled = getDisabledToolNames(settingsManager);
53582
53585
  return disabled.size > 0 ? tools.filter((tool) => !disabled.has(tool.name)) : [...tools];
53583
53586
  }
53587
+ function isPlainObject(value) {
53588
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53589
+ }
53584
53590
  function mergeSnapshotDelta(atLoad, current, onDisk) {
53585
- const merged = { ...onDisk };
53586
- const load = atLoad;
53587
- const cur = current;
53588
- const allKeys = /* @__PURE__ */ new Set([...Object.keys(load), ...Object.keys(cur)]);
53591
+ return mergeDeltaNode(atLoad, current, onDisk);
53592
+ }
53593
+ function mergeDeltaNode(load, cur, disk) {
53594
+ if (!isPlainObject(disk)) {
53595
+ return JSON.stringify(load) === JSON.stringify(cur) ? disk : cur;
53596
+ }
53597
+ const merged = { ...disk };
53598
+ const l = load === void 0 || load === null ? {} : isPlainObject(load) ? load : null;
53599
+ const c = cur === void 0 || cur === null ? {} : isPlainObject(cur) ? cur : null;
53600
+ if (l === null || c === null) {
53601
+ return JSON.stringify(load) === JSON.stringify(cur) ? disk : cur;
53602
+ }
53603
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(l), ...Object.keys(c)]);
53604
+ const own = (o, key) => Object.hasOwn(o, key) ? o[key] : void 0;
53589
53605
  for (const key of allKeys) {
53590
- const before = JSON.stringify(load[key]);
53591
- const after = JSON.stringify(cur[key]);
53592
- if (before === after) continue;
53593
- if (after === void 0) delete merged[key];
53594
- else merged[key] = cur[key];
53606
+ const before = own(l, key);
53607
+ const after = own(c, key);
53608
+ if (JSON.stringify(before) === JSON.stringify(after)) continue;
53609
+ const diskValue = own(merged, key);
53610
+ if (after === void 0) {
53611
+ if (isPlainObject(before) && isPlainObject(diskValue)) {
53612
+ const pruned = applyNodeDeletion(before, diskValue);
53613
+ if (Object.keys(pruned).length === 0) delete merged[key];
53614
+ else setOwnDataProperty(merged, key, pruned);
53615
+ } else {
53616
+ delete merged[key];
53617
+ }
53618
+ continue;
53619
+ }
53620
+ if (isPlainObject(after) && (isPlainObject(diskValue) || diskValue === void 0 || diskValue === null)) {
53621
+ setOwnDataProperty(merged, key, mergeDeltaNode(before, after, diskValue ?? {}));
53622
+ } else {
53623
+ setOwnDataProperty(merged, key, after);
53624
+ }
53595
53625
  }
53596
53626
  return merged;
53597
53627
  }
53628
+ function setOwnDataProperty(target, key, value) {
53629
+ Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
53630
+ }
53631
+ function applyNodeDeletion(before, diskNode) {
53632
+ const pruned = { ...diskNode };
53633
+ for (const key of Object.keys(before)) {
53634
+ if (!Object.hasOwn(pruned, key)) continue;
53635
+ const beforeChild = before[key];
53636
+ const diskChild = pruned[key];
53637
+ if (isPlainObject(beforeChild) && isPlainObject(diskChild)) {
53638
+ const nested = applyNodeDeletion(beforeChild, diskChild);
53639
+ if (Object.keys(nested).length === 0) delete pruned[key];
53640
+ else setOwnDataProperty(pruned, key, nested);
53641
+ } else {
53642
+ delete pruned[key];
53643
+ }
53644
+ }
53645
+ return pruned;
53646
+ }
53647
+ function cloneJsonDocument(value) {
53648
+ return JSON.parse(JSON.stringify(value));
53649
+ }
53650
+ function writeJsonAtomic(filePath, value) {
53651
+ fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
53652
+ const tempPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;
53653
+ try {
53654
+ const fd = fs4.openSync(tempPath, "w");
53655
+ try {
53656
+ fs4.writeFileSync(fd, JSON.stringify(value, null, 2));
53657
+ fs4.fsyncSync(fd);
53658
+ } finally {
53659
+ fs4.closeSync(fd);
53660
+ }
53661
+ fs4.renameSync(tempPath, filePath);
53662
+ try {
53663
+ const dirFd = fs4.openSync(path4.dirname(filePath), "r");
53664
+ try {
53665
+ fs4.fsyncSync(dirFd);
53666
+ } finally {
53667
+ fs4.closeSync(dirFd);
53668
+ }
53669
+ } catch {
53670
+ }
53671
+ } catch (error) {
53672
+ try {
53673
+ fs4.unlinkSync(tempPath);
53674
+ } catch {
53675
+ }
53676
+ throw error;
53677
+ }
53678
+ }
53598
53679
  var SettingsManager = class {
53599
53680
  settingsPath;
53600
53681
  legacyConfigPath;
@@ -53612,9 +53693,9 @@ var SettingsManager = class {
53612
53693
  this.settingsPath = getSettingsPath();
53613
53694
  this.legacyConfigPath = `${configDir}/config.json`;
53614
53695
  this.settings = this.loadSettingsFile();
53615
- this.settingsLastWritten = structuredClone(this.settings);
53696
+ this.settingsLastWritten = cloneJsonDocument(this.settings);
53616
53697
  this.legacyConfig = this.loadLegacyConfigFile();
53617
- this.legacyConfigLastWritten = structuredClone(this.legacyConfig);
53698
+ this.legacyConfigLastWritten = cloneJsonDocument(this.legacyConfig);
53618
53699
  }
53619
53700
  readJson(filePath) {
53620
53701
  if (!fs4.existsSync(filePath)) return null;
@@ -53635,9 +53716,9 @@ var SettingsManager = class {
53635
53716
  }
53636
53717
  writeSettings() {
53637
53718
  this.ensureDir();
53638
- const written = this.writeMergedOrFull(() => this.settingsPath, () => this.settingsLastWritten, () => this.settings, (value) => this.writeSettingsValue(value));
53639
- this.settings = structuredClone(written);
53640
- this.settingsLastWritten = structuredClone(written);
53719
+ const { value } = this.mergeOntoDisk(this.settingsPath, this.settingsLastWritten, this.settings);
53720
+ this.settings = cloneJsonDocument(value);
53721
+ this.settingsLastWritten = cloneJsonDocument(value);
53641
53722
  }
53642
53723
  /** Merged write for an explicit candidate state (setters that build a
53643
53724
  * nextSettings object): writes FIRST (a failed write leaves this
@@ -53645,39 +53726,33 @@ var SettingsManager = class {
53645
53726
  * written — callers assign THAT (not their candidate, which lacks keys
53646
53727
  * preserved from disk) so state and lastWritten never diverge. */
53647
53728
  writeSettingsWith(next) {
53648
- const written = this.writeMergedOrFull(() => this.settingsPath, () => this.settingsLastWritten, () => next, (value) => this.writeSettingsValue(value));
53649
- this.settingsLastWritten = structuredClone(written);
53650
- return written;
53729
+ const { value } = this.mergeOntoDisk(this.settingsPath, this.settingsLastWritten, next);
53730
+ this.settingsLastWritten = cloneJsonDocument(value);
53731
+ return value;
53651
53732
  }
53652
- /** Shared write path: merge our delta onto the current on-disk object and
53653
- * return the value actually written. When the file EXISTS but does not
53654
- * parse (another process mid-write over a non-atomic writeFileSync),
53733
+ /** Shared write path: merge our delta onto the current on-disk object
53734
+ * (atomic temp+rename write) and return the value actually written. When
53735
+ * the file EXISTS but does not parse (a foreign non-atomic writer),
53655
53736
  * merging onto {} would silently delete every unrelated key — fall back
53656
53737
  * to the full candidate write instead. */
53657
- writeMergedOrFull(path34, lastWritten, current, write) {
53738
+ mergeOntoDisk(filePath, lastWritten, current) {
53658
53739
  let onDisk = null;
53659
53740
  let corrupt = false;
53660
- if (fs4.existsSync(path34())) {
53741
+ if (fs4.existsSync(filePath)) {
53661
53742
  try {
53662
- onDisk = JSON.parse(fs4.readFileSync(path34(), "utf-8"));
53743
+ onDisk = JSON.parse(fs4.readFileSync(filePath, "utf-8"));
53663
53744
  } catch {
53664
53745
  corrupt = true;
53665
53746
  }
53666
53747
  }
53667
- const value = corrupt ? current() : mergeSnapshotDelta(lastWritten(), current(), onDisk ?? {});
53668
- write(value);
53669
- return value;
53670
- }
53671
- writeSettingsValue(settings) {
53672
- this.ensureDir();
53673
- fs4.writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2));
53748
+ const value = corrupt ? current : mergeSnapshotDelta(lastWritten, current, onDisk ?? {});
53749
+ writeJsonAtomic(filePath, value);
53750
+ return { value };
53674
53751
  }
53675
53752
  writeLegacyConfig() {
53676
53753
  this.ensureDir();
53677
- this.writeMergedOrFull(() => this.legacyConfigPath, () => this.legacyConfigLastWritten, () => this.legacyConfig, (value) => {
53678
- fs4.writeFileSync(this.legacyConfigPath, JSON.stringify(value, null, 2));
53679
- this.legacyConfigLastWritten = structuredClone(value);
53680
- });
53754
+ const { value } = this.mergeOntoDisk(this.legacyConfigPath, this.legacyConfigLastWritten, this.legacyConfig);
53755
+ this.legacyConfigLastWritten = cloneJsonDocument(value);
53681
53756
  }
53682
53757
  getLegacyApiKey() {
53683
53758
  return this.legacyConfig.apiKey;
@@ -53760,11 +53835,9 @@ var SettingsManager = class {
53760
53835
  readProjectSettings(cwd) {
53761
53836
  return this.readJson(getProjectSettingsPath(cwd)) || {};
53762
53837
  }
53763
- writeProjectSettings(cwd, settings) {
53764
- const settingsPath = getProjectSettingsPath(cwd);
53765
- fs4.mkdirSync(path4.dirname(settingsPath), { recursive: true });
53766
- fs4.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
53767
- }
53838
+ /** Per-cwd last-written baselines so project-scope writes use the same
53839
+ * recursive delta merge as user settings instead of whole-file rewrites. */
53840
+ projectLastWritten = /* @__PURE__ */ new Map();
53768
53841
  /** Raw extension enablement map for one scope. */
53769
53842
  getExtensionSettings(scope = "user", cwd = process.cwd()) {
53770
53843
  const settings = scope === "project" ? this.readProjectSettings(cwd) : this.settings;
@@ -53788,9 +53861,12 @@ var SettingsManager = class {
53788
53861
  */
53789
53862
  setExtensionEnabled(identity, enabled, scope = "user", cwd = process.cwd()) {
53790
53863
  if (scope === "project") {
53791
- const projectSettings = this.readProjectSettings(cwd);
53792
- setExtensionEnabledInSettings(projectSettings, identity, enabled);
53793
- this.writeProjectSettings(cwd, projectSettings);
53864
+ const loaded = this.readProjectSettings(cwd);
53865
+ const next = cloneJsonDocument(loaded);
53866
+ setExtensionEnabledInSettings(next, identity, enabled);
53867
+ const settingsPath = getProjectSettingsPath(cwd);
53868
+ const { value } = this.mergeOntoDisk(settingsPath, this.projectLastWritten.get(cwd) ?? loaded, next);
53869
+ this.projectLastWritten.set(cwd, cloneJsonDocument(value));
53794
53870
  return;
53795
53871
  }
53796
53872
  const nextSettings = {
@@ -55234,7 +55310,7 @@ function runCapabilitiesCommand(args) {
55234
55310
  import fs8 from "node:fs";
55235
55311
 
55236
55312
  // src/session-control.ts
55237
- import * as fs7 from "node:fs";
55313
+ import fs7 from "fs";
55238
55314
  import * as path7 from "node:path";
55239
55315
  var SessionControlError = class extends Error {
55240
55316
  constructor(code, message, recoverable = sessionControlErrorRecoverable(code)) {
@@ -55642,11 +55718,12 @@ function recoverOnePublishJournal(params) {
55642
55718
  warn(message);
55643
55719
  return { action: "unreadable-journal", journal: journalName, message };
55644
55720
  }
55645
- const dirExists = (id) => {
55721
+ const entryKind = (id) => {
55646
55722
  try {
55647
- return fs7.statSync(path7.join(params.sessionsDir, id)).isDirectory();
55648
- } catch {
55649
- return false;
55723
+ return fs7.lstatSync(path7.join(params.sessionsDir, id)).isDirectory() ? "directory" : "aliased";
55724
+ } catch (error) {
55725
+ const code = error?.code;
55726
+ return code === "ENOENT" ? "absent" : "unknown";
55650
55727
  }
55651
55728
  };
55652
55729
  const removeSessionDir = (id) => {
@@ -55655,9 +55732,23 @@ function recoverOnePublishJournal(params) {
55655
55732
  const removeJournal = () => {
55656
55733
  fs7.rmSync(params.journalPath, { force: true });
55657
55734
  };
55658
- const targetExists = dirExists(record.targetId);
55659
- const backupExists = record.backupDir ? dirExists(record.backupDir) : false;
55660
- const stagingExists = dirExists(record.stagingId);
55735
+ const targetKind = entryKind(record.targetId);
55736
+ const backupKind = record.backupDir ? entryKind(record.backupDir) : "absent";
55737
+ const stagingKind = entryKind(record.stagingId);
55738
+ const targetExists = targetKind === "directory";
55739
+ const backupExists = backupKind === "directory";
55740
+ const stagingExists = stagingKind === "directory";
55741
+ const ambiguous = (kind) => kind === "aliased" || kind === "unknown" ? kind : null;
55742
+ const ambiguousKinds = [
55743
+ ambiguous(targetKind) ? `target(${ambiguous(targetKind)})` : null,
55744
+ ambiguous(backupKind) ? `backup(${ambiguous(backupKind)})` : null,
55745
+ ambiguous(stagingKind) ? `staging(${ambiguous(stagingKind)})` : null
55746
+ ].filter(Boolean).join("+");
55747
+ if (ambiguousKinds !== "") {
55748
+ const message = `[session-control] Interrupted publish journal ${journalName} references an ambiguous ${ambiguousKinds} entry (target=${record.targetId}, backup=${record.backupDir ?? "none"}, staging=${record.stagingId}); nothing was deleted \u2014 inspect ${params.sessionsDir} manually`;
55749
+ warn(message);
55750
+ return { action: "ambiguous-preserved", targetId: record.targetId, message };
55751
+ }
55661
55752
  try {
55662
55753
  if (targetExists && !stagingExists && record.failedAt === void 0) {
55663
55754
  if (backupExists && record.backupDir) removeSessionDir(record.backupDir);
@@ -57748,14 +57839,21 @@ var Box = class {
57748
57839
  this.invalidateCache();
57749
57840
  }
57750
57841
  setBgFn(bgFn) {
57842
+ if (this.bgFn === bgFn) return;
57751
57843
  this.bgFn = bgFn;
57844
+ this.invalidateCache();
57752
57845
  }
57753
57846
  invalidateCache() {
57754
57847
  this.cache = void 0;
57755
57848
  }
57756
- matchCache(width, childLines, bgSample) {
57849
+ matchCache(width, childOutputs, bgSample) {
57757
57850
  const cache = this.cache;
57758
- return !!cache && cache.width === width && cache.bgSample === bgSample && cache.childLines.length === childLines.length && cache.childLines.every((line, i) => line === childLines[i]);
57851
+ if (!cache || cache.width !== width || cache.bgSample !== bgSample) return false;
57852
+ if (cache.childOutputs.length !== childOutputs.length) return false;
57853
+ for (let i = 0; i < childOutputs.length; i++) {
57854
+ if (cache.childOutputs[i] !== childOutputs[i]) return false;
57855
+ }
57856
+ return true;
57759
57857
  }
57760
57858
  invalidate() {
57761
57859
  this.invalidateCache();
@@ -57765,22 +57863,29 @@ var Box = class {
57765
57863
  }
57766
57864
  render(width) {
57767
57865
  if (this.children.length === 0) {
57768
- return [];
57866
+ if (!this.cache) this.cache = { childOutputs: [], width, bgSample: void 0, lines: [] };
57867
+ return this.cache.lines;
57769
57868
  }
57770
57869
  const contentWidth = Math.max(1, width - this.paddingX * 2);
57771
57870
  const leftPad = " ".repeat(this.paddingX);
57772
- const childLines = [];
57871
+ const childOutputs = [];
57773
57872
  for (const child of this.children) {
57774
- const lines = child.render(contentWidth);
57775
- for (const line of lines) {
57873
+ childOutputs.push(child.render(contentWidth));
57874
+ }
57875
+ const bgSample = this.bgFn ? this.bgFn("test") : void 0;
57876
+ if (this.matchCache(width, childOutputs, bgSample)) {
57877
+ return this.cache.lines;
57878
+ }
57879
+ const childLines = [];
57880
+ let anyContent = false;
57881
+ for (const output of childOutputs) {
57882
+ for (const line of output) {
57776
57883
  childLines.push(leftPad + line);
57884
+ anyContent = true;
57777
57885
  }
57778
57886
  }
57779
- if (childLines.length === 0) {
57780
- return [];
57781
- }
57782
- const bgSample = this.bgFn ? this.bgFn("test") : void 0;
57783
- if (this.matchCache(width, childLines, bgSample)) {
57887
+ if (!anyContent) {
57888
+ this.cache = { childOutputs, width, bgSample, lines: [] };
57784
57889
  return this.cache.lines;
57785
57890
  }
57786
57891
  const result = [];
@@ -57793,7 +57898,7 @@ var Box = class {
57793
57898
  for (let i = 0; i < this.paddingY; i++) {
57794
57899
  result.push(this.applyBg("", width));
57795
57900
  }
57796
- this.cache = { childLines, width, bgSample, lines: result };
57901
+ this.cache = { childOutputs, width, bgSample, lines: result };
57797
57902
  return result;
57798
57903
  }
57799
57904
  applyBg(line, width) {
@@ -58544,30 +58649,66 @@ function isImageLine(line) {
58544
58649
  // ../../packages/tui/src/component-tree.ts
58545
58650
  var Container = class {
58546
58651
  children = [];
58652
+ memoWidth = -1;
58653
+ memoChildOutputs = [];
58654
+ memoLines = null;
58547
58655
  addChild(component) {
58548
58656
  this.children.push(component);
58657
+ this.dropMemo();
58549
58658
  }
58550
58659
  removeChild(component) {
58551
58660
  const index = this.children.indexOf(component);
58552
58661
  if (index !== -1) {
58553
58662
  this.children.splice(index, 1);
58663
+ this.dropMemo();
58554
58664
  }
58555
58665
  }
58556
58666
  clear() {
58557
58667
  this.children = [];
58668
+ this.dropMemo();
58558
58669
  }
58559
58670
  invalidate() {
58671
+ this.dropMemo();
58560
58672
  for (const child of this.children) {
58561
58673
  child.invalidate?.();
58562
58674
  }
58563
58675
  }
58564
58676
  render(width) {
58677
+ const childCount = this.children.length;
58678
+ if (this.memoLines !== null && this.memoWidth === width && this.memoChildOutputs.length === childCount) {
58679
+ const outputs2 = new Array(childCount);
58680
+ let unchanged = true;
58681
+ for (let i = 0; i < childCount; i++) {
58682
+ const childLines = this.children[i].render(width);
58683
+ outputs2[i] = childLines;
58684
+ if (childLines !== this.memoChildOutputs[i]) unchanged = false;
58685
+ }
58686
+ if (unchanged) return this.memoLines;
58687
+ return this.rebuildMemo(width, outputs2);
58688
+ }
58689
+ const outputs = new Array(childCount);
58690
+ for (let i = 0; i < childCount; i++) {
58691
+ outputs[i] = this.children[i].render(width);
58692
+ }
58693
+ return this.rebuildMemo(width, outputs);
58694
+ }
58695
+ rebuildMemo(width, outputs) {
58565
58696
  const lines = [];
58566
- for (const child of this.children) {
58567
- lines.push(...child.render(width));
58697
+ for (const childLines of outputs) {
58698
+ for (let j2 = 0; j2 < childLines.length; j2++) {
58699
+ lines.push(childLines[j2]);
58700
+ }
58568
58701
  }
58702
+ this.memoWidth = width;
58703
+ this.memoChildOutputs = outputs;
58704
+ this.memoLines = lines;
58569
58705
  return lines;
58570
58706
  }
58707
+ dropMemo() {
58708
+ this.memoWidth = -1;
58709
+ this.memoChildOutputs = [];
58710
+ this.memoLines = null;
58711
+ }
58571
58712
  };
58572
58713
 
58573
58714
  // ../../packages/tui/src/tui-core.ts
@@ -58873,26 +59014,17 @@ function extractCursorPosition(lines, height) {
58873
59014
  const viewportTop = Math.max(0, lines.length - height);
58874
59015
  for (let row = lines.length - 1; row >= viewportTop; row--) {
58875
59016
  const line = lines[row];
59017
+ if (line === void 0) continue;
58876
59018
  const markerIndex = line.indexOf(CURSOR_MARKER);
58877
59019
  if (markerIndex !== -1) {
58878
59020
  const beforeMarker = line.slice(0, markerIndex);
58879
59021
  const col = visibleWidth(beforeMarker);
58880
- lines[row] = line.slice(0, markerIndex) + line.slice(markerIndex + CURSOR_MARKER.length);
58881
- return { row, col };
59022
+ const strippedLine = beforeMarker + line.slice(markerIndex + CURSOR_MARKER.length);
59023
+ return { row, col, strippedLine };
58882
59024
  }
58883
59025
  }
58884
59026
  return null;
58885
59027
  }
58886
- function applyLineResets(lines) {
58887
- const reset = "\x1B[0m\x1B]8;;\x07";
58888
- for (let i = 0; i < lines.length; i++) {
58889
- const line = lines[i];
58890
- if (!isImageLine(line)) {
58891
- lines[i] = line + reset;
58892
- }
58893
- }
58894
- return lines;
58895
- }
58896
59028
  function findChangedRange(previousLines, newLines) {
58897
59029
  let firstChanged = -1;
58898
59030
  let lastChanged = -1;
@@ -58971,6 +59103,10 @@ var TerminalProtocol = class {
58971
59103
  };
58972
59104
 
58973
59105
  // ../../packages/tui/src/tui.ts
59106
+ var LINE_RESET_SUFFIX = "\x1B[0m\x1B]8;;\x07";
59107
+ function lineForWrite(line) {
59108
+ return isImageLine(line) ? line : line + LINE_RESET_SUFFIX;
59109
+ }
58974
59110
  var TUI = class extends Container {
58975
59111
  terminal;
58976
59112
  previousLines = [];
@@ -59146,7 +59282,10 @@ var TUI = class extends Container {
59146
59282
  newLines = this.overlayManager.composite(newLines, width, height, this.maxLinesRendered);
59147
59283
  }
59148
59284
  const cursorPos = extractCursorPosition(newLines, height);
59149
- newLines = applyLineResets(newLines);
59285
+ if (cursorPos) {
59286
+ newLines = newLines.slice();
59287
+ newLines[cursorPos.row] = cursorPos.strippedLine;
59288
+ }
59150
59289
  const widthChanged = this.previousWidth !== 0 && this.previousWidth !== width;
59151
59290
  const fullRender = (clear) => {
59152
59291
  this.fullRedrawCount += 1;
@@ -59154,7 +59293,7 @@ var TUI = class extends Container {
59154
59293
  if (clear) buffer2 += "\x1B[3J\x1B[2J\x1B[H";
59155
59294
  for (let i = 0; i < newLines.length; i++) {
59156
59295
  if (i > 0) buffer2 += "\r\n";
59157
- buffer2 += newLines[i];
59296
+ buffer2 += lineForWrite(newLines[i]);
59158
59297
  }
59159
59298
  buffer2 += "\x1B[?2026l";
59160
59299
  this.terminal.write(buffer2);
@@ -59293,7 +59432,7 @@ var TUI = class extends Container {
59293
59432
  ].join("\n");
59294
59433
  throw new Error(errorMsg);
59295
59434
  }
59296
- buffer += line;
59435
+ buffer += lineForWrite(line);
59297
59436
  }
59298
59437
  let finalCursorRow = renderEnd;
59299
59438
  if (this.previousLines.length > newLines.length) {
@@ -62624,6 +62763,11 @@ var Xt = b.parse;
62624
62763
  var Jt = x.lex;
62625
62764
 
62626
62765
  // ../../packages/tui/src/components/markdown.ts
62766
+ function appendLines(target, source) {
62767
+ for (let i = 0; i < source.length; i++) {
62768
+ target.push(source[i]);
62769
+ }
62770
+ }
62627
62771
  var Markdown = class {
62628
62772
  text;
62629
62773
  paddingX;
@@ -62672,7 +62816,7 @@ var Markdown = class {
62672
62816
  const token = tokens[i];
62673
62817
  const nextToken = tokens[i + 1];
62674
62818
  const tokenLines = this.renderToken(token, contentWidth, nextToken?.type);
62675
- renderedLines.push(...tokenLines);
62819
+ appendLines(renderedLines, tokenLines);
62676
62820
  }
62677
62821
  const wrappedLines = [];
62678
62822
  for (const line of renderedLines) {
@@ -62836,12 +62980,12 @@ var Markdown = class {
62836
62980
  }
62837
62981
  case "list": {
62838
62982
  const listLines = this.renderList(token, 0);
62839
- lines.push(...listLines);
62983
+ appendLines(lines, listLines);
62840
62984
  break;
62841
62985
  }
62842
62986
  case "table": {
62843
62987
  const tableLines = this.renderTable(token, width);
62844
- lines.push(...tableLines);
62988
+ appendLines(lines, tableLines);
62845
62989
  break;
62846
62990
  }
62847
62991
  case "blockquote": {
@@ -62992,7 +63136,7 @@ var Markdown = class {
62992
63136
  for (const token of tokens) {
62993
63137
  if (token.type === "list") {
62994
63138
  const nestedLines = this.renderList(token, parentDepth + 1);
62995
- lines.push(...nestedLines);
63139
+ appendLines(lines, nestedLines);
62996
63140
  } else if (token.type === "text") {
62997
63141
  const text = token.tokens && token.tokens.length > 0 ? this.renderInlineTokens(token.tokens) : token.text || "";
62998
63142
  lines.push(text);
@@ -63367,19 +63511,25 @@ var SettingsList = class {
63367
63511
  // ../../packages/tui/src/components/spacer.ts
63368
63512
  var Spacer = class {
63369
63513
  lines;
63514
+ cache = [];
63370
63515
  constructor(lines = 1) {
63371
63516
  this.lines = lines;
63372
63517
  }
63373
63518
  setLines(lines) {
63519
+ if (lines === this.lines) return;
63374
63520
  this.lines = lines;
63521
+ this.cache = [];
63375
63522
  }
63376
63523
  invalidate() {
63524
+ this.cache = [];
63377
63525
  }
63378
63526
  render(_width) {
63527
+ if (this.cache.length === this.lines) return this.cache;
63379
63528
  const result = [];
63380
63529
  for (let i = 0; i < this.lines; i++) {
63381
63530
  result.push("");
63382
63531
  }
63532
+ this.cache = result;
63383
63533
  return result;
63384
63534
  }
63385
63535
  };
@@ -64639,7 +64789,7 @@ function restoreInteractiveSessionState(options) {
64639
64789
  }
64640
64790
 
64641
64791
  // src/keybindings.ts
64642
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
64792
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
64643
64793
  import { join as join8 } from "path";
64644
64794
  var DEFAULT_APP_KEYBINDINGS = {
64645
64795
  interrupt: "escape",
@@ -64723,9 +64873,9 @@ var KeybindingsManager = class _KeybindingsManager {
64723
64873
  return new _KeybindingsManager(config);
64724
64874
  }
64725
64875
  static loadFromFile(path34) {
64726
- if (!existsSync9(path34)) return {};
64876
+ if (!existsSync8(path34)) return {};
64727
64877
  try {
64728
- return JSON.parse(readFileSync8(path34, "utf-8"));
64878
+ return JSON.parse(readFileSync7(path34, "utf-8"));
64729
64879
  } catch {
64730
64880
  return {};
64731
64881
  }
@@ -65915,6 +66065,9 @@ function setTheme(name, enableWatcher = false) {
65915
66065
  function getCurrentThemeName() {
65916
66066
  return currentThemeName ?? getDefaultTheme();
65917
66067
  }
66068
+ function onThemeChange(callback) {
66069
+ onThemeChangeCallback = callback;
66070
+ }
65918
66071
  function startThemeWatcher() {
65919
66072
  if (themeWatcher) {
65920
66073
  themeWatcher.close();
@@ -71838,9 +71991,14 @@ ${theme.fg("error", "Error: " + errorOutput)}`;
71838
71991
  };
71839
71992
 
71840
71993
  // src/runtime/footer-lifecycle.ts
71994
+ var FOOTER_LABEL_MAX_WIDTH = 60;
71995
+ var MIN_PROGRESS_WIDTH = 8;
71841
71996
  function messageRole(event) {
71842
71997
  return String(event.message.role);
71843
71998
  }
71999
+ function isCuratedProgressDetails(details) {
72000
+ return typeof details === "object" && details !== null && "increment" in details;
72001
+ }
71844
72002
  function formatFooterLifecycleEvent(event) {
71845
72003
  switch (event.type) {
71846
72004
  case "message_start":
@@ -71852,8 +72010,15 @@ function formatFooterLifecycleEvent(event) {
71852
72010
  case "tool_execution_start":
71853
72011
  return `\u25B6 ${event.toolName}`;
71854
72012
  case "tool_execution_update": {
71855
- const progress = latestProgressText(event.partialResult?.content);
71856
- return progress ? clipProgressText(progress) : `\u21BB ${event.toolName}`;
72013
+ const status = `\u21BB ${event.toolName}`;
72014
+ const progress = isCuratedProgressDetails(event.partialResult?.details) ? latestProgressText(event.partialResult?.content) : void 0;
72015
+ if (!progress) return clipProgressText(status, FOOTER_LABEL_MAX_WIDTH);
72016
+ const prefixWidth = visibleWidth(`${status} \xB7 `);
72017
+ const progressBudget = FOOTER_LABEL_MAX_WIDTH - prefixWidth;
72018
+ if (progressBudget < MIN_PROGRESS_WIDTH) {
72019
+ return clipProgressText(status, FOOTER_LABEL_MAX_WIDTH);
72020
+ }
72021
+ return `${status} \xB7 ${clipProgressText(progress, progressBudget)}`;
71857
72022
  }
71858
72023
  case "tool_execution_end":
71859
72024
  return event.isError ? `\u2715 ${event.toolName}` : `\u2713 ${event.toolName}`;
@@ -71862,63 +72027,76 @@ function formatFooterLifecycleEvent(event) {
71862
72027
  }
71863
72028
  }
71864
72029
 
71865
- // src/runtime/terminal-bell.ts
71866
- var BEL = "\x07";
71867
- var DEFAULT_MIN_INTERVAL_MS = 500;
71868
- var lastRingAtMs = 0;
71869
- function ringTerminalBell(options = {}) {
71870
- const {
71871
- enabled = true,
71872
- stdout = process.stdout,
71873
- now = Date.now,
71874
- minIntervalMs = DEFAULT_MIN_INTERVAL_MS
71875
- } = options;
71876
- if (!enabled) return false;
71877
- if (!stdout.isTTY) return false;
71878
- const atMs = now();
71879
- if (atMs - lastRingAtMs < minIntervalMs) return false;
71880
- lastRingAtMs = atMs;
71881
- stdout.write(BEL);
71882
- return true;
71883
- }
71884
- function ringTurnSettledBell(host) {
71885
- if ((host.agentSessionController?.pendingMessageCount ?? 0) > 0) return;
71886
- if (host.executionState.read().awaitingContinuation) return;
71887
- if (host.peekTodoAutoContinuePrompt?.()) return;
71888
- host.ringAttentionBell?.("turn-settled");
71889
- }
71890
- function attentionMessage(reason) {
71891
- switch (reason) {
71892
- case "interaction":
71893
- return { title: "Pie", body: "Waiting for your input" };
71894
- case "turn-settled":
71895
- return { title: "Pie", body: "Response ready" };
71896
- case "turn-failed":
71897
- return { title: "Pie", body: "Turn failed" };
71898
- case "stall-recovery":
71899
- return { title: "Pie", body: "Recovered from a stalled turn" };
72030
+ // src/runtime/cli-attention.ts
72031
+ var CliAttentionAdapter = class {
72032
+ constructor(host) {
72033
+ this.host = host;
72034
+ this.coordinator.handle({ type: "session_work_started", ...this.identity });
72035
+ this.coordinator.subscribe((event) => {
72036
+ if (event.type !== "attention_requested") return;
72037
+ host.updateTerminalTitle?.();
72038
+ host.ringAttentionBell?.(event.reason === "failed" ? "turn-failed" : event.reason === "blocked" ? "stall-recovery" : "interaction");
72039
+ });
71900
72040
  }
71901
- }
71902
- var DEFAULT_NOTIFICATION_MIN_INTERVAL_MS = 1e3;
71903
- var lastNotificationAtMs = 0;
71904
- function sendDesktopNotification(reason, options = {}) {
71905
- const {
71906
- enabled = true,
71907
- terminal,
71908
- now = Date.now,
71909
- minIntervalMs = DEFAULT_NOTIFICATION_MIN_INTERVAL_MS
71910
- } = options;
71911
- if (!enabled) return false;
71912
- if (!terminal || typeof terminal.notify !== "function") return false;
71913
- const atMs = now();
71914
- if (atMs - lastNotificationAtMs < minIntervalMs) return false;
71915
- lastNotificationAtMs = atMs;
71916
- const { title, body } = attentionMessage(reason);
71917
- terminal.notify(title, body);
71918
- return true;
72041
+ host;
72042
+ coordinator = new AttentionCoordinator({ completion: "off" });
72043
+ identity = { sessionId: void 0, generation: 0 };
72044
+ hasWorkEvent = false;
72045
+ handle(event) {
72046
+ this.hasWorkEvent = true;
72047
+ this.identity = { sessionId: event.sessionId, generation: event.generation };
72048
+ if (event.type === "session_invalidated" && event.reason === "dispose") this.coordinator.dispose();
72049
+ else this.coordinator.handle(event);
72050
+ }
72051
+ request(reason, id) {
72052
+ const identity = this.host.agentSessionController?.workIdentity ?? this.identity;
72053
+ if (!this.hasWorkEvent) {
72054
+ this.handle({ type: "session_work_started", ...identity });
72055
+ }
72056
+ return this.coordinator.request(identity, reason, id);
72057
+ }
72058
+ resolve(request, resolution) {
72059
+ if (request) this.coordinator.resolve(request.id, request, resolution);
72060
+ }
72061
+ };
72062
+ var adapters = /* @__PURE__ */ new WeakMap();
72063
+ function cliAttention(host) {
72064
+ let adapter = adapters.get(host);
72065
+ if (!adapter) {
72066
+ adapter = new CliAttentionAdapter(host);
72067
+ adapters.set(host, adapter);
72068
+ }
72069
+ return adapter;
71919
72070
  }
71920
72071
 
71921
72072
  // src/runtime/interactive-runtime-events.ts
72073
+ var STREAM_RENDER_MIN_INTERVAL_MS = 80;
72074
+ var lastStreamRenderAt = 0;
72075
+ var trailingStreamRenderTimer = null;
72076
+ function requestStreamingRender(host) {
72077
+ const now = Date.now();
72078
+ if (now - lastStreamRenderAt >= STREAM_RENDER_MIN_INTERVAL_MS) {
72079
+ lastStreamRenderAt = now;
72080
+ host.safeRequestRender();
72081
+ return;
72082
+ }
72083
+ if (trailingStreamRenderTimer === null) {
72084
+ trailingStreamRenderTimer = setTimeout(() => {
72085
+ trailingStreamRenderTimer = null;
72086
+ lastStreamRenderAt = Date.now();
72087
+ host.safeRequestRender();
72088
+ }, STREAM_RENDER_MIN_INTERVAL_MS - (now - lastStreamRenderAt));
72089
+ }
72090
+ }
72091
+ var pendingAttentionRings = /* @__PURE__ */ new WeakMap();
72092
+ function queueFailedRing(host) {
72093
+ pendingAttentionRings.set(host, "failed");
72094
+ }
72095
+ function takeAttentionRing(host) {
72096
+ const attention = pendingAttentionRings.get(host);
72097
+ pendingAttentionRings.delete(host);
72098
+ return attention;
72099
+ }
71922
72100
  function bindInteractiveRuntimeEvents(host) {
71923
72101
  host.agent.subscribe((event) => {
71924
72102
  switch (event.type) {
@@ -72022,8 +72200,15 @@ function bindInteractiveRuntimeEvents(host) {
72022
72200
  host.streamingComponent = void 0;
72023
72201
  removeCalmToolLines(host.chatContainer, host.pendingTools);
72024
72202
  host.pendingTools.clear();
72025
- host.emitExtensionEvent({ type: "agent:end", messages: host.agent.state.messages });
72203
+ if (!hasControllerTurnAuthority(host)) {
72204
+ host.emitExtensionEvent({ type: "agent:end", messages: host.agent.state.messages });
72205
+ }
72026
72206
  host.footer.setState({ status: "ready" });
72207
+ host.updateTerminalTitle?.();
72208
+ const attention = takeAttentionRing(host);
72209
+ if (attention === "failed" && !hasControllerTurnAuthority(host)) {
72210
+ cliAttention(host).request("failed");
72211
+ }
72027
72212
  host.safeRequestRender();
72028
72213
  break;
72029
72214
  }
@@ -72090,7 +72275,6 @@ function handleSemanticAgentEvent(host, event) {
72090
72275
  host.appendWarningSummary(outcome.terminalSummary);
72091
72276
  }
72092
72277
  host.sessionTrace.completeTurn(event.message, false);
72093
- ringTurnSettledBell(host);
72094
72278
  host._semanticTurnIndex++;
72095
72279
  break;
72096
72280
  }
@@ -72102,6 +72286,7 @@ function handleSemanticAgentEvent(host, event) {
72102
72286
  });
72103
72287
  break;
72104
72288
  }
72289
+ queueFailedRing(host);
72105
72290
  host.clearContinuationTimeout("turn_failed");
72106
72291
  host.emitExtensionEvent({
72107
72292
  type: "turn:failed",
@@ -72125,7 +72310,6 @@ function handleSemanticAgentEvent(host, event) {
72125
72310
  host.appendWarningSummary(outcome.terminalSummary);
72126
72311
  }
72127
72312
  host.sessionTrace.completeTurn(event.message, true);
72128
- host.ringAttentionBell?.("turn-failed");
72129
72313
  host._semanticTurnIndex++;
72130
72314
  break;
72131
72315
  }
@@ -72138,6 +72322,7 @@ function handleSemanticAgentEvent(host, event) {
72138
72322
  host.clearContinuationTimeout("status_idle");
72139
72323
  const outcome = host.executionState.markAwaitingContinuationStalled();
72140
72324
  if (outcome.changed) {
72325
+ cliAttention(host).request("blocked", "continuation-stalled");
72141
72326
  host.logDebug("execution:continuation_stalled", {
72142
72327
  turnIndex: host._semanticTurnIndex,
72143
72328
  currentStepId: host.executionState.read().currentStepId,
@@ -72257,7 +72442,7 @@ function handleMessageUpdate(host, event) {
72257
72442
  role: "assistant",
72258
72443
  content
72259
72444
  });
72260
- host.safeRequestRender();
72445
+ requestStreamingRender(host);
72261
72446
  }
72262
72447
  function findToolRenderHint(host, toolName) {
72263
72448
  const tools = host.agent?.state?.tools ?? [];
@@ -77859,7 +78044,8 @@ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
77859
78044
  }
77860
78045
 
77861
78046
  // src/runtime/retry-policy.ts
77862
- var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|(?:read\s+|write\s+)?EPIPE|socket hang up|request timeout|client network socket disconnected before secure TLS connection was established|certificate (?:has )?expired|unable to verify (?:the first certificate|certificate)|self[- ]signed cert|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i;
78047
+ var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|(?:read\s+|write\s+)?EPIPE|socket hang up|request timeout|client network socket disconnected before secure TLS connection was established|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i;
78048
+ var TLS_VALIDATION_ERROR_PATTERN = /certificate (?:has )?expired|certificate is not yet valid|unable to verify (?:the first certificate|certificate)|unable to get (?:local )?issuer certificate|self[-_ ]signed cert(?:ificate)?|CERT_HAS_EXPIRED|SELF_SIGNED_CERT|does not match certificate's altnames|ERR_TLS_CERT_ALTNAME_INVALID/i;
77863
78049
  var PROVIDER_TRANSIENT_ERROR_PATTERN = /overloaded|rate.?limit|too many requests|\b429\b|\b500\b|\b502\b|\b503\b|\b504\b|service.?unavailable|server error|internal error|retry delay|quota (?:has been |is )?exhausted/i;
77864
78050
  var USER_ABORT_PATTERN = /request was aborted|local abort signal|user abort|user_interrupt|interrupted by user|esc pressed|ctrl\+c/i;
77865
78051
  var AUTH_CONFIG_PATTERN = /missing api key|invalid api key|api key.*invalid|unauthorized|\b401\b|\b403\b|forbidden|authentication failed|no configured model|no models? are configured/i;
@@ -77869,6 +78055,7 @@ var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schem
77869
78055
  var CLI_RETRY_MAX_RETRIES = 3;
77870
78056
  var CLI_RETRY_BASE_DELAY_MS = 2e3;
77871
78057
  var CLI_TRANSIENT_NETWORK_MAX_RETRIES = 10;
78058
+ var CLI_RETRY_TOTAL_MAX_RETRIES = 12;
77872
78059
  var CLI_RETRY_MAX_DELAY_MS = 6e4;
77873
78060
  function isCliContextOverflowError(errorMessage4) {
77874
78061
  return /context length exceeded|context_length_exceeded|maximum context length|maximum context size|too many tokens|input.*too long|prompt.*too long|context window|token limit exceeded|exceeds.{0,20}context/i.test(
@@ -77906,6 +78093,12 @@ function classifyCliFailure(context) {
77906
78093
  if (AUTH_CONFIG_PATTERN.test(errorMessage4) || QUOTA_EXHAUSTED_PATTERN.test(errorMessage4)) {
77907
78094
  return createClassification("auth_config", errorMessage4);
77908
78095
  }
78096
+ if (TLS_VALIDATION_ERROR_PATTERN.test(errorMessage4)) {
78097
+ return createClassification("tls_config", errorMessage4, {
78098
+ retryable: true,
78099
+ terminal: false
78100
+ });
78101
+ }
77909
78102
  if (TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage4)) {
77910
78103
  return createClassification("transient_network", errorMessage4, {
77911
78104
  retryable: true,
@@ -77962,13 +78155,22 @@ function getCliRetryMaxDelayMs() {
77962
78155
  const parsed = Number(override);
77963
78156
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_MAX_DELAY_MS;
77964
78157
  }
78158
+ function getCliRetryTotalMaxRetries() {
78159
+ const override = process.env.PIE_RETRY_TOTAL_MAX_RETRIES;
78160
+ if (!override) {
78161
+ return CLI_RETRY_TOTAL_MAX_RETRIES;
78162
+ }
78163
+ const parsed = Number(override);
78164
+ return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_RETRY_TOTAL_MAX_RETRIES;
78165
+ }
77965
78166
  function createCliRetryPolicy(isRetryableErrorOverride) {
77966
78167
  return {
77967
78168
  isRetryableError: isRetryableErrorOverride ?? isCliRetryableError,
77968
78169
  maxRetries: CLI_RETRY_MAX_RETRIES,
77969
78170
  baseDelayMs: getCliRetryBaseDelayMs(),
77970
78171
  maxDelayMs: getCliRetryMaxDelayMs(),
77971
- maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : CLI_RETRY_MAX_RETRIES
78172
+ maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : CLI_RETRY_MAX_RETRIES,
78173
+ maxTotalRetries: getCliRetryTotalMaxRetries()
77972
78174
  };
77973
78175
  }
77974
78176
 
@@ -78028,6 +78230,10 @@ function setupUI(host) {
78028
78230
  thinkingLevel: host.thinkingLevel,
78029
78231
  contextWindow: host.options.model?.contextWindow ?? 0
78030
78232
  });
78233
+ onThemeChange(() => {
78234
+ host.ui.invalidate();
78235
+ host.ui.requestRender();
78236
+ });
78031
78237
  host.editorContainer = new Container();
78032
78238
  host.todoWidgetContainer = new Container();
78033
78239
  host.todoWidget = new TodoWidgetComponent(host.executionState);
@@ -78561,7 +78767,7 @@ async function recoverHardStalledTurn(host, stalledMs) {
78561
78767
  void host.refreshTodoWidget();
78562
78768
  host.footer.setState({ status: "ready" });
78563
78769
  host.safeRequestRender();
78564
- host.ringAttentionBell?.("stall-recovery");
78770
+ cliAttention(host).request("blocked", "hard-stall-recovery");
78565
78771
  appendWarningSummary(host, "Recovered from a stalled turn (no progress for " + Math.round(stalledMs / 1e3) + "s). The previous turn was interrupted; review its partial output before continuing.");
78566
78772
  if (abortOutcome.terminalSummary) {
78567
78773
  appendWarningSummary(host, abortOutcome.terminalSummary);
@@ -78615,6 +78821,7 @@ function scheduleContinuationTimeout(host) {
78615
78821
  terminalStatus: outcome.terminalStatus
78616
78822
  });
78617
78823
  if (outcome.changed) {
78824
+ cliAttention(host).request("blocked", "continuation-stalled");
78618
78825
  host.persistExecutionState();
78619
78826
  void host.refreshTodoWidget();
78620
78827
  if (outcome.terminalSummary) {
@@ -78661,7 +78868,7 @@ function createInteractiveAgentSessionController(host) {
78661
78868
  }
78662
78869
  let retryElapsedTicker;
78663
78870
  let retryElapsedStartedAt = 0;
78664
- let pendingSettleKind;
78871
+ const attention = cliAttention(host);
78665
78872
  const stopRetryElapsedTicker = () => {
78666
78873
  if (retryElapsedTicker !== void 0) {
78667
78874
  clearInterval(retryElapsedTicker);
@@ -78674,6 +78881,9 @@ function createInteractiveAgentSessionController(host) {
78674
78881
  cwd: host.options.cwd,
78675
78882
  baseSystemPrompt: host.agent.state.systemPrompt,
78676
78883
  prepareTurn: (context) => prepareAgentTurn(host, context),
78884
+ onExecutionEnd: async ({ messages }) => {
78885
+ await host.emitExtensionEvent({ type: "agent:end", messages });
78886
+ },
78677
78887
  canDispatchQueuedTurn: () => {
78678
78888
  if (host.isWaitingForUserInteraction()) {
78679
78889
  return { allowed: false, reason: "waiting_for_user_interaction", retryAfterMs: 100 };
@@ -78734,7 +78944,10 @@ function createInteractiveAgentSessionController(host) {
78734
78944
  onEvent: (event) => {
78735
78945
  stopRetryElapsedTicker();
78736
78946
  host.sessionTrace.noteRuntimeEvent(event.type, event);
78737
- if (event.type === "session_synced") {
78947
+ if (event.type === "session_work_started" || event.type === "session_invalidated" || event.type === "session_quiescent") {
78948
+ if (event.type === "session_quiescent") host.setControllerBusy(false);
78949
+ attention.handle(event);
78950
+ } else if (event.type === "session_synced") {
78738
78951
  host.lastSyncedMessageCount = event.totalMessages;
78739
78952
  } else if (event.type === "queue_changed") {
78740
78953
  host.updatePendingMessagesDisplay();
@@ -78805,14 +79018,9 @@ function createInteractiveAgentSessionController(host) {
78805
79018
  host.appendWarningSummary(outcome.terminalSummary);
78806
79019
  }
78807
79020
  host.sessionTrace.completeTurn(event.message, false);
78808
- pendingSettleKind = "completed";
78809
79021
  host._semanticTurnIndex++;
78810
79022
  } else if (event.type === "turn_settled") {
78811
79023
  host.setControllerBusy(false);
78812
- if (pendingSettleKind === "completed") {
78813
- ringTurnSettledBell(host);
78814
- }
78815
- pendingSettleKind = void 0;
78816
79024
  } else if (event.type === "turn_failed_terminal") {
78817
79025
  host.clearContinuationTimeout("turn_failed_terminal");
78818
79026
  if (event.message) {
@@ -78847,8 +79055,6 @@ function createInteractiveAgentSessionController(host) {
78847
79055
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
78848
79056
  }
78849
79057
  host.showError(event.failure.message);
78850
- host.ringAttentionBell?.("turn-failed");
78851
- pendingSettleKind = "failed";
78852
79058
  host.setControllerBusy(false);
78853
79059
  } else if (event.type === "turn_cancelled") {
78854
79060
  const outcome = host.executionState.abort();
@@ -78865,7 +79071,6 @@ function createInteractiveAgentSessionController(host) {
78865
79071
  if (outcome.terminalSummary) {
78866
79072
  host.appendWarningSummary(outcome.terminalSummary);
78867
79073
  }
78868
- pendingSettleKind = "cancelled";
78869
79074
  host.setControllerBusy(false);
78870
79075
  } else if (event.type === "runtime_guard_advisory") {
78871
79076
  host.noteRuntimeGuardAdvisory(event);
@@ -79124,6 +79329,56 @@ function selectToolsForRuntimeMode(tools, mode) {
79124
79329
  return selectToolsForRuntimePolicy(tools, mode);
79125
79330
  }
79126
79331
 
79332
+ // src/runtime/terminal-bell.ts
79333
+ var BEL = "\x07";
79334
+ var DEFAULT_MIN_INTERVAL_MS = 500;
79335
+ var lastRingAtMs = 0;
79336
+ function ringTerminalBell(options = {}) {
79337
+ const {
79338
+ enabled = true,
79339
+ stdout = process.stdout,
79340
+ now = Date.now,
79341
+ minIntervalMs = DEFAULT_MIN_INTERVAL_MS
79342
+ } = options;
79343
+ if (!enabled) return false;
79344
+ if (!stdout.isTTY) return false;
79345
+ const atMs = now();
79346
+ if (atMs - lastRingAtMs < minIntervalMs) return false;
79347
+ lastRingAtMs = atMs;
79348
+ stdout.write(BEL);
79349
+ return true;
79350
+ }
79351
+ function attentionMessage(reason) {
79352
+ switch (reason) {
79353
+ case "interaction":
79354
+ return { title: "Pie", body: "Waiting for your input" };
79355
+ case "turn-settled":
79356
+ return { title: "Pie", body: "Response ready" };
79357
+ case "turn-failed":
79358
+ return { title: "Pie", body: "Turn failed" };
79359
+ case "stall-recovery":
79360
+ return { title: "Pie", body: "Recovered from a stalled turn" };
79361
+ }
79362
+ }
79363
+ var DEFAULT_NOTIFICATION_MIN_INTERVAL_MS = 1e3;
79364
+ var lastNotificationAtMs = 0;
79365
+ function sendDesktopNotification(reason, options = {}) {
79366
+ const {
79367
+ enabled = true,
79368
+ terminal,
79369
+ now = Date.now,
79370
+ minIntervalMs = DEFAULT_NOTIFICATION_MIN_INTERVAL_MS
79371
+ } = options;
79372
+ if (!enabled) return false;
79373
+ if (!terminal || typeof terminal.notify !== "function") return false;
79374
+ const atMs = now();
79375
+ if (atMs - lastNotificationAtMs < minIntervalMs) return false;
79376
+ lastNotificationAtMs = atMs;
79377
+ const { title, body } = attentionMessage(reason);
79378
+ terminal.notify(title, body);
79379
+ return true;
79380
+ }
79381
+
79127
79382
  // src/input/direct-bash.ts
79128
79383
  function buildDirectShellCommand(command) {
79129
79384
  if (process.platform === "win32") {
@@ -79257,13 +79512,25 @@ async function handleDirectBash(host, command) {
79257
79512
 
79258
79513
  // src/extensions/interactive-extension-ui.ts
79259
79514
  function createInteractiveExtensionUIContext(host) {
79515
+ const interact = async (run) => {
79516
+ const attention = cliAttention(host);
79517
+ const request = attention.request("input-required");
79518
+ let resolution = "cancelled";
79519
+ try {
79520
+ const value = await run();
79521
+ resolution = interactionAttentionResolution({ value });
79522
+ return value;
79523
+ } finally {
79524
+ attention.resolve(request, resolution);
79525
+ }
79526
+ };
79260
79527
  return {
79261
79528
  notify: (message, type = "info") => showExtensionNotification(host, message, type),
79262
- select: (title, options, opts) => showExtensionSelector2(host, title, options, opts),
79263
- confirm: (title, message, opts) => showExtensionConfirm(host, title, message, opts),
79264
- input: (title, placeholder, opts) => showExtensionInput(host, title, placeholder, opts),
79265
- editor: (title, prefill) => showExtensionEditor(host, title, prefill),
79266
- custom: (factory, options) => showExtensionCustom(host, factory, options),
79529
+ select: (title, options, opts) => interact(() => showExtensionSelector2(host, title, options, opts)),
79530
+ confirm: async (title, message, opts) => await interact(() => showExtensionConfirm(host, title, message, opts)) === true,
79531
+ input: (title, placeholder, opts) => interact(() => showExtensionInput(host, title, placeholder, opts)),
79532
+ editor: (title, prefill) => interact(() => showExtensionEditor(host, title, prefill)),
79533
+ custom: (factory, options) => interact(() => showExtensionCustom(host, factory, options)),
79267
79534
  setStatus: (key, text) => {
79268
79535
  host.footer.extensionStatuses.set(key, text);
79269
79536
  host.ui.requestRender();
@@ -79276,11 +79543,14 @@ function createInteractiveExtensionUIContext(host) {
79276
79543
  }
79277
79544
  function installInteractiveInteractionHandler(host) {
79278
79545
  registerInteractionHandler(async (request) => {
79546
+ const attention = cliAttention(host);
79547
+ let attentionRequest;
79279
79548
  const beginWaitingForUser = () => {
79280
79549
  host.activeInteractionCount++;
79281
79550
  host.setWaitingForUser(true);
79282
79551
  host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
79283
- host.ringAttentionBell?.("interaction");
79552
+ const reason = interactionAttentionReason(request);
79553
+ if (reason) attentionRequest = attention.request(reason, "id" in request ? request.id : void 0);
79284
79554
  host.safeRequestRender();
79285
79555
  };
79286
79556
  const endWaitingForUser = () => {
@@ -79308,9 +79578,13 @@ function installInteractiveInteractionHandler(host) {
79308
79578
  };
79309
79579
  const waitForUser = async (createPromise) => {
79310
79580
  beginWaitingForUser();
79581
+ let resolution = "cancelled";
79311
79582
  try {
79312
- return await withTimeout2(createPromise());
79583
+ const result = await withTimeout2(createPromise());
79584
+ resolution = interactionAttentionResolution(result);
79585
+ return result;
79313
79586
  } finally {
79587
+ attention.resolve(attentionRequest, resolution);
79314
79588
  endWaitingForUser();
79315
79589
  }
79316
79590
  };
@@ -79355,7 +79629,7 @@ function installInteractiveInteractionHandler(host) {
79355
79629
  confirmed,
79356
79630
  timedOut
79357
79631
  });
79358
- return { type: "confirm", id: request.id, confirmed: confirmed === true, timedOut, skipped: timedOut };
79632
+ return { type: "confirm", id: request.id, confirmed: confirmed === true, timedOut, skipped: timedOut || confirmed === void 0 };
79359
79633
  }
79360
79634
  case "text_input": {
79361
79635
  host.logDebug("interaction:text_input:start", {
@@ -79437,7 +79711,7 @@ function showExtensionSelector2(host, title, options, opts) {
79437
79711
  async function showExtensionConfirm(host, title, message, opts) {
79438
79712
  const result = await showExtensionSelector2(host, `${title}
79439
79713
  ${message}`, ["Yes", "No"], opts);
79440
- return result === "Yes";
79714
+ return result === void 0 ? void 0 : result === "Yes";
79441
79715
  }
79442
79716
  function showExtensionInput(host, title, placeholder, opts) {
79443
79717
  return new Promise((resolve7) => {
@@ -80265,7 +80539,7 @@ var InteractiveMode = class {
80265
80539
  async init() {
80266
80540
  const activeSession = this.sessionManager.getActiveSession();
80267
80541
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
80268
- const { Agent } = await import("./chunks/src-XT5S6AUQ.js");
80542
+ const { Agent } = await import("./chunks/src-ZJXBS2DB.js");
80269
80543
  this.agent = new Agent({
80270
80544
  initialState: {
80271
80545
  systemPrompt: this.buildSystemPrompt(),
@@ -80681,7 +80955,7 @@ var InteractiveMode = class {
80681
80955
  };
80682
80956
 
80683
80957
  // src/app/runtime-composition.ts
80684
- import { existsSync as existsSync19, mkdirSync as mkdirSync14 } from "fs";
80958
+ import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
80685
80959
  import * as path31 from "path";
80686
80960
  var CliMissingApiKeyError = class extends Error {
80687
80961
  constructor(message) {
@@ -80708,8 +80982,8 @@ function getCliRuntimePaths() {
80708
80982
  return { configDir, sessionsDir, skillsDir, builtinSkillsDir };
80709
80983
  }
80710
80984
  function ensureCliRuntimePaths(paths = getCliRuntimePaths()) {
80711
- if (!existsSync19(paths.skillsDir)) {
80712
- mkdirSync14(paths.skillsDir, { recursive: true });
80985
+ if (!existsSync18(paths.skillsDir)) {
80986
+ mkdirSync13(paths.skillsDir, { recursive: true });
80713
80987
  }
80714
80988
  return paths;
80715
80989
  }
@@ -80960,7 +81234,7 @@ function createPrintModeRetryStatusReporter() {
80960
81234
  console.error(`Retry succeeded after ${event.attempts} attempt${event.attempts === 1 ? "" : "s"}.`);
80961
81235
  } else if (event.type === "retry_exhausted") {
80962
81236
  exhaustedRetryBudget = event.maxRetries;
80963
- } else if (event.type === "turn_failed_terminal" && (event.failure.kind === "transient_network" || event.failure.kind === "provider_transient")) {
81237
+ } else if (event.type === "turn_failed_terminal" && (event.failure.kind === "transient_network" || event.failure.kind === "provider_transient" || event.failure.kind === "tls_config")) {
80964
81238
  console.error(`Request failed after ${exhaustedRetryBudget ?? CLI_RETRY_MAX_RETRIES} retries: ${event.failure.message}`);
80965
81239
  }
80966
81240
  };
@@ -81097,7 +81371,7 @@ async function runPrintMode(params) {
81097
81371
  `);
81098
81372
  console.log("Assistant: ");
81099
81373
  }
81100
- const { Agent: AgentClass } = await import("./chunks/src-XT5S6AUQ.js");
81374
+ const { Agent: AgentClass } = await import("./chunks/src-ZJXBS2DB.js");
81101
81375
  let agent;
81102
81376
  const printModeExtensions = await loadPrintModeExtensions({
81103
81377
  cwd: runtime.cwd,
@@ -81958,7 +82232,7 @@ async function runJsonEventsMode(params) {
81958
82232
  return;
81959
82233
  }
81960
82234
  const parent = agent;
81961
- const { Agent: AgentClass } = await import("./chunks/src-XT5S6AUQ.js");
82235
+ const { Agent: AgentClass } = await import("./chunks/src-ZJXBS2DB.js");
81962
82236
  const sideAgent = new AgentClass({
81963
82237
  initialState: {
81964
82238
  systemPrompt: parent.state.systemPrompt,
@@ -82302,7 +82576,7 @@ async function runJsonEventsMode(params) {
82302
82576
  if (runtime.initialModel.unconfigured || controller) {
82303
82577
  return;
82304
82578
  }
82305
- const { Agent: AgentClass } = await import("./chunks/src-XT5S6AUQ.js");
82579
+ const { Agent: AgentClass } = await import("./chunks/src-ZJXBS2DB.js");
82306
82580
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
82307
82581
  extensionRunner = jsonModeExtensions.runner;
82308
82582
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -82896,7 +83170,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
82896
83170
  }
82897
83171
  async function createCliTestStreamFnFromEnv() {
82898
83172
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
82899
- return (await import("./chunks/test-stream-FNWDHRTM.js")).createTestStreamFnFromSequenceEnv();
83173
+ return (await import("./chunks/test-stream-TI2S25NY.js")).createTestStreamFnFromSequenceEnv();
82900
83174
  }
82901
83175
  function resolveJsonEventsDetailFlag() {
82902
83176
  for (let index = 0; index < process.argv.length; index += 1) {