unsent¶
unsent saves the message you have typed into a coding agent and not sent yet, and gives it back after a closed window, a reboot, a crash or one Ctrl+C too many. Claude Code, Codex, pi and agy save your conversations, not the message still in the box. unsent runs the agent in your terminal as before, reads its input box every 0.4 seconds and keeps the text on disk; it saves your half-typed zsh command line too. Start with Getting started, then Using unsent. Supported agents says which version of each agent was last checked, and how.
-
Homebrew, Go or a release binary for macOS and Linux.
-
unsent setuponce,unsent statusto check, and the line you see after a crash. -
Recovering drafts, the JSON output for scripts, the Claude Code hook, and what differs in Codex, agy and pi.
-
Every environment variable, what it does when unset, and how to turn
unsentoff for a while.
After a crash¶
The next time you start the agent in that folder, unsent says what it kept, before the agent starts and again after it exits:
unsent restore copies the draft to the clipboard. Reopen the conversation you typed it in instead (claude --resume, codex resume --last, agy -c) and the draft goes back into the empty box on its own, with a line under it: unsent: put back your draft from 18:42 today, not sent. Nothing is sent for you.
What it saves¶
- The agent's input box as you type, including a long draft that scrolls inside the box and a big paste the agent shows as a placeholder such as
[Pasted text #1 +39 lines]. - Drafts you cleared or replaced, in a history that
unsent list --allshows. - Every message you send, in a sent log per conversation (per run in pi), so an older session's messages are one command away. For Claude Code, each message can also carry its context: the question it answered.
- Your half-typed zsh command line, when a window closes or Ctrl+C clears it.
How it runs¶
flowchart LR
T[Your terminal<br/>any app, tmux or SSH]
U[unsent<br/>a pseudo-terminal and a copy of the screen]
A[The agent<br/>Claude Code, Codex, pi or agy]
D[(~/.local/state/unsent<br/>drafts, history, sent logs)]
T <-->|every byte, untouched| U
U <--> A
U -->|when the box changes| D
unsentwraps the agent, not the terminal, so it works in any terminal app, in tmux and over SSH, with the agent's own keys and arguments.- Each save goes to a temporary file, is flushed with
fsyncand renamed into place, so a power cut leaves the previous save or the new one, never half a file. unsent setupadds one block to your shell's rc file soclaude,codex,piandagyrun throughunsentfrom the next shell. How it works has the whole mechanism.
What it does not do¶
- It never sends anything for you. A draft goes back into an empty box or to the clipboard, and you decide.
- It can't see agents that don't run in a terminal, such as the Claude Desktop app or the VS Code extension's chat panel.
- An agent it has no reader for runs through it unchanged and nothing is saved;
unsentsays so before the agent starts and after it exits. - It runs on macOS and Linux, including WSL, not on native Windows.
- Text scrolled out of sight in a tall draft is inferred, and an inference can be wrong; the correct copy then waits in history. Limits lists every case.
Privacy¶
Drafts and sent logs are plain JSON under ~/.local/state/unsent/, in a folder only you can read (0700, files 0600). unsent makes no network connections. The optional Claude Code hook passes Claude the first line of a kept draft, cut to 100 characters, and nothing more. To add context to sent messages, unsent reads Claude Code's transcripts, but only your own turns and the agent's text right before each one, and writes only to its own folder. Configuration says how to move the folder or keep nothing of what you send.
Getting help¶
- After an agent update
unsentsayscould not read claude 2.1.290's box this session (last verified 2.1.282), nothing was saved: the agent changed its box. SetUNSENT_OFF=1until a release reads it, and open an issue with both versions. unsent statuslists a launcher that bypasses the wrapper: Using unsent says which launchers skip it and how to name the agent with--as.- Something else: open an issue. A security problem goes through the security policy, never a public issue.
- Running the tests and recording a fixture from a real agent: Development.
License¶
unsent is released under the GPL-3.0-or-later license.