agent-trace: migrate diff capture to message.updated and add model attribution#42
agent-trace: migrate diff capture to message.updated and add model attribution#42ivke995 wants to merge 8 commits into
Conversation
📝 WalkthroughWalkthroughThis PR extends the agent trace pipeline to capture and track model provenance ( ChangesModel ID Provenance Through Agent Trace Pipeline
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
0d08c52 to
25153cf
Compare
….updated The agent-trace plugin previously captured session.diff events, but the upstream OpenCode API no longer emits this event reliably. Switch to message.updated events, which carry the same diff information per user message. Key changes: - Listen for message.updated instead of session.diff - Extract diffs from properties.info.summary?.diffs[].patch instead of properties.diff[] - Filter to user messages only (info.role === user) - Fall back sessionID to unknown when info.sessionID is absent - Use patch-only extraction (no diff field fallback needed for FileDiff) - Bump @opencode-ai/plugin from 1.3.0 to 1.14.28 for message.updated support Generated plugin outputs regenerated via Pkl; context documentation updated to reflect the new event contract. Plan: agent-trace-plugin-message-updated (T01, T02, T03) Co-authored-by: SCE <sce@crocoder.dev>
Capture the OpenCode model identifier in emitted diff-trace payloads and add the nullable diff_traces.model_id migration so later Rust hook wiring can persist it without changing recent-patch reads. Plan: add-diff-traces-model-id Updated tasks: T01, T02 Co-authored-by: SCE <sce@crocoder.dev>
Register the model_id migration and extend DiffTraceInsert plus the diff_traces INSERT statement so AgentTraceDb writes nullable model identifiers with captured diff traces. This is the DB-layer slice; hook payload parsing remains in the next task. Plan: add-diff-traces-model-id Task: T03 Co-authored-by: SCE <sce@crocoder.dev>
Parse the OpenCode-provided model_id from diff-trace payloads and pass it through to AgentTraceDb inserts so stored diff_traces retain model attribution. Plan: add-diff-traces-model-id Task: T04 Co-authored-by: SCE <sce@crocoder.dev>
…plan - Mark T05 (Validation and cleanup) as done with evidence - Add execution record: pkl-check-generated passed, nix flake check passed - Append validation report confirming all 6 success criteria met - Clean stale context/tmp/ artifacts (157 files removed) - Confirm no new test files remain from this plan Plan: add-diff-traces-model-id Task: T05 Co-authored-by: SCE <sce@crocoder.dev>
Co-authored-by: SCE <sce@crocoder.dev>
…ributor output Carry nullable diff_traces.model_id through recent patch loading into PatchHunk provenance, preserve it across patch combine/intersect operations, and emit contributor.model_id for ai/mixed conversations when available. This keeps attribution metadata intact from stored diff traces to generated Agent Trace payloads while preserving deterministic patch shaping and serialization behavior. Co-authored-by: SCE <sce@crocoder.dev>
7853cc8 to
37e5071
Compare
Update Agent Trace docs to reflect that the minimal builder is consumed by the active post-commit hook flow, with contributor model provenance and validated AgentTraceDb persistence documented consistently. Co-authored-by: SCE <sce@crocoder.dev>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.opencode/plugins/sce-agent-trace.ts (1)
73-81:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEmpty patches are collected, potentially producing empty diff content.
The loop pushes
entry.patch || ""unconditionally, so entries with empty or missingpatchfields contribute empty strings. If all diff entries have empty patches, the finaldifffield will be an empty string (or just newlines), which will fail the backend'srequired_non_empty_string_fieldvalidation for"diff".Consider filtering out empty patches before pushing:
🛡️ Proposed fix
const entryObj = entry as {patch?:string}; const patch = entryObj.patch || ""; - - patches.push(patch); + if (patch.trim().length > 0) { + patches.push(patch); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/plugins/sce-agent-trace.ts around lines 73 - 81, The loop is pushing entry.patch || "" into the patches array which allows empty strings to be collected and yields an empty "diff" later; change the logic where entryObj and patch are derived (the entryObj = entry as {patch?:string} / const patch = entryObj.patch || "") to compute a trimmedPatch (e.g., entryObj.patch?.trim()) and only push into patches when trimmedPatch is a non-empty string, leaving the existing check (if (patches.length === 0) return undefined) intact.config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts (1)
55-88:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winMissing null check on
modelbefore property access.Line 87 accesses
model.providerIDandmodel.modelIDwithout verifying thatmodelis defined. IfinfoObj.modelisundefinedornull, this will throw a runtime exception.🐛 Proposed fix: Add null guard for model
const model = infoObj.model; + if (typeof model !== "object" || model === null) { + return undefined; + } // Access info.summary?.diffs via explicit checks const summary = infoObj.summary;Alternatively, if you want to allow traces without model attribution:
+ const modelId = + typeof model === "object" && + model !== null && + typeof model.providerID === "string" && + typeof model.modelID === "string" + ? `${model.providerID}/${model.modelID}` + : "unknown"; + return { sessionID, diff: patches.join("\n"), time: Date.now(), - model_id: `${model.providerID}/${model.modelID}`, + model_id: modelId, };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts` around lines 55 - 88, The code reads infoObj.model into model then unconditionally accesses model.providerID/model.modelID when returning the trace object, which can throw if model is null/undefined; update the logic in this function to guard against a missing model (infoObj.model) before using its properties — either return undefined or set a safe default for model_id when model is falsy, e.g. check model !== null && model !== undefined (or use a short-circuit) before constructing model_id, and ensure sessionID/diff/time logic remains unchanged so the function returns undefined or a trace with a defensive model_id instead of throwing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.opencode/plugins/sce-agent-trace.ts:
- Line 55: The code reads const model = infoObj.model without validating it but
later accesses model.providerID and model.modelID; add a null/undefined guard
before any property access (e.g., check that model is truthy and has
providerID/modelID) and handle the missing case (return early, throw a
descriptive error, or use safe defaults) in the function containing this logic
so accesses like model.providerID and model.modelID are only executed when model
is valid.
In `@config/.opencode/plugins/sce-agent-trace.ts`:
- Around line 73-77: The loop is currently pushing empty patch strings into
patches (using entryObj, patch, patches); update the logic to only push
non-empty patches by checking something like if (patch && patch.trim() !== "")
before calling patches.push(patch) so empty/whitespace-only patch values are
filtered out and won't produce an empty diff.
- Line 55: The code reads const model = infoObj.model and later accesses
model.providerID and model.modelID without checking for null/undefined; add a
defensive null check for model (and ensure providerID/modelID exist) before
accessing their properties in the functions that reference model (e.g., where
const model = infoObj.model is declared and the subsequent accesses around the
locations noted, including the accesses at the later references), returning
early or handling the error path (log/throw) if model is missing so property
access won't throw.
In `@config/automated/.opencode/plugins/sce-agent-trace.ts`:
- Line 55: The assignment const model = infoObj.model is used without a
null/undefined guard before accessing model.providerID and model.modelID (also
at the other occurrences around the block at lines referenced), so add a
defensive check: verify model is non-null (e.g., if (!model) { handle/maybe
throw or return } or provide safe defaults) before reading providerID/modelID;
update the same pattern for the other occurrences (the accesses at the
referenced 87-88 locations) to avoid runtime TypeError when infoObj.model is
missing.
- Around line 73-77: The code collects empty patch strings into patches; update
the logic around entryObj / patch so only non-empty patches are added — e.g.,
derive patch = (entry as {patch?: string}).patch || "" and push it into patches
only when patch.trim() !== ""; alternatively filter patches after collection
with patches = patches.filter(p => p && p.trim() !== ""); ensure you reference
the entryObj/patch variables and the patches array when making the change.
---
Outside diff comments:
In @.opencode/plugins/sce-agent-trace.ts:
- Around line 73-81: The loop is pushing entry.patch || "" into the patches
array which allows empty strings to be collected and yields an empty "diff"
later; change the logic where entryObj and patch are derived (the entryObj =
entry as {patch?:string} / const patch = entryObj.patch || "") to compute a
trimmedPatch (e.g., entryObj.patch?.trim()) and only push into patches when
trimmedPatch is a non-empty string, leaving the existing check (if
(patches.length === 0) return undefined) intact.
In `@config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts`:
- Around line 55-88: The code reads infoObj.model into model then
unconditionally accesses model.providerID/model.modelID when returning the trace
object, which can throw if model is null/undefined; update the logic in this
function to guard against a missing model (infoObj.model) before using its
properties — either return undefined or set a safe default for model_id when
model is falsy, e.g. check model !== null && model !== undefined (or use a
short-circuit) before constructing model_id, and ensure sessionID/diff/time
logic remains unchanged so the function returns undefined or a trace with a
defensive model_id instead of throwing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d6c74f66-c3dc-406a-b666-3fed7debb2c7
⛔ Files ignored due to path filters (14)
config/lib/agent-trace-plugin/bun.lockis excluded by!**/*.lockcontext/architecture.mdis excluded by!context/**/*.mdcontext/cli/cli-command-surface.mdis excluded by!context/**/*.mdcontext/cli/patch-service.mdis excluded by!context/**/*.mdcontext/context-map.mdis excluded by!context/**/*.mdcontext/glossary.mdis excluded by!context/**/*.mdcontext/overview.mdis excluded by!context/**/*.mdcontext/patterns.mdis excluded by!context/**/*.mdcontext/plans/add-diff-traces-model-id.mdis excluded by!context/**/*.mdcontext/plans/agent-trace-plugin-message-updated.mdis excluded by!context/**/*.mdcontext/sce/agent-trace-db.mdis excluded by!context/**/*.mdcontext/sce/agent-trace-hooks-command-routing.mdis excluded by!context/**/*.mdcontext/sce/agent-trace-minimal-generator.mdis excluded by!context/**/*.mdcontext/sce/opencode-agent-trace-plugin-runtime.mdis excluded by!context/**/*.md
📒 Files selected for processing (10)
.opencode/plugins/sce-agent-trace.tscli/migrations/agent-trace/005_add_diff_traces_model_id.sqlcli/src/services/agent_trace.rscli/src/services/agent_trace_db/mod.rscli/src/services/hooks/mod.rscli/src/services/patch.rsconfig/.opencode/plugins/sce-agent-trace.tsconfig/automated/.opencode/plugins/sce-agent-trace.tsconfig/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.tsconfig/lib/agent-trace-plugin/package.json
Summary by CodeRabbit
New Features
Chores