---
url: /wave-agent/specs/multi-agent/task-management-tools.md
description: TaskCreate/TaskGet/TaskUpdate/TaskList，`~/.wave/tasks/` 存储和任务列表 UI
---

# 功能规格说明：任务管理工具与 UI

**创建日期**：2026-02-11

## 用户场景与测试 *（必填）*

### 用户故事：任务工具默认关闭，可按需开启（优先级：P1）

作为用户，我希望任务管理工具（TaskCreate / TaskGet / TaskUpdate / TaskList）默认不注册，需要时用环境变量 `WAVE_ENABLE_TASK_TOOLS` 打开（形态对齐 Claude Code 的 `CLAUDE_CODE_ENABLE_TODO_TOOLS`，命名按我们自己的工具——我们已无 TodoWrite），以便不需要任务列表的对话不必为这四个工具付出工具定义、按需发现与任务提醒的开销。

**为什么是这个优先级**：默认值决定所有会话的工具集与提示词，是本功能的前提，故为 P1。

**独立测试**：可以在未做任何配置的仓库里启动新会话，验证四个任务工具缺席、系统提示词不含任务管理提示句、任务提醒不注入；再以 `WAVE_ENABLE_TASK_TOOLS=1` 启动（或写进 settings.json 的 `env` 块后热重载），验证四个工具、提示词与提醒一并恢复来进行完整测试。

**验收场景**：

1. **假设** `WAVE_ENABLE_TASK_TOOLS` 未设置，**当** 新会话启动时，**则** TaskCreate / TaskGet / TaskUpdate / TaskList 都不在工具集中——既不扁平声明，也不进入 Exec 的按需池——系统提示词不含任务管理提示句，任务提醒不注入。
2. **假设** `WAVE_ENABLE_TASK_TOOLS` 为 `1` 或 `true`，**当** 新会话启动时，**则** 四个任务工具按原样注册（仍然 `defer`，经 Exec 按需加载），系统提示词的任务管理句与任务提醒一并恢复；值为 `0` 或 `false` 时与未设置一致。
3. **假设** 会话运行中把 `env.WAVE_ENABLE_TASK_TOOLS` 写进 settings.json，**当** 设置热重载完成后，**则** 从下一个回合起四个工具可用，无需重启会话。
4. **假设** 会话运行中把该变量去掉或置为 `0`，**当** 设置热重载完成后，**则** 四个工具注销、不再进按需池；磁盘上的既有任务数据不受影响。
5. **假设** 主会话因开关关闭而没有任务工具，**当** 它派生子代理时，**则** 子代理同样没有任务工具——可用性与主会话一致，不按子代理自身模型或其他配置单独判定。
6. **假设** settings.json 的 `env` 块与进程环境都设置了 `WAVE_ENABLE_TASK_TOOLS`，**当** 解析开关时，**则** settings.json 的值生效——与其它 `WAVE_*` 变量的解析顺序一致（会话环境快照优先于进程环境）。
7. **假设** 任务工具被关闭，**当** 使用后台任务（TaskStop、`/tasks` 面板、Ctrl+T 任务列表切换）时，**则** 行为不变——后台任务与任务列表是两套机制。

***

### 用户故事：创建和跟踪任务（优先级：P1）

作为用户，我希望创建一个新任务，以便跟踪我在特定目标上的进度。

**为什么是这个优先级**：这是核心功能。没有任务创建，其他工具就没有数据可以操作。

**独立测试**：可以通过调用 TaskCreate 并验证在正确目录中创建了具有预期内容的 JSON 文件来进行完整测试。

**验收场景**：

1. **假设**会话处于活动状态，**当**我使用主题和描述调用 TaskCreate 时，**则**创建一个具有唯一 ID 的新任务并存储在 `~/.wave/tasks/{taskListId}/{taskId}.json` 中。
2. **假设**任务已创建，**当**我使用 taskId 调用 TaskGet 时，**则**我收到该任务的完整详情。

