pi2dsh 0.13.2 → 0.13.3

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.
Files changed (106) hide show
  1. package/README.md +22 -19
  2. package/README.zh.md +18 -15
  3. package/dist/{analyzer-CJFwQlSG.mjs → analyzer-DBtUYKI1.mjs} +2 -2
  4. package/dist/{analyzer-CJFwQlSG.mjs.map → analyzer-DBtUYKI1.mjs.map} +1 -1
  5. package/dist/anthropic-messages-CaYTgvLF.mjs +6436 -0
  6. package/dist/anthropic-messages-CaYTgvLF.mjs.map +1 -0
  7. package/dist/azure-openai-responses-HKWkzl2m.mjs +208 -0
  8. package/dist/azure-openai-responses-HKWkzl2m.mjs.map +1 -0
  9. package/dist/cli.mjs +3 -2
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/compat/pi-ai.d.mts +3 -2636
  12. package/dist/compat/pi-ai.mjs +3 -2
  13. package/dist/compat/pi-coding-agent.d.mts +2 -2
  14. package/dist/compat/pi-coding-agent.mjs +2 -2
  15. package/dist/{mcp-config-_idn-pzI.mjs → compatibility-Bz6ZXA7F.mjs} +8 -185
  16. package/dist/compatibility-Bz6ZXA7F.mjs.map +1 -0
  17. package/dist/completionchunk-Dhk56lZl.mjs +5212 -0
  18. package/dist/completionchunk-Dhk56lZl.mjs.map +1 -0
  19. package/dist/{credentials-oauth-Txs6oew8.mjs → credentials-oauth-CAohSLcB.mjs} +3 -3
  20. package/dist/{credentials-oauth-Txs6oew8.mjs.map → credentials-oauth-CAohSLcB.mjs.map} +1 -1
  21. package/dist/credentials-oauth.mjs +1 -1
  22. package/dist/deferred-tools-BD4g5QUx.mjs +32 -0
  23. package/dist/deferred-tools-BD4g5QUx.mjs.map +1 -0
  24. package/dist/diagnostics-BqYkodgB.mjs +35 -0
  25. package/dist/diagnostics-BqYkodgB.mjs.map +1 -0
  26. package/dist/dist-QBBA-dL_.mjs +1246 -0
  27. package/dist/dist-QBBA-dL_.mjs.map +1 -0
  28. package/dist/error-body-DKA3lknR.mjs +104 -0
  29. package/dist/error-body-DKA3lknR.mjs.map +1 -0
  30. package/dist/event-stream-QxN1yvIF.mjs +64 -0
  31. package/dist/event-stream-QxN1yvIF.mjs.map +1 -0
  32. package/dist/from-BCscBQ9P.mjs +3856 -0
  33. package/dist/from-BCscBQ9P.mjs.map +1 -0
  34. package/dist/github-copilot-headers-CZo2QHrt.mjs +25 -0
  35. package/dist/github-copilot-headers-CZo2QHrt.mjs.map +1 -0
  36. package/dist/google-generative-ai-2gCFYp-t.mjs +356 -0
  37. package/dist/google-generative-ai-2gCFYp-t.mjs.map +1 -0
  38. package/dist/google-shared-75IdVyrx.mjs +29041 -0
  39. package/dist/google-shared-75IdVyrx.mjs.map +1 -0
  40. package/dist/google-vertex-Bhyo4z2i.mjs +400 -0
  41. package/dist/google-vertex-Bhyo4z2i.mjs.map +1 -0
  42. package/dist/hash-Dbggamac.mjs +19 -0
  43. package/dist/hash-Dbggamac.mjs.map +1 -0
  44. package/dist/headers-CPzZhJQn.mjs +17 -0
  45. package/dist/headers-CPzZhJQn.mjs.map +1 -0
  46. package/dist/host.d.mts +12 -2
  47. package/dist/host.d.mts.map +1 -1
  48. package/dist/host.mjs +43 -5
  49. package/dist/host.mjs.map +1 -1
  50. package/dist/index.d.mts +5 -3
  51. package/dist/index.d.mts.map +1 -1
  52. package/dist/index.mjs +40 -16
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/json-parse-CZ0U0sj_.mjs +384 -0
  55. package/dist/json-parse-CZ0U0sj_.mjs.map +1 -0
  56. package/dist/mcp-config-jmzbgdHa.mjs +181 -0
  57. package/dist/mcp-config-jmzbgdHa.mjs.map +1 -0
  58. package/dist/mistral-conversations-DJyeTo8C.mjs +36921 -0
  59. package/dist/mistral-conversations-DJyeTo8C.mjs.map +1 -0
  60. package/dist/multipart-parser-SRcGkVuX.mjs +301 -0
  61. package/dist/multipart-parser-SRcGkVuX.mjs.map +1 -0
  62. package/dist/{pi-ai-D7PqVeWs.mjs → openai-DkzRh5_G.mjs} +78 -5063
  63. package/dist/openai-DkzRh5_G.mjs.map +1 -0
  64. package/dist/openai-codex-responses-DedxQjNG.mjs +1220 -0
  65. package/dist/openai-codex-responses-DedxQjNG.mjs.map +1 -0
  66. package/dist/openai-completions-DrjrWRdX.mjs +945 -0
  67. package/dist/openai-completions-DrjrWRdX.mjs.map +1 -0
  68. package/dist/openai-prompt-cache-BDWtZRKh.mjs +11 -0
  69. package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +1 -0
  70. package/dist/openai-responses-BRdGpBX6.mjs +251 -0
  71. package/dist/openai-responses-BRdGpBX6.mjs.map +1 -0
  72. package/dist/openai-responses-shared-BmnSvdKL.mjs +570 -0
  73. package/dist/openai-responses-shared-BmnSvdKL.mjs.map +1 -0
  74. package/dist/otel-CBhmS-NR.mjs +24935 -0
  75. package/dist/otel-CBhmS-NR.mjs.map +1 -0
  76. package/dist/pi-ai-CfokFhIL.d.mts +2673 -0
  77. package/dist/pi-ai-CfokFhIL.d.mts.map +1 -0
  78. package/dist/pi-ai-Dh7HUxbt.mjs +3239 -0
  79. package/dist/pi-ai-Dh7HUxbt.mjs.map +1 -0
  80. package/dist/{pi-coding-agent-B0PT23gE.d.mts → pi-coding-agent-69yY0ke-.d.mts} +4 -2
  81. package/dist/{pi-coding-agent-B0PT23gE.d.mts.map → pi-coding-agent-69yY0ke-.d.mts.map} +1 -1
  82. package/dist/{pi-coding-agent-BVZu0dHG.mjs → pi-coding-agent-Cvez0jkF.mjs} +0 -0
  83. package/dist/{pi-coding-agent-BVZu0dHG.mjs.map → pi-coding-agent-Cvez0jkF.mjs.map} +1 -1
  84. package/dist/pi-messages-D61G2tgs.mjs +307 -0
  85. package/dist/pi-messages-D61G2tgs.mjs.map +1 -0
  86. package/dist/provider-env-C72pF4vP.mjs +40 -0
  87. package/dist/provider-env-C72pF4vP.mjs.map +1 -0
  88. package/dist/provider-retry-Cw6YJ4cr.mjs +80 -0
  89. package/dist/provider-retry-Cw6YJ4cr.mjs.map +1 -0
  90. package/dist/rolldown-runtime-CTfmNlz1.mjs +44 -0
  91. package/dist/{runtime-DEjs_Qlf.mjs → runtime-Dd2KhoX-.mjs} +659 -125
  92. package/dist/runtime-Dd2KhoX-.mjs.map +1 -0
  93. package/dist/runtime.d.mts +135 -26
  94. package/dist/runtime.d.mts.map +1 -1
  95. package/dist/runtime.mjs +1 -1
  96. package/dist/simple-options-B_GO8FDE.mjs +456 -0
  97. package/dist/simple-options-B_GO8FDE.mjs.map +1 -0
  98. package/dist/src-DlTagfW5.mjs +1186 -0
  99. package/dist/src-DlTagfW5.mjs.map +1 -0
  100. package/dist/{types-7IWJPPvS.d.mts → types-B9VBF8le.d.mts} +2 -9
  101. package/dist/{types-7IWJPPvS.d.mts.map → types-B9VBF8le.d.mts.map} +1 -1
  102. package/package.json +2 -2
  103. package/dist/compat/pi-ai.d.mts.map +0 -1
  104. package/dist/mcp-config-_idn-pzI.mjs.map +0 -1
  105. package/dist/pi-ai-D7PqVeWs.mjs.map +0 -1
  106. package/dist/runtime-DEjs_Qlf.mjs.map +0 -1
@@ -1,8 +1,9 @@
1
1
 
2
- import { X as isContextOverflow, Y as AssistantMessageEventStream, c as builtinProviders, i as __setPiAiLlmBridge, x as getSupportedThinkingLevels } from "./pi-ai-D7PqVeWs.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-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-Cvez0jkF.mjs";
4
3
  import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
