From c568284f1b27e7b12f7f199712f7d72090685bff Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 9 Dec 2025 03:33:16 +0100 Subject: [PATCH] Build: fix RPC sendable params and CLI imports --- apps/macos/Sources/Clawdis/AgentRPC.swift | 8 ++++++-- apps/macos/Sources/Clawdis/ControlChannel.swift | 12 ++++++++---- apps/macos/Sources/Clawdis/VoiceWakeChime.swift | 4 ++-- src/cli/program.ts | 3 +-- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/macos/Sources/Clawdis/AgentRPC.swift b/apps/macos/Sources/Clawdis/AgentRPC.swift index c8f9bb94f..73260c6ac 100644 --- a/apps/macos/Sources/Clawdis/AgentRPC.swift +++ b/apps/macos/Sources/Clawdis/AgentRPC.swift @@ -1,6 +1,10 @@ import Foundation import OSLog +struct ControlRequestParams: @unchecked Sendable { + let raw: [String: AnyHashable] +} + actor AgentRPC { static let shared = AgentRPC() @@ -173,13 +177,13 @@ actor AgentRPC { } } - func controlRequest(method: String, params: [String: Any]? = nil) async throws -> Data { + func controlRequest(method: String, params: ControlRequestParams? = nil) async throws -> Data { if self.process?.isRunning != true { try await self.start() } let id = UUID().uuidString var frame: [String: Any] = ["type": "control-request", "id": id, "method": method] - if let params { frame["params"] = params } + if let params { frame["params"] = params.raw } let data = try JSONSerialization.data(withJSONObject: frame) guard let stdinHandle else { throw RpcError(message: "stdin missing") } return try await withCheckedThrowingContinuation { (cont: CheckedContinuation) in diff --git a/apps/macos/Sources/Clawdis/ControlChannel.swift b/apps/macos/Sources/Clawdis/ControlChannel.swift index e94a02da1..7a060d623 100644 --- a/apps/macos/Sources/Clawdis/ControlChannel.swift +++ b/apps/macos/Sources/Clawdis/ControlChannel.swift @@ -71,6 +71,7 @@ final class ControlChannel: ObservableObject { enum ConnectionState: Equatable { case disconnected + case connecting case connected case degraded(String) } @@ -87,6 +88,7 @@ final class ControlChannel: ObservableObject { func configure() async { do { + self.state = .connecting try await AgentRPC.shared.start() self.state = .connected } catch { @@ -96,11 +98,11 @@ final class ControlChannel: ObservableObject { func configure(mode: Mode) async throws { // Mode is retained for API compatibility; transport is always stdio now. - try await self.configure() + await self.configure() } func health(timeout: TimeInterval? = nil) async throws -> Data { - let params = timeout.map { ["timeoutMs": Int($0 * 1000)] } + let params = timeout.map { ControlRequestParams(raw: ["timeoutMs": AnyHashable(Int($0 * 1000))]) } do { let start = Date() let payload = try await AgentRPC.shared.controlRequest(method: "health", params: params) @@ -120,7 +122,7 @@ final class ControlChannel: ObservableObject { return try? JSONDecoder().decode(ControlHeartbeatEvent.self, from: data) } - func request(method: String, params: [String: Any]? = nil) async throws -> Data { + func request(method: String, params: ControlRequestParams? = nil) async throws -> Data { do { let data = try await AgentRPC.shared.controlRequest(method: method, params: params) self.state = .connected @@ -132,7 +134,9 @@ final class ControlChannel: ObservableObject { } func sendSystemEvent(_ text: String) async throws { - _ = try await self.request(method: "system-event", params: ["text": text]) + _ = try await self.request( + method: "system-event", + params: ControlRequestParams(raw: ["text": AnyHashable(text)])) } } diff --git a/apps/macos/Sources/Clawdis/VoiceWakeChime.swift b/apps/macos/Sources/Clawdis/VoiceWakeChime.swift index 3eb8c35b3..fb0947024 100644 --- a/apps/macos/Sources/Clawdis/VoiceWakeChime.swift +++ b/apps/macos/Sources/Clawdis/VoiceWakeChime.swift @@ -79,14 +79,14 @@ struct VoiceWakeChimeCatalog { private static let discoveredSoundMap: [String: URL] = { var map: [String: URL] = [:] - for root in self.searchRoots { + for root in Self.searchRoots { guard let contents = try? FileManager.default.contentsOfDirectory( at: root, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else { continue } - for url in contents where self.allowedExtensions.contains(url.pathExtension.lowercased()) { + for url in contents where Self.allowedExtensions.contains(url.pathExtension.lowercased()) { let name = url.deletingPathExtension().lastPathComponent // Preserve the first match in priority order. if map[name] == nil { diff --git a/src/cli/program.ts b/src/cli/program.ts index 71167fc90..e5521ca46 100644 --- a/src/cli/program.ts +++ b/src/cli/program.ts @@ -12,7 +12,6 @@ import { getLastHeartbeatEvent, onHeartbeatEvent, } from "../infra/heartbeat-events.js"; -import { onAgentEvent } from "../infra/agent-events.js"; import { getResolvedLoggerSettings } from "../logging.js"; import { loginWeb, @@ -35,8 +34,8 @@ import { } from "../webchat/server.js"; import { createDefaultDeps, logWebSelfId } from "./deps.js"; import { onAgentEvent } from "../infra/agent-events.js"; +import { enqueueSystemEvent } from "../infra/system-events.js"; import { - enqueueSystemEvent, listSystemPresence, updateSystemPresence, } from "../infra/system-presence.js";