Summary
plannotator-tui last cannot resolve transcripts for Hermes CLI panes. hermes is not in the supported host list, so herdr last (and Herdr Annotate's annotate.last / prefix+shift+o) falls back to the pane's recent screen output instead of the agent's last reply.
Hermes differs from every host you support today in two ways that make it worth its own reader:
- Herdr reports its session as an id, not a path (
AgentSessionRefKind is ["id", "path"]).
- Hermes stores conversations in SQLite, not in per-session JSONL files. There is no transcript file, so
--session <transcript> cannot be used as a workaround either.
Environment
- plannotator-tui 0.3.0, as bundled by herdr-annotate 0.3.0 (commit
8dc0de1)
- Herdr 0.8.2; Linux x86_64 and macOS arm64
- Agent: Hermes CLI; Herdr integration
hermes reports current (v5)
What happens
omp-style flow: herdr last passes only PLANNOTATOR_TUI_HOST=hermes plus a pid to the doc pane. hermes is not among claude, codex, copilot, droid, pi, so it takes the documented fallback — … is not supported yet; supported hosts: … → — showing the pane's recent output instead — and the header reads hermes · screen instead of hermes · last message.
What Herdr provides
The Herdr integration Hermes ships (~/.hermes/plugins/herdr-agent-state/__init__.py, HERDR_INTEGRATION_VERSION=5) reports identity on on_session_start, on_session_reset and pre_llm_call with:
command = [herdr, "pane", "report-agent-session", pane_id,
"--source", "herdr:hermes", "--agent", "hermes",
"--seq", str(time.time_ns()),
"--agent-session-id", session_id,
"--session-start-source", start_source]
So herdr agent get <pane> yields agent_session with kind: "id" and value = the Hermes session id. Note the string agent_session does not occur anywhere in the shipped 0.3.0 binary, so this field appears to be unused today — for Hermes it is the only usable handle, since there is no file to discover.
Where the messages live
SQLite at ~/.hermes/state.db (WAL mode; state.db-wal / state.db-shm alongside). Relevant schema, verbatim:
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
source TEXT NOT NULL,
display_name TEXT,
model TEXT,
parent_session_id TEXT,
started_at REAL NOT NULL,
ended_at REAL,
message_count INTEGER DEFAULT 0,
...
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL REFERENCES sessions(id),
role TEXT NOT NULL,
content TEXT,
tool_call_id TEXT,
tool_calls TEXT,
tool_name TEXT,
timestamp REAL NOT NULL,
reasoning TEXT,
observed INTEGER DEFAULT 0,
active INTEGER NOT NULL DEFAULT 1,
compacted INTEGER NOT NULL DEFAULT 0,
...
);
CREATE INDEX idx_messages_session_active
ON messages(session_id, active, timestamp);
sessions.id is exactly the id Herdr reports. Observed role values: user, assistant, tool, session_meta.
Getting the last reply is one indexed query — verified against a live database (3,215 sessions, 165,079 messages, 1.6 GB):
SELECT content FROM messages
WHERE session_id = ?1 AND role = 'assistant' AND active = 1
ORDER BY timestamp DESC
LIMIT 1;
That returned the correct newest assistant reply. idx_messages_session_active(session_id, active, timestamp) covers it, which matters at this size — a scan is not an option.
Expected
last on a Hermes pane shows that pane's newest assistant reply, with the header hermes · last message, and the recent-replies picker works as it does for Claude and Codex.
Suggested fix
- Add
crates/plannotator-tui-hosts/src/hermes.rs resolving a session id against ~/.hermes/state.db with the query above; --pick N maps to LIMIT N+1 / OFFSET N over the same ordering. Fixture can be a tiny generated .db next to the existing JSONL fixtures.
- Open the database read-only and non-destructively:
file:…state.db?mode=ro while honouring the WAL, so a live Hermes process is never disturbed. A stale-read is preferable to touching the agent's own store.
- This host has no file transcript, so the reader must accept an id, not a path. That likely means threading Herdr's
agent_session ({kind, value}) through instead of only host+pid — which would also fix omp and the other Herdr-integrated agents in one go (herdr integration status lists 17 against your 5 hosts).
HERMES_HOME may be worth honouring for the database root; the default is ~/.hermes.
Happy to test a patch against a real Hermes setup.
Summary
plannotator-tui lastcannot resolve transcripts for Hermes CLI panes.hermesis not in the supported host list, soherdr last(and Herdr Annotate'sannotate.last/prefix+shift+o) falls back to the pane's recent screen output instead of the agent's last reply.Hermes differs from every host you support today in two ways that make it worth its own reader:
AgentSessionRefKindis["id", "path"]).--session <transcript>cannot be used as a workaround either.Environment
8dc0de1)hermesreportscurrent (v5)What happens
omp-style flow:herdr lastpasses onlyPLANNOTATOR_TUI_HOST=hermesplus a pid to the doc pane.hermesis not amongclaude,codex,copilot,droid,pi, so it takes the documented fallback —… is not supported yet; supported hosts: …→— showing the pane's recent output instead— and the header readshermes · screeninstead ofhermes · last message.What Herdr provides
The Herdr integration Hermes ships (
~/.hermes/plugins/herdr-agent-state/__init__.py,HERDR_INTEGRATION_VERSION=5) reports identity onon_session_start,on_session_resetandpre_llm_callwith:So
herdr agent get <pane>yieldsagent_sessionwithkind: "id"andvalue= the Hermes session id. Note the stringagent_sessiondoes not occur anywhere in the shipped 0.3.0 binary, so this field appears to be unused today — for Hermes it is the only usable handle, since there is no file to discover.Where the messages live
SQLite at
~/.hermes/state.db(WAL mode;state.db-wal/state.db-shmalongside). Relevant schema, verbatim:sessions.idis exactly the id Herdr reports. Observedrolevalues:user,assistant,tool,session_meta.Getting the last reply is one indexed query — verified against a live database (3,215 sessions, 165,079 messages, 1.6 GB):
That returned the correct newest assistant reply.
idx_messages_session_active(session_id, active, timestamp)covers it, which matters at this size — a scan is not an option.Expected
laston a Hermes pane shows that pane's newest assistant reply, with the headerhermes · last message, and the recent-replies picker works as it does for Claude and Codex.Suggested fix
crates/plannotator-tui-hosts/src/hermes.rsresolving a session id against~/.hermes/state.dbwith the query above;--pick Nmaps toLIMIT N+1/OFFSET Nover the same ordering. Fixture can be a tiny generated.dbnext to the existing JSONL fixtures.file:…state.db?mode=rowhile honouring the WAL, so a live Hermes process is never disturbed. A stale-read is preferable to touching the agent's own store.agent_session({kind, value}) through instead of only host+pid — which would also fix omp and the other Herdr-integrated agents in one go (herdr integration statuslists 17 against your 5 hosts).HERMES_HOMEmay be worth honouring for the database root; the default is~/.hermes.Happy to test a patch against a real Hermes setup.