5
- 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-Txs6oew8.mjs";
4
+ import { $ as isContextOverflow, Q as AssistantMessageEventStream, S as getSupportedThinkingLevels, c as builtinProviders, o as __runWithPiAiRuntime, r as __createPiAiRuntimeRegistry } from "./pi-ai-Dh7HUxbt.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-CAohSLcB.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";
@@ -2274,7 +2276,12 @@ function piProviderDshAdapter(providerId, provider, host) {
2274
2276
  async *stream(options) {
2275
2277
  const auth = (await host.resolveAuth())?.auth;
2276
2278
  const modelId = String(options.model ?? "");
2277
- const model = providerModels(provider).find((entry) => entry.id === modelId) ?? {
2279
+ let model = providerModels(provider).find((entry) => entry.id === modelId);
2280
+ if (model === void 0 && host.ensureModel !== void 0) {
2281
+ await host.ensureModel(modelId);
2282
+ model = providerModels(provider).find((entry) => entry.id === modelId);
2283
+ }
2284
+ model ??= {
2278
2285
  id: modelId,
2279
2286
  provider: providerId
2280
2287
  };
@@ -2297,6 +2304,7 @@ function piProviderDshAdapter(providerId, provider, host) {
2297
2304
  ...host.beforeProviderRequest === void 0 ? {} : { onPayload: (payload) => host.beforeProviderRequest(payload, {
2298
2305
  provider: providerId,
2299
2306
  model,
2307
+ ...typeof options.sessionId === "string" ? { sessionId: options.sessionId } : {},
2300
2308
  ...options.signal instanceof AbortSignal ? { signal: options.signal } : {}
2301
2309
  }) },
2302
2310
  ...effort === void 0 || effort === "off" ? {} : { reasoning: effort }
@@ -2654,6 +2662,220 @@ function registerPiProviderRoute(options) {
2654
2662
  }
2655
2663
  }
2656
2664
  //#endregion
2665
+ //#region src/tui-surfaces.ts
2666
+ function service(ctx, name) {
2667
+ try {
2668
+ return ctx.get(name, false);
2669
+ } catch {
2670
+ return;
2671
+ }
2672
+ }
2673
+ function surfaceToken(value) {
2674
+ const normalized = value.toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
2675
+ if (normalized.length === 0) return "entry";
2676
+ return /^[a-z]/u.test(normalized) ? normalized : `k-${normalized}`;
2677
+ }
2678
+ function sceneIdFor(packageName) {
2679
+ return `pi2dsh-${surfaceToken(packageName)}-custom`;
2680
+ }
2681
+ function statusKeyFor(packageName, key) {
2682
+ return `pi2dsh:${surfaceToken(packageName)}:${surfaceToken(key)}`;
2683
+ }
2684
+ function rawTerminalInput(input, key, event) {
2685
+ const keypress = event?.keypress;
2686
+ if (typeof keypress?.sequence === "string" && keypress.sequence.length > 0) return keypress.sequence;
2687
+ if (key.return === true) return "\r";
2688
+ if (key.escape === true) return "\x1B";
2689
+ if (key.tab === true) return " ";
2690
+ if (key.backspace === true) return "";
2691
+ if (key.upArrow === true) return "\x1B[A";
2692
+ if (key.downArrow === true) return "\x1B[B";
2693
+ if (key.rightArrow === true) return "\x1B[C";
2694
+ if (key.leftArrow === true) return "\x1B[D";
2695
+ if (key.ctrl === true && input.length === 1) {
2696
+ const code = input.toLowerCase().charCodeAt(0);
2697
+ if (code >= 97 && code <= 122) return String.fromCharCode(code - 96);
2698
+ }
2699
+ return input;
2700
+ }
2701
+ function preferredWidth(options) {
2702
+ const width = (typeof options?.overlayOptions === "function" ? options.overlayOptions() : options?.overlayOptions)?.width;
2703
+ return typeof width === "number" && Number.isFinite(width) && width > 0 ? Math.floor(width) : void 0;
2704
+ }
2705
+ /**
2706
+ * One package-scoped Pi custom-surface host backed by one dsh-TUI scene.
2707
+ * The MCP adapter is a consumer, not a special case: every component using
2708
+ * the public Pi component protocol follows the same path.
2709
+ */
2710
+ var TuiSurfaceAdapter = class {
2711
+ packageName;
2712
+ instanceKey;
2713
+ sceneId;
2714
+ scenes;
2715
+ status;
2716
+ statusDisposers = /* @__PURE__ */ new Map();
2717
+ listeners = /* @__PURE__ */ new Set();
2718
+ unregisterScene;
2719
+ revision = 0;
2720
+ active;
2721
+ disposed = false;
2722
+ constructor(ctx, packageName, instanceKey) {
2723
+ this.packageName = packageName;
2724
+ this.instanceKey = instanceKey;
2725
+ const scenes = service(ctx, "tuiScenes");
2726
+ if (scenes === void 0) throw new Error("pi2dsh: dsh-TUI scene service is unavailable");
2727
+ this.scenes = scenes;
2728
+ this.status = service(ctx, "tuiStatus");
2729
+ this.sceneId = sceneIdFor(instanceKey === void 0 ? packageName : `${packageName}-${instanceKey}`);
2730
+ this.unregisterScene = scenes.register({
2731
+ id: this.sceneId,
2732
+ title: `${packageName} custom panel`,
2733
+ component: (props) => this.scene(props)
2734
+ });
2735
+ }
2736
+ get available() {
2737
+ return !this.disposed;
2738
+ }
2739
+ subscribe(listener) {
2740
+ this.listeners.add(listener);
2741
+ return () => this.listeners.delete(listener);
2742
+ }
2743
+ snapshot() {
2744
+ return this.revision;
2745
+ }
2746
+ setStatus(key, text) {
2747
+ if (this.disposed || this.status === void 0) return;
2748
+ const dshKey = statusKeyFor(this.instanceKey === void 0 ? this.packageName : `${this.packageName}-${this.instanceKey}`, key);
2749
+ this.statusDisposers.get(dshKey)?.();
2750
+ this.statusDisposers.delete(dshKey);
2751
+ if (text === void 0) return;
2752
+ const dispose = this.status.set(dshKey, String(text));
2753
+ if (typeof dispose === "function") this.statusDisposers.set(dshKey, dispose);
2754
+ }
2755
+ custom(factory, theme, keybindings, options) {
2756
+ if (this.disposed) return Promise.resolve(void 0);
2757
+ this.finish(void 0);
2758
+ return new Promise((resolve, reject) => {
2759
+ const run = {
2760
+ options,
2761
+ resolve,
2762
+ reject
2763
+ };
2764
+ this.active = run;
2765
+ const driver = { requestRender: () => this.invalidate() };
2766
+ const done = (value) => this.finishFor(run, value);
2767
+ Promise.resolve().then(() => factory(driver, theme, keybindings, done)).then((component) => {
2768
+ if (this.active !== run) {
2769
+ component.dispose?.();
2770
+ return;
2771
+ }
2772
+ if (typeof component?.render !== "function") {
2773
+ this.failFor(run, /* @__PURE__ */ new TypeError("Pi custom component must implement render(width)"));
2774
+ return;
2775
+ }
2776
+ run.component = component;
2777
+ this.invalidate();
2778
+ if (!this.scenes.open(this.sceneId)) this.finishFor(run, void 0);
2779
+ }, (error) => this.failFor(run, error));
2780
+ });
2781
+ }
2782
+ render(width) {
2783
+ const active = this.active;
2784
+ if (active?.component === void 0) return [];
2785
+ const available = Math.max(12, Math.floor(width) - 2);
2786
+ const requested = preferredWidth(active.options);
2787
+ return active.component.render(requested === void 0 ? available : Math.min(available, requested));
2788
+ }
2789
+ handleInput(data) {
2790
+ const component = this.active?.component;
2791
+ if (component === void 0) return;
2792
+ component.handleInput?.(data);
2793
+ component.invalidate?.();
2794
+ this.invalidate();
2795
+ }
2796
+ dispose() {
2797
+ if (this.disposed) return;
2798
+ this.disposed = true;
2799
+ this.finish(void 0);
2800
+ for (const dispose of this.statusDisposers.values()) dispose();
2801
+ this.statusDisposers.clear();
2802
+ this.unregisterScene();
2803
+ this.listeners.clear();
2804
+ }
2805
+ invalidate() {
2806
+ this.revision += 1;
2807
+ for (const listener of this.listeners) listener();
2808
+ }
2809
+ finish(value) {
2810
+ const active = this.active;
2811
+ if (active !== void 0) this.finishFor(active, value);
2812
+ }
2813
+ finishFor(run, value, closeScene = true) {
2814
+ if (this.active !== run) return;
2815
+ this.active = void 0;
2816
+ run.component?.dispose?.();
2817
+ if (closeScene) this.scenes.close();
2818
+ this.invalidate();
2819
+ run.resolve(value);
2820
+ }
2821
+ failFor(run, error) {
2822
+ if (this.active !== run) return;
2823
+ this.active = void 0;
2824
+ run.component?.dispose?.();
2825
+ this.scenes.close();
2826
+ this.invalidate();
2827
+ run.reject(error);
2828
+ }
2829
+ scene(props) {
2830
+ const { React, ui } = props;
2831
+ React.useSyncExternalStore((listener) => this.subscribe(listener), () => this.snapshot(), () => this.snapshot());
2832
+ React.useEffect(() => () => {
2833
+ const active = this.active;
2834
+ if (active !== void 0) this.finishFor(active, void 0, false);
2835
+ }, []);
2836
+ ui.useInput((input, key, event) => this.handleInput(rawTerminalInput(input, key, event)));
2837
+ const { columns } = ui.useTerminalSize();
2838
+ const lines = this.render(columns);
2839
+ const Line = ui.Ansi ?? ui.Text;
2840
+ return React.createElement(ui.Box, {
2841
+ flexDirection: "column",
2842
+ width: "100%",
2843
+ paddingX: 1
2844
+ }, ...lines.map((line, index) => React.createElement(Line, { key: index }, line)));
2845
+ }
2846
+ };
2847
+ /**
2848
+ * Attach lazily to dsh-TUI. Cordis reloads this child fiber whenever the
2849
+ * service implementation changes, so a TUI update cannot leave a stale scene
2850
+ * proxy in the bridge.
2851
+ */
2852
+ function mountTuiSurfaceAdapter(ctx, packageName, publish, instanceKey) {
2853
+ const attach = (surfaceCtx) => {
2854
+ const adapter = new TuiSurfaceAdapter(surfaceCtx, packageName, instanceKey);
2855
+ publish(adapter);
2856
+ surfaceCtx.effect?.(() => () => {
2857
+ adapter.dispose();
2858
+ publish(void 0);
2859
+ });
2860
+ };
2861
+ if (service(ctx, "tuiScenes") !== void 0) {
2862
+ attach(ctx);
2863
+ return;
2864
+ }
2865
+ if (typeof ctx.inject === "function") {
2866
+ ctx.inject(["tuiScenes"], attach);
2867
+ return;
2868
+ }
2869
+ }
2870
+ /**
2871
+ * dsh-TUI handles `/mcp` locally before it asks DSH's command registry. Until
2872
+ * it exposes a reserved-command seam, keep its native status command and make
2873
+ * an installed Pi MCP manager reachable as `/pi-mcp`.
2874
+ */
2875
+ function commandNameForDshTui(name, tuiAvailable) {
2876
+ return tuiAvailable && name === "mcp" ? "pi-mcp" : name;
2877
+ }
2878
+ //#endregion
2657
2879
  //#region src/runtime.ts
2658
2880
  /** Fallback thread ids when a child session reports none. */
2659
2881
  let sidePanelSerial = 0;
@@ -3042,9 +3264,8 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3042
3264
  ...prefill === void 0 ? {} : { detail: `Current text:\n${String(prefill)}` }
3043
3265
  }),
3044
3266
  setStatus: (key, text) => {
3045
- const surfaces = state.shared.browserSurfaces;
3046
- if (surfaces === void 0) return;
3047
- surfaces.setStatus(sessionIdOf(state, agent), state.packageName, String(key), text);
3267
+ state.shared.browserSurfaces?.setStatus(sessionIdOf(state, agent), state.packageName, String(key), text);
3268
+ state.tuiSurfaces?.setStatus(String(key), text);
3048
3269
  },
3049
3270
  setWidget: (key, content) => {
3050
3271
  const surfaces = state.shared.browserSurfaces;
@@ -3063,7 +3284,11 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3063
3284
  setFooter: (factory) => putSurface(ctx, state, agent, "footer", factory),
3064
3285
  setHeader: (factory) => putSurface(ctx, state, agent, "header", factory),
3065
3286
  setTitle: (title) => putSurface(ctx, state, agent, "title", title),
3066
- custom: async () => void 0,
3287
+ custom: async (factory, options) => {
3288
+ const surfaces = state.tuiSurfaces;
3289
+ if (surfaces === void 0 || typeof factory !== "function") return void 0;
3290
+ return surfaces.custom(factory, state.theme, getKeybindings(), options);
3291
+ },
3067
3292
  pasteToEditor(text) {
3068
3293
  const surfaces = state.shared.browserSurfaces;
3069
3294
  const sessionId = sessionIdOf(state, agent);
@@ -3258,9 +3483,11 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3258
3483
  }
3259
3484
  },
3260
3485
  ui,
3261
- mode: "rpc",
3486
+ get mode() {
3487
+ return state.tuiSurfaces?.available === true ? "tui" : "rpc";
3488
+ },
3262
3489
  get hasUI() {
3263
- return humanAnswererAvailable(userQuestions, agent);
3490
+ return state.tuiSurfaces?.available === true || humanAnswererAvailable(userQuestions, agent);
3264
3491
  },
3265
3492
  cwd: contextCwd,
3266
3493
  sessionManager: session === void 0 ? state.bridge.readonlySessionManager({
@@ -3461,8 +3688,8 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3461
3688
  return { cancelled: false };
3462
3689
  },
3463
3690
  reload: async () => {
3464
- const remounts = state.shared.packageRemounts;
3465
- if (remounts === void 0 || remounts.size === 0) return;
3691
+ const remounts = packageRemountsOf(ctx, state.shared);
3692
+ if (remounts.size === 0) return;
3466
3693
  for (const [name, remount] of remounts) try {
3467
3694
  await remount();
3468
3695
  } catch (error) {
@@ -3481,10 +3708,19 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3481
3708
  async function dispatch(state, eventName, event, eventContext) {
3482
3709
  const results = [];
3483
3710
  const agent = eventContext.__agent;
3484
- for (const handler of state.handlers.get(eventName) ?? []) results.push(await state.agentScope.run(agent, () => handler(event, eventContext)));
3711
+ for (const handler of state.handlers.get(eventName) ?? []) results.push(await runInPiRuntime(state, agent, () => handler(event, eventContext)));
3485
3712
  return results;
3486
3713
  }
3487
3714
  /**
3715
+ * Bind every compatibility singleton to this exact Agent runtime for the
3716
+ * duration of extension-owned work. AsyncLocalStorage follows promises and
3717
+ * detached work spawned inside the callback, so two live Agents cannot
3718
+ * overwrite each other's model bridge or child-session factory.
3719
+ */
3720
+ function runInPiRuntime(state, agent, callback) {
3721
+ return state.agentScope.run(agent, () => __runWithSubagentSessionFactory(state.subagentSessionFactory, () => __runWithPiAiRuntime(state.llmBridge, state.piAiRegistry, callback)));
3722
+ }
3723
+ /**
3488
3724
  * Pi's request-body event is a waterfall over the body the provider transport
3489
3725
  * will actually send. Only package-registered Pi transports expose that body
3490
3726
  * through SimpleStreamOptions.onPayload; native DSH adapters build it behind
@@ -3499,7 +3735,7 @@ async function dispatchBeforeProviderRequest(ctx, state, payload, request) {
3499
3735
  provider: request.provider
3500
3736
  };
3501
3737
  for (const handler of state.handlers.get("before_provider_request") ?? []) try {
3502
- const result = await state.agentScope.run(agent, () => handler({
3738
+ const result = await runInPiRuntime(state, agent, () => handler({
3503
3739
  type: "before_provider_request",
3504
3740
  payload: current
3505
3741
  }, eventContext));
@@ -3509,6 +3745,14 @@ async function dispatchBeforeProviderRequest(ctx, state, payload, request) {
3509
3745
  }
3510
3746
  return current;
3511
3747
  }
3748
+ /** Run Pi's provider-body waterfall through every package runtime of one Agent. */
3749
+ async function dispatchHostProviderRequest(ctx, shared, fallback, payload, request) {
3750
+ const scoped = request.sessionId === void 0 ? void 0 : shared.runtimeStatesBySession.get(request.sessionId);
3751
+ const states = scoped === void 0 || scoped.size === 0 ? [fallback] : [...scoped];
3752
+ let current = payload;
3753
+ for (const state of states) current = await dispatchBeforeProviderRequest(ctx, state, current, request);
3754
+ return current;
3755
+ }
3512
3756
  /**
3513
3757
  * What triggered a compaction, in Pi's vocabulary.
3514
3758
  *
@@ -3611,41 +3855,167 @@ async function messageFromSessionEvent(ctx, event) {
3611
3855
  function sourceReason(value) {
3612
3856
  return value === "resume" ? "resume" : value === "fork" ? "fork" : "startup";
3613
3857
  }
3858
+ /**
3859
+ * Claim Pi's once-per-session `session_start` event.
3860
+ *
3861
+ * dsh-TUI can execute a slash command before DSH starts the first model turn,
3862
+ * so `agent/session-start` has not necessarily been announced yet. Pi, by
3863
+ * contrast, announces `session_start` before the user can execute a command.
3864
+ * The command bridge uses this same claim before invoking a Pi command; when
3865
+ * DSH later announces the real agent lifecycle, the durable session id keeps
3866
+ * the second path from restarting the extension.
3867
+ */
3868
+ function claimPiSessionStart(state, agent) {
3869
+ const id = agentSession(agent)?.id;
3870
+ if (typeof id === "string") {
3871
+ if (state.startedSessions.has(id)) return false;
3872
+ state.startedSessions.add(id);
3873
+ return true;
3874
+ }
3875
+ if (typeof agent === "object" && agent !== null) {
3876
+ if (state.startedSessionlessAgents.has(agent)) return false;
3877
+ state.startedSessionlessAgents.add(agent);
3878
+ return true;
3879
+ }
3880
+ return false;
3881
+ }
3882
+ async function ensurePiSessionStarted(ctx, state, agent, signal) {
3883
+ await startPiSession(ctx, state, agent, "startup", signal);
3884
+ }
3885
+ function inFlightPiSessionStart(state, agent) {
3886
+ const id = agentSession(agent)?.id;
3887
+ if (typeof id === "string") return state.sessionStartTasks.get(id);
3888
+ if (typeof agent === "object" && agent !== null) return state.sessionlessStartTasks.get(agent);
3889
+ }
3890
+ function rememberPiSessionStart(state, agent, task) {
3891
+ const id = agentSession(agent)?.id;
3892
+ if (typeof id === "string") state.sessionStartTasks.set(id, task);
3893
+ else if (typeof agent === "object" && agent !== null) state.sessionlessStartTasks.set(agent, task);
3894
+ }
3895
+ function forgetPiSessionStart(state, agent, task) {
3896
+ const id = agentSession(agent)?.id;
3897
+ if (typeof id === "string") {
3898
+ if (state.sessionStartTasks.get(id) === task) state.sessionStartTasks.delete(id);
3899
+ } else if (typeof agent === "object" && agent !== null && state.sessionlessStartTasks.get(agent) === task) state.sessionlessStartTasks.delete(agent);
3900
+ }
3901
+ function releasePiSessionClaim(state, agent) {
3902
+ const id = agentSession(agent)?.id;
3903
+ if (typeof id === "string") {
3904
+ state.startedSessions.delete(id);
3905
+ state.sessionStartTasks.delete(id);
3906
+ } else if (typeof agent === "object" && agent !== null) {
3907
+ state.startedSessionlessAgents.delete(agent);
3908
+ state.sessionlessStartTasks.delete(agent);
3909
+ }
3910
+ }
3911
+ function replacementReason(reason) {
3912
+ if (reason === "resume") return "resume";
3913
+ if (reason === "fork") return "fork";
3914
+ return "new";
3915
+ }
3916
+ function trackRuntimeSession(state, agent) {
3917
+ const id = agentSession(agent)?.id;
3918
+ if (typeof id !== "string") return;
3919
+ let states = state.shared.runtimeStatesBySession.get(id);
3920
+ if (states === void 0) {
3921
+ states = /* @__PURE__ */ new Set();
3922
+ state.shared.runtimeStatesBySession.set(id, states);
3923
+ }
3924
+ states.add(state);
3925
+ }
3926
+ function untrackRuntimeSession(state, agent) {
3927
+ const id = agentSession(agent)?.id;
3928
+ if (typeof id !== "string") return;
3929
+ const states = state.shared.runtimeStatesBySession.get(id);
3930
+ states?.delete(state);
3931
+ if (states?.size === 0) state.shared.runtimeStatesBySession.delete(id);
3932
+ }
3933
+ /** Dispatch or join the one in-flight Pi session_start for this session. */
3934
+ async function startPiSession(ctx, state, agent, reason, signal) {
3935
+ if (!acceptsAgent(state, agent)) return;
3936
+ const existing = inFlightPiSessionStart(state, agent);
3937
+ if (existing !== void 0) {
3938
+ await existing;
3939
+ return;
3940
+ }
3941
+ const task = runInPiRuntime(state, agent, async () => {
3942
+ const previous = state.hostAgent;
3943
+ const replacing = typeof previous === "object" && previous !== null && previous !== agent;
3944
+ const transitionReason = replacing ? replacementReason(reason) : reason;
3945
+ if (replacing && !state.piShutdownAgents.has(previous)) {
3946
+ state.piShutdownAgents.add(previous);
3947
+ releasePiSessionClaim(state, previous);
3948
+ await runInPiRuntime(state, previous, () => dispatch(state, "session_shutdown", {
3949
+ type: "session_shutdown",
3950
+ reason: replacementReason(reason)
3951
+ }, contextFor(ctx, state, previous, signal)));
3952
+ }
3953
+ if (typeof agent === "object" && agent !== null) {
3954
+ state.hostAgent = agent;
3955
+ state.piShutdownAgents.delete(agent);
3956
+ }
3957
+ if (!claimPiSessionStart(state, agent)) return;
3958
+ try {
3959
+ await dispatch(state, "session_start", {
3960
+ type: "session_start",
3961
+ reason: transitionReason
3962
+ }, contextFor(ctx, state, agent, signal));
3963
+ } catch (error) {
3964
+ releasePiSessionClaim(state, agent);
3965
+ throw error;
3966
+ }
3967
+ });
3968
+ rememberPiSessionStart(state, agent, task);
3969
+ try {
3970
+ await task;
3971
+ } finally {
3972
+ forgetPiSessionStart(state, agent, task);
3973
+ }
3974
+ }
3614
3975
  function subscribeLifecycle(ctx, state) {
3615
3976
  const cordis = ctx;
3616
3977
  const warn = (event, error) => logger(ctx).warn(`[pi2dsh] ${event} handler failed: ${String(error)}`);
3617
3978
  cordis.on("agent/session-start", (payload) => {
3618
3979
  const agent = payload.agent;
3619
- if (isSubagentOrigin(agent)) {
3620
- state.activeAgents.add(agent);
3621
- return;
3622
- }
3980
+ if (!acceptsAgent(state, agent)) return;
3623
3981
  state.activeAgents.add(agent);
3982
+ trackRuntimeSession(state, agent);
3624
3983
  const session = agentSession(agent);
3625
3984
  if (session !== void 0) state.bridge.load(session.id);
3626
- if (state.pendingActiveTools !== void 0) state.agentScope.run(agent, () => setActiveTools(ctx, state, state.pendingActiveTools));
3627
- dispatch(state, "session_start", {
3628
- type: "session_start",
3629
- reason: sourceReason(payload.source)
3630
- }, contextFor(ctx, state, agent, void 0)).catch((error) => warn("session_start", error));
3985
+ if (state.pendingActiveTools !== void 0) runInPiRuntime(state, agent, () => setActiveTools(ctx, state, state.pendingActiveTools));
3986
+ if (!state.extensionsReady) {
3987
+ state.pendingSessionStarts.set(agent, sourceReason(payload.source));
3988
+ return;
3989
+ }
3990
+ startPiSession(ctx, state, agent, sourceReason(payload.source), void 0).catch((error) => warn("session_start", error));
3631
3991
  });
3632
3992
  cordis.on("agent/disposed", (payload) => {
3633
3993
  const agent = payload.agent;
3994
+ if (!acceptsAgent(state, agent)) return;
3995
+ const ownsHostSession = state.hostAgent === agent;
3634
3996
  state.activeAgents.delete(agent);
3997
+ untrackRuntimeSession(state, agent);
3998
+ state.pendingSessionStarts.delete(agent);
3635
3999
  if (typeof agent === "object" && agent !== null) {
3636
4000
  state.toolRestrictions.get(agent)?.();
3637
4001
  state.toolRestrictions.delete(agent);
3638
4002
  }
3639
4003
  if (typeof agent === "object" && agent !== null && !state.disposedAgents.has(agent)) {
3640
4004
  state.disposedAgents.add(agent);
3641
- if (!isSubagentOrigin(agent)) dispatch(state, "session_shutdown", {
3642
- type: "session_shutdown",
3643
- reason: "quit"
3644
- }, contextFor(ctx, state, agent, void 0)).catch((error) => warn("session_shutdown", error));
4005
+ releasePiSessionClaim(state, agent);
4006
+ if (ownsHostSession && !state.piShutdownAgents.has(agent)) {
4007
+ state.piShutdownAgents.add(agent);
4008
+ state.hostAgent = void 0;
4009
+ dispatch(state, "session_shutdown", {
4010
+ type: "session_shutdown",
4011
+ reason: "quit"
4012
+ }, contextFor(ctx, state, agent, void 0)).catch((error) => warn("session_shutdown", error));
4013
+ }
3645
4014
  }
3646
4015
  });
3647
4016
  cordis.on("session/event", (session, event) => {
3648
- if (isSubagentOrigin(session)) return;
4017
+ const ownedSession = agentSession(state.ownerAgent);
4018
+ if (ownedSession !== void 0 ? session !== ownedSession : isSubagentOrigin(session)) return;
3649
4019
  const agent = [...state.activeAgents].find((candidate) => candidate.session === session);
3650
4020
  const eventContext = contextFor(ctx, state, agent, void 0);
3651
4021
  const type = event.type;
@@ -3796,7 +4166,7 @@ function subscribeLifecycle(ctx, state) {
3796
4166
  cordis.on("agent/request", async (payload, next) => {
3797
4167
  const config = await next();
3798
4168
  const agent = payload.agent;
3799
- if (agent === void 0) return config;
4169
+ if (agent === void 0 || !acceptsAgent(state, agent)) return config;
3800
4170
  const override = state.modelOverrides.get(agent);
3801
4171
  const thinking = state.thinkingLevels.get(agent);
3802
4172
  if (override === void 0 && thinking === void 0) return config;
@@ -3808,23 +4178,71 @@ function subscribeLifecycle(ctx, state) {
3808
4178
  };
3809
4179
  });
3810
4180
  cordis.effect(() => async () => {
3811
- for (const agent of state.activeAgents) if (typeof agent === "object" && agent !== null && !state.disposedAgents.has(agent)) {
4181
+ const agent = state.hostAgent;
4182
+ if (typeof agent === "object" && agent !== null && !state.piShutdownAgents.has(agent)) {
4183
+ state.piShutdownAgents.add(agent);
3812
4184
  state.disposedAgents.add(agent);
3813
4185
  await dispatch(state, "session_shutdown", {
3814
4186
  type: "session_shutdown",
3815
4187
  reason: "quit"
3816
4188
  }, contextFor(ctx, state, agent, void 0));
3817
4189
  }
4190
+ state.hostAgent = void 0;
4191
+ for (const active of state.activeAgents) untrackRuntimeSession(state, active);
3818
4192
  state.activeAgents.clear();
4193
+ state.startedSessions.clear();
4194
+ state.sessionStartTasks.clear();
4195
+ state.pendingSessionStarts.clear();
3819
4196
  for (const dispose of state.toolDisposers.values()) dispose();
3820
4197
  state.toolDisposers.clear();
4198
+ for (const provider of [...state.ownedProviderRoutes]) releaseSharedProviderRoute(state, provider);
3821
4199
  state.eventBus.removeAllListeners();
3822
4200
  }, "pi2dsh session shutdown");
3823
4201
  }
4202
+ async function flushPendingSessionStarts(ctx, state) {
4203
+ state.extensionsReady = true;
4204
+ const pending = [...state.pendingSessionStarts];
4205
+ state.pendingSessionStarts.clear();
4206
+ for (const [agent, reason] of pending) await startPiSession(ctx, state, agent, reason, void 0);
4207
+ }
4208
+ /**
4209
+ * Start freshly reloaded extension handlers in every live Pi host session.
4210
+ *
4211
+ * `ctx.reload()` creates a new extension instance. The durable session has
4212
+ * already been claimed by the previous instance, so the ordinary once-per-
4213
+ * session guard must deliberately NOT suppress this replay: from the new
4214
+ * instance's point of view this is its first `session_start`. The command's
4215
+ * AsyncLocalStorage agent is included because a lean composition can execute
4216
+ * a Pi command without publishing DSH's optional agent/session-start event.
4217
+ */
4218
+ async function restartReloadedPiSessions(ctx, state) {
4219
+ state.extensionsReady = true;
4220
+ const pending = new Map(state.pendingSessionStarts);
4221
+ state.pendingSessionStarts.clear();
4222
+ const commandAgent = currentAgent(state);
4223
+ const candidates = [...state.activeAgents, ...commandAgent === void 0 ? [] : [commandAgent]];
4224
+ const seenSessions = /* @__PURE__ */ new Set();
4225
+ const seenSessionless = /* @__PURE__ */ new Set();
4226
+ for (const agent of candidates) {
4227
+ if (!acceptsAgent(state, agent)) continue;
4228
+ const sessionId = agentSession(agent)?.id;
4229
+ if (typeof sessionId === "string") {
4230
+ if (seenSessions.has(sessionId)) continue;
4231
+ seenSessions.add(sessionId);
4232
+ } else {
4233
+ if (seenSessionless.has(agent)) continue;
4234
+ seenSessionless.add(agent);
4235
+ }
4236
+ await runInPiRuntime(state, agent, () => dispatch(state, "session_start", {
4237
+ type: "session_start",
4238
+ reason: pending.get(agent) ?? "resume"
4239
+ }, contextFor(ctx, state, agent, void 0)));
4240
+ }
4241
+ }
3824
4242
  function subscribeInterceptors(ctx, state) {
3825
4243
  const cordis = ctx;
3826
4244
  cordis.on("tools/pre-execute", async (exec, next) => {
3827
- if (isSubagentOrigin(exec.agent)) return next();
4245
+ if (!acceptsAgent(state, exec.agent)) return next();
3828
4246
  const input = cloneJson(exec.arguments);
3829
4247
  const event = {
3830
4248
  type: "tool_call",
@@ -3862,7 +4280,7 @@ function subscribeInterceptors(ctx, state) {
3862
4280
  return next();
3863
4281
  });
3864
4282
  cordis.on("tools/post-execute", async (exec, result, next) => {
3865
- if (isSubagentOrigin(exec.agent)) return next();
4283
+ if (!acceptsAgent(state, exec.agent)) return next();
3866
4284
  const downstream = await next();
3867
4285
  await dispatch(state, "tool_execution_end", {
3868
4286
  type: "tool_execution_end",
@@ -3904,7 +4322,7 @@ function subscribeInterceptors(ctx, state) {
3904
4322
  });
3905
4323
  cordis.on("agent/inbox/claimed", (payload) => {
3906
4324
  const agent = payload.agent;
3907
- if (agent === void 0 || isSubagentOrigin(agent)) return;
4325
+ if (agent === void 0 || !acceptsAgent(state, agent)) return;
3908
4326
  const claimed = state.claimedForStep.get(agent) ?? [];
3909
4327
  claimed.push({
3910
4328
  message: payload.message,
@@ -3914,7 +4332,7 @@ function subscribeInterceptors(ctx, state) {
3914
4332
  });
3915
4333
  cordis.on("agent/pre-step", async (payload, next) => {
3916
4334
  const agent = payload.agent;
3917
- if (isSubagentOrigin(agent)) return next();
4335
+ if (!acceptsAgent(state, agent)) return next();
3918
4336
  const tally = state.terminateBatch.get(agent);
3919
4337
  state.terminateBatch.delete(agent);
3920
4338
  if (tally !== void 0 && tally.calls > 0 && tally.calls === tally.terminating) {
@@ -3946,7 +4364,8 @@ function subscribeInterceptors(ctx, state) {
3946
4364
  const original = renderPrompt(downstream);
3947
4365
  state.currentSystemPrompt = original;
3948
4366
  const agent = assembleContext.agent ?? assembleContext.scope;
3949
- if (agent !== void 0 && !isSubagentOrigin(agent)) {
4367
+ if (agent !== void 0 && !acceptsAgent(state, agent)) return downstream;
4368
+ if (agent !== void 0 && acceptsAgent(state, agent)) {
3950
4369
  await runBeforeAgentStart(ctx, state, agent, assembleContext.signal, original);
3951
4370
  const override = state.turnSystemPromptOverrides.get(agent);
3952
4371
  if (override === void 0) return downstream;
@@ -4154,6 +4573,9 @@ function sharedHostStateOf(ctx) {
4154
4573
  shared = {
4155
4574
  companionRoutes: /* @__PURE__ */ new Map(),
4156
4575
  providerRouteDisposers: /* @__PURE__ */ new Map(),
4576
+ providerModelDiscoveries: /* @__PURE__ */ new Map(),
4577
+ providerRouteOwners: /* @__PURE__ */ new Map(),
4578
+ runtimeStatesBySession: /* @__PURE__ */ new Map(),
4157
4579
  publishedOAuthKeys: /* @__PURE__ */ new Map(),
4158
4580
  providers: /* @__PURE__ */ new Map()
4159
4581
  };
@@ -4161,6 +4583,17 @@ function sharedHostStateOf(ctx) {
4161
4583
  }
4162
4584
  return shared;
4163
4585
  }
4586
+ function packageRemountsOf(ctx, shared) {
4587
+ const scoped = scopeOf(ctx);
4588
+ const owner = typeof scoped === "object" && scoped !== null ? scoped : ctx;
4589
+ const byOwner = shared.packageRemounts ??= /* @__PURE__ */ new WeakMap();
4590
+ let remounts = byOwner.get(owner);
4591
+ if (remounts === void 0) {
4592
+ remounts = /* @__PURE__ */ new Map();
4593
+ byOwner.set(owner, remounts);
4594
+ }
4595
+ return remounts;
4596
+ }
4164
4597
  /**
4165
4598
  * Image-admission companion routes: for every text-only route in the DSH
4166
4599
  * llm directory, a `<route>-vision` route that admits images at the host's
@@ -4285,11 +4718,15 @@ async function materializeAttachmentImage(ctx, attachment) {
4285
4718
  }
4286
4719
  }
4287
4720
  function ensureLoginCommand(ctx, state) {
4288
- if (state.shared.loginRegistered === true) return;
4289
- state.shared.loginRegistered = true;
4721
+ const scoped = scopeOf(ctx);
4722
+ const owner = typeof scoped === "object" && scoped !== null ? scoped : ctx;
4723
+ const registered = state.shared.loginRegistered ??= /* @__PURE__ */ new WeakSet();
4724
+ if (registered.has(owner)) return;
4725
+ registered.add(owner);
4290
4726
  try {
4291
4727
  registerLoginCommand(ctx, state);
4292
4728
  } catch (error) {
4729
+ registered.delete(owner);
4293
4730
  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
4731
  }
4295
4732
  }
@@ -4432,7 +4869,7 @@ function keepOAuthCredentialFresh(ctx, state) {
4432
4869
  state.shared.oauthRefreshHooked = true;
4433
4870
  ctx.on("llm/stream", ((options, next) => {
4434
4871
  const provider = typeof options.provider === "string" ? options.provider : void 0;
4435
- const config = provider === void 0 ? void 0 : state.providers.get(provider);
4872
+ const config = provider === void 0 ? void 0 : state.shared.providers.get(provider);
4436
4873
  if (provider === void 0 || config === void 0 || !providerSupportsOAuth(config)) return next();
4437
4874
  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
4875
  return (async function* () {
@@ -4461,37 +4898,47 @@ function keepOAuthCredentialFresh(ctx, state) {
4461
4898
  async function discoverProviderModels(ctx, state, name, value) {
4462
4899
  const refreshModels = value.refreshModels;
4463
4900
  if (typeof refreshModels !== "function") return void 0;
4901
+ const existing = state.shared.providerModelDiscoveries.get(name);
4902
+ if (existing !== void 0) return await existing;
4903
+ const task = (async () => {
4904
+ try {
4905
+ const apiKey = ((await resolvePiProviderAuth({
4906
+ providerId: name,
4907
+ providerConfig: value,
4908
+ store: oauthStoreOf(state)
4909
+ }).catch(() => void 0))?.auth)?.apiKey;
4910
+ await Promise.resolve(refreshModels.call(value, {
4911
+ stored: void 0,
4912
+ ...apiKey === void 0 ? {} : { credential: {
4913
+ type: "api_key",
4914
+ key: apiKey
4915
+ } },
4916
+ store: {
4917
+ read: async () => void 0,
4918
+ write: async () => {},
4919
+ delete: async () => {}
4920
+ },
4921
+ allowNetwork: true,
4922
+ signal: new AbortController().signal,
4923
+ publish: async (publication) => {
4924
+ publication.update?.();
4925
+ return true;
4926
+ }
4927
+ }));
4928
+ } catch (error) {
4929
+ 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)}`);
4930
+ return;
4931
+ }
4932
+ state.modelCatalog?.refresh();
4933
+ state.providerRouteDisposers.get(name)?.reannounce?.();
4934
+ return await llmOf(ctx)?.listModels(name).then((models) => models.length).catch(() => void 0);
4935
+ })();
4936
+ state.shared.providerModelDiscoveries.set(name, task);
4464
4937
  try {
4465
- const apiKey = ((await resolvePiProviderAuth({
4466
- providerId: name,
4467
- providerConfig: value,
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;
4938
+ return await task;
4939
+ } finally {
4940
+ if (state.shared.providerModelDiscoveries.get(name) === task) state.shared.providerModelDiscoveries.delete(name);
4491
4941
  }
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
4942
  }
4496
4943
  function registerLoginCommand(ctx, state) {
4497
4944
  registerCommand(ctx, state, {
@@ -4576,6 +5023,8 @@ function registerTool(ctx, state, tool) {
4576
5023
  isConcurrencySafe: () => tool.executionMode === "parallel",
4577
5024
  async execute(args, exec) {
4578
5025
  const live = state.tools.get(tool.name) ?? tool;
5026
+ const agent = exec.agent;
5027
+ await ensurePiSessionStarted(ctx, state, agent, exec.signal);
4579
5028
  const mutated = state.argMutations.get(exec);
4580
5029
  if (mutated !== void 0) state.argMutations.delete(exec);
4581
5030
  const effective = mutated ?? args;
@@ -4586,8 +5035,7 @@ function registerTool(ctx, state, tool) {
4586
5035
  name: live.name,
4587
5036
  arguments: live.prepareArguments?.(cloneJson(effective)) ?? effective
4588
5037
  });
4589
- const agent = exec.agent;
4590
- const result = await normalizeToolResultForDsh(ctx, await state.agentScope.run(agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
5038
+ const result = await normalizeToolResultForDsh(ctx, await runInPiRuntime(state, agent, () => live.execute(String(exec.callId), prepared, exec.signal, (update) => {
4591
5039
  dispatch(state, "tool_execution_update", {
4592
5040
  type: "tool_execution_update",
4593
5041
  toolCallId: String(exec.callId),
@@ -4621,6 +5069,11 @@ function currentAgent(state) {
4621
5069
  if (scoped !== void 0) return scoped;
4622
5070
  if (state.activeAgents.size === 1) return state.activeAgents.values().next().value;
4623
5071
  }
5072
+ /** Exact-Agent ownership for scoped mounts; legacy root mounts retain their old host-session rule. */
5073
+ function acceptsAgent(state, agent) {
5074
+ if (state.ownerAgent !== void 0) return agent === state.ownerAgent;
5075
+ return !isSubagentOrigin(agent);
5076
+ }
4624
5077
  function toolRuntime(ctx, agent) {
4625
5078
  return (agent?.ctx)?.tools ?? ctx.tools;
4626
5079
  }
@@ -4845,10 +5298,16 @@ async function executePiCommand(service, cwd, command, args, options) {
4845
5298
  operation.cleanup();
4846
5299
  }
4847
5300
  }
4848
- function dshCommandName(ctx, piName) {
5301
+ function normalizedPiCommandName(piName) {
4849
5302
  const normalized = piName.toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^[^a-z]+/u, "");
4850
- const name = normalized.length > 0 ? normalized : "pi-command";
4851
- if (name !== piName) logger(ctx).warn(`[pi2dsh] Pi command /${piName} registered as /${name} to satisfy DSH command naming`);
5303
+ return normalized.length > 0 ? normalized : "pi-command";
5304
+ }
5305
+ function dshCommandName(ctx, state, piName) {
5306
+ const name = commandNameForDshTui(normalizedPiCommandName(piName), state.tuiSurfaces?.available === true || optionalService(ctx, "tuiScenes") !== void 0);
5307
+ if (name !== piName) {
5308
+ const reason = name === "pi-mcp" ? "because dsh-TUI reserves /mcp for its native MCP status view" : "to satisfy DSH command naming";
5309
+ logger(ctx).warn(`[pi2dsh] Pi command /${piName} registered as /${name} ${reason}`);
5310
+ }
4852
5311
  return name;
4853
5312
  }
4854
5313
  function registerCommand(ctx, state, command) {
@@ -4862,7 +5321,13 @@ function registerCommand(ctx, state, command) {
4862
5321
  logger(ctx).warn(`[pi2dsh] command /${command.name} was not registered because this DSH composition has no ctx.commands`);
4863
5322
  return;
4864
5323
  }
4865
- const baseName = dshCommandName(ctx, command.name);
5324
+ const baseName = dshCommandName(ctx, state, command.name);
5325
+ const existingOwner = state.dshCommandOwners.get(baseName);
5326
+ if (existingOwner !== void 0 && existingOwner !== command.name && normalizedPiCommandName(command.name) === baseName && normalizedPiCommandName(existingOwner) !== baseName) {
5327
+ state.commandDisposers.get(existingOwner)?.();
5328
+ state.commandDisposers.delete(existingOwner);
5329
+ state.dshCommandOwners.delete(baseName);
5330
+ }
4866
5331
  const definitionFor = (dshName) => ({
4867
5332
  name: dshName,
4868
5333
  description: command.description || `Migrated Pi command /${command.name}`,
@@ -4871,7 +5336,10 @@ function registerCommand(ctx, state, command) {
4871
5336
  const agent = invocation.agent;
4872
5337
  const commandContext = contextFor(ctx, state, agent, invocation.signal, true);
4873
5338
  const live = state.commands.get(command.name) ?? command;
4874
- await state.agentScope.run(agent, () => live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext));
5339
+ await runInPiRuntime(state, agent, async () => {
5340
+ await ensurePiSessionStarted(ctx, state, agent, invocation.signal);
5341
+ await live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext);
5342
+ });
4875
5343
  const notices = commandContext.__notices;
4876
5344
  return {
4877
5345
  kind: "success",
@@ -4884,6 +5352,7 @@ function registerCommand(ctx, state, command) {
4884
5352
  const dshName = ordinal === 1 ? baseName : `${baseName}-${ordinal}`;
4885
5353
  try {
4886
5354
  state.commandDisposers.set(command.name, commands.register(definitionFor(dshName)));
5355
+ state.dshCommandOwners.set(dshName, command.name);
4887
5356
  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
5357
  return;
4889
5358
  } catch (error) {
@@ -4942,6 +5411,56 @@ function requireSession(state, operation) {
4942
5411
  if (session === void 0) throw new Error(`pi2dsh: ${operation} requires one active DSH agent with a durable session`);
4943
5412
  return session;
4944
5413
  }
5414
+ function releaseSharedProviderRoute(state, name) {
5415
+ if (!state.ownedProviderRoutes.delete(name)) return;
5416
+ const owners = state.shared.providerRouteOwners.get(name);
5417
+ owners?.delete(state);
5418
+ if (owners !== void 0 && owners.size > 0) return;
5419
+ state.shared.providerRouteOwners.delete(name);
5420
+ const route = state.shared.providerRouteDisposers.get(name);
5421
+ state.shared.providerRouteDisposers.delete(name);
5422
+ route?.();
5423
+ }
5424
+ function registerSharedProviderRoute(ctx, state, name, value) {
5425
+ const shared = state.shared;
5426
+ const existing = shared.providerRouteDisposers.get(name);
5427
+ if (existing !== void 0) {
5428
+ let owners = shared.providerRouteOwners.get(name);
5429
+ if (owners === void 0) {
5430
+ owners = /* @__PURE__ */ new Set();
5431
+ shared.providerRouteOwners.set(name, owners);
5432
+ }
5433
+ owners.add(state);
5434
+ state.ownedProviderRoutes.add(name);
5435
+ return existing;
5436
+ }
5437
+ const canonical = shared.providers.get(name) ?? value;
5438
+ shared.providers.set(name, canonical);
5439
+ const route = registerPiProviderRoute({
5440
+ llm: llmOf(ctx),
5441
+ ctx,
5442
+ providerId: name,
5443
+ provider: canonical,
5444
+ host: {
5445
+ resolveAuth: async () => resolvePiProviderAuth({
5446
+ providerId: name,
5447
+ providerConfig: canonical,
5448
+ store: oauthStoreOf(state)
5449
+ }),
5450
+ ensureModel: async () => {
5451
+ await discoverProviderModels(ctx, state, name, canonical);
5452
+ },
5453
+ warn: (message) => logger(ctx).warn(message),
5454
+ beforeProviderRequest: (payload, request) => dispatchHostProviderRequest(ctx, shared, state, payload, request),
5455
+ resolveAttachments: () => optionalService(ctx, "attachments")
5456
+ }
5457
+ });
5458
+ if (route === void 0) return void 0;
5459
+ shared.providerRouteDisposers.set(name, route);
5460
+ shared.providerRouteOwners.set(name, /* @__PURE__ */ new Set([state]));
5461
+ state.ownedProviderRoutes.add(name);
5462
+ return route;
5463
+ }
4945
5464
  function createPiApi(ctx, state) {
4946
5465
  return {
4947
5466
  on(event, handler) {
@@ -4971,28 +5490,12 @@ function createPiApi(ctx, state) {
4971
5490
  const name = typeof providerOrName === "string" ? providerOrName : String(providerOrName?.id ?? providerOrName?.name ?? "unnamed");
4972
5491
  const value = typeof providerOrName === "string" ? config ?? {} : providerOrName;
4973
5492
  state.providers.set(name, value);
5493
+ if (!state.shared.providers.has(name)) state.shared.providers.set(name, value);
4974
5494
  if (providerSupportsOAuth(value)) {
4975
5495
  ensureLoginCommand(ctx, state);
4976
5496
  logger(ctx).info(`[pi2dsh] Pi provider ${JSON.stringify(name)} supports OAuth — log in with /login ${name}`);
4977
5497
  }
4978
- const routeDisposer = registerPiProviderRoute({
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);
5498
+ if (registerSharedProviderRoute(ctx, state, name, value) !== void 0) {
4996
5499
  state.modelCatalog?.refresh();
4997
5500
  if (providerCarriesTransport(value)) logger(ctx).info(`[pi2dsh] Pi provider ${JSON.stringify(name)} registered as a native DSH llm route`);
4998
5501
  } else if (!providerSupportsOAuth(value)) logger(ctx).info(`[pi2dsh] recorded Pi provider ${JSON.stringify(name)}; model calls stay on DSH llm adapters`);
@@ -5000,11 +5503,7 @@ function createPiApi(ctx, state) {
5000
5503
  },
5001
5504
  unregisterProvider(name) {
5002
5505
  state.providers.delete(name);
5003
- const routeDisposer = state.providerRouteDisposers.get(name);
5004
- if (routeDisposer !== void 0) {
5005
- state.providerRouteDisposers.delete(name);
5006
- routeDisposer();
5007
- }
5506
+ releaseSharedProviderRoute(state, name);
5008
5507
  },
5009
5508
  registerMessageRenderer(customType, renderer) {
5010
5509
  state.messageRenderers.set(customType, renderer);
@@ -5200,15 +5699,10 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabi
5200
5699
  resolveShim("pi-ai")
5201
5700
  ]);
5202
5701
  const aliases = {};
5702
+ const require = createRequire(import.meta.url);
5203
5703
  for (const family of ["@earendil-works", "@mariozechner"]) {
5204
5704
  aliases[`${family}/pi-coding-agent`] = codingAgentShim;
5205
5705
  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
5706
  for (const api of [
5213
5707
  "anthropic-messages",
5214
5708
  "openai-completions",
@@ -5220,9 +5714,19 @@ async function loadExtensions(rootDir, manifest, api, onExtensionError, onCapabi
5220
5714
  "mistral-conversations",
5221
5715
  "bedrock-converse-stream",
5222
5716
  "pi-messages"
5223
- ]) aliases[`${family}/pi-ai/api/${api}`] = aiShim;
5717
+ ]) {
5718
+ try {
5719
+ aliases[`${family}/pi-ai/api/${api}.lazy`] = fileURLToPath(import.meta.resolve(`@earendil-works/pi-ai/api/${api}.lazy`));
5720
+ } catch {}
5721
+ aliases[`${family}/pi-ai/api/${api}`] = aiShim;
5722
+ }
5723
+ aliases[`${family}/pi-ai/compat`] = aiShim;
5724
+ aliases[`${family}/pi-ai/oauth`] = aiShim;
5725
+ aliases[`${family}/pi-ai/providers/all`] = aiShim;
5726
+ aliases[`${family}/pi-ai/bedrock-provider`] = aiShim;
5727
+ aliases[`${family}/pi-ai/bun-oauth`] = aiShim;
5728
+ aliases[`${family}/pi-ai`] = aiShim;
5224
5729
  }
5225
- const require = createRequire(import.meta.url);
5226
5730
  for (const entry of [
5227
5731
  "typebox",
5228
5732
  "typebox/value",
@@ -5272,9 +5776,12 @@ async function applyPiPackage(ctx, options) {
5272
5776
  }
5273
5777
  }));
5274
5778
  const shared = sharedHostStateOf(ctx);
5779
+ const scopedOwner = options.ownerAgent ?? scopeOf(ctx);
5780
+ const ownerAgent = typeof scopedOwner === "object" && scopedOwner !== null ? scopedOwner : void 0;
5275
5781
  const state = {
5276
5782
  shared,
5277
5783
  packageName: options.manifest.package.name,
5784
+ ownerAgent,
5278
5785
  handlers: /* @__PURE__ */ new Map(),
5279
5786
  tools: runtimeTools,
5280
5787
  runner: new ExtensionRunner(piToolRecords),
@@ -5282,21 +5789,33 @@ async function applyPiPackage(ctx, options) {
5282
5789
  toolRestrictions: /* @__PURE__ */ new WeakMap(),
5283
5790
  commands: /* @__PURE__ */ new Map(),
5284
5791
  commandDisposers: /* @__PURE__ */ new Map(),
5792
+ dshCommandOwners: /* @__PURE__ */ new Map(),
5285
5793
  companionRoutes: shared.companionRoutes,
5286
5794
  flags: /* @__PURE__ */ new Map(),
5287
5795
  notifications: [],
5288
5796
  activeAgents: /* @__PURE__ */ new Set(),
5797
+ hostAgent: void 0,
5798
+ piShutdownAgents: /* @__PURE__ */ new WeakSet(),
5289
5799
  disposedAgents: /* @__PURE__ */ new WeakSet(),
5800
+ startedSessions: /* @__PURE__ */ new Set(),
5801
+ sessionStartTasks: /* @__PURE__ */ new Map(),
5802
+ startedSessionlessAgents: /* @__PURE__ */ new WeakSet(),
5803
+ sessionlessStartTasks: /* @__PURE__ */ new WeakMap(),
5804
+ extensionsReady: false,
5805
+ pendingSessionStarts: /* @__PURE__ */ new Map(),
5290
5806
  currentSystemPrompt: "",
5291
5807
  messageSource: `pi2dsh:${options.manifest.package.name}`,
5292
5808
  eventBus: new EventEmitter(),
5293
5809
  agentScope: new AsyncLocalStorage(),
5810
+ llmBridge: void 0,
5811
+ piAiRegistry: __createPiAiRuntimeRegistry(),
5812
+ subagentSessionFactory: void 0,
5294
5813
  bridge: new PiSessionBridge(),
5295
5814
  theme: new Theme(),
5296
5815
  shortcuts: /* @__PURE__ */ new Map(),
5297
5816
  messageRenderers: /* @__PURE__ */ new Map(),
5298
5817
  entryRenderers: /* @__PURE__ */ new Map(),
5299
- providers: shared.providers,
5818
+ providers: /* @__PURE__ */ new Map(),
5300
5819
  autocompleteProviders: [],
5301
5820
  editorBuffers: /* @__PURE__ */ new WeakMap(),
5302
5821
  toolsExpanded: false,
@@ -5314,15 +5833,24 @@ async function applyPiPackage(ctx, options) {
5314
5833
  streamingTexts: /* @__PURE__ */ new Map(),
5315
5834
  lastLoggedModels: /* @__PURE__ */ new WeakMap(),
5316
5835
  providerRouteDisposers: shared.providerRouteDisposers,
5317
- publishedOAuthKeys: shared.publishedOAuthKeys
5836
+ ownedProviderRoutes: /* @__PURE__ */ new Set(),
5837
+ publishedOAuthKeys: shared.publishedOAuthKeys,
5838
+ tuiSurfaces: void 0
5318
5839
  };
5840
+ mountTuiSurfaceAdapter(ctx, state.packageName, (adapter) => {
5841
+ state.tuiSurfaces = adapter;
5842
+ }, typeof ownerAgent?.id === "string" ? ownerAgent.id : void 0);
5319
5843
  subscribeLifecycle(ctx, state);
5320
5844
  subscribeInterceptors(ctx, state);
5321
- for (const provider of builtinProviders()) state.providers.set(provider.id, {
5322
- name: provider.name,
5323
- baseUrl: provider.baseUrl,
5324
- oauth: provider.auth.oauth
5325
- });
5845
+ for (const provider of builtinProviders()) {
5846
+ const config = {
5847
+ name: provider.name,
5848
+ baseUrl: provider.baseUrl,
5849
+ oauth: provider.auth.oauth
5850
+ };
5851
+ state.providers.set(provider.id, config);
5852
+ if (!shared.providers.has(provider.id)) shared.providers.set(provider.id, config);
5853
+ }
5326
5854
  ensureLoginCommand(ctx, state);
5327
5855
  for (const [name, config] of [...state.providers]) try {
5328
5856
  await ensureLoggedInProviderRoute(ctx, state, name, config);
@@ -5333,12 +5861,13 @@ async function applyPiPackage(ctx, options) {
5333
5861
  if (ctx.get("skills") === void 0) logger(ctx).warn("[pi2dsh] migrated skills were not mounted because this DSH composition has no ctx.skills");
5334
5862
  else {
5335
5863
  const { apply: applyFilesystemSkills } = await import("@deepseek-ai/dsh-skill-filesystem");
5336
- applyFilesystemSkills(ctx, {
5864
+ const config = {
5337
5865
  providerName: `pi2dsh-${options.manifest.package.name.replace(/[^a-zA-Z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "")}`,
5338
5866
  includeDefaultRoots: false,
5339
5867
  customSkillDirs: options.manifest.skillDirs.map((path) => join(rootDir, path)),
5340
5868
  watch: false
5341
- });
5869
+ };
5870
+ await ctx.plugin(Object.assign((skillCtx) => applyFilesystemSkills(skillCtx, config), { inject: ["skills"] }));
5342
5871
  }
5343
5872
  }
5344
5873
  const llm = llmOf(ctx);
@@ -5352,12 +5881,11 @@ async function applyPiPackage(ctx, options) {
5352
5881
  });
5353
5882
  }
5354
5883
  await state.modelCatalog.refresh();
5355
- __setPiAiLlmBridge((model, context, callOptions) => streamViaDshLlm(llm, {
5884
+ state.llmBridge = (model, context, callOptions) => streamViaDshLlm(llm, {
5356
5885
  model,
5357
5886
  context,
5358
5887
  options: callOptions
5359
- }));
5360
- ctx.effect(() => () => __setPiAiLlmBridge(void 0));
5888
+ });
5361
5889
  }
5362
5890
  const browserSurfaces = state.shared.browserSurfaces ??= new BrowserSurfaces();
5363
5891
  if (state.shared.browserSurfacesRouted !== true) state.shared.browserSurfacesRouted = registerBrowserSurfaceRoute(ctx, browserSurfaces);
@@ -5416,7 +5944,7 @@ async function applyPiPackage(ctx, options) {
5416
5944
  }
5417
5945
  return rendered;
5418
5946
  }));
5419
- __setSubagentSessionFactory(async (subagentOptions) => {
5947
+ state.subagentSessionFactory = async (subagentOptions) => {
5420
5948
  const created = await createBridgedAgentSession(subagentHost(), subagentOptions);
5421
5949
  const parent = agentSession(currentAgent(state));
5422
5950
  const parentId = parent === void 0 ? "" : String(parent.id ?? "");
@@ -5430,7 +5958,7 @@ async function applyPiPackage(ctx, options) {
5430
5958
  ctx.effect(() => dispose);
5431
5959
  }
5432
5960
  return created;
5433
- });
5961
+ };
5434
5962
  const subagentHost = () => ({
5435
5963
  cordis: ctx,
5436
5964
  cwd: () => cwdOf(currentAgent(state)),
@@ -5451,7 +5979,6 @@ async function applyPiPackage(ctx, options) {
5451
5979
  messageSource: state.messageSource,
5452
5980
  packageName: state.packageName
5453
5981
  });
5454
- ctx.effect(() => () => __setSubagentSessionFactory(void 0));
5455
5982
  await registerPromptCommands(ctx, state, rootDir, options.manifest);
5456
5983
  const onExtensionError = (failure) => logger(ctx).warn(`[pi2dsh] extension entry failed and was skipped (matching Pi's per-extension error isolation): ${failure}`);
5457
5984
  const onCapabilityGap = (gap) => capabilityLedgerOf(ctx, state).reportUnusable({
@@ -5466,14 +5993,21 @@ async function applyPiPackage(ctx, options) {
5466
5993
  guidance: "",
5467
5994
  packageName: state.packageName
5468
5995
  });
5469
- const mountExtensions = () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference);
5996
+ const mountExtensions = () => runInPiRuntime(state, currentAgent(state), () => loadExtensions(rootDir, options.manifest, createPiApi(ctx, state), onExtensionError, onCapabilityGap, onHostInfraReference));
5470
5997
  await mountExtensions();
5471
- const remounts = shared.packageRemounts ??= /* @__PURE__ */ new Map();
5998
+ await flushPendingSessionStarts(ctx, state);
5999
+ const remounts = packageRemountsOf(ctx, shared);
5472
6000
  remounts.set(state.packageName, async () => {
6001
+ state.extensionsReady = false;
5473
6002
  state.handlers.clear();
5474
6003
  state.eventBus.removeAllListeners();
5475
- await registerPromptCommands(ctx, state, rootDir, options.manifest);
5476
- await mountExtensions();
6004
+ try {
6005
+ await registerPromptCommands(ctx, state, rootDir, options.manifest);
6006
+ await mountExtensions();
6007
+ await restartReloadedPiSessions(ctx, state);
6008
+ } finally {
6009
+ state.extensionsReady = true;
6010
+ }
5477
6011
  });
5478
6012
  ctx.effect(() => () => {
5479
6013
  remounts.delete(state.packageName);
@@ -5497,4 +6031,4 @@ const runtimeInternals = {
5497
6031
  //#endregion
5498
6032
  export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
5499
6033
 
5500
- //# sourceMappingURL=runtime-DEjs_Qlf.mjs.map
6034
+ //# sourceMappingURL=runtime-Dd2KhoX-.mjs.map