# Turn-Target 回复路由

> 规范路径：`specs/daemon/turn-target-reply-routing.md`
>
> 状态：当前合同。
>
> 范围：同一 interactive runtime process 复用 channel、DM、reply thread 和
> task discussion 时，Daemon 如何传递 exact target，并把可见回复结算交给
> Server 的 target freshness frontier。

## 1. 摘要

Runtime outbound tools 使用扁平的显式目标字段。Daemon 不为每条消息建立
per-delivery routing context、reply selector 或 server-facing routing envelope：

```text
model-provided exact target
    -> target parser / local transport checks
    -> Server/workspace/product authorization
    -> canonical TargetKey + SeenUpToSeq settlement
```

目标是地址，不是能力凭证。Daemon 的 delivery ownership、surface epoch、重试和
restart/reconciliation 仍是本地生命周期状态；它们不向模型暴露，也不替代 Server
授权。

## 2. 核心不变量

1. `message_send` 必须提供一个可解析的 exact `target` 和完整 `content`。目标可以
   是当前消息目标，也可以是有意的 proactive destination；Daemon 不因为本地存在
   active task hint 而阻断一个合法的 channel、DM 或 thread 目标。
2. 目标解析只做 canonicalization 和格式校验。实际读写权限、workspace scope、
   task scope、Team/DM 成员关系和 Extension/App 权限由 Server 及对应产品 API
   在每次请求中检查。
3. MCP/Exec 不读取“最近一次 delivery”来推断缺省目标。Task get/update/checkpoint、
   task list/claim 和 scheduled-message 等需要地址的 Exec action 必须使用其
   typed request 的显式 locator；省略必填 locator 直接返回 validation error。
4. `BindingID`、wake token、surface epoch、delivery ownership journal 和 runtime
   correlation 只属于 Daemon 内部生命周期，不能进入模型 input、公共 DTO、history
   或 durable ownership journal。
5. Delivery retry、deferred replay、restart/recovery 只恢复 durable message body
   和 canonical target/freshness identity；重新 surface 时不恢复旧的临时授权或
   selector。
6. `message_send` 的 flat `target` + `content` wire shape、完整稳定 tool inventory、
   Pi activity/delivery reconciliation 和 stale-Idle Web 行为保持不变。

## 3. `message_send`

Schema 保持跨 Claude、Codex、Kimi 和 OpenCode 可用的 flat object：

```json
{
  "type": "object",
  "properties": {
    "target": {"type": "string"},
    "content": {"type": "string"},
    "file_paths": {"type": "array", "items": {"type": "string"}},
    "attachment_ids": {"type": "array", "items": {"type": "string"}},
    "team_invoke_team_id": {"type": "string"}
  },
  "required": ["target", "content"],
  "additionalProperties": false
}
```

Daemon 的顺序是：strict JSON decode、目标 canonicalization、attachment/path
边界和本地 rate limit，然后调用现有 API/channel send path。缺失或非法目标在
Daemon 端失败；合法目标是否可写仍由 Server/product authorization 决定。Daemon
不把 active task 或当前 delivery 当作 message-send 的 authorization source。

Server 返回的成功响应仍可携带 `TargetKey` 和 `SeenUpToSeq`。只有 `sent` 或
`already_sent` 的成功结果才可调用 target-aware settlement；`held`、失败和本地
accepted 状态不结算任何 obligation。

## 4. 可见回复结算

每条普通 visible-reply delivery 保留自己的 `MessageID`、canonical `TargetKey`、
`TargetSeq` 和 wake status。Daemon 可按 target 聚合 final-check presentation，但
不使用 wake token 或本地 accepted sequence 作为结算 frontier。

- 成功 send 的 `TargetKey + SeenUpToSeq` 结算同一 canonical target 且
  `TargetSeq <= SeenUpToSeq` 的已 surfaced obligations。
- 不同 target 独立结算。
- 旧 frontier 不能结算同 target 的更高 seq；更高 seq 会在后续边界重新触发
  fresh final-check。
- held 或失败结果不清除 obligation，并保留必要的 refresh/retry context。
- final-check 没有可发送内容时，该 delivery 只产生一次
  `completed_without_visible_reply`，释放本地状态，不循环注入。
- signal-only delivery（Workflow callback、system、reaction）只保留简单 delivery
  分类，不参加普通 visible-reply final-check。Context recovery continuation 使用
  persisted exact target 作为新的 visible-reply delivery。

Durable delivery ownership journal、去重、FIFO admission、runtime surface receipt、
重试、restart/reconciliation 和 transient recovery 继续由 `Process`、Dispatcher
及其现有 store 负责。Surface epoch 只阻止迟到的 runtime-surface acknowledgement
重新打开已结束的 delivery lifecycle。

## 5. Exec 与 Extension

Exec 仍使用 bearer-bound runtime authorization、catalog snapshot、revocation 和
Server API authorization；这些能力用于证明运行时、Agent、Server、catalog 与
request scope，不承载 conversation routing context。所有 built-in action 通过
统一 strict decode/typed handler 进入 concrete domain handler，不再运行
ambient target resolver。

Extension action wire envelope 只携带已校验的 `input`。Extension/App identity、
connection、catalog digest、provider credentials 和 action permission 由 Server
每次 authoritative preflight/execute/reauthorize 解析；模型不得提供这些内部
identity。需要写入 AgentTeam conversation 的 Extension action 必须在 action input 中
携带显式目标，并由目标 materializer/API 再次授权。

Task create 不再接受 Workflow callback 的 run/effect context 或
Step-scoped `idempotencyKey`。普通 Task 创建只使用显式 board/parent locator；
parent task 使用 canonical `parentTaskScope`，source target 如需保存 provenance
则显式提供。

## 6. 失败与安全边界

- 缺失/非法 target、partial task locator、unknown field 和 malformed JSON：在
  owning Daemon/API boundary 返回 bounded validation error。
- stale/revoked bearer、session replacement、Server/workspace/product permission
  失败：fail closed，不回退到 Agent ID、最近 delivery 或本地 active hint。
- Provider/Extension mutation 的 credential、scope、target 和 identity 检查仍在
  Server/owning service 负责；删除 Daemon conversation context 不会放宽这些检查。
- Daemon 不伪造一个新的兼容 flag 来替代已删除的 routing authorization
  state。

## 7. 验证

至少覆盖：

- channel、DM、reply thread、task scope、Team exact target；
- proactive target 在当前 runtime 有其他 delivery 时仍通过目标解析并交给 Server
  authorization；
- 缺失/非法 target 和 unknown daemon-private fields 失败；
- same-target freshness frontier 批量结算、不同 target 独立、held 不结算、旧
  frontier 不结算更高 seq；
- final-check 后无回复只完成一次；signal-only 不触发普通 final-check；
- delivery ownership/FIFO/retry/reconciliation/restart 回归；
- Extension/Exec catalog、revocation、provider permission 和 explicit target
  reauthorization 回归。

## 8. 相关规范

- [Runtime Outbound Tools](./runtime-outbound-tools.md)
- [Wake Visible Reply Contract](./wake-visible-reply-contract.md)
- [Prompt Compiler Architecture](./prompt-compiler-architecture.md)
- [Server-side Message Freshness Gate](../message/server-side-freshness-gate.md)
