From 7aa7eb735c19194443ed327b7c938ec13fb565fe Mon Sep 17 00:00:00 2001 From: Nikita Ashikhmin Date: Thu, 15 Oct 2026 17:50:36 +0400 Subject: [PATCH] docs(rfd): propose a prompt_suggestions session update Proposes a `prompt_suggestions` variant of `SessionUpdate` for v1 and v2, carrying the prompts an Agent predicts the user is likely to send next: an ordered list that each update replaces whole, where an empty list withdraws the offer and accepting a suggestion fills the composer without sending it. In v1 a `ClientSessionCapabilities.promptSuggestions` capability gates the event. Unlike notices there is no fallback channel behind it: it tells the Agent whether asking the model for a prediction is worth anything at all. The mechanism already runs as `_meta.promptSuggestions` between the Claude Agent adapter and the JetBrains AIR Client, and Codex has an open request for the same feature, so the RFD standardizes an existing shape rather than inventing one. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- docs/docs.json | 3 +- docs/rfds/prompt-suggestions.mdx | 273 +++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 docs/rfds/prompt-suggestions.mdx diff --git a/docs/docs.json b/docs/docs.json index 6ebe6190f..06bf361fd 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -196,7 +196,8 @@ "rfds/end-turn-token-usage", "rfds/get-auth-state", "rfds/subagents", - "rfds/client-system-prompt" + "rfds/client-system-prompt", + "rfds/prompt-suggestions" ] }, { diff --git a/docs/rfds/prompt-suggestions.mdx b/docs/rfds/prompt-suggestions.mdx new file mode 100644 index 000000000..ad4950e27 --- /dev/null +++ b/docs/rfds/prompt-suggestions.mdx @@ -0,0 +1,273 @@ +--- +title: "Prompt Suggestions" +--- + +Author(s): [@nikita-ashihmin](https://github.com/nikita-ashihmin) + +## Elevator pitch + +> What are you proposing to change? + +Add a `prompt_suggestions` session update to ACP v1 and v2 that carries +prompts the Agent predicts the user is likely to send next, for the Client to +offer in its composer. In v1, a Client capability tells the Agent whether +predicting them is worth the work at all. + +The payload is deliberately small: + +```json +{ + "sessionUpdate": "prompt_suggestions", + "suggestions": [{ "text": "Run the test suite and fix what breaks" }] +} +``` + +## Status quo + +> How do things work today and what problems does this cause? Why would we change things? + +Agents increasingly predict a plausible next prompt at the end of a turn. The +model has the transcript in context and the prediction rides the same prompt +cache, so producing one is close to free, while a user who accepts it with a +single keypress skips typing a prompt they would have typed anyway. Editors +already have the surface for it: the empty composer, which renders a +grayed-out suggestion, completes it on Tab, and drops it on Escape or on the +first typed character. + +ACP has no event for this. A predicted prompt is neither an agent message +(it is not conversation content and must not enter history), nor a plan, nor a +notice, nor session metadata. So each Agent–Client pair invents its own +channel: + +- The Claude Agent adapter and the JetBrains AIR Client carry the value as + `session_info_update._meta.promptSuggestions` + ([claude-agent-acp PR #1356](https://github.com/agentclientprotocol/claude-agent-acp/pull/1356)). + The key deliberately avoids a vendor prefix precisely because the value is + not vendor-specific — a sign that it wants to be protocol. +- Codex has an open request for the same feature + ([openai/codex#42587](https://github.com/openai/codex/issues/42587)). + +Two further problems follow from having no protocol event: + +- **The Agent cannot tell whether the prediction will ever be shown.** An + Agent that predicts for every Client either wastes work on Clients with no + composer affordance, or ships a Client allowlist. +- **Clearing is undefined.** A prediction is tied to a moment in the + conversation. Without an agreed way to withdraw one, a Client either keeps + showing a stale prompt or invents its own expiry rule. + +## What we propose to do about it + +> What are you proposing to improve the situation? + +Add a `prompt_suggestions` variant to `SessionUpdate` in ACP v1 and v2. + +### The update + +```json +{ + "sessionUpdate": "prompt_suggestions", + "suggestions": [ + { "text": "Run the test suite and fix what breaks" }, + { "text": "Open a PR with these changes" } + ] +} +``` + +- `suggestions` is a required, non-null list of suggestion objects, ordered + most likely first. An empty list is meaningful: it withdraws whatever the + Client is currently offering. +- Each suggestion has a required, non-null `text`: a plain-text prompt, ready + to be placed in the composer verbatim. A Client **MUST** ignore an entry + whose `text` is empty or whitespace-only rather than offering a blank + suggestion. +- A suggestion may carry `_meta`, optional and nullable, with omission and + `null` equivalent. + +Each update **replaces** the session's current set of suggestions. There is no +append, no per-suggestion identity, and no removal event for a single entry. + +### Lifetime + +A suggestion describes what makes sense to send _at this point in the +conversation_. It is a live offer, not conversation history, and **SHOULD NOT** +be included in session replay or in `session/load` history. + +Both sides may end the offer: + +- The Agent withdraws it by sending `suggestions: []`. +- The Client drops it locally — because the user sent a prompt, dismissed it, + started typing, or because the Client decided it has gone stale. A Client + **MUST NOT** offer suggestions from before the most recent prompt it sent. + +Dismissal is local. The Client does not report it, and the Agent does not +learn whether a suggestion was shown, accepted, or edited before sending. An +Agent that needs that signal is using the wrong primitive. + +### Timing + +An Agent typically sends the update right after a turn ends, which is after +the `session/prompt` response. That is an ordinary agent-initiated update +outside a turn and follows the existing timing rules; this RFD does not relax +them. An Agent **MAY** also send one during a turn — a Client that hides +suggestions while work is running simply has nothing to show yet. + +An Agent is not required to send an update for every turn. Predictions are +commonly suppressed on the first turn of a session, after an API error, while +a Client-visible mode makes a follow-up meaningless, or when the account is +rate-limited. A turn that ends with no update is normal, and a Client **MUST +NOT** wait for one before returning its composer to its idle state. + +### Client presentation + +A Client that advertises support decides everything about presentation: how +many suggestions it shows, where, with which keybinding, and whether it shows +any at all for a given session or user preference. The composer's grayed-out +completion is the motivating surface, but a chip row, a menu, or a slash-style +palette are equally valid. + +Accepting a suggestion **MUST NOT** send it. It fills the composer and leaves +the user in control: ACP already has `session/prompt` for sending, and a +prediction the user has not read is not consent. + +### Version-specific capability + +For v1, add an optional `promptSuggestions` object to +`ClientSessionCapabilities`: + +```json +{ + "clientCapabilities": { + "session": { + "promptSuggestions": {} + } + } +} +``` + +Omitting `session`, setting `session` to `null`, omitting +`promptSuggestions`, or setting it to `null` all mean the Client does not +advertise support. Supplying `{}` means the Client can offer predicted prompts +with the semantics in this RFD. Agents **MUST NOT** send `prompt_suggestions` +updates to a v1 Client unless it advertises this capability. + +Unlike [notices](/rfds/session-notices), the capability has no fallback +channel behind it: an Agent with no composer to fill does not reroute the +prediction into an agent message. The capability instead tells the Agent +whether to ask the model for a prediction in the first place. Advertising +support does not promise that any individual suggestion is displayed. + +V2 does not add a capability. It preserves an unknown session update through +its unknown-update fallback, after which a Client may ignore it. V1 keeps its +closed `SessionUpdate` union and uses the capability rather than requiring +unsupported Clients to accept a new variant. + +## Shiny future + +> How will things will play out once this feature exists? + +Any Agent that can predict a next prompt reaches any Client with a composer, +without a vendor key or a Client allowlist. A Client implements the +grayed-out-suggestion affordance once and gets it from every Agent that +supports the capability. An Agent that sees no capability spends nothing +predicting prompts nobody will see. + +## Implementation details and plan + +> Tell me more about your implementation. What is your detailed implementation plan? + +### Proposed phases + +1. Define `PromptSuggestion` and the `prompt_suggestions` variant of + `SessionUpdate` in both protocol versions, and the v1 + `ClientSessionCapabilities.promptSuggestions` capability. +2. Migrate the existing `_meta.promptSuggestions` implementations in the + Claude Agent adapter and the AIR Client to the protocol event, keeping the + `_meta` key for one release for older peers. +3. Gather Client evidence from at least one more Client surface before moving + out of draft. + +### Existing implementation evidence + +The mechanism is already running in production, which is the motivation for +standardizing it rather than designing it from scratch: + +| Implementation | Reference | Behavior | +| -------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| JetBrains AIR Client | Ships in the IDE | Reads `session_info_update._meta.promptSuggestions`, shows the first non-blank text as grayed-out text in an empty composer, completes it on Tab without sending, drops it on typing. | +| Claude Agent adapter | [PR #1356](https://github.com/agentclientprotocol/claude-agent-acp/pull/1356) | Forwards one prediction per turn after the turn's result, clears it on the next prompt and on a conversation reset, drops a stale prediction, and sends nothing to a Client that cannot show it. | + +## Frequently asked questions + +> What questions have arisen over the course of authoring this document or during subsequent discussions? + +### Why not a field on `session_info_update`? + +That is where the current `_meta` implementations put it, and it is the wrong +home. [Session Info Update](/rfds/session-info-update) requires +`SessionInfoUpdate` to stay aligned with the `SessionInfo` of `session/list`, +and a predicted prompt has no business in a session listing: it is ephemeral, +tied to one point in the conversation, and meaningless once the user sends +anything. A dedicated variant also lets a Client that cannot offer suggestions +ignore the event wholesale instead of filtering metadata it must otherwise +accept. + +### Why a list instead of one suggestion? + +A single string is what the motivating composer surface renders, but a chip row +or a menu can offer several, and an Agent that has ranked candidates loses +nothing by sending them. Clients are free to use only the first. Starting with +a list avoids a second event later when one Client wants three. + +### Why is there no "the Agent is still deciding" state? + +An intermediate `pending` state was considered, so a Client could hold its idle +composer hint instead of showing it and replacing it a moment later. It was +left out because there is no bound on how long the state may last: an Agent +that decides not to predict — after an API error, at a rate limit, on the first +turn — sends nothing at all, so a Client would have to invent a timeout to +leave the state. A Client that wants to avoid the flicker can delay its own +idle presentation briefly after a turn; that is a local choice with a local +remedy. If implementations show the flicker is a real problem, a `pending` +signal with defined expiry can be added later without changing this event. + +### Why does accepting a suggestion not send it? + +Because the Agent wrote it. Filling the composer keeps the user as the author +of every prompt in the transcript, which also makes a wrong prediction cheap: +the user edits or clears it. A Client is of course free to let a user send +immediately after accepting. + +### Why is the text plain rather than content blocks? + +The destination is a text composer. Embedding resources or images in a +suggestion would make the Client responsible for materializing content the user +has not asked for yet. If a future use case needs richer suggestions, a +content-block form can be added alongside `text`. + +### How does this interact with slash commands and other composer affordances? + +It does not replace them. A suggestion is prompt text; if a Client's composer +expands slash commands, a suggestion that begins with one behaves exactly as if +the user had typed it. Which affordance wins when several apply at once is a +Client decision. + +### Does the Agent learn whether the suggestion was used? + +No. The follow-up prompt arrives as an ordinary `session/prompt`, with nothing +marking it as accepted. Reporting acceptance would be useful for evaluating +prediction quality, but it is a separate primitive with its own privacy +questions, and this event is useful without it. + +### What alternative approaches did you consider, and why did you settle on this one? + +Three were considered: a field on `session_info_update` (rejected above), a +dedicated `session/prompt_suggestions` request from the Client (rejected: it +makes the Client poll for something the Agent already knows at turn end, and +adds a round trip on a path where latency is visible), and leaving it to +`_meta` (the status quo, which is what produced two incompatible +implementations of the same idea). + +## Revision history + +