@cydm/pie 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin/extensions/ask-user/index.js +1 -1
- package/dist/builtin/extensions/computer-use/index.js +59 -23
- package/dist/builtin/extensions/herdr-agent-state/index.js +272 -0
- package/dist/builtin/extensions/herdr-agent-state/package.json +11 -0
- package/dist/builtin/extensions/mcp-adapter/index.js +186 -62
- package/dist/builtin/extensions/plan-mode/index.js +1 -1
- package/dist/builtin/extensions/subagent/index.js +3 -3
- package/dist/builtin/extensions/todo/index.js +1 -1
- package/dist/builtin/skills/tui-use/tui-use-cli.cjs +18 -0
- package/dist/builtin/skills/tui-use/tui-use-daemon.cjs +14 -1
- package/dist/chunks/{chunk-TJ2F4FL5.js → chunk-P3QPJXS7.js} +5 -5
- package/dist/chunks/{chunk-LU7RKALH.js → chunk-UPPICLVS.js} +4 -3
- package/dist/cli.js +76 -45
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-UPPICLVS.js";
|
|
5
5
|
import "../../../chunks/chunk-QJH6YN2W.js";
|
|
6
6
|
import "../../../chunks/chunk-IM43UYVV.js";
|
|
7
7
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
@@ -3776,10 +3776,26 @@ function containsEditable(node) {
|
|
|
3776
3776
|
if (node.canSetValue || node.role.toLowerCase().includes("text")) return true;
|
|
3777
3777
|
return node.children.some(containsEditable);
|
|
3778
3778
|
}
|
|
3779
|
+
function wireableInputDescendants(node) {
|
|
3780
|
+
const matches = [];
|
|
3781
|
+
const visit = (current) => {
|
|
3782
|
+
for (const child of current.children) {
|
|
3783
|
+
if (child.isTextInput && child.wireRef && !child.pictureOnly && !child.offscreen) matches.push(child);
|
|
3784
|
+
visit(child);
|
|
3785
|
+
}
|
|
3786
|
+
};
|
|
3787
|
+
visit(node);
|
|
3788
|
+
return matches;
|
|
3789
|
+
}
|
|
3779
3790
|
function recordActionFocus(action, state, node, coordinateFocus = false) {
|
|
3780
3791
|
if (!["click", "press", "keypress", "typeText"].includes(action.action) || !action.ref && action.action !== "click") return;
|
|
3781
3792
|
state.currentFocus = Boolean(node?.isTextInput && !node.pictureOnly) || coordinateFocus;
|
|
3782
3793
|
state.focusRef = node?.isTextInput && node.wireRef && !node.pictureOnly ? node.ref : void 0;
|
|
3794
|
+
if (!state.currentFocus && node && !node.pictureOnly && action.ref && (action.action === "click" || action.action === "press") && containsEditable(node)) {
|
|
3795
|
+
const wireable = wireableInputDescendants(node);
|
|
3796
|
+
state.currentFocus = true;
|
|
3797
|
+
state.focusRef = wireable.length === 1 ? wireable[0].ref : void 0;
|
|
3798
|
+
}
|
|
3783
3799
|
}
|
|
3784
3800
|
function editorAtPoint(nodes, x, y) {
|
|
3785
3801
|
const matches = nodes.filter((node) => node.isTextInput && node.wireRef && !node.pictureOnly && !node.offscreen && node.rect && x >= node.rect.x && x < node.rect.x + node.rect.w && y >= node.rect.y && y < node.rect.y + node.rect.h);
|
|
@@ -3790,24 +3806,23 @@ function prepareAction(action, state, env) {
|
|
|
3790
3806
|
const focusedKeyboard = !env.headless && state.currentFocus && !action.ref && (operation === "typeText" || operation === "keypress");
|
|
3791
3807
|
const usesCurrentFocus = focusedKeyboard && !state.focusRef;
|
|
3792
3808
|
const target = usesCurrentFocus ? focusedTarget(env) : nativeTarget(focusedKeyboard && state.focusRef ? { ...action, ref: state.focusRef } : action, operation, env);
|
|
3793
|
-
const establishesFocus = !env.headless && Boolean(action.ref) && (operation === "click" || operation === "press") && containsEditable(env.node(action.ref));
|
|
3794
3809
|
const needsForeground = !env.headless && (operation === "click" || operation === "press") && "x" in target;
|
|
3795
3810
|
switch (operation) {
|
|
3796
3811
|
case "press":
|
|
3797
3812
|
case "click":
|
|
3798
|
-
return { action: operation, target, params: { button: mouseButton(action.button), clickCount: clickCount(action.clickCount) },
|
|
3813
|
+
return { action: operation, target, params: { button: mouseButton(action.button), clickCount: clickCount(action.clickCount) }, usesCurrentFocus: false, needsForeground };
|
|
3799
3814
|
case "setText":
|
|
3800
|
-
return { action: operation, target, params: { text: action.text ?? "" },
|
|
3815
|
+
return { action: operation, target, params: { text: action.text ?? "" }, usesCurrentFocus: false, needsForeground: false };
|
|
3801
3816
|
case "typeText":
|
|
3802
|
-
return { action: operation, target, params: { text: action.text ?? "" },
|
|
3817
|
+
return { action: operation, target, params: { text: action.text ?? "" }, usesCurrentFocus, needsForeground: false };
|
|
3803
3818
|
case "keypress":
|
|
3804
|
-
return { action: operation, target, params: { keys: keys(action.keys) },
|
|
3819
|
+
return { action: operation, target, params: { keys: keys(action.keys) }, usesCurrentFocus, needsForeground: false };
|
|
3805
3820
|
case "scroll":
|
|
3806
|
-
return { action: operation, target, params: { scrollX: scrollDelta(action.scrollX), scrollY: scrollDelta(action.scrollY) },
|
|
3821
|
+
return { action: operation, target, params: { scrollX: scrollDelta(action.scrollX), scrollY: scrollDelta(action.scrollY) }, usesCurrentFocus: false, needsForeground: false };
|
|
3807
3822
|
case "drag":
|
|
3808
|
-
return { action: operation, target, params: { path: path(action.path, env) },
|
|
3823
|
+
return { action: operation, target, params: { path: path(action.path, env) }, usesCurrentFocus: false, needsForeground: false };
|
|
3809
3824
|
case "moveMouse":
|
|
3810
|
-
return { action: operation, target, params: {},
|
|
3825
|
+
return { action: operation, target, params: {}, usesCurrentFocus: false, needsForeground: false };
|
|
3811
3826
|
}
|
|
3812
3827
|
}
|
|
3813
3828
|
function canRetryInForeground(action, outcome, headless) {
|
|
@@ -6901,17 +6916,28 @@ async function releaseOwnedBrowser(child, port, profile) {
|
|
|
6901
6916
|
if (port) await requestBrowserClose(port).catch(() => void 0);
|
|
6902
6917
|
if (!await waitForExit(child, 700)) {
|
|
6903
6918
|
if (process.platform === "win32" && child.pid) {
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
})
|
|
6919
|
+
const pid = child.pid;
|
|
6920
|
+
await new Promise((resolve) => {
|
|
6921
|
+
execFile("taskkill.exe", ["/PID", String(pid), "/T", "/F"], {
|
|
6922
|
+
windowsHide: true,
|
|
6923
|
+
timeout: 2e3
|
|
6924
|
+
}, () => resolve());
|
|
6925
|
+
});
|
|
6926
|
+
if (!await waitForExit(child, 1500)) {
|
|
6927
|
+
try {
|
|
6928
|
+
process.kill(pid);
|
|
6929
|
+
} catch {
|
|
6930
|
+
}
|
|
6931
|
+
if (!await waitForExit(child, 2e3)) {
|
|
6932
|
+
throw new Error(`Owned browser process ${pid} survived taskkill escalation.`);
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6911
6935
|
} else child.kill("SIGTERM");
|
|
6912
|
-
if (
|
|
6913
|
-
if (
|
|
6914
|
-
|
|
6936
|
+
if (process.platform !== "win32") {
|
|
6937
|
+
if (!await waitForExit(child, 2e3)) {
|
|
6938
|
+
child.kill("SIGKILL");
|
|
6939
|
+
if (!await waitForExit(child, 2e3)) throw new Error("Owned browser did not exit.");
|
|
6940
|
+
}
|
|
6915
6941
|
}
|
|
6916
6942
|
}
|
|
6917
6943
|
}
|
|
@@ -7206,16 +7232,24 @@ function shutdownComputerUseSession() {
|
|
|
7206
7232
|
}
|
|
7207
7233
|
async function releaseComputerUseSession() {
|
|
7208
7234
|
const drain = resourceScheduler.close();
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7235
|
+
const failures = [];
|
|
7236
|
+
const attempt = (step, work) => work().catch((error) => {
|
|
7237
|
+
failures.push(error instanceof Error ? new Error(`${step}: ${error.message}`) : error);
|
|
7238
|
+
});
|
|
7239
|
+
await attempt("disconnect CDP", () => disconnectCdp());
|
|
7240
|
+
await attempt("platform shutdown", async () => {
|
|
7241
|
+
await currentPlatformBackend.shutdown?.();
|
|
7242
|
+
});
|
|
7243
|
+
await attempt("drain scheduler", () => drain);
|
|
7244
|
+
await attempt("release owned browser", () => releaseOwnedBrowserState(runtimeState));
|
|
7213
7245
|
if (runtimeState.managedBrowserCdpPort && process.env.PIE_COMPUTER_USE_CDP_PORT === runtimeState.managedBrowserCdpPort) {
|
|
7214
7246
|
if (runtimeState.previousCdpPort === void 0) delete process.env.PIE_COMPUTER_USE_CDP_PORT;
|
|
7215
7247
|
else process.env.PIE_COMPUTER_USE_CDP_PORT = runtimeState.previousCdpPort;
|
|
7216
7248
|
}
|
|
7217
|
-
runtimeState.managedBrowserCdpPort = void 0;
|
|
7218
7249
|
runtimeState.previousCdpPort = void 0;
|
|
7250
|
+
if (runtimeState.managedBrowser === void 0) {
|
|
7251
|
+
runtimeState.managedBrowserCdpPort = void 0;
|
|
7252
|
+
}
|
|
7219
7253
|
savedStates.clear();
|
|
7220
7254
|
clearStoredOutputs();
|
|
7221
7255
|
runtimeState.windowRefs.clear();
|
|
@@ -7227,6 +7261,8 @@ async function releaseComputerUseSession() {
|
|
|
7227
7261
|
runtimeState.helperDiagnostics = void 0;
|
|
7228
7262
|
runtimeState.lastPermissionCheckAt = 0;
|
|
7229
7263
|
resourceScheduler = new ResourceScheduler();
|
|
7264
|
+
if (failures.length === 1) throw failures[0];
|
|
7265
|
+
if (failures.length > 1) throw new AggregateError(failures, "computer-use session release had partial failures (state was reset)");
|
|
7230
7266
|
}
|
|
7231
7267
|
function currentRuntimeMode() {
|
|
7232
7268
|
return isHeadlessMode() ? "stealth" : "default";
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
+
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
3
|
+
|
|
4
|
+
// builtin/extensions/herdr-agent-state/index.ts
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
var AGENT_LABEL = "pie";
|
|
7
|
+
var SOURCE_ID = "pie";
|
|
8
|
+
var BLOCKING_TOOL_NAME = "ask_user_multi";
|
|
9
|
+
var REPORT_TIMEOUT_MS = 3e3;
|
|
10
|
+
var MAX_RESUME_ARGS = 64;
|
|
11
|
+
var MAX_RESUME_TOTAL_CHARS = 8192;
|
|
12
|
+
var RESUME_ARG_PATTERN = /^[A-Za-z0-9._:/=@,%+-]+$/;
|
|
13
|
+
var MESSAGE_MAX_CHARS = 120;
|
|
14
|
+
function readHerdrEnvironment(env = process.env) {
|
|
15
|
+
if (env.HERDR_ENV !== "1") return void 0;
|
|
16
|
+
const paneId = env.HERDR_PANE_ID;
|
|
17
|
+
const binPath = env.HERDR_BIN_PATH;
|
|
18
|
+
if (typeof paneId !== "string" || !paneId) return void 0;
|
|
19
|
+
if (typeof binPath !== "string" || !binPath) return void 0;
|
|
20
|
+
return { paneId, binPath };
|
|
21
|
+
}
|
|
22
|
+
function createReportSequencer(now = Date.now) {
|
|
23
|
+
let seq = now() * 1e3;
|
|
24
|
+
return () => {
|
|
25
|
+
seq += 1;
|
|
26
|
+
return seq;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function summarizeAskUserQuestion(args) {
|
|
30
|
+
if (!args || typeof args !== "object") return void 0;
|
|
31
|
+
const record = args;
|
|
32
|
+
let text = record.question;
|
|
33
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
34
|
+
const questions = record.questions;
|
|
35
|
+
if (Array.isArray(questions) && questions.length > 0) {
|
|
36
|
+
const first = questions[0];
|
|
37
|
+
if (first && typeof first === "object") {
|
|
38
|
+
text = first.question;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (typeof text !== "string") return void 0;
|
|
43
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
44
|
+
if (!cleaned) return void 0;
|
|
45
|
+
return cleaned.length > MESSAGE_MAX_CHARS ? `${cleaned.slice(0, MESSAGE_MAX_CHARS - 3)}...` : cleaned;
|
|
46
|
+
}
|
|
47
|
+
function buildResumeArgv(sessionId) {
|
|
48
|
+
const argv = ["pie", "chat", "--session-id", sessionId];
|
|
49
|
+
if (argv.length > MAX_RESUME_ARGS) return void 0;
|
|
50
|
+
if (argv.join(" ").length > MAX_RESUME_TOTAL_CHARS) return void 0;
|
|
51
|
+
for (const arg of argv) {
|
|
52
|
+
if (!RESUME_ARG_PATTERN.test(arg)) return void 0;
|
|
53
|
+
}
|
|
54
|
+
return argv;
|
|
55
|
+
}
|
|
56
|
+
function buildStateReportArgs(params) {
|
|
57
|
+
const args = [
|
|
58
|
+
"pane",
|
|
59
|
+
"report-agent",
|
|
60
|
+
params.paneId,
|
|
61
|
+
"--source",
|
|
62
|
+
params.source,
|
|
63
|
+
"--agent",
|
|
64
|
+
params.agent,
|
|
65
|
+
"--state",
|
|
66
|
+
params.state,
|
|
67
|
+
"--seq",
|
|
68
|
+
String(params.seq)
|
|
69
|
+
];
|
|
70
|
+
if (params.message) {
|
|
71
|
+
args.push("--message", params.message);
|
|
72
|
+
}
|
|
73
|
+
if (params.agentSessionId) {
|
|
74
|
+
args.push("--agent-session-id", params.agentSessionId);
|
|
75
|
+
}
|
|
76
|
+
if (params.resumeArgv && params.resumeArgv.length > 0) {
|
|
77
|
+
args.push("--", ...params.resumeArgv);
|
|
78
|
+
}
|
|
79
|
+
return args;
|
|
80
|
+
}
|
|
81
|
+
function buildSessionReportArgs(params) {
|
|
82
|
+
const args = [
|
|
83
|
+
"pane",
|
|
84
|
+
"report-agent-session",
|
|
85
|
+
params.paneId,
|
|
86
|
+
"--source",
|
|
87
|
+
params.source,
|
|
88
|
+
"--agent",
|
|
89
|
+
params.agent,
|
|
90
|
+
"--seq",
|
|
91
|
+
String(params.seq),
|
|
92
|
+
"--agent-session-id",
|
|
93
|
+
params.agentSessionId
|
|
94
|
+
];
|
|
95
|
+
if (params.resumeArgv?.length) args.push("--", ...params.resumeArgv);
|
|
96
|
+
return args;
|
|
97
|
+
}
|
|
98
|
+
function buildReleaseReportArgs(params) {
|
|
99
|
+
return [
|
|
100
|
+
"pane",
|
|
101
|
+
"release-agent",
|
|
102
|
+
params.paneId,
|
|
103
|
+
"--source",
|
|
104
|
+
params.source,
|
|
105
|
+
"--agent",
|
|
106
|
+
params.agent,
|
|
107
|
+
"--seq",
|
|
108
|
+
String(params.seq)
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
function spawnHerdrReport(binPath, args, timeoutMs = REPORT_TIMEOUT_MS) {
|
|
112
|
+
try {
|
|
113
|
+
const child = spawn(binPath, args, { detached: true, stdio: "ignore" });
|
|
114
|
+
let killTimer;
|
|
115
|
+
const clearTimers = () => {
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
if (killTimer) clearTimeout(killTimer);
|
|
118
|
+
};
|
|
119
|
+
const timer = setTimeout(() => {
|
|
120
|
+
try {
|
|
121
|
+
child.kill("SIGTERM");
|
|
122
|
+
killTimer = setTimeout(() => {
|
|
123
|
+
try {
|
|
124
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}, 1e3);
|
|
128
|
+
killTimer.unref?.();
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}, timeoutMs);
|
|
132
|
+
timer.unref?.();
|
|
133
|
+
child.on("error", clearTimers);
|
|
134
|
+
child.on("exit", clearTimers);
|
|
135
|
+
child.unref();
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function attachHerdrReporter(host, herdr, transport = (args) => spawnHerdrReport(herdr.binPath, args), sequencer = createReportSequencer()) {
|
|
140
|
+
const nextSeq = sequencer;
|
|
141
|
+
let agentActive = false;
|
|
142
|
+
const blockingToolCalls = /* @__PURE__ */ new Map();
|
|
143
|
+
let blockedMessage;
|
|
144
|
+
let heldPane = false;
|
|
145
|
+
let lastSessionId;
|
|
146
|
+
let lastState;
|
|
147
|
+
let lastMessage;
|
|
148
|
+
function report(args) {
|
|
149
|
+
transport(args);
|
|
150
|
+
}
|
|
151
|
+
function publishState(options) {
|
|
152
|
+
const next = desiredState();
|
|
153
|
+
const unchanged = next.state === lastState && next.message === lastMessage && !options?.force && !options?.resumeArgv;
|
|
154
|
+
if (unchanged) return;
|
|
155
|
+
lastState = next.state;
|
|
156
|
+
lastMessage = next.message;
|
|
157
|
+
heldPane = true;
|
|
158
|
+
report(
|
|
159
|
+
buildStateReportArgs({
|
|
160
|
+
paneId: herdr.paneId,
|
|
161
|
+
source: SOURCE_ID,
|
|
162
|
+
agent: AGENT_LABEL,
|
|
163
|
+
state: next.state,
|
|
164
|
+
seq: nextSeq(),
|
|
165
|
+
message: next.message,
|
|
166
|
+
agentSessionId: lastSessionId,
|
|
167
|
+
resumeArgv: options?.resumeArgv
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function publishSession(sessionId) {
|
|
172
|
+
const resumeArgv = buildResumeArgv(sessionId);
|
|
173
|
+
if (!heldPane) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
report(
|
|
177
|
+
buildSessionReportArgs({
|
|
178
|
+
paneId: herdr.paneId,
|
|
179
|
+
source: SOURCE_ID,
|
|
180
|
+
agent: AGENT_LABEL,
|
|
181
|
+
seq: nextSeq(),
|
|
182
|
+
agentSessionId: sessionId,
|
|
183
|
+
resumeArgv
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
function desiredState() {
|
|
188
|
+
if (blockingToolCalls.size > 0) {
|
|
189
|
+
return { state: "blocked", message: blockedMessage };
|
|
190
|
+
}
|
|
191
|
+
if (agentActive) {
|
|
192
|
+
return { state: "working" };
|
|
193
|
+
}
|
|
194
|
+
return { state: "idle" };
|
|
195
|
+
}
|
|
196
|
+
host.on("session:changed", (event) => {
|
|
197
|
+
const sessionId = typeof event.sessionId === "string" && event.sessionId ? event.sessionId : void 0;
|
|
198
|
+
const sessionIdChanged = sessionId !== lastSessionId;
|
|
199
|
+
lastSessionId = sessionId;
|
|
200
|
+
if (!heldPane) {
|
|
201
|
+
publishState({
|
|
202
|
+
force: true,
|
|
203
|
+
resumeArgv: sessionId ? buildResumeArgv(sessionId) : void 0
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (sessionIdChanged && sessionId) {
|
|
208
|
+
publishSession(sessionId);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
host.on("turn:start", () => {
|
|
212
|
+
agentActive = true;
|
|
213
|
+
publishState();
|
|
214
|
+
});
|
|
215
|
+
host.on("agent:end", () => {
|
|
216
|
+
agentActive = false;
|
|
217
|
+
blockingToolCalls.clear();
|
|
218
|
+
blockedMessage = void 0;
|
|
219
|
+
publishState();
|
|
220
|
+
});
|
|
221
|
+
host.on("tool:execution:start", (event) => {
|
|
222
|
+
if (event.toolName !== BLOCKING_TOOL_NAME) return;
|
|
223
|
+
const message = summarizeAskUserQuestion(event.args);
|
|
224
|
+
blockingToolCalls.set(event.toolCallId, message);
|
|
225
|
+
if (message) {
|
|
226
|
+
blockedMessage = message;
|
|
227
|
+
} else if (!blockedMessage) {
|
|
228
|
+
blockedMessage = "waiting for your answer";
|
|
229
|
+
}
|
|
230
|
+
publishState();
|
|
231
|
+
});
|
|
232
|
+
host.on("tool:execution:end", (event) => {
|
|
233
|
+
if (event.toolName !== BLOCKING_TOOL_NAME) return;
|
|
234
|
+
blockingToolCalls.delete(event.toolCallId);
|
|
235
|
+
blockedMessage = void 0;
|
|
236
|
+
for (const message of blockingToolCalls.values()) {
|
|
237
|
+
if (message) blockedMessage = message;
|
|
238
|
+
}
|
|
239
|
+
publishState();
|
|
240
|
+
});
|
|
241
|
+
host.registerCleanup?.((reason) => {
|
|
242
|
+
if (reason !== "shutdown") return;
|
|
243
|
+
if (!heldPane) return;
|
|
244
|
+
report(
|
|
245
|
+
buildReleaseReportArgs({
|
|
246
|
+
paneId: herdr.paneId,
|
|
247
|
+
source: SOURCE_ID,
|
|
248
|
+
agent: AGENT_LABEL,
|
|
249
|
+
seq: nextSeq()
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function herdrAgentStateExtension(ctx) {
|
|
255
|
+
const herdr = readHerdrEnvironment();
|
|
256
|
+
if (!herdr) return;
|
|
257
|
+
if (!ctx.hasUI) return;
|
|
258
|
+
ctx.log("herdr agent-state reporter enabled");
|
|
259
|
+
attachHerdrReporter(ctx, herdr);
|
|
260
|
+
}
|
|
261
|
+
export {
|
|
262
|
+
attachHerdrReporter,
|
|
263
|
+
buildReleaseReportArgs,
|
|
264
|
+
buildResumeArgv,
|
|
265
|
+
buildSessionReportArgs,
|
|
266
|
+
buildStateReportArgs,
|
|
267
|
+
createReportSequencer,
|
|
268
|
+
herdrAgentStateExtension as default,
|
|
269
|
+
readHerdrEnvironment,
|
|
270
|
+
spawnHerdrReport,
|
|
271
|
+
summarizeAskUserQuestion
|
|
272
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "herdr-agent-state-extension",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Report Pie interactive agent lifecycle state (working/idle/blocked), session identity, and resume command to the Herdr terminal workspace manager.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"pie": {
|
|
8
|
+
"name": "herdr-agent-state",
|
|
9
|
+
"main": "index.js"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -544,6 +544,7 @@ function normalizeEntry(name, raw, sourcePath, sourceScope, homeDir) {
|
|
|
544
544
|
const hasStdio = typeof raw.command === "string" && raw.command.length > 0;
|
|
545
545
|
const hasHttp = typeof raw.url === "string" && raw.url.length > 0;
|
|
546
546
|
if (!hasStdio && !hasHttp) return null;
|
|
547
|
+
const directTools = normalizeDirectTools(raw.directTools);
|
|
547
548
|
return {
|
|
548
549
|
name,
|
|
549
550
|
command: hasStdio ? expandHome(raw.command, homeDir) : void 0,
|
|
@@ -554,8 +555,8 @@ function normalizeEntry(name, raw, sourcePath, sourceScope, homeDir) {
|
|
|
554
555
|
headers: asStringRecord(raw.headers),
|
|
555
556
|
inheritEnv: raw.inheritEnv !== false,
|
|
556
557
|
connectTimeoutMs: typeof raw.connectTimeoutMs === "number" && Number.isFinite(raw.connectTimeoutMs) && raw.connectTimeoutMs > 0 ? raw.connectTimeoutMs : void 0,
|
|
557
|
-
directTools:
|
|
558
|
-
directToolsWhitelist:
|
|
558
|
+
directTools: directTools.mode,
|
|
559
|
+
directToolsWhitelist: directTools.whitelist,
|
|
559
560
|
disabled: raw.disabled === true,
|
|
560
561
|
sourcePath,
|
|
561
562
|
sourceScope
|
|
@@ -615,8 +616,8 @@ function resolveMcpAdapterConfig(cwd, configDir, homeDir = os.homedir()) {
|
|
|
615
616
|
continue;
|
|
616
617
|
}
|
|
617
618
|
const existing = merged.get(name);
|
|
618
|
-
if (existing && raw.disabled === true) {
|
|
619
|
-
merged.set(name, { ...existing, disabled:
|
|
619
|
+
if (existing && (raw.disabled === true || raw.disabled === false)) {
|
|
620
|
+
merged.set(name, { ...existing, disabled: raw.disabled });
|
|
620
621
|
}
|
|
621
622
|
}
|
|
622
623
|
}
|
|
@@ -24438,6 +24439,7 @@ import * as path2 from "node:path";
|
|
|
24438
24439
|
var CONNECT_TIMEOUT_MS = 3e4;
|
|
24439
24440
|
var PACKAGE_MANAGER_CONNECT_TIMEOUT_MS = 12e4;
|
|
24440
24441
|
var PACKAGE_MANAGER_COMMANDS = /* @__PURE__ */ new Set(["npx", "npm", "bunx", "uvx", "uv", "pnpm", "pnpmx"]);
|
|
24442
|
+
var METADATA_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
24441
24443
|
var CALL_TIMEOUT_MS = 12e4;
|
|
24442
24444
|
var STDERR_TAIL_LIMIT = 4e3;
|
|
24443
24445
|
function connectTimeoutFor(definition) {
|
|
@@ -24445,6 +24447,17 @@ function connectTimeoutFor(definition) {
|
|
|
24445
24447
|
const base = path2.basename(definition.command ?? "");
|
|
24446
24448
|
return PACKAGE_MANAGER_COMMANDS.has(base) ? PACKAGE_MANAGER_CONNECT_TIMEOUT_MS : CONNECT_TIMEOUT_MS;
|
|
24447
24449
|
}
|
|
24450
|
+
function unpinnedWarnings(definitions) {
|
|
24451
|
+
const commandPattern = new RegExp(`(^|\\s)(?:${[...PACKAGE_MANAGER_COMMANDS].join("|")})\\s`);
|
|
24452
|
+
const warnings = [];
|
|
24453
|
+
for (const definition of definitions) {
|
|
24454
|
+
const spec = [definition.command ?? "", ...definition.args].join(" ");
|
|
24455
|
+
if (commandPattern.test(spec) && /@latest\b/.test(spec)) {
|
|
24456
|
+
warnings.push(`${definition.name}: unpinned "@latest" fetch \u2014 pin an exact version (e.g. pkg@1.2.3).`);
|
|
24457
|
+
}
|
|
24458
|
+
}
|
|
24459
|
+
return warnings;
|
|
24460
|
+
}
|
|
24448
24461
|
function appendTail(tail, chunk) {
|
|
24449
24462
|
return (tail + String(chunk)).slice(-STDERR_TAIL_LIMIT);
|
|
24450
24463
|
}
|
|
@@ -24487,11 +24500,8 @@ function withTimeout(promise, timeoutMs, label, signal) {
|
|
|
24487
24500
|
});
|
|
24488
24501
|
}
|
|
24489
24502
|
var McpServerManager = class {
|
|
24490
|
-
|
|
24491
|
-
|
|
24492
|
-
cachePath;
|
|
24493
|
-
log;
|
|
24494
|
-
constructor(definitions, cachePath, log) {
|
|
24503
|
+
constructor(definitions, cacheDir, log, now = Date.now) {
|
|
24504
|
+
this.now = now;
|
|
24495
24505
|
this.definitions = new Map(definitions.map((definition) => [sanitizeName(definition.name), definition]));
|
|
24496
24506
|
if (this.definitions.size !== definitions.length) {
|
|
24497
24507
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -24506,9 +24516,22 @@ var McpServerManager = class {
|
|
|
24506
24516
|
);
|
|
24507
24517
|
}
|
|
24508
24518
|
}
|
|
24509
|
-
this.
|
|
24519
|
+
this.cacheDir = cacheDir;
|
|
24510
24520
|
this.log = log;
|
|
24511
24521
|
}
|
|
24522
|
+
now;
|
|
24523
|
+
runtimes = /* @__PURE__ */ new Map();
|
|
24524
|
+
definitions;
|
|
24525
|
+
// Cache directory holding one <server>.json shard per connected server. A
|
|
24526
|
+
// legacy pre-sharding single file (<configDir>/mcp-metadata.json) is
|
|
24527
|
+
// deliberately ignored — the cache is disposable and shards rebuild on the
|
|
24528
|
+
// next connect.
|
|
24529
|
+
cacheDir;
|
|
24530
|
+
log;
|
|
24531
|
+
/** Set by dispose(); after it, no new connects and no late attach. */
|
|
24532
|
+
disposed = false;
|
|
24533
|
+
/** Prefixed names claimed by more than one server in the last loadCatalog(); callTool fail-closes on them. */
|
|
24534
|
+
conflictedNames = /* @__PURE__ */ new Set();
|
|
24512
24535
|
getDefinition(server) {
|
|
24513
24536
|
return this.definitions.get(sanitizeName(server));
|
|
24514
24537
|
}
|
|
@@ -24549,6 +24572,7 @@ var McpServerManager = class {
|
|
|
24549
24572
|
}
|
|
24550
24573
|
/** Connect lazily. Concurrent callers share one in-flight connect. */
|
|
24551
24574
|
async connect(server, signal) {
|
|
24575
|
+
if (this.disposed) throw new Error("MCP server manager is disposed");
|
|
24552
24576
|
const key = sanitizeName(server);
|
|
24553
24577
|
const definition = this.definitions.get(key);
|
|
24554
24578
|
if (!definition) throw new Error(`unknown MCP server "${server}"`);
|
|
@@ -24561,12 +24585,17 @@ var McpServerManager = class {
|
|
|
24561
24585
|
runtime.connecting = (async () => {
|
|
24562
24586
|
this.log(`mcp: connecting to "${key}" (${definition.command ?? definition.url})`);
|
|
24563
24587
|
const { client, transport } = this.startTransport(definition, runtime);
|
|
24588
|
+
runtime.transport = transport;
|
|
24564
24589
|
client.onclose = () => {
|
|
24565
24590
|
this.log(`mcp: server "${key}" connection closed`);
|
|
24566
24591
|
runtime.client = void 0;
|
|
24567
24592
|
};
|
|
24568
24593
|
try {
|
|
24569
24594
|
await withTimeout(client.connect(transport), connectTimeoutFor(definition), `connect "${key}"`, signal);
|
|
24595
|
+
if (this.disposed) {
|
|
24596
|
+
await transport.close().catch(() => void 0);
|
|
24597
|
+
throw new Error(`MCP manager disposed while connecting to "${key}"`);
|
|
24598
|
+
}
|
|
24570
24599
|
runtime.client = client;
|
|
24571
24600
|
await this.refreshTools(runtime, connectTimeoutFor(definition));
|
|
24572
24601
|
return runtime;
|
|
@@ -24576,6 +24605,7 @@ var McpServerManager = class {
|
|
|
24576
24605
|
}
|
|
24577
24606
|
})().finally(() => {
|
|
24578
24607
|
runtime.connecting = void 0;
|
|
24608
|
+
runtime.transport = void 0;
|
|
24579
24609
|
});
|
|
24580
24610
|
try {
|
|
24581
24611
|
return await runtime.connecting;
|
|
@@ -24603,13 +24633,11 @@ var McpServerManager = class {
|
|
|
24603
24633
|
}
|
|
24604
24634
|
seen.add(tool.prefixedName);
|
|
24605
24635
|
}
|
|
24606
|
-
for (const
|
|
24636
|
+
for (const otherRuntime of this.runtimes.values()) {
|
|
24607
24637
|
if (otherRuntime === runtime || !otherRuntime.tools) continue;
|
|
24608
24638
|
for (const otherTool of otherRuntime.tools) {
|
|
24609
24639
|
if (seen.has(otherTool.prefixedName)) {
|
|
24610
|
-
|
|
24611
|
-
`tool name collision between "${runtime.definition.name}" and "${otherKey}": ${otherTool.prefixedName}`
|
|
24612
|
-
);
|
|
24640
|
+
this.log(`mcp: tool name "${otherTool.prefixedName}" is claimed by more than one server \u2014 disabled until the servers are renamed (fail-closed)`);
|
|
24613
24641
|
}
|
|
24614
24642
|
}
|
|
24615
24643
|
}
|
|
@@ -24618,16 +24646,45 @@ var McpServerManager = class {
|
|
|
24618
24646
|
}
|
|
24619
24647
|
persistMetadata() {
|
|
24620
24648
|
try {
|
|
24621
|
-
|
|
24649
|
+
fs2.mkdirSync(this.cacheDir, { recursive: true });
|
|
24622
24650
|
for (const [key, runtime] of this.runtimes) {
|
|
24623
|
-
if (runtime.tools)
|
|
24651
|
+
if (!runtime.tools) continue;
|
|
24652
|
+
const shardPath = path2.join(this.cacheDir, `${key}.json`);
|
|
24653
|
+
const tempPath = `${shardPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
24654
|
+
fs2.writeFileSync(tempPath, JSON.stringify({ updatedAt: this.now(), tools: runtime.tools }, null, 2), "utf8");
|
|
24655
|
+
try {
|
|
24656
|
+
fs2.renameSync(tempPath, shardPath);
|
|
24657
|
+
} catch (error2) {
|
|
24658
|
+
fs2.unlinkSync(tempPath);
|
|
24659
|
+
throw error2;
|
|
24660
|
+
}
|
|
24624
24661
|
}
|
|
24625
|
-
|
|
24626
|
-
fs2.writeFileSync(this.cachePath, JSON.stringify({ updatedAt: Date.now(), tools: snapshot }, null, 2), "utf8");
|
|
24662
|
+
this.deleteExpiredShards();
|
|
24627
24663
|
} catch (error2) {
|
|
24628
24664
|
this.log(`mcp: metadata cache write failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
24629
24665
|
}
|
|
24630
24666
|
}
|
|
24667
|
+
/** Delete shard files older than METADATA_TTL_MS (stale entries of offline servers age out). */
|
|
24668
|
+
deleteExpiredShards() {
|
|
24669
|
+
let names;
|
|
24670
|
+
try {
|
|
24671
|
+
names = fs2.readdirSync(this.cacheDir);
|
|
24672
|
+
} catch {
|
|
24673
|
+
return;
|
|
24674
|
+
}
|
|
24675
|
+
for (const name of names) {
|
|
24676
|
+
if (!name.endsWith(".json")) continue;
|
|
24677
|
+
const filePath = path2.join(this.cacheDir, name);
|
|
24678
|
+
try {
|
|
24679
|
+
const mtimeMs = fs2.statSync(filePath).mtimeMs;
|
|
24680
|
+
const parsed = JSON.parse(fs2.readFileSync(filePath, "utf8"));
|
|
24681
|
+
if (typeof parsed.updatedAt !== "number" || this.now() - parsed.updatedAt > METADATA_TTL_MS) {
|
|
24682
|
+
if (fs2.statSync(filePath).mtimeMs === mtimeMs) fs2.unlinkSync(filePath);
|
|
24683
|
+
}
|
|
24684
|
+
} catch {
|
|
24685
|
+
}
|
|
24686
|
+
}
|
|
24687
|
+
}
|
|
24631
24688
|
/** Cached descriptors for one server (from the metadata cache file). */
|
|
24632
24689
|
cachedToolsFor(server) {
|
|
24633
24690
|
return this.loadCatalog().filter((tool) => tool.server === sanitizeName(server));
|
|
@@ -24635,47 +24692,98 @@ var McpServerManager = class {
|
|
|
24635
24692
|
/** Metadata catalog: live runtimes plus the last persisted snapshot. */
|
|
24636
24693
|
loadCatalog() {
|
|
24637
24694
|
const catalog = /* @__PURE__ */ new Map();
|
|
24638
|
-
const
|
|
24639
|
-
|
|
24695
|
+
const ownerOf = /* @__PURE__ */ new Map();
|
|
24696
|
+
const conflicts = /* @__PURE__ */ new Set();
|
|
24697
|
+
const claim2 = (tool, live) => {
|
|
24698
|
+
const owner = ownerOf.get(tool.prefixedName);
|
|
24699
|
+
if (owner !== void 0 && owner !== tool.server) {
|
|
24700
|
+
conflicts.add(tool.prefixedName);
|
|
24701
|
+
return;
|
|
24702
|
+
}
|
|
24703
|
+
if (owner !== void 0 && !live) return;
|
|
24704
|
+
catalog.set(tool.prefixedName, tool);
|
|
24705
|
+
ownerOf.set(tool.prefixedName, tool.server);
|
|
24706
|
+
};
|
|
24707
|
+
for (const tools of Object.values(this.readCacheFile())) {
|
|
24640
24708
|
for (const tool of tools) {
|
|
24641
24709
|
if (!this.definitions.has(tool.server)) continue;
|
|
24642
|
-
|
|
24643
|
-
this.log(`mcp: metadata cache collision on ${tool.prefixedName} (server "${tool.server}", first seen from another server) \u2014 keeping the first entry`);
|
|
24644
|
-
continue;
|
|
24645
|
-
}
|
|
24646
|
-
catalog.set(tool.prefixedName, tool);
|
|
24710
|
+
claim2(tool, false);
|
|
24647
24711
|
}
|
|
24648
24712
|
}
|
|
24649
24713
|
for (const runtime of this.runtimes.values()) {
|
|
24650
|
-
for (const tool of runtime.tools ?? [])
|
|
24714
|
+
for (const tool of runtime.tools ?? []) claim2(tool, true);
|
|
24715
|
+
}
|
|
24716
|
+
for (const name of conflicts) {
|
|
24717
|
+
catalog.delete(name);
|
|
24718
|
+
this.log(`mcp: tool name "${name}" is claimed by more than one server \u2014 disabled until the servers are renamed (fail-closed)`);
|
|
24651
24719
|
}
|
|
24720
|
+
this.conflictedNames = conflicts;
|
|
24652
24721
|
return [...catalog.values()].sort((a, b) => a.prefixedName.localeCompare(b.prefixedName));
|
|
24653
24722
|
}
|
|
24654
|
-
|
|
24723
|
+
cacheSignature;
|
|
24655
24724
|
cacheSnapshot = {};
|
|
24656
|
-
|
|
24725
|
+
cacheUpdatedAt = {};
|
|
24726
|
+
pruneExpiredSnapshot() {
|
|
24727
|
+
for (const [key, updatedAt] of Object.entries(this.cacheUpdatedAt)) {
|
|
24728
|
+
if (this.now() - updatedAt > METADATA_TTL_MS) {
|
|
24729
|
+
delete this.cacheSnapshot[key];
|
|
24730
|
+
delete this.cacheUpdatedAt[key];
|
|
24731
|
+
}
|
|
24732
|
+
}
|
|
24733
|
+
return this.cacheSnapshot;
|
|
24734
|
+
}
|
|
24735
|
+
/**
|
|
24736
|
+
* Read the sharded metadata cache (<cacheDir>/<server>.json), merging every
|
|
24737
|
+
* shard's tools by server key (the file name is the sanitized server key).
|
|
24738
|
+
* Re-reads only when the signature changed; the signature must cover each
|
|
24739
|
+
* shard's name + mtime — the directory's own mtime is NOT enough, because a
|
|
24740
|
+
* rename replacing an existing shard leaves it unchanged.
|
|
24741
|
+
*/
|
|
24657
24742
|
readCacheFile() {
|
|
24658
|
-
let
|
|
24743
|
+
let names;
|
|
24659
24744
|
try {
|
|
24660
|
-
|
|
24745
|
+
names = fs2.readdirSync(this.cacheDir).sort();
|
|
24661
24746
|
} catch {
|
|
24662
|
-
this.
|
|
24747
|
+
this.cacheSignature = void 0;
|
|
24663
24748
|
this.cacheSnapshot = {};
|
|
24749
|
+
this.cacheUpdatedAt = {};
|
|
24664
24750
|
return this.cacheSnapshot;
|
|
24665
24751
|
}
|
|
24666
|
-
|
|
24667
|
-
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24752
|
+
let signature = "";
|
|
24753
|
+
for (const name of names) {
|
|
24754
|
+
try {
|
|
24755
|
+
signature += `${name}:${fs2.statSync(path2.join(this.cacheDir, name)).mtimeMs};`;
|
|
24756
|
+
} catch {
|
|
24757
|
+
}
|
|
24758
|
+
}
|
|
24759
|
+
if (signature === this.cacheSignature) return this.pruneExpiredSnapshot();
|
|
24760
|
+
const snapshot = {};
|
|
24761
|
+
const updatedAt = {};
|
|
24762
|
+
for (const name of names) {
|
|
24763
|
+
if (!name.endsWith(".json")) continue;
|
|
24764
|
+
try {
|
|
24765
|
+
const parsed = JSON.parse(fs2.readFileSync(path2.join(this.cacheDir, name), "utf8"));
|
|
24766
|
+
if (typeof parsed.updatedAt !== "number" || this.now() - parsed.updatedAt > METADATA_TTL_MS) continue;
|
|
24767
|
+
if (!Array.isArray(parsed.tools)) continue;
|
|
24768
|
+
const key = name.slice(0, -".json".length);
|
|
24769
|
+
snapshot[key] = parsed.tools;
|
|
24770
|
+
updatedAt[key] = parsed.updatedAt;
|
|
24771
|
+
} catch {
|
|
24772
|
+
}
|
|
24674
24773
|
}
|
|
24774
|
+
this.cacheSnapshot = snapshot;
|
|
24775
|
+
this.cacheUpdatedAt = updatedAt;
|
|
24776
|
+
this.cacheSignature = signature;
|
|
24675
24777
|
return this.cacheSnapshot;
|
|
24676
24778
|
}
|
|
24677
24779
|
async callTool(prefixedName, args, signal) {
|
|
24678
24780
|
const catalog = this.loadCatalog();
|
|
24781
|
+
if (this.conflictedNames.has(prefixedName)) {
|
|
24782
|
+
return {
|
|
24783
|
+
delivery: "not_sent",
|
|
24784
|
+
error: `MCP tool name "${prefixedName}" is claimed by more than one configured server. Rename one of the servers so every tool name is unambiguous; the call was NOT sent.`
|
|
24785
|
+
};
|
|
24786
|
+
}
|
|
24679
24787
|
const descriptor = catalog.find((tool) => tool.prefixedName === prefixedName);
|
|
24680
24788
|
let server = descriptor?.server;
|
|
24681
24789
|
let toolName = descriptor?.tool;
|
|
@@ -24746,12 +24854,17 @@ ${tail}` : "")
|
|
|
24746
24854
|
}
|
|
24747
24855
|
/** Close every client and reap every child process. */
|
|
24748
24856
|
async dispose() {
|
|
24857
|
+
this.disposed = true;
|
|
24749
24858
|
for (const runtime of this.runtimes.values()) {
|
|
24750
24859
|
try {
|
|
24751
24860
|
await runtime.client?.close();
|
|
24752
24861
|
} catch {
|
|
24753
24862
|
}
|
|
24863
|
+
if (!runtime.client && runtime.transport) {
|
|
24864
|
+
await runtime.transport.close().catch(() => void 0);
|
|
24865
|
+
}
|
|
24754
24866
|
runtime.client = void 0;
|
|
24867
|
+
runtime.transport = void 0;
|
|
24755
24868
|
}
|
|
24756
24869
|
this.runtimes.clear();
|
|
24757
24870
|
}
|
|
@@ -24820,10 +24933,14 @@ var DEFAULT_OUTPUT_MAX_BYTES = 8192;
|
|
|
24820
24933
|
var SEARCH_RESULT_LIMIT = 20;
|
|
24821
24934
|
function truncate(text, maxBytes) {
|
|
24822
24935
|
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
24823
|
-
const
|
|
24824
|
-
return `${cut}
|
|
24936
|
+
const notice = `
|
|
24825
24937
|
|
|
24826
24938
|
\u2026[truncated: result exceeded ${maxBytes} bytes. Narrow the call arguments or read the resource directly.]`;
|
|
24939
|
+
const noticeBytes = Buffer.byteLength(notice, "utf8");
|
|
24940
|
+
const bodyBudget = Math.max(1, maxBytes - noticeBytes);
|
|
24941
|
+
let cut = Buffer.from(text, "utf8").subarray(0, bodyBudget).toString("utf8");
|
|
24942
|
+
if (cut.endsWith("\uFFFD")) cut = cut.slice(0, -1);
|
|
24943
|
+
return `${cut}${notice}`;
|
|
24827
24944
|
}
|
|
24828
24945
|
function summarizeParams(schema) {
|
|
24829
24946
|
if (!schema || typeof schema !== "object") return "";
|
|
@@ -24915,7 +25032,7 @@ function createMcpProxyTool(getManager, ensureTrusted, options = {}) {
|
|
|
24915
25032
|
return {
|
|
24916
25033
|
name: "mcp",
|
|
24917
25034
|
label: "MCP",
|
|
24918
|
-
description: 'Call tools on configured MCP servers without loading their definitions. Discover: mcp({ search: "screenshot" }) lists matching <server>_<tool> names with compact parameter hints (cached metadata; servers do not start for search). Inspect: mcp({ tool: "<name>", describe: true }) returns the full input schema. Call: mcp({ tool: "<name>", args: { ... } }) \u2014 the server starts lazily on first use. args may be a JSON object or JSON string. If a result is marked [outcome unknown], the tool may have executed: do not retry automatically \u2014 verify state with a read-only call first.',
|
|
25035
|
+
description: 'Call tools on configured MCP servers without loading their definitions. Discover: mcp({ search: "screenshot" }) lists matching <server>_<tool> names with compact parameter hints (cached metadata; servers do not start for search). Inspect: mcp({ tool: "<name>", describe: true }) returns the full input schema. Call: mcp({ tool: "<name>", args: { ... } }) \u2014 the server starts lazily on first use. args may be a JSON object or JSON string. Tool lists are a snapshot taken when a server connects: tools added or removed by a running server only appear after /reload refreshes the session. If a result is marked [outcome unknown], the tool may have executed: do not retry automatically \u2014 verify state with a read-only call first.',
|
|
24919
25036
|
parameters: Type.Object({
|
|
24920
25037
|
search: Type.Optional(Type.String({ description: "Query text to discover MCP tools by name/description" })),
|
|
24921
25038
|
tool: Type.Optional(Type.String({ description: "Prefixed tool name <server>_<tool> to describe or call" })),
|
|
@@ -25115,10 +25232,10 @@ function formatServerForTrustPrompt(definition) {
|
|
|
25115
25232
|
function mcpAdapter(ctx) {
|
|
25116
25233
|
const configDir = resolveConfigDir();
|
|
25117
25234
|
const trustStore = new McpTrustStore(path4.join(configDir, "mcp-trust.json"));
|
|
25118
|
-
const
|
|
25235
|
+
const metadataCacheDir = path4.join(configDir, "mcp-metadata");
|
|
25119
25236
|
let manager = new McpServerManager(
|
|
25120
25237
|
resolveMcpAdapterConfig(ctx.cwd, configDir).servers,
|
|
25121
|
-
|
|
25238
|
+
metadataCacheDir,
|
|
25122
25239
|
ctx.log
|
|
25123
25240
|
);
|
|
25124
25241
|
async function reloadManager() {
|
|
@@ -25126,23 +25243,29 @@ function mcpAdapter(ctx) {
|
|
|
25126
25243
|
try {
|
|
25127
25244
|
manager = new McpServerManager(
|
|
25128
25245
|
resolveMcpAdapterConfig(ctx.cwd, configDir).servers,
|
|
25129
|
-
|
|
25246
|
+
metadataCacheDir,
|
|
25130
25247
|
ctx.log
|
|
25131
25248
|
);
|
|
25132
25249
|
} catch (error2) {
|
|
25133
25250
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
25134
25251
|
ctx.log(`mcp: config reload failed: ${message}`);
|
|
25135
25252
|
if (ctx.hasUI) ctx.ui.notify(`MCP adapter config error: ${message}`, "error");
|
|
25136
|
-
manager = new McpServerManager([],
|
|
25253
|
+
manager = new McpServerManager([], metadataCacheDir, ctx.log);
|
|
25137
25254
|
}
|
|
25138
25255
|
}
|
|
25139
|
-
|
|
25256
|
+
ctx.registerCleanup?.(() => manager.dispose().catch(() => void 0));
|
|
25257
|
+
function readProjectOverride() {
|
|
25140
25258
|
const filePath = path4.join(ctx.cwd, ".pie", "mcp-adapter.json");
|
|
25141
25259
|
let parsed = {};
|
|
25142
25260
|
try {
|
|
25143
25261
|
parsed = JSON.parse(fs4.readFileSync(filePath, "utf8"));
|
|
25144
25262
|
} catch {
|
|
25145
25263
|
}
|
|
25264
|
+
return parsed;
|
|
25265
|
+
}
|
|
25266
|
+
function writeProjectOverride(serverName, disabled) {
|
|
25267
|
+
const filePath = path4.join(ctx.cwd, ".pie", "mcp-adapter.json");
|
|
25268
|
+
const parsed = readProjectOverride();
|
|
25146
25269
|
const servers = parsed.mcpServers && typeof parsed.mcpServers === "object" && !Array.isArray(parsed.mcpServers) ? { ...parsed.mcpServers } : {};
|
|
25147
25270
|
if (disabled === null) {
|
|
25148
25271
|
delete servers[serverName];
|
|
@@ -25184,16 +25307,6 @@ function mcpAdapter(ctx) {
|
|
|
25184
25307
|
if (eagerRegistered.length > 0) {
|
|
25185
25308
|
ctx.log(`mcp: registered ${eagerRegistered.length} direct tool(s) from metadata cache: ${eagerRegistered.join(", ")}`);
|
|
25186
25309
|
}
|
|
25187
|
-
function unpinnedWarnings() {
|
|
25188
|
-
const warnings = [];
|
|
25189
|
-
for (const definition of manager.listDefinitions()) {
|
|
25190
|
-
const spec = [definition.command ?? "", ...definition.args].join(" ");
|
|
25191
|
-
if (/(^|\s)(npx|bunx|uvx)\s/.test(spec) && /@latest\b/.test(spec)) {
|
|
25192
|
-
warnings.push(`${definition.name}: unpinned "@latest" fetch \u2014 pin an exact version (e.g. pkg@1.2.3).`);
|
|
25193
|
-
}
|
|
25194
|
-
}
|
|
25195
|
-
return warnings;
|
|
25196
|
-
}
|
|
25197
25310
|
ctx.registerCommand({
|
|
25198
25311
|
name: "mcp",
|
|
25199
25312
|
description: "MCP adapter: status (default), enable/disable <server>",
|
|
@@ -25209,10 +25322,21 @@ function mcpAdapter(ctx) {
|
|
|
25209
25322
|
commandCtx.ui.notify(`Unknown MCP server "${target}". Run /mcp for the configured list.`, "warning");
|
|
25210
25323
|
return;
|
|
25211
25324
|
}
|
|
25212
|
-
const
|
|
25325
|
+
const overrides = readProjectOverride().mcpServers;
|
|
25326
|
+
const forceEnabled = overrides?.[definition.name]?.disabled === false;
|
|
25327
|
+
const filePath = writeProjectOverride(
|
|
25328
|
+
definition.name,
|
|
25329
|
+
// enable must lift a source-level disabled:true with an explicit
|
|
25330
|
+
// {disabled:false} force-override: deleting the override alone
|
|
25331
|
+
// would leave the source-level disable in force. Preserve an
|
|
25332
|
+
// existing force-enable on repeated enable commands too.
|
|
25333
|
+
sub === "disable" ? true : definition.disabled || forceEnabled ? false : null
|
|
25334
|
+
);
|
|
25213
25335
|
await reloadManager();
|
|
25336
|
+
const effectiveDisabled = manager.getDefinition(definition.name)?.disabled ?? false;
|
|
25337
|
+
const stateLabel = effectiveDisabled ? "disabled" : sub === "disable" ? "still enabled" : "enabled";
|
|
25214
25338
|
commandCtx.ui.notify(
|
|
25215
|
-
`MCP server "${definition.name}" ${
|
|
25339
|
+
`MCP server "${definition.name}" ${stateLabel} (wrote ${filePath}). ` + (eagerRegistered.length > 0 ? "Restart or /reload to resync direct tools." : "Effective immediately."),
|
|
25216
25340
|
"info"
|
|
25217
25341
|
);
|
|
25218
25342
|
return;
|
|
@@ -25234,15 +25358,15 @@ function mcpAdapter(ctx) {
|
|
|
25234
25358
|
lines.push(`- ${sanitizeName(definition.name)} [${manager.statusOf(definition.name)}, ${scopeLabel}] ${spec}`);
|
|
25235
25359
|
}
|
|
25236
25360
|
}
|
|
25237
|
-
const warnings = unpinnedWarnings();
|
|
25361
|
+
const warnings = unpinnedWarnings(manager.listDefinitions());
|
|
25238
25362
|
if (warnings.length > 0) lines.push("", "Warnings:", ...warnings.map((warning) => `- ${warning}`));
|
|
25239
25363
|
const config2 = resolveMcpAdapterConfig(commandCtx.cwd, configDir);
|
|
25240
25364
|
if (config2.problems.length > 0) lines.push("", "Config problems:", ...config2.problems.map((problem) => `- ${problem}`));
|
|
25241
25365
|
commandCtx.ui.notify(lines.join("\n"), "info");
|
|
25242
25366
|
}
|
|
25243
25367
|
});
|
|
25244
|
-
ctx.on("session:changed", () => {
|
|
25245
|
-
|
|
25368
|
+
ctx.on("session:changed", async () => {
|
|
25369
|
+
await reloadManager();
|
|
25246
25370
|
});
|
|
25247
25371
|
}
|
|
25248
25372
|
export {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isPlanModeSafeCommand,
|
|
8
8
|
markCompletedPlanSteps,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-UPPICLVS.js";
|
|
11
11
|
import "../../../chunks/chunk-QJH6YN2W.js";
|
|
12
12
|
import "../../../chunks/chunk-IM43UYVV.js";
|
|
13
13
|
import {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-5353DEEC.js";
|
|
4
|
+
} from "../../../chunks/chunk-P3QPJXS7.js";
|
|
6
5
|
import {
|
|
7
6
|
createSharedFileSystemTools,
|
|
8
7
|
createSubagentCapability
|
|
9
|
-
} from "../../../chunks/chunk-
|
|
8
|
+
} from "../../../chunks/chunk-UPPICLVS.js";
|
|
10
9
|
import "../../../chunks/chunk-QJH6YN2W.js";
|
|
11
10
|
import "../../../chunks/chunk-IM43UYVV.js";
|
|
11
|
+
import "../../../chunks/chunk-5353DEEC.js";
|
|
12
12
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
13
13
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
executeManageTodoList,
|
|
9
9
|
executionStateToTodos,
|
|
10
10
|
restoreExecutionState
|
|
11
|
-
} from "../../../chunks/chunk-
|
|
11
|
+
} from "../../../chunks/chunk-UPPICLVS.js";
|
|
12
12
|
import "../../../chunks/chunk-QJH6YN2W.js";
|
|
13
13
|
import "../../../chunks/chunk-IM43UYVV.js";
|
|
14
14
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
@@ -9999,6 +9999,16 @@ function validateRequest(value) {
|
|
|
9999
9999
|
string("command", true, 32768);
|
|
10000
10000
|
string("cwd");
|
|
10001
10001
|
string("label", false, 1024);
|
|
10002
|
+
if (r.env !== void 0) {
|
|
10003
|
+
if (!r.env || typeof r.env !== "object" || Array.isArray(r.env)) throw new Error("Invalid env: expected an object of string to string.");
|
|
10004
|
+
const entries = Object.entries(r.env);
|
|
10005
|
+
if (entries.length > 512) throw new Error("Invalid env: at most 512 variables.");
|
|
10006
|
+
for (const [k, v] of entries) {
|
|
10007
|
+
if (!k.length || k.length > 256 || k.includes("\0") || typeof v !== "string" || v.length > 65536 || v.includes("\0")) {
|
|
10008
|
+
throw new Error(`Invalid env entry ${JSON.stringify(k)}: expected string key and value without NUL bytes.`);
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
}
|
|
10002
10012
|
if (r.argv !== void 0 && (!Array.isArray(r.argv) || r.argv.length < 1 || r.argv.length > 256 || r.argv.some((a) => typeof a !== "string" || a.includes("\0") || a.length > 32768) || !r.argv[0])) throw new Error("Invalid argv: expected a non-empty array of executable and arguments.");
|
|
10003
10013
|
// Fall through to the shared PTY size limits.
|
|
10004
10014
|
case "resize":
|
|
@@ -10294,6 +10304,13 @@ var SUPPORTED_KEYS = Object.keys(KEY_MAP);
|
|
|
10294
10304
|
var import_node_fs2 = require("node:fs");
|
|
10295
10305
|
var import_node_path3 = __toESM(require("node:path"));
|
|
10296
10306
|
var program2 = new Command();
|
|
10307
|
+
function callerEnv() {
|
|
10308
|
+
const env = {};
|
|
10309
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
10310
|
+
if (typeof value === "string") env[key] = value;
|
|
10311
|
+
}
|
|
10312
|
+
return env;
|
|
10313
|
+
}
|
|
10297
10314
|
program2.name("tui-use").description("TUI automation for AI agents \u2014 like BrowserUse, but for the terminal").option("--session <id>", "Target this session explicitly (safe for concurrent tasks)").version(version);
|
|
10298
10315
|
program2.command("start [args...]").description("Start an interactive program in a PTY session").option("--cwd <dir>", "Working directory for the command").option("--label <name>", "Human-readable label for this session").option("--cols <n>", "Terminal width (default: 120)", "120").option("--rows <n>", "Terminal height (default: 30)", "30").option("--argv-stdin", "Read a JSON array of executable and arguments from UTF-8 stdin; bypass the shell").action(async (args, opts) => {
|
|
10299
10316
|
let argv;
|
|
@@ -10311,6 +10328,7 @@ program2.command("start [args...]").description("Start an interactive program in
|
|
|
10311
10328
|
type: "start",
|
|
10312
10329
|
command,
|
|
10313
10330
|
cwd: import_node_path3.default.resolve(opts.cwd ?? process.cwd()),
|
|
10331
|
+
env: callerEnv(),
|
|
10314
10332
|
label: opts.label,
|
|
10315
10333
|
cols: Number(opts.cols),
|
|
10316
10334
|
rows: Number(opts.rows),
|
|
@@ -7267,7 +7267,10 @@ var Session = class {
|
|
|
7267
7267
|
rows,
|
|
7268
7268
|
useConptyDll: process.platform === "win32",
|
|
7269
7269
|
cwd: options.cwd ?? process.cwd(),
|
|
7270
|
-
env:
|
|
7270
|
+
// The caller's env, not the daemon starter's: a persistent daemon
|
|
7271
|
+
// outlives many projects, and each session must see the environment
|
|
7272
|
+
// of the shell that started it (PATH setups, PIE_* overrides, ...).
|
|
7273
|
+
env: options.env ?? process.env
|
|
7271
7274
|
});
|
|
7272
7275
|
} catch (error) {
|
|
7273
7276
|
this.terminal.dispose();
|
|
@@ -7606,6 +7609,16 @@ function validateRequest(value) {
|
|
|
7606
7609
|
string("command", true, 32768);
|
|
7607
7610
|
string("cwd");
|
|
7608
7611
|
string("label", false, 1024);
|
|
7612
|
+
if (r.env !== void 0) {
|
|
7613
|
+
if (!r.env || typeof r.env !== "object" || Array.isArray(r.env)) throw new Error("Invalid env: expected an object of string to string.");
|
|
7614
|
+
const entries = Object.entries(r.env);
|
|
7615
|
+
if (entries.length > 512) throw new Error("Invalid env: at most 512 variables.");
|
|
7616
|
+
for (const [k, v] of entries) {
|
|
7617
|
+
if (!k.length || k.length > 256 || k.includes("\0") || typeof v !== "string" || v.length > 65536 || v.includes("\0")) {
|
|
7618
|
+
throw new Error(`Invalid env entry ${JSON.stringify(k)}: expected string key and value without NUL bytes.`);
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7609
7622
|
if (r.argv !== void 0 && (!Array.isArray(r.argv) || r.argv.length < 1 || r.argv.length > 256 || r.argv.some((a) => typeof a !== "string" || a.includes("\0") || a.length > 32768) || !r.argv[0])) throw new Error("Invalid argv: expected a non-empty array of executable and arguments.");
|
|
7610
7623
|
// Fall through to the shared PTY size limits.
|
|
7611
7624
|
case "resize":
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
getBuiltinDir,
|
|
4
|
-
getConfigDir
|
|
5
|
-
} from "./chunk-5353DEEC.js";
|
|
6
2
|
import {
|
|
7
3
|
classifyShellCommand,
|
|
8
4
|
createCodeIntelTool,
|
|
@@ -12,7 +8,11 @@ import {
|
|
|
12
8
|
createSharedWebSearchTool,
|
|
13
9
|
interpretShellExit,
|
|
14
10
|
requestInteraction
|
|
15
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UPPICLVS.js";
|
|
12
|
+
import {
|
|
13
|
+
getBuiltinDir,
|
|
14
|
+
getConfigDir
|
|
15
|
+
} from "./chunk-5353DEEC.js";
|
|
16
16
|
import {
|
|
17
17
|
Type
|
|
18
18
|
} from "./chunk-XXGYUYXI.js";
|
|
@@ -4856,7 +4856,7 @@ var AgentSessionRetryController = class {
|
|
|
4856
4856
|
}
|
|
4857
4857
|
return;
|
|
4858
4858
|
}
|
|
4859
|
-
const maxRetries = this.retry.maxRetries ?? 3;
|
|
4859
|
+
const maxRetries = this.retry.maxRetriesForFailure?.(failure) ?? this.retry.maxRetries ?? 3;
|
|
4860
4860
|
this.retryAttempt += 1;
|
|
4861
4861
|
if (this.retryAttempt > maxRetries) {
|
|
4862
4862
|
const terminalFailure = asTerminalFailure(failure);
|
|
@@ -4869,7 +4869,8 @@ var AgentSessionRetryController = class {
|
|
|
4869
4869
|
return;
|
|
4870
4870
|
}
|
|
4871
4871
|
const attempt = this.retryAttempt;
|
|
4872
|
-
const
|
|
4872
|
+
const rawDelayMs = (this.retry.baseDelayMs ?? 2e3) * 2 ** (attempt - 1);
|
|
4873
|
+
const delayMs = this.retry.maxDelayMs !== void 0 ? Math.min(rawDelayMs, this.retry.maxDelayMs) : rawDelayMs;
|
|
4873
4874
|
this.ensurePending();
|
|
4874
4875
|
this.setRecoveryState("retry_pending");
|
|
4875
4876
|
this.emit({ type: "turn_recovery_pending", recovery: "retry", failure, attempt, maxRetries, delayMs });
|
|
@@ -4917,7 +4918,7 @@ var AgentSessionRetryController = class {
|
|
|
4917
4918
|
userAbort: false,
|
|
4918
4919
|
terminal: true
|
|
4919
4920
|
});
|
|
4920
|
-
const maxRetries = this.retry?.maxRetries ?? 3;
|
|
4921
|
+
const maxRetries = (failure !== void 0 ? this.retry?.maxRetriesForFailure?.(failure) : void 0) ?? this.retry?.maxRetries ?? 3;
|
|
4921
4922
|
this.emit({ type: "retry_exhausted", errorMessage: terminalFailure.message, maxRetries });
|
|
4922
4923
|
this.emit({ type: "turn_failed_terminal", failure: terminalFailure });
|
|
4923
4924
|
this.emit({ type: "turn_settled" });
|
package/dist/cli.js
CHANGED
|
@@ -7,25 +7,7 @@ import {
|
|
|
7
7
|
createCliWebResearchHealthStore,
|
|
8
8
|
formatSize,
|
|
9
9
|
truncateHeadTail
|
|
10
|
-
} from "./chunks/chunk-
|
|
11
|
-
import {
|
|
12
|
-
VERSION,
|
|
13
|
-
getAgentDir,
|
|
14
|
-
getBuiltinDir,
|
|
15
|
-
getConfigDir,
|
|
16
|
-
getCustomThemesDir,
|
|
17
|
-
getLogsDir,
|
|
18
|
-
getPackageDir,
|
|
19
|
-
getProjectSettingsPath,
|
|
20
|
-
getRuntimeLogPath,
|
|
21
|
-
getSessionsDir,
|
|
22
|
-
getSettingsPath,
|
|
23
|
-
getThemesDir,
|
|
24
|
-
migrateConfigFromAgentDir
|
|
25
|
-
} from "./chunks/chunk-5353DEEC.js";
|
|
26
|
-
import {
|
|
27
|
-
resolveCliProjectRoot
|
|
28
|
-
} from "./chunks/chunk-WLFOBECN.js";
|
|
10
|
+
} from "./chunks/chunk-P3QPJXS7.js";
|
|
29
11
|
import {
|
|
30
12
|
AGENTS_CONTEXT_FILE_NAME,
|
|
31
13
|
AgentSessionController,
|
|
@@ -78,7 +60,7 @@ import {
|
|
|
78
60
|
setExtensionEnabledInSettings,
|
|
79
61
|
shouldPreserveExecutionStateForUserText,
|
|
80
62
|
supersedeExecutionState
|
|
81
|
-
} from "./chunks/chunk-
|
|
63
|
+
} from "./chunks/chunk-UPPICLVS.js";
|
|
82
64
|
import "./chunks/chunk-QJH6YN2W.js";
|
|
83
65
|
import {
|
|
84
66
|
Deref,
|
|
@@ -104,6 +86,24 @@ import {
|
|
|
104
86
|
sortToolModelCandidatesByCapability,
|
|
105
87
|
type_exports
|
|
106
88
|
} from "./chunks/chunk-IM43UYVV.js";
|
|
89
|
+
import {
|
|
90
|
+
VERSION,
|
|
91
|
+
getAgentDir,
|
|
92
|
+
getBuiltinDir,
|
|
93
|
+
getConfigDir,
|
|
94
|
+
getCustomThemesDir,
|
|
95
|
+
getLogsDir,
|
|
96
|
+
getPackageDir,
|
|
97
|
+
getProjectSettingsPath,
|
|
98
|
+
getRuntimeLogPath,
|
|
99
|
+
getSessionsDir,
|
|
100
|
+
getSettingsPath,
|
|
101
|
+
getThemesDir,
|
|
102
|
+
migrateConfigFromAgentDir
|
|
103
|
+
} from "./chunks/chunk-5353DEEC.js";
|
|
104
|
+
import {
|
|
105
|
+
resolveCliProjectRoot
|
|
106
|
+
} from "./chunks/chunk-WLFOBECN.js";
|
|
107
107
|
import {
|
|
108
108
|
IsArray,
|
|
109
109
|
IsBigInt,
|
|
@@ -77119,7 +77119,7 @@ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
|
|
|
77119
77119
|
}
|
|
77120
77120
|
|
|
77121
77121
|
// src/runtime/retry-policy.ts
|
|
77122
|
-
var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|socket hang up|request timeout|client network socket disconnected before secure TLS connection was established|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i;
|
|
77122
|
+
var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|(?:read\s+|write\s+)?EPIPE|socket hang up|request timeout|client network socket disconnected before secure TLS connection was established|certificate (?:has )?expired|unable to verify (?:the first certificate|certificate)|self[- ]signed cert|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i;
|
|
77123
77123
|
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/i;
|
|
77124
77124
|
var USER_ABORT_PATTERN = /request was aborted|local abort signal|user abort|user_interrupt|interrupted by user|esc pressed|ctrl\+c/i;
|
|
77125
77125
|
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;
|
|
@@ -77127,6 +77127,8 @@ var INVALID_FILE_REFERENCE_PATTERN = /not the owner of this file|owner of this f
|
|
|
77127
77127
|
var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schema|schema validation|invalid parameter|unsupported parameter|malformed/i;
|
|
77128
77128
|
var CLI_RETRY_MAX_RETRIES = 3;
|
|
77129
77129
|
var CLI_RETRY_BASE_DELAY_MS = 2e3;
|
|
77130
|
+
var CLI_TRANSIENT_NETWORK_MAX_RETRIES = 10;
|
|
77131
|
+
var CLI_RETRY_MAX_DELAY_MS = 6e4;
|
|
77130
77132
|
function isCliContextOverflowError(errorMessage4) {
|
|
77131
77133
|
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(
|
|
77132
77134
|
errorMessage4
|
|
@@ -77203,6 +77205,31 @@ function getCliRetryBaseDelayMs() {
|
|
|
77203
77205
|
const parsed = Number(override);
|
|
77204
77206
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_BASE_DELAY_MS;
|
|
77205
77207
|
}
|
|
77208
|
+
function getCliTransientNetworkMaxRetries() {
|
|
77209
|
+
const override = process.env.PIE_RETRY_TRANSIENT_NETWORK_MAX_RETRIES;
|
|
77210
|
+
if (!override) {
|
|
77211
|
+
return CLI_TRANSIENT_NETWORK_MAX_RETRIES;
|
|
77212
|
+
}
|
|
77213
|
+
const parsed = Number(override);
|
|
77214
|
+
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TRANSIENT_NETWORK_MAX_RETRIES;
|
|
77215
|
+
}
|
|
77216
|
+
function getCliRetryMaxDelayMs() {
|
|
77217
|
+
const override = process.env.PIE_RETRY_MAX_DELAY_MS;
|
|
77218
|
+
if (!override) {
|
|
77219
|
+
return CLI_RETRY_MAX_DELAY_MS;
|
|
77220
|
+
}
|
|
77221
|
+
const parsed = Number(override);
|
|
77222
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : CLI_RETRY_MAX_DELAY_MS;
|
|
77223
|
+
}
|
|
77224
|
+
function createCliRetryPolicy(isRetryableErrorOverride) {
|
|
77225
|
+
return {
|
|
77226
|
+
isRetryableError: isRetryableErrorOverride ?? isCliRetryableError,
|
|
77227
|
+
maxRetries: CLI_RETRY_MAX_RETRIES,
|
|
77228
|
+
baseDelayMs: getCliRetryBaseDelayMs(),
|
|
77229
|
+
maxDelayMs: getCliRetryMaxDelayMs(),
|
|
77230
|
+
maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : CLI_RETRY_MAX_RETRIES
|
|
77231
|
+
};
|
|
77232
|
+
}
|
|
77206
77233
|
|
|
77207
77234
|
// src/runtime/interactive-context-ui.ts
|
|
77208
77235
|
function calculateContextUsage(host) {
|
|
@@ -77921,11 +77948,9 @@ function createInteractiveAgentSessionController(host) {
|
|
|
77921
77948
|
},
|
|
77922
77949
|
getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
|
|
77923
77950
|
classifyFailure: (context) => classifyCliFailure(context),
|
|
77924
|
-
retry:
|
|
77925
|
-
|
|
77926
|
-
|
|
77927
|
-
baseDelayMs: getCliRetryBaseDelayMs()
|
|
77928
|
-
},
|
|
77951
|
+
retry: createCliRetryPolicy(
|
|
77952
|
+
(errorMessage4) => !host._isContextOverflowError(errorMessage4) && isCliRetryableError(errorMessage4)
|
|
77953
|
+
),
|
|
77929
77954
|
compaction: {
|
|
77930
77955
|
compact: async (context) => {
|
|
77931
77956
|
const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
|
|
@@ -79597,6 +79622,7 @@ var InteractiveMode = class {
|
|
|
79597
79622
|
await this.loadExtensions();
|
|
79598
79623
|
this.applyRuntimeToolMode();
|
|
79599
79624
|
this.noteRuntimeModeChanged("extension_reload");
|
|
79625
|
+
await this.syncSessionScopedState();
|
|
79600
79626
|
this.ui.requestRender();
|
|
79601
79627
|
}
|
|
79602
79628
|
calculateContextUsage() {
|
|
@@ -80152,6 +80178,24 @@ async function composeCliRuntime(params) {
|
|
|
80152
80178
|
};
|
|
80153
80179
|
}
|
|
80154
80180
|
|
|
80181
|
+
// src/app/print-mode-retry-status.ts
|
|
80182
|
+
function createPrintModeRetryStatusReporter() {
|
|
80183
|
+
let exhaustedRetryBudget;
|
|
80184
|
+
return (event) => {
|
|
80185
|
+
if (event.type === "turn_recovery_pending" && event.recovery === "retry") {
|
|
80186
|
+
console.error(`Transient request error: ${event.failure.message}. Retrying in ${(event.delayMs ?? 0) / 1e3}s (${event.attempt}/${event.maxRetries}).`);
|
|
80187
|
+
} else if (event.type === "turn_retry_started") {
|
|
80188
|
+
console.error(`Retrying request (${event.attempt}/${event.maxRetries})...`);
|
|
80189
|
+
} else if (event.type === "turn_retry_succeeded") {
|
|
80190
|
+
console.error(`Retry succeeded after ${event.attempts} attempt${event.attempts === 1 ? "" : "s"}.`);
|
|
80191
|
+
} else if (event.type === "retry_exhausted") {
|
|
80192
|
+
exhaustedRetryBudget = event.maxRetries;
|
|
80193
|
+
} else if (event.type === "turn_failed_terminal" && (event.failure.kind === "transient_network" || event.failure.kind === "provider_transient")) {
|
|
80194
|
+
console.error(`Request failed after ${exhaustedRetryBudget ?? CLI_RETRY_MAX_RETRIES} retries: ${event.failure.message}`);
|
|
80195
|
+
}
|
|
80196
|
+
};
|
|
80197
|
+
}
|
|
80198
|
+
|
|
80155
80199
|
// src/app/print-mode.ts
|
|
80156
80200
|
function createNoOpExtensionUIContext() {
|
|
80157
80201
|
return {
|
|
@@ -80329,6 +80373,7 @@ async function runPrintMode(params) {
|
|
|
80329
80373
|
streamFn: params.streamFn
|
|
80330
80374
|
});
|
|
80331
80375
|
const sessionTrace = new SessionTraceWriter(runtime.paths.sessionsDir, runtime.sessionManager);
|
|
80376
|
+
const reportRetryStatus = createPrintModeRetryStatusReporter();
|
|
80332
80377
|
const agentSessionController = new AgentSessionController({
|
|
80333
80378
|
agent,
|
|
80334
80379
|
sessionManager: runtime.sessionManager,
|
|
@@ -80349,11 +80394,7 @@ async function runPrintMode(params) {
|
|
|
80349
80394
|
messages: result.messages?.length ? [...context.messages, ...result.messages] : context.messages
|
|
80350
80395
|
};
|
|
80351
80396
|
},
|
|
80352
|
-
retry:
|
|
80353
|
-
isRetryableError: isCliRetryableError,
|
|
80354
|
-
maxRetries: CLI_RETRY_MAX_RETRIES,
|
|
80355
|
-
baseDelayMs: getCliRetryBaseDelayMs()
|
|
80356
|
-
},
|
|
80397
|
+
retry: createCliRetryPolicy(),
|
|
80357
80398
|
getAutoCompactDecision: ({ message, messages }) => createCliAutoCompactDecision({
|
|
80358
80399
|
message,
|
|
80359
80400
|
messages,
|
|
@@ -80388,20 +80429,14 @@ async function runPrintMode(params) {
|
|
|
80388
80429
|
classifyFailure: (context) => classifyCliFailure(context),
|
|
80389
80430
|
onEvent: (event) => {
|
|
80390
80431
|
sessionTrace.noteRuntimeEvent(event.type, event);
|
|
80391
|
-
if (event.type === "turn_recovery_pending" && event.recovery === "
|
|
80392
|
-
console.error(`Transient request error: ${event.failure.message}. Retrying in ${(event.delayMs ?? 0) / 1e3}s (${event.attempt}/${event.maxRetries}).`);
|
|
80393
|
-
} else if (event.type === "turn_recovery_pending" && event.recovery === "compaction") {
|
|
80432
|
+
if (event.type === "turn_recovery_pending" && event.recovery === "compaction") {
|
|
80394
80433
|
console.error(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
|
|
80395
80434
|
} else if (event.type === "auto_compaction_start") {
|
|
80396
80435
|
console.error(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
|
|
80397
80436
|
} else if (event.type === "auto_compaction_end" && !event.errorMessage) {
|
|
80398
80437
|
runtime.fileObservationState.clear();
|
|
80399
|
-
} else
|
|
80400
|
-
|
|
80401
|
-
} else if (event.type === "turn_retry_succeeded") {
|
|
80402
|
-
console.error(`Retry succeeded after ${event.attempts} attempt${event.attempts === 1 ? "" : "s"}.`);
|
|
80403
|
-
} else if (event.type === "turn_failed_terminal" && (event.failure.kind === "transient_network" || event.failure.kind === "provider_transient")) {
|
|
80404
|
-
console.error(`Request failed after ${CLI_RETRY_MAX_RETRIES} retries: ${event.failure.message}`);
|
|
80438
|
+
} else {
|
|
80439
|
+
reportRetryStatus(event);
|
|
80405
80440
|
}
|
|
80406
80441
|
}
|
|
80407
80442
|
});
|
|
@@ -81518,11 +81553,7 @@ async function runJsonEventsMode(params) {
|
|
|
81518
81553
|
messages: result?.messages?.length ? [...context.messages, ...result.messages] : context.messages
|
|
81519
81554
|
};
|
|
81520
81555
|
},
|
|
81521
|
-
retry:
|
|
81522
|
-
isRetryableError: isCliRetryableError,
|
|
81523
|
-
maxRetries: CLI_RETRY_MAX_RETRIES,
|
|
81524
|
-
baseDelayMs: getCliRetryBaseDelayMs()
|
|
81525
|
-
},
|
|
81556
|
+
retry: createCliRetryPolicy(),
|
|
81526
81557
|
getAutoCompactDecision: ({ message, messages }) => createCliAutoCompactDecision({
|
|
81527
81558
|
message,
|
|
81528
81559
|
messages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cydm/pie",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Pie AI Agent CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"pretest:offline": "npm run build --workspace @pie/agent-framework && npm run build --workspace @pie/shared-headless-capabilities && npm run build && npm run test:skill-regressions",
|
|
20
20
|
"test:skill-regressions": "node --test --import tsx test/bash-lifecycle.test.ts test/skill-creator-runner.test.ts test/tui-use-contract.test.ts test/tui-use-runtime.test.ts test/browser-runtime.test.ts test/computer-evidence.test.ts test/interactive-lifecycle.test.ts",
|
|
21
21
|
"posttest:offline": "node --test --import tsx test/remote-model-catalog.test.ts",
|
|
22
|
-
"test:offline": "node --test --test-concurrency=4 --import tsx test/builtin-extensions.test.ts test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-provider.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/hierarchical-command-system.test.ts test/mcp-adapter.test.ts test/extensions-enablement.test.ts test/extension-selector-shortcuts.test.ts test/skill-tool-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/code-intel-typescript-provider.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/plan-mode-extension.test.ts test/rendered-page-reader.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/bash-yolo.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/ctrl-d-exit.test.ts test/session-display-title.test.ts test/calm-mode.test.ts test/project-root.test.ts test/guard-advisory-context.test.ts test/clear-command-usage.test.ts test/daily-agent-smoke.terminal.test.ts test/slash-menu-toggles.terminal.test.ts test/cli-args.test.ts test/extensions-configured-paths.test.ts test/extensions-loader-path.test.ts test/file-picker.test.ts test/interrupt-recovery.test.ts test/mention-insert-regression.test.ts test/mention-picker-navigation.test.ts test/pie-unity-rpc-skill.test.ts test/skill-creator-scripts.test.ts",
|
|
22
|
+
"test:offline": "node --test --test-concurrency=4 --import tsx test/builtin-extensions.test.ts test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-provider.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/hierarchical-command-system.test.ts test/mcp-adapter.test.ts test/herdr-agent-state.test.ts test/extensions-enablement.test.ts test/extension-selector-shortcuts.test.ts test/skill-tool-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/code-intel-typescript-provider.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/plan-mode-extension.test.ts test/rendered-page-reader.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/bash-yolo.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/ctrl-d-exit.test.ts test/session-display-title.test.ts test/calm-mode.test.ts test/project-root.test.ts test/guard-advisory-context.test.ts test/clear-command-usage.test.ts test/daily-agent-smoke.terminal.test.ts test/slash-menu-toggles.terminal.test.ts test/cli-args.test.ts test/extensions-configured-paths.test.ts test/extensions-loader-path.test.ts test/file-picker.test.ts test/interrupt-recovery.test.ts test/mention-insert-regression.test.ts test/mention-picker-navigation.test.ts test/pie-unity-rpc-skill.test.ts test/skill-creator-scripts.test.ts",
|
|
23
23
|
"test:all": "npm run test:offline && npm run test:terminal:vitest",
|
|
24
24
|
"test:terminal:vitest": "vitest run test/mention-file-select.terminal.test.ts test/tool-partial-display.terminal.test.ts test/footer-plan-mode.test.ts test/viewport-position-final.test.ts test/resume-header.terminal.test.ts --no-file-parallelism",
|
|
25
25
|
"test:todo-tui": "node ../../scripts/run-with-env.mjs PIE_REAL_TODO_TUI=1 -- --test --import tsx test/todo-closure.terminal.test.ts",
|