Skip to content

feat(claude): show SDK prompt suggestions as ghost text in the composer - #8524

Open
getbrikly wants to merge 1 commit into
pingdotgg:mainfrom
getbrikly:feat/claude-prompt-suggestions
Open

feat(claude): show SDK prompt suggestions as ghost text in the composer#8524
getbrikly wants to merge 1 commit into
pingdotgg:mainfrom
getbrikly:feat/claude-prompt-suggestions

Conversation

@getbrikly

@getbrikly getbrikly commented Aug 28, 2026

Copy link
Copy Markdown

What Changed

Claude Code already predicts the user's next prompt after each turn; the Claude adapter receives that prompt_suggestion message today and drops it (#4244 added the case as "no T3 surface"), and the SDK option that produces it is never passed. This PR gives it a surface, using only what the SDK provides — no second model call, no new dependency.

  • ClaudeAdapter: pass promptSuggestions: true to query(); map the trailing prompt_suggestion message to a new turn.prompt-suggestion runtime event attributed to the turn that just completed (the message arrives after result, once turnState is cleared, so the adapter remembers lastCompletedTurnId).
  • contracts: the turn.prompt-suggestion event schema ({ suggestion }).
  • ProviderRuntimeIngestion: persist it as one prompt-suggestion activity through the existing activity channel, so it reaches every client and survives reloads. Web and mobile keep it out of the work log.
  • Web composer: derivePromptSuggestion picks the suggestion for the latest completed turn and drops it once anything newer exists (a later user message, a running turn). The composer renders it as faded text in the existing placeholder slot with a Tab hint — only while the editor is empty and idle (not during approvals, questions, plan follow-ups, or while sending/connecting). Tab inserts it into the draft, never sends; typing ignores it.
  • Docs: docs/user/composer.md section.
  • Tests: adapter (option passed; event emitted for the completed turn; dropped when there is no turn to attach to), ingestion mapping, work-log filtering, and the derive helper (stale-turn, superseded-by-user-message, session-running cases).

13 files, +458/−2. Claude-only by construction — no other harness exposes an equivalent — but the event is provider-agnostic, so a provider that grows one later can emit it without client changes.

Deliberately not included: a T3 settings toggle. Suggestions follow the user's own Claude Code settings (promptSuggestionEnabled: false in settings.json or CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false suppresses them; Claude Code also skips the first turn, plan mode, and API-error turns), so there's no second switch to keep in sync. Easy to add if you'd rather have one in Settings.

Why

Discussed in #6839 ("Suggest a follow-up prompt when a turn finishes"). The moment a turn settles is when the user has to re-derive context and decide what to ask next; Claude Code fills that gap in the terminal and the prediction is generated off the turn's own prompt cache, so it is nearly free. T3 was already receiving it. The original proposal in #6839 builds suggestions with a new text-generation operation; this is the SDK-native version I described in that thread, with much less machinery.

UI Changes

Empty composer after a completed turn shows the suggestion as placeholder-coloured text followed by a Tab key hint; pressing Tab fills the draft. A short screen recording is attached in the first comment below.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Built with Claude Fable 5 in Claude Code.

https://claude.ai/code/session_01Dzo12DZh4ZQF9mUjr26qzj


Note

Low Risk
Additive feature path with guarded client display rules and extensive tests; no auth or data-model breaking changes.

Overview
Wires Claude Code’s post-turn prompt suggestions through the stack and surfaces them in the web composer as ghost text (Tab to accept), instead of dropping SDK prompt_suggestion messages.

Claude adapter opts into promptSuggestions: true, remembers lastCompletedTurnId after each turn finishes, and emits provider-agnostic turn.prompt-suggestion events (trimmed text, correct turn even when the suggestion arrives after result or a new turn has already started). Suggestions with no turn to attach to are dropped.

Contracts + ingestion add the turn.prompt-suggestion schema and persist it as a prompt-suggestion thread activity. Web uses derivePromptSuggestion to show the latest completed turn’s suggestion only while the composer is idle and empty (not on mobile, during approvals/questions/plan follow-up, or while connecting/sending); Tab fills the draft without sending. Work logs on web and mobile skip these activities; mobile has no composer UI yet.

User docs describe behavior and that suggestions follow Claude Code settings (promptSuggestionEnabled / env var).

Reviewed by Cursor Bugbot for commit 1894b8e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Show Claude SDK prompt suggestions as ghost text in composer

  • The Claude adapter now passes promptSuggestions: true to the SDK and maps prompt_suggestion messages to turn.prompt-suggestion provider runtime events, attributed to the last completed turn
  • The ingestion layer surfaces these as prompt-suggestion orchestration activities; both web and mobile deriveWorkLogEntries now exclude them from work logs
  • derivePromptSuggestion deterministically picks a valid suggestion from thread state — it must belong to the latest completed turn, the session must be idle, and no newer user message may exist
  • ChatComposer renders the suggestion as ghost text with a Tab hint on desktop when the composer is empty and idle; Tab accepts it into the prompt
  • Behavioral Change: ClaudeSessionContext gains lastCompletedTurnId; blank or unattributed suggestions are silently dropped rather than emitted

Macroscope summarized 1894b8e.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a0e0b10a-827b-4875-81b3-7548615bd279

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 28, 2026
Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

UI consistency review of the composer prompt-suggestion surface. Two findings on the new ghost-text affordance in ComposerPromptEditor / ChatComposer; the rest of the web changes (shared Kbd primitive reuse, text-placeholder token, work-log exclusion, derived-state helper + tests) look consistent with the existing composer patterns.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ComposerPromptEditor.tsx
Comment thread apps/web/src/components/chat/ChatComposer.tsx
@getbrikly
getbrikly force-pushed the feat/claude-prompt-suggestions branch 2 times, most recently from 6bfdf55 to 8b5643a Compare August 28, 2026 11:13
Comment thread apps/web/src/components/chat/ChatComposer.tsx
The Claude Agent SDK can predict the user's next prompt after each turn
(`promptSuggestions: true` emits one `prompt_suggestion` message after
`result`, generated off the turn's prompt cache). The adapter received that
message but dropped it, and never opted in, so the composer sat empty at the
exact moment the user has to decide what to ask next.

Opt in and carry the suggestion over the existing activity channel: the
adapter maps `prompt_suggestion` to a new `turn.prompt-suggestion` runtime
event attributed to the turn that just completed; ingestion stores it as a
`prompt-suggestion` activity, hidden from the work log on web and mobile.
The web composer derives the suggestion for the latest completed turn and
renders it as faded text in the empty editor with a Tab hint. Tab inserts it
into the draft (never sends), typing ignores it, and it clears once a newer
user message or turn exists. Claude only; the user's own Claude settings
(`promptSuggestionEnabled: false`, `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION`)
still suppress it, so there is no separate T3 toggle.

Claude Fable 5 in Claude Code.

Claude-Session: https://claude.ai/code/session_016YSyurTEFmMXRr9t8vNHTT
@getbrikly
getbrikly force-pushed the feat/claude-prompt-suggestions branch from 8b5643a to 1894b8e Compare August 28, 2026 11:16
@macroscopeapp

macroscopeapp Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new Claude SDK capability and carries it through runtime events, persisted activities, and a new Tab-driven composer interaction. Its cross-layer production behavior and automatic SDK opt-in exceed the bounded additive scope suitable for automatic approval.

You can add or adjust custom eligibility rules. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant