makecindy / makecindy/cindy

umbrella: 补齐 cindy_helper 会话操作工具面(移动/置顶/删除/导出/新窗口/分叉/分支)

Open
#4,385 6 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
2.7k
Forks
401
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 背景

`cindy_helper` control 类目此前只暴露了「改名」「归档」两类会话操作。真实踩坑:agent 要把一个已存在的 session 从对话型移到某个项目下(改 `workingDir` + `workspaceKind`),GUI 有「移动到项目」,但工具面没有,agent 只能绕过 app 直写 SQLite,跳过 renderer 广播与 `upsertRecentWorkdir` 等副作用,界面要重启才刷新。

调研结论:GUI 会话菜单里 agent 缺失的操作有 移动到项目/对话、置顶、删除、导出分享包、在新窗口打开、Fork、会话分支;「复制任务链接」不需要工具(固定 `cindy://session/`),Rewind 是作曲器内交互式改写,不暴露。

#4383 是一次性完成全部工具的参考实现(已转 draft,只作对照,不直接合并)。本 issue 把它拆成可独立评审的小 PR。

## 共同约束

- **必须复用主进程既有 session 写路径**(`local-db:sessions:update` 业务体 / session-share 导出编排 / `forkSessionAtMessage`),不另造绕过广播与副作用的写入链路。
- GUI 守卫在工具侧复现:远程(SSH)会话不支持;运行中(含 Orca lead 的 worker)拦截;IM 接管中(`bindingStore.findByTarget`)拦截;已归档 / 已删除、空草稿、review 会话按各操作口径拦截;不允许对当前 session 自己做移动 / 删除。
- 工具契约与 `archive_sessions` 同风格:批量 + 失败码 `NOT_FOUND` / `PRECONDITION_FAILED` / `INVALID_ARGS` / `HOST_NOT_READY` / `INTERNAL`;批量先全量校验、任一不过整批不写。
- 不新增 schema / migration;main 不用动态 `import()`;package 不反向依赖 main。
- 每个 PR 带单测;提交前跑 `pnpm test:unit:related` + 相关 package `typecheck`;`git commit -s`(DCO)。
- 参考实现可从 #4383(分支 `cofyc:cindy/sincere-kalam`)按文件 cherry-pick 对应 hunk。

## 当前状态(2026-09-13)

全部 8 个子 PR 均已实现、评审到 Greptile **5/5**、CI 全绿。T2 已合入 main;其余 7 个 `mergeable=MERGEABLE`、`mergeStateStatus=BLOCKED`,**唯一阻塞项是 main 规则要求的 1 个 approving review**(必需检查 DCO / `verify` / `Windows unit tests` 均已 SUCCESS)。

| 顺序 | PR | head | Greptile | CI | 状态 |
|---|---|---|---|---|---|
| T1 | #4388 | `4f60ee608` | 5/5 | 全绿 | 待 approve |
| T2 | #4387 | `d5766bfad` | 5/5 | 全绿 | ✅ 已合入 `07eac8958` |
| T3 | #4389 | `d25486a9f` | 5/5 | 全绿 | 待 approve |
| T4 | #4391 | `9d818078b` | 5/5 | 全绿 | 待 approve |
| T5 | #4392 | `f8939fbfe` | 5/5 | 全绿 | 待 approve |
| T6 | #4390 | `bd9b7c68f` | 5/5 | 全绿 | 待 approve |
| T7 | #4394 | `44fa636ce` | 5/5 | 全绿 | 待 approve |
| T8 | #4393 | `9b3aed45c` | 5/5 | 全绿 | 待 approve(最后合入) |

**建议合入顺序**:T1 → T3 → T4 / T5 / T6 / T7(可并行)→ T8 最后。T5 分支内含 T2 的两个提交(rebase 副本,与已入 main 的内容相同),GitHub 判定可干净合并,无需额外处理。

**T8 合入后仍需一步**:`capabilities.ts` 目前只描述本 PR 与 T3 已注册的工具(避免单独合入时承诺不存在的工具);全部合入后补一条覆盖全部会话工具的完整说明,即下方 T9。

## 拆分(按依赖顺序)

### 阶段 1(可并行)
- [ ] **T1 refactor(sessions.ts)** → #4388:把 `local-db:sessions:update` handler 的业务体抽成导出的 `updateSessionInDb(sid, patch, opts)`,IPC handler 只做 adapter;`registerSessionIpc` 记下 opts 供非 IPC 调用沿用 `closeIdleSessionForMove`。纯搬动(`git diff -w` 应只剩函数头尾),同步更新 `writableDirectoryGrantWiring.test.ts` 的源码文本断言。参考 #4383 的 `apps/desktop/src/main/localDb/ipc/sessions.ts`。
- [x] ~~**T2 refactor(lizi-mcps)**~~ **已合入** → #4387:把 `rename_sessions.ts` 里的 HMAC 确认 token 编解码抽到 `xdt-helper/_confirmation_token.ts`(`encodeConfirmationToken(payload)` / `decodeConfirmationToken(token, validate)`),rename 行为不变。参考 #4383 同名文件。

