The project's README from github.com/weijiafu14/pi2dsh. As one markdown file: README.md.
English | 中文
Run the Pi ecosystem's plugins on DeepSeek Harness, unmodified.
dsh plugin add pi2dsh # once
dsh plugin add <any-pi-plugin> # then any Pi plugin, straight from npm
DeepSeek Harness is built on ideas worth betting on — a durable, reconstructable session log, a clean service composition, an agent loop you can actually reason about. What it does not have yet is a large plugin ecosystem: it is early, and the plugins people want on day one — web search, memory, code navigation, subagents, vision — are mostly not written for it yet.
Pi has that ecosystem already, and it is mature: hundreds of published packages, many with real users.
pi2dsh is one compatibility layer that implements Pi's public extension ABI on top of DSH's native services, so a Pi package runs on DSH as published — no fork, no patch, no per-package adapter. You install a Pi plugin the same way you install anything else in DSH, and it works.
At the same time, pi2dsh is an ongoing, full-surface, real-world test of DSH's architecture. Instead of patching individual plugins, it asks whether the models, tools, sessions, interaction, resources and client capabilities that Pi plugins rely on can preserve their logic and lifecycle using only DSH's public services and extension seams. If they can, that is strong evidence that DSH's architectural goals for building agents and an agent-plugin ecosystem have been achieved, at least along this dimension. Wherever the bridge must bypass, degrade or cannot express a capability, it pinpoints an architectural gap that remains.
One engine, then whatever plugins you want:
dsh plugin --profile web add pi2dsh
dsh plugin --profile web add pi-mcp-adapter
Then restart dsh — plugins mount at startup.
A profile needs a surface bundle. DSH's built-in templates are
webandheadless. A custom profile is valid when its product installs a surface — for example@deepseek-harness-tui/dsh-tuiin thedsh-tuiprofile. A bare arbitrary profile has no surface and can start with nothing to drive it, so add the intended surface todsh.profile.bundlesfirst.
That is the whole model. There is no conversion step, no generated bundle, no build. The engine discovers the Pi packages in your profile (every one is something you explicitly added) and mounts them through a single bridge instance: one model directory, one login, one credential store, one upgrade unit.
Day-to-day:
| Task | Command |
|---|---|
| Add a plugin | dsh plugin add <pkg> (then restart dsh) |
| Remove a plugin | dsh plugin remove <pkg> — remove plugins before removing the engine |
| Upgrade a plugin | dsh plugin add <pkg>@latest — the engine is untouched |
| Upgrade the engine | dsh plugin add pi2dsh@latest — your plugins are untouched |
| Check a plugin before upgrading | npx pi2dsh inspect <pkg>@<version> |
Two installer messages worth knowing:
ERR_PNPM_IGNORED_BUILDS — pnpm blocks dependency build scripts by
default. Run pnpm approve-builds inside
$DSH_HOME/profiles/web, or set the listed packages to true
under allowBuilds in that profile's pnpm-workspace.yaml. Then re-run the
add. (This is your call to make, so the bridge does not work around it.)minimumReleaseAge skips versions published very recently. Pin it:
dsh plugin add pi2dsh@<version>.Requires Node.js 22.19+ and DeepSeek Harness.
The engine reads one config block from its plugin row. Today it takes a
single opt-in:
# $DSH_HOME/profiles/<profile>/cordis.patch.yml
- id: pi2dsh
config:
serveNativeSubagents: true
serveNativeSubagents (default: off) serves DSH-native subagents with
the profile's Pi packages. With it on, a child agent DSH spawns through its
own subagent delegation (one whose session carries the subagent origin)
receives every discovered Pi package mounted on its own agent scope — the
package's tools, commands and prompt sections appear for that child only,
and every contribution unwinds when the child ends. With it off, such
children run as plain DSH agents, exactly as before.
The mount is guaranteed for the child's first turn: prompt assembly and
tool execution wait for it, so even a child that runs a single turn sees the
full tool set. Each child is served exactly once, whichever path DSH created
it through — children the agent registry reports as runtime roots are served
by the engine's root mount path, and the subagent path serves the rest; the
two never overlap. Pi subagent-bridge children are unaffected either way:
they already receive the creator package's own per-spawn loader mount, and
the bridge recognizes them by the pi2dsh-sub- session-id prefix (stable
across a persisted resume), so no child is ever mounted twice.
The clearest example of what the bridge buys you. dsh-TUI ships a native
/mcp command for DSH's official MCP client — it works, and it stays
untouched. The Pi ecosystem has a much richer MCP power tool: a full-screen
server manager, lazy tool discovery, one proxy tool instead of flooding the
model context with dozens of tools, JavaScript orchestration of multiple MCP
calls, OAuth logins, resources and prompts. With the bridge, that package runs
unmodified.
dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui # skip if the profile exists
dsh plugin --profile dsh-tui add pi2dsh
dsh plugin --profile dsh-tui add pi-mcp-adapter
Or use the same unmodified Pi packages in dsh-pi-tui:
dsh plugin --profile pi-tui add @xmoon76/dsh-pi-tui
dsh plugin --profile pi-tui add pi2dsh pi-mcp-adapter @tintinweb/pi-subagents
dsh --profile pi-tui
That composition keeps dsh-pi-tui's native /login and projects Pi OAuth
flows into it, mounts the original MCP manager through the public
piTuiExtensions surface, and exposes the original Pi subagent flow at
/agents. See examples/pi-tui-ecosystem.
Then restart dsh — plugins mount at startup.
Inside dsh-TUI, run:
/pi-mcp setup
The setup flow can adopt MCP server definitions from host configs you already
have into the adapter's own standard mcp.json. No bridge-specific
configuration exists — everything you touch is the package's own surface.
/pi-mcp
opens the full-screen interactive server manager — its footer documents the
keys for enable/disable, reconnect and OAuth login. The model receives the
adapter's mcp and mcpScript tools through DSH's normal tool registry, and
each agent (/new included) gets its own fully connected instance.
dsh-TUI's native command remains separate, and both stay available:
/mcp # native DSH MCP-client status
/pi-mcp # the installed Pi adapter's manager
What is verified behind this walkthrough: 16 host-influenced capabilities
end to end on the stock npm stack — three real transports, discovery, proxy
and hot-loaded direct tools, mcpScript, resources, prompts, images becoming
real DSH attachments, MCP Apps, tool approval through DSH questions,
elicitation, sampling against the real DSH model runtime, cancellation and
session restart. The full evidence matrix:
docs/mcp-compatibility.md.
Full runnable version: examples/tui-mcp.
Two levels, and they are not the same claim.
Someone sat down, used the plugin's real feature on a real DSH loop, and saw it work. This is the list to trust.
| Plugin | What was exercised | Where | Example |
|---|---|---|---|
@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 |
@crazygit/pi-codex-image-gen |
ChatGPT/Codex OAuth → gpt-image-2 generation; local reference-image upload through DSH approval; image edit; native attachment storage and inline Web rendering |
CLI + web | codex-image-gen |
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 |
pi-powerline-footer |
A terminal status line — model, thinking level, project, context usage — drawn as a floating status pill in DSH web, colour included | web | presentation-surfaces |
pi-mcp-adapter |
Original full-screen manager; stdio/Streamable HTTP/SSE; discovery, direct/proxy/scripted calls, resources, prompts, images, structured content, MCP Apps, approval, elicitation, sampling, cancellation and restart through DSH runtimes | CLI + web + dsh-TUI + dsh-pi-tui | tui-mcp · mcp-at-scale · pi-tui-ecosystem · evidence matrix |
@tintinweb/pi-subagents |
The model delegates to autonomous subagents: spawn (with real host-native tools), background + completion notify, mid-run steer, wait-for-result, resume with memory, stop that stays stopped, interactive manager, and cross-restart reopen by archive identity — every child a native DSH session | CLI + dsh-TUI + dsh-pi-tui | subagents · pi-tui-ecosystem · acceptance report |
pi-provider-alibaba |
Alibaba Token Plan (CN) with its plan-specific key: live catalog, cold-start dynamic model, complete tool loop and restart. The package also declares Coding/API routes, but each requires its own non-interchangeable credential | CLI + web | alibaba-token-plan |
@ff-labs/pi-fff + pi-lens |
Code navigation for the model: ffgrep content search finds a marker that exists in exactly one file, and lsp_diagnostics reports a planted TS2322 through a real local typescript-language-server — both asserted from the tools’ own results in the session log |
CLI + web | code-navigation |
pi-vision-tool |
Tool registration through a JSON-Schema shape DSH had to convert (anyOf → oneOf) |
CLI + web | — |
pi-approval-guardian |
Every tool call reviewed by a second model before execution; allow and deny both observed | CLI (bare env) | — |
pi-hermes-memory |
Cross-session memory end to end: memory_add in one session, recalled in a second, fresh one (the codeword existed nowhere else); five tools, ten /memory-* commands, system-prompt memory injection |
CLI | persistent-memory |
pi-background-tasks |
Background shell job started with bg_run, output read mid-run with bg_logs: the log result carries early ticks of a still-running 60-second job, with the final tick provably absent |
CLI | background-tasks |
Examples for the two remaining rows are still to be written; per this project's own rule they get re-verified from scratch before an example lands, so the table says plainly which have one today.
The Pi catalog's top 50 packages by monthly downloads, each mounted in a
real DSH runtime and then called through a black-box probe. Status as of
2026-08-14; per-package machine-readable evidence in
community/.
47 of 50 exercised successfully · 1 with no probeable surface · 2 pending a re-run.
What this level does not tell you: that the plugin's actual feature works
the way you would use it. A probe calls a registered surface with synthetic
arguments; a user runs a workflow. pi-btw is the cautionary example — it
graded "working" here for weeks while /btw <question> failed on a real
session, because the feature needed two ABI gaps closed (Pi's settable
AgentState.messages, and an input descriptor on bridged commands) that no
probe exercised. Both are fixed in 0.11.0, and both were general fixes that
unlock every plugin doing the same thing.
So read the table below as "the bridge covers what this plugin touches", not as "this plugin is known-good". When you try one, a report either way is useful.
| Area | Packages |
|---|---|
| MCP | pi-mcp-adapter · pi-mcp-extension |
| Web search & fetch | pi-web-access · pi-deepseek-search · pi-web-search · @ollama/pi-web-search · @juicesharp/rpiv-web-tools |
| Code navigation & editing | pi-lens (ast-grep) · @narumitw/pi-lsp · pi-readseek · @ff-labs/pi-fff · pi-landstrip · pi-hashline-edit-pro¹ |
| Subagents & background work | @gotgenes/pi-subagents · @mjasnikovs/pi-task |
| Memory | pi-hermes-memory · pi-goosedump |
| Planning & goals | @narumitw/pi-goal · pi-goal-list-loop-audit · @narumitw/pi-plan-mode · @juicesharp/rpiv-todo |
| Asking you / approvals | @juicesharp/rpiv-ask-user-question · pi-ask-user · @gotgenes/pi-permission-system · @juicesharp/rpiv-advisor |
| Side conversations | pi-btw · @narumitw/pi-btw |
| Models & providers | pi-provider-litellm · pi-llama-cpp · pi-prompt-template-model · @vigolium/piolium |
| Images | @kassing/pi-vision (see above) · @amaster.ai/pi-image-gen |
| External integrations | @llblab/pi-telegram · pi-cursor-sdk² · @howaboua/pi-codex-conversion · pi-agent-browser-native² · pi-harness-runtime |
| Prompting & workflow | pi-simplify · pi-fabric² · mitsupi · pi-cc-extensions · pi-rtk-optimizer · pi-interview¹ |
| Terminal decoration | pi-powerline-footer · @narumitw/pi-statusline · pi-zentui |
| Voice | @juicesharp/rpiv-voice |
| Usage reporting | @alexanderfortin/pi-deepseek-usage³ |
¹ Mounts; the exercise run is pending a re-run (a harness-side failure, not a package or bridge gap). ² Ran its own business logic end to end and rejected the synthetic probe arguments — working, correctly validating. ³ A pure event-hook package: all subscriptions attach, but every handler is gated on a live DeepSeek billing session, so a black-box probe has nothing safely callable to assert.
Packages outside the top 50 are not a separate case — the bridge has no per-package code. If one hits an ABI gap, fixing that gap unlocks every package that shares it.
Level 1 grows by working through Level 2 one plugin at a time. The full verification ladder, with what each rung does and does not prove: support matrix.
Three layers, and nothing crosses them:
┌─ Pi plugin ─────────────────────────────────────────────────┐
│ unmodified npm package. It sees a complete Pi host: the │
│ three Pi runtime imports, registerX, ctx.*, 33 lifecycle │
│ events. It never learns DSH exists. │
└──────────────────────────┬──────────────────────────────────┘
│ Pi's public ABI
┌──────────────────────────▼──────────────────────────────────┐
│ pi2dsh — the translator, and the only place that knows both │
│ vocabularies. Registry projection, event bridge, session & │
│ subagent bridge, credentials, vendored Pi logic. │
└──────────────────────────┬──────────────────────────────────┘
│ ordinary DSH plugin + llm adapter
┌──────────────────────────▼──────────────────────────────────┐
│ DeepSeek Harness. Sees a normal plugin. Never learns Pi │
│ exists. │
└─────────────────────────────────────────────────────────────┘
DSH is two halves, and so is the bridge. The column above is the server; the browser shell has its own plugin surface, and a Pi capability that is a SHAPE rather than a behaviour lands there:
┌──────────── DSH server (cordis) ────────────┐ ┌──────── DSH browser shell ────────┐
│ services · waterfalls · durable events │ │ dsh.client + exports "./client" │
│ │ │ slot registry (ui-slots) │
│ pi2dsh engine │ │ shell.overlay ← panel, pills │
│ tools · commands · models · sessions │ │ session.header.utilities ← hdr │
│ subagent bridge ─────────────┐ │ │ overlay ← widget pills/cards │
│ browser-state registry │ │ │ composer.dock ← working/footer │
│ GET /pi2dsh/browser-state ─┼── own route┼──┼─▶ all four seats, one poller │
└────────────────────────────────┴────────────┘ └───────────────────────────────────┘
The browser half's data rides this package's own route, not DSH's typed
Remote system: that one is a first-party, code-generated contract, and an
out-of-tree plugin talking to its own UI should carry its own channel. One
payload per session serves every seat — the side-conversation panel, plus the
Pi presentation surfaces (status, widget, header, footer, title and the
working/thinking chrome), which are drawn in the host's own slot seats rather
than re-implemented. Two host rules make the browser half load at all — the
package must export ./package.json (the host resolves the manifest by
subpath), and the ./client bundle is a closure-factory artifact, not plain
ESM.
The rules that keep it honest:
dsh-mcp-client, skills to
dsh-skill-filesystem, and questions to DSH's user questions. An explicitly
installed Pi capability package can retain beh