# 唤醒可见回复契约

> 规范路径：`specs/daemon/wake-visible-reply-contract.md`
> 相关规范：[AgentTeam Daemon 规范](./daemon.md)、[Prompt Compiler Architecture](./prompt-compiler-architecture.md)、[消息投递运行时可靠性](../message/delivery-runtime.md)
> 状态：当前合同
> 范围：AgentTeam daemon 从 wake 到可见回复的语义、prompt compiler 的通信契约指引，以及面向仍声明旧 capability 的 daemon 的兼容投影边界。可见回复契约始终启用（always-on），不再有账号级门控或 agent 级开关。

## 1. 概要

AgentTeam agent 运行在外部 LLM runtime 中。这些 runtime 可以在 stdout、final output 或本地 transcript 中产出 assistant 文本，但只有 agent 显式使用 AgentTeam 通信面（例如 `message_send` / `mcp__agentteam__message_send`）时，AgentTeam 对话历史才会产生持久的可见消息。

本契约要强化这条边界：

- 只有通过通信工具路径确认的输出，才是有效的可见回复；
- runtime stdout 或 final text 绝不作为普通消息自动 relay 到 AgentTeam；
- 一个 wake 在没有确认可见回复的情况下结束时，只能进入一个终态 wake status；
- prompt compiler 要向 LLM agent 明确通信契约；
- 可见回复契约的增强 prompt 和诊断行为始终启用（always-on）。

Daemon 不静默修复模型行为。LLM agent 仍然负责判断是否需要回复以及如何回复；daemon 负责让结果可观测，并保证状态转换有界、不会循环。

长任务或 context compact 后，wake 开始时注入的提示可能已经被弱模型遗忘或被压缩摘要稀释。因此 agent 还需要一个完成前的 one-shot final-check prompt：当 daemon 观察到 runtime turn 已结束、当前 wake 仍没有确认可见回复、且进程即将进入 idle 时，daemon 可以向同一 runtime session 再注入一次极短提示，要求 agent 自己判断是否还有面向 AgentTeam 参与者的结果需要通过通信工具发送。该 final-check prompt 不是自动 relay，也不携带 runtime 刚刚输出的正文；它只恢复“是否需要可见回复”的决策点。

## 2. 问题描述

AgentTeam wake 可能以一种很迷惑的方式失败：

1. 用户发送消息并唤醒 agent。
2. Agent runtime 在本地产出一段完整、结构化的 assistant 回答。
3. Runtime 结束本轮，但没有调用 `message_send` 或等价的 AgentTeam 通信工具。
4. AgentTeam 没有创建持久消息，所以用户看不到任何回答；但从 runtime 本地视角看，模型似乎已经回答了。

把 runtime 文本自动 relay 到 AgentTeam 不是可接受的修复方式。它可能发布草稿、使用错误 target，或者当 relay 出来的消息再次触发 AgentTeam 投递时造成 echo agentteam。因此，本契约必须在不把 runtime stdout 变成消息来源的前提下，消除这种静默失败。

## 3. 目标

- 明确通信边界：本地 runtime 文本不是 AgentTeam 回复。
- 确保每个 wake 最多只有一个终态结果：`reply_sent`、`failed` 或 `completed_without_visible_reply`。
- 让无回复结果可观测，并提供足够诊断信息，以区分纯文本误用和其他无回复情况。
- 强化 agent prompt 指引，使 LLM agent 在结束 turn 前执行最终自检。
- 该能力为产品默认行为，不需要改变机器或 runtime 级行为即可生效。
- 在无回复路径上不新增消息写入、fanout job、read cursor 变更或自动 wake retry。

## 4. 非目标

