pi2dsh 0.9.0 → 0.11.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 +262 -148
- package/README.zh.md +223 -129
- package/dist/{analyzer-C86ba-27.mjs → analyzer-CsrBeS8r.mjs} +2 -2
- package/dist/{analyzer-C86ba-27.mjs.map → analyzer-CsrBeS8r.mjs.map} +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/compat/pi-ai.d.mts +2 -1
- package/dist/compat/pi-ai.d.mts.map +1 -1
- package/dist/compat/pi-ai.mjs +2 -3
- package/dist/compat/pi-coding-agent.d.mts +259 -29
- package/dist/compat/pi-coding-agent.d.mts.map +1 -1
- package/dist/compat/pi-coding-agent.mjs +2 -2
- package/dist/credentials-oauth.mjs +2 -2
- package/dist/{generator-C0jNM-yu.mjs → generator-CSOv6wYT.mjs} +2 -2
- package/dist/{generator-C0jNM-yu.mjs.map → generator-CSOv6wYT.mjs.map} +1 -1
- package/dist/host.mjs +1 -1
- package/dist/index.mjs +4 -4
- package/dist/{mcp-config-Dlou-ZA-.mjs → mcp-config-Cn5gWemZ.mjs} +29 -29
- package/dist/mcp-config-Cn5gWemZ.mjs.map +1 -0
- package/dist/{pi-ai-BA3h0Dwv.mjs → pi-ai-Dyg4zyLZ.mjs} +196 -75
- package/dist/pi-ai-Dyg4zyLZ.mjs.map +1 -0
- package/dist/{pi-coding-agent-CznNsOnV.mjs → pi-coding-agent-Ctw-BhoY.mjs} +0 -0
- package/dist/pi-coding-agent-Ctw-BhoY.mjs.map +1 -0
- package/dist/{runtime-DMEJknhK.mjs → runtime-DFD6u4NO.mjs} +548 -141
- package/dist/runtime-DFD6u4NO.mjs.map +1 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +8 -4
- package/dist/mcp-config-Dlou-ZA-.mjs.map +0 -1
- package/dist/pi-ai-BA3h0Dwv.mjs.map +0 -1
- package/dist/pi-coding-agent-CznNsOnV.mjs.map +0 -1
- package/dist/pi-uuid-Db8ShZsK.mjs +0 -47
- package/dist/pi-uuid-Db8ShZsK.mjs.map +0 -1
- package/dist/runtime-DMEJknhK.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { F as AssistantMessageEventStream, a as builtinProviders, i as __setPiAiLlmBridge } from "./pi-ai-Dyg4zyLZ.mjs";
|
|
3
|
+
import { P as generateBranchSummary, Tn as PiCapabilityError, b as Theme, d as ExtensionRunner, w as __setSubagentSessionFactory, wn as CapabilityLedger } from "./pi-coding-agent-Ctw-BhoY.mjs";
|
|
3
4
|
import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
|
|
4
|
-
import { E as __setSubagentSessionFactory, S as Theme, f as ExtensionRunner } from "./pi-coding-agent-CznNsOnV.mjs";
|
|
5
5
|
import { a as resolvePiProviderAuth, n as loginPiProvider, r as providerSupportsOAuth, t as FileCredentialStore } from "./oauth-bridge-CfyImWx4.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -94,6 +94,17 @@ var PiSessionBridge = class {
|
|
|
94
94
|
});
|
|
95
95
|
return id;
|
|
96
96
|
}
|
|
97
|
+
appendBranchSummary(sessionId, summary, fromId) {
|
|
98
|
+
const id = randomUUID();
|
|
99
|
+
this.persist(sessionId, {
|
|
100
|
+
kind: "branch_summary",
|
|
101
|
+
id,
|
|
102
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
103
|
+
summary,
|
|
104
|
+
fromId
|
|
105
|
+
});
|
|
106
|
+
return id;
|
|
107
|
+
}
|
|
97
108
|
appendLabel(sessionId, targetId, label) {
|
|
98
109
|
this.persist(sessionId, {
|
|
99
110
|
kind: "label",
|
|
@@ -197,6 +208,16 @@ var PiSessionBridge = class {
|
|
|
197
208
|
...record.data === void 0 ? {} : { data: record.data }
|
|
198
209
|
}
|
|
199
210
|
});
|
|
211
|
+
else if (record.kind === "branch_summary") merged.push({
|
|
212
|
+
time,
|
|
213
|
+
entry: {
|
|
214
|
+
type: "branch_summary",
|
|
215
|
+
id: record.id,
|
|
216
|
+
timestamp: record.timestamp,
|
|
217
|
+
summary: record.summary,
|
|
218
|
+
fromId: record.fromId
|
|
219
|
+
}
|
|
220
|
+
});
|
|
200
221
|
else if (record.kind === "label") merged.push({
|
|
201
222
|
time,
|
|
202
223
|
entry: {
|
|
@@ -282,9 +303,22 @@ var PiSessionBridge = class {
|
|
|
282
303
|
//#endregion
|
|
283
304
|
//#region src/subagent-bridge.ts
|
|
284
305
|
let subagentSerial = 0;
|
|
306
|
+
/** Flatten a Pi message's content to text for transcript seeding. */
|
|
307
|
+
function piMessageText(message) {
|
|
308
|
+
const content = message.content;
|
|
309
|
+
if (typeof content === "string") return content;
|
|
310
|
+
if (!Array.isArray(content)) return "";
|
|
311
|
+
return content.map((block) => {
|
|
312
|
+
const record = block;
|
|
313
|
+
if (record?.type === "text" && typeof record.text === "string") return record.text;
|
|
314
|
+
if (record?.type === "toolCall" && typeof record.name === "string") return `(tool ${record.name})`;
|
|
315
|
+
return "";
|
|
316
|
+
}).filter((text) => text.length > 0).join("\n");
|
|
317
|
+
}
|
|
285
318
|
var PiBridgedAgentSession = class {
|
|
286
319
|
agent = {};
|
|
287
320
|
messages = [];
|
|
321
|
+
#seed = [];
|
|
288
322
|
#host;
|
|
289
323
|
#handle;
|
|
290
324
|
#session;
|
|
@@ -292,6 +326,10 @@ var PiBridgedAgentSession = class {
|
|
|
292
326
|
#subscribers = /* @__PURE__ */ new Set();
|
|
293
327
|
#disposers = [];
|
|
294
328
|
#activeToolNames;
|
|
329
|
+
#state;
|
|
330
|
+
#model;
|
|
331
|
+
#streaming = false;
|
|
332
|
+
#pendingToolCalls = /* @__PURE__ */ new Set();
|
|
295
333
|
#sessionName = "";
|
|
296
334
|
#turns = 0;
|
|
297
335
|
#aborted = false;
|
|
@@ -320,6 +358,51 @@ var PiBridgedAgentSession = class {
|
|
|
320
358
|
return next();
|
|
321
359
|
});
|
|
322
360
|
this.#disposers.push(offEvents, offPre);
|
|
361
|
+
const session = this;
|
|
362
|
+
this.#state = {
|
|
363
|
+
get systemPrompt() {
|
|
364
|
+
return "";
|
|
365
|
+
},
|
|
366
|
+
get model() {
|
|
367
|
+
return session.#model;
|
|
368
|
+
},
|
|
369
|
+
get thinkingLevel() {
|
|
370
|
+
return "off";
|
|
371
|
+
},
|
|
372
|
+
get tools() {
|
|
373
|
+
return [...session.#tools];
|
|
374
|
+
},
|
|
375
|
+
set tools(_next) {},
|
|
376
|
+
get messages() {
|
|
377
|
+
return [...session.messages];
|
|
378
|
+
},
|
|
379
|
+
set messages(next) {
|
|
380
|
+
session.#assignTranscript(next);
|
|
381
|
+
},
|
|
382
|
+
get isStreaming() {
|
|
383
|
+
return session.#streaming;
|
|
384
|
+
},
|
|
385
|
+
get pendingToolCalls() {
|
|
386
|
+
return new Set(session.#pendingToolCalls);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
this.agent.state = this.#state;
|
|
390
|
+
}
|
|
391
|
+
/** Pi's AgentState projection, shared by `session.state` and `session.agent.state`. */
|
|
392
|
+
get state() {
|
|
393
|
+
return this.#state;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Pi's `state.messages = …` assignment. Messages already present in this
|
|
397
|
+
* child's projection stay as they are (they are durable); anything new is
|
|
398
|
+
* queued and delivered with the next prompt so the child model really sees
|
|
399
|
+
* the transcript the package seeded.
|
|
400
|
+
*/
|
|
401
|
+
#assignTranscript(next) {
|
|
402
|
+
const identity = (message) => JSON.stringify([message.role, message.content]);
|
|
403
|
+
const known = new Set(this.messages.map(identity));
|
|
404
|
+
this.#seed = next.filter((message) => !known.has(identity(message)));
|
|
405
|
+
for (const message of this.#seed) this.messages.push(message);
|
|
323
406
|
}
|
|
324
407
|
#project(event) {
|
|
325
408
|
const type = event.type;
|
|
@@ -328,12 +411,27 @@ var PiBridgedAgentSession = class {
|
|
|
328
411
|
handler(piEvent);
|
|
329
412
|
} catch {}
|
|
330
413
|
};
|
|
331
|
-
if (type === "turn/start")
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
414
|
+
if (type === "turn/start") {
|
|
415
|
+
this.#streaming = true;
|
|
416
|
+
emit({
|
|
417
|
+
type: "turn_start",
|
|
418
|
+
turnIndex: Number(event.data.turn ?? 1) - 1
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
if (type === "request/header") {
|
|
422
|
+
const config = event.data.config ?? {};
|
|
423
|
+
if (typeof config.model === "string") this.#model = {
|
|
424
|
+
id: config.model,
|
|
425
|
+
provider: String(config.provider ?? "")
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (type === "tools/result" || type === "tool/result") {
|
|
429
|
+
const data = event.data;
|
|
430
|
+
this.#pendingToolCalls.delete(String(data.callId ?? ""));
|
|
431
|
+
}
|
|
335
432
|
if (type === "tool/call") {
|
|
336
433
|
const data = event.data;
|
|
434
|
+
this.#pendingToolCalls.add(String(data.callId ?? ""));
|
|
337
435
|
let args = {};
|
|
338
436
|
try {
|
|
339
437
|
args = JSON.parse(String(data.arguments ?? "{}"));
|
|
@@ -380,6 +478,8 @@ var PiBridgedAgentSession = class {
|
|
|
380
478
|
}
|
|
381
479
|
if (type === "turn/end") {
|
|
382
480
|
this.#turns += 1;
|
|
481
|
+
this.#streaming = false;
|
|
482
|
+
this.#pendingToolCalls.clear();
|
|
383
483
|
emit({
|
|
384
484
|
type: "turn_end",
|
|
385
485
|
turnIndex: Number(event.data.turn ?? 1) - 1,
|
|
@@ -396,6 +496,28 @@ var PiBridgedAgentSession = class {
|
|
|
396
496
|
return () => this.#subscribers.delete(handler);
|
|
397
497
|
}
|
|
398
498
|
async prompt(text) {
|
|
499
|
+
if (this.#seed.length > 0) {
|
|
500
|
+
const seeded = this.#seed;
|
|
501
|
+
this.#seed = [];
|
|
502
|
+
const rendered = seeded.map((message) => {
|
|
503
|
+
const role = String(message.role ?? "user");
|
|
504
|
+
const content = piMessageText(message);
|
|
505
|
+
return content.length === 0 ? void 0 : `[${role}]: ${content}`;
|
|
506
|
+
}).filter((line) => line !== void 0);
|
|
507
|
+
if (rendered.length > 0) {
|
|
508
|
+
const seedBlocks = await this.#host.piContentToDsh([{
|
|
509
|
+
type: "text",
|
|
510
|
+
text: `<prior-conversation>\n${rendered.join("\n\n")}\n</prior-conversation>`
|
|
511
|
+
}]);
|
|
512
|
+
this.#host.deliver(this.#handle.agent, createUserMessage({
|
|
513
|
+
content: seedBlocks,
|
|
514
|
+
source: {
|
|
515
|
+
kind: "plugin",
|
|
516
|
+
plugin: this.#host.messageSource
|
|
517
|
+
}
|
|
518
|
+
}), "inject");
|
|
519
|
+
}
|
|
520
|
+
}
|
|
399
521
|
const blocks = await this.#host.piContentToDsh([{
|
|
400
522
|
type: "text",
|
|
401
523
|
text: String(text)
|
|
@@ -475,6 +597,18 @@ var PiBridgedAgentSession = class {
|
|
|
475
597
|
await this.#handle.dispose();
|
|
476
598
|
}
|
|
477
599
|
};
|
|
600
|
+
/**
|
|
601
|
+
* The name DSH's own child catalog shows for a side thread. Pi packages rarely
|
|
602
|
+
* label a child, so the package that started it is the honest fallback: with
|
|
603
|
+
* several Pi packages installed, their threads stay apart in one list.
|
|
604
|
+
* @param requested - a label the calling package supplied, if any.
|
|
605
|
+
* @param packageName - the Pi package the call came from.
|
|
606
|
+
*/
|
|
607
|
+
function childLabel(requested, packageName) {
|
|
608
|
+
if (typeof requested === "string" && requested.trim().length > 0) return requested.trim().slice(0, 80);
|
|
609
|
+
const owner = packageName?.trim();
|
|
610
|
+
return owner === void 0 || owner.length === 0 ? "Pi side conversation" : `${owner} side conversation`;
|
|
611
|
+
}
|
|
478
612
|
async function createBridgedAgentSession(host, options) {
|
|
479
613
|
const agents = host.cordis.get("agents");
|
|
480
614
|
if (agents?.create === void 0) throw new Error("pi2dsh: createAgentSession() needs the DSH agent registry in the host composition");
|
|
@@ -515,6 +649,17 @@ async function createBridgedAgentSession(host, options) {
|
|
|
515
649
|
throw new Error(`pi2dsh: subagent creation needs the DSH host loop (model runtime) to provide the agent factory; this composition cannot run one (${error instanceof Error ? error.message : String(error)})`);
|
|
516
650
|
}
|
|
517
651
|
const tools = [...Array.isArray(options.tools) ? options.tools : [], ...Array.isArray(options.customTools) ? options.customTools : []];
|
|
652
|
+
const childSession = handle.agent.session;
|
|
653
|
+
if (typeof childSession?.append === "function") try {
|
|
654
|
+
childSession.append("subagent/descriptor", {
|
|
655
|
+
version: 2,
|
|
656
|
+
mode: "continuable",
|
|
657
|
+
provider: "pi2dsh",
|
|
658
|
+
label: childLabel(options.label, host.packageName)
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
host.cordis.logger?.warn?.(`[pi2dsh] child session could not record its subagent identity: ${error instanceof Error ? error.message : String(error)}`);
|
|
662
|
+
}
|
|
518
663
|
return { session: new PiBridgedAgentSession(host, handle, tools) };
|
|
519
664
|
}
|
|
520
665
|
//#endregion
|
|
@@ -1493,6 +1638,11 @@ function cwdOf(agent) {
|
|
|
1493
1638
|
function unsupported(name) {
|
|
1494
1639
|
throw new Error(`pi2dsh: Pi API ${name} requires a native DSH port; inspect the compatibility report`);
|
|
1495
1640
|
}
|
|
1641
|
+
function capabilityLedgerOf(ctx, state) {
|
|
1642
|
+
const shared = state.shared;
|
|
1643
|
+
if (shared.capabilityLedger === void 0) shared.capabilityLedger = new CapabilityLedger((message) => logger(ctx).warn(message));
|
|
1644
|
+
return shared.capabilityLedger;
|
|
1645
|
+
}
|
|
1496
1646
|
function optionalService(ctx, name) {
|
|
1497
1647
|
return ctx.get(name);
|
|
1498
1648
|
}
|
|
@@ -1551,7 +1701,45 @@ function thinkingLevelOf(state, agent) {
|
|
|
1551
1701
|
}
|
|
1552
1702
|
return state.globalThinkingLevel;
|
|
1553
1703
|
}
|
|
1554
|
-
function
|
|
1704
|
+
function durableSeqOf(entryId) {
|
|
1705
|
+
const match = /^dsh-(\d+)$/.exec(entryId);
|
|
1706
|
+
return match === null ? void 0 : Number(match[1]);
|
|
1707
|
+
}
|
|
1708
|
+
function shrinkToTurnBoundary(events, boundary) {
|
|
1709
|
+
const slice = events.slice(0, boundary + 1);
|
|
1710
|
+
for (let i = slice.length - 1; i >= 0; i--) {
|
|
1711
|
+
const type = slice[i]?.type;
|
|
1712
|
+
if (type === "turn/end") return boundary;
|
|
1713
|
+
if (type === "turn/start") return Number(slice[i].seq) - 1;
|
|
1714
|
+
}
|
|
1715
|
+
return boundary;
|
|
1716
|
+
}
|
|
1717
|
+
async function summarizeAbandonedBranch(ctx, state, agent, events, boundary, options) {
|
|
1718
|
+
const abandoned = events.slice(boundary + 1);
|
|
1719
|
+
if (abandoned.length === 0) return void 0;
|
|
1720
|
+
const model = agent === void 0 ? void 0 : currentPiModel(state, agent);
|
|
1721
|
+
if (model === void 0) {
|
|
1722
|
+
logger(ctx).warn("[pi2dsh] ctx.navigateTree: no current model route, navigating without a branch summary");
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
const entries = state.bridge.readonlySessionManager({
|
|
1726
|
+
id: "pi2dsh-abandoned-branch",
|
|
1727
|
+
events: abandoned
|
|
1728
|
+
}, cwdOf(agent)).getEntries?.() ?? [];
|
|
1729
|
+
if (entries.length === 0) return void 0;
|
|
1730
|
+
const result = await generateBranchSummary(entries, {
|
|
1731
|
+
model,
|
|
1732
|
+
signal: new AbortController().signal,
|
|
1733
|
+
...typeof options.customInstructions === "string" ? { customInstructions: options.customInstructions } : {},
|
|
1734
|
+
...options.replaceInstructions === true ? { replaceInstructions: true } : {}
|
|
1735
|
+
});
|
|
1736
|
+
if (result.error !== void 0) {
|
|
1737
|
+
logger(ctx).warn(`[pi2dsh] ctx.navigateTree: branch summarization failed (${result.error}), navigating without a summary`);
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
return result.summary;
|
|
1741
|
+
}
|
|
1742
|
+
function contextFor(ctx, state, agent, signal, command = false, sessionOverride) {
|
|
1555
1743
|
const notices = [];
|
|
1556
1744
|
const userQuestions = optionalService(ctx, "userQuestions");
|
|
1557
1745
|
const ui = {
|
|
@@ -1626,7 +1814,7 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1626
1814
|
state.toolsExpanded = expanded === true;
|
|
1627
1815
|
}
|
|
1628
1816
|
};
|
|
1629
|
-
const session = agentSession(agent);
|
|
1817
|
+
const session = sessionOverride ?? agentSession(agent);
|
|
1630
1818
|
const providerConfig = (name) => state.providers.get(name);
|
|
1631
1819
|
const catalog = state.modelCatalog;
|
|
1632
1820
|
const piNativeEntry = (provider, id) => {
|
|
@@ -1666,119 +1854,121 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1666
1854
|
stream: dshRoutedStream,
|
|
1667
1855
|
streamSimple: dshRoutedStream
|
|
1668
1856
|
});
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const provider = String(model?.provider ?? "");
|
|
1688
|
-
if (provider.length === 0) return false;
|
|
1689
|
-
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider);
|
|
1690
|
-
},
|
|
1691
|
-
getApiKeyAndHeaders: async (model) => {
|
|
1692
|
-
const provider = String(model?.provider ?? "");
|
|
1693
|
-
const config = providerConfig(provider);
|
|
1694
|
-
if (config !== void 0) {
|
|
1695
|
-
const auth = (await resolvePiProviderAuth({
|
|
1696
|
-
providerId: provider,
|
|
1697
|
-
providerConfig: config,
|
|
1698
|
-
store: oauthStoreOf(state)
|
|
1699
|
-
}))?.auth;
|
|
1700
|
-
if (auth?.apiKey === void 0) return { ok: false };
|
|
1701
|
-
return {
|
|
1702
|
-
ok: true,
|
|
1703
|
-
apiKey: auth.apiKey,
|
|
1704
|
-
...auth.headers === void 0 ? {} : { headers: auth.headers },
|
|
1705
|
-
...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
try {
|
|
1709
|
-
const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
|
|
1710
|
-
if (entry === void 0) return { ok: false };
|
|
1711
|
-
const section = ctx.get("settings")?.get(entry.settingsNs);
|
|
1712
|
-
const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
|
|
1713
|
-
if (typeof ref !== "string" || ref.length === 0) return { ok: false };
|
|
1714
|
-
const resolved = await ctx.get("credentials")?.resolve(ref);
|
|
1715
|
-
if (typeof resolved?.value !== "string" || resolved.value.length === 0) return { ok: false };
|
|
1716
|
-
return {
|
|
1717
|
-
ok: true,
|
|
1718
|
-
apiKey: resolved.value
|
|
1719
|
-
};
|
|
1720
|
-
} catch {
|
|
1721
|
-
return { ok: false };
|
|
1722
|
-
}
|
|
1723
|
-
},
|
|
1724
|
-
getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
|
|
1725
|
-
getProvider: (provider) => {
|
|
1726
|
-
const base = providerConfig(provider);
|
|
1727
|
-
if (base !== void 0) return state.providerRouteDisposers.has(provider) ? dshRoutedPiProvider(base, provider) : base;
|
|
1728
|
-
if ((catalog?.all() ?? []).some((entry) => entry.provider === provider)) return dshRoutedPiProvider({
|
|
1729
|
-
id: provider,
|
|
1730
|
-
name: provider
|
|
1731
|
-
}, provider);
|
|
1732
|
-
},
|
|
1733
|
-
complete: async (model, context, options) => {
|
|
1734
|
-
const result = await dshRoutedStream(model, context ?? {}, options).result();
|
|
1735
|
-
if (result instanceof Error) throw result;
|
|
1736
|
-
return result;
|
|
1737
|
-
},
|
|
1738
|
-
getRegisteredProviderConfig: (provider) => providerConfig(provider),
|
|
1739
|
-
getRegisteredProviderIds: () => [...state.providers.keys()],
|
|
1740
|
-
getProviderDisplayName: (provider) => {
|
|
1741
|
-
const config = providerConfig(provider);
|
|
1742
|
-
return typeof config?.name === "string" ? config.name : provider;
|
|
1743
|
-
},
|
|
1744
|
-
getProviderAuth: async (provider) => {
|
|
1745
|
-
const config = providerConfig(provider);
|
|
1746
|
-
if (config === void 0) return void 0;
|
|
1747
|
-
const resolved = await resolvePiProviderAuth({
|
|
1748
|
-
providerId: provider,
|
|
1749
|
-
providerConfig: config,
|
|
1750
|
-
store: oauthStoreOf(state)
|
|
1751
|
-
});
|
|
1752
|
-
if (resolved?.auth === void 0) return resolved;
|
|
1753
|
-
return resolved.auth.baseUrl === void 0 && config.baseUrl !== void 0 ? {
|
|
1754
|
-
...resolved,
|
|
1755
|
-
auth: {
|
|
1756
|
-
...resolved.auth,
|
|
1757
|
-
baseUrl: config.baseUrl
|
|
1758
|
-
}
|
|
1759
|
-
} : resolved;
|
|
1760
|
-
},
|
|
1761
|
-
getApiKeyForProvider: async (provider) => {
|
|
1762
|
-
const config = providerConfig(provider);
|
|
1763
|
-
if (config === void 0) return void 0;
|
|
1764
|
-
return ((await resolvePiProviderAuth({
|
|
1857
|
+
const modelRegistry = {
|
|
1858
|
+
getAll: () => allModels(),
|
|
1859
|
+
getAvailable: () => allModels(),
|
|
1860
|
+
find: (provider, modelId) => {
|
|
1861
|
+
const entry = catalog?.find(provider, modelId);
|
|
1862
|
+
return entry === void 0 ? void 0 : restorePiShape(entry);
|
|
1863
|
+
},
|
|
1864
|
+
getError: () => void 0,
|
|
1865
|
+
hasConfiguredAuth: (model) => {
|
|
1866
|
+
const provider = String(model?.provider ?? "");
|
|
1867
|
+
if (provider.length === 0) return false;
|
|
1868
|
+
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider);
|
|
1869
|
+
},
|
|
1870
|
+
getApiKeyAndHeaders: async (model) => {
|
|
1871
|
+
const provider = String(model?.provider ?? "");
|
|
1872
|
+
const config = providerConfig(provider);
|
|
1873
|
+
if (config !== void 0) {
|
|
1874
|
+
const auth = (await resolvePiProviderAuth({
|
|
1765
1875
|
providerId: provider,
|
|
1766
1876
|
providerConfig: config,
|
|
1767
1877
|
store: oauthStoreOf(state)
|
|
1768
|
-
}))?.auth
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1878
|
+
}))?.auth;
|
|
1879
|
+
if (auth?.apiKey === void 0) return { ok: false };
|
|
1880
|
+
return {
|
|
1881
|
+
ok: true,
|
|
1882
|
+
apiKey: auth.apiKey,
|
|
1883
|
+
...auth.headers === void 0 ? {} : { headers: auth.headers },
|
|
1884
|
+
...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
try {
|
|
1888
|
+
const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
|
|
1889
|
+
if (entry === void 0) return { ok: false };
|
|
1890
|
+
const section = ctx.get("settings")?.get(entry.settingsNs);
|
|
1891
|
+
const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
|
|
1892
|
+
if (typeof ref !== "string" || ref.length === 0) return { ok: false };
|
|
1893
|
+
const resolved = await ctx.get("credentials")?.resolve(ref);
|
|
1894
|
+
if (typeof resolved?.value !== "string" || resolved.value.length === 0) return { ok: false };
|
|
1776
1895
|
return {
|
|
1777
|
-
|
|
1778
|
-
|
|
1896
|
+
ok: true,
|
|
1897
|
+
apiKey: resolved.value
|
|
1779
1898
|
};
|
|
1899
|
+
} catch {
|
|
1900
|
+
return { ok: false };
|
|
1780
1901
|
}
|
|
1781
1902
|
},
|
|
1903
|
+
getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
|
|
1904
|
+
getProvider: (provider) => {
|
|
1905
|
+
const base = providerConfig(provider);
|
|
1906
|
+
if (base !== void 0) return state.providerRouteDisposers.has(provider) ? dshRoutedPiProvider(base, provider) : base;
|
|
1907
|
+
if ((catalog?.all() ?? []).some((entry) => entry.provider === provider)) return dshRoutedPiProvider({
|
|
1908
|
+
id: provider,
|
|
1909
|
+
name: provider
|
|
1910
|
+
}, provider);
|
|
1911
|
+
},
|
|
1912
|
+
complete: async (model, context, options) => {
|
|
1913
|
+
const result = await dshRoutedStream(model, context ?? {}, options).result();
|
|
1914
|
+
if (result instanceof Error) throw result;
|
|
1915
|
+
return result;
|
|
1916
|
+
},
|
|
1917
|
+
getRegisteredProviderConfig: (provider) => providerConfig(provider),
|
|
1918
|
+
getRegisteredProviderIds: () => [...state.providers.keys()],
|
|
1919
|
+
getProviderDisplayName: (provider) => {
|
|
1920
|
+
const config = providerConfig(provider);
|
|
1921
|
+
return typeof config?.name === "string" ? config.name : provider;
|
|
1922
|
+
},
|
|
1923
|
+
getProviderAuth: async (provider) => {
|
|
1924
|
+
const config = providerConfig(provider);
|
|
1925
|
+
if (config === void 0) return void 0;
|
|
1926
|
+
const resolved = await resolvePiProviderAuth({
|
|
1927
|
+
providerId: provider,
|
|
1928
|
+
providerConfig: config,
|
|
1929
|
+
store: oauthStoreOf(state)
|
|
1930
|
+
});
|
|
1931
|
+
if (resolved?.auth === void 0) return resolved;
|
|
1932
|
+
return resolved.auth.baseUrl === void 0 && config.baseUrl !== void 0 ? {
|
|
1933
|
+
...resolved,
|
|
1934
|
+
auth: {
|
|
1935
|
+
...resolved.auth,
|
|
1936
|
+
baseUrl: config.baseUrl
|
|
1937
|
+
}
|
|
1938
|
+
} : resolved;
|
|
1939
|
+
},
|
|
1940
|
+
getApiKeyForProvider: async (provider) => {
|
|
1941
|
+
const config = providerConfig(provider);
|
|
1942
|
+
if (config === void 0) return void 0;
|
|
1943
|
+
return ((await resolvePiProviderAuth({
|
|
1944
|
+
providerId: provider,
|
|
1945
|
+
providerConfig: config,
|
|
1946
|
+
store: oauthStoreOf(state)
|
|
1947
|
+
}))?.auth)?.apiKey;
|
|
1948
|
+
},
|
|
1949
|
+
isUsingOAuth: (model) => {
|
|
1950
|
+
const provider = String(model?.provider ?? "");
|
|
1951
|
+
return provider.length > 0 && providerSupportsOAuth(providerConfig(provider));
|
|
1952
|
+
},
|
|
1953
|
+
refresh: async () => {
|
|
1954
|
+
await catalog?.refresh();
|
|
1955
|
+
return {
|
|
1956
|
+
models: allModels(),
|
|
1957
|
+
errors: []
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
};
|
|
1961
|
+
const contextCwd = typeof (sessionOverride?.header)?.cwd === "string" ? sessionOverride.header.cwd : cwdOf(agent);
|
|
1962
|
+
const base = {
|
|
1963
|
+
ui,
|
|
1964
|
+
mode: "rpc",
|
|
1965
|
+
hasUI: userQuestions !== void 0,
|
|
1966
|
+
cwd: contextCwd,
|
|
1967
|
+
sessionManager: session === void 0 ? state.bridge.readonlySessionManager({
|
|
1968
|
+
id: "pi2dsh-detached",
|
|
1969
|
+
events: []
|
|
1970
|
+
}, contextCwd) : state.bridge.readonlySessionManager(session, contextCwd),
|
|
1971
|
+
modelRegistry,
|
|
1782
1972
|
model: agent === void 0 ? void 0 : currentPiModel(state, agent),
|
|
1783
1973
|
scopedModels: catalog?.all() ?? [],
|
|
1784
1974
|
thinkingLevel: thinkingLevelOf(state, agent),
|
|
@@ -1794,9 +1984,55 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1794
1984
|
});
|
|
1795
1985
|
},
|
|
1796
1986
|
hasPendingMessages: () => false,
|
|
1797
|
-
shutdown: () =>
|
|
1987
|
+
shutdown: () => {
|
|
1988
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
1989
|
+
capability: "ctx.shutdown",
|
|
1990
|
+
reason: "Pi delegates shutdown behavior to the host, and on DSH the user owns process exit.",
|
|
1991
|
+
guidance: "The shutdown request was recorded and ignored.",
|
|
1992
|
+
packageName: state.packageName
|
|
1993
|
+
});
|
|
1994
|
+
},
|
|
1798
1995
|
getContextUsage: () => void 0,
|
|
1799
|
-
compact: () =>
|
|
1996
|
+
compact: (options) => {
|
|
1997
|
+
const ledger = capabilityLedgerOf(ctx, state);
|
|
1998
|
+
const compaction = optionalService(ctx, "compaction");
|
|
1999
|
+
const target = agent;
|
|
2000
|
+
const onError = options?.onError;
|
|
2001
|
+
if (compaction === void 0 || typeof target?.runMaintenance !== "function") {
|
|
2002
|
+
const gap = new PiCapabilityError({
|
|
2003
|
+
capability: "ctx.compact",
|
|
2004
|
+
reason: compaction === void 0 ? "this DSH composition mounts no compaction service." : "compaction needs a live DSH agent for this turn.",
|
|
2005
|
+
guidance: "Compaction runs through the host compaction plugin when one is composed.",
|
|
2006
|
+
packageName: state.packageName
|
|
2007
|
+
});
|
|
2008
|
+
ledger.reportDegraded({
|
|
2009
|
+
capability: "ctx.compact",
|
|
2010
|
+
reason: gap.message,
|
|
2011
|
+
guidance: "",
|
|
2012
|
+
packageName: state.packageName
|
|
2013
|
+
});
|
|
2014
|
+
onError?.(gap);
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
compaction.compactNow(target, new AbortController().signal).then((result) => {
|
|
2018
|
+
if (result === null || result === void 0) return;
|
|
2019
|
+
const dsh = result;
|
|
2020
|
+
const summaryText = (Array.isArray(dsh.summary) ? dsh.summary : []).filter((block) => typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
2021
|
+
const onComplete = options?.onComplete;
|
|
2022
|
+
onComplete?.({
|
|
2023
|
+
summary: summaryText,
|
|
2024
|
+
firstKeptEntryId: "",
|
|
2025
|
+
tokensBefore: dsh.shadowedTokenCount ?? 0
|
|
2026
|
+
});
|
|
2027
|
+
}).catch((error) => {
|
|
2028
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
2029
|
+
if (onError !== void 0) {
|
|
2030
|
+
onError(failure);
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
logger(ctx).warn(`[pi2dsh] plugin "${state.packageName}": ctx.compact failed: ${failure.message}`);
|
|
2034
|
+
});
|
|
2035
|
+
},
|
|
1800
2036
|
getSystemPrompt: () => state.currentSystemPrompt,
|
|
1801
2037
|
__agent: agent,
|
|
1802
2038
|
__notices: notices
|
|
@@ -1807,11 +2043,139 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1807
2043
|
const wait = agent?.whenIdle;
|
|
1808
2044
|
if (typeof wait === "function") await wait.call(agent);
|
|
1809
2045
|
},
|
|
1810
|
-
newSession: () =>
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2046
|
+
newSession: async (options) => {
|
|
2047
|
+
const sessions = optionalService(ctx, "sessions");
|
|
2048
|
+
if (sessions === void 0) {
|
|
2049
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
2050
|
+
capability: "ctx.newSession",
|
|
2051
|
+
reason: "this DSH composition mounts no session service.",
|
|
2052
|
+
guidance: "",
|
|
2053
|
+
packageName: state.packageName
|
|
2054
|
+
});
|
|
2055
|
+
return { cancelled: true };
|
|
2056
|
+
}
|
|
2057
|
+
const parent = agentSession(agent);
|
|
2058
|
+
const created = sessions.create(void 0, { meta: {
|
|
2059
|
+
cwd: cwdOf(agent),
|
|
2060
|
+
...parent?.id === void 0 ? {} : { parentSession: parent.id }
|
|
2061
|
+
} });
|
|
2062
|
+
const withSession = options?.withSession;
|
|
2063
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, created));
|
|
2064
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2065
|
+
capability: "ctx.newSession",
|
|
2066
|
+
reason: `a new DSH session was created (${String(created.id)}).`,
|
|
2067
|
+
guidance: "Which session the surface shows stays a host choice — open it from the DSH session list.",
|
|
2068
|
+
packageName: state.packageName
|
|
2069
|
+
});
|
|
2070
|
+
return { cancelled: false };
|
|
2071
|
+
},
|
|
2072
|
+
fork: async (entryId, options) => {
|
|
2073
|
+
const sessions = optionalService(ctx, "sessions");
|
|
2074
|
+
const source = agentSession(agent);
|
|
2075
|
+
if (sessions === void 0 || source === void 0) {
|
|
2076
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
2077
|
+
capability: "ctx.fork",
|
|
2078
|
+
reason: sessions === void 0 ? "this DSH composition mounts no session service." : "forking needs the live session of an active agent.",
|
|
2079
|
+
guidance: "",
|
|
2080
|
+
packageName: state.packageName
|
|
2081
|
+
});
|
|
2082
|
+
return { cancelled: true };
|
|
2083
|
+
}
|
|
2084
|
+
const seq = durableSeqOf(String(entryId));
|
|
2085
|
+
if (seq === void 0) throw new Error(`pi2dsh: ctx.fork(${JSON.stringify(String(entryId))}) — only durable-log entries (projected ids "dsh-<seq>") can anchor a fork; package-appended sidecar entries are not part of the DSH durable log`);
|
|
2086
|
+
const position = options?.position ?? "before";
|
|
2087
|
+
const events = source.events ?? [];
|
|
2088
|
+
const requested = Math.min(position === "at" ? seq : seq - 1, events.length - 1);
|
|
2089
|
+
const boundary = requested < 0 ? -1 : shrinkToTurnBoundary(events, requested);
|
|
2090
|
+
const child = boundary < 0 ? sessions.create(void 0, { meta: {
|
|
2091
|
+
cwd: cwdOf(agent),
|
|
2092
|
+
parentSession: source.id
|
|
2093
|
+
} }) : sessions.fork(source, boundary);
|
|
2094
|
+
const withSession = options?.withSession;
|
|
2095
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, child));
|
|
2096
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2097
|
+
capability: "ctx.fork",
|
|
2098
|
+
reason: `the session was forked on DSH's official prefix-fork surface (child ${String(child.id)}; DSH forks land on completed-turn boundaries).`,
|
|
2099
|
+
guidance: "Open the forked session from the DSH session list.",
|
|
2100
|
+
packageName: state.packageName
|
|
2101
|
+
});
|
|
2102
|
+
return { cancelled: false };
|
|
2103
|
+
},
|
|
2104
|
+
navigateTree: async (targetId, options) => {
|
|
2105
|
+
const sessions = optionalService(ctx, "sessions");
|
|
2106
|
+
const source = agentSession(agent);
|
|
2107
|
+
if (sessions === void 0 || source === void 0) {
|
|
2108
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
2109
|
+
capability: "ctx.navigateTree",
|
|
2110
|
+
reason: sessions === void 0 ? "this DSH composition mounts no session service." : "tree navigation needs the live session of an active agent.",
|
|
2111
|
+
guidance: "",
|
|
2112
|
+
packageName: state.packageName
|
|
2113
|
+
});
|
|
2114
|
+
return { cancelled: true };
|
|
2115
|
+
}
|
|
2116
|
+
const seq = durableSeqOf(String(targetId));
|
|
2117
|
+
if (seq === void 0) throw new Error(`pi2dsh: ctx.navigateTree(${JSON.stringify(String(targetId))}) — only durable-log entries (projected ids "dsh-<seq>") can be navigation targets; package-appended sidecar entries are not part of the DSH durable log`);
|
|
2118
|
+
const events = source.events ?? [];
|
|
2119
|
+
const capped = Math.min(seq, events.length - 1);
|
|
2120
|
+
const boundary = capped < 0 ? -1 : shrinkToTurnBoundary(events, capped);
|
|
2121
|
+
const child = boundary < 0 ? sessions.create(void 0, { meta: {
|
|
2122
|
+
cwd: cwdOf(agent),
|
|
2123
|
+
parentSession: source.id
|
|
2124
|
+
} }) : sessions.fork(source, boundary);
|
|
2125
|
+
if (options?.summarize === true) {
|
|
2126
|
+
const summary = await summarizeAbandonedBranch(ctx, state, agent, events, boundary, options);
|
|
2127
|
+
if (summary !== void 0) state.bridge.appendBranchSummary(String(child.id), summary, String(targetId));
|
|
2128
|
+
}
|
|
2129
|
+
if (typeof options?.label === "string") state.bridge.appendLabel(String(child.id), String(targetId), options.label);
|
|
2130
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2131
|
+
capability: "ctx.navigateTree",
|
|
2132
|
+
reason: `navigation forked the session at the target on DSH's official surface (child ${String(child.id)}; the DSH tree lives BETWEEN sessions via fork lineage, not inside one log).`,
|
|
2133
|
+
guidance: "Open the navigated session from the DSH session list.",
|
|
2134
|
+
packageName: state.packageName
|
|
2135
|
+
});
|
|
2136
|
+
return { cancelled: false };
|
|
2137
|
+
},
|
|
2138
|
+
switchSession: async (sessionPath, options) => {
|
|
2139
|
+
const sessions = optionalService(ctx, "sessions");
|
|
2140
|
+
if (sessions === void 0) {
|
|
2141
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
2142
|
+
capability: "ctx.switchSession",
|
|
2143
|
+
reason: "this DSH composition mounts no session service.",
|
|
2144
|
+
guidance: "",
|
|
2145
|
+
packageName: state.packageName
|
|
2146
|
+
});
|
|
2147
|
+
return { cancelled: true };
|
|
2148
|
+
}
|
|
2149
|
+
const raw = String(sessionPath);
|
|
2150
|
+
const base = raw.replace(/\\/g, "/").split("/").pop() ?? raw;
|
|
2151
|
+
const candidate = base.endsWith(".jsonl") ? base.slice(0, -6) : base;
|
|
2152
|
+
const target = sessions.get(candidate) ?? sessions.list().find((entry) => String(entry.id) === candidate);
|
|
2153
|
+
if (target === void 0) throw new Error(`pi2dsh: ctx.switchSession(${JSON.stringify(raw)}) — no live DSH session ${JSON.stringify(candidate)}; switching to persisted sessions is host-owned (resume them from the DSH surface first)`);
|
|
2154
|
+
const withSession = options?.withSession;
|
|
2155
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, target));
|
|
2156
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2157
|
+
capability: "ctx.switchSession",
|
|
2158
|
+
reason: `the live DSH session ${JSON.stringify(candidate)} was targeted.`,
|
|
2159
|
+
guidance: "Which session the surface shows stays a host choice — open it from the DSH session list.",
|
|
2160
|
+
packageName: state.packageName
|
|
2161
|
+
});
|
|
2162
|
+
return { cancelled: false };
|
|
2163
|
+
},
|
|
2164
|
+
reload: async () => {
|
|
2165
|
+
const remounts = state.shared.packageRemounts;
|
|
2166
|
+
if (remounts === void 0 || remounts.size === 0) return;
|
|
2167
|
+
for (const [name, remount] of remounts) try {
|
|
2168
|
+
await remount();
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
logger(ctx).warn(`[pi2dsh] ctx.reload: remounting "${name}" failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2171
|
+
}
|
|
2172
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2173
|
+
capability: "ctx.reload",
|
|
2174
|
+
reason: "extension entries were disposed and remounted through a fresh loader.",
|
|
2175
|
+
guidance: "Skills, prompts, and themes are host-managed and reload when dsh restarts.",
|
|
2176
|
+
packageName: state.packageName
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
1815
2179
|
});
|
|
1816
2180
|
return base;
|
|
1817
2181
|
}
|
|
@@ -2488,7 +2852,12 @@ function registerLoginCommand(ctx, state) {
|
|
|
2488
2852
|
});
|
|
2489
2853
|
}
|
|
2490
2854
|
function registerTool(ctx, state, tool) {
|
|
2491
|
-
if (state.tools.has(tool.name))
|
|
2855
|
+
if (state.tools.has(tool.name)) {
|
|
2856
|
+
const previous = state.tools.get(tool.name);
|
|
2857
|
+
state.tools.set(tool.name, tool);
|
|
2858
|
+
if (JSON.stringify(previous.parameters ?? null) !== JSON.stringify(tool.parameters ?? null)) logger(ctx).warn(`[pi2dsh] tool ${tool.name} was re-registered with a different parameter schema; the new schema takes effect when dsh restarts (behavior is already live)`);
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2492
2861
|
const normalized = normalizeToolSchema(tool.parameters);
|
|
2493
2862
|
for (const warning of normalized.warnings) logger(ctx).warn(`[pi2dsh] tool ${tool.name}: ${warning}`);
|
|
2494
2863
|
state.tools.set(tool.name, tool);
|
|
@@ -2503,12 +2872,13 @@ function registerTool(ctx, state, tool) {
|
|
|
2503
2872
|
},
|
|
2504
2873
|
isConcurrencySafe: () => tool.executionMode === "parallel",
|
|
2505
2874
|
async execute(args, exec) {
|
|
2875
|
+
const live = state.tools.get(tool.name) ?? tool;
|
|
2506
2876
|
const mutated = state.argMutations.get(exec);
|
|
2507
2877
|
if (mutated !== void 0) state.argMutations.delete(exec);
|
|
2508
2878
|
const effective = mutated ?? args;
|
|
2509
|
-
const prepared =
|
|
2879
|
+
const prepared = live.prepareArguments?.(cloneJson(effective)) ?? effective;
|
|
2510
2880
|
const agent = exec.agent;
|
|
2511
|
-
const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () =>
|
|
2881
|
+
const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
|
|
2512
2882
|
dispatch(state, "tool_execution_update", {
|
|
2513
2883
|
type: "tool_execution_update",
|
|
2514
2884
|
toolCallId: String(exec.callId),
|
|
@@ -2649,8 +3019,8 @@ function dshCommandName(ctx, piName) {
|
|
|
2649
3019
|
}
|
|
2650
3020
|
function registerCommand(ctx, state, command) {
|
|
2651
3021
|
if (state.commands.has(command.name)) {
|
|
2652
|
-
state.
|
|
2653
|
-
|
|
3022
|
+
state.commands.set(command.name, command);
|
|
3023
|
+
return;
|
|
2654
3024
|
}
|
|
2655
3025
|
state.commands.set(command.name, command);
|
|
2656
3026
|
const commands = ctx.get("commands");
|
|
@@ -2662,11 +3032,12 @@ function registerCommand(ctx, state, command) {
|
|
|
2662
3032
|
const definitionFor = (dshName) => ({
|
|
2663
3033
|
name: dshName,
|
|
2664
3034
|
description: command.description || `Migrated Pi command /${command.name}`,
|
|
2665
|
-
|
|
3035
|
+
input: { hint: command.argumentHint ?? "arguments (optional)" },
|
|
2666
3036
|
async handler(invocation) {
|
|
2667
3037
|
const agent = invocation.agent;
|
|
2668
3038
|
const commandContext = contextFor(ctx, state, agent, invocation.signal, true);
|
|
2669
|
-
|
|
3039
|
+
const live = state.commands.get(command.name) ?? command;
|
|
3040
|
+
await state.agentScope.run(agent, () => live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext));
|
|
2670
3041
|
const notices = commandContext.__notices;
|
|
2671
3042
|
return {
|
|
2672
3043
|
kind: "success",
|
|
@@ -2955,7 +3326,7 @@ async function registerPromptCommands(ctx, state, rootDir, manifest) {
|
|
|
2955
3326
|
});
|
|
2956
3327
|
}
|
|
2957
3328
|
}
|
|
2958
|
-
async function loadExtensions(rootDir, manifest, api, onExtensionError) {
|
|
3329
|
+
async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabilityGap, onHostInfraReference) {
|
|
2959
3330
|
const resolveShim = async (name) => {
|
|
2960
3331
|
const compiled = fileURLToPath(new URL(`./compat/${name}.mjs`, import.meta.url));
|
|
2961
3332
|
try {
|
|
@@ -2995,14 +3366,22 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError) {
|
|
|
2995
3366
|
});
|
|
2996
3367
|
const failures = [];
|
|
2997
3368
|
let mounted = 0;
|
|
2998
|
-
for (const extension of manifest.extensions)
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3369
|
+
for (const extension of manifest.extensions) {
|
|
3370
|
+
try {
|
|
3371
|
+
const source = await readFile(join(rootDir, extension), "utf8");
|
|
3372
|
+
const infraImport = /import[^;]*?\b(ModelRuntime|DefaultPackageManager)\b[^;]*?from\s*['"]@(?:earendil-works|mariozechner)\/pi-coding-agent['"]/su.exec(source);
|
|
3373
|
+
if (infraImport !== null) onHostInfraReference?.(infraImport[1], extension);
|
|
3374
|
+
} catch {}
|
|
3375
|
+
try {
|
|
3376
|
+
const loaded = await jiti.import(join(rootDir, extension));
|
|
3377
|
+
const candidate = typeof loaded === "object" && loaded !== null && "default" in loaded ? loaded.default : loaded;
|
|
3378
|
+
if (typeof candidate !== "function") throw new TypeError(`Pi extension ${extension} has no default factory function`);
|
|
3379
|
+
await candidate(api);
|
|
3380
|
+
mounted += 1;
|
|
3381
|
+
} catch (error) {
|
|
3382
|
+
if (error instanceof PiCapabilityError || error instanceof Error && error.name === "PiCapabilityError") onCapabilityGap?.(error, extension);
|
|
3383
|
+
failures.push(`${extension}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3384
|
+
}
|
|
3006
3385
|
}
|
|
3007
3386
|
if (failures.length > 0 && mounted === 0 && manifest.extensions.length > 0) throw new Error(`every Pi extension entry failed to load:\n${failures.map((item) => `- ${item}`).join("\n")}`);
|
|
3008
3387
|
for (const failure of failures) onExtensionError?.(failure);
|
|
@@ -3023,6 +3402,7 @@ async function applyPiPackage(ctx, options) {
|
|
|
3023
3402
|
const shared = sharedHostStateOf(ctx);
|
|
3024
3403
|
const state = {
|
|
3025
3404
|
shared,
|
|
3405
|
+
packageName: options.manifest.package.name,
|
|
3026
3406
|
handlers: /* @__PURE__ */ new Map(),
|
|
3027
3407
|
tools: runtimeTools,
|
|
3028
3408
|
runner: new ExtensionRunner(piToolRecords),
|
|
@@ -3112,12 +3492,39 @@ async function applyPiPackage(ctx, options) {
|
|
|
3112
3492
|
piContentToDsh: (content) => piToDshContent(ctx, content),
|
|
3113
3493
|
deliver: (agent, message, mode) => deliverAgentMessage(agent, message, mode),
|
|
3114
3494
|
messageFromSessionEvent,
|
|
3115
|
-
messageSource: state.messageSource
|
|
3495
|
+
messageSource: state.messageSource,
|
|
3496
|
+
packageName: state.packageName
|
|
3116
3497
|
}, subagentOptions));
|
|
3117
3498
|
ctx.effect(() => () => __setSubagentSessionFactory(void 0));
|
|
3118
3499
|
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
3119
|
-
|
|
3120
|
-
|
|
3500
|
+
const onExtensionError = (failure) => logger(ctx).warn(`[pi2dsh] extension entry failed and was skipped (matching Pi's per-extension error isolation): ${failure}`);
|
|
3501
|
+
const onCapabilityGap = (gap) => capabilityLedgerOf(ctx, state).reportUnusable({
|
|
3502
|
+
capability: gap.capability,
|
|
3503
|
+
reason: "this package needs it during startup.",
|
|
3504
|
+
guidance: gap.message,
|
|
3505
|
+
packageName: state.packageName
|
|
3506
|
+
});
|
|
3507
|
+
const onHostInfraReference = (symbol) => capabilityLedgerOf(ctx, state).reportStartupReference({
|
|
3508
|
+
capability: symbol,
|
|
3509
|
+
reason: symbol === "DefaultPackageManager" ? "installing packages is owned by the DSH host and its security gates (dsh plugin add/remove)." : "standalone model stacks are owned by the DSH host llm configuration (packages read ctx.modelRegistry).",
|
|
3510
|
+
guidance: "",
|
|
3511
|
+
packageName: state.packageName
|
|
3512
|
+
});
|
|
3513
|
+
const mountExtensions = () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference);
|
|
3514
|
+
await mountExtensions();
|
|
3515
|
+
const remounts = shared.packageRemounts ??= /* @__PURE__ */ new Map();
|
|
3516
|
+
remounts.set(state.packageName, async () => {
|
|
3517
|
+
state.handlers.clear();
|
|
3518
|
+
state.eventBus.removeAllListeners();
|
|
3519
|
+
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
3520
|
+
await mountExtensions();
|
|
3521
|
+
});
|
|
3522
|
+
ctx.effect(() => () => {
|
|
3523
|
+
remounts.delete(state.packageName);
|
|
3524
|
+
});
|
|
3525
|
+
const health = state.shared.capabilityLedger?.healthOf(state.packageName);
|
|
3526
|
+
const healthSuffix = health === void 0 || health.status === "ok" ? "" : ` — ${health.status.toUpperCase()}: missing Pi capabilities ${health.gaps.join(", ")}`;
|
|
3527
|
+
logger(ctx).info(`[pi2dsh] loaded ${options.manifest.package.name}: ${state.tools.size} tools, ${state.commands.size} commands, ${options.manifest.skillDirs.length} skill roots${healthSuffix}`);
|
|
3121
3528
|
}
|
|
3122
3529
|
const runtimeInternals = {
|
|
3123
3530
|
expandPrompt,
|
|
@@ -3128,4 +3535,4 @@ const runtimeInternals = {
|
|
|
3128
3535
|
//#endregion
|
|
3129
3536
|
export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
|
|
3130
3537
|
|
|
3131
|
-
//# sourceMappingURL=runtime-
|
|
3538
|
+
//# sourceMappingURL=runtime-DFD6u4NO.mjs.map
|