新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理

后端:
- 工作流推进链(D-03):advance_task/状态机/闸门走 df-nodes Node trait,conditions 条件引擎扩展
- 想法评估闭环:启发式评分+对抗评估,df-ideas/scoring + df-storage/idea_eval_repo + idea 前端打通
- 全局事件数据总线:df-ai/context+context_helpers+augmentation 跨模块解耦
- AI planner/plan_hint/intent:aichat B 路线并行多轮基础
- patch_file 加固(TD-03/04):读改写整体锁防 lost update,expected_hash 合约闭环
- 压缩超时兜底(F-15 卡死根治)
- F-09 多会话并发:LlmConcurrency per-conv + streamingGuard 前端守护 + verify 脚本
- 知识注入 DRY/skills/audit 扩展

清理:
- aichat 技术债(误报 allow/死导入/过时注释 30 项)
- URGENT.md 删除(11 项加急全解决/迁 todo)
- 文档整理(todo/待决策/待审查/ARCHITECTURE/INDEX + 总线/技术债审查新文档)
This commit is contained in:
2026-06-21 20:51:26 +08:00
parent 330bb7f505
commit bd6a41fe6e
111 changed files with 11932 additions and 1034 deletions

View File

@@ -57,6 +57,44 @@
</div>
</div>
<!-- grep 结果:跨文件内容搜索(content/files_with_matches/count 三模式,F-260621)
对齐 search_files 文件栏样式;content 模式按命中行渲染(文件::内容),支持 -C 上下文折叠 -->
<div v-else-if="tc.name === 'grep' && tc.result && tc.status === 'completed' && parsed" class="ai-tool-grep-result">
<div class="ai-tool-file-bar">
<span class="ai-tool-file-icon" v-html="searchIcon"></span>
<span class="ai-tool-file-path">{{ parsed?.path }}</span>
<span v-if="parsed?.pattern" class="ai-tool-file-meta">{{ parsed?.pattern }}</span>
<span class="ai-tool-file-meta">{{ grepHitLabel }}</span>
<span v-if="parsed?.truncated" class="ai-tool-file-meta ai-tool-file-meta--warn">{{ $t('aiTool.grepTruncated') }}</span>
</div>
<!-- files_with_matches 模式:仅文件列表 -->
<div v-if="parsed?.output_mode === 'files_with_matches'" class="ai-tool-dir-entries">
<div v-for="(f, i) in parsed?.files" :key="i" class="ai-tool-dir-entry">
<span class="ai-tool-dir-icon ai-tool-dir-icon--file" v-html="fileIcon"></span>
<span class="ai-tool-dir-name">{{ f }}</span>
</div>
</div>
<!-- count 模式:文件 + 命中行数 -->
<div v-else-if="parsed?.output_mode === 'count'" class="ai-tool-dir-entries">
<div v-for="(c, i) in parsed?.counts" :key="i" class="ai-tool-dir-entry">
<span class="ai-tool-dir-icon ai-tool-dir-icon--file" v-html="fileIcon"></span>
<span class="ai-tool-dir-name">{{ c.file }}</span>
<span class="ai-tool-dir-size">{{ $t('aiTool.grepCountLines', { n: c.count }) }}</span>
</div>
</div>
<!-- content 模式:命中行(文件::内容),支持上下文折叠 -->
<div v-else class="ai-tool-grep-matches">
<div v-for="(m, i) in parsed?.matches" :key="i" class="ai-tool-grep-match">
<div class="ai-tool-grep-match-head">
<span class="ai-tool-grep-file">{{ m.file }}</span>
<span v-if="m.line !== null && m.line !== undefined" class="ai-tool-grep-line">:{{ m.line }}</span>
</div>
<code class="ai-tool-grep-content">{{ m.content }}</code>
<pre v-if="m.context" class="ai-tool-grep-context"><code>{{ m.context }}</code></pre>
</div>
</div>
</div>
<!-- write_file 结果 -->
<div v-else-if="tc.name === 'write_file' && tc.result && tc.status === 'completed' && parsed" class="ai-tool-write-result">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--df-success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
@@ -142,8 +180,10 @@ import {
formatSizeDiff,
combineOutputs,
formatToolResult,
formatGrep,
dirIcon,
fileIcon,
searchIcon,
} from '@/composables/ai/useToolCard'
import { parseDiffLines } from '@/composables/ai/useToolCardRender'
import type { AiToolCallInfo } from '@/api/types'
@@ -201,6 +241,16 @@ const cmdOutput = computed(() => {
*/
const resultDiffLines = computed(() => parseDiffLines(parsed.value?.diff, 120))
/**
* grep 命中计数标签(F-260621):G-1 复用 useToolCard.formatGrep 单一函数,
* 消除原与 ToolResultBody 重复的三分支(files_with_matches/count/content 文案)。
* parsed 为 null 返空串,等价原逻辑;非 null 委托 formatGrep。
*/
const grepHitLabel = computed(() => {
const r = parsed.value
return r ? formatGrep(r) : ''
})
// SW-260618-06: status 切到 completed 时按成功/失败初始化折叠态(失败默认展开便于看 stderr/错误 body)。
// immediate 时若初始非 completed,两 ref 保持默认 false,无副作用。
watch(() => props.tc.status, (s) => {
@@ -266,6 +316,71 @@ watch(() => props.tc.status, (s) => {
color: var(--df-text-dim);
flex-shrink: 0;
}
.ai-tool-file-meta--warn {
color: var(--df-warning);
}
/* grep 结果(F-260621):文件栏复用 ai-tool-file-bar,命中行用等宽渲染 */
.ai-tool-grep-result {
border-top: 0.5px solid var(--df-border);
}
.ai-tool-grep-matches {
padding: 4px 0;
max-height: 260px;
overflow-y: auto;
}
.ai-tool-grep-match {
padding: 3px 12px;
border-bottom: 0.5px solid rgba(255,255,255,0.03);
}
.ai-tool-grep-match:last-child {
border-bottom: none;
}
.ai-tool-grep-match-head {
display: flex;
align-items: baseline;
gap: 1px;
font-family: var(--df-font-mono);
font-size: 10px;
margin-bottom: 1px;
}
.ai-tool-grep-file {
color: var(--df-accent);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ai-tool-grep-line {
color: var(--df-text-dim);
flex-shrink: 0;
}
.ai-tool-grep-content {
display: block;
font-family: var(--df-font-mono);
font-size: 11px;
color: var(--df-text);
white-space: pre-wrap;
word-break: break-all;
background: rgba(61,219,160,0.05);
padding: 2px 6px;
border-radius: var(--df-radius-xs, 3px);
}
.ai-tool-grep-context {
margin: 2px 0 0;
padding: 0;
max-height: 120px;
overflow: auto;
font-family: var(--df-font-mono);
font-size: 10px;
color: var(--df-text-dim);
background: rgba(255,255,255,0.02);
border-radius: var(--df-radius-xs, 3px);
}
.ai-tool-grep-context code {
display: block;
padding: 4px 6px;
white-space: pre;
}
.ai-tool-expand-btn {
font-family: var(--df-font-sans);
font-size: 10px;