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.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
}
})