Changelog¶
All notable changes to the analyzer are recorded here. The version is
printed by --version. Changes to the timeline.jsonl and
sessions.jsonl fields (timeline.csv and sessions.csv columns up to
0.7.0) are called out, since they are interfaces.
The format follows Keep a Changelog.
Unreleased¶
0.22.0 - 2026-10-30¶
Added¶
- The bundled catalog copy gains the catalog lines of collector 1.20.0, so detection follows them: a Hermes home or desktop user-data directory with any
HERMES_DATA_DIR_SUFFIX(.hermes?*,AppData/Local/hermes?*andHermes?*under each OS's application data directory, replacing.hermes_*andAppData/Local/hermes_*) is detected ashermes(#324); and inAppData/Roaming/devinonly the Devin CLI'smcp_config.jsonandcredentials.tomlare attributed towindsurf, not the Devin IDE's files beside them (#321). - The bundled catalog copy gains the collector's new Hermes secret globs; the secret globs change as in the collector's Unreleased entry (#326).
Fixed¶
- The
hermesparser reads a home withHERMES_DATA_DIR_SUFFIXappended with no separator (.hermes<suffix>andAppData/Local/hermes<suffix>, such as.hermes-dev, as Hermes writes it) and the desktop app's suffixedHermes<suffix>/connections.json; it read only.hermes_<suffix>, so such a home was detected but not parsed (#324).
0.21.0 - 2026-10-28¶
Added¶
- The bundled catalog copy gains the Claude Code macOS lines of collector 1.19.0 (the
claude-cliURL handler bundle under~/Applications, the relocated-homeide/*.lockandbridge-spawn/*/mcp.jsonentries, and the GitHub CLIhosts.ymllines under thesharedagent), so detection follows them (#256). - The bundled catalog copy has the collector's Claude Code lines from the
Windows round: the Windows temp
root
AppData/Local/Temp/claudeand the project's.git/info/excludeare detected,.claude/paste-cacheis no longer excluded,.claude/plugins/plugin-directory-cache*is, and the session daemon's keys and the.claude.jsonbackups are secrets, in.claudeand, with the session keys, in a~/.claude-<name>config home (#256). - The
claude-codeparser yieldssession synced: remote control bridge <bridgeSessionId> url - (from <path>, file mtime)from a.claude/sessions/<pid>.jsonregistry file that names a bridge id, at the file's modification time, so a session whose transcript is gone but whose registry file shows Remote Control attached readssyncedyesinsessions.jsonlrather thanno(#256). - The URL of a
session synced: remote control bridgerow is thebridge_statusrecord that belongs to that bridge, the first at or after thebridge-sessionrecord up to the next bridge or detach, else the nearest before it back to the previous one, else-; a second bridge in one transcript took the first bridge's URL before (#256). - The bundled catalog copy has the collector's seven new project lines,
.git/HEAD,.git/config,.git/FETCH_HEAD,.git/packed-refs,.git/refs,.git/logsand.git/worktrees, and its exclusion of each linked worktree's staging area andmodules/, so the analyzer attributes a collected project's repository files toproject, the shared project files, anddetectcounts them under(shared)(#253). detect --jsongainsremotesandheadas the last two keys of eachprojects[]object, new keys of that interface: the remotes of the project's.git/config(name, andurlsandpushurls, everyurlandpushurlvalue as the file records them, in file order, so a second push destination is kept; sorted by name) and the branch its.git/HEADnames, or the commit of a detached HEAD;[]and""when the collection holds neither file, and also when one of them cannot be read, which is then aproblem:line. Thedetecttext output adds aremotes:line under each project that has any (#253).inventory.jsongainsremotes,headandrecorded_remotesas the last three keys of eachprojects.<path>andscratch_directories.<path>entry, new keys of that interface:remotesandheadas indetect --json([]and""for areferencedproject), andrecorded_remotes, the sorted distinct URLs of thegit remote:rows of the sessions counted under the project after the filters, so a project whose directory is gone still names the remote its agent recorded.outside_projectshas none of the three (#253).git remote: <url>systemrows, once per session per URL at the time of the record that names it: thecodex-cliandopen-interpreterparsers read the rollout'ssession_metagit.repository_urland thestate_5.sqlitethreads.git_origin_url(a secondary source, so the timeline keeps one of the two); thezedparser reads the thread snapshot'sgit_state.remote_url; theopenclawparser reads theprojectstable'sorigin_urlofstate/openclaw.sqlite, with the project path itsrepo_rootand no session (#253).- The
claude-codeparser writessession surface: <entrypoint>at the first record of each transcript file (cli,claude-vscodefor the VS Code extension,claude-desktop), andsession surface: <old> -> <new>when it changes (#256). - The
claude-codeparser writesprompt trigger: promptSource=<v> turnOrigin=<v>after a prompt that was not typed by the person at the terminal: one sent from a Remote Control client (queued), from the VS Code extension (sdk), a suggestion the person accepted (suggestion_accepted), a/loopfiring (turnOrigin=scheduled) or a background task's completion (#256). - The
claude-codeparser writesscratch directory: <path> (session scratchpad, from the environment attachment)for the session scratchpad a transcript'senvironmentattachment names, soinventory.jsonlists it underscratch_directories(#256). - The
claude-codeparser writessession continued in: <session id>for a 2.1.309continued-inrecord, the session daemon's handover of a console session to the worker session that holds the later turns, andremote control: bridge session <id or none> lastSequenceNum=<n>for a Remote Controlbridge-sessionrecord (#256). - The
claude-codeparser writesretention setting: cleanupPeriodDays=<n> (from <file>, file mtime)from a settings file, after itspermission setting:rows, because the period decides which transcripts Claude Code's own retention sweep has deleted; a session known only fromhistory.jsonlis what a swept session leaves (#256). - The
claude-codeparser writessource host: github status=<status> (from .claude.json, file mtime)from thegithubWebConnectionStatusCachethat Claude Code 2.1.310 keeps in.claude.jsononce the GitHub app is connected; the account and organization UUIDs beside it are not printed (#256). - The
claude-codeparser records where a session began, so its sessions haveoriginremoteorlocalinsessions.jsonl(and the inventory'sremote_sessionsfollows):session origin: remote (Remote Control, entrypoint sdk-cli)for a session started from claude.ai throughclaude remote-control,session origin: remote (teleport, remoteSourced)for a cloud session brought local withclaude --teleport,session origin: remote (teleport, cloud scratchpad <path>)in place of the scratch directory row for the cloud machine's replayed scratchpad, andsession origin: local (VS Code extension).claude-desktopsessions keeporigin""(#256). - The
claude-codeparser readsprojects/<slug>/<session>/ccr-tip.jsonassession synced: remote event <eventId> at <updatedAt> (from <file>, file mtime), so a Remote Control session readssyncedyesafter its bridge has stopped, andprojects/<slug>/bridge-pointer.jsonasremote control: environment <environmentId> session <sessionId> source=<source> (from <file>, file mtime)(#256). - A teleported cloud session (
remoteSourcedrecords) getssession synced: cloud session (teleported: remoteSourced records replayed from the vendor's copy)beside its origin row, so it readssyncedyeslike a Remote Control spawn: the conversation is held on Anthropic's servers (#256). - The
claude-codeparser writesremote control: spawn mode <mode> for <path>,remote control: machine id setandcloud: hasRemoteEnvironment=<value>rows from.claude.json, andmodel setting: model=<alias>from a settings file'smodel, each(from <file>, file mtime); the machine id is not printed (#256).
Changed¶
claude-codesessions now havesyncedyesornoinsessions.jsonl(and the inventory'ssynced_sessionsand projectsyncedfollow): a session with a Remote Control bridge gets asession synced: remote control bridge <id> url <url>row, because the bridge puts the session on claude.ai.claude-desktopsessions keepsynced""(#256).- A
scratch directory:row no longer decides a session'sproject_pathinsessions.jsonlwhen another row of the session has a path; it is used only when no other row has one (#256). - The
claude-codeparser takes a persisted tool output's file from the 2.1.309toolUseResult.persistedOutputPathwhen the line has one tool result, and from the<persisted-output>stub's text otherwise; the file must still be in the session's owntool-results/(#256). - A
claude-codedenial recorded withtoolDenialKindand no decision object, which Claude Code 2.1.310 writes on macOS forNoat the permission dialog and for a declined plan, now readspermission decision: reject (interrupted) toolDenialKind=<kind>instead ofpermission decision: - source=- reason=- toolDenialKind=<kind>(#256). - A
scheduled_task_firerecord (a/loopfiring) is nowscheduled task fired: <taskId> cron=<cron>instead ofscheduled_task_fire: <content>(#256). - A
claude-codehistory.jsonlrow now carries the pasted text in place of a[Pasted text #<n> +<m> lines]placeholder, from the line'spastedContentsorpaste-cache/<contentHash>.txtwhen either still holds it, as the transcript's prompt does (#256). - The errors the analyzer reports to the analyst share one exception
class,
doubleagent.errors.DoubleAgentError: a bad--since,--untilor--match, an input that is neither a file, a directory nor an extractable archive, and a VS Code mutation-log record that cannot be applied raise it or a subclass, and none of them is aValueError. The messages and exit codes are unchanged (#277). - The package builds with the
hatchlingbackend instead ofsetuptools; the wheel holds the samedoubleagentpackage andcatalog.txt. The test suite runs under pytest, whichrequirements-dev.txtinstalls:tests/run.shpasses its arguments topython3 -m pytest, so a test is named by its pytest node id (tests/test_catalog.py::CatalogTests) rather than its unittest name (#277).
Fixed¶
claude-codesessions no longer readrecoveredpartialoryesbecause of their own slash commands and pastes: ahistory.jsonlline with a command as typed (/model,/help,/rename lab-round) now pairs with the transcript's tag-form command record, within 600 seconds because a command that opens a picker is recorded when it completes, and a line with a paste with the prompt that wraps it in<pasted_content>, so only lines no transcript records match stay recovered:/clear(filed under the ended session),/logoutand an/exittyped during a turn. The match keeps whitespace (only runs of it collapse), the wider window reaches forward only and against transcript records only, a line whose own record is missing keeps its own time instead of taking a later line's record, and apaste-cachefile that is there but cannot be read gives aparser:row naming it (#256).- The
codyparser reports each chat interaction it cannot decode in a damaged chat history as its ownparser: unparseable record <N> (<reason>): <record>row with the chat's session id, beside the rows of the chats it recovered; the record was dropped silently before (#280). - A relative
--sinceor--untilvalue past Python's integer conversion digit limit (4300 by default) gives the usual out-of-range error instead of Python's own conversion message, and every rejected bound's message now ends with the accepted forms, the out-of-range relative and epoch values included (#283). - Detection recognises the section headings of the bundled catalog by
their full text, so a comment inside a table that starts with a
heading's first words (
excluded,credential files) can no longer move the lines after it into another table and leave their agents undetected (#285). - The
crushandgemini-cliparsers give aparser:row, with the file's project, for an unreadable Crushprojects.json, Gemini CLIlogs.jsonor legacy Gemini CLI session file, so the failure sits in the timeline beside the file's other rows; before, the error left the parser and the command reported it as aproblem:line (#282). - The
codex-cli,continue,copilot-cli,gooseandhermesparsers end a line of the YAML, Starlark, Markdown and log files they read, and on Python 3.10 the TOML files (3.11 and later read TOML with the standard library), at LF or CRLF only and trim only the whitespace the format defines, so a value holding a Unicode line or paragraph separator (U+2028, U+2029, U+0085) or a form feed is no longer split across lines, and one with a no-break space or another Unicode space at its edge is no longer trimmed. On Python 3.10, where the analyzer reads TOML itself,automation.tomlkeeps the spaces before a multi-line basic string's first line end. It also keeps a line ending in an escaped backslash (two backslashes) instead of joining the next line. On 3.10 the Codex TOML readers also reject what the standard library's reader on 3.11 and later rejects: an unquoted value padded with a Unicode space, an integer with a leading zero, a non-ASCII digit or a misplaced underscore, and a backslash before a lone space in a string. Such a value gives oneparser:row forautomation.toml; on 3.10config.tomlleaves the entry out of itspermission setting:rows with noparser:row (#281). - The examples of the plain
detectoutput,detect --json, thetimelinestdout,inventory.json,timeline.jsonlandsessions.jsonlindocs/output.mdare now the analyzer's output on a synthetic collection, built by the newtools/example_collection.pyand regenerated by the newtools/regenerate_examples.py; the collection holds several Claude Code sessions, a Remote Control and a teleported one among them, a Codex CLI thread, settings, a prompt history that names a deleted session, a cut transcript and a cut manifest row, and The example collection describes it. Before, theinventory.jsonexample showed analyzer 0.14.0, keys of thedetect --jsonandinventory.jsonexamples had been filled in by hand, the project table underdetectlisted files its collection never held, and thetimelinestdout and the two JSONL files had no complete example.tests/test_examples.pyfails when an example is stale (#269).
0.20.1 - 2026-10-21¶
Added¶
- Source-derived rows for the open-source agents, from the 2026-10-19 review's issues; each agent's research document marks them source-derived until its real-install round. The lines below list them per agent.
- The
agent-zeroparser writespermission decision:rows for tools a policy blocked, provider MCP approvals denied automatically and Infection Check verdicts, andpermission setting:rows from the_tool_accessand_orchestratorconfig.jsonfiles and the Infection Check and Orchestrator plugin switches (#204). - The
agent-zeroparser writes an editor (ACP) session's mode, model and container working directory (permission mode:,model: requested,scratch directory:), takes the workspaceacp_cwdas the project path, and writes the per-chat model override without its API key (#204). - The
agent-zeroparser readsusr/scheduler/tasks.jsonand the Telegramstate.jsonas secondary sources; a mapped Telegram chat that is gone gives asession deleted:row (#204). - The
agent-zeroparser writes asession origin: remoterow from the A0 CLI connector's last chat, sooriginreadsremoteorlocalfor Agent Zero sessions (#204). - The
aiderparser writesmodel: <name>andmodel: <old> -> <new>rows from the startup and/modelannouncement lines, with(editor model)and(weak model)for those roles, and fills themodelcolumn on them (#203). - The
aiderparser writes apermission decision:row for each confirmation answer in the chat history; a yes or no notes that it may have come from--yes-always(#203). - The
clineparser reads the SDK hook audit log~/.cline/data/logs/hooks.jsonlas a secondary source, recovering the prompts, tool calls, results and final replies of sessions whose transcripts are gone, plushook audit:rows for run starts, aborts, errors and stale-session shutdowns (#190). - The
clineparser writespermission decision: rejectedorblockedbeside SDK tool results the user rejected or the policy blocked, andpermission setting:rows forautoApprovalSettingsandmodeinglobalState.json, the tool settings insettings/global-settings.json, and each MCP server'sautoApproveanddisabled(#190). - The
clineparser writessession origin: remoteandsession synced:rows for the Cline Cloud sessions inglobalState.jsoncloudSessionStatusesandcloud-pending-starts/*.json, and acloud task target:row for the last cloud task target; a session imported from Claude Code, Codex or OpenCode names its source agent, session and path on its start row (#190). - The
codex-cliandopen-interpreterparsers writepermission setting:rows for the standing grants inconfig.tomlandrules/*.rules, athread goal:row perthread_goal_updatedevent and amemory summary:row permemories/rollout_summariesfile, and readdb-backups/*/state_5.sqlite, a copy Codex set aside as corrupt, as a secondary source (#200). - The
codex-cliparser writespermission decision:rows forapply_patchapprovals inlogs_<N>.sqliteand for failed automatic reviews instate_5.sqliteguardian_review_feedback(#200). - The
codyparser writespermission setting:rows for the Cody keys of the editor'sUser/settings.json(Deep Cody shell allow and block lists, agentic context switches, MCP server and tool toggles) andmodel setting:rows fromcody-model-preferences, in the VS Code state row and the JetBrains global-state file (#197). - The
codyparser reads a JetBrains project's legacy.idea/cody_history.xmlas a secondary source, recovering chats and unpaired messages the agent store no longer holds (#197). - The
continueparser writes apermission setting:row for each rule in the CLI'spermissions.yaml,permission decision: allow alwaysfor thecn.log"Added policy rule" line, the reason a call was denied (canceled reason=user,canceled reason=policy,errored reason=policyfor a Security Policy Violation), and apermission mode:row for a session's mode (#198). - The
continueparser reads the CLI'sinput_history.jsonand thechatFeedbackevents as secondary sources, parses theeditInteractionandeditOutcomeevents (permission decision: edit ...), and lists acn reviewworktree as a scratch directory (#198). - The
crushparser writes apermission decision:row after a tool call the user denied or aPreToolUsehook allowed, denied or halted, andpermission setting:rows from everycrush.json, thecrushrcpermissions allow|denylines and the per-project MCP server overrides (#195). - The
crushparser writessession relayed:rows for sessions driven by an MCP channel, asession deleted:row for the missing parent of an orphaned sub-agent session, andfile version:andfile read:rows from thefilesandread_filestables (#195). - The
gemini-cliparser writespermission setting:rows fromsettings.json(permission, sandbox and MCP lists, in the home and the project), the policy files under.gemini/policies/(Python 3.11 or later; 3.10 gives oneparser:row),trusted_hooks.jsonandacknowledgments/agents.json, andsession retention:rows naming ageneral.sessionRetentionsetting, so an expired session can be told from a deleted one (#186). - The
gemini-cliparser readsshell_historyand the/chat savecheckpoints as secondary sources (#186) and the/bughistory exportsbug-report-history-<ms>.jsontoo (#155). - The
gooseparser writespermission setting:rows forpermission.yamlandroaming_trust.jsonentries at the file's modification time, and onescheduled task:row perschedule.jsonjob (#209). - The
hermesparser writessession relayed:rows for messaging-gateway sessions,relay peer:, Bot Mode room and desktopremote backend:rows, andscratch directory:rows for the git worktrees the agent created (#208). - The
hermesparser writes terminal approval outcomes aspermission decision:rows and/yolo,command_allowlistand pairing approvals aspermission setting:rows;state_metadeletion fences givesession deleted:rows, and start rows mark archived and imported sessions (#208). - The
hermesparser reads.hermes_history, the cron run files,response_store.dband the trajectory files as secondary sources (#208). - The
kilo-codeparser reads the configpermissiongrants, the sandbox policy and preference files,retention/state.jsonand theremote control:lines of its logs (#192, #221). - The
kiroparser writes apermission decision:row after each cancelled Amazon Q Developer CLI tool call (a baren, a deny rule, an interrupt or the user's own words), and onepermission setting:row perallowedToolsentry andtoolsSettingskey of each global or projectcli-agents/*.jsonfile (#212). - The
kiroparser reads the prompt history.aws/amazonq/.cli_bash_historyas a secondary source, recovers turns only a conversation's checkpoint snapshots or tangent-mode main history still hold, and turns the delegate tool's.amazonq/.subagents/<agent>.jsonrecords into launch and completion rows (#212). - The
lettaparser writespermission setting:rows from the four settings files,session relayed:rows from the channel routes, and parses the scheduled tasks (crons.json), their run logs (runs/*.jsonl) and interrupted listener turns with their approval decisions and tool results (#202). - The
little-coderparser reads the v0.0.x~/.little-coderlayout: saved sessions,history.json,session_latest.json,input_history.txtand checkpoint snapshots, and from itsconfig.jsonsession synced: gist,session relayed: telegram chatandpermission setting: permission_moderows, never reading its tokens (#157). - The
little-coderparser writespermission decision: denied (<reason>)after a call its permission gate refused (#201). - The
nanobotparser writessession relayed:rows for sessions keyed to or routed through a messaging channel,permission setting:andpermission mode:rows fromconfig.json,pairing.jsonand the WebUI workspace scope, andmodel: <old> -> <new> (preset) requested=... resolved=...rows for/modelswitches (#207). - The
nanobotparser reads the cron and trigger run records and the WebUI sidebar cache as secondary sources, recovering the scheduled turns of deleted sessions (#207). - The
ollamaparser writes apermission setting:row for each launcher integration'sautomodekey in.ollama/config.json, and for theautoModeEnabledkey it writes into Claude Desktop's third-party profile, at each file's modification time (#188). - The
openclawparser writessession relayed:rows for sessions carried through a chat platform or started by a Gmail hook,permission decision:rows for Gateway approvals, andpermission setting:rows for the exec approvals policy, standing cron grants, allowed channel senders and per-session settings (#206). - The
openclawparser writes amodel:row for a session's model override,channel delivery:andchannel ingress:rows,session deleted:,session archived:andsession forgotten from memory:rows, andsource host: github account <login>rows for the pushes and pull requests the Gateway made; it recovers prompts that never reached the transcript and entries only the full-text index still holds (#206). - The
opencodeandkilo-codeparsers writepermission decision:rows for refused tool calls,permission setting:rows for saved grants and session rule lists,session archivedrows andscratch directory:rows for the agent's own worktrees (#194, #192). - The
opencodeandkilo-codeparsers read the terminal UI'sprompt-history.jsonlas a secondary source andprompt-stash.jsonlasprompt stashed (unsent):rows;opencodealso reads the pinned sessions insession.json(#194, #192). - The
openhandsparser writes apermission decision: analyzed risk=<risk> analyzer=<analyzer> policy=<policy>row for a security analyzer's verdict, joined to the call, andpermission setting:rows from each conversation'sbase_state.json(#205). - The
openhandsparser reads the CLI'sprompt_history.jsonas a secondary source, and writes ascratch directory:row for a conversation in the Agent Server's default worktree root,/tmp/conversation-worktrees(#205). - The
pearaiparser writespermission decision:rows for the denial and approval texts of the agent surface (the Roo Code-based agent pane, as opposed to the chat), one row pertaskHistoryentry and per standing auto-approval setting in the editor'sstate.vscdb, and onetokens generated:row per model request fromdev_data/tokens_generated.jsonl(#193). - The
pearaiparser reads thePearAIoutput channel log anddev_data/chat.jsonlas secondary sources (#193). - The
piparser writes apermission setting:row fordefaultProjectTrust, reads.pi/agent/pi-debug.logas a secondary source, and writes onecrash:row per.pi/agent/crashes.jsonrecord (#201). - The
qwen-codeparser writespermission decision: <decision> (tool_call ui_telemetry)rows joined on the call id (the event namedqwen-code.tool_callortool_call),model:andpermission mode:rows fromexecutionContextandsession_approval_moderecords, andpermission setting:rows from.qwen/settings.json(#187). - The
qwen-codeparser reads~/.qwen/usage_record.jsonlas a secondary source, onesession usage:row per session summary (#187). - The
roo-codeparser writespermission decision:rows for approved tool asks and for the denial and approval results in the API history,permission mode:for each task, andpermission setting:rows from the global state,.roo/cli-settings.jsonand the MCPalwaysAllowlists (#191). - The
roo-codeparser writessync setting:rows from the Roo Code Cloud settings cache andcloud upload queued:rows from the cloud retry queue of releases up to 3.53.0 (#159). - The
shellgptparser writespermission setting:rows for three.sgptrckeys, amodel: ... (configured default, ...)row forDEFAULT_MODELand one row per function-store file, and reads no other.sgptrckey; it reads the response cache as a secondary source (#189). - The
tabbyparser reads the server's AI-written pages aspage:<id>sessions,model configured:rows from.tabby/config.toml(Python 3.11 or later), the IntelliJ and VS Code inline-edit command histories as secondary sources, andsource host:andsource host repository:rows from the GitHub and GitLab integrations (#211). - The
twinnyparser writespermission mode:andpermission setting:rows for Twinny's standing grants and the accepted team gateway policy (#154), afile changed:row per file each reply changed, reads theagent-changes/records and the Twinny log as secondary sources, and writes asource host repository:row for the repository the review tab last listed (#199). - The
vscodeparser writespermission level:rows, the model's options onmodel:rows,permission setting:rows for the standing tool approvals and the auto-approve and default-permission settings, amodel setting:row forchat.defaultModel, andsource host:andsource host repository:rows; it reads the chat editing statechatEditingSessions/<id>/state.jsonas a secondary source (edit state:rows) (#210). -
The
zedparser writes standingpermission setting:rows fromsettings.json,global_settings.jsonand thetrusted_worktreestable,permission decision: deniedandauto_deniedrows,scratch directory:rows for the macOS sandboxed-terminal temp directory and Zed-created worktrees, andunsent draft:,archived worktree:,sidebar terminal:andsidebar thread:rows (#196). -
The
cursorparser reads Cursor cloud agents, which run on Cursor's servers and keep no conversation records in the editor's database. Each entry of thecloudAgentRepository.agents.<account>list instate.vscdbbecomes a session with the agent'sbc-<id>assession_idand its repository (github.com/<owner>/<repo>) asproject_path, withsession origin: remote cursor cloud agent,session synced: cursor cloud agent <bc-id> (vendor copy; ...),model: <model>(withrequested=<id>when the requested id differs),source host: github.com <owner>/<repo> (cloud agent)andcloud agent: <name> repo=... status=... workflowStatus=... archived=... killed=... vmStarted=...rows; such a session'soriginisremoteand itssyncedyesinsessions.jsonland the inventory, since Cursor holds the conversation, and every other Cursor session'ssyncednow readsnoinstead of"". The repository listcloudEnvironmentTemplate.githubReposgives onesource host repository: <url> type=<type>row per repository, at the file's modification time. The encryptedsecret://connection tokens beside them are never read, and the protobufbcCachedDetails:<bc-id>record is not decoded yet (#236). -
The
cursorparser reads the textconversation-search.dbholds for a conversation, from its full-text index'sconversation_fts_contenttable (read as a plain table, so SQLite's fts5 module is not needed): onecached conversation text (conversation-search.db, source=<source>): <text>row per conversation with text, a singlesystemrow because the index runs the prompt and the reply together. For a cloud agent'scloud-cacherow this is the only local copy of the conversation's text, and the row also gives the session'ssession origin: remote cursor cloud agentrow (#236). -
The
cursorparser reads the editor's MCP servers and plugins:mcp server: <server> url=<url> (from state.vscdb mcpOAuth.global, mcp_oauth_updated_at_ms)per MCP server with OAuth state, endingtokens=stored (ciphertext, not read)when the editor holds the server's OAuth tokens, which are encrypted and never read (the parser decodes only the key names ofmcpOAuth.*);plugin installed: id=<id> sources=<sources> scope=<scope>per installed plugin, with the folder asproject_pathwhen the plugin is enabled for one; andmcp oauth attempt: server=<url> client_id=<id> workspace=<workspace>from the plaintextUser/globalStorage/mcp-oauth-attempts/<uuid>.json, the OAuth client registration of the server's sign-in (#236). - The
cursorparser yieldspermission decision: <decision> mcp auth for cloud agent <bc-id> tool_call=<id>fromcloudAgentRepository.mcpAuthResolution, the user's answer to a cloud agent's request to authorise an MCP server, in the cloud agent's session and joined on the tool call. An MCP tool call in a local conversation is atool_userow namedmcp-<server>-<tool>; the editor records no per-call decision for it, so it has nopermission decision:row (#236). - The
cursorparser yieldssource host: github account <login> scopes=<scopes>for the editor's own GitHub sign-in (thegithub-<login>andgithub-<login>-usageskeys ofstate.vscdb), at the account's last use, andremote agent host enrolled: <name>when the editor has enrolled the machine as a remote agent host for the Cursor account, at the file's modification time. Neither reads the encryptedsecret://values beside them (#236).
Changed¶
- The
cursorparser'sconversation deletedrow ends(isArchived is set by the delete)when the kept header is marked archived, since the editor sets that flag when it deletes a conversation that was never archived (#236). - The
cursorparser'sremote agent host enrolled:row now endsfile mtime; on by default, a paired device leaves no local record): the enrolment is on by default, and pairing a phone writes nothing on the computer (#236). - The bundled catalog copy follows the collector on Windows: Cursor's
sync store is read from
AppData/Local/Cursor/AgentStores, where the editor writes it, instead ofAppData/Roaming/Cursor/AgentStores(#236). - The bundled catalog copy follows the collector: the Linux sync-store
line
.local/state/cursor/agent-storesreplaces.config/Cursor/AgentStores(#236). - The
cursorparser'spermission setting:rows from the editor's settings blob now end(from state.vscdb composerState, file mtime, written late by the app)and itsmodel last used:rows(from state.vscdb aiSettings.modelLastUsedAt, written late by the app): the editor writes that blob some time after the change, so neither time is the time of the change (#237). inventory.jsonlists an agent's own worktree inside a project (<project>/.claude/worktrees/<name>, and the same under.kilo,.gemini,.qwenand.codex) underscratch_directoriesinstead of as a project: a path under a directory that one agent's project catalog entry names is a scratch directory, in loose mode too (#227).sessions.jsonlrelayednow readsyesornoforcrush,hermes,letta,little-coder,nanobotandopenclawsessions,syncedforlittle-codersessions andoriginforclineandagent-zerosessions, instead of""; every little-coder session readsno, since its rows name no session (#195, #208, #202, #157, #207, #206, #190, #204).sessions.jsonlrecoverednow readsno,partialoryesinstead of""for the agents whose parsers read a secondary source since this release:agent-zero,cody,continue,kilo-code,kiro,little-coder,openhands,pearai,pi,shellgpt,twinnyandvscode(#204, #197, #198, #192, #212, #157, #205, #193, #201, #189, #199, #210).- The
gooseparser writes tool confirmations aspermission request:andpermission decision: <permission> (toolConfirmationResponse)rows joined to the call, in place of the genericaction required:row, and apermission decision: not executable (goose.executable=false)row for a call the permission check refused or held without an answer (#209). - The
gooseparser reads thellm_requestlogs as a secondary source matched within 600 seconds, assembling streamed chunks first, so replies the database holds appear once and a removed or rewound session's replies are marked recovered (#126). - The
lettaparser readssessions.jsonlas exit lines only, and a transcript line takes its project from the agent's next exit line (#158). - The bundled catalog copy follows the collector's tables: the
.crewaiproject entry, the CLAI 2.0 state directory, Cody's JetBrains files, nanobot's workspace cron and trigger files, the OpenCode desktop app, the Kilo sandbox files, the Twinny workspace storage and the Agent Zero connector temp directory are attributed; the Gemini CLI and Qwen Code worktrees, the CLAI clones, OpenClaw's Gateway clones and OpenHands'workspacesare excluded; Hermes'state-snapshotsis no longer excluded; and the secret globs change as in collector 1.18.0 (#223, #224, #225, #214, #220, #213, #221, #215, #217, #216, #219, #218, #123, #231, #230, #159).
Fixed¶
- The
cursorparser no longer reads a permission prompt that was still open when the editor's database was copied as a decision: such a call (reviewData.statusother thanDone, ortoolFormerData.statusloading) yieldspermission prompt pending: <tool> reason=<reason> options=<allowlist options>instead of apermission decision:row carrying the prompt's preset option (#237). - The
cursorparser'spermission decision:row endsallowlisted=for the editor window's "add ... to the allow list", which stores the decision asrunwith the added commands, as it does for the Agents window's Always run (#237). - The
cursorparser no longer reads the tool call that was running when the user stopped a turn as a pending prompt: such a call (reviewData.statusNone,additionalData.statusrejected) yieldspermission decision: stopped by the user (reviewData.status None, additionalData.status rejected)beside its[rejected]result, and the calls queued after it (cancelled) yield theirtool_userow and no permission row (#236). - An OpenClaw database that is only a PostgreSQL engine anchor yields a
parser:row naming the store instead of nothing (#206). - A Tabby server root collected from a Docker or Podman volume is parsed; before, it was detected but not parsed (#124).
0.20.0 - 2026-10-20¶
Added¶
- A
cursorparser for the Cursor IDE's agent conversations: the composers and bubbles of the editor'sUser/globalStorage/state.vscdb(user,assistant,thinkingwith--include-thinking,tool_useandtool_resultrows, in both the 3.25.9 inline header list and the 3.26.13composerConvHeaderschunks), withsession name:,conversation archivedandunsent draft:rows;model:rows from each user message's model; apermission decision:row for each command the user was asked about, with the outcome, the reason and the commands an Always run added;permission setting:rows for the run mode of each agent mode, the command, domain and mode-transition lists and the auto-run switches, and for.cursor/cli-config.jsonand.cursor/permissions.json; andmodel last used:rows (#51). - The
cursorparser recovers a conversation deleted in the editor: its survivingcomposerHeadersrow gives aconversation deletedrow, the JSONagentKvprompt blobs that name it giveuserrows markedrecovered, and the agent worker's transcript copy under.cursor/projects/<project>/agent-transcripts/, a secondary source read on every run, gives its prompts (at the minute the transcript records), replies and tool calls.conversation-search.dband.cursor/ai-tracking/ai-code-tracking.dbgiveconversation index:,ai code:,conversation summary:,file deleted by agent:andscored commit:rows. Cursor sessions readoriginlocal, orremotefor a conversation whose header names a non-local agent location, andrecoveredno,yesorpartialinsessions.jsonland the inventory;cursormoves fromdetected but not parsedtoparsed(#51). - The bundled catalog copy follows collector 1.17.0: it gains Cursor's
AgentStoresandupdate-supervisordirectories in the editor's user-data directory, so files there are detected ascursor, and its exclusion section gains Cursor's embedded Agent CLI, update supervisor binary, canvasnode_modulesandstatsig-cache.json, which detection does not read (#6).
0.19.0 - 2026-10-19¶
Changed¶
- The
continueandpearaiparsers now want the session indexsessions/sessions.json, sodetect --fileslists it as parsed; it yields rows only when it is damaged (#170). - The
vscodesession-start row ends withworkspace=<path>when the workspace is a.code-workspacefile, and a multi-root session with noworkingDirectoryof its own now has an emptyproject_path, where it had the.code-workspacefile's path (#185). - The
gooseparser reports acontent_jsonthat holds a bare JSON string as not a JSON array, since Goose writes an array there (#183). - The
crushmodel:rows end withrequested=<model>when the requested model differs from the one that served the turn, and themodelcolumn reads<provider>/<served model>(#167). - The
opencodeparser now reads secondary sources, so an OpenCode session'srecoveredinsessions.jsonland the inventory readsno,yesorpartialwhere it was"";kilo-codekeeps"", since Kilo Code has no such source (#165). - The
hermesparser may show one model switch twice: themodel: <old> -> <new>row of the stored switch marker and the row of the session's model usage, at different times (#180).
Fixed¶
- A JSONL line that is valid JSON but not an object (an array, a
string, a number,
null) is its ownsystemrowparser: unparseable line (not a JSON object): <line>in every parser that reads JSONL through the shared reader, instead of being skipped silently (#228). - Ollama: a
.ollama/historyline that is not valid UTF-8 is no longer shown as auserrow with replacement characters; it becomes its ownsystemrowparser: unparseable line (<reason>): <line>carrying the line as read, and the other lines are still yielded (#174). trustedFolders.jsonfor Gemini CLI and Qwen Code is read as Qwen Code reads it: a byte-order mark and trailing commas no longer reduce the file to a single "is not a JSON object" row, so the standing trust grants appear in the timeline (#173).- Twinny: a message the user sends while a reply runs (a
saidtool step, since Twinny 4.3.5) is now auserrow; it was atool_userow namedsaidwith an emptytool_result(#171). - Cody: a Deep Cody confirmation the user refused now reads
permission decision: rejected confirmation state=error error=Command rejected ..., and a pending one readsapproved or unanswered, because Cody saves an approval and an unanswered request the same way; any other confirmation error is printed aserror=<message>(#169). - OpenClaw: a legacy
sessions.jsonthat cannot be read, does not decode or is not a JSON object now yields aparser:row in each transcript beside it instead of silently losing the session key, and a transcript or archive line that is valid JSON but not an object is its ownparser: unparseable line (not a JSON object): <line>row instead of being dropped (#177). - Qwen Code
cronandnotificationrecords, andagent_messageandagent_mentionrecords written by another workspace agent, aresystemrows naming the subtype, provenance and agent (cron (provenance=system): ...,agent_message (provenance=external_agent agent=<name> agentId=<id>): ...) instead ofuserrows that read as prompts the user typed; the user's own @-mention stays auserrow (#172). - Crush: a
messages.partsvalue that does not decode is carried in full in aparser: unparseable record <rowid> (message <id> parts: <reason>): <parts>row, where only the error was reported (#166). - Crush: the model that served a Hyper Prism turn (
prism_model_id) is named in themodel:rows and themodelcolumn, with the requested model beside it asrequested=<model>, where only the requested model was shown whenever both were set (#167). - Codex CLI and Open Interpreter: each
thread_history_1.sqlitethread_itemsrow whoseitem_jsondoes not decode to a JSON object is now its ownparser: unparseable record <rowid> (<reason>): <item_json>row carrying the stored value, with its thread's session and project, instead of one row that counted them (#184). - OpenHands: a
meta.jsonorbase_state.jsonthat cannot be read, does not decode or is not a JSON object now gives aparser:row naming the file and the reason, instead of silently dropping the conversation's title, model and project (#176). - Agent Zero chats collected from a Docker or Podman volume (
*a0_usr,a0-launcher-*-usr), whose paths start atchats/under the volume's_data, are now parsed; they were collected and detected but yielded no rows (#175). - Goose: a
sessions.dbcontent_json,metadata_jsonormodel_config_jsonthat does not parse, or is not the array or object Goose writes, is now its ownparser: unparseable record <N> (<table>.<column>: <reason>): <stored value>row carrying the stored value in full, and the rest of the session is still read; before, the content row lost the stored text and bad metadata or model config was silently dropped (#183). - VS Code: a chat's project path is now its own
workingDirectory, then the workspace's folder, and never a.code-workspacefile, so Agents-window chats are no longer classed under the editor'sagent-sessions.code-workspacescratch file; the workspace file is named in the session-start row asworkspace=<path>(#185). - VS Code: a
workspace.jsonthat cannot be read or is not a JSON object now gives aparser:row instead of being skipped silently (#185). - Zed: a
threads.dbordb.sqlitethat is not SQLite is reported as oneparser: not a SQLite databaserow, and a SQLite error asparser: SQLite error: <error>, instead of yielding nothing (#168). - Zed: a thread blob that does not decode is now
parser: unparseable record <rowid> (<reason>): <stored value>, carrying thejsontext or thezstdbytes as hex, instead of an unprefixedthread <id> could not be decodedrow (#168). - Zed:
messages[]items,Agentcontent items, legacy segments and tool entries of an unknown variant each become aparser: unparseable record <N> (<reason>): <item>row instead of being skipped, and aCompactionof an unknown variant is no longer reported as provider native (#168). - Hermes: a model switch made in the TUI or desktop app (a stored message
with
display_kindmodel_switch) is now asystemrowmodel: <old> -> <new> provider=<provider>at the switch time, instead of a user prompt, or a[rewound]user prompt once a later switch superseded it (#180). - Hermes: a Hermes home collected from a Docker or Podman named volume,
whose files start at the volume root (
state.db,sessions/,state-snapshots/,pastes/), is now parsed; before, it was collected and never read (#181). - Hermes: a
tool_callsvalue that is not valid JSON, is not a list or holds a non-object entry is now aparser: unparseable record <id> (<reason>): <value>row carrying the stored value, instead of being dropped silently or reported without the value (#182). - Continue: an unreadable or damaged
.continue/sessions/sessions.json(not a JSON array, cut mid-write, an entry without asessionId) now yieldsparser:rows from the index file instead of being dropped silently, and the entries before a cut still give their sessions a time (#170). - PearAI: the same for
.pearai/sessions/sessions.json, which is read through the Continue parser's index reader (#170). - nanobot: sessions stored in
sessions/<workspace-id>/sessions.sqlite3are read, each undecodable record its ownparser:row, and the JSONL files andjsonl-backup/copies the database imported are secondary sources, so each event shows once and a turn the database lost is recovered (#178). - nanobot: the CLI history
history/cli_historyis read under any.nanobot*state directory, where--homemoves it, not only under.nanobot(#179). - OpenCode 2.0 databases are read: session headers and project paths
from
session_v2, thesession_messagetranscript including the 2.0 shell, location-switched, skill and idle records; a migrated session is no longer shown twice, a session deleted in 2.0 is recovered from the 1.x tables left behind, andopencode-next.dbis a secondary source (#165). - OpenCode and Kilo Code: each undecodable message, part,
session_messageor event record and each undecodable part file is its ownparser:row carrying the record in full, replacing the "N part(s) of message ... could not be decoded" and "N record(s) with undecodable data" count rows (#139).
0.18.0 - 2026-10-19¶
Added¶
inventory.jsonends with ascratch_directorieskey, an object keyed and shaped likeprojects, that lists the scratch directories: the working directories that belong to the agent rather than to the user, such as the ChatGPT desktop app's projectless~/Documents/Codex/<date>/<slug>, a directory under the agent's own state directory (.copilot/chats/<id>) and the working directory of a Copilot Desktop session the app pre-created and never prompted. A path is one when a parser'sscratch directory:row names it, or when it lies under a directory a home catalog entry names in one of the input's homes (#149).- The
codex-cliparser yields asystemrowscratch directory: <path> (projectless thread <id>) (from .codex/.codex-global-state.json thread-projectless-output-directories, file mtime)for each projectless output directory the ChatGPT desktop app records, with that path asproject_path(#149). - The
copilot-cliparser yields asystemrowscratch directory: <cwd> (working directory of a session the app pre-created and never prompted: no events.jsonl)after thesession metadata:row of a session directory that holds aworkspace.yamlwith acwdand noevents.jsonl(#149). - The bundled catalog copy gains the collector's
codex-cli|Documents/Codexline, so a home with the ChatGPT desktop app's projectless output directories is detected ascodex-cliand the directories fall underscratch_directoriesby the catalog rule (#149).
Changed¶
inventory.jsonprojectsno longer lists the scratch directories, which move toscratch_directories; therowsandsessionsofprojects,scratch_directoriesandoutside_projectsadd up to the totals.timeline.jsonlandsessions.jsonlare unchanged (#149).- The
timelinestdout line for the inventory readsinventory: out/inventory.json (homes, projects, scratch directories, counts per agent, problems)(#149).
0.17.0 - 2026-10-19¶
Added¶
- A
chatgpt-desktopparser for the ChatGPT desktop app's list of the account's ChatGPT conversations, thechatgptrows of.codex/sqlite/codex-dev.dblocal_thread_catalog, which the app writes at sign-in. A conversation's text stays on OpenAI's servers, so each gives threesystemrows at its update time:account conversation: <title> origin=<conversation_origin> created=<UTC> updated=<UTC> host=<host_id> (from .codex/sqlite/codex-dev.db local_thread_catalog),session origin: remote chatgpt.com conversation <id> (listed at sign-in; its transcript is server-side)andsession synced: chatgpt.com holds conversation <id>, no local transcript, so its session readsoriginremoteandsyncedyesinsessions.jsonl(#49).
Changed¶
chatgpt-desktopis no longer reported ascollected only: with a parser it is listed underparsed, or underparser found no rowswhen its files hold no account conversation (#49).
0.16.0 - 2026-10-19¶
Added¶
- The
codex-cliandopen-interpreterparsers read the rollout'sthread_settings_appliedevents, which the ChatGPT desktop app writes when the user changes the model, the reviewer or the access level, somodel:andpermission mode:rows are dated when the setting changed, not at the next turn (#160). - A turn whose
task_startedrecord names its trigger gets aturn trigger: <turn_trigger> turn=<id>row:composer,remote_iosandremote_android(a prompt sent from a paired phone over remote control),guardian_review,scheduled_task_setup,automation_heartbeat_run_nowandautomation_heartbeat_scheduled(#160). - The
codex-cliparser yields the ChatGPT desktop app's per-call approvals: apermission decision: <decision> exec <item id> ...row per user decision recorded inlogs_<N>.sqlite, joined to the call ontool_use_id, and apermission decision: <outcome> (auto_review ...)row per verdict of a guardian review thread (the automatic reviewer of the "Approve for me" mode), attributed to the user's thread (#160, #49). - The
codex-cliandopen-interpreterparsers write aremote control: enabled=<0|1> server=<id> environment=<id> host=<name> ...row perstate_5.sqliteremote_control_enrollmentsrow (#160). - The
codex-cliparser reads the desktop app's.codex/.codex-global-state.jsonas a secondary source: its prompt history asuserrows (kept and markedrecoveredfor a thread whose rollout is gone), andsession deleted:,thread description:,permission mode (standing):,permission setting:andremote control: mobile devicerows, so a thread deleted in the app still shows its prompts and its deletion (#160, #49).
Changed¶
- The
permission mode:row of thecodex-cliandopen-interpreterparsers ends with two more fields,reviewer=<approvals_reviewer> profile_id=<active_permission_profile.id>(-when the rollout records neither), a change to the row text oftimeline.jsonl(#160).
0.15.0 - 2026-10-18¶
Added¶
- The
openclawparser records sync state: a session published through the Gateway's public link gets asession synced: public share <id> generation <sessionId>row fromsession_nodes.entry_json, sosessions.jsonlsayssyncedyesornofor OpenClaw sessions, and it readsstate/openclaw.sqlitefor onebackup uploaded:row per successfulopenclaw backupupload or Git backup (#153). - The
agent-zeroparser records sync state: asession synced: provider responserow for each agent that chained a turn on a response the provider stored, and asession relayed:row for a chat carried by the Telegram, WhatsApp or email plugin (#153). - The
hermesparser readspastes/pending.json: onedebug report uploaded: <url>row per paste.rs link of a recenthermes debug shareupload (#153). - The
gooseparser writes asession relayed: gateway <platform> user <user_id>row for a session the gateway relayed through its Telegram bot (#153). - The
codex-cliandopen-interpreterparsers readlogs_<N>.sqlite, the tracing log under the agent's home: each/feedbackreport sent to Sentry is asession synced: feedback report uploaded to Sentry, ...row with the reported thread as its session, and both parsers now setsyncedinsessions.jsonl(nofor a thread without the row) (#153). - The
clineparser yields asession synced: enterprise prompt upload to ...row for an SDK session whose manifest orsessions.dbmetadata hasenterprise.blobUpload, naming the organisation's bucket and object, and setssyncedinsessions.jsonl(#153). - The
clineparser reads the VS Code extension's upload queuecache/sync-queue.json: each queued task file is anenterprise prompt upload <status> ...row for its task, prefixedsession synced:when the file was uploaded, without the file content the queue holds (#153). - The
continueparser reads.continue/logs/cn.logand its rotatedcn<N>.logfiles: eachcn servemetadata post to Continue's agent API is anagent metadata posted:row with its result, and a skipped snapshot upload astorage upload requested, skippedrow (#153). - The
qwen-codeparser reads the channel routes files.qwen/channels/sessions.jsonand.qwen/channels/daemon/<hash>/routes.json: each session driven from a messaging platform is asession relayed: channel ...row (#153). - The
opencodeandkilo-codeparsers write asession synced: sharerow for each session published to OpenCode's share service, from the database'ssession_sharetable, and, for a session shared and then unshared,session synced: share urlandsession unsharedrows from itssession.updated.1events;sessions.jsonlsyncedis nowyesornofor their sessions (#153). - The
kilo-codeparser reads Kilo's cloud session marker,.local/share/kilo/storage/session_share/<sessionID>.json, as asession synced: kilo sessionrow (#153). - The
piparser writes asession synced: bug reportrow for a/bugreport uploaded to radius.pi.dev and abug report exported:row for one written to a zip, from the session'spi.bug-reportentry;sessions.jsonlsyncedis nowyesornofor pi sessions (#153). - The
lettaparser writes asession synced: server conversationrow for each transcript of a server-side agent and reads.letta/settings.jsonand a project's.letta/settings.local.jsonfor asession synced: last session onrow per server;sessions.jsonlsyncedis nowyesornofor Letta sessions (#153). - The
vscodeparser reads Copilot Chat'sUser/globalStorage/github.copilot-chat/cloudSessions.json, asession synced: remote sessionrow per chat session uploaded to the user's GitHub account, and the cloud agent tasks in the extension'sstate.vscdbglobal state, asession synced: cloud agent task <id>row each, since GitHub holds those sessions too, and asession origin: remote GitHub cloud agentrow beside it;sessions.jsonlsyncedis nowyesornofor VS Code sessions,yesfor each cloud agent task (#153). sessions.jsonlgains a last fieldrelayed, aftersynced, a new field of that interface:yeswhen the session has asession relayed:row,nofor a session of an agent whose parser records relay state (agent-zero,goose,qwen-code) without one,""for every other agent.inventory.jsongains tworelayed_sessionskeys, new keys of that interface: the last top-level key, aftersynced_sessions, counting the sessions whoserelayedisyes, and the last key of eachhomes[].agents[]object, aftersynced_sessions, counting them per home and agent (#153).sessions.jsonlgains a last fieldorigin, afterrelayed, a new field of that interface:remotewhen the session has asession origin: remoterow,localfor a session of an agent whose parser records origin (vscode) without one,""for every other agent.inventory.jsongains tworemote_sessionskeys, new keys of that interface: the last top-level key, afterrelayed_sessions, counting the sessions whoseoriginisremote, and the last key of eachhomes[].agents[]object, afterrelayed_sessions, counting them per home and agent (#153).timeline.jsonlgains a last fieldrecovered, aftersource_line, a new field of that interface:yeson an event row (anyturn_typebutsystem) of a secondary source that survived deduplication, so the primary store does not hold the event,""on every other row.sessions.jsonlgains a last fieldrecovered, afterorigin, a new field of that interface, decided by the session's event rows:yeswhen every one is recovered (the primary transcript is gone),partialwhen some are,nowhen none is and the agent's parser reads a secondary source,""for the others.inventory.jsongainsrecovered_sessions, new keys of that interface, counting theyesandpartialsessions: the last top-level key, afterremote_sessions; the last key of eachhomes[].agents[]object, afterremote_sessions. Eachprojects.<path>entry andoutside_projectsend withsyncedandrelayed(yes,partial,noor""),origin(remote,mixed,localor"") andrecovered(yes,partial,noor""), summarising their sessions: new keys of that interface, in last position.
Changed¶
sessions.jsonlsyncednow readsyesorno, where it read"", for the sessions ofagent-zero,cline,codex-cli,kilo-code,letta,open-interpreter,openclaw,opencode,piandvscode, whose parsers now record sync state, andinventory.jsonsynced_sessionsand thesessions:line count their synced sessions. Thesessions:line also counts the relayed sessions, the sessions of remote origin and the recovered sessions, ending(<N> synced, <M> relayed, <K> remote, <R> recovered), each part shown only when its count is greater than 0. This changes the values of asessions.jsonlfield (#153).timeline.jsonlgainssystemrow forms:session synced:from the parsers above,session relayed:(agent-zero,goose,qwen-code),session origin: remote(vscode),backup uploaded:(openclaw),debug report uploaded:(hermes),agent metadata posted:andstorage upload requested, skipped(continue),bug report exported:(pi),session unshared(opencode,kilo-code) andcline'senterprise prompt upload <status>for a queued upload that has not gone yet (#153).- The bundled catalog copy follows collector 1.14.0: its credential
section gains the secret globs of the session sync round and replaces
Qwen Code's
.qwen/channels/*.json, and its exclusion section drops Cline's VS Codecachedirectory and Kilo Code'slogdirectory. Detection reads neither section, so it is unchanged (#153).
0.14.0 - 2026-10-17¶
Added¶
inventory.jsonbegins with adoubleagent_versionkey, the analyzer version that wrote it (#138).inventory.jsonends with acollected_onlykey and theagents:line gains acollected only:group, naming the detected agents whose files hold nothing to parse (camel-ai,chatgpt-desktop,copilot,metagpt,shared). This adds a key to theinventory.jsoninterface (#143).- A
copilot-cliparser for GitHub Copilot CLI. It reads each session's event log.copilot/session-state/<id>/events.jsonl: prompts, replies, tool calls and results joined ontoolCallId, the system prompt, the runtime's utility-model calls,model:rows fromsession.model_change, andpermission request:,permission decision:andpermission mode:rows beside the call they govern (#50). - The parser reads
.copilot/session-state/<id>/workspace.yaml: asession name:row and asession metadata:row (user-named, forks, compactions) at the session's creation time; itscwdis the project path of rows that record none (#50). - The parser reads
.copilot/session-store.db(SQLite with WAL sidecars) as a secondary source with a one-hour window, since a turn is stamped when it ends: the prompt and final reply of each turn, the session name, andfile changed:,reference:andcheckpointrows, so turns a/rewindremoved from the log, or a deleted session's, are kept (#50). - The parser reads each session's
rewind-file-snapshots/index.jsonas a secondary source: the prompt of each snapshot and arewind snapshot:row naming the files it saved (#50). - The parser reads the prompt history
.copilot/command-history-state.jsonas a secondary source: oneuserrow per prompt with no timestamp, session or project, matched on text alone (#50). - The parser reads the standing approvals in
.copilot/permissions-config.json(tool_approvalsandallowed_directoriesper location) and theallowedUrlsanddeniedUrlsof.copilot/settings.json: onepermission setting:row per entry at the file's modification time (#50). - The parser reads the sign-in line of
.copilot/logs/process-*.logas asigned in: <host> as <login>row, and takes the project path of database and snapshot rows that record none from.copilot/sidebar-sessions-state/*.json(#50). - The parser yields a
systemrowsession.info <infoType>: <message>(withurl=andtip=when present) for eachsession.infonotice in the event log, so the CLI's own report of a/modelswitch ("Model changed from ... to ...") and its Auto routing line keep their rows beside themodel:row (#133). - The bundled catalog copy follows collector 1.13.0: GitHub Copilot
Desktop is detected as
copilot-desktopfrom its own files inside.copilot(data.db,repo-metadata-cache.db,chats,repos,run,media-cache,canvas-catalog-probe,logs/github-app.*.log), its data directorycom.github.githubappand Foundry Local's.github-copilot-cli; its sessions under.copilot/session-statestaycopilot-cli(#147). - A
copilot-desktopparser for the GitHub Copilot Desktop app. It reads the app's store.copilot/data.db(SQLite with WAL sidecars), onesystemrow per record: the signed-in account (account:, with whether a token is stored, never the token), the app settings at the file's modification time, the model provider, each repository added (project added:), worktree and workspace, each session (desktop session:with its type, model,auto_approveandpermission_mode, andforked from sessionwhen forked), activity items, checkpoint commits, scheduled tasks with their triggers and runs, session automations and session deletion requests. The app's session logs stay with thecopilot-cliparser (#147). - The
copilot-desktopparser also reads the checkpoint refs the app writes into its clone of a project,.copilot/repos/<repo>/.git/refs/copilot/checkpoints/<session>/<seq>/<event>: onesystemrow per ref,checkpoint ref <seq>: commit <hash> event <event id> (file mtime), at the file's modification time, with the session id from the path. The refs outlive a session the app deleted, and their commits are snapshots of the session's worktree (#147). - The
copilot-cliparser yieldshook <hookType> start: <input>andhook <hookType> end: success=<b> output=<output>(witherror=when present) rows for thehook.startandhook.endevents the desktop app's sessions record, joined to the tool call onparentToolCallId; they werehook.start: <compact JSON>rows before (#147). sessions.jsonlgains a last fieldsynced, a new field of thesessions.jsonlinterface:yeswhen the session carries asession synced:row,nowhen the agent's parser records sync state (todaycopilot-cli, which also covers Copilot Desktop's sessions) and the session has none,""when the parser records no sync state (#151).inventory.jsongains twosynced_sessionskeys, new keys of that interface: the last top-level key, the number of sessions whosesyncedisyes, and the last key of eachhomes[].agents[]object, the same count for that user and agent. Thesessions:line ends with(<N> synced)when N is greater than 0 (#151).- The
copilot-cliparser yields asession synced: remote session <id> task <id> last event <id> remote_steerable=<value> (from workspace.yaml, file mtime)row when a session'sworkspace.yamlhas a non-emptymc_session_id, the marker of a session synced to the GitHub account, at the file's modification time (#151).
Changed¶
- The
copilot-cliparser'ssession metadata:row ends withclient=<client_name>fromworkspace.yaml,github/clifor a CLI session andgithub/autopilotfor one the desktop app ran (#147). -
The
copilot-cliparser writes the desktop app'ssession.permissions_changedform, which carriesallowAllPermissionModeand nomode, aspermission mode: allow-all <old> -> <new>instead ofpermission mode: - -> -(#147). -
The package metadata (
pyproject.toml[project.urls]) links the documentation site, the repository, the issue tracker and this changelog. - Each line a parser cannot decode is now its own
systemrow carrying the line in full,parser: unparseable line (<reason>): <line>, in place of the one row per fileparser: N unparseable line(s), first at line M; a JSON array record that does not decode is likewiseparser: unparseable record N (<reason>): <text>, with the record's text to the end of the file, and a whole-file error is one rowparser: <message>. This changes the timeline row text interface (#136).
Fixed¶
camel-ai,chatgpt-desktop,copilot,metagptandshared, whose files are a token store, configuration or app state with no transcript, are no longer listed as a parser backlog underdetected but not parsedand indetected_not_parsed, which changes the meaning of thatinventory.jsonkey; they are listed ascollected only(#143).- The Claude Code and Antigravity
history.jsonlreaders report unparseable lines assystemrows instead of dropping them silently; the valid prompts before and after are still yielded (#134). - A
parser:row from a secondary source (a history file that could not be read, or held an unparseable line) is no longer dropped as a duplicate of the same row from the primary store: each describes its own file, so both are kept (#135). - The Codex and Open Interpreter
history.jsonlreaders report unparseable lines assystemrows instead of dropping them silently; the valid prompts before and after are still yielded (#125).
0.13.0 - 2026-10-15¶
Added¶
- A
dropped:line on thetimelinestdout, always printed beforeagents:, counting the rows of secondary sources dropped because a primary store, or another secondary source, records the same event, with the agents that had any (#122). inventory.jsonkeydeduplicated, the new last key: the dropped rows per agent,{}when none (#122).- The
hermesparser reads the quick snapshotsstate-snapshots/<id>/state.db(collected with--full) likestate.db, after onesystemrow naming the snapshot, as a secondary source, so a session pruned or deleted from the live database is recovered once however many snapshots hold it (#122). - Cline's
~/.cline/data/db/session-search.db, the SDK's full-text index of each session, is read as a secondary source: onesystemrow per indexed session at its start, then auserorassistantrow per message with no timestamp, so a session whose transcript is gone still shows its turns (#122). - Cline's CLI prompt history
~/.cline/data/cache/user_input_history.jsonlis read as a secondary source: oneuserrow per prompt, the last one at itstsand the others with no timestamp, since the CLI restamps every line each time it rewrites the file (#122). - OpenClaw: compaction checkpoints (
*.checkpoint.<uuid>.jsonl) and pre-doctor repair backups (*.jsonl.pre-doctor-*-repair-*.bak) in the sessions directory are parsed as secondary sources; a backup keeps the abandoned branches a doctor repair removed (#122). - The
nanobotparser reads the WebUI display transcript (.nanobot*/webui/<key>.jsonland its.segments/NNNNNN.jsonl) as a secondary source:user,assistant,tool_use,tool_result, opt-inthinkingandsystemrows (file edits, fork markers, context compactions) at the record'screated_at_ms, which is true UTC unlike the session files' local time, so they pair with session rows within 15 hours; a WebUI turn whose session file a/newemptied is now in the timeline (#122). - The
nanobotparser reads~/.nanobot/history/cli_history, the CLI's prompt history, as a secondary source: oneuserrow per prompt at its own local time, with no session or project (#122). - The
codex-cliandopen-interpreterparsers readstate_5.sqlite(athread record:system row per thread and its first prompt, with no timestamp) andthread_history_1.sqlite(the turns and turn items Codex projects from a rollout) as secondary sources, so a thread whose rollout was removed outside Codex, excluded by size or truncated is still in the timeline (#122). - Roo Code: the CLI's input history
~/.roo/cli-history.jsonis read, oneuserrow per entry with no timestamp, task or project, as a secondary source that matches the task rows on text alone (#122). - The
antigravityparser reads the brain mirror of each conversation,brain/<uuid>/.system_generated/logs/transcript_full.jsonl(else its chunks, else the shortenedtranscript.jsonl, else its chunks) under every data directory, as a secondary source paired with the conversation database within 600 seconds, so a conversation whose database is gone or unreadable is still in the timeline (#122).
Changed¶
- Interface change: the
timeline.jsonlandsessions.jsonlproject_pathof a Windows path (a drive letter or a\\network share) is written in one canonical form, whichever agent and store recorded it: upper-case drive letter, backslash separators, no repeated or trailing separator (except in a drive root such asC:\), and the extended-length prefix\\?\removed. Antigravity'sc:/Users/sean/demo(from afile:URI) andC:/users/sean/demo2are nowC:\Users\sean\demoandC:\users\sean\demo2. The case after the drive is kept as recorded; POSIX paths are unchanged (#112). - Interface change:
inventory.jsonprojectsgroups Windows paths that differ only in case into one project, keyed by the spelling of the collected project directory when it is one, so itsstatusiscollectedand its rows, sessions and files are under one key; a project the input does not hold is keyed by the first spelling the rows record. Project directories fromcollection.jsonare in the same canonical form (#112). - Interface change:
timeline.jsonlno longer holds a row of a second copy that repeats a transcript row or another copy. The secondary sources are Aider's.aider.input.historyand.aider.llm.history; Antigravity's CLIhistory.jsonland brain mirror; Claude Code'shistory.jsonl; Claude Desktop's Coworkaudit.jsonl; Cline'ssession-search.dband CLIuser_input_history.jsonl; Codex CLI's and Open Interpreter'shistory.jsonl,state_5.sqliteandthread_history_1.sqlite; Gemini CLI's and Qwen Code'slogs.json; Goose'shistory.txt; Hermes'sstate-snapshots/<id>/state.dbandsessions/<id>.jsonldiverts; Letta'stranscript.jsonl; Muse Code'stui-history.jsonl; nanobot's WebUI transcript andcli_history; OpenClaw's reset and deleted archive files, compaction checkpoints and pre-doctor repair backups; Roo Code'scli-history.json; and Tabby's database backups andevents/log. One of their rows is dropped when a primary-store row of the same host, user and agent has the sameturn_typeandtext(twotool_useor twotool_resultrows that both carry atool_use_id: the sametool_use_idinstead), the samesession_id(or, without one, the sameproject_path, or, without either, any session or project) and, when the secondary row has atimestamp_utc, one at most 2 seconds away (a wider window where the parser table says so); a row with no timestamp, such as Muse Code's, matches on text alone, the session-scoped ones first, so the most specific copy takes the primary row. Secondary rows that no primary row took are then matched against the copies a primary row took and against each other the same way, the timestamped rows first in time order and the undated ones after, and looking under a broader scope too (the project, then any) when a candidate's session and project do not differ from the row's; so three copies of a primary event leave only the primary row, copies of one event kept by several backups appear once, as the earliest timestamped copy, and of an undated and a timestamped copy the timestamped one is kept. A row pairs only with one from a different file, so two identical lines in one file both stay. Secondary rows with no match are kept, so a prompt whose transcript is gone is still in the timeline (#122). - Goose's CLI
history.txt(both locations) is a secondary source: a history row is dropped when asessions.dbuser row has the same text, and kept when none does, such as a prompt of a removed session (#122). - Aider's
.aider.input.historyand.aider.llm.historyare secondary sources of.aider.chat.history.md: an input-history prompt the chat history records is dropped, since the chat row takes its time from it, and one the chat history lacks is kept (#122). - Claude Desktop: each Cowork session's
audit.jsonlis read in full on every run, its prompts, replies and tool calls too, not only when the session dir holds no transcript, and is a secondary source of that transcript; the audit rows without a session record take theinitline'scwdasproject_path, as the transcript's rows take the JSONLcwd. The Cowork session recordlocal_<uuid>.jsonis a secondary source too, and itsinitialMessageis now always auserrow, with no timestamp, instead of only when neither a transcript nor an audit log was collected; a Code-tab record's stays a fallback, since its transcript belongs to agentclaude-code(#122). - The
lettaparser reads.letta/transcripts/<agent>/<conversation>/transcript.jsonlalways, also when the conversation's local-backendmessages.jsonlwas collected (it gave onesystemrow pointing at it before), as a secondary source paired withmessages.jsonlwithin one hour, since Letta stamps each line at the end of its turn; itstool_usetext is now the command, path or query, as themessages.jsonlrow shows it, instead of the arguments JSON (#122). - The
hermesparser'ssessions/<id>.jsonldiverts are a secondary source: a message the livestate.dbalso holds for that session is shown once (#122). - Tabby's pre-migration backups
db.backup-*.sqliteand the event logevents/YYYY-MM-DD.jsonare secondary sources: a backup's thread and event rows that the live database (or an earlier backup) also holds, and an event log prompt or choice thatuser_eventsalso holds, are dropped. The backup's leadingsystemrow now names the backup file (#122). - OpenClaw: a published reset or deleted archive blob in
session_transcript_archivesis parsed in full instead of onesystemrow naming its file, and the.jsonl.reset.*/.jsonl.deleted.*file is a secondary source; the session id of a legacy JSONL file is now its headerid(#122).
0.12.0 - 2026-10-15¶
Added¶
timelinewritesinventory.jsonbesidetimeline.jsonlandsessions.jsonl: the input'skind,notesandproblems(the problems met while opening the input, then each file a parser could not read), the files, bytes and parser of each agent in each user home and Docker volume (homes, the same arraydetect --jsonprints), row and session counts per project (projects) and outside any project (outside_projects), each split by agent andturn_type, and the lists of agents parsed, detected but not parsed, and whose parser found no rows. The row and session counts follow the filters, as the stdout breakdown does;homes,notes,problemsand the file counts do not (#103, #104).- Each
inventory.jsonproject starts withstatus:collectedwhen the path is a project directory the collection holds (collection.jsonprojects),referencedwhen only timeline rows name it. A collected project that no row names is listed with zero counts (#104). - Each
inventory.jsonproject counts the files the collection holds in its directory: every agent gainsfilesandbytes, an agent with project files and no rows (a.cursor/directory, an agent with no parser) is listed with zero rows, so the keys ofagentsare every agent used on the project, andshared_filescounts the files no single agent owns (AGENTS.md,.mcp.json). These counts do not follow the filters (#104). detectprints project directories in a table of their own after the table of homes: one line per agent of each project with its files, bytes and parser, and a(shared)line for files such asAGENTS.md.detect --jsonholds them asprojects(path,users,agentswithagent,files,bytes,parser,shared_files,shared_bytes).projectnever appears as an agent. Collections from collectors before and after 1.12.0 give the same lists, and loose input lists no project (#104).- An agent whose only files are in a project directory counts as
detected on the timeline's
agents:line and ininventory.json(#104). timelineprints a line forinventory.jsonafter thesessions:line:inventory: <path> (homes, projects, counts per agent, problems). Therows:andsessions:lines are unchanged (#103, #104).docs/output.mddocuments every key ofdetect --jsonandinventory.jsonand every field ofsessions.jsonlin a table with its type, and thetimeline.jsonltable gains a type column (#103, #104).- The bundled catalog copy detects the Antigravity CLI's file secret
store
plugin-secrets.json(.config/antigravity,Library/Application Support/antigravity,AppData/Roaming/antigravity) asantigravity(#90). - The bundled catalog copy follows collector 1.12.0: the Antigravity
desktop app's Electron user-data directory
(
Library/Application Support/Antigravity,.config/Antigravity,AppData/Roaming/Antigravity) and its macOS logs directoryLibrary/Logs/Antigravityare detected asantigravity(#83). - The bundled catalog copy detects
AppData/Local/antigravity, the Windows staging directory of the Antigravity desktop app and CLI, asantigravity, following collector 1.12.0 (#83). - The
antigravityparser reads the Antigravity desktop app's conversations:conversations/*.dbandconversation_summaries.dbunder.gemini/antigravity(the desktop app 2.x), and under.gemini/antigravity-ideand.gemini/antigravity-backup(the separately installed IDE and the app's migration backup) as well as.gemini/antigravity-cli. Before, only the CLI's conversations produced rows and the app's were collected but silently skipped. Each conversation's session start row now names the surface that wrote it,source=<trajectory source>(clifor the CLI,cascade_clientfor the desktop app, the number for a value not in the table) anddata_dir=<the .gemini subdirectory>(#83). - Rows from the Antigravity CLI's
history.jsonlcarry the conversation's id insession_idwhen the line hasconversationId, which CLI 1.3.x writes; older lines have none and keepsession_idempty. A prompt in the history can now be joined to its conversation database (#89). - The
antigravityparser turns Antigravity's permission state intosystemrows. Each grant in a step'ssteps.permissionsblob is apermission grant: action=<action> target=<target> allow=<true|false> scope=<scope>row, and each file access entry apermission grant: path=<path> is_directory=<true|false> allow=<true|false> scope=<scope>row, after the step's rows and with itstool_nameandtool_use_id. The standing grants in.gemini/config/config.json,.gemini/config/projects/*.json,.gemini/config/sidecars/*/sidecar.jsonand the CLI's.gemini/antigravity-cli/settings.json(allow lists, sidecar access grants, the migration and sandbox flags, trusted workspaces) are onepermission setting: <key>=<value> (from <file>, file mtime)row per entry, timestamped by the file's modification time (#92). - The
claude-codeparser records the permission mode and the model as events:permission mode: <mode>at a session's firstpermission-moderecord orpermissionModeprompt key andpermission mode: <old> -> <new>on each change, andmodel: <id>at the first assistant reply andmodel: <old> -> <new>on each change, withrequested=<requestedModel>when the requested model differs. Theclaude-desktoptranscripts, read with the same reader, gain the same rows (#93). - The
claude-codeparser reads the standing permission settings: onesystemrow perpermissions.allow,deny,askandadditionalDirectoriesentry and one fordefaultModeanddisableBypassPermissionsModein.claude/settings.jsonand.claude/settings.local.json(the user's and, from a collection, a discovered project's), and one perallowedToolsentry and forhasTrustDialogAcceptedandhasClaudeMdExternalIncludesApprovedof each project in.claude.json, timestamped by the file's modification time and saying so. These files were not read before; no other key in them is read (#93). - The
claude-desktopsession-record row ends withpermission_mode=<mode> effort=<effort>, and each per-session grant in the record (alwaysAllowedReasonsandsessionPermissionUpdatesentries,chromePermissionMode,chromeAllowedDomains,cuAllowedApps,cuGrantFlags,cuFlagsGrantedAt) is apermission grant:row at the record'slastActivityAt(#93). - The
codex-cliandopen-interpreterparsers record the model and the permission policy of each turn as events:model: <id>at the firstturn_contextthat names a model andmodel: <old> -> <new>on each change, andpermission mode: approval=<approval_policy> sandbox=<sandbox_policy> profile=<permission_profile> network=<network>at the firstturn_contextwith a policy andpermission mode: <old> -> <new>on each change (-for a missing field). Codex writes no per-call approval to its rollouts, so there is no decision row (#96, #97). - The
gemini-cliandqwen-codeparsers readtrustedFolders.json(.gemini/trustedFolders.json,.qwen/trustedFolders.json): onepermission setting: trustedFolders.<path>=<TrustLevel> (from <file>, file mtime)row per folder, withproject_paththe folder and the file's modification time. A tool call whose status iscancelledorawaiting_approvalis followed by apermission decision: <status>row with itstool_use_id; the[cancelled]prefix on the result row stays.gemini-cliwritesmodel: <id>at a session's first gemini message that names a model andmodel: <old> -> <new>on each change, andqwen-codewritesmodel: <id>from the first assistant record when nosession_modelrecord named one, and a change row when an assistant record's model differs (#96, #97). - The
antigravityparser writesmodel: <id>before the rows of a conversation's first step thatgen_metadatanames a model for andmodel: <old> -> <new>before those of every later step on another model, and apermission decision: user_rejectedrow after the result of arun_commandormcp_toolcall the user rejected, with the call'stool_use_id; arun_commandresult that ran in the sandbox ends withsandbox=true(#96, #97). - The
opencodeandkilo-codeparsers record the model and the agent as events:model: <modelID> provider=<providerID>before the first message of a session that names a model andmodel: <old> -> <new> provider=<providerID>before each message that changes it, andpermission mode: agent=<name>andpermission mode: agent=<old> -> agent=<new>for the message'sagent, since an OpenCode agent carries its own permission ruleset. In a session that only the V2session_messagetable holds, themodel-switchedandagent-switchedrecords, which were printed as raw JSON before, become the change rows (#96, #97). - The
crushparser records the model as events:model: <model> provider=<provider>before the first message of a session that names one andmodel: <old> -> <new> provider=<provider>before each message that changes it (#96). - The
piparser reads.pi/agent/trust.json, the project trust decisions in the agent directory pi and little-coder share: onepermission setting: <path>=<true|false> (from .pi/agent/trust.json, file mtime)row per project, withproject_paththe project andtimestamp_utcthe file's modification time, not the time of the decision. The file was not read before (#97). - The
lettaparser adds amodel: <id> provider=<provider>row after the first.letta/sessions.jsonlline of each CLI session that names a model, andmodel: <old> -> <new> provider=<provider>when a later line of the session names another (#96). - The
continueandpearaichat-session parsers yield asystemrowmodel: <id>at a session's first model andmodel: <old> -> <new>ahead of a later history item that names a different one: Continue starts fromchatModelTitle(else the firstpromptLogs[].modelTitle) and compares each item'spromptLogs[].modelTitle, PearAI compares each item'spromptLogs[].completionOptions.model. Thecontinueparser also yieldspermission decision: accepted=<value>after eachtoolUsage.jsonlrecord'stool_resultrow andpermission decision: canceledfor a session tool call whose state iscanceled(#96, #97). - The
vscodeparser writesmodel: <id>andmodel: <old> -> <new>rows from the requestmodelId, andpermission mode: <mode>andpermission mode: <old> -> <new>rows from the requestmodeInfo.telemetryModeId(ask,agent,edit,custom). A tool invocation'sisConfirmedbecomes apermission decision: confirmed=<value>row after its result (trueorfalsebefore VS Code 1.104, else the confirmation kind such asUserAction(4)orDenied(0)with itssource,scope,id,selectedButtonorreason, andtool_source=<type>), aconfirmationresponse part apermission decision: confirmation isUsed=<bool> buttons=<list> title=<title>row, and a request sent by a confirmation button apermission decision: confirmation=<label>row (#96, #97). - The
codyparser writesmodel: <id>andmodel: <old> -> <new>rows from the assistant messagemodel, per chat, and turns eachprocesses[]entry of typeconfirmation, dropped before, into apermission decision: confirmation state=<state> title=<title>row (#96, #97). - The
twinnyparser writesmodel: <id>andmodel: <old> -> <new>rows frommeta.model, per conversation, and apermission decision: waitingorpermission decision: skippedrow (withapproval=<command|change>when recorded) after the result of a tool step saved while it waited for the user's Run or Skip, or after the user skipped it (#96, #97). - The
zedparser yieldsmodel: <model> provider=<provider>once per thread after the thread-start row, and onepermission setting: <key>=<value> (thread blob, as of updated_at)row per thread-scoped grant in the thread blob:profile, eachsandbox_grants.write_pathsandsandbox_grants.network_hostsentry, and each of thenetwork_any_host,allow_fs_write_all,unsandboxedandsandbox_fallbackflags that is set, with the thread id assession_idand the thread'supdated_atas the time (#96, #97). - The
gooseparser yieldsmodel: <model_name> provider=<provider_name>andpermission mode: <goose_mode>after each session's start row, andmodel: <old> -> <new> provider=<provider>(withrequested=<requestedModel>when the resolved model differs) before a message whosemetadata_json.inferencenames another model (#96, #97). - The
kiroparser yieldsmodel: <id>before a conversation's first history entry andmodel: <old> -> <new>before each entry whoserequest_metadata.model_iddiffers; an entry without one falls back tomodel_info.model_id(or the legacymodel) and the row endssource=model_info(orsource=model) (#96). - The
hermesparser yieldsmodel: <id>andmodel: <old> -> <new>rows from the main-loop rows of thesession_model_usagetable, at each model'sfirst_seenand withprovider=<billing_provider>, or onemodel: <sessions.model>row atstarted_atwhen the session has none (#96). - The
ollamaparser yieldsmodel: <model_name>before a chat's first assistant message that names a model andmodel: <old> -> <new>before each assistant message whosemodel_namediffers (#96). - The
openhands,agent-zeroandnanobotparsers record the model as an event:openhandswritesmodel: <agent.llm.model>after each conversation's header row;agent-zerowritesmodel: <id>before an agent's first reply andmodel: <old> -> <new>whenmetadata.responses.provider_model_keychanges, endingagent=<n>for a subordinate agent;nanobotwritesmodel: <preset> (preset)after the session start row, since the session records a model preset of its configuration, not a model id (#96). - The
openhandsparser follows a rejected call'stool_resultwith asystemrowpermission decision: rejected rejection_source=<source> <reason>, and ends an action'stool_usetext withsecurity_risk=<value>, the security analyzer's rating (#97). - The
muse-codeparser records the starting permission profile and security mode aspermission mode: approval=<a> reviewer=<r> profile=<id>(with itscauseand declaredformat) andpermission mode: security_mode=<value>, the automated reviewer's verdict aspermission decision: <outcome> kind=automated_review_completed ...with its rationale, the model asmodel: <id> provider=<provider>at the first model a session log names andmodel: <old> -> <new>on each change, and each trusted project in.config/muse/trust.json, not read before, as apermission setting: projects.<path>.decision=<decision> (from .config/muse/trust.json, file mtime)row (#96, #97). - The
claude-codepermission decision:row ends withtoolDenialKind=<value> userFeedback=<text>when the line records either key, and a line with either key but nopermissionDecisiongets the row too (#97). - The
claude-desktopparser writesmodel: <id>from each audit log'sinitline even when the transcript exists,model: <old> -> <new>in the full audit fallback, and amodel_refusal_fallback:row; adds onepermission grant:row per entry ofapprovedToolNames,webFetchAllowedUrls,egressAllowedDomains,userApprovedFileAccessPaths,enabledMcpToolsandfileDeleteApprovedMountsin a session record; ends each scheduled task row withpermission_mode=<mode> model=<model>; and readsremote-session-spaces.json, not read before, as onepermission setting:row per folder granted to a remote Cowork session, at the file's modification time (#96, #97). - The catalog copy detects the ChatGPT desktop app's renamed Windows
package,
OpenAI.Codex(its Chromium profile, log directory and settings hive), aschatgpt-desktop(#113). - The
codex-cliandopen-interpretersession start row ends withaccount=<creator_account_id> user=<creator_user_id>when the rollout'ssession_metanames the ChatGPT account and user that created the thread, as Codex Desktop inside the ChatGPT desktop app writes them; a rollout without them keeps the old text (#118). - The
codex-cliparser reads the ChatGPT desktop app's scheduled automations,.codex/automations/<slug>/automation.toml, not read before: onesystemrow per file,scheduled task: <id> name=<name> kind=<kind> status=<status> rrule=<rrule> target_thread=<thread id> created=<UTC> updated=<UTC> (from <file>, file mtime) prompt: <prompt>, at the file's modification time, withsession_idthe thread the automation runs in (#119). - The catalog copy detects the ChatGPT desktop app on macOS (its
Library/Application Support/Codexprofile, logs, preferences,HTTPStoragesand computer-use group container) aschatgpt-desktop, and attributesLibrary/Application Support/com.openai.codextochatgpt-desktopinstead ofcodex-cli(#121).
Changed¶
- The bundled catalog copy follows collector 1.12.0's project catalog: a
project-directory file is detected as the agent that owns it
(
claude-codefor.claude/andCLAUDE.md,cursorfor.cursor/,aiderfor.aider.chat.history.md), and only files several agents read (AGENTS.md,AGENTS.override.md,AGENT.md,.agents/,.mcp.json, the shared GitHub Copilot instruction files,SOUL.md,USER.md) stayproject(#104). - Files from a project directory are attributed to the agent that owns
them in
detectandinventory.json, not toproject: a repository's.claude/isclaude-code, its.aider.chat.history.mdaider. A manifest row with agentproject, as collectors before 1.12.0 wrote for every project file, is re-attributed from the bundled project catalog; a file several agents read (AGENTS.md) staysprojectand is counted as a shared file of its project. Project directories are recognised fromcollection.jsonprojects(or a row with agentproject), not from the agent, so theclaude-codeparser still gives a project's settings rows the project'sproject_path(#104). detect --json: the flat keyagents, one object per user, home and agent, is replaced byhomes[].agents, each home's agents sorted by name withagent,files,bytesandparser(0.11.0'shomeshad noagentskey);homeslists user homes and Docker volumes only, since project directories moved toprojects. Nothing in the object is namedoutside_projects. The first tabledetectprints leaves out project directories the same way. A script that read.agentsreads.homes[].agents, plus.projects[].agentsfor project files (#104).- The
agents:linetimelineprints readsparsed: <list>; detected but not parsed: <list>, withnonefor an empty list, followed by; parser found no rows: <list>when a detected agent's parser produced no rows, andnone were foundfor an input with no agent data. It used to readparsed <list>; detected only <list>, which saiddetected only nonewhen every detected agent was parsed,parsed none; detected only nonefor an empty input, and nothing about agents whose parser found no rows (#85). - The
qwen-codemodel rows take the common form: asession_modelrecord readsmodel: <id> auth=<authType>(wassession_model: <id> auth=<authType>), and alogs.jsonmodel_switchentry readsmodel: <from> -> <to> reason=<reason>(wasmodel_switch: <from> -> <to> (<reason>)) (#96). - The pi session reader, which parses
pisessions, the little-coder sessions in pi's directory and Letta's local-backendmessages.jsonl, writesmodel: <id> provider=<provider>at the first entry that names a model (amodel_changeor an assistant message) andmodel: <old> -> <new> provider=<provider>on each change, withrequested=<model>when the assistant'sresponseModeldiffers from themodelasked for. Amodel_changeentry that names the model already in force, a switch the user made, still gets the plainmodel: <id> provider=<provider>row. It replacesmodel_change: <provider>/<id>, which appeared only for amodel_changeentry, so a session whose model was named only on assistant messages had no model row. Themodelcolumn is nowresponseModelwhen the message has one (#96). - The
openclawparser writesmodel: <id> provider=<provider>at a session's first model (thesession_windowsmodel, else the firstmodel_changeor assistant message) andmodel: <old> -> <new> provider=<provider>on each change, withrequested=<model>and the repeatedmodel_changerow as above, in place ofmodel change: <provider>/<id>(#96). - The
clineparser's model rows take themodel: <id>andmodel: <old> -> <new>form. An SDK session's start row no longer ends withmodel=<id>; amodel: <id> provider=<provider>row follows it (also for asessions.dbindex row), and a message whosemodelInfo.iddiffers yieldsmodel: <old> -> <new> provider=<provider>. The legacymodel_usage: model_id=...rows fromtask_metadata.jsonbecomemodel: <id> model_provider_id=<p> mode=<m>at the first entry and on a change ofmodel_id, andpermission mode: <mode>andpermission mode: <old> -> <new>rows carry the entry'smode; an entry that changes neither has no row (#96, #97). - The
muse-codeapproval decision row readspermission decision: <decision> source=<decision_source.kind> policy_result=<p> amendment=<a>, where it readapproval <decision> by <source>; and the/modelrow readsmodel: <old> -> <new>, preceded bymodel: <previous>when the log named no model before it, where it readmodel: <new>(#96, #97).
Fixed¶
tools/proto_descriptors.py extractno longer fails with aTypeErroron Mach-O binaries such as the macOS builds ofagyand the Antigravity desktop app's language server (#86).- The
antigravityparser reads the path of acode_actionstep that deletes a file:ActionSpecDeleteFilekeeps the path in field 1, where the parser had read field 2, so thetool_resultrow lost the path or the step could not be decoded (#88). tools/shape.pyredacts OpenAI user and organisation ids (user-ororg-and 20 or more letters and digits) as<user-id>and<org-id>in keys and paths, awin32:/...path key and an absolute path after a<prefix>:in a key (local:C:\...) as<abs-path>, and the elements of a key that is a JSON-encoded array, which it printed whole before: Codex Desktop's.codex-global-state.jsonkeys state by arrays holding the user id and absolute file paths (#117).
Removed¶
timelineno longer writesdetect.json; itskind,notes,problemsand per-home agent table are ininventory.jsonaskind,notes,problemsandhomes(#104).
0.11.0 - 2026-10-12¶
Added¶
- The bundled catalog copy follows collector 1.11.0: Claude Desktop's
MSIX data directories under
AppData/Local/Packages, andAppData/Local/ClaudeandAppData/Local/Claude-Data, are detected asclaude-desktop(#73). -
The bundled catalog copy follows collector 1.11.0: the Claude Code CLI cache directory (
Library/Caches/claude-cli-nodejs,.cache/claude-cli-nodejs,AppData/Local/claude-cli-nodejs/Cache/*), which holds per-project MCP server logs, is detected asclaude-code(#81). -
A
claude-desktopparser: Claude Desktop's Cowork and Code-tab transcripts (Claude Code JSONL under the app-data directory, with the project path taken from the session record), each Cowork session'saudit.jsonl(permission requests, responses and automatic decisions, and every turn when the transcript is missing), session records, scheduled tasks and Code-tab worktrees. The rows come out under agentclaude-desktop(#48). - The bundled catalog copy follows collector 1.10.0: the agents
autogen-studio,camel-ai,crewai,dify,flowise,langflow,metagpt,n8nandpydantic-clai, the project entry.clai, the legacy Open Interpreter directories underopen-interpreter, and the Dify, Langflow and n8n Docker volume patterns are detected; none is parsed yet (#64). - Claude Code 2.1.295 permission decisions: a tool-result line that
records how the call was allowed or denied gets a
systemrow after its result,permission decision: <decision> source=<source> reason=<reasonType>, carrying the call'stool_use_id(#76). - Claude Code's per-process registry
~/.claude/sessions/<pid>.jsonis read: onesystemrow per process at its start time,claude-code process <pid> <kind> status=<status> entrypoint=<entrypoint> bridge=<id or none> host_session=<id or none>, joined to the session. Abridge=id means the process was under Remote Control when the collection ran; check it against the EDR's process list, since a crashed process can leave its file behind (#80). - Claude Desktop Code-tab session rows end with the session's persisted
Remote Control state,
remote_control=<bool> bridges=<n> spawned_remotely=<bool> steered_remotely=<bool>, and a session that was under Remote Control when the app quit to update gets aremote control live at update quitrow at that time (#80).
Fixed¶
- The detection test for the framework agents passes on a case-insensitive filesystem (macOS): the two spellings of CrewAI's Windows directory are built in separate homes instead of one (#75).
0.10.0 - 2026-10-06¶
Changed¶
- The analyzer is now
doubleagent:pip install .installs the distributiondoubleagent(wascoding-agent-analyzer) with the commanddoubleagent(wasanalyze-agent-artifacts), and the Python package isdoubleagent(wasagent_analyzer), sopython3 -m doubleagentruns it from a checkout.--versionprintsdoubleagent <version>. The output files and their fields are unchanged (#47). zstandardis a required dependency, imported at start-up likepython-dateutil. Thesystemrow that said "zstandard package not installed" for each compressed Zed thread, Codex or Open Interpreter rollout and OpenClaw row is gone (#47).pyproject.tomlreads the version fromdoubleagent.VERSION, so it is set in one place (#47).
0.9.0 - 2026-10-06¶
Added¶
- An
ollamaparser for the Ollama desktop app's chat database (Library/Application Support/Ollama/db.sqliteon macOS,AppData/Local/Ollama/db.sqliteon Windows, schema versions 16 to 19) and theollama runprompt history.ollama/history. Tool calls and results are paired by position, withtool_use_idset to<chat id>:<tool_calls.id>; attachments appear as their name and size only, and the account and settings tables are not read. History rows have no timestamp or session, so they sort last and are dropped by--sinceand--untilunless--keep-undatedis given (#43).
Changed¶
- The bundled catalog copy follows the collector: the Ollama exclusion is
now
.ollama/models/blobs, so.ollama/models/manifestsandmodels/metadatafiles appear in collections (#44).
Fixed¶
- Timestamps with a fraction of one, two, four or five digits, such as
go-sqlite3's
2026-03-01 09:00:00.5-08:00, are converted on Python 3.10 as well; before, they gave an emptytimestamp_utcthere (#45). tests/run.shruns only the named tests when given a module, class or method name (tests/run.sh test_catalog,tests/run.sh test_parsers.ClaudeCodeTests.test_rows); before, it passed the name tounittest discover, which ignored it and ran the whole suite. With no names it still runs discover with the options given, and with names it refuses the discover-only options-p,-sand-t(#34).
Removed¶
- The bundled catalog copy drops the collectors' unused credential pattern
.claude-*/.device-keys.json; detection is unchanged (#41).
0.8.1 - 2026-10-05¶
Added¶
detectprintsproblem:lines too, anddetect --jsonanddetect.jsonhave a new last keyproblems, an array of the problem texts; indetect.jsonit also holds the files a parser could not read (#37).
Fixed¶
- An input that cannot be opened, including a missing path, an archive
that is not readable, an archive inside a directory that cannot be
traversed, an input directory that cannot be listed and an unreadable
manifest.jsonl, now printserror: <path>: <reason>with the OS reason in lower case (no such file or directory,permission denied) and exits 2 underdetect,timelineandinventory. Before, it printed only the path, or a Python traceback for permission errors. A corrupt or truncated archive printserror: <path>: cannot extract: <reason>instead of a traceback, and the extracted work directory is removed on every such error (#37). - A directory inside a loose tree that cannot be listed is now a
problem:line instead of being skipped silently (#37). - A manifest row whose file is absent from the extracted collection is now
one
problem:line,<original path>: missing from the collection(#37). - Problem lines and the
collection.json unreadablenote name the path once:<path>: permission deniedinstead of<path>: [Errno 13] Permission denied: '<path>'. The Continue and Codexparser: unreadable ...system rows carry only the reason, sincesource_filenames the file (#37).
0.8.0 - 2026-10-04¶
Added¶
timeline --since WHENand--until WHENkeep only the rows inside a UTC time window,--keep-undatedalso keeps rows without a timestamp, and--match REGEX(repeatable,-ifor case-insensitive) keeps rows whosetextmatches, with the paired tool call or result.WHENis an ISO 8601 date or time (a space may replace theT, and an offset may follow after a space), a year or month, an epoch number,today,yesterday,now, or a relative time such as45m,36h,3d,2w,6mo,1y. A value names a period to its last written unit, so--since 2026-10-01 --until 2026-10-01is that whole day. The resolved window and the dropped-row counts are printed.sessions.jsonlis summarised from the kept rows.python-dateutilis a new required dependency, used for--sinceand--until; reinstall withpip install -r requirements.txt.- The analyzer README has recipes for filtering a written timeline by time with jq, DuckDB, Python and PowerShell.
Changed¶
timelinewritestimeline.jsonlandsessions.jsonlinstead oftimeline.csvandsessions.csv: one JSON object per line, UTF-8, LF line ends, every field present in the old column order,""for no value. Interface change:source_line,user_turns,assistant_turnsandtool_callsare JSON integers, andmodelsis an array of strings instead of a space-separated string. Field names are unchanged.- The
textfield keeps the event's line breaks and indentation, so tool output, diffs and multi-line prompts read as they were written; only leading and trailing whitespace is removed. Before, whitespace runs were collapsed to one space. - The analyzer README has a section on reading and searching large JSONL timelines with jq, grep, DuckDB, Python and PowerShell.
Removed¶
--max-text-length.textis always written in full; cut it while reading, for example with.text[0:200]in jq.- The
shell-historycatalog agent, following collector 1.7.0, which no longer collects shell history. Loose input no longer attributes.bash_history,.zsh_historyand the other shell history files to an agent. - The
shared|.envandproject|.envcatalog entries, following collector 1.7.0. Loose input no longer attributes a home or project.envfile tosharedorproject.
0.7.0 - 2026-10-04¶
Added¶
- Claude Code: sessions and
history.jsonlin a config home moved withCLAUDE_CONFIG_DIRto a.claude-<name>directory (such as~/.claude-work) are parsed, and the bundled catalog copy detects them;~/.claude-code-routerand other.claude-tools are not. - Claude Code: a tool result stored as a
<persisted-output>stub now carries the full output from the session'stool-results/*.txtfile; when the file is missing, the stub is kept with a[persisted output not found: ...]note. - Claude Code: each subagent transcript opens with a
systemrowsubagent <agent id> of <session id>. - Claude Code:
session title:rows forai-title,custom-titleandagent-namerecords,cwd changed:rows forrelocatedrecords (later rows take the newproject_path),hook:rows for hook system messages andedited file:rows foredited_text_fileattachments (the path only, not the snippet).
Changed¶
- Claude Code: the Bash
tool_usetext is the command the model sent, with itscd <dir> &&prefix, instead of the normalised one. - Claude Code: other injected context (
isMetarecords) starts withcontext:, andsystemrecords withoutcontent(api_error,memory_saved, ...) show their fields as JSON after the subtype. - Open Interpreter rollouts are read by the same code as Codex CLI's, so every Codex CLI change below applies to them too.
- Codex CLI: a rollout that holds an ancestor's
session_meta(a copied fork) keeps the file's own session id, project and branch; the ancestor's record is asystemrowcopied from ancestor: ..., and a fork gets aforked from <id>row. - Codex CLI: a non-zero exit code from a command's
item_completedrecord prefixes that call'stool_resulttext as[exit N]. - Codex CLI: a subagent rollout starts with a
systemrowsubagent <id> of <parent id>(with its agent path and role), and its first prompt, the task from the parent, is asystemrowsubagent task: ...instead ofuser. Messages copied from the parent (inherited_user_message) are no longer repeated in the subagent's rows.
Fixed¶
- Claude Code: prompts typed while a turn was running and delivered as
queued_commandattachments are nowuserrows; they were missing. Theenqueuethat repeated a delivered prompt's text is nowprompt queued: delivered at line N, keeping the time it was typed; anenqueuewith no delivery in the file is stillqueue enqueue: <prompt>. - Claude Code: a subagent's task, task notifications, slash commands and
their output, peer and coordinator messages, interruption markers and
compaction summaries were
userrows; they are nowsystemrows with a label (subagent task:,task notification:,slash command:,command output:,peer message:,coordinator message:,interrupted:,compaction summary:). - Claude Code: local API errors (model
<synthetic>) wereassistantrows; they are nowsystemrowsapi error: <status> <error>: <text>. - Claude Code: empty thinking blocks (signature only) no longer produce
[redacted]thinking rows; onlyredacted_thinkingblocks do. - Claude Code:
pr-linkrows, re-written on every metadata flush, appear once per session and URL, and with the session's project and branch, like the queue rows. - Claude Code: tool results that list tools (
tool_referenceblocks) show the tool names instead of empty text. - Codex CLI: user-role messages the harness injects (environment context,
AGENTS.md and skill instructions, subagent notifications, user shell
commands) were
userrows; they are nowsystemrowscontext: <kind>: ..., by content kind where the rollout records one (current releases) and otherwise by the harness's own wrapper around the block (<environment_context>,# AGENTS.md instructions,<skill>and others), souserrows anduser_turnscount only what the person typed. A tag typed inside a prompt does not change its type. - Codex CLI: compaction summaries, messages between agents
(
inter_agent_communication,agent_message), image generation prompts, tool searches, realtime voice transcripts, aborted and rolled-back turns, anditem_completedturn items that no other record carries (web searches inside code-mode calls, MCP and dynamic tool calls, plans) were dropped; they now produce rows. - Codex CLI: a reverted thread's rollout
(
rollout-<time>-<thread id>_<rollout id>.jsonl) cut before itssession_metatook the rollout id as session id; it now takes the thread id. - Codex CLI: a reasoning item with no summary text was a
thinkingrow with empty text under--include-thinking; it is now skipped.
0.6.0 - 2026-10-04¶
Added¶
- Muse Code parser (
muse-code): session logs under.local/share/muse/sessions/and their subagent logs (user prompts, assistant replies, tool calls and results, approval requests and decisions, failed runs; reasoning summaries with--include-thinking), and thetui-history.jsonlprompt history. - The catalog copy detects
muse-code(Meta's Muse Code CLI).
Changed¶
- Minimum Python raised from 3.9 to 3.10.
Fixed¶
- OpenClaw: a
transcript_eventsrow whoseevent_jsonis valid JSON but not an object (such asnull) no longer stops the parser withUnboundLocalError; it becomes asystemrow, "event seq N unreadable: not a JSON object", and the session's later events are still read.
0.5.0 - 2026-10-04¶
Added¶
inventorycommand: merges the saved stdout of collector 1.6.0--inventory/-Inventoryruns (files, or directories of files) into one CSV,fleet-inventory.csvunless-o FILEis given.- New CSV interface,
fleet-inventory.csv, columnshost,collector,mode,at,user,agent,files,bytes,first,last,projects,evidence; times normalised to UTC with milliseconds. A host with no agents gives one row with the agent columns empty. - Console junk lines in a capture are skipped and counted on stderr.
- A per-agent rollup on stdout after the CSV: hosts, users and latest
last. - The catalog copy carries collector 1.5.0's
DOCKER_VOLUMEStable; the reader keeps those lines in their own list instead of the secret list.
Changed¶
- The README says the
livepseudo-agent appears only in collections made by collectors before 1.6.0, which removed the live snapshot.
0.4.0 - 2026-10-04¶
Added¶
- Transcript parsers for fourteen more agents:
tabby,openhands,shellgpt,pi,little-coder,letta,hermes,agent-zero,open-interpreter,openclaw,nanobot,cody,twinnyandpearai, for thirty parsed agents in all. little-coder and Letta Code sessions are read with the pi parser's reader, and Open Interpreter rollouts with the Codex one. reads_agentsrouting: a parser can name other catalog agents whose files it is also offered. Cody and Twinny use it to read their rows of thestate.vscdbthat the catalog attributes tovscodeor to a fork (cursor,windsurf,pearai,kiro,antigravity); the rows come out undercodyortwinny, and--agent codyselects them.agent_analyzer/vscode_state.py, a helper that reads one extension's global state out of a VS Code familystate.vscdbthroughsqlite_util, never readingsecret://rows.- Detection of the fifteen agents added in collector 1.4.0:
pearai,cody,twinny,tabby,open-interpreter,openhands,pi,little-coder,letta,hermes,openclaw,nanobot,agent-zero,shellgptandlocal-deep-research, from the regenerated catalog copy.
Changed¶
- The Codex parser now also reads archived rollouts under
archived_sessions/and zstd-compressed.jsonl.zstrollouts (needszstandard; without it each compressed rollout is onesystemrow). Its rollout reader is now the reusableRolloutParserbase class.
0.3.1 - 2026-10-04¶
Fixed¶
- The
timelinesummary no longer listsprojectamong the agents that were detected but not parsed.
0.3.0 - 2026-10-03¶
Added¶
- Transcript parsers for Gemini CLI, Qwen Code, Kiro (Amazon Q Developer CLI conversations), Crush, Goose, Zed, VS Code chat sessions, Continue, Aider, OpenCode, Kilo Code, Cline and Roo Code, for sixteen parsed agents in all.
- Dependency on the
zstandardpackage for Zed threads; without it each Zed thread becomes one undecodablesystemrow and everything else runs.
Changed¶
- Files the collector found inside a discovered repository (agent
projectin the manifest) are offered to every parser, so a repository's.crush/crush.dband.aider.*files are parsed and their rows carry the real agent.--agentfilters on that agent, so--agent aiderincludes repository-level Aider files. detectno longer reportsprojectas a parsed agent.
0.2.0 - 2026-10-03¶
Added¶
- Antigravity CLI parser for its SQLite conversation databases, decoded from the protobuf schemas embedded in the shipped binary. SQLite stores are read from a copy taken with their WAL and SHM sidecars, so the evidence copy is never opened and uncheckpointed rows are kept.
- Detection of the catalog entries added in collectors 1.2.0 and 1.3.0.
Changed¶
timeline.csv: thesummarycolumn is renamedtext, in the same position, and holds the event's full text by default.--max-text-length Nreplaces--summary-length Nand defaults to0, no cut; the old default cut at 400 characters.- In loose mode a nested catalog entry claims its subtree, as in the
collectors, so Antigravity CLI files under
~/.geminiare attributed toantigravity.
0.1.0 - 2026-10-03¶
Added¶
detect,timelineandcatalogcommands, reading a collector archive, an extracted collection, or a loose directory such as a mounted image, a copied home or a single agent directory.- Detection of every agent in the collectors' catalog. Host, user and agent come from the manifest when present; for loose input they are inferred from paths and marked as inferred.
- Parsers for Claude Code (session transcripts, subagent transcripts,
history.jsonl) and Codex CLI (rollouts,history.jsonl). timeline.csvcolumns:timestamp_utc,host,user,agent,session_id,project_path,git_branch,turn_type,model,tool_name,tool_use_id,summary,source_file,source_line.sessions.csvcolumns:host,user,agent,session_id,project_path,first_timestamp_utc,last_timestamp_utc,models,user_turns,assistant_turns,tool_calls,source_file.- Options
--host,--user,--work-dir,--keep-extracted,--summary-length(default 400),--include-thinkingand--agent;detect --jsonand--files;catalog --agents.