fix(profiles): coder output adapter reassembles result JSON from opencode event fragments#179
Merged
Merged
Conversation
…code event fragments parseCoderEvents only scanned per-event data.text/data.delta (claude-code's shape) for the fenced result block. opencode streams assistant text as incremental message.part.updated fragments (data.part.text / data.delta), so the final JSON result is split across many events and present in none — the adapter returned an empty CoderOutput for a run that actually produced a patch, which runLoop then graded as 'no candidate passed validation' with $0 cost. Accumulate assistant text across the whole stream (both shapes, reasoning parts excluded), then scan the concatenation for the last valid fenced block. Closes #178.
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.
Closes #178.
Problem
runLoop+coderProfilereturned an emptyCoderOutput(patch"",costUsd: 0) for every non-claude-code harness — the validator then graded the emptiness as "no candidate passed validation". Reproduced 9/9 against opencode in live sandbox runs.Isolated by elimination (all on the same box): bare
box.prompt()✓, barebox.streamPrompt()✓ (27 events, terminal present), inlineAgentProfile✓, model overrides ✓ — the empty result appeared only through the coder loop.Root cause
parseCoderEventsscanned each event'sdata.text/data.deltafor the fenced result block. That's claude-code's shape. opencode streams assistant text as incrementalmessage.part.updatedfragments (data.part.text, with incrementaldata.delta), so:data.part.text— never read; andSo the agent's real output was invisible and the loop reported an empty patch +
$0.Fix
parseCoderEventsnow accumulates assistant text across the whole stream viacollectAssistantText, tolerating both shapes (claude-codedata.text/data.deltaand opencodemessage.part.updatedtext parts; reasoning/thinking parts excluded so they can't inject a decoy block), then scans the concatenation for the last valid fenced JSON block. The structured-result-event fast path is unchanged.Tests
tests/profiles/coder.test.ts(15 pass): addedmessage.part.updateddeltas, andpnpm typecheck,pnpm lint, and the profiles/sandbox-events/coder-delegate suites are green.Note for reviewers
This was the terminal blocker in a long live-debugging session (cli-bridge→Rust migration via the Pi loops extension). Sibling platform issues filed separately: agent-dev-container #1770 (host CD), #1775 (router CF/auth), #1781 (claude-code BYOK in sandbox).