Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/peri-e2e.rev
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b20d44118b1fc626d3a18b168f6f4821ccbc0280
acd7f33c259d8f9503e2c38d82cd15f4a96cef5d
22 changes: 20 additions & 2 deletions docs/code-index/peri-acp.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# peri-acp 代码索引

更新:2026-11-04
更新:2026-11-12

子会话只读历史入口是 `session/history/child` 与 `session/history/child/body`,实现见 `host/history_window/{child,child_display}.rs`。父会话只可读取 canonical 父链可达的后代 own 历史(最多 64 跳、检测环),不装载 child runtime;每个父会话只保留一个子历史 scope。分页与正文同时校验父结构代次、child 身份、固定尾部、snapshot、read ticket 与受众;正文投影为有界 `display_text`,不向客户端暴露 ProviderState、签名或未知内部字段。未知格式显式失败,随机正文偏移需从头扫描。回归见 `host/child_history_test.rs`、`child_display_test.rs`。

Expand Down Expand Up @@ -65,6 +65,24 @@ workspace 资源输入从本次选中 `ConfigSource` 的资源投影取关闭位
## 速查表


连接级会话切换门位于 `host/connection.rs` 的 `ConnectionContext::reserve_session_setup_for` 与
`SessionSetupReservation`:`server_loop.rs::dispatch_request` 在 history 只读分类返回之后、MCP
声明校验和环境装配之前取得同步 reservation,setup 成功响应发送后提交当前 ID;匹配 ID 的
close/delete 成功响应发送后释放。拒绝使用 `-32010` 与稳定 `data.reason`。通知分发已按职责移到
`host/server_loop/notification.rs`。进入会话恢复仅在 partition 含 working child 时由
`host/recovery/coordinator.rs` 读取整棵 session tree;内部 child 装配不经过 wire gate。外部集成
必须在切换前自行 `session/close`/`delete` 当前会话;Peri 不自动回收旧会话,未按约定关闭的
资源驻留或泄露由客户端承担,边界见 `docs/design/peri-acp-protocol.md` §2.1。
Cold restore publication 的唯一权威在 `host/requests/session_restore/load_shared.rs`:
`prepare_shared` 返回 `RestorePublication(session_id, session_generation)`;load 在同一 handler 内完成
replay、响应与 initial cursor,失败按 generation 标记 closing 并排空;fork 在 target 创建前清理
request-owned cold source,preexisting source 不受影响。`rollback_restore_publication` 仅在 manager 与
environment 都确认 drain 后删除 state;Incomplete 保留 owner,返回
`session_cleanup_incomplete + sessionId`,由显式 close 重试。该错误响应成功发送后,
`server_loop.rs::cleanup_incomplete_session_id` 把 Preparing reservation 提交为 cleanup owner,阻止同
连接 setup 其他 ID;close owner 成功响应沿既有 `release_current_session` 释放。错误响应发送失败
不提交,仍归 transport terminal 与 host shutdown;restore 层不复制第二套响应写失败清理。

ACP 出站错误诊断统一位于 `src/host/diagnostics.rs`:`ServerLoop` 的普通请求、prompt、MCP Apps、MCP over ACP、准入拒绝及生命周期锁失败均经 `ResponseDiagnostics::send` 记录方法、RPC ID、可用会话 ID、错误码与错误消息,再原样发送响应;内部错误为 ERROR,其余拒绝为 WARN,正常取消为 DEBUG。`notify.rs` 的会话信息、配置项和命令列表通知共用 `send_session_update` 记录投递失败,不记录请求参数、响应数据或通知内容。针对性回归:`./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- host::diagnostics`。

