feat(intent): 意图删除支持假删除/真删除两种模式

删除入口区分两种模式(待领/运行中/已暂停的意图均可删除):

- 假删除(默认):意图置 state='deleted'、删除原因记入新字段 delete_reason,
  保留节点与全部产出/血缘,不再在图上另挂 fact。
- 真删除:物理删除意图及「仅由它支撑」的独占子孙(沿 yields/derived_from
  级联到叶子),避免孤立数据;共享节点、目标、任务根事实保留。被删意图先做
  token rollup 并清理 activity/副会话,被删 finding 先删 findings 行。

两种模式都照常用 cancelled 触发通知 planner(带删除前捕获的意图摘要 + 原因),
planner 文案不再声称原因挂为事实。

- DB: 新增 SoftDeleteIntent、重写 CancelIntent 为深度独占级联;schema 加
  delete_reason 列与 intent 的 deleted 状态(含旧库迁移)。
- 前端: 删除对话框加模式切换与动态说明,状态徽章/图标新增「已删除」,
  播报板展开展示删除原因。
This commit is contained in:
Autumn-27 committed 2026-09-17 12:52:57 -04:00
1 parent 6e3dbf6452
commit 4756bb1c45
17 files changed
+593 -238

No files matched your search

+2 -2
View File
@@ -45,8 +45,8 @@ var (
"规划者调用 kill_work 主动终止了这条意图,通常表示方向跑偏或已无继续价值;意图会标记为 stopped,不会自动重新领取")
AbortWorkPausedByUser = cause("work_paused_by_user", "用户暂停了这条 Worker 意图",
"用户暂停了正在运行的 Worker。本次调用被取消,意图转为 paused;已经登记的意图、事实、漏洞和活动记录全部保留,恢复后从头重新执行")
AbortWorkCancelledByUser = cause("work_cancelled_by_user", "用户取消了这条 Worker 意图",
"用户取消了正在运行的 Worker。本次调用被取消;Worker 退出写入区后,服务端会事务性删除该意图及其直接产生的事实、漏洞和执行记录")
AbortWorkCancelledByUser = cause("work_cancelled_by_user", "用户删除了这条 Worker 意图",
"用户删除了正在运行的 Worker。本次调用被取消;Worker 退出写入区后,服务端按用户选择的删除模式处理该意图——假删除仅标记为已删除并保留全部产出,真删除会级联移除该意图及仅由它支撑的下游节点")
AbortWorkFinished = cause("work_finished", "Worker 已正常结束并释放 context",
"Worker 已正常结束,引擎在 detachWork 中释放其 context 资源。这不是运行中断;若它出现在中断消息中,说明取消与收场事件发生了竞态")
AbortPausedRaceGuard = cause("paused_race_guard", "任务暂停期间拒绝启动新运行",
+7 -1
View File
@@ -159,6 +159,7 @@ type TriggerEvent struct {
Kind string
IntentID int64
Detail string
Summary string // Kind=="cancelled" 专用:删除前捕获的意图摘要(真删除后节点已不存在,无法再查)
Goals []string // Kind=="goal" 专用:本次 set_goals 新增的目标文本(1 条或多条)
OldGoal string // Kind=="goal_edited" 专用:修改前的目标文本
NewGoal string // Kind=="goal_edited" 专用:修改后的目标文本
@@ -196,7 +197,12 @@ func renderTriggers(ts *db.ExplorationStore, evs []TriggerEvent) string {
case "finding":
b.WriteString(fmt.Sprintf("\n- 意图 #%d(%s)的 worker 报告了一个 finding:%s", ev.IntentID, intentSummary(ts, ev.IntentID), ev.Detail))
case "cancelled":
b.WriteString(fmt.Sprintf("\n- 意图 #%d 由用户删除,意图内容是:%s、删除原因是:%s。该意图已停止(不再执行),其原因已作为事实挂在该意图上;请据此重新规划。", ev.IntentID, intentSummary(ts, ev.IntentID), ev.Detail))
// 意图内容优先用删除时捕获的 Summary(真删除后节点已不存在,intentSummary 查不到)。
sm := ev.Summary
if sm == "" {
sm = intentSummary(ts, ev.IntentID)
}
b.WriteString(fmt.Sprintf("\n- 意图 #%d 由用户删除,意图内容是:%s、删除原因是:%s。该意图已删除(不再执行);请据此重新规划。", ev.IntentID, sm, ev.Detail))
default: // "done"
b.WriteString(fmt.Sprintf("\n- 意图 #%d(%s)的 worker 结束,输出结论:%s", ev.IntentID, intentSummary(ts, ev.IntentID), workerOutput(ts, ev.IntentID)))
if fids := factIDsYielded(ts, ev.IntentID); fids != "" {