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/

Packages

Package details

extension

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:

  1. 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.
  2. Router (configured for deepseek-v4-flash): classify the first user message into the measured spec / weak / react behavior 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.extensionlessLaunchers is 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: extensionlessLaunchers defaults to false.
  • The preload only modifies pi CLI invocations that already carry --no-extensions / -ne; ordinary pi calls 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:

  1. First model request → expose only one shell + read (anchors the Minimal trajectory).
  2. 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.tools is filtered down to the bootstrap set before the first durable tool call, and payload.messages[0] (or top-level payload.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 toolResult message or an assistant message containing a toolCall), 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 — /resume and /reload preserve 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: true in 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 — w7 for flash (neutral + classify + recall/anti-runaway anchors), w6c for 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 (isComplexTask picks a fast-convergence guide or a decision-closure deep guide), appended once per real user message on the serialized payload;
  • parseMode and the dev_router_status / dev_router_mode surface, exposed here as the /anchored-route command.

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.json is the base.
  • A trusted project's .pi/settings.json deep-merges over it — nested objects merge recursively, arrays are replaced wholesale (never concatenated), undefined skips, 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 against provider/modelId and bare modelId. 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 Harness minimal preset) on every request for a target model. Set false to 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.
  • extensionlessLauncherswhen 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). Default false.
  • router.enabled — master switch for the reasoning-mode router. Default false.
  • router.models — model globs for the router program. Default []. Bind it to ["deepseek-v4-flash"] (or any other model) while the anchor models list keeps handling V4 Pro.
  • router.mode — fixed route or "auto" (classify the first user message). Accepts the DSH parseMode vocabulary: spec, weak, mixed, react, 0-100, 0.0-1.0. Default "auto".
  • router.guidance — append the depth-adaptive near-field guidance on weak sessions. Default true.
  • 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:

  1. NODE_OPTIONS data-URL preload. The extension extends NODE_OPTIONS with --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.
  2. 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.ts to process.argv before pi's main() runs. pi still honors explicit --extension paths under --no-extensions, so exactly this extension loads while ambient discovery stays disabled.
  3. child_process patch (defense-in-depth). The bootstrap also patches spawn / spawnSync / execFile inside the child process for cases where a worker spawns pi with an env that drops NODE_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/read bootstrap, 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.json is not consulted in children that pass --no-approve, so enable anchoredTools in the global ~/.pi/agent/settings.json for those launchers;
  • the injection relies on NODE_OPTIONS surviving into the pi child; for workers that explicitly rebuild env without spreading process.env, the in-process child_process patch 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.