Pi Web UI

A full-stack web interface for the Pi coding agent, providing browser access to a system-level Pi agent with bash, file read/write/edit, and extension tools.

Read the writeup on Sleeping Robots: Pi Web UI: A Browser Interface for the Pi Coding Agent.

Architecture

Browser (Lit + pi-web-ui components)
  │  WebSocket (/api/ws)
  ▼
Node.js server (Express + ws)
  │
  ▼
Session broker (runtime ownership and WebSocket attachments)
  │  Pi SDK (createAgentSession)
  ▼
System tools (bash, files, skills, extensions)
  • Server (server/index.ts): Owns broker runtimes that contain Pi SDK AgentSession instances with full system access. WebSocket clients attach to and detach from runtimes. A disconnect does not stop active work. Loads auth and model config from ~/.pi/agent through ModelRuntime. Model discovery can use LiteLLM.
  • Client (client/main.ts): Lit-based UI using @earendil-works/pi-web-ui components (MessageList, MessageEditor, StreamingMessageContainer). Communicates only through WebSocket.
  • Protocol (shared/protocol.ts): Typed JSON message definitions for the WebSocket wire format.

Setup

npm install
npm run build

Environment variables

Variable Default Description
PORT 3001 Server listen port
HOST 127.0.0.1 Server listen address. Use 0.0.0.0 only on a trusted network and with PI_WEBUI_TOKEN set.
PI_WEBUI_TOKEN (unset) Required by default shared secret for /api/ws and the skills editor API. If set, open /?token=<token> once on first load; the client stores it in localStorage and removes the query parameter from the URL.
PI_WEBUI_ALLOW_ANONYMOUS false Local development escape hatch only: tokenless mode requires this flag and a loopback HOST. Non-loopback/public binding always requires PI_WEBUI_TOKEN.
PI_WEBUI_ALLOWED_ORIGINS same host as request Additional comma-separated WebSocket Origin allowlist values.
PI_WEBUI_TITLE Pi Web UI Browser title and visible application name.
PI_CODING_AGENT_DIR ~/.pi/agent Pi agent config directory for auth, models, settings, sessions, and shared skills.
PI_WEBUI_SKILL_MAX_FILES 220 Max SKILL parse candidates per skill directory scan.
PI_WEBUI_SKILL_PARSE_BYTE_BUDGET 3000000 Max total bytes read while scanning skills for a list call.
PI_WEBUI_SKILL_FILE_MAX_BYTES 240000 Max bytes per individual skill file.
PI_WEBUI_SKILL_MAX_DEPTH 8 Max recursive directory depth while scanning skill directories.
PI_WEBUI_SKILL_DIRECTORY_ENTRY_LIMIT 1000 Max entries inspected in any one visited skill directory.
PI_WEBUI_SESSION_DIRECTORY_SCAN_LIMIT 800 Max directory entries considered for a session listing scan before sorting and pagination.
PI_WEBUI_SESSION_MAX_FILES 240 Max session files parsed per listing request.
PI_WEBUI_SESSION_PARSE_BYTE_BUDGET 6000000 Max total bytes read for session list parsing per request.
PI_WEBUI_SESSION_FILE_MAX_BYTES 220000 Max leading bytes read from each session for bounded listing/search; large sessions remain listable but searches do not inspect content beyond this prefix.
PI_WEBUI_USER_SKILLS_DIR ~/.pi/skills Private skills edited from /skills. With the default working directory this is loaded by Pi as a project-local skills directory.
PI_WEBUI_SHARED_SKILLS_DIR $PI_CODING_AGENT_DIR/skills Shared read-only skills listed in /skills and available to all agents using this agent dir.
PI_WEBUI_RESTRICT_MODELS_TO_SCOPED (unset) If true/yes/on/1, only models from PI_WEBUI_MODEL, PI_MODEL, settings.defaultProvider/defaultModel, or settings.enabledModels are listed/selectable.
PI_WEBUI_FORCE_MODEL_ALIAS_LABELS (unset) If true/yes/on/1, show configured model aliases (name) instead of raw provider/model ids in the UI and response metadata.
SESSION_LIST_LIMIT 30 Default sessions fetched per sidebar page.
SESSION_LIST_MAX_LIMIT 100 Maximum sessions accepted per sidebar page request.
MESSAGE_PAGE_LIMIT 60 Default newest messages sent for an opened conversation.
MESSAGE_PAGE_MAX_LIMIT 200 Maximum messages accepted per conversation page request.
SESSION_LIST_CACHE_MS 10000 Sessions sidebar cache TTL.
LITELLM_URL (empty) Optional LiteLLM API base URL for extra model listing.
LITELLM_KEY (from ~/.pi/agent) LiteLLM API key (auto-detected from Pi config if unset)

Development

npm run dev

Starts the server (tsx watch) on port 3001 and Vite dev server on port 5173 with WebSocket proxy.

Production

npm run build
HOST=127.0.0.1 PORT=8085 PI_WEBUI_TOKEN=change-me npm start

Features

  • Full Pi agent with system access (bash, read, edit, write, extensions)
  • Broker-owned sessions with persistence, history, reconnect support, and multi-tab observation
  • Session search, rename, deletion, stable session links, and paged loading
  • Model switching with scoped/default models first and an optional full model list
  • Optional scoped-only model selection and alias-only model labels
  • Configurable thinking level with saved session settings restored on resume
  • Startup resource summary for the model, session, tools, and advertised skills
  • Activity and compaction status during long turns
  • Prompt history through the Up and Down keys
  • Structured tool-result summaries and one-toggle Input and Result details
  • Authenticated previews and downloads for files in the agent home directory
  • Built-in skills manager at /skills for private and shared skills
  • Expanded-by-default responsive session sidebar and mobile top-bar controls
  • Dark/light theme toggle with green accent

Project structure

├── client/
│   ├── index.html        Entry point
│   ├── main.ts           Client application (state, WebSocket, rendering)
│   └── app.css           Theme overrides and sidebar styles
├── server/
│   ├── index.ts          Express + WebSocket server and Pi SDK adapter
│   └── session-broker.ts Runtime ownership, attachments, commands, and cleanup
├── shared/
│   └── protocol.ts       WebSocket message type definitions
├── vite.config.ts        Vite config (build, dev proxy)
├── tsconfig.json         TypeScript config
└── package.json          Dependencies and scripts
Languages
TypeScript 90.5%
CSS 9.4%
HTML 0.1%