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