pi2dsh 0.10.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
@@ -2,205 +2,365 @@
2
2
 
3
3
  **English** | [中文](README.zh.md)
4
4
 
5
- **Bridging the Pi and DeepSeek Harness ecosystems.** pi2dsh is dedicated to connecting [Pi](https://pi.dev/)'s extension ecosystem with [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): one general **Pi Host ABI compatibility layer** that runs unmodified Pi extensions as native DSH plugins — not per-package patches.
5
+ **Run the Pi ecosystem's plugins on DeepSeek Harness, unmodified.**
6
6
 
7
7
  ```sh
8
- # install the engine once, then install Pi packages straight from npm
9
- dsh plugin --profile headless add pi2dsh
10
- dsh plugin --profile headless add @kassing/pi-vision
11
- dsh plugin --profile headless add pi-vision-tool
8
+ dsh plugin add pi2dsh # once
9
+ dsh plugin add <any-pi-plugin> # then any Pi plugin, straight from npm
12
10
  ```
13
11
 
14
- No conversion step, no generated bundles: the engine discovers every Pi
15
- package you added to the profile and mounts them all through one bridge
16
- instance. Mounting happens at startup — **restart `dsh` after adding or
17
- removing plugins**. Remove a plugin with `dsh plugin remove <pkg>` (remove
18
- plugins before removing the engine, or they sit unmounted); upgrade the
19
- engine with `dsh plugin add pi2dsh@latest` (your plugins are untouched),
20
- upgrade a plugin with `dsh plugin add <pkg>@latest` (the engine is
21
- untouched).
12
+ ## Why this exists
22
13
 
23
- If an add stops with `ERR_PNPM_IGNORED_BUILDS` (pnpm blocks dependency
24
- build scripts by default), set the listed packages to `true` under
25
- `allowBuilds` in the profile's `pnpm-workspace.yaml` (or run
26
- `pnpm approve-builds` there), then re-run the add.
14
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) is built
15
+ on ideas worth betting on a durable, reconstructable session log, a clean
16
+ service composition, an agent loop you can actually reason about. What it does
17
+ not have yet is a large plugin ecosystem: it is early, and the plugins people
18
+ want on day one — web search, memory, code navigation, subagents, vision — are
19
+ mostly not written for it yet.
27
20
 
