@victor-software-house/pi-acp 0.12.0 → 0.13.1
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 (
|
|
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 #
|
|
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/
|
|
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
|
|
@@ -877,6 +877,7 @@ var PiAcpSession = class {
|
|
|
877
877
|
lastEmit = Promise.resolve();
|
|
878
878
|
unsubscribe;
|
|
879
879
|
cleanups;
|
|
880
|
+
pendingDiagnosticsReport;
|
|
880
881
|
constructor(opts) {
|
|
881
882
|
this.sessionId = opts.sessionId;
|
|
882
883
|
this.cwd = opts.cwd;
|
|
@@ -885,6 +886,7 @@ var PiAcpSession = class {
|
|
|
885
886
|
this.conn = opts.conn;
|
|
886
887
|
this.supportsTerminalOutput = opts.supportsTerminalOutput ?? false;
|
|
887
888
|
this.cleanups = opts.cleanups ?? [];
|
|
889
|
+
this.pendingDiagnosticsReport = opts.diagnosticsReport !== void 0 && opts.diagnosticsReport !== "" ? opts.diagnosticsReport : null;
|
|
888
890
|
this.unsubscribe = this.piSession.subscribe((ev) => this.handlePiEvent(ev));
|
|
889
891
|
}
|
|
890
892
|
dispose() {
|
|
@@ -930,6 +932,20 @@ var PiAcpSession = class {
|
|
|
930
932
|
};
|
|
931
933
|
});
|
|
932
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
|
+
}
|
|
933
949
|
this.piSession.prompt(message, { images: imageContents }).catch(() => {
|
|
934
950
|
this.flushEmits().finally(() => {
|
|
935
951
|
const reason = this.cancelRequested ? "cancelled" : "error";
|
|
@@ -1345,6 +1361,43 @@ function acpPromptToPiMessage(blocks) {
|
|
|
1345
1361
|
};
|
|
1346
1362
|
}
|
|
1347
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
|
|
1348
1401
|
//#region src/resources/sources/local.ts
|
|
1349
1402
|
/**
|
|
1350
1403
|
* LocalBackend: wraps pi's DefaultResourceLoader for one (cwd, agentDir) root.
|
|
@@ -1954,7 +2007,7 @@ var SshBackend = class {
|
|
|
1954
2007
|
//#endregion
|
|
1955
2008
|
//#region package.json
|
|
1956
2009
|
var name = "@victor-software-house/pi-acp";
|
|
1957
|
-
var version = "0.
|
|
2010
|
+
var version = "0.13.1";
|
|
1958
2011
|
//#endregion
|
|
1959
2012
|
//#region src/acp/agent.ts
|
|
1960
2013
|
/** Builtin ACP slash commands handled directly by the adapter. */
|
|
@@ -2086,15 +2139,20 @@ var PiAcpAgent = class {
|
|
|
2086
2139
|
* Phase 7 adds `kind: "http"`. `acp-fs` still parses fine but surfaces as
|
|
2087
2140
|
* a diagnostic until its backend lands in a subsequent phase.
|
|
2088
2141
|
*/
|
|
2089
|
-
async buildResourceLoader(cwd, sessionParams) {
|
|
2142
|
+
async buildResourceLoader(cwd, sessionParams, opts) {
|
|
2090
2143
|
const loaded = await loadManifest({
|
|
2091
2144
|
cwd,
|
|
2092
2145
|
sessionParams
|
|
2093
2146
|
});
|
|
2094
|
-
const modeResult = resolveMode({
|
|
2147
|
+
const modeResult = opts?.resolveCwdMode !== false ? resolveMode({
|
|
2095
2148
|
manifest: loaded.manifest,
|
|
2096
2149
|
requestedCwd: cwd
|
|
2097
|
-
})
|
|
2150
|
+
}) : {
|
|
2151
|
+
mode: loaded.manifest.mode,
|
|
2152
|
+
cwd,
|
|
2153
|
+
cleanup: () => {},
|
|
2154
|
+
ephemeral: false
|
|
2155
|
+
};
|
|
2098
2156
|
const effectiveCwd = modeResult.cwd;
|
|
2099
2157
|
const diagnostics = [...loaded.diagnostics];
|
|
2100
2158
|
const sources = [];
|
|
@@ -2149,7 +2207,9 @@ var PiAcpAgent = class {
|
|
|
2149
2207
|
}
|
|
2150
2208
|
return {
|
|
2151
2209
|
loader,
|
|
2152
|
-
modeResult
|
|
2210
|
+
modeResult,
|
|
2211
|
+
diagnosticsEnabled: loaded.manifest.diagnostics === true,
|
|
2212
|
+
manifestDiagnostics: diagnostics
|
|
2153
2213
|
};
|
|
2154
2214
|
}
|
|
2155
2215
|
/**
|
|
@@ -2223,7 +2283,7 @@ var PiAcpAgent = class {
|
|
|
2223
2283
|
};
|
|
2224
2284
|
}
|
|
2225
2285
|
async newSession(params) {
|
|
2226
|
-
const { loader: resourceLoader, modeResult } = await this.buildResourceLoader(params.cwd, params).catch((e) => {
|
|
2286
|
+
const { loader: resourceLoader, modeResult, diagnosticsEnabled, manifestDiagnostics } = await this.buildResourceLoader(params.cwd, params).catch((e) => {
|
|
2227
2287
|
const authErr = detectAuthError(e);
|
|
2228
2288
|
if (authErr !== null) throw authErr;
|
|
2229
2289
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -2234,6 +2294,10 @@ var PiAcpAgent = class {
|
|
|
2234
2294
|
modeResult.cleanup();
|
|
2235
2295
|
throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
|
|
2236
2296
|
}
|
|
2297
|
+
const diagnosticsReport = diagnosticsEnabled ? buildDiagnosticsReport({
|
|
2298
|
+
sources: resourceLoader.listSources(),
|
|
2299
|
+
manifestDiagnostics
|
|
2300
|
+
}).text : "";
|
|
2237
2301
|
const acpReadOverlay = this.buildAcpReadOverlay(effectiveCwd);
|
|
2238
2302
|
let result;
|
|
2239
2303
|
try {
|
|
@@ -2269,7 +2333,8 @@ var PiAcpAgent = class {
|
|
|
2269
2333
|
piSession,
|
|
2270
2334
|
conn: this.conn,
|
|
2271
2335
|
supportsTerminalOutput: this.clientCapabilities.terminalOutput,
|
|
2272
|
-
cleanups: modeResult.ephemeral ? [modeResult.cleanup] : []
|
|
2336
|
+
cleanups: modeResult.ephemeral ? [modeResult.cleanup] : [],
|
|
2337
|
+
...diagnosticsReport !== "" ? { diagnosticsReport } : {}
|
|
2273
2338
|
});
|
|
2274
2339
|
this.sessions.register(session);
|
|
2275
2340
|
this.registerWithDaemon({
|
|
@@ -2519,7 +2584,7 @@ var PiAcpAgent = class {
|
|
|
2519
2584
|
let result;
|
|
2520
2585
|
try {
|
|
2521
2586
|
const sm = SessionManager.open(sessionFile);
|
|
2522
|
-
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
|
|
2587
|
+
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
|
|
2523
2588
|
result = await createAgentSession({
|
|
2524
2589
|
cwd: params.cwd,
|
|
2525
2590
|
sessionManager: sm,
|
|
@@ -2625,7 +2690,7 @@ var PiAcpAgent = class {
|
|
|
2625
2690
|
let result;
|
|
2626
2691
|
try {
|
|
2627
2692
|
const sm = SessionManager.open(sessionFile);
|
|
2628
|
-
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
|
|
2693
|
+
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
|
|
2629
2694
|
result = await createAgentSession({
|
|
2630
2695
|
cwd: params.cwd,
|
|
2631
2696
|
sessionManager: sm,
|
|
@@ -2690,7 +2755,7 @@ var PiAcpAgent = class {
|
|
|
2690
2755
|
let result;
|
|
2691
2756
|
try {
|
|
2692
2757
|
const sm = SessionManager.forkFrom(sourceFile, params.cwd);
|
|
2693
|
-
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params);
|
|
2758
|
+
const { loader: resourceLoader } = await this.buildResourceLoader(params.cwd, params, { resolveCwdMode: false });
|
|
2694
2759
|
result = await createAgentSession({
|
|
2695
2760
|
cwd: params.cwd,
|
|
2696
2761
|
sessionManager: sm,
|
|
@@ -3327,4 +3392,4 @@ async function runDaemon() {
|
|
|
3327
3392
|
//#endregion
|
|
3328
3393
|
export { runDaemon };
|
|
3329
3394
|
|
|
3330
|
-
//# sourceMappingURL=daemon-
|
|
3395
|
+
//# sourceMappingURL=daemon-BErbUhcE.mjs.map
|