iOS: use dedicated session key for chat sheet (#21139)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 31a27b0c5b
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
Mariano
2026-02-19 18:42:56 +00:00
committed by GitHub
parent 42d11a3ec5
commit a1d5dce7ab
4 changed files with 23 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Agents/Streaming: keep assistant partial streaming active during reasoning streams, handle native `thinking_*` stream events consistently, dedupe mixed reasoning-end signals, and clear stale mutating tool errors after same-target retry success. (#20635) Thanks @obviyus.
- iOS/Chat: use a dedicated iOS chat session key for ChatSheet routing to avoid cross-client session collisions with main-session traffic. (#21139) thanks @mbelinky.
- iOS/Chat: auto-resync chat history after reconnect sequence gaps, clear stale pending runs, and avoid dead-end manual refresh errors after transient disconnects. (#21135) thanks @mbelinky.
- iOS/Screen: move `WKWebView` lifecycle ownership into `ScreenWebView` coordinator and explicit attach/detach flow to reduce gesture/lifecycle crash risk (`__NSArrayM insertObject:atIndex:` paths) during screen tab updates. (#20366) Thanks @ngutman.
- iOS/Onboarding: prevent pairing-status flicker during auto-resume by keeping resumed state transitions stable. (#20310) Thanks @mbelinky.

View File

@@ -1603,6 +1603,14 @@ extension NodeAppModel {
return SessionKey.makeAgentSessionKey(agentId: agentId, baseKey: base)
}
var chatSessionKey: String {
let base = "ios"
let agentId = (self.selectedAgentId ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
let defaultId = (self.gatewayDefaultAgentId ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
if agentId.isEmpty || (!defaultId.isEmpty && agentId == defaultId) { return base }
return SessionKey.makeAgentSessionKey(agentId: agentId, baseKey: base)
}
var activeAgentName: String {
let agentId = (self.selectedAgentId ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
let defaultId = (self.gatewayDefaultAgentId ?? "").trimmingCharacters(in: .whitespacesAndNewlines)

View File

@@ -99,7 +99,7 @@ struct RootCanvas: View {
ChatSheet(
// Chat RPCs run on the operator session (read/write scopes).
gateway: self.appModel.operatorSession,
sessionKey: self.appModel.mainSessionKey,
sessionKey: self.appModel.chatSessionKey,
agentName: self.appModel.activeAgentName,
userAccent: self.appModel.seamColor)
case .quickSetup:

View File

@@ -77,6 +77,19 @@ private final class MockWatchMessagingService: WatchMessagingServicing, @uncheck
#expect(json.contains("\"value\""))
}
@Test @MainActor func chatSessionKeyDefaultsToIOSBase() {
let appModel = NodeAppModel()
#expect(appModel.chatSessionKey == "ios")
}
@Test @MainActor func chatSessionKeyUsesAgentScopedKeyForNonDefaultAgent() {
let appModel = NodeAppModel()
appModel.gatewayDefaultAgentId = "main"
appModel.setSelectedAgentId("agent-123")
#expect(appModel.chatSessionKey == SessionKey.makeAgentSessionKey(agentId: "agent-123", baseKey: "ios"))
#expect(appModel.mainSessionKey == "agent:agent-123:main")
}
@Test @MainActor func handleInvokeRejectsBackgroundCommands() async {
let appModel = NodeAppModel()
appModel.setScenePhase(.background)