- 不保证所有 LLM runtime 都一定遵循 prompt。系统保证的是可观测性和有界状态转换，不是模型服从性。
- 不自动发送 runtime stdout、final text 或捕获到的 assistant text 作为 AgentTeam 消息。
- 不自动重试原始任务，也不注入携带答案正文的 daemon 侧 repair turn。
- 不把 final-check prompt 设计成隐藏修复：它必须出现在 `prompt_input` 轨迹中，且只提醒 agent 判断是否需要可见回复。
- 不为该功能增加机器级策略或本地 override。可见回复契约始终启用，不再有发布控制状态。
- 不为 runtime output 创建新的持久消息账本或服务端 receive queue。

## 5. 定义

- **Wake**：daemon runtime 收到的一个具体路由事件，通常来自某条消息 mention 或选择了某个 agent。
- **可见回复**：通过 `message_send`、对触发消息的已确认 reaction，或其他被显式建模的 AgentTeam 通信面产生的、AgentTeam 中可见的持久回复。
- **本地 runtime 文本**：LLM runtime 以 assistant text、final output、stdout 或类似本地 transcript 输出形式产出的文本。它可以出现在 runtime 诊断或轨迹中，但不是 AgentTeam 消息。
- **Wake 终态状态**：`reply_sent`、`failed` 或 `completed_without_visible_reply` 之一。一旦进入终态，同一个 wake 不得再产生另一个终态状态。
- **可见回复契约**：始终启用的产品默认行为，为 agent 提供增强通信契约 prompt 指引和无回复诊断。

## 6. 核心不变量

### 6.1 通信工具是唯一消息写入者

Daemon 不得把本地 runtime 文本转换成 `scope_messages` 记录，也不得把 channel
`Send` 当作 stdout fallback 来调用。AgentTeam 可见回复必须来自显式通信工具路径；
该路径负责 invocation routing、target parsing、freshness、限流、权限和 wake
确认。Reply-bearing delivery 按
[Turn-Target 回复路由上下文](./turn-target-reply-routing.md) 显示 exact target，
但不暴露 daemon 私有 delivery selector。`message_send` 必须显式携带 exact
`target + content`；Desktop 将 exact target 与 pending delivery 关联，未命中时按
intentional proactive send 处理。

### 6.2 Wake 终态状态必须幂等

对于每个 wake token，daemon 最多只能发出一个终态 wake status。终态发出后，进程必须清理 pending wake state，确保后续 `turn_end`、`runtime_idle`、compaction 或 stale runtime event 不能再次发出相同终态。

这条幂等性是基础安全规则，不应依赖实验开关。

### 6.3 Prompt 指引是纠偏机制

可见回复契约始终启用，prompt compiler 会加入更强的通信契约指引。指引必须要求 LLM agent 在结束 turn 前自检，并对所有用户可见回复使用可用的 AgentTeam 通信面。

Daemon 不得再增加第二套行为策略去推断回复内容，或代替模型发送回复。

### 6.4 Final-check prompt 有界且一次性

可见回复契约始终启用，daemon 可以在一个 wake 即将以无可见回复结束时注入一次 final-check prompt。该 prompt 必须满足：

- 每个普通 pending delivery 在当前 provider work 内最多注入一次；
- 不包含、复述或重放 runtime stdout / final text / assistant text；
- 不代表 daemon 代替 agent 发送消息；
- 不创建 AgentTeam 普通消息；
- 注入行为必须通过 `prompt_input` 轨迹可见；
- final-check turn 如果仍然没有确认可见回复，daemon 必须进入终态 `completed_without_visible_reply`，不得再次注入。

该设计把“最后是否回复”的判断交给 agent，同时避免无限套娃。

### 6.5 默认不在诊断中保存捕获的回复正文

无回复诊断可以记录观察到了本地 assistant 文本，也可以记录有界 metadata，例如字节数或 rune 数。诊断不得把本地文本正文作为 fallback reply 持久化或传输。如果未来诊断需要 preview，该 preview 必须明确标注为 diagnostic-only，并且不得被消息发送代码复用。

### 6.6 Context recovery 保留精确 target

