@victor-software-house/pi-acp 0.11.0 → 0.12.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.
@@ -2,8 +2,8 @@
2
2
  import { a as removeStaleSocketIfAny, i as releaseLock, n as controlSocketPath, o as socketPath, r as ensureSocketParentDir, t as acquireLock } from "./socket-wvV053VI.mjs";
3
3
  import { t as piChangelogPath } from "./pi-package-aHs6rWNo.mjs";
4
4
  import { createServer } from "node:net";
5
- import { existsSync, readFileSync } from "node:fs";
6
- import { homedir } from "node:os";
5
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
6
+ import { homedir, tmpdir } from "node:os";
7
7
  import { isAbsolute, join, resolve } from "node:path";
8
8
  import { Hono } from "hono";
9
9
  import { AgentSideConnection, RequestError, ndJsonStream } from "@agentclientprotocol/sdk";
@@ -876,6 +876,7 @@ var PiAcpSession = class {
876
876
  lastAssistantStopReason = null;
877
877
  lastEmit = Promise.resolve();
878
878
  unsubscribe;
879
+ cleanups;
879
880
  constructor(opts) {
880
881
  this.sessionId = opts.sessionId;
881
882
  this.cwd = opts.cwd;
@@ -883,11 +884,15 @@ var PiAcpSession = class {
883
884
  this.piSession = opts.piSession;
884
885
  this.conn = opts.conn;
885
886
  this.supportsTerminalOutput = opts.supportsTerminalOutput ?? false;
887
+ this.cleanups = opts.cleanups ?? [];
886
888
  this.unsubscribe = this.piSession.subscribe((ev) => this.handlePiEvent(ev));
887
889
  }
888
890
  dispose() {
889
891
  this.unsubscribe?.();
890
892
  this.piSession.dispose();
893
+ for (const cleanup of this.cleanups) try {
894
+ cleanup();
895
+ } catch {}
891
896
  }
892
897
  /**
893
898
  * Drop event subscription without disposing the underlying piSession.
@@ -1679,6 +1684,58 @@ function tryFromFile(path, source, diagnostics) {
1679
1684
  return null;
1680
1685
  }
1681
1686
  //#endregion
1687
+ //#region src/resources/modes.ts
1688
+ /**
1689
+ * Cwd-independence modes (PRD-002 §FR-5).
1690
+ *
1691
+ * | Mode | cwd handling | Tool target |
1692
+ * |-----------|--------------------------------------------|---------------|
1693
+ * | `local` | ACP params.cwd (must be absolute) | params.cwd |
1694
+ * | `overlay` | same as local — manifest aux roots compose | params.cwd |
1695
+ * | `none` | substitute ephemeral tmpdir | tmpdir |
1696
+ *
1697
+ * `local` and `overlay` are functionally identical in the current substrate
1698
+ * because `VirtualResourceLoader` always overlays manifest roots on top of
1699
+ * the implicit local root. The `overlay` keyword is retained for
1700
+ * forward-compatibility and operator clarity (declaring `mode: overlay`
1701
+ * documents the intent even if the runtime path is the same).
1702
+ *
1703
+ * `none` mints `mkdtemp(...)` under the OS tmpdir and returns a cleanup
1704
+ * thunk the caller must invoke at session close. The cleanup is
1705
+ * best-effort — never throws — so a session dispose path that runs it
1706
+ * after the directory has already been removed is safe.
1707
+ */
1708
+ const TMPDIR_PREFIX = "pi-acp-session-";
1709
+ function resolveMode(input) {
1710
+ const mode = input.manifest.mode;
1711
+ if (mode === "none") {
1712
+ const dir = mkdtempSync(join(tmpdir(), TMPDIR_PREFIX));
1713
+ let removed = false;
1714
+ const cleanup = () => {
1715
+ if (removed) return;
1716
+ removed = true;
1717
+ try {
1718
+ rmSync(dir, {
1719
+ recursive: true,
1720
+ force: true
1721
+ });
1722
+ } catch {}
1723
+ };
1724
+ return {
1725
+ mode,
1726
+ cwd: dir,
1727
+ cleanup,
1728
+ ephemeral: true
1729
+ };
1730
+ }
1731
+ return {
1732
+ mode,
1733
+ cwd: input.requestedCwd,
1734
+ cleanup: () => {},
1735
+ ephemeral: false
1736
+ };
1737
+ }
1738
+ //#endregion
1682
1739
  //#region src/resources/sources/http.ts
1683
1740
  const DEFAULT_CACHE_TTL_SECONDS = 300;
1684
1741
  const DEFAULT_TIMEOUT_MS$1 = 5e3;
@@ -1897,7 +1954,7 @@ var SshBackend = class {
1897
1954
  //#endregion
1898
1955
  //#region package.json
1899
1956
  var name = "@victor-software-house/pi-acp";
1900
- var version = "0.11.0";
1957
+ var version = "0.12.0";
1901
1958
  //#endregion
1902
1959
  //#region src/acp/agent.ts
1903
1960
  /** Builtin ACP slash commands handled directly by the adapter. */
@@ -2034,13 +2091,18 @@ var PiAcpAgent = class {
2034
2091
  cwd,
2035
2092
  sessionParams
2036
2093
  });
2094
+ const modeResult = resolveMode({
2095
+ manifest: loaded.manifest,
2096
+ requestedCwd: cwd
2097
+ });
2098
+ const effectiveCwd = modeResult.cwd;
2037
2099
  const diagnostics = [...loaded.diagnostics];
2038
2100
  const sources = [];
2039
2101
  for (const root of loaded.manifest.roots) {
2040
2102
  if (root.kind === "local") {
2041
2103
  sources.push(new LocalBackend({
2042
2104
  id: root.id,
2043
- cwd: root.paths.cwd ?? cwd,
2105
+ cwd: root.paths.cwd ?? effectiveCwd,
2044
2106
  agentDir: root.paths.agentDir ?? getAgentDir()
2045
2107
  }));
2046
2108
  continue;
@@ -2073,7 +2135,7 @@ var PiAcpAgent = class {
2073
2135
  diagnostics.push(diag);
2074
2136
  }
2075
2137
  if (!sources.some((s) => s.kind === "local")) sources.unshift(new LocalBackend({
2076
- cwd,
2138
+ cwd: effectiveCwd,
2077
2139
  agentDir: getAgentDir()
2078
2140
  }));
2079
2141
  const loader = new VirtualResourceLoader({
@@ -2085,7 +2147,10 @@ var PiAcpAgent = class {
2085
2147
  const where = d.path !== void 0 ? ` ${d.path}` : "";
2086
2148
  process.stderr.write(`pi-acp manifest [${d.source}${where}]: ${d.message}\n`);
2087
2149
  }
2088
- return loader;
2150
+ return {
2151
+ loader,
2152
+ modeResult
2153
+ };
2089
2154
  }
2090
2155
  /**
2091
2156
  * PRD-002 §FR-6 — `read` tool ACP-FS delegation overlay.
@@ -2158,13 +2223,22 @@ var PiAcpAgent = class {
2158
2223
  };
2159
2224
  }
2160
2225
  async newSession(params) {
2161
- if (!isAbsolute(params.cwd)) throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
2162
- const acpReadOverlay = this.buildAcpReadOverlay(params.cwd);
2226
+ const { loader: resourceLoader, modeResult } = await this.buildResourceLoader(params.cwd, params).catch((e) => {
2227
+ const authErr = detectAuthError(e);
2228
+ if (authErr !== null) throw authErr;
2229
+ const msg = e instanceof Error ? e.message : String(e);
2230
+ throw RequestError.internalError({}, `Failed to load pi-acp manifest: ${msg}`);
2231
+ });
2232
+ const effectiveCwd = modeResult.cwd;
2233
+ if (modeResult.mode !== "none" && !isAbsolute(params.cwd)) {
2234
+ modeResult.cleanup();
2235
+ throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
2236
+ }
2237
+ const acpReadOverlay = this.buildAcpReadOverlay(effectiveCwd);
2163
2238
  let result;
2164
2239
  try {
2165
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2166
2240
  result = await createAgentSession({
2167
- cwd: params.cwd,
2241
+ cwd: effectiveCwd,
2168
2242
  resourceLoader,
2169
2243
  ...acpReadOverlay ? {
2170
2244
  tools: acpReadOverlay.tools,
@@ -2172,6 +2246,7 @@ var PiAcpAgent = class {
2172
2246
  } : {}
2173
2247
  });
2174
2248
  } catch (e) {
2249
+ modeResult.cleanup();
2175
2250
  const authErr = detectAuthError(e);
2176
2251
  if (authErr !== null) throw authErr;
2177
2252
  const msg = e instanceof Error ? e.message : String(e);
@@ -2181,6 +2256,7 @@ var PiAcpAgent = class {
2181
2256
  if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2182
2257
  if (piSession.modelRegistry.getAvailable().length === 0) {
2183
2258
  piSession.dispose();
2259
+ modeResult.cleanup();
2184
2260
  throw RequestError.authRequired({ authMethods: buildAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
2185
2261
  }
2186
2262
  const sessionId = piSession.sessionManager.getSessionId();
@@ -2188,11 +2264,12 @@ var PiAcpAgent = class {
2188
2264
  if (sessionFile !== void 0) this.sessionPaths.set(sessionId, sessionFile);
2189
2265
  const session = new PiAcpSession({
2190
2266
  sessionId,
2191
- cwd: params.cwd,
2267
+ cwd: effectiveCwd,
2192
2268
  mcpServers: params.mcpServers,
2193
2269
  piSession,
2194
2270
  conn: this.conn,
2195
- supportsTerminalOutput: this.clientCapabilities.terminalOutput
2271
+ supportsTerminalOutput: this.clientCapabilities.terminalOutput,
2272
+ cleanups: modeResult.ephemeral ? [modeResult.cleanup] : []
2196
2273
  });
2197
2274
  this.sessions.register(session);
2198
2275
  this.registerWithDaemon({
@@ -2442,7 +2519,7 @@ var PiAcpAgent = class {
2442
2519
  let result;
2443
2520
  try {
2444
2521
  const sm = SessionManager.open(sessionFile);
2445
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2522
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2446
2523
  result = await createAgentSession({
2447
2524
  cwd: params.cwd,
2448
2525
  sessionManager: sm,
@@ -2548,7 +2625,7 @@ var PiAcpAgent = class {
2548
2625
  let result;
2549
2626
  try {
2550
2627
  const sm = SessionManager.open(sessionFile);
2551
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2628
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2552
2629
  result = await createAgentSession({
2553
2630
  cwd: params.cwd,
2554
2631
  sessionManager: sm,
@@ -2613,7 +2690,7 @@ var PiAcpAgent = class {
2613
2690
  let result;
2614
2691
  try {
2615
2692
  const sm = SessionManager.forkFrom(sourceFile, params.cwd);
2616
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2693
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2617
2694
  result = await createAgentSession({
2618
2695
  cwd: params.cwd,
2619
2696
  sessionManager: sm,
@@ -3250,4 +3327,4 @@ async function runDaemon() {
3250
3327
  //#endregion
3251
3328
  export { runDaemon };
3252
3329
 
3253
- //# sourceMappingURL=daemon-COFaIaTB.mjs.map
3330
+ //# sourceMappingURL=daemon-D6QKWz5C.mjs.map