v0.3.0
PiCode v0.3.0
Added
-
Terminals appear in Agent permissions (ADR-0143): the section now lists
the terminals you started in PiCode beside the managed agents — name, tier
and domains, with the same editor — and says plainly that apistarted
outside PiCode has no identity and always reads the tab on screen. -
Muse Code and Antigravity accept native handoffs.
Continue in…
now offers them innativemode, not justbrief: the conversation
lands as a session their own CLI lists and resumes (Muse: an index row
plus a session log; Antigravity: a summaries row plus the brain
transcript). Both were proven against the real CLIs, including a
same-shape round trip before the write counts. -
Terminals are listed as principals in the browser grants API (ADR-0143):
GET /api/browser/policiesreturns each terminal with its effective grant,
andPOST /api/browser/policyacceptstermbesideagent— the key
carries the namespace (term:<id>), so a terminal can never widen an
agent's grant or the other way round. The settings rows that show them are
the next slice. -
One release, two binaries, one resident to swap (ADR-0142, slice 3).
The release workflow buildspicode-shell-windows-amd64.exebeside the
Go binaries (Rust + cargo-xwin recipe, proven on a fresh Ubuntu 24.04
container) and stamps the tag into the shell.picode-desktop update
downloads both exes, verifies them againstSHA256SUMS— refusing an
unverified binary like the daemon's updater — and swaps both or neither,
rolling the tool back if the shell fails.scripts/desktop-swap.sh
relaunches whoever thePiCodeDesktoptask points at (tray today, shell
after the migration). -
Terminal agents identify themselves to the built-in browser (ADR-0143):
thebrowsertool now sends the house identity tuple — managed agent id
first, then the PiCode terminal id — and the daemon resolves a terminal's
own grant (browser.policy.term:<id>) beside the existing per-agent key.
A caller with neither stays read-only on the tab on screen. -
The shell tray reaches disk parity (ADR-0142, slice 2). The tray now
shows the disk line (WSL … · ≈… held · C: … free, with thelow
warning), a Give-back item with the readiness interlock, an explicit
stop-the-distro confirmation and a measured result, plus Restart PiCode
and View logs — every duty the Go tray menu owned. One board composes
status, disk and tooltip so no timer erases another's write; the
keepalive re-arms itself after a compact. -
Antigravity launch is editable. Same honest shape as Muse Code in
the previous slice: real defaults editor, profiles, the Customize
checkbox and the row menu's Launch settings. No hook surface in
its build either, so Activity reporting stays off with the one-line
note andintegration: trueis refused. -
ADR-0143: terminal agents are principals in the browser permissions —
the identity tuple (managed agent → terminal → unmanaged), the
term:<id>grant key and the decision table behind it. -
The shell is becoming the only Windows resident (ADR-0142, slice 1).
picode-shell.exenow holds the WSL distro open with a supervised
keepalive, polls daemon health every 5 seconds, and reports it in a tray
status line and tooltip — the duties the Go tray owned.--hiddenstarts
the resident with no window (the logon-task mode); a second launch brings
the window up.picode-desktop startup-repair --retarget-shellmoves the
PiCodeDesktoptask to the shell, refusing foreign tasks and missing
executables; plain repair now accepts either resident. -
Muse Code launch is editable. Its Launch tab has the real defaults
editor (executable, arguments, PATH, environment), profiles, the
Customize checkbox and the row menu's Launch settings — the gaps in
the terminal···menu are closed for Muse. Sessions and resume are
unchanged. -
JavaScript (Settings ▸ Browser ▸ Browser permissions): sites may use
JavaScript — on by default, applied to every open tab at once and to the
ones created later, through the platform's own setting. -
ADR-0142: the shell becomes the only Windows resident. The Go systray
(picode-desktop.exe --tray) retires;picode-shell.exetakes autostart,
the WSL keepalive, health/disk polling and the single tray icon, while the
Go binary stays as the headless CLI tool the shell drives. Window close
hides, tray Quit exits. Migration runbook in
docs/plans/retire-go-tray.md. -
Site settings (Settings ▸ Browser ▸ Browser permissions): one dialog
with the six kinds that matter — camera, microphone, location,
notifications, clipboard, autoplay — each set to Allow, Block or
the platform's own default, applied to every site through the shell's
policy, plus Recent decisions: every standing the browser recorded,
with the site, the kind and a Reset. Until the Ask prompt lands, a kind
with no choice follows the platform's default (deny), which the dialog
says plainly. -
Site permissions, the shell half (Settings ▸ Browser): every tab now
answers permission requests — camera, microphone, location, notifications,
clipboard, autoplay, sensors, MIDI, fonts, file system — from the policy
the user set (btab_set_permission_policy), and reports each outcome as
btat://permission. The app records the standing through the daemon's
API, so the Site settings dialog has real data to list and edit. -
Site permissions, the data half (Settings ▸ Browser): the daemon now
keeps one standing per site and kind — camera, microphone, location,
notifications, clipboard, autoplay, sensors, MIDI, fonts, file system —
withGET/POST /api/browser/permissions,DELETE /api/browser/permissions/{id}andPOST /api/browser/permissions/clear
(optionally one kind), all tested, on migration 050 and the events
invariant. The shell feeds it and the Site settings dialog reads it in the
next slice. -
Muse Code and Antigravity sessions can be continued elsewhere.
Both are now handoff sources:Continue in…appears on their session
rows, offering every CLI that can receive the conversation. Muse reads
through its ownexport --session(official transcript); Antigravity
reads the CLI's per-conversation transcript, deliberately not the
SQLite protobuf (unversioned field numbers). Reasoning never travels;
oversized sessions fall back to the brief, which both already support. -
Muse Code and Antigravity can receive a brief handoff.
Continue in…
(on a session row, a terminal row, or the pane menu) now lists them as
destinations: the conversation travels as an opening brief —
muse "<brief>"starts the session with it,
agy --prompt-interactive "<brief>"does the same. They are the first
prompt-only targets (no native session import yet), sobriefis the
only mode offered until their writers land. -
Downloads (Settings ▸ Browser): Location shows where the built-in
browser saves files (the system Downloads folder until changed), with a
Change dialog; "Ask where to save downloads" decides between a save
prompt and writing straight to the folder; Download history lists every
file with its size, time and outcome, searchable, with Open file /
Show in folder / Copy path / Remove per row and a two-step Clear all. -
The shell reports each download (start and outcome) through
btab://download; the list lives in the daemon store
(GET/POST /api/browser/downloads,POST /api/browser/downloads/status,
DELETE /api/browser/downloads/{id},POST /api/browser/downloads/clear). -
Every Agent CLI now shows the same tabs. Launch, Terminals, Sessions,
Providers, Settings, Keyboard, Packages and Connectors appear for all of
them, so the pane has one shape instead of three. The tabs a CLI has no
native editor for — today all of them except Pi — say
"… for are in development — coming soon" instead of pointing at Pi,
and a deep link on a phone scrolls its own tab into view. -
The tmux app shows the machine's tmux servers. A new Sockets tab
lists every tmux server this instance can see — the default socket, every
named one, and PiCode's own dedicated socket — with what each holds: N
session(s), how many are PiCode's, and whether anything is listening (a
socket file with no server is labelled as the leftover it is). The tab
badge counts the running servers, and the row for PiCode's own socket says
where new terminals will land. -
Custom provider form: per-model display name, input (
text,
image) and cost (USD per 1M tokens, all four rates or none), a
streaming usage compat flag, and a provider string per thinking level
(xhigh→high), so a gateway likemeta-aiis fully describable in
the GUI — no hand-editedmodels.json. Clearing a row field removes the
stored key; Edit prefills everything the file holds. The API key flow is
unchanged: a literal credential intoauth.json, exactly as
cheaperinference. -
Servers in the inspector rail: what is listening on this machine, who owns it (the PiCode terminal or agent whose process holds the port) and what the page calls itself, with one Open that shows it in PiCode's own browser tab.
-
A loopback URL printed in a terminal (Ctrl+click, or the pane menu's Open …) opens in PiCode's own browser instead of the system browser — unless Browser settings says local development sites should open externally, in which case nothing changes.
-
Without the desktop app, that browser tab renders a page from this machine in a frame, with one line saying so, and its address survives a reload.
-
POST /api/browser/history/deleteremoves a selection of visits in
one transaction. -
PiCode terminals get their own tmux server. Each instance runs tmux on
a socket inside its data directory (~/.picode/tmux.sockfor the main
install), separate from your personal tmux: akill-serveron one side can
no longer take the other down, and scratch/QA instances stop creating their
sessions in your tmux. Attach from your own shell with
tmux -S ~/.picode/tmux.sock attach -t picode-…; inside a PiCode terminal
everything works as before. -
Terminals created before the change keep running: the daemon follows both
servers while they drain, and the terminal list, status and tmux app show
one fleet. They move to the new server when they are restarted or recreated. -
The tmux guard has a switch. Preferences ▸ Terminal (Terminal defaults)
gains a Safety section: the tmux guard shows its state and toggles
between On (refuseskill-server, pattern kills and other terminals'
sessions inside PiCode terminals) and Off. The line says the change
applies to terminals opened from now on, links to how it works, and keeps
the switch honest under failure — a failed refresh says so and offers
Try again, a failed toggle reverts and reports. -
The wiring status now installs the guard on first read, so a fresh instance
reports the default-on state instead of "off" until its first terminal. -
Antigravity sessions. The Antigravity pane has a Sessions tab like
Muse Code and the adapter CLIs: every conversation Google's CLI keeps on this
machine, with its folder, age, size and turn count, grouped by folder and
filtered to the workspace you are in. Open in terminal resumes that
conversation (agy --conversation <id>) in its own folder. Read-only. -
Sessions for a CLI whose history exists before it has an adapter: the pane
shows the tab whenever the server reports a session source, instead of only
for CLIs with activity reporting. -
tmux server loss is now recorded while it happens. A daemon-side watch
probes the tmux server every 15 seconds: a server that dies with sessions
running gets aterminal.server_lostentry (last known session count,
socket and when it was last seen) in the feed and a line in the daemon
log; its recovery getsterminal.server_back, and a running server whose
session count drops by five or more between probes leaves a log line.
Until now a lost fleet was only reconstructed on the next daemon start. -
tmux guard. PiCode terminals now run a
tmuxwrapper that refuses
server-wide kills (kill-server,kill-window,kill-pane), pattern
kills, and kills of sessions another terminal or the user created. It
allows exact kills of sessions your terminal created and stamps sessions
you launch with your terminal's mark, so cleanup stays possible. On by
default; every refusal is explained on the pane and logged to
<dataDir>/tmux-guard.log. -
Clear browsing data (Settings ▸ Browser ▸ Browsing history): one
two-step button clears cookies, site storage, cache, service workers,
download history and WebView2's browsing history from the shared work
profile — saved passwords and autofill are deliberately untouched (the
Autofill switches own those). Our own history list clears with it. -
Muse Code sessions. The Muse Code pane has a Sessions tab: every
conversation the CLI has on this machine, with its folder, model, age and
size, filtered to the workspace you are in. Open in terminal resumes that
exact conversation (muse --resume <id>) in its own folder. Read-only —
PiCode lists and resumes, it never rewrites Muse's session store. -
Sessions for a CLI whose history exists before it has an adapter: the pane
now shows the tab whenever the server reports a session source, instead of
only for CLIs with activity reporting. -
Autofill and passwords (Settings ▸ Browser): Password manager and
Contact info switches push straight into the work profile — every
webview applies them at creation and live on change. -
Open destinations (Settings ▸ Browser): web links and popups, and
local development sites, each open in PiCode (adopted as tabs) or in
the system default browser. The work browser's shell hands external
destinations over with a scheme-checkedbtab_open_external. -
Show full URL (Settings ▸ Browser ▸ Address bar): on — the address
bar keeps the path, query and fragment (today's behavior); off — site
origin only. Saved per machine, live on every open tab. -
HTML previews run on their own address (
<ticket>.localhost), so a page can keep settings, register a worker and use its own storage — and neither Save nor Reload resets it. -
When the browser cannot open that address, or when PiCode is reached from another machine, the preview falls back to the sandboxed frame with one line above it saying so.
-
Browsing history (slice 3, first half): the work browser records one
visit per navigation — a URL re-reported updates the newest row in place
instead of piling up (typed flag sticks once set). Settings ▸ Browser
gains a Browsing history section: newest-first list, per-row Remove,
and a two-step Clear history. The address-bar dropdown reading this
store is the next increment. -
Muse Code and Antigravity in Agent CLIs. The catalog lists Meta's
muse
and Google'sagy: installed or not, Check setup, New terminal, which
runs the CLI in a PiCode terminal with its own defaults, and Check for
updates in the same ⋯ menu every other CLI uses — Muse Code against its
release channel, Antigravity against the vendor's release manifest. There
are no launch settings, no session list and no activity state for these two
yet. Both wear their vendor's mark (Meta, Antigravity) wherever the CLI
appears. -
HTML previews render unsaved editor changes too: the pane hands the buffer to the preview, and saving the file returns it to disk.
-
The grants editor lives in Settings ▸ Browser: one row per managed
agent with its effective access — Read (the tab on screen), Act or Full
plus the hosts it may reach. Pasted entries are forgiven (scheme, path
and port stripped); the change saves per row and is live for the
agent's next command. Slice 4 of the desktop browser plan is complete. -
HTML previews reload themselves: editing the page or any file it loaded on disk refreshes the frame (served-asset watch over SSE), and a page too large for the text editor still previews from disk.
-
HTML files preview as real pages — scripts run, styles and images load from their folder — in a sandbox that never reaches PiCode's session, with Reload and Open in browser.
-
The shell enforces the browser grant at navigation time: once an
act-tier command drives a tab, agent-caused loads outside the agent's
domains — script redirects included, not just thenavigateverb the
daemon pre-checks — are cancelled. User-initiated navigation is
untouched; navigating the pane from its own toolbar disarms the gate
until the agent acts again. -
A public Changelog on the docs site. Newest first, including what is
still Unreleased. Not a blog — the same Keep a Changelog file, readable
next to the guides. -
An MCP cookbook. The MCP page is the index. Gmail and DeepWiki each
have their own page: install, sign-in if any, and how to revoke. -
A Configure overview. Which screen edits which file, the one
workflow that does not mix Preferences with pi Settings, and a worked
example for a custom endpoint. -
Agent browser split (ADR-0135): a terminal that hosts an agent — the
agent's TUI or a CLI launch — can open the work browser beside it
(right-click → Open browser), bound to that agent so thebrowserPi tool
acts on the pane on screen. The split resizes (drag), maximizes, and closes
from the pane's own controls. -
The split survives a relaunch: the layout (which tabs host a pane, the
dragged width, the maximized flag) and each pane's last url persist in
localStorage. After the desktop app restarts, the panes come back at the
pages they were on — the first time a host tab is shown, its webview is
recreated where the pane is. Panes whose agent or terminal is gone are
pruned on boot instead of reopening to nowhere. -
Archiving and deleting snippets now work on the phone. A snippet's page
has Archive / Unarchive and Delete (with a confirm naming the
snippet), and the list has an Archived view next to Active — so an
archived snippet is visible and can come back instead of disappearing with no
handle on the other side. -
The Snippets guide now covers the whole editor: what the slug line means
while you type (free, or in use with Save off), the placeholder table
(default, optional, and the choices list that becomes a dropdown when you
send), where snippets come from (starters, saving a selection, importing a
prompt, duplicating), and that a half-written snippet no longer dies with the
tab. -
The phone's snippet editor does what the desk's does. The placeholder
table is there — default, optional, and the list of offered choices per
{{name}}, with a reserved name ({{branch}},{{cwd}}…) saying where it
comes from instead of pretending you can set it. Broken{{placeholders}}
are named under the body while you type, Save waits until they are fixed,
and an address already in use is a line under the Slug field rather than
a refusal after the tap. Choices you add keep working when the snippet runs:
a value outside the list is refused. -
Custom endpoints: the base-URL field now explains itself. A hint and an
example follow the API type you pick — an OpenAI-style root usually ends in
/v1, Google's in/v1beta, and Anthropic-compatible endpoints differ — so
the field stops being guesswork. -
More thinking formats, including the two that need an object. The format
picker now offers what pi actually supports:openai(the old
reasoning_effortname was written back asopenai),deepseek,qwen,
qwen-chat-template,openrouter,together,zai,ant-ling,
string-thinking, plus chat-template and baseten, which reveal a
JSON editor forchat_template_kwargs/chat_template_argswith pi's
$varreferences. A typo is explained inline instead of being saved. -
Verify a custom endpoint for real. The row's Verify with the endpoint
(1 request) sends one minimal completion (one word in, the smallest output
ceiling the API accepts) and reports what the endpoint said — the model, how
long it took and the tokens it counted. The dialog can do the same before
saving. Built-in providers keep pi's free answer. -
The snippet editor tells you when an address is already taken — while you
type, not after the save: the line under the Slug field says which
snippet holds/snip:…, and Save stays off until you pick another one. On an
existing snippet its own address is never reported as a clash. -
Docs: browser tools for pi — a new guide explains what an agent can read
in the work browser, what a grant adds (act:evaluate,navigate), and
who may call it: a managed agent has its own grant, a plainpiTUI reads
the tab on screen. -
The dashboard now says what is waiting on you. One line above the
numbers — “3 need you · 2 questions in Inbox · 1 terminal waiting” — with a
single action that goes to the Inbox, or to the waiting terminal when the
Inbox is empty. It appears only while something is blocked. -
Sidebar Apps tab: an APPS header and a live "Search apps" filter (Escape clears), with tiles sorted alphabetically — same pattern as the Pins tab.
-
Custom endpoints: load the model list instead of copying it. The Add/Edit
endpoint dialog has a Load models button under the ids: it asks the
endpoint what it serves and adds the ids you are missing (your typed ones
stay, nothing is reordered). It also fills context window and max output when
every listed model reports the same number, and says so when they differ. It
works for OpenAI-compatible gateways, Anthropic and Google endpoints. -
The failure says what to fix. A refused key, a URL that needs
/v1, a
host that does not resolve, or an account that cannot list: each is one line
naming the next step, in both apps and in the same words. -
The tmux app: every session on your tmux server, in one screen. PiCode
runs every terminal and interactive agent in tmux on your own server, and
until now a session with no terminal or agent behind it was invisible in the
product — you had to leave PiCode and runtmux lsto find it. Apps → tmux (on the phone, More → Apps) now shows the whole server: PiCode's sessions (with Open), sessions no
longer in PiCode's records, and your own tmux sessions beside them, marked as
not PiCode's and left alone. A row expands to the folder, command, uptime and
attached clients; the Server tab carries the version, socket, client
count and keyboard mode, plus the terminals whose sessions are gone
(including the ones the flight recorder saw die in a restart). -
Removing a leftover asks, and re-reads the session before it acts. A
session PiCode cannot attribute can be removed one row at a time, behind a
confirmation and a receipt (session id, start time and pane process): if the
session changed since the page was drawn, nothing happens. There is no bulk
cleanup, because a session missing from PiCode's records may belong to
another PiCode on the same machine — the row says so instead of guessing, and
every removal is recorded as an audit event. -
A grant can let an agent act. With the
acttier, two verbs join the
browser tool:evaluate(run an expression in the page) andnavigate(go
to a URL). Without a grant both are refused by name, with the way to grant
them. -
Agents can read the page open in the work browser. A new
browsertool
(snapshot,screenshot,events) reads the tab on screen in the desktop
app and answers with its structure, a PNG path, or what the tab recorded. -
Custom endpoints: pick how thinking travels on the wire. The Advanced
section of the Add/Edit endpoint dialog now names the thinking format —
reasoning_effort(the OpenAI standard), DeepSeek, Qwen, OpenRouter,
Together or Z.AI — instead of assuming every gateway speaks
reasoning_effort. The choice is written ascompat.thinkingFormatin
~/.pi/agent/models.json; leaving it at the default writes nothing, so pi
keeps choosing for that API type. -
Starters in the snippets studio. An empty page now offers six ready
prompts — review a pull request, explain an error, write a commit message,
summarize uncommitted changes, run the tests and fix failures, standup
update — each opening in the editor with its placeholders, tags and address
already filled in. Once you have snippets of your own the list collapses into
one line you can reopen. -
Duplicate, on a list row and in the snippet itself. A near-miss becomes a
copy you can edit: title gets “copy”, the address gets-copy, and the
original is untouched. -
Snippets are easier to write. The studio editor now validates the
body as you type — a broken{{placeholder}}shows a line naming the
problem and Save says why it is off — and everything the grammar
knows is editable from a table under the body: Default,
Optional and Enum per placeholder, written back into the text.
A Try it pane gives each placeholder a sample value (enums become a
picker) and shows the expanded prompt, including what will be asked for
at run time. -
Save a prompt straight from the composer. Select text and a Save
as snippet button appears in the message bar; the same action is in
the right-click menu as Save selection as snippet, so any text you
select — in the composer or anywhere in PiCode — can become a snippet
without retyping it in the studio. On the phone it is Message
options → Save as snippet. -
Import a prompt from another tool. The snippets list gained
Import: paste a prompt and PiCode finds its slots, offering
[BRACKETS]andUPPER_CASEas{{placeholders}}— each suggestion
can be switched off — then opens the editor with the result. Available
on the phone too. -
Custom endpoint form (Add provider → Custom endpoint): a Reasoning
model switch with the thinking levels the model answers on
(minimal…max). The selection is written as pi's per-model
thinkingLevelMap, so the levels a gateway really supports show up in the
model picker —maxincluded — instead of stopping athigh. Edit
prefills the same levels from the file. -
Work browser: the shell now speaks CDP itself. The desktop app carries
a command bridge over the page host API, so a page's DevTools protocol is
reachable without any open debug port. Read-tier page events (navigation,
console, network, log) are recorded per tab for whoever polls them. -
Continue in Pi now asks where it opens. Pi is both a CLI and the
platform's managed agent, so its "Continue in…" entry has a second
menu level — Pi agent · in the app (a stopped agent, no installed
CLI needed) or Pi CLI · in a terminal. The dialog's new Where
section lets you change the choice before continuing; a brief to the
agent lands as its first queued prompt. -
Files: switch workspace from the tree's toolbar. The folder line at the
top of a Files tab is now the workspace that folder belongs to, opening as a
picker of your other workspaces — every folder is offered, repositories or
not, with the folder path as its second line and a check on the current one.
Picking one re-points the tab: the same folder swaps the owner in place,
another folder moves the tab to it (one tab per folder, as ever), and a pick
that cannot resolve leaves the tree exactly as it was. A single workspace
keeps the plain folder line. -
Snippets. Save a reusable prompt or a shell command under Tools or
the command palette (#/snippets). Placeholders use{{name}}.
Type/snip:in an agent composer to insert or send one, pick Send
snippet in the command palette, right-click a running Agent CLI
terminal and choose Send to terminal…, or Run command… on a
shell pane — the confirm always shows the exact command before it runs. -
Mobile Git: switch workspace from the screen. The folder line at the top
of a Git screen is now a control when there is more than one workspace to
read through: it opens a sheet of your workspaces, each with the branch that
folder is on and a check on the current one. Picking one opens the Git screen
for that workspace, so Back returns where you were; folders that are not Git
repositories are never offered, and a single workspace keeps the plain line. -
Git graph: switch workspace from the toolbar. The graph's first item is
no longer the repository's name but the workspace its history is read
through — a dropdown of your other workspaces, each with the branch that
folder is on. Picking one retargets the reading folder without leaving the
tab: sibling worktrees of one repository swap in place (the HEAD dot and the
this worktreerow move), and a pick into another repository moves the tab
to it, so one tab per repository still holds. Folders that are not
repositories are never offered, and a pick that cannot resolve leaves the
graph exactly as it was. -
Providers: Custom endpoint in Add provider registers any OpenAI-compatible (or Anthropic-/Google-compatible) gateway for pi from the GUI — name, base URL, API key and model ids; no hand-editing
~/.pi/agent/models.json. Definitions merge into pi's own models file, the key is stored with every other sign-in, and the roster gains acustombadge with Edit endpoint / Sign out / Remove endpoint actions. -
Continue a terminal's conversation in another CLI. The sidebar
⋯
menu, Agent CLIs → Terminals⋯, and the pane's right-click menu offer
Continue in… for the conversation that terminal is running. The
existing preview dialog (what travels, what is left behind) opens; the
original terminal stays put. A memory tool that injects a “where you
left off” note in the same folder is a separate layer, not this action. -
Text on the canvas. The toolbar has a Text element: draw a
rectangle and a panel appears there ready to type in — a label beside a
terminal, a note to whoever opens the canvas next. It saves as you pause
and when you click away, and everyone looking at that canvas sees it.
Two thousand characters; a pin is still what holds longer writing. -
The zoom percentage is back, under the zoom buttons. It says where the
camera is, and clicking it returns to 100 % — the only zoom at which a
click inside a terminal lands on the character it points at. -
Add an explicit, guarded “Activate now” action for open Agent CLI conversations that still need their first native event.
-
Git guard:
maincan no longer be rewound — the reference-transaction
hook refuses any move ofmainthat is not a fast-forward, including a
stale-ref fast-forward onto a divergent tip that silently erased merged
work once. A deliberate rollback requiresPICODE_ALLOW_MAIN_REWIND=1.
The guard's functional tests live in the hooks selftest. -
Desktop shell: every window carries a dedicated app bar in the ChatGPT
style — brand on the left, drag anywhere on the bar, double-click to
maximize, and flat Windows caption buttons (close turns red) on the
right. The bar belongs to the shell and works no matter which version
of the web UI the daemon serves; the browser renders no bar. -
Right-click the canvas. The plane has its own menu now — the same one
the⋯button opens, with Show controls on top. -
Show controls hides the canvas chrome. One switch takes away the zoom
column, the toolbar and the minimap, so the plane is only the plane. The
right-click menu still works with them gone, which is how you bring them
back; the choice is remembered per browser. -
Terminal attachments: sketch. The desktop attach bar and the phone
sheet gain a Sketch button — an Excalidraw pad (the dependency, not the
pin studio) whose drawing leaves the composer as a PNG. The chip reopens
for editing until Send. -
The Inspector follows the panel you are on in a canvas. Click a
terminal or agent panel and the rail shows that one's files, changes and
branch — until now a canvas left it on whatever it was showing before, or
empty. Focusing a note, file or diff panel leaves the rail where it was. -
Desktop shell: the tray item is now Management — a window with three
views over the WSL distro. Disk shows what the distro holds and runs
the Give back flow with live progress; Clean measures the prunable
caches (picode clean --list) and prunes the selected ones — Pi sessions
and the PiCode database are never cleanable; Config edits
.wslconfig(memory, processors, swap, sparse disk) with an automatic
backup, leaving unknown settings untouched; changes apply at the next
full WSL restart. -
picode clean— new subcommand that prunes the cachespicode disk
measures (--listto measure,--apply id1,id2 --yesto prune,
--jsonfor tools). Data directories are refused even when asked for by
name. -
Desktop shell: sharper taskbar/window icon (the icon file's largest
image now comes first) and the shell's local pages use the product's
design tokens. -
Packages: Configure now works for descriptor-declared packages.
pi-roles was the only package with a Configure button; any extension whose
configuration is described (PiCode's catalog, or apicode.configmanifest
in the extension itself) now shows one, backed by a form that edits the
package's own config file. Configurable today: pi-web-search (the model
that backs native web search) and pi-compact (compaction policy — enabled,
token/percent triggers, floor, cooldown, summarizer model, thinking and
instructions). The files stay the packages' own source of truth: unknown
keys survive saves, unset fields stay unset, and a broken file is reported
and never silently replaced. No description for a package you installed?
"Describe config…" lets you describe its configuration yourself — create,
edit and delete the description at any time; PiCode stores it locally.
Public docs (docs-site/guide/packages.md) now explain configuring and
describing packages. -
PiCode Desktop: give the held space back from the tray. When the disk
file is holding space the distro freed, the tray offers Give back ≈92
GB…. It first asks PiCode whether anyone is working (the same interlock as
a deploy — someone mid-turn is named, and it stops), then asks once in a
dialog that names the cost, stops Ubuntu, converts the disk file to
sparse, starts Ubuntu again and reports the before/after measured on the
file. From then on WSL returns freed blocks on its own. -
picode-desktop disk-compact— the same flow from a terminal.
--dry-runprints the plan and stops nothing;--yesruns it;--force
overrides the working check;--method optimize-vhdcompacts with Hyper-V's
Optimize-VHD from an administrator terminal (Windows Home: upgrade WSL for
the sparse path instead). -
PiCode Desktop: the tray now tells you what the disk is doing. One line
under the status —WSL 218 GB · ≈92 GB held by Windows · C: 27 GB free—
refreshed every five minutes, ending in— lowunder 20 GB free, with the
tooltip carrying the same sentence and the command that shows the rest. -
picode-desktop diskreports both halves of a WSL disk in one screen.
The Windows side (the VHDX path from WSL's own registry, the file's real
size, whether it is sparse, free space on the volume) and the distro side
(filesystem, the caches with the exact command that gives each back, the
top-level breakdown of home). It shows held for nothing: the space the
distro has already freed that the disk file still occupies — invisible in
Explorer, and the reason a fullC:stays full. Read-only. -
picode disklists what occupies the machine and what is safe to
reclaim, one line per item withsafe,redownloadordata, plus the
paths it could not read named as such instead of folded into a category.
--jsonfeeds the tray and, later, the Storage app.
Changed
-
The tmux watcher integration test and the kill-server test address a server they own with
-Sinstead of rebinding the process-wideTMUX_TMPDIR: a test that ends a server can no longer strand other tests — or a background goroutine — in the same binary on the server it dismantles. The kill test keeps asserting the directory guard; the watcher test asserts the suite's namespace is untouched. -
No Activity reporting for Muse Code, stated plainly. Its build
offers no hook surface, so the toggle is replaced by a one-line note
("its terminals stay Open") and the server refusesintegration: true
with 400. The startup seed that switches Activity on for empty configs
skips hookless CLIs. -
The work-browser tab no longer spends a line telling you it is a frame: the page starts directly under the address bar.
-
The address bar's Open button fills the pill it shares with the field (its hover covered only a 28px strip) and uses the return glyph instead of a text
↵, which sat high in its box. -
The setup tabs are placeholders until each CLI's editor exists; Pi keeps its
real Providers, Settings, Keyboard, Packages and Connectors panes. No editor
is implemented for another CLI before its launch settings match the others. -
Autofill and passwords rows now open Manage dialogs instead of
hiding the switches on the page: Password manager offers "Offer to save
passwords" and a two-step delete of everything the profile stored;
Contact info offers "Save and fill addresses" and the same delete for
saved form data. Each dialog says plainly that saved entries live in
this machine's browser profile and cannot be listed or edited per entry
— WebView2 exposes the switches and a wipe by kind, nothing that reads
entries back. -
Browsing history now presents the data the way the reference does,
inside the dialog: a search field, day groups that collapse (newest
open, "Today"/"Yesterday" named), and a row per visit with its site
icon, host, visit time and a per-row menu (Copy link, Remove from
history). Rows can be selected and removed in one go, and "Clear
browsing data" sits on the section header. -
Scratch instances (QA scratch, docs fixture) and their cleanup operate on
their own tmux server, so their sessions no longer appear in — or collide
with — your tmux. -
Custom endpoint is now Custom provider. Titles, buttons, menus,
toasts and the Providers guide use the new name (matching ADR-0129);
routes, API paths and behavior are unchanged. -
Clear browsing data now opens a dialog in the reference shape: a
time-range picker (hour / 24 hours / 7 days / 4 weeks / all time) over
a checklist of what to clear — browsing history, cookies and site
data, cached images and files, download history, autofill form data
and site settings — each showing what it will remove. -
Web: the editor tab strip (browser and desktop) now follows Chrome's CR23
tab language — raised active tab, hover pill, idle separators — instead of
the rectangular accent-underline tabs. Overflow, keys and the all-tabs list
are unchanged. -
Header matches Chrome's tab strip: 41px tall with 35px tabs inset 6px
from the top, 20px idle separators, 8px corner radius
(docs/benchmarks/2026-09-15-chrome-tab-header.md). -
The selected tab now reads connected to the page on
/browserand
/desktop: it shares the toolbar background and no hairline cuts it
off, while the sidebar head, tab strip and inspector head read as one
header bar with a single hairline between them and the content and no
pane verticals crossing it. -
Settings ▸ Browser follows the reference layout: a page title and
lede, section headers, and cards of rows whose title and description
sit left with the control pinned right, divided by hairlines. Switches
are pills, selects and actions are outline buttons, and Browsing
history opens in a dialog. The chrome around the page stays PiCode's. -
The master Browser switch is real: off refuses every browser verb
for every agent server-side until it is turned back on. -
AGENTS.md: the scratch-tmux rule now names the measured mechanism —
$TMUXoutranksTMUX_TMPDIR(so a "isolated" client still talks to the
server it was started in),-Loverrides$TMUX, andTMUX_TMPDIRonly
counts when that directory exists. -
Custom endpoint is a page, not a dialog. Add provider → Custom
endpoint (and Edit endpoint) opens#/clis/pi/providers/customwith the
form in Identity / Connection / Models sections, Name + API type side by
side on wide screens, and Verify key beside the field it checks. The
how-to prose moved to the Providers guide; the page carries a Setup
guide link instead. -
Muse Code and Antigravity keep the same launch screens as every other
CLI. The New terminal screen shows the CLI row and the Launch preview
(which command PiCode will run, what it adds), and the Launch tab shows the
plan summary instead of a one-line notice. Nothing is editable for them yet
— the customize checkbox and launch profiles stay absent — so the screen
reads the same and says plainly that the CLI keeps its own defaults. -
Agent CLIs describe what a row can do as capabilities: a CLI can open a
terminal without carrying activity, launch settings or sessions. -
Web: the active editor tab is the elevated surface (the white the sidebar
reads as), so it no longer disappears into the strip gray. -
A stopped CLI terminal now sits in a simulated terminal window. The
state — what stopped it, the conversation Resume last session would
reopen, and the two actions — is drawn inside a framed terminal window
(titlebar, traffic lights, the terminal's own name and folder as the
title), centered on the page and hugging its content instead of filling
the pane. The page keeps the app's theme; the window is a picture of a
terminal and is dark in both themes. Same on the phone. -
A stopped CLI terminal is a real empty state, not a sentence in the
corner. Its pane now shows a mark, what stopped it (a plain stop, a
restart that ended it, or a failed last launch), the conversation
Resume last session would reopen — CLI, name and age, with the opening
words on hover — and the two actions. A terminal with nothing pinned says
no session to resume instead of offering no button and no reason. -
The empty terminal pane follows the app's theme (owner call): a light
app no longer shows a black well where a terminal used to be — the ground
is the app's own and the message reads in the app's inks, in both themes. -
make desktop-restartswaps the v2 shell too: the target now builds
picode-shell.exe(Tauri, ADR-0120) alongside the tray + native host, and
scripts/desktop-swap.shstops, copies and relaunches it when it was
running. A stale shell refuses page commands with a Tauri ACL error — its
capability list compiles into the exe (2026-09-14:btab_cdp_callrefused
for days after the CDP bridge landed).DRY_RUN=1prints the plan and
touches nothing. -
Tools docs open with Where and Not this. Browser tools, Chrome
extension, Docker, Integrations, tmux, CLI activity reporting and
keyboard each say what the page is, where to click, and what it is not. -
Inbox: "Clear all done" sits beside the filter on the Done tab, right-aligned on the toolbar row, instead of trailing the list — the same place every list page keeps its bulk action. Any app that declares a list-pane actions row gets this for free; an item's own actions and empty-state actions are untouched.
-
Chat and work docs open with Where and Not this. Canvas, session
messages, Inbox tools, Checklist, Compact earlier and the diff panel
each say what the page is, where to click, and what it is not. -
Agents docs open with Where and Not this. Agent CLIs, Packages, MCP,
llama.cpp, Automations and Snippets each say what the page is, where to
click, and what it is not. -
One right-click menu for every terminal pane (managed Pi in-terminal, Agent CLI, plain shell). Chat and the composer stay on the generic menu.
-
Send to terminal… on an in-terminal Pi pastes into the TUI (palette Send snippet too); the sheet says “Sent to the terminal.”
-
Attach files… / Ask Pi about this on an in-terminal Pi use
/api/agents/{id}/dropand/prompt, not a terminal id. -
Rename agent… / Remove agent… (existing cleanup confirm, never “This stops the tmux session.”). Terminal settings from an agent pane opens global Terminal defaults.
-
Continue in… on an in-terminal Pi uses that pane’s session file and asks to continue while the TUI is still writing.
-
Close browser split actually appears when the split is open.
-
Getting started installs from a GitHub release, not
make build.
Download the binary, runpicode install, open the app. Building from
source is a separate page. -
Public docs sidebar is Start / Use / Run / Configure / Reference. The
old flat Guides list is gone. Settings and Providers sit under Configure;
a Use catalog lists every capability in one place. -
A snippet Command no longer runs into a repository another agent is
writing in. The confirm step still shows the exact command; running it is
refused with a message naming who is at work there, the same rule the
terminal's Run command… already followed. -
Docker, Inbox and tmux now read like every other page. Their surfaces keep the app's tab strip, head actions and filter, but inside the same page frame a system route uses: a 1240px card, the view's tabs as an underline nav with count chips, the filter in the card toolbar, and the list and detail panes inside the card (side by side, stacking on narrow windows). Empty, blocked and error states are one line plus one action instead of a full-page poster.
-
The custom endpoint dialog reads field by field. Name, Base URL, API key
and Model ids carry a label above the control and one line of help under it,
with a clear gap between one field and the next — the placeholders were the
only label before, so a filled field could not be told from an empty one.
The helper prose that read as a lecture (a file path, a protocol note) is
gone; what stayed says what the control does. -
Limits belong to the model, not to the list. Context window and max
output are one row per model id (Advanced → Model limits), so Load models
fills each model's own numbers — a gateway whose models disagree is no
longer refused with a blank field and an explanation. A number typed by hand
survives a load; a row follows its id into and out of the list. -
Advanced is a section, not a wall. The disclosure holds grouped sections
— Request compatibility, Thinking, Model limits — each with a legend and a
hairline, so it reads as structure once open and stays one line while
closed. -
Load models and Verify key report inside Model ids, the field that owns
them, so a refusal line ("The endpoint refused the key (401): invalid api
key provided. Check the API key and try again.") sits next to the action that
caused it and never pushes the alternative route below the fold. -
Half-written snippets no longer die with the tab. Drafts moved from the
per-tab shelf to the browser's durable one, so closing the tab or reloading
brings the text back (a capture or an import is still handed over as a new
snippet rather than announced as "unsaved changes"). -
The Fleet tile counts agent CLI terminals, not just managed agents.
The sidebar's Claude Code, Codex, Grok, Hermes and OpenCode terminals were
invisible to the dashboard, which could read1 / 1 runningbeside seven
live terminals. The tile now showsN live(agents + agent CLIs), states
working/need you/idle/no signal, names each one, opens its tab
on click, and counts plain shells apart —no signalmeans a CLI is running
but has not reported activity, never that it is idle. -
The handoff board is an index now, not a ledger. Next steps still render
inline (they are bounded and they are what a session acts on), but debts are
one line per topic — the count, the topic file and its plan. Rendering every
debt made the board grow with the backlog, which is what filled it; it is
about half the size and no longer trips its budget on one honest entry.
make handoffnow fails, naming the file, when a topic file lists bullets
outside its## Next/## Debtsheadings — six snippet debts were invisible
that way for a day. -
Six snippet debts were invisible on the handoff board. The board renders
the bullets under a topic file's## Next/## Debtsheadings, and
docs/handoff/open/snippets.mdhad neither — so its debts never reached the
sessions that read the board. It has the heading now, consolidated to the
three that carry the meaning; the roadmap lives in the plan. -
Also pruned there and elsewhere: bullets that only restated a plan file, a
debt already carried by its owning topic, and the 0.2.0 release step (cut;
tagv0.2.0exists). -
Continue dialog uses the width of the screen. How and How much sit
as two columns; the brief preview is wider and taller instead of a
520 px column in the middle of the pane. -
Web: the dashboard no longer offers a folder-scope filter — the "This machine | PiCode" chips are gone and
GET /api/sessions/statsmeasures the whole machine only (ADR-0127).Spend by workspacekeeps ranking every folder, naming claimed workspaces and leaving the rest as their own folder; the payload'sscopefield and the?scope=parameter are removed. -
Web: the inspector panel no longer carries its own hide button — closing it stays with the single toggle at the end of the editor tab strip (browser and desktop).
-
An empty canvas is empty. The card explaining what a panel is no longer
floats in the middle of a new canvas — the plane, its texture and the
toolbar are the whole page. -
The canvas switcher is as wide as the canvas's name. It kept a fixed
minimum width, which left a gap after a short name. -
Panels are added from the canvas toolbar and nowhere else. The
Add panel… row left the⋯menu, and the empty canvas no longer
carries its own button — pick an element below, then draw where it goes. -
Surface split (routes): the responsive web app previously served at
/desktop/now lives at/browser/, and/desktop/serves the new
bundle composed for the Windows shell only (its entry wraps the app with
the shell chrome; the browser never loads it). The Management page moved
into that bundle — its design tokens are imported from the shared
package, ending the hand-copied token block. The launcher and the docs
screenshot machinery follow the new routes. ADR-0122. -
You choose where a panel goes. Add panel has left the toolbar. In
its place, at the bottom of the canvas, there is one button per thing a
canvas holds — agent, terminal, pin. Press one, draw the rectangle where
you want it, and pick which agent or terminal goes there. The panel is
created exactly in the rectangle, at the size you drew.Esc, or pressing
the button again, cancels. -
A new panel no longer appears off screen. It used to be placed from the
canvas origin outward, which is nowhere near what you are looking at once
you have panned. -
The canvas switcher and the
⋯menu moved to the top-left corner. The
bottom edge is now for the hands: what you add in the middle, zoom and
Fit on the left, the minimap on the right. -
Files and changes are added from
⋯→ Add panel… — they are opened
from a tab rather than drawn on the plane. -
The keyboard map is a pane of its own. It was a Keys sub-tab under the
Settings pane, which put two tab rows inside one view; it is now Keyboard,
sitting between Settings and Packages in the Agent CLIs pane bar
(#/clis/pi/keyboard). The Settings pane no longer has a sub-tab row, and the
link keeps the agent and layer you came from, so going back lands where you
left. A?tab=keysbookmark still opens it. -
The sketch controls sit where a thumb is. On the phone the tool row now
sits at the bottom of the drawing with the colour/undo row above it, and the
library/lock/hand column no longer crowds the pad; the pen is active on open,
so drawing starts with the first touch. -
Go tests are cached across worktrees.
scripts/go-test.shbuilds with
-trimpath, so the test cache no longer depends on the tree's absolute path:
a package tested once is reused in every worktree and terminal (measured 10.4 s
per package before,(cached)after). Sessions that touch several packages
get those minutes back on every iteration. -
make cikeeps its full output invar/ci-last.logand says so when it
fails. One full-matrix run failed unreproducibly with nothing but a bare
FAILleft in the transcript; retries would hide that, evidence does not. -
The contributor docs point at the generated board:
CONTRIBUTING.md, the
uiux-review/visual-reviewskills and the affected plans now send a durable
next-up or debt todocs/handoff/open/<topic>.mdinstead of telling the
reader to editdocs/handoff.md, whichmake handoffrenders (ADR-0123). -
Opening a sketch on the phone starts with the pen: draw immediately instead
of tapping the pencil in the toolbar first. -
The project board is generated.
docs/handoff.mdis no longer a file
every session edits (it was the repository's most-written file: 406 commits
in ten days, 99.8% of its size cap, net losing text).make handoffrenders
it — what is in flight from git, next up and debts from
docs/handoff/open/<topic>.mdand the session notes — and it is not
committed (ADR-0123). Contributors with an old clone see it disappear from
git status; that is the change. -
make worktree-statusshows what is actually open: each worktree's branch,
commits ahead/behindmain, dirty files, last commit and last green gate —
and flags a tree that has stopped producing commits. -
make closereuses the green scoped run when a catch-up merge brought
unrelated work (ADR-0124), instead of re-testing a tree whose tested content
did not change; it prints which of the two cases applied and why. -
docs-site/public/llms.txtis generated by the docs build and the deploy
instead of being committed (ADR-0125);make docsstill writes it. -
make dev,make ci-scopedandmake closeprint the worktree and branch
they are running in. -
The floating-controls experiment and the frame handshake were replaced
by the dedicated bar (ADR-0122 supersedes ADR-0121). -
The canvas controls stand in three places. Zoom and Fit are a
column at the bottom-left, the canvas switcher and Add panel are
centred, and the minimap keeps the right corner. The zoom percentage is
gone from the row. -
The canvas switcher is a PiCode menu, not the browser's. It used to be
a native dropdown, which opened an operating-system list over the plane in
colours nothing else in the app uses. -
The terminal message field is a textarea that grows. One line tall at
rest, four lines maximum, then it scrolls. Enter sends on the desktop and
Shift+Enter breaks the line; on a phone Enter breaks the line (no Shift on
a soft keyboard) and Send or Ctrl/⌘+Enter submits. -
Agent CLIs → Settings edits one layer at a time. A labelled switcher
(Edit: This machine / workspace / agent) picks the layer, the line under
it names the file that layer writes, and the body shows only that layer's
rows — instead of stacking the same six knobs for the machine and the
workspace and hiding the agent's below them. The chosen layer and the tab
travel on the URL (?layer=…&tab=…), so a reload or a bookmark lands on the
same view; an agent link opens that agent's layer. -
Rows say where their value comes from: Set here (with an accent bar) when
this layer sets it, Pi default or From This machine when it inherits.
A row this layer sets offers Use inherited, which hands the value back to
the layer below instead of freezing a copy of it. -
Keys is a sub-tab of its own: the whole 89-row keyboard map keeps its
filter and its Add-then-press-a-key flow, without sitting at the end of the
settings scroll. The global settings pane went from ~6000 px of scroll to
under 1000 px. -
The Canvas toolbar is one button group. The canvas and the camera were
two groups with a gap; they are now a single row — the canvas switcher,
Add panel, the zoom controls, and the⋯menu last. Add panel is
no longer filled in the accent colour, and Fit wears React Flow's own
four-corner glyph instead of diagonal arrows. -
The canvas switcher is always a dropdown, with the app's icon. With a
single canvas it had become a plain label; it is a menu again, so the
canvases you could have are visible from the one you are on. -
picode-desktopgained acleansubcommand that passespicode clean
through to the distro, streaming its progress. -
The Canvas chrome is now one toolbar on the bottom edge. The canvas
switcher, Add panel and the⋯menu left the top-left corner and
joined the zoom controls in a single dock centred at the bottom, as two
button groups: the canvas and its actions, then the camera. Add panel
is the filled button there. The minimap keeps the bottom-right corner, and
the top of the plane is now empty — nothing floats where you start
reading. On a narrow pane the minimap steps aside so the two never
overlap. -
Agent CLIs → Connectors is a roster, not a catalog grid. The pane opens
with one primary action (Add connector), then the configured services:
each row carries its state, the layer it lives in (title: the config file),
the target, a switch and an overflow menu (Sign out, Remove, whose
confirmation names the file). The catalog, the file picker and the target
pills left the pane and became one dialog with a search field, a labelled
Save to control, and two quiet secondary entries (Custom server…,
Import a file…). -
The redundant workspace line under the Connectors tab bar is gone (it
repeated the scope already shown per row), and every control in the pane is
the 36 px control height instead of a 28 px pill beside a 36 px button. -
Live status is only claimed while an agent runs: with the agent stopped the
pane says so once, instead of showingIdleon every row. A server that
needs a login shows Sign in on its row even with the agent stopped. -
The install target is part of the route (
?scope=), so it survives a
reload, and it is stated where the write happens instead of above the list. -
The Canvas camera controls moved to the bottom-left, as a column. Zoom
in, zoom out, the 100 % readout and Fit now stand at the far end of the
bottom edge, with the minimap opposite them on the right instead of
stacked above. Fit is an icon there; its keyboard shortcut (0) is
unchanged. -
A Canvas panel resizes from its bottom edge, its right edge and the
corner between them. The other five targets are gone: dragging one of
them moved the panel's top-left corner while it resized, so the panel
slid away from under the pointer. -
Agent CLIs → Packages: the install target sits with the action it
modifies. The "This machine / workspace / agent" radios left the row they
had under the source input — where they read as if they belonged to the
Installed list below — and now sit in the install bar, labelled Install
to, immediately before Install. Every control in that bar shares the 36 px
control height, and the bar wraps as a unit (source, then target plus
Install) when the pane is narrow. -
The redundant workspace line under the Packages tab bar is gone: it repeated
the workspace the target pill already names, and it sat hard against the tab
rule. The pane owns a 12 px gap under the tabs instead, on Packages and on
the pi-roles settings sub-page alike. -
Fullscreen hides PiCode, and leaves your browser window alone. Turning
it on no longer takes the browser fullscreen with it: your other tabs and
your address bar stay where they are, and the mode is exactly what it says —
the sidebar, the tab strip and the Inspector step aside, the edges bring
them back,Esccloses an open panel and leaves on the next press.F11
still works and now stacks on top of the mode, in either order. -
The canvas switcher is a name until there is something to switch to.
With one canvas the top-left control opened a list holding the canvas you
were already on. It is now simply that canvas's name; the moment a second
canvas exists it becomes a picker again. New canvas is in the⋯menu
either way, and the controls do not shift when the second one appears. -
An empty canvas now offers its next step in the middle of the plane —
one line saying what a panel is, and Add panel — instead of a blank
plane with the controls in the corner. The plane, its background and the
minimap stay where they are, and the line goes the instant the first panel
lands. -
The plane no longer shows the React Flow credit. The drawing library
behind the canvas is MIT-licensed and unchanged; only its badge is hidden. -
What's New opens on a fresh install too. A released build used to wait
until you had made a workspace, an agent or a terminal, which meant a brand
new install saw nothing and looked broken. It now opens once on the first
load, still closes for good when you dismiss it, and still waits while a
dialog, a reconnect, a waiting agent, an Inbox item or a create/share flow
needs you first. -
Agent CLIs strip is CLIs | Messages. Settings, Packages and Connectors
(MCP) are panes of the selected CLI, next to Launch / Terminals / Sessions /
Providers. Webhooks stay a PiCode page. Old Settings, Packages, Integrations
connectors and#/mcpsaddresses rewrite. -
Providers pane dropped the “This machine” lecture; empty llama.cpp is one
line plus Set up. -
A Canvas panel's edges and corners are now something you can actually
grab. Every resize target is the same size to the hand however far in
or out the plane is zoomed — a 20-pixel square at each corner, a
12-pixel band along each edge — instead of shrinking with the camera
until there was nothing left to aim at. What you see is the grid's old
language back: a short bar at the middle of each edge and a corner mark,
appearing when you hover, focus or select the panel. -
The line between two linked panels is a curve, and the line you drag
while making the link is the same curve you end up with. Two panels
sitting in the same row still join with a straight segment — that is what
a symmetric curve between two aligned points is. -
A link is easier to hit when zoomed out. The invisible band along the
line no longer thins with the camera. -
The Canvas background texture can be seen. Dots, Grid and Cross were
drawn in the colour of a hairline — on the light theme the grid was at
1.16:1 against the plane, which is to say invisible. All three now use a
colour chosen for a texture, and the dot is two pixels rather than one.
The spacing is unchanged, so nothing on a canvas moves. -
A stopped agent's panel offers Run as the accented button the agent
tab uses for the same thing, instead of a grey chip that read as
disabled. The same rule now runs through every panel placeholder: the
action that starts work is accented, the action that only opens a tab
or drops a dead binding is not. -
The Canvas background is now chosen on the canvas.
⋯→ Background
opens a submenu with Plain, Dots, Grid and Cross, each showing a real
sample of its texture and a tick on the one you are using. The rows stay
open while you pick, so the plane behind the menu is the preview. Your
choice is the same one you had — it is still remembered per browser, and
nothing on a canvas moves when the texture changes. -
Preferences → Appearance is PiCode's own chrome again: the theme, and
nothing else. The Canvas background group has left it, and so has the
⋯menu's oldBackground…item, which existed only to send you there.
An app's settings now live in the app — a project rule from today, not
just a tidy-up (ADR-0109). -
Messages' list of hand-drawn links reads as Messages', because that is
whose it is: it is now Granted contacts — the pairs you allowed to
message each other by drawing a link between two panels on a canvas. Same
rows, same Remove, same guarantee that a link never lets one session read
another's history. -
A failed compact no longer leaves the machine without its distro. The
restart afterwsl --terminateruns even when the conversion fails, and the
tray re-arms the keepalive child the terminate killed — without it, WSL
idles out sixty seconds after a compact. -
Agent CLIs opens from the last icon in the desktop sidebar header
(stacked boxes — the catalog of CLIs), not from the user menu Tools
list. Clicking it does not switch a rail tab. Package-update marks
move to that icon.Ctrl+Kand mobile More are unchanged. -
Launch Customize sits on the Launch pane heading again, not on the
Launch / Terminals / Sessions / Providers tab row. -
A CLI's page hosts Launch, Terminals, Sessions and
Providers as inner tabs. The catalog is the CLI picker. Pi shows the
account roster; other CLIs explain that Providers is unavailable and offer
Open Pi.#/clis/<cli>/providersis the list;
#/clis/<cli>/providers/newopens Add provider. Old#/clis/providers*,
#/providers*and#/more/providers*rewrite onto those addresses.
OAuth still returns to the list in the same app. -
A VHDX that is not sparse is named in the report as the reason WSL cannot
give freed blocks back, with the one command that changes it — it is not run,
because converting or compacting the file stops the distro and its sessions. -
Fullscreen: the exit control is a button again, not a sentence. The
label and the chord moved into its hint (Leave fullscreen (Ctrl+Shift+Enter)), and the glyph now sits in the same 28px square as the
Inspector toggle beside it. -
What's new is a two-column board on a desktop. The release dialog takes
880 px instead of 400 and lays the highlights out in two columns, so a whole
release fits one screen: the nine highlights of v0.2.0 are read without
scrolling, and a summary stops wrapping at ~40 characters. The dialog also
centres itself at any window height — a short window scrolls inside the
dialog instead of pushing it against the top of the screen. -
A CLI's page hosts Launch, Terminals and Sessions as inner
tabs. The catalog is the CLI picker; switching CLI keeps the pane.
#/clis/<cli>/sessionslists every folder;#/clis/<cli>/sessions/<id>
lists one. Old#/clis/sessions*,?cli=and#/sessions*links rewrite
onto those addresses. Dashboard top-session rows open that CLI's pane. -
The Canvas has no bar across the top any more. The plane runs from
edge to edge and the controls float on it: which canvas, Add panel
and a⋯menu sit in one small cluster top-left, the zoom controls and
the minimap stay bottom-right. The bar's icon and title were the tab's
own words, and its Close was the tab's ×. -
New canvas, Tidy panels, Rename, Delete canvas and Close tab moved
into that⋯menu. Everything the bar carried is still one press
away, by mouse or by keyboard —Tabreaches both clusters before it
reaches the panels. -
Fit now leaves the controls their corners, so opening a canvas no
longer parks a panel's header under them. -
Maximizing a panel hides the floating controls while it is up: they
belong to the plane, and the plane is what the panel covers.Esc
brings both back. -
The Canvas plane's background is yours to pick. Preferences →
Appearance now offers Plain, Dots (what it drew before), Grid
and Cross, each card showing the pattern rather than naming it. The
choice is remembered per browser and applies the moment you make it, to
every canvas already open. It follows the theme's colours, so switching
between light and dark re-tints the ground without a reload. -
Light mode is no longer pure white. The page was white and the panels
on it were grey, which is upside down: nothing lifted, and the app read as
one flat sheet. The page is now a soft, slightly cool grey and the
surfaces above it are near-white, with dialogs and menus in white above
those. Text sits at 15.9:1 on the page and 17.3:1 on a panel, and links
and primary buttons went one step darker to clear the readability bar they
were under. -
Providers: one row per account, columns that line up. The roster is a
dense table now — provider, account, identity, usage, 7-day spend and the
actions sit in fixed columns instead of being pushed to the two edges of
the card. The quota reading moved into its own column, so two accounts'
bars can be compared without reading a label, and a long vendor reason
("Rate limited.") stays on one line instead of wrapping under the buttons.
Ten providers plus the llama.cpp pointer and Recently used now fit one
1000 px-tall window without scrolling. -
The search field and Add provider are one cluster instead of two distant
corners, and a second account of the same provider repeats the provider's
name, dimmed and indented, so a group reads on its own. -
Recently used is a line of chips (provider, Sign in, remove) instead of a
full-height list of rows. -
Saving a terminal's launch settings — or opening one that was removed
meanwhile — returns to that CLI's page instead of the machine-wide list. -
The Matrix app is now called Canvas. Same boards, same panels, same
links; the name says what it is. Old bookmarks keep working —
#/app/matrixand#/app/matrix/<id>land on the canvas — and a tab
you had open reopens as the Canvas tab, with the canvas and the view
you left it at. -
Every board is the canvas plane. The 12-column grid layout is gone,
so there is one place a panel can be and one way to move it. A board
still in grid mode was converted once, keeping the arrangement it had —
a panel that was smaller than the canvas minimum comes out slightly
larger. -
The API and the feed were renamed with it:
/api/matrices…is now
/api/canvases…and thematrix.*events arecanvas.*. PiCode's own
clients ship in the same binary and were changed together. -
PiCode loads lighter for everyone. Canvas is fetched the first time
you open it instead of riding in every page load, and the two layout
libraries the grid needed are gone: 18 KB gzip off the first load of the
desktop, whether or not you use the app.
Removed
-
The Go tray is deleted (ADR-0142).
picode-shell.exeis the only
Windows resident — autostart, keepalive, health, disk line, Give-back,
Restart, Logs — andpicode-desktop.exeis its headless boundary tool
(doctor install disk disk-compact clean startup-check startup-repair update). The systray dependency is gone, so a second tray cannot come
back. A logon task that still points at the tray fails loud with the
repair named;installregisters the shell, staging it from the
matching release when no sibling is beside the tool. -
The last
surface: terminalcatalog row. Every launchable CLI now
carries an adapter entry; the terminal-only shape stays as the form a
future CLI without one rejoins. -
The user menu no longer carries a layout switch. The
Layoutgroup
(Desktop / Auto / Mobile) is gone from the menu in the browser, the desktop
shell and the narrow/mobile layout — the surface follows the address you
open (the launcher,/browser/,/desktop/,/mobile/). Theme
(Light / System / Dark) stays in the menu. -
From a Pi session in the create dialog (desktop and mobile) and its
API,POST /api/clis/{cli}/sessions/adopt. Agents are born only from
new sessions (ADR-0126, superseding ADR-0021). Agents already created by
adoption keep working; session files were never modified. The sessions
surface keeps list, delete and auto-clean. -
File and change panels can no longer be added. They had no tool of
their own and the old Add panel dialog was their only way in. Panels
already on a canvas keep working. -
The sketch pad's canvas menu drops Load, Save to file, Export image and Save
as image — the drawing is inserted into the composer, not saved from the pad. -
Fullscreen no longer hands the browser's reserved keys to your agent.
CapturingCtrl+T,Ctrl+Wand the rest needs a page that asked the
browser for fullscreen itself, which the mode no longer does; those keys
stay with the browser in every window. Everything else still reaches the
terminal untouched. -
Agent CLIs: the general Providers tab is gone. Provider accounts of a
CLI are read in that CLI's Providers pane, so the tab strip no longer
carries a second CLI picker. -
Agent CLIs: the general Sessions tab is gone. Sessions of a CLI are
read in that CLI's Sessions pane, so the tab strip no longer carries a
machine-wide list. -
Agent CLIs: the general Terminals tab is gone. A terminal is read in
the Terminals section of the CLI that launches it, so the tab strip no
longer carries a machine-wide list; an old#/clis/terminalslink opens
the CLI catalog.
Fixed
-
A terminal's folder is right from its first instant, for every reader.
#{pane_current_path}answers with the tmux server's directory — the daemon's own working folder — for a pane tmux has not polled yet (#{pane_pid}is already set while it does: 33 of 40 creations in a loop), so anything reading a terminal's cwd in that window saw the wrong folder: the creation response, the file reader that resolves a relative path (GET /api/terminals/{id}/text, which answered 404 once under a sharded CI run), the Inspector's root. The tmux manager now remembers the folder it created each session in and answers with it for the first seconds, then follows the shell again. -
In the desktop app, Documentation and other external links now open in
the system browser instead of dead-clicking. -
scripts/qa-scratch.sh stopends the daemon it started (the pid the daemon itself wrote todata/server.json, with the remembered pid as fallback) and verifies the port afterwards, instead of killing whoever holds the recorded port: a stale port file used to makestopkill a neighbouring scratch's daemon and leave its own alive. If a different process holds that port, the stop now leaves it alone and says so; terminals are still removed through the API first, so no tmux session is orphaned. -
startrefuses a port someone else holds instead of killing its holder, records the daemon's pid, andstatusprints port, pid, liveness and health. -
A tmux socket that cannot be created is an error, not silence:
NewWithSocketcreates the socket's directory, and tmux'serror creating …/error connecting to …text (printed with exit status 0 bynew-session) is promoted to an error. Before, a session on an unusable socket path "succeeded" with nothing behind it. -
A new terminal's folder is right from the first response. The creation answer read the pane's directory live, and that read races the pane's own process: tmux returns the server's directory — the daemon's own working folder — until the shell has spawned (measured: 12 of 30 creations in a loop), so the UI and the Inspector could see the wrong folder for a moment. The creation answer now names the folder the session was created in; every later poll still reads the live path, where a
cdshows up. -
The app shell now really runs under the Content-Security-Policy:
/browser/,/desktop/and/mobile/— the URLs the launcher and the desktop shell actually load — carried no policy at all, because the header matched only/,/index.htmland*.html. The script hash is computed from the file each path serves, so each shell's inline theme bootstrap stays allowed. -
The Inspector's Servers tab is reachable with nothing selected: the tab row used to render only when an agent, terminal or workspace was the anchor, so the rail's empty state ("Open an agent or terminal to inspect its files.") had no way to the one panel that is about the machine rather than the selection.
-
The download switch (and every browser preference) survives a save
now. The page read the daemon's preferences twice — on load and after
each save — with two copies of the same mapping, and the save path's
copy had lostaskDownload: the switch turned on, the answer came
back, and the row snapped off, never to turn on again. One reader,
web/browser/src/lib/browserPrefs.js, with the regression covered by
tests. -
The work browser's settings switches and actions now actually reach
the app. The shell's ACL manifest (build.rs) never listed the
commands added after the first slice —btab_set_prefs,
btab_clear_data,btab_open_externaland the whole Downloads set —
so every invoke was refused with "not allowed by ACL" and the page
swallowed the error. The commands are in the manifest and the
capability now, with their generated permission files. -
The download folder is picked with the app's folder browser (the
same one the Add workspace dialog uses) instead of a typed path, and
the picked place is translated from the daemon's tree to the Windows
drive the browser writes on (/mnt/c/...→C:\...). A folder
outside a Windows drive is refused with a line saying why. -
One PiCode can no longer remove another PiCode's terminals. Every
session PiCode creates now carriesPICODE_INSTANCE, the data directory of
the instance that created it, and the tmux inspector reads it: a session
from a different PiCode on the same machine is labelled another PiCode
instance's work — with the text saying which one — instead of being
offered as a removable leftover. The removal itself refuses it, so the rule
holds even if the request does not come from the screen. Sessions created
before this version are told apart the same way, by the loopback port their
session environment names. -
The work-browser tab took the whole window down:
WebTabSurfacenamed the "Show full URL" preference in an effect's dependency array above theuseStatethat declares it, so rendering any browser tab threwReferenceError: Cannot access … before initializationand React unmounted the app (blank page, every tab gone). A deploy from main would have carried it; a plain-browser session on main reproduces it in one click on New browser tab. -
The work-browser tab crashed the whole shell: an address-bar preference was read by an effect before its own state existed (
ReferenceError: Cannot access … before initialization, blank window on every work-browser tab). The tab strip also never received the tabs' addresses, so every web tab read "New tab". -
A long session preview no longer breaks the row. In the Sessions tabs the
second line is the CLI's own prompt text — a paragraph, or a quoted Windows
path. It sized the line to its whole text and pushed Open in terminal and
the row menu outside the card; at the same time the model name (a secondary,
single-line attribute) refused to shrink, squeezed the session name to
nothing and painted over the age/size/count. A session row is now two
deterministic lines: identity + facts, then the preview with its buttons,
each clamped with…. Seen on Muse Code and Grok; every CLI's Sessions tab
uses the same row. -
Terminal settings: a failed refresh of the guard no longer leaves the old
state on screen without a word. -
Clearing site data no longer touches saved passwords or autofill; the
earlier one-click version included general autofill in its mask and
missed WebView2's own browsing history. -
The server test that launches a terminal for the change feed no longer leaves
the tmux session behind: cleanup ran witht.Context(), which is canceled
before cleanups start, so every tmux call it made failed and each test run
left one session in the shared tmux server. -
make cino longer leaves orphan shells on your tmux server. The test
suites that start real tmux servers (server, tmux, term) now run them on a
private namespace that is torn down with the run — after a 2026-09-15 CI
run left twelve orphan shells behind. The harness also refuses, by
construction, to end a server outside its own directory. -
Terminal settings say what is wrong when there is no tmux server to
read. With every terminal closed, the terminal settings catalog now
answers "Start a terminal to read the tmux option list." instead of an
internal error — a state an isolated test suite exposed on 2026-09-15. -
A guard-style wrapper finding its real binary no longer depends on
dirname(1): under a minimal PATH it could resolve to itself and loop. -
README and architecture docs corrected. The Agent CLIs list now names
Muse Code and Antigravity as onboarding terminal-only entries, and the
architecture pages match the current routes (/browser/,
/api/canvases), the Go MCP broker, and theweb/browsercomponent
paths. -
Muse Code and Antigravity terminals wear their own mark in the sidebar,
the tabs and the phone. A terminal launched with a CLI that reports no
activity (both of them today) fell back to the plain-shell icon, the
Shell session subtitle and the Terminal open status. Every surface now
resolves the identity from the runtime CLI, otherwise from the CLI the
terminal was launched with, so the row reads Antigravity · Open or
Muse Code · Open like any other CLI. It also lands in the dashboard's
CLI bucket instead of counting as a plain shell. -
A terminal created while the page is open stops looking like a shell.
The creation only announced the bare record, so a new Muse Code or
Antigravity terminal kept the default icon until a reload; the launch view
now travels on the feed with it. -
Canvas: the plane fills the pane again, and No canvas yet sits in the
middle of it. The page-frame change of 2026-09-14 dropped the surface's
own flex column, so the stage collapsed to its content: an open canvas
measured 0px tall (an invisible plane) and the empty state hugged the top
of the tab. A native surface (Canvas, the QA demo) keeps that column now. -
Create an agent in the grants empty state now opens the New agent
form. It used to navigate home and nothing else (the handler called a
helper that fell through to the default route). The copy also says what
is true: grants apply to managed agents — sidebar sessions always read
the tab on screen. -
Grok 1.0.30 suggestions no longer block message delivery. The new
build restyled the unaccepted suggestion in its composer, so automated
prompts silently stayed pending. Both captured suggestion styles are now
recognized, with the same strict frame, cursor and footer requirements. -
Inbox replies reach legacy terminal questions. An
ask_humanitem
from pi in an Agent CLI terminal that predates per-question session
stamping can now be answered from the Inbox when the terminal's pinned
session and the conversation its receiver is showing agree; the item
still stays open with an honest refusal when they disagree. -
The agent split's browser pane stayed painted over other tabs: the
native WebView2 kept its last bounds when the hosting tab was switched
away (the pane mounts only for the active tab, and unmount never told the
shell to hide). Switching tabs — or closing the pane — now parks the
view; its page state survives and remounting brings it back. -
Resuming a stopped CLI terminal that fails now says why on the pane
("Codex was not found. Check its executable or PATH.") instead of nothing
changing when the button is pressed. -
Sidebar: each tab (Workspaces, Agents, Terminals, Apps, Pins) keeps its header and search bar pinned at the top; scrolling now moves only the list, not the tab's controls.
-
Communication delivery survives slow TUI redraws. A native CLI
(Grok and the others) that renders the received prompt late under load no
longer ends a delivery as uncertain: the post-paste composer check is
sampled inside a bounded window, and a refusal log names which guard
refused without exposing screen content. -
Destructive buttons on the phone (Delete) read destructive before the
tap. They only had a hover style, and a phone has no hover. -
Opening a link to an inbox item that is gone (answered or removed elsewhere) says so — "This item is no longer in the list" with Back to the list — instead of
not foundwith a Try again that could never work. -
The tmux app's Sessions tab shows its session count instead of a
21 · 21 unclaimedsentence, which no tab badge in the product had room for. -
The split view's empty detail pane says "Pick an item from the list" — on a narrow window the list is above, not to the left.
-
Communication: OpenCode now receives messages in narrow windows with deep project folders. The delivery check no longer mistakes the wrapped folder path in OpenCode's footer for typed text; it accepts exactly the path rows the width implies and still refuses anything else.
-
Desktop: clicking a work-browser tab in the tab strip now selects it (it
silently did nothing —openTabhad no web branch and fell through to the
agent lookup), and a selected browser tab no longer snaps back to the
previous tab on the next route reconcile. Work-browser tabs own a
#/web/<id>address the router writes and honors. -
The guide was showing
{{name}}instead of{{name}}.
Every placeholder on the public Snippets page was written as an HTML entity,
which a code block renders as literal text. The braces are real now, on that
page and everywhere the guide shows one. -
The Add/Edit endpoint buttons stay in reach. A tall custom endpoint form
scrolled its Back / Add endpoint buttons off the bottom of the dialog,
so saving meant scrolling to find them. The action row is now pinned to the
dialog's (or the phone sheet's) bottom edge and the form scrolls under it. -
Verify says what it covers instead of a status it never had. An endpoint
whose API type PiCode cannot speak (a hand-editedmodels.json) answered
"rejected the request (0)"; it now names the type and the four API shapes
verification can address, and reports that nothing was spent. -
A wrong key on a custom endpoint no longer reads as verified. Verify used
to answer from credential presence, which stayed green for a bogus key; a
gateway that cannot list, a refused key, an exhausted account and an unknown
model each now say which one happened. -
Quota readings no longer outlive their window.
Limitsnow marks a
reading whose window has already reset asstale · reset 2d agoinstead of
counting down to a past instant, and every row carries how old the reading
is (hover). A panel fed by one CLI also says so:Covers Codex only. -
A gateway's error message can no longer leak your key. If an endpoint
echoes the API key back in an error, PiCode redacts it before the message
reaches the browser. -
The
Todayrange drew one bar stretched across the whole chart. A
one-day window was one calendar day, so the panel showed a single
full-width block under a range picker that promised a chart.Todayis now
bucketed by hour (24 bars, labelledmidnight…11pm, panel titled
Hourly), and every longer range keeps one bar per day. -
The
eventsverb now honourssince: the sequence number the agent last saw
travelled at the top level of the request, where the channel never read it,
so every poll replayed the whole ring. -
A hand-edited
compatkey no longer hides the provider from the GUI. A
string value incompat(such as"thinkingFormat": "deepseek") made the
whole entry fail to decode, so the provider silently vanished from the
roster, Edit and the catalog while pi kept using it. The file is now read
key by key and unknown keys are left untouched. -
Right-clicking a selection inside a text field now offers Copy and
Save selection as snippet enabled: the menu reads the field's own
selection, which the page's selection never saw. -
make desktop-shellactually builds the Windows shell again — the target
was shadowed by thedesktop-shell/directory and reported "up to date". -
Agent CLIs: the page no longer sticks on its loading skeletons when the terminal list is slow —
GET /api/terminalsnow serves a shared snapshot (singleflight + 1s TTL) computed by a bounded worker pool, and the page keeps the last result that landed instead of discarding every response older than the newest request. -
Agent CLIs: web-tab ids (
w:<n>) no longer reach/api/agents/{id}/slashand/role-stateas agent ids (console 404s). -
A file, folder, git or app tab no longer asks the API for an agent's role.
Selecting one of those tabs fetched/role-stateand/slashwith a tab id
the server does not know as an agent — two 404s per selection, a chat
composer asking a repository its role. Both fetches now askisAgentTab
(a bare id, not a tagged surface), so they run where an agent is on screen
and nowhere else; the composer's role chip and slash list are unchanged on
an agent tab. -
Removing a workspace no longer takes the server down. The git watcher
killed the whole daemon when it noticed a watched folder was gone: that path
had left the watch set, and the pass still read the group it no longer had.
A removal — a workspace, its last agent, or a folder that stopped being a
repository — is now a quiet pass; the removal event is what reconciles the
sidebar, as it always did. -
Private connection setup no longer lingers on disk. PiCode removes a conversation's private setup files once its connection is revoked or the owner is deleted; the credential stopped working before, but the 0600 files used to stay behind.
-
Continue: a session too large for Native can still use Brief. The
dialog no longer leaves Continue disabled; Native is offered only when
the conversation fits, and Brief uses the recent turns. -
Grok terminals show Needs you while a question card waits. The
ask_user_questioncard notified anelicitation_dialognotification that
the hook map ignored, so the row stayed on Working for the whole wait. The
question is now reported as a held attention, and it survives the sibling
tool completions Grok runs in the same parallel batch — the hold ends only
when the question tool itself completes. -
Terminal side padding is even. xterm's fit floors the column count, so
unused pixels collected on the right of every pane (desktop app, browser,
and phone). The leftover is now split so both sides match the terminal
padding preference. -
Right-clicking a canvas opens the canvas's own menu again. PiCode's
generic menu (Copy, Paste, Reload PiCode…) was answering instead, which
also meant that once you hid the canvas controls there was no way to bring
them back. Right-clicking inside a terminal panel still opens that
terminal's menu. -
Keep a newly opened Claude Code terminal running when communication is enabled before its conversation is saved; connect after its first message creates a resumable conversation.
-
Keep terminal Pi message notifications pending while its editor contains an unsent draft, allowing delivery once the draft is cleared.
-
Explicitly ask native agents to execute the communication diagnostic after reading it.
-
Clicking the keyboard map no longer silently returns to Settings: the pane
rewrote the route to carry the selected agent and dropped the sub-tab with
it (the new pane cannot lose a tab it does not have, and the layer survives
the same rewrite). -
Deliver first-message notifications to an idle Grok welcome screen while preserving draft, identity and exact-submit safeguards.
-
Recognize an empty OpenCode editor with its sidebar visible and working directory wrapped below the footer. Communication now measures pointer fit against the editor width while retaining draft and post-paste edit protection.
-
The sketch pad on the phone fits the screen. The drawing surface now
fills the usable area — no header under the status bar, no dead strip under
the drawing — and its Cancel/Insert buttons are thumb-sized (44px). It lives
inside the shell's viewport, so the software keyboard shrinks it correctly. -
The sketch canvas is dark in dark mode. The pad asked Excalidraw for a
near-black canvas, which its dark-theme filter inverted back to light; the
canvas is plain white paper now, so the screen is dark and the PNG attached
to the terminal stays a white sheet. -
make worktree-statusno longer reports a finished branch as a stalled one:
a worktree whose branchmainalready contains reads merged —make worktree-gccan remove it, and the handoff board's In flight section
lists only trees with unfinished work (a dirty tree is never called merged). -
A settings value can now be un-set:
PUT /api/pi-settingsaccepts
patch.reset[]and removes exactly those keys from the layer's file
(other keys, including ones PiCode does not know, stay). An unknown name is
refused rather than reported as inherited. After a reset, a running agent
adopts the effective compaction/steering/follow-up values instead of keeping
the override that just left the file. -
A malformed
~/.pi/agent/settings.jsonno longer disables the agent's own
Model/Tools/Checklist fields on the mobile quick sheet. -
web: a TUI booting inside a terminal (OpenCode boots straight into it) could
freeze the pane at its first painted line until a reload. esbuild's minifier
dropped theletdeclaration of xterm 6'srequestModeenum while keeping
the(n = {})assignment, so the first DECRQM query threw
ReferenceError: n is not definedinside the parser and stalled xterm's
write pipeline. The desktop and mobile builds now pin@xterm/xtermto its
UMD build, which ships the enum pre-compiled. -
Recover observed native conversations and activity after a PiCode server restart on Linux/WSL, preserving running terminals, drafts and pending messages (ADR-0112).
-
Show activity, connection status and completed communication tests separately on desktop and mobile; keep selected participants visible while reconnecting.
-
Renew Pi receiver presence without restarting its terminal, and bind Hermes message commands to the native conversation when a background review changes its environment.
-
Deliver pending messages when Grok's empty composer shows an unaccepted native suggestion, preserving typed drafts and native approval guards.
-
Keep live native hooks working on hosts without Linux process metadata; distinguish temporary recorder writes from permanent recording failures.
-
Show a failed connection with a Terminal controls action when state recording is blocked, and record bounded delivery failure reasons without logging message content.
-
Bind native message commands to the launched CLI even when PiCode inherits another agent's environment; refuse a missing native identity instead of selecting the parent conversation.
-
Mobile rendered the Connectors pane without its stylesheet (the CSS was only
imported by the webhooks view, which mobile loads lazily). The pane imports
its own styles now. -
Removing the unreachable second "MCPs" view (
#/mcpsalready rewrote to the
Connectors pane) leaves one rendering path for both apps. -
Zooming out no longer turns a terminal panel white. Below 75 % a panel
shows its last screen as text instead of a live terminal, and that text
was landing on the panel's own light ground — the same words, suddenly
looking like something other than a terminal. The still now keeps the
terminal's background and foreground, following your terminal theme. -
Deploying from a PiCode terminal no longer refuses because of that
terminal. The interlock that protects other people's turns counted the
pane runningpicode deploy— which is working, by the act of asking —
so a deploy started from inside PiCode could be told to wait for itself.
It now ignores the caller's own pane;--forcestill means ending
someone else's turn. -
Opening Packages, Connectors or Settings from Agent CLIs while a
workspace agent is selected (sidebar icon, then the pane tab) puts that
agent and folder on the URL, so This workspace / This agent appear
next to This machine. -
Canvas links now attach to the sides of the panels that face each
other. A link used to leave from one point at the top of each card
wherever the two cards sat, so it could run out of a corner, cut across
the panel it belonged to, or — when a panel carried several links — leave
every one of them from the same spot. A link to a panel on the right now
joins right edge to left edge, one below joins bottom to top, and a
diagonal neighbour is met on the side it actually lies beyond. The line
leaves and enters at a right angle to the border it touches, and follows
the panels as you drag them. -
Several links out of one panel fan out instead of stacking. Each one
leaves from the point on that side which faces its own target, and two
that would land on top of each other are pushed apart just far enough to
read as two lines — in the order of their targets, so they do not cross. -
The line you drag is the line you get. While you drag a new link, the
preview leaves the same border the finished link will, and jumps to the
other panel's facing border as soon as you are over a panel the link can
land on. -
Connectors opened from the composer, user menu, palette or More keep the
selected agent and workspace in the URL, so “This agent” and reload after
Add still work. Free agents resolve the same way as Packages. -
#/packagesand#/settingswithout a query adopt the selected pane
again once the fleet is ready.#/clis/connectorsrewrites to Pi.
Extra path on Settings is blocked instead of opening the editor. -
The terminal shows one scrollbar, never two. The web terminal is a tmux
client and tmux attaches on the alternate screen, so xterm has no scrollback
of its own — and yet it painted two one-pixel artefacts at the right edge of
every terminal: its own scrollbar (one pixel wide, because
overviewRuler.widthis also itsverticalScrollbarSize) and the overview
ruler's white outline. Both are hidden now, on desktop and mobile, together
with the eight-pixel scrollbar gutter the viewport reserved for a bar nobody
could see (Chromium only removed it on touch platforms; the terminal surface
painted over it). The fit keeps reserving the one pixel, so the text grid is
unchanged, and the wheel still scrolls what it always scrolled — tmux's
history or the TUI's own viewport. The scrollbar a reader sees is the one
that belongs to whoever holds the scrollback. -
The Inspector's branch chip is readable again. A branch name and a
worktree name were sharing one chip's width, each with its own ellipsis,
and together they read as· fe… ·…. The row shows the branch, which is
what it is for; the worktree is in the tooltip with the rest, and on a
narrow rail the unpublished / detached word steps aside for the name
instead of the other way round. -
Fullscreen: the Inspector button in the top strip shows the panel. It
only flipped the rail's dock preference while fullscreen keeps the rail off
screen by itself, so the click changed an icon and nothing on the page.
Inside the mode it now lays the rail over the page at once — opening the
rail when it was closed — and hiding it there takes only the overlay down,
never the dock you set outside the mode. A panel opened that way stays until
the same button, Escape or the mode: it no longer slides shut behind a
pointer that never entered it. The right edge still works, and the rail can
be turned on from the strip with the mode already running. -
Approving a permission prompt no longer leaves the row saying "Needs
you" while the agent works. Grok, Claude Code and Codex report a waiting
prompt asneeds-you, but none of them emits a "permission resolved"
event, and none of them registered a tool-activity hook to contradict it —
so the row stayedNeeds youfor the rest of the turn. A Grok plan-mode
approval showed it for 10+ minutes after the plan had been approved. The
tool lifecycle is now the resume signal (PostToolUse, plus
PostToolUseFailurewhere the CLI has it), so the first tool that runs
after the answer returns the terminal toWorking. -
A Grok notification that is not a permission prompt (
task_complete, …)
no longer turns the row into a falseNeeds you; only a permission UI
that is actually waiting does. -
The release dialog was 400 px wide, not the 560 px it asked for:
.dlgis
declared later in the stylesheet at the same specificity, so itswidth—
and itspadding, which kept the header and footer hairlines inset from the
dialog's edge — won silently. -
The matrix highlight of v0.2.0 wore a generic sparkle: the note's icon name
had no glyph in either shell. Desktop draws it with the Canvas glyph, the
phone with the panel grid, andweb/tools/release-note-icons.test.mjsnow
fails when a published icon name is missing. -
A build with no release notes showed the changelog link twice — once as the
empty state's action and again in the footer. -
Column headings are only drawn when there are rows, so an empty search no
longer leaves a header over nothing, and the empty roster shows one action
instead of two identical Add provider buttons. -
With no providers connected the page no longer renders an empty toolbar
row. -
Dashboard: Grok's tokens, cost, turns, tool calls and timings now show
up. The Grok coverage row said "prompt history only" and rendered—
beside a CLI that has been writing asummary.json, anevents.jsonl
turn/tool timeline and ausage.jsonper-turn count into every session
directory. Turns, tools and durations come fromevents.jsonl; the model
fromsummary.json; tokens and cost fromusage.json, which Grok only
began writing in 1.0.x. Because older sessions have nousage.json,
tokens and cost report as partial with both counts — the coverage row
says exactly how many turns are priced — instead of a total that quietly
omits them. -
Inbox: ignoring a question from a pi in an Agent CLI terminal now
closes it. Ignore sends nothing, so it no longer needs that terminal to
be live and on the same session — it never claims "no reply" and then
refuses to be dismissed. -
Inbox: a reply to a terminal question fails with the truth. A pi in
that terminal that had not opened a conversation (a nestedpi -p, a
print-mode run) could take the reply file and answer "the terminal is
showing a different session". The daemon now refuses before parking when
the receiver names no session, each reply file is addressed to the
process whose hello was accepted, and a sessionless hello can no longer
take that address over — so another pi in the same terminal can neither
consume nor blind the reply. -
Codex terminals no longer try to resume a sub-agent conversation. Codex multi-agent v2 sub-agents (helper threads like "Gibbs") could overwrite the conversation a terminal remembers, and Codex refuses to resume them (
Process exited (1)). Sub-agent threads are now ignored for resume, in live hook reports and in the session list — resume always brings back the conversation you were in. -
An old
#/clis/terminals?…link now rewrites the address to#/clis
like the plain address always did, instead of keeping the stale URL while
showing the catalog.
Security
-
frame-src(with the dev-server preview) allows PiCode's own browser surface to frame this machine'slocalhost/127.0.0.1pages over http(s) and nothing else; the framed page remains a separate origin with its own policy, andframe-ancestors 'self'keeps PiCode itself unframed by others. -
Read is the default and the ceiling: an agent with no grant reads the tab the
human has on screen, and cannot click, type or navigate. Anything more needs
an explicit per-agent grant (Settings ▸ Browser, next). -
The desktop debug port is off by default. It was opened on loopback in
every launch; it is now an explicitPICODE_CDP_PORTopt-in for external
tooling. The agent access tiers (read/act/full) are enforced
against a named command catalog, and an unnamed command is refused at every
tier.