fix(desktop): 工作目录缺失时恢复目录并继续对话 - #4060
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
| Filename | Overview |
|---|---|
| apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts | 新增普通本地目录重建及一次性模型提示状态;该状态缺少会话生命周期清理。 |
| apps/desktop/src/main/maker-ipc/makerSendTransaction.ts | 为存活会话增加 DB cwd 重建,并在 wire payload 中注入和按 accepted 语义消费恢复说明。 |
| apps/desktop/src/main/maker-ipc/register.ts | 接入目录恢复服务并区分托管 worktree、远端会话和普通本地目录,但未将恢复状态接入关闭清理。 |
| apps/desktop/src/main/maker-ipc/tests/workingDirectoryRecovery.test.ts | 覆盖 ENOENT 重建、非目录路径、权限错误和提示消费,但缺少会话关闭清理场景。 |
| apps/desktop/src/main/maker-ipc/tests/makerSendTransaction.test.ts | 覆盖 DB 工作目录兜底、历史恢复、SSH 排除以及恢复提示的 accepted 消费语义。 |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[发送消息] --> B{当前 runtime 的 cwd 存在?}
B -->|是| G[发送给 provider]
B -->|否且 DB 有不同目录| C[验证 DB 工作目录]
C -->|有效| D[从 DB 对齐模型和原生会话 ID]
D --> E[关闭旧 runtime 并用 DB cwd 重建]
E --> G
C -->|无效| H[返回 WORKDIR_MISSING]
B -->|普通本地目录且 ENOENT| F[原址递归重建目录并记录恢复说明]
F --> G
G --> I{provider accepted?}
I -->|是| J[消费恢复说明]
I -->|否| K[保留说明供重试]
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts:8
`pending` 仅在消息被 provider 接受后通过 `consume` 删除;会话的 close、clear、delete 和 runtime replacement 流程都不会清除此状态。如果目录重建后的消息未被接受,而会话随后关闭,该记录会一直留在模块级 Map 中,长期运行时会随不同 sessionId 持续增长;同 ID 的新实例还可能收到包含旧目录的恢复说明。建议提供按 sessionId 清理的方法,并接入现有的会话清理生命周期。
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(desktop): 工作目录缺失时恢复目录并继续对话" | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c299f0ff96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@dashhuang 👋 这个 PR 还有 3 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts / apps/desktop/src/main/maker-ipc/makerSendTransaction.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 28d828c712
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 68b8938356
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d503f87d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 29fcd143a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
本轮收敛检查点(对应原生命令、恢复选项与目录别名):
没有新增持久化、独立注册表或重试系统。挂载盘断开时的产品行为仍待需求方确认,本轮没有改变该边界。 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa9607ed78
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@dashhuang 👋 这个 PR 还有 3 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts / apps/desktop/src/main/maker-ipc/makerSendTransaction.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
|
启动配置恢复族的收敛检查点(16b10d023):
无新增注册表、持久化、锁或重试机制。挂载盘断开时的产品行为仍待需求方确认,本轮不改变该边界。 |
|
@dashhuang 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
|
挂载不可用恢复的收敛检查点:
测试覆盖设备消失/底层替身目录、同卷子目录删除、I/O 与超时错误、分配期间清理、/Volumes 根与系统盘别名、三种引擎改 cwd 后再发送、真实 Maker 对旧 DB 行的运行目录投影。验证结果以 PR 正文为准;未进行真实磁盘拔插或 SMB 断线端到端测试。 |
MagicLizi
left a comment
There was a problem hiding this comment.
独立审查未通过(P1×2)。当前 head e139f365c0ea2b9245f0ba06effa7ae6ea974e37 不能合。
-
[P1] apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts:112 / apps/desktop/src/main/maker-ipc/register.ts:1054 —
stat已改走有界 utility-process,但原址恢复的mkdir(..., { recursive: true })仍在 Electron main 里直接fsp.mkdir。探测得到 ENOENT 的网络盘/UNC 父路径仍可能在 mkdir 上卡住 libuv,主进程无法取消。请把 mkdir 也走可杀进程/超时,超时则走现有 fallback。 -
[P1] apps/desktop/src/main/maker-ipc/makerSendTransaction.ts:686 → bootstrapSession → prepareDirectoryGrantsForBootstrap — 文件系统不可用时本 PR 会强制
reason: 'workdir'重建 runtime。prepareDirectoryGrantsForBootstrap用新 cwd 再 stat extra/writable;挂载上的授权目录会变成 not-exist,随后 persist 把 SQLite 里的 extra_dirs/writable_dirs 写空。盘回来也不会恢复。unavailable/fallback 重建时不要把这次失败当成授权撤销。
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…oundaries Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…cwd recovery Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
… is unavailable Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
e139f36 to
e967fea
Compare
|
针对 #4060 (review) 的两项 P1,已在
对称路径:成功→原址说明;stat/诊断/mkdir 不可用或超时→既有 fallback;普通权限错误→原失败;worker 超时→终止、忽略迟到响应、确认 exit 后释放槽;cleanup 先完成→迟到 IO 不重建内存路由;fallback note 消费→保留路由与授权保护;显式换目录→丢弃原路由。fallback 目录自身不可用仍按既有失败处理,不递归创建 fallback。 回归覆盖 mkdir 超时/不可用/权限错误、不同操作不共用 stat 结果、worker 迟到响应与后续正常探测、真实目录创建与错误、fallback 授权过滤/恢复,以及只读或可写别名解析超时。Main–utility 协议仅内部使用,不新增 Renderer/Device Link 能力。没有新增持久化、挂载清单、锁或重试系统。 验证:同步主干后,根 |
MagicLizi
left a comment
There was a problem hiding this comment.
Code Review — PR #4060: fix(desktop): 工作目录缺失时恢复目录并继续对话
Tier: heavy | Head: e967fea | Reviewer: magiclizi (auto-review)
Summary
This PR adds working directory recovery for ordinary local directories. When a conversation's working directory is missing (deleted or filesystem unavailable), Cindy now recreates it in-place (for normal ENOENT) or falls back to a temporary dialogue workspace (for unavailable filesystems). A one-time recovery note is injected into the wire message to inform the AI, without modifying the user display or system prompt.
Architecture
- workingDirectoryRecovery.ts (164 lines): Stateless per-session Map tracking recovery state bound to
(sessionId, workingDir). Proper lifecycle cleanup viadiscard(session close),clear(owner boundary), andconsume(message accepted). Notes are compare-and-set to prevent stale consumption. - Bounded IO: All filesystem operations (
stat,mkdir,realpath,readdir) run in the existing utility-process pool with 5s timeouts. No new worker or retry mechanism. - Grant protection: Fallback bootstrap uses injectable
statDirectory/realpathDirectorywithpreservePersistedGrantsflag. When canonicalization times out, conservatively returns empty grants. Never writes temporary unavailability back to persistentextra_dirs/writable_dirs. - Native runtime refresh: After same-path recovery, Claude Code/Pi sessions are rehydrated through the existing
rehydrateActiveSessionpath. Bot resources are pre-checked before closing the old instance. - Startup preferences:
hostStartupPreferences(userPrompt, makerMemoryEnabled, displayReasoning) are captured once at Session creation as a frozen readonly snapshot, surviving recovery without new DB fields.
Security & Safety (heavy tier)
- No credentials, tokens, or PII in diff — stage 1 security scan passed clean (0 hard/0 soft hits).
- No new IPC channels exposed to Renderer or Device Link. The utility-process protocol extensions (
mkdir,realpath,similar) are internal Main<->utility data; resolved paths never reach controllers. - No new dependencies added.
- No data exfiltration vectors — recovery module operates only on local filesystem paths.
- Conservative grant handling — timeout or unavailability results in empty grants, never expanded permissions.
- Dependency audit: N/A (no new deps). Semgrep: not executed (no base SHA provided to scope scan to new code only).
Findings
0 P0, 0 P1 found.
The implementation is well-structured with proper boundary handling:
- Managed Git worktrees are excluded from recovery (they keep their own restore path).
- SSH sessions skip local directory fallback.
- Permission errors (EACCES) and non-directory paths (ENOTDIR) are not treated as ENOENT.
- TOCTOU between stat and mkdir is handled: mkdir failure returns false (no false recovery claim).
- Cleanup races (discard/clear during pending IO) are guarded by identity checks on the Map entry.
- Pi slash commands and Claude Code
/compactbypass the note prepend. - Session
workDirnow reports actual runtime cwd, not stale DB value — tested explicitly.
Test Coverage
- 320 new lines in
workingDirectoryRecovery.test.tscovering real filesystem operations, concurrent IO, lifecycle cleanup, alias resolution, mount unavailability, and fallback. - 259 new lines in
makerSendTransaction.test.tscovering DB fallback, same-path refresh, Pi/Claude command bypass, note acceptance semantics, Bot preflight failure/retry, and SSH exclusion. - Additional tests for grant protection, utility-process operations, and maker-core startup preferences.
Verification
pnpm test:unit:related— author reports pass (Desktop, maker-core, lizi-mcps, orca-workflow).pnpm --filter desktop run typecheck— author reports pass.- CI (
client-ci) — green on current head. - Security scan (stage 1): pass. Dependency audit: N/A. Semgrep: not executed (no base SHA; not a scan pass claim).
- No client E2E or real disk-unplug testing; explicitly documented as out of scope.
Bot Settle Notice
copilot-pull-request-reviewer and chatgpt-codex-connector timed out (433+ min > 20 min limit). Their opinions may arrive after merge.
Conclusion
Pass — 0 P0/P1 findings. Clean review with comprehensive test coverage, conservative security posture, and proper lifecycle management. Ready for approve and merge.
…d-harness Keep grok-build as AgentKind in checkWorkDirExists while taking makecindy#4060 working-directory recovery. Signed-off-by: Bill Yuchen Lin <10104354+yuchenlin@users.noreply.github.com>
这次改了什么
摘要
普通本地工作目录被删除后,发送消息会被
WORKDIR_MISSING拦住。现在在原位置重建缺失目录,让原任务继续接收消息,无需先选择文件夹。发给 AI 的消息会附带一次环境说明:仅重建了目录,原文件尚未恢复;是否查找文件、恢复项目或询问用户由 AI 根据当前请求决定。同时补齐存活任务的旧目录兜底:数据库已保存有效的新目录时,按该目录恢复原生会话,保留原生历史后再发送。
同路径重建后,Claude Code / Pi 也沿现有恢复路径刷新原生进程,避免继续使用指向已删除目录的 cwd。发现相似名称的路径时,把候选位置交给 AI 核实,不自动切换项目。
检测到文件系统不可用或已观测的设备身份改变时,使用 Cindy 现有对话工作目录继续交流,不在断开的挂载点下创建替身目录。AI 收到原目录、临时工作位置及文件未恢复的说明;磁盘重连后不自动切回或迁移产物。该例外已获需求方确认。
恢复时保留调用方原始 userPrompt、Maker Memory 与 reasoning 显示偏好,项目/Orca 内容仍由原启动流程生成。关闭旧实例前复用 Bot 资源预检,实际启动继续统一装配 Bot 身份与资源。Pi 的 slash 输入保留在开头,由 Pi 决定是否执行;恢复说明留给后续普通消息。
变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
UI 变化
不涉及。
怎么验证的
自动验证
手工验证
未运行客户端手工交互。
未执行的验证
未运行 macOS/Windows 客户端实机端到端验证、手机或 SSH 实机验证;本次为后端恢复改动,使用文件系统与发送事务测试验证。完整单测由 CI 执行。
风险
风险分类
影响与回滚
ENOENT后原址重建普通目录,不覆盖已存在的文件;权限错误、非目录路径和托管 Git worktree 恢复失败仍保留原处理。提交前检查
git commit -s,见 DCO)