***

### 用户故事：更新任务进度（优先级：P2）

作为用户，我希望更新任务状态并添加评论，以便保持我的进度记录为最新。

**为什么是这个优先级**：任务管理是动态的；能够更新状态和添加注释对于跟踪工作进度至关重要。

**独立测试**：可以通过对现有任务调用 TaskUpdate 然后通过 TaskGet 验证更改或检查 JSON 文件来进行测试。

**验收场景**：

1. **假设**存在一个现有任务，**当**我使用新状态（例如"in_progress"、"completed"）调用 TaskUpdate 时，**则**任务的状态在存储中被更新。
2. **假设**存在一个现有任务，**当**我使用元数据或描述更改调用 TaskUpdate 时，**则**任务被相应更新。

***

### 用户故事：列出所有任务（优先级：P3）

作为用户，我希望看到当前会话的所有任务列表，以便获得工作概览。

**为什么是这个优先级**：提供跨多个任务的可见性，这对于管理复杂工作流很重要。

**独立测试**：可以通过创建多个任务并调用 TaskList 来确保所有任务都被返回来进行测试。

**验收场景**：

1. **假设**当前任务列表存在多个任务，**当**我调用 TaskList 时，**则**我收到所有任务的摘要列表，包括其 ID、主题和当前状态。

***

### 用户故事：在聊天 UI 中查看任务列表（优先级：P1）

作为用户，我希望在消息列表底部看到当前任务的摘要，以便始终跟踪进度而无需手动列出任务。

**为什么是这个优先级**：这在对话流程中提供对任务状态的即时可见性。

**独立测试**：可以通过创建任务并验证任务列表组件出现在聊天界面底部来进行测试。

**验收场景**：

1. **假设**当前会话中有活动任务，**当**我查看消息列表时，**则**任务列表摘要显示在底部。
2. **假设**任务列表已显示，**当**任务状态更改时，**则**任务列表 UI 更新以反映新状态。
3. **假设**一个已保存会话的任务全部处于已完成状态，**当**我打开或切换到该会话时，**则**任务列表立即保持隐藏，不会先短暂显示再于数秒后消失。
4. **假设**当前会话的活动任务超过 4 个，**当**我查看任务列表时，**则**列表最多同时显示 4 个任务，其余任务在列表区域内滚动查看。

***

### 用户故事：废弃遗留 TodoWrite 工具（优先级：P4）

作为系统维护者，我希望移除遗留的 TodoWrite 工具，以便 agent 专门使用新的任务管理系统。

**为什么是这个优先级**：确保向新系统的平稳过渡，并防止旧任务管理方法与新方法之间的混淆。

**独立测试**：验证 `TodoWrite` 不再出现在 agent 的工具集中。

**验收场景**：

1. **假设** agent 已初始化，**当**我列出可用工具时，**则** `TodoWrite` 不在列表中。

***

### 用户故事：任务提醒注入（优先级：P3）

作为系统，我必须定期提醒 agent 使用任务跟踪工具，以确保 agent 不会遗忘对任务进度的管理。

**为什么是这个优先级**：任务提醒是辅助性功能，提升 agent 自主管理任务的意识，但不影响核心任务 CRUD 操作。

**独立测试**：可以通过模拟连续 10 个 assistant turn 不使用 TaskCreate/TaskUpdate，然后验证下一次 API 调用中注入了提醒消息来进行测试。

**验收场景**：

1. **假设**会话处于活动状态且 TaskCreate/TaskUpdate 工具可用（无论它当下是扁平声明、还是因延迟判定而只能经 `Exec` 沙箱可达），**当**连续 10 个 assistant turn 未调用 TaskCreate 或 TaskUpdate，且距上次提醒也已过去至少 10 个 assistant turn 时，**则**系统在下一次 API 调用中注入一条 user 消息形式的任务提醒，并将该提醒持久化到会话历史中作为 `isMeta: true` 消息，确保后续轮次的计数器能检测到该提醒。
2. **假设**提醒已注入，**当** agent 收到提醒时，**则**提醒内容指示 agent 不得向用户提及该提醒的存在。
3. **假设**当前存在活动任务，**当**提醒触发时，**则**提醒消息末尾附加当前任务列表，格式为 `#id [status] subject`。
4. **假设**当前任务列表为空，**当**提醒触发时，**则**提醒消息仍然注入，作为通用提醒，不包含任务列表。
5. **假设** agent 调用了 TaskCreate 或 TaskUpdate（无论该调用是扁平发出的、还是包在 `Exec` 脚本里发出的），**当**工具执行完成后，**则** `turnsSinceLastTaskManagement` 计数器重置为 0。
6. **假设** agent 调用了 TaskList 或 TaskGet（只读工具），**当**工具执行完成后，**则** `turnsSinceLastTaskManagement` 计数器**不**被重置。
7. **假设** TaskCreate/TaskUpdate 因延迟判定而未扁平声明、只能经 `Exec` 沙箱调用，**当** agent 在脚本里调用它们时，**则** 可用性判据与计数重置必须与扁平调用**完全一致**——判据取的是「这个工具有没有被调用过」这一宿主侧事实，而不是它当下以哪种形态被声明；反之，不得因为工具进了池就把它读成"不可用"而静默停掉提醒。

***

### 边界情况

* **任务工具关闭后任务列表面板显示什么？** 面板数据来自 TaskManager，关闭只是不再产生新任务；历史任务仍在 `~/.wave/tasks/` 下，面板按数据渲染，没有任务时即空面板。
* **关闭前创建的任务会怎样？** 不删除、不清空；重新打开开关后照旧可见、可更新。
* **为什么保留开关而不是删掉工具？** 需要显式任务清单的场景（长任务、跨会话协作）可以一键恢复，而默认路径不必为一个多数对话用不上的清单付上下文成本。
* **任务 ID 不存在时会发生什么？** TaskGet 和 TaskUpdate 应返回清晰的错误消息，指示任务未找到。
* **系统如何处理无效的状态转换？** 系统应验证提供的状态是允许值之一。
* **如果存储目录不可写会怎样？** 工具应优雅地处理文件系统错误。
* **任务很多时会发生什么？** `TaskManager.listTasks()` 始终按 ID 升序返回；TaskList 组件在此稳定顺序之上，最多同时显示 4 个任务，多余任务在固定高度的列表区域内滚动查看，并通过折叠摘要和自动隐藏已完成任务来处理长任务列表。
* **如果 TaskCreate/TaskUpdate 工具不可用会怎样？** 系统必须完全跳过任务提醒注入，不发送任何提醒消息。这里的"不可用"**包括**扁平声明与 `Exec` 沙箱可达两条路都不通的情形（例如被权限规则拒绝）；而工具**仅因延迟判定**而未扁平声明、仍可从沙箱调用时，一律按"可用"处理，不构成跳过条件。
* **如果任务列表为空会怎样？** 提醒仍然触发，作为通用提醒鼓励 agent 使用任务跟踪，但不附加任务列表。
* **并发创建任务时会发生什么？** 系统使用会话级排他文件锁串行化"读取最大 ID + 写入任务文件"这一临界区，确保并发 `TaskCreate` 调用获得唯一递增 ID，不会产生重复或冲突。
* **进程在持有锁时崩溃会怎样？** 后续获取者检测到锁文件 mtime 超过陈旧阈值（10 秒）时，判定持有者已不活跃，将其删除后重试，不会因遗留锁文件而永久卡死。
* **Windows 上释放锁后立即重新获取会怎样？** Windows 文件系统在删除文件后存在 pending-delete 窗口，此时以 `wx` 方式打开可能返回 `EPERM` 而非 `EEXIST`；系统将 `EPERM` 视为瞬时错误进行有界重试，而非硬失败。
