diff --git a/src/agentkit/server/frontend/src/api/types.ts b/src/agentkit/server/frontend/src/api/types.ts index fe641c3..6a9323b 100644 --- a/src/agentkit/server/frontend/src/api/types.ts +++ b/src/agentkit/server/frontend/src/api/types.ts @@ -104,6 +104,9 @@ export type WsServerMessage = | { type: 'expert_step'; data: { expert_id: string; expert_name: string; expert_color: string; content: string; step: number } } | { type: 'expert_result'; data: { expert_id: string; expert_name: string; expert_color: string; content: string } } | { type: 'plan_update'; data: { plan_phases: ITeamPlanPhase[] } } + | { type: 'phase_started'; data: { phase_id: string; phase_name: string; assigned_expert: string; depends_on: string[] } } + | { type: 'phase_completed'; data: { phase_id: string; phase_name: string; result_summary: string } } + | { type: 'phase_failed'; data: { phase_id: string; phase_name: string; error: string } } | { type: 'team_synthesis'; data: { content: string } } | { type: 'team_dissolved'; data: { team_id: string } } // Board Meeting 模式事件 @@ -130,9 +133,12 @@ export interface ITeamPlanPhase { id: string name: string assigned_expert: string + task_description?: string + depends_on: string[] status: 'pending' | 'in_progress' | 'completed' | 'failed' - parallel_type: 'serial' | 'subtask_parallel' | 'competitive_parallel' - milestone: string + result?: string + parallel_type?: 'serial' | 'subtask_parallel' | 'competitive_parallel' + milestone?: string } /** Expert team state */ diff --git a/src/agentkit/server/frontend/src/components/chat/ExpertTeamView.vue b/src/agentkit/server/frontend/src/components/chat/ExpertTeamView.vue index fed4c08..e670722 100644 --- a/src/agentkit/server/frontend/src/components/chat/ExpertTeamView.vue +++ b/src/agentkit/server/frontend/src/components/chat/ExpertTeamView.vue @@ -35,9 +35,17 @@ class="expert-team-view__phase" :class="`expert-team-view__phase--${phase.status}`" > - {{ phase.name }} - {{ phase.assigned_expert }} - {{ statusLabel(phase.status) }} +
+ {{ phase.name }} + {{ phase.assigned_expert }} + {{ statusLabel(phase.status) }} +
+
+ {{ phase.task_description }} +
+
+ {{ phase.result }} +
@@ -152,11 +160,22 @@ function statusLabel(status: string): string { } .expert-team-view__phase { + display: flex; + flex-direction: column; + gap: var(--space-1); + padding: var(--space-2) 0; + font-size: var(--font-xs); + border-bottom: 1px solid var(--border-primary); +} + +.expert-team-view__phase:last-child { + border-bottom: none; +} + +.expert-team-view__phase-header { display: flex; align-items: center; gap: var(--space-2); - padding: var(--space-1) 0; - font-size: var(--font-xs); } .expert-team-view__phase-name { @@ -167,11 +186,52 @@ function statusLabel(status: string): string { color: var(--text-secondary); } -.expert-team-view__phase--completed { - opacity: 0.7; +.expert-team-view__phase-desc { + color: var(--text-secondary); + font-size: var(--font-xs); + padding-left: var(--space-2); + border-left: 2px solid var(--border-primary); + white-space: pre-wrap; + word-break: break-word; +} + +.expert-team-view__phase-result { + color: var(--text-tertiary); + font-size: var(--font-xs); + padding-left: var(--space-2); + border-left: 2px solid var(--color-success); + white-space: pre-wrap; + word-break: break-word; + max-height: 80px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} + +.expert-team-view__phase--pending { + opacity: 0.6; } .expert-team-view__phase--in_progress { font-weight: var(--font-weight-medium); + background: var(--bg-tertiary); + margin: 0 calc(-1 * var(--space-3)); + padding-left: var(--space-3); + padding-right: var(--space-3); + border-radius: var(--radius-sm); +} + +.expert-team-view__phase--completed { + opacity: 0.7; +} + +.expert-team-view__phase--failed { + color: var(--color-error); +} + +.expert-team-view__phase--failed .expert-team-view__phase-result { + border-left-color: var(--color-error); } diff --git a/src/agentkit/server/frontend/src/components/chat/PlanVisualization.vue b/src/agentkit/server/frontend/src/components/chat/PlanVisualization.vue index 4309446..c880c69 100644 --- a/src/agentkit/server/frontend/src/components/chat/PlanVisualization.vue +++ b/src/agentkit/server/frontend/src/components/chat/PlanVisualization.vue @@ -16,7 +16,7 @@ 执行者: {{ phase.assigned_expert }} | 里程碑: {{ phase.milestone }} -
+
{{ parallelLabel(phase.parallel_type) }}
diff --git a/src/agentkit/server/frontend/src/stores/chat.ts b/src/agentkit/server/frontend/src/stores/chat.ts index d112591..975fedc 100644 --- a/src/agentkit/server/frontend/src/stores/chat.ts +++ b/src/agentkit/server/frontend/src/stores/chat.ts @@ -661,6 +661,33 @@ export const useChatStore = defineStore('chat', () => { break } + case 'phase_started': { + const teamStore = _getTeamStore() + if (teamStore?.teamState) { + teamStore.updatePhaseStatus(payload.phase_id, 'in_progress') + streamingSteps.value.push(`阶段开始: ${payload.phase_name} (${payload.assigned_expert})`) + } + break + } + + case 'phase_completed': { + const teamStore = _getTeamStore() + if (teamStore?.teamState) { + teamStore.updatePhaseStatus(payload.phase_id, 'completed', payload.result_summary) + streamingSteps.value.push(`阶段完成: ${payload.phase_name}`) + } + break + } + + case 'phase_failed': { + const teamStore = _getTeamStore() + if (teamStore?.teamState) { + teamStore.updatePhaseStatus(payload.phase_id, 'failed', payload.error) + streamingSteps.value.push(`阶段失败: ${payload.phase_name} - ${payload.error}`) + } + break + } + // ── Board Meeting 模式事件 ──────────────────────────────────────── case 'board_started': { diff --git a/src/agentkit/server/frontend/src/stores/team.ts b/src/agentkit/server/frontend/src/stores/team.ts index 6921a1f..94f5faa 100644 --- a/src/agentkit/server/frontend/src/stores/team.ts +++ b/src/agentkit/server/frontend/src/stores/team.ts @@ -38,6 +38,21 @@ export const useTeamStore = defineStore('team', () => { } } + function updatePhaseStatus( + phaseId: string, + status: ITeamPlanPhase['status'], + result?: string, + ) { + if (!teamState.value) return + const phases = teamState.value.plan_phases.map((p) => { + if (p.id !== phaseId) return p + return result !== undefined + ? { ...p, status, result } + : { ...p, status } + }) + teamState.value = { ...teamState.value, plan_phases: phases } + } + function selectExpert(expertId: string | null) { selectedExpertId.value = expertId } @@ -50,6 +65,6 @@ export const useTeamStore = defineStore('team', () => { return { teamState, selectedExpertId, activeExperts, leadExpert, isTeamMode, currentPhase, completedPhases, - setTeamState, updatePhases, selectExpert, clearTeam + setTeamState, updatePhases, updatePhaseStatus, selectExpert, clearTeam } })