Skip to main content

Module scan

Module scan 

Source
Expand description

Offline session scanners - stateless filesystem helpers that read transcripts from <config_dir>/projects/<project_key>/*.jsonl.

Session metadata (list_sessions) comes from an internal head + tail lite read, so the fields it parses cost two 64 KiB reads whatever the transcriptโ€™s size. The worker tag is the exception and is read in full: it can sit anywhere in the file and the last one wins, so there is no window that settles it.

Re-exportsยง

use std::collections::HashMap; ๐Ÿ”’
use std::collections::HashSet; ๐Ÿ”’
use std::fs; ๐Ÿ”’
use std::io::BufRead; ๐Ÿ”’
use std::io::BufReader; ๐Ÿ”’
use std::io::Read; ๐Ÿ”’
use std::io::Seek; ๐Ÿ”’
use std::io::SeekFrom; ๐Ÿ”’
use std::path::Path; ๐Ÿ”’
use std::path::PathBuf; ๐Ÿ”’
use serde_json::Value; ๐Ÿ”’
use unicode_normalization::UnicodeNormalization; ๐Ÿ”’
use uuid::Uuid; ๐Ÿ”’
use forge_primitives::FORGE_WORKER_TAG_PREFIX; ๐Ÿ”’
use forge_primitives::SDKSessionInfo; ๐Ÿ”’
use forge_primitives::SessionHistory; ๐Ÿ”’
use forge_primitives::SessionMessage; ๐Ÿ”’
use forge_primitives::SessionMessageKind; ๐Ÿ”’
use forge_primitives::worker_tag; ๐Ÿ”’
use forge_sdk::projects_dir_for; ๐Ÿ”’

Structsยง

Block ๐Ÿ”’
One sidecarโ€™s frames and the dispatch it ran as.
LiteSessionFile ๐Ÿ”’
Head / tail snapshot of a session file - enough to recover all SDKSessionInfo fields without a full scan. The tag field is populated by a separate streaming line-scan because tag rows can appear anywhere in the file (start-of-file at spawn, mid-file via /new re-tag) and the head + tail windows miss both cases on large transcripts.
SessionTagCache
Carries the previous runโ€™s tag scans into this one and collects what changed, so the whole catalog costs one store read up front and one store write at the end rather than a transaction per transcript.
SessionTagScan
What a tag scan learned about one transcript, and how far into it the answer is known to hold.
SidecarLink ๐Ÿ”’
A sidecarโ€™s own meta: the dispatch its agent ran as, and the agent whose sidecar fired that dispatch.
Window ๐Ÿ”’
One window of a transcript file: the bytes read and the byte they start at.

Enumsยง

Workers
Which worker sessions a listing may carry.

Constantsยง

LIST_SESSIONS_MAX_CONCURRENT ๐Ÿ”’
Bounded-concurrency cap for list_sessions per-file lite reads.
LITE_READ_BUF_SIZE ๐Ÿ”’
Size of the head / tail byte buffer for lite metadata reads. The CLI constant - match exactly so the two implementations slice transcripts at the same boundary.
MAX_SANITIZED_LENGTH ๐Ÿ”’
ROWS_PAST_CUT ๐Ÿ”’
How many rows past a pageโ€™s own count a cut may step back over, so that it does not open on a result without its call.
SIDECAR_BUDGET_BYTES ๐Ÿ”’
How many bytes of sidecar frames one resume reads in total. A session can hold hundreds of sidecars and most of them are older than the window the conversation is cut to, so the read is bounded rather than whole (#1763).
SPAN_CAP_BYTES
SPAN_ROWS_PER_TURN
How many rows a pageโ€™s own turn count may ask for.
SPAN_STEP_BYTES
How many bytes of a transcript one paging pass reads before growing, and how wide a single window it takes may be.
START_REASONS ๐Ÿ”’
The removal reasons that mean the CLI TOOK the prompt, read off this machineโ€™s transcripts: absorbed_mid_turn (pressed into the running turn), delivered_to_agent (handed over at its boundary), and the empty reason on rows written before the field existed. Any other reason did not start the prompt and draws no row.
SYSTEM_ROWS_DRAWN_ELSEWHERE ๐Ÿ”’
The system subtypes whose row is not the conversationโ€™s: the task lifecycle lands on its callโ€™s row, the background-task and command lists feed their own surfaces, the handshake is not content, and a hook run draws from its attachment row. The client filters the same list (its own SYSTEM_DRAWN_ELSEWHERE), because the read and the live path are one obligation - a subtype added here belongs there, and the other way round.
UNREAD_TRANSCRIPT_COUNT_NOTE ๐Ÿ”’
Tail shared by every get_session_messages bail. The compaction count these paths return is unknown rather than zero, and the Projects-pane row hides at zero, so โ€œcount says 0 on a session I know compactedโ€ has no triage path unless each bail is greppable.
UNSERVED_ROWS ๐Ÿ”’
Top-level rows the read does not serve, of two kinds: the transcriptโ€™s own bookkeeping, which the CLI writes to disk and never streams, and the wire types that live only on the stream and have no durable row.

Functionsยง

already_kept ๐Ÿ”’
Whether this read has already kept a row under uuid.
answered_dispatches ๐Ÿ”’
The calls row answers.
answers_dispatch ๐Ÿ”’
Whether row is the tool result answering dispatch.
block_field ๐Ÿ”’
The field of every content block of kind in row.
canonicalize_path ๐Ÿ”’
Resolve a directory to its realpath and apply NFC normalisation. Wraps the CLIโ€™s _canonicalize_path (falls back to the input, NFC-normalised, when the path canโ€™t be canonicalised: most commonly because it doesnโ€™t exist). NFC is essential on filesystems that donโ€™t auto-normalise (Linux ext4, Windows NTFS) so decomposed inputs still hash to the CLIโ€™s on-disk project-key layout.
carries_a_result ๐Ÿ”’
Whether a row carries a tool result: the kind of row a cut may not land on, because the call it answers sits just above it in the page below.
compact_boundary_frame ๐Ÿ”’
The boundary row in the shape the wire sends.
emit_block ๐Ÿ”’
Append one block, with each child block inserted after the row that carries its dispatch.
emit_sidecar_tree ๐Ÿ”’
One ownerโ€™s blocks, flattened in the order the work ran: a block emits at the row naming its dispatch, and a block no other names - its parent body was past the budget - follows the roots.
extract_command_name ๐Ÿ”’
Extract <command-name>CMD</command-name> when present.
extract_first_prompt_from_head ๐Ÿ”’
Extract the first meaningful user prompt from a JSONL head chunk. Skips tool_result, isMeta, isCompactSummary, slash-command messages (with command-name fallback), and the fixed-prefix skip patterns the CLIโ€™s _SKIP_FIRST_PROMPT_PATTERN matches. Truncates to 200 chars with an ellipsis.
extract_json_string_field ๐Ÿ”’
Extract the first occurrence of a JSON string field ("key":"value" or "key": "value"). Scans bytes directly to survive partial tail reads; unescapes via serde_json only when the value contains a backslash. Returns None when the field is absent or unterminated.
extract_last_json_string_field ๐Ÿ”’
Like extract_json_string_field but returns the LAST occurrence.
find_bytes ๐Ÿ”’
Find the first byte offset where needle begins in haystack.
fold_subagent_sidecars ๐Ÿ”’
Insert each dispatchโ€™s sidecar frames under the first row its chain answers. Appending them at the end would put them at the newest edge of the resumed window, ahead of the main rows.
get_session_messages
Read the full transcript for one session.
has_a_transcript_row
Whether a transcript row carries this frame.
hook_outcome ๐Ÿ”’
The outcome the live frame reports for one persisted run record.
hook_output ๐Ÿ”’
What one persisted hook run printed, as a hook_response frame carries it.
is_hook_attachment ๐Ÿ”’
Whether an attachment row records a hook run the live stream forwards.
is_queued_attachment_row ๐Ÿ”’
Whether a transcript line is the attachment the CLI writes for a prompt queued mid-turn.
is_valid_uuid ๐Ÿ”’
True if s is a canonical 8-4-4-4-12 hyphenated UUID. The length guard rejects the hyphenless / braced / URN forms that Uuid::try_parse otherwise accepts - session ids on disk are always the hyphenated form the CLI emits.
list_sessions
List sessions. When directory is Some, scans that project dir; when None, scans every project directory under config_dirโ€™s projects/ tree. A capped listing stats each candidate for its mtime, reads newest first on the tokio blocking pool (at most 16 at once), and stops once the rows the cap serves are in hand - a short batch is filled by the next, so the cap bounds the reads without shortening the listing. An uncapped listing (the boot scan) reads every candidate. workers decides which worker-tagged rows may appear, and it lands before the cap: a listing is never shortened by rows it would not have carried.
live_hook_event ๐Ÿ”’
The hook events whose runs the live stream forwards, and therefore the only ones a read serves as rows.
messages_from_rows ๐Ÿ”’
The rows a transcriptโ€™s messages convert to, typed as the wire sends them and stamped with session_id.
missing_boundary_facts ๐Ÿ”’
Which of the facts the kept boundary frame reads the row did not yield - the trigger, and the counts either side of the cut.
owning_dispatch ๐Ÿ”’
The dispatch whose row a sidecarโ€™s frames belong under: the first dispatch in its chain the transcript answers.
parse_rfc3339_ms ๐Ÿ”’
Parse the CLIโ€™s RFC-3339 timestamp (e.g. 2026-04-22T04:15:27.123Z) into Unix epoch milliseconds. Sub-millisecond precision is truncated.
parse_session_info_from_lite ๐Ÿ”’
Build an SDKSessionInfo from a lite head/tail read. Skips sidechain transcripts and metadata-only sessions (no summary after all fallbacks).
parse_session_messages ๐Ÿ”’
project_dir_for ๐Ÿ”’
project_key_for_directory
Map a directory path to the CLIโ€™s on-disk project key. Canonicalises the path first and then applies the CLIโ€™s JS-style sanitisation hash. None defaults to "." (the processโ€™s current working directory).
queue_remove_row ๐Ÿ”’
The queue-operation remove line as the queued promptโ€™s row, or None for any other line.
queue_row_id ๐Ÿ”’
The rowโ€™s id, minted from the one line an anchored read can reproduce.
queued_prompt_text ๐Ÿ”’
The prompt words an attachment rowโ€™s synthesised message carries.
queued_start_row ๐Ÿ”’
The row a remove line draws, taking the attachment that named the press if one is waiting.
read_rows
Read the rows of session_idโ€™s transcript that sit strictly before below, newest first, up to rows_wanted of them.
read_rows_with ๐Ÿ”’
read_rows with the pageโ€™s own budget in the callerโ€™s hands, so the growth, the cap and the search bound can be driven by a test without a transcript of the size the production budget is written for.
read_session_info ๐Ÿ”’
read_session_info_at
The one transcript at path, read for its title and clock.
read_session_info_with ๐Ÿ”’
read_session_lite ๐Ÿ”’
Open a session file, stat it, read at most LITE_READ_BUF_SIZE bytes from the head and the same from the tail. For files smaller than the buffer, tail == head (single read). Returns None on any I/O error or for empty files.
read_session_messages ๐Ÿ”’
read_session_messages_main_thread ๐Ÿ”’
get_session_messages over the sessionโ€™s own rows only: no sub-agent sidecars are opened or folded. The handoff brief takes this one, so it neither pays for the sidecar parse nor carries frames it does not read. A page read does not: a dispatch row draws its sidecar frames (#1763).
resolve_anchor ๐Ÿ”’
Where a readโ€™s window ends for a named anchor: the byte its row starts at, seeking when the cursor carries a byte and searching from the fileโ€™s end when it does not.
row_at ๐Ÿ”’
The row that starts at at, for the anchored readโ€™s verify.
sanitize_path ๐Ÿ”’
Sanitise a path the same way the claude CLI does - non-alphanumerics become hyphens, and overlong paths are truncated with a base-36 hash suffix (matching JSโ€™s String.prototype.hashCode trick).
sanitize_path_public ๐Ÿ”’
Crate-internal re-export of the path sanitiser - other modules need it to derive the same on-disk project-key layout the CLI uses. Not part of the public API; downstream consumers should call project_key_for_directory instead.
scan_tag_from ๐Ÿ”’
Return the value of the LAST {"type":"tag"} rowโ€™s "tag" field. Empty strings are filtered. None when no tag row is present or any read errors out.
session_in_row ๐Ÿ”’
The session a transcript row belongs to. The wire spells it session_id; a row the CLI wrote spells it sessionId.
session_row ๐Ÿ”’
One line of a transcript as its row, or None for a line that is not one.
session_transcript ๐Ÿ”’
The file a session id reads from: its projectโ€™s directory when the caller places it, the first copy named after it otherwise.
should_exclude_worker_tag
True when info represents a worker session: one whose transcript carries a forge:worker:<label> tag.
should_skip_first_prompt ๐Ÿ”’
Fixed-prefix counterpart to the CLIโ€™s _SKIP_FIRST_PROMPT_PATTERN.
simple_hash ๐Ÿ”’
32-bit integer hash to base-36, matching the CLIโ€™s directory naming.
subagent_sidecars ๐Ÿ”’
The frames a sessionโ€™s sub-agent sidecars hold, with each sidecarโ€™s own dispatch and every metaโ€™s chain link. The links are read for all sidecars, not only the ones inside the budget; the fold stamps the rows themselves.
subagent_sidecars_with_budget ๐Ÿ”’
subagent_sidecars with the read budget given, so the cap is drivable at a size a test can build.
synthesize_hook_frame ๐Ÿ”’
One persisted hook run as the live frame the chat folds.
synthesize_queued_command_message ๐Ÿ”’
Build a {"role":"user","content":[{queued_command}]} envelope from a JSONL attachment rowโ€™s attachment field. Used during session replay to surface mid-turn queued inputs that claude persisted as type:"attachment" rows (which the scanner would otherwise skip).
tag_and_project_of ๐Ÿ”’
The last tag AND the project its row names, with the same cache and read-error behaviour as tag_of. A worker resume uses the project to tell this projectโ€™s session from another projectโ€™s in a directory both compose to.
tag_of ๐Ÿ”’
The last tag pathโ€™s transcript carries, with the read error read_session_lite turns into โ€œuntaggedโ€ handed back as one: a caller that has to tell an unreadable transcript from an untagged one cannot use the lite read.
tool_use_ids ๐Ÿ”’
The calls made in row.
transcript_row ๐Ÿ”’
One transcript row as the sessionโ€™s message, or None for a row that is not one. The flag says the row is a compaction boundary, which the read counts.
try_read_dir ๐Ÿ”’
Open dir for directory iteration. NotFound is the expected case for the catalogโ€™s projects/ tree on a fresh forge install and is silent; real I/O failures (perm denied, broken FS) log at warn so the user gets a triage signal rather than silently empty catalog reads.
unescape_json_string ๐Ÿ”’
Unescape a JSON string value. No-op when there are no backslashes.
window_ending ๐Ÿ”’
Read up to take bytes ending at end, and the byte before the window as well.
window_rows ๐Ÿ”’
The rows a window holds, each with the byte it starts at.
wire_session_frame ๐Ÿ”’
A kept row carrying the session under the spelling the live frame sends.

Type Aliasesยง

SidecarRead ๐Ÿ”’
The sidecar bodies the budget admitted, and every metaโ€™s chain link.