pi2dsh 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
- import { a as registerVisionCompanions } from "./runtime-BN4E4OtI.mjs";
2
+ import { a as registerVisionCompanions } from "./runtime-BlbKWw7W.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { applyPiHost, applyPreparedPiHost, manifestForInstalled, preparePiHost } from "./host.mjs";
5
- import { a as PI_AI_PACKAGES, c as UI_CONTEXT_RULES, d as ruleForEvent, f as ruleForHostImport, i as HOST_IMPORT_RULES, l as ruleForApi, n as CONTEXT_RULES, o as PI_CODING_AGENT_PACKAGES, p as ruleForUiContextProperty, r as EVENT_RULES, s as PI_TUI_PACKAGES, t as API_RULES, u as ruleForContextProperty } from "./compatibility-DH_Gi96X.mjs";
5
+ import { a as PI_AI_PACKAGES, c as UI_CONTEXT_RULES, d as ruleForEvent, f as ruleForHostImport, i as HOST_IMPORT_RULES, l as ruleForApi, n as CONTEXT_RULES, o as PI_CODING_AGENT_PACKAGES, p as ruleForUiContextProperty, r as EVENT_RULES, s as PI_TUI_PACKAGES, t as API_RULES, u as ruleForContextProperty } from "./compatibility-DVMqXJux.mjs";
6
6
  import { n as convertPiMcpConfig, r as renderMcpPatch, t as collectPiMcpServers } from "./mcp-config-jmzbgdHa.mjs";
7
7
  import { readFile } from "node:fs/promises";
8
8
  import { existsSync } from "node:fs";
@@ -234,7 +234,7 @@ async function apply(ctx, config = {}) {
234
234
  * @returns the compatibility report.
235
235
  */
236
236
  async function analyzePackage(pkg) {
237
- const { analyzePackage: run } = await import("./analyzer-er3QPDc0.mjs");
237
+ const { analyzePackage: run } = await import("./analyzer-C5FQXB7R.mjs");
238
238
  return run(pkg);
239
239
  }
240
240
  //#endregion
@@ -2,7 +2,7 @@
2
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";
3
3
  import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
4
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";
5
+ import { W as getKeybindings, _t as stripTerminalSequences } from "./pi-tui-DruSeKmd.mjs";
6
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";
7
7
  import { createRequire } from "node:module";
8
8
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
@@ -13,7 +13,7 @@ import { tmpdir } from "node:os";
13
13
  import { EventEmitter } from "node:events";
14
14
  import { AsyncLocalStorage } from "node:async_hooks";
15
15
  import { createJiti } from "jiti";
16
- import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmError, QUOTA_EXCEEDED_CODE, createUserMessage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
16
+ import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, QUOTA_EXCEEDED_CODE, createUserMessage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
17
17
  import { scopeOf } from "@deepseek-ai/dsh-scope";
18
18
  import { renderPrompt } from "@deepseek-ai/dsh-system-prompt";
19
19
  import { randomUUID } from "node:crypto";
@@ -2252,6 +2252,18 @@ function piCarriedFields(model) {
2252
2252
  * Wrap one Pi provider object as a DSH LlmAdapter-shaped route handler.
2253
2253
  * Structural typing carries it through `llm.registerAdapter`.
2254
2254
  */
2255
+ /** Build the route handler ON the host's own LlmAdapter base class. The
2256
+ * 0.1.0-rc.8 dispatch reads adapter methods structurally, so a plain object
2257
+ * worked; 0.1.1's dispatch calls `adapter.prepareCall` — a method the BASE
2258
+ * CLASS provides (it binds resolveModel + stream into a one-shot prepared
2259
+ * call). Because `@deepseek-ai/dsh-llm` is a peer, `LlmAdapter` here is
2260
+ * whichever generation the host installed: own-property methods carry the
2261
+ * bridge behavior on both, and everything the base contributes on newer
2262
+ * generations (prepareCall, retry-policy defaults) is inherited instead of
2263
+ * re-implemented. */
2264
+ function llmAdapterInstance(methods) {
2265
+ return Object.assign(new LlmAdapter(), methods);
2266
+ }
2255
2267
  function piProviderDshAdapter(providerId, provider, host) {
2256
2268
  let warnedStop = false;
2257
2269
  const warnStopUnsupported = () => {
@@ -2259,7 +2271,7 @@ function piProviderDshAdapter(providerId, provider, host) {
2259
2271
  warnedStop = true;
2260
2272
  host.warn(`[pi2dsh] a request to Pi provider ${JSON.stringify(providerId)} carries stop sequences, which Pi models at no layer — they cannot be forwarded, so the model will not halt on them`);
2261
2273
  };
2262
- return {
2274
+ return llmAdapterInstance({
2263
2275
  providerInfo: (id) => ({
2264
2276
  id,
2265
2277
  name: typeof provider.name === "string" ? provider.name : id
@@ -2333,7 +2345,7 @@ function piProviderDshAdapter(providerId, provider, host) {
2333
2345
  reasoningEffort: reasoning
2334
2346
  }), contextWindow);
2335
2347
  }
2336
- };
2348
+ });
2337
2349
  }
2338
2350
  const IMAGE_OMITTED_TEXT = "[image attachment omitted: the selected model reads text only]";
2339
2351
  const imageNoticeText = (path) => path === void 0 ? IMAGE_OMITTED_TEXT : `[image attached at ${path} — the selected model reads text only; use an image-capable tool to view it]`;
@@ -2382,7 +2394,7 @@ function imageAdmissionCompanionAdapter(options) {
2382
2394
  inputModalities: modalities
2383
2395
  };
2384
2396
  };
2385
- return {
2397
+ return llmAdapterInstance({
2386
2398
  providerInfo: (id) => {
2387
2399
  return {
2388
2400
  id,
@@ -2405,7 +2417,7 @@ function imageAdmissionCompanionAdapter(options) {
2405
2417
  ...Array.isArray(streamOptions.messages) ? { messages: await textOnlyMessages(streamOptions.messages, materialize) } : {}
2406
2418
  });
2407
2419
  }
2408
- };
2420
+ });
2409
2421
  }
