refactor: remove stale skill routing paths
This commit is contained in:
parent
be24832f80
commit
bc2e31f13b
@ -646,15 +646,6 @@ class AppController extends ChangeNotifier {
|
||||
AssistantExecutionTarget target,
|
||||
) => resolvedDefaultModel.trim();
|
||||
|
||||
List<AssistantThreadSkillEntry> assistantImportedSkillsForSession(
|
||||
String sessionKey,
|
||||
) =>
|
||||
assistantThreadRecordsInternal[normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
)]
|
||||
?.importedSkills ??
|
||||
const [];
|
||||
|
||||
void navigateTo(WorkspaceDestination destination) =>
|
||||
AppControllerDesktopNavigation(this).navigateTo(destination);
|
||||
|
||||
@ -692,6 +683,6 @@ class AppController extends ChangeNotifier {
|
||||
this,
|
||||
).refreshMultiAgentMounts(sync: sync);
|
||||
|
||||
double get assistantSkillCount => 0; // Legacy
|
||||
int get currentAssistantSkillCount => 0; // Legacy
|
||||
double get assistantSkillCount => skills.length.toDouble();
|
||||
int get currentAssistantSkillCount => skills.length;
|
||||
}
|
||||
|
||||
@ -46,28 +46,27 @@ extension AppControllerDesktopExternalAcpRouting on AppController {
|
||||
);
|
||||
final thread = assistantThreadRecordsInternal[normalizedSessionKey];
|
||||
const preferredGatewayTarget = kCanonicalGatewayProviderId;
|
||||
final availableSkills =
|
||||
assistantImportedSkillsForSession(normalizedSessionKey)
|
||||
.map((item) {
|
||||
return ExternalCodeAgentAcpAvailableSkill(
|
||||
id: item.key,
|
||||
label: item.label,
|
||||
description: item.description,
|
||||
);
|
||||
})
|
||||
.toList(growable: false);
|
||||
final selectedSkills =
|
||||
assistantSelectedSkillsForSession(normalizedSessionKey)
|
||||
.map((item) {
|
||||
return item.label.trim().isNotEmpty ? item.label : item.key;
|
||||
})
|
||||
.where((item) => item.trim().isNotEmpty)
|
||||
.toList(growable: false);
|
||||
final availableSkills = skills
|
||||
.map((item) {
|
||||
return ExternalCodeAgentAcpAvailableSkill(
|
||||
id: item.skillKey,
|
||||
label: item.name,
|
||||
description: item.description,
|
||||
);
|
||||
})
|
||||
.toList(growable: false);
|
||||
final selectedSkills = assistantSelectedSkillKeysForSession(
|
||||
normalizedSessionKey,
|
||||
);
|
||||
|
||||
final currentTarget = assistantExecutionTargetForSession(
|
||||
normalizedSessionKey,
|
||||
);
|
||||
final resolvedProvider = assistantProviderForSession(normalizedSessionKey);
|
||||
final resolvedExecutionTarget =
|
||||
explicitExecutionTarget?.trim().isNotEmpty == true
|
||||
? explicitExecutionTarget!.trim()
|
||||
: _routingExecutionTargetValueInternal(currentTarget);
|
||||
final resolvedExplicitProviderId =
|
||||
thread?.hasExplicitProviderSelection == true &&
|
||||
!currentTarget.isGateway &&
|
||||
@ -80,33 +79,10 @@ extension AppControllerDesktopExternalAcpRouting on AppController {
|
||||
final resolvedExplicitSkills = thread?.hasExplicitSkillSelection ?? false
|
||||
? selectedSkills
|
||||
: const <String>[];
|
||||
final hasAnyExplicitSelection =
|
||||
resolvedExplicitProviderId.isNotEmpty ||
|
||||
resolvedExplicitModel.trim().isNotEmpty ||
|
||||
resolvedExplicitSkills.isNotEmpty;
|
||||
final resolvedExplicitExecutionTarget =
|
||||
explicitExecutionTarget?.trim().isNotEmpty == true
|
||||
? explicitExecutionTarget!.trim()
|
||||
: (hasAnyExplicitSelection || currentTarget.isGateway)
|
||||
? _routingExecutionTargetValueInternal(currentTarget)
|
||||
: '';
|
||||
final hasExplicitSelection =
|
||||
resolvedExplicitExecutionTarget.isNotEmpty ||
|
||||
resolvedExplicitProviderId.isNotEmpty ||
|
||||
resolvedExplicitModel.trim().isNotEmpty ||
|
||||
resolvedExplicitSkills.isNotEmpty;
|
||||
|
||||
if (!hasExplicitSelection) {
|
||||
return ExternalCodeAgentAcpRoutingConfig.auto(
|
||||
preferredGatewayTarget: preferredGatewayTarget,
|
||||
availableSkills: availableSkills,
|
||||
);
|
||||
}
|
||||
|
||||
return ExternalCodeAgentAcpRoutingConfig(
|
||||
mode: ExternalCodeAgentAcpRoutingMode.explicit,
|
||||
preferredGatewayTarget: preferredGatewayTarget,
|
||||
explicitExecutionTarget: resolvedExplicitExecutionTarget,
|
||||
explicitExecutionTarget: resolvedExecutionTarget,
|
||||
explicitProviderId: resolvedExplicitProviderId,
|
||||
explicitModel: resolvedExplicitModel,
|
||||
explicitSkills: resolvedExplicitSkills,
|
||||
|
||||
@ -214,40 +214,20 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
);
|
||||
}
|
||||
|
||||
int assistantSkillCountForSession(String sessionKey) {
|
||||
return skills.length;
|
||||
}
|
||||
|
||||
int get currentAssistantSkillCount =>
|
||||
assistantSkillCountForSession(currentSessionKey);
|
||||
|
||||
List<String> assistantSelectedSkillKeysForSession(String sessionKey) {
|
||||
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
);
|
||||
final importedKeys = assistantImportedSkillsForSession(
|
||||
normalizedSessionKey,
|
||||
).map((item) => item.key).toSet();
|
||||
final selected =
|
||||
assistantThreadRecordsInternal[normalizedSessionKey]
|
||||
?.selectedSkillKeys ??
|
||||
const <String>[];
|
||||
final availableKeys = skills.map((item) => item.skillKey).toSet();
|
||||
return selected
|
||||
.where((item) => importedKeys.contains(item))
|
||||
.where(availableKeys.contains)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
List<AssistantThreadSkillEntry> assistantSelectedSkillsForSession(
|
||||
String sessionKey,
|
||||
) {
|
||||
final selectedKeys = assistantSelectedSkillKeysForSession(
|
||||
sessionKey,
|
||||
).toSet();
|
||||
return assistantImportedSkillsForSession(
|
||||
sessionKey,
|
||||
).where((item) => selectedKeys.contains(item.key)).toList(growable: false);
|
||||
}
|
||||
|
||||
String assistantModelForSession(String sessionKey) {
|
||||
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
|
||||
@ -372,10 +372,8 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
|
||||
if (normalizedSkillKey.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final importedKeys = assistantImportedSkillsForSession(
|
||||
normalizedSessionKey,
|
||||
).map((item) => item.key).toSet();
|
||||
if (!importedKeys.contains(normalizedSkillKey)) {
|
||||
final availableKeys = skills.map((item) => item.skillKey).toSet();
|
||||
if (!availableKeys.contains(normalizedSkillKey)) {
|
||||
return;
|
||||
}
|
||||
final nextSelected = List<String>.from(
|
||||
|
||||
@ -37,58 +37,13 @@ import 'assistant_page_composer_skill_picker.dart';
|
||||
import 'assistant_page_composer_clipboard.dart';
|
||||
import 'assistant_page_components_core.dart';
|
||||
|
||||
const List<ComposerSkillOptionInternal> fallbackSkillOptionsInternal =
|
||||
<ComposerSkillOptionInternal>[
|
||||
ComposerSkillOptionInternal(
|
||||
key: '1password',
|
||||
label: '1password',
|
||||
description: '安全读取和注入本地凭据。',
|
||||
sourceLabel: 'Local',
|
||||
icon: Icons.auto_awesome_rounded,
|
||||
),
|
||||
ComposerSkillOptionInternal(
|
||||
key: 'xlsx',
|
||||
label: 'xlsx',
|
||||
description: '读取、整理和生成表格文件。',
|
||||
sourceLabel: 'Local',
|
||||
icon: Icons.auto_awesome_rounded,
|
||||
),
|
||||
ComposerSkillOptionInternal(
|
||||
key: 'web-processing',
|
||||
label: '网页处理',
|
||||
description: '打开网页、提取内容并完成网页操作。',
|
||||
sourceLabel: 'Web',
|
||||
icon: Icons.language_rounded,
|
||||
),
|
||||
ComposerSkillOptionInternal(
|
||||
key: 'apple-reminders',
|
||||
label: 'apple-reminders',
|
||||
description: '管理提醒事项和任务提醒。',
|
||||
sourceLabel: 'Local',
|
||||
icon: Icons.auto_awesome_rounded,
|
||||
),
|
||||
ComposerSkillOptionInternal(
|
||||
key: 'blogwatcher',
|
||||
label: 'blogwatcher',
|
||||
description: '跟踪博客更新并生成摘要。',
|
||||
sourceLabel: 'Local',
|
||||
icon: Icons.auto_awesome_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
ComposerSkillOptionInternal skillOptionFromGatewayInternal(
|
||||
GatewaySkillSummary skill,
|
||||
) {
|
||||
final normalizedKey = skill.skillKey.trim().toLowerCase();
|
||||
final normalizedName = skill.name.trim().toLowerCase();
|
||||
final isWebSkill =
|
||||
normalizedKey.contains('browser') ||
|
||||
normalizedKey.contains('open-link') ||
|
||||
normalizedKey.contains('web') ||
|
||||
normalizedName.contains('browser') ||
|
||||
normalizedName.contains('网页');
|
||||
final label = isWebSkill ? '网页处理' : skill.name.trim();
|
||||
final key = isWebSkill ? 'web-processing' : normalizedKey;
|
||||
final key = skill.skillKey.trim().isEmpty
|
||||
? skill.name.trim().toLowerCase()
|
||||
: skill.skillKey.trim();
|
||||
final label = skill.name.trim().isEmpty ? key : skill.name.trim();
|
||||
final sourceLabel = skill.source.trim().isEmpty ? 'Gateway' : skill.source;
|
||||
final description = skill.description.trim().isEmpty
|
||||
? appText('可在当前任务中调用的技能。', 'Skill available in the current task.')
|
||||
@ -99,22 +54,6 @@ ComposerSkillOptionInternal skillOptionFromGatewayInternal(
|
||||
label: label,
|
||||
description: description,
|
||||
sourceLabel: sourceLabel,
|
||||
icon: isWebSkill ? Icons.language_rounded : Icons.auto_awesome_rounded,
|
||||
);
|
||||
}
|
||||
|
||||
ComposerSkillOptionInternal skillOptionFromThreadSkillInternal(
|
||||
AssistantThreadSkillEntry skill,
|
||||
) {
|
||||
return ComposerSkillOptionInternal(
|
||||
key: skill.key,
|
||||
label: skill.label.trim().isEmpty ? skill.key : skill.label.trim(),
|
||||
description: skill.description.trim().isEmpty
|
||||
? appText('已绑定到当前线程的本地技能。', 'Local skill bound to this thread.')
|
||||
: skill.description.trim(),
|
||||
sourceLabel: skill.sourceLabel.trim().isEmpty
|
||||
? skill.sourcePath
|
||||
: skill.sourceLabel.trim(),
|
||||
icon: Icons.auto_awesome_rounded,
|
||||
);
|
||||
}
|
||||
|
||||
@ -262,17 +262,20 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal {
|
||||
addOption(option);
|
||||
}
|
||||
|
||||
for (final option in fallbackSkillOptionsInternal) {
|
||||
addOption(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
List<String> selectedSkillKeysForInternal(AppController controller) {
|
||||
return controller.assistantSelectedSkillKeysForSession(
|
||||
controller.currentSessionKey,
|
||||
);
|
||||
final selected =
|
||||
controller.taskThreadForSessionInternal(controller.currentSessionKey)
|
||||
?.selectedSkillKeys ??
|
||||
const <String>[];
|
||||
final availableKeys = availableSkillOptionsInternal(controller)
|
||||
.map((option) => option.key)
|
||||
.toSet();
|
||||
return selected
|
||||
.where(availableKeys.contains)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
List<String> resolveSelectedSkillLabelsInternal(AppController controller) {
|
||||
|
||||
@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/app/app_controller.dart';
|
||||
import 'package:xworkmate/app/app_controller_desktop_external_acp_routing.dart';
|
||||
import 'package:xworkmate/features/assistant/assistant_page_composer_skill_models.dart';
|
||||
import 'package:xworkmate/runtime/go_task_service_client.dart';
|
||||
import 'package:xworkmate/runtime/runtime_models.dart';
|
||||
import 'package:xworkmate/runtime/secure_config_store.dart';
|
||||
@ -213,6 +214,30 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'bridge skill summaries preserve bridge key and name without remap',
|
||||
() {
|
||||
final option = skillOptionFromGatewayInternal(
|
||||
const GatewaySkillSummary(
|
||||
name: 'Browser Fetch',
|
||||
description: 'Bridge-managed browser skill',
|
||||
source: 'bridge',
|
||||
skillKey: 'browser-fetch',
|
||||
primaryEnv: null,
|
||||
eligible: true,
|
||||
disabled: false,
|
||||
missingBins: <String>[],
|
||||
missingEnv: <String>[],
|
||||
missingConfig: <String>[],
|
||||
),
|
||||
);
|
||||
|
||||
expect(option.key, 'browser-fetch');
|
||||
expect(option.label, 'Browser Fetch');
|
||||
expect(option.description, 'Bridge-managed browser skill');
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'locks the gateway provider catalog to the canonical openclaw contract',
|
||||
() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user