fix: handle iOS global agent transcripts

This commit is contained in:
Peter Steinberger
2026-05-31 17:54:40 +01:00
parent 0f6be951e0
commit 772d13c19d
5 changed files with 107 additions and 2 deletions

View File

@@ -95,6 +95,7 @@ import Testing
@Test func mapsSessionMessageEventToSessionMessage() {
let payload = AnyCodable([
"sessionKey": AnyCodable("agent:main:main"),
"agentId": AnyCodable("main"),
"messageId": AnyCodable("msg-1"),
"messageSeq": AnyCodable(7),
"message": AnyCodable([
@@ -119,6 +120,7 @@ import Testing
switch mapped {
case let .sessionMessage(message):
#expect(message.sessionKey == "agent:main:main")
#expect(message.agentId == "main")
#expect(message.messageId == "msg-1")
#expect(message.messageSeq == 7)
#expect(message.message?.role == "assistant")

View File

@@ -326,17 +326,20 @@ public struct OpenClawChatEventPayload: Codable, Sendable {
public struct OpenClawSessionMessageEventPayload: Codable, Sendable {
public let sessionKey: String?
public let agentId: String?
public let message: OpenClawChatMessage?
public let messageId: String?
public let messageSeq: Int?
public init(
sessionKey: String?,
agentId: String? = nil,
message: OpenClawChatMessage?,
messageId: String?,
messageSeq: Int?)
{
self.sessionKey = sessionKey
self.agentId = agentId
self.message = message
self.messageId = messageId
self.messageSeq = messageSeq

View File

@@ -8,7 +8,21 @@ extension OpenClawChatViewModel {
mainSessionKey: self.resolvedMainSessionKey)
}
static func matchesCurrentSessionKey(incoming: String, current: String, mainSessionKey: String) -> Bool {
func matchesCurrentSessionKey(incoming: String, agentId: String?, current: String) -> Bool {
Self.matchesCurrentSessionKey(
incoming: incoming,
agentId: agentId,
current: current,
mainSessionKey: self.resolvedMainSessionKey)
}
static func matchesCurrentSessionKey(
incoming: String,
agentId: String? = nil,
current: String,
mainSessionKey: String)
-> Bool
{
let incomingNormalized = incoming.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
let currentNormalized = current.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if incomingNormalized == currentNormalized {
@@ -23,6 +37,13 @@ extension OpenClawChatViewModel {
{
return true
}
if Self.matchesSelectedAgentGlobal(
incoming: incomingNormalized,
agentId: agentId,
current: currentNormalized)
{
return true
}
return false
}
@@ -36,4 +57,14 @@ extension OpenClawChatViewModel {
return (current == "main" && incoming == "agent:main:main") ||
(incoming == "main" && current == "agent:main:main")
}
private static func matchesSelectedAgentGlobal(incoming: String, agentId: String?, current: String) -> Bool {
guard incoming == "global",
let selectedAgentId = agentId?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased(),
!selectedAgentId.isEmpty
else {
return false
}
return current == "agent:\(selectedAgentId):global"
}
}

View File

@@ -1302,7 +1302,7 @@ public final class OpenClawChatViewModel {
private func handleSessionMessageEvent(_ payload: OpenClawSessionMessageEventPayload) {
if let sessionKey = payload.sessionKey,
!self.matchesCurrentSessionKey(incoming: sessionKey, current: self.sessionKey)
!self.matchesCurrentSessionKey(incoming: sessionKey, agentId: payload.agentId, current: self.sessionKey)
{
return
}

View File

@@ -1092,6 +1092,75 @@ extension TestChatTransportState {
}
}
@Test func appendsGlobalSessionUserMessageForSelectedAgent() async throws {
let now = Date().timeIntervalSince1970 * 1000
let (transport, vm) = await makeViewModel(
sessionKey: "agent:work:global",
historyResponses: [historyPayload(sessionKey: "agent:work:global")])
await MainActor.run { vm.load() }
try await waitUntil("bootstrap history loaded") { await MainActor.run { vm.messages.isEmpty } }
transport.emit(
.sessionMessage(
OpenClawSessionMessageEventPayload(
sessionKey: "global",
agentId: "work",
message: OpenClawChatMessage(
role: "user",
content: [
OpenClawChatMessageContent(
type: "text",
text: "global transcript",
mimeType: nil,
fileName: nil,
content: nil),
],
timestamp: now),
messageId: "msg-global-work",
messageSeq: 1)))
try await waitUntil("selected agent global transcript visible") {
await MainActor.run {
vm.messages.count == 1 &&
vm.messages.first?.role == "user" &&
vm.messages.first?.content.first?.text == "global transcript"
}
}
}
@Test func ignoresGlobalSessionUserMessageForDifferentAgent() async throws {
let now = Date().timeIntervalSince1970 * 1000
let (transport, vm) = await makeViewModel(
sessionKey: "agent:work:global",
historyResponses: [historyPayload(sessionKey: "agent:work:global")])
await MainActor.run { vm.load() }
try await waitUntil("bootstrap history loaded") { await MainActor.run { vm.messages.isEmpty } }
transport.emit(
.sessionMessage(
OpenClawSessionMessageEventPayload(
sessionKey: "global",
agentId: "main",
message: OpenClawChatMessage(
role: "user",
content: [
OpenClawChatMessageContent(
type: "text",
text: "wrong global transcript",
mimeType: nil,
fileName: nil,
content: nil),
],
timestamp: now),
messageId: "msg-global-main",
messageSeq: 1)))
try await Task.sleep(nanoseconds: 100_000_000)
#expect(await MainActor.run { vm.messages.isEmpty })
}
@Test func ignoresAgentMainSessionMessageForDifferentCurrentMainAlias() async throws {
let now = Date().timeIntervalSince1970 * 1000
let (transport, vm) = await makeViewModel(historyResponses: [historyPayload()])