# Isolation Pod > Run sandbox-constrained isolated tasks outside the DSH main session. The main session stays > **completely unaware** by default: no messages are written, nothing enters its model context, > and no automatic status appears in its UI. Returning results and exporting files both require > an explicit user confirmation. [简体中文](README.md) | English | Item | Value | |---|---| | Version | **v0.2.0** (Host `build: 2`) | | Sidecar protocol / DB schema | `1` / `1` | | DSH support | **DSH 0.2.1+** (validated on desktop 0.2.1-rc.2; 0.1.7 is unsupported; v0.1.3 is the last 0.1.x release) | | Shape | Rust sidecar (`isolation-pod-core`) + thin JS Host + official-conversation Client | | Dependencies | No runtime npm dependencies; the sidecar statically links SQLite | | Platforms | Windows x64 locally validated; Linux x64 and macOS x64/arm64 built by CI (no local panel acceptance) | | License | MIT, see [`LICENSE`](LICENSE) | ## Document index | Document | Audience | Content | |---|---|---| | `README.en.md` (this file) | Users, plugin developers | Features, architecture, install, usage, config, API, data, limitations, development | | [`README.md`](README.md) | Chinese readers | Chinese version | | [`使用教程.md`](使用教程.md) | First-time users | Step-by-step walkthrough, troubleshooting, acceptance checklist (Chinese) | | [`需求说明.md`](需求说明.md) | Requirements / acceptance | Frozen functional requirements baseline (Chinese) | | [`CHANGELOG.md`](CHANGELOG.md) | Everyone | Version history (Keep a Changelog) | | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Maintainers | Layers, process boundaries, state ownership, design decisions | | [`docs/PROTOCOL.md`](docs/PROTOCOL.md) | Maintainers | Field-level sidecar/Host/Client contract | | [`docs/MIGRATION.md`](docs/MIGRATION.md) | Upgrading users | v0.1.3 → v0.2.0 migration, rollback, FAQ | | [`NOTICE.md`](NOTICE.md) | Compliance | Third-party dependencies and licenses | ## Features - **Zero automatic trace in the main session**: a task runs in its own normal root session; it never writes messages or context into the main session. The only injection point is the user-confirmed "Return to main session" action. - **Official conversation UI**: opening a task calls `uiWorkspace.openSession(sessionId)`, so DSH's official conversation panel renders the transcript, tool cards, reasoning, and composer. The pod does not re-implement chat rendering. - **DSH-enforced sandbox**: the task session cwd is the sandbox root with `sandbox/mode=workspace-write`; writes outside the root are hard-denied by the DSH filesystem sandbox. A pre-flight out-of-sandbox write probe must also be denied before the first model turn. - **Rust core**: configuration, task state machine, FIFO queue, per-turn deadlines, leases, file listing/preview, exports/undo, v0.1.3 migration, audit, and diagnostics live in the sidecar. The Host keeps only DSH integration and process-local handles. - **Queue and restart recovery**: tasks above the concurrency limit queue automatically; after a host restart, stored task sessions can be reopened and resumed. - **Safe exports**: copy/move, overwrite/skip/rename, BLAKE3 hashes, backups before overwrite, conflict-aware undo when a hash is available. Every export needs its own confirmation. - **Enforced tool allowlist**: the allowlist is an upper bound enforced by `tools.guard`; a missing preset never empties it silently. - **Cross-platform core**: the plugin's own directory/file/export/cleanup work no longer uses PowerShell; the Rust sidecar implements it once for Windows, Linux, and macOS. - **Observable**: `/status`, a diagnostics tab, core restart, stderr ring, protocol/schema checks. ## Architecture ### Three layers ``` Client (React panel / task action bar / resume card) │ /isolation-pod/api (per-activation token) JS Host (Cordis plugin) ├─ talks to the Rust sidecar over stdio NDJSON └─ uses DSH services: agents / sessions / sessionQuery / sandboxPolicy / fs / presets Rust sidecar (SQLite / queue / sandbox paths / files / exports / migration) ``` Opening a task calls Host `openTask`, then `ctx.uiWorkspace.openSession(sessionId)` selects the official `conversation` main panel. The complete transcript is rendered from the DSH session log; the pod stores only task state, summaries, audit, and export records. See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and [`docs/PROTOCOL.md`](docs/PROTOCOL.md) for details. ### Task lifecycle 1. The panel submits a description, optional sandbox subdirectory, and main session. 2. The sidecar creates `//`, takes a concurrency slot, or queues. 3. The Host creates a **normal root session** with cwd = sandbox root. In its setup window it appends `sandbox/mode`, `approval/policy=never`, mounts the preset, installs the tool guard, and injects the system-prompt rules. 4. After `task.markRunning`, the Host runs the out-of-sandbox probe; once it passes, the Host sends one clean user message: `任务:` (Task: ...). 5. The official conversation streams the run; the task action bar exposes panel/files/return/ export/stop. 6. Follow-ups, manual return, export, and cleanup are all user-driven; the main session is never written automatically. ## Requirements - **DSH 0.2.1+** (desktop or `dsh web`; locally validated on desktop 0.2.1-rc.2). - A profile with the Web UI; a CLI/TUI profile without `webServer` does not activate the plugin. - Node.js supplied by DSH; no npm package install is required at runtime. - A Rust sidecar binary: - Windows x64: the repository ships `lib/bin/win32-x64/isolation-pod-core.exe`; - other platforms: use the matching Release package, or build from source with Rust stable ≥ 1.85. - Optional: `ISOLATION_POD_CORE_BIN` to point at a custom sidecar binary. ## Install ### Option A: bundle (recommended) 1. DSH sidebar → **Plugins**; 2. **Add plugin** → **Install third-party plugin** (npm package, Git repository, or local directory); 3. Select the absolute path of this repository (or the extracted Release directory) and install; 4. Confirm that the `dsh-isolation-pod` bundle and its plugin row are enabled; 5. If the page says "restart DSH to apply changes", restart once. Host modules (`lib/index.js`) are cached by URL by Node, so a changed Host half requires a restart; only Client-half changes can hot-reload. ### Option B: patch line (development/fallback) Append to `\profiles\\cordis.patch.yml`: ```yaml - insert: - id: dsh-isolation-pod name: '\lib\index.js' ``` The host reapplies the profile; Host changes still require a restart. **Never enable both a bundle and this patch line**: the Host duplicate-activation guard ignores the second instance, but duplicate Client entries and confusing task/config state remain. Keep one installation method only. ### Verify the install ```powershell curl.exe -s http://127.0.0.1:/isolation-pod/status ``` Expect `pluginVersion: "0.2.0"`, `coreVersion: "0.2.0"`, `protocol: 1`, `schemaVersion: 1`, `coreStatus: "ready"`, and `build: 2`. A sidebar entry named Isolation Pod should appear. ### Upgrade / roll back - **Upgrade**: replace the package, reinstall/update the bundle, or `git pull && pnpm install && pnpm run build`; restart DSH after Host changes. - **Roll back to v0.1.3**: see [`docs/MIGRATION.md`](docs/MIGRATION.md) §5. New data is only in `pod.db`, which the old version does not read. - Do not edit a live `pod.db`; use a read-only SQLite tool only while DSH is stopped. ## Quick start 1. **Configure the sandbox root**: Config → enter an absolute path **outside the workspace** (for example `E:\pod-work`) or pick a directory; save. 2. **Start a task**: New → describe the task (optional subdirectory) → choose the main session → "Start isolated task". 3. **Watch it run**: the official conversation opens automatically; alternatively click "Open conversation" in the task list. 4. **Continue**: type in the official composer; sandbox, approval, guard, and system prompt stay in effect. 5. **Stop**: use the task action bar or the official composer's stop; the task becomes cancelled. 6. **Return to the main session** (optional): task action bar → Return → edit the preview → confirm. The main session receives exactly one concise user message. 7. **Export** (optional): task action bar → Export → select files, choose an absolute target outside the sandbox, mode/conflict policy → confirm. Every export is confirmed separately. 8. **Clean up**: task list/detail → Clean up; delete sandbox files only, or delete the record together with files (the HTTP `cleanup` method also has a `record` scope; the task panel has no separate record-only button). The full walkthrough is in [`使用教程.md`](使用教程.md) (Chinese). ## UI guide ### Main panel (sidebar "Isolation Pod") | Tab | Content | |---|---| | **Tasks** | Search/filter (all, active, finished, failed); rows show status, turns, directory, live/queue info; actions: open conversation, detail panel, files, stop/resume, cleanup; bulk cleanup of finished tasks. | | **New** | Description, optional subdirectory, main-session selector; sandbox/preset/queue hints. | | **Config** | Sandbox root, allow writes, tool allowlist, timeout/concurrency/queue, export options, preset, preview/probe/auto-resume/log level. | | **Exports** | Per-export target, mode, conflict policy, per-item status; copy exports can be undone. | | **Diagnostics** | Core/Host/protocol/schema facts, DB/binary paths, stderr/diagnostics tail, compatibility, "Restart core". | ### Isolation widgets inside the official conversation - **Task action bar** (`conversation.input.dock`, id `isolation-pod-task`): status chip, panel, files, return, export, stop, and a Fork warning. - **Header button** (`conversation.session.header.utilities`, id `isolation-pod`): opens the panel. - **Resume card** (`conversation.composer` chain, priority −5): takes over the composer only while the task session is not live; shows a read-only explanation and "Resume isolated session". A live pod session uses the official composer unchanged. ### Main session vs task session - A task session is a **normal root session**, never a subagent; it is not added to any workspace and does not appear in the sidebar workspace list. It may appear in DSH session search/history. - Opening a task makes the official main view show that task session; this is the task view, not the user's main session. - Returning to the main session and reading main-session context use the task's recorded `mainSessionId`; the new-task selector uses the user main-session list (pod task sessions and subagent sessions are excluded). - The warning "sandbox root is inside the main-session workspace" only appears when the **user's main session** cwd really contains the sandbox root. The task session cwd (= sandbox root) never triggers it. ## Tasks, queue, recovery ### Statuses | Status | Meaning | |---|---| | `queued` | Waiting for a concurrency slot | | `starting` | Slot taken; creating the Agent/setup | | `running` | Runtime phase (the sandbox probe runs before the first model turn) | | `completed` | Last turn completed (`max-tokens` and `forked` also settle as completed) | | `failed` | Creation/execution/tool error or blocked | | `timeout` | Per-turn deadline exceeded | | `cancelled` | User stopped/cancelled | | `interrupted` | Host restart/process gone; history remains, task can be resumed | ### Queue - `maxConcurrent` counts `starting+running`. Above the limit and with `queueEnabled=true`, tasks enter a FIFO queue and show their position; finishing/cancelling a task dispatches the next. - With `queueEnabled=false`, above the limit and with no existing queue, the call returns "busy"; if a queue already exists, new tasks still join its tail (clear the queue after toggling). - On restart, `queued/starting/running` become `interrupted`; tasks are not replayed automatically. ### Timeout - `timeoutMs` is a **per-turn** limit (30 s–2 h; default 10 min), not a whole-task limit. - The sidecar emits `deadline.expired`; the Host cancels the Agent, with a 10-second settlement grace. The Host also runs a `timeoutMs+30s` watchdog. - After a timeout, a live session can continue; the next turn gets a fresh deadline. ### Recovery - `autoResumeOnOpen=true` (default): opening a task whose session is not live makes the Host `task.reattach` + `agents.resume` and reinstall isolation, without sending any message. - If recovery fails, history still opens; the read-only resume card offers "Resume isolated session". - Migrated legacy tasks have no recoverable Agent; only their read-only records remain. - If the official UI resumes a stored pod session by itself, the Host's `agent/created` safety net installs isolation before the first turn. ### Stop - The task action bar stop and the official composer stop both cancel the current turn. - Cancelling affects only that task and never writes to the main session. ## Return to the main session - Task action bar → Return: optionally include status, the latest conclusion, or a text file; the generated preview (≤ 4000 chars) is editable. Confirm to inject. - Exactly **one** user message (≤ 8000 chars) is injected. The main session must be live, otherwise the Host returns `MAIN_SESSION_NOT_LIVE`. - Nothing is returned by default. Return and export are independent operations. - Content comes only from the task summary, user-edited text, and text files in the task directory; binary files are never injected. ## Export and undo - Task detail/action bar → Export: select files/directories from the task directory, specify an absolute target **outside the sandbox**, choose copy/move and overwrite/skip/rename, then confirm. - Each export is a single authorization; `allowRememberChoice` only pre-fills the last target and still requires confirmation. - **Undo**: only **copy** exports are undoable in the UI. Before overwriting, the existing target is copied to `/.isolation-pod-undo//`; with `exportVerify` enabled, undo checks the BLAKE3 hash (hashes are always recorded on export) and reports a conflict if the target was modified/deleted/became a link. **Legacy/no-hash records are not verified; undo deletes the target directly and cannot restore an overwritten original, so back up first.** Move mode has no UI undo. - The target cannot be inside the sandbox; copy mode never modifies sandbox sources (move deletes the source after a successful copy). - Limits: 5000 files / 2 GiB total / 1 GiB per file. ## Files and preview - The task detail Files section lists the task directory (depth ≤ 8, ≤ 2000 entries); symlinks are not followed. - Text preview supports UTF-8 and UTF-16 LE/BE with BOM, bounded by `previewBytes` and `previewChars`; binary files return a clear message instead of garbage. - Preview is read-only; absolute paths, `..`, and escapes are rejected. ## Cleanup - Task detail/list: - **Delete sandbox files only**: remove the task directory, keep the task record; - **Clean record and files**: delete the record (and audit/legacy log) and release the Agent; - **Bulk cleanup of finished tasks**: only terminal tasks are selected; optionally delete sandbox files. Running/queued tasks are not included in bulk cleanup. - The sandbox root itself is always protected; cleanup never touches the main session or files outside the sandbox. - Startup pruning uses `maxTasks`, `maxExports`, and `retentionDays` (`maxTasks`/`retentionDays` target terminal task records; `maxExports` targets export records and is also pruned after each export; task files are not deleted automatically). ## Configuration Configuration lives in the sidecar SQLite store; edit it in the Config tab or via `setConfig`. | Key | Default | Meaning | |---|---|---| | `sandboxRoot` | `''` | Required dedicated absolute directory; empty refuses tasks and never falls back to the workspace. Rejects filesystem roots, the user home, `DSH_HOME`/DB dir, the current cwd, `SystemRoot`/`ProgramFiles`, and must not contain them | | `allowWrites` | `true` | `false` runs tasks in read-only sandbox mode | | `allowedTools` | `pwsh, bash, read, write, edit, glob, grep` | Upper bound enforced by the tool guard; at least one | | `timeoutMs` | `600000` | Per-turn timeout, 30 000–7 200 000 ms | | `readContext` | `true` | Inject up to 12 recent main-session messages as read-only background | | `maxConcurrent` | `3` | 1–8 | | `queueEnabled` | `true` | Queue above the limit; when disabled, return busy only if no queue exists (an existing queue still accepts tasks) | | `enableExport` | `true` | Disable export/undo when false | | `allowRememberChoice` | `false` | Enables path pre-fill only; never an authorization | | `presetId` | `''` | Agent preset to mount; empty mounts no preset (possibly no tools) | | `maxTasks` / `maxExports` | `200` / `200` | 1–5000; `maxTasks` prunes excess terminal task records at startup, `maxExports` prunes excess export records at startup and after each export | | `retentionDays` | `0` | 0–3650; startup pruning of terminal records; files are not deleted | | `previewBytes` | `307200` | Per-file preview byte limit, 16 KiB–1 MiB (enforced by the sidecar) | | `previewChars` | `20000` | Per-preview character limit, 200–20 000 | | `exportVerify` | `true` | Verify the BLAKE3 hash on undo (hashes are always recorded on export; disabling this skips undo verification) | | `sandboxProbeMode` | `'strict'` | `strict`/`warn`; see below | | `autoResumeOnOpen` | `true` | Try to resume a non-live task session when opening it | | `logLevel` | `'info'` | trace/debug/info/warn/error/off (UI offers error/warn/info/debug; the API accepts all six) | `sandboxProbeMode`: - An **allowed** out-of-sandbox write always fails the task in both modes. - `ambiguous`/`failed` fails in `strict` and continues with a warning in `warn`. - `skipped-no-parent` (sandbox root is a drive root) passes with a warning. ## HTTP API The Host registers the `/isolation-pod` prefix: | Route | Auth | Purpose | |---|---|---| | `GET /isolation-pod/status` | none (local diagnostics) | Versions, core status, DB/binary paths, sandbox summary, compat, diagnostics; never the full token (only the 8-char `tokenPrefix`) | | `GET /isolation-pod/bootstrap` | DSH browser auth | Returns `{ok:true, token}`; 401 otherwise | | `POST /isolation-pod/api` | `x-ipp-token` header | `{method,args}`; body ≤ 256 KiB | The token is a random UUID per activation, delivered via the structured index-injection row `__DSH_IPP__`, and never written to disk. The Client recovery ladder is `__DSH_IPP__` → bootstrap → same-origin HTML scrape → one guarded reload. Failures return `{ok:false,code,message}`; 403/404/405/413 cover token/path/method/body-size errors. Methods (18): | Method | Args | Purpose | |---|---|---| | `getState` | `{mainSessionId?}` | Full panel state (config/tasks/exports/core/sandbox/compat) | | `setConfig` | `{patch}` | Validate/save config, return messages | | `startTask` | `{mainSessionId, description, subdir?, label?}` | Create a task; may queue | | `openTask` | `{taskId}` | Open/auto-resume the task conversation | | `resumeTask` | `{taskId}` | Resume a stored task conversation | | `cancelTask` | `{taskId, reason?}` | Cancel the current turn/queued task | | `cleanup` | `{taskId, scope?}` | `record`/`files`/`all` | | `bulkCleanup` | `{statuses?, olderThanMs?, deleteFiles?}` | Bulk cleanup by status (the UI only passes terminal statuses; the API does not enforce terminal-only) | | `listFiles` | `{taskId}` | Task directory listing | | `readSandboxFile` | `{taskId, relPath, maxChars?}` | Text preview | | `previewReturn` | `{taskId, parts?}` | Build the editable return preview | | `returnToMain` | `{mainSessionId, text}` | Inject one user message (after confirmation) | | `exportEntries` | `{taskId, entries, targetDir, mode?, conflict?, remember?}` | Run one export | | `listExports` | `{}` | Export records | | `undoExport` | `{exportId}` | Undo a copy export | | `getDiagnostics` | `{}` | Diagnostics/stderr/compat | | `restartCore` | `{}` | Restart the sidecar (interrupts live tasks) | | `getLegacyTask` | `{taskId}` | Read-only legacy transcript | Field-level details: [`docs/PROTOCOL.md`](docs/PROTOCOL.md). ## Data and file layout ``` / isolation-pod/ pod.db SQLite WAL (config/tasks/audit/exports/legacy log) core.log Rotating sidecar log pod.db-wal, pod.db-shm SQLite WAL runtime files isolation-pod.json v0.1.x legacy file; renamed .json.bak after migration profiles//cordis.patch.yml Development patch line, when used sessions/... DSH session logs (source of truth for the official conversation) / // Task directory ( when subdir is empty) / .isolation-pod-undo// Undo backups for overwrite exports ``` - Only the sidecar writes SQLite; the Host keeps a read-only cache. - On startup (and after each export for `maxExports`), records beyond the limit are pruned together with their undo backups; `retentionDays` only removes task records, never files. - Keep the migrated `.json.bak` file for rollback. - The sidecar log rotates at 1 MiB to `core.log.1` (current + one previous file); it never records file contents, main-session context, or the token. ## Isolation and permissions | Location/capability | Pod itself | Manual user action | |---|---|---| | Inside the sandbox | Read/write | View/export/clean | | Outside the sandbox | Read-only | Read-only by default | | Main session history/context/message stream | No injection | One confirmed return message | | Tools | Allowlist only; guard denies others | Change config (affects new tasks/turns) | | Export target | No write | Confirm a copy/move | | Temporary directories | DSH workspace-write allows them; the prompt asks for sandbox paths | Known limitation | In one line: **writable inside the sandbox, read-only outside; reads are unrestricted; writes outside the sandbox and returns to the main session require manual confirmation; every export is a separate authorization; tasks are invisible to the main session by default.** ## Platforms and binaries - Sidecar layout: `lib/bin/-/` (`win32-x64`, `linux-x64`, `darwin-x64`, `darwin-arm64`; more can be added). The directory uses Node's `process.platform` (`win32-x64`), while Release asset/zip names use `windows-x64`. - The repository commits the **Windows x64** release binary; other platforms are built by GitHub Actions and attached to the Release. `node scripts/fetch-core.mjs` downloads and SHA-256-verifies the matching asset. - Local validation: Windows x64. Linux/macOS are CI-built and were not manually panel-tested. - The binary does not ship MinGW/VCRUNTIME runtimes; its PE imports are Windows system DLLs only (UCRT API sets, kernel32/ntdll/bcrypt, etc.). It is not code-signed; SmartScreen/Gatekeeper may warn. - Current Windows x64 sidecar: 1,902,592 bytes, SHA-256 `a34a701da220fc5c1f6b74eb027ac167c22c9284e0ff460684ccd9a42d442701`. The release zip contains build timestamps, so its hash changes on rebuild; trust the accompanying `SHA256SUMS.txt`. ## Known limitations 1. **Fork escapes isolation**: the official conversation UI's Fork creates a new ordinary session without the pod's setup. The task action bar warns about it; do not use Fork to continue a pod task. 2. **DSH temporary directories**: workspace-write permits platform temp directories. The system prompt asks for sandbox paths, but the Host cannot synchronously prevent shell writes to TEMP. 3. **Legacy tasks are read-only**: migrated v0.1.3 tasks have no recoverable Agent; only their old records/files/exports are accessible. 4. **Task sessions are outside workspaces**: they are not listed in the sidebar workspace list, but may appear in DSH session search/history. They never write to the main session. 5. **Official UI commands/permission controls**: they may change model/approval settings. The Host re-asserts sandbox/approval, but the UI may briefly show the change. 6. **Shell tools**: the allowlist restricts tool names; paths written inside a shell command are enforced by the DSH sandbox, not by Host command parsing. 7. **Unsigned binaries**: Windows/macOS may show a security prompt on first run. 8. **Linux/macOS panel acceptance**: CI builds the artifacts; no local manual walkthrough was done; path/appearance differences may remain. 9. **Network**: the sidecar is offline; running the plugin needs no network, while building or fetching binaries may. 10. **DSH version**: only 0.2.1+ is supported; re-check compat after a DSH upgrade. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | No Isolation Pod sidebar entry | Plugin disabled/not restarted/no webServer | Check the Plugins page and install method; restart; use a profile with a Web UI | | `/status` unreachable | Host not loaded or wrong port | Use the desktop setting or the URL printed by `dsh web`; restart | | `coreStatus: missing/down/restarting/incompatible` | Missing/crashed/mismatched binary | Check `lib/bin/-/`; restart from Diagnostics; use one Host+sidecar version | | `CORE_UNAVAILABLE` | Sidecar not ready/restarting | Wait or restart the core in Diagnostics | | Panel stuck loading/token error | Page loaded before the plugin/token expired | Reload the page; quit and reopen the desktop app; for Web, reopen the `dsh web` URL | | Start button disabled | No sandbox root / no main session / empty description | Save an absolute root in Config; choose a main session | | `ROOT_PROTECTED` at start | Sandbox root is (or contains) a filesystem root, home, DSH data dir, cwd, or system dir | Choose a dedicated directory outside the workspace | | `BAD_SUBDIR` at start | Subdir is absolute, >8 segments, a segment >64 chars, or has an illegal/reserved name | Fix the subdir per the message | | Warning: sandbox inside main workspace | The sandbox root really is under the main session cwd | Move it outside; the task session cwd does not count | | Task stuck "running" | Model/tool is slow or timed out | Wait or stop; check Diagnostics/timeout | | Task opens with no conversation | Legacy task or session log cleaned | Use the legacy record view; rerun a new task | | Export target rejected | Target inside the sandbox / not absolute | Choose an absolute directory outside the sandbox | | Undo conflict | Target modified/deleted (and a hash exists) | Expected; use a new copy export | | Legacy undo unsafe | Legacy/no-hash records are not verified; undo deletes the target, and no overwrite backup exists | Back up the target manually; do not rely on legacy undo | | Two plugin entries | Bundle and patch line both enabled | Keep one installation method and restart | ## Development ### Layout ``` core/ Rust sidecar (Cargo) lib/ Host half + built lib/client.js + lib/bin src/client/ Client TypeScript/React sources scripts/ build-client / build-core / fetch-core / check-version / package test/ host / client / core e2e docs/ ARCHITECTURE / PROTOCOL / MIGRATION locale/ icon.svg Bundle metadata cordis.patch.yml Bundle patch ``` ### Commands ```powershell pnpm install pnpm run build:client # src/client -> lib/client.js pnpm run build:core # cargo release -> lib/bin/-/ pnpm run build # client + core pnpm run test:host # Host tests (stub DSH + fake/real sidecar) pnpm run test:client # Client tests pnpm run test:core # Rust tests pnpm run test:e2e # build sidecar + real-binary e2e pnpm run check # version/metadata/protocol/schema/locale checks pnpm run release:zip # local packaging into dist/ ``` Environment: Node 24 (build/test), pnpm, Rust stable ≥ 1.85; on Windows a C compiler is needed for `rusqlite bundled` (MSVC Build Tools or MinGW gcc). Build/test artifacts are not shipped. ### Versioning and release - Keep the version in `package.json`, `core/Cargo.toml`, `lib/version.js`, and the CHANGELOG heading identical; keep protocol/schema in sync with [`docs/PROTOCOL.md`](docs/PROTOCOL.md). - `scripts/check-version.mjs` validates metadata, locales, patch, workflows, and the Client bundle. - Release: push an annotated `vX.Y.Z` tag; GitHub Actions builds four-platform sidecars and the Client, packages zips/raw binaries/`SHA256SUMS.txt`, and creates the Release. - Host changes require a DSH restart; Client-only changes can hot-reload. ### Verification matrix | Item | Command | Local result (2026-10-17) | |---|---|---| | Rust quality gates | `cargo fmt --check` / `clippy -D warnings` / `cargo test --locked` | Passed; 51 lib + 4 integration | | Host | `pnpm run test:host` | 64/64 (including real-sidecar tests) | | Client | `pnpm run test:client` + `tsc` | 41/41 + types clean | | Client build | `pnpm run build:client` | ≈204.9 KiB; externals only React | | Sidecar e2e | `pnpm run test:e2e` | 4/4 (real binary) | | Metadata | `pnpm run check` | Passed | | Packaging | `pnpm run release:zip` | Windows x64 zip + raw-sidecar SHA-256 verified | | Real panel | manual | Windows desktop 0.2.1-rc.2 manual walkthrough; acceptance checklist is appendix C of `使用教程.md` (not a per-item record) | ## Glossary | Term | Meaning | |---|---| | Main session | The user's normal DSH conversation; tasks must not write to it automatically | | Task | One isolation record backed by a dedicated normal-root DSH session | | Task session / pod session | The task's own DSH session; cwd = sandbox root; not in any workspace | | Sandbox root | User-specified writable root; task directories live under it | | Task directory | `//` | | Task action bar | The pod dock above the official composer | | Resume card | Read-only composer takeover while a task session is not live | | Sidecar | The Rust `isolation-pod-core` process | | Host half | `lib/index.js` and the Cordis plugin code | | Client half | `lib/client.js`, the in-page panel/widgets | ## Differences from the requirements baseline `需求说明.md` is frozen; intentional differences: | Baseline | This implementation | |---|---| | Separate manually opened UI | Normal root sessions kept outside workspaces, opened from the pod panel | | Self-drawn UI | Official conversation UI; task action bar/resume card are injected | | Single turn | Official-composer follow-ups and restart recovery | | Reject above concurrency | FIFO queue by default (`queueEnabled`) | | Single-task cleanup | `record`/`files`/`all` and bulk cleanup of terminal tasks | | Export/undo | Conflict policies, BLAKE3, overwrite backups, copy-undo conflict checks; no UI undo for move | | Tool allowlist | Upper bound only; guard enforced; a missing preset never empties it | | Windows-only | Cross-platform Rust; Windows x64 manually validated, others CI-built | | Configuration | Adds queue/preview/exportVerify/probeMode/autoResumeOnOpen/logLevel/maxTasks/maxExports/retentionDays | | Older DSH | DSH 0.2.1+ only; v0.1.3 data migrates automatically | ## Related documents - [`使用教程.md`](使用教程.md): step-by-step walkthrough (Chinese). - [`需求说明.md`](需求说明.md): frozen requirements baseline (Chinese). - [`CHANGELOG.md`](CHANGELOG.md): version history. - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md): architecture and design decisions. - [`docs/PROTOCOL.md`](docs/PROTOCOL.md): protocol and API contract. - [`docs/MIGRATION.md`](docs/MIGRATION.md): migration and rollback. - [`NOTICE.md`](NOTICE.md): third-party dependencies and licenses. ## License MIT, see [`LICENSE`](LICENSE). Third-party components are listed in [`NOTICE.md`](NOTICE.md).