Files
PiAgentUI/docs/superpowers/plans/2026-05-28-pi-agent-extension-migration.md
T
Evandro b3489e1ede
Build & Push Frontend Docker Image / build-and-push (push) Canceled after 0s
Build Validation / validate (push) Canceled after 6s
Build & Push Backend Docker Image / build-and-push (push) Failing after 10m10s
Build & Push Gateway Docker Image / build-and-push (push) Failing after 10m9s
🎉 Initial commit: PiAgentUI — AI Agent Desktop & Web UI
A professional workspace for AI coding agents with:
- Real-time streaming chat with SSE
- Session management (fork, clone, tree navigation)
- MCP tools browser & metadata panel
- Skills browser (global, project, package sources)
- Voice input & STT provider configuration
- Integrated terminal & file explorer
- Model selector with capability metadata
- Keyboard-first UI with command palette
- 8 languages (en, pt-BR, es, ja, zh-CN, ar, bn, hi)
- Tauri 2 desktop app
- React 19 + TypeScript + Vite 8 + Tailwind CSS 4
- Pi Agent extension runtime

Originally forked from OpenCodeUI, now an independent project.
License: GPL-3.0-only
2026-06-01 00:50:06 -03:00

846 lines
28 KiB
Markdown

# Pi Agent Extension Migration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Migrate PiAgentUi from OpenCode to Pi Agent while preserving the current UI and interaction model.
**Architecture:** Build a Pi package/extension as the primary integration. Pi loads `extensions/piagentui-server.ts`, which starts a loopback local server, serves the existing React build, and exposes an owned compatibility API shaped to the current frontend. Tauri/Rust desktop service cleanup is a second track after the Pi package MVP works.
**Tech Stack:** React 19, Vite 8, TypeScript 5.9, Vitest, Tauri 2/Rust, Pi extension API (`@earendil-works/pi-coding-agent`), Node HTTP, SSE/WebSocket event streaming.
---
## Decisions Required Before Implementation
Default recommendations are listed; please confirm or override them before code changes.
1. **MVP session model:** recommended = active Pi session mirror first. The UI controls the currently running Pi session; full session CRUD/fork/switch is deferred.
2. **Desktop/Tauri scope:** recommended = defer process spawning. Tauri can connect to an already-running Pi extension server later, but Track A must not depend on Tauri.
3. **Unsupported features:** recommended = disable/stub PTY, MCP, LSP, provider OAuth, worktrees, and multi-session operations until Pi equivalents are verified.
4. **Token/bootstrap:** recommended = same-origin injected boot data in HTML served by the extension; bearer token stays in memory, never query strings/localStorage.
5. **Endpoint convention:** recommended = keep OpenCode-shaped endpoint paths/envelopes where the current frontend already expects them, then gradually move wrappers to owned names internally.
6. **Product name:** needs user decision. Options: `PiAgentUi`, `Pi Agent UI`, or another exact name.
Implementation should not begin until these are confirmed.
---
## Non-Negotiable Scope
- No UI redesign.
- No `/hud` command.
- No `Ctrl+Shift+H` HUD toggle.
- Remove OpenCode runtime usage completely by final migration.
- Remove `@opencode-ai/sdk` by final migration.
- Reuse user Pi settings/auth/model setup; do not collect provider credentials in PiAgentUi.
- Use `better-tau/extensions/mirror-server.ts` as the main reference for a Pi extension serving HTTP/WebSocket from inside Pi.
- Current known `better-tau` unrelated changes must not be touched: `.gitignore`, `extensions/helper-bridge.ts`, `extensions/helper/`.
---
## Core Corrections from Critical Review
- **Do not conflate Pi package and Tauri app.** Track A is Pi package/extension; Track B is desktop cleanup.
- **Do not flatten message DTOs.** Current UI expects `MessageWithParts { info, parts }`, and many callsites read `message.info.*`.
- **Do not emit raw event envelopes unless `events.ts` is rewritten first.** Current frontend accepts `{ directory, payload: { type, properties } }` from `/global/event`.
- **Use actual Pi lifecycle events.** The shutdown event is `session_shutdown`, not `session_end`.
- **Typecheck extension files.** Existing `tsc -b` does not include `extensions/**/*.ts`.
- **Resolve token bootstrap early.** The UI cannot call protected APIs unless it receives base URL/token safely.
- **Treat `pi.sendUserMessage(...)` as asynchronous fire-and-stream.** It should return `202 accepted`; real message IDs arrive through events.
---
## File/Module Responsibility Map
### New files
- `extensions/piagentui-server.ts`
- Pi extension entrypoint.
- Starts loopback HTTP/SSE/WS server.
- Serves built `dist/` assets.
- Injects safe runtime boot data into `index.html`.
- Bridges Pi session lifecycle and messages to current frontend-compatible events.
- `extensions/piagentui-server-core.ts`
- Pure helpers for testable server behavior: loopback validation, auth validation, body-size guard, path traversal prevention, event envelope helpers.
- `extensions/piagentui-server-core.test.ts`
- Unit tests for server security helpers.
- `tsconfig.extension.json`
- TypeScript project for extension files and extension tests/helpers.
- `src/api/piClient.ts`
- Typed fetch client replacing SDK usage.
- Sends local bearer auth in headers only.
- Normalizes typed errors.
- `src/api/piClient.test.ts`
- Request/auth/error behavior tests.
- `src/types/api/compat.ts`
- Owned compatibility DTOs shaped for current UI.
- `src/api/piContract.ts`
- Owned request/response contracts and current event envelope definitions.
- `src/api/piEventTranslator.test.ts`
- Contract tests for frontend event processing.
- `docs/superpowers/specs/2026-05-28-pi-agent-extension-migration-design.md`
- Design spec, endpoint matrix, unsupported features, security, acceptance criteria.
### Modified files
- `package.json`
- Add `pi.extensions`.
- Add package metadata needed for Pi package usage.
- Add extension typecheck script.
- Add/remove dependencies in the correct sequence.
- Remove `@opencode-ai/sdk` when no imports remain.
- `tsconfig.json`
- Include `tsconfig.extension.json` in references.
- `vite.config.ts`
- Remove OpenCode dev proxy when API layer no longer uses OpenCode server.
- Add Pi extension dev proxy only if chosen.
- `src/constants/api.ts`, `src/api/http.ts`, `src/store/serverStore.ts`
- Convert from OpenCode server/basic-auth concepts to PiAgentUi local server boot/connection state.
- No provider credential storage.
- `src/api/sdk.ts`
- Delete after all imports are removed, or temporarily replace with a non-SDK compatibility facade.
- `src/api/*.ts`
- Replace SDK calls with owned client calls or unsupported-feature stubs.
- Files likely in scope: `agent.ts`, `client.ts`, `command.ts`, `config.ts`, `file.ts`, `global.ts`, `lsp.ts`, `mcp.ts`, `message.ts`, `permission.ts`, `pty.ts`, `session.ts`, `skill.ts`, `todo.ts`, `tool.ts`, `vcs.ts`, `worktree.ts`.
- `src/types/api/*.ts`
- Replace SDK-derived type aliases with owned DTOs or explicit unsupported types.
- Files likely in scope: `agent.ts`, `common.ts`, `config.ts`, `event.ts`, `file.ts`, `mcp.ts`, `message.ts`, `model.ts`, `permission.ts`, `project.ts`, `pty.ts`, `session.ts`, `skill.ts`, `tool.ts`, `vcs.ts`, `worktree.ts`.
- `src/main.tsx`, `src/features/settings/components/ServiceSettings.tsx`, `src/hooks/useCloseServiceDialog.ts`, `src/store/serviceStore.ts`
- Track A: ensure OpenCode auto-start does not run while Pi extension mode is active.
- Track B: rename desktop service integration.
- `src-tauri/src/app/commands/opencode.rs`, `src-tauri/src/app/commands/mod.rs`, `src-tauri/src/app/mod.rs`
- Track B only: rename commands and remove OpenCode process management.
- `src-tauri/src/app/bridge/args.rs`, `src-tauri/src/app/commands/bridge.rs`
- Track B only: enforce loopback URL allowlist in Rust bridge.
- `src-tauri/capabilities/default.json`, `src-tauri/tauri.conf.json`, `src-tauri/Cargo.toml`
- Track B only: remove OpenCode branding and tighten security.
---
## Compatibility Contract
### DTO shape must match current UI
Current UI expects message shape similar to:
```ts
export interface MessageWithParts {
info: Message
parts: Part[]
}
```
Therefore owned DTOs must preserve this shape first, then adapt internally:
```ts
export interface CompatMessageInfo {
id: string
sessionID: string
role: 'user' | 'assistant'
time?: { created?: number; completed?: number }
summary?: string
modelID?: string
providerID?: string
}
export interface CompatMessageWithParts {
info: CompatMessageInfo
parts: CompatPart[]
}
export type CompatPart =
| CompatTextPart
| CompatReasoningPart
| CompatToolPart
| CompatFilePart
| CompatStepStartPart
| CompatStepFinishPart
| CompatUnsupportedPart
export interface CompatTextPart {
id: string
sessionID: string
messageID: string
type: 'text'
text: string
}
export interface CompatReasoningPart {
id: string
sessionID: string
messageID: string
type: 'reasoning'
text: string
}
export interface CompatToolPart {
id: string
sessionID: string
messageID: string
type: 'tool'
tool: string
state: { status: 'pending' | 'running' | 'completed' | 'error'; input?: unknown; output?: unknown; error?: string }
}
export interface CompatFilePart {
id: string
sessionID: string
messageID: string
type: 'file'
mime?: string
filename?: string
url?: string
}
export interface CompatStepStartPart {
id: string
sessionID: string
messageID: string
type: 'step-start'
}
export interface CompatStepFinishPart {
id: string
sessionID: string
messageID: string
type: 'step-finish'
}
export interface CompatUnsupportedPart {
id: string
sessionID: string
messageID: string
type: 'unsupported'
label: string
data?: unknown
}
```
Exact fields must be finalized by a field inventory before implementation.
### Event envelope must match current frontend first
Current `src/api/events.ts` expects:
```ts
export interface CompatGlobalEvent {
directory?: string
payload: {
type: string
properties: unknown
}
}
```
For MVP, the extension should emit this envelope on the current path or the frontend parser must be rewritten before server events are implemented.
Recommended MVP: keep `/global/event` and OpenCode-style event names already handled by the UI:
- `server.connected`
- `session.updated`
- `session.status`
- `session.idle`
- `session.error`
- `message.updated`
- `message.part.updated`
- `message.part.delta`
- `message.part.removed`
- `permission.asked` only if mapped; otherwise unsupported notification
- `question.asked` only if mapped; otherwise unsupported notification
For text deltas, current store expects payload shape compatible with its `PartDeltaPayload`, including `sessionID`, `messageID`, `partID`, `field`, and `delta`.
### Send message contract
Current frontend send params include:
```ts
interface SendMessageParams {
sessionId: string
text: string
attachments: Attachment[]
model: { providerID: string; modelID: string }
agent?: string
variant?: string
directory?: string
}
```
MVP behavior:
- `text`: supported.
- `directory`: supported only as metadata/active project hint if Pi context allows it.
- `model`: support only if Pi allows model switch before send; otherwise ignore with warning event or reject with unsupported error.
- `attachments`: defer unless image/file input mapping is verified.
- `agent`, `variant`: defer unless Pi equivalent is verified.
- response: `202 accepted` plus optional synthetic client request ID; real assistant message ID is learned from events.
---
## Endpoint Coverage Matrix
### MVP supported
| Capability | Existing frontend area | Compatibility path | Backend source |
| ----------------------- | --------------------------------- | ---------------------------------------------------- | ------------------------------------- |
| Health | `serverStore`, `client.ts` | `/global/health` or current health path | extension runtime state |
| Event stream | `events.ts` | `/global/event` | Pi extension lifecycle/events |
| Active models | `client.ts`, model hooks | wrapper-owned endpoint, final path decided in Task 1 | Pi model registry/settings |
| Active session snapshot | `session.ts`, stores | wrapper-owned endpoint, OpenCode-shaped response | active Pi context/session mirror |
| Send message | `message.ts`, `useChatSession.ts` | wrapper-owned endpoint | `pi.sendUserMessage(...)` |
| Abort turn | message/session controls | wrapper-owned endpoint | current Pi context abort if available |
| Static assets | browser/webview | `/`, `/assets/*` | extension static server |
### Explicitly unsupported/deferred for MVP
- PTY terminal sessions.
- MCP management.
- LSP diagnostics endpoints.
- Provider OAuth/login.
- Full multi-session create/fork/switch/delete.
- Worktrees.
- VCS operations unless trivially provided by current UI independent of OpenCode.
- Permission/question flows unless Pi extension UI/RPC gives equivalent semantics.
- Share/export unless Pi exposes safe API from extension context.
- Config, project, agent, skill, todo, tool, file APIs that currently depend on OpenCode SDK.
Every deferred feature must return a typed unsupported error and must not call an OpenCode endpoint.
---
## Security Contract
Required for Track A:
- Bind only to `127.0.0.1` and optionally `::1`.
- Use OS-selected random high port by default.
- Generate per-run bearer token.
- Bootstrap token via same-origin injected boot data in served HTML, unless user chooses another mechanism.
- Keep bearer token in memory/session only; do not store it in `localStorage` or settings backups.
- Reject API and stream requests without `Authorization: Bearer <token>`.
- For browser-native SSE/WS where custom headers are constrained, prefer same-origin cookie/header strategy from extension-served page; do not put token in query strings or userinfo URLs.
- Validate `Host` and `Origin`.
- No wildcard CORS.
- Limit request body and WS frame sizes.
- Reject static path traversal.
- Never send provider API keys/OAuth tokens to browser.
- Never log local bearer token, provider secrets, or prompts by default.
- Close HTTP server and sockets on `session_shutdown`.
Required for Track B if touched:
- Rust bridge must validate URLs server-side, not only via Tauri capabilities.
- Allow only `http://127.0.0.1:*`, `http://localhost:*`, `ws://127.0.0.1:*`, `ws://localhost:*` as appropriate.
- Reject userinfo URLs, LAN IPs, non-loopback hosts, and external HTTPS URLs.
- Narrow `src-tauri/capabilities/default.json` from broad `http://**` / `https://**` to loopback-only.
- Reintroduce CSP instead of `csp: null` if compatible with current app.
---
## Validation Contract
Run during implementation milestones:
```bash
cd PiAgentUi
npm run typecheck
npm run lint
npm run test:run
npm run build
```
After extension tsconfig is added:
```bash
cd PiAgentUi
npm run typecheck:extensions
```
If Track B is touched:
```bash
cd PiAgentUi/src-tauri
cargo test
```
OpenCode cleanup check must use an allowlist because docs may mention migration history:
```bash
cd PiAgentUi
rg -n "@opencode-ai/sdk|getSDKClient|unwrap\(|opencode serve|OpenCode|opencode" src src-tauri extensions package.json vite.config.ts
```
Expected final runtime result:
- no `@opencode-ai/sdk` imports;
- no `opencode serve` launcher;
- no OpenCode user-facing app title;
- only temporary deprecated aliases if Track B explicitly keeps them for one release.
Pi package smoke:
```bash
cd PiAgentUi
npm run build
npm pack --dry-run
```
Then install/load the package in Pi using the local package workflow chosen for this repo and verify:
1. Pi loads the extension.
2. Extension serves the UI.
3. UI receives boot data.
4. UI sends a message to current Pi session.
5. Assistant response streams into existing chat UI.
6. Refresh/reconnect recovers current snapshot.
7. Invalid HTTP/SSE/WS auth is rejected.
---
## Implementation Tasks
### Task 1: Confirm Decisions and Write Design Spec
**Files:**
- Create: `docs/superpowers/specs/2026-05-28-pi-agent-extension-migration-design.md`
- [ ] Confirm all decisions in “Decisions Required Before Implementation”.
- [ ] Write the design spec with: MVP, non-goals, endpoint matrix, DTO/event contract, security contract, validation contract, unsupported-feature behavior, Track A/Track B split.
- [ ] Include exact better-tau reference: `D:/Documents/Github/PiAgentUI/better-tau/extensions/mirror-server.ts`.
- [ ] Run:
```bash
cd PiAgentUi
npm run typecheck
```
Expected: unchanged project still typechecks before migration starts.
### Task 2: Inventory Current Frontend API Fields and SDK Coupling
**Files:**
- Create: `docs/superpowers/specs/2026-05-28-pi-agent-ui-field-inventory.md`
- [ ] Run:
```bash
cd PiAgentUi
rg -n "@opencode-ai/sdk|getSDKClient|unwrap\(|from './sdk'|from '../api/sdk'|Client\[|SDK[A-Za-z]" src
```
- [ ] Inventory all files importing SDK-derived types or `getSDKClient`.
- [ ] Inventory consumed fields for:
- `Session`
- `MessageWithParts`
- `Message.info`
- every `Part` variant rendered by components/stores
- `GlobalEvent`
- `PartDeltaPayload`
- `SendMessageParams`
- [ ] Mark each API module as: supported, stubbed, hidden, or deferred.
- [ ] Do not edit runtime code in this task.
### Task 3: Add Owned Compatibility Types Without Breaking UI Shape
**Files:**
- Create: `src/types/api/compat.ts`
- Create: `src/api/piContract.ts`
- Modify: `src/types/api/message.ts`, `src/types/api/event.ts`, `src/types/api/session.ts` only after inventory confirms exact fields.
- [ ] Add owned compatibility DTOs preserving current UI shape.
- [ ] Replace SDK aliases gradually with owned DTO aliases that match current field names.
- [ ] Do not flatten `MessageWithParts`.
- [ ] Keep `GlobalEvent` envelope compatible with current `events.ts` unless rewriting `events.ts` in the same task.
- [ ] Run:
```bash
cd PiAgentUi
npm run typecheck
npm run test:run -- src/store/messageStore.test.ts src/api/events.test.ts src/hooks/useChatSession.test.tsx
```
Expected: failures only from intentional type differences fixed before proceeding.
### Task 4: Add Extension Typecheck and Package Metadata
**Files:**
- Create: `tsconfig.extension.json`
- Modify: `tsconfig.json`
- Modify: `package.json`
- [ ] Add `tsconfig.extension.json` including `extensions/**/*.ts`.
- [ ] Add script:
```json
"typecheck:extensions": "tsc -p tsconfig.extension.json --noEmit"
```
- [ ] Include `typecheck:extensions` in `validate` after extension files exist.
- [ ] Add Pi package metadata:
```json
"pi": {
"extensions": ["./extensions/piagentui-server.ts"]
}
```
- [ ] Add package contents/prepack strategy so `dist/` and `extensions/` are included when packed/installed.
- [ ] Decide dependency strategy for `@earendil-works/pi-coding-agent`:
- runtime should be supplied by Pi package loader if supported;
- local typecheck still needs a devDependency or path-compatible type resolution.
- [ ] Run:
```bash
cd PiAgentUi
npm run typecheck
npm run typecheck:extensions
npm pack --dry-run
```
### Task 5: Implement Extension Server Core Helpers and Tests
**Files:**
- Create: `extensions/piagentui-server-core.ts`
- Create: `extensions/piagentui-server-core.test.ts`
- [ ] Write tests first for:
- loopback host accepted;
- LAN/external host rejected;
- userinfo URL rejected;
- malformed auth rejected;
- valid bearer auth accepted;
- Origin/Host mismatch rejected;
- static path traversal rejected;
- body-size guard rejects oversized body;
- boot data injection does not put token in URL.
Use test token construction that is obviously fake and not a credential:
```ts
const sampleToken = ['test', 'token', 'value'].join('-')
```
- [ ] Implement pure helpers to pass tests.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run -- extensions/piagentui-server-core.test.ts
npm run typecheck:extensions
```
### Task 6: Implement Pi Extension Server Skeleton
**Files:**
- Create: `extensions/piagentui-server.ts`
- Modify: extension tests/helpers as needed
- [ ] Implement extension entrypoint importing `ExtensionAPI` from `@earendil-works/pi-coding-agent`.
- [ ] Start server on `session_start` or the earliest correct lifecycle event.
- [ ] Cleanup on `session_shutdown`.
- [ ] Bind loopback on random high port.
- [ ] Serve `dist/index.html`, injecting boot data same-origin.
- [ ] Serve `/assets/*` safely.
- [ ] Implement authenticated health endpoint compatible with frontend/serverStore path decision.
- [ ] Run:
```bash
cd PiAgentUi
npm run typecheck:extensions
npm run build
npm pack --dry-run
```
### Task 7: Replace Basic Auth Server Store with Pi Local Boot State
**Files:**
- Modify: `src/store/serverStore.ts`
- Modify: `src/store/serverStore.test.ts`
- Modify: `src/api/http.ts`
- Modify: `src/constants/api.ts`
- [ ] Remove provider/server password persistence from active Pi mode.
- [ ] Store only base URL and in-memory/session-scoped local token.
- [ ] Add migration tests proving old Basic auth/password values are not exported in backups after migration.
- [ ] Ensure auth headers use bearer token and never query strings.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run -- src/store/serverStore.test.ts src/api/http.test.ts
npm run typecheck
```
### Task 8: Add `piClient` and Replace SDK Wrapper Entry Points
**Files:**
- Create: `src/api/piClient.ts`
- Create: `src/api/piClient.test.ts`
- Modify: `src/api/client.ts`
- Modify: `src/api/session.ts`
- Modify: `src/api/message.ts`
- [ ] Write `piClient` tests for bearer headers, no token in URL, JSON errors, base URL normalization.
- [ ] Implement `get`, `post`, `delete`, and stream helper if needed.
- [ ] Replace model/session/message wrappers first because they are MVP-supported.
- [ ] Preserve existing exported function names consumed by hooks/components.
- [ ] For send message, return accepted state if Pi returns no message ID; rely on events for message updates.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run -- src/api/piClient.test.ts src/hooks/useSessions.test.tsx src/hooks/useChatSession.test.tsx src/hooks/useModelSelection.test.tsx
npm run typecheck
```
### Task 9: Implement Pi Event Translation Compatible with Existing `events.ts`
**Files:**
- Modify: `extensions/piagentui-server.ts`
- Create: `src/api/piEventTranslator.test.ts`
- Modify: `src/api/events.ts` only if necessary and covered by tests.
- [ ] Add tests for the current envelope `{ directory, payload: { type, properties } }`.
- [ ] Add tests for `message.part.delta` shape expected by `messageStore`.
- [ ] Map Pi events to existing frontend events:
- turn start -> `session.status` running;
- turn end -> `session.idle` or `session.status` idle;
- message update -> `message.updated`;
- part delta/update -> `message.part.delta` / `message.part.updated`;
- tool events -> tool part updates;
- errors -> `session.error`.
- [ ] Add initial snapshot event on connect.
- [ ] Add reconnect behavior using existing generation/snapshot logic.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run -- src/api/events.test.ts src/api/sse.test.ts src/api/piEventTranslator.test.ts src/store/messageStore.test.ts
npm run typecheck
```
### Task 10: Gate or Stub All Unsupported OpenCode-Only Features
**Files:**
- Modify: remaining `src/api/*.ts`
- Modify: affected hooks/components only when they crash on unsupported errors.
- [ ] Run:
```bash
cd PiAgentUi
rg -n "getSDKClient|unwrap\(|@opencode-ai/sdk|opencode|OpenCode" src
```
- [ ] For every remaining API module, implement one of:
- supported Pi-backed wrapper;
- typed unsupported error;
- hidden/disabled UI action with existing notification style.
- [ ] Cover at least:
- `agent.ts`
- `command.ts`
- `config.ts`
- `file.ts`
- `global.ts`
- `lsp.ts`
- `mcp.ts`
- `permission.ts`
- `pty.ts`
- `skill.ts`
- `todo.ts`
- `tool.ts`
- `vcs.ts`
- `worktree.ts`
- [ ] Ensure `pty.ts` never embeds credentials/tokens in URLs.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run
npm run typecheck
```
### Task 11: Prevent OpenCode Auto-Start in Pi Extension Mode
**Files:**
- Modify: `src/main.tsx`
- Modify: `src/store/serviceStore.ts`
- Modify: `src/features/settings/components/ServiceSettings.tsx`
- Modify: `src/hooks/useCloseServiceDialog.ts`
- [ ] Detect Pi extension mode from boot data.
- [ ] In Pi extension mode, do not auto-start `opencode serve`.
- [ ] Hide/disable OpenCode service settings without changing layout structure more than necessary.
- [ ] Do not rename desktop commands yet unless Track B is approved.
- [ ] Run:
```bash
cd PiAgentUi
npm run test:run -- src/store/serviceStore.test.ts src/utils/settingsBackup.test.ts
npm run typecheck
```
### Task 12: Remove `@opencode-ai/sdk` and SDK Facade
**Files:**
- Modify: `package.json`, lockfile if present
- Delete or rewrite: `src/api/sdk.ts`
- Modify: tests that mock SDK
- [ ] Confirm no imports remain:
```bash
cd PiAgentUi
rg -n "@opencode-ai/sdk|getSDKClient|unwrap\(" src
```
- [ ] Remove dependency using the repo's package manager:
```bash
cd PiAgentUi
npm uninstall @opencode-ai/sdk
```
If package manager/lockfile state is unclear, stop and ask.
- [ ] Delete `src/api/sdk.ts` if unused.
- [ ] Update tests to mock `piClient` or API wrappers instead of SDK.
- [ ] Run:
```bash
cd PiAgentUi
npm run validate
npm run typecheck:extensions
```
### Task 13: Track B — Tauri Desktop Cleanup and Security Hardening
Only start after Track A smoke passes and user approves desktop scope.
**Files:**
- Rename: `src-tauri/src/app/commands/opencode.rs` -> `src-tauri/src/app/commands/pi_agent.rs`
- Modify: `src-tauri/src/app/commands/mod.rs`
- Modify: `src-tauri/src/app/mod.rs`
- Modify: `src-tauri/src/app/bridge/args.rs`
- Modify: `src-tauri/src/app/commands/bridge.rs`
- Modify: `src/store/serviceStore.ts`
- Modify: `src-tauri/capabilities/default.json`
- Modify: `src-tauri/tauri.conf.json`
- Modify: `src-tauri/Cargo.toml`
- [ ] Decide desktop behavior: connect to existing Pi extension server, spawn `pi --mode rpc`, or defer.
- [ ] Rename service commands to Pi names if desktop remains.
- [ ] Keep old command aliases for one transition release only if required by existing frontend code.
- [ ] Migrate storage keys:
- `opencode-auto-start-service` -> `pi-agent-auto-start-service`
- `opencode-binary-path` -> `pi-agent-binary-path`
- `opencode-service-env-vars` -> `pi-agent-service-env-vars`
- [ ] Add Rust-side URL allowlist tests for bridge connect/send paths.
- [ ] Narrow Tauri HTTP permissions to loopback-only.
- [ ] Reintroduce CSP if compatible.
- [ ] Run:
```bash
cd PiAgentUi
npm run typecheck
npm run test:run
cd src-tauri
cargo test
```
### Task 14: Final Product Metadata and OpenCode Cleanup
**Files:**
- Modify: `package.json`
- Modify: `src-tauri/Cargo.toml`
- Modify: `src-tauri/tauri.conf.json`
- Modify: README/docs/user-facing strings
- [ ] Apply user-approved final product name.
- [ ] Update package name/description.
- [ ] Update Tauri product name/window title/identifier if Track B is in scope.
- [ ] Remove OpenCode user-facing strings.
- [ ] Run allowlisted grep:
```bash
cd PiAgentUi
rg -n "@opencode-ai/sdk|getSDKClient|unwrap\(|opencode serve|OpenCode|opencode" src src-tauri extensions package.json vite.config.ts README.md
```
Expected: no runtime/user-facing OpenCode references except approved temporary aliases.
- [ ] Run full validation:
```bash
cd PiAgentUi
npm run validate
npm run typecheck:extensions
npm run build
npm pack --dry-run
```
If Track B is in scope:
```bash
cd PiAgentUi/src-tauri
cargo test
```
---
## Review Checklist for Implementation Milestones
After Tasks 6, 9, 12, and 13, run a critical review pass before continuing:
- Correctness/regression review against this plan.
- Test coverage and validation review.
- Security review for local HTTP/SSE/WS, token handling, and Tauri bridge if touched.
- Simplicity review to catch unnecessary OpenCode compatibility that can be deleted.
The reviewer output should be synthesized before any fixes are applied. Apply fixes with one writer only.
---
## Current Recommended Milestone Split
1. **Milestone 1:** decisions + design spec + field inventory.
2. **Milestone 2:** owned DTOs + extension typecheck/package metadata.
3. **Milestone 3:** secure extension server skeleton + boot data.
4. **Milestone 4:** model/session/message wrappers + event streaming.
5. **Milestone 5:** unsupported feature gating + SDK removal.
6. **Milestone 6:** Pi package smoke and final OpenCode cleanup.
7. **Milestone 7:** optional Tauri desktop cleanup/hardening.