非原生 compact runtime 因 context window 超限重启时，daemon 必须在原 wake 进入终态并清理前复制当前 FIFO 中的精确 target、target frontier 与有界 wake identity。原 wake 仍按正常规则逐条进入 `completed_without_visible_reply` 或其他既定终态；恢复 continuation 使用新的 deterministic synthetic message id，不能复活原 wake token。

同一次超限事件中的多条 wake 应作为一个 recovery batch 持久化，并按非空 canonical `TargetKey` 聚合：同 target 只保留最高 unresolved `TargetSeq`，不同 target 各保留一个 marker。每次 fresh runtime 只消费一个 marker，其余 target marker 留在 durable deferred delivery 中等待后续 continuation；recovery prompt 不把其他 wake target 作为第二套 routing context 投影给模型。每个 synthetic recovery 都是新的、可见回复的 exact-target delivery：需要向参与者发送结果时，agent 使用该 delivery header 中的 target。Reply-thread target 在持久化 roundtrip 中必须保留 short id，例如 `#channel:shortid` 和 `dm:@member:shortid` 不能退化成父 channel 或 DM 主线。

Recovery batch 为每个 canonical target 创建一个 synthetic wake 生命周期。它不会把原 wake 重新标记为 `reply_sent`，也不保证每个原 wake 都重新获得独立 status；原 wake 的终态与各 synthetic recovery wake 必须保持分离。重复持久化同一 target frontier 只保留一个 deterministic marker；若旧 marker 已 ReplayPending，较高 frontier 必须另存为新的 deterministic marker，不能改写已 launch marker；旧 process boundary 只结算旧 marker，较高 marker 随后通过 deferred lifecycle 启动。该幂等保护不参与 target authorization。

Dispatcher 只能使用 Process 捕获的 wake target，或当前 conversation binding 中已验证的精确 target 作为无 wake fallback。Daemon WebSocket / AgentTeam server connection id（例如本地 `srv_*` / legacy `chn_*`）不是消息 target，禁止拼接成 `#channel`。没有精确 target 或持久化失败时必须 fail closed，并发布 runtime failure evidence；不得静默猜测 `#all` 或其他 channel。

## 7. Always-on 与兼容投影

可见回复契约是产品默认行为，始终启用。不再存在账号级门控或 agent 级开关：历史 per-account / per-agent 关闭值不再作为有效配置输入，服务端不会基于其下发或响应 false，历史 DB 中残留的 `visible_reply_contract_enabled=false` 不再产生任何有效关闭效果。legacy behavior-settings DTO 固定回 enabled=true；machine catalog 与 `agent:start` 仅对仍声明 `agent:visible-reply-contract-v1` capability 的旧 desktop 固定投影 enabled=true（当前 Desktop 不声明该 capability、本地始终 always-on，server 不再下发投影）。

### 7.1 兼容投影边界

唯一需要保留的兼容边界面向仍声明 `agent:visible-reply-contract-v1` capability 的旧 desktop：

- 服务端对该类客户端的 machine catalog 与 `agent:start` config 固定投影 `visibleReplyContract.enabled=true`，忽略任何历史 DB 值。
- 已不再声明该 capability 的当前 desktop 不会收到 `visibleReplyContract` 投影；其 daemon 侧的可见回复契约行为同样始终启用，由 daemon 本地默认决定，不依赖服务端下发。

该投影不是运行边界，不参与 `runtime_policy_generation`、runtime policy fingerprint、thread contract fingerprint 或 runtime start fingerprint。

### 7.2 配置同步

由于不再有可变开关，该能力不存在配置变更同步。声明 capability 的旧 daemon 收到的 catalog 始终携带 `enabled=true`；离线旧机器在下次连接或下一次 `agent:start` 时拿到同样的固定投影。投影下发不得主动 restart / replace runtime，也不得为了应用 prompt 变更而触发 `fresh_start`。

### 7.3 UX 要求

