Skip to content

Commit dbfa316

Browse files
committed
feat: multi-agent routing + multi-account providers
1 parent 50d4b17 commit dbfa316

129 files changed

Lines changed: 3760 additions & 1126 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/macos/Sources/Clawdbot/Bridge/BridgeServer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ actor BridgeServer {
187187
thinking: "low",
188188
deliver: false,
189189
to: nil,
190-
channel: .last))
190+
provider: .last))
191191

192192
case "agent.request":
193193
guard let json = evt.payloadJSON, let data = json.data(using: .utf8) else {
@@ -205,15 +205,15 @@ actor BridgeServer {
205205
?? "node-\(nodeId)"
206206
let thinking = link.thinking?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
207207
let to = link.to?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
208-
let channel = GatewayAgentChannel(raw: link.channel)
208+
let provider = GatewayAgentProvider(raw: link.channel)
209209

210210
_ = await GatewayConnection.shared.sendAgent(GatewayAgentInvocation(
211211
message: message,
212212
sessionKey: sessionKey,
213213
thinking: thinking,
214214
deliver: link.deliver,
215215
to: to,
216-
channel: channel))
216+
provider: provider))
217217

218218
default:
219219
break

apps/macos/Sources/Clawdbot/CanvasA2UIActionMessageHandler.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,14 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
7979
GatewayProcessManager.shared.setActive(true)
8080
}
8181

82-
let result = await GatewayConnection.shared.sendAgent(GatewayAgentInvocation(
83-
message: text,
84-
sessionKey: self.sessionKey,
85-
thinking: "low",
86-
deliver: false,
87-
to: nil,
88-
channel: .last,
89-
idempotencyKey: actionId))
82+
let result = await GatewayConnection.shared.sendAgent(GatewayAgentInvocation(
83+
message: text,
84+
sessionKey: self.sessionKey,
85+
thinking: "low",
86+
deliver: false,
87+
to: nil,
88+
provider: .last,
89+
idempotencyKey: actionId))
9090

