# Runtime Idle Compaction

> 规范路径: `specs/daemon/idle-context-compaction.md`
> 状态：目标行为
> 范围：runtime 在普通工作完成并进入 idle 时，检查是否需要在同一 session compact context。
> 相关规范：[AgentTeam Daemon](./daemon.md)、[Message Delivery Runtime](../message/delivery-runtime.md)、[Wake Visible Reply Contract](./wake-visible-reply-contract.md) 和 [Token Context 归因](./token-context-attribution.md)。

## 1. 目的

Idle compaction 的目标是在两次普通工作之间缩小 runtime context，而不是等到
context overflow 后再恢复失败工作。

标准流程是：

```text
普通 turn / 回复 / wake 完成
        |
        v
Workflow Step 不再 awaiting，且当前 boundary 允许检查
        |
        v
无 inbox、无 pending delivery，runtime 真正 idle
        |
        v
adapter 检查当前 context 是否需要 compact
        |
        +-- 不需要 --> 保持 idle
        |
        `-- 需要 ----> 同一 session compact --> 回到 idle
```

因此，正常完成回复和 wake 后会进入 idle-compaction **检查点**。只有 adapter
确认需要 compact 时才真正发起 compaction。

## 2. 当前实现与缺口

公共入口已经存在：

- `desktop/internal/agent/drivercontract/driver.go` 的
  `ProactiveIdleCompactionDriver`；
- `desktop/internal/agent/process_events.go` 在普通 `TurnEnd` 完成 wake、inbox 和
  delivery 处理后调用 idle-compaction hook；
- `desktop/internal/agent/workflow_compaction_gate.go` 只跟踪当前 Process
  generation 中 model-visible Workflow Step 的 compaction eligibility；
- `desktop/internal/api/exec/workflow_handler.go` 把 Server 确认的 callback
  complete/fail 和本地发起的 Run cancel 结果投影到该 gate；
- `desktop/internal/agent/process_recovery.go` 的
  `startProactiveIdleCompactionAfterTurnEnd` 负责 preflight 和启动。

OpenCode 和 Codex 都实现该接口。两者使用同一 AgentTeam-owned token threshold 与
cooldown policy，把各自的 active-context signal 映射为普通 turn 后的
same-session compaction。

Codex 的 `IdleCompactionDriver` 是另一条路径：只有 terminal
`contextWindowExceeded && willRetry=false` 先 queue，才会在 idle boundary 调用
`thread/compact/start`。这里的 idle 只是 mutation 的安全执行时机，不是“进入 idle
就检查是否需要 compact”的 policy。该 overflow recovery 与 Codex 新增的 proactive
能力共享底层 `thread/compact/start`，但保留独立 purpose、failure 和 continuation
语义。

## 3. 目标与非目标

### 3.1 目标

- 每个普通 turn 在真正进入 idle 前执行一次 cheap compaction-due check。
- 正常完成回复和 wake 后，只要没有待处理 input，就进入该检查点。
- Workflow callback awaiting 期间禁止 proactive compaction；只有 Server 确认的
  Step complete 成功才允许随后一次 idle due check。
- 需要 compact 时只修改当前 session；下一条消息继续使用同一 session。
- Compaction 期间不丢失或越过后来到达的 delivery。
- 复用现有 Process hook 与可选 driver interface，不新增 Server API、DB 或持久化
  状态机。
- Adapter 没有可靠 context signal 时返回 no-op，不猜测。

### 3.2 非目标

- 不处理 terminal context overflow、失败工作 continuation 或 retry。
- 不执行 session rotation、fresh-session fallback 或 Process replacement recovery。
- 不改变 runtime-native auto-compaction。
- 不要求所有 runtime 暴露相同格式的 context signal。
- 不保证 provider summary 保留每一个历史细节。

## 4. Idle 检查点

Process 只在以下条件全部满足时调用 `StartProactiveIdleCompaction`：

1. 当前 ordinary turn 已到达权威 `TurnEnd`。
2. 可见回复检查已完成；如果 wake 仍需 final-check，先执行 final-check turn。
3. 当前不处于 awaiting Workflow Step；若刚结束 Workflow Step，该 terminal
   outcome 明确允许本 boundary 检查。
4. Inbox 为空，且没有 queued、inflight 或 outcome-unknown runtime input。
5. Delivery gate 已持有，当前 Process 未 stopping，session ID 非空。
6. Runtime 已证明当前 session idle，且没有 compaction 或其他 mutation 在运行。

如果有待处理 input，Process 先投递 input，不为了 maintenance compaction 增加消息
延迟。Input 在 due check 之后、compaction mutation 之前到达时，也必须由同一个
delivery gate 排到 compaction 之后，不能并发写 runtime。

一次 ordinary turn 只检查一次。Compaction completion 产生的 `RuntimeIdle` 不再次
触发检查，避免 `compact -> idle -> compact` 循环。初始空 session 也不需要检查；
检查由已经增加 context 的 ordinary turn 驱动。

### 4.1 Workflow Step boundary

Workflow compaction gate 属于 `internal/agent` 的 Process policy，只限制 proactive
maintenance compaction。它不授予 callback authority，不占用 `isIdle`，不阻塞普通
消息或后续 Workflow delivery，也不改变 Workflow FIFO/recovery。

当带有有效 `WorkflowContext` 的 callback 消息真正 model-visible 时，Process 用
exact `(runId, effectId)` 激活 awaiting Step。Transport accept、queued 状态或尚未
确认的 correlated delivery 都不能提前激活。规则如下：

- Step awaiting 期间，每个 `TurnEnd` 都跳过 proactive due check；中间插入普通聊天
  turn、`workflow step pause` 或 `park` 也不会解除抑制。
- `workflow step complete` 只有在 Server 返回成功时才记录
  `completed_pending_check`；相同 idempotency receipt 的 replay 也是成功。工具结果
  必须先返回 runtime，当前 turn 再正常结束，不能在 Exec handler 内同步 compact。
- `workflow step fail`、已确认的 Run cancel，以及
  `step_not_waiting`/`step_already_resolved` 这类权威 terminal evidence 只记录
  `unsuccessful_terminal`。它们解除 Step ownership，但其对应 boundary 必须跳过
  compaction；后续一个独立、成功结束的普通 turn 恢复普通检查规则。
- validation failure、`step_not_ready`、pause、park 和 ambiguous transport
  failure 都不证明工作完成，gate 保持 awaiting。
- Terminal signal 必须匹配 exact `(runId, effectId)`；Run cancel 必须匹配 exact
  `runId`。若 Step B 已 model-visible，Step A 的迟到 complete/fail/cancel 结果不能
  清除或推进 Step B。
- Visible-reply final-check、inbox 和 pending delivery 始终优先。Final-check turn
  不消费 complete permission；若下一条 Workflow callback 先 surface，它以新的
  awaiting Step 替换旧 permission。

Desktop 只根据本 generation 实际观察到的权威结果推进 gate。若 Human/UI 在外部
取消 Run，但本地尚未收到任何权威 terminal evidence，保留 awaiting 只会保守地少做
maintenance compaction，不能 fail-open 提前改写 context。

Overflow compaction/recovery 不受该 gate 限制；被打断的失败工作仍按原有 recovery
语义处理。

## 5. 是否需要 compact

“进入 idle”决定 **何时检查**；adapter 的可靠 context signal 决定 **是否执行**。
公共 Process 不读取 token usage，也不按 runtime name 分支。

`StartProactiveIdleCompaction(ctx, sessionID) (started, err)` 保持现有语义：

- signal 不可用或未达到条件：返回 `started=false, err=nil`；
- 已在当前 session 启动 compaction：返回 `started=true`；
- pre-dispatch failure：返回 error，Process 保持普通 idle；
- mutation 结果可能已到达 runtime：返回 `started=true` 和 error；Process 保持
  non-idle/resume-unsafe，不得 blind retry，等待 exact completion 或停止 unsafe
  Process。

当前 AgentTeam-owned policy 使用共享的绝对阈值 150,000 active-context tokens 与两分钟
cooldown；`AGENTTEAM_PROACTIVE_COMPACTION_TOKENS` 可按 launch 覆盖，设为 `0` 时禁用。
Adapter 负责把 runtime 的可信 signal 映射为 active-context 长度，并在 native 或
AgentTeam compaction 成功后刷新 cooldown。阈值不能基于累计 usage；缺少可信
active-context signal 时必须 no-op。

## 6. Runtime 映射

| Runtime | Idle due signal | 当前映射 |
| --- | --- | --- |
| OpenCode | 最新 `message.updated` 的 input-context token count、共享阈值和 cooldown。 | 已实现 `ProactiveIdleCompactionDriver`；普通 turn 后可能 same-session summarize。 |
| Codex | 当前 primary thread 最新 `thread/tokenUsage/updated.last.totalTokens`、共享阈值和 cooldown。 | 已实现 `ProactiveIdleCompactionDriver`；权威 idle 时调用同一 thread 的 `thread/compact/start`。Runtime-native compaction completion 进入同一 cooldown，避免随后重复 manual compact；overflow queue 仍优先且保持独立 recovery purpose。 |
| Claude | `/compact` 是显式 manual mutation；当前 adapter 没有可信 active-context due signal。 | 暂不实现；缺少 signal 时 no-op。 |
| Kimi | 只暴露 runtime-native compaction lifecycle，没有 AgentTeam-triggered same-session compact API。 | 保持 native behavior，不模拟。 |

这张表描述的是能力事实，不要求公共层为 unsupported runtime 增加 fallback。

## 7. Compaction 完成与失败

Adapter 只有在 provider 的当前 session、generation 和本次 compaction operation
全部匹配时，才报告 completion。普通 idle、旧 generation event 或其他 turn 的
completion 不能结束当前 compaction。

成功后：

- session ID 保持不变；
- Adapter 先发一次明确的 `runtime_compacted` completion status，再发
  `runtime_idle`；Activity 因而按“Compacting context… → Context compacted.
  → Idle”持久化，不以直接进入 idle 暗示成功；
- Codex 在 exact item boundary 已观测到 before/after active-context usage 时，
  completion status 同时展示这两个 token count；usage 不完整时使用无数字的
  bounded detail，不猜测；
- Process 回到 idle；
- 不注入 continuation；
- 排队的新 input 按现有 delivery ownership 继续投递。

失败后：

- 不 rotation，不创建新 session，不 retry 已完成的普通工作；
- 记录 bounded、无正文 diagnostic；
- known pre-dispatch failure 可以直接回到 idle；
- ambiguous mutation 保持 non-idle/resume-unsafe，直到 exact boundary 或 Process
  被显式停止，不重发第二次 compact；
- Codex 的 matching proactive compaction terminal failure 标记为 maintenance
  failure，回到同一 thread 的权威 idle，不 queue overflow recovery、不注入
  continuation。

## 8. Session 连续性

Idle compaction 前后必须使用相同 provider session ID。AgentTeam 不清空 conversation
binding，也不重新生成 session-start prompt。因此 `x = 1` 后再问 `x + 5` 不会因
AgentTeam 主动换 session 而丢失。

Compaction summary 是否保留 `x = 1` 属于 provider 质量；AgentTeam 的保证是不在这条
路径中额外切换 session。

## 9. 验收条件

- 正常 turn 完成回复和 wake 后，在无 pending input 时调用一次 proactive
  idle-compaction check。
- Workflow Step awaiting（包括穿插普通 turn）时不调用 proactive due check。
- Server-confirmed complete success/replay 只在随后 quiescent `TurnEnd` 允许一次
  due check；fail/cancel 只清理 Step 并跳过对应 boundary。
- Validation、not-ready 和 ambiguous callback failure 保持 awaiting；旧 Step 的
  terminal result 不影响新的 model-visible Step。
- Final-check、inbox 和已有 delivery 优先于 maintenance compaction。
- Due signal 缺失或条件未满足时不发送 provider mutation。
- OpenCode 与 Codex 都通过同一 capability interface 和共享 threshold policy
  进入检查，不在 Process 中按 runtime name 分支。
- 成功 completion 在 idle 前产生一次 `runtime_compacted` Activity/trajectory
  记录；失败继续产生明确的 maintenance error，不得把两者都折叠成 idle。
- Compaction completion 不会再次触发 due check。
- Compaction 前后 session ID 不变，不注入 continuation，不 rotation。
- Compaction 期间到达的新 input 被排队，并在成功 completion 后投递。
- Duplicate、wrong-session、wrong-turn 和 old-generation event 不推进状态。
- Focused tests 覆盖 normal reply/wake、final-check、pending input、due/no-op、
  same-session completion、failure/ambiguous 和 no-recursive-check。
