# PRD: Pi Clean Seam Refactor

**Author:** Tom Boucher
**Date:** 2026-04-14
**ADR:** [ADR-010-pi-clean-seam-architecture.md](./ADR-010-pi-clean-seam-architecture.md)
**Priority:** High — blocks safe pi-mono upstream updates

---

## Problem Statement

SF is built on top of pi-mono, an open-source agent framework maintained by Mario Zechner at [github.com/badlogic/pi-mono](https://github.com/badlogic/pi-mono). SF takes pi-mono as vendored source copies — four packages (`pi-agent-core`, `pi-ai`, `pi-tui`, `pi-coding-agent`) are copied directly into `/packages/` and modified in-place.

This worked as a starting point but has created a structural problem: **SF-authored code lives inside the pi packages**. The 98KB `agent-session.ts`, the compaction system, three run modes (interactive, RPC, print), the CLI utilities, and the `createAgentSession()` factory are all authored by SF but stored inside `pi-coding-agent`. Approximately 79 SF-authored TypeScript files are mixed in with pi's upstream source.

The consequence is that every pi-mono update requires manually diffing SF's modifications against the incoming upstream changes file-by-file. There is no reliable way to tell which files are SF's and which are pi's without reading them. Updates that should take hours become multi-day archaeology projects. Pi-mono is currently 10 versions behind upstream (0.57.1 vs 0.67.2 as of April 2026), with a blocking API change (`session_switch`/`session_fork` removal in v0.65.0) unresolved.

Beyond update pain, there is a project risk: if pi-mono stops being maintained or changes direction, SF's business logic is entangled with a dependency it no longer controls.

## Vision

SF's code is clearly separated from pi's code at the module system level. The vendored pi packages contain only upstream code (plus the extension system, which is intentionally pi-typed). SF's agent logic lives in SF-owned packages that **depend on** pi but do not live inside it. When a new pi release comes out, a maintainer updates the vendored pi packages, runs the TypeScript compiler, and fixes the errors that surface in the SF packages — without ever needing to diff individual files to find what's ours vs. theirs.

## Success Criteria

| Criterion | Measurement |
|-----------|-------------|
| Zero SF business logic in vendored pi packages | `pi-coding-agent/src/` contains no files that import from `@sf/` packages (except the extension system's bundled module map) |
| Module boundary is compiler-enforced | TypeScript `paths` config or package `exports` prevents pi packages from importing SF packages |
| Applying a pi-mono update is scoped | Updating pi packages produces type errors only in `@sf/agent-core` and `@sf/agent-modes` — no changes required in pi package source files |
| Install experience is unchanged | `npm install -g sf-run@latest` produces an identical binary from the user's perspective |
| Existing extensions continue to work | All built-in SF extensions load and execute without modification |
| Build time does not regress significantly | Full build completes within 120% of current baseline |

## Non-Goals

- **Not** moving pi packages from vendored source to npm dependencies (that is a potential Phase 2)
- **Not** creating an abstraction layer that hides pi types from SF code — SF packages may freely use pi's `AgentMessage`, `Model`, `TUI`, etc.
- **Not** upstreaming SF's modifications to pi-mono (desirable long-term but out of scope)
- **Not** changing the published npm package name, install command, or any user-facing CLI behavior
- **Not** removing or replacing the extension system — it stays in `pi-coding-agent` and remains typed against pi's types

## Stakeholders

- **Maintainers applying pi updates** — primary beneficiary; this work directly reduces their update burden
- **Extension authors** — must not be broken; the extension API surface stays in `@sf/pi-coding-agent`
- **End users** — not impacted; the refactor is entirely internal

## Requirements

### R1 — New package: `@sf/agent-core`

A new workspace package at `packages/sf-agent-core/` that owns all SF session orchestration logic. It depends on `@sf/pi-coding-agent`, `@sf/pi-agent-core`, and `@sf/pi-ai`. Nothing in the vendored pi packages depends on it.

Must contain:
- `agent-session.ts` and all `AgentSession` types
- `compaction/` (orchestrator, branch summarization, utilities)
- `system-prompt.ts`
- `bash-executor.ts`
- `fallback-resolver.ts`
- `lifecycle-hooks.ts`
- `image-overflow-recovery.ts`
- `contextual-tips.ts`
- `keybindings.ts`
- `sdk.ts` (the `createAgentSession()` factory — the primary public API of this package)
- `artifact-manager.ts`, `blob-store.ts`
- `export-html/`

### R2 — New package: `@sf/agent-modes`

A new workspace package at `packages/sf-agent-modes/` that owns all run-mode and CLI code. It depends on `@sf/agent-core`, `@sf/pi-coding-agent`, and `@sf/pi-tui`. It is the layer the top-level `sf-run` binary entry point assembles.

Must contain:
- `modes/interactive/` (full TUI interactive mode and all components)
- `modes/rpc/` (RPC server, RPC client, JSON protocol)
- `modes/print/` (print and machine-surface output)
- `cli/` (arg parsing, config selector, session picker, model lister, file processor)
- `main.ts` entry point logic

### R3 — `pi-coding-agent` contains only upstream code and the extension system

After the migration, the vendored `pi-coding-agent` source must not contain files that:
- Import from `@sf/agent-core` or `@sf/agent-modes`
- Contain SF business logic (compaction, session management, run modes, CLI)

The extension system (`src/core/extensions/`) remains in `pi-coding-agent` because it is legitimately pi-typed: extension authors write against pi's `AgentMessage`, `Model`, and `TUI` types. The virtual module map in `extensions/loader.ts` must be updated to include `@sf/agent-core` and `@sf/agent-modes` so extensions can import from them.

### R4 — Public API surfaces are explicit

Each new package must have an `index.ts` that declares its public API. Internal files must not be imported by path from outside the package. Specifically:
- `web/bridge-service.ts` currently imports `AgentSessionEvent` from an internal path in `pi-coding-agent` — this must be fixed to use the public export from `@sf/agent-core`
- Any other internal-path imports identified during migration must be fixed

### R5 — Build order is updated

The workspace build script must be updated to build packages in dependency order:
1. `@sf/pi-agent-core`, `@sf/pi-ai`, `@sf/pi-tui` (parallel, no dependencies between them)
2. `@sf/pi-coding-agent`
3. `@sf/agent-core`
4. `@sf/agent-modes`
5. `sf-run` (top-level binary)

### R6 — No change to the extension loader's public interface

Extensions are loaded by `pi-coding-agent`'s jiti-based loader. The virtual module map (`STATIC_BUNDLED_MODULES`) must be updated to resolve `@sf/agent-core` and `@sf/agent-modes` alongside the existing pi package mappings. This requires both a map entry and a top-level bundle import in `loader.ts` (see ADR-009 for the exact diff). Extension authors must not need to change their import paths.

## Open Questions

1. Does `clearQueue()` on `AgentSession` need to be added to a public type export, or is it already accessible to the auto-mode extension that uses it?
2. Does `buildSessionContext()` on `SessionManager` need a public re-export from `@sf/agent-core`?
3. Should `@sf/agent-modes` re-export `createAgentSession()` as a convenience, or should consumers always import it from `@sf/agent-core` directly?
