feat: trace recorder for inference / system / tools + content provenance#21
Merged
Conversation
Adds session.Recorder, a synchronous observer on core.Agent's event bus that translates lifecycle events into transcript records. Wired via core.Config.OnEvent (new BuildParams.OnEvent passthrough; constructed by Session.NewRecorder at agent start). New records per turn: inference.requested digests of system prompt / tool list / message chain inference.responded stop reason, latency, token usage System observability: Use(sec, caller), Drop(name, caller) on core.System require explicit caller strings (system:init, command:identity, subagent:init). System.SetObserver replays existing sections on attach so the event chain is complete from t0. Recorder writes system.section.added/removed. Tools observability: Same pattern on core.Tools: Add/Remove gain caller, SetObserver replays. MCP registrations pass caller="mcp:<toolname>". Both wrappers (permissionTools, progressTools) pass-through. Recorder writes tools.added (with schema) / tools.removed (with name). Content provenance: splitTextByProvenance splits user-message content on <system-reminder> boundaries and tags those blocks with Source="reminder". Round-trip safe: extractUserContent concatenates back, preserving core.Message.Content byte-for-byte. Non-blocking telemetry: agent.emitTelemetry uses select-default on the outbox so observer-fired events never deadlock the agent goroutine on a slow TUI consumer. Tests: 4 new Recorder tests + 2 provenance tests.
238e838 to
e714a07
Compare
NewRecorder now calls FileStore.Start synchronously before returning, so
session.started lands on disk first. Previously the call order
1. app builds Recorder
2. core.NewAgent wires System/Tools.SetObserver
3. SetObserver replays existing members → AppendSystemSection/AppendTools
creates the transcript file
4. Store.Save → Start → file exists, no-op
left provider/model/parentID metadata absent from every transcript that
involved any observer replay (i.e. every main-agent session). Resume
and the projects index would show empty Provider/Model.
Fix lives in the recorder constructor because that's the latest point
before observers can fire. RecorderOptions gains Cwd and ProjectID so
Start can be called with the right metadata. Setup.NewRecorder threads
them through from Store.cwd/Store.projectID.
Test: TestRecorder_WritesSessionStartedBeforeTelemetry asserts that
after NewRecorder() returns and an OnSystemChange event is fired, the
first record on disk is session.started with provider+model populated.
Member
Author
|
Confirmed and fixed in 606aa68 (force-pushed; #22 and #23 rebased). The bug was exactly as described:
Fix: `NewRecorder` now calls `FileStore.Start` synchronously before returning. `RecorderOptions` gains `Cwd` + `ProjectID` so it has enough to populate the start record. `Setup.NewRecorder` threads them through from the `Store`. `Start` remains idempotent, so the later `Save` call is still a safe no-op. Test: `TestRecorder_WritesSessionStartedBeforeTelemetry` (in `recorder_lifecycle_test.go`) — constructs a recorder, fires `OnSystemChange`, asserts the first on-disk record is `session.started` with provider+model populated. This test failed before the fix and passes after. All 5 stack branches rebuilt + verified independently. |
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 3 of 5 — stacked on #20.
Adds the structured event records that make sessions traceable.
`session.Recorder` subscribes to `core.Agent.OnEvent` and writes:
Wiring: `core.Config.OnEvent` exposed via new `agent.BuildParams.OnEvent`. Non-blocking `emitTelemetry` so observer events don't deadlock the agent goroutine.
Test
🤖 Generated with Claude Code