fix: unify bridge gateway readiness
This commit is contained in:
parent
ef2fe35c54
commit
d8be39f5cf
@ -236,7 +236,9 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
var connectionState = currentAssistantConnectionState;
|
||||
if (!connectionState.connected &&
|
||||
isBridgeAcpRuntimeConfiguredInternal() &&
|
||||
!bridgeCapabilitiesRefreshAttemptedInternal) {
|
||||
bridgeCapabilityRefreshNeededForAssistantTargetInternal(
|
||||
currentTarget,
|
||||
)) {
|
||||
try {
|
||||
await refreshAcpCapabilitiesInternal(forceRefresh: true);
|
||||
connectionState = currentAssistantConnectionState;
|
||||
|
||||
@ -173,6 +173,18 @@ AssistantThreadConnectionState resolveGatewayThreadConnectionStateInternal({
|
||||
);
|
||||
}
|
||||
|
||||
bool bridgeCapabilityReadyForExecutionTargetInternal({
|
||||
required AssistantExecutionTarget target,
|
||||
required bool bridgeConfigured,
|
||||
required List<SingleAgentProvider> providers,
|
||||
required List<AssistantExecutionTarget> availableTargets,
|
||||
}) {
|
||||
if (!bridgeConfigured || providers.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return availableTargets.isEmpty || availableTargets.contains(target);
|
||||
}
|
||||
|
||||
extension AppControllerDesktopThreadSessions on AppController {
|
||||
AssistantExecutionTarget resolveAssistantExecutionTargetFromRecordsInternal(
|
||||
TaskThread? primaryRecord, {
|
||||
@ -361,10 +373,12 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
final providers = providerCatalogForExecutionTarget(target);
|
||||
final availableTargets = bridgeAvailableExecutionTargets;
|
||||
final bridgeConfigured = isBridgeAcpRuntimeConfiguredInternal();
|
||||
final bridgeReady =
|
||||
bridgeConfigured &&
|
||||
providers.isNotEmpty &&
|
||||
(availableTargets.isEmpty || availableTargets.contains(target));
|
||||
final bridgeReady = bridgeCapabilityReadyForExecutionTargetInternal(
|
||||
target: target,
|
||||
bridgeConfigured: bridgeConfigured,
|
||||
providers: providers,
|
||||
availableTargets: availableTargets,
|
||||
);
|
||||
final bridgeEndpoint = resolveBridgeAcpEndpointInternal();
|
||||
final bridgeLabel = bridgeEndpoint?.host.trim().isNotEmpty == true
|
||||
? bridgeEndpoint!.host.trim()
|
||||
@ -382,6 +396,26 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
);
|
||||
}
|
||||
|
||||
bool bridgeCapabilityReadyForAssistantTargetInternal(
|
||||
AssistantExecutionTarget target,
|
||||
) {
|
||||
return bridgeCapabilityReadyForExecutionTargetInternal(
|
||||
target: target,
|
||||
bridgeConfigured: isBridgeAcpRuntimeConfiguredInternal(),
|
||||
providers: providerCatalogForExecutionTarget(target),
|
||||
availableTargets: bridgeAvailableExecutionTargets,
|
||||
);
|
||||
}
|
||||
|
||||
bool bridgeCapabilityRefreshNeededForAssistantTargetInternal(
|
||||
AssistantExecutionTarget target,
|
||||
) {
|
||||
if (!isBridgeAcpRuntimeConfiguredInternal()) {
|
||||
return false;
|
||||
}
|
||||
return !bridgeCapabilityReadyForAssistantTargetInternal(target);
|
||||
}
|
||||
|
||||
String get assistantConnectionStatusLabel =>
|
||||
currentAssistantConnectionState.primaryLabel;
|
||||
String get assistantConnectionTargetLabel =>
|
||||
|
||||
@ -28,6 +28,33 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('requires the target capability contract before reporting ready', () {
|
||||
expect(
|
||||
bridgeCapabilityReadyForExecutionTargetInternal(
|
||||
target: AssistantExecutionTarget.gateway,
|
||||
bridgeConfigured: true,
|
||||
providers: const <SingleAgentProvider>[SingleAgentProvider.openclaw],
|
||||
availableTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
],
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
|
||||
expect(
|
||||
bridgeCapabilityReadyForExecutionTargetInternal(
|
||||
target: AssistantExecutionTarget.gateway,
|
||||
bridgeConfigured: true,
|
||||
providers: const <SingleAgentProvider>[SingleAgentProvider.openclaw],
|
||||
availableTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('maps blocked bridge authorization into the token-missing state', () {
|
||||
final state = resolveGatewayThreadConnectionStateInternal(
|
||||
target: AssistantExecutionTarget.gateway,
|
||||
|
||||
@ -54,6 +54,74 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'uses the same gateway capability readiness for status and send guard',
|
||||
() async {
|
||||
final controller = await _isolatedController(
|
||||
initialBridgeProviderCatalog: const <SingleAgentProvider>[
|
||||
SingleAgentProvider.codex,
|
||||
],
|
||||
initialGatewayProviderCatalog: const <SingleAgentProvider>[
|
||||
SingleAgentProvider.openclaw,
|
||||
],
|
||||
initialAvailableExecutionTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
environmentOverride: const <String, String>{
|
||||
'BRIDGE_AUTH_TOKEN': 'bridge-token',
|
||||
},
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
await controller.sessionsController.switchSession('session-1');
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.gateway,
|
||||
);
|
||||
|
||||
final state = controller.currentAssistantConnectionState;
|
||||
expect(state.connected, isTrue);
|
||||
expect(
|
||||
controller.bridgeCapabilityRefreshNeededForAssistantTargetInternal(
|
||||
AssistantExecutionTarget.gateway,
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'refreshes gateway capabilities when previous discovery missed openclaw',
|
||||
() async {
|
||||
final controller = await _isolatedController(
|
||||
initialBridgeProviderCatalog: const <SingleAgentProvider>[
|
||||
SingleAgentProvider.codex,
|
||||
],
|
||||
initialAvailableExecutionTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
environmentOverride: const <String, String>{
|
||||
'BRIDGE_AUTH_TOKEN': 'bridge-token',
|
||||
},
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
await controller.sessionsController.switchSession('session-1');
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.gateway,
|
||||
);
|
||||
|
||||
expect(controller.currentAssistantConnectionState.connected, isFalse);
|
||||
expect(
|
||||
controller.bridgeCapabilityRefreshNeededForAssistantTargetInternal(
|
||||
AssistantExecutionTarget.gateway,
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('keeps signed-out generic runtime failures disconnected', () async {
|
||||
final controller = await _isolatedController();
|
||||
addTearDown(controller.dispose);
|
||||
@ -221,6 +289,7 @@ Future<AppController> _isolatedController({
|
||||
List<SingleAgentProvider>? initialBridgeProviderCatalog,
|
||||
List<SingleAgentProvider>? initialGatewayProviderCatalog,
|
||||
List<AssistantExecutionTarget>? initialAvailableExecutionTargets,
|
||||
Map<String, String> environmentOverride = const <String, String>{},
|
||||
}) async {
|
||||
final storeRoot = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-assistant-connection-state-',
|
||||
@ -242,7 +311,7 @@ Future<AppController> _isolatedController({
|
||||
);
|
||||
await store.initialize();
|
||||
return AppController(
|
||||
environmentOverride: const <String, String>{},
|
||||
environmentOverride: environmentOverride,
|
||||
store: store,
|
||||
initialBridgeProviderCatalog: initialBridgeProviderCatalog,
|
||||
initialGatewayProviderCatalog: initialGatewayProviderCatalog,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user