diff --git a/apps/.i18n/native-source.json b/apps/.i18n/native-source.json index 4801fe443cc5..f8c17c39e820 100644 --- a/apps/.i18n/native-source.json +++ b/apps/.i18n/native-source.json @@ -25931,7 +25931,7 @@ }, { "kind": "conditional-branch", - "line": 8825, + "line": 8837, "path": "apps/ios/Sources/Model/NodeAppModel.swift", "source": "Approval", "surface": "apple", @@ -25939,7 +25939,7 @@ }, { "kind": "conditional-branch", - "line": 8825, + "line": 8837, "path": "apps/ios/Sources/Model/NodeAppModel.swift", "source": "This approval was already", "surface": "apple", @@ -25947,7 +25947,7 @@ }, { "kind": "conditional-branch", - "line": 8831, + "line": 8843, "path": "apps/ios/Sources/Model/NodeAppModel.swift", "source": "This approval was already set to Always Allow.", "surface": "apple", @@ -25955,7 +25955,7 @@ }, { "kind": "conditional-branch", - "line": 8832, + "line": 8844, "path": "apps/ios/Sources/Model/NodeAppModel.swift", "source": "Approval set to Always Allow.", "surface": "apple", @@ -25963,7 +25963,7 @@ }, { "kind": "conditional-branch", - "line": 10202, + "line": 10214, "path": "apps/ios/Sources/Model/NodeAppModel.swift", "source": "\\(urlText.prefix(500))…", "surface": "apple", diff --git a/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift b/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift index dad97033bfe1..f9c249b478c0 100644 --- a/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift +++ b/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift @@ -684,7 +684,7 @@ struct IOSGatewayChatTransport: OpenClawChatTransport { agentID: String? = nil, expectedSessionRoutingContract: String? = nil, message: String, - thinking: String, + thinking: String?, idempotencyKey: String, attachments: [OpenClawChatAttachmentPayload], ifCurrentRoute expectedRoute: GatewayNodeSessionRoute?, diff --git a/apps/ios/Sources/Model/NodeAppModel.swift b/apps/ios/Sources/Model/NodeAppModel.swift index a9925dbf58ed..2939e12ecc2b 100644 --- a/apps/ios/Sources/Model/NodeAppModel.swift +++ b/apps/ios/Sources/Model/NodeAppModel.swift @@ -6736,6 +6736,17 @@ extension NodeAppModel { event.messageKind ?? .chat } + nonisolated static func watchThinkingOverride(for messageKind: WatchMessageKind) -> String? { + // Free-form Watch chat has no one-turn thinking control, so it must not + // invent an override. Quick replies intentionally use a cheap level. + switch messageKind { + case .chat: + nil + case .quickReply: + "low" + } + } + private func forwardWatchMessage( _ event: WatchAppCommandEvent, requeueOnFailure: Bool) async -> WatchMessageSendOutcome @@ -6758,7 +6769,7 @@ extension NodeAppModel { if messageKind == .chat { self.focusChatSession(sessionKey) } - let thinking = messageKind == .quickReply ? "low" : "auto" + let thinkingOverride = Self.watchThinkingOverride(for: messageKind) do { let submittedAtMs = Int64(Date().timeIntervalSince1970 * 1000) @@ -6766,7 +6777,8 @@ extension NodeAppModel { let response = try await appleReviewDemoChatTransport.sendMessage( sessionKey: sessionKey, message: text, - thinking: thinking, + // Demo mode has no Gateway session from which to resolve an omitted level. + thinking: thinkingOverride ?? "auto", idempotencyKey: event.commandId, attachments: []) if messageKind == .quickReply { @@ -6810,7 +6822,7 @@ extension NodeAppModel { let response = try await transport.sendMessage( sessionKey: sessionKey, message: text, - thinking: thinking, + thinking: thinkingOverride, idempotencyKey: event.commandId, attachments: [], ifCurrentRoute: operatorRoute) diff --git a/apps/ios/Tests/NodeAppModelInvokeTests.swift b/apps/ios/Tests/NodeAppModelInvokeTests.swift index 0e9d8717c606..c3082fa10b54 100644 --- a/apps/ios/Tests/NodeAppModelInvokeTests.swift +++ b/apps/ios/Tests/NodeAppModelInvokeTests.swift @@ -6165,6 +6165,11 @@ private func overrideNotificationServingPreference(_ enabled: Bool) -> () -> Voi #expect(outbox.nextQueuedMessage(isAvailable: true, gatewayStableID: "gateway-a") == reply) } + @Test func `watch messages only override thinking for quick replies`() { + #expect(NodeAppModel.watchThinkingOverride(for: .chat) == nil) + #expect(NodeAppModel.watchThinkingOverride(for: .quickReply) == "low") + } + @Test func `watch message outbox discards permanent gateway failures`() { #expect(NodeAppModel._test_shouldDiscardFailedWatchMessage(code: "INVALID_REQUEST")) #expect(!NodeAppModel._test_shouldDiscardFailedWatchMessage( diff --git a/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatGatewayRequestTests.swift b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatGatewayRequestTests.swift index 7cba3353cbfe..f9cc7ff122da 100644 --- a/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatGatewayRequestTests.swift +++ b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatGatewayRequestTests.swift @@ -313,6 +313,18 @@ struct ChatGatewayRequestTests { #expect(String(decoding: encoded, as: UTF8.self).contains("a.png")) } + @Test func `send request omits inherited thinking override`() { + let inherited = OpenClawChatGatewayRequests.sendMessage( + sessionKey: "global", + agentID: nil, + expectedSessionRoutingContract: nil, + message: "inherit", + thinking: nil, + idempotencyKey: "send-inherit", + attachments: []) + #expect(inherited.params["thinking"] == nil) + } + @Test func `question resolve request preserves answer arrays`() throws { let request = OpenClawChatGatewayRequests.resolveQuestion( id: "ask_123",