新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理
后端: - 工作流推进链(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:
@@ -2,15 +2,18 @@
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
|
||||
import type { AiChatEvent, AiConversationDetail, AiConversationSummary, AiProviderConfig, ContentPart, ModelConfig, SkillInfo } from './types'
|
||||
import type { AiChatEvent, AiConversationDetail, AiConversationSummary, AiProviderConfig, ContentPart, MentionSpan, ModelConfig, SkillInfo } from './types'
|
||||
|
||||
export const aiApi = {
|
||||
/**
|
||||
* 发送消息(非阻塞,通过 ai-chat-event 流式返回);skill 传技能名则注入其 SKILL.md。
|
||||
* F-260614-05 Phase 2c: parts 透传给后端 ai_chat_send → user_parts → provider vision 端点。
|
||||
* parts 为空/undefined → 后端走 user() 纯文本(向后兼容)。
|
||||
* Input Augmentation: mentionSpans 透传给后端 ai_chat_send 的 mention_spans 参数
|
||||
* (后端 resolve_and_inject 按 kind 投影成 Augmentation 注入 system prompt)。
|
||||
* undefined/空数组 → 不传(后端 None 走无 mention 路径,向后兼容)。
|
||||
*/
|
||||
sendMessage(message: string, language?: string, skill?: string, modelOverride?: string | null, parts?: ContentPart[], conversationId?: string | null): Promise<string> {
|
||||
sendMessage(message: string, language?: string, skill?: string, modelOverride?: string | null, parts?: ContentPart[], conversationId?: string | null, mentionSpans?: MentionSpan[]): Promise<string> {
|
||||
return invoke('ai_chat_send', {
|
||||
message,
|
||||
language: language || 'zh-CN',
|
||||
@@ -19,11 +22,13 @@ export const aiApi = {
|
||||
parts: parts && parts.length > 0 ? parts : null,
|
||||
// F-260616-09 B 批4(决策 e):传 conv_id,操作指定 conv 的 per_conv(不依赖 active 单值)。
|
||||
conversationId: conversationId || null,
|
||||
// Input Augmentation: 非空数组才传(undefined/空让 payload 不含该键,后端默认 None)。
|
||||
mentionSpans: mentionSpans && mentionSpans.length > 0 ? mentionSpans : null,
|
||||
})
|
||||
},
|
||||
|
||||
/** 强制发送(B-260616-02: 复位 generating 残留后走 send 同款流程);parts 透传同 sendMessage。 */
|
||||
forceSend(message: string, language?: string, skill?: string, modelOverride?: string | null, parts?: ContentPart[], conversationId?: string | null): Promise<string> {
|
||||
/** 强制发送(B-260616-02: 复位 generating 残留后走 send 同款流程);parts/mentionSpans 透传同 sendMessage。 */
|
||||
forceSend(message: string, language?: string, skill?: string, modelOverride?: string | null, parts?: ContentPart[], conversationId?: string | null, mentionSpans?: MentionSpan[]): Promise<string> {
|
||||
return invoke('ai_chat_force_send', {
|
||||
message,
|
||||
language: language || 'zh-CN',
|
||||
@@ -32,6 +37,8 @@ export const aiApi = {
|
||||
parts: parts && parts.length > 0 ? parts : null,
|
||||
// F-260616-09 B 批4(决策 e):传 conv_id,强制复位+发送仅作用于目标 conv。
|
||||
conversationId: conversationId || null,
|
||||
// Input Augmentation: 同 sendMessage,非空数组才传(后端 mention_spans 参数)。
|
||||
mentionSpans: mentionSpans && mentionSpans.length > 0 ? mentionSpans : null,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -94,8 +101,9 @@ export const aiApi = {
|
||||
return invoke('ai_stop_loop', { conversationId })
|
||||
},
|
||||
|
||||
/** 查询某对话积压的待审批工具(重启后恢复 toolCard pending_approval 态用) */
|
||||
pendingToolCalls(convId: string): Promise<{ tool_call_id: string; conversation_id: string | null }[]> {
|
||||
/** 查询某对话积压的待审批工具(重启后恢复 toolCard pending_approval 态用)。
|
||||
* 阶段4:返 kind 字段(risk/path),前端按 kind 渲染不同决策按钮。 */
|
||||
pendingToolCalls(convId: string): Promise<{ tool_call_id: string; conversation_id: string | null; kind: 'risk' | 'path' }[]> {
|
||||
return invoke('ai_pending_tool_calls', { convId })
|
||||
},
|
||||
|
||||
@@ -224,6 +232,17 @@ export const aiApi = {
|
||||
return invoke('ai_list_skills')
|
||||
},
|
||||
|
||||
/**
|
||||
* Input Augmentation:进程内重载 skills 缓存(OnceLock → RwLock 后支持热重载)。
|
||||
*
|
||||
* 用户增删/修改 SKILL.md 后无需重启即可让后续 `/技能` 注入读到最新内容。
|
||||
* 后端 ai_reload_skills 复用 scan_skills 走剥 frontmatter 的读取 + invalidate 缓存,
|
||||
* 返回重载后的全量 skills 列表(供前端同步刷新 skills state)。
|
||||
*/
|
||||
reloadSkills(): Promise<SkillInfo[]> {
|
||||
return invoke('ai_reload_skills')
|
||||
},
|
||||
|
||||
/** F-260619-03 Phase A: 获取 AI 工具文件访问授权目录列表(含 workspace_root) */
|
||||
getAllowedDirs(): Promise<string[]> {
|
||||
return invoke<string[]>('ai_get_allowed_dirs')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import type { IdeaRecord, CreateIdeaInput, PromotionResult } from './types'
|
||||
import type { IdeaRecord, CreateIdeaInput, PromotionResult, IdeaEvaluationRecord } from './types'
|
||||
|
||||
export const ideaApi = {
|
||||
/** 列出灵感,可选按 status 过滤(draft/pending_review/promoted 等) */
|
||||
@@ -28,4 +28,9 @@ export const ideaApi = {
|
||||
promote(id: string): Promise<PromotionResult> {
|
||||
return invoke('promote_idea', { id })
|
||||
},
|
||||
|
||||
/** 列出灵感评估历史(version DESC)。Tauri 默认把 ideaId 映射到 idea_id。 */
|
||||
listEvaluations(ideaId: string): Promise<IdeaEvaluationRecord[]> {
|
||||
return invoke('list_idea_evaluations', { ideaId })
|
||||
},
|
||||
}
|
||||
|
||||
124
src/api/types.ts
124
src/api/types.ts
@@ -20,6 +20,8 @@ export interface IdeaRecord {
|
||||
promoted_to: string | null
|
||||
ai_analysis: string | null
|
||||
scores: string | null
|
||||
/** 关联灵感 id JSON 数组字符串(对齐后端 related_ids Option<String>),null/空串=无关联 */
|
||||
related_ids: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
@@ -32,6 +34,22 @@ export interface CreateIdeaInput {
|
||||
source?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 灵感评估历史记录(后端 list_idea_evaluations 返回)。
|
||||
* 每次评估(启发式/LLM/降级)落一行,version 递增,按 version DESC 返回。
|
||||
* ai_analysis / scores 为 JSON 字符串(对齐 IdeaRecord 同名字段)。
|
||||
*/
|
||||
export interface IdeaEvaluationRecord {
|
||||
id: string
|
||||
idea_id: string
|
||||
version: number
|
||||
ai_analysis: string | null
|
||||
scores: string | null
|
||||
score: number | null
|
||||
evaluated_by: string | null
|
||||
evaluated_at: string
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 项目
|
||||
// ============================================================
|
||||
@@ -281,7 +299,14 @@ export type AiChatEvent = ({
|
||||
// 不写消息/不动 pending(Started/Completed 仍独立发),仅提示性事件。
|
||||
type: 'AiToolAutoApproved'; id: string; tool: string; dir: string
|
||||
} | {
|
||||
// path_auth 审批链阶段3b(统一审批模型):AiApprovalRequired 携带 kind 字段区分
|
||||
// 'path'(F-260619-03 Phase B 路径授权挂起,走 once/always/deny)vs 'risk'(普通 RiskLevel 审批,
|
||||
// 走 approve/reject)。后端阶段3a 未在 wire 上给该事件加 kind(老 path 挂起仍走独立
|
||||
// AiDirAuthRequired 事件),前端 useAiEvents 入口处据 event.type 归一打标:risk 类默认 'risk',
|
||||
// path 类(AiDirAuthRequired 归一)打 'path'。故此处 kind 为可选(老后端不传时前端默认 'risk')。
|
||||
// 开关 df-ai-unified-approval:true=归一进 pendingApprovals 带 kind;false=回退 DirAuthDialog 老链路。
|
||||
type: 'AiApprovalRequired'; id: string; name: string; args: unknown; reason: string; diff?: string
|
||||
kind?: 'path' | 'risk'
|
||||
} | {
|
||||
type: 'AiApprovalResult'; id: string; approved: boolean
|
||||
} | {
|
||||
@@ -343,9 +368,86 @@ export interface AiMessage {
|
||||
* 历史消息从 DB JSON 反序列化时透传(useAiConversations switchConversation)。
|
||||
*/
|
||||
parts?: ContentPart[]
|
||||
/**
|
||||
* Input Augmentation:@ mention 区间元数据(仅 user 消息)。
|
||||
*
|
||||
* 用户选中 @项目/@任务/@灵感/`/技能` 时按选区记 {start,length,kind,refId,label},
|
||||
* 随消息透传到后端 ai_chat_send 的 mention_spans 参数(后端 resolve 投影成 Augmentation 注入)。
|
||||
* 前端 MessageList 据此精确切区间渲染 chip(替代正则,防误匹配)。
|
||||
*
|
||||
* undefined/空=无 mention(纯文本消息零回归);历史消息从 DB 反序列化时透传。
|
||||
*/
|
||||
mentionSpans?: MentionSpan[]
|
||||
/**
|
||||
* Input Augmentation:resolve 后的增强上下文(仅展示用,后端 resolve 的回传或落库快照)。
|
||||
*
|
||||
* serde 镜像后端 df-types Augmentation(tag=kind, snake_case 变体名)。
|
||||
* 当前透传链路仅传 mentionSpans,augmentations 主要用于落库消息回显/调试;前端宽容对待
|
||||
* (不强制校验变体字段,未知 kind 忽略,对齐后端「新增变体向后兼容」设计)。
|
||||
*/
|
||||
augmentations?: Augmentation[]
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Input Augmentation 层类型(@ mention / / skill 统一增强)
|
||||
// ============================================================
|
||||
//
|
||||
// 与后端 crates/df-types/src/augmentation.rs 严格对齐:
|
||||
// - MentionSpanDto(start/length/kind/refId/label)→ 前端 MentionSpan
|
||||
// - Augmentation(serde tag=kind, snake_case)→ 前端 Augmentation 宽松镜像
|
||||
// - SanitizedPath(String newtype, serde transparent = 裸字符串)→ 前端 type SanitizedPath = string
|
||||
//
|
||||
// refId 用 camelCase(后端 serde rename = "refId",与前端 TS 风格一致);
|
||||
// Augmentation 内部字段沿用后端 snake_case(path/project_name 等,与 wire 严格对齐,不做转译)。
|
||||
|
||||
/**
|
||||
* 用户消息内 mention 区间的元数据(对齐后端 MentionSpanDto)。
|
||||
*
|
||||
* 前端按 {start, length} 在原文中切出区间并替换为 chip;区间被编辑破坏(长度/内容不再匹配)
|
||||
* 时降级为纯文本展示,避免正则误匹配。
|
||||
*
|
||||
* 注意字段名:refId(camelCase,后端 serde rename)。
|
||||
*/
|
||||
export interface MentionSpan {
|
||||
/** 区间起点(字符偏移,前端约定,后端仅透传不解释) */
|
||||
start: number
|
||||
/** 区间长度 */
|
||||
length: number
|
||||
/** kind 标签:project / task / idea / skill */
|
||||
kind: 'project' | 'task' | 'idea' | 'skill'
|
||||
/** 引用稳定标识(Project/Task/Idea 为 id,Skill 为 name),前端用于反查 */
|
||||
refId: string
|
||||
/** chip 展示文本(项目名/任务标题/技能名) */
|
||||
label: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 脱敏后的文件系统路径(对齐后端 SanitizedPath newtype)。
|
||||
*
|
||||
* 后端 serde(transparent)在 wire 上即裸字符串,前端用 string 别名即可。
|
||||
* Local provider(localhost/127.0.0.1/...)保留全路径;Remote 仅 basename。
|
||||
*/
|
||||
export type SanitizedPath = string
|
||||
|
||||
/**
|
||||
* Augmentation 变体集合字面量(对齐后端 serde tag=kind, rename_all=snake_case)。
|
||||
*/
|
||||
export type AugmentationKind = 'project' | 'task' | 'idea' | 'skill'
|
||||
|
||||
/**
|
||||
* resolve 后的增强上下文(对齐后端 Augmentation)。
|
||||
*
|
||||
* 宽松镜像:tag=kind 区分变体,各变体字段按需声明(后端新增字段时前端不必同步改,
|
||||
* 多余字段 JSON 解析保留不报错)。前端主要读 name/title/path 用于 chip/展示,
|
||||
* 需读具体变体字段时用 type narrowing(kind === 'project' 等)。
|
||||
*/
|
||||
export type Augmentation = {
|
||||
kind: AugmentationKind
|
||||
/** 变体特有字段按需读取(用 type narrowing 访问);索引签名容纳后端新增字段 */
|
||||
[field: string]: unknown
|
||||
}
|
||||
|
||||
// AiToolCallInfo.status union 类型 — 前端构建(后端无对应 struct,经 audit log/stream event 组装)。
|
||||
// 值集核验:grep src/composables/ai + src/components 实际使用点 = {running,pending_approval,completed,rejected}。
|
||||
// 前端为权威源(无后端 enum 约束),保持现状仅抽 alias 便于复用。
|
||||
@@ -363,6 +465,25 @@ export interface AiToolCallInfo {
|
||||
/** AE-2025-03:write_file 行级 unified diff(旧文件 vs 新内容),审批卡即时预览。
|
||||
* 旧文件不存在(新建)或非 write_file 为 undefined,前端回退显新 content。 */
|
||||
diff?: string
|
||||
/**
|
||||
* path_auth 审批链阶段3b(统一审批模型):审批类型,区分两类挂起。
|
||||
* - 'path'(F-260619-03 Phase B 路径授权挂起):ToolCard 显 once/always/deny 三选项,
|
||||
* 调 aiApi.authorizeDir(once 写会话临时授权 / always 写持久白名单 / deny 工具返 Err)。
|
||||
* - 'risk'(普通 RiskLevel 审批,Med/High 风险工具):ToolCard 显 approve/reject 两选项,
|
||||
* 调 aiApi.approve(一次性)。
|
||||
* undefined = 未进 pending_approval 态(非挂起)或老后端 risk 类(默认 'risk')。
|
||||
* 开关 df-ai-unified-approval 控制 path 类是否归一进 pendingApprovals 带此字段。
|
||||
*/
|
||||
kind?: 'path' | 'risk'
|
||||
/**
|
||||
* path_auth 审批链阶段3b:'path' 类挂起携带的目录信息(规范化后的待授权目录)。
|
||||
* AiDirAuthRequired 归一为 pendingApprovals 项时透传,ToolCard 显提示"LLM 想访问 X 目录"。
|
||||
* 'risk' 类为 undefined。
|
||||
*/
|
||||
dir?: string
|
||||
/** path_auth 审批链阶段3b:'path' 类挂起携带的原始路径(工具调用参数中的 path)。
|
||||
* ToolCard 审批提示文案展示用(LLM 想访问 path,父目录为 dir)。'risk' 类为 undefined。 */
|
||||
path?: string
|
||||
}
|
||||
|
||||
/** 对话列表摘要 */
|
||||
@@ -494,9 +615,8 @@ export interface UpdateKnowledgeInput {
|
||||
/** 知识库行为配置(提取 + 注入 + 向量检索) */
|
||||
export interface KnowledgeConfig {
|
||||
auto_extract: boolean
|
||||
trigger_mode: 'on_complete' | 'on_idle' | 'manual_only'
|
||||
trigger_mode: 'on_complete' | 'manual_only'
|
||||
min_messages: number
|
||||
idle_timeout_ms: number
|
||||
auto_inject: boolean
|
||||
/** 语义检索(向量)开关,默认 false——关闭时纯 LIKE 零外部依赖 */
|
||||
vector_enabled: boolean
|
||||
|
||||
Reference in New Issue
Block a user