可见回复契约已升级为默认行为，account settings 与 agent settings 均不再暴露开关。UI 不得暗示存在可关闭的设置，也不得暗示该能力能强制模型回复——它只改变 prompt 指引和诊断；模型仍然可以选择不回答。

## 8. Prompt Compiler 契约

可见回复契约始终启用，prompt compiler 必须在 wake / resume / fresh turn 的动态 turn context 中加入通信契约块，并向每次具体 delivery 添加一段简短的 delivery-tail reminder。

### 8.1 动态 turn context contract

可见回复契约属于 AgentTeam 平台通信规则，但它不是 thread contract identity。Prompt compiler 应将它渲染为 turn-only context block，使配置变更可以在下一次真实 wake / resume / fresh turn 生效，而不要求 runtime restart 或 fresh thread cutover。内容必须包含以下语义：

- Plain text、final text、runtime text 都只是本地输出，不会投递到 AgentTeam。
- 面向用户可见的 channel、DM、thread、task、review 和 completion 回复必须使用当前可用的 AgentTeam 通信工具。
- 在开始撰写任何分析结果、review 结论、完成摘要或其他可能面向用户的内容前，agent 必须先分类这段内容是否应被 AgentTeam 参与者看到。
- 如果内容面向 AgentTeam 参与者，agent 必须先选择通信工具路径，并把正文写入该工具调用；不得先把正文写成本地 final text。
- 如果不需要可见回答，应结束 turn，不产出用户可见 prose。
- Agent 不得把面向用户的回答打印成 final text 并假设 AgentTeam 会投递它。

该 block 不得进入 Codex `baseInstructions` / `developerInstructions`、OpenCode contract carrier、或任何用于判断 provider session 是否可 resume 的 thread-contract fingerprint。它可以进入 turn context fingerprint / trajectory，用于 prompt inspection 和诊断。

### 8.2 Delivery tail reminder

每次 wake delivery 的末尾应追加一段简短、包含 target 的提醒。提醒要把“是否需要可见回复”的判断前置到撰写正文之前，而不只是事后检查。语义示例：

```text
结束前先自检：在撰写任何分析结果、review 结论或完成摘要之前，先判断它是否要给 AgentTeam 参与者看。
如果需要回复这条输入，不要输出 plain/final text；请先调用可用的 AgentTeam 通信工具，使用当前输入的精确 target "<target>"，并把正文写入 content。如果明确要主动发往其他 conversation，显式提供那个 destination 的 exact target。
如果不需要可见回复，请停止，不要输出面向用户的 final answer。
```

对于当前 MCP 通信面，reminder 必须点名 runtime 具体工具名，例如 `mcp__agentteam__message_send` 或 `mcp_agentteam_message_send`。

Reminder 应保持简短，不应在每次 wake 中重复完整通信手册。
当 delivery 本身已经包含带实际工具名和 exact target 的通信指令时，该指令就是
delivery-tail reminder；不得再追加第二段等价的 final self-check。

### 8.3 完成前 final-check prompt

Daemon 在 runtime turn end 后、进程进入 idle 或 idle compaction 之前，如果发现该 wake 尚无确认可见回复，应注入一段 one-shot final-check prompt。该 prompt 不由 prompt compiler 的普通 delivery envelope 产生，但必须使用同一套工具命名规则，并为每条 unresolved wake 包含精确 target 与 message id，作为 `prompt_input` 记录。

语义示例：

```text
Final delivery check:
These wakes are about to finish without confirmed replies:
- exact target "<target>" (wake message "<message id>")
Before going idle, decide for each wake whether you have any result, answer, review, blocker, or completion summary intended for participants.
For every wake listed with an exact target that needs a reply, call <send tool> now with that target and put the user-facing content in that tool call.
Do not call the send tool for a wake marked reply unavailable.
If no reply is needed for a wake, do not produce plain/final text for it.
Do not print a user-facing answer as plain/final text; it will not be posted to the conversation.
```

