# Agent Note: 面向人类的 `/goal` 命令

Status: implemented

[English](2026-07-19-human-goal-command.md) | 中文

## 问题

同会话目标领域和模型工具提供了状态机与自然语言语义路径，但尚不足以构成面向人类的 UX。用户需要在不询问模型的情况下检查准确的当前阶段与 Round 预算，在不消耗模型轮次的情况下明确暂停或清除工作，并在会话恢复后经过必要的人类决策重新激活已恢复的活跃目标。若在各 UI 中分别实现这些操作，就会重复解析逻辑、导致各界面发生偏差，还可能把未知或不可用的命令交给模型处理。

该命令还必须遵守目标设计中的两类状态。持久阶段、目标描述、修订号与 Round 来自会话日志；进程本地激活态决定活跃目标能否自动继续。恢复后若只显示“活跃”，就会掩盖目标已被有意设为未激活、正在等待人类授权这一事实。

## 决策

位于 `packages/goal/command-goal/` 的 `@deepseek-ai/dsh-command-goal` 是构建在 `ctx.commands` 与 `ctx.goals` 之上的命令生产方。它注册一个全局 `goal` 定义，因此组合中的每个命令适配器都会发现同一个命令；不兼容的应用应省略该生产方，而不是在适配器处屏蔽其注册。处理器从命令分发接收准确的目标 agent（智能体），通过领域服务读取或改变该 agent 的目标，并返回直接的纯文本 UI 输出。它不导入任何适配器或具体 agent loop（智能体循环）。

