pi2dsh 0.13.2 → 0.14.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 +24 -19
- package/README.zh.md +20 -15
- package/dist/{analyzer-CJFwQlSG.mjs → analyzer-DPZ8LzSt.mjs} +2 -2
- package/dist/{analyzer-CJFwQlSG.mjs.map → analyzer-DPZ8LzSt.mjs.map} +1 -1
- package/dist/anthropic-messages-CaYTgvLF.mjs +6436 -0
- package/dist/anthropic-messages-CaYTgvLF.mjs.map +1 -0
- package/dist/azure-openai-responses-HKWkzl2m.mjs +208 -0
- package/dist/azure-openai-responses-HKWkzl2m.mjs.map +1 -0
- package/dist/cli.mjs +3 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/compat/pi-ai.d.mts +3 -2636
- package/dist/compat/pi-ai.mjs +3 -2
- package/dist/compat/pi-coding-agent.d.mts +2 -2
- package/dist/compat/pi-coding-agent.mjs +2 -2
- package/dist/{mcp-config-_idn-pzI.mjs → compatibility-CP2tpyhg.mjs} +20 -185
- package/dist/compatibility-CP2tpyhg.mjs.map +1 -0
- package/dist/completionchunk-Dhk56lZl.mjs +5212 -0
- package/dist/completionchunk-Dhk56lZl.mjs.map +1 -0
- package/dist/{credentials-oauth-Txs6oew8.mjs → credentials-oauth-Didpkkjx.mjs} +3 -3
- package/dist/{credentials-oauth-Txs6oew8.mjs.map → credentials-oauth-Didpkkjx.mjs.map} +1 -1
- package/dist/credentials-oauth.mjs +1 -1
- package/dist/deferred-tools-BD4g5QUx.mjs +32 -0
- package/dist/deferred-tools-BD4g5QUx.mjs.map +1 -0
- package/dist/diagnostics-BqYkodgB.mjs +35 -0
- package/dist/diagnostics-BqYkodgB.mjs.map +1 -0
- package/dist/dist-QBBA-dL_.mjs +1246 -0
- package/dist/dist-QBBA-dL_.mjs.map +1 -0
- package/dist/error-body-DKA3lknR.mjs +104 -0
- package/dist/error-body-DKA3lknR.mjs.map +1 -0
- package/dist/event-stream-QxN1yvIF.mjs +64 -0
- package/dist/event-stream-QxN1yvIF.mjs.map +1 -0
- package/dist/from-BCscBQ9P.mjs +3856 -0
- package/dist/from-BCscBQ9P.mjs.map +1 -0
- package/dist/github-copilot-headers-CZo2QHrt.mjs +25 -0
- package/dist/github-copilot-headers-CZo2QHrt.mjs.map +1 -0
- package/dist/google-generative-ai-2gCFYp-t.mjs +356 -0
- package/dist/google-generative-ai-2gCFYp-t.mjs.map +1 -0
- package/dist/google-shared-75IdVyrx.mjs +29041 -0
- package/dist/google-shared-75IdVyrx.mjs.map +1 -0
- package/dist/google-vertex-Bhyo4z2i.mjs +400 -0
- package/dist/google-vertex-Bhyo4z2i.mjs.map +1 -0
- package/dist/hash-Dbggamac.mjs +19 -0
- package/dist/hash-Dbggamac.mjs.map +1 -0
- package/dist/headers-CPzZhJQn.mjs +17 -0
- package/dist/headers-CPzZhJQn.mjs.map +1 -0
- package/dist/host.d.mts +14 -2
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +44 -5
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +35 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +128 -22
- package/dist/index.mjs.map +1 -1
- package/dist/json-parse-CZ0U0sj_.mjs +384 -0
- package/dist/json-parse-CZ0U0sj_.mjs.map +1 -0
- package/dist/mcp-config-jmzbgdHa.mjs +181 -0
- package/dist/mcp-config-jmzbgdHa.mjs.map +1 -0
- package/dist/mistral-conversations-DJyeTo8C.mjs +36921 -0
- package/dist/mistral-conversations-DJyeTo8C.mjs.map +1 -0
- package/dist/multipart-parser-SRcGkVuX.mjs +301 -0
- package/dist/multipart-parser-SRcGkVuX.mjs.map +1 -0
- package/dist/{pi-ai-D7PqVeWs.mjs → openai-DkzRh5_G.mjs} +78 -5063
- package/dist/openai-DkzRh5_G.mjs.map +1 -0
- package/dist/openai-codex-responses-DedxQjNG.mjs +1220 -0
- package/dist/openai-codex-responses-DedxQjNG.mjs.map +1 -0
- package/dist/openai-completions-DrjrWRdX.mjs +945 -0
- package/dist/openai-completions-DrjrWRdX.mjs.map +1 -0
- package/dist/openai-prompt-cache-BDWtZRKh.mjs +11 -0
- package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +1 -0
- package/dist/openai-responses-BRdGpBX6.mjs +251 -0
- package/dist/openai-responses-BRdGpBX6.mjs.map +1 -0
- package/dist/openai-responses-shared-BmnSvdKL.mjs +570 -0
- package/dist/openai-responses-shared-BmnSvdKL.mjs.map +1 -0
- package/dist/otel-CBhmS-NR.mjs +24935 -0
- package/dist/otel-CBhmS-NR.mjs.map +1 -0
- package/dist/pi-ai-DpqiLdPA.mjs +3244 -0
- package/dist/pi-ai-DpqiLdPA.mjs.map +1 -0
- package/dist/pi-ai-ZU5MelXy.d.mts +2678 -0
- package/dist/pi-ai-ZU5MelXy.d.mts.map +1 -0
- package/dist/{pi-coding-agent-B0PT23gE.d.mts → pi-coding-agent-69yY0ke-.d.mts} +4 -2
- package/dist/{pi-coding-agent-B0PT23gE.d.mts.map → pi-coding-agent-69yY0ke-.d.mts.map} +1 -1
- package/dist/{pi-coding-agent-BVZu0dHG.mjs → pi-coding-agent-DK8hbJP5.mjs} +0 -0
- package/dist/{pi-coding-agent-BVZu0dHG.mjs.map → pi-coding-agent-DK8hbJP5.mjs.map} +1 -1
- package/dist/pi-messages-D61G2tgs.mjs +307 -0
- package/dist/pi-messages-D61G2tgs.mjs.map +1 -0
- package/dist/provider-env-C72pF4vP.mjs +40 -0
- package/dist/provider-env-C72pF4vP.mjs.map +1 -0
- package/dist/provider-retry-Cw6YJ4cr.mjs +80 -0
- package/dist/provider-retry-Cw6YJ4cr.mjs.map +1 -0
- package/dist/rolldown-runtime-CTfmNlz1.mjs +44 -0
- package/dist/{runtime-DEjs_Qlf.mjs → runtime-Bb9Bfk4M.mjs} +715 -130
- package/dist/runtime-Bb9Bfk4M.mjs.map +1 -0
- package/dist/runtime.d.mts +149 -26
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/simple-options-B_GO8FDE.mjs +456 -0
- package/dist/simple-options-B_GO8FDE.mjs.map +1 -0
- package/dist/src-DlTagfW5.mjs +1186 -0
- package/dist/src-DlTagfW5.mjs.map +1 -0
- package/dist/{types-7IWJPPvS.d.mts → types-B9VBF8le.d.mts} +2 -9
- package/dist/{types-7IWJPPvS.d.mts.map → types-B9VBF8le.d.mts.map} +1 -1
- package/package.json +9 -7
- package/dist/compat/pi-ai.d.mts.map +0 -1
- package/dist/mcp-config-_idn-pzI.mjs.map +0 -1
- package/dist/pi-ai-D7PqVeWs.mjs.map +0 -1
- package/dist/runtime-DEjs_Qlf.mjs.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
import { P as generateBranchSummary, Tn as PiCapabilityError, b as Theme, d as ExtensionRunner, w as __setSubagentSessionFactory, wn as CapabilityLedger } from "./pi-coding-agent-BVZu0dHG.mjs";
|
|
2
|
+
import { En as PiCapabilityError, F as generateBranchSummary, Tn as CapabilityLedger, b as Theme, d as ExtensionRunner, w as __runWithSubagentSessionFactory } from "./pi-coding-agent-DK8hbJP5.mjs";
|
|
4
3
|
import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
|
|
5
|
-
import {
|
|
4
|
+
import { $ as isContextOverflow, Q as AssistantMessageEventStream, S as getSupportedThinkingLevels, c as builtinProviders, o as __runWithPiAiRuntime, r as __createPiAiRuntimeRegistry } from "./pi-ai-DpqiLdPA.mjs";
|
|
5
|
+
import { W as getKeybindings } from "./pi-tui-DruSeKmd.mjs";
|
|
6
|
+
import { a as FileCredentialStore, c as providerSupportsOAuth, i as oauthCredentialRef, l as resolvePiProviderAuth, o as joinSignals, s as loginPiProvider, u as storedOAuthCredential } from "./credentials-oauth-Didpkkjx.mjs";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
8
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
9
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
@@ -13,6 +14,7 @@ import { EventEmitter } from "node:events";
|
|
|
13
14
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
15
|
import { createJiti } from "jiti";
|
|
15
16
|
import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmError, QUOTA_EXCEEDED_CODE, createUserMessage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
|
|
17
|
+
import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
16
18
|
import { renderPrompt } from "@deepseek-ai/dsh-system-prompt";
|
|
17
19
|
import { randomUUID } from "node:crypto";
|
|
18
20
|
import { foldSurface } from "@deepseek-ai/dsh-session";
|
|
@@ -356,6 +358,18 @@ var PiSessionBridge = class {
|
|
|
356
358
|
//#endregion
|
|
357
359
|
//#region src/subagent-bridge.ts
|
|
358
360
|
let subagentSerial = 0;
|
|
361
|
+
/**
|
|
362
|
+
* Cross-scope subscription base. Session events dispatch under the OWNING
|
|
363
|
+
* session's scope; a package instance mounted per Agent registers listeners
|
|
364
|
+
* inside its own Agent's scope and would never see a child session's events.
|
|
365
|
+
* The bridge's callbacks filter by exact session/agent identity, so they
|
|
366
|
+
* subscribe unscoped at the root context — the same posture the old
|
|
367
|
+
* host-level mount had.
|
|
368
|
+
*/
|
|
369
|
+
function unscopedEventContext(cordis) {
|
|
370
|
+
const host = cordis;
|
|
371
|
+
return host.root ?? host;
|
|
372
|
+
}
|
|
359
373
|
/** Flatten a Pi message's content to text for transcript seeding. */
|
|
360
374
|
function piMessageText(message) {
|
|
361
375
|
const content = message.content;
|
|
@@ -395,7 +409,7 @@ var PiBridgedAgentSession = class {
|
|
|
395
409
|
this.#session = handle.agent.session ?? {};
|
|
396
410
|
this.#tools = tools;
|
|
397
411
|
this.#activeToolNames = tools.map((tool) => tool?.name).filter((name) => typeof name === "string");
|
|
398
|
-
const cordis = host.cordis;
|
|
412
|
+
const cordis = unscopedEventContext(host.cordis);
|
|
399
413
|
const offEvents = cordis.on("session/event", (session, event) => {
|
|
400
414
|
if (session !== this.#session) return;
|
|
401
415
|
this.#project(event);
|
|
@@ -594,7 +608,7 @@ var PiBridgedAgentSession = class {
|
|
|
594
608
|
type: "text",
|
|
595
609
|
text: String(text)
|
|
596
610
|
}]);
|
|
597
|
-
const cordis = this.#host.cordis;
|
|
611
|
+
const cordis = unscopedEventContext(this.#host.cordis);
|
|
598
612
|
const completed = new Promise((resolve) => {
|
|
599
613
|
const off = cordis.on("session/event", (session, event) => {
|
|
600
614
|
if (session !== this.#session) return;
|
|
@@ -2274,7 +2288,12 @@ function piProviderDshAdapter(providerId, provider, host) {
|
|
|
2274
2288
|
async *stream(options) {
|
|
2275
2289
|
const auth = (await host.resolveAuth())?.auth;
|
|
2276
2290
|
const modelId = String(options.model ?? "");
|
|
2277
|
-
|
|
2291
|
+
let model = providerModels(provider).find((entry) => entry.id === modelId);
|
|
2292
|
+
if (model === void 0 && host.ensureModel !== void 0) {
|
|
2293
|
+
await host.ensureModel(modelId);
|
|
2294
|
+
model = providerModels(provider).find((entry) => entry.id === modelId);
|
|
2295
|
+
}
|
|
2296
|
+
model ??= {
|
|
2278
2297
|
id: modelId,
|
|
2279
2298
|
provider: providerId
|
|
2280
2299
|
};
|
|
@@ -2297,6 +2316,7 @@ function piProviderDshAdapter(providerId, provider, host) {
|
|
|
2297
2316
|
...host.beforeProviderRequest === void 0 ? {} : { onPayload: (payload) => host.beforeProviderRequest(payload, {
|
|
2298
2317
|
provider: providerId,
|
|
2299
2318
|
model,
|
|
2319
|
+
...typeof options.sessionId === "string" ? { sessionId: options.sessionId } : {},
|
|
2300
2320
|
...options.signal instanceof AbortSignal ? { signal: options.signal } : {}
|
|
2301
2321
|
}) },
|
|
2302
2322
|
...effort === void 0 || effort === "off" ? {} : { reasoning: effort }
|
|
@@ -2654,6 +2674,220 @@ function registerPiProviderRoute(options) {
|
|
|
2654
2674
|
}
|
|
2655
2675
|
}
|
|
2656
2676
|
//#endregion
|
|
2677
|
+
//#region src/tui-surfaces.ts
|
|
2678
|
+
function service(ctx, name) {
|
|
2679
|
+
try {
|
|
2680
|
+
return ctx.get(name, false);
|
|
2681
|
+
} catch {
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
function surfaceToken(value) {
|
|
2686
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
2687
|
+
if (normalized.length === 0) return "entry";
|
|
2688
|
+
return /^[a-z]/u.test(normalized) ? normalized : `k-${normalized}`;
|
|
2689
|
+
}
|
|
2690
|
+
function sceneIdFor(packageName) {
|
|
2691
|
+
return `pi2dsh-${surfaceToken(packageName)}-custom`;
|
|
2692
|
+
}
|
|
2693
|
+
function statusKeyFor(packageName, key) {
|
|
2694
|
+
return `pi2dsh:${surfaceToken(packageName)}:${surfaceToken(key)}`;
|
|
2695
|
+
}
|
|
2696
|
+
function rawTerminalInput(input, key, event) {
|
|
2697
|
+
const keypress = event?.keypress;
|
|
2698
|
+
if (typeof keypress?.sequence === "string" && keypress.sequence.length > 0) return keypress.sequence;
|
|
2699
|
+
if (key.return === true) return "\r";
|
|
2700
|
+
if (key.escape === true) return "\x1B";
|
|
2701
|
+
if (key.tab === true) return " ";
|
|
2702
|
+
if (key.backspace === true) return "";
|
|
2703
|
+
if (key.upArrow === true) return "\x1B[A";
|
|
2704
|
+
if (key.downArrow === true) return "\x1B[B";
|
|
2705
|
+
if (key.rightArrow === true) return "\x1B[C";
|
|
2706
|
+
if (key.leftArrow === true) return "\x1B[D";
|
|
2707
|
+
if (key.ctrl === true && input.length === 1) {
|
|
2708
|
+
const code = input.toLowerCase().charCodeAt(0);
|
|
2709
|
+
if (code >= 97 && code <= 122) return String.fromCharCode(code - 96);
|
|
2710
|
+
}
|
|
2711
|
+
return input;
|
|
2712
|
+
}
|
|
2713
|
+
function preferredWidth(options) {
|
|
2714
|
+
const width = (typeof options?.overlayOptions === "function" ? options.overlayOptions() : options?.overlayOptions)?.width;
|
|
2715
|
+
return typeof width === "number" && Number.isFinite(width) && width > 0 ? Math.floor(width) : void 0;
|
|
2716
|
+
}
|
|
2717
|
+
/**
|
|
2718
|
+
* One package-scoped Pi custom-surface host backed by one dsh-TUI scene.
|
|
2719
|
+
* The MCP adapter is a consumer, not a special case: every component using
|
|
2720
|
+
* the public Pi component protocol follows the same path.
|
|
2721
|
+
*/
|
|
2722
|
+
var TuiSurfaceAdapter = class {
|
|
2723
|
+
packageName;
|
|
2724
|
+
instanceKey;
|
|
2725
|
+
sceneId;
|
|
2726
|
+
scenes;
|
|
2727
|
+
status;
|
|
2728
|
+
statusDisposers = /* @__PURE__ */ new Map();
|
|
2729
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2730
|
+
unregisterScene;
|
|
2731
|
+
revision = 0;
|
|
2732
|
+
active;
|
|
2733
|
+
disposed = false;
|
|
2734
|
+
constructor(ctx, packageName, instanceKey) {
|
|
2735
|
+
this.packageName = packageName;
|
|
2736
|
+
this.instanceKey = instanceKey;
|
|
2737
|
+
const scenes = service(ctx, "tuiScenes");
|
|
2738
|
+
if (scenes === void 0) throw new Error("pi2dsh: dsh-TUI scene service is unavailable");
|
|
2739
|
+
this.scenes = scenes;
|
|
2740
|
+
this.status = service(ctx, "tuiStatus");
|
|
2741
|
+
this.sceneId = sceneIdFor(instanceKey === void 0 ? packageName : `${packageName}-${instanceKey}`);
|
|
2742
|
+
this.unregisterScene = scenes.register({
|
|
2743
|
+
id: this.sceneId,
|
|
2744
|
+
title: `${packageName} custom panel`,
|
|
2745
|
+
component: (props) => this.scene(props)
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
get available() {
|
|
2749
|
+
return !this.disposed;
|
|
2750
|
+
}
|
|
2751
|
+
subscribe(listener) {
|
|
2752
|
+
this.listeners.add(listener);
|
|
2753
|
+
return () => this.listeners.delete(listener);
|
|
2754
|
+
}
|
|
2755
|
+
snapshot() {
|
|
2756
|
+
return this.revision;
|
|
2757
|
+
}
|
|
2758
|
+
setStatus(key, text) {
|
|
2759
|
+
if (this.disposed || this.status === void 0) return;
|
|
2760
|
+
const dshKey = statusKeyFor(this.instanceKey === void 0 ? this.packageName : `${this.packageName}-${this.instanceKey}`, key);
|
|
2761
|
+
this.statusDisposers.get(dshKey)?.();
|
|
2762
|
+
this.statusDisposers.delete(dshKey);
|
|
2763
|
+
if (text === void 0) return;
|
|
2764
|
+
const dispose = this.status.set(dshKey, String(text));
|
|
2765
|
+
if (typeof dispose === "function") this.statusDisposers.set(dshKey, dispose);
|
|
2766
|
+
}
|
|
2767
|
+
custom(factory, theme, keybindings, options) {
|
|
2768
|
+
if (this.disposed) return Promise.resolve(void 0);
|
|
2769
|
+
this.finish(void 0);
|
|
2770
|
+
return new Promise((resolve, reject) => {
|
|
2771
|
+
const run = {
|
|
2772
|
+
options,
|
|
2773
|
+
resolve,
|
|
2774
|
+
reject
|
|
2775
|
+
};
|
|
2776
|
+
this.active = run;
|
|
2777
|
+
const driver = { requestRender: () => this.invalidate() };
|
|
2778
|
+
const done = (value) => this.finishFor(run, value);
|
|
2779
|
+
Promise.resolve().then(() => factory(driver, theme, keybindings, done)).then((component) => {
|
|
2780
|
+
if (this.active !== run) {
|
|
2781
|
+
component.dispose?.();
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
if (typeof component?.render !== "function") {
|
|
2785
|
+
this.failFor(run, /* @__PURE__ */ new TypeError("Pi custom component must implement render(width)"));
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
run.component = component;
|
|
2789
|
+
this.invalidate();
|
|
2790
|
+
if (!this.scenes.open(this.sceneId)) this.finishFor(run, void 0);
|
|
2791
|
+
}, (error) => this.failFor(run, error));
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
render(width) {
|
|
2795
|
+
const active = this.active;
|
|
2796
|
+
if (active?.component === void 0) return [];
|
|
2797
|
+
const available = Math.max(12, Math.floor(width) - 2);
|
|
2798
|
+
const requested = preferredWidth(active.options);
|
|
2799
|
+
return active.component.render(requested === void 0 ? available : Math.min(available, requested));
|
|
2800
|
+
}
|
|
2801
|
+
handleInput(data) {
|
|
2802
|
+
const component = this.active?.component;
|
|
2803
|
+
if (component === void 0) return;
|
|
2804
|
+
component.handleInput?.(data);
|
|
2805
|
+
component.invalidate?.();
|
|
2806
|
+
this.invalidate();
|
|
2807
|
+
}
|
|
2808
|
+
dispose() {
|
|
2809
|
+
if (this.disposed) return;
|
|
2810
|
+
this.disposed = true;
|
|
2811
|
+
this.finish(void 0);
|
|
2812
|
+
for (const dispose of this.statusDisposers.values()) dispose();
|
|
2813
|
+
this.statusDisposers.clear();
|
|
2814
|
+
this.unregisterScene();
|
|
2815
|
+
this.listeners.clear();
|
|
2816
|
+
}
|
|
2817
|
+
invalidate() {
|
|
2818
|
+
this.revision += 1;
|
|
2819
|
+
for (const listener of this.listeners) listener();
|
|
2820
|
+
}
|
|
2821
|
+
finish(value) {
|
|
2822
|
+
const active = this.active;
|
|
2823
|
+
if (active !== void 0) this.finishFor(active, value);
|
|
2824
|
+
}
|
|
2825
|
+
finishFor(run, value, closeScene = true) {
|
|
2826
|
+
if (this.active !== run) return;
|
|
2827
|
+
this.active = void 0;
|
|
2828
|
+
run.component?.dispose?.();
|
|
2829
|
+
if (closeScene) this.scenes.close();
|
|
2830
|
+
this.invalidate();
|
|
2831
|
+
run.resolve(value);
|
|
2832
|
+
}
|
|
2833
|
+
failFor(run, error) {
|
|
2834
|
+
if (this.active !== run) return;
|
|
2835
|
+
this.active = void 0;
|
|
2836
|
+
run.component?.dispose?.();
|
|
2837
|
+
this.scenes.close();
|
|
2838
|
+
this.invalidate();
|
|
2839
|
+
run.reject(error);
|
|
2840
|
+
}
|
|
2841
|
+
scene(props) {
|
|
2842
|
+
const { React, ui } = props;
|
|
2843
|
+
React.useSyncExternalStore((listener) => this.subscribe(listener), () => this.snapshot(), () => this.snapshot());
|
|
2844
|
+
React.useEffect(() => () => {
|
|
2845
|
+
const active = this.active;
|
|
2846
|
+
if (active !== void 0) this.finishFor(active, void 0, false);
|
|
2847
|
+
}, []);
|
|
2848
|
+
ui.useInput((input, key, event) => this.handleInput(rawTerminalInput(input, key, event)));
|
|
2849
|
+
const { columns } = ui.useTerminalSize();
|
|
2850
|
+
const lines = this.render(columns);
|
|
2851
|
+
const Line = ui.Ansi ?? ui.Text;
|
|
2852
|
+
return React.createElement(ui.Box, {
|
|
2853
|
+
flexDirection: "column",
|
|
2854
|
+
width: "100%",
|
|
2855
|
+
paddingX: 1
|
|
2856
|
+
}, ...lines.map((line, index) => React.createElement(Line, { key: index }, line)));
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
/**
|
|
2860
|
+
* Attach lazily to dsh-TUI. Cordis reloads this child fiber whenever the
|
|
2861
|
+
* service implementation changes, so a TUI update cannot leave a stale scene
|
|
2862
|
+
* proxy in the bridge.
|
|
2863
|
+
*/
|
|
2864
|
+
function mountTuiSurfaceAdapter(ctx, packageName, publish, instanceKey) {
|
|
2865
|
+
const attach = (surfaceCtx) => {
|
|
2866
|
+
const adapter = new TuiSurfaceAdapter(surfaceCtx, packageName, instanceKey);
|
|
2867
|
+
publish(adapter);
|
|
2868
|
+
surfaceCtx.effect?.(() => () => {
|
|
2869
|
+
adapter.dispose();
|
|
2870
|
+
publish(void 0);
|
|
2871
|
+
});
|
|
2872
|
+
};
|
|
2873
|
+
if (service(ctx, "tuiScenes") !== void 0) {
|
|
2874
|
+
attach(ctx);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
if (typeof ctx.inject === "function") {
|
|
2878
|
+
ctx.inject(["tuiScenes"], attach);
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* dsh-TUI handles `/mcp` locally before it asks DSH's command registry. Until
|
|
2884
|
+
* it exposes a reserved-command seam, keep its native status command and make
|
|
2885
|
+
* an installed Pi MCP manager reachable as `/pi-mcp`.
|
|
2886
|
+
*/
|
|
2887
|
+
function commandNameForDshTui(name, tuiAvailable) {
|
|
2888
|
+
return tuiAvailable && name === "mcp" ? "pi-mcp" : name;
|
|
2889
|
+
}
|
|
2890
|
+
//#endregion
|
|
2657
2891
|
//#region src/runtime.ts
|
|
2658
2892
|
/** Fallback thread ids when a child session reports none. */
|
|
2659
2893
|
let sidePanelSerial = 0;
|
|
@@ -3042,9 +3276,8 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
|
|
|
3042
3276
|
...prefill === void 0 ? {} : { detail: `Current text:\n${String(prefill)}` }
|
|
3043
3277
|
}),
|
|
3044
3278
|
setStatus: (key, text) => {
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
surfaces.setStatus(sessionIdOf(state, agent), state.packageName, String(key), text);
|
|
3279
|
+
state.shared.browserSurfaces?.setStatus(sessionIdOf(state, agent), state.packageName, String(key), text);
|
|
3280
|
+
state.tuiSurfaces?.setStatus(String(key), text);
|
|
3048
3281
|
},
|
|
3049
3282
|
setWidget: (key, content) => {
|
|
3050
3283
|
const surfaces = state.shared.browserSurfaces;
|
|
@@ -3063,7 +3296,11 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
|
|
|
3063
3296
|
setFooter: (factory) => putSurface(ctx, state, agent, "footer", factory),
|
|
3064
3297
|
setHeader: (factory) => putSurface(ctx, state, agent, "header", factory),
|
|
3065
3298
|
setTitle: (title) => putSurface(ctx, state, agent, "title", title),
|
|
3066
|
-
custom: async () =>
|
|
3299
|
+
custom: async (factory, options) => {
|
|
3300
|
+
const surfaces = state.tuiSurfaces;
|
|
3301
|
+
if (surfaces === void 0 || typeof factory !== "function") return void 0;
|
|
3302
|
+
return surfaces.custom(factory, state.theme, getKeybindings(), options);
|
|
3303
|
+
},
|
|
3067
3304
|
pasteToEditor(text) {
|
|
3068
3305
|
const surfaces = state.shared.browserSurfaces;
|
|
3069
3306
|
const sessionId = sessionIdOf(state, agent);
|
|
@@ -3258,9 +3495,11 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
|
|
|
3258
3495
|
}
|
|
3259
3496
|
},
|
|
3260
3497
|
ui,
|
|
3261
|
-
mode
|
|
3498
|
+
get mode() {
|
|
3499
|
+
return state.tuiSurfaces?.available === true ? "tui" : "rpc";
|
|
3500
|
+
},
|
|
3262
3501
|
get hasUI() {
|
|
3263
|
-
return humanAnswererAvailable(userQuestions, agent);
|
|
3502
|
+
return state.tuiSurfaces?.available === true || humanAnswererAvailable(userQuestions, agent);
|
|
3264
3503
|
},
|
|
3265
3504
|
cwd: contextCwd,
|
|
3266
3505
|
sessionManager: session === void 0 ? state.bridge.readonlySessionManager({
|
|
@@ -3461,8 +3700,8 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
|
|
|
3461
3700
|
return { cancelled: false };
|
|
3462
3701
|
},
|
|
3463
3702
|
reload: async () => {
|
|
3464
|
-
const remounts = state.shared
|
|
3465
|
-
if (remounts
|
|
3703
|
+
const remounts = packageRemountsOf(ctx, state.shared);
|
|
3704
|
+
if (remounts.size === 0) return;
|
|
3466
3705
|
for (const [name, remount] of remounts) try {
|
|
3467
3706
|
await remount();
|
|
3468
3707
|
} catch (error) {
|
|
@@ -3481,10 +3720,19 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
|
|
|
3481
3720
|
async function dispatch(state, eventName, event, eventContext) {
|
|
3482
3721
|
const results = [];
|
|
3483
3722
|
const agent = eventContext.__agent;
|
|
3484
|
-
for (const handler of state.handlers.get(eventName) ?? []) results.push(await state
|
|
3723
|
+
for (const handler of state.handlers.get(eventName) ?? []) results.push(await runInPiRuntime(state, agent, () => handler(event, eventContext)));
|
|
3485
3724
|
return results;
|
|
3486
3725
|
}
|
|
3487
3726
|
/**
|
|
3727
|
+
* Bind every compatibility singleton to this exact Agent runtime for the
|
|
3728
|
+
* duration of extension-owned work. AsyncLocalStorage follows promises and
|
|
3729
|
+
* detached work spawned inside the callback, so two live Agents cannot
|
|
3730
|
+
* overwrite each other's model bridge or child-session factory.
|
|
3731
|
+
*/
|
|
3732
|
+
function runInPiRuntime(state, agent, callback) {
|
|
3733
|
+
return state.agentScope.run(agent, () => __runWithSubagentSessionFactory(state.subagentSessionFactory, () => __runWithPiAiRuntime(state.llmBridge, state.piAiRegistry, callback)));
|
|
3734
|
+
}
|
|
3735
|
+
/**
|
|
3488
3736
|
* Pi's request-body event is a waterfall over the body the provider transport
|
|
3489
3737
|
* will actually send. Only package-registered Pi transports expose that body
|
|
3490
3738
|
* through SimpleStreamOptions.onPayload; native DSH adapters build it behind
|
|
@@ -3499,7 +3747,7 @@ async function dispatchBeforeProviderRequest(ctx, state, payload, request) {
|
|
|
3499
3747
|
provider: request.provider
|
|
3500
3748
|
};
|
|
3501
3749
|
for (const handler of state.handlers.get("before_provider_request") ?? []) try {
|
|
3502
|
-
const result = await state
|
|
3750
|
+
const result = await runInPiRuntime(state, agent, () => handler({
|
|
3503
3751
|
type: "before_provider_request",
|
|
3504
3752
|
payload: current
|
|
3505
3753
|
}, eventContext));
|
|
@@ -3509,6 +3757,14 @@ async function dispatchBeforeProviderRequest(ctx, state, payload, request) {
|
|
|
3509
3757
|
}
|
|
3510
3758
|
return current;
|
|
3511
3759
|
}
|
|
3760
|
+
/** Run Pi's provider-body waterfall through every package runtime of one Agent. */
|
|
3761
|
+
async function dispatchHostProviderRequest(ctx, shared, fallback, payload, request) {
|
|
3762
|
+
const scoped = request.sessionId === void 0 ? void 0 : shared.runtimeStatesBySession.get(request.sessionId);
|
|
3763
|
+
const states = scoped === void 0 || scoped.size === 0 ? [fallback] : [...scoped];
|
|
3764
|
+
let current = payload;
|
|
3765
|
+
for (const state of states) current = await dispatchBeforeProviderRequest(ctx, state, current, request);
|
|
3766
|
+
return current;
|
|
3767
|
+
}
|
|
3512
3768
|
/**
|
|
3513
3769
|
* What triggered a compaction, in Pi's vocabulary.
|
|
3514
3770
|
*
|
|
@@ -3611,41 +3867,167 @@ async function messageFromSessionEvent(ctx, event) {
|
|
|
3611
3867
|
function sourceReason(value) {
|
|
3612
3868
|
return value === "resume" ? "resume" : value === "fork" ? "fork" : "startup";
|
|
3613
3869
|
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Claim Pi's once-per-session `session_start` event.
|
|
3872
|
+
*
|
|
3873
|
+
* dsh-TUI can execute a slash command before DSH starts the first model turn,
|
|
3874
|
+
* so `agent/session-start` has not necessarily been announced yet. Pi, by
|
|
3875
|
+
* contrast, announces `session_start` before the user can execute a command.
|
|
3876
|
+
* The command bridge uses this same claim before invoking a Pi command; when
|
|
3877
|
+
* DSH later announces the real agent lifecycle, the durable session id keeps
|
|
3878
|
+
* the second path from restarting the extension.
|
|
3879
|
+
*/
|
|
3880
|
+
function claimPiSessionStart(state, agent) {
|
|
3881
|
+
const id = agentSession(agent)?.id;
|
|
3882
|
+
if (typeof id === "string") {
|
|
3883
|
+
if (state.startedSessions.has(id)) return false;
|
|
3884
|
+
state.startedSessions.add(id);
|
|
3885
|
+
return true;
|
|
3886
|
+
}
|
|
3887
|
+
if (typeof agent === "object" && agent !== null) {
|
|
3888
|
+
if (state.startedSessionlessAgents.has(agent)) return false;
|
|
3889
|
+
state.startedSessionlessAgents.add(agent);
|
|
3890
|
+
return true;
|
|
3891
|
+
}
|
|
3892
|
+
return false;
|
|
3893
|
+
}
|
|
3894
|
+
async function ensurePiSessionStarted(ctx, state, agent, signal) {
|
|
3895
|
+
await startPiSession(ctx, state, agent, "startup", signal);
|
|
3896
|
+
}
|
|
3897
|
+
function inFlightPiSessionStart(state, agent) {
|
|
3898
|
+
const id = agentSession(agent)?.id;
|
|
3899
|
+
if (typeof id === "string") return state.sessionStartTasks.get(id);
|
|
3900
|
+
if (typeof agent === "object" && agent !== null) return state.sessionlessStartTasks.get(agent);
|
|
3901
|
+
}
|
|
3902
|
+
function rememberPiSessionStart(state, agent, task) {
|
|
3903
|
+
const id = agentSession(agent)?.id;
|
|
3904
|
+
if (typeof id === "string") state.sessionStartTasks.set(id, task);
|
|
3905
|
+
else if (typeof agent === "object" && agent !== null) state.sessionlessStartTasks.set(agent, task);
|
|
3906
|
+
}
|
|
3907
|
+
function forgetPiSessionStart(state, agent, task) {
|
|
3908
|
+
const id = agentSession(agent)?.id;
|
|
3909
|
+
if (typeof id === "string") {
|
|
3910
|
+
if (state.sessionStartTasks.get(id) === task) state.sessionStartTasks.delete(id);
|
|
3911
|
+
} else if (typeof agent === "object" && agent !== null && state.sessionlessStartTasks.get(agent) === task) state.sessionlessStartTasks.delete(agent);
|
|
3912
|
+
}
|
|
3913
|
+
function releasePiSessionClaim(state, agent) {
|
|
3914
|
+
const id = agentSession(agent)?.id;
|
|
3915
|
+
if (typeof id === "string") {
|
|
3916
|
+
state.startedSessions.delete(id);
|
|
3917
|
+
state.sessionStartTasks.delete(id);
|
|
3918
|
+
} else if (typeof agent === "object" && agent !== null) {
|
|
3919
|
+
state.startedSessionlessAgents.delete(agent);
|
|
3920
|
+
state.sessionlessStartTasks.delete(agent);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
function replacementReason(reason) {
|
|
3924
|
+
if (reason === "resume") return "resume";
|
|
3925
|
+
if (reason === "fork") return "fork";
|
|
3926
|
+
return "new";
|
|
3927
|
+
}
|
|
3928
|
+
function trackRuntimeSession(state, agent) {
|
|
3929
|
+
const id = agentSession(agent)?.id;
|
|
3930
|
+
if (typeof id !== "string") return;
|
|
3931
|
+
let states = state.shared.runtimeStatesBySession.get(id);
|
|
3932
|
+
if (states === void 0) {
|
|
3933
|
+
states = /* @__PURE__ */ new Set();
|
|
3934
|
+
state.shared.runtimeStatesBySession.set(id, states);
|
|
3935
|
+
}
|
|
3936
|
+
states.add(state);
|
|
3937
|
+
}
|
|
3938
|
+
function untrackRuntimeSession(state, agent) {
|
|
3939
|
+
const id = agentSession(agent)?.id;
|
|
3940
|
+
if (typeof id !== "string") return;
|
|
3941
|
+
const states = state.shared.runtimeStatesBySession.get(id);
|
|
3942
|
+
states?.delete(state);
|
|
3943
|
+
if (states?.size === 0) state.shared.runtimeStatesBySession.delete(id);
|
|
3944
|
+
}
|
|
3945
|
+
/** Dispatch or join the one in-flight Pi session_start for this session. */
|
|
3946
|
+
async function startPiSession(ctx, state, agent, reason, signal) {
|
|
3947
|
+
if (!acceptsAgent(state, agent)) return;
|
|
3948
|
+
const existing = inFlightPiSessionStart(state, agent);
|
|
3949
|
+
if (existing !== void 0) {
|
|
3950
|
+
await existing;
|
|
3951
|
+
return;
|
|
3952
|
+
}
|
|
3953
|
+
const task = runInPiRuntime(state, agent, async () => {
|
|
3954
|
+
const previous = state.hostAgent;
|
|
3955
|
+
const replacing = typeof previous === "object" && previous !== null && previous !== agent;
|
|
3956
|
+
const transitionReason = replacing ? replacementReason(reason) : reason;
|
|
3957
|
+
if (replacing && !state.piShutdownAgents.has(previous)) {
|
|
3958
|
+
state.piShutdownAgents.add(previous);
|
|
3959
|
+
releasePiSessionClaim(state, previous);
|
|
3960
|
+
await runInPiRuntime(state, previous, () => dispatch(state, "session_shutdown", {
|
|
3961
|
+
type: "session_shutdown",
|
|
3962
|
+
reason: replacementReason(reason)
|
|
3963
|
+
}, contextFor(ctx, state, previous, signal)));
|
|
3964
|
+
}
|
|
3965
|
+
if (typeof agent === "object" && agent !== null) {
|
|
3966
|
+
state.hostAgent = agent;
|
|
3967
|
+
state.piShutdownAgents.delete(agent);
|
|
3968
|
+
}
|
|
3969
|
+
if (!claimPiSessionStart(state, agent)) return;
|
|
3970
|
+
try {
|
|
3971
|
+
await dispatch(state, "session_start", {
|
|
3972
|
+
type: "session_start",
|
|
3973
|
+
reason: transitionReason
|
|
3974
|
+
}, contextFor(ctx, state, agent, signal));
|
|
3975
|
+
} catch (error) {
|
|
3976
|
+
releasePiSessionClaim(state, agent);
|
|
3977
|
+
throw error;
|
|
3978
|
+
}
|
|
3979
|
+
});
|
|
3980
|
+
rememberPiSessionStart(state, agent, task);
|
|
3981
|
+
try {
|
|
3982
|
+
await task;
|
|
3983
|
+
} finally {
|
|
3984
|
+
forgetPiSessionStart(state, agent, task);
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3614
3987
|
function subscribeLifecycle(ctx, state) {
|
|
3615
3988
|
const cordis = ctx;
|
|
3616
3989
|
const warn = (event, error) => logger(ctx).warn(`[pi2dsh] ${event} handler failed: ${String(error)}`);
|
|
3617
3990
|
cordis.on("agent/session-start", (payload) => {
|
|
3618
3991
|
const agent = payload.agent;
|
|
3619
|
-
if (
|
|
3620
|
-
state.activeAgents.add(agent);
|
|
3621
|
-
return;
|
|
3622
|
-
}
|
|
3992
|
+
if (!acceptsAgent(state, agent)) return;
|
|
3623
3993
|
state.activeAgents.add(agent);
|
|
3994
|
+
trackRuntimeSession(state, agent);
|
|
3624
3995
|
const session = agentSession(agent);
|
|
3625
3996
|
if (session !== void 0) state.bridge.load(session.id);
|
|
3626
|
-
if (state.pendingActiveTools !== void 0) state
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
}
|
|
3997
|
+
if (state.pendingActiveTools !== void 0) runInPiRuntime(state, agent, () => setActiveTools(ctx, state, state.pendingActiveTools));
|
|
3998
|
+
if (!state.extensionsReady) {
|
|
3999
|
+
state.pendingSessionStarts.set(agent, sourceReason(payload.source));
|
|
4000
|
+
return;
|
|
4001
|
+
}
|
|
4002
|
+
startPiSession(ctx, state, agent, sourceReason(payload.source), void 0).catch((error) => warn("session_start", error));
|
|
3631
4003
|
});
|
|
3632
4004
|
cordis.on("agent/disposed", (payload) => {
|
|
3633
4005
|
const agent = payload.agent;
|
|
4006
|
+
if (!acceptsAgent(state, agent)) return;
|
|
4007
|
+
const ownsHostSession = state.hostAgent === agent;
|
|
3634
4008
|
state.activeAgents.delete(agent);
|
|
4009
|
+
untrackRuntimeSession(state, agent);
|
|
4010
|
+
state.pendingSessionStarts.delete(agent);
|
|
3635
4011
|
if (typeof agent === "object" && agent !== null) {
|
|
3636
4012
|
state.toolRestrictions.get(agent)?.();
|
|
3637
4013
|
state.toolRestrictions.delete(agent);
|
|
3638
4014
|
}
|
|
3639
4015
|
if (typeof agent === "object" && agent !== null && !state.disposedAgents.has(agent)) {
|
|
3640
4016
|
state.disposedAgents.add(agent);
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
4017
|
+
releasePiSessionClaim(state, agent);
|
|
4018
|
+
if (ownsHostSession && !state.piShutdownAgents.has(agent)) {
|
|
4019
|
+
state.piShutdownAgents.add(agent);
|
|
4020
|
+
state.hostAgent = void 0;
|
|
4021
|
+
dispatch(state, "session_shutdown", {
|
|
4022
|
+
type: "session_shutdown",
|
|
4023
|
+
reason: "quit"
|
|
4024
|
+
}, contextFor(ctx, state, agent, void 0)).catch((error) => warn("session_shutdown", error));
|
|
4025
|
+
}
|
|
3645
4026
|
}
|
|
3646
4027
|
});
|
|
3647
4028
|
cordis.on("session/event", (session, event) => {
|
|
3648
|
-
|
|
4029
|
+
const ownedSession = agentSession(state.ownerAgent);
|
|
4030
|
+
if (ownedSession !== void 0 ? session !== ownedSession : isSubagentOrigin(session)) return;
|
|
3649
4031
|
const agent = [...state.activeAgents].find((candidate) => candidate.session === session);
|
|
3650
4032
|
const eventContext = contextFor(ctx, state, agent, void 0);
|
|
3651
4033
|
const type = event.type;
|
|
@@ -3796,7 +4178,7 @@ function subscribeLifecycle(ctx, state) {
|
|
|
3796
4178
|
cordis.on("agent/request", async (payload, next) => {
|
|
3797
4179
|
const config = await next();
|
|
3798
4180
|
const agent = payload.agent;
|
|
3799
|
-
if (agent === void 0) return config;
|
|
4181
|
+
if (agent === void 0 || !acceptsAgent(state, agent)) return config;
|
|
3800
4182
|
const override = state.modelOverrides.get(agent);
|
|
3801
4183
|
const thinking = state.thinkingLevels.get(agent);
|
|
3802
4184
|
if (override === void 0 && thinking === void 0) return config;
|
|
@@ -3808,23 +4190,81 @@ function subscribeLifecycle(ctx, state) {
|
|
|
3808
4190
|
};
|
|
3809
4191
|
});
|
|
3810
4192
|
cordis.effect(() => async () => {
|
|
3811
|
-
|
|
4193
|
+
const agent = state.hostAgent;
|
|
4194
|
+
if (typeof agent === "object" && agent !== null && !state.piShutdownAgents.has(agent)) {
|
|
4195
|
+
state.piShutdownAgents.add(agent);
|
|
3812
4196
|
state.disposedAgents.add(agent);
|
|
3813
4197
|
await dispatch(state, "session_shutdown", {
|
|
3814
4198
|
type: "session_shutdown",
|
|
3815
4199
|
reason: "quit"
|
|
3816
4200
|
}, contextFor(ctx, state, agent, void 0));
|
|
3817
4201
|
}
|
|
4202
|
+
state.hostAgent = void 0;
|
|
4203
|
+
for (const active of state.activeAgents) untrackRuntimeSession(state, active);
|
|
3818
4204
|
state.activeAgents.clear();
|
|
4205
|
+
state.startedSessions.clear();
|
|
4206
|
+
state.sessionStartTasks.clear();
|
|
4207
|
+
state.pendingSessionStarts.clear();
|
|
3819
4208
|
for (const dispose of state.toolDisposers.values()) dispose();
|
|
3820
4209
|
state.toolDisposers.clear();
|
|
4210
|
+
for (const provider of [...state.ownedProviderRoutes]) releaseSharedProviderRoute(state, provider);
|
|
3821
4211
|
state.eventBus.removeAllListeners();
|
|
3822
4212
|
}, "pi2dsh session shutdown");
|
|
3823
4213
|
}
|
|
4214
|
+
async function flushPendingSessionStarts(ctx, state) {
|
|
4215
|
+
state.extensionsReady = true;
|
|
4216
|
+
if (state.ownerAgent !== void 0 && !state.pendingSessionStarts.has(state.ownerAgent)) {
|
|
4217
|
+
const owned = state.ownerAgent;
|
|
4218
|
+
if (!state.activeAgents.has(owned)) {
|
|
4219
|
+
state.activeAgents.add(owned);
|
|
4220
|
+
trackRuntimeSession(state, owned);
|
|
4221
|
+
const session = agentSession(owned);
|
|
4222
|
+
if (session !== void 0) state.bridge.load(session.id);
|
|
4223
|
+
}
|
|
4224
|
+
state.pendingSessionStarts.set(owned, "startup");
|
|
4225
|
+
}
|
|
4226
|
+
const pending = [...state.pendingSessionStarts];
|
|
4227
|
+
state.pendingSessionStarts.clear();
|
|
4228
|
+
for (const [agent, reason] of pending) await startPiSession(ctx, state, agent, reason, void 0);
|
|
4229
|
+
}
|
|
4230
|
+
/**
|
|
4231
|
+
* Start freshly reloaded extension handlers in every live Pi host session.
|
|
4232
|
+
*
|
|
4233
|
+
* `ctx.reload()` creates a new extension instance. The durable session has
|
|
4234
|
+
* already been claimed by the previous instance, so the ordinary once-per-
|
|
4235
|
+
* session guard must deliberately NOT suppress this replay: from the new
|
|
4236
|
+
* instance's point of view this is its first `session_start`. The command's
|
|
4237
|
+
* AsyncLocalStorage agent is included because a lean composition can execute
|
|
4238
|
+
* a Pi command without publishing DSH's optional agent/session-start event.
|
|
4239
|
+
*/
|
|
4240
|
+
async function restartReloadedPiSessions(ctx, state) {
|
|
4241
|
+
state.extensionsReady = true;
|
|
4242
|
+
const pending = new Map(state.pendingSessionStarts);
|
|
4243
|
+
state.pendingSessionStarts.clear();
|
|
4244
|
+
const commandAgent = currentAgent(state);
|
|
4245
|
+
const candidates = [...state.activeAgents, ...commandAgent === void 0 ? [] : [commandAgent]];
|
|
4246
|
+
const seenSessions = /* @__PURE__ */ new Set();
|
|
4247
|
+
const seenSessionless = /* @__PURE__ */ new Set();
|
|
4248
|
+
for (const agent of candidates) {
|
|
4249
|
+
if (!acceptsAgent(state, agent)) continue;
|
|
4250
|
+
const sessionId = agentSession(agent)?.id;
|
|
4251
|
+
if (typeof sessionId === "string") {
|
|
4252
|
+
if (seenSessions.has(sessionId)) continue;
|
|
4253
|
+
seenSessions.add(sessionId);
|
|
4254
|
+
} else {
|
|
4255
|
+
if (seenSessionless.has(agent)) continue;
|
|
4256
|
+
seenSessionless.add(agent);
|
|
4257
|
+
}
|
|
4258
|
+
await runInPiRuntime(state, agent, () => dispatch(state, "session_start", {
|
|
4259
|
+
type: "session_start",
|
|
4260
|
+
reason: pending.get(agent) ?? "resume"
|
|
4261
|
+
}, contextFor(ctx, state, agent, void 0)));
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
3824
4264
|
function subscribeInterceptors(ctx, state) {
|
|
3825
4265
|
const cordis = ctx;
|
|
3826
4266
|
cordis.on("tools/pre-execute", async (exec, next) => {
|
|
3827
|
-
if (
|
|
4267
|
+
if (!acceptsAgent(state, exec.agent)) return next();
|
|
3828
4268
|
const input = cloneJson(exec.arguments);
|
|
3829
4269
|
const event = {
|
|
3830
4270
|
type: "tool_call",
|
|
@@ -3862,7 +4302,7 @@ function subscribeInterceptors(ctx, state) {
|
|
|
3862
4302
|
return next();
|
|
3863
4303
|
});
|
|
3864
4304
|
cordis.on("tools/post-execute", async (exec, result, next) => {
|
|
3865
|
-
if (
|
|
4305
|
+
if (!acceptsAgent(state, exec.agent)) return next();
|
|
3866
4306
|
const downstream = await next();
|
|
3867
4307
|
await dispatch(state, "tool_execution_end", {
|
|
3868
4308
|
type: "tool_execution_end",
|
|
@@ -3904,7 +4344,7 @@ function subscribeInterceptors(ctx, state) {
|
|
|
3904
4344
|
});
|
|
3905
4345
|
cordis.on("agent/inbox/claimed", (payload) => {
|
|
3906
4346
|
const agent = payload.agent;
|
|
3907
|
-
if (agent === void 0 ||
|
|
4347
|
+
if (agent === void 0 || !acceptsAgent(state, agent)) return;
|
|
3908
4348
|
const claimed = state.claimedForStep.get(agent) ?? [];
|
|
3909
4349
|
claimed.push({
|
|
3910
4350
|
message: payload.message,
|
|
@@ -3914,7 +4354,7 @@ function subscribeInterceptors(ctx, state) {
|
|
|
3914
4354
|
});
|
|
3915
4355
|
cordis.on("agent/pre-step", async (payload, next) => {
|
|
3916
4356
|
const agent = payload.agent;
|
|
3917
|
-
if (
|
|
4357
|
+
if (!acceptsAgent(state, agent)) return next();
|
|
3918
4358
|
const tally = state.terminateBatch.get(agent);
|
|
3919
4359
|
state.terminateBatch.delete(agent);
|
|
3920
4360
|
if (tally !== void 0 && tally.calls > 0 && tally.calls === tally.terminating) {
|
|
@@ -3936,7 +4376,7 @@ function subscribeInterceptors(ctx, state) {
|
|
|
3936
4376
|
};
|
|
3937
4377
|
});
|
|
3938
4378
|
const systemPrompt = optionalService(ctx, "systemPrompt");
|
|
3939
|
-
if (systemPrompt !== void 0) ctx.effect(() => systemPrompt.section({
|
|
4379
|
+
if (systemPrompt !== void 0 && !state.hostAnchor) ctx.effect(() => systemPrompt.section({
|
|
3940
4380
|
name: `pi2dsh:tool-guidance:${state.packageName ?? "pi"}`,
|
|
3941
4381
|
order: 150,
|
|
3942
4382
|
text: () => piToolPromptContribution(ctx, state)
|
|
@@ -3946,7 +4386,8 @@ function subscribeInterceptors(ctx, state) {
|
|
|
3946
4386
|
const original = renderPrompt(downstream);
|
|
3947
4387
|
state.currentSystemPrompt = original;
|
|
3948
4388
|
const agent = assembleContext.agent ?? assembleContext.scope;
|
|
3949
|
-
if (agent !== void 0 && !
|
|
4389
|
+
if (agent !== void 0 && !acceptsAgent(state, agent)) return downstream;
|
|
4390
|
+
if (agent !== void 0 && acceptsAgent(state, agent)) {
|
|
3950
4391
|
await runBeforeAgentStart(ctx, state, agent, assembleContext.signal, original);
|
|
3951
4392
|
const override = state.turnSystemPromptOverrides.get(agent);
|
|
3952
4393
|
if (override === void 0) return downstream;
|
|
@@ -4147,6 +4588,16 @@ function oauthStoreOf(state) {
|
|
|
4147
4588
|
}
|
|
4148
4589
|
const COMPANION_ROUTE_PREFIX = "pi2dsh-companion/";
|
|
4149
4590
|
const SHARED_HOST_STATE = /* @__PURE__ */ new WeakMap();
|
|
4591
|
+
/**
|
|
4592
|
+
* The engine's own built-in OAuth directory entries, by object identity.
|
|
4593
|
+
* They are login placeholders (no models, no transport), preloaded so
|
|
4594
|
+
* `/login <provider>` works before any package is installed — and a package
|
|
4595
|
+
* registering the SAME provider id owns the richer definition (transport,
|
|
4596
|
+
* catalog, its own flow) and must supersede the placeholder in the shared
|
|
4597
|
+
* ledger. Two PACKAGES colliding on one id still keep first-wins, matching
|
|
4598
|
+
* the route-ownership rule.
|
|
4599
|
+
*/
|
|
4600
|
+
const BUILTIN_PROVIDER_SEEDS = /* @__PURE__ */ new WeakSet();
|
|
4150
4601
|
function sharedHostStateOf(ctx) {
|
|
4151
4602
|
const key = ctx.root ?? ctx;
|
|
4152
4603
|
let shared = SHARED_HOST_STATE.get(key);
|
|
@@ -4154,6 +4605,9 @@ function sharedHostStateOf(ctx) {
|
|
|
4154
4605
|
shared = {
|
|
4155
4606
|
companionRoutes: /* @__PURE__ */ new Map(),
|
|
4156
4607
|
providerRouteDisposers: /* @__PURE__ */ new Map(),
|
|
4608
|
+
providerModelDiscoveries: /* @__PURE__ */ new Map(),
|
|
4609
|
+
providerRouteOwners: /* @__PURE__ */ new Map(),
|
|
4610
|
+
runtimeStatesBySession: /* @__PURE__ */ new Map(),
|
|
4157
4611
|
publishedOAuthKeys: /* @__PURE__ */ new Map(),
|
|
4158
4612
|
providers: /* @__PURE__ */ new Map()
|
|
4159
4613
|
};
|
|
@@ -4161,6 +4615,17 @@ function sharedHostStateOf(ctx) {
|
|
|
4161
4615
|
}
|
|
4162
4616
|
return shared;
|
|
4163
4617
|
}
|
|
4618
|
+
function packageRemountsOf(ctx, shared) {
|
|
4619
|
+
const scoped = scopeOf(ctx);
|
|
4620
|
+
const owner = typeof scoped === "object" && scoped !== null ? scoped : ctx;
|
|
4621
|
+
const byOwner = shared.packageRemounts ??= /* @__PURE__ */ new WeakMap();
|
|
4622
|
+
let remounts = byOwner.get(owner);
|
|
4623
|
+
if (remounts === void 0) {
|
|
4624
|
+
remounts = /* @__PURE__ */ new Map();
|
|
4625
|
+
byOwner.set(owner, remounts);
|
|
4626
|
+
}
|
|
4627
|
+
return remounts;
|
|
4628
|
+
}
|
|
4164
4629
|
/**
|
|
4165
4630
|
* Image-admission companion routes: for every text-only route in the DSH
|
|
4166
4631
|
* llm directory, a `<route>-vision` route that admits images at the host's
|
|
@@ -4285,11 +4750,15 @@ async function materializeAttachmentImage(ctx, attachment) {
|
|
|
4285
4750
|
}
|
|
4286
4751
|
}
|
|
4287
4752
|
function ensureLoginCommand(ctx, state) {
|
|
4288
|
-
|
|
4289
|
-
|
|
4753
|
+
const scoped = scopeOf(ctx);
|
|
4754
|
+
const owner = typeof scoped === "object" && scoped !== null ? scoped : ctx;
|
|
4755
|
+
const registered = state.shared.loginRegistered ??= /* @__PURE__ */ new WeakSet();
|
|
4756
|
+
if (registered.has(owner)) return;
|
|
4757
|
+
registered.add(owner);
|
|
4290
4758
|
try {
|
|
4291
4759
|
registerLoginCommand(ctx, state);
|
|
4292
4760
|
} catch (error) {
|
|
4761
|
+
registered.delete(owner);
|
|
4293
4762
|
logger(ctx).warn(`[pi2dsh] /login is already registered by an earlier package in this host; this package's providers use that command (${error instanceof Error ? error.message : String(error)})`);
|
|
4294
4763
|
}
|
|
4295
4764
|
}
|
|
@@ -4432,7 +4901,7 @@ function keepOAuthCredentialFresh(ctx, state) {
|
|
|
4432
4901
|
state.shared.oauthRefreshHooked = true;
|
|
4433
4902
|
ctx.on("llm/stream", ((options, next) => {
|
|
4434
4903
|
const provider = typeof options.provider === "string" ? options.provider : void 0;
|
|
4435
|
-
const config = provider === void 0 ? void 0 : state.providers.get(provider);
|
|
4904
|
+
const config = provider === void 0 ? void 0 : state.shared.providers.get(provider);
|
|
4436
4905
|
if (provider === void 0 || config === void 0 || !providerSupportsOAuth(config)) return next();
|
|
4437
4906
|
const publish = () => publishOAuthCredential(ctx, state, provider, config).catch((error) => logger(ctx).warn(`[pi2dsh] could not refresh the stored credential for ${JSON.stringify(provider)}: ${error instanceof Error ? error.message : String(error)}`));
|
|
4438
4907
|
return (async function* () {
|
|
@@ -4461,37 +4930,47 @@ function keepOAuthCredentialFresh(ctx, state) {
|
|
|
4461
4930
|
async function discoverProviderModels(ctx, state, name, value) {
|
|
4462
4931
|
const refreshModels = value.refreshModels;
|
|
4463
4932
|
if (typeof refreshModels !== "function") return void 0;
|
|
4933
|
+
const existing = state.shared.providerModelDiscoveries.get(name);
|
|
4934
|
+
if (existing !== void 0) return await existing;
|
|
4935
|
+
const task = (async () => {
|
|
4936
|
+
try {
|
|
4937
|
+
const apiKey = ((await resolvePiProviderAuth({
|
|
4938
|
+
providerId: name,
|
|
4939
|
+
providerConfig: value,
|
|
4940
|
+
store: oauthStoreOf(state)
|
|
4941
|
+
}).catch(() => void 0))?.auth)?.apiKey;
|
|
4942
|
+
await Promise.resolve(refreshModels.call(value, {
|
|
4943
|
+
stored: void 0,
|
|
4944
|
+
...apiKey === void 0 ? {} : { credential: {
|
|
4945
|
+
type: "api_key",
|
|
4946
|
+
key: apiKey
|
|
4947
|
+
} },
|
|
4948
|
+
store: {
|
|
4949
|
+
read: async () => void 0,
|
|
4950
|
+
write: async () => {},
|
|
4951
|
+
delete: async () => {}
|
|
4952
|
+
},
|
|
4953
|
+
allowNetwork: true,
|
|
4954
|
+
signal: new AbortController().signal,
|
|
4955
|
+
publish: async (publication) => {
|
|
4956
|
+
publication.update?.();
|
|
4957
|
+
return true;
|
|
4958
|
+
}
|
|
4959
|
+
}));
|
|
4960
|
+
} catch (error) {
|
|
4961
|
+
logger(ctx).warn(`[pi2dsh] model catalog refresh for Pi provider ${JSON.stringify(name)} failed (its registry entries stay static): ${error instanceof Error ? error.message : String(error)}`);
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
state.modelCatalog?.refresh();
|
|
4965
|
+
state.providerRouteDisposers.get(name)?.reannounce?.();
|
|
4966
|
+
return await llmOf(ctx)?.listModels(name).then((models) => models.length).catch(() => void 0);
|
|
4967
|
+
})();
|
|
4968
|
+
state.shared.providerModelDiscoveries.set(name, task);
|
|
4464
4969
|
try {
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
store: oauthStoreOf(state)
|
|
4469
|
-
}).catch(() => void 0))?.auth)?.apiKey;
|
|
4470
|
-
await Promise.resolve(refreshModels.call(value, {
|
|
4471
|
-
stored: void 0,
|
|
4472
|
-
...apiKey === void 0 ? {} : { credential: {
|
|
4473
|
-
type: "api_key",
|
|
4474
|
-
key: apiKey
|
|
4475
|
-
} },
|
|
4476
|
-
store: {
|
|
4477
|
-
read: async () => void 0,
|
|
4478
|
-
write: async () => {},
|
|
4479
|
-
delete: async () => {}
|
|
4480
|
-
},
|
|
4481
|
-
allowNetwork: true,
|
|
4482
|
-
signal: new AbortController().signal,
|
|
4483
|
-
publish: async (publication) => {
|
|
4484
|
-
publication.update?.();
|
|
4485
|
-
return true;
|
|
4486
|
-
}
|
|
4487
|
-
}));
|
|
4488
|
-
} catch (error) {
|
|
4489
|
-
logger(ctx).warn(`[pi2dsh] model catalog refresh for Pi provider ${JSON.stringify(name)} failed (its registry entries stay static): ${error instanceof Error ? error.message : String(error)}`);
|
|
4490
|
-
return;
|
|
4970
|
+
return await task;
|
|
4971
|
+
} finally {
|
|
4972
|
+
if (state.shared.providerModelDiscoveries.get(name) === task) state.shared.providerModelDiscoveries.delete(name);
|
|
4491
4973
|
}
|
|
4492
|
-
state.modelCatalog?.refresh();
|
|
4493
|
-
state.providerRouteDisposers.get(name)?.reannounce?.();
|
|
4494
|
-
return await llmOf(ctx)?.listModels(name).then((models) => models.length).catch(() => void 0);
|
|
4495
4974
|
}
|
|
4496
4975
|
function registerLoginCommand(ctx, state) {
|
|
4497
4976
|
registerCommand(ctx, state, {
|
|
@@ -4564,6 +5043,10 @@ function registerTool(ctx, state, tool) {
|
|
|
4564
5043
|
const normalized = normalizeToolSchema(tool.parameters);
|
|
4565
5044
|
for (const warning of normalized.warnings) logger(ctx).warn(`[pi2dsh] tool ${tool.name}: ${warning}`);
|
|
4566
5045
|
state.tools.set(tool.name, tool);
|
|
5046
|
+
if (state.hostAnchor) {
|
|
5047
|
+
state.toolDisposers.set(tool.name, () => {});
|
|
5048
|
+
return;
|
|
5049
|
+
}
|
|
4567
5050
|
const definition = {
|
|
4568
5051
|
name: tool.name,
|
|
4569
5052
|
description: tool.description,
|
|
@@ -4576,6 +5059,8 @@ function registerTool(ctx, state, tool) {
|
|
|
4576
5059
|
isConcurrencySafe: () => tool.executionMode === "parallel",
|
|
4577
5060
|
async execute(args, exec) {
|
|
4578
5061
|
const live = state.tools.get(tool.name) ?? tool;
|
|
5062
|
+
const agent = exec.agent;
|
|
5063
|
+
await ensurePiSessionStarted(ctx, state, agent, exec.signal);
|
|
4579
5064
|
const mutated = state.argMutations.get(exec);
|
|
4580
5065
|
if (mutated !== void 0) state.argMutations.delete(exec);
|
|
4581
5066
|
const effective = mutated ?? args;
|
|
@@ -4586,8 +5071,7 @@ function registerTool(ctx, state, tool) {
|
|
|
4586
5071
|
name: live.name,
|
|
4587
5072
|
arguments: live.prepareArguments?.(cloneJson(effective)) ?? effective
|
|
4588
5073
|
});
|
|
4589
|
-
const
|
|
4590
|
-
const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
|
|
5074
|
+
const result = await normalizeToolResultForDsh(ctx, await runInPiRuntime(state, agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
|
|
4591
5075
|
dispatch(state, "tool_execution_update", {
|
|
4592
5076
|
type: "tool_execution_update",
|
|
4593
5077
|
toolCallId: String(exec.callId),
|
|
@@ -4621,6 +5105,12 @@ function currentAgent(state) {
|
|
|
4621
5105
|
if (scoped !== void 0) return scoped;
|
|
4622
5106
|
if (state.activeAgents.size === 1) return state.activeAgents.values().next().value;
|
|
4623
5107
|
}
|
|
5108
|
+
/** Exact-Agent ownership for scoped mounts; legacy root mounts retain their old host-session rule. */
|
|
5109
|
+
function acceptsAgent(state, agent) {
|
|
5110
|
+
if (state.hostAnchor) return false;
|
|
5111
|
+
if (state.ownerAgent !== void 0) return agent === state.ownerAgent;
|
|
5112
|
+
return !isSubagentOrigin(agent);
|
|
5113
|
+
}
|
|
4624
5114
|
function toolRuntime(ctx, agent) {
|
|
4625
5115
|
return (agent?.ctx)?.tools ?? ctx.tools;
|
|
4626
5116
|
}
|
|
@@ -4845,10 +5335,16 @@ async function executePiCommand(service, cwd, command, args, options) {
|
|
|
4845
5335
|
operation.cleanup();
|
|
4846
5336
|
}
|
|
4847
5337
|
}
|
|
4848
|
-
function
|
|
5338
|
+
function normalizedPiCommandName(piName) {
|
|
4849
5339
|
const normalized = piName.toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^[^a-z]+/u, "");
|
|
4850
|
-
|
|
4851
|
-
|
|
5340
|
+
return normalized.length > 0 ? normalized : "pi-command";
|
|
5341
|
+
}
|
|
5342
|
+
function dshCommandName(ctx, state, piName) {
|
|
5343
|
+
const name = commandNameForDshTui(normalizedPiCommandName(piName), state.tuiSurfaces?.available === true || optionalService(ctx, "tuiScenes") !== void 0);
|
|
5344
|
+
if (name !== piName) {
|
|
5345
|
+
const reason = name === "pi-mcp" ? "because dsh-TUI reserves /mcp for its native MCP status view" : "to satisfy DSH command naming";
|
|
5346
|
+
logger(ctx).warn(`[pi2dsh] Pi command /${piName} registered as /${name} ${reason}`);
|
|
5347
|
+
}
|
|
4852
5348
|
return name;
|
|
4853
5349
|
}
|
|
4854
5350
|
function registerCommand(ctx, state, command) {
|
|
@@ -4857,12 +5353,19 @@ function registerCommand(ctx, state, command) {
|
|
|
4857
5353
|
return;
|
|
4858
5354
|
}
|
|
4859
5355
|
state.commands.set(command.name, command);
|
|
5356
|
+
if (state.hostAnchor) return;
|
|
4860
5357
|
const commands = ctx.get("commands");
|
|
4861
5358
|
if (commands === void 0) {
|
|
4862
5359
|
logger(ctx).warn(`[pi2dsh] command /${command.name} was not registered because this DSH composition has no ctx.commands`);
|
|
4863
5360
|
return;
|
|
4864
5361
|
}
|
|
4865
|
-
const baseName = dshCommandName(ctx, command.name);
|
|
5362
|
+
const baseName = dshCommandName(ctx, state, command.name);
|
|
5363
|
+
const existingOwner = state.dshCommandOwners.get(baseName);
|
|
5364
|
+
if (existingOwner !== void 0 && existingOwner !== command.name && normalizedPiCommandName(command.name) === baseName && normalizedPiCommandName(existingOwner) !== baseName) {
|
|
5365
|
+
state.commandDisposers.get(existingOwner)?.();
|
|
5366
|
+
state.commandDisposers.delete(existingOwner);
|
|
5367
|
+
state.dshCommandOwners.delete(baseName);
|
|
5368
|
+
}
|
|
4866
5369
|
const definitionFor = (dshName) => ({
|
|
4867
5370
|
name: dshName,
|
|
4868
5371
|
description: command.description || `Migrated Pi command /${command.name}`,
|
|
@@ -4871,7 +5374,10 @@ function registerCommand(ctx, state, command) {
|
|
|
4871
5374
|
const agent = invocation.agent;
|
|
4872
5375
|
const commandContext = contextFor(ctx, state, agent, invocation.signal, true);
|
|
4873
5376
|
const live = state.commands.get(command.name) ?? command;
|
|
4874
|
-
await state
|
|
5377
|
+
await runInPiRuntime(state, agent, async () => {
|
|
5378
|
+
await ensurePiSessionStarted(ctx, state, agent, invocation.signal);
|
|
5379
|
+
await live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext);
|
|
5380
|
+
});
|
|
4875
5381
|
const notices = commandContext.__notices;
|
|
4876
5382
|
return {
|
|
4877
5383
|
kind: "success",
|
|
@@ -4884,6 +5390,7 @@ function registerCommand(ctx, state, command) {
|
|
|
4884
5390
|
const dshName = ordinal === 1 ? baseName : `${baseName}-${ordinal}`;
|
|
4885
5391
|
try {
|
|
4886
5392
|
state.commandDisposers.set(command.name, commands.register(definitionFor(dshName)));
|
|
5393
|
+
state.dshCommandOwners.set(dshName, command.name);
|
|
4887
5394
|
if (ordinal > 1) logger(ctx).warn(`[pi2dsh] command /${command.name} collides with an earlier registration in this host; mounted as /${dshName} (Pi numbers colliding commands the same way)`);
|
|
4888
5395
|
return;
|
|
4889
5396
|
} catch (error) {
|
|
@@ -4942,6 +5449,56 @@ function requireSession(state, operation) {
|
|
|
4942
5449
|
if (session === void 0) throw new Error(`pi2dsh: ${operation} requires one active DSH agent with a durable session`);
|
|
4943
5450
|
return session;
|
|
4944
5451
|
}
|
|
5452
|
+
function releaseSharedProviderRoute(state, name) {
|
|
5453
|
+
if (!state.ownedProviderRoutes.delete(name)) return;
|
|
5454
|
+
const owners = state.shared.providerRouteOwners.get(name);
|
|
5455
|
+
owners?.delete(state);
|
|
5456
|
+
if (owners !== void 0 && owners.size > 0) return;
|
|
5457
|
+
state.shared.providerRouteOwners.delete(name);
|
|
5458
|
+
const route = state.shared.providerRouteDisposers.get(name);
|
|
5459
|
+
state.shared.providerRouteDisposers.delete(name);
|
|
5460
|
+
route?.();
|
|
5461
|
+
}
|
|
5462
|
+
function registerSharedProviderRoute(ctx, state, name, value) {
|
|
5463
|
+
const shared = state.shared;
|
|
5464
|
+
const existing = shared.providerRouteDisposers.get(name);
|
|
5465
|
+
if (existing !== void 0) {
|
|
5466
|
+
let owners = shared.providerRouteOwners.get(name);
|
|
5467
|
+
if (owners === void 0) {
|
|
5468
|
+
owners = /* @__PURE__ */ new Set();
|
|
5469
|
+
shared.providerRouteOwners.set(name, owners);
|
|
5470
|
+
}
|
|
5471
|
+
owners.add(state);
|
|
5472
|
+
state.ownedProviderRoutes.add(name);
|
|
5473
|
+
return existing;
|
|
5474
|
+
}
|
|
5475
|
+
const canonical = shared.providers.get(name) ?? value;
|
|
5476
|
+
shared.providers.set(name, canonical);
|
|
5477
|
+
const route = registerPiProviderRoute({
|
|
5478
|
+
llm: llmOf(ctx),
|
|
5479
|
+
ctx,
|
|
5480
|
+
providerId: name,
|
|
5481
|
+
provider: canonical,
|
|
5482
|
+
host: {
|
|
5483
|
+
resolveAuth: async () => resolvePiProviderAuth({
|
|
5484
|
+
providerId: name,
|
|
5485
|
+
providerConfig: canonical,
|
|
5486
|
+
store: oauthStoreOf(state)
|
|
5487
|
+
}),
|
|
5488
|
+
ensureModel: async () => {
|
|
5489
|
+
await discoverProviderModels(ctx, state, name, canonical);
|
|
5490
|
+
},
|
|
5491
|
+
warn: (message) => logger(ctx).warn(message),
|
|
5492
|
+
beforeProviderRequest: (payload, request) => dispatchHostProviderRequest(ctx, shared, state, payload, request),
|
|
5493
|
+
resolveAttachments: () => optionalService(ctx, "attachments")
|
|
5494
|
+
}
|
|
5495
|
+
});
|
|
5496
|
+
if (route === void 0) return void 0;
|
|
5497
|
+
shared.providerRouteDisposers.set(name, route);
|
|
5498
|
+
shared.providerRouteOwners.set(name, /* @__PURE__ */ new Set([state]));
|
|
5499
|
+
state.ownedProviderRoutes.add(name);
|
|
5500
|
+
return route;
|
|
5501
|
+
}
|
|
4945
5502
|
function createPiApi(ctx, state) {
|
|
4946
5503
|
return {
|
|
4947
5504
|
on(event, handler) {
|
|
@@ -4971,28 +5528,23 @@ function createPiApi(ctx, state) {
|
|
|
4971
5528
|
const name = typeof providerOrName === "string" ? providerOrName : String(providerOrName?.id ?? providerOrName?.name ?? "unnamed");
|
|
4972
5529
|
const value = typeof providerOrName === "string" ? config ?? {} : providerOrName;
|
|
4973
5530
|
state.providers.set(name, value);
|
|
5531
|
+
const sharedExisting = state.shared.providers.get(name);
|
|
5532
|
+
if (sharedExisting === void 0 || BUILTIN_PROVIDER_SEEDS.has(sharedExisting)) {
|
|
5533
|
+
state.shared.providers.set(name, value);
|
|
5534
|
+
if (sharedExisting !== void 0) {
|
|
5535
|
+
const stale = state.shared.providerRouteDisposers.get(name);
|
|
5536
|
+
if (stale !== void 0) {
|
|
5537
|
+
state.shared.providerRouteDisposers.delete(name);
|
|
5538
|
+
state.shared.providerRouteOwners.delete(name);
|
|
5539
|
+
stale();
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
4974
5543
|
if (providerSupportsOAuth(value)) {
|
|
4975
5544
|
ensureLoginCommand(ctx, state);
|
|
4976
5545
|
logger(ctx).info(`[pi2dsh] Pi provider ${JSON.stringify(name)} supports OAuth — log in with /login ${name}`);
|
|
4977
5546
|
}
|
|
4978
|
-
|
|
4979
|
-
llm: llmOf(ctx),
|
|
4980
|
-
ctx,
|
|
4981
|
-
providerId: name,
|
|
4982
|
-
provider: value,
|
|
4983
|
-
host: {
|
|
4984
|
-
resolveAuth: async () => resolvePiProviderAuth({
|
|
4985
|
-
providerId: name,
|
|
4986
|
-
providerConfig: value,
|
|
4987
|
-
store: oauthStoreOf(state)
|
|
4988
|
-
}),
|
|
4989
|
-
warn: (message) => logger(ctx).warn(message),
|
|
4990
|
-
beforeProviderRequest: (payload, request) => dispatchBeforeProviderRequest(ctx, state, payload, request),
|
|
4991
|
-
resolveAttachments: () => optionalService(ctx, "attachments")
|
|
4992
|
-
}
|
|
4993
|
-
});
|
|
4994
|
-
if (routeDisposer !== void 0) {
|
|
4995
|
-
state.providerRouteDisposers.set(name, routeDisposer);
|
|
5547
|
+
if (registerSharedProviderRoute(ctx, state, name, value) !== void 0) {
|
|
4996
5548
|
state.modelCatalog?.refresh();
|
|
4997
5549
|
if (providerCarriesTransport(value)) logger(ctx).info(`[pi2dsh] Pi provider ${JSON.stringify(name)} registered as a native DSH llm route`);
|
|
4998
5550
|
} else if (!providerSupportsOAuth(value)) logger(ctx).info(`[pi2dsh] recorded Pi provider ${JSON.stringify(name)}; model calls stay on DSH llm adapters`);
|
|
@@ -5000,11 +5552,7 @@ function createPiApi(ctx, state) {
|
|
|
5000
5552
|
},
|
|
5001
5553
|
unregisterProvider(name) {
|
|
5002
5554
|
state.providers.delete(name);
|
|
5003
|
-
|
|
5004
|
-
if (routeDisposer !== void 0) {
|
|
5005
|
-
state.providerRouteDisposers.delete(name);
|
|
5006
|
-
routeDisposer();
|
|
5007
|
-
}
|
|
5555
|
+
releaseSharedProviderRoute(state, name);
|
|
5008
5556
|
},
|
|
5009
5557
|
registerMessageRenderer(customType, renderer) {
|
|
5010
5558
|
state.messageRenderers.set(customType, renderer);
|
|
@@ -5200,15 +5748,10 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabi
|
|
|
5200
5748
|
resolveShim("pi-ai")
|
|
5201
5749
|
]);
|
|
5202
5750
|
const aliases = {};
|
|
5751
|
+
const require = createRequire(import.meta.url);
|
|
5203
5752
|
for (const family of ["@earendil-works", "@mariozechner"]) {
|
|
5204
5753
|
aliases[`${family}/pi-coding-agent`] = codingAgentShim;
|
|
5205
5754
|
aliases[`${family}/pi-tui`] = tuiShim;
|
|
5206
|
-
aliases[`${family}/pi-ai`] = aiShim;
|
|
5207
|
-
aliases[`${family}/pi-ai/compat`] = aiShim;
|
|
5208
|
-
aliases[`${family}/pi-ai/oauth`] = aiShim;
|
|
5209
|
-
aliases[`${family}/pi-ai/providers/all`] = aiShim;
|
|
5210
|
-
aliases[`${family}/pi-ai/bedrock-provider`] = aiShim;
|
|
5211
|
-
aliases[`${family}/pi-ai/bun-oauth`] = aiShim;
|
|
5212
5755
|
for (const api of [
|
|
5213
5756
|
"anthropic-messages",
|
|
5214
5757
|
"openai-completions",
|
|
@@ -5220,9 +5763,19 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabi
|
|
|
5220
5763
|
"mistral-conversations",
|
|
5221
5764
|
"bedrock-converse-stream",
|
|
5222
5765
|
"pi-messages"
|
|
5223
|
-
])
|
|
5766
|
+
]) {
|
|
5767
|
+
try {
|
|
5768
|
+
aliases[`${family}/pi-ai/api/${api}.lazy`] = fileURLToPath(import.meta.resolve(`@earendil-works/pi-ai/api/${api}.lazy`));
|
|
5769
|
+
} catch {}
|
|
5770
|
+
aliases[`${family}/pi-ai/api/${api}`] = aiShim;
|
|
5771
|
+
}
|
|
5772
|
+
aliases[`${family}/pi-ai/compat`] = aiShim;
|
|
5773
|
+
aliases[`${family}/pi-ai/oauth`] = aiShim;
|
|
5774
|
+
aliases[`${family}/pi-ai/providers/all`] = aiShim;
|
|
5775
|
+
aliases[`${family}/pi-ai/bedrock-provider`] = aiShim;
|
|
5776
|
+
aliases[`${family}/pi-ai/bun-oauth`] = aiShim;
|
|
5777
|
+
aliases[`${family}/pi-ai`] = aiShim;
|
|
5224
5778
|
}
|
|
5225
|
-
const require = createRequire(import.meta.url);
|
|
5226
5779
|
for (const entry of [
|
|
5227
5780
|
"typebox",
|
|
5228
5781
|
"typebox/value",
|
|
@@ -5272,9 +5825,13 @@ async function applyPiPackage(ctx, options) {
|
|
|
5272
5825
|
}
|
|
5273
5826
|
}));
|
|
5274
5827
|
const shared = sharedHostStateOf(ctx);
|
|
5828
|
+
const scopedOwner = options.ownerAgent ?? scopeOf(ctx);
|
|
5829
|
+
const ownerAgent = typeof scopedOwner === "object" && scopedOwner !== null ? scopedOwner : void 0;
|
|
5275
5830
|
const state = {
|
|
5276
5831
|
shared,
|
|
5277
5832
|
packageName: options.manifest.package.name,
|
|
5833
|
+
ownerAgent,
|
|
5834
|
+
hostAnchor: options.hostAnchor === true,
|
|
5278
5835
|
handlers: /* @__PURE__ */ new Map(),
|
|
5279
5836
|
tools: runtimeTools,
|
|
5280
5837
|
runner: new ExtensionRunner(piToolRecords),
|
|
@@ -5282,21 +5839,33 @@ async function applyPiPackage(ctx, options) {
|
|
|
5282
5839
|
toolRestrictions: /* @__PURE__ */ new WeakMap(),
|
|
5283
5840
|
commands: /* @__PURE__ */ new Map(),
|
|
5284
5841
|
commandDisposers: /* @__PURE__ */ new Map(),
|
|
5842
|
+
dshCommandOwners: /* @__PURE__ */ new Map(),
|
|
5285
5843
|
companionRoutes: shared.companionRoutes,
|
|
5286
5844
|
flags: /* @__PURE__ */ new Map(),
|
|
5287
5845
|
notifications: [],
|
|
5288
5846
|
activeAgents: /* @__PURE__ */ new Set(),
|
|
5847
|
+
hostAgent: void 0,
|
|
5848
|
+
piShutdownAgents: /* @__PURE__ */ new WeakSet(),
|
|
5289
5849
|
disposedAgents: /* @__PURE__ */ new WeakSet(),
|
|
5850
|
+
startedSessions: /* @__PURE__ */ new Set(),
|
|
5851
|
+
sessionStartTasks: /* @__PURE__ */ new Map(),
|
|
5852
|
+
startedSessionlessAgents: /* @__PURE__ */ new WeakSet(),
|
|
5853
|
+
sessionlessStartTasks: /* @__PURE__ */ new WeakMap(),
|
|
5854
|
+
extensionsReady: false,
|
|
5855
|
+
pendingSessionStarts: /* @__PURE__ */ new Map(),
|
|
5290
5856
|
currentSystemPrompt: "",
|
|
5291
5857
|
messageSource: `pi2dsh:${options.manifest.package.name}`,
|
|
5292
5858
|
eventBus: new EventEmitter(),
|
|
5293
5859
|
agentScope: new AsyncLocalStorage(),
|
|
5860
|
+
llmBridge: void 0,
|
|
5861
|
+
piAiRegistry: __createPiAiRuntimeRegistry(),
|
|
5862
|
+
subagentSessionFactory: void 0,
|
|
5294
5863
|
bridge: new PiSessionBridge(),
|
|
5295
5864
|
theme: new Theme(),
|
|
5296
5865
|
shortcuts: /* @__PURE__ */ new Map(),
|
|
5297
5866
|
messageRenderers: /* @__PURE__ */ new Map(),
|
|
5298
5867
|
entryRenderers: /* @__PURE__ */ new Map(),
|
|
5299
|
-
providers:
|
|
5868
|
+
providers: /* @__PURE__ */ new Map(),
|
|
5300
5869
|
autocompleteProviders: [],
|
|
5301
5870
|
editorBuffers: /* @__PURE__ */ new WeakMap(),
|
|
5302
5871
|
toolsExpanded: false,
|
|
@@ -5314,31 +5883,42 @@ async function applyPiPackage(ctx, options) {
|
|
|
5314
5883
|
streamingTexts: /* @__PURE__ */ new Map(),
|
|
5315
5884
|
lastLoggedModels: /* @__PURE__ */ new WeakMap(),
|
|
5316
5885
|
providerRouteDisposers: shared.providerRouteDisposers,
|
|
5317
|
-
|
|
5886
|
+
ownedProviderRoutes: /* @__PURE__ */ new Set(),
|
|
5887
|
+
publishedOAuthKeys: shared.publishedOAuthKeys,
|
|
5888
|
+
tuiSurfaces: void 0
|
|
5318
5889
|
};
|
|
5890
|
+
mountTuiSurfaceAdapter(ctx, state.packageName, (adapter) => {
|
|
5891
|
+
state.tuiSurfaces = adapter;
|
|
5892
|
+
}, typeof ownerAgent?.id === "string" ? ownerAgent.id : void 0);
|
|
5319
5893
|
subscribeLifecycle(ctx, state);
|
|
5320
5894
|
subscribeInterceptors(ctx, state);
|
|
5321
|
-
for (const provider of builtinProviders())
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5895
|
+
for (const provider of builtinProviders()) {
|
|
5896
|
+
const config = {
|
|
5897
|
+
name: provider.name,
|
|
5898
|
+
baseUrl: provider.baseUrl,
|
|
5899
|
+
oauth: provider.auth.oauth
|
|
5900
|
+
};
|
|
5901
|
+
BUILTIN_PROVIDER_SEEDS.add(config);
|
|
5902
|
+
state.providers.set(provider.id, config);
|
|
5903
|
+
if (!shared.providers.has(provider.id)) shared.providers.set(provider.id, config);
|
|
5904
|
+
}
|
|
5326
5905
|
ensureLoginCommand(ctx, state);
|
|
5327
5906
|
for (const [name, config] of [...state.providers]) try {
|
|
5328
5907
|
await ensureLoggedInProviderRoute(ctx, state, name, config);
|
|
5329
5908
|
} catch (error) {
|
|
5330
5909
|
logger(ctx).warn(`[pi2dsh] could not restore the route for logged-in provider ${JSON.stringify(name)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
5331
5910
|
}
|
|
5332
|
-
if (options.manifest.skillDirs.length > 0) {
|
|
5911
|
+
if (options.manifest.skillDirs.length > 0 && state.ownerAgent === void 0) {
|
|
5333
5912
|
if (ctx.get("skills") === void 0) logger(ctx).warn("[pi2dsh] migrated skills were not mounted because this DSH composition has no ctx.skills");
|
|
5334
5913
|
else {
|
|
5335
5914
|
const { apply: applyFilesystemSkills } = await import("@deepseek-ai/dsh-skill-filesystem");
|
|
5336
|
-
|
|
5915
|
+
const config = {
|
|
5337
5916
|
providerName: `pi2dsh-${options.manifest.package.name.replace(/[^a-zA-Z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "")}`,
|
|
5338
5917
|
includeDefaultRoots: false,
|
|
5339
5918
|
customSkillDirs: options.manifest.skillDirs.map((path) => join(rootDir, path)),
|
|
5340
5919
|
watch: false
|
|
5341
|
-
}
|
|
5920
|
+
};
|
|
5921
|
+
await ctx.plugin(Object.assign((skillCtx) => applyFilesystemSkills(skillCtx, config), { inject: ["skills"] }));
|
|
5342
5922
|
}
|
|
5343
5923
|
}
|
|
5344
5924
|
const llm = llmOf(ctx);
|
|
@@ -5352,12 +5932,11 @@ async function applyPiPackage(ctx, options) {
|
|
|
5352
5932
|
});
|
|
5353
5933
|
}
|
|
5354
5934
|
await state.modelCatalog.refresh();
|
|
5355
|
-
|
|
5935
|
+
state.llmBridge = (model, context, callOptions) => streamViaDshLlm(llm, {
|
|
5356
5936
|
model,
|
|
5357
5937
|
context,
|
|
5358
5938
|
options: callOptions
|
|
5359
|
-
})
|
|
5360
|
-
ctx.effect(() => () => __setPiAiLlmBridge(void 0));
|
|
5939
|
+
});
|
|
5361
5940
|
}
|
|
5362
5941
|
const browserSurfaces = state.shared.browserSurfaces ??= new BrowserSurfaces();
|
|
5363
5942
|
if (state.shared.browserSurfacesRouted !== true) state.shared.browserSurfacesRouted = registerBrowserSurfaceRoute(ctx, browserSurfaces);
|
|
@@ -5416,7 +5995,7 @@ async function applyPiPackage(ctx, options) {
|
|
|
5416
5995
|
}
|
|
5417
5996
|
return rendered;
|
|
5418
5997
|
}));
|
|
5419
|
-
|
|
5998
|
+
state.subagentSessionFactory = async (subagentOptions) => {
|
|
5420
5999
|
const created = await createBridgedAgentSession(subagentHost(), subagentOptions);
|
|
5421
6000
|
const parent = agentSession(currentAgent(state));
|
|
5422
6001
|
const parentId = parent === void 0 ? "" : String(parent.id ?? "");
|
|
@@ -5430,7 +6009,7 @@ async function applyPiPackage(ctx, options) {
|
|
|
5430
6009
|
ctx.effect(() => dispose);
|
|
5431
6010
|
}
|
|
5432
6011
|
return created;
|
|
5433
|
-
}
|
|
6012
|
+
};
|
|
5434
6013
|
const subagentHost = () => ({
|
|
5435
6014
|
cordis: ctx,
|
|
5436
6015
|
cwd: () => cwdOf(currentAgent(state)),
|
|
@@ -5451,7 +6030,6 @@ async function applyPiPackage(ctx, options) {
|
|
|
5451
6030
|
messageSource: state.messageSource,
|
|
5452
6031
|
packageName: state.packageName
|
|
5453
6032
|
});
|
|
5454
|
-
ctx.effect(() => () => __setSubagentSessionFactory(void 0));
|
|
5455
6033
|
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
5456
6034
|
const onExtensionError = (failure) => logger(ctx).warn(`[pi2dsh] extension entry failed and was skipped (matching Pi's per-extension error isolation): ${failure}`);
|
|
5457
6035
|
const onCapabilityGap = (gap) => capabilityLedgerOf(ctx, state).reportUnusable({
|
|
@@ -5466,21 +6044,28 @@ async function applyPiPackage(ctx, options) {
|
|
|
5466
6044
|
guidance: "",
|
|
5467
6045
|
packageName: state.packageName
|
|
5468
6046
|
});
|
|
5469
|
-
const mountExtensions = () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference);
|
|
6047
|
+
const mountExtensions = () => runInPiRuntime(state, currentAgent(state), () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference));
|
|
5470
6048
|
await mountExtensions();
|
|
5471
|
-
|
|
6049
|
+
await flushPendingSessionStarts(ctx, state);
|
|
6050
|
+
const remounts = packageRemountsOf(ctx, shared);
|
|
5472
6051
|
remounts.set(state.packageName, async () => {
|
|
6052
|
+
state.extensionsReady = false;
|
|
5473
6053
|
state.handlers.clear();
|
|
5474
6054
|
state.eventBus.removeAllListeners();
|
|
5475
|
-
|
|
5476
|
-
|
|
6055
|
+
try {
|
|
6056
|
+
await registerPromptCommands(ctx, state, rootDir, options.manifest);
|
|
6057
|
+
await mountExtensions();
|
|
6058
|
+
await restartReloadedPiSessions(ctx, state);
|
|
6059
|
+
} finally {
|
|
6060
|
+
state.extensionsReady = true;
|
|
6061
|
+
}
|
|
5477
6062
|
});
|
|
5478
6063
|
ctx.effect(() => () => {
|
|
5479
6064
|
remounts.delete(state.packageName);
|
|
5480
6065
|
});
|
|
5481
6066
|
const health = state.shared.capabilityLedger?.healthOf(state.packageName);
|
|
5482
6067
|
const healthSuffix = health === void 0 || health.status === "ok" ? "" : ` — ${health.status.toUpperCase()}: missing Pi capabilities ${health.gaps.join(", ")}`;
|
|
5483
|
-
logger(ctx).info(`[pi2dsh] loaded ${options.manifest.package.name}: ${state.tools.size} tools, ${state.commands.size} commands, ${options.manifest.skillDirs.length} skill roots${healthSuffix}`);
|
|
6068
|
+
logger(ctx).info(`[pi2dsh] loaded ${options.manifest.package.name}: ${state.tools.size} tools, ${state.commands.size} commands, ${options.manifest.skillDirs.length} skill roots${healthSuffix}${state.hostAnchor ? " (host anchor)" : ""}`);
|
|
5484
6069
|
}
|
|
5485
6070
|
const runtimeInternals = {
|
|
5486
6071
|
compactionReason,
|
|
@@ -5497,4 +6082,4 @@ const runtimeInternals = {
|
|
|
5497
6082
|
//#endregion
|
|
5498
6083
|
export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
|
|
5499
6084
|
|
|
5500
|
-
//# sourceMappingURL=runtime-
|
|
6085
|
+
//# sourceMappingURL=runtime-Bb9Bfk4M.mjs.map
|