DSH / Atlas
2026-07-19implementedfeature

Human `/goal` command

面向人类的 `/goal` 命令

The same-session goal domain and model tools provide the state machine and semantic natural-language path, but they are not a sufficient human UX. A user needs to inspect the exact current phase and round budget without asking the model, explicitly pause or clear work without spending a model turn, and rearm a restored active goal after the required post-resume human decision. Implementing those actions independently

English

Problem

The same-session goal domain and model tools provide the state machine and semantic natural-language path, but they are not a sufficient human UX. A user needs to inspect the exact current phase and round budget without asking the model, explicitly pause or clear work without spending a model turn, and rearm a restored active goal after the required post-resume human decision. Implementing those actions independently in each UI would duplicate parsing, let the surfaces drift, and risk routing an unknown or unavailable command into the model.

The command must also respect the goal design's two kinds of state. Durable phase, objective, revisions, and rounds come from the session log; process-local activation decides whether an active goal may continue automatically. Showing only “active” after a resume would be misleading when the restored goal is intentionally disarmed and waiting for human authorization.

Decision

@deepseek-ai/dsh-command-goal in packages/goal/command-goal/ is a command producer over ctx.commands and ctx.goals. It registers one global goal definition, so every command adapter in the composition discovers the same command; an incompatible app omits this producer rather than masking its registration at an adapter. The handler receives the exact target agent from command dispatch, reads or mutates that agent's goal through the domain service, and returns direct plain-text UI output. It does not import either adapter or the concrete agent loop.

The command follows the compact Codex shape in the public OpenAI Codex TUI dispatcher at commit 678157a: bare status, a free-form objective, and clear, edit, pause, or resume controls. The commit permalink makes the researched grammar durable even as Codex evolves. This repository keeps its own event-sourced state, round-count policy, and post-resume activation rule rather than copying Codex's SQLite, token budget, or automatic-resume behavior.

Grammar and lifecycle verbs

/goal reports the objective, human-readable durable phase, roundsStarted/maxGoalRounds, process-local armed or disarmed activation, and commands meaningful from that state. With no current goal it reports that fact plus complete usage. Reading status adds no session event.

/goal <objective> creates an active armed goal. A completed goal may be replaced, which creates a fresh goal identity through the existing domain rule. Any unfinished goal makes the command fail directly with instructions to use inline edit or explicit clear. The generic command service deliberately has no modal confirmation API, so silently clearing and creating two durable records would manufacture destructive consent and expose a non-atomic failure window.

/goal edit <objective> edits the current non-complete goal without changing phase or activation. On a completed goal it creates a fresh active goal because the domain does not permit completed state to resume and a new completion objective is a new goal identity. Bare edit is an error rather than an editor launch because the portable unstructured command contract has no modal editor.

/goal pause, /goal resume, and /goal clear call the matching compare-and-set domain verbs against the current view. Resume covers both stopped durable phases and an active-but-disarmed goal after session resume, fork, or driver replacement. Domain rules still reject exhausted round caps, redundant active/armed resume, invalid phase transitions, and stale identity. Clear removes the current pointer while the session log retains the revisioned tombstone and earlier snapshots.

Control words are ASCII-case-insensitive after outer whitespace trimming. They are controls only when they occupy the full suffix; any other non-empty text is an objective. This matches the predictable free-form command rule: /goal pause after verification is a goal objective, not a partially parsed pause command.

Output and failure boundary

Status output omits branded ids and compare-and-set revisions because those are model/plugin coordination details rather than human controls. It includes activation because that fact changes whether work will continue, and a blocked goal includes its durable policy code and human-readable explanation. Command hints are derived from the exact state: an armed active goal offers pause, a disarmed active or paused/blocked goal offers resume, and a completed goal offers replacement or clear.

Expected GoalError failures become one stable, branded-id-free CommandResult.error, so domain diagnostics do not leak compare-and-set internals into the UI and invalid operations never enter model history. The current status supplies the actionable state-specific recovery. Other exceptions remain adapter-visible command failures; treating programmer faults as ordinary domain errors would hide defects. The command handler performs only synchronous domain mutations, so request cancellation is decided by the command registry before the mutation begins and there is no escaped asynchronous side effect to unwind.

Generic slash input, status text, and errors are not persisted. Successful goal mutations append the domain-owned goal/change event and do not queue model context. The command introduces no second audit record that could disagree with the domain event.

App composition

