Claude Code 源码分析Claude Code 源码分析
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
  • 概览

    • Claude Code 源码分析
    • 源码统计
    • CodeGraph 图谱
  • 架构

    • 系统架构
    • UML 图表索引
    • 查询引擎
    • 核心流程
    • 消息系统
    • 状态管理
  • 功能模块

    • 工具系统
    • 斜杠命令
    • 服务层
    • MCP 协议
    • Skills 技能
    • 子代理系统
  • 分层深度

    • 入口层
    • UI / Ink 层
    • utils 基础设施
    • 桥接 / 远程
    • 上下文压缩
  • 原理与安全

    • 底层原理
    • 技术难点
    • 权限与安全
    • 内部机制
    • 遥测与分析
  • 深度专题

    • Hooks 系统
    • 插件系统
    • 记忆系统
    • API 通信层
    • Ink 终端 UI
    • 认证系统
    • 构建与发布
    • 术语表
  • 调用分析

    • 调用链分析
    • 核心文件索引
  • 模块详解

    • utils

      • 模块: utils
      • messages · 消息工厂与规范化
      • session-storage · JSONL 会话持久化
      • permissions · 工具权限决策
      • shell-hooks · 用户 Shell Hook 系统
    • components

      • 模块: components
      • REPL · 主屏编排
      • messages · 消息行渲染
      • PermissionRequest · 权限弹窗
      • PromptInput · 底部输入
    • services

      • 模块: services
      • api-claude · Anthropic API 流式与重试
      • mcp-client · MCP 连接与工具调用
      • compact · 上下文压缩与自动触发
      • analytics · GrowthBook、Datadog 与 1P 事件
    • tools

      • 模块: tools
      • tool-interface · Tool 契约与注册表
      • bash-tool · Shell 执行与权限
      • streaming-executor · 流式工具并发调度
      • agent-tool · 子 Agent 委派
    • commands

      • 模块: commands
      • command-registry · commands.ts 注册与分派
      • model-command · /model 模型选择
      • mcp-commands · /mcp 服务器管理
      • compact-memory-commands · /compact 与 /memory
    • ink

      • 模块: ink
      • Ink 渲染管线 · Screen 与终端输出
      • 终端事件 · resize、paste、stdin
      • Ink Hooks · 输入、搜索、终端状态
      • Ink 组件 · Box、Text、ScrollBox 原语
    • hooks

      • 模块: hooks
      • useCanUseTool · 权限 UI 接缝
      • 输入与快捷键 Hook
      • 合并态 Hook(MCP + 本地)
      • notifs 通知 Hook
    • bridge

      • 模块: bridge
      • repl-bridge · REPL 桥初始化与传输
      • bridge-messaging · 桥消息路由与入站处理
      • remote-bridge-core · env-less 核心与守护主循环
      • bridge-permissions-ui · 权限、API 与 TUI
    • cli

      • 模块: cli
      • Structured IO · NDJSON SDK 协议
      • CLI Transports · Session Ingress 传输层
      • CLI Handlers · 子命令懒加载实现
      • Update & Upload · 自更新与串行上传原语
    • screens

      • 模块: screens
      • REPL 屏 · Screen 类型与顶层路由
      • ResumeConversation · 会话恢复选择器
      • Doctor · 安装诊断全屏
    • entrypoints

      • 模块: entrypoints
      • cli-entrypoint · Bootstrap 与快路径
      • sdk-types · core / control / runtime 类型体系
      • mcp-entrypoint · MCP stdio 服务器
      • sandbox-types · 沙箱配置单一真相源
    • skills

      • 模块: skills
      • skills-loading · 磁盘加载与 bundled 注册表
      • bundled-skills · 内置 skill 与 initBundledSkills
      • mcp-skills · MCP prompt 转 skill
      • skill-tool-integration · SkillTool 与命令注册
    • types

      • 模块: types
      • message-types · Message 联合与 content blocks
      • tool-permission-types · Tool、Permission、Command 类型
      • api-sdk-types · API 与 Hooks 协议类型
      • misc-types · ids、plugin、generated 与其余类型
    • tasks

      • 模块: tasks
      • local-agent-task · 本地 Agent 与主会话后台化
      • remote-agent-task · 远程 CCR 与 In-Process Teammate
      • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
      • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybindings

      • 模块: keybindings
      • keybinding-registry · 注册、Provider 与 useKeybinding
      • default-bindings · 默认键位表与平台差异
      • command-bindings · command:* 动态斜杠命令绑定
      • vim-bindings · Vim 模式与 keybindings 边界
    • memdir

      • 模块: memdir
      • memdir-core · 路径、加载与 MEMORY.md
      • memory-extraction · extractMemories 与 SessionMemory
      • memdir-commands · /memory、/remember 与命令集成
    • state

      • 模块: state
      • app-state-core · store、AppState 类型与 Provider
      • app-state-selectors · selectors 与 onChangeAppState
      • teammate-state · 队友视图与 swarm 状态
      • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query

      • 模块: query
      • query config 与 deps · 配置快照与依赖注入
      • query tokenBudget · +500k 自动续跑
      • query transitions · Continue / Terminal 状态机
      • query stopHooks · Stop 事件与 turn 结束编排
  • 模块详解(扩展)

    • messages · 消息工厂与规范化
    • session-storage · JSONL 会话持久化
    • permissions · 工具权限决策
    • shell-hooks · 用户 Shell Hook 系统
    • REPL · 主屏编排
    • messages · 消息行渲染
    • PermissionRequest · 权限弹窗
    • PromptInput · 底部输入
    • api-claude · Anthropic API 流式与重试
    • mcp-client · MCP 连接与工具调用
    • compact · 上下文压缩与自动触发
    • analytics · GrowthBook、Datadog 与 1P 事件
    • tool-interface · Tool 契约与注册表
    • bash-tool · Shell 执行与权限
    • streaming-executor · 流式工具并发调度
    • agent-tool · 子 Agent 委派
    • command-registry · commands.ts 注册与分派
    • model-command · /model 模型选择
    • mcp-commands · /mcp 服务器管理
    • compact-memory-commands · /compact 与 /memory
    • Ink 渲染管线 · Screen 与终端输出
    • 终端事件 · resize、paste、stdin
    • Ink Hooks · 输入、搜索、终端状态
    • Ink 组件 · Box、Text、ScrollBox 原语
    • useCanUseTool · 权限 UI 接缝
    • 输入与快捷键 Hook
    • 合并态 Hook(MCP + 本地)
    • notifs 通知 Hook
    • repl-bridge · REPL 桥初始化与传输
    • bridge-messaging · 桥消息路由与入站处理
    • remote-bridge-core · env-less 核心与守护主循环
    • bridge-permissions-ui · 权限、API 与 TUI
    • Structured IO · NDJSON SDK 协议
    • CLI Transports · Session Ingress 传输层
    • CLI Handlers · 子命令懒加载实现
    • Update & Upload · 自更新与串行上传原语
    • REPL 屏 · Screen 类型与顶层路由
    • ResumeConversation · 会话恢复选择器
    • Doctor · 安装诊断全屏
    • cli-entrypoint · Bootstrap 与快路径
    • sdk-types · core / control / runtime 类型体系
    • mcp-entrypoint · MCP stdio 服务器
    • sandbox-types · 沙箱配置单一真相源
    • skills-loading · 磁盘加载与 bundled 注册表
    • bundled-skills · 内置 skill 与 initBundledSkills
    • mcp-skills · MCP prompt 转 skill
    • skill-tool-integration · SkillTool 与命令注册
    • message-types · Message 联合与 content blocks
    • tool-permission-types · Tool、Permission、Command 类型
    • api-sdk-types · API 与 Hooks 协议类型
    • misc-types · ids、plugin、generated 与其余类型
    • local-agent-task · 本地 Agent 与主会话后台化
    • remote-agent-task · 远程 CCR 与 In-Process Teammate
    • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
    • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybinding-registry · 注册、Provider 与 useKeybinding
    • default-bindings · 默认键位表与平台差异
    • command-bindings · command:* 动态斜杠命令绑定
    • vim-bindings · Vim 模式与 keybindings 边界
    • memdir-core · 路径、加载与 MEMORY.md
    • memory-extraction · extractMemories 与 SessionMemory
    • memdir-commands · /memory、/remember 与命令集成
    • app-state-core · store、AppState 类型与 Provider
    • app-state-selectors · selectors 与 onChangeAppState
    • teammate-state · 队友视图与 swarm 状态
    • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query config 与 deps · 配置快照与依赖注入
    • query tokenBudget · +500k 自动续跑
    • query transitions · Continue / Terminal 状态机
    • query stopHooks · Stop 事件与 turn 结束编排
  • 工具详解

    • tool-interface · Tool 契约与注册表
    • tool-permission-types · Tool、Permission、Command 类型
    • 工具: Bash
    • 工具: PowerShell
    • 工具: Agent
    • 工具: LSP
    • 工具: FileEdit
    • 工具: FileRead
    • 工具: Skill
    • 工具: WebFetch
    • 工具: MCP
    • 工具: SendMessage
    • 工具: FileWrite
    • 工具: Config
    • 工具: Grep
    • 工具: Brief
    • 工具: ExitPlanMode
    • 工具: ToolSearch
    • 工具: NotebookEdit
    • 工具: TaskOutput
    • 工具: WebSearch
    • 工具: ScheduleCron

本章总览

tasks/DreamTask/(约 158 行)为 autoDream 记忆整理 fork agent 提供 UI 可见性:registerDreamTask 写入 type=dream 的 running 状态,addDreamTurn 增量更新 turns/filesTouched/phase,completeDreamTask 立即 notified=true(无 model-facing XML)。MonitorMcpTask/ 与 LocalWorkflowTask 类似,当前为 stub(isMonitorMcpTask 恒 false),types 与 pillLabel 已预留 monitor_mcp 分支。pillLabel.ts(87 行)集中定义 footer pill 与 turn-duration 行的文案契约:getPillLabel 按 homogenous task 集合生成 "N shells" / "◇ ultraplan" / "dreaming" 等字符串,pillNeedsCta 判定 ultraplan attention 态是否显示「↓ to view」。本章要求你理解 Dream 任务的 UI-only 定位,以及 pill 文案与 RemoteAgentTask ultraplanPhase 的联动。

学完本章你应该能

  • 说明 DreamTaskState 字段(phase、sessionsReviewing、filesTouched、turns)
  • 解释 Dream kill 时 rollbackConsolidationLock 与 fork-failure 同路径
  • 理解 getPillLabel 各 type 分支与 allSameType 优化
  • 掌握 pillNeedsCta 与 ultraplanPhase 状态图
  • 对照 MonitorMcpTask stub 与 LocalShellTask kind=monitor 的区别

核心概念(先读懂这些)

Dream 是 UI surfacing,不是新 agent 类型

autoDream.ts 仍 fork 既有 dream agent;DreamTask 仅 registerTask 让 footer pill 显示 "dreaming"。complete/fail 设 notified=true 满足 eviction guard;用户可见完成面是 appendSystemMessage 而非 task-notification XML。

pillLabel 双表面一致

BackgroundTasksIndicator footer pill 与 turn-duration transcript 行共用 getPillLabel,避免「footer 说 2 shells、transcript 说 2 tasks」术语分裂。异构 task 集合 fallback 为 "N background tasks"。

monitor 的两条路径

LocalShellTask kind=monitor 已落地(Bash 脚本 monitor)。MonitorMcpTask type=monitor_mcp 为 MCP 侧预留 stub。pillLabel local_bash 分支会 split shells vs monitors 计数。

建议学习步骤

  1. 阅读 DreamTaskState 与 registerDreamTask
  2. 阅读 addDreamTurn phase 翻转逻辑
  3. 阅读 completeDreamTask / failDreamTask / DreamTask.kill
  4. 阅读 getPillLabel 全分支
  5. 阅读 pillNeedsCta ultraplan 条件
  6. 对照 MonitorMcpTask stub 与 types.ts

常见误区

注意

filesTouched 仅 pattern-match Edit/Write tool_use,不完整反映实际变更

注意

Dream turns 不含 prompt,MAX_TURNS=30 环形缓冲

注意

pillNeedsCta 要求 tasks.length===1 且 remote ultraplan

注意

MonitorMcpTask runtime 不存在,勿与 local_bash kind=monitor 混淆

DreamTask 在 autoDream 链路中的位置

services/autoDream/autoDream.ts
  → fork dream agent (既有 runAgent 路径)
  → registerDreamTask(setAppState, { sessionsReviewing, priorMtime, abortController })
  → onMessage hook → addDreamTurn(taskId, turn, touchedPaths)
  → completeDreamTask / failDreamTask / DreamTask.kill
  → appendSystemMessage 用户可见完成说明

DreamTask 不改变 dream agent 的 prompt 或 tool 池;它仅是 AppState.tasks 注册 + pill 数据源。kill 时 abortController.abort() 并 rollbackConsolidationLock(priorMtime),与 fork-failure catch 共享锁回滚语义,确保下一 session 可重试 consolidation。

DreamTaskState 字段

DreamTaskState 扩展 TaskStateBase:

字段含义
phase'starting' → 首次 Edit/Write 触达 → 'updating'
sessionsReviewing本次 dream 扫描的 session 数量
filesTouchedEdit/Write tool_use 观测路径(不完整)
turnsDreamTurn[] assistant 文本 + toolUseCount 折叠
abortControllerkill 时 abort
priorMtimeconsolidation lock mtime,kill 回滚用

DreamTurn = { text, toolUseCount }。Prompt deliberately 不进入 turns(隐私与体积)。

注释强调:不对 dream prompt 四阶段做 parse,phase 仅二元。

源码引用: src/tasks/DreamTask/DreamTask.ts · 第 14–50 行(共 158 行)

  14| // A single assistant turn from the dream agent, tool uses collapsed to a count.
  15| export type DreamTurn = {
  16|   text: string
  17|   toolUseCount: number
  18| }
  19| 
  20| // No phase detection — the dream prompt has a 4-stage structure
  21| // (orient/gather/consolidate/prune) but we don't parse it. Just flip from
  22| // 'starting' to 'updating' when the first Edit/Write tool_use lands.
  23| export type DreamPhase = 'starting' | 'updating'
  24| 
  25| export type DreamTaskState = TaskStateBase & {
  26|   type: 'dream'
  27|   phase: DreamPhase
  28|   sessionsReviewing: number
  29|   /**
  30|    * Paths observed in Edit/Write tool_use blocks via onMessage. This is an
  31|    * INCOMPLETE reflection of what the dream agent actually changed — it misses
  32|    * any bash-mediated writes and only captures the tool calls we pattern-match.
  33|    * Treat as "at least these were touched", not "only these were touched".
  34|    */
  35|   filesTouched: string[]
  36|   /** Assistant text responses, tool uses collapsed. Prompt is NOT included. */
  37|   turns: DreamTurn[]
  38|   abortController?: AbortController
  39|   /** Stashed so kill can rewind the lock mtime (same path as fork-failure). */
  40|   priorMtime: number
  41| }
  42| 
  43| export function isDreamTask(task: unknown): task is DreamTaskState {
  44|   return (
  45|     typeof task === 'object' &&
  46|     task !== null &&
  47|     'type' in task &&
  48|     task.type === 'dream'
  49|   )
  50| }

register / addDreamTurn / complete

registerDreamTask:

  • generateTaskId('dream')
  • createTaskStateBase(id, 'dream', 'dreaming')
  • status: running, phase: starting, 空 turns/filesTouched
  • registerTask

addDreamTurn updateTaskState:

  • 去重 filesTouched
  • 空 turn 且无新 touched → no-op skip re-render
  • newTouched.length > 0 → phase: 'updating'
  • turns slice(-(MAX_TURNS-1)).concat(turn)

completeDreamTask / failDreamTask:terminal + endTime + notified: true + 清 abortController。无 enqueuePendingNotification。

DreamTask.kill:killed + notified + abort + 条件 rollbackConsolidationLock。

源码引用: src/tasks/DreamTask/DreamTask.ts · 第 52–74 行(共 158 行)

  52| export function registerDreamTask(
  53|   setAppState: SetAppState,
  54|   opts: {
  55|     sessionsReviewing: number
  56|     priorMtime: number
  57|     abortController: AbortController
  58|   },
  59| ): string {
  60|   const id = generateTaskId('dream')
  61|   const task: DreamTaskState = {
  62|     ...createTaskStateBase(id, 'dream', 'dreaming'),
  63|     type: 'dream',
  64|     status: 'running',
  65|     phase: 'starting',
  66|     sessionsReviewing: opts.sessionsReviewing,
  67|     filesTouched: [],
  68|     turns: [],
  69|     abortController: opts.abortController,
  70|     priorMtime: opts.priorMtime,
  71|   }
  72|   registerTask(task, setAppState)
  73|   return id
  74| }

源码引用: src/tasks/DreamTask/DreamTask.ts · 第 76–104 行(共 158 行)

  76| export function addDreamTurn(
  77|   taskId: string,
  78|   turn: DreamTurn,
  79|   touchedPaths: string[],
  80|   setAppState: SetAppState,
  81| ): void {
  82|   updateTaskState<DreamTaskState>(taskId, setAppState, task => {
  83|     const seen = new Set(task.filesTouched)
  84|     const newTouched = touchedPaths.filter(p => !seen.has(p) && seen.add(p))
  85|     // Skip the update entirely if the turn is empty AND nothing new was
  86|     // touched. Avoids re-rendering on pure no-ops.
  87|     if (
  88|       turn.text === '' &&
  89|       turn.toolUseCount === 0 &&
  90|       newTouched.length === 0
  91|     ) {
  92|       return task
  93|     }
  94|     return {
  95|       ...task,
  96|       phase: newTouched.length > 0 ? 'updating' : task.phase,
  97|       filesTouched:
  98|         newTouched.length > 0
  99|           ? [...task.filesTouched, ...newTouched]
 100|           : task.filesTouched,
 101|       turns: task.turns.slice(-(MAX_TURNS - 1)).concat(turn),
 102|     }
 103|   })
 104| }

源码引用: src/tasks/DreamTask/DreamTask.ts · 第 106–157 行(共 158 行)

 106| export function completeDreamTask(
 107|   taskId: string,
 108|   setAppState: SetAppState,
 109| ): void {
 110|   // notified: true immediately — dream has no model-facing notification path
 111|   // (it's UI-only), and eviction requires terminal + notified. The inline
 112|   // appendSystemMessage completion note IS the user surface.
 113|   updateTaskState<DreamTaskState>(taskId, setAppState, task => ({
 114|     ...task,
 115|     status: 'completed',
 116|     endTime: Date.now(),
 117|     notified: true,
 118|     abortController: undefined,
 119|   }))
 120| }
 121| 
 122| export function failDreamTask(taskId: string, setAppState: SetAppState): void {
 123|   updateTaskState<DreamTaskState>(taskId, setAppState, task => ({
 124|     ...task,
 125|     status: 'failed',
 126|     endTime: Date.now(),
 127|     notified: true,
 128|     abortController: undefined,
 129|   }))
 130| }
 131| 
 132| export const DreamTask: Task = {
 133|   name: 'DreamTask',
 134|   type: 'dream',
 135| 
 136|   async kill(taskId, setAppState) {
 137|     let priorMtime: number | undefined
 138|     updateTaskState<DreamTaskState>(taskId, setAppState, task => {
 139|       if (task.status !== 'running') return task
 140|       task.abortController?.abort()
 141|       priorMtime = task.priorMtime
 142|       return {
 143|         ...task,
 144|         status: 'killed',
 145|         endTime: Date.now(),
 146|         notified: true,
 147|         abortController: undefined,
 148|       }
 149|     })
 150|     // Rewind the lock mtime so the next session can retry. Same path as the
 151|     // fork-failure catch in autoDream.ts. If updateTaskState was a no-op
 152|     // (already terminal), priorMtime stays undefined and we skip.
 153|     if (priorMtime !== undefined) {
 154|       await rollbackConsolidationLock(priorMtime)
 155|     }
 156|   },
 157| }

getPillLabel 分支详解

getPillLabel(tasks) 先判 allSameType = tasks.every(t => t.type === tasks[0].type):

local_bash(同质):

  • 拆分 kind===monitor vs 普通 shell
  • "1 shell" / "N shells" + "1 monitor" / "N monitors" 逗号连接

in_process_teammate:按 identity.teamName Set 计数 → "1 team" / "N teams"

local_agent:"1 local agent" / "N local agents"

remote_agent:

  • 单任务 ultraplan:ultraplanPhase plan_ready → ◆ filled "ultraplan ready";needs_input → ◇ "needs your input";default ◇ "ultraplan"
  • 否则 ◇ "N cloud sessions"

local_workflow:"N background workflow(s)"

monitor_mcp:"N monitor(s)"(stub type 预留)

dream:固定 "dreaming"

异构混合 → "${n} background task(s)"

源码引用: src/tasks/pillLabel.ts · 第 10–67 行(共 83 行)

  10| export function getPillLabel(tasks: BackgroundTaskState[]): string {
  11|   const n = tasks.length
  12|   const allSameType = tasks.every(t => t.type === tasks[0]!.type)
  13| 
  14|   if (allSameType) {
  15|     switch (tasks[0]!.type) {
  16|       case 'local_bash': {
  17|         const monitors = count(
  18|           tasks,
  19|           t => t.type === 'local_bash' && t.kind === 'monitor',
  20|         )
  21|         const shells = n - monitors
  22|         const parts: string[] = []
  23|         if (shells > 0)
  24|           parts.push(shells === 1 ? '1 shell' : `${shells} shells`)
  25|         if (monitors > 0)
  26|           parts.push(monitors === 1 ? '1 monitor' : `${monitors} monitors`)
  27|         return parts.join(', ')
  28|       }
  29|       case 'in_process_teammate': {
  30|         const teamCount = new Set(
  31|           tasks.map(t =>
  32|             t.type === 'in_process_teammate' ? t.identity.teamName : '',
  33|           ),
  34|         ).size
  35|         return teamCount === 1 ? '1 team' : `${teamCount} teams`
  36|       }
  37|       case 'local_agent':
  38|         return n === 1 ? '1 local agent' : `${n} local agents`
  39|       case 'remote_agent': {
  40|         const first = tasks[0]!
  41|         // Per design mockup: ◇ open diamond while running/needs-input,
  42|         // ◆ filled once ExitPlanMode is awaiting approval.
  43|         if (n === 1 && first.type === 'remote_agent' && first.isUltraplan) {
  44|           switch (first.ultraplanPhase) {
  45|             case 'plan_ready':
  46|               return `${DIAMOND_FILLED} ultraplan ready`
  47|             case 'needs_input':
  48|               return `${DIAMOND_OPEN} ultraplan needs your input`
  49|             default:
  50|               return `${DIAMOND_OPEN} ultraplan`
  51|           }
  52|         }
  53|         return n === 1
  54|           ? `${DIAMOND_OPEN} 1 cloud session`
  55|           : `${DIAMOND_OPEN} ${n} cloud sessions`
  56|       }
  57|       case 'local_workflow':
  58|         return n === 1 ? '1 background workflow' : `${n} background workflows`
  59|       case 'monitor_mcp':
  60|         return n === 1 ? '1 monitor' : `${n} monitors`
  61|       case 'dream':
  62|         return 'dreaming'
  63|     }
  64|   }
  65| 
  66|   return `${n} background ${n === 1 ? 'task' : 'tasks'}`
  67| }

pillNeedsCta 与 ultraplan 状态图

pillNeedsCta(tasks) 返回 footer 是否显示 dimmed " · ↓ to view":

条件全部满足:

  1. tasks.length === 1
  2. task.type === 'remote_agent'
  3. task.isUltraplan === true
  4. task.ultraplanPhase !== undefined

即 needs_input 与 plan_ready 两种 attention 态 surface CTA;plain running 仅 diamond + label。

RemoteAgentTask poller / ExitPlanModeScanner 写入 ultraplanPhase;UI BackgroundTasksIndicator 消费 pillNeedsCta 控制 CTA 渲染。

设计 mockup 注释:◇ open while running/needs-input,◆ filled once ExitPlanMode awaiting approval。

源码引用: src/tasks/pillLabel.ts · 第 69–82 行(共 83 行)

  69| /**
  70|  * True when the pill should show the dimmed " · ↓ to view" call-to-action.
  71|  * Per the state diagram: only the two attention states (needs_input,
  72|  * plan_ready) surface the CTA; plain running shows just the diamond + label.
  73|  */
  74| export function pillNeedsCta(tasks: BackgroundTaskState[]): boolean {
  75|   if (tasks.length !== 1) return false
  76|   const t = tasks[0]!
  77|   return (
  78|     t.type === 'remote_agent' &&
  79|     t.isUltraplan === true &&
  80|     t.ultraplanPhase !== undefined
  81|   )
  82| }

源码引用: src/tasks/RemoteAgentTask/RemoteAgentTask.tsx · 第 51–58 行(共 1103 行)

  51| import {
  52|   archiveRemoteSession,
  53|   pollRemoteSessionEvents,
  54| } from '../../utils/teleport.js'
  55| import type { TodoList } from '../../utils/todo/types.js'
  56| import type { UltraplanPhase } from '../../utils/ultraplan/ccrSession.js'
  57| 
  58| export type RemoteAgentTaskState = TaskStateBase & {

MonitorMcpTask stub

MonitorMcpTask.ts 当前:

export type MonitorMcpTaskState = Record<string, unknown>
export function isMonitorMcpTask(_value: unknown): boolean {
  return false
}

与 LocalShellTask kind='monitor' 的区别:

维度local_bash + kind=monitormonitor_mcp (stub)
实现LocalShellTask.tsx 完整无 Task 实现
触发BashTool MONITOR_TOOL未来 MCP monitor
pillgetPillLabel local_bash 分支内计数独立 monitor_mcp 分支
isBackgroundTask可用永不匹配

types.ts 已纳入 MonitorMcpTaskState 联合;pillLabel 已写 monitor_mcp case。待 product 实现 MCP monitor runner 后填充。

源码引用: src/tasks/MonitorMcpTask/MonitorMcpTask.ts · 第 1–5 行(共 6 行)

   1| export type MonitorMcpTaskState = Record<string, unknown>
   2| 
   3| export function isMonitorMcpTask(_value: unknown): boolean {
   4|   return false
   5| }

源码引用: src/tasks/types.ts · 第 22–29 行(共 47 行)

  22| export type BackgroundTaskState =
  23|   | LocalShellTaskState
  24|   | LocalAgentTaskState
  25|   | RemoteAgentTaskState
  26|   | InProcessTeammateTaskState
  27|   | LocalWorkflowTaskState
  28|   | MonitorMcpTaskState
  29|   | DreamTaskState

isBackgroundTask 与 Dream

isBackgroundTask(types.ts)对 dream 任务:

  • dream 注册时 status: running,无 isBackgrounded 字段
  • 谓词第三段:'isBackgrounded' in task && task.isBackgrounded === false → false;dream 无此字段 → 通过
  • 故 running dream 始终出现在 background pill(文案 "dreaming")

Dream complete 后 status 变 terminal,isBackgroundTask 返回 false,pill 自动消失。

Contrast local_agent foreground:显式 isBackgrounded: false 隐藏 pill 直至 background。

源码引用: src/tasks/types.ts · 第 37–46 行(共 47 行)

  37| export function isBackgroundTask(task: TaskState): task is BackgroundTaskState {
  38|   if (task.status !== 'running' && task.status !== 'pending') {
  39|     return false
  40|   }
  41|   // Foreground tasks (isBackgrounded === false) are not yet "background tasks"
  42|   if ('isBackgrounded' in task && task.isBackgrounded === false) {
  43|     return false
  44|   }
  45|   return true
  46| }

源码引用: src/tasks/DreamTask/DreamTask.ts · 第 43–50 行(共 158 行)

  43| export function isDreamTask(task: unknown): task is DreamTaskState {
  44|   return (
  45|     typeof task === 'object' &&
  46|     task !== null &&
  47|     'type' in task &&
  48|     task.type === 'dream'
  49|   )
  50| }

UI 消费方与调试

pillLabel 消费方:

  • components/BackgroundTasksIndicator — footer pill 主入口
  • turn-duration transcript line — 注释要求 terminology 一致
  • useBackgroundTaskNavigation — Shift+Down 对话框标题可能 derive 自同类逻辑

调试 pill 文案错误:打印 getPillLabel(filteredTasks) 输入集合,确认 allSameType 与 type 字段。Ultraplan CTA 不显示:查 ultraplanPhase 是否 undefined(running 态正常无 CTA)。

Dream pill 不消失:查 completeDreamTask 是否调用、status 是否 terminal。Dream kill 后 consolidation 不重试:查 rollbackConsolidationLock 是否执行。

本章小结与延伸

DreamTask = autoDream 的 pill 适配层。pillLabel = footer/transcript 文案 SSOT。MonitorMcpTask = 预留 stub。 继续学习:

  • shell-workflow-tasks
  • remote-agent-task
Prev
shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
Next
keybinding-registry · 注册、Provider 与 useKeybinding