# Pi / Codex 新会话能力与统一接口

本轮先检查实际安装版本、类型/源码，再运行隔离的无 prompt Pi manager 探针；Codex只生成本地schema并读固定版本源码，不连接生产daemon。结果在 [Pi evidence](evidence/pi-capabilities.json) 与 [Codex evidence](evidence/codex-capabilities.json)。

## Pi：@earendil-works/pi-coding-agent 0.85.1

安装路径：`/home/lighthouse/.nvm/versions/node/v22.22.1/lib/node_modules/@earendil-works/pi-coding-agent`。不是凭旧的 `@mariozechner` API 记忆推断。

| 问题 | 已确认答案 |
|---|---|
| 正式创建入口 | `SessionManager.create(cwd, sessionDir?, options?)`。`AgentSessionRuntime.newSession()` 是替换其已有runtime的 /new 语义，会 teardown old runtime，不适合对共享runtime直接调用 |
| cwd | 明确传注册project的canonical cwd；runtime factory也传同cwd。新服务用 `createAgentSessionServices({cwd,agentDir,...})`，再 `createAgentSessionFromServices({services,sessionManager,model?,thinkingLevel?})` |
| session_id | manager创建即 `getSessionId()`；也支持合法 `options.id`。不可从文件名猜ID |
| 不立即prompt | 可以。隔离manager探针从未创建runtime/加载extension/发prompt/访问模型 |
| idle / dormant | 活的AgentSession可IDLE；无runtime的manager不是已落盘DORMANT。`SessionManager.list`只从文件列出可发现会话 |
| 持久化 | `_persist` 在没有assistant message且尚未flushed时不写文件；appendSessionInfo、appendModelChange不触发首次落盘。探针验证创建后和添加元数据后文件均不存在、list为空 |

本地证据：`dist/core/session-manager.js:645,739,848,1206`；`agent-session-runtime.d.ts`；`agent-session-services.d.ts:28,45..48`；项目 `integrations/pi/sdk-host.mjs:factory/acquire/dispatch`。现有 `create_smoke` 只是CLI测试分支，会acquire runtime，但没有HTTP/AgentAdapter create API。

不能用 `_persist` 等内部方法硬写、伪造assistant回复、偷偷发“hello”或把 `forkFrom` 当新建来绕过持久化语义。fork是复制有历史的会话，与空白创建不同。仅memory runtime在Gateway重启后丢失，不应承诺“创建完成且以后随时恢复”。

0.6c若要“不立刻prompt、以后可用”，Gateway需要保存自己的draft（project、requested model/effort、name、operation id），首条用户已确认输入到来时重新构造runtime并交给正式SDK。Pi能重用预留ID，但必须处理原生文件已存在、并发创建和operation重试；不能用draft覆盖已有session。首次assistant持久化之前仍有失败窗口，需把accepted input与恢复状态记入operation ledger。

## Codex CLI / App Server 0.154.0

实际执行 `codex --version` → `codex-cli 0.154.0`；生成 `codex app-server generate-json-schema --experimental`，没有调用聊天工具创建任何用户侧新chat。

| 方法 | 本地schema确认 |
|---|---|
| `thread/start` | 有；接收cwd/model/ephemeral及policy等；返回 `result.thread.id`（canonical device session ID）和loaded idle thread |
| `thread/create` | 无 |
| `thread/resume` | 有；优先按threadId；可以rejoin已加载thread，不是创建新身份。不能对已有thread传另一project cwd来“新建” |
| `turn/start` | 独立方法，发送input开始生成；thread/start不要求立即prompt |
| `thread/name/set` | 有；名称独立设置 |
| `thread/settings/update` | experimental；model、effort、threadId等；需 initialize 声明experimentalApi，现有adapter已使用此能力 |

