Skip to content

feat(agent-chat): warm Stop + approval dock polish (steer UI flag-gated)#5477

Open
ardaerzin wants to merge 4 commits into
feat/sessions-storage-reworkfrom
feat/agent-cancel-steer
Open

feat(agent-chat): warm Stop + approval dock polish (steer UI flag-gated)#5477
ardaerzin wants to merge 4 commits into
feat/sessions-storage-reworkfrom
feat/agent-cancel-steer

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

What

  • Stop = warm cancel (runner): Stop now cooperatively cancels the current turn (control-plane cancel command) 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 new INTERRUPTED_BY_USER sentinel; continuity treats a cancel like a pause (invalidate, don't advance the ledger). Hard-kill stays opt-in behind NEXT_PUBLIC_AGENT_CHAT_STOP_KILLS_SESSION.
  • Approval-dock UI polish: 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) — UI complete, gated OFF behind NEXT_PUBLIC_AGENT_CHAT_STEER (see below).
  • Minor: tighten the config region header-to-first-row gap (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

  • Stop: long generation → click Stop → stream halts, the turn reads cancelled/interrupted, and the session answers a follow-up prompt. Runner unit test added (services/runner/tests/unit/session-keepalive-approval.test.ts).
  • Dock / steer (NEXT_PUBLIC_AGENT_CHAT_STEER=true): Redirect opens inline, coordinated animation, auto-focus, neutral border; switch sessions → no lingering popover.
  • Default (flag off): the dock shows only Deny / Approve (today's behavior).

…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.
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 24, 2026 9:53am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added cooperative turn cancellation when users select Stop, preserving the session while cleanly closing active operations.
    • Added an optional Redirect workflow for approval prompts, allowing users to deny an action and send follow-up instructions.
    • Added feature-flag support for enabling the Redirect workflow.
  • Bug Fixes

    • Prevented cancelled turns from appearing as errors or leaving unfinished operations in the conversation.
    • Improved approval and cancellation handling during in-progress interactions.
  • Style

    • Adjusted spacing in the playground configuration view.

Walkthrough

The 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.

Changes

Turn cancellation and approval steering

Layer / File(s) Summary
Cancelled turn terminalization
services/runner/src/engines/sandbox_agent/run-turn.ts, services/runner/src/tracing/otel.ts, services/runner/tests/unit/session-keepalive-approval.test.ts
Abort signals resolve as cancelled; open tool calls receive INTERRUPTED_BY_USER, cancelled turns skip ledger advancement, and approval-park cancellation is tested.
Cooperative session stop
web/oss/src/components/AgentChatSlice/AgentConversation.tsx
Default Stop sends a control-plane cancellation while preserving the session; kill-session handling now requires session identifiers.
Approval redirect flow
web/oss/src/components/AgentChatSlice/components/ApprovalDock.tsx, web/oss/src/components/AgentChatSlice/AgentConversation.tsx, web/oss/src/components/AgentChatSlice/assets/constants.ts, web/oss/src/lib/helpers/dynamicEnv.ts
A feature-gated Redirect panel submits trimmed denial messages as follow-up turns and resets its state across approval changes.

Playground spacing adjustment

Layer / File(s) Summary
Agent configuration spacing
web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx
Agent configuration content changes from pt-1 to pt-0.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: warm Stop behavior, approval dock polish, and flag-gated steer UI.
Description check ✅ Passed The description is detailed and directly matches the runner, approval dock, steer flag, and config spacing changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-cancel-steer

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ardaerzin
ardaerzin marked this pull request as ready for review July 24, 2026 10:06
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request Frontend labels Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 17366e1 and d748049.

📒 Files selected for processing (8)
  • services/runner/src/engines/sandbox_agent/run-turn.ts
  • services/runner/src/tracing/otel.ts
  • services/runner/tests/unit/session-keepalive-approval.test.ts
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/assets/constants.ts
  • web/oss/src/components/AgentChatSlice/components/ApprovalDock.tsx
  • web/oss/src/lib/helpers/dynamicEnv.ts
  • web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx

Comment on lines +1861 to +1863
// 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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

@github-actions

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-production-a5ed.up.railway.app/w
Project agenta-oss-pr-5477
Image tag pr-5477-ffd08e0
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-07-24T10:16:43.485Z

ardaerzin added a commit that referenced this pull request Jul 24, 2026
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.
ardaerzin added a commit that referenced this pull request Jul 24, 2026
…/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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Frontend size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant