03.3 任务系统

关注源码

  • src/Task.ts
  • src/tasks/*
  • src/utils/task/framework.ts
  • src/utils/task/diskOutput.ts

为什么要有任务层

一旦工具调用不再是瞬时完成,就需要统一表示:

  • 谁在后台运行
  • 输出落在哪里
  • 何时通知主会话
  • 何时可以中止或回收

任务层就是为此存在的统一 substrate。

Task.ts 定义了最小公共协议

公共字段包括:

  • id
  • type
  • status
  • description
  • outputFile
  • outputOffset
  • notified
  • startTime / endTime

同时定义了统一的 TaskType,当前覆盖:

  • local_bash
  • local_agent
  • remote_agent
  • in_process_teammate
  • local_workflow
  • monitor_mcp
  • dream

主要任务实现

LocalShellTask

负责本地 shell 命令的长时执行、输出持久化和 kill。

LocalAgentTask

负责后台 agent:

  • 记录 transcript
  • 聚合 tool use/token 进度
  • 维护 foreground/background 状态
  • 支持 SendMessage 追加消息

RemoteAgentTask

负责远端 agent 会话和本地可见状态的衔接。

InProcessTeammateTask

负责 swarms/teammates 这种协作执行单元。

framework.ts 提供的统一能力

utils/task/framework.ts 负责:

  • registerTask
  • updateTaskState
  • pollTasks
  • 输出 offset 维护
  • terminal task eviction
  • task notification 入队

最关键的是它把“任务输出来自磁盘、状态来自内存”这两个通道统一起来了。

为什么输出要落盘

任务不是普通消息,因为它们可能:

  • 持续很久
  • 输出很多
  • 暂时不在前台视图里
  • 需要被 resume / 远端查看

因此每个任务都有 outputFile,而不是只把输出塞进 AppState 数组里。

任务与主消息流怎么连接

任务不会直接强耦合到对话框,而是通过:

  • XML 风格 task notification attachment
  • messageQueueManager
  • TaskOutputTool

重新回到主消息流。这让模型和 UI 都可以“看见后台结果”。

设计上的关键点

1. 任务是第一类对象

系统并不假设一切都在前台串行完成。后台 agent、shell、remote 都有正式建模。

2. 任务输出和任务状态分离

状态在内存里快速更新,输出在磁盘上持续累积,这能兼顾性能和持久化。

3. framework 处理通用生命周期,具体任务只负责业务差异

这样新增任务类型时,不需要重写一套通知、轮询、offset、回收逻辑。