Project map
Where things are in Pi Pocket, for agents working on it. index.md lists every guide; AGENTS.md and self-editing.md have the rules for editing it while it runs; architecture.md explains how the parts depend on each other and the orderings they rely on; features.md says what each feature does.
Processes
Section titled “Processes”bin/pi-pocket.js checks Node, then runs src/launcher/main.ts. The launcher reads its options (options.ts), asks how devices connect (access.ts: this device, LAN, Cloudflare quick tunnel, Tailscale), runs the server (src/server/main.ts) as a child, restarts it on exit code 75 or a crash, and keeps the tunnel up. term.ts draws its menu, panel, and QR code in the terminal. The server takes the data folder’s lock, opens one PocketApp, and serves it with http.ts.
Data (~/.pi-pocket/, or PI_POCKET_DIR)
Section titled “Data (~/.pi-pocket/, or PI_POCKET_DIR)”pocket.sqlite: Pi Durable’s storage: conversations, entries, tasks, and Pi Pocket’s documents.config.json: people, roles, hashed tokens, settings.push.json: VAPID keys and push subscriptions.uploads/<conversation>/,worktrees/(sessions’ git worktrees),extensions/(the owner’s drop-ins),themes/(the owner’s theme folders, and.removed/for removed ones),browser/profile/(the built-in browser’s profile; each session’s cookies live in memory only).
Server: src/server/
Section titled “Server: src/server/”A module split into parts keeps its name for the part others import, with the rest in a folder of the same name beside it (http.ts and http/, browser.ts and browser/). Modules import what they use from the module that owns it; there are no barrels. PocketApp creates the app’s parts and hands each one the app; callers use them as fields (app.workspace.viewFile(…), app.attribution.requesterOf(…)).
The core
Section titled “The core”| File | Owns |
|---|---|
main.ts |
The server process: options, the port, signals, then PocketApp.open and createHandler |
app.ts |
PocketApp: opening the harness, reading state back at startup (#recover), the commit coordinator (#committed), connected tabs and their peek tiles, access checks (canSee, requireSteer, requireDriver), the session list (each session’s subagents counted by state), every subagent for the subagents board (subagents, setBoard), hello, model list with Pi’s enabledModels scope (modelList), extensions on and off |
attribution.ts |
Who Pi works for: who wrote each message to Pi or whose work it is (authors documents, from request ids), who queued each submission; read back at startup |
room.ts |
One conversation’s live view for its tabs: Pi Durable’s view plus ROOM_DOCS, sent every 90 ms as changes; its peek tile (peek, at most once a second) for the tabs that show it (PocketApp.setPeeks) |
projection.ts |
Entries as compact JSON for browsers; usageCost |
timing.ts |
Every model stream timed: when it started, its first token, and its end, stamped on the response for the status line’s speed |
entry-format.ts |
The kinds of Pi Pocket’s own entries, and the markers it writes into message text ([from: …], attached files). Stored data and the web app depend on their exact values |
fence.ts |
Text from elsewhere between tags as data, for Pi: no closing tag survives inside it |
docs.ts |
Every durable document Pi Pocket defines |
host.ts, reload.ts |
PocketHost (what extensions get) and Approvals; the extension loader: built-ins in ORDER, drop-ins (never with a name Pi Pocket keeps, BUILT_IN_TOOLS), live reload |
extension-web.ts |
What extension modules add to the app (web, their default export’s second argument): routes, the browser module and its files’ versions, events to it, commands (BUILT_IN_COMMANDS), cleanups; who may use each (mayUse), POCKET_API |
lock.ts |
One process per data folder |
errors.ts, paths.ts |
HttpError, describe, and input checks; ~ paths |
People
Section titled “People”| File | Owns |
|---|---|
config.ts, auth.ts |
People and settings; cookies, tokens, invites |
requests.ts |
Request ids, which say whose each message to Pi is (u: a person’s own, p: sent for a person) |
collab.ts |
Chat, activity lines (addActivity), reactions, pins, notes, typing, take turns |
alerts.ts, push.ts |
Push notifications: who hears about what; Web Push without dependencies (RFC 8291, 8292) |
spend.ts |
Cost per conversation and person; limits, a team’s (linked sessions’) against the budget of the session that started it |
What people ask of Pi
Section titled “What people ask of Pi”| File | Owns |
|---|---|
commands.ts |
What people ask of Pi: sessions, messages (with skills, templates, and mentioned files expanded), forks and resends, model and folder, reset, instructions, plan mode, goals, schedules, worktrees, notes Pi is told |
resend.ts |
A message sent again: the task made with its fork that sends it |
extension-commands.ts |
Extensions’ commands run: who may, a durable task per run that a retry finds and a restart never runs again, cards, and stopping |
subagent-runs.ts |
How a subagent’s run ended, in the words its record keeps: for the subagent tool’s reports, and for what a person’s message to a failed subagent came to |
shell.ts |
! and !! commands: a background task per command that runs it and writes a pocket.shell entry; a restart cuts it off rather than running it again |
schedules.ts, when.ts |
Scheduled messages (a durable task) and their time grammar |
goals.ts |
“Done when” checks |
titles.ts |
A short title for a session with a long first message, or one that runs a skill or prompt template, from the session’s own model |
prompts.ts |
Pi’s prompt templates as slash commands, and /skill:name expanded |
pi-sessions.ts |
Pi’s sessions from the terminal (~/.pi/agent/sessions/), read and never written: listing them, and one as a new session’s history, with Pi’s own context of it (compactions, context edits); commands.continuePiSession makes the session |
skills.ts |
Which skills a session’s folder has, from where Pi’s CLI looks and in its order, with Pi’s project trust; the system prompt and /skill:name both use it. Reads and saves the owner’s answer to “trust this project?” |
models.ts |
The models people can pick (marked for Pi’s enabledModels scope and when their sign-in needs renewing), the one a conversation runs with, finding one by name, and the thinking levels |
project-model.ts |
Trusted project model/thinking defaults and fresh global-only settings read for each new session, validation, and safe errors; no resource paths or per-project run policy |
model-names.ts |
Which models a text names, word by word: PiP passes or names a model only when its person named it |
running.ts |
Running now, from Pi Durable’s task graph |
pip.ts |
PiP, the owner’s assistant: its conversation (made on first open, kept out of the session list), what its tools do as its person, and the follows (a durable task each) that report to it when a session’s run ends |
pip-compact.ts |
PiP’s context kept small: when it compacts itself, and its own summaries, written with what came from outside left out and counted in its spend |
watches.ts |
A session’s PR watch: the durable task that looks at the PR with gh and tells Pi about failed checks, conflicts, and comments that count |
hooks.ts |
Webhooks: making and removing them, letting a post in by its secret or GitHub’s signature, and turning it into a message for its person |
repo-watches.ts |
Repository watches: PiP’s standing orders on a GitHub repository, and the durable task that looks at it and tells PiP or a session what happened |
github.ts |
GitHub through gh: running it unattended, repository and PR names in every form, and whether gh’s account can push to a repository |
memory.ts |
Memory: lessons Pi keeps across sessions, each for a project (a repository with its worktrees, or a folder) or everywhere; the lessons in force for a session, and people’s view, writes, and approvals |
folder-favorites.ts |
Each person’s favorite folders (/api/folder-favorites): starring and unstarring one in a transaction, and each person’s list sent only to their own tabs |
linked-sessions.ts |
Linked sessions (LinkedSessions, host.linkedSessions as LinkedSessionsApi in host.ts): starting a session from another and sending its first message, with a record of the call that makes a repeat find the first; the addresses each session knows its linked ones by; messages between them, sent once, as the person Pi works for, through Commands.submit; and a team’s bounds (10 sessions, one level, 20 messages without a person) |
A conversation’s folder and history
Section titled “A conversation’s folder and history”| File | Owns |
|---|---|
workspace.ts |
app.workspace: which files a person may load or read through a conversation, the file viewer, the files for @ mentions, Changes, uploads, new folders from the folder picker |
transcripts.ts |
app.transcripts: stored history as people read it: an entry (its own, or one a fork inherited), the images in it, what came before the active context, and the session as Markdown |
files.ts |
The files in a session’s folder for @ mentions: git’s list, or a capped walk; kept briefly per folder, versioned, and compressed once. The file viewer’s reads, and the paths a message mentions |
changes.ts, worktrees.ts, git.ts |
The Files tile’s Changes (and undoing a file there), per-session worktrees, and the git runner both use |
branches.ts |
A session’s git branch: read from the repository’s HEAD for its view (Room looks every few seconds), listed and switched for the branch picker |
export.ts |
A session as Markdown |
HTTP: http.ts and http/
Section titled “HTTP: http.ts and http/”| File | Owns |
|---|---|
http.ts |
The one request handler: which part answers a path, and the error boundary that turns what it throws into a status |
http/api.ts |
/api: signing in, the X-Pocket header, and the app-wide routes (people, sessions, Pi’s sessions from the terminal, peeks, the subagents board, spend, folders, settings, the models for subagents, extensions, providers, invites, restart) |
http/conversation-routes.ts |
/api/c/:id: one conversation’s routes, after the API checked that the person may see it |
http/browser-routes.ts |
/api/c/:id/browser: the Browser panel’s frames, console, and input |
http/push-routes.ts |
/api/push: this device’s subscription, what to notify about, a test |
http/hook-routes.ts |
/hook/:id/:secret: posts to webhooks, with no sign-in |
http/events.ts |
Each tab’s events: the event stream (/api/events), or long polling (/api/poll) where a tunnel holds streams back |
http/assets.ts |
The web app’s files, the vendored modules, the app’s content security policy, and image files |
http/theme-routes.ts |
/themes/:id/:rev/…, a theme’s files for everyone signed in: its CSS as checked, from memory; its fonts and images from disk, opened as checked and sniffed |
http/sign-in.ts |
/login, /join/:code, and /share before the service worker takes shares |
http/artifacts.ts |
/a/…: an artifact as its own page, in a sandbox; /a/frame, the page a reply’s HTML runs in once someone taps Run |
http/extension-routes.ts |
/api/x/:module/…, extension modules’ routes (after the API’s checks), and /x/:module/:rev/…, their browser modules’ files |
http/io.ts |
What every route uses: reading a body, writing JSON, ApiRequest; a file to serve, read as it was checked (readChecked, opened); the policy an SVG is served with |
The built-in browser: browser.ts and browser/
Section titled “The built-in browser: browser.ts and browser/”| File | Owns |
|---|---|
browser.ts |
Browsers: one headless Chromium for every conversation, started with the first page and stopped after the last; each conversation’s page |
browser/page.ts |
BrowserPage: one conversation’s page: its events, screencast frames, console, navigation, and what Pi and people do to it |
browser/cdp.ts |
The DevTools protocol over a pipe (--remote-debugging-pipe), and the Chromium process; BrowserError |
browser/discovery.ts |
Finding a Chromium-based browser on this machine, and where its profile goes |
browser/page-scripts.ts |
The scripts that run inside a page: the outline with refs for Pi, finding a target, selecting |
browser/keys.ts |
Key names and combinations as key events |
browser/viewport.ts, browser/urls.ts |
Page sizes and their presets; what people and Pi type, made into addresses |
browser/local-servers.ts |
The web servers running on this machine, for the panel’s start screen |
Elsewhere
Section titled “Elsewhere”| File | Owns |
|---|---|
providers.ts, net.ts |
Provider sign-ins, and those that need signing in again (a subscription its provider will not renew); HTTP settings for provider streams |
gallery.ts |
The extension gallery (app.gallery, host.gallery): its index read and kept in the data folder’s gallery/, downloads checked against their pinned SHA-256, installs (off), updates with a diff, removal, the <name>.gallery.json provenance beside each, revoked versions, and Share’s draft entries |
gallery/entry.ts |
The index’s format, read strictly: entries, file names in the drop-in folder’s rules, limits, compatibility, addresses. The index repository’s checks import it too |
gallery/files.ts, gallery/fetch.ts |
An extension’s files in the drop-in folder: names checked, never through a link, a version put in place all or none with an undo journal that recover replays before extensions load; reading from other sites, https only, within size and time limits |
gallery/diff.ts, gallery/share.ts |
A file’s change as a unified diff, for updates; whether a drop-in stands on its own, and the draft of its entry, for Share |
gallery/themes.ts |
Themes from the gallery in the data folder’s themes/: a version written to a hidden folder, checked with readTheme, then put in place in one rename; the .gallery.json provenance in each; recovery at the start |
lancet.ts |
Loads Lancet Guard from Pi’s install |
omarchy.ts |
The Omarchy desktop’s current theme and wallpaper, read-only, for Follow desktop (/api/theme) |
themes.ts, themes/ |
Theme folders (app.themes): the built-in ones in web/themes/ and the owner’s in the data folder’s themes/, read and checked (themes/format.ts: theme.json, colors.toml, limits; themes/css.ts: CSS’s tokens and the files it loads), each one’s last good version, watching, Duplicate, Remove, Edit with Pi |
versions.ts |
Pi Pocket’s versions compared, for gallery entries’ and themes’ minPocket |
Outside src/: flake.nix and nix/ build Pi Pocket with Nix (nix/package.nix) and run it as a NixOS service (nix/module.nix, with nix/manage-extensions.sh to copy declared drop-ins). nix/tests/ runs only under Nix; npm test skips it.
Extensions: src/server/extensions/
Section titled “Extensions: src/server/extensions/”Each default-exports (host: PocketHost, web: ExtensionWeb) => Extension | Extension[] | void (web adds to the app: extensions/app.md) and is installed in this order: prompt (system prompt, with where Pi Pocket’s docs are), artifacts (artifact tool), browser (browser tool and its prompt section; through host.browsers), subagents (subagent tool and its tasks: anchors, reporters, and the courier that sends waiting reports to the parent together; a subagent runs on its session’s model, or one the owner allowed, and the app rebuilds the module when that list changes), agent-teams (Agent Teams, off by default: the team tool and its prompt section, through host.linkedSessions; its browser module agent-teams.js describes the tool’s calls with pocket.toolCall; it imports nothing of Pi Pocket’s but types, as a drop-in would), schedules (schedule tool; installs the schedule task), goals (hook after each answer), plan (tool hook and prompt section), guard (tool hook that asks for approval), codemode (codemode tool; a script’s calls go through the same hooks), memory (memory tool and the prompt section of lessons in force; through host.memory), pip (PiP’s tools, prompt, and follow task, through host.pip; only PiP’s conversation selects it, as PocketApp’s settings leave it out of every other conversation’s), triggers (the PR and repository watch tasks and the watch_pr tool, through host.watches and host.repoWatches; webhooks need it on), gallery (the extension gallery’s owner-only routes under /api/x/gallery/, through host.gallery, and its twice-daily look for revoked versions; it installs pocket-gallery, which tells the web app it is on).
Web: web/ (Preact and htm, no build)
Section titled “Web: web/ (Preact and htm, no build)”Plain ES modules, loaded as they are. store.js holds the shared state, and nothing imports in a circle. Some modules do work when they load: back.js (it hears back and forward), store.js, ui.js (Markdown and sanitizer setup, document listeners), theme.js (the saved appearance, which prefs() reads, and safe mode, safe-mode.js, which it imports first), seen.js (when this browser last looked at each session), peeks.js, subagents-board.js (it tells the server while the board shows), browser-panel.js and files-panel.js (the panels’ saved widths), rich.js (gives Markdown its rich block), sheets.js (a step back for each sheet opened from another), transcript.js (it hears which answer a jump asked to see), extension-ui.js (it loads extensions’ browser modules as the server lists them, and hears their events), and app.js. store.set calls subscribers in the order they subscribed, so keep the order in which modules load when moving code between them.
| File | Owns |
|---|---|
store.js |
State, the event stream (SSE, or long polling when a tunnel holds it back), api(), actions, workspaceOrder (the numbers, by session-tree.js) |
back.js |
The back stack: routes and the layers over them (sheets, the drawer, panels that cover the screen, a file over the tree) as history entries, so back closes what opened last; useBack, pushRoute, goHome, the list put under a conversation opened straight away, taps let go that land on what just slid in |
gestures.js |
Touch gestures, each following its own finger by its identifier, passive where they can be: sheets dragged down and the drawer dragged left to close (useDragToClose); on phones, swipes on the conversation (right back, left Files), in Files (right back), and up from the message box (the places); a swipe in from the left edge goes back in an iPhone’s home screen app |
app.js |
Layout (tiled windows, and the subagents board in the conversation’s place), top bar (with what changed, from diff.js), notices, keyboard shortcuts, routing |
transcript.js |
The conversation as turns: messages, Pi’s replies, its thinking and tool calls folded into groups that open into a timeline, extensions’ command cards, approvals, the live line (and Pi’s thinking above it), breadcrumbs (and linked-sessions.js, the links between linked sessions); a message from a linked session shows without its routing words |
turns.js |
How the transcript reads a conversation, without drawing it: answers split into runs of words and groups of calls (itemsOf), a group in a line (summarize, with extensions’ own tools as extension-ui.js gives them: setCallSummary), and what a call came to from what its tool printed. No imports, so Node tests it |
calls.js |
A tool call in one line, for the transcript’s timeline and peek tiles; extensions’ own tools as extension-ui.js gives them (setCallDescriber) |
composer.js |
Message box, chips, the queue, plan and goal bars, @ file suggestions, ! commands, ↑ and Ctrl+R history, long-paste placeholders, the mic (voice.js), a subagent’s way back to its session, the status line under it (kept to one row) |
subagents.js |
The subagents bar above the message box: each subagent’s state, what it was asked, and what it does now (its peek, through useOnScreen in peeks.js); Stop, open, put away. Its states and times (stateOf, nowDoing, elapsed, ordered) and failures dealt with (unresolved, dismiss, seeFailure) serve the board too |
dock.js |
What sits above the message box (the queue, the bars) and how it shares the room with the conversation: bars whole, the queue and the subagents’ list sized to what is left, bars on one line when room is short |
voice.js |
Dictation: the browser’s speech recognition into a message box (useDictation, words at the caret, the draft kept by the box’s own saving), the mic (MicButton), and Alt+V (toggleDictation, the newest box) |
history.js |
What this browser sent, for ↑ and Ctrl+R |
files.js |
@ mentions: the folder’s file list, fetched once and checked in the background, and matched here as people type |
commands.js |
Slash commands (the app’s own, and extensions’), and prompt templates |
trust.js |
Project trust: the bar that asks the owner whether to trust a session’s project for its own skills and its .pi/settings.json model defaults, and the sheet (/trust, the menu) that shows and changes the answer |
sheets.js |
Which sheet is open, the component that shows it, and the nested-sheet back action supplied to Sheet through SheetBack |
sheets/*.js |
One sheet (or a few close ones) each: the model and branch pickers, folder, extensions, providers, people and invites, the file viewer (FileView, which the Files tile shows too), find, spend, running now, schedules, webhooks, memory (the lessons Pi keeps, with the menu’s hint), worktree, Pi’s terminal sessions, appearance, shortcuts, the menu, a session’s actions, the extension gallery (gallery.js: browsing, an entry, its source, an update, sharing; galleryAvailable) |
peeks.js |
Peek tiles: their switches (the top bar’s and the rail’s), which sessions get one and in what order, the column and the strip, the watch list |
seen.js |
What this browser has seen: when it last looked at each session (markSeen), which runs ended since (finishedUnseen), and the server’s clock (fromServer). Peek tiles and the session list’s squares read it |
chat.js |
People panel: presence roster, take-turns shortcut, chat bubbles, pins, notes; the chat sheet |
avatar.js |
People’s avatars and colors, and folders’ colors |
subagents-board.js |
The subagents board: every subagent in every session (the server’s subagents events, while it shows), soft state-colored cells and rounded session rows, the proportional state bar and filter pills, Needs you, the selected detail card (its peek), the arrows, and the phone’s layout |
extension-ui.js |
Extensions’ browser modules: loading them as hello lists them (server.ui), pocket (the app’s API for them), and what they add: rail buttons (ExtensionRailButtons), menu rows, launcher actions, pages in the conversation’s place (ExtensionPage, state.page), and sheets (ExtensionSheet); one that fails, alone, and how their tools’ calls show (pocket.toolCall, given to calls.js and turns.js) |
sessions.js |
The sidebar: the rail (RailNav: the tools, you, and, folded, numbered sessions) and the session list beside it (sidebar, drawer, a phone’s home screen), a line per session; selecting rows and archiving them with undo (setArchived), and a touch screen’s long press on a row, which opens sheets/session.js; linked sessions under the one that started them, with branch lines (session-tree.js, the tree as data) |
home.js, signin.js |
The wide home screen; the sign-in screen |
notify.js, sw.js |
Push, the icon badge, approvals from notifications, shares; the notifications sheet |
share.js |
Share to Pi |
ui.js |
htm binding, Markdown (code blocks colored and labeled, split into parts around the blocks rich.js draws), formatting, icons, Sheet and its optional SheetBack context, Switch, Keys, item (a menu row), usePresence (animate out), useSlide (sliding indicators), useSide (which side of the screen a region a theme may move is on), railState. Shared controls and inset .sheet-list groups are styled in style.css |
theme.js |
Appearance: the theme (a theme folder’s stylesheets in the theme layer, swapped in as they change), the colors (palettes as CSS variables in the palette layer, a folder’s own, Follow desktop), tiling, motion, text size, sidebar shape, pins; the reveal; the cache index.html paints from; the owner’s default theme and colors for browsers whose person has not chosen |
themes.js |
Omarchy’s themes as palettes, generated from /usr/share/omarchy/themes/*/colors.toml. themes/<id>/ holds the built-in theme folders (Pocket and Lantern); theme-surface.js lists the tokens and parts themes may rely on; safe-mode.js is ?noext=1 |
launcher.js |
The Ctrl/⌘+K launcher: search-type pills, local fuzzy matches and field highlights, leading result tiles and session status pills, live theme previews, combobox selection, focus isolation, and a New session action for each favorite folder (named as sheets/folder.js names them); app.js gives every logical opening a fresh identity |
pip.js |
PiP’s panel over every screen (a rounded floating window in the corner, a bottom sheet on phones), fed by pipview events while open; its private file/image composer, the all-project pending-memory shortcut, its rail button, the mark that shows while it works or has news, and openPip, askPip, togglePip for the places, the launcher, /pip, and Alt+A |
pip-attachments.js |
PiP’s browser-only text/file draft controller: account/conversation scoping, uploads, object URLs, and submit tokens; drafts survive panel closes and stale callbacks cannot change newer drafts |
browser.js |
Whether the Browser panel is open, and opening an address in it |
browser-panel.js |
The Browser panel: frames by long polling, taps, drags, wheel, and keys as input events, the live address bar, sizes, the console, the start screen’s local-server list |
files-panel.js |
The Files tile: whether it is open and on which tab, the folder’s tree (folders read as they open), Go to file, the open file beside the tree, and the Changes tab |
diff.js |
The diff viewer: one file’s diff (unified or split, line numbers, colors, changed words, hidden lines on request), DiffBlock for tool calls and replies, and DiffReview, every change file by file with viewed marks |
diff-parse.js |
Diffs as data, without the DOM: unified diffs and Pi’s edit format as files, hunks, and lines; rows that pair changed lines by likeness; the words that changed |
rich.js |
Code blocks in replies that show more than code (a page previewed, with Run; an SVG; a diff), given to Markdown at load (setRichBlock); HtmlPreview, Highlighted |
run-frame.js |
RunFrame: a reply’s HTML run on a tap, in the server’s sandboxed /a/frame page, sized to fit it |
highlight.js |
Syntax colors without dependencies: highlightLines gives escaped HTML with flat hl- spans per line, which diff.js marks changed words in |
Manual refresh lives in src/server/model-refresh.ts, owned by Providers.modelRefresh; web/model-refresh.js shares the refresh and its feedback between the menu, Providers, and the launcher. test/model-refresh.test.ts checks local configuration, external sign-ins, provider isolation, cancellation, session use, and endpoint access.
PiP’s pip-window.js handles pointer resize zones and the account/browser size preference.
The menu in sheets/menu.js keeps Sheet’s focus, Escape, drag-to-close, and the sheet trail’s Back behavior. Its MenuFrame positions it below the top bar’s menu button at ≥700px, and adds arrow navigation and focus return; below that width it stays a bottom sheet. PlacesSheet uses the same three-column tiles.
How a message travels
Section titled “How a message travels”POST /api/c/:id/submit→commands.submit: checks access, turns, and spend, expands a template, thenconversation.submit()with request idu:<userId>:<clientId>. The request id makes a retry a no-op and names the author.- Pi Durable runs the generation and tool tasks, committing each step to
pocket.sqlite. Extension hooks run inside those tasks. app.ts’s commit coordinator (#committed) hears every commit: spend counts usage first, then each document change updates what the app keeps in memory and the rooms that show it, andattribution.tsnotes who each message is for.- Each room sends its tabs what changed.
store.jsapplies it, and Preact renders.
Adding things
Section titled “Adding things”- A document: define it in
docs.ts. To show it live, add it toROOM_DOCSand the view inroom.ts, and read it instore.js. - A command: a method in
commands.ts(check access first), a route inhttp/conversation-routes.ts(orhttp/api.tsfor one that is not about a conversation), an action instore.js, then the UI. - A sheet: a component in
web/sheets/, a case insheetBody(web/sheets.js), andopenSheet({ type })where it opens. - Something back should close (a screen over the conversation, a view that takes another’s place on a phone):
useBack(open, close)fromweb/back.jsin the component that shows it. Sheets already have it. - A built-in extension: a file in
extensions/(its extension namedpocket-…), its place inORDER, its title inTITLES, and its tools inBUILT_IN_TOOLS(reload.ts), which no test checks. - A member of the extension API (on the host,
web,pocket, or a command’s context): add it totypes/pocket.d.tswith@since, and to its page indocs/extensions/reference/.test/public-types.ts(innpm run check) fails until the code and the types agree for the host,web, and a command’s context; forpocket,extension-ui.test.tscompares only the members’ names, so check its signatures by reading. A host member for built-ins only goes inInternalinpublic-types.tsinstead. - A built-in slash command:
web/commands.js, with its name kept from modules’ commands inBUILT_IN_COMMANDS(extension-web.ts). - A command of the owner’s own: a drop-in with
web.command, not a change here (extensions/commands.md). - A page, rail button, or route that is the owner’s own: a drop-in with a web part, not a change here (extensions/app.md).
Rules that are easy to break
Section titled “Rules that are easy to break”- Browsers only see committed state. Nothing is shown before it is stored.
- Document kinds, scopes, and fork settings are stored data: never change them.
- Hooks get only
memoandsnapshot. Anything a replay must not repeat goes in a memo or behind an idempotent request id. - Whose work Pi does comes from request ids (
requests.ts): a message sent for someone needs their id in its request id; one without it leaves Pi working for whoever it worked for before. At startup, what a crash kept out of the authors document is read back from Pi Durable’s records. - A tool is
replay: "safe"only if running it twice is harmless. - Extensions reach the app only through
PocketHost, and add to it only throughweb(extension-web.ts). - Server code is erasable TypeScript with
.tsimports. Comments are plain sentences.
Tests: test/
Section titled “Tests: test/”npm test runs every *.test.ts with Node’s test runner. helpers.ts provides a scripted model (scriptedModel(route): faux-1, faux-2, faux-vision), openApp (the same data folder again is a restart; now moves the clock), newSession, say, until, and fakeTab. The scripted model costs nothing: spend tests write pi.usage themselves. browser.test.ts drives a real Chromium, where one is installed, for the built-in browser, and the *-ui.test.ts files load the whole web app in it. docs-examples.test.ts loads the example extensions in docs/examples/ as drop-ins. extension-web.test.ts covers extension modules’ routes, browser module files, and events, extension-loader.test.ts the loader with a real registry, and extension-ui.test.ts their browser modules in the app, in Chromium. memory.test.ts checks the pending-lesson queue’s access and approval safety. linked-sessions.test.ts checks linked sessions: a repeated start or send acts once, a hidden origin stays hidden, the checks of whom Pi works for come first, and an unavailable model makes nothing. gallery.test.ts checks what the gallery takes from its index: a file name that leaves the extensions folder, a file whose hash differs, an install under a name that was on before (it stays off), a revoked version, and a theme that installs only as pinned and only when its CSS passes the theme check. turns.test.ts runs web/turns.js in Node: how the transcript groups Pi’s answers, and which calls can still run. voice.test.ts runs web/voice.js in Node with a stand-in speech recognizer: where dictated words land as people type. hooks.test.ts covers webhooks and fence.ts. The rest of the web app has no tests: check a change to it in a browser, on a copy (node test/serve.ts: this folder’s code, its own temporary data, a scripted model).
These pages describe Pi Pocket 0.14.0. Your copy has the pages of its own version in its docs/ folder.