Files
DevFlow/docs/02-架构设计/专项设计/对话内定时延时任务-设计-2026-10-10.md
lxy 8df4e33656 文档: 看板销账与登记 + 设计档六份 + 索引补录
- 看板: 907 补注/909~941 登记与销账(含 929 模型停用根因与 4 处次生缺口、931 队列丢失、933/935/937 新需求、939 上下文瓶颈清单、941 C-r0 行为变更待拍板)
- 归档: 全量完成态条目/段落 + 只读核验销账 4 段迁 todo归档/2026-10-10.md(机械搬运零措辞改动,未完成项零丢失)
- 设计档: 输入框列表预览与自动编号 / 定时消息与待发箱持久化(统一 ai_outbox 底座) / 对话内定时延时任务 / 消息渲染管线重构(P0-P2 分期) / 上下文分层组装器落地方案(记账基点单一真值,与并行写者职责划清) / FileTree 大目录方案(推荐后端分页,否决虚拟滚动) / u-relay 运维手册
- 索引与事实修正: docs/INDEX crate 数 8→13 + 四子树补录 25 条 + 专项设计补录 27 条;纠正「项目无独立 ARCHITECTURE.md」等过时声明;统一 bun 口径
- CLAUDE.md: 新增「命名与代码风格」节(变量/方法名精简+准确;DRY·简洁·扩展性·高内聚低耦合·单一真值来源)
2026-10-10 08:09:25 +08:00

24 KiB

对话内定时/延时任务 — 设计

日期: 2026-10-10 | 状态: 设计稿(待拍板) | 需求锚点: docs/todo.md:703 条目 937 | 勘察: 只读实测(文件:行已核) 目标一句话: 在对话里直接说「1 小时后跑 X / 明早 9 点把任务 A 推进到审查」,系统登记一条持久化的 kind='task' 待发箱项,到点自动执行(注入发起会话续跑),任务卡显示「定时中/已触发」,并可在统一「定时列表」里查看/改期/取消。 边界: 仅设计,不改码。 与 935 的关系(要害): 937 不新造调度底座。表 / 调度器 / IPC / 事件 / KV / 「定时列表」抽屉全部复用 定时消息与待发箱持久化-设计-2026-10-10.md(该设计已覆盖 todo 935 定时消息 + 931 队列,并在其 §3.1 明确预留 kind='task' 给 937、§五 P2 交棒)。本设计只定义 kind='task' 这一分支的语义 + AI 工具面 + 触发执行 + 权限边界。 关联: 并行化与异步唤醒-设计(§二 wake delay 型 / §6.3 挂机)/无人值守长任务-设计(TaskBoundary 边界)。


一、现状勘察(锚点实测)

1.1 AI 工具面(新工具的正确落点)

件 锚点 现状
注册宏 crates/df-ai/src/ai_tools_decl.rs declare_tool! 纯 macro_rules!,零新依赖;新工具只加一个 tools/<域>.rs 文件 + 在对应 register_* 接一行
注册总入口 src-tauri/src/commands/ai/tool_registry.rs:541 build_ai_tool_registry register_data_tools(:735) 内 register_task_tools(:737)/register_workflow_tools(:739);二者已 thin 委托 tools::task::register(:767)/tools::workflow::register(:813)
任务工具 src-tauri/src/commands/ai/tools/task.rs 6 个:list_tasks(:40)/create_task(:72)/advance_task(:185,状态机推进唯一合法路径)/delete_task/get_task_count。无任何「延后」维度
工作流工具 src-tauri/src/commands/ai/tools/workflow.rs:35 run_workflow High 风险;handler 是防御兜底(返 Err),真正执行经 ai_approve → execute_run_workflow_for_tool(src-tauri/src/commands/ai/mod.rs:1367)→ run_workflow_inner(src-tauri/src/commands/workflow.rs:127);空 dag + target_status 时按 template_for(crates/df-nodes/src/task_workflow_templates.rs:28)自动选模板
conv_id 透传先例 execute_run_workflow_for_tool(mod.rs:1367) AI 工具 handler 无 State,发起会话 id 经调用方(task-local/参数)注入——schedule_task 记录发起会话照此
基线守卫 tool_registry.rs 测试 test_build_ai_tool_registry_baseline_tool_count 钉死工具名集合;新增工具须同步该断言(+1)

1.2 任务体系与推进链(被定时触发的目标能力,均已就位)

