diff --git a/apps/macos/Sources/Clawdbot/AnyCodable+Helpers.swift b/apps/macos/Sources/Clawdbot/AnyCodable+Helpers.swift index bada8e26a..831076040 100644 --- a/apps/macos/Sources/Clawdbot/AnyCodable+Helpers.swift +++ b/apps/macos/Sources/Clawdbot/AnyCodable+Helpers.swift @@ -1,6 +1,11 @@ +import ClawdbotKit import ClawdbotProtocol import Foundation +// Prefer the ClawdbotKit wrapper to keep gateway request payloads consistent. +typealias AnyCodable = ClawdbotKit.AnyCodable +typealias InstanceIdentity = ClawdbotKit.InstanceIdentity + extension AnyCodable { var stringValue: String? { self.value as? String } var boolValue: Bool? { self.value as? Bool } @@ -20,3 +25,23 @@ extension AnyCodable { } } } + +extension ClawdbotProtocol.AnyCodable { + var stringValue: String? { self.value as? String } + var boolValue: Bool? { self.value as? Bool } + var intValue: Int? { self.value as? Int } + var doubleValue: Double? { self.value as? Double } + var dictionaryValue: [String: ClawdbotProtocol.AnyCodable]? { self.value as? [String: ClawdbotProtocol.AnyCodable] } + var arrayValue: [ClawdbotProtocol.AnyCodable]? { self.value as? [ClawdbotProtocol.AnyCodable] } + + var foundationValue: Any { + switch self.value { + case let dict as [String: ClawdbotProtocol.AnyCodable]: + dict.mapValues { $0.foundationValue } + case let array as [ClawdbotProtocol.AnyCodable]: + array.map(\.foundationValue) + default: + self.value + } + } +} diff --git a/apps/macos/Sources/Clawdbot/ControlChannel.swift b/apps/macos/Sources/Clawdbot/ControlChannel.swift index 8aaca85a2..83f342e81 100644 --- a/apps/macos/Sources/Clawdbot/ControlChannel.swift +++ b/apps/macos/Sources/Clawdbot/ControlChannel.swift @@ -346,7 +346,7 @@ final class ControlChannel { let phase = event.data["phase"]?.value as? String ?? "" let name = event.data["name"]?.value as? String let meta = event.data["meta"]?.value as? String - let args = event.data["args"]?.value as? [String: AnyCodable] + let args = Self.bridgeToProtocolArgs(event.data["args"]) WorkActivityStore.shared.handleTool( sessionKey: sessionKey, phase: phase, @@ -357,6 +357,27 @@ final class ControlChannel { break } } + + private static func bridgeToProtocolArgs( + _ value: AnyCodable?) -> [String: ClawdbotProtocol.AnyCodable]? + { + guard let value else { return nil } + if let dict = value.value as? [String: ClawdbotProtocol.AnyCodable] { + return dict + } + if let dict = value.value as? [String: AnyCodable], + let data = try? JSONEncoder().encode(dict), + let decoded = try? JSONDecoder().decode([String: ClawdbotProtocol.AnyCodable].self, from: data) + { + return decoded + } + if let data = try? JSONEncoder().encode(value), + let decoded = try? JSONDecoder().decode([String: ClawdbotProtocol.AnyCodable].self, from: data) + { + return decoded + } + return nil + } } extension Notification.Name { diff --git a/apps/macos/Sources/Clawdbot/GatewayConnection.swift b/apps/macos/Sources/Clawdbot/GatewayConnection.swift index 83c297fc8..32e04db10 100644 --- a/apps/macos/Sources/Clawdbot/GatewayConnection.swift +++ b/apps/macos/Sources/Clawdbot/GatewayConnection.swift @@ -244,9 +244,9 @@ actor GatewayConnection { return trimmed.isEmpty ? nil : trimmed } - private func sessionDefaultString(_ defaults: [String: AnyCodable]?, key: String) -> String { - (defaults?[key]?.stringValue ?? "") - .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + private func sessionDefaultString(_ defaults: [String: ClawdbotProtocol.AnyCodable]?, key: String) -> String { + let raw = defaults?[key]?.value as? String + return (raw ?? "").trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } func cachedMainSessionKey() -> String? { diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayErrors.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayErrors.swift index 2b876467f..6ff8b8293 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayErrors.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayErrors.swift @@ -29,5 +29,10 @@ public struct GatewayDecodingError: LocalizedError, Sendable { public let method: String public let message: String + public init(method: String, message: String) { + self.method = method + self.message = message + } + public var errorDescription: String? { "\(self.method): \(self.message)" } }