diff --git a/crates/host-core/src/db/project_groups.rs b/crates/host-core/src/db/project_groups.rs index c57db08ec..b5719b432 100644 --- a/crates/host-core/src/db/project_groups.rs +++ b/crates/host-core/src/db/project_groups.rs @@ -35,7 +35,7 @@ pub struct ProjectGroupRecord { pub last_opened_at: i64, #[serde(default)] pub legacy: bool, - /// Roots removed from the group remain suppressed as legacy projections. + /// Roots removed without chats remain suppressed as legacy projections. #[serde(default)] pub detached_paths: Vec, } @@ -266,6 +266,7 @@ impl Database { .map(|root| root.path.as_str()) .filter(|path| !ordered.iter().any(|candidate| candidate == path)) .collect::>(); + let mut removed_without_sessions = Vec::new(); for path in &removed { let has_sessions: bool = self.conn.query_row( "SELECT EXISTS( @@ -276,10 +277,8 @@ impl Database { params![path], |row| row.get(0), )?; - if has_sessions { - return Err(anyhow!( - "cannot remove a folder that still has chats: {path}" - )); + if !has_sessions { + removed_without_sessions.push(*path); } } for path in &ordered { @@ -306,7 +305,7 @@ impl Database { }) .collect::>(); let mut detached_paths = current.detached_paths; - for path in removed { + for path in removed_without_sessions { if !detached_paths.iter().any(|candidate| candidate == path) { detached_paths.push(path.to_string()); } diff --git a/crates/host-core/src/db/tests.rs b/crates/host-core/src/db/tests.rs index 1c2081d01..4c67a7758 100644 --- a/crates/host-core/src/db/tests.rs +++ b/crates/host-core/src/db/tests.rs @@ -1315,7 +1315,7 @@ fn project_group_roundtrips_roots_and_shared_context() { } #[test] -fn project_group_update_adjusts_roots_without_orphaning_chats() { +fn project_group_update_detaches_roots_with_chats_without_orphaning_them() { let dir = tempfile::tempdir().unwrap(); let primary = dir.path().join("primary"); let first = dir.path().join("first"); @@ -1378,15 +1378,28 @@ fn project_group_update_adjusts_roots_without_orphaning_chats() { Some(canonical_second.as_str()) ); - assert!(db + let detached = db .update_project_group( &updated.id, "Adjusted again", &[primary.to_string_lossy().into()], ) - .unwrap_err() - .to_string() - .contains("still has chats")); + .expect("a root with chats can be detached from the group"); + assert_eq!(detached.roots.len(), 1); + assert_eq!(detached.roots[0].path, group.primary_path); + assert_eq!(detached.detached_paths.len(), 1); + + let groups = db.list_project_groups().unwrap(); + assert_eq!(groups.len(), 2); + let standalone = groups + .iter() + .find(|candidate| candidate.roots[0].path == canonical_second) + .expect("the detached project's chats remain reachable"); + assert!(standalone.legacy); + assert_eq!( + db.project_session_ids(&canonical_second).unwrap(), + [session.id] + ); } #[test] diff --git a/crates/host-core/src/rpc/mod.rs b/crates/host-core/src/rpc/mod.rs index ab1a75bb4..b81ff2c94 100644 --- a/crates/host-core/src/rpc/mod.rs +++ b/crates/host-core/src/rpc/mod.rs @@ -5364,6 +5364,118 @@ mod tests { assert_eq!(updated["group"]["roots"].as_array().unwrap().len(), 2); } + #[tokio::test] + async fn grouped_folder_with_chats_can_be_detached_and_deleted_separately() { + let data_dir = tempfile::tempdir().unwrap(); + let primary = data_dir.path().join("primary"); + let member = data_dir.path().join("member"); + fs::create_dir_all(&primary).unwrap(); + fs::create_dir_all(&member).unwrap(); + let primary_input = primary.to_string_lossy().to_string(); + let member_input = member.to_string_lossy().to_string(); + let mut app_state = AppState::open(data_dir.path()).unwrap(); + app_state.handshook = true; + let state = Arc::new(Mutex::new(app_state)); + + let group_response = handle_request( + state.clone(), + "project.group.create", + json!({ "name": "Grouped", "folders": [primary_input, member_input] }), + mpsc::unbounded_channel().0, + ) + .await + .unwrap(); + let group_id = group_response["group"]["id"].as_str().unwrap().to_string(); + let primary_path = group_response["group"]["primaryPath"] + .as_str() + .unwrap() + .to_string(); + let member_path = group_response["group"]["roots"][1]["path"] + .as_str() + .unwrap() + .to_string(); + + // This chat predates removing the member from the group. + let session_id = { + let st = state.lock().await; + sessions::create_session_with_options( + &st.db, + sessions::SessionCreateOptions { + project_path: Some(member_path.clone()), + ..Default::default() + }, + ) + .unwrap() + .id + }; + + let updated = handle_request( + state.clone(), + "project.group.update", + json!({ "groupId": group_id, "name": "Grouped", "folders": [primary_path] }), + mpsc::unbounded_channel().0, + ) + .await + .expect("a member with chats can be detached from the group"); + assert_eq!(updated["group"]["roots"].as_array().unwrap().len(), 1); + + let detached_groups = handle_request( + state.clone(), + "project.groups.list", + json!({}), + mpsc::unbounded_channel().0, + ) + .await + .unwrap(); + assert!(detached_groups["groups"] + .as_array() + .unwrap() + .iter() + .any(|group| { + group["legacy"] == json!(true) && group["roots"][0]["path"] == json!(member_path) + })); + + let removed = handle_request( + state.clone(), + "projects.remove", + json!({ "path": member_path }), + mpsc::unbounded_channel().0, + ) + .await + .expect("the detached project can then be deleted through the project action"); + assert_eq!(removed["removed"], json!(true)); + assert_eq!(removed["sessionsRemoved"], json!(1)); + assert!(member.exists()); + + let groups_after_delete = handle_request( + state.clone(), + "project.groups.list", + json!({}), + mpsc::unbounded_channel().0, + ) + .await + .unwrap(); + assert_eq!(groups_after_delete["groups"].as_array().unwrap().len(), 1); + assert_eq!( + groups_after_delete["groups"][0]["roots"][0]["path"], + json!(primary_path) + ); + + let sessions_after_delete = handle_request( + state, + "session.list", + json!({}), + mpsc::unbounded_channel().0, + ) + .await + .unwrap(); + assert!(sessions_after_delete["sessions"] + .as_array() + .unwrap() + .iter() + .all(|session| session["id"] != json!(session_id))); + } + #[tokio::test] async fn project_group_rpc_roundtrips_context_and_roots() { let data_dir = tempfile::tempdir().unwrap(); diff --git a/docs/adr/0249-chatgpt-style-logical-project-groups.md b/docs/adr/0249-chatgpt-style-logical-project-groups.md index cf2301911..f56c5eeaf 100644 --- a/docs/adr/0249-chatgpt-style-logical-project-groups.md +++ b/docs/adr/0249-chatgpt-style-logical-project-groups.md @@ -40,7 +40,10 @@ policy. 6. The project overflow action is named **Edit project**. The editor can change the group name and adjust its root list through the additive `project.group.update` capability. The primary root remains first and cannot - be removed; a root with existing chats cannot be detached. + be removed. An additional root can be detached even when it has chats; those + chats remain available under the detached path as a standalone project. + Future chats use that project's path-scoped context, while the group's shared + instructions and memory remain with the original group. 7. Group creation, editing, shared instruction editing, and shared memory editing are additive IPC capabilities. Legacy path-scoped instruction and memory APIs remain available for legacy single-root groups. @@ -53,9 +56,9 @@ policy. presentation metadata. - The first folder remains meaningful as the default execution root and cannot be reordered in this iteration. Additional roots can be adjusted from Edit - project, subject to chat-preservation and host ownership checks, and are - available only through explicit absolute paths that pass host canonical - containment. + project; detaching one with chats preserves those sessions as a standalone + project. Group membership and path access still pass host ownership checks + and explicit absolute paths through host canonical containment. - Selecting another group still changes the one visible host workspace; a background session remains bound to its own primary path. - The native picker remains local-only. Remote project sources are not implied. diff --git a/docs/adr/0251-project-delete-with-owned-sessions.md b/docs/adr/0251-project-delete-with-owned-sessions.md index bd7521634..d0a847ac4 100644 --- a/docs/adr/0251-project-delete-with-owned-sessions.md +++ b/docs/adr/0251-project-delete-with-owned-sessions.md @@ -47,7 +47,9 @@ Two integrity facts constrain the fix: 4. A path that is a root of a stored multi-folder project group is refused with a structured error. The user removes the folder from the group first, which keeps the group's primary root valid; legacy single-root projections are not - affected and delete normally. + affected and delete normally. Edit project can detach an additional root + even when it has chats; those chats remain available as a standalone project + until the user explicitly deletes it. 5. Deleting is a renderer action behind a second confirmation that names the project and the number of sessions it owns. On success the renderer drops the matching renderer-local record in the same operation: the archived/pinned @@ -96,10 +98,11 @@ Two integrity facts constrain the fix: transcript directory and still look like a project in the index. - **Delete the project's folder on disk too.** Rejected: an application action must not remove user files. -- **Detach the root from a multi-folder group inside the same call.** Rejected - for now: `project.group.update` refuses to detach a root that has chats, and - silently rewriting group structure during a delete makes the outcome hard to - predict. The refusal path can be relaxed by a later ADR. +- **Detach the root from a multi-folder group inside the same call.** Rejected: + root membership changes remain an explicit Edit project action, separate + from the destructive project-delete confirmation. When a detached root has + chats, the edit preserves them as a standalone project so the user can review + or delete them separately. - **Abort the running turns inside the same call.** Rejected: host-core can settle a turn's own bookkeeping but cannot stop the agent runtime that is still streaming into that session, so an aborted-then-deleted session can diff --git a/docs/spec/03-runtime/04-data-storage.md b/docs/spec/03-runtime/04-data-storage.md index f99dd2b43..90ecf86ba 100644 --- a/docs/spec/03-runtime/04-data-storage.md +++ b/docs/spec/03-runtime/04-data-storage.md @@ -37,10 +37,12 @@ relational schema. The host stores one JSON record per group in the `projectGroups` namespace, shared memory in `projectGroupMemory`, and shared instructions in `projectGroupInstructions`. The record contains the stable group id, display name, ordered canonical roots, primary root, timestamps, and optional -`detachedPaths`. Removed roots stay in `detachedPaths` so an old path project -record is not recreated as a standalone legacy group; sessions and files are not -deleted. Existing path projects are projected as legacy single-root groups at -read time; their path-scoped memory and filesystem instructions remain readable. +`detachedPaths`. Removed roots without sessions stay in `detachedPaths` so an old +path project record is not recreated as a standalone legacy group. A removed root +with sessions is omitted from `detachedPaths` and remains readable as a standalone +legacy group; removing a root from group membership never deletes sessions or files. +Existing path projects are projected as legacy single-root groups at read time; +their path-scoped memory and filesystem instructions remain readable. 5. **Plan/Goal checkpoints are immutable host artifacts** with recorded path, hash, and size; the existing approval row also carries execution fields. Startup interruption is the process-epoch fence and no work is replayed. diff --git a/docs/spec/04-ux/08-component-spec.md b/docs/spec/04-ux/08-component-spec.md index 20ba98958..903dc5cdd 100644 --- a/docs/spec/04-ux/08-component-spec.md +++ b/docs/spec/04-ux/08-component-spec.md @@ -727,7 +727,7 @@ controls. | Project reorder | press-and-move on the title (8px), or ArrowUp/ArrowDown on that title, writes contiguous normalized-path order to sidebar preferences; accent insertion line; no visible grip | | Project archive | omitted from default view; restorable from archived view | | Project close | removes retained tab only; durable project/sessions remain | -| Project delete | row-menu danger action behind a second confirmation that names the project and the number of its sessions; refused with a message while any of those sessions is running; removes the durable project row, those sessions, their transcripts, and its project memory; never deletes the folder on disk; a path owned by a multi-folder project group is refused with a message, and a path the host no longer knows is still removed from the list | +| Project delete | row-menu danger action behind a second confirmation that names the project and the number of its sessions; refused with a message while any of those sessions is running; removes the durable project row, those sessions, their transcripts, and its project memory; never deletes the folder on disk; a path still owned by a multi-folder project group is refused with a message until detached in Edit project, and a path the host no longer knows is still removed from the list. Detaching a root with chats preserves them under a standalone project | | Project memory | row-menu editor reads and saves a compact list of titled or untitled memory cards for the exact project path; cards can be added, edited, and removed, the context is available in later chats, and it is never a higher-priority instruction | | Session list | exact-path matches only; no basename grouping | | Active group | exactly one group reflects the selected host workspace | diff --git a/docs/spec/04-ux/09-interaction-patterns.md b/docs/spec/04-ux/09-interaction-patterns.md index 75b32eca4..496b77a02 100644 --- a/docs/spec/04-ux/09-interaction-patterns.md +++ b/docs/spec/04-ux/09-interaction-patterns.md @@ -251,9 +251,12 @@ may be retained while exactly one workspace supplies the visible shell context. trims and persists a 1–80 Unicode-code-point group name and lists every registered folder. The Primary folder stays first and cannot be removed; additional folders can be added through the native multi-selection picker or - removed individually. Saving updates the host-owned group while preserving - the normalized paths, workspace identity, sessions, transcripts, and on-disk - folders. A folder with existing chats cannot be removed. + removed individually. Saving updates group membership without rewriting path + identities, workspace identity, session ownership, transcripts, or on-disk + folders. Removing an additional folder with chats detaches it as a standalone + project and keeps its chats available there. Future chats use that project's + path-scoped context; shared instructions and memory stay with the original + group. - **Pin** toggles presentation priority. Pinned projects/conversations appear before unpinned rows within the selected secondary order. In the sidebar, a pinned project replaces its Folder glyph with a filled accent Star so its diff --git a/docs/spec/06-delivery/04-e2e-test-plan.md b/docs/spec/06-delivery/04-e2e-test-plan.md index 5aadef689..c0efd920a 100644 --- a/docs/spec/06-delivery/04-e2e-test-plan.md +++ b/docs/spec/06-delivery/04-e2e-test-plan.md @@ -3809,7 +3809,7 @@ identify the platform validation still needed. - **Preconditions**: One retained logical project is visible in the sidebar and in Settings → Project archive; it has a primary folder and one additional - folder. + folder without chats. - **Steps**: 1) Open the project's overflow menu in the sidebar and choose Edit project. 2) Change the name, remove the additional folder, and add it again with the native folder picker. 3) Confirm the Primary row cannot be @@ -3825,8 +3825,7 @@ identify the platform validation still needed. Primary folder as the first row, and updates the root count without removing another row. Saving persists one logical group with the adjusted roots; the name survives restart, while normalized paths, workspace identity, sessions, - and on-disk folders remain unchanged. A root with existing chats is rejected - instead of orphaning those chats. + and on-disk folders remain unchanged. - **Specs linked**: `04-ux/01-ui-ia.md`, `04-ux/08-component-spec.md`, `04-ux/09-interaction-patterns.md` - **Acceptance**: D (workspace identity), F (local presentation persistence) @@ -3834,6 +3833,28 @@ identify the platform validation still needed. - **Status**: Unit-covered (`project-edit.test.mjs`, `sidebar-preferences.test.mjs`); rendered scenario Draft +#### E2E-048c: Detach a project folder with chats and delete it + +- **Preconditions**: One retained logical project has a primary folder and an + additional folder with at least one saved chat. All chats are idle. +- **Steps**: 1) Open Edit project and remove the additional folder. 2) Save and + inspect the remaining group and the standalone project row for the detached + folder. 3) Open the detached project's chat and confirm its transcript is + intact. 4) Delete the detached project using its two-click delete action. + 5) Inspect the remaining group, project list, chat list, and folder on disk. +- **Expected**: Removing the folder from the group preserves its project row, + chats, and transcripts, and exposes it as a standalone project. Future chats + use the detached project's path-scoped context; shared instructions and + memory remain with the original group. The confirmed delete then removes that + project's sessions and transcripts while keeping its folder on disk; the + original group remains with its primary folder. +- **Specs linked**: `03-runtime/04-data-storage.md`, + `04-ux/08-component-spec.md`, `04-ux/09-interaction-patterns.md`, ADR 0249, + ADR 0251. +- **Acceptance**: C (project and chat interaction), F (persistence), Quality. +- **Milestone**: M5 +- **Status**: Host RPC integration-covered; full UI scenario Draft + #### E2E-048A: Project session lists fold after the ten most recent rows - **Preconditions**: One retained project contains more than ten durable @@ -9287,6 +9308,7 @@ must keep splitting are covered by `markdown-blocks.test.mjs`. | A / C / Quality — Sidebar material and settings return | E2E-LAYOUT-sidebar-settings | | C / Quality — Destination loading and focus | E2E-087b | | A / H / Quality — Renderer process crash recovery | E2E-RUNTIME-renderer-crash-recovery | +| C / F / Quality — Project folder with chats detaches and deletes safely | E2E-048c | | B / F / Security — Provider copy | E2E-PROVIDER-copy-config-without-credentials | | B / F / Quality — Selected model order | E2E-MODEL-selected-order-persists | | E / F / Quality — MCP server timeout override | E2E-261 | diff --git a/docs/zh-CN/spec/06-delivery/04-e2e-test-plan.md b/docs/zh-CN/spec/06-delivery/04-e2e-test-plan.md index ca7583835..d2f08da03 100644 --- a/docs/zh-CN/spec/06-delivery/04-e2e-test-plan.md +++ b/docs/zh-CN/spec/06-delivery/04-e2e-test-plan.md @@ -2034,7 +2034,7 @@ hover/focus 不带移位标签,项目标题 hover/focus 路径显示 #### E2E-048b:编辑逻辑项目名称和文件夹根目录 - **前提**:一个逻辑项目同时出现在侧边栏和设置 → 项目存档中,包含一个 - Primary 文件夹和一个附加文件夹。 + Primary 文件夹和一个没有聊天的附加文件夹。 - **步骤**:1) 打开侧边栏项目溢出菜单并选择“编辑项目”。2) 修改名称,移除 附加文件夹,再通过原生文件夹选择器将其添加回来。3) 确认 Primary 行不能移除。 4) 保存并检查侧边栏、项目存档中的根目录和当前工作区。5) 重启应用并再次检查项目组。 @@ -2045,8 +2045,7 @@ hover/focus 不带移位标签,项目标题 hover/focus 路径显示 - **预期**:两个项目菜单都提供编辑项目;编辑器保持焦点、去除首尾空格并限制为 80 个 Unicode 字符,Primary 文件夹始终是第一行,根目录数量更新且不会误删其他行。 保存后仍是一个逻辑项目组,调整后的根目录会持久化;名称在重启后保留,规范化 - 路径、工作区身份、会话和磁盘文件夹均不改变。已有聊天的根目录会被拒绝移除, - 不会产生孤立会话。 + 路径、工作区身份、会话和磁盘文件夹均不改变。 - **链接规格**:`04-ux/01-ui-ia.md`、`04-ux/08-component-spec.md`、 `04-ux/09-interaction-patterns.md` - **验收**:D(工作区身份)、F(本地演示持久性) @@ -2054,6 +2053,16 @@ hover/focus 不带移位标签,项目标题 hover/focus 路径显示 - **状态**:单位覆盖(`project-edit.test.mjs`、 `sidebar-preferences.test.mjs`);完整的 UI 场景草稿 +#### E2E-048c:分离含聊天的项目文件夹并单独删除 + +- **前提:** 一个保留的逻辑项目包含一个主文件夹和一个有至少一条已保存聊天的附加文件夹;所有聊天均处于空闲状态。 +- **步骤:** 1) 打开“编辑项目”并移除附加文件夹。2) 保存后检查剩余项目组及该文件夹对应的独立项目行。3) 打开独立项目中的聊天,确认对话记录完整。4) 使用两步删除操作删除该独立项目。5) 检查剩余项目组、项目列表、聊天列表及磁盘上的文件夹。 +- **预期:** 从项目组移除文件夹会保留其项目记录、聊天和对话记录,并将其作为独立项目显示。新聊天使用该项目的路径级上下文;共享说明和记忆仍属于原项目组。确认删除后会移除该项目的会话及对话记录,但保留磁盘上的文件夹;原项目组仍保留主文件夹。 +- **链接规格:** `03-runtime/04-data-storage.md`、`04-ux/08-component-spec.md`、`04-ux/09-interaction-patterns.md`、ADR 0249、ADR 0251。 +- **验收:** C(项目和聊天交互)、F(持久化)、品质。 +- **里程碑:** M5 +- **状态:** Host RPC 集成测试已覆盖;完整 UI 场景仍为草稿 + #### E2E-048A:项目会话在最近十行之后列出折叠 - **前提**:一个保留项目包含十个以上耐久 @@ -5548,23 +5557,24 @@ eleven-tool-round desktop paths are verified by | A / C / Quality — 侧栏材质与设置返回 | E2E-LAYOUT-sidebar-settings | | C / Quality — 目的页加载与焦点 | E2E-087b | | A / H / Quality — 渲染器进程崩溃恢复 | E2E-RUNTIME-renderer-crash-recovery | +| C / F / Quality — 含聊天的项目文件夹安全分离并删除 | E2E-048c | | B / F / Security — 提供商复制 | E2E-PROVIDER-copy-config-without-credentials | | B / F / Quality — 已选模型顺序 | E2E-MODEL-selected-order-persists | | E / F / Quality — MCP 服务器超时覆盖 | E2E-261 | | A — 应用程序启动 | E2E-001、E2E-002、E2E-003、E2E-004、E2E-067、E2E-076、E2E-079、E2E-092、E2E-097、E2E-143、E2E-150、E2E-168、E2E-204、E2E-217 | | B——模型配置 | E2E-005、E2E-005G、E2E-006、E2E-007、E2E-038、E2E-050、E2E-052、E2E-055、E2E-066、E2E-080、E2E-082、E2E-151、E2E-005J、E2E-199、E2E-201、E2E-202、E2E-203、E2E-209、E2E-166 | -| C — 对话和直播 | E2E-CHAT-running-status-survives-output-pauses、E2E-008、E2E-008d、E2E-008a、E2E-009、E2E-010、E2E-011、E2E-011a、E2E-011b、E2E-031、E2E-040、E2E-047、E2E-048、E2E-048A、E2E-049、E2E-052、 E2E-053、E2E-054、E2E-055、E2E-059、E2E-059a、E2E-060c、E2E-060d、E2E-061、E2E-061a、E2E-062、E2E-064、E2E-065、E2E-068、E2E-071、 E2E-073、E2E-074、E2E-075、E2E-081、E2E-083、E2E-084、E2E-086、E2E-087、E2E-088、E2E-088b、E2E-089、E2E-090、E2E-094、E2E-095、E2E-096、 E2E-097、E2E-098、E2E-099、E2E-102、E2E-102a、E2E-102b、E2E-106、E2E-109、E2E-111、E2E-114、E2E-116、E2E-117、E2E-118、E2E-119、 E2E-120、E2E-121、E2E-代理-001、E2E-142、E2E-144、E2E-145、E2E-146、E2E-147、E2E-151、E2E-199、E2E-250、E2E-166、E2E-SUBAGENT-resume-a-settled-delegation | +| C — 对话和直播 | E2E-CHAT-running-status-survives-output-pauses、E2E-008、E2E-008d、E2E-008a、E2E-009、E2E-010、E2E-011、E2E-011a、E2E-011b、E2E-031、E2E-040、E2E-047、E2E-048、E2E-048c、E2E-048A、E2E-049、E2E-052、 E2E-053、E2E-054、E2E-055、E2E-059、E2E-059a、E2E-060c、E2E-060d、E2E-061、E2E-061a、E2E-062、E2E-064、E2E-065、E2E-068、E2E-071、 E2E-073、E2E-074、E2E-075、E2E-081、E2E-083、E2E-084、E2E-086、E2E-087、E2E-088、E2E-088b、E2E-089、E2E-090、E2E-094、E2E-095、E2E-096、 E2E-097、E2E-098、E2E-099、E2E-102、E2E-102a、E2E-102b、E2E-106、E2E-109、E2E-111、E2E-114、E2E-116、E2E-117、E2E-118、E2E-119、 E2E-120、E2E-121、E2E-代理-001、E2E-142、E2E-144、E2E-145、E2E-146、E2E-147、E2E-151、E2E-199、E2E-250、E2E-166、E2E-SUBAGENT-resume-a-settled-delegation | | C — 对话和直播(输入框草稿) | E2E-011c、E2E-011c-1 | | A / C / F / Quality — Tray session navigation | E2E-TRAY-bounded-session-navigation | | D——工作区 | E2E-012、E2E-013、E2E-022B、E2E-024I、E2E-047、E2E-049、E2E-057、E2E-058、E2E-060、E2E-068、E2E-075、E2E-078、E2E-153 | | D——工作区(项目排序) | E2E-253 | | E——工具和权限 | E2E-008a、E2E-014、E2E-015、E2E-016、E2E-017、E2E-018、E2E-019、E2E-024I、E2E-024K、E2E-040、E2E-049、E2E-074、E2E-093、E2E-097、 E2E-099、E2E-100、E2E-101、E2E-102、E2E-103、E2E-105、E2E-106、E2E-107、E2E-111、E2E-112、E2E-113、E2E-114、E2E-115、E2E-116、 E2E-119、E2E-121、E2E-122、E2E-123、E2E-142、E2E-145、E2E-147、E2E-PLUGIN-imported-pi-package-skills、E2E-166 | -| F——坚持 | E2E-020、E2E-021、E2E-036、E2E-037、E2E-038、E2E-040、E2E-042、E2E-047、E2E-048、E2E-051、E2E-054、E2E-056、E2E-061、E2E-062、 E2E-064、E2E-066、E2E-068、E2E-071、E2E-072、E2E-073、E2E-082、E2E-084、E2E-096、E2E-098、E2E-102、E2E-102b、E2E-103、E2E-代理-001、 E2E-061a、E2E-073a、E2E-104、E2E-106、E2E-107、E2E-108、E2E-109、E2E-110、E2E-112、E2E-118、E2E-119、E2E-120、E2E-121、E2E-123、E2E-142、E2E-146、E2E-148、E2E-151、E2E-171、E2E-005J | +| F——坚持 | E2E-020、E2E-021、E2E-036、E2E-037、E2E-038、E2E-040、E2E-042、E2E-047、E2E-048、E2E-048c、E2E-051、E2E-054、E2E-056、E2E-061、E2E-062、 E2E-064、E2E-066、E2E-068、E2E-071、E2E-072、E2E-073、E2E-082、E2E-084、E2E-096、E2E-098、E2E-102、E2E-102b、E2E-103、E2E-代理-001、 E2E-061a、E2E-073a、E2E-104、E2E-106、E2E-107、E2E-108、E2E-109、E2E-110、E2E-112、E2E-118、E2E-119、E2E-120、E2E-121、E2E-123、E2E-142、E2E-146、E2E-148、E2E-151、E2E-171、E2E-005J | | F——持久化(项目排序) | E2E-253 | | G——插件 | E2E-022、E2E-022A、E2E-022B、E2E-022C、E2E-023、E2E-024、E2E-024B、E2E-024C、E2E-024D、E2E-024AA、E2E-024E、E2E-024W、E2E-024F、E2E-024G、E2E-024H、 E2E-024I、E2E-024J、E2E-024K、E2E-024L、E2E-024M、E2E-024N、E2E-024O、E2E-024P、E2E-025、E2E-026、E2E-105、E2E-117、E2E-120、E2E-122、E2E-123、E2E-148、E2E-153、E2E-PLUGIN-imported-pi-package-skills、E2E-PLUGIN-imported-pi-package-wrapper、E2E-PLUGIN-import-extension-installs-dependencies、E2E-PLUGIN-import-extension-reports-missing-dependency、E2E-PLUGIN-global-shortcut-owns-only-its-own-command、E2E-PLUGIN-permission-gate-for-real-time-capabilities、E2E-PLUGIN-background-audio-and-realtime-connection | | H——诊断 | E2E-027、E2E-031、E2E-034、E2E-042、E2E-096、E2E-098、E2E-104、E2E-107、E2E-108、E2E-109、E2E-110、E2E-113、E2E-115、E2E-116、 E2E-118、E2E-121、E2E-146、E2E-194、E2E-195 | | 安全性 | E2E-028、E2E-029、E2E-030、E2E-024J、E2E-024K、E2E-024M、E2E-049、E2E-068、E2E-086、E2E-105、E2E-106、E2E-107、E2E-108、E2E-109、 E2E-110、E2E-112、E2E-113、E2E-115、E2E-116、E2E-117、E2E-119、E2E-121、E2E-122、E2E-123、E2E-142、E2E-148、E2E-151、E2E-153 | -| 品质 | E2E-CHAT-running-status-survives-output-pauses、E2E-032、E2E-033、E2E-039、E2E-043、E2E-044、E2E-045、E2E-046、E2E-047、E2E-048、E2E-048A、E2E-049、E2E-050、E2E-053、E2E-055、 E2E-056、E2E-057、E2E-058、E2E-059、E2E-060、E2E-061、E2E-062、E2E-063、E2E-064、E2E-065、E2E-066、E2E-067、E2E-068、E2E-069、 E2E-070、E2E-071、E2E-072、E2E-073、E2E-074、E2E-075、E2E-076、E2E-077、E2E-078、E2E-079、E2E-080、E2E-081、E2E-082、E2E-083、 E2E-084、E2E-085、E2E-086、E2E-092、E2E-093、E2E-094、E2E-095、E2E-096、E2E-097、E2E-098、E2E-099、E2E-100、E2E-101、E2E-102、 E2E-102a、E2E-102b、E2E-103、E2E-AGENTS-001、E2E-024N、E2E-024O、E2E-059a、E2E-060b、E2E-060c、E2E-060d、E2E-061a、E2E-073a、E2E-111、 E2E-114、E2E-117、E2E-118、E2E-119、E2E-120、E2E-122、E2E-123、E2E-142、E2E-143、E2E-144、E2E-145、E2E-146、E2E-147、E2E-148、E2E-150、E2E-151、E2E-153、E2E-194、E2E-195、E2E-199、E2E-200、E2E-201、E2E-202、E2E-203、E2E-204、E2E-209、E2E-210、E2E-UPDATE-preference-and-once-only-reminder、E2E-250、E2E-PLUGIN-imported-pi-package-skills、E2E-SUBAGENT-resume-a-settled-delegation | +| 品质 | E2E-CHAT-running-status-survives-output-pauses、E2E-032、E2E-033、E2E-039、E2E-043、E2E-044、E2E-045、E2E-046、E2E-047、E2E-048、E2E-048c、E2E-048A、E2E-049、E2E-050、E2E-053、E2E-055、 E2E-056、E2E-057、E2E-058、E2E-059、E2E-060、E2E-061、E2E-062、E2E-063、E2E-064、E2E-065、E2E-066、E2E-067、E2E-068、E2E-069、 E2E-070、E2E-071、E2E-072、E2E-073、E2E-074、E2E-075、E2E-076、E2E-077、E2E-078、E2E-079、E2E-080、E2E-081、E2E-082、E2E-083、 E2E-084、E2E-085、E2E-086、E2E-092、E2E-093、E2E-094、E2E-095、E2E-096、E2E-097、E2E-098、E2E-099、E2E-100、E2E-101、E2E-102、 E2E-102a、E2E-102b、E2E-103、E2E-AGENTS-001、E2E-024N、E2E-024O、E2E-059a、E2E-060b、E2E-060c、E2E-060d、E2E-061a、E2E-073a、E2E-111、 E2E-114、E2E-117、E2E-118、E2E-119、E2E-120、E2E-122、E2E-123、E2E-142、E2E-143、E2E-144、E2E-145、E2E-146、E2E-147、E2E-148、E2E-150、E2E-151、E2E-153、E2E-194、E2E-195、E2E-199、E2E-200、E2E-201、E2E-202、E2E-203、E2E-204、E2E-209、E2E-210、E2E-UPDATE-preference-and-once-only-reminder、E2E-250、E2E-PLUGIN-imported-pi-package-skills、E2E-SUBAGENT-resume-a-settled-delegation | | 品质(项目排序) | E2E-253 | | C — 对话和直播(输入法斜杠别名) | E2E-255 | | E——工具和权限(Skill 常驻) | E2E-254 | @@ -5613,7 +5623,7 @@ eleven-tool-round desktop paths are verified by | M2 | E2E-004、E2E-005、E2E-006、E2E-007、E2E-008、E2E-008d、E2E-009、E2E-010、E2E-011、E2E-011a、E2E-011b、E2E-020、E2E-021、E2E-027、E2E-031、 E2E-036、E2E-037、E2E-042、E2E-087、E2E-088、E2E-088b、E2E-089、E2E-090、E2E-144、E2E-005J、E2E-201 | | M3 | E2E-012、E2E-013、E2E-014、E2E-015、E2E-016、E2E-017、E2E-018、E2E-019、E2E-040 | | M4 | E2E-022、E2E-023、E2E-024、E2E-025、E2E-026、E2E-030、E2E-038 | -| M5 | E2E-CHAT-running-status-survives-output-pauses、E2E-008a、E2E-032、E2E-033、E2E-034、E2E-039、E2E-043、E2E-044、E2E-045、E2E-046、E2E-047、E2E-048、E2E-048A、E2E-049、E2E-050、 E2E-051、E2E-052、E2E-053、E2E-054、E2E-055、E2E-056、E2E-057、E2E-058、E2E-059、E2E-060、E2E-061、E2E-062、E2E-063、E2E-064、 E2E-065、E2E-066、E2E-067、E2E-068、E2E-069、E2E-070、E2E-071、E2E-072、E2E-073、E2E-074、E2E-075、E2E-076、E2E-077、E2E-078、 E2E-079、E2E-080、E2E-081、E2E-082、E2E-083、E2E-084、E2E-085、E2E-086、E2E-092、E2E-093、E2E-096、E2E-097、E2E-098、E2E-099、 E2E-100、E2E-101、E2E-102、E2E-102a、E2E-102b、E2E-AGENTS-001、E2E-059a、E2E-060b、E2E-060c、E2E-061a、E2E-073a、E2E-094、E2E-095、E2E-143、E2E-145、E2E-146、E2E-147、E2E-194、E2E-195、E2E-204、E2E-250 | +| M5 | E2E-CHAT-running-status-survives-output-pauses、E2E-008a、E2E-032、E2E-033、E2E-034、E2E-039、E2E-043、E2E-044、E2E-045、E2E-046、E2E-047、E2E-048、E2E-048c、E2E-048A、E2E-049、E2E-050、 E2E-051、E2E-052、E2E-053、E2E-054、E2E-055、E2E-056、E2E-057、E2E-058、E2E-059、E2E-060、E2E-061、E2E-062、E2E-063、E2E-064、 E2E-065、E2E-066、E2E-067、E2E-068、E2E-069、E2E-070、E2E-071、E2E-072、E2E-073、E2E-074、E2E-075、E2E-076、E2E-077、E2E-078、 E2E-079、E2E-080、E2E-081、E2E-082、E2E-083、E2E-084、E2E-085、E2E-086、E2E-092、E2E-093、E2E-096、E2E-097、E2E-098、E2E-099、 E2E-100、E2E-101、E2E-102、E2E-102a、E2E-102b、E2E-AGENTS-001、E2E-059a、E2E-060b、E2E-060c、E2E-061a、E2E-073a、E2E-094、E2E-095、E2E-143、E2E-145、E2E-146、E2E-147、E2E-194、E2E-195、E2E-204、E2E-250 | | M5(项目排序) | E2E-253 | | M2(输入法斜杠别名) | E2E-255 | | M5(Skill 常驻) | E2E-254 | diff --git a/packages/i18n/src/locales/de/index.ts b/packages/i18n/src/locales/de/index.ts index 0ae6207ab..b394c53c9 100644 --- a/packages/i18n/src/locales/de/index.ts +++ b/packages/i18n/src/locales/de/index.ts @@ -1677,7 +1677,7 @@ sklm: { "openActions": "Aktionen für {{name}} öffnen", "reorder": "Reihenfolge von {{name}} ändern", "editTitle": "Projekt bearbeiten", - "editDescription": "Projektname und Ordner aktualisieren.", + "editDescription": "Ein entfernter Ordner mit Chats wird zu einem eigenen Projekt; die Chats bleiben erhalten.", "editAction": "Änderungen speichern", "editSaving": "Speichern…", "editCancel": "Abbrechen", diff --git a/packages/i18n/src/locales/en/index.ts b/packages/i18n/src/locales/en/index.ts index 348572378..aa2a45ddd 100644 --- a/packages/i18n/src/locales/en/index.ts +++ b/packages/i18n/src/locales/en/index.ts @@ -1700,7 +1700,7 @@ sklm: { openActions: "Open actions for {{name}}", reorder: "Reorder {{name}}", editTitle: "Edit project", - editDescription: "Update this project's name and folders.", + editDescription: "A removed folder with chats becomes its own project, and those chats stay available.", editAction: "Save changes", editSaving: "Saving…", editCancel: "Cancel", diff --git a/packages/i18n/src/locales/es/index.ts b/packages/i18n/src/locales/es/index.ts index c28e34a4f..031507cf3 100644 --- a/packages/i18n/src/locales/es/index.ts +++ b/packages/i18n/src/locales/es/index.ts @@ -1677,7 +1677,7 @@ sklm: { "openActions": "Abrir acciones para {{name}}", "reorder": "Reordenar {{name}}", "editTitle": "Editar proyecto", - "editDescription": "Actualiza el nombre y las carpetas del proyecto.", + "editDescription": "Una carpeta quitada con chats pasa a ser un proyecto independiente y conserva sus chats.", "editAction": "Guardar cambios", "editSaving": "Guardando…", "editCancel": "Cancelar", diff --git a/packages/i18n/src/locales/fr/index.ts b/packages/i18n/src/locales/fr/index.ts index be15511e0..a34bf1d53 100644 --- a/packages/i18n/src/locales/fr/index.ts +++ b/packages/i18n/src/locales/fr/index.ts @@ -1677,7 +1677,7 @@ sklm: { "openActions": "Ouvrir les actions pour {{name}}", "reorder": "Réorganiser {{name}}", "editTitle": "Modifier le projet", - "editDescription": "Mettez à jour le nom et les dossiers du projet.", + "editDescription": "Un dossier retiré qui contient des discussions devient un projet distinct ; ses discussions sont conservées.", "editAction": "Enregistrer les modifications", "editSaving": "Enregistrement…", "editCancel": "Annuler", diff --git a/packages/i18n/src/locales/ko/index.ts b/packages/i18n/src/locales/ko/index.ts index 9ee8513c9..1b219b865 100644 --- a/packages/i18n/src/locales/ko/index.ts +++ b/packages/i18n/src/locales/ko/index.ts @@ -1692,7 +1692,7 @@ sklm: { openActions: "{{name}} 작업 열기", reorder: "{{name}} 순서 변경", editTitle: "프로젝트 편집", - editDescription: "프로젝트 이름과 폴더를 수정하세요.", + editDescription: "대화가 있는 폴더를 제거하면 별도 프로젝트로 분리되며 대화는 유지됩니다.", editAction: "변경 사항 저장", editSaving: "저장 중…", editCancel: "취소", diff --git a/packages/i18n/src/locales/pt-BR/index.ts b/packages/i18n/src/locales/pt-BR/index.ts index b74d14eed..c4601eae3 100644 --- a/packages/i18n/src/locales/pt-BR/index.ts +++ b/packages/i18n/src/locales/pt-BR/index.ts @@ -1632,7 +1632,7 @@ export const ptBR = { openActions: "Abrir ações de {{name}}", reorder: "Reordenar {{name}}", editTitle: "Editar projeto", - editDescription: "Atualize o nome e as pastas deste projeto.", + editDescription: "Uma pasta removida com conversas vira um projeto separado, e as conversas são mantidas.", editAction: "Salvar alterações", editSaving: "Salvando…", editCancel: "Cancelar", diff --git a/packages/i18n/src/locales/tr/index.ts b/packages/i18n/src/locales/tr/index.ts index 2749a16fc..d7edf4b1e 100644 --- a/packages/i18n/src/locales/tr/index.ts +++ b/packages/i18n/src/locales/tr/index.ts @@ -1682,7 +1682,7 @@ sklm: { openActions: "{{name}} eylemlerini aç", reorder: "{{name}} sırasını değiştir", editTitle: "Projeyi düzenle", - editDescription: "Proje adını ve klasörlerini güncelleyin.", + editDescription: "Sohbetleri olan bir klasör kaldırıldığında ayrı bir proje olur; sohbetleri korunur.", editAction: "Değişiklikleri kaydet", editSaving: "Kaydediliyor…", editCancel: "İptal", diff --git a/packages/i18n/src/locales/zh-CN/index.ts b/packages/i18n/src/locales/zh-CN/index.ts index f80cbef22..f255513e9 100644 --- a/packages/i18n/src/locales/zh-CN/index.ts +++ b/packages/i18n/src/locales/zh-CN/index.ts @@ -1663,7 +1663,7 @@ sklm: { openActions: "打开“{{name}}”的操作菜单", reorder: "调整“{{name}}”顺序", editTitle: "编辑项目", - editDescription: "调整项目名称和文件夹。", + editDescription: "移除含有对话的文件夹后,它会成为独立项目,对话仍会保留。", editAction: "保存更改", editSaving: "保存中…", editCancel: "取消", diff --git a/packages/i18n/src/locales/zh-TW/index.ts b/packages/i18n/src/locales/zh-TW/index.ts index ee102f0ee..d93b1ec69 100644 --- a/packages/i18n/src/locales/zh-TW/index.ts +++ b/packages/i18n/src/locales/zh-TW/index.ts @@ -1663,7 +1663,7 @@ sklm: { openActions: "開啟“{{name}}”的操作選單", reorder: "調整「{{name}}」順序", editTitle: "編輯專案", - editDescription: "調整專案名稱與資料夾。", + editDescription: "移除含有對話的資料夾後,它會成為獨立專案,對話仍會保留。", editAction: "儲存變更", editSaving: "儲存中…", editCancel: "取消",