Task 系统总览

目标

这页回答:

Claude Code 里,子 agent / 远程 agent 为什么不只是“跑完就完”,而要挂进 Task 系统?Task 层到底负责什么?

关键文件:

  • src/tasks/LocalAgentTask/LocalAgentTask.tsx
  • src/tasks/RemoteAgentTask/RemoteAgentTask.tsx

一句话结论

runAgent() 负责“执行”,而 Task 系统负责“生命周期管理”。

也就是:

  • agent 怎么跑 → AgentTool / runAgent
  • agent 作为后台任务如何注册、跟踪、通知、恢复、停止 → Task

Task 不是推理层,是 orchestration / lifecycle 层。


1. 为什么需要 Task 层?

如果 agent 只有同步运行,那工具执行完直接回一个 tool_result 就够了。

但这里系统支持:

  • 后台 agent
  • foreground agent 随时 background 化
  • remote agent
  • task notification
  • output file
  • 恢复 / resume
  • kill
  • 进度追踪

一旦有这些需求,单纯靠 runAgent() 就不够了。

所以 Task 层本质上是在回答:

这个 agent 作为一个“持续中的任务”如何被系统管理?


2. LocalAgentTask:本地子 agent 的生命周期层

它管理什么?

LocalAgentTaskState 里能看到它关心的字段很多:

  • agentId
  • prompt
  • selectedAgent
  • status
  • abortController
  • progress
  • messages
  • pendingMessages
  • isBackgrounded
  • retain
  • diskLoaded
  • evictAfter

这说明本地 agent task 不是一个简陋状态机,而是同时承担:

  • 运行态跟踪
  • 面板/UI 展示
  • transcript 保持
  • 用户继续发消息
  • 结束后延迟驱逐

3. LocalAgentTask 的三种核心注册方式

A. registerAsyncAgent()

直接注册一个后台 agent。

特点:

  • 一上来就是 isBackgrounded: true
  • 会初始化 output file symlink 到 transcript
  • 会创建 abortController
  • 会注册 cleanup
  • 会把 task state 放进 AppState

这对应“从一开始就后台跑”的 agent。


B. registerAgentForeground()

注册一个前台 agent,但允许稍后 background 化。

特点:

  • 初始 isBackgrounded: false
  • 会返回一个 backgroundSignal promise
  • 可以设置 autoBackgroundMs

这正好对应 AgentTool 同步子 agent 那条线:

  • 先前台跑
  • 运行太久时,用户或系统可切后台

C. backgroundAgentTask()

把一个前台 agent 标记成后台,并 resolve 它的 backgroundSignal。

这让上层 agent loop 能感知:

  • 现在该从前台等待模式切到后台通知模式了

4. LocalAgentTask 的进度系统

LocalAgentTask 不是只记录“running / done”。 它还会维护一个比较细的进度模型:

  • toolUseCount
  • tokenCount
  • lastActivity
  • recentActivities
  • summary

这套进度怎么来的?

通过:

  • createProgressTracker()
  • updateProgressFromMessage()
  • getProgressUpdate()

它会从 assistant message 中提取:

  • usage token
  • 最近调用过的工具
  • 工具输入
  • activity description

所以 task progress 不是瞎猜,而是从真实 agent transcript 里增量提取出来的。


5. LocalAgentTask 为什么要有通知机制?

函数:

  • enqueueAgentNotification()

它会构造一段标准化 task-notification XML:

  • task_id
  • output_file
  • status
  • summary
  • 可选 result
  • 可选 usage
  • 可选 worktree

然后通过:

enqueuePendingNotification({ mode: 'task-notification' })

放进消息队列。

这意味着什么?

后台 agent 完成后,不是直接神秘地“出现在用户眼前”。 而是通过统一消息机制,把任务完成信号重新送回主对话循环。

这点很关键: 后台任务的完成事件最终也被当成对话输入来消费。


6. LocalAgentTask 的结束路径

完成

  • completeAgentTask(result, setAppState)

做的事:

  • status → completed
  • 保存 result
  • 清理 abort / cleanup 句柄
  • 设置 endTime
  • 安排 evict

但注意:

  • 真正通知不是它发
  • 通知由 AgentTool 侧调用 enqueueAgentNotification() 完成

失败

  • failAgentTask(...)

类似,只是 status → failed。

终止

  • killAsyncAgent(...)

会:

  • abort controller
  • unregister cleanup
  • status → killed
  • 释放 output

7. 为什么 LocalAgentTask 里还有 pendingMessages

这是个很有意思的点。

它支持:

  • 用户在 agent 运行中通过 SendMessage 往 task 里塞消息
  • 消息先暂存到 pendingMessages
  • 再在合适时机由 agent loop drain

这说明 task 不只是“运行状态容器”, 它还承担了:

给正在运行中的 agent 做中途通信缓冲。


8. RemoteAgentTask:远程 agent 的生命周期层

远程任务这边的状态完全不同。

RemoteAgentTaskState 关注的是:

  • sessionId
  • remoteTaskType
  • command
  • title
  • log
  • todoList
  • pollStartedAt
  • isRemoteReview
  • reviewProgress
  • isUltraplan

这说明 remote task 的核心不是“本地执行过程”,而是:

一个远端 session 的本地镜像与轮询控制。


9. registerRemoteAgentTask() 做什么?

它会:

  • 生成 taskId
  • 初始化 output file
  • 创建 RemoteAgentTaskState
  • 注册进 AppState
  • 把 metadata 持久化到 session sidecar
  • 启动 startRemoteSessionPolling()

为什么要写 sidecar?

因为 remote task 能跨会话恢复。

也就是说,本地程序重开后,系统还能:

  • 扫到之前没结束的 remote task
  • 重新连上对应 remote session
  • 继续轮询状态

这就是 remote task 和本地 task 的一个大差别: 它天然带有恢复需求。


10. RemoteAgentTask 的核心是轮询器

函数:

  • startRemoteSessionPolling()

这东西非常关键。

它会周期性:

  • pollRemoteSessionEvents(...)
  • 追加新日志
  • 更新 task state
  • 判断 session 是否 archived / completed / failed / timed out
  • 特判 remote review / ultraplan / long-running task
  • 发出 notification

这说明 remote task 的完成不是本地函数返回驱动的

而是:

  • 由远端 session 的事件流驱动
  • 本地通过 poller 做状态归约

这已经是另一种 orchestration 模式了。


11. RemoteAgentTask 的通知和 LocalAgentTask 类似,但语义不同

enqueueRemoteNotification() 也会发 task-notification XML, 但它更偏:

  • 远程任务已完成 / 失败 / 停止
  • output_file 在哪
  • summary 是什么

另外还有专门分支:

  • enqueueUltraplanFailureNotification()
  • remote review completion / failure notification

说明 remote task 不只是“通用 agent”,还承载特定产品形态。


12. Remote task 为什么复杂?

因为它要处理很多本地 agent 不需要的事:

  • 远端 API 出错
  • 远端 session archived
  • 轮询 idle 抖动
  • review timeout
  • 从 hook_progress / assistant text 中提取结果 tag
  • sidecar 恢复
  • archive remote session

所以 RemoteAgentTask 本质上已经像一个小型“远程作业控制器”。


13. 这两类 Task 的本质区别

LocalAgentTask

像:

  • 本地异步子 agent 的任务记录 + UI / 通知 / kill 外壳

RemoteAgentTask

像:

  • 远程 session 的本地代理对象 + poller + resume 机制

两者共同点:

  • 都挂进统一 Task 框架
  • 都有 status / notification / output_file
  • 都能 kill / complete / fail

两者差异:

  • Local 以本地执行流为核心
  • Remote 以远程 session 轮询与恢复为核心

14. Task 系统和 Agent 系统如何分工

这点一定要分清。

Agent 系统负责

  • prompt
  • tool pool
  • subagent context
  • query loop
  • agent result

Task 系统负责

  • 注册
  • 状态
  • 后台化
  • 进度
  • 通知
  • 输出文件
  • resume
  • kill
  • 清理

所以 Task 不是 agent 的替代,而是 agent 的壳。


当前结论

Claude Code 的 Task 系统可以理解成:

把“一个正在运行或等待完成的 agent 工作单元”提升为一等对象。

这样系统才能稳定支持:

  • background agents
  • fork workers
  • remote sessions
  • resume / restore
  • UI 面板与通知

如果没有 Task 层,agent 只能停留在“同步工具调用”这个初级阶段。

而现在这套设计,已经明显是面向长期运行、多任务并存、可恢复执行的。