该命令遵循 [OpenAI Codex 公共仓库 `678157a` 提交中的 TUI 分发实现](https://github.com/openai/codex/blob/678157acaa819d5510adfe359abb5d0392cfe461/codex-rs/tui/src/chatwidget/slash_dispatch.rs#L750-L805)所呈现的紧凑形态：无参数状态查询、自由形式目标描述，以及 `clear`、`edit`、`pause` 或 `resume` 控制。固定到提交的链接使调研所得语法在 Codex 后续演进时仍可核验。本仓库保留自身的事件溯源状态、Round 计数策略与恢复后激活规则，而不复制 Codex 的 SQLite、token 预算或自动恢复行为。

### 语法与生命周期动词

`/goal` 报告目标描述、面向人类的持久阶段、`roundsStarted/maxGoalRounds`、进程本地 `armed` 或 `disarmed` 激活态，以及当前状态下有意义的命令。没有当前目标时，它会报告该事实与完整用法。读取状态不会添加会话事件。

`/goal <objective>` 创建活跃且已激活的目标。已完成目标可以被替换，此时通过现有领域规则创建新的目标身份。任何未完成目标都会让命令直接失败，并提示用户使用行内编辑或明确清除。通用命令服务有意不提供模态确认 API；若静默执行清除再创建两条持久记录，就等于凭空制造破坏性同意，并暴露一个非原子的失败窗口。

`/goal edit <objective>` 编辑当前未完成目标，但不改变其阶段或激活态。若目标已经完成，则创建一个新的活跃目标，因为领域不允许恢复已完成状态，而新的目标描述应拥有新的目标身份。单独使用 `edit` 会返回错误而不是启动编辑器，因为可移植的非结构化命令契约没有模态编辑器。

`/goal pause`、`/goal resume` 与 `/goal clear` 使用当前视图调用相应的比较并交换领域动词。恢复既适用于停止的持久阶段，也适用于会话恢复、fork 或驱动器替换后处于活跃但未激活状态的目标。领域规则仍会拒绝已耗尽的 Round 上限、对已活跃且已激活目标的重复恢复、非法阶段转换与陈旧身份。清除会移除当前指针，而会话日志保留带修订号的墓碑和此前快照。

控制词会在去除两端空白后按 ASCII 大小写不敏感方式匹配。只有占据完整后缀时才被视为控制；其余任何非空文本都是目标描述。这保持了可预测的自由形式命令规则：`/goal pause after verification` 是目标描述，而不是被部分解析的暂停命令。

### 输出与失败边界

状态输出省略品牌化 id 与比较并交换修订号，因为它们属于模型/插件协调细节，而不是人类控制项。输出包含激活态，因为该事实会改变工作是否继续；被阻塞的目标还会包含其持久策略代码和面向人类的说明。命令提示从准确状态派生：已激活的活跃目标提供暂停，未激活的活跃目标或已暂停/被阻塞目标提供恢复，已完成目标则提供替换或清除。

预期的 `GoalError` 失败会变为一个稳定且不含品牌化 id 的 `CommandResult.error`，使领域诊断不会向面向人类的界面泄露比较并交换内部细节，非法操作也绝不会进入模型历史。当前状态负责提供针对具体状态且可执行的恢复路径。其他异常仍是适配器可见的命令失败；若把程序缺陷当成普通领域错误，就会隐藏问题。命令处理器只执行同步领域变更，因此请求取消会在变更开始前由命令注册表决定，不存在需要回滚的外逸异步副作用。

通用斜杠输入、状态文本与错误不会持久化。成功的 goal 变更会追加领域自有的 `goal/change` 事件，而且不会把模型上下文排队。该命令不会引入可能与领域事件不一致的第二份审计记录。

### 应用组合

`agent-spine-demo` 接受可选的 `goals` 组合对象，其中包含目标领域与模型工具的所有者配置。省略或设为 `false` 时不会挂载该栈。对无头单次调用方而言，明确选择加入非常重要：它们的结果 API 会在与调用关联的一个物理轮次后结束，不能静默变成长时间运行的逻辑目标操作。

TUI 应用包作出相反的产品选择。它默认让 `goals` 使用所有者默认值，并挂载目标领域、模型工具、同会话驱动器、命令注册表与本生产方；`goals: false` 会一致地移除整个栈。[ACP（Agent Client Protocol）自动化应用](../simplification/2026-07-23-acp-automation-only-protocol.md)也默认挂载目标领域与模型工具，但有意省略命令服务。Python SDK 运行时闭包交付本生产方、命令与目标栈，使外部 `cordis.yml` 能组合相同命令。

## 测试

生产方测试套件使用真实命令注册表、目标服务、agent 注册表与会话日志。它覆盖 Loader 安全导出、注册表发现、dispose（资源释放）、空状态、目标描述解析、拒绝未完成目标替换、行内编辑、已完成目标替换、无目标状态下的所有控制命令、暂停/恢复/清除、每个持久阶段、阻塞代码/说明展示、已激活/未激活展示、经净化的领域错误、意外失败与持久变更记录。应用组合测试覆盖显式主干选择加入、TUI 默认值、一致停用、转发的领域/工具配置、命令发现、打包运行时闭包与扩展后的模型工具组装。ACP 后端快照继续固定目标工具 schema，与这项面向人类的命令无关。

## 考虑过的替代方案

- **让模型把 `/goal` 当作普通文本处理**——不予采纳，因为状态与直接生命周期操作会消耗模型轮次、可能被重新解释，也无法提供确定性的命令发现。
- **在各 UI 中分别实现处理器**——不予采纳，因为语法、错误行为与目标状态格式会发生偏差，可选部署也无法把该功能作为一个 effect 统一增删。
- **为 `ctx.commands` 添加模态编辑与替换确认**——不予采纳，因为现有跨界面契约是非结构化输入加直接输出；通用交互协议所需的设计远超这一个生产方。
- **静默替换未完成目标**——不予采纳，因为这会在没有原子性或明确破坏性意图的情况下组合清除与创建。
- **在人类状态中暴露目标 id 与修订号**——不予采纳，因为人类操作始终在一个同步处理器内针对确切的当前视图；这些字段只会增加实现噪声，无法避免另一处竞态。
- **在无 UI 主干中无条件启用目标**——不予采纳，因为单次 SDK/CLI 的结束契约是物理轮次 API，而不是目标操作 API。

## 后果

- TUI 暴露由可移除插件提供的 Codex 形态 `/goal` 命令。
- 人类状态会区分持久阶段与实时激活态，并报告准确的目标 Round 上限。
- 直接暂停、恢复、清除、创建与编辑不消耗模型轮次，而其已接受变更仍可从会话日志重建。
- 恢复后的会话等待人类决策；`/goal resume` 是字面命令路径，任何语言的普通提示词则可以授权模型工具路径。
- 无头组合保持单轮行为，除非明确选择加入目标并定义自己的长时间运行结束契约。

## 已知限制与暂缓事项

- 可移植命令契约没有模态编辑器或确认交互；在出现通用跨界面交互原语之前，行内编辑与明确清除是有意选择。
- `/goal` 不接受逐命令 Round 上限。部署配置拥有默认值；得到直接人类指示后，已授权模型工具可以编辑上限。
- TUI 渲染可移植纯文本，而不是持续更新的目标状态组件。可重连命令输出和适配器专用状态指示器予以延期。
- ACP 自动化服务器、无头 CLI 与 JSON-RPC 前端不消费命令注册表。
- 该命令观察并改变状态，但不认证完成或阻塞。基于评估器的认证延期到具有明确权限与隔离契约的独立策略层。
