A live structured chat view for agents running inside Herdr.
Herdr Chat is a local chat overlay for Codex and Claude Code sessions running inside Herdr. It turns the bound JSONL transcript into a readable conversation while the original agent keeps running in its source pane.
The overlay renders messages, Markdown, code, thinking, tool activity, diffs, plans, todos, usage, approximate cost, images, and Claude subagents. Its composer sends text through Herdr's session-bound agent API, with file, command, and skill completion.
Requirements
- Apple Silicon Mac. Intel macOS and Linux are not supported by the pinned renderer yet.
- Node.js 22 or newer.
- Herdr 0.8.2 or newer.
- A local Ghostty or kitty window with Kitty graphics support.
- A Herdr integration for each agent you use.
Enable the graphics API in ~/.config/herdr/config.toml:
[experimental]
kitty_graphics = true
Reload a running server after changing it:
herdr server reload-config
Install the agent integrations, then restart any running agent sessions so they begin reporting session identity:
herdr integration install codex
herdr integration install claude
Install
Review the repository and manifest, then install it from GitHub:
herdr plugin install eliasstravik/herdr-chat
Herdr runs the three manifest build steps: npm ci, npm run build, and the pinned terminal-browser installer. The installer supports darwin-arm64 only, verifies the archive against scripts/terminal-browser.lock, and does not run terminal-browser's setup command.
For local development, build and link the checkout instead:
npm ci
npm run build
npm run install:terminal-browser
herdr plugin link "$PWD"
Open and close
Focus a Herdr pane running Codex or Claude Code, then invoke:
herdr plugin action invoke herdr-chat.open
The same action is a toggle. Invoke it from the source pane or the open overlay to close the overlay. Invoke it from a different agent pane to move the overlay binding to that pane.
Plugins cannot ship user keybindings. To bind the recommended prefix+c, add this to ~/.config/herdr/config.toml:
[[keys.command]]
key = "prefix+c"
type = "plugin_action"
command = "herdr-chat.open"
Controls
| Control | Result |
|---|---|
Enter |
Send the composer text |
Shift+Enter |
Insert a newline |
Ctrl+X |
Interrupt the bound agent |
Esc |
Leave the composer, then close the overlay when the draft is empty |
/ |
Focus the composer |
Ctrl+S |
Stash the draft in local browser storage |
? |
Show the shortcut panel |
PgUp, PgDn, wheel |
Scroll the conversation |
@path |
Search workspace files and insert a path |
/command |
Complete Codex built-ins or Claude commands and skills |
| Paste or drop an image | Save it under plugin state and insert its path |
| Claude mode button | Send Shift+Tab to cycle permission or plan mode |
| Open raw terminal | Close the overlay and reveal the source pane |
Codex /model and /approvals, plus Claude /rewind, send the command and return to the raw terminal for the agent's interactive picker. Claude /model <name> can remain in chat. The selected text from the source pane appears as an optional context chip.
You can override the seven displayed shortcuts by creating keybindings.json in the directory printed by herdr plugin config-dir herdr-chat:
{
"send": "Enter",
"newline": "Shift+Enter",
"interrupt": "Ctrl+X",
"quit": "Esc",
"focusComposer": "/",
"stash": "Ctrl+S",
"shortcuts": "?"
}
Values use combinations such as Ctrl+X, Shift+Enter, Alt+K, or Cmd+K.
Session detection
Herdr's Codex or Claude integration reports the focused pane's agent type and session identity. Herdr Chat binds that identity to the pane before it reads or sends anything.
- Codex UUID sessions resolve to one
rollout-…-<uuid>.jsonlbelow$CODEX_HOME/sessions(normally~/.codex/sessions). - Claude UUID sessions resolve to one
<uuid>.jsonlbelow$CLAUDE_CONFIG_DIR/projects(normally~/.claude/projects). Claude subagents resolve only below that session'ssubagentsdirectory. - An integration may report an explicit transcript path. Its real path must still be a regular file below the corresponding agent root.
No match, more than one match, a path outside the root, or a changed pane identity fails closed with a text screen or disabled input. A new session in the same pane reloads the conversation. The transcript is tailed read-only; prompts and key presses go through Herdr's agent APIs and are rechecked against the binding immediately before sending.
Limits
- v1 is local-terminal only. It does not render through mosh, mobile, or a standalone web view.
- Approval prompts and
AskUserQuestioncards are read-only. Use Open raw terminal to answer them. - Unknown transcript records are retained as optional raw cards and never become actions.
- Uploaded images are limited to PNG, JPEG, GIF, or WebP files up to 10 MB. Claude can read the inserted path; Codex may ignore an image path in an existing session.
- Initial transcript loading is capped at the newest 32 MB; refreshes read at most 4 MB and individual JSONL records at most 1 MB.
- Cost is an estimate derived from transcript token totals and bundled list-price assumptions. It is not billing data.
- Links are displayed and copyable, but the overlay does not open them.
Security model
Herdr plugins are executable code and run as your user. Review the manifest and scripts before installing this or any third-party plugin.
Herdr Chat starts one HTTP server on a random 127.0.0.1 port for each overlay. Requests must carry the exact loopback Host, and control routes require a random per-launch session token. A one-use bootstrap token is passed in the terminal-browser URL, which means it is briefly visible in the renderer process argv and initial query string. The session token used by the EventSource stream also appears in that loopback request's query string because the EventSource API cannot set a custom header. Other control requests put it in a header. Both tokens die with the overlay and are never written intentionally, but local process inspection or renderer diagnostics may expose them while it runs.
The bridge has no shell-execution endpoint. It caps request bodies, restricts uploads to a private plugin-state directory, validates transcript and subagent paths by real path, and serves static files only from the built web root. Transcript content is untrusted display data: Markdown HTML is escaped, control characters are stripped, and unknown records remain inert. The vendored renderer is version-pinned and checksum-verified during install.
Development
npm run check
npm test
npm run test:page
npm audit --audit-level=high
The integration suite starts isolated headless Herdr servers with temporary home, config, and state directories. It never starts or resumes a real coding agent and never touches the user's Herdr plugin registry.
{
"request": {
"method": "GET",
"url": "https://api.eliasstravik.com/projects/herdr-chat"
},
"response": {
"status": "200 OK",
"headers": {
"content-type": "application/json; charset=utf-8",
"x-powered-by": "one person + systems"
},
"body": {
"name": "herdr-chat",
"description": "A live structured chat view for agents running inside Herdr",
"language": "TypeScript",
"stars": 0,
"forks": 0,
"repo": "https://github.com/eliasstravik/herdr-chat",
"readme": "https://github.com/eliasstravik/herdr-chat#readme"
}
}
}