件 锚点 现状
状态机 crates/df-nodes/src/task_state_machine.rs 8 态合法转换纯函数;done 唯一终态
原子推进 crates/df-nodes/src/task_advance_node.rs:50 advance_task_atomic / :335 advance_task_with_parent 状态机校验 + CAS + review_rounds + 父聚合,已与 IPC/AI 工具三方同源

结论: 「到点后要做的事」(推进任务/跑工作流/执行指令)能力齐备,缺的只是外部时间驱动源与工具面的延时表达。

1.3 后端常驻调度器先例(unattended;935 已定 outbox 抄其形态)

件 锚点 现状
常驻轮询 src-tauri/src/commands/ai/unattended.rs:508 start;lib.rs:84 挂载 async_runtime::spawn + interval.tick() / notify.notified() select!
共享态/唤醒 unattended.rs:459 EngineShared{notify: Notify, in_flight,…};:483 notify_batch_done 单在途 + 完成钩子即时唤醒
开工链路 unattended.rs:847 spawn_agentic_session(chat.rs:796,10 参,pin_active=false) 后台会话派发正典入口
生命周期事实 lib.rs:614 on_window_event prevent_close+hide 托盘化 关窗=进程仍在=调度器照跑;进程退出=停,重启补发

关键缺口(937 视角): 引擎作业源是看板文件,不落库、不看时间点;真正解决「持久化 + 到点」的是 935 设计的 ai_outbox(见下节),937 直接搭车。

1.4 存储 / 恢复 / 事件 / UI 样板(937 复用链)

