v0.2.0
PiCode v0.2.0
Added
-
Matrix: a managed agent's panel is now its live conversation. Where a managed agent's panel said Managed agent — open to read., it shows what the agent is saying as it says it — the same turns, tool cards, diffs and markdown its tab shows, scrolling itself, in a read-only layout with no composer and no queue controls. When the agent is waiting on a person the chip says Needs you, the body shows the question and the choices it offers, and a line across the bottom of the panel carries it with Open, which takes you to the tab where you answer. The chip comes from the fleet, not from the panel's connection, so a panel that is asleep, paused or zoomed down to a name-plate still tells you which agent is blocked.
-
Link two Matrix panels, and the two sessions can message each other
(ADR-0116). On a matrix canvas, hover a panel and drag the connector in
its header onto another panel. The line grants exactly one thing: those
two sessions gain each other as a contact in PiCode's existing messaging
(ADR-0104). One can send the other a message and read the replies — that
is all it does. -
A link never grants a transcript. It does not let one session read the
other's history, scrollback, session file or anything typed into it. The
supported way to get context out of another session is to ask it and let
it answer in its own words. The guide says so in plain words. -
Two sessions in different project folders can now be paired, per pair,
by hand. The workspace rule is unchanged and there is no owner-wide
switch: the blast radius of a link is two named sessions you drew a line
between. -
Nothing is connected quietly. Drawing a line to a session that is not
connected offers the existing connection in one line, with what it grants;
drawing across two project folders asks again, separately, naming both
folders. Cancel at either point writes nothing at all — no link, no
connection. -
Removing the line removes the permission, with nothing left over:
PiCode derives who may message whom from the links that exist right now,
so deleting the line, either panel, or the matrix revokes it immediately. -
A link that grants nothing reads broken, in amber and marked
Broken, with the reason when you point at it — the connection was
revoked, the session moved, the target is gone, or it was never connected.
It is never a faded version of a working link. -
Matrix links, in the Messages view. Every link you have drawn,
anywhere: both ends, the matrix it lives on, whether it works right now,
and a Remove that revokes it exactly as the canvas does. It is not
filtered by the folder picker, because links across folders are the ones
worth seeing. Grid mode, which has no plane to draw on, shows a per-panel
link count in the header and sends you here. -
Matrix: a panel can be a pinned note. Add one from Add panel →
Pins: the panel shows that pin's markdown, read-only, follows it as
you edit it elsewhere, and its header opens Pin Studio. A note whose pin
was deleted says so and offers Remove, the way a deleted terminal does. -
Matrix: a panel can be a file. Add panel → Open files offers
the files you already have open in a tab; the panel is the same editor,
with Save. It says Unsaved while you have changes, keeps them when you
maximize it or switch layout, and never goes to sleep holding them. -
Matrix: a panel can be the changes to a file. The same list offers
each open file as Changes to an open file: the panel shows that
path's diff and keeps it current as the file changes, with Open file
to jump to the editor. One file can be on a matrix twice, as the file and
as its diff. -
The Matrix canvas — your panels on a plane you pan and zoom. A matrix header now has a Grid | Canvas switch. On a canvas a panel goes anywhere: drag it by its header, resize it from any edge or corner, drag a box around several and move them together, pan with the middle button or by holding Space, zoom with the wheel or the
+/−buttons, and0fits everything on screen. A minimap in the corner shows the whole plane. The arrow keys still move between panels — an off-screen one is brought into view — Enter still starts typing in that panel's terminal, and Delete still removes it with an Undo. Switching to the canvas converts the whole matrix in one step and switching back packs it into the 12 columns again (it asks first, since the plane positions are not kept). Where you left the camera is remembered per browser, so two people looking at the same matrix never yank each other's view. -
Zoomed out, a panel shows its last screen instead of a live one. Below about 75 % a panel stops being a connected terminal and shows the text of the screen it last had — sharp, free, and stamped with its age in the header if the panel has done something since; below about 40 % it becomes a name-plate with the face, name and status, sized so it stays readable however far out you are. Zoom back in and the same terminals reconnect where they left off. That is what lets a canvas hold hundreds of panels without hundreds of connections.
-
A terminal takes the mouse at 100 % only, and says so. A terminal panel is readable and typeable from 80 % up, but a click is only accurate at 100 %: below it the terminal would put your click on the wrong cell, and with mouse reporting on that wrong cell reaches the program running inside. So away from 100 % the panel body does not take clicks — clicking it zooms the canvas back to 100 % first, and then your click lands where you aimed it. The zoom percentage sits next to the minimap and takes you back to 100 % in one press.
-
Tidy. The matrix menu gains Tidy panels on a canvas: it lays them out again in reading order, keeping each panel's size, and saves once.
-
Mobile: Back from an agent or terminal lands where the work lives. A workspace's agent or terminal returns to Workspaces focused on that workspace's group; a free one returns to its own Agents or Terminals list — never a flat section it was never in.
-
A matrix can be a canvas, from the API. Every matrix now has a layout mode: the 12-column grid it has always had, or a
canvaswhere panels sit on a plane with no columns and no bottom — coordinates in 8 px units that may run negative.PATCH /api/matrices/{id}takesmodeand answers with the matrix plus every panel it moved: switching converts the whole matrix in one step (a grid cell becomes 8 units across and 3 down; coming back divides, rounds and tidies the panels so none overlap and none is lost), and switching to the mode it already has changes nothing. Matrices made before this keep the grid. There is no canvas to look at yet — the panels, the mode switch and the pan-and-zoom surface are the next step; this release is the model underneath, exercised by tests. -
Fullscreen hands the browser's keys to your agent. In a normal window the browser keeps its reserved shortcuts (
Ctrl+Tnew tab,Ctrl+Wclose tab,Ctrl+Nnew window) before PiCode can see them — so a CLI chord like Codex'sCtrl+Topened a browser tab instead. Fullscreen mode (Ctrl+Shift+Enter) now also locks the keyboard (Chrome/Edge/Opera): every key reaches the page, terminals get their chords back,Escapestill leaves the mode outside a terminal, and holdingEscapefor about two seconds is always an exit. Firefox and Safari keep the previous behavior until they support the API. -
Fullscreen: any tab can take the whole window. Right-click anywhere in the desktop app — a terminal, an app, a file, the conversation — and choose Fullscreen (
Ctrl+Shift+Enter,Cmd+Shift+Enter, or the command palette). The sidebar, the tab strip and the Inspector rail step out of the way and the tab you are on fills the screen. Nothing is closed: touch the left edge with the pointer and the sidebar slides back over the page, the top edge brings the tabs back — with a Leave fullscreen button at their right end — and the right edge brings the Inspector back if it was open when you started. Each edge waits a moment before opening, so crossing it on the way somewhere else does not flash it, and it stays while the pointer is on it. Esc leaves. The mode is remembered per browser, so a reload comes back into it, and it is offered on every tab except the git graph's own commit menu. -
The browser goes fullscreen too. Turning it on also asks the browser for real fullscreen, so the app is the whole screen and not just the whole page; leaving gives the window back, and if you exit fullscreen yourself (F11, Esc) the mode ends with it. A browser that refuses fullscreen still hides the app's own chrome. A reload cannot ask for fullscreen — there is no click to ask with — so it returns to the in-app mode, with the top edge still there to leave from.
-
Matrix — a live grid of your agents and terminals. The Apps tab has a Matrix tile: create a matrix, add panels from a picker of your agents and terminals, drag them by the header and resize them by the corner or an edge. Each panel shows the real terminal or agent screen — the same one its tab shows — with the sidebar's Working / Needs you / Ready word in its header. Only the panels near what you are looking at stay connected, so a matrix can hold hundreds; layouts save as you go and every browser sees the same matrices (
#/app/matrix/<id>). The guide is Matrix. -
Keyboard across a matrix. Click a panel or tab into one, then move with the arrow keys (the target scrolls into view and wakes up), Home and End for the first and last, Enter to start typing in that panel's terminal, Shift+Esc to come back out to the panel, Delete to remove it with an Undo. A plain Esc still belongs to the program in the terminal.
-
Maximize a panel. The ⤢ button gives one panel the whole surface — the terminal resizes to the space, the matrix keeps its layout underneath, and the panel's slot says Shown maximized. Esc or the button puts it back.
-
Workspace Communication on desktop and mobile: select managed Pi agents and native terminals, apply their connections, and follow preparation and activity in one view.
-
Run a connection test through the participants' native tools. A test passes only after a message, correlated reply and both acknowledgments; pending and unconfirmed results remain visible.
-
Agent CLIs → Messages: native Grok and Hermes communication through
picode messages contacts|send|read|ack, sharing the existing MCP mailbox, authorization and history. -
Stored messages can notify an already open conversation through Pi's native receiver or a guarded TUI pointer. History distinguishes pending, notified, unconfirmed and acknowledged messages.
-
Matrix API and feed events (ADR-0108). Matrices — named 12-column grids of agent and terminal panels — are stored, shared across browsers and announced on the change feed:
GET/POST /api/matrices,GET/PATCH/DELETE /api/matrices/{id},PATCH …/layout(the changed subset, 409 when stale),POST …/panels,DELETE …/panels/{panelId}, and sixmatrix.*events. Limits refuse with the limit named (64 matrices, 500 panels each, 80-character names, panels of at least 4×8 cells). No surface yet — the Matrix app itself is phase 3. -
picode version(--version,-v). Prints the build identity and exits. The install verifier already ranpicode --version; until now the flag silently started a server instead. -
Messages can save private setup for recorded Pi, Claude Code, Codex and OpenCode conversations and attach it when they resume. Desktop and mobile show the next action, including installing the Pi adapter when needed.
-
Automatic local HTTPS setup supplies verified public CA material to the launched client while preserving existing configured CA bundles.
-
Direct session messages (ADR-0104): an embedded HTTP MCP endpoint with same-workspace opt-in, scoped credentials, durable receipts, retry deduplication and explicit acknowledgements. Agent CLIs → Messages on desktop/mobile manages connections and history. Client setup is manual per conversation; no automatic agent turn or terminal migration.
-
Automations: several schedules per automation. The editor's
Schedule block is now a list: each rule has its own preset (Hourly,
Daily, Weekdays, Weekly, Custom), an optional label, its own switch and
Remove, plus Add a schedule — "weekdays at 09:00 and Saturday at noon"
no longer needs two automations. Each rule is its own clock (own jitter,
own catch-up, saved in the browser's time zone); the list and the detail
summarise every rule; the Runs table names the rule that fired. The API
returnsscheduleson every automation (each withnextFireAt) and
takesscheduleson create and PATCH;cronstays as the one-rule
shorthand. Editing a rule's time starts it over instead of catching up
a slot it was never asked for. Migration 038 moves existing schedules
into rows with their last fire intact. (ADR-0045 amendment 2026-09-09.) -
Pins on the phone: create and edit. More → Pins lists your pins
(starred first, search over title, tags and note) with a + that
opens a form — title, tags, the note as markdown — and the pin screen
gains Edit. Drafts are retained and a stale save answers with the
conflict message, as on the desk. Files, sketches and reminders stay
with the desktop studio. -
Git graph: per-commit +/- in the listing. Every commit row shows
the lines its own diff adds and removes (+178 −80, green/red, blank
when a commit changes no text — empty or binary-only commits), so the
shape of history reads without opening each commit. The numbers are
git's own shortstat totals over the commit's first-parent diff — the
same diff the opened commit detail shows per file (they reconcile
exactly, merges included). -
Pins: search, keep on top, archive. The Pins tab has a search box
over title, tags and note (every word must match; archived pins are
found too and say so). A star keeps a pin on top of the list; Archive
takes it out of the sidebar into an "N archived" view one click away,
pauses its reminder and closes an open reminder item, and Unarchive
brings it back (a slot missed meanwhile fires once). The studio has
Archive and Keep on top beside Delete; the phone's pin screen shows
"on top" / "archived".GET /api/pins?q=and?archived=1,
POST /api/pins/{id}/starredand/archived(migration 037). -
Pins: reminders on screen (ADR-0100, slice 3). The studio has a
"Remind me" chip: presets first (in 1 h, in 3 h, tomorrow morning, next
Monday morning, every day, every weekday — day presets at a morning hour
you can change), then every N hours with "count from when I close it",
a date-and-time picker and a cron field; the chip and the sidebar card
read the rule back in words with its next fire ("every day at 09:00 ·
next tomorrow 09:00"). When a reminder fires, a card with the pin's
glyph stays on screen until you act — on the desk and on the phone: X
closes it (the Inbox item goes done everywhere), Snooze hides it for an
hour, Open lands on the pin. A reminder owed is shown on load too; more
than three open reminders collapse into one "N reminders" card that
opens the Inbox. Preferences → Notifications gains "When a pin reminder
is due" for the card, and the push switches gain the same for the
phone. The Inbox app's reminder rows say "Pin" and their "Open pin" goes
to the pin. On the phone#/pins/<id>opens a read-only pin (title,
tags, reminder line, pictures, note) instead of landing on Settings. -
Pins: reminders, server side (ADR-0100).
PUT /api/pins/{id}/reminder
sets one cadence per pin —onceat a date and time,intervalevery
N minutes (at least 5) counted from the schedule or from the moment you
close the reminder, orcron(five fields) evaluated in the IANA zone
the browser sends — andDELETEremoves it; the rule rides
GET /api/pins/{id}and the list summary with its next fire and a
plain-language label ("every day at 09:00", "every 3 h after you close
it"). A one-minute engine (internal/remind, same shape as
Automations) files each fire as an Inbox item of the new kind
reminder(sourcepin): the item is the acknowledgement — unread
means owed, snoozed means snoozed, done means closed — and the feed
announcespin.reminded. A fire while the previous item is still open
re-raises that item instead of piling up; a snoozed item stays quiet and
is re-raised once the snooze ends; a slot missed while PiCode was down
fires once with "Was due …" in the body; deleting the pin or the rule
closes its open item. Phones with push get areminder:<item>push
under a newreminderspreference (on by default), kept on screen where
the platform honoursrequireInteraction. The Inbox app shows the kind
with an "Open pin" action.GET /api/inbox?kind=filters. The in-app
sticky card and the picker are the next slice. -
Packages: view, edit and reset package configuration (pi-roles first,
ADR-0099). Installed packages render as the familiar card grid, now filterable,
with Configure beside Update/Remove on packages with a known config
adapter. The pi-roles editor (#/packages/config/pi-roles)
edits the same files the extension reads — the workspace
.pi/roles.jsonand the per-agent overlay.pi/roles/<agentId>.json—
and shows the effective merge: inherited slots are visible (and can be
overridden for one agent), overrides can fall back to the workspace
value with one click, and each layer can be cleared with a confirmed
"Clear file…". A file the parser rejects is reported, never silently
overwritten (explicit replace). Saves write atomically, preserve
unknown keys, announce on the change feed, and say when they apply
("on the agent's next message"). The page follows the house control
recipe throughout — selects and inputs share the system's height, radius
and focus language in light and dark — and a thinking level cannot be set
without a model (it would have been silently dropped on save). -
Mobile: Preferences → Layout — fit the shell to your screen. Two
dials with platform-smart defaults: Bottom bar buttons (Auto / Low /
Screen edge) and Bottom bar height (48/56/64px). "Auto" anchors the
buttons low only when the bootstrap measured a letterboxed standalone
install (WebKit 313800/317153 — the behavior varies by iOS generation and
icon install date); "Screen edge" is the former strip-probe experiment as
a user choice, honest about the clipping risk in its own label. Applied
before first paint, persisted per device like the theme. Side fix: the
Preferences tabs kept their choice in the desktop hash space
(#/preferences/<tab>), which the mobile router can't express — every
tab click silently fell back to Appearance; tabs are now component state. -
Public docs: working inside a terminal. The Agent CLIs guide gains
Inside the terminal pane — the right-click menu, theShiftbypass to
the browser's own menu, the message bar that opens from it, find with its
three toggles, and a table of every terminal key. The Attach paragraph no
longer describes a bar that stands under the pane on desktop; it opens
from the menu now.
Agent CLIs. -
The git graph's actions on the phone. A long press on a history row —
or Actions on a commit or worktree view — opens a sheet with the same
vocabulary the desktop graph offers, in the same tiers, through the same
doors: prepared in a terminal, run when nobody is working there, or asked
of an agent or pi terminal living in that worktree. The exact command is
composed by the server and shown before anything is sent; a tier C action
through the run door asks you to type a word first. -
The worktree removal form names its field. "Name" over a worktree folder
read as nothing; it says Worktree folder name now, like the create form. -
A clean sibling worktree can be removed from its branch pill. A checkout
with nothing uncommitted draws no row of its own, so the graph had no place
to offer Remove this worktree for it; the branch pill of a branch checked
out elsewhere now carries the worktree's rows. -
"Ask Pi" reaches pi running as a terminal. A pi launched from Agent
CLIs now carries the same receiver an interactive agent does, so the git
graph lists it beside the agents of its worktree, offers Open Pi into
its pane, and can ask it to do a git action in its own turn — delivered
through the receiver with the session row as proof, never pasted. Without
a live receiver or a named session the option is absent and the request
says why. Other CLIs (Claude Code, Codex…) are unchanged: no receiver, no
ask. -
The git graph acts on what you point at. Thirty-odd git actions bound
to the row or pill under the cursor — create a branch, tag or worktree,
check out, merge, rebase, cherry-pick, revert, reset, pull, push, delete —
each delivered through a door that already existed: prepared in your
terminal for you to press Enter, run there when nobody else is working in
the repository, or asked of an agent that lives in that folder. PiCode
still never runs git itself. Actions carry a risk tier: the ones that
publish or destroy work are marked, and when PiCode is the one pressing
Enter they ask you to type a word first. The exact command is composed on
the server and shown before anything is sent, so the preview and the
command are the same string. -
A worktree and the agent that lives in it, in one gesture. "Create a
worktree…" on any commit or branch can also start an agent in it once the
folder exists. -
Undo, where there is an honest one. After an action that only moved the
branch — merge, rebase, pull, reset, commit — the graph offers to put it
back where it was, and says plainly that it prepares the command rather
than undoing anything by itself. A push or agit cleangets no such
offer, because there is no inverse to give. -
The git graph answers a right-click. Every row and pill now carries a
menu naming what you pointed at: a commit offers its hash and subject, a
branch its name, a worktree row its path and the agents living there. A
local branch says which checkout holds it — and when that is a sibling
worktree, the menu offers Open instead of a checkout git would
refuse. Branch pills show how far they have drifted from their upstream
(↑182). The pills on a row are also reachable from the row's own menu, so
a keyboard reaches everything a right-click does. This is the read-only
first phase of ADR-0096; nothing here runs git yet. -
Find inside a terminal (
Ctrl+Shift+F, or Find… in the pane's
right-click menu): a field floats over the terminal — searching never
resizes the pane — highlights every match, counts them (3/14), and walks
them with Enter and Shift+Enter; Escape closes it and gives the keyboard
back to the terminal. Match case, whole word and regular
expression are three toggles in the field, kept for the life of the page;
a pattern that does not compile yet says Invalid pattern rather than
pretending there is nothing to find. The plainCtrl+Fstill belongs to whatever runs in the
pane (less,vim, readline), and the chord is rebindable in
Settings → Shortcuts. -
A waiting agent says so on the screen you are on, whatever agent it
is: when any managed agent stops for a question, a card names it —
Atlas · needs you · QA, the question, its detail and an Answer
button that opens the conversation. Unlike the finished card this one
covers the whole fleet, not just the agent you have open: the runtime
already publishes every dialog edge on the change feed. The card has no
timer — it goes away when the question is answered, or when you open the
conversation that answers it — and a reload never replays a backlog the
sidebar badge already shows. On the phone the Now screen stays quiet,
because that screen is the queue. -
Notification preferences say what to announce, not where the box
sits: When an agent needs me and When a run finishes, worded like
the push switches above them. Turning one off silences that
announcement only — the sidebar badge, the Inbox and the phone are
untouched. "Close position" and "Rich colors" are gone: the card draws
its own close control, and a level already reads from its glyph and
border. Position, duration, visible-at-once, expand and close button are
unchanged, and an old stored value is simply ignored. -
Right-click inside a terminal now opens PiCode's own menu, built from
what that pane can actually do: copy, paste and select all; on a running
Agent CLI, Ask about this and Attach files…; Open for the
file or link under the cursor; go to the end and text size; Clear on a
bare shell only, because a TUI owns its screen; rename, terminal settings,
the folder in Files, close the tab and remove the terminal — the last group
never on an agent's own TUI pane. Holding the bypass modifier (Shift by
default,Settings → Context menu) still hands the click to the browser's
own menu. The right button no longer reaches tmux, which used to answer it
with a second menu drawn inside the terminal. -
The terminal's message bar is no longer permanent chrome. It opens from
that menu — empty, or already carrying the selection: one line lands in the
message, anything longer is attached asselection.txtso the CLI reads a
whole file instead of a mangled paste. A close button (or Escape) gives the
pane the full height of the editor back. -
Toasts are notice cards, and a finished agent turn is one of them:
an announcement now carries who spoke, how long they worked, what
changed and one way out —claude · finished · worked for 7s, the
agent's own last sentence,1 file +46 −1, and an Open pill —
instead of a bare string. When a turn settles while you are looking at
another tab, another view, or another application, that card is what
tells you; it stays quiet when the agent's own conversation is the
focused surface, and a second finish from the same agent replaces its
card instead of stacking a new one. Errors now live 12–30 s instead of
4, because an error nobody was looking at was a lost error. The 317
existing one-line toasts keep their wording and gain a level glyph.
Position, duration, visible-at-once, expand, close button, close
position and rich colours all keep working. Adapted from the Superset
study indocs/benchmarks/2026-09-07-superset-notifications.md. -
Public docs: agent terminals over SSH: a new guide page shows how to
reach PiCode's tmux sessions over SSH (tmux attach, read-only
supervision with-r, Tailscale SSH on a server box) and the one rule:
never kill sessions by name pattern.
Agent terminals over SSH. -
Public docs for moving a conversation between agents: the Agent CLIs
guide now explains the two ways a session arrives (a native session, or
the vendor's ownimportfor the SQLite-backed CLIs), the choices in the
dialog, and what stays behind; the home page names the capability. -
A workspace reaches its own files and history with nobody in it:
Files and Git graph on the workspace card's menu open the project's
own file tree and commit graph — no agent and no terminal required. The
graph, the commit details and the uncommitted changes read through
/api/workspaces/{id}/…, which the server has answered since ADR-0030;
a workspace-owned graph tab now also survives a reload and a deep link
(#/git/w/<id>) instead of reporting "That agent is gone". -
Handoff dialog polish, from the visual pass that was owed: a session
with no title of its own is no longer named after the handoff note, the
dialog names it by a short id instead of a full UUID, and the summary
line counts a CLI's internal record types instead of spelling them out
("bridge session left out (4)" meant nothing to a reader). -
Repository picker in the clone form: the New-workspace "Clone
repository" URL field is now a filterable combobox fed by the GitHub
repositories behind the machine's ownghlogin (gh repo listvia the
newGET /api/github/repos, cached 5 min). Typing filtersowner/repo
and descriptions, grouped by owner, with a lock on private repositories;
picking one fills URL, name and destination at once. Pasting any URL
still works first-class — a pasted URL shows a "Use this URL" row and
Enter submits it as before. Missinggh/ not-logged-in machines show
one line with the one action that fixes it (install guide / copy the
gh auth logincommand); mobile gets the same list as a tap-to-open
picker next to the URL field. -
Mobile Files and Git: full-screen file browsing, search, editing and previews
with unsaved-change and file-conflict recovery; Git changes, history, branches,
worktree/commit details, PR status and existing terminal/agent action channels.
Workspace history now works without an agent; supplied folder preconditions
are checked for history and commits across workspaces, agents and terminals. -
Mobile tools: Agent CLIs now includes Sessions, and More includes
Automations with list, editor, run history and recoverable loading errors. -
Every Agent CLI can now receive a session handoff (ADR-0094). OpenCode
joins as a source and a target, Grok gains a native session (a live probe
showedsummary.jsonandchat_history.jsonlare all--resumeneeds),
and Hermes becomes a target throughhermes sessions import. For the two
SQLite-backed CLIs PiCode hands the conversation to the vendor's own
import command instead of writing their store, then reads the session
back. A written session records the model of the installation that will
answer next, never the source's, which Grok refused and Claude Code could
not restore; the source model rides in the handoff note instead. -
llama.cpp installation cleanup: desktop and mobile can review and remove
verified older installations while retaining the current version, restoration
version and unknown files. -
Install a missing CLI (ADR-0093): the Agent CLIs surface offers an
Install action when a catalogued CLI is not installed — npm-backed for
pi, Codex and Claude Code through the same durable job lane; Grok and
Hermes Agent show their vendor's install guide instead. Installing an
installed CLI is refused. -
Managed local llama.cpp service: desktop/mobile Local service page with
CPU presets, saved settings, verified installation, reviewed start/stop/
restart/update/rollback, durable progress, ownership-aware cache cleanup and
redacted diagnostics. External routers are never adopted. -
Attach a photo or file to an Agent CLI terminal (ADR-0089). On a
running Claude Code, Codex, Grok, Hermes, OpenCode or Pi CLI terminal, an attach
bar (desktop) or header paperclip (phone) stages the file in the
terminal folder and types the path into the TUI. Caps: four files, 4 MB
each. Inspector Git type/run still does not type into a CLI. -
CLI lifecycle management (ADR-0087): update checks, update, reinstall
and uninstall for catalogued agent CLIs. The Agent CLIs surface shows an
update badge with the latest version (npm registry or the vendor's own
--checkcommand) and runs each CLI's native update/reinstall/uninstall
command as a durable job with streamed output, terminal guards and typed
uninstall confirmation. Install methods PiCode cannot manage (Homebrew,
manual checkouts; Grok and native Claude Code uninstalls) show their
official guide instead of controls. Jobs survive daemon restarts as
interrupted— never replayed. -
OpenCode in Agent CLIs: the catalog launches the installed
opencode
command in a terminal, lists top-level sessions from
~/.local/share/opencode/opencode.db(or$XDG_DATA_HOME/opencode/opencode.db)
read-only, and resumes withopencode --session <id>. Activity reporting
is a presence lease plus a session-only plugin injected through
OPENCODE_CONFIG(session busy/idle, permission and question prompts) —
not a data-dir overlay and not by writing~/.config/opencode.
Maintenance subcommands skip the plugin.
Update/reinstall/uninstall useopencode upgradeand
opencode uninstall --keep-config --keep-data --force(the vendor
commands, including bun-global installs classified as npm by path).
OpenCode lists sessions for the Sessions tab, and gives and receives a
handoff (see below). -
Continue a session in another Agent CLI (ADR-0088): the Sessions tab
offers "Continue in …" for every CLI the server says can receive the
session. A preview shows what travels and what is left behind; the
handoff writes a new native session for Claude Code or Codex, adopts a
new Pi agent, or starts a CLI from a deterministic brief. Grok sessions
now list their real transcripts (title, model, size) and Hermes
sessions can be read. Lineage shows on both rows. -
Hermes Agent in Agent CLIs: the catalog launches the installed
hermes
command in a terminal, lists cli/tui sessions from~/.hermes/state.db
read-only, and resumes withhermes --resume <id>. Activity reporting is
a presence lease plus session-only activity hooks (LLM start/end and
approval prompts) injected throughPYTHONPATHsitecustomize — not a
HERMES_HOMEoverlay and not by writingconfig.yaml. Hermes may still
record PiCode's hook command in its ownshell-hooks-allowlist.jsonwhen
auto-accepting. Check setup keeps the first--versionline so Hermes'
install dump does not wrap the heading. -
Deploy refuses while agents work (ADR-0086).
picode deployasks the
running daemon who is mid-turn and stops before touching the installed
binary when anyone is — the refusal names each agent or terminal and why.
picode deploy --force(orPICODE_DEPLOY_FORCE=1 make deploy) is the
deliberate override.make deploy-batchand thepicode-deploy.timer
(12:00, 18:00, 23:00;make timersinstalls it) shipmainin batches,
refreshing stale public screenshots first. New loopback-only route
GET /api/deploy/readiness. -
Inspector: ask a running agent to do a Git action (ADR-0078). For every
agent running in the rail's repository — managed or in its own terminal —
the Git menu now lists an "Ask <name>" submenu with the same actions.
Asking sends the agent a plain-language message through the channel that
already carries its prompts: a queued turn for a managed agent (delivered
at once, or as a follow-up once its current turn ends) or the Inbox
reply's own door into a TUI (the receiver extension, or a bracketed
paste). The agent decides how and runs it in its own turn; PiCode never
runs git here. The commit form's message becomes optional when asking —
left empty, the agent writes one from the changes. A stopped agent, or a
terminal hosting a coding CLI, is not offered. -
Inspector: run Git actions when nobody is working (ADR-0078). The Git
menu gains a per-viewer checkbox, "Run when no agent is working here". With
it on, PiCode types the command into your terminal and presses Enter itself,
but only when no agent in that repository is mid-turn, no automation is
running there, and no other terminal there is working or holding a program;
otherwise the command is prepared as before and a note says who is busy.
Git still runs in your own shell with your credentials and hooks. Typing
now also refuses a terminal that moved away from the folder or whose pane
is not at a shell prompt, and takes a fresh terminal instead. Right-hand
toasts step left of the rail while it is open, so a note never covers its
buttons. -
Session forensics (ADR-0085): the daemon now records which tmux
sessions were alive at a graceful shutdown and reports at boot exactly
which ones did not survive (log +var/restart-report-*.json), stopped
CLI terminals say "PiCode restarted while this terminal was running"
when that applies, launch scripts ignore SIGHUP like interactive shells
always did (explicit Stop escalates to SIGTERM so stopping still
stops), and everypicode deployappends who/what/where to
var/deploy-log.jsonl. The next session-loss incident self-reports. -
CLI terminal session recovery (ADR-0084): every CLI terminal now pins
the native conversation it is running (claude, codex, grok, pi) and a
stopped terminal offers "Resume last session" — one click relaunches the
CLI with its verified resume arguments. A deploy, crash or daemon restart
no longer costs the conversation; the pin is stored (terminal.last_session
event) so it survives the process. Plain start is unchanged; nothing
auto-restarts. Desktop and mobile terminal surfaces both carry the button. -
Contextual llama.cpp model guidance: GGUF choices show file size,
estimated runtime memory and a hardware-oriented starting recommendation. -
Checklist disclosure on sidebar cards (
docs/plans/sidebar-checklist- expand.md). Clicking a card's plan line expands the full list in place:
☑ on finished steps, the braille spinner on the one being executed, ☐ on
the rest. The items are already in client state and arrive over the feed,
so opening costs no fetch and updates render live while open. Works on
agent and terminal cards; the line is a real button (aria-expanded,
Enter opens, Escape closes) and never navigates. No server change. -
llama.cpp operation activity (ADR-0083): load, unload and download now
run as persistent jobs. Activity shows per-file progress, reconnect outcomes
and supported download cancellation on desktop/mobile. Duplicate requests
are deduplicated; conflicting model operations are refused. Unknown results
remain visible rather than being reported as success.
Changed
-
Matrix: what a conversation panel costs, and what bounds it. A conversation holds a WebSocket and a transcript, so it is live only where it can be read: in the band and at 40 % zoom or more. Below 40 % a panel is a name-plate and its connection closes; outside the band it unmounts after the same five seconds every other body gets, and reconnects with its conversation when it comes back. At most twelve conversations are live at once — the twelve you scrolled to most recently — and the rest say Paused until a slot frees. Measured with twenty managed agents on one matrix: nine connections at rest, twelve at the cap with six paused mid-scroll, never thirteen.
-
Participant selection carries forward to future conversations in the selected workspace. Each conversation keeps a separate credential; clearing a selection revokes its connections atomically.
-
Normal setup offers Apply and connect, Open and connect and repair actions, with per-conversation configuration under Advanced.
-
Agent CLI terminal menus are one menu everywhere: the sidebar terminal rows and the Agent CLIs terminal list render the same rows — Rename…, Launch settings, Terminal settings, Start/Restart/Stop terminal (per running state) and Remove terminal. The sidebar gains Start/Restart/Stop and Launch settings; the Agent CLIs list gains Rename… and Terminal settings.
-
Grok/Hermes integration uses native hooks/plugins with ownership receipts and preserves their native executable, home and unrelated configuration.
-
Apps host: a first-party app may declare a native surface (ADR-0109). A manifest can say
surface: "native": the app's body is then a component compiled into the desktop shell instead of a primitives view, opened from the same tile, tab and#/app/<id>route. The phone lists such an app as Desktop only and answers its link with one line and Back; a desktop build that lacks the surface dims the tile as needing a newer PiCode. No shipped app uses it yet — the only native app is the hidden QA demo behindPICODE_DEMO_APP=1, which shows a live terminal; a terminal shown there and in its own tab follows whichever is visible. -
Workspace cards: the plan (checklist) line no longer shows a chevron — the whole line stays clickable to expand the plan, and the line is now italic, matching the plan's voice.
-
Document verified native conversation resumes and acknowledged message roundtrips for Claude Code/Codex and OpenCode/Claude Code, including OpenCode
zai/glm-5.3-flashwith variantmax; runtime behavior is unchanged. -
Development process (ADR-0105). Deploy happens only when the owner runs
make deploy; the deploy timer andmake deploy-batchare gone. The
changelog is assembled fromdocs/changelog.d/fragments, the handoff
keeps no shipped-work prose,docs/architecture.mdis an index over
docs/architecture/, andmake adrseeds a decision record with a
boundary line. Gates:internal/servertests run sharded across four
processes,make ciruns its gates in parallel,make webis a no-op when
nothing underweb/changed, andmake closereuses a green
ci-scopedfor an unchanged tree. -
Sidebar: selection without the blue bar. A selected agent or
terminal card no longer paints the accent bar that read as a blue
border around the card — selection is the tinted background plus the
quiet border (the bar stays on Needs you, where it marks attention).
The card's checklist chevron joins the card grid: the>now sits on
the same column as the folder/branch icons below it (supersedes the
2026-09-06 own-gutter refinement), its text on the folder/branch label
column, and expanded plan steps follow the same two columns. -
User menu: llama.cpp in Tools. The desktop user menu's Tools group
now lists llama.cpp (models, server connection and local service),
matching the mobile More screen; it is searchable like every row.
Previously the manager was reachable only through Providers or by URL. -
Sidebar: PiCode without the build number. The version string
(v0.1.0+…) no longer sits next to the name in the sidebar header;
it still lives in the user menu. -
User menu: one Tools group. After Providers, Settings and Packages
moved under Agent CLIs, the leftover Agents and connections heading
(Integrations alone) is gone. The desktop user menu and the phone's More
screen list Integrations with Agent CLIs and Automations under Tools. -
Desktop: one panel width for every page. All routes now share the
Agent CLIs panel geometry — fluid up to 1240px, centred, same gutters.
System, Integrations, MCPs, Devices, Preferences and Pins no longer use
narrower centred cards (680px), and Automations, llama.cpp and Terminal
defaults stop at 1240px instead of 1080px. Workspace surfaces (agents,
terminals, files, git, apps) are unchanged. -
Providers under Agent CLIs. Pi accounts, keys, quotas and verification
now live in Agent CLIs → Providers, with a CLI selector and explicit machine
scope. Old links and login shortcuts redirect; OAuth returns to the same
desktop/mobile app. Failed catalog refreshes keep the current roster. -
Packages under Agent CLIs. Pi package management and desktop package
configuration now have explicit CLI and workspace/agent URLs. Old links
redirect with their context, update indicators lead to Agent CLIs, and
failed context reads preserve drafts while blocking stale writes. -
Pins: the reminder picker is a form, not a menu of presets. Once —
a date and a time; Repeat — every N hours, or every N days at a time of
day (one day at a time is a wall-clock rule; several days count as a
duration and say so), optionally counted from when you close the card.
One Set, Remove beside it; opening starts from the rule that is set. The
"morning hour" preference went with the presets. An interval may now
name its first fire (atonPUT /api/pins/{id}/reminder), and its
label says the time of day ("every 2 days at 18:30"). -
Mobile Work: workspace favicons match agent and terminal marks. The group head uses the same 24px slot and 22px glyph as the rows under it, so a project icon is no longer smaller than the CLI faces in the same list.
-
Mobile Work empty states sit in the well. Agents, terminals and workspaces with nothing to list center one line and a primary create button in the remaining space (muted icon, no essay); a search miss stays at the top with Clear search. Copy drops "free".
-
Sending a message to the terminal no longer toasts "Sent to the
terminal." The user is looking at the pane and sees the message land —
the confirmation was chrome talking to itself. The message bar/sheet
closes over the visible delivery instead; toasts stay reserved for
failures (send errors still surface viatoastError). Both surfaces
changed together: desktop bar (TermAttachBar.jsx) and mobile sheet
(TermAttachSheet.jsx). -
Settings now lives under Agent CLIs → Settings → Pi (ADR-0101). Desktop and mobile retain global, trusted workspace, agent and key settings. Old links redirect; contextual URLs preserve the agent on reload. Native settings load independently of terminal setup, and failed saves retain edits.
-
Agent CLIs: one CLI combobox with favicons. Settings, Sessions and the new-terminal form share a
--ctl-hcombobox that shows each CLI's mark instead of an unstyled native select. -
Development: the public docs site moved from
www/todocs-site/—
same VitePress build and GitHub Pages URL (cfpperche.github.io/picode/);
thewww/name is reserved for the future product website. Make targets
(DOCS_STAMP), CI workflows, scripts and living docs updated in the same
commit; ADR-0086 amended in place (owner-approved). -
The dashboard counts every agent CLI, not just Pi.
GET /api/sessions/statsnow aggregates all six agent CLIs — Pi, Claude Code,
Codex, OpenCode, Hermes Agent and Grok — so spend, activity, tokens, tools and turns
describe the machine rather than one CLI. On the machine this was built
on, the same 7-day window went from $517.04 to $1,916.35 — the old
number was 27% of the truth, with nothing on screen to say so. Two new
breakdowns ride along:byCli(which CLI the money went to) and
coverage(what each CLI can and cannot report), plus code impact,
timings and quota windows where a CLI records them. A metric a CLI never
writes is reported as not reported, never as0— Codex is never given
an invented price, and shows the quota window it does record instead
(81% of a weekly limit, resetting Saturday, on the machine this was built
on). Parsing is cached per file, so the minute-by-minute refresh costs
46 ms rather than the 2.4 s a full re-read would. A new
?scope=machine|picodenarrows the window to folders a PiCode workspace
claims; the default counts everything, as before. (ADR-0097) -
The dashboard shows which CLI the money went to. A By CLI card
ranks spend per agent CLI with a billing badge (api/sub), and four
new panels read what the guest CLIs record and Pi never did: Code
impact (lines added/removed, cost per line), Agent time (waiting on
models vs. running tools — agent time, not elapsed, since sessions run at
once), Limits (a quota window's headroom and reset), and
Efficiency (cache hit, cost per turn). A What each CLI reports
matrix shows the blind spots as data. A This machine / PiCode control
narrows the window to claimed workspaces. -
The dashboard never prints
$0.00for spend it could not measure. A
day whose sessions are all still running has no price on disk yet; it now
reads—with "not priced by any CLI that ran" instead of a zero that
looked like "free". Sub-cent spend reads<$0.01, a plan-covered CLI
reads "not priced", and a period with no activity says so instead of
ranking six CLIs at zero. -
Spend now lands on the day it was earned. A compaction marker is
counted on its own timestamp instead of the session file's modification
time, so a long-running session no longer dumps its whole compaction
history onto the day it was last touched. -
The desktop user menu follows the mobile v2 pattern: rows are grouped
(Tools / Agents and connections / PiCode / Continue) and each carries a
one-line description; a search field filters the menu with the same
matcher as the phone's More screen; a no-match search says so with a
Clear search action. Theme and layout radios stay in the menu; the
install button and version hide while searching.
Citesdocs/benchmarks/2026-09-07-mobile-v2.md. -
The workspace card's actions are two, not five: a New menu (agent,
shell terminal, Agent CLI terminal) and one overflow menu holding Files,
Git graph, Sessions and Remove workspace. Both triggers stay visible at
rest — the old strip appeared only on hover, out of reach of touch — and
the destructive action no longer sits beside the creative one. The menu
hides Git graph on a folder that is not a repository and Sessions while
the workspace has no agents, where the route answers 409. The empty state
gained its two actions: "Empty — add an agent or a terminal". The card
header stays one line: the rows under it already carry path and branch, so
repeating them on the workspace was noise (owner's call, 2026-09-07).
Seedocs/benchmarks/2026-09-07-workspace-card-toolbar.md. -
Mobile v2: compact conversation and terminal controls, searchable Work
views and grouped tools give more space to active work. Each agent keeps
its unsent draft and attachments while navigating; message options open
in a sheet and Enter adds a new line. Failed sends offer Retry. -
Pi terminals no longer show a checklist strip above the pane. The
TUI already draws the plan; the sidebar card still carries the one-line
step. The pane is just the terminal. -
Composer attaches a photo from this device. An image button next to
the paperclip opens Photos, the camera, or a file picker. The paperclip
still attaches a file from the agent's folder on the PiCode machine.
Same caps as paste/drop: four images, 4 MB each. -
Mobile extra-keys row no longer shows a vertical overlay scrollbar.
Dragging the row sideways ispan-xonly; the terminal screen, the
row and xterm hide overlay scrollbars so iOS cannot paint a gray strip
down the right edge. -
Mobile extra keys no longer leave a black strip, and hide the TUI
behind them. The phone shell fills the screen while the keyboard is
closed. Opening it shrinks the terminal (and refits xterm) so the
prompt stays above the extra-keys row; that row is opaque. Safari's
undo/Done pill above the keyboard is the system IME — a web app cannot
remove it. -
Mobile extra keys sit above the phone keyboard. The terminal key
bar is one horizontally scrolling row (esc, tab, ctrl, alt, arrows,
Ctrl+C, then Home/End/pages and| ~ / -) that opens and closes with
the software keyboard instead of a two-row Termux grid the IME could
cover. The phone shell sizes itself to the visual viewport so the chat
composer stays visible while typing. Sticky Ctrl/Alt are unchanged.
The same row is available on an agent's Terminal view. -
Loopback browser sessions end when the access ends (ADR-0049
amendment 2026-09-06). An auto-minted loopback browser session — the
silent mint every browser on this machine gets, the headless QA fleet
included — is revoked by a minute housekeeping sweep once its last
authenticated request is 10 minutes old: a closed browser stops
refreshinglast_seen_at, an open one keeps the row alive even with
Chrome's once-a-minute background-timer throttle. Each revocation is a
session.revokedevent, so open Devices views drop the row live; the
daily prune deletes the rows a week later. Paired devices (phones,
paired loopbacks) and the install token are never touched. The pile of
offline "Headless browser" rows self-clears on the first sweep after
upgrade. -
Development process (ADR-0086, owner-approved after the 2026-09-06
cost review):make ci-scopedruns only the gates a branch's diff can
break;make closeends a worktree session (scoped gates, regenerated
OpenAPI/llms/captures, fast-forward check, closing summary);make worktree NAME=xhardlinksnode_modulesinstead ofnpm ci;make worktree-gcremoves merged, clean, idle trees;make docs-checkwarns
on stale capture fingerprints instead of failing (--strictfor the
old gate);docs/handoff.mdis capped at 100 lines by the pre-commit
hook and per-session notes move todocs/handoff/;docs/screenshots/
is frozen (evidence stays invar/screenshots/). -
Sidebar text column corrected for the smaller identity mark. The
runtime-favicon resize shrank agent/terminal identity marks from 24px to
16px without updating the folder/branch line's (and, transitively, the
checklist line's) left inset, so every row's.ws-contextsub-line sat
8px right of its own title in production. Found while re-verifying the
checklist chevron fix below against the deployed favicon change; the
inset is now 23px (16px mark + 7px row gap) everywhere it is used. -
Checklist chevron moved to its own gutter (owner refinement, follow-up
to the compact-view alignment below). The disclosure's chevron sat in the
same 31px column as the title/folder text, but a rendered icon's ink
rarely touches its own bounding box the way plain text does — the owner
spotted the chevron reading as offset from the title above it even though
the boxes lined up. The chevron now sits in a dedicated gutter before
that column (the tree-view convention: a leading disclosure mark, then
the label), so the text of the checklist line and its expanded steps
lands on the exact same column as the title, subtitle and folder/branch
line, independent of the chevron icon's own inset. No JSX change. -
Checklist compact-view alignment (owner refinement,
docs/plans/ sidebar-checklist-expand.md). The counter drops its parentheses (5/8,
not(5/8)) and moves to a fixed column at the row's end, the Linear/
GitHub sub-issue idiom, instead of prefixing the step text. The line and
its expanded list now sit in the same 31px text column as the card's
folder/branch line below it — previously flush left, out of the card's
grid. The disclosure line's focus ring matches the row's own selection
style (box-shadow) instead of a text-field-like inset outline. A
finished plan (every step completed) dims to the same weight as a
completed step, so it stops reading as open activity. The plan line
moves above the folder/branch line on agent and terminal cards, matching
identity → activity → location. Mobile's sub-line drops its parens too
(5/8 · text). No server or domain change. -
Identity favicons match the workspace favicon. Agent faces (sidebar
rows, collapsed strips, editor tabs, inspector) and terminal CLI badges
now render at the workspace favicon's 16px box with its 3px radius —
natural image shape, no forced circular crop. Letter/glyph fallbacks keep
the white plate for sidebar contrast; image faces stay full-bleed. Row
indent (ws-meta) re-aligned to the smaller identity mark. -
Checklist line refinements on cards (ADR-0092). The
(x/n)counter
no longer wears the accent color — the operator line is one muted line.
An absent checklist (the task required a plan and none was written)
now renders as silence: no line, no "No checklist", on agent cards,
terminal cards, the terminal pane strip and mobile rows. The data plane
is unchanged — the absent marker is still published and stored.
Removed
- Mobile: the "Running" section is gone from the Now home; agents and terminals running stay visible with their state chips in the Work tab.
Fixed
-
Reloading no longer halves fullscreen mode. A reload always brought the mode's layout back but not the browser part — no gesture, so no fullscreen and no keyboard lock — and you had to toggle it off and on to get the keys back. Now the first click or keypress after the reload completes it on its own: the browser goes fullscreen again, the terminal keeps every key, nothing else changes. If the first gesture is Escape or the fullscreen chord, it does what it means (leaves the mode); synthetic input never triggers it.
-
Mobile: the Work tab no longer scrolls horizontally — the per-workspace action strip (Communication, Files, Git, +Agent, +Terminal) grew wider than a phone screen and pushed the whole page sideways; the actions now live in an "…" menu on each workspace row.
-
Prevent OpenCode from hanging when communication reconnects its native conversation; validate resumed readiness without overriding newer activity or permission requests.
-
Connect identified Codex conversations without restarting, and preserve their native identity when older launchers emit auxiliary completion notifications.
-
Preserve terminal dimensions during communication setup and recognize Claude Code's compact footer without requiring the browser panel to resize.
-
Communication now recognizes Grok 1.0.25's bordered input when delivering message and connection-test prompts. Drafts, unfamiliar layouts and prompts that would wrap remain protected.
-
A panel whose terminal or agent is deleted lets go of its screen at once instead of holding it until the cache trims, and keeps the name it was showing, so the row reads shell · Gone — That terminal is gone.
-
Pi connection setup shares its receiver registration, preserves native input and rejects stale process or conversation identity.
-
Communication preparation resumes the exact idle terminal conversation only after verifying the previous writer has exited; an unverified shutdown blocks replacement.
-
Managed Pi reconnect checks pending delivery, native commands and approvals before stopping. A stale stop cannot remove its replacement.
-
Sidebar: the plan (checklist) line's text starts on the card's left edge — same column as the title, subtitle and folder/branch icons — instead of floating ~16px right on a ghost indent.
-
Native conversation identity no longer falls back to the most recent session for enrolled terminals; daemon restart recovery verifies the current pane/process.
-
Prevent duplicate conversation addresses across
/newand resume, stale activity reports, multiline draft submission, and attention starvation behind another recipient's backlog. -
An explicit successful Messages refresh clears stale action errors while preserving history.
-
Codex lifecycle hooks remain active on resume/fork by placing their scoped overrides in the native subcommand.
-
Desktop: the default right-click menu (Copy, Paste, Reload PiCode, Toggle theme) again renders rows like the terminal context menu — icon and label together on the left, instead of the label pushed to the menu's right edge.
-
Creating two terminals at once on a machine with no tmux server running
no longer fails one of them with "server exited unexpectedly": the daemon
retries the tmux startup race forhas-sessionandnew-session. -
The HTTP docs pair shells the way the server actually pairs.
docs/apinow showspicode pair(the old example called an endpoint that never existed) and no longer claimsPICODE_INSECURE=1skips pairing — that is the Who must pair: Off setting (PICODE_AUTH_MODE=off). -
The architecture index renders as one table again on GitHub and the docs site (a stray blank line had broken it at ADR-0090).
-
Stale file path in the routes doc (
web/src/lib/fileDocument.js→web/desktop/…, mirrored inweb/mobile). -
Development process (ADR-0105 follow-up). The pre-commit hook lets the
release cut through (aCHANGELOG.mdedit that adds a## [x.y.z]
heading) and parses staged changelog fragments so a malformed one fails at
commit time, not at release.make deploycommits only the refreshed
captures, never whatever else was staged.make adrallocates the number
across every worktree even when run from inside one. The split
architecture files link todocs/plans,docs/designand
docs/benchmarksagain. Capture tolerance is an absolute 128 px budget
with the count printed per surface. -
Pi terminal resumes use the recorded conversation file, including older session pins without resume arguments.
-
Communication setup preserves unrelated OpenCode inline JSON settings and MCP servers; malformed inline configuration blocks launch with an actionable error.
-
Sidebar cards: folder/git icons no longer crush on long paths. A
deep workspace path flex-shrank the row's folder or branch SVG to a
sliver; the label ellipsizes now and the icon keeps its 12px slot. -
Sidebar checklist: no more
undefined/undefined. Creating a plan in
the same turn as a refused edit/bash posted an emptyblockedmarker that
could land after the real list; the disclosure then concatenated missing
numbers. Absent plans render as silence again (ADR-0092); a parallel
mutator no longer overwrites a plan this task already wrote. -
Stable Agent CLIs layout. All tabs share the same page width, card
padding and tab alignment. Switching to Packages no longer narrows the
page; desktop scrolling keeps its horizontal space reserved. Sessions and
CLI setup actions wrap within narrow screens. -
Native settings recovery. Keep unsaved model patterns across temporary refresh failures, block stale writes until retry succeeds, and leave mobile agent settings and global key bindings usable when Pi defaults cannot be read. Desktop tool-mode restart failures stay visible as partial success and stop the sequence. Tools and Checklist menus now fit within the desktop viewport.
-
Pins: opening a pin with a heading or a list no longer restores an
"unsaved" draft nobody typed. The editor normalizes the markdown it
loads and announced that as an edit; loading is silent now. -
Mobile: the Work list wears the workspace's project favicon. The
workspace groups on the phone's Work screen showed a plain folder icon
even when the project has a favicon the desktop sidebar displays. The
group head now uses the samehasFaviconadvertisement and
/api/workspaces/{id}/faviconendpoint, falling back to the folder
icon when the project has none or the file fails to load. -
Schedules in a zone with daylight-saving time no longer hang the
daemon.internal/cron's next-match search stepped the wall clock by
hour and looped forever across the spring-forward gap (02:00 does not
exist, so the step went backwards); it now steps by duration through the
gap. Automations never hit it because the host zone has no DST; pin
reminders in a named zone would have. -
Web: CLI favicons no longer vanish on the dark theme. The dark-mode
invert for transparent monochrome vendor marks was scoped to the Agent
CLIs view only, so terminal rows in the mobile Work list and the desktop
sidebar, tabs and inspector rendered black-on-dark. The rule now applies
on every surface; Pi and colored raster fallbacks keep their native
colors. -
Pins: limits refuse instead of truncating, large screenshots can be
annotated, an edited sketch shows its new picture, and two editors no
longer overwrite each other. A title or note over the limit answers
400 with the limit spelled out (the studio counts the note near 100 KB)
instead of a byte-sliced text that left invalid UTF-8 behind; tags are
capped at 40 characters and whitespace folds to one-. Annotating an
image keeps the picture by reference, so the drawing's 2 MB cap is about
the drawing and a 6 MB screenshot annotates fine; the old "scene is
required" message for that case is gone. Preview URLs carry the file's
version, so re-saving a sketch changes its thumbnail and the picture in
the note at once. Save sends the version it loaded and a 409 offers
Reload instead of losing the other writer's edit; the studio retains an
unsaved draft across navigation and reload ("Unsaved changes restored",
Discard). A file dropped on a new pin names the pin after the file, never
"Untitled", and Cancel offers to delete a pin created that way. The
sidebar follows the change feed instead of refetching on every
navigation, and the list no longer carries note bodies (nor do
pin.created/pin.updated, which now share one summary shape).
Sketch bytes are written atomically; a boot sweep removes attachment
directories whose pin is gone; downloads keep accented names
(RFC 6266). Pins v2 review:docs/plans/pins-v2.md. -
Inbox: replies to
ask_humanquestions asked from a terminal pi now
actually reach the terminal. A pi running as an Agent CLI terminal
filed questions aspi (unmanaged); the reply path only delivers to
agent-sourced items, so replying marked the item done with a "Reply
sent" toast while nothing was ever delivered. Terminal-sourced items
now carry the terminal's identity and the reply rides that terminal's
receiver back into the exact session that asked (ADR-0037 amendment,
ADR-0089's door in reverse); failures reopen the item with the reply
preserved. A blocking question from a source with no channel at all is
now refused visibly and stays open — it can no longer be closed while
nothing was sent. Requires pi-inbox 0.2.0 installed in the pi session
(pi install -l …/packages/pi-inbox); items filed by 0.1.x as
pi (unmanaged)must still be answered by hand. -
Mobile: the installed web app's tab buttons sit as low as the platform
allows. In standalone the tab content was centered in a 56px bar that
ends at the (shortened) layout viewport, leaving ~11px of dead nav below
the labels on top of WebKit's unreachable strip; the content and its pill
now anchor to the bar's bottom edge there. The standalone detection also
measures instead of assuming (screen height vs. visual viewport;
WebKit 317153 reports the behavior varies with the iOS generation and the
icon's install date), so edge-to-edge installs keep their real insets.
An opt-in probe, opening the app with?strip-probe=1, extends the shell
into the unreachable strip to test whether element painting survives —
if labels survive at the screen's physical bottom, that offset can become
the default; if they are clipped, the answer is no.
Preferences → Layout now exposes that extension as the user's own
Screen edge choice (clipping risk stated in the option label). -
Mobile: the active-tab pill covers the label, and the installed web
app no longer shows a dead band above the home indicator. The active
pill used to hug the icon only (28px tall) and its bottom edge cut
through the label's first two pixels; it now sits behind icon + label as
one surface. On iOS home-screen installs (standalone), WebKit parks a
status-bar-sized strip below the layout viewport that no element can
reach and still reportsenv(safe-area-inset-bottom)inside it — a
double count that wasted ~81pt under the tab bar (WebKit bugs 313800,
254868). The shell now flags iOS standalone at bootstrap
(navigator.standalone), treats the bottom inset as already reserved
(Safari and Android keep real insets), and paints the canvas so the
unreachable strip continues the surface it sits under — the tab bar's
panel on tab screens, plain content on pushed screens — instead of a
black band. Recovers the 34pt of doubled inset for content. -
The dashboard's reliability numbers were wrong for every guest CLI, and
said otherwise. Claude Code showed 0 errors against 409 real ones (a
tool's failure comes back on the user turn, which the counter skipped),
45 aborts that never happened (a normalstop_sequencewas read as one)
and none of its 4 refusals; Codex showed 620 prompts against 455 (its own
AGENTS.md injections were counted as the person's); a week of Claude Code
showed 42 sessions where 30 ran (subagent transcripts counted as their
own). The "What each CLI reports" matrix now derives from what the parser
actually saw instead of a per-CLI claim, and the tests parse real lines
from each CLI's store. (ADR-0097) -
A prepared command no longer swallows the next one. A git command typed
into a terminal and never submitted stayed on the prompt; the next one
landed glued to its end and the shell answeredfatal: only one reference expected. The line is cleared before anything is typed — for the Inspector
rail's Git menu as well as the graph's. Stated plainly: anything you had
typed at that prompt and not submitted is discarded, where before it was
corrupted. -
Undo never composes
reset --hardover work you just made. Undoing a
commit uncommits it and leaves the changes staged; undoing a merge, rebase,
pull, cherry-pick or revert moves the branch back with--keep, which
refuses rather than lose a local change. A hard reset is offered only to
undo a hard reset. -
A git action that could not be sent no longer reads as sent. A busy
repository or a moved terminal keeps the form open with the reason; the
graph does not wait for a change that was never asked for. -
A worktree created from inside a worktree is a sibling, not a child.
The command names<repository>/.worktrees/<name>absolutely instead of a
path relative to wherever the terminal sat. -
Branches of a second remote work.
upstream/feat-xis fetched from,
pulled from and deleted onupstream, not refused as "not a branch". -
Keyboard focus stays inside a git action form that has no field of its
own (merge, fetch, rebase…); it landed on the page behind before. -
The graph's "waiting" line no longer spends its patience while the tab is
hidden, and when it does give up it shows the repository as it is now. -
Browser-tab icon is back on
/desktop/and/mobile/. Since the
desktop/mobile split (2026-09-05) the app pages linked the favicon, Apple
touch icon and PWA manifest under the application path (/desktop/favicon.svg),
where nothing is served — tabs fell back to the browser's placeholder icon
and the manifest 404'd. The three brand links now keep pointing at the
site-root files (as ADR-0072 intends), and the build enforces it. -
Reloading no longer announces every agent that was already waiting:
the needs-you pass could not tell "nothing is waiting" from "nothing has
been read yet", so it seeded from the first render's empty fleet and the
first real answer looked like a fresh arrival — a reload with three
blocked agents raised three sticky cards on top of the badges that
already said so. The pass now waits for the fleet to have been read
once. A question that arrives while you are looking still announces
itself, which is the whole point of the card. -
Agent CLI attachments no longer dirty the project's
.gitignoreor
pile up forever: attaching an image or file to a Claude Code/Codex/
Grok/Hermes/Pi terminal (ADR-0089) used to append a silent, uncommitted
.picode/drop/line to the project's own tracked.gitignoreon the
first attach — showing up as an unexplainedM .gitignoreingit status— and did nothing at all in a project with no root.gitignore,
leaving every staged attachment fully untracked. A nested
.picode/.gitignorenow covers the folder unconditionally, without
touching the project's file either way. Staged attachments older than
7 days are also swept the next time anything is attached in that
project — nothing removed them before. -
Terminals reattach after a phone lock: locking the phone (or any
network drop) no longer leaves a dead "— detached —" terminal that
must be exited and reopened, losing the reader's place. The browser
reattaches automatically — backoff 1 s → 10 s for up to ~95 s per
burst, an immediate retry when the app becomes visible or the network
returns — keeping the same xterm pane; tmux preserves the copy-mode
scroll position across attaches. A tmux session that is truly gone
says "Session ended. Reopen the terminal." once, and heals by itself
if the session comes back. -
Mobile recovery: partial fleet failures preserve prior results; stale
session responses cannot replace the newly selected CLI. A CLI diagnostic
such as "No models available" no longer appears as a provider/model.
Automation actions reject duplicate taps and restore rejected toggles;
session handoff keeps controls locked through a confirmed retry and shows
execution failures inside its sheet. -
New Agent CLIs get Activity reporting on. A CLI added to the catalog
no longer imports with the switch off because it was missing from
enabled.json. OpenCode rows frozen that way are turned on once at boot.
Opening an OpenCode terminal printsStarting OpenCode...until the TUI
draws, so the pane is not a blank cursor. -
llama.cpp setup recovery: failed or interrupted initial setup removes
only its recorded empty folders, preserving existing or unexpected content.
Unreadable ownership proof retains the recovery record until access is restored. -
Phone terminal: extra keys stay off until you open the keyboard.
Opening a terminal no longer focuses xterm on attach, so iOS does not
show the extra-keys row without the phone keyboard. The header keyboard
button lets that tap summon the IME. Pinning the shell to the visual
viewport requires a real shrink against the unfocused baseline, so a
rest-state gap no longer leaves a black strip under the TUI. -
Update checks failed on real installs: install-method detection now
resolves symlinks (~/.local/bin/claude→ the native versions dir) and
reads wrapper scripts (Hermes Agent), so update/reinstall/uninstall
controls appear for actual installs instead of "No managed lifecycle". -
Owned llama.cpp model cleanup now recognizes verified Hugging Face cache
downloads even when the router omits file paths. It records the exact blob
and snapshot link, validates their hashes and refuses shared or changed files. -
GitHub CI on macOS: the worktree test compares symlink-resolved
paths (/var→/private/var), and the terminal run/type routes
validate the request (404/409) before asking for tmux (503), so the
matrix is green without tmux installed.