Skip to content

External-agent conversation UX: errors, titles, drafts, disposable sessions - #56

Merged
soorya-u merged 4 commits into
mainfrom
apply/external-agent-conversation-ux
Jul 15, 2026
Merged

External-agent conversation UX: errors, titles, drafts, disposable sessions#56
soorya-u merged 4 commits into
mainfrom
apply/external-agent-conversation-ux

Conversation

@soorya-u

@soorya-u soorya-u commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Persist and render thread_error in the feed; disable composer send while the thread cannot accept input (without a duplicate banner above the composer).
  • Auto-title threads after the first turn (user message), honor agent title updates / user rename via titleSource.
  • Persist structured composer drafts client-side; refresh catalog after model change; keep draft ACP sessions in memory until the first message.
  • Archive external-agent-conversation-ux and sync specs (thread-error-surfacing, thread-title, conversation-view, wire-schemas, session router).

Test plan

  • Open a new thread: first agent/model select without writing sessionId to DB before send
  • Stop/restart worker on an empty draft: no resume-session spam; rebinding creates a fresh session
  • Send first message: agentName/sessionId persist and agent locks
  • Force a bind/resume failure: single error in chat feed; send stays blocked
  • Leave unsent text (+ file chip): reload restores draft; send clears it
  • Change model: efforts/personas refresh without empty flash
  • Agents loading: composer shows one full-rectangle skeleton

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added inline thread error reporting with clear messages and recovery guidance.
    • Added automatic thread titles, agent-provided title updates, and protection for manually renamed threads.
    • Draft messages are now saved per thread and restored when returning.
    • Model changes automatically reset invalid configuration selections.
    • Draft agent sessions remain available until the first message is sent.
  • Bug Fixes

    • Improved session cleanup when deleting threads or projects.
    • Failed turns now report errors consistently and mark the turn interrupted.
    • Stale draft session bindings are cleared safely.

soorya-u and others added 3 commits July 15, 2026 22:44
…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>
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cyrus Ready Ready Preview, Comment Jul 15, 2026 5:35pm

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@soorya-u, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28ff797e-13c4-4fd9-ba23-a0a5fef1aefa

📥 Commits

Reviewing files that changed from the base of the PR and between b15cf1a and 5de2e58.

📒 Files selected for processing (10)
  • apps/cli/src/core/agents/runtime.ts
  • apps/cli/src/core/threads/coordinator.ts
  • apps/cli/src/handlers/controller/agents.ts
  • apps/cli/src/handlers/controller/chat.ts
  • apps/web/src/components/chat/main/thread-workspace.tsx
  • openspec/specs/conversation-view/spec.md
  • shared/database/src/repositories/threads.ts
  • shared/hooks/src/connection/use-agent-catalog.ts
  • shared/utils/src/conversations/thread-feed.test.ts
  • shared/utils/src/conversations/thread-feed.ts
📝 Walkthrough

Walkthrough

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

Changes

External agent conversation UX

Layer / File(s) Summary
Draft session lifecycle and catalog state
apps/cli/src/core/..., shared/hooks/src/connection/..., shared/hooks/src/stores/..., openspec/specs/acp-*.md
Draft sessions are kept live in memory until persistBoundSession; stale bindings are cleared, committed sessions are resumed selectively, and catalog selections are refreshed or reset after model changes.
Thread error event pipeline
apps/cli/src/utils/..., shared/errors/..., shared/schemas/src/rtc/..., shared/utils/src/..., apps/web/src/components/chat/feed/...
Typed turn failures emit persisted thread_error events, fold exposes conversation errors, deriveFeed creates flat error entries, and the web feed renders them.
Thread title persistence and updates
shared/database/src/..., apps/cli/src/handlers/controller/chat.ts, openspec/specs/thread-title/spec.md
Thread rows track title origin, automatic titles are applied after completed turns, and agent title updates are accepted without overriding manual names.
Composer drafts and client interaction
shared/hooks/src/stores/composer-draft.ts, apps/web/src/components/chat/composer/..., apps/mobile/app/..., openspec/changes/archive/...
Composer messages are persisted per thread, restored and cleared around sends, blocked on thread errors, and supported by updated editor handles and loading UI.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • soorya-u/cyrus#51 — Shares the ACP draft-session lifecycle and ThreadCoordinator binding changes.
  • soorya-u/cyrus#52 — Shares draft/live session resolution and lifecycle integration tests.
  • soorya-u/cyrus#36 — Shares the CLI turn execution and emitted turn-event pipeline.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main UX changes: errors, titles, drafts, and disposable sessions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch apply/external-agent-conversation-ux

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.

