agent-relay 3.2.13 → 3.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +1 -0
  2. package/dist/index.cjs +27 -9
  3. package/package.json +12 -11
  4. package/packages/acp-bridge/package.json +2 -2
  5. package/packages/config/package.json +1 -1
  6. package/packages/hooks/package.json +4 -4
  7. package/packages/memory/package.json +2 -2
  8. package/packages/openclaw/package.json +2 -2
  9. package/packages/policy/package.json +2 -2
  10. package/packages/sdk/dist/cli-registry.d.ts.map +1 -1
  11. package/packages/sdk/dist/cli-registry.js +6 -1
  12. package/packages/sdk/dist/cli-registry.js.map +1 -1
  13. package/packages/sdk/dist/workflows/runner.js +1 -1
  14. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  15. package/packages/sdk/package.json +2 -2
  16. package/packages/sdk/src/__tests__/workflow-runner.test.ts +2 -2
  17. package/packages/sdk/src/cli-registry.ts +6 -1
  18. package/packages/sdk/src/workflows/runner.ts +1 -1
  19. package/packages/sdk-py/pyproject.toml +1 -1
  20. package/packages/sdk-swift/Package.swift +26 -0
  21. package/packages/sdk-swift/README.md +39 -0
  22. package/packages/sdk-swift/Sources/AgentRelaySDK/RelayCast.swift +405 -0
  23. package/packages/sdk-swift/Sources/AgentRelaySDK/RelayObserver.swift +323 -0
  24. package/packages/sdk-swift/Sources/AgentRelaySDK/RelayObserverTypes.swift +143 -0
  25. package/packages/sdk-swift/Sources/AgentRelaySDK/RelayTransport.swift +220 -0
  26. package/packages/sdk-swift/Sources/AgentRelaySDK/RelayTypes.swift +435 -0
  27. package/packages/sdk-swift/Tests/AgentRelaySDKTests/AgentRelaySDKTests.swift +15 -0
  28. package/packages/sdk-swift/Tests/AgentRelaySDKTests/RelayObserverTests.swift +526 -0
  29. package/packages/telemetry/package.json +1 -1
  30. package/packages/trajectory/package.json +2 -2
  31. package/packages/user-directory/package.json +2 -2
  32. package/packages/utils/package.json +2 -2