Session ID 恢复与生命周期回归:`src/host/requests_workspace_cases_test.rs` 验证
Expand Down Expand Up @@ -221,7 +239,7 @@ Session ID 恢复与生命周期回归:`src/host/requests_workspace_cases_test
| Host 任务所有权 | host/task_scope.rs | `HostTaskOwner` / `HostTaskSpawner`;生产 timeout driver + 测试 controlled phase driver |
| 装配 | `host/assemble.rs` | `assemble_server_config`;`build_legacy_frozen_data` 按保存目录准备配置与插件输入;新建 MCP pool 在初始化前绑定 `ConfigSource` snapshot,host Langfuse 消费同快照的 observability;观测成功时安装指标出口,未配置时不安装、指标不落盘 |
| stage 构建 | host/stage_builder.rs | `build_stage_context`:消费单一 `FrozenSessionData`,派生 frozen language/MetaHarness/date 与 Agent 装配输入,禁止从当轮 config 建第二事实源;frozen 中的项目指令正文来自 P4 内容准入期经 builtin `workspace` 实例读取的 `peri-instruction://workspace/{main|local}` 资源(`AgentsMdMiddleware` 为纯 adapter、不读盘) |
| workflow 宿主面 | host/workflow_agent.rs + host/workflow_consumer.rs + host/requests/workflow.rs | 本地运行时路径(`create_session_workflow_chain` :235 经 `WorkflowAgentFactory` 端口装配 executor/runtime 链、`WorkflowTaskConsumer` 就绪屏障 + 订阅 `workflow://tasks`、`workflow_face_closed` 关闭集特例、`AcpServerConfig::workflow_middleware_factory`)按 `not(emscripten)` 门控:wasm 产物不打包本地 workflow 运行时,该目标不建链、不绑定;ACP 四 handler(`host/requests/workflow.rs`)经**会话池协议面**调用实例 `Workflow`/`WorkflowStatus`/`WorkflowKill`,不随目标门控(远端通路保留) |
| workflow 宿主面 | host/workflow_agent.rs + host/workflow_consumer.rs + host/requests/workflow.rs | 本地运行时路径(`create_session_workflow_chain` 经 `WorkflowAgentFactory` 端口装配 executor/runtime 链、`WorkflowTaskConsumer` 就绪屏障 + 订阅 `workflow://tasks`、`workflow_face_closed` 关闭集特例、`AcpServerConfig::workflow_middleware_factory`)按 `not(emscripten)` 门控:wasm 产物不打包本地 workflow 运行时,该目标不建链、不绑定;ACP 四 handler(`host/requests/workflow.rs`)经**会话池协议面**调用实例 `Workflow`/`WorkflowStatus`/`WorkflowKill`,不随目标门控(远端通路保留) |
| stdio 部署 | host/stdio/ | `run_acp_stdio`(`StdioInput` → `assemble_stdio_config` → `run_acp_server_with_sessions`,业务处理走统一宿主);集成测试 `run_server_integration_test.rs`(initialize → session/new → 通知 wire 链路) |

