pi2dsh 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,12 +36,20 @@ here, you should switch to it. That would be the bridge doing its job.
36
36
  One engine, then whatever plugins you want:
37
37
 
38
38
  ```sh
39
- dsh plugin --profile <your-profile> add pi2dsh
40
- dsh plugin --profile <your-profile> add @kassing/pi-vision
39
+ dsh plugin --profile web add pi2dsh
40
+ dsh plugin --profile web add @kassing/pi-vision
41
41
  ```
42
42
 
43
43
  Then **restart `dsh`** — plugins mount at startup.
44
44
 
45
+ > **Use `web` or `headless` as the profile name.** DSH ships a template for
46
+ > exactly those two, and each includes a surface (the web app / the one-shot
47
+ > driver). `dsh plugin --profile <any-other-name>` creates a profile with no
48
+ > surface at all, and that profile **starts up and then hangs with no
49
+ > diagnostic** — nothing to do with pi2dsh, but easy to hit on your first
50
+ > install. If you want a differently named profile, add the surface bundle to
51
+ > its `dsh.profile.bundles` yourself.
52
+
45
53
  That is the whole model. There is no conversion step, no generated bundle, no
46
54
  build. The engine discovers the Pi packages in your profile (every one is
47
55
  something you explicitly added) and mounts them through a single bridge
@@ -62,7 +70,7 @@ Two installer messages worth knowing:
62
70
 
63
71
  - **`ERR_PNPM_IGNORED_BUILDS`** — pnpm blocks dependency build scripts by
64
72
  default. Run `pnpm approve-builds` inside
65
- `$DSH_HOME/profiles/<your-profile>`, or set the listed packages to `true`
73
+ `$DSH_HOME/profiles/web`, or set the listed packages to `true`
66
74
  under `allowBuilds` in that profile's `pnpm-workspace.yaml`. Then re-run the
67
75
  add. (This is your call to make, so the bridge does not work around it.)
68
76
  - **An add silently installs an older version** right after a release —
@@ -81,7 +89,7 @@ analysis back into the conversation.
81
89
  ### 1. Install the plugin
82
90
 
83
91
  ```sh
84
- dsh plugin --profile <your-profile> add @kassing/pi-vision
92
+ dsh plugin --profile web add @kassing/pi-vision
85
93
  ```
86
94
 
87
95
  ### 2. Point it at a multimodal model
@@ -124,7 +132,7 @@ non-default `temperature` some vision plugins send.
124
132
  In the CLI, mention a path:
125
133
 
126
134
  ```sh
127
- dsh --profile <your-profile> "What color fills $PWD/photo.png ? One word."
135
+ dsh --profile web "What color fills $PWD/photo.png ? One word."
128
136
  ```
129
137
 
130
138
  In the web app, **just paste the image** — even though your main model is
@@ -140,7 +148,7 @@ text-only wire.
140
148
 
141
149
  Companions are automatic. To turn them off, or narrow them to specific routes,
142
150
  set `visionCompanions` in the engine's plugin config
143
- (`$DSH_HOME/profiles/<your-profile>/cordis.patch.yml`):
151
+ (`$DSH_HOME/profiles/web/cordis.patch.yml`):
144
152
 
