概览
@kagurazakayashi/dsh-delete-session
README / ZH
插件文档
目录摘要
dsh.pub 核对固定版本的组合包契约、运行时事实与分发语义;完整 README 请查看源仓库。
在 GitHub 阅读完整 READMELIMITATIONS
已知限制
- **Running sessions refused**: `ctx.agents.get(id)?.status !== 'idle'` returns 409, and a second check runs right before deletion. Only idle sessions resident in memory are exempt. - **Auto-archive before delete**: the destructive `rm` is preceded by `workspaceRegistry.archiveSession(id)`, which lets the sidebar hide the session immediately via the `host/archived-sessions-changed` broadcast (and lets the client auto-clear the current selection), so it does not linger in the list until restart. Archiving is idempotent; a failure does not block the main deletion flow. - **Session locating strategy**: the core no longer exposes the `supportsRawArtifacts` flag. The plugin first calls the JSONL backend's public API `resolveCurrentLog(id)` (async, returning the absolute path of the current-format-generation log); for sessions that have not been migrated yet — filenames without a `vN` marker — it returns `undefined` (on this machine only 4 of 89 existing sessions were in the new format), so the plugin falls back to the backend's still-present-at-runtime `locate(header)` (returning `{ kind, path }`). When neither yields a path it returns 501. If the backend throws an error carrying `kind: 'jsonl'` and `path` diagnostics (for example a log in a newer version), the plugin reuses that path to complete the deletion. - **Deletes only the session directory**: `rm(dirname(logPath), { recursive: true, force: false })`; an archive marker is written first, but workspace groups, projection caches, and shared attachments are left untouched. Before deleting, the target directory name must be **exactly the session id** (the backend names it with `encodeSegment(id)`, and session ids only contain `[A-Za-z0-9._-]`), so the whole project directory can never be recursively removed. - **Irreversible**: deletion is a recursive `rm` with no recycle bin; operate with care. - **In-memory two-step state** (`click-again` mode only): the warning state lives only in browser memory (per session id, 8-second window) and disappears on plugin unmount, page refresh, or timeout; no persistent state is produced. Switching to another mode stops showing it. - **Official session-menu slot, no DOM injection**: the plugin contributes a normal entry to the `sidebar.workspaces.session.menu.item` list (registration id `delete-session`, order `450`), which the host menu renders as one `role="menuitem"` button. The slot supplies `{ sessionId, displayTitle }` plus a `useMenuOpenState` hook, so the plugin receives the exact session id and never captures the row's `…` button click, runs a `MutationObserver` over `document.body`, parses the row's `aria-label` (`会话“{name}”的操作` / `Session actions for {name}`), re-implements the core's relative-time bucketing, or clones the "Archive session" item. Keyboard traversal, focus return and menu dismissal are handled by the host menu, so no DOM structure and no localized text is depended upon. - **Availability follows the slot**: the item shows up wherever the host renders the session menu; the plugin waits for that slot to be declared, so a deployment that does not compose `@deepseek-ai/dsh-client-ui-workspace` leaves no trace of it.