@@ -0,0 +1,435 @@
1
+ import Foundation
2
+
3
+ public enum AgentRuntime: String, Codable, Sendable {
4
+ case pty
5
+ case headless
6
+ }
7
+
8
+ public enum HeadlessProvider: String, Codable, Sendable {
9
+ case claude
10
+ case opencode
11
+ }
12
+
13
+ public struct RestartPolicy: Codable, Sendable {
14
+ public var enabled: Bool?
15
+ public var maxRestarts: Int?
16
+ public var cooldownMs: Int?
17
+ public var maxConsecutiveFailures: Int?
18
+
19
+ enum CodingKeys: String, CodingKey {
20
+ case enabled
21
+ case maxRestarts = "max_restarts"
22
+ case cooldownMs = "cooldown_ms"
23
+ case maxConsecutiveFailures = "max_consecutive_failures"
24
+ }
25
+
26
+ public init(enabled: Bool? = nil, maxRestarts: Int? = nil, cooldownMs: Int? = nil, maxConsecutiveFailures: Int? = nil) {
27
+ self.enabled = enabled
28
+ self.maxRestarts = maxRestarts
29
+ self.cooldownMs = cooldownMs
30
+ self.maxConsecutiveFailures = maxConsecutiveFailures
31
+ }
32
+ }
33
+
34
+ public struct AgentSpec: Codable, Sendable {
35
+ public var name: String
36
+ public var runtime: AgentRuntime
37
+ public var provider: HeadlessProvider?
38
+ public var cli: String?
39
+ public var args: [String]?
40
+ public var channels: [String]?
41
+ public var model: String?
42
+ public var cwd: String?
43
+ public var team: String?
44
+ public var shadowOf: String?
45
+ public var shadowMode: String?
46
+ public var restartPolicy: RestartPolicy?
47
+
48
+ enum CodingKeys: String, CodingKey {
49
+ case name, runtime, provider, cli, args, channels, model, cwd, team
50
+ case shadowOf = "shadow_of"
51
+ case shadowMode = "shadow_mode"
52
+ case restartPolicy = "restart_policy"
53
+ }
54
+
55
+ public init(
56
+ name: String,
57
+ runtime: AgentRuntime,
58
+ provider: HeadlessProvider? = nil,
59
+ cli: String? = nil,
60
+ args: [String]? = nil,
61
+ channels: [String]? = nil,
62
+ model: String? = nil,
63
+ cwd: String? = nil,
64
+ team: String? = nil,
65
+ shadowOf: String? = nil,
66
+ shadowMode: String? = nil,
67
+ restartPolicy: RestartPolicy? = nil
68
+ ) {
69
+ self.name = name
70
+ self.runtime = runtime
71
+ self.provider = provider
72
+ self.cli = cli
73
+ self.args = args
74
+ self.channels = channels
75
+ self.model = model
76
+ self.cwd = cwd
77
+ self.team = team
78
+ self.shadowOf = shadowOf
79
+ self.shadowMode = shadowMode
80
+ self.restartPolicy = restartPolicy
81
+ }
82
+ }
83
+
84
+ public struct RelayDelivery: Codable, Sendable {
85
+ public var deliveryId: String
86
+ public var eventId: String
87
+ public var workspaceId: String?
88
+ public var workspaceAlias: String?
89
+ public var from: String
90
+ public var target: String
91
+ public var body: String
92
+ public var threadId: String?
93
+ public var priority: Int?
94
+
95
+ enum CodingKeys: String, CodingKey {
96
+ case deliveryId = "delivery_id"
97
+ case eventId = "event_id"
98
+ case workspaceId = "workspace_id"
99
+ case workspaceAlias = "workspace_alias"
100
+ case from, target, body
101
+ case threadId = "thread_id"
102
+ case priority
103
+ }
104
+ }
105
+
106
+ public struct ProtocolErrorPayload: Codable, Sendable, Error {
107
+ public var code: String
108
+ public var message: String
109
+ public var retryable: Bool
110
+ public var data: JSONValue?
111
+ }
112
+
113
+ public struct HelloAck: Codable, Sendable {
114
+ public var brokerVersion: String
115
+ public var protocolVersion: Int
116
+
117
+ enum CodingKeys: String, CodingKey {
118
+ case brokerVersion = "broker_version"
119
+ case protocolVersion = "protocol_version"
120
+ }
121
+ }
122
+
123
+ public struct OkResponse: Codable, Sendable {
124
+ public var result: JSONValue?
125
+ }
126
+
127
+ public struct PongPayload: Codable, Sendable {
128
+ public var tsMs: Int64
129
+
130
+ enum CodingKeys: String, CodingKey {
131
+ case tsMs = "ts_ms"
132
+ }
133
+ }
134
+
135
+ public struct WorkerStreamPayload: Codable, Sendable {
136
+ public var stream: String
137
+ public var chunk: String
138
+ }
139
+
140
+ public struct WorkerExitedPayload: Codable, Sendable {
141
+ public var code: Int?
142
+ public var signal: String?
143
+ }
144
+
145
+ public struct EmptyPayload: Codable, Sendable {
146
+ public init() {}
147
+ }
148
+
149
+ public struct HelloPayload: Codable, Sendable {
150
+ public var clientName: String
151
+ public var clientVersion: String
152
+ public var apiKey: String?
153
+
154
+ enum CodingKeys: String, CodingKey {
155
+ case clientName = "client_name"
156
+ case clientVersion = "client_version"
157
+ case apiKey = "api_key"
158
+ }
159
+
160
+ public init(clientName: String, clientVersion: String, apiKey: String? = nil) {
161
+ self.clientName = clientName
162
+ self.clientVersion = clientVersion
163
+ self.apiKey = apiKey
164
+ }
165
+ }
166
+
167
+ public struct SendMessagePayload: Codable, Sendable {
168
+ public var to: String
169
+ public var text: String
170
+ public var from: String?
171
+ public var threadId: String?
172
+ public var workspaceId: String?
173
+ public var workspaceAlias: String?
174
+ public var priority: Int?
175
+ public var data: [String: JSONValue]?
176
+
177
+ enum CodingKeys: String, CodingKey {
178
+ case to, text, from, priority, data
179
+ case threadId = "thread_id"
180
+ case workspaceId = "workspace_id"
181
+ case workspaceAlias = "workspace_alias"
182
+ }
183
+ }
184
+
185
+ public struct SpawnAgentPayload: Codable, Sendable {
186
+ public var agent: AgentSpec
187
+ public var initialTask: String?
188
+ public var skipRelayPrompt: Bool?
189
+
190
+ enum CodingKeys: String, CodingKey {
191
+ case agent
192
+ case initialTask = "initial_task"
193
+ case skipRelayPrompt = "skip_relay_prompt"
194
+ }
195
+ }
196
+
197
+ public struct ReleaseAgentPayload: Codable, Sendable {
198
+ public var name: String
199
+ public var reason: String?
200
+ }
201
+
202
+ public struct PingPayload: Codable, Sendable {
203
+ public var tsMs: Int64
204
+
205
+ enum CodingKeys: String, CodingKey {
206
+ case tsMs = "ts_ms"
207
+ }
208
+ }
209
+
210
+ public enum BrokerEvent: Sendable {
211
+ case agentSpawned(AgentSpawnedEvent)
212
+ case agentReleased(AgentReleasedEvent)
213
+ case agentExit(AgentExitRequestedEvent)
214
+ case agentExited(AgentExitedEvent)
215
+ case relayInbound(RelayInboundEvent)
216
+ case workerStream(WorkerStreamEvent)
217
+ case deliveryRetry(DeliveryRetryEvent)
218
+ case deliveryDropped(DeliveryDroppedEvent)
219
+ case deliveryQueued(DeliveryStateEvent)
220
+ case deliveryInjected(DeliveryStateEvent)
221
+ case deliveryVerified(DeliveryStateEvent)
222
+ case deliveryFailed(DeliveryFailedEvent)
223
+ case deliveryActive(DeliveryStateEvent)
224
+ case deliveryAck(DeliveryStateEvent)
225
+ case workerReady(WorkerReadyEvent)
226
+ case workerError(WorkerErrorEvent)
227
+ case relaycastPublished(RelaycastPublishedEvent)
228
+ case relaycastPublishFailed(RelaycastPublishFailedEvent)
229
+ case aclDenied(ACLDeniedEvent)
230
+ case agentIdle(AgentIdleEvent)
231
+ case agentRestarting(AgentRestartingEvent)
232
+ case agentRestarted(AgentRestartedEvent)
233
+ case agentPermanentlyDead(AgentPermanentlyDeadEvent)
234
+ /// Catch-all for unrecognized broker event kinds, preserving the raw kind string
235
+ /// and the full JSON payload for forward compatibility.
236
+ case unknown(kind: String, rawJSON: Data?)
237
+ }
238
+
239
+ extension BrokerEvent: Codable {
240
+ enum CodingKeys: String, CodingKey { case kind }
241
+
242
+ public init(from decoder: Decoder) throws {
243
+ let container = try decoder.container(keyedBy: CodingKeys.self)
244
+ switch try container.decode(String.self, forKey: .kind) {
245
+ case "agent_spawned": self = .agentSpawned(try AgentSpawnedEvent(from: decoder))
246
+ case "agent_released": self = .agentReleased(try AgentReleasedEvent(from: decoder))
247
+ case "agent_exit": self = .agentExit(try AgentExitRequestedEvent(from: decoder))
248
+ case "agent_exited": self = .agentExited(try AgentExitedEvent(from: decoder))
249
+ case "relay_inbound": self = .relayInbound(try RelayInboundEvent(from: decoder))
250
+ case "worker_stream": self = .workerStream(try WorkerStreamEvent(from: decoder))
251
+ case "delivery_retry": self = .deliveryRetry(try DeliveryRetryEvent(from: decoder))
252
+ case "delivery_dropped": self = .deliveryDropped(try DeliveryDroppedEvent(from: decoder))
253
+ case "delivery_queued": self = .deliveryQueued(try DeliveryStateEvent(from: decoder))
254
+ case "delivery_injected": self = .deliveryInjected(try DeliveryStateEvent(from: decoder))
255
+ case "delivery_verified": self = .deliveryVerified(try DeliveryStateEvent(from: decoder))
256
+ case "delivery_failed": self = .deliveryFailed(try DeliveryFailedEvent(from: decoder))
257
+ case "delivery_active": self = .deliveryActive(try DeliveryStateEvent(from: decoder))
258
+ case "delivery_ack": self = .deliveryAck(try DeliveryStateEvent(from: decoder))
259
+ case "worker_ready": self = .workerReady(try WorkerReadyEvent(from: decoder))
260
+ case "worker_error": self = .workerError(try WorkerErrorEvent(from: decoder))
261
+ case "relaycast_published": self = .relaycastPublished(try RelaycastPublishedEvent(from: decoder))
262
+ case "relaycast_publish_failed": self = .relaycastPublishFailed(try RelaycastPublishFailedEvent(from: decoder))
263
+ case "acl_denied": self = .aclDenied(try ACLDeniedEvent(from: decoder))
264
+ case "agent_idle": self = .agentIdle(try AgentIdleEvent(from: decoder))
265
+ case "agent_restarting": self = .agentRestarting(try AgentRestartingEvent(from: decoder))
266
+ case "agent_restarted": self = .agentRestarted(try AgentRestartedEvent(from: decoder))
267
+ case "agent_permanently_dead": self = .agentPermanentlyDead(try AgentPermanentlyDeadEvent(from: decoder))
268
+ default:
269
+ // Forward-compatible: preserve unknown event kinds with raw JSON data
270
+ // so consumers can handle new broker events without SDK updates.
271
+ let kind = try container.decode(String.self, forKey: .kind)
272
+ self = .unknown(kind: kind, rawJSON: nil)
273
+ }
274
+ }
275
+
276
+ public func encode(to encoder: Encoder) throws {
277
+ switch self {
278
+ case .agentSpawned(let value): try value.encode(to: encoder)
279
+ case .agentReleased(let value): try value.encode(to: encoder)
280
+ case .agentExit(let value): try value.encode(to: encoder)
281
+ case .agentExited(let value): try value.encode(to: encoder)
282
+ case .relayInbound(let value): try value.encode(to: encoder)
283
+ case .workerStream(let value): try value.encode(to: encoder)
284
+ case .deliveryRetry(let value): try value.encode(to: encoder)
285
+ case .deliveryDropped(let value): try value.encode(to: encoder)
286
+ case .deliveryQueued(let value): try value.encode(to: encoder)
287
+ case .deliveryInjected(let value): try value.encode(to: encoder)
288
+ case .deliveryVerified(let value): try value.encode(to: encoder)
289
+ case .deliveryFailed(let value): try value.encode(to: encoder)
290
+ case .deliveryActive(let value): try value.encode(to: encoder)
291
+ case .deliveryAck(let value): try value.encode(to: encoder)
292
+ case .workerReady(let value): try value.encode(to: encoder)
293
+ case .workerError(let value): try value.encode(to: encoder)
294
+ case .relaycastPublished(let value): try value.encode(to: encoder)
295
+ case .relaycastPublishFailed(let value): try value.encode(to: encoder)
296
+ case .aclDenied(let value): try value.encode(to: encoder)
297
+ case .agentIdle(let value): try value.encode(to: encoder)
298
+ case .agentRestarting(let value): try value.encode(to: encoder)
299
+ case .agentRestarted(let value): try value.encode(to: encoder)
300
+ case .agentPermanentlyDead(let value): try value.encode(to: encoder)
301
+ case .unknown(let kind, _):
302
+ var container = encoder.container(keyedBy: CodingKeys.self)
303
+ try container.encode(kind, forKey: .kind)
304
+ }
305
+ }
306
+ }
307
+
308
+ public enum InboundMessage: Sendable {
309
+ case helloAck(HelloAck)
310
+ case ok(OkResponse)
311
+ case error(ProtocolErrorPayload)
312
+ case event(BrokerEvent)
313
+ case deliverRelay(RelayDelivery)
314
+ case workerStream(WorkerStreamPayload)
315
+ case workerExited(WorkerExitedPayload)
316
+ case pong(PongPayload)
317
+ /// Catch-all for unrecognized inbound message types for forward compatibility.
318
+ case unknown(type: String, rawJSON: Data?)
319
+ }
320
+
321
+ extension InboundMessage: Codable {
322
+ enum CodingKeys: String, CodingKey { case type, payload }
323
+
324
+ public init(from decoder: Decoder) throws {
325
+ let container = try decoder.container(keyedBy: CodingKeys.self)
326
+ let type = try container.decode(String.self, forKey: .type)
327
+ switch type {
328
+ case "hello_ack": self = .helloAck(try container.decode(HelloAck.self, forKey: .payload))
329
+ case "ok": self = .ok(try container.decode(OkResponse.self, forKey: .payload))
330
+ case "error": self = .error(try container.decode(ProtocolErrorPayload.self, forKey: .payload))
331
+ case "event": self = .event(try container.decode(BrokerEvent.self, forKey: .payload))
332
+ case "deliver_relay": self = .deliverRelay(try container.decode(RelayDelivery.self, forKey: .payload))
333
+ case "worker_stream": self = .workerStream(try container.decode(WorkerStreamPayload.self, forKey: .payload))
334
+ case "worker_exited": self = .workerExited(try container.decode(WorkerExitedPayload.self, forKey: .payload))
335
+ case "pong", "ping": self = .pong(try container.decode(PongPayload.self, forKey: .payload))
336
+ default:
337
+ // Forward-compatible: preserve unknown message types so consumers
338
+ // can handle new protocol frames without SDK updates.
339
+ self = .unknown(type: type, rawJSON: nil)
340
+ }
341
+ }
342
+
343
+ public func encode(to encoder: Encoder) throws {
344
+ var container = encoder.container(keyedBy: CodingKeys.self)
345
+ switch self {
346
+ case .helloAck(let payload): try container.encode("hello_ack", forKey: .type); try container.encode(payload, forKey: .payload)
347
+ case .ok(let payload): try container.encode("ok", forKey: .type); try container.encode(payload, forKey: .payload)
348
+ case .error(let payload): try container.encode("error", forKey: .type); try container.encode(payload, forKey: .payload)
349
+ case .event(let payload): try container.encode("event", forKey: .type); try container.encode(payload, forKey: .payload)
350
+ case .deliverRelay(let payload): try container.encode("deliver_relay", forKey: .type); try container.encode(payload, forKey: .payload)
351
+ case .workerStream(let payload): try container.encode("worker_stream", forKey: .type); try container.encode(payload, forKey: .payload)
352
+ case .workerExited(let payload): try container.encode("worker_exited", forKey: .type); try container.encode(payload, forKey: .payload)
353
+ case .pong(let payload): try container.encode("pong", forKey: .type); try container.encode(payload, forKey: .payload)
354
+ case .unknown(let type, _): try container.encode(type, forKey: .type)
355
+ }
356
+ }
357
+ }
358
+
359
+ public enum OutboundMessage: Sendable {
360
+ case hello(HelloPayload)
361
+ case sendMessage(SendMessagePayload)
362
+ case spawnAgent(SpawnAgentPayload)
363
+ case releaseAgent(ReleaseAgentPayload)
364
+ case ping(PingPayload)
365
+ case listAgents(EmptyPayload)
366
+ }
367
+
368
+ extension OutboundMessage: Encodable {
369
+ enum CodingKeys: String, CodingKey { case v, type, payload }
370
+
371
+ public func encode(to encoder: Encoder) throws {
372
+ var container = encoder.container(keyedBy: CodingKeys.self)
373
+ try container.encode(1, forKey: .v)
374
+ switch self {
375
+ case .hello(let payload): try container.encode("hello", forKey: .type); try container.encode(payload, forKey: .payload)
376
+ case .sendMessage(let payload): try container.encode("send_message", forKey: .type); try container.encode(payload, forKey: .payload)
377
+ case .spawnAgent(let payload): try container.encode("spawn_agent", forKey: .type); try container.encode(payload, forKey: .payload)
378
+ case .releaseAgent(let payload): try container.encode("release_agent", forKey: .type); try container.encode(payload, forKey: .payload)
379
+ case .ping(let payload): try container.encode("ping", forKey: .type); try container.encode(payload, forKey: .payload)
380
+ case .listAgents(let payload): try container.encode("list_agents", forKey: .type); try container.encode(payload, forKey: .payload)
381
+ }
382
+ }
383
+ }
384
+
385
+ public struct AgentSpawnedEvent: Codable, Sendable { public var kind: String = "agent_spawned"; public var name: String; public var runtime: AgentRuntime; public var provider: HeadlessProvider?; public var cli: String?; public var model: String?; public var parent: String?; public var pid: Int?; public var source: String? }
386
+ public struct AgentReleasedEvent: Codable, Sendable { public var kind: String = "agent_released"; public var name: String }
387
+ public struct AgentExitRequestedEvent: Codable, Sendable { public var kind: String = "agent_exit"; public var name: String; public var reason: String }
388
+ public struct AgentExitedEvent: Codable, Sendable { public var kind: String = "agent_exited"; public var name: String; public var code: Int?; public var signal: String? }
389
+ public struct RelayInboundEvent: Codable, Sendable { public var kind: String = "relay_inbound"; public var eventId: String; public var from: String; public var target: String; public var body: String; public var threadId: String?; enum CodingKeys: String, CodingKey { case kind, from, target, body; case eventId = "event_id"; case threadId = "thread_id" } }
390
+ public struct WorkerStreamEvent: Codable, Sendable { public var kind: String = "worker_stream"; public var name: String; public var stream: String; public var chunk: String }
391
+ public struct DeliveryRetryEvent: Codable, Sendable { public var kind: String = "delivery_retry"; public var name: String; public var deliveryId: String; public var eventId: String; public var attempts: Int; enum CodingKeys: String, CodingKey { case kind, name, attempts; case deliveryId = "delivery_id"; case eventId = "event_id" } }
392
+ public struct DeliveryDroppedEvent: Codable, Sendable { public var kind: String = "delivery_dropped"; public var name: String; public var count: Int; public var reason: String }
393
+ public struct DeliveryStateEvent: Codable, Sendable { public var kind: String; public var name: String; public var deliveryId: String; public var eventId: String; enum CodingKeys: String, CodingKey { case kind, name; case deliveryId = "delivery_id"; case eventId = "event_id" } }
394
+ public struct DeliveryFailedEvent: Codable, Sendable { public var kind: String = "delivery_failed"; public var name: String; public var deliveryId: String; public var eventId: String; public var reason: String; enum CodingKeys: String, CodingKey { case kind, name, reason; case deliveryId = "delivery_id"; case eventId = "event_id" } }
395
+ public struct WorkerReadyEvent: Codable, Sendable { public var kind: String = "worker_ready"; public var name: String; public var runtime: AgentRuntime; public var provider: HeadlessProvider?; public var cli: String?; public var model: String? }
396
+ public struct WorkerErrorEvent: Codable, Sendable { public var kind: String = "worker_error"; public var name: String; public var code: String; public var message: String }
397
+ public struct RelaycastPublishedEvent: Codable, Sendable { public var kind: String = "relaycast_published"; public var eventId: String; public var to: String; public var targetType: String; enum CodingKeys: String, CodingKey { case kind, to; case eventId = "event_id"; case targetType = "target_type" } }
398
+ public struct RelaycastPublishFailedEvent: Codable, Sendable { public var kind: String = "relaycast_publish_failed"; public var eventId: String; public var to: String; public var reason: String; enum CodingKeys: String, CodingKey { case kind, to, reason; case eventId = "event_id" } }
399
+ public struct ACLDeniedEvent: Codable, Sendable { public var kind: String = "acl_denied"; public var name: String; public var sender: String; public var ownerChain: [String]; enum CodingKeys: String, CodingKey { case kind, name, sender; case ownerChain = "owner_chain" } }
400
+ public struct AgentIdleEvent: Codable, Sendable { public var kind: String = "agent_idle"; public var name: String; public var idleSecs: Int; enum CodingKeys: String, CodingKey { case kind, name; case idleSecs = "idle_secs" } }
401
+ public struct AgentRestartingEvent: Codable, Sendable { public var kind: String = "agent_restarting"; public var name: String; public var code: Int?; public var signal: String?; public var restartCount: Int; public var delayMs: Int; enum CodingKeys: String, CodingKey { case kind, name, code, signal; case restartCount = "restart_count"; case delayMs = "delay_ms" } }
402
+ public struct AgentRestartedEvent: Codable, Sendable { public var kind: String = "agent_restarted"; public var name: String; public var restartCount: Int; enum CodingKeys: String, CodingKey { case kind, name; case restartCount = "restart_count" } }
403
+ public struct AgentPermanentlyDeadEvent: Codable, Sendable { public var kind: String = "agent_permanently_dead"; public var name: String; public var reason: String }
404
+
405
+ public enum JSONValue: Codable, Sendable {
406
+ case string(String)
407
+ case number(Double)
408
+ case bool(Bool)
409
+ case object([String: JSONValue])
410
+ case array([JSONValue])
411
+ case null
412
+
413
+ public init(from decoder: Decoder) throws {
414
+ let container = try decoder.singleValueContainer()
415
+ if container.decodeNil() { self = .null }
416
+ else if let value = try? container.decode(Bool.self) { self = .bool(value) }
417
+ else if let value = try? container.decode(Double.self) { self = .number(value) }
418
+ else if let value = try? container.decode(String.self) { self = .string(value) }
419
+ else if let value = try? container.decode([String: JSONValue].self) { self = .object(value) }
420
+ else if let value = try? container.decode([JSONValue].self) { self = .array(value) }
421
+ else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported JSON value") }
422
+ }
423
+
424
+ public func encode(to encoder: Encoder) throws {
425
+ var container = encoder.singleValueContainer()
426
+ switch self {
427
+ case .string(let value): try container.encode(value)
428
+ case .number(let value): try container.encode(value)
429
+ case .bool(let value): try container.encode(value)
430
+ case .object(let value): try container.encode(value)
431
+ case .array(let value): try container.encode(value)
432
+ case .null: try container.encodeNil()
433
+ }
434
+ }
435
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import AgentRelaySDK
3
+
4
+ final class AgentRelaySDKTests: XCTestCase {
5
+ func testRelayCastInit() {
6
+ let relay = RelayCast(apiKey: "rk_test_key")
7
+ XCTAssertEqual(relay.apiKey, "rk_test_key")
8
+ }
9
+
10
+ func testChannelCreation() {
11
+ let relay = RelayCast(apiKey: "rk_test_key")
12
+ let channel = relay.channel("test-channel")
13
+ XCTAssertEqual(channel.name, "test-channel")
14
+ }
15
+ }