对于当前 MCP 通信面，`<send tool>` 应渲染为 runtime 实际可用的 `mcp__agentteam__message_send` 或 `mcp_agentteam_message_send`。

Final-check prompt 不是 retry prompt：它不能要求 agent “重新做任务”，也不能把上一轮本地输出作为草稿交给 agent 发送。它只恢复通信决策点。

Daemon 在当前 provider work 内维护 process-generation-scoped 的 turn
message-send scope（仅 `message_send` 防误投 guard 使用）：它只记录当前
runtime-visible input 随身携带的 exact sendable destinations
（conversation target，以及消息明确携带的 task scope / workflow target），
不提供模型可选择的 logical-turn selector，也不是 MCP/Exec/Task/Extension
的通用 routing 或授权基底。ordinary `turn_end` / idle 只有在没有同-work
final-check 或 compaction continuation 时才退役该 scope，process
replacement 与 daemon restart 不恢复它。Final-check 必须保留每个
unresolved canonical target；如果 scope 已退役，后续回复应从新的
runtime-visible input 重新建立 context，不得从“最近一次 delivery”隐式选择
target。跨 scope 的主动投递必须显式声明（`allow_cross_target`）并记录审计。

### 8.4 Prompt 可见性

实际渲染出的 turn context、delivery 文本和 final-check prompt 必须继续出现在现有 `prompt_input` trajectory surface 中，便于 operator 检查 prompt 而无需新增 prompt inspection API。

## 9. Wake Runtime 契约

### 9.1 Pending delivery state

进程保留一个有界、FIFO 的 pending delivery 表。每行对应一个已经
runtime-visible 的消息，并保留终态状态所需的消息与目标身份：

```go
type PendingDelivery struct {
    MessageID     string
    Target        string
    TargetKey     string
    TargetSeq     int64
    WakeToken     string
    DeliveryClass string // visible_reply | signal_only
    Status        string // awaiting_check | final_check_sent
}
```

`DeliveryClass` 只是投递分类：合法 Workflow callback、system、reaction 使用
`signal_only`，不产生普通 visible-reply final-check；context recovery continuation
使用 persisted exact target，属于 `visible_reply`，通过普通 final-check；其原始 wake
仍保持独立终态。
普通消息按 canonical `TargetKey` 聚合检查，但每行仍独立保留
`MessageID`、`TargetSeq` 与 wake status 终态。`WakeToken` 只属于 daemon 的
传输关联和去重，不是模型可见 selector，也不是 settlement frontier。

### 9.2 事件处理

- 观察到 AgentTeam communication tool call 不改变 pending delivery 状态，也不产生
  额外的 started/injected 状态镜像。
- `message_send` 只有在服务端返回 `sent` / `already_sent` 以及 canonical
  `TargetKey + SeenUpToSeq` 时，才能按 target frontier 批量终结 covered rows。
- 对触发消息的 reaction 确认成功时，按 exact delivery 终结对应 row；reaction
  signal 自身不参加普通 final-check。
- Pending delivery 遇到 runtime error 时，发出 `failed`，清理 pending 表，并
  标记所有仍在表中的行终态。
- `turn_end` 发现有 `visible_reply` 且 `Status=awaiting_check` 的 row 时，按
  canonical target 聚合生成一次 one-shot final-check；发送成功后把这些 rows
  变为 `final_check_sent`，保持 delivery pending 并跳过本次 idle / compaction。
- 下一次 boundary 若 rows 仍为 `final_check_sent`，或 final-check 不可用 / 失败，则为
  相应消息发出 `completed_without_visible_reply`，清空它们；不得再次注入。

旧 frontier 只结清 `TargetSeq <= SeenUpToSeq` 的同 target rows。更高 seq rows
保持 pending，并在后续 boundary 获得 fresh final-check；不同 target 始终独立。

### 9.3 无回复原因分类

