diff --git a/lib/runtime/go_task_service_client.dart b/lib/runtime/go_task_service_client.dart index 89139e62..36c843aa 100644 --- a/lib/runtime/go_task_service_client.dart +++ b/lib/runtime/go_task_service_client.dart @@ -255,6 +255,10 @@ class GoTaskServiceRequest { Map toExternalAcpParams() { final resolvedRouting = effectiveRouting; + final providerId = provider.isUnspecified ? '' : provider.providerId; + final gatewayProviderId = normalizedTarget.isGateway + ? (providerId.isEmpty ? kCanonicalGatewayProviderId : providerId) + : ''; final params = { 'sessionId': sessionId, 'threadId': threadId, @@ -289,7 +293,11 @@ class GoTaskServiceRequest { }, ) .toList(growable: false), - if (!provider.isUnspecified) 'provider': provider.providerId, + if (providerId.isNotEmpty) 'provider': providerId, + if (gatewayProviderId.isNotEmpty) ...{ + 'gatewayProvider': gatewayProviderId, + 'gatewayProviderId': gatewayProviderId, + }, if (remoteWorkingDirectoryHint.trim().isNotEmpty) 'remoteWorkingDirectoryHint': remoteWorkingDirectoryHint.trim(), if (model.trim().isNotEmpty) 'model': model.trim(), diff --git a/test/runtime/gateway_acp_client_auth_test.dart b/test/runtime/gateway_acp_client_auth_test.dart index 6ca665da..f689f64d 100644 --- a/test/runtime/gateway_acp_client_auth_test.dart +++ b/test/runtime/gateway_acp_client_auth_test.dart @@ -247,38 +247,48 @@ void main() { ); test( - 'desktop task execution routes Hermes through bridge RPC with provider params', + 'desktop agent task execution routes bridge-owned providers through bridge RPC', () async { - final capture = await _startAcpHttpServer(); - addTearDown(capture.close); - final client = GatewayAcpClient( - endpointResolver: () => capture.baseEndpoint, - authorizationResolver: (_) async => 'bridge-token', - ); + for (final providerId in [ + 'codex', + 'opencode', + 'gemini', + 'hermes', + ]) { + final capture = await _startAcpHttpServer(); + addTearDown(capture.close); + final client = GatewayAcpClient( + endpointResolver: () => capture.baseEndpoint, + authorizationResolver: (_) async => 'bridge-token', + ); - final transport = ExternalCodeAgentAcpDesktopTransport( - client: client, - endpointResolver: (_) => capture.baseEndpoint, - taskEndpointResolver: (_) => capture.baseEndpoint, - ); + final transport = ExternalCodeAgentAcpDesktopTransport( + client: client, + endpointResolver: (_) => capture.baseEndpoint, + taskEndpointResolver: (_) => capture.baseEndpoint, + ); - await transport.executeTask( - _taskRequest( - target: AssistantExecutionTarget.agent, - provider: SingleAgentProvider.fromJsonValue('hermes'), - ), - onUpdate: (_) {}, - ); + await transport.executeTask( + _taskRequest( + target: AssistantExecutionTarget.agent, + provider: SingleAgentProvider.fromJsonValue(providerId), + ), + onUpdate: (_) {}, + ); - expect(capture.authorizationHeader, 'Bearer bridge-token'); - expect(capture.requestPath, '/acp/rpc'); - expect(capture.requestPath, isNot(contains('/acp-server'))); - expect(capture.requestPath, isNot(contains('/gateway/openclaw'))); - expect(capture.requestBody, contains('"provider":"hermes"')); - expect( - capture.requestBody, - contains('"requestedExecutionTarget":"agent"'), - ); + final params = _lastRequestParams(capture); + final routing = params['routing'] as Map; + expect(capture.authorizationHeader, 'Bearer bridge-token'); + expect(capture.requestPath, '/acp/rpc'); + expect(capture.requestPath, isNot(contains('/acp-server'))); + expect(capture.requestPath, isNot(contains('/gateway/openclaw'))); + expect(params['provider'], providerId); + expect(params['requestedExecutionTarget'], 'agent'); + expect(routing['explicitProviderId'], providerId); + expect(routing['explicitExecutionTarget'], 'agent'); + expect(params.containsKey('gatewayProvider'), isFalse); + expect(params.containsKey('gatewayProviderId'), isFalse); + } }, ); @@ -342,11 +352,16 @@ void main() { expect(capture.requestPath, isNot(contains('/acp-server'))); expect(capture.requestPath, isNot(contains('/acp-server/gateway'))); expect(capture.requestPath, isNot(contains('/gateway/openclaw'))); - expect(capture.requestBody, contains('"provider":"openclaw"')); - expect( - capture.requestBody, - contains('"requestedExecutionTarget":"gateway"'), - ); + final params = _lastRequestParams(capture); + final routing = params['routing'] as Map; + expect(params['provider'], 'openclaw'); + expect(params['gatewayProvider'], 'openclaw'); + expect(params['gatewayProviderId'], 'openclaw'); + expect(params['executionTarget'], 'gateway'); + expect(params['requestedExecutionTarget'], 'gateway'); + expect(routing['preferredGatewayTarget'], 'openclaw'); + expect(routing['explicitExecutionTarget'], 'gateway'); + expect(routing['explicitProviderId'], 'openclaw'); expect(capture.requestBody, contains('"method":"session.start"')); expect(capture.requestBody, isNot(contains('"method":"thread/start"'))); }, @@ -553,6 +568,11 @@ String _decodeRequestId(String body) { return 'request-id'; } +Map _lastRequestParams(_CapturedAcpHttpServer capture) { + final decoded = jsonDecode(capture.requestBody) as Map; + return (decoded['params'] as Map).cast(); +} + class _CapturedAcpHttpServer { _CapturedAcpHttpServer._(this._server, this.baseEndpoint);