9191
await MainActor.run {
9292
guard let webView else { return }

apps/macos/Sources/Clawdbot/CronJobEditor+Helpers.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ extension CronJobEditor {
3333
case let .systemEvent(text):
3434
self.payloadKind = .systemEvent
3535
self.systemEventText = text
36-
case let .agentTurn(message, thinking, timeoutSeconds, deliver, channel, to, bestEffortDeliver):
36+
case let .agentTurn(message, thinking, timeoutSeconds, deliver, provider, to, bestEffortDeliver):
3737
self.payloadKind = .agentTurn
3838
self.agentMessage = message
3939
self.thinking = thinking ?? ""
4040
self.timeoutSeconds = timeoutSeconds.map(String.init) ?? ""
4141
self.deliver = deliver ?? false
42-
self.channel = GatewayAgentChannel(raw: channel)
42+
self.provider = GatewayAgentProvider(raw: provider)
4343
self.to = to ?? ""
4444
self.bestEffortDeliver = bestEffortDeliver ?? false
4545
}
@@ -166,7 +166,7 @@ extension CronJobEditor {
166166
if let n = Int(self.timeoutSeconds), n > 0 { payload["timeoutSeconds"] = n }
167167
payload["deliver"] = self.deliver
168168
if self.deliver {
169-
payload["channel"] = self.channel.rawValue
169+
payload["provider"] = self.provider.rawValue
170170
let to = self.to.trimmingCharacters(in: .whitespacesAndNewlines)
171171
if !to.isEmpty { payload["to"] = to }
172172
payload["bestEffortDeliver"] = self.bestEffortDeliver

apps/macos/Sources/Clawdbot/CronJobEditor+Testing.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension CronJobEditor {
1313
self.payloadKind = .agentTurn
1414
self.agentMessage = "Run diagnostic"
1515
self.deliver = true
16-
self.channel = .last
16+
self.provider = .last
1717
self.to = "+15551230000"
1818
self.thinking = "low"
1919
self.timeoutSeconds = "90"

apps/macos/Sources/Clawdbot/CronJobEditor.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct CronJobEditor: View {
1717
static let scheduleKindNote =
1818
"“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression."
1919
static let isolatedPayloadNote =
20-
"Isolated jobs always run an agent turn. The result can be delivered to a surface, "
20+
"Isolated jobs always run an agent turn. The result can be delivered to a provider, "
2121
+ "and a short summary is posted back to your main chat."
2222
static let mainPayloadNote =
2323
"System events are injected into the current main session. Agent turns require an isolated session target."
@@ -42,7 +42,7 @@ struct CronJobEditor: View {
4242
@State var systemEventText: String = ""
4343
@State var agentMessage: String = ""
4444
@State var deliver: Bool = false
45-
@State var channel: GatewayAgentChannel = .last
45+
@State var provider: GatewayAgentProvider = .last
4646
@State var to: String = ""
4747
@State var thinking: String = ""
4848
@State var timeoutSeconds: String = ""
@@ -309,23 +309,23 @@ struct CronJobEditor: View {
309309
}
310310
GridRow {
311311
self.gridLabel("Deliver")
312-
Toggle("Deliver result to a surface", isOn: self.$deliver)
312+
Toggle("Deliver result to a provider", isOn: self.$deliver)
313313
.toggleStyle(.switch)
314314
}
315315
}
316316

317317
if self.deliver {
318318
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
319319
GridRow {
320-
self.gridLabel("Channel")
321-
Picker("", selection: self.$channel) {
322-
Text("last").tag(GatewayAgentChannel.last)
323-
Text("whatsapp").tag(GatewayAgentChannel.whatsapp)
324-
Text("telegram").tag(GatewayAgentChannel.telegram)
325-
Text("discord").tag(GatewayAgentChannel.discord)
326-
Text("slack").tag(GatewayAgentChannel.slack)
327-
Text("signal").tag(GatewayAgentChannel.signal)
328-
Text("imessage").tag(GatewayAgentChannel.imessage)
320+
self.gridLabel("Provider")
321+
Picker("", selection: self.$provider) {
322+
Text("last").tag(GatewayAgentProvider.last)
323+
Text("whatsapp").tag(GatewayAgentProvider.whatsapp)
324+
Text("telegram").tag(GatewayAgentProvider.telegram)
325+
Text("discord").tag(GatewayAgentProvider.discord)
326+
Text("slack").tag(GatewayAgentProvider.slack)
327+
Text("signal").tag(GatewayAgentProvider.signal)
328+
Text("imessage").tag(GatewayAgentProvider.imessage)
329329
}
330330
.labelsHidden()
331331
.pickerStyle(.segmented)

apps/macos/Sources/Clawdbot/CronModels.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ enum CronPayload: Codable, Equatable {
7474
thinking: String?,
7575
timeoutSeconds: Int?,
7676
deliver: Bool?,
77-
channel: String?,
77+
provider: String?,
7878
to: String?,
7979
bestEffortDeliver: Bool?)
8080

8181
enum CodingKeys: String, CodingKey {
82-
case kind, text, message, thinking, timeoutSeconds, deliver, channel, to, bestEffortDeliver
82+
case kind, text, message, thinking, timeoutSeconds, deliver, provider, to, bestEffortDeliver
8383
}
8484

8585
var kind: String {
@@ -101,7 +101,7 @@ enum CronPayload: Codable, Equatable {
101101
thinking: container.decodeIfPresent(String.self, forKey: .thinking),
102102
timeoutSeconds: container.decodeIfPresent(Int.self, forKey: .timeoutSeconds),
103103
deliver: container.decodeIfPresent(Bool.self, forKey: .deliver),
104-
channel: container.decodeIfPresent(String.self, forKey: .channel),
104+
provider: container.decodeIfPresent(String.self, forKey: .provider),
105105
to: container.decodeIfPresent(String.self, forKey: .to),
106106
bestEffortDeliver: container.decodeIfPresent(Bool.self, forKey: .bestEffortDeliver))
107107
default:
@@ -118,12 +118,12 @@ enum CronPayload: Codable, Equatable {
118118
switch self {
119119
case let .systemEvent(text):
120120
try container.encode(text, forKey: .text)
121-
case let .agentTurn(message, thinking, timeoutSeconds, deliver, channel, to, bestEffortDeliver):
121+
case let .agentTurn(message, thinking, timeoutSeconds, deliver, provider, to, bestEffortDeliver):
122122
try container.encode(message, forKey: .message)
123123
try container.encodeIfPresent(thinking, forKey: .thinking)
124124
try container.encodeIfPresent(timeoutSeconds, forKey: .timeoutSeconds)
125125
try container.encodeIfPresent(deliver, forKey: .deliver)
126-
try container.encodeIfPresent(channel, forKey: .channel)
126+
try container.encodeIfPresent(provider, forKey: .provider)
127127
try container.encodeIfPresent(to, forKey: .to)
128128
try container.encodeIfPresent(bestEffortDeliver, forKey: .bestEffortDeliver)
129129
}

apps/macos/Sources/Clawdbot/CronSettings+Rows.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ extension CronSettings {
206206
Text(text)
207207
.font(.callout)
208208
.textSelection(.enabled)
209-
case let .agentTurn(message, thinking, timeoutSeconds, deliver, channel, to, _):
209+
case let .agentTurn(message, thinking, timeoutSeconds, deliver, provider, to, _):
210210
VStack(alignment: .leading, spacing: 4) {
211211
Text(message)
212212
.font(.callout)
@@ -216,7 +216,7 @@ extension CronSettings {
216216
if let timeoutSeconds { StatusPill(text: "\(timeoutSeconds)s", tint: .secondary) }
217217
if deliver ?? false {
218218
StatusPill(text: "deliver", tint: .secondary)
219-
if let channel, !channel.isEmpty { StatusPill(text: channel, tint: .secondary) }
219+
if let provider, !provider.isEmpty { StatusPill(text: provider, tint: .secondary) }
220220
if let to, !to.isEmpty { StatusPill(text: to, tint: .secondary) }
221221
}
222222
}

apps/macos/Sources/Clawdbot/CronSettings+Testing.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct CronSettings_Previews: PreviewProvider {
2020
thinking: "low",
2121
timeoutSeconds: 600,
2222
deliver: true,
23-
channel: "last",
23+
provider: "last",
2424
to: nil,
2525
bestEffortDeliver: true),
2626
isolation: CronIsolation(postToMainPrefix: "Cron"),
@@ -72,7 +72,7 @@ extension CronSettings {
7272
thinking: "low",
7373
timeoutSeconds: 120,
7474
deliver: true,
75-
channel: "whatsapp",
75+
provider: "whatsapp",
7676
to: "+15551234567",
7777
bestEffortDeliver: true),
7878
isolation: CronIsolation(postToMainPrefix: "[cron] "),

apps/macos/Sources/Clawdbot/DeepLinks.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ final class DeepLinkHandler {
5959
}
6060

6161
do {
62-
let channel = GatewayAgentChannel(raw: link.channel)
62+
let provider = GatewayAgentProvider(raw: link.channel)
6363
let explicitSessionKey = link.sessionKey?
6464
.trimmingCharacters(in: .whitespacesAndNewlines)
6565
.nonEmpty
@@ -72,9 +72,9 @@ final class DeepLinkHandler {
7272
message: messagePreview,
7373
sessionKey: resolvedSessionKey,
7474
thinking: link.thinking?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty,
75-
deliver: channel.shouldDeliver(link.deliver),
75+
deliver: provider.shouldDeliver(link.deliver),
7676
to: link.to?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty,
77-
channel: channel,
77+
provider: provider,
7878
timeoutSeconds: link.timeoutSeconds,
7979
idempotencyKey: UUID().uuidString)
8080

apps/macos/Sources/Clawdbot/GatewayConnection.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import OSLog
55

66
private let gatewayConnectionLogger = Logger(subsystem: "com.clawdbot", category: "gateway.connection")
77

8-
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
8+
enum GatewayAgentProvider: String, Codable, CaseIterable, Sendable {
99
case last
1010
case whatsapp
1111
case telegram
@@ -17,7 +17,7 @@ enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
1717

1818
init(raw: String?) {
1919
let normalized = (raw ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
20-
self = GatewayAgentChannel(rawValue: normalized) ?? .last
20+
self = GatewayAgentProvider(rawValue: normalized) ?? .last
2121
}
2222

2323
var isDeliverable: Bool { self != .webchat }
@@ -31,7 +31,7 @@ struct GatewayAgentInvocation: Sendable {
3131
var thinking: String?
3232
var deliver: Bool = false
3333
var to: String?
34-
var channel: GatewayAgentChannel = .last
34+
var provider: GatewayAgentProvider = .last
3535
var timeoutSeconds: Int?
3636
var idempotencyKey: String = UUID().uuidString
3737
}
@@ -368,7 +368,7 @@ extension GatewayConnection {
368368
"thinking": AnyCodable(invocation.thinking ?? "default"),
369369
"deliver": AnyCodable(invocation.deliver),
370370
"to": AnyCodable(invocation.to ?? ""),
371-
"channel": AnyCodable(invocation.channel.rawValue),
371+
"provider": AnyCodable(invocation.provider.rawValue),
372372
"idempotencyKey": AnyCodable(invocation.idempotencyKey),
373373
]
374374
if let timeout = invocation.timeoutSeconds {
@@ -389,7 +389,7 @@ extension GatewayConnection {
389389
sessionKey: String,
390390
deliver: Bool,
391391
to: String?,
392-
channel: GatewayAgentChannel = .last,
392+
provider: GatewayAgentProvider = .last,
393393
timeoutSeconds: Int? = nil,
394394
idempotencyKey: String = UUID().uuidString) async -> (ok: Bool, error: String?)
395395
{
@@ -399,7 +399,7 @@ extension GatewayConnection {
399399
thinking: thinking,
400400
deliver: deliver,
401401
to: to,
402-
channel: channel,
402+
provider: provider,
403403
timeoutSeconds: timeoutSeconds,
404404
idempotencyKey: idempotencyKey))
405405
}

0 commit comments

Comments
 (0)