refactor: remove multi-agent orchestration subsystem (Path B)
Remove the entire multi-agent collaboration execution path, including: - MultiAgentOrchestrator and its 4-phase pipeline (Architect→Engineer→Tester→Iteration) - ARIS framework preset and mount infrastructure - Hardcoded model defaults (kimi-k2.5, minimax-m2.7, glm-5) - Deprecated runCliPromptInternal() and its fallback call chain - All related types: MultiAgentConfig, AgentWorkerConfig, MultiAgentRole, etc. This collapses the architecture to a single clean path: Flutter → GoTaskServiceClient → ACP Transport → Go Bridge → Remote Execution 2886 lines removed across 41 files.
This commit is contained in:
parent
db956dba2f
commit
a908b5118f
@ -33,12 +33,9 @@ import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/external_code_agent_acp_desktop_transport.dart';
|
||||
import '../runtime/go_task_service_client.dart';
|
||||
import '../runtime/go_task_service_desktop_service.dart';
|
||||
import '../runtime/go_multi_agent_mount_desktop_client.dart';
|
||||
import '../runtime/go_runtime_dispatch_desktop_client.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_mounts.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'task_thread_repositories.dart';
|
||||
import 'app_controller_openclaw_task_queue.dart';
|
||||
@ -71,7 +68,6 @@ class AppController extends ChangeNotifier {
|
||||
AccountRuntimeClient Function(String baseUrl)? accountClientFactory,
|
||||
Map<String, String>? environmentOverride,
|
||||
GoTaskServiceClient? goTaskServiceClient,
|
||||
MultiAgentMountManager? multiAgentMountManager,
|
||||
}) {
|
||||
environmentOverrideInternal = environmentOverride == null
|
||||
? null
|
||||
@ -165,19 +161,6 @@ class AppController extends ChangeNotifier {
|
||||
taskEndpointResolver: resolveExternalAcpEndpointForRequestInternal,
|
||||
),
|
||||
);
|
||||
multiAgentOrchestratorInternal = MultiAgentOrchestrator(
|
||||
config: resolveMultiAgentConfigInternal(
|
||||
settingsControllerInternal.snapshot,
|
||||
),
|
||||
);
|
||||
multiAgentMountManagerInternal =
|
||||
multiAgentMountManager ??
|
||||
MultiAgentMountManager(
|
||||
resolver: GoMultiAgentMountDesktopClient(
|
||||
client: gatewayAcpClientInternal,
|
||||
endpointResolver: resolveGatewayAcpEndpointInternal,
|
||||
),
|
||||
);
|
||||
bridgeAgentProviderCatalogInternal = normalizeSingleAgentProviderList(
|
||||
initialBridgeProviderCatalog ?? const <SingleAgentProvider>[],
|
||||
);
|
||||
@ -223,7 +206,6 @@ class AppController extends ChangeNotifier {
|
||||
tasksControllerInternal.dispose();
|
||||
storeInternal.dispose();
|
||||
desktopPlatformServiceInternal.dispose();
|
||||
unawaited(multiAgentMountManagerInternal.dispose());
|
||||
unawaited(goTaskServiceClientInternal.dispose());
|
||||
unawaited(gatewayAcpClientInternal.dispose());
|
||||
super.dispose();
|
||||
@ -249,9 +231,6 @@ class AppController extends ChangeNotifier {
|
||||
late final DesktopPlatformService desktopPlatformServiceInternal;
|
||||
late final GatewayAcpClient gatewayAcpClientInternal;
|
||||
late final GoTaskServiceClient goTaskServiceClientInternal;
|
||||
late final MultiAgentOrchestrator multiAgentOrchestratorInternal;
|
||||
late final MultiAgentMountManager multiAgentMountManagerInternal;
|
||||
|
||||
GoTaskServiceClient get goTaskServiceClientForTest =>
|
||||
goTaskServiceClientInternal;
|
||||
|
||||
@ -291,7 +270,6 @@ class AppController extends ChangeNotifier {
|
||||
<String, OpenClawGatewayQueuedTurnInternal>{};
|
||||
int get openClawGatewayActiveTasksInternal =>
|
||||
openClawGatewayActiveTurnsInternal.length;
|
||||
bool multiAgentRunPendingInternal = false;
|
||||
int localMessageCounterInternal = 0;
|
||||
int assistantDraftSessionCounterInternal = 0;
|
||||
|
||||
@ -370,10 +348,6 @@ class AppController extends ChangeNotifier {
|
||||
GatewayAgentsController get agentsController => agentsControllerInternal;
|
||||
GatewaySessionsController get sessionsController =>
|
||||
sessionsControllerInternal;
|
||||
MultiAgentOrchestrator get multiAgentOrchestrator =>
|
||||
multiAgentOrchestratorInternal;
|
||||
MultiAgentMountManager get multiAgentMountManager =>
|
||||
multiAgentMountManagerInternal;
|
||||
GatewayChatController get chatController => chatControllerInternal;
|
||||
SkillsController get skillsController => skillsControllerInternal;
|
||||
ModelsController get modelsController => modelsControllerInternal;
|
||||
@ -618,11 +592,6 @@ class AppController extends ChangeNotifier {
|
||||
selectedSkillLabels: selectedSkillLabels,
|
||||
);
|
||||
|
||||
Future<void> refreshMultiAgentMounts({bool sync = false}) =>
|
||||
AppControllerDesktopThreadSessions(
|
||||
this,
|
||||
).refreshMultiAgentMounts(sync: sync);
|
||||
|
||||
double get assistantSkillCount => skills.length.toDouble();
|
||||
int get currentAssistantSkillCount => skills.length;
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/go_task_service_client.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_thread_sessions.dart';
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import '../runtime/account_runtime_client.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_gateway.dart';
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -73,26 +72,6 @@ Future<void> refreshAcpCapabilitiesRuntimeInternal(
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
if (persistMountTargets && !controller.disposedInternal) {
|
||||
try {
|
||||
final currentConfig = controller.settings.multiAgent;
|
||||
final nextConfig = await controller.multiAgentMountManagerInternal
|
||||
.reconcile(
|
||||
config: currentConfig,
|
||||
aiGatewayUrl: controller.aiGatewayUrl,
|
||||
);
|
||||
if (jsonEncode(nextConfig.toJson()) !=
|
||||
jsonEncode(currentConfig.toJson())) {
|
||||
await controller.settingsControllerInternal.saveSnapshot(
|
||||
controller.settings.copyWith(multiAgent: nextConfig),
|
||||
);
|
||||
controller.multiAgentOrchestratorInternal.updateConfig(nextConfig);
|
||||
}
|
||||
} catch (_) {
|
||||
// Mount reconciliation is an optional bridge capability. A missing or
|
||||
// older remote method must not block assistant startup or task execution.
|
||||
}
|
||||
}
|
||||
if (!controller.disposedInternal) {
|
||||
controller.notifyListeners();
|
||||
}
|
||||
@ -122,46 +101,6 @@ Future<void> refreshSingleAgentCapabilitiesRuntimeInternal(
|
||||
}
|
||||
}
|
||||
|
||||
List<ManagedMountTargetState>
|
||||
mergeAcpCapabilitiesIntoMountTargetsRuntimeInternal(
|
||||
AppController controller,
|
||||
List<ManagedMountTargetState> current,
|
||||
GatewayAcpCapabilities capabilities,
|
||||
) {
|
||||
final source = current.isEmpty ? ManagedMountTargetState.defaults() : current;
|
||||
final agentProviders = capabilities.providerCatalog
|
||||
.map((item) => item.providerId)
|
||||
.toSet();
|
||||
final gatewayProviders = capabilities.gatewayProviderCatalog
|
||||
.map((item) => item.providerId)
|
||||
.toSet();
|
||||
return source
|
||||
.map((item) {
|
||||
final available = switch (item.targetId) {
|
||||
'codex' => agentProviders.contains('codex'),
|
||||
'opencode' => agentProviders.contains('opencode'),
|
||||
'gemini' => agentProviders.contains('gemini'),
|
||||
'openclaw' => gatewayProviders.contains('openclaw'),
|
||||
_ => false,
|
||||
};
|
||||
return item.copyWith(
|
||||
available: available,
|
||||
discoveryState: available ? 'ready' : 'unavailable',
|
||||
syncState: available ? item.syncState : 'idle',
|
||||
detail: available
|
||||
? appText(
|
||||
'来源:Gateway ACP capabilities',
|
||||
'Source: Gateway ACP capabilities',
|
||||
)
|
||||
: appText(
|
||||
'Gateway ACP 未报告该能力。',
|
||||
'Gateway ACP did not report this capability.',
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
String? assistantWorkingDirectoryForSessionRuntimeInternal(
|
||||
AppController controller,
|
||||
String sessionKey,
|
||||
|
||||
@ -33,7 +33,6 @@ import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/go_task_service_client.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -602,15 +601,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
forceRefresh: forceRefresh,
|
||||
);
|
||||
|
||||
List<ManagedMountTargetState> mergeAcpCapabilitiesIntoMountTargetsInternal(
|
||||
List<ManagedMountTargetState> current,
|
||||
GatewayAcpCapabilities capabilities,
|
||||
) => mergeAcpCapabilitiesIntoMountTargetsRuntimeInternal(
|
||||
this,
|
||||
current,
|
||||
capabilities,
|
||||
);
|
||||
|
||||
String? assistantWorkingDirectoryForSessionInternal(String sessionKey) =>
|
||||
assistantWorkingDirectoryForSessionRuntimeInternal(this, sessionKey);
|
||||
|
||||
@ -681,7 +671,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
cronJobsControllerInternal.addListener(relayChildChangeInternal);
|
||||
devicesControllerInternal.addListener(relayChildChangeInternal);
|
||||
tasksControllerInternal.addListener(relayChildChangeInternal);
|
||||
multiAgentOrchestratorInternal.addListener(relayChildChangeInternal);
|
||||
}
|
||||
|
||||
void detachChildListenersInternal() {
|
||||
@ -696,7 +685,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
cronJobsControllerInternal.removeListener(relayChildChangeInternal);
|
||||
devicesControllerInternal.removeListener(relayChildChangeInternal);
|
||||
tasksControllerInternal.removeListener(relayChildChangeInternal);
|
||||
multiAgentOrchestratorInternal.removeListener(relayChildChangeInternal);
|
||||
}
|
||||
|
||||
void handleSettingsControllerChangeInternal() {
|
||||
@ -737,7 +725,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
return;
|
||||
}
|
||||
setActiveAppLanguage(current.appLanguage);
|
||||
multiAgentOrchestratorInternal.updateConfig(current.multiAgent);
|
||||
if (previous.codeAgentRuntimeMode != current.codeAgentRuntimeMode) {
|
||||
registerCodexExternalProviderInternal();
|
||||
if (disposedInternal) {
|
||||
|
||||
@ -30,7 +30,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -64,10 +63,8 @@ extension AppControllerDesktopSettings on AppController {
|
||||
return;
|
||||
}
|
||||
settingsDraftInternal = sanitizeFeatureFlagSettingsInternal(
|
||||
sanitizeMultiAgentSettingsInternal(
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(snapshot),
|
||||
),
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(snapshot),
|
||||
),
|
||||
);
|
||||
settingsDraftInitializedInternal = true;
|
||||
@ -304,7 +301,6 @@ extension AppControllerDesktopSettings on AppController {
|
||||
openClawGatewayQueuedTurnsInternal.clear();
|
||||
openClawGatewayQueuedTurnsBySessionInternal.clear();
|
||||
openClawGatewayActiveTurnsInternal.clear();
|
||||
multiAgentRunPendingInternal = false;
|
||||
final sessionKey = createAssistantDraftSessionKeyInternal();
|
||||
initializeAssistantThreadContext(
|
||||
sessionKey,
|
||||
|
||||
@ -31,7 +31,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -434,11 +433,9 @@ extension AppControllerDesktopSettingsRuntime on AppController {
|
||||
}
|
||||
}
|
||||
final normalized = sanitizeFeatureFlagSettingsInternal(
|
||||
sanitizeMultiAgentSettingsInternal(
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(
|
||||
settingsControllerInternal.snapshot,
|
||||
),
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(
|
||||
settingsControllerInternal.snapshot,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -460,7 +457,6 @@ extension AppControllerDesktopSettingsRuntime on AppController {
|
||||
}
|
||||
lastObservedSettingsSnapshotInternal = settings;
|
||||
modelsControllerInternal.restoreFromSettings(settings.aiGateway);
|
||||
multiAgentOrchestratorInternal.updateConfig(settings.multiAgent);
|
||||
setActiveAppLanguage(settings.appLanguage);
|
||||
await desktopPlatformServiceInternal.initialize(settings.linuxDesktop);
|
||||
await desktopPlatformServiceInternal.setLaunchAtLogin(
|
||||
@ -670,10 +666,8 @@ extension AppControllerDesktopSettingsRuntime on AppController {
|
||||
SettingsSnapshot snapshot,
|
||||
) async {
|
||||
final sanitized = sanitizeFeatureFlagSettingsInternal(
|
||||
sanitizeMultiAgentSettingsInternal(
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(snapshot),
|
||||
),
|
||||
sanitizeOllamaCloudSettingsInternal(
|
||||
sanitizeCodeAgentSettingsInternal(snapshot),
|
||||
),
|
||||
);
|
||||
lastObservedSettingsSnapshotInternal = sanitized;
|
||||
@ -688,7 +682,6 @@ extension AppControllerDesktopSettingsRuntime on AppController {
|
||||
required bool refreshAfterSave,
|
||||
}) async {
|
||||
setActiveAppLanguage(current.appLanguage);
|
||||
multiAgentOrchestratorInternal.updateConfig(current.multiAgent);
|
||||
agentsControllerInternal.restoreSelection(
|
||||
current
|
||||
.gatewayProfileForExecutionTarget(
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
|
||||
@ -31,7 +31,6 @@ import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/go_task_service_client.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_openclaw_task_queue.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
@ -74,11 +73,7 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
(turn) => !turn.cancelled,
|
||||
) ==
|
||||
true ||
|
||||
(multiAgentRunPendingInternal &&
|
||||
matchesSessionKey(
|
||||
normalized,
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
));
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> connectSavedGateway() async {
|
||||
@ -1505,39 +1500,6 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
}
|
||||
|
||||
Future<void> abortRun() async {
|
||||
if (multiAgentRunPendingInternal) {
|
||||
final sessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
);
|
||||
try {
|
||||
await goTaskServiceClientInternal.cancelTask(
|
||||
route: GoTaskServiceRoute.externalAcpMulti,
|
||||
target: assistantExecutionTargetForSession(sessionKey),
|
||||
sessionId: sessionKey,
|
||||
threadId: sessionKey,
|
||||
association: taskThreadForSessionInternal(
|
||||
sessionKey,
|
||||
)?.openClawTaskAssociation,
|
||||
);
|
||||
} catch (_) {
|
||||
// Best effort cancellation only.
|
||||
}
|
||||
multiAgentRunPendingInternal = false;
|
||||
upsertTaskThreadInternal(
|
||||
sessionKey,
|
||||
lifecycleStatus: 'ready',
|
||||
lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
lastResultCode: 'aborted',
|
||||
lastRemoteWorkingDirectory: '',
|
||||
lastArtifactSyncAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
lastArtifactSyncStatus: 'failed',
|
||||
lastTaskArtifactRelativePaths: const <String>[],
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
);
|
||||
recomputeTasksInternal();
|
||||
notifyIfActiveInternal();
|
||||
return;
|
||||
}
|
||||
final sessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
);
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -481,22 +480,6 @@ extension AppControllerDesktopThreadSessions on AppController {
|
||||
|
||||
Future<String> loadAiGatewayApiKey() =>
|
||||
loadAiGatewayApiKeyThreadSessionInternal(this);
|
||||
Future<void> saveMultiAgentConfig(MultiAgentConfig config) =>
|
||||
saveMultiAgentConfigThreadSessionInternal(this, config);
|
||||
Future<void> refreshMultiAgentMounts({bool sync = false}) =>
|
||||
refreshMultiAgentMountsThreadSessionInternal(this, sync: sync);
|
||||
Future<void> runMultiAgentCollaboration({
|
||||
required String rawPrompt,
|
||||
required String composedPrompt,
|
||||
required List<CollaborationAttachment> attachments,
|
||||
required List<String> selectedSkillLabels,
|
||||
}) => runMultiAgentCollaborationThreadSessionInternal(
|
||||
this,
|
||||
rawPrompt: rawPrompt,
|
||||
composedPrompt: composedPrompt,
|
||||
attachments: attachments,
|
||||
selectedSkillLabels: selectedSkillLabels,
|
||||
);
|
||||
Future<void> openOnlineWorkspace() =>
|
||||
openOnlineWorkspaceThreadSessionInternal(this);
|
||||
List<String> get aiGatewayModelChoices =>
|
||||
|
||||
@ -30,7 +30,6 @@ import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/go_task_service_client.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -51,241 +50,6 @@ Future<String> loadAiGatewayApiKeyThreadSessionInternal(
|
||||
return controller.settingsControllerInternal.loadEffectiveAiGatewayApiKey();
|
||||
}
|
||||
|
||||
Future<void> saveMultiAgentConfigThreadSessionInternal(
|
||||
AppController controller,
|
||||
MultiAgentConfig config,
|
||||
) async {
|
||||
final resolved = controller.resolveMultiAgentConfigInternal(
|
||||
controller.settings.copyWith(multiAgent: config),
|
||||
);
|
||||
await AppControllerDesktopSettings(controller).saveSettings(
|
||||
controller.settings.copyWith(multiAgent: resolved),
|
||||
refreshAfterSave: false,
|
||||
);
|
||||
await refreshMultiAgentMountsThreadSessionInternal(
|
||||
controller,
|
||||
sync: resolved.autoSync,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> refreshMultiAgentMountsThreadSessionInternal(
|
||||
AppController controller, {
|
||||
bool sync = false,
|
||||
}) async {
|
||||
final currentConfig = controller.settings.multiAgent;
|
||||
final effectiveConfig = currentConfig.copyWith(autoSync: sync);
|
||||
var nextConfig = await controller.multiAgentMountManagerInternal.reconcile(
|
||||
config: effectiveConfig,
|
||||
aiGatewayUrl: controller.aiGatewayUrl,
|
||||
);
|
||||
if (nextConfig.autoSync != currentConfig.autoSync) {
|
||||
nextConfig = nextConfig.copyWith(autoSync: currentConfig.autoSync);
|
||||
}
|
||||
if (jsonEncode(nextConfig.toJson()) != jsonEncode(currentConfig.toJson())) {
|
||||
await controller.settingsControllerInternal.saveSnapshot(
|
||||
controller.settings.copyWith(multiAgent: nextConfig),
|
||||
);
|
||||
controller.multiAgentOrchestratorInternal.updateConfig(nextConfig);
|
||||
}
|
||||
await controller.refreshAcpCapabilitiesInternal(forceRefresh: true);
|
||||
if (!controller.disposedInternal) {
|
||||
controller.notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> runMultiAgentCollaborationThreadSessionInternal(
|
||||
AppController controller, {
|
||||
required String rawPrompt,
|
||||
required String composedPrompt,
|
||||
required List<CollaborationAttachment> attachments,
|
||||
required List<String> selectedSkillLabels,
|
||||
}) async {
|
||||
if (!controller.isAppOwnedAssistantSessionKeyInternal(
|
||||
controller.currentSessionKey,
|
||||
)) {
|
||||
await controller.ensureActiveAssistantThreadInternal();
|
||||
}
|
||||
final sessionKey = controller.currentSessionKey.trim();
|
||||
await controller.enqueueThreadTurnInternal<void>(sessionKey, () async {
|
||||
await controller.ensureDesktopTaskThreadBindingInternal(
|
||||
sessionKey,
|
||||
executionTarget: controller.assistantExecutionTargetForSession(
|
||||
sessionKey,
|
||||
),
|
||||
);
|
||||
final workingDirectory = controller
|
||||
.assistantWorkingDirectoryForSessionInternal(sessionKey);
|
||||
final remoteWorkingDirectoryHint = controller
|
||||
.assistantRemoteWorkingDirectoryHintForSessionInternal(sessionKey);
|
||||
if (workingDirectory == null || workingDirectory.trim().isEmpty) {
|
||||
final error = StateError(
|
||||
appText(
|
||||
'当前线程缺少工作路径,无法启动 Gateway 协作。',
|
||||
'This thread has no workspace path, so gateway collaboration cannot start.',
|
||||
),
|
||||
);
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'assistant',
|
||||
text: error.message.toString(),
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: 'Multi-Agent',
|
||||
stopReason: null,
|
||||
pending: false,
|
||||
error: true,
|
||||
),
|
||||
);
|
||||
controller.recomputeTasksInternal();
|
||||
controller.notifyIfActiveInternal();
|
||||
throw error;
|
||||
}
|
||||
controller.multiAgentRunPendingInternal = true;
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'user',
|
||||
text: rawPrompt,
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: null,
|
||||
stopReason: null,
|
||||
pending: false,
|
||||
error: false,
|
||||
),
|
||||
);
|
||||
controller.recomputeTasksInternal();
|
||||
try {
|
||||
final taskPrompt = controller.taskWorkspaceContextPromptInternal(
|
||||
sessionKey: sessionKey,
|
||||
userPrompt: composedPrompt,
|
||||
workingDirectory: workingDirectory,
|
||||
remoteWorkingDirectoryHint: remoteWorkingDirectoryHint?.trim() ?? '',
|
||||
target: controller.assistantExecutionTargetForSession(sessionKey),
|
||||
);
|
||||
final result = await controller.goTaskServiceClientInternal.executeTask(
|
||||
GoTaskServiceRequest(
|
||||
sessionId: sessionKey,
|
||||
threadId: sessionKey,
|
||||
target: controller.assistantExecutionTargetForSession(sessionKey),
|
||||
prompt: taskPrompt,
|
||||
workingDirectory: workingDirectory,
|
||||
remoteWorkingDirectoryHint: remoteWorkingDirectoryHint?.trim() ?? '',
|
||||
model: controller.assistantModelForSession(sessionKey),
|
||||
thinking: 'medium',
|
||||
selectedSkills: selectedSkillLabels,
|
||||
inlineAttachments: const <GatewayChatAttachmentPayload>[],
|
||||
localAttachments: attachments,
|
||||
agentId: '',
|
||||
metadata: const <String, dynamic>{},
|
||||
routingHint: 'gateway',
|
||||
collaborationMode: GoTaskServiceCollaborationMode.standard,
|
||||
resumeSession: true,
|
||||
),
|
||||
onUpdate: (update) {
|
||||
final text = update.text.trim().isNotEmpty
|
||||
? update.text.trim()
|
||||
: update.message.trim();
|
||||
if (text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'assistant',
|
||||
text: text,
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: 'Multi-Agent',
|
||||
stopReason: null,
|
||||
pending: update.pending,
|
||||
error: update.error,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
await controller.persistGoTaskArtifactsForSessionInternal(
|
||||
sessionKey,
|
||||
result,
|
||||
);
|
||||
controller.upsertTaskThreadInternal(
|
||||
sessionKey,
|
||||
lastRemoteWorkingDirectory:
|
||||
result.remoteWorkingDirectory.trim().isNotEmpty
|
||||
? result.remoteWorkingDirectory.trim()
|
||||
: null,
|
||||
lastRemoteWorkspaceRefKind: result.remoteWorkspaceRefKind,
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
);
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'assistant',
|
||||
text: result.success
|
||||
? (result.message.trim().isNotEmpty
|
||||
? result.message.trim()
|
||||
: appText(
|
||||
'多 Agent 协作完成。',
|
||||
'Multi-agent collaboration completed.',
|
||||
))
|
||||
: appText(
|
||||
'多 Agent 协作失败:${result.errorMessage.trim().isEmpty ? result.message : result.errorMessage}',
|
||||
'Multi-agent collaboration failed: ${result.errorMessage.trim().isEmpty ? result.message : result.errorMessage}',
|
||||
),
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: 'Multi-Agent',
|
||||
stopReason: null,
|
||||
pending: false,
|
||||
error: !result.success,
|
||||
),
|
||||
);
|
||||
} on GatewayAcpException catch (error) {
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'assistant',
|
||||
text: appText(
|
||||
'多 Agent 协作不可用(ACP):${error.message}',
|
||||
'Multi-agent collaboration is unavailable (ACP): ${error.message}',
|
||||
),
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: 'Multi-Agent',
|
||||
stopReason: null,
|
||||
pending: false,
|
||||
error: true,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
controller.appendLocalSessionMessageInternal(
|
||||
sessionKey,
|
||||
GatewayChatMessage(
|
||||
id: controller.nextLocalMessageIdInternal(),
|
||||
role: 'assistant',
|
||||
text: error.toString(),
|
||||
timestampMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
toolCallId: null,
|
||||
toolName: 'Multi-Agent',
|
||||
stopReason: null,
|
||||
pending: false,
|
||||
error: true,
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
controller.multiAgentRunPendingInternal = false;
|
||||
controller.recomputeTasksInternal();
|
||||
controller.notifyIfActiveInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> openOnlineWorkspaceThreadSessionInternal(
|
||||
AppController controller,
|
||||
) async {
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
@ -166,17 +165,6 @@ extension AppControllerDesktopThreadStorage on AppController {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSnapshot sanitizeMultiAgentSettingsInternal(
|
||||
SettingsSnapshot snapshot,
|
||||
) {
|
||||
final resolved = resolveMultiAgentConfigInternal(snapshot);
|
||||
if (jsonEncode(snapshot.multiAgent.toJson()) ==
|
||||
jsonEncode(resolved.toJson())) {
|
||||
return snapshot;
|
||||
}
|
||||
return snapshot.copyWith(multiAgent: resolved);
|
||||
}
|
||||
|
||||
SettingsSnapshot sanitizeFeatureFlagSettingsInternal(
|
||||
SettingsSnapshot snapshot,
|
||||
) {
|
||||
@ -184,9 +172,6 @@ extension AppControllerDesktopThreadStorage on AppController {
|
||||
final sanitizedExecutionTarget = sanitizeExecutionTargetInternal(
|
||||
features.sanitizeExecutionTarget(snapshot.assistantExecutionTarget),
|
||||
);
|
||||
final multiAgentConfig = features.supportsMultiAgent
|
||||
? snapshot.multiAgent
|
||||
: snapshot.multiAgent.copyWith(enabled: false);
|
||||
final experimentalCanvas =
|
||||
features.allowsExperimentalSetting(
|
||||
UiFeatureKeys.settingsExperimentalCanvas,
|
||||
@ -207,7 +192,6 @@ extension AppControllerDesktopThreadStorage on AppController {
|
||||
: false;
|
||||
return snapshot.copyWith(
|
||||
assistantExecutionTarget: sanitizedExecutionTarget,
|
||||
multiAgent: multiAgentConfig,
|
||||
experimentalCanvas: experimentalCanvas,
|
||||
experimentalBridge: experimentalBridge,
|
||||
experimentalDebug: experimentalDebug,
|
||||
@ -271,39 +255,6 @@ extension AppControllerDesktopThreadStorage on AppController {
|
||||
return sanitizeExecutionTargetInternal(target);
|
||||
}
|
||||
|
||||
MultiAgentConfig resolveMultiAgentConfigInternal(SettingsSnapshot snapshot) {
|
||||
final defaults = MultiAgentConfig.defaults();
|
||||
final current = snapshot.multiAgent;
|
||||
final ollamaEndpoint = snapshot.ollamaLocal.endpoint.trim().isEmpty
|
||||
? current.ollamaEndpoint
|
||||
: snapshot.ollamaLocal.endpoint.trim();
|
||||
final engineerModel = current.engineer.model.trim().isNotEmpty
|
||||
? current.engineer.model.trim()
|
||||
: snapshot.ollamaLocal.defaultModel.trim().isNotEmpty
|
||||
? snapshot.ollamaLocal.defaultModel.trim()
|
||||
: defaults.engineer.model;
|
||||
final architectModel = current.architect.model.trim().isNotEmpty
|
||||
? current.architect.model.trim()
|
||||
: defaults.architect.model;
|
||||
final testerModel = current.tester.model.trim().isNotEmpty
|
||||
? current.tester.model.trim()
|
||||
: defaults.tester.model;
|
||||
return current.copyWith(
|
||||
framework: current.arisEnabled
|
||||
? MultiAgentFramework.aris
|
||||
: current.framework,
|
||||
arisEnabled:
|
||||
current.framework == MultiAgentFramework.aris || current.arisEnabled,
|
||||
ollamaEndpoint: ollamaEndpoint,
|
||||
architect: current.architect.copyWith(model: architectModel),
|
||||
engineer: current.engineer.copyWith(model: engineerModel),
|
||||
tester: current.tester.copyWith(model: testerModel),
|
||||
mountTargets: current.mountTargets.isEmpty
|
||||
? MultiAgentConfig.defaults().mountTargets
|
||||
: current.mountTargets,
|
||||
);
|
||||
}
|
||||
|
||||
void appendAssistantThreadMessageInternal(
|
||||
String sessionKey,
|
||||
GatewayChatMessage message,
|
||||
|
||||
@ -29,7 +29,6 @@ import '../runtime/assistant_artifacts.dart';
|
||||
import '../runtime/desktop_thread_artifact_service.dart';
|
||||
import '../runtime/mode_switcher.dart';
|
||||
import '../runtime/agent_registry.dart';
|
||||
import '../runtime/multi_agent_orchestrator.dart';
|
||||
import '../runtime/platform_environment.dart';
|
||||
import 'app_controller_desktop_core.dart';
|
||||
import 'app_controller_desktop_navigation.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -15,7 +15,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -15,7 +15,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
@ -154,15 +153,6 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal {
|
||||
sessionKey: submittedSessionKey,
|
||||
thinking: thinkingLabelInternal,
|
||||
attachments: attachmentPayloads,
|
||||
localAttachments: submittedAttachments
|
||||
.map(
|
||||
(item) => CollaborationAttachment(
|
||||
name: item.name,
|
||||
description: item.mimeType,
|
||||
path: item.path,
|
||||
),
|
||||
)
|
||||
.toList(growable: false),
|
||||
selectedSkillLabels: selectedSkillLabels,
|
||||
);
|
||||
clearComposerDraftForSessionInternal(submittedSessionKey);
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/gateway_acp_client.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -16,7 +16,6 @@ import '../../app/app_metadata.dart';
|
||||
import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/multi_agent_orchestrator.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
class ArisLlmChatClient {
|
||||
ArisLlmChatClient({Duration rpcTimeout = const Duration(minutes: 2)});
|
||||
|
||||
Future<String> chat({
|
||||
required String endpoint,
|
||||
required String apiKey,
|
||||
required String model,
|
||||
required String prompt,
|
||||
String systemPrompt = '',
|
||||
}) {
|
||||
return _callTool(
|
||||
toolName: 'chat',
|
||||
environment: <String, String>{},
|
||||
arguments: <String, dynamic>{},
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> claudeReview({
|
||||
required String prompt,
|
||||
String model = '',
|
||||
String systemPrompt = '',
|
||||
String tools = '',
|
||||
}) {
|
||||
return _callTool(
|
||||
toolName: 'claude_review',
|
||||
environment: <String, String>{},
|
||||
arguments: <String, dynamic>{},
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _callTool({
|
||||
required String toolName,
|
||||
required Map<String, String> environment,
|
||||
required Map<String, dynamic> arguments,
|
||||
}) async {
|
||||
// Local Go core execution is deprecated in favor of bridge-mediated execution.
|
||||
throw UnsupportedError(
|
||||
'Local Go core execution is disabled. Use the managed bridge ACP runtime instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import 'gateway_acp_client.dart';
|
||||
import 'multi_agent_mount_resolver.dart';
|
||||
import 'runtime_models.dart';
|
||||
|
||||
class GoMultiAgentMountDesktopClient implements MultiAgentMountResolver {
|
||||
GoMultiAgentMountDesktopClient({
|
||||
required GatewayAcpClient client,
|
||||
required Uri? Function() endpointResolver,
|
||||
}) : _client = client,
|
||||
_endpointResolver = endpointResolver;
|
||||
|
||||
final GatewayAcpClient _client;
|
||||
final Uri? Function() _endpointResolver;
|
||||
|
||||
@override
|
||||
Future<MultiAgentConfig?> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
required String codexHome,
|
||||
required String opencodeHome,
|
||||
required ArisMountProbe arisProbe,
|
||||
}) async {
|
||||
final response = await _client.request(
|
||||
method: 'xworkmate.mounts.reconcile',
|
||||
params: <String, dynamic>{
|
||||
'config': <String, dynamic>{
|
||||
'autoSync': config.autoSync,
|
||||
'usesAris': config.usesAris,
|
||||
'managedMcpServers': config.managedMcpServers
|
||||
.map((item) => item.toJson())
|
||||
.toList(growable: false),
|
||||
},
|
||||
'aiGatewayUrl': aiGatewayUrl.trim(),
|
||||
'codexHome': codexHome.trim(),
|
||||
'opencodeHome': opencodeHome.trim(),
|
||||
'aris': arisProbe.toJson(),
|
||||
},
|
||||
endpointOverride: _endpointResolver(),
|
||||
);
|
||||
final result = _castMap(response['result']);
|
||||
final rawTargets = result['mountTargets'];
|
||||
final mountTargets = rawTargets is List
|
||||
? rawTargets
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => ManagedMountTargetState.fromJson(
|
||||
item.cast<String, dynamic>(),
|
||||
),
|
||||
)
|
||||
.toList(growable: false)
|
||||
: config.mountTargets;
|
||||
return config.copyWith(
|
||||
mountTargets: mountTargets,
|
||||
arisBundleVersion:
|
||||
result['arisBundleVersion']?.toString().trim().isNotEmpty == true
|
||||
? result['arisBundleVersion'].toString().trim()
|
||||
: config.arisBundleVersion,
|
||||
arisCompatStatus:
|
||||
result['arisCompatStatus']?.toString().trim().isNotEmpty == true
|
||||
? result['arisCompatStatus'].toString().trim()
|
||||
: config.arisCompatStatus,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() => _client.dispose();
|
||||
|
||||
Map<String, dynamic> _castMap(Object? value) {
|
||||
if (value is Map<String, dynamic>) {
|
||||
return value;
|
||||
}
|
||||
if (value is Map) {
|
||||
return value.cast<String, dynamic>();
|
||||
}
|
||||
return const <String, dynamic>{};
|
||||
}
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
import 'runtime_models.dart';
|
||||
|
||||
abstract class FrameworkPreset {
|
||||
const FrameworkPreset();
|
||||
|
||||
String get id;
|
||||
String get label;
|
||||
|
||||
Future<String> roleInstructionBlock({
|
||||
required MultiAgentRole role,
|
||||
required String tool,
|
||||
required List<String> selectedSkills,
|
||||
});
|
||||
}
|
||||
|
||||
class NativeFrameworkPreset extends FrameworkPreset {
|
||||
const NativeFrameworkPreset();
|
||||
|
||||
@override
|
||||
String get id => MultiAgentFramework.native.name;
|
||||
|
||||
@override
|
||||
String get label => MultiAgentFramework.native.label;
|
||||
|
||||
@override
|
||||
Future<String> roleInstructionBlock({
|
||||
required MultiAgentRole role,
|
||||
required String tool,
|
||||
required List<String> selectedSkills,
|
||||
}) async {
|
||||
final selected = selectedSkills.isEmpty
|
||||
? '- 无'
|
||||
: selectedSkills.map((item) => '- $item').join('\n');
|
||||
return '''
|
||||
当前协作框架:$label
|
||||
当前角色:${role.label}
|
||||
当前工具:$tool
|
||||
|
||||
用户当前选中的技能:
|
||||
$selected
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
class ArisFrameworkPreset extends FrameworkPreset {
|
||||
const ArisFrameworkPreset();
|
||||
|
||||
@override
|
||||
String get id => MultiAgentFramework.aris.name;
|
||||
|
||||
@override
|
||||
String get label => MultiAgentFramework.aris.label;
|
||||
|
||||
@override
|
||||
Future<String> roleInstructionBlock({
|
||||
required MultiAgentRole role,
|
||||
required String tool,
|
||||
required List<String> selectedSkills,
|
||||
}) async {
|
||||
// ARIS data has been removed from assets.
|
||||
// Fallback to basic instruction.
|
||||
final selected = selectedSkills.isEmpty
|
||||
? '- 无'
|
||||
: selectedSkills.map((item) => '- $item').join('\n');
|
||||
return '''
|
||||
当前协作框架:$label
|
||||
当前角色:${role.label}
|
||||
当前工具:$tool
|
||||
|
||||
用户当前选中的技能:
|
||||
$selected
|
||||
''';
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
import 'runtime_models.dart';
|
||||
|
||||
class ArisMountProbe {
|
||||
const ArisMountProbe({
|
||||
required this.available,
|
||||
required this.bundleVersion,
|
||||
required this.llmChatServerPath,
|
||||
required this.skillCount,
|
||||
required this.bridgeAvailable,
|
||||
this.error = '',
|
||||
});
|
||||
|
||||
const ArisMountProbe.unavailable({this.error = ''})
|
||||
: available = false,
|
||||
bundleVersion = '',
|
||||
llmChatServerPath = '',
|
||||
skillCount = 0,
|
||||
bridgeAvailable = false;
|
||||
|
||||
final bool available;
|
||||
final String bundleVersion;
|
||||
final String llmChatServerPath;
|
||||
final int skillCount;
|
||||
final bool bridgeAvailable;
|
||||
final String error;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'available': available,
|
||||
'bundleVersion': bundleVersion,
|
||||
'llmChatServerPath': llmChatServerPath,
|
||||
'skillCount': skillCount,
|
||||
'bridgeAvailable': bridgeAvailable,
|
||||
'error': error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MultiAgentMountResolver {
|
||||
Future<MultiAgentConfig?> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
required String codexHome,
|
||||
required String opencodeHome,
|
||||
required ArisMountProbe arisProbe,
|
||||
});
|
||||
|
||||
Future<void> dispose();
|
||||
}
|
||||
@ -1,310 +0,0 @@
|
||||
import 'codex_config_bridge.dart';
|
||||
import 'multi_agent_mount_resolver.dart';
|
||||
import 'opencode_config_bridge.dart';
|
||||
import 'runtime_models.dart';
|
||||
|
||||
/// 协作模式挂载管理器
|
||||
///
|
||||
/// 在云中性设计下,挂载目标的发现与状态调和应通过桥接同步到远程端点。
|
||||
class MultiAgentMountManager {
|
||||
MultiAgentMountManager({
|
||||
CodexConfigBridge? codexConfigBridge,
|
||||
OpencodeConfigBridge? opencodeConfigBridge,
|
||||
MultiAgentMountResolver? resolver,
|
||||
}) : this._(
|
||||
codexConfigBridge: codexConfigBridge ?? CodexConfigBridge(),
|
||||
opencodeConfigBridge: opencodeConfigBridge ?? OpencodeConfigBridge(),
|
||||
resolver: resolver,
|
||||
);
|
||||
|
||||
MultiAgentMountManager._({
|
||||
required CodexConfigBridge codexConfigBridge,
|
||||
required OpencodeConfigBridge opencodeConfigBridge,
|
||||
MultiAgentMountResolver? resolver,
|
||||
}) : _codexConfigBridge = codexConfigBridge,
|
||||
_opencodeConfigBridge = opencodeConfigBridge,
|
||||
_resolver = resolver,
|
||||
_adapters = <CliMountAdapter>[
|
||||
CodexMountAdapter(codexConfigBridge),
|
||||
ClaudeMountAdapter(),
|
||||
GeminiMountAdapter(),
|
||||
OpencodeMountAdapter(opencodeConfigBridge),
|
||||
OpenClawMountAdapter(),
|
||||
];
|
||||
|
||||
final CodexConfigBridge _codexConfigBridge;
|
||||
final OpencodeConfigBridge _opencodeConfigBridge;
|
||||
final MultiAgentMountResolver? _resolver;
|
||||
final List<CliMountAdapter> _adapters;
|
||||
|
||||
Future<MultiAgentConfig> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
final resolved = await _resolver?.reconcile(
|
||||
config: config,
|
||||
aiGatewayUrl: aiGatewayUrl,
|
||||
codexHome: _codexConfigBridge.codexHome,
|
||||
opencodeHome: _opencodeConfigBridge.opencodeHome,
|
||||
arisProbe: await _buildArisProbe(),
|
||||
);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
return _reconcileLocally(config: config, aiGatewayUrl: aiGatewayUrl);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _resolver?.dispose();
|
||||
}
|
||||
|
||||
Future<ArisMountProbe> _buildArisProbe() async {
|
||||
return const ArisMountProbe(
|
||||
available: false,
|
||||
bundleVersion: '',
|
||||
llmChatServerPath: '',
|
||||
skillCount: 0,
|
||||
bridgeAvailable: false,
|
||||
error: 'Legacy local agent execution is disabled.',
|
||||
);
|
||||
}
|
||||
|
||||
Future<MultiAgentConfig> _reconcileLocally({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
final states = <ManagedMountTargetState>[];
|
||||
for (final adapter in _adapters) {
|
||||
states.add(
|
||||
await adapter.reconcile(config: config, aiGatewayUrl: aiGatewayUrl),
|
||||
);
|
||||
}
|
||||
return config.copyWith(
|
||||
mountTargets: states,
|
||||
arisBundleVersion: '',
|
||||
arisCompatStatus: 'missing',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CliMountAdapter {
|
||||
String get targetId;
|
||||
String get label;
|
||||
bool get supportsSkills;
|
||||
bool get supportsMcp;
|
||||
bool get supportsAiGatewayInjection;
|
||||
|
||||
Future<bool> isInstalled();
|
||||
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
});
|
||||
|
||||
int countMcpTomlSections(String content) {
|
||||
return RegExp(
|
||||
r'^\[mcp_servers\.[^\]]+\]',
|
||||
multiLine: true,
|
||||
).allMatches(content).length;
|
||||
}
|
||||
}
|
||||
|
||||
class CodexMountAdapter extends CliMountAdapter {
|
||||
CodexMountAdapter(CodexConfigBridge bridge);
|
||||
|
||||
@override
|
||||
String get targetId => 'codex';
|
||||
|
||||
@override
|
||||
String get label => 'Codex';
|
||||
|
||||
@override
|
||||
bool get supportsSkills => true;
|
||||
|
||||
@override
|
||||
bool get supportsMcp => true;
|
||||
|
||||
@override
|
||||
bool get supportsAiGatewayInjection => true;
|
||||
|
||||
@override
|
||||
Future<bool> isInstalled() async => false;
|
||||
|
||||
@override
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
return ManagedMountTargetState.placeholder(
|
||||
targetId: targetId,
|
||||
label: label,
|
||||
supportsSkills: supportsSkills,
|
||||
supportsMcp: supportsMcp,
|
||||
supportsAiGatewayInjection: supportsAiGatewayInjection,
|
||||
).copyWith(
|
||||
available: false,
|
||||
discoveryState: 'missing',
|
||||
syncState: 'missing',
|
||||
detail:
|
||||
'Local CLI interaction is disabled. Use bridge for orchestration.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ClaudeMountAdapter extends CliMountAdapter {
|
||||
@override
|
||||
String get targetId => 'claude';
|
||||
|
||||
@override
|
||||
String get label => 'Claude';
|
||||
|
||||
@override
|
||||
bool get supportsSkills => true;
|
||||
|
||||
@override
|
||||
bool get supportsMcp => true;
|
||||
|
||||
@override
|
||||
bool get supportsAiGatewayInjection => true;
|
||||
|
||||
@override
|
||||
Future<bool> isInstalled() async => false;
|
||||
|
||||
@override
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
return ManagedMountTargetState.placeholder(
|
||||
targetId: targetId,
|
||||
label: label,
|
||||
supportsSkills: supportsSkills,
|
||||
supportsMcp: supportsMcp,
|
||||
supportsAiGatewayInjection: supportsAiGatewayInjection,
|
||||
).copyWith(
|
||||
available: false,
|
||||
discoveryState: 'missing',
|
||||
syncState: 'disabled',
|
||||
detail: 'Local CLI interaction is disabled.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GeminiMountAdapter extends CliMountAdapter {
|
||||
@override
|
||||
String get targetId => 'gemini';
|
||||
|
||||
@override
|
||||
String get label => 'Gemini';
|
||||
|
||||
@override
|
||||
bool get supportsSkills => true;
|
||||
|
||||
@override
|
||||
bool get supportsMcp => true;
|
||||
|
||||
@override
|
||||
bool get supportsAiGatewayInjection => true;
|
||||
|
||||
@override
|
||||
Future<bool> isInstalled() async => false;
|
||||
|
||||
@override
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
return ManagedMountTargetState.placeholder(
|
||||
targetId: targetId,
|
||||
label: label,
|
||||
supportsSkills: supportsSkills,
|
||||
supportsMcp: supportsMcp,
|
||||
supportsAiGatewayInjection: supportsAiGatewayInjection,
|
||||
).copyWith(
|
||||
available: false,
|
||||
discoveryState: 'missing',
|
||||
syncState: 'disabled',
|
||||
detail: 'Local CLI interaction is disabled.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OpencodeMountAdapter extends CliMountAdapter {
|
||||
OpencodeMountAdapter(OpencodeConfigBridge bridge);
|
||||
|
||||
@override
|
||||
String get targetId => 'opencode';
|
||||
|
||||
@override
|
||||
String get label => 'OpenCode';
|
||||
|
||||
@override
|
||||
bool get supportsSkills => true;
|
||||
|
||||
@override
|
||||
bool get supportsMcp => true;
|
||||
|
||||
@override
|
||||
bool get supportsAiGatewayInjection => true;
|
||||
|
||||
@override
|
||||
Future<bool> isInstalled() async => false;
|
||||
|
||||
@override
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
return ManagedMountTargetState.placeholder(
|
||||
targetId: targetId,
|
||||
label: label,
|
||||
supportsSkills: supportsSkills,
|
||||
supportsMcp: supportsMcp,
|
||||
supportsAiGatewayInjection: supportsAiGatewayInjection,
|
||||
).copyWith(
|
||||
available: false,
|
||||
discoveryState: 'missing',
|
||||
syncState: 'missing',
|
||||
detail: 'Local CLI interaction is disabled.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OpenClawMountAdapter extends CliMountAdapter {
|
||||
@override
|
||||
String get targetId => 'openclaw';
|
||||
|
||||
@override
|
||||
String get label => 'OpenClaw';
|
||||
|
||||
@override
|
||||
bool get supportsSkills => true;
|
||||
|
||||
@override
|
||||
bool get supportsMcp => false;
|
||||
|
||||
@override
|
||||
bool get supportsAiGatewayInjection => true;
|
||||
|
||||
@override
|
||||
Future<bool> isInstalled() async => false;
|
||||
|
||||
@override
|
||||
Future<ManagedMountTargetState> reconcile({
|
||||
required MultiAgentConfig config,
|
||||
required String aiGatewayUrl,
|
||||
}) async {
|
||||
return ManagedMountTargetState.placeholder(
|
||||
targetId: targetId,
|
||||
label: label,
|
||||
supportsSkills: supportsSkills,
|
||||
supportsMcp: supportsMcp,
|
||||
supportsAiGatewayInjection: supportsAiGatewayInjection,
|
||||
).copyWith(
|
||||
available: false,
|
||||
discoveryState: 'missing',
|
||||
syncState: 'disabled',
|
||||
detail: 'Local CLI interaction is disabled.',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export 'multi_agent_orchestrator_protocol.dart';
|
||||
export 'multi_agent_orchestrator_workflow.dart';
|
||||
export 'multi_agent_orchestrator_support.dart';
|
||||
export 'multi_agent_orchestrator_core.dart';
|
||||
@ -1,384 +0,0 @@
|
||||
// ignore_for_file: unused_import, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'embedded_agent_launch_policy.dart';
|
||||
import 'multi_agent_frameworks.dart';
|
||||
import 'runtime_models.dart';
|
||||
import 'multi_agent_orchestrator_protocol.dart';
|
||||
import 'multi_agent_orchestrator_workflow.dart';
|
||||
import 'multi_agent_orchestrator_support.dart';
|
||||
|
||||
/// 多 Agent 协作编排器
|
||||
///
|
||||
/// 管理 Architect(调度/文档)→ Lead Engineer(主程)→ Worker/Review(并行 worker + 复审)
|
||||
/// 的工作流。
|
||||
///
|
||||
/// 在云中性设计下,编排逻辑应通过桥接转发到远程 ACP 端点执行。
|
||||
class MultiAgentOrchestrator extends ChangeNotifier {
|
||||
MultiAgentOrchestrator({
|
||||
required MultiAgentConfig config,
|
||||
HttpClient Function()? httpClientFactory,
|
||||
}) : configInternal = config,
|
||||
httpClientFactoryInternal = httpClientFactory ?? HttpClient.new;
|
||||
|
||||
/// 当前配置
|
||||
MultiAgentConfig configInternal;
|
||||
MultiAgentConfig get config => configInternal;
|
||||
final HttpClient Function() httpClientFactoryInternal;
|
||||
|
||||
HttpClient? activeHttpClientInternal;
|
||||
bool abortRequestedInternal = false;
|
||||
|
||||
/// 协作模式是否启用
|
||||
bool collaborationEnabledInternal = false;
|
||||
bool get collaborationEnabled => collaborationEnabledInternal;
|
||||
|
||||
/// 是否正在运行
|
||||
bool isRunningInternal = false;
|
||||
bool get isRunning => isRunningInternal;
|
||||
|
||||
/// 最后错误
|
||||
String? lastErrorInternal;
|
||||
String? get lastError => lastErrorInternal;
|
||||
|
||||
/// 当前迭代轮次
|
||||
int currentIterationInternal = 0;
|
||||
int get currentIteration => currentIterationInternal;
|
||||
|
||||
/// 状态日志
|
||||
final List<CollaborationLogEntry> logEntriesInternal = [];
|
||||
List<CollaborationLogEntry> get logEntries =>
|
||||
List.unmodifiable(logEntriesInternal);
|
||||
|
||||
/// 更新配置
|
||||
void updateConfig(MultiAgentConfig config) {
|
||||
configInternal = config;
|
||||
collaborationEnabledInternal = config.enabled;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> abort() async {
|
||||
abortRequestedInternal = true;
|
||||
final client = activeHttpClientInternal;
|
||||
activeHttpClientInternal = null;
|
||||
if (client != null) {
|
||||
try {
|
||||
client.close(force: true);
|
||||
} catch (_) {
|
||||
// Best effort only.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 启用协作模式
|
||||
void enable() {
|
||||
configInternal = configInternal.copyWith(enabled: true);
|
||||
collaborationEnabledInternal = true;
|
||||
lastErrorInternal = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 禁用协作模式
|
||||
void disable() {
|
||||
configInternal = configInternal.copyWith(enabled: false);
|
||||
collaborationEnabledInternal = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 切换协作模式
|
||||
void toggle() {
|
||||
if (collaborationEnabledInternal) {
|
||||
disable();
|
||||
} else {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
/// 执行完整的协作工作流
|
||||
Future<CollaborationResult> runCollaboration({
|
||||
required String taskPrompt,
|
||||
required String workingDirectory,
|
||||
List<CollaborationAttachment> attachments = const [],
|
||||
List<String> selectedSkills = const [],
|
||||
void Function(MultiAgentRunEvent event)? onEvent,
|
||||
}) async {
|
||||
if (isRunningInternal) {
|
||||
throw StateError('Collaboration is already running');
|
||||
}
|
||||
|
||||
isRunningInternal = true;
|
||||
currentIterationInternal = 0;
|
||||
abortRequestedInternal = false;
|
||||
logEntriesInternal.clear();
|
||||
lastErrorInternal = null;
|
||||
notifyListeners();
|
||||
|
||||
final startTime = DateTime.now();
|
||||
final steps = <CollaborationStep>[];
|
||||
final preset = configInternal.usesAris
|
||||
? const ArisFrameworkPreset()
|
||||
: const NativeFrameworkPreset();
|
||||
|
||||
try {
|
||||
// === Phase 1: Architect 分析任务 ===
|
||||
throwIfAbortedInternal();
|
||||
logInternal(
|
||||
CollaborationLogLevel.info,
|
||||
'🎨',
|
||||
'${roleLabelInternal(MultiAgentRole.architect)} 开始分析任务...',
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.architect),
|
||||
message: '${roleLabelInternal(MultiAgentRole.architect)} 开始分析任务…',
|
||||
pending: true,
|
||||
error: false,
|
||||
role: 'architect',
|
||||
),
|
||||
);
|
||||
final architectResult = await runArchitectInternal(
|
||||
taskPrompt,
|
||||
preset: preset,
|
||||
selectedSkills: selectedSkills,
|
||||
);
|
||||
steps.add(
|
||||
CollaborationStep(
|
||||
role: 'architect',
|
||||
status: StepStatus.completed,
|
||||
output: architectResult.output,
|
||||
duration: architectResult.duration,
|
||||
),
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.architect),
|
||||
message: '完成任务分析并生成执行分解。',
|
||||
pending: false,
|
||||
error: false,
|
||||
role: 'architect',
|
||||
data: <String, dynamic>{
|
||||
'taskCount': architectResult.decomposedTasks.length,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// === Phase 2: Engineer 实现 ===
|
||||
throwIfAbortedInternal();
|
||||
logInternal(
|
||||
CollaborationLogLevel.info,
|
||||
'🔧',
|
||||
'${roleLabelInternal(MultiAgentRole.engineer)} 开始实现...',
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.engineer),
|
||||
message: '${roleLabelInternal(MultiAgentRole.engineer)} 开始实现任务…',
|
||||
pending: true,
|
||||
error: false,
|
||||
role: 'engineer',
|
||||
),
|
||||
);
|
||||
final engineerResult = await runEngineerInternal(
|
||||
architectResult.decomposedTasks,
|
||||
workingDirectory,
|
||||
attachments,
|
||||
preset: preset,
|
||||
selectedSkills: selectedSkills,
|
||||
);
|
||||
steps.add(
|
||||
CollaborationStep(
|
||||
role: 'engineer',
|
||||
status: StepStatus.completed,
|
||||
output: engineerResult.output,
|
||||
duration: engineerResult.duration,
|
||||
),
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.engineer),
|
||||
message: '完成首轮实现。',
|
||||
pending: false,
|
||||
error: false,
|
||||
role: 'engineer',
|
||||
),
|
||||
);
|
||||
|
||||
// === Phase 3: Tester 审阅 ===
|
||||
throwIfAbortedInternal();
|
||||
logInternal(
|
||||
CollaborationLogLevel.info,
|
||||
'🔍',
|
||||
'${roleLabelInternal(MultiAgentRole.testerDoc)} 开始审阅...',
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.testerDoc),
|
||||
message: '${roleLabelInternal(MultiAgentRole.testerDoc)} 开始审阅实现…',
|
||||
pending: true,
|
||||
error: false,
|
||||
role: 'tester',
|
||||
),
|
||||
);
|
||||
final testerResult = await runTesterInternal(
|
||||
engineerResult.codeOutput,
|
||||
preset: preset,
|
||||
);
|
||||
steps.add(
|
||||
CollaborationStep(
|
||||
role: 'tester',
|
||||
status: StepStatus.completed,
|
||||
output: testerResult.output,
|
||||
duration: testerResult.duration,
|
||||
score: testerResult.score,
|
||||
),
|
||||
);
|
||||
emitEventInternal(
|
||||
onEvent,
|
||||
MultiAgentRunEvent(
|
||||
type: 'step',
|
||||
title: roleLabelInternal(MultiAgentRole.testerDoc),
|
||||
message: '完成代码审阅。',
|
||||
pending: false,
|
||||
error: false,
|
||||
role: 'tester',
|
||||
score: testerResult.score,
|
||||
),
|
||||
);
|
||||
|
||||
// === Phase 4: 迭代审阅循环(如需要)===
|
||||
if (testerResult.score < configInternal.minAcceptableScore) {
|
||||
logInternal(
|
||||
CollaborationLogLevel.warning,
|
||||
'⚠️',
|
||||
'质量评分 ${testerResult.score}/10 未达标,开始迭代审阅...',
|
||||
);
|
||||
|
||||
for (var i = 0; i < configInternal.maxIterations; i++) {
|
||||
throwIfAbortedInternal();
|
||||
currentIterationInternal = i + 1;
|
||||
logInternal(
|
||||
CollaborationLogLevel.info,
|
||||
'🔄',
|
||||
'迭代 $currentIterationInternal/${configInternal.maxIterations}...',
|
||||
);
|
||||
notifyListeners();
|
||||
|
||||
// Lead Engineer 接收反馈并修复
|
||||
final fixedResult = await runFixInternal(
|
||||
engineerResult.codeOutput,
|
||||
testerResult.feedback,
|
||||
workingDirectory,
|
||||
preset: preset,
|
||||
);
|
||||
steps.add(
|
||||
CollaborationStep(
|
||||
role: 'engineer',
|
||||
status: StepStatus.completed,
|
||||
output: fixedResult.output,
|
||||
duration: fixedResult.duration,
|
||||
iteration: currentIterationInternal,
|
||||
),
|
||||
);
|
||||
|
||||
// Tester 重新审阅
|
||||
final reReview = await runTesterInternal(
|
||||
fixedResult.codeOutput,
|
||||
preset: preset,
|
||||
);
|
||||
steps.add(
|
||||
CollaborationStep(
|
||||
role: 'tester',
|
||||
status: StepStatus.completed,
|
||||
output: reReview.output,
|
||||
duration: reReview.duration,
|
||||
score: reReview.score,
|
||||
iteration: currentIterationInternal,
|
||||
),
|
||||
);
|
||||
|
||||
if (reReview.score >= configInternal.minAcceptableScore) {
|
||||
logInternal(
|
||||
CollaborationLogLevel.success,
|
||||
'✅',
|
||||
'质量达标 (${reReview.score}/10),迭代结束',
|
||||
);
|
||||
engineerResult.codeOutput = fixedResult.codeOutput;
|
||||
break;
|
||||
} else if (currentIterationInternal >= configInternal.maxIterations) {
|
||||
logInternal(
|
||||
CollaborationLogLevel.error,
|
||||
'❌',
|
||||
'达到最大迭代次数 ${configInternal.maxIterations},质量仍未达标',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logInternal(
|
||||
CollaborationLogLevel.success,
|
||||
'✅',
|
||||
'质量达标 (${testerResult.score}/10),无需迭代',
|
||||
);
|
||||
}
|
||||
|
||||
final duration = DateTime.now().difference(startTime);
|
||||
isRunningInternal = false;
|
||||
notifyListeners();
|
||||
|
||||
return CollaborationResult(
|
||||
success: true,
|
||||
steps: steps,
|
||||
finalCode: engineerResult.codeOutput,
|
||||
finalScore: testerResult.score,
|
||||
duration: duration,
|
||||
iterations: currentIterationInternal,
|
||||
);
|
||||
} catch (e) {
|
||||
lastErrorInternal = e.toString();
|
||||
logInternal(CollaborationLogLevel.error, '❌', '协作失败: $lastErrorInternal');
|
||||
isRunningInternal = false;
|
||||
notifyListeners();
|
||||
|
||||
return CollaborationResult(
|
||||
success: false,
|
||||
steps: steps,
|
||||
finalCode: '',
|
||||
finalScore: 0,
|
||||
duration: DateTime.now().difference(startTime),
|
||||
iterations: currentIterationInternal,
|
||||
error: lastErrorInternal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 记录日志
|
||||
void logInternal(CollaborationLogLevel level, String emoji, String message) {
|
||||
logEntriesInternal.add(
|
||||
CollaborationLogEntry(
|
||||
timestamp: DateTime.now(),
|
||||
level: level,
|
||||
emoji: emoji,
|
||||
message: message,
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 清除日志
|
||||
void clearLogs() {
|
||||
logEntriesInternal.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@ -1,200 +0,0 @@
|
||||
// ignore_for_file: unused_import, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'embedded_agent_launch_policy.dart';
|
||||
import 'go_core.dart';
|
||||
import 'aris_llm_chat_client.dart';
|
||||
import 'multi_agent_frameworks.dart';
|
||||
import 'runtime_models.dart';
|
||||
import 'multi_agent_orchestrator_workflow.dart';
|
||||
import 'multi_agent_orchestrator_support.dart';
|
||||
import 'multi_agent_orchestrator_core.dart';
|
||||
|
||||
typedef CliProcessStarter =
|
||||
Future<Process> Function(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
Map<String, String>? environment,
|
||||
String? workingDirectory,
|
||||
});
|
||||
|
||||
/// 协作日志条目
|
||||
class CollaborationLogEntry {
|
||||
const CollaborationLogEntry({
|
||||
required this.timestamp,
|
||||
required this.level,
|
||||
required this.emoji,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
final DateTime timestamp;
|
||||
final CollaborationLogLevel level;
|
||||
final String emoji;
|
||||
final String message;
|
||||
|
||||
String get formattedTime {
|
||||
final h = timestamp.hour.toString().padLeft(2, '0');
|
||||
final m = timestamp.minute.toString().padLeft(2, '0');
|
||||
final s = timestamp.second.toString().padLeft(2, '0');
|
||||
return '$h:$m:$s';
|
||||
}
|
||||
}
|
||||
|
||||
enum CollaborationLogLevel { debug, info, warning, error, success }
|
||||
|
||||
/// CLI 执行结果
|
||||
class CliResult {
|
||||
const CliResult({
|
||||
required this.output,
|
||||
required this.error,
|
||||
required this.exitCode,
|
||||
});
|
||||
|
||||
final String output;
|
||||
final String error;
|
||||
final int exitCode;
|
||||
|
||||
bool get success => exitCode == 0 && error.isEmpty;
|
||||
}
|
||||
|
||||
/// Architect 执行结果
|
||||
class ArchitectResult {
|
||||
ArchitectResult({
|
||||
required this.output,
|
||||
required this.decomposedTasks,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
final String output;
|
||||
final List<SubTask> decomposedTasks;
|
||||
final Duration duration;
|
||||
}
|
||||
|
||||
/// Engineer 执行结果
|
||||
class EngineerResult {
|
||||
EngineerResult({
|
||||
required this.output,
|
||||
required this.codeOutput,
|
||||
required this.completedTasks,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
final String output;
|
||||
String codeOutput;
|
||||
final List<SubTask> completedTasks;
|
||||
final Duration duration;
|
||||
}
|
||||
|
||||
/// Tester 执行结果
|
||||
class TesterResult {
|
||||
TesterResult({
|
||||
required this.output,
|
||||
required this.score,
|
||||
required this.feedback,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
final String output;
|
||||
final int score;
|
||||
final String feedback;
|
||||
final Duration duration;
|
||||
}
|
||||
|
||||
/// 协作步骤
|
||||
class CollaborationStep {
|
||||
const CollaborationStep({
|
||||
required this.role,
|
||||
required this.status,
|
||||
required this.output,
|
||||
required this.duration,
|
||||
this.iteration,
|
||||
this.score,
|
||||
});
|
||||
|
||||
final String role;
|
||||
final StepStatus status;
|
||||
final String output;
|
||||
final Duration duration;
|
||||
final int? iteration;
|
||||
final int? score;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'role': role,
|
||||
'status': status.name,
|
||||
'output': output,
|
||||
'durationMs': duration.inMilliseconds,
|
||||
if (iteration != null) 'iteration': iteration,
|
||||
if (score != null) 'score': score,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum StepStatus { pending, running, completed, failed }
|
||||
|
||||
/// 子任务
|
||||
class SubTask {
|
||||
const SubTask({
|
||||
required this.id,
|
||||
required this.description,
|
||||
required this.order,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String description;
|
||||
final int order;
|
||||
final SubTaskType type;
|
||||
}
|
||||
|
||||
enum SubTaskType { design, implementation, testing, documentation, deployment }
|
||||
|
||||
/// 附件
|
||||
class CollaborationAttachment {
|
||||
const CollaborationAttachment({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.path,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String description;
|
||||
final String path;
|
||||
}
|
||||
|
||||
/// 协作最终结果
|
||||
class CollaborationResult {
|
||||
const CollaborationResult({
|
||||
required this.success,
|
||||
required this.steps,
|
||||
required this.finalCode,
|
||||
required this.finalScore,
|
||||
required this.duration,
|
||||
required this.iterations,
|
||||
this.error,
|
||||
});
|
||||
|
||||
final bool success;
|
||||
final List<CollaborationStep> steps;
|
||||
final String finalCode;
|
||||
final int finalScore;
|
||||
final Duration duration;
|
||||
final int iterations;
|
||||
final String? error;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'success': success,
|
||||
'steps': steps.map((item) => item.toJson()).toList(growable: false),
|
||||
'finalCode': finalCode,
|
||||
'finalScore': finalScore,
|
||||
'durationMs': duration.inMilliseconds,
|
||||
'iterations': iterations,
|
||||
if (error != null) 'error': error,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,269 +0,0 @@
|
||||
// ignore_for_file: unused_import, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'embedded_agent_launch_policy.dart';
|
||||
import 'go_core.dart';
|
||||
import 'aris_llm_chat_client.dart';
|
||||
import 'multi_agent_frameworks.dart';
|
||||
import 'runtime_models.dart';
|
||||
import 'multi_agent_orchestrator_protocol.dart';
|
||||
import 'multi_agent_orchestrator_workflow.dart';
|
||||
import 'multi_agent_orchestrator_core.dart';
|
||||
|
||||
extension MultiAgentOrchestratorSupportInternal on MultiAgentOrchestrator {
|
||||
String openAiCompatibleBaseUrlInternal() {
|
||||
final normalized = configInternal.ollamaEndpoint.trim();
|
||||
return normalized.endsWith('/v1') ? normalized : '$normalized/v1';
|
||||
}
|
||||
|
||||
String openAiCompatibleApiKeyInternal() {
|
||||
return 'ollama';
|
||||
}
|
||||
|
||||
String systemPromptForRoleInternal(MultiAgentRole role) {
|
||||
return switch (role) {
|
||||
MultiAgentRole.architect =>
|
||||
'You are the architecture and documentation lane in a multi-agent coding workflow. Focus on requirements, acceptance evidence, task slicing, and milestones.',
|
||||
MultiAgentRole.engineer =>
|
||||
'You are the lead engineer in a multi-agent coding workflow. Produce implementation-oriented output for the critical path.',
|
||||
MultiAgentRole.testerDoc =>
|
||||
'You are the worker-review lane in a multi-agent coding workflow. Review, score, and suggest follow-up fixes and worker follow-ups.',
|
||||
};
|
||||
}
|
||||
|
||||
String roleLabelInternal(MultiAgentRole role) {
|
||||
return switch (role) {
|
||||
MultiAgentRole.architect => 'Architect',
|
||||
MultiAgentRole.engineer => 'Lead Engineer',
|
||||
MultiAgentRole.testerDoc => 'Worker/Review',
|
||||
};
|
||||
}
|
||||
|
||||
String modelForRoleInternal(MultiAgentRole role) {
|
||||
return switch (role) {
|
||||
MultiAgentRole.architect => configInternal.architect.model,
|
||||
MultiAgentRole.engineer => configInternal.engineer.model,
|
||||
MultiAgentRole.testerDoc => configInternal.tester.model,
|
||||
};
|
||||
}
|
||||
|
||||
bool prefersOllamaLaunchInternal({
|
||||
required String tool,
|
||||
required String model,
|
||||
}) {
|
||||
final normalizedTool = tool.trim().toLowerCase();
|
||||
final normalizedModel = model.trim();
|
||||
if (normalizedModel.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (normalizedTool != 'claude' &&
|
||||
normalizedTool != 'codex' &&
|
||||
normalizedTool != 'opencode') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> buildOllamaLaunchArgsInternal({
|
||||
required String tool,
|
||||
required String model,
|
||||
required String prompt,
|
||||
required String cwd,
|
||||
}) {
|
||||
final args = <String>['launch', tool, '--model', model];
|
||||
if (tool == 'claude') {
|
||||
args.add('--yes');
|
||||
args.addAll(<String>['--', '-p', prompt]);
|
||||
return args;
|
||||
}
|
||||
if (tool == 'codex') {
|
||||
args.addAll(<String>[
|
||||
'--',
|
||||
'exec',
|
||||
'--skip-git-repo-check',
|
||||
'--color',
|
||||
'never',
|
||||
if (cwd.isNotEmpty) ...<String>['-C', cwd],
|
||||
prompt,
|
||||
]);
|
||||
return args;
|
||||
}
|
||||
if (tool == 'opencode') {
|
||||
args.addAll(<String>[
|
||||
'--',
|
||||
'run',
|
||||
'--format',
|
||||
'default',
|
||||
if (cwd.isNotEmpty) ...<String>['--dir', cwd],
|
||||
prompt,
|
||||
]);
|
||||
return args;
|
||||
}
|
||||
args.addAll(<String>['--', '-p', prompt]);
|
||||
return args;
|
||||
}
|
||||
|
||||
void throwIfAbortedInternal() {
|
||||
if (abortRequestedInternal) {
|
||||
throw StateError('Multi-agent collaboration aborted.');
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析 Architect 分解的任务
|
||||
List<SubTask> parseDecomposedTasksInternal(String architectOutput) {
|
||||
final tasks = <SubTask>[];
|
||||
final lines = architectOutput.split('\n');
|
||||
|
||||
var order = 1;
|
||||
for (final line in lines) {
|
||||
final trimmed = line.trim();
|
||||
if (trimmed.isEmpty) continue;
|
||||
|
||||
// 匹配 "- 描述" 或 "1. 描述" 格式
|
||||
final dashMatch = RegExp(r'^[-*]\s+(.+)').firstMatch(trimmed);
|
||||
final numMatch = RegExp(r'^\d+[.、)]\s*(.+)').firstMatch(trimmed);
|
||||
|
||||
String? description;
|
||||
if (dashMatch != null) {
|
||||
description = dashMatch.group(1);
|
||||
} else if (numMatch != null) {
|
||||
description = numMatch.group(1);
|
||||
}
|
||||
|
||||
if (description != null && description.isNotEmpty) {
|
||||
// 去除复杂度等技术注释
|
||||
description = description.replaceAll(RegExp(r'\s*\|.*'), '').trim();
|
||||
|
||||
// 判断任务类型
|
||||
SubTaskType type = SubTaskType.implementation;
|
||||
final lower = description.toLowerCase();
|
||||
if (lower.contains('测试') || lower.contains('test')) {
|
||||
type = SubTaskType.testing;
|
||||
} else if (lower.contains('文档') || lower.contains('doc')) {
|
||||
type = SubTaskType.documentation;
|
||||
} else if (lower.contains('设计') || lower.contains('design')) {
|
||||
type = SubTaskType.design;
|
||||
} else if (lower.contains('部署') || lower.contains('deploy')) {
|
||||
type = SubTaskType.deployment;
|
||||
}
|
||||
|
||||
tasks.add(
|
||||
SubTask(
|
||||
id: order.toString(),
|
||||
description: description,
|
||||
order: order,
|
||||
type: type,
|
||||
),
|
||||
);
|
||||
order++;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果解析失败,至少返回一个包含完整需求的子任务
|
||||
if (tasks.isEmpty) {
|
||||
tasks.add(
|
||||
SubTask(
|
||||
id: '1',
|
||||
description: architectOutput.length > 200
|
||||
? '${architectOutput.substring(0, 200)}...'
|
||||
: architectOutput,
|
||||
order: 1,
|
||||
type: SubTaskType.implementation,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
/// 解析审阅评分
|
||||
int parseReviewScoreInternal(String output) {
|
||||
// 尝试匹配 "评分 (1-10)" 模式
|
||||
final patterns = [
|
||||
RegExp(r'评分\s*\(?[1100]\)?\s*[::]?\s*(\d+)'),
|
||||
RegExp(r'score\s*[::]?\s*(\d+)', caseSensitive: false),
|
||||
RegExp(r'评分[::\s]*(\d+)'),
|
||||
RegExp(r'\*\*(\d+)\s*/\s*10\*\*'),
|
||||
RegExp(r'(\d+)\s*/\s*10'),
|
||||
];
|
||||
|
||||
for (final pattern in patterns) {
|
||||
final match = pattern.firstMatch(output);
|
||||
if (match != null) {
|
||||
final scoreStr = match.group(1)!;
|
||||
final score = int.tryParse(
|
||||
scoreStr.replaceAll('1', '1').replaceAll('0', '0'),
|
||||
);
|
||||
if (score != null && score >= 1 && score <= 10) {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 默认中等评分
|
||||
return 5;
|
||||
}
|
||||
|
||||
/// 提取审阅反馈
|
||||
String extractFeedbackInternal(String output) {
|
||||
final feedbackIndex = output.indexOf(RegExp(r'##?\s*问题|##?\s*改进|##?\s*建议'));
|
||||
if (feedbackIndex >= 0) {
|
||||
final endIndex = output.indexOf(
|
||||
RegExp(r'##?\s*测试|##?\s*文档'),
|
||||
feedbackIndex + 1,
|
||||
);
|
||||
if (endIndex > feedbackIndex) {
|
||||
return output.substring(feedbackIndex, endIndex).trim();
|
||||
}
|
||||
return output.substring(feedbackIndex).trim();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/// 构建 Ollama 环境变量
|
||||
Map<String, String> buildCliEnvVarsInternal({required String tool}) {
|
||||
final baseEnv = <String, String>{...Platform.environment};
|
||||
final ollamaEndpoint = configInternal.ollamaEndpoint.trim();
|
||||
if (ollamaEndpoint.isNotEmpty) {
|
||||
baseEnv['OLLAMA_BASE_URL'] = ollamaEndpoint;
|
||||
baseEnv['OLLAMA_HOST'] = ollamaEndpoint;
|
||||
baseEnv['OPENAI_API_KEY'] = 'ollama';
|
||||
baseEnv['OPENAI_BASE_URL'] = ollamaEndpoint.endsWith('/v1')
|
||||
? ollamaEndpoint
|
||||
: '$ollamaEndpoint/v1';
|
||||
}
|
||||
if (tool == 'claude' || tool == 'codex') {
|
||||
baseEnv['ANTHROPIC_AUTH_TOKEN'] = 'ollama';
|
||||
baseEnv['ANTHROPIC_API_KEY'] = '';
|
||||
baseEnv['ANTHROPIC_BASE_URL'] = ollamaEndpoint;
|
||||
}
|
||||
return baseEnv;
|
||||
}
|
||||
|
||||
/// 解析 CLI 工具路径
|
||||
String resolveCliPathInternal(String tool) {
|
||||
switch (tool) {
|
||||
case 'claude':
|
||||
return 'claude';
|
||||
case 'codex':
|
||||
return 'codex';
|
||||
case 'gemini':
|
||||
return 'gemini';
|
||||
case 'opencode':
|
||||
return 'opencode';
|
||||
default:
|
||||
return tool;
|
||||
}
|
||||
}
|
||||
|
||||
void emitEventInternal(
|
||||
void Function(MultiAgentRunEvent event)? onEvent,
|
||||
MultiAgentRunEvent event,
|
||||
) {
|
||||
onEvent?.call(event);
|
||||
}
|
||||
}
|
||||
@ -1,456 +0,0 @@
|
||||
// ignore_for_file: unused_import, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'embedded_agent_launch_policy.dart';
|
||||
import 'go_core.dart';
|
||||
import 'aris_llm_chat_client.dart';
|
||||
import 'multi_agent_frameworks.dart';
|
||||
import 'runtime_models.dart';
|
||||
import 'multi_agent_orchestrator_protocol.dart';
|
||||
import 'multi_agent_orchestrator_support.dart';
|
||||
import 'multi_agent_orchestrator_core.dart';
|
||||
|
||||
extension MultiAgentOrchestratorWorkflowInternal on MultiAgentOrchestrator {
|
||||
/// 运行 Architect(调度/文档分析)
|
||||
Future<ArchitectResult> runArchitectInternal(
|
||||
String task, {
|
||||
required FrameworkPreset preset,
|
||||
required List<String> selectedSkills,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
try {
|
||||
// 根据配置选择 Architect 工具
|
||||
if (configInternal.architectEnabled) {
|
||||
final tool = await resolveToolForRoleInternal(
|
||||
MultiAgentRole.architect,
|
||||
configInternal.architectTool,
|
||||
);
|
||||
final instructionBlock = await preset.roleInstructionBlock(
|
||||
role: MultiAgentRole.architect,
|
||||
tool: tool,
|
||||
selectedSkills: selectedSkills,
|
||||
);
|
||||
final result = await runCliPromptInternal(
|
||||
role: MultiAgentRole.architect,
|
||||
tool: tool,
|
||||
model: resolvedModelForRoleInternal(
|
||||
MultiAgentRole.architect,
|
||||
configuredModel: configInternal.architectModel,
|
||||
),
|
||||
prompt: buildArchitectPromptInternal(
|
||||
task,
|
||||
selectedSkills,
|
||||
instructionBlock,
|
||||
),
|
||||
cwd: '',
|
||||
);
|
||||
stopwatch.stop();
|
||||
|
||||
// 解析分解后的任务
|
||||
final tasks = parseDecomposedTasksInternal(result.output);
|
||||
return ArchitectResult(
|
||||
output: result.output,
|
||||
decomposedTasks: tasks,
|
||||
duration: stopwatch.elapsed,
|
||||
);
|
||||
} else {
|
||||
// Architect 被禁用,直接返回原任务作为单一子任务
|
||||
stopwatch.stop();
|
||||
return ArchitectResult(
|
||||
output: task,
|
||||
decomposedTasks: [
|
||||
SubTask(
|
||||
id: '1',
|
||||
description: task,
|
||||
order: 1,
|
||||
type: SubTaskType.implementation,
|
||||
),
|
||||
],
|
||||
duration: stopwatch.elapsed,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
stopwatch.stop();
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// 运行 Lead Engineer(主实现)
|
||||
Future<EngineerResult> runEngineerInternal(
|
||||
List<SubTask> tasks,
|
||||
String workingDirectory,
|
||||
List<CollaborationAttachment> attachments, {
|
||||
required FrameworkPreset preset,
|
||||
required List<String> selectedSkills,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final tool = await resolveToolForRoleInternal(
|
||||
MultiAgentRole.engineer,
|
||||
configInternal.engineerTool,
|
||||
);
|
||||
final instructionBlock = await preset.roleInstructionBlock(
|
||||
role: MultiAgentRole.engineer,
|
||||
tool: tool,
|
||||
selectedSkills: selectedSkills,
|
||||
);
|
||||
|
||||
final taskList = tasks
|
||||
.map((t) => '## ${t.order}. ${t.description}')
|
||||
.join('\n\n');
|
||||
|
||||
final prompt =
|
||||
'''
|
||||
$instructionBlock
|
||||
|
||||
你是一个资深工程师,负责完成以下编码任务:
|
||||
|
||||
### 任务列表
|
||||
$taskList
|
||||
|
||||
### 工作目录
|
||||
$workingDirectory
|
||||
|
||||
### 附件信息
|
||||
${attachments.map((a) => '- ${a.name}: ${a.description}').join('\n')}
|
||||
|
||||
### 优先技能
|
||||
${selectedSkills.isEmpty ? '- 无' : selectedSkills.map((item) => '- $item').join('\n')}
|
||||
|
||||
请完成这些任务,输出完整的代码实现。
|
||||
''';
|
||||
|
||||
final result = await runCliPromptInternal(
|
||||
role: MultiAgentRole.engineer,
|
||||
tool: tool,
|
||||
model: resolvedModelForRoleInternal(
|
||||
MultiAgentRole.engineer,
|
||||
configuredModel: configInternal.engineerModel,
|
||||
),
|
||||
prompt: prompt,
|
||||
cwd: workingDirectory,
|
||||
);
|
||||
stopwatch.stop();
|
||||
|
||||
return EngineerResult(
|
||||
output: result.output,
|
||||
codeOutput: result.output,
|
||||
completedTasks: tasks,
|
||||
duration: stopwatch.elapsed,
|
||||
);
|
||||
}
|
||||
|
||||
/// 运行 Worker/Review(代码审阅)
|
||||
Future<TesterResult> runTesterInternal(
|
||||
String codeOutput, {
|
||||
required FrameworkPreset preset,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final tool = await resolveToolForRoleInternal(
|
||||
MultiAgentRole.testerDoc,
|
||||
configInternal.testerTool,
|
||||
);
|
||||
final instructionBlock = await preset.roleInstructionBlock(
|
||||
role: MultiAgentRole.testerDoc,
|
||||
tool: tool,
|
||||
selectedSkills: const <String>[],
|
||||
);
|
||||
|
||||
final prompt =
|
||||
'''
|
||||
$instructionBlock
|
||||
|
||||
请审阅以下代码,并按以下格式输出:
|
||||
|
||||
## 评分 (1-10)
|
||||
[1-10 的分数,10 最高]
|
||||
|
||||
## 问题列表
|
||||
[发现的问题,格式:- 问题描述 (严重程度: 高/中/低)]
|
||||
|
||||
## 改进建议
|
||||
[具体的改进建议]
|
||||
|
||||
## 测试用例
|
||||
```[语言]
|
||||
[生成的测试用例代码]
|
||||
```
|
||||
|
||||
## 文档建议
|
||||
[如有需要补充的文档说明]
|
||||
|
||||
### 待审阅代码
|
||||
${codeOutput.length > 4000 ? '${codeOutput.substring(0, 4000)}\n...[代码已截断]' : codeOutput}
|
||||
''';
|
||||
|
||||
final testerModel = resolvedModelForRoleInternal(
|
||||
MultiAgentRole.testerDoc,
|
||||
configuredModel: configInternal.testerModel,
|
||||
);
|
||||
final result = configInternal.usesAris && tool == 'claude'
|
||||
? await runArisTesterViaClaudeReviewInternal(
|
||||
model: testerModel,
|
||||
prompt: prompt,
|
||||
)
|
||||
: await runCliPromptInternal(
|
||||
role: MultiAgentRole.testerDoc,
|
||||
tool: tool,
|
||||
model: testerModel,
|
||||
prompt: prompt,
|
||||
cwd: '',
|
||||
);
|
||||
stopwatch.stop();
|
||||
|
||||
final score = parseReviewScoreInternal(result.output);
|
||||
final feedback = extractFeedbackInternal(result.output);
|
||||
|
||||
return TesterResult(
|
||||
output: result.output,
|
||||
score: score,
|
||||
feedback: feedback,
|
||||
duration: stopwatch.elapsed,
|
||||
);
|
||||
}
|
||||
|
||||
/// 运行修复(迭代循环中)
|
||||
Future<EngineerResult> runFixInternal(
|
||||
String originalCode,
|
||||
String feedback,
|
||||
String workingDirectory, {
|
||||
required FrameworkPreset preset,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final tool = await resolveToolForRoleInternal(
|
||||
MultiAgentRole.engineer,
|
||||
configInternal.engineerTool,
|
||||
);
|
||||
final instructionBlock = await preset.roleInstructionBlock(
|
||||
role: MultiAgentRole.engineer,
|
||||
tool: tool,
|
||||
selectedSkills: const <String>[],
|
||||
);
|
||||
|
||||
final prompt =
|
||||
'''
|
||||
$instructionBlock
|
||||
|
||||
你是一个资深工程师。请根据审阅反馈修复代码。
|
||||
|
||||
## 审阅反馈
|
||||
$feedback
|
||||
|
||||
## 原始代码
|
||||
$originalCode
|
||||
|
||||
请完成修复,输出修复后的完整代码。
|
||||
''';
|
||||
|
||||
final result = await runCliPromptInternal(
|
||||
role: MultiAgentRole.engineer,
|
||||
tool: tool,
|
||||
model: resolvedModelForRoleInternal(
|
||||
MultiAgentRole.engineer,
|
||||
configuredModel: configInternal.engineerModel,
|
||||
),
|
||||
prompt: prompt,
|
||||
cwd: workingDirectory,
|
||||
);
|
||||
stopwatch.stop();
|
||||
|
||||
return EngineerResult(
|
||||
output: result.output,
|
||||
codeOutput: result.output,
|
||||
completedTasks: [],
|
||||
duration: stopwatch.elapsed,
|
||||
);
|
||||
}
|
||||
|
||||
/// 通用的 CLI 进程执行方法 (DEPRECATED: Use bridge instead)
|
||||
Future<CliResult> runCliPromptInternal({
|
||||
required MultiAgentRole role,
|
||||
required String tool,
|
||||
required String model,
|
||||
required String prompt,
|
||||
required String cwd,
|
||||
}) async {
|
||||
// In cloud-neutral architecture, local CLI execution is disabled.
|
||||
// We should fallback to OpenAI compatible API or bridge execution.
|
||||
return runArisFallbackInternal(role: role, model: model, prompt: prompt);
|
||||
}
|
||||
|
||||
/// 构建 Architect 的 Prompt
|
||||
String buildArchitectPromptInternal(
|
||||
String task,
|
||||
List<String> selectedSkills,
|
||||
String instructionBlock,
|
||||
) {
|
||||
return '''
|
||||
$instructionBlock
|
||||
|
||||
你是一个多 Agent 协作调度者。请先收敛 requirements -> acceptance evidence,再输出可执行的主程/worker分工。
|
||||
|
||||
## 用户需求
|
||||
$task
|
||||
|
||||
## 优先技能
|
||||
${selectedSkills.isEmpty ? '- 无' : selectedSkills.map((item) => '- $item').join('\n')}
|
||||
|
||||
请输出:
|
||||
1. 任务概述(2-3 句话)
|
||||
2. 子任务列表(3-5 个),每个子任务包含:
|
||||
- 任务编号和描述
|
||||
- 负责角色(文档/主程/worker)
|
||||
- 接受标准
|
||||
- 关键技术点
|
||||
3. 推荐的执行顺序与关键里程碑
|
||||
|
||||
请严格按以下格式输出:
|
||||
## 概述
|
||||
[你的概述]
|
||||
|
||||
## 子任务
|
||||
1. [任务描述] | 角色:[文档/主程/worker] | 接受标准:[可验证结果] | 关键技术:[技术点]
|
||||
2. [任务描述] | 角色:[文档/主程/worker] | 接受标准:[可验证结果] | 关键技术:[技术点]
|
||||
...
|
||||
''';
|
||||
}
|
||||
|
||||
Future<String> resolveToolForRoleInternal(
|
||||
MultiAgentRole role,
|
||||
String configuredTool,
|
||||
) async {
|
||||
return configuredTool;
|
||||
}
|
||||
|
||||
String resolvedModelForRoleInternal(
|
||||
MultiAgentRole role, {
|
||||
required String configuredModel,
|
||||
}) {
|
||||
final trimmed = configuredModel.trim();
|
||||
if (trimmed.isNotEmpty) {
|
||||
return trimmed;
|
||||
}
|
||||
switch (role) {
|
||||
case MultiAgentRole.architect:
|
||||
return 'kimi-k2.5:cloud';
|
||||
case MultiAgentRole.engineer:
|
||||
return 'minimax-m2.7:cloud';
|
||||
case MultiAgentRole.testerDoc:
|
||||
return 'glm-5:cloud';
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> binaryExistsInternal(String command) async {
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<CliResult> runArisFallbackInternal({
|
||||
required MultiAgentRole role,
|
||||
required String model,
|
||||
required String prompt,
|
||||
}) async {
|
||||
if (role == MultiAgentRole.testerDoc) {
|
||||
final viaLlmChat = await runArisTesterViaLlmChatInternal(
|
||||
model: model,
|
||||
prompt: prompt,
|
||||
);
|
||||
if (viaLlmChat.success) {
|
||||
return viaLlmChat;
|
||||
}
|
||||
}
|
||||
return runOpenAiCompatiblePromptInternal(
|
||||
role: role,
|
||||
model: model,
|
||||
prompt: prompt,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CliResult> runArisTesterViaLlmChatInternal({
|
||||
required String model,
|
||||
required String prompt,
|
||||
}) async {
|
||||
return runOpenAiCompatiblePromptInternal(
|
||||
role: MultiAgentRole.testerDoc,
|
||||
model: model,
|
||||
prompt: prompt,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CliResult> runArisTesterViaClaudeReviewInternal({
|
||||
required String model,
|
||||
required String prompt,
|
||||
}) async {
|
||||
return runArisFallbackInternal(
|
||||
role: MultiAgentRole.testerDoc,
|
||||
model: model,
|
||||
prompt: prompt,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CliResult> runOpenAiCompatiblePromptInternal({
|
||||
required MultiAgentRole role,
|
||||
required String model,
|
||||
required String prompt,
|
||||
}) async {
|
||||
final client = httpClientFactoryInternal();
|
||||
activeHttpClientInternal = client;
|
||||
try {
|
||||
final request = await client.postUrl(
|
||||
Uri.parse(
|
||||
'${openAiCompatibleBaseUrlInternal().replaceAll(RegExp(r'/$'), '')}/chat/completions',
|
||||
),
|
||||
);
|
||||
request.headers.set(HttpHeaders.contentTypeHeader, 'application/json');
|
||||
request.headers.set(
|
||||
HttpHeaders.authorizationHeader,
|
||||
'Bearer ${openAiCompatibleApiKeyInternal()}',
|
||||
);
|
||||
request.add(
|
||||
utf8.encode(
|
||||
jsonEncode(<String, dynamic>{
|
||||
'model': model,
|
||||
'stream': false,
|
||||
'messages': <Map<String, String>>[
|
||||
<String, String>{
|
||||
'role': 'system',
|
||||
'content': systemPromptForRoleInternal(role),
|
||||
},
|
||||
<String, String>{'role': 'user', 'content': prompt},
|
||||
],
|
||||
}),
|
||||
),
|
||||
);
|
||||
final response = await request.close().timeout(
|
||||
Duration(seconds: configInternal.timeoutSeconds),
|
||||
);
|
||||
final body = await utf8.decodeStream(response);
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
return CliResult(
|
||||
output: '',
|
||||
error: body,
|
||||
exitCode: response.statusCode,
|
||||
);
|
||||
}
|
||||
final decoded = jsonDecode(body) as Map<String, dynamic>;
|
||||
final choices = decoded['choices'] as List? ?? const <Object>[];
|
||||
final firstChoice = choices.isNotEmpty ? choices.first : null;
|
||||
final output =
|
||||
((firstChoice as Map?)?['message'] as Map?)?['content']?.toString() ??
|
||||
'';
|
||||
return CliResult(output: output, error: '', exitCode: 0);
|
||||
} catch (error) {
|
||||
return CliResult(output: '', error: error.toString(), exitCode: -1);
|
||||
} finally {
|
||||
activeHttpClientInternal = null;
|
||||
try {
|
||||
client.close(force: true);
|
||||
} catch (_) {
|
||||
// Best effort only.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,5 +6,3 @@ export 'runtime_models_settings_snapshot.dart';
|
||||
export 'runtime_models_ui_state.dart';
|
||||
export 'runtime_models_runtime_payloads.dart';
|
||||
export 'runtime_models_gateway_entities.dart';
|
||||
export 'runtime_models_multi_agent.dart';
|
||||
export 'multi_agent_orchestrator_protocol.dart';
|
||||
|
||||
@ -1,107 +1,3 @@
|
||||
// ignore_for_file: unused_import, unnecessary_import
|
||||
|
||||
import 'dart:convert';
|
||||
import '../i18n/app_language.dart';
|
||||
import '../models/app_models.dart';
|
||||
import 'runtime_models_connection.dart';
|
||||
import 'runtime_models_profiles.dart';
|
||||
import 'runtime_models_configs.dart';
|
||||
import 'runtime_models_settings_snapshot.dart';
|
||||
import 'runtime_models_runtime_payloads.dart';
|
||||
import 'runtime_models_gateway_entities.dart';
|
||||
|
||||
enum MultiAgentRole {
|
||||
architect, // 调度/文档:需求收口、接受标准、工作流设计
|
||||
engineer, // 主程:关键实现、重构、集成
|
||||
testerDoc, // worker/review:并行切片、复审、回归建议
|
||||
}
|
||||
|
||||
enum MultiAgentFramework { native, aris }
|
||||
|
||||
extension MultiAgentFrameworkCopy on MultiAgentFramework {
|
||||
String get label => switch (this) {
|
||||
MultiAgentFramework.native => appText('原生多 Agent', 'Native Multi-Agent'),
|
||||
MultiAgentFramework.aris => appText('ARIS 框架', 'ARIS Framework'),
|
||||
};
|
||||
|
||||
static MultiAgentFramework fromJsonValue(String? value) {
|
||||
return MultiAgentFramework.values.firstWhere(
|
||||
(item) => item.name == value,
|
||||
orElse: () => MultiAgentFramework.native,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MultiAgentRoleCopy on MultiAgentRole {
|
||||
String get label => switch (this) {
|
||||
MultiAgentRole.architect => 'Architect(调度/文档)',
|
||||
MultiAgentRole.engineer => 'Lead Engineer(主程)',
|
||||
MultiAgentRole.testerDoc => 'Worker/Review(Worker 池)',
|
||||
};
|
||||
|
||||
String get description => switch (this) {
|
||||
MultiAgentRole.architect => '负责需求收口、接受标准、文档与协作调度',
|
||||
MultiAgentRole.engineer => '负责主实现、关键改动、集成收口',
|
||||
MultiAgentRole.testerDoc => '负责并行 worker、复审、回归和补充说明',
|
||||
};
|
||||
}
|
||||
|
||||
enum AiGatewayInjectionPolicy { disabled, launchScoped, appManagedDefault }
|
||||
|
||||
extension AiGatewayInjectionPolicyCopy on AiGatewayInjectionPolicy {
|
||||
String get label => switch (this) {
|
||||
AiGatewayInjectionPolicy.disabled => appText('禁用', 'Disabled'),
|
||||
AiGatewayInjectionPolicy.launchScoped => appText(
|
||||
'仅当前协作运行',
|
||||
'Launch scoped',
|
||||
),
|
||||
AiGatewayInjectionPolicy.appManagedDefault => appText(
|
||||
'XWorkmate 默认',
|
||||
'XWorkmate default',
|
||||
),
|
||||
};
|
||||
|
||||
static AiGatewayInjectionPolicy fromJsonValue(String? value) {
|
||||
return AiGatewayInjectionPolicy.values.firstWhere(
|
||||
(item) => item.name == value,
|
||||
orElse: () => AiGatewayInjectionPolicy.appManagedDefault,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 单个 Agent Worker 配置
|
||||
class AgentWorkerConfig {
|
||||
const AgentWorkerConfig({
|
||||
required this.role,
|
||||
required this.cliTool,
|
||||
required this.model,
|
||||
required this.enabled,
|
||||
this.maxRetries = 2,
|
||||
});
|
||||
|
||||
final MultiAgentRole role;
|
||||
final String cliTool; // e.g. 'claude' | 'codex' | 'opencode' | 'gemini'
|
||||
final String model;
|
||||
final bool enabled;
|
||||
final int maxRetries;
|
||||
|
||||
AgentWorkerConfig copyWith({
|
||||
MultiAgentRole? role,
|
||||
String? cliTool,
|
||||
String? model,
|
||||
bool? enabled,
|
||||
int? maxRetries,
|
||||
}) {
|
||||
return AgentWorkerConfig(
|
||||
role: role ?? this.role,
|
||||
cliTool: cliTool ?? this.cliTool,
|
||||
model: model ?? this.model,
|
||||
enabled: enabled ?? this.enabled,
|
||||
maxRetries: maxRetries ?? this.maxRetries,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ManagedSkillEntry {
|
||||
const ManagedSkillEntry({
|
||||
required this.key,
|
||||
@ -426,415 +322,3 @@ class ManagedMountTargetState {
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/// 多 Agent 协作配置
|
||||
class MultiAgentConfig {
|
||||
const MultiAgentConfig({
|
||||
required this.enabled,
|
||||
required this.autoSync,
|
||||
required this.framework,
|
||||
required this.arisEnabled,
|
||||
required this.arisMode,
|
||||
required this.arisBundleVersion,
|
||||
required this.arisCompatStatus,
|
||||
required this.architect,
|
||||
required this.engineer,
|
||||
required this.tester,
|
||||
required this.ollamaEndpoint,
|
||||
required this.maxIterations,
|
||||
required this.minAcceptableScore,
|
||||
required this.timeoutSeconds,
|
||||
required this.aiGatewayInjectionPolicy,
|
||||
required this.managedSkills,
|
||||
required this.managedMcpServers,
|
||||
required this.mountTargets,
|
||||
});
|
||||
|
||||
final bool enabled;
|
||||
final bool autoSync;
|
||||
final MultiAgentFramework framework;
|
||||
final bool arisEnabled;
|
||||
final String arisMode;
|
||||
final String arisBundleVersion;
|
||||
final String arisCompatStatus;
|
||||
final AgentWorkerConfig architect;
|
||||
final AgentWorkerConfig engineer;
|
||||
final AgentWorkerConfig tester;
|
||||
final String ollamaEndpoint;
|
||||
final int maxIterations;
|
||||
final int minAcceptableScore;
|
||||
final int timeoutSeconds;
|
||||
final AiGatewayInjectionPolicy aiGatewayInjectionPolicy;
|
||||
final List<ManagedSkillEntry> managedSkills;
|
||||
final List<ManagedMcpServerEntry> managedMcpServers;
|
||||
final List<ManagedMountTargetState> mountTargets;
|
||||
|
||||
/// Architect 配置的便捷访问
|
||||
bool get architectEnabled => architect.enabled;
|
||||
String get architectTool => architect.cliTool;
|
||||
String get architectModel => architect.model;
|
||||
|
||||
/// Engineer 配置的便捷访问
|
||||
String get engineerTool => engineer.cliTool;
|
||||
String get engineerModel => engineer.model;
|
||||
|
||||
/// Tester 配置的便捷访问
|
||||
String get testerTool => tester.cliTool;
|
||||
String get testerModel => tester.model;
|
||||
|
||||
bool get usesAris => arisEnabled || framework == MultiAgentFramework.aris;
|
||||
|
||||
factory MultiAgentConfig.defaults() {
|
||||
return MultiAgentConfig(
|
||||
enabled: false,
|
||||
autoSync: true,
|
||||
framework: MultiAgentFramework.native,
|
||||
arisEnabled: false,
|
||||
arisMode: 'full',
|
||||
arisBundleVersion: '',
|
||||
arisCompatStatus: 'idle',
|
||||
architect: const AgentWorkerConfig(
|
||||
role: MultiAgentRole.architect,
|
||||
cliTool: 'claude',
|
||||
model: 'kimi-k2.5:cloud',
|
||||
enabled: true,
|
||||
),
|
||||
engineer: const AgentWorkerConfig(
|
||||
role: MultiAgentRole.engineer,
|
||||
cliTool: 'codex',
|
||||
model: 'minimax-m2.7:cloud',
|
||||
enabled: true,
|
||||
),
|
||||
tester: const AgentWorkerConfig(
|
||||
role: MultiAgentRole.testerDoc,
|
||||
cliTool: 'opencode',
|
||||
model: 'glm-5:cloud',
|
||||
enabled: true,
|
||||
),
|
||||
ollamaEndpoint: 'http://127.0.0.1:11434',
|
||||
maxIterations: 3,
|
||||
minAcceptableScore: 7,
|
||||
timeoutSeconds: 120,
|
||||
aiGatewayInjectionPolicy: AiGatewayInjectionPolicy.appManagedDefault,
|
||||
managedSkills: const <ManagedSkillEntry>[],
|
||||
managedMcpServers: const <ManagedMcpServerEntry>[],
|
||||
mountTargets: const <ManagedMountTargetState>[
|
||||
ManagedMountTargetState(
|
||||
targetId: 'aris',
|
||||
label: 'ARIS',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: true,
|
||||
supportsAiGatewayInjection: false,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
ManagedMountTargetState(
|
||||
targetId: 'codex',
|
||||
label: 'Codex',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: true,
|
||||
supportsAiGatewayInjection: true,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
ManagedMountTargetState(
|
||||
targetId: 'claude',
|
||||
label: 'Claude',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: true,
|
||||
supportsAiGatewayInjection: true,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
ManagedMountTargetState(
|
||||
targetId: 'gemini',
|
||||
label: 'Gemini',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: true,
|
||||
supportsAiGatewayInjection: true,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
ManagedMountTargetState(
|
||||
targetId: 'opencode',
|
||||
label: 'OpenCode',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: true,
|
||||
supportsAiGatewayInjection: true,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
ManagedMountTargetState(
|
||||
targetId: 'openclaw',
|
||||
label: 'OpenClaw',
|
||||
available: false,
|
||||
supportsSkills: true,
|
||||
supportsMcp: false,
|
||||
supportsAiGatewayInjection: true,
|
||||
discoveryState: 'idle',
|
||||
syncState: 'idle',
|
||||
discoveredSkillCount: 0,
|
||||
discoveredMcpCount: 0,
|
||||
managedMcpCount: 0,
|
||||
detail: '',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
MultiAgentConfig copyWith({
|
||||
bool? enabled,
|
||||
bool? autoSync,
|
||||
MultiAgentFramework? framework,
|
||||
bool? arisEnabled,
|
||||
String? arisMode,
|
||||
String? arisBundleVersion,
|
||||
String? arisCompatStatus,
|
||||
AgentWorkerConfig? architect,
|
||||
AgentWorkerConfig? engineer,
|
||||
AgentWorkerConfig? tester,
|
||||
String? ollamaEndpoint,
|
||||
int? maxIterations,
|
||||
int? minAcceptableScore,
|
||||
int? timeoutSeconds,
|
||||
AiGatewayInjectionPolicy? aiGatewayInjectionPolicy,
|
||||
List<ManagedSkillEntry>? managedSkills,
|
||||
List<ManagedMcpServerEntry>? managedMcpServers,
|
||||
List<ManagedMountTargetState>? mountTargets,
|
||||
}) {
|
||||
return MultiAgentConfig(
|
||||
enabled: enabled ?? this.enabled,
|
||||
autoSync: autoSync ?? this.autoSync,
|
||||
framework: framework ?? this.framework,
|
||||
arisEnabled: arisEnabled ?? this.arisEnabled,
|
||||
arisMode: arisMode ?? this.arisMode,
|
||||
arisBundleVersion: arisBundleVersion ?? this.arisBundleVersion,
|
||||
arisCompatStatus: arisCompatStatus ?? this.arisCompatStatus,
|
||||
architect: architect ?? this.architect,
|
||||
engineer: engineer ?? this.engineer,
|
||||
tester: tester ?? this.tester,
|
||||
ollamaEndpoint: ollamaEndpoint ?? this.ollamaEndpoint,
|
||||
maxIterations: maxIterations ?? this.maxIterations,
|
||||
minAcceptableScore: minAcceptableScore ?? this.minAcceptableScore,
|
||||
timeoutSeconds: timeoutSeconds ?? this.timeoutSeconds,
|
||||
aiGatewayInjectionPolicy:
|
||||
aiGatewayInjectionPolicy ?? this.aiGatewayInjectionPolicy,
|
||||
managedSkills: managedSkills ?? this.managedSkills,
|
||||
managedMcpServers: managedMcpServers ?? this.managedMcpServers,
|
||||
mountTargets: mountTargets ?? this.mountTargets,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'enabled': enabled,
|
||||
'autoSync': autoSync,
|
||||
'framework': framework.name,
|
||||
'arisEnabled': arisEnabled,
|
||||
'arisMode': arisMode,
|
||||
'arisBundleVersion': arisBundleVersion,
|
||||
'arisCompatStatus': arisCompatStatus,
|
||||
'architect': {
|
||||
'role': architect.role.name,
|
||||
'cliTool': architect.cliTool,
|
||||
'model': architect.model,
|
||||
'enabled': architect.enabled,
|
||||
'maxRetries': architect.maxRetries,
|
||||
},
|
||||
'engineer': {
|
||||
'role': engineer.role.name,
|
||||
'cliTool': engineer.cliTool,
|
||||
'model': engineer.model,
|
||||
'enabled': engineer.enabled,
|
||||
'maxRetries': engineer.maxRetries,
|
||||
},
|
||||
'tester': {
|
||||
'role': tester.role.name,
|
||||
'cliTool': tester.cliTool,
|
||||
'model': tester.model,
|
||||
'enabled': tester.enabled,
|
||||
'maxRetries': tester.maxRetries,
|
||||
},
|
||||
'ollamaEndpoint': ollamaEndpoint,
|
||||
'maxIterations': maxIterations,
|
||||
'minAcceptableScore': minAcceptableScore,
|
||||
'timeoutSeconds': timeoutSeconds,
|
||||
'aiGatewayInjectionPolicy': aiGatewayInjectionPolicy.name,
|
||||
'managedSkills': managedSkills.map((item) => item.toJson()).toList(),
|
||||
'managedMcpServers': managedMcpServers
|
||||
.map((item) => item.toJson())
|
||||
.toList(),
|
||||
'mountTargets': mountTargets.map((item) => item.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
factory MultiAgentConfig.fromJson(Map<String, dynamic> json) {
|
||||
final defaults = MultiAgentConfig.defaults();
|
||||
final architectJson = json['architect'] as Map<String, dynamic>? ?? {};
|
||||
final engineerJson = json['engineer'] as Map<String, dynamic>? ?? {};
|
||||
final testerJson = json['tester'] as Map<String, dynamic>? ?? {};
|
||||
final rawManagedSkills = json['managedSkills'];
|
||||
final rawManagedMcpServers = json['managedMcpServers'];
|
||||
final rawMountTargets = json['mountTargets'];
|
||||
|
||||
AgentWorkerConfig parseWorker(
|
||||
Map<String, dynamic> m,
|
||||
MultiAgentRole role,
|
||||
String defaultTool,
|
||||
) {
|
||||
return AgentWorkerConfig(
|
||||
role: role,
|
||||
cliTool: m['cliTool'] as String? ?? defaultTool,
|
||||
model: m['model'] as String? ?? '',
|
||||
enabled: m['enabled'] as bool? ?? true,
|
||||
maxRetries: m['maxRetries'] as int? ?? 2,
|
||||
);
|
||||
}
|
||||
|
||||
return MultiAgentConfig(
|
||||
enabled: json['enabled'] as bool? ?? false,
|
||||
autoSync: json['autoSync'] as bool? ?? defaults.autoSync,
|
||||
framework: MultiAgentFrameworkCopy.fromJsonValue(
|
||||
json['framework'] as String?,
|
||||
),
|
||||
arisEnabled: json['arisEnabled'] as bool? ?? defaults.arisEnabled,
|
||||
arisMode: json['arisMode'] as String? ?? defaults.arisMode,
|
||||
arisBundleVersion:
|
||||
json['arisBundleVersion'] as String? ?? defaults.arisBundleVersion,
|
||||
arisCompatStatus:
|
||||
json['arisCompatStatus'] as String? ?? defaults.arisCompatStatus,
|
||||
architect: parseWorker(
|
||||
architectJson,
|
||||
MultiAgentRole.architect,
|
||||
defaults.architect.cliTool,
|
||||
),
|
||||
engineer: parseWorker(
|
||||
engineerJson,
|
||||
MultiAgentRole.engineer,
|
||||
defaults.engineer.cliTool,
|
||||
),
|
||||
tester: parseWorker(
|
||||
testerJson,
|
||||
MultiAgentRole.testerDoc,
|
||||
defaults.tester.cliTool,
|
||||
),
|
||||
ollamaEndpoint:
|
||||
json['ollamaEndpoint'] as String? ?? defaults.ollamaEndpoint,
|
||||
maxIterations: json['maxIterations'] as int? ?? defaults.maxIterations,
|
||||
minAcceptableScore:
|
||||
json['minAcceptableScore'] as int? ?? defaults.minAcceptableScore,
|
||||
timeoutSeconds: json['timeoutSeconds'] as int? ?? defaults.timeoutSeconds,
|
||||
aiGatewayInjectionPolicy: AiGatewayInjectionPolicyCopy.fromJsonValue(
|
||||
json['aiGatewayInjectionPolicy'] as String?,
|
||||
),
|
||||
managedSkills: rawManagedSkills is List
|
||||
? rawManagedSkills
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) =>
|
||||
ManagedSkillEntry.fromJson(item.cast<String, dynamic>()),
|
||||
)
|
||||
.toList(growable: false)
|
||||
: defaults.managedSkills,
|
||||
managedMcpServers: rawManagedMcpServers is List
|
||||
? rawManagedMcpServers
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => ManagedMcpServerEntry.fromJson(
|
||||
item.cast<String, dynamic>(),
|
||||
),
|
||||
)
|
||||
.toList(growable: false)
|
||||
: defaults.managedMcpServers,
|
||||
mountTargets: rawMountTargets is List
|
||||
? rawMountTargets
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => ManagedMountTargetState.fromJson(
|
||||
item.cast<String, dynamic>(),
|
||||
),
|
||||
)
|
||||
.toList(growable: false)
|
||||
: defaults.mountTargets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiAgentRunEvent {
|
||||
const MultiAgentRunEvent({
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.message,
|
||||
required this.pending,
|
||||
required this.error,
|
||||
this.role,
|
||||
this.iteration,
|
||||
this.score,
|
||||
this.data = const <String, dynamic>{},
|
||||
});
|
||||
|
||||
final String type;
|
||||
final String title;
|
||||
final String message;
|
||||
final bool pending;
|
||||
final bool error;
|
||||
final String? role;
|
||||
final int? iteration;
|
||||
final int? score;
|
||||
final Map<String, dynamic> data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'type': type,
|
||||
'title': title,
|
||||
'message': message,
|
||||
'pending': pending,
|
||||
'error': error,
|
||||
if (role != null) 'role': role,
|
||||
if (iteration != null) 'iteration': iteration,
|
||||
if (score != null) 'score': score,
|
||||
'data': data,
|
||||
};
|
||||
}
|
||||
|
||||
factory MultiAgentRunEvent.fromJson(Map<String, dynamic> json) {
|
||||
return MultiAgentRunEvent(
|
||||
type: json['type'] as String? ?? 'status',
|
||||
title: json['title'] as String? ?? '',
|
||||
message: json['message'] as String? ?? '',
|
||||
pending: json['pending'] as bool? ?? false,
|
||||
error: json['error'] as bool? ?? false,
|
||||
role: json['role'] as String?,
|
||||
iteration: (json['iteration'] as num?)?.toInt(),
|
||||
score: (json['score'] as num?)?.toInt(),
|
||||
data:
|
||||
(json['data'] as Map?)?.cast<String, dynamic>() ??
|
||||
const <String, dynamic>{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import 'runtime_models_profiles.dart';
|
||||
import 'runtime_models_configs.dart';
|
||||
import 'runtime_models_runtime_payloads.dart';
|
||||
import 'runtime_models_gateway_entities.dart';
|
||||
import 'runtime_models_multi_agent.dart';
|
||||
|
||||
const int settingsSnapshotSchemaVersion = 2;
|
||||
|
||||
@ -34,7 +33,6 @@ class SettingsSnapshot {
|
||||
required this.vault,
|
||||
required this.aiGateway,
|
||||
required this.webSessionPersistence,
|
||||
required this.multiAgent,
|
||||
required this.themeMode,
|
||||
required this.experimentalCanvas,
|
||||
required this.experimentalBridge,
|
||||
@ -67,7 +65,6 @@ class SettingsSnapshot {
|
||||
final VaultConfig vault;
|
||||
final AiGatewayProfile aiGateway;
|
||||
final WebSessionPersistenceConfig webSessionPersistence;
|
||||
final MultiAgentConfig multiAgent;
|
||||
final ThemeMode themeMode;
|
||||
final bool experimentalCanvas;
|
||||
final bool experimentalBridge;
|
||||
@ -101,7 +98,6 @@ class SettingsSnapshot {
|
||||
vault: VaultConfig.defaults(),
|
||||
aiGateway: AiGatewayProfile.defaults(),
|
||||
webSessionPersistence: WebSessionPersistenceConfig.defaults(),
|
||||
multiAgent: MultiAgentConfig.defaults(),
|
||||
themeMode: ThemeMode.system,
|
||||
experimentalCanvas: false,
|
||||
experimentalBridge: false,
|
||||
@ -136,7 +132,6 @@ class SettingsSnapshot {
|
||||
VaultConfig? vault,
|
||||
AiGatewayProfile? aiGateway,
|
||||
WebSessionPersistenceConfig? webSessionPersistence,
|
||||
MultiAgentConfig? multiAgent,
|
||||
ThemeMode? themeMode,
|
||||
bool? experimentalCanvas,
|
||||
bool? experimentalBridge,
|
||||
@ -179,7 +174,6 @@ class SettingsSnapshot {
|
||||
aiGateway: aiGateway ?? this.aiGateway,
|
||||
webSessionPersistence:
|
||||
webSessionPersistence ?? this.webSessionPersistence,
|
||||
multiAgent: multiAgent ?? this.multiAgent,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
experimentalCanvas: experimentalCanvas ?? this.experimentalCanvas,
|
||||
experimentalBridge: experimentalBridge ?? this.experimentalBridge,
|
||||
@ -223,7 +217,6 @@ class SettingsSnapshot {
|
||||
'vault': vault.toJson(),
|
||||
'aiGateway': aiGateway.toJson(),
|
||||
'webSessionPersistence': webSessionPersistence.toJson(),
|
||||
'multiAgent': multiAgent.toJson(),
|
||||
'themeMode': themeMode.name,
|
||||
'experimentalCanvas': experimentalCanvas,
|
||||
'experimentalBridge': experimentalBridge,
|
||||
@ -307,9 +300,6 @@ class SettingsSnapshot {
|
||||
(json['webSessionPersistence'] as Map?)?.cast<String, dynamic>() ??
|
||||
const {},
|
||||
),
|
||||
multiAgent: MultiAgentConfig.fromJson(
|
||||
(json['multiAgent'] as Map?)?.cast<String, dynamic>() ?? const {},
|
||||
),
|
||||
themeMode: ThemeMode.values.firstWhere(
|
||||
(m) => m.name == json['themeMode'],
|
||||
orElse: () => ThemeMode.system,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user