agent-spine-demo accepts an optional goals composition object containing the goal-domain and model-tool owner configs. Omission or false leaves the stack unmounted. This explicit opt-in is important for headless one-shot callers: their result API settles one correlated physical turn and must not silently become a long-running logical goal operation.

The TUI app bundle makes the opposite product choice. It defaults goals to the owner defaults and mounts the goal domain, model tools, same-session driver, command registry, and this producer; goals: false removes the stack coherently. The ACP automation app also defaults the goal domain and model tools but deliberately omits command services. The Python SDK runtime closure ships this producer, commands, and the goal stack so an external cordis.yml can compose the same command.

Testing

The producer suite uses the real command registry, goal service, agent registry, and session log. It covers Loader-safe exports, registry discovery, disposal, empty status, objective parsing, unfinished replacement refusal, inline edit, completed replacement, all missing-state controls, pause/resume/clear, every durable phase, blocked code/explanation presentation, armed/disarmed presentation, sanitized domain errors, unexpected failures, and persisted mutation records. App composition tests cover explicit spine opt-in, TUI defaults, coherent opt-out, forwarded domain/tool config, command discovery, the packaged-runtime closure, and the expanded model-tool assembly. ACP backend snapshots continue to pin the goal tool schemas independently of this human command.

Alternatives considered

  • Let the model handle /goal as ordinary text — rejected because status and direct lifecycle actions would cost a model turn, could be reinterpreted, and would not provide deterministic command discovery.
  • Implement separate handlers in each UI — rejected because grammar, error behavior, and goal-state formatting would drift and optional deployments could not add or remove the capability as one effect.
  • Add modal editing and replacement confirmation to ctx.commands — rejected because the existing cross-UI contract is unstructured input plus direct output; a general interaction protocol needs more than this one producer.
  • Silently replace an unfinished goal — rejected because it combines clear and create without atomicity or explicit destructive intent.
  • Expose goal id and revision in human status — rejected because human actions always target the exact current view inside one synchronous handler; those fields add implementation noise without preventing another race.
  • Enable goals unconditionally in the UI-less spine — rejected because one-shot SDK/CLI settlement is a physical-turn API, not a goal-operation API.

Consequences

  • TUI exposes one Codex-shaped /goal command supplied by a removable plugin.
  • Human status distinguishes durable phase from live activation and reports the exact goal-round cap.
  • Direct pause, resume, clear, creation, and edit consume no model turn while their accepted mutations remain reconstructable from the session log.
  • Restored sessions wait for a human decision; /goal resume is the literal command path, while an ordinary prompt in any language may authorize the model tool path.
  • Headless compositions retain one-turn behavior unless they explicitly opt into goals and define their own long-running settlement contract.

Known limitations and deferred work

  • The portable command contract has no modal editor or confirmation interaction; inline edit and explicit clear are intentional until a general cross-UI interaction primitive exists.
  • /goal does not accept a per-command round cap. Deployment config owns the default, and the authorized model tool can edit a cap after direct human instruction.
  • TUI renders portable plain text rather than a continuously updated goal status widget. Reconnectable command output and adapter-specific status indicators are deferred.
  • The ACP automation server, headless CLI, and JSON-RPC entry points do not consume the command registry.
  • The command observes and mutates state but does not certify completion or blockers. Evaluator-backed certification remains deferred to a separate policy layer with an explicit authority and isolation contract.

中文

问题

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

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

决策

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

该命令遵循 OpenAI Codex 公共仓库 678157a 提交中的 TUI 分发实现所呈现的紧凑形态:无参数状态查询、自由形式目标描述,以及 cleareditpauseresume 控制。固定到提交的链接使调研所得语法在 Codex 后续演进时仍可核验。本仓库保留自身的事件溯源状态、Round 计数策略与恢复后激活规则,而不复制 Codex 的 SQLite、token 预算或自动恢复行为。

语法与生命周期动词

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

/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/change 事件,而且不会将模型上下文加入队列。该命令不会引入可能与领域事件不一致的第二份审计记录。

应用组合

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

TUI 应用包作出相反的产品选择。它默认让 goals 使用所有者默认值,并挂载目标领域、模型工具、同会话驱动器、命令注册表与本生产方;goals: false 会一致地移除整个栈。ACP(Agent Client Protocol)自动化应用也默认挂载目标领域与模型工具,但有意省略命令服务。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 运行入口不消费命令注册表。
  • 该命令观察并改变状态,但不认证完成或阻塞。基于评估器的认证延期到具有明确权限与隔离约定的独立策略层。