agent_sessions 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +71 -3
  4. data/doc/Agent/Sessions/Adapters/Amp.md +91 -0
  5. data/doc/Agent/Sessions/Adapters/Base.md +198 -0
  6. data/doc/Agent/Sessions/Adapters/Claude.md +63 -0
  7. data/doc/Agent/Sessions/Adapters/Codex.md +79 -0
  8. data/doc/Agent/Sessions/Adapters/Copilot.md +38 -0
  9. data/doc/Agent/Sessions/Adapters/Cursor.md +54 -0
  10. data/doc/Agent/Sessions/Adapters/CursorIde.md +49 -0
  11. data/doc/Agent/Sessions/Adapters/Enumeration.md +135 -0
  12. data/doc/Agent/Sessions/Adapters/Gemini.md +67 -0
  13. data/doc/Agent/Sessions/Adapters/Grok.md +64 -0
  14. data/doc/Agent/Sessions/Adapters/Opencode.md +93 -0
  15. data/doc/Agent/Sessions/Adapters/Pi.md +137 -0
  16. data/doc/Agent/Sessions/Adapters/Qwen.md +32 -0
  17. data/doc/Agent/Sessions/Adapters.md +5 -0
  18. data/doc/Agent/Sessions/Audit/Finding.md +27 -0
  19. data/doc/Agent/Sessions/Audit.md +21 -0
  20. data/doc/Agent/Sessions/CLI.md +36 -0
  21. data/doc/Agent/Sessions/Check.md +27 -0
  22. data/doc/Agent/Sessions/Compaction.md +23 -0
  23. data/doc/Agent/Sessions/EnvOverride.md +19 -0
  24. data/doc/Agent/Sessions/Error.md +6 -0
  25. data/doc/Agent/Sessions/HomeExpansion.md +11 -0
  26. data/doc/Agent/Sessions/Location.md +60 -0
  27. data/doc/Agent/Sessions/Message.md +59 -0
  28. data/doc/Agent/Sessions/MissingDependency.md +6 -0
  29. data/doc/Agent/Sessions/Node.md +24 -0
  30. data/doc/Agent/Sessions/Part.md +41 -0
  31. data/doc/Agent/Sessions/Readers/Amp.md +41 -0
  32. data/doc/Agent/Sessions/Readers/Base.md +91 -0
  33. data/doc/Agent/Sessions/Readers/Claude.md +77 -0
  34. data/doc/Agent/Sessions/Readers/Codex.md +81 -0
  35. data/doc/Agent/Sessions/Readers/Copilot.md +31 -0
  36. data/doc/Agent/Sessions/Readers/Gemini.md +38 -0
  37. data/doc/Agent/Sessions/Readers/Grok.md +45 -0
  38. data/doc/Agent/Sessions/Readers/Opencode.md +37 -0
  39. data/doc/Agent/Sessions/Readers/Pi.md +38 -0
  40. data/doc/Agent/Sessions/Readers/Qwen.md +30 -0
  41. data/doc/Agent/Sessions/Readers.md +5 -0
  42. data/doc/Agent/Sessions/Session.md +73 -0
  43. data/doc/Agent/Sessions/Sqlite.md +43 -0
  44. data/doc/Agent/Sessions/Store.md +57 -0
  45. data/doc/Agent/Sessions/UnknownAgent.md +6 -0
  46. data/doc/Agent/Sessions/UnreadableStore.md +6 -0
  47. data/doc/Agent/Sessions/UnsupportedFormat.md +6 -0
  48. data/doc/Agent/Sessions/Usage.md +56 -0
  49. data/doc/Agent/Sessions.md +177 -0
  50. data/doc/Agent.md +5 -0
  51. data/doc/CHANGELOG.md +69 -0
  52. data/doc/README.md +162 -0
  53. data/doc/index.csv +316 -0
  54. data/exe/agent-sessions +0 -2
  55. data/lib/agent/sessions/adapters/cursor.rb +3 -3
  56. data/lib/agent/sessions/cli.rb +2 -2
  57. data/lib/agent/sessions/message.rb +1 -2
  58. data/lib/agent/sessions/part.rb +1 -2
  59. data/lib/agent/sessions/version.rb +1 -1
  60. data/llm.txt +177 -0
  61. metadata +54 -2
