pi2dsh 0.8.0 → 0.10.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 +8 -2
- package/README.zh.md +7 -2
- package/dist/{analyzer-CKTsWVzn.mjs → analyzer-CsrBeS8r.mjs} +3 -5
- package/dist/{analyzer-CKTsWVzn.mjs.map → analyzer-CsrBeS8r.mjs.map} +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/cli.mjs.map +1 -1
- 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-eyViezvs.mjs → generator-CSOv6wYT.mjs} +3 -5
- package/dist/{generator-eyViezvs.mjs.map → generator-CSOv6wYT.mjs.map} +1 -1
- package/dist/host.d.mts +2 -2
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +1 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +6 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- 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-C6Xag6xt.mjs → pi-ai-Dyg4zyLZ.mjs} +196 -85
- 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-B-Mun-jX.mjs → runtime-Ce3PTbWU.mjs} +511 -171
- package/dist/runtime-Ce3PTbWU.mjs.map +1 -0
- package/dist/runtime.d.mts +18 -10
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +5 -3
- package/dist/mcp-config-Dlou-ZA-.mjs.map +0 -1
- package/dist/pi-ai-C6Xag6xt.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-B-Mun-jX.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: {
|
|
@@ -1223,7 +1244,8 @@ async function textOnlyMessages(messages, materialize) {
|
|
|
1223
1244
|
* extension handles the turn's entering messages.
|
|
1224
1245
|
*/
|
|
1225
1246
|
function imageAdmissionCompanionAdapter(options) {
|
|
1226
|
-
const { originalId,
|
|
1247
|
+
const { originalId, llm } = options;
|
|
1248
|
+
const admits = (modelId) => options.imageModels === void 0 || options.imageModels.has(modelId);
|
|
1227
1249
|
const materialize = options.materializeImage ?? (async () => void 0);
|
|
1228
1250
|
const admitImage = (info, id) => {
|
|
1229
1251
|
const modalities = Array.isArray(info.inputModalities) ? info.inputModalities.slice() : ["text"];
|
|
@@ -1242,10 +1264,10 @@ function imageAdmissionCompanionAdapter(options) {
|
|
|
1242
1264
|
};
|
|
1243
1265
|
},
|
|
1244
1266
|
providerRetryPolicy: () => void 0,
|
|
1245
|
-
listModels: async (id) => (await llm.listModels(originalId)).filter((model) =>
|
|
1267
|
+
listModels: async (id) => (await llm.listModels(originalId)).filter((model) => admits(String(model.id))).map((model) => admitImage(model, id)),
|
|
1246
1268
|
resolveModel: async (id, modelId) => {
|
|
1247
1269
|
const info = await llm.resolveModelInfo(originalId, modelId);
|
|
1248
|
-
return
|
|
1270
|
+
return admits(modelId) ? admitImage(info, id) : {
|
|
1249
1271
|
...info,
|
|
1250
1272
|
provider: id
|
|
1251
1273
|
};
|
|
@@ -1492,6 +1514,11 @@ function cwdOf(agent) {
|
|
|
1492
1514
|
function unsupported(name) {
|
|
1493
1515
|
throw new Error(`pi2dsh: Pi API ${name} requires a native DSH port; inspect the compatibility report`);
|
|
1494
1516
|
}
|
|
1517
|
+
function capabilityLedgerOf(ctx, state) {
|
|
1518
|
+
const shared = state.shared;
|
|
1519
|
+
if (shared.capabilityLedger === void 0) shared.capabilityLedger = new CapabilityLedger((message) => logger(ctx).warn(message));
|
|
1520
|
+
return shared.capabilityLedger;
|
|
1521
|
+
}
|
|
1495
1522
|
function optionalService(ctx, name) {
|
|
1496
1523
|
return ctx.get(name);
|
|
1497
1524
|
}
|
|
@@ -1550,7 +1577,45 @@ function thinkingLevelOf(state, agent) {
|
|
|
1550
1577
|
}
|
|
1551
1578
|
return state.globalThinkingLevel;
|
|
1552
1579
|
}
|
|
1553
|
-
function
|
|
1580
|
+
function durableSeqOf(entryId) {
|
|
1581
|
+
const match = /^dsh-(\d+)$/.exec(entryId);
|
|
1582
|
+
return match === null ? void 0 : Number(match[1]);
|
|
1583
|
+
}
|
|
1584
|
+
function shrinkToTurnBoundary(events, boundary) {
|
|
1585
|
+
const slice = events.slice(0, boundary + 1);
|
|
1586
|
+
for (let i = slice.length - 1; i >= 0; i--) {
|
|
1587
|
+
const type = slice[i]?.type;
|
|
1588
|
+
if (type === "turn/end") return boundary;
|
|
1589
|
+
if (type === "turn/start") return Number(slice[i].seq) - 1;
|
|
1590
|
+
}
|
|
1591
|
+
return boundary;
|
|
1592
|
+
}
|
|
1593
|
+
async function summarizeAbandonedBranch(ctx, state, agent, events, boundary, options) {
|
|
1594
|
+
const abandoned = events.slice(boundary + 1);
|
|
1595
|
+
if (abandoned.length === 0) return void 0;
|
|
1596
|
+
const model = agent === void 0 ? void 0 : currentPiModel(state, agent);
|
|
1597
|
+
if (model === void 0) {
|
|
1598
|
+
logger(ctx).warn("[pi2dsh] ctx.navigateTree: no current model route, navigating without a branch summary");
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
const entries = state.bridge.readonlySessionManager({
|
|
1602
|
+
id: "pi2dsh-abandoned-branch",
|
|
1603
|
+
events: abandoned
|
|
1604
|
+
}, cwdOf(agent)).getEntries?.() ?? [];
|
|
1605
|
+
if (entries.length === 0) return void 0;
|
|
1606
|
+
const result = await generateBranchSummary(entries, {
|
|
1607
|
+
model,
|
|
1608
|
+
signal: new AbortController().signal,
|
|
1609
|
+
...typeof options.customInstructions === "string" ? { customInstructions: options.customInstructions } : {},
|
|
1610
|
+
...options.replaceInstructions === true ? { replaceInstructions: true } : {}
|
|
1611
|
+
});
|
|
1612
|
+
if (result.error !== void 0) {
|
|
1613
|
+
logger(ctx).warn(`[pi2dsh] ctx.navigateTree: branch summarization failed (${result.error}), navigating without a summary`);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
return result.summary;
|
|
1617
|
+
}
|
|
1618
|
+
function contextFor(ctx, state, agent, signal, command = false, sessionOverride) {
|
|
1554
1619
|
const notices = [];
|
|
1555
1620
|
const userQuestions = optionalService(ctx, "userQuestions");
|
|
1556
1621
|
const ui = {
|
|
@@ -1625,7 +1690,7 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1625
1690
|
state.toolsExpanded = expanded === true;
|
|
1626
1691
|
}
|
|
1627
1692
|
};
|
|
1628
|
-
const session = agentSession(agent);
|
|
1693
|
+
const session = sessionOverride ?? agentSession(agent);
|
|
1629
1694
|
const providerConfig = (name) => state.providers.get(name);
|
|
1630
1695
|
const catalog = state.modelCatalog;
|
|
1631
1696
|
const piNativeEntry = (provider, id) => {
|
|
@@ -1665,119 +1730,121 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1665
1730
|
stream: dshRoutedStream,
|
|
1666
1731
|
streamSimple: dshRoutedStream
|
|
1667
1732
|
});
|
|
1668
|
-
const
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const provider = String(model?.provider ?? "");
|
|
1687
|
-
if (provider.length === 0) return false;
|
|
1688
|
-
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider);
|
|
1689
|
-
},
|
|
1690
|
-
getApiKeyAndHeaders: async (model) => {
|
|
1691
|
-
const provider = String(model?.provider ?? "");
|
|
1692
|
-
const config = providerConfig(provider);
|
|
1693
|
-
if (config !== void 0) {
|
|
1694
|
-
const auth = (await resolvePiProviderAuth({
|
|
1695
|
-
providerId: provider,
|
|
1696
|
-
providerConfig: config,
|
|
1697
|
-
store: oauthStoreOf(state)
|
|
1698
|
-
}))?.auth;
|
|
1699
|
-
if (auth?.apiKey === void 0) return { ok: false };
|
|
1700
|
-
return {
|
|
1701
|
-
ok: true,
|
|
1702
|
-
apiKey: auth.apiKey,
|
|
1703
|
-
...auth.headers === void 0 ? {} : { headers: auth.headers },
|
|
1704
|
-
...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
|
|
1705
|
-
};
|
|
1706
|
-
}
|
|
1707
|
-
try {
|
|
1708
|
-
const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
|
|
1709
|
-
if (entry === void 0) return { ok: false };
|
|
1710
|
-
const section = ctx.get("settings")?.get(entry.settingsNs);
|
|
1711
|
-
const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
|
|
1712
|
-
if (typeof ref !== "string" || ref.length === 0) return { ok: false };
|
|
1713
|
-
const resolved = await ctx.get("credentials")?.resolve(ref);
|
|
1714
|
-
if (typeof resolved?.value !== "string" || resolved.value.length === 0) return { ok: false };
|
|
1715
|
-
return {
|
|
1716
|
-
ok: true,
|
|
1717
|
-
apiKey: resolved.value
|
|
1718
|
-
};
|
|
1719
|
-
} catch {
|
|
1720
|
-
return { ok: false };
|
|
1721
|
-
}
|
|
1722
|
-
},
|
|
1723
|
-
getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
|
|
1724
|
-
getProvider: (provider) => {
|
|
1725
|
-
const base = providerConfig(provider);
|
|
1726
|
-
if (base !== void 0) return state.providerRouteDisposers.has(provider) ? dshRoutedPiProvider(base, provider) : base;
|
|
1727
|
-
if ((catalog?.all() ?? []).some((entry) => entry.provider === provider)) return dshRoutedPiProvider({
|
|
1728
|
-
id: provider,
|
|
1729
|
-
name: provider
|
|
1730
|
-
}, provider);
|
|
1731
|
-
},
|
|
1732
|
-
complete: async (model, context, options) => {
|
|
1733
|
-
const result = await dshRoutedStream(model, context ?? {}, options).result();
|
|
1734
|
-
if (result instanceof Error) throw result;
|
|
1735
|
-
return result;
|
|
1736
|
-
},
|
|
1737
|
-
getRegisteredProviderConfig: (provider) => providerConfig(provider),
|
|
1738
|
-
getRegisteredProviderIds: () => [...state.providers.keys()],
|
|
1739
|
-
getProviderDisplayName: (provider) => {
|
|
1740
|
-
const config = providerConfig(provider);
|
|
1741
|
-
return typeof config?.name === "string" ? config.name : provider;
|
|
1742
|
-
},
|
|
1743
|
-
getProviderAuth: async (provider) => {
|
|
1744
|
-
const config = providerConfig(provider);
|
|
1745
|
-
if (config === void 0) return void 0;
|
|
1746
|
-
const resolved = await resolvePiProviderAuth({
|
|
1747
|
-
providerId: provider,
|
|
1748
|
-
providerConfig: config,
|
|
1749
|
-
store: oauthStoreOf(state)
|
|
1750
|
-
});
|
|
1751
|
-
if (resolved?.auth === void 0) return resolved;
|
|
1752
|
-
return resolved.auth.baseUrl === void 0 && config.baseUrl !== void 0 ? {
|
|
1753
|
-
...resolved,
|
|
1754
|
-
auth: {
|
|
1755
|
-
...resolved.auth,
|
|
1756
|
-
baseUrl: config.baseUrl
|
|
1757
|
-
}
|
|
1758
|
-
} : resolved;
|
|
1759
|
-
},
|
|
1760
|
-
getApiKeyForProvider: async (provider) => {
|
|
1761
|
-
const config = providerConfig(provider);
|
|
1762
|
-
if (config === void 0) return void 0;
|
|
1763
|
-
return ((await resolvePiProviderAuth({
|
|
1733
|
+
const modelRegistry = {
|
|
1734
|
+
getAll: () => allModels(),
|
|
1735
|
+
getAvailable: () => allModels(),
|
|
1736
|
+
find: (provider, modelId) => {
|
|
1737
|
+
const entry = catalog?.find(provider, modelId);
|
|
1738
|
+
return entry === void 0 ? void 0 : restorePiShape(entry);
|
|
1739
|
+
},
|
|
1740
|
+
getError: () => void 0,
|
|
1741
|
+
hasConfiguredAuth: (model) => {
|
|
1742
|
+
const provider = String(model?.provider ?? "");
|
|
1743
|
+
if (provider.length === 0) return false;
|
|
1744
|
+
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider);
|
|
1745
|
+
},
|
|
1746
|
+
getApiKeyAndHeaders: async (model) => {
|
|
1747
|
+
const provider = String(model?.provider ?? "");
|
|
1748
|
+
const config = providerConfig(provider);
|
|
1749
|
+
if (config !== void 0) {
|
|
1750
|
+
const auth = (await resolvePiProviderAuth({
|
|
1764
1751
|
providerId: provider,
|
|
1765
1752
|
providerConfig: config,
|
|
1766
1753
|
store: oauthStoreOf(state)
|
|
1767
|
-
}))?.auth
|
|
1768
|
-
|
|
1769
|
-
isUsingOAuth: (model) => {
|
|
1770
|
-
const provider = String(model?.provider ?? "");
|
|
1771
|
-
return provider.length > 0 && providerSupportsOAuth(providerConfig(provider));
|
|
1772
|
-
},
|
|
1773
|
-
refresh: async () => {
|
|
1774
|
-
await catalog?.refresh();
|
|
1754
|
+
}))?.auth;
|
|
1755
|
+
if (auth?.apiKey === void 0) return { ok: false };
|
|
1775
1756
|
return {
|
|
1776
|
-
|
|
1777
|
-
|
|
1757
|
+
ok: true,
|
|
1758
|
+
apiKey: auth.apiKey,
|
|
1759
|
+
...auth.headers === void 0 ? {} : { headers: auth.headers },
|
|
1760
|
+
...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
|
|
1778
1761
|
};
|
|
1779
1762
|
}
|
|
1763
|
+
try {
|
|
1764
|
+
const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
|
|
1765
|
+
if (entry === void 0) return { ok: false };
|
|
1766
|
+
const section = ctx.get("settings")?.get(entry.settingsNs);
|
|
1767
|
+
const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
|
|
1768
|
+
if (typeof ref !== "string" || ref.length === 0) return { ok: false };
|
|
1769
|
+
const resolved = await ctx.get("credentials")?.resolve(ref);
|
|
1770
|
+
if (typeof resolved?.value !== "string" || resolved.value.length === 0) return { ok: false };
|
|
1771
|
+
return {
|
|
1772
|
+
ok: true,
|
|
1773
|
+
apiKey: resolved.value
|
|
1774
|
+
};
|
|
1775
|
+
} catch {
|
|
1776
|
+
return { ok: false };
|
|
1777
|
+
}
|
|
1780
1778
|
},
|
|
1779
|
+
getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
|
|
1780
|
+
getProvider: (provider) => {
|
|
1781
|
+
const base = providerConfig(provider);
|
|
1782
|
+
if (base !== void 0) return state.providerRouteDisposers.has(provider) ? dshRoutedPiProvider(base, provider) : base;
|
|
1783
|
+
if ((catalog?.all() ?? []).some((entry) => entry.provider === provider)) return dshRoutedPiProvider({
|
|
1784
|
+
id: provider,
|
|
1785
|
+
name: provider
|
|
1786
|
+
}, provider);
|
|
1787
|
+
},
|
|
1788
|
+
complete: async (model, context, options) => {
|
|
1789
|
+
const result = await dshRoutedStream(model, context ?? {}, options).result();
|
|
1790
|
+
if (result instanceof Error) throw result;
|
|
1791
|
+
return result;
|
|
1792
|
+
},
|
|
1793
|
+
getRegisteredProviderConfig: (provider) => providerConfig(provider),
|
|
1794
|
+
getRegisteredProviderIds: () => [...state.providers.keys()],
|
|
1795
|
+
getProviderDisplayName: (provider) => {
|
|
1796
|
+
const config = providerConfig(provider);
|
|
1797
|
+
return typeof config?.name === "string" ? config.name : provider;
|
|
1798
|
+
},
|
|
1799
|
+
getProviderAuth: async (provider) => {
|
|
1800
|
+
const config = providerConfig(provider);
|
|
1801
|
+
if (config === void 0) return void 0;
|
|
1802
|
+
const resolved = await resolvePiProviderAuth({
|
|
1803
|
+
providerId: provider,
|
|
1804
|
+
providerConfig: config,
|
|
1805
|
+
store: oauthStoreOf(state)
|
|
1806
|
+
});
|
|
1807
|
+
if (resolved?.auth === void 0) return resolved;
|
|
1808
|
+
return resolved.auth.baseUrl === void 0 && config.baseUrl !== void 0 ? {
|
|
1809
|
+
...resolved,
|
|
1810
|
+
auth: {
|
|
1811
|
+
...resolved.auth,
|
|
1812
|
+
baseUrl: config.baseUrl
|
|
1813
|
+
}
|
|
1814
|
+
} : resolved;
|
|
1815
|
+
},
|
|
1816
|
+
getApiKeyForProvider: async (provider) => {
|
|
1817
|
+
const config = providerConfig(provider);
|
|
1818
|
+
if (config === void 0) return void 0;
|
|
1819
|
+
return ((await resolvePiProviderAuth({
|
|
1820
|
+
providerId: provider,
|
|
1821
|
+
providerConfig: config,
|
|
1822
|
+
store: oauthStoreOf(state)
|
|
1823
|
+
}))?.auth)?.apiKey;
|
|
1824
|
+
},
|
|
1825
|
+
isUsingOAuth: (model) => {
|
|
1826
|
+
const provider = String(model?.provider ?? "");
|
|
1827
|
+
return provider.length > 0 && providerSupportsOAuth(providerConfig(provider));
|
|
1828
|
+
},
|
|
1829
|
+
refresh: async () => {
|
|
1830
|
+
await catalog?.refresh();
|
|
1831
|
+
return {
|
|
1832
|
+
models: allModels(),
|
|
1833
|
+
errors: []
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
const contextCwd = typeof (sessionOverride?.header)?.cwd === "string" ? sessionOverride.header.cwd : cwdOf(agent);
|
|
1838
|
+
const base = {
|
|
1839
|
+
ui,
|
|
1840
|
+
mode: "rpc",
|
|
1841
|
+
hasUI: userQuestions !== void 0,
|
|
1842
|
+
cwd: contextCwd,
|
|
1843
|
+
sessionManager: session === void 0 ? state.bridge.readonlySessionManager({
|
|
1844
|
+
id: "pi2dsh-detached",
|
|
1845
|
+
events: []
|
|
1846
|
+
}, contextCwd) : state.bridge.readonlySessionManager(session, contextCwd),
|
|
1847
|
+
modelRegistry,
|
|
1781
1848
|
model: agent === void 0 ? void 0 : currentPiModel(state, agent),
|
|
1782
1849
|
scopedModels: catalog?.all() ?? [],
|
|
1783
1850
|
thinkingLevel: thinkingLevelOf(state, agent),
|
|
@@ -1793,9 +1860,55 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1793
1860
|
});
|
|
1794
1861
|
},
|
|
1795
1862
|
hasPendingMessages: () => false,
|
|
1796
|
-
shutdown: () =>
|
|
1863
|
+
shutdown: () => {
|
|
1864
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
1865
|
+
capability: "ctx.shutdown",
|
|
1866
|
+
reason: "Pi delegates shutdown behavior to the host, and on DSH the user owns process exit.",
|
|
1867
|
+
guidance: "The shutdown request was recorded and ignored.",
|
|
1868
|
+
packageName: state.packageName
|
|
1869
|
+
});
|
|
1870
|
+
},
|
|
1797
1871
|
getContextUsage: () => void 0,
|
|
1798
|
-
compact: () =>
|
|
1872
|
+
compact: (options) => {
|
|
1873
|
+
const ledger = capabilityLedgerOf(ctx, state);
|
|
1874
|
+
const compaction = optionalService(ctx, "compaction");
|
|
1875
|
+
const target = agent;
|
|
1876
|
+
const onError = options?.onError;
|
|
1877
|
+
if (compaction === void 0 || typeof target?.runMaintenance !== "function") {
|
|
1878
|
+
const gap = new PiCapabilityError({
|
|
1879
|
+
capability: "ctx.compact",
|
|
1880
|
+
reason: compaction === void 0 ? "this DSH composition mounts no compaction service." : "compaction needs a live DSH agent for this turn.",
|
|
1881
|
+
guidance: "Compaction runs through the host compaction plugin when one is composed.",
|
|
1882
|
+
packageName: state.packageName
|
|
1883
|
+
});
|
|
1884
|
+
ledger.reportDegraded({
|
|
1885
|
+
capability: "ctx.compact",
|
|
1886
|
+
reason: gap.message,
|
|
1887
|
+
guidance: "",
|
|
1888
|
+
packageName: state.packageName
|
|
1889
|
+
});
|
|
1890
|
+
onError?.(gap);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
compaction.compactNow(target, new AbortController().signal).then((result) => {
|
|
1894
|
+
if (result === null || result === void 0) return;
|
|
1895
|
+
const dsh = result;
|
|
1896
|
+
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");
|
|
1897
|
+
const onComplete = options?.onComplete;
|
|
1898
|
+
onComplete?.({
|
|
1899
|
+
summary: summaryText,
|
|
1900
|
+
firstKeptEntryId: "",
|
|
1901
|
+
tokensBefore: dsh.shadowedTokenCount ?? 0
|
|
1902
|
+
});
|
|
1903
|
+
}).catch((error) => {
|
|
1904
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
1905
|
+
if (onError !== void 0) {
|
|
1906
|
+
onError(failure);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
logger(ctx).warn(`[pi2dsh] plugin "${state.packageName}": ctx.compact failed: ${failure.message}`);
|
|
1910
|
+
});
|
|
1911
|
+
},
|
|
1799
1912
|
getSystemPrompt: () => state.currentSystemPrompt,
|
|
1800
1913
|
__agent: agent,
|
|
1801
1914
|
__notices: notices
|
|
@@ -1806,11 +1919,139 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
1806
1919
|
const wait = agent?.whenIdle;
|
|
1807
1920
|
if (typeof wait === "function") await wait.call(agent);
|
|
1808
1921
|
},
|
|
1809
|
-
newSession: () =>
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1922
|
+
newSession: async (options) => {
|
|
1923
|
+
const sessions = optionalService(ctx, "sessions");
|
|
1924
|
+
if (sessions === void 0) {
|
|
1925
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
1926
|
+
capability: "ctx.newSession",
|
|
1927
|
+
reason: "this DSH composition mounts no session service.",
|
|
1928
|
+
guidance: "",
|
|
1929
|
+
packageName: state.packageName
|
|
1930
|
+
});
|
|
1931
|
+
return { cancelled: true };
|
|
1932
|
+
}
|
|
1933
|
+
const parent = agentSession(agent);
|
|
1934
|
+
const created = sessions.create(void 0, { meta: {
|
|
1935
|
+
cwd: cwdOf(agent),
|
|
1936
|
+
...parent?.id === void 0 ? {} : { parentSession: parent.id }
|
|
1937
|
+
} });
|
|
1938
|
+
const withSession = options?.withSession;
|
|
1939
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, created));
|
|
1940
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
1941
|
+
capability: "ctx.newSession",
|
|
1942
|
+
reason: `a new DSH session was created (${String(created.id)}).`,
|
|
1943
|
+
guidance: "Which session the surface shows stays a host choice — open it from the DSH session list.",
|
|
1944
|
+
packageName: state.packageName
|
|
1945
|
+
});
|
|
1946
|
+
return { cancelled: false };
|
|
1947
|
+
},
|
|
1948
|
+
fork: async (entryId, options) => {
|
|
1949
|
+
const sessions = optionalService(ctx, "sessions");
|
|
1950
|
+
const source = agentSession(agent);
|
|
1951
|
+
if (sessions === void 0 || source === void 0) {
|
|
1952
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
1953
|
+
capability: "ctx.fork",
|
|
1954
|
+
reason: sessions === void 0 ? "this DSH composition mounts no session service." : "forking needs the live session of an active agent.",
|
|
1955
|
+
guidance: "",
|
|
1956
|
+
packageName: state.packageName
|
|
1957
|
+
});
|
|
1958
|
+
return { cancelled: true };
|
|
1959
|
+
}
|
|
1960
|
+
const seq = durableSeqOf(String(entryId));
|
|
1961
|
+
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`);
|
|
1962
|
+
const position = options?.position ?? "before";
|
|
1963
|
+
const events = source.events ?? [];
|
|
1964
|
+
const requested = Math.min(position === "at" ? seq : seq - 1, events.length - 1);
|
|
1965
|
+
const boundary = requested < 0 ? -1 : shrinkToTurnBoundary(events, requested);
|
|
1966
|
+
const child = boundary < 0 ? sessions.create(void 0, { meta: {
|
|
1967
|
+
cwd: cwdOf(agent),
|
|
1968
|
+
parentSession: source.id
|
|
1969
|
+
} }) : sessions.fork(source, boundary);
|
|
1970
|
+
const withSession = options?.withSession;
|
|
1971
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, child));
|
|
1972
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
1973
|
+
capability: "ctx.fork",
|
|
1974
|
+
reason: `the session was forked on DSH's official prefix-fork surface (child ${String(child.id)}; DSH forks land on completed-turn boundaries).`,
|
|
1975
|
+
guidance: "Open the forked session from the DSH session list.",
|
|
1976
|
+
packageName: state.packageName
|
|
1977
|
+
});
|
|
1978
|
+
return { cancelled: false };
|
|
1979
|
+
},
|
|
1980
|
+
navigateTree: async (targetId, options) => {
|
|
1981
|
+
const sessions = optionalService(ctx, "sessions");
|
|
1982
|
+
const source = agentSession(agent);
|
|
1983
|
+
if (sessions === void 0 || source === void 0) {
|
|
1984
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
1985
|
+
capability: "ctx.navigateTree",
|
|
1986
|
+
reason: sessions === void 0 ? "this DSH composition mounts no session service." : "tree navigation needs the live session of an active agent.",
|
|
1987
|
+
guidance: "",
|
|
1988
|
+
packageName: state.packageName
|
|
1989
|
+
});
|
|
1990
|
+
return { cancelled: true };
|
|
1991
|
+
}
|
|
1992
|
+
const seq = durableSeqOf(String(targetId));
|
|
1993
|
+
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`);
|
|
1994
|
+
const events = source.events ?? [];
|
|
1995
|
+
const capped = Math.min(seq, events.length - 1);
|
|
1996
|
+
const boundary = capped < 0 ? -1 : shrinkToTurnBoundary(events, capped);
|
|
1997
|
+
const child = boundary < 0 ? sessions.create(void 0, { meta: {
|
|
1998
|
+
cwd: cwdOf(agent),
|
|
1999
|
+
parentSession: source.id
|
|
2000
|
+
} }) : sessions.fork(source, boundary);
|
|
2001
|
+
if (options?.summarize === true) {
|
|
2002
|
+
const summary = await summarizeAbandonedBranch(ctx, state, agent, events, boundary, options);
|
|
2003
|
+
if (summary !== void 0) state.bridge.appendBranchSummary(String(child.id), summary, String(targetId));
|
|
2004
|
+
}
|
|
2005
|
+
if (typeof options?.label === "string") state.bridge.appendLabel(String(child.id), String(targetId), options.label);
|
|
2006
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2007
|
+
capability: "ctx.navigateTree",
|
|
2008
|
+
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).`,
|
|
2009
|
+
guidance: "Open the navigated session from the DSH session list.",
|
|
2010
|
+
packageName: state.packageName
|
|
2011
|
+
});
|
|
2012
|
+
return { cancelled: false };
|
|
2013
|
+
},
|
|
2014
|
+
switchSession: async (sessionPath, options) => {
|
|
2015
|
+
const sessions = optionalService(ctx, "sessions");
|
|
2016
|
+
if (sessions === void 0) {
|
|
2017
|
+
capabilityLedgerOf(ctx, state).reportDegraded({
|
|
2018
|
+
capability: "ctx.switchSession",
|
|
2019
|
+
reason: "this DSH composition mounts no session service.",
|
|
2020
|
+
guidance: "",
|
|
2021
|
+
packageName: state.packageName
|
|
2022
|
+
});
|
|
2023
|
+
return { cancelled: true };
|
|
2024
|
+
}
|
|
2025
|
+
const raw = String(sessionPath);
|
|
2026
|
+
const base = raw.replace(/\\/g, "/").split("/").pop() ?? raw;
|
|
2027
|
+
const candidate = base.endsWith(".jsonl") ? base.slice(0, -6) : base;
|
|
2028
|
+
const target = sessions.get(candidate) ?? sessions.list().find((entry) => String(entry.id) === candidate);
|
|
2029
|
+
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)`);
|
|
2030
|
+
const withSession = options?.withSession;
|
|
2031
|
+
await withSession?.(contextFor(ctx, state, agent, signal, true, target));
|
|
2032
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2033
|
+
capability: "ctx.switchSession",
|
|
2034
|
+
reason: `the live DSH session ${JSON.stringify(candidate)} was targeted.`,
|
|
2035
|
+
guidance: "Which session the surface shows stays a host choice — open it from the DSH session list.",
|
|
2036
|
+
packageName: state.packageName
|
|
2037
|
+
});
|
|
2038
|
+
return { cancelled: false };
|
|
2039
|
+
},
|
|
2040
|
+
reload: async () => {
|
|
2041
|
+
const remounts = state.shared.packageRemounts;
|
|
2042
|
+
if (remounts === void 0 || remounts.size === 0) return;
|
|
2043
|
+
for (const [name, remount] of remounts) try {
|
|
2044
|
+
await remount();
|
|
2045
|
+
} catch (error) {
|
|
2046
|
+
logger(ctx).warn(`[pi2dsh] ctx.reload: remounting "${name}" failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2047
|
+
}
|
|
2048
|
+
capabilityLedgerOf(ctx, state).reportHostDecision({
|
|
2049
|
+
capability: "ctx.reload",
|
|
2050
|
+
reason: "extension entries were disposed and remounted through a fresh loader.",
|
|
2051
|
+
guidance: "Skills, prompts, and themes are host-managed and reload when dsh restarts.",
|
|
2052
|
+
packageName: state.packageName
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
1814
2055
|
});
|
|
1815
2056
|
return base;
|
|
1816
2057
|
}
|
|
@@ -2327,45 +2568,102 @@ function sharedHostStateOf(ctx) {
|
|
|
2327
2568
|
return shared;
|
|
2328
2569
|
}
|
|
2329
2570
|
/**
|
|
2330
|
-
* Image-admission companion routes
|
|
2331
|
-
*
|
|
2332
|
-
* registers a `<route>-vision` route that admits images at the host's
|
|
2571
|
+
* Image-admission companion routes: for every text-only route in the DSH
|
|
2572
|
+
* llm directory, a `<route>-vision` route that admits images at the host's
|
|
2333
2573
|
* admission checks, replaces image blocks with explicit path-carrying
|
|
2334
|
-
* notices, and forwards text-only to the original route.
|
|
2335
|
-
* an
|
|
2336
|
-
*
|
|
2337
|
-
*
|
|
2338
|
-
*
|
|
2574
|
+
* notices, and forwards text-only to the original route. What happens to
|
|
2575
|
+
* an admitted image is decided at run time by whatever is mounted — a
|
|
2576
|
+
* vision extension analyzes it through the turn's entering messages, and
|
|
2577
|
+
* without one the notice's file path lets any image-capable tool read it —
|
|
2578
|
+
* so companions need no knowledge of any particular plugin and are
|
|
2579
|
+
* registered AUTOMATICALLY (zero configuration). `visionCompanions: false`
|
|
2580
|
+
* turns them off; an explicit `{ <route>: [modelIds] }` map narrows them.
|
|
2581
|
+
* The directory is live: adapters-updated re-sweeps, adding companions for
|
|
2582
|
+
* new text-only routes and disposing companions whose original vanished.
|
|
2583
|
+
* The companion is an ordinary directory entry (single-directory
|
|
2584
|
+
* contract); Pi's ctx.model reports the original route for it
|
|
2585
|
+
* (companionRoutes). Idempotent per host.
|
|
2339
2586
|
*/
|
|
2340
|
-
function registerVisionCompanions(ctx,
|
|
2341
|
-
if (
|
|
2587
|
+
function registerVisionCompanions(ctx, config) {
|
|
2588
|
+
if (config === false) return;
|
|
2342
2589
|
const shared = sharedHostStateOf(ctx);
|
|
2343
2590
|
const llm = llmOf(ctx);
|
|
2344
|
-
if (llm === void 0)
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
if (
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
if (shared.providerRouteDisposers.has(`${COMPANION_ROUTE_PREFIX}${companionId}`)) continue;
|
|
2357
|
-
try {
|
|
2358
|
-
const dispose = llm.registerAdapter([companionId], imageAdmissionCompanionAdapter({
|
|
2591
|
+
if (llm === void 0) return;
|
|
2592
|
+
const sweep = async () => {
|
|
2593
|
+
const explicit = config;
|
|
2594
|
+
const providers = llm.listProviders();
|
|
2595
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
2596
|
+
if (explicit !== void 0) for (const [originalId, modelIds] of Object.entries(explicit)) {
|
|
2597
|
+
if (!Array.isArray(modelIds) || modelIds.length === 0) continue;
|
|
2598
|
+
if (!providers.some((provider) => provider.id === originalId)) {
|
|
2599
|
+
logger(ctx).warn(`[pi2dsh] visionCompanions names route ${JSON.stringify(originalId)}, but no such llm route exists; no companion route was registered`);
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
wanted.set(`${originalId}-vision`, {
|
|
2359
2603
|
originalId,
|
|
2360
|
-
imageModels: new Set(modelIds.map(String))
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
shared.
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2604
|
+
imageModels: new Set(modelIds.map(String))
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
else for (const provider of providers) {
|
|
2608
|
+
if (shared.companionRoutes.has(provider.id)) continue;
|
|
2609
|
+
try {
|
|
2610
|
+
const models = await llm.listModels(provider.id);
|
|
2611
|
+
if (models.length === 0) continue;
|
|
2612
|
+
if (models.every((model) => !(Array.isArray(model.inputModalities) && model.inputModalities.includes("image")))) wanted.set(`${provider.id}-vision`, {
|
|
2613
|
+
originalId: provider.id,
|
|
2614
|
+
imageModels: void 0
|
|
2615
|
+
});
|
|
2616
|
+
} catch {}
|
|
2617
|
+
}
|
|
2618
|
+
for (const [key, dispose] of shared.providerRouteDisposers) {
|
|
2619
|
+
if (!key.startsWith(COMPANION_ROUTE_PREFIX)) continue;
|
|
2620
|
+
const companionId = key.slice(17);
|
|
2621
|
+
const originalId = shared.companionRoutes.get(companionId);
|
|
2622
|
+
if (originalId !== void 0 && !providers.some((provider) => provider.id === originalId)) {
|
|
2623
|
+
shared.providerRouteDisposers.delete(key);
|
|
2624
|
+
dispose();
|
|
2625
|
+
logger(ctx).info(`[pi2dsh] companion route ${JSON.stringify(companionId)} disposed (its original route ${JSON.stringify(originalId)} left the directory)`);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
for (const [companionId, spec] of wanted) {
|
|
2629
|
+
shared.companionRoutes.set(companionId, spec.originalId);
|
|
2630
|
+
if (shared.providerRouteDisposers.has(`${COMPANION_ROUTE_PREFIX}${companionId}`)) continue;
|
|
2631
|
+
try {
|
|
2632
|
+
const dispose = llm.registerAdapter([companionId], imageAdmissionCompanionAdapter({
|
|
2633
|
+
originalId: spec.originalId,
|
|
2634
|
+
...spec.imageModels === void 0 ? {} : { imageModels: spec.imageModels },
|
|
2635
|
+
llm,
|
|
2636
|
+
materializeImage: (attachment) => materializeAttachmentImage(ctx, attachment)
|
|
2637
|
+
}));
|
|
2638
|
+
shared.providerRouteDisposers.set(`${COMPANION_ROUTE_PREFIX}${companionId}`, dispose);
|
|
2639
|
+
logger(ctx).info(`[pi2dsh] image-admission companion route ${JSON.stringify(companionId)} registered for ${JSON.stringify(spec.originalId)}`);
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
logger(ctx).warn(`[pi2dsh] companion route ${JSON.stringify(companionId)} already has a live adapter in this host (${error instanceof Error ? error.message : String(error)}); reusing it`);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
let sweeping = false;
|
|
2646
|
+
let queued = false;
|
|
2647
|
+
const runSweep = () => {
|
|
2648
|
+
if (sweeping) {
|
|
2649
|
+
queued = true;
|
|
2650
|
+
return;
|
|
2368
2651
|
}
|
|
2652
|
+
sweeping = true;
|
|
2653
|
+
sweep().finally(() => {
|
|
2654
|
+
sweeping = false;
|
|
2655
|
+
if (queued) {
|
|
2656
|
+
queued = false;
|
|
2657
|
+
runSweep();
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
};
|
|
2661
|
+
runSweep();
|
|
2662
|
+
if (shared.companionSweepSubscribed !== true) {
|
|
2663
|
+
shared.companionSweepSubscribed = true;
|
|
2664
|
+
ctx.on("llm/adapters-updated", () => {
|
|
2665
|
+
runSweep();
|
|
2666
|
+
});
|
|
2369
2667
|
}
|
|
2370
2668
|
}
|
|
2371
2669
|
const IMAGE_EXTENSIONS = {
|
|
@@ -2430,7 +2728,12 @@ function registerLoginCommand(ctx, state) {
|
|
|
2430
2728
|
});
|
|
2431
2729
|
}
|
|
2432
2730
|
function registerTool(ctx, state, tool) {
|
|
2433
|
-
if (state.tools.has(tool.name))
|
|
2731
|
+
if (state.tools.has(tool.name)) {
|
|
2732
|
+
const previous = state.tools.get(tool.name);
|
|
2733
|
+
state.tools.set(tool.name, tool);
|
|
2734
|
+
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)`);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2434
2737
|
const normalized = normalizeToolSchema(tool.parameters);
|
|
2435
2738
|
for (const warning of normalized.warnings) logger(ctx).warn(`[pi2dsh] tool ${tool.name}: ${warning}`);
|
|
2436
2739
|
state.tools.set(tool.name, tool);
|
|
@@ -2445,12 +2748,13 @@ function registerTool(ctx, state, tool) {
|
|
|
2445
2748
|
},
|
|
2446
2749
|
isConcurrencySafe: () => tool.executionMode === "parallel",
|
|
2447
2750
|
async execute(args, exec) {
|
|
2751
|
+
const live = state.tools.get(tool.name) ?? tool;
|
|
2448
2752
|
const mutated = state.argMutations.get(exec);
|
|
2449
2753
|
if (mutated !== void 0) state.argMutations.delete(exec);
|
|
2450
2754
|
const effective = mutated ?? args;
|
|
2451
|
-
const prepared =
|
|
2755
|
+
const prepared = live.prepareArguments?.(cloneJson(effective)) ?? effective;
|
|
2452
2756
|
const agent = exec.agent;
|
|
2453
|
-
const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () =>
|
|
2757
|
+
const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
|
|
2454
2758
|
dispatch(state, "tool_execution_update", {
|
|
2455
2759
|
type: "tool_execution_update",
|
|
2456
2760
|
toolCallId: String(exec.callId),
|
|
@@ -2591,8 +2895,8 @@ function dshCommandName(ctx, piName) {
|
|
|
2591
2895
|
}
|
|
2592
2896
|
function registerCommand(ctx, state, command) {
|
|
2593
2897
|
if (state.commands.has(command.name)) {
|
|
2594
|
-
state.
|
|
2595
|
-
|
|
2898
|
+
state.commands.set(command.name, command);
|
|
2899
|
+
return;
|
|
2596
2900
|
}
|
|
2597
2901
|
state.commands.set(command.name, command);
|
|
2598
2902
|
const commands = ctx.get("commands");
|
|
@@ -2608,7 +2912,8 @@ function registerCommand(ctx, state, command) {
|
|
|
2608
2912
|
async handler(invocation) {
|
|
2609
2913
|
const agent = invocation.agent;
|
|
2610
2914
|
const commandContext = contextFor(ctx, state, agent, invocation.signal, true);
|
|
2611
|
-
|
|
2915
|
+
const live = state.commands.get(command.name) ?? command;
|
|
2916
|
+
await state.agentScope.run(agent, () => live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext));
|
|
2612
2917
|
const notices = commandContext.__notices;
|
|
2613
2918
|
return {
|
|
2614
2919
|
kind: "success",
|
|
@@ -2897,7 +3202,7 @@ async function registerPromptCommands(ctx, state, rootDir, manifest) {
|
|
|
2897
3202
|
});
|
|
2898
3203
|
}
|
|
2899
3204
|
}
|
|
2900
|
-
async function loadExtensions(rootDir, manifest, api, onExtensionError) {
|
|
3205
|
+
async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabilityGap, onHostInfraReference) {
|
|
2901
3206
|
const resolveShim = async (name) => {
|
|
2902
3207
|
const compiled = fileURLToPath(new URL(`./compat/${name}.mjs`, import.meta.url));
|
|
2903
3208
|
try {
|
|
@@ -2937,14 +3242,22 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError) {
|
|
|
2937
3242
|
});
|
|
2938
3243
|
const failures = [];
|
|
2939
3244
|
let mounted = 0;
|
|
2940
|
-
for (const extension of manifest.extensions)
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
3245
|
+
for (const extension of manifest.extensions) {
|
|
3246
|
+
try {
|
|
3247
|
+
const source = await readFile(join(rootDir, extension), "utf8");
|
|
3248
|
+
const infraImport = /import[^;]*?\b(ModelRuntime|DefaultPackageManager)\b[^;]*?from\s*['"]@(?:earendil-works|mariozechner)\/pi-coding-agent['"]/su.exec(source);
|
|
3249
|
+
if (infraImport !== null) onHostInfraReference?.(infraImport[1], extension);
|
|
3250
|
+
} catch {}
|
|
3251
|
+
try {
|
|
3252
|
+
const loaded = await jiti.import(join(rootDir, extension));
|
|
3253
|
+
const candidate = typeof loaded === "object" && loaded !== null && "default" in loaded ? loaded.default : loaded;
|
|
3254
|
+
if (typeof candidate !== "function") throw new TypeError(`Pi extension ${extension} has no default factory function`);
|
|
3255
|
+
await candidate(api);
|
|
3256
|
+
mounted += 1;
|
|
3257
|
+
} catch (error) {
|
|
3258
|
+
if (error instanceof PiCapabilityError || error instanceof Error && error.name === "PiCapabilityError") onCapabilityGap?.(error, extension);
|
|
3259
|
+
failures.push(`${extension}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3260
|
+
}
|
|
2948
3261
|
}
|
|
2949
3262
|
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")}`);
|
|
2950
3263
|
for (const failure of failures) onExtensionError?.(failure);
|
|
@@ -2965,6 +3278,7 @@ async function applyPiPackage(ctx, options) {
|
|
|
2965
3278
|
const shared = sharedHostStateOf(ctx);
|
|
2966
3279
|
const state = {
|
|
2967
3280
|
shared,
|
|
3281
|
+
packageName: options.manifest.package.name,
|
|
2968
3282
|
handlers: /* @__PURE__ */ new Map(),
|
|
2969
3283
|
tools: runtimeTools,
|
|
2970
3284
|
runner: new ExtensionRunner(piToolRecords),
|
|
@@ -3058,8 +3372,34 @@ async function applyPiPackage(ctx, options) {
|
|
|
3058
3372
|
}, subagentOptions));
|
|
3059
3373
|
ctx.effect(() => () => __setSubagentSessionFactory(void 0));
|
|
3060
3374
|
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
3061
|
-
|
|
3062
|
-
|
|
3375
|
+
const onExtensionError = (failure) => logger(ctx).warn(`[pi2dsh] extension entry failed and was skipped (matching Pi's per-extension error isolation): ${failure}`);
|
|
3376
|
+
const onCapabilityGap = (gap) => capabilityLedgerOf(ctx, state).reportUnusable({
|
|
3377
|
+
capability: gap.capability,
|
|
3378
|
+
reason: "this package needs it during startup.",
|
|
3379
|
+
guidance: gap.message,
|
|
3380
|
+
packageName: state.packageName
|
|
3381
|
+
});
|
|
3382
|
+
const onHostInfraReference = (symbol) => capabilityLedgerOf(ctx, state).reportStartupReference({
|
|
3383
|
+
capability: symbol,
|
|
3384
|
+
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).",
|
|
3385
|
+
guidance: "",
|
|
3386
|
+
packageName: state.packageName
|
|
3387
|
+
});
|
|
3388
|
+
const mountExtensions = () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference);
|
|
3389
|
+
await mountExtensions();
|
|
3390
|
+
const remounts = shared.packageRemounts ??= /* @__PURE__ */ new Map();
|
|
3391
|
+
remounts.set(state.packageName, async () => {
|
|
3392
|
+
state.handlers.clear();
|
|
3393
|
+
state.eventBus.removeAllListeners();
|
|
3394
|
+
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
3395
|
+
await mountExtensions();
|
|
3396
|
+
});
|
|
3397
|
+
ctx.effect(() => () => {
|
|
3398
|
+
remounts.delete(state.packageName);
|
|
3399
|
+
});
|
|
3400
|
+
const health = state.shared.capabilityLedger?.healthOf(state.packageName);
|
|
3401
|
+
const healthSuffix = health === void 0 || health.status === "ok" ? "" : ` — ${health.status.toUpperCase()}: missing Pi capabilities ${health.gaps.join(", ")}`;
|
|
3402
|
+
logger(ctx).info(`[pi2dsh] loaded ${options.manifest.package.name}: ${state.tools.size} tools, ${state.commands.size} commands, ${options.manifest.skillDirs.length} skill roots${healthSuffix}`);
|
|
3063
3403
|
}
|
|
3064
3404
|
const runtimeInternals = {
|
|
3065
3405
|
expandPrompt,
|
|
@@ -3070,4 +3410,4 @@ const runtimeInternals = {
|
|
|
3070
3410
|
//#endregion
|
|
3071
3411
|
export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
|
|
3072
3412
|
|
|
3073
|
-
//# sourceMappingURL=runtime-
|
|
3413
|
+
//# sourceMappingURL=runtime-Ce3PTbWU.mjs.map
|