新增: 推进链阶段3收口(advance_task/run_workflow工具+status状态机收口)
This commit is contained in:
@@ -359,7 +359,7 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
|
||||
})},
|
||||
);
|
||||
registry.register(
|
||||
"update_task", "更新任务的指定字段(title/description/status/priority/assignee 等),需要提供任务 ID、字段名和新值",
|
||||
"update_task", "更新任务的指定字段(title/description/priority/assignee 等),需要提供任务 ID、字段名和新值。注意:status 改动须走 advance_task 工具(状态机推进,7 态 todo/in_progress/in_review/testing/blocked/done/cancelled,按 target_status 推进不可跳跃),本工具不接受 status 字段(防绕过状态机)",
|
||||
df_ai::ai_tools::object_schema(vec![("id", "string", true), ("field", "string", true), ("value", "string", true)]),
|
||||
RiskLevel::Medium,
|
||||
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
|
||||
@@ -368,6 +368,14 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
|
||||
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
|
||||
let field = args["field"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 field"))?;
|
||||
let value = args["value"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 value"))?;
|
||||
// F-03 收口(子项3):status 改动必须走 advance_task 工具(状态机推进),
|
||||
// 本工具显式拒绝 status 字段,防 AI 绕过状态机直接改库。
|
||||
// 对齐 Agent B df-storage tasks 白名单移 status——双重防御(schema 拒绝 + 白名单拒绝)。
|
||||
if field == "status" {
|
||||
anyhow::bail!(
|
||||
"status 改动须走 advance_task 工具(状态机推进,7 态不可跳跃),本工具不接受 status 字段"
|
||||
);
|
||||
}
|
||||
// 复用 df-storage CRUD 白名单(按表隔离),与 update_field 校验同源
|
||||
if !df_storage::crud::is_allowed_column("tasks", field) {
|
||||
anyhow::bail!("不允许更新字段 '{}'", field);
|
||||
@@ -378,6 +386,65 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
|
||||
})
|
||||
})},
|
||||
);
|
||||
// F-03 收口(子项1):advance_task AI 工具 — 推进任务状态的唯一合法路径。
|
||||
// D-260616-03:推进链业务逻辑(状态机校验 + 原子 CAS + review_rounds 累加)落
|
||||
// df-nodes::task_advance_node::advance_task_atomic,本 handler 直接调它(与 IPC
|
||||
// commands::task::advance_task 同源,thin 转发),无需 AppHandle/State/EventBus。
|
||||
// 风险 Medium:status 变更属状态机推进(非破坏),但改变任务流转语义需人工知情。
|
||||
registry.register(
|
||||
"advance_task", "推进任务状态,必须走状态机(7 态: todo/in_progress/in_review/testing/blocked/done/cancelled),按 target_status 推进,不可跳跃(非法转换会被状态机拦截)。退回转换(in_review→in_progress 等)自动累加 review_rounds。返回推进后的最新 TaskRecord",
|
||||
df_ai::ai_tools::object_schema(vec![("id", "string", true), ("target_status", "string", true)]),
|
||||
RiskLevel::Medium,
|
||||
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
|
||||
let db = db.clone();
|
||||
Box::pin(async move {
|
||||
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
|
||||
let target_status = args["target_status"].as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("缺少 target_status"))?;
|
||||
// 复用 df-nodes 推进链核心逻辑(状态机 + 原子 CAS + review_rounds),
|
||||
// 与 commands::task::advance_task IPC 同源,避免双轨。
|
||||
let repo = df_storage::crud::TaskRepo::new(&db);
|
||||
let updated = df_nodes::task_advance_node::advance_task_atomic(
|
||||
&repo, id, target_status,
|
||||
).await?;
|
||||
// 返回推进后的 TaskRecord(含新 status / 累加后的 review_rounds),供 LLM 确认推进结果。
|
||||
Ok(serde_json::to_value(&updated)?)
|
||||
})
|
||||
})},
|
||||
);
|
||||
// F-260616-07 阶段3(子项2):run_workflow AI 工具注册实装。
|
||||
// 实施路径文档 §三 列为阶段3 必做项(tool_registry.rs 此前无此工具连空壳都没有)。
|
||||
// 描述明确按任务 target_status 推进对应工作流(含 AiNode 自审 / HumanNode 核对闸门)。
|
||||
//
|
||||
// handler 约束说明:run_workflow 真正执行需要 AppHandle(转发 workflow-event 到前端) +
|
||||
// AppState(registry 构建 DAG / event_bus 订阅 / workflows Repo 落库 / workflow_state_registry
|
||||
// 注销)等 Tauri 注入态,这些在 tool handler(仅持有 db: Arc<Database>)中无法构造。
|
||||
// 现阶段仅注册 ToolDefinition(schema + risk + 描述)让 LLM 知晓此能力并产出 tool_call;
|
||||
// 真正触发须走 Tauri IPC(经 invoke_handler 注册的 run_workflow 命令,持有完整 State)。
|
||||
// handler 显式报错引导走 IPC,避免在 handler 内重放 DAG 执行引擎(违反单一执行路径原则)。
|
||||
// 后续若需 AI 直驱完整工作流,需扩展 build_ai_tool_registry 注入 AppState 句柄(改 state.rs,
|
||||
// 留待推进链后续批次)。
|
||||
registry.register(
|
||||
"run_workflow", "按任务 target_status 推进对应工作流(含 AiNode 自审 / HumanNode 核对闸门)。参数 task_id + target_status 同时提供才联动任务推进(完成后按 target_status 推进任务,失败按退回态回滚)。属高风险操作(触发工作流引擎执行),须人工批准",
|
||||
df_ai::ai_tools::object_schema(vec![("task_id", "string", true), ("target_status", "string", true)]),
|
||||
RiskLevel::High,
|
||||
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
|
||||
let _db = db.clone();
|
||||
Box::pin(async move {
|
||||
let task_id = args["task_id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 task_id"))?;
|
||||
let target_status = args["target_status"].as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("缺少 target_status"))?;
|
||||
// handler 无法访问 AppHandle/State(registry/event_bus/workflows Repo),
|
||||
// 真正执行须走 run_workflow Tauri IPC(持有完整 State)。
|
||||
// 这里返回明确错误引导前端走 IPC,而非在 handler 内重放 DAG 引擎。
|
||||
Err(anyhow::anyhow!(
|
||||
"run_workflow 工具需经 Tauri IPC 执行(持有 AppHandle/State),handler 无 State 句柄。\
|
||||
请前端收到此 tool_call 后转调 invoke('run_workflow', {{ task_id: {}, target_status: {} }})。",
|
||||
task_id, target_status
|
||||
))
|
||||
})
|
||||
})},
|
||||
);
|
||||
// FR-D6:delete_task 对齐 commands::task::delete_task(D-260616-02 软删除)
|
||||
// tasks 表 V14 加 deleted_at,delete_task 改调 soft_delete 移入回收站(对标 delete_project)
|
||||
registry.register(
|
||||
@@ -1189,6 +1256,9 @@ pub(crate) fn display_hint_for_tool(name: &str) -> Option<(&'static str, &'stati
|
||||
"patch_file" => ("修改文件:{}", &["path"]),
|
||||
"delete_file" => ("删除文件:{}({})", &["path", "permanent"]),
|
||||
"rename_file" => ("重命名/移动:{} → {}", &["from", "to"]),
|
||||
// F-03 收口:推进链工具审批文案(advance_task/run_workflow)
|
||||
"advance_task" => ("推进任务状态:{} → {}", &["id", "target_status"]),
|
||||
"run_workflow" => ("触发工作流:任务{} 推进到 {}", &["task_id", "target_status"]),
|
||||
_ => return None,
|
||||
};
|
||||
Some((template, keys))
|
||||
@@ -1213,6 +1283,8 @@ pub(crate) fn tool_display_hint(name: &str) -> Option<&'static str> {
|
||||
"bind_directory" => Some("绑定目录"),
|
||||
"create_idea" => Some("捕获灵感"),
|
||||
"update_task" => Some("修改任务"),
|
||||
"advance_task" => Some("推进任务状态"),
|
||||
"run_workflow" => Some("触发工作流"),
|
||||
"delete_task" => Some("删除任务"),
|
||||
"update_idea" => Some("修改灵感"),
|
||||
"delete_idea" => Some("删除灵感"),
|
||||
|
||||
Reference in New Issue
Block a user