xworkmate-app/lib/runtime/runtime_models_multi_agent.dart

841 lines
25 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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/ReviewWorker 池)',
};
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,
required this.label,
required this.source,
required this.selected,
});
final String key;
final String label;
final String source;
final bool selected;
ManagedSkillEntry copyWith({
String? key,
String? label,
String? source,
bool? selected,
}) {
return ManagedSkillEntry(
key: key ?? this.key,
label: label ?? this.label,
source: source ?? this.source,
selected: selected ?? this.selected,
);
}
Map<String, dynamic> toJson() {
return {'key': key, 'label': label, 'source': source, 'selected': selected};
}
factory ManagedSkillEntry.fromJson(Map<String, dynamic> json) {
return ManagedSkillEntry(
key: json['key'] as String? ?? '',
label: json['label'] as String? ?? '',
source: json['source'] as String? ?? '',
selected: json['selected'] as bool? ?? false,
);
}
}
class ManagedMcpServerEntry {
const ManagedMcpServerEntry({
required this.id,
required this.name,
required this.transport,
required this.command,
required this.url,
required this.args,
required this.envKeys,
required this.enabled,
});
final String id;
final String name;
final String transport;
final String command;
final String url;
final List<String> args;
final List<String> envKeys;
final bool enabled;
ManagedMcpServerEntry copyWith({
String? id,
String? name,
String? transport,
String? command,
String? url,
List<String>? args,
List<String>? envKeys,
bool? enabled,
}) {
return ManagedMcpServerEntry(
id: id ?? this.id,
name: name ?? this.name,
transport: transport ?? this.transport,
command: command ?? this.command,
url: url ?? this.url,
args: args ?? this.args,
envKeys: envKeys ?? this.envKeys,
enabled: enabled ?? this.enabled,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'transport': transport,
'command': command,
'url': url,
'args': args,
'envKeys': envKeys,
'enabled': enabled,
};
}
factory ManagedMcpServerEntry.fromJson(Map<String, dynamic> json) {
final rawArgs = json['args'];
final rawEnvKeys = json['envKeys'];
return ManagedMcpServerEntry(
id: json['id'] as String? ?? '',
name: json['name'] as String? ?? '',
transport: json['transport'] as String? ?? 'stdio',
command: json['command'] as String? ?? '',
url: json['url'] as String? ?? '',
args: rawArgs is List
? rawArgs.map((item) => item.toString()).toList(growable: false)
: const <String>[],
envKeys: rawEnvKeys is List
? rawEnvKeys.map((item) => item.toString()).toList(growable: false)
: const <String>[],
enabled: json['enabled'] as bool? ?? true,
);
}
}
class ManagedMountTargetState {
const ManagedMountTargetState({
required this.targetId,
required this.label,
required this.available,
required this.supportsSkills,
required this.supportsMcp,
required this.supportsAiGatewayInjection,
required this.discoveryState,
required this.syncState,
required this.discoveredSkillCount,
required this.discoveredMcpCount,
required this.managedMcpCount,
required this.detail,
});
final String targetId;
final String label;
final bool available;
final bool supportsSkills;
final bool supportsMcp;
final bool supportsAiGatewayInjection;
final String discoveryState;
final String syncState;
final int discoveredSkillCount;
final int discoveredMcpCount;
final int managedMcpCount;
final String detail;
ManagedMountTargetState copyWith({
String? targetId,
String? label,
bool? available,
bool? supportsSkills,
bool? supportsMcp,
bool? supportsAiGatewayInjection,
String? discoveryState,
String? syncState,
int? discoveredSkillCount,
int? discoveredMcpCount,
int? managedMcpCount,
String? detail,
}) {
return ManagedMountTargetState(
targetId: targetId ?? this.targetId,
label: label ?? this.label,
available: available ?? this.available,
supportsSkills: supportsSkills ?? this.supportsSkills,
supportsMcp: supportsMcp ?? this.supportsMcp,
supportsAiGatewayInjection:
supportsAiGatewayInjection ?? this.supportsAiGatewayInjection,
discoveryState: discoveryState ?? this.discoveryState,
syncState: syncState ?? this.syncState,
discoveredSkillCount: discoveredSkillCount ?? this.discoveredSkillCount,
discoveredMcpCount: discoveredMcpCount ?? this.discoveredMcpCount,
managedMcpCount: managedMcpCount ?? this.managedMcpCount,
detail: detail ?? this.detail,
);
}
Map<String, dynamic> toJson() {
return {
'targetId': targetId,
'label': label,
'available': available,
'supportsSkills': supportsSkills,
'supportsMcp': supportsMcp,
'supportsAiGatewayInjection': supportsAiGatewayInjection,
'discoveryState': discoveryState,
'syncState': syncState,
'discoveredSkillCount': discoveredSkillCount,
'discoveredMcpCount': discoveredMcpCount,
'managedMcpCount': managedMcpCount,
'detail': detail,
};
}
factory ManagedMountTargetState.fromJson(Map<String, dynamic> json) {
return ManagedMountTargetState(
targetId: json['targetId'] as String? ?? '',
label: json['label'] as String? ?? '',
available: json['available'] as bool? ?? false,
supportsSkills: json['supportsSkills'] as bool? ?? false,
supportsMcp: json['supportsMcp'] as bool? ?? false,
supportsAiGatewayInjection:
json['supportsAiGatewayInjection'] as bool? ?? false,
discoveryState: json['discoveryState'] as String? ?? 'idle',
syncState: json['syncState'] as String? ?? 'idle',
discoveredSkillCount: json['discoveredSkillCount'] as int? ?? 0,
discoveredMcpCount: json['discoveredMcpCount'] as int? ?? 0,
managedMcpCount: json['managedMcpCount'] as int? ?? 0,
detail: json['detail'] as String? ?? '',
);
}
factory ManagedMountTargetState.placeholder({
required String targetId,
required String label,
required bool supportsSkills,
required bool supportsMcp,
required bool supportsAiGatewayInjection,
}) {
return ManagedMountTargetState(
targetId: targetId,
label: label,
available: false,
supportsSkills: supportsSkills,
supportsMcp: supportsMcp,
supportsAiGatewayInjection: supportsAiGatewayInjection,
discoveryState: 'idle',
syncState: 'idle',
discoveredSkillCount: 0,
discoveredMcpCount: 0,
managedMcpCount: 0,
detail: '',
);
}
static List<ManagedMountTargetState> defaults() {
return 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: '',
),
];
}
}
/// 多 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>{},
);
}
}