@automatalabs/pi-acp 0.1.3 → 0.2.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/README.md +7 -3
- package/dist/agent.d.ts +11 -7
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +374 -60
- package/dist/child-process-registry.d.ts +82 -0
- package/dist/child-process-registry.d.ts.map +1 -0
- package/dist/child-process-registry.js +408 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +22 -6
- package/dist/deps.d.ts +3 -3
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +2 -2
- package/dist/errors.d.ts +17 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -5
- package/dist/index.js +5 -6
- package/dist/mcp-bridge.d.ts +235 -17
- package/dist/mcp-bridge.d.ts.map +1 -1
- package/dist/mcp-bridge.js +1331 -144
- package/dist/mcp-sampling-payload.d.ts +25 -0
- package/dist/mcp-sampling-payload.d.ts.map +1 -0
- package/dist/mcp-sampling-payload.js +263 -0
- package/dist/session.d.ts +27 -9
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +301 -145
- package/dist/translate.d.ts.map +1 -1
- package/dist/translate.js +10 -6
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/package.json +1 -1
- package/dist/structured-output.d.ts +0 -14
- package/dist/structured-output.d.ts.map +0 -1
- package/dist/structured-output.js +0 -71
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import type { BashOperations } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { PiAcpDeps } from "./deps.js";
|
|
4
|
+
interface ChildRecord {
|
|
5
|
+
pid: number;
|
|
6
|
+
pgid?: number;
|
|
7
|
+
child: ChildProcess;
|
|
8
|
+
leaderClosed: Promise<void>;
|
|
9
|
+
termination?: Promise<void>;
|
|
10
|
+
terminationSettled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ChildProcessRegistryDeps extends Pick<PiAcpDeps, "graceMs" | "sleep"> {
|
|
13
|
+
platform?: NodeJS.Platform;
|
|
14
|
+
processGroupState?(pgid: number): "alive" | "gone" | "error";
|
|
15
|
+
killProcessGroup?(pgid: number): void;
|
|
16
|
+
taskkillTree?(pid: number, signal: AbortSignal): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface SpawnLease {
|
|
19
|
+
register(child: ChildProcess): ChildRecord;
|
|
20
|
+
failed(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class ChildCleanupFailure extends Error {
|
|
23
|
+
readonly remainingChildren: number;
|
|
24
|
+
constructor(remainingChildren: number);
|
|
25
|
+
}
|
|
26
|
+
export declare function isTaskkillAlreadyGone(output: string): boolean;
|
|
27
|
+
/** One monotonic ownership epoch. A failed drain remains closing and is retryable. */
|
|
28
|
+
export declare class ChildProcessRegistry {
|
|
29
|
+
private readonly deps;
|
|
30
|
+
private state;
|
|
31
|
+
private readonly children;
|
|
32
|
+
private pendingSpawns;
|
|
33
|
+
private drain;
|
|
34
|
+
private generationSignal;
|
|
35
|
+
private changeWaiters;
|
|
36
|
+
constructor(deps: ChildProcessRegistryDeps);
|
|
37
|
+
private get platform();
|
|
38
|
+
get remainingChildren(): number;
|
|
39
|
+
get leaderPids(): readonly number[];
|
|
40
|
+
beginSpawn(): SpawnLease;
|
|
41
|
+
private registerChild;
|
|
42
|
+
/** Natural operation completion never proves away a surviving Unix process group. */
|
|
43
|
+
complete(record: ChildRecord): void;
|
|
44
|
+
/** Terminate one timed-out/aborted tool tree without closing admission for unrelated tools. */
|
|
45
|
+
terminateOne(record: ChildRecord): Promise<void>;
|
|
46
|
+
terminateAll(deadlineSignal: AbortSignal): Promise<void>;
|
|
47
|
+
private drainGeneration;
|
|
48
|
+
private groupState;
|
|
49
|
+
private startTermination;
|
|
50
|
+
private terminateRecord;
|
|
51
|
+
private changed;
|
|
52
|
+
private waitForChange;
|
|
53
|
+
}
|
|
54
|
+
export declare class ChildProcessRegistrySlot {
|
|
55
|
+
private readonly deps;
|
|
56
|
+
private epoch;
|
|
57
|
+
private cleanupFailed;
|
|
58
|
+
constructor(deps: ChildProcessRegistryDeps);
|
|
59
|
+
get registry(): ChildProcessRegistry;
|
|
60
|
+
get childCleanupFailed(): boolean;
|
|
61
|
+
get remainingChildren(): number;
|
|
62
|
+
beginSpawn(): SpawnLease;
|
|
63
|
+
latchFailure(): void;
|
|
64
|
+
clearFailure(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Close admission on the current epoch synchronously and return that exact
|
|
67
|
+
* epoch with its drain. Rotation is deliberately a separate commit: a
|
|
68
|
+
* cancel generation must not publish a fresh spawn epoch until both Pi and
|
|
69
|
+
* this captured registry are idle.
|
|
70
|
+
*/
|
|
71
|
+
closeEpoch(deadlineSignal: AbortSignal): {
|
|
72
|
+
epoch: ChildProcessRegistry;
|
|
73
|
+
drain: Promise<void>;
|
|
74
|
+
};
|
|
75
|
+
commitRotation(captured: ChildProcessRegistry): void;
|
|
76
|
+
/** Compatibility seam for focused registry tests; production uses the
|
|
77
|
+
* explicit closeEpoch/commitRotation transaction above. */
|
|
78
|
+
terminateAll(shouldRotate: () => boolean, deadlineSignal: AbortSignal): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
export declare function createTrackedBashOperations(slot: ChildProcessRegistrySlot, shellPath: string | undefined, deps: Pick<PiAcpDeps, "graceMs" | "sleep">, onCleanupFailure: () => void): BashOperations;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=child-process-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child-process-registry.d.ts","sourceRoot":"","sources":["../src/child-process-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
export class ChildCleanupFailure extends Error {
|
|
6
|
+
remainingChildren;
|
|
7
|
+
constructor(remainingChildren) {
|
|
8
|
+
super("child process cleanup failed");
|
|
9
|
+
this.remainingChildren = remainingChildren;
|
|
10
|
+
this.name = "ChildCleanupFailure";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function isTaskkillAlreadyGone(output) {
|
|
14
|
+
return /ERROR:\s+The process "[^"]+" not found\./i.test(output)
|
|
15
|
+
|| /Reason:\s+There is no running instance of the task\./i.test(output);
|
|
16
|
+
}
|
|
17
|
+
/** One monotonic ownership epoch. A failed drain remains closing and is retryable. */
|
|
18
|
+
export class ChildProcessRegistry {
|
|
19
|
+
deps;
|
|
20
|
+
state = "open";
|
|
21
|
+
children = new Map();
|
|
22
|
+
pendingSpawns = 0;
|
|
23
|
+
drain;
|
|
24
|
+
generationSignal;
|
|
25
|
+
changeWaiters = new Set();
|
|
26
|
+
constructor(deps) {
|
|
27
|
+
this.deps = deps;
|
|
28
|
+
}
|
|
29
|
+
get platform() { return this.deps.platform ?? process.platform; }
|
|
30
|
+
get remainingChildren() { return this.children.size; }
|
|
31
|
+
get leaderPids() { return [...this.children.keys()]; }
|
|
32
|
+
beginSpawn() {
|
|
33
|
+
if (this.state !== "open")
|
|
34
|
+
throw new Error("aborted");
|
|
35
|
+
this.pendingSpawns += 1;
|
|
36
|
+
let settled = false;
|
|
37
|
+
return {
|
|
38
|
+
failed: () => {
|
|
39
|
+
if (settled)
|
|
40
|
+
return;
|
|
41
|
+
settled = true;
|
|
42
|
+
this.pendingSpawns -= 1;
|
|
43
|
+
this.changed();
|
|
44
|
+
},
|
|
45
|
+
register: (child) => {
|
|
46
|
+
if (settled)
|
|
47
|
+
throw new Error("spawn lease already settled");
|
|
48
|
+
settled = true;
|
|
49
|
+
this.pendingSpawns -= 1;
|
|
50
|
+
const record = this.registerChild(child);
|
|
51
|
+
if (this.state !== "open") {
|
|
52
|
+
const signal = this.generationSignal;
|
|
53
|
+
if (signal)
|
|
54
|
+
this.startTermination(record, signal);
|
|
55
|
+
}
|
|
56
|
+
this.changed();
|
|
57
|
+
return record;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
registerChild(child) {
|
|
62
|
+
if (!child.pid)
|
|
63
|
+
throw new Error("bash child spawned without pid");
|
|
64
|
+
let closeError;
|
|
65
|
+
const leaderClosed = new Promise((resolve, reject) => {
|
|
66
|
+
child.once("error", (error) => {
|
|
67
|
+
closeError = error;
|
|
68
|
+
reject(error);
|
|
69
|
+
});
|
|
70
|
+
child.once("close", () => closeError === undefined ? resolve() : reject(closeError));
|
|
71
|
+
});
|
|
72
|
+
// A rejected leader promise is always observed by normal execution or cleanup.
|
|
73
|
+
leaderClosed.catch(() => undefined);
|
|
74
|
+
const record = {
|
|
75
|
+
pid: child.pid,
|
|
76
|
+
pgid: this.platform === "win32" ? undefined : child.pid,
|
|
77
|
+
child,
|
|
78
|
+
leaderClosed,
|
|
79
|
+
};
|
|
80
|
+
this.children.set(record.pid, record);
|
|
81
|
+
return record;
|
|
82
|
+
}
|
|
83
|
+
/** Natural operation completion never proves away a surviving Unix process group. */
|
|
84
|
+
complete(record) {
|
|
85
|
+
if (this.children.get(record.pid) !== record)
|
|
86
|
+
return;
|
|
87
|
+
// A Windows leader close is not descendant-disappearance proof. Keep the
|
|
88
|
+
// ownership record until terminateRecord observes successful taskkill /T
|
|
89
|
+
// /F as well as leader close. Unix may discharge ownership with ESRCH for
|
|
90
|
+
// the process group plus the already-observed leader completion.
|
|
91
|
+
if (this.platform !== "win32" && this.groupState(record.pgid ?? record.pid) === "gone") {
|
|
92
|
+
this.children.delete(record.pid);
|
|
93
|
+
this.changed();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Terminate one timed-out/aborted tool tree without closing admission for unrelated tools. */
|
|
97
|
+
async terminateOne(record) {
|
|
98
|
+
if (this.children.get(record.pid) !== record)
|
|
99
|
+
return;
|
|
100
|
+
const deadline = new AbortController();
|
|
101
|
+
const timer = new AbortController();
|
|
102
|
+
const expiry = this.deps.sleep(this.deps.graceMs, timer.signal).then(() => {
|
|
103
|
+
deadline.abort(new ChildCleanupFailure(this.children.size));
|
|
104
|
+
throw new ChildCleanupFailure(this.children.size);
|
|
105
|
+
});
|
|
106
|
+
expiry.catch(() => undefined);
|
|
107
|
+
try {
|
|
108
|
+
await Promise.race([this.startTermination(record, deadline.signal), expiry]);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
timer.abort();
|
|
112
|
+
if (record.termination) {
|
|
113
|
+
await record.termination.catch(() => undefined);
|
|
114
|
+
record.termination = undefined;
|
|
115
|
+
record.terminationSettled = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
terminateAll(deadlineSignal) {
|
|
120
|
+
if (this.drain)
|
|
121
|
+
return this.drain;
|
|
122
|
+
if (this.state === "closed" && this.children.size === 0 && this.pendingSpawns === 0) {
|
|
123
|
+
return Promise.resolve();
|
|
124
|
+
}
|
|
125
|
+
this.state = "closing";
|
|
126
|
+
this.generationSignal = deadlineSignal;
|
|
127
|
+
for (const record of this.children.values()) {
|
|
128
|
+
record.termination = undefined;
|
|
129
|
+
record.terminationSettled = false;
|
|
130
|
+
this.startTermination(record, deadlineSignal);
|
|
131
|
+
}
|
|
132
|
+
this.drain = this.drainGeneration(deadlineSignal)
|
|
133
|
+
.then(() => { this.generationSignal = undefined; })
|
|
134
|
+
.finally(() => { this.drain = undefined; });
|
|
135
|
+
return this.drain;
|
|
136
|
+
}
|
|
137
|
+
async drainGeneration(deadlineSignal) {
|
|
138
|
+
while (true) {
|
|
139
|
+
for (const record of this.children.values())
|
|
140
|
+
this.startTermination(record, deadlineSignal);
|
|
141
|
+
if (this.pendingSpawns === 0 && this.children.size === 0) {
|
|
142
|
+
this.state = "closed";
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (deadlineSignal.aborted)
|
|
146
|
+
throw new ChildCleanupFailure(this.children.size);
|
|
147
|
+
const terminations = [...this.children.values()]
|
|
148
|
+
.flatMap((record) => record.termination ? [record.termination] : []);
|
|
149
|
+
if (this.pendingSpawns === 0 && terminations.length > 0) {
|
|
150
|
+
await Promise.allSettled(terminations);
|
|
151
|
+
if (this.children.size > 0) {
|
|
152
|
+
if (deadlineSignal.aborted)
|
|
153
|
+
throw new ChildCleanupFailure(this.children.size);
|
|
154
|
+
// Every admitted record was attempted and at least one proof failed.
|
|
155
|
+
const allSettled = [...this.children.values()].every((record) => record.terminationSettled);
|
|
156
|
+
if (allSettled)
|
|
157
|
+
throw new ChildCleanupFailure(this.children.size);
|
|
158
|
+
}
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
await Promise.race([this.waitForChange(), abortPromise(deadlineSignal)]);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new ChildCleanupFailure(this.children.size);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
groupState(pgid) {
|
|
170
|
+
if (this.deps.processGroupState)
|
|
171
|
+
return this.deps.processGroupState(pgid);
|
|
172
|
+
try {
|
|
173
|
+
process.kill(-pgid, 0);
|
|
174
|
+
return "alive";
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const code = error.code;
|
|
178
|
+
if (code === "ESRCH")
|
|
179
|
+
return "gone";
|
|
180
|
+
if (code === "EPERM")
|
|
181
|
+
return "alive";
|
|
182
|
+
return "error";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
startTermination(record, deadlineSignal) {
|
|
186
|
+
if (record.termination)
|
|
187
|
+
return record.termination;
|
|
188
|
+
record.terminationSettled = false;
|
|
189
|
+
record.termination = this.terminateRecord(record, deadlineSignal)
|
|
190
|
+
.finally(() => { record.terminationSettled = true; });
|
|
191
|
+
record.termination.catch(() => undefined);
|
|
192
|
+
return record.termination;
|
|
193
|
+
}
|
|
194
|
+
async terminateRecord(record, deadlineSignal) {
|
|
195
|
+
if (this.platform === "win32") {
|
|
196
|
+
const taskkill = this.deps.taskkillTree
|
|
197
|
+
? this.deps.taskkillTree(record.pid, deadlineSignal)
|
|
198
|
+
: new Promise((resolve, reject) => {
|
|
199
|
+
const killer = spawn("taskkill", ["/PID", String(record.pid), "/T", "/F"], { windowsHide: true });
|
|
200
|
+
let output = "";
|
|
201
|
+
killer.stdout?.setEncoding("utf8");
|
|
202
|
+
killer.stderr?.setEncoding("utf8");
|
|
203
|
+
killer.stdout?.on("data", (chunk) => { output += chunk; });
|
|
204
|
+
killer.stderr?.on("data", (chunk) => { output += chunk; });
|
|
205
|
+
killer.once("error", reject);
|
|
206
|
+
killer.once("close", (code) => code === 0 || isTaskkillAlreadyGone(output)
|
|
207
|
+
? resolve()
|
|
208
|
+
: reject(new Error(`taskkill exited ${code}`)));
|
|
209
|
+
});
|
|
210
|
+
try {
|
|
211
|
+
await raceAbort(taskkill, deadlineSignal);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error.code !== "ESRCH")
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
if (this.deps.killProcessGroup) {
|
|
220
|
+
this.deps.killProcessGroup(record.pgid ?? record.pid);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
try {
|
|
224
|
+
process.kill(-(record.pgid ?? record.pid), "SIGKILL");
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
if (error.code !== "ESRCH")
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
await raceAbort(record.leaderClosed, deadlineSignal);
|
|
233
|
+
if (this.platform !== "win32") {
|
|
234
|
+
while (true) {
|
|
235
|
+
const state = this.groupState(record.pgid ?? record.pid);
|
|
236
|
+
if (state === "gone")
|
|
237
|
+
break;
|
|
238
|
+
if (state === "error")
|
|
239
|
+
throw new Error("child process-group probe failed");
|
|
240
|
+
await raceAbort(this.deps.sleep(10, deadlineSignal), deadlineSignal);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
this.children.delete(record.pid);
|
|
244
|
+
this.changed();
|
|
245
|
+
}
|
|
246
|
+
changed() {
|
|
247
|
+
const waiters = this.changeWaiters;
|
|
248
|
+
this.changeWaiters = new Set();
|
|
249
|
+
for (const resolve of waiters)
|
|
250
|
+
resolve();
|
|
251
|
+
}
|
|
252
|
+
waitForChange() {
|
|
253
|
+
return new Promise((resolve) => {
|
|
254
|
+
this.changeWaiters.add(resolve);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function abortPromise(signal) {
|
|
259
|
+
return new Promise((_, reject) => {
|
|
260
|
+
if (signal.aborted)
|
|
261
|
+
reject(signal.reason);
|
|
262
|
+
else
|
|
263
|
+
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
async function raceAbort(promise, signal) {
|
|
267
|
+
promise.catch(() => undefined);
|
|
268
|
+
return Promise.race([promise, abortPromise(signal)]);
|
|
269
|
+
}
|
|
270
|
+
export class ChildProcessRegistrySlot {
|
|
271
|
+
deps;
|
|
272
|
+
epoch;
|
|
273
|
+
cleanupFailed = false;
|
|
274
|
+
constructor(deps) {
|
|
275
|
+
this.deps = deps;
|
|
276
|
+
this.epoch = new ChildProcessRegistry(deps);
|
|
277
|
+
}
|
|
278
|
+
get registry() { return this.epoch; }
|
|
279
|
+
get childCleanupFailed() { return this.cleanupFailed; }
|
|
280
|
+
get remainingChildren() { return this.epoch.remainingChildren; }
|
|
281
|
+
beginSpawn() { return this.epoch.beginSpawn(); }
|
|
282
|
+
latchFailure() { this.cleanupFailed = true; }
|
|
283
|
+
clearFailure() { this.cleanupFailed = false; }
|
|
284
|
+
/**
|
|
285
|
+
* Close admission on the current epoch synchronously and return that exact
|
|
286
|
+
* epoch with its drain. Rotation is deliberately a separate commit: a
|
|
287
|
+
* cancel generation must not publish a fresh spawn epoch until both Pi and
|
|
288
|
+
* this captured registry are idle.
|
|
289
|
+
*/
|
|
290
|
+
closeEpoch(deadlineSignal) {
|
|
291
|
+
const captured = this.epoch;
|
|
292
|
+
return { epoch: captured, drain: captured.terminateAll(deadlineSignal) };
|
|
293
|
+
}
|
|
294
|
+
commitRotation(captured) {
|
|
295
|
+
if (this.epoch === captured) {
|
|
296
|
+
this.epoch = new ChildProcessRegistry(this.deps);
|
|
297
|
+
this.cleanupFailed = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/** Compatibility seam for focused registry tests; production uses the
|
|
301
|
+
* explicit closeEpoch/commitRotation transaction above. */
|
|
302
|
+
async terminateAll(shouldRotate, deadlineSignal) {
|
|
303
|
+
const { epoch, drain } = this.closeEpoch(deadlineSignal);
|
|
304
|
+
await drain;
|
|
305
|
+
if (shouldRotate())
|
|
306
|
+
this.commitRotation(epoch);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
class BashCleanupSentinel extends Error {
|
|
310
|
+
}
|
|
311
|
+
export function createTrackedBashOperations(slot, shellPath, deps, onCleanupFailure) {
|
|
312
|
+
return {
|
|
313
|
+
async exec(command, cwd, { onData, signal, timeout, env }) {
|
|
314
|
+
if (signal?.aborted)
|
|
315
|
+
throw new Error("aborted");
|
|
316
|
+
if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {
|
|
317
|
+
throw new Error("Invalid timeout: must be a finite number of seconds");
|
|
318
|
+
}
|
|
319
|
+
const timeoutMs = timeout === undefined ? undefined : timeout * 1000;
|
|
320
|
+
if (timeoutMs !== undefined && timeoutMs > 2_147_483_647) {
|
|
321
|
+
throw new Error(`Invalid timeout: maximum is ${2_147_483_647 / 1000} seconds`);
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
await access(cwd, constants.F_OK);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
throw new Error(`Working directory does not exist: ${cwd}\nCannot execute bash commands.`);
|
|
328
|
+
}
|
|
329
|
+
// access() is asynchronous. A cancel may have closed the captured
|
|
330
|
+
// generation while it was pending, so re-check before taking a lease.
|
|
331
|
+
if (signal?.aborted)
|
|
332
|
+
throw new Error("aborted");
|
|
333
|
+
const shell = getShellConfig(shellPath);
|
|
334
|
+
const stdin = shell.commandTransport === "stdin";
|
|
335
|
+
const lease = slot.beginSpawn();
|
|
336
|
+
let child;
|
|
337
|
+
try {
|
|
338
|
+
child = spawn(shell.shell, stdin ? shell.args : [...shell.args, command], {
|
|
339
|
+
cwd,
|
|
340
|
+
detached: process.platform !== "win32",
|
|
341
|
+
env,
|
|
342
|
+
stdio: [stdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
343
|
+
windowsHide: true,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
lease.failed();
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
let record;
|
|
351
|
+
try {
|
|
352
|
+
record = lease.register(child);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
lease.failed();
|
|
356
|
+
try {
|
|
357
|
+
child.kill("SIGKILL");
|
|
358
|
+
}
|
|
359
|
+
catch { /* no PID/tree was admitted */ }
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
if (stdin) {
|
|
363
|
+
child.stdin?.on("error", () => undefined);
|
|
364
|
+
child.stdin?.end(command);
|
|
365
|
+
}
|
|
366
|
+
child.stdout?.on("data", onData);
|
|
367
|
+
child.stderr?.on("data", onData);
|
|
368
|
+
const timerController = new AbortController();
|
|
369
|
+
const timeoutResult = timeoutMs === undefined
|
|
370
|
+
? new Promise(() => undefined)
|
|
371
|
+
: deps.sleep(timeoutMs, timerController.signal).then(() => ({ type: "timeout" }));
|
|
372
|
+
timeoutResult.catch(() => undefined);
|
|
373
|
+
const abortResult = signal
|
|
374
|
+
? new Promise((resolve) => {
|
|
375
|
+
if (signal.aborted)
|
|
376
|
+
resolve({ type: "abort" });
|
|
377
|
+
else
|
|
378
|
+
signal.addEventListener("abort", () => resolve({ type: "abort" }), { once: true });
|
|
379
|
+
})
|
|
380
|
+
: new Promise(() => undefined);
|
|
381
|
+
const exitResult = new Promise((resolve, reject) => {
|
|
382
|
+
child.once("error", reject);
|
|
383
|
+
child.once("close", (exitCode) => resolve({ type: "exit", exitCode }));
|
|
384
|
+
});
|
|
385
|
+
try {
|
|
386
|
+
const outcome = await Promise.race([exitResult, abortResult, timeoutResult]);
|
|
387
|
+
if (outcome.type === "exit") {
|
|
388
|
+
slot.registry.complete(record);
|
|
389
|
+
return { exitCode: outcome.exitCode };
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
await slot.registry.terminateOne(record);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
slot.latchFailure();
|
|
396
|
+
onCleanupFailure();
|
|
397
|
+
throw new BashCleanupSentinel();
|
|
398
|
+
}
|
|
399
|
+
if (outcome.type === "abort")
|
|
400
|
+
throw new Error("aborted");
|
|
401
|
+
throw new Error(`timeout:${timeout}`);
|
|
402
|
+
}
|
|
403
|
+
finally {
|
|
404
|
+
timerController.abort();
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
|
|
2
2
|
import type { AgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
3
4
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
4
5
|
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
5
6
|
export declare function thinkingLevelOption(session: AgentSession): SessionConfigOption;
|
|
6
|
-
export declare function
|
|
7
|
+
export declare function modelOption(session: AgentSession, availableModels: readonly Model<Api>[]): SessionConfigOption;
|
|
8
|
+
export declare function applyConfig(session: AgentSession, modelRuntime: ModelRuntime, _availableModels: readonly Model<Api>[], configId: string, value: string | boolean): Promise<{
|
|
9
|
+
configOptions: SessionConfigOption[];
|
|
10
|
+
availableModels: readonly Model<Api>[];
|
|
11
|
+
}>;
|
|
7
12
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGxD,eAAO,MAAM,eAAe,sEAAuE,CAAC;AACpG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,mBAAmB,CAS9E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,mBAAmB,CAS9G;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,YAAY,EAC1B,gBAAgB,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,EACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,OAAO,GACtB,OAAO,CAAC;IAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;IAAC,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAA;CAAE,CAAC,CAmC3F"}
|
package/dist/config.js
CHANGED
|
@@ -10,7 +10,17 @@ export function thinkingLevelOption(session) {
|
|
|
10
10
|
options: THINKING_LEVELS.map((value) => ({ value, name: value })),
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
export
|
|
13
|
+
export function modelOption(session, availableModels) {
|
|
14
|
+
return {
|
|
15
|
+
id: "model",
|
|
16
|
+
name: "Model",
|
|
17
|
+
type: "select",
|
|
18
|
+
category: "model",
|
|
19
|
+
currentValue: session.model ? `${session.model.provider}/${session.model.id}` : "",
|
|
20
|
+
options: availableModels.map((model) => ({ value: `${model.provider}/${model.id}`, name: model.name })),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function applyConfig(session, modelRuntime, _availableModels, configId, value) {
|
|
14
24
|
if (configId !== "thinkingLevel" && configId !== "model") {
|
|
15
25
|
throw adapterError("unknown_config_option");
|
|
16
26
|
}
|
|
@@ -20,19 +30,22 @@ export async function applyConfig(session, modelRuntime, configId, value) {
|
|
|
20
30
|
if (!THINKING_LEVELS.includes(value)) {
|
|
21
31
|
throw adapterError("invalid_config_value");
|
|
22
32
|
}
|
|
33
|
+
const availableModels = [...await modelRuntime.getAvailable()];
|
|
23
34
|
session.setThinkingLevel(value);
|
|
24
|
-
return
|
|
35
|
+
return {
|
|
36
|
+
configOptions: [thinkingLevelOption(session), modelOption(session, availableModels)],
|
|
37
|
+
availableModels,
|
|
38
|
+
};
|
|
25
39
|
}
|
|
26
40
|
const separator = value.indexOf("/");
|
|
27
41
|
if (separator <= 0 || separator === value.length - 1)
|
|
28
42
|
throw adapterError("invalid_model");
|
|
29
43
|
const provider = value.slice(0, separator);
|
|
30
44
|
const modelId = value.slice(separator + 1);
|
|
31
|
-
const
|
|
45
|
+
const availableModels = [...await modelRuntime.getAvailable()];
|
|
46
|
+
const model = availableModels.find((candidate) => candidate.provider === provider && candidate.id === modelId);
|
|
32
47
|
if (!model)
|
|
33
48
|
throw adapterError("invalid_model");
|
|
34
|
-
if (!modelRuntime.hasConfiguredAuth(model.provider))
|
|
35
|
-
throw adapterError("auth_error");
|
|
36
49
|
try {
|
|
37
50
|
await session.setModel(model);
|
|
38
51
|
}
|
|
@@ -42,5 +55,8 @@ export async function applyConfig(session, modelRuntime, configId, value) {
|
|
|
42
55
|
}
|
|
43
56
|
throw error;
|
|
44
57
|
}
|
|
45
|
-
return
|
|
58
|
+
return {
|
|
59
|
+
configOptions: [thinkingLevelOption(session), modelOption(session, availableModels)],
|
|
60
|
+
availableModels,
|
|
61
|
+
};
|
|
46
62
|
}
|
package/dist/deps.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ModelRuntime, SessionManager, type CreateAgentSessionOptions, type CreateAgentSessionResult, type NewSessionOptions, type SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type {
|
|
3
|
-
import { type McpClientHandle } from "./mcp-bridge.js";
|
|
2
|
+
import type { McpServer } from "@agentclientprotocol/sdk";
|
|
3
|
+
import { type McpClientHandle, type McpSessionBinding } from "./mcp-bridge.js";
|
|
4
4
|
export interface PiAcpDeps {
|
|
5
5
|
createAgentSession(opts: CreateAgentSessionOptions): Promise<CreateAgentSessionResult>;
|
|
6
6
|
sessions: {
|
|
@@ -12,7 +12,7 @@ export interface PiAcpDeps {
|
|
|
12
12
|
};
|
|
13
13
|
modelRuntime: ModelRuntime;
|
|
14
14
|
sessionDir?: string;
|
|
15
|
-
connectMcpClient(server:
|
|
15
|
+
connectMcpClient(server: McpServer, signal: AbortSignal, binding?: McpSessionBinding): Promise<McpClientHandle>;
|
|
16
16
|
sleep(ms: number, signal: AbortSignal): Promise<void>;
|
|
17
17
|
graceMs: number;
|
|
18
18
|
mcpTimeoutMs: number;
|
package/dist/deps.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,cAAc,EAEd,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,cAAc,EAEd,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,SAAS;IACxB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACvF,QAAQ,EAAE;QACR,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;QACtF,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;QAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;QAClH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;KACtD,CAAC;IACF,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CACd,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBxE;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAuBtF"}
|
package/dist/deps.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ModelRuntime, SessionManager, createAgentSession, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import { connectDefaultMcpClient } from "./mcp-bridge.js";
|
|
3
|
+
import { connectDefaultMcpClient, } from "./mcp-bridge.js";
|
|
4
4
|
export function realSleep(ms, signal) {
|
|
5
5
|
return new Promise((resolve, reject) => {
|
|
6
6
|
if (signal.aborted) {
|
|
@@ -34,6 +34,6 @@ export async function resolveDeps(partial = {}) {
|
|
|
34
34
|
graceMs: partial.graceMs ?? 5_000,
|
|
35
35
|
mcpTimeoutMs,
|
|
36
36
|
connectMcpClient: partial.connectMcpClient ??
|
|
37
|
-
((server, signal) => connectDefaultMcpClient(server, signal, mcpTimeoutMs, sleep)),
|
|
37
|
+
((server, signal, binding) => connectDefaultMcpClient(server, signal, mcpTimeoutMs, sleep, binding)),
|
|
38
38
|
};
|
|
39
39
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestError } from "@agentclientprotocol/sdk";
|
|
2
|
-
export type ErrorKind = "auth_error" | "rate_limit" | "billing_error" | "provider_error" | "invalid_model" | "empty_prompt" | "session_busy" | "invalid_config_value" | "invalid_config_type" | "unknown_config_option" | "invalid_cwd" | "unknown_session" | "session_already_open" | "session_terminated" | "session_corrupt" | "session_not_forkable" | "mcp_init_error" | "unsupported_mcp_transport" | "
|
|
2
|
+
export type ErrorKind = "auth_error" | "rate_limit" | "billing_error" | "provider_error" | "invalid_model" | "empty_prompt" | "session_busy" | "invalid_config_value" | "invalid_config_type" | "unknown_config_option" | "invalid_cwd" | "unknown_session" | "session_already_open" | "session_terminated" | "session_corrupt" | "session_not_forkable" | "mcp_init_error" | "unsupported_mcp_transport" | "extension_setup_error" | "child_cleanup_error" | "invalid_cursor" | "unknown_auth_method" | "notification_error" | "internal_error";
|
|
3
3
|
export interface DiagnosticLike {
|
|
4
4
|
type: string;
|
|
5
5
|
timestamp: number;
|
|
@@ -20,15 +20,28 @@ export declare function redactedDiagnostics(diagnostics: readonly DiagnosticLike
|
|
|
20
20
|
type: string;
|
|
21
21
|
timestamp: number;
|
|
22
22
|
}[] | undefined;
|
|
23
|
-
|
|
24
|
-
server?: string;
|
|
23
|
+
type DiagnosticDetails = {
|
|
25
24
|
details?: Array<{
|
|
26
25
|
type: string;
|
|
27
26
|
timestamp: number;
|
|
28
27
|
}>;
|
|
29
|
-
}
|
|
28
|
+
};
|
|
29
|
+
type ServerDetails = {
|
|
30
|
+
server: string;
|
|
31
|
+
};
|
|
32
|
+
type ChildDetails = {
|
|
33
|
+
details: {
|
|
34
|
+
remainingChildren: number;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export declare function adapterError(kind: "mcp_init_error" | "unsupported_mcp_transport", extras: ServerDetails): RequestError;
|
|
38
|
+
export declare function adapterError(kind: "provider_error" | "internal_error", extras?: DiagnosticDetails): RequestError;
|
|
39
|
+
export declare function adapterError(kind: "child_cleanup_error", extras: ChildDetails): RequestError;
|
|
40
|
+
export declare function adapterError(kind: Exclude<ErrorKind, "mcp_init_error" | "unsupported_mcp_transport" | "provider_error" | "internal_error" | "child_cleanup_error">): RequestError;
|
|
30
41
|
export declare function classifyPreflight(error: unknown): RequestError;
|
|
31
42
|
export declare function classifyTerminal(message: TerminalAssistantLike): RequestError;
|
|
32
43
|
export declare function unexpectedError(error: unknown, terminal?: TerminalAssistantLike): RequestError;
|
|
33
44
|
export declare function isRequestError(error: unknown): error is RequestError;
|
|
45
|
+
export declare function isChildCleanupError(error: unknown): error is RequestError;
|
|
46
|
+
export {};
|
|
34
47
|
//# sourceMappingURL=errors.d.ts.map
|