diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx new file mode 100644 index 0000000..3943c5b --- /dev/null +++ b/docs/rfds/next-edit-suggestions.mdx @@ -0,0 +1,661 @@ +--- +title: "Next Edit Suggestions" +--- + +- Author(s): [@scotteveritt](https://github.com/scotteveritt) +- Champion: + +## Elevator pitch + +> What are you proposing to change? + +We propose adding support for **next edit suggestions** (also known as tab-completions, predictive edits, or next action prediction) to ACP. This feature allows agents to proactively suggest: + +1. **Edits** the user might want to make next +2. **Navigation targets** (cursor jumps) to where the next edit will occur +3. **Chained sequences** of edits that flow naturally from one to the next + +Unlike traditional LSP completions (which suggest tokens at the cursor) or full agentic responses (which require explicit prompts), next edit suggestions occupy a middle ground: lightweight, sub-second, contextual predictions that users can accept with a single keystroke. + +Key characteristics: +- **Proactive**: Agent suggests without explicit user prompts +- **Low-latency**: Sub-second responses (target: <500ms) +- **Contextual**: Based on recent edits, cursor position, open files, and codebase understanding +- **Chainable**: Accepting one suggestion can trigger the next (tab-tab-tab flow) +- **Non-blocking**: Suggestions are advisory and easily dismissable + +## Status quo + +> How do things work today and what problems does this cause? Why would we change things? + +Currently, ACP supports two primary interaction modes: + +1. **Reactive prompts**: Users explicitly send messages via `session/prompt`, and agents respond with tool calls, diffs, and content. This is powerful but requires user initiation. + +2. **Slash commands**: Users can invoke predefined commands, but these still require explicit user action. + +Neither mode supports the increasingly common UX pattern where AI assistants proactively suggest the "next edit" based on context. Modern AI-powered editors have popularized this pattern: + +| Tool | Key Innovation | +|------|----------------| +| **Cursor Tab** | Predicts edits AND cursor jumps; "Next Action Prediction" vision | +| **GitHub Copilot NES** | Low-latency task-specific model; insertions, deletions, or mixed | +| **Supermaven** | 250ms latency; sees code as edit diffs, not file sequences | +| **Cody** | Four-phase pipeline; retention tracking; partial acceptance | + +Common use cases these tools enable: +- After writing a function signature, suggest the implementation +- After fixing a bug in one location, suggest the same fix elsewhere +- After adding an import, suggest code that uses it +- After renaming a variable, suggest updating documentation +- After modifying a test, suggest updating the implementation +- **Predict where the cursor should jump next** (navigation) + +**Problems with the status quo:** + +1. **No standardized mechanism**: Agents must use proprietary extensions or work outside the protocol. + +2. **Lost context opportunity**: Agents observe edits but cannot act on patterns without user prompting. + +3. **UX friction**: Users must context-switch from editing to prompting. + +4. **Inconsistent implementations**: Each agent/client pair implements this differently, fragmenting the ecosystem. + +## What we propose to do about it + +> What are you proposing to improve the situation? + +We propose adding a new subsystem to ACP for next edit suggestions with the following components: + +### 1. Edit Context Notifications (Client → Agent) + +Clients notify agents about edit context changes. This is the primary signal agents use to generate suggestions. + +```typescript +// Notification: editContext/didChange +interface EditContextDidChangeParams { + /** Current editing context */ + context: EditContext; +} + +interface EditContext { + // Note: Only uri, documentVersion, and selections are required. + // All other fields are OPTIONAL and agents MAY ignore unsupported fields. + // Agents SHOULD advertise which context fields they utilize via capabilities. + + /** Active document URI */ + uri: string; + + /** Document version for cache invalidation */ + documentVersion: number; + + /** Cursor position(s) in the active file */ + selections: Selection[]; + + /** Language identifier (e.g., "typescript", "python") */ + languageId?: string; + + /** Currently selected text, if any */ + selectedText?: string; + + /** Visible range in the editor viewport */ + visibleRange?: Range; + + /** Recently modified files (URIs), ordered by recency */ + recentFiles?: string[]; + + /** Recent edits made by the user (for pattern detection) */ + recentEdits?: RecentEdit[]; + + /** Agent-specific context extensions */ + extensions?: Record; +} + +interface Selection { + /** Cursor/anchor position */ + anchor: Position; + /** Active/head position (may differ from anchor if text selected) */ + active: Position; +} + +interface RecentEdit { + /** File that was edited */ + uri: string; + /** Range that was replaced */ + range: Range; + /** New text that was inserted */ + newText: string; + /** Text that was replaced (for understanding intent) */ + oldText?: string; + /** Timestamp of the edit */ + timestamp: number; +} +``` + +### 2. Suggestion Requests (Client → Agent) + +Clients can explicitly request suggestions for the current context: + +```typescript +// Method: suggestions/request +interface SuggestionsRequestParams { + /** Current edit context */ + context: EditContext; + + /** Maximum number of suggestions to return */ + maxSuggestions?: number; + + /** Timeout in milliseconds (agents SHOULD return partial results if exceeded) */ + timeout?: number; + + /** Whether to include navigation suggestions */ + includeNavigation?: boolean; + + /** Trigger kind indicating what prompted this request */ + triggerKind?: SuggestionTriggerKind; +} + +type SuggestionTriggerKind = + | "automatic" // Triggered by typing/editing + | "manual" // Explicitly requested by user (e.g., keyboard shortcut) + | "continuation" // Triggered after accepting a previous suggestion +``` + +### 3. Suggestion Responses (Agent → Client) + +Agents respond with suggestions, which can be **edits** or **navigation targets**: + +```typescript +// Response to suggestions/request +interface SuggestionsResponse { + /** Edit suggestions */ + suggestions: Suggestion[]; + + /** Whether more suggestions may be available (for pagination) */ + isIncomplete?: boolean; +} + +type Suggestion = EditSuggestion | NavigationSuggestion; + +interface EditSuggestion { + type: "edit"; + + /** Unique identifier for this suggestion */ + id: string; + + /** Human-readable label (shown in UI) */ + label: string; + + /** Longer description explaining the suggestion */ + description?: string; + + /** The proposed text edits */ + edits: TextEdit[]; + + /** Where to place cursor after applying edits */ + cursorPosition?: Position; + + /** Confidence score (0.0 - 1.0) */ + confidence?: number; + + /** Category of suggestion for UI grouping/filtering */ + kind?: EditSuggestionKind; + + /** Priority for ordering (higher = more prominent) */ + priority?: number; + + /** If true, this edit is part of a sequence; more suggestions follow */ + hasFollowUp?: boolean; + + /** Document version this suggestion is valid for. Client SHOULD discard if version changes */ + validForDocumentVersion?: number; + + /** Agent-specific metadata */ + metadata?: Record; +} + +type EditSuggestionKind = + | "completion" // Complete partial/incomplete code + | "continuation" // Continue an established pattern + | "insertion" // Insert new code + | "deletion" // Remove code + | "replacement" // Replace existing code + | "fix" // Fix an error or issue + | "refactor" // Improve/restructure code + | "related" // Edit related code elsewhere in codebase + | "documentation" // Add/update comments or docs + | "test" // Add/update tests + | "other" + | `x-${string}`; // Custom agent-specific kinds + +interface NavigationSuggestion { + type: "navigation"; + + /** Unique identifier */ + id: string; + + /** Human-readable label */ + label: string; + + /** Target location to navigate to */ + target: Location; + + /** Why this navigation is suggested */ + reason?: string; + + /** Confidence score (0.0 - 1.0) */ + confidence?: number; + + /** Priority for ordering */ + priority?: number; +} + +interface Location { + uri: string; + range: Range; +} + +interface TextEdit { + /** Target file URI */ + uri: string; + /** Range to replace (empty range = insertion) */ + range: Range; + /** New text to insert */ + newText: string; +} +``` + +### 4. Suggestion Actions (Client → Agent) + +Clients notify agents of user interactions with suggestions: + +```typescript +// Notification: suggestions/accept +interface SuggestionsAcceptParams { + /** ID of the accepted suggestion */ + suggestionId: string; + + /** For partial accepts: the range that was accepted */ + acceptedRange?: Range; + + /** For partial accepts: how much was accepted */ + acceptKind?: "full" | "word" | "line" | "custom"; +} + +// Notification: suggestions/reject +interface SuggestionsRejectParams { + /** ID of the rejected suggestion */ + suggestionId: string; + + /** Reason for rejection (for learning) */ + reason?: "wrong" | "irrelevant" | "timing" | "style" | "other"; +} + +// Notification: suggestions/dismiss +interface SuggestionsDismissParams { + /** IDs of dismissed suggestions (user moved on without explicit action) */ + suggestionIds: string[]; + + /** Why suggestions were dismissed */ + reason?: "context_changed" | "timeout" | "user_continued" | "other"; +} + +// Notification: suggestions/retained +interface SuggestionsRetainedParams { + /** ID of a previously accepted suggestion */ + suggestionId: string; + + /** How the suggestion was treated after acceptance */ + retention: "kept" | "modified" | "deleted"; + + /** If modified, how much changed (0.0 = completely rewritten, 1.0 = kept as-is) */ + similarity?: number; + + /** Time in ms between acceptance and this report */ + elapsedMs: number; +} +``` + +### 5. Proactive Suggestions (Agent → Client) + +Agents can push suggestions without explicit requests: + +```typescript +// Notification: suggestions/offer +interface SuggestionsOfferParams { + /** The suggestions being offered */ + suggestions: Suggestion[]; + + /** Context these suggestions apply to */ + context: EditContext; + + /** TTL in milliseconds before suggestions expire */ + ttl?: number; + + /** If set, these suggestions replace previously offered ones */ + replacesPreviousOffer?: boolean; +} + +// Notification: suggestions/invalidate +// Note: This notification is OPTIONAL and advisory. Clients own suggestion +// validity via documentVersion and SHOULD self-invalidate when it changes. +// Agents MAY send invalidation hints, but clients MUST NOT depend on receiving them. +interface SuggestionsInvalidateParams { + /** IDs of suggestions that are no longer valid */ + suggestionIds: string[]; + + /** Reason for invalidation */ + reason: "context_changed" | "expired" | "superseded" | "other"; +} +``` + +### 6. Request Lifecycle + +Clients can cancel in-flight suggestion requests: + +```typescript +// Uses ACP's $/cancel_request notification +// Agents SHOULD stop processing and return partial results if available +``` + +## Shiny future + +> How will things play out once this feature exists? + +Once next edit suggestions are part of ACP: + +1. **Seamless editing flow**: Users code with an AI partner that anticipates their needs. After making an edit, relevant follow-up suggestions appear naturally, accepted with Tab. + +2. **Tab-tab-tab sequences**: Accepting one suggestion immediately surfaces the next. Users can flow through a series of related edits without pausing. + +3. **Intelligent navigation**: Agents predict not just what to edit, but where to go next. After completing a function, jump to its test file. After fixing a bug, jump to similar occurrences. + +4. **Pattern-aware assistance**: Agents detect repetitive edits and proactively suggest completing the pattern across files. + +5. **Learning from feedback**: The accept/reject/dismiss/retain feedback loop allows agents to improve over time, learning user preferences and coding patterns. + +6. **Cross-file intelligence**: Unlike LSP completions confined to the current file, agent suggestions span the codebase—updating tests, documentation, and related code. + +7. **Standardized UX**: Clients implement consistent suggestion UI knowing all ACP-compatible agents speak the same protocol. + +8. **Composable with ACP features**: Suggestions integrate with session modes, permissions, and agent plans. + +## Implementation details and plan + +> Tell me more about your implementation. What is your detailed implementation plan? + +### Capability Advertisement + +Agents advertise suggestion support in initialization: + +```json +{ + "capabilities": { + "suggestions": { + "supportedTriggerKinds": ["automatic", "manual", "continuation"], + "supportedEditKinds": ["completion", "continuation", "fix", "refactor", "related"], + "supportsNavigation": true, + "supportsProactiveOffers": true, + "maxConcurrentRequests": 3 + } + } +} +``` + +Clients advertise their support: + +```json +{ + "capabilities": { + "suggestions": { + "supportsPartialAccept": true, + "supportsRetentionTracking": true, + "acceptsProactiveOffers": true, + "maxDisplayedSuggestions": 5 + } + } +} +``` + +### Protocol Flows + +**Basic request-response flow:** + +``` +Client Agent + | | + |--[editContext/didChange]--------->| (user edited/moved cursor) + | | + |--[suggestions/request]----------->| (client wants suggestions) + | | + |<-[suggestions/request response]---| (agent returns suggestions) + | | + |--[suggestions/accept]------------>| (user pressed Tab) + | | +``` + +**Chained suggestions (tab-tab-tab):** + +``` +Client Agent + | | + |--[suggestions/accept]------------>| (user accepted suggestion) + | | + |<-[suggestions/offer]--------------| (agent offers follow-up) + | | + |--[suggestions/accept]------------>| (user accepts again) + | | + |<-[suggestions/offer]--------------| (another follow-up) + | | +``` + +**Proactive flow with invalidation:** + +``` +Client Agent + | | + |--[editContext/didChange]--------->| (user made an edit) + | | + |<-[suggestions/offer]--------------| (agent proactively suggests) + | | + |--[editContext/didChange]--------->| (user continued typing) + | | + |<-[suggestions/invalidate]---------| (previous suggestions invalid) + |<-[suggestions/offer]--------------| (new suggestions) + | | +``` + +### Latency and Performance Guidelines + +| Metric | Target | Notes | +|--------|--------|-------| +| Request to complete response | <500ms | For good UX | +| Context notification debounce | 50-150ms | Client-side | +| Suggestion TTL | 5-30s | Agent-configurable | + +Recommendations: +1. Clients SHOULD debounce `editContext/didChange` (50-150ms recommended) +2. Clients SHOULD cancel pending requests when context changes significantly +3. Agents SHOULD respect timeout and return partial results if available +4. Clients SHOULD track retention asynchronously (e.g., 5-30s after acceptance) +5. Clients own suggestion validity via `documentVersion` and SHOULD self-invalidate + +### Integration with Existing ACP Features + +**Session Modes**: Suggestions can be mode-aware. An agent in "architect" mode might suggest documentation edits, while "code" mode suggests implementations. + +**Tool Calls**: Complex suggestions could trigger tool call flows for edits requiring additional context or confirmation. + +**Permissions**: Suggestions are advisory; actual file writes still go through normal `fs/writeTextFile` permission flows. + +**Request Cancellation**: Uses the existing `$/cancel_request` notification from the unstable cancellation RFD. + +### Implementation Phases + +**Phase 1 - Core Protocol (Draft)** +- Define schema types for suggestions, context, and basic actions +- Implement `editContext/didChange` notification +- Implement `suggestions/request` and response +- Implement `suggestions/accept`, `suggestions/reject`, `suggestions/dismiss` +- Add capability advertisement + +**Phase 2 - Navigation & Chaining (Preview)** +- Add `NavigationSuggestion` type +- Implement `hasFollowUp` and chained suggestion flow +- Add `suggestions/offer` for proactive suggestions +- Add `suggestions/invalidate` for stale suggestions + +**Phase 3 - Analytics & Learning (Preview)** +- Add `suggestions/retained` notification +- Define retention tracking semantics +- Implement partial accept tracking + +**Phase 4 - Refinements (Preview → Completed)** +- Gather feedback from implementations +- Performance optimization guidelines +- Stabilize and document best practices + +## Frequently asked questions + +> What questions have arisen over the course of authoring this document or during subsequent discussions? + +### How does this differ from LSP completions? + +| Aspect | LSP Completions | ACP Edit Suggestions | +|--------|-----------------|----------------------| +| Scope | Current cursor position | Anywhere in codebase | +| Trigger | Typing specific characters | Any context change | +| Content | Token/snippet completion | Full edits, deletions, multi-file | +| Intelligence | Syntax/type-driven | Semantic, intent-driven | +| Navigation | No | Yes (cursor jumps) | + +The two are complementary. Clients show LSP completions for immediate token completion and agent suggestions for higher-level edit predictions. + +### Why include navigation suggestions? + +Cursor's research shows that predicting "where to edit next" is as valuable as predicting "what to edit." After completing a function, users often need to: +- Jump to write a test +- Navigate to update documentation +- Move to similar code elsewhere + +Navigation suggestions reduce the cognitive load of figuring out where to go next. + +### How do chained suggestions (tab-tab-tab) work? + +When a suggestion has `hasFollowUp: true`, the agent signals that accepting it will likely produce another suggestion. After the client sends `suggestions/accept`, the agent can immediately send `suggestions/offer` with the next suggestion in the sequence. + +This creates a fluid flow where users can Tab through a series of related edits without waiting or requesting. + +### Why not stream suggestions character-by-character? + +We considered streaming but decided against it: + +1. **No production precedent**: Major NES implementations (GitHub Copilot, Cursor Tab, Supermaven) return complete suggestions rather than streaming character-by-character +2. **UX concerns**: Character-by-character updates can be distracting during editing, breaking the user's flow +3. **Semantic complexity**: Streaming deletions or multi-edit replacements is unclear—what does it mean to "stream" a deletion? +4. **Simplicity**: Batch responses simplify both protocol and implementation + +Latency is better addressed through small, task-specific models optimized for fast inference rather than streaming partial results. + +### How should clients handle partial acceptance? + +The protocol supports partial accept via `acceptedRange` and `acceptKind`: + +- **Word-by-word**: User presses Ctrl+Right to accept one word +- **Line-by-line**: User presses a key to accept one line +- **Custom range**: User selects specific portion to accept + +Clients should send partial accept notifications so agents can learn user preferences. + +### What about retention tracking? + +After a user accepts a suggestion, they might: +- Keep it exactly as suggested +- Modify it slightly +- Delete it entirely + +Tracking this (via `suggestions/retained`) helps agents understand suggestion quality beyond simple accept rates. A suggestion accepted but immediately deleted is worse than one that's kept. + +### Should this be part of ACP or a separate protocol? + +This belongs in ACP because: +1. Suggestions benefit from session context (prompts, plans, tool history) +2. Accepting suggestions may trigger ACP tool calls +3. Agent capabilities and permissions apply +4. Unified protocol reduces integration complexity +5. Shares infrastructure (transports, auth, capabilities) + +### How do agents decide what to suggest? + +Implementation-specific. Common approaches: +- **Edit patterns**: Detect repetitive edits, suggest continuing the pattern +- **Cursor context**: Analyze incomplete code at cursor position +- **Conversation history**: Consider recent prompts and stated intent +- **Codebase analysis**: Suggest related edits based on dependencies +- **Error detection**: Suggest fixes for detected issues + +The protocol provides communication; suggestion intelligence is the agent's responsibility. + +### What about multi-file and multi-cursor edits? + +The `TextEdit[]` array supports: +- **Multi-cursor**: Multiple edits in one file +- **Multi-file**: Edits across different URIs + +Clients may preview these atomically or let users cherry-pick. + +### How does invalidation work? + +Suggestions become stale when: +- User continues typing (context changed) +- Document version changes +- TTL expires +- Agent generates better suggestions + +**Clients own suggestion validity** via `documentVersion`. When the document version changes, clients SHOULD self-invalidate stale suggestions without waiting for agent notification. Suggestions include a `validForDocumentVersion` field to make this explicit. + +Agents MAY send `suggestions/invalidate` as an optimization hint, but this notification is advisory—clients MUST NOT depend on receiving it. This approach simplifies agent implementations by removing the requirement to track and notify about stale suggestions. + +### What alternative approaches did you consider? + +1. **Extending `session/prompt`**: Rejected—different latency requirements, avoid history pollution + +2. **MCP-style resources**: Suggestions are ephemeral and context-dependent, not static resources + +3. **Pure notification-based**: Rejected—request/response allows client control over when to request + +4. **Separate protocol**: Rejected—loses session context, duplicates infrastructure + +5. **Only edit suggestions (no navigation)**: Rejected—navigation is a key part of modern tab-completion UX + +### What about "Next Action Prediction" beyond edits? + +Cursor's vision includes predicting terminal commands, file operations, etc. This RFD focuses on edit and navigation suggestions as the core use case. Future RFDs could extend to: +- Terminal command suggestions +- File creation/deletion suggestions +- Git operation suggestions + +The architecture supports extension via new `Suggestion` type variants. + +## Revision history + +- 2025-12-09: Initial draft +- 2025-12-09: Major revision based on industry research + - Added navigation suggestions (cursor jumps) + - Added streaming support for ghost text UX + - Added partial acceptance tracking + - Added retention tracking for learning + - Added suggestion invalidation + - Expanded EditContext with languageId, documentVersion, selectedText, recentFiles + - Added chained suggestions (hasFollowUp) for tab-tab-tab flow + - Added latency guidelines + - Comprehensive FAQ additions +- 2025-12-11: Major revision based on community feedback (@olegtaratuhin) + - Removed streaming support (not aligned with production implementations) + - Made EditContext fields more optional/flexible (only uri, documentVersion, selections required) + - Added `extensions` field to EditContext for agent-specific context + - Clarified client-owned invalidation model via `validForDocumentVersion` + - Made `suggestions/invalidate` notification advisory, not required + - Added extensibility patterns (`x-` prefix for custom kinds, `metadata` field) + - Updated FAQ to explain streaming removal rationale +