Skip to content

feat: add acp client support#13

Merged
sirily11 merged 1 commit into
mainfrom
acp
May 15, 2026
Merged

feat: add acp client support#13
sirily11 merged 1 commit into
mainfrom
acp

Conversation

@sirily11

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings May 15, 2026 09:44
@vercel

vercel Bot commented May 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
rxcode Ready Ready Preview, Comment May 15, 2026 9:45am

Request Review

@autopilot-project-manager autopilot-project-manager Bot added the enhancement New feature or request label May 15, 2026
@sirily11 sirily11 enabled auto-merge (squash) May 15, 2026 09:45
@sirily11 sirily11 merged commit 94c3d71 into main May 15, 2026
8 checks passed
@sirily11 sirily11 deleted the acp branch May 15, 2026 09:47

Copilot AI 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.

Pull request overview

This PR adds Agent Client Protocol (ACP) support to RxCode, including registry-backed client installation/configuration, model discovery, and UI integration so ACP clients appear in the model picker and can power chat sessions.

Changes:

  • Add ACP client management UI (installed clients + registry browser + editor sheet) and integrate ACP client icons into pickers.
  • Introduce ACP runtime services: registry fetch + caching, binary installer, and an ACP JSON-RPC-over-stdio service with model probing/discovery events.
  • Extend persistence/session plumbing to support ACP-originated sessions and persist ACP client specs + registry snapshots.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
RxCode/Views/SettingsView.swift Adds a new “ACP Clients” settings tab; updates model picker section IDs/labels.
RxCode/Views/Settings/ACPClientSettingsTab.swift New UI for installing, enabling, editing, and removing ACP clients + registry search.
RxCode/Views/MainView.swift Updates model picker UI to handle per-section IDs/titles and show ACP client icons.
RxCode/Views/ACPIconView.swift Adds remote icon loading + disk/memory caching for ACP client/agent icons.
RxCode/Services/PersistenceService.swift Adds ACP session origin routing + persistence for ACP clients and registry snapshot URL.
RxCode/Services/ACPService.swift New ACP transport implementation (process spawn, JSON-RPC framing, streaming events, model probing).
RxCode/Services/ACPRegistryService.swift New service to fetch and snapshot the ACP registry with TTL caching.
RxCode/Services/ACPInstallerService.swift New installer to download/extract ACP binary distributions into Application Support.
RxCode/App/RxCodeApp.swift Adds ACP provider handling in menu bar status display.
RxCode/App/AppState.swift Integrates ACP provider: client persistence, registry refresh, model label resolution, stream routing.
Packages/Sources/RxCodeCore/Models/StreamEvent.swift Adds an event for “ACP models discovered” to sync picker state.
Packages/Sources/RxCodeCore/Models/SessionOrigin.swift Adds .acpAgent session origin.
Packages/Sources/RxCodeCore/Models/AgentModel.swift Adds AgentProvider.acp and default session origin mapping.
Packages/Sources/RxCodeCore/Models/ACPClient.swift Adds ACP registry schema + installed client spec models.
Packages/Sources/RxCodeChatKit/StatusLineView.swift Adds ACP provider label/version handling.
Packages/Sources/RxCodeChatKit/PlanCardView.swift Keeps “plan ready” text visible alongside plan card.
Packages/Sources/RxCodeChatKit/MessageListView.swift Stops suppressing plan-ready followups (but leaves some dead logic).
Comments suppressed due to low confidence (1)

Packages/Sources/RxCodeChatKit/MessageListView.swift:338

  • _ = hasRecentPlanCard is now a no-op and leaves a dead variable (hasRecentPlanCard) in the function. Since plan-ready followups are intentionally no longer suppressed, consider removing hasRecentPlanCard and the related (now-unused) code paths entirely to avoid confusion and keep the logic easy to follow.
        let hasExitPlan = assistantRun.contains { PlanCardView.containsExitPlanMode($0) }
        var hasRecentPlanCard = false

        for message in assistantRun {
            if hasExitPlan && PlanCardView.isPurePlanFileWriteMessage(message) {
                continue
            }

            // Plan-ready follow-up messages (e.g. "Plan is ready at /…/plans/foo.md")
            // are intentionally kept visible alongside the plan card so the user
            // sees the summary while approval is pending.
            _ = hasRecentPlanCard

            if PlanCardView.containsExitPlanMode(message) {
                hasRecentPlanCard = true
            }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +71 to +78
actor ACPIconCache {
static let shared = ACPIconCache()
private var cache: [URL: NSImage] = [:]

func image(for url: URL) -> NSImage? { cache[url] }

func store(_ image: NSImage, for url: URL) { cache[url] = image }

Comment on lines +60 to +67
do {
let (data, _) = try await URLSession.shared.data(from: parsed)
guard let nsImage = ACPIconCache.makeImage(from: data, sourceURL: parsed) else { return }
await ACPIconCache.shared.store(nsImage, for: parsed)
image = nsImage
} catch {
// Fall back to placeholder; nothing to log noisily about.
}
Comment on lines +525 to +528
return (path, args, env)
case .custom(let command, let args, let env):
return (command, args, env)
}
Comment on lines +885 to +894
private func handleFsReadTextFile(streamId: UUID, id: JSONValue, params: JSONValue) async {
guard let path = params.objectValue?["path"]?.stringValue else {
sendError(streamId: streamId, id: id, code: -32602, message: "Missing path")
return
}
do {
let line = params.objectValue?["line"]?.intValue
let limit = params.objectValue?["limit"]?.intValue
let content = try Self.readTextFile(path: path, line: line, limit: limit)
sendResult(streamId: streamId, id: id, result: .object(["content": .string(content)]))
Comment on lines +900 to +911
private func handleFsWriteTextFile(streamId: UUID, id: JSONValue, params: JSONValue) async {
guard let path = params.objectValue?["path"]?.stringValue,
let content = params.objectValue?["content"]?.stringValue else {
sendError(streamId: streamId, id: id, code: -32602, message: "Missing path or content")
return
}
do {
try Self.writeTextFile(path: path, content: content)
sendResult(streamId: streamId, id: id, result: .object([:]))
} catch {
sendError(streamId: streamId, id: id, code: -32000, message: error.localizedDescription)
}
Comment on lines +46 to +50
static func installRoot() -> URL {
let fm = FileManager.default
let support = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
return support.appendingPathComponent("RxCode/acp-binaries", isDirectory: true)
}
@sirily11

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 1.3.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

enhancement New feature or request released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants