LocalAgentTask vs RemoteAgentTask

这页回答什么

Claude Code 里的 LocalAgentTaskRemoteAgentTask 都属于 Task 系统,但两者到底共享什么、分叉什么、分别解决什么问题?

相关页面:


一句话结论

两者共享的是“长期任务协议”,分叉的是“执行载体”。LocalAgentTask 管本地子 agent 的后台生命周期,RemoteAgentTask 管远端 session 的本地代理与轮询恢复。

换句话说:

  • Local = 本地执行流外面套一个 task 壳
  • Remote = 远端事件流在本地投影成 task 对象

1. 它们为什么会被放进同一个 Task 系统

因为两者都需要这些能力:

  • 任务注册
  • 状态展示
  • output file
  • kill / stop
  • completion notification
  • 与主对话的回流协议

如果没有 Task 层:

  • local async agent 只能是一个裸后台 Promise
  • remote session 只能是一个散装 poller

而 Claude Code 想要的是:

不管任务跑在本地还是远端,只要它是“持续中的工作单元”,就该是一个一等 task。


2. LocalAgentTask 的核心职责

LocalAgentTask 负责的是:

  • 后台本地子 agent 的注册
  • 运行态跟踪
  • 进度更新
  • transcript / output file 管理
  • pendingMessages 缓冲
  • background / foreground 切换
  • 完成/失败/终止状态管理

它围绕的是:

本地 runAgent() 已经在跑,Task 负责把它托管起来。

所以 LocalAgentTask 更像:

  • lifecycle wrapper
  • status shell
  • progress envelope

而不是执行器本身。


3. RemoteAgentTask 的核心职责

RemoteAgentTask 负责的是:

  • 记录远端 session 身份
  • 持久化 sidecar metadata
  • 周期轮询远端 event stream
  • 把远端日志映射到本地 output file
  • 解释 completion / failure / timeout
  • resume 后重新接管 still-running session

它围绕的是:

真正的 agent 执行已经发生在远端,本地只保留一个代理对象。

所以 RemoteAgentTask 更像:

  • remote session proxy
  • poller
  • state reducer
  • resume controller

4. 共享点:两者都遵守同一套任务协议

A. 都在 AppState 中注册为 task

这样 UI、状态面板、生命周期工具都能统一处理。

B. 都有 output file

任务输出可落盘、可引用、可延迟读取。

C. 都有 status

  • running
  • completed
  • failed
  • killed

D. 都能 stop / kill

虽然底层动作不同,但表层任务语义统一。

E. 都最终走 task-notification

这是最关键的公共协议。

无论 local 还是 remote,最终都会:

enqueuePendingNotification(mode='task-notification')
  → query.ts drain queue
  → attachments
  → 后续轮次上下文

也就是说:

Task 系统真正统一的是回流协议,而不是底层执行方式。


5. 分叉点:完成信号从哪里来

LocalAgentTask

完成信号来自:

  • 本地 runAgent() 执行流
  • finalizeAgentTool()
  • completeAsyncAgent() / failAsyncAgent() / killAsyncAgent()

它的世界观是:

“我知道 agent 在本地跑到了哪一步。”

RemoteAgentTask

完成信号来自:

  • pollRemoteSessionEvents() 拉回的远端事件
  • result / archived / idle / hook tag / timeout 的解释

它的世界观是:

“我不知道远端如何一步步运行,但我能从事件流推断它现在的状态。”

所以 Local 是执行流驱动,Remote 是事件归约驱动。


6. 分叉点:是否天然带恢复语义

LocalAgentTask

当然也可以保留状态,但本质上依赖本地进程生命周期。

RemoteAgentTask

从设计上就要求:

  • sidecar 持久化
  • restoreRemoteAgentTasks()
  • 重新连接 still-running session

因为远端 session 不随本地 CLI 一起消失。

所以 remote task 的“可恢复”不是锦上添花,而是默认约束。


7. 分叉点:进度的来源不同

LocalAgentTask

进度来自本地 agent transcript:

  • toolUseCount
  • tokenCount
  • recent activity
  • summary

RemoteAgentTask

进度来自远端 event stream:

  • assistant text
  • result event
  • hook_progress / hook_response
  • review-specific JSON tags

这使得 RemoteTask 的进度逻辑往往更脆弱、更产品化,也更依赖特定远端协议。


8. 分叉点:Task 的“壳”厚度不同

LocalAgentTask

壳比较薄。 因为真正复杂的逻辑大多还在:

  • AgentTool
  • runAgent()
  • 本地 query loop

Task 主要负责托管。

RemoteAgentTask

壳更厚。 因为它自己就承担:

  • polling
  • event accumulation
  • completion inference
  • timeout handling
  • resume
  • specialized notification branches

所以 RemoteAgentTask 更像一个小控制器,而不只是壳。


9. 两者如何在用户层表现成同一种东西

虽然内部差异很大,但用户层会看到相似体验:

  • 一个任务启动了
  • 它有 task id / output file / 状态
  • 它完成时会收到通知
  • 主 agent 能基于这个结果继续工作

这正是 Task 系统成功的地方:

把两种完全不同的执行模型,压平为一种统一的会话语义。


10. 最简对照表

LocalAgentTask

  • 执行位置:本地进程
  • 核心驱动:runAgent() 执行流
  • 任务角色:后台托管壳
  • 进度来源:本地 transcript
  • 完成判定:本地函数完成/异常/abort
  • 恢复需求:较弱
  • 典型风险:本地 cleanup / worktree / kill 协调

RemoteAgentTask

  • 执行位置:远端 session
  • 核心驱动:event polling + state reduction
  • 任务角色:远端代理对象
  • 进度来源:远端 SDK events / hook events
  • 完成判定:result / archived / idle / timeout / checker
  • 恢复需求:很强
  • 典型风险:轮询抖动、错误归约、session 恢复

当前结论

LocalAgentTaskRemoteAgentTask 的关系可以概括成:

它们不是同一个东西的两个实现细节,而是两种不同执行世界在 Task 协议层的统一投影。

Claude Code 真正聪明的地方,不是让 remote 看起来像 local, 而是让:

  • local execution
  • remote session orchestration

都能最终说同一种“任务语言”。

这门语言就是:

  • task state
  • output file
  • notification
  • attachment 回流

下一步最值得继续追

  1. 单独深挖 teleportToRemote() 的 source/bundle/environment 决策树
  2. queued_command attachment 进入模型输入的最终序列化过程
  3. 对比 sync / async / remote 三种 AgentTool 路径在 transcript 上的差异