Session Discovery
Finding transcripts by cwd, session ID, and time window.
Harnesses write transcripts to global, harness-owned locations
(~/.claude/projects, ~/.codex/sessions,
~/.copilot/session-state, ~/.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).
Subagent files belong to their session
A Claude Code session that delegated work to subagents spans several
files: the parent transcript, plus one transcript per subagent under
<session-id>/subagents/ next to it. Discovery treats them as one
session rather than several. A session-ID lookup returns the parent
path and every subagent path together (the Go ref’s SubagentPaths,
shown above, is the same grouping):
$ agentminutes sessions --harness claude-code --session-id 4d51ce48-0e3d-4321-82f5-435769bc5ab4
~/.claude/projects/my-project/4d51ce48-0e3d-4321-82f5-435769bc5ab4.jsonl
~/.claude/projects/my-project/4d51ce48-0e3d-4321-82f5-435769bc5ab4/subagents/agent-a32569d82c684ca51.jsonl
~/.claude/projects/my-project/4d51ce48-0e3d-4321-82f5-435769bc5ab4/subagents/agent-a69749826ce0a9ed8.jsonl
~/.claude/projects/my-project/4d51ce48-0e3d-4321-82f5-435769bc5ab4/subagents/agent-a7578b0c629cb97e7.jsonl
~/.claude/projects/my-project/4d51ce48-0e3d-4321-82f5-435769bc5ab4/subagents/agent-a9966d78dab862c6f.jsonl
agentminutes: 1 session, 0 filtered out, 0 skipped files, 0 errors
Two edge behaviors are deliberate. A subagent transcript whose parent
file is missing still surfaces, as a standalone ref, so partial
archives stay discoverable. And the .meta.json sidecars next to the
subagent transcripts are excluded with a counted skip reason, under
the same accounting discipline described below.
Codex subagents are sibling rollout files rather than nested ones, and
their filenames carry each thread’s own id while the in-band
session_id records the root thread’s. Consequences for discovery: a
scan groups a task by session_id (the filter also matches a
subagent’s own thread id), while Locate resolves exactly one thread’s
file, so --harness codex --session-id <parent> returns the parent
alone. Antigravity subagent conversations are structurally ordinary
sessions; nothing in the layout or transcript marks them, and the
linkage is embedded in step content. Gathering a whole task on either
harness is Locator.Gather (what stats --include-subagents uses),
which does the sibling scan for Codex and the content join for
Antigravity. For how the files tie back
to the parent on each harness and how to analyze a whole task, see
Subagents.
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. Resuming never creates a second file to forget; delegation does, and the same ref lists those subagent files too (see above).
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.