refactor: remove stale app-driven external ACP provider sync

This commit is contained in:
Haitao Pan 2026-04-10 21:00:18 +08:00
parent 7609e2b38d
commit 94ecedee84
6 changed files with 83 additions and 46 deletions

View File

@ -307,9 +307,6 @@ class AppController extends ChangeNotifier {
<String, List<GatewayChatMessage>>{};
final Map<String, String> aiGatewayStreamingTextBySessionInternal =
<String, String>{};
final Map<String, ExternalCodeAgentAcpSyncedProvider>
syncedGoAgentProvidersInternal =
<String, ExternalCodeAgentAcpSyncedProvider>{};
final DesktopThreadArtifactService threadArtifactServiceInternal =
DesktopThreadArtifactService();
List<AssistantThreadSkillEntry> singleAgentSharedImportedSkillsInternal =

View File

@ -37,47 +37,6 @@ import 'app_controller_desktop_core.dart';
import 'app_controller_desktop_thread_sessions.dart';
extension AppControllerDesktopExternalAcpRouting on AppController {
Future<List<ExternalCodeAgentAcpSyncedProvider>>
buildExternalAcpSyncedProvidersInternal() async {
final providers = <ExternalCodeAgentAcpSyncedProvider>[];
for (final profile in settings.externalAcpEndpoints) {
final builtinProvider = profile.builtinProvider;
final effectiveProfile = builtinProvider == null
? profile
: settings.externalAcpEndpointForProvider(builtinProvider);
final providerId = effectiveProfile.providerKey.trim();
final endpoint = effectiveProfile.endpoint.trim();
if (providerId.isEmpty || endpoint.isEmpty) {
continue;
}
final authorizationHeader = effectiveProfile.authRef.trim().isEmpty
? ''
: await settingsControllerInternal.resolveSecretValueInternal(
refName: effectiveProfile.authRef.trim(),
);
providers.add(
ExternalCodeAgentAcpSyncedProvider(
providerId: providerId,
label: effectiveProfile.label,
endpoint: endpoint,
authorizationHeader: authorizationHeader,
enabled: effectiveProfile.enabled,
),
);
}
return providers;
}
Future<void> syncExternalAcpProvidersInternal() async {
final providers = await buildExternalAcpSyncedProvidersInternal();
syncedGoAgentProvidersInternal
..clear()
..addEntries(
providers.map((item) => MapEntry(item.providerId.trim(), item)),
);
await goTaskServiceClientInternal.syncExternalProviders(providers);
}
ExternalCodeAgentAcpRoutingConfig buildExternalAcpRoutingForSessionInternal(
String sessionKey, {
String? explicitExecutionTarget,

View File

@ -103,7 +103,6 @@ Future<void> refreshSingleAgentCapabilitiesRuntimeInternal(
AppController controller, {
bool forceRefresh = false,
}) async {
await controller.syncExternalAcpProvidersInternal();
final capabilities = await controller.goTaskServiceClientInternal
.loadExternalAcpCapabilities(
target: AssistantExecutionTarget.singleAgent,

View File

@ -58,7 +58,6 @@ Future<void> sendSingleAgentMessageDesktopGoTaskFlowInternal(
sessionKey,
);
final selection = controller.singleAgentProviderForSession(sessionKey);
await controller.syncExternalAcpProvidersInternal();
final capabilities = await controller.goTaskServiceClientInternal
.loadExternalAcpCapabilities(
target: AssistantExecutionTarget.singleAgent,

View File

@ -140,6 +140,9 @@ class ExternalCodeAgentAcpDesktopTransport
Future<void> dispose() => _bridge.dispose();
Future<void> _syncProviders() async {
if (_syncedProviders.isEmpty) {
return;
}
await _bridge.request(
method: 'xworkmate.providers.sync',
params: <String, dynamic>{

View File

@ -0,0 +1,80 @@
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/runtime/external_code_agent_acp_desktop_transport.dart';
import 'package:xworkmate/runtime/go_acp_stdio_bridge.dart';
import 'package:xworkmate/runtime/go_task_service_client.dart';
import 'package:xworkmate/runtime/runtime_models.dart';
class _FakeGoAcpStdioBridge extends GoAcpStdioBridge {
_FakeGoAcpStdioBridge();
final List<String> methods = <String>[];
final StreamController<Map<String, dynamic>> _notifications =
StreamController<Map<String, dynamic>>.broadcast();
@override
Stream<Map<String, dynamic>> get notifications => _notifications.stream;
@override
Future<Map<String, dynamic>> request({
required String method,
required Map<String, dynamic> params,
Duration timeout = const Duration(seconds: 120),
}) async {
methods.add(method);
if (method == 'acp.capabilities') {
return <String, dynamic>{
'result': <String, dynamic>{
'singleAgent': true,
'multiAgent': true,
'providers': <String>['codex', 'opencode', 'gemini'],
},
};
}
return <String, dynamic>{'result': <String, dynamic>{}};
}
@override
Future<void> dispose() async {
await _notifications.close();
}
}
void main() {
group('ExternalCodeAgentAcpDesktopTransport', () {
test('reads bridge capabilities without pushing an empty provider sync', () async {
final bridge = _FakeGoAcpStdioBridge();
final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge);
final capabilities = await transport.loadExternalAcpCapabilities(
target: AssistantExecutionTarget.singleAgent,
);
expect(bridge.methods, <String>['acp.capabilities']);
expect(
capabilities.providers.map((item) => item.providerId).toList()..sort(),
<String>['codex', 'gemini', 'opencode'],
);
});
test('only syncs when app has explicit provider overrides to send', () async {
final bridge = _FakeGoAcpStdioBridge();
final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge);
await transport.syncExternalProviders(
const <ExternalCodeAgentAcpSyncedProvider>[
ExternalCodeAgentAcpSyncedProvider(
providerId: 'codex',
label: 'Codex',
endpoint: 'https://acp-server.svc.plus/codex/acp/rpc',
authorizationHeader: '',
enabled: true,
),
],
);
expect(bridge.methods, <String>['xworkmate.providers.sync']);
});
});
}