2410
2422
  /** A wire protocol DSH's official adapter can serve, or undefined. */
2411
2423
  function protocolOf(value) {
@@ -3126,11 +3138,16 @@ function answerText(answer) {
3126
3138
  async function askOne(ctx, agent, signal, question) {
3127
3139
  const service = optionalService(ctx, "userQuestions");
3128
3140
  if (service === void 0) unsupported("ctx.ui AskUser");
3129
- return answerText((await service.ask({
3130
- questions: [question],
3131
- ...agent !== void 0 ? { agent } : {},
3132
- signal
3133
- })).answers.find((answer) => answer.id === question.id));
3141
+ try {
3142
+ return answerText((await service.ask({
3143
+ questions: [question],
3144
+ ...agent !== void 0 ? { agent } : {},
3145
+ signal
3146
+ })).answers.find((answer) => answer.id === question.id));
3147
+ } catch (error) {
3148
+ if (signal?.aborted === true) return void 0;
3149
+ throw error;
3150
+ }
3134
3151
  }
3135
3152
  function agentSession(agent) {
3136
3153
  const session = agent?.session;
@@ -3229,9 +3246,76 @@ async function summarizeAbandonedBranch(ctx, state, agent, events, boundary, opt
3229
3246
  }
3230
3247
  return result.summary;
3231
3248
  }
3249
+ /** Resolve Pi's official `ExtensionUIDialogOptions` third argument
3250
+ * (`{ signal?, timeout? }`, types.ts:96 at the pinned upstream) into one
3251
+ * abort signal. A bare AbortSignal is also accepted — pi-ai's OAuth prompt
3252
+ * callbacks hand the signal directly. Anything else resolves to undefined.
3253
+ * The `timeout` field is realized with AbortSignal.timeout, so a dialog the
3254
+ * package wants auto-dismissed really leaves the screen. Silently ignoring
3255
+ * the options object is what left the OAuth paste box on screen after the
3256
+ * browser callback had already won (pi-mcp-adapter passes
3257
+ * `ui.input(title, undefined, { signal })` and cancels it in its
3258
+ * waitForAuthorizationResponse finally block). */
3259
+ function dialogAbortSignal(opts) {
3260
+ if (opts instanceof AbortSignal) return opts;
3261
+ if (typeof opts !== "object" || opts === null) return void 0;
3262
+ const record = opts;
3263
+ const signals = [];
3264
+ if (record.signal instanceof AbortSignal) signals.push(record.signal);
3265
+ if (typeof record.timeout === "number" && Number.isFinite(record.timeout) && record.timeout > 0) signals.push(AbortSignal.timeout(record.timeout));
3266
+ if (signals.length === 0) return void 0;
3267
+ return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
3268
+ }
3269
+ const OSC8_HYPERLINK = /\u001b\]8;[^;]*;([^\u0007\u001b]*)(?:\u0007|\u001b\\)([\s\S]*?)\u001b\]8;;(?:\u0007|\u001b\\)/gu;
3270
+ const MARKDOWN_LINK = /\[([^\]]+)\]\(([^)]+)\)/gu;
3271
+ /**
3272
+ * Translate terminal-oriented Pi dialog copy into DSH's native question
3273
+ * shape. Pi packages legitimately put OSC 8 links and a multi-line body in
3274
+ * the dialog title because Pi's TUI owns that whole text block. DSH separates
3275
+ * a plain-text heading from a Markdown detail block, and its Web client renders
3276
+ * terminal control bytes literally. Keep the plugin's words and destination,
3277
+ * but move them onto the host's real presentation contract.
3278
+ */
3279
+ function projectDialogPrompt(title, supplementalDetail, linkMode) {
3280
+ const linkedUrls = /* @__PURE__ */ new Set();
3281
+ const normalize = (value) => {
3282
+ const formatted = String(value).replace(OSC8_HYPERLINK, (whole, url, label) => {
3283
+ linkedUrls.add(url);
3284
+ if (linkMode === "terminal") return whole;
3285
+ return `[${stripTerminalSequences(label).replace(/([\\\[\]])/gu, "\\$1")}](${url})`;
3286
+ });
3287
+ return (linkMode === "terminal" ? formatted : stripTerminalSequences(formatted)).replace(/\r\n?/gu, "\n");
3288
+ };
3289
+ const titleLines = normalize(title).split("\n");
3290
+ while (titleLines[0]?.trim() === "") titleLines.shift();
3291
+ while (titleLines.at(-1)?.trim() === "") titleLines.pop();
3292
+ const headingLine = titleLines.shift()?.trim() || "Input required";
3293
+ const headingHasLink = linkMode === "terminal" ? headingLine.includes("\x1B]8;") : [...headingLine.matchAll(MARKDOWN_LINK)].length > 0;
3294
+ const question = linkMode === "terminal" ? stripTerminalSequences(headingLine) : headingLine.replace(MARKDOWN_LINK, "$1");
3295
+ if (headingHasLink) titleLines.unshift("", headingLine);
3296
+ const detailLines = [...titleLines];
3297
+ if (supplementalDetail !== void 0) {
3298
+ if (detailLines.length > 0 && detailLines.at(-1)?.trim() !== "") detailLines.push("");
3299
+ detailLines.push(...normalize(supplementalDetail).split("\n"));
3300
+ }
3301
+ const deduplicated = [];
3302
+ for (const line of detailLines) {
3303
+ if (linkedUrls.has(line.trim())) continue;
3304
+ if (line.trim() === "" && deduplicated.at(-1)?.trim() === "") continue;
3305
+ deduplicated.push(line.replace(/[ \t]+$/gu, ""));
3306
+ }
3307
+ while (deduplicated[0]?.trim() === "") deduplicated.shift();
3308
+ while (deduplicated.at(-1)?.trim() === "") deduplicated.pop();
3309
+ const detail = deduplicated.join("\n");
3310
+ return {
3311
+ question,
3312
+ ...detail.length === 0 ? {} : { detail }
3313
+ };
3314
+ }
3232
3315
  function contextFor(ctx, state, agent, signal, command = false, sessionOverride) {
3233
3316
  const notices = [];
3234
3317
  const userQuestions = optionalService(ctx, "userQuestions");
3318
+ const dialogLinkMode = optionalService(ctx, "tuiScenes") === void 0 ? "markdown" : "terminal";
3235
3319
  const ui = {
3236
3320
  notify(message, type) {
3237
3321
  const text = String(message);
@@ -3240,40 +3324,36 @@ function contextFor(ctx, state, agent, signal, command = false, sessionOverride)
3240
3324
  const level = type === "warning" || type === "error" ? "warn" : "info";
3241
3325
  logger(ctx)[level](`[pi2dsh] ${text}`);
3242
3326
  },
3243
- select: async (title, options, promptSignal) => {
3327
+ select: async (title, options, opts) => {
3244
3328
  const labels = options.map((option) => String(option));
3245
- const answered = await askOne(ctx, agent, joinSignals(signal, promptSignal), {
3329
+ const answered = await askOne(ctx, agent, joinSignals(signal, dialogAbortSignal(opts)), {
3246
3330
  id: "pi2dsh-select",
3247
- question: String(title),
3331
+ ...projectDialogPrompt(title, void 0, dialogLinkMode),
3248
3332
  options: labels.map((label) => ({ label }))
3249
3333
  });
3250
3334
  return answered === void 0 ? void 0 : resolveOfferedChoice(answered, labels);
3251
3335
  },
3252
- async confirm(title, message) {
3253
- return await askOne(ctx, agent, signal, {
3336
+ async confirm(title, message, opts) {
3337
+ return await askOne(ctx, agent, joinSignals(signal, dialogAbortSignal(opts)), {
3254
3338
  id: "pi2dsh-confirm",
3255
- question: String(title),
3256
- detail: String(message),
3339
+ ...projectDialogPrompt(title, message, dialogLinkMode),
3257
3340
  options: [{ label: "Yes" }, { label: "No" }]
3258
3341
  }) === "Yes";
3259
3342
  },
3260
- input: (title, placeholder, promptSignal) => askOne(ctx, agent, joinSignals(signal, promptSignal), {
3343
+ input: (title, placeholder, opts) => askOne(ctx, agent, joinSignals(signal, dialogAbortSignal(opts)), {
3261
3344
  id: "pi2dsh-input",
3262
- question: String(title),
3263
- ...placeholder === void 0 ? {} : { detail: String(placeholder) }
3345
+ ...projectDialogPrompt(title, placeholder, dialogLinkMode)
3264
3346
  }),
3265
- deviceCode: async (title, detail, promptSignal) => {
3266
- await askOne(ctx, agent, joinSignals(signal, promptSignal), {
3347
+ deviceCode: async (title, detail, opts) => {
3348
+ await askOne(ctx, agent, joinSignals(signal, dialogAbortSignal(opts)), {
3267
3349
  id: "pi2dsh-device-code",
3268
- question: String(title),
3269
- detail: String(detail),
3350
+ ...projectDialogPrompt(title, detail, dialogLinkMode),
3270
3351
  options: [{ label: "Cancel login" }]
3271
3352
  });
3272
3353
  },
3273
3354
  editor: (title, prefill) => askOne(ctx, agent, signal, {
3274
3355
  id: "pi2dsh-editor",
3275
- question: String(title),
3276
- ...prefill === void 0 ? {} : { detail: `Current text:\n${String(prefill)}` }
3356
+ ...projectDialogPrompt(title, prefill === void 0 ? void 0 : `Current text:\n${String(prefill)}`, dialogLinkMode)
3277
3357
  }),
3278
3358
  setStatus: (key, text) => {
3279
3359
  state.shared.browserSurfaces?.setStatus(sessionIdOf(state, agent), state.packageName, String(key), text);
@@ -6206,4 +6286,4 @@ const runtimeInternals = {
6206
6286
  //#endregion
6207
6287
  export { registerVisionCompanions as a, overlayProviderConfig as i, mergeProviderRegistration as n, runtimeInternals as o, normalizeToolSchema as r, applyPiPackage as t };
6208
6288
 
6209
- //# sourceMappingURL=runtime-BN4E4OtI.mjs.map
6289
+ //# sourceMappingURL=runtime-BlbKWw7W.mjs.map