herdr-convo
v0.1.1
Published
Normalized, incremental conversation turns from OpenCode, Codex, Claude Code and Pi sessions
Maintainers
Readme
herdr-convo
Read another coding agent's conversation as normalized turns.
Herdr knows which agent occupies a pane and whether it is working or finished.
It does not read what that agent said. herdr-convo does, and presents Claude Code, Codex, OpenCode
and Pi through one shape — so an orchestrator can follow another agent without knowing which agent
is on the other side, and without scraping a terminal.
herdr-convo latest w1:p2 --text # what that agent last said
herdr-convo read w1:p2 --cursor "$C" # only what happened sinceInstall
Node 24 or newer, no runtime dependencies.
npm install -g herdr-convo # the command
herdr plugin install arvemy/herdr-convo # Herdr pane actions
npx skills add arvemy/herdr-convo # agent skillOr npx herdr-convo … to run it without installing.
Usage
herdr-convo read <target> [--cursor C] [--limit N] [--text]
herdr-convo latest <target> [--text]
herdr-convo locate <target>
herdr-convo inspect <ref>A target is a Herdr agent name or pane id, and defaults to the invoking pane inside a plugin action.
Without a Herdr server, address a session directly with --agent claude --session <id>.
Output is JSON unless --text is given. Errors are JSON on stderr with a code — handle
cursor_stale, session_not_found, no_agent_session, herdr_unavailable and unsupported_agent.
cursor=$(herdr-convo read w1:p2 | jq -r .cursor)
while sleep 5; do
new=$(herdr-convo read w1:p2 --cursor "$cursor")
echo "$new" | jq -r '.turns[] | "\(.role): \(.text)"'
cursor=$(echo "$new" | jq -r .cursor)
doneWhat a turn is
One user message, or one assistant response with everything it did to produce it. Reasoning, tool calls, results and patches are counted, never inlined, so reading a conversation does not drag a session's logs into your context.
{
"turn_id": "claude:5df48142-…:a71c",
"role": "assistant",
"text": "There are 2 files.",
"agent": "claude",
"session_id": "5df48142-…",
"omitted": { "reasoning": 1, "tool_calls": 1, "patches": 0 },
"refs": ["eyJhIjoiY2xhdWRl…"],
"complete": true
}Nothing is thrown away: every omitted block leaves a ref, and inspect <ref> returns that one
block without reading the rest of the session.
Two invariants, because getting either wrong fails silently. A cursor never advances past a turn still being written — an in-progress answer is re-sent whole once it finishes, never in halves. And only what a person typed becomes a user turn: every harness writes its own context as user messages (slash commands, environment blocks, skill prompts, compaction summaries), and each adapter filters them on the strongest signal its format carries.
Providers
| Agent | Source | Position |
| --- | --- | --- |
| Claude Code | ~/.claude/projects/<cwd-slug>/<session>.jsonl | byte offset |
| Codex | ~/.codex/sessions/YYYY/MM/DD/rollout-*-<session>.jsonl | byte offset |
| Pi | ~/.pi/agent/sessions/<cwd-slug>/<time>_<session>.jsonl | byte offset |
| OpenCode | ~/.local/share/opencode/opencode.db | part and message ids |
Sessions are read where the agent writes them. Nothing is copied or cached, and reads are read-only.
Development
git clone https://github.com/arvemy/herdr-convo && cd herdr-convo
npm install
npm run check # typecheck and the full suite
npm link # herdr-convo on PATH
herdr plugin link . # plugin follows the working tree, unlike `install`From a checkout, TypeScript runs directly. The published package ships compiled JavaScript, because
Node refuses to strip types under node_modules.
Every provider goes through the same conformance cases in test/conformance.ts, so a new adapter is
a classification table, a fixture directory and one runConformance call. Fixtures are synthetic;
HERDR_CONVO_REQUIRE_REAL_SESSIONS=1 additionally asserts against real sessions on your machine.
docs/conversation-layer.md is the contract: turn model, cursor and ref semantics, per-provider
turn-end signals, error codes, and the bounds that are stated rather than papered over.
License
Apache 2.0 — see LICENSE.
