Skip to content

Conversation

nikomatsakis
Copy link
Contributor

Issue #, if available:

Description of changes:

Implement the Agent Client Protocol. Don't read this code yet, it's very messy and not finished; I plan to clean it up.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

--

RFC draft (motivation, high-level idea)

Summary

Add Agent Client Protocol (ACP) server capability to Amazon Q CLI, allowing editors like Zed and Neovim to use Q as an AI coding assistant through a standardized JSON-RPC interface.

What is ACP? Agent Client Protocol is a JSON-RPC standard that lets editors communicate with AI agents over stdio. Instead of building custom integrations for each editor, agents implement ACP once and work with any ACP-compatible editor.

User Experience: Users run q acp to start Q in server mode, then configure their editor to connect to this process. The editor handles the UI while Q provides the AI capabilities - same models, tools, and features as q chat.

Motivation

Problem: Currently Q CLI provides two options to users: an interactive, CLI-based chat interface and a non-interactive mode. But some use-cases demand interaction but in a programmatic or scripted fashion. This includes custom GUI in editors, automation tools, IDEs, web interfaces, and other applications. But right now each application must adapt to each agent independently. This means applications are likely only to build on the most widely used alternatives (e.g., with the Claude Code SDK, which provides programmatic access to the Claude Code agent).

Solution: ACP provides an alternative, using a JSON-RPC protocol inspired by MCP to let any application integrate with any agent, sending user input and receiving the agent's responses in a streaming fashion.

Immediate Benefits: This provides immediate value to Q CLI users by allowing them to access Q from editors that support ACP (Zed, Neovim) with native integration - same models, tools, and MCP servers, but in their preferred editor instead of switching to terminal.

Strategic Benefits: Supporting ACP also helps boost the protocol itself. The more editors and agents use ACP, the more likely it will succeed as a standard. This avoids the problem of tooling being built atop proprietary options like the Claude Code SDK, which would lock Q CLI out of future editor integrations.

Guide-level Explanation

Setup:

# Start Q in ACP server mode
q acp --agent my-profile

# Configure editor to connect to this process
# (Editor-specific configuration)

Usage: Once connected, users interact with Q through their editor's AI interface:

  • Chat with Q in editor panels/sidebars
  • Q can read/write files through the editor (sees unsaved changes)
  • Tool execution with editor-native permission prompts
  • Same models, agents, and MCP servers as terminal Q CLI

JSON-RPC Communication: Editor and Q communicate over stdio using structured messages:

// Editor → Q: User sends a message
{"method": "session/prompt", "params": {"sessionId": "123", "messages": [...]}}

// Q → Editor: Streaming response
{"method": "session/update", "params": {"sessionId": "123", "update": {"AgentMessageChunk": {...}}}}

nikomatsakis and others added 3 commits September 28, 2025 16:08
Add Agent Client Protocol (ACP) server capability to Amazon Q CLI,
allowing editors like Zed and Neovim to use Q as an AI coding assistant
through a standardized JSON-RPC interface.

Core implementation:
- Actor-based ACP server architecture with session management
- Complete ACP protocol support (initialize, sessions, prompts, streaming)
- `q acp --agent profile` command with feature gating (acp.enabled setting)
- Integration with existing Q CLI conversation and tool systems
- Transport over stdio with proper async handling

The ACP server reuses Q CLI's existing ConversationState, API client,
and tool systems, providing the same models and capabilities as
`q chat` but accessible from editors through the standardized protocol.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Add comprehensive testing infrastructure for ACP server development:

- Mock LLM system with bidirectional channel-based communication
  - Enables deterministic testing without real backend calls
  - Supports scripted conversation flows and response validation
  - Actor-based design for clean async testing

- ACP client test utility for integration testing
  - Simple ACP client that can connect to the server
  - Validates end-to-end protocol functionality
  - Useful for manual testing and automated integration tests

This infrastructure enables reliable development and testing of the
ACP server implementation without dependencies on external services.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Add comprehensive documentation for the ACP server implementation:

- RFC document (agent-client-protocol-rfc.md) with detailed technical
  specification including:
  - Complete motivation and architectural design
  - Protocol mapping between ACP and Q CLI systems
  - Implementation decisions and deferred features
  - Session lifecycle and authentication flow

- Implementation tracking document (PR_PLAN.md) showing:
  - Phase-by-phase development progress
  - Current implementation status (functionally complete for basic chat)
  - Remaining work items for advanced features

This documentation provides reviewers with complete context for the
ACP integration design and implementation approach.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
nikomatsakis and others added 8 commits September 28, 2025 18:01
Remove references to missing chat-script crate that was preventing
workspace compilation. This appears to be a leftover reference from
previous development work.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Update lock file after removing chat-script dependency references.
This reflects the version changes and dependency updates from the
workspace reorganization.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Add cancellation support to ACP server and client:

Server-side changes:
- Modify handle_prompt to accept session_rx parameter
- Use tokio::select! to monitor both LLM stream and cancellation messages
- Handle Cancel messages by dropping stream and returning Cancelled response
- Reset conversation state on cancellation

Client-side changes:
- Add Cancel variant to ClientConnectionMethod enum
- Add public cancel() method to AcpClientConnectionHandle
- Wire up cancel handling in client actor loop

This enables graceful cancellation of in-progress prompts while maintaining
the existing actor pattern without additional complexity.
…rocessing

Instead of just logging warnings when receiving non-cancel messages during
prompt processing, properly respond with errors to let clients know their
requests failed.

