dshbase

插件目录 / Developer / dsh-agent-preset-switcher

dsh-agent-preset-switcher

已验证 · 实测可装 aefuimn

✓ 持续维护

查看 GitHub ↗ ← 返回插件目录

0Stars
0Forks
0未关闭 issue
—语言
2026-08-21最近推送
跨平台平台

功能简介

Hot-switch agent presets (working modes) inside a running DSH session, instead of choosing a preset only when a session is created.

✅
我们的评价
可用 — 实测通过,早期项目

Hot-switch agent presets (working modes) inside a running DSH session, instead of choosing a preset only when a session is created. 实测能干净安装、正常启动。早期项目,但功能可用。

「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。

README

dsh-agent-preset-switcher

中文文档

Hot-switch agent presets (working modes) inside a running DSH session, instead of choosing a preset only when a session is created.

What it solves

A DSH agent preset (Standard / Minimal / PTC / any custom preset) decides a session agent's tool catalog, system prompt, and capabilities. Officially the preset is fixed once a session starts (turn/start exists):

  • the browser RPC agentPresets.select checks sessionBlank(agent.session) and returns agent-preset-locked for started sessions;
  • the reason: history transcripts were generated under the old tool set, and swapping tools mid-conversation would leave logged tool calls that the new composition cannot make.

This plugin turns that channel into hot-switching at any stage: a switch is applied only at a step boundary (after one model request finishes, before the next one is assembled), reusing the exact official mechanism — AgentPresets.recompose() (single-flight standing preset mounts + the scope-parent binding re-link held by the roster).

Quick start

Install from GitHub (recommended for everyone else)

The repo ships build artifacts (lib/), so no local build is needed. Install it as a plugin bundle with dsh plugin:

# SSH (clone/install over SSH; requires your GitHub SSH key)
dsh plugin --profile web add "git+ssh://git@github.com/aefuimn/dsh-agent-preset-switcher.git"

# or HTTPS (no SSH key needed; a read token may be required for private repos)
dsh plugin --profile web add "git+https://github.com/aefuimn/dsh-agent-preset-switcher.git"

Then restart the dsh web process and refresh the page:

# restart your dsh web server, then hard-refresh the browser tab

Install from a local checkout (developing this plugin)

dsh plugin --profile web add link:/absolute/path/to/dsh-agent-preset-switcher
# restart dsh web and refresh the browser tab

Verify

  • any session accepts /mode list and /mode <preset-id> slash commands;
  • dsh plugin --profile web ls should list dsh-agent-preset-switcher among the installed packages.

Layout

dsh-agent-preset-switcher/
├── package.json               # dsh plugin manifest (host only)
├── cordis.patch.yml           # bundle patch that inserts the plugin row
├── src/
│   ├── index.ts               # host half: service registration, /mode command, announcement
│   └── switcher.ts            # hot-switch core (armed -> step-boundary recompose)
├── lib/
│   └── index.js / switcher.js # tsc output
└── test/
    └── switcher.test.mjs      # unit tests

How it works

Triggering

Everything funnels into ctx.modeSwitcher.request(sessionId, presetId). The current entry is the /mode slash command:

  • /mode list shows the current preset and the full roster;
  • /mode <preset-id> requests a switch.

A request arms the switch (emitting mode-switcher/requested after resolving the target preset) rather than applying it immediately.

Applying (step boundary)

request(sessionId, targetId)
  └─ armed (latest wins; repeat requests overwrite)
       │
       ▼  agent/pre-step (before the next model request is assembled)
  └─ pop armed
       ├─ target == current   → no-op (no log event, no re-link)
       ├─ agentPresets.recompose(agent.ctx, targetId)
       │     └─ ensureStanding(target) (single-flight) → binding.rebind(standing.key)
       ├─ agent.session.append('agent-preset/selected', …)   # official log event
       ├─ mode-switcher/switched event
       └─ delegate to the default step (new sections/tools apply to the next request)

Key points:

  • Not an unmount/remount: standing mounts are permanent and shared; the switch only moves the agent scope's parent link.
  • Honest logging: agent-preset/selected is appended together with the re-link, so resume/fork rebuild the same composition via resolveSessionPreset, and existing browser agent-preset/selected listeners refresh.
  • Failure semantics: if recompose throws, nothing changes; mode-switcher/switch-failed is emitted and the step proceeds with the old composition.
  • Idempotency & concurrency: switches for one session are serialized through a per-session promise chain; same-target requests are no-ops.
  • Subagents: host rules are preserved — subagent sessions cannot be switched (their composition follows the parent).

Host API

declare module '@deepseek-ai/cordis' {
  interface Context {
    modeSwitcher: ModeSwitcherService
  }
}
  • modeSwitcher.request(sessionId, presetId) → {accepted:true,pending:true} | {accepted:false,reason}
  • Events: mode-switcher/requested, mode-switcher/switched, mode-switcher/switch-failed

Consistency with the official recompose path

The official blank-session switch (agentPresets.select) does:

presets.recompose(agent.ctx, id)  // ensureStanding + binding.rebind
agent.session.append('agent-preset/selected', { agentPreset: preset.id })

This plugin performs the exact same two steps for a non-blank session — it only moves the moment from "when the request arrives" to inside the agent/pre-step waterfall (between model requests). The recompose docs explicitly state the CALLER owns the blank check: the official RPC chooses the blank check, this plugin chooses the step boundary; the mechanism is the same.

Build

npm install            # dev dependencies (host types)
npm run build          # tsc for the host half

Limitations & follow-ups

  • Step-boundary semantics: a switch never interrupts the current model request; the longest wait is until the current step finishes.
  • Transcript consistency: switching happens between model requests; tool schemas/prompt sections change at the step boundary while history stays as-is (that is the value — and the boundary — of hot switching).
  • Future: the service in src/index.ts can gain a browser RPC (Typert/remote face) so future surfaces can call it directly; the current entry goes through the /mode slash command, so the host adds zero new wire.

License

MIT

安装

🧩 让 Agent 自动装(推荐)

装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:

dsh plugin add dshbase-catalog

然后对 agent 说「帮我装 dsh-agent-preset-switcher」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。

该插件是 GitHub 源码(未发 npm)——直接从仓库装:

Web profile:

dsh plugin --profile web add github:aefuimn/dsh-agent-preset-switcher

Headless(CLI)profile:

dsh plugin --profile headless add github:aefuimn/dsh-agent-preset-switcher

实测报告

验证通过:从 GitHub 源码完成 L1 安装 + L2 加载 + L3 运行(dsh 0.1.0-rc.6)。

安全:尚未扫描——我们的每日静态扫描将很快覆盖它。

分享徽章

Developer 里更多

浏览全部 7819 个插件 →