@@ -0,0 +1,27 @@
1
+ # Class Agent::Sessions::Audit::Finding <a id="class-Agent-Sessions-Audit-Finding"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/audit.rb |
7
+
8
+ ## Attributes
9
+ ### `agent` [R] <a id="attribute-i-agent"></a> <a id="agent-instance_method"></a>
10
+ Returns the value of attribute agent
11
+ - **@return** [Object] the current value of agent
12
+
13
+ ### `bytes` [R] <a id="attribute-i-bytes"></a> <a id="bytes-instance_method"></a>
14
+ Returns the value of attribute bytes
15
+ - **@return** [Object] the current value of bytes
16
+
17
+ ### `kind` [R] <a id="attribute-i-kind"></a> <a id="kind-instance_method"></a>
18
+ Returns the value of attribute kind
19
+ - **@return** [Object] the current value of kind
20
+
21
+ ### `path` [R] <a id="attribute-i-path"></a> <a id="path-instance_method"></a>
22
+ Returns the value of attribute path
23
+ - **@return** [Object] the current value of path
24
+
25
+ ### `synced_to` [R] <a id="attribute-i-synced_to"></a> <a id="synced_to-instance_method"></a>
26
+ Returns the value of attribute synced_to
27
+ - **@return** [Object] the current value of synced_to
@@ -0,0 +1,21 @@
1
+ # Class Agent::Sessions::Audit <a id="class-Agent-Sessions-Audit"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Object |
6
+ | **Includes** | [Agent::Sessions::HomeExpansion](HomeExpansion.md) |
7
+ | **Defined in** | lib/agent/sessions/audit.rb |
8
+
9
+ Answers: are these plaintext transcripts inside anything that syncs? Needs
10
+ only Layer 1. Time Machine exclusion status is a planned addition.
11
+
12
+ ## Constants
13
+ ### `SYNC_ROOTS` <a id="constant-SYNC_ROOTS"></a> <a id="SYNC_ROOTS-constant"></a>
14
+ Not documented.
15
+
16
+ ## Public Instance Methods
17
+ ### `initialize(stores, env: ENV)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
18
+ - **@return** [Audit] a new instance of Audit
19
+
20
+ ### `report()` <a id="method-i-report"></a> <a id="report-instance_method"></a>
21
+ Not documented.
@@ -0,0 +1,36 @@
1
+ # Class Agent::Sessions::CLI <a id="class-Agent-Sessions-CLI"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Object |
6
+ | **Defined in** | lib/agent/sessions/cli.rb |
7
+
8
+ ## Constants
9
+ ### `GROUP_COLUMN_MAX` <a id="constant-GROUP_COLUMN_MAX"></a> <a id="GROUP_COLUMN_MAX-constant"></a>
10
+ Group names in `du --by project` are the same shape of problem one cap wider:
11
+ a real project path on this machine ran 169 characters, wrapping every row
12
+ across three lines on an 80-column terminal (list's own id column tops out at
13
+ 72 total). Wider than ID_COLUMN_MAX because a path's head (which user, which
14
+ drive) and tail (the actual project directory) are both worth keeping, and
15
+ both need more room than a bare uuid does.
16
+
17
+ ### `ID_COLUMN_MAX` <a id="constant-ID_COLUMN_MAX"></a> <a id="ID_COLUMN_MAX-constant"></a>
18
+ Cap the id column. Cursor's ids are two nested uuids joined by "/" (36 + 1 +
19
+ 36 = 73 chars) where every other agent needs a bare uuid (36) or less, so one
20
+ Cursor row makes the global id_width 73 — padding every other row with ~35
21
+ spaces and pushing the line past 100 chars, which wraps on an 80-column
22
+ terminal. Elide the middle and keep both ends, since the ends are what a human
23
+ matches against a directory name.
24
+
25
+ ### `SINCE_UNITS` <a id="constant-SINCE_UNITS"></a> <a id="SINCE_UNITS-constant"></a>
26
+ Not documented.
27
+
28
+ ### `STATUS_MARKS` <a id="constant-STATUS_MARKS"></a> <a id="STATUS_MARKS-constant"></a>
29
+ Not documented.
30
+
31
+ ## Public Instance Methods
32
+ ### `initialize(argv, env: ENV, stdout: $stdout, stderr: $stderr, now: Time.now)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
33
+ - **@return** [CLI] a new instance of CLI
34
+
35
+ ### `run()` <a id="method-i-run"></a> <a id="run-instance_method"></a>
36
+ Not documented.
@@ -0,0 +1,27 @@
1
+ # Class Agent::Sessions::Check <a id="class-Agent-Sessions-Check"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/check.rb |
7
+
8
+ ## Attributes
9
+ ### `agent` [R] <a id="attribute-i-agent"></a> <a id="agent-instance_method"></a>
10
+ Returns the value of attribute agent
11
+ - **@return** [Object] the current value of agent
12
+
13
+ ### `claim` [R] <a id="attribute-i-claim"></a> <a id="claim-instance_method"></a>
14
+ Returns the value of attribute claim
15
+ - **@return** [Object] the current value of claim
16
+
17
+ ### `detail` [R] <a id="attribute-i-detail"></a> <a id="detail-instance_method"></a>
18
+ Returns the value of attribute detail
19
+ - **@return** [Object] the current value of detail
20
+
21
+ ### `status` [R] <a id="attribute-i-status"></a> <a id="status-instance_method"></a>
22
+ Returns the value of attribute status
23
+ - **@return** [Object] the current value of status
24
+
25
+ ## Public Instance Methods
26
+ ### `pass?()` <a id="method-i-pass-3F"></a> <a id="pass?-instance_method"></a>
27
+ - **@return** [Boolean]
@@ -0,0 +1,23 @@
1
+ # Class Agent::Sessions::Compaction <a id="class-Agent-Sessions-Compaction"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/compaction.rb |
7
+
8
+ A point where the agent replaced earlier turns with a summary. Not a message:
9
+ its own payload restates turns already yielded, so anyone counting would count
10
+ them twice. replaced_count is how many turns it stood in for.
11
+
12
+ ## Attributes
13
+ ### `at` [R] <a id="attribute-i-at"></a> <a id="at-instance_method"></a>
14
+ Returns the value of attribute at
15
+ - **@return** [Object] the current value of at
16
+
17
+ ### `raw` [R] <a id="attribute-i-raw"></a> <a id="raw-instance_method"></a>
18
+ Returns the value of attribute raw
19
+ - **@return** [Object] the current value of raw
20
+
21
+ ### `replaced_count` [R] <a id="attribute-i-replaced_count"></a> <a id="replaced_count-instance_method"></a>
22
+ Returns the value of attribute replaced_count
23
+ - **@return** [Object] the current value of replaced_count
@@ -0,0 +1,19 @@
1
+ # Class Agent::Sessions::EnvOverride <a id="class-Agent-Sessions-EnvOverride"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/env_override.rb |
7
+
8
+ ## Attributes
9
+ ### `name` [R] <a id="attribute-i-name"></a> <a id="name-instance_method"></a>
10
+ Returns the value of attribute name
11
+ - **@return** [Object] the current value of name
12
+
13
+ ### `value` [R] <a id="attribute-i-value"></a> <a id="value-instance_method"></a>
14
+ Returns the value of attribute value
15
+ - **@return** [Object] the current value of value
16
+
17
+ ## Public Instance Methods
18
+ ### `active?()` <a id="method-i-active-3F"></a> <a id="active?-instance_method"></a>
19
+ - **@return** [Boolean]
@@ -0,0 +1,6 @@
1
+ # Class Agent::Sessions::Error <a id="class-Agent-Sessions-Error"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | StandardError |
6
+ | **Defined in** | lib/agent/sessions/error.rb |
@@ -0,0 +1,11 @@
1
+ # Module Agent::Sessions::HomeExpansion <a id="module-Agent-Sessions-HomeExpansion"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Defined in** | lib/agent/sessions/home_expansion.rb |
6
+
7
+ Shared path expansion for Adapters::Base and Audit. Expands "~" against the
8
+ injected env so callers can resolve paths for a machine that is not their own;
9
+ joins relative paths (including "~user"-looking strings that are not a real
10
+ shell lookup here) under that same home; and treats an explicitly empty HOME
11
+ the same as an absent one.
@@ -0,0 +1,60 @@
1
+ # Class Agent::Sessions::Location <a id="class-Agent-Sessions-Location"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/location.rb |
7
+
8
+ One resolved layer of an agent's store.
9
+
10
+ A location is one of three shapes, and `files` answers each differently:
11
+
12
+ glob a directory plus a pattern -> the pattern's matches
13
+ single_file one file (a `path:` store) -> itself, if it is there
14
+ directory a directory with no known shape -> nothing, and enumerable? is false
15
+
16
+ The third shape is a store whose internal layout this gem has not learned yet
17
+ (opencode's pre-1.2.0 storage/ tree, Cursor's acp-sessions/). It returns [] so
18
+ a caller sweeping every layer does not blow up, and answers enumerable? false
19
+ so that caller can tell "nothing here to enumerate" apart from "enumerated,
20
+ found none".
21
+
22
+ single_file comes from the adapter's store DSL: <code>path:</code> means one
23
+ file, <code>dir:</code> means a directory. Resolution used to discard that
24
+ distinction, which made a Layer 2 enumerator written as
25
+ layers.flat_map(&:files) silently skip history.jsonl, session_index.jsonl and
26
+ secrets.json — a missing-session bug, not a visible error.
27
+
28
+ ## Attributes
29
+ ### `format` [R] <a id="attribute-i-format"></a> <a id="format-instance_method"></a>
30
+ Returns the value of attribute format
31
+ - **@return** [Object] the current value of format
32
+
33
+ ### `glob` [R] <a id="attribute-i-glob"></a> <a id="glob-instance_method"></a>
34
+ Returns the value of attribute glob
35
+ - **@return** [Object] the current value of glob
36
+
37
+ ### `kind` [R] <a id="attribute-i-kind"></a> <a id="kind-instance_method"></a>
38
+ Returns the value of attribute kind
39
+ - **@return** [Object] the current value of kind
40
+
41
+ ### `path` [R] <a id="attribute-i-path"></a> <a id="path-instance_method"></a>
42
+ Returns the value of attribute path
43
+ - **@return** [Object] the current value of path
44
+
45
+ ### `single_file` [R] <a id="attribute-i-single_file"></a> <a id="single_file-instance_method"></a>
46
+ Returns the value of attribute single_file
47
+ - **@return** [Object] the current value of single_file
48
+
49
+ ## Public Instance Methods
50
+ ### `enumerable?()` <a id="method-i-enumerable-3F"></a> <a id="enumerable?-instance_method"></a>
51
+ - **@return** [Boolean]
52
+
53
+ ### `exists?()` <a id="method-i-exists-3F"></a> <a id="exists?-instance_method"></a>
54
+ - **@return** [Boolean]
55
+
56
+ ### `files()` <a id="method-i-files"></a> <a id="files-instance_method"></a>
57
+ Not documented.
58
+
59
+ ### `initialize(kind:, path:, format:, glob: nil, single_file: false)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
60
+ - **@return** [Location] a new instance of Location
@@ -0,0 +1,59 @@
1
+ # Class Agent::Sessions::Message <a id="class-Agent-Sessions-Message"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/message.rb |
7
+
8
+ One turn. `role` is normalized to :user, :assistant, :system or :tool, with
9
+ :unknown for a role the adapter did not recognize — the four the spec names
10
+ were written before any real corpus was read, and Codex promptly said
11
+ "developer".
12
+
13
+ raw is never dropped (Layer 3 rule 1): when this normalization is wrong or
14
+ incomplete, a caller escapes the abstraction instead of forking the gem.
15
+
16
+ usage and model are nil wherever the format does not put them on the message
17
+ itself — Codex records tokens in separate event records and the model in its
18
+ session header, so its messages carry neither; the reader's session-level
19
+ `usage` is where those formats answer. A nil here means "not recorded on this
20
+ message", never "zero tokens".
21
+
22
+ ## Constants
23
+ ### `ROLES` <a id="constant-ROLES"></a> <a id="ROLES-constant"></a>
24
+ Not documented.
25
+
26
+ ## Attributes
27
+ ### `at` [R] <a id="attribute-i-at"></a> <a id="at-instance_method"></a>
28
+ Returns the value of attribute at
29
+ - **@return** [Object] the current value of at
30
+
31
+ ### `model` [R] <a id="attribute-i-model"></a> <a id="model-instance_method"></a>
32
+ Returns the value of attribute model
33
+ - **@return** [Object] the current value of model
34
+
35
+ ### `parts` [R] <a id="attribute-i-parts"></a> <a id="parts-instance_method"></a>
36
+ Returns the value of attribute parts
37
+ - **@return** [Object] the current value of parts
38
+
39
+ ### `raw` [R] <a id="attribute-i-raw"></a> <a id="raw-instance_method"></a>
40
+ Returns the value of attribute raw
41
+ - **@return** [Object] the current value of raw
42
+
43
+ ### `role` [R] <a id="attribute-i-role"></a> <a id="role-instance_method"></a>
44
+ Returns the value of attribute role
45
+ - **@return** [Object] the current value of role
46
+
47
+ ### `usage` [R] <a id="attribute-i-usage"></a> <a id="usage-instance_method"></a>
48
+ Returns the value of attribute usage
49
+ - **@return** [Object] the current value of usage
50
+
51
+ ## Public Instance Methods
52
+ ### `initialize(role:, at:, parts:, raw:, usage: nil, model: nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
53
+ - **@raise** [ArgumentError]
54
+ - **@return** [Message] a new instance of Message
55
+
56
+ ### `text()` <a id="method-i-text"></a> <a id="text-instance_method"></a>
57
+ Concatenated :text parts, as the design doc specifies — no separator inserted,
58
+ because a separator is a formatting decision this layer has no business
59
+ making. A caller that needs the boundaries has `parts`.
@@ -0,0 +1,6 @@
1
+ # Class Agent::Sessions::MissingDependency <a id="class-Agent-Sessions-MissingDependency"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | [Agent::Sessions::Error](Error.md) |
6
+ | **Defined in** | lib/agent/sessions/missing_dependency.rb |
@@ -0,0 +1,24 @@
1
+ # Class Agent::Sessions::Node <a id="class-Agent-Sessions-Node"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/node.rb |
7
+
8
+ One message in a branching conversation, with the messages that follow it.
9
+ Agents that let a turn be edited and re-run record two children under one
10
+ parent: 380 such branch points sit across 85 of 151 real Claude transcripts,
11
+ so a caller reading `messages` in file order is reading two alternative
12
+ histories interleaved without being told.
13
+
14
+ children is a plain Array and the Node is frozen, so the shape is settled
15
+ before anyone sees it.
16
+
17
+ ## Attributes
18
+ ### `children` [R] <a id="attribute-i-children"></a> <a id="children-instance_method"></a>
19
+ Returns the value of attribute children
20
+ - **@return** [Object] the current value of children
21
+
22
+ ### `message` [R] <a id="attribute-i-message"></a> <a id="message-instance_method"></a>
23
+ Returns the value of attribute message
24
+ - **@return** [Object] the current value of message
@@ -0,0 +1,41 @@
1
+ # Class Agent::Sessions::Part <a id="class-Agent-Sessions-Part"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Data |
6
+ | **Defined in** | lib/agent/sessions/part.rb |
7
+
8
+ One piece of a message. `type` is the normalized vocabulary (design doc §5);
9
+ everything an agent said that this gem could not classify arrives as :unknown
10
+ rather than as an exception, and the message's `raw` still holds it.
11
+
12
+ text carries the readable content for :text, :thinking and :tool_result. name
13
+ and call_id are tool plumbing, nil elsewhere. An :image part has neither — its
14
+ URL or payload stays in raw, because normalizing an image would mean deciding
15
+ whether to load it, and reading is stat-cheap by design.
16
+
17
+ ## Constants
18
+ ### `TYPES` <a id="constant-TYPES"></a> <a id="TYPES-constant"></a>
19
+ Not documented.
20
+
21
+ ## Attributes
22
+ ### `call_id` [R] <a id="attribute-i-call_id"></a> <a id="call_id-instance_method"></a>
23
+ Returns the value of attribute call_id
24
+ - **@return** [Object] the current value of call_id
25
+
26
+ ### `name` [R] <a id="attribute-i-name"></a> <a id="name-instance_method"></a>
27
+ Returns the value of attribute name
28
+ - **@return** [Object] the current value of name
29
+
30
+ ### `text` [R] <a id="attribute-i-text"></a> <a id="text-instance_method"></a>
31
+ Returns the value of attribute text
32
+ - **@return** [Object] the current value of text
33
+
34
+ ### `type` [R] <a id="attribute-i-type"></a> <a id="type-instance_method"></a>
35
+ Returns the value of attribute type
36
+ - **@return** [Object] the current value of type
37
+
38
+ ## Public Instance Methods
39
+ ### `initialize(type:, text: nil, name: nil, call_id: nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
40
+ - **@raise** [ArgumentError]
41
+ - **@return** [Part] a new instance of Part
@@ -0,0 +1,41 @@
1
+ # Class Agent::Sessions::Readers::Amp <a id="class-Agent-Sessions-Readers-Amp"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | [Agent::Sessions::Readers::Base](Base.md) |
6
+ | **Defined in** | lib/agent/sessions/readers/amp.rb |
7
+
8
+ Amp threads. Written against the one real thread available (2026-08-14): 24
9
+ messages, content parts tool_use 14, tool_result 14, text 2, thinking 1. One
10
+ thread is thin evidence beside Codex's 415 files, and this reader says so
11
+ through partial? rather than pretending otherwise.
12
+
13
+ Two things make Amp unlike the JSONL readers:
14
+
15
+ A thread is ONE JSON document, so it cannot be streamed a record at a time.
16
+ Reading any of it means holding all of it, which is the gem's one unbounded
17
+ read (0.2 follow-up 8). each_record below is where that bound finally lives.
18
+
19
+ And its tool results are spelled its own way — toolUseID rather than
20
+ tool_use_id, the payload under run.result rather than content. A mapper copied
21
+ from Claude's would produce empty tool results and no warning, which is why
22
+ each reader maps its own agent rather than sharing one.
23
+
24
+ ## Constants
25
+ ### `CONTENT_PARTS` <a id="constant-CONTENT_PARTS"></a> <a id="CONTENT_PARTS-constant"></a>
26
+ Not documented.
27
+
28
+ ### `MAX_DOCUMENT_BYTES` <a id="constant-MAX_DOCUMENT_BYTES"></a> <a id="MAX_DOCUMENT_BYTES-constant"></a>
29
+ 150x the observed thread. A cap has to exist because nothing about a thread
30
+ file announces its size before it is opened, and JSON.parse of a 200 MB
31
+ document costs several times that in live objects. Refused and reported beats
32
+ NoMemoryError, and beats silence either way.
33
+
34
+ ### `ROLES` <a id="constant-ROLES"></a> <a id="ROLES-constant"></a>
35
+ Not documented.
36
+
37
+ ## Public Instance Methods
38
+ ### `partial?()` <a id="method-i-partial-3F"></a> <a id="partial?-instance_method"></a>
39
+ The server holds the canonical copy; a local thread may be a mirror of part of
40
+ the conversation. The adapter carries the same warning.
41
+ - **@return** [Boolean]
@@ -0,0 +1,91 @@
1
+ # Class Agent::Sessions::Readers::Base <a id="class-Agent-Sessions-Readers-Base"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Object |
6
+ | **Defined in** | lib/agent/sessions/readers/base.rb |
7
+
8
+ Layer 3: turning one session file into messages. Subclasses supply the
9
+ mapping; everything about *how the file is read* lives here, because the three
10
+ rules that make this layer survivable (design doc §5) are properties of the
11
+ reading, not of any one agent's format:
12
+
13
+ 1. raw is never dropped.
14
+ 2. Unknown records become :unknown parts and warnings, never exceptions.
15
+ 3. Reading streams. No code path may assume a file fits in memory.
16
+
17
+ Rule 3 is why this does not use File.foreach without a chunk size. A truncated
18
+ log can hold no newline at all, and "read one line" would then mean "read 2.6
19
+ GB into a String" — the file the article that started this gem found on a real
20
+ machine.
21
+
22
+ ## Constants
23
+ ### `MAX_RECORD_BYTES` <a id="constant-MAX_RECORD_BYTES"></a> <a id="MAX_RECORD_BYTES-constant"></a>
24
+ Chunk size, and so the largest record that can be read whole. Measured against
25
+ 415 real Codex rollout files (128,987 records, 2026-08-12): 14 records exceed
26
+ 1 MB and the largest is 2.41 MB, so Layer 2's MAX_LINE_BYTES of 1 MB would
27
+ silently drop real messages. 8 MB is ~3.3x the observed maximum. A record
28
+ beyond it is reported, never dropped in silence, because a missing message is
29
+ this gem's worst failure mode.
30
+
31
+ ## Attributes
32
+ ### `session` [R] <a id="attribute-i-session"></a> <a id="session-instance_method"></a>
33
+ Returns the value of attribute session.
34
+
35
+ ## Public Instance Methods
36
+ ### `branching?()` <a id="method-i-branching-3F"></a> <a id="branching?-instance_method"></a>
37
+ Whether this agent records which turn each turn followed. False here: most
38
+ stores are an append-only list and a tree would have to be invented.
39
+ - **@return** [Boolean]
40
+
41
+ ### `compactions()` <a id="method-i-compactions"></a> <a id="compactions-instance_method"></a>
42
+ Boundaries where the agent replaced earlier turns with a summary. Its own
43
+ pass: a caller asking only for compactions should not have to materialize
44
+ every message to get them.
45
+
46
+ ### `each_message()` <a id="method-i-each_message"></a> <a id="each_message-instance_method"></a>
47
+ Streams. Yields each message as it is parsed; a caller that breaks after one
48
+ has read one record, not the file.
49
+
50
+ ### `fidelity()` <a id="method-i-fidelity"></a> <a id="fidelity-instance_method"></a>
51
+ Not documented.
52
+
53
+ ### `initialize(session, include_events: false)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
54
+ - **@return** [Base] a new instance of Base
55
+
56
+ ### `messages()` <a id="method-i-messages"></a> <a id="messages-instance_method"></a>
57
+ Eager, for sessions small enough to hold. The design doc offers both and names
58
+ this the convenience: `messages` is what a script wants, and `each_message` is
59
+ what a 2.6 GB file requires.
60
+
61
+ ### `partial?()` <a id="method-i-partial-3F"></a> <a id="partial?-instance_method"></a>
62
+ True where the local file is not the whole story — Amp, whose server holds the
63
+ canonical copy. Overridden there, false everywhere else.
64
+ - **@return** [Boolean]
65
+
66
+ ### `tree()` <a id="method-i-tree"></a> <a id="tree-instance_method"></a>
67
+ The conversation as roots and their continuations, for an agent that records
68
+ parent links. Unlike every other method here this cannot stream — a tree is
69
+ not knowable until the last record is read — so it holds one session's
70
+ messages at once and says so rather than pretending otherwise.
71
+
72
+ Raises rather than returning an empty list or nil for a store with no parent
73
+ links, for the reason Agent::Sessions.read raises: "this format does not
74
+ record that" must never read as "this session has none".
75
+
76
+ ### `usage()` <a id="method-i-usage"></a> <a id="usage-instance_method"></a>
77
+ This session's token totals as a Usage, or nil where the format does not
78
+ record them (Amp) or this reader has not learned where they live. nil, not an
79
+ empty Usage: "this store does not say" must never read as "this session cost
80
+ nothing" — the same rule tree() enforces by raising.
81
+
82
+ Each reader that overrides this also decides its own summation rule, because
83
+ that rule is format knowledge: Claude repeats one API response's usage across
84
+ several records (94 of 124 message ids in one real transcript), Codex writes a
85
+ running total where only the last record counts. A base-class sum would get
86
+ both wrong.
87
+
88
+ ### `warnings()` <a id="method-i-warnings"></a> <a id="warnings-instance_method"></a>
89
+ Populated as records are read, so this answers for whatever has been consumed
90
+ so far. uniq because a second pass over the same file would otherwise repeat
91
+ every warning it already reported.
@@ -0,0 +1,77 @@
1
+ # Class Agent::Sessions::Readers::Claude <a id="class-Agent-Sessions-Readers-Claude"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | [Agent::Sessions::Readers::Base](Base.md) |
6
+ | **Defined in** | lib/agent/sessions/readers/claude.rb |
7
+
8
+ Claude Code transcripts. Written against 142 real transcripts, 29,688 records,
9
+ inventoried 2026-08-12.
10
+
11
+ The content vocabulary is a straight match for this gem's: text, thinking,
12
+ tool_use, tool_result and image are exactly the five part types the design doc
13
+ names, so nothing here has to invent a mapping. What Claude adds is everything
14
+ **around** the conversation — a third of all records are session state, and
15
+ two more kinds carry context the model saw without being a turn anyone took.
16
+
17
+ ## Constants
18
+ ### `CONTENT_PARTS` <a id="constant-CONTENT_PARTS"></a> <a id="CONTENT_PARTS-constant"></a>
19
+ Not documented.
20
+
21
+ ### `EVENT_TYPES` <a id="constant-EVENT_TYPES"></a> <a id="EVENT_TYPES-constant"></a>
22
+ Context the model saw, but not a turn: `system` is turn_duration,
23
+ stop_hook_summary, away_summary, local_command; `attachment` is hook output,
24
+ skill listings, task reminders, pasted files. Same judgement Codex's event_msg
25
+ gets — available on request, never on by default.
26
+
27
+ ### `MAX_SPILL_BYTES` <a id="constant-MAX_SPILL_BYTES"></a> <a id="MAX_SPILL_BYTES-constant"></a>
28
+ A spilled file is read whole. The largest observed is well under this; the cap
29
+ exists because the pointer says nothing about the size.
30
+
31
+ ### `NON_MESSAGE_TYPES` <a id="constant-NON_MESSAGE_TYPES"></a> <a id="NON_MESSAGE_TYPES-constant"></a>
32
+ State, not conversation, and together 11,000+ of the records written. Skipped
33
+ in silence: warning about a record deliberately classified would teach a
34
+ caller that warnings are noise.
35
+
36
+ atis-latch and bridge-session postdate the corpus above — found by running
37
+ this reader over a live 2026-08-24 transcript and reading its own warnings (23
38
+ and 17 records), the same way Codex's tool list grew. Both are session
39
+ plumbing: a latch marker, and the record tying a local transcript to its cloud
40
+ session id (bridgeSessionId, owner uuids). Neither is a turn anyone took.
41
+
42
+ ### `SPILL` <a id="constant-SPILL"></a> <a id="SPILL-constant"></a>
43
+ How Claude Code points at output too large to inline. It is prose, not a
44
+ structured field — 24 real tool_result parts and 149 attachments carry this
45
+ sentence — so the path has to be matched out of the text.
46
+
47
+ ## Public Instance Methods
48
+ ### `branching?()` <a id="method-i-branching-3F"></a> <a id="branching?-instance_method"></a>
49
+ Every uuid-bearing record names the record it followed, and 380 branch points
50
+ sit across 85 of 151 real transcripts — a turn edited and re-run leaves two
51
+ children under one parent. Exactly one root per file and no orphaned parent
52
+ link was found in that corpus, so the links are trustworthy enough to build a
53
+ tree from.
54
+ - **@return** [Boolean]
55
+
56
+ ### `initialize(session, resolve_spills: true, **rest)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
57
+ - **@return** [Claude] a new instance of Claude
58
+
59
+ ### `subagents()` <a id="method-i-subagents"></a> <a id="subagents-instance_method"></a>
60
+ The transcripts of agents this session spawned, as readers of their own.
61
+ Exposed rather than inlined, per design doc 8.1: a subagent's turns are not
62
+ the parent's turns, and merging them would break every count taken from this
63
+ reader. 124 of these sit beside real sessions on this machine.
64
+
65
+ isSidechain is false on all 22,072 records in the main transcripts, so there
66
+ is nothing to filter out there — the separation is already how Claude Code
67
+ writes them.
68
+
69
+ ### `usage()` <a id="method-i-usage"></a> <a id="usage-instance_method"></a>
70
+ Session totals, summed over assistant records but deduplicated by message.id
71
+ first — and the dedup is most of the number. One API response streams into one
72
+ record PER CONTENT BLOCK, each carrying the same message.id and the same
73
+ usage: in one real transcript on this machine (2026-08-24), 260 assistant
74
+ records share 124 message ids, 94 of which repeat with byte-identical usage. A
75
+ naive sum reports roughly double what Anthropic billed. An id-less record (not
76
+ observed, but rule 2 says formats drift) is counted rather than dropped:
77
+ overcounting a novelty beats silently ignoring it.