Mac: type agent events end-to-end

This commit is contained in:
Peter Steinberger
2025-12-09 15:38:22 +01:00
parent 318457cb2c
commit 20d247b3f7
2 changed files with 19 additions and 23 deletions

View File

@@ -97,7 +97,8 @@ struct AgentEventsWindow_Previews: PreviewProvider {
seq: 1, seq: 1,
stream: "tool", stream: "tool",
ts: Date().timeIntervalSince1970 * 1000, ts: Date().timeIntervalSince1970 * 1000,
data: ["phase": AnyCodable("start"), "name": AnyCodable("bash")]) data: ["phase": AnyCodable("start"), "name": AnyCodable("bash")],
summary: nil)
AgentEventStore.shared.append(sample) AgentEventStore.shared.append(sample)
return AgentEventsWindow() return AgentEventsWindow()
} }

View File

@@ -13,12 +13,14 @@ struct ControlHeartbeatEvent: Codable {
let reason: String? let reason: String?
} }
struct ControlAgentEvent: Codable, Sendable { struct ControlAgentEvent: Codable, Sendable, Identifiable {
var id: String { "\(runId)-\(seq)" }
let runId: String let runId: String
let seq: Int let seq: Int
let stream: String let stream: String
let ts: Double let ts: Double
let data: [String: AnyCodable] let data: [String: AnyCodable]
let summary: String?
} }
enum ControlChannelError: Error, LocalizedError { enum ControlChannelError: Error, LocalizedError {
@@ -135,31 +137,24 @@ final class ControlChannel: ObservableObject {
queue: .main) queue: .main)
{ [weak self] note in { [weak self] note in
guard let self, guard let self,
let obj = note.userInfo as? [String: Any], let frame = note.object as? GatewayFrame else { return }
let event = obj["event"] as? String else { return } switch frame {
switch event { case let .event(evt) where evt.event == "agent":
case "agent": if let data = evt.payload?.value,
if let payload = obj["payload"] as? [String: Any], JSONSerialization.isValidJSONObject(data),
let runId = payload["runId"] as? String, let blob = try? JSONSerialization.data(withJSONObject: data),
let seq = payload["seq"] as? Int, let agent = try? JSONDecoder().decode(AgentEvent.self, from: blob) {
let stream = payload["stream"] as? String,
let ts = payload["ts"] as? Double,
let dataDict = payload["data"] as? [String: Any]
{
let wrapped = dataDict.mapValues { AnyCodable($0) }
Task { @MainActor in Task { @MainActor in
AgentEventStore.shared.append(ControlAgentEvent( AgentEventStore.shared.append(ControlAgentEvent(
runId: runId, runId: agent.runid,
seq: seq, seq: agent.seq,
stream: stream, stream: agent.stream,
ts: ts, ts: Double(agent.ts),
data: wrapped)) data: agent.data.mapValues { Clawdis.AnyCodable($0.value) },
summary: nil))
} }
} }
case "presence": case let .event(evt) where evt.event == "shutdown":
// InstancesStore listens separately via notification
break
case "shutdown":
Task { @MainActor in self.state = .degraded("gateway shutdown") } Task { @MainActor in self.state = .degraded("gateway shutdown") }
default: default:
break break