fix: sync custom single-agent providers before execution
This commit is contained in:
parent
10015e56f8
commit
ffbd0f1c5f
@ -86,11 +86,12 @@ extension AppControllerDesktopSingleAgent on AppController {
|
||||
try {
|
||||
final routing = buildExternalAcpRoutingForSessionInternal(sessionKey);
|
||||
final selection = singleAgentProviderForSession(sessionKey);
|
||||
await syncExternalAcpProvidersInternal();
|
||||
final capabilities = await goTaskServiceClientInternal
|
||||
.loadExternalAcpCapabilities(
|
||||
target: AssistantExecutionTarget.singleAgent,
|
||||
forceRefresh: true,
|
||||
);
|
||||
target: AssistantExecutionTarget.singleAgent,
|
||||
forceRefresh: true,
|
||||
);
|
||||
final availableProviders = configuredSingleAgentProviders
|
||||
.where(capabilities.providers.contains)
|
||||
.toList(growable: false);
|
||||
@ -222,12 +223,15 @@ extension AppControllerDesktopSingleAgent on AppController {
|
||||
final resolvedWorkingDirectory = result.resolvedWorkingDirectory.trim();
|
||||
if (resolvedWorkspaceKind != null &&
|
||||
resolvedWorkingDirectory.isNotEmpty) {
|
||||
final existingThread = requireTaskThreadForSessionInternal(sessionKey);
|
||||
final existingThread = requireTaskThreadForSessionInternal(
|
||||
sessionKey,
|
||||
);
|
||||
upsertTaskThreadInternal(
|
||||
sessionKey,
|
||||
workspaceBinding: WorkspaceBinding(
|
||||
workspaceId: existingThread.workspaceBinding.workspaceId,
|
||||
workspaceKind: resolvedWorkspaceKind == WorkspaceRefKind.remotePath
|
||||
workspaceKind:
|
||||
resolvedWorkspaceKind == WorkspaceRefKind.remotePath
|
||||
? WorkspaceKind.remoteFs
|
||||
: WorkspaceKind.localFs,
|
||||
workspacePath: resolvedWorkingDirectory,
|
||||
|
||||
@ -15,7 +15,8 @@ typedef ExternalCodeAgentAcpProcessStarter =
|
||||
String? workingDirectory,
|
||||
});
|
||||
|
||||
class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransport {
|
||||
class ExternalCodeAgentAcpDesktopTransport
|
||||
implements ExternalCodeAgentAcpTransport {
|
||||
ExternalCodeAgentAcpDesktopTransport({
|
||||
required GatewayAcpClient acpClient,
|
||||
required Uri? Function(AssistantExecutionTarget target) endpointResolver,
|
||||
@ -32,7 +33,7 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
|
||||
arguments,
|
||||
environment: environment,
|
||||
workingDirectory: workingDirectory,
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
final GatewayAcpClient _acpClient;
|
||||
@ -43,22 +44,21 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
|
||||
Process? _localProcess;
|
||||
Uri? _localEndpoint;
|
||||
Future<Uri?>? _localEndpointFuture;
|
||||
List<ExternalCodeAgentAcpSyncedProvider> _syncedProviders =
|
||||
const <ExternalCodeAgentAcpSyncedProvider>[];
|
||||
|
||||
@override
|
||||
Future<void> syncExternalProviders(
|
||||
List<ExternalCodeAgentAcpSyncedProvider> providers,
|
||||
) async {
|
||||
_syncedProviders = List<ExternalCodeAgentAcpSyncedProvider>.unmodifiable(
|
||||
providers,
|
||||
);
|
||||
final endpoint = await _ensureLocalEndpoint();
|
||||
if (endpoint == null) {
|
||||
return;
|
||||
}
|
||||
await _acpClient.request(
|
||||
method: 'xworkmate.providers.sync',
|
||||
params: <String, dynamic>{
|
||||
'providers': providers.map((item) => item.toJson()).toList(growable: false),
|
||||
},
|
||||
endpointOverride: endpoint,
|
||||
);
|
||||
await _syncProvidersToEndpoint(endpoint, _syncedProviders);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -70,6 +70,10 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
|
||||
if (endpoint == null) {
|
||||
return const ExternalCodeAgentAcpCapabilities.empty();
|
||||
}
|
||||
if (target == AssistantExecutionTarget.singleAgent ||
|
||||
target == AssistantExecutionTarget.auto) {
|
||||
await _syncProvidersToEndpoint(endpoint, _syncedProviders);
|
||||
}
|
||||
final capabilities = await _acpClient.loadCapabilities(
|
||||
forceRefresh: forceRefresh,
|
||||
endpointOverride: endpoint,
|
||||
@ -94,6 +98,10 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
|
||||
code: 'EXTERNAL_ACP_ENDPOINT_MISSING',
|
||||
);
|
||||
}
|
||||
if (request.target == AssistantExecutionTarget.singleAgent ||
|
||||
request.target == AssistantExecutionTarget.auto) {
|
||||
await _syncProvidersToEndpoint(endpoint, _syncedProviders);
|
||||
}
|
||||
var streamedText = '';
|
||||
String? completedMessage;
|
||||
final response = await _acpClient.request(
|
||||
@ -251,4 +259,22 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
|
||||
await dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _syncProvidersToEndpoint(
|
||||
Uri endpoint,
|
||||
List<ExternalCodeAgentAcpSyncedProvider> providers,
|
||||
) async {
|
||||
if (providers.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await _acpClient.request(
|
||||
method: 'xworkmate.providers.sync',
|
||||
params: <String, dynamic>{
|
||||
'providers': providers
|
||||
.map((item) => item.toJson())
|
||||
.toList(growable: false),
|
||||
},
|
||||
endpointOverride: endpoint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,13 +132,21 @@ class FakeGoTaskServiceClientInternal implements GoTaskServiceClient {
|
||||
int capabilitiesCalls = 0;
|
||||
int executeCalls = 0;
|
||||
int cancelCalls = 0;
|
||||
int syncProvidersCalls = 0;
|
||||
GoTaskServiceRequest? lastRequest;
|
||||
final List<GoTaskServiceRequest> requests = <GoTaskServiceRequest>[];
|
||||
final List<List<ExternalCodeAgentAcpSyncedProvider>> syncedProvidersHistory =
|
||||
<List<ExternalCodeAgentAcpSyncedProvider>>[];
|
||||
|
||||
@override
|
||||
Future<void> syncExternalProviders(
|
||||
List<ExternalCodeAgentAcpSyncedProvider> providers,
|
||||
) async {}
|
||||
) async {
|
||||
syncProvidersCalls += 1;
|
||||
syncedProvidersHistory.add(
|
||||
List<ExternalCodeAgentAcpSyncedProvider>.unmodifiable(providers),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ExternalCodeAgentAcpCapabilities> loadExternalAcpCapabilities({
|
||||
|
||||
@ -41,7 +41,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: 'codex-sonnet',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -100,6 +100,93 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'AppController syncs custom single-agent providers before execution',
|
||||
() async {
|
||||
final tempDirectory = await createTempDirectoryInternal(
|
||||
'xworkmate-single-agent-custom-provider-',
|
||||
);
|
||||
final store = createStoreFromTempDirectoryInternal(tempDirectory);
|
||||
const customProvider = SingleAgentProvider(
|
||||
providerId: 'custom-agent-1',
|
||||
label: 'Codex',
|
||||
badge: 'C',
|
||||
);
|
||||
final client = FakeGoTaskServiceClientInternal(
|
||||
capabilities: ExternalCodeAgentAcpCapabilities(
|
||||
singleAgent: true,
|
||||
multiAgent: false,
|
||||
providers: <SingleAgentProvider>{customProvider},
|
||||
raw: <String, dynamic>{},
|
||||
),
|
||||
result: const GoTaskServiceResult(
|
||||
success: true,
|
||||
message: 'CUSTOM_PROVIDER_REPLY',
|
||||
turnId: 'turn-custom',
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
store: store,
|
||||
availableSingleAgentProvidersOverride: const <SingleAgentProvider>[
|
||||
customProvider,
|
||||
],
|
||||
runtimeCoordinator: RuntimeCoordinator(
|
||||
gateway: FakeGatewayRuntimeInternal(store: store),
|
||||
codex: FakeCodexRuntimeInternal(),
|
||||
),
|
||||
goTaskServiceClient: client,
|
||||
);
|
||||
await controller.saveSettings(
|
||||
controller.settings.copyWith(
|
||||
externalAcpEndpoints: normalizeExternalAcpEndpoints(
|
||||
profiles: const <ExternalAcpEndpointProfile>[
|
||||
ExternalAcpEndpointProfile(
|
||||
providerKey: 'custom-agent-1',
|
||||
label: 'Codex',
|
||||
badge: 'C',
|
||||
endpoint: 'ws://127.0.0.1:9101/acp',
|
||||
authRef: '',
|
||||
enabled: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
multiAgent: controller.settings.multiAgent.copyWith(
|
||||
autoSync: false,
|
||||
),
|
||||
),
|
||||
refreshAfterSave: false,
|
||||
);
|
||||
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
await controller.setSingleAgentProvider(customProvider);
|
||||
|
||||
await controller.sendChatMessage(
|
||||
'请输出 CUSTOM_PROVIDER_REPLY',
|
||||
thinking: 'low',
|
||||
);
|
||||
|
||||
expect(client.syncProvidersCalls, greaterThanOrEqualTo(1));
|
||||
expect(client.executeCalls, 1);
|
||||
expect(client.lastRequest?.provider, customProvider);
|
||||
expect(
|
||||
client.syncedProvidersHistory.any(
|
||||
(batch) => batch.any(
|
||||
(provider) =>
|
||||
provider.providerId == 'custom-agent-1' &&
|
||||
provider.endpoint == 'ws://127.0.0.1:9101/acp',
|
||||
),
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'AppController treats Auto as ready before the first routing resolution when any route is available',
|
||||
() async {
|
||||
@ -173,7 +260,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: 'codex-sonnet',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -220,7 +307,9 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
final store = createStoreFromTempDirectoryInternal(tempDirectory);
|
||||
await store.initialize();
|
||||
await store.saveSettingsSnapshot(
|
||||
SettingsSnapshot.defaults().copyWith(workspacePath: tempDirectory.path),
|
||||
SettingsSnapshot.defaults().copyWith(
|
||||
workspacePath: tempDirectory.path,
|
||||
),
|
||||
);
|
||||
final client = FakeGoTaskServiceClientInternal(
|
||||
capabilities: ExternalCodeAgentAcpCapabilities(
|
||||
@ -236,7 +325,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: 'codex-sonnet',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -311,7 +400,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: 'codex-sonnet',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -607,7 +696,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -667,7 +756,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -748,7 +837,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
@ -839,7 +928,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
final controller = await createAppControllerInternal(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user