# Chat 刷新后 Running 状态恢复计划

## Current Design

- 前端刷新后通过 `/api/workspace/{workspace_id}/messages?include_parts=false` 恢复历史。
- 后端 `WebACPClient` 在内存里累计 assistant message 和 tool parts。
- assistant message 只在 tool 完成或 prompt 终止时 flush 到 `messages_v2`。
- `include_parts=false` 会省略所有 parts；即使 DB 中有 running tool part，刷新后也不会展示。

## Risks

- 刷新时运行中的 assistant message 可能尚未写入 DB，前端只看到 user message。
- 已写入的 streaming assistant 如果没有 parts，前端也不会看到 running tool。
- 如果直接改成 `include_parts=true`，历史里已完成 tool 的大 output 会重新进入聊天列表，带来 payload 和渲染风险。

## Target Contract

- `messages_v2` 是刷新恢复运行态的 canonical source of truth。
- 后端在 assistant turn 开始时写入 `status=streaming` 的 assistant row。
- 后端在 tool start / running progress 时写入 running tool part，并通知消息订阅者。
- 历史 API 默认仍可 compact；前端刷新只额外恢复 active message 的 parts。
- 已终止 message 的大 tool output 不因为刷新恢复 running 状态而重新加载。

## Implementation Steps

1. 增加 `ACPSession.persist_assistant_message_snapshot()`，只 flush 当前 accumulator，不 reset。
2. chat stream 创建 assistant message 后立即 persist streaming row。
3. `WebACPClient.session_update()` 在 tool start 和 tool progress 更新 parts 后 persist snapshot。
4. `_db_get_messages_v2()` 增加 `include_active_parts`，只在 `status=streaming` 时带 parts。
5. 前端加载历史改为 `include_parts=false&include_active_parts=true`，并显示历史里的 streaming badge。

## Verification Checklist

- 后端测试覆盖 streaming snapshot 持久化 running tool part。
- DB helper 测试覆盖 compact history + active parts。
- 前端 `tsc --noEmit` 和 lint 通过。
