diff --git a/lib/app/app_controller_desktop.dart b/lib/app/app_controller_desktop.dart index 278435c4..6a4123f7 100644 --- a/lib/app/app_controller_desktop.dart +++ b/lib/app/app_controller_desktop.dart @@ -40,19 +40,13 @@ class _SingleAgentSkillScanRoot { required this.path, required this.source, required this.scope, - this.bookmark = '', }); final String path; final String source; final String scope; - final String bookmark; } -const String _singleAgentLocalSkillsCacheRelativePath = - 'cache/single-agent-local-skills.json'; -const int _singleAgentLocalSkillsCacheSchemaVersion = 2; - class AppController extends ChangeNotifier { static const List<_SingleAgentSkillScanRoot> _defaultSingleAgentGlobalSkillScanRoots = <_SingleAgentSkillScanRoot>[ @@ -77,32 +71,12 @@ class AppController extends ChangeNotifier { scope: 'user', ), ]; - static const List<_SingleAgentSkillScanRoot> - _defaultSingleAgentWorkspaceSkillScanRoots = <_SingleAgentSkillScanRoot>[ - _SingleAgentSkillScanRoot( - path: '.agents/skills', - source: 'agents', - scope: 'workspace', - ), - _SingleAgentSkillScanRoot( - path: '.codex/skills', - source: 'codex', - scope: 'workspace', - ), - _SingleAgentSkillScanRoot( - path: '.workbuddy/skills', - source: 'workbuddy', - scope: 'workspace', - ), - ]; - AppController({ SecureConfigStore? store, RuntimeCoordinator? runtimeCoordinator, DesktopPlatformService? desktopPlatformService, UiFeatureManifest? uiFeatureManifest, SkillDirectoryAccessService? skillDirectoryAccessService, - List? singleAgentLocalSkillScanRoots, List? availableSingleAgentProvidersOverride, ArisBundleRepository? arisBundleRepository, SingleAgentRunner? singleAgentRunner, @@ -147,8 +121,6 @@ class AppController extends ChangeNotifier { desktopPlatformService ?? createDesktopPlatformService(); _skillDirectoryAccessService = skillDirectoryAccessService ?? createSkillDirectoryAccessService(); - _singleAgentLocalSkillScanRootOverrides = singleAgentLocalSkillScanRoots - ?.toList(growable: false); _gatewayAcpClient = GatewayAcpClient( endpointResolver: _resolveGatewayAcpEndpoint, ); @@ -192,7 +164,6 @@ class AppController extends ChangeNotifier { late final DerivedTasksController _tasksController; late final DesktopPlatformService _desktopPlatformService; late final SkillDirectoryAccessService _skillDirectoryAccessService; - late final List? _singleAgentLocalSkillScanRootOverrides; late final GatewayAcpClient _gatewayAcpClient; late final DirectSingleAgentAppServerClient _singleAgentAppServerClient; late final List? _availableSingleAgentProvidersOverride; @@ -217,9 +188,6 @@ class AppController extends ChangeNotifier { {}; final DesktopThreadArtifactService _threadArtifactService = DesktopThreadArtifactService(); - List _singleAgentSharedImportedSkills = - const []; - bool _singleAgentLocalSkillsHydrated = false; final Map _aiGatewayStreamingClients = {}; final Set _aiGatewayPendingSessionKeys = {}; @@ -256,14 +224,6 @@ class AppController extends ChangeNotifier { Future _assistantThreadPersistQueue = Future.value(); Future _settingsObservationQueue = Future.value(); - List<_SingleAgentSkillScanRoot> get _singleAgentGlobalSkillScanRoots => - (_singleAgentLocalSkillScanRootOverrides?.map( - _singleAgentGlobalSkillScanRootFromOverride, - ))?.toList(growable: false) ?? - settings.authorizedSkillDirectories - .map(_singleAgentGlobalSkillScanRootFromAuthorizedDirectory) - .toList(growable: false); - WorkspaceDestination get destination => _destination; UiFeatureManifest get uiFeatureManifest => _uiFeatureManifest; AppCapabilities get capabilities => @@ -2170,15 +2130,10 @@ class AppController extends ChangeNotifier { AssistantExecutionTarget.singleAgent) { return; } - if (!_singleAgentLocalSkillsHydrated) { - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: false); - } final previousImported = _assistantThreadRecords[normalizedSessionKey]?.importedSkills ?? const []; - final fallbackSkills = await _singleAgentLocalFallbackSkillsForSession( - normalizedSessionKey, - ); + const fallbackSkills = []; final provider = singleAgentResolvedProviderForSession(normalizedSessionKey) ?? currentSingleAgentResolvedProvider; @@ -2238,7 +2193,6 @@ class AppController extends ChangeNotifier { Future refreshSingleAgentLocalSkillsForSession( String sessionKey, ) async { - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: true); await refreshSingleAgentSkillsForSession(sessionKey); } @@ -2907,7 +2861,6 @@ class AppController extends ChangeNotifier { return; } _disposed = true; - unawaited(_persistSharedSingleAgentLocalSkillsCache()); _runtimeEventsSubscription?.cancel(); _detachChildListeners(); _runtimeCoordinator.dispose(); @@ -2933,7 +2886,6 @@ class AppController extends ChangeNotifier { try { await _settingsController.initialize(); _restoreAssistantThreads(await _store.loadAssistantThreadRecords()); - await _restoreSharedSingleAgentLocalSkillsCache(); if (_disposed) { return; } @@ -2994,7 +2946,6 @@ class AppController extends ChangeNotifier { ); await _restoreInitialAssistantSessionSelection(); await _ensureActiveAssistantThread(); - unawaited(_startupRefreshSharedSingleAgentLocalSkillsCache()); if (isSingleAgentMode) { await refreshSingleAgentSkillsForSession(currentSessionKey); } @@ -3144,22 +3095,6 @@ class AppController extends ChangeNotifier { static bool _isGatewayDraftKey(String key) => key.startsWith('gateway_token_') || key.startsWith('gateway_password_'); - bool _authorizedSkillDirectoriesChanged( - SettingsSnapshot previous, - SettingsSnapshot current, - ) { - return jsonEncode( - previous.authorizedSkillDirectories - .map((item) => item.toJson()) - .toList(growable: false), - ) != - jsonEncode( - current.authorizedSkillDirectories - .map((item) => item.toJson()) - .toList(growable: false), - ); - } - Future _persistSettingsSnapshot(SettingsSnapshot snapshot) async { final sanitized = _sanitizeFeatureFlagSettings( _sanitizeMultiAgentSettings( @@ -3206,16 +3141,6 @@ class AppController extends ChangeNotifier { return; } } - if (_authorizedSkillDirectoriesChanged(previous, current)) { - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: true); - if (_disposed) { - return; - } - if (assistantExecutionTargetForSession(currentSessionKey) == - AssistantExecutionTarget.singleAgent) { - await refreshSingleAgentSkillsForSession(currentSessionKey); - } - } if (refreshAfterSave) { _recomputeTasks(); } @@ -4218,221 +4143,9 @@ class AppController extends ChangeNotifier { return target.promptValue; } - Future> _scanSingleAgentSkillEntries( - List<_SingleAgentSkillScanRoot> roots, { - String workspaceRef = '', - }) async { - final dedupedByName = {}; - for (final rootSpec in roots) { - var resolvedRootPath = _resolveSingleAgentSkillRootPath( - rootSpec.path, - workspaceRef: workspaceRef, - ); - if (resolvedRootPath.isEmpty) { - continue; - } - SkillDirectoryAccessHandle? accessHandle; - try { - if (rootSpec.bookmark.trim().isNotEmpty) { - accessHandle = await _skillDirectoryAccessService.openDirectory( - AuthorizedSkillDirectory( - path: resolvedRootPath, - bookmark: rootSpec.bookmark, - ), - ); - if (accessHandle == null) { - continue; - } - resolvedRootPath = normalizeAuthorizedSkillDirectoryPath( - accessHandle.path, - ); - } - final root = Directory(resolvedRootPath); - if (!await root.exists()) { - continue; - } - await for (final entity in root.list( - recursive: true, - followLinks: false, - )) { - if (entity is! File || entity.uri.pathSegments.last != 'SKILL.md') { - continue; - } - final entry = await _skillEntryFromFile( - entity, - rootSpec, - resolvedRootPath, - ); - final normalizedName = entry.label.trim().toLowerCase(); - if (normalizedName.isEmpty) { - continue; - } - dedupedByName[normalizedName] = entry; - } - } finally { - await accessHandle?.close(); - } - } - final entries = dedupedByName.values.toList(growable: false); - entries.sort((left, right) => left.label.compareTo(right.label)); - return entries; - } - - Future> _scanSingleAgentGlobalSkillEntries() { - return _scanSingleAgentSkillEntries(_singleAgentGlobalSkillScanRoots); - } - - Future> - _scanSingleAgentBundledSkillEntries() async { - try { - final bundle = await _arisBundleRepository.ensureReady(); - final skillsRoot = Directory(bundle.resolve('skills')); - if (!await skillsRoot.exists()) { - return const []; - } - return _scanSingleAgentSkillEntries(const <_SingleAgentSkillScanRoot>[ - _SingleAgentSkillScanRoot(path: '', source: 'bundle', scope: 'bundle'), - ], workspaceRef: skillsRoot.path); - } catch (_) { - return const []; - } - } - - Future> _scanSingleAgentWorkspaceSkillEntries( - String sessionKey, - ) { - return _scanSingleAgentSkillEntries( - _defaultSingleAgentWorkspaceSkillScanRoots, - workspaceRef: assistantWorkspaceRefForSession(sessionKey), - ); - } - - _SingleAgentSkillScanRoot _singleAgentGlobalSkillScanRootFromOverride( - String rawPath, - ) { - final normalizedPath = rawPath.trim(); - final lowered = normalizedPath.toLowerCase(); - return _SingleAgentSkillScanRoot( - path: normalizedPath, - source: _sourceForSkillRootPath(lowered), - scope: normalizedPath.startsWith('/etc/') ? 'system' : 'user', - ); - } - - _SingleAgentSkillScanRoot - _singleAgentGlobalSkillScanRootFromAuthorizedDirectory( - AuthorizedSkillDirectory directory, - ) { - final normalizedPath = normalizeAuthorizedSkillDirectoryPath( - directory.path, - ); - final lowered = normalizedPath.toLowerCase(); - return _SingleAgentSkillScanRoot( - path: normalizedPath, - source: _sourceForSkillRootPath(lowered), - scope: normalizedPath.startsWith('/etc/') ? 'system' : 'user', - bookmark: directory.bookmark, - ); - } - - String _resolveSingleAgentSkillRootPath( - String rawPath, { - String workspaceRef = '', - }) { - final trimmed = rawPath.trim().replaceFirst(RegExp(r'^\./'), ''); - if (trimmed.isEmpty) { - return ''; - } - if (trimmed.startsWith('/')) { - return trimmed; - } - if (trimmed.startsWith('~/')) { - final home = Platform.environment['HOME']?.trim() ?? ''; - return home.isEmpty ? trimmed : '$home/${trimmed.substring(2)}'; - } - final normalizedWorkspace = workspaceRef.trim(); - if (normalizedWorkspace.isEmpty) { - return ''; - } - final base = normalizedWorkspace.endsWith('/') - ? normalizedWorkspace.substring(0, normalizedWorkspace.length - 1) - : normalizedWorkspace; - return '$base/$trimmed'; - } - - String _sourceForSkillRootPath(String path) { - if (path.startsWith('/etc/skills')) { - return 'system'; - } - if (_pathContainsSourceToken(path, 'workbuddy')) { - return 'workbuddy'; - } - if (_pathContainsSourceToken(path, 'opencode')) { - return 'opencode'; - } - if (_pathContainsSourceToken(path, 'claude')) { - return 'claude'; - } - if (_pathContainsSourceToken(path, 'agents')) { - return 'agents'; - } - return 'codex'; - } - - bool _pathContainsSourceToken(String path, String token) { - final pattern = RegExp('(^|[./_-])$token([./_-]|\$)'); - return pattern.hasMatch(path); - } - - Future _skillEntryFromFile( - File file, - _SingleAgentSkillScanRoot root, - String rootPath, - ) async { - final content = await file.readAsString(); - final nameMatch = RegExp( - "^name:\\s*[\"']?(.+?)[\"']?\\s*\$", - multiLine: true, - ).firstMatch(content); - final descriptionMatch = RegExp( - "^description:\\s*[\"']?(.+?)[\"']?\\s*\$", - multiLine: true, - ).firstMatch(content); - final directory = file.parent; - final label = - (nameMatch?.group(1) ?? - directory.uri.pathSegments - .where((item) => item.isNotEmpty) - .last) - .trim(); - final relativeSource = directory.path.startsWith(rootPath) - ? directory.path - .substring(rootPath.length) - .replaceFirst(RegExp(r'^/'), '') - : directory.path; - final sourceSegments = [ - root.source, - if (root.scope != root.source) root.scope, - ].where((item) => item.trim().isNotEmpty).toList(growable: false); - final sourceLabel = sourceSegments.join(' · '); - return AssistantThreadSkillEntry( - key: directory.path, - label: label, - description: (descriptionMatch?.group(1) ?? '').trim(), - source: root.source, - sourcePath: file.path, - scope: root.scope, - sourceLabel: relativeSource.isEmpty - ? sourceLabel - : '$sourceLabel · $relativeSource', - ); - } - void _restoreAssistantThreads(List records) { _assistantThreadRecords.clear(); _assistantThreadMessages.clear(); - _singleAgentSharedImportedSkills = const []; - _singleAgentLocalSkillsHydrated = false; final archivedKeys = settings.assistantArchivedTaskKeys .map(_normalizedAssistantSessionKey) .toSet(); @@ -4485,132 +4198,6 @@ class AppController extends ChangeNotifier { } } - Future _refreshSharedSingleAgentLocalSkillsCache({ - required bool forceRescan, - }) async { - if (!forceRescan && _singleAgentLocalSkillsHydrated) { - return; - } - if (!forceRescan && await _restoreSharedSingleAgentLocalSkillsCache()) { - return; - } - final globalSkills = await _scanSingleAgentGlobalSkillEntries(); - final availableSkills = globalSkills.isNotEmpty - ? globalSkills - : await _scanSingleAgentBundledSkillEntries(); - _singleAgentSharedImportedSkills = availableSkills; - _singleAgentLocalSkillsHydrated = true; - await _persistSharedSingleAgentLocalSkillsCache(); - } - - Future ensureSharedSingleAgentLocalSkillsLoaded() async { - if (_singleAgentLocalSkillsHydrated) { - return; - } - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: false); - } - - Future _startupRefreshSharedSingleAgentLocalSkillsCache() async { - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: true); - if (_disposed) { - return; - } - if (assistantExecutionTargetForSession(currentSessionKey) == - AssistantExecutionTarget.singleAgent) { - await refreshSingleAgentSkillsForSession(currentSessionKey); - return; - } - _notifyIfActive(); - } - - Future> - _singleAgentLocalFallbackSkillsForSession(String sessionKey) async { - final workspaceSkills = await _scanSingleAgentWorkspaceSkillEntries( - sessionKey, - ); - return _mergeSingleAgentLocalSkills( - globalSkills: _singleAgentSharedImportedSkills, - workspaceSkills: workspaceSkills, - ); - } - - List _mergeSingleAgentLocalSkills({ - required List globalSkills, - required List workspaceSkills, - }) { - final merged = {}; - for (final skill in globalSkills) { - final normalizedName = skill.label.trim().toLowerCase(); - if (normalizedName.isEmpty) { - continue; - } - merged[normalizedName] = skill; - } - for (final skill in workspaceSkills) { - final normalizedName = skill.label.trim().toLowerCase(); - if (normalizedName.isEmpty || merged.containsKey(normalizedName)) { - continue; - } - merged[normalizedName] = skill; - } - final entries = merged.values.toList(growable: false); - entries.sort((left, right) => left.label.compareTo(right.label)); - return entries; - } - - Future _restoreSharedSingleAgentLocalSkillsCache() async { - try { - final payload = await _store.loadSupportJson( - _singleAgentLocalSkillsCacheRelativePath, - ); - if (payload == null) { - return false; - } - final schemaVersion = int.tryParse( - payload['schemaVersion']?.toString() ?? '', - ); - if (schemaVersion != _singleAgentLocalSkillsCacheSchemaVersion) { - return false; - } - final skills = asList(payload['skills']) - .map(asMap) - .map( - (item) => AssistantThreadSkillEntry.fromJson( - item.cast(), - ), - ) - .where((item) => item.key.trim().isNotEmpty && item.label.isNotEmpty) - .toList(growable: false); - if (skills.isEmpty) { - _singleAgentSharedImportedSkills = const []; - _singleAgentLocalSkillsHydrated = false; - return false; - } - _singleAgentSharedImportedSkills = skills; - _singleAgentLocalSkillsHydrated = true; - return true; - } catch (_) { - return false; - } - } - - Future _persistSharedSingleAgentLocalSkillsCache() async { - try { - await _store.saveSupportJson( - _singleAgentLocalSkillsCacheRelativePath, - { - 'schemaVersion': _singleAgentLocalSkillsCacheSchemaVersion, - 'savedAtMs': DateTime.now().millisecondsSinceEpoch.toDouble(), - 'skills': _singleAgentSharedImportedSkills - .map((item) => item.toJson()) - .toList(growable: false), - }, - ); - } catch (_) { - // Best effort only for local cache persistence. - } - } - Future _replaceSingleAgentThreadSkills( String sessionKey, List importedSkills, @@ -5437,16 +5024,6 @@ class AppController extends ChangeNotifier { return; } } - if (_authorizedSkillDirectoriesChanged(previous, current)) { - await _refreshSharedSingleAgentLocalSkillsCache(forceRescan: true); - if (_disposed) { - return; - } - if (assistantExecutionTargetForSession(currentSessionKey) == - AssistantExecutionTarget.singleAgent) { - await refreshSingleAgentSkillsForSession(currentSessionKey); - } - } _notifyIfActive(); } diff --git a/test/features/assistant_page_suite.dart b/test/features/assistant_page_suite.dart index 4d07268e..c18afa11 100644 --- a/test/features/assistant_page_suite.dart +++ b/test/features/assistant_page_suite.dart @@ -550,230 +550,6 @@ void main() { expect(find.text('远程 OpenClaw Gateway'), findsWidgets); }); - testWidgets( - 'AssistantPage shows a persistent skill popover in single-agent mode and keeps thread selections isolated', - (WidgetTester tester) async { - late final Directory tempDirectory; - late final AppController controller; - await tester.runAsync(() async { - tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-assistant-skills-ui-', - ); - final agentsRoot = Directory('${tempDirectory.path}/agents-skills'); - final codexRoot = Directory('${tempDirectory.path}/codex-skills'); - final workbuddyRoot = Directory( - '${tempDirectory.path}/workbuddy-skills', - ); - await _writeSkill( - agentsRoot, - 'browser', - skillName: 'Browser Automation', - description: 'Browse websites', - ); - await _writeSkill( - codexRoot, - 'ppt', - skillName: 'PPT', - description: 'Presentation skill', - ); - await _writeSkill( - workbuddyRoot, - 'wordx', - skillName: 'WordX', - description: 'Document skill', - ); - - controller = await _createControllerWithThreadRecords( - records: const [], - useFakeGatewayRuntime: true, - singleAgentLocalSkillScanRoots: [ - agentsRoot.path, - codexRoot.path, - workbuddyRoot.path, - ], - ); - }); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - addTearDown(controller.dispose); - - tester.view.devicePixelRatio = 1; - tester.view.physicalSize = const Size(1600, 1000); - addTearDown(() { - tester.view.resetPhysicalSize(); - tester.view.resetDevicePixelRatio(); - }); - await tester.pumpWidget( - MaterialApp( - locale: const Locale('zh'), - supportedLocales: const [Locale('zh'), Locale('en')], - localizationsDelegates: GlobalMaterialLocalizations.delegates, - theme: AppTheme.light(), - darkTheme: AppTheme.dark(), - home: Scaffold( - body: AssistantPage(controller: controller, onOpenDetail: (_) {}), - ), - ), - ); - await _pumpForUiSync(tester); - await tester.runAsync(() async { - await _waitForCondition( - () => - controller - .assistantImportedSkillsForSession( - controller.currentSessionKey, - ) - .length == - 3, - ); - }); - await _pumpForUiSync(tester); - - await tester.tap(find.byKey(const Key('assistant-skill-picker-button'))); - await _pumpForUiSync(tester); - - expect( - find.byKey(const Key('assistant-skill-picker-popover')), - findsOneWidget, - ); - expect( - find.byKey(const Key('assistant-skill-picker-dialog')), - findsNothing, - ); - - await tester.enterText( - find.byKey(const Key('assistant-skill-picker-search')), - 'browser', - ); - await _pumpForUiSync(tester); - expect(find.text('Browser Automation'), findsOneWidget); - expect(find.text('PPT'), findsNothing); - - final browserSkill = controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .firstWhere((skill) => skill.label == 'Browser Automation'); - final pptSkill = controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .firstWhere((skill) => skill.label == 'PPT'); - final wordxSkill = controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .firstWhere((skill) => skill.label == 'WordX'); - - await tester.tap( - find.byKey( - ValueKey('assistant-skill-option-${browserSkill.key}'), - ), - ); - await _pumpForUiSync(tester); - expect( - find.byKey(const Key('assistant-skill-picker-popover')), - findsOneWidget, - ); - expect( - find.byKey( - ValueKey('assistant-selected-skill-${browserSkill.key}'), - ), - findsOneWidget, - ); - - await tester.enterText( - find.byKey(const Key('assistant-skill-picker-search')), - '', - ); - await _pumpForUiSync(tester); - await tester.tap( - find.byKey(ValueKey('assistant-skill-option-${pptSkill.key}')), - ); - await _pumpForUiSync(tester); - expect( - find.byKey(const Key('assistant-skill-picker-popover')), - findsOneWidget, - ); - expect( - find.byKey( - ValueKey('assistant-selected-skill-${pptSkill.key}'), - ), - findsOneWidget, - ); - - await tester.tapAt(const Offset(24, 24)); - await _pumpForUiSync(tester); - expect( - find.byKey(const Key('assistant-skill-picker-popover')), - findsNothing, - ); - - controller.initializeAssistantThreadContext( - 'draft:task-b', - title: 'Task B', - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - ); - await tester.runAsync(() async { - await controller.switchSession('draft:task-b'); - }); - await _pumpForUiSync(tester); - - expect( - find.byKey( - ValueKey('assistant-selected-skill-${browserSkill.key}'), - ), - findsNothing, - ); - expect( - find.byKey( - ValueKey('assistant-selected-skill-${pptSkill.key}'), - ), - findsNothing, - ); - - await tester.tap(find.byKey(const Key('assistant-skill-picker-button'))); - await _pumpForUiSync(tester); - await tester.tap( - find.byKey( - ValueKey('assistant-skill-option-${wordxSkill.key}'), - ), - ); - await _pumpForUiSync(tester); - - expect( - find.byKey( - ValueKey('assistant-selected-skill-${wordxSkill.key}'), - ), - findsOneWidget, - ); - - await tester.runAsync(() async { - await controller.switchSession('main'); - }); - await _pumpForUiSync(tester); - - expect( - find.byKey( - ValueKey('assistant-selected-skill-${browserSkill.key}'), - ), - findsOneWidget, - ); - expect( - find.byKey( - ValueKey('assistant-selected-skill-${pptSkill.key}'), - ), - findsOneWidget, - ); - expect( - find.byKey( - ValueKey('assistant-selected-skill-${wordxSkill.key}'), - ), - findsNothing, - ); - }, - ); - testWidgets('AssistantPage hides gated attachment and multi-agent actions', ( WidgetTester tester, ) async { @@ -1231,7 +1007,6 @@ Future _createControllerWithThreadRecords({ WidgetTester? tester, required List records, bool useFakeGatewayRuntime = false, - List? singleAgentLocalSkillScanRoots, }) async { SharedPreferences.setMockInitialValues({}); final tempDirectory = await Directory.systemTemp.createTemp( @@ -1289,7 +1064,6 @@ Future _createControllerWithThreadRecords({ codex: _FakeCodexRuntime(), ) : null, - singleAgentLocalSkillScanRoots: singleAgentLocalSkillScanRoots, ); final stopwatch = Stopwatch()..start(); while (controller.initializing) { @@ -1305,34 +1079,11 @@ Future _createControllerWithThreadRecords({ return controller; } -Future _writeSkill( - Directory root, - String folderName, { - required String skillName, - required String description, -}) async { - final directory = Directory('${root.path}/$folderName'); - await directory.create(recursive: true); - await File( - '${directory.path}/SKILL.md', - ).writeAsString('---\nname: $skillName\ndescription: $description\n---\n'); -} - Future _pumpForUiSync(WidgetTester tester) async { await tester.pump(); await tester.pump(const Duration(milliseconds: 200)); } -Future _waitForCondition(bool Function() predicate) async { - final deadline = DateTime.now().add(const Duration(seconds: 20)); - while (!predicate()) { - if (DateTime.now().isAfter(deadline)) { - fail('Timed out waiting for condition'); - } - await Future.delayed(const Duration(milliseconds: 20)); - } -} - class _FakeGatewayRuntime extends GatewayRuntime { _FakeGatewayRuntime({required super.store}) : super(identityStore: DeviceIdentityStore(store)); diff --git a/test/runtime/app_controller_thread_skills_suite.dart b/test/runtime/app_controller_thread_skills_suite.dart deleted file mode 100644 index adb8887f..00000000 --- a/test/runtime/app_controller_thread_skills_suite.dart +++ /dev/null @@ -1,894 +0,0 @@ -@TestOn('vm') -library; - -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:xworkmate/app/app_controller.dart'; -import 'package:xworkmate/runtime/runtime_models.dart'; -import 'package:xworkmate/runtime/secure_config_store.dart'; - -void main() { - test( - 'AppController scans shared single-agent global skills on startup and shares them across providers', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-single-agent-shared-skills-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final systemRoot = Directory('${tempDirectory.path}/etc-skills'); - final agentsRoot = Directory('${tempDirectory.path}/agents-skills'); - final codexRoot = Directory('${tempDirectory.path}/codex-skills'); - final workbuddyRoot = Directory('${tempDirectory.path}/workbuddy-skills'); - await _writeSkill( - systemRoot, - 'analysis', - skillName: 'Analysis', - description: 'System version should be overridden', - ); - await _writeSkill( - agentsRoot, - 'browser', - skillName: 'Browser Automation', - description: 'Shared browser skill', - ); - await _writeSkill( - codexRoot, - 'ppt', - skillName: 'PPT', - description: 'Presentation skill', - ); - await _writeSkill( - workbuddyRoot, - 'analysis', - skillName: 'Analysis', - description: 'WorkBuddy version wins', - ); - await _writeSkill( - workbuddyRoot, - 'cicd-audit', - skillName: 'CICD Audit', - description: 'Pipeline audit skill', - ); - - final controller = AppController( - store: await _createStore(tempDirectory.path), - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - SingleAgentProvider.claude, - ], - singleAgentLocalSkillScanRoots: [ - systemRoot.path, - agentsRoot.path, - codexRoot.path, - workbuddyRoot.path, - ], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await controller.setAssistantExecutionTarget( - AssistantExecutionTarget.singleAgent, - ); - await controller.setSingleAgentProvider(SingleAgentProvider.codex); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 4, - ); - - final firstSessionKey = controller.currentSessionKey; - expect( - controller - .assistantImportedSkillsForSession(firstSessionKey) - .map((skill) => skill.label), - containsAll(const [ - 'Analysis', - 'Browser Automation', - 'PPT', - 'CICD Audit', - ]), - ); - final analysisSkill = controller - .assistantImportedSkillsForSession(firstSessionKey) - .firstWhere((skill) => skill.label == 'Analysis'); - expect(analysisSkill.description, 'WorkBuddy version wins'); - expect(analysisSkill.source, 'workbuddy'); - expect(analysisSkill.scope, 'user'); - - await controller.toggleAssistantSkillForSession( - firstSessionKey, - controller - .assistantImportedSkillsForSession(firstSessionKey) - .firstWhere((skill) => skill.label == 'PPT') - .key, - ); - expect( - controller - .assistantSelectedSkillsForSession(firstSessionKey) - .map((skill) => skill.label), - const ['PPT'], - ); - - await controller.setSingleAgentProvider(SingleAgentProvider.claude); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(firstSessionKey) - .length == - 4, - ); - expect( - controller - .assistantSelectedSkillsForSession(firstSessionKey) - .map((skill) => skill.label), - const ['PPT'], - ); - }, - ); - - test( - 'AppController hot reloads authorized skill directories from settings.yaml', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-skill-directory-hot-reload-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final agentsRoot = Directory('${tempDirectory.path}/agents-skills'); - await _writeSkill( - agentsRoot, - 'browser', - skillName: 'Browser', - description: 'Browser tasks', - ); - - final store = await _createStore(tempDirectory.path); - final controller = AppController( - store: store, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await controller.setAssistantExecutionTarget( - AssistantExecutionTarget.singleAgent, - ); - expect( - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .where((skill) => skill.label == 'Browser'), - isEmpty, - ); - - final updatedSnapshot = - _singleAgentTestSettings(workspacePath: tempDirectory.path).copyWith( - authorizedSkillDirectories: [ - AuthorizedSkillDirectory(path: agentsRoot.path), - ], - ); - final settingsFile = File('${tempDirectory.path}/config/settings.yaml'); - await settingsFile.writeAsString( - encodeYamlDocument(updatedSnapshot.toJson()), - flush: true, - ); - - await _waitFor( - () => controller.authorizedSkillDirectories - .map((item) => item.path) - .contains(agentsRoot.path), - ); - await _waitFor( - () => controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .any((skill) => skill.label == 'Browser'), - ); - expect( - controller.authorizedSkillDirectories.map((item) => item.path), - [agentsRoot.path], - ); - }, - ); - - test( - 'AppController keeps thread-bound skills isolated and restores them after restart', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-thread-isolation-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final agentsRoot = Directory('${tempDirectory.path}/agents-skills'); - final codexRoot = Directory('${tempDirectory.path}/codex-skills'); - final workbuddyRoot = Directory('${tempDirectory.path}/workbuddy-skills'); - await _writeSkill( - agentsRoot, - 'browser', - skillName: 'Browser', - description: 'Browser tasks', - ); - await _writeSkill( - codexRoot, - 'ppt', - skillName: 'PPT', - description: 'Presentation tasks', - ); - await _writeSkill( - workbuddyRoot, - 'wordx', - skillName: 'WordX', - description: 'Document tasks', - ); - await _writeSkill( - workbuddyRoot, - 'cicd-audit', - skillName: 'CICD Audit', - description: 'Pipeline tasks', - ); - - Future createStore() { - return _createStore(tempDirectory.path); - } - - Future createController() async { - return AppController( - store: await createStore(), - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - SingleAgentProvider.claude, - ], - singleAgentLocalSkillScanRoots: [ - agentsRoot.path, - codexRoot.path, - workbuddyRoot.path, - ], - ); - } - - final controller = await createController(); - await _waitFor(() => !controller.initializing); - await controller.setAssistantExecutionTarget( - AssistantExecutionTarget.singleAgent, - ); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 4, - ); - final taskA = controller.currentSessionKey; - await controller.toggleAssistantSkillForSession( - taskA, - controller - .assistantImportedSkillsForSession(taskA) - .firstWhere((skill) => skill.label == 'PPT') - .key, - ); - - controller.initializeAssistantThreadContext( - 'draft:task-b', - title: 'Task B', - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - singleAgentProvider: SingleAgentProvider.claude, - ); - await controller.switchSession('draft:task-b'); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 4, - ); - final taskB = controller.currentSessionKey; - await controller.toggleAssistantSkillForSession( - taskB, - controller - .assistantImportedSkillsForSession(taskB) - .firstWhere((skill) => skill.label == 'WordX') - .key, - ); - - controller.initializeAssistantThreadContext( - 'draft:task-c', - title: 'Task C', - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - ); - await controller.switchSession('draft:task-c'); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 4, - ); - final taskC = controller.currentSessionKey; - await controller.toggleAssistantSkillForSession( - taskC, - controller - .assistantImportedSkillsForSession(taskC) - .firstWhere((skill) => skill.label == 'Browser') - .key, - ); - - expect( - controller - .assistantSelectedSkillsForSession(taskA) - .map((skill) => skill.label), - const ['PPT'], - ); - expect( - controller - .assistantSelectedSkillsForSession(taskB) - .map((skill) => skill.label), - const ['WordX'], - ); - expect( - controller - .assistantSelectedSkillsForSession(taskC) - .map((skill) => skill.label), - const ['Browser'], - ); - - controller.dispose(); - - final restoredController = await createController(); - addTearDown(restoredController.dispose); - await _waitFor(() => !restoredController.initializing); - await restoredController.switchSession(taskA); - await _waitFor( - () => - restoredController - .assistantImportedSkillsForSession(taskA) - .length == - 4, - ); - expect( - restoredController - .assistantSelectedSkillsForSession(taskA) - .map((skill) => skill.label), - const ['PPT'], - ); - await restoredController.switchSession(taskB); - await _waitFor( - () => - restoredController - .assistantImportedSkillsForSession(taskB) - .length == - 4, - ); - expect( - restoredController - .assistantSelectedSkillsForSession(taskB) - .map((skill) => skill.label), - const ['WordX'], - ); - await restoredController.switchSession(taskC); - await _waitFor( - () => - restoredController - .assistantImportedSkillsForSession(taskC) - .length == - 4, - ); - expect( - restoredController - .assistantSelectedSkillsForSession(taskC) - .map((skill) => skill.label), - const ['Browser'], - ); - }, - ); - - test( - 'AppController restores shared global skills cache on restart', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-single-agent-skills-cache-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final agentsRoot = Directory('${tempDirectory.path}/agents-skills'); - final codexRoot = Directory('${tempDirectory.path}/codex-skills'); - await _writeSkill( - agentsRoot, - 'browser', - skillName: 'Browser', - description: 'Browser tasks', - ); - await _writeSkill( - codexRoot, - 'ppt', - skillName: 'PPT', - description: 'Presentation tasks', - ); - - Future createStore() { - return _createStore(tempDirectory.path); - } - - final firstStore = await createStore(); - final controller = AppController( - store: firstStore, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: [ - agentsRoot.path, - codexRoot.path, - ], - ); - await _waitFor(() => !controller.initializing); - await controller.setAssistantExecutionTarget( - AssistantExecutionTarget.singleAgent, - ); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 2, - ); - expect( - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .map((item) => item.label), - containsAll(const ['Browser', 'PPT']), - ); - - final cacheFile = await firstStore.supportFile( - 'cache/single-agent-local-skills.json', - ); - expect(cacheFile, isNotNull); - await _waitFor(() => cacheFile != null && cacheFile.existsSync()); - controller.dispose(); - - if (await agentsRoot.exists()) { - await agentsRoot.delete(recursive: true); - } - if (await codexRoot.exists()) { - await codexRoot.delete(recursive: true); - } - - final restoredController = AppController( - store: await createStore(), - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: [ - agentsRoot.path, - codexRoot.path, - ], - ); - addTearDown(restoredController.dispose); - await _waitFor(() => !restoredController.initializing); - await restoredController.setAssistantExecutionTarget( - AssistantExecutionTarget.singleAgent, - ); - await _waitFor( - () => - restoredController - .assistantImportedSkillsForSession( - restoredController.currentSessionKey, - ) - .length == - 2, - ); - - expect( - restoredController - .assistantImportedSkillsForSession( - restoredController.currentSessionKey, - ) - .map((item) => item.label), - containsAll(const ['Browser', 'PPT']), - ); - }, - ); - - test( - 'AppController uses thread workspaceRef for repo-local fallback', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-workspace-ref-skills-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final workspaceRoot = Directory('${tempDirectory.path}/workspace'); - await _writeSkill( - Directory('${workspaceRoot.path}/.codex/skills'), - 'workspace-only', - skillName: 'Workspace Only Skill', - description: 'Repo-local fallback', - ); - final store = SecureConfigStore( - enableSecureStorage: false, - databasePathResolver: () async => - '${tempDirectory.path}/settings.sqlite3', - fallbackDirectoryPathResolver: () async => tempDirectory.path, - defaultSupportDirectoryPathResolver: () async => tempDirectory.path, - ); - await store.initialize(); - await store.saveSettingsSnapshot( - _singleAgentTestSettings( - workspacePath: '${tempDirectory.path}/unused-default-workspace', - ), - ); - await store.saveAssistantThreadRecords([ - AssistantThreadRecord( - sessionKey: 'main', - messages: const [], - updatedAtMs: 1, - title: '', - archived: false, - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - workspaceRef: workspaceRoot.path, - workspaceRefKind: WorkspaceRefKind.localPath, - ), - ]); - - final controller = AppController( - store: store, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: const [], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await _waitFor( - () => controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .any((item) => item.label == 'Workspace Only Skill'), - ); - - expect( - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .map((item) => item.label), - contains('Workspace Only Skill'), - ); - }, - ); - - test( - 'AppController keeps global roots ahead of repo-local fallback and only fills missing skills', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-global-overrides-repo-local-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final workbuddyRoot = Directory('${tempDirectory.path}/workbuddy-skills'); - final workspaceRoot = Directory('${tempDirectory.path}/workspace'); - await _writeSkill( - workbuddyRoot, - 'shared-skill', - skillName: 'Shared Skill', - description: 'Global wins', - ); - await _writeSkill( - workbuddyRoot, - 'global-only', - skillName: 'Global Only', - description: 'Only from global', - ); - await _writeSkill( - Directory('${workspaceRoot.path}/.codex/skills'), - 'shared-skill', - skillName: 'Shared Skill', - description: 'Repo-local should not override', - ); - await _writeSkill( - Directory('${workspaceRoot.path}/.codex/skills'), - 'workspace-only', - skillName: 'Workspace Only', - description: 'Only from repo-local', - ); - - final store = SecureConfigStore( - enableSecureStorage: false, - databasePathResolver: () async => - '${tempDirectory.path}/settings.sqlite3', - fallbackDirectoryPathResolver: () async => tempDirectory.path, - defaultSupportDirectoryPathResolver: () async => tempDirectory.path, - ); - await store.initialize(); - await store.saveSettingsSnapshot( - _singleAgentTestSettings(workspacePath: tempDirectory.path), - ); - await store.saveAssistantThreadRecords([ - AssistantThreadRecord( - sessionKey: 'main', - messages: const [], - updatedAtMs: 1, - title: '', - archived: false, - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - workspaceRef: workspaceRoot.path, - workspaceRefKind: WorkspaceRefKind.localPath, - ), - ]); - - final controller = AppController( - store: store, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: [workbuddyRoot.path], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await _waitFor( - () => - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .length == - 3, - ); - - final sharedSkill = controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .firstWhere((item) => item.label == 'Shared Skill'); - expect(sharedSkill.description, 'Global wins'); - expect( - controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .map((item) => item.label), - containsAll(const [ - 'Shared Skill', - 'Global Only', - 'Workspace Only', - ]), - ); - }, - ); - - test( - 'AppController scans repo-local skills directories in fixed order and skips missing roots', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-repo-local-order-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - final workspaceRoot = Directory('${tempDirectory.path}/workspace'); - await _writeSkill( - Directory('${workspaceRoot.path}/.agents/skills'), - 'shared-skill', - skillName: 'Shared Skill', - description: 'Agents version', - ); - await _writeSkill( - Directory('${workspaceRoot.path}/.codex/skills'), - 'shared-skill', - skillName: 'Shared Skill', - description: 'Codex version wins', - ); - - final store = SecureConfigStore( - enableSecureStorage: false, - databasePathResolver: () async => - '${tempDirectory.path}/settings.sqlite3', - fallbackDirectoryPathResolver: () async => tempDirectory.path, - defaultSupportDirectoryPathResolver: () async => tempDirectory.path, - ); - await store.initialize(); - await store.saveSettingsSnapshot( - _singleAgentTestSettings(workspacePath: tempDirectory.path), - ); - await store.saveAssistantThreadRecords([ - AssistantThreadRecord( - sessionKey: 'main', - messages: const [], - updatedAtMs: 1, - title: '', - archived: false, - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - workspaceRef: workspaceRoot.path, - workspaceRefKind: WorkspaceRefKind.localPath, - ), - ]); - - final controller = AppController( - store: store, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: const [], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await _waitFor( - () => controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .isNotEmpty, - ); - - final sharedSkill = controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .firstWhere((item) => item.label == 'Shared Skill'); - expect(sharedSkill.description, 'Codex version wins'); - expect(sharedSkill.source, 'codex'); - }, - ); - - test( - 'AppController can return empty skills when neither global nor repo-local roots exist', - () async { - SharedPreferences.setMockInitialValues({}); - final tempDirectory = await Directory.systemTemp.createTemp( - 'xworkmate-empty-relative-skills-', - ); - addTearDown(() async { - if (await tempDirectory.exists()) { - try { - await tempDirectory.delete(recursive: true); - } catch (_) {} - } - }); - - final store = SecureConfigStore( - enableSecureStorage: false, - databasePathResolver: () async => - '${tempDirectory.path}/settings.sqlite3', - fallbackDirectoryPathResolver: () async => tempDirectory.path, - defaultSupportDirectoryPathResolver: () async => tempDirectory.path, - ); - await store.initialize(); - await store.saveSettingsSnapshot( - _singleAgentTestSettings( - workspacePath: '${tempDirectory.path}/missing-workspace', - ), - ); - await store.saveAssistantThreadRecords([ - AssistantThreadRecord( - sessionKey: 'main', - messages: const [], - updatedAtMs: 1, - title: '', - archived: false, - executionTarget: AssistantExecutionTarget.singleAgent, - messageViewMode: AssistantMessageViewMode.rendered, - workspaceRef: '${tempDirectory.path}/missing-workspace', - workspaceRefKind: WorkspaceRefKind.localPath, - ), - ]); - - final controller = AppController( - store: store, - availableSingleAgentProvidersOverride: const [ - SingleAgentProvider.codex, - ], - singleAgentLocalSkillScanRoots: const [], - ); - addTearDown(controller.dispose); - await _waitFor(() => !controller.initializing); - await _waitFor( - () => controller - .assistantImportedSkillsForSession(controller.currentSessionKey) - .isEmpty, - ); - - expect( - controller.assistantImportedSkillsForSession( - controller.currentSessionKey, - ), - isEmpty, - ); - }, - ); -} - -Future _writeSkill( - Directory root, - String folderName, { - required String description, - required String skillName, -}) async { - final directory = Directory('${root.path}/$folderName'); - await directory.create(recursive: true); - await File( - '${directory.path}/SKILL.md', - ).writeAsString('---\nname: $skillName\ndescription: $description\n---\n'); -} - -Future _waitFor(bool Function() predicate) async { - final deadline = DateTime.now().add(const Duration(seconds: 20)); - while (!predicate()) { - if (DateTime.now().isAfter(deadline)) { - fail('Timed out waiting for condition'); - } - await Future.delayed(const Duration(milliseconds: 20)); - } -} - -Future _createStore(String rootPath) async { - final store = SecureConfigStore( - enableSecureStorage: false, - databasePathResolver: () async => '$rootPath/settings.sqlite3', - fallbackDirectoryPathResolver: () async => rootPath, - defaultSupportDirectoryPathResolver: () async => rootPath, - ); - await store.initialize(); - await store.saveSettingsSnapshot( - _singleAgentTestSettings(workspacePath: rootPath), - ); - return store; -} - -SettingsSnapshot _singleAgentTestSettings({required String workspacePath}) { - final defaults = SettingsSnapshot.defaults(); - return defaults.copyWith( - gatewayProfiles: replaceGatewayProfileAt( - replaceGatewayProfileAt( - defaults.gatewayProfiles, - kGatewayLocalProfileIndex, - defaults.primaryLocalGatewayProfile.copyWith( - host: '127.0.0.1', - port: 9, - tls: false, - ), - ), - kGatewayRemoteProfileIndex, - defaults.primaryRemoteGatewayProfile.copyWith( - host: '127.0.0.1', - port: 9, - tls: false, - ), - ), - assistantExecutionTarget: AssistantExecutionTarget.singleAgent, - workspacePath: workspacePath, - ); -} diff --git a/test/test_support.dart b/test/test_support.dart index c29ae38c..9bec7f4b 100644 --- a/test/test_support.dart +++ b/test/test_support.dart @@ -29,7 +29,6 @@ Future createTestController( WidgetTester tester, { DesktopPlatformService? desktopPlatformService, UiFeatureManifest? uiFeatureManifest, - List? singleAgentLocalSkillScanRoots, }) async { SharedPreferences.setMockInitialValues({}); final testRoot = @@ -42,7 +41,6 @@ Future createTestController( ), desktopPlatformService: desktopPlatformService, uiFeatureManifest: uiFeatureManifest, - singleAgentLocalSkillScanRoots: singleAgentLocalSkillScanRoots, ); addTearDown(controller.dispose); await tester.pump(const Duration(milliseconds: 100));