**`ephemeral=false` 也不能证明空thread已经落盘。** 为避免用生产daemon实测创建，另外读取 [rust-v0.154.0 thread_start test](https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/app-server/tests/suite/v2/thread_start.rs#L381)：返回状态Idle，分配path，但该path在第一条用户消息前不存在；[rollout recorder](https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/rollout/src/recorder.rs#L831)明确deferred creation / persist。这个结论是源码证据，不是本轮daemon重启实验。当前官方 [App Server 文档](https://learn.chatgpt.com/docs/app-server)也把thread/start和turn/start分为两步；0.154.0细节以本地schema和版本固定源码为准。

只创建后拿到ID可以在该loaded runtime内继续，但空thread crash/restart后的恢复不能按已有会话承诺。之后设置name/effort是否会改变materialization时机也需独立验证。本轮没有调用shared daemon、加载其thread或把模型凭据复制到测试目录。现有项目0.2.2报告证明的是**已完成turn**后能恢复，不是空thread durability。

## 统一接口的可行范围

两边都能提供：指定cwd、创建原生身份、无prompt idle、后续发input。两边都不能仅凭“create返回成功”保证空session持久存在；触发持久化的阶段不同（Pi等assistant，Codex测试指出首个user）。因此现在可以统一**创建流程/能力描述**，不能把两个函数直接拼成一个假定durable的 `create_session(project)`。

建议Adapter先暴露能力：

```text
SessionCreationCapabilities {
  can_start_without_input: true,
  empty_session_durable: false,
  caller_supplied_native_id: true(Pi) / false(Codex schema)
}
create_session(project, options) -> {
  native_session_id, live, persistence: "volatile" | "durable"
}
```

Gateway拥有durable operation/draft，Adapter只负责native runtime。不要在API返回“DORMANT”包装volatile句柄。原生创建用独立runtime，不变更当前会话；target selection仍需显式ACK，创建本身不自动执行用户代码或改target。

设计阶段的端点候选：

```http
POST /api/projects/{project_id}/sessions
Idempotency-Key: generated-operation-id

{"agent":"pi","name":"UI design","model":null,"effort":null}
```

无initial input时建议先返回Gateway draft：`{operation_id,draft_id,project_id,agent,state:"DRAFT",native_session_id:null,selectable:false}`，由Gateway自己的durable registry保证请求不丢。**DRAFT不是0.6b catalog v1的新增隐式状态**；0.6c需协商新capability/新UI并加入draft activation flow。

首条明确确认的输入通过 `/api/session-drafts/{draft_id}/activate` 开始原生runtime/turn，拿到实际native ID后再明确设置target。Codex不能假定能按预留UUID复原；draft_id与native_session_id必须分开。创建过程中可取消的是尚未commit的操作；客户端超时必须查operation状态。Native started但尚未durable时持续记录volatile状态，待adapter证实可恢复再转原有IDLE/DORMANT。需要至少once输入恢复策略和重复发送防护，不能只靠“重试POST”。

名称最多72 UTF-8 bytes用于device display，服务端可保留完整160字符name。model从真实catalog验证；Codex effort按模型验证；Pi SDK虽有thinkingLevel，当前Gateway尚未接入effort，先拒绝不支持的effort，不能静默忽略。runtime创建可能加载项目extension/资源，因此无prompt不等于无副作用；需要既有项目trust/权限/approval策略，不由手表开启新的allow-all策略。

## 进入0.6c前的隔离验收

使用单独profile/sessionDir、无生产credentials和daemon，tmp project全部位于测试root：

1. 两种后端分别测创建→不prompt→正常关闭与crash→重启→列出/恢复，清楚记录volatile阶段。
2. 创建后取ID/cwd必须一致；默认model/effort与请求一致，不支持配置明确422。
3. 同operation重复请求、超时ACK丢失、并发create、project删除/改授权不会制造重复native session或错误cwd。
4. 首条input从确认→accepted→assistant持久化间的失败注入；不能自动重复有工具副作用的prompt。
5. runtime idle资源限制、expiry/dispose、extension加载失败、owner socket冲突、session恢复身份不变。

本轮仅Pi manager探针是实际执行验证，Codex是schema/source验证；完整new-session endpoint和上述crash matrix保留design-only。优先完成已有session picker，对日常使用的收益更确定。
