pi2dsh 0.25.2 → 0.26.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.
@@ -17,7 +17,7 @@ import { EventEmitter } from "node:events";
17
17
  import { AsyncLocalStorage } from "node:async_hooks";
18
18
  import { createJiti } from "jiti";
19
19
  import * as llm from "@deepseek-ai/dsh-llm";
20
- import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, QUOTA_EXCEEDED_CODE, createUserMessage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
20
+ import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, QUOTA_EXCEEDED_CODE, ToolCallId, createToolResultMessage, createUserMessage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
21
21
  import { scopeOf } from "@deepseek-ai/dsh-scope";
22
22
  import { renderPrompt } from "@deepseek-ai/dsh-system-prompt";
23
23
  import { foldSurface } from "@deepseek-ai/dsh-session";
@@ -626,7 +626,6 @@ var PiSessionBridge = class {
626
626
  });
627
627
  } else if (type === "tool/result") {
628
628
  const message = data.message ?? {};
629
- const tool = (Array.isArray(message.content) ? message.content : []).find((block) => block.type === "tool-result");
630
629
  merged.push({
631
630
  time,
632
631
  entry: {
@@ -635,9 +634,9 @@ var PiSessionBridge = class {
635
634
  timestamp: new Date(time).toISOString(),
636
635
  message: {
637
636
  role: "toolResult",
638
- toolCallId: tool?.toolCallId,
639
- content: dshToPiContent$1(tool?.content ?? []),
640
- isError: tool?.isError === true
637
+ toolCallId: message.toolCallId,
638
+ content: dshToPiContent$1(message.content ?? []),
639
+ isError: message.isError === true
641
640
  }
642
641
  }
643
642
  });
@@ -834,6 +833,9 @@ var PiBridgedAgentSession = class {
834
833
  #aborted = false;
835
834
  #liveStreamAttempt;
836
835
  #streamingText = "";
836
+ #turnAssistant;
837
+ #pendingPrompt;
838
+ #promptActive = false;
837
839
  constructor(host, handle, tools, thinkingLevel) {
838
840
  this.#thinkingLevel = typeof thinkingLevel === "string" && thinkingLevel.length > 0 ? thinkingLevel : "off";
839
841
  this.#host = host;
@@ -962,35 +964,30 @@ var PiBridgedAgentSession = class {
962
964
  });
963
965
  }
964
966
  if (type === "request/header") {
965
- const config = event.data.config ?? {};
967
+ const config = event.data.header?.config ?? {};
966
968
  if (typeof config.model === "string") this.#model = {
967
969
  id: config.model,
968
970
  provider: String(config.provider ?? "")
969
971
  };
970
972
  }
971
973
  if (type === "tools/result" || type === "tool/result") {
972
- const data = event.data;
973
- const message = data.message;
974
- const blocks = Array.isArray(message?.content) ? message.content : [];
975
- for (const block of blocks) {
976
- const record = block;
977
- if (record?.type !== "tool-result") continue;
978
- const callId = String(record.toolCallId ?? (message?.source)?.callId ?? data.callId ?? "");
974
+ const message = event.data.message;
975
+ if (message?.role === "tool") {
976
+ const callId = String(message.toolCallId ?? "");
979
977
  this.#pendingToolCalls.delete(callId);
980
- const isError = record.isError === true;
978
+ const isError = message.isError === true;
981
979
  emit({
982
980
  type: "tool_execution_end",
983
981
  toolCallId: callId,
984
982
  toolName: this.#toolCallNames.get(callId) ?? "",
985
983
  result: {
986
- content: record.content ?? [],
984
+ content: message.content ?? [],
987
985
  isError
988
986
  },
989
987
  isError
990
988
  });
991
989
  this.#toolCallNames.delete(callId);
992
990
  }
993
- if (blocks.length === 0) this.#pendingToolCalls.delete(String(data.callId ?? ""));
994
991
  }
995
992
  if (type === "tool/call") {
996
993
  const data = event.data;
@@ -1037,6 +1034,10 @@ var PiBridgedAgentSession = class {
1037
1034
  if (message === void 0) return;
1038
1035
  if ((event.data?.source)?.form === "snapshot") this.#carried.add(message);
1039
1036
  this.messages.push(message);
1037
+ if (type === "assistant/message") this.#turnAssistant = {
1038
+ turn: Number(event.data.turn),
1039
+ message
1040
+ };
1040
1041
  emit({
1041
1042
  type: "message_start",
1042
1043
  message
@@ -1051,15 +1052,51 @@ var PiBridgedAgentSession = class {
1051
1052
  this.#streaming = false;
1052
1053
  this.#pendingToolCalls.clear();
1053
1054
  this.#messageProjection = this.#messageProjection.then(() => {
1055
+ const data = event.data;
1056
+ const turn = Number(data.turn);
1057
+ const reason = data.reason;
1058
+ const stopReason = reason?.kind === "error" || reason?.kind === "blocked" ? "error" : reason?.kind === "aborted" ? "aborted" : reason?.kind === "max-tokens" ? "length" : "stop";
1059
+ let message = this.#turnAssistant?.turn === turn ? this.#turnAssistant.message : void 0;
1060
+ const synthetic = message === void 0 && stopReason !== "stop";
1061
+ if (message === void 0 && stopReason !== "stop") {
1062
+ message = {
1063
+ role: "assistant",
1064
+ content: [],
1065
+ timestamp: Date.now()
1066
+ };
1067
+ this.messages.push(message);
1068
+ }
1069
+ if (message !== void 0) {
1070
+ message.stopReason = stopReason;
1071
+ if (stopReason === "error") {
1072
+ const failure = reason?.error;
1073
+ message.errorMessage = reason?.kind === "blocked" ? "DSH blocked the child turn before completion" : String(failure?.message ?? "DSH child turn failed");
1074
+ }
1075
+ if (synthetic) {
1076
+ emit({
1077
+ type: "message_start",
1078
+ message
1079
+ });
1080
+ emit({
1081
+ type: "message_end",
1082
+ message
1083
+ });
1084
+ }
1085
+ }
1054
1086
  emit({
1055
1087
  type: "turn_end",
1056
- turnIndex: Number(event.data.turn ?? 1) - 1,
1057
- message: {
1088
+ turnIndex: turn - 1,
1089
+ message: message ?? {
1058
1090
  role: "assistant",
1059
- content: []
1091
+ content: [],
1092
+ stopReason
1060
1093
  },
1061
1094
  toolResults: []
1062
1095
  });
1096
+ emit({
1097
+ type: "agent_end",
1098
+ messages: [...this.messages]
1099
+ });
1063
1100
  });
1064
1101
  }
1065
1102
  }
@@ -1068,6 +1105,15 @@ var PiBridgedAgentSession = class {
1068
1105
  return () => this.#subscribers.delete(handler);
1069
1106
  }
1070
1107
  async prompt(text) {
1108
+ if (this.#promptActive) throw new Error("pi2dsh: this child session is already processing a prompt");
1109
+ this.#promptActive = true;
1110
+ try {
1111
+ await this.#runPrompt(text);
1112
+ } finally {
1113
+ this.#promptActive = false;
1114
+ }
1115
+ }
1116
+ async #runPrompt(text) {
1071
1117
  this.#aborted = false;
1072
1118
  if (this.#seed.length > 0) {
1073
1119
  const seeded = this.#seed;
@@ -1085,7 +1131,7 @@ var PiBridgedAgentSession = class {
1085
1131
  this.#host.deliver(this.#handle.agent, createUserMessage({
1086
1132
  content: seedBlocks,
1087
1133
  source: {
1088
- kind: "plugin",
1134
+ kind: "pi2dsh",
1089
1135
  plugin: this.#host.messageSource
1090
1136
  }
1091
1137
  }), "inject");
@@ -1096,26 +1142,43 @@ var PiBridgedAgentSession = class {
1096
1142
  text: String(text)
1097
1143
  }]);
1098
1144
  const cordis = unscopedEventContext(this.#host.cordis);
1099
- const completed = new Promise((resolve) => {
1100
- const off = cordis.on("session/event", (session, event) => {
1101
- if (session !== this.#session) return;
1102
- if (event.type === "turn/end") {
1103
- off();
1104
- resolve();
1105
- }
1106
- });
1107
- this.#disposers.push(off);
1108
- });
1109
- this.#host.deliver(this.#handle.agent, createUserMessage({
1145
+ const message = createUserMessage({
1110
1146
  content: blocks,
1111
1147
  source: {
1112
- kind: "plugin",
1148
+ kind: "pi2dsh",
1113
1149
  plugin: this.#host.messageSource,
1114
1150
  form: "relay"
1115
1151
  }
1116
- }), "followup");
1117
- await completed;
1118
- await this.#messageProjection;
1152
+ });
1153
+ let turn;
1154
+ const offs = [];
1155
+ const cleanup = () => {
1156
+ for (const off of offs.splice(0)) off();
1157
+ };
1158
+ const completed = new Promise((resolve, reject) => {
1159
+ this.#pendingPrompt = {
1160
+ reject,
1161
+ dispose: cleanup
1162
+ };
1163
+ offs.push(cordis.on("agent/inbox/claimed", (payload) => {
1164
+ if (payload.agent === this.#handle.agent && payload.message?.id === message.id) turn = Number(payload.turn);
1165
+ }));
1166
+ offs.push(cordis.on("agent/disposed", (payload) => {
1167
+ if (payload.agent === this.#handle.agent) reject(/* @__PURE__ */ new Error("pi2dsh: child agent was disposed before its prompt completed"));
1168
+ }));
1169
+ offs.push(cordis.on("session/event", (session, event) => {
1170
+ if (session !== this.#session) return;
1171
+ if (event.type === "turn/end" && turn !== void 0 && Number(event.data.turn) === turn) resolve();
1172
+ }));
1173
+ });
1174
+ try {
1175
+ this.#host.deliver(this.#handle.agent, message, "followup");
1176
+ await completed;
1177
+ await this.#messageProjection;
1178
+ } finally {
1179
+ cleanup();
1180
+ this.#pendingPrompt = void 0;
1181
+ }
1119
1182
  }
1120
1183
  async steer(text, images) {
1121
1184
  await this.#queueMessage(text, images, "steer");
@@ -1133,7 +1196,7 @@ var PiBridgedAgentSession = class {
1133
1196
  this.#host.deliver(this.#handle.agent, createUserMessage({
1134
1197
  content: blocks,
1135
1198
  source: {
1136
- kind: "plugin",
1199
+ kind: "pi2dsh",
1137
1200
  plugin: this.#host.messageSource
1138
1201
  }
1139
1202
  }), mode);
