Everything below is the real commit history, rewritten so it reads as a story rather
than a list of subject lines. The short hashes are the commits in the project's history. Releases 0.7.0 to
0.14.0 and 0.16.0 to 0.20.x have not been written up here yet. 0.1.0 is by far the longest entry because it is the entry in
which the application was written; 0.1.1 and 0.1.2 are small on purpose; 0.1.3 and
0.1.4 are where remote access and Windows stopped being claims; 0.1.5 and 0.1.6 are
each one thing that had never actually worked for anybody; 0.1.7 is the two that had
never worked on Windows; 0.2.0 is mostly subtraction; and 0.3.0 is where the app
grew an assistant of its own.
Terminal Deck was built in two long days and an overnight run, in waves of parallel
work, so the timestamps say almost nothing useful. Grouping by what changed is more
honest than grouping by hour.
0.21.4
12 November 2026current
After a Mac restart your tabs come back at once, each on its own conversation and with its own account.
A tab that could not come back is listed with the reason and a Retry button.
Text typed or dictated in one session no longer shows up in another session's message box.
Lower processor use while the app is idle.
Terminal Deck now runs as one app instead of separate helpers. After the update macOS shows one notice that Terminal Deck added items that can run in the background; that is expected.
Tool steps in a chat fold into one line you can open, and charts in replies draw at their full size.
A chat no longer stays stuck on "Working" after the AI has finished.
Simulator streaming to the phone is fast again, with live dragging.
Clearer advice when Chrome shows its notice about the app.
The server host is 0.21.4; Windows stays on 0.21.2.0 in this release.
0.21.3
6 November 2026
Chat replies show wide tables, nested lists, charts and diagrams in place, and code sits in its own card with Copy. iPhone and Android show the same replies.
Sending goes straight through: no red flash after you send, and no extra Send now click when the AI is idle. A message sent while the AI is still starting waits for it and is then delivered.
Copy sits under every message, yours and the AI's. Rewind and Fork appear on every chat, older chats included, and on the phones where your Mac offers them.
The chat header shows the agent you are talking to, with its photo and name. Side-panel rows show the agent's picture instead of a plain chat icon, and a session keeps one name everywhere, also after a rename.
Accounts can have a name, set in the account list in Settings. Chats show that name, or the first part of the signed-in address, never "Default".
Pasting an image attaches it to the message box, and attached images show a small preview with a remove button. "Add screenshot" is gone from the + menu.
The four buttons above the chat and the old session-engine notice are gone.
Gemini chats start even when the Gemini CLI does not offer extra prompt context.
On the phones, links in chats open in the app's own browser, and the app lock covers every dialog.
A fresh Mac install starts its session keeper reliably. The server host is 0.21.3; Windows stays on 0.21.2.0 in this release.
0.21.2
5 November 2026
Chat keeps new replies near the message box. Scrolling up pauses following; sending or returning to the bottom resumes it. Drafts stay with their own session.
Working and running tasks share the message box. Send, Queue and Stop use one button, with individual task controls where the AI supports them.
Replies have clearer names and pictures, readable tables, code that scrolls within its own box, and plain error messages. Chat unread dots and empty bottom space are gone.
Supported Claude and Codex replies can be rewound or forked from their recorded history. Failed fork creation reports what was kept and what was cleaned up.
Browser tools can navigate, wait, inspect pages and tables, use keyboard and mouse actions, handle dialogs, download files and capture full pages or elements.
Agents can upload ordinary files after approval. Uploads stay bound to the approved page and origin; browser sign-in files and private credential stores remain blocked. The file chooser also offers Photos.
Account names and sign-in actions are clearer. Switching accounts from a terminal selects the new session reliably, and pins keep their stable session keys across restarts.
Claude and Codex questions use readable forms. Codex Bypass uses its supported no-prompt policy; real questions and sign-in requests remain visible.
Interrupted jobs show Ended by restart with Resume. New starts can choose an AI or saved agent, use permitted project subfolders, and wait for Codex readiness before sending their brief.
Phone file browsing handles large folders and long text in parts, shows empty or inaccessible files clearly, and prevents special files from blocking the host.
Server data uses the direct connection or TURN path. A blocked connection offers Retry. The server host is included with the same release.
Windows gains title-bar dragging, smoother terminal text, reliable focus, faster tab switching and reorder, corrected toolbar icons, a terminal-shell preference, clearer folder names and ended-session notices, and Windows 10 terminal sizing fixes. Remote terminal starts follow the same shell preference.
The Mac updater shows checking progress and gives its verified update helper enough time to become ready. If an older copy cannot finish the update, install the Mac 0.21.2 DMG once. If asked, choose Finish the update for the new session host. Re-pin any sessions that need their sidebar pins restored.
Starting a new session works again on every account.
Sessions and chat
The account selected while preparing a session stays selected when it starts. A changed selection is checked again safely.
Finished agent jobs now say Finished. Chat tools show pending questions and allow enough time to send; same-folder refusals explain the next step.
If the 0.21.0 update card says the update helper did not become ready, use the signed Mac 0.21.1 DMG for a manual install. The in-card restart did not pass the scratch check: verification exceeded its three-second helper deadline.
Windows, iPhone, Android, the web client and the server host remain on 0.21.0. This update keeps the same background session keeper.
0.21.0
4 November 2026
The Mac and Windows apps update themselves from here on, Hoot waits until you open it,
and your own devices reach everything.
Updates arrive inside the app
On the Mac and on Windows, a new release now shows up as a card just above Settings
in the side panel: Update available with its size, then
Download (on the Mac) and Restart to update.
Sessions keep running through the restart, and the card says beforehand if any
would close. On Windows the app also restarts to update by itself once nobody has
used the PC for ten minutes and no AI is working. What’s new opens the release notes, and ×
hides that version until a newer one comes out. The card arrives with 0.21.0, so
0.21.0 itself comes from the download page.
If Hoot is in the middle of an answer when you press Restart, the app waits, says
so, and restarts as soon as the answer is done. Cancel stops it.
Nothing is replaced until the app has checked the release: it must be signed with
the project’s release key and match its published SHA-256, and on the Mac it
must also be signed by the project’s own Apple developer account.
Check for updates lives in one place, Settings › General.
On the Mac, the duplicate button in Help › About is gone.
Hoot opens when you open it
The app opens on what you had open last. Hoot no longer opens itself at every
launch, closing the last tab leaves an empty pane instead of opening Hoot, and
Hoot’s own window is not reopened when the app starts.
On Windows, Hoot’s tab closes like any other tab and stays closed until you
open Hoot again.
Your own devices reach everything
On the Mac, pairing a device now starts on My device with Full control: every
login, tool and folder, as on the Mac. Someone else’s is
there for a guest, with its own folder and login steps.
A terminal started from your phone uses the login you picked on the phone. On your
own device, git, SSH and the GitHub sign-in work in it the way they do on the Mac.
Choosing Work on the pairing sheet now lets the device in; before,
Let it in did nothing.
Phones and the web reach your Mac directly
A phone’s or the web version’s terminals, messages and screens now travel
between it and your Mac only over a direct connection, or through Cloudflare’s
TURN relay when the network needs one. Opening a session takes about a third of a
second (it was one to two seconds), the screen arrives at the phone’s size, and
typing never waits behind the phone’s other questions.
No more Reconnecting every few minutes: a relay restart no longer
touches a phone whose direct connection is up.
On a network that blocks both the direct path and Cloudflare’s relay, the phone
says “Can’t reach your Mac — this network blocks the direct
connection.” and offers Retry.
Other Macs’ screens use the direct connection too; the window’s header says
“direct” or “via Cloudflare”.
Your Mac’s files on your phone
On iPhone and Android, Files in the side panel opens your Mac’s
home folder. Walk into any folder; a folder with thousands of items keeps loading as
you scroll. Text files open and keep reading as you scroll. A guest’s phone never
shows Files.
Phones reach a Windows PC directly
A phone’s terminals, messages and screens now travel between the phone and a
Windows PC only over a direct connection, or through Cloudflare’s TURN relay
when the network needs one. The old relay route for that data is gone,
so the connection no longer breaks when the relay server restarts.
On a network that blocks the direct connection, the phone says so plainly and
offers Retry. A phone app older than 0.20.3 is asked to update.
Fixed
A Claude session no longer shows Needs you a minute after every
finished answer. Only a real question or permission request marks it now.
The web app lives only at terminaldeck.app. Its previous address has been
removed.
Downloads and licence
Windows is one package of the native app (.msix), signed with the
project’s own certificate, which you trust once on each PC. The old installer
and portable .exe are gone. The download page has the steps.
The version number is the same everywhere: 0.21.0 for Mac, Windows, iPhone, Android
and the web version.
A server’s host now comes with each release: the one-line installer
(curl -fsSL https://terminaldeck.dev/install.sh | sh) installs this
release’s host after checking it against the signed release list, the same host
the Mac’s Servers section installs over SSH.
The Mac and Windows apps stay MIT-licensed. A NOTICE file now asks anyone who
builds on Terminal Deck to credit it; that is a request, and the licence is
unchanged.
Removed
On Windows, watching the PC’s own screen from the browser page opened on that
PC. It needs the direct connection, as on the Mac.
0.15.0
28 August 2026
Sessions on a server stopped dying, and the assistant became a real chat.
Fixed
A session started on a headless server was closing on its own right after it
began. It runs normally now. The assistant’s tools had been rerouting every
session through the assistant’s own channel; a plain session gets the simple
browser-only tools again, and the assistant runs on its own.
The desktop no longer claims that a server cannot run the assistant or
has no browser. Both capabilities work now, and the text said otherwise.
The assistant is a real chat
Your messages sit in a box, its replies are plain text, each one has a copy
button and a time, and a suggested command comes with a Run button. There is a
typing indicator, and you can attach files. The floating browser pill is gone
while you are in it.
A server is managed from its own page
Restart, Stop and Start the host over the relay — so an offline address no
longer breaks it — and Connect GitHub, on the phone and on the desktop.
Desktop server management is one place now; the duplicate in Settings is gone.
Sign in and out of your Claude, Codex and Gemini accounts from the phone. It was
desktop-only before.
A connected server shows once, under Servers, instead of in both lists.
The usage reading only shows when it is fresh, and hides cleanly when it would
be stale. No number left lying, and nothing running in the background to keep it
updated.
Android
The assistant can be restarted; a login the browser asks for can be typed from the
phone; tapping a “needs you” notification opens that session; and you
can sign out of an account from the bar.
Removed
Chat mode is gone — on the Mac, on Windows and on the
phone. A session could be drawn as a conversation instead of as a terminal, read
out of the agent’s own transcript file. It never worked smoothly: switching
into it often showed nothing, and it went on doing that across versions. The
toggle, the pane and the reading over the wire are all removed. A session is a
terminal. Talking to the assistant is unchanged — that is its own
conversation, on its own screen.
0.6.1
19 August 2026
The button that lets Windows hold a session inside its folder.
Windows can hold a session now
On a Mac, a session someone starts from a paired device is held inside the
folder you granted and can reach nothing else. On Windows the same mechanism
was built and switched off — it needs a one-time administrator
permission, and there was no button to ask for it. Settings → Remote
→ Folders has one: it names the folders it would cover and raises the
prompt once.
The screen believes the machine rather than the press. Dismiss the prompt and
nothing changes, because the state is read back after asking rather than
assumed from a click.
Until somebody presses it, a session from a device on that PC runs
unconfined — and the panel says so, instead of claiming the feature
does not exist.
0.6.0
19 August 2026
One pairing decides the assistant, a rented server becomes a machine like any
other, and Windows stops being half a product.
The assistant no longer has a connection of its own
Approving a device as your own is now the whole
authorisation: the assistant is simply there, on the phone and in the browser,
with nothing to connect. A guest never sees it — not a
disabled tab, not a greyed row. Absent.
Gone with it: the six-digit assistant code, the separate credential, the file
that stored them, and every state where a device was paired, trusted, and
told to connect first.
Servers
A machine nobody sits at is now the same shape as one you do: a group in the
sidebar, sessions with tab pills, and its private localhost opening in the
same browser window. Three things to add one — an address, a username,
and a password or a key. Nothing to install on it first.
Signing in with a key no longer asks you to open the file in a text editor.
The app reads your key folder and offers what it finds by name, and never
offers the public twin sitting beside it.
The headless host installs on a server with no Node on it: it fetches an
official runtime, checks its SHA-256 before unpacking, and touches nothing
outside its own folder.
Windows
Attaching a file to a message did not work on Windows at all — browse,
drag-and-drop and paste all failed on a path check that assumed one shape of
separator. Fixed, in every door.
Every scheduled routine failed on Windows, because the runner kept the
agent’s command and threw away its arguments.
Windows now runs the same test suite as macOS on every change. It never did
before, which is why the three above went unnoticed.
Fixed
Token totals were inflated for any project with no session open.
Driving mode ran the scan and never showed the answer — the card that
combines it fetched once and never again.
The usage bar would not read on its own for a session started as a shell.
0.5.0
18 August 2026
A second review of the shipped build, an audit of what the first one actually
delivered, and two defects that were live on somebody’s machine while they
worked.
Two things it was doing to a real session
bfdc4c6It was typing /usage into a working session and
leaving the panel open on top of the conversation, over and
over. On an account billed through the API there is no rolling subscription
window, so the reading could never be found: every attempt timed out, every
attempt left the panel, and it tried again on the next quiet moment for as long
as the session was open. The panel is now closed and the close is
verified by reading the screen back; “there are no limits to
read” is an answer rather than a failure; and an attempt that typed and
found nothing stops that session for good.
bfdc4c6A session where you opened a shell and ran an agent in it — which the app
offers a button for — had no model, effort or usage controls at
all. Two parts of one bar were answering the same question from two
places: the account chip read the screen and showed the agent, the controls
beside it read the session record, saw “shell”, and drew nothing.
Every tunnelled response larger than a socket buffer was cut short
bf4e88cClosing the browser’s socket used destroy(), which does not
flush — anything accepted from write() but not yet handed to
the kernel was thrown away. Measured at 64 MB into a loopback socket:
destroy() delivered 327,212 bytes, end() delivered all
67,108,864. That number is macOS’s auto-tuned loopback buffer, which is why
nothing was ever lost there; Windows defaults to 64 KB and does not
auto-tune, so a real page was truncated mid-body. Orderly closes flush now;
error paths still discard, so no descriptor outlives its tunnel.
abffc6aA folder with nothing left unread could describe itself as incomplete. When the
scan hits its ceiling it drops what is still queued and counts it as unread
— and it was counting files it had already read, which can be back
in the queue because the operating system replays file events from just before a
watch was attached.
The browser stops caring which machine it is
bfdc4c6A machine chooser beside the address bar. The start page lists what
that machine is serving, and typing 3000 opens its port
3000 — same window, same tabs, same everything. Choosing a machine changes
what localhost means and nothing else: a public address is the same
site from either computer, so it is left alone.
bfdc4c6Port lists and tunnels are for your own devices. A guest gets
neither and is never told the capability exists — a port scan can say which
program holds a port but never which project, so it cannot be narrowed to the
folders a guest was granted. Every port or none, and none is the honest
answer.
bfdc4c6A remote machine now looks and behaves like a project: one machine mark on the
heading rather than one on every row, the same menu a local project has, and
remote sessions get a tab in the strip. Closing a machine ends its sessions and
folds it away without disconnecting it.
Smaller, and all of it asked for
bfdc4c6A device waiting for approval says so, instead of appearing only in a settings
pane you had to already be looking at. The web app takes an address, with a
machine chooser and one theme icon in place of three buttons. The assistant
settings page is half the height it was and every file it lists can be edited in
place. The model and effort chips keep their labels at every window width —
they had been fading to nothing below 1000 pixels. On the phone the assistant’s
tab bar is out of the way of the composer and appears only when it is
connected.
Sixteen tests that could only ever have passed on a Mac
bf4e88cThe Windows job gates this installer, and over three days it caught a file
URL’s pathname used as a path, POSIX separators asserted against a
normalising function, folders named but never created, and tests that waited one
tick for an asynchronous parser. It also caught the truncation above, which no
assertion was ever going to find. Where the runner itself was simply slower, the
timeouts were raised on Windows only, with the measurements written
beside them.
0.4.0
18 August 2026
Ninety-one items from a recorded review of the whole application, and underneath
them six doors that were never locked, three tools that could not do what they
said, and a list of controls that are now gone rather than pretending.
Two of three doors were open
584d01aA paired device could reach every session on the machine,
whatever folders it had been granted. Creating a session checked the grant
correctly. Listing sessions took no device id at all, and attaching admitted
any id off that list — so starting a new shell in an ungranted
folder was refused while typing into an agent already running in one
was not. Underneath all three, a device with no grant record was read as
“fall back to whatever this desktop is offering”, which is every
open project plus the working directory of every running session.
584d01aApproving a device wrote nothing. Approval is now the choice itself, and it
writes before it admits — the kind of device, then its folders, then the
approval, in that order, held by a test that reads the call order
rather than the end state, because the end state looks identical when the
approval comes first.
584d01aA device is either yours or a guest, and it
cannot change kind afterwards. A guest reaches only the folders you tick and is
never offered the assistant at all — absent rather than defaulted off,
because an unchecked box still advertises the thing. Folder containment is
re-checked on every keystroke. Devices paired by an earlier version
have no kind recorded and become guests with nothing: a fix that keeps
working for everyone who already had everything has not fixed anything.
The assistant could not drive, and it was right
584d01aOpening a page refused whenever no browser tab happened to be open already and
told the assistant to ask a person to open one — so it quietly fetched the
page instead and said it had not used the browser. Reading a page returned an
outline with no page text in it at all, leaving the model guessing CSS
selectors to read one line. Handing control back always failed, because the
ninety-second window it waited in outlives the sixty-second limit on the call
itself. All twenty of its tools were then re-checked by asking a running
assistant to use them in words, rather than against a stand-in.
584d01aIt knows where you are: ask what you are looking at and it can answer with the
page, the pane, and the address and text of a page it is driving.
584d01aDriving mode scans instead of reading to you. It used to hold
each stop long enough for a person to read it. Now it goes through every
session at machine speed — including the ones you started yourself —
with the window under a field of dots and the quoted line cut clear of them,
then returns one answer grouped by session. The reading-time estimate, the
pacer and the pace dial are deleted rather than disabled.
Every sign-in that stuck was the same bug
584d01aA page’s request to open a window was always refused, and refusing it
makes the page’s own call return null — so a sign-in
library opened its popup, was handed nothing, and waited forever for a message
from a window it never had. The destination did open, as a tab, and the sign-in
did complete. It had no way to say so.
584d01aGoogle sign-in works. It was being served a cut-down flow because the browser
identified the earlier desktop version in its browser identity. Nothing else
about the browser is disguised. If a site still refuses there is a deliberate handover — finish in your own
browser and press a button to bring the session back.
584d01aSaved logins and browser profiles, with the password store in the main process
only: the summary the interface receives has no field a password could be in,
and matching is on exact origin rather than domain grouping, so a subdomain
somebody else controls cannot ask for a password stored for the parent.
Counted twice
584d01aToken totals were over-counted by about 4%. Resuming or forking a conversation
copies its history into a new session record, and each record was de-duplicated
only against itself. On one real folder that was 11,110 distinct requests
counted 11,598 times. Verified against an independent re-implementation written
from the file format rather than from this code.
Built, and reaching nothing
584d01aArtifacts showed files because nothing in the code held the meaning of the
word, and the previous fix had written that conclusion into the source as
settled. An artifact is a file an agent produced whole; a file it only edited is
a change to your project and sits one chip away with its count.
584d01aSource control led to Files because the function that fetches a diff had
zero callers anywhere in the interface. It is list-left, diff-right in
place now.
584d01aA setting the assistant changes now takes effect on the open window. It used to
write the change, report success, and leave the screen as it was until a
reload — and it now says which of the two happened, measured rather than
claimed.
Removed, rather than left looking usable
584d01aThe row of controls inside the message box, which repeated the bar above it.
Refresh and Reconnect on the phone — one duplicated a gesture, the other
was an admission the automatic reconnect might not work. The language picker,
which had one option. The bottom band of the browser. The copy of the hooks page
inside Settings. And a list of past sessions where every row opened the same
one, which was not a bug in the list but the only thing it could do.
Two platforms, and the runner that proves it
baf9b1dSix tests measured the machine they ran on rather than the rule they were
about, and every one passed on macOS and failed on the Windows runner that
gates this release: a file URL’s pathname used as a path, a POSIX
separator written into an expectation for a function whose job is to normalise,
folders named but never created, and a test that waited one tick for an
asynchronous parser and hoped.
3024a58That last one earned a real method: the plan-limit tracker can now be asked to
flush, resolving in the terminal parser’s own write callback, so the wait
is exact instead of a guess about how long parsing takes.
0.3.0
17 August 2026
The app gets an assistant of its own, the browser gets an agent that can drive it,
and a long list of screens stop contradicting themselves.
An assistant
e6996c3An assistant for the deck itself, pinned above the session list: it can see
your sessions, read their transcripts, start new ones and change settings, and
it remembers your conversation with it. A developer’s assistant rather
than a general one — no inbox, no calendar. It is built as
a real session rather than a hidden service, which is the
whole point: its folder, its instructions, its memory and its transcript are
files you can open, and the assistant settings page shows what it is handed before
it starts.
e6996c3It runs inside the same sandbox as any session started from another device
— including from us, and proved against
sandbox-exec rather than asserted: it cannot read your home
directory, your SSH keys, your keychain or another account’s login. Its
routines and its action log live outside the one folder it can write
to, because a routine it could author unasked is not gated and an audit log the
audited party can rewrite is not an audit log. A consequence worth stating: it
starts signed out, because the keychain is closed to it, and
its first screen says so.
f5eee9fThe assistant got hands, and three separate features turned out to share one
shape — the mechanism present, the connection absent. Its tool
configuration was being written and never passed to it, so every sentence about
being bounded by a consent gate described a gate that was not in the path. The
session fan-out honoured a hidden predicate everywhere and was built without
one, so a paired phone could attach to the assistant’s own terminal and
type into it. And the hook endpoint baked a port into the hook files that
changed on every launch.
f5eee9fRoutines — saved instructions that run on their own,
triggered by something happening rather than by a clock: a session finishing, a
session failing, git state changing, a file changing. A schedule is one trigger
among several rather than the foundation. One that fires while nobody is at the
machine and then needs consent is refused immediately and legibly, instead of
hanging on a dialog no one can answer.
a293ec9It can show you what it found rather than only telling you: it writes a tour
and the app plays it, a box around the line that matters, everything else
dimmed, pausing the instant you touch anything. What it refused is the
part worth reporting. On the first real run the model quoted a line that was
never in the terminal and called an idle session blocked; both were dropped
before they reached the screen — one by checking the quote appeared
verbatim in the source, one by re-checking the claim against the app’s
own view — and the recap names the drops rather than hiding them.
A browser an agent can drive
a293ec9Driving goes through the browser’s own debugging protocol with a real
actionability loop — resolve the element, check it is visible, wait for
its box to stop moving, check it is enabled, hit-test where the click will
land, then dispatch. Screenshots show you what happened; they are not how
anything is decided.
a293ec9And no debugging port. The off-the-shelf way to do this wants
a listening socket, and a loopback socket is not a user boundary: on a shared
machine another logged-in user connects to it freely, and the renderer is a
target on that port holding the preload bridge — so a single evaluate
would walk past every consent gate in the process. The engine is in-house for
that reason. Measured on the way: a protocol-level navigation to
file:///etc/passwd succeeds on a view whose navigation guard
calls preventDefault, because that guard only covers navigation
the page starts.
a293ec9One page, and the two of you take turns on it. Any real interaction of yours
takes it back at once and the agent’s next command is refused; while you
hold it, every command is refused, reads included. That is deliberate
and it is why the rule is not limited to clicks: a screenshot taken while you
are typing a password is the leak, and nothing can redact what was never
produced.
e6996c3Draw on a page and send it to an agent, alongside inspect and record. A pane
can hold a browser page too — before this a pane could only name a
session, so anything else was by definition a closed one and the split
collapsed.
The window
e6996c3Tabs take Chrome’s shape and join the pane below them. The
+ leaves the strip; a terminal and a globe sit after the last tab;
there is one path to a new session and it is the dialog. A tab’s
✕ closes the view and leaves the session running — only the
sidebar’s ✕ ends one.
e6996c3Chrome belongs to the pane rather than the window. An account chip drawn once
above two sessions from two projects is wrong for at least one of them with
nothing saying which; each pane names its own account, folder and session now,
and the main session keeps its chrome in the top bar, which is what makes it
read as the main one.
f5eee9fModel, effort, connectors and usage sit in the chrome beside the account, for
a terminal as much as for a chat. Clicking a model changes the session that is
running — and refuses, saying why, when you have something typed, because
the way a CLI is told to change model is a line typed at it and the next Return
would have been yours.
f5eee9fAdd your own agent. The list is no longer four names compiled
into the app: name a command and it runs, with the caveat drawn on the row that
an agent nobody here has characterised gets a terminal and not a model picker.
The command is resolved against your login shell’s PATH
before it is saved, so a name that cannot be found is refused at the form
rather than becoming a row that dies when you pick it.
e6996c3Attach a file from anywhere — the real system dialog, drag and drop, or
paste. The picker had only ever offered files inside the project. Notifications
open as a popup instead of taking over the window, and Settings is one surface
rather than three greys pretending to be one screen.
Fixed
e6996c3Hooks stopped working every time the app restarted. The endpoint took a new
port on each launch and that port was written into the hook files, so
session-finished events silently never fired — and a credential was
written world-readable into an agent’s settings file. The endpoint is a
socket now, at a path the kernel cannot hand to a stranger.
f5eee9fTwo faults that only appear when you run the app rather than read it.
Transcripts under a symlinked path were invisible to usage, chat, alerts and
loop detection, all of which reported the miss as nothing to see —
/tmp is symlinked on every Mac. And a shell session was handed
another session’s conversation, complete with 780,000 tokens belonging to
a session that had already ended; shells write no transcript, ever.
f5eee9fA web page could paint over the terminal, and the trigger was
⌘R: React cleanups do not run on a page unload, and the host is
the same object across a reload, so both safety nets missed for the same
reason. Visibility is decided by the main process now, from the
renderer’s document generation, which a renderer that stops talking
cannot defeat. A 21-pixel tooltip also used to blank a 587×644 page,
because any intersection counted as total coverage.
34ff13fA confined session died on its first turn on macOS, before producing a token,
because the agent CLI keeps scratch files at a path outside every sandbox. The
same fault broke sessions started from a paired phone. A claim about the
sandbox also moved off the terminal that could not hold it.
92c6b6cWait for the shell before typing at it, rather than sending the first
keystroke into a terminal that is not listening yet.
a293ec9Windows could not run hooks at all: a socket path there maps to a named pipe,
which rejects a filename, and the tool the hooks used cannot reach a pipe. Only
the program had to change, which an experiment established rather than a
document. Two boundary tests were also passing without checking anything, their
setup returning early off macOS and leaving the path-agreement case asserting
against an empty string.
e6996c3Screens that contradicted themselves: the model picker typed into a live
prompt and left a confirmation dialog standing for the next Return to answer;
Power’s notice denied its own switch; a language dropdown had one option;
a debug file offered Reveal when it did not exist; the file viewer drew
characters the file did not contain, because programming ligatures were on in a
pane whose whole job is showing text verbatim. Clicking a file in the tree also
closed the Files page, because navigation was a toggle.
Removed
e6996c3Every price and cost figure, finished. One of the two could not be computed at
all — nothing is published from which to derive what a subscription costs
per token — and the other told somebody on a flat monthly fee that they
had spent money they had not. Token counts, cache share and the context window
stay, because those are facts rather than inferences.
The clients
7df2fe5iOS gets a Localhost tab and a three-tab shell — and the first build any
of that had ever been compiled in.
b8e542eThe web client catches up: localhost ports grouped and nameable, and more than
one machine at a time.
9ede37cOne live pairing code per machine, and a code that admits when it cannot find
the machine it names rather than failing blankly.
a293ec9The assistant reaches your phone over its own connection — a separate
code, a separate credential and four independent refusals, rather than a
checkbox on the session pairing. It carries no device id, so a leaked one is
half a pair rather than a bearer token.
4a50e699,136 tests passed and the release still failed, on a directory removal. The
release script is the thing that has to survive a green test run.
0.2.0
16 August 2026
The release that took things away. A pairing code with one method instead of three, a
network with one path instead of two, and every price in the app deleted.
Pairing is six digits, and nothing else
3babd08One way in, and it is six digits on a numeric keypad. The
QR code and the pairing link are gone entirely — deleted, not
hidden: the QR encoder, the iOS scanner and its camera permission, the Android
scanner and its CAMERA permission, and every
terminaldeck://pair route into a credential write. The QR did not
work, and a link was a two-hundred-character string carrying a live bearer
secret that had to travel through a messaging app to be useful — which is a
pairing token somebody else’s server then has a copy of.
3babd08Six digits is a million codes where eight characters were 1.1 trillion, a
reduction by a factor of 1,099,511, and the number is stated rather than
buried. What makes it sound is unchanged and now pinned by tests rather than
assumed: a code lives sixty seconds, is single-use, and dies after five wrong
guesses — the code dies, not the guesser, so minting a fresh key for every
attempt buys no fresh budget. Redeeming one still only produces a
pending device that a human approves on the machine itself.
9ede37cOne live code per machine, and a code that admits when it cannot find the
machine it names rather than failing blankly.
The relay is the network
3babd08The “Direct on your tailnet” card is gone from the app.
There is one path to a machine now: Terminal Deck’s own rendezvous relay,
which both ends dial out to. No port forwarding, no VPN, no account, and
nothing anybody has to install or understand before a phone can see a terminal.
Everything it carries is sealed under keys the relay never sees, so it can
end a session and cannot read one.
3babd08Machines and Remote are one section, in the sidebar rather than inside
Settings. Pairing a device is something you do, standing at two keyboards — not
something you configure once and forget, which is what everything else in
Settings is.
No prices anywhere
aa08a84Every dollar figure is deleted — the rate card, the per-session and per-day
spend, the money column in the session inspector and the cost tile on the
dashboard. The API number was correct arithmetic and it still misled: almost
everybody running this is on a subscription, and telling someone on a flat
monthly fee that they spent four figures states a number that never left their
account. The subscription number cannot be computed at all, because no plan
publishes a token allowance or a per-token value to measure a session
against.
aa08a84What replaced it is what was always underneath: tokens split by fresh input,
output, cache read and cache write; the share of the prompt served from cache;
the request count; what the project has moved today; and context-window
pressure with the compaction count beside it. The dashboard tile is called
Usage. Deleting the rate card also removed a live bug — a time-boxed price due
to revert on a date nobody was watching, for an increase that has since been
cancelled.
The window
aa08a84Overview is a live board of running sessions — what each agent is doing, how
long it has been doing it, and which one is waiting on you. Deliberately no
progress bar: an agent does not report progress, and a number the app invented
would be worse on that screen than no number.
aa08a84Search is replaced by Artifacts — every file your agents wrote or changed, with
the diff of each change. Searching past sessions moved into the command palette
under ?, one keystroke from what you were already doing. Search’s
own results had never been clickable.
8e49830Sessions can be dragged into the top strip. The strip was built and nothing in
the app was draggable, so it looked finished and did nothing. Promote and demote
also work from a row toggle and ⌥← / ⌥→.
aa08a84Windows has one title bar instead of an OS strip, a menu strip and the
app’s own chrome, with real minimise, maximise, close and snap layouts.
About nine hundred words of on-screen prose were removed, with every warning
kept.
c620f03An account belongs to an agent. Adding one asks which — Claude Code or Codex
CLI, both verified to isolate a login. Gemini is listed and refused, with the
reason on the row: its token lives in one keychain slot no config directory
moves, so a second login would overwrite the first rather than sit beside
it.
d060cffLight and dark in the web client, following the system by default.
Fixed
aa08a84Keep-awake held nothing. The app’s own power-save blocker was gated
behind a privileged system setting, so on every machine where that had not been
granted — including every machine on first run — no lock was held at all.
aa08a84Sessions did not come back on Windows: a WSL path was resolved against the
Windows host, so /home/you/x became C:\home\you\x
and matched nothing the Linux agent had written. The same fault emptied the
Files page there.
c620f03Adding an account always signed you into Claude. The preload dropped the
options object, so the provider never crossed the bridge — signing in beside a
Codex account opened a Claude session, which then correctly refused the account
and silently discarded it.
64da716Wait for the shell before typing at it, rather than sending the first keystroke
into a terminal that is not listening yet.
Removed
942aca5The GitHub notifications bell. GitHub’s notifications endpoints accept
only classic personal access tokens, a GitHub App user token is not one, and no
permission can be added to change that. Sign-in is the GitHub App and nothing
else now.
0.1.9
16 August 2026
Four batches of work, and the release where one machine could first drive another.
Machines, devices and what they may reach
43273bbOne computer can be paired to another and drive its sessions from the Machines
panel. A typed code cannot carry an address, so it names a slot at the relay:
the machine showing the code sits in that slot for the sixty seconds the code
lives and answers with its real URL, host id and public key. A hostile relay
cannot answer in its place, because the responder’s key pair is derived
from the code as well.
9279d76Per-device folder grants: the machine that owns the files decides which folders
each paired device may start a session in. Read that literally
— it is organisation, not a sandbox, and nothing in the app says otherwise. A
shell that starts in a granted folder can cd anywhere the user
account can reach. The security boundary is the one that was already there:
pairing, plus a human approving the device on the machine itself.
9279d76The credential proxy — their GitHub, from their device, never yours. A session
started from another device is handed its own git configuration, so
git push cannot reach the owner’s account through the
credential helper, the rest of the global config, gh, or ssh. When
git needs a login, the request crosses the sealed channel to the device that
started the session and is used once in memory; nothing is written to the
host’s disk, so revocation is disconnection.
43273bbSessions in WSL, routed by the folder rather than by a switch. A Linux path
launches through wsl.exe inside the distribution; a Windows path
launches through cmd.exe as before. There is no “use
WSL” toggle on purpose — a toggle is what lets the two disagree.
Two things that were believed and were wrong
0761f87Notifications were never a signing problem. The theory was that macOS keyed
authorisation on a signed bundle identity and refused an ad-hoc one. Measured
instead: macOS registers the ad-hoc bundle perfectly well and had simply put up
its authorisation prompt, which arrives as a notification banner with
Allow hidden behind an Options disclosure nobody had opened.
Clicking Allow was the entire fix, and macOS’s own notification store
recorded the deliveries afterwards.
72b1015The app stopped saying “Sent.” for a banner it never watched
arrive.
Windows, and the guards that were not running
e50f276Answer for the platform you were asked about, not the one you are running on —
with two follow-ups for the same fault in other places.
57ab908A guard that cannot run on Windows is worse than no guard, because it reads
green. Several were rewritten so they run on the platform they were written to
protect.
5db67d0Three ways this Mac lied about being a clean machine, found and closed, so a
passing suite here means something on somebody else’s.
Tailscale leaves the product
388ee5bThe browser client dials the relay, and Tailscale leaves the product’s
face. Nothing anyone downloads needs it, needs to know what it is, or is asked
to install it — a point 0.2.0 finished by deleting the tailnet card
outright.
0.1.8
14 August 2026
One fix, and a test so it is the last time.
Wording that crosses to a phone
fa2d5c8Nothing sent to a phone calls a Windows PC a Mac any more. Fourteen sentences
in the remote server named a platform and ten of them were sealed up and read
on the phone — “Pair it again from the Mac.”,
“This Mac cannot start sessions from a phone.” — and one
phone now holds several machines at once, so those could appear directly
beneath a row naming a Windows PC. The phone already prints each machine’s
own label beside anything it says, so wire copy names no platform at all;
copy the person at the keyboard reads keeps the noun and asks the code that
knows which one. This was the third sweep of the same rule by the third pair
of hands, so it is scanned now rather than swept again: a string in a module
whose output crosses the wire may not name a platform, and the test says which
line when one does. Run against the previous release it reports all fourteen,
including four a careful reading of the diff had missed.
0.1.7
14 August 2026
The release in which Windows stopped being the platform where two things quietly did
not work. Both were measured on a real Windows machine rather than reasoned about.
The localhost tunnel
81a4cedIt reaches a dev server on ::1, which on Windows is most of them.
Windows resolves localhost to ::1 before
127.0.0.1, so vite, next dev and
node --host localhost bind IPv6 and nothing else — and the tunnel
only ever dialled 127.0.0.1. The port was scanned, listed, offered
to the phone, and then refused the moment it was tapped, with nothing anywhere
saying why. A port that is listed and unreachable is worse than one that is not
listed. The scan now carries the address family it always knew and the tunnel
dials the loopback the port is actually on, deciding once per tap by connecting
rather than per browser connection by guessing; when neither loopback answers
the tunnel is refused with a sentence naming what was tried. Nothing changed on
the wire, and nothing changed about the tunnel being a byte pipe rather than an
HTTP proxy — which is what keeps hot reload, server-sent events, cookies and
the WebSocket upgrade working. Proved on a real Windows machine in all three
ways a dev server is started, pulling a real HTTP response and a real
101 Switching Protocols upgrade through the tunnel each time.
Updating in place
81a4cedWindows installs its own updates without anyone clicking an installer. Finding
and downloading already worked there; installing did not. The app quit, an
installer window nobody asked for opened, and it sat on a page waiting for a
click — fifty seconds later the setup was still running, the old version was
still installed, and there was no app on screen at all. It installs silently now
and the app comes back, which is what a button saying Restart
promises. Watched working end to end: an installed build took itself from 0.1.7
to 0.1.8 through its own panel and relaunched.
81a4cedThe portable Windows build no longer offers an update it cannot install. It is
the same build as the installed one and carries the same release feed, so it
reported itself updatable — and an update on Windows is an installer, which
would have put a second, installed copy of the app somewhere the user did not
choose while the portable executable they were running stayed old. It says
plainly that a portable app cannot update itself and points at the releases
page.
0.1.6
14 August 2026
One fix, for something no release had ever contained.
The phone client
c7054e2The page you open on your phone is in the app now. It never has been: 0.1.3,
0.1.4 and 0.1.5 all shipped without it, so the tailnet address the Remote panel
prints for you to open answered with nothing at all. The native phone apps were
unaffected — they speak the sealed protocol directly and never fetch that page.
Nothing failed loudly enough to notice, because the client is build output and
therefore gitignored, so the line in the packaging allowlist that names it
matched zero files in a clean release checkout — which looks exactly like a line
whose files were all excluded on purpose. It only ever worked on a machine where
somebody had built the phone client by hand at some point. Packaging builds it
now, and the release check fails if the built page is not in the bundle.
0.1.5
14 August 2026
The release that made remote access work without anyone switching it on, which is the
whole point of it.
Remote access
3938381The desktop dials the relay at launch now. The relay had existed for a day and
this Mac had never dialled it once outside a test:
start() ran only from a two-press switch in Settings → Remote, and
nothing re-ran it on the next launch, so a computer that had been restarted
simply was not reachable. Measured on the machine this was found on — the host
identity on disk, two paired iPhones in the trust store, the relay up, and not
one socket to it. A phone attaching to that host was attaching to something that
was not there, which is exactly what “connected, but showing me old output”
looks like from the sofa.
3938381Dialling on its own exposes nothing, which is why it can be the default: the
relay learns that a host is online and no more, and a device still has to be
paired and approved before one byte moves. Switching it off is
remembered, so off still means off. Proven on the packaged app rather than
asserted — an established socket from its own process to the relay, the panel
reading Connected against this machine’s host id, and the relay’s host count
rising when the app launches and falling when it quits.
3938381The Remote switch stopped describing the opposite of its own position. It read
“Off by default. Nothing can reach this Mac while it is off.” beside a switch
that is now on.
c0d88f5On the phone, the keyboard is raised before you type at a terminal, and the
rename alert is presented from the root rather than from a view that may already
be gone.
0.1.4
14 August 2026
The release that works on Windows. Everything 0.1.2 could not do there — find an
agent CLI, start a session, reach Settings, quit from the menu bar — works now, on a
real Windows 11 machine rather than in a runner. The whole test suite runs on a
Windows runner and gates the installer, so a red Windows test stops the release
rather than being waved through. One Windows fault is still open and is named at the
bottom of this page.
Windows
53016edAgent CLIs report their version. They are .cmd shims on Windows,
which Node refuses to spawn without a shell, so every version column was blank.
The shim path is quoted too, or the fix would still have failed for anyone whose
Node lives under C:\Program Files.
53016edNo more console windows flashing over whatever you were looking at. Seven child
processes were spawned without windowsHide, including the Tailscale
status check that runs the whole time the remote panel is open.
53016edSettings, Keyboard Shortcuts, About and Exit are back. All four lived only in
the macOS application menu, which Windows drops wholesale, so the menu bar had
no way to reach Settings and no way to quit — and Ctrl+, and
Ctrl+/ were unregistered along with them.
53016edThe diagnostics bundle prints a readable PATH. It was split on the
POSIX separator, so every entry was torn apart at its drive letter — in the one
file somebody attaches to an issue that says a CLI cannot be found.
9555b0bTurning remote access on is instant again. It used to sit for fifteen seconds
with the panel spinning and then say “Tailscale did not answer”. Tailscale had
answered, immediately: Serve is not enabled on your tailnet, with a
link to switch it on, and then waited forever for someone to click it. The
output is read as it arrives now, so the refusal comes back in under a second
carrying Tailscale’s own words and the link. Remote access was never blocked by
this — the relay does not go through Tailscale — but nothing said so for fifteen
seconds.
9555b0bStarting a session from a phone no longer refuses a folder that is on the list
the phone is showing. Two spellings of one Windows folder — a lower-cased drive
letter is enough — compared as different directories, and the refusal said “open
it on the Mac first” about a folder already open.
a0a114aThe last six places that build a child process environment go through one
helper, so Windows is never left holding two spellings of PATH with
no rule about which one the child searches. It affected the GitHub panel, the
git status poller, the readiness checks, assistant detection and every stdio MCP
server.
a206d5e, 2c96717, 677aa94The Windows test suite became a gate. The suite was ported and the release
workflow no longer lets that job fail without stopping the build. The cases that
cannot mean anything on Windows — POSIX file modes, the POSIX shell the hook
command is written for, the macOS-only updater — are skipped individually, each
saying why, rather than the suite being waved through as a whole.
One phone, several machines
4e45d5fA phone paired to a second computer used to quietly drop the first. Pairing
adds a machine now: each owns its own transport, its own sealed channel
against that machine’s static key and its own sessions, so two machines cannot
read each other’s work. All of them stay connected, so switching needs no
handshake, and the keepalive they need is folded into one app-wide tick — 144
radio wake-ups an hour at one machine and at five, measured rather than
asserted.
4e45d5fInspect mode, on the phone. Tap an element on the tunnelled page, say what
should change, and it lands in a terminal on the desktop as exactly one line.
The rule is transcribed from the desktop’s own capture panel rather than
approximated, so both clients hand the agent identical strings — a newline would
submit the prompt early and an escape would repaint the terminal it arrived
in.
4e45d5fThe phone’s Connected badge no longer claims a connection the app does not
have. Observed against the live relay: no guest attached for a sustained forty
seconds while the app read Connected. “Online” had been decided before the phone
went in a pocket, and nothing tells a socket that a carrier NAT reclaimed it.
Resuming now doubts the channel, says “Checking”, and probes immediately.
9e00c74The Android client gained the same several machines the iPhone had.
Surfaces that said something they did not mean
0f0cdcdEvery page has one blank, and it is the same blank. Empty states were four
different designs across ten panels — GitHub drew its own glyph, title and
button, MCP printed a bare sentence with literal backticks in it, the board
dropped inline text into a column. There are two now: one for a page with
nothing on it, one for a section of a working page or a page still reading,
parked at the same height so the answer lands exactly where “Reading…”
was.
0f0cdcdThe inspector stopped jumping under the pointer. It re-centred on every tab
press, because the tallest of its four panels ran past the floor and moved the
sheet 50px each time. A definite height parks it and the body scrolls.
0f0cdcdContext → “How it filled” draws something. It plotted a percentage of the
context window against a fixed 0–100 axis, and a healthy session peaks near four
percent, so every point landed within a pixel of the baseline. The axis scales
to the peak now, and both the ticks and the caption print the ceiling — the
honesty problem was never the scale, it was a chart that drew nothing.
0f0cdcd“Unknown” is gone where the answer was never knowable. Fast mode said Unknown
beside three siblings that always resolve, which read as this app failing; the
CLI announces fast mode only when it changes, so a session that has never been
told has nothing to report. It says “Not reported” and the menu says why. The
no-project screens stopped offering four ways to open a project, and Settings →
Help stopped drawing its own copy of the window’s own menu.
The build, and quitting
2004da5The installer is checked for what is inside it, not just that it exists. A
platform files: list in the earlier desktop version’s packager
replaces the root allowlist rather than extending it, which shipped the whole repository — a 1.0 GB app —
until it was caught. Both platform blocks were fixed, and CI now fails if a build
carries ios/, android/, relay/,
src/ or build/, if the app cannot start, or if an
artifact lands outside a sane size band.
2004da5A release build can be produced from a commit without publishing it. The
workflow can be dispatched by hand and publishing stays gated on a version tag,
so “let me check the installer size” and “ship it to everyone” stopped being the
same button.
ae86843Quitting with a live session no longer prints a wall of errors. Stopping the app
while a terminal was still producing output threw
Render frame was disposed before WebFrameMain could be accessed,
once per message still in flight, because the main process was broadcasting into
a window that had already gone. Nothing was lost and nothing was corrupted — the
errors were the whole symptom — but they were the last thing a packaged build
printed.
ae86843The debug trace no longer writes unless you ask for it. A build could leave an
ipc-trace.log of 12 MB and growing in its application-support folder
with Debug mode switched off. It is written only while Debug mode is on now,
capped at 4 MB with one previous generation kept, and listed in Settings →
Advanced so it can be found and cleared. A file left by an earlier version is
deleted the next time the app starts with tracing off.
0.1.3
14 August 2026
The release where remote access started working, and the release where Windows was
launched for the first time in this project’s history — and then fixed.
Remote access
9728d2bRemote access had never worked, on any platform. The earlier desktop version
links BoringSSL, which ships 28 ciphers and not one ChaCha, so
createCipheriv('chacha20-poly1305', …) threw
Unknown cipher, a silent catch swallowed it, and every relayed
handshake closed with nothing on the wire and nothing in the log. The whole test
suite passed throughout, because vitest runs under plain Node. The cipher now
comes from one implementation used in every runtime, with no “native when
available” fast path, and the suite runs the sealed channel under the earlier
desktop version’s own Node runtime and fails the build if that stops being true.
9728d2bA paired device no longer has to be paired again after a restart. Host identity
validated its stored keypair by running a handshake, so the bug above
quarantined and regenerated a perfectly good identity on every launch, orphaning
every device paired to it.
9728d2bBoth phone clients were one byte off the wire spec, and both stand-in hosts
shared the bug — so the fixtures agreed with each other and disagreed with
reality. The stand-ins import the real framing now rather than reimplementing
it.
9728d2bSee the desktop’s localhost on your phone. Tap a port and the phone dials
127.0.0.1 on the desktop through the sealed channel. It is a raw TCP
byte pipe rather than an HTTP proxy, so WebSockets, hot reload, service workers
and cookies all survive untouched. Only 127.0.0.1 is dialable, only
ports something is listening on right now, and only after a person taps
one.
9728d2bStart a session from the phone. Both phone clients had had a New Session button
for a while, gated on a capability no desktop advertised — so it could never
appear, and each client had invented its own frame shape against its own
stand-in. There is one shape now, parsed and narrowed like every other frame; the
desktop advertises it only when its session layer can actually start one; and a
session started this way is a real pseudo-terminal made by the same call the
desktop’s own button makes. A phone may name a folder only if the desktop is
already offering it.
1880202A session started from a phone appears on the desktop, without focus, so
answering something on your phone never pulls the desktop out of the terminal
you were typing into.
Windows, run for the first time
9118e11, e18b47eThe two faults that made 0.1.2 unusable on Windows, found by installing it
there and fixed here. which was spawned as a literal command;
spec.bin was spawned where spec.spawn was meant, and an
npm-installed CLI answers a Windows PATH lookup with a
.cmd shim that CreateProcess will not run; and three
sites that spread process.env over a PATH key lost
against Windows’ own Path. 31 Windows test failures became 0.
The window
9728d2bThe desktop speaks Apple’s language: one sidebar, one toolbar, Settings
bottom-left, liquid glass, and both themes first-class. Three entries came out of
the reachability allowlist and none went in — each was a setting that turned
nothing on.
9728d2bRoughly 87,800 fewer wake-ups a day. One shared renderer scheduler where N jobs
cost one wake-up rather than N, nothing armed at all while the window is hidden,
and a panel that polled a channel already pushing to it now subscribes instead.
The remote panel alone was a 500 ms interval — 172,800 wake-ups a day to move
labels that mostly change once a minute.
1880202Every page got a designed empty state instead of a bare sentence floating in the
middle of the window; pages and Settings hold a measure and centre it, so a wide
display no longer parks the content in the top-left corner; and the update notice
became an inset card wearing the same glass as the sidebar rather than a
full-bleed grey strip.
Packaging
9728d2b, 1880202Every macOS build had been shipping the whole repository. The packager for
the earlier desktop version has a per-platform files: list that
replaces the root allowlist rather
than extending it, so the mac: block had no allowlist at all —
invisible until the phone clients existed, at which point the app reached 1.0 GB.
It is 287 MB now, and the identical latent bug in the win: block was
fixed in the same pass.
8a988baStopped tracking a node_modules symlink that pointed into somebody’s
home directory.
0.1.2
13 August 2026
The first release with a Windows build in it. The installer and the portable
executable are both x64 and both unsigned. Running them on a real Windows 11 machine
is what found the two faults that made this version unusable there — every agent CLI
reported missing, and New Session failing — and both were fixed in 0.1.3.
Windows in the release
1ecca75Version 0.1.2 — the first tag whose artifacts include an x64 installer and a
portable executable alongside the macOS disk image.
03b6a42Let the Windows installer build while its test port is unfinished, so a
failing test on a platform nobody has shipped to does not hold back the
artifact people are asking for.
ef0312d, 5648d65Stopped the test suite assuming POSIX where the product does not, and fixed
two tests that depended on the machine they happened to run on.
af1f02dPut back the workflow step that creates the release, missing since the
previous tag.
0.1.1
13 August 2026
The release that taught an unsigned build to update itself.
Updates
40e84d9Update inside the app, without Squirrel and without a signature. Squirrel.Mac
refuses to replace a bundle it cannot verify, which is every unsigned build —
but the feed, the archive and its SHA-512 are all public, so the app reads the
feed itself, proves the bytes, and swaps the bundle. The old app is moved aside
rather than deleted, and moved back if the new one fails to land.
b161be7Build Windows on Windows. Cross-building was producing an artifact the
prebuilds rule then broke; building it natively in CI does not.
0.1.0
13 August 2026first release
The first version that is a usable application. It runs from source and packages into
an unsigned macOS build, published as a disk image; it predates both the Windows
target and the in-app update path, so it can neither be installed on Windows nor
replace itself.
The window, and the processes behind it
473d9bbThe earlier desktop version: multiple terminal sessions in one window, each a real
process, with the tab strip and scrollback that survive switching away.
51f59c5Projects and window bounds persist between launches, installed agent CLIs are
detected through the login shell’s PATH, and a strict content
security policy is set from the main process — permissive only while
developing.
Knowing what a session is doing
b72448aSession status arrives: working, waiting, needs input, exited — with tests, and
a full build plan for the eight phases that follow.
17a418dThe first real correction. Status had been classified from the tail of the
output stream, which is meaningless for a full-screen program that repaints by
moving the cursor. Each session now feeds a headless terminal emulator and the
status is read from the rendered screen — including the discovery that Claude
Code draws its prompt as ❯, and never on the last line.
The features, built in waves
769ab1dSix modules built in parallel and wired in: transcript reading, cost and
context maths, the git panel, the file tree and viewer, project search, and the
command palette.
678e6bdSession resume — continue the last conversation in a folder rather than
starting a fresh one.
6265ba5Wave two: the session inspector, the GitHub panel driven by
gh, AI-readiness scoring, the project dashboard and the swarm
view.
b601f77Wave three: full-text search across past transcripts, the alerts panel, agent
profiles with isolated logins, provider hooks, the MCP inspector, the first
embedded browser, and one keymap table that replaced three drifting copies of
the shortcut list.
7502ef7Wave four: the settings window, a browser that is a real browser rather than a
panel, the new-session dialog, in-app help and a debug panel.
Making it look like one application
0c5724cThe Notion-style palette — near-neutral surfaces, warm grey text, hairline
dividers, one blue reserved for selection and focus — and a side panel that
collapses and resizes.
d21799cThe app shell proper, which made every feature reachable. Several panels
existed and worked before this and simply had no way in.
b51b44fThe view switcher was styled as a control, because it read as a caption.
a2971c3Tabs moved into the window header, and sessions and browsers were merged into
one strip. Two separate controls for “what do I have open” meant the browser was
effectively unreachable.
0bf3904One header bar. Overview and Board moved to the side rail, where views of a
project belong — the tab strip is for windows you opened.
Making every panel actually work
f8ac57eFixed the panels and views that rendered nothing. Typechecks and tests had all
passed; the failures were contract mismatches at the bridge and runtime throws,
neither of which a compiler sees.
56e5273Every remaining mismatch between the interface and the main process fixed, and
the app made installable.
6f4e500Tracing across the bridge, so a panel that silently fails says why instead of
showing an empty box.
2abed94The browser fix worth naming: a request-style call answered by an event-style
handler rejects every time, so every browser action failed.
b986623A test over the seam itself, so a method the interface calls and the main
process does not implement fails a test rather than a user.
64b2370The browser hides while a dialog is open. It is a native view layered above the
interface, so pressing ⌘, on a browser tab dimmed the
window and showed nothing — settings were underneath the web page.
The browser grows up
b98be29A Setup section that reports what your machine actually has, cookie import from
an installed Chrome so a dev server behind a login does not need signing into
twice, and per-tab isolation for when you need two accounts at once.
Chat mode
6be5d78A session as a conversation, read from Claude Code’s own transcript — prompts
and replies, none of the tool plumbing. The address bar in the browser stopped
guessing at port 3000 and started listing the dev servers actually
listening.
c4c15f6Chat mode gained a composer. It writes into the session’s own terminal, so the
two views never disagree about what was said.
Housekeeping
3472525A README and a working guide for the repository — which also closed two of the
app’s own readiness findings against itself.
6b99916, d951c4c, a3e6acbThe roadmap kept current after each wave landed.
41a96fcRenamed to Terminal Deck. The name lives in exactly one file, so this was an
edit rather than a hunt.
Not in any version yet
Listed here so the absence is on the record rather than discovered after installing.
Peer-to-peer session sharing with another person. Reaching your own machine from your
own phone is a different thing, and it works from 0.1.3 onwards.
Updating in place on Windows. On macOS it works, from 0.1.1 onwards. On Windows the
check is wired to the earlier desktop version's update installer and nobody has
watched it run, and the portable executable is a loose file nothing can replace for you.
Translations.
A signed installer on either platform. The disk image, the Windows installer and the
portable executable are all built; nothing signs any of them, so macOS needs the
right-click step and Windows needs the SmartScreen override.
Split panes. The layout code and its chords are in the repository, but nothing in the
app renders the split view, so there is no way to reach it.
Anything at all on Linux. Intel Macs are deliberately out of scope.