feat(coding-agent): surface refinement status and outcomes - #447
Conversation
# Conflicts: # packages/coding-agent/CHANGELOG.md
# Conflicts: # packages/coding-agent/CHANGELOG.md
# Conflicts: # packages/coding-agent/CHANGELOG.md # packages/coding-agent/src/core/agent-session.ts
# Conflicts: # packages/coding-agent/CHANGELOG.md
# Conflicts: # packages/coding-agent/CHANGELOG.md
# Conflicts: # packages/coding-agent/CHANGELOG.md
# Conflicts: # packages/coding-agent/CHANGELOG.md # packages/coding-agent/src/cli/daemon-launch.ts # packages/coding-agent/src/modes/agents-view/agents-view-state.ts # packages/coding-agent/src/modes/rpc/rpc-mode.ts
|
Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies. |
…nement lines, tighten comments
…nt mode and context rebuilds
Replace the bespoke checkmark rendering with the shared custom-message pattern: a boxed bold [refinement] label on the customMessage background, collapsed to a single summary line and expanded through the same tool-output toggle (ctrl+o) that compaction summaries and skill invocations use, instead of a separate edit-diff axis.
The apply phase is sub-second, so a "Refining" loader, agents-view status label, daemon status string, heartbeat deferral, and an isRefining wire flag were all periphery for something users can barely see. Drop refinement_start/end events and every isRefining touchpoint (daemon protocol schema stays at 16 — no wire change at all), and slim the outcome-message validator to the shallow envelope checks the compaction outcome uses. The feature is now just the persisted [refinement] transcript card.
No new events or state: start the loader when the /refine slash-command message reaches the transcript and stop it on the already-wire-visible refine_complete/refine_failed events, mirroring the compaction loader.
Compaction, skill, and now refinement cards each copied the same Box subclass with an expanded flag, setExpanded, invalidate, and bold-label formatting. Pull that into ExpandableCustomMessageBox + a customMessageLabel helper and rebase all three components onto it, so the refinement card only carries its edit-row/diff formatting.
…s-queue # Conflicts: # packages/coding-agent/CHANGELOG.md
A failed /refine persisted its command-error row without emitting refine_failed, so the TUI loader kept spinning. Emit it from the queued command catch, matching the refine.run and auto-refine failure paths.
Collapsed card now renders the [refinement] label line above the summary line, matching the compaction card structure. The refine loader joins compaction/retry in the syncWorkingLoader ownership guard so periodic reconcile paths (subagent updates, connection refreshes) cannot clear it mid-refine.
…s-queue # Conflicts: # packages/coding-agent/CHANGELOG.md
Long summaries wrapped the collapsed line onto a second row. Render the collapsed line through a width-aware component that ellipsizes the summary while keeping the edit count and expand hint visible.
…d line A compaction that starts mid-refine owns the status container; on compaction_end syncWorkingLoader now remounts the refine loader instead of bailing on the ownership guard. The collapsed outcome line is also clipped to the render width after assembly so extreme narrow viewports cannot wrap it.
…s-queue # Conflicts: # packages/coding-agent/CHANGELOG.md
…n teardown refine_complete carries no request identity, so an agent or auto refinement settling while a queued user /refine waited on it killed the loader early. The /refine result row is the user refine's settle edge (emitted after refine() returns, error row on failure), so the loader stops there — and on refine_failed, which covers a failed result-row append. Session switches and stop() now discard the loader timer, which previously leaked when the view never received a settle event.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6f101fd. Configure here.
| } catch (error) { | ||
| if (error instanceof CompactionSkippedError) return; | ||
| const commandError = error instanceof Error ? error : new Error(String(error)); | ||
| if (input.command.name === "refine") this._emitRefineFailed(commandError); |
There was a problem hiding this comment.
Success path emits refine failure
Medium Severity
The queued /refine catch now always calls _emitRefineFailed, including when refine() already succeeded and only the hidden result-row persist fails. The transcript already has a refinement_outcome, but the UI still surfaces a refinement failure and may append a Command failed row, so a completed harness update is reported as failed.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 6f101fd. Configure here.


Refinement (the continual-harness update that runs after a turn ends) was invisible in the transcript: the TUI literally ignored
refine_complete, and nothing showed what a refinement changed. This PR persists a durable outcome message and shows a live loader for user-issued /refine.What it does
After every refinement (manual
/refine, agentrefine.run, or auto-refine), arefinement_outcomecustom message is persisted into the session transcript and rendered like the compaction/skill cards:[refinement] <summary> · N edits appliedwith the shared Ctrl+O expand hint.app.tools.expandtoggle as compaction summaries and skill invocations): one row per harness edit — action, scope, kind, entry id, error for failed edits — plus exact before/after entry payloads as inline diffs.compaction_outcome), and is excluded from model context (convertToLlm)./refineresult row is still persisted for branch topology but no longer displayed — the outcome message supersedes it.compaction_outcome./refineexecutes — built entirely from existing edges (the /refine slash-command message atmessage_start, and the already-wire-visiblerefine_complete/refine_failed), reusing the compactionLoader. No new events or state.Shared component skeleton extracted
Compaction, skill, and refinement cards each duplicated the same Box-subclass skeleton (expanded flag,
setExpanded,invalidate, bold label). That skeleton now lives inExpandableCustomMessageBox+customMessageLabel, and all three components sit on it — the compaction and skill components each shrank by ~20 LOC.Deliberately NOT included
Earlier revisions plumbed
refinement_start/refinement_endevents and anisRefiningflag through the daemon protocol, RPC state, agents-view status labels, and heartbeat deferral. The apply phase is sub-second and both loader edges already existed on the wire, so all of that was stripped. No protocol/schema changes (revision stays at 16).Size
+574/−111 (src +353/−108): component 102 + shared base 33, message type/factory/validator ~60, agent-session ~40, loader + wiring ~74, minus ~57 removed from compaction/skill components.
Validation
Fixes ENG-5285 (https://linear.app/primeintellect/issue/ENG-5285)
Note
Add durable
refinement_outcometranscript messages and live/refineloaderrefinement_outcomecustom message via_recordRefinementOutcomein agent-session.ts, rendered as an expandable card with per-edit diffs byRefinementOutcomeMessageComponent/refinehides its slash-command result row (display=false) and shows a liveRefining continual harness state...loader; on planner failure it emitsrefine_failed_unpersistedOutcomes), so refinement outcomes that fail to persist are merged back on context rebuildsconvertToLlm) and skipped by headless terminal result selectionCompactionSummaryMessageComponentandSkillInvocationMessageComponentonto a sharedExpandableCustomMessageBoxbase_mergeUnpersistedOutcomesnow handles both compaction and refinement outcomes;_appendDurableSessionCommandMessageaccepts adisplayparam that can persist hidden result rowsMacroscope summarized 6f101fd.
Note
Medium Risk
Touches session persistence, context rebuilds, and interactive event/loader wiring for refinements. Not auth-critical, but incorrect merge or loader settle logic could drop outcomes or leave a stuck status spinner.
Overview
Makes refinements visible in the transcript. After each successful refine (slash command, agent, or auto), the session now records a durable
refinement_outcomecustom message with summary, scope, and applied edits. Persistence failures keep the outcome in memory so context rebuilds still show it; the message is excluded from LLM context.The TUI renders a boxed
[refinement]card that collapses to a one-liner and expands (same tool-output toggle as compaction/skill) to per-edit rows with before/after diffs. The generic/refineresult row stays for branch topology but is hidden. User/refineshows a “Refining continual harness state...” loader until the command result settles;refine_complete/refine_failedfrom background refines do not stop it.Compaction and skill cards now share
ExpandableCustomMessageBox. Headless/print mode skips trailing refinement outcomes when picking the terminal result.Reviewed by Cursor Bugbot for commit 6f101fd. Bugbot is set up for automated code reviews on this repo. Configure here.