Repository navigation
Make every agent herdr detects visible: Grok and Antigravity chats, terminal rows for the rest, Codex fixes - #172
Merged
Merged
Conversation
This was referenced Oct 16, 2026
Owner
|
Thanks, we'd like to take everything except the screen chat. Could you drop b69a624 (ScreenView, the w9 demo pane and regression/screen-agent)? Agents without a reader should stay terminal rows, since the Terminal tab already covers them. If they're missing on Android or in the Agents view, showing them there as terminal rows would be the preferred fix. |
4 tasks done
The thread picked its send target by focus alone: the focused pane with any agent. In a workspace holding Claude and an agent the app cannot read (opencode, say), focusing the other pane on the host sent the phone's message there, while the thread kept showing Claude's transcript, so the echo was never confirmed and the bubble said "Failed to send". Both the polled target and the one re-read at send time now prefer a conversational agent, focused first, and fall back to any agent only when the chat has none. (cherry picked from commit 4663cef)
Grok was a terminal row with no history: the app read Claude, Codex, Pi and
OMP, and which agents those were was retyped in a dozen files, so adding one
meant touching fifteen to twenty.
`src/lib/herdr/agents.ts` is now the one place an agent is described: its
name, the reader for its history, its herdr integration, its default
terminal titles and what to say when its session cannot be found. The
supported list, agent names, Pi-family checks, default titles, the
integration banner, the install button and the "unsupported" copy all read
it. `TranscriptStore.transcriptPath` and `forgetTranscript` pick the lookup
by the agent's reader, so the thread and the chat list's previews no longer
branch on the agent themselves. Claude's session signature stays bare and
Codex's stays `codex:<id>`, so no cached chat is re-keyed.
Grok's integration reports the session id, which names the session's folder
under `${GROK_HOME:-~/.grok}/sessions/<encoded cwd>/`. The store finds that
exact folder (never the newest one for a cwd) and reads its `updates.jsonl`,
a log of ACP `session/update` notifications. `acp.ts` maps them: prompts
(not the ones Grok writes for itself), replies, thoughts, tool calls and
their final update as the result, and the prompt's model id for the header.
Measured against Grok 1.0.52 on eight real sessions: 308 calls, every
finished result paired. Grok's tool names fold like the others ("ran 1
command"). New Chat can start Grok through `herdr agent start --kind grok`.
The "can't identify this chat" screen offered Claude's integration for any
agent it did not list; it now offers the agent's own, or none.
The Demo gains a Grok workspace (w8, "infra"), answered by the same lookup
and format, with `regression/grok` covering its row, header model, folded
run and a confirmed reply.
(cherry picked from commit 8e1b1fd)
The rule the last two commits rest on, written where the next change will read it: a new agent is a table entry, a parser and a Demo pane, never a new branch on its kind in a feature, and an agent with no reader is still a chat that shows its screen. (cherry picked from commit 7115da7)
Since Codex 0.157 (openai/codex #38127, paginated local history by default from #47900) a rewind writes the thread on into a new rollout, `rollout-<ts>-<id>_<rollout id>.jsonl`, and leaves the old file. The lookup searched only `rollout-*-<id>.jsonl`, so after a rewind the chat kept showing the old file, reverted turns included, and no new turn ever arrived. Every file of the id is found now. One is opened as before. More than one is settled by Codex's own pointer, `threads.rollout_path` in its state database, never by recency; without an answer (no `sqlite3` on the host, a schema this was not written against) nothing is opened, as before. Written from Codex's source at 0.163, not checked against a real rewound session: Codex is not installed here. A rewound file's earlier history lives in its `history_base` prefix file, which is not followed yet, so such a chat shows the turns since the rewind. (cherry picked from commit 3f914eb)
Codex records four calls the Responses API runs on its own, and the reader dropped all of them: a model's local shell (`local_shell_call`, an argv whose output arrives as a `function_call_output`), web search, tool search with its own output item, and image generation. A turn that searched the web or ran a command through the local shell showed nothing of it. Each now folds into the tool run like any other call: "ran 1 command", "searched 1 time". Codex 0.157 replaced the `channel` field on a reply with `phase` (commentary, partial answer, final answer). The TUI shows every phase, so the thread does too; `channel: 'analysis'` stays hidden, since rollouts written before 0.157 still carry it. A test pins each phase. Shapes from codex-rs/protocol/src/models.rs at rust-v0.163.0, and rollout/src/policy.rs for which items are persisted. (cherry picked from commit b7d8da1)
A rewound or forked Codex rollout holds only its own turns. Its first line, `session_meta`, names the file the earlier ones are in: `history_base`, a rollout id (the thread id, or the `_<rollout id>` of a rewind) and the byte offset where the inherited part ends. The thread stopped at the top of the new file, so after a rewind the conversation seemed to start at the rewind. `TranscriptStore.codexHistoryBase` reads that pointer and finds the prefix file by its exact rollout id in the live and archived sessions; anything but exactly one match is no answer, never a guess. When paging reaches the top of a Codex file, the thread continues in the prefix from that offset, and only a file with no base is the start. The search shared with the session lookup is one helper now. Shape from codex-rs/protocol/src/protocol.rs (`HistoryPosition`) at rust-v0.163.0. Not run against a real rewound session: Codex is not installed here. (cherry picked from commit dff450f)
Codex can compress a rollout untouched for 7 days into `.jsonl.zst` (rollout/src/compression.rs). The feature is off by default at 0.163, but once on, the lookup found no `.jsonl` and the chat said the session's transcript was not on the host, sending the reader to look for a file that is there. The lookup now sees the compressed file in the same search and says HerdrChat cannot read a compressed Codex history yet. A plain file beside it is still opened as before. (cherry picked from commit bdff6a1)
…pects A Grok approval reached the phone as bare chips. Grok draws each choice as `┃ 2 (○) Yes, proceed` (herdr's grok.toml, from Grok 1.0.34 pane reads), and the parser only knew `2.` and `2)`, so it found no menu. Those rows now parse, the `┃` gutter already gone with the other borders; the letter rows (`z (○) Type your answer here`) take typing, not a key, and stay out. Grok picks an option on its digit alone (`1`–`9`, Grok's 03-keyboard-shortcuts.md), as Claude does. Every agent but Claude was sent the digit and then Enter, which for Grok would land the Enter in whatever had the keyboard after the card closed. Which agents answer on the digit is now `digitAnswers` in the agent table; an agent not described there keeps the digit and Enter. The fixture is built from the rows herdr quotes, not captured from a live Grok prompt: the Grok here runs with approvals off. (cherry picked from commit 2ea08cd)
Grok titles its terminal `grok`, then `<session> - grok` once the session has a name, with a braille spinner and activity text ahead of it during a turn and "⚠ Action Required" during a prompt (herdr's grok.toml, from Grok 1.0.34). Read as is, a Grok chat was titled "grok", or "⠧ Reading files - Deploy check flake - grok" while it worked. The session name is now the segment before ` - grok`, and a bare `grok` is a default title that names no chat, so the row falls through to the workspace as Claude Code's "Claude Code" does. Pi's default title is still not listed: neither herdr's pi.toml nor anything here shows one. (cherry picked from commit f872a6c)
The host watcher quoted the agent's last words for Claude and Codex only, so a push from a Grok, Pi or OMP chat said just "finished its task". It also read `~/.claude` and `~/.codex/sessions` directly, ignoring the `CLAUDE_CONFIG_DIR` and `CODEX_HOME` the app honours, and never looked in `archived_sessions`. - Claude and Codex are read from their configured folders, archived Codex rollouts included. A rewound Codex thread goes on in `rollout-<ts>-<id>_<rollout id>.jsonl`; every such file is that one thread's, so the one written last is quoted. - Grok's `updates.jsonl` is found by its exact session id: the reply is the last message chunk, and a waiting Grok names the command it waits on. - Pi and OMP report their journal's path, read only when it is absolute, ends in `.jsonl` and holds no `..`. - A Grok terminal title is read as the app reads it (`<session> - grok`), and `grok` joins the default titles that name no chat. What reaches the relay is unchanged: the same fields, with `NOTIFY_PREVIEW=0` still keeping the agent's words off it. WATCHER_VERSION is 7, so the app offers hosts the update. (cherry picked from commit 039bac5)
Typing `/` in a Grok chat showed nothing, while Grok's own terminal lists about sixty commands. Grok gets its menu as Codex does: a fixed list, from the 04-slash-commands.md bundled with Grok 1.0.52, each name, its arguments, and the first sentence under it. Aliases are left to the fuzzy match, and enabled skills, which Grok lists too, are not read. A picked Grok command is sent the way a Codex one is: typed and run, with no prompt wait and no second Enter, and no wait for a receipt, since Grok writes nothing to its log for a command. Grok's `/home`, which leaves the session, joins the commands that are filled rather than sent from a tap. The palette now follows `sendTarget`, so it offers the menu of the agent a message would go to; with an agent the app cannot read focused beside Claude, it showed no commands while messages went to Claude. (cherry picked from commit bbe3e1f)
Codex runs a hook only after a person trusts it (openai/codex #20321), and the Herdr hook is what reports a Codex session's id. Untrusted, it never runs, herdr has no id, and the chat says "Can't identify this chat" with nothing in the guide that names the cause. The Codex section now does: trust the hook in `/hooks`, then exit and resume. (cherry picked from commit 5dbd795)
agy is the agent most used on the host, and the phone listed none of its
chats. herdr detects it (`agent: "agy"`), but the app read no Antigravity
history, so an agy pane was at best a screen. The format was held back as
undocumented (HC-63); it is read here from agy 1.3.6 itself.
- The table names it: `agy`, "Antigravity", integration `antigravity-cli`.
herdr's integration hook reports the conversation id and, when agy gives
it, the transcript's path (read from the hook herdr installs).
- The id is the name of its folder under ~/.gemini/antigravity-cli/brain,
and the log is `.system_generated/logs/transcript.jsonl`. The `_full`
copy beside it differs only in keeping tool output whole. A reported path
is taken as reported, with the same checks as OMP's.
- `agy.ts` reads a step a line: the prompt (the text inside
<USER_REQUEST>, so a sent message's echo is confirmed), the model's turn
(thinking, reply, tool calls with their JSON-encoded arguments decoded),
and a tool's output. An output is the step after its call's, which names
the call: paired by order alone, an interrupted call in a real
conversation took the next output and shifted every one after it.
- The header has the model a prompt switched to ("Gemini 3.8 Flash (High)")
and the context of each turn.
- run_command, view_file, search_web and edit_file fold as commands, reads,
searches and edits.
- New chats can start Antigravity (`agent start --kind agy`).
- A chat waiting for its session names Antigravity, not `antigravity-cli`:
the placeholder is given the integration's name, which for every agent
before this one was the agent's own.
The Demo gains an Antigravity workspace (wA, "assets"), and
regression/agy opens it, checks the model and the folded run, and sends a
message. Tests read a transcript in agy's shape, the interrupted call, and
the Demo's chat end to end.
(cherry picked from commit 07783b8)
regression/history and regression/multi-agent failed on the simulator once the Demo list grew past a screen (HC-115). A row scrolled into the middle moved before the tap, as agents finished and rows changed group, and the tap landed on the search field above it. Typing the workspace's name into the search narrows the list to the row, which then cannot move away. (cherry picked from commit 76fc037)
On a real host, a search for "Antigravity" found nothing, though the row says Antigravity and the empty list says to try an agent's name: the search matched herdr's id for the agent, `agy`. Every other agent's name holds its id, so only this one was lost. The search now matches both, and regression/agy finds its row that way. (cherry picked from commit 76bf5c4)
… Agents An agent with no history reader (opencode, Copilot, Letta and the rest herdr detects) is a terminal row under its workspace in Spaces, which opens its pane's terminal. Android draws no terminal rows, since a shell's would only open the screen saying the terminal is iPhone and iPad only, and the Agents view lists agents with a reader. So such an agent was in no list at all there. On Android it now keeps its terminal row (a shell still has none), and the Agents view lists it as a terminal row of its own, under no card, in the group its status puts it in, found by its agent, folder or workspace. The Demo gains such an agent again (w9, "docs", opencode), as a terminal row, and regression/list-modes checks its row in Agents.
woosal1337
force-pushed
the
hc-45-agent-support
branch
from
October 16, 2026 15:35
30f030a to
9002aac
Compare
…asses The Demo's Grok, opencode and Antigravity workspaces make its list longer, and four of the flows that came with the tabs (#178) failed on it in dark and light: composer-keys, terminal, space-layout and list-modes. Each scrolled to a row and tapped or checked it where the shorter list had put it. A row scrolled to near the end sits under the floating tab bar, and a tap there lands on the Chats tab, which scrolls the list back to the top; a scroll UP to a row that is now below fails outright. composer-keys and terminal open w2 through `_open-chat.yaml`, the search field, as history and multi-agent already do (HC-115). space-layout finds w7 by searching "site", which leaves its card and both rows in view. list-modes scrolls UP to w6's working agent, which sits above w2's Idle. All four pass in dark and light on the stack's Release build.
Collaborator
Author
|
Done in the rebase onto |
Merged
5 tasks done
woosal1337
added a commit
that referenced
this pull request
Oct 16, 2026
The Demo's docs space holds only OpenCode, and its card said "no agent · 1 tab · 1 pane" above a row reading "OpenCode · demo/docs". The line counted the agents the app reads and nothing else; one it cannot read is a terminal row (#172), and still an agent (HC-135). `spaceStats` counts both, and what each is doing from its pane's status, so a waiting OpenCode reads "1 agent needs you". The panes fallback counts each pane once. rowText.test.ts covers alone, waiting and beside a Claude, and fails without the change; regression/space-layout finds the docs space by search and reads "1 agent" on it, in dark and light.
cobanov
marked this pull request as ready for review
October 20, 2026 08:03
cobanov
added a commit
that referenced
this pull request
Oct 20, 2026
Both sides bumped the watcher from 6 to 7 on their own, so it is 8 here: a host running build 91's watcher gets the Grok, Pi and OMP previews. The grok and agy flows join the new e2e flow list and the thread flows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cobanov
pushed a commit
that referenced
this pull request
Oct 26, 2026
The Demo's docs space holds only OpenCode, and its card said "no agent · 1 tab · 1 pane" above a row reading "OpenCode · demo/docs". The line counted the agents the app reads and nothing else; one it cannot read is a terminal row (#172), and still an agent (HC-135). `spaceStats` counts both, and what each is doing from its pane's status, so a waiting OpenCode reads "1 agent needs you". The panes fallback counts each pane once. rowText.test.ts covers alone, waiting and beside a Claude, and fails without the change; regression/space-layout finds the docs space by search and reads "1 agent" on it, in dark and light.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebased onto
mainat 2ce4a9a (#178), with the changes asked for in review. Commit ids below are the rebased ones.HerdrChat read the history of four agents and treated the other fourteen that herdr detects as plain terminal rows (iOS) or nothing at all (Android, and the Agents view). This branch does three things:
src/lib/herdr/agents.tsholds each agent's name, history reader, herdr integration, default titles and the text for a missing session. The supported list, names, titles, integration banner and install button all read it, andTranscriptStore.transcriptPathpicks the lookup by reader. Adding an agent used to touch 15–20 files.agy): its history, model and tool runs, and starting it from New Chat.It also fixes a misrouted send, follows Codex rewinds, and shows Codex calls that were silently dropped.
#18
#18 asks for one agent at a time, on request, and no terminal view. Grok is the requested agent here (used daily, 69 local sessions). The screen chat for agents with no reader was dropped in review: such an agent stays a terminal row, since the Terminal tab already covers it.
Changes, one commit each
Agents
fb6655dA chat's message goes to the agent whose transcript it shows. In a workspace with Claude and an agent the app cannot read, focus alone picked the target, so the message went to the other agent and the bubble said "Failed to send".sendTargetpicks a conversational agent first.a1b67efThe agent table, and Grok's reader. Grok keeps each session in${GROK_HOME:-~/.grok}/sessions/<cwd>/<id>/updates.jsonl, a log of ACPsession/updates; the store finds the folder by the exact id herdr reports, never by recency. Demo workspacew8andregression/grok.9002aacAn agent with no reader is a terminal row on Android and in the Agents view, where it was missing. Demo workspacew9(opencode) has one, andregression/list-modesfinds its row in the Agents view.da18f19CLAUDE.md records the rule: a new agent is a table entry, a parser and a Demo pane, never another=== 'codex'branch in a feature. An agent with no reader stays a terminal row.Grok
8e12808Approval prompts: Grok's┃ 2 (○) Yes, proceedrows parse, and the digit alone answers (Grok's own keyboard docs). Which agents answer on the digit isdigitAnswersin the table; Codex and unknown agents keep digit then Enter.10a2bdcTitles:<session> - grokis read as the session, and a baregroknames no chat.decfbd9Slash commands: Grok's ~60 commands from its bundled docs, sent like Codex's. The palette now followssendTarget, so it offers the menu of the agent a message would go to.6b6a6cdPush previews for Grok, Pi and OMP; the watcher also honoursCLAUDE_CONFIG_DIR,CODEX_HOMEandarchived_sessions.WATCHER_VERSIONis 7. What reaches the relay is unchanged.Antigravity (against agy 1.3.6 on this Mac)
600d7d9Antigravity chats are read from agy's own conversation log.antigravity-clihook reports the conversation id, which is the name of the folder under~/.gemini/antigravity-cli/brain. The log is.system_generated/logs/transcript.jsonlin that folder.<USER_REQUEST>), the model's turn (thinking, reply, tool calls), or a tool's output.wA("assets"), andregression/agyopens it, checks the header and the folded run, and sends a message.1e272c0The search finds Antigravity by the name on its row. On a real host, a search for "Antigravity" found nothing: the search matched herdr's id for the agent,agy. It now matches the id and the name, andregression/agyfinds its row that way.UI flows
5a4cf24Four flows that came with the tabs (Files, Terminal and Computers tabs; every computer is its own host (builds 87 to 90) #178),composer-keys,terminal,space-layoutandlist-modes, failed on the longer Demo list in dark and light: a row scrolled near the end sat under the floating tab bar, and a tap there scrolled the list to the top. They now open their rows through the search field (_open-chat.yaml), ashistoryandmulti-agentdo.a16d4ecregression/historyandregression/multi-agentfind their rows with the search field, not by scrolling (HC-115). With the longer Demo list, a row moved between groups during the scroll, and the tap landed on the search field.Codex (against the source at
rust-v0.163.0)187a16cA rewind (0.157+) goes on inrollout-<ts>-<id>_<rollout id>.jsonl. The lookup finds every file of the id and, when there are several, asks Codex's state database (threads.rollout_path) which is current. Never by recency; no answer opens nothing.56b31e1The earlier turns of a rewound or forked rollout live in the file itssession_meta.history_basenames. Paging continues there fromend_byte_offset.23a736flocal_shell_call,web_search_call,tool_search_call/_outputandimage_generation_callshow in the tool run. Everyphaseof a reply shows; the pre-0.157channel: 'analysis'stays hidden.ac78f62A compressed rollout (.jsonl.zst) is reported as unreadable, not as missing.4121c80The guide says to trust the Herdr hook in/hooks. Codex runs no untrusted hook, so herdr never gets the session id. This is the likely reason Codex chats show "Can't identify this chat".Test plan
npx tsc --noEmit: no errorsnpx expo lint: no errorsnpx jest: 1,979 passed, 14 skippedpython3 -m unittest discover -s scripts -p 'test_*.py': passesregression/grokpassed once in Dark on an iOS 27 simulator (development build)regression/agyon the iPhone 17 Pro simulator (iOS 27), Dark and Lightherdr integration install antigravity-cliand an agy restart: herdr reports the session, and the chat shows its historynpm run e2ein Dark and Light on the iPhone 17 Pro simulator (iOS 27), Release build of the rebased stack (Make every agent herdr detects visible: Grok and Antigravity chats, terminal rows for the rest, Codex fixes #172 to Close the remaining product gaps: one socket bridge, Antigravity dialogs, encrypted keys, accessibility #177): 52 of 52, the four flows5a4cf24fixes rerun on their own after it. Screenshots opened in both modes.The Codex changes follow upstream source and are not tested against real rewound files, because Codex is not installed here. The Grok approval fixture is built from the rows herdr's
grok.tomlquotes, not from a live prompt.Not in this PR