feat(agent-chat): warm Stop + approval dock polish (steer UI flag-gated) - #5477
feat(agent-chat): warm Stop + approval dock polish (steer UI flag-gated)#5477ardaerzin wants to merge 5 commits into
Conversation
…killing or leaking Today "Stop" either just aborts the client stream (the runner keeps running and billing) or, behind a flag, kills the whole session. Neither cancels the turn cleanly. This adds a cooperative cancel: Runner: on a user Stop the control-plane `cancel` command drops the alive lock and the heartbeat aborts the run signal. run-turn now races that abort to a distinct `stopReason:"cancelled"` (instead of falling through to the error catch), settles every still-open tool call with a new INTERRUPTED_BY_USER sentinel — no fake success, no orphaned "running" part — and treats continuity like a pause (invalidate, don't advance the ledger). Keep-warm-on-cancel is deferred to v2 (cold replay resumes fine and it avoids the unverified Claude-ACP warm-cancel question); the session stays open, so a follow-up prompt cold-resumes. FE: handleStop sends the `cancel` command by default (turn stops, session stays open) instead of leaving the runner running; the hard kill stays opt-in behind NEXT_PUBLIC_AGENT_CHAT_STOP_KILLS_SESSION. Runner tsc clean; 78 files / 1245 unit tests pass (incl. a new cancel test).
Adds "reject-with-message" from the approval proposal: a denial can carry an instruction that reruns as the next turn, so the agent hears "no — do this instead" rather than a bare refusal (the redirect case, e.g. "write to staging, not prod"). Pure FE, reusing tested paths — no runner/wire change: the ApprovalDock gets a subtle "Redirect" popover (progressive disclosure — the instruction field only appears on click; bare Deny stays one-click) that answers the gate as denied AND carries the note. handleApprovalResponse then denies via addToolApprovalResponse and sends the note through the existing queue `submit`, which holds it until the paused turn settles and drives the next turn. (v2: fuse the note into the denial itself as model-facing guidance rather than a follow-up turn.) tsc/prettier/eslint clean.
Approval dock: inline Redirect affordance (replaces the body-portal popover that lingered across sessions), a single primary action, a subtle filled redirect field with a neutral hover/focus border, and the action row / always-allow toggle / redirect panel animate as one coordinated collapse-expand swap; the field auto-focuses on open. Steer (deny + redirect) is UI-complete but gated OFF behind NEXT_PUBLIC_AGENT_CHAT_STEER: the redirect can only run as a follow-up turn today (the harness continues the original prompt on reject and exposes no reject-with-feedback channel), so the control is hidden until the runner-level reject-and-redirect lands. The implementation ships intact behind the flag.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe runner now treats user stops as cancelled turns, settles open tool calls with an interruption sentinel, and avoids cancelled ledger updates. Agent Chat sends cooperative cancellations and supports feature-gated approval redirection. Playground configuration spacing is tightened. ChangesTurn cancellation and approval steering
Playground spacing adjustment
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant AgentConversation
participant Runner
participant ToolCalls
User->>AgentConversation: select Stop
AgentConversation->>Runner: commandSessionStream cancellation
Runner->>ToolCalls: settle open calls as INTERRUPTED_BY_USER
Runner-->>AgentConversation: cancelled turn
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1b11f23f-32df-434d-adac-ad2cfc7b841b
📒 Files selected for processing (8)
services/runner/src/engines/sandbox_agent/run-turn.tsservices/runner/src/tracing/otel.tsservices/runner/tests/unit/session-keepalive-approval.test.tsweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/assets/constants.tsweb/oss/src/components/AgentChatSlice/components/ApprovalDock.tsxweb/oss/src/lib/helpers/dynamicEnv.tsweb/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx
| // pt-0 (not py-3) tightens the gap between the region header and the first section | ||
| // row; pb-3 keeps the bottom breathing room. The loading fallback below matches. | ||
| return <div className="px-4 pb-3 pt-1">{props.defaultRender()}</div> | ||
| return <div className="px-4 pb-3 pt-0">{props.defaultRender()}</div> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep the loading fallback spacing synchronized.
The agent content now uses pt-0, but the loading fallback at Lines [1898-1901] still uses pt-1 despite claiming to mirror this wrapper. This causes the content to shift vertically when loading completes. Update the fallback to pt-0 as well.
Railway Preview Environment
|
Stop is verified warm (§5 acceptance passes). Steer's 'message as model feedback' (§3) is NOT reachable pure-FE — both harnesses continue the original turn on reject and the ACP reply carries no text; the redirect needs runner path A or ACP-chain patch C. UI shipped flag-gated (#5477). Batch surfacing is a small runner change (#5470), not blocked by #5391.
…/Stop/steer) (#5444) * docs(agent-workflows): reject-siblings approval policy proposal (Deny/Stop/steer) * docs(agent-workflows): add task-5 implementation findings Stop is verified warm (§5 acceptance passes). Steer's 'message as model feedback' (§3) is NOT reachable pure-FE — both harnesses continue the original turn on reject and the ACP reply carries no text; the redirect needs runner path A or ACP-chain patch C. UI shipped flag-gated (#5477). Batch surfacing is a small runner change (#5470), not blocked by #5391.
What
cancelcommand) instead of only aborting the client stream — the turn ends cleanly and the session stays open for a follow-up prompt. Open tool calls settle with a newINTERRUPTED_BY_USERsentinel; continuity treats a cancel like a pause (invalidate, don't advance the ledger). Hard-kill stays opt-in behindNEXT_PUBLIC_AGENT_CHAT_STOP_KILLS_SESSION.NEXT_PUBLIC_AGENT_CHAT_STEER(see below).pt-0).Why steer is gated (not shipped live)
On both harnesses (Claude + pi), denying a gate makes the model continue the original turn and react to a bare "permission denied" before the user's redirect lands — the ACP permission reply is a closed outcome (
selected/cancelled) with no text field, and the harness continues on reject (claude-agent-acp returns{behavior:"deny", message:"User refused permission"}and keeps going). So a pure-FE steer can only send the note as a follow-up turn, and the model flails on the bare denial first (retrying the blocked action → a new gate that traps the note, or reasoning around it).The clean fix is runner-level (reject-and-redirect) or a per-harness ACP-chain patch to carry the deny
message. Until then the control is hidden; the implementation ships intact behind the flag. Design, evidence, and the A-vs-C options: #5444.Test plan
services/runner/tests/unit/session-keepalive-approval.test.ts).NEXT_PUBLIC_AGENT_CHAT_STEER=true): Redirect opens inline, coordinated animation, auto-focus, neutral border; switch sessions → no lingering popover.