diff --git a/lib/runtime/go_runtime_dispatch_desktop_client.dart b/lib/runtime/go_runtime_dispatch_desktop_client.dart index 19914ca0..c0286d81 100644 --- a/lib/runtime/go_runtime_dispatch_desktop_client.dart +++ b/lib/runtime/go_runtime_dispatch_desktop_client.dart @@ -19,8 +19,19 @@ class GoRuntimeDispatchDesktopClient implements RuntimeDispatchResolver { Iterable requiredCapabilities = const [], }) async { final response = await _client.request( - method: 'xworkmate.dispatch.resolve', + method: 'xworkmate.routing.resolve', params: { + 'routing': { + 'routingMode': 'auto', + if (preferredProviderId.trim().isNotEmpty) + 'preferredGatewayTarget': preferredProviderId.trim(), + 'explicitExecutionTarget': '', + 'explicitProviderId': preferredProviderId.trim(), + 'explicitModel': '', + 'explicitSkills': const [], + 'allowSkillInstall': false, + 'availableSkills': const [], + }, 'preferredProviderId': preferredProviderId.trim(), 'requiredCapabilities': requiredCapabilities .map((item) => item.trim()) @@ -45,8 +56,19 @@ class GoRuntimeDispatchDesktopClient implements RuntimeDispatchResolver { required Map nodeInfo, }) async { final response = await _client.request( - method: 'xworkmate.dispatch.resolve', + method: 'xworkmate.routing.resolve', params: { + 'routing': { + 'routingMode': 'auto', + if (preferredProviderId.trim().isNotEmpty) + 'preferredGatewayTarget': preferredProviderId.trim(), + 'explicitExecutionTarget': '', + 'explicitProviderId': preferredProviderId.trim(), + 'explicitModel': '', + 'explicitSkills': const [], + 'allowSkillInstall': false, + 'availableSkills': const [], + }, 'preferredProviderId': preferredProviderId.trim(), 'requiredCapabilities': requiredCapabilities .map((item) => item.trim()) diff --git a/test/runtime/go_runtime_dispatch_desktop_client_test.dart b/test/runtime/go_runtime_dispatch_desktop_client_test.dart new file mode 100644 index 00000000..c7dbb552 --- /dev/null +++ b/test/runtime/go_runtime_dispatch_desktop_client_test.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:xworkmate/runtime/gateway_acp_client.dart'; +import 'package:xworkmate/runtime/go_runtime_dispatch_desktop_client.dart'; +import 'package:xworkmate/runtime/runtime_external_code_agents.dart'; + +void main() { + test('desktop dispatch resolver uses xworkmate.routing.resolve', () async { + final capture = await _startAcpHttpServer(); + addTearDown(capture.close); + + final client = GatewayAcpClient( + endpointResolver: () => capture.baseEndpoint, + ); + final resolver = GoRuntimeDispatchDesktopClient( + client: client, + endpointResolver: () => capture.baseEndpoint, + ); + addTearDown(resolver.dispose); + + await resolver.resolveGatewayDispatch( + providers: const [], + preferredProviderId: 'codex', + requiredCapabilities: const ['skill-a'], + nodeState: const {}, + nodeInfo: const {}, + ); + + expect(capture.method, 'xworkmate.routing.resolve'); + expect(capture.body, contains('"routingMode":"auto"')); + expect(capture.body, contains('"preferredGatewayTarget":"codex"')); + }); +} + +Future<_CapturedAcpHttpServer> _startAcpHttpServer() async { + final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); + final capture = _CapturedAcpHttpServer._( + server, + Uri.parse('http://127.0.0.1:${server.port}'), + ); + server.listen((request) async { + final body = await utf8.decoder.bind(request).join(); + capture.body = body; + final decoded = jsonDecode(body); + capture.method = decoded['method']?.toString() ?? ''; + final id = decoded['id']?.toString() ?? 'request-id'; + request.response.headers.contentType = ContentType.json; + request.response.write( + jsonEncode({ + 'jsonrpc': '2.0', + 'id': id, + 'result': {'providerId': 'codex'}, + }), + ); + await request.response.close(); + }); + return capture; +} + +class _CapturedAcpHttpServer { + _CapturedAcpHttpServer._(this._server, this.baseEndpoint); + + final HttpServer _server; + final Uri baseEndpoint; + String method = ''; + String body = ''; + + Future close() => _server.close(force: true); +}