145
153
  ```yaml
146
154
  - id: pi2dsh
@@ -150,13 +158,49 @@ set `visionCompanions` in the engine's plugin config
150
158
 
151
159
  Full runnable version, with probe images: [`examples/vision-bridge`](examples/vision-bridge/).
152
160
 
153
- ## What you can install today
161
+ ## What actually works today
162
+
163
+ Two levels, and they are not the same claim.
164
+
165
+ ### Level 1 — verified end to end, with a runnable example
166
+
167
+ Someone sat down, used the plugin's real feature on a real DSH loop, and saw
168
+ it work. **This is the list to trust.**
169
+
170
+ | Plugin | What was exercised | Where | Example |
171
+ |---|---|---|---|
172
+ | [`@kassing/pi-vision`](https://www.npmjs.com/package/@kassing/pi-vision) | Image analysis delegated to a vision model; image-admission companion route; analysis injected into a text-only model's turn | CLI + web | [`vision-bridge`](examples/vision-bridge/) |
173
+ | [`pi-btw`](https://www.npmjs.com/package/pi-btw) | `/btw <question>` as a real child session in DSH's subagent UI; `/btw-inject`; `/btw --save`; main thread stays clean | CLI + web | [`side-conversation`](examples/side-conversation/) |
174
+ | [`pi-vision-tool`](https://www.npmjs.com/package/pi-vision-tool) | Tool registration through a JSON-Schema shape DSH had to convert (`anyOf` → `oneOf`) | CLI + web | — |
175
+ | [`pi-approval-guardian`](https://www.npmjs.com/package/pi-approval-guardian) | Every tool call reviewed by a second model before execution; allow and deny both observed | CLI (bare env) | — |
176
+ | [`pi-hermes-memory`](https://www.npmjs.com/package/pi-hermes-memory) | Cross-session memory: written in one process, read back in a second, fresh one | CLI | — |
154
177
 
155
- The Pi catalog's **top 50 packages by monthly downloads**, all verified on a
156
- real DSH runtime mounted, then actually exercised. Status as of 2026-08-14;
157
- per-package machine-readable evidence in [`community/`](community/).
178
+ Examples for the last three are still to be written; per this project's own
179
+ rule they get re-verified from scratch before an example lands, so the table
180
+ says plainly which have one today.
158
181
 
159
- **47 of 50 verified working · 1 with no probeable surface · 2 pending a re-run.**
182
+ ### Level 2 mounts and its surface answers a probe
183
+
184
+ The Pi catalog's **top 50 packages by monthly downloads**, each mounted in a
185
+ real DSH runtime and then called through a black-box probe. Status as of
186
+ 2026-08-14; per-package machine-readable evidence in
187
+ [`community/`](community/).
188
+
189
+ **47 of 50 exercised successfully · 1 with no probeable surface · 2 pending a
190
+ re-run.**
191
+
192
+ **What this level does not tell you:** that the plugin's actual feature works
193
+ the way you would use it. A probe calls a registered surface with synthetic
194
+ arguments; a user runs a workflow. `pi-btw` is the cautionary example — it
195
+ graded "working" here for weeks while `/btw <question>` failed on a real
196
+ session, because the feature needed two ABI gaps closed (Pi's settable
197
+ `AgentState.messages`, and an input descriptor on bridged commands) that no
198
+ probe exercised. Both are fixed in 0.11.0, and both were general fixes that
199
+ unlock every plugin doing the same thing.
200
+
201
+ So read the table below as **"the bridge covers what this plugin touches"**,
202
+ not as "this plugin is known-good". When you try one, a report either way is
203
+ useful.
160
204
 
161
205
  | Area | Packages |
162
206
  |---|---|
@@ -187,6 +231,10 @@ Packages outside the top 50 are not a separate case — the bridge has no
187
231
  per-package code. If one hits an ABI gap, fixing that gap unlocks every
188
232
  package that shares it.
189
233
 
234
+ Level 1 grows by working through Level 2 one plugin at a time. The full
235
+ verification ladder, with what each rung does and does not prove:
236
+ [support matrix](docs/posting-kit/support-matrix.md).
237
+
190
238
  ## How it works
191
239
 
192
240
  Three layers, and nothing crosses them:
@@ -286,6 +334,7 @@ in one has actually been executed against a real DSH loop before landing.
286
334
  |---|---|
287
335
  | [`vision-bridge`](examples/vision-bridge/) | A text-only model answers questions about images — CLI and web, probe images included |
288
336
  | [`side-conversation`](examples/side-conversation/) | `/btw <question>` runs a side thread in DSH's native subagent UI; your main conversation stays clean |
337
+ | [`gateway-compat`](examples/gateway-compat/) | Private / domestic / proxy gateways that reject the `developer` role: why reasoning 400s, and how a Pi provider plugin gets past it (fake-endpoint probe included) |
289
338
  | [`custom-gateways`](examples/custom-gateways/) | Add any OpenAI-compatible gateway the official DSH way, and every Pi plugin sees it |
290
339
 
291
340
  ## Other tools
package/README.zh.md CHANGED
@@ -30,12 +30,18 @@ DSH 插件一样装一个 Pi 插件,它就能用。
30
30
  一次引擎,之后想装谁装谁:
31
31
 
32
32
  ```sh
