diff --git a/src/agentkit/server/frontend/src/api/types.ts b/src/agentkit/server/frontend/src/api/types.ts index c5a3599..9ff3421 100644 --- a/src/agentkit/server/frontend/src/api/types.ts +++ b/src/agentkit/server/frontend/src/api/types.ts @@ -146,6 +146,9 @@ export type WsServerMessage = | { 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 } } + // PLAN_EXEC (U4) — phase lifecycle events emitted by ReActEngine. + | { type: 'phase_changed'; data: { phase: string; previous: string } } + | { type: 'phase_violation'; data: { current_phase: string; tool: string; message: string; violation_kind: string; command_preview?: string } } | { type: 'team_synthesis'; data: { content: string } } | { type: 'team_dissolved'; data: { team_id: string } } // Board Meeting 模式事件 diff --git a/src/agentkit/server/frontend/src/components/chat/PhaseIndicator.vue b/src/agentkit/server/frontend/src/components/chat/PhaseIndicator.vue new file mode 100644 index 0000000..9a08107 --- /dev/null +++ b/src/agentkit/server/frontend/src/components/chat/PhaseIndicator.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/src/agentkit/server/frontend/src/stores/chat.ts b/src/agentkit/server/frontend/src/stores/chat.ts index 728b441..b49e5be 100644 --- a/src/agentkit/server/frontend/src/stores/chat.ts +++ b/src/agentkit/server/frontend/src/stores/chat.ts @@ -174,6 +174,27 @@ export const useChatStore = defineStore("chat", () => { () => boardState.value !== null && boardState.value.status === "discussing", ); + // PLAN_EXEC phase state (U4) — tracks current phase + violations for the + // PhaseIndicator component. Set when the first phase_* event arrives. + // Reset on conversation switch or final_answer. + const currentPhase = ref(null); + const phaseViolations = ref< + Array<{ + phase: string; + tool: string; + message: string; + violation_kind: string; + command_preview?: string; + ts: number; + }> + >([]); + const isPlanExec = computed(() => currentPhase.value !== null); + + function resetPlanExecState(): void { + currentPhase.value = null; + phaseViolations.value = []; + } + // Debate state (transient, only active during a debate collaboration) const debateState = ref<{ topic: string; @@ -1096,6 +1117,8 @@ export const useChatStore = defineStore("chat", () => { // across multiple interactions. The UI has already transitioned // to showing the final assistant message. clearConvSteps(conversationId); + // Reset PLAN_EXEC phase state — the conversation is done. + resetPlanExecState(); break; } @@ -1390,6 +1413,60 @@ export const useChatStore = defineStore("chat", () => { break; } + // ── PLAN_EXEC (U4) — phase lifecycle events from ReActEngine ──────── + + case "phase_changed": { + currentPhase.value = data.data.phase; + const cid = resolveIncomingConvId(); + if (cid) { + appendStep( + { + type: "milestone", + label: "阶段切换", + detail: `${data.data.previous || "—"} → ${data.data.phase}`, + status: "success", + }, + cid, + ); + } + break; + } + + case "phase_violation": { + // Track current phase from violation data (backend doesn't emit + // phase_changed yet — U4 frontend is forward-compatible). + currentPhase.value = data.data.current_phase; + const violation = { + phase: data.data.current_phase, + tool: data.data.tool, + message: data.data.message, + violation_kind: data.data.violation_kind, + command_preview: data.data.command_preview, + ts: Date.now(), + }; + phaseViolations.value = [...phaseViolations.value, violation].slice(-5); + // Toast notification via ant-design-vue message. + import("ant-design-vue").then(({ message }) => { + message.warning( + `[${data.data.current_phase}] 工具 ${data.data.tool} 被拦截: ${data.data.message}`, + 5, + ); + }); + const cid = resolveIncomingConvId(); + if (cid) { + appendStep( + { + type: "team_event", + label: "阶段违规", + detail: `${data.data.current_phase} · ${data.data.tool}`, + status: "error", + }, + cid, + ); + } + break; + } + // ── Board Meeting 模式事件 ──────────────────────────────────────── case "board_started": { @@ -1920,6 +1997,10 @@ export const useChatStore = defineStore("chat", () => { boardState, debateState, collaborationState, + // PLAN_EXEC (U4) + currentPhase, + phaseViolations, + isPlanExec, // Legacy aliases (derive from current conversation for backward compat). // New code should use `isCurrentLoading` / `currentStreamingSteps` instead. isLoading: isCurrentLoading, diff --git a/src/agentkit/server/frontend/src/views/ChatView.vue b/src/agentkit/server/frontend/src/views/ChatView.vue index 81b9cc5..b22ad64 100644 --- a/src/agentkit/server/frontend/src/views/ChatView.vue +++ b/src/agentkit/server/frontend/src/views/ChatView.vue @@ -20,6 +20,7 @@