Skip to content

feat: add Goose parser - #69

Merged
adityajha2005 merged 2 commits into
Entelligence-AI:mainfrom
Monika-rolls:feat/goose-parser
Nov 4, 2026
Merged

adityajha2005 merged 2 commits into
Entelligence-AI:mainfrom
Monika-rolls:feat/goose-parser

Conversation

@Monika-rolls

@Monika-rolls Monika-rolls commented Nov 2, 2026 •

Copy link
Copy Markdown

Summary

Adds a Goose parser. Goose 1.10+ keeps sessions in SQLite (<data-dir>/sessions/sessions.db), so this adds a decoder to materialize.py next to the OpenCode one, plus parsers/goose.py.

  • Decoder: Goose holds the WAL-mode db open, so it takes a consistent snapshot through SQLite's backup API (source opened read-only), falling back to copying the db with its -wal/-shm. Only existing columns are selected, so older schemas decode. Each session becomes one cached NDJSON: a goose_session header, then its messages, each usage_ledger row placed after the assistant message it bills. Subagent and hidden sessions are skipped (main lane only).
  • Parser: a user message with text opens a turn; messages Goose hides from the user (turnContext, compaction summaries) don't. Tool failures come from Goose's own flags (status: "error" / isError), not output text. Tokens come from the usage Goose stamps on each assistant message (metadata.usage), which ties each request to its turn; usage_ledger adds compaction requests, and is the fallback for older sessions. input_tokens already includes cache reads and writes.
  • Tools: shell→Bash, write, edit, tree→ListDir, delegate→Task, plus the older developer__shell and developer__text_editor (classified by its command).
  • Discovery: ~/.local/share/goose (Linux/macOS, XDG), %APPDATA%\Block\goose\data (Windows), $GOOSE_PATH_ROOT/data when set; override with AGENTPLAY_GOOSE_HOMES. README tables updated.

Closes #66

