@victor-software-house/pi-acp 0.14.0 → 0.16.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.
|
@@ -544,6 +544,86 @@ function skillCommandsEnabled(cwd) {
|
|
|
544
544
|
return true;
|
|
545
545
|
}
|
|
546
546
|
//#endregion
|
|
547
|
+
//#region src/acp/providers.ts
|
|
548
|
+
/**
|
|
549
|
+
* Map an ACP `LlmProtocol` to a pi `Api` identifier. Used when
|
|
550
|
+
* `unstable_setProvider` injects a provider whose `apiType` is one of the
|
|
551
|
+
* spec's enumerated protocols. Unknown protocols pass through verbatim
|
|
552
|
+
* (LlmProtocol is `KnownProtocol | string`).
|
|
553
|
+
*/
|
|
554
|
+
function acpProtocolToPiApi(protocol) {
|
|
555
|
+
switch (protocol) {
|
|
556
|
+
case "anthropic": return "anthropic-messages";
|
|
557
|
+
case "openai": return "openai-responses";
|
|
558
|
+
case "azure": return "azure-openai-responses";
|
|
559
|
+
case "vertex": return "google-vertex";
|
|
560
|
+
case "bedrock": return "bedrock-converse-stream";
|
|
561
|
+
default: return protocol;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Reverse map: pi `Api` → ACP `LlmProtocol`. Used by `listProviders` to
|
|
566
|
+
* describe each pi-known model's transport. Falls back to the raw `api`
|
|
567
|
+
* string when no canonical bucket matches.
|
|
568
|
+
*/
|
|
569
|
+
function piApiToAcpProtocol(api) {
|
|
570
|
+
if (api.startsWith("anthropic")) return "anthropic";
|
|
571
|
+
if (api.startsWith("openai")) return "openai";
|
|
572
|
+
if (api.startsWith("azure")) return "azure";
|
|
573
|
+
if (api.includes("vertex")) return "vertex";
|
|
574
|
+
if (api.startsWith("bedrock")) return "bedrock";
|
|
575
|
+
if (api.startsWith("google")) return "vertex";
|
|
576
|
+
return api;
|
|
577
|
+
}
|
|
578
|
+
function buildListProvidersResponse(deps) {
|
|
579
|
+
const first = firstRegistry(deps.registries());
|
|
580
|
+
if (first === void 0) return { providers: [] };
|
|
581
|
+
const byProvider = /* @__PURE__ */ new Map();
|
|
582
|
+
for (const model of first.getAll()) {
|
|
583
|
+
const entry = byProvider.get(model.provider) ?? {
|
|
584
|
+
baseUrls: /* @__PURE__ */ new Set(),
|
|
585
|
+
protocols: /* @__PURE__ */ new Set()
|
|
586
|
+
};
|
|
587
|
+
entry.baseUrls.add(model.baseUrl);
|
|
588
|
+
entry.protocols.add(piApiToAcpProtocol(model.api));
|
|
589
|
+
byProvider.set(model.provider, entry);
|
|
590
|
+
}
|
|
591
|
+
return { providers: Array.from(byProvider.entries()).map(([id, entry]) => {
|
|
592
|
+
const supported = Array.from(entry.protocols);
|
|
593
|
+
const primaryBaseUrl = Array.from(entry.baseUrls)[0] ?? "";
|
|
594
|
+
const primaryProtocol = supported[0] ?? "openai";
|
|
595
|
+
return {
|
|
596
|
+
id,
|
|
597
|
+
supported,
|
|
598
|
+
required: false,
|
|
599
|
+
current: deps.disabled.has(id) ? null : {
|
|
600
|
+
apiType: primaryProtocol,
|
|
601
|
+
baseUrl: primaryBaseUrl
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
}) };
|
|
605
|
+
}
|
|
606
|
+
function applySetProvider(deps, params) {
|
|
607
|
+
const config = {
|
|
608
|
+
baseUrl: params.baseUrl,
|
|
609
|
+
api: acpProtocolToPiApi(params.apiType),
|
|
610
|
+
...params.headers !== void 0 ? { headers: params.headers } : {}
|
|
611
|
+
};
|
|
612
|
+
for (const reg of deps.registries()) reg.registerProvider(params.id, config);
|
|
613
|
+
deps.disabled.delete(params.id);
|
|
614
|
+
return {};
|
|
615
|
+
}
|
|
616
|
+
function applyDisableProvider(deps, params) {
|
|
617
|
+
deps.disabled.add(params.id);
|
|
618
|
+
for (const reg of deps.registries()) try {
|
|
619
|
+
reg.unregisterProvider(params.id);
|
|
620
|
+
} catch {}
|
|
621
|
+
return {};
|
|
622
|
+
}
|
|
623
|
+
function firstRegistry(it) {
|
|
624
|
+
for (const reg of it) return reg;
|
|
625
|
+
}
|
|
626
|
+
//#endregion
|
|
547
627
|
//#region src/acp/translate/tool-content.ts
|
|
548
628
|
const textBlockSchema = z.object({
|
|
549
629
|
type: z.literal("text"),
|
|
@@ -2115,7 +2195,7 @@ var SshBackend = class {
|
|
|
2115
2195
|
//#endregion
|
|
2116
2196
|
//#region package.json
|
|
2117
2197
|
var name = "@victor-software-house/pi-acp";
|
|
2118
|
-
var version = "0.
|
|
2198
|
+
var version = "0.16.0";
|
|
2119
2199
|
//#endregion
|
|
2120
2200
|
//#region src/acp/agent.ts
|
|
2121
2201
|
/** Builtin ACP slash commands handled directly by the adapter. */
|
|
@@ -2213,6 +2293,12 @@ var PiAcpAgent = class {
|
|
|
2213
2293
|
connectionId = randomUUID();
|
|
2214
2294
|
extMethods;
|
|
2215
2295
|
startedAt = Date.now();
|
|
2296
|
+
/**
|
|
2297
|
+
* pi-acp-side soft-disable set for providers. Pi has only `unregister`
|
|
2298
|
+
* (destructive); we layer a disabled-set on top so `listProviders` can
|
|
2299
|
+
* report `current: null` per ACP spec even after disable.
|
|
2300
|
+
*/
|
|
2301
|
+
disabledProviders = /* @__PURE__ */ new Set();
|
|
2216
2302
|
dispose() {
|
|
2217
2303
|
if (this.daemonContext !== void 0) {
|
|
2218
2304
|
const registry = this.daemonContext.sessionRegistry;
|
|
@@ -2237,6 +2323,37 @@ var PiAcpAgent = class {
|
|
|
2237
2323
|
async extNotification(method, params) {
|
|
2238
2324
|
await this.extMethods.handleNotification(method, params);
|
|
2239
2325
|
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Iterable of every live `ModelRegistry` instance — local SessionManager
|
|
2328
|
+
* plus daemon SessionRegistry. Used by the providers/* methods to apply
|
|
2329
|
+
* mutations across all live sessions.
|
|
2330
|
+
*/
|
|
2331
|
+
liveModelRegistries() {
|
|
2332
|
+
const out = [];
|
|
2333
|
+
for (const s of this.sessions.values()) out.push(s.piSession.modelRegistry);
|
|
2334
|
+
if (this.daemonContext !== void 0) for (const e of this.daemonContext.sessionRegistry.listAll()) out.push(e.piSession.modelRegistry);
|
|
2335
|
+
return out;
|
|
2336
|
+
}
|
|
2337
|
+
async unstable_listProviders(_params) {
|
|
2338
|
+
return buildListProvidersResponse({
|
|
2339
|
+
registries: () => this.liveModelRegistries(),
|
|
2340
|
+
disabled: this.disabledProviders
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
async unstable_setProvider(params) {
|
|
2344
|
+
if (params.id === "") throw RequestError.invalidParams("provider id must be non-empty");
|
|
2345
|
+
return applySetProvider({
|
|
2346
|
+
registries: () => this.liveModelRegistries(),
|
|
2347
|
+
disabled: this.disabledProviders
|
|
2348
|
+
}, params);
|
|
2349
|
+
}
|
|
2350
|
+
async unstable_disableProvider(params) {
|
|
2351
|
+
if (params.id === "") throw RequestError.invalidParams("provider id must be non-empty");
|
|
2352
|
+
return applyDisableProvider({
|
|
2353
|
+
registries: () => this.liveModelRegistries(),
|
|
2354
|
+
disabled: this.disabledProviders
|
|
2355
|
+
}, params);
|
|
2356
|
+
}
|
|
2240
2357
|
registerWithDaemon(input) {
|
|
2241
2358
|
if (this.daemonContext === void 0) return;
|
|
2242
2359
|
this.daemonContext.sessionRegistry.register({
|
|
@@ -2413,8 +2530,10 @@ var PiAcpAgent = class {
|
|
|
2413
2530
|
list: {},
|
|
2414
2531
|
close: {},
|
|
2415
2532
|
resume: {},
|
|
2416
|
-
fork: {}
|
|
2417
|
-
|
|
2533
|
+
fork: {},
|
|
2534
|
+
delete: {}
|
|
2535
|
+
},
|
|
2536
|
+
providers: {}
|
|
2418
2537
|
}
|
|
2419
2538
|
};
|
|
2420
2539
|
}
|
|
@@ -2786,6 +2905,40 @@ var PiAcpAgent = class {
|
|
|
2786
2905
|
else if (local !== void 0) this.sessions.detach(params.sessionId);
|
|
2787
2906
|
return {};
|
|
2788
2907
|
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Deletes a session's on-disk file + releases any live state.
|
|
2910
|
+
*
|
|
2911
|
+
* Pi's SessionManager exposes no `delete()` method (verified against
|
|
2912
|
+
* session-manager.d.ts) — sessions are append-only JSONL files. We
|
|
2913
|
+
* unlink the file directly via `fs.rmSync`. `resolveSessionFile`
|
|
2914
|
+
* sources paths from `PiSessionManager.listAll`, so the unlinked path
|
|
2915
|
+
* is always inside `~/.pi/agent/sessions/...`.
|
|
2916
|
+
*
|
|
2917
|
+
* Refuses to delete sessions owned by ANOTHER connection in the daemon
|
|
2918
|
+
* registry — security boundary: clients may only delete sessions they
|
|
2919
|
+
* own or sessions that are not currently live. Always releases the
|
|
2920
|
+
* daemon registry entry first so the live piSession is disposed
|
|
2921
|
+
* cleanly before the file disappears.
|
|
2922
|
+
*
|
|
2923
|
+
* Gated by `sessionCapabilities.delete = {}` (advertised in initialize).
|
|
2924
|
+
*/
|
|
2925
|
+
async unstable_deleteSession(params) {
|
|
2926
|
+
const sessionFile = await this.resolveSessionFile(params.sessionId);
|
|
2927
|
+
if (sessionFile === null) throw RequestError.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2928
|
+
const live = this.daemonContext?.sessionRegistry.get(params.sessionId);
|
|
2929
|
+
if (live !== void 0 && live.ownerConnectionId !== this.connectionId) throw RequestError.invalidParams(`Session ${params.sessionId} is owned by another connection — cannot delete`);
|
|
2930
|
+
if (live !== void 0) {
|
|
2931
|
+
if (this.releaseFromDaemon(params.sessionId).disposed) this.sessions.close(params.sessionId);
|
|
2932
|
+
} else if (this.sessions.maybeGet(params.sessionId) !== void 0) this.sessions.close(params.sessionId);
|
|
2933
|
+
try {
|
|
2934
|
+
rmSync(sessionFile, { force: true });
|
|
2935
|
+
} catch (e) {
|
|
2936
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2937
|
+
throw RequestError.internalError({}, `Failed to delete session file: ${msg}`);
|
|
2938
|
+
}
|
|
2939
|
+
this.sessionPaths.delete(params.sessionId);
|
|
2940
|
+
return { _meta: { piAcp: { deletedFile: sessionFile } } };
|
|
2941
|
+
}
|
|
2789
2942
|
async resumeSession(params) {
|
|
2790
2943
|
if (!isAbsolute(params.cwd)) throw RequestError.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
|
|
2791
2944
|
const existing = this.sessions.maybeGet(params.sessionId);
|
|
@@ -3528,4 +3681,4 @@ async function runDaemon() {
|
|
|
3528
3681
|
//#endregion
|
|
3529
3682
|
export { runDaemon };
|
|
3530
3683
|
|
|
3531
|
-
//# sourceMappingURL=daemon-
|
|
3684
|
+
//# sourceMappingURL=daemon-DKl32dgA.mjs.map
|