普通 `completed_without_visible_reply` 只携带稳定的 `reason=no_reply`（或实现
所需的 bounded runtime failure detail）；signal-only row 只携带其
`delivery_class=signal_only` 分类。本地 assistant text 和 tool-call 观察不得
改变 reason，也不得作为消息正文保存或发送。

Reason 只用于诊断。它不得改变消息投递、target freshness、read cursor 或 agent
inbox 状态。

### 9.4 终态清理

`reply_sent`、`failed` 与 `completed_without_visible_reply` 共用同一终态清理：
移除对应 pending delivery rows、释放 FIFO admission、取消 retry ownership
和无效的 runtime correlation。Signal-only rows 也只保留其简单分类到终态，
不进入普通 reply settlement。清理完成后不得由 stale turn event 重新创建
pending row。

## 10. 可观测性

### 10.1 Wake status events

现有 `agent:wake_status` 仍然是主要观测面。所有 agent 都会按上文定义增加结构化 no-reply detail。

Event payload 应包含足够上下文：

- agent id；
- server 在接受事件时解析出的 Agent 公共技术名称与展示名称；
- runtime type；
- runtime context 中已有的 model；
- wake target；
- wake status；
- no-reply reason，如存在。

高基数指标不得把 agent id 作为 metric label。Agent 级分析可以使用 event logs 或有界查询面完成。

### 10.2 Metrics

推荐聚合 counter：

- `wake_received_total{runtime}`：收到的 wake 总数。
- `wake_reply_sent_total{runtime}`：已确认可见回复的 wake 总数。
- `wake_completed_without_visible_reply_total{runtime, reason}`：无可见回复结束的 wake 总数。
- `wake_failed_total{runtime}`：失败的 wake 总数。

这些 counter 用于衡量 local-only text failure 是否减少。

### 10.3 Activity surface

无回复诊断不应产生新的普通 chat message。Browser 会话界面应在当前会话末尾的单一非消息状态区域显示活跃 wake；没有活跃 wake 时，才显示当前 scope 最新且相关的终态，不应在历史消息下重复渲染终态。`completed_without_visible_reply` 应使用“已收到消息并回到空闲”等中性状态文案，不应描述为智能体本应回复却未回复。失败详情仅在用户主动展开时显示。所有 UI copy 都应表现为 status，而不是 agent-authored text。

## 11. 实施计划

### Phase 1：终态 wake 加固

- 添加 helper：只发出一次终态 wake status，并清理 pending wake state。
- 在 `reply_sent`、`failed` 和 `completed_without_visible_reply` 中统一使用该 helper。
- 增加测试，证明无回复后的重复 `turn_end` 不会发出重复终态事件。
- 该阶段保持始终开启，因为它是状态机安全不变量。

### Phase 2：Prompt compiler always-on 渲染

- 可见回复契约始终启用；服务端对声明 `agent:visible-reply-contract-v1` capability 的旧 daemon 固定投影 `visibleReplyContract.enabled=true`，历史 per-account / per-agent 关闭值不再作为有效配置输入，服务端不会基于其下发或响应 false。
- 确保该投影不纳入 runtime policy generation / fingerprint、runtime start fingerprint 或 thread contract fingerprint。
- 始终渲染动态 turn context contract、delivery tail reminder 和 final-check 行为。
- 为 MCP 与 CLI agent-tools 渲染添加 prompt compiler snapshot 或 contains 测试。

### Phase 3：无回复诊断

- 为 pending delivery 终态发布稳定、bounded 的 no-reply reason detail。
- 不跟踪或保存本地 assistant text 正文；如果 payload 形状变化，补充 event
  mapper / browser diagnostics 测试。

### Phase 4：One-shot final-check prompt

