* feat(workspace): stop a Session's running work before archiving it Archiving was a pure visibility write, so a Session archived mid-turn kept running out of sight. The Workspace registry now owns archive admission through two Host events: `workspace/session-activity` reports what still runs and refuses the plain archive as `workspace/session-active`; `workspace/session-stop`, requested with `stopActivity`, stops that work the way the user's own stop actions do before the archive write. The API Session Controller reports and stops turns, subagent descendants, and owned jobs and rejects any model step for an archived Session; the Schedule plugin reports and deletes active reminders. The sidebar turns the refusal into a stop-and-archive confirmation listing the Host's answer. * test(workspace): pin the widened archive signature and prove child liveness by heartbeat The Client inspect catalog spec pins archiveSession's new options parameter, and the archive e2e reads the Bash child's liveness from a heartbeat file instead of a pid: the Linux sandbox runs the tool in its own PID namespace, so the pid it reports is not comparable from the test. * test(workspace): cover the archive-admission providers and dialog to the per-file gate Drop the provider's unused disposer and the dialog's unreachable confirm guard, read reminders without a catch that no composition can reach, and add the cases the coverage lane found missing: a stop without a jobs registry, a looping lineage, a descendant whose projections reject, the dialog's Escape guard and non-Error reason, and a family merged by another provider. * fix(workspace): write the archive before stopping, gate subagent lineage, and delete reminders under the schedule barriers Review round on #4765. The registry now writes the archive set before it dispatches `workspace/session-stop`, so the durable set already gates every wake the stops induce; the controller's `agent/pre-step` gate also blocks subagent descendants of an archived Session and contains each cancel or kill on its own. The Schedule plugin's stop listener runs as a management delete in the agent's transaction queue between the same two persistence barriers the `schedule_delete` tool uses. The confirm dialog selects singular or plural English lines and keys list rows by index. Docs: archive-admission section with type-equiv blocks on the workspace subsystem page, write-first order and residual windows in the READMEs and the Agent Note, and the missing zh reminders sentence. * test(web): scope the question composer's New Session wait to the Sessions tree The wide sidebar's New Session button carries the same visible label as a fresh session row, so the unscoped `getByText` either settled on the button before the row existed or hit a strict-mode violation once the row rendered first, which is how the snapshots lane failed on ece68c2ab5. Waiting inside the Sessions tree names the row alone. * fix(schedule): answer archive admission from the owned runtime's fold, not the projection registry The `sessionProjections.snapshot` API is the Client projection view and is not a Host read path (review on #4765). `ScheduleRuntime.activeRecords()` folds the live log the way every drive does, and the archive-admission listeners ask the owned runtime for that fold: a session without a live agent or without an owned runtime has nothing armed to fire, so it reports nothing and has nothing to stop. Covers the runtime-less agent, a fold that turns unreadable at the persistence barrier, and the stopping/faulted runtime answering nothing. * refactor(subagent): report and stop subagent descendants from the Subagent runtime The `subagent` family of archive admission moves out of the API Session Controller into `dsh-subagent`, which records the lineage at delegation and owns the descriptor (review on #4765): `SubagentRuntime` answers `workspace/session-activity` with the running descendants, labelled from each child's descriptor through a live Session observation, and `workspace/session-stop` by cancelling each of them with the parent cause. The controller keeps turns, owned jobs, and the lineage step gate, and composes its provider through `ctx.plugin(SessionActivityProvider)` with declared injections instead of a bare installer call. `dsh-workspace` becomes an optional type-only peer of `dsh-subagent`. * refactor(jobs): answer archive admission for owned jobs from the job registry seam The `job` family moves out of the API Session Controller into `dsh-jobs` (review on #4765): the seam's constructor installs the `workspace/session-activity` and `workspace/session-stop` listeners for every implementation through the abstract `list` and `kill` alone, so the controller no longer knows the jobs vocabulary and keeps only the turn family and the archived-lineage step gate. `dsh-workspace` becomes an optional type-only peer of `dsh-jobs`. * refactor(workspace): leave SessionActivityKindMap empty and let each provider merge its family The workspace package no longer names any activity family (review on #4765): `SessionActivityKindMap` is empty, and each provider merges its own key from a module both its faces import — `turn` in the API Session Controller's types, `job` in the job registry seam's view, `subagent` in the Subagent runtime's control types, `schedule` in the Schedule plugin's types. The confirm dialog imports those modules for its cases and keeps the generic line for any other key; the registry and controller suites merge test-owned families instead of borrowing the providers'. * refactor(agent): report and stop the running turn from the Agent registry The `turn` family moves out of the API Session Controller into `dsh-agent` (review on #4765): the registry's constructor installs the `workspace/session-activity` and `workspace/session-stop` listeners for every Agent it publishes, answering with its own lookup and cancelling the turn with the user cause, and merges the `turn` key from its client-safe types. The controller keeps only the archived-lineage step gate, renamed `ArchivedSessionGate`. `dsh-workspace` becomes an optional type-only peer of `dsh-agent`; the workspace package depends on none of the providers, so the edge stays acyclic. Since #3127 a foreground Bash command is a registered job from its start, so the archive dialog lists it beside the turn; the e2e and its ARIA golden expect that row, and the capture keeps the running job by design.
5.2 KiB
description, kind
| description | kind |
|---|---|
| Host 与 Client 工作区控制:修改工作区导航并跟随其完整投影。 | package-reference |
Workspace Controller
English | 中文
概述
@deepseek-ai/dsh-api-workspace-controller 拥有 Host 的 ctx.workspaceController 服务和生成的 Client ctx.remote.workspace namespace。它的 Remote 方法负责创建、重命名、移除和重排 Workspace,在 Workspace 内重排 Session,归档与取消归档 Session,以及跟随完整的 Workspace 投影。当 Client 必须修改或跟随 Workspace 导航时,请通过 API 网关使用它。本包同时拥有 ctx.directoryPickerController 与生成的 ctx.remote.directoryPicker namespace,因为它承载的选目录 seam 是抽象的,自身从不作为 Loader entry。
目录
使用本包
Host 控制器会串行执行正确性取决于当前注册表状态的变更,并为预期失败抛出带有稳定错误码的 RemoteError。它的 follow() 流会同步订阅持久 Workspace 变更,先发出一份完整 baseline,再按顺序发出 upsert、remove、order、archived 和 pinned 增量。归档与置顶集合都是会话 id 数组,置顶数组把最近置顶的 id 放在前面。重连会以替换 baseline 开始新一代,因此消费方不依赖收到断线期间的每个增量。不带 stopActivity 的 archiveSession 会以 workspace/session-active 拒绝仍有工作在跑的会话,其 details 按族(turn、subagent、job、schedule)列出这些工作及各项的 id 与名称;带 stopActivity: true 时注册表的提供方先停止这些工作,归档集合持久化后即返回响应,停止在后台收敛。
Client 入口提供 ClientWorkspaceModel 和 createWorkspaceStateStream()。该模型拥有 Workspace 行、registry 顺序、归档与置顶会话身份、一元变更回显,以及流与一元调用的竞态处理。较新的 Host 行按 updatedAt 获胜;已提交的流顺序优先于较旧的一元响应;已经移除的 Workspace id 不会被延迟数据复活。置顶快照仅在会话身份或顺序变化时更新。该包公开与框架无关的快照和订阅,把导航策略与 React 钩子留给 UI owner。WorkspaceController.archiveSession(sessionId, { stopActivity }) 抛出携带 Host rpcError 的 WorkspaceArchiveError,界面因此能区分"仍有工作在跑"的拒绝与会话缺失或载体故障,并提议停止这些工作。
首次使用工作区
workspace.initializeDefault({ directoryName, title }) 返回持久化的默认工作区;Client service 通过 workspaces.initializeDefault(request, signal?) 提供同一请求。Workspace Client按启动时的语言解析这些名称。Host 将目录放在其账户的 <Documents>/deepseek-harness 下,远程 Web Host 也遵循此规则。目录名必须是非空的单个片段,不能含分隔符、冒号、NUL、首尾空白或末尾句点;标题不能为空。操作系统的文件名限制同样适用。Linux 系统查询要求存在 xdg-user-dir 且启用了 Documents 目录;不具备该条件的 Host 必须配置 documentsDirectory 或使用文件夹选择器。
Workspace 注册表负责资格判断、目录创建和持久化初始化。已有默认工作区直接返回,不再查询 Documents;请求中的名称不会将其重命名。不满足首次使用条件时返回 undefined,启动流程可将目录选择留给用户。名称无效时以 gateway/bad-request 拒绝;查询和创建失败遵循标准 Remote 错误处理。初始化不创建 Session,也不发送消息。
| 配置 | 默认值 | 用途 |
|---|---|---|
documentsDirectory |
系统 Documents 目录 | 完全限定的 Host 目录覆盖值 |
documentsLookupTimeoutMs |
10000 |
操作系统目录查询的正数最大时长,单位为毫秒 |
Documents 查询占用注册表变更队列,因此其他 Workspace 变更(包括登记已选目录)最多可能等待 documentsLookupTimeoutMs。取消可以停止查询;解析成功后,取消不会回滚创建或登记。
模型体验
无,因为 Workspace 组织属于浏览器和 Host 的控制状态,并且不注册提示词、工具或会话事件。
KV Cache 影响
无直接影响;Workspace 变更不会改变模型请求。
已知限制与延期工作
follow()在重连后替换完整投影,不提供持久 cursor 或增量追赶协议。- 进程内删除标记只会在 Client 模型生命周期内阻止延迟数据复活已移除的 Workspace。
开发备注
维护者工作上下文——点击展开
无。
运行时不变式: 不发布伴生入口。Workspace 注册表负责持久化,每次流生成都是完整投影。