Class: Agent::Sessions::Adapters::Grok
- Defined in:
- lib/agent/sessions/adapters/grok.rb
Overview
Grok Build (xAI). PROVISIONAL: ~/.grok does not exist on the machine this was written on (2026-08-24), so every claim follows tokentelemetry's working parser of the same store (resources/tokentelemetry, _scan_grok_sessions and _grok_usage_from_unified_log) rather than observation.
A Grok session is a DIRECTORY, not a file:
~/.grok/sessions/<url-encoded cwd>/<session-uuid>/
summary.json chat_history.jsonl events.jsonl updates.jsonl
signals.json plan_mode.json subagents/<spawn-id>/meta.json
summary.json is what this adapter enumerates, because it is the record
that always exists and carries the session's own metadata. The
transcript beside it is what the reader reads, and bytes counts the
whole directory — the same choice Claude's adapter makes for its
sidecar tree, and for the same reason: those bytes belong to this
session, and a du that ignored them would disagree with the disk.
Constant Summary
Constants inherited from Base
Constants included from Enumeration
Class Method Summary collapse
Instance Method Summary collapse
-
#bytes_for(path, stat) ⇒ Object
The whole session directory, not just summary.json: the transcript and every sibling log live in it.
- #decode_project(name) ⇒ Object
- #encode_project(dir) ⇒ Object
- #project_dir_name(path) ⇒ Object
-
#project_path_for(path) ⇒ Object
The project bucket is a URL-encoded absolute path (tokentelemetry unquotes it), so unlike Claude's and pi's dash encodings this one is losslessly reversible.
-
#session_id_from(path) ⇒ Object
/ / /summary.json — the id is the directory holding the file, not the file's own basename, which is the constant "summary". -
#started_at_for(path, stat) ⇒ Object
summary.json carries the session's own clock; the file's mtime is only ever a proxy for it.
- #updated_at_for(path, stat) ⇒ Object
- #warnings ⇒ Object
Methods inherited from Base
#base_dir, fidelity_value, homedir_config, #initialize, #locate, #retention, #retention_source, store_configs, #verify
Methods included from Enumeration
#project_paths, #sessions, #sessions_for_project
Constructor Details
This class inherits a constructor from Agent::Sessions::Adapters::Base
Class Method Details
Instance Method Details
#bytes_for(path, stat) ⇒ Object
The whole session directory, not just summary.json: the transcript and every sibling log live in it.
101 102 103 104 105 106 107 108 |
# File 'lib/agent/sessions/adapters/grok.rb', line 101 def bytes_for(path, stat) dir = File.dirname(path) Dir.glob(File.join(escape_glob(dir), "**", "*"), File::FNM_DOTMATCH).sum do |entry| File.file?(entry) ? File.size(entry) : 0 rescue SystemCallError 0 end end |
#decode_project(name) ⇒ Object
81 82 83 84 85 |
# File 'lib/agent/sessions/adapters/grok.rb', line 81 def decode_project(name) URI.decode_www_form_component(name) rescue ArgumentError # a name that is not valid percent-encoding name end |
#encode_project(dir) ⇒ Object
74 75 76 77 78 79 |
# File 'lib/agent/sessions/adapters/grok.rb', line 74 def encode_project(dir) # Percent-encode everything a path separator is not, matching what # URL-encoding a whole path produces. CGI.escape is deliberately not # used: it encodes a space as "+", which decodes back to "+" here. URI.encode_www_form_component(dir).gsub("+", "%20") end |
#project_dir_name(path) ⇒ Object
70 71 72 |
# File 'lib/agent/sessions/adapters/grok.rb', line 70 def project_dir_name(path) File.basename(File.dirname(File.dirname(path))) end |
#project_path_for(path) ⇒ Object
The project bucket is a URL-encoded absolute path (tokentelemetry unquotes it), so unlike Claude's and pi's dash encodings this one is losslessly reversible. summary.json's own info.cwd is preferred where readable, because a recorded path beats a decoded directory name; the decode is the fallback, and a good one.
63 64 65 66 67 68 |
# File 'lib/agent/sessions/adapters/grok.rb', line 63 def project_path_for(path) recorded = read_json(path).dig("info", "cwd") return recorded if recorded.is_a?(String) decode_project(File.basename(File.dirname(File.dirname(path)))) end |
#session_id_from(path) ⇒ Object
54 55 56 |
# File 'lib/agent/sessions/adapters/grok.rb', line 54 def session_id_from(path) File.basename(File.dirname(path)) end |
#started_at_for(path, stat) ⇒ Object
summary.json carries the session's own clock; the file's mtime is only ever a proxy for it. Both are ISO 8601 strings per the reference parser, with created_at standing in when updated_at is absent.
90 91 92 |
# File 'lib/agent/sessions/adapters/grok.rb', line 90 def started_at_for(path, stat) parse_time(read_json(path)["created_at"]) || super end |
#updated_at_for(path, stat) ⇒ Object
94 95 96 97 |
# File 'lib/agent/sessions/adapters/grok.rb', line 94 def updated_at_for(path, stat) summary = read_json(path) parse_time(summary["updated_at"]) || parse_time(summary["created_at"]) || super end |
#warnings ⇒ Object
41 42 43 44 45 46 47 48 49 |
# File 'lib/agent/sessions/adapters/grok.rb', line 41 def warnings list = super if primary_layer.exists? list << "Grok's store shape is unverified — no ~/.grok existed on the machine this " \ "adapter was written on, so it follows tokentelemetry's parser of the same " \ "format. Please open an issue if sessions, projects or usage look wrong." end list end |