pi-anchored-tool-for-dspro
Pi extension that anchors a target model's first request to a minimal tool catalog (shell + read), then restores the full catalog after the first tool call. For DeepSeek V4 Pro and other models that condition on the API-visible tool list (Standard/PTC 91/
Package details
Install pi-anchored-tool-for-dspro from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-anchored-tool-for-dspro- Package
pi-anchored-tool-for-dspro- Version
0.4.0- Published
- Aug 15, 2026
- Downloads
- 356/mo · 8/wk
- Author
- dbydd
- License
- MIT
- Types
- extension
- Size
- 68.1 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-anchored-tool-for-dspro
A pi extension that conditions DeepSeek V4 models before the first provider request with two independent, per-model programs:
- Anchor (default for
deepseek-v4-pro): expose a minimal tool catalog (bash+read) on the first request, promote to the full catalog after the first durable tool call, and rewrite the harness system prompt to the DSH minimal-mode persona for the whole session. - Router (configured for
deepseek-v4-flash): classify the first user message into the measuredspec / weak / reactbehavior bands, inject the matching persona and band-specific first-turn core tools, then promote to the full catalog after the first tool call.
The anchor is a port of
xiaobright/dsh-anchored-standard;
the router is a port of the pure routing core in
yjh051108/dsh-router-standard
(part of dsh-routing-suite).
Both run on pi's serialized provider payload.
This is an experimental prompt-conditioning patch, not a correctness guarantee. The underlying benchmarks are personal evaluations, not universal claims. No network requests, no telemetry.
⚠️ Code injection notice — read before enabling extensionlessLaunchers
When
anchoredTools.extensionlessLaunchersis enabled, this plugin injects JavaScript code into Node child processes started by the current pi session. This is deliberate, in-process, and scoped, but it is still code injection and must not be a surprise.
Why it injects: pi has no assembly hook for child pi processes that are
launched with --no-extensions (pi-goal-list-loop-audit's detached auditor,
pi-subagents workers, and potentially future plugins). Those children never
load this package, so the anchor/router payload rewrite cannot run there. pi
does still honor an explicit --extension path under --no-extensions, so
the injection's only job is to add that explicit path for exactly those
children.
Injection paths:
| # | Path | What happens |
|---|---|---|
| 1 | NODE_OPTIONS |
The plugin sets --import=data:text/javascript,... containing a tiny bootstrap. Every Node child process that inherits the session env runs it before its own code. |
| 2 | process.argv |
The bootstrap appends -e <this-package>/src/index.ts before pi's main() runs — only when the process is a pi CLI invoked with --no-extensions / -ne. |
| 3 | node:child_process |
The bootstrap (and the current process, best-effort) patches spawn / spawnSync / execFile so a worker that spawns pi with a stripped env still gets the explicit extension arg. |
Scope and safeguards:
- Inactive by default:
extensionlessLaunchersdefaults tofalse. - The preload only modifies pi CLI invocations that already carry
--no-extensions/-ne; ordinarypicalls and unrelated Node programs are passed through unchanged. - Ambient extension discovery stays disabled in those children — only this package is force-loaded.
- Nothing is written to disk, no PATH is modified, no launcher env vars are set, and no network traffic is added.
- The embedded bootstrap is generated from
src/inject.ts, so its full source is visible and auditable in this repository. - This plugin is a pi extension: installing it already grants arbitrary code execution inside pi. The injection adds no new privilege; it only makes that code reach the extension-less child pi processes described above.
To disable: set "extensionlessLaunchers": false in anchoredTools and
/reload the session.
Why this exists
The modeltest Project2 evaluation (DeepSeek V4 Pro, reasoningEffort=max) showed:
| Preset | Ability (run1/run2) | let me count |
Tool catalog |
|---|---|---|---|
| Standard | 91 | 208 | full (25 tools) |
| PTC | 92 | 194 | run_code |
| Minimal | 99 / 96 | 0 / 0 | 2 tools |
| Anchored Standard | 98 / 99 | 1 / 0 | 2 tools first, then full 25 |
The pattern: V4 Pro's RL-aligned scaffold reacts badly to a wide tool catalog
on the first request (high let me, degraded planning), but performs at its
best with the two-tool Minimal catalog. Staying on Minimal permanently gives
up the Standard tool set — hence the two-phase trick:
- First model request → expose only one shell +
read(anchors the Minimal trajectory). - After the first durable tool call → expose every tool (no capability is ever lost).
The trigger mechanism experiments also isolated the system prompt as a
co-equal variable: the minimal-mode persona is a codex-style one-liner
(You are a helpful software engineer assistant.), and rewording it breaks
the We need reasoning style (paraphrase runs went standard-like). The
persona is therefore kept byte-identical to the Harness minimal preset and
stays in place for the whole session — only the tool catalog promotes.
Both runs of the DSH preset emitted exactly two tool-catalog snapshots
(bootstrap, then full), and the second run had zero let me across the whole
trajectory — evidence it is not luck-of-the-draw.
Full methodology, trajectory statistics, and SHA-256 evidence:
xiaobright/modeltest
(docs/v4.1/DEEPSEEK_V4_TRIGGER_MECHANISM_EXPERIMENTS_20260814.md,
evaluator/trajectory_evidence/).
What this extension does in pi
pi has no system-prompt/assemble hook, so both programs work one layer
lower — on the serialized provider payload handed to
before_provider_request. Returning a replacement payload is the actual
request pi sends.
Anchor program
The DSH plugin hooks system-prompt/assemble and filters the tool catalog
before the model sees it:
before_provider_request: for a configured target model,payload.toolsis filtered down to the bootstrap set before the first durable tool call, andpayload.messages[0](or top-levelpayload.system) is rewritten to the DSH minimal persona on every request of the session. Returning a replacement payload is the actual request pi sends.- After the first durable tool call (a
toolResultmessage or an assistant message containing atoolCall), the tool filter stops firing and the full catalog flows through untouched — the minimal persona stays. - The phase is derived from session entries, not memory —
/resumeand/reloadpreserve it automatically, exactly like the DSH preset derives promotion from durable session events.
Semantics match the DSH plugin precisely:
- The minimal persona is permanent (like
complete: truein the Harness preset): only the tool catalog changes phase. - First response makes no tool call → no promotion.
- A failed tool execution still promotes (a durable tool call exists).
- The catalog changes exactly once per session (one request-prefix cache discontinuity, same as DSH).
Differences from the DSH plugin
| dsh-anchored-standard | pi-anchored-tool-for-dspro | |
|---|---|---|
| Host | DeepSeek Harness (Cordis) | pi (ExtensionAPI) |
| Hook layer | system-prompt/assemble |
before_provider_request (payload) |
| Phase source | session.events (tool/call) |
sessionManager.buildContextEntries() |
| Platform shell | bash or pwsh (one per platform) |
bash (pi's unified shell tool) |
| Minimal persona | preset persona row, complete: true |
payload system-message rewrite, permanent |
| Config failure | throws (loud fail) | warns + skips (fail safe) |
| Config | preset YAML | pi settings.json, multi-level override |
Reasoning-mode router (from dsh-routing-suite)
Borrowed from
yjh051108/dsh-router-standard
(dsh-routing-suite): task-aware routing on the measured react ↔ spec
behavior axis. The DSH probe (21 mode points × n=2 on V4 Pro) found the axis
collapses into three stable bands plus a model-specific weak region, so
the classifier quantizes instead of pretending the axis is continuous:
| band | mode | behavior | first-turn pi core tools |
|---|---|---|---|
spec |
0 |
plan-first, collective (We, let-me ≈ 0) |
read, edit, grep, find |
mixed |
0.3 |
transition trap (never auto-selected) | union: read, edit, write, grep, find |
react |
1 |
doer, produce-verify-fix, test ceremony suppressed | read, write, edit |
weak |
"weak" |
model decides per task (classify-then-act) | (react default, see coreTools) |
The platform shell (bash/pwsh) is added to whatever core set is chosen,
exactly like the DSH plugin. What was ported:
- keyword classifier (
react/spec, ties/unmatched →weak); - band quantization and the four personas, including the model-specific weak
personas —
w7for flash (neutral + classify + recall/anti-runaway anchors),w6cfor pro (spec sentence + classify, no anchors); - first-turn core tool filtering per band, with promotion to the full catalog after the first durable tool call;
- depth-adaptive near-field guidance for weak mode (
isComplexTaskpicks a fast-convergence guide or a decision-closure deep guide), appended once per real user message on the serialized payload; parseModeand thedev_router_status/dev_router_modesurface, exposed here as the/anchored-routecommand.
Deliberately not ported: DSH's applyPersona section surgery (pi has no
section model — we rewrite the front system message instead, the same trade
as the anchor program) and dev_mode_subagent (pi's ExtensionAPI has no
equivalent in-process LLM stream API).
The router is configured as a separate program keyed by model globs, so it can be bound to any model without touching the anchor configuration. The suggested setup is anchor on V4 Pro, router on V4 Flash:
{
"anchoredTools": {
"models": ["deepseek-v4-pro"],
"router": {
"enabled": true,
"models": ["deepseek-v4-flash"],
"mode": "auto",
"guidance": true
}
}
}
If a model matches both models and router.models, the router wins.
Install
Via npm (once published):
pi install pi-anchored-tool-for-dspro
The package itself has no runtime dependency on Bun or on a specific package
manager: pi installs it through its own package machinery and loads the
TypeScript extension with jiti under Node. npm, pnpm, and yarn users only
differ in how the (dev) dependencies are fetched; the shipped src/ code is
identical.
Or clone this repo and add it in ~/.pi/agent/settings.json:
{ "packages": ["git:github.com/dbydd/pi-anchored-tool-for-dspro@v0.1.0"] }
Or copy src/ into ~/.pi/agent/extensions/anchored-tools/. Run /reload
in pi after installing.
Config
Configuration lives in pi's settings.json under the top-level
anchoredTools key, with the same multi-level override semantics pi uses
for its own settings:
- Global
~/.pi/agent/settings.jsonis the base. - A trusted project's
.pi/settings.jsondeep-merges over it — nested objects merge recursively, arrays are replaced wholesale (never concatenated),undefinedskips, project values win. - Re-read on every request, so edits take effect immediately.
// ~/.pi/agent/settings.json (global base)
{
"anchoredTools": {
"enabled": true,
"models": ["deepseek-v4-pro"],
"bootstrapTools": ["bash", "read"],
"minimalSystemPrompt": true,
"notify": true,
"extensionlessLaunchers": true,
"router": {
"enabled": true,
"models": ["deepseek-v4-flash"],
"mode": "auto",
"guidance": true
}
}
}
// .pi/settings.json (project override — replaces whole arrays for this project)
{
"anchoredTools": {
"models": ["claude-*"],
"bootstrapTools": ["bash", "read", "ffgrep", "fffind"],
"minimalSystemPrompt": false,
"notify": false,
"extensionlessLaunchers": false,
"router": {
"enabled": false,
"models": [],
"mode": "auto",
"guidance": true
}
}
}
enabled— master switch.models— glob patterns matched againstprovider/modelIdand baremodelId. Patterns containing/match the qualified form only ("deepseek/*","*/deepseek-v4-pro"). Empty array = no model anchored (safe default).bootstrapTools— tools exposed on the anchored first request. Must be a subset of the catalog; if a name is missing from the catalog the filter fails safe (skips with a logged warning) rather than stripping tools.minimalSystemPrompt— rewrite the harness-injected system prompt (the front of the context) to the DSH minimal persona (You are a helpful software engineer assistant., byte-identical to the Harnessminimalpreset) on every request for a target model. Setfalseto keep pi's default system prompt. The text is deliberately not configurable: the evaluation showed rewording the persona breaks the intended reasoning style.notify— show one TUI notice when a session is promoted.extensionlessLaunchers— when true, this plugin injects code into pi processes that other packages launch with--no-extensions, from inside the plugin (see the ⚠️ notice near the top and the section below). Defaultfalse.router.enabled— master switch for the reasoning-mode router. Defaultfalse.router.models— model globs for the router program. Default[]. Bind it to["deepseek-v4-flash"](or any other model) while the anchormodelslist keeps handling V4 Pro.router.mode— fixed route or"auto"(classify the first user message). Accepts the DSHparseModevocabulary:spec,weak,mixed,react,0-100,0.0-1.0. Default"auto".router.guidance— append the depth-adaptive near-field guidance on weak sessions. Defaulttrue.router.personas— optional per-band persona overrides (spec,mixed,react,weak,weakFlash). Defaults are the byte-identical DSH router strings.router.coreTools— optional per-band first-turn tool overrides (spec,mixed,react); the shell tool is always added automatically. Missing names are tolerated for the router (pi catalogs vary per launcher), unlike the anchor's strict fail-safe.
Extension-less child pi processes
When extensionlessLaunchers: true, this plugin injects code — see the
injection notice at the top of this README before enabling it.
pi's own payload hook is an extension hook, so a child pi started with
--no-extensions never runs this package and a fallback deepseek-v4-pro
auditor keeps pi's built-in system prompt. Several packages do this —
pi-goal-list-loop-audit for its detached auditor and pi-subagents for its
workers are the known ones today; the mechanism below is deliberately generic
so future plugins with the same pattern are covered without code changes.
With extensionlessLaunchers: true the extension installs the injection from
inside the plugin — no wrapper file, no PATH mutation, no launcher env vars:
NODE_OPTIONSdata-URL preload. The extension extendsNODE_OPTIONSwith--import=data:text/javascript,...carrying a tiny, dependency-free bootstrap. Every Node child process — GLLA's detached auditor worker, pi-subagents workers, or any future plugin's worker — runs that bootstrap before its own code.- pi argv rewrite. When the bootstrap detects that the process is a pi
CLI launched with
--no-extensions/-ne, it appends one explicit-e <this-package>/src/index.tstoprocess.argvbefore pi'smain()runs. pi still honors explicit--extensionpaths under--no-extensions, so exactly this extension loads while ambient discovery stays disabled. - child_process patch (defense-in-depth). The bootstrap also patches
spawn/spawnSync/execFileinside the child process for cases where a worker spawns pi with an env that dropsNODE_OPTIONS. The same patch is applied to the current process on a best-effort basis.
The original child arguments are preserved exactly — --no-extensions,
--no-session, --no-approve, the tool allowlist — plus the explicit
--extension for this package. The bootstrap is embedded in the environment
as a data URL, so nothing is ever written to disk. The result in those
children is:
- the matching program applies in the child session: anchor models get the
DSH minimal persona +
bash/readbootstrap, router models get their classified band persona + band core tools, and both promote to the full catalog after the first durable tool call (with--no-session, pi's in-memory session supplies the phase); - project-level
.pi/settings.jsonis not consulted in children that pass--no-approve, so enableanchoredToolsin the global~/.pi/agent/settings.jsonfor those launchers; - the injection relies on
NODE_OPTIONSsurviving into the pi child; for workers that explicitly rebuildenvwithout spreadingprocess.env, the in-processchild_processpatch is the fallback;
To pick up a settings change, /reload the owning pi session and restart or
re-trigger the detached auditor/subagent.
Verify
Run /anchored-tools inside pi — it reports the active program
(anchor / router / none), the current model, the phase
(bootstrap, routed <band>, or promoted), the router band and persona,
and the minimal system-prompt / extensionless-launcher switches. The console
log shows one line per session when an anchor or router persona is first
rewritten, plus one line when the first request catalog is filtered.
Run /anchored-route to inspect or override the current router session:
/anchored-route # status: mode, band, persona, core tools
/anchored-route react # force the doer band for this session
/anchored-route weak # internal-routing band (model classifies)
/anchored-route 0.3 # mixed band (explicit opt-in only)
/anchored-route auto # clear the override, back to classification
Development
Zero runtime dependencies. One pinned dev dependency (tsx); tests use
Node's built-in test runner (node:test + node:assert), so the same
commands work under npm, pnpm, and yarn:
npm test # = npx tsx --test test/core.test.ts test/router.test.ts test/inject.test.ts
npm test:bun # optional: run the same files with bun test
Requires Node >= 22.19.0 (pi's own engine floor, which also guarantees
process.getBuiltinModule for the extensionless-launcher patch). The package
lockfile is committed for npm users; pnpm/yarn resolve the same pinned tsx
version from package.json.
License
MIT. Concept ported from
xiaobright/dsh-anchored-standard
(MIT), itself derived from the DeepSeek Harness Standard preset, and the
router core ported from
yjh051108/dsh-router-standard
(MIT, part of dsh-routing-suite) — see the upstream projects for the
original notices. This package is not affiliated with or endorsed by DeepSeek
or the pi project.