件 锚点 现状 / 复用点
迁移框架 crates/df-storage/src/migrations.rs:9 run;调度数组 :108-121(止于 (50, migrate_v50) @ :1483) 追加 (N, migrate_vN) 即挂;935 占 V51(建 ai_outbox),937 用 V52 加列
加列样板 migrations.rs:1366 migrate_v45 column_exists 守卫 + ALTER TABLE ADD COLUMN,幂等可重跑;可 DROP COLUMN 回退
启动恢复样板 audit/restore.rs:26 restore_pending_approvals 从表重建内存态;outbox 的 reset_stale_sending 同款
会话来源 models.rs:389 AiConversationRecord.origin(现仅 "sub_agent") 定时触发落后台会话时可用新值 "schedule"
事件 mod.rs:134 AiChatEvent(#[serde(tag="type")]) 935 定的 ai-outbox-changed/sent/failed 直接复用
卡片范式 src/components/ai/TaskParkedCard.vue 「定时任务卡」可照此(状态徽标 + 行为后更新)

1.5 缺口清单(937 专属,底座缺口由 935 覆盖)

  1. 工具面无延时维度:LLM 无法表达「1 小时后」(advance_task/run_workflow/create_task 均即时)。
  2. 无 kind='task' 分支:935 的 ai_outbox 表/调度器只服务消息派发,kind='task' 的载荷字段(结构化指令/任务关联/边界快照)与派发目标(注入会话 or 直推任务)尚未定义。
  3. 无触发执行的落点语义:到点「谁来跑、跑哪个会话、结果给谁看」未定。
  4. 无对话内定时卡片:工具卡如何呈现「定时中/已触发」未定。

二、目标与验收口径

目标 验收口径(可实测)
G1 对话内创建 LLM 调 schedule_task(instruction, delay_secs),对话出现定时任务卡(指令摘要 + 触发时刻/倒计时 + 「定时中」徽标);ai_outbox 落一行 kind='task', status=pending
G2 到点触发 到 due_at → 调度器把指令注入发起会话续跑(等价一条 user 消息注入),卡片变「已触发」,新 assistant 回复出现在原会话
G3 持久存活 应用关闭跨过 due_at:关窗托盘化期间照常触发;进程真退出则重启后按 935 追赶策略(§四)补触发或标过期;卡片状态经重启不丢
G4 管理面 统一「定时列表」(935 抽屉,937 项并列显示,类型标「任务」)可查看/改期/取消;改期/取消即时写库刷新卡片
G5 边界合规 创建 = Medium 风险(走既有审批);触发执行零旁路——写/High 风险仍在触发会话内走既有 audit/path_auth;黑名单永不放行
G6 不打架 触发时发起会话正生成 → 轮界排队(不打断);会话已删/不可用 → 回落独立后台会话(origin=schedule)
G7 零回归 既有 advance_task/run_workflow/create_task 行为不变;无 task 作业时调度器空转零副作用;935 的 queue/scheduled 分支不受影响

三、设计方案

3.1 总体判断(先说结论)

  1. 新工具 schedule_task,不复用 advance_task 加调度字段。 advance_task 的契约是「此刻」推进状态机(CAS + applied.matched_target 断言,tools/task.rs:185),loop 收敛判定依赖它;改成「可能延后」会污染该断言并让「任务状态」与「调度时点」耦合。调度是正交能力:指令里可以提到任务,但调度本身不是状态迁移。
  2. 底座全复用 935 的 ai_outbox + outbox.rs 调度器,937 只加 kind='task' 分支。 不新造表/不新造 loop,与需求「与 935 共用调度器」一致;亦避免「双调度器」的草率聚合。
  3. 触发执行的通用形态 = 注入发起会话(把指令当一条用户消息续跑),由触发后的 agentic loop 统一消化(它本就会 advance_task/run_workflow);确定性「直推任务状态」作 P2 快路径(§3.6、拍板点 4)。

3.2 数据形态:复用 ai_outbox + 迁移 V52 增列

935 已定义 ai_outbox(定时消息…设计 §3.1,V51),其列 text / skill / model_override / parts_json / spans_json / due_at / status / attempts 面向消息。937 复用其 text(= 指令原文,供统一列表渲染摘要)、due_at(= 触发时刻)、status、attempts,并新增三列承载 task 专属载荷:

-- 迁移 V52(937):幂等 ALTER(照 migrate_v45 样板),可 DROP COLUMN 回退
ALTER TABLE ai_outbox ADD COLUMN title         TEXT;   -- 卡片/列表标题(缺省取指令前 N 字)
ALTER TABLE ai_outbox ADD COLUMN payload_json  TEXT;   -- 结构化载荷 {instruction, task_id?, target_status?}
ALTER TABLE ai_outbox ADD COLUMN boundary_json TEXT;   -- 触发执行继承的边界快照(可空=审批模式)
  • kind='task' 行:text=指令人读文本(列表摘要用);payload_json={instruction, task_id?, target_status?}(instruction 冗余存一份便于派发时直接取;task_id/target_status 供 P2 直推快路径)。
  • 不新增独立表。AiOutboxRecord(定时消息…设计 §3.1)对应加三个 Option 字段;outbox_repo.rs 的 insert/读方法带这三列(其余复用)。
  • 迁移编号:935 占 V51,937 占 V52;若两需求同批落地,可合入一个迁移函数一次到位(建表 + 增列),但保持「V51 建表 / V52 增列」两段亦可(幂等,顺序无关)。

3.3 工具面:新增 schedule_task(declare_tool!,Medium)

新文件 src-tauri/src/commands/ai/tools/schedule.rs,仅一个工具;tool_registry.rs:737 旁接线(register_schedule_tools),基线测试 +1。

declare_tool!(
    registry, db: Arc<Database>,
    "schedule_task",
    "登记一个定时/延时执行的任务作业:到点后在发起会话里自动执行 instruction(等价到点时我把这句话重新对你说一遍)。\
     用 delay_secs 表达相对延时(如「1 小时后」=3600),或用 at 表达绝对时刻(RFC3339 或 epoch ms)。二者至少给一个,同时给以 at 为准。\
     到点执行受既有审批/路径授权约束,不绕过任何安全闸。返回 job_id 与触发时刻。",
    RiskLevel::Medium,   // 登记未来自动执行,须人工知情(对齐 advance_task 的 Medium)
    meta: ToolMeta {
        entity_action: Some(("schedule", "create")),
        approval_hint: Some(("创建定时任务:{}", &["instruction"])),
        display_verb: Some("创建定时任务"),
        ..Default::default()
    },
    schema: object_schema(vec![
        ("instruction", "string", true),   // 到点要执行的自然语言任务(必填)
        ("delay_secs", "integer", false),  // 相对延时(秒),与 at 至少一个
        ("at", "string", false),           // 绝对时刻 RFC3339;优先于 delay_secs
        ("title", "string", false),        // 卡片标题(缺省取 instruction 前 N 字)
    ]),
    args => {
        // 1. 解析 instruction(必填)/delay_secs/at/title → 折算 due_at(epoch ms)
        // 2. 发起 conv_id:handler 无 State → 经调用方注入(task-local,对齐 execute_run_workflow_for_tool mod.rs:1367)
        // 3. outbox_repo::insert(kind="task", text=instruction, payload_json={instruction}, title, due_at, status=pending)
        // 4. 唤醒调度器 notify → emit("ai-outbox-changed") 前端插卡
        // 5. 返回 { job_id, due_at }
    }
);
  • 相对 delay_secs 为主(免时区/解析歧义);at 为便利补充(LLM 依 system prompt 里的当前时间折算)。
  • conv_id:AI 工具 handler 是无 State 闭包;发起会话 id 沿用 workflow 工具先例经 task-local 注入(execute_run_workflow_for_tool 由 ai_approve 带 conv_id 调,mod.rs:1367)。ai_outbox.conversation_id 为 NOT NULL,而 AI 工具场景必在会话内,故恒可填;极少数拿不到 conv_id 的路径退回「前端/列表入口创建」(935 §3.4 composer 入口)。

3.4 调度器:复用 935 outbox.rs,加 kind='task' 派发分支

935 的调度循环(定时消息…设计 §3.3)按 kind 分派。937 只加一个分支:

// outbox.rs list_due 取项后的派发(schema 由 935 定义,此处补 kind 分支)
match item.kind.as_str() {
    "queue" | "scheduled" => { /* 935 既有:spawn_agentic_session(text,...) */ }
    "task" => fire_task_job(app, state, &item),   // ← 937 新增(§3.5)
    other => { warn!(unknown kind); }
}
  • CAS/并发/在途/唤醒钩子全部复用 935:同会话忙(can_accept_request=false)→ 本轮跳过下次再取(不丢);notify_conv_idle 完成钩子即时续发;claim CAS 防双取;reset_stale_sending 启动复位。
  • df-outbox-enabled 复用:关 = 所有 kind 冻结(937 的 task 也冻结),一致。

3.5 触发执行语义(fire_task_job,937 核心)

  1. 注入文本:[定时任务触发] {instruction}(固定前缀族,便于前端识别 + 完成卡归属)。
  2. 落点选择:
    • 发起会话存在且 can_accept_request → 注入发起会话:走 spawn_agentic_session(text=<前缀+instruction>, …, Some(conv), …, pin_active=false)(与 935 派发同源,chat.rs:796)。
    • 发起会话正生成 → 轮界排队:revert_pending(复用 935 的忙回退),下一 tick 重试(不打断流)。
    • 发起会话已删/不存在 → 回落独立后台会话:Some(new_id()) + origin="schedule",不静默丢。
  3. 落库:mark_sent(复用 935 语义:spawn 返回 Ok 即受理成功)+ emit("ai-outbox-sent", {id, conv})。
  4. 失败:spawn 前置失败(provider 未配/永久 4xx)→ mark_failed(permament(error) 复用 935 判据)+ 通知;非永久 → revert_pending 下轮重试。LLM 轮次内的失败不回流(与 935 同界定,交既有 loop 自愈)。

3.6 关闭/重启行为(复用 935 追赶策略,937 不另立规则)

  • 迁移/恢复:复用 935 的 reset_stale_sending(启动把残留 sending → pending);调度器只认 due_at <= now,故进程退出期间到期的 task 项重启后立即命中。
  • 迟到追赶策略 = 复用 935 拍板点 4(阈值 12h,建议值):迟到 ≤ 阈值 → 补触发;迟到 > 阈值 → 标过期(status='expired'? 935 用 failed/canceled,建议新增 expired 或用 failed+last_error="missed")+ 通知,列表可见可手动重排。937 不引入 24h 另一套阈值,统一走 df-outbox-* 配置(如需 task 专属阈值,加 KV df-outbox-task-missed-grace-secs,默认同 12h)。
  • 有界性:任何 task 作业按 due_at + 宽限 有界,不无限滞留。

3.7 权限与边界(与既有 audit/boundary 链的关系)

  • 创建:schedule_task Medium → 走既有 audit/approval(三档/边界会话按现行规则);创建 ≠ 授权执行。
  • 触发执行:走同一 pipeline。fire_task_job 只把一句话送进会话,此后 agentic loop 里每个工具照常过 audit(写/High 审批)、path_auth(路径授权)、黑名单拒绝。不新增任何旁路,不预授权(红线)。
  • 边界快照 boundary_json(可选):若发起会话已声明 TaskBoundary(boundary.rs,risk_ceiling≤Medium,落 ai_conversations.boundary_json V45),创建时可快照;触发时注入触发会话 → 边界内自动放行、边界外照旧拒绝。未声明 → 不写快照 → 触发走普通审批(首个写操作停靠等人工 + 通知)。
  • 黑名单/破坏性命令:永不放行(同 all 档/子代理先例)。
  • MCP 面(独立表面,首期不接):crates/df-mcp/src/tools.rs:101 all_tools() 是独立硬编码清单(非 AI registry 派生);首期不把 schedule_task 加入 MCP(定时依赖本机 GUI 进程 + 调度 loop,远程场景语义不闭合),MCP 侧零改动。

3.8 UI 形态(复用 935 抽屉,937 只加卡片)

  • 对话内定时任务卡(工具卡变体):useToolCard.ts:513 登记 schedule_task formatter;卡面 = 标题 + 指令摘要 + 「⏰ 触发于 MM-DD HH:mm(还有 X 分钟)」+ 状态徽标(定时中/已触发/已取消/已过期/失败)。状态随 ai-outbox-changed/sent/failed 事件更新(到点晚于工具返回,不能靠静态 tool_result)。形态照 TaskParkedCard.vue(per-conv 卡,行为后摘/更新)。
  • 统一「定时列表」抽屉 = 复用 935(定时消息…设计 §3.4):937 的 task 项与 935 的 scheduled/queue 项同列同抽屉,仅多一列「类型=任务/消息」+ task 项显示关联任务(若有)。改期/取消复用 935 的 ai_outbox_reschedule/cancel IPC。
  • 可选 task 专属入口:ChatInput ⏰ 入口(935 已加,ChatInput.vue:107)+ 若要在列表里「手动新增定时任务」,复用同一 IPC(ai_outbox_enqueue 加 kind='task'+payload,或 937 加 ai_schedule_task 薄封装,见拍板点)。
  • 设置页/TopBar 徽标:复用 935 的 df-outbox-* 区块与状态查询,不另开。

3.9 与既有链路的关系

  • worktree / 评审:触发执行 = 一次普通 agentic run,worktree/review/run_workflow 全照旧;指令若含「推进任务/跑工作流」,由触发后的 loop 调 advance_task/run_workflow(工作流仍走 High 审批)。
  • 无人值守引擎:两者独立 loop、独立作业源;挂机在途(in_flight)与 outbox 派发各管各 conv,不冲突。
  • P2 快路径(可选):确定性作业「到点直推任务状态」(payload_json.task_id+target_status)可不起 LLM,直接 advance_task_with_parent(task_advance_node.rs:335)——省 token、免 LLM(状态机推进本身非破坏);须在创建时校验状态机合法性。见拍板点 4。

四、分期与最小可用

期 范围 交付 依赖
P1(最小可用) 工具面 + task 分支闭环 V52 增列 + AiOutboxRecord 三字段 + repo + schedule_task 工具 + fire_task_job 注入发起会话 + 启动追赶快照 + 工具卡徽标 935 P0 底座(ai_outbox 表 + outbox.rs 调度器 + 列表抽屉)
P2(管理/快路径) 列表类型区分 + 直推 列表 task 项类型标 + 关联任务显示;可选确定性直推快路径 + KV 阈值 P1 稳定
P3 边界快照 / 与挂机联动 boundary_json 快照注入触发执行;挂机自领定时任务(可选) P2 + 边界模型落地

最小可用定义: P1 完成即达标——「对话里说 1 小时后跑 X → 卡片定时中 → 到点(含重启补触发)在原会话自动执行」。 关键依赖: 937 的 kind='task' 分支不能脱离 935 的 ai_outbox 底座独立交付;两需求应同批或先后紧邻落地,先 935 P0(表+调度器)后接 937 分支。这是本设计对编排的硬约束。


五、影响面(新能力引入评估纪律 · 五维)

维度 判定
兼容性 复用 ai_outbox(V51)+ 937 加三列(V52,column_exists 幂等,可 DROP 回退);既有工具/任务/工作流零改动。基线测试 test_build_ai_tool_registry_baseline_tool_count 须 +1(schedule_task)。935 的 queue/scheduled 分支不受影响(仅多一个 match 分支)。PASS(带迁移)
破坏性 无破坏性数据操作;不改 advance_task/run_workflow 语义;触发=发消息/起会话,可逆(取消/停止)。PASS(低)
安全面 新攻击面 = 「未来自动执行 LLM/用户给定指令」。缓解:①创建 Medium 须知情审批 ②触发执行零旁路,写/High/路径/黑名单全走既有闸(G5/G6)③边界快照仅在会话已声明边界时生效(不放大权限)④可随时取消。红线:不得为「定时」开任何 approval/path 旁路。PASS(受控)
资源面 复用 935 表与 10s tick loop;937 只多三列 + 一个 match 分支,资源增量≈0。task 项随 outbox 统一设上限(935 已定 sent/canceled 有界策略)。PASS
回退开关 复用 df-outbox-enabled(置 false → task 项只入不发,冻结);V52 可 DROP COLUMN 回退。KV 热读免重启。PASS(带开关)

六、拍板点(需人定,附建议)

  1. 触发执行落点:注入发起会话(结果可见、上下文连续,贴「对话内创建」语义)【建议默认】 vs 独立后台会话(不打扰但结果另起)。建议前者,发起会话忙则轮界排队、不可用则回落后台会话(§3.5 两全)。
  2. 与 935 的落地顺序:937 的 kind='task' 分支依附 935 的 ai_outbox 底座——先 935 P0 后接 937【建议】 vs 937 自建一套表/调度器(违反「共用调度器」且制造双调度,否决) vs 两设计合并为一次实施(978/935/937 同批)。建议前者,把「共用底座」写死为编排约束。
  3. 触发执行授权:默认普通审批模式(未声明边界的发起会话 → 触发后首个写操作停靠等人工并通知)【建议,最安全】 vs 创建即固化「完全信任」(攻击面大) vs 仅当发起会话已声明 TaskBoundary 时携带边界快照(边界内免打扰)。建议:默认普通审批 + 「有边界则快照」并存。
  4. 快路径:起步只做 instruction(通用,触发后 loop 消化一切)【建议】 vs 起步即带 task_id+target_status 确定性直推(省 LLM/免审批,但限死用途、需额外状态机校验)。建议 instruction 起步,直推作 P2(§3.9)。
  5. 迟到追赶阈值:复用 935 拍板点 4 的统一阈值(建议 12h)【建议】 vs 为 task 单设更短阈值(定时任务含副作用,可能希望更保守)。建议统一阈值起步,观测后如需再拆 KV。

七、明确不做

  • 不做 cron 表达式/周期重复任务(首期只做一次性定时;周期任务=挂机模式职责,另议)。
  • 不做跨设备/云端定时(复用 935 边界:关窗托盘化期间生效,进程退出期间不触发,重启补发)。
  • 不做定时执行结果的独立推送通道(复用 notify_session_event + 飞书,与 935 同出口)。
  • 不为定时开任何 approval/path 旁路(红线)。
  • 不新造调度底座(表/循环/IPC/事件/抽屉全复用 935)。

附:实施落点清单(供实施会话直接取用;底座项见 935 设计,此处只列 937 增量)

文件 改动
crates/df-storage/src/migrations.rs 增 migrate_v52(3 个 ALTER TABLE ai_outbox ADD COLUMN,幂等);调度数组追加 (52, migrate_v52);补幂等单测。注意:V51 由 935 占,顺序不得换
crates/df-storage/src/models.rs AiOutboxRecord(935 已加)再补 title/payload_json/boundary_json: Option<String>
crates/df-storage/src/crud/outbox_repo.rs(935 已建) insert/读方法带三新列;list_by_status/list_due 复用
src-tauri/src/commands/ai/tools/schedule.rs(新) schedule_task 工具(declare_tool!,Medium);tool_registry.rs:737 旁接线 + 基线测试 +1
src-tauri/src/commands/ai/outbox.rs(935 已建) 调度派发 match kind 加 "task" => fire_task_job(...);fire_task_job(§3.5)+ 追赶快照
src-tauri/src/commands/ai/mod.rs 事件复用 ai-outbox-changed/sent/failed(935 已定);如卡片需状态细分可加可选字段,不新事件名
src-tauri/src/lib.rs outbox::start 由 935 挂载;937 无需另挂
src/composables/ai/useToolCard.ts :513 映射增 schedule_task formatter(定时任务卡)
src/components/ai/ 定时任务卡(仿 TaskParkedCard.vue);列表抽屉 task 项类型标(复用 935 抽屉)
src/api/ai.ts 复用 935 ai_outbox_* 封装;若新增「手动建定时任务」入口再加薄封装
src/i18n/zh-CN/* + en/* 卡片状态词(定时中/已触发/已过期…)/类型「任务」

参照档: 定时消息与待发箱持久化-设计-2026-10-10.md(935/931,共用底座,必读)· 无人值守长任务-设计-2026-10-04.md(结构/详略)· 并行化与异步唤醒-设计-2026-10-04.md(§二 wake delay 型/§6.3 挂机)· 输入框列表预览与自动编号-设计-2026-10-10.md(同目录同日写法/五维自评)。