diff --git a/.agents/notes/implemented/feature/2026-09-20-default-workspace.i18n.yaml b/.agents/notes/implemented/feature/2026-09-20-default-workspace.i18n.yaml index cdb2673df8..a3ed2bee71 100644 --- a/.agents/notes/implemented/feature/2026-09-20-default-workspace.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-09-20-default-workspace.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-09-20-default-workspace.md -2026-09-20-default-workspace.md: d2a32d47a407dcf211c1cad8bb98f29847a77a3b -2026-09-20-default-workspace.zh.md: 6b0ec542da6e9e4fe76fb56b6562edea9e5777f4 +2026-09-20-default-workspace.md: 376b2eaf7ac26c13e7483838700c88151b44135d +2026-09-20-default-workspace.zh.md: e16c5d47c6e1bceef293089edf882162812dfa1b diff --git a/.agents/notes/implemented/feature/2026-09-20-default-workspace.md b/.agents/notes/implemented/feature/2026-09-20-default-workspace.md index d2a32d47a4..376b2eaf7a 100644 --- a/.agents/notes/implemented/feature/2026-09-20-default-workspace.md +++ b/.agents/notes/implemented/feature/2026-09-20-default-workspace.md @@ -21,6 +21,7 @@ Creation failure preserves the draft and offers the existing folder picker. A su ## Alternatives considered - Creating on application startup would write directories for users who never send a message. +- Falling back to `/Documents` when system lookup is unavailable would choose an unverified Documents location; deployments that need another directory use the explicit Host override. - Inferring first use from visible sidebar rows would ignore archived, hidden, and cwd-less Sessions. - Using the localized path as the initialization marker would allow language changes or deletion to create another default. - Changing a Session's cwd after creation would change the meaning of its recorded tools and attachments. diff --git a/.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md b/.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md index 6b0ec542da..e16c5d47c6 100644 --- a/.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md +++ b/.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md @@ -21,6 +21,7 @@ Client 在发送时按其语言解析初始目录名和标题。Host 控制器 ## Alternatives considered - 在应用启动时创建,会为从未发送消息的用户写入目录。 +- 系统查询不可用时回退到 `/Documents`,会选择未经确认的 Documents 位置;需要其他目录的部署使用显式 Host 覆盖配置。 - 根据侧栏可见行推断首次使用,会忽略已归档、隐藏和无 cwd 的 Session。 - 将本地化路径作为初始化标记,会允许切换语言或删除后再次创建默认工作区。 - 在 Session 创建后更改 cwd,会改变其已记录工具和附件的含义。 diff --git a/docs/subsystems/workspace.i18n.yaml b/docs/subsystems/workspace.i18n.yaml index a7a3e3bb91..f9bef3d306 100644 --- a/docs/subsystems/workspace.i18n.yaml +++ b/docs/subsystems/workspace.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/workspace.md -workspace.md: b68acd965b55df462a0751d0b7e3230c82a62172 -workspace.zh.md: 074f59438eb16a4ffb1423540c3e1868857ae40f +workspace.md: 43a631ad0b04dd272436e272c931c2a16b7bbe88 +workspace.zh.md: c4571083f3e445828b4b3b41a27ed8abee9ee04d diff --git a/docs/subsystems/workspace.md b/docs/subsystems/workspace.md index b68acd965b..43a631ad0b 100644 --- a/docs/subsystems/workspace.md +++ b/docs/subsystems/workspace.md @@ -485,6 +485,7 @@ async create(path: string, title?: string): Promise * @param resolveDirectory - resolve the absolute directory and initial title; * called only for eligible creation, inside the registry mutation queue. * Missing directories are created recursively before registration. + * After resolution, caller cancellation does not roll back creation or registration. * @returns the initialized Workspace, or undefined when automatic creation is ineligible. */ initializeDefault(resolveDirectory: () => Promise<{ path: string; title: string }>): Promise diff --git a/docs/subsystems/workspace.zh.md b/docs/subsystems/workspace.zh.md index 074f59438e..c4571083f3 100644 --- a/docs/subsystems/workspace.zh.md +++ b/docs/subsystems/workspace.zh.md @@ -485,6 +485,7 @@ async create(path: string, title?: string): Promise * @param resolveDirectory - resolve the absolute directory and initial title; * called only for eligible creation, inside the registry mutation queue. * Missing directories are created recursively before registration. + * After resolution, caller cancellation does not roll back creation or registration. * @returns the initialized Workspace, or undefined when automatic creation is ineligible. */ initializeDefault(resolveDirectory: () => Promise<{ path: string; title: string }>): Promise diff --git a/packages/api/workspace-controller/README.i18n.yaml b/packages/api/workspace-controller/README.i18n.yaml index e7bc836965..6300d2f2e4 100644 --- a/packages/api/workspace-controller/README.i18n.yaml +++ b/packages/api/workspace-controller/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/api/workspace-controller/README.md -README.md: 275c51d0018dda5b2209cc7bece71b618e7d113d -README.zh.md: 92ca3f86f11ce77b51f834ce7c24f195095065b1 +README.md: 24bf0610689bfb857b24e7c8298b1b0494f1ca02 +README.zh.md: ca140abfdda246b62b0485e63ebfd05ab2efe6f9 diff --git a/packages/api/workspace-controller/README.md b/packages/api/workspace-controller/README.md index 275c51d001..24bf061068 100644 --- a/packages/api/workspace-controller/README.md +++ b/packages/api/workspace-controller/README.md @@ -29,7 +29,7 @@ The Client entry provides `ClientWorkspaceModel` and `createWorkspaceStateStream ### First-use Workspace -`workspace.initializeDefault({ directoryName, title })` returns the durable default Workspace; the Client service exposes the same request as `workspaces.initializeDefault(request, signal?)`. The [Conversation Client](../../client/ui-conversation/README.md) resolves these names from its send-time language. The Host places the directory under its account's `/deepseek-harness`, including on remote Web hosts. The directory name must be one non-blank segment without separators, colon, NUL, surrounding whitespace, or a trailing dot; the title must be non-blank. Linux system lookup requires `xdg-user-dir` with an enabled Documents directory. +`workspace.initializeDefault({ directoryName, title })` returns the durable default Workspace; the Client service exposes the same request as `workspaces.initializeDefault(request, signal?)`. The [Conversation Client](../../client/ui-conversation/README.md) resolves these names from its send-time language. The Host places the directory under its account's `/deepseek-harness`, including on remote Web hosts. The directory name must be one non-blank segment without separators, colon, NUL, surrounding whitespace, or a trailing dot; the title must be non-blank. OS filename restrictions also apply. Linux system lookup requires `xdg-user-dir` with an enabled Documents directory; hosts without it must configure `documentsDirectory` or use the folder picker. The [Workspace registry](../../workspace/workspace/README.md#first-use-workspace) owns eligibility, directory creation, and durable initialization. An existing default Workspace is returned without another Documents lookup; request names do not rename it. Invalid names or ineligible first use reject with `gateway/bad-request`; lookup and creation failures use standard Remote error handling. Initialization creates no Session and sends no message. @@ -38,6 +38,8 @@ The [Workspace registry](../../workspace/workspace/README.md#first-use-workspace | `documentsDirectory` | System Documents directory | Fully qualified Host directory override | | `documentsLookupTimeoutMs` | `10000` | Positive maximum duration of OS directory lookup, in milliseconds | +Documents lookup holds the registry mutation queue, so other Workspace mutations, including registration of a picked directory, can wait up to `documentsLookupTimeoutMs`. Cancellation can stop the lookup; after resolution succeeds, it does not roll back creation or registration. + ----- diff --git a/packages/api/workspace-controller/README.zh.md b/packages/api/workspace-controller/README.zh.md index 92ca3f86f1..ca140abfdd 100644 --- a/packages/api/workspace-controller/README.zh.md +++ b/packages/api/workspace-controller/README.zh.md @@ -29,7 +29,7 @@ Client 入口提供 `ClientWorkspaceModel` 和 `createWorkspaceStateStream()`。 ### 首次使用工作区 -`workspace.initializeDefault({ directoryName, title })` 返回持久化的默认工作区;Client service 通过 `workspaces.initializeDefault(request, signal?)` 提供同一请求。[Conversation Client](../../client/ui-conversation/README.zh.md)按发送时的语言解析这些名称。Host 将目录放在其账户的 `/deepseek-harness` 下,远程 Web Host 也遵循此规则。目录名必须是非空的单个片段,不能含分隔符、冒号、NUL、首尾空白或末尾句点;标题不能为空。Linux 系统查询要求存在 `xdg-user-dir` 且启用了 Documents 目录。 +`workspace.initializeDefault({ directoryName, title })` 返回持久化的默认工作区;Client service 通过 `workspaces.initializeDefault(request, signal?)` 提供同一请求。[Conversation Client](../../client/ui-conversation/README.zh.md)按发送时的语言解析这些名称。Host 将目录放在其账户的 `/deepseek-harness` 下,远程 Web Host 也遵循此规则。目录名必须是非空的单个片段,不能含分隔符、冒号、NUL、首尾空白或末尾句点;标题不能为空。操作系统的文件名限制同样适用。Linux 系统查询要求存在 `xdg-user-dir` 且启用了 Documents 目录;不具备该条件的 Host 必须配置 `documentsDirectory` 或使用文件夹选择器。 [Workspace 注册表](../../workspace/workspace/README.zh.md#first-use-workspace)负责资格判断、目录创建和持久化初始化。已有默认工作区直接返回,不再查询 Documents;请求中的名称不会将其重命名。名称无效或不满足首次使用条件时,以 `gateway/bad-request` 拒绝;查询和创建失败遵循标准 Remote 错误处理。初始化不创建 Session,也不发送消息。 @@ -38,6 +38,8 @@ Client 入口提供 `ClientWorkspaceModel` 和 `createWorkspaceStateStream()`。 | `documentsDirectory` | 系统 Documents 目录 | 完全限定的 Host 目录覆盖值 | | `documentsLookupTimeoutMs` | `10000` | 操作系统目录查询的正数最大时长,单位为毫秒 | +Documents 查询占用注册表变更队列,因此其他 Workspace 变更(包括登记已选目录)最多可能等待 `documentsLookupTimeoutMs`。取消可以停止查询;解析成功后,取消不会回滚创建或登记。 + ----- diff --git a/packages/api/workspace-controller/src/types.ts b/packages/api/workspace-controller/src/types.ts index e69c2b8c91..97188d9f23 100644 --- a/packages/api/workspace-controller/src/types.ts +++ b/packages/api/workspace-controller/src/types.ts @@ -56,7 +56,7 @@ export interface WorkspaceCreateRequest { /** Names chosen by the first-use Client before default Workspace initialization. */ export interface WorkspaceInitializeDefaultRequest { - /** Single non-blank name; no separators, colon, NUL, surrounding whitespace, or trailing dot. */ + /** Host rejects blank names, separators, colon, NUL, surrounding whitespace, and trailing dots; OS filename restrictions also apply. */ readonly directoryName: string /** Initial display title, retained across language changes. */ readonly title: string diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index eac1fcac40..b89f2f3ada 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -3376,7 +3376,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ { signature: 'initializeDefault(resolveDirectory: () => Promise<{ path: string; title: string }>): Promise', description: 'Initialize the default Workspace only while both the registry and Session history are empty. Repeated requests reuse its durable identity; deleting that registration permanently disables automatic creation.', - parameters: [{ name: 'resolveDirectory', description: 'resolve the absolute directory and initial title; called only for eligible creation, inside the registry mutation queue. Missing directories are created recursively before registration.' }], + parameters: [{ name: 'resolveDirectory', description: 'resolve the absolute directory and initial title; called only for eligible creation, inside the registry mutation queue. Missing directories are created recursively before registration. After resolution, caller cancellation does not roll back creation or registration.' }], returns: 'the initialized Workspace, or undefined when automatic creation is ineligible.', }, { diff --git a/packages/workspace/workspace/README.i18n.yaml b/packages/workspace/workspace/README.i18n.yaml index 43e9c7d159..7f50ab3f55 100644 --- a/packages/workspace/workspace/README.i18n.yaml +++ b/packages/workspace/workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/workspace/workspace/README.md -README.md: f11d6496c04cf346c21f869e00ec5fb5526008db -README.zh.md: 3d458e13157bda612c3d33d0e7bfb57fa914e9b3 +README.md: 897aca8eb725bc9c3e0808e394e5c976886dd877 +README.zh.md: dc7a81340c9b134110975e700d43d5569fa6f03d diff --git a/packages/workspace/workspace/README.md b/packages/workspace/workspace/README.md index f11d6496c0..897aca8eb7 100644 --- a/packages/workspace/workspace/README.md +++ b/packages/workspace/workspace/README.md @@ -66,7 +66,7 @@ ctx.workspaceRegistry.list() // shows the project, newest first The directory resolver runs inside the mutation queue only when creation is eligible. It returns an absolute path and initial title; the registry creates missing parent directories, canonicalizes the path, rechecks Session history, and commits the Workspace with its initialization marker. An existing directory is reused; a file conflict or directory failure rejects initialization. The [Host controller](../../api/workspace-controller/README.md#first-use-workspace) supplies the Documents path policy. -The first successful registration records its identity durably. Repeated calls return it without resolving a directory again; renaming keeps that identity, and deleting its registration does not permit another automatic creation. Directory or registration failure leaves initialization unset for retry. Directories created before a later failure remain on disk. The [first-use decision](../../../.agents/notes/implemented/feature/2026-09-20-default-workspace.md) explains this lifetime. +The first successful registration records its identity durably. Repeated calls return it without resolving a directory again; renaming keeps that identity, and deleting its registration does not permit another automatic creation. Directory or registration failure leaves initialization unset for retry. Directories created before a later failure remain on disk. Once directory resolution succeeds, caller cancellation does not roll back directory creation or registration. The [first-use decision](../../../.agents/notes/implemented/feature/2026-09-20-default-workspace.md) explains this lifetime. ### Grouping sessions under a project diff --git a/packages/workspace/workspace/README.zh.md b/packages/workspace/workspace/README.zh.md index 3d458e1315..dc7a81340c 100644 --- a/packages/workspace/workspace/README.zh.md +++ b/packages/workspace/workspace/README.zh.md @@ -66,7 +66,7 @@ ctx.workspaceRegistry.list() // shows the project, newest first 目录解析器仅在允许创建时于变更队列内运行。它返回绝对路径和初始标题;注册表创建缺失的父目录、规范化路径、重新检查 Session 历史,再一起提交 Workspace 和初始化标记。已存在的目录直接复用;文件冲突或目录操作失败时拒绝初始化。[Host 控制器](../../api/workspace-controller/README.zh.md#first-use-workspace)提供 Documents 路径策略。 -首次成功登记会持久保存工作区身份。重复调用直接返回它,不再解析目录;改名保留该身份,删除登记也不会允许再次自动创建。目录或登记失败时,初始化状态保持未设置,可以重试。后续步骤失败前已创建的目录会保留在磁盘上。[首次使用决策](../../../.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md)说明这一生命周期。 +首次成功登记会持久保存工作区身份。重复调用直接返回它,不再解析目录;改名保留该身份,删除登记也不会允许再次自动创建。目录或登记失败时,初始化状态保持未设置,可以重试。后续步骤失败前已创建的目录会保留在磁盘上。目录解析成功后,调用方取消操作不会回滚目录创建或登记。[首次使用决策](../../../.agents/notes/implemented/feature/2026-09-20-default-workspace.zh.md)说明这一生命周期。 ### 将会话归入项目 diff --git a/packages/workspace/workspace/src/index.ts b/packages/workspace/workspace/src/index.ts index 6cb179022a..d0f3d3b8a5 100644 --- a/packages/workspace/workspace/src/index.ts +++ b/packages/workspace/workspace/src/index.ts @@ -182,6 +182,7 @@ export class WorkspaceRegistry extends Service { * @param resolveDirectory - resolve the absolute directory and initial title; * called only for eligible creation, inside the registry mutation queue. * Missing directories are created recursively before registration. + * After resolution, caller cancellation does not roll back creation or registration. * @returns the initialized Workspace, or undefined when automatic creation is ineligible. */ initializeDefault(resolveDirectory: () => Promise<{ path: string; title: string }>): Promise {