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.
Files changed (106) hide show
  1. package/README.md +24 -19
  2. package/README.zh.md +20 -15
  3. package/dist/{analyzer-CJFwQlSG.mjs → analyzer-DPZ8LzSt.mjs} +2 -2
  4. package/dist/{analyzer-CJFwQlSG.mjs.map → analyzer-DPZ8LzSt.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-CP2tpyhg.mjs} +20 -185
  16. package/dist/compatibility-CP2tpyhg.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-Didpkkjx.mjs} +3 -3
  20. package/dist/{credentials-oauth-Txs6oew8.mjs.map → credentials-oauth-Didpkkjx.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 +14 -2
  47. package/dist/host.d.mts.map +1 -1
  48. package/dist/host.mjs +44 -5
  49. package/dist/host.mjs.map +1 -1
  50. package/dist/index.d.mts +35 -3
  51. package/dist/index.d.mts.map +1 -1
  52. package/dist/index.mjs +128 -22
  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-DpqiLdPA.mjs +3244 -0
  77. package/dist/pi-ai-DpqiLdPA.mjs.map +1 -0
  78. package/dist/pi-ai-ZU5MelXy.d.mts +2678 -0
  79. package/dist/pi-ai-ZU5MelXy.d.mts.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-DK8hbJP5.mjs} +0 -0
  83. package/dist/{pi-coding-agent-BVZu0dHG.mjs.map → pi-coding-agent-DK8hbJP5.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-Bb9Bfk4M.mjs} +715 -130
  92. package/dist/runtime-Bb9Bfk4M.mjs.map +1 -0
  93. package/dist/runtime.d.mts +149 -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 +9 -7
  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-DK8hbJP5.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-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
- const model = providerModels(provider).find((entry) => entry.id === modelId) ?? {
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
- const surfaces = state.shared.browserSurfaces;
3046
- if (surfaces === void 0) return;
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 () => void 0,
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: "rpc",
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.packageRemounts;
3465
- if (remounts === void 0 || remounts.size === 0) return;
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.agentScope.run(agent, () => handler(event, eventContext)));
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.agentScope.run(agent, () => handler({
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 (isSubagentOrigin(agent)) {
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.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));
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
- 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));
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
- if (isSubagentOrigin(session)) return;
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
- for (const agent of state.activeAgents) if (typeof agent === "object" && agent !== null && !state.disposedAgents.has(agent)) {
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 (isSubagentOrigin(exec.agent)) return next();
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 (isSubagentOrigin(exec.agent)) return next();
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 || isSubagentOrigin(agent)) return;
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 (isSubagentOrigin(agent)) return next();
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 && !isSubagentOrigin(agent)) {
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
- if (state.shared.loginRegistered === true) return;
4289
- state.shared.loginRegistered = true;
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
- 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;
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 agent = exec.agent;
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 dshCommandName(ctx, piName) {
5338
+ function normalizedPiCommandName(piName) {
4849
5339
  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`);
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.agentScope.run(agent, () => live.handler(String(invocation.rawInput ?? "").trimStart(), commandContext));
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
- 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);
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
- const routeDisposer = state.providerRouteDisposers.get(name);
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
- ]) aliases[`${family}/pi-ai/api/${api}`] = aiShim;
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: shared.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
- publishedOAuthKeys: shared.publishedOAuthKeys
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()) state.providers.set(provider.id, {
5322
- name: provider.name,
5323
- baseUrl: provider.baseUrl,
5324
- oauth: provider.auth.oauth
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
- applyFilesystemSkills(ctx, {
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
- __setPiAiLlmBridge((model, context, callOptions) => streamViaDshLlm(llm, {
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
- __setSubagentSessionFactory(async (subagentOptions) => {
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
- const remounts = shared.packageRemounts ??= /* @__PURE__ */ new Map();
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
- await registerPromptCommands(ctx, state, rootDir, options.manifest);
5476
- await mountExtensions();
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-DEjs_Qlf.mjs.map
6085
+ //# sourceMappingURL=runtime-Bb9Bfk4M.mjs.map