新增: 并行调度+Token预算池+事件协议+前端类型+编译警告清理

- Coordinator.dispatch_with_budget: JoinSet层内并行+层间串行+预算超限降级串行

- TokenBudgetPool: AtomicU64 CAS无锁并发安全,0=不限制

- 4个新事件: AiPlanCreated/AiSubTaskStatusChanged/AiMergeCompleted/AiConflictResolved

- 前端类型: PlanRecord/SubTaskRecord/ConflictRecord/PlanLayerInfo/ConflictInfo

- PlanProgress.vue: 接入真实状态+persona徽章+冲突徽章+i18n

- 编译警告全部清零(audit子模块allow+事件allow+record allow)

- Coordinator测试27个全绿(含4个Token预算+4个并行调度)
This commit is contained in:
lxy
2026-07-01 22:25:18 +08:00
parent 4483358f6b
commit 3c9077b043
9 changed files with 538 additions and 35 deletions
+62 -22
View File
@@ -1,7 +1,7 @@
<template>
<div class="plan-progress" v-if="visible">
<div class="plan-progress" v-if="visible && layers.length > 0">
<div class="plan-progress-header">
<span class="plan-progress-title">{{ t('aiChat.planProgress') }}</span>
<span class="plan-progress-title">📋 {{ t('aiChat.planProgress') }}</span>
<span class="plan-progress-count">{{ doneCount }}/{{ totalCount }}</span>
</div>
<div class="plan-progress-layers">
@@ -9,45 +9,73 @@
<div class="plan-layer-label">{{ t('aiChat.planLayer', { n: li + 1 }) }}</div>
<div class="plan-layer-items">
<div
v-for="(item, ii) in layer"
v-for="(item, ii) in layer.items"
:key="ii"
class="plan-item"
:class="'plan-item--' + item.status"
>
<span class="plan-item-icon">
<template v-if="item.status === 'done'">&#x2713;</template>
<template v-else-if="item.status === 'running'">&#x25B6;</template>
<template v-else-if="item.status === 'error'">&#x2717;</template>
<template v-else>&#x25CB;</template>
<span class="plan-item-icon" :class="'persona-' + (item.persona_id || 'default')">
{{ personaIcon(item.persona_id) }}
</span>
<span class="plan-item-label">{{ item.label }}</span>
<span class="plan-item-label">{{ item.intent }}</span>
<span class="plan-item-status">{{ statusIcon(item.status) }}</span>
</div>
</div>
<div v-if="li < layers.length - 1" class="plan-layer-arrow">&#x2193;</div>
<div v-if="li < layers.length - 1" class="plan-layer-arrow"></div>
</div>
</div>
<!-- 冲突徽章 -->
<div v-if="conflicts.length > 0" class="plan-conflicts-badge" @click="emit('show-conflicts')">
{{ conflicts.length }} 处冲突待处理
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import type { PlanLayerInfo, ConflictInfo } from '@/api/types'
const { t } = useI18n()
interface PlanItem {
id: string
label: string
status: 'pending' | 'running' | 'done' | 'error'
}
const props = defineProps<{
visible: boolean
layers: PlanItem[][]
layers: PlanLayerInfo[]
conflicts?: ConflictInfo[]
}>()
const totalCount = computed(() => props.layers.reduce((s, l) => s + l.length, 0))
const doneCount = computed(() => props.layers.reduce((s, l) => s + l.filter(i => i.status === 'done').length, 0))
const emit = defineEmits<{
(e: 'show-conflicts'): void
}>()
const conflicts = computed(() => props.conflicts || [])
const totalCount = computed(() =>
props.layers.reduce((s, l) => s + l.items.length, 0)
)
const doneCount = computed(() =>
props.layers.reduce((s, l) => s + l.items.filter(i => i.status === 'done').length, 0)
)
function personaIcon(personaId: string | null): string {
switch (personaId) {
case 'coder': return '🔵'
case 'reviewer': return '🔍'
case 'architect': return '📐'
case 'tester': return '🧪'
case 'analyst': return '📊'
default: return '⚪'
}
}
function statusIcon(status: string): string {
switch (status) {
case 'done': return '✓'
case 'running': return '▶'
case 'error': return '✗'
default: return '○'
}
}
</script>
<style scoped>
@@ -67,9 +95,7 @@ const doneCount = computed(() => props.layers.reduce((s, l) => s + l.filter(i =>
}
.plan-progress-title { color: var(--df-text); }
.plan-progress-count { color: var(--df-text-dim); }
.plan-layer {
margin-bottom: 4px;
}
.plan-layer { margin-bottom: 4px; }
.plan-layer-label {
font-size: 11px;
color: var(--df-text-dim);
@@ -94,5 +120,19 @@ const doneCount = computed(() => props.layers.reduce((s, l) => s + l.filter(i =>
.plan-item--running { border-color: var(--df-primary); color: var(--df-primary); }
.plan-item--error { border-color: var(--df-danger); color: var(--df-danger); }
.plan-item-icon { font-size: 10px; }
.plan-item-status { font-size: 10px; font-weight: 700; }
.plan-layer-arrow { text-align: center; color: var(--df-text-dim); font-size: 10px; }
.plan-conflicts-badge {
margin-top: 8px;
padding: 4px 8px;
background: var(--df-warning-bg, rgba(255, 193, 7, 0.1));
border: 1px solid var(--df-warning, #ffc107);
border-radius: 4px;
color: var(--df-warning, #ffc107);
cursor: pointer;
font-size: 11px;
}
.plan-conflicts-badge:hover {
opacity: 0.8;
}
</style>