Module Structure
该节点包含三类不同的“持续工作”:每 Session 一个长期 Goal、供 Agent/团队协调的轻量任务列表,以及由 Web/CLI/调度器创建并独立运行的顶层任务 Session。
Directory Layout
packages/cli/src/goals/— Goal sidecar、状态机、提前停止检测和 continuation promptpackages/cli/src/agent/runtime/TaskScheduler.ts—blade serve拥有的定时扫描与 dispatchpackages/cli/src/agent/runtime/scheduleTiming.ts— cron、interval、once 解析与下次执行计算packages/cli/src/services/ScheduleStore.ts— 全局 schedule JSON 存储packages/cli/src/services/SessionTaskService.ts— 顶层任务 Session 与 worktree 创建packages/cli/src/tools/builtin/goal/— Goal 查询、创建和完成候选工具packages/cli/src/tools/builtin/task/— Agent 任务列表与子代理 Task 入口packages/cli/src/commands/schedule.ts— CLI schedule 管理与远程手动触发packages/cli/src/server/routes/task.ts— 顶层任务创建、更新、重试、diff 与交付 APIpackages/cli/src/server/routes/schedule.ts— schedule CRUD 与手动运行 APIpackages/cli/web/src/components/tasks/,packages/cli/web/src/components/kanban/,packages/cli/web/src/components/schedules/— Web 任务与调度表面
Key Entry Points
GoalStore.create()/GoalStore.requestCompletion()inpackages/cli/src/goals/GoalStore.ts— Goal 启动与完成候选GoalStore.finalizeVerifiedCompletion()inpackages/cli/src/goals/GoalStore.ts— 宿主授权的最终完成TaskListManager.mutate()inpackages/cli/src/tools/builtin/task/TaskListManager.ts— 任务列表串行读改写SessionTaskService.createSessionTask()inpackages/cli/src/services/SessionTaskService.ts— 顶层任务和隔离 workspace 创建TaskScheduler.tick()/TaskScheduler.fire()inpackages/cli/src/agent/runtime/TaskScheduler.ts— 定时触发与账本推进computeNextRun()inpackages/cli/src/agent/runtime/scheduleTiming.ts— 三类 trigger 的统一时间计算
Gotchas
UpdateGoal({status:"complete"})只把状态改为verifying并增加 attempt;必须存在 fresh verifier Session ID 和 SHA-256 evidence digest 的 PASS,宿主才允许complete(packages/cli/src/tools/builtin/goal/goalTools.ts,packages/cli/src/goals/GoalStore.ts,git:0eff1b2a)- reserved verifier 的启动本身证明宿主已持久接受 completion candidate;审计期间的
verifying/pending是等待该 verifier verdict 的必要中间态,不能被解释成执行 Agent 未调用UpdateGoal complete,但也不能替代对实际交付物的验证 (packages/cli/src/agent/loop/goalCompletionVerification.ts,packages/cli/src/agent/subagents/builtinGoalVerificationAgent.ts) - Goal 已有 PASS 后发生 workspace mutation、用户 steering 或 Stop hook continuation 会使证据失效;进程重启也不信任没有 exact finalization receipt 的旧 PASS (
packages/cli/src/agent/loop/executeLoopGenerator.ts,docs/reference/goal-completion-verification.md) - Goal 最终 assistant commit 与 sidecar 完成不是一个文件事务;恢复只在 goal ID、attempt、verifier ID、evidence digest 和 goal revision 全匹配 receipt 时幂等补写 complete (
packages/cli/src/goals/GoalStore.ts,docs/reference/goal-completion-verification.md,git:ebd505c9) - 同一脱敏 verifier 反馈连续 3 次会把 Goal 置为 blocked;不同反馈重置计数,PASS、编辑或显式 resume 清除 stall 状态 (
packages/cli/src/goals/GoalStore.ts,packages/cli/src/goals/types.ts,git:490df8c5) - premature-stop 只检查最终非空段落中一组保守的英文起始模式;同一模式连续 3 次才自动 blocked,模式改变会从 1 重新计数 (
packages/cli/src/goals/prematureStop.ts,packages/cli/src/goals/GoalStore.ts) - Goal token 使用按每次 continuation 的 run metadata 累加,达到显式预算立即进入
budget_limited;结束一个模型回合不会隐式暂停或完成 Goal (packages/cli/src/agent/Agent.ts,packages/cli/src/goals/GoalStore.ts) - 通用 TaskList 的状态流转主要由工具提示约束,存储层允许直接设置任一合法状态;调用方若绕过
pending → in_progress → completed,不会由TaskListManager自动拒绝 (packages/cli/src/tools/builtin/task/taskListTools.ts,packages/cli/src/tools/builtin/task/TaskListManager.ts) - 通用 TaskList 可添加不存在的
blockedByID,结果会永久不可认领;Team 创建路径额外要求依赖只能引用更早任务,不能假设这个校验存在于底层 manager (packages/cli/src/tools/builtin/task/TaskListManager.ts,packages/cli/src/agent/teams/TeamRuntime.ts) - Schedule 只有
blade serve内的TaskScheduler会自动触发;CLI 只读写共享文件,blade schedule run也必须调用正在运行的 server (packages/cli/src/commands/schedule.ts,packages/cli/src/agent/runtime/TaskScheduler.ts) - 同一 schedule 的 overlap 防护和 tick 重入锁都只在当前 server 进程内;
ScheduleStore的写链也只保证进程内串行,多个进程同时管理 schedule 仍是 last-writer-wins (packages/cli/src/agent/runtime/TaskScheduler.ts,packages/cli/src/services/ScheduleStore.ts) - dispatch 抛错仍会推进 recurring 的下一次时间,once 则消费唯一机会并停用;错误路径不增加
runCount,因此该字段统计成功 dispatch 而不是所有尝试 (packages/cli/src/agent/runtime/TaskScheduler.ts) - 手动运行 recurring schedule 保留原
nextRunAt,不会把 cadence 从手动触发时刻重新锚定;手动运行 once 仍会将其停用 (packages/cli/src/agent/runtime/TaskScheduler.ts) ScheduleStore对缺失或损坏文件都返回空列表并记录 warning;后续写操作可能以空集合覆盖损坏内容,诊断数据丢失前应先检查日志与原文件 (packages/cli/src/services/ScheduleStore.ts)parseIntervalMs()接受秒级输入,但持久 schedule 的validateTrigger()要求至少 60 秒;只调用解析器通过不代表 trigger 可创建 (packages/cli/src/agent/runtime/scheduleTiming.ts)
Architecture
- Goal sidecar 由 Session/workspace 共同分区并使用 keyed mutex、原子 fsync 写;监听器在持久化后通知,观察者异常被吞掉且不能回滚已提交状态 (
packages/cli/src/goals/GoalStore.ts) - Goal continuation 由
Agent.chatStream()在普通 follow-up 耗尽后自动开启,重新加载 durable snapshot、累计用量并注入 objective/验证反馈;没有固定 continuation 上限 (packages/cli/src/agent/Agent.ts,packages/cli/src/goals/prompts.ts) - TaskList 同时使用进程内 keyed mutex 和
proper-lockfile跨进程锁,再原子替换 JSON;Team task graph 复用相同存储,所以普通任务工具与 teammate claim 可以安全共享列表 (packages/cli/src/tools/builtin/task/TaskListManager.ts,packages/cli/src/tools/builtin/task/TaskListFileLock.ts) - 顶层任务由
SessionTaskService先可选创建 worktree,再写 Session metadata;metadata 创建失败会移除刚建的 worktree并丢弃其中状态,避免留下无主隔离目录 (packages/cli/src/services/SessionTaskService.ts) - Scheduler 只负责发现到期项和调用注入的
dispatchTask;任务终态通过全局task.statusBus 事件异步回写 schedule,因此schedule.fired的初始 status 可能仍是 queued/running (packages/cli/src/agent/runtime/TaskScheduler.ts) - 定时触发统一创建顶层任务 Session,默认 worktree + default permission mode;schedule 自身不持有 Agent Runtime、队列 permit 或执行日志 (
packages/cli/src/agent/runtime/TaskScheduler.ts,packages/cli/src/services/ScheduleStore.ts)
Decisions
- Goal 完成权归宿主持有而不是交给执行模型:内置
goal-verification以只读、fresh、结构化 verdict 检查持久 objective,避免模型仅凭自述结束长期目标 (packages/cli/src/agent/loop/goalCompletionVerification.ts,packages/cli/src/agent/subagents/builtinGoalVerificationAgent.ts) - TaskList 的依赖边在写入时双向同步,删除任务也会清除其他任务的
blocks/blockedBy;认领阶段只信完成集合,不维护额外派生索引 (packages/cli/src/tools/builtin/task/TaskListManager.ts) - schedule 使用单个全局 JSON 文件服务所有项目,每条记录显式绑定
projectPath;这是低频控制面的简单持久化选择,不具备跨进程事务语义 (packages/cli/src/services/ScheduleStore.ts) - cron 采用标准 5 段且 DOM/DOW 同时受限时按 OR 匹配,星期日 0/7 归一;下一次运行按分钟扫描最多 4 年,无法命中时返回 null (
packages/cli/src/agent/runtime/scheduleTiming.ts)
Patterns
- Goal 所有状态更新都在同一文件锁内执行“读取最新状态 → schema 校验 → 原子持久化 → 发布副本”,调用方不应缓存 snapshot 后自行合并 (
packages/cli/src/goals/GoalStore.ts) - TaskList 每次 mutation 都重读磁盘并保持单调
nextId;旧数组格式可迁移读取,重复 ID 或任一非法 task 会让整个列表 fail closed (packages/cli/src/tools/builtin/task/TaskListManager.ts) - recurring schedule 离线错过多个 slot 时只补跑一次,并从当前触发时刻计算下一次;同一个 tick 顺序 await 各 schedule,避免调度器自身形成无界并发 (
packages/cli/src/agent/runtime/scheduleTiming.ts,packages/cli/src/agent/runtime/TaskScheduler.ts)
Scheduling Boundaries
- cron 创建时若未给 timezone 会冻结当前系统 IANA timezone;匹配时通过
Intl.DateTimeFormat投影目标时区,不能把 schedule 当成固定 UTC 偏移 (packages/cli/src/services/ScheduleStore.ts,packages/cli/src/agent/runtime/scheduleTiming.ts) - 普通
ScheduleStore.create()将expiresAt设为 null;7 天 expiry helper 只供另行设置的有界 recurring 调用使用,server-owned schedule 不会仅因创建满 7 天自动过期 (packages/cli/src/services/ScheduleStore.ts,packages/cli/src/agent/runtime/scheduleTiming.ts) - 启用一个曾停用的 schedule 会从当前时间重算
nextRunAt,但单纯修改 prompt、模型或权限不会改变 cadence (packages/cli/src/services/ScheduleStore.ts)