# dsh-workspace-presets
**Per-workspace Agent preset bindings for DeepSeek Harness.** Bind an Agent preset to a workspace once, and every new session started in that workspace automatically boots with that preset's full capabilities — deterministic, zero extra latency, zero extra model cost.
> **TL;DR** — Bind an Agent preset to each workspace in **Settings → Workspace presets**, and every new session in that workspace boots with it: deterministic, zero latency, zero extra model cost.
> Install: `dsh plugin --profile web add dsh-workspace-presets` · Uninstall: `dsh plugin --profile web remove dsh-workspace-presets`
>
> **TL;DR** — 在 **设置 → 工作区预设** 给每个工作区绑定一个 Agent 预设,该工作区之后新建的会话自动以它启动:确定性、零延迟、零额外模型调用。
> 安装:`dsh plugin --profile web add dsh-workspace-presets` · 卸载:`dsh plugin --profile web remove dsh-workspace-presets`
---
## What it does
- Adds a native **"Workspace presets"** page to the DSH settings panel: one row per workspace, each with a preset picker (system / user presets, broken presets flagged). The page draws its own heading and intro — the 0.1.5 settings panel only supplies the nav and content columns, and no longer renders a per-section title.
- **Applies the binding automatically** whenever a *blank* session belongs to a bound workspace — including the hidden reusable blank session the sidebar keeps per workspace, in every open tab.
- Falls back cleanly: a workspace without a binding keeps DSH's normal behavior (global default, hero-screen chip). An explicit chip choice on a blank session always wins over the binding.
- Sessions that have started, subagent sessions, or archived sessions will not be touched.
## Installation
**Prerequisites:** a DSH deployment that composes Agent presets (the standard install does), and `pnpm` on your PATH (`dsh plugin` forwards to pnpm).
**Supported DSH versions:** `0.1.5-rc.1` (peer range; its bundled client packages are `0.1.5-rc.2`).
**From npm** (once published):
```sh
dsh plugin --profile web add dsh-workspace-presets
```
**Straight from GitHub, without cloning:**
```sh
dsh plugin --profile web add github:GBDJXB/dsh-workspace-presets
```
**From a local checkout** (clone first):
```powershell
git clone https://github.com/GBDJXB/dsh-workspace-presets.git
cd dsh-workspace-presets
npm install # ← required for local installs: see the note below
dsh plugin --profile web add .\dsh-workspace-presets # or: dsh plugin --profile web add .
```
> Local-folder installs are `link:` installs: Node resolves the package's modules from the repo's real path, so the plugin's own runtime dependency (`@deepseek-ai/schemastery`) must be installed inside the repo (`npm install` / `pnpm install`) before the profile boots. Registry and git installs do not need this step.
All three forms do the same thing: pnpm installs the package into the profile and `dsh` reconciles `dsh.profile.bundles` automatically (the package declares `dsh.bundle.patch`), so no profile file edits are needed. Then **restart the profile** (`dsh web`) and **hard-refresh the browser** (Ctrl+Shift+R). Open **Settings** from the sidebar footer and pick **Workspace presets** — the page should be there.
**Or let an LLM install it for you** — paste this prompt into any DSH session (or your favorite agent):
```text
Install the dsh-workspace-presets plugin (per-workspace Agent preset bindings for DSH):
1. Pick ONE install source and run it:
- npm: dsh plugin --profile web add dsh-workspace-presets
- GitHub: dsh plugin --profile web add github:OWNER/dsh-workspace-presets
- local: git clone && cd dsh-workspace-presets && npm install --legacy-peer-deps
dsh plugin --profile web add
(Local installs MUST run npm install inside the repo first, or boot fails with
ERR_MODULE_NOT_FOUND — see Troubleshooting below.)
2. Restart the profile (dsh web) and tell the user to hard-refresh the browser (Ctrl+Shift+R).
3. Verify: Settings shows the "Workspace presets" page; bind a preset to a workspace;
a new session in that workspace boots with it (a toast confirms the apply).
If anything fails, check the Troubleshooting table in the README before retrying.
```
Troubleshooting
| Symptom | Cause & fix |
|---|---|
| `dsh plugin` says pnpm is not found | Install pnpm (`npm i -g pnpm`) and re-run. |
| Boot fails with `ERR_MODULE_NOT_FOUND … imported from …\host.js` | Local-folder install without the plugin's own deps: run `npm install` (or `pnpm install`) inside the repo, then restart. Registry/git installs never hit this. |
| Settings page doesn't show after install | The Host half activates on a profile restart — restart `dsh web`, then hard-refresh the browser. |
| The page shows "Could not load: …" or stays blank | That is the old client half talking to a newer DSH (or a Host half that never loaded). Upgrade with `dsh plugin --profile web add dsh-workspace-presets@latest` so the plugin matches your DSH version line, then restart + hard-refresh. |
| A bound workspace's new session still starts with the default preset | The binding only applies to **blank** sessions, and a manual hero-chip choice always wins; subagent sessions are never touched. Already-started sessions are fixed by design. |
| Plugin mounts twice / namespace registration fails at boot | You combined the bundle channel with a manual `cordis.patch.yml` insert — keep only one. |
Updating
```sh
dsh plugin --profile web add dsh-workspace-presets@latest
```
or bump the version in `$DSH_HOME/profiles/web/package.json` and re-run `pnpm install`. Then restart / hard-refresh as above.
## Usage
1. Open **Settings** from the sidebar footer, then **Workspace presets**.
2. Pick a preset for each workspace (or leave *Follow global default*).
3. Start a new session in that workspace — it boots with the bound preset; a small toast confirms each automatic apply.
Notes:
- The binding applies at session creation to blank sessions. Changing a binding affects future sessions (a still-blank session running a previous binding is updated too); already-started sessions are never touched.
- A manual preset choice made with the hero-screen chip on a blank session always overrides the workspace binding for that session.
- If a bound preset is deleted or broken, the apply is skipped and the page flags the workspace row; nothing crashes.
## Uninstall
```sh
dsh plugin --profile web remove dsh-workspace-presets
```
then restart the profile. To **disable temporarily** instead, append to the profile's `cordis.patch.yml`:
```yaml
- id: workspace-presets
disabled: true
```
**Residue policy:** the plugin creates no files of its own — every runtime effect (slots, listeners, styles, the settings namespace) is removed with it. The only possible remainder is an **inert** `workspace-agent-presets:` section in DSH's own settings document: no namespace resolves it, so it affects nothing. To purge it cleanly, click **Settings → Workspace presets → Clear all bindings** before removing the plugin, or delete the section from the settings document afterwards.
## Compatibility
- **Non-invasive.** UI lives only in two additive slots with fresh ids (`settings.section` entry `workspace-presets`, `shell.overlay` entry `workspace-presets.overlay`); no shipped UI is patched or replaced.
- **Official APIs only.** Settings namespace + `settings.describe/update/replace`, `agentPresets.list/select`, the `slots`/`locale`/`connection`/`remote`/`timer` services, and the `sessions`/`workspaces` list stores.
- **0.1.5 Remote contract.** Every call goes through `ctx.remote..(positional args)` and answers with the `RemoteResult` branch (`{ok:true,value}` / `{ok:false,error}`); the removed `ctx.connection.api` object envelope (`{result:{ok,value}}`) is not used. Each Remote namespace is its own cordis service (`remote.`), so `remote.agentPresets` and `remote.settings` are declared in the plugin's `inject` list — an undeclared `ctx.remote.` is refused with `cannot get property "remote.agentPresets" without inject`. A session's own preset is read from `session.projectionValues.agentPreset` (the session list summary no longer carries that field).
- **No files written.** Bindings persist in DSH's own settings document; the plugin never creates or deletes preset directories, session logs, or its own storage.
- **Multi-tab safe.** Selects are idempotent and Host-serialized per session; settings writes are revision-guarded.
## Repository layout
```
dsh-workspace-presets/
├── host.js # Host half: registers the settings namespace (≈40 lines)
├── client.js # Web half: settings page + overlay reconciler + toast
├── cordis.patch.yml # bundle patch: inserts the host row (id: workspace-presets)
├── package.json # dual-face manifest (main → host.js, ./client → client.js)
├── .gitignore # node_modules/ (local linked installs create it)
├── LICENSE # MIT
├── README.md # this file (English)
└── README.zh.md # 中文版
```
No build step, no `lib/`, no generated files — the repository **is** the shipped package. (A local `npm install` for linked development creates `node_modules/`, which `.gitignore` excludes.)
`package.json` essentials:
```jsonc
{
"name": "dsh-workspace-presets",
"type": "module",
"main": "host.js",
"exports": { ".": "./host.js", "./client": "./client.js" },
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": {
"platform": "web",
"inject": [
"@deepseek-ai/dsh-api-remotes",
"@deepseek-ai/dsh-api-session-controller",
"@deepseek-ai/dsh-api-workspace-controller",
"@deepseek-ai/dsh-client-connection",
"@deepseek-ai/dsh-client-locale",
"@deepseek-ai/dsh-client-ui-renderer",
"@deepseek-ai/dsh-client-ui-settings",
"@deepseek-ai/dsh-client-ui-session",
"@deepseek-ai/dsh-client-ui-workspace"
]
}
},
"dependencies": { "@deepseek-ai/schemastery": "^3.18.1" }
}
```
(`dsh.client.inject` mirrors the 0.1.5 shipped `ui-workspace` / `ui-agent-preset` client packages; when you target another DSH version, sync it with the `@deepseek-ai/*` client packages that version actually publishes. The field is loading/prefetch metadata, not Cordis service injection — the browser module-table baseline already carries `react`, `@deepseek-ai/dsh-client-ui-slots`, `@deepseek-ai/dsh-client-ui-primitives`, and friends, so they need no declaration.)
## Development
Plain JavaScript (ESM), no build step, no bundler, no JSX — the repo is the shipped package. `client.js` is a hand-written `window.__ModuleLoader__.load(...)` module using `React.createElement`; the only Host import is `@deepseek-ai/schemastery`, the same fork DSH's own `settings` service validates schemas against.
Editing `client.js` is picked up by the 0.1.5 client HMR watcher (it polls bundle mtimes and reloads that bundle; hard-refresh if in doubt). Editing `host.js` or `package.json` needs a profile restart.
## License
MIT