33
- dsh plugin --profile <你的 profile> add pi2dsh
34
- dsh plugin --profile <你的 profile> add @kassing/pi-vision
33
+ dsh plugin --profile web add pi2dsh
34
+ dsh plugin --profile web add @kassing/pi-vision
35
35
  ```
36
36
 
37
37
  然后**重启 `dsh`**——插件在启动时挂载。
38
38
 
39
+ > **profile 名字请用 `web` 或 `headless`。** DSH 只为这两个名字内置了模板,
40
+ > 每个都带一个界面层(网页应用 / 一次性执行器)。`dsh plugin --profile <别的
41
+ > 名字>` 建出来的 profile **没有任何界面层**,起来之后会**直接挂住、不报任何
42
+ > 错**——这跟 pi2dsh 无关,但第一次装的时候很容易撞上。确实想用别的名字,就
43
+ > 自己往它的 `dsh.profile.bundles` 里加界面 bundle。
44
+
39
45
  就这一种方式。没有转换步骤,没有生成产物,不用构建。引擎会读出你 profile 里的
40
46
  Pi 包(每一个都是你显式装的),用同一个桥实例挂载它们:一个模型目录、一个登录、
41
47
  一个凭证存储、一个升级单元。
@@ -53,7 +59,7 @@ Pi 包(每一个都是你显式装的),用同一个桥实例挂载它们
53
59
  两条安装期提示值得提前知道:
54
60
 
55
61
  - **`ERR_PNPM_IGNORED_BUILDS`**:pnpm 默认拦截依赖的构建脚本。在
56
- `$DSH_HOME/profiles/<你的 profile>` 里跑 `pnpm approve-builds`,或者把提示
62
+ `$DSH_HOME/profiles/web` 里跑 `pnpm approve-builds`,或者把提示
57
63
  里的包在该 profile 的 `pnpm-workspace.yaml` 的 `allowBuilds` 下设成 `true`,
58
64
  然后重跑 add。(这是你的决定权,桥不会绕过它。)
59
65
  - **刚发版后 add 装到了旧版本**:pnpm 的 `minimumReleaseAge` 会跳过刚发布不久
@@ -69,7 +75,7 @@ Pi 生态里正好有插件干这件事:把图片交给你指定的视觉模
69
75
  ### 1. 装插件
70
76
 
71
77
  ```sh
72
- dsh plugin --profile <你的 profile> add @kassing/pi-vision
78
+ dsh plugin --profile web add @kassing/pi-vision
73
79
  ```
74
80
 
75
81
  ### 2. 给它配一个多模态模型
@@ -111,7 +117,7 @@ Pi 格式文件。
111
117
  CLI 里直接提路径:
112
118
 
113
119
  ```sh
