pi-model-replacements

Model alias/tier (low/medium/high) replacement extension for pi coding agent

Packages

Package details

extension

Install pi-model-replacements from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-model-replacements
Package
pi-model-replacements
Version
0.3.1
Published
Jun 9, 2026
Downloads
not available
Author
fractiunate
License
MIT
Types
extension
Size
1.6 MB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ],
  "image": "https://raw.githubusercontent.com/fractiunate/pi-model-replacement/main/hero-image.png"
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-model-replacements

hero

A standalone pi extension that lets agents, subagents, and prompt templates reference abstract model tiers (low / medium / high, or any alias name) instead of hard-coding provider/model-id. A single change in settings.json re-points every template and subagent.

How it works

Add a model_replacements map to the active agent dir's settings.json (the dir selected by PI_CODING_AGENT_DIR, e.g. ~/.pi/agent/settings.json):

{
  "model_replacements": {
    "low": "github-copilot/claude-haiku-4.5",
    "medium": "github-copilot/claude-sonnet-4.6",
    "high": "github-copilot/claude-opus-4.8"
  }
}

Now any model: low in a prompt-template frontmatter, an agent definition, or a subagent tool call resolves to the concrete model — no file writes, no edits to other extensions.

Mechanism

  1. session_start — each alias is injected into the live model registry as a synthetic model cloned from its concrete target (grouped per provider, idempotent across /reload). Unresolvable targets warn once and are skipped.
  2. model_select + initial swap — when an alias becomes the active model, it is swapped to the concrete Model object via pi.setModel. Self-terminating (the target is never an alias key, so no loop).
  3. tool_call — an explicit subagent model: "high" argument is rewritten in place to the concrete provider/model-id before the child process spawns.
  4. before_agent_start — a per-turn safety net that swaps any still-active alias immediately before the API call (covers the agent-frontmatter path and child startup ordering). The same extension code runs fresh inside subagent child processes.

Configuration resolution

PI_CODING_AGENT_DIR is honoured exactly like pi-subagents: ~ / ~/... are expanded; otherwise the literal value is used; default is ~/.pi/agent. Reading is fail-soft — a missing or malformed settings.json, or absent model_replacements, yields no aliases (extension is a no-op).

Installation

Install via pi:

pi install npm:pi-model-replacements

Development

Setup

npm install

Testing

npm test   # tsx --test *.test.ts

Works well with

Contracts and limitations

Thinking-suffix handling

pi-model-replacements resolves aliases by exact key match. A suffixed string such as "high:thinking" does not match alias key "high" directly, so the tool_call handler splits the trailing :thinking suffix, resolves the base alias ("high") to its concrete target, and re-attaches the suffix — producing e.g. "github-copilot/claude-opus-4.8:thinking".

This is required because pi-subagents' resolveModelCandidate() only matches the base model id against the live registry. Since pi-model-replacements injects each alias as a synthetic registry entry, passing "high:thinking" through unresolved would make pi-subagents select the synthetic alias id ("github-copilot/high"), which is not a real API model and fails with 400 model not supported. The alias must therefore be resolved to its concrete target here, before the child spawns.

This behaviour is locked by a regression test in tool-call.test.ts ("subagent tool_call: suffixed alias ('high:thinking') resolves base alias and re-attaches suffix").

Unsupported: alias chaining

A configuration like { "low": "medium", "medium": "provider/model" } — where an alias target is itself another alias key — is not supported. buildAliasMap detects chained entries at map-build time, emits a console.warn, and skips them. Only direct alias → "provider/model-id" mappings are accepted.

Code note: getAgentDir() duplication

alias-resolver.ts:getAgentDir() is a character-for-character copy of pi-subagents/src/shared/utils.ts:getAgentDir(). Both are intentionally kept separate to avoid cross-package coupling. Any future change to path-expansion logic (e.g., Windows normalisation) must be applied to both files.


Made with ♥ by fractiunate