@@ -1231,6 +1294,8 @@ var PiBridgedAgentSession = class {
1231
1294
  } catch {}
1232
1295
  }
1233
1296
  async dispose() {
1297
+ this.#pendingPrompt?.reject(/* @__PURE__ */ new Error("pi2dsh: child session was disposed before its prompt completed"));
1298
+ this.#pendingPrompt?.dispose();
1234
1299
  for (const dispose of this.#disposers.splice(0)) try {
1235
1300
  dispose();
1236
1301
  } catch {}
@@ -2403,7 +2468,7 @@ function dshBlocksFromPiContent(content) {
2403
2468
  }
2404
2469
  /**
2405
2470
  * Pi request context → DSH GenerateOptions parts. System prompt moves to the
2406
- * dedicated slot; toolResult messages become DSH tool-result user messages.
2471
+ * dedicated slot; toolResult messages become native DSH tool-role messages.
2407
2472
  */
2408
2473
  function piRequestToDshMessages(context) {
2409
2474
  const messages = [];
@@ -2422,19 +2487,11 @@ function piRequestToDshMessages(context) {
2422
2487
  model: String(message.model ?? context.model ?? "unknown")
2423
2488
  }
2424
2489
  });
2425
- else if (message.role === "toolResult") messages.push({
2426
- role: "user",
2427
- content: [{
2428
- type: "tool-result",
2429
- toolCallId: String(message.toolCallId ?? ""),
2430
- content: dshBlocksFromPiContent(message.content),
2431
- isError: message.isError === true
2432
- }],
2433
- source: {
2434
- kind: "tool",
2435
- callId: String(message.toolCallId ?? "")
2436
- }
2437
- });
2490
+ else if (message.role === "toolResult") messages.push(createToolResultMessage({
2491
+ callId: ToolCallId(String(message.toolCallId ?? "")),
2492
+ content: dshBlocksFromPiContent(message.content),
2493
+ isError: message.isError === true
2494
+ }));
2438
2495
  const system = typeof context.systemPrompt === "string" && context.systemPrompt.length > 0 ? context.systemPrompt : void 0;
