@victor-software-house/pi-acp 0.13.0 → 0.14.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 CHANGED
@@ -7,7 +7,7 @@ ACP ([Agent Client Protocol](https://agentclientprotocol.com/get-started/introdu
7
7
  ## Specs and decisions
8
8
 
9
9
  - [`docs/prd/PRD-001-acp-v013-zed-alignment.md`](docs/prd/PRD-001-acp-v013-zed-alignment.md) — v0.5 release PRD (Shipped).
10
- - [`docs/prd/PRD-002-portable-runtime.md`](docs/prd/PRD-002-portable-runtime.md) — v0.6 portable runtime + multi-host resource composition (Draft).
10
+ - [`docs/prd/PRD-002-portable-runtime.md`](docs/prd/PRD-002-portable-runtime.md) — v0.6 portable runtime + multi-host resource composition (Substrate Shipped; Phases 8b/9 deferred).
11
11
  - [`docs/prd/PRD-003-runtime-daemon.md`](docs/prd/PRD-003-runtime-daemon.md) — v0.6 long-running daemon + thin-client binary (Draft).
12
12
  - [`docs/architecture/plan-acp-v013-zed-alignment.md`](docs/architecture/plan-acp-v013-zed-alignment.md) — v0.5 phased implementation plan.
13
13
  - [`docs/architecture/plan-portable-runtime.md`](docs/architecture/plan-portable-runtime.md) — v0.6 portable-runtime plan.
@@ -53,6 +53,63 @@ Active development. ACP compliance is improving steadily. Development is centere
53
53
  - Built-in adapter commands (see below)
54
54
  - Authentication via Terminal Auth (ACP Registry support)
55
55
  - Startup info block with pi version and context (configurable via `quietStartup` setting)
56
+ - **Resource composition manifest** (`.pi-acp.yaml`) — PRD-002 §FR-3
57
+ - Cascade: ACP session params > project `<cwd>/.pi-acp.yaml` > user-global `~/.pi-acp/config.yaml` > synthesized default
58
+ - Backends: `local`, `ssh` (Bun Shell `$` + ssh self-terminate options), `http` (HTTPS-only fetch + per-URL TTL cache, default 300s)
59
+ - Merge strategies: `append` (default) or `override-by-name` for skills and prompts
60
+ - Opt-in diagnostics surface (`diagnostics: true`) — one-line resource summary on first prompt of each session
61
+ - **Cwd-independence modes** (PRD-002 §FR-5)
62
+ - `local` (default) / `overlay` — ACP `params.cwd` used as session cwd; manifest roots compose
63
+ - `none` — pi-acp mints an ephemeral tmpdir under `os.tmpdir()/pi-acp-session-*`, cleaned up at session dispose. For one-shot Q&A sessions that shouldn't pollute any project directory.
64
+ - **ACP-FS `read` delegation** (PRD-002 §FR-6) — When the client advertises `clientCapabilities.fs.readTextFile`, pi-acp routes pi's built-in `read` tool through `connection.fs.readTextFile` instead of local disk. Lets Zed Remote read the actual remote workspace files (the ones the user is editing) while pi runs locally.
65
+
66
+ ## Resource composition (`.pi-acp.yaml`)
67
+
68
+ Drop a `.pi-acp.yaml` at the project root (or `~/.pi-acp/config.yaml` for user-global defaults). Schema version `1`:
69
+
70
+ ```yaml
71
+ version: 1
72
+ mode: local # local (default) | overlay | none
73
+ mergeStrategy: append # append | override-by-name
74
+ diagnostics: false # true: emit a one-line resource summary on first prompt
75
+
76
+ roots:
77
+ # Local roots (cwd + optional alt agentDir)
78
+ - id: project
79
+ kind: local
80
+ paths:
81
+ cwd: .
82
+ agentDir: ~/.pi/agent
83
+
84
+ # Remote files over SSH (operator's ~/.ssh/config honored end-to-end)
85
+ - id: cvm
86
+ kind: ssh
87
+ host: cvm
88
+ user: varaujo
89
+ paths:
90
+ agentsFiles:
91
+ - /home/varaujo/.pi/agent/AGENTS.md
92
+ - /workspace/team/SECURITY.md
93
+ # skills/prompts/extensions over SSH not yet implemented;
94
+ # declaring paths.skills here emits a diagnostic at session start.
95
+
96
+ # Public HTTPS fetch (e.g. team's shared AGENTS file on a public repo)
97
+ - id: team
98
+ kind: http
99
+ baseUrl: https://raw.githubusercontent.com/team/dotfiles/main
100
+ cache:
101
+ ttl: 600 # per-URL TTL in seconds; default 300, 0 disables
102
+ paths:
103
+ agentsFiles:
104
+ - AGENTS.md
105
+ ```
106
+
107
+ Cascade precedence (highest first):
108
+
109
+ 1. ACP session params: `params._meta.piAcp.manifest` (inline manifest object OR string path to a YAML file)
110
+ 2. Project: `<cwd>/.pi-acp.yaml`
111
+ 3. User-global: `~/.pi-acp/config.yaml`
112
+ 4. Synthesized default (single implicit local root)
56
113
 
57
114
  ## Prerequisites
58
115
 
@@ -151,7 +208,7 @@ bun run dev # run from src
151
208
  bun run build # tsdown -> dist/index.mjs
152
209
  bun run typecheck # tsc --noEmit
153
210
  bun run lint # biome + oxlint
154
- bun test # 26 tests
211
+ bun test # 277 tests
155
212
  ```
156
213
 
157
214
  Project layout:
@@ -191,7 +248,7 @@ test/
191
248
  ### Not implemented (MAY / client capabilities)
192
249
 
193
250
  - **`agent_plan`** -- plan updates not emitted before tool execution. pi has no equivalent planning surface.
194
- - **ACP filesystem delegation** (`fs/read_text_file`, `fs/write_text_file`) -- pi reads/writes locally. Not advertised.
251
+ - **ACP filesystem `write` delegation** (`fs/write_text_file`) -- pi writes locally. Not advertised. `fs/read_text_file` IS routed through ACP when the client advertises the capability (see Features → ACP-FS `read` delegation).
195
252
  - **ACP terminal delegation** (`terminal/*`) -- pi executes commands locally. Not advertised.
196
253
 
197
254
  ### Design decisions
@@ -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, createReadToolDefinition, getAgentDir } from "@earendil-works/pi-coding-agent";
11
+ import { DefaultResourceLoader, SessionManager, createAgentSession, createBashToolDefinition, 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,78 @@ function resolveIdleMs() {
184
184
  return n * 1e3;
185
185
  }
186
186
  //#endregion
187
+ //#region src/acp/acp-bash-operations.ts
188
+ const POLL_INTERVAL_MS = 100;
189
+ const SHELL_PATH = "/bin/sh";
190
+ function createAcpBashOperations(deps) {
191
+ const { conn, getSessionId } = deps;
192
+ return { async exec(command, cwd, options) {
193
+ const sessionId = getSessionId();
194
+ if (sessionId === "") throw new Error("pi-acp acp-bash: sessionId not yet bound");
195
+ const env = options.env !== void 0 ? Object.entries(options.env).filter(([, v]) => v !== void 0).map(([name, value]) => ({
196
+ name,
197
+ value: String(value)
198
+ })) : [];
199
+ const createParams = {
200
+ sessionId,
201
+ command: SHELL_PATH,
202
+ args: ["-c", command],
203
+ cwd,
204
+ env
205
+ };
206
+ const terminal = await conn.createTerminal(createParams);
207
+ let lastOutputLen = 0;
208
+ let cancelled = false;
209
+ const abortHandler = () => {
210
+ cancelled = true;
211
+ terminal.kill().catch(() => {});
212
+ };
213
+ options.signal?.addEventListener("abort", abortHandler);
214
+ const pollLoop = async () => {
215
+ while (!cancelled) {
216
+ try {
217
+ const snap = await terminal.currentOutput();
218
+ if (snap.output.length > lastOutputLen) {
219
+ const delta = snap.output.slice(lastOutputLen);
220
+ lastOutputLen = snap.output.length;
221
+ options.onData(Buffer.from(delta, "utf8"));
222
+ }
223
+ if (snap.exitStatus !== null && snap.exitStatus !== void 0) return;
224
+ } catch {
225
+ return;
226
+ }
227
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
228
+ }
229
+ };
230
+ const timeoutPromise = options.timeout !== void 0 && options.timeout > 0 ? new Promise((resolve) => setTimeout(() => resolve({ timedOut: true }), options.timeout)) : null;
231
+ try {
232
+ const pollPromise = pollLoop();
233
+ const exitPromise = terminal.waitForExit();
234
+ const winner = timeoutPromise !== null ? await Promise.race([exitPromise, timeoutPromise]) : await exitPromise;
235
+ let exitCode;
236
+ if ("timedOut" in winner) {
237
+ await terminal.kill().catch(() => {});
238
+ exitCode = (await terminal.waitForExit()).exitCode ?? null;
239
+ } else exitCode = winner.exitCode ?? null;
240
+ cancelled = true;
241
+ await pollPromise;
242
+ try {
243
+ const final = await terminal.currentOutput();
244
+ if (final.output.length > lastOutputLen) {
245
+ const delta = final.output.slice(lastOutputLen);
246
+ options.onData(Buffer.from(delta, "utf8"));
247
+ }
248
+ } catch {}
249
+ return { exitCode };
250
+ } finally {
251
+ options.signal?.removeEventListener("abort", abortHandler);
252
+ try {
253
+ await terminal.release();
254
+ } catch {}
255
+ }
256
+ } };
257
+ }
258
+ //#endregion
187
259
  //#region src/acp/acp-read-operations.ts
188
260
  function createAcpReadOperations(deps) {
189
261
  const { conn, getSessionId } = deps;
@@ -263,41 +335,66 @@ function detectAuthError(err) {
263
335
  }
264
336
  //#endregion
265
337
  //#region src/acp/client-capabilities.ts
266
- /**
267
- * Extract well-known capability flags from ACP `ClientCapabilities`.
268
- *
269
- * Reads from:
270
- * - `_meta.terminal_output` (terminal output rendering)
271
- * - `_meta.terminal-auth` (terminal auth with command metadata)
272
- * - `auth._meta.gateway` (gateway auth, future use)
273
- */
274
338
  function parseClientCapabilities(caps) {
275
- if (caps === void 0 || caps === null) return {
276
- terminalOutput: false,
277
- terminalAuth: false,
278
- gatewayAuth: false,
279
- fsReadTextFile: false
280
- };
281
- const meta = caps._meta;
282
- const terminalOutput = typeof meta === "object" && meta !== null && meta["terminal_output"] === true;
283
- const terminalAuth = typeof meta === "object" && meta !== null && meta["terminal-auth"] === true;
284
- let gatewayAuth = false;
285
- if ("auth" in caps) {
286
- const auth = caps.auth;
287
- if (typeof auth === "object" && auth !== null && "_meta" in auth) {
288
- const authMeta = auth._meta;
289
- if (typeof authMeta === "object" && authMeta !== null && "gateway" in authMeta) gatewayAuth = authMeta["gateway"] === true;
290
- }
291
- }
292
- const fsReadTextFile = caps.fs?.readTextFile === true;
339
+ const safe = caps ?? {};
340
+ const meta = safe._meta;
341
+ const metaIsObject = typeof meta === "object" && meta !== null;
342
+ const authMeta = "auth" in safe && typeof safe.auth === "object" && safe.auth !== null && "_meta" in safe.auth ? safe.auth._meta : void 0;
343
+ const authMetaIsObject = typeof authMeta === "object" && authMeta !== null;
293
344
  return {
294
- terminalOutput,
295
- terminalAuth,
296
- gatewayAuth,
297
- fsReadTextFile
345
+ terminalOutput: metaIsObject && meta["terminal_output"] === true,
346
+ terminalAuth: metaIsObject && meta["terminal-auth"] === true,
347
+ gatewayAuth: authMetaIsObject && authMeta["gateway"] === true,
348
+ fsReadTextFile: safe.fs?.readTextFile === true,
349
+ terminal: safe.terminal === true
298
350
  };
299
351
  }
300
352
  //#endregion
353
+ //#region src/acp/ext-methods.ts
354
+ /**
355
+ * ACP `extMethod` / `extNotification` dispatcher.
356
+ *
357
+ * ACP spec recommends prefixing extension method names with a unique
358
+ * identifier (e.g., a domain name). pi-acp uses the `pi-acp/` prefix for
359
+ * its built-ins; client-defined methods can also be routed here by
360
+ * registering handlers via `register()`.
361
+ *
362
+ * Unknown request methods throw `RequestError.methodNotFound`. Unknown
363
+ * notification methods are silently ignored per JSON-RPC 2.0 semantics —
364
+ * notifications have no response channel, so erroring is meaningless.
365
+ */
366
+ var ExtMethodDispatcher = class {
367
+ requestHandlers = /* @__PURE__ */ new Map();
368
+ notificationHandlers = /* @__PURE__ */ new Map();
369
+ constructor(deps) {
370
+ this.requestHandlers.set("pi-acp/ping", () => ({
371
+ ok: true,
372
+ ts: Date.now()
373
+ }));
374
+ this.requestHandlers.set("pi-acp/runtime-info", () => ({
375
+ version: deps.version,
376
+ uptimeMs: Date.now() - deps.startedAt,
377
+ sessionCount: deps.sessionCount()
378
+ }));
379
+ }
380
+ register(method, handler) {
381
+ this.requestHandlers.set(method, handler);
382
+ }
383
+ registerNotification(method, handler) {
384
+ this.notificationHandlers.set(method, handler);
385
+ }
386
+ async handleRequest(method, params) {
387
+ const handler = this.requestHandlers.get(method);
388
+ if (handler === void 0) throw RequestError.methodNotFound(method);
389
+ return await handler(params);
390
+ }
391
+ async handleNotification(method, params) {
392
+ const handler = this.notificationHandlers.get(method);
393
+ if (handler === void 0) return;
394
+ await handler(params);
395
+ }
396
+ };
397
+ //#endregion
301
398
  //#region src/acp/model-alias.ts
302
399
  /**
303
400
  * Tokenize a string: split on non-alphanumeric, lowercase, strip "claude".
@@ -856,6 +953,17 @@ var SessionManager$1 = class {
856
953
  if (!s) throw RequestError.invalidParams(`Unknown sessionId: ${sessionId}`);
857
954
  return s;
858
955
  }
956
+ size() {
957
+ return this.sessions.size;
958
+ }
959
+ values() {
960
+ return this.sessions.values();
961
+ }
962
+ /** First registered session, or undefined. Order = insertion order. */
963
+ first() {
964
+ const it = this.sessions.values().next();
965
+ return it.done === true ? void 0 : it.value;
966
+ }
859
967
  };
860
968
  var PiAcpSession = class {
861
969
  sessionId;
@@ -2007,7 +2115,7 @@ var SshBackend = class {
2007
2115
  //#endregion
2008
2116
  //#region package.json
2009
2117
  var name = "@victor-software-house/pi-acp";
2010
- var version = "0.13.0";
2118
+ var version = "0.14.0";
2011
2119
  //#endregion
2012
2120
  //#region src/acp/agent.ts
2013
2121
  /** Builtin ACP slash commands handled directly by the adapter. */
@@ -2097,11 +2205,14 @@ var PiAcpAgent = class {
2097
2205
  terminalOutput: false,
2098
2206
  terminalAuth: false,
2099
2207
  gatewayAuth: false,
2100
- fsReadTextFile: false
2208
+ fsReadTextFile: false,
2209
+ terminal: false
2101
2210
  };
2102
2211
  daemonContext;
2103
2212
  /** Unique ID for this ACP connection. Used as the ownership key in the daemon SessionRegistry. */
2104
2213
  connectionId = randomUUID();
2214
+ extMethods;
2215
+ startedAt = Date.now();
2105
2216
  dispose() {
2106
2217
  if (this.daemonContext !== void 0) {
2107
2218
  const registry = this.daemonContext.sessionRegistry;
@@ -2114,6 +2225,17 @@ var PiAcpAgent = class {
2114
2225
  constructor(conn, daemonContext) {
2115
2226
  this.conn = conn;
2116
2227
  this.daemonContext = daemonContext;
2228
+ this.extMethods = new ExtMethodDispatcher({
2229
+ version,
2230
+ startedAt: this.startedAt,
2231
+ sessionCount: () => this.sessions.size()
2232
+ });
2233
+ }
2234
+ async extMethod(method, params) {
2235
+ return this.extMethods.handleRequest(method, params);
2236
+ }
2237
+ async extNotification(method, params) {
2238
+ await this.extMethods.handleNotification(method, params);
2117
2239
  }
2118
2240
  registerWithDaemon(input) {
2119
2241
  if (this.daemonContext === void 0) return;
@@ -2139,15 +2261,20 @@ var PiAcpAgent = class {
2139
2261
  * Phase 7 adds `kind: "http"`. `acp-fs` still parses fine but surfaces as
2140
2262
  * a diagnostic until its backend lands in a subsequent phase.
2141
2263
  */
2142
- async buildResourceLoader(cwd, sessionParams) {
2264
+ async buildResourceLoader(cwd, sessionParams, opts) {
2143
2265
  const loaded = await loadManifest({
2144
2266
  cwd,
2145
2267
  sessionParams
2146
2268
  });
2147
- const modeResult = resolveMode({
2269
+ const modeResult = opts?.resolveCwdMode !== false ? resolveMode({
2148
2270
  manifest: loaded.manifest,
2149
2271
  requestedCwd: cwd
2150
- });
2272
+ }) : {
2273
+ mode: loaded.manifest.mode,
2274
+ cwd,
2275
+ cleanup: () => {},
2276
+ ephemeral: false
2277
+ };
2151
2278
  const effectiveCwd = modeResult.cwd;
2152
2279
  const diagnostics = [...loaded.diagnostics];
2153
2280
  const sources = [];
@@ -2208,26 +2335,43 @@ var PiAcpAgent = class {
2208
2335
  };
2209
2336
  }
2210
2337
  /**
2211
- * PRD-002 §FR-6 — `read` tool ACP-FS delegation overlay.
2338
+ * PRD-002 §FR-6 + §FR-6.5 — tool overrides for ACP-FS read + ACP terminal bash.
2212
2339
  *
2213
- * When the client advertises `fs.readTextFile`, we override pi's
2214
- * built-in `read` with a custom `read` tool that proxies to
2215
- * `connection.fs.readTextFile`. The allowlist MUST include "read" so
2216
- * pi's customTool registration loop (which filters by name) can
2217
- * register the override; the override then shadows the builtin via
2218
- * the tool-definition `Map.set` path inside AgentSession.
2340
+ * For each tool we override, the allowlist MUST include the original
2341
+ * tool name so pi's customTool registration loop (which filters by
2342
+ * name) can register the override; the override then shadows the
2343
+ * builtin via the tool-definition `Map.set` path inside AgentSession
2344
+ * (verified against pi source agent-session.js:1811).
2219
2345
  *
2220
- * The sessionId ref is mutated by the caller right after
2221
- * `createAgentSession` returns, before any model turn the tool
2222
- * isn't invoked until prompt-time, so the late binding is safe.
2346
+ * SessionId binding is late: pi mints the id inside `createAgentSession`
2347
+ * (after `customTools` is built), so we share a single mutable ref
2348
+ * across all overrides. The caller mutates `sessionIdRef.current`
2349
+ * right after createAgentSession returns, before any model turn — the
2350
+ * tools aren't invoked until prompt-time, so the late binding is safe.
2223
2351
  *
2224
- * Returns `null` when the client doesn't advertise the capability;
2225
- * callers then skip the overlay and pi's built-in `read` handles
2226
- * everything locally.
2352
+ * Returns `null` when neither capability is advertised; callers skip
2353
+ * the overlay and pi's built-in tools handle everything locally.
2227
2354
  */
2228
- buildAcpReadOverlay(cwd) {
2229
- if (!this.clientCapabilities.fsReadTextFile) return null;
2355
+ buildAcpToolOverlay(cwd) {
2356
+ const wantRead = this.clientCapabilities.fsReadTextFile;
2357
+ const wantBash = this.clientCapabilities.terminal;
2358
+ if (!wantRead && !wantBash) return null;
2230
2359
  const sessionIdRef = { current: "" };
2360
+ const customTools = [];
2361
+ if (wantRead) {
2362
+ const readToolDef = createReadToolDefinition(cwd, { operations: createAcpReadOperations({
2363
+ conn: this.conn,
2364
+ getSessionId: () => sessionIdRef.current
2365
+ }) });
2366
+ customTools.push(readToolDef);
2367
+ }
2368
+ if (wantBash) {
2369
+ const bashToolDef = createBashToolDefinition(cwd, { operations: createAcpBashOperations({
2370
+ conn: this.conn,
2371
+ getSessionId: () => sessionIdRef.current
2372
+ }) });
2373
+ customTools.push(bashToolDef);
2374
+ }
2231
2375
  return {
2232
2376
  sessionIdRef,
2233
2377
  tools: [
@@ -2239,10 +2383,7 @@ var PiAcpAgent = class {
2239
2383
  "find",
2240
2384
  "ls"
2241
2385
  ],
2242
- customTools: [createReadToolDefinition(cwd, { operations: createAcpReadOperations({
2243
- conn: this.conn,
2244
- getSessionId: () => sessionIdRef.current
2245
- }) })]
2386
+ customTools
2246
2387
  };
2247
2388
  }
2248
2389
  async initialize(params) {
@@ -2293,15 +2434,15 @@ var PiAcpAgent = class {
2293
2434
  sources: resourceLoader.listSources(),
2294
2435
  manifestDiagnostics
2295
2436
  }).text : "";
2296
- const acpReadOverlay = this.buildAcpReadOverlay(effectiveCwd);
2437
+ const acpToolOverlay = this.buildAcpToolOverlay(effectiveCwd);
2297
2438
  let result;
2298
2439
  try {
2299
2440
  result = await createAgentSession({
2300
2441
  cwd: effectiveCwd,
2301
2442
  resourceLoader,
2302
- ...acpReadOverlay ? {
2303
- tools: acpReadOverlay.tools,
2304
- customTools: acpReadOverlay.customTools
2443
+ ...acpToolOverlay ? {
2444
+ tools: acpToolOverlay.tools,
2445
+ customTools: acpToolOverlay.customTools
2305
2446
  } : {}
2306
2447
  });
2307
2448
  } catch (e) {
@@ -2312,7 +2453,7 @@ var PiAcpAgent = class {
2312
2453
  throw RequestError.internalError({}, `Failed to create pi session: ${msg}`);
2313
2454
  }
2314
2455
  const piSession = result.session;
2315
- if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2456
+ if (acpToolOverlay !== null) acpToolOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2316
2457
  if (piSession.modelRegistry.getAvailable().length === 0) {
2317
2458
  piSession.dispose();
2318
2459
  modeResult.cleanup();
@@ -2575,18 +2716,18 @@ var PiAcpAgent = class {
2575
2716
  this.sessions.close(params.sessionId);
2576
2717
  const sessionFile = await this.resolveSessionFile(params.sessionId);
2577
2718
  if (sessionFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
2578
- const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
2719
+ const acpToolOverlay = this.buildAcpToolOverlay(params.cwd);
2579
2720
  let result;
2580
2721
  try {
2581
2722
  const sm = SessionManager.open(sessionFile);
2582
- const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2723
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
2583
2724
  result = await createAgentSession({
2584
2725
  cwd: params.cwd,
2585
2726
  sessionManager: sm,
2586
2727
  resourceLoader,
2587
- ...acpReadOverlay ? {
2588
- tools: acpReadOverlay.tools,
2589
- customTools: acpReadOverlay.customTools
2728
+ ...acpToolOverlay ? {
2729
+ tools: acpToolOverlay.tools,
2730
+ customTools: acpToolOverlay.customTools
2590
2731
  } : {}
2591
2732
  });
2592
2733
  } catch (e) {
@@ -2596,7 +2737,7 @@ var PiAcpAgent = class {
2596
2737
  throw RequestError.internalError({}, `Failed to load pi session: ${msg}`);
2597
2738
  }
2598
2739
  const piSession = result.session;
2599
- if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2740
+ if (acpToolOverlay !== null) acpToolOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2600
2741
  const session = new PiAcpSession({
2601
2742
  sessionId: params.sessionId,
2602
2743
  cwd: params.cwd,
@@ -2681,18 +2822,18 @@ var PiAcpAgent = class {
2681
2822
  }
2682
2823
  const sessionFile = await this.resolveSessionFile(params.sessionId);
2683
2824
  if (sessionFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
2684
- const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
2825
+ const acpToolOverlay = this.buildAcpToolOverlay(params.cwd);
2685
2826
  let result;
2686
2827
  try {
2687
2828
  const sm = SessionManager.open(sessionFile);
2688
- const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2829
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
2689
2830
  result = await createAgentSession({
2690
2831
  cwd: params.cwd,
2691
2832
  sessionManager: sm,
2692
2833
  resourceLoader,
2693
- ...acpReadOverlay ? {
2694
- tools: acpReadOverlay.tools,
2695
- customTools: acpReadOverlay.customTools
2834
+ ...acpToolOverlay ? {
2835
+ tools: acpToolOverlay.tools,
2836
+ customTools: acpToolOverlay.customTools
2696
2837
  } : {}
2697
2838
  });
2698
2839
  } catch (e) {
@@ -2702,7 +2843,7 @@ var PiAcpAgent = class {
2702
2843
  throw RequestError.internalError({}, `Failed to resume pi session: ${msg}`);
2703
2844
  }
2704
2845
  const piSession = result.session;
2705
- if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2846
+ if (acpToolOverlay !== null) acpToolOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2706
2847
  const session = new PiAcpSession({
2707
2848
  sessionId: params.sessionId,
2708
2849
  cwd: params.cwd,
@@ -2746,18 +2887,18 @@ var PiAcpAgent = class {
2746
2887
  if (!isAbsolute(params.cwd)) throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
2747
2888
  const sourceFile = await this.resolveSessionFile(params.sessionId);
2748
2889
  if (sourceFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
2749
- const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
2890
+ const acpToolOverlay = this.buildAcpToolOverlay(params.cwd);
2750
2891
  let result;
2751
2892
  try {
2752
2893
  const sm = SessionManager.forkFrom(sourceFile, params.cwd);
2753
- const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2894
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
2754
2895
  result = await createAgentSession({
2755
2896
  cwd: params.cwd,
2756
2897
  sessionManager: sm,
2757
2898
  resourceLoader,
2758
- ...acpReadOverlay ? {
2759
- tools: acpReadOverlay.tools,
2760
- customTools: acpReadOverlay.customTools
2899
+ ...acpToolOverlay ? {
2900
+ tools: acpToolOverlay.tools,
2901
+ customTools: acpToolOverlay.customTools
2761
2902
  } : {}
2762
2903
  });
2763
2904
  } catch (e) {
@@ -2768,7 +2909,7 @@ var PiAcpAgent = class {
2768
2909
  }
2769
2910
  const piSession = result.session;
2770
2911
  const newSessionId = piSession.sessionManager.getSessionId();
2771
- if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = newSessionId;
2912
+ if (acpToolOverlay !== null) acpToolOverlay.sessionIdRef.current = newSessionId;
2772
2913
  const newSessionFile = piSession.sessionManager.getSessionFile();
2773
2914
  if (newSessionFile !== void 0) this.sessionPaths.set(newSessionId, newSessionFile);
2774
2915
  const session = new PiAcpSession({
@@ -3387,4 +3528,4 @@ async function runDaemon() {
3387
3528
  //#endregion
3388
3529
  export { runDaemon };
3389
3530
 
3390
- //# sourceMappingURL=daemon-BdW4nUzA.mjs.map
3531
+ //# sourceMappingURL=daemon-CjPR14E_.mjs.map