Clean up bridge provider dialog copy
This commit is contained in:
parent
ee452fc7ea
commit
9e407bf090
@ -218,6 +218,26 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
if (lowered.contains('gateway not connected') ||
|
||||
lowered.contains('code: offline') ||
|
||||
lowered.contains('offlin') && lowered.contains('gateway')) {
|
||||
if (target == AssistantExecutionTarget.singleAgent) {
|
||||
final selection = singleAgentProviderForSession(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
);
|
||||
final provider =
|
||||
resolvedSingleAgentProviderInternal(selection) ?? selection;
|
||||
final providerLabel = provider == SingleAgentProvider.auto
|
||||
? appText('Bridge Provider', 'Bridge Provider')
|
||||
: provider.label;
|
||||
final address = _extractGatewayAddressFromErrorInternal(raw);
|
||||
return address.isEmpty
|
||||
? appText(
|
||||
'当前线程的 Bridge Provider($providerLabel)未连接。请先在设置里连接并同步后再重试。',
|
||||
'The Bridge Provider for this thread ($providerLabel) is not connected. Connect and sync it from Settings, then try again.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程的 Bridge Provider($providerLabel)未连接:$address。请先在设置里连接并同步后再重试。',
|
||||
'The Bridge Provider for this thread ($providerLabel) is not connected: $address. Connect and sync it from Settings, then try again.',
|
||||
);
|
||||
}
|
||||
final profile = gatewayProfileForAssistantExecutionTargetInternal(target);
|
||||
final address = gatewayAddressLabelInternal(profile);
|
||||
final targetLabel = target.label;
|
||||
@ -234,6 +254,13 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
return raw;
|
||||
}
|
||||
|
||||
String _extractGatewayAddressFromErrorInternal(String raw) {
|
||||
final match = RegExp(
|
||||
r'((?:\d{1,3}\.){3}\d{1,3}:\d+|localhost:\d+|[a-zA-Z0-9.-]+:\d+)',
|
||||
).firstMatch(raw);
|
||||
return match?.group(1)?.trim() ?? '';
|
||||
}
|
||||
|
||||
String formatAiGatewayHttpErrorInternal(int statusCode, String detail) {
|
||||
final base = switch (statusCode) {
|
||||
400 => appText(
|
||||
|
||||
@ -83,12 +83,12 @@ String singleAgentUnavailableLabelDesktopInternal(
|
||||
)) {
|
||||
return detail.isEmpty
|
||||
? appText(
|
||||
'当前线程固定为 ${selection.label},但它在这台设备上不可用。检测到其他外部 Agent ACP 端点时不会自动改线,请切到可用的 ACP Server。',
|
||||
'This thread is pinned to ${selection.label}, but it is unavailable on this device. XWorkmate will not reroute to another external Agent ACP endpoint automatically. Switch to an available ACP Server.',
|
||||
'当前线程固定为 ${selection.label},但它在这台设备上不可用。检测到其他 Bridge Provider 时不会自动改线,请手动切到可用 Provider。',
|
||||
'This thread is pinned to ${selection.label}, but it is unavailable on this device. XWorkmate will not reroute to another bridge provider automatically. Switch to an available provider manually.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程固定为 ${selection.label}:$detail 检测到其他外部 Agent ACP 端点时不会自动改线,请切到可用的 ACP Server。',
|
||||
'This thread is pinned to ${selection.label}: $detail XWorkmate will not reroute to another external Agent ACP endpoint automatically. Switch to an available ACP Server.',
|
||||
'当前线程固定为 ${selection.label}:$detail 检测到其他 Bridge Provider 时不会自动改线,请手动切到可用 Provider。',
|
||||
'This thread is pinned to ${selection.label}: $detail XWorkmate will not reroute to another bridge provider automatically. Switch to an available provider manually.',
|
||||
);
|
||||
}
|
||||
if (controller.singleAgentNeedsAiGatewayConfigurationForSession(
|
||||
@ -96,21 +96,21 @@ String singleAgentUnavailableLabelDesktopInternal(
|
||||
)) {
|
||||
return detail.isEmpty
|
||||
? appText(
|
||||
'当前没有可用的外部 Agent ACP 端点。请先配置外部 Agent 连接。',
|
||||
'No external Agent ACP endpoint is available. Configure an external Agent connection first.',
|
||||
'当前没有可用的 Bridge Provider。请先在设置里配置并同步外部 Agent 连接。',
|
||||
'No bridge provider is available. Configure and sync an external agent connection in Settings first.',
|
||||
)
|
||||
: appText(
|
||||
'$detail 当前没有可用的外部 Agent ACP 端点。请先配置外部 Agent 连接。',
|
||||
'$detail No external Agent ACP endpoint is available. Configure an external Agent connection first.',
|
||||
'$detail 当前没有可用的 Bridge Provider。请先在设置里配置并同步外部 Agent 连接。',
|
||||
'$detail No bridge provider is available. Configure and sync an external agent connection in Settings first.',
|
||||
);
|
||||
}
|
||||
return detail.isEmpty
|
||||
? appText(
|
||||
'当前线程的外部 Agent ACP 连接尚未就绪。',
|
||||
'The external Agent ACP connection for this thread is not ready yet.',
|
||||
'当前线程的 Bridge Provider 尚未就绪。',
|
||||
'The bridge provider for this thread is not ready yet.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程的外部 Agent ACP 连接尚未就绪:$detail',
|
||||
'The external Agent ACP connection for this thread is not ready yet: $detail',
|
||||
'当前线程的 Bridge Provider 尚未就绪:$detail',
|
||||
'The bridge provider for this thread is not ready yet: $detail',
|
||||
);
|
||||
}
|
||||
|
||||
@ -351,7 +351,7 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
);
|
||||
final target = assistantExecutionTargetForSession(normalizedSessionKey);
|
||||
if (target == AssistantExecutionTarget.singleAgent) {
|
||||
final primaryLabel = appText('ACP Server Local', 'ACP Server Local');
|
||||
final primaryLabel = appText('Bridge', 'Bridge');
|
||||
final provider = singleAgentProviderForSession(normalizedSessionKey);
|
||||
final resolvedProvider = singleAgentResolvedProviderForSession(
|
||||
normalizedSessionKey,
|
||||
@ -362,19 +362,19 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
? joinConnectionPartsInternal(<String>[resolvedProvider.label, model])
|
||||
: singleAgentShouldSuggestAcpSwitchForSession(normalizedSessionKey)
|
||||
? appText(
|
||||
'${provider.label} 不可用,请切到可用的 ACP Server。',
|
||||
'${provider.label} is unavailable. Switch to an available ACP Server.',
|
||||
'${provider.label} 当前不可用,请改成 Bridge 当前可用的 Provider。',
|
||||
'${provider.label} is unavailable. Switch to a provider currently advertised by the bridge.',
|
||||
)
|
||||
: singleAgentNeedsAiGatewayConfigurationForSession(
|
||||
normalizedSessionKey,
|
||||
)
|
||||
? appText(
|
||||
'没有可用的外部 Agent ACP 端点,请先配置可用的 ACP Server。',
|
||||
'No external Agent ACP endpoint is available. Configure an ACP Server first.',
|
||||
'当前没有可用的 Bridge Provider。请先在设置里配置并同步可用连接。',
|
||||
'No bridge provider is currently available. Configure and sync an available upstream connection in Settings first.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程的外部 Agent ACP 连接尚未就绪。',
|
||||
'The external Agent ACP connection for this thread is not ready yet.',
|
||||
'当前线程的 Bridge Provider 尚未就绪。',
|
||||
'The bridge provider for this thread is not ready yet.',
|
||||
);
|
||||
return AssistantThreadConnectionState(
|
||||
executionTarget: target,
|
||||
|
||||
@ -511,10 +511,16 @@ class AssistantEmptyStateInternal extends StatelessWidget {
|
||||
final reconnectAvailable = controller.canQuickConnectGateway;
|
||||
final title = singleAgent
|
||||
? connected
|
||||
? appText('开始 ACP Server 任务', 'Start an ACP Server task')
|
||||
? appText('开始智能体任务', 'Start an agent task')
|
||||
: singleAgentNeedsAiGateway
|
||||
? appText('先配置 ACP Server', 'Configure ACP Server first')
|
||||
: appText('先准备 ACP Server', 'Prepare the ACP Server first')
|
||||
? appText(
|
||||
'先配置 Bridge Provider',
|
||||
'Configure a bridge provider first',
|
||||
)
|
||||
: appText(
|
||||
'先准备 Bridge Provider',
|
||||
'Prepare the bridge provider first',
|
||||
)
|
||||
: connected
|
||||
? appText('开始对话或运行任务', 'Start a chat or run a task')
|
||||
: connectionState.status == RuntimeConnectionStatus.error
|
||||
@ -523,22 +529,22 @@ class AssistantEmptyStateInternal extends StatelessWidget {
|
||||
final description = singleAgent
|
||||
? connected
|
||||
? appText(
|
||||
'当前线程通过 ACP Server 处理任务,不会建立 OpenClaw Gateway 会话。',
|
||||
'This thread runs through the ACP Server path and does not open an OpenClaw Gateway session.',
|
||||
'当前线程会通过 Bridge 当前广告的 Provider 处理任务,不会建立 OpenClaw Gateway 会话。',
|
||||
'This thread runs through the provider currently advertised by the bridge and does not open an OpenClaw Gateway session.',
|
||||
)
|
||||
: singleAgentSuggestsAcpSwitch
|
||||
? appText(
|
||||
'当前线程固定为 $providerLabel,但它在这台设备上不可用。请改成可用的 ACP Server。',
|
||||
'This thread is pinned to $providerLabel, but it is unavailable on this device. Switch to an available ACP Server.',
|
||||
'当前线程固定为 $providerLabel,但它在这台设备上不可用。请改成 Bridge 当前可用的 Provider。',
|
||||
'This thread is pinned to $providerLabel, but it is unavailable on this device. Switch to a provider currently advertised by the bridge.',
|
||||
)
|
||||
: singleAgentNeedsAiGateway
|
||||
? appText(
|
||||
'请先在 设置 -> 集成 中配置可用的外部 Agent ACP 端点,然后以 ACP Server 模式继续当前任务。',
|
||||
'Configure an external Agent ACP endpoint in Settings -> Integrations, then continue this task in ACP Server mode.',
|
||||
'请先在 设置 -> 集成 中配置并同步可用的外部 Agent 连接,然后再继续当前任务。',
|
||||
'Configure and sync an available external agent connection in Settings -> Integrations before continuing this task.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程的外部 Agent ACP 连接尚未就绪。请先配置 $providerLabel 对应端点。',
|
||||
'The external Agent ACP connection for this thread is not ready yet. Configure the endpoint for $providerLabel first.',
|
||||
'当前线程的 Bridge Provider 尚未就绪。请先检查 $providerLabel 对应连接。',
|
||||
'The bridge provider for this thread is not ready yet. Check the connection mapped to $providerLabel first.',
|
||||
)
|
||||
: connected
|
||||
? appText(
|
||||
|
||||
@ -37,5 +37,5 @@ import 'assistant_page_composer_skill_models.dart';
|
||||
import 'assistant_page_composer_skill_picker.dart';
|
||||
import 'assistant_page_composer_clipboard.dart';
|
||||
|
||||
// Lightweight compatibility anchor. The Composer and assistant widgets now
|
||||
// live in focused part files under the same library.
|
||||
// Composer and assistant widgets live in focused part files under the same
|
||||
// library to keep UI ownership local to the assistant feature.
|
||||
|
||||
@ -474,7 +474,7 @@ class ComposerBarStateInternal extends State<ComposerBarInternal> {
|
||||
if (singleAgent) ...[
|
||||
PopupMenuButton<SingleAgentProvider>(
|
||||
key: const Key('assistant-single-agent-provider-button'),
|
||||
tooltip: appText('单机智能体执行器', 'Single Agent Provider'),
|
||||
tooltip: appText('Bridge Provider', 'Bridge Provider'),
|
||||
onSelected: (value) {
|
||||
unawaited(controller.setSingleAgentProvider(value));
|
||||
},
|
||||
|
||||
@ -45,8 +45,8 @@ String executionTargetTooltipInternal(AssistantExecutionTarget target) =>
|
||||
|
||||
String singleAgentProviderTooltipInternal(SingleAgentProvider provider) =>
|
||||
appText(
|
||||
'单机智能体执行器: ${provider.label}',
|
||||
'Single-agent provider: ${provider.label}',
|
||||
'Bridge Provider: ${provider.label}',
|
||||
'Bridge Provider: ${provider.label}',
|
||||
);
|
||||
|
||||
String modelTooltipInternal(String modelLabel) =>
|
||||
|
||||
@ -116,8 +116,8 @@ class SkillsFocusPreviewInternal extends StatelessWidget {
|
||||
message: typedController.isSingleAgentMode
|
||||
? (typedController.currentSingleAgentNeedsAiGatewayConfiguration
|
||||
? appText(
|
||||
'当前没有可用的外部 Agent ACP 端点,请先配置 ACP Server。',
|
||||
'No external Agent ACP endpoint is available. Configure an ACP server first.',
|
||||
'当前没有可用的 Bridge Provider,请先在设置里配置并同步连接。',
|
||||
'No bridge provider is available. Configure and sync a connection in Settings first.',
|
||||
)
|
||||
: appText(
|
||||
'当前线程还没有已加载技能。切换 provider 后会读取该线程自己的 skills 列表。',
|
||||
|
||||
@ -620,7 +620,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
controller.chatMessages.any(
|
||||
(message) =>
|
||||
message.role == 'assistant' &&
|
||||
message.text.contains('当前没有可用的外部 Agent ACP 端点'),
|
||||
message.text.contains('当前没有可用的 Bridge Provider'),
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
@ -684,7 +684,7 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
controller.chatMessages.any(
|
||||
(message) =>
|
||||
message.role == 'assistant' &&
|
||||
message.text.contains('当前没有可用的外部 Agent ACP 端点'),
|
||||
message.text.contains('当前没有可用的 Bridge Provider'),
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
|
||||
@ -35,8 +35,11 @@ void main() {
|
||||
controller.currentAssistantExecutionTarget,
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
expect(controller.currentAssistantConnectionState.isSingleAgent, isTrue);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(
|
||||
controller.currentAssistantConnectionState.isSingleAgent,
|
||||
isTrue,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
},
|
||||
);
|
||||
|
||||
@ -63,7 +66,10 @@ void main() {
|
||||
controller.currentAssistantExecutionTarget,
|
||||
AssistantExecutionTarget.local,
|
||||
);
|
||||
expect(controller.currentAssistantConnectionState.isSingleAgent, isFalse);
|
||||
expect(
|
||||
controller.currentAssistantConnectionState.isSingleAgent,
|
||||
isFalse,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, '已连接');
|
||||
expect(controller.assistantConnectionTargetLabel, '127.0.0.1:4317');
|
||||
},
|
||||
@ -92,7 +98,10 @@ void main() {
|
||||
controller.currentAssistantExecutionTarget,
|
||||
AssistantExecutionTarget.remote,
|
||||
);
|
||||
expect(controller.currentAssistantConnectionState.isSingleAgent, isFalse);
|
||||
expect(
|
||||
controller.currentAssistantConnectionState.isSingleAgent,
|
||||
isFalse,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, '已连接');
|
||||
expect(
|
||||
controller.assistantConnectionTargetLabel,
|
||||
@ -100,6 +109,32 @@ void main() {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'core flow 04 formats single-agent offline errors with bridge provider wording',
|
||||
() async {
|
||||
final controller = await createCoreFlowControllerInternal();
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
final sessionKey = buildDraftSessionKeyInternal();
|
||||
controller.initializeAssistantThreadContext(
|
||||
sessionKey,
|
||||
title: '新对话',
|
||||
executionTarget: AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
|
||||
await controller.switchSession(sessionKey);
|
||||
await controller.setSingleAgentProvider(SingleAgentProvider.opencode);
|
||||
|
||||
expect(
|
||||
controller.gatewayExecutionErrorLabelInternal(
|
||||
'gateway not connected: 127.0.0.1:18789',
|
||||
target: AssistantExecutionTarget.singleAgent,
|
||||
),
|
||||
'当前线程的 Bridge Provider(OpenCode)未连接:127.0.0.1:18789。请先在设置里连接并同步后再重试。',
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -144,10 +144,10 @@ void registerExecutionTargetSwitchConnectionTests() {
|
||||
RuntimeConnectionMode.remote,
|
||||
);
|
||||
expect(gateway.disconnectCount, 1);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
expect(
|
||||
controller.assistantConnectionTargetLabel,
|
||||
'没有可用的外部 Agent ACP 端点,请先配置可用的 ACP Server。',
|
||||
'当前没有可用的 Bridge Provider。请先在设置里配置并同步可用连接。',
|
||||
);
|
||||
expect(
|
||||
gateway.connectedProfiles,
|
||||
@ -337,7 +337,7 @@ void registerExecutionTargetSwitchConnectionTests() {
|
||||
controller.settings.assistantExecutionTarget,
|
||||
AssistantExecutionTarget.remote,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
},
|
||||
);
|
||||
|
||||
@ -479,7 +479,7 @@ void registerExecutionTargetSwitchConnectionTests() {
|
||||
controller.assistantExecutionTarget,
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
expect(completed, isFalse);
|
||||
} finally {
|
||||
if (!disconnectGate.isCompleted) {
|
||||
@ -497,7 +497,7 @@ void registerExecutionTargetSwitchConnectionTests() {
|
||||
controller.assistantExecutionTarget,
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@ -98,7 +98,7 @@ void registerExecutionTargetSwitchThreadTests() {
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
expect(gateway.disconnectCount, 1);
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
expect(
|
||||
controller.settings.assistantExecutionTarget,
|
||||
AssistantExecutionTarget.local,
|
||||
@ -195,10 +195,10 @@ void registerExecutionTargetSwitchThreadTests() {
|
||||
);
|
||||
await controller.switchSession('main');
|
||||
|
||||
expect(controller.assistantConnectionStatusLabel, 'ACP Server Local');
|
||||
expect(controller.assistantConnectionStatusLabel, 'Bridge');
|
||||
expect(
|
||||
controller.assistantConnectionTargetLabel,
|
||||
'没有可用的外部 Agent ACP 端点,请先配置可用的 ACP Server。',
|
||||
'当前没有可用的 Bridge Provider。请先在设置里配置并同步可用连接。',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user