@victor-software-house/pi-acp 0.11.0 → 0.13.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,8 @@ var PiAcpSession = class {
876
876
  lastAssistantStopReason = null;
877
877
  lastEmit = Promise.resolve();
878
878
  unsubscribe;
879
+ cleanups;
880
+ pendingDiagnosticsReport;
879
881
  constructor(opts) {
880
882
  this.sessionId = opts.sessionId;
881
883
  this.cwd = opts.cwd;
@@ -883,11 +885,16 @@ var PiAcpSession = class {
883
885
  this.piSession = opts.piSession;
884
886
  this.conn = opts.conn;
885
887
  this.supportsTerminalOutput = opts.supportsTerminalOutput ?? false;
888
+ this.cleanups = opts.cleanups ?? [];
889
+ this.pendingDiagnosticsReport = opts.diagnosticsReport !== void 0 && opts.diagnosticsReport !== "" ? opts.diagnosticsReport : null;
886
890
  this.unsubscribe = this.piSession.subscribe((ev) => this.handlePiEvent(ev));
887
891
  }
888
892
  dispose() {
889
893
  this.unsubscribe?.();
890
894
  this.piSession.dispose();
895
+ for (const cleanup of this.cleanups) try {
896
+ cleanup();
897
+ } catch {}
891
898
  }
892
899
  /**
893
900
  * Drop event subscription without disposing the underlying piSession.
@@ -925,6 +932,20 @@ var PiAcpSession = class {
925
932
  };
926
933
  });
927
934
  const imageContents = Array.isArray(images) ? images.filter((img) => typeof img === "object" && img !== null && "type" in img && img.type === "image") : [];
935
+ if (this.pendingDiagnosticsReport !== null) {
936
+ const report = this.pendingDiagnosticsReport;
937
+ this.pendingDiagnosticsReport = null;
938
+ this.conn.sessionUpdate({
939
+ sessionId: this.sessionId,
940
+ update: {
941
+ sessionUpdate: "agent_message_chunk",
942
+ content: {
943
+ type: "text",
944
+ text: `${report}\n`
945
+ }
946
+ }
947
+ }).catch(() => {});
948
+ }
928
949
  this.piSession.prompt(message, { images: imageContents }).catch(() => {
929
950
  this.flushEmits().finally(() => {
930
951
  const reason = this.cancelRequested ? "cancelled" : "error";
@@ -1340,6 +1361,43 @@ function acpPromptToPiMessage(blocks) {
1340
1361
  };
1341
1362
  }
1342
1363
  //#endregion
1364
+ //#region src/resources/diagnostics.ts
1365
+ function buildDiagnosticsReport(input) {
1366
+ const sourceStats = input.sources.map((source) => {
1367
+ const skills = source.getSkills();
1368
+ const prompts = source.getPrompts();
1369
+ const failures = skills.diagnostics.filter((d) => d.type === "warning" || d.type === "error").map((d) => d.message);
1370
+ return {
1371
+ id: source.id,
1372
+ kind: source.kind,
1373
+ agentsFiles: source.getAgentsFiles().length,
1374
+ skills: skills.skills.length,
1375
+ prompts: prompts.prompts.length,
1376
+ failures
1377
+ };
1378
+ });
1379
+ const lines = [];
1380
+ if (sourceStats.length > 0) {
1381
+ lines.push("[pi-acp] resources active:");
1382
+ for (const s of sourceStats) lines.push(` ${s.id.padEnd(20)} kind=${s.kind} (${s.agentsFiles} AGENTS files, ${s.skills} skills, ${s.prompts} prompts)`);
1383
+ }
1384
+ const allFailures = [];
1385
+ for (const s of sourceStats) for (const f of s.failures) allFailures.push(` ${s.id.padEnd(20)} ${f}`);
1386
+ for (const d of input.manifestDiagnostics) {
1387
+ const where = d.path !== void 0 ? ` ${d.path}` : "";
1388
+ allFailures.push(` manifest[${d.source}${where}] ${d.message}`);
1389
+ }
1390
+ if (allFailures.length > 0) {
1391
+ if (lines.length > 0) lines.push("");
1392
+ lines.push("[pi-acp] resource failures:");
1393
+ lines.push(...allFailures);
1394
+ }
1395
+ return {
1396
+ text: lines.join("\n"),
1397
+ sourceStats
1398
+ };
1399
+ }
1400
+ //#endregion
1343
1401
  //#region src/resources/sources/local.ts
1344
1402
  /**
1345
1403
  * LocalBackend: wraps pi's DefaultResourceLoader for one (cwd, agentDir) root.
@@ -1679,6 +1737,58 @@ function tryFromFile(path, source, diagnostics) {
1679
1737
  return null;
1680
1738
  }
1681
1739
  //#endregion
1740
+ //#region src/resources/modes.ts
1741
+ /**
1742
+ * Cwd-independence modes (PRD-002 §FR-5).
1743
+ *
1744
+ * | Mode | cwd handling | Tool target |
1745
+ * |-----------|--------------------------------------------|---------------|
1746
+ * | `local` | ACP params.cwd (must be absolute) | params.cwd |
1747
+ * | `overlay` | same as local — manifest aux roots compose | params.cwd |
1748
+ * | `none` | substitute ephemeral tmpdir | tmpdir |
1749
+ *
1750
+ * `local` and `overlay` are functionally identical in the current substrate
1751
+ * because `VirtualResourceLoader` always overlays manifest roots on top of
1752
+ * the implicit local root. The `overlay` keyword is retained for
1753
+ * forward-compatibility and operator clarity (declaring `mode: overlay`
1754
+ * documents the intent even if the runtime path is the same).
1755
+ *
1756
+ * `none` mints `mkdtemp(...)` under the OS tmpdir and returns a cleanup
1757
+ * thunk the caller must invoke at session close. The cleanup is
1758
+ * best-effort — never throws — so a session dispose path that runs it
1759
+ * after the directory has already been removed is safe.
1760
+ */
1761
+ const TMPDIR_PREFIX = "pi-acp-session-";
1762
+ function resolveMode(input) {
1763
+ const mode = input.manifest.mode;
1764
+ if (mode === "none") {
1765
+ const dir = mkdtempSync(join(tmpdir(), TMPDIR_PREFIX));
1766
+ let removed = false;
1767
+ const cleanup = () => {
1768
+ if (removed) return;
1769
+ removed = true;
1770
+ try {
1771
+ rmSync(dir, {
1772
+ recursive: true,
1773
+ force: true
1774
+ });
1775
+ } catch {}
1776
+ };
1777
+ return {
1778
+ mode,
1779
+ cwd: dir,
1780
+ cleanup,
1781
+ ephemeral: true
1782
+ };
1783
+ }
1784
+ return {
1785
+ mode,
1786
+ cwd: input.requestedCwd,
1787
+ cleanup: () => {},
1788
+ ephemeral: false
1789
+ };
1790
+ }
1791
+ //#endregion
1682
1792
  //#region src/resources/sources/http.ts
1683
1793
  const DEFAULT_CACHE_TTL_SECONDS = 300;
1684
1794
  const DEFAULT_TIMEOUT_MS$1 = 5e3;
@@ -1897,7 +2007,7 @@ var SshBackend = class {
1897
2007
  //#endregion
1898
2008
  //#region package.json
1899
2009
  var name = "@victor-software-house/pi-acp";
1900
- var version = "0.11.0";
2010
+ var version = "0.13.0";
1901
2011
  //#endregion
1902
2012
  //#region src/acp/agent.ts
1903
2013
  /** Builtin ACP slash commands handled directly by the adapter. */
@@ -2034,13 +2144,18 @@ var PiAcpAgent = class {
2034
2144
  cwd,
2035
2145
  sessionParams
2036
2146
  });
2147
+ const modeResult = resolveMode({
2148
+ manifest: loaded.manifest,
2149
+ requestedCwd: cwd
2150
+ });
2151
+ const effectiveCwd = modeResult.cwd;
2037
2152
  const diagnostics = [...loaded.diagnostics];
2038
2153
  const sources = [];
2039
2154
  for (const root of loaded.manifest.roots) {
2040
2155
  if (root.kind === "local") {
2041
2156
  sources.push(new LocalBackend({
2042
2157
  id: root.id,
2043
- cwd: root.paths.cwd ?? cwd,
2158
+ cwd: root.paths.cwd ?? effectiveCwd,
2044
2159
  agentDir: root.paths.agentDir ?? getAgentDir()
2045
2160
  }));
2046
2161
  continue;
@@ -2073,7 +2188,7 @@ var PiAcpAgent = class {
2073
2188
  diagnostics.push(diag);
2074
2189
  }
2075
2190
  if (!sources.some((s) => s.kind === "local")) sources.unshift(new LocalBackend({
2076
- cwd,
2191
+ cwd: effectiveCwd,
2077
2192
  agentDir: getAgentDir()
2078
2193
  }));
2079
2194
  const loader = new VirtualResourceLoader({
@@ -2085,7 +2200,12 @@ var PiAcpAgent = class {
2085
2200
  const where = d.path !== void 0 ? ` ${d.path}` : "";
2086
2201
  process.stderr.write(`pi-acp manifest [${d.source}${where}]: ${d.message}\n`);
2087
2202
  }
2088
- return loader;
2203
+ return {
2204
+ loader,
2205
+ modeResult,
2206
+ diagnosticsEnabled: loaded.manifest.diagnostics === true,
2207
+ manifestDiagnostics: diagnostics
2208
+ };
2089
2209
  }
2090
2210
  /**
2091
2211
  * PRD-002 §FR-6 — `read` tool ACP-FS delegation overlay.
@@ -2158,13 +2278,26 @@ var PiAcpAgent = class {
2158
2278
  };
2159
2279
  }
2160
2280
  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);
2281
+ const { loader: resourceLoader, modeResult, diagnosticsEnabled, manifestDiagnostics } = await this.buildResourceLoader(params.cwd, params).catch((e) => {
2282
+ const authErr = detectAuthError(e);
2283
+ if (authErr !== null) throw authErr;
2284
+ const msg = e instanceof Error ? e.message : String(e);
2285
+ throw RequestError.internalError({}, `Failed to load pi-acp manifest: ${msg}`);
2286
+ });
2287
+ const effectiveCwd = modeResult.cwd;
2288
+ if (modeResult.mode !== "none" && !isAbsolute(params.cwd)) {
2289
+ modeResult.cleanup();
2290
+ throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
2291
+ }
2292
+ const diagnosticsReport = diagnosticsEnabled ? buildDiagnosticsReport({
2293
+ sources: resourceLoader.listSources(),
2294
+ manifestDiagnostics
2295
+ }).text : "";
2296
+ const acpReadOverlay = this.buildAcpReadOverlay(effectiveCwd);
2163
2297
  let result;
2164
2298
  try {
2165
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2166
2299
  result = await createAgentSession({
2167
- cwd: params.cwd,
2300
+ cwd: effectiveCwd,
2168
2301
  resourceLoader,
2169
2302
  ...acpReadOverlay ? {
2170
2303
  tools: acpReadOverlay.tools,
@@ -2172,6 +2305,7 @@ var PiAcpAgent = class {
2172
2305
  } : {}
2173
2306
  });
2174
2307
  } catch (e) {
2308
+ modeResult.cleanup();
2175
2309
  const authErr = detectAuthError(e);
2176
2310
  if (authErr !== null) throw authErr;
2177
2311
  const msg = e instanceof Error ? e.message : String(e);
@@ -2181,6 +2315,7 @@ var PiAcpAgent = class {
2181
2315
  if (acpReadOverlay !== null) acpReadOverlay.sessionIdRef.current = piSession.sessionManager.getSessionId();
2182
2316
  if (piSession.modelRegistry.getAvailable().length === 0) {
2183
2317
  piSession.dispose();
2318
+ modeResult.cleanup();
2184
2319
  throw RequestError.authRequired({ authMethods: buildAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
2185
2320
  }
2186
2321
  const sessionId = piSession.sessionManager.getSessionId();
@@ -2188,11 +2323,13 @@ var PiAcpAgent = class {
2188
2323
  if (sessionFile !== void 0) this.sessionPaths.set(sessionId, sessionFile);
2189
2324
  const session = new PiAcpSession({
2190
2325
  sessionId,
2191
- cwd: params.cwd,
2326
+ cwd: effectiveCwd,
2192
2327
  mcpServers: params.mcpServers,
2193
2328
  piSession,
2194
2329
  conn: this.conn,
2195
- supportsTerminalOutput: this.clientCapabilities.terminalOutput
2330
+ supportsTerminalOutput: this.clientCapabilities.terminalOutput,
2331
+ cleanups: modeResult.ephemeral ? [modeResult.cleanup] : [],
2332
+ ...diagnosticsReport !== "" ? { diagnosticsReport } : {}
2196
2333
  });
2197
2334
  this.sessions.register(session);
2198
2335
  this.registerWithDaemon({
@@ -2442,7 +2579,7 @@ var PiAcpAgent = class {
2442
2579
  let result;
2443
2580
  try {
2444
2581
  const sm = SessionManager.open(sessionFile);
2445
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2582
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2446
2583
  result = await createAgentSession({
2447
2584
  cwd: params.cwd,
2448
2585
  sessionManager: sm,
@@ -2548,7 +2685,7 @@ var PiAcpAgent = class {
2548
2685
  let result;
2549
2686
  try {
2550
2687
  const sm = SessionManager.open(sessionFile);
2551
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2688
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2552
2689
  result = await createAgentSession({
2553
2690
  cwd: params.cwd,
2554
2691
  sessionManager: sm,
@@ -2613,7 +2750,7 @@ var PiAcpAgent = class {
2613
2750
  let result;
2614
2751
  try {
2615
2752
  const sm = SessionManager.forkFrom(sourceFile, params.cwd);
2616
- const resourceLoader = await this.buildResourceLoader(params.cwd, params);
2753
+ const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
2617
2754
  result = await createAgentSession({
2618
2755
  cwd: params.cwd,
2619
2756
  sessionManager: sm,
@@ -3250,4 +3387,4 @@ async function runDaemon() {
3250
3387
  //#endregion
3251
3388
  export { runDaemon };
3252
3389
 
3253
- //# sourceMappingURL=daemon-COFaIaTB.mjs.map
3390
+ //# sourceMappingURL=daemon-BdW4nUzA.mjs.map