28
- ## Architecture
21
+ [Pi](https://pi.dev/) has that ecosystem already, and it is mature: hundreds
22
+ of published packages, many with real users.
29
23
 
30
- The bridge implements Pi's public extension surface **once**, mapping every call onto DSH's native services. A package that sticks to Pi's public API runs verbatim; capabilities with no safe mapping fail explicitly instead of faking success.
24
+ pi2dsh is one compatibility layer that implements Pi's public extension ABI on
25
+ top of DSH's native services, so a Pi package runs on DSH **as published** —
26
+ no fork, no patch, no per-package adapter. You install a Pi plugin the same
27
+ way you install anything else in DSH, and it works.
31
28
 
32
- ```
33
- Pi package (unmodified npm dependency)
34
- │ loaded verbatim: default-export factory, package.json pi.extensions
35
-
36
- ┌─────────────────── Pi Host ABI (pi2dsh) ───────────────────┐
37
- registerTool / setActiveTools → DSH tools + per-agent restrict │
38
- │ 33 Pi lifecycle events → DSH durable events & hook seams│
39
- exec → DSH subprocess (local / E2B) │
40
- │ sendMessage / sendUserMessage → DSH inject / steer / followup │
41
- │ ui.select/confirm/input → DSH userQuestions (real waits) │
42
- session entries/labels/name → durable sidecar + log projection│
43
- images → DSH attachments (refs, not b64)│
44
- │ pi-tui / pi-coding-agent / pi-ai imports → vendored/headless │
45
- │ shims (width/keys/session math byte-identical to Pi, MIT) │
46
- │ setModel / setThinkingLevel → agent/request seam overrides │
47
- └────────────────────────────────────────────────────────────────┘
48
-
49
- DeepSeek Harness native services (Cordis composition)
29
+ This is deliberately a bridge, not a destination. Every capability you reach
30
+ through pi2dsh is a capability DSH's own ecosystem will eventually offer
31
+ natively and when a better native plugin shows up for something you use
32
+ here, you should switch to it. That would be the bridge doing its job.
33
+
34
+ ## Install
35
+
36
+ One engine, then whatever plugins you want:
37
+
38
+ ```sh
39
+ dsh plugin --profile web add pi2dsh
40
+ dsh plugin --profile web add @kassing/pi-vision
50
41
  ```
51
42
 
52
- Delivery modes:
43
+ Then **restart `dsh`** — plugins mount at startup.
53
44
 
54
- | Mode | What it does |
55
- |---|---|
56
- | **Engine** (default) | `dsh plugin add pi2dsh` installs the bridge once as a DSH plugin. Every Pi package you then `dsh plugin add` is discovered from the profile's dependency manifest (each entry is an explicit add — never a node_modules scan) and mounted through ONE bridge instance: one model directory, one `/login`, one credential store, one upgrade unit |
57
- | **Host bundle** | One generated DSH bundle mounts a fixed list of Pi packages as npm dependencies — for pinned, reproducible compositions |
58
- | **Convert** | A reviewable per-package bundle: vendored source snapshot + machine-readable compatibility report, for supply-chain-sensitive or unpublished/local packages |
59
- | **MCP config translation** | Pi's six `mcpServers` layers → official `@deepseek-ai/dsh-mcp-client` patch entries. The Pi MCP adapter's code never runs; `$VAR` becomes `!!js process.env.VAR`, literal secrets are warned about |
60
-
61
- Engine config (optional, in the profile's `cordis.patch.yml`): `packages:
62
- [a, b]` mounts exactly that list instead of discovering; `exclude: [c]`
63
- skips individual dependencies; `visionCompanions: false` turns off the
64
- automatic `<route>-vision` image-admission companions that every text-only
65
- model route gets by default (an explicit `{route: [modelIds]}` map narrows
66
- them instead).
67
-
68
- **Plugin upgrades and compatibility.** Installed plugin versions are locked
69
- by pnpm's lockfile — a plugin never upgrades behind your back; only an
70
- explicit `dsh plugin add <pkg>@latest` moves it. Before upgrading, run
71
- `pi2dsh inspect <pkg>@<version>` for the compatibility report. The bridge
72
- intercepts the Pi runtime imports (`pi-coding-agent`/`pi-tui`/`pi-ai` are
73
- served by its shims), so a plugin's own Pi dependency pins never load — the
74
- only drift that can bite is a plugin adopting a Pi host API the bridge does
75
- not cover yet, which the report shows and which fails loudly, package-
76
- isolated, at runtime.
77
-
78
- Three hard rules keep it general:
79
-
80
- 1. The core contains **no `if (packageName === …)`** branching.
81
- 2. Every capability has a **public-API contract test** (`pnpm test`, 55 tests); "some plugin loads" is never the success criterion.
82
- 3. The top-50 corpus is verified **black-box only**: failures file public ABI gaps, and fixing one gap unlocks every package that hits it (e.g. one jiti subpath-alias fix unlocked 4 packages at once).
83
-
84
- ## Progress: Pi catalog top 50 by monthly downloads
85
-
86
- Status as of 2026-08-14. Static analysis screens; the black-box run certifies. Full per-package machine-readable evidence in [community/](community/).
87
-
88
- | Tier | Count | Meaning |
89
- |---|---|---|
90
- | ✅ **Tested working** | **49 / 50** | Mounted in a real DSH runtime AND real execution verified: 42 returned success, 7 ran their business logic end-to-end and rejected the synthetic probe arguments (2 of the 49 verified through host mode). Real-service coverage along the way: a real LSP subprocess, real web search/fetch, PNG generation, a real MCP stdio server bridged end-to-end, real child-`pi` dispatch answered by a live model, real DeepSeek search on user credentials, and the official `dsh plugin` add/activate/remove flow |
91
- | 🟡 **Mounts, not fully verified** | **1 / 50** | `@alexanderfortin/pi-deepseek-usage` — a pure event-hook package: all four lifecycle subscriptions attach, but every handler is gated on an active DeepSeek model session (it fetches billing usage and renders a footer), so a black-box probe has no safely-assertable callable surface. A harness limit, not a package or bridge gap |
92
- | ❌ **Not yet supported** | **0 / 50** | The last four Pi-internal-runtime packages are bridged: vendored built-in tool constructors, provider factories, a real-semantics `ExtensionRunner` facade, and `createAgentSession` driving genuine DSH child agents |
93
- | **Total mountable today** | **50 / 50** | 48 through convert/host bundles directly; 2 snapshot-limited packages through host mode ([evidence](community/host-mode-results.json)) |
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.
94
52
 
95
- The v6 harness also hardened the probe methodology itself: the bridge's own host-native surface (e.g. the built-in `/login` command) is measured by mounting a zero-contribution fixture extension and subtracted from every probe, so a grade reflects the package's own increment only; unsafe-name screening is word-level (`litellm_skill_list` is not a "kill" tool); and the fixture environment serves a real MCP stdio server, a LiteLLM-gateway-shaped skills API, image-model settings under Pi's config-dir contract, and — opt-in via `PI2DSH_BLACKBOX_PI_BIN` + `DEEPSEEK_API_KEY` — real child-`pi` dispatch answered by a live model.
53
+ That is the whole model. There is no conversion step, no generated bundle, no
54
+ build. The engine discovers the Pi packages in your profile (every one is
55
+ something you explicitly added) and mounts them through a single bridge
56
+ instance: one model directory, one login, one credential store, one upgrade
57
+ unit.
96
58
 
97
- Additional verified layers: a **host bundle** mounting two unmodified packages passed the official plugin-manager flow end-to-end; a **real model run** (`deepseek-v4-flash`) called a migrated Pi tool with the durable session log asserted and zero credential persistence ([evidence](community/live-deepseek-results.json)).
59
+ Day-to-day:
98
60
 
99
- ### How the last four internal-runtime packages were bridged
61
+ | Task | Command |
62
+ |---|---|
63
+ | Add a plugin | `dsh plugin add <pkg>` (then restart dsh) |
64
+ | Remove a plugin | `dsh plugin remove <pkg>` — remove plugins before removing the engine |
65
+ | Upgrade a plugin | `dsh plugin add <pkg>@latest` — the engine is untouched |
66
+ | Upgrade the engine | `dsh plugin add pi2dsh@latest` — your plugins are untouched |
67
+ | Check a plugin before upgrading | `npx pi2dsh inspect <pkg>@<version>` |
68
+
69
+ Two installer messages worth knowing:
70
+
71
+ - **`ERR_PNPM_IGNORED_BUILDS`** — pnpm blocks dependency build scripts by
72
+ default. Run `pnpm approve-builds` inside
73
+ `$DSH_HOME/profiles/web`, or set the listed packages to `true`
74
+ under `allowBuilds` in that profile's `pnpm-workspace.yaml`. Then re-run the
75
+ add. (This is your call to make, so the bridge does not work around it.)
76
+ - **An add silently installs an older version** right after a release —
77
+ pnpm's `minimumReleaseAge` skips versions published very recently. Pin it:
78
+ `dsh plugin add pi2dsh@<version>`.
100
79
 
101
- Each landed as a reusable public-surface bridge, not a package patch: `pi-landstrip` and `pi-fabric` run on Pi's built-in tool constructors (bash/read/edit/write/grep/find/ls) vendored byte-identical with their pure-logic closure; `pi-provider-litellm` runs on the vendored pi-ai `createProvider` factory — providers key by `id` and the registry's `getProviderAuth` runs Pi's full credential chain (stored OAuth → stored key → the package's own env resolution), while model transports stay native to DSH llm; `pi-fabric` additionally hooks a real-semantics `ExtensionRunner` facade — patching `prototype.getAllRegisteredTools` genuinely filters the tool catalog, as under Pi; `@tintinweb/pi-subagents` runs on `createAgentSession` bridged to genuine DSH child agents through `ctx.agents` the bridge owns no model loop, so compositions without one fail explicitly instead of simulating a subagent.
80
+ Requires Node.js 22.19+ and DeepSeek Harness.
102
81
 
103
- ### How the screener judges compatibility
82
+ ## Walkthrough: give a text-only model eyes
104
83
 
105
- The screener models **load-time vs lazy reachability**: only an unresolvable dependency on the load-time static closure blocks a package — function-body dynamic imports, files reached only through dynamic import, and worker/data assets are lazy paths that behave identically under Pi and are graded as reviewable, never fatal. `bun:*` is treated like `node:*` (a host builtin of Pi's Bun-compiled distribution), and snapshots preserve the published file layout byte for byte. These rules are contract-tested; under them, packages that mix Bun-only branches, optional heavyweight dependencies, or bundler-generated worker paths — `pi-hermes-memory`, `@mjasnikovs/pi-task`, `pi-harness-runtime`, `mitsupi`, `pi-lens` — all mount and work as published, with no changes needed upstream.
84
+ The clearest example of what the bridge buys you. DeepSeek models are
85
+ text-only, so DSH cannot send them an image. The Pi ecosystem has a plugin for
86
+ exactly this — it hands the image to a vision model you choose and injects the
87
+ analysis back into the conversation.
106
88
 
107
- ### Roadmap
89
+ ### 1. Install the plugin
108
90
 
109
- 1. ✅ Done: the 9 "mounts, not fully verified" lifted — 8 grade tested-working (credentialed fixtures, a real MCP stdio server, a live-agent probe path for userQuestions, Pi-config-dir settings, real child-`pi` dispatch, and two registry-semantics fixes in the bridge: providers keyed by `id`, and `getProviderAuth` running Pi's full credential chain instead of OAuth only); the 1 remaining is a pure event-hook package graded honestly as having no probeable surface.
110
- 2. Done: interactive OAuth host seam — Pi provider `oauth.login/refreshToken/getApiKey` flows run on DSH-native interaction, credentials persist with Pi's `auth.json` semantics with double-checked-lock refresh, and the four official Pi flows ship built in; verified end-to-end against a real ChatGPT Pro account (see "Interactive OAuth" above).
111
- 3. ✅ Done: all four Pi-internal-runtime packages bridged (see above) — every top-50 package mounts.
112
- 4. ✅ Done: the 2 snapshot-limited packages verified through host mode ([evidence](community/host-mode-results.json)).
113
- 5. ✅ Done: load-time vs lazy reachability screening landed; the five packages it unblocked all mount, four tested-working (see above).
91
+ ```sh
92
+ dsh plugin --profile web add @kassing/pi-vision
93
+ ```
114
94
 
115
- ## Quick start
95
+ ### 2. Point it at a multimodal model
116
96
 
117
- Requires Node.js 22.19+ and DeepSeek Harness.
97
+ **This is the step to get right** — the plugin needs its own vision model, and
98
+ it is a different model from the one you chat with. Any OpenAI-compatible
99
+ vision endpoint works (OpenRouter, DashScope/Qwen-VL, a self-hosted vLLM, …).
100
+
101
+ The plugin reads its configuration from environment variables — the standard
102
+ way Pi plugins are configured, and a plain DSH-side action for you:
118
103
 
119
104
  ```sh
120
- # Engine (default): install once, then add Pi packages directly
121
- dsh plugin --profile headless add pi2dsh
122
- dsh plugin --profile headless add @kassing/pi-vision
123
-
124
- # Optional CLI (inspect / convert / host / mcp-config)
125
- npx pi2dsh inspect @narumitw/pi-lsp # compatibility report
126
- npx pi2dsh convert @narumitw/pi-lsp --out ./dsh-pi-lsp # vendored snapshot
127
- npx pi2dsh host --packages 'pi-simplify' --out ./pi-host # pinned bundle
128
- npx pi2dsh mcp-config # Pi mcpServers → DSH patch
105
+ export VISION_BRIDGE_BASE_URL=https://openrouter.ai/api/v1
106
+ export VISION_BRIDGE_MODEL=qwen/qwen2.5-vl-72b-instruct
107
+ export VISION_BRIDGE_API_KEY=$OPENROUTER_API_KEY
129
108
  ```
130
109
 
131
- ## Examples: copy-paste working capabilities
110
+ That is enough to work. If you would also like that vision model to appear in
111
+ DSH's own model picker (so you can chat with it directly), add it as a normal
112
+ DSH route as well — the `llm-pi-ai:` section of `$DSH_HOME/settings.yaml`:
132
113
 
133
- **Every verified capability ships as a complete, runnable example under
134
- [`examples/`](examples/)** — clone the repo, follow one example's README from
135
- zero to seeing the feature run. Every command in an example has actually been
136
- executed against a real DSH loop (CLI and web) before landing here; nothing
137
- is aspirational.
114
+ ```yaml
115
+ llm-pi-ai:
116
+ providers:
117
+ openrouter:
118
+ baseUrl: https://openrouter.ai/api/v1
119
+ apiKeyEnv: OPENROUTER_API_KEY
120
+ models:
121
+ - id: qwen/qwen2.5-vl-72b-instruct
122
+ ```
138
123
 
139
- | Example | What you get |
140
- |---|---|
141
- | [`examples/vision-bridge`](examples/vision-bridge/) | A text-only model answers questions about images: mention an image path, a configured vision model reads it, the analysis is injected into the conversation (works in CLI and the DSH web app; probe images included) |
142
- | [`examples/custom-gateways`](examples/custom-gateways/) | Add any OpenAI-compatible gateway the official DSH way (the `llm-pi-ai:` section of DSH settings) — it appears in the DSH model picker, works as the main model, and every Pi plugin sees it through the bridge's registry projection; the bridge owns zero model configuration |
124
+ Both are ordinary DSH configuration. The bridge owns no model configuration of
125
+ its own, and there is no Pi-format file for you to write.
143
126
 
144
- More verified capabilities (approval guardian, cross-session memory,
145
- interactive OAuth, MCP config conversion, host mode) get their examples as
146
- each one is re-verified end to end under the same bar.
127
+ Avoid GPT-5/o-family models as the vision backend: that generation rejects the
128
+ non-default `temperature` some vision plugins send.
147
129
 
148
- ## Interactive OAuth: sign in with your subscription
130
+ ### 3. Ask about an image
149
131
 
150
- DSH ships static HTTP headers only; pi2dsh adds the interactive OAuth layer from the Pi ecosystem. Any Pi provider package that registers an `oauth` block gets a working `/login <provider>` command on DSH, driven by the package's own protocol code. Pi's four official flows ship built in (vendored byte-identical): **OpenAI Codex (ChatGPT Plus/Pro)**, **Anthropic**, **GitHub Copilot**, **Kimi Code**.
132
+ In the CLI, mention a path:
151
133
 
152
134
  ```sh
153
- # inside a DSH session with a pi2dsh host bundle mounted
154
- /login openai-codex # prints the authorization URL, spins up the localhost callback
155
- # → approve in your browser; the credential lands in auth.json (0600)
135
+ dsh --profile web "What color fills $PWD/photo.png ? One word."
156
136
  ```
157
137
 
158
- What you get, end to end: PKCE + `localhost:1455` callback (device-code fallback for headless boxes), credentials persisted in Pi's `auth.json` format — so packages like `@narumitw/pi-accounts` manage the same file they already know automatic refresh with Pi's double-checked-lock rotation (5-minute expiry window, refreshed token persisted before release), and `getProviderAuth`/`getApiKeyForProvider` on the extension registry returning live keys.
138
+ In the web app, **just paste the image**even though your main model is
139
+ text-only. DSH normally refuses image attachments for a text-only model, so
140
+ the engine registers an *image-admission companion* route for every text-only
141
+ route in your directory, named `<route>-vision`. Pick it in the model picker
142
+ (it shows up as a "+ Vision Bridge" group), paste, and ask.
143
+
144
+ What you will see: your image becomes guide text, a
145
+ `pi2dsh:@kassing/pi-vision` context-injection row carries the analysis, and
146
+ your text-only model answers about the picture. Pixels never reach the
147
+ text-only wire.
159
148
 
160
- **And the token drives real model calls through DSH's native LLM path.** `pi2dsh/credentials-oauth` is a standard `dsh-credentials` provider: any reference shaped `PI2DSH_OAUTH_<PROVIDER>` resolves per request from `auth.json` (running the refresh rotation on the way), everything else falls through to the environment. Point an official `@deepseek-ai/dsh-llm-pi-ai` route at it and `ctx.llm.stream()` runs on your subscription:
149
+ Companions are automatic. To turn them off, or narrow them to specific routes,
150
+ set `visionCompanions` in the engine's plugin config
151
+ (`$DSH_HOME/profiles/web/cordis.patch.yml`):
161
152
 
162
153
  ```yaml
163
- - id: llm
164
- name: '@deepseek-ai/dsh-llm-pi-ai'
154
+ - id: pi2dsh
165
155
  config:
166
- providers:
167
- openai-codex:
168
- apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX
169
- models:
170
- - id: gpt-5.6-luna
156
+ visionCompanions: false
171
157
  ```
172
158
 
173
- Both layers are verified against a real ChatGPT Pro account: browser authorization → callback → token exchange → store → refreshable key (`scripts/verify-oauth-e2e.mjs`), then credentials provider → official pi-ai route → DSH-native `ctx.llm.stream()` → a real model reply on the subscription (`scripts/verify-oauth-llm-e2e.mjs`). On networks that need a proxy, both scripts honor `HTTPS_PROXY`.
159
+ Full runnable version, with probe images: [`examples/vision-bridge`](examples/vision-bridge/).
160
+
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
174
166
 
175
- ## Compatibility boundaries (explicit, never silent)
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.**
176
169
 
177
- | Area | Mapping |
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 | — |
177
+
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.
181
+
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.
204
+
205
+ | Area | Packages |
206
+ |---|---|
207
+ | **MCP** | `pi-mcp-adapter` · `pi-mcp-extension` |
208
+ | **Web search & fetch** | `pi-web-access` · `pi-deepseek-search` · `pi-web-search` · `@ollama/pi-web-search` · `@juicesharp/rpiv-web-tools` |
209
+ | **Code navigation & editing** | `pi-lens` (ast-grep) · `@narumitw/pi-lsp` · `pi-readseek` · `@ff-labs/pi-fff` · `pi-landstrip` · `pi-hashline-edit-pro`¹ |
210
+ | **Subagents & background work** | `@tintinweb/pi-subagents` · `@gotgenes/pi-subagents` · `pi-background-tasks`² · `@mjasnikovs/pi-task` |
211
+ | **Memory** | `pi-hermes-memory` · `pi-goosedump` |
212
+ | **Planning & goals** | `@narumitw/pi-goal` · `pi-goal-list-loop-audit` · `@narumitw/pi-plan-mode` · `@juicesharp/rpiv-todo` |
213
+ | **Asking you / approvals** | `@juicesharp/rpiv-ask-user-question` · `pi-ask-user` · `@gotgenes/pi-permission-system` · `@juicesharp/rpiv-advisor` |
214
+ | **Side conversations** | `pi-btw` · `@narumitw/pi-btw` |
215
+ | **Models & providers** | `pi-provider-litellm` · `pi-llama-cpp` · `pi-prompt-template-model` · `@vigolium/piolium` |
216
+ | **Images** | `@kassing/pi-vision` (see above) · `@amaster.ai/pi-image-gen` |
217
+ | **External integrations** | `@llblab/pi-telegram` · `pi-cursor-sdk`² · `@howaboua/pi-codex-conversion` · `pi-agent-browser-native`² · `pi-harness-runtime` |
218
+ | **Prompting & workflow** | `pi-simplify` · `pi-fabric`² · `mitsupi` · `pi-cc-extensions` · `pi-rtk-optimizer` · `pi-interview`¹ |
219
+ | **Terminal decoration** | `pi-powerline-footer` · `@narumitw/pi-statusline` · `pi-zentui` |
220
+ | **Voice** | `@juicesharp/rpiv-voice` |
221
+ | **Usage reporting** | `@alexanderfortin/pi-deepseek-usage`³ |
222
+
223
+ ¹ Mounts; the exercise run is pending a re-run (a harness-side failure, not a
224
+ package or bridge gap). ² Ran its own business logic end to end and rejected
225
+ the synthetic probe arguments — working, correctly validating.
226
+ ³ A pure event-hook package: all subscriptions attach, but every handler is
227
+ gated on a live DeepSeek billing session, so a black-box probe has nothing
228
+ safely callable to assert.
229
+
230
+ Packages outside the top 50 are not a separate case — the bridge has no
231
+ per-package code. If one hits an ABI gap, fixing that gap unlocks every
232
+ package that shares it.
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
+
238
+ ## How it works
239
+
240
+ Three layers, and nothing crosses them:
241
+
242
+ ```
243
+ ┌─ Pi plugin ─────────────────────────────────────────────────┐
244
+ │ unmodified npm package. It sees a complete Pi host: the │
245
+ │ three Pi runtime imports, registerX, ctx.*, 33 lifecycle │
246
+ │ events. It never learns DSH exists. │
247
+ └──────────────────────────┬──────────────────────────────────┘
248
+ │ Pi's public ABI
249
+ ┌──────────────────────────▼──────────────────────────────────┐
250
+ │ pi2dsh — the translator, and the only place that knows both │
251
+ │ vocabularies. Registry projection, event bridge, session & │
252
+ │ subagent bridge, credentials, vendored Pi logic. │
253
+ └──────────────────────────┬──────────────────────────────────┘
254
+ │ ordinary DSH plugin + llm adapter
255
+ ┌──────────────────────────▼──────────────────────────────────┐
256
+ │ DeepSeek Harness. Sees a normal plugin. Never learns Pi │
257
+ │ exists. │
258
+ └─────────────────────────────────────────────────────────────┘
259
+ ```
260
+
261
+ The rules that keep it honest:
262
+
263
+ - **Never a second implementation of something DSH already has.** Tools go to
264
+ DSH's tool registry, models to DSH's llm configuration, MCP to
265
+ `dsh-mcp-client`, skills to `dsh-skill-filesystem`, questions to DSH's user
266
+ questions. The bridge translates configuration; it does not build a parallel
267
+ runtime.
268
+ - **You never see Pi.** Everything you configure, read, or type is DSH-shaped:
269
+ DSH settings, DSH commands, DSH credentials. Pi vocabulary exists only
270
+ inside the plugin's view and the bridge's own internals.
271
+ - **No per-package special cases.** The core contains no
272
+ `if (packageName === …)`. One ABI gap fixed unlocks every package that hits
273
+ it.
274
+ - **Never fake success.** A capability with no safe mapping is reported —
275
+ once, per plugin, in plain language — instead of silently returning
276
+ something invented. If a plugin needs one during startup, it is marked
277
+ unusable with a removal hint rather than half-working.
278
+ - **Verified, not asserted.** Every capability has a public-API contract test,
279
+ and ships only after running end to end on a real DSH loop — CLI *and* web.
280
+
281
+ ## Pi capabilities on DSH
282
+
283
+ Every surface a Pi package can touch, and what it maps onto. These tables are
284
+ generated from the rules the bridge consults at runtime, so they cannot drift
285
+ from the code.
286
+
287
+ | Area | Pi surfaces | Status |
288
+ |---|---|---|
289
+ | [Tools](docs/capabilities/tools.md) | 12 | 3 same semantics · 9 mapped, difference stated |
290
+ | [Commands, flags, editor input](docs/capabilities/commands.md) | 13 | 13 mapped, difference stated |
291
+ | [Messages, context, agent loop](docs/capabilities/conversation.md) | 20 | 7 same semantics · 13 mapped, difference stated |
292
+ | [Sessions & side conversations](docs/capabilities/sessions.md) | 24 | 4 same semantics · 20 mapped, difference stated |
293
+ | [Models, providers, credentials](docs/capabilities/models.md) | 15 | 12 mapped, difference stated · 3 not available |
294
+ | [Asking the user, rendering](docs/capabilities/interaction.md) | 24 | 4 same semantics · 20 mapped, difference stated |
295
+ | [Project environment & resources](docs/capabilities/environment.md) | 4 | 1 same semantics · 1 mapped · 2 not available |
296
+ | **Total** | **112** | **19 same · 88 mapped with stated differences · 5 not available** |
297
+
298
+ Plus **202 imported symbols** from Pi's three runtime packages
299
+ (`pi-coding-agent`, `pi-tui`, `pi-ai`), served from vendored or headless
300
+ shims — so a plugin's own Pi version pins never load.
301
+
302
+ Start at the [capability index](docs/capabilities/README.md). Machine-readable:
303
+ `pi2dsh matrix --json`.
304
+
305
+ **Signing in with a subscription** works too: DSH ships static HTTP headers
306
+ only, and the bridge adds the Pi ecosystem's interactive OAuth layer. Any Pi
307
+ provider package that declares an `oauth` block gets a working
308
+ `/login <provider>`, driven by the package's own protocol code — Pi's four
309
+ official flows (OpenAI Codex, Anthropic, GitHub Copilot, Kimi Code) ship built
310
+ in. Credentials persist with Pi's `auth.json` semantics and resolve per
311
+ request through a standard `dsh-credentials` provider, so your subscription
312
+ drives real calls on DSH's native llm path. Details in
313
+ [models](docs/capabilities/models.md).
314
+
315
+ **What is deliberately not available**, and why: runtime package installation
316
+ and standalone model runtimes stay with the host and its security gates;
317
+ provider payload/header/response interception belongs in a DSH llm adapter;
318
+ project trust is a host decision. See
319
+ [models](docs/capabilities/models.md) and
320
+ [environment](docs/capabilities/environment.md).
321
+
322
+ **The one gap we own:** plugin-drawn cards. Pi plugins can ship their own
323
+ renderers; today those registrations are accepted but not invoked, so such a
324
+ note appears as a native context-injection row — the content reaches you and
325
+ the model, without the plugin's styling. DSH has the machinery for this; we
326
+ have not built our client half yet.
327
+
328
+ ## Examples
329
+
330
+ Every verified capability ships as a complete, runnable example. Every command
331
+ in one has actually been executed against a real DSH loop before landing.
332
+
333
+ | Example | What you get |
178
334
  |---|---|
179
- | Tools | Native DSH tools; Pi's in-place `tool_call` argument mutation works for Pi-owned tools (DSH-native tools reject itDSH logs arguments before policy) |
180
- | Sessions | Messages project from DSH's durable log; Pi custom entries/labels/names persist in a pi2dsh sidecar (DSH has no out-of-repo plugin-event channel yet) |
181
- | Pi TUI | Pure logic vendored byte-identical; components construct headlessly; `ui.custom` resolves `undefined` exactly like Pi's own rpc mode |
182
- | Providers/OAuth | Interactive OAuth is live: `/login <provider>` runs the package's own flow, credentials persist in Pi's `auth.json` with automatic refresh; model transports stay native to DSH `llm` |
183
- | Model runtime | `modelRegistry` projects the live DSH llm directory as Pi Model objects (refreshed on `llm/adapters-updated`); `ctx.model` reflects the agent's real route; `setModel`/`setThinkingLevel` switch the loop through the `agent/request` waterfall; pi-ai `complete()`/`stream()` run REAL calls through `ctx.llm.stream()` with two-way message conversion (verified against a live model: `scripts/verify-model-bridge-e2e.mjs`) |
184
- | Session control | REAL on DSH's own surfaces: `newSession` creates a DSH session with lineage, `fork` uses DSH's official prefix-fork (landing on completed-turn boundaries), `navigateTree` forks at the target with an optional vendored branch summary, `switchSession` targets live sessions. The DSH tree lives *between* sessions (fork lineage); which session the surface shows stays a host choice |
185
- | Compaction & summaries | `ctx.compact()` triggers DSH's official manual compaction; Pi's `generateSummary`/`generateBranchSummary`/`findCutPoint` are vendored with model calls on the DSH llm bridge |
186
- | shutdown / reload | `shutdown` is absorbed (Pi defines its behavior as host-provided; the user owns DSH process exit); `reload` really remounts extension entries — skills/prompts/themes reload with dsh itself |
187
- | Host-owned capabilities | `ModelRuntime` and `DefaultPackageManager` stay unavailable **by design** (the host owns model configuration and package install with its security gates). Importing them is flagged at startup; constructing them throws a structured error, and doing so during plugin startup marks the plugin unusable with a clear removal hint. Every capability gap is reported to you once per plugin — never a silent failure, never a fake success. **Known plugins unusable for this reason: none so far** (this list will name any we find) |
188
- | Terminal decoration | footer/statusline/shortcuts register but never fire — matching Pi's own non-TUI modes |
335
+ | [`vision-bridge`](examples/vision-bridge/) | A text-only model answers questions about imagesCLI and web, probe images included |
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) |
338
+ | [`custom-gateways`](examples/custom-gateways/) | Add any OpenAI-compatible gateway the official DSH way, and every Pi plugin sees it |
189
339
 
190
- Full machine-readable matrix: `pi2dsh matrix --json`. Capability-by-capability acceptance evidence: [docs/acceptance.md](docs/acceptance.md). The complete 114-item Pi-surface → DSH-semantics verdict (3 red / 21 yellow / ~90 green): [docs/pi-abi-coverage.md](docs/pi-abi-coverage.md).
340
+ ## Other tools
191
341
 
192
- ## Development and verification
342
+ Beyond the engine, the CLI has a few helpers:
193
343
 
194
344
  ```sh
195
- pnpm verify # typecheck + 55 contract tests + packaging
196
- pnpm audit:community # static screening, top 50
197
- node scripts/blackbox-community.mjs community/blackbox-results.json --exercise
198
- # add DEEPSEEK_API_KEY=… PI2DSH_BLACKBOX_PI_BIN=$(command -v pi) for the
199
- # credentialed probes and real child-pi dispatch (keys from env only)
200
- pnpm test:community # deep runtime + official manager + host e2e
201
- DEEPSEEK_API_KEY=… pnpm test:live # real-model acceptance (key from env only)
345
+ npx pi2dsh inspect <pkg>@<version> # compatibility report before an upgrade
346
+ npx pi2dsh matrix --json # the full capability matrix
347
+ npx pi2dsh mcp-config # Pi mcpServers config → official DSH MCP entries
202
348
  ```
203
349
 
350
+ ## Development
351
+
352
+ ```sh
353
+ pnpm verify # typecheck + contract tests + packaging
354
+ pnpm audit:community # static screening over the top-50 corpus
355
+ pnpm test:community # deep runtime + official plugin-manager + e2e
356
+ DEEPSEEK_API_KEY=… pnpm test:live # real-model acceptance (key from env only)
357
+ ```
358
+
359
+ Acceptance evidence per capability: [docs/acceptance.md](docs/acceptance.md).
360
+ Working standards: [CLAUDE.md](CLAUDE.md) and [docs/STANDARDS.md](docs/STANDARDS.md).
361
+
204
362
  ## License
205
363
 
206
- MIT. Vendored Pi sources retain their upstream MIT license (`src/compat/vendor/PI-LICENSE`); generated bundles retain copied upstream license/notice files.
364
+ MIT. Vendored Pi sources retain their upstream MIT license
365
+ (`src/compat/vendor/PI-LICENSE`); generated bundles retain copied upstream
366
+ license and notice files.