114
- dsh --profile <你的 profile> "$PWD/photo.png 这张图是什么颜色?只答一个词。"
120
+ dsh --profile web "$PWD/photo.png 这张图是什么颜色?只答一个词。"
115
121
  ```
116
122
 
117
123
  Web 里**直接粘图**——哪怕你的主模型是纯文本的。DSH 正常情况下会拒绝给纯文本模型
@@ -124,7 +130,7 @@ Web 里**直接粘图**——哪怕你的主模型是纯文本的。DSH 正常
124
130
  那条线。
125
131
 
126
132
  伴生路由是全自动的。想关掉、或者只给某些路由开,在引擎的插件配置里设
127
- `visionCompanions`(`$DSH_HOME/profiles/<你的 profile>/cordis.patch.yml`):
133
+ `visionCompanions`(`$DSH_HOME/profiles/web/cordis.patch.yml`):
128
134
 
129
135
  ```yaml
130
136
  - id: pi2dsh
@@ -134,13 +140,42 @@ Web 里**直接粘图**——哪怕你的主模型是纯文本的。DSH 正常
134
140
 
135
141
  完整可跑版本(含纯色探针图):[`examples/vision-bridge`](examples/vision-bridge/)。
136
142
 
137
- ## 现在能装哪些插件
143
+ ## 现在到底哪些真能用
144
+
145
+ 分两级,这两件事不是一回事。
146
+
147
+ ### 第一级——端到端实测过,配可跑示例
148
+
149
+ 有人真坐下来,在真实 DSH loop 上用了这个插件的真功能,亲眼看到它工作。
150
+ **要信就信这张表。**
151
+
152
+ | 插件 | 验证了什么 | 在哪验的 | 示例 |
153
+ |---|---|---|---|
154
+ | [`@kassing/pi-vision`](https://www.npmjs.com/package/@kassing/pi-vision) | 图片委托给视觉模型;贴图伴生路由;分析结果注入纯文本模型的这一轮 | CLI + Web | [`vision-bridge`](examples/vision-bridge/) |
155
+ | [`pi-btw`](https://www.npmjs.com/package/pi-btw) | `/btw <问题>` 跑成 DSH 子代理界面里的真子会话;`/btw-inject`;`/btw --save`;主会话保持干净 | CLI + Web | [`side-conversation`](examples/side-conversation/) |
156
+ | [`pi-vision-tool`](https://www.npmjs.com/package/pi-vision-tool) | 工具注册,且带一个 DSH 需要转换的 JSON Schema 形状(`anyOf` → `oneOf`) | CLI + Web | — |
157
+ | [`pi-approval-guardian`](https://www.npmjs.com/package/pi-approval-guardian) | 每次工具调用先由第二个模型审批;放行与拒绝两条路都看到了 | CLI(裸环境) | — |
158
+ | [`pi-hermes-memory`](https://www.npmjs.com/package/pi-hermes-memory) | 跨会话记忆:一个进程写入,另一个全新进程读回 | CLI | — |
138
159
 
139
- Pi 目录里**月下载量前 50 的包**,全部在真实 DSH 运行时上验证过——先挂载,再真的
140
- 调起来跑。状态截至 2026-08-14;逐包的机器可读证据在
141
- [`community/`](community/)。
160
+ 后三个的示例还没写。按本项目自己的规矩,补示例前必须重新端到端验证一遍,所以
161
+ 这张表如实标出今天谁有示例。
142
162
 
143
- **50 个里 47 个实测可用 · 1 个没有可探测面 · 2 个待复跑。**
163
+ ### 第二级——能挂载,且注册面能被探针调起来
164
+
165
+ Pi 目录里**月下载量前 50 的包**,每个都在真实 DSH 运行时里挂载,然后用黑盒探针
166
+ 调用。状态截至 2026-08-14;逐包的机器可读证据在 [`community/`](community/)。
167
+
168
+ **50 个里 47 个探针调用成功 · 1 个没有可探测面 · 2 个待复跑。**
169
+
170
+ **这一级不能说明的事**:不能说明这个插件的真功能按你的用法能跑通。探针是拿合成
171
+ 参数去调一个注册面,用户跑的是一整条工作流。`pi-btw` 就是最好的反例——它在这张
172
+ 表里挂着"working"挂了好几周,而真实会话里 `/btw <问题>` 是直接失败的:这个功能
173
+ 需要补两个 ABI 缺口(Pi 公开可写的 `AgentState.messages`,以及给桥接命令声明输入
174
+ 描述符),而任何探针都不会碰到它们。两个缺口都在 0.11.0 修好了,而且都是通用修
175
+ 复——同样用法的插件一起解锁。
176
+
177
+ 所以下面这张表请读成**"桥覆盖了这个插件用到的面"**,而不是"这个插件已知可用"。
178
+ 你要是试了哪个,不管成没成,反馈回来都有用。
144
179
 
145
180
  | 能力 | 插件 |
146
181
  |---|---|
@@ -168,6 +203,9 @@ Pi 目录里**月下载量前 50 的包**,全部在真实 DSH 运行时上验
168
203
  前 50 之外的包不是另一类情况——桥里没有任何逐包代码。哪个包撞上 ABI 缺口,补上
169
204
  那个缺口,撞同一处的包一起解锁。
170
205
 
206
+ 第一级是靠一个一个啃第二级长出来的。完整的验证阶梯、以及每一级分别能证明什么、
207
+ 不能证明什么:[support matrix](docs/posting-kit/support-matrix.md)。
208
+
171
209
  ## 技术架构
172
210
 
173
211
  三层,谁也不跨谁:
@@ -251,6 +289,7 @@ loop 上实际跑过才会进来。
251
289
  |---|---|
252
290
  | [`vision-bridge`](examples/vision-bridge/) | 纯文本模型回答图片问题——CLI 与 Web 双端,附探针图 |
253
291
  | [`side-conversation`](examples/side-conversation/) | `/btw <问题>` 在 DSH 原生子代理界面里开一条侧边线程,主会话保持干净 |
292
+ | [`gateway-compat`](examples/gateway-compat/) | 私有 / 国内 / 代理网关拒收 `developer` 角色:为什么一开推理就 400,以及用 Pi provider 插件怎么绕过去(附假端点探针) |
254
293
  | [`custom-gateways`](examples/custom-gateways/) | 按 DSH 官方方式接任何 OpenAI 兼容网关,每个 Pi 插件都能看到它 |
255
294
 
256
295
  ## 其它工具
package/dist/host.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-DFD6u4NO.mjs";
2
+ import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-Bh5k4Kjt.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as registerVisionCompanions } from "./runtime-DFD6u4NO.mjs";
2
+ import { r as registerVisionCompanions } from "./runtime-Bh5k4Kjt.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { applyPiHost, generateHostBundle, manifestForInstalled } from "./host.mjs";
5
5
  import { a as CONTEXT_RULES, c as PI_AI_PACKAGES, d as UI_CONTEXT_RULES, f as ruleForApi, g as ruleForUiContextProperty, h as ruleForHostImport, i as API_RULES, l as PI_CODING_AGENT_PACKAGES, m as ruleForEvent, n as convertPiMcpConfig, o as EVENT_RULES, p as ruleForContextProperty, r as renderMcpPatch, s as HOST_IMPORT_RULES, t as collectPiMcpServers, u as PI_TUI_PACKAGES } from "./mcp-config-Cn5gWemZ.mjs";
@@ -1,5 +1,5 @@
1
1
 
2
- import { F as AssistantMessageEventStream, a as builtinProviders, i as __setPiAiLlmBridge } from "./pi-ai-Dyg4zyLZ.mjs";
2
+ import { F as AssistantMessageEventStream, a as builtinProviders, h as getSupportedThinkingLevels, i as __setPiAiLlmBridge } from "./pi-ai-Dyg4zyLZ.mjs";
3
3
  import { P as generateBranchSummary, Tn as PiCapabilityError, b as Theme, d as ExtensionRunner, w as __setSubagentSessionFactory, wn as CapabilityLedger } from "./pi-coding-agent-Ctw-BhoY.mjs";
4
4
  import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
5
5
  import { a as resolvePiProviderAuth, n as loginPiProvider, r as providerSupportsOAuth, t as FileCredentialStore } from "./oauth-bridge-CfyImWx4.mjs";
@@ -1279,6 +1279,33 @@ const PI_CARRIED_FIELDS = [
1279
1279
  "compat",
1280
1280
  "headers"
1281
1281
  ];
1282
+ /**
1283
+ * Pi's reasoning capability, in DSH's shape.
1284
+ *
1285
+ * The two sides say the same thing differently: Pi carries a `reasoning`
1286
+ * boolean plus a `thinkingLevelMap` whose `null` entries mark unsupported
1287
+ * levels, while DSH asks an adapter for the selectable efforts directly.
1288
+ * Translating is this layer's job — carrying Pi's boolean through under the
1289
+ * name `reasoning` would collide with DSH's `{efforts}` object, and carrying
1290
+ * nothing (what this bridge did before) leaves every package-registered route
1291
+ * with no selectable effort at all, so any reasoning request is rejected.
1292
+ *
1293
+ * The mapping is DSH's own: its native pi-ai adapter derives efforts from
1294
+ * `getSupportedThinkingLevels` and names them the same way, so a route a Pi
1295
+ * package registers offers exactly the efforts it would through the host's
1296
+ * own adapter. It belongs to the exact-route resolve, not the catalog listing
1297
+ * — `reasoning` is a resolved-model field in DSH's contract.
1298
+ * @param model - the Pi model descriptor as its package declared it.
1299
+ */
1300
+ function reasoningProjection(model) {
1301
+ if (model.reasoning !== true) return {};
1302
+ const levels = getSupportedThinkingLevels(model);
1303
+ if (levels.length === 0) return {};
1304
+ return { reasoning: { efforts: levels.map((level) => ({
1305
+ id: level,
1306
+ name: `${level.charAt(0).toUpperCase()}${level.slice(1)}`
1307
+ })) } };
1308
+ }
1282
1309
  function piCarriedFields(model) {
1283
1310
  const carried = {};
1284
1311
  for (const field of PI_CARRIED_FIELDS) if (model[field] !== void 0) carried[field] = model[field];
@@ -1306,6 +1333,7 @@ function piProviderDshAdapter(providerId, provider, host) {
1306
1333
  const known = providerModels(provider).find((model) => model.id === modelId);
1307
1334
  return {
1308
1335
  ...known === void 0 ? {} : piCarriedFields(known),
1336
+ ...known === void 0 ? {} : reasoningProjection(known),
1309
1337
  provider: id,
1310
1338
  id: modelId,
1311
1339
  name: String(known?.name ?? modelId),
@@ -1327,7 +1355,8 @@ function piProviderDshAdapter(providerId, provider, host) {
1327
1355
  ...auth?.headers === void 0 ? {} : { headers: auth.headers },
1328
1356
  ...options.signal instanceof AbortSignal ? { signal: options.signal } : {},
1329
1357
  ...typeof options.maxTokens === "number" ? { maxTokens: options.maxTokens } : {},
1330
- ...typeof options.temperature === "number" ? { temperature: options.temperature } : {}
1358
+ ...typeof options.temperature === "number" ? { temperature: options.temperature } : {},
1359
+ ...typeof options.reasoningEffort === "string" ? { reasoningEffort: options.reasoningEffort } : {}
1331
1360
  };
1332
1361
  yield* piEventsToDshChunks(provider.stream(model, piContext, piOptions));
1333
1362
  }
@@ -3535,4 +3564,4 @@ const runtimeInternals = {
3535
3564
  //#endregion
3536
3565
  export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
3537
3566
 
3538
- //# sourceMappingURL=runtime-DFD6u4NO.mjs.map
3567
+ //# sourceMappingURL=runtime-Bh5k4Kjt.mjs.map