Session Discovery
Finding transcripts by cwd, session ID, and time window.
Harnesses write transcripts to global, harness-owned locations
(~/.claude/projects, ~/.codex/sessions,
~/.gemini/antigravity-cli/brain). Discovery scans those roots, reads
each transcript’s identity cheaply (a header read via the real parser,
never a second format), and filters by the keys harnesses actually
record.
From the CLI
The sessions command is the discovery entry point; see
CLI for its filters and examples.
From Go
agentminutes.Scan enumerates every harness’s default root.
agentminutes.Locate resolves a known session ID to its transcript
path(s), which is the capture-time primitive for runners that archive
transcripts right after a headless invocation:
ref, err := agentminutes.Locate(harness.ClaudeCode, sessionID)
if err != nil {
return err // wraps os.ErrNotExist when the session has no transcript
}
archive(ref.Path) // the main transcript
for _, p := range ref.SubagentPaths { // Claude Code agent-*.jsonl files
archive(p)
}
For non-default roots, use agentminutes.LocatorFor(id).Scan(root, opts).
Refs survive resumed turns
A located ref stays valid across resumed turns: every supported harness
appends resumes to the same transcript rather than forking a new session
(validated on the versions listed in
Harness Support). Multi-turn runners can re-Locate
the same ID after each turn to pick up the grown transcript and any new
subagent files.
Here is what that looks like end to end, driving Claude Code through two turns with agentsummons and locating between them. Turn 1 presets the session identity, and the transcript is findable as soon as the turn completes:
$ SESSION=84cffcb8-161f-4921-8564-ed889068ddb3
$ agentsummons run --harness claude-code --session-id "$SESSION" \
-p "Reply with exactly one word: ping" --auto-approve
ping
$ agentminutes sessions --harness claude-code --session-id "$SESSION"
~/.claude/projects/-tmp-demo/84cffcb8-161f-4921-8564-ed889068ddb3.jsonl
$ wc -l < ~/.claude/projects/-tmp-demo/84cffcb8-*.jsonl
10
Turn 2 resumes the same session. Locating again returns the same path, and the transcript has grown in place:
$ agentsummons run --harness claude-code --resume "$SESSION" \
-p "Reply with exactly one word: pong" --auto-approve
pong
$ agentminutes sessions --harness claude-code --session-id "$SESSION"
~/.claude/projects/-tmp-demo/84cffcb8-161f-4921-8564-ed889068ddb3.jsonl
$ wc -l < ~/.claude/projects/-tmp-demo/84cffcb8-*.jsonl
18
Parsing that one transcript yields one session record covering both turns:
$ agentminutes stats ~/.claude/projects/-tmp-demo/84cffcb8-*.jsonl | jq '{user_messages, event_counts, final_answer}'
{
"user_messages": 2,
"event_counts": {
"assistant_message": 2,
"session_meta": 1,
"system": 4,
"thinking": 2,
"user_message": 2
},
"final_answer": "pong"
}
The archiving consequence: a runner that grabs the ref after turn 1 can keep re-reading the same path after every later turn, and an archive taken after the final turn contains the whole conversation. There is no second file to forget.
The accounting discipline
Scans obey the same accounting discipline as parses, lifted from
per-line to per-file: everything under a root is a yielded ref, a
reported skip (ScanOptions.OnSkip), or a *harness.ScanError, which,
unlike a parse error, does not end the scan.
Discovery never parses beyond each transcript’s head, and it never
invents identity except where the layout is the documented source. An
Antigravity session ID is its conversation directory name; the format
records none in-band. From agy 1.1.8, a headless run with
--output-format json receives the conversation ID in its result
envelope, so capture-time runners need time-window discovery only in
text mode or on older releases.