- 在 `turn_end` 到 idle / compaction 之间增加 final-check 注入点。
- 注入成功后保持 wake pending，禁止本轮进入 idle；下一次 `turn_end` 若仍未确认回复则进入 `completed_without_visible_reply`。
- 注入失败时立即进入无回复终态，不做 retry。
- 增加测试覆盖 final-check 只注入一次、不会循环、确认回复后清理 wake，以及 stdin 不可用时回退为无回复终态。

## 12. 测试计划

- Daemon wake 状态机单元测试：
  - `reply_sent` 后重复 `turn_end` 不再产生第二个终态；
  - no-reply `turn_end` 后重复 `turn_end` 不再产生第二个终态；
  - 首次 no-reply `turn_end` 注入 final-check prompt 而不是直接终态；
  - final-check 后第二次 no-reply `turn_end` 进入终态且不再次注入；
  - reaction 确认为可见回复证据，并关闭 wake；
  - runtime error 进入 `failed`，不会再被重分类为无可见回复。
- Prompt compiler 测试：
  - 在 wake / resume / fresh turn context 始终渲染通信契约 block；
  - `visibleReplyContract.enabled` 投影不改变 thread contract fingerprint；
  - MCP runtime 的 tail reminder 点名 MCP communication tool；
  - MCP runtime 的 tail reminder 点名当前 `agentteam` namespace 通信工具；
  - tail reminder 对 reply-bearing wake 显示精确 target，并明确显式跨 target
    仍直接使用另一个 exact target；
  - 多条 unresolved delivery 按 canonical target 聚合 final-check，同时保留
    每条 message id 与 target sequence 的终态记录。
- Agent policy / launch config 测试：
  - durable config、DTO、machine catalog、runtime launch config 字段映射正确；
  - 历史存储 `visible_reply_contract_enabled=false` 时，声明 capability 的客户端投影仍为 `enabled=true`；
  - policy fingerprint、runtime start fingerprint 和 Codex instruction fingerprint 不随 `visibleReplyContract` 投影变化。
- 诊断测试：
  - 没有服务端 visible-reply confirmation 时 reason 为 `no_reply`；
  - 本地 assistant text 不会出现在 wake status 或消息正文中。

## 13. Rollout 与回滚

1. 终态 wake 幂等加固始终开启，避免重复终态事件和循环风险。
2. 可见回复契约已为产品默认行为（always-on）：历史 per-agent / per-account 关闭值不再作为有效配置输入，服务端不会基于其下发或响应 false，声明 `agent:visible-reply-contract-v1` capability 的旧 daemon 收到的投影固定为 `enabled=true`。
3. 通过 `wake_completed_without_visible_reply_total{reason="no_reply"}`、`wake_reply_sent_total` 与 `wake_failed_total` 观察无可见回复终态。
4. 该能力不再提供运行时关闭入口；若 prompt 变更需要调整，应通过独立的 prompt variant 框架迭代，而不是复活 per-agent 关闭值。终态幂等加固始终保留。

## 14. 方案取舍

| 方案 | 结论 | 原因 |
| --- | --- | --- |
| 自动 relay stdout / final text 为消息 | 拒绝 | 会发布未确认草稿、容易使用错误 target，并可能重新触发 AgentTeam 投递造成循环。 |
| Daemon 注入隐藏 repair turn | 拒绝 | 难以判断模型真实意图，会制造第二轮不可见行为，也可能和用户消息/agent 输出形成套娃。 |
| 完全只靠 wake tail prompt | 不足 | 长任务后 context 可能被 compact，tail prompt 容易被压缩摘要稀释；弱模型仍可能在最终输出阶段忘记通信工具。 |
| One-shot final-check prompt | 采用 | 在最接近 idle 的时刻恢复通信决策点；不携带答案、不自动发送、每个 wake 只允许一次，能降低不回复概率且避免循环。 |
| Prompt-first + final-check + daemon 终态诊断（always-on） | 采用 | 将回复内容和是否回复的责任留给 LLM agent，同时由 daemon 保证状态有界、可观测。 |
