@cydm/pie 2.7.11 → 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,11 +23,13 @@ import {
20
23
  getSessionsDir,
21
24
  getSettingsPath,
22
25
  getThemesDir,
26
+ isCliContextOverflowError,
27
+ isCliRetryableError,
23
28
  logTransportLifecycle,
24
29
  migrateConfigFromAgentDir,
25
30
  setYoloGrantListener,
26
31
  truncateHeadTail
27
- } from "./chunks/chunk-3NFKVBGY.js";
32
+ } from "./chunks/chunk-YI3CSQGD.js";
28
33
  import {
29
34
  resolveCliProjectRoot
30
35
  } from "./chunks/chunk-WLFOBECN.js";
@@ -89,17 +94,15 @@ import {
89
94
  shouldPreserveExecutionStateForUserText,
90
95
  supersedeExecutionState,
91
96
  verifyPreparedCompactionCommit
92
- } from "./chunks/chunk-XIRBNP2S.js";
93
- import "./chunks/chunk-XVF6V6Q5.js";
97
+ } from "./chunks/chunk-AFPRX5XI.js";
98
+ import "./chunks/chunk-2NBUFVUQ.js";
94
99
  import {
95
100
  Deref,
96
- EMPTY_ASSISTANT_CONTENT_ERROR,
97
101
  Errors,
98
102
  ExtendsUndefinedCheck,
99
103
  HasTransform,
100
104
  Hash,
101
105
  MODEL_CLASS_FALLBACK_ORDER,
102
- OPENAI_COMPAT_PROTOCOL_ERROR_CODE,
103
106
  TransformDecode,
104
107
  TransformDecodeCheckError,
105
108
  TransformEncode,
@@ -115,7 +118,7 @@ import {
115
118
  sortToolModelCandidatesByCapability,
116
119
  streamSimple,
117
120
  type_exports
118
- } from "./chunks/chunk-3VEIP2VY.js";
121
+ } from "./chunks/chunk-EN47TIBG.js";
119
122
  import {
120
123
  IsArray,
121
124
  IsBigInt,
@@ -1937,7 +1940,7 @@ var require_core = __commonJS({
1937
1940
  return match && match.index === 0;
1938
1941
  }
1939
1942
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
1940
- function join22(regexps, separator = "|") {
1943
+ function join24(regexps, separator = "|") {
1941
1944
  let numCaptures = 0;
1942
1945
  return regexps.map((regex) => {
1943
1946
  numCaptures += 1;
@@ -2241,7 +2244,7 @@ var require_core = __commonJS({
2241
2244
  this.exec = () => null;
2242
2245
  }
2243
2246
  const terminators = this.regexes.map((el) => el[1]);
2244
- this.matcherRe = langRe(join22(terminators), true);
2247
+ this.matcherRe = langRe(join24(terminators), true);
2245
2248
  this.lastIndex = 0;
2246
2249
  }
2247
2250
  /** @param {string} s */
@@ -58062,7 +58065,9 @@ var SYMBOL_KEYS = /* @__PURE__ */ new Set([
58062
58065
  var MODIFIERS = {
58063
58066
  shift: 1,
58064
58067
  alt: 2,
58065
- ctrl: 4
58068
+ ctrl: 4,
58069
+ /** Kitty keyboard protocol bit 8: Super (macOS Command / Windows/Linux Super). */
58070
+ super: 8
58066
58071
  };
58067
58072
  var LOCK_MASK = 64 + 128;
58068
58073
  var CODEPOINTS = {
@@ -58256,7 +58261,8 @@ function parseKeyId(keyId) {
58256
58261
  key,
58257
58262
  ctrl: parts.includes("ctrl"),
58258
58263
  shift: parts.includes("shift"),
58259
- alt: parts.includes("alt")
58264
+ alt: parts.includes("alt"),
58265
+ super: parts.includes("super") || parts.includes("cmd") || parts.includes("meta")
58260
58266
  };
58261
58267
  }
58262
58268
  function matchesKey(data, keyId) {
@@ -58267,6 +58273,7 @@ function matchesKey(data, keyId) {
58267
58273
  if (shift) modifier |= MODIFIERS.shift;
58268
58274
  if (alt) modifier |= MODIFIERS.alt;
58269
58275
  if (ctrl) modifier |= MODIFIERS.ctrl;
58276
+ if (parsed.super) modifier |= MODIFIERS.super;
58270
58277
  switch (key) {
58271
58278
  case "escape":
58272
58279
  case "esc":
@@ -63655,6 +63662,8 @@ import { EventEmitter } from "events";
63655
63662
  var ESC = "\x1B";
63656
63663
  var BRACKETED_PASTE_START = "\x1B[200~";
63657
63664
  var BRACKETED_PASTE_END = "\x1B[201~";
63665
+ var OSC_5522_PREFIX = "\x1B]5522;";
63666
+ var OSC_5522_MAX_FRAME = 70 * 1024 * 1024;
63658
63667
  function isCompleteSequence(data) {
63659
63668
  if (!data.startsWith(ESC)) {
63660
63669
  return "not-escape";
@@ -63741,11 +63750,19 @@ function isCompleteApcSequence(data) {
63741
63750
  }
63742
63751
  return "incomplete";
63743
63752
  }
63744
- function extractCompleteSequences(buffer) {
63753
+ function extractCompleteSequences(buffer, maxClipboardFrame = OSC_5522_MAX_FRAME) {
63745
63754
  const sequences = [];
63746
63755
  let pos = 0;
63747
63756
  while (pos < buffer.length) {
63748
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
+ }
63749
63766
  if (remaining.startsWith(ESC)) {
63750
63767
  let seqEnd = 1;
63751
63768
  while (seqEnd <= remaining.length) {
@@ -63779,9 +63796,13 @@ var StdinBuffer = class extends EventEmitter {
63779
63796
  timeoutMs;
63780
63797
  pasteMode = false;
63781
63798
  pasteBuffer = "";
63799
+ discardingClipboard = false;
63800
+ discardEsc = false;
63801
+ options;
63782
63802
  constructor(options = {}) {
63783
63803
  super();
63784
63804
  this.timeoutMs = options.timeout ?? 10;
63805
+ this.options = options;
63785
63806
  }
63786
63807
  process(data) {
63787
63808
  if (this.timeout) {
@@ -63803,6 +63824,21 @@ var StdinBuffer = class extends EventEmitter {
63803
63824
  this.emit("data", "");
63804
63825
  return;
63805
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
+ }
63806
63842
  this.buffer += str2;
63807
63843
  if (this.pasteMode) {
63808
63844
  this.pasteBuffer += this.buffer;
@@ -63846,12 +63882,40 @@ var StdinBuffer = class extends EventEmitter {
63846
63882
  }
63847
63883
  return;
63848
63884
  }
63849
- const result = extractCompleteSequences(this.buffer);
63885
+ const result = extractCompleteSequences(this.buffer, this.options.clipboardMaxFrame);
63850
63886
  this.buffer = result.remainder;
63851
63887
  for (const sequence of result.sequences) {
63852
63888
  this.emit("data", sequence);
63853
63889
  }
63854
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
+ }
63855
63919
  this.timeout = setTimeout(() => {
63856
63920
  const flushed = this.flush();
63857
63921
  for (const sequence of flushed) {
@@ -63868,7 +63932,7 @@ var StdinBuffer = class extends EventEmitter {
63868
63932
  if (this.buffer.length === 0) {
63869
63933
  return [];
63870
63934
  }
63871
- 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];
63872
63936
  this.buffer = "";
63873
63937
  return sequences;
63874
63938
  }
@@ -63880,6 +63944,8 @@ var StdinBuffer = class extends EventEmitter {
63880
63944
  this.buffer = "";
63881
63945
  this.pasteMode = false;
63882
63946
  this.pasteBuffer = "";
63947
+ this.discardingClipboard = false;
63948
+ this.discardEsc = false;
63883
63949
  }
63884
63950
  getBuffer() {
63885
63951
  return this.buffer;
@@ -63892,8 +63958,193 @@ var StdinBuffer = class extends EventEmitter {
63892
63958
  // ../../packages/tui/src/terminal.ts
63893
63959
  import * as fs11 from "node:fs";
63894
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
63895
64140
  var ProcessTerminal = class {
63896
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
+ }
63897
64148
  inputHandler;
63898
64149
  resizeHandler;
63899
64150
  _kittyProtocolActive = false;
@@ -63935,9 +64186,12 @@ var ProcessTerminal = class {
63935
64186
  * to handle the case where the response arrives split across multiple events.
63936
64187
  */
63937
64188
  setupStdinBuffer() {
63938
- this.stdinBuffer = new StdinBuffer({ timeout: 10 });
64189
+ this.stdinBuffer = new StdinBuffer({ timeout: 10, clipboardResponsePending: () => this.osc5522Client.responsePending });
63939
64190
  const kittyResponsePattern = /^\x1b\[\?(\d+)u$/;
63940
64191
  this.stdinBuffer.on("data", (sequence) => {
64192
+ if (this.osc5522Client.feed(sequence)) {
64193
+ return;
64194
+ }
63941
64195
  if (!this._kittyProtocolActive) {
63942
64196
  const match = sequence.match(kittyResponsePattern);
63943
64197
  if (match) {
@@ -64196,11 +64450,14 @@ var ExtensionRunner = class {
64196
64450
  };
64197
64451
  }
64198
64452
  /**
64199
- * 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.
64200
64458
  */
64201
64459
  setUIContext(uiContext) {
64202
64460
  this.uiContext = uiContext;
64203
- this.hasUI = true;
64204
64461
  }
64205
64462
  /**
64206
64463
  * Wrap an unregister handle so this runner can revoke it during
@@ -64948,7 +65205,7 @@ var DEFAULT_APP_KEYBINDINGS = {
64948
65205
  steer: "alt+enter",
64949
65206
  followUp: [],
64950
65207
  dequeue: ["alt+up", "ctrl+shift+up"],
64951
- pasteImage: "ctrl+v",
65208
+ pasteImage: ["ctrl+v", "alt+v", "super+v"],
64952
65209
  newSession: [],
64953
65210
  tree: [],
64954
65211
  fork: [],
@@ -65102,32 +65359,6 @@ function buildCliProjectContextSection(cwd) {
65102
65359
  return buildProjectContextSection(collectAncestorAgentsFiles(cwd));
65103
65360
  }
65104
65361
 
65105
- // src/file-access-context.ts
65106
- import * as path11 from "node:path";
65107
- function uniqueNormalizedPaths(paths) {
65108
- const seen = /* @__PURE__ */ new Set();
65109
- const result = [];
65110
- for (const entry of paths) {
65111
- const trimmed = String(entry || "").trim();
65112
- if (!trimmed) continue;
65113
- const normalized = path11.normalize(trimmed);
65114
- if (seen.has(normalized)) continue;
65115
- seen.add(normalized);
65116
- result.push(normalized);
65117
- }
65118
- return result;
65119
- }
65120
- function buildCliFileAccessOptions(params) {
65121
- return {
65122
- accessRoots: uniqueNormalizedPaths([
65123
- resolveCliProjectRoot(params.cwd),
65124
- params.configDir,
65125
- ...params.skills.map((skill) => skill.baseDir)
65126
- ]),
65127
- allowlistedDirs: params.allowlistedDirs
65128
- };
65129
- }
65130
-
65131
65362
  // src/runtime/computer-use-owner.ts
65132
65363
  var COMPUTER_USE_OWNER_ENV = "PIE_COMPUTER_USE_OWNER_ID";
65133
65364
  var COMPUTER_USE_EXECUTION_ENV = "PIE_COMPUTER_USE_EXECUTION_ID";
@@ -65147,7 +65378,7 @@ function computerUseOwnerEnv(ownerId, executionId) {
65147
65378
 
65148
65379
  // src/theme/theme.ts
65149
65380
  import * as fs13 from "node:fs";
65150
- import * as path12 from "node:path";
65381
+ import * as path11 from "node:path";
65151
65382
 
65152
65383
  // ../../node_modules/@sinclair/typebox/build/esm/compiler/compiler.mjs
65153
65384
  var TypeCheck = class {
@@ -66034,8 +66265,8 @@ var BUILTIN_THEMES;
66034
66265
  function getBuiltinThemes() {
66035
66266
  if (!BUILTIN_THEMES) {
66036
66267
  const themesDir = getThemesDir();
66037
- const darkPath = path12.join(themesDir, "dark.json");
66038
- const lightPath = path12.join(themesDir, "light.json");
66268
+ const darkPath = path11.join(themesDir, "dark.json");
66269
+ const lightPath = path11.join(themesDir, "light.json");
66039
66270
  BUILTIN_THEMES = {
66040
66271
  dark: JSON.parse(fs13.readFileSync(darkPath, "utf-8")),
66041
66272
  light: JSON.parse(fs13.readFileSync(lightPath, "utf-8"))
@@ -66072,23 +66303,23 @@ function parseThemeJson(label, json) {
66072
66303
  otherErrors.push(` - ${e.path}: ${e.message}`);
66073
66304
  }
66074
66305
  }
66075
- let errorMessage4 = `Invalid theme "${label}":
66306
+ let errorMessage5 = `Invalid theme "${label}":
66076
66307
  `;
66077
66308
  if (missingColors.length > 0) {
66078
- errorMessage4 += "\nMissing required color tokens:\n";
66079
- errorMessage4 += missingColors.map((c) => ` - ${c}`).join("\n");
66080
- errorMessage4 += `
66309
+ errorMessage5 += "\nMissing required color tokens:\n";
66310
+ errorMessage5 += missingColors.map((c) => ` - ${c}`).join("\n");
66311
+ errorMessage5 += `
66081
66312
 
66082
66313
  Please add these colors to your theme's "colors" object.`;
66083
- 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.";
66084
66315
  }
66085
66316
  if (otherErrors.length > 0) {
66086
- errorMessage4 += `
66317
+ errorMessage5 += `
66087
66318
 
66088
66319
  Other errors:
66089
66320
  ${otherErrors.join("\n")}`;
66090
66321
  }
66091
- throw new Error(errorMessage4);
66322
+ throw new Error(errorMessage5);
66092
66323
  }
66093
66324
  return json;
66094
66325
  }
@@ -66115,7 +66346,7 @@ function loadThemeJson(name) {
66115
66346
  throw new Error(`Theme "${name}" does not have a source path for export`);
66116
66347
  }
66117
66348
  const customThemesDir = getCustomThemesDir();
66118
- const themePath = path12.join(customThemesDir, `${name}.json`);
66349
+ const themePath = path11.join(customThemesDir, `${name}.json`);
66119
66350
  if (!fs13.existsSync(themePath)) {
66120
66351
  throw new Error(`Theme not found: ${name}`);
66121
66352
  }
@@ -66235,7 +66466,7 @@ function startThemeWatcher() {
66235
66466
  return;
66236
66467
  }
66237
66468
  const customThemesDir = getCustomThemesDir();
66238
- const themeFile = path12.join(customThemesDir, `${currentThemeName}.json`);
66469
+ const themeFile = path11.join(customThemesDir, `${currentThemeName}.json`);
66239
66470
  if (!fs13.existsSync(themeFile)) {
66240
66471
  return;
66241
66472
  }
@@ -66436,7 +66667,7 @@ function getSettingsListTheme() {
66436
66667
  var import_yaml = __toESM(require_dist2(), 1);
66437
66668
  import * as fs14 from "fs";
66438
66669
  import * as os4 from "os";
66439
- import * as path13 from "path";
66670
+ import * as path12 from "path";
66440
66671
  function parseSkillDescription(content) {
66441
66672
  content = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
66442
66673
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
@@ -66460,8 +66691,8 @@ function parseSkillDescription(content) {
66460
66691
  }
66461
66692
  var USER_CONTROLLED_SOURCES = /* @__PURE__ */ new Set(["user-agents", "user"]);
66462
66693
  function pathIsInside(child, parent) {
66463
- const relative3 = path13.relative(parent, child);
66464
- 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);
66465
66696
  }
66466
66697
  function resolveSkillSymlink(fullPath, dir, source) {
66467
66698
  let target;
@@ -66486,7 +66717,7 @@ function loadSkillsFromDir(dir, source) {
66486
66717
  const entries = fs14.readdirSync(dir, { withFileTypes: true });
66487
66718
  for (const entry of entries) {
66488
66719
  if (entry.name.startsWith(".")) continue;
66489
- const fullPath = path13.join(dir, entry.name);
66720
+ const fullPath = path12.join(dir, entry.name);
66490
66721
  let entryPath = fullPath;
66491
66722
  let entryIsDirectory = entry.isDirectory();
66492
66723
  let entryIsFile = entry.isFile();
@@ -66501,7 +66732,7 @@ function loadSkillsFromDir(dir, source) {
66501
66732
  entryIsFile = resolved.isFile;
66502
66733
  }
66503
66734
  if (entryIsDirectory) {
66504
- const skillMdPath = path13.join(entryPath, "SKILL.md");
66735
+ const skillMdPath = path12.join(entryPath, "SKILL.md");
66505
66736
  if (fs14.existsSync(skillMdPath)) {
66506
66737
  try {
66507
66738
  const content = fs14.readFileSync(skillMdPath, "utf-8");
@@ -66533,7 +66764,7 @@ function loadSkillsFromDir(dir, source) {
66533
66764
  content,
66534
66765
  filePath: entryPath,
66535
66766
  source,
66536
- baseDir: path13.dirname(entryPath)
66767
+ baseDir: path12.dirname(entryPath)
66537
66768
  });
66538
66769
  } catch (e) {
66539
66770
  diagnostics.push({
@@ -66547,18 +66778,18 @@ function loadSkillsFromDir(dir, source) {
66547
66778
  return { skills, diagnostics };
66548
66779
  }
66549
66780
  function discoverAgentsSkillsDirs(cwd) {
66550
- const resolvedCwd = path13.resolve(cwd || process.cwd());
66781
+ const resolvedCwd = path12.resolve(cwd || process.cwd());
66551
66782
  const projectRoot = resolveCliProjectRoot(resolvedCwd);
66552
66783
  const projectAgentsDirs = [];
66553
66784
  let current = projectRoot;
66554
66785
  while (true) {
66555
- projectAgentsDirs.push(path13.join(current, ".agents", "skills"));
66786
+ projectAgentsDirs.push(path12.join(current, ".agents", "skills"));
66556
66787
  if (current === resolvedCwd) break;
66557
- const child = path13.relative(current, resolvedCwd).split(path13.sep)[0];
66788
+ const child = path12.relative(current, resolvedCwd).split(path12.sep)[0];
66558
66789
  if (!child) break;
66559
- current = path13.join(current, child);
66790
+ current = path12.join(current, child);
66560
66791
  }
66561
- return { userAgentsDir: path13.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66792
+ return { userAgentsDir: path12.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66562
66793
  }
66563
66794
  function buildSkillDirs(options) {
66564
66795
  const { userAgentsDir, projectAgentsDirs } = discoverAgentsSkillsDirs(options.cwd);
@@ -66732,7 +66963,7 @@ function validateSkillName(name) {
66732
66963
  import * as path30 from "path";
66733
66964
 
66734
66965
  // src/capabilities/read-file-understanding.ts
66735
- import * as path22 from "node:path";
66966
+ import * as path21 from "node:path";
66736
66967
  import * as fs22 from "node:fs/promises";
66737
66968
 
66738
66969
  // src/core/provider-capabilities.ts
@@ -66760,7 +66991,7 @@ function getProviderDisplayName(platform2) {
66760
66991
 
66761
66992
  // src/files/adapters/kimi-adapter.ts
66762
66993
  import * as fs15 from "node:fs";
66763
- import * as path14 from "node:path";
66994
+ import * as path13 from "node:path";
66764
66995
 
66765
66996
  // src/files/errors.ts
66766
66997
  var FileErrorMessages = {
@@ -66897,7 +67128,7 @@ var KimiFileAdapter = class {
66897
67128
  // Upload
66898
67129
  // ============================================================================
66899
67130
  async upload(filePath, options = {}) {
66900
- const fileName = options.sourceName || path14.basename(filePath);
67131
+ const fileName = options.sourceName || path13.basename(filePath);
66901
67132
  const mimeType = options.mimeType || this.guessMimeType(fileName);
66902
67133
  const purpose = this.detectPurpose(filePath, options.purpose, mimeType);
66903
67134
  const stats = fs15.statSync(filePath);
@@ -66959,7 +67190,7 @@ var KimiFileAdapter = class {
66959
67190
  return "file-extract";
66960
67191
  }
66961
67192
  guessMimeType(filename) {
66962
- const ext = path14.extname(filename).toLowerCase();
67193
+ const ext = path13.extname(filename).toLowerCase();
66963
67194
  switch (ext) {
66964
67195
  case ".jpg":
66965
67196
  case ".jpeg":
@@ -67122,7 +67353,7 @@ var KimiFileAdapter = class {
67122
67353
 
67123
67354
  // src/files/adapters/gemini-adapter.ts
67124
67355
  import * as fs16 from "node:fs";
67125
- import * as path15 from "node:path";
67356
+ import * as path14 from "node:path";
67126
67357
  import { Readable } from "node:stream";
67127
67358
  var GeminiFileAdapter = class {
67128
67359
  platform = "gemini";
@@ -67187,7 +67418,7 @@ var GeminiFileAdapter = class {
67187
67418
  );
67188
67419
  }
67189
67420
  const mimeType = options.mimeType || this.guessMimeType(filePath);
67190
- const displayName = options.sourceName || path15.basename(filePath);
67421
+ const displayName = options.sourceName || path14.basename(filePath);
67191
67422
  const startResponse = await fetch(
67192
67423
  `${this.baseUrl}/upload/v1beta/files?key=${this.apiKey}`,
67193
67424
  {
@@ -67423,7 +67654,7 @@ var GeminiFileAdapter = class {
67423
67654
 
67424
67655
  // src/files/adapters/openai-compatible-files-adapter.ts
67425
67656
  import * as fs17 from "node:fs";
67426
- import * as path16 from "node:path";
67657
+ import * as path15 from "node:path";
67427
67658
  var OpenAICompatibleFilesAdapter = class {
67428
67659
  constructor(apiKey, baseUrl, extraHeaders = {}) {
67429
67660
  this.apiKey = apiKey;
@@ -67449,7 +67680,7 @@ var OpenAICompatibleFilesAdapter = class {
67449
67680
  ]
67450
67681
  };
67451
67682
  async upload(filePath, options = {}) {
67452
- const fileName = options.sourceName || path16.basename(filePath);
67683
+ const fileName = options.sourceName || path15.basename(filePath);
67453
67684
  const mimeType = options.mimeType || guessMimeType(fileName);
67454
67685
  const stats = fs17.statSync(filePath);
67455
67686
  if (stats.size > this.capabilities.maxFileSize) {
@@ -67575,7 +67806,7 @@ function extractContentText(raw) {
67575
67806
  return raw;
67576
67807
  }
67577
67808
  function guessMimeType(filename) {
67578
- const ext = path16.extname(filename).toLowerCase();
67809
+ const ext = path15.extname(filename).toLowerCase();
67579
67810
  switch (ext) {
67580
67811
  case ".pdf":
67581
67812
  return "application/pdf";
@@ -67592,12 +67823,12 @@ function guessMimeType(filename) {
67592
67823
 
67593
67824
  // src/files/file-manager.ts
67594
67825
  import * as fs21 from "node:fs";
67595
- import * as path21 from "node:path";
67826
+ import * as path20 from "node:path";
67596
67827
  import * as crypto2 from "node:crypto";
67597
67828
 
67598
67829
  // src/files/file-cache.ts
67599
67830
  import * as fs18 from "node:fs";
67600
- import * as path18 from "node:path";
67831
+ import * as path17 from "node:path";
67601
67832
  import * as crypto from "node:crypto";
67602
67833
 
67603
67834
  // src/files/logger.ts
@@ -67692,7 +67923,7 @@ var DEFAULT_PIC_OPTIONS = {
67692
67923
  };
67693
67924
 
67694
67925
  // src/files/utils.ts
67695
- import * as path17 from "node:path";
67926
+ import * as path16 from "node:path";
67696
67927
  import { homedir as homedir5 } from "node:os";
67697
67928
  var WINDOWS_ABSOLUTE_PATH_RE = /^(?:[a-zA-Z]:[\\/]|[\\/]{2}[^\\/]+[\\/][^\\/]+)/;
67698
67929
  function isWindowsAbsolutePath(filePath) {
@@ -67713,13 +67944,13 @@ function formatBytes(bytes) {
67713
67944
  }
67714
67945
  function resolvePath2(inputPath) {
67715
67946
  if (inputPath.startsWith("~")) {
67716
- return path17.join(getHomeDirectory(), inputPath.slice(1));
67947
+ return path16.join(getHomeDirectory(), inputPath.slice(1));
67717
67948
  }
67718
67949
  if (isWindowsAbsolutePath(inputPath)) {
67719
67950
  return inputPath;
67720
67951
  }
67721
- if (!path17.isAbsolute(inputPath)) {
67722
- return path17.resolve(process.cwd(), inputPath);
67952
+ if (!path16.isAbsolute(inputPath)) {
67953
+ return path16.resolve(process.cwd(), inputPath);
67723
67954
  }
67724
67955
  return inputPath;
67725
67956
  }
@@ -67747,8 +67978,8 @@ var FileCache = class _FileCache {
67747
67978
  maxEntries;
67748
67979
  constructor(baseDir, maxEntries = DEFAULT_MAX_CACHE_ENTRIES) {
67749
67980
  this.baseDir = baseDir;
67750
- this.dataDir = path18.join(baseDir, "data");
67751
- this.indexPath = path18.join(baseDir, "index.json");
67981
+ this.dataDir = path17.join(baseDir, "data");
67982
+ this.indexPath = path17.join(baseDir, "index.json");
67752
67983
  this.maxEntries = maxEntries;
67753
67984
  this.index = this.loadIndex();
67754
67985
  this.ensureDirectories();
@@ -67814,15 +68045,15 @@ var FileCache = class _FileCache {
67814
68045
  // ============================================================================
67815
68046
  getStoragePath(hash) {
67816
68047
  const subdir = hash.slice(0, 2);
67817
- const fullDir = path18.join(this.dataDir, subdir);
68048
+ const fullDir = path17.join(this.dataDir, subdir);
67818
68049
  if (!fs18.existsSync(fullDir)) {
67819
68050
  fs18.mkdirSync(fullDir, { recursive: true });
67820
68051
  }
67821
- return path18.join(fullDir, hash);
68052
+ return path17.join(fullDir, hash);
67822
68053
  }
67823
68054
  getStorageRelativePath(hash) {
67824
68055
  const subdir = hash.slice(0, 2);
67825
- return path18.join(subdir, hash);
68056
+ return path17.join(subdir, hash);
67826
68057
  }
67827
68058
  async storeFile(sourcePath, name, mimeType) {
67828
68059
  const hash = await this.computeHash(sourcePath);
@@ -67884,7 +68115,7 @@ var FileCache = class _FileCache {
67884
68115
  getLocalPath(hash) {
67885
68116
  const entry = this.index.files[hash];
67886
68117
  if (!entry) return void 0;
67887
- return path18.join(this.dataDir, entry.localPath);
68118
+ return path17.join(this.dataDir, entry.localPath);
67888
68119
  }
67889
68120
  exists(hash) {
67890
68121
  return hash in this.index.files;
@@ -67964,7 +68195,7 @@ var FileCache = class _FileCache {
67964
68195
  this.index.pathHints[this.getPathHintKey(resolvedPath)] = {
67965
68196
  path: resolvedPath,
67966
68197
  hash,
67967
- name: name || entry?.name || path18.basename(resolvedPath),
68198
+ name: name || entry?.name || path17.basename(resolvedPath),
67968
68199
  size: stat2.size,
67969
68200
  mtimeMs: normalizedMtimeMs,
67970
68201
  lastSeenAt: Date.now(),
@@ -68012,7 +68243,7 @@ var FileCache = class _FileCache {
68012
68243
  deleteEntry(hash, entry) {
68013
68244
  entry = entry || this.index.files[hash];
68014
68245
  if (!entry) return;
68015
- const localPath = path18.join(this.dataDir, entry.localPath);
68246
+ const localPath = path17.join(this.dataDir, entry.localPath);
68016
68247
  if (fs18.existsSync(localPath)) {
68017
68248
  fs18.unlinkSync(localPath);
68018
68249
  }
@@ -68028,7 +68259,7 @@ var FileCache = class _FileCache {
68028
68259
  }
68029
68260
  clear() {
68030
68261
  for (const entry of Object.values(this.index.files)) {
68031
- const localPath = path18.join(this.dataDir, entry.localPath);
68262
+ const localPath = path17.join(this.dataDir, entry.localPath);
68032
68263
  if (fs18.existsSync(localPath)) {
68033
68264
  fs18.unlinkSync(localPath);
68034
68265
  }
@@ -68083,11 +68314,11 @@ var FileCache = class _FileCache {
68083
68314
  };
68084
68315
  }
68085
68316
  static getMimeType(fileName) {
68086
- const ext = path18.extname(fileName).toLowerCase();
68317
+ const ext = path17.extname(fileName).toLowerCase();
68087
68318
  return MIME_TYPES[ext] || "application/octet-stream";
68088
68319
  }
68089
68320
  static getFileTypeFromName(fileName) {
68090
- const baseName = path18.basename(fileName);
68321
+ const baseName = path17.basename(fileName);
68091
68322
  if (!baseName || baseName.startsWith(".")) {
68092
68323
  return void 0;
68093
68324
  }
@@ -68127,7 +68358,7 @@ var FileCache = class _FileCache {
68127
68358
 
68128
68359
  // src/files/session-file-manager.ts
68129
68360
  import * as fs19 from "node:fs";
68130
- import * as path19 from "node:path";
68361
+ import * as path18 from "node:path";
68131
68362
  var INITIAL_REFRESH_BACKOFF_MS = 6e4;
68132
68363
  var MAX_REFRESH_BACKOFF_MS = 60 * 60 * 1e3;
68133
68364
  var SessionFileManager = class {
@@ -68141,7 +68372,7 @@ var SessionFileManager = class {
68141
68372
  constructor(sessionId, sessionsDir, fileCache, authFingerprint) {
68142
68373
  this.sessionId = sessionId;
68143
68374
  this.sessionsDir = sessionsDir;
68144
- this.statePath = path19.join(this.sessionsDir, `${sessionId}-files.json`);
68375
+ this.statePath = path18.join(this.sessionsDir, `${sessionId}-files.json`);
68145
68376
  this.fileCache = fileCache;
68146
68377
  this.authFingerprint = authFingerprint;
68147
68378
  this.state = this.loadState();
@@ -68487,7 +68718,7 @@ var SessionFileManager = class {
68487
68718
  const files = fs19.readdirSync(sessionsDir);
68488
68719
  for (const file of files) {
68489
68720
  if (!file.endsWith(".json")) continue;
68490
- const filePath = path19.join(sessionsDir, file);
68721
+ const filePath = path18.join(sessionsDir, file);
68491
68722
  const stat2 = fs19.statSync(filePath);
68492
68723
  if (now - stat2.mtime.getTime() > maxAge) {
68493
68724
  fs19.unlinkSync(filePath);
@@ -68499,7 +68730,7 @@ var SessionFileManager = class {
68499
68730
 
68500
68731
  // src/files/ffmpeg-processor.ts
68501
68732
  import * as fs20 from "node:fs";
68502
- import * as path20 from "node:path";
68733
+ import * as path19 from "node:path";
68503
68734
  import { spawn } from "node:child_process";
68504
68735
  var FFmpegProcessor = class {
68505
68736
  ffmpegPath = "ffmpeg";
@@ -68603,7 +68834,7 @@ var FFmpegProcessor = class {
68603
68834
  }
68604
68835
  async compressVideo(inputPath, outputDir, info, opts, onProgress) {
68605
68836
  const outputName = `compressed_${Date.now()}.mp4`;
68606
- const outputPath = path20.join(outputDir, outputName);
68837
+ const outputPath = path19.join(outputDir, outputName);
68607
68838
  const adaptiveParams = this.calculateAdaptiveParams(info.duration, opts);
68608
68839
  const args = [
68609
68840
  "-i",
@@ -68665,9 +68896,9 @@ var FFmpegProcessor = class {
68665
68896
  }
68666
68897
  async extractKeyframes(inputPath, outputDir, info, opts, onProgress) {
68667
68898
  const interval = Math.floor(info.duration / opts.keyframeCount);
68668
- const framesDir = path20.join(outputDir, `keyframes_${Date.now()}`);
68899
+ const framesDir = path19.join(outputDir, `keyframes_${Date.now()}`);
68669
68900
  fs20.mkdirSync(framesDir, { recursive: true });
68670
- const outputPattern = path20.join(framesDir, "frame_%03d.jpg");
68901
+ const outputPattern = path19.join(framesDir, "frame_%03d.jpg");
68671
68902
  const args = [
68672
68903
  "-i",
68673
68904
  inputPath,
@@ -68685,7 +68916,7 @@ var FFmpegProcessor = class {
68685
68916
  onProgress?.(percent);
68686
68917
  }
68687
68918
  });
68688
- 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));
68689
68920
  if (frames.length < opts.keyframeCount / 2) {
68690
68921
  fs20.rmSync(framesDir, { recursive: true, force: true });
68691
68922
  fs20.mkdirSync(framesDir, { recursive: true });
@@ -68704,7 +68935,7 @@ var FFmpegProcessor = class {
68704
68935
  onProgress?.(percent);
68705
68936
  }
68706
68937
  });
68707
- 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));
68708
68939
  }
68709
68940
  const totalOutputSize = frames.reduce(
68710
68941
  (sum, f) => sum + fs20.statSync(f).size,
@@ -68728,7 +68959,7 @@ var FFmpegProcessor = class {
68728
68959
  const opts = { ...DEFAULT_PIC_OPTIONS, ...options };
68729
68960
  const ext = opts.format === "webp" ? ".webp" : ".jpg";
68730
68961
  const outputName = `optimized_${Date.now()}${ext}`;
68731
- const outputPath = path20.join(outputDir, outputName);
68962
+ const outputPath = path19.join(outputDir, outputName);
68732
68963
  const args = [
68733
68964
  "-i",
68734
68965
  inputPath,
@@ -68903,8 +69134,8 @@ var DIRECTORY_LISTING_TTL_MS = 1500;
68903
69134
  var FileManager = class {
68904
69135
  constructor(options) {
68905
69136
  this.options = options;
68906
- const baseDir = options.baseDir || path21.join(process.env.HOME || ".", ".pie", "files");
68907
- 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");
68908
69139
  this.fileCache = new FileCache(baseDir);
68909
69140
  this.authFingerprint = options.apiKey ? this.buildAuthFingerprint(options.apiKey) : void 0;
68910
69141
  this.sessionManager = new SessionFileManager(
@@ -68915,7 +69146,7 @@ var FileManager = class {
68915
69146
  );
68916
69147
  this.ffmpeg = new FFmpegProcessor(options.ffmpegPath);
68917
69148
  this.platform = normalizeFileManagerPlatform(options.platform);
68918
- this.tempDir = path21.join(baseDir, "temp");
69149
+ this.tempDir = path20.join(baseDir, "temp");
68919
69150
  this.ensureDirectories();
68920
69151
  }
68921
69152
  options;
@@ -68962,7 +69193,7 @@ var FileManager = class {
68962
69193
  // ============================================================================
68963
69194
  async stageLocal(filePath, options = {}) {
68964
69195
  const resolvedPath = resolvePath2(filePath);
68965
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69196
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68966
69197
  if (!fs21.existsSync(resolvedPath)) {
68967
69198
  throw new FileManagerError(
68968
69199
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -68992,7 +69223,7 @@ var FileManager = class {
68992
69223
  }
68993
69224
  async upload(filePath, options = {}) {
68994
69225
  const resolvedPath = resolvePath2(filePath);
68995
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69226
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68996
69227
  if (!fs21.existsSync(resolvedPath)) {
68997
69228
  throw new FileManagerError(
68998
69229
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -69082,7 +69313,7 @@ var FileManager = class {
69082
69313
  }
69083
69314
  const cachedResult = await this.tryReuseCachedSourceUpload(
69084
69315
  resolvedPath,
69085
- options.sourceName || path21.basename(resolvedPath),
69316
+ options.sourceName || path20.basename(resolvedPath),
69086
69317
  options.platform || this.platform
69087
69318
  );
69088
69319
  if (cachedResult) {
@@ -69099,9 +69330,9 @@ var FileManager = class {
69099
69330
  const result = await this.upload(optimizedPath, {
69100
69331
  ...options,
69101
69332
  purpose: "image",
69102
- sourceName: path21.basename(resolvedPath)
69333
+ sourceName: path20.basename(resolvedPath)
69103
69334
  });
69104
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69335
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69105
69336
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69106
69337
  try {
69107
69338
  fs21.unlinkSync(optimizedPath);
@@ -69122,7 +69353,7 @@ var FileManager = class {
69122
69353
  }
69123
69354
  const cachedResult = await this.tryReuseCachedSourceUpload(
69124
69355
  resolvedPath,
69125
- options.sourceName || path21.basename(resolvedPath),
69356
+ options.sourceName || path20.basename(resolvedPath),
69126
69357
  options.platform || this.platform
69127
69358
  );
69128
69359
  if (cachedResult) {
@@ -69172,9 +69403,9 @@ var FileManager = class {
69172
69403
  result = await this.upload(preprocessResult.output, {
69173
69404
  ...options,
69174
69405
  purpose: "video",
69175
- sourceName: path21.basename(resolvedPath)
69406
+ sourceName: path20.basename(resolvedPath)
69176
69407
  });
69177
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69408
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69178
69409
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69179
69410
  } else {
69180
69411
  throw new FileManagerError(
@@ -69466,7 +69697,7 @@ var FileManager = class {
69466
69697
  * Detect purpose from file path based on extension
69467
69698
  */
69468
69699
  detectPurpose(filePath) {
69469
- const ext = path21.extname(filePath).toLowerCase();
69700
+ const ext = path20.extname(filePath).toLowerCase();
69470
69701
  switch (ext) {
69471
69702
  case ".mp4":
69472
69703
  case ".mov":
@@ -69648,7 +69879,7 @@ var FileManager = class {
69648
69879
  if (!entry.name || entry.name.startsWith(".")) {
69649
69880
  continue;
69650
69881
  }
69651
- const fullPath = path21.join(resolvedDir, entry.name);
69882
+ const fullPath = path20.join(resolvedDir, entry.name);
69652
69883
  if (entry.isDirectory()) {
69653
69884
  listing.directories.push({ name: entry.name, path: fullPath });
69654
69885
  continue;
@@ -69689,7 +69920,7 @@ var FileManager = class {
69689
69920
  return listing;
69690
69921
  }
69691
69922
  invalidateDirectoryListingCacheForPath(filePath) {
69692
- this.directoryListingCache.delete(path21.dirname(resolvePath2(filePath)));
69923
+ this.directoryListingCache.delete(path20.dirname(resolvePath2(filePath)));
69693
69924
  }
69694
69925
  buildAuthFingerprint(apiKey) {
69695
69926
  return crypto2.createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
@@ -69738,8 +69969,8 @@ function createFileManagerForModel(params) {
69738
69969
  sessionId: params.sessionId,
69739
69970
  platform: platform2,
69740
69971
  apiKey: params.apiKey,
69741
- baseDir: path22.join(params.configDir, "files"),
69742
- sessionsDir: path22.join(params.configDir, "sessions")
69972
+ baseDir: path21.join(params.configDir, "files"),
69973
+ sessionsDir: path21.join(params.configDir, "sessions")
69743
69974
  });
69744
69975
  switch (getFileManagerAdapterKind(platform2)) {
69745
69976
  case "kimi":
@@ -69779,7 +70010,7 @@ async function prepareInlineMediaContent(params) {
69779
70010
  const limitBytes = limitMb * 1024 * 1024;
69780
70011
  if (stat2.size > limitBytes) {
69781
70012
  throw new Error(
69782
- `${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.`
69783
70014
  );
69784
70015
  }
69785
70016
  const data = await fs22.readFile(params.request.absolutePath, "base64");
@@ -69818,7 +70049,7 @@ async function prepareFileContent(configDir, params) {
69818
70049
  });
69819
70050
  if (params.request.kind === "document") {
69820
70051
  const upload2 = await manager.upload(params.request.absolutePath, {
69821
- sourceName: path22.basename(params.request.displayPath),
70052
+ sourceName: path21.basename(params.request.displayPath),
69822
70053
  purpose: "file-extract"
69823
70054
  });
69824
70055
  await waitForReady({ manager, fileId: upload2.fileId, timeoutMs: 12e4, signal: params.request.signal });
@@ -69841,8 +70072,8 @@ async function prepareFileContent(configDir, params) {
69841
70072
  }
69842
70073
  };
69843
70074
  }
69844
- 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, {
69845
- 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),
69846
70077
  purpose: params.request.kind === "image" ? "image" : "file-extract"
69847
70078
  });
69848
70079
  if (params.request.kind !== "video") {
@@ -70012,6 +70243,53 @@ function createCliFileSystemTools(cwd, options) {
70012
70243
  });
70013
70244
  }
70014
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
+
70015
70293
  // src/runtime/yolo-grant.ts
70016
70294
  var YoloGrantTracker = class {
70017
70295
  untilMs = 0;
@@ -77106,35 +77384,40 @@ var RuntimeModeBlockInjector = class {
77106
77384
  // src/runtime/host-envelope-registry.ts
77107
77385
  var sharedCliEnvelopeRegistry;
77108
77386
  function cliHostEnvelopeRegistry() {
77109
- if (sharedCliEnvelopeRegistry) return sharedCliEnvelopeRegistry;
77110
- sharedCliEnvelopeRegistry = createHostEnvelopeProjectorRegistry();
77387
+ return sharedCliEnvelopeRegistry ??= createCliHostEnvelopeRegistry();
77388
+ }
77389
+ function createCliHostEnvelopeRegistry() {
77390
+ const registry = createHostEnvelopeProjectorRegistry();
77111
77391
  for (const { kind, version } of runtimeModeEnvelopeRegistry().listPins()) {
77112
- sharedCliEnvelopeRegistry.register({
77392
+ registry.register({
77113
77393
  kind,
77114
77394
  version,
77115
77395
  project: (data) => runtimeModeEnvelopeRegistry().resolve(kind, version).project(data)
77116
77396
  });
77117
77397
  }
77118
- return sharedCliEnvelopeRegistry;
77398
+ return registry;
77119
77399
  }
77120
- function registerExternalHostEnvelopeProjector(projector) {
77121
- cliHostEnvelopeRegistry().register(projector);
77400
+ function registerExternalHostEnvelopeProjector(projector, registry = cliHostEnvelopeRegistry()) {
77401
+ registry.register(projector);
77122
77402
  return () => {
77123
77403
  };
77124
77404
  }
77125
77405
  function resetCliHostEnvelopeRegistry() {
77126
77406
  sharedCliEnvelopeRegistry = void 0;
77127
77407
  }
77128
- function cliHostEnvelopeKinds() {
77129
- 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);
77130
77413
  }
77131
- function projectCliHostEnvelopes(messages, onProjection) {
77132
- return projectHostEnvelopes(messages, cliHostEnvelopeRegistry(), onProjection);
77414
+ function cliCompactionModelView(message) {
77415
+ return projectCliHostEnvelopes([message])[0];
77133
77416
  }
77134
77417
  function isIntakeHostContextError(value) {
77135
77418
  return value.code !== void 0;
77136
77419
  }
77137
- function intakeHostContextBlock(value) {
77420
+ function intakeHostContextBlock(value, registry = cliHostEnvelopeRegistry()) {
77138
77421
  if (!isHostContextBlock(value)) {
77139
77422
  return {
77140
77423
  ok: false,
@@ -77143,12 +77426,12 @@ function intakeHostContextBlock(value) {
77143
77426
  };
77144
77427
  }
77145
77428
  const block = value;
77146
- const projector = cliHostEnvelopeRegistry().resolve(block.kind, block.version);
77429
+ const projector = registry.resolve(block.kind, block.version);
77147
77430
  if (!projector) {
77148
77431
  return {
77149
77432
  ok: false,
77150
77433
  code: "host_context_unknown_projector",
77151
- 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(", ")}`
77152
77435
  };
77153
77436
  }
77154
77437
  try {
@@ -77163,8 +77446,8 @@ function intakeHostContextBlock(value) {
77163
77446
  }
77164
77447
  return { ok: true, block };
77165
77448
  }
77166
- function intakeHostContextFailure(value) {
77167
- const result = intakeHostContextBlock(value);
77449
+ function intakeHostContextFailure(value, registry = cliHostEnvelopeRegistry()) {
77450
+ const result = intakeHostContextBlock(value, registry);
77168
77451
  return isIntakeHostContextError(result) ? { code: result.code, message: result.message } : void 0;
77169
77452
  }
77170
77453
 
@@ -77196,13 +77479,14 @@ async function handleCompactCommand(host) {
77196
77479
  const preparation = prepareSessionCompaction({
77197
77480
  sessionManager: host.sessionManager,
77198
77481
  messages: existingMessages,
77482
+ modelView: cliCompactionModelView,
77199
77483
  budgetTokens: host.options.model?.contextWindow
77200
77484
  });
77201
77485
  if (preparation.entries.length === 0) {
77202
77486
  host.showStatus("Need at least one real user message before compacting");
77203
77487
  return;
77204
77488
  }
77205
- const tokensBefore = calculateTotalTokens(preparation.messages);
77489
+ const tokensBefore = preparation.inputTokens;
77206
77490
  const compactOptions = {
77207
77491
  model: host.options.model,
77208
77492
  apiKey: host.options.apiKey,
@@ -78420,6 +78704,7 @@ var FileStatusMonitor = class {
78420
78704
 
78421
78705
  // src/files/mention/mention-handler.ts
78422
78706
  import * as fs26 from "node:fs";
78707
+ import { randomUUID } from "node:crypto";
78423
78708
  import * as path28 from "node:path";
78424
78709
  var MentionHandler = class {
78425
78710
  fileManager;
@@ -78513,75 +78798,11 @@ var MentionHandler = class {
78513
78798
  this.onInsertMention(mention, queryToken);
78514
78799
  this.onStatus?.(`Added ${option.name} (already uploaded to ${this.fileManager.getPlatformDisplayName()})`);
78515
78800
  } else if (option.type === "upload" && option.path) {
78516
- this.pendingUploadCount += 1;
78517
- const pendingMention = this.formatPendingMention(option.name);
78518
- this.onInsertMention(pendingMention, queryToken);
78519
- const pendingStatusKey = `${option.path}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
78520
- try {
78521
- if (option.cacheStatus === "uploaded") {
78522
- this.pushPendingStatus(
78523
- pendingStatusKey,
78524
- `Preparing ${option.name} (reusing upload from ${this.fileManager.getPlatformDisplayName()})...`
78525
- );
78526
- } else if (option.cacheStatus === "cached") {
78527
- this.pushPendingStatus(
78528
- pendingStatusKey,
78529
- `Preparing ${option.name} (reusing local cache)...`
78530
- );
78531
- } else {
78532
- this.pushPendingStatus(pendingStatusKey, `Preparing ${option.name}...`);
78533
- }
78534
- const mimeType = FileCache.getMimeType(option.path);
78535
- const isMedia = mimeType.startsWith("image/") || mimeType.startsWith("video/");
78536
- const shouldStageLocally = !isMedia || !this.fileManager.hasAdapter();
78537
- if (isMedia && shouldStageLocally) {
78538
- this.pushPendingStatus(pendingStatusKey, `Staging ${option.name} locally (no upload API for this provider)...`);
78539
- }
78540
- const onProgress = (progress, stage) => {
78541
- this.pushPendingStatus(
78542
- pendingStatusKey,
78543
- this.formatUploadProgress(option.name, progress, stage)
78544
- );
78545
- };
78546
- const result = shouldStageLocally ? await this.fileManager.stageLocal(option.path, { sourceName: option.name }) : mimeType.startsWith("video/") ? await this.fileManager.uploadVideo(option.path, {
78547
- sourceName: option.name,
78548
- onProgress
78549
- }) : mimeType.startsWith("image/") ? await this.fileManager.uploadPic(option.path, {
78550
- sourceName: option.name,
78551
- onProgress
78552
- }) : await this.fileManager.upload(option.path, {
78553
- sourceName: option.name,
78554
- onProgress
78555
- });
78556
- if (result.success) {
78557
- const mention = this.formatMention(result.name, result.fileId);
78558
- this.onInsertMention(mention, pendingMention);
78559
- this.clearPendingStatus(pendingStatusKey);
78560
- this.onStatus?.(shouldStageLocally ? `Added local file ${result.name}` : `Added ${result.name}`);
78561
- } else {
78562
- this.onInsertMention("", pendingMention);
78563
- this.clearPendingStatus(pendingStatusKey);
78564
- this.onError?.("File upload failed. Please try again.");
78565
- }
78566
- } catch (err) {
78567
- this.onInsertMention("", pendingMention);
78568
- this.clearPendingStatus(pendingStatusKey);
78569
- const errorMessage4 = err instanceof Error ? err.message : String(err);
78570
- if (errorMessage4.includes("PLATFORM_NOT_SUPPORTED")) {
78571
- this.onError?.("The current AI model does not support file uploads.");
78572
- } else if (errorMessage4.includes("ENOENT")) {
78573
- this.onError?.("File does not exist or cannot be accessed.");
78574
- } else if (errorMessage4.includes("EACCES") || errorMessage4.includes("PERMISSION")) {
78575
- this.onError?.("Permission denied while accessing the file.");
78576
- } else if (errorMessage4.includes("network") || errorMessage4.includes("ETIMEDOUT") || errorMessage4.includes("ECONNREFUSED")) {
78577
- this.onError?.("Network error. Check the connection and try again.");
78578
- } else {
78579
- this.onError?.(`Upload failed: ${errorMessage4}`);
78580
- }
78581
- } finally {
78582
- this.clearPendingStatus(pendingStatusKey);
78583
- this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78584
- }
78801
+ await this.attachFileFromPath(option.path, {
78802
+ sourceName: option.name,
78803
+ queryToken,
78804
+ cacheStatus: option.cacheStatus
78805
+ });
78585
78806
  } else {
78586
78807
  this.onError?.("Invalid file option.");
78587
78808
  }
@@ -78589,6 +78810,110 @@ var MentionHandler = class {
78589
78810
  this.onHidePicker();
78590
78811
  this.isPickerVisible = false;
78591
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
+ }
78592
78917
  /**
78593
78918
  * Parse mentions in text and convert to platform format
78594
78919
  */
@@ -79328,6 +79653,10 @@ function setupInteractiveKeybindings(host) {
79328
79653
  host.handleInterrupt();
79329
79654
  return { consume: true };
79330
79655
  }
79656
+ if (!host.mentionPickerComponent && host.keybindings.matches(data, "pasteImage")) {
79657
+ host.handlePasteImage();
79658
+ return { consume: true };
79659
+ }
79331
79660
  if (host.keybindings.matches(data, "expandTools")) {
79332
79661
  host.toggleToolsExpansion();
79333
79662
  return { consume: true };
@@ -79348,6 +79677,473 @@ function setupInteractiveKeybindings(host) {
79348
79677
  });
79349
79678
  }
79350
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
+
79351
80147
  // src/runtime/interactive-queue-ui.ts
79352
80148
  function updatePendingMessagesDisplay(host) {
79353
80149
  host.pendingMessagesContainer.clear();
@@ -79359,11 +80155,11 @@ function updatePendingMessagesDisplay(host) {
79359
80155
  const steerMessages = queuedInputs.filter((m2) => m2.mode === "steer");
79360
80156
  const followUpMessages = queuedInputs.filter((m2) => m2.mode === "followUp");
79361
80157
  for (const msg of steerMessages) {
79362
- const text = theme.fg("dim", ` [steer] ${truncate(msg.displayText, 50)}`);
80158
+ const text = theme.fg("dim", ` [steer] ${truncate2(msg.displayText, 50)}`);
79363
80159
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79364
80160
  }
79365
80161
  for (const msg of followUpMessages) {
79366
- const text = theme.fg("dim", ` [followUp] ${truncate(msg.displayText, 50)}`);
80162
+ const text = theme.fg("dim", ` [followUp] ${truncate2(msg.displayText, 50)}`);
79367
80163
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79368
80164
  }
79369
80165
  const dequeueHint = host.keybindings.getKeys("dequeue")[0] || "alt+up";
@@ -79472,7 +80268,7 @@ function handleSteer(host) {
79472
80268
  void host.editor.onSubmit(text);
79473
80269
  }
79474
80270
  }
79475
- function truncate(text, maxLength) {
80271
+ function truncate2(text, maxLength) {
79476
80272
  if (text.length <= maxLength) return text;
79477
80273
  return text.slice(0, maxLength) + "...";
79478
80274
  }
@@ -79572,6 +80368,12 @@ var FooterComponent = class {
79572
80368
  * derived from this at render time so long silent tool runs (e.g.
79573
80369
  * subagents) still visibly tick — a moving elapsed says "alive". */
79574
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;
79575
80377
  /** Keyed ambient status slots for extensions (ctx.ui.setStatus). */
79576
80378
  extensionStatuses = new ExtensionStatusSlots();
79577
80379
  /** Set callback to request re-render when animation updates (pass true to force) */
@@ -79589,6 +80391,17 @@ var FooterComponent = class {
79589
80391
  this.busySince = null;
79590
80392
  this.stopSpinner();
79591
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
+ }
79592
80405
  }
79593
80406
  setAutoCompactEnabled(enabled) {
79594
80407
  this.autoCompactEnabled = enabled;
@@ -79597,6 +80410,7 @@ var FooterComponent = class {
79597
80410
  }
79598
80411
  dispose() {
79599
80412
  this.stopSpinner();
80413
+ this.stopWaitingTimer();
79600
80414
  }
79601
80415
  /** Start spinner animation */
79602
80416
  startSpinner() {
@@ -79614,6 +80428,20 @@ var FooterComponent = class {
79614
80428
  this.spinnerTimer = null;
79615
80429
  }
79616
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
+ }
79617
80445
  /** Get current spinner character */
79618
80446
  getSpinner() {
79619
80447
  if (this.state.status !== "thinking" && this.state.status !== "processing") {
@@ -79688,23 +80516,27 @@ var FooterComponent = class {
79688
80516
  processing: "warning",
79689
80517
  error: "error"
79690
80518
  };
79691
- const spinner = this.getSpinner();
79692
- let statusText;
79693
- if (state.lifecycleEvent && state.status !== "ready") {
79694
- statusText = state.lifecycleEvent;
79695
- } else {
79696
- const statusDisplayMap = {
79697
- ready: "Ready",
79698
- thinking: "Thinking",
79699
- processing: "Processing",
79700
- error: "Error"
79701
- };
79702
- statusText = statusDisplayMap[state.status] || state.status;
79703
- }
79704
- const isBusy = state.status === "thinking" || state.status === "processing";
79705
- const busyElapsed = this.busySince !== null && isBusy ? ` \xB7 ${formatElapsed(performance.now() - this.busySince)}` : "";
79706
- const statusWithSpinner = `${spinner ? `${spinner} ` : ""}${statusText}${busyElapsed}`;
79707
- 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);
79708
80540
  let statsLeft = statsParts.join(" ");
79709
80541
  statsLeft += this.extensionStatuses.format();
79710
80542
  const modelName = state.modelName || "no-model";
@@ -79835,8 +80667,8 @@ function calculateCliContextUsage(params) {
79835
80667
  };
79836
80668
  }
79837
80669
  function createCliAutoCompactDecision(params) {
79838
- const errorMessage4 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
79839
- if (errorMessage4 && params.isContextOverflowError(errorMessage4)) {
80670
+ const errorMessage5 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
80671
+ if (errorMessage5 && params.isContextOverflowError(errorMessage5)) {
79840
80672
  return { reason: "overflow", willRetry: true };
79841
80673
  }
79842
80674
  if (!params.autoCompactEnabled || params.contextWindow <= 0) {
@@ -79890,7 +80722,7 @@ async function createCliAutoCompactionResult(params) {
79890
80722
  systemPrompt: params.systemPrompt,
79891
80723
  signal: params.context.signal
79892
80724
  };
79893
- 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);
79894
80726
  if (params.context.signal?.aborted) {
79895
80727
  return { aborted: true };
79896
80728
  }
@@ -79898,7 +80730,7 @@ async function createCliAutoCompactionResult(params) {
79898
80730
  summary,
79899
80731
  retainedTail,
79900
80732
  ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79901
- tokensBefore: calculateTotalTokens(params.context.messages)
80733
+ tokensBefore: params.context.preparation?.inputTokens ?? calculateTotalTokens(projectCliHostEnvelopes(params.context.messages, void 0, params.envelopeRegistry))
79902
80734
  };
79903
80735
  }
79904
80736
  function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
@@ -79923,153 +80755,6 @@ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
79923
80755
  });
79924
80756
  }
79925
80757
 
79926
- // src/runtime/retry-policy.ts
79927
- 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;
79928
- 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;
79929
- 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;
79930
- var USER_ABORT_PATTERN = /request was aborted|local abort signal|user abort|user_interrupt|interrupted by user|esc pressed|ctrl\+c/i;
79931
- 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;
79932
- 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;
79933
- var INVALID_FILE_REFERENCE_PATTERN = /not the owner of this file|owner of this file|invalid_authentication_error/i;
79934
- var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schema|schema validation|invalid parameter|unsupported parameter|malformed/i;
79935
- var CLI_RETRY_MAX_RETRIES = 3;
79936
- var CLI_RETRY_BASE_DELAY_MS = 2e3;
79937
- var CLI_TRANSIENT_NETWORK_MAX_RETRIES = 10;
79938
- var CLI_RETRY_TOTAL_MAX_RETRIES = 12;
79939
- var CLI_RETRY_MAX_DELAY_MS = 6e4;
79940
- function isCliContextOverflowError(errorMessage4) {
79941
- 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(
79942
- errorMessage4
79943
- );
79944
- }
79945
- function createClassification(kind, message, overrides) {
79946
- return {
79947
- kind,
79948
- message,
79949
- retryable: false,
79950
- compactable: false,
79951
- userAbort: false,
79952
- terminal: true,
79953
- ...overrides
79954
- };
79955
- }
79956
- function classifyCliFailure(context) {
79957
- const errorMessage4 = typeof context === "string" ? context : context.errorMessage;
79958
- const structuredReplayClass = typeof context === "string" ? void 0 : context.message?.role === "assistant" ? context.message.replayClass : void 0;
79959
- if (isCliContextOverflowError(errorMessage4)) {
79960
- return createClassification("context_overflow", errorMessage4, {
79961
- compactable: true,
79962
- terminal: false
79963
- });
79964
- }
79965
- if (USER_ABORT_PATTERN.test(errorMessage4)) {
79966
- return createClassification("user_abort", errorMessage4, {
79967
- userAbort: true,
79968
- terminal: false
79969
- });
79970
- }
79971
- if (INVALID_FILE_REFERENCE_PATTERN.test(errorMessage4)) {
79972
- return createClassification("invalid_file_reference", errorMessage4);
79973
- }
79974
- if (AUTH_CONFIG_PATTERN.test(errorMessage4) || QUOTA_EXHAUSTED_PATTERN.test(errorMessage4)) {
79975
- return createClassification("auth_config", errorMessage4);
79976
- }
79977
- if (TLS_VALIDATION_ERROR_PATTERN.test(errorMessage4)) {
79978
- return createClassification("tls_config", errorMessage4, {
79979
- retryable: true,
79980
- terminal: false
79981
- });
79982
- }
79983
- if (structuredReplayClass === "stream_lost" || structuredReplayClass === "stream_lost_partial" || TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage4)) {
79984
- return createClassification("transient_network", errorMessage4, {
79985
- retryable: true,
79986
- terminal: false
79987
- });
79988
- }
79989
- if (errorMessage4.includes(EMPTY_ASSISTANT_CONTENT_ERROR)) {
79990
- return createClassification("provider_transient", errorMessage4, {
79991
- retryable: true,
79992
- terminal: false
79993
- });
79994
- }
79995
- if (errorMessage4.includes(OPENAI_COMPAT_PROTOCOL_ERROR_CODE)) {
79996
- return createClassification("provider_transient", errorMessage4, {
79997
- retryable: true,
79998
- terminal: false
79999
- });
80000
- }
80001
- if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage4)) {
80002
- return createClassification("provider_transient", errorMessage4, {
80003
- retryable: true,
80004
- terminal: false
80005
- });
80006
- }
80007
- if (INVALID_REQUEST_PATTERN.test(errorMessage4)) {
80008
- return createClassification("invalid_request", errorMessage4);
80009
- }
80010
- return createClassification("unknown", errorMessage4);
80011
- }
80012
- function isCliRetryableError(errorMessage4) {
80013
- return classifyCliFailure(errorMessage4).retryable;
80014
- }
80015
- function getCliRetryBaseDelayMs() {
80016
- const override = process.env.PIE_TEST_CLI_RETRY_BASE_DELAY_MS;
80017
- if (!override) {
80018
- return CLI_RETRY_BASE_DELAY_MS;
80019
- }
80020
- const parsed = Number(override);
80021
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_BASE_DELAY_MS;
80022
- }
80023
- function getCliTransientNetworkMaxRetries() {
80024
- const override = process.env.PIE_RETRY_TRANSIENT_NETWORK_MAX_RETRIES;
80025
- if (!override) {
80026
- return CLI_TRANSIENT_NETWORK_MAX_RETRIES;
80027
- }
80028
- const parsed = Number(override);
80029
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TRANSIENT_NETWORK_MAX_RETRIES;
80030
- }
80031
- function getCliRetryMaxDelayMs() {
80032
- const override = process.env.PIE_RETRY_MAX_DELAY_MS;
80033
- if (!override) {
80034
- return CLI_RETRY_MAX_DELAY_MS;
80035
- }
80036
- const parsed = Number(override);
80037
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_MAX_DELAY_MS;
80038
- }
80039
- function getCliRetryTotalMaxRetries() {
80040
- const override = process.env.PIE_RETRY_TOTAL_MAX_RETRIES;
80041
- if (!override) {
80042
- return CLI_RETRY_TOTAL_MAX_RETRIES;
80043
- }
80044
- const parsed = Number(override);
80045
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_RETRY_TOTAL_MAX_RETRIES;
80046
- }
80047
- function createCliRetryPolicy(isRetryableErrorOverride) {
80048
- return {
80049
- isRetryableError: isRetryableErrorOverride ?? isCliRetryableError,
80050
- maxRetries: CLI_RETRY_MAX_RETRIES,
80051
- baseDelayMs: getCliRetryBaseDelayMs(),
80052
- maxDelayMs: getCliRetryMaxDelayMs(),
80053
- maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : failure.replayExempt === true ? getCliTimeoutReplayMaxRetries() : CLI_RETRY_MAX_RETRIES,
80054
- maxTotalRetries: getCliRetryTotalMaxRetries(),
80055
- systemicReplayStallLimit: getCliSystemicReplayStallLimit()
80056
- };
80057
- }
80058
- var CLI_TIMEOUT_REPLAY_MAX_RETRIES = 5;
80059
- function getCliTimeoutReplayMaxRetries() {
80060
- const override = process.env.PIE_TIMEOUT_REPLAY_MAX_RETRIES;
80061
- if (!override) return CLI_TIMEOUT_REPLAY_MAX_RETRIES;
80062
- const parsed = Number(override);
80063
- return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TIMEOUT_REPLAY_MAX_RETRIES;
80064
- }
80065
- var CLI_SYSTEMIC_REPLAY_STALL_LIMIT = 3;
80066
- function getCliSystemicReplayStallLimit() {
80067
- const override = process.env.PIE_SYSTEMIC_REPLAY_STALL_LIMIT;
80068
- if (!override) return CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
80069
- const parsed = Number(override);
80070
- return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
80071
- }
80072
-
80073
80758
  // src/runtime/interactive-context-ui.ts
80074
80759
  function calculateContextUsage(host) {
80075
80760
  const messages = host.agent?.state.messages ?? [];
@@ -80246,23 +80931,23 @@ function setupAutocomplete(host) {
80246
80931
  };
80247
80932
  host.editor.setAutocompleteProvider(provider);
80248
80933
  }
80249
- function isContextOverflowError(errorMessage4) {
80250
- return isCliContextOverflowError(errorMessage4);
80934
+ function isContextOverflowError(errorMessage5) {
80935
+ return isCliContextOverflowError(errorMessage5);
80251
80936
  }
80252
80937
  function shouldAutoCompact(host, contextTokens, contextWindow) {
80253
80938
  if (!host.options.settingsManager.getAutoCompactEnabled()) return false;
80254
80939
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
80255
80940
  return shouldCliAutoCompactTokens({ tokens: contextTokens, contextWindow, reserveTokens });
80256
80941
  }
80257
- function isRetryableError(errorMessage4) {
80258
- return isCliRetryableError(errorMessage4);
80942
+ function isRetryableError(errorMessage5) {
80943
+ return isCliRetryableError(errorMessage5);
80259
80944
  }
80260
- function isInvalidFileReferenceError(errorMessage4) {
80945
+ function isInvalidFileReferenceError(errorMessage5) {
80261
80946
  return /not the owner of this file|owner of this file|invalid_authentication_error/i.test(
80262
- errorMessage4
80947
+ errorMessage5
80263
80948
  );
80264
80949
  }
80265
- function recoverFromInvalidFileReferenceError(host, errorMessage4) {
80950
+ function recoverFromInvalidFileReferenceError(host, errorMessage5) {
80266
80951
  if (!host.fileManager || !host.agent) return;
80267
80952
  const messages = host.agent.state.messages.slice();
80268
80953
  const invalidFileIds = /* @__PURE__ */ new Set();
@@ -80280,7 +80965,7 @@ function recoverFromInvalidFileReferenceError(host, errorMessage4) {
80280
80965
  }
80281
80966
  if (invalidFileIds.size === 0) return;
80282
80967
  for (const fileId of invalidFileIds) {
80283
- host.fileManager.markFileReferenceError(fileId, errorMessage4);
80968
+ host.fileManager.markFileReferenceError(fileId, errorMessage5);
80284
80969
  }
80285
80970
  let changed = false;
80286
80971
  const sanitizedMessages = messages.map((message) => {
@@ -80574,9 +81259,9 @@ function clearStatusLine(host) {
80574
81259
  }
80575
81260
  if (removed) host.ui.requestRender();
80576
81261
  }
80577
- function showError(host, errorMessage4) {
81262
+ function showError(host, errorMessage5) {
80578
81263
  host.chatContainer.addChild(new Spacer(1));
80579
- 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));
80580
81265
  host.ui.requestRender();
80581
81266
  }
80582
81267
  async function executeCommand(host, command) {
@@ -80772,22 +81457,42 @@ function scheduleContinuationTimeout(host) {
80772
81457
 
80773
81458
  // src/runtime/interactive-controller-setup.ts
80774
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
+ }
80775
81480
  if (!host.extensionRunner) {
80776
- return void 0;
81481
+ return messages === context.messages ? void 0 : { messages };
80777
81482
  }
80778
81483
  const baseSystemPrompt = context.baseSystemPrompt ?? host.buildSystemPrompt();
80779
81484
  const result = await host.extensionRunner.emitBeforeAgentStart({
80780
81485
  type: "before_agent_start",
80781
- messages: context.messages,
81486
+ messages,
80782
81487
  baseSystemPrompt,
80783
81488
  source: context.source
80784
81489
  });
80785
81490
  if (!result) {
80786
- return { systemPrompt: baseSystemPrompt };
81491
+ return { systemPrompt: baseSystemPrompt, messages };
80787
81492
  }
80788
81493
  return {
80789
81494
  systemPrompt: result.systemPrompt ?? baseSystemPrompt,
80790
- messages: result.messages?.length ? [...context.messages, ...result.messages] : context.messages
81495
+ messages: result.messages?.length ? [...messages, ...result.messages] : messages
80791
81496
  };
80792
81497
  }
80793
81498
  function createInteractiveAgentSessionController(host) {
@@ -80807,6 +81512,7 @@ function createInteractiveAgentSessionController(host) {
80807
81512
  let retryElapsedStartedAt = 0;
80808
81513
  let executionWork;
80809
81514
  let autoCompactBeforeStats = null;
81515
+ let lastShownCompactionError = null;
80810
81516
  const attention = cliAttention(host);
80811
81517
  const stopRetryElapsedTicker = () => {
80812
81518
  if (retryElapsedTicker !== void 0) {
@@ -80860,9 +81566,10 @@ function createInteractiveAgentSessionController(host) {
80860
81566
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80861
81567
  classifyFailure: (context) => classifyCliFailure(context),
80862
81568
  retry: createCliRetryPolicy(
80863
- (errorMessage4) => !host._isContextOverflowError(errorMessage4) && isCliRetryableError(errorMessage4)
81569
+ (errorMessage5) => !host._isContextOverflowError(errorMessage5) && isCliRetryableError(errorMessage5)
80864
81570
  ),
80865
81571
  compaction: {
81572
+ modelView: cliCompactionModelView,
80866
81573
  compact: async (context) => {
80867
81574
  const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
80868
81575
  type: "session:before_compact",
@@ -81013,7 +81720,10 @@ function createInteractiveAgentSessionController(host) {
81013
81720
  if (state.mode === "todo" && state.lifecycle === "paused" && state.steps.length > 0) {
81014
81721
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
81015
81722
  }
81016
- host.showError(event.failure.message);
81723
+ if (event.failure.message !== lastShownCompactionError) {
81724
+ host.showError(event.failure.message);
81725
+ }
81726
+ lastShownCompactionError = null;
81017
81727
  host.setControllerBusy(false);
81018
81728
  } else if (event.type === "turn_cancelled") {
81019
81729
  const outcome = host.executionState.abort();
@@ -81057,14 +81767,17 @@ function createInteractiveAgentSessionController(host) {
81057
81767
  willRetry: event.willRetry
81058
81768
  });
81059
81769
  } else if (event.type === "auto_compaction_end") {
81770
+ const stillRunning = host.agent.state.isStreaming;
81060
81771
  host.isCompacting = false;
81061
- host.isProcessing = false;
81772
+ host.isProcessing = stillRunning;
81773
+ if (stillRunning) host.setControllerBusy(true);
81062
81774
  host.setSubmitLock(false);
81063
81775
  if (event.errorMessage) {
81064
81776
  const prefix = event.reason === "overflow" ? "Context overflow recovery failed" : "Auto-compaction failed";
81065
81777
  host.showError(`${prefix}: ${event.errorMessage}`);
81066
- host.footer.setState({ status: "ready" });
81067
- host.setControllerBusy(false);
81778
+ lastShownCompactionError = event.willRetry || stillRunning ? event.errorMessage : null;
81779
+ host.footer.setState({ status: stillRunning ? "processing" : "ready" });
81780
+ host.setControllerBusy(stillRunning);
81068
81781
  autoCompactBeforeStats = null;
81069
81782
  return;
81070
81783
  }
@@ -81080,7 +81793,7 @@ function createInteractiveAgentSessionController(host) {
81080
81793
  const contextWindow = host.options.model?.contextWindow ?? 0;
81081
81794
  const contextPercent = contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0;
81082
81795
  host.footer.setState({
81083
- status: "ready",
81796
+ status: stillRunning ? "processing" : "ready",
81084
81797
  lifecycleEvent: "auto_compact",
81085
81798
  inputTokens: contextUsage.input,
81086
81799
  outputTokens: contextUsage.output,
@@ -81323,8 +82036,8 @@ async function handleDirectBash(host, command) {
81323
82036
  outputContainer.addChild(new Spacer(1));
81324
82037
  host.chatContainer.addChild(outputContainer);
81325
82038
  host.ui.requestRender();
81326
- const { spawn: spawn2 } = await import("child_process");
81327
- const child = spawn2(shellCommand.file, shellCommand.args, {
82039
+ const { spawn: spawn3 } = await import("child_process");
82040
+ const child = spawn3(shellCommand.file, shellCommand.args, {
81328
82041
  cwd: process.cwd(),
81329
82042
  env: process.env
81330
82043
  });
@@ -81363,15 +82076,14 @@ async function handleDirectBash(host, command) {
81363
82076
  // src/extensions/interactive-extension-ui.ts
81364
82077
  function createInteractiveExtensionUIContext(host) {
81365
82078
  const interact = async (run) => {
81366
- const attention = cliAttention(host);
81367
- const request = attention.request("input-required");
82079
+ const request = beginExtensionWaiting(host, "input-required");
81368
82080
  let resolution = "cancelled";
81369
82081
  try {
81370
82082
  const value = await run();
81371
82083
  resolution = interactionAttentionResolution({ value });
81372
82084
  return value;
81373
82085
  } finally {
81374
- attention.resolve(request, resolution);
82086
+ endExtensionWaiting(host, request, resolution);
81375
82087
  }
81376
82088
  };
81377
82089
  return {
@@ -81393,23 +82105,13 @@ function createInteractiveExtensionUIContext(host) {
81393
82105
  }
81394
82106
  function installInteractiveInteractionHandler(host) {
81395
82107
  registerInteractionHandler(async (request) => {
81396
- const attention = cliAttention(host);
81397
82108
  let attentionRequest;
81398
82109
  const beginWaitingForUser = () => {
81399
- host.activeInteractionCount++;
81400
- host.setWaitingForUser(true);
81401
- host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
81402
82110
  const reason = interactionAttentionReason(request);
81403
- if (reason) attentionRequest = attention.request(reason, "id" in request ? request.id : void 0);
81404
- host.safeRequestRender();
82111
+ attentionRequest = beginExtensionWaiting(host, reason, "id" in request ? request.id : void 0);
81405
82112
  };
81406
- const endWaitingForUser = () => {
81407
- host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
81408
- if (host.activeInteractionCount === 0) {
81409
- host.setWaitingForUser(false);
81410
- }
81411
- host.footer.setState({ lifecycleEvent: "\u25B6 resuming" });
81412
- host.safeRequestRender();
82113
+ const endWaitingForUser = (resolution) => {
82114
+ endExtensionWaiting(host, attentionRequest, resolution);
81413
82115
  };
81414
82116
  const withTimeout2 = async (createPromise) => {
81415
82117
  const controller = new AbortController();
@@ -81438,8 +82140,7 @@ function installInteractiveInteractionHandler(host) {
81438
82140
  resolution = interactionAttentionResolution(result);
81439
82141
  return result;
81440
82142
  } finally {
81441
- attention.resolve(attentionRequest, resolution);
81442
- endWaitingForUser();
82143
+ endWaitingForUser(resolution);
81443
82144
  }
81444
82145
  };
81445
82146
  switch (request.type) {
@@ -81447,7 +82148,8 @@ function installInteractiveInteractionHandler(host) {
81447
82148
  showExtensionNotification(
81448
82149
  host,
81449
82150
  request.message,
81450
- request.level === "success" ? "info" : request.level
82151
+ request.level === "success" ? "info" : request.level,
82152
+ { markdown: request.markdown === true }
81451
82153
  );
81452
82154
  return { type: "notify", acknowledged: true };
81453
82155
  case "select_one": {
@@ -81536,6 +82238,30 @@ function installInteractiveInteractionHandler(host) {
81536
82238
  }
81537
82239
  });
81538
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
+ }
81539
82265
  function showExtensionNotification(host, message, type = "info", opts) {
81540
82266
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
81541
82267
  host.chatContainer.addChild(new Spacer(1));
@@ -82313,24 +83039,21 @@ var InteractiveMode = class {
82313
83039
  isYoloMode: () => this.yoloMode || this.yoloGrant.isActive(),
82314
83040
  getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid))
82315
83041
  });
82316
- const fileSystemToolOptions = (allowlistedDirs) => ({
82317
- ...buildCliFileAccessOptions({
82318
- cwd: this.options.cwd,
82319
- configDir: this.options.configDir,
82320
- skills: this.skills,
82321
- allowlistedDirs
82322
- }),
83042
+ const fileSystemToolOptions = (allowlistedDirs) => buildInteractiveFilesystemToolOptions({
83043
+ cwd: this.options.cwd,
82323
83044
  configDir: this.options.configDir,
82324
83045
  fileObservationState: this.options.fileObservationState,
83046
+ skills: this.skills,
83047
+ getActiveSessionId: () => this.sessionManager.getActiveSessionId(),
82325
83048
  getAgentModel: () => this.options.model,
82326
- getSessionId: () => this.sessionManager.getActiveSessionId() || void 0,
82327
83049
  resolveToolModel: (purpose) => this.options.modelRegistry.resolveToolModel(purpose, this.options.model),
82328
- getPolicyContext: () => ({
83050
+ resolveToolModelCandidates: this.options.modelRegistry.resolveToolModelCandidates ? (purpose) => this.options.modelRegistry.resolveToolModelCandidates(purpose, this.options.model) : void 0,
83051
+ getPlanPolicyContext: () => ({
82329
83052
  mode: this.planModeEnabled ? "plan" : "normal",
82330
83053
  yoloMode: this.yoloMode,
82331
83054
  confirmationPolicy: "allow"
82332
83055
  })
82333
- });
83056
+ }, allowlistedDirs);
82334
83057
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
82335
83058
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
82336
83059
  return {
@@ -82460,7 +83183,7 @@ var InteractiveMode = class {
82460
83183
  async init() {
82461
83184
  const activeSession = this.sessionManager.getActiveSession();
82462
83185
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82463
- const { Agent } = await import("./chunks/src-2RCV3I3P.js");
83186
+ const { Agent } = await import("./chunks/src-XDNJ3PHL.js");
82464
83187
  this.agent = new Agent({
82465
83188
  initialState: {
82466
83189
  systemPrompt: this.buildSystemPrompt(),
@@ -82637,20 +83360,20 @@ var InteractiveMode = class {
82637
83360
  setupAgentSubscription() {
82638
83361
  bindInteractiveRuntimeEvents(this.runtimeEventHost);
82639
83362
  }
82640
- _isContextOverflowError(errorMessage4) {
82641
- return isContextOverflowError(errorMessage4);
83363
+ _isContextOverflowError(errorMessage5) {
83364
+ return isContextOverflowError(errorMessage5);
82642
83365
  }
82643
83366
  _shouldAutoCompact(contextTokens, contextWindow) {
82644
83367
  return shouldAutoCompact(this.contextUiHost, contextTokens, contextWindow);
82645
83368
  }
82646
- _isRetryableError(errorMessage4) {
82647
- return isRetryableError(errorMessage4);
83369
+ _isRetryableError(errorMessage5) {
83370
+ return isRetryableError(errorMessage5);
82648
83371
  }
82649
- _isInvalidFileReferenceError(errorMessage4) {
82650
- return isInvalidFileReferenceError(errorMessage4);
83372
+ _isInvalidFileReferenceError(errorMessage5) {
83373
+ return isInvalidFileReferenceError(errorMessage5);
82651
83374
  }
82652
- _recoverFromInvalidFileReferenceError(errorMessage4) {
82653
- return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage4);
83375
+ _recoverFromInvalidFileReferenceError(errorMessage5) {
83376
+ return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage5);
82654
83377
  }
82655
83378
  async handleCompactCommand() {
82656
83379
  return handleCompactCommand(this.compactCommandHost);
@@ -82661,6 +83384,9 @@ var InteractiveMode = class {
82661
83384
  setupKeybindings() {
82662
83385
  return setupInteractiveKeybindings(this.keybindingsHost);
82663
83386
  }
83387
+ handlePasteImage() {
83388
+ void handleImagePaste(this);
83389
+ }
82664
83390
  async selectMentionOption(option) {
82665
83391
  return selectMentionOption(this.filePipelineHost, option);
82666
83392
  }
@@ -82836,8 +83562,8 @@ var InteractiveMode = class {
82836
83562
  clearStatusLine() {
82837
83563
  return clearStatusLine(this.uiHelperHost);
82838
83564
  }
82839
- showError(errorMessage4) {
82840
- return showError(this.uiHelperHost, errorMessage4);
83565
+ showError(errorMessage5) {
83566
+ return showError(this.uiHelperHost, errorMessage5);
82841
83567
  }
82842
83568
  async handleDirectBash(command) {
82843
83569
  return handleDirectBash(this.directBashHost, command);
@@ -82888,8 +83614,8 @@ var InteractiveMode = class {
82888
83614
  };
82889
83615
 
82890
83616
  // src/app/runtime-composition.ts
82891
- import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82892
- import { randomUUID } from "crypto";
83617
+ import { existsSync as existsSync19, mkdirSync as mkdirSync13 } from "fs";
83618
+ import { randomUUID as randomUUID2 } from "crypto";
82893
83619
  import * as path31 from "path";
82894
83620
  var CliMissingApiKeyError = class extends Error {
82895
83621
  constructor(message) {
@@ -82916,7 +83642,7 @@ function getCliRuntimePaths() {
82916
83642
  return { configDir, sessionsDir, skillsDir, builtinSkillsDir };
82917
83643
  }
82918
83644
  function ensureCliRuntimePaths(paths = getCliRuntimePaths()) {
82919
- if (!existsSync18(paths.skillsDir)) {
83645
+ if (!existsSync19(paths.skillsDir)) {
82920
83646
  mkdirSync13(paths.skillsDir, { recursive: true });
82921
83647
  }
82922
83648
  return paths;
@@ -83019,7 +83745,7 @@ async function composeCliRuntime(params) {
83019
83745
  await sessionManager.save();
83020
83746
  }
83021
83747
  const sessionId = sessionManager.getActiveSession()?.id;
83022
- const computerUseExecutionId = randomUUID();
83748
+ const computerUseExecutionId = randomUUID2();
83023
83749
  const fileObservationState = createFileObservationState();
83024
83750
  if (sessionId) {
83025
83751
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -83053,6 +83779,11 @@ async function composeCliRuntime(params) {
83053
83779
  });
83054
83780
  const fileSystemToolOptions = {
83055
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: [],
83056
83787
  configDir: paths.configDir,
83057
83788
  fileObservationState,
83058
83789
  getAgentModel: getCurrentModel,
@@ -83180,6 +83911,7 @@ function createPrintModeRetryStatusReporter() {
83180
83911
  // src/app/print-mode-compaction.ts
83181
83912
  function createPrintModeCompactionHandler(params) {
83182
83913
  return {
83914
+ modelView: cliCompactionModelView,
83183
83915
  compact: async (context) => {
83184
83916
  const beforeCompact = await params.runner.emitSessionBeforeCompact({
83185
83917
  type: "session:before_compact",
@@ -83343,7 +84075,7 @@ async function runPrintMode(params) {
83343
84075
  `);
83344
84076
  console.log("Assistant: ");
83345
84077
  }
83346
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.js");
84078
+ const { Agent: AgentClass } = await import("./chunks/src-XDNJ3PHL.js");
83347
84079
  let agent;
83348
84080
  const printModeExtensions = await loadPrintModeExtensions({
83349
84081
  cwd: runtime.cwd,
@@ -83512,21 +84244,21 @@ async function runPrintMode(params) {
83512
84244
  console.log(assistantText);
83513
84245
  }
83514
84246
  } catch (err) {
83515
- const errorMessage4 = err instanceof Error ? err.message : String(err);
83516
- sessionTrace.noteStalled(errorMessage4, { mode: "print" });
84247
+ const errorMessage5 = err instanceof Error ? err.message : String(err);
84248
+ sessionTrace.noteStalled(errorMessage5, { mode: "print" });
83517
84249
  process.exitCode = 1;
83518
84250
  if (params.jsonOutput) {
83519
84251
  console.log(
83520
84252
  JSON.stringify({
83521
84253
  ok: false,
83522
- error: errorMessage4,
84254
+ error: errorMessage5,
83523
84255
  sessionId: runtime.sessionManager.getActiveSession()?.id,
83524
84256
  provider: runtime.provider,
83525
84257
  modelId: runtime.modelId
83526
84258
  })
83527
84259
  );
83528
84260
  }
83529
- console.error("Error:", errorMessage4);
84261
+ console.error("Error:", errorMessage5);
83530
84262
  }
83531
84263
  try {
83532
84264
  if (!params.rawOutput && !params.jsonOutput) console.log("");
@@ -83772,7 +84504,7 @@ async function loadJsonModeExtensions(params) {
83772
84504
  getRuntimeTools: () => params.getAgent()?.state.tools ?? [],
83773
84505
  resolveModelClass: (modelClass) => params.runtime.modelRegistry.resolveModelClass(modelClass),
83774
84506
  resolveModelRef: (modelRef) => params.runtime.modelRegistry.resolveModelRef(modelRef),
83775
- registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
84507
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector, params.envelopeRegistry),
83776
84508
  getMessages: () => params.getAgent()?.state.messages ?? [],
83777
84509
  getSessionMetadata: () => ({ ...params.runtime.sessionManager.getActiveSession()?.metadata ?? {} }),
83778
84510
  setSessionMetadata: (key, value) => {
@@ -83783,7 +84515,7 @@ async function loadJsonModeExtensions(params) {
83783
84515
  },
83784
84516
  yoloMode: false
83785
84517
  };
83786
- const { extensions } = await loadExtensions(
84518
+ const { extensions, errors } = await loadExtensions(
83787
84519
  params.runtime.extensionPaths,
83788
84520
  params.runtime.cwd,
83789
84521
  uiContext,
@@ -83803,6 +84535,10 @@ async function loadJsonModeExtensions(params) {
83803
84535
  false
83804
84536
  );
83805
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
+ }
83806
84542
  try {
83807
84543
  await restoreExtensionProviderCredentials(params.runtime.modelRegistry);
83808
84544
  const extensionTools = filterEnabledTools(convertExtensionTools(extensions), params.runtime.settingsManager);
@@ -83822,6 +84558,7 @@ async function runJsonEventsMode(params) {
83822
84558
  const sessionId = params.explicitSessionId || params.runtime?.sessionManager.getActiveSession()?.id || `pie-json-${Date.now()}`;
83823
84559
  let activeTurn = null;
83824
84560
  let runtime = params.runtime;
84561
+ let envelopeRegistry = createCliHostEnvelopeRegistry();
83825
84562
  let agent;
83826
84563
  let controller;
83827
84564
  let disposeAgentSubscription;
@@ -83838,6 +84575,7 @@ async function runJsonEventsMode(params) {
83838
84575
  const completedSessionControlRequests = /* @__PURE__ */ new Map();
83839
84576
  const completedSidechatRequests = /* @__PURE__ */ new Map();
83840
84577
  const ensureRuntimeInFlight = /* @__PURE__ */ new Map();
84578
+ let compositionChain = Promise.resolve();
83841
84579
  let runtimeClosing = false;
83842
84580
  let activeSidechat = null;
83843
84581
  let activeSidechatPromise = null;
@@ -83853,7 +84591,7 @@ async function runJsonEventsMode(params) {
83853
84591
  // Capability broadcast: the envelope kinds THIS build can project at
83854
84592
  // intake (embedded external kinds included), so embedders can probe
83855
84593
  // before their first turn instead of learning mismatches from failures.
83856
- capabilities: { hostContextKinds: cliHostEnvelopeKinds() }
84594
+ capabilities: { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) }
83857
84595
  });
83858
84596
  function trackSidechatPromise(promise) {
83859
84597
  activeSidechatPromise = promise;
@@ -83870,13 +84608,6 @@ async function runJsonEventsMode(params) {
83870
84608
  function delay(ms) {
83871
84609
  return new Promise((resolve7) => setTimeout(resolve7, Math.max(0, ms)));
83872
84610
  }
83873
- async function waitForActiveController(timeoutMs = 1e3) {
83874
- const deadline = Date.now() + timeoutMs;
83875
- while (!controller && activeTurn && !activeTurn.finalized && Date.now() < deadline) {
83876
- await delay(5);
83877
- }
83878
- return controller;
83879
- }
83880
84611
  function touch() {
83881
84612
  if (activeTurn) {
83882
84613
  activeTurn.lastActivityAt = Date.now();
@@ -84214,7 +84945,7 @@ async function runJsonEventsMode(params) {
84214
84945
  }
84215
84946
  };
84216
84947
  try {
84217
- await ensureRuntime();
84948
+ await ensureRuntime(runtime?.cwd);
84218
84949
  const hostContextFailure = validateIntakeHostContext(message);
84219
84950
  if (hostContextFailure) {
84220
84951
  writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
@@ -84232,22 +84963,26 @@ async function runJsonEventsMode(params) {
84232
84963
  return;
84233
84964
  }
84234
84965
  const parent = agent;
84235
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.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");
84236
84971
  const sideAgent = new AgentClass({
84237
84972
  initialState: {
84238
84973
  systemPrompt: parent.state.systemPrompt,
84239
84974
  model: parent.state.model,
84240
84975
  thinkingLevel: parent.state.thinkingLevel,
84241
84976
  tools: [],
84242
- messages: structuredClone(parent.state.messages)
84977
+ messages: snapshot
84243
84978
  },
84244
- apiKey: runtime.apiKey,
84979
+ apiKey: sideApiKey,
84245
84980
  // The sidechat snapshot clones the parent's stored history, including
84246
84981
  // persisted hostContext facts; project them like the main agent does.
84247
- transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
84982
+ transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => sideTrace?.noteRuntimeEvent(
84248
84983
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84249
84984
  outcome.audit
84250
- )),
84985
+ ), sideRegistry),
84251
84986
  // Same body-free transport timeline as the main agent: a sidechat is
84252
84987
  // an independent Responses request and previously had no record at all
84253
84988
  // when it stalled. `streamSimple` matches the host default when the
@@ -84303,6 +85038,7 @@ async function runJsonEventsMode(params) {
84303
85038
  const turn = activeTurn;
84304
85039
  if (!turn || turn.finalized) return;
84305
85040
  turn.finalized = true;
85041
+ turn.releaseReady();
84306
85042
  const activeEntryId = activeHeadEntryId();
84307
85043
  const base = {
84308
85044
  sessionId: turn.sessionId,
@@ -84535,6 +85271,7 @@ async function runJsonEventsMode(params) {
84535
85271
  agent.releaseTransport();
84536
85272
  }
84537
85273
  agent = void 0;
85274
+ envelopeRegistry = createCliHostEnvelopeRegistry();
84538
85275
  try {
84539
85276
  await runtime?.sessionManager.save();
84540
85277
  } catch {
@@ -84561,7 +85298,9 @@ async function runJsonEventsMode(params) {
84561
85298
  const effectiveCwd = requestedCwd ?? process.cwd();
84562
85299
  const inFlight = ensureRuntimeInFlight.get(effectiveCwd);
84563
85300
  if (inFlight) return inFlight;
84564
- const composed = composeRuntime(effectiveCwd);
85301
+ const composed = compositionChain.then(() => composeRuntime(effectiveCwd));
85302
+ compositionChain = composed.catch(() => {
85303
+ });
84565
85304
  ensureRuntimeInFlight.set(effectiveCwd, composed);
84566
85305
  void composed.finally(() => ensureRuntimeInFlight.delete(effectiveCwd)).catch(() => {
84567
85306
  });
@@ -84592,14 +85331,16 @@ async function runJsonEventsMode(params) {
84592
85331
  if (runtime.initialModel.unconfigured || controller) {
84593
85332
  return;
84594
85333
  }
84595
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.js");
85334
+ const { Agent: AgentClass } = await import("./chunks/src-XDNJ3PHL.js");
84596
85335
  if (runtimeClosing) return;
84597
- const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
85336
+ const compositionRegistry = createCliHostEnvelopeRegistry();
85337
+ const jsonModeExtensions = await loadJsonModeExtensions({ runtime, envelopeRegistry: compositionRegistry, getAgent: () => agent });
84598
85338
  if (runtimeClosing) {
84599
85339
  await jsonModeExtensions.runner.dispose("shutdown");
84600
85340
  return;
84601
85341
  }
84602
85342
  extensionRunner = jsonModeExtensions.runner;
85343
+ envelopeRegistry = compositionRegistry;
84603
85344
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
84604
85345
  const restoredApiKey = runtime.modelRegistry.getApiKey(runtime.model);
84605
85346
  if (restoredApiKey) runtime.apiKey = restoredApiKey;
@@ -84635,7 +85376,7 @@ async function runJsonEventsMode(params) {
84635
85376
  transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
84636
85377
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84637
85378
  outcome.audit
84638
- )),
85379
+ ), compositionRegistry),
84639
85380
  // Body-free transport timeline for the main (JSON-events) agent.
84640
85381
  streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84641
85382
  });
@@ -84671,8 +85412,10 @@ async function runJsonEventsMode(params) {
84671
85412
  reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84672
85413
  })),
84673
85414
  compaction: {
85415
+ modelView: (message) => projectCliHostEnvelopes([message], void 0, compositionRegistry)[0],
84674
85416
  compact: async (context) => createCliAutoCompactionResult({
84675
85417
  context,
85418
+ envelopeRegistry: compositionRegistry,
84676
85419
  model: runtime.model,
84677
85420
  apiKey: runtime.apiKey,
84678
85421
  systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt,
@@ -84780,7 +85523,7 @@ async function runJsonEventsMode(params) {
84780
85523
  return message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : [{ type: "text", text: message.prompt }];
84781
85524
  }
84782
85525
  function validateIntakeHostContext(message) {
84783
- return message.hostContext ? intakeHostContextFailure(message.hostContext) : void 0;
85526
+ return message.hostContext ? intakeHostContextFailure(message.hostContext, envelopeRegistry) : void 0;
84784
85527
  }
84785
85528
  async function runTurn(message) {
84786
85529
  if (isSessionMismatch(message)) {
@@ -84793,21 +85536,19 @@ async function runJsonEventsMode(params) {
84793
85536
  return;
84794
85537
  }
84795
85538
  if (activeTurn && !activeTurn.finalized) {
84796
- const queuedHostContextFailure = validateIntakeHostContext(message);
84797
- if (queuedHostContextFailure) {
84798
- writeEvent({
84799
- type: "turn.failed",
84800
- sessionId,
84801
- turnId: message.turnId,
84802
- error: queuedHostContextFailure.message,
84803
- ...queuedHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
84804
- });
84805
- return;
84806
- }
84807
- await queueActiveTurnInput(message);
85539
+ const owner = activeTurn;
85540
+ owner.queue = owner.queue.then(() => queueActiveTurnInput(message, owner));
85541
+ await owner.queue;
84808
85542
  return;
84809
85543
  }
85544
+ let releaseReady;
85545
+ const ready = new Promise((resolve7) => {
85546
+ releaseReady = resolve7;
85547
+ });
84810
85548
  activeTurn = {
85549
+ ready,
85550
+ releaseReady,
85551
+ queue: Promise.resolve(),
84811
85552
  sessionId,
84812
85553
  turnId: message.turnId,
84813
85554
  prompt: message.prompt,
@@ -84828,7 +85569,7 @@ async function runJsonEventsMode(params) {
84828
85569
  finalize("failed", {
84829
85570
  code: mockHostContextFailure.code,
84830
85571
  message: mockHostContextFailure.message,
84831
- ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85572
+ ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84832
85573
  });
84833
85574
  if (activeTurn === turn) emitSettled(turn);
84834
85575
  if (activeTurn === turn) activeTurn = null;
@@ -84868,12 +85609,21 @@ async function runJsonEventsMode(params) {
84868
85609
  if (activeTurn === turn) activeTurn = null;
84869
85610
  return;
84870
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
+ }
84871
85621
  const hostContextFailure = validateIntakeHostContext(message);
84872
85622
  if (hostContextFailure) {
84873
85623
  finalize("failed", {
84874
85624
  code: hostContextFailure.code,
84875
85625
  message: hostContextFailure.message,
84876
- ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85626
+ ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84877
85627
  });
84878
85628
  if (activeTurn === turn) emitSettled(turn);
84879
85629
  if (activeTurn === turn) activeTurn = null;
@@ -84913,7 +85663,9 @@ async function runJsonEventsMode(params) {
84913
85663
  try {
84914
85664
  trace?.notePendingUserText(message.prompt);
84915
85665
  trace?.noteDispatchStart({ mode: "json-events" });
84916
- await controller.prompt(promptInputFor(message), { source: "json-events" });
85666
+ const dispatched = controller.prompt(promptInputFor(message), { source: "json-events" });
85667
+ turn.releaseReady();
85668
+ await dispatched;
84917
85669
  trace?.noteDispatchSettled({ mode: "json-events" });
84918
85670
  trace?.noteWaitForIdleStart({ mode: "json-events" });
84919
85671
  await controller.waitForSettled();
@@ -84951,7 +85703,7 @@ async function runJsonEventsMode(params) {
84951
85703
  if (activeTurn === turn) activeTurn = null;
84952
85704
  }
84953
85705
  }
84954
- async function queueActiveTurnInput(message) {
85706
+ async function queueActiveTurnInput(message, owner) {
84955
85707
  if (!message.streamingBehavior) {
84956
85708
  writeEvent({
84957
85709
  type: "turn.failed",
@@ -84961,8 +85713,9 @@ async function runJsonEventsMode(params) {
84961
85713
  });
84962
85714
  return;
84963
85715
  }
84964
- const activeController = await waitForActiveController();
84965
- if (!activeController) {
85716
+ await owner.ready;
85717
+ const activeController = controller;
85718
+ if (!activeController || activeTurn !== owner || owner.finalized || owner.interrupted || runtimeClosing) {
84966
85719
  writeEvent({
84967
85720
  type: "turn.failed",
84968
85721
  sessionId,
@@ -84971,6 +85724,17 @@ async function runJsonEventsMode(params) {
84971
85724
  });
84972
85725
  return;
84973
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
+ }
84974
85738
  try {
84975
85739
  applyRuntimeSelection(message);
84976
85740
  trace?.notePendingUserText(message.prompt);
@@ -84989,7 +85753,7 @@ async function runJsonEventsMode(params) {
84989
85753
  sessionId,
84990
85754
  turnId: message.turnId,
84991
85755
  mode: message.streamingBehavior,
84992
- activeTurnId: activeTurn?.turnId
85756
+ activeTurnId: owner.turnId
84993
85757
  });
84994
85758
  } catch (error) {
84995
85759
  writeEvent({
@@ -85254,7 +86018,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
85254
86018
  }
85255
86019
  async function createCliTestStreamFnFromEnv() {
85256
86020
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
85257
- return (await import("./chunks/test-stream-Y2S3H7DK.js")).createTestStreamFnFromSequenceEnv();
86021
+ return (await import("./chunks/test-stream-LUUGOJMP.js")).createTestStreamFnFromSequenceEnv();
85258
86022
  }
85259
86023
  function resolveJsonEventsDetailFlag() {
85260
86024
  for (let index = 0; index < process.argv.length; index += 1) {