### src/agent/(装配面薄壳)
Expand Down
2 changes: 1 addition & 1 deletion docs/code-index/peri-tui.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Live 工具卡归属:`kit/acp_events/live_tool_links.rs` 按 `active_session_i

嵌入式 TUI 与 print 由 `src/launch.rs`、`src/cli_print.rs` 创建普通 ACP host/client pump,
不启动 Bun SDK sidecar,不依赖持久 execution registry 或 reverse admission。
history 加载保留原 session load、事件重放和 load reservation;不恢复旧执行。`session/load` 响应中的 `_meta["peri.recovery"]` 只投影为通知(`acp_client/client/recovery_view.rs`);恢复不是完全成功时保留目标会话与历史,不写粘滞 `restore_error`。启动 `-r` 失败保留目标,下一次 `ensure_session` 先重试同一会话;`/clear` 或交互式切换清除该意图。服务端 `-32017` 是确定性拒绝,原稿恢复为待发且不自动重试。
history 加载保留原 session load、事件重放和 load reservation;不恢复旧执行。`session/load` 响应中的 `_meta["peri.recovery"]` 只投影为通知(`acp_client/client/recovery_view.rs`);恢复不是完全成功时保留目标会话与历史,不写粘滞 `restore_error`。启动 `-r` 失败保留目标,下一次 `ensure_session` 先重试同一会话;`/clear` 或交互式切换清除该意图。服务端 `-32017` 是确定性拒绝,原稿恢复为待发且不自动重试。`session/load` 返回 `session_cleanup_incomplete` 时,连接已被服务端绑定到该 owner;`acp_client/client/session/transition.rs` 记录同一 `sessionId` 为待关闭目标,下一次建立/切换(含同 ID 重载)先 `session/close` 再继续,回归 `cleanup_incomplete_load_remembers_owner_for_next_switch` 与 `cleanup_incomplete_same_id_retry_closes_owner_first`。

当前进程输入撤回入口 `src/kit/steer_state.rs` 以原 snapshot 与 pending command 共同校验:
Queued 与未 claim 的 Dispatching 可以请求权威 withdraw receipt;Claimed 不可以。
Expand Down
24 changes: 24 additions & 0 deletions docs/design/peri-acp-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,30 @@ TUI 的所有主动行为通过标准 ACP JSON-RPC 方法调用。不定义自
| `session/list` | `{ cwd? }` | `{ sessions: SessionInfo[] }` | 列出会话(可按 cwd 过滤) |
| `session/rename` | `{ sessionId, title }` | `{ sessionId, title }` | 重命名会话并持久化标题 |

同一 ACP 连接默认表示客户端的会话切换关系,并且至多绑定一个 setup 会话。连接已有当前
会话时,`session/new|fork` 与指向不同 ID 的 `session/load|resume` 返回 `-32010`、
`data.reason="session_switch_requires_close"`;客户端必须先成功 `session/close` 或
`session/delete` 当前 ID。同 ID load/resume 保持允许并复用现有环境。并发 setup 在首个请求
Preparing 时返回 `-32010`、`data.reason="session_setup_in_progress"`。正常绑定只在 setup 成功
响应发送后提交,且只在匹配当前 ID 的 close/delete 成功响应发送后释放。唯一错误响应例外是
`-32010` + `data.reason="session_cleanup_incomplete"` + `sessionId`:响应发送成功后,连接绑定到该
cleanup owner,其他 ID setup 继续返回 `session_switch_requires_close`;客户端须成功
`session/close(owner)` 才释放。错误响应发送失败不绑定,仍由 transport terminal shutdown 收口。
带 cursor/history 的 `session/load`、`session/history/body` 与 child history 是
只读浏览,不建立或切换连接绑定;恢复协调内部装配 child 也不经过该 wire gate。

Cold load 与 fork 临时装配 source 时,Host 以 `(sessionId, sessionGeneration)` 记录本请求实际
发布的环境。load 的 replay、响应装配或 initial cursor 失败,以及 fork 的成功/失败收尾,只能
清理该 generation;已存在 source 或后来替换的新 generation 不受影响。清理先把匹配 state 标为
closing 并保留 manager/environment owner,二者都确认排空后才删除;未确认时返回 `-32010`、
`data.reason="session_cleanup_incomplete"` 与可操作的 `sessionId`,该会话拒绝再次 load,客户端可
用 `session/close` 重试收口。响应写失败仍按 transport terminal 与 host shutdown 契约统一排空,
不由自定义 transport 测试定义第二套资源清理语义。

客户端必须在切换前调用 Peri 的 `session/close`(或 `session/delete`)并等待成功响应。未按此
约定关闭时 setup 会被拒绝,因此不会创建新的会话环境;Peri 不自动关闭、LRU 淘汰或池化旧
会话,未按约定使用导致的旧会话资源驻留或泄露不由 Peri 承担。

会话级 `mcpServers` 条目可通过 `_meta["peri.subscriptions"]` 声明 `subscriptions/listen`,
形状与 `.mcp.json` 的 `subscriptions` 相同;HTTP 与 stdio 生效,非法值返回 `-32602`,
`type: "acp"` 暂不支持。
Expand Down
6 changes: 6 additions & 0 deletions docs/standards/architecture-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@
- **Rule**:普通 thread load 必须在 producer 的同步入队边界取得引用计数 reservation,并持有到 `session/load` 提交或请求被丢弃;`ensure_session` 与所有 prompt 入口在选择 Stable session / 打开 prompt lease 前等待 reservation 清零。等待不得持有 lifecycle operation gate;Stable 决策或 `open_prompt` 必须在 reservation mutex 下线性化。send failure 不得把 guard 暴露给调用方,shutdown 必须能取消 in-flight load 并释放 reservation。compact replay 必须先 reserve load,再 drain buffered input,禁止依赖异步 consumer 调度顺序。
- **Verify**:`cargo test -p peri-tui --lib load_reservation`、`cargo test -p peri-tui --lib test_failed_load_dispatch_releases_reservation`、`cargo test -p peri-tui --lib test_shutdown_cancels_inflight_load_and_releases_reservation`、`cargo test -p peri-tui --lib test_compact_turndone_reload`;人工检查 `ThreadLoadDispatcher::send`、`AcpTuiClient::open_prompt_after_session_loads` 与 consumer drop/cancel 分支。

### ARC-ACP-SESSION-SWITCH-001

- **Scope**:ACP connection、会话 setup/close/delete 与进入会话 recovery。
- **Rule**:同一 ACP 连接默认表达会话切换关系,至多绑定一个由客户端 setup 的当前会话。`session/new|fork` 在已有当前会话时拒绝;`session/load|resume` 仅允许同 ID 复用,切换不同 ID 必须先成功 `session/close` 或 `session/delete` 当前 ID。setup 在只读 history 分流之后、声明校验与环境装配之前取得连接级同步 reservation;Vacant 只能有一个 Preparing,同 ID 复用不进入 Preparing,锁不得跨 await。setup 错误、取消、普通响应后处理错误与响应发送失败默认不提交绑定;唯一例外是 `session_cleanup_incomplete` 错误响应成功发送后,把仍持有 retry owner 的 `sessionId` 提交为当前会话,强制客户端先显式 close;该错误响应发送失败仍由 reservation Drop 回滚并交 transport terminal shutdown 收口。`prepare_shared` 以 `(session_id, session_generation)` 标识 request-owned cold publication,load 的 replay/响应/cursor 后处理与 fork 的 cold source 生命周期均由 restore handler 就近清理,旧 generation 不得删除新 state。普通错误清理先把匹配 state 标为 closing 并保留 owner,只有 manager 与 environment 都确认排空后才删除;未完成返回 `-32010`、`data.reason = "session_cleanup_incomplete"` 与 `sessionId`,拒绝 reload,并允许显式 close 重试。响应写失败由 transport terminal shutdown 收口。正常 setup 只有成功响应发送后才提交当前 ID;cleanup-incomplete 只在错误响应成功发送后提交 cleanup owner。只有匹配当前 ID 的 close/delete 成功响应发送后释放;关闭失败、发送失败或关闭其他 ID 均保留绑定。切换拒绝固定使用 `-32010` 与 `data.reason = "session_switch_requires_close"`,并发 setup 使用 `data.reason = "session_setup_in_progress"`。cursor/history window/body/child history 是只读浏览,不建立或改变绑定;内部 recovery child 不经过 wire gate。恢复 partition 只有存在 working child 时才读取 session tree。客户端必须在切换前自行成功关闭当前会话;未按约定关闭而产生的旧会话资源驻留或泄露由客户端集成负责,Peri 不提供自动 close、LRU 或 MCP 池化兜底。
- **Verify**:`./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- session_switch`、`./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- connection`、`./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- recovery_wire`、`./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- history_page_wire` 与 `./scripts/cargo-rmcp-patched.sh test --locked -p peri-acp --lib -- close_failure`;过滤结果必须命中非零目标用例。

### ARC-EVENT-001

- **Scope**:v2 事件(`peri-acp-types/src/event_v2.rs`)、v1 `ExecutorEvent` 协议化载体、ACP 映射、TUI 通知。
Expand Down
Loading
Loading