iOS: stabilize pairing/reconnect loops (#20056)

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

Prepared head SHA: b01a482a17
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-18 13:23:06 +00:00
committed by GitHub
parent ff50d3303d
commit fc65f70a9b
3 changed files with 12 additions and 1 deletions

View File

@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- iOS/Onboarding: stabilize pairing and reconnect behavior by resetting stale pairing request state on manual retry, disconnecting both operator and node gateways on operator failure, and avoiding duplicate pairing loops from operator transport identity attachment. (#20056) Thanks @mbelinky.
- Browser/Relay: reuse an already-running extension relay when the relay port is occupied by another OpenClaw process, while still failing on non-relay port collisions to avoid masking unrelated listeners. (#20035) Thanks @mbelinky.
- Telegram/Cron/Heartbeat: honor explicit Telegram topic targets in cron and heartbeat delivery (`<chatId>:topic:<threadId>`) so scheduled sends land in the configured topic instead of the last active thread. (#19367) Thanks @Lukavyi.
- iOS/Signing: restore local auto-selected signing-team overrides during iOS project generation by wiring `.local-signing.xcconfig` into the active signing config and emitting `OPENCLAW_DEVELOPMENT_TEAM` in local signing setup. (#19993) Thanks @ngutman.

View File

@@ -584,8 +584,11 @@ final class NodeAppModel {
onFailure: { [weak self] _ in
guard let self else { return }
await self.operatorGateway.disconnect()
await self.nodeGateway.disconnect()
await MainActor.run {
self.operatorConnected = false
self.gatewayConnected = false
self.gatewayStatusText = "Reconnecting…"
self.talkMode.updateGatewayConnected(false)
}
})
@@ -1928,7 +1931,9 @@ private extension NodeAppModel {
clientId: clientId,
clientMode: "ui",
clientDisplayName: displayName,
includeDeviceIdentity: true)
// Operator traffic should authenticate via shared gateway auth only.
// Including device identity here can trigger duplicate pairing flows.
includeDeviceIdentity: false)
}
func legacyClientIdFallback(currentClientId: String, error: Error) -> String? {

View File

@@ -675,6 +675,11 @@ struct OnboardingWizardView: View {
// We intentionally stop reconnect churn while unpaired to avoid generating multiple pending requests.
self.appModel.gatewayAutoReconnectEnabled = true
self.appModel.gatewayPairingPaused = false
self.appModel.gatewayPairingRequestId = nil
// Pairing state is sticky to prevent UI flip-flop during reconnect churn.
// Once the user explicitly resumes after approving, clear the sticky issue
// so new status/auth errors can surface instead of being masked as pairing.
self.issue = .none
self.connectMessage = "Retrying after approval…"
self.statusLine = "Retrying after approval…"
Task { await self.retryLastAttempt() }