External-agent conversation UX: errors, titles, drafts, disposable sessions - #56
Conversation
…sessions. Persist thread_error events and auto titles, keep composer drafts and catalog state coherent, and defer writing ACP session ids until the first user message so empty drafts stay disposable. Co-authored-by: Cursor <cursoragent@cursor.com>
Move the completed change into the dated archive now that main specs include thread errors, titles, and conversation-view updates. Co-authored-by: Cursor <cursoragent@cursor.com>
Nullish transform keeps draft/legacy rows parsing when title_source is absent. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 40 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughThis change updates draft ACP session persistence, structured thread error propagation, catalog refresh and selection reconciliation, thread title sources and automatic title updates, persisted composer drafts, and web conversation error rendering. It also updates related specifications, integration tests, and manual lifecycle verification. ChangesExternal agent conversation UX
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 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 `@apps/cli/src/core/agents/runtime.ts`:
- Around line 181-209: Update the model-change flow around session.setModel and
reconcileDependentConfigOptions so dependent reset failures do not falsely
report the entire operation as unsuccessful after the model has already changed.
Either refresh catalogs and expose reset failures separately after partial
success, or roll back the model on failure; preserve truthful setModel mutation
semantics and prevent the client from retaining stale options or selections.
In `@apps/cli/src/core/threads/coordinator.ts`:
- Around line 51-65: Serialize all per-thread binding transitions in the
coordinator, including bindAgent, persistBoundSession, and close operations,
with a mutex keyed by threadId. Acquire the mutex before reading or mutating
live bindings, re-run findLiveBinding inside the critical section, and release
it on every success and failure path so concurrent calls cannot create or
persist duplicate sessions.
In `@apps/cli/src/handlers/controller/agents.ts`:
- Around line 12-23: Update the bindAgent handler to avoid calling
persistCoordinatorThreadError for non-fatal bind validation failures, such as
rejecting an agent change on a locked thread, while still propagating the error
through throwOrpc. Persist only errors that invalidate thread input, using the
result.error classification or established validation-error handling.
In `@apps/cli/src/handlers/controller/chat.ts`:
- Around line 42-55: Update the chat handler’s persistBoundSession call to pass
the requested agentName, and change persistBoundSession to validate that
expected agent before calling bindThreadAgent. Preserve the existing mismatch
rejection while ensuring invalid or stale requests cannot persist the binding.
In `@apps/web/src/components/chat/main/thread-workspace.tsx`:
- Around line 50-53: Update the latestThreadError selection near the
conversation.errors lookup to only use conversation.errors.at(-1) when that
error occurred after the latest turn (lastTurn); otherwise return null when no
error matches the current turn. Preserve the existing current-turn error
preference and ensure recovered historical errors no longer block the composer.
In `@openspec/specs/conversation-view/spec.md`:
- Around line 131-143: Update the “Draft composer client persistence”
requirement and its scenarios to persist the complete structured composer
ChatMessage per thread, including resource and attachment blocks, rather than
only plain text. Require exact restoration of that ChatMessage when returning to
a thread and clearing the persisted structured draft after a successful send.
In `@shared/database/src/repositories/threads.ts`:
- Around line 195-207: Prevent TOCTOU overwrites in
shared/database/src/repositories/threads.ts:195-207 by making
applyAutoThreadTitle’s write conditional on the previously observed titleSource
remaining non-user (or use a transaction). Apply equivalent optimistic state
checks or transactional protection to applyAgentThreadTitle at
shared/database/src/repositories/threads.ts:209-222, and ensure
clearThreadDraftBinding at shared/database/src/repositories/threads.ts:312-325
only updates when the observed agentLocked state is unchanged.
- Around line 16-20: Update threadNameFromPrompt to truncate the trimmed message
by Unicode code points rather than UTF-16 code units, using Array.from or the
project’s established Unicode-safe utility before applying the 50-character
limit. Preserve the default name for empty input and return unmodified trimmed
text when it is within the limit.
In `@shared/hooks/src/connection/use-agent-catalog.ts`:
- Around line 333-387: The auto-bind effect currently lets a previous thread’s
bindAgentMutation.isError suppress binding and retain bindError when threadId
changes. Update the thread-change handling around bindAgentMutation and the
useEffect guard to reset the mutation state or otherwise scope the error to the
current thread, ensuring each new thread can auto-bind and the composer is not
blocked by an earlier thread’s failure.
In `@shared/utils/src/conversations/thread-feed.ts`:
- Around line 183-192: Update the conversation feed assembly around the
orphaned-error loop and final entries result so orphaned error entries are
chronologically interleaved with turn entries by their timestamps, rather than
appended after all other entries. Preserve existing filtering of errors with
known turn IDs and ensure the final feed remains historically ordered.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 31b5a7fe-0805-4968-b4c6-44c4891c4f30
📒 Files selected for processing (58)
apps/cli/__tests__/integration/draft-session-lifecycle.test.tsapps/cli/src/core/agents/catalog.tsapps/cli/src/core/agents/runtime.tsapps/cli/src/core/threads/coordinator.tsapps/cli/src/handlers/controller/agents.tsapps/cli/src/handlers/controller/chat.tsapps/cli/src/handlers/controller/projects.tsapps/cli/src/handlers/controller/threads.tsapps/cli/src/utils/run-turn.test.tsapps/cli/src/utils/run-turn.tsapps/cli/src/utils/thread-errors.tsapps/cli/src/utils/thread-title.tsapps/mobile/app/(drawer)/(tabs)/index.tsxapps/web/src/components/chat/composer/composer-prompt-editor.tsxapps/web/src/components/chat/composer/composer-skeleton.tsxapps/web/src/components/chat/composer/index.tsxapps/web/src/components/chat/feed/error-row.tsxapps/web/src/components/chat/feed/feed-entry-view.tsxapps/web/src/components/chat/main/thread-workspace.tsxopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/.openspec.yamlopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/design.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/proposal.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/chat-timeline-ui/spec.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/conversation-view/spec.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/thread-error-surfacing/spec.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/thread-title/spec.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/wire-schemas/spec.mdopenspec/changes/archive/2026-07-15-external-agent-conversation-ux/tasks.mdopenspec/changes/external-agent-conversation-ux/tasks.mdopenspec/specs/acp-draft-session/spec.mdopenspec/specs/acp-session-router/spec.mdopenspec/specs/chat-timeline-ui/spec.mdopenspec/specs/conversation-persistence/spec.mdopenspec/specs/conversation-view/spec.mdopenspec/specs/thread-error-surfacing/spec.mdopenspec/specs/thread-title/spec.mdopenspec/specs/wire-schemas/spec.mdshared/constants/src/operation-keys.tsshared/database/__tests__/integration/repositories.test.tsshared/database/src/models/threads.tsshared/database/src/repositories/threads.tsshared/errors/src/common.tsshared/errors/src/turn.tsshared/hooks/src/connection/use-agent-catalog.tsshared/hooks/src/connection/use-thread-conversation.tsshared/hooks/src/connection/use-worker-conversation-sync.tsshared/hooks/src/stores/agent-catalog.tsshared/hooks/src/stores/composer-draft.tsshared/schemas/src/rtc/chat.test.tsshared/schemas/src/rtc/chat.tsshared/schemas/src/rtc/threads.test.tsshared/schemas/src/rtc/threads.tsshared/schemas/src/view/index.tsshared/utils/src/conversations/thread-feed.test.tsshared/utils/src/conversations/thread-feed.tsshared/utils/src/fold.test.tsshared/utils/src/fold.tstests/e2e/manual/verify-draft-session.ts
💤 Files with no reviewable changes (1)
- openspec/changes/external-agent-conversation-ux/tasks.md
Serialize bind/persist per thread, avoid persisting non-fatal bind failures, and keep composer unblocked by historical turn errors so retries remain possible. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
thread_errorin the feed; disable composer send while the thread cannot accept input (without a duplicate banner above the composer).titleSource.external-agent-conversation-uxand sync specs (thread-error-surfacing,thread-title, conversation-view, wire-schemas, session router).Test plan
sessionIdto DB before sendagentName/sessionIdpersist and agent locksMade with Cursor
Summary by CodeRabbit
New Features
Bug Fixes