@victor-software-house/pi-acp 0.10.0 → 0.11.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.
|
@@ -8,7 +8,7 @@ import { isAbsolute, join, resolve } from "node:path";
|
|
|
8
8
|
import { Hono } from "hono";
|
|
9
9
|
import { AgentSideConnection, RequestError, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import { DefaultResourceLoader, SessionManager, createAgentSession, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { DefaultResourceLoader, SessionManager, createAgentSession, createReadToolDefinition, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import * as z from "zod";
|
|
13
13
|
import { parse } from "yaml";
|
|
14
14
|
import { $ } from "bun";
|
|
@@ -184,6 +184,30 @@ function resolveIdleMs() {
|
|
|
184
184
|
return n * 1e3;
|
|
185
185
|
}
|
|
186
186
|
//#endregion
|
|
187
|
+
//#region src/acp/acp-read-operations.ts
|
|
188
|
+
function createAcpReadOperations(deps) {
|
|
189
|
+
const { conn, getSessionId } = deps;
|
|
190
|
+
return {
|
|
191
|
+
async readFile(absolutePath) {
|
|
192
|
+
const sessionId = getSessionId();
|
|
193
|
+
if (sessionId === "") throw new Error("pi-acp acp-fs read: sessionId not yet bound");
|
|
194
|
+
const response = await conn.readTextFile({
|
|
195
|
+
sessionId,
|
|
196
|
+
path: absolutePath
|
|
197
|
+
});
|
|
198
|
+
return Buffer.from(response.content, "utf8");
|
|
199
|
+
},
|
|
200
|
+
async access(absolutePath) {
|
|
201
|
+
const sessionId = getSessionId();
|
|
202
|
+
if (sessionId === "") throw new Error("pi-acp acp-fs access: sessionId not yet bound");
|
|
203
|
+
await conn.readTextFile({
|
|
204
|
+
sessionId,
|
|
205
|
+
path: absolutePath
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
187
211
|
//#region src/acp/auth.ts
|
|
188
212
|
const AUTH_METHOD_ID = "pi_terminal_login";
|
|
189
213
|
function buildAuthMethods(opts) {
|
|
@@ -251,7 +275,8 @@ function parseClientCapabilities(caps) {
|
|
|
251
275
|
if (caps === void 0 || caps === null) return {
|
|
252
276
|
terminalOutput: false,
|
|
253
277
|
terminalAuth: false,
|
|
254
|
-
gatewayAuth: false
|
|
278
|
+
gatewayAuth: false,
|
|
279
|
+
fsReadTextFile: false
|
|
255
280
|
};
|
|
256
281
|
const meta = caps._meta;
|
|
257
282
|
const terminalOutput = typeof meta === "object" && meta !== null && meta["terminal_output"] === true;
|
|
@@ -264,10 +289,12 @@ function parseClientCapabilities(caps) {
|
|
|
264
289
|
if (typeof authMeta === "object" && authMeta !== null && "gateway" in authMeta) gatewayAuth = authMeta["gateway"] === true;
|
|
265
290
|
}
|
|
266
291
|
}
|
|
292
|
+
const fsReadTextFile = caps.fs?.readTextFile === true;
|
|
267
293
|
return {
|
|
268
294
|
terminalOutput,
|
|
269
295
|
terminalAuth,
|
|
270
|
-
gatewayAuth
|
|
296
|
+
gatewayAuth,
|
|
297
|
+
fsReadTextFile
|
|
271
298
|
};
|
|
272
299
|
}
|
|
273
300
|
//#endregion
|
|
@@ -1870,7 +1897,7 @@ var SshBackend = class {
|
|
|
1870
1897
|
//#endregion
|
|
1871
1898
|
//#region package.json
|
|
1872
1899
|
var name = "@victor-software-house/pi-acp";
|
|
1873
|
-
var version = "0.
|
|
1900
|
+
var version = "0.11.0";
|
|
1874
1901
|
//#endregion
|
|
1875
1902
|
//#region src/acp/agent.ts
|
|
1876
1903
|
/** Builtin ACP slash commands handled directly by the adapter. */
|
|
@@ -1959,7 +1986,8 @@ var PiAcpAgent = class {
|
|
|
1959
1986
|
clientCapabilities = {
|
|
1960
1987
|
terminalOutput: false,
|
|
1961
1988
|
terminalAuth: false,
|
|
1962
|
-
gatewayAuth: false
|
|
1989
|
+
gatewayAuth: false,
|
|
1990
|
+
fsReadTextFile: false
|
|
1963
1991
|
};
|
|
1964
1992
|
daemonContext;
|
|
1965
1993
|
/** Unique ID for this ACP connection. Used as the ownership key in the daemon SessionRegistry. */
|
|
@@ -2059,6 +2087,44 @@ var PiAcpAgent = class {
|
|
|
2059
2087
|
}
|
|
2060
2088
|
return loader;
|
|
2061
2089
|
}
|
|
2090
|
+
/**
|
|
2091
|
+
* PRD-002 §FR-6 — `read` tool ACP-FS delegation overlay.
|
|
2092
|
+
*
|
|
2093
|
+
* When the client advertises `fs.readTextFile`, we override pi's
|
|
2094
|
+
* built-in `read` with a custom `read` tool that proxies to
|
|
2095
|
+
* `connection.fs.readTextFile`. The allowlist MUST include "read" so
|
|
2096
|
+
* pi's customTool registration loop (which filters by name) can
|
|
2097
|
+
* register the override; the override then shadows the builtin via
|
|
2098
|
+
* the tool-definition `Map.set` path inside AgentSession.
|
|
2099
|
+
*
|
|
2100
|
+
* The sessionId ref is mutated by the caller right after
|
|
2101
|
+
* `createAgentSession` returns, before any model turn — the tool
|
|
2102
|
+
* isn't invoked until prompt-time, so the late binding is safe.
|
|
2103
|
+
*
|
|
2104
|
+
* Returns `null` when the client doesn't advertise the capability;
|
|
2105
|
+
* callers then skip the overlay and pi's built-in `read` handles
|
|
2106
|
+
* everything locally.
|
|
2107
|
+
*/
|
|
2108
|
+
buildAcpReadOverlay(cwd) {
|
|
2109
|
+
if (!this.clientCapabilities.fsReadTextFile) return null;
|
|
2110
|
+
const sessionIdRef = { current: "" };
|
|
2111
|
+
return {
|
|
2112
|
+
sessionIdRef,
|
|
2113
|
+
tools: [
|
|
2114
|
+
"read",
|
|
2115
|
+
"bash",
|
|
2116
|
+
"edit",
|
|
2117
|
+
"write",
|
|
2118
|
+
"grep",
|
|
2119
|
+
"find",
|
|
2120
|
+
"ls"
|
|
2121
|
+
],
|
|
2122
|
+
customTools: [createReadToolDefinition(cwd, { operations: createAcpReadOperations({
|
|
2123
|
+
conn: this.conn,
|
|
2124
|
+
getSessionId: () => sessionIdRef.current
|
|
2125
|
+
}) })]
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2062
2128
|
async initialize(params) {
|
|
2063
2129
|
const supportedVersion = 1;
|
|
2064
2130
|
const requested = params.protocolVersion;
|
|
@@ -2093,12 +2159,17 @@ var PiAcpAgent = class {
|
|
|
2093
2159
|
}
|
|
2094
2160
|
async newSession(params) {
|
|
2095
2161
|
if (!isAbsolute(params.cwd)) throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
|
|
2162
|
+
const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
|
|
2096
2163
|
let result;
|
|
2097
2164
|
try {
|
|
2098
2165
|
const resourceLoader = await this.buildResourceLoader(params.cwd, params);
|
|
2099
2166
|
result = await createAgentSession({
|
|
2100
2167
|
cwd: params.cwd,
|
|
2101
|
-
resourceLoader
|
|
2168
|
+
resourceLoader,
|
|
2169
|
+
...acpReadOverlay ? {
|
|
2170
|
+
tools: acpReadOverlay.tools,
|
|
2171
|
+
customTools: acpReadOverlay.customTools
|
|
2172
|
+
} : {}
|
|
2102
2173
|
});
|
|
2103
2174
|
} catch (e) {
|
|
2104
2175
|
const authErr = detectAuthError(e);
|
|
@@ -2107,6 +2178,7 @@ var PiAcpAgent = class {
|
|
|
2107
2178
|
throw RequestError.internalError({}, `Failed to create pi session: ${msg}`);
|
|
2108
2179
|
}
|
|
2109
2180
|
const piSession = result.session;
|
|
2181
|
+
if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
|
|
2110
2182
|
if (piSession.modelRegistry.getAvailable().length === 0) {
|
|
2111
2183
|
piSession.dispose();
|
|
2112
2184
|
throw RequestError.authRequired({ authMethods: buildAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
|
|
@@ -2366,6 +2438,7 @@ var PiAcpAgent = class {
|
|
|
2366
2438
|
this.sessions.close(params.sessionId);
|
|
2367
2439
|
const sessionFile = await this.resolveSessionFile(params.sessionId);
|
|
2368
2440
|
if (sessionFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2441
|
+
const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
|
|
2369
2442
|
let result;
|
|
2370
2443
|
try {
|
|
2371
2444
|
const sm = SessionManager.open(sessionFile);
|
|
@@ -2373,7 +2446,11 @@ var PiAcpAgent = class {
|
|
|
2373
2446
|
result = await createAgentSession({
|
|
2374
2447
|
cwd: params.cwd,
|
|
2375
2448
|
sessionManager: sm,
|
|
2376
|
-
resourceLoader
|
|
2449
|
+
resourceLoader,
|
|
2450
|
+
...acpReadOverlay ? {
|
|
2451
|
+
tools: acpReadOverlay.tools,
|
|
2452
|
+
customTools: acpReadOverlay.customTools
|
|
2453
|
+
} : {}
|
|
2377
2454
|
});
|
|
2378
2455
|
} catch (e) {
|
|
2379
2456
|
const authErr = detectAuthError(e);
|
|
@@ -2382,6 +2459,7 @@ var PiAcpAgent = class {
|
|
|
2382
2459
|
throw RequestError.internalError({}, `Failed to load pi session: ${msg}`);
|
|
2383
2460
|
}
|
|
2384
2461
|
const piSession = result.session;
|
|
2462
|
+
if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
|
|
2385
2463
|
const session = new PiAcpSession({
|
|
2386
2464
|
sessionId: params.sessionId,
|
|
2387
2465
|
cwd: params.cwd,
|
|
@@ -2466,6 +2544,7 @@ var PiAcpAgent = class {
|
|
|
2466
2544
|
}
|
|
2467
2545
|
const sessionFile = await this.resolveSessionFile(params.sessionId);
|
|
2468
2546
|
if (sessionFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2547
|
+
const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
|
|
2469
2548
|
let result;
|
|
2470
2549
|
try {
|
|
2471
2550
|
const sm = SessionManager.open(sessionFile);
|
|
@@ -2473,7 +2552,11 @@ var PiAcpAgent = class {
|
|
|
2473
2552
|
result = await createAgentSession({
|
|
2474
2553
|
cwd: params.cwd,
|
|
2475
2554
|
sessionManager: sm,
|
|
2476
|
-
resourceLoader
|
|
2555
|
+
resourceLoader,
|
|
2556
|
+
...acpReadOverlay ? {
|
|
2557
|
+
tools: acpReadOverlay.tools,
|
|
2558
|
+
customTools: acpReadOverlay.customTools
|
|
2559
|
+
} : {}
|
|
2477
2560
|
});
|
|
2478
2561
|
} catch (e) {
|
|
2479
2562
|
const authErr = detectAuthError(e);
|
|
@@ -2482,6 +2565,7 @@ var PiAcpAgent = class {
|
|
|
2482
2565
|
throw RequestError.internalError({}, `Failed to resume pi session: ${msg}`);
|
|
2483
2566
|
}
|
|
2484
2567
|
const piSession = result.session;
|
|
2568
|
+
if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
|
|
2485
2569
|
const session = new PiAcpSession({
|
|
2486
2570
|
sessionId: params.sessionId,
|
|
2487
2571
|
cwd: params.cwd,
|
|
@@ -2525,6 +2609,7 @@ var PiAcpAgent = class {
|
|
|
2525
2609
|
if (!isAbsolute(params.cwd)) throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
|
|
2526
2610
|
const sourceFile = await this.resolveSessionFile(params.sessionId);
|
|
2527
2611
|
if (sourceFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2612
|
+
const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
|
|
2528
2613
|
let result;
|
|
2529
2614
|
try {
|
|
2530
2615
|
const sm = SessionManager.forkFrom(sourceFile, params.cwd);
|
|
@@ -2532,7 +2617,11 @@ var PiAcpAgent = class {
|
|
|
2532
2617
|
result = await createAgentSession({
|
|
2533
2618
|
cwd: params.cwd,
|
|
2534
2619
|
sessionManager: sm,
|
|
2535
|
-
resourceLoader
|
|
2620
|
+
resourceLoader,
|
|
2621
|
+
...acpReadOverlay ? {
|
|
2622
|
+
tools: acpReadOverlay.tools,
|
|
2623
|
+
customTools: acpReadOverlay.customTools
|
|
2624
|
+
} : {}
|
|
2536
2625
|
});
|
|
2537
2626
|
} catch (e) {
|
|
2538
2627
|
const authErr = detectAuthError(e);
|
|
@@ -2542,6 +2631,7 @@ var PiAcpAgent = class {
|
|
|
2542
2631
|
}
|
|
2543
2632
|
const piSession = result.session;
|
|
2544
2633
|
const newSessionId = piSession.sessionManager.getSessionId();
|
|
2634
|
+
if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = newSessionId;
|
|
2545
2635
|
const newSessionFile = piSession.sessionManager.getSessionFile();
|
|
2546
2636
|
if (newSessionFile !== void 0) this.sessionPaths.set(newSessionId, newSessionFile);
|
|
2547
2637
|
const session = new PiAcpSession({
|
|
@@ -3160,4 +3250,4 @@ async function runDaemon() {
|
|
|
3160
3250
|
//#endregion
|
|
3161
3251
|
export { runDaemon };
|
|
3162
3252
|
|
|
3163
|
-
//# sourceMappingURL=daemon-
|
|
3253
|
+
//# sourceMappingURL=daemon-COFaIaTB.mjs.map
|