@cydm/pie 2.7.10 → 2.7.12

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
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
3
3
  import {
4
+ CLI_RETRY_MAX_RETRIES,
4
5
  VERSION,
6
+ classifyCliFailure,
5
7
  cleanupBashOutputArtifacts,
6
8
  createBashTool,
7
9
  createCliHostCapabilities,
10
+ createCliRetryPolicy,
8
11
  createCliWebResearchHealthStore,
9
12
  createTraceTransportSink,
10
13
  createTransportDiagnosticsStreamFn,
@@ -20,10 +23,13 @@ import {
20
23
  getSessionsDir,
21
24
  getSettingsPath,
22
25
  getThemesDir,
26
+ isCliContextOverflowError,
27
+ isCliRetryableError,
23
28
  logTransportLifecycle,
24
29
  migrateConfigFromAgentDir,
30
+ setYoloGrantListener,
25
31
  truncateHeadTail
26
- } from "./chunks/chunk-5GUPRFNP.js";
32
+ } from "./chunks/chunk-YI3CSQGD.js";
27
33
  import {
28
34
  resolveCliProjectRoot
29
35
  } from "./chunks/chunk-WLFOBECN.js";
@@ -88,17 +94,15 @@ import {
88
94
  shouldPreserveExecutionStateForUserText,
89
95
  supersedeExecutionState,
90
96
  verifyPreparedCompactionCommit
91
- } from "./chunks/chunk-C4LXD56O.js";
92
- import "./chunks/chunk-JO6EDWHP.js";
97
+ } from "./chunks/chunk-AFPRX5XI.js";
98
+ import "./chunks/chunk-2NBUFVUQ.js";
93
99
  import {
94
100
  Deref,
95
- EMPTY_ASSISTANT_CONTENT_ERROR,
96
101
  Errors,
97
102
  ExtendsUndefinedCheck,
98
103
  HasTransform,
99
104
  Hash,
100
105
  MODEL_CLASS_FALLBACK_ORDER,
101
- OPENAI_COMPAT_PROTOCOL_ERROR_CODE,
102
106
  TransformDecode,
103
107
  TransformDecodeCheckError,
104
108
  TransformEncode,
@@ -114,7 +118,7 @@ import {
114
118
  sortToolModelCandidatesByCapability,
115
119
  streamSimple,
116
120
  type_exports
117
- } from "./chunks/chunk-2B6VS2NH.js";
121
+ } from "./chunks/chunk-EN47TIBG.js";
118
122
  import {
119
123
  IsArray,
120
124
  IsBigInt,
@@ -1936,7 +1940,7 @@ var require_core = __commonJS({
1936
1940
  return match && match.index === 0;
1937
1941
  }
1938
1942
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
1939
- function join22(regexps, separator = "|") {
1943
+ function join24(regexps, separator = "|") {
1940
1944
  let numCaptures = 0;
1941
1945
  return regexps.map((regex) => {
1942
1946
  numCaptures += 1;
@@ -2240,7 +2244,7 @@ var require_core = __commonJS({
2240
2244
  this.exec = () => null;
2241
2245
  }
2242
2246
  const terminators = this.regexes.map((el) => el[1]);
2243
- this.matcherRe = langRe(join22(terminators), true);
2247
+ this.matcherRe = langRe(join24(terminators), true);
2244
2248
  this.lastIndex = 0;
2245
2249
  }
2246
2250
  /** @param {string} s */
@@ -58061,7 +58065,9 @@ var SYMBOL_KEYS = /* @__PURE__ */ new Set([
58061
58065
  var MODIFIERS = {
58062
58066
  shift: 1,
58063
58067
  alt: 2,
58064
- ctrl: 4
58068
+ ctrl: 4,
58069
+ /** Kitty keyboard protocol bit 8: Super (macOS Command / Windows/Linux Super). */
58070
+ super: 8
58065
58071
  };
58066
58072
  var LOCK_MASK = 64 + 128;
58067
58073
  var CODEPOINTS = {
@@ -58255,7 +58261,8 @@ function parseKeyId(keyId) {
58255
58261
  key,
58256
58262
  ctrl: parts.includes("ctrl"),
58257
58263
  shift: parts.includes("shift"),
58258
- alt: parts.includes("alt")
58264
+ alt: parts.includes("alt"),
58265
+ super: parts.includes("super") || parts.includes("cmd") || parts.includes("meta")
58259
58266
  };
58260
58267
  }
58261
58268
  function matchesKey(data, keyId) {
@@ -58266,6 +58273,7 @@ function matchesKey(data, keyId) {
58266
58273
  if (shift) modifier |= MODIFIERS.shift;
58267
58274
  if (alt) modifier |= MODIFIERS.alt;
58268
58275
  if (ctrl) modifier |= MODIFIERS.ctrl;
58276
+ if (parsed.super) modifier |= MODIFIERS.super;
58269
58277
  switch (key) {
58270
58278
  case "escape":
58271
58279
  case "esc":
@@ -59666,6 +59674,13 @@ var SelectList = class {
59666
59674
  this.filteredItems = this.items.filter((item) => item.value.toLowerCase().startsWith(filter.toLowerCase()));
59667
59675
  this.selectedIndex = 0;
59668
59676
  }
59677
+ /** Replace the item set (e.g. a scope toggle) and reset the cursor. */
59678
+ setItems(items) {
59679
+ this.items = items;
59680
+ this.filteredItems = items;
59681
+ this.selectedIndex = 0;
59682
+ if (items[0]) this.onSelectionChange?.(items[0]);
59683
+ }
59669
59684
  getVisibleRange() {
59670
59685
  const startIndex = Math.max(
59671
59686
  0,
@@ -63647,6 +63662,8 @@ import { EventEmitter } from "events";
63647
63662
  var ESC = "\x1B";
63648
63663
  var BRACKETED_PASTE_START = "\x1B[200~";
63649
63664
  var BRACKETED_PASTE_END = "\x1B[201~";
63665
+ var OSC_5522_PREFIX = "\x1B]5522;";
63666
+ var OSC_5522_MAX_FRAME = 70 * 1024 * 1024;
63650
63667
  function isCompleteSequence(data) {
63651
63668
  if (!data.startsWith(ESC)) {
63652
63669
  return "not-escape";
@@ -63733,11 +63750,19 @@ function isCompleteApcSequence(data) {
63733
63750
  }
63734
63751
  return "incomplete";
63735
63752
  }
63736
- function extractCompleteSequences(buffer) {
63753
+ function extractCompleteSequences(buffer, maxClipboardFrame = OSC_5522_MAX_FRAME) {
63737
63754
  const sequences = [];
63738
63755
  let pos = 0;
63739
63756
  while (pos < buffer.length) {
63740
63757
  const remaining = buffer.slice(pos);
63758
+ if (remaining.startsWith(`${ESC}]`)) {
63759
+ const end = /\x07|\x1b\\/.exec(remaining);
63760
+ if (!end) return { sequences, remainder: remaining };
63761
+ const length = end.index + end[0].length;
63762
+ if (!remaining.startsWith(OSC_5522_PREFIX) || length <= maxClipboardFrame) sequences.push(remaining.slice(0, length));
63763
+ pos += length;
63764
+ continue;
63765
+ }
63741
63766
  if (remaining.startsWith(ESC)) {
63742
63767
  let seqEnd = 1;
63743
63768
  while (seqEnd <= remaining.length) {
@@ -63771,9 +63796,13 @@ var StdinBuffer = class extends EventEmitter {
63771
63796
  timeoutMs;
63772
63797
  pasteMode = false;
63773
63798
  pasteBuffer = "";
63799
+ discardingClipboard = false;
63800
+ discardEsc = false;
63801
+ options;
63774
63802
  constructor(options = {}) {
63775
63803
  super();
63776
63804
  this.timeoutMs = options.timeout ?? 10;
63805
+ this.options = options;
63777
63806
  }
63778
63807
  process(data) {
63779
63808
  if (this.timeout) {
@@ -63795,6 +63824,21 @@ var StdinBuffer = class extends EventEmitter {
63795
63824
  this.emit("data", "");
63796
63825
  return;
63797
63826
  }
63827
+ if (this.discardingClipboard) {
63828
+ const discarded = (this.discardEsc ? ESC : "") + str2;
63829
+ const end = /\x07|\x1b\\/.exec(discarded);
63830
+ if (!end) {
63831
+ this.discardEsc = discarded.endsWith(ESC);
63832
+ this.timeout = setTimeout(() => {
63833
+ this.discardingClipboard = false;
63834
+ this.discardEsc = false;
63835
+ }, this.options.clipboardFrameTimeout ?? 1e3);
63836
+ return;
63837
+ }
63838
+ this.discardingClipboard = false;
63839
+ this.discardEsc = false;
63840
+ str2 = discarded.slice(end.index + end[0].length);
63841
+ }
63798
63842
  this.buffer += str2;
63799
63843
  if (this.pasteMode) {
63800
63844
  this.pasteBuffer += this.buffer;
@@ -63838,12 +63882,40 @@ var StdinBuffer = class extends EventEmitter {
63838
63882
  }
63839
63883
  return;
63840
63884
  }
63841
- const result = extractCompleteSequences(this.buffer);
63885
+ const result = extractCompleteSequences(this.buffer, this.options.clipboardMaxFrame);
63842
63886
  this.buffer = result.remainder;
63843
63887
  for (const sequence of result.sequences) {
63844
63888
  this.emit("data", sequence);
63845
63889
  }
63846
63890
  if (this.buffer.length > 0) {
63891
+ const heldFor5522 = this.buffer.startsWith(OSC_5522_PREFIX) || this.buffer.length >= 2 && OSC_5522_PREFIX.startsWith(this.buffer) || this.buffer === ESC && this.options.clipboardResponsePending?.();
63892
+ if (heldFor5522 && this.buffer.length <= (this.options.clipboardMaxFrame ?? OSC_5522_MAX_FRAME)) {
63893
+ this.timeout = setTimeout(() => {
63894
+ const ambiguousEscape = this.buffer === ESC;
63895
+ this.discardEsc = this.buffer.endsWith(ESC);
63896
+ this.buffer = "";
63897
+ if (ambiguousEscape) {
63898
+ this.emit("data", ESC);
63899
+ return;
63900
+ }
63901
+ this.discardingClipboard = true;
63902
+ this.timeout = setTimeout(() => {
63903
+ this.discardingClipboard = false;
63904
+ this.discardEsc = false;
63905
+ }, this.options.clipboardFrameTimeout ?? 1e3);
63906
+ }, this.options.clipboardFrameTimeout ?? 1e3);
63907
+ return;
63908
+ }
63909
+ if (heldFor5522) {
63910
+ this.discardEsc = this.buffer.endsWith(ESC);
63911
+ this.buffer = "";
63912
+ this.discardingClipboard = true;
63913
+ this.timeout = setTimeout(() => {
63914
+ this.discardingClipboard = false;
63915
+ this.discardEsc = false;
63916
+ }, this.options.clipboardFrameTimeout ?? 1e3);
63917
+ return;
63918
+ }
63847
63919
  this.timeout = setTimeout(() => {
63848
63920
  const flushed = this.flush();
63849
63921
  for (const sequence of flushed) {
@@ -63860,7 +63932,7 @@ var StdinBuffer = class extends EventEmitter {
63860
63932
  if (this.buffer.length === 0) {
63861
63933
  return [];
63862
63934
  }
63863
- const sequences = [this.buffer];
63935
+ const sequences = this.buffer.startsWith(OSC_5522_PREFIX) || this.buffer.length >= 2 && OSC_5522_PREFIX.startsWith(this.buffer) ? [] : [this.buffer];
63864
63936
  this.buffer = "";
63865
63937
  return sequences;
63866
63938
  }
@@ -63872,6 +63944,8 @@ var StdinBuffer = class extends EventEmitter {
63872
63944
  this.buffer = "";
63873
63945
  this.pasteMode = false;
63874
63946
  this.pasteBuffer = "";
63947
+ this.discardingClipboard = false;
63948
+ this.discardEsc = false;
63875
63949
  }
63876
63950
  getBuffer() {
63877
63951
  return this.buffer;
@@ -63884,8 +63958,193 @@ var StdinBuffer = class extends EventEmitter {
63884
63958
  // ../../packages/tui/src/terminal.ts
63885
63959
  import * as fs11 from "node:fs";
63886
63960
  import koffi from "koffi";
63961
+
63962
+ // ../../packages/tui/src/osc-5522.ts
63963
+ var OSC = "\x1B]";
63964
+ var ST = "\x1B\\";
63965
+ var READ_TIMEOUT_MS = 8e3;
63966
+ var SUPPORT_TIMEOUT_MS = 1200;
63967
+ var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
63968
+ function parseOsc5522Packet(data) {
63969
+ const match = data.match(/^\x1b\]5522;([^;]*);?([^\x1b\x07]*)(?:\x1b\\|\x07)$/);
63970
+ if (!match) return void 0;
63971
+ return { metadata: match[1] ?? "", payload: match[2] ?? "" };
63972
+ }
63973
+ function isStrictBase64(value) {
63974
+ if (value.length % 4 !== 0) return false;
63975
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value)) return false;
63976
+ const padding = value.length - value.replace(/=+$/, "").length;
63977
+ if (padding > 2) return false;
63978
+ if (padding > 0 && value.slice(0, -padding).includes("=")) return false;
63979
+ return true;
63980
+ }
63981
+ function parseMetadata(metadata) {
63982
+ const fields = /* @__PURE__ */ new Map();
63983
+ for (const part of metadata.split(":")) {
63984
+ const eq = part.indexOf("=");
63985
+ if (eq === -1) {
63986
+ if (part) fields.set(part, "");
63987
+ } else {
63988
+ fields.set(part.slice(0, eq), part.slice(eq + 1));
63989
+ }
63990
+ }
63991
+ return fields;
63992
+ }
63993
+ var Osc5522Client = class {
63994
+ constructor(write = (data) => process.stdout.write(data)) {
63995
+ this.write = write;
63996
+ }
63997
+ write;
63998
+ session;
63999
+ supportMode = "unknown";
64000
+ pendingSupportResolve;
64001
+ get responsePending() {
64002
+ return this.session !== void 0 || this.pendingSupportResolve !== void 0;
64003
+ }
64004
+ /**
64005
+ * Feed one stdin sequence. Returns true when the packet was consumed as
64006
+ * part of this protocol, so the caller can drop it before normal routing.
64007
+ */
64008
+ feed(data) {
64009
+ const support = data.match(/^\x1b\[\?5522;(\d+)\$y$/);
64010
+ if (support) {
64011
+ const ps = Number(support[1]);
64012
+ const supported = ps !== 0 && ps !== 4;
64013
+ this.supportMode = supported ? "supported" : "unsupported";
64014
+ this.pendingSupportResolve?.(supported);
64015
+ this.pendingSupportResolve = void 0;
64016
+ return true;
64017
+ }
64018
+ const packet = parseOsc5522Packet(data);
64019
+ if (!packet) return false;
64020
+ const fields = parseMetadata(packet.metadata);
64021
+ const type = fields.get("type") ?? "";
64022
+ if (!type.startsWith("read")) return false;
64023
+ const status = fields.get("status");
64024
+ const session = this.session;
64025
+ if (!session) return true;
64026
+ if (status === void 0) {
64027
+ this.finishFor(session, { ok: false, kind: "malformed", message: `OSC 5522 read packet without status: ${packet.metadata}` });
64028
+ return true;
64029
+ }
64030
+ if (status === "OK") {
64031
+ return true;
64032
+ }
64033
+ if (status === "DATA") {
64034
+ const mimeEncoded = fields.get("mime");
64035
+ if (mimeEncoded === void 0 || mimeEncoded === "") {
64036
+ this.finishFor(session, { ok: false, kind: "malformed", message: `DATA packet without mime field: ${packet.metadata}` });
64037
+ return true;
64038
+ }
64039
+ if (!isStrictBase64(mimeEncoded)) {
64040
+ this.finishFor(session, { ok: false, kind: "malformed", message: "Invalid base64 MIME in DATA packet" });
64041
+ return true;
64042
+ }
64043
+ const mimeType = Buffer.from(mimeEncoded, "base64").toString("utf8");
64044
+ if (session.mimeType !== void 0 && session.mimeType !== mimeType) {
64045
+ return true;
64046
+ }
64047
+ if (session.mimeType === void 0) {
64048
+ session.mimeType = mimeType;
64049
+ }
64050
+ if (!isStrictBase64(packet.payload)) {
64051
+ this.finishFor(session, { ok: false, kind: "malformed", message: "Invalid base64 payload in DATA packet" });
64052
+ return true;
64053
+ }
64054
+ const chunk = Buffer.from(packet.payload, "base64");
64055
+ session.totalBytes += chunk.length;
64056
+ if (session.totalBytes > MAX_TOTAL_BYTES) {
64057
+ this.finishFor(session, { ok: false, kind: "too-large", message: `Clipboard image exceeds the ${Math.floor(MAX_TOTAL_BYTES / (1024 * 1024))}MB limit.` });
64058
+ return true;
64059
+ }
64060
+ session.chunks.push(chunk);
64061
+ return true;
64062
+ }
64063
+ if (status === "DONE") {
64064
+ const mimeType = session.mimeType;
64065
+ if (!mimeType || session.chunks.length === 0) {
64066
+ this.finishFor(session, { ok: false, kind: "unavailable", message: "Clipboard read completed without data." });
64067
+ return true;
64068
+ }
64069
+ this.finishFor(session, { ok: true, mimeType, bytes: Buffer.concat(session.chunks) });
64070
+ return true;
64071
+ }
64072
+ const kind = status === "EPERM" ? "denied" : status === "EBUSY" ? "busy" : status === "ENOSYS" ? "unavailable" : "malformed";
64073
+ const message = status === "EPERM" ? "Terminal denied the clipboard read (permission)." : status === "EBUSY" ? "Clipboard is busy (another client is using it)." : status === "ENOSYS" ? "Requested clipboard type is not available." : `Unexpected OSC 5522 status: ${status}`;
64074
+ this.finishFor(session, { ok: false, kind, message });
64075
+ return true;
64076
+ }
64077
+ /**
64078
+ * Query DECRQM support (CSI ? 5522 $ p). Resolves false on timeout —
64079
+ * terminals that do not know the mode never reply.
64080
+ */
64081
+ async querySupport(timeoutMs = SUPPORT_TIMEOUT_MS) {
64082
+ if (this.supportMode !== "unknown") return this.supportMode === "supported";
64083
+ return new Promise((resolve7) => {
64084
+ let settled = false;
64085
+ const settle = (supported) => {
64086
+ if (settled) return;
64087
+ settled = true;
64088
+ clearTimeout(timer);
64089
+ this.pendingSupportResolve = void 0;
64090
+ resolve7(supported);
64091
+ };
64092
+ const timer = setTimeout(() => settle(false), timeoutMs);
64093
+ this.pendingSupportResolve = settle;
64094
+ this.write(`\x1B[?5522$p`);
64095
+ });
64096
+ }
64097
+ /**
64098
+ * Read the clipboard's image data. The first offered representation of
64099
+ * the requested types is returned (png preferred over jpeg by request
64100
+ * order). Resolves unsupported when the terminal never replies to the
64101
+ * DECRQM probe.
64102
+ */
64103
+ async readImage(timeoutMs = READ_TIMEOUT_MS, supportTimeoutMs = SUPPORT_TIMEOUT_MS) {
64104
+ if (this.session) {
64105
+ return { ok: false, kind: "busy", message: "Another OSC 5522 read is already in flight." };
64106
+ }
64107
+ const session = {
64108
+ resolve: void 0,
64109
+ timer: void 0,
64110
+ chunks: [],
64111
+ totalBytes: 0
64112
+ };
64113
+ const promise = new Promise((resolve7) => {
64114
+ session.resolve = resolve7;
64115
+ });
64116
+ session.timer = setTimeout(() => {
64117
+ this.finishFor(session, { ok: false, kind: "timeout", message: "Terminal did not respond to the clipboard read in time." });
64118
+ }, timeoutMs);
64119
+ this.session = session;
64120
+ const supported = await this.querySupport(supportTimeoutMs);
64121
+ if (!supported) {
64122
+ this.finishFor(session, { ok: false, kind: "unsupported", message: "Terminal does not support the OSC 5522 clipboard protocol." });
64123
+ return promise;
64124
+ }
64125
+ if (this.session !== session) return promise;
64126
+ const mimeList = Buffer.from("image/png image/jpeg").toString("base64");
64127
+ this.write(`${OSC}5522;type=read;${mimeList}${ST}`);
64128
+ return promise;
64129
+ }
64130
+ /** Finish only if `session` is still the live session (identity-bound). */
64131
+ finishFor(session, result) {
64132
+ if (this.session !== session) return;
64133
+ this.session = void 0;
64134
+ clearTimeout(session.timer);
64135
+ session.resolve(result);
64136
+ }
64137
+ };
64138
+
64139
+ // ../../packages/tui/src/terminal.ts
63887
64140
  var ProcessTerminal = class {
63888
64141
  wasRaw = false;
64142
+ /** Client for the Kitty OSC 5522 clipboard protocol (read side). */
64143
+ osc5522Client = new Osc5522Client();
64144
+ /** Access the OSC 5522 clipboard client (terminal-level clipboard reads). */
64145
+ get clipboard() {
64146
+ return this.osc5522Client;
64147
+ }
63889
64148
  inputHandler;
63890
64149
  resizeHandler;
63891
64150
  _kittyProtocolActive = false;
@@ -63927,9 +64186,12 @@ var ProcessTerminal = class {
63927
64186
  * to handle the case where the response arrives split across multiple events.
63928
64187
  */
63929
64188
  setupStdinBuffer() {
63930
- this.stdinBuffer = new StdinBuffer({ timeout: 10 });
64189
+ this.stdinBuffer = new StdinBuffer({ timeout: 10, clipboardResponsePending: () => this.osc5522Client.responsePending });
63931
64190
  const kittyResponsePattern = /^\x1b\[\?(\d+)u$/;
63932
64191
  this.stdinBuffer.on("data", (sequence) => {
64192
+ if (this.osc5522Client.feed(sequence)) {
64193
+ return;
64194
+ }
63933
64195
  if (!this._kittyProtocolActive) {
63934
64196
  const match = sequence.match(kittyResponsePattern);
63935
64197
  if (match) {
@@ -64188,11 +64450,14 @@ var ExtensionRunner = class {
64188
64450
  };
64189
64451
  }
64190
64452
  /**
64191
- * Set the UI context for interactive mode.
64453
+ * Set the UI context for interactive mode. Does NOT change hasUI: the
64454
+ * constructor's hasUI argument (host-provided: true for interactive, false
64455
+ * for print/json-events no-op contexts) is the single source of truth.
64456
+ * Flipping it here used to make headless hosts report ctx.hasUI === true
64457
+ * while their ui.* methods silently no-op'd.
64192
64458
  */
64193
64459
  setUIContext(uiContext) {
64194
64460
  this.uiContext = uiContext;
64195
- this.hasUI = true;
64196
64461
  }
64197
64462
  /**
64198
64463
  * Wrap an unregister handle so this runner can revoke it during
@@ -64940,7 +65205,7 @@ var DEFAULT_APP_KEYBINDINGS = {
64940
65205
  steer: "alt+enter",
64941
65206
  followUp: [],
64942
65207
  dequeue: ["alt+up", "ctrl+shift+up"],
64943
- pasteImage: "ctrl+v",
65208
+ pasteImage: ["ctrl+v", "alt+v", "super+v"],
64944
65209
  newSession: [],
64945
65210
  tree: [],
64946
65211
  fork: [],
@@ -65094,32 +65359,6 @@ function buildCliProjectContextSection(cwd) {
65094
65359
  return buildProjectContextSection(collectAncestorAgentsFiles(cwd));
65095
65360
  }
65096
65361
 
65097
- // src/file-access-context.ts
65098
- import * as path11 from "node:path";
65099
- function uniqueNormalizedPaths(paths) {
65100
- const seen = /* @__PURE__ */ new Set();
65101
- const result = [];
65102
- for (const entry of paths) {
65103
- const trimmed = String(entry || "").trim();
65104
- if (!trimmed) continue;
65105
- const normalized = path11.normalize(trimmed);
65106
- if (seen.has(normalized)) continue;
65107
- seen.add(normalized);
65108
- result.push(normalized);
65109
- }
65110
- return result;
65111
- }
65112
- function buildCliFileAccessOptions(params) {
65113
- return {
65114
- accessRoots: uniqueNormalizedPaths([
65115
- resolveCliProjectRoot(params.cwd),
65116
- params.configDir,
65117
- ...params.skills.map((skill) => skill.baseDir)
65118
- ]),
65119
- allowlistedDirs: params.allowlistedDirs
65120
- };
65121
- }
65122
-
65123
65362
  // src/runtime/computer-use-owner.ts
65124
65363
  var COMPUTER_USE_OWNER_ENV = "PIE_COMPUTER_USE_OWNER_ID";
65125
65364
  var COMPUTER_USE_EXECUTION_ENV = "PIE_COMPUTER_USE_EXECUTION_ID";
@@ -65139,7 +65378,7 @@ function computerUseOwnerEnv(ownerId, executionId) {
65139
65378
 
65140
65379
  // src/theme/theme.ts
65141
65380
  import * as fs13 from "node:fs";
65142
- import * as path12 from "node:path";
65381
+ import * as path11 from "node:path";
65143
65382
 
65144
65383
  // ../../node_modules/@sinclair/typebox/build/esm/compiler/compiler.mjs
65145
65384
  var TypeCheck = class {
@@ -66026,8 +66265,8 @@ var BUILTIN_THEMES;
66026
66265
  function getBuiltinThemes() {
66027
66266
  if (!BUILTIN_THEMES) {
66028
66267
  const themesDir = getThemesDir();
66029
- const darkPath = path12.join(themesDir, "dark.json");
66030
- const lightPath = path12.join(themesDir, "light.json");
66268
+ const darkPath = path11.join(themesDir, "dark.json");
66269
+ const lightPath = path11.join(themesDir, "light.json");
66031
66270
  BUILTIN_THEMES = {
66032
66271
  dark: JSON.parse(fs13.readFileSync(darkPath, "utf-8")),
66033
66272
  light: JSON.parse(fs13.readFileSync(lightPath, "utf-8"))
@@ -66064,23 +66303,23 @@ function parseThemeJson(label, json) {
66064
66303
  otherErrors.push(` - ${e.path}: ${e.message}`);
66065
66304
  }
66066
66305
  }
66067
- let errorMessage4 = `Invalid theme "${label}":
66306
+ let errorMessage5 = `Invalid theme "${label}":
66068
66307
  `;
66069
66308
  if (missingColors.length > 0) {
66070
- errorMessage4 += "\nMissing required color tokens:\n";
66071
- errorMessage4 += missingColors.map((c) => ` - ${c}`).join("\n");
66072
- errorMessage4 += `
66309
+ errorMessage5 += "\nMissing required color tokens:\n";
66310
+ errorMessage5 += missingColors.map((c) => ` - ${c}`).join("\n");
66311
+ errorMessage5 += `
66073
66312
 
66074
66313
  Please add these colors to your theme's "colors" object.`;
66075
- errorMessage4 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
66314
+ errorMessage5 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
66076
66315
  }
66077
66316
  if (otherErrors.length > 0) {
66078
- errorMessage4 += `
66317
+ errorMessage5 += `
66079
66318
 
66080
66319
  Other errors:
66081
66320
  ${otherErrors.join("\n")}`;
66082
66321
  }
66083
- throw new Error(errorMessage4);
66322
+ throw new Error(errorMessage5);
66084
66323
  }
66085
66324
  return json;
66086
66325
  }
@@ -66107,7 +66346,7 @@ function loadThemeJson(name) {
66107
66346
  throw new Error(`Theme "${name}" does not have a source path for export`);
66108
66347
  }
66109
66348
  const customThemesDir = getCustomThemesDir();
66110
- const themePath = path12.join(customThemesDir, `${name}.json`);
66349
+ const themePath = path11.join(customThemesDir, `${name}.json`);
66111
66350
  if (!fs13.existsSync(themePath)) {
66112
66351
  throw new Error(`Theme not found: ${name}`);
66113
66352
  }
@@ -66227,7 +66466,7 @@ function startThemeWatcher() {
66227
66466
  return;
66228
66467
  }
66229
66468
  const customThemesDir = getCustomThemesDir();
66230
- const themeFile = path12.join(customThemesDir, `${currentThemeName}.json`);
66469
+ const themeFile = path11.join(customThemesDir, `${currentThemeName}.json`);
66231
66470
  if (!fs13.existsSync(themeFile)) {
66232
66471
  return;
66233
66472
  }
@@ -66428,7 +66667,7 @@ function getSettingsListTheme() {
66428
66667
  var import_yaml = __toESM(require_dist2(), 1);
66429
66668
  import * as fs14 from "fs";
66430
66669
  import * as os4 from "os";
66431
- import * as path13 from "path";
66670
+ import * as path12 from "path";
66432
66671
  function parseSkillDescription(content) {
66433
66672
  content = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
66434
66673
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
@@ -66452,8 +66691,8 @@ function parseSkillDescription(content) {
66452
66691
  }
66453
66692
  var USER_CONTROLLED_SOURCES = /* @__PURE__ */ new Set(["user-agents", "user"]);
66454
66693
  function pathIsInside(child, parent) {
66455
- const relative3 = path13.relative(parent, child);
66456
- return relative3 !== "" && !relative3.startsWith(`..${path13.sep}`) && relative3 !== ".." && !path13.isAbsolute(relative3);
66694
+ const relative3 = path12.relative(parent, child);
66695
+ return relative3 !== "" && !relative3.startsWith(`..${path12.sep}`) && relative3 !== ".." && !path12.isAbsolute(relative3);
66457
66696
  }
66458
66697
  function resolveSkillSymlink(fullPath, dir, source) {
66459
66698
  let target;
@@ -66478,7 +66717,7 @@ function loadSkillsFromDir(dir, source) {
66478
66717
  const entries = fs14.readdirSync(dir, { withFileTypes: true });
66479
66718
  for (const entry of entries) {
66480
66719
  if (entry.name.startsWith(".")) continue;
66481
- const fullPath = path13.join(dir, entry.name);
66720
+ const fullPath = path12.join(dir, entry.name);
66482
66721
  let entryPath = fullPath;
66483
66722
  let entryIsDirectory = entry.isDirectory();
66484
66723
  let entryIsFile = entry.isFile();
@@ -66493,7 +66732,7 @@ function loadSkillsFromDir(dir, source) {
66493
66732
  entryIsFile = resolved.isFile;
66494
66733
  }
66495
66734
  if (entryIsDirectory) {
66496
- const skillMdPath = path13.join(entryPath, "SKILL.md");
66735
+ const skillMdPath = path12.join(entryPath, "SKILL.md");
66497
66736
  if (fs14.existsSync(skillMdPath)) {
66498
66737
  try {
66499
66738
  const content = fs14.readFileSync(skillMdPath, "utf-8");
@@ -66525,7 +66764,7 @@ function loadSkillsFromDir(dir, source) {
66525
66764
  content,
66526
66765
  filePath: entryPath,
66527
66766
  source,
66528
- baseDir: path13.dirname(entryPath)
66767
+ baseDir: path12.dirname(entryPath)
66529
66768
  });
66530
66769
  } catch (e) {
66531
66770
  diagnostics.push({
@@ -66539,18 +66778,18 @@ function loadSkillsFromDir(dir, source) {
66539
66778
  return { skills, diagnostics };
66540
66779
  }
66541
66780
  function discoverAgentsSkillsDirs(cwd) {
66542
- const resolvedCwd = path13.resolve(cwd || process.cwd());
66781
+ const resolvedCwd = path12.resolve(cwd || process.cwd());
66543
66782
  const projectRoot = resolveCliProjectRoot(resolvedCwd);
66544
66783
  const projectAgentsDirs = [];
66545
66784
  let current = projectRoot;
66546
66785
  while (true) {
66547
- projectAgentsDirs.push(path13.join(current, ".agents", "skills"));
66786
+ projectAgentsDirs.push(path12.join(current, ".agents", "skills"));
66548
66787
  if (current === resolvedCwd) break;
66549
- const child = path13.relative(current, resolvedCwd).split(path13.sep)[0];
66788
+ const child = path12.relative(current, resolvedCwd).split(path12.sep)[0];
66550
66789
  if (!child) break;
66551
- current = path13.join(current, child);
66790
+ current = path12.join(current, child);
66552
66791
  }
66553
- return { userAgentsDir: path13.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66792
+ return { userAgentsDir: path12.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66554
66793
  }
66555
66794
  function buildSkillDirs(options) {
66556
66795
  const { userAgentsDir, projectAgentsDirs } = discoverAgentsSkillsDirs(options.cwd);
@@ -66724,7 +66963,7 @@ function validateSkillName(name) {
66724
66963
  import * as path30 from "path";
66725
66964
 
66726
66965
  // src/capabilities/read-file-understanding.ts
66727
- import * as path22 from "node:path";
66966
+ import * as path21 from "node:path";
66728
66967
  import * as fs22 from "node:fs/promises";
66729
66968
 
66730
66969
  // src/core/provider-capabilities.ts
@@ -66752,7 +66991,7 @@ function getProviderDisplayName(platform2) {
66752
66991
 
66753
66992
  // src/files/adapters/kimi-adapter.ts
66754
66993
  import * as fs15 from "node:fs";
66755
- import * as path14 from "node:path";
66994
+ import * as path13 from "node:path";
66756
66995
 
66757
66996
  // src/files/errors.ts
66758
66997
  var FileErrorMessages = {
@@ -66889,7 +67128,7 @@ var KimiFileAdapter = class {
66889
67128
  // Upload
66890
67129
  // ============================================================================
66891
67130
  async upload(filePath, options = {}) {
66892
- const fileName = options.sourceName || path14.basename(filePath);
67131
+ const fileName = options.sourceName || path13.basename(filePath);
66893
67132
  const mimeType = options.mimeType || this.guessMimeType(fileName);
66894
67133
  const purpose = this.detectPurpose(filePath, options.purpose, mimeType);
66895
67134
  const stats = fs15.statSync(filePath);
@@ -66951,7 +67190,7 @@ var KimiFileAdapter = class {
66951
67190
  return "file-extract";
66952
67191
  }
66953
67192
  guessMimeType(filename) {
66954
- const ext = path14.extname(filename).toLowerCase();
67193
+ const ext = path13.extname(filename).toLowerCase();
66955
67194
  switch (ext) {
66956
67195
  case ".jpg":
66957
67196
  case ".jpeg":
@@ -67114,7 +67353,7 @@ var KimiFileAdapter = class {
67114
67353
 
67115
67354
  // src/files/adapters/gemini-adapter.ts
67116
67355
  import * as fs16 from "node:fs";
67117
- import * as path15 from "node:path";
67356
+ import * as path14 from "node:path";
67118
67357
  import { Readable } from "node:stream";
67119
67358
  var GeminiFileAdapter = class {
67120
67359
  platform = "gemini";
@@ -67179,7 +67418,7 @@ var GeminiFileAdapter = class {
67179
67418
  );
67180
67419
  }
67181
67420
  const mimeType = options.mimeType || this.guessMimeType(filePath);
67182
- const displayName = options.sourceName || path15.basename(filePath);
67421
+ const displayName = options.sourceName || path14.basename(filePath);
67183
67422
  const startResponse = await fetch(
67184
67423
  `${this.baseUrl}/upload/v1beta/files?key=${this.apiKey}`,
67185
67424
  {
@@ -67415,7 +67654,7 @@ var GeminiFileAdapter = class {
67415
67654
 
67416
67655
  // src/files/adapters/openai-compatible-files-adapter.ts
67417
67656
  import * as fs17 from "node:fs";
67418
- import * as path16 from "node:path";
67657
+ import * as path15 from "node:path";
67419
67658
  var OpenAICompatibleFilesAdapter = class {
67420
67659
  constructor(apiKey, baseUrl, extraHeaders = {}) {
67421
67660
  this.apiKey = apiKey;
@@ -67441,7 +67680,7 @@ var OpenAICompatibleFilesAdapter = class {
67441
67680
  ]
67442
67681
  };
67443
67682
  async upload(filePath, options = {}) {
67444
- const fileName = options.sourceName || path16.basename(filePath);
67683
+ const fileName = options.sourceName || path15.basename(filePath);
67445
67684
  const mimeType = options.mimeType || guessMimeType(fileName);
67446
67685
  const stats = fs17.statSync(filePath);
67447
67686
  if (stats.size > this.capabilities.maxFileSize) {
@@ -67567,7 +67806,7 @@ function extractContentText(raw) {
67567
67806
  return raw;
67568
67807
  }
67569
67808
  function guessMimeType(filename) {
67570
- const ext = path16.extname(filename).toLowerCase();
67809
+ const ext = path15.extname(filename).toLowerCase();
67571
67810
  switch (ext) {
67572
67811
  case ".pdf":
67573
67812
  return "application/pdf";
@@ -67584,12 +67823,12 @@ function guessMimeType(filename) {
67584
67823
 
67585
67824
  // src/files/file-manager.ts
67586
67825
  import * as fs21 from "node:fs";
67587
- import * as path21 from "node:path";
67826
+ import * as path20 from "node:path";
67588
67827
  import * as crypto2 from "node:crypto";
67589
67828
 
67590
67829
  // src/files/file-cache.ts
67591
67830
  import * as fs18 from "node:fs";
67592
- import * as path18 from "node:path";
67831
+ import * as path17 from "node:path";
67593
67832
  import * as crypto from "node:crypto";
67594
67833
 
67595
67834
  // src/files/logger.ts
@@ -67684,7 +67923,7 @@ var DEFAULT_PIC_OPTIONS = {
67684
67923
  };
67685
67924
 
67686
67925
  // src/files/utils.ts
67687
- import * as path17 from "node:path";
67926
+ import * as path16 from "node:path";
67688
67927
  import { homedir as homedir5 } from "node:os";
67689
67928
  var WINDOWS_ABSOLUTE_PATH_RE = /^(?:[a-zA-Z]:[\\/]|[\\/]{2}[^\\/]+[\\/][^\\/]+)/;
67690
67929
  function isWindowsAbsolutePath(filePath) {
@@ -67705,13 +67944,13 @@ function formatBytes(bytes) {
67705
67944
  }
67706
67945
  function resolvePath2(inputPath) {
67707
67946
  if (inputPath.startsWith("~")) {
67708
- return path17.join(getHomeDirectory(), inputPath.slice(1));
67947
+ return path16.join(getHomeDirectory(), inputPath.slice(1));
67709
67948
  }
67710
67949
  if (isWindowsAbsolutePath(inputPath)) {
67711
67950
  return inputPath;
67712
67951
  }
67713
- if (!path17.isAbsolute(inputPath)) {
67714
- return path17.resolve(process.cwd(), inputPath);
67952
+ if (!path16.isAbsolute(inputPath)) {
67953
+ return path16.resolve(process.cwd(), inputPath);
67715
67954
  }
67716
67955
  return inputPath;
67717
67956
  }
@@ -67739,8 +67978,8 @@ var FileCache = class _FileCache {
67739
67978
  maxEntries;
67740
67979
  constructor(baseDir, maxEntries = DEFAULT_MAX_CACHE_ENTRIES) {
67741
67980
  this.baseDir = baseDir;
67742
- this.dataDir = path18.join(baseDir, "data");
67743
- this.indexPath = path18.join(baseDir, "index.json");
67981
+ this.dataDir = path17.join(baseDir, "data");
67982
+ this.indexPath = path17.join(baseDir, "index.json");
67744
67983
  this.maxEntries = maxEntries;
67745
67984
  this.index = this.loadIndex();
67746
67985
  this.ensureDirectories();
@@ -67806,15 +68045,15 @@ var FileCache = class _FileCache {
67806
68045
  // ============================================================================
67807
68046
  getStoragePath(hash) {
67808
68047
  const subdir = hash.slice(0, 2);
67809
- const fullDir = path18.join(this.dataDir, subdir);
68048
+ const fullDir = path17.join(this.dataDir, subdir);
67810
68049
  if (!fs18.existsSync(fullDir)) {
67811
68050
  fs18.mkdirSync(fullDir, { recursive: true });
67812
68051
  }
67813
- return path18.join(fullDir, hash);
68052
+ return path17.join(fullDir, hash);
67814
68053
  }
67815
68054
  getStorageRelativePath(hash) {
67816
68055
  const subdir = hash.slice(0, 2);
67817
- return path18.join(subdir, hash);
68056
+ return path17.join(subdir, hash);
67818
68057
  }
67819
68058
  async storeFile(sourcePath, name, mimeType) {
67820
68059
  const hash = await this.computeHash(sourcePath);
@@ -67876,7 +68115,7 @@ var FileCache = class _FileCache {
67876
68115
  getLocalPath(hash) {
67877
68116
  const entry = this.index.files[hash];
67878
68117
  if (!entry) return void 0;
67879
- return path18.join(this.dataDir, entry.localPath);
68118
+ return path17.join(this.dataDir, entry.localPath);
67880
68119
  }
67881
68120
  exists(hash) {
67882
68121
  return hash in this.index.files;
@@ -67956,7 +68195,7 @@ var FileCache = class _FileCache {
67956
68195
  this.index.pathHints[this.getPathHintKey(resolvedPath)] = {
67957
68196
  path: resolvedPath,
67958
68197
  hash,
67959
- name: name || entry?.name || path18.basename(resolvedPath),
68198
+ name: name || entry?.name || path17.basename(resolvedPath),
67960
68199
  size: stat2.size,
67961
68200
  mtimeMs: normalizedMtimeMs,
67962
68201
  lastSeenAt: Date.now(),
@@ -68004,7 +68243,7 @@ var FileCache = class _FileCache {
68004
68243
  deleteEntry(hash, entry) {
68005
68244
  entry = entry || this.index.files[hash];
68006
68245
  if (!entry) return;
68007
- const localPath = path18.join(this.dataDir, entry.localPath);
68246
+ const localPath = path17.join(this.dataDir, entry.localPath);
68008
68247
  if (fs18.existsSync(localPath)) {
68009
68248
  fs18.unlinkSync(localPath);
68010
68249
  }
@@ -68020,7 +68259,7 @@ var FileCache = class _FileCache {
68020
68259
  }
68021
68260
  clear() {
68022
68261
  for (const entry of Object.values(this.index.files)) {
68023
- const localPath = path18.join(this.dataDir, entry.localPath);
68262
+ const localPath = path17.join(this.dataDir, entry.localPath);
68024
68263
  if (fs18.existsSync(localPath)) {
68025
68264
  fs18.unlinkSync(localPath);
68026
68265
  }
@@ -68075,11 +68314,11 @@ var FileCache = class _FileCache {
68075
68314
  };
68076
68315
  }
68077
68316
  static getMimeType(fileName) {
68078
- const ext = path18.extname(fileName).toLowerCase();
68317
+ const ext = path17.extname(fileName).toLowerCase();
68079
68318
  return MIME_TYPES[ext] || "application/octet-stream";
68080
68319
  }
68081
68320
  static getFileTypeFromName(fileName) {
68082
- const baseName = path18.basename(fileName);
68321
+ const baseName = path17.basename(fileName);
68083
68322
  if (!baseName || baseName.startsWith(".")) {
68084
68323
  return void 0;
68085
68324
  }
@@ -68119,7 +68358,7 @@ var FileCache = class _FileCache {
68119
68358
 
68120
68359
  // src/files/session-file-manager.ts
68121
68360
  import * as fs19 from "node:fs";
68122
- import * as path19 from "node:path";
68361
+ import * as path18 from "node:path";
68123
68362
  var INITIAL_REFRESH_BACKOFF_MS = 6e4;
68124
68363
  var MAX_REFRESH_BACKOFF_MS = 60 * 60 * 1e3;
68125
68364
  var SessionFileManager = class {
@@ -68133,7 +68372,7 @@ var SessionFileManager = class {
68133
68372
  constructor(sessionId, sessionsDir, fileCache, authFingerprint) {
68134
68373
  this.sessionId = sessionId;
68135
68374
  this.sessionsDir = sessionsDir;
68136
- this.statePath = path19.join(this.sessionsDir, `${sessionId}-files.json`);
68375
+ this.statePath = path18.join(this.sessionsDir, `${sessionId}-files.json`);
68137
68376
  this.fileCache = fileCache;
68138
68377
  this.authFingerprint = authFingerprint;
68139
68378
  this.state = this.loadState();
@@ -68479,7 +68718,7 @@ var SessionFileManager = class {
68479
68718
  const files = fs19.readdirSync(sessionsDir);
68480
68719
  for (const file of files) {
68481
68720
  if (!file.endsWith(".json")) continue;
68482
- const filePath = path19.join(sessionsDir, file);
68721
+ const filePath = path18.join(sessionsDir, file);
68483
68722
  const stat2 = fs19.statSync(filePath);
68484
68723
  if (now - stat2.mtime.getTime() > maxAge) {
68485
68724
  fs19.unlinkSync(filePath);
@@ -68491,7 +68730,7 @@ var SessionFileManager = class {
68491
68730
 
68492
68731
  // src/files/ffmpeg-processor.ts
68493
68732
  import * as fs20 from "node:fs";
68494
- import * as path20 from "node:path";
68733
+ import * as path19 from "node:path";
68495
68734
  import { spawn } from "node:child_process";
68496
68735
  var FFmpegProcessor = class {
68497
68736
  ffmpegPath = "ffmpeg";
@@ -68595,7 +68834,7 @@ var FFmpegProcessor = class {
68595
68834
  }
68596
68835
  async compressVideo(inputPath, outputDir, info, opts, onProgress) {
68597
68836
  const outputName = `compressed_${Date.now()}.mp4`;
68598
- const outputPath = path20.join(outputDir, outputName);
68837
+ const outputPath = path19.join(outputDir, outputName);
68599
68838
  const adaptiveParams = this.calculateAdaptiveParams(info.duration, opts);
68600
68839
  const args = [
68601
68840
  "-i",
@@ -68657,9 +68896,9 @@ var FFmpegProcessor = class {
68657
68896
  }
68658
68897
  async extractKeyframes(inputPath, outputDir, info, opts, onProgress) {
68659
68898
  const interval = Math.floor(info.duration / opts.keyframeCount);
68660
- const framesDir = path20.join(outputDir, `keyframes_${Date.now()}`);
68899
+ const framesDir = path19.join(outputDir, `keyframes_${Date.now()}`);
68661
68900
  fs20.mkdirSync(framesDir, { recursive: true });
68662
- const outputPattern = path20.join(framesDir, "frame_%03d.jpg");
68901
+ const outputPattern = path19.join(framesDir, "frame_%03d.jpg");
68663
68902
  const args = [
68664
68903
  "-i",
68665
68904
  inputPath,
@@ -68677,7 +68916,7 @@ var FFmpegProcessor = class {
68677
68916
  onProgress?.(percent);
68678
68917
  }
68679
68918
  });
68680
- let frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path20.join(framesDir, f));
68919
+ let frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
68681
68920
  if (frames.length < opts.keyframeCount / 2) {
68682
68921
  fs20.rmSync(framesDir, { recursive: true, force: true });
68683
68922
  fs20.mkdirSync(framesDir, { recursive: true });
@@ -68696,7 +68935,7 @@ var FFmpegProcessor = class {
68696
68935
  onProgress?.(percent);
68697
68936
  }
68698
68937
  });
68699
- frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path20.join(framesDir, f));
68938
+ frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
68700
68939
  }
68701
68940
  const totalOutputSize = frames.reduce(
68702
68941
  (sum, f) => sum + fs20.statSync(f).size,
@@ -68720,7 +68959,7 @@ var FFmpegProcessor = class {
68720
68959
  const opts = { ...DEFAULT_PIC_OPTIONS, ...options };
68721
68960
  const ext = opts.format === "webp" ? ".webp" : ".jpg";
68722
68961
  const outputName = `optimized_${Date.now()}${ext}`;
68723
- const outputPath = path20.join(outputDir, outputName);
68962
+ const outputPath = path19.join(outputDir, outputName);
68724
68963
  const args = [
68725
68964
  "-i",
68726
68965
  inputPath,
@@ -68895,8 +69134,8 @@ var DIRECTORY_LISTING_TTL_MS = 1500;
68895
69134
  var FileManager = class {
68896
69135
  constructor(options) {
68897
69136
  this.options = options;
68898
- const baseDir = options.baseDir || path21.join(process.env.HOME || ".", ".pie", "files");
68899
- const sessionsDir = options.sessionsDir || path21.join(process.env.HOME || ".", ".pie", "sessions");
69137
+ const baseDir = options.baseDir || path20.join(process.env.HOME || ".", ".pie", "files");
69138
+ const sessionsDir = options.sessionsDir || path20.join(process.env.HOME || ".", ".pie", "sessions");
68900
69139
  this.fileCache = new FileCache(baseDir);
68901
69140
  this.authFingerprint = options.apiKey ? this.buildAuthFingerprint(options.apiKey) : void 0;
68902
69141
  this.sessionManager = new SessionFileManager(
@@ -68907,7 +69146,7 @@ var FileManager = class {
68907
69146
  );
68908
69147
  this.ffmpeg = new FFmpegProcessor(options.ffmpegPath);
68909
69148
  this.platform = normalizeFileManagerPlatform(options.platform);
68910
- this.tempDir = path21.join(baseDir, "temp");
69149
+ this.tempDir = path20.join(baseDir, "temp");
68911
69150
  this.ensureDirectories();
68912
69151
  }
68913
69152
  options;
@@ -68954,7 +69193,7 @@ var FileManager = class {
68954
69193
  // ============================================================================
68955
69194
  async stageLocal(filePath, options = {}) {
68956
69195
  const resolvedPath = resolvePath2(filePath);
68957
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69196
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68958
69197
  if (!fs21.existsSync(resolvedPath)) {
68959
69198
  throw new FileManagerError(
68960
69199
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -68984,7 +69223,7 @@ var FileManager = class {
68984
69223
  }
68985
69224
  async upload(filePath, options = {}) {
68986
69225
  const resolvedPath = resolvePath2(filePath);
68987
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69226
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68988
69227
  if (!fs21.existsSync(resolvedPath)) {
68989
69228
  throw new FileManagerError(
68990
69229
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -69074,7 +69313,7 @@ var FileManager = class {
69074
69313
  }
69075
69314
  const cachedResult = await this.tryReuseCachedSourceUpload(
69076
69315
  resolvedPath,
69077
- options.sourceName || path21.basename(resolvedPath),
69316
+ options.sourceName || path20.basename(resolvedPath),
69078
69317
  options.platform || this.platform
69079
69318
  );
69080
69319
  if (cachedResult) {
@@ -69091,9 +69330,9 @@ var FileManager = class {
69091
69330
  const result = await this.upload(optimizedPath, {
69092
69331
  ...options,
69093
69332
  purpose: "image",
69094
- sourceName: path21.basename(resolvedPath)
69333
+ sourceName: path20.basename(resolvedPath)
69095
69334
  });
69096
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69335
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69097
69336
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69098
69337
  try {
69099
69338
  fs21.unlinkSync(optimizedPath);
@@ -69114,7 +69353,7 @@ var FileManager = class {
69114
69353
  }
69115
69354
  const cachedResult = await this.tryReuseCachedSourceUpload(
69116
69355
  resolvedPath,
69117
- options.sourceName || path21.basename(resolvedPath),
69356
+ options.sourceName || path20.basename(resolvedPath),
69118
69357
  options.platform || this.platform
69119
69358
  );
69120
69359
  if (cachedResult) {
@@ -69164,9 +69403,9 @@ var FileManager = class {
69164
69403
  result = await this.upload(preprocessResult.output, {
69165
69404
  ...options,
69166
69405
  purpose: "video",
69167
- sourceName: path21.basename(resolvedPath)
69406
+ sourceName: path20.basename(resolvedPath)
69168
69407
  });
69169
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69408
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69170
69409
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69171
69410
  } else {
69172
69411
  throw new FileManagerError(
@@ -69458,7 +69697,7 @@ var FileManager = class {
69458
69697
  * Detect purpose from file path based on extension
69459
69698
  */
69460
69699
  detectPurpose(filePath) {
69461
- const ext = path21.extname(filePath).toLowerCase();
69700
+ const ext = path20.extname(filePath).toLowerCase();
69462
69701
  switch (ext) {
69463
69702
  case ".mp4":
69464
69703
  case ".mov":
@@ -69640,7 +69879,7 @@ var FileManager = class {
69640
69879
  if (!entry.name || entry.name.startsWith(".")) {
69641
69880
  continue;
69642
69881
  }
69643
- const fullPath = path21.join(resolvedDir, entry.name);
69882
+ const fullPath = path20.join(resolvedDir, entry.name);
69644
69883
  if (entry.isDirectory()) {
69645
69884
  listing.directories.push({ name: entry.name, path: fullPath });
69646
69885
  continue;
@@ -69681,7 +69920,7 @@ var FileManager = class {
69681
69920
  return listing;
69682
69921
  }
69683
69922
  invalidateDirectoryListingCacheForPath(filePath) {
69684
- this.directoryListingCache.delete(path21.dirname(resolvePath2(filePath)));
69923
+ this.directoryListingCache.delete(path20.dirname(resolvePath2(filePath)));
69685
69924
  }
69686
69925
  buildAuthFingerprint(apiKey) {
69687
69926
  return crypto2.createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
@@ -69730,8 +69969,8 @@ function createFileManagerForModel(params) {
69730
69969
  sessionId: params.sessionId,
69731
69970
  platform: platform2,
69732
69971
  apiKey: params.apiKey,
69733
- baseDir: path22.join(params.configDir, "files"),
69734
- sessionsDir: path22.join(params.configDir, "sessions")
69972
+ baseDir: path21.join(params.configDir, "files"),
69973
+ sessionsDir: path21.join(params.configDir, "sessions")
69735
69974
  });
69736
69975
  switch (getFileManagerAdapterKind(platform2)) {
69737
69976
  case "kimi":
@@ -69771,7 +70010,7 @@ async function prepareInlineMediaContent(params) {
69771
70010
  const limitBytes = limitMb * 1024 * 1024;
69772
70011
  if (stat2.size > limitBytes) {
69773
70012
  throw new Error(
69774
- `${params.request.kind} understanding for ${params.model.provider}/${params.model.id} supports inline files up to ${limitMb}MB; ${path22.basename(params.request.displayPath)} is ${(stat2.size / 1024 / 1024).toFixed(1)}MB. Configure a model with a file upload adapter, or compress/trim the file before reading it.`
70013
+ `${params.request.kind} understanding for ${params.model.provider}/${params.model.id} supports inline files up to ${limitMb}MB; ${path21.basename(params.request.displayPath)} is ${(stat2.size / 1024 / 1024).toFixed(1)}MB. Configure a model with a file upload adapter, or compress/trim the file before reading it.`
69775
70014
  );
69776
70015
  }
69777
70016
  const data = await fs22.readFile(params.request.absolutePath, "base64");
@@ -69810,7 +70049,7 @@ async function prepareFileContent(configDir, params) {
69810
70049
  });
69811
70050
  if (params.request.kind === "document") {
69812
70051
  const upload2 = await manager.upload(params.request.absolutePath, {
69813
- sourceName: path22.basename(params.request.displayPath),
70052
+ sourceName: path21.basename(params.request.displayPath),
69814
70053
  purpose: "file-extract"
69815
70054
  });
69816
70055
  await waitForReady({ manager, fileId: upload2.fileId, timeoutMs: 12e4, signal: params.request.signal });
@@ -69833,8 +70072,8 @@ async function prepareFileContent(configDir, params) {
69833
70072
  }
69834
70073
  };
69835
70074
  }
69836
- const upload = params.request.kind === "video" ? await manager.uploadVideo(params.request.absolutePath, { sourceName: path22.basename(params.request.displayPath), strategy: "compress" }) : await manager.upload(params.request.absolutePath, {
69837
- sourceName: path22.basename(params.request.displayPath),
70075
+ const upload = params.request.kind === "video" ? await manager.uploadVideo(params.request.absolutePath, { sourceName: path21.basename(params.request.displayPath), strategy: "compress" }) : await manager.upload(params.request.absolutePath, {
70076
+ sourceName: path21.basename(params.request.displayPath),
69838
70077
  purpose: params.request.kind === "image" ? "image" : "file-extract"
69839
70078
  });
69840
70079
  if (params.request.kind !== "video") {
@@ -70004,6 +70243,86 @@ function createCliFileSystemTools(cwd, options) {
70004
70243
  });
70005
70244
  }
70006
70245
 
70246
+ // src/file-access-context.ts
70247
+ import * as path22 from "node:path";
70248
+ function uniqueNormalizedPaths(paths) {
70249
+ const seen = /* @__PURE__ */ new Set();
70250
+ const result = [];
70251
+ for (const entry of paths) {
70252
+ const trimmed = String(entry || "").trim();
70253
+ if (!trimmed) continue;
70254
+ const normalized = path22.normalize(trimmed);
70255
+ if (seen.has(normalized)) continue;
70256
+ seen.add(normalized);
70257
+ result.push(normalized);
70258
+ }
70259
+ return result;
70260
+ }
70261
+ function buildCliFileAccessOptions(params) {
70262
+ return {
70263
+ accessRoots: uniqueNormalizedPaths([
70264
+ resolveCliProjectRoot(params.cwd),
70265
+ params.configDir,
70266
+ ...params.skills.map((skill) => skill.baseDir)
70267
+ ]),
70268
+ allowlistedDirs: params.allowlistedDirs,
70269
+ descriptionAllowlistedDirs: params.descriptionAllowlistedDirs
70270
+ };
70271
+ }
70272
+
70273
+ // src/interactive-filesystem-tools.ts
70274
+ function buildInteractiveFilesystemToolOptions(host, allowlistedDirs) {
70275
+ return {
70276
+ ...buildCliFileAccessOptions({
70277
+ cwd: host.cwd,
70278
+ configDir: host.configDir,
70279
+ skills: host.skills,
70280
+ allowlistedDirs,
70281
+ descriptionAllowlistedDirs: []
70282
+ }),
70283
+ configDir: host.configDir,
70284
+ fileObservationState: host.fileObservationState,
70285
+ getAgentModel: host.getAgentModel,
70286
+ getSessionId: () => host.getActiveSessionId() || void 0,
70287
+ resolveToolModel: host.resolveToolModel,
70288
+ resolveToolModelCandidates: host.resolveToolModelCandidates,
70289
+ getPolicyContext: host.getPlanPolicyContext
70290
+ };
70291
+ }
70292
+
70293
+ // src/runtime/yolo-grant.ts
70294
+ var YoloGrantTracker = class {
70295
+ untilMs = 0;
70296
+ isActive(now = Date.now()) {
70297
+ return this.untilMs > now;
70298
+ }
70299
+ award(grantMs, now = Date.now()) {
70300
+ this.untilMs = now + Math.max(0, grantMs);
70301
+ }
70302
+ expire() {
70303
+ this.untilMs = 0;
70304
+ }
70305
+ /** Expiry timestamp (0 when no grant is held); useful for display. */
70306
+ get until() {
70307
+ return this.untilMs;
70308
+ }
70309
+ };
70310
+ function renderYoloGrantBanner(grantMs, sink) {
70311
+ const minutes = Math.max(1, Math.round(grantMs / 6e4));
70312
+ sink.addChild(new Spacer(1));
70313
+ sink.addChild(
70314
+ new Text(theme.fg("warning", `\u26A0\uFE0F YOLO granted for ${minutes} min (high-risk commands skip confirmation)`), 1, 0)
70315
+ );
70316
+ sink.addChild(new Spacer(1));
70317
+ }
70318
+ function parseYoloGrantMs(options, selected) {
70319
+ const yoloOption = options.find((option) => /^\s*yolo(\s*\(|\s|$)/i.test(option));
70320
+ if (!yoloOption || selected !== yoloOption) return void 0;
70321
+ const minutesMatch = /\(\s*(\d+(?:\.\d+)?)\s*min/i.exec(yoloOption);
70322
+ const minutes = minutesMatch ? Number(minutesMatch[1]) : 5;
70323
+ return Math.round(minutes * 6e4);
70324
+ }
70325
+
70007
70326
  // src/execution/state-manager.ts
70008
70327
  function getTextFromMessageContent(content) {
70009
70328
  if (typeof content === "string") {
@@ -70502,6 +70821,14 @@ function normalizeForComparison(value, flavor) {
70502
70821
  const withoutTrailingSeparators = normalized.length > root.length ? normalized.replace(flavor === "windows" ? /[\\/]+$/ : /\/+$/, "") : normalized;
70503
70822
  return flavor === "windows" ? withoutTrailingSeparators.toLowerCase() : withoutTrailingSeparators;
70504
70823
  }
70824
+ function sessionBelongsToCwd(sessionCwd, currentCwd) {
70825
+ if (typeof sessionCwd !== "string" || sessionCwd.length === 0) return true;
70826
+ if (!currentCwd) return true;
70827
+ const sessionFlavor = getPathFlavor(sessionCwd, currentCwd);
70828
+ const currentFlavor = getPathFlavor(currentCwd, sessionCwd);
70829
+ if (sessionFlavor !== currentFlavor) return false;
70830
+ return normalizeForComparison(sessionCwd, sessionFlavor) === normalizeForComparison(currentCwd, currentFlavor);
70831
+ }
70505
70832
  function formatSessionCwdTag(sessionCwd, currentCwd, homeDir) {
70506
70833
  if (typeof sessionCwd !== "string" || sessionCwd.length === 0 || !currentCwd) return void 0;
70507
70834
  const sessionFlavor = getPathFlavor(sessionCwd, currentCwd);
@@ -70527,31 +70854,47 @@ var SessionSelectorComponent = class extends Container {
70527
70854
  callbacks;
70528
70855
  options;
70529
70856
  items;
70857
+ /** All sessions ever handed to the selector; the visible list is the current scope's filter. */
70858
+ allSessions;
70859
+ globalScope;
70530
70860
  refreshAttempted = /* @__PURE__ */ new Set();
70531
70861
  refreshInFlight = false;
70532
70862
  refreshQueue = [];
70533
70863
  refreshQueued = /* @__PURE__ */ new Set();
70534
70864
  refreshTimer;
70535
70865
  destroyed = false;
70866
+ titleText;
70867
+ hintText;
70536
70868
  constructor(sessions, callbacks, options = {}) {
70537
70869
  super();
70538
70870
  this.sessions = sessions;
70871
+ this.allSessions = sessions;
70872
+ this.globalScope = options.globalScope ?? false;
70539
70873
  this.callbacks = callbacks;
70540
70874
  this.options = options;
70541
- const truncated = options.totalCount !== void 0 && options.totalCount > sessions.length;
70542
- const title = truncated ? `Resume Session (recent ${sessions.length} of ${options.totalCount})` : "Resume Session";
70543
- this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
70875
+ const scoped = this.globalScope ? sessions : sessions.filter((session) => sessionBelongsToCwd(session.cwd, options.currentCwd));
70876
+ this.sessions = scoped;
70877
+ const title = this.isWindowTruncated() ? `Resume Session (recent ${scoped.length} of ${options.totalCount})` : "Resume Session";
70878
+ this.titleText = new Text(theme.bold(theme.fg("accent", title)), 0, 0);
70879
+ this.addChild(this.titleText);
70544
70880
  this.addChild(new Spacer(1));
70545
- this.items = sessions.map((session) => this.createItem(session));
70881
+ this.items = scoped.map((session) => this.createItem(session));
70546
70882
  this.selectList = new SelectList(
70547
70883
  this.items,
70548
- Math.min(this.items.length, 8),
70884
+ 8,
70549
70885
  {
70550
70886
  selectedPrefix: (text) => theme.fg("accent", text),
70551
70887
  selectedText: (text) => theme.fg("accent", text),
70552
70888
  description: (text) => theme.fg("muted", text),
70553
70889
  scrollInfo: (text) => theme.fg("dim", text),
70554
- noMatch: (text) => theme.fg("dim", text)
70890
+ // The noMatch hook carries the empty-directory-scope copy (SelectList
70891
+ // hardcodes "No matching commands", which is wrong for sessions).
70892
+ // When the loaded list is a truncated recent window, `g` cannot show
70893
+ // this directory's older sessions either — promise nothing it can't do.
70894
+ noMatch: () => theme.fg(
70895
+ "dim",
70896
+ this.options.totalCount !== void 0 && this.options.totalCount > this.allSessions.length ? " No recent sessions in this directory" : " No sessions in this directory \xB7 g shows all sessions"
70897
+ )
70555
70898
  }
70556
70899
  );
70557
70900
  this.selectList.onSelect = (item) => {
@@ -70569,14 +70912,49 @@ var SessionSelectorComponent = class extends Container {
70569
70912
  };
70570
70913
  this.addChild(this.selectList);
70571
70914
  this.addChild(new Spacer(1));
70572
- this.addChild(
70573
- new Text(theme.fg("dim", " \u2191\u2193 to navigate \xB7 \u2190\u2192 to page \xB7 Enter to select \xB7 Esc to cancel"), 0, 0)
70574
- );
70915
+ this.hintText = new Text(theme.fg("dim", this.buildHintLine()), 0, 0);
70916
+ this.addChild(this.hintText);
70575
70917
  this.scheduleVisibleRefreshes();
70576
70918
  }
70919
+ /** True when the visible list is the unfiltered window AND that window is
70920
+ * truncated vs the disk total (only then does "recent N of M" describe it). */
70921
+ isWindowTruncated() {
70922
+ return this.sessions.length === this.allSessions.length && this.options.totalCount !== void 0 && this.options.totalCount > this.allSessions.length;
70923
+ }
70924
+ /** Shared count expression so the title and the `g` hint never disagree. */
70925
+ scopeCountText(count) {
70926
+ return this.options.totalCount !== void 0 && this.options.totalCount > count ? `recent ${count} of ${this.options.totalCount}` : `all ${count}`;
70927
+ }
70928
+ buildHintLine() {
70929
+ const scopeNote = this.globalScope ? `${this.scopeCountText(this.allSessions.length)} sessions` : `${this.sessions.length} in this directory`;
70930
+ return ` \u2191\u2193 navigate \xB7 \u2190\u2192 page \xB7 g global (${scopeNote}) \xB7 Enter select \xB7 Esc cancel`;
70931
+ }
70577
70932
  handleInput(data) {
70933
+ if (this.destroyed) return;
70934
+ if (data === "g" || data === "G") {
70935
+ this.toggleScope();
70936
+ return;
70937
+ }
70578
70938
  this.selectList.handleInput(data);
70579
70939
  }
70940
+ /** Toggle between current-directory sessions and the full loaded recent list. */
70941
+ toggleScope() {
70942
+ if (this.destroyed) return;
70943
+ this.globalScope = !this.globalScope;
70944
+ this.sessions = this.globalScope ? this.allSessions : this.allSessions.filter((session) => sessionBelongsToCwd(session.cwd, this.options.currentCwd));
70945
+ this.items = this.sessions.map((session) => this.createItem(session));
70946
+ this.selectList.setItems(this.items);
70947
+ this.updateTitle();
70948
+ this.updateHint();
70949
+ this.scheduleVisibleRefreshes();
70950
+ }
70951
+ updateTitle() {
70952
+ const windowText = this.isWindowTruncated() ? ` (recent ${this.sessions.length} of ${this.options.totalCount})` : "";
70953
+ this.titleText.setText(theme.bold(theme.fg("accent", `Resume Session${windowText}`)));
70954
+ }
70955
+ updateHint() {
70956
+ this.hintText.setText(theme.fg("dim", this.buildHintLine()));
70957
+ }
70580
70958
  getSelectList() {
70581
70959
  return this.selectList;
70582
70960
  }
@@ -70584,10 +70962,11 @@ var SessionSelectorComponent = class extends Container {
70584
70962
  const cwdTag = formatSessionCwdTag(session.cwd, this.options.currentCwd, this.options.homeDir);
70585
70963
  const unknownTag = cwdTag === void 0 && this.options.currentCwd && !(typeof session.cwd === "string" && session.cwd.length > 0) ? "unknown path" : void 0;
70586
70964
  const tag = cwdTag ?? unknownTag;
70965
+ const meta = `${session.id} \xB7 ${session.messageCount} messages \xB7 ${formatDate(session.updatedAt)}`;
70587
70966
  return {
70588
70967
  value: session.id,
70589
70968
  label: session.name || `Session ${session.id.slice(0, 8)}`,
70590
- description: tag ? `${tag} \xB7 ${session.messageCount} messages \xB7 ${formatDate(session.updatedAt)}` : `${session.messageCount} messages \xB7 ${formatDate(session.updatedAt)}`
70969
+ description: tag ? `${tag} \xB7 ${meta}` : meta
70591
70970
  };
70592
70971
  }
70593
70972
  clearRefreshTimer() {
@@ -70634,6 +71013,8 @@ var SessionSelectorComponent = class extends Container {
70634
71013
  updateItem(metadata) {
70635
71014
  const sessionIndex = this.sessions.findIndex((session) => session.id === metadata.id);
70636
71015
  if (sessionIndex >= 0) this.sessions[sessionIndex] = metadata;
71016
+ const allIndex = this.allSessions.findIndex((session) => session.id === metadata.id);
71017
+ if (allIndex >= 0) this.allSessions[allIndex] = metadata;
70637
71018
  const itemIndex = this.items.findIndex((item2) => item2.value === metadata.id);
70638
71019
  if (itemIndex < 0) return;
70639
71020
  const nextItem = this.createItem(metadata);
@@ -72622,8 +73003,8 @@ async function handleResumeCommand(host) {
72622
73003
  host.showSelector((done) => {
72623
73004
  const selector = new SessionSelectorComponent(sessions, {
72624
73005
  onSelect: async (sessionId) => {
72625
- await host.loadSession(sessionId);
72626
73006
  done();
73007
+ await host.loadSession(sessionId);
72627
73008
  },
72628
73009
  onCancel: () => {
72629
73010
  done();
@@ -72635,7 +73016,7 @@ async function handleResumeCommand(host) {
72635
73016
  requestRender: () => host.ui.requestRender(),
72636
73017
  totalCount
72637
73018
  });
72638
- return { component: selector, focus: selector.getSelectList() };
73019
+ return { component: selector, focus: selector };
72639
73020
  });
72640
73021
  }
72641
73022
  async function loadSession(host, sessionId) {
@@ -76373,6 +76754,7 @@ function cleanupAndExit(host) {
76373
76754
  exitingHosts.add(host);
76374
76755
  console.log("\n[Cleanup] Stopping file status monitor...");
76375
76756
  const shutdown = shutdownInteractiveHost(host);
76757
+ const sessionId = host.sessionManager?.getActiveSessionId();
76376
76758
  void (async () => {
76377
76759
  try {
76378
76760
  await shutdown;
@@ -76380,7 +76762,7 @@ function cleanupAndExit(host) {
76380
76762
  console.error("Interactive shutdown failed:", error);
76381
76763
  process.exitCode = 1;
76382
76764
  }
76383
- console.log("[Cleanup] Goodbye!");
76765
+ console.log(`[Cleanup] Goodbye!${sessionId ? ` session: ${sessionId}` : ""}`);
76384
76766
  process.exit(process.exitCode || 0);
76385
76767
  })();
76386
76768
  }
@@ -77002,35 +77384,40 @@ var RuntimeModeBlockInjector = class {
77002
77384
  // src/runtime/host-envelope-registry.ts
77003
77385
  var sharedCliEnvelopeRegistry;
77004
77386
  function cliHostEnvelopeRegistry() {
77005
- if (sharedCliEnvelopeRegistry) return sharedCliEnvelopeRegistry;
77006
- sharedCliEnvelopeRegistry = createHostEnvelopeProjectorRegistry();
77387
+ return sharedCliEnvelopeRegistry ??= createCliHostEnvelopeRegistry();
77388
+ }
77389
+ function createCliHostEnvelopeRegistry() {
77390
+ const registry = createHostEnvelopeProjectorRegistry();
77007
77391
  for (const { kind, version } of runtimeModeEnvelopeRegistry().listPins()) {
77008
- sharedCliEnvelopeRegistry.register({
77392
+ registry.register({
77009
77393
  kind,
77010
77394
  version,
77011
77395
  project: (data) => runtimeModeEnvelopeRegistry().resolve(kind, version).project(data)
77012
77396
  });
77013
77397
  }
77014
- return sharedCliEnvelopeRegistry;
77398
+ return registry;
77015
77399
  }
77016
- function registerExternalHostEnvelopeProjector(projector) {
77017
- cliHostEnvelopeRegistry().register(projector);
77400
+ function registerExternalHostEnvelopeProjector(projector, registry = cliHostEnvelopeRegistry()) {
77401
+ registry.register(projector);
77018
77402
  return () => {
77019
77403
  };
77020
77404
  }
77021
77405
  function resetCliHostEnvelopeRegistry() {
77022
77406
  sharedCliEnvelopeRegistry = void 0;
77023
77407
  }
77024
- function cliHostEnvelopeKinds() {
77025
- return cliHostEnvelopeRegistry().listPins().map(({ kind, version }) => `${kind}@${version}`).sort();
77408
+ function cliHostEnvelopeKinds(registry = cliHostEnvelopeRegistry()) {
77409
+ return registry.listPins().map(({ kind, version }) => `${kind}@${version}`).sort();
77410
+ }
77411
+ function projectCliHostEnvelopes(messages, onProjection, registry = cliHostEnvelopeRegistry()) {
77412
+ return projectHostEnvelopes(messages, registry, onProjection);
77026
77413
  }
77027
- function projectCliHostEnvelopes(messages, onProjection) {
77028
- return projectHostEnvelopes(messages, cliHostEnvelopeRegistry(), onProjection);
77414
+ function cliCompactionModelView(message) {
77415
+ return projectCliHostEnvelopes([message])[0];
77029
77416
  }
77030
77417
  function isIntakeHostContextError(value) {
77031
77418
  return value.code !== void 0;
77032
77419
  }
77033
- function intakeHostContextBlock(value) {
77420
+ function intakeHostContextBlock(value, registry = cliHostEnvelopeRegistry()) {
77034
77421
  if (!isHostContextBlock(value)) {
77035
77422
  return {
77036
77423
  ok: false,
@@ -77039,12 +77426,12 @@ function intakeHostContextBlock(value) {
77039
77426
  };
77040
77427
  }
77041
77428
  const block = value;
77042
- const projector = cliHostEnvelopeRegistry().resolve(block.kind, block.version);
77429
+ const projector = registry.resolve(block.kind, block.version);
77043
77430
  if (!projector) {
77044
77431
  return {
77045
77432
  ok: false,
77046
77433
  code: "host_context_unknown_projector",
77047
- message: `no projector for ${block.kind}@${block.version}; this build supports: ${cliHostEnvelopeKinds().join(", ")}`
77434
+ message: `no projector for ${block.kind}@${block.version}; this build supports: ${cliHostEnvelopeKinds(registry).join(", ")}`
77048
77435
  };
77049
77436
  }
77050
77437
  try {
@@ -77059,8 +77446,8 @@ function intakeHostContextBlock(value) {
77059
77446
  }
77060
77447
  return { ok: true, block };
77061
77448
  }
77062
- function intakeHostContextFailure(value) {
77063
- const result = intakeHostContextBlock(value);
77449
+ function intakeHostContextFailure(value, registry = cliHostEnvelopeRegistry()) {
77450
+ const result = intakeHostContextBlock(value, registry);
77064
77451
  return isIntakeHostContextError(result) ? { code: result.code, message: result.message } : void 0;
77065
77452
  }
77066
77453
 
@@ -77092,13 +77479,14 @@ async function handleCompactCommand(host) {
77092
77479
  const preparation = prepareSessionCompaction({
77093
77480
  sessionManager: host.sessionManager,
77094
77481
  messages: existingMessages,
77482
+ modelView: cliCompactionModelView,
77095
77483
  budgetTokens: host.options.model?.contextWindow
77096
77484
  });
77097
77485
  if (preparation.entries.length === 0) {
77098
77486
  host.showStatus("Need at least one real user message before compacting");
77099
77487
  return;
77100
77488
  }
77101
- const tokensBefore = calculateTotalTokens(preparation.messages);
77489
+ const tokensBefore = preparation.inputTokens;
77102
77490
  const compactOptions = {
77103
77491
  model: host.options.model,
77104
77492
  apiKey: host.options.apiKey,
@@ -78316,6 +78704,7 @@ var FileStatusMonitor = class {
78316
78704
 
78317
78705
  // src/files/mention/mention-handler.ts
78318
78706
  import * as fs26 from "node:fs";
78707
+ import { randomUUID } from "node:crypto";
78319
78708
  import * as path28 from "node:path";
78320
78709
  var MentionHandler = class {
78321
78710
  fileManager;
@@ -78409,75 +78798,11 @@ var MentionHandler = class {
78409
78798
  this.onInsertMention(mention, queryToken);
78410
78799
  this.onStatus?.(`Added ${option.name} (already uploaded to ${this.fileManager.getPlatformDisplayName()})`);
78411
78800
  } else if (option.type === "upload" && option.path) {
78412
- this.pendingUploadCount += 1;
78413
- const pendingMention = this.formatPendingMention(option.name);
78414
- this.onInsertMention(pendingMention, queryToken);
78415
- const pendingStatusKey = `${option.path}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
78416
- try {
78417
- if (option.cacheStatus === "uploaded") {
78418
- this.pushPendingStatus(
78419
- pendingStatusKey,
78420
- `Preparing ${option.name} (reusing upload from ${this.fileManager.getPlatformDisplayName()})...`
78421
- );
78422
- } else if (option.cacheStatus === "cached") {
78423
- this.pushPendingStatus(
78424
- pendingStatusKey,
78425
- `Preparing ${option.name} (reusing local cache)...`
78426
- );
78427
- } else {
78428
- this.pushPendingStatus(pendingStatusKey, `Preparing ${option.name}...`);
78429
- }
78430
- const mimeType = FileCache.getMimeType(option.path);
78431
- const isMedia = mimeType.startsWith("image/") || mimeType.startsWith("video/");
78432
- const shouldStageLocally = !isMedia || !this.fileManager.hasAdapter();
78433
- if (isMedia && shouldStageLocally) {
78434
- this.pushPendingStatus(pendingStatusKey, `Staging ${option.name} locally (no upload API for this provider)...`);
78435
- }
78436
- const onProgress = (progress, stage) => {
78437
- this.pushPendingStatus(
78438
- pendingStatusKey,
78439
- this.formatUploadProgress(option.name, progress, stage)
78440
- );
78441
- };
78442
- const result = shouldStageLocally ? await this.fileManager.stageLocal(option.path, { sourceName: option.name }) : mimeType.startsWith("video/") ? await this.fileManager.uploadVideo(option.path, {
78443
- sourceName: option.name,
78444
- onProgress
78445
- }) : mimeType.startsWith("image/") ? await this.fileManager.uploadPic(option.path, {
78446
- sourceName: option.name,
78447
- onProgress
78448
- }) : await this.fileManager.upload(option.path, {
78449
- sourceName: option.name,
78450
- onProgress
78451
- });
78452
- if (result.success) {
78453
- const mention = this.formatMention(result.name, result.fileId);
78454
- this.onInsertMention(mention, pendingMention);
78455
- this.clearPendingStatus(pendingStatusKey);
78456
- this.onStatus?.(shouldStageLocally ? `Added local file ${result.name}` : `Added ${result.name}`);
78457
- } else {
78458
- this.onInsertMention("", pendingMention);
78459
- this.clearPendingStatus(pendingStatusKey);
78460
- this.onError?.("File upload failed. Please try again.");
78461
- }
78462
- } catch (err) {
78463
- this.onInsertMention("", pendingMention);
78464
- this.clearPendingStatus(pendingStatusKey);
78465
- const errorMessage4 = err instanceof Error ? err.message : String(err);
78466
- if (errorMessage4.includes("PLATFORM_NOT_SUPPORTED")) {
78467
- this.onError?.("The current AI model does not support file uploads.");
78468
- } else if (errorMessage4.includes("ENOENT")) {
78469
- this.onError?.("File does not exist or cannot be accessed.");
78470
- } else if (errorMessage4.includes("EACCES") || errorMessage4.includes("PERMISSION")) {
78471
- this.onError?.("Permission denied while accessing the file.");
78472
- } else if (errorMessage4.includes("network") || errorMessage4.includes("ETIMEDOUT") || errorMessage4.includes("ECONNREFUSED")) {
78473
- this.onError?.("Network error. Check the connection and try again.");
78474
- } else {
78475
- this.onError?.(`Upload failed: ${errorMessage4}`);
78476
- }
78477
- } finally {
78478
- this.clearPendingStatus(pendingStatusKey);
78479
- this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78480
- }
78801
+ await this.attachFileFromPath(option.path, {
78802
+ sourceName: option.name,
78803
+ queryToken,
78804
+ cacheStatus: option.cacheStatus
78805
+ });
78481
78806
  } else {
78482
78807
  this.onError?.("Invalid file option.");
78483
78808
  }
@@ -78485,6 +78810,110 @@ var MentionHandler = class {
78485
78810
  this.onHidePicker();
78486
78811
  this.isPickerVisible = false;
78487
78812
  }
78813
+ /**
78814
+ * Attach a local file from disk without going through the mention picker.
78815
+ *
78816
+ * Used by the clipboard image paste flow: the file goes through the exact
78817
+ * same pending-mention → stage/upload → final-mention lifecycle as a
78818
+ * picker-selected file, so submit-time queueing and attachment resolution
78819
+ * behave identically.
78820
+ */
78821
+ async attachLocalFile(path34, displayName, pendingMention, isCurrent) {
78822
+ await this.attachFileFromPath(path34, { sourceName: displayName, queryToken: pendingMention, isCurrent });
78823
+ }
78824
+ /** Reserve a real queue-replaceable token before asynchronous clipboard IO. */
78825
+ beginPendingAttachment(name) {
78826
+ const token = `@[${name}](pending:clipboard-${randomUUID()})`;
78827
+ this.pendingUploadCount++;
78828
+ this.onInsertMention(`${token} `);
78829
+ let finished = false;
78830
+ return { token, finish: () => {
78831
+ if (finished) return;
78832
+ finished = true;
78833
+ this.onInsertMention("", token);
78834
+ this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78835
+ } };
78836
+ }
78837
+ /**
78838
+ * Shared core of "attach a file from a local path": insert a pending
78839
+ * mention, stage or upload the file, then replace the pending mention with
78840
+ * the final one (or remove it on failure).
78841
+ */
78842
+ async attachFileFromPath(path34, options) {
78843
+ const sourceName = options.sourceName ?? path34.split(/[\\/]/).pop() ?? path34;
78844
+ this.pendingUploadCount += 1;
78845
+ const pendingMention = options.queryToken?.includes("pending:clipboard-") ? options.queryToken : this.formatPendingMention(sourceName);
78846
+ this.onInsertMention(pendingMention, options.queryToken);
78847
+ const pendingStatusKey = `${path34}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
78848
+ try {
78849
+ if (options.cacheStatus === "uploaded") {
78850
+ this.pushPendingStatus(
78851
+ pendingStatusKey,
78852
+ `Preparing ${sourceName} (reusing upload from ${this.fileManager.getPlatformDisplayName()})...`
78853
+ );
78854
+ } else if (options.cacheStatus === "cached") {
78855
+ this.pushPendingStatus(
78856
+ pendingStatusKey,
78857
+ `Preparing ${sourceName} (reusing local cache)...`
78858
+ );
78859
+ } else {
78860
+ this.pushPendingStatus(pendingStatusKey, `Preparing ${sourceName}...`);
78861
+ }
78862
+ const mimeType = FileCache.getMimeType(path34);
78863
+ const isMedia = mimeType.startsWith("image/") || mimeType.startsWith("video/");
78864
+ const shouldStageLocally = !isMedia || !this.fileManager.hasAdapter();
78865
+ if (isMedia && shouldStageLocally) {
78866
+ this.pushPendingStatus(pendingStatusKey, `Staging ${sourceName} locally (no upload API for this provider)...`);
78867
+ }
78868
+ const onProgress = (progress, stage) => {
78869
+ if (options.isCurrent && !options.isCurrent()) return;
78870
+ this.pushPendingStatus(
78871
+ pendingStatusKey,
78872
+ this.formatUploadProgress(sourceName, progress, stage)
78873
+ );
78874
+ };
78875
+ const result = shouldStageLocally ? await this.fileManager.stageLocal(path34, { sourceName }) : mimeType.startsWith("video/") ? await this.fileManager.uploadVideo(path34, {
78876
+ sourceName,
78877
+ onProgress
78878
+ }) : mimeType.startsWith("image/") ? await this.fileManager.uploadPic(path34, {
78879
+ sourceName,
78880
+ onProgress
78881
+ }) : await this.fileManager.upload(path34, {
78882
+ sourceName,
78883
+ onProgress
78884
+ });
78885
+ if (options.isCurrent && !options.isCurrent()) return;
78886
+ if (result.success) {
78887
+ const mention = this.formatMention(result.name, result.fileId);
78888
+ this.onInsertMention(mention, pendingMention);
78889
+ this.clearPendingStatus(pendingStatusKey);
78890
+ this.onStatus?.(shouldStageLocally ? `Added local file ${result.name}` : `Added ${result.name}`);
78891
+ } else {
78892
+ this.onInsertMention("", pendingMention);
78893
+ this.clearPendingStatus(pendingStatusKey);
78894
+ this.onError?.("File upload failed. Please try again.");
78895
+ }
78896
+ } catch (err) {
78897
+ if (options.isCurrent && !options.isCurrent()) return;
78898
+ this.onInsertMention("", pendingMention);
78899
+ this.clearPendingStatus(pendingStatusKey);
78900
+ const errorMessage5 = err instanceof Error ? err.message : String(err);
78901
+ if (errorMessage5.includes("PLATFORM_NOT_SUPPORTED")) {
78902
+ this.onError?.("The current AI model does not support file uploads.");
78903
+ } else if (errorMessage5.includes("ENOENT")) {
78904
+ this.onError?.("File does not exist or cannot be accessed.");
78905
+ } else if (errorMessage5.includes("EACCES") || errorMessage5.includes("PERMISSION")) {
78906
+ this.onError?.("Permission denied while accessing the file.");
78907
+ } else if (errorMessage5.includes("network") || errorMessage5.includes("ETIMEDOUT") || errorMessage5.includes("ECONNREFUSED")) {
78908
+ this.onError?.("Network error. Check the connection and try again.");
78909
+ } else {
78910
+ this.onError?.(`Upload failed: ${errorMessage5}`);
78911
+ }
78912
+ } finally {
78913
+ this.clearPendingStatus(pendingStatusKey);
78914
+ this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78915
+ }
78916
+ }
78488
78917
  /**
78489
78918
  * Parse mentions in text and convert to platform format
78490
78919
  */
@@ -79224,6 +79653,10 @@ function setupInteractiveKeybindings(host) {
79224
79653
  host.handleInterrupt();
79225
79654
  return { consume: true };
79226
79655
  }
79656
+ if (!host.mentionPickerComponent && host.keybindings.matches(data, "pasteImage")) {
79657
+ host.handlePasteImage();
79658
+ return { consume: true };
79659
+ }
79227
79660
  if (host.keybindings.matches(data, "expandTools")) {
79228
79661
  host.toggleToolsExpansion();
79229
79662
  return { consume: true };
@@ -79244,6 +79677,473 @@ function setupInteractiveKeybindings(host) {
79244
79677
  });
79245
79678
  }
79246
79679
 
79680
+ // src/clipboard/read-clipboard-image.ts
79681
+ import { spawn as spawn2 } from "node:child_process";
79682
+ import { randomBytes as randomBytes2 } from "node:crypto";
79683
+ import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync12, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "node:fs";
79684
+ import { tmpdir } from "node:os";
79685
+ import { join as join20 } from "node:path";
79686
+ var MAX_IMAGE_BYTES = 50 * 1024 * 1024;
79687
+ var COMMAND_TIMEOUT_MS = 1e4;
79688
+ function isSpawnFailure(outcome) {
79689
+ return !outcome.ok;
79690
+ }
79691
+ function defaultSpawnFn(file, args, spawnImpl = spawn2, maxBytes = MAX_IMAGE_BYTES, timeoutMs = COMMAND_TIMEOUT_MS) {
79692
+ return new Promise((resolve7) => {
79693
+ let child;
79694
+ try {
79695
+ child = spawnImpl(file, args, {
79696
+ stdio: ["ignore", "pipe", "pipe"],
79697
+ windowsHide: true
79698
+ });
79699
+ } catch (err) {
79700
+ resolve7({ ok: false, error: "spawn-failed", message: err instanceof Error ? err.message : String(err) });
79701
+ return;
79702
+ }
79703
+ let settled = false;
79704
+ const stdoutChunks = [];
79705
+ let stderrText = "";
79706
+ let stdoutBytes = 0;
79707
+ const timer = setTimeout(() => {
79708
+ if (settled) return;
79709
+ settled = true;
79710
+ try {
79711
+ child.kill("SIGKILL");
79712
+ } catch {
79713
+ }
79714
+ resolve7({ ok: false, error: "timeout" });
79715
+ }, timeoutMs);
79716
+ child.on("error", (err) => {
79717
+ if (settled) return;
79718
+ settled = true;
79719
+ clearTimeout(timer);
79720
+ resolve7({ ok: false, error: err.code === "ENOENT" ? "not-found" : "spawn-failed", message: err.message });
79721
+ });
79722
+ child.stdout?.on("data", (chunk) => {
79723
+ if (settled) return;
79724
+ if (chunk.length > maxBytes - stdoutBytes) {
79725
+ settled = true;
79726
+ clearTimeout(timer);
79727
+ stdoutChunks.length = 0;
79728
+ resolve7({ ok: false, error: "too-large" });
79729
+ try {
79730
+ child.kill("SIGKILL");
79731
+ } catch {
79732
+ }
79733
+ return;
79734
+ }
79735
+ stdoutBytes += chunk.length;
79736
+ stdoutChunks.push(chunk);
79737
+ });
79738
+ child.stderr?.on("data", (chunk) => {
79739
+ if (!settled && stderrText.length < 4e3) stderrText += chunk.subarray(0, 4e3 - stderrText.length).toString("utf8");
79740
+ });
79741
+ child.on("close", (code) => {
79742
+ if (settled) return;
79743
+ settled = true;
79744
+ clearTimeout(timer);
79745
+ resolve7({ ok: true, result: { code: code ?? -1, stdout: Buffer.concat(stdoutChunks, stdoutBytes), stderr: stderrText } });
79746
+ });
79747
+ });
79748
+ }
79749
+ function defaultMkTempPath(ext) {
79750
+ return join20(tmpdir(), `pie-clipboard-${randomBytes2(6).toString("hex")}${ext}`);
79751
+ }
79752
+ async function readClipboardImage(deps = {}) {
79753
+ const platform2 = deps.platform ?? process.platform;
79754
+ const env = deps.env ?? process.env;
79755
+ const spawnImpl = deps.spawnImpl ?? defaultSpawnFn;
79756
+ const mkTempPath = deps.mkTempPath ?? defaultMkTempPath;
79757
+ switch (platform2) {
79758
+ case "darwin":
79759
+ return readMacClipboard(spawnImpl, mkTempPath);
79760
+ case "win32":
79761
+ return readWindowsClipboard(spawnImpl, mkTempPath);
79762
+ case "linux":
79763
+ case "freebsd":
79764
+ case "openbsd":
79765
+ case "netbsd":
79766
+ return readLinuxClipboard(spawnImpl, mkTempPath, env);
79767
+ default:
79768
+ return {
79769
+ ok: false,
79770
+ kind: "unsupported-platform",
79771
+ message: `Clipboard image paste is not supported on ${platform2}.`
79772
+ };
79773
+ }
79774
+ }
79775
+ function escapeAppleScriptString(value) {
79776
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
79777
+ }
79778
+ async function readMacClipboard(spawnImpl, mkTempPath) {
79779
+ const targetPath = mkTempPath(".png");
79780
+ const outcome = await spawnImpl("osascript", [
79781
+ "-e",
79782
+ "set pngData to (the clipboard as \xABclass PNGf\xBB)",
79783
+ "-e",
79784
+ `set fp to open for access POSIX file "${escapeAppleScriptString(targetPath)}" with write permission`,
79785
+ "-e",
79786
+ "write pngData to fp",
79787
+ "-e",
79788
+ "close access fp"
79789
+ ]);
79790
+ if (isSpawnFailure(outcome)) {
79791
+ return spawnFailure(outcome, "osascript");
79792
+ }
79793
+ if (outcome.result.code !== 0) {
79794
+ const stderr = outcome.result.stderr.trim();
79795
+ if (/PNGf|Can't|can’t/i.test(stderr)) {
79796
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79797
+ }
79798
+ return {
79799
+ ok: false,
79800
+ kind: "failed",
79801
+ message: `osascript failed: ${truncate(stderr) || `exit ${outcome.result.code}`}`
79802
+ };
79803
+ }
79804
+ return finalizeTempImage(targetPath, "image/png");
79805
+ }
79806
+ function escapePowerShellSingleQuoted(value) {
79807
+ return value.replace(/'/g, "''");
79808
+ }
79809
+ async function readWindowsClipboard(spawnImpl, mkTempPath) {
79810
+ const targetPath = mkTempPath(".png");
79811
+ const script = [
79812
+ "Add-Type -AssemblyName System.Windows.Forms",
79813
+ "Add-Type -AssemblyName System.Drawing",
79814
+ "$img = [System.Windows.Forms.Clipboard]::GetImage()",
79815
+ "if ($null -eq $img) { exit 2 }",
79816
+ `$img.Save('${escapePowerShellSingleQuoted(targetPath)}', [System.Drawing.Imaging.ImageFormat]::Png)`,
79817
+ "exit 0"
79818
+ ].join("\n");
79819
+ const outcome = await spawnImpl("powershell", [
79820
+ "-NoProfile",
79821
+ "-NonInteractive",
79822
+ "-STA",
79823
+ "-Command",
79824
+ script
79825
+ ]);
79826
+ if (isSpawnFailure(outcome)) {
79827
+ return spawnFailure(outcome, "powershell");
79828
+ }
79829
+ if (outcome.result.code === 2) {
79830
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79831
+ }
79832
+ if (outcome.result.code !== 0) {
79833
+ return {
79834
+ ok: false,
79835
+ kind: "failed",
79836
+ message: `powershell failed: ${truncate(outcome.result.stderr.trim()) || `exit ${outcome.result.code}`}`
79837
+ };
79838
+ }
79839
+ return finalizeTempImage(targetPath, "image/png");
79840
+ }
79841
+ var PREFERRED_IMAGE_MIMES = ["image/png", "image/jpeg"];
79842
+ function extensionForMime(mimeType) {
79843
+ switch (mimeType) {
79844
+ case "image/png":
79845
+ return ".png";
79846
+ case "image/jpeg":
79847
+ return ".jpg";
79848
+ case "image/gif":
79849
+ return ".gif";
79850
+ case "image/webp":
79851
+ return ".webp";
79852
+ case "image/bmp":
79853
+ return ".bmp";
79854
+ default:
79855
+ return ".png";
79856
+ }
79857
+ }
79858
+ function pickImageMime(advertised) {
79859
+ for (const mime of PREFERRED_IMAGE_MIMES) {
79860
+ if (advertised.includes(mime)) return mime;
79861
+ }
79862
+ return advertised.find((mime) => mime.startsWith("image/"));
79863
+ }
79864
+ async function readLinuxClipboard(spawnImpl, mkTempPath, env) {
79865
+ if (env.WAYLAND_DISPLAY) {
79866
+ return readWaylandClipboard(spawnImpl, mkTempPath);
79867
+ }
79868
+ if (env.DISPLAY) {
79869
+ return readX11Clipboard(spawnImpl, mkTempPath);
79870
+ }
79871
+ return {
79872
+ ok: false,
79873
+ kind: "failed",
79874
+ message: "No graphical session detected (neither WAYLAND_DISPLAY nor DISPLAY is set)."
79875
+ };
79876
+ }
79877
+ async function readWaylandClipboard(spawnImpl, mkTempPath) {
79878
+ const listOutcome = await spawnImpl("wl-paste", ["--list-types"]);
79879
+ if (isSpawnFailure(listOutcome)) {
79880
+ return spawnFailure(listOutcome, "wl-paste");
79881
+ }
79882
+ if (listOutcome.result.code !== 0) {
79883
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79884
+ }
79885
+ const advertised = listOutcome.result.stdout.toString("utf8").split("\n").map((line) => line.trim()).filter(Boolean);
79886
+ const mime = pickImageMime(advertised);
79887
+ if (!mime) {
79888
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79889
+ }
79890
+ const dataOutcome = await spawnImpl("wl-paste", ["--type", mime]);
79891
+ if (isSpawnFailure(dataOutcome)) {
79892
+ return spawnFailure(dataOutcome, "wl-paste");
79893
+ }
79894
+ if (dataOutcome.result.code !== 0) {
79895
+ return {
79896
+ ok: false,
79897
+ kind: "failed",
79898
+ message: `wl-paste failed: ${truncate(dataOutcome.result.stderr.trim()) || `exit ${dataOutcome.result.code}`}`
79899
+ };
79900
+ }
79901
+ return writeImageBytes(dataOutcome.result.stdout, mime, mkTempPath);
79902
+ }
79903
+ async function readX11Clipboard(spawnImpl, mkTempPath) {
79904
+ const listOutcome = await spawnImpl("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"]);
79905
+ if (isSpawnFailure(listOutcome)) {
79906
+ return spawnFailure(listOutcome, "xclip");
79907
+ }
79908
+ if (listOutcome.result.code !== 0) {
79909
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79910
+ }
79911
+ const advertised = listOutcome.result.stdout.toString("utf8").split("\n").map((line) => line.trim()).filter(Boolean);
79912
+ const mime = pickImageMime(advertised);
79913
+ if (!mime) {
79914
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79915
+ }
79916
+ const dataOutcome = await spawnImpl("xclip", ["-selection", "clipboard", "-t", mime, "-o"]);
79917
+ if (isSpawnFailure(dataOutcome)) {
79918
+ return spawnFailure(dataOutcome, "xclip");
79919
+ }
79920
+ if (dataOutcome.result.code !== 0) {
79921
+ return {
79922
+ ok: false,
79923
+ kind: "failed",
79924
+ message: `xclip failed: ${truncate(dataOutcome.result.stderr.trim()) || `exit ${dataOutcome.result.code}`}`
79925
+ };
79926
+ }
79927
+ return writeImageBytes(dataOutcome.result.stdout, mime, mkTempPath);
79928
+ }
79929
+ function writeImageBytes(bytes, declaredMime, mkTempPath) {
79930
+ if (bytes.length === 0) {
79931
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79932
+ }
79933
+ if (bytes.length > MAX_IMAGE_BYTES) {
79934
+ return { ok: false, kind: "too-large", message: `Clipboard image is too large (${formatBytes2(bytes.length)}).` };
79935
+ }
79936
+ const mimeType = detectMimeFromMagic(bytes) ?? declaredMime;
79937
+ const targetPath = mkTempPath(extensionForMime(mimeType));
79938
+ try {
79939
+ writeFileSync12(targetPath, bytes);
79940
+ } catch (err) {
79941
+ return { ok: false, kind: "failed", message: `Failed to write temp image: ${errorMessage4(err)}` };
79942
+ }
79943
+ return {
79944
+ ok: true,
79945
+ path: targetPath,
79946
+ mimeType,
79947
+ cleanup: makeCleanup(targetPath)
79948
+ };
79949
+ }
79950
+ function finalizeTempImage(path34, mimeType) {
79951
+ let bytes;
79952
+ try {
79953
+ if (!existsSync18(path34)) {
79954
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79955
+ }
79956
+ const size = statSync12(path34).size;
79957
+ if (size === 0) {
79958
+ makeCleanup(path34)();
79959
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79960
+ }
79961
+ if (size > MAX_IMAGE_BYTES) {
79962
+ makeCleanup(path34)();
79963
+ return { ok: false, kind: "too-large", message: `Clipboard image is too large (${formatBytes2(size)}).` };
79964
+ }
79965
+ bytes = readFileSync17(path34);
79966
+ } catch (err) {
79967
+ return { ok: false, kind: "failed", message: `Failed to read clipboard image: ${errorMessage4(err)}` };
79968
+ }
79969
+ const detected = detectMimeFromMagic(bytes);
79970
+ if (!detected) {
79971
+ makeCleanup(path34)();
79972
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain a supported image (PNG/JPEG)." };
79973
+ }
79974
+ return { ok: true, path: path34, mimeType: detected, cleanup: makeCleanup(path34) };
79975
+ }
79976
+ function detectMimeFromMagic(bytes) {
79977
+ if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
79978
+ return "image/png";
79979
+ }
79980
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
79981
+ return "image/jpeg";
79982
+ }
79983
+ return void 0;
79984
+ }
79985
+ function makeCleanup(path34) {
79986
+ return () => {
79987
+ try {
79988
+ unlinkSync8(path34);
79989
+ } catch {
79990
+ }
79991
+ };
79992
+ }
79993
+ function spawnFailure(outcome, tool) {
79994
+ if (outcome.error === "too-large") {
79995
+ return { ok: false, kind: "too-large", message: "Clipboard image is too large." };
79996
+ }
79997
+ if (outcome.error === "not-found") {
79998
+ return {
79999
+ ok: false,
80000
+ kind: "missing-tool",
80001
+ message: `${tool} is not installed; it is required to read clipboard images on this platform.`
80002
+ };
80003
+ }
80004
+ if (outcome.error === "timeout") {
80005
+ return { ok: false, kind: "timeout", message: `Reading the clipboard with ${tool} timed out.` };
80006
+ }
80007
+ return { ok: false, kind: "failed", message: `Failed to run ${tool}: ${outcome.message ?? "unknown error"}` };
80008
+ }
80009
+ function truncate(text, max = 200) {
80010
+ const trimmed = text.trim();
80011
+ return trimmed.length > max ? `${trimmed.slice(0, max)}\u2026` : trimmed;
80012
+ }
80013
+ function errorMessage4(err) {
80014
+ return err instanceof Error ? err.message : String(err);
80015
+ }
80016
+ function formatBytes2(bytes) {
80017
+ if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
80018
+ return `${Math.max(1, Math.round(bytes / 1024))}KB`;
80019
+ }
80020
+
80021
+ // src/input/image-paste.ts
80022
+ import { mkdtempSync, writeFileSync as writeFileSync13, rmSync as rmSync3 } from "node:fs";
80023
+ import { tmpdir as tmpdir2 } from "node:os";
80024
+ import { join as join21 } from "node:path";
80025
+ var pastingHosts = /* @__PURE__ */ new WeakSet();
80026
+ function formatPastedImageName(date, extension = ".png") {
80027
+ const pad = (value) => String(value).padStart(2, "0");
80028
+ const hh = pad(date.getHours());
80029
+ const mm = pad(date.getMinutes());
80030
+ const ss = pad(date.getSeconds());
80031
+ return `pasted-${hh}${mm}${ss}${extension}`;
80032
+ }
80033
+ function terminalResultToClipboardResult(result) {
80034
+ if (result.ok !== true) {
80035
+ const kindMap = {
80036
+ unsupported: "failed",
80037
+ timeout: "timeout",
80038
+ denied: "failed",
80039
+ busy: "failed",
80040
+ unavailable: "no-image",
80041
+ "too-large": "too-large",
80042
+ malformed: "failed"
80043
+ };
80044
+ return { ok: false, kind: kindMap[result.kind], message: result.message };
80045
+ }
80046
+ const ext = result.mimeType === "image/jpeg" ? ".jpg" : ".png";
80047
+ let dir;
80048
+ try {
80049
+ dir = mkdtempSync(join21(tmpdir2(), "pie-osc5522-"));
80050
+ const path34 = join21(dir, formatPastedImageName(/* @__PURE__ */ new Date(), ext));
80051
+ writeFileSync13(path34, result.bytes);
80052
+ return {
80053
+ ok: true,
80054
+ path: path34,
80055
+ mimeType: result.mimeType,
80056
+ cleanup: () => {
80057
+ try {
80058
+ rmSync3(dir, { recursive: true, force: true });
80059
+ } catch {
80060
+ }
80061
+ }
80062
+ };
80063
+ } catch (err) {
80064
+ if (dir) {
80065
+ try {
80066
+ rmSync3(dir, { recursive: true, force: true });
80067
+ } catch {
80068
+ }
80069
+ }
80070
+ return { ok: false, kind: "failed", message: `Failed to materialize clipboard image: ${err instanceof Error ? err.message : String(err)}` };
80071
+ }
80072
+ }
80073
+ async function handleImagePaste(host, options = {}) {
80074
+ if (pastingHosts.has(host)) {
80075
+ host.showStatus("Still reading the clipboard\u2026");
80076
+ return;
80077
+ }
80078
+ if (!host.mentionHandler) {
80079
+ host.showStatus("File attachments are not available yet (still initializing).");
80080
+ return;
80081
+ }
80082
+ pastingHosts.add(host);
80083
+ const handler = host.mentionHandler;
80084
+ const sessionId = host.sessionManager?.getActiveSessionId();
80085
+ const controller = host.agentSessionController;
80086
+ let invalidated = false;
80087
+ const unsubscribe = controller?.subscribe((event) => {
80088
+ if (event.type === "session_invalidated") invalidated = true;
80089
+ });
80090
+ const isCurrent = () => host.mentionHandler === handler && host.sessionManager?.getActiveSessionId() === sessionId && host.agentSessionController === controller && !invalidated;
80091
+ const displayName = formatPastedImageName(options.now?.() ?? /* @__PURE__ */ new Date());
80092
+ let pending;
80093
+ try {
80094
+ pending = handler.beginPendingAttachment(displayName);
80095
+ let result;
80096
+ const terminal = host.ui?.terminal;
80097
+ const readTerminal = options.readTerminalClipboardImage ?? (terminal?.clipboard ? () => terminal.clipboard.readImage() : void 0);
80098
+ if (readTerminal) {
80099
+ host.showStatus("Reading clipboard via terminal\u2026");
80100
+ const terminalResult = await readTerminal();
80101
+ if (terminalResult.ok === true) {
80102
+ result = terminalResultToClipboardResult(terminalResult);
80103
+ } else if (terminalResult.kind === "unsupported") {
80104
+ result = await (options.readClipboardImage ?? readClipboardImage)();
80105
+ } else {
80106
+ result = terminalResultToClipboardResult(terminalResult);
80107
+ }
80108
+ } else {
80109
+ result = await (options.readClipboardImage ?? readClipboardImage)();
80110
+ }
80111
+ if (result.ok === false) {
80112
+ switch (result.kind) {
80113
+ case "no-image":
80114
+ host.showStatus("Clipboard does not contain an image.");
80115
+ break;
80116
+ case "missing-tool":
80117
+ host.showWarning(result.message);
80118
+ break;
80119
+ case "too-large":
80120
+ host.showError(result.message);
80121
+ break;
80122
+ default:
80123
+ host.showError(result.message);
80124
+ break;
80125
+ }
80126
+ return;
80127
+ }
80128
+ try {
80129
+ if (!isCurrent()) return;
80130
+ await handler.attachLocalFile(result.path, displayName, pending.token, isCurrent);
80131
+ } finally {
80132
+ result.cleanup();
80133
+ }
80134
+ } catch (err) {
80135
+ host.showError(`Paste failed: ${err instanceof Error ? err.message : String(err)}`);
80136
+ } finally {
80137
+ try {
80138
+ pending?.finish();
80139
+ if (isCurrent()) controller?.requestQueueDispatch?.();
80140
+ } finally {
80141
+ unsubscribe?.();
80142
+ pastingHosts.delete(host);
80143
+ }
80144
+ }
80145
+ }
80146
+
79247
80147
  // src/runtime/interactive-queue-ui.ts
79248
80148
  function updatePendingMessagesDisplay(host) {
79249
80149
  host.pendingMessagesContainer.clear();
@@ -79255,11 +80155,11 @@ function updatePendingMessagesDisplay(host) {
79255
80155
  const steerMessages = queuedInputs.filter((m2) => m2.mode === "steer");
79256
80156
  const followUpMessages = queuedInputs.filter((m2) => m2.mode === "followUp");
79257
80157
  for (const msg of steerMessages) {
79258
- const text = theme.fg("dim", ` [steer] ${truncate(msg.displayText, 50)}`);
80158
+ const text = theme.fg("dim", ` [steer] ${truncate2(msg.displayText, 50)}`);
79259
80159
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79260
80160
  }
79261
80161
  for (const msg of followUpMessages) {
79262
- const text = theme.fg("dim", ` [followUp] ${truncate(msg.displayText, 50)}`);
80162
+ const text = theme.fg("dim", ` [followUp] ${truncate2(msg.displayText, 50)}`);
79263
80163
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79264
80164
  }
79265
80165
  const dequeueHint = host.keybindings.getKeys("dequeue")[0] || "alt+up";
@@ -79368,7 +80268,7 @@ function handleSteer(host) {
79368
80268
  void host.editor.onSubmit(text);
79369
80269
  }
79370
80270
  }
79371
- function truncate(text, maxLength) {
80271
+ function truncate2(text, maxLength) {
79372
80272
  if (text.length <= maxLength) return text;
79373
80273
  return text.slice(0, maxLength) + "...";
79374
80274
  }
@@ -79468,6 +80368,12 @@ var FooterComponent = class {
79468
80368
  * derived from this at render time so long silent tool runs (e.g.
79469
80369
  * subagents) still visibly tick — a moving elapsed says "alive". */
79470
80370
  busySince = null;
80371
+ /** When the footer last started waiting for the user. Same derived-at-render
80372
+ * approach: the wait elapsed ticks only while a wait timer runs. */
80373
+ waitingSince = null;
80374
+ /** Animation timer for the waiting state (drives the wait elapsed ticker
80375
+ * in place of the stopped busy spinner). */
80376
+ waitingTimer = null;
79471
80377
  /** Keyed ambient status slots for extensions (ctx.ui.setStatus). */
79472
80378
  extensionStatuses = new ExtensionStatusSlots();
79473
80379
  /** Set callback to request re-render when animation updates (pass true to force) */
@@ -79485,6 +80391,17 @@ var FooterComponent = class {
79485
80391
  this.busySince = null;
79486
80392
  this.stopSpinner();
79487
80393
  }
80394
+ if (state.waitingForUser === true) {
80395
+ if (this.waitingSince === null) this.waitingSince = performance.now();
80396
+ this.startWaitingTimer();
80397
+ this.stopSpinner();
80398
+ } else if (state.waitingForUser === false) {
80399
+ this.waitingSince = null;
80400
+ this.stopWaitingTimer();
80401
+ if (this.state.status === "thinking" || this.state.status === "processing") {
80402
+ this.startSpinner();
80403
+ }
80404
+ }
79488
80405
  }
79489
80406
  setAutoCompactEnabled(enabled) {
79490
80407
  this.autoCompactEnabled = enabled;
@@ -79493,6 +80410,7 @@ var FooterComponent = class {
79493
80410
  }
79494
80411
  dispose() {
79495
80412
  this.stopSpinner();
80413
+ this.stopWaitingTimer();
79496
80414
  }
79497
80415
  /** Start spinner animation */
79498
80416
  startSpinner() {
@@ -79510,6 +80428,20 @@ var FooterComponent = class {
79510
80428
  this.spinnerTimer = null;
79511
80429
  }
79512
80430
  }
80431
+ /** Ticker for the waiting state: no spinner animation (nothing is
80432
+ * progressing), just a render tick so the wait elapsed updates. */
80433
+ startWaitingTimer() {
80434
+ if (this.waitingTimer) return;
80435
+ this.waitingTimer = setInterval(() => {
80436
+ this.onRequestRender?.();
80437
+ }, SPINNER_INTERVAL);
80438
+ }
80439
+ stopWaitingTimer() {
80440
+ if (this.waitingTimer) {
80441
+ clearInterval(this.waitingTimer);
80442
+ this.waitingTimer = null;
80443
+ }
80444
+ }
79513
80445
  /** Get current spinner character */
79514
80446
  getSpinner() {
79515
80447
  if (this.state.status !== "thinking" && this.state.status !== "processing") {
@@ -79584,23 +80516,27 @@ var FooterComponent = class {
79584
80516
  processing: "warning",
79585
80517
  error: "error"
79586
80518
  };
79587
- const spinner = this.getSpinner();
79588
- let statusText;
79589
- if (state.lifecycleEvent && state.status !== "ready") {
79590
- statusText = state.lifecycleEvent;
79591
- } else {
79592
- const statusDisplayMap = {
79593
- ready: "Ready",
79594
- thinking: "Thinking",
79595
- processing: "Processing",
79596
- error: "Error"
79597
- };
79598
- statusText = statusDisplayMap[state.status] || state.status;
79599
- }
79600
- const isBusy = state.status === "thinking" || state.status === "processing";
79601
- const busyElapsed = this.busySince !== null && isBusy ? ` \xB7 ${formatElapsed(performance.now() - this.busySince)}` : "";
79602
- const statusWithSpinner = `${spinner ? `${spinner} ` : ""}${statusText}${busyElapsed}`;
79603
- statsParts.push(theme.fg(statusColor[state.status], statusWithSpinner));
80519
+ const isWaitingForUser2 = state.waitingForUser === true;
80520
+ const waitingElapsed = this.waitingSince !== null && isWaitingForUser2 ? ` \xB7 ${formatElapsed(performance.now() - this.waitingSince)}` : "";
80521
+ const statusWithSpinner = isWaitingForUser2 ? theme.fg("accent", `\u23F8 Waiting for you${waitingElapsed}`) : (() => {
80522
+ const spinner = this.getSpinner();
80523
+ let statusText;
80524
+ if (state.lifecycleEvent && state.status !== "ready") {
80525
+ statusText = state.lifecycleEvent;
80526
+ } else {
80527
+ const statusDisplayMap = {
80528
+ ready: "Ready",
80529
+ thinking: "Thinking",
80530
+ processing: "Processing",
80531
+ error: "Error"
80532
+ };
80533
+ statusText = statusDisplayMap[state.status] || state.status;
80534
+ }
80535
+ const isBusy = state.status === "thinking" || state.status === "processing";
80536
+ const busyElapsed = this.busySince !== null && isBusy ? ` \xB7 ${formatElapsed(performance.now() - this.busySince)}` : "";
80537
+ return theme.fg(statusColor[state.status], `${spinner ? `${spinner} ` : ""}${statusText}${busyElapsed}`);
80538
+ })();
80539
+ statsParts.push(statusWithSpinner);
79604
80540
  let statsLeft = statsParts.join(" ");
79605
80541
  statsLeft += this.extensionStatuses.format();
79606
80542
  const modelName = state.modelName || "no-model";
@@ -79731,8 +80667,8 @@ function calculateCliContextUsage(params) {
79731
80667
  };
79732
80668
  }
79733
80669
  function createCliAutoCompactDecision(params) {
79734
- const errorMessage4 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
79735
- if (errorMessage4 && params.isContextOverflowError(errorMessage4)) {
80670
+ const errorMessage5 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
80671
+ if (errorMessage5 && params.isContextOverflowError(errorMessage5)) {
79736
80672
  return { reason: "overflow", willRetry: true };
79737
80673
  }
79738
80674
  if (!params.autoCompactEnabled || params.contextWindow <= 0) {
@@ -79786,7 +80722,7 @@ async function createCliAutoCompactionResult(params) {
79786
80722
  systemPrompt: params.systemPrompt,
79787
80723
  signal: params.context.signal
79788
80724
  };
79789
- const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages, params.onProjection), compactOptions);
80725
+ const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages, params.onProjection, params.envelopeRegistry), compactOptions);
79790
80726
  if (params.context.signal?.aborted) {
79791
80727
  return { aborted: true };
79792
80728
  }
@@ -79794,7 +80730,7 @@ async function createCliAutoCompactionResult(params) {
79794
80730
  summary,
79795
80731
  retainedTail,
79796
80732
  ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79797
- tokensBefore: calculateTotalTokens(params.context.messages)
80733
+ tokensBefore: params.context.preparation?.inputTokens ?? calculateTotalTokens(projectCliHostEnvelopes(params.context.messages, void 0, params.envelopeRegistry))
79798
80734
  };
79799
80735
  }
79800
80736
  function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
@@ -79819,153 +80755,6 @@ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
79819
80755
  });
79820
80756
  }
79821
80757
 
79822
- // src/runtime/retry-policy.ts
79823
- var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|responses stream ended without a terminal event|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|(?:read\s+|write\s+)?EPIPE|socket hang up|request timeout|websocket upgrade refused|websocket handshake|websocket connection failed|websocket transport failed|connection closed before opening|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;
79824
- 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;
79825
- 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;
79826
- var USER_ABORT_PATTERN = /request was aborted|local abort signal|user abort|user_interrupt|interrupted by user|esc pressed|ctrl\+c/i;
79827
- 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;
79828
- var QUOTA_EXHAUSTED_PATTERN = /insufficient[_ ]quota|exceeded[_ ]current[_ ]quota|exceeded your current quota|billing[_ ]hard[_ ]limit|insufficient[_ ]balance|credit balance is too low|\barrearage\b|余额不足|\b(?:account|monthly|billing) quota (?:has been |is )?exhausted/i;
79829
- var INVALID_FILE_REFERENCE_PATTERN = /not the owner of this file|owner of this file|invalid_authentication_error/i;
79830
- var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schema|schema validation|invalid parameter|unsupported parameter|malformed/i;
79831
- var CLI_RETRY_MAX_RETRIES = 3;
79832
- var CLI_RETRY_BASE_DELAY_MS = 2e3;
79833
- var CLI_TRANSIENT_NETWORK_MAX_RETRIES = 10;
79834
- var CLI_RETRY_TOTAL_MAX_RETRIES = 12;
79835
- var CLI_RETRY_MAX_DELAY_MS = 6e4;
79836
- function isCliContextOverflowError(errorMessage4) {
79837
- 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(
79838
- errorMessage4
79839
- );
79840
- }
79841
- function createClassification(kind, message, overrides) {
79842
- return {
79843
- kind,
79844
- message,
79845
- retryable: false,
79846
- compactable: false,
79847
- userAbort: false,
79848
- terminal: true,
79849
- ...overrides
79850
- };
79851
- }
79852
- function classifyCliFailure(context) {
79853
- const errorMessage4 = typeof context === "string" ? context : context.errorMessage;
79854
- const structuredReplayClass = typeof context === "string" ? void 0 : context.message?.role === "assistant" ? context.message.replayClass : void 0;
79855
- if (isCliContextOverflowError(errorMessage4)) {
79856
- return createClassification("context_overflow", errorMessage4, {
79857
- compactable: true,
79858
- terminal: false
79859
- });
79860
- }
79861
- if (USER_ABORT_PATTERN.test(errorMessage4)) {
79862
- return createClassification("user_abort", errorMessage4, {
79863
- userAbort: true,
79864
- terminal: false
79865
- });
79866
- }
79867
- if (INVALID_FILE_REFERENCE_PATTERN.test(errorMessage4)) {
79868
- return createClassification("invalid_file_reference", errorMessage4);
79869
- }
79870
- if (AUTH_CONFIG_PATTERN.test(errorMessage4) || QUOTA_EXHAUSTED_PATTERN.test(errorMessage4)) {
79871
- return createClassification("auth_config", errorMessage4);
79872
- }
79873
- if (TLS_VALIDATION_ERROR_PATTERN.test(errorMessage4)) {
79874
- return createClassification("tls_config", errorMessage4, {
79875
- retryable: true,
79876
- terminal: false
79877
- });
79878
- }
79879
- if (structuredReplayClass === "stream_lost" || structuredReplayClass === "stream_lost_partial" || TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage4)) {
79880
- return createClassification("transient_network", errorMessage4, {
79881
- retryable: true,
79882
- terminal: false
79883
- });
79884
- }
79885
- if (errorMessage4.includes(EMPTY_ASSISTANT_CONTENT_ERROR)) {
79886
- return createClassification("provider_transient", errorMessage4, {
79887
- retryable: true,
79888
- terminal: false
79889
- });
79890
- }
79891
- if (errorMessage4.includes(OPENAI_COMPAT_PROTOCOL_ERROR_CODE)) {
79892
- return createClassification("provider_transient", errorMessage4, {
79893
- retryable: true,
79894
- terminal: false
79895
- });
79896
- }
79897
- if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage4)) {
79898
- return createClassification("provider_transient", errorMessage4, {
79899
- retryable: true,
79900
- terminal: false
79901
- });
79902
- }
79903
- if (INVALID_REQUEST_PATTERN.test(errorMessage4)) {
79904
- return createClassification("invalid_request", errorMessage4);
79905
- }
79906
- return createClassification("unknown", errorMessage4);
79907
- }
79908
- function isCliRetryableError(errorMessage4) {
79909
- return classifyCliFailure(errorMessage4).retryable;
79910
- }
79911
- function getCliRetryBaseDelayMs() {
79912
- const override = process.env.PIE_TEST_CLI_RETRY_BASE_DELAY_MS;
79913
- if (!override) {
79914
- return CLI_RETRY_BASE_DELAY_MS;
79915
- }
79916
- const parsed = Number(override);
79917
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_BASE_DELAY_MS;
79918
- }
79919
- function getCliTransientNetworkMaxRetries() {
79920
- const override = process.env.PIE_RETRY_TRANSIENT_NETWORK_MAX_RETRIES;
79921
- if (!override) {
79922
- return CLI_TRANSIENT_NETWORK_MAX_RETRIES;
79923
- }
79924
- const parsed = Number(override);
79925
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TRANSIENT_NETWORK_MAX_RETRIES;
79926
- }
79927
- function getCliRetryMaxDelayMs() {
79928
- const override = process.env.PIE_RETRY_MAX_DELAY_MS;
79929
- if (!override) {
79930
- return CLI_RETRY_MAX_DELAY_MS;
79931
- }
79932
- const parsed = Number(override);
79933
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_MAX_DELAY_MS;
79934
- }
79935
- function getCliRetryTotalMaxRetries() {
79936
- const override = process.env.PIE_RETRY_TOTAL_MAX_RETRIES;
79937
- if (!override) {
79938
- return CLI_RETRY_TOTAL_MAX_RETRIES;
79939
- }
79940
- const parsed = Number(override);
79941
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_RETRY_TOTAL_MAX_RETRIES;
79942
- }
79943
- function createCliRetryPolicy(isRetryableErrorOverride) {
79944
- return {
79945
- isRetryableError: isRetryableErrorOverride ?? isCliRetryableError,
79946
- maxRetries: CLI_RETRY_MAX_RETRIES,
79947
- baseDelayMs: getCliRetryBaseDelayMs(),
79948
- maxDelayMs: getCliRetryMaxDelayMs(),
79949
- maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : failure.replayExempt === true ? getCliTimeoutReplayMaxRetries() : CLI_RETRY_MAX_RETRIES,
79950
- maxTotalRetries: getCliRetryTotalMaxRetries(),
79951
- systemicReplayStallLimit: getCliSystemicReplayStallLimit()
79952
- };
79953
- }
79954
- var CLI_TIMEOUT_REPLAY_MAX_RETRIES = 5;
79955
- function getCliTimeoutReplayMaxRetries() {
79956
- const override = process.env.PIE_TIMEOUT_REPLAY_MAX_RETRIES;
79957
- if (!override) return CLI_TIMEOUT_REPLAY_MAX_RETRIES;
79958
- const parsed = Number(override);
79959
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TIMEOUT_REPLAY_MAX_RETRIES;
79960
- }
79961
- var CLI_SYSTEMIC_REPLAY_STALL_LIMIT = 3;
79962
- function getCliSystemicReplayStallLimit() {
79963
- const override = process.env.PIE_SYSTEMIC_REPLAY_STALL_LIMIT;
79964
- if (!override) return CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
79965
- const parsed = Number(override);
79966
- return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
79967
- }
79968
-
79969
80758
  // src/runtime/interactive-context-ui.ts
79970
80759
  function calculateContextUsage(host) {
79971
80760
  const messages = host.agent?.state.messages ?? [];
@@ -80142,23 +80931,23 @@ function setupAutocomplete(host) {
80142
80931
  };
80143
80932
  host.editor.setAutocompleteProvider(provider);
80144
80933
  }
80145
- function isContextOverflowError(errorMessage4) {
80146
- return isCliContextOverflowError(errorMessage4);
80934
+ function isContextOverflowError(errorMessage5) {
80935
+ return isCliContextOverflowError(errorMessage5);
80147
80936
  }
80148
80937
  function shouldAutoCompact(host, contextTokens, contextWindow) {
80149
80938
  if (!host.options.settingsManager.getAutoCompactEnabled()) return false;
80150
80939
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
80151
80940
  return shouldCliAutoCompactTokens({ tokens: contextTokens, contextWindow, reserveTokens });
80152
80941
  }
80153
- function isRetryableError(errorMessage4) {
80154
- return isCliRetryableError(errorMessage4);
80942
+ function isRetryableError(errorMessage5) {
80943
+ return isCliRetryableError(errorMessage5);
80155
80944
  }
80156
- function isInvalidFileReferenceError(errorMessage4) {
80945
+ function isInvalidFileReferenceError(errorMessage5) {
80157
80946
  return /not the owner of this file|owner of this file|invalid_authentication_error/i.test(
80158
- errorMessage4
80947
+ errorMessage5
80159
80948
  );
80160
80949
  }
80161
- function recoverFromInvalidFileReferenceError(host, errorMessage4) {
80950
+ function recoverFromInvalidFileReferenceError(host, errorMessage5) {
80162
80951
  if (!host.fileManager || !host.agent) return;
80163
80952
  const messages = host.agent.state.messages.slice();
80164
80953
  const invalidFileIds = /* @__PURE__ */ new Set();
@@ -80176,7 +80965,7 @@ function recoverFromInvalidFileReferenceError(host, errorMessage4) {
80176
80965
  }
80177
80966
  if (invalidFileIds.size === 0) return;
80178
80967
  for (const fileId of invalidFileIds) {
80179
- host.fileManager.markFileReferenceError(fileId, errorMessage4);
80968
+ host.fileManager.markFileReferenceError(fileId, errorMessage5);
80180
80969
  }
80181
80970
  let changed = false;
80182
80971
  const sanitizedMessages = messages.map((message) => {
@@ -80470,9 +81259,9 @@ function clearStatusLine(host) {
80470
81259
  }
80471
81260
  if (removed) host.ui.requestRender();
80472
81261
  }
80473
- function showError(host, errorMessage4) {
81262
+ function showError(host, errorMessage5) {
80474
81263
  host.chatContainer.addChild(new Spacer(1));
80475
- host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage4}`), 1, 0));
81264
+ host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage5}`), 1, 0));
80476
81265
  host.ui.requestRender();
80477
81266
  }
80478
81267
  async function executeCommand(host, command) {
@@ -80502,6 +81291,13 @@ function restoreEditor(host) {
80502
81291
  host.ui.invalidate();
80503
81292
  host.ui.requestRender();
80504
81293
  }
81294
+ function showMissingModelConfigurationNotice(host) {
81295
+ const message = "No model configured. Add one in ~/.pie/models.json, then use /settings/model.";
81296
+ host.chatContainer.addChild(new Spacer(1));
81297
+ host.chatContainer.addChild(new Text(theme.fg("warning", `\u26A0 ${message}`), 1, 0));
81298
+ host.chatContainer.addChild(new Spacer(1));
81299
+ host.showStatus(message);
81300
+ }
80505
81301
 
80506
81302
  // src/runtime/interactive-continuation.ts
80507
81303
  function getContinuationTimeoutMs() {
@@ -80661,22 +81457,42 @@ function scheduleContinuationTimeout(host) {
80661
81457
 
80662
81458
  // src/runtime/interactive-controller-setup.ts
80663
81459
  async function prepareAgentTurn(host, context) {
81460
+ let messages = context.messages;
81461
+ if (context.source === "interactive" && context.streamingBehavior && host.tryBuildAttachmentContentFromExtensions) {
81462
+ messages = await Promise.all(messages.map(async (message) => {
81463
+ if (message.role !== "user") return message;
81464
+ const blocks = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
81465
+ if (blocks.some((block) => block.type !== "text" && block.type !== "hostContext")) return message;
81466
+ const content = [];
81467
+ for (const block of blocks) {
81468
+ if (block.type !== "text" || !/@\[[^\]]+\]\(file:/.test(block.text)) {
81469
+ content.push(block);
81470
+ continue;
81471
+ }
81472
+ const resolved = await host.tryBuildAttachmentContentFromExtensions(block.text);
81473
+ if (resolved.handled && resolved.content.length) content.push(...resolved.content);
81474
+ else content.push(block);
81475
+ if (resolved.warnings.length) host.showStatus(resolved.warnings.join(" | "));
81476
+ }
81477
+ return { ...message, content };
81478
+ }));
81479
+ }
80664
81480
  if (!host.extensionRunner) {
80665
- return void 0;
81481
+ return messages === context.messages ? void 0 : { messages };
80666
81482
  }
80667
81483
  const baseSystemPrompt = context.baseSystemPrompt ?? host.buildSystemPrompt();
80668
81484
  const result = await host.extensionRunner.emitBeforeAgentStart({
80669
81485
  type: "before_agent_start",
80670
- messages: context.messages,
81486
+ messages,
80671
81487
  baseSystemPrompt,
80672
81488
  source: context.source
80673
81489
  });
80674
81490
  if (!result) {
80675
- return { systemPrompt: baseSystemPrompt };
81491
+ return { systemPrompt: baseSystemPrompt, messages };
80676
81492
  }
80677
81493
  return {
80678
81494
  systemPrompt: result.systemPrompt ?? baseSystemPrompt,
80679
- messages: result.messages?.length ? [...context.messages, ...result.messages] : context.messages
81495
+ messages: result.messages?.length ? [...messages, ...result.messages] : messages
80680
81496
  };
80681
81497
  }
80682
81498
  function createInteractiveAgentSessionController(host) {
@@ -80696,6 +81512,7 @@ function createInteractiveAgentSessionController(host) {
80696
81512
  let retryElapsedStartedAt = 0;
80697
81513
  let executionWork;
80698
81514
  let autoCompactBeforeStats = null;
81515
+ let lastShownCompactionError = null;
80699
81516
  const attention = cliAttention(host);
80700
81517
  const stopRetryElapsedTicker = () => {
80701
81518
  if (retryElapsedTicker !== void 0) {
@@ -80749,9 +81566,10 @@ function createInteractiveAgentSessionController(host) {
80749
81566
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80750
81567
  classifyFailure: (context) => classifyCliFailure(context),
80751
81568
  retry: createCliRetryPolicy(
80752
- (errorMessage4) => !host._isContextOverflowError(errorMessage4) && isCliRetryableError(errorMessage4)
81569
+ (errorMessage5) => !host._isContextOverflowError(errorMessage5) && isCliRetryableError(errorMessage5)
80753
81570
  ),
80754
81571
  compaction: {
81572
+ modelView: cliCompactionModelView,
80755
81573
  compact: async (context) => {
80756
81574
  const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
80757
81575
  type: "session:before_compact",
@@ -80902,7 +81720,10 @@ function createInteractiveAgentSessionController(host) {
80902
81720
  if (state.mode === "todo" && state.lifecycle === "paused" && state.steps.length > 0) {
80903
81721
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
80904
81722
  }
80905
- host.showError(event.failure.message);
81723
+ if (event.failure.message !== lastShownCompactionError) {
81724
+ host.showError(event.failure.message);
81725
+ }
81726
+ lastShownCompactionError = null;
80906
81727
  host.setControllerBusy(false);
80907
81728
  } else if (event.type === "turn_cancelled") {
80908
81729
  const outcome = host.executionState.abort();
@@ -80946,14 +81767,17 @@ function createInteractiveAgentSessionController(host) {
80946
81767
  willRetry: event.willRetry
80947
81768
  });
80948
81769
  } else if (event.type === "auto_compaction_end") {
81770
+ const stillRunning = host.agent.state.isStreaming;
80949
81771
  host.isCompacting = false;
80950
- host.isProcessing = false;
81772
+ host.isProcessing = stillRunning;
81773
+ if (stillRunning) host.setControllerBusy(true);
80951
81774
  host.setSubmitLock(false);
80952
81775
  if (event.errorMessage) {
80953
81776
  const prefix = event.reason === "overflow" ? "Context overflow recovery failed" : "Auto-compaction failed";
80954
81777
  host.showError(`${prefix}: ${event.errorMessage}`);
80955
- host.footer.setState({ status: "ready" });
80956
- host.setControllerBusy(false);
81778
+ lastShownCompactionError = event.willRetry || stillRunning ? event.errorMessage : null;
81779
+ host.footer.setState({ status: stillRunning ? "processing" : "ready" });
81780
+ host.setControllerBusy(stillRunning);
80957
81781
  autoCompactBeforeStats = null;
80958
81782
  return;
80959
81783
  }
@@ -80969,7 +81793,7 @@ function createInteractiveAgentSessionController(host) {
80969
81793
  const contextWindow = host.options.model?.contextWindow ?? 0;
80970
81794
  const contextPercent = contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0;
80971
81795
  host.footer.setState({
80972
- status: "ready",
81796
+ status: stillRunning ? "processing" : "ready",
80973
81797
  lifecycleEvent: "auto_compact",
80974
81798
  inputTokens: contextUsage.input,
80975
81799
  outputTokens: contextUsage.output,
@@ -81212,8 +82036,8 @@ async function handleDirectBash(host, command) {
81212
82036
  outputContainer.addChild(new Spacer(1));
81213
82037
  host.chatContainer.addChild(outputContainer);
81214
82038
  host.ui.requestRender();
81215
- const { spawn: spawn2 } = await import("child_process");
81216
- const child = spawn2(shellCommand.file, shellCommand.args, {
82039
+ const { spawn: spawn3 } = await import("child_process");
82040
+ const child = spawn3(shellCommand.file, shellCommand.args, {
81217
82041
  cwd: process.cwd(),
81218
82042
  env: process.env
81219
82043
  });
@@ -81252,21 +82076,20 @@ async function handleDirectBash(host, command) {
81252
82076
  // src/extensions/interactive-extension-ui.ts
81253
82077
  function createInteractiveExtensionUIContext(host) {
81254
82078
  const interact = async (run) => {
81255
- const attention = cliAttention(host);
81256
- const request = attention.request("input-required");
82079
+ const request = beginExtensionWaiting(host, "input-required");
81257
82080
  let resolution = "cancelled";
81258
82081
  try {
81259
82082
  const value = await run();
81260
82083
  resolution = interactionAttentionResolution({ value });
81261
82084
  return value;
81262
82085
  } finally {
81263
- attention.resolve(request, resolution);
82086
+ endExtensionWaiting(host, request, resolution);
81264
82087
  }
81265
82088
  };
81266
82089
  return {
81267
82090
  notify: (message, type = "info", opts) => showExtensionNotification(host, message, type, opts),
81268
82091
  select: (title, options, opts) => interact(() => showExtensionSelector2(host, title, options, opts)),
81269
- confirm: async (title, message, opts) => await interact(() => showExtensionConfirm(host, title, message, opts)) === true,
82092
+ confirm: async (title, message, opts) => (await interact(() => showExtensionConfirm(host, title, message, opts)))?.confirmed === true,
81270
82093
  input: (title, placeholder, opts) => interact(() => showExtensionInput(host, title, placeholder, opts)),
81271
82094
  editor: (title, prefill) => interact(() => showExtensionEditor(host, title, prefill)),
81272
82095
  custom: (factory, options) => interact(() => showExtensionCustom(host, factory, options)),
@@ -81282,23 +82105,13 @@ function createInteractiveExtensionUIContext(host) {
81282
82105
  }
81283
82106
  function installInteractiveInteractionHandler(host) {
81284
82107
  registerInteractionHandler(async (request) => {
81285
- const attention = cliAttention(host);
81286
82108
  let attentionRequest;
81287
82109
  const beginWaitingForUser = () => {
81288
- host.activeInteractionCount++;
81289
- host.setWaitingForUser(true);
81290
- host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
81291
82110
  const reason = interactionAttentionReason(request);
81292
- if (reason) attentionRequest = attention.request(reason, "id" in request ? request.id : void 0);
81293
- host.safeRequestRender();
82111
+ attentionRequest = beginExtensionWaiting(host, reason, "id" in request ? request.id : void 0);
81294
82112
  };
81295
- const endWaitingForUser = () => {
81296
- host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
81297
- if (host.activeInteractionCount === 0) {
81298
- host.setWaitingForUser(false);
81299
- }
81300
- host.footer.setState({ lifecycleEvent: "\u25B6 resuming" });
81301
- host.safeRequestRender();
82113
+ const endWaitingForUser = (resolution) => {
82114
+ endExtensionWaiting(host, attentionRequest, resolution);
81302
82115
  };
81303
82116
  const withTimeout2 = async (createPromise) => {
81304
82117
  const controller = new AbortController();
@@ -81327,8 +82140,7 @@ function installInteractiveInteractionHandler(host) {
81327
82140
  resolution = interactionAttentionResolution(result);
81328
82141
  return result;
81329
82142
  } finally {
81330
- attention.resolve(attentionRequest, resolution);
81331
- endWaitingForUser();
82143
+ endWaitingForUser(resolution);
81332
82144
  }
81333
82145
  };
81334
82146
  switch (request.type) {
@@ -81336,7 +82148,8 @@ function installInteractiveInteractionHandler(host) {
81336
82148
  showExtensionNotification(
81337
82149
  host,
81338
82150
  request.message,
81339
- request.level === "success" ? "info" : request.level
82151
+ request.level === "success" ? "info" : request.level,
82152
+ { markdown: request.markdown === true }
81340
82153
  );
81341
82154
  return { type: "notify", acknowledged: true };
81342
82155
  case "select_one": {
@@ -81365,15 +82178,28 @@ function installInteractiveInteractionHandler(host) {
81365
82178
  prompt: request.prompt,
81366
82179
  detail: request.detail
81367
82180
  });
81368
- const { value: confirmed, timedOut } = await waitForUser(
81369
- (signal) => showExtensionConfirm(host, request.prompt, request.detail ?? "", { signal })
82181
+ const { value, timedOut } = await waitForUser(
82182
+ (signal) => showExtensionConfirm(host, request.prompt, request.detail ?? "", {
82183
+ signal,
82184
+ options: request.options
82185
+ })
81370
82186
  );
82187
+ const confirmed = value?.confirmed;
82188
+ const yoloGrantMs = value?.yoloGrantMs;
81371
82189
  host.logDebug("interaction:confirm:end", {
81372
82190
  id: request.id,
81373
82191
  confirmed,
82192
+ yoloGrantMs,
81374
82193
  timedOut
81375
82194
  });
81376
- return { type: "confirm", id: request.id, confirmed: confirmed === true, timedOut, skipped: timedOut || confirmed === void 0 };
82195
+ return {
82196
+ type: "confirm",
82197
+ id: request.id,
82198
+ confirmed: confirmed === true,
82199
+ timedOut,
82200
+ skipped: timedOut || confirmed === void 0,
82201
+ yoloGrantMs
82202
+ };
81377
82203
  }
81378
82204
  case "text_input": {
81379
82205
  host.logDebug("interaction:text_input:start", {
@@ -81412,6 +82238,30 @@ function installInteractiveInteractionHandler(host) {
81412
82238
  }
81413
82239
  });
81414
82240
  }
82241
+ function beginExtensionWaiting(host, reason, id) {
82242
+ host.activeInteractionCount++;
82243
+ try {
82244
+ host.setWaitingForUser(true);
82245
+ host.footer.setState({ lifecycleEvent: "waiting_for_user_input", waitingForUser: true });
82246
+ const attention = reason ? cliAttention(host).request(reason, id) : void 0;
82247
+ host.safeRequestRender();
82248
+ return attention;
82249
+ } catch (error) {
82250
+ host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
82251
+ throw error;
82252
+ }
82253
+ }
82254
+ function endExtensionWaiting(host, attentionRequest, resolution) {
82255
+ host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
82256
+ if (host.activeInteractionCount === 0) {
82257
+ host.setWaitingForUser(false);
82258
+ host.footer.setState({ lifecycleEvent: "\u25B6 resuming", waitingForUser: false });
82259
+ } else {
82260
+ host.footer.setState({ lifecycleEvent: "waiting_for_user_input", waitingForUser: true });
82261
+ }
82262
+ if (attentionRequest) cliAttention(host).resolve(attentionRequest, resolution);
82263
+ host.safeRequestRender();
82264
+ }
81415
82265
  function showExtensionNotification(host, message, type = "info", opts) {
81416
82266
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
81417
82267
  host.chatContainer.addChild(new Spacer(1));
@@ -81467,9 +82317,15 @@ function showExtensionSelector2(host, title, options, opts) {
81467
82317
  });
81468
82318
  }
81469
82319
  async function showExtensionConfirm(host, title, message, opts) {
82320
+ const options = opts?.options?.length ? opts.options : ["Yes", "No"];
81470
82321
  const result = await showExtensionSelector2(host, `${title}
81471
- ${message}`, ["Yes", "No"], opts);
81472
- return result === void 0 ? void 0 : result === "Yes";
82322
+ ${message}`, options, opts);
82323
+ if (result === void 0) return void 0;
82324
+ const yoloGrantMs = parseYoloGrantMs(options, result);
82325
+ if (yoloGrantMs !== void 0) {
82326
+ return { confirmed: true, yoloGrantMs };
82327
+ }
82328
+ return { confirmed: result === "Yes" };
81473
82329
  }
81474
82330
  function showExtensionInput(host, title, placeholder, opts) {
81475
82331
  return new Promise((resolve7) => {
@@ -82017,6 +82873,8 @@ var InteractiveMode = class {
82017
82873
  mentionPickerOptions = [];
82018
82874
  attachmentResolver;
82019
82875
  yoloMode = false;
82876
+ yoloGrant = new YoloGrantTracker();
82877
+ // time-boxed confirm-dialog grant ("Yolo (5 min)")
82020
82878
  pristineTools = [];
82021
82879
  // Pristine (extension-unwrapped) tool assembly: the mode-rebuild source.
82022
82880
  /** Calm mode: render only questions and answers (display-only; persisted). */
@@ -82108,16 +82966,9 @@ var InteractiveMode = class {
82108
82966
  hasConfiguredModel() {
82109
82967
  return Boolean(this.options.apiKey) && this.options.model?.provider !== "unconfigured";
82110
82968
  }
82111
- showMissingModelConfigurationNotice() {
82112
- const message = "No model configured. Add one in ~/.pie/models.json, then use /settings/model.";
82113
- this.chatContainer.addChild(new Spacer(1));
82114
- this.chatContainer.addChild(new Text(theme.fg("warning", `\u26A0 ${message}`), 1, 0));
82115
- this.chatContainer.addChild(new Spacer(1));
82116
- this.showStatus(message);
82117
- }
82118
82969
  ensureAgentPromptReady() {
82119
82970
  if (this.hasConfiguredModel()) return true;
82120
- this.showMissingModelConfigurationNotice();
82971
+ showMissingModelConfigurationNotice(this.filePipelineHost);
82121
82972
  this.safeRequestRender();
82122
82973
  return false;
82123
82974
  }
@@ -82177,32 +83028,32 @@ var InteractiveMode = class {
82177
83028
  }
82178
83029
  getPolicyContext = () => ({
82179
83030
  mode: this.planModeEnabled ? "plan" : "normal",
82180
- yoloMode: this.yoloMode,
83031
+ yoloMode: this.yoloMode || this.yoloGrant.isActive(),
82181
83032
  confirmationPolicy: "allow"
82182
83033
  });
82183
83034
  rebuildSkillAwareTools(sourceTools) {
82184
83035
  const preservedTools = sourceTools.filter(
82185
83036
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash" && tool.name !== "shell"
82186
83037
  );
82187
- const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode, getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid)) });
82188
- const fileSystemToolOptions = (allowlistedDirs) => ({
82189
- ...buildCliFileAccessOptions({
82190
- cwd: this.options.cwd,
82191
- configDir: this.options.configDir,
82192
- skills: this.skills,
82193
- allowlistedDirs
82194
- }),
83038
+ const bashTool = createBashTool(this.options.cwd, {
83039
+ isYoloMode: () => this.yoloMode || this.yoloGrant.isActive(),
83040
+ getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid))
83041
+ });
83042
+ const fileSystemToolOptions = (allowlistedDirs) => buildInteractiveFilesystemToolOptions({
83043
+ cwd: this.options.cwd,
82195
83044
  configDir: this.options.configDir,
82196
83045
  fileObservationState: this.options.fileObservationState,
83046
+ skills: this.skills,
83047
+ getActiveSessionId: () => this.sessionManager.getActiveSessionId(),
82197
83048
  getAgentModel: () => this.options.model,
82198
- getSessionId: () => this.sessionManager.getActiveSessionId() || void 0,
82199
83049
  resolveToolModel: (purpose) => this.options.modelRegistry.resolveToolModel(purpose, this.options.model),
82200
- getPolicyContext: () => ({
83050
+ resolveToolModelCandidates: this.options.modelRegistry.resolveToolModelCandidates ? (purpose) => this.options.modelRegistry.resolveToolModelCandidates(purpose, this.options.model) : void 0,
83051
+ getPlanPolicyContext: () => ({
82201
83052
  mode: this.planModeEnabled ? "plan" : "normal",
82202
83053
  yoloMode: this.yoloMode,
82203
83054
  confirmationPolicy: "allow"
82204
83055
  })
82205
- });
83056
+ }, allowlistedDirs);
82206
83057
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
82207
83058
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
82208
83059
  return {
@@ -82332,7 +83183,7 @@ var InteractiveMode = class {
82332
83183
  async init() {
82333
83184
  const activeSession = this.sessionManager.getActiveSession();
82334
83185
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82335
- const { Agent } = await import("./chunks/src-WFN4JWLE.js");
83186
+ const { Agent } = await import("./chunks/src-XDNJ3PHL.js");
82336
83187
  this.agent = new Agent({
82337
83188
  initialState: {
82338
83189
  systemPrompt: this.buildSystemPrompt(),
@@ -82376,7 +83227,7 @@ var InteractiveMode = class {
82376
83227
  await this.initializeFileManager();
82377
83228
  this.syncSessionModelMetadata();
82378
83229
  if (!this.hasConfiguredModel()) {
82379
- this.showMissingModelConfigurationNotice();
83230
+ showMissingModelConfigurationNotice(this.filePipelineHost);
82380
83231
  }
82381
83232
  }
82382
83233
  createExtensionContextForCommands() {
@@ -82421,6 +83272,11 @@ var InteractiveMode = class {
82421
83272
  async loadExtensions() {
82422
83273
  this.extensionUIContext = createInteractiveExtensionUIContext(this);
82423
83274
  installInteractiveInteractionHandler(this);
83275
+ setYoloGrantListener((grantMs) => {
83276
+ this.yoloGrant.award(grantMs);
83277
+ renderYoloGrantBanner(grantMs, this.chatContainer);
83278
+ this.ui.requestRender();
83279
+ });
82424
83280
  await loadInteractiveExtensions(this.extensionLoaderHost);
82425
83281
  }
82426
83282
  /**
@@ -82504,20 +83360,20 @@ var InteractiveMode = class {
82504
83360
  setupAgentSubscription() {
82505
83361
  bindInteractiveRuntimeEvents(this.runtimeEventHost);
82506
83362
  }
82507
- _isContextOverflowError(errorMessage4) {
82508
- return isContextOverflowError(errorMessage4);
83363
+ _isContextOverflowError(errorMessage5) {
83364
+ return isContextOverflowError(errorMessage5);
82509
83365
  }
82510
83366
  _shouldAutoCompact(contextTokens, contextWindow) {
82511
83367
  return shouldAutoCompact(this.contextUiHost, contextTokens, contextWindow);
82512
83368
  }
82513
- _isRetryableError(errorMessage4) {
82514
- return isRetryableError(errorMessage4);
83369
+ _isRetryableError(errorMessage5) {
83370
+ return isRetryableError(errorMessage5);
82515
83371
  }
82516
- _isInvalidFileReferenceError(errorMessage4) {
82517
- return isInvalidFileReferenceError(errorMessage4);
83372
+ _isInvalidFileReferenceError(errorMessage5) {
83373
+ return isInvalidFileReferenceError(errorMessage5);
82518
83374
  }
82519
- _recoverFromInvalidFileReferenceError(errorMessage4) {
82520
- return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage4);
83375
+ _recoverFromInvalidFileReferenceError(errorMessage5) {
83376
+ return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage5);
82521
83377
  }
82522
83378
  async handleCompactCommand() {
82523
83379
  return handleCompactCommand(this.compactCommandHost);
@@ -82528,6 +83384,9 @@ var InteractiveMode = class {
82528
83384
  setupKeybindings() {
82529
83385
  return setupInteractiveKeybindings(this.keybindingsHost);
82530
83386
  }
83387
+ handlePasteImage() {
83388
+ void handleImagePaste(this);
83389
+ }
82531
83390
  async selectMentionOption(option) {
82532
83391
  return selectMentionOption(this.filePipelineHost, option);
82533
83392
  }
@@ -82703,8 +83562,8 @@ var InteractiveMode = class {
82703
83562
  clearStatusLine() {
82704
83563
  return clearStatusLine(this.uiHelperHost);
82705
83564
  }
82706
- showError(errorMessage4) {
82707
- return showError(this.uiHelperHost, errorMessage4);
83565
+ showError(errorMessage5) {
83566
+ return showError(this.uiHelperHost, errorMessage5);
82708
83567
  }
82709
83568
  async handleDirectBash(command) {
82710
83569
  return handleDirectBash(this.directBashHost, command);
@@ -82755,8 +83614,8 @@ var InteractiveMode = class {
82755
83614
  };
82756
83615
 
82757
83616
  // src/app/runtime-composition.ts
82758
- import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82759
- import { randomUUID } from "crypto";
83617
+ import { existsSync as existsSync19, mkdirSync as mkdirSync13 } from "fs";
83618
+ import { randomUUID as randomUUID2 } from "crypto";
82760
83619
  import * as path31 from "path";
82761
83620
  var CliMissingApiKeyError = class extends Error {
82762
83621
  constructor(message) {
@@ -82783,7 +83642,7 @@ function getCliRuntimePaths() {
82783
83642
  return { configDir, sessionsDir, skillsDir, builtinSkillsDir };
82784
83643
  }
82785
83644
  function ensureCliRuntimePaths(paths = getCliRuntimePaths()) {
82786
- if (!existsSync18(paths.skillsDir)) {
83645
+ if (!existsSync19(paths.skillsDir)) {
82787
83646
  mkdirSync13(paths.skillsDir, { recursive: true });
82788
83647
  }
82789
83648
  return paths;
@@ -82886,7 +83745,7 @@ async function composeCliRuntime(params) {
82886
83745
  await sessionManager.save();
82887
83746
  }
82888
83747
  const sessionId = sessionManager.getActiveSession()?.id;
82889
- const computerUseExecutionId = randomUUID();
83748
+ const computerUseExecutionId = randomUUID2();
82890
83749
  const fileObservationState = createFileObservationState();
82891
83750
  if (sessionId) {
82892
83751
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -82920,6 +83779,11 @@ async function composeCliRuntime(params) {
82920
83779
  });
82921
83780
  const fileSystemToolOptions = {
82922
83781
  ...buildCliFileAccessOptions({ cwd, configDir: paths.configDir, skills, allowlistedDirs: [paths.configDir] }),
83782
+ // Must match interactive-mode's rebuild view byte-for-byte: the rebuilt
83783
+ // tool surface replaces this composition-time set on the first mode
83784
+ // toggle/skill reload, and any description drift there would invalidate
83785
+ // the prompt-cache prefix exactly like the /yolo scope flip did.
83786
+ descriptionAllowlistedDirs: [],
82923
83787
  configDir: paths.configDir,
82924
83788
  fileObservationState,
82925
83789
  getAgentModel: getCurrentModel,
@@ -83047,6 +83911,7 @@ function createPrintModeRetryStatusReporter() {
83047
83911
  // src/app/print-mode-compaction.ts
83048
83912
  function createPrintModeCompactionHandler(params) {
83049
83913
  return {
83914
+ modelView: cliCompactionModelView,
83050
83915
  compact: async (context) => {
83051
83916
  const beforeCompact = await params.runner.emitSessionBeforeCompact({
83052
83917
  type: "session:before_compact",
@@ -83210,7 +84075,7 @@ async function runPrintMode(params) {
83210
84075
  `);
83211
84076
  console.log("Assistant: ");
83212
84077
  }
83213
- const { Agent: AgentClass } = await import("./chunks/src-WFN4JWLE.js");
84078
+ const { Agent: AgentClass } = await import("./chunks/src-XDNJ3PHL.js");
83214
84079
  let agent;
83215
84080
  const printModeExtensions = await loadPrintModeExtensions({
83216
84081
  cwd: runtime.cwd,
@@ -83379,21 +84244,21 @@ async function runPrintMode(params) {
83379
84244
  console.log(assistantText);
83380
84245
  }
83381
84246
  } catch (err) {
83382
- const errorMessage4 = err instanceof Error ? err.message : String(err);
83383
- sessionTrace.noteStalled(errorMessage4, { mode: "print" });
84247
+ const errorMessage5 = err instanceof Error ? err.message : String(err);
84248
+ sessionTrace.noteStalled(errorMessage5, { mode: "print" });
83384
84249
  process.exitCode = 1;
83385
84250
  if (params.jsonOutput) {
83386
84251
  console.log(
83387
84252
  JSON.stringify({
83388
84253
  ok: false,
83389
- error: errorMessage4,
84254
+ error: errorMessage5,
83390
84255
  sessionId: runtime.sessionManager.getActiveSession()?.id,
83391
84256
  provider: runtime.provider,
83392
84257
  modelId: runtime.modelId
83393
84258
  })
83394
84259
  );
83395
84260
  }
83396
- console.error("Error:", errorMessage4);
84261
+ console.error("Error:", errorMessage5);
83397
84262
  }
83398
84263
  try {
83399
84264
  if (!params.rawOutput && !params.jsonOutput) console.log("");
@@ -83639,7 +84504,7 @@ async function loadJsonModeExtensions(params) {
83639
84504
  getRuntimeTools: () => params.getAgent()?.state.tools ?? [],
83640
84505
  resolveModelClass: (modelClass) => params.runtime.modelRegistry.resolveModelClass(modelClass),
83641
84506
  resolveModelRef: (modelRef) => params.runtime.modelRegistry.resolveModelRef(modelRef),
83642
- registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
84507
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector, params.envelopeRegistry),
83643
84508
  getMessages: () => params.getAgent()?.state.messages ?? [],
83644
84509
  getSessionMetadata: () => ({ ...params.runtime.sessionManager.getActiveSession()?.metadata ?? {} }),
83645
84510
  setSessionMetadata: (key, value) => {
@@ -83650,7 +84515,7 @@ async function loadJsonModeExtensions(params) {
83650
84515
  },
83651
84516
  yoloMode: false
83652
84517
  };
83653
- const { extensions } = await loadExtensions(
84518
+ const { extensions, errors } = await loadExtensions(
83654
84519
  params.runtime.extensionPaths,
83655
84520
  params.runtime.cwd,
83656
84521
  uiContext,
@@ -83670,6 +84535,10 @@ async function loadJsonModeExtensions(params) {
83670
84535
  false
83671
84536
  );
83672
84537
  runner.setUIContext(uiContext);
84538
+ if (errors.length) {
84539
+ await runner.dispose("shutdown");
84540
+ throw new Error(errors.map(({ path: path34, error }) => `${path34}: ${error}`).join("\n"));
84541
+ }
83673
84542
  try {
83674
84543
  await restoreExtensionProviderCredentials(params.runtime.modelRegistry);
83675
84544
  const extensionTools = filterEnabledTools(convertExtensionTools(extensions), params.runtime.settingsManager);
@@ -83689,6 +84558,7 @@ async function runJsonEventsMode(params) {
83689
84558
  const sessionId = params.explicitSessionId || params.runtime?.sessionManager.getActiveSession()?.id || `pie-json-${Date.now()}`;
83690
84559
  let activeTurn = null;
83691
84560
  let runtime = params.runtime;
84561
+ let envelopeRegistry = createCliHostEnvelopeRegistry();
83692
84562
  let agent;
83693
84563
  let controller;
83694
84564
  let disposeAgentSubscription;
@@ -83705,6 +84575,7 @@ async function runJsonEventsMode(params) {
83705
84575
  const completedSessionControlRequests = /* @__PURE__ */ new Map();
83706
84576
  const completedSidechatRequests = /* @__PURE__ */ new Map();
83707
84577
  const ensureRuntimeInFlight = /* @__PURE__ */ new Map();
84578
+ let compositionChain = Promise.resolve();
83708
84579
  let runtimeClosing = false;
83709
84580
  let activeSidechat = null;
83710
84581
  let activeSidechatPromise = null;
@@ -83720,7 +84591,7 @@ async function runJsonEventsMode(params) {
83720
84591
  // Capability broadcast: the envelope kinds THIS build can project at
83721
84592
  // intake (embedded external kinds included), so embedders can probe
83722
84593
  // before their first turn instead of learning mismatches from failures.
83723
- capabilities: { hostContextKinds: cliHostEnvelopeKinds() }
84594
+ capabilities: { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) }
83724
84595
  });
83725
84596
  function trackSidechatPromise(promise) {
83726
84597
  activeSidechatPromise = promise;
@@ -83737,13 +84608,6 @@ async function runJsonEventsMode(params) {
83737
84608
  function delay(ms) {
83738
84609
  return new Promise((resolve7) => setTimeout(resolve7, Math.max(0, ms)));
83739
84610
  }
83740
- async function waitForActiveController(timeoutMs = 1e3) {
83741
- const deadline = Date.now() + timeoutMs;
83742
- while (!controller && activeTurn && !activeTurn.finalized && Date.now() < deadline) {
83743
- await delay(5);
83744
- }
83745
- return controller;
83746
- }
83747
84611
  function touch() {
83748
84612
  if (activeTurn) {
83749
84613
  activeTurn.lastActivityAt = Date.now();
@@ -84081,7 +84945,7 @@ async function runJsonEventsMode(params) {
84081
84945
  }
84082
84946
  };
84083
84947
  try {
84084
- await ensureRuntime();
84948
+ await ensureRuntime(runtime?.cwd);
84085
84949
  const hostContextFailure = validateIntakeHostContext(message);
84086
84950
  if (hostContextFailure) {
84087
84951
  writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
@@ -84099,22 +84963,26 @@ async function runJsonEventsMode(params) {
84099
84963
  return;
84100
84964
  }
84101
84965
  const parent = agent;
84102
- const { Agent: AgentClass } = await import("./chunks/src-WFN4JWLE.js");
84966
+ const sideRegistry = envelopeRegistry;
84967
+ const sideTrace = trace;
84968
+ const sideApiKey = runtime.apiKey;
84969
+ const snapshot = structuredClone(parent.state.messages);
84970
+ const { Agent: AgentClass } = await import("./chunks/src-XDNJ3PHL.js");
84103
84971
  const sideAgent = new AgentClass({
84104
84972
  initialState: {
84105
84973
  systemPrompt: parent.state.systemPrompt,
84106
84974
  model: parent.state.model,
84107
84975
  thinkingLevel: parent.state.thinkingLevel,
84108
84976
  tools: [],
84109
- messages: structuredClone(parent.state.messages)
84977
+ messages: snapshot
84110
84978
  },
84111
- apiKey: runtime.apiKey,
84979
+ apiKey: sideApiKey,
84112
84980
  // The sidechat snapshot clones the parent's stored history, including
84113
84981
  // persisted hostContext facts; project them like the main agent does.
84114
- transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
84982
+ transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => sideTrace?.noteRuntimeEvent(
84115
84983
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84116
84984
  outcome.audit
84117
- )),
84985
+ ), sideRegistry),
84118
84986
  // Same body-free transport timeline as the main agent: a sidechat is
84119
84987
  // an independent Responses request and previously had no record at all
84120
84988
  // when it stalled. `streamSimple` matches the host default when the
@@ -84170,6 +85038,7 @@ async function runJsonEventsMode(params) {
84170
85038
  const turn = activeTurn;
84171
85039
  if (!turn || turn.finalized) return;
84172
85040
  turn.finalized = true;
85041
+ turn.releaseReady();
84173
85042
  const activeEntryId = activeHeadEntryId();
84174
85043
  const base = {
84175
85044
  sessionId: turn.sessionId,
@@ -84402,6 +85271,7 @@ async function runJsonEventsMode(params) {
84402
85271
  agent.releaseTransport();
84403
85272
  }
84404
85273
  agent = void 0;
85274
+ envelopeRegistry = createCliHostEnvelopeRegistry();
84405
85275
  try {
84406
85276
  await runtime?.sessionManager.save();
84407
85277
  } catch {
@@ -84428,7 +85298,9 @@ async function runJsonEventsMode(params) {
84428
85298
  const effectiveCwd = requestedCwd ?? process.cwd();
84429
85299
  const inFlight = ensureRuntimeInFlight.get(effectiveCwd);
84430
85300
  if (inFlight) return inFlight;
84431
- const composed = composeRuntime(effectiveCwd);
85301
+ const composed = compositionChain.then(() => composeRuntime(effectiveCwd));
85302
+ compositionChain = composed.catch(() => {
85303
+ });
84432
85304
  ensureRuntimeInFlight.set(effectiveCwd, composed);
84433
85305
  void composed.finally(() => ensureRuntimeInFlight.delete(effectiveCwd)).catch(() => {
84434
85306
  });
@@ -84459,14 +85331,16 @@ async function runJsonEventsMode(params) {
84459
85331
  if (runtime.initialModel.unconfigured || controller) {
84460
85332
  return;
84461
85333
  }
84462
- const { Agent: AgentClass } = await import("./chunks/src-WFN4JWLE.js");
85334
+ const { Agent: AgentClass } = await import("./chunks/src-XDNJ3PHL.js");
84463
85335
  if (runtimeClosing) return;
84464
- const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
85336
+ const compositionRegistry = createCliHostEnvelopeRegistry();
85337
+ const jsonModeExtensions = await loadJsonModeExtensions({ runtime, envelopeRegistry: compositionRegistry, getAgent: () => agent });
84465
85338
  if (runtimeClosing) {
84466
85339
  await jsonModeExtensions.runner.dispose("shutdown");
84467
85340
  return;
84468
85341
  }
84469
85342
  extensionRunner = jsonModeExtensions.runner;
85343
+ envelopeRegistry = compositionRegistry;
84470
85344
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
84471
85345
  const restoredApiKey = runtime.modelRegistry.getApiKey(runtime.model);
84472
85346
  if (restoredApiKey) runtime.apiKey = restoredApiKey;
@@ -84502,7 +85376,7 @@ async function runJsonEventsMode(params) {
84502
85376
  transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
84503
85377
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84504
85378
  outcome.audit
84505
- )),
85379
+ ), compositionRegistry),
84506
85380
  // Body-free transport timeline for the main (JSON-events) agent.
84507
85381
  streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84508
85382
  });
@@ -84538,8 +85412,10 @@ async function runJsonEventsMode(params) {
84538
85412
  reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84539
85413
  })),
84540
85414
  compaction: {
85415
+ modelView: (message) => projectCliHostEnvelopes([message], void 0, compositionRegistry)[0],
84541
85416
  compact: async (context) => createCliAutoCompactionResult({
84542
85417
  context,
85418
+ envelopeRegistry: compositionRegistry,
84543
85419
  model: runtime.model,
84544
85420
  apiKey: runtime.apiKey,
84545
85421
  systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt,
@@ -84647,7 +85523,7 @@ async function runJsonEventsMode(params) {
84647
85523
  return message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : [{ type: "text", text: message.prompt }];
84648
85524
  }
84649
85525
  function validateIntakeHostContext(message) {
84650
- return message.hostContext ? intakeHostContextFailure(message.hostContext) : void 0;
85526
+ return message.hostContext ? intakeHostContextFailure(message.hostContext, envelopeRegistry) : void 0;
84651
85527
  }
84652
85528
  async function runTurn(message) {
84653
85529
  if (isSessionMismatch(message)) {
@@ -84660,21 +85536,19 @@ async function runJsonEventsMode(params) {
84660
85536
  return;
84661
85537
  }
84662
85538
  if (activeTurn && !activeTurn.finalized) {
84663
- const queuedHostContextFailure = validateIntakeHostContext(message);
84664
- if (queuedHostContextFailure) {
84665
- writeEvent({
84666
- type: "turn.failed",
84667
- sessionId,
84668
- turnId: message.turnId,
84669
- error: queuedHostContextFailure.message,
84670
- ...queuedHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
84671
- });
84672
- return;
84673
- }
84674
- await queueActiveTurnInput(message);
85539
+ const owner = activeTurn;
85540
+ owner.queue = owner.queue.then(() => queueActiveTurnInput(message, owner));
85541
+ await owner.queue;
84675
85542
  return;
84676
85543
  }
85544
+ let releaseReady;
85545
+ const ready = new Promise((resolve7) => {
85546
+ releaseReady = resolve7;
85547
+ });
84677
85548
  activeTurn = {
85549
+ ready,
85550
+ releaseReady,
85551
+ queue: Promise.resolve(),
84678
85552
  sessionId,
84679
85553
  turnId: message.turnId,
84680
85554
  prompt: message.prompt,
@@ -84695,7 +85569,7 @@ async function runJsonEventsMode(params) {
84695
85569
  finalize("failed", {
84696
85570
  code: mockHostContextFailure.code,
84697
85571
  message: mockHostContextFailure.message,
84698
- ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85572
+ ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84699
85573
  });
84700
85574
  if (activeTurn === turn) emitSettled(turn);
84701
85575
  if (activeTurn === turn) activeTurn = null;
@@ -84735,12 +85609,21 @@ async function runJsonEventsMode(params) {
84735
85609
  if (activeTurn === turn) activeTurn = null;
84736
85610
  return;
84737
85611
  }
85612
+ if (activeTurn !== turn || turn.finalized || turn.interrupted) {
85613
+ if (activeTurn === turn && !turn.finalized) finalize("cancelled", "user_interrupt");
85614
+ turn.releaseReady();
85615
+ if (activeTurn === turn) {
85616
+ emitSettled(turn);
85617
+ activeTurn = null;
85618
+ }
85619
+ return;
85620
+ }
84738
85621
  const hostContextFailure = validateIntakeHostContext(message);
84739
85622
  if (hostContextFailure) {
84740
85623
  finalize("failed", {
84741
85624
  code: hostContextFailure.code,
84742
85625
  message: hostContextFailure.message,
84743
- ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85626
+ ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84744
85627
  });
84745
85628
  if (activeTurn === turn) emitSettled(turn);
84746
85629
  if (activeTurn === turn) activeTurn = null;
@@ -84780,7 +85663,9 @@ async function runJsonEventsMode(params) {
84780
85663
  try {
84781
85664
  trace?.notePendingUserText(message.prompt);
84782
85665
  trace?.noteDispatchStart({ mode: "json-events" });
84783
- await controller.prompt(promptInputFor(message), { source: "json-events" });
85666
+ const dispatched = controller.prompt(promptInputFor(message), { source: "json-events" });
85667
+ turn.releaseReady();
85668
+ await dispatched;
84784
85669
  trace?.noteDispatchSettled({ mode: "json-events" });
84785
85670
  trace?.noteWaitForIdleStart({ mode: "json-events" });
84786
85671
  await controller.waitForSettled();
@@ -84818,7 +85703,7 @@ async function runJsonEventsMode(params) {
84818
85703
  if (activeTurn === turn) activeTurn = null;
84819
85704
  }
84820
85705
  }
84821
- async function queueActiveTurnInput(message) {
85706
+ async function queueActiveTurnInput(message, owner) {
84822
85707
  if (!message.streamingBehavior) {
84823
85708
  writeEvent({
84824
85709
  type: "turn.failed",
@@ -84828,8 +85713,9 @@ async function runJsonEventsMode(params) {
84828
85713
  });
84829
85714
  return;
84830
85715
  }
84831
- const activeController = await waitForActiveController();
84832
- if (!activeController) {
85716
+ await owner.ready;
85717
+ const activeController = controller;
85718
+ if (!activeController || activeTurn !== owner || owner.finalized || owner.interrupted || runtimeClosing) {
84833
85719
  writeEvent({
84834
85720
  type: "turn.failed",
84835
85721
  sessionId,
@@ -84838,6 +85724,17 @@ async function runJsonEventsMode(params) {
84838
85724
  });
84839
85725
  return;
84840
85726
  }
85727
+ const failure = validateIntakeHostContext(message);
85728
+ if (failure) {
85729
+ writeEvent({
85730
+ type: "turn.failed",
85731
+ sessionId,
85732
+ turnId: message.turnId,
85733
+ error: failure.message,
85734
+ hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry)
85735
+ });
85736
+ return;
85737
+ }
84841
85738
  try {
84842
85739
  applyRuntimeSelection(message);
84843
85740
  trace?.notePendingUserText(message.prompt);
@@ -84856,7 +85753,7 @@ async function runJsonEventsMode(params) {
84856
85753
  sessionId,
84857
85754
  turnId: message.turnId,
84858
85755
  mode: message.streamingBehavior,
84859
- activeTurnId: activeTurn?.turnId
85756
+ activeTurnId: owner.turnId
84860
85757
  });
84861
85758
  } catch (error) {
84862
85759
  writeEvent({
@@ -85121,7 +86018,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
85121
86018
  }
85122
86019
  async function createCliTestStreamFnFromEnv() {
85123
86020
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
85124
- return (await import("./chunks/test-stream-GXZ7NT4H.js")).createTestStreamFnFromSequenceEnv();
86021
+ return (await import("./chunks/test-stream-LUUGOJMP.js")).createTestStreamFnFromSequenceEnv();
85125
86022
  }
85126
86023
  function resolveJsonEventsDetailFlag() {
85127
86024
  for (let index = 0; index < process.argv.length; index += 1) {