Changes:
- Add Debug derive to ServerSessionMethod enum
- Match on specific message types and send error responses via oneshot channels
- Use structured error handling instead of silent warnings

This provides clearer feedback to clients about why their requests are
being rejected during active prompt processing.
- Replace MockConversationState with MockLLMContext for cleaner API
- Implement per-turn execution model matching real LLM behavior
- Add streaming integration via MockLLMHandle receiver channels
- Update ApiClient::send_message to return streams instead of collecting vectors
- Add clean test API with read_user_message() and respond_to_user()
- Fix performance issue by removing clone() from hot path
- Add MockLLMError for proper error handling
- Update ACP test harness to use new stateless API
- Maintain backward compatibility with type alias

This refactor enables deterministic testing of the actor-based ACP system
with a mock that behaves like real stateless LLMs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

feat(mock-llm): Implement sophisticated regex-based conversation matching API

Replace primitive read_user_message() API with powerful pattern matching:

- Add match_conversation() method with regex patterns for history and current message
- Support named capture groups (?P<name>...) with union of all captures
- Add convenience methods match_and_respond() and match_and_respond_with_captures()
- Sequential pattern matching against conversation history subsequences
- Proper message formatting ("user: content" / "assistant: content")

API improvements:
- Declarative pattern-based testing vs imperative contains() checks
- Structured data extraction through regex captures
- Template-based responses with {capture_name} substitution
- Clean separation of matching logic from response generation

Performance fixes:
- Remove unnecessary conversation.clone() from ApiClient::send_message
- Only affects mock path, production paths use moved values directly

Updated ACP tests to demonstrate sophisticated pattern matching capabilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

fix(mock-llm): Improve error handling with proper Result types

Replace Option returns with Result<Option<T>> for better error propagation:

- match_conversation() now returns Result<Option<ConversationMatches>>
- Regex compilation errors propagated with clear error messages
- Internal errors properly surfaced instead of silently failing
- Convenience methods return Result<bool> for success/match indication

Error handling improvements:
- Clear error messages for malformed regex patterns
- Proper propagation of channel closure errors
- Future should return eyre::Result<()> - Ok ends turn, Err sends error

API semantics:
- Ok(Some(captures)) - patterns matched with capture groups
- Ok(None) - valid patterns but no match
- Err(...) - regex compilation or internal error

Updated ACP tests to handle new Result<Option<_>> pattern properly.
Enhanced documentation to clarify error conditions and return semantics.

This fixes the issue where regex compilation errors were silently swallowed,
making debugging pattern matching much easier for test writers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

feat(mock-llm): Add declarative pattern matching with tuple array API

Introduce powerful new try_patterns() method that dramatically simplifies mock LLM test code:

New API:
- try_patterns(&[(&[], pattern, response)]) - declarative pattern matching
- Automatic regex substitution with proper $name syntax
- Pattern precedence - tries in order until first match
- Returns Ok(()) on match, Err("unexpected input") if no match

Key improvements:
- Uses regex::Captures::expand() for robust $name substitution
- Supports both $name and ${name} capture group syntax
- Single method call replaces complex imperative if-chains
- Clear pattern priority and fallback handling

Example transformation:
Before: 20+ lines of manual pattern matching with early returns
After: 5 lines of declarative pattern configuration

```rust
// Before: imperative, verbose
if ctx.match_and_respond(...).await? { return Ok(()); }
if ctx.match_and_respond_with_captures(...).await? { return Ok(()); }
// Complex fallback logic...

// After: declarative, clean
ctx.try_patterns(&[
    (&[], r"(?i)hi,?\s+claude", "Hi, you! What's your name?"),
    (&[r"assistant.*name"], r"(?P<name>\w+)", "Hi $name, I'm Q!"),
    (&[], r".*", "I didn't understand that."),
]).await
```

This makes writing conversational mock LLM tests much more pleasant and maintainable.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

fix: Resolve MockLLM compilation errors and update PR plan

Fix compilation issues from MockLLM streaming refactor:

Parser test fixes:
- Add create_mock_receiver() helper to convert Vec<ChatResponseStream> to streaming format
- Update SendMessageOutput::Mock to use new Receiver<Result<...>> format
- Maintain backward compatibility for existing tests

Struct field fixes:
- Add missing 'title' field to PromptArgument initializers
- Add missing 'title' and 'icons' fields to rmcp::model::Prompt initializers
- Use sensible defaults for test scenarios

All tests now pass:
- test_response_parser_ignores_licensed_code: ✅
- test_response_parser_avoid_invalid_json: ✅
- Full project compilation: ✅ (309 tests total)

Update PR_PLAN.md:
- Mark Phase 2.5 as ✅ COMPLETED with detailed achievements
- Document new declarative test API improvements
- Add 🔍 READY FOR REVIEW section with key review areas
- Highlight sophisticated pattern matching and streaming capabilities

The MockLLM refactor work is now complete and ready for in-depth code review.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Implement set_mock_output method for new stateless MockLLM API
- Add count_user_messages() helper to MockLLMContext for cleaner response indexing
- Remove obsolete _disabled_test_mock_llm_integration test (redundant with ACP tests)
- Convert JSON response groups to sequential mock responses based on user message count
- Add fallback handling when predefined responses are exhausted

The set_mock_output method now works with the new MockLLM architecture,
enabling tests that use predefined JSON response sequences.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

fix(mock-llm): Fix compilation errors in set_mock_output

- Fix closure ownership issue by cloning response_groups within closure
- Fix unused variable warning by prefixing with underscore
- Enable set_mock_output method to compile with new MockLLM API

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

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

1 participant