From 3f3c94feb311c2ec834bcf8dd34cd55beb06c8d2 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 8 May 2026 17:25:26 +0800 Subject: [PATCH] fix: use OpenClaw task submit endpoint --- ...pp_controller_desktop_runtime_helpers.dart | 19 +++ lib/runtime/gateway_acp_client.dart | 17 +++ .../runtime/gateway_acp_client_auth_test.dart | 120 +++++++++--------- 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/lib/app/app_controller_desktop_runtime_helpers.dart b/lib/app/app_controller_desktop_runtime_helpers.dart index aa6439fc..411fd1f2 100644 --- a/lib/app/app_controller_desktop_runtime_helpers.dart +++ b/lib/app/app_controller_desktop_runtime_helpers.dart @@ -951,6 +951,9 @@ extension AppControllerDesktopRuntimeHelpers on AppController { if (bridgeEndpoint == null) { return null; } + if (_usesOpenClawTaskSubmitEndpointInternal(request)) { + return bridgeEndpoint.replace(path: '/gateway/openclaw'); + } return resolveAcpHttpRpcEndpoint(bridgeEndpoint); } @@ -1051,6 +1054,22 @@ extension AppControllerDesktopRuntimeHelpers on AppController { ) => kGatewayRemoteProfileIndex; } +bool _usesOpenClawTaskSubmitEndpointInternal(GoTaskServiceRequest request) { + if (request.isMultiAgentRequest || !request.target.isGateway) { + return false; + } + final providerId = normalizeSingleAgentProviderId( + request.provider.providerId, + ); + if (providerId == kCanonicalGatewayProviderId) { + return true; + } + return normalizeSingleAgentProviderId( + request.effectiveRouting.preferredGatewayTarget, + ) == + kCanonicalGatewayProviderId; +} + String _normalizeAuthorizationHeaderInternal(String raw) { final trimmed = raw.trim(); if (trimmed.isEmpty) { diff --git a/lib/runtime/gateway_acp_client.dart b/lib/runtime/gateway_acp_client.dart index 17ffc74c..34e3558e 100644 --- a/lib/runtime/gateway_acp_client.dart +++ b/lib/runtime/gateway_acp_client.dart @@ -1174,6 +1174,14 @@ class GatewayAcpClient { Uri? _resolveHttpRpcEndpoint([Uri? endpointOverride, String method = '']) { final endpoint = endpointOverride ?? endpointResolver(); + if (_isOpenClawTaskSubmitEndpoint(endpoint) && + _isOpenClawTaskSubmitMethod(method)) { + return endpoint?.replace( + path: '/gateway/openclaw', + query: null, + fragment: null, + ); + } return resolveAcpHttpRpcEndpoint(endpoint); } @@ -1237,6 +1245,15 @@ bool _isOpenClawTaskSubmitMethod(String method) { return normalized == 'session.start' || normalized == 'session.message'; } +bool _isOpenClawTaskSubmitEndpoint(Uri? endpoint) { + var path = endpoint?.path.trim() ?? ''; + if (!path.startsWith('/')) { + path = '/$path'; + } + path = path.replaceFirst(RegExp(r'/+$'), ''); + return path == '/gateway/openclaw'; +} + Duration gatewayAcpHttpResponseTimeoutFor( Uri endpoint, String method, [ diff --git a/test/runtime/gateway_acp_client_auth_test.dart b/test/runtime/gateway_acp_client_auth_test.dart index 2ecf64d2..29e4fa25 100644 --- a/test/runtime/gateway_acp_client_auth_test.dart +++ b/test/runtime/gateway_acp_client_auth_test.dart @@ -832,7 +832,7 @@ void main() { ); test( - 'desktop task execution routes OpenClaw through managed bridge RPC', + 'desktop task execution routes OpenClaw through required task endpoint', () async { final capture = await _startAcpHttpServer( streamResponse: true, @@ -865,7 +865,8 @@ void main() { final transport = ExternalCodeAgentAcpDesktopTransport( client: client, endpointResolver: (_) => capture.baseEndpoint, - taskEndpointResolver: (_) => capture.baseEndpoint, + taskEndpointResolver: (_) => + capture.baseEndpoint.replace(path: '/gateway/openclaw'), ); final result = await transport.executeTask( @@ -878,8 +879,7 @@ void main() { expect(capture.authorizationHeader, 'Bearer bridge-token'); expect(capture.acceptHeader, 'text/event-stream, application/json'); - expect(capture.requestPath, '/acp/rpc'); - expect(capture.requestPath, isNot(contains('/gateway/openclaw'))); + expect(capture.requestPath, '/gateway/openclaw'); expect(capture.requestPath, isNot(contains('/acp-server'))); expect(capture.requestPath, isNot(contains('/acp-server/gateway'))); final params = _lastRequestParams(capture); @@ -915,7 +915,7 @@ void main() { ); test( - 'desktop OpenClaw follow-up routes through managed bridge RPC', + 'desktop OpenClaw follow-up routes through required task endpoint', () async { final capture = await _startAcpHttpServer(); addTearDown(capture.close); @@ -927,7 +927,8 @@ void main() { final transport = ExternalCodeAgentAcpDesktopTransport( client: client, endpointResolver: (_) => capture.baseEndpoint, - taskEndpointResolver: (_) => capture.baseEndpoint, + taskEndpointResolver: (_) => + capture.baseEndpoint.replace(path: '/gateway/openclaw'), ); await transport.executeTask( @@ -940,8 +941,7 @@ void main() { ); expect(capture.acceptHeader, 'text/event-stream, application/json'); - expect(capture.requestPath, '/acp/rpc'); - expect(capture.requestPath, isNot(contains('/gateway/openclaw'))); + expect(capture.requestPath, '/gateway/openclaw'); expect(capture.requestBody, contains('"method":"session.message"')); }, ); @@ -983,58 +983,62 @@ void main() { ); }); - test('desktop controller keeps task submit on managed bridge RPC', () { - final controller = AppController( - environmentOverride: const {}, - ); - addTearDown(controller.dispose); + test( + 'desktop controller uses OpenClaw endpoint only for gateway task submit', + () { + final controller = AppController( + environmentOverride: const {}, + ); + addTearDown(controller.dispose); - final openClawStart = controller - .resolveExternalAcpEndpointForRequestInternal( - _taskRequest( - target: AssistantExecutionTarget.gateway, - provider: SingleAgentProvider.openclaw, - ), - ); - final openClawFollowUp = controller - .resolveExternalAcpEndpointForRequestInternal( - _taskRequest( - target: AssistantExecutionTarget.gateway, - provider: SingleAgentProvider.openclaw, - resumeSession: true, - ), - ); - final unspecifiedGateway = controller - .resolveExternalAcpEndpointForRequestInternal( - _taskRequest( - target: AssistantExecutionTarget.gateway, - provider: SingleAgentProvider.unspecified, - ), - ); - final multiAgentGateway = controller - .resolveExternalAcpEndpointForRequestInternal( - _taskRequest( - target: AssistantExecutionTarget.gateway, - provider: SingleAgentProvider.openclaw, - multiAgent: true, - ), - ); - final agentTask = controller.resolveExternalAcpEndpointForRequestInternal( - _taskRequest( - target: AssistantExecutionTarget.agent, - provider: SingleAgentProvider.codex, - ), - ); + final openClawStart = controller + .resolveExternalAcpEndpointForRequestInternal( + _taskRequest( + target: AssistantExecutionTarget.gateway, + provider: SingleAgentProvider.openclaw, + ), + ); + final openClawFollowUp = controller + .resolveExternalAcpEndpointForRequestInternal( + _taskRequest( + target: AssistantExecutionTarget.gateway, + provider: SingleAgentProvider.openclaw, + resumeSession: true, + ), + ); + final unspecifiedGateway = controller + .resolveExternalAcpEndpointForRequestInternal( + _taskRequest( + target: AssistantExecutionTarget.gateway, + provider: SingleAgentProvider.unspecified, + ), + ); + final multiAgentGateway = controller + .resolveExternalAcpEndpointForRequestInternal( + _taskRequest( + target: AssistantExecutionTarget.gateway, + provider: SingleAgentProvider.openclaw, + multiAgent: true, + ), + ); + final agentTask = controller + .resolveExternalAcpEndpointForRequestInternal( + _taskRequest( + target: AssistantExecutionTarget.agent, + provider: SingleAgentProvider.codex, + ), + ); - expect(openClawStart?.path, '/acp/rpc'); - expect(openClawFollowUp?.path, '/acp/rpc'); - expect(unspecifiedGateway?.path, '/acp/rpc'); - expect(multiAgentGateway?.path, '/acp/rpc'); - expect(agentTask?.path, '/acp/rpc'); - }); + expect(openClawStart?.path, '/gateway/openclaw'); + expect(openClawFollowUp?.path, '/gateway/openclaw'); + expect(unspecifiedGateway?.path, '/gateway/openclaw'); + expect(multiAgentGateway?.path, '/acp/rpc'); + expect(agentTask?.path, '/acp/rpc'); + }, + ); test( - 'desktop controller resolves OpenClaw gateway submit to managed bridge RPC', + 'desktop controller resolves OpenClaw gateway submit to required task endpoint', () { final controller = AppController( environmentOverride: const {}, @@ -1051,10 +1055,10 @@ void main() { expect( endpoint.toString(), - 'https://xworkmate-bridge.svc.plus/acp/rpc', + 'https://xworkmate-bridge.svc.plus/gateway/openclaw', ); expect(endpoint, isNotNull); - expect(endpoint!.path, isNot('/gateway/openclaw')); + expect(endpoint!.path, isNot('/acp/rpc')); }, );