From 6feca1b9dc14ba696f35cfc0dddec4c66aa34c83 Mon Sep 17 00:00:00 2001 From: "bakudies@microsoft.com" Date: Sat, 8 Aug 2026 00:24:54 -0700 Subject: [PATCH 1/3] feat(connection): enforce gateway protocol compatibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/ARCHITECTURE.md | 3 + docs/CONNECTION_PROTOCOL_RESEARCH.md | 7 +- docs/TELEMETRY.md | 19 +- .../gateway-compatibility/gateway-too-new.png | Bin 0 -> 89843 bytes .../gateway-compatibility/gateway-too-old.png | Bin 0 -> 85171 bytes .../ConnectionStateMachine.cs | 57 +++ .../GatewayConnectionManager.cs | 112 ++++- .../GatewayConnectionSnapshot.cs | 34 ++ src/OpenClaw.Connection/INodeConnector.cs | 1 + .../NodeConnectionCoordinator.cs | 69 ++- src/OpenClaw.Connection/NodeConnector.cs | 3 + src/OpenClaw.Shared/ConnectEnvelopeBuilder.cs | 4 +- src/OpenClaw.Shared/GatewayErrorClassifier.cs | 11 + .../GatewayProtocolCompatibility.cs | 74 ++++ .../GatewayProtocolContract.cs | 105 +++++ src/OpenClaw.Shared/Mcp/McpToolBridge.cs | 2 +- src/OpenClaw.Shared/OpenClawGatewayClient.cs | 67 ++- .../Telemetry/OpenClawTelemetryTags.cs | 8 +- src/OpenClaw.Shared/WindowsNodeClient.cs | 64 ++- .../App.CapabilityHandlers.cs | 3 +- .../Pages/ConnectionPage.xaml | 3 +- .../Pages/ConnectionPage.xaml.cs | 66 ++- .../Pages/ConnectionPagePlan.cs | 87 ++++ .../Pages/ConnectionPageRowState.cs | 11 + .../ConnectionDiagnosticsProjection.cs | 72 ++- .../Strings/en-us/Resources.resw | 15 + .../Strings/fr-fr/Resources.resw | 15 + .../Strings/nl-nl/Resources.resw | 15 + .../Strings/zh-cn/Resources.resw | 15 + .../Strings/zh-tw/Resources.resw | 15 + src/OpenClaw.WinNode.Cli/skill.md | 13 +- .../ConnectionStateMachineTests.cs | 125 ++++++ .../GatewayConnectionManagerTests.cs | 352 +++++++++++++++ .../GatewayErrorClassifierTests.cs | 25 ++ .../GatewayProtocolContractTests.cs | 214 +++++++++ .../McpToolBridgeTests.cs | 4 + tests/OpenClaw.Shared.Tests/ModelsTests.cs | 4 +- .../OpenClawGatewayClientTests.cs | 246 ++++++++++- .../Telemetry/OpenClawTelemetryTests.cs | 5 + .../WindowsClientMetadataTests.cs | 8 +- .../WindowsNodeClientTests.cs | 418 +++++++++++++++++- .../ConnectionDiagnosticsProjectionTests.cs | 86 ++++ ...onnectionPageProtocolCompatibilityTests.cs | 211 +++++++++ .../ConnectionPageRowStateTests.cs | 19 + .../SkillMdDriftTests.cs | 14 + 45 files changed, 2638 insertions(+), 63 deletions(-) create mode 100644 docs/proof/gateway-compatibility/gateway-too-new.png create mode 100644 docs/proof/gateway-compatibility/gateway-too-old.png create mode 100644 src/OpenClaw.Shared/GatewayProtocolCompatibility.cs create mode 100644 src/OpenClaw.Shared/GatewayProtocolContract.cs create mode 100644 tests/OpenClaw.Shared.Tests/GatewayProtocolContractTests.cs create mode 100644 tests/OpenClaw.Tray.Tests/ConnectionPageProtocolCompatibilityTests.cs diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 810840343..0c99bad54 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -82,6 +82,7 @@ These are the canonical homes. Do not reintroduce private copies elsewhere. | Windows node connection generation, cancellation, start ordering, recovery, events, and telemetry | `NodeConnectionCoordinator` | authoritative | | Bootstrap/shared/device credential handoff, durable clear gate, and operator token recovery timing | `BootstrapTokenLifecycle` | authoritative | | Device role-upgrade approval, confirmation, and bounded node reconnect queue | `DevicePairApprovalCoordinator` | authoritative | +| Gateway wire protocol range, minimal `hello-ok` validation, and sanitized compatibility state | `GatewayProtocolContract` + `GatewayProtocolCompatibility` | authoritative | | Capability UI metadata | `NodeCapabilityUiCatalog` (planned) | planned | | Capability registration/gating | `NodeCapabilityRegistrationPolicy` (planned) | planned | | Local MCP exposure policy | `McpCapabilityPolicy` (planned) | planned | @@ -195,6 +196,8 @@ leading and trailing pipe. Columns, in order: | connect-envelope | authoritative | src/OpenClaw.Shared/OpenClawGatewayClient.cs + src/OpenClaw.Shared/WindowsNodeClient.cs | connect envelope wire shape, auth field mapping, and v3/v2 signing arguments | ConnectEnvelopeBuilder | callers explicitly select role, scopes, credential profile, and lifecycle/fallback state | the builder cannot infer credential precedence; exact v3/v2 signing bytes and protocol 3/4 remain unchanged | ConnectEnvelopeBuilderTests.Build_CompleteProfileMatrix_PreservesWireShapeAndSigningArguments | golden | - | | gateway-connect-inline-closed | closed | src/OpenClaw.Shared/OpenClawGatewayClient.cs + src/OpenClaw.Shared/WindowsNodeClient.cs | anonymous connect envelope, auth dictionary, and direct signature/payload construction | ConnectEnvelopeBuilder | explicit role/scope/credential profile selection, lifecycle/fallback state and persistence, redacted logging, transport send | both clients delegate connect construction without moving credential precedence into the builder | GatewayProtocolCoreClosureTests.GatewayClients_DoNotReintroduce_InlineConnectEnvelopeConstruction | source-shape | when both clients are removed or no longer initiate gateway connect handshakes | | gateway-pending-inline-closed | closed | src/OpenClaw.Shared/OpenClawGatewayClient.cs | pending maps and locks plus Track-Take-Clear and chat-send helper families | PendingRequestRegistry | request-id creation, timeout policy, response parsing/routing, transport | the client delegates registration, take, removal, and disconnect drain so request ids cannot leak and only one completion wins | GatewayProtocolCoreClosureTests.OpenClawGatewayClient_DoesNotReintroduce_InlinePendingRequestTracking | source-shape | when OpenClawGatewayClient is removed or no longer issues correlated requests | +| gateway-protocol-contract | authoritative | ConnectEnvelopeBuilder + src/OpenClaw.Shared/OpenClawGatewayClient.cs + src/OpenClaw.Shared/WindowsNodeClient.cs | duplicated wire-version range, ad hoc successful hello acceptance, and raw mismatch interpretation | GatewayProtocolContract + GatewayProtocolCompatibility | the builder retains exact envelope construction; clients retain credential precedence, signature fallback, generation fencing, and role-specific success side effects | Windows advertises Gateway protocols 3 through 4 and validates the minimal hello-ok shape before success side effects; after the Gateway accepts that range, an integer hello-ok protocol at or above 3 is accepted because it reports the Gateway current constant rather than a negotiated selection; structured mismatch details remain finite and sanitized relative to the advertised range | GatewayProtocolContractTests.SupportedRange_IsThreeThroughFour | behavioral | - | +| gateway-protocol-literals-closed | closed | ConnectEnvelopeBuilder | private minProtocol and maxProtocol numeric literals | GatewayProtocolContract | the builder serializes its existing envelopes using the shared constants | operator and node connect envelopes advertise the same 3-through-4 protocol range | GatewayProtocolContractTests.Clients_use_shared_contract_for_protocol_range | source-shape | when connect envelopes no longer carry a Gateway protocol range | | ui-dispatcher | authoritative | src/OpenClaw.Tray.WinUI/App.xaml.cs | UI-thread marshaling abstraction for presentation code | IUiDispatcher | App and existing WinUI code may call DispatcherQueue directly until the view-model migration | presentation view models depend on IUiDispatcher not a concrete DispatcherQueue | UiDispatcherContractTests.PageViewModel_ReceivesRegisteredDispatcher | behavioral | - | | navigation-scope | authoritative | src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml.cs | page view-model activation/deactivation and disposal lifetime | NavigationScopeManager | HubWindow keeps frame navigation back-stack and rail selection | transient page view models are activated on navigation and deactivated then disposed on navigate-away | NavigationScopeManagerTests.NavigatingAway_DeactivatesAndDisposesPreviousViewModel | behavioral | - | | composition-root | authoritative | src/OpenClaw.Tray.WinUI/App.xaml.cs | presentation-layer service construction and wiring | AppServiceRegistration | App remains the composition root and owns non-DI service lifetimes | one validated root ServiceProvider; App-owned singletons registered as instances are never disposed by the container | AppServiceRegistrationTests.Dispose_DoesNotDisposeAppOwnedInstanceSingletons | behavioral | - | diff --git a/docs/CONNECTION_PROTOCOL_RESEARCH.md b/docs/CONNECTION_PROTOCOL_RESEARCH.md index ed2353184..5f8c5f201 100644 --- a/docs/CONNECTION_PROTOCOL_RESEARCH.md +++ b/docs/CONNECTION_PROTOCOL_RESEARCH.md @@ -71,8 +71,11 @@ stable release-validation evidence. The managed gateway release pin is not the WebSocket protocol pin. Windows currently advertises `minProtocol: 3` and `maxProtocol: 4`; the gateway reports its current protocol constant in `hello-ok.protocol`, not a per-connection -negotiated value. Windows records that value for diagnostics but does not -currently branch behavior on it. Current upstream gateways use the protocol-3 +negotiated value. After the gateway accepts the advertised range, Windows records +that integer constant for diagnostics and requires it to be at least +`MinimumSupportedVersion`; a lower or malformed value is a terminal protocol +failure. A structured `PROTOCOL_MISMATCH` rejection determines which side falls +outside the advertised range. Current upstream gateways use the protocol-3 N-1 node window only when both `role` and `client.mode` are `node` and the client range does not support the gateway's current protocol. Because Windows advertises `maxProtocol: 4`, its node connection uses protocol 4 against a diff --git a/docs/TELEMETRY.md b/docs/TELEMETRY.md index d77843325..e07e1d4e5 100644 --- a/docs/TELEMETRY.md +++ b/docs/TELEMETRY.md @@ -100,10 +100,25 @@ The tray exports gateway lifecycle diagnostics when an endpoint is configured: - structured state logs in the `OpenClaw.Telemetry.Connection` category Lifecycle attributes are limited to role, operation, outcome, coarse error -category, and finite operator/node/overall states. Gateway URLs, IDs, device -IDs, pairing request IDs, credentials, error messages, and diagnostic-ring +category, finite operator/node/overall states, and these protocol fields: + +- `openclaw.protocol.client`: the current client wire protocol integer; Windows + currently advertises support from protocol 3 through protocol 4 +- `openclaw.protocol.gateway`: the finite relative bucket `older`, `current`, + `newer`, or `unknown`, relative to the advertised 3-through-4 range; this + bucket is not itself a compatibility verdict +- `openclaw.protocol.compatibility`: `unknown`, `compatible`, + `gateway_too_old`, `gateway_too_new`, or `mismatch` + +Gateway package versions are not protocol values and are not exported by this +instrumentation. Gateway URLs, IDs, device IDs, pairing request IDs, +credentials, integrity values, error messages, raw frames, and diagnostic-ring text are not exported. +Only an explicit protocol mismatch maps to the `protocolmismatch` error +category. Unknown or otherwise unclassified Gateway errors map to +`internalerror`; they are never promoted to protocol mismatch. + The operator phase spans distinguish local credential/client/tunnel preparation, WebSocket transport establishment, and the gateway challenge/hello handshake. The Windows node initiates its gateway connection: its prepare span includes diff --git a/docs/proof/gateway-compatibility/gateway-too-new.png b/docs/proof/gateway-compatibility/gateway-too-new.png new file mode 100644 index 0000000000000000000000000000000000000000..490cda3fa5bbe1705ee557728961c289957704ed GIT binary patch literal 89843 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 Ky|QxCXa5CqeV73N literal 0 HcmV?d00001 diff --git a/docs/proof/gateway-compatibility/gateway-too-old.png b/docs/proof/gateway-compatibility/gateway-too-old.png new file mode 100644 index 0000000000000000000000000000000000000000..be2e9903a1825b37839192257e8e17bcca79b4f8 GIT binary patch literal 85171 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 node info (device ID, pairing status, optional request ID) in the snapshot. internal void SetNodeInfo( string? deviceId, @@ -302,6 +336,7 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail) _operatorState = RoleConnectionState.Connecting; _operatorError = null; _operatorErrorKind = null; + _operatorProtocolCompatibility = OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; break; case ConnectionTrigger.ConnectRequestSent: @@ -369,6 +404,9 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail) _operatorError = null; _operatorErrorKind = null; _nodeError = null; + _nodeErrorKind = null; + _operatorProtocolCompatibility = OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; + _nodeProtocolCompatibility = OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; _operatorCredentialSource = null; _nodeCredentialSource = null; _operatorCredentialStatus = null; @@ -385,6 +423,7 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail) _operatorState = RoleConnectionState.Connecting; _operatorError = null; _operatorErrorKind = null; + _operatorProtocolCompatibility = OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; break; case ConnectionTrigger.ReconnectSuppressed: @@ -401,21 +440,29 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail) case ConnectionTrigger.NodeConnected: _nodeState = RoleConnectionState.Connected; _nodeError = null; + _nodeErrorKind = null; break; case ConnectionTrigger.NodeDisconnected: + if (_nodeErrorKind == OpenClaw.Shared.GatewayErrorKind.ProtocolMismatch) + break; + _nodeState = RoleConnectionState.Idle; _nodeError = null; + _nodeErrorKind = null; + _nodeProtocolCompatibility = OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; break; case ConnectionTrigger.NodePairingRequired: _nodeState = RoleConnectionState.PairingRequired; _nodeError = null; + _nodeErrorKind = null; break; case ConnectionTrigger.NodePaired: _nodeState = RoleConnectionState.Connected; _nodeError = null; + _nodeErrorKind = null; break; case ConnectionTrigger.NodePairingRejected: @@ -437,6 +484,11 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail) private void RebuildSnapshot() { + var (protocolCompatibility, protocolRole) = + GatewayConnectionSnapshot.DeriveProtocolCompatibility( + _operatorProtocolCompatibility, + _nodeProtocolCompatibility, + _nodeEnabled); Current = Current with { OverallState = GatewayConnectionSnapshot.DeriveOverall(_operatorState, _nodeState, _nodeEnabled), @@ -448,6 +500,7 @@ private void RebuildSnapshot() OperatorCredentialFallbackUsed = _operatorCredentialFallbackUsed, OperatorCredentialBootstrapRequired = _operatorCredentialBootstrapRequired, OperatorCredentialDetail = _operatorCredentialDetail, + OperatorProtocolCompatibility = _operatorProtocolCompatibility, OperatorPairingRequired = _operatorState == RoleConnectionState.PairingRequired, // Clear requestId when no longer in PairingRequired to prevent stale reads OperatorPairingRequestId = _operatorState == RoleConnectionState.PairingRequired @@ -455,11 +508,15 @@ private void RebuildSnapshot() NodeConnectionIntended = _nodeEnabled, NodeState = _nodeState, NodeError = _nodeError, + NodeErrorKind = _nodeErrorKind, NodeCredentialSource = _nodeCredentialSource, NodeCredentialStatus = _nodeCredentialStatus, NodeCredentialFallbackUsed = _nodeCredentialFallbackUsed, NodeCredentialBootstrapRequired = _nodeCredentialBootstrapRequired, NodeCredentialDetail = _nodeCredentialDetail, + NodeProtocolCompatibility = _nodeProtocolCompatibility, + ProtocolCompatibility = protocolCompatibility, + ProtocolCompatibilityRole = protocolRole, // Clear requestId when no longer in PairingRequired to prevent stale reads NodePairingRequestId = _nodeState == RoleConnectionState.PairingRequired ? Current.NodePairingRequestId : null, diff --git a/src/OpenClaw.Connection/GatewayConnectionManager.cs b/src/OpenClaw.Connection/GatewayConnectionManager.cs index f2c57556a..31cad11c3 100644 --- a/src/OpenClaw.Connection/GatewayConnectionManager.cs +++ b/src/OpenClaw.Connection/GatewayConnectionManager.cs @@ -74,6 +74,7 @@ private readonly Func _userDisconnectedGatewayIds = new(StringComparer.Ordinal); // Shared exclusive lease serializing destructive gateway lifecycle operations (manual WSL @@ -98,6 +99,9 @@ private readonly Func + { + if (!IsCurrentGatewayAttempt(gen, subscribedGatewayId)) return; + RecordOperatorProtocolCompatibility(gen, compatibility); + }; lifecycle.DataClient.TransportConnected += (s, e) => { if (!IsCurrentGatewayAttempt(gen, subscribedGatewayId)) return; @@ -2036,14 +2046,18 @@ private async Task HandleOperatorStatusChangedAsync(ConnectionStatus status, lon _diagnostics.RecordWebSocketEvent("WebSocket error"); if (_stateMachine.Current.OperatorState != RoleConnectionState.PairingRequired) { + _stateMachine.SetOperatorProtocolCompatibility( + ReadOperatorProtocolCompatibility(gen)); // AuthenticationFailed and Status=Error are raised back-to-back and handled // asynchronously. If the auth handler already promoted the failure to a more // specific terminal kind (for example LocalPortConflict), never let the later // generic status handler overwrite it with the original token/transport kind. - if (_stateMachine.Current.OperatorState != RoleConnectionState.Error || + var failureKind = ReadOperatorFailureKind(gen); + if (failureKind == GatewayErrorKind.ProtocolMismatch || + _stateMachine.Current.OperatorState != RoleConnectionState.Error || _stateMachine.Current.OperatorErrorKind is null) { - _stateMachine.SetOperatorErrorKind(ReadOperatorFailureKind(gen)); + _stateMachine.SetOperatorErrorKind(failureKind); _stateMachine.TryTransition( ConnectionTrigger.WebSocketError, "Transport error"); @@ -2052,7 +2066,7 @@ private async Task HandleOperatorStatusChangedAsync(ConnectionStatus status, lon CompleteOperatorTelemetryAttempt( gen, "failure", - ConnectionErrorCategory.NetworkUnreachable); + MapConnectionErrorCategory(ReadOperatorFailureKind(gen))); break; case ConnectionStatus.Connecting: _diagnostics.RecordWebSocketEvent("WebSocket connecting"); @@ -2129,12 +2143,14 @@ await _bootstrapTokenLifecycle.TryScheduleOperatorTokenRecoveryAsync( return; _diagnostics.Record("error", "Authentication failed", message); + _stateMachine.SetOperatorProtocolCompatibility( + ReadOperatorProtocolCompatibility(gen)); _stateMachine.SetOperatorErrorKind(failureKind); _stateMachine.TryTransition(ConnectionTrigger.AuthenticationFailed, message); CompleteOperatorTelemetryAttempt( gen, "failure", - ConnectionErrorCategory.AuthFailure); + MapConnectionErrorCategory(failureKind)); EmitStateChanged(); } finally @@ -2172,6 +2188,55 @@ private void ClearOperatorFailureKind(long generation) } } + private void RecordOperatorProtocolCompatibility( + long generation, + GatewayProtocolCompatibility compatibility) + { + lock (_operatorProtocolCompatibilityLock) + { + _pendingOperatorProtocolGeneration = generation; + _pendingOperatorProtocolCompatibility = compatibility; + } + + lock (_telemetryLock) + { + if (_operatorTelemetryAttempt?.Generation == generation) + _operatorTelemetryAttempt.ProtocolCompatibility = compatibility; + } + } + + private GatewayProtocolCompatibility ReadOperatorProtocolCompatibility(long generation) + { + lock (_operatorProtocolCompatibilityLock) + { + return _pendingOperatorProtocolGeneration == generation + ? _pendingOperatorProtocolCompatibility + : GatewayProtocolCompatibility.Unknown; + } + } + + private static ConnectionErrorCategory MapConnectionErrorCategory( + GatewayErrorKind? errorKind) => + errorKind switch + { + GatewayErrorKind.Auth or + GatewayErrorKind.TokenDrift or + GatewayErrorKind.DeviceTokenMismatch or + GatewayErrorKind.ScopeMismatch => ConnectionErrorCategory.AuthFailure, + GatewayErrorKind.PairingRequired => ConnectionErrorCategory.PairingPending, + GatewayErrorKind.PairingRejected => ConnectionErrorCategory.PairingRejected, + GatewayErrorKind.RateLimited => ConnectionErrorCategory.RateLimited, + GatewayErrorKind.Tunnel => ConnectionErrorCategory.SshTunnelFailure, + GatewayErrorKind.Network or + GatewayErrorKind.Tls => ConnectionErrorCategory.NetworkUnreachable, + GatewayErrorKind.Server => ConnectionErrorCategory.ServerClose, + GatewayErrorKind.ProtocolMismatch => ConnectionErrorCategory.ProtocolMismatch, + GatewayErrorKind.LocalPortConflict => ConnectionErrorCategory.InternalError, + GatewayErrorKind.Unknown => ConnectionErrorCategory.InternalError, + null => ConnectionErrorCategory.NetworkUnreachable, + _ => ConnectionErrorCategory.InternalError + }; + // Auto credential recovery clears a device token and falls back to a stronger shared/bootstrap // credential. Restrict that to trusted endpoints (mirrors the Mac app, which only retries // credentials on loopback or explicitly trusted transport): a loopback/local endpoint (traffic @@ -2415,6 +2480,8 @@ private async Task HandleHandshakeSucceededAsync(long gen) var prev = _stateMachine.Current.OverallState; _diagnostics.Record("state", "Handshake succeeded (hello-ok)"); + _stateMachine.SetOperatorProtocolCompatibility( + ReadOperatorProtocolCompatibility(gen)); _stateMachine.TryTransition(ConnectionTrigger.HandshakeSucceeded); CompleteOperatorTelemetryAttempt(gen, "success"); var nodeModeIntended = SyncNodeIntentFromSettings(); @@ -2744,6 +2811,13 @@ private void OnNodeConnectionFailure(object? sender, GatewayErrorKind errorKind) _nodeConnectionCoordinator.HandleConnectionFailure(errorKind); } + private void OnNodeProtocolCompatibilityChanged( + object? sender, + GatewayProtocolCompatibility compatibility) + { + _nodeConnectionCoordinator.HandleProtocolCompatibilityChanged(compatibility); + } + private void OnNodeDeviceTokenReceived(object? sender, DeviceTokenReceivedEventArgs e) { _nodeConnectionCoordinator.HandleDeviceTokenReceived(e); @@ -2902,6 +2976,8 @@ async Task INodeConnectionStateSink.PublishNodeStatusAsync( switch (status) { case ConnectionStatus.Connected: + _stateMachine.SetNodeProtocolCompatibility( + connector.ProtocolCompatibility); _stateMachine.TryTransition(ConnectionTrigger.NodeConnected); break; case ConnectionStatus.Connecting: @@ -2913,11 +2989,16 @@ async Task INodeConnectionStateSink.PublishNodeStatusAsync( break; case ConnectionStatus.Error: if (_stateMachine.Current.NodeState != RoleConnectionState.PairingRequired) + { + _stateMachine.SetNodeProtocolCompatibility( + connector.ProtocolCompatibility); + _stateMachine.SetNodeErrorKind(connector.FailureKind); _stateMachine.TryTransition( ConnectionTrigger.NodeError, string.IsNullOrWhiteSpace(_stateMachine.Current.NodeError) ? "Node transport error" : _stateMachine.Current.NodeError); + } break; } @@ -2959,9 +3040,13 @@ async Task INodeConnectionStateSink.PublishNodePairingAsync( if (!_nodeConnectionCoordinator.IsCurrentNodeAttempt(attempt)) return false; + _stateMachine.SetNodeProtocolCompatibility( + connector.ProtocolCompatibility); switch (pairing.Status) { case PairingStatus.Paired: + _stateMachine.SetNodeProtocolCompatibility( + connector.ProtocolCompatibility); _stateMachine.TryTransition(ConnectionTrigger.NodePaired); break; case PairingStatus.Pending: @@ -3250,6 +3335,22 @@ private static void FinishConnectionTelemetryAttempt( OpenClawTelemetryTag.String(OperationTag, attempt.Operation), OpenClawTelemetryTag.String(OpenClawTelemetryTagKey.Outcome, outcome) }; + var compatibility = attempt.ProtocolCompatibility; + tags.Add(OpenClawTelemetryTag.Number( + OpenClawTelemetryTagKey.ClientProtocol, + GatewayProtocolContract.CurrentVersion)); + tags.Add(OpenClawTelemetryTag.String( + OpenClawTelemetryTagKey.GatewayProtocol, + compatibility.GatewayProtocol switch + { + < GatewayProtocolContract.MinimumSupportedVersion => "older", + > GatewayProtocolContract.MaximumSupportedVersion => "newer", + not null => "current", + _ => "unknown" + })); + tags.Add(OpenClawTelemetryTag.String( + OpenClawTelemetryTagKey.ProtocolCompatibility, + compatibility.NormalizedState)); if (errorCategory.HasValue) { tags.Add(OpenClawTelemetryTag.String( @@ -3410,6 +3511,7 @@ private async Task DisposeCoreAsync() { telemetryEvents.TransportConnected -= OnNodeTransportConnected; telemetryEvents.ConnectionFailure -= OnNodeConnectionFailure; + telemetryEvents.ProtocolCompatibilityChanged -= OnNodeProtocolCompatibilityChanged; } } await _devicePairApprovalCoordinator.StopAsync().ConfigureAwait(false); @@ -3468,6 +3570,8 @@ private sealed record TelemetryAttempt( public Activity? PhaseActivity { get; set; } public string? PhaseName { get; set; } public long PhaseGeneration { get; set; } + public GatewayProtocolCompatibility ProtocolCompatibility { get; set; } = + GatewayProtocolCompatibility.Unknown; } private void ObserveBackgroundFault(Task task, string message) diff --git a/src/OpenClaw.Connection/GatewayConnectionSnapshot.cs b/src/OpenClaw.Connection/GatewayConnectionSnapshot.cs index 4ba8ebd36..120b607c9 100644 --- a/src/OpenClaw.Connection/GatewayConnectionSnapshot.cs +++ b/src/OpenClaw.Connection/GatewayConnectionSnapshot.cs @@ -1,5 +1,11 @@ namespace OpenClaw.Connection; +public enum GatewayProtocolCompatibilityRole +{ + Operator, + Node +} + /// /// Immutable, cross-thread-safe representation of the entire connection /// state at a point in time. Safe to cache, compare, and pass between threads. @@ -20,6 +26,8 @@ public sealed record GatewayConnectionSnapshot public bool OperatorCredentialFallbackUsed { get; init; } public bool OperatorCredentialBootstrapRequired { get; init; } public string? OperatorCredentialDetail { get; init; } + public OpenClaw.Shared.GatewayProtocolCompatibility OperatorProtocolCompatibility { get; init; } = + OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; /// /// The requestId returned by the gateway when operator pairing is required. /// Used by setup flows to approve the specific pairing request via CLI. @@ -30,6 +38,7 @@ public sealed record GatewayConnectionSnapshot public bool NodeConnectionIntended { get; init; } public RoleConnectionState NodeState { get; init; } public string? NodeError { get; init; } + public OpenClaw.Shared.GatewayErrorKind? NodeErrorKind { get; init; } public OpenClaw.Shared.PairingStatus NodePairingStatus { get; init; } public string? NodeDeviceId { get; init; } public string? NodeCredentialSource { get; init; } @@ -37,6 +46,8 @@ public sealed record GatewayConnectionSnapshot public bool NodeCredentialFallbackUsed { get; init; } public bool NodeCredentialBootstrapRequired { get; init; } public string? NodeCredentialDetail { get; init; } + public OpenClaw.Shared.GatewayProtocolCompatibility NodeProtocolCompatibility { get; init; } = + OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; /// /// The requestId returned by the gateway when node pairing is required. /// Used by the connection page to show the correct approval command. @@ -50,6 +61,10 @@ public sealed record GatewayConnectionSnapshot public string? GatewayName { get; init; } // ─── Derived ─── + public OpenClaw.Shared.GatewayProtocolCompatibility ProtocolCompatibility { get; init; } = + OpenClaw.Shared.GatewayProtocolCompatibility.Unknown; + public GatewayProtocolCompatibilityRole? ProtocolCompatibilityRole { get; init; } + public bool IsFullyConnected => OperatorState == RoleConnectionState.Connected && NodeState == RoleConnectionState.Connected; @@ -62,6 +77,25 @@ public sealed record GatewayConnectionSnapshot NodePairingStatus = OpenClaw.Shared.PairingStatus.Unknown }; + internal static ( + OpenClaw.Shared.GatewayProtocolCompatibility Compatibility, + GatewayProtocolCompatibilityRole? Role) + DeriveProtocolCompatibility( + OpenClaw.Shared.GatewayProtocolCompatibility operatorCompatibility, + OpenClaw.Shared.GatewayProtocolCompatibility nodeCompatibility, + bool nodeEnabled) + { + if (operatorCompatibility.IsMismatch) + return (operatorCompatibility, GatewayProtocolCompatibilityRole.Operator); + if (nodeEnabled && nodeCompatibility.IsMismatch) + return (nodeCompatibility, GatewayProtocolCompatibilityRole.Node); + if (operatorCompatibility.State == OpenClaw.Shared.GatewayProtocolCompatibilityState.Compatible) + return (operatorCompatibility, GatewayProtocolCompatibilityRole.Operator); + if (nodeEnabled && nodeCompatibility.State == OpenClaw.Shared.GatewayProtocolCompatibilityState.Compatible) + return (nodeCompatibility, GatewayProtocolCompatibilityRole.Node); + return (OpenClaw.Shared.GatewayProtocolCompatibility.Unknown, null); + } + /// /// Derive the overall connection state from operator and node sub-states. /// diff --git a/src/OpenClaw.Connection/INodeConnector.cs b/src/OpenClaw.Connection/INodeConnector.cs index 45bfad80b..b7bd85fd4 100644 --- a/src/OpenClaw.Connection/INodeConnector.cs +++ b/src/OpenClaw.Connection/INodeConnector.cs @@ -57,6 +57,7 @@ public interface INodeConnectorTelemetryEvents { event EventHandler TransportConnected; event EventHandler ConnectionFailure; + event EventHandler ProtocolCompatibilityChanged; } public interface INodeConnectorReconnectPolicy diff --git a/src/OpenClaw.Connection/NodeConnectionCoordinator.cs b/src/OpenClaw.Connection/NodeConnectionCoordinator.cs index ada5c3409..d7225c124 100644 --- a/src/OpenClaw.Connection/NodeConnectionCoordinator.cs +++ b/src/OpenClaw.Connection/NodeConnectionCoordinator.cs @@ -68,7 +68,9 @@ internal interface INodeConnectionStateSource internal readonly record struct NodeConnectorSnapshot( bool IsConnected, PairingStatus PairingStatus, - string? NodeDeviceId); + string? NodeDeviceId, + GatewayProtocolCompatibility ProtocolCompatibility, + GatewayErrorKind? FailureKind); internal enum NodeAutomaticStartDisposition { @@ -152,6 +154,9 @@ internal sealed class NodeConnectionCoordinator : INodePairReconnectPort private long _startGuardVersion; private CancellationTokenSource? _operationCts; private string? _tokenRecoveryAttemptedGatewayId; + private GatewayProtocolCompatibility _protocolCompatibility = + GatewayProtocolCompatibility.Unknown; + private GatewayErrorKind? _failureKind; private TelemetryAttempt? _telemetryAttempt; private int _stopped; @@ -397,6 +402,8 @@ private async Task StartAttemptAsync( operationToken = operationCts.Token; var nodeGeneration = Interlocked.Increment(ref _nodeGeneration); attempt = new NodeAttemptStamp(gatewayAttempt, nodeGeneration); + _protocolCompatibility = GatewayProtocolCompatibility.Unknown; + _failureKind = null; _operationCts = operationCts; } @@ -745,6 +752,7 @@ internal void HandleConnectionFailure(GatewayErrorKind errorKind) lock (_operationLock) { + _failureKind = errorKind; Interlocked.CompareExchange( ref _startLifecycleGeneration, -1, @@ -766,6 +774,27 @@ internal void HandleConnectionFailure(GatewayErrorKind errorKind) } } + internal void HandleProtocolCompatibilityChanged( + GatewayProtocolCompatibility compatibility) + { + var attempt = CaptureCurrentAttempt(); + if (!IsCurrentNodeAttempt(attempt)) + return; + + lock (_operationLock) + { + if (!IsCurrentNodeAttempt(attempt)) + return; + _protocolCompatibility = compatibility; + } + + lock (_telemetryLock) + { + if (_telemetryAttempt?.Generation == attempt.NodeGeneration) + _telemetryAttempt.ProtocolCompatibility = compatibility; + } + } + internal void HandleDeviceTokenReceived(DeviceTokenReceivedEventArgs token) { var attempt = CaptureCurrentAttempt(); @@ -1058,11 +1087,18 @@ private long AcquireStartGuard(long lifecycleGeneration) return version; } - private NodeConnectorSnapshot CaptureConnectorSnapshot() => - new( - _nodeConnector?.IsConnected == true, - _nodeConnector?.PairingStatus ?? PairingStatus.Unknown, - _nodeConnector?.NodeDeviceId); + private NodeConnectorSnapshot CaptureConnectorSnapshot() + { + lock (_operationLock) + { + return new( + _nodeConnector?.IsConnected == true, + _nodeConnector?.PairingStatus ?? PairingStatus.Unknown, + _nodeConnector?.NodeDeviceId, + _protocolCompatibility, + _failureKind); + } + } private void TrackBackground(Task task) { @@ -1291,6 +1327,22 @@ private void FinishTelemetryAttempt( OpenClawTelemetryTag.String(OperationTag, attempt.Operation), OpenClawTelemetryTag.String(OpenClawTelemetryTagKey.Outcome, outcome) }; + var compatibility = attempt.ProtocolCompatibility; + tags.Add(OpenClawTelemetryTag.Number( + OpenClawTelemetryTagKey.ClientProtocol, + GatewayProtocolContract.CurrentVersion)); + tags.Add(OpenClawTelemetryTag.String( + OpenClawTelemetryTagKey.GatewayProtocol, + compatibility.GatewayProtocol switch + { + < GatewayProtocolContract.MinimumSupportedVersion => "older", + > GatewayProtocolContract.MaximumSupportedVersion => "newer", + not null => "current", + _ => "unknown" + })); + tags.Add(OpenClawTelemetryTag.String( + OpenClawTelemetryTagKey.ProtocolCompatibility, + compatibility.NormalizedState)); if (errorCategory.HasValue) { tags.Add(OpenClawTelemetryTag.String( @@ -1364,7 +1416,8 @@ GatewayErrorKind.DeviceTokenMismatch or GatewayErrorKind.Network or GatewayErrorKind.Tls => ConnectionErrorCategory.NetworkUnreachable, GatewayErrorKind.Server => ConnectionErrorCategory.ServerClose, - _ => ConnectionErrorCategory.ProtocolMismatch + GatewayErrorKind.ProtocolMismatch => ConnectionErrorCategory.ProtocolMismatch, + _ => ConnectionErrorCategory.InternalError }; private static bool HasPersistedIdentityFailure( @@ -1391,6 +1444,8 @@ private sealed record TelemetryAttempt( public Activity? PhaseActivity { get; set; } public string? PhaseName { get; set; } public long PhaseGeneration { get; set; } + public GatewayProtocolCompatibility ProtocolCompatibility { get; set; } = + GatewayProtocolCompatibility.Unknown; } private sealed class NodeStartGuardLease diff --git a/src/OpenClaw.Connection/NodeConnector.cs b/src/OpenClaw.Connection/NodeConnector.cs index d5a9af349..c928b1f37 100644 --- a/src/OpenClaw.Connection/NodeConnector.cs +++ b/src/OpenClaw.Connection/NodeConnector.cs @@ -25,6 +25,7 @@ public sealed class NodeConnector : INodeConnector, INodeConnectorTelemetryEvent public event EventHandler? ClientCreated; public event EventHandler? TransportConnected; public event EventHandler? ConnectionFailure; + public event EventHandler? ProtocolCompatibilityChanged; public NodeConnector(IOpenClawLogger logger, ConnectionDiagnostics? diagnostics = null) { @@ -174,6 +175,8 @@ private async Task ConnectCoreAsync( ForwardIfCurrent(s, generation, EventArgs.Empty, TransportConnected); client.ConnectionFailure += (s, e) => ForwardIfCurrent(s, generation, e, ConnectionFailure); + client.ProtocolCompatibilityChanged += (s, e) => + ForwardIfCurrent(s, generation, e, ProtocolCompatibilityChanged); client.PairingStatusChanged += (s, e) => ForwardIfCurrent(s, generation, e, PairingStatusChanged); client.DeviceTokenReceived += (s, e) => diff --git a/src/OpenClaw.Shared/ConnectEnvelopeBuilder.cs b/src/OpenClaw.Shared/ConnectEnvelopeBuilder.cs index 2e4bb5e5e..d6afef662 100644 --- a/src/OpenClaw.Shared/ConnectEnvelopeBuilder.cs +++ b/src/OpenClaw.Shared/ConnectEnvelopeBuilder.cs @@ -297,8 +297,8 @@ internal string Serialize(string? signature) method = "connect", @params = new { - minProtocol = 3, - maxProtocol = 4, + minProtocol = GatewayProtocolContract.MinimumSupportedVersion, + maxProtocol = GatewayProtocolContract.MaximumSupportedVersion, client = new { id = SigningArguments.ClientId, diff --git a/src/OpenClaw.Shared/GatewayErrorClassifier.cs b/src/OpenClaw.Shared/GatewayErrorClassifier.cs index 6d3172f86..6c31d9d50 100644 --- a/src/OpenClaw.Shared/GatewayErrorClassifier.cs +++ b/src/OpenClaw.Shared/GatewayErrorClassifier.cs @@ -65,6 +65,9 @@ public enum GatewayErrorKind /// Rate limited by the gateway. RateLimited, + + /// The Gateway wire protocol is incompatible with this client. + ProtocolMismatch, } /// @@ -81,6 +84,9 @@ public static GatewayErrorKind Classify(string? error) var e = error.ToLowerInvariant(); + if (Contains(e, "protocol mismatch")) + return GatewayErrorKind.ProtocolMismatch; + if ((Contains(e, "rate") && Contains(e, "limit")) || Contains(e, "429") || Contains(e, "too many request")) return GatewayErrorKind.RateLimited; @@ -169,6 +175,9 @@ public static bool IsSharedGatewayTokenMismatch(string? message) => /// The structured gateway code for a wrong shared/gateway token. public const string SharedTokenMismatchCode = "AUTH_TOKEN_MISMATCH"; + /// The structured gateway code for an incompatible wire protocol. + public const string ProtocolMismatchCode = "PROTOCOL_MISMATCH"; + /// /// Code-aware classification. Structured error codes (top-level error.code and /// nested error.details.code) are authoritative and are checked BEFORE the textual @@ -188,6 +197,8 @@ public static GatewayErrorKind ClassifyWithCode(string? message, params string?[ { if (string.IsNullOrWhiteSpace(code)) continue; + if (string.Equals(code, ProtocolMismatchCode, StringComparison.OrdinalIgnoreCase)) + return GatewayErrorKind.ProtocolMismatch; if (string.Equals(code, DeviceTokenMismatchCode, StringComparison.OrdinalIgnoreCase)) return GatewayErrorKind.DeviceTokenMismatch; // A wrong shared/gateway token is terminal auth but must NOT be treated as a diff --git a/src/OpenClaw.Shared/GatewayProtocolCompatibility.cs b/src/OpenClaw.Shared/GatewayProtocolCompatibility.cs new file mode 100644 index 000000000..ff1437405 --- /dev/null +++ b/src/OpenClaw.Shared/GatewayProtocolCompatibility.cs @@ -0,0 +1,74 @@ +namespace OpenClaw.Shared; + +public enum GatewayProtocolCompatibilityState +{ + Unknown, + Compatible, + GatewayTooOld, + GatewayTooNew, + Mismatch +} + +/// +/// Sanitized Gateway wire-protocol compatibility details safe for state, +/// diagnostics, and low-cardinality telemetry. +/// +public sealed record GatewayProtocolCompatibility +{ + public required GatewayProtocolCompatibilityState State { get; init; } + public int ClientMinimumProtocol { get; init; } = GatewayProtocolContract.MinimumSupportedVersion; + public int ClientMaximumProtocol { get; init; } = GatewayProtocolContract.MaximumSupportedVersion; + public int? SelectedProtocol { get; init; } + public int? GatewayExpectedProtocol { get; init; } + public int? GatewayMinimumProtocol { get; init; } + public bool Retryable { get; init; } + + public bool IsMismatch => + State is GatewayProtocolCompatibilityState.GatewayTooOld + or GatewayProtocolCompatibilityState.GatewayTooNew + or GatewayProtocolCompatibilityState.Mismatch; + + public int? GatewayProtocol => SelectedProtocol ?? GatewayExpectedProtocol; + + public string NormalizedState => State switch + { + GatewayProtocolCompatibilityState.Compatible => "compatible", + GatewayProtocolCompatibilityState.GatewayTooOld => "gateway_too_old", + GatewayProtocolCompatibilityState.GatewayTooNew => "gateway_too_new", + GatewayProtocolCompatibilityState.Mismatch => "mismatch", + _ => "unknown" + }; + + public static GatewayProtocolCompatibility Unknown { get; } = new() + { + State = GatewayProtocolCompatibilityState.Unknown, + Retryable = true + }; + + public static GatewayProtocolCompatibility Compatible(int protocol) => new() + { + State = GatewayProtocolCompatibilityState.Compatible, + SelectedProtocol = protocol, + Retryable = false + }; + + public static GatewayProtocolCompatibility FromGatewayExpectation( + int? expectedProtocol, + int? minimumProbeProtocol = null) + { + var state = expectedProtocol switch + { + < GatewayProtocolContract.MinimumSupportedVersion => GatewayProtocolCompatibilityState.GatewayTooOld, + > GatewayProtocolContract.MaximumSupportedVersion => GatewayProtocolCompatibilityState.GatewayTooNew, + _ => GatewayProtocolCompatibilityState.Mismatch + }; + + return new GatewayProtocolCompatibility + { + State = state, + GatewayExpectedProtocol = expectedProtocol, + GatewayMinimumProtocol = minimumProbeProtocol, + Retryable = false + }; + } +} diff --git a/src/OpenClaw.Shared/GatewayProtocolContract.cs b/src/OpenClaw.Shared/GatewayProtocolContract.cs new file mode 100644 index 000000000..9edd47749 --- /dev/null +++ b/src/OpenClaw.Shared/GatewayProtocolContract.cs @@ -0,0 +1,105 @@ +using System.Text.Json; + +namespace OpenClaw.Shared; + +/// +/// Defines the Windows clients' supported Gateway wire protocol and validates +/// the minimal successful handshake contract. +/// +public static class GatewayProtocolContract +{ + public const int SupportedVersion = 4; + public const int CurrentVersion = SupportedVersion; + public const int MinimumSupportedVersion = 3; + public const int MaximumSupportedVersion = CurrentVersion; + public const string HelloOkType = "hello-ok"; + + public static bool IsHelloOk(JsonElement payload) => + payload.ValueKind == JsonValueKind.Object && + payload.TryGetProperty("type", out var type) && + type.ValueKind == JsonValueKind.String && + string.Equals(type.GetString(), HelloOkType, StringComparison.Ordinal); + + public static bool TryValidateHelloOk(JsonElement payload, out string error) + { + if (payload.ValueKind != JsonValueKind.Object) + { + error = "hello-ok payload must be an object"; + return false; + } + + if (!IsHelloOk(payload)) + { + error = "connect success payload must have type hello-ok"; + return false; + } + + if (!payload.TryGetProperty("protocol", out var protocol) || + protocol.ValueKind != JsonValueKind.Number || + !protocol.TryGetInt32(out var version)) + { + error = "hello-ok protocol must be an integer"; + return false; + } + + // hello-ok.protocol is the Gateway's current protocol constant, not a + // negotiated selection. Once the Gateway accepts our advertised range, + // future protocol values remain valid unless they fall below our floor. + if (version < MinimumSupportedVersion) + { + error = $"hello-ok protocol {version} is unsupported"; + return false; + } + + error = string.Empty; + return true; + } + + public static bool TryGetProtocol(JsonElement payload, out int protocol) + { + protocol = default; + return payload.ValueKind == JsonValueKind.Object && + payload.TryGetProperty("protocol", out var protocolValue) && + protocolValue.ValueKind == JsonValueKind.Number && + protocolValue.TryGetInt32(out protocol); + } + + public static GatewayProtocolCompatibility ParseMismatch(JsonElement response) + { + if (!TryGetErrorDetails(response, out var details)) + return GatewayProtocolCompatibility.FromGatewayExpectation(expectedProtocol: null); + + return GatewayProtocolCompatibility.FromGatewayExpectation( + TryGetInteger(details, "expectedProtocol"), + TryGetInteger(details, "minimumProbeProtocol")); + } + + private static bool TryGetErrorDetails(JsonElement response, out JsonElement details) + { + details = default; + if (response.ValueKind != JsonValueKind.Object || + !response.TryGetProperty("error", out var error) || + error.ValueKind != JsonValueKind.Object) + { + return false; + } + + if (error.TryGetProperty("details", out details) && + details.ValueKind == JsonValueKind.Object) + { + return true; + } + + return error.TryGetProperty("data", out var data) && + data.ValueKind == JsonValueKind.Object && + data.TryGetProperty("details", out details) && + details.ValueKind == JsonValueKind.Object; + } + + private static int? TryGetInteger(JsonElement value, string propertyName) => + value.TryGetProperty(propertyName, out var property) && + property.ValueKind == JsonValueKind.Number && + property.TryGetInt32(out var integer) + ? integer + : null; +} diff --git a/src/OpenClaw.Shared/Mcp/McpToolBridge.cs b/src/OpenClaw.Shared/Mcp/McpToolBridge.cs index 9828790e0..d5b6a09ca 100644 --- a/src/OpenClaw.Shared/Mcp/McpToolBridge.cs +++ b/src/OpenClaw.Shared/Mcp/McpToolBridge.cs @@ -386,7 +386,7 @@ private object HandleToolsList() ["app.chat.queue.cancel"] = "Cancel/remove one native chat outgoing queue entry before it is sent. Args: queuedMessageId (string, required), threadId/sessionKey (string, required; use the threadId returned by app.chat.queue.list or app.chat.snapshot). Only Queued/Failed entries can be removed; Sending entries may already have reached the gateway. Returns { canceled, threadId, queuedMessageId, remainingCount, error? }.", ["app.connection.status"] = - "READ-ONLY local MCP connection diagnostics. No args. Returns effective mode/state, active gateway metadata, operator/node credential resolution, MCP runtime state, browser proxy caveat, pending approval actions, retry hints, and recent diagnostic events.", + "READ-ONLY local MCP connection diagnostics. No args. Returns effective mode/state, installed Gateway package version, selected wire protocol and normalized compatibility details, active gateway metadata, operator/node credential resolution, MCP runtime state, browser proxy caveat, pending approval actions, retry hints, and recent diagnostic events.", ["app.connection.gateways"] = "READ-ONLY saved gateway diagnostics. No args. Returns { activeGatewayId, count, gateways[] } with per-gateway id/name/url, active flag, lastConnected, credential presence booleans, SSH/browser-proxy configuration, and no token values.", ["app.connection.applySetupCode"] = diff --git a/src/OpenClaw.Shared/OpenClawGatewayClient.cs b/src/OpenClaw.Shared/OpenClawGatewayClient.cs index e929b0727..a012a4d89 100644 --- a/src/OpenClaw.Shared/OpenClawGatewayClient.cs +++ b/src/OpenClaw.Shared/OpenClawGatewayClient.cs @@ -96,6 +96,7 @@ internal IConnectEnvelopeSigner ConnectEnvelopeSigner private string? _pairingRequiredRequestId; private bool _authFailed; private readonly HandshakeChallengeGate _handshakeChallengeGate = new(); + private bool _protocolMismatch; private string? _lastSkillsStatusAgentId; private readonly bool _tokenIsBootstrapToken; private readonly bool _bootstrapPairAsNode; @@ -178,7 +179,7 @@ protected void RaiseTransportConnected() => protected override bool ShouldAutoReconnect() { // PairingRequired must stay visible, but approval only takes effect on a fresh socket. - return !_authFailed; + return !_authFailed && !_protocolMismatch; } protected override void OnDisconnected() @@ -268,6 +269,8 @@ public Func>? /// kind for policy/UI decisions and keep the accompanying text only for sanitized detail. /// public event EventHandler? ConnectionFailure; + /// Raised with sanitized wire-protocol compatibility details for the current handshake. + public event EventHandler? ProtocolCompatibilityChanged; public string? OperatorDeviceId => _operatorDeviceId; public IReadOnlyList GrantedOperatorScopes => _grantedOperatorScopes; @@ -1919,6 +1922,12 @@ private void ProcessMessageForConnection( if (!IsCurrentConnectionGeneration(sourceConnectionGeneration)) return; + if (_protocolMismatch) + { + _logger.Warn("[HANDSHAKE] Ignoring message after terminal protocol mismatch"); + return; + } + try { using var doc = JsonDocument.Parse(json); @@ -2032,7 +2041,30 @@ private void HandleResponseForConnection( return; } - if (!root.TryGetProperty("payload", out var payload)) return; + if (!root.TryGetProperty("payload", out var payload)) + { + if (string.Equals(requestMethod, "connect", StringComparison.Ordinal)) + HandleProtocolMismatch("connect success response has no payload"); + return; + } + + var isHelloOk = GatewayProtocolContract.IsHelloOk(payload); + if (isHelloOk && + HandshakeAuthorizationAsync is not null && + !string.Equals(requestMethod, "connect", StringComparison.Ordinal)) + { + _logger.Warn("[HANDSHAKE] Ignoring uncorrelated hello-ok on guarded validation connection."); + return; + } + + if ((string.Equals(requestMethod, "connect", StringComparison.Ordinal) || isHelloOk) && + !GatewayProtocolContract.TryValidateHelloOk(payload, out var protocolError)) + { + var compatibility = GatewayProtocolCompatibility.FromGatewayExpectation( + GatewayProtocolContract.TryGetProtocol(payload, out var protocol) ? protocol : null); + HandleProtocolMismatch(protocolError, compatibility); + return; + } if (!string.IsNullOrEmpty(requestMethod) && HandleKnownResponse(requestMethod!, payload)) { @@ -2040,7 +2072,7 @@ private void HandleResponseForConnection( } // Handle handshake acknowledgement payload. - if (payload.TryGetProperty("type", out var t) && t.GetString() == "hello-ok") + if (isHelloOk) { if (!IsCurrentConnectionGeneration(sourceConnectionGeneration) || !_handshakeChallengeGate.IsAuthorized(sourceConnectionGeneration) || @@ -2054,6 +2086,11 @@ private void HandleResponseForConnection( Volatile.Write(ref _pairingRequiredAwaitingApproval, false); Volatile.Write(ref _pairingRequiredRequestId, null); _authFailed = false; + _protocolMismatch = false; + _ = GatewayProtocolContract.TryGetProtocol(payload, out var acceptedProtocol); + ProtocolCompatibilityChanged?.Invoke( + this, + GatewayProtocolCompatibility.Compatible(acceptedProtocol)); ResetReconnectAttempts(); _operatorDeviceId = TryGetHandshakeDeviceId(payload); _grantedOperatorScopes = TryGetHandshakeScopes(payload); @@ -2343,6 +2380,7 @@ private void HandleRequestError(string? method, JsonElement root) { var message = TryGetErrorMessage(root) ?? "request failed"; var detailCode = method == "connect" ? TryGetErrorDetailCode(root) : null; + var topLevelCode = method == "connect" ? TryGetErrorTopLevelCode(root) : null; if (string.IsNullOrEmpty(method)) { @@ -2359,6 +2397,14 @@ private void HandleRequestError(string? method, JsonElement root) _logger.Info($"[HANDSHAKE] Raw error response: {rawJson}"); } + if (method == "connect" && + GatewayErrorClassifier.ClassifyWithCode(message, topLevelCode, detailCode) == + GatewayErrorKind.ProtocolMismatch) + { + HandleProtocolMismatch(message, GatewayProtocolContract.ParseMismatch(root)); + return; + } + if (method == "connect" && detailCode == "DEVICE_AUTH_SIGNATURE_EXPIRED") { _authFailed = true; @@ -2405,7 +2451,6 @@ private void HandleRequestError(string? method, JsonElement root) // error.code and the structured error.details.code so a device-token mismatch delivered in // either place is recognized (the gateway may send the reason only as a code with a generic // message). - var topLevelCode = TryGetErrorTopLevelCode(root); if (method == "connect" && (IsTerminalAuthError(message) || IsTerminalAuthDetailCode(detailCode) || IsTerminalAuthDetailCode(topLevelCode))) { @@ -2500,6 +2545,20 @@ private void HandleRequestError(string? method, JsonElement root) _logger.Warn($"{method} failed: {message}"); } + private void HandleProtocolMismatch( + string detail, + GatewayProtocolCompatibility? compatibility = null) + { + _protocolMismatch = true; + AbortCurrentWebSocket(CurrentConnectionGeneration); + _logger.Warn($"[HANDSHAKE] Gateway protocol mismatch: {detail}"); + ProtocolCompatibilityChanged?.Invoke( + this, + compatibility ?? GatewayProtocolCompatibility.FromGatewayExpectation(expectedProtocol: null)); + RaiseConnectionFailure(GatewayErrorKind.ProtocolMismatch); + RaiseStatusChanged(ConnectionStatus.Error); + } + private static bool TryGetSessionsPayload(JsonElement payload, out JsonElement sessions) { if (payload.ValueKind == JsonValueKind.Object && diff --git a/src/OpenClaw.Shared/Telemetry/OpenClawTelemetryTags.cs b/src/OpenClaw.Shared/Telemetry/OpenClawTelemetryTags.cs index 9bc304069..721a1ed38 100644 --- a/src/OpenClaw.Shared/Telemetry/OpenClawTelemetryTags.cs +++ b/src/OpenClaw.Shared/Telemetry/OpenClawTelemetryTags.cs @@ -7,7 +7,10 @@ public enum OpenClawTelemetryTagKey ErrorCategory, ErrorType, Reason, - Status + Status, + ClientProtocol, + GatewayProtocol, + ProtocolCompatibility } /// @@ -24,6 +27,9 @@ public static string ToTelemetryName(this OpenClawTelemetryTagKey key) => OpenClawTelemetryTagKey.ErrorType => "error.type", OpenClawTelemetryTagKey.Reason => "openclaw.reason", OpenClawTelemetryTagKey.Status => "openclaw.status", + OpenClawTelemetryTagKey.ClientProtocol => "openclaw.protocol.client", + OpenClawTelemetryTagKey.GatewayProtocol => "openclaw.protocol.gateway", + OpenClawTelemetryTagKey.ProtocolCompatibility => "openclaw.protocol.compatibility", _ => throw new ArgumentOutOfRangeException(nameof(key), key, "Unknown OpenClaw telemetry tag key.") }; } diff --git a/src/OpenClaw.Shared/WindowsNodeClient.cs b/src/OpenClaw.Shared/WindowsNodeClient.cs index 171f49fe6..886bfa671 100644 --- a/src/OpenClaw.Shared/WindowsNodeClient.cs +++ b/src/OpenClaw.Shared/WindowsNodeClient.cs @@ -38,6 +38,7 @@ public class WindowsNodeClient : WebSocketClientBase // even after OnDisconnected clears _isPendingApproval. private volatile bool _pairingBlocked; private volatile bool _rateLimited; + private volatile bool _protocolMismatch; private bool _useV2Signature; // true after v3 signature rejected by gateway public bool UseV2Signature { get => _useV2Signature; set => _useV2Signature = value; } private readonly HandshakeChallengeGate _handshakeChallengeGate = new(); @@ -90,6 +91,8 @@ internal IConnectEnvelopeSigner ConnectEnvelopeSigner /// public Func>? HandshakeAuthorizationAsync { get; set; } + /// Raised with sanitized wire-protocol compatibility details for the current handshake. + public event EventHandler? ProtocolCompatibilityChanged; protected override void OnReconnectAuthorizationDenied( ReconnectAuthorizationResult authorization) @@ -297,6 +300,12 @@ protected override async Task ProcessMessageForConnectionAsync( } var type = typeProp.GetString(); _logger.Debug($"[NODE] Processing message type: {type}"); + + if (_protocolMismatch) + { + _logger.Warn("[NODE] Ignoring message after terminal protocol mismatch"); + return; + } switch (type) { @@ -874,12 +883,32 @@ private void HandleResponseForConnection( if (!root.TryGetProperty("payload", out var payload)) { - _logger.Warn("[NODE] Response has no payload"); + if (isConnectResponse) + HandleProtocolMismatch("connect success response has no payload"); + return; + } + + var isHelloOk = GatewayProtocolContract.IsHelloOk(payload); + if (isHelloOk && !isConnectResponse) + { + _logger.Warn("[HANDSHAKE] Ignoring uncorrelated node hello-ok."); return; } + + if (isConnectResponse && + !GatewayProtocolContract.TryValidateHelloOk(payload, out var protocolError)) + { + var compatibility = GatewayProtocolCompatibility.FromGatewayExpectation( + GatewayProtocolContract.TryGetProtocol(payload, out var protocol) ? protocol : null); + HandleProtocolMismatch(protocolError, compatibility); + return; + } + + if (!isHelloOk) + return; // Handle hello-ok (successful registration) - if (payload.TryGetProperty("type", out var t) && t.GetString() == "hello-ok") + if (isHelloOk) { if (!isConnectResponse || !_handshakeChallengeGate.IsAuthorized(sourceConnectionGeneration)) @@ -894,6 +923,11 @@ private void HandleResponseForConnection( var reconnectingAfterApproval = _pairingApprovedAwaitingReconnect; _isConnected = true; _rateLimited = false; // Clear transient rate-limit on successful connect + _protocolMismatch = false; + _ = GatewayProtocolContract.TryGetProtocol(payload, out var acceptedProtocol); + ProtocolCompatibilityChanged?.Invoke( + this, + GatewayProtocolCompatibility.Compatible(acceptedProtocol)); ResetReconnectAttempts(); // Extract node ID if returned @@ -1032,6 +1066,13 @@ private void HandleRequestError(JsonElement root) var effectiveErrorCode = detailCode ?? errorCode; _logger.Info($"[HANDSHAKE] Connect error: message=\"{error}\", code={errorCode}, detailCode={detailCode ?? "none"}"); + if (!_isConnected && + ClassifyConnectionFailure(error, errorCode, detailCode) == GatewayErrorKind.ProtocolMismatch) + { + HandleProtocolMismatch(error, GatewayProtocolContract.ParseMismatch(root)); + return; + } + if (string.Equals(errorCode, "NOT_PAIRED", StringComparison.OrdinalIgnoreCase)) { if (_isPendingApproval) @@ -1113,6 +1154,22 @@ private static GatewayErrorKind ClassifyConnectionFailure(string error, string e return GatewayErrorClassifier.ClassifyWithCode(error, errorCode, detailsCode); } + private void HandleProtocolMismatch( + string detail, + GatewayProtocolCompatibility? compatibility = null) + { + _protocolMismatch = true; + _isConnected = false; + Volatile.Write(ref _pendingConnectRequestId, null); + AbortCurrentWebSocket(CurrentConnectionGeneration); + _logger.Warn($"[NODE] Gateway protocol mismatch: {TokenSanitizer.Sanitize(detail)}"); + ProtocolCompatibilityChanged?.Invoke( + this, + compatibility ?? GatewayProtocolCompatibility.FromGatewayExpectation(expectedProtocol: null)); + ConnectionFailure?.Invoke(this, GatewayErrorKind.ProtocolMismatch); + RaiseStatusChanged(ConnectionStatus.Error); + } + // Structured terminal-auth codes (wrong shared/bootstrap token, rate limit, token not // configured, device-token mismatch). These are permanent for the current connection, so the // node client must stop its own auto-reconnect even when the human message is generic. @@ -1876,6 +1933,9 @@ protected override bool ShouldAutoReconnect() if (_rateLimited) return false; + if (_protocolMismatch) + return false; + return true; } diff --git a/src/OpenClaw.Tray.WinUI/App.CapabilityHandlers.cs b/src/OpenClaw.Tray.WinUI/App.CapabilityHandlers.cs index 8e93ebbe9..6a9b6547b 100644 --- a/src/OpenClaw.Tray.WinUI/App.CapabilityHandlers.cs +++ b/src/OpenClaw.Tray.WinUI/App.CapabilityHandlers.cs @@ -269,7 +269,8 @@ private void WireAppCapabilityHandlers() mcpError: mcpPlan.ShouldShow ? mcpPlan.Message : null, nodeBrowserProxyEnabled: _settings?.NodeBrowserProxyEnabled != false, recentDiagnostics: recentDiagnostics, - diagnosticEventCount: diagnostics?.Count ?? recentDiagnostics.Count)); + diagnosticEventCount: diagnostics?.Count ?? recentDiagnostics.Count, + gatewaySelf: _appState?.GatewaySelf)); }; connection.GatewaysHandler = () => diff --git a/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml b/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml index 4b6220e84..1cc80730b 100644 --- a/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml +++ b/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml @@ -683,7 +683,8 @@ - +