Test plan

  • Ran each make check step by hand (no make on this Windows machine): ruff check src tests passes, pnpm run typecheck passes, scripts/check_clean.py reports clean, viewer tests 354/354 pass.
  • pytest -q: 1485 passed, 10 failed. 8 are Windows symlink-permission tests (test_fixes_safety.py, test_autotune_lifecycle.py, test_robustness.py::test_store_never_reads_outside_maps, test_search_health_fixes.py) that fail the same way on main without this change; 2 are test_robustness.py malformed-host/origin server tests that fail intermittently on this machine with and without this change. CI passed on every platform.
  • test_goose_parser.py (43 tests): builds a db with Goose 1.57's fresh-install DDL and decodes it (rows still in the WAL, an older schema, an unreadable db, skipped session types); parses three redacted real sessions (Goose 1.57, Windows, openai); asserts no placeholder text, path or delegate instruction reaches the built map. Review fixes each have a regression test (usage billed to the right turn when the next prompt shares its second, a first message past the 5 MB head, old_str/new_str normalization, user-only result text, epoch-zero dates, isError alone, the snapshot fallback); all but the last two fail without the fix.
  • End to end on real Goose 1.57 sessions on Windows: agentplay agents lists Goose; agentplay build --agents goose built 4 sessions with 0 failures. After the review fixes, all four sessions' map tokens and cost match Goose's own usage_ledger exactly (e.g. 396,280 tokens / $0.0086 on a 5-turn session). Checked its map against every string in the session's messages: none leaked.
  • Not tested: Goose on Linux/macOS (the path follows Goose's etcetera XDG strategy, covered by a unit test only); legacy per-session JSONL files (Goose imports them into the db itself).

Known builder gaps seen with Goose on Windows, not changed here: git commit at the end of a cmd & chain isn't counted as a commit, and findstr/Select-String count as exec rather than search.

  • make check passes (its steps run individually; see above for the pre-existing Windows-only failures)
  • No prompt text, model output, tool input/output, file contents or command arguments can reach a map, a log line or the viewer (redaction contract)
  • No new network calls or Python runtime dependencies

@entelligence-ai-pr-reviews

entelligence-ai-pr-reviews Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

EntelligenceAI PR Summary

flowchart TD
   classDef newBehavior fill:#dcfce7,stroke:#16a34a,color:#14532d;
   A["Goose data directory"] --> B["Snapshot sessions.db"]
   B --> C["Synthesize session NDJSON"]
   C --> D["Discover and index sessions"]
   D --> E["Parse messages, tools, and usage"]
   E --> F["Session, turn, and tool spans"]
   class A,B,C,D,E,F newBehavior;
Loading

🟢 Green = new or changed in this PR


Review Scorecard

Dimension Rating Basis
Code Quality ●●●●● 5/5 — Excellent no critical/significant findings — rated Excellent
Blast Radius Low _blocks is referenced in 1 other file(s), 8 file(s) / ~1003 line(s) changed (size only — not a blast signal)
Merge Confidence ●●●●● 5/5 — Safe to Merge equals code quality; blast radius is shown for context only
Test Coverage — unavailable dependencies failed to install
Security Scan No findings on changed lines SonarQube · Semgrep · TruffleHog on 4 changed files

Issues found:

  • Unresolved python/tests/core/fixtures/goose/failed_shell.jsonl — Add a flag-only failure case (unresolved from an earlier review)

Safe to merge with a low blast radius; code quality rated Excellent (5/5): the review found nothing to flag. 1 comment from earlier reviews is still open.

Evaluated against
  • 4/4 changed files reviewed
  • criteria: correctness, security & access control, robustness & error handling, concurrency & data integrity, repo conventions / steering docs
  • steering docs: none found in repo
  • PR checks: unit tests (unavailable) · security scan (SonarQube, Semgrep, TruffleHog on 4 changed files)

@entelligence-ai-pr-reviews

entelligence-ai-pr-reviews Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Walkthrough

Adds end-to-end local ingestion for Goose coding-agent sessions. Goose SQLite databases are snapshotted and materialized as NDJSON, discovered from configured platform-specific home directories, and parsed into session, turn, and tool spans with usage and edit evidence. The update also documents Goose support and adds fixtures and tests for schema variations, filtering, parsing, tool aliases, and discovery.

Changes

Files

  • python/src/agentplay/materialize.py
  • python/src/agentplay/_core/ingestion/coding_agent/_sources/local/source.py
  • python/src/agentplay/_core/ingestion/coding_agent/_sources/local/parsers/goose.py
  • python/src/agentplay/_core/ingestion/coding_agent/_sources/local/parsers/base.py
  • python/src/agentplay/_core/ingestion/coding_agent/_sources/local/parsers/__init__.py
  • python/src/agentplay/_core/ingestion/coding_agent/tool_taxonomy.py

Adds Goose database snapshotting and transcript materialization, local session indexing, and parser support for sessions, turns, tool calls, usage, costs, timestamps, and edit evidence. Registers Goose agent detection and normalizes supported current and legacy tool names.

Files

  • python/src/agentplay/discover.py
  • README.md

Registers Goose in discovery, including platform-aware home defaults, environment overrides, candidate materialization, and cache selection. Documents local session reading, skipped hidden and subagent sessions, database locations, and AGENTPLAY_GOOSE_HOMES.

Files

  • python/tests/core/fixtures/goose/edit_file.jsonl
  • python/tests/core/fixtures/goose/failed_shell.jsonl
  • python/tests/core/fixtures/goose/list_and_create.jsonl

Adds redacted JSONL transcripts covering edit and shell workflows, tool failures, message metadata, usage records, and session attributes.

Files

  • python/tests/core/test_goose_parser.py
  • python/tests/core/test_parser_robustness.py
  • python/tests/core/test_tool_taxonomy.py

Adds coverage for Goose database schema compatibility, snapshots, filtering, caching, parsing, usage attribution, failures, privacy, and discovery. Extends shared parser robustness and verifies Goose tool aliases and alias-table size.

Comment thread python/src/agentplay/materialize.py Resolved
Comment thread python/src/agentplay/materialize.py Outdated
Comment on lines +1742 to +1744
shutil.copy2(db, copy)
for suffix in ("-wal", "-shm"):
side = db.with_name(db.name + suffix)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR RACE_CONDITION Use a consistent snapshot for the live Goose database

If Goose commits or checkpoints during these separate copies, the copied database and WAL can describe different snapshots, causing SQLite to reject the copy or materialization to omit committed messages. Read from a consistent SQLite snapshot instead.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In python/src/agentplay/materialize.py:1742-1747, avoid copying the live Goose database and its WAL/SHM sidecars independently. Read through a consistent SQLite snapshot or backup API, then materialize from that snapshot so concurrent Goose writes cannot produce a torn database/WAL pair.

Comment thread python/src/agentplay/materialize.py Resolved
{"type":"goose_message","id":"msg_1","role":"user","created":1793637899.0,"content":[{"type":"text","text":"PLACEHOLDER-TEXT-1"}],"metadata":{"userVisible":true,"agentVisible":true}}
{"type":"goose_message","id":"msg_2","role":"user","created":1793637901.0,"content":[{"type":"text","text":"PLACEHOLDER-TEXT-2"}],"metadata":{"userVisible":false,"agentVisible":true,"turnContext":true}}
{"type":"goose_message","id":"resp_1","role":"assistant","created":1793637902.0,"content":[{"type":"toolRequest","id":"call_1","toolCall":{"status":"success","value":{"name":"shell","arguments":{"command":"python ARG-1"}}},"_meta":{"goose_extension":"developer"}}],"metadata":{"userVisible":true,"agentVisible":true,"inference":{"provider":"openai","requestedModel":"gpt-6-luna"},"usage":{"inputTokens":5116,"outputTokens":29,"totalTokens":5145,"cacheReadTokens":0,"cacheWriteTokens":5113,"cost":0.000653925,"costSource":"estimated","elapsedMs":1166,"timeToFirstTokenMs":1166}}}
{"type":"goose_message","id":"msg_3","role":"user","created":1793637902.0,"content":[{"type":"toolResponse","id":"call_1","toolResult":{"status":"success","value":{"resultType":"complete","content":[{"type":"text","text":"PLACEHOLDER-OUT-1\n\nCommand exited with code 3","annotations":{"priority":0.0}}],"structuredContent":{"stdout":"","stderr":"","exit_code":3},"isError":true}}}],"metadata":{"userVisible":true,"agentVisible":true}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT TEST_COVERAGE Add a flag-only failure case

This result has both isError: true and Command exited with code 3, so the test still passes if failure detection is based on output text instead of Goose's flag.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In python/tests/core/fixtures/goose/failed_shell.jsonl:5, preserve the real-session fixture, but add a synthetic regression case in python/tests/core/test_goose_parser.py where `isError` is true and the output contains no exit-code text; assert that the parsed tool status is ERROR.

Read Goose sessions from its SQLite store, <data-dir>/sessions/sessions.db
(~/.local/share/goose on Linux and macOS, %APPDATA%\Block\goose\data on
Windows; honours GOOSE_PATH_ROOT; override with AGENTPLAY_GOOSE_HOMES).

The db is WAL-mode and live while Goose runs, so the decoder copies it with
its -wal/-shm and reads the copy read-only, selecting only columns that exist
so older schemas still decode. Each session becomes one NDJSON transcript in
the cache: a goose_session header, then messages and per-request usage rows
in time order. Subagent and hidden sessions are skipped (main lane only).

Goose records tool failures itself (toolResult status "error" or isError), so
no output text is parsed for them. Messages Goose hides from the user
(turnContext, compaction summaries) open no turn. The older
developer__text_editor is classified by its command.

Fixtures are three real Goose 1.57 sessions, redacted.

Closes Entelligence-AI#66
- Read the live sessions.db through SQLite's backup API, so a commit or
  checkpoint by a running Goose cannot tear the db and its WAL; fall back
  to copying the files when it cannot be opened read-only.
- Bill each request to its turn: use the usage Goose stamps on the
  assistant message (metadata.usage) and keep only compaction rows from
  usage_ledger; for older sessions place each ledger row after the last
  assistant message at or before its time.
- Stream the whole transcript when checking for messages, so a first
  message larger than the 5 MB head still indexes.
- Normalize developer__text_editor old_str/new_str/file_text, drop
  user-only result text, and keep epoch-zero timestamps.

@entelligence-ai-pr-reviews entelligence-ai-pr-reviews Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entelligence: code quality 5/5 — Excellent.

@entelligence-ai-pr-reviews

Copy link
Copy Markdown
Also noticed in code this push did not change at `35ea6cf` (1, not blocking)

These lines were already in the PR at the last review, so they are listed here instead of opening new threads.

  • Remove cache entries for sessions absent from the database (python/src/agentplay/materialize.py:1811, MAJOR)

@adityajha2005

Copy link
Copy Markdown
Contributor

thanks @Monika-rolls for the contribution! LGTM

@adityajha2005
adityajha2005 merged commit b5fcffe into Entelligence-AI:main Nov 4, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a parser for Goose

3 participants