2439
2496
  return {
2440
2497
  ...system === void 0 ? {} : { system },
@@ -2712,18 +2769,17 @@ async function dshRequestToPiContext(options, attachments) {
2712
2769
  }
2713
2770
  for (const message of history) {
2714
2771
  const content = Array.isArray(message.content) ? message.content : [];
2715
- const source = message.source;
2716
2772
  if (message.role === "system") throw new Error("pi2dsh: this Pi provider cannot represent an in-history system prompt update");
2717
- else if (message.role === "user" && source?.kind === "tool") {
2718
- const result = content[0];
2719
- const toolCallId = String(result?.toolCallId ?? "");
2773
+ else if (message.role === "developer") throw new LlmError("pi2dsh: this Pi provider cannot represent developer messages", "UNSUPPORTED_CONTENT");
2774
+ else if (message.role === "tool") {
2775
+ const toolCallId = String(message.toolCallId ?? "");
2720
2776
  const toolName = toolNamesById.get(toolCallId);
2721
2777
  piMessages.push({
2722
2778
  role: "toolResult",
2723
2779
  toolCallId,
2724
2780
  ...toolName === void 0 ? {} : { toolName },
2725
- content: await piContentFromDshBlocks(Array.isArray(result?.content) ? result.content : [], attachments),
2726
- isError: result?.isError === true
2781
+ content: await piContentFromDshBlocks(content, attachments),
2782
+ isError: message.isError === true
2727
2783
  });
2728
2784
  } else if (message.role === "user") piMessages.push({
2729
2785
  role: "user",
@@ -3310,6 +3366,7 @@ const DSH_THINKING_FORMATS = /* @__PURE__ */ new Set([
3310
3366
  "deepseek",
3311
3367
  "openrouter",
3312
3368
  "together",
3369
+ "baseten",
3313
3370
  "zai",
3314
3371
  "qwen",
3315
3372
  "chat-template",
@@ -3321,6 +3378,7 @@ const DSH_MAX_TOKENS_FIELDS = /* @__PURE__ */ new Set(["max_completion_tokens",
3321
3378
  const DSH_CACHE_CONTROL_FORMATS = /* @__PURE__ */ new Set(["anthropic"]);
3322
3379
  const DSH_CHAT_TEMPLATE_VARS = /* @__PURE__ */ new Set(["thinking.enabled", "thinking.effort"]);
3323
3380
  const COMPLETIONS = /* @__PURE__ */ new Set(["openai-completions"]);
3381
+ const RESPONSES = /* @__PURE__ */ new Set(["openai-responses"]);
3324
3382
  const ANTHROPIC = /* @__PURE__ */ new Set(["anthropic-messages"]);
3325
3383
  const COMPLETIONS_AND_RESPONSES = /* @__PURE__ */ new Set(["openai-completions", "openai-responses"]);
3326
3384
  const DSH_COMPAT_FIELD_PROTOCOLS = {
@@ -3331,6 +3389,9 @@ const DSH_COMPAT_FIELD_PROTOCOLS = {
3331
3389
  supportsFinishReason: COMPLETIONS,
3332
3390
  chatTemplateArgs: COMPLETIONS,
3333
3391
  supportsThinkingTokenBudget: COMPLETIONS,
3392
+ thinkingTokenBudgetField: COMPLETIONS,
3393
+ vllmPriority: COMPLETIONS,
3394
+ supportsMaxOutputTokens: RESPONSES,
3334
3395
  maxTokensField: COMPLETIONS,
3335
3396
  requiresToolResultName: COMPLETIONS,
3336
3397
  requiresAssistantAfterToolResult: COMPLETIONS,
@@ -3385,9 +3446,13 @@ function compatProfileOf(value, api) {
3385
3446
  const candidate = compat[field];
3386
3447
  if (field === "maxTokensField") {
3387
3448
  if (typeof candidate === "string" && DSH_MAX_TOKENS_FIELDS.has(candidate)) output[field] = candidate;
3449
+ } else if (field === "vllmPriority") {
3450
+ if (typeof candidate === "number" && Number.isSafeInteger(candidate)) output[field] = candidate;
3451
+ } else if (field === "thinkingTokenBudgetField") {
3452
+ if (candidate === "thinking_token_budget" || candidate === "thinking_budget" || candidate === "thinking_budget_tokens") output[field] = candidate;
3388
3453
  } else if (field === "thinkingFormat") {
3389
3454
  if (typeof candidate === "string" && DSH_THINKING_FORMATS.has(candidate)) output[field] = candidate;
3390
- } else if (field === "chatTemplateKwargs") {
3455
+ } else if (field === "chatTemplateKwargs" || field === "chatTemplateArgs") {
3391
3456
  const kwargs = chatTemplateKwargsOf(candidate);
3392
3457
  if (kwargs !== void 0) output[field] = kwargs;
3393
3458
  } else if (field === "cacheControlFormat") {
@@ -4087,12 +4152,16 @@ async function createPiTuiExtensionSurfaceAdapter(ctx, packageName, instanceKey)
4087
4152
  const extensionService = service(ctx);
4088
4153
  if (extensionService === void 0) return void 0;
4089
4154
  const api = extensionService.api();
4090
- if (api.apiVersion !== 1 || !api.capabilities.has("unstable.surface.handle")) {
4155
+ if (api.apiVersion !== 2 || !api.capabilities.has("unstable.surface.handle")) {
4091
4156
  ctx.logger?.warn(`pi2dsh: piTuiExtensions cannot host Pi components (api=${api.apiVersion}, raw surface=${api.capabilities.has("unstable.surface.handle")})`);
4092
4157
  return;
4093
4158
  }
4094
4159
  try {
4095
- const [hostModule, unstableModule] = await Promise.all([import("@xmoon76/dsh-pi-tui"), import("@xmoon76/dsh-pi-tui/extensions/unstable")]);
4160
+ const hostEntry = "@xmoon76/dsh-pi-tui";
4161
+ const unstableEntry = "@xmoon76/dsh-pi-tui/extensions/unstable";
4162
+ const loader = ctx.get("loader", false);
4163
+ const importPublic = (name) => loader === void 0 ? import(name) : loader.import(name);
4164
+ const [hostModule, unstableModule] = await Promise.all([importPublic(hostEntry), importPublic(unstableEntry)]);
4096
4165
  if (unstableModule.UNSTABLE_API_LEVEL !== 1) {
4097
4166
  ctx.logger?.warn(`pi2dsh: unsupported dsh-pi-tui unstable API level ${unstableModule.UNSTABLE_API_LEVEL}`);
4098
4167
  return;
@@ -4744,21 +4813,7 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride,
4744
4813
  ...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
4745
4814
  };
4746
4815
  }
4747
- try {
4748
- const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
4749
- if (entry === void 0) return { ok: false };
4750
- const section = ctx.get("settings")?.get(entry.settingsNs);
4751
- const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
4752
- if (typeof ref !== "string" || ref.length === 0) return { ok: false };
4753
- const resolved = await ctx.get("credentials")?.resolve(ref);
4754
- if (typeof resolved?.value !== "string" || resolved.value.length === 0) return { ok: false };
4755
- return {
4756
- ok: true,
4757
- apiKey: resolved.value
4758
- };
4759
- } catch {
4760
- return { ok: false };
4761
- }
4816
+ return resolveHostModelAuth(ctx, provider);
4762
4817
  },
4763
4818
  getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
4764
4819
  getProvider: (provider) => {
@@ -5216,12 +5271,12 @@ async function messageFromSessionEvent(ctx, event) {
5216
5271
  };
5217
5272
  }
5218
5273
  if (type === "tool/result") {
5219
- const tool = (record.message?.content ?? []).find((block) => block.type === "tool-result");
5274
+ const message = record.message;
5220
5275
  return {
5221
5276
  role: "toolResult",
5222
- toolCallId: tool?.toolCallId,
5223
- content: await dshToPiContent(ctx, tool?.content ?? []),
5224
- isError: tool?.isError === true
5277
+ toolCallId: message?.toolCallId,
5278
+ content: await dshToPiContent(ctx, message?.content ?? []),
5279
+ isError: message?.isError === true
5225
5280
  };
5226
5281
  }
5227
5282
  }
@@ -5231,12 +5286,9 @@ function sourceReason(value) {
5231
5286
  /**
5232
5287
  * Claim Pi's once-per-session `session_start` event.
5233
5288
  *
5234
- * dsh-TUI can execute a slash command before DSH starts the first model turn,
5235
- * so `agent/session-start` has not necessarily been announced yet. Pi, by
5236
- * contrast, announces `session_start` before the user can execute a command.
5237
- * The command bridge uses this same claim before invoking a Pi command; when
5238
- * DSH later announces the real agent lifecycle, the durable session id keeps
5239
- * the second path from restarting the extension.
5289
+ * Normal engine mounts complete inside DSH's serial agent/created event.
5290
+ * The same idempotent claim also protects direct runtime users and reloads:
5291
+ * command/tool entry must not restart an already initialized Pi extension.
5240
5292
  */
5241
5293
  function claimPiSessionStart(state, agent) {
5242
5294
  const id = agentSession(agent)?.id;
@@ -5362,7 +5414,7 @@ async function startPiSession(ctx, state, agent, reason, signal) {
5362
5414
  function subscribeLifecycle(ctx, state) {
5363
5415
  const cordis = ctx;
5364
5416
  const warn = (event, error) => logger(ctx).warn(`[pi2dsh] ${event} handler failed: ${String(error)}`);
5365
- cordis.on("agent/session-start", (payload) => {
5417
+ cordis.on("agent/created", async (payload) => {
5366
5418
  const agent = payload.agent;
5367
5419
  if (!acceptsAgent(state, agent)) return;
5368
5420
  state.activeAgents.add(agent);
@@ -5374,7 +5426,7 @@ function subscribeLifecycle(ctx, state) {
5374
5426
  state.pendingSessionStarts.set(agent, sourceReason(payload.source));
5375
5427
  return;
5376
5428
  }
5377
- startPiSession(ctx, state, agent, sourceReason(payload.source), void 0).catch((error) => warn("session_start", error));
5429
+ await startPiSession(ctx, state, agent, sourceReason(payload.source), payload.signal);
5378
5430
  });
5379
5431
  cordis.on("agent/disposed", (payload) => {
5380
5432
  const agent = payload.agent;
@@ -5668,7 +5720,7 @@ async function flushPendingSessionStarts(ctx, state) {
5668
5720
  * session guard must deliberately NOT suppress this replay: from the new
5669
5721
  * instance's point of view this is its first `session_start`. The command's
5670
5722
  * AsyncLocalStorage agent is included because a lean composition can execute
5671
- * a Pi command without publishing DSH's optional agent/session-start event.
5723
+ * a Pi command through the runtime directly, outside engine-managed creation.
5672
5724
  */
5673
5725
  async function restartReloadedPiSessions(ctx, state) {
5674
5726
  state.extensionsReady = true;
@@ -6041,7 +6093,7 @@ async function runBeforeAgentStart(ctx, state, agent, signal, assembled) {
6041
6093
  injected.push(createUserMessage({
6042
6094
  content: blocks,
6043
6095
  source: {
6044
- kind: "plugin",
6096
+ kind: "pi2dsh",
6045
6097
  plugin: state.messageSource,
6046
6098
  ...typeof message.customType === "string" ? { piCustomType: message.customType } : {}
6047
6099
  }
@@ -6199,7 +6251,7 @@ async function applyPiContextTransform(ctx, state, agent, signal, pending) {
6199
6251
  rebuilt.push(createUserMessage({
6200
6252
  content: blocks.map((block) => block.type === "text" ? files.get(block.text) ?? block : block),
6201
6253
  source: original.source ?? {
6202
- kind: "plugin",
6254
+ kind: "pi2dsh",
6203
6255
  plugin: state.messageSource
6204
6256
  }
6205
6257
  }));
@@ -6547,6 +6599,7 @@ async function materializeAttachmentImage(ctx, attachment) {
6547
6599
  }
6548
6600
  function ensureLoginCommand(ctx, state) {
6549
6601
  if (!shouldRegisterCompatibilityCommand("login", state.tuiSurfaces?.nativeCommands) && optionalService(ctx, "authorization") !== void 0) {
6602
+ state.shared.retireLoginCommand?.();
6550
6603
  logger(ctx).debug("[pi2dsh] native terminal owns /login and the authorization service is composed; Pi OAuth flows are available through it");
6551
6604
  return;
6552
6605
  }
@@ -6554,8 +6607,26 @@ function ensureLoginCommand(ctx, state) {
6554
6607
  state.shared.loginCommandRegistered = true;
6555
6608
  try {
6556
6609
  registerLoginCommand(ctx, state);
6557
- ctx.effect(() => () => {
6558
- state.shared.loginCommandRegistered = false;
6610
+ const retire = () => {
6611
+ state.commandDisposers.get("login")?.();
6612
+ state.commandDisposers.delete("login");
6613
+ state.commands.delete("login");
6614
+ for (const [name, owner] of state.dshCommandOwners) if (owner === "login") state.dshCommandOwners.delete(name);
6615
+ if (state.shared.retireLoginCommand === retire) {
6616
+ delete state.shared.retireLoginCommand;
6617
+ state.shared.loginCommandRegistered = false;
6618
+ }
6619
+ };
6620
+ state.shared.retireLoginCommand = retire;
6621
+ ctx.effect(() => retire);
6622
+ ctx.inject(["piTuiExtensions", "authorization"], async (inner) => {
6623
+ const terminal = await createPiTuiExtensionSurfaceAdapter(inner, state.packageName);
6624
+ if (terminal === void 0) return;
6625
+ try {
6626
+ if (!shouldRegisterCompatibilityCommand("login", terminal.nativeCommands)) retire();
6627
+ } finally {
6628
+ terminal.dispose();
6629
+ }
6559
6630
  });
6560
6631
  } catch (error) {
6561
6632
  state.shared.loginCommandRegistered = false;
@@ -7280,7 +7351,7 @@ async function sendPiMessage(ctx, state, content, mode, sessionOverride, customT
7280
7351
  const message = createUserMessage({
7281
7352
  content: blocks,
7282
7353
  source: {
7283
- kind: "plugin",
7354
+ kind: "pi2dsh",
7284
7355
  plugin: state.messageSource,
7285
7356
  ...customType === void 0 ? { form: "relay" } : { piCustomType: customType }
7286
7357
  }
@@ -7882,7 +7953,7 @@ async function registerPromptCommands(ctx, state, rootDir, manifest) {
7882
7953
  text: expandPrompt(text, rawInput)
7883
7954
  }],
7884
7955
  source: {
7885
- kind: "plugin",
7956
+ kind: "pi2dsh",
7886
7957
  plugin: `pi2dsh:${manifest.package.name}`,
7887
7958
  form: "relay"
7888
7959
  }
@@ -8524,7 +8595,28 @@ async function applyPiPackage(ctx, options) {
8524
8595
  const healthSuffix = health === void 0 || health.status === "ok" ? "" : ` — ${health.status.toUpperCase()}: missing Pi capabilities ${health.gaps.join(", ")}`;
8525
8596
  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)" : ""}`);
8526
8597
  }
8598
+ /** Resolve a host-owned route through its public config descriptor and credential reference. */
8599
+ async function resolveHostModelAuth(ctx, provider) {
8600
+ try {
8601
+ const entry = ctx.get("llm")?.listConfigurableProviders().find((candidate) => candidate.provider === provider);
8602
+ if (entry === void 0) return { ok: false };
8603
+ const section = ctx.get("settings")?.describe({ redactSecrets: true }).find((item) => item.ns === entry.settingsNs)?.value;
8604
+ const ref = entry.settingsPath.reduce((node, key) => typeof node === "object" && node !== null ? node[key] : void 0, section)?.apiKeyEnv;
8605
+ if (typeof ref !== "string" || ref.length === 0) return { ok: false };
8606
+ const resolved = await ctx.get("credentials")?.resolve(ref);
8607
+ return typeof resolved?.value === "string" && resolved.value.length > 0 ? {
8608
+ ok: true,
8609
+ apiKey: resolved.value
8610
+ } : { ok: false };
8611
+ } catch (error) {
8612
+ return {
8613
+ ok: false,
8614
+ error: `Host credential resolution failed: ${error instanceof Error ? error.message : String(error)}`
8615
+ };
8616
+ }
8617
+ }
8527
8618
  const runtimeInternals = {
8619
+ resolveHostModelAuth,
8528
8620
  compactionReason,
8529
8621
  resolveOfferedChoice,
8530
8622
  currentPiModel,
@@ -8543,4 +8635,4 @@ const runtimeInternals = {
8543
8635
  //#endregion
8544
8636
  export { overlayProviderConfig as a, runtimeInternals as c, normalizeToolSchema as i, getSharedChildExtensionCatalog as n, registerChildExtensionCatalog as o, mergeProviderRegistration as r, registerVisionCompanions as s, applyPiPackage as t };
8545
8637
 
8546
- //# sourceMappingURL=runtime-C6AOQRt7.mjs.map
8638
+ //# sourceMappingURL=runtime-BFJsumJX.mjs.map