@cydm/pie 2.7.11 → 2.7.13

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-D7RMNFVM.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-CIFC6QVB.js";
98
+ import "./chunks/chunk-HGUYS5SF.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-R46YPPTU.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 */
@@ -52608,18 +52611,18 @@ function validateRemoteModel(profileId, model) {
52608
52611
  validateWebSearchCapability(model.webSearch, `Remote profile "${profileId}" model "${model.id}" webSearch`);
52609
52612
  validateFileCapabilities(model.fileCapabilities, `Remote profile "${profileId}" model "${model.id}" fileCapabilities`);
52610
52613
  }
52611
- function assertHeadersShape(headers, label) {
52612
- if (headers === void 0) return;
52613
- if (!isPlainRecord(headers)) throw new Error(`${label} headers must be an object`);
52614
- for (const [name, value] of Object.entries(headers)) {
52614
+ function assertHeadersShape(headers2, label) {
52615
+ if (headers2 === void 0) return;
52616
+ if (!isPlainRecord(headers2)) throw new Error(`${label} headers must be an object`);
52617
+ for (const [name, value] of Object.entries(headers2)) {
52615
52618
  if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) throw new Error(`${label} header name ${name || "(empty)"} is invalid`);
52616
52619
  if (typeof value !== "string") throw new Error(`${label} header ${name} must be a string`);
52617
52620
  if (/[\0-\x08\x0A-\x1F\x7F]/.test(value)) throw new Error(`${label} header ${name} contains an invalid control character`);
52618
52621
  }
52619
52622
  }
52620
- function assertRemoteHeadersSafe(headers, label) {
52621
- assertHeadersShape(headers, label);
52622
- for (const name of Object.keys(headers || {})) {
52623
+ function assertRemoteHeadersSafe(headers2, label) {
52624
+ assertHeadersShape(headers2, label);
52625
+ for (const name of Object.keys(headers2 || {})) {
52623
52626
  if (/(?:authorization|cookie|api[-_]?key|token|secret|credential)/i.test(name)) {
52624
52627
  throw new Error(`${label} must not provide credential-bearing header ${name}`);
52625
52628
  }
@@ -53249,10 +53252,10 @@ var ModelRegistry = class {
53249
53252
  let payload = cached?.payload;
53250
53253
  let response;
53251
53254
  try {
53252
- const headers = { Authorization: `Bearer ${apiKey}` };
53253
- if (cached?.etag) headers["If-None-Match"] = cached.etag;
53255
+ const headers2 = { Authorization: `Bearer ${apiKey}` };
53256
+ if (cached?.etag) headers2["If-None-Match"] = cached.etag;
53254
53257
  response = await fetch(url, {
53255
- headers,
53258
+ headers: headers2,
53256
53259
  redirect: "manual",
53257
53260
  signal: AbortSignal.timeout(3e3)
53258
53261
  });
@@ -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":
@@ -63652,9 +63659,38 @@ var Spacer = class {
63652
63659
 
63653
63660
  // ../../packages/tui/src/stdin-buffer.ts
63654
63661
  import { EventEmitter } from "events";
63662
+
63663
+ // ../../packages/tui/src/clipboard-framing.ts
63664
+ var headers = [
63665
+ { kind: "osc", prefix: "\x1B]5522" },
63666
+ { kind: "csi", prefix: "\x1B[?5522" }
63667
+ ];
63668
+ function clipboardFrameEnd(data, kind) {
63669
+ const match = kind === "osc" ? /\x07|\x1b\\/.exec(data) : /[\x40-\x7e]/.exec(data);
63670
+ return match ? match.index + match[0].length : void 0;
63671
+ }
63672
+ function classifyClipboardFragment(data, pending = false) {
63673
+ if (!data) return void 0;
63674
+ for (const { kind, prefix } of headers) {
63675
+ if (prefix.startsWith(data)) {
63676
+ const ambiguous = data === "\x1B" || data === "\x1B[";
63677
+ if (!ambiguous || pending) return { kind, valid: false };
63678
+ } else if (data.startsWith(prefix)) {
63679
+ const end = clipboardFrameEnd(data.slice(2), kind);
63680
+ const length = end === void 0 ? void 0 : end + 2;
63681
+ const frame = length === void 0 ? data : data.slice(0, length);
63682
+ const valid = kind === "osc" ? frame.startsWith(`${prefix};`) : /^\x1b\[\?5522;[0-4]\$y$/.test(frame);
63683
+ return { kind, length, valid };
63684
+ }
63685
+ }
63686
+ return void 0;
63687
+ }
63688
+
63689
+ // ../../packages/tui/src/stdin-buffer.ts
63655
63690
  var ESC = "\x1B";
63656
63691
  var BRACKETED_PASTE_START = "\x1B[200~";
63657
63692
  var BRACKETED_PASTE_END = "\x1B[201~";
63693
+ var OSC_5522_MAX_FRAME = 70 * 1024 * 1024;
63658
63694
  function isCompleteSequence(data) {
63659
63695
  if (!data.startsWith(ESC)) {
63660
63696
  return "not-escape";
@@ -63741,11 +63777,32 @@ function isCompleteApcSequence(data) {
63741
63777
  }
63742
63778
  return "incomplete";
63743
63779
  }
63744
- function extractCompleteSequences(buffer) {
63780
+ function extractCompleteSequences(buffer, maxClipboardFrame = OSC_5522_MAX_FRAME, pending = false) {
63745
63781
  const sequences = [];
63746
63782
  let pos = 0;
63747
63783
  while (pos < buffer.length) {
63748
63784
  const remaining = buffer.slice(pos);
63785
+ const clipboard = classifyClipboardFragment(remaining, pending);
63786
+ if (clipboard) {
63787
+ if (clipboard.length === void 0) return { sequences, remainder: remaining };
63788
+ if (clipboard.valid && clipboard.length <= Math.min(maxClipboardFrame, clipboard.kind === "csi" ? 64 : Infinity)) {
63789
+ sequences.push(remaining.slice(0, clipboard.length));
63790
+ }
63791
+ pos += clipboard.length;
63792
+ continue;
63793
+ }
63794
+ if (remaining.startsWith(BRACKETED_PASTE_START)) {
63795
+ sequences.push(BRACKETED_PASTE_START);
63796
+ return { sequences, remainder: remaining.slice(BRACKETED_PASTE_START.length) };
63797
+ }
63798
+ if (remaining.startsWith(`${ESC}]`)) {
63799
+ const end = /\x07|\x1b\\/.exec(remaining);
63800
+ if (!end) return { sequences, remainder: remaining };
63801
+ const length = end.index + end[0].length;
63802
+ sequences.push(remaining.slice(0, length));
63803
+ pos += length;
63804
+ continue;
63805
+ }
63749
63806
  if (remaining.startsWith(ESC)) {
63750
63807
  let seqEnd = 1;
63751
63808
  while (seqEnd <= remaining.length) {
@@ -63779,9 +63836,14 @@ var StdinBuffer = class extends EventEmitter {
63779
63836
  timeoutMs;
63780
63837
  pasteMode = false;
63781
63838
  pasteBuffer = "";
63839
+ discardingClipboard;
63840
+ clipboardStarted = 0;
63841
+ discardEsc = false;
63842
+ options;
63782
63843
  constructor(options = {}) {
63783
63844
  super();
63784
63845
  this.timeoutMs = options.timeout ?? 10;
63846
+ this.options = options;
63785
63847
  }
63786
63848
  process(data) {
63787
63849
  if (this.timeout) {
@@ -63803,6 +63865,28 @@ var StdinBuffer = class extends EventEmitter {
63803
63865
  this.emit("data", "");
63804
63866
  return;
63805
63867
  }
63868
+ if (this.discardingClipboard) {
63869
+ const kind = this.discardingClipboard;
63870
+ const heldEscape = this.discardEsc;
63871
+ const discarded = (heldEscape ? ESC : "") + str2;
63872
+ const end = clipboardFrameEnd(discarded, kind);
63873
+ const escape = discarded.indexOf(ESC, heldEscape && str2.startsWith(ESC) ? 1 : 0);
63874
+ const resync = escape >= 0 && escape + 1 < discarded.length && (end === void 0 || escape < end - 1) && !(kind === "osc" && end !== void 0 && escape === end - 2 && discarded.startsWith(`${ESC}\\`, escape));
63875
+ if (resync) {
63876
+ this.discardingClipboard = void 0;
63877
+ this.discardEsc = false;
63878
+ str2 = discarded.slice(escape);
63879
+ } else {
63880
+ if (!end) {
63881
+ this.discardEsc = discarded.endsWith(ESC);
63882
+ this.scheduleDiscardRecovery();
63883
+ return;
63884
+ }
63885
+ this.discardingClipboard = void 0;
63886
+ this.discardEsc = false;
63887
+ str2 = discarded.slice(end);
63888
+ }
63889
+ }
63806
63890
  this.buffer += str2;
63807
63891
  if (this.pasteMode) {
63808
63892
  this.pasteBuffer += this.buffer;
@@ -63820,16 +63904,13 @@ var StdinBuffer = class extends EventEmitter {
63820
63904
  }
63821
63905
  return;
63822
63906
  }
63823
- const startIndex = this.buffer.indexOf(BRACKETED_PASTE_START);
63824
- if (startIndex !== -1) {
63825
- if (startIndex > 0) {
63826
- const beforePaste = this.buffer.slice(0, startIndex);
63827
- const result2 = extractCompleteSequences(beforePaste);
63828
- for (const sequence of result2.sequences) {
63829
- this.emit("data", sequence);
63830
- }
63831
- }
63832
- this.buffer = this.buffer.slice(startIndex + BRACKETED_PASTE_START.length);
63907
+ const result = extractCompleteSequences(this.buffer, this.options.clipboardMaxFrame, this.options.clipboardResponsePending?.());
63908
+ if (result.remainder.length < this.buffer.length) this.clipboardStarted = 0;
63909
+ this.buffer = result.remainder;
63910
+ for (const sequence of result.sequences) {
63911
+ if (sequence !== BRACKETED_PASTE_START) this.emit("data", sequence);
63912
+ }
63913
+ if (result.sequences.at(-1) === BRACKETED_PASTE_START) {
63833
63914
  this.pasteMode = true;
63834
63915
  this.pasteBuffer = this.buffer;
63835
63916
  this.buffer = "";
@@ -63846,12 +63927,29 @@ var StdinBuffer = class extends EventEmitter {
63846
63927
  }
63847
63928
  return;
63848
63929
  }
63849
- const result = extractCompleteSequences(this.buffer);
63850
- this.buffer = result.remainder;
63851
- for (const sequence of result.sequences) {
63852
- this.emit("data", sequence);
63853
- }
63854
63930
  if (this.buffer.length > 0) {
63931
+ const fragment = classifyClipboardFragment(this.buffer, this.options.clipboardResponsePending?.());
63932
+ if (fragment) {
63933
+ this.clipboardStarted ||= Date.now();
63934
+ const limit = Math.min(this.options.clipboardMaxFrame ?? OSC_5522_MAX_FRAME, fragment.kind === "csi" ? 64 : Infinity);
63935
+ if (this.buffer.length > limit) {
63936
+ this.discardClipboard(fragment.kind);
63937
+ return;
63938
+ }
63939
+ const deadline = Math.min(
63940
+ this.options.clipboardFrameTimeout ?? 1e3,
63941
+ (this.options.clipboardFrameMaxDuration ?? 1e4) - (Date.now() - this.clipboardStarted)
63942
+ );
63943
+ this.timeout = setTimeout(() => {
63944
+ if (this.buffer === ESC) {
63945
+ this.buffer = "";
63946
+ this.clipboardStarted = 0;
63947
+ this.emit("data", ESC);
63948
+ } else this.discardClipboard(fragment.kind);
63949
+ }, Math.max(0, deadline));
63950
+ return;
63951
+ }
63952
+ this.clipboardStarted = 0;
63855
63953
  this.timeout = setTimeout(() => {
63856
63954
  const flushed = this.flush();
63857
63955
  for (const sequence of flushed) {
@@ -63860,6 +63958,19 @@ var StdinBuffer = class extends EventEmitter {
63860
63958
  }, this.timeoutMs);
63861
63959
  }
63862
63960
  }
63961
+ scheduleDiscardRecovery() {
63962
+ this.timeout = setTimeout(() => {
63963
+ this.discardingClipboard = void 0;
63964
+ this.discardEsc = false;
63965
+ }, this.options.clipboardFrameTimeout ?? 1e3);
63966
+ }
63967
+ discardClipboard(kind) {
63968
+ this.discardEsc = this.buffer.endsWith(ESC);
63969
+ this.buffer = "";
63970
+ this.clipboardStarted = 0;
63971
+ this.discardingClipboard = kind;
63972
+ this.scheduleDiscardRecovery();
63973
+ }
63863
63974
  flush() {
63864
63975
  if (this.timeout) {
63865
63976
  clearTimeout(this.timeout);
@@ -63868,7 +63979,13 @@ var StdinBuffer = class extends EventEmitter {
63868
63979
  if (this.buffer.length === 0) {
63869
63980
  return [];
63870
63981
  }
63982
+ const fragment = classifyClipboardFragment(this.buffer, this.options.clipboardResponsePending?.());
63983
+ if (fragment && this.buffer !== ESC) {
63984
+ this.discardClipboard(fragment.kind);
63985
+ return [];
63986
+ }
63871
63987
  const sequences = [this.buffer];
63988
+ this.clipboardStarted = 0;
63872
63989
  this.buffer = "";
63873
63990
  return sequences;
63874
63991
  }
@@ -63880,6 +63997,9 @@ var StdinBuffer = class extends EventEmitter {
63880
63997
  this.buffer = "";
63881
63998
  this.pasteMode = false;
63882
63999
  this.pasteBuffer = "";
64000
+ this.clipboardStarted = 0;
64001
+ this.discardingClipboard = void 0;
64002
+ this.discardEsc = false;
63883
64003
  }
63884
64004
  getBuffer() {
63885
64005
  return this.buffer;
@@ -63892,8 +64012,196 @@ var StdinBuffer = class extends EventEmitter {
63892
64012
  // ../../packages/tui/src/terminal.ts
63893
64013
  import * as fs11 from "node:fs";
63894
64014
  import koffi from "koffi";
64015
+
64016
+ // ../../packages/tui/src/osc-5522.ts
64017
+ import { randomBytes as randomBytes2 } from "node:crypto";
64018
+ var OSC = "\x1B]";
64019
+ var ST = "\x1B\\";
64020
+ var READ_TIMEOUT_MS = 8e3;
64021
+ var SUPPORT_TIMEOUT_MS = 1200;
64022
+ var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
64023
+ function parseOsc5522Packet(data) {
64024
+ const match = data.match(/^\x1b\]5522;([^;]*);?([^\x1b\x07]*)(?:\x1b\\|\x07)$/);
64025
+ if (!match) return void 0;
64026
+ return { metadata: match[1] ?? "", payload: match[2] ?? "" };
64027
+ }
64028
+ function isStrictBase64(value) {
64029
+ if (value.length % 4 !== 0) return false;
64030
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value)) return false;
64031
+ const padding = value.length - value.replace(/=+$/, "").length;
64032
+ if (padding > 2) return false;
64033
+ if (padding > 0 && value.slice(0, -padding).includes("=")) return false;
64034
+ return true;
64035
+ }
64036
+ function parseMetadata(metadata) {
64037
+ const fields = /* @__PURE__ */ new Map();
64038
+ for (const part of metadata.split(":")) {
64039
+ const eq = part.indexOf("=");
64040
+ if (eq === -1) {
64041
+ if (part) fields.set(part, "");
64042
+ } else {
64043
+ fields.set(part.slice(0, eq), part.slice(eq + 1));
64044
+ }
64045
+ }
64046
+ return fields;
64047
+ }
64048
+ var Osc5522Client = class {
64049
+ constructor(write = (data) => process.stdout.write(data)) {
64050
+ this.write = write;
64051
+ }
64052
+ write;
64053
+ session;
64054
+ supportMode = "unknown";
64055
+ pendingSupportResolve;
64056
+ get responsePending() {
64057
+ return this.session !== void 0 || this.pendingSupportResolve !== void 0;
64058
+ }
64059
+ /**
64060
+ * Feed one stdin sequence. Returns true when the packet was consumed as
64061
+ * part of this protocol, so the caller can drop it before normal routing.
64062
+ */
64063
+ feed(data) {
64064
+ const support = data.match(/^\x1b\[\?5522;(\d+)\$y$/);
64065
+ if (support) {
64066
+ const ps = Number(support[1]);
64067
+ const supported = ps !== 0 && ps !== 4;
64068
+ this.supportMode = supported ? "supported" : "unsupported";
64069
+ this.pendingSupportResolve?.(supported);
64070
+ this.pendingSupportResolve = void 0;
64071
+ return true;
64072
+ }
64073
+ const packet = parseOsc5522Packet(data);
64074
+ if (!packet) return data.startsWith(`${OSC}5522;`);
64075
+ const fields = parseMetadata(packet.metadata);
64076
+ const type = fields.get("type") ?? "";
64077
+ if (!type.startsWith("read")) return true;
64078
+ const status = fields.get("status");
64079
+ const session = this.session;
64080
+ if (!session) return true;
64081
+ if (fields.get("id") !== session.id) return true;
64082
+ if (status === void 0) {
64083
+ this.finishFor(session, { ok: false, kind: "malformed", message: `OSC 5522 read packet without status: ${packet.metadata}` });
64084
+ return true;
64085
+ }
64086
+ if (status === "OK") {
64087
+ return true;
64088
+ }
64089
+ if (status === "DATA") {
64090
+ const mimeEncoded = fields.get("mime");
64091
+ if (mimeEncoded === void 0 || mimeEncoded === "") {
64092
+ this.finishFor(session, { ok: false, kind: "malformed", message: `DATA packet without mime field: ${packet.metadata}` });
64093
+ return true;
64094
+ }
64095
+ if (!isStrictBase64(mimeEncoded)) {
64096
+ this.finishFor(session, { ok: false, kind: "malformed", message: "Invalid base64 MIME in DATA packet" });
64097
+ return true;
64098
+ }
64099
+ const mimeType = Buffer.from(mimeEncoded, "base64").toString("utf8");
64100
+ if (session.mimeType !== void 0 && session.mimeType !== mimeType) {
64101
+ return true;
64102
+ }
64103
+ if (session.mimeType === void 0) {
64104
+ session.mimeType = mimeType;
64105
+ }
64106
+ if (!isStrictBase64(packet.payload)) {
64107
+ this.finishFor(session, { ok: false, kind: "malformed", message: "Invalid base64 payload in DATA packet" });
64108
+ return true;
64109
+ }
64110
+ const chunk = Buffer.from(packet.payload, "base64");
64111
+ session.totalBytes += chunk.length;
64112
+ if (session.totalBytes > MAX_TOTAL_BYTES) {
64113
+ this.finishFor(session, { ok: false, kind: "too-large", message: `Clipboard image exceeds the ${Math.floor(MAX_TOTAL_BYTES / (1024 * 1024))}MB limit.` });
64114
+ return true;
64115
+ }
64116
+ session.chunks.push(chunk);
64117
+ return true;
64118
+ }
64119
+ if (status === "DONE") {
64120
+ const mimeType = session.mimeType;
64121
+ if (!mimeType || session.chunks.length === 0) {
64122
+ this.finishFor(session, { ok: false, kind: "unavailable", message: "Clipboard read completed without data." });
64123
+ return true;
64124
+ }
64125
+ this.finishFor(session, { ok: true, mimeType, bytes: Buffer.concat(session.chunks) });
64126
+ return true;
64127
+ }
64128
+ const kind = status === "EPERM" ? "denied" : status === "EBUSY" ? "busy" : status === "ENOSYS" ? "unavailable" : "malformed";
64129
+ 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}`;
64130
+ this.finishFor(session, { ok: false, kind, message });
64131
+ return true;
64132
+ }
64133
+ /**
64134
+ * Query DECRQM support (CSI ? 5522 $ p). Resolves false on timeout —
64135
+ * terminals that do not know the mode never reply.
64136
+ */
64137
+ async querySupport(timeoutMs = SUPPORT_TIMEOUT_MS) {
64138
+ if (this.supportMode !== "unknown") return this.supportMode === "supported";
64139
+ return new Promise((resolve7) => {
64140
+ let settled = false;
64141
+ const settle = (supported) => {
64142
+ if (settled) return;
64143
+ settled = true;
64144
+ clearTimeout(timer);
64145
+ this.pendingSupportResolve = void 0;
64146
+ resolve7(supported);
64147
+ };
64148
+ const timer = setTimeout(() => settle(false), timeoutMs);
64149
+ this.pendingSupportResolve = settle;
64150
+ this.write(`\x1B[?5522$p`);
64151
+ });
64152
+ }
64153
+ /**
64154
+ * Read the clipboard's image data. The first offered representation of
64155
+ * the requested types is returned (png preferred over jpeg by request
64156
+ * order). Resolves unsupported when the terminal never replies to the
64157
+ * DECRQM probe.
64158
+ */
64159
+ async readImage(timeoutMs = READ_TIMEOUT_MS, supportTimeoutMs = SUPPORT_TIMEOUT_MS) {
64160
+ if (this.session) {
64161
+ return { ok: false, kind: "busy", message: "Another OSC 5522 read is already in flight." };
64162
+ }
64163
+ const session = {
64164
+ id: `pie-${randomBytes2(8).toString("hex")}`,
64165
+ resolve: void 0,
64166
+ timer: void 0,
64167
+ chunks: [],
64168
+ totalBytes: 0
64169
+ };
64170
+ const promise = new Promise((resolve7) => {
64171
+ session.resolve = resolve7;
64172
+ });
64173
+ session.timer = setTimeout(() => {
64174
+ this.finishFor(session, { ok: false, kind: "timeout", message: "Terminal did not respond to the clipboard read in time." });
64175
+ }, timeoutMs);
64176
+ this.session = session;
64177
+ const supported = await this.querySupport(supportTimeoutMs);
64178
+ if (!supported) {
64179
+ this.finishFor(session, { ok: false, kind: "unsupported", message: "Terminal does not support the OSC 5522 clipboard protocol." });
64180
+ return promise;
64181
+ }
64182
+ if (this.session !== session) return promise;
64183
+ const mimeList = Buffer.from("image/png image/jpeg").toString("base64");
64184
+ this.write(`${OSC}5522;id=${session.id}:type=read;${mimeList}${ST}`);
64185
+ return promise;
64186
+ }
64187
+ /** Finish only if `session` is still the live session (identity-bound). */
64188
+ finishFor(session, result) {
64189
+ if (this.session !== session) return;
64190
+ this.session = void 0;
64191
+ clearTimeout(session.timer);
64192
+ session.resolve(result);
64193
+ }
64194
+ };
64195
+
64196
+ // ../../packages/tui/src/terminal.ts
63895
64197
  var ProcessTerminal = class {
63896
64198
  wasRaw = false;
64199
+ /** Client for the Kitty OSC 5522 clipboard protocol (read side). */
64200
+ osc5522Client = new Osc5522Client();
64201
+ /** Access the OSC 5522 clipboard client (terminal-level clipboard reads). */
64202
+ get clipboard() {
64203
+ return this.osc5522Client;
64204
+ }
63897
64205
  inputHandler;
63898
64206
  resizeHandler;
63899
64207
  _kittyProtocolActive = false;
@@ -63935,9 +64243,12 @@ var ProcessTerminal = class {
63935
64243
  * to handle the case where the response arrives split across multiple events.
63936
64244
  */
63937
64245
  setupStdinBuffer() {
63938
- this.stdinBuffer = new StdinBuffer({ timeout: 10 });
64246
+ this.stdinBuffer = new StdinBuffer({ timeout: 10, clipboardResponsePending: () => this.osc5522Client.responsePending });
63939
64247
  const kittyResponsePattern = /^\x1b\[\?(\d+)u$/;
63940
64248
  this.stdinBuffer.on("data", (sequence) => {
64249
+ if (this.osc5522Client.feed(sequence)) {
64250
+ return;
64251
+ }
63941
64252
  if (!this._kittyProtocolActive) {
63942
64253
  const match = sequence.match(kittyResponsePattern);
63943
64254
  if (match) {
@@ -64196,11 +64507,14 @@ var ExtensionRunner = class {
64196
64507
  };
64197
64508
  }
64198
64509
  /**
64199
- * Set the UI context for interactive mode.
64510
+ * Set the UI context for interactive mode. Does NOT change hasUI: the
64511
+ * constructor's hasUI argument (host-provided: true for interactive, false
64512
+ * for print/json-events no-op contexts) is the single source of truth.
64513
+ * Flipping it here used to make headless hosts report ctx.hasUI === true
64514
+ * while their ui.* methods silently no-op'd.
64200
64515
  */
64201
64516
  setUIContext(uiContext) {
64202
64517
  this.uiContext = uiContext;
64203
- this.hasUI = true;
64204
64518
  }
64205
64519
  /**
64206
64520
  * Wrap an unregister handle so this runner can revoke it during
@@ -64948,7 +65262,7 @@ var DEFAULT_APP_KEYBINDINGS = {
64948
65262
  steer: "alt+enter",
64949
65263
  followUp: [],
64950
65264
  dequeue: ["alt+up", "ctrl+shift+up"],
64951
- pasteImage: "ctrl+v",
65265
+ pasteImage: ["ctrl+v", "alt+v", "super+v"],
64952
65266
  newSession: [],
64953
65267
  tree: [],
64954
65268
  fork: [],
@@ -65102,32 +65416,6 @@ function buildCliProjectContextSection(cwd) {
65102
65416
  return buildProjectContextSection(collectAncestorAgentsFiles(cwd));
65103
65417
  }
65104
65418
 
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
65419
  // src/runtime/computer-use-owner.ts
65132
65420
  var COMPUTER_USE_OWNER_ENV = "PIE_COMPUTER_USE_OWNER_ID";
65133
65421
  var COMPUTER_USE_EXECUTION_ENV = "PIE_COMPUTER_USE_EXECUTION_ID";
@@ -65147,7 +65435,7 @@ function computerUseOwnerEnv(ownerId, executionId) {
65147
65435
 
65148
65436
  // src/theme/theme.ts
65149
65437
  import * as fs13 from "node:fs";
65150
- import * as path12 from "node:path";
65438
+ import * as path11 from "node:path";
65151
65439
 
65152
65440
  // ../../node_modules/@sinclair/typebox/build/esm/compiler/compiler.mjs
65153
65441
  var TypeCheck = class {
@@ -66034,8 +66322,8 @@ var BUILTIN_THEMES;
66034
66322
  function getBuiltinThemes() {
66035
66323
  if (!BUILTIN_THEMES) {
66036
66324
  const themesDir = getThemesDir();
66037
- const darkPath = path12.join(themesDir, "dark.json");
66038
- const lightPath = path12.join(themesDir, "light.json");
66325
+ const darkPath = path11.join(themesDir, "dark.json");
66326
+ const lightPath = path11.join(themesDir, "light.json");
66039
66327
  BUILTIN_THEMES = {
66040
66328
  dark: JSON.parse(fs13.readFileSync(darkPath, "utf-8")),
66041
66329
  light: JSON.parse(fs13.readFileSync(lightPath, "utf-8"))
@@ -66072,23 +66360,23 @@ function parseThemeJson(label, json) {
66072
66360
  otherErrors.push(` - ${e.path}: ${e.message}`);
66073
66361
  }
66074
66362
  }
66075
- let errorMessage4 = `Invalid theme "${label}":
66363
+ let errorMessage5 = `Invalid theme "${label}":
66076
66364
  `;
66077
66365
  if (missingColors.length > 0) {
66078
- errorMessage4 += "\nMissing required color tokens:\n";
66079
- errorMessage4 += missingColors.map((c) => ` - ${c}`).join("\n");
66080
- errorMessage4 += `
66366
+ errorMessage5 += "\nMissing required color tokens:\n";
66367
+ errorMessage5 += missingColors.map((c) => ` - ${c}`).join("\n");
66368
+ errorMessage5 += `
66081
66369
 
66082
66370
  Please add these colors to your theme's "colors" object.`;
66083
- errorMessage4 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
66371
+ errorMessage5 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
66084
66372
  }
66085
66373
  if (otherErrors.length > 0) {
66086
- errorMessage4 += `
66374
+ errorMessage5 += `
66087
66375
 
66088
66376
  Other errors:
66089
66377
  ${otherErrors.join("\n")}`;
66090
66378
  }
66091
- throw new Error(errorMessage4);
66379
+ throw new Error(errorMessage5);
66092
66380
  }
66093
66381
  return json;
66094
66382
  }
@@ -66115,7 +66403,7 @@ function loadThemeJson(name) {
66115
66403
  throw new Error(`Theme "${name}" does not have a source path for export`);
66116
66404
  }
66117
66405
  const customThemesDir = getCustomThemesDir();
66118
- const themePath = path12.join(customThemesDir, `${name}.json`);
66406
+ const themePath = path11.join(customThemesDir, `${name}.json`);
66119
66407
  if (!fs13.existsSync(themePath)) {
66120
66408
  throw new Error(`Theme not found: ${name}`);
66121
66409
  }
@@ -66235,7 +66523,7 @@ function startThemeWatcher() {
66235
66523
  return;
66236
66524
  }
66237
66525
  const customThemesDir = getCustomThemesDir();
66238
- const themeFile = path12.join(customThemesDir, `${currentThemeName}.json`);
66526
+ const themeFile = path11.join(customThemesDir, `${currentThemeName}.json`);
66239
66527
  if (!fs13.existsSync(themeFile)) {
66240
66528
  return;
66241
66529
  }
@@ -66436,7 +66724,7 @@ function getSettingsListTheme() {
66436
66724
  var import_yaml = __toESM(require_dist2(), 1);
66437
66725
  import * as fs14 from "fs";
66438
66726
  import * as os4 from "os";
66439
- import * as path13 from "path";
66727
+ import * as path12 from "path";
66440
66728
  function parseSkillDescription(content) {
66441
66729
  content = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
66442
66730
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
@@ -66460,8 +66748,8 @@ function parseSkillDescription(content) {
66460
66748
  }
66461
66749
  var USER_CONTROLLED_SOURCES = /* @__PURE__ */ new Set(["user-agents", "user"]);
66462
66750
  function pathIsInside(child, parent) {
66463
- const relative3 = path13.relative(parent, child);
66464
- return relative3 !== "" && !relative3.startsWith(`..${path13.sep}`) && relative3 !== ".." && !path13.isAbsolute(relative3);
66751
+ const relative3 = path12.relative(parent, child);
66752
+ return relative3 !== "" && !relative3.startsWith(`..${path12.sep}`) && relative3 !== ".." && !path12.isAbsolute(relative3);
66465
66753
  }
66466
66754
  function resolveSkillSymlink(fullPath, dir, source) {
66467
66755
  let target;
@@ -66486,7 +66774,7 @@ function loadSkillsFromDir(dir, source) {
66486
66774
  const entries = fs14.readdirSync(dir, { withFileTypes: true });
66487
66775
  for (const entry of entries) {
66488
66776
  if (entry.name.startsWith(".")) continue;
66489
- const fullPath = path13.join(dir, entry.name);
66777
+ const fullPath = path12.join(dir, entry.name);
66490
66778
  let entryPath = fullPath;
66491
66779
  let entryIsDirectory = entry.isDirectory();
66492
66780
  let entryIsFile = entry.isFile();
@@ -66501,7 +66789,7 @@ function loadSkillsFromDir(dir, source) {
66501
66789
  entryIsFile = resolved.isFile;
66502
66790
  }
66503
66791
  if (entryIsDirectory) {
66504
- const skillMdPath = path13.join(entryPath, "SKILL.md");
66792
+ const skillMdPath = path12.join(entryPath, "SKILL.md");
66505
66793
  if (fs14.existsSync(skillMdPath)) {
66506
66794
  try {
66507
66795
  const content = fs14.readFileSync(skillMdPath, "utf-8");
@@ -66533,7 +66821,7 @@ function loadSkillsFromDir(dir, source) {
66533
66821
  content,
66534
66822
  filePath: entryPath,
66535
66823
  source,
66536
- baseDir: path13.dirname(entryPath)
66824
+ baseDir: path12.dirname(entryPath)
66537
66825
  });
66538
66826
  } catch (e) {
66539
66827
  diagnostics.push({
@@ -66547,18 +66835,18 @@ function loadSkillsFromDir(dir, source) {
66547
66835
  return { skills, diagnostics };
66548
66836
  }
66549
66837
  function discoverAgentsSkillsDirs(cwd) {
66550
- const resolvedCwd = path13.resolve(cwd || process.cwd());
66838
+ const resolvedCwd = path12.resolve(cwd || process.cwd());
66551
66839
  const projectRoot = resolveCliProjectRoot(resolvedCwd);
66552
66840
  const projectAgentsDirs = [];
66553
66841
  let current = projectRoot;
66554
66842
  while (true) {
66555
- projectAgentsDirs.push(path13.join(current, ".agents", "skills"));
66843
+ projectAgentsDirs.push(path12.join(current, ".agents", "skills"));
66556
66844
  if (current === resolvedCwd) break;
66557
- const child = path13.relative(current, resolvedCwd).split(path13.sep)[0];
66845
+ const child = path12.relative(current, resolvedCwd).split(path12.sep)[0];
66558
66846
  if (!child) break;
66559
- current = path13.join(current, child);
66847
+ current = path12.join(current, child);
66560
66848
  }
66561
- return { userAgentsDir: path13.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66849
+ return { userAgentsDir: path12.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
66562
66850
  }
66563
66851
  function buildSkillDirs(options) {
66564
66852
  const { userAgentsDir, projectAgentsDirs } = discoverAgentsSkillsDirs(options.cwd);
@@ -66732,7 +67020,7 @@ function validateSkillName(name) {
66732
67020
  import * as path30 from "path";
66733
67021
 
66734
67022
  // src/capabilities/read-file-understanding.ts
66735
- import * as path22 from "node:path";
67023
+ import * as path21 from "node:path";
66736
67024
  import * as fs22 from "node:fs/promises";
66737
67025
 
66738
67026
  // src/core/provider-capabilities.ts
@@ -66760,7 +67048,7 @@ function getProviderDisplayName(platform2) {
66760
67048
 
66761
67049
  // src/files/adapters/kimi-adapter.ts
66762
67050
  import * as fs15 from "node:fs";
66763
- import * as path14 from "node:path";
67051
+ import * as path13 from "node:path";
66764
67052
 
66765
67053
  // src/files/errors.ts
66766
67054
  var FileErrorMessages = {
@@ -66897,7 +67185,7 @@ var KimiFileAdapter = class {
66897
67185
  // Upload
66898
67186
  // ============================================================================
66899
67187
  async upload(filePath, options = {}) {
66900
- const fileName = options.sourceName || path14.basename(filePath);
67188
+ const fileName = options.sourceName || path13.basename(filePath);
66901
67189
  const mimeType = options.mimeType || this.guessMimeType(fileName);
66902
67190
  const purpose = this.detectPurpose(filePath, options.purpose, mimeType);
66903
67191
  const stats = fs15.statSync(filePath);
@@ -66959,7 +67247,7 @@ var KimiFileAdapter = class {
66959
67247
  return "file-extract";
66960
67248
  }
66961
67249
  guessMimeType(filename) {
66962
- const ext = path14.extname(filename).toLowerCase();
67250
+ const ext = path13.extname(filename).toLowerCase();
66963
67251
  switch (ext) {
66964
67252
  case ".jpg":
66965
67253
  case ".jpeg":
@@ -67122,7 +67410,7 @@ var KimiFileAdapter = class {
67122
67410
 
67123
67411
  // src/files/adapters/gemini-adapter.ts
67124
67412
  import * as fs16 from "node:fs";
67125
- import * as path15 from "node:path";
67413
+ import * as path14 from "node:path";
67126
67414
  import { Readable } from "node:stream";
67127
67415
  var GeminiFileAdapter = class {
67128
67416
  platform = "gemini";
@@ -67187,7 +67475,7 @@ var GeminiFileAdapter = class {
67187
67475
  );
67188
67476
  }
67189
67477
  const mimeType = options.mimeType || this.guessMimeType(filePath);
67190
- const displayName = options.sourceName || path15.basename(filePath);
67478
+ const displayName = options.sourceName || path14.basename(filePath);
67191
67479
  const startResponse = await fetch(
67192
67480
  `${this.baseUrl}/upload/v1beta/files?key=${this.apiKey}`,
67193
67481
  {
@@ -67423,7 +67711,7 @@ var GeminiFileAdapter = class {
67423
67711
 
67424
67712
  // src/files/adapters/openai-compatible-files-adapter.ts
67425
67713
  import * as fs17 from "node:fs";
67426
- import * as path16 from "node:path";
67714
+ import * as path15 from "node:path";
67427
67715
  var OpenAICompatibleFilesAdapter = class {
67428
67716
  constructor(apiKey, baseUrl, extraHeaders = {}) {
67429
67717
  this.apiKey = apiKey;
@@ -67449,7 +67737,7 @@ var OpenAICompatibleFilesAdapter = class {
67449
67737
  ]
67450
67738
  };
67451
67739
  async upload(filePath, options = {}) {
67452
- const fileName = options.sourceName || path16.basename(filePath);
67740
+ const fileName = options.sourceName || path15.basename(filePath);
67453
67741
  const mimeType = options.mimeType || guessMimeType(fileName);
67454
67742
  const stats = fs17.statSync(filePath);
67455
67743
  if (stats.size > this.capabilities.maxFileSize) {
@@ -67575,7 +67863,7 @@ function extractContentText(raw) {
67575
67863
  return raw;
67576
67864
  }
67577
67865
  function guessMimeType(filename) {
67578
- const ext = path16.extname(filename).toLowerCase();
67866
+ const ext = path15.extname(filename).toLowerCase();
67579
67867
  switch (ext) {
67580
67868
  case ".pdf":
67581
67869
  return "application/pdf";
@@ -67592,12 +67880,12 @@ function guessMimeType(filename) {
67592
67880
 
67593
67881
  // src/files/file-manager.ts
67594
67882
  import * as fs21 from "node:fs";
67595
- import * as path21 from "node:path";
67883
+ import * as path20 from "node:path";
67596
67884
  import * as crypto2 from "node:crypto";
67597
67885
 
67598
67886
  // src/files/file-cache.ts
67599
67887
  import * as fs18 from "node:fs";
67600
- import * as path18 from "node:path";
67888
+ import * as path17 from "node:path";
67601
67889
  import * as crypto from "node:crypto";
67602
67890
 
67603
67891
  // src/files/logger.ts
@@ -67692,7 +67980,7 @@ var DEFAULT_PIC_OPTIONS = {
67692
67980
  };
67693
67981
 
67694
67982
  // src/files/utils.ts
67695
- import * as path17 from "node:path";
67983
+ import * as path16 from "node:path";
67696
67984
  import { homedir as homedir5 } from "node:os";
67697
67985
  var WINDOWS_ABSOLUTE_PATH_RE = /^(?:[a-zA-Z]:[\\/]|[\\/]{2}[^\\/]+[\\/][^\\/]+)/;
67698
67986
  function isWindowsAbsolutePath(filePath) {
@@ -67713,13 +68001,13 @@ function formatBytes(bytes) {
67713
68001
  }
67714
68002
  function resolvePath2(inputPath) {
67715
68003
  if (inputPath.startsWith("~")) {
67716
- return path17.join(getHomeDirectory(), inputPath.slice(1));
68004
+ return path16.join(getHomeDirectory(), inputPath.slice(1));
67717
68005
  }
67718
68006
  if (isWindowsAbsolutePath(inputPath)) {
67719
68007
  return inputPath;
67720
68008
  }
67721
- if (!path17.isAbsolute(inputPath)) {
67722
- return path17.resolve(process.cwd(), inputPath);
68009
+ if (!path16.isAbsolute(inputPath)) {
68010
+ return path16.resolve(process.cwd(), inputPath);
67723
68011
  }
67724
68012
  return inputPath;
67725
68013
  }
@@ -67747,8 +68035,8 @@ var FileCache = class _FileCache {
67747
68035
  maxEntries;
67748
68036
  constructor(baseDir, maxEntries = DEFAULT_MAX_CACHE_ENTRIES) {
67749
68037
  this.baseDir = baseDir;
67750
- this.dataDir = path18.join(baseDir, "data");
67751
- this.indexPath = path18.join(baseDir, "index.json");
68038
+ this.dataDir = path17.join(baseDir, "data");
68039
+ this.indexPath = path17.join(baseDir, "index.json");
67752
68040
  this.maxEntries = maxEntries;
67753
68041
  this.index = this.loadIndex();
67754
68042
  this.ensureDirectories();
@@ -67814,15 +68102,15 @@ var FileCache = class _FileCache {
67814
68102
  // ============================================================================
67815
68103
  getStoragePath(hash) {
67816
68104
  const subdir = hash.slice(0, 2);
67817
- const fullDir = path18.join(this.dataDir, subdir);
68105
+ const fullDir = path17.join(this.dataDir, subdir);
67818
68106
  if (!fs18.existsSync(fullDir)) {
67819
68107
  fs18.mkdirSync(fullDir, { recursive: true });
67820
68108
  }
67821
- return path18.join(fullDir, hash);
68109
+ return path17.join(fullDir, hash);
67822
68110
  }
67823
68111
  getStorageRelativePath(hash) {
67824
68112
  const subdir = hash.slice(0, 2);
67825
- return path18.join(subdir, hash);
68113
+ return path17.join(subdir, hash);
67826
68114
  }
67827
68115
  async storeFile(sourcePath, name, mimeType) {
67828
68116
  const hash = await this.computeHash(sourcePath);
@@ -67884,7 +68172,7 @@ var FileCache = class _FileCache {
67884
68172
  getLocalPath(hash) {
67885
68173
  const entry = this.index.files[hash];
67886
68174
  if (!entry) return void 0;
67887
- return path18.join(this.dataDir, entry.localPath);
68175
+ return path17.join(this.dataDir, entry.localPath);
67888
68176
  }
67889
68177
  exists(hash) {
67890
68178
  return hash in this.index.files;
@@ -67964,7 +68252,7 @@ var FileCache = class _FileCache {
67964
68252
  this.index.pathHints[this.getPathHintKey(resolvedPath)] = {
67965
68253
  path: resolvedPath,
67966
68254
  hash,
67967
- name: name || entry?.name || path18.basename(resolvedPath),
68255
+ name: name || entry?.name || path17.basename(resolvedPath),
67968
68256
  size: stat2.size,
67969
68257
  mtimeMs: normalizedMtimeMs,
67970
68258
  lastSeenAt: Date.now(),
@@ -68012,7 +68300,7 @@ var FileCache = class _FileCache {
68012
68300
  deleteEntry(hash, entry) {
68013
68301
  entry = entry || this.index.files[hash];
68014
68302
  if (!entry) return;
68015
- const localPath = path18.join(this.dataDir, entry.localPath);
68303
+ const localPath = path17.join(this.dataDir, entry.localPath);
68016
68304
  if (fs18.existsSync(localPath)) {
68017
68305
  fs18.unlinkSync(localPath);
68018
68306
  }
@@ -68028,7 +68316,7 @@ var FileCache = class _FileCache {
68028
68316
  }
68029
68317
  clear() {
68030
68318
  for (const entry of Object.values(this.index.files)) {
68031
- const localPath = path18.join(this.dataDir, entry.localPath);
68319
+ const localPath = path17.join(this.dataDir, entry.localPath);
68032
68320
  if (fs18.existsSync(localPath)) {
68033
68321
  fs18.unlinkSync(localPath);
68034
68322
  }
@@ -68083,11 +68371,11 @@ var FileCache = class _FileCache {
68083
68371
  };
68084
68372
  }
68085
68373
  static getMimeType(fileName) {
68086
- const ext = path18.extname(fileName).toLowerCase();
68374
+ const ext = path17.extname(fileName).toLowerCase();
68087
68375
  return MIME_TYPES[ext] || "application/octet-stream";
68088
68376
  }
68089
68377
  static getFileTypeFromName(fileName) {
68090
- const baseName = path18.basename(fileName);
68378
+ const baseName = path17.basename(fileName);
68091
68379
  if (!baseName || baseName.startsWith(".")) {
68092
68380
  return void 0;
68093
68381
  }
@@ -68127,7 +68415,7 @@ var FileCache = class _FileCache {
68127
68415
 
68128
68416
  // src/files/session-file-manager.ts
68129
68417
  import * as fs19 from "node:fs";
68130
- import * as path19 from "node:path";
68418
+ import * as path18 from "node:path";
68131
68419
  var INITIAL_REFRESH_BACKOFF_MS = 6e4;
68132
68420
  var MAX_REFRESH_BACKOFF_MS = 60 * 60 * 1e3;
68133
68421
  var SessionFileManager = class {
@@ -68141,7 +68429,7 @@ var SessionFileManager = class {
68141
68429
  constructor(sessionId, sessionsDir, fileCache, authFingerprint) {
68142
68430
  this.sessionId = sessionId;
68143
68431
  this.sessionsDir = sessionsDir;
68144
- this.statePath = path19.join(this.sessionsDir, `${sessionId}-files.json`);
68432
+ this.statePath = path18.join(this.sessionsDir, `${sessionId}-files.json`);
68145
68433
  this.fileCache = fileCache;
68146
68434
  this.authFingerprint = authFingerprint;
68147
68435
  this.state = this.loadState();
@@ -68487,7 +68775,7 @@ var SessionFileManager = class {
68487
68775
  const files = fs19.readdirSync(sessionsDir);
68488
68776
  for (const file of files) {
68489
68777
  if (!file.endsWith(".json")) continue;
68490
- const filePath = path19.join(sessionsDir, file);
68778
+ const filePath = path18.join(sessionsDir, file);
68491
68779
  const stat2 = fs19.statSync(filePath);
68492
68780
  if (now - stat2.mtime.getTime() > maxAge) {
68493
68781
  fs19.unlinkSync(filePath);
@@ -68499,7 +68787,7 @@ var SessionFileManager = class {
68499
68787
 
68500
68788
  // src/files/ffmpeg-processor.ts
68501
68789
  import * as fs20 from "node:fs";
68502
- import * as path20 from "node:path";
68790
+ import * as path19 from "node:path";
68503
68791
  import { spawn } from "node:child_process";
68504
68792
  var FFmpegProcessor = class {
68505
68793
  ffmpegPath = "ffmpeg";
@@ -68603,7 +68891,7 @@ var FFmpegProcessor = class {
68603
68891
  }
68604
68892
  async compressVideo(inputPath, outputDir, info, opts, onProgress) {
68605
68893
  const outputName = `compressed_${Date.now()}.mp4`;
68606
- const outputPath = path20.join(outputDir, outputName);
68894
+ const outputPath = path19.join(outputDir, outputName);
68607
68895
  const adaptiveParams = this.calculateAdaptiveParams(info.duration, opts);
68608
68896
  const args = [
68609
68897
  "-i",
@@ -68665,9 +68953,9 @@ var FFmpegProcessor = class {
68665
68953
  }
68666
68954
  async extractKeyframes(inputPath, outputDir, info, opts, onProgress) {
68667
68955
  const interval = Math.floor(info.duration / opts.keyframeCount);
68668
- const framesDir = path20.join(outputDir, `keyframes_${Date.now()}`);
68956
+ const framesDir = path19.join(outputDir, `keyframes_${Date.now()}`);
68669
68957
  fs20.mkdirSync(framesDir, { recursive: true });
68670
- const outputPattern = path20.join(framesDir, "frame_%03d.jpg");
68958
+ const outputPattern = path19.join(framesDir, "frame_%03d.jpg");
68671
68959
  const args = [
68672
68960
  "-i",
68673
68961
  inputPath,
@@ -68685,7 +68973,7 @@ var FFmpegProcessor = class {
68685
68973
  onProgress?.(percent);
68686
68974
  }
68687
68975
  });
68688
- let frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path20.join(framesDir, f));
68976
+ let frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
68689
68977
  if (frames.length < opts.keyframeCount / 2) {
68690
68978
  fs20.rmSync(framesDir, { recursive: true, force: true });
68691
68979
  fs20.mkdirSync(framesDir, { recursive: true });
@@ -68704,7 +68992,7 @@ var FFmpegProcessor = class {
68704
68992
  onProgress?.(percent);
68705
68993
  }
68706
68994
  });
68707
- frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path20.join(framesDir, f));
68995
+ frames = fs20.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
68708
68996
  }
68709
68997
  const totalOutputSize = frames.reduce(
68710
68998
  (sum, f) => sum + fs20.statSync(f).size,
@@ -68728,7 +69016,7 @@ var FFmpegProcessor = class {
68728
69016
  const opts = { ...DEFAULT_PIC_OPTIONS, ...options };
68729
69017
  const ext = opts.format === "webp" ? ".webp" : ".jpg";
68730
69018
  const outputName = `optimized_${Date.now()}${ext}`;
68731
- const outputPath = path20.join(outputDir, outputName);
69019
+ const outputPath = path19.join(outputDir, outputName);
68732
69020
  const args = [
68733
69021
  "-i",
68734
69022
  inputPath,
@@ -68903,8 +69191,8 @@ var DIRECTORY_LISTING_TTL_MS = 1500;
68903
69191
  var FileManager = class {
68904
69192
  constructor(options) {
68905
69193
  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");
69194
+ const baseDir = options.baseDir || path20.join(process.env.HOME || ".", ".pie", "files");
69195
+ const sessionsDir = options.sessionsDir || path20.join(process.env.HOME || ".", ".pie", "sessions");
68908
69196
  this.fileCache = new FileCache(baseDir);
68909
69197
  this.authFingerprint = options.apiKey ? this.buildAuthFingerprint(options.apiKey) : void 0;
68910
69198
  this.sessionManager = new SessionFileManager(
@@ -68915,7 +69203,7 @@ var FileManager = class {
68915
69203
  );
68916
69204
  this.ffmpeg = new FFmpegProcessor(options.ffmpegPath);
68917
69205
  this.platform = normalizeFileManagerPlatform(options.platform);
68918
- this.tempDir = path21.join(baseDir, "temp");
69206
+ this.tempDir = path20.join(baseDir, "temp");
68919
69207
  this.ensureDirectories();
68920
69208
  }
68921
69209
  options;
@@ -68962,7 +69250,7 @@ var FileManager = class {
68962
69250
  // ============================================================================
68963
69251
  async stageLocal(filePath, options = {}) {
68964
69252
  const resolvedPath = resolvePath2(filePath);
68965
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69253
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68966
69254
  if (!fs21.existsSync(resolvedPath)) {
68967
69255
  throw new FileManagerError(
68968
69256
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -68992,7 +69280,7 @@ var FileManager = class {
68992
69280
  }
68993
69281
  async upload(filePath, options = {}) {
68994
69282
  const resolvedPath = resolvePath2(filePath);
68995
- const sourceName = options.sourceName || path21.basename(resolvedPath);
69283
+ const sourceName = options.sourceName || path20.basename(resolvedPath);
68996
69284
  if (!fs21.existsSync(resolvedPath)) {
68997
69285
  throw new FileManagerError(
68998
69286
  "LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
@@ -69082,7 +69370,7 @@ var FileManager = class {
69082
69370
  }
69083
69371
  const cachedResult = await this.tryReuseCachedSourceUpload(
69084
69372
  resolvedPath,
69085
- options.sourceName || path21.basename(resolvedPath),
69373
+ options.sourceName || path20.basename(resolvedPath),
69086
69374
  options.platform || this.platform
69087
69375
  );
69088
69376
  if (cachedResult) {
@@ -69099,9 +69387,9 @@ var FileManager = class {
69099
69387
  const result = await this.upload(optimizedPath, {
69100
69388
  ...options,
69101
69389
  purpose: "image",
69102
- sourceName: path21.basename(resolvedPath)
69390
+ sourceName: path20.basename(resolvedPath)
69103
69391
  });
69104
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69392
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69105
69393
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69106
69394
  try {
69107
69395
  fs21.unlinkSync(optimizedPath);
@@ -69122,7 +69410,7 @@ var FileManager = class {
69122
69410
  }
69123
69411
  const cachedResult = await this.tryReuseCachedSourceUpload(
69124
69412
  resolvedPath,
69125
- options.sourceName || path21.basename(resolvedPath),
69413
+ options.sourceName || path20.basename(resolvedPath),
69126
69414
  options.platform || this.platform
69127
69415
  );
69128
69416
  if (cachedResult) {
@@ -69172,9 +69460,9 @@ var FileManager = class {
69172
69460
  result = await this.upload(preprocessResult.output, {
69173
69461
  ...options,
69174
69462
  purpose: "video",
69175
- sourceName: path21.basename(resolvedPath)
69463
+ sourceName: path20.basename(resolvedPath)
69176
69464
  });
69177
- this.fileCache.rememberPathHint(resolvedPath, result.hash, path21.basename(resolvedPath));
69465
+ this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
69178
69466
  this.invalidateDirectoryListingCacheForPath(resolvedPath);
69179
69467
  } else {
69180
69468
  throw new FileManagerError(
@@ -69466,7 +69754,7 @@ var FileManager = class {
69466
69754
  * Detect purpose from file path based on extension
69467
69755
  */
69468
69756
  detectPurpose(filePath) {
69469
- const ext = path21.extname(filePath).toLowerCase();
69757
+ const ext = path20.extname(filePath).toLowerCase();
69470
69758
  switch (ext) {
69471
69759
  case ".mp4":
69472
69760
  case ".mov":
@@ -69648,7 +69936,7 @@ var FileManager = class {
69648
69936
  if (!entry.name || entry.name.startsWith(".")) {
69649
69937
  continue;
69650
69938
  }
69651
- const fullPath = path21.join(resolvedDir, entry.name);
69939
+ const fullPath = path20.join(resolvedDir, entry.name);
69652
69940
  if (entry.isDirectory()) {
69653
69941
  listing.directories.push({ name: entry.name, path: fullPath });
69654
69942
  continue;
@@ -69689,7 +69977,7 @@ var FileManager = class {
69689
69977
  return listing;
69690
69978
  }
69691
69979
  invalidateDirectoryListingCacheForPath(filePath) {
69692
- this.directoryListingCache.delete(path21.dirname(resolvePath2(filePath)));
69980
+ this.directoryListingCache.delete(path20.dirname(resolvePath2(filePath)));
69693
69981
  }
69694
69982
  buildAuthFingerprint(apiKey) {
69695
69983
  return crypto2.createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
@@ -69738,8 +70026,8 @@ function createFileManagerForModel(params) {
69738
70026
  sessionId: params.sessionId,
69739
70027
  platform: platform2,
69740
70028
  apiKey: params.apiKey,
69741
- baseDir: path22.join(params.configDir, "files"),
69742
- sessionsDir: path22.join(params.configDir, "sessions")
70029
+ baseDir: path21.join(params.configDir, "files"),
70030
+ sessionsDir: path21.join(params.configDir, "sessions")
69743
70031
  });
69744
70032
  switch (getFileManagerAdapterKind(platform2)) {
69745
70033
  case "kimi":
@@ -69779,7 +70067,7 @@ async function prepareInlineMediaContent(params) {
69779
70067
  const limitBytes = limitMb * 1024 * 1024;
69780
70068
  if (stat2.size > limitBytes) {
69781
70069
  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.`
70070
+ `${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
70071
  );
69784
70072
  }
69785
70073
  const data = await fs22.readFile(params.request.absolutePath, "base64");
@@ -69818,7 +70106,7 @@ async function prepareFileContent(configDir, params) {
69818
70106
  });
69819
70107
  if (params.request.kind === "document") {
69820
70108
  const upload2 = await manager.upload(params.request.absolutePath, {
69821
- sourceName: path22.basename(params.request.displayPath),
70109
+ sourceName: path21.basename(params.request.displayPath),
69822
70110
  purpose: "file-extract"
69823
70111
  });
69824
70112
  await waitForReady({ manager, fileId: upload2.fileId, timeoutMs: 12e4, signal: params.request.signal });
@@ -69841,8 +70129,8 @@ async function prepareFileContent(configDir, params) {
69841
70129
  }
69842
70130
  };
69843
70131
  }
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),
70132
+ 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, {
70133
+ sourceName: path21.basename(params.request.displayPath),
69846
70134
  purpose: params.request.kind === "image" ? "image" : "file-extract"
69847
70135
  });
69848
70136
  if (params.request.kind !== "video") {
@@ -70012,18 +70300,74 @@ function createCliFileSystemTools(cwd, options) {
70012
70300
  });
70013
70301
  }
70014
70302
 
70303
+ // src/file-access-context.ts
70304
+ import * as path22 from "node:path";
70305
+ function uniqueNormalizedPaths(paths) {
70306
+ const seen = /* @__PURE__ */ new Set();
70307
+ const result = [];
70308
+ for (const entry of paths) {
70309
+ const trimmed = String(entry || "").trim();
70310
+ if (!trimmed) continue;
70311
+ const normalized = path22.normalize(trimmed);
70312
+ if (seen.has(normalized)) continue;
70313
+ seen.add(normalized);
70314
+ result.push(normalized);
70315
+ }
70316
+ return result;
70317
+ }
70318
+ function buildCliFileAccessOptions(params) {
70319
+ return {
70320
+ accessRoots: uniqueNormalizedPaths([
70321
+ resolveCliProjectRoot(params.cwd),
70322
+ params.configDir,
70323
+ ...params.skills.map((skill) => skill.baseDir)
70324
+ ]),
70325
+ allowlistedDirs: params.allowlistedDirs,
70326
+ descriptionAllowlistedDirs: params.descriptionAllowlistedDirs
70327
+ };
70328
+ }
70329
+
70330
+ // src/interactive-filesystem-tools.ts
70331
+ function buildInteractiveFilesystemToolOptions(host, allowlistedDirs) {
70332
+ return {
70333
+ ...buildCliFileAccessOptions({
70334
+ cwd: host.cwd,
70335
+ configDir: host.configDir,
70336
+ skills: host.skills,
70337
+ allowlistedDirs,
70338
+ descriptionAllowlistedDirs: []
70339
+ }),
70340
+ configDir: host.configDir,
70341
+ fileObservationState: host.fileObservationState,
70342
+ getAgentModel: host.getAgentModel,
70343
+ getSessionId: () => host.getActiveSessionId() || void 0,
70344
+ resolveToolModel: host.resolveToolModel,
70345
+ resolveToolModelCandidates: host.resolveToolModelCandidates,
70346
+ getPolicyContext: host.getPlanPolicyContext
70347
+ };
70348
+ }
70349
+
70015
70350
  // src/runtime/yolo-grant.ts
70016
70351
  var YoloGrantTracker = class {
70017
70352
  untilMs = 0;
70018
70353
  isActive(now = Date.now()) {
70019
70354
  return this.untilMs > now;
70020
70355
  }
70356
+ remainingMs(now = Date.now()) {
70357
+ return Math.max(0, this.untilMs - now);
70358
+ }
70021
70359
  award(grantMs, now = Date.now()) {
70022
70360
  this.untilMs = now + Math.max(0, grantMs);
70023
70361
  }
70024
70362
  expire() {
70025
70363
  this.untilMs = 0;
70026
70364
  }
70365
+ /** Revoke the active grant and report whether revocation changed live state. */
70366
+ revoke() {
70367
+ const wasActive = this.isActive();
70368
+ this.expire();
70369
+ return wasActive;
70370
+ }
70027
70371
  /** Expiry timestamp (0 when no grant is held); useful for display. */
70028
70372
  get until() {
70029
70373
  return this.untilMs;
@@ -76298,7 +76642,9 @@ async function handleCalmCommand(host, args) {
76298
76642
  async function handleYoloCommand(host, args) {
76299
76643
  const mode = (args || "").trim().toLowerCase();
76300
76644
  if (mode === "status") {
76301
- host.showStatus(`YOLO mode is ${host.yoloMode ? "on" : "off"}`);
76645
+ const persistent = `YOLO mode is ${host.yoloMode ? "on" : "off"}`;
76646
+ const grantRemaining = host.getYoloGrantRemainingMs();
76647
+ host.showStatus(grantRemaining > 0 ? `${persistent}; temporary high-risk shell grant active for ${Math.max(1, Math.ceil(grantRemaining / 6e4))} more min` : persistent);
76302
76648
  return;
76303
76649
  }
76304
76650
  if (mode && !["on", "off", "enable", "enabled", "disable", "disabled", "true", "false", "1", "0"].includes(mode)) {
@@ -76306,9 +76652,10 @@ async function handleYoloCommand(host, args) {
76306
76652
  return;
76307
76653
  }
76308
76654
  const nextYoloMode = mode ? ["on", "enable", "enabled", "true", "1"].includes(mode) : !host.yoloMode;
76655
+ const revokedGrant = nextYoloMode ? false : host.revokeYoloGrant();
76309
76656
  if (host.yoloMode === nextYoloMode) {
76310
76657
  host.footer.setState({ yoloMode: host.yoloMode });
76311
- host.showStatus(`YOLO mode already ${host.yoloMode ? "on" : "off"}`);
76658
+ host.showStatus(`YOLO mode already ${host.yoloMode ? "on" : "off"}${revokedGrant ? "; temporary grant revoked" : ""}`);
76312
76659
  return;
76313
76660
  }
76314
76661
  host.yoloMode = nextYoloMode;
@@ -76326,6 +76673,7 @@ async function handleYoloCommand(host, args) {
76326
76673
  host.chatContainer.addChild(new Spacer(1));
76327
76674
  host.chatContainer.addChild(new Text(theme.fg("accent", "\u2713 YOLO mode disabled"), 1, 0));
76328
76675
  host.chatContainer.addChild(new Text(theme.fg("dim", " Sandbox directory restrictions restored."), 1, 0));
76676
+ if (revokedGrant) host.chatContainer.addChild(new Text(theme.fg("dim", " Temporary high-risk shell grant revoked."), 1, 0));
76329
76677
  host.chatContainer.addChild(new Spacer(1));
76330
76678
  }
76331
76679
  host.ui.requestRender();
@@ -77106,35 +77454,40 @@ var RuntimeModeBlockInjector = class {
77106
77454
  // src/runtime/host-envelope-registry.ts
77107
77455
  var sharedCliEnvelopeRegistry;
77108
77456
  function cliHostEnvelopeRegistry() {
77109
- if (sharedCliEnvelopeRegistry) return sharedCliEnvelopeRegistry;
77110
- sharedCliEnvelopeRegistry = createHostEnvelopeProjectorRegistry();
77457
+ return sharedCliEnvelopeRegistry ??= createCliHostEnvelopeRegistry();
77458
+ }
77459
+ function createCliHostEnvelopeRegistry() {
77460
+ const registry = createHostEnvelopeProjectorRegistry();
77111
77461
  for (const { kind, version } of runtimeModeEnvelopeRegistry().listPins()) {
77112
- sharedCliEnvelopeRegistry.register({
77462
+ registry.register({
77113
77463
  kind,
77114
77464
  version,
77115
77465
  project: (data) => runtimeModeEnvelopeRegistry().resolve(kind, version).project(data)
77116
77466
  });
77117
77467
  }
77118
- return sharedCliEnvelopeRegistry;
77468
+ return registry;
77119
77469
  }
77120
- function registerExternalHostEnvelopeProjector(projector) {
77121
- cliHostEnvelopeRegistry().register(projector);
77470
+ function registerExternalHostEnvelopeProjector(projector, registry = cliHostEnvelopeRegistry()) {
77471
+ registry.register(projector);
77122
77472
  return () => {
77123
77473
  };
77124
77474
  }
77125
77475
  function resetCliHostEnvelopeRegistry() {
77126
77476
  sharedCliEnvelopeRegistry = void 0;
77127
77477
  }
77128
- function cliHostEnvelopeKinds() {
77129
- return cliHostEnvelopeRegistry().listPins().map(({ kind, version }) => `${kind}@${version}`).sort();
77478
+ function cliHostEnvelopeKinds(registry = cliHostEnvelopeRegistry()) {
77479
+ return registry.listPins().map(({ kind, version }) => `${kind}@${version}`).sort();
77480
+ }
77481
+ function projectCliHostEnvelopes(messages, onProjection, registry = cliHostEnvelopeRegistry()) {
77482
+ return projectHostEnvelopes(messages, registry, onProjection);
77130
77483
  }
77131
- function projectCliHostEnvelopes(messages, onProjection) {
77132
- return projectHostEnvelopes(messages, cliHostEnvelopeRegistry(), onProjection);
77484
+ function cliCompactionModelView(message) {
77485
+ return projectCliHostEnvelopes([message])[0];
77133
77486
  }
77134
77487
  function isIntakeHostContextError(value) {
77135
77488
  return value.code !== void 0;
77136
77489
  }
77137
- function intakeHostContextBlock(value) {
77490
+ function intakeHostContextBlock(value, registry = cliHostEnvelopeRegistry()) {
77138
77491
  if (!isHostContextBlock(value)) {
77139
77492
  return {
77140
77493
  ok: false,
@@ -77143,12 +77496,12 @@ function intakeHostContextBlock(value) {
77143
77496
  };
77144
77497
  }
77145
77498
  const block = value;
77146
- const projector = cliHostEnvelopeRegistry().resolve(block.kind, block.version);
77499
+ const projector = registry.resolve(block.kind, block.version);
77147
77500
  if (!projector) {
77148
77501
  return {
77149
77502
  ok: false,
77150
77503
  code: "host_context_unknown_projector",
77151
- message: `no projector for ${block.kind}@${block.version}; this build supports: ${cliHostEnvelopeKinds().join(", ")}`
77504
+ message: `no projector for ${block.kind}@${block.version}; this build supports: ${cliHostEnvelopeKinds(registry).join(", ")}`
77152
77505
  };
77153
77506
  }
77154
77507
  try {
@@ -77163,8 +77516,8 @@ function intakeHostContextBlock(value) {
77163
77516
  }
77164
77517
  return { ok: true, block };
77165
77518
  }
77166
- function intakeHostContextFailure(value) {
77167
- const result = intakeHostContextBlock(value);
77519
+ function intakeHostContextFailure(value, registry = cliHostEnvelopeRegistry()) {
77520
+ const result = intakeHostContextBlock(value, registry);
77168
77521
  return isIntakeHostContextError(result) ? { code: result.code, message: result.message } : void 0;
77169
77522
  }
77170
77523
 
@@ -77196,13 +77549,14 @@ async function handleCompactCommand(host) {
77196
77549
  const preparation = prepareSessionCompaction({
77197
77550
  sessionManager: host.sessionManager,
77198
77551
  messages: existingMessages,
77552
+ modelView: cliCompactionModelView,
77199
77553
  budgetTokens: host.options.model?.contextWindow
77200
77554
  });
77201
77555
  if (preparation.entries.length === 0) {
77202
77556
  host.showStatus("Need at least one real user message before compacting");
77203
77557
  return;
77204
77558
  }
77205
- const tokensBefore = calculateTotalTokens(preparation.messages);
77559
+ const tokensBefore = preparation.inputTokens;
77206
77560
  const compactOptions = {
77207
77561
  model: host.options.model,
77208
77562
  apiKey: host.options.apiKey,
@@ -78420,6 +78774,7 @@ var FileStatusMonitor = class {
78420
78774
 
78421
78775
  // src/files/mention/mention-handler.ts
78422
78776
  import * as fs26 from "node:fs";
78777
+ import { randomUUID } from "node:crypto";
78423
78778
  import * as path28 from "node:path";
78424
78779
  var MentionHandler = class {
78425
78780
  fileManager;
@@ -78513,75 +78868,11 @@ var MentionHandler = class {
78513
78868
  this.onInsertMention(mention, queryToken);
78514
78869
  this.onStatus?.(`Added ${option.name} (already uploaded to ${this.fileManager.getPlatformDisplayName()})`);
78515
78870
  } 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
- }
78871
+ await this.attachFileFromPath(option.path, {
78872
+ sourceName: option.name,
78873
+ queryToken,
78874
+ cacheStatus: option.cacheStatus
78875
+ });
78585
78876
  } else {
78586
78877
  this.onError?.("Invalid file option.");
78587
78878
  }
@@ -78589,6 +78880,110 @@ var MentionHandler = class {
78589
78880
  this.onHidePicker();
78590
78881
  this.isPickerVisible = false;
78591
78882
  }
78883
+ /**
78884
+ * Attach a local file from disk without going through the mention picker.
78885
+ *
78886
+ * Used by the clipboard image paste flow: the file goes through the exact
78887
+ * same pending-mention → stage/upload → final-mention lifecycle as a
78888
+ * picker-selected file, so submit-time queueing and attachment resolution
78889
+ * behave identically.
78890
+ */
78891
+ async attachLocalFile(path34, displayName, pendingMention, isCurrent) {
78892
+ await this.attachFileFromPath(path34, { sourceName: displayName, queryToken: pendingMention, isCurrent });
78893
+ }
78894
+ /** Reserve a real queue-replaceable token before asynchronous clipboard IO. */
78895
+ beginPendingAttachment(name) {
78896
+ const token = `@[${name}](pending:clipboard-${randomUUID()})`;
78897
+ this.pendingUploadCount++;
78898
+ this.onInsertMention(`${token} `);
78899
+ let finished = false;
78900
+ return { token, finish: () => {
78901
+ if (finished) return;
78902
+ finished = true;
78903
+ this.onInsertMention("", token);
78904
+ this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78905
+ } };
78906
+ }
78907
+ /**
78908
+ * Shared core of "attach a file from a local path": insert a pending
78909
+ * mention, stage or upload the file, then replace the pending mention with
78910
+ * the final one (or remove it on failure).
78911
+ */
78912
+ async attachFileFromPath(path34, options) {
78913
+ const sourceName = options.sourceName ?? path34.split(/[\\/]/).pop() ?? path34;
78914
+ this.pendingUploadCount += 1;
78915
+ const pendingMention = options.queryToken?.includes("pending:clipboard-") ? options.queryToken : this.formatPendingMention(sourceName);
78916
+ this.onInsertMention(pendingMention, options.queryToken);
78917
+ const pendingStatusKey = `${path34}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
78918
+ try {
78919
+ if (options.cacheStatus === "uploaded") {
78920
+ this.pushPendingStatus(
78921
+ pendingStatusKey,
78922
+ `Preparing ${sourceName} (reusing upload from ${this.fileManager.getPlatformDisplayName()})...`
78923
+ );
78924
+ } else if (options.cacheStatus === "cached") {
78925
+ this.pushPendingStatus(
78926
+ pendingStatusKey,
78927
+ `Preparing ${sourceName} (reusing local cache)...`
78928
+ );
78929
+ } else {
78930
+ this.pushPendingStatus(pendingStatusKey, `Preparing ${sourceName}...`);
78931
+ }
78932
+ const mimeType = FileCache.getMimeType(path34);
78933
+ const isMedia = mimeType.startsWith("image/") || mimeType.startsWith("video/");
78934
+ const shouldStageLocally = !isMedia || !this.fileManager.hasAdapter();
78935
+ if (isMedia && shouldStageLocally) {
78936
+ this.pushPendingStatus(pendingStatusKey, `Staging ${sourceName} locally (no upload API for this provider)...`);
78937
+ }
78938
+ const onProgress = (progress, stage) => {
78939
+ if (options.isCurrent && !options.isCurrent()) return;
78940
+ this.pushPendingStatus(
78941
+ pendingStatusKey,
78942
+ this.formatUploadProgress(sourceName, progress, stage)
78943
+ );
78944
+ };
78945
+ const result = shouldStageLocally ? await this.fileManager.stageLocal(path34, { sourceName }) : mimeType.startsWith("video/") ? await this.fileManager.uploadVideo(path34, {
78946
+ sourceName,
78947
+ onProgress
78948
+ }) : mimeType.startsWith("image/") ? await this.fileManager.uploadPic(path34, {
78949
+ sourceName,
78950
+ onProgress
78951
+ }) : await this.fileManager.upload(path34, {
78952
+ sourceName,
78953
+ onProgress
78954
+ });
78955
+ if (options.isCurrent && !options.isCurrent()) return;
78956
+ if (result.success) {
78957
+ const mention = this.formatMention(result.name, result.fileId);
78958
+ this.onInsertMention(mention, pendingMention);
78959
+ this.clearPendingStatus(pendingStatusKey);
78960
+ this.onStatus?.(shouldStageLocally ? `Added local file ${result.name}` : `Added ${result.name}`);
78961
+ } else {
78962
+ this.onInsertMention("", pendingMention);
78963
+ this.clearPendingStatus(pendingStatusKey);
78964
+ this.onError?.("File upload failed. Please try again.");
78965
+ }
78966
+ } catch (err) {
78967
+ if (options.isCurrent && !options.isCurrent()) return;
78968
+ this.onInsertMention("", pendingMention);
78969
+ this.clearPendingStatus(pendingStatusKey);
78970
+ const errorMessage5 = err instanceof Error ? err.message : String(err);
78971
+ if (errorMessage5.includes("PLATFORM_NOT_SUPPORTED")) {
78972
+ this.onError?.("The current AI model does not support file uploads.");
78973
+ } else if (errorMessage5.includes("ENOENT")) {
78974
+ this.onError?.("File does not exist or cannot be accessed.");
78975
+ } else if (errorMessage5.includes("EACCES") || errorMessage5.includes("PERMISSION")) {
78976
+ this.onError?.("Permission denied while accessing the file.");
78977
+ } else if (errorMessage5.includes("network") || errorMessage5.includes("ETIMEDOUT") || errorMessage5.includes("ECONNREFUSED")) {
78978
+ this.onError?.("Network error. Check the connection and try again.");
78979
+ } else {
78980
+ this.onError?.(`Upload failed: ${errorMessage5}`);
78981
+ }
78982
+ } finally {
78983
+ this.clearPendingStatus(pendingStatusKey);
78984
+ this.pendingUploadCount = Math.max(0, this.pendingUploadCount - 1);
78985
+ }
78986
+ }
78592
78987
  /**
78593
78988
  * Parse mentions in text and convert to platform format
78594
78989
  */
@@ -79271,6 +79666,9 @@ async function selectMentionOption(host, option) {
79271
79666
  }
79272
79667
 
79273
79668
  // src/input/interactive-keybindings.ts
79669
+ function mainEditorOwnsInput(host) {
79670
+ return host.editor.focused === true && host.editorContainer.children.includes(host.editor) && !host.ui.hasOverlay() && !host.mentionPickerComponent;
79671
+ }
79274
79672
  function setupInteractiveKeybindings(host) {
79275
79673
  host.ui.addInputListener((data) => {
79276
79674
  if (isKeyRelease(data)) {
@@ -79328,6 +79726,10 @@ function setupInteractiveKeybindings(host) {
79328
79726
  host.handleInterrupt();
79329
79727
  return { consume: true };
79330
79728
  }
79729
+ if (host.keybindings.matches(data, "pasteImage") && mainEditorOwnsInput(host)) {
79730
+ host.handlePasteImage();
79731
+ return { consume: true };
79732
+ }
79331
79733
  if (host.keybindings.matches(data, "expandTools")) {
79332
79734
  host.toggleToolsExpansion();
79333
79735
  return { consume: true };
@@ -79348,6 +79750,513 @@ function setupInteractiveKeybindings(host) {
79348
79750
  });
79349
79751
  }
79350
79752
 
79753
+ // src/clipboard/read-clipboard-image.ts
79754
+ import { spawn as spawn2 } from "node:child_process";
79755
+ import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync18, fchmodSync, mkdtempSync, openSync as openSync4, readFileSync as readFileSync17, rmdirSync, statSync as statSync12, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "node:fs";
79756
+ import { tmpdir } from "node:os";
79757
+ import { join as join20 } from "node:path";
79758
+ var MAX_IMAGE_BYTES = 50 * 1024 * 1024;
79759
+ var COMMAND_TIMEOUT_MS = 1e4;
79760
+ function isSpawnFailure(outcome) {
79761
+ return !outcome.ok;
79762
+ }
79763
+ function defaultSpawnFn(file, args, spawnImpl = spawn2, maxBytes = MAX_IMAGE_BYTES, timeoutMs = COMMAND_TIMEOUT_MS) {
79764
+ return new Promise((resolve7) => {
79765
+ let child;
79766
+ try {
79767
+ child = spawnImpl(file, args, {
79768
+ stdio: ["ignore", "pipe", "pipe"],
79769
+ windowsHide: true
79770
+ });
79771
+ } catch (err) {
79772
+ resolve7({ ok: false, error: "spawn-failed", message: err instanceof Error ? err.message : String(err) });
79773
+ return;
79774
+ }
79775
+ let settled = false;
79776
+ const stdoutChunks = [];
79777
+ let stderrText = "";
79778
+ let stdoutBytes = 0;
79779
+ const timer = setTimeout(() => {
79780
+ if (settled) return;
79781
+ settled = true;
79782
+ try {
79783
+ child.kill("SIGKILL");
79784
+ } catch {
79785
+ }
79786
+ resolve7({ ok: false, error: "timeout" });
79787
+ }, timeoutMs);
79788
+ child.on("error", (err) => {
79789
+ if (settled) return;
79790
+ settled = true;
79791
+ clearTimeout(timer);
79792
+ resolve7({ ok: false, error: err.code === "ENOENT" ? "not-found" : "spawn-failed", message: err.message });
79793
+ });
79794
+ child.stdout?.on("data", (chunk) => {
79795
+ if (settled) return;
79796
+ if (chunk.length > maxBytes - stdoutBytes) {
79797
+ settled = true;
79798
+ clearTimeout(timer);
79799
+ stdoutChunks.length = 0;
79800
+ resolve7({ ok: false, error: "too-large" });
79801
+ try {
79802
+ child.kill("SIGKILL");
79803
+ } catch {
79804
+ }
79805
+ return;
79806
+ }
79807
+ stdoutBytes += chunk.length;
79808
+ stdoutChunks.push(chunk);
79809
+ });
79810
+ child.stderr?.on("data", (chunk) => {
79811
+ if (!settled && stderrText.length < 4e3) stderrText += chunk.subarray(0, 4e3 - stderrText.length).toString("utf8");
79812
+ });
79813
+ child.on("close", (code) => {
79814
+ if (settled) return;
79815
+ settled = true;
79816
+ clearTimeout(timer);
79817
+ resolve7({ ok: true, result: { code: code ?? -1, stdout: Buffer.concat(stdoutChunks, stdoutBytes), stderr: stderrText } });
79818
+ });
79819
+ });
79820
+ }
79821
+ var TempImageResources = class {
79822
+ constructor(injectedPath) {
79823
+ this.injectedPath = injectedPath;
79824
+ }
79825
+ injectedPath;
79826
+ directory;
79827
+ files = /* @__PURE__ */ new Set();
79828
+ create = (ext) => {
79829
+ if (!this.injectedPath && !this.directory) {
79830
+ this.directory = mkdtempSync(join20(tmpdir(), "pie-clipboard-"));
79831
+ if (process.platform !== "win32") chmodSync2(this.directory, 448);
79832
+ }
79833
+ const path34 = this.injectedPath ? this.injectedPath(ext) : join20(this.directory, `image${ext}`);
79834
+ const fd = openSync4(path34, "wx", 384);
79835
+ this.files.add(path34);
79836
+ try {
79837
+ if (process.platform !== "win32") fchmodSync(fd, 384);
79838
+ } finally {
79839
+ closeSync4(fd);
79840
+ }
79841
+ return path34;
79842
+ };
79843
+ cleanup = () => {
79844
+ for (const path34 of this.files) {
79845
+ try {
79846
+ unlinkSync8(path34);
79847
+ this.files.delete(path34);
79848
+ } catch (err) {
79849
+ if (err.code === "ENOENT") this.files.delete(path34);
79850
+ }
79851
+ }
79852
+ if (this.directory) {
79853
+ try {
79854
+ rmdirSync(this.directory);
79855
+ this.directory = void 0;
79856
+ } catch (err) {
79857
+ if (err.code === "ENOENT") this.directory = void 0;
79858
+ }
79859
+ }
79860
+ };
79861
+ };
79862
+ async function readClipboardImage(deps = {}) {
79863
+ const platform2 = deps.platform ?? process.platform;
79864
+ const env = deps.env ?? process.env;
79865
+ const spawnImpl = deps.spawnImpl ?? defaultSpawnFn;
79866
+ const resources = new TempImageResources(deps.mkTempPath);
79867
+ let retained = false;
79868
+ try {
79869
+ const result = await readPlatformClipboard(platform2, env, spawnImpl, resources.create);
79870
+ if (result.ok === true) {
79871
+ retained = true;
79872
+ return { ...result, cleanup: resources.cleanup };
79873
+ }
79874
+ return result;
79875
+ } catch (err) {
79876
+ return { ok: false, kind: "failed", message: `Failed to read clipboard image: ${errorMessage4(err)}` };
79877
+ } finally {
79878
+ if (!retained) resources.cleanup();
79879
+ }
79880
+ }
79881
+ async function readPlatformClipboard(platform2, env, spawnImpl, mkTempPath) {
79882
+ switch (platform2) {
79883
+ case "darwin":
79884
+ return readMacClipboard(spawnImpl, mkTempPath);
79885
+ case "win32":
79886
+ return readWindowsClipboard(spawnImpl, mkTempPath);
79887
+ case "linux":
79888
+ case "freebsd":
79889
+ case "openbsd":
79890
+ case "netbsd":
79891
+ return readLinuxClipboard(spawnImpl, mkTempPath, env);
79892
+ default:
79893
+ return {
79894
+ ok: false,
79895
+ kind: "unsupported-platform",
79896
+ message: `Clipboard image paste is not supported on ${platform2}.`
79897
+ };
79898
+ }
79899
+ }
79900
+ function escapeAppleScriptString(value) {
79901
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
79902
+ }
79903
+ async function readMacClipboard(spawnImpl, mkTempPath) {
79904
+ const targetPath = mkTempPath(".png");
79905
+ const outcome = await spawnImpl("osascript", [
79906
+ "-e",
79907
+ "set pngData to (the clipboard as \xABclass PNGf\xBB)",
79908
+ "-e",
79909
+ `set fp to open for access POSIX file "${escapeAppleScriptString(targetPath)}" with write permission`,
79910
+ "-e",
79911
+ "write pngData to fp",
79912
+ "-e",
79913
+ "close access fp"
79914
+ ]);
79915
+ if (isSpawnFailure(outcome)) {
79916
+ return spawnFailure(outcome, "osascript");
79917
+ }
79918
+ if (outcome.result.code !== 0) {
79919
+ const stderr = outcome.result.stderr.trim();
79920
+ if (/PNGf|Can't|can’t/i.test(stderr)) {
79921
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79922
+ }
79923
+ return {
79924
+ ok: false,
79925
+ kind: "failed",
79926
+ message: `osascript failed: ${truncate(stderr) || `exit ${outcome.result.code}`}`
79927
+ };
79928
+ }
79929
+ return finalizeTempImage(targetPath, "image/png");
79930
+ }
79931
+ function escapePowerShellSingleQuoted(value) {
79932
+ return value.replace(/'/g, "''");
79933
+ }
79934
+ async function readWindowsClipboard(spawnImpl, mkTempPath) {
79935
+ const targetPath = mkTempPath(".png");
79936
+ const script = [
79937
+ "Add-Type -AssemblyName System.Windows.Forms",
79938
+ "Add-Type -AssemblyName System.Drawing",
79939
+ "$img = [System.Windows.Forms.Clipboard]::GetImage()",
79940
+ "if ($null -eq $img) { exit 2 }",
79941
+ `$img.Save('${escapePowerShellSingleQuoted(targetPath)}', [System.Drawing.Imaging.ImageFormat]::Png)`,
79942
+ "exit 0"
79943
+ ].join("\n");
79944
+ const outcome = await spawnImpl("powershell", [
79945
+ "-NoProfile",
79946
+ "-NonInteractive",
79947
+ "-STA",
79948
+ "-Command",
79949
+ script
79950
+ ]);
79951
+ if (isSpawnFailure(outcome)) {
79952
+ return spawnFailure(outcome, "powershell");
79953
+ }
79954
+ if (outcome.result.code === 2) {
79955
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
79956
+ }
79957
+ if (outcome.result.code !== 0) {
79958
+ return {
79959
+ ok: false,
79960
+ kind: "failed",
79961
+ message: `powershell failed: ${truncate(outcome.result.stderr.trim()) || `exit ${outcome.result.code}`}`
79962
+ };
79963
+ }
79964
+ return finalizeTempImage(targetPath, "image/png");
79965
+ }
79966
+ var PREFERRED_IMAGE_MIMES = ["image/png", "image/jpeg"];
79967
+ function extensionForMime(mimeType) {
79968
+ switch (mimeType) {
79969
+ case "image/png":
79970
+ return ".png";
79971
+ case "image/jpeg":
79972
+ return ".jpg";
79973
+ case "image/gif":
79974
+ return ".gif";
79975
+ case "image/webp":
79976
+ return ".webp";
79977
+ case "image/bmp":
79978
+ return ".bmp";
79979
+ default:
79980
+ return ".png";
79981
+ }
79982
+ }
79983
+ function pickImageMime(advertised) {
79984
+ for (const mime of PREFERRED_IMAGE_MIMES) {
79985
+ if (advertised.includes(mime)) return mime;
79986
+ }
79987
+ return advertised.find((mime) => mime.startsWith("image/"));
79988
+ }
79989
+ async function readLinuxClipboard(spawnImpl, mkTempPath, env) {
79990
+ if (env.WAYLAND_DISPLAY) {
79991
+ return readWaylandClipboard(spawnImpl, mkTempPath);
79992
+ }
79993
+ if (env.DISPLAY) {
79994
+ return readX11Clipboard(spawnImpl, mkTempPath);
79995
+ }
79996
+ return {
79997
+ ok: false,
79998
+ kind: "failed",
79999
+ message: "No graphical session detected (neither WAYLAND_DISPLAY nor DISPLAY is set)."
80000
+ };
80001
+ }
80002
+ async function readWaylandClipboard(spawnImpl, mkTempPath) {
80003
+ const listOutcome = await spawnImpl("wl-paste", ["--list-types"]);
80004
+ if (isSpawnFailure(listOutcome)) {
80005
+ return spawnFailure(listOutcome, "wl-paste");
80006
+ }
80007
+ if (listOutcome.result.code !== 0) {
80008
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80009
+ }
80010
+ const advertised = listOutcome.result.stdout.toString("utf8").split("\n").map((line) => line.trim()).filter(Boolean);
80011
+ const mime = pickImageMime(advertised);
80012
+ if (!mime) {
80013
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80014
+ }
80015
+ const dataOutcome = await spawnImpl("wl-paste", ["--type", mime]);
80016
+ if (isSpawnFailure(dataOutcome)) {
80017
+ return spawnFailure(dataOutcome, "wl-paste");
80018
+ }
80019
+ if (dataOutcome.result.code !== 0) {
80020
+ return {
80021
+ ok: false,
80022
+ kind: "failed",
80023
+ message: `wl-paste failed: ${truncate(dataOutcome.result.stderr.trim()) || `exit ${dataOutcome.result.code}`}`
80024
+ };
80025
+ }
80026
+ return writeImageBytes(dataOutcome.result.stdout, mime, mkTempPath);
80027
+ }
80028
+ async function readX11Clipboard(spawnImpl, mkTempPath) {
80029
+ const listOutcome = await spawnImpl("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"]);
80030
+ if (isSpawnFailure(listOutcome)) {
80031
+ return spawnFailure(listOutcome, "xclip");
80032
+ }
80033
+ if (listOutcome.result.code !== 0) {
80034
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80035
+ }
80036
+ const advertised = listOutcome.result.stdout.toString("utf8").split("\n").map((line) => line.trim()).filter(Boolean);
80037
+ const mime = pickImageMime(advertised);
80038
+ if (!mime) {
80039
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80040
+ }
80041
+ const dataOutcome = await spawnImpl("xclip", ["-selection", "clipboard", "-t", mime, "-o"]);
80042
+ if (isSpawnFailure(dataOutcome)) {
80043
+ return spawnFailure(dataOutcome, "xclip");
80044
+ }
80045
+ if (dataOutcome.result.code !== 0) {
80046
+ return {
80047
+ ok: false,
80048
+ kind: "failed",
80049
+ message: `xclip failed: ${truncate(dataOutcome.result.stderr.trim()) || `exit ${dataOutcome.result.code}`}`
80050
+ };
80051
+ }
80052
+ return writeImageBytes(dataOutcome.result.stdout, mime, mkTempPath);
80053
+ }
80054
+ function writeImageBytes(bytes, declaredMime, mkTempPath) {
80055
+ if (bytes.length === 0) {
80056
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80057
+ }
80058
+ if (bytes.length > MAX_IMAGE_BYTES) {
80059
+ return { ok: false, kind: "too-large", message: `Clipboard image is too large (${formatBytes2(bytes.length)}).` };
80060
+ }
80061
+ const mimeType = detectMimeFromMagic(bytes) ?? declaredMime;
80062
+ const targetPath = mkTempPath(extensionForMime(mimeType));
80063
+ try {
80064
+ writeFileSync12(targetPath, bytes, { flag: "r+", mode: 384 });
80065
+ } catch (err) {
80066
+ return { ok: false, kind: "failed", message: `Failed to write temp image: ${errorMessage4(err)}` };
80067
+ }
80068
+ return {
80069
+ ok: true,
80070
+ path: targetPath,
80071
+ mimeType
80072
+ };
80073
+ }
80074
+ function finalizeTempImage(path34, mimeType) {
80075
+ let bytes;
80076
+ try {
80077
+ if (!existsSync18(path34)) {
80078
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80079
+ }
80080
+ const size = statSync12(path34).size;
80081
+ if (size === 0) {
80082
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain an image." };
80083
+ }
80084
+ if (size > MAX_IMAGE_BYTES) {
80085
+ return { ok: false, kind: "too-large", message: `Clipboard image is too large (${formatBytes2(size)}).` };
80086
+ }
80087
+ bytes = readFileSync17(path34);
80088
+ } catch (err) {
80089
+ return { ok: false, kind: "failed", message: `Failed to read clipboard image: ${errorMessage4(err)}` };
80090
+ }
80091
+ const detected = detectMimeFromMagic(bytes);
80092
+ if (!detected) {
80093
+ return { ok: false, kind: "no-image", message: "Clipboard does not contain a supported image (PNG/JPEG)." };
80094
+ }
80095
+ return { ok: true, path: path34, mimeType: detected };
80096
+ }
80097
+ function detectMimeFromMagic(bytes) {
80098
+ if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
80099
+ return "image/png";
80100
+ }
80101
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
80102
+ return "image/jpeg";
80103
+ }
80104
+ return void 0;
80105
+ }
80106
+ function spawnFailure(outcome, tool) {
80107
+ if (outcome.error === "too-large") {
80108
+ return { ok: false, kind: "too-large", message: "Clipboard image is too large." };
80109
+ }
80110
+ if (outcome.error === "not-found") {
80111
+ return {
80112
+ ok: false,
80113
+ kind: "missing-tool",
80114
+ message: `${tool} is not installed; it is required to read clipboard images on this platform.`
80115
+ };
80116
+ }
80117
+ if (outcome.error === "timeout") {
80118
+ return { ok: false, kind: "timeout", message: `Reading the clipboard with ${tool} timed out.` };
80119
+ }
80120
+ return { ok: false, kind: "failed", message: `Failed to run ${tool}: ${outcome.message ?? "unknown error"}` };
80121
+ }
80122
+ function truncate(text, max = 200) {
80123
+ const trimmed = text.trim();
80124
+ return trimmed.length > max ? `${trimmed.slice(0, max)}\u2026` : trimmed;
80125
+ }
80126
+ function errorMessage4(err) {
80127
+ return err instanceof Error ? err.message : String(err);
80128
+ }
80129
+ function formatBytes2(bytes) {
80130
+ if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
80131
+ return `${Math.max(1, Math.round(bytes / 1024))}KB`;
80132
+ }
80133
+
80134
+ // src/input/image-paste.ts
80135
+ import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync13, rmSync as rmSync3 } from "node:fs";
80136
+ import { tmpdir as tmpdir2 } from "node:os";
80137
+ import { join as join21 } from "node:path";
80138
+ var pastingHosts = /* @__PURE__ */ new WeakSet();
80139
+ function formatPastedImageName(date, extension = ".png") {
80140
+ const pad = (value) => String(value).padStart(2, "0");
80141
+ const hh = pad(date.getHours());
80142
+ const mm = pad(date.getMinutes());
80143
+ const ss = pad(date.getSeconds());
80144
+ return `pasted-${hh}${mm}${ss}${extension}`;
80145
+ }
80146
+ function terminalResultToClipboardResult(result) {
80147
+ if (result.ok !== true) {
80148
+ const kindMap = {
80149
+ unsupported: "failed",
80150
+ timeout: "timeout",
80151
+ denied: "failed",
80152
+ busy: "failed",
80153
+ unavailable: "no-image",
80154
+ "too-large": "too-large",
80155
+ malformed: "failed"
80156
+ };
80157
+ return { ok: false, kind: kindMap[result.kind], message: result.message };
80158
+ }
80159
+ const ext = result.mimeType === "image/jpeg" ? ".jpg" : ".png";
80160
+ let dir;
80161
+ try {
80162
+ dir = mkdtempSync2(join21(tmpdir2(), "pie-osc5522-"));
80163
+ const path34 = join21(dir, formatPastedImageName(/* @__PURE__ */ new Date(), ext));
80164
+ writeFileSync13(path34, result.bytes);
80165
+ return {
80166
+ ok: true,
80167
+ path: path34,
80168
+ mimeType: result.mimeType,
80169
+ cleanup: () => {
80170
+ try {
80171
+ rmSync3(dir, { recursive: true, force: true });
80172
+ } catch {
80173
+ }
80174
+ }
80175
+ };
80176
+ } catch (err) {
80177
+ if (dir) {
80178
+ try {
80179
+ rmSync3(dir, { recursive: true, force: true });
80180
+ } catch {
80181
+ }
80182
+ }
80183
+ return { ok: false, kind: "failed", message: `Failed to materialize clipboard image: ${err instanceof Error ? err.message : String(err)}` };
80184
+ }
80185
+ }
80186
+ async function handleImagePaste(host, options = {}) {
80187
+ if (pastingHosts.has(host)) {
80188
+ host.showStatus("Still reading the clipboard\u2026");
80189
+ return;
80190
+ }
80191
+ if (!host.mentionHandler) {
80192
+ host.showStatus("File attachments are not available yet (still initializing).");
80193
+ return;
80194
+ }
80195
+ pastingHosts.add(host);
80196
+ const handler = host.mentionHandler;
80197
+ const sessionId = host.sessionManager?.getActiveSessionId();
80198
+ const controller = host.agentSessionController;
80199
+ let invalidated = false;
80200
+ const unsubscribe = controller?.subscribe((event) => {
80201
+ if (event.type === "session_invalidated") invalidated = true;
80202
+ });
80203
+ const isCurrent = () => host.mentionHandler === handler && host.sessionManager?.getActiveSessionId() === sessionId && host.agentSessionController === controller && !invalidated;
80204
+ const displayName = formatPastedImageName(options.now?.() ?? /* @__PURE__ */ new Date());
80205
+ let pending;
80206
+ try {
80207
+ pending = handler.beginPendingAttachment(displayName);
80208
+ let result;
80209
+ const terminal = host.ui?.terminal;
80210
+ const readTerminal = options.readTerminalClipboardImage ?? (terminal?.clipboard ? () => terminal.clipboard.readImage() : void 0);
80211
+ if (readTerminal) {
80212
+ host.showStatus("Reading clipboard via terminal\u2026");
80213
+ const terminalResult = await readTerminal();
80214
+ if (terminalResult.ok === true) {
80215
+ result = terminalResultToClipboardResult(terminalResult);
80216
+ } else if (terminalResult.kind === "unsupported") {
80217
+ result = await (options.readClipboardImage ?? readClipboardImage)();
80218
+ } else {
80219
+ result = terminalResultToClipboardResult(terminalResult);
80220
+ }
80221
+ } else {
80222
+ result = await (options.readClipboardImage ?? readClipboardImage)();
80223
+ }
80224
+ if (result.ok === false) {
80225
+ switch (result.kind) {
80226
+ case "no-image":
80227
+ host.showStatus("Clipboard does not contain an image.");
80228
+ break;
80229
+ case "missing-tool":
80230
+ host.showWarning(result.message);
80231
+ break;
80232
+ case "too-large":
80233
+ host.showError(result.message);
80234
+ break;
80235
+ default:
80236
+ host.showError(result.message);
80237
+ break;
80238
+ }
80239
+ return;
80240
+ }
80241
+ try {
80242
+ if (!isCurrent()) return;
80243
+ await handler.attachLocalFile(result.path, displayName, pending.token, isCurrent);
80244
+ } finally {
80245
+ result.cleanup();
80246
+ }
80247
+ } catch (err) {
80248
+ host.showError(`Paste failed: ${err instanceof Error ? err.message : String(err)}`);
80249
+ } finally {
80250
+ try {
80251
+ pending?.finish();
80252
+ if (isCurrent()) controller?.requestQueueDispatch?.();
80253
+ } finally {
80254
+ unsubscribe?.();
80255
+ pastingHosts.delete(host);
80256
+ }
80257
+ }
80258
+ }
80259
+
79351
80260
  // src/runtime/interactive-queue-ui.ts
79352
80261
  function updatePendingMessagesDisplay(host) {
79353
80262
  host.pendingMessagesContainer.clear();
@@ -79359,11 +80268,11 @@ function updatePendingMessagesDisplay(host) {
79359
80268
  const steerMessages = queuedInputs.filter((m2) => m2.mode === "steer");
79360
80269
  const followUpMessages = queuedInputs.filter((m2) => m2.mode === "followUp");
79361
80270
  for (const msg of steerMessages) {
79362
- const text = theme.fg("dim", ` [steer] ${truncate(msg.displayText, 50)}`);
80271
+ const text = theme.fg("dim", ` [steer] ${truncate2(msg.displayText, 50)}`);
79363
80272
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79364
80273
  }
79365
80274
  for (const msg of followUpMessages) {
79366
- const text = theme.fg("dim", ` [followUp] ${truncate(msg.displayText, 50)}`);
80275
+ const text = theme.fg("dim", ` [followUp] ${truncate2(msg.displayText, 50)}`);
79367
80276
  host.pendingMessagesContainer.addChild(new Text(text, 1, 0));
79368
80277
  }
79369
80278
  const dequeueHint = host.keybindings.getKeys("dequeue")[0] || "alt+up";
@@ -79472,7 +80381,7 @@ function handleSteer(host) {
79472
80381
  void host.editor.onSubmit(text);
79473
80382
  }
79474
80383
  }
79475
- function truncate(text, maxLength) {
80384
+ function truncate2(text, maxLength) {
79476
80385
  if (text.length <= maxLength) return text;
79477
80386
  return text.slice(0, maxLength) + "...";
79478
80387
  }
@@ -79572,6 +80481,12 @@ var FooterComponent = class {
79572
80481
  * derived from this at render time so long silent tool runs (e.g.
79573
80482
  * subagents) still visibly tick — a moving elapsed says "alive". */
79574
80483
  busySince = null;
80484
+ /** When the footer last started waiting for the user. Same derived-at-render
80485
+ * approach: the wait elapsed ticks only while a wait timer runs. */
80486
+ waitingSince = null;
80487
+ /** Animation timer for the waiting state (drives the wait elapsed ticker
80488
+ * in place of the stopped busy spinner). */
80489
+ waitingTimer = null;
79575
80490
  /** Keyed ambient status slots for extensions (ctx.ui.setStatus). */
79576
80491
  extensionStatuses = new ExtensionStatusSlots();
79577
80492
  /** Set callback to request re-render when animation updates (pass true to force) */
@@ -79589,6 +80504,17 @@ var FooterComponent = class {
79589
80504
  this.busySince = null;
79590
80505
  this.stopSpinner();
79591
80506
  }
80507
+ if (state.waitingForUser === true) {
80508
+ if (this.waitingSince === null) this.waitingSince = performance.now();
80509
+ this.startWaitingTimer();
80510
+ this.stopSpinner();
80511
+ } else if (state.waitingForUser === false) {
80512
+ this.waitingSince = null;
80513
+ this.stopWaitingTimer();
80514
+ if (this.state.status === "thinking" || this.state.status === "processing") {
80515
+ this.startSpinner();
80516
+ }
80517
+ }
79592
80518
  }
79593
80519
  setAutoCompactEnabled(enabled) {
79594
80520
  this.autoCompactEnabled = enabled;
@@ -79597,6 +80523,7 @@ var FooterComponent = class {
79597
80523
  }
79598
80524
  dispose() {
79599
80525
  this.stopSpinner();
80526
+ this.stopWaitingTimer();
79600
80527
  }
79601
80528
  /** Start spinner animation */
79602
80529
  startSpinner() {
@@ -79614,6 +80541,20 @@ var FooterComponent = class {
79614
80541
  this.spinnerTimer = null;
79615
80542
  }
79616
80543
  }
80544
+ /** Ticker for the waiting state: no spinner animation (nothing is
80545
+ * progressing), just a render tick so the wait elapsed updates. */
80546
+ startWaitingTimer() {
80547
+ if (this.waitingTimer) return;
80548
+ this.waitingTimer = setInterval(() => {
80549
+ this.onRequestRender?.();
80550
+ }, SPINNER_INTERVAL);
80551
+ }
80552
+ stopWaitingTimer() {
80553
+ if (this.waitingTimer) {
80554
+ clearInterval(this.waitingTimer);
80555
+ this.waitingTimer = null;
80556
+ }
80557
+ }
79617
80558
  /** Get current spinner character */
79618
80559
  getSpinner() {
79619
80560
  if (this.state.status !== "thinking" && this.state.status !== "processing") {
@@ -79688,23 +80629,27 @@ var FooterComponent = class {
79688
80629
  processing: "warning",
79689
80630
  error: "error"
79690
80631
  };
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));
80632
+ const isWaitingForUser2 = state.waitingForUser === true;
80633
+ const waitingElapsed = this.waitingSince !== null && isWaitingForUser2 ? ` \xB7 ${formatElapsed(performance.now() - this.waitingSince)}` : "";
80634
+ const statusWithSpinner = isWaitingForUser2 ? theme.fg("accent", `\u23F8 Waiting for you${waitingElapsed}`) : (() => {
80635
+ const spinner = this.getSpinner();
80636
+ let statusText;
80637
+ if (state.lifecycleEvent && state.status !== "ready") {
80638
+ statusText = state.lifecycleEvent;
80639
+ } else {
80640
+ const statusDisplayMap = {
80641
+ ready: "Ready",
80642
+ thinking: "Thinking",
80643
+ processing: "Processing",
80644
+ error: "Error"
80645
+ };
80646
+ statusText = statusDisplayMap[state.status] || state.status;
80647
+ }
80648
+ const isBusy = state.status === "thinking" || state.status === "processing";
80649
+ const busyElapsed = this.busySince !== null && isBusy ? ` \xB7 ${formatElapsed(performance.now() - this.busySince)}` : "";
80650
+ return theme.fg(statusColor[state.status], `${spinner ? `${spinner} ` : ""}${statusText}${busyElapsed}`);
80651
+ })();
80652
+ statsParts.push(statusWithSpinner);
79708
80653
  let statsLeft = statsParts.join(" ");
79709
80654
  statsLeft += this.extensionStatuses.format();
79710
80655
  const modelName = state.modelName || "no-model";
@@ -79835,8 +80780,8 @@ function calculateCliContextUsage(params) {
79835
80780
  };
79836
80781
  }
79837
80782
  function createCliAutoCompactDecision(params) {
79838
- const errorMessage4 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
79839
- if (errorMessage4 && params.isContextOverflowError(errorMessage4)) {
80783
+ const errorMessage5 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
80784
+ if (errorMessage5 && params.isContextOverflowError(errorMessage5)) {
79840
80785
  return { reason: "overflow", willRetry: true };
79841
80786
  }
79842
80787
  if (!params.autoCompactEnabled || params.contextWindow <= 0) {
@@ -79890,7 +80835,7 @@ async function createCliAutoCompactionResult(params) {
79890
80835
  systemPrompt: params.systemPrompt,
79891
80836
  signal: params.context.signal
79892
80837
  };
79893
- const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages, params.onProjection), compactOptions);
80838
+ const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages, params.onProjection, params.envelopeRegistry), compactOptions);
79894
80839
  if (params.context.signal?.aborted) {
79895
80840
  return { aborted: true };
79896
80841
  }
@@ -79898,7 +80843,7 @@ async function createCliAutoCompactionResult(params) {
79898
80843
  summary,
79899
80844
  retainedTail,
79900
80845
  ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79901
- tokensBefore: calculateTotalTokens(params.context.messages)
80846
+ tokensBefore: params.context.preparation?.inputTokens ?? calculateTotalTokens(projectCliHostEnvelopes(params.context.messages, void 0, params.envelopeRegistry))
79902
80847
  };
79903
80848
  }
79904
80849
  function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
@@ -79923,153 +80868,6 @@ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
79923
80868
  });
79924
80869
  }
79925
80870
 
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
80871
  // src/runtime/interactive-context-ui.ts
80074
80872
  function calculateContextUsage(host) {
80075
80873
  const messages = host.agent?.state.messages ?? [];
@@ -80246,23 +81044,23 @@ function setupAutocomplete(host) {
80246
81044
  };
80247
81045
  host.editor.setAutocompleteProvider(provider);
80248
81046
  }
80249
- function isContextOverflowError(errorMessage4) {
80250
- return isCliContextOverflowError(errorMessage4);
81047
+ function isContextOverflowError(errorMessage5) {
81048
+ return isCliContextOverflowError(errorMessage5);
80251
81049
  }
80252
81050
  function shouldAutoCompact(host, contextTokens, contextWindow) {
80253
81051
  if (!host.options.settingsManager.getAutoCompactEnabled()) return false;
80254
81052
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
80255
81053
  return shouldCliAutoCompactTokens({ tokens: contextTokens, contextWindow, reserveTokens });
80256
81054
  }
80257
- function isRetryableError(errorMessage4) {
80258
- return isCliRetryableError(errorMessage4);
81055
+ function isRetryableError(errorMessage5) {
81056
+ return isCliRetryableError(errorMessage5);
80259
81057
  }
80260
- function isInvalidFileReferenceError(errorMessage4) {
81058
+ function isInvalidFileReferenceError(errorMessage5) {
80261
81059
  return /not the owner of this file|owner of this file|invalid_authentication_error/i.test(
80262
- errorMessage4
81060
+ errorMessage5
80263
81061
  );
80264
81062
  }
80265
- function recoverFromInvalidFileReferenceError(host, errorMessage4) {
81063
+ function recoverFromInvalidFileReferenceError(host, errorMessage5) {
80266
81064
  if (!host.fileManager || !host.agent) return;
80267
81065
  const messages = host.agent.state.messages.slice();
80268
81066
  const invalidFileIds = /* @__PURE__ */ new Set();
@@ -80280,7 +81078,7 @@ function recoverFromInvalidFileReferenceError(host, errorMessage4) {
80280
81078
  }
80281
81079
  if (invalidFileIds.size === 0) return;
80282
81080
  for (const fileId of invalidFileIds) {
80283
- host.fileManager.markFileReferenceError(fileId, errorMessage4);
81081
+ host.fileManager.markFileReferenceError(fileId, errorMessage5);
80284
81082
  }
80285
81083
  let changed = false;
80286
81084
  const sanitizedMessages = messages.map((message) => {
@@ -80574,9 +81372,9 @@ function clearStatusLine(host) {
80574
81372
  }
80575
81373
  if (removed) host.ui.requestRender();
80576
81374
  }
80577
- function showError(host, errorMessage4) {
81375
+ function showError(host, errorMessage5) {
80578
81376
  host.chatContainer.addChild(new Spacer(1));
80579
- host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage4}`), 1, 0));
81377
+ host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage5}`), 1, 0));
80580
81378
  host.ui.requestRender();
80581
81379
  }
80582
81380
  async function executeCommand(host, command) {
@@ -80772,22 +81570,42 @@ function scheduleContinuationTimeout(host) {
80772
81570
 
80773
81571
  // src/runtime/interactive-controller-setup.ts
80774
81572
  async function prepareAgentTurn(host, context) {
81573
+ let messages = context.messages;
81574
+ if (context.source === "interactive" && context.streamingBehavior && host.tryBuildAttachmentContentFromExtensions) {
81575
+ messages = await Promise.all(messages.map(async (message) => {
81576
+ if (message.role !== "user") return message;
81577
+ const blocks = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
81578
+ if (blocks.some((block) => block.type !== "text" && block.type !== "hostContext")) return message;
81579
+ const content = [];
81580
+ for (const block of blocks) {
81581
+ if (block.type !== "text" || !/@\[[^\]]+\]\(file:/.test(block.text)) {
81582
+ content.push(block);
81583
+ continue;
81584
+ }
81585
+ const resolved = await host.tryBuildAttachmentContentFromExtensions(block.text);
81586
+ if (resolved.handled && resolved.content.length) content.push(...resolved.content);
81587
+ else content.push(block);
81588
+ if (resolved.warnings.length) host.showStatus(resolved.warnings.join(" | "));
81589
+ }
81590
+ return { ...message, content };
81591
+ }));
81592
+ }
80775
81593
  if (!host.extensionRunner) {
80776
- return void 0;
81594
+ return messages === context.messages ? void 0 : { messages };
80777
81595
  }
80778
81596
  const baseSystemPrompt = context.baseSystemPrompt ?? host.buildSystemPrompt();
80779
81597
  const result = await host.extensionRunner.emitBeforeAgentStart({
80780
81598
  type: "before_agent_start",
80781
- messages: context.messages,
81599
+ messages,
80782
81600
  baseSystemPrompt,
80783
81601
  source: context.source
80784
81602
  });
80785
81603
  if (!result) {
80786
- return { systemPrompt: baseSystemPrompt };
81604
+ return { systemPrompt: baseSystemPrompt, messages };
80787
81605
  }
80788
81606
  return {
80789
81607
  systemPrompt: result.systemPrompt ?? baseSystemPrompt,
80790
- messages: result.messages?.length ? [...context.messages, ...result.messages] : context.messages
81608
+ messages: result.messages?.length ? [...messages, ...result.messages] : messages
80791
81609
  };
80792
81610
  }
80793
81611
  function createInteractiveAgentSessionController(host) {
@@ -80807,6 +81625,7 @@ function createInteractiveAgentSessionController(host) {
80807
81625
  let retryElapsedStartedAt = 0;
80808
81626
  let executionWork;
80809
81627
  let autoCompactBeforeStats = null;
81628
+ let lastShownCompactionError = null;
80810
81629
  const attention = cliAttention(host);
80811
81630
  const stopRetryElapsedTicker = () => {
80812
81631
  if (retryElapsedTicker !== void 0) {
@@ -80860,9 +81679,10 @@ function createInteractiveAgentSessionController(host) {
80860
81679
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80861
81680
  classifyFailure: (context) => classifyCliFailure(context),
80862
81681
  retry: createCliRetryPolicy(
80863
- (errorMessage4) => !host._isContextOverflowError(errorMessage4) && isCliRetryableError(errorMessage4)
81682
+ (errorMessage5) => !host._isContextOverflowError(errorMessage5) && isCliRetryableError(errorMessage5)
80864
81683
  ),
80865
81684
  compaction: {
81685
+ modelView: cliCompactionModelView,
80866
81686
  compact: async (context) => {
80867
81687
  const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
80868
81688
  type: "session:before_compact",
@@ -81013,7 +81833,10 @@ function createInteractiveAgentSessionController(host) {
81013
81833
  if (state.mode === "todo" && state.lifecycle === "paused" && state.steps.length > 0) {
81014
81834
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
81015
81835
  }
81016
- host.showError(event.failure.message);
81836
+ if (event.failure.message !== lastShownCompactionError) {
81837
+ host.showError(event.failure.message);
81838
+ }
81839
+ lastShownCompactionError = null;
81017
81840
  host.setControllerBusy(false);
81018
81841
  } else if (event.type === "turn_cancelled") {
81019
81842
  const outcome = host.executionState.abort();
@@ -81057,14 +81880,17 @@ function createInteractiveAgentSessionController(host) {
81057
81880
  willRetry: event.willRetry
81058
81881
  });
81059
81882
  } else if (event.type === "auto_compaction_end") {
81883
+ const stillRunning = host.agent.state.isStreaming;
81060
81884
  host.isCompacting = false;
81061
- host.isProcessing = false;
81885
+ host.isProcessing = stillRunning;
81886
+ if (stillRunning) host.setControllerBusy(true);
81062
81887
  host.setSubmitLock(false);
81063
81888
  if (event.errorMessage) {
81064
81889
  const prefix = event.reason === "overflow" ? "Context overflow recovery failed" : "Auto-compaction failed";
81065
81890
  host.showError(`${prefix}: ${event.errorMessage}`);
81066
- host.footer.setState({ status: "ready" });
81067
- host.setControllerBusy(false);
81891
+ lastShownCompactionError = event.willRetry || stillRunning ? event.errorMessage : null;
81892
+ host.footer.setState({ status: stillRunning ? "processing" : "ready" });
81893
+ host.setControllerBusy(stillRunning);
81068
81894
  autoCompactBeforeStats = null;
81069
81895
  return;
81070
81896
  }
@@ -81080,7 +81906,7 @@ function createInteractiveAgentSessionController(host) {
81080
81906
  const contextWindow = host.options.model?.contextWindow ?? 0;
81081
81907
  const contextPercent = contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0;
81082
81908
  host.footer.setState({
81083
- status: "ready",
81909
+ status: stillRunning ? "processing" : "ready",
81084
81910
  lifecycleEvent: "auto_compact",
81085
81911
  inputTokens: contextUsage.input,
81086
81912
  outputTokens: contextUsage.output,
@@ -81323,8 +82149,8 @@ async function handleDirectBash(host, command) {
81323
82149
  outputContainer.addChild(new Spacer(1));
81324
82150
  host.chatContainer.addChild(outputContainer);
81325
82151
  host.ui.requestRender();
81326
- const { spawn: spawn2 } = await import("child_process");
81327
- const child = spawn2(shellCommand.file, shellCommand.args, {
82152
+ const { spawn: spawn3 } = await import("child_process");
82153
+ const child = spawn3(shellCommand.file, shellCommand.args, {
81328
82154
  cwd: process.cwd(),
81329
82155
  env: process.env
81330
82156
  });
@@ -81363,15 +82189,14 @@ async function handleDirectBash(host, command) {
81363
82189
  // src/extensions/interactive-extension-ui.ts
81364
82190
  function createInteractiveExtensionUIContext(host) {
81365
82191
  const interact = async (run) => {
81366
- const attention = cliAttention(host);
81367
- const request = attention.request("input-required");
82192
+ const request = beginExtensionWaiting(host, "input-required");
81368
82193
  let resolution = "cancelled";
81369
82194
  try {
81370
82195
  const value = await run();
81371
82196
  resolution = interactionAttentionResolution({ value });
81372
82197
  return value;
81373
82198
  } finally {
81374
- attention.resolve(request, resolution);
82199
+ endExtensionWaiting(host, request, resolution);
81375
82200
  }
81376
82201
  };
81377
82202
  return {
@@ -81393,23 +82218,13 @@ function createInteractiveExtensionUIContext(host) {
81393
82218
  }
81394
82219
  function installInteractiveInteractionHandler(host) {
81395
82220
  registerInteractionHandler(async (request) => {
81396
- const attention = cliAttention(host);
81397
82221
  let attentionRequest;
81398
82222
  const beginWaitingForUser = () => {
81399
- host.activeInteractionCount++;
81400
- host.setWaitingForUser(true);
81401
- host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
81402
82223
  const reason = interactionAttentionReason(request);
81403
- if (reason) attentionRequest = attention.request(reason, "id" in request ? request.id : void 0);
81404
- host.safeRequestRender();
82224
+ attentionRequest = beginExtensionWaiting(host, reason, "id" in request ? request.id : void 0);
81405
82225
  };
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();
82226
+ const endWaitingForUser = (resolution) => {
82227
+ endExtensionWaiting(host, attentionRequest, resolution);
81413
82228
  };
81414
82229
  const withTimeout2 = async (createPromise) => {
81415
82230
  const controller = new AbortController();
@@ -81438,8 +82253,7 @@ function installInteractiveInteractionHandler(host) {
81438
82253
  resolution = interactionAttentionResolution(result);
81439
82254
  return result;
81440
82255
  } finally {
81441
- attention.resolve(attentionRequest, resolution);
81442
- endWaitingForUser();
82256
+ endWaitingForUser(resolution);
81443
82257
  }
81444
82258
  };
81445
82259
  switch (request.type) {
@@ -81447,7 +82261,8 @@ function installInteractiveInteractionHandler(host) {
81447
82261
  showExtensionNotification(
81448
82262
  host,
81449
82263
  request.message,
81450
- request.level === "success" ? "info" : request.level
82264
+ request.level === "success" ? "info" : request.level,
82265
+ { markdown: request.markdown === true }
81451
82266
  );
81452
82267
  return { type: "notify", acknowledged: true };
81453
82268
  case "select_one": {
@@ -81536,6 +82351,30 @@ function installInteractiveInteractionHandler(host) {
81536
82351
  }
81537
82352
  });
81538
82353
  }
82354
+ function beginExtensionWaiting(host, reason, id) {
82355
+ host.activeInteractionCount++;
82356
+ try {
82357
+ host.setWaitingForUser(true);
82358
+ host.footer.setState({ lifecycleEvent: "waiting_for_user_input", waitingForUser: true });
82359
+ const attention = reason ? cliAttention(host).request(reason, id) : void 0;
82360
+ host.safeRequestRender();
82361
+ return attention;
82362
+ } catch (error) {
82363
+ host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
82364
+ throw error;
82365
+ }
82366
+ }
82367
+ function endExtensionWaiting(host, attentionRequest, resolution) {
82368
+ host.activeInteractionCount = Math.max(0, host.activeInteractionCount - 1);
82369
+ if (host.activeInteractionCount === 0) {
82370
+ host.setWaitingForUser(false);
82371
+ host.footer.setState({ lifecycleEvent: "\u25B6 resuming", waitingForUser: false });
82372
+ } else {
82373
+ host.footer.setState({ lifecycleEvent: "waiting_for_user_input", waitingForUser: true });
82374
+ }
82375
+ if (attentionRequest) cliAttention(host).resolve(attentionRequest, resolution);
82376
+ host.safeRequestRender();
82377
+ }
81539
82378
  function showExtensionNotification(host, message, type = "info", opts) {
81540
82379
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
81541
82380
  host.chatContainer.addChild(new Spacer(1));
@@ -82300,6 +83139,12 @@ var InteractiveMode = class {
82300
83139
  get controllerSetupHost() {
82301
83140
  return this;
82302
83141
  }
83142
+ getYoloGrantRemainingMs() {
83143
+ return this.yoloGrant.remainingMs();
83144
+ }
83145
+ revokeYoloGrant() {
83146
+ return this.yoloGrant.revoke();
83147
+ }
82303
83148
  getPolicyContext = () => ({
82304
83149
  mode: this.planModeEnabled ? "plan" : "normal",
82305
83150
  yoloMode: this.yoloMode || this.yoloGrant.isActive(),
@@ -82313,24 +83158,21 @@ var InteractiveMode = class {
82313
83158
  isYoloMode: () => this.yoloMode || this.yoloGrant.isActive(),
82314
83159
  getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid))
82315
83160
  });
82316
- const fileSystemToolOptions = (allowlistedDirs) => ({
82317
- ...buildCliFileAccessOptions({
82318
- cwd: this.options.cwd,
82319
- configDir: this.options.configDir,
82320
- skills: this.skills,
82321
- allowlistedDirs
82322
- }),
83161
+ const fileSystemToolOptions = (allowlistedDirs) => buildInteractiveFilesystemToolOptions({
83162
+ cwd: this.options.cwd,
82323
83163
  configDir: this.options.configDir,
82324
83164
  fileObservationState: this.options.fileObservationState,
83165
+ skills: this.skills,
83166
+ getActiveSessionId: () => this.sessionManager.getActiveSessionId(),
82325
83167
  getAgentModel: () => this.options.model,
82326
- getSessionId: () => this.sessionManager.getActiveSessionId() || void 0,
82327
83168
  resolveToolModel: (purpose) => this.options.modelRegistry.resolveToolModel(purpose, this.options.model),
82328
- getPolicyContext: () => ({
83169
+ resolveToolModelCandidates: this.options.modelRegistry.resolveToolModelCandidates ? (purpose) => this.options.modelRegistry.resolveToolModelCandidates(purpose, this.options.model) : void 0,
83170
+ getPlanPolicyContext: () => ({
82329
83171
  mode: this.planModeEnabled ? "plan" : "normal",
82330
83172
  yoloMode: this.yoloMode,
82331
83173
  confirmationPolicy: "allow"
82332
83174
  })
82333
- });
83175
+ }, allowlistedDirs);
82334
83176
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
82335
83177
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
82336
83178
  return {
@@ -82460,7 +83302,7 @@ var InteractiveMode = class {
82460
83302
  async init() {
82461
83303
  const activeSession = this.sessionManager.getActiveSession();
82462
83304
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82463
- const { Agent } = await import("./chunks/src-2RCV3I3P.js");
83305
+ const { Agent } = await import("./chunks/src-5JJZZ5H7.js");
82464
83306
  this.agent = new Agent({
82465
83307
  initialState: {
82466
83308
  systemPrompt: this.buildSystemPrompt(),
@@ -82637,20 +83479,20 @@ var InteractiveMode = class {
82637
83479
  setupAgentSubscription() {
82638
83480
  bindInteractiveRuntimeEvents(this.runtimeEventHost);
82639
83481
  }
82640
- _isContextOverflowError(errorMessage4) {
82641
- return isContextOverflowError(errorMessage4);
83482
+ _isContextOverflowError(errorMessage5) {
83483
+ return isContextOverflowError(errorMessage5);
82642
83484
  }
82643
83485
  _shouldAutoCompact(contextTokens, contextWindow) {
82644
83486
  return shouldAutoCompact(this.contextUiHost, contextTokens, contextWindow);
82645
83487
  }
82646
- _isRetryableError(errorMessage4) {
82647
- return isRetryableError(errorMessage4);
83488
+ _isRetryableError(errorMessage5) {
83489
+ return isRetryableError(errorMessage5);
82648
83490
  }
82649
- _isInvalidFileReferenceError(errorMessage4) {
82650
- return isInvalidFileReferenceError(errorMessage4);
83491
+ _isInvalidFileReferenceError(errorMessage5) {
83492
+ return isInvalidFileReferenceError(errorMessage5);
82651
83493
  }
82652
- _recoverFromInvalidFileReferenceError(errorMessage4) {
82653
- return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage4);
83494
+ _recoverFromInvalidFileReferenceError(errorMessage5) {
83495
+ return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage5);
82654
83496
  }
82655
83497
  async handleCompactCommand() {
82656
83498
  return handleCompactCommand(this.compactCommandHost);
@@ -82661,6 +83503,9 @@ var InteractiveMode = class {
82661
83503
  setupKeybindings() {
82662
83504
  return setupInteractiveKeybindings(this.keybindingsHost);
82663
83505
  }
83506
+ handlePasteImage() {
83507
+ void handleImagePaste(this);
83508
+ }
82664
83509
  async selectMentionOption(option) {
82665
83510
  return selectMentionOption(this.filePipelineHost, option);
82666
83511
  }
@@ -82836,8 +83681,8 @@ var InteractiveMode = class {
82836
83681
  clearStatusLine() {
82837
83682
  return clearStatusLine(this.uiHelperHost);
82838
83683
  }
82839
- showError(errorMessage4) {
82840
- return showError(this.uiHelperHost, errorMessage4);
83684
+ showError(errorMessage5) {
83685
+ return showError(this.uiHelperHost, errorMessage5);
82841
83686
  }
82842
83687
  async handleDirectBash(command) {
82843
83688
  return handleDirectBash(this.directBashHost, command);
@@ -82888,8 +83733,8 @@ var InteractiveMode = class {
82888
83733
  };
82889
83734
 
82890
83735
  // src/app/runtime-composition.ts
82891
- import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82892
- import { randomUUID } from "crypto";
83736
+ import { existsSync as existsSync19, mkdirSync as mkdirSync13 } from "fs";
83737
+ import { randomUUID as randomUUID2 } from "crypto";
82893
83738
  import * as path31 from "path";
82894
83739
  var CliMissingApiKeyError = class extends Error {
82895
83740
  constructor(message) {
@@ -82916,7 +83761,7 @@ function getCliRuntimePaths() {
82916
83761
  return { configDir, sessionsDir, skillsDir, builtinSkillsDir };
82917
83762
  }
82918
83763
  function ensureCliRuntimePaths(paths = getCliRuntimePaths()) {
82919
- if (!existsSync18(paths.skillsDir)) {
83764
+ if (!existsSync19(paths.skillsDir)) {
82920
83765
  mkdirSync13(paths.skillsDir, { recursive: true });
82921
83766
  }
82922
83767
  return paths;
@@ -83019,7 +83864,7 @@ async function composeCliRuntime(params) {
83019
83864
  await sessionManager.save();
83020
83865
  }
83021
83866
  const sessionId = sessionManager.getActiveSession()?.id;
83022
- const computerUseExecutionId = randomUUID();
83867
+ const computerUseExecutionId = randomUUID2();
83023
83868
  const fileObservationState = createFileObservationState();
83024
83869
  if (sessionId) {
83025
83870
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -83053,6 +83898,11 @@ async function composeCliRuntime(params) {
83053
83898
  });
83054
83899
  const fileSystemToolOptions = {
83055
83900
  ...buildCliFileAccessOptions({ cwd, configDir: paths.configDir, skills, allowlistedDirs: [paths.configDir] }),
83901
+ // Must match interactive-mode's rebuild view byte-for-byte: the rebuilt
83902
+ // tool surface replaces this composition-time set on the first mode
83903
+ // toggle/skill reload, and any description drift there would invalidate
83904
+ // the prompt-cache prefix exactly like the /yolo scope flip did.
83905
+ descriptionAllowlistedDirs: [],
83056
83906
  configDir: paths.configDir,
83057
83907
  fileObservationState,
83058
83908
  getAgentModel: getCurrentModel,
@@ -83180,6 +84030,7 @@ function createPrintModeRetryStatusReporter() {
83180
84030
  // src/app/print-mode-compaction.ts
83181
84031
  function createPrintModeCompactionHandler(params) {
83182
84032
  return {
84033
+ modelView: cliCompactionModelView,
83183
84034
  compact: async (context) => {
83184
84035
  const beforeCompact = await params.runner.emitSessionBeforeCompact({
83185
84036
  type: "session:before_compact",
@@ -83343,7 +84194,7 @@ async function runPrintMode(params) {
83343
84194
  `);
83344
84195
  console.log("Assistant: ");
83345
84196
  }
83346
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.js");
84197
+ const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
83347
84198
  let agent;
83348
84199
  const printModeExtensions = await loadPrintModeExtensions({
83349
84200
  cwd: runtime.cwd,
@@ -83512,21 +84363,21 @@ async function runPrintMode(params) {
83512
84363
  console.log(assistantText);
83513
84364
  }
83514
84365
  } catch (err) {
83515
- const errorMessage4 = err instanceof Error ? err.message : String(err);
83516
- sessionTrace.noteStalled(errorMessage4, { mode: "print" });
84366
+ const errorMessage5 = err instanceof Error ? err.message : String(err);
84367
+ sessionTrace.noteStalled(errorMessage5, { mode: "print" });
83517
84368
  process.exitCode = 1;
83518
84369
  if (params.jsonOutput) {
83519
84370
  console.log(
83520
84371
  JSON.stringify({
83521
84372
  ok: false,
83522
- error: errorMessage4,
84373
+ error: errorMessage5,
83523
84374
  sessionId: runtime.sessionManager.getActiveSession()?.id,
83524
84375
  provider: runtime.provider,
83525
84376
  modelId: runtime.modelId
83526
84377
  })
83527
84378
  );
83528
84379
  }
83529
- console.error("Error:", errorMessage4);
84380
+ console.error("Error:", errorMessage5);
83530
84381
  }
83531
84382
  try {
83532
84383
  if (!params.rawOutput && !params.jsonOutput) console.log("");
@@ -83643,7 +84494,6 @@ function parseInput(line) {
83643
84494
  };
83644
84495
  }
83645
84496
  if (parsed.type === "sidechat.request" && typeof parsed.requestId === "string" && typeof parsed.prompt === "string") {
83646
- if (parsed.hostContext !== void 0 && !isHostContextBlock(parsed.hostContext)) return null;
83647
84497
  return {
83648
84498
  type: "sidechat.request",
83649
84499
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
@@ -83653,7 +84503,6 @@ function parseInput(line) {
83653
84503
  };
83654
84504
  }
83655
84505
  if ((parsed.type === "turn.request" || parsed.type === "turn.input") && typeof parsed.turnId === "string" && typeof parsed.prompt === "string") {
83656
- if (parsed.hostContext !== void 0 && !isHostContextBlock(parsed.hostContext)) return null;
83657
84506
  const streamingBehavior = parsed.streamingBehavior;
83658
84507
  if (streamingBehavior !== void 0 && streamingBehavior !== "steer" && streamingBehavior !== "followUp") {
83659
84508
  return null;
@@ -83772,7 +84621,7 @@ async function loadJsonModeExtensions(params) {
83772
84621
  getRuntimeTools: () => params.getAgent()?.state.tools ?? [],
83773
84622
  resolveModelClass: (modelClass) => params.runtime.modelRegistry.resolveModelClass(modelClass),
83774
84623
  resolveModelRef: (modelRef) => params.runtime.modelRegistry.resolveModelRef(modelRef),
83775
- registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
84624
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector, params.envelopeRegistry),
83776
84625
  getMessages: () => params.getAgent()?.state.messages ?? [],
83777
84626
  getSessionMetadata: () => ({ ...params.runtime.sessionManager.getActiveSession()?.metadata ?? {} }),
83778
84627
  setSessionMetadata: (key, value) => {
@@ -83783,7 +84632,7 @@ async function loadJsonModeExtensions(params) {
83783
84632
  },
83784
84633
  yoloMode: false
83785
84634
  };
83786
- const { extensions } = await loadExtensions(
84635
+ const { extensions, errors } = await loadExtensions(
83787
84636
  params.runtime.extensionPaths,
83788
84637
  params.runtime.cwd,
83789
84638
  uiContext,
@@ -83803,6 +84652,10 @@ async function loadJsonModeExtensions(params) {
83803
84652
  false
83804
84653
  );
83805
84654
  runner.setUIContext(uiContext);
84655
+ if (errors.length) {
84656
+ await runner.dispose("shutdown");
84657
+ throw new Error(errors.map(({ path: path34, error }) => `${path34}: ${error}`).join("\n"));
84658
+ }
83806
84659
  try {
83807
84660
  await restoreExtensionProviderCredentials(params.runtime.modelRegistry);
83808
84661
  const extensionTools = filterEnabledTools(convertExtensionTools(extensions), params.runtime.settingsManager);
@@ -83822,6 +84675,7 @@ async function runJsonEventsMode(params) {
83822
84675
  const sessionId = params.explicitSessionId || params.runtime?.sessionManager.getActiveSession()?.id || `pie-json-${Date.now()}`;
83823
84676
  let activeTurn = null;
83824
84677
  let runtime = params.runtime;
84678
+ let envelopeRegistry = createCliHostEnvelopeRegistry();
83825
84679
  let agent;
83826
84680
  let controller;
83827
84681
  let disposeAgentSubscription;
@@ -83838,6 +84692,7 @@ async function runJsonEventsMode(params) {
83838
84692
  const completedSessionControlRequests = /* @__PURE__ */ new Map();
83839
84693
  const completedSidechatRequests = /* @__PURE__ */ new Map();
83840
84694
  const ensureRuntimeInFlight = /* @__PURE__ */ new Map();
84695
+ let compositionChain = Promise.resolve();
83841
84696
  let runtimeClosing = false;
83842
84697
  let activeSidechat = null;
83843
84698
  let activeSidechatPromise = null;
@@ -83853,7 +84708,7 @@ async function runJsonEventsMode(params) {
83853
84708
  // Capability broadcast: the envelope kinds THIS build can project at
83854
84709
  // intake (embedded external kinds included), so embedders can probe
83855
84710
  // before their first turn instead of learning mismatches from failures.
83856
- capabilities: { hostContextKinds: cliHostEnvelopeKinds() }
84711
+ capabilities: { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) }
83857
84712
  });
83858
84713
  function trackSidechatPromise(promise) {
83859
84714
  activeSidechatPromise = promise;
@@ -83870,13 +84725,6 @@ async function runJsonEventsMode(params) {
83870
84725
  function delay(ms) {
83871
84726
  return new Promise((resolve7) => setTimeout(resolve7, Math.max(0, ms)));
83872
84727
  }
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
84728
  function touch() {
83881
84729
  if (activeTurn) {
83882
84730
  activeTurn.lastActivityAt = Date.now();
@@ -84192,6 +85040,11 @@ async function runJsonEventsMode(params) {
84192
85040
  }
84193
85041
  const startedAt = Date.now();
84194
85042
  if (params.mockResponse !== void 0) {
85043
+ const hostContextFailure = validateIntakeHostContext(message);
85044
+ if (hostContextFailure) {
85045
+ writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
85046
+ return;
85047
+ }
84195
85048
  writeEvent({ type: "sidechat.started", sessionId, requestId });
84196
85049
  const event = {
84197
85050
  type: "sidechat.completed",
@@ -84214,7 +85067,7 @@ async function runJsonEventsMode(params) {
84214
85067
  }
84215
85068
  };
84216
85069
  try {
84217
- await ensureRuntime();
85070
+ await ensureRuntime(runtime?.cwd);
84218
85071
  const hostContextFailure = validateIntakeHostContext(message);
84219
85072
  if (hostContextFailure) {
84220
85073
  writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
@@ -84232,22 +85085,26 @@ async function runJsonEventsMode(params) {
84232
85085
  return;
84233
85086
  }
84234
85087
  const parent = agent;
84235
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.js");
85088
+ const sideRegistry = envelopeRegistry;
85089
+ const sideTrace = trace;
85090
+ const sideApiKey = runtime.apiKey;
85091
+ const snapshot = structuredClone(parent.state.messages);
85092
+ const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
84236
85093
  const sideAgent = new AgentClass({
84237
85094
  initialState: {
84238
85095
  systemPrompt: parent.state.systemPrompt,
84239
85096
  model: parent.state.model,
84240
85097
  thinkingLevel: parent.state.thinkingLevel,
84241
85098
  tools: [],
84242
- messages: structuredClone(parent.state.messages)
85099
+ messages: snapshot
84243
85100
  },
84244
- apiKey: runtime.apiKey,
85101
+ apiKey: sideApiKey,
84245
85102
  // The sidechat snapshot clones the parent's stored history, including
84246
85103
  // persisted hostContext facts; project them like the main agent does.
84247
- transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
85104
+ transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => sideTrace?.noteRuntimeEvent(
84248
85105
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84249
85106
  outcome.audit
84250
- )),
85107
+ ), sideRegistry),
84251
85108
  // Same body-free transport timeline as the main agent: a sidechat is
84252
85109
  // an independent Responses request and previously had no record at all
84253
85110
  // when it stalled. `streamSimple` matches the host default when the
@@ -84303,6 +85160,7 @@ async function runJsonEventsMode(params) {
84303
85160
  const turn = activeTurn;
84304
85161
  if (!turn || turn.finalized) return;
84305
85162
  turn.finalized = true;
85163
+ turn.releaseReady();
84306
85164
  const activeEntryId = activeHeadEntryId();
84307
85165
  const base = {
84308
85166
  sessionId: turn.sessionId,
@@ -84535,6 +85393,7 @@ async function runJsonEventsMode(params) {
84535
85393
  agent.releaseTransport();
84536
85394
  }
84537
85395
  agent = void 0;
85396
+ envelopeRegistry = createCliHostEnvelopeRegistry();
84538
85397
  try {
84539
85398
  await runtime?.sessionManager.save();
84540
85399
  } catch {
@@ -84561,7 +85420,9 @@ async function runJsonEventsMode(params) {
84561
85420
  const effectiveCwd = requestedCwd ?? process.cwd();
84562
85421
  const inFlight = ensureRuntimeInFlight.get(effectiveCwd);
84563
85422
  if (inFlight) return inFlight;
84564
- const composed = composeRuntime(effectiveCwd);
85423
+ const composed = compositionChain.then(() => composeRuntime(effectiveCwd));
85424
+ compositionChain = composed.catch(() => {
85425
+ });
84565
85426
  ensureRuntimeInFlight.set(effectiveCwd, composed);
84566
85427
  void composed.finally(() => ensureRuntimeInFlight.delete(effectiveCwd)).catch(() => {
84567
85428
  });
@@ -84592,14 +85453,16 @@ async function runJsonEventsMode(params) {
84592
85453
  if (runtime.initialModel.unconfigured || controller) {
84593
85454
  return;
84594
85455
  }
84595
- const { Agent: AgentClass } = await import("./chunks/src-2RCV3I3P.js");
85456
+ const { Agent: AgentClass } = await import("./chunks/src-5JJZZ5H7.js");
84596
85457
  if (runtimeClosing) return;
84597
- const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
85458
+ const compositionRegistry = createCliHostEnvelopeRegistry();
85459
+ const jsonModeExtensions = await loadJsonModeExtensions({ runtime, envelopeRegistry: compositionRegistry, getAgent: () => agent });
84598
85460
  if (runtimeClosing) {
84599
85461
  await jsonModeExtensions.runner.dispose("shutdown");
84600
85462
  return;
84601
85463
  }
84602
85464
  extensionRunner = jsonModeExtensions.runner;
85465
+ envelopeRegistry = compositionRegistry;
84603
85466
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
84604
85467
  const restoredApiKey = runtime.modelRegistry.getApiKey(runtime.model);
84605
85468
  if (restoredApiKey) runtime.apiKey = restoredApiKey;
@@ -84635,7 +85498,7 @@ async function runJsonEventsMode(params) {
84635
85498
  transformContext: async (messages) => projectCliHostEnvelopes(messages, (outcome) => trace?.noteRuntimeEvent(
84636
85499
  outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected",
84637
85500
  outcome.audit
84638
- )),
85501
+ ), compositionRegistry),
84639
85502
  // Body-free transport timeline for the main (JSON-events) agent.
84640
85503
  streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84641
85504
  });
@@ -84671,8 +85534,10 @@ async function runJsonEventsMode(params) {
84671
85534
  reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84672
85535
  })),
84673
85536
  compaction: {
85537
+ modelView: (message) => projectCliHostEnvelopes([message], void 0, compositionRegistry)[0],
84674
85538
  compact: async (context) => createCliAutoCompactionResult({
84675
85539
  context,
85540
+ envelopeRegistry: compositionRegistry,
84676
85541
  model: runtime.model,
84677
85542
  apiKey: runtime.apiKey,
84678
85543
  systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt,
@@ -84780,7 +85645,7 @@ async function runJsonEventsMode(params) {
84780
85645
  return message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : [{ type: "text", text: message.prompt }];
84781
85646
  }
84782
85647
  function validateIntakeHostContext(message) {
84783
- return message.hostContext ? intakeHostContextFailure(message.hostContext) : void 0;
85648
+ return message.hostContext !== void 0 ? intakeHostContextFailure(message.hostContext, envelopeRegistry) : void 0;
84784
85649
  }
84785
85650
  async function runTurn(message) {
84786
85651
  if (isSessionMismatch(message)) {
@@ -84793,21 +85658,19 @@ async function runJsonEventsMode(params) {
84793
85658
  return;
84794
85659
  }
84795
85660
  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);
85661
+ const owner = activeTurn;
85662
+ owner.queue = owner.queue.then(() => queueActiveTurnInput(message, owner));
85663
+ await owner.queue;
84808
85664
  return;
84809
85665
  }
85666
+ let releaseReady;
85667
+ const ready = new Promise((resolve7) => {
85668
+ releaseReady = resolve7;
85669
+ });
84810
85670
  activeTurn = {
85671
+ ready,
85672
+ releaseReady,
85673
+ queue: Promise.resolve(),
84811
85674
  sessionId,
84812
85675
  turnId: message.turnId,
84813
85676
  prompt: message.prompt,
@@ -84828,7 +85691,7 @@ async function runJsonEventsMode(params) {
84828
85691
  finalize("failed", {
84829
85692
  code: mockHostContextFailure.code,
84830
85693
  message: mockHostContextFailure.message,
84831
- ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85694
+ ...mockHostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84832
85695
  });
84833
85696
  if (activeTurn === turn) emitSettled(turn);
84834
85697
  if (activeTurn === turn) activeTurn = null;
@@ -84868,12 +85731,21 @@ async function runJsonEventsMode(params) {
84868
85731
  if (activeTurn === turn) activeTurn = null;
84869
85732
  return;
84870
85733
  }
85734
+ if (activeTurn !== turn || turn.finalized || turn.interrupted) {
85735
+ if (activeTurn === turn && !turn.finalized) finalize("cancelled", "user_interrupt");
85736
+ turn.releaseReady();
85737
+ if (activeTurn === turn) {
85738
+ emitSettled(turn);
85739
+ activeTurn = null;
85740
+ }
85741
+ return;
85742
+ }
84871
85743
  const hostContextFailure = validateIntakeHostContext(message);
84872
85744
  if (hostContextFailure) {
84873
85745
  finalize("failed", {
84874
85746
  code: hostContextFailure.code,
84875
85747
  message: hostContextFailure.message,
84876
- ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
85748
+ ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
84877
85749
  });
84878
85750
  if (activeTurn === turn) emitSettled(turn);
84879
85751
  if (activeTurn === turn) activeTurn = null;
@@ -84913,7 +85785,9 @@ async function runJsonEventsMode(params) {
84913
85785
  try {
84914
85786
  trace?.notePendingUserText(message.prompt);
84915
85787
  trace?.noteDispatchStart({ mode: "json-events" });
84916
- await controller.prompt(promptInputFor(message), { source: "json-events" });
85788
+ const dispatched = controller.prompt(promptInputFor(message), { source: "json-events" });
85789
+ turn.releaseReady();
85790
+ await dispatched;
84917
85791
  trace?.noteDispatchSettled({ mode: "json-events" });
84918
85792
  trace?.noteWaitForIdleStart({ mode: "json-events" });
84919
85793
  await controller.waitForSettled();
@@ -84951,7 +85825,7 @@ async function runJsonEventsMode(params) {
84951
85825
  if (activeTurn === turn) activeTurn = null;
84952
85826
  }
84953
85827
  }
84954
- async function queueActiveTurnInput(message) {
85828
+ async function queueActiveTurnInput(message, owner) {
84955
85829
  if (!message.streamingBehavior) {
84956
85830
  writeEvent({
84957
85831
  type: "turn.failed",
@@ -84961,8 +85835,9 @@ async function runJsonEventsMode(params) {
84961
85835
  });
84962
85836
  return;
84963
85837
  }
84964
- const activeController = await waitForActiveController();
84965
- if (!activeController) {
85838
+ await owner.ready;
85839
+ const activeController = controller;
85840
+ if (!activeController || activeTurn !== owner || owner.finalized || owner.interrupted || runtimeClosing) {
84966
85841
  writeEvent({
84967
85842
  type: "turn.failed",
84968
85843
  sessionId,
@@ -84971,6 +85846,18 @@ async function runJsonEventsMode(params) {
84971
85846
  });
84972
85847
  return;
84973
85848
  }
85849
+ const failure = validateIntakeHostContext(message);
85850
+ if (failure) {
85851
+ writeEvent({
85852
+ type: "turn.failed",
85853
+ sessionId,
85854
+ turnId: message.turnId,
85855
+ error: failure.message,
85856
+ code: failure.code,
85857
+ ...failure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds(envelopeRegistry) } : {}
85858
+ });
85859
+ return;
85860
+ }
84974
85861
  try {
84975
85862
  applyRuntimeSelection(message);
84976
85863
  trace?.notePendingUserText(message.prompt);
@@ -84989,7 +85876,7 @@ async function runJsonEventsMode(params) {
84989
85876
  sessionId,
84990
85877
  turnId: message.turnId,
84991
85878
  mode: message.streamingBehavior,
84992
- activeTurnId: activeTurn?.turnId
85879
+ activeTurnId: owner.turnId
84993
85880
  });
84994
85881
  } catch (error) {
84995
85882
  writeEvent({
@@ -85254,7 +86141,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
85254
86141
  }
85255
86142
  async function createCliTestStreamFnFromEnv() {
85256
86143
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
85257
- return (await import("./chunks/test-stream-Y2S3H7DK.js")).createTestStreamFnFromSequenceEnv();
86144
+ return (await import("./chunks/test-stream-ZBTSPU7J.js")).createTestStreamFnFromSequenceEnv();
85258
86145
  }
85259
86146
  function resolveJsonEventsDetailFlag() {
85260
86147
  for (let index = 0; index < process.argv.length; index += 1) {