@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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8436cdd and b15cf1a.

📒 Files selected for processing (58)
  • apps/cli/__tests__/integration/draft-session-lifecycle.test.ts
  • apps/cli/src/core/agents/catalog.ts
  • apps/cli/src/core/agents/runtime.ts
  • apps/cli/src/core/threads/coordinator.ts
  • apps/cli/src/handlers/controller/agents.ts
  • apps/cli/src/handlers/controller/chat.ts
  • apps/cli/src/handlers/controller/projects.ts
  • apps/cli/src/handlers/controller/threads.ts
  • apps/cli/src/utils/run-turn.test.ts
  • apps/cli/src/utils/run-turn.ts
  • apps/cli/src/utils/thread-errors.ts
  • apps/cli/src/utils/thread-title.ts
  • apps/mobile/app/(drawer)/(tabs)/index.tsx
  • apps/web/src/components/chat/composer/composer-prompt-editor.tsx
  • apps/web/src/components/chat/composer/composer-skeleton.tsx
  • apps/web/src/components/chat/composer/index.tsx
  • apps/web/src/components/chat/feed/error-row.tsx
  • apps/web/src/components/chat/feed/feed-entry-view.tsx
  • apps/web/src/components/chat/main/thread-workspace.tsx
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/.openspec.yaml
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/design.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/proposal.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/chat-timeline-ui/spec.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/conversation-view/spec.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/thread-error-surfacing/spec.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/thread-title/spec.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/specs/wire-schemas/spec.md
  • openspec/changes/archive/2026-07-15-external-agent-conversation-ux/tasks.md
  • openspec/changes/external-agent-conversation-ux/tasks.md
  • openspec/specs/acp-draft-session/spec.md
  • openspec/specs/acp-session-router/spec.md
  • openspec/specs/chat-timeline-ui/spec.md
  • openspec/specs/conversation-persistence/spec.md
  • openspec/specs/conversation-view/spec.md
  • openspec/specs/thread-error-surfacing/spec.md
  • openspec/specs/thread-title/spec.md
  • openspec/specs/wire-schemas/spec.md
  • shared/constants/src/operation-keys.ts
  • shared/database/__tests__/integration/repositories.test.ts
  • shared/database/src/models/threads.ts
  • shared/database/src/repositories/threads.ts
  • shared/errors/src/common.ts
  • shared/errors/src/turn.ts
  • shared/hooks/src/connection/use-agent-catalog.ts
  • shared/hooks/src/connection/use-thread-conversation.ts
  • shared/hooks/src/connection/use-worker-conversation-sync.ts
  • shared/hooks/src/stores/agent-catalog.ts
  • shared/hooks/src/stores/composer-draft.ts
  • shared/schemas/src/rtc/chat.test.ts
  • shared/schemas/src/rtc/chat.ts
  • shared/schemas/src/rtc/threads.test.ts
  • shared/schemas/src/rtc/threads.ts
  • shared/schemas/src/view/index.ts
  • shared/utils/src/conversations/thread-feed.test.ts
  • shared/utils/src/conversations/thread-feed.ts
  • shared/utils/src/fold.test.ts
  • shared/utils/src/fold.ts
  • tests/e2e/manual/verify-draft-session.ts
💤 Files with no reviewable changes (1)
  • openspec/changes/external-agent-conversation-ux/tasks.md

Comment thread apps/cli/src/core/agents/runtime.ts
Comment thread apps/cli/src/core/threads/coordinator.ts
Comment thread apps/cli/src/handlers/controller/agents.ts
Comment thread apps/cli/src/handlers/controller/chat.ts Outdated
Comment thread apps/web/src/components/chat/main/thread-workspace.tsx Outdated
Comment thread openspec/specs/conversation-view/spec.md Outdated
Comment thread shared/database/src/repositories/threads.ts
Comment thread shared/database/src/repositories/threads.ts
Comment thread shared/hooks/src/connection/use-agent-catalog.ts
Comment thread shared/utils/src/conversations/thread-feed.ts Outdated
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>
@soorya-u
soorya-u merged commit f94eb07 into main Jul 15, 2026
7 checks passed
@soorya-u
soorya-u deleted the apply/external-agent-conversation-ux branch July 15, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 2b: External agent conversation UX

1 participant