A sandbox is a new env type (`type: "gondolin"`): a long-lived Gondolin micro-VM on an SSH environment, with
listed host folders and an HTTP(S) host allowlist. The worker reaches it over SSH to the host, where p2's launcher
(plugins/envs/sandbox: native, or Docker when the host's Node/QEMU are too old) runs pi-env's daemon inside the VM,
so tools, workspace, terminals, uploads and MCP work unchanged. Names outside the allowlist are refused before
the host resolves them.
Conversations working in a sandbox cannot leave it: location tools, subagent spawn/configure, AgentLocations.move,
the terminal tool, and one-shot agents (which now inherit their caller's own location) all keep them there; only the
owner's Relocate moves a session out, after a confirmation. conversationConfinement() exposes it to plugins.
Owner-only management in the Envs sheet: sandboxes listed under their host, host test reports sandbox support,
create/edit/start/stop/restart/test, lock icons in the top bar and sidebar. Tests in test/p2/sandboxes.test.ts
(real VM cases with P2_TEST_GONDOLIN=1) and ui-sandboxes.test.ts; docs in docs/remote-envs.md.
p2 rewritten by the Chord v2 authoring rules (decision "App authoring rules confirmed
by the p2 audit"), in four parts merged together:
- core: session lifecycle (relocate, clone, fork, archive) and search rebuild are
persisted jobs on a new server store; Sessions v6 with client-chosen ids and visible
operations; Retry answers with the worker's real outcome; workers push branch,
subagent and status state to the server; roots take the method's ctx.
- services: models, envs, ports, login, mcp, logs, sharing and voice follow external
state (self.follow, followNative) instead of copy loops; methods answer after their
state commits; presence from host.references.
- chat: chat mode, artifacts and prompt resources follow Durable and native state;
shell commands are Chord jobs (shellId is the requestId); status pushed before
send and stop answer; race-free draft claim.
- ui: optimistic updates on web (useOptimistic) and terminal (TuiScope.optimistic,
extended in Chord with a reducer and a pending flag), pending states for long work,
visible rollback, load waterfalls removed; merged with the one composer and
immediate first send (ccff9876e) and the folder + button (5a31a6d5f).
docs/optimistic-ui.md describes the pattern.
Every touch put the transparent input field under the finger, so a long press on
output selected in the empty field (Paste only) and never a word of the output;
the field's focus also opened the keyboard, whose resize redrew all rows. Rows
were replaced whenever output changed them, which dropped any selection.
Now a long press on a character of the output is Chrome's own (word, handles,
Copy); only a blank cell gets the field for Paste. Touch contextmenus are never
cancelled. While output is selected rows are not redrawn (15 s after the last
change, or until the selection ends or the user types, pastes or scrolls). Copy
joins soft-wrapped rows (new Frame row flag from libghostty) and drops the
blanks that fill rows.
The agent drives the session's terminals (shared with the owner's panel) with
tools.terminal({ command, args }): new-window, list-windows, send-keys, read,
capture-pane, kill-window, with waits (quiet, exit, /regex/) and plain-text
output. The terminal host keeps each terminal's last MiB of output and the
agent's read cursor, answers calls with ids, and does keyed calls once, so the
tool is replay-safe. AgentTerminals serves the session's current worker on its
environments; archiving closes a session's terminals. Without code mode the
worker registry installs tool_search once for all deferred tools.
Each branch is a top-level conversation forked from another at an entry;
the session-scoped p2.main document lists them and names the current one.
Earlier branches are read-only. The main agent's per-agent state
(subagents, artifacts, voice and chat mode, pending move) stays on
conversation 1 for every branch.
- tree plugin: SessionTree (tree, navigate, select); the worker reports
main and branches to the catalog (Sessions v4, catalog schema 4)
- TUI: /tree and double Escape (pi's default "tree"), a tree selector
adapted from coding-agent
- web: Edit on user messages, Branch from here on answers, branch rows
under the session in the sidebar
- main-agent checks use the branch list instead of conversation 1
- search indexes each branch's own entries
Design: apps/p2/docs/tree.md
p2 (Pi Mobile on Chord v2) moves from pi-private into pi as one commit. Root scripts that loop all workspaces
use --if-present; npm test, ./test.sh and the release script cover packages/* only (p2: cd apps/p2 && npm test);
sync-versions also updates apps' workspace pins.