dsh-advisor-group 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +96 -37
- package/README.md +98 -38
- package/lib/client.js +183 -1
- package/lib/index.d.mts +45 -0
- package/lib/index.mjs +2717 -207
- package/package.json +14 -14
package/README.en.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# dsh-advisor-group
|
|
2
2
|
|
|
3
|
-
> A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) plugin that lets
|
|
3
|
+
> A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) plugin that lets one session **borrow wisdom outward and memory sideways**.
|
|
4
|
+
>
|
|
5
|
+
> For professional, long-tail world-knowledge, high-risk or uncertain questions, the main model consults **multiple expert advisor models** in a retro chat-group card; for something another session already knows, it asks that session instead of re-deriving it from scratch.
|
|
6
|
+
>
|
|
7
|
+
> To try it: `dsh plugin --profile web add dsh-advisor-group`, restart DSH, and put `@顾问群` in your question.
|
|
4
8
|
|
|
5
9
|
[](https://github.com/topics/dsh-plugin)
|
|
6
10
|
|
|
@@ -12,24 +16,28 @@
|
|
|
12
16
|
|
|
13
17
|
## ✨ Features
|
|
14
18
|
|
|
15
|
-
- **Auto-deepen consultation pipeline** — one `ask_advisors` call runs up to `maxRounds` rounds
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
19
|
+
- **Auto-deepen consultation pipeline** — one `ask_advisors` call runs up to `maxRounds` rounds (1–5, default 2). Round 1 uses your question as-is. Later rounds begin with a deeper follow-up from the driver model, or repeat the question if `autoDeepen` is disabled. Within each round, advisors relay in sequence (A → B sees A → C sees A+B → …). The driver concludes with a synthesis that always generates, regardless of `autoDeepen`.
|
|
20
|
+
- **Advisors can call session tools (2026-09-05)** — advisors can invoke tools visible to the session before answering. By default, access is limited to a read-only whitelist (read/grep/glob/web_search/web_fetch…). Tool calls route through the official `ctx.tools` pipeline (scope dispatch, guards, and pre/post policies), capped at 4 tool rounds before the advisor must reply in text. Each invocation appears as a `⛭ tool name · running / done / ⚠ failed` row in the card, expandable to inspect input and output. Switch between all session tools or off in settings.
|
|
21
|
+
- **Resume after a stop** — if a user stops the consultation or DSH restarts, the card's 「⏹ 停止」 button changes to 「▶ 继续聊天」 (Resume chat). Clicking it triggers `POST /advisor-group/resume` to continue from the checkpoint: unanswered advisors respond, previously answered ones are not queried again, remaining rounds run, and the final synthesis generates as usual. Session snapshots persist to `storages/advisor-group/sessions/<id>.json` (storing advisor IDs, messages, and statuses without credentials) and recover cleanly across restarts.
|
|
22
|
+
- **Zero-config driver model** — the driver model writes deep follow-up questions by reusing the active agent's provider and model. No separate API key or model configuration is required. If session headers are unreadable, it falls back to `discussion.driverModel`.
|
|
23
|
+
- **Three ways to activate** — mention `@顾问群` to force start; repeat the same question 3 times without resolution; or let the main model trigger consultation when its confidence drops below the threshold.
|
|
24
|
+
- **Optional Jev semantic pre-classification (off by default)** — when `trigger.jev.enabled` is active, the configured Jev model evaluates unforced requests (determining advisor need, high risk, or web-search suitability based on `trigger.jev.*` thresholds). If Jev is unreachable, the local rule-based classifier handles the decision. When `trigger.jev.useEnglishState` is enabled, Jev evaluates the caller-provided English gist (`questionEn`) instead of the Chinese prompt, while UI cards and session logs remain in Chinese.
|
|
25
|
+
- **Retro CRT chat cards** — green, amber, and blue themes with scanline effects and LIVE/DONE status badges. The 💭 thinking panel opens by default and expands as streams arrive. Content renders via lightweight Markdown with a strict link-protocol whitelist, supporting headings, lists, code blocks, blockquotes, links, and tables.
|
|
26
|
+
- **Ask another session (`ask_session`)** — forwards a question to another live session and wakes it. Use `list_sessions` to browse the roster and `ask_result` to collect asynchronous outputs. Responses represent conversation windows rather than 1-to-1 answers; see **The global swarm** for boundaries, cost, and usage.
|
|
27
|
+
- **Provider presets (12 platforms · 28 presets)** — DeepSeek, Moonshot Kimi, Kimi Code, Aliyun Bailian, Zhipu AI, OpenAI, Claude, Gemini, SiliconFlow, AIHubMix, OpenRouter, and **OpenCode Go** (including OpenAI and Anthropic-compatible variants).
|
|
28
|
+
- **Security-minded by design** — API keys follow official `SecretField` semantics and are never returned to the browser. The `apiKeysByProvider` credential archive stays strictly server-side. Diagnostic endpoints enforce SSRF protections (HTTPS-only, loopback allowed, rejecting raw IP literals and redirects). All `/advisor-group/*` routes require a per-boot token. You can also enforce a daily limit on new consultations (see **Daily quota**).
|
|
29
|
+
- **Runtime toggle** — `toggle_advisor_group` enables or disables the plugin and persists the setting.
|
|
24
30
|
|
|
25
31
|
## ✅ Compatibility
|
|
26
32
|
|
|
27
33
|
| Surface | Status |
|
|
28
34
|
|---|---|
|
|
29
|
-
| Harness |
|
|
35
|
+
| Harness | **`0.2.1-rc.2` — the version this release was gated against**; the declared peer range is `>=0.1.7-rc.2 <0.3.0` |
|
|
30
36
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
31
37
|
| Platforms | DSH Web and the desktop app (same client bundle) + headless host logic |
|
|
32
38
|
|
|
39
|
+
> On the lower bound: `0.1.7-rc.2` remains inside the declared peer range, but was not re-verified during this round. Type checks, tests, and build were all completed against `0.2.1-rc.2`. If an older 0.1.x host fails to load the plugin, the issue stems from interface changes rather than bugs in this release.
|
|
40
|
+
|
|
33
41
|
## 📦 Installation
|
|
34
42
|
|
|
35
43
|
```sh
|
|
@@ -40,6 +48,8 @@ dsh plugin --profile web add dsh-advisor-group
|
|
|
40
48
|
npx @deepseek-ai/dsh web
|
|
41
49
|
```
|
|
42
50
|
|
|
51
|
+
> **About step 2**: `npx @deepseek-ai/dsh web` pulls whichever DSH version is latest at that moment, which can fall outside this plugin's peer range (`>=0.1.7-rc.2 <0.3.0`); the host then refuses to load the plugin. Restart the way you normally do (an existing start script or the desktop app works too) — this exact command is not required.
|
|
52
|
+
|
|
43
53
|
> **From source (development)**
|
|
44
54
|
> ```sh
|
|
45
55
|
> npm install --legacy-peer-deps --no-audit --no-fund
|
|
@@ -54,22 +64,53 @@ npx @deepseek-ai/dsh web
|
|
|
54
64
|
<img width="791" height="797" alt="image" src="https://github.com/user-attachments/assets/5fba07bc-8c36-40f1-b423-d206aa495192" />
|
|
55
65
|
|
|
56
66
|
1. Restart DSH web and hard-refresh the browser (`Ctrl+Shift+R`).
|
|
57
|
-
2. Go to **Settings → Plugins → Advisor Group**: configure your advisors (provider route + model; use *获取模型列表* to pull the authoritative model list) and tune `maxRounds`, thresholds, and the UI theme.
|
|
67
|
+
2. Go to **Settings → Plugins → Advisor Group**: configure your advisors (provider route + model; use *获取模型列表* (Fetch model list) to pull the authoritative model list) and tune `maxRounds`, thresholds, and the UI theme.
|
|
58
68
|
3. Just start a conversation:
|
|
59
69
|
- type `@顾问群` in your question to force a consultation, or
|
|
60
70
|
- ask a professional/uncertain question — the plugin escalates automatically when appropriate.
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
Tools available to the model: `ask_advisors`, `toggle_advisor_group`, `ask_session`, `list_sessions`, `ask_result`.
|
|
73
|
+
|
|
74
|
+
## 🐝 The global swarm: sessions that can ask each other
|
|
75
|
+
|
|
76
|
+
### One session is an expert with memory
|
|
77
|
+
|
|
78
|
+
Every session accumulates context: the code it read, the detours it took, the decisions it settled. A session is not a throwaway chat window but a **node with memory** — and re-deriving what another session already knows is both slower and more expensive.
|
|
79
|
+
|
|
80
|
+
So the plugin does two things: it borrows **wisdom** outward (consulting expert models) and **memory** sideways (asking the session that already knows). Both are opt-in, and neither takes the decision away from you.
|
|
81
|
+
|
|
82
|
+
### Four principles
|
|
83
|
+
|
|
84
|
+
1. **Reuse, don't rebuild.** The session that already knows answers better and faster — its context and its cache prefix are warm.
|
|
85
|
+
2. **Peers, no root.** Sessions ask each other as equals; nothing dispatches from the centre. No central bottleneck, and no single session that must know everything.
|
|
86
|
+
3. **Candidates, never autopilot.** Routing (automatic candidate selection) proposes candidates with reasons; delivery needs you or the model to name one. Better to ask once more than to wake the wrong session.
|
|
87
|
+
4. **Task-first, never chatter.** Hop limit, one concurrent wait per session, long runs detached so they never block, silent fallback on every failure. **More nodes is not more intelligence — routing quality is.**
|
|
63
88
|
|
|
64
|
-
|
|
89
|
+
### Three tools
|
|
65
90
|
|
|
66
|
-
|
|
91
|
+
| Tool | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `list_sessions` | The roster: title, working directory, age, running state. Read-only — it never wakes anyone. |
|
|
94
|
+
| `ask_session` | Hands a question to another session and wakes it; waits by default, or with `detach: true` returns an `askId` immediately. |
|
|
95
|
+
| `ask_result` | Reads a detached result (`waiting` / `done` / `timeout` / `error`); the record carries `parentAskId`, so a relay chain can be walked. |
|
|
96
|
+
|
|
97
|
+
### Finding the one to ask
|
|
98
|
+
|
|
99
|
+
Without a named target, the plugin scores sessions based on **title + working-directory segments** (with CJK text split into two-character groups). A few hard rules then narrow the candidates: a session's exact name mentioned in your message, a title keyword match, or a directory match.
|
|
100
|
+
|
|
101
|
+
Sorting evaluates criteria in order: score → title hit over directory-only hit → whether running → sufficient context (log size reaching the 160,380-byte floor) → activity recency (hot/warm/cold) → larger context within the same project → more recent activity → shorter title.
|
|
67
102
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
You can optionally add a **Jev** judge (Settings → Plugins → Advisor Group → 全局蜂群 [Global swarm] → 路由方式 [Routing method]). Candidates are passed as a closed option set with a "none of these" fallback; a low-confidence decision keeps the local ranking.
|
|
104
|
+
|
|
105
|
+
Jev can also evaluate whether a turn should consult another session at all. There are three modes: off, local prefilter + Jev (default), and every turn. The default mode runs a zero-cost clue prefilter first: it only calls Jev when a message clearly points to another session and names no target. Even when Jev recommends a query, it only injects a suggestion line into the next system prompt. Whether to deliver the message remains the main model's choice.
|
|
106
|
+
|
|
107
|
+
### Practicalities and honest limits
|
|
108
|
+
|
|
109
|
+
- **Picking the target**: provide an exact session ID, exact title, or a natural short name. The workspace directory name is usually sufficient (for example, refer to a session under `I:\极简遥控器\…` as 「极简遥控器」). A unique match routes immediately. Multiple matches produce a candidate list for you to choose from. The plugin never guesses.
|
|
110
|
+
- **What you get back**: everything the target said between the **delivery receipt** and its next **whole-session idle** state. This captures what the session produced during that window, rather than a 1-to-1 reply. A `status` flag (`ok` / `partial` / `failed`) indicates response reliability.
|
|
111
|
+
- **What you see**: the sender side opens a chat-group card displaying 「已投递,等待对方回应…」 (Delivered, awaiting response...), followed by the response bubble and a 「📌 投递说明」 (Delivery details) note. If target resolution fails, no card opens and the error is reported directly in the tool row.
|
|
112
|
+
- **Guards**: hops are capped at 2, asking oneself or subagents is forbidden, each session allows only one concurrent wait, and the payload budget is 32 KB. Detached results are cleaned up after their TTL expires. Sessions listed under 「不参与路由的会话」 (Sessions excluded from routing) never appear as candidates and reject direct requests. Target sessions are never cancelled midway.
|
|
113
|
+
- **Cost**: see **Usage and cost**. Detached results are pruned on read, and every Jev decision is logged to `storages/advisor-group/asks/routing-jev.log`.
|
|
73
114
|
|
|
74
115
|
In one line: say "ask the 极简遥控器 session how far along it is".
|
|
75
116
|
|
|
@@ -78,13 +119,13 @@ In one line: say "ask the 极简遥控器 session how far along it is".
|
|
|
78
119
|
| Key | Type | Default | Description |
|
|
79
120
|
|---|---|---|---|
|
|
80
121
|
| `enabled` | boolean | `true` | Enable advisor group |
|
|
81
|
-
| `discussion.maxRounds` | number | `2` | Total rounds of the auto-deepen pipeline (each round = driver
|
|
122
|
+
| `discussion.maxRounds` | number | `2` | Total rounds of the auto-deepen pipeline (1–5; each round = all advisors in relay, with a driver follow-up before every round after the first) |
|
|
82
123
|
| `discussion.maxAdvisorsPerCall` | number | `3` | Max advisors per call (1–10) |
|
|
83
|
-
| `discussion.autoDeepen` | boolean | `true` |
|
|
124
|
+
| `discussion.autoDeepen` | boolean | `true` | Whether to generate a deeper follow-up between rounds; it does not change the round count — when off, the same question repeats for `maxRounds` rounds and the synthesis is still generated |
|
|
84
125
|
| `discussion.driverModel` | object | – | Fallback driver model `{provider, model}` when the session header cannot be read |
|
|
85
126
|
| `discussion.advisorTimeoutMs` | number | `600000` | Per-advisor call timeout (ms, 1000–600000), applied to both channels |
|
|
86
127
|
| `discussion.driverTimeoutMs` | number | `600000` | Driver generation timeout (deep-question / conclusion; ms, 1000–1200000) |
|
|
87
|
-
| `discussion.advisorTools` | string | `'readonly'` | Global default advisor tool scope when an advisor sets no own `tools`: `readonly` / `all` (every session-visible tool incl. writable — elevated risk, see Security) / `off`. Tool calling needs the direct-http (OpenAI/Anthropic) channel. |
|
|
128
|
+
| `discussion.advisorTools` | string | `'readonly'` | Global default advisor tool scope when an advisor sets no own `tools`: `readonly` / `all` (every session-visible tool incl. writable — elevated risk, see Security) / `off`. Tool calling needs the direct-http (OpenAI/Anthropic) channel (requirements in **Security & privacy**). |
|
|
88
129
|
| `quota.enabled` | boolean | `true` | Enable the daily new-consultation cap (cost safety valve) |
|
|
89
130
|
| `quota.maxPerDay` | number | `50` | Max new consultations per UTC day (1–100000); ignored when `quota.enabled` is `false` |
|
|
90
131
|
| `trigger.requireClassifier` | boolean | `true` | Run the pre-classifier before starting |
|
|
@@ -95,40 +136,58 @@ In one line: say "ask the 极简遥控器 session how far along it is".
|
|
|
95
136
|
| `trigger.jev.confidenceThreshold` | number | `0.6` | Escalate when Jev answers `needsAdvisor` yes (or its 0–1 score reaches this value) |
|
|
96
137
|
| `trigger.jev.highRiskThreshold` | number | follows `jev.confidenceThreshold` | Flag high-risk when Jev answers `high_risk` yes (or its 0–1 score reaches this value); unset = follows `trigger.jev.confidenceThreshold` |
|
|
97
138
|
| `trigger.jev.useEnglishState` | boolean | `false` | Judge using the caller-supplied English gist (`questionEn`) instead of the Chinese question; display and session records stay Chinese |
|
|
139
|
+
| `swarm.enabled` | boolean | `true` | Master switch for cross-session work; off makes `ask_session` / `list_sessions` / `ask_result` refuse instead of failing quietly |
|
|
140
|
+
| `swarm.defaultMode` | string | `'wait'` | Default reply mode: `wait` (blocking) or `detach` (deliver and return an `askId`); an explicit argument always wins |
|
|
141
|
+
| `swarm.defaultWaitMs` | number | `180000` | Default wait ceiling in ms (1000–600000) |
|
|
142
|
+
| `swarm.resultTtlHours` | number | `24` | How long detached results are kept, in hours (1–720); expired records are pruned on read |
|
|
143
|
+
| `swarm.rosterIncludeSubagents` | boolean | `false` | List subagent sessions in the roster |
|
|
144
|
+
| `swarm.routingEnabled` | boolean | `true` | With no named target, match candidates from the question (candidates only — never delivered automatically) |
|
|
145
|
+
| `swarm.routingTopN` | number | `3` | How many candidates to return (1–8) |
|
|
146
|
+
| `swarm.routingEngine` | string | `'local'` | `local` scoring, or `jev` which reranks a closed option set and falls back to local on any failure |
|
|
147
|
+
| `swarm.routingAsk` | string | `'gated'` | Whether a turn should consult another session: `off`, `gated` (local prefilter + Jev, default), `always` |
|
|
148
|
+
| `swarm.routingExclude` | string[] | `[]` | Sessions excluded from routing (a title keyword or an exact sessionId per line); naming one is refused |
|
|
98
149
|
| `ui.theme` | string | `retro-green` | Chat card theme (`retro-green` / `retro-amber` / `retro-blue`) |
|
|
99
150
|
| `ui.showTimestamps` | boolean | `true` | Show timestamps |
|
|
100
151
|
| `ui.autoExpand` | boolean | `true` | Auto-expand card |
|
|
101
|
-
| `advisors[].tools` | string | – | Per-advisor tool-scope override (`readonly`/`all`/`off`; unset follows the global default) |
|
|
152
|
+
| `advisors[].tools` | string | – | Per-advisor tool-scope override (`readonly`/`all`/`off`; unset follows the global default); in the settings card, direct-channel advisors get a dropdown, while built-in-channel advisors are told that the built-in route (DSH credentials) does not support tool calling and that `baseURL` / `apiKey` / `apiKeyEnv` enables it |
|
|
153
|
+
| `advisors[].thinking` | string | – | Per-advisor thinking mode: `enabled` / `disabled`; unset = provider default, and `disabled` forces no thinking block (mapped to `reasoningEffort: 'off'` on the built-in channel) |
|
|
154
|
+
| `advisors[].reasoningEffort` | string | – | Per-advisor thinking strength: `off` / `low` / `high` / `max`; unset = provider/model default, and `off` disables thinking per request |
|
|
155
|
+
| `advisors[].temperature` | number | – | Sampling temperature (0–2); unset = provider default |
|
|
156
|
+
| `advisors[].maxTokens` | number | – | Token cap for one answer (≥1); unset = provider default |
|
|
157
|
+
| `advisors[].authMode` | string | – | Anthropic direct-channel auth header style: `x-api-key` / `bearer` |
|
|
158
|
+
| `advisors[].avatar` | string | – | Short avatar shown on the card (emoji or ASCII) |
|
|
102
159
|
| `advisors` | array | `[]` | Advisor list (provider/model/baseURL/apiKey/apiKeyEnv/protocol…, keys are `role('secret')`) |
|
|
103
160
|
|
|
104
161
|
> `discussion.parallel` and `discussion.stopOnConsensus` are deprecated leftovers kept only for stored-config compatibility.
|
|
105
162
|
|
|
106
163
|
## 🧮 Daily quota
|
|
107
164
|
|
|
108
|
-
The plugin
|
|
165
|
+
The plugin includes a cost safety valve. It tracks **new consultations** per UTC calendar day (default cap: 50), which can be adjusted or disabled in settings. The quota applies solely to newly initiated consultations. Resuming conversations via 「▶ 继续聊天」 (Resume chat) and follow-up prompts do not consume quota.
|
|
166
|
+
|
|
167
|
+
Evaluation and increment occur within a single synchronous block, preventing concurrency race conditions. The counter persists atomically to `$DSH_HOME/storages/advisor-group/daily-guard.json`, survives restarts, and resets at 00:00 UTC. When the quota is reached, `ask_advisors` aborts and returns an explanatory message. The settings page displays "today's remaining consultations X / Y" in real time. Disabling the cap continues tracking counts without blocking requests.
|
|
109
168
|
|
|
110
169
|
## 💸 Usage and cost
|
|
111
170
|
|
|
112
|
-
|
|
171
|
+
A single consultation consists of a chain of model calls. Under the default configuration (`maxRounds = 2`, up to 3 advisors), launching a new consultation involves **8** model calls: round 1 initiates 3 advisor calls; round 2 includes 1 driver follow-up and 3 advisor calls; finally, the driver generates 1 overall synthesis. If advisors invoke tools, each tool loop adds an extra model call (capped at 4 tool rounds per advisor, plus 1 final text response), so actual usage may exceed 8 calls.
|
|
113
172
|
|
|
114
|
-
Billing
|
|
173
|
+
Billing is calculated independently per channel. Advisors bill against their configured routes and models, while the driver model bills against the current session's model. To minimize expenses, you can lower `maxRounds`, reduce the number of advisors, set `tools: 'off'` for advisors, and configure an appropriate limit under **Daily quota**. If a consultation is interrupted, clicking 「▶ 继续聊天」 (Resume chat) only queries pending advisors without restarting the entire run, costing substantially less than a fresh consultation.
|
|
115
174
|
|
|
116
|
-
`ask_session`
|
|
175
|
+
`ask_session` does not consume consultation quota. It simply wakes the target session to run a standard response turn billed to that session's own model, and does not count as a new advisor consultation.
|
|
117
176
|
|
|
118
177
|
## 🔒 Security & privacy
|
|
119
178
|
|
|
120
|
-
- Direct API keys can be stored in
|
|
121
|
-
- `/advisor-group/*` routes
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
- Classifier shadow mode
|
|
125
|
-
- **`advisorTools: 'all'` is an elevated-risk scope.** It exposes
|
|
179
|
+
- Direct API keys can be stored in DSH `settings.yaml` (marked as secret, never returned to the browser by `describe`). If you prefer not to store keys on disk, use the `apiKeyEnv` environment variable mode.
|
|
180
|
+
- The `/advisor-group/*` routes utilize a per-boot shared token designed for single-user local setups. Add a reverse-proxy authentication layer before exposing ports to a local network or the public internet.
|
|
181
|
+
- Diagnostic endpoints resolve keys server-side before contacting providers, applying SSRF validation (HTTPS-only and loopback allowed). DNS rebinding is a known architectural boundary.
|
|
182
|
+
- For counting rules, storage paths, and reset schedules regarding the daily quota, see **Daily quota**.
|
|
183
|
+
- Classifier shadow mode: when `trigger.requireClassifier` is enabled, all classification samples are appended to the read-only endpoint `/advisor-group/shadow` (including forced triggers like `@顾问群` or repeated-question scenarios, tagged as `provider: 'bypass'`). This data is used exclusively for threshold tuning and never affects runtime logic.
|
|
184
|
+
- **`advisorTools: 'all'` is an elevated-risk scope.** It exposes all session-visible tools (including write and command execution tools such as `pwsh`, `bash`, and `write`) to advisor models under official guard pipelines. Enable this only for trusted local models. Tool calling requires the direct-http (OpenAI/Anthropic) channel; the DSH built-in channel reuses host credentials and cannot invoke tools. Configure `baseURL`, `apiKey`, or `apiKeyEnv` for any advisor needing tools. Every non-read-only tool execution triggers a `console.warn` audit log. The default recommendation is `readonly` or `off` (see read-only whitelist in **Features**).
|
|
126
185
|
|
|
127
186
|
## ⚠️ Known limitations
|
|
128
187
|
|
|
129
|
-
-
|
|
130
|
-
- Risk
|
|
131
|
-
- Cross-session
|
|
188
|
+
- If the DSH host crashes abruptly, an active card may continue displaying **LIVE** until refreshed (a disconnection notice is now displayed). Refreshing restores the true state from session logs.
|
|
189
|
+
- Risk notices are strictly fact-based rather than text-based. They track truncations (advisor streams interrupted by timeout or network drop) and cancellations (consultation stopped by user). Notices appear in stopped or completed cards and return to the main model via `ask_advisors`. Advisor responses are not scanned for keywords; text-heuristic matching was removed in v0.1.1 to avoid false positives on negative statements like 「没有任何风险」.
|
|
190
|
+
- Cross-session queries (`ask_session`) currently lack a per-session tool visibility toggle. When `swarm.enabled` is active, the three session tools remain visible across all sessions, relying on system prompts to limit usage to explicit user instructions. Mutual wait deadlocks between two sessions are not currently detected.
|
|
132
191
|
|
|
133
192
|
## 🛠️ Development
|
|
134
193
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# dsh-advisor-group
|
|
2
2
|
|
|
3
|
-
> [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH
|
|
3
|
+
> [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)插件:让一个会话**既能向其他模型借智慧,也能向其他会话借记忆**。
|
|
4
|
+
>
|
|
5
|
+
> 遇到专业、长尾世界知识、高风险或不确定的问题,主模型召集多位**专家顾问模型**,在复古 CRT 聊天组卡片中真流式会诊;遇到别的会话已经知道的事,跨会话去问,复用已有上下文,无需从头再查。
|
|
6
|
+
>
|
|
4
7
|
|
|
5
8
|
[](https://github.com/topics/dsh-plugin)
|
|
6
9
|
|
|
@@ -12,26 +15,28 @@
|
|
|
12
15
|
|
|
13
16
|
## ✨ 功能特性
|
|
14
17
|
|
|
15
|
-
- **自动深挖咨询流水线**:一次 `ask_advisors`
|
|
16
|
-
- **顾问可调用会话工具(2026-09-05
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- **Jev 语义前置分类(可选,默认关闭)**:开启 `trigger.jev.enabled` 后,未强制启动的咨询先由配置的 Jev
|
|
21
|
-
- **复古 CRT
|
|
22
|
-
-
|
|
23
|
-
- **供应商预设(
|
|
24
|
-
- **注重安全**:API Key
|
|
25
|
-
-
|
|
18
|
+
- **自动深挖咨询流水线**:一次 `ask_advisors` 跑满 `maxRounds` 轮(1–5,默认 2)。首轮直接用原问题,后续各轮先由驱动模型生成更深入的追问(`autoDeepen` 关闭时改用原问题)。顾问按 A → B(见 A)→ C(见 A+B)接力应答,最后由驱动模型生成综合结论(总会生成,不受 `autoDeepen` 影响)。
|
|
19
|
+
- **顾问可调用会话工具(2026-09-05)**:顾问回答前可调用当前会话工具,默认仅放行只读白名单(read/grep/glob/web_search/web_fetch 等)。调用走官方 `ctx.tools` 完整管线,最多 4 轮后强制用文字作答。每次调用在卡片展示 `⛭ 工具名 · 调用中 / 调用完成 / ⚠ 失败`,展开可看输入输出。可在设置中切换为「全部会话工具」或「关闭」。
|
|
20
|
+
- **停止后可继续**:人工停止或 DSH 重启后,卡片「⏹ 停止」按钮变成「▶ 继续聊天」。点击调用 `POST /advisor-group/resume` 从断点续跑:未答顾问补答,已答顾问不重问,剩余轮次与最终综合结论照常生成。会话快照持久化保存在 `storages/advisor-group/sessions/<id>.json`(仅存顾问 id、消息和状态,不含凭据),重启后自动恢复。
|
|
21
|
+
- **驱动模型零配置复用**:驱动模型负责生成深挖追问,直接复用当前会话的 provider 和 model。你不需要额外配置密钥或模型;会话头信息不可读时,自动回退到 `discussion.driverModel`。
|
|
22
|
+
- **三种触发方式**:在提问中带 `@顾问群`(强制启动);同一问题重复提问 3 次未解决;主模型自评置信度低于预设阈值。
|
|
23
|
+
- **Jev 语义前置分类(可选,默认关闭)**:开启 `trigger.jev.enabled` 后,未强制启动的咨询先由配置的 Jev 模型做语义判断(是否该找顾问、是否高风险、是否适合联网,阈值见 `trigger.jev.*`)。Jev 不可用时,由本地规则分类器接管。开启 `trigger.jev.useEnglishState` 后,Jev 改读主模型给出的英文概要(`questionEn`),卡片展示与会话记录仍保持中文。
|
|
24
|
+
- **复古 CRT 聊天组卡片**:提供绿、琥珀、蓝三种主题,带扫描线和 LIVE/DONE 状态标。💭 思考面板默认展开并随流式内容滚动。正文采用轻量 Markdown 渲染,设有限制协议白名单,支持标题、列表、代码、引用、链接与表格。
|
|
25
|
+
- **全局蜂群(跨会话提问)**:`ask_session` 把问题交给另一个活着的会话并唤醒它,`list_sessions` 查看名录,`ask_result` 获取异步结果。拿回的不是逐条问答,详情见「🐝 全局蜂群」。
|
|
26
|
+
- **供应商预设(12 平台 · 28 预设)**:DeepSeek、月之暗面 Kimi、Kimi Code、阿里云百炼、智谱 AI、OpenAI、Claude、Gemini、硅基流动、AIHubMix、OpenRouter、**OpenCode Go**(含 OpenAI 与 Anthropic 兼容变体)。
|
|
27
|
+
- **注重安全**:API Key 遵循官方 `SecretField` 语义,浏览器永不回显,供应商密钥档案 `apiKeysByProvider` 仅保存在服务端。诊断端点经过 SSRF 加固(仅限 https 与 loopback,拒绝 IP 字面量和重定向)。`/advisor-group/*` 路由在启动期生成 token 进行鉴权。支持每日新咨询上限(见「🧮 每日配额」)。
|
|
28
|
+
- **运行时开关**:调用 `toggle_advisor_group` 启停插件,状态自动持久化到设置。
|
|
26
29
|
|
|
27
30
|
## ✅ 兼容性
|
|
28
31
|
|
|
29
32
|
| 项目 | 状态 |
|
|
30
33
|
|---|---|
|
|
31
|
-
| Harness |
|
|
34
|
+
| Harness | **`0.2.1-rc.2`(实测运行版本,本版门禁即对着它跑)**;声明的 peer 区间为 `>=0.1.7-rc.2 <0.3.0` |
|
|
32
35
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
33
36
|
| 平台 | DSH Web 与桌面端(共用同一份客户端 bundle)+ headless 宿主逻辑 |
|
|
34
37
|
|
|
38
|
+
> 关于下限:`0.1.7-rc.2` 仍在声明的 peer 区间内,但本轮没有重新实装验证。类型检查、测试与构建均对着 `0.2.1-rc.2` 完成。如果在更早的 0.1.x 宿主上遇到加载失败,多半是因接口变动,而非当前版本代码缺陷。
|
|
39
|
+
|
|
35
40
|
## 📦 安装
|
|
36
41
|
|
|
37
42
|
```sh
|
|
@@ -42,6 +47,8 @@ dsh plugin --profile web add dsh-advisor-group
|
|
|
42
47
|
npx @deepseek-ai/dsh web
|
|
43
48
|
```
|
|
44
49
|
|
|
50
|
+
> **第 2 步提醒**:`npx @deepseek-ai/dsh web` 拉取的是当时的最新版 DSH,可能跨出本插件的 peer 区间(`>=0.1.7-rc.2 <0.3.0`),宿主会因此拒绝加载插件。按你平时的方式重启即可(已有的启动脚本、桌面端都可以),不必非用这条命令。
|
|
51
|
+
|
|
45
52
|
> **从源码开发安装**
|
|
46
53
|
> ```sh
|
|
47
54
|
> npm install --legacy-peer-deps --no-audit --no-fund
|
|
@@ -62,17 +69,52 @@ npx @deepseek-ai/dsh web
|
|
|
62
69
|
- 提问时带 `@顾问群` 强制发起咨询,或
|
|
63
70
|
- 直接提专业/不确定的问题——插件会按需自动升级。
|
|
64
71
|
|
|
65
|
-
模型可用工具:`ask_advisors`、`toggle_advisor_group`、`ask_session`。
|
|
72
|
+
模型可用工具:`ask_advisors`、`toggle_advisor_group`、`ask_session`、`list_sessions`、`ask_result`。
|
|
73
|
+
|
|
74
|
+
## 🐝 全局蜂群:让会话互相提问
|
|
75
|
+
|
|
76
|
+
### 一个会话,就是一个有记忆的专家
|
|
77
|
+
|
|
78
|
+
DSH 里每个会话都在积累上下文:读过的代码、走过的弯路、定下的约定、否掉的方案。它们不是一次性的聊天窗口,而是**带着记忆的节点**——同一个问题在别处早已查清,从零再问一遍既慢又贵。
|
|
66
79
|
|
|
67
|
-
|
|
80
|
+
所以这个插件做两件事:**向外借智慧**——召集专家顾问模型;**向旁边借记忆**——问那个已经知道的会话。
|
|
68
81
|
|
|
69
|
-
|
|
82
|
+
### 四条原则
|
|
83
|
+
|
|
84
|
+
1. **复用,而不是重造。** 让知道答案的会话来回答,比让新会话从头查更准、也更快——它的上下文与缓存前缀都是热的。
|
|
85
|
+
2. **对等,没有根节点。** 会话之间平级互相提问,不存在"总控 Agent"派发一切。没有中心,就没有单点瓶颈,也不需要一个知道全部事情的会话。
|
|
86
|
+
3. **只提候选,绝不代发。** 路由(自动挑选候选会话)只给候选与理由,投递必须由你或主模型点名。宁可多问一句,也不猜错打扰。
|
|
87
|
+
4. **任务导向,不为通信而通信。** 跳数(一次提问最多经几个会话中转)上限、同一时刻每会话只等一个、长任务走异步不阻塞、失败一律静默降级。**节点多不等于聪明,路由质量才是。**
|
|
88
|
+
|
|
89
|
+
### 三个工具
|
|
90
|
+
|
|
91
|
+
| 工具 | 作用 |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `list_sessions` | 会话名录:标题、工作目录、活跃度、是否运行中。只读,不唤醒任何人 |
|
|
94
|
+
| `ask_session` | 把问题交给另一个会话并唤醒它;可同步等,也可 `detach` 异步投递,投递即返回 `askId` |
|
|
95
|
+
| `ask_result` | 取异步结果(等待中 / 已完成 / 超时 / 出错);记录里带 `parentAskId`,可顺着链条往上追 |
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
97
|
+
### 怎么找到"该问的那一个"
|
|
98
|
+
|
|
99
|
+
没点名时,插件按**标题 + 工作目录**给会话打分。中文按两字组切分。随后用硬规则收敛:消息里出现某会话名称、标题命中关键词,或目录命中项目名。排序时依次比较:综合分数 → 标题命中优先于目录命中 → 是否运行中 → 上下文是否足够(日志体积达到 160,380 字节门槛)→ 活跃时段(热/温/冷)→ 同项目上下文更大 → 最近活跃时间 → 标题长度(越短越前)。
|
|
100
|
+
|
|
101
|
+
可选再叠加一层 Jev 判断(设置 → 插件 → 顾问群 → 全局蜂群 → 路由方式)。把候选作为闭合选项交给 Jev,若置信度不足则保持本地排序。
|
|
102
|
+
|
|
103
|
+
它还能判断当前轮次是否需要向其他会话提问,分为三档:关、本地预过滤 + Jev(默认)、每轮都问。默认档先做零成本规则初筛:只有当用户消息暗示需要外援且未指定目标时,才会发起 Jev 调用。即便判定为真,也只在下轮系统提示中注入一行建议,最终仍由主模型自主决定是否发问。
|
|
104
|
+
|
|
105
|
+
### 边界与诚实
|
|
106
|
+
|
|
107
|
+
- **区间语义**:获取的内容是「从投递回执到目标会话下一次空闲」之间的全部发言,并非逐条问答。结果附带 `status`(`ok` / `partial` / `failed`)标明可靠性。
|
|
108
|
+
- **成本护栏**:限制中转跳数 ≤ 2,禁止向自身或子会话发问,同一会话同一时刻只等一个请求,载荷上限 32 KB。异步结果超时自动清理。可在「不参与路由的会话」中排除特定会话。插件永不强行中断对方的回合。
|
|
109
|
+
- **失败即沉默**:遇到 Jev 超时、响应异常或候选超出名录时,静默退回本地规则判定,不打扰用户、不误投递、不乱报错。
|
|
110
|
+
- **可追溯**:每次 Jev 判定都会在 `storages/advisor-group/asks/routing-jev.log` 写入一行日志,方便事后审计。
|
|
111
|
+
|
|
112
|
+
### 实操细节
|
|
113
|
+
|
|
114
|
+
- **怎么指定目标**:支持完整会话 ID、精准标题或自然简称。通常输入工作目录名即可(例如目录为 `I:\极简遥控器\…` 的会话,直接说「极简遥控器」)。唯一匹配直接投递;命中多个时列出候选供你挑选;匹配不确定时绝不盲猜。
|
|
115
|
+
- **界面上看到什么**:提问侧弹出聊天组卡片。先显示「已投递,等待对方回应…」,随后呈现对方回答气泡与「📌 投递说明」(标明通道与截止时间)。若目标定位失败则不弹卡片,仅在工具调用行输出报错。
|
|
116
|
+
- **不想等怎么办**:默认同步等待(180 秒,可配置)。超时仅返回已收到的部分内容,不打断对方执行。需要并行处理时传入 `detach`,投递后立即返回 `askId`,后续通过 `ask_result` 查询。
|
|
117
|
+
- **成本**:详见「💸 用量与成本」。
|
|
76
118
|
|
|
77
119
|
一句话用法:直接说「问一下极简遥控器那个会话:进度到哪了」。
|
|
78
120
|
|
|
@@ -81,14 +123,13 @@ npx @deepseek-ai/dsh web
|
|
|
81
123
|
| 键 | 类型 | 默认 | 说明 |
|
|
82
124
|
|---|---|---|---|
|
|
83
125
|
| `enabled` | boolean | `true` | 启用顾问群 |
|
|
84
|
-
| `discussion.maxRounds` | number | `2` |
|
|
126
|
+
| `discussion.maxRounds` | number | `2` | 自动深挖流水线总轮数(1–5;每轮 = 全体顾问接力,第 2 轮起先由驱动模型追问) |
|
|
85
127
|
| `discussion.maxAdvisorsPerCall` | number | `3` | 单次咨询最大顾问数(1–10) |
|
|
86
|
-
| `discussion.autoDeepen` | boolean | `true` |
|
|
128
|
+
| `discussion.autoDeepen` | boolean | `true` | 是否在轮与轮之间生成更深追问;不影响轮数——关掉后仍按 `maxRounds` 重复提问,综合结论照常生成 |
|
|
87
129
|
| `discussion.driverModel` | object | – | 兜底驱动模型 `{provider, model}`(会话头信息不可读时) |
|
|
88
130
|
| `discussion.advisorTimeoutMs` | number | `600000` | 单顾问调用超时(毫秒,1000–600000),双通道均生效 |
|
|
89
131
|
| `discussion.driverTimeoutMs` | number | `600000` | 驱动模型生成超时(深挖追问/综合结论;毫秒,1000–1200000) |
|
|
90
|
-
| `discussion.advisorTools` | string | `'readonly'` |
|
|
91
|
-
| `advisors[].tools` | string | – | 每个顾问的**单独**工具调用范围覆盖(`readonly`/`all`/`off`;未设置时跟随全局默认);设置卡片上:可直连的顾问显示下拉,DSH 内置通道顾问显示“不可配置”。 |
|
|
132
|
+
| `discussion.advisorTools` | string | `'readonly'` | 全局默认顾问工具调用范围(未单独设置时):`readonly`(只读白名单 read/grep/glob/web_search/web_fetch…)|`all`(全部会话可见工具,含可写,慎用)|`off`(关闭)。仅直连通道(OpenAI/Anthropic)支持工具调用(要求见「🔒 安全与隐私」)。 |
|
|
92
133
|
| `quota.enabled` | boolean | `true` | 启用每日咨询上限(成本安全阀) |
|
|
93
134
|
| `quota.maxPerDay` | number | `50` | 每日 UTC 日周期内最多新咨询次数(1–100000);`quota.enabled=false` 时不生效 |
|
|
94
135
|
| `trigger.requireClassifier` | boolean | `true` | 发起前先跑前置分类器 |
|
|
@@ -99,39 +140,58 @@ npx @deepseek-ai/dsh web
|
|
|
99
140
|
| `trigger.jev.confidenceThreshold` | number | `0.6` | Jev 判「该找顾问」为是(或 0–1 分数达到该值)时升级 |
|
|
100
141
|
| `trigger.jev.highRiskThreshold` | number | 跟随 `jev.confidenceThreshold` | Jev 判「高风险」为是(或 0–1 分数达到该值)时标记高风险;未设置则跟随 `trigger.jev.confidenceThreshold` |
|
|
101
142
|
| `trigger.jev.useEnglishState` | boolean | `false` | 判定改用主模型附带的英文概要(`questionEn`)而非中文原问;展示与会话记录仍为中文 |
|
|
143
|
+
| `swarm.enabled` | boolean | `true` | 跨会话功能总开关;关闭后 `ask_session` / `list_sessions` / `ask_result` 明确拒绝,不静默失效 |
|
|
144
|
+
| `swarm.defaultMode` | string | `'wait'` | 默认应答方式:`wait` 同步等待|`detach` 异步投递(显式传参永远优先) |
|
|
145
|
+
| `swarm.defaultWaitMs` | number | `180000` | 默认等待上限(毫秒,1000–600000) |
|
|
146
|
+
| `swarm.resultTtlHours` | number | `24` | 异步结果保留时长(小时,1–720;取结果时顺带清理过期记录) |
|
|
147
|
+
| `swarm.rosterIncludeSubagents` | boolean | `false` | 会话名录是否列出子代理会话 |
|
|
148
|
+
| `swarm.routingEnabled` | boolean | `true` | 没点名目标时,按问题匹配候选(只提候选,绝不自动投递) |
|
|
149
|
+
| `swarm.routingTopN` | number | `3` | 返回的候选个数(1–8) |
|
|
150
|
+
| `swarm.routingEngine` | string | `'local'` | 路由方式:`local` 本地打分|`jev` 交 Jev 排序(闭合选项,失败自动回落本地) |
|
|
151
|
+
| `swarm.routingAsk` | string | `'gated'` | 是否该问别的会话:`off`|`gated` 本地预过滤 + Jev(默认)|`always` 每轮都问 |
|
|
152
|
+
| `swarm.routingExclude` | string[] | `[]` | 不参与路由的会话(标题关键词或完整 sessionId,一行一个);被点名时拒绝投递 |
|
|
102
153
|
| `ui.theme` | string | `retro-green` | 卡片主题(retro-green / retro-amber / retro-blue) |
|
|
103
154
|
| `ui.showTimestamps` | boolean | `true` | 显示时间戳 |
|
|
104
155
|
| `ui.autoExpand` | boolean | `true` | 自动展开卡片 |
|
|
156
|
+
| `advisors[].tools` | string | – | 每个顾问的**单独**工具调用范围覆盖(`readonly`/`all`/`off`;未设置时跟随全局默认);设置卡片上:可直连的顾问显示下拉,DSH 内置通道的顾问会提示「当前为 DSH 内置通道(复用 DSH 凭据),不支持工具调用」——为其配置 `baseURL`/`apiKey`/`apiKeyEnv` 走直连通道后即可启用。 |
|
|
157
|
+
| `advisors[].thinking` | string | – | 单顾问思考模式:`enabled` / `disabled`;未设置为供应商默认,`disabled` 强制不带思考块(内置通道映射为 `reasoningEffort: 'off'`) |
|
|
158
|
+
| `advisors[].reasoningEffort` | string | – | 单顾问思考强度:`off` / `low` / `high` / `max`;未设置为供应商/模型默认,`off` 在每次请求中关闭思考 |
|
|
159
|
+
| `advisors[].temperature` | number | – | 采样温度(0–2);未设置为供应商默认 |
|
|
160
|
+
| `advisors[].maxTokens` | number | – | 单次回答的 token 上限(≥1);未设置为供应商默认 |
|
|
161
|
+
| `advisors[].authMode` | string | – | Anthropic 直连通道的鉴权头风格:`x-api-key` / `bearer` |
|
|
162
|
+
| `advisors[].avatar` | string | – | 卡片上的短头像(emoji 或 ASCII) |
|
|
105
163
|
| `advisors` | array | `[]` | 顾问列表(provider/model/baseURL/apiKey/apiKeyEnv/protocol…,key 均标 `role('secret')`) |
|
|
106
164
|
|
|
107
165
|
> `discussion.parallel` 与 `discussion.stopOnConsensus` 为弃用遗留项,仅保留以兼容已存配置。
|
|
108
166
|
|
|
109
167
|
## 🧮 每日配额
|
|
110
168
|
|
|
111
|
-
|
|
169
|
+
插件内置成本安全阀。它按 UTC 日历日统计新咨询次数,默认上限为 50 次,支持在设置页调整或直接关闭。配额仅限制新发起的咨询,通过「▶ 继续聊天」续跑及后续追问均不计入。
|
|
170
|
+
|
|
171
|
+
判断与自增在同一同步块内完成,避免并发穿透。计数结果原子落盘于 `$DSH_HOME/storages/advisor-group/daily-guard.json`,重启不丢失,并在 UTC 零点自动重置。额度耗尽时,`ask_advisors` 会拒绝启动并返回说明。设置页实时显示「今日剩余咨询次数 X / Y」;关闭上限开关后仅统计数据,不执行拦截。
|
|
112
172
|
|
|
113
173
|
## 💸 用量与成本
|
|
114
174
|
|
|
115
|
-
|
|
175
|
+
一次咨询由一连串模型调用组成。在默认配置(`maxRounds = 2`、最多 3 位顾问)下,发起一次新咨询共包含 **8 次**模型调用:第 1 轮 3 位顾问各调用 1 次;第 2 轮驱动模型追问 1 次,随后 3 位顾问各调用 1 次;最后由驱动模型生成综合结论 1 次。若顾问启用了工具调用,每轮工具往返会额外增加调用(单顾问上限 4 轮工具交互,外加 1 次最终文字作答),实际消耗会高于 8 次。
|
|
116
176
|
|
|
117
|
-
|
|
177
|
+
费用按渠道独立结算:顾问按各自配置的渠道与模型计费,驱动模型按当前会话的模型计费。若需控制成本,可调低 `maxRounds`、减少顾问人数、将顾问工具设为 `tools: 'off'`,并在「🧮 每日配额」中设定合理的每日上限。咨询中断后,点击「▶ 继续聊天」仅为未完成的顾问补答,无需重跑整场流程,成本显著低于新建咨询。
|
|
118
178
|
|
|
119
|
-
`ask_session`
|
|
179
|
+
`ask_session` 不占用咨询配额。它仅唤醒目标会话并触发一次常规应答,费用记入目标会话自身模型,不视作新发起的顾问咨询。
|
|
120
180
|
|
|
121
181
|
## 🔒 安全与隐私
|
|
122
182
|
|
|
123
|
-
- 直连 API Key
|
|
124
|
-
- `/advisor-group/*`
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
- **`advisorTools: 'all'`
|
|
183
|
+
- 直连 API Key 存于 DSH `settings.yaml`(标注为 secret 字段,官方 `describe` 不向前端回传)。若不希望密钥落盘,可使用 `apiKeyEnv` 环境变量模式。
|
|
184
|
+
- `/advisor-group/*` 路由采用单机共享 token 机制,面向本地单用户场景。若需暴露到局域网或公网,必须在外层前置反向代理鉴权。
|
|
185
|
+
- 诊断端点在服务端解出真实密钥后请求服务商,并对目标 URL 执行 https-only 与 loopback 的 SSRF 校验。DNS rebinding 属于已知的架构边界。
|
|
186
|
+
- 每日咨询上限的计数机制、持久化文件与重置规则详见「🧮 每日配额」。
|
|
187
|
+
- 分类器影子模式:开启 `trigger.requireClassifier` 时,所有判定样本均会记录到只读端点 `/advisor-group/shadow`(包含 `@顾问群` 与重复 3 次等跳过分类器的场景,标记为 `provider: 'bypass'`),仅用于阈值调优,不影响运行逻辑。
|
|
188
|
+
- **`advisorTools: 'all'` 属于高风险作用域**:该模式会将当前会话所有可见工具(包括 `pwsh`/`bash`/`write` 等写操作与命令执行类)暴露给顾问模型,并由官方守卫管线执行。建议仅对受信任的本地模型开启。工具调用仅支持直连通道(OpenAI/Anthropic);DSH 内置通道复用宿主凭据,不支持工具调用,如需使用请为顾问配置 `baseURL`/`apiKey`/`apiKeyEnv` 直连。所有非只读工具调用均输出 `console.warn` 留存审计日志。日常建议配置为 `readonly` 或 `off`(只读白名单见「✨ 功能特性」)。
|
|
129
189
|
|
|
130
190
|
## ⚠️ 已知限制
|
|
131
191
|
|
|
132
|
-
- DSH
|
|
133
|
-
-
|
|
134
|
-
- 跨会话提问(`ask_session
|
|
192
|
+
- DSH 宿主进程崩溃时,未刷新的卡片可能仍保持 **LIVE** 标识(目前前端已加入断线提示)。页面刷新后会按会话持久化日志恢复真实状态。
|
|
193
|
+
- 风险提示基于客观运行事实生成,包括超时/网络中断引起的截断,以及手动停止导致的取消。提示会展示在已停止或已完成的卡片中,并随 `ask_advisors` 返回主模型。插件不扫描顾问回复的关键词;早期版本那套"凭字眼猜立场"的启发式已在 0.1.1 移除,原因是它连「没有任何风险」这样的否定句也会当成风险提示——盲测里 4 条纯否定句全部误报。
|
|
194
|
+
- 跨会话提问(`ask_session`)目前未提供针对单会话隐藏工具的独立开关。`swarm.enabled` 作为全局总开关,开启后三个工具在所有会话中均可见,依靠提示词约束仅在明确要求时调用。暂不支持检测两个会话循环互相等待的死锁情况。
|
|
135
195
|
|
|
136
196
|
## 🛠️ 开发
|
|
137
197
|
|