Files
paperclip/packages
Jannes Stubbemann 6e4aca9c67 feat(pi-local): env-driven gateway routing via PAPERCLIP_PI_PROVIDERS models.json (#7920)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `pi-local` adapter runs the Pi coding agent, including inside
remote/sandboxed execution targets; Pi resolves `--provider P --model M`
by an exact (provider, id) match against its model registry, and it has
no base-url CLI flag or env var: a `models.json` in its agent config dir
(`$PI_CODING_AGENT_DIR`, falling back to `$HOME/.pi/agent`) is its only
mechanism for custom or OpenAI/Anthropic-compatible endpoints
> - Deployments increasingly put an LLM gateway between the harness and
the model for cost, governance, or data-residency reasons: LiteLLM,
OpenRouter, Portkey, Kong, a corporate proxy, self-hosted models
(vLLM/Ollama), or region-pinned/sovereign endpoints. Today there is no
supported way to get such provider config into Pi's registry for
orchestrated runs
> - The opencode adapter gained the equivalent capability in #7837 and
codex in #7919; this pull request is the Pi analogue, so the harness
layer stays gateway-agnostic regardless of which CLI an agent uses;
nothing here is specific to one hosting setup
> - This pull request reads `PAPERCLIP_PI_PROVIDERS` (Pi's `models.json`
`providers` shape), materialises a managed `models.json` in a temp
agent-config dir, points `PI_CODING_AGENT_DIR` at it, and ships it to
remote execution targets with the run
> - The benefit is Pi works behind any compatible gateway with config
only; with no env set, behavior is unchanged

## Linked Issues or Issue Description

No existing issue; describing in-PR (feature / adapter enhancement).

- **Gap:** there is no supported way to register custom/gateway
providers + models for `pi-local`. Pi's only custom-endpoint mechanism
is a `models.json` in its agent config dir, and orchestrated (especially
sandboxed) runs have no way to provision one declaratively.
- Related: #7837 (the opencode-local analogue, same env-driven
gateway-routing pattern) and #7919 (the codex-local analogue). Searched
for duplicate or related PRs: no existing pi-local
gateway/provider-routing PR found.

> Note on ROADMAP: this is adapter-level, opt-in config (defaults
unchanged) that *enables* gateway routing for one harness; it is not the
core "Cloud / Sandbox agents" platform work itself.

## What Changed

- New `packages/adapters/pi-local/src/server/runtime-config.ts`:
`preparePiRuntimeConfig()` reads `PAPERCLIP_PI_PROVIDERS` (a JSON object
in pi's `models.json` `providers` shape) from the run env, then
`process.env`. When set, it expands `{env:VAR}` placeholders (run env
first, then process env; unresolvable placeholders left intact), writes
`{"providers": ...}` to a managed temp dir as `models.json`, and returns
env with `PI_CODING_AGENT_DIR` pointing at it plus a cleanup handle.
- `execute.ts`: the prepared dir ships to remote execution targets as
the managed-runtime asset `agentConfig` (same mechanism as opencode's
`xdgConfig`), and `PI_CODING_AGENT_DIR` is repointed to the in-target
path; cleanup runs in `finally`.
- Misconfiguration is visible, not silent: a set-but-unusable
`PAPERCLIP_PI_PROVIDERS` (invalid JSON, not an object, no provider
objects) surfaces an explanatory note instead of proceeding unconfigured
into an opaque model-not-found failure later, and provider entries with
non-object values are skipped with a note naming them. Unset/empty stays
a silent no-op (feature off).
- Defaults unchanged: with `PAPERCLIP_PI_PROVIDERS` unset, the adapter
behaves byte-for-byte as before, for local runs and for every existing
sandbox provider.

## Verification

- All pi-local tests green against this base (new: providers written
verbatim, `{env:VAR}` expansion from run env/process env/unresolvable,
no-op when unset, `PI_CODING_AGENT_DIR` set and shipped, the
misconfiguration notes incl. skipped non-object entries, remote asset
sync + env repoint). Typecheck and build clean.
- Production end-to-end evidence (our deployment, used as verification,
not as the scope of the change): a pi agent in a Kubernetes gVisor
sandbox resolved a custom provider from the shipped `models.json`,
completed an assigned issue through an Anthropic-compatible gateway, and
landed a billed usage row.

## Risks

Low. The entire feature is opt-in behind one env var; the only behavior
change when it is set is the intended one. The managed dir replaces the
host agent dir for the run by design (credentials travel inside the
provider config or via env-key indirection), which is the correct
posture for orchestrated runs. No migration/UI impact.

## Model Used

Claude Opus 4.8 (`claude-opus-4-8`, 1M context), extended thinking +
tool use, via Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md (adapter-level opt-in config enabling
gateway routing; not the core sandbox-platform work, noted above)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (#7837 and #7919 are the opencode/codex analogues; no
pi-local duplicate found)
- [x] I have either (a) linked existing issues OR (b) described the
issue in-PR following the relevant issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (n/a, no UI)
- [ ] I have updated relevant documentation to reflect my changes (env
var documented inline; no central doc references the adapter env yet)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green (green on the previous head;
re-running on the final note-copy polish commit)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(both prior review findings are fixed at head: the indirect notes-based
guard is now an explicit `agentConfigDir` handle, and a failed
`models.json` write no longer leaks the temp dir; a re-review is
requested for the note-copy polish)
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 21:12:23 -07:00
..