Files
DevFlow/src/api/types.ts
T
lxy 63779e8d69 新增: 知识库检索分词化与晋升配置化及对话工具
整句 LIKE 命中率趋零,注入检索改 CJK 2-gram+ASCII 词分词 OR 匹配,标题命中计分排序
停用词剥离前置到切分前,滤掉跨词组合噪音 gram;ASCII 等频精确词优先
candidate 纳入检索面(include_candidates 默认开,配开关可回仅 published)
复用即信任自动晋升配置化:默认关+阈值 5,等晋升把关策略拍板后再开,防未审核产物经误命中固化污染
晋升留痕进时间线(auto/agent 来源区分);补 candidate 复用达标自动转正机制
agent 对话侧新增 knowledge_search/insert/promote 三工具,insert 固定 candidate,转正仅限 candidate 单向
知识配置结构新增两字段带 serde 默认,旧配置 JSON 缺字段不破坏启动恢复
2026-10-01 02:40:36 +08:00

932 lines
37 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! TypeScript 类型定义 — 与 Rust Record 结构体严格对齐
// ============================================================
// 域 ID newtype(纯类型级标记,零运行时开销)
// ============================================================
/** 项目 ID — branded newtype,替代 string */
export type ProjectId = string & { readonly __brand: 'ProjectId' }
/** 任务 ID — branded newtype,替代 string */
export type TaskId = string & { readonly __brand: 'TaskId' }
/** 对话 ID — branded newtype,替代 string */
export type ConvId = string & { readonly __brand: 'ConvId' }
/** 工程/模块 ID — branded newtype,替代 string */
export type ModuleId = string & { readonly __brand: 'ModuleId' }
/** 消息 ID — branded newtype,替代 string */
export type MessageId = string & { readonly __brand: 'MessageId' }
// ============================================================
// 灵感
// ============================================================
// IdeaRecord.status union 类型 — 值集对齐后端 IdeaStatus serde (df-types/src/types.rs:52)
// snake_case: draft | pending_review | approved | rejected | promoted | archived
export type IdeaStatus = 'draft' | 'pending_review' | 'approved' | 'rejected' | 'promoted' | 'archived'
export interface IdeaRecord {
id: string
title: string
description: string
status: IdeaStatus
priority: number
score: number | null
tags: string | null // JSON 数组字符串
source: string | null
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
}
export interface CreateIdeaInput {
title: string
description?: string
priority?: number
tags?: string
source?: string
}
/**
* 灵感多条件查询入参(F-260621-02,对齐后端 IdeaQuery)。
*
* 所有字段可选;全空 → 后端返回全量(created_at DESC)。
* - `status`:状态精确匹配(后端 WHERE,取代前端 filter)
* - `keyword`:title/description LIKE %kw%(后端 LIKE,取代前端 filter)
* - `order_by`:白名单 created_at/updated_at/priority/status/score(后端白名单校验防注入)
* - `limit`/`offset`:分页(limit 钳制上限 200)
*/
export interface IdeaQuery {
status?: string | null
keyword?: string | null
order_by?: string | null
limit?: number | null
offset?: number | null
}
/**
* 灵感评估历史记录(后端 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
}
// ============================================================
// 项目
// ============================================================
// ProjectRecord.status union 类型 — 值集对齐后端 ProjectStatus serde (df-types/src/types.rs:90)
// snake_case: planning | in_progress | testing | releasing | completed | paused | cancelled
// 注:前端 constants/dashboard 的 'active' 是 UI 虚拟派生态(非 record 落库值),不进此 union。
export type ProjectStatus =
| 'planning' | 'in_progress' | 'testing' | 'releasing' | 'completed' | 'paused' | 'cancelled'
export interface ProjectRecord {
id: ProjectId
name: string
description: string
status: ProjectStatus
idea_id: string | null
/** 绑定的本地代码目录绝对路径(null=未绑定) */
path: string | null
/** 技术栈 JSON 数组字符串(如 ["rust","vue"],null=未探测) */
stack: string | null
created_at: string
updated_at: string
/**
* 最近活跃时间(问题3):COALESCE(project_events 最新事件 created_at, updated_at)。
* 反映业务活跃(任务/灵感/状态推进等事件),无事件回退 updated_at。
* 可选(旧后端/回收站等路径不返):消费方需用 ?? updated_at 兜底。
*/
last_active_at?: string
}
export interface CreateProjectInput {
name: string
description?: string
idea_id?: string
/** 绑定的本地代码目录(可选) */
path?: string
/** 技术栈 JSON 数组字符串(可选,空则后端自动探测) */
stack?: string
}
/** AI 扫描项目结果(预览用,用户确认后填入) */
export interface AiScanResult {
/** 项目摘要(空=LLM 未得出,需手填) */
description: string
/** 技术栈(规则 ∪ LLM,去重小写) */
stack: string[]
/** 项目类型(web/api/cli/library/desktop/mobile/monorepo/other) */
project_type: string | null
/** LLM 原始返回(降级时含错误信息) */
raw: string | null
}
/** 灵感晋升结果(后端 promote_idea 返回) */
export interface PromotionResult {
idea_id: string
project_id: ProjectId
promoted: boolean
reason: string
}
// ============================================================
// 任务
// ============================================================
// TaskRecord.status union 类型 — 值集对齐后端 TaskStatus serde (df-types/src/types.rs:131)
// snake_case: todo | in_progress | in_review | testing | done | blocked | cancelled | deferred
// 注:constants/project.ts 的 mapLegacyStatus 兜底老 DB 旧态(completed/review_ready/merged/abandoned),
// 仅在 taskStatusLabel/taskStatusClass 辅助函数内映射,record.status 本身经后端 is_valid 拦截无旧态。
export type TaskStatus =
| 'todo' | 'in_progress' | 'in_review' | 'testing' | 'done' | 'blocked' | 'cancelled' | 'deferred'
export interface TaskRecord {
id: TaskId
project_id: ProjectId
title: string
description: string
status: TaskStatus
priority: number // 0=critical, 1=high, 2=medium, 3=low
branch_name: string | null
assignee: string | null
workflow_def_id: string | null
base_branch: string | null
/** review 退回累计轮数(in_review→in_progress / testing→in_review 各 +1) */
review_rounds: number
/**
* 任务产出 JSON 字符串(决策 a:task 中心,产出跟 task 走)。
* schema: { text: string, model?: string, usage?: {...}, review?: {
* verdict: 'pass'|'fail'|'unknown', summary: string, suggestions: string[], ... } }
* 旧任务无产出时为 undefined。前端 parsedOutput try/catch 兜底解析。
*/
output_json?: string
/** 关联灵感 ID(F-260619-01,1对1 单向,任务→灵感)。未关联时为 undefined。 */
idea_id?: string
/**
* 管理维度池标记(知识图谱 Phase 1,V29 列)。与 status(执行维度)正交。
* 取值 backlog(需求池)/todo(待办池)/decision(待决策池)/active(执行中)/done(已完成)。
* 后端 DEFAULT 'todo' 保证老任务迁移后取值确定,前端始终收到该字段。
*/
queue: string
/** 父任务 ID(1 级嵌套,无孙任务,由 IPC 校验)。顶层任务(无父)为 null/undefined。 */
parent_id?: string | null
/**
* 结构化需求规格 JSON 字符串(知识图谱 Phase 1,V29 列)。
* 结构 { background, acceptance_criteria[], scope[], technical_design, custom_fields }。
* 老任务无结构化规格时为 undefined。
*/
content_json?: string
created_at: string
updated_at: string
}
export interface TaskTreeNode {
/** 父任务记录(含 parent_id/queue 等全字段) */
parent: TaskRecord
/** 父任务的直接子任务(1 级嵌套,无孙任务) */
children: TaskRecord[]
}
/** delete_task 新返回结构(破坏性变更):删除父任务时级联软删其子任务 */
export interface TaskDeleteResult {
ok: boolean
/** 级联软删的子任务数 */
cascaded: number
}
export interface CreateTaskInput {
project_id: ProjectId
title: string
description?: string
priority?: number
branch_name?: string
assignee?: string
/** 关联灵感 ID(1对1 单向,可空)。空字符串视为不关联(与后端一致)。 */
idea_id?: string
/** 管理池标记(backlog/todo/decision/active/done,可空)。不传后端默认 todo。 */
queue?: string
/** 父任务 ID(1 级嵌套,可空)。顶层任务不传;空字符串后端视为 None。 */
parent_id?: string | null
}
/**
* 任务多条件查询入参(F-260621-02,对齐后端 TaskQuery)。
*
* 所有字段可选;全空 → 后端返回全量未删任务(created_at DESC,等价改造前 list_active)。
* - `project_id`:项目精确匹配(命中 idx_tasks_project_id)
* - `status`:状态精确匹配(P1 下沉,命中 idx_tasks_status,取代前端 filter)
* - `priority`:优先级精确匹配(0=critical..3=low)
* - `assignee`:负责人精确匹配
* - `keyword`:title/description LIKE %kw%(P2,取代前端 filter)
* - `order_by`:白名单 created_at/updated_at/priority/status(后端白名单校验防注入),恒 DESC
* - `limit`/`offset`:分页(limit 钳制上限 500)
*
* 注:project_id/priority/assignee/order_by/limit/offset 为基建就绪,当前视图仅用
* status(P1)+keyword(P2);P3 排序分页 UI 待数据量增长。
*/
export interface TaskQuery {
project_id?: string | null
status?: string | null
priority?: number | null
assignee?: string | null
keyword?: string | null
order_by?: string | null
limit?: number | null
offset?: number | null
/** 管理池标记精确匹配(backlog/todo/decision/active/done,可空) */
queue?: string | null
/** 父任务 ID 精确匹配(取某父任务的全部子任务,可空) */
parent_id?: string | null
}
// ============================================================
// 工作流
// ============================================================
// WorkflowRecord.status union 类型 — 值集对齐后端 WorkflowStatus serde (df-types/src/types.rs:195)
// snake_case: pending | running | paused | completed | failed | cancelled
export type WorkflowStatus =
| 'pending' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled'
export interface WorkflowRecord {
id: string
name: string
dag_json: string
status: WorkflowStatus
triggered_by: string | null
project_id: string | null
task_id: string | null
created_at: string
completed_at: string | null
}
/** 工作流事件载荷(后端 emit 的结构) */
/** 工作流事件类型字面量联合(对应后端 WorkflowEvent serde tag=type rename_all=snake_case,R7收窄防写错 type 字符串) */
export type WorkflowEventType =
| 'node_started' | 'node_progress' | 'node_output' | 'node_completed' | 'node_failed' | 'node_cancelled'
| 'workflow_paused' | 'workflow_completed' | 'workflow_failed'
| 'human_approval_request' | 'human_approval_response'
export interface WorkflowEventPayload {
execution_id: string
event: {
type: WorkflowEventType
[key: string]: unknown
}
}
/** 工作流 DAG 节点实时状态(WorkflowDagDisplay 高亮 + workflow store 共享推导) */
export type NodeStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped'
// ============================================================
// 通用
// ============================================================
export type UpdateField = {
id: string
field: string
value: string
}
// ============================================================
// 数据变更联动刷新(AR-11 方案A)
// ============================================================
/**
* 后端 AI 工具(create/update/delete/restore/purge/bind_directory 等)执行成功后
* emit 的 "df-data-changed" 事件负载。前端 store listen 后按 entity 调对应 load 刷新列表。
*/
export interface DfDataChangedPayload {
/** 变更实体:project / task / idea */
entity: 'project' | 'task' | 'idea'
/** 变更动作:create / update / delete */
action: 'create' | 'update' | 'delete'
}
// ============================================================
// AI 聊天
// ============================================================
export interface AiProviderConfig {
id: string
name: string
provider_type: string
api_key: string
base_url: string
default_model: string
models: string | null
/** 模型能力配置数组4 维度 + 路由控制;DB TEXT 列 JSON 解析,老库可能缺失) */
model_configs?: ModelConfig[]
is_default: boolean
config: string | null
created_at: string
updated_at: string
/** provider 是否进入负载均衡池(c)。false=仅配置存在,不参与主链路由/选池。老库默认 true。 */
enabled?: boolean
/** provider 在负载均衡池中的选择权重(0-100,c)。高权重优先被选为主;同权重近似轮询。老库默认 50。 */
weight?: number
}
// ============================================================
// F-01 模型能力系统(ModelConfig,与 crates/df-ai-core/src/model.rs 严格对齐)
// ============================================================
/** 维度 1 — 模态(模型能接收什么输入)。serde snake_case 字面量。 */
export type Modality = 'text' | 'vision' | 'audio' | 'video'
/** 维度 2 — 能力(模型能做什么)。serde snake_case 字面量。 */
export type Capability = 'tool_use' | 'embedding' | 'code_gen'
/** 维度 3 — 价格分级(成本)。serde snake_case 字面量。'free' 已删(B-260618-05 死档,预设/启发式从不赋值)。 */
export type CostTier = 'low' | 'medium' | 'high'
/** 维度 4 — 智力分级。serde snake_case 字面量。CR-260618-11#3:与 CostTier 同,cost/intel 已从路由硬过滤及前端标签去除,字段保留仅 DB 兼容,不再参与路由选模型。 */
export type IntelligenceTier = 'lite' | 'standard' | 'plus' | 'ultra'
/** 探测来源(只读,由后端探测器填充)。serde snake_case 字面量。 */
export type ProbeSource = 'user_set' | 'preset_table' | 'heuristic' | 'default'
/** 单模型完整配置(4 维度 + 路由控制 + 探测元数据)。 */
export interface ModelConfig {
/** 模型名(如 "glm-4v-flash") */
model_id: string
/** 启用/禁用(false=配了但不参与路由) */
enabled: boolean
/** 用户自定义别名(可选) */
label?: string
/** 模态集合 */
modalities: Modality[]
/** 能力集合 */
capabilities: Capability[]
/** 价格分级 */
cost_tier: CostTier
/** 智力分级 */
intelligence: IntelligenceTier
/** 权重(0-100,同能力候选中优先选权重高的) */
weight: number
/** 上下文窗口大小(tokens) */
context_window: number
/** 探测来源(可选) */
probe_source?: ProbeSource
}
/**
* AI 错误分类字面量集。
* 与后端 `commands::ai::ErrorType` 的 serde(rename_all = "snake_case") 严格对齐。
* 前端消费方 switch 于此常量集做差异化提示(auth 引导填 key / network 提示连接 / 等)。
*/
export type AiErrorType = 'auth' | 'network' | 'timeout' | 'provider_config' | 'unknown'
/**
* L2 统一状态机 — 对话生命周期状态字面量(对齐后端 ConvState serde rename_all=snake_case,
* crates/df-ai/.../agentic/conv_state.rs)。5 态:
* - idle:空闲,可接新请求
* - generating:生成中(含审批挂起期,审批是 loop 内暂停点非独立态)
* - stopping:停中(用户已点停止,loop 收尾中,瞬态)
* - error:出错(用户可重试回 generating)
* - compressed:压缩中(Generating/Idle 期间瞬态派生,压缩完成回原态)
*
* 批4 双轨收口后(2026-06-25):convStates(enum Map)是「会话是否生成中」的唯一真相源,
* 旧 generatingConvs bool 轨已退役。CONV_STATE_ENABLED=off 或老后端不发 AiConvStateChanged 时,
* useAiEvents.handleEvent 活跃事件兜底写 setConvState('generating') 作为 enum 写入口
* (替代旧 bool 轨 add),消费方不再回退 bool 判断。streaming 单值是另一条过渡态
* (F-09 per-conv streaming 收口前保留),不在本状态机口径内。
*/
export type ConvState = 'idle' | 'generating' | 'stopping' | 'error' | 'compressed'
/** AI 聊天事件(后端 emit 的结构,conversation_id 用于多对话流式路由) */
export type AiChatEvent = ({
type: 'AiTextDelta'; delta: string
} | {
type: 'AiToolCallStarted'; id: string; name: string; args: unknown
} | {
type: 'AiToolCallCompleted'; id: string; result: unknown
} | {
// AE-2025-04 会话级信任:同会话已批准过同类操作(同工具+同目录)自动放行,前端显示轻量 toast。
// 不写消息/不动 pending(Started/Completed 仍独立发),仅提示性事件。
type: 'AiToolAutoApproved'; id: string; tool: string; dir: string
} | {
// path_auth 审批链阶段3b(统一审批模型):AiApprovalRequired 携带 kind 字段区分
// 'path'(F-260619-03 Phase B 路径授权挂起,走 once/session/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
} | {
// UX-2025-04 / CR-30-2 / 决策 F-260616-07 a1: incomplete 标记流中途失败保文(网络中断),
// 前端据此差异化展示(如系统提示「⚠ 响应因网络中断不完整」)。正常完成/停止均为 undefined。
type: 'AiCompleted'; total_tokens: number; prompt_tokens: number; completion_tokens: number
/** token 分项显示(2026-08-02):cache 命中(低价,deepseek prompt_cache_hit/anthropic cache_read) */
prompt_cache_hit_tokens: number
/** 未命中(全价真实输入,deepseek prompt_cache_miss/anthropic cache_creation)。前端 in 显示用此 */
prompt_cache_miss_tokens: number
/** 思考(deepseek-reasoner/o1 reasoning_tokens,隐藏输出)。0=非 reasoning 模型 */
reasoning_tokens: number
/**
* G4.3:该轮 token 用量是否估算值(后端 agentic 运行时兜底:round_usage.prompt_tokens==0
* 时用 estimated_prompt 填充并打标)。true=per-response 估算(prompt 非真实,provider 未报),
* undefined/false=真实值。前端 MessageList 据此标注『估算』区分 estimated vs real。
*/
is_estimated?: boolean
incomplete?: boolean; pinned_goals?: GoalEntry[]
} | {
type: 'AiError'; error: string; error_type?: AiErrorType
} | {
// F-260622-02 跨端用户消息同步:远程(miniapp)发来的用户消息,后端 route_send_message
// 放行后 publish_event 广播。桌面 useAiEvents 收到后补 user 气泡(桌面本地 sendMessage 是
// 乐观渲染不发此事件)。handleEvent 去重防双气泡(末条已是同 content user 跳过)。
type: 'AiUserMessage'; message: string
} | {
type: 'AiAgentRound'; round: number
} | {
type: 'AiHeartbeat'
} | {
// 达 max_iterations 暂停态(后端仍 generating=true),前端展示操作卡询问继续/停止
type: 'AiMaxRoundsReached'
} | {
// F-260619-03 Phase B: 路径授权弹窗(LLM 想访问白名单外目录,后端挂起 loop 等用户决定)。
// 前端弹窗四选项:"本次"(once 执行后清)/"当前会话"(session 切会话清)/"始终放行"(persistent)/"拒绝" → ai_authorize_dir IPC。
type: 'AiDirAuthRequired'; id: string; tool: string; path: string; dir: string
} | {
// L1 求助协议(aichat 体验与 agent 能力系统化重构 §2.3,2026-06-21):
// agent 断路器熔断 / 自省时发,结构化求助(区别于 prompt 教 AI 主动问用户——LLM 不可靠)。
// 字段对齐后端 AiChatEvent::AiHelpRequired:reason(原因摘要)/context(已试情况)/options(建议选项)。
// 前端渲染求助卡显 reason + options 按钮供用户选,与 AiApprovalRequired(工具执行前审批)/
// AiMaxRoundsReached(达 max 被动截断)语义区分——求助=AI 主动「我搞不定」。
type: 'AiHelpRequired'; reason: string; context: string; options: string[]
} | {
// L2 统一状态机(批2 1b/1c):ConvState 经 AiChatEvent 推前端,供 status union 消费。
// conv_state 值为 idle|generating|stopping|error|compressed(snake_case,对齐后端 ConvState serde)。
// 后端在 conv_state 变化时(入口→generating / reset·drop→idle)emit;conversation_id 标识
// 哪个会话的状态变了(F-09 多会话并发下前端按 convId 路由)。批4 双轨收口后:CONV_STATE_ENABLED=off
// 或老后端不发本事件时,useAiEvents.handleEvent 活跃事件兜底写 setConvState('generating') 作为
// enum 写入口(替代旧 bool 轨 add),消费方不再回退 streaming/generating bool 判断。
type: 'AiConvStateChanged'; conv_state: ConvState
} | {
// 流式调用自动重试提示(前端可在错误气泡内显示「重试 n/m」)
type: 'AiStreamRetry'; attempt: number; max_attempts: number
} | {
// F-15 上下文管理生命周期事件(原 useAiContext 独立监听器,现合入统一分发器)
// 后端在手动/自动压缩、清空上下文时 emit,前端据此复位 loading + toast/刷新。
type: 'AiCompressing'
} | {
// 手动压缩成功(IPC compressContext 完成后 emit):带摘要供前端回填 system 消息
type: 'AiManualCompressed'; summary: string
} | {
// 自动压缩成功(loop 内超预算压缩):前端静默复位 loading(不弹 toast,不打断流)
type: 'AiAutoCompressed'; summary: string
} | {
// 上下文已清空:前端刷新消息列表 + toast 提示
type: 'AiContextCleared'
}) & {
conversation_id?: ConvId
}
/**
* 多模态消息内容片(F-260614-05 Phase 2b 前端类型,对齐后端 df-ai-core ContentPart)。
*
* 后端 serde:`#[serde(tag = "type", rename_all = "snake_case")]`,
* 前端 discriminator 用字面量 `type` 字段('text' | 'image'),与后端 wire 格式严格对齐。
* Image 片:url / base64 二选一(base64 非空时 url 忽略),media_type 仅 base64 模式必填。
*/
export type ContentPart =
| { type: 'text'; text: string }
| {
type: 'image'
url?: string | null
base64?: string | null
/** base64 模式必填(image/png|jpeg|webp|gif);url 模式可空 */
media_type?: string | null
/** 可选 alt(vision 模型/降级文本时用) */
alt?: string | null
}
/** AI 消息(前端渲染用,isError 标记错误消息用于差异化样式) */
export interface AiMessage {
id: MessageId
role: 'user' | 'assistant' | 'tool' | 'system'
content: string
isError?: boolean
/**
* 消息上下文状态(F-15 手动上下文管理)。
* null | undefined | 'active' → 正常消息
* 'archived_segment' → 清空上下文归档标记(前端折叠成"已归档 N 条"分隔条)
* 'compressed' → 压缩标记(后端不再发给 LLM;前端照常展开)
* 'truncated' → switchConversation 已过滤,前端不渲染
* 字面量联合:改 status 取值时编译器拦截,避免散布 string cast 失去类型校验。
*/
status?: 'active' | 'archived_segment' | 'compressed' | 'truncated' | null
toolCalls?: AiToolCallInfo[]
/** 生成该消息的 model(仅 assistant 消息,历史消息从 DB 读) */
model?: string
/**
* 多模态内容片(F-260614-05 Phase 2b)。
* undefined/空=纯文本消息(content 即全部载荷);非空含 Image 片=多模态消息,
* 用户气泡内渲染 <img>(base64 data URI 或 url)。仅 user 消息有(parts 来自粘贴/拖拽输入)。
* 历史消息从 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[]
/**
* 这一轮对话的 token 用量(仅 assistant 消息,每轮 AiCompleted 时回填)。
* undefined=未记录(历史消息/未启用 token 显示)。
*
* token 分项显示(2026-08-02):tokenUsage 扩展 cache_hit/cache_miss/reasoning。
* in 显示用 cache_miss(全价真实),非 prompt(总,含 cache_hit 掩盖命中比例)。
* 老消息(无 cache 字段)cache_hit/cache_miss/reasoning undefined,前端 fallback prompt_tokens。
*/
tokenUsage?: {
prompt: number
completion: number
/** 缓存命中(低价) */
cache_hit?: number
/** 未命中(全价真实输入,前端 in 显示用此) */
cache_miss?: number
/** 思考 token(隐藏输出) */
reasoning?: number
/**
* G4.3:该轮 token 用量是否估算值(后端 AiCompleted.is_estimated 透传)。
* true=prompt_tokens 缺失按估算展示,MessageList 标注『估算』;undefined=false=真实值。
*/
is_estimated?: boolean
}
/**
* 本轮输入 token(消息级持久化,后端 ChatMessage.prompt_tokens 镜像)。
* reload 时由 switchConversation 映射回 tokenUsage(历史 assistant 消息 token 回显)。
* 仅 assistant 消息有;undefined=老消息(无此列时 DB 返 NULL)。
*/
prompt_tokens?: number
/** 本轮输出 token(语义同 prompt_tokens)。 */
completion_tokens?: number
/** 缓存命中 token(低价)。老消息 undefined(向前兼容)。 */
prompt_cache_hit_tokens?: number
/** 未命中 token(全价真实输入,前端 in 显示用此)。老消息 undefined。 */
prompt_cache_miss_tokens?: number
/** 思考 token(deepseek-reasoner/o1 reasoning_tokens)。老消息 undefined。 */
reasoning_tokens?: number
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 便于复用。
export type AiToolCallStatus = 'running' | 'pending_approval' | 'completed' | 'rejected'
/** 工具调用信息 */
export interface AiToolCallInfo {
id: string
name: string
args: unknown
status: AiToolCallStatus
result?: unknown
/** 审批风险说明(AiApprovalRequired 时填充) */
reason?: string
/** write_file 行级 unified diff(旧文件 vs 新内容),审批卡即时预览。
* 旧文件不存在(新建)或非 write_file 为 undefined,前端回退显新 content。 */
diff?: string
/**
* path_auth 审批链阶段3b(统一审批模型):审批类型,区分两类挂起。
* - 'path'(F-260619-03 Phase B 路径授权挂起):ToolCard 显 once/session/always/deny 四选项,
* 调 aiApi.authorizeDir(once 写 once 层单次执行后清 / session 写会话临时 / 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
/** 工具开始执行时间(前端在 AiToolCallStarted 时记录,毫秒)。历史消息从 DB 回载无此字段,
* 后端事件协议不含耗时,卡片耗时/轨迹总耗时由前端时间差计算,缺字段时隐藏不显 0。 */
startedAt?: number
/** 工具执行完成时间(前端在 AiToolCallCompleted 时记录,毫秒)。语义同 startedAt。 */
completedAt?: number
/**
* A2-B10 conv-scoped 审批收尾:挂起审批所属的会话 id(前端判定用,仅挂起项设置)。
* 来源:AiApprovalRequired / AiDirAuthRequired(统一审批归一)事件透传 conversation_id
* (缺失时兜底 activeConversationId);switchConversation 恢复历史挂起时填目标会话 id。
* cleanupTerminatedConversation 据此仅清目标会话的待审批项 + 对称清其审批超时计时器,
* 不连累并发会话正在审批的卡(conv-scoped,误清其他会话审批 = 回归)。
* undefined = 非挂起项或无法归属(保守保留,不清)。对齐 PendingDirAuth.conversationId。
*/
conversationId?: string
}
/** 对话列表摘要 */
export interface AiConversationSummary {
id: ConvId
title: string | null
provider_id: string | null
model: string | null
pinned_goals?: GoalEntry[]
archived: boolean
pinned?: boolean
prompt_tokens?: number | null
completion_tokens?: number | null
created_at: string
updated_at: string
}
/** G1 目标钉扎:单条目标记录(含状态追踪) */
export interface GoalEntry {
text: string
/** "active" | "completed" */
status: 'active' | 'completed'
}
/** 审批选择类型single=单选默认,multiple=多选) */
export type ApprovalSelectType = 'single' | 'multiple'
/** 人工审批请求 */
export interface HumanApprovalRequest {
execution_id: string
node_id: string
title: string
description: string
options: string[]
/** 缺省 single(向后兼容),multiple 时允许多选 */
select_type?: ApprovalSelectType
}
/** 人工审批响应 */
export interface HumanApprovalResponse {
execution_id: string
node_id: string
decision: string
/** 多选结果(single 时长度=1,multiple 时长度≥1) */
decisions?: string[]
comment?: string
}
/** 切换对话返回(含 messages JSON) */
export interface AiConversationDetail {
id: ConvId
title: string | null
messages: string // JSON string of ChatMessage[]
/** 生成中返回 true(后端 ai_conversation_switch 在 generating 时置),前端据此禁编辑*/
readonly?: boolean
}
/** 技能信息(本机 Claude skill,供 `/` 联想 + 注入) */
export interface SkillInfo {
name: string
description: string
argument_hint?: string
/** skill | command | plugin */
source: string
/** SKILL.md 绝对路径(后端注入时读全文) */
path: string
}
// ============================================================
// 知识库
// ============================================================
/** 知识条目(与 Rust KnowledgeRecord 严格对齐) */
export interface KnowledgeRecord {
id: string
kind: string // review_rule | prompt_template | pitfall | architecture_pattern | diagnosis | deployment_note | workflow_optimization
title: string
content: string
tags: string | null // JSON 数组字符串
status: string // candidate | pending_review | published | archived
confidence: string | null // high | medium | low
reuse_count: number
verified: boolean
source_project: string | null
source_ref: string | null
/** AI 提炼判断依据("为何值得沉淀"),手动录入为 null */
reasoning: string | null
created_at: string
updated_at: string
}
/** 创建知识入参 */
export interface CreateKnowledgeInput {
kind: string
title: string
content: string
tags?: string
source_project?: string
source_ref?: string
confidence?: string
}
/** 检索知识入参 */
export interface KnowledgeSearchInput {
query: string
kind?: string
limit?: number
}
/** 知识生命线事件记录(产生/审核/引用/归档审计) */
export interface KnowledgeEventRecord {
id: string
knowledge_id: string
/** created | extracted | status_changed | referenced */
event_type: string
source_ref: string | null
/** JSON 字符串,内容因 event_type 而异(见后端 KnowledgeTimeline) */
context_json: string | null
timestamp: string
}
/** 知识详情聚合负载(基本信息 + 全部生命线事件) */
export interface KnowledgeDetailPayload {
knowledge: KnowledgeRecord
events: KnowledgeEventRecord[]
}
/** 编辑知识入参(部分更新,仅传需改字段) */
export interface UpdateKnowledgeInput {
title?: string
content?: string
tags?: string
confidence?: string
reasoning?: string
}
/** 知识库行为配置(提取 + 注入 + 向量检索) */
export interface KnowledgeConfig {
auto_extract: boolean
trigger_mode: 'on_complete' | 'manual_only'
min_messages: number
auto_inject: boolean
/** 语义检索(向量)开关,默认 false——关闭时纯 LIKE 零外部依赖 */
vector_enabled: boolean
/** embedding 用的 provider id(仅 openai_compat 类型) */
embedding_provider_id: string | null
/** embedding 模型名(如 embedding-3) */
embedding_model: string | null
}
// ============================================================
// 多 Agent 并行执行(Plan/SubTask/Conflict)
// ============================================================
/** Plan 执行状态 */
export type PlanStatus = 'planning' | 'executing' | 'merging' | 'done' | 'error'
/** SubTask 执行状态 */
export type SubTaskStatus = 'pending' | 'running' | 'done' | 'error'
/** 冲突解决状态 */
export type ConflictResolution = 'pending' | 'a' | 'b' | 'merged' | 'manual'
/** 冲突类型 */
export type ConflictType = 'file' | 'semantic'
/** Plan 记录 */
export interface PlanRecord {
id: string
conversation_id: string
user_message_id: string | null
status: PlanStatus
subtask_count: number
created_at: string
completed_at: string | null
}
/** SubTask 记录 */
export interface SubTaskRecord {
id: string
plan_id: string
persona_id: string | null
intent: string
status: SubTaskStatus
layer: number
/** 依赖的 SubTask id 列表(JSON 数组字符串,需 parse) */
deps: string | null
/** Git worktree 分支名(null=非 Git 工程降级串行) */
branch: string | null
created_at: string
completed_at: string | null
}
/** 冲突记录 */
export interface ConflictRecord {
id: string
plan_id: string
file_path: string
conflict_type: ConflictType
subtask_a: string | null
subtask_b: string | null
diff_a: string | null
diff_b: string | null
resolution: ConflictResolution
resolved_by: 'reviewer' | 'user' | null
created_at: string
resolved_at: string | null
}
/** 冲突信息(AiMergeCompleted 事件载荷) */
export interface ConflictInfo {
id: string
file_path: string
conflict_type: ConflictType
subtask_a: string | null
subtask_b: string | null
diff_a?: string | null
diff_b?: string | null
}