SF vendors four packages from [pi-mono](https://github.com/badlogic/pi-mono) (an open-source coding agent framework) by copying their source directly into `/packages/`:
Vendoring was chosen over npm dependencies to allow SF to modify the upstream packages freely. However, over time, SF has written substantial original logic directly inside `pi-coding-agent` — approximately 79 files including:
This SF-authored code is mixed in with upstream pi code inside the same package. The pi packages are currently 10 versions behind upstream (0.57.1 vs 0.67.2), with a breaking API change from v0.65.0 (`session_switch`/`session_fork` removal) unresolved. The primary obstacle to applying updates is that there is no reliable way to distinguish SF files from pi files without reading them individually.
Pi-mono does publish to npm as `@mariozechner/pi-*`. Moving to npm dependencies would eliminate vendoring entirely, but it is blocked by:
1.`@gsd/native` bindings are imported directly inside the vendored pi-tui and pi-coding-agent source — the upstream npm packages do not have these imports
2. ~50 direct source modification commits to the vendored packages since March 2026 would need to be evaluated individually
3. The upstream extension API (~25 events) is a subset of SF's extension system (~50+ events) — the delta would need to be re-architected before the move
Introduce two new workspace packages that own all SF-authored code currently living inside `pi-coding-agent`. The vendored pi packages become close-to-upstream source copies. SF code depends on pi; pi code does not depend on SF.
**Purpose:** SF's session orchestration layer. Owns the `AgentSession` class, compaction, bash execution, system prompt construction, and the `createAgentSession()` factory that wires everything together.
**Key dependency note:** `agent-session.ts` imports pi types directly (`Agent`, `AgentEvent`, `AgentMessage`, `AgentState`, `AgentTool`, `ThinkingLevel` from `@gsd/pi-agent-core`; `Model`, `Message` from `@gsd/pi-ai`). This is intentional — SF's session layer is pi-typed, not abstracting over pi. This makes the seam a clear seam, not an abstraction.
**Purpose:** SF's run-mode and CLI layer. Assembles the agent session (from `@gsd/agent-core`) with a specific interface: interactive TUI, headless RPC server, or print output. Contains the `main()` entry point logic invoked by the `gsd` binary.
The extension system remains here because it is legitimately pi-typed. Extensions subscribe to pi events (`session_start`, `tool_execution_start`, `model_select`, etc.) and receive pi types in their handlers. Moving the extension system out of `pi-coding-agent` would require re-expressing those types in SF terms, which is the abstraction-layer work explicitly out of scope for this phase.
`src/core/extensions/loader.ts` maintains a `STATIC_BUNDLED_MODULES` map of packages that extensions can import at runtime. After the migration, `@gsd/agent-core` and `@gsd/agent-modes` must be added to this map so that extensions importing those packages continue to resolve correctly in compiled Bun binaries:
```typescript
// Before (current)
const STATIC_BUNDLED_MODULES = {
"@gsd/pi-agent-core": _bundledPiAgentCore,
"@gsd/pi-ai": _bundledPiAi,
"@gsd/pi-tui": _bundledPiTui,
"@gsd/pi-coding-agent": _bundledPiCodingAgent,
// ...
}
// After
const STATIC_BUNDLED_MODULES = {
"@gsd/pi-agent-core": _bundledPiAgentCore,
"@gsd/pi-ai": _bundledPiAi,
"@gsd/pi-tui": _bundledPiTui,
"@gsd/pi-coding-agent": _bundledPiCodingAgent,
"@gsd/agent-core": _bundledGsdAgentCore, // NEW
"@gsd/agent-modes": _bundledGsdAgentModes, // NEW
// ...
}
```
---
## How Pi Updates Work After This Change
1. Download the new pi-mono release for the four vendored packages
2. Copy the upstream source into `packages/pi-agent-core/`, `pi-ai/`, `pi-tui/`, `pi-coding-agent/`
- Do not touch `packages/gsd-agent-core/` or `packages/gsd-agent-modes/`
3. Run `tsc --noEmit` (or the build) across the workspace
4. Fix type errors in `@gsd/agent-core` and `@gsd/agent-modes` only
5. If upstream changed the extension event API, fix extension system integration in `pi-coding-agent/src/core/extensions/`
Steps 2-5 are scoped to known files. No archaeology required.
---
## Known Issues to Fix During Migration
| Issue | Location | Fix |
|---|---|---|
| Internal-path import of `AgentSessionEvent` | `src/web/bridge-service.ts` | Import from `@gsd/agent-core` public export |
| `clearQueue()` not in typed public API | `AgentSession` | Add to public interface in `@gsd/agent-core/index.ts` |
| `buildSessionContext()` on `SessionManager` | Used by SF code, not publicly exported | Evaluate: re-export from `@gsd/agent-core` or remove dependency |
| Deprecated `session_switch`, `session_fork`, `session_directory` usage | 2+ files in `pi-coding-agent` | Migrate to `session_start` with `reason` field (required for v0.65.0 compat) — can be done as part of or after clean seam work |
Move everything into one package instead of two. Simpler dependency graph but creates a large package where session logic and TUI logic share a build unit. Rejected because headless/RPC use cases would pull in the TUI unnecessarily, and the two concerns have meaningfully different consumers.
### Directory convention within `pi-coding-agent` (no new packages)
Add a `src/gsd/` subdirectory inside `pi-coding-agent` to clearly mark SF files without creating new packages. Fastest to implement but the seam is a convention, not enforced by the module system. A future accidental cross-import would not be caught by the compiler. Rejected because the enforcement value of proper packages is worth the modest extra setup.
Take `@mariozechner/pi-*` from npm and skip vendoring entirely. Blocked by `@gsd/native` imports baked into the vendored source, ~50 direct source modification commits, and the upstream extension API gap. Deferred to Phase 2.
---
## Implementation Notes
The migration should proceed in this order to maintain a working build at each step:
1.**Audit** — identify all imports of `pi-coding-agent` internal paths (non-index) and document them
2.**Create packages** — scaffold `gsd-agent-core` and `gsd-agent-modes` with `package.json` and empty `index.ts`
3.**Move files in batches** — start with leaf files (no downstream dependents within pi-coding-agent), work toward `agent-session.ts` last
4.**Fix imports incrementally** — TypeScript will identify broken imports after each batch
5.**Update extension loader** — add new packages to virtual module map
6.**Update build script** — insert new packages in dependency order
7.**Verify** — full build, existing tests pass, `gsd --version` works
The pi update to v0.67.2 (and the deprecated API migration) can be done as a follow-on once the clean seam is in place, since that work will be dramatically simpler with the new structure.