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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ ci-shards/
!docs/issue/终端ID复制主机路由/**
!docs/issue/终端创建链路诊断/
!docs/issue/终端创建链路诊断/**
!docs/issue/终端对话转聊天退出判定/
!docs/issue/终端对话转聊天退出判定/**
# Paired CI timing reports and temporary baseline plugin.
.build/preflight-benchmark/
/.build/windows-native-benchmark/
Expand Down
27 changes: 27 additions & 0 deletions config/architecture-policies.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,8 @@
"docs/issue/终端ID复制主机路由/**",
"src/renderer/src/components/terminal-pane/terminal-pane-menu-copy-actions.ts",
"src/renderer/src/components/terminal-pane/use-terminal-pane-context-menu.ts",
"src/main/cli-writer-login-shell/**",
"src/main/runtime/structured-agent-cli-writers.ts",
"mobile/src/session/mobile-session-terminal-create-request.ts",
"src/wsl-guest/agent-hook-provider-normalization.ts",
"src/renderer/src/components/browser-pane/workspace-doc/HtmlDocPreview.tsx",
Expand Down Expand Up @@ -1828,6 +1830,31 @@
"message": "Change is outside terminal ID copy routing."
}
]
},
{
"id": "cli-writer-login-shell",
"description": "终端写者核查穿过 macOS login 包装检查真正的 shell,agent 退出后可在聊天界面继续同一段对话",
"rules": [
{
"id": "cli-writer-login-shell-worktree-scope",
"type": "change-scope",
"comparison": "worktree",
"whenChanged": [
"src/main/cli-writer-login-shell/**",
"docs/issue/终端对话转聊天退出判定/**"
],
"allowedPaths": [
"src/main/cli-writer-login-shell/**",
"docs/issue/终端对话转聊天退出判定/**",
"src/main/runtime/structured-agent-cli-writers.ts",
"config/fork-features.jsonc",
"config/architecture-policies.jsonc",
"docs/issue/README.md",
".gitignore"
],
"message": "Change is outside the terminal writer login-shell fix."
}
]
}
]
}
21 changes: 21 additions & 0 deletions config/fork-features.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2624,6 +2624,27 @@
"checks": [
"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/terminal-id-copy-routing src/renderer/src/components/terminal-pane/terminal-handle-copy.test.ts"
]
},
{
"id": "cli-writer-login-shell",
"title": "终端对话转聊天退出判定:macOS 登录包装下识别 agent 已退出",
"goal": "macOS 终端里的 Codex 或 Claude 退出后,能在聊天界面继续同一段对话,不再一直报「无法确认已退出」",
"journal": "docs/issue/终端对话转聊天退出判定/journal.md",
"dependsOn": ["src/shared/process-table-index.ts", "src/shared/process-table-snapshot.ts"],
"policyId": "cli-writer-login-shell",
"ownedPaths": ["src/main/cli-writer-login-shell/**", "docs/issue/终端对话转聊天退出判定/**"],
"requiredFiles": ["src/main/cli-writer-login-shell/login-wrapped-pty-shell.ts"],
"seams": [
{
"file": "src/main/runtime/structured-agent-cli-writers.ts",
"mustContain": "const root = ptyShellRow(index, rootPid)",
"why": "The terminal-writer census inspects the shell below the macOS login wrapper, not login itself (REQ-1)"
}
],
"tests": ["src/main/cli-writer-login-shell/login-wrapped-pty-shell.test.ts"],
"checks": [
"pnpm exec vitest run --config config/vitest.config.ts src/main/cli-writer-login-shell src/main/runtime/structured-agent-cli-writers.test.ts"
]
}
]
}
39 changes: 20 additions & 19 deletions docs/issue/README.md

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions docs/issue/终端对话转聊天退出判定/journal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: '终端对话转聊天退出判定'
slug: '终端对话转聊天退出判定'
status: testing
created: 2026-10-23
updated: 2026-10-23
---

# 终端对话转聊天退出判定 开发 Journal

## 1. 关键文档链接

| 类型 | 文档 | 状态 | 说明 |
| -------- | ---------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| 需求 | [requirements/requirement.md](requirements/requirement.md) | ready | REQ-1~REQ-3 |
| 交互 | - | not-required | 无 UI 变更;执行主机核查语义修复 |
| 调研 | - | not-required | 复现与根因证据在 `.docs/cli-chat-resume-ui-validation/2026-10-23/`(git 忽略),结论摘录于需求 §1 与方案 §1 |
| 方案 | [solutions/overview.md](solutions/overview.md) | approved | 核查时穿过 macOS login 包装找到 shell |
| 测试用例 | [tests/cases/终端对话转聊天退出判定测试.md](tests/cases/终端对话转聊天退出判定测试.md) | ready | TC-1~TC-5 |
| 测试记录 | [tests/runs/2026-10-23-本机与SSH真机验证.md](tests/runs/2026-10-23-本机与SSH真机验证.md) | completed | 本机修复前后对照、SSH 修复前后对照 |

## 2. 决策点记录

### D-1 在核查处穿过 login 包装,不改 rootProcessId 语义

- 日期:2026-10-23
- 背景:PTY 根进程是 `/usr/bin/login`,写者核查要求根进程是 shell。
- 最终决定:只在 `posixCliWriterShellAlone` 里用 `ptyShellRow` 取 login 唯一的子进程作为 shell,其余规则不变。
- 原因:`rootProcessId` 有多个读者;`confirmShellForegroundProcess` 放过后台任务,弱于这里的要求;关掉 login 包装会破坏 TCC 归属。
- 影响范围:`src/main/runtime/structured-agent-cli-writers.ts` 一个接缝;REQ-1、REQ-2。

#### 备选项

- 核查处穿过 login(采纳)
- provider 改报 shell pid
- 复用 `confirmShellForegroundProcess`
- 关闭 TCC login 包装

#### 关联文档与需求点

- [方案 §2、§4](solutions/overview.md)、[REQ-1/REQ-2](requirements/requirement.md)

### D-2 SSH 与远程主机保持上游行为

- 日期:2026-10-23
- 背景:上游只对本机执行的终端会话提供转聊天;远端 orcad 的终端在 macOS 上同样经过 login。
- 最终决定:不扩展远端支持,只验证修复不改变 SSH 行为。
- 原因:远端转聊天是上游未提供的新能力,需单独设计;本次只修 bug。
- 影响范围:REQ-3、TC-4。

#### 备选项

- 保持上游行为(采纳)
- 同批开放远端转聊天

#### 关联文档与需求点

- [REQ-3](requirements/requirement.md)、[方案 §3](solutions/overview.md)

### D-3 复现误改用户 Codex 配置的处理

- 日期:2026-10-23
- 背景:一轮复现脚本在 Codex 的 hook 审核提示里选了「全部信任」,以为用的是隔离副本,实际写进了真实 `~/.codex/config.toml`。
- 最终决定:用第三方工具 21:22 留下的配置备份和 Codex 的 hook 哈希算法重算出被批准的 5 个 hook,把信任状态还原到误操作前;此后脚本只选「不信任继续」,并断言真实配置修改时间不变。
- 原因:hook 信任决定第三方脚本能否在沙箱外运行,必须由用户本人决定。
- 影响范围:验证脚本与测试记录;产品代码无关。

#### 备选项

- 精确还原(采纳)
- 保留误批准

#### 关联文档与需求点

- [测试记录](tests/runs/2026-10-23-本机与SSH真机验证.md)

### D-4 这是上游缺陷,fork 补丁在上游修复后撤除

- 日期:2026-10-23
- 背景:被修的函数来自上游 #29242,`origin/main` 与 fork 完全一致,上游 macOS 用户默认都会遇到;上游迟早会改这几行,届时与 fork 接缝冲突。
- 最终决定:同样的修复建议提给 stablyai/orca(待用户确认后再提)。上游合入后,按 fork 规则 6 先在 `config/fork-features.jsonc` 删除本功能登记并在此记录,再删除 `src/main/cli-writer-login-shell/` 与接缝改动。
- 原因:上游修掉才是冲突最少的做法;fork 补丁只是在此之前让用户能用。
- 影响范围:功能登记、接缝、本 journal。

#### 备选项

- 只在 fork 修
- fork 先修并提给上游(采纳)

#### 关联文档与需求点

- [方案 §4](solutions/overview.md)

## 3. 开发记录

### 2026-10-23 复现、修复与真机验证

- 本轮目标:查清「终端 Codex 切不到聊天界面、一直报无法确认已退出」并按 fork 流程修复。
- 完成内容:隔离实例在 Movo 复现(开启 login 包装时 Codex 退出后仍被拒绝,关闭包装时可恢复);新增 `ptyShellRow` 并在上游核查处接入;补回归测试;登记 fork 功能、策略与文档。
- 代码或文档变更:新增 `src/main/cli-writer-login-shell/login-wrapped-pty-shell.ts` 与测试;修改 `src/main/runtime/structured-agent-cli-writers.ts`(接缝 3 行);`config/fork-features.jsonc`、`config/architecture-policies.jsonc`、`.gitignore`;本目录文档。
- 验证证据:TC-1~TC-5 全部通过,详见[测试记录](tests/runs/2026-10-23-本机与SSH真机验证.md);`pnpm tc` 通过;反向对照证实测试能抓到该缺陷。
- 未解决问题:Codex 运行中点窗格按钮必然失败、退出后按钮又消失的交互问题未处理(需求 Out of Scope);`run-terminal-shell-alone.ts` 中相近写法未验证。
- 下一步:提交 PR 合入 `fork/integration`,出包后在正式应用中复验。

### 2026-10-23 独立代码审查

- 本轮目标:请独立审查确认正确性、可复用的上游实现与上游冲突面。
- 完成内容:审查结论为可合,上游改动已是 fork 内最小(1 个文件 3 行);确认 `confirmShellForegroundProcess` 与 `inspectSpawnFileChildProcessesFromRows` 都不能直接复用;补上「login 包装下 shell 有后台或挂起任务时拒绝」以及窗格入口的断言;方案补充第二个被否决的备选;记录 D-4 撤除条件。
- 代码或文档变更:`login-wrapped-pty-shell.test.ts`;本目录方案与 journal。
- 验证证据:功能 checks 2 个文件 5 用例通过(新增的后台、挂起任务与窗格入口断言均通过);`check:fork-features`、`check:fork-docs` 通过。
- 未解决问题:向上游提交修复需用户确认;`run-terminal-shell-alone.ts` 同类写法未处理。
- 下一步:用户确认后向 stablyai/orca 提交同样的修复。
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: '终端对话转聊天退出判定'
document_type: requirement
status: ready
created: 2026-10-23
updated: 2026-10-23
---

# 终端对话转聊天退出判定

## 1. 背景与目标

终端里跑着 Codex 或 Claude 时,用户可以把同一段对话转到聊天界面继续(窗格标题栏的「在聊天界面中继续」,或 Session History 的「在新聊天中恢复」)。为防止两个进程同时写同一段对话,执行主机会先核查终端里已经没有能写这段对话的程序:shell 回到前台,且一个任务都不剩(前台、后台、挂起都算)。这个核查来自上游 #29242(2026-10-19)。

2026-10-23 用户在 Movo 工作区反馈「一直报错,切不了 chat view」,提示是「Orca can't confirm Codex has exited in its terminal…」。复现结论:macOS 上 Orca 通过 `/usr/bin/login` 包装启动每个终端(TCC 隐私授权归属),终端的根进程是 `login`,shell 是它的子进程;核查直接要求根进程是 shell,拿到 `login` 就永远不通过。所以 **Codex 退出后照样被拒绝**,只有关掉整个终端才行。e2e 测试统一关掉了这层包装(`ORCA_DISABLE_MACOS_LOGIN_SHELL=1`),所以测试一直没发现。

目标:agent 真正退出后,能在聊天界面继续同一段对话;agent 还在运行或终端里还有任务时,保护照旧。

## 2. 用户场景

用户在 macOS 的 Orca 里开终端运行 Codex,聊了几轮后想换到聊天界面。Codex 运行中点切换,Orca 提示先停掉终端里的程序;用户退出 Codex,shell 回到提示符,再从 Session History 选「在新聊天中恢复」,聊天界面打开并载入原对话。

## 3. 范围

### In Scope

- 执行主机核查本机终端是否「只剩 shell」时,穿过 macOS `login` 包装找到真正的 shell 再检查。
- 回归测试:login 包装下 agent 运行中拒绝、退出后放行、login 下多出其他子进程时拒绝。

### Out of Scope

- 「Codex 运行中点按钮却一定失败」的交互问题(例如点按钮时代为退出 agent):属于新交互,需单独设计。
- 远程主机和 SSH 工作区支持转聊天:上游只对本机执行的终端会话提供这个动作,本需求不改变。
- `src/main/runtime/run-terminal-shell-alone.ts` 中写法相近的另一处检查:它先走 provider 的前台确认,用户可见影响未证实,不在本次范围。

## 4. 需求点

### REQ-1 agent 退出后可以转到聊天界面

- 目标级别:P0;当前状态:已实现(2026-10-23 单测与本机真机验证通过,见 TC-1、TC-2、TC-3)。
- 描述:终端经 macOS `login` 包装启动,agent 已退出、shell 在前台且没有任何任务时,从窗格按钮或 Session History 转到聊天界面不得被拒绝,聊天打开后载入原对话。核查在运行这个终端的机器上执行。
- 本机终端:按上述规则放行。
- SSH 工作区与远程主机终端:客户端不提供这个动作,行为与修复前相同(见 REQ-3)。

### REQ-2 保护不放松

- 目标级别:P0;当前状态:已实现(2026-10-23 单测与上游回归套件通过,见 TC-1、TC-3、TC-5)。
- 描述:agent 仍在运行,或 shell 下还有任何任务(前台、后台、挂起),或 `login` 下除 shell 外还有其他子进程时,仍然拒绝。终端根进程本身就是 shell 的情况(Linux、关闭包装的 macOS)规则不变;Windows 走单独的分支,不受影响。
- 本机终端:如上。
- SSH 工作区与远程主机终端:不涉及(见 REQ-3)。

### REQ-3 SSH 工作区与远程主机行为不变

- 目标级别:P1;当前状态:已实现(2026-10-23 隔离 SSH 真机修复前后对照一致,见 TC-4)。
- 描述:SSH 工作区(由远端 orcad 执行)和配对远程主机上的终端会话,窗格上不出现「在聊天界面中继续」,Session History 不出现「在新聊天中恢复」,修复前后一致。修复只改变执行主机检查自己本机终端的方式,不改任何客户端与主机之间的协议。
Loading
Loading