### 阶段 2(依赖 T1)
- [ ] **T3 feat:`move_sessions`** → #4389:工具(`packages/lizi-mcps/src/xdt-helper/move_sessions.ts` + `_session_ops.ts` 共享类型)+ host 业务体 `apps/desktop/src/main/mcp-integrations/sessionOperations.ts`(依赖全注入)+ `sessionOperationsHost.ts` 装配 + `mcp-providers.ts` 注入 `sessionOps`(运行中判断经 `DesktopMcpProvidersDeps.isSessionTurnRunning` 由 maker-host 注入,避免 mcp-providers 反向 import maker-host 成环)+ `lizi_xdtHelperMcpServer.ts` 的 `sessionOps` deps 与注册 + 两层单测。本任务同时建立后续工具复用的骨架。

### 阶段 3(依赖 T3;可并行)
- [ ] **T4 feat:`pin_sessions` / `unpin_sessions`** → #4391:经 `updateSessionInDb` 写 `pinnedAt`(ISO / null,与 GUI 同);已归档 / 已删除拒绝。
- [ ] **T5 feat:`delete_sessions`** → #4392(依赖 T2):dry-run 预览(含 `dirty_worktree`,取 `WorktreeManager.getRemovalPreview`)+ `confirmation_token` 两段式;真正删除经 `updateSessionInDb` 写 `status=deleted`。
- [ ] **T6 feat:`export_session`** → #4390:调用方给绝对 `target_path`(自动补 `.cshare`,父目录须存在),复用 `exportSessionShare`;`oversize` → `OVERSIZE` 并附体积数据。
- [ ] **T7 feat:`open_session_in_new_window` + `get_session_branches`** → #4394:前者复用 `secondary-windows.openSessionInNewWindow`;后者沿 `parentSessionId` 向上找根、向下 BFS,返回含 `parent_session_id` / `forked_at_message_id` 的家族。
- [ ] **T8 feat:`fork_session`** → #4393:`message_id` 取 `get_chat_history` 的消息 id,host 换算成 `messages.clientId` 后调 `forkSessionAtMessage` + `emitSessionCreated`;fork 错误码映射(`NOT_USER_MESSAGE`→`INVALID_ARGS`,`SOURCE_NEVER_RAN`/`NO_PRIOR_ASSISTANT`/`REMOTE_NOT_SUPPORTED`→`PRECONDITION_FAILED`,`UNSUPPORTED_HISTORY`→`UNSUPPORTED_CAPABILITY`)。

### 跟踪事项(本组 PR 明确不做,登记在案)

- [ ] **远程(SSH)会话适配**:`move_sessions` / `fork_session` / `export_session` 目前对 `remoteHostId`
非空一律**显式拒绝**并说明原因(`PRECONDITION_FAILED`),不是静默失败。按
`docs/dev-rules/remote-and-mobile-adaptation.md` 的合同,这里登记后续适配:三者各需一条独立的远程链路
—— move 要搬迁远端转录目录并重算远端 workdir;fork 的 rollout 在远端(`forkSessionStripEncrypted`
对远端 Codex 会话本来也显式拒绝);export 要在远端打包再回传。放进本组「补齐本机工具面」的 PR 会让
每个 PR 风险面翻倍,故单独跟踪。
- [ ] **批量置顶触发的摘要生成并发**:侧栏卡片模式下,`updateSessionInDb` 在 `pinnedAt` 落非空时会
fire-and-forget 调 `maybeGenerateSessionTaskSummary(sid, { force: true })`,而 `force` 会绕过
`inFlight` 去重(`sessionTaskSummary.ts:261`)。GUI 一次只置顶一个,批量置顶是本组新增能力,
一次可发起与批量大小相同的并发付费 LLM 请求;`sessionTaskSummary.ts` 的回填路径已特意逐个 `await`。
根治要在该模块加共享并发闸门(影响 turn-done 等既有调用方),属独立改动,单独跟踪。
- [ ] **Orca Lead 工作区移动与 Worker 派发的跨会话串行化**:`move_sessions` 对 Orca Lead 会检查
「有 Worker 在运行则拒绝」,写入前复核也已放进 Lead 的路由锁 + 状态写锁内。但 Worker 派发走的是
**各自**的 worker transition / session lock,与 Lead 的路由锁互不等待 —— 复核判定 Worker 空闲之后、
写入 Lead 新 workdir 之前,一条 `send_to_worker` 仍可能开始派发,导致 Worker 在旧工作区运行而 Lead
的 workdir 已被改写。根治需要按稳定顺序同时锁住相关 Worker,或让最终复核与写入和 Worker 派发共享
同一个 reservation —— 属跨会话锁编排,单独跟踪(该问题在 #4389 / #4392 / #4393 的评审中反复出现,
口径统一为本组 PR 不就地实现)。
- [ ] **T9 之后**:全部子 PR 合入后补一条覆盖全部会话工具的 `capabilities.ts` 完整说明。

### 收尾
- [ ] **T9 docs:`capabilities.ts`** → #4393 session-management 条目补齐全部新工具与 `cindy://session/` 链接格式(可并入最后一个合入的 feat PR)。

Contributor guide

Open the contributing guide

Research direction

This umbrella is already split across #4388, #4387, #4389, #4391, #4392, #4390, #4394, and #4393, with the implementations and review status listed in the issue. Start by reviewing those PRs and their referenced files, especially the session operation host and MCP tool modules. The remaining documented follow-up is T9 in capabilities.ts, which should describe all session tools and the cindy://session/ link format after the feature PRs merge.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, typescript
Domain
api, backend, desktop, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
15/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.