dsh-advisor-group 0.1.3 → 0.1.5

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 CHANGED
@@ -1,144 +1,276 @@
1
1
  # dsh-advisor-group
2
2
 
3
- > A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) plugin that lets the main model consult **multiple expert advisor models** in a retro chat-group card — for professional, long-tail world-knowledge, high-risk, or uncertain questions.
3
+ > Let one [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) session **borrow wisdom from other models** and **reuse the memory of other sessions**.
4
4
 
5
- [![dsh-plugin](https://img.shields.io/badge/DSH%20plugin-dsh--plugin-3f8cff)](https://github.com/topics/dsh-plugin)
5
+ [中文](README.md) · [Installation](#compatibility-and-installation) · [Quick start](#quick-start) · [Core features](#core-features) · [Configuration](#configuration-reference) · [Development](#development)
6
6
 
7
- 📖 [中文文档 / Chinese: README.md](README.md)
7
+ [![npm version](https://img.shields.io/npm/v/dsh-advisor-group.svg)](https://www.npmjs.com/package/dsh-advisor-group)
8
+ [![DSH plugin](https://img.shields.io/badge/DSH%20plugin-dsh--plugin-3f8cff)](https://github.com/topics/dsh-plugin)
9
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
8
10
 
9
- ---
11
+ | Capability | What it helps you do |
12
+ | --- | --- |
13
+ | **Multi-model consultation (Advisor Group)** | For professional, high-risk, or uncertain questions, let expert advisors answer in sequence before a driver model synthesizes their conclusions. |
14
+ | **Cross-session collaboration (global swarm)** | Ask another existing session and reuse the code it read, the investigation it completed, and the decisions it settled instead of starting over. Beyond this session's subagent capability, it lets the model write across projects. |
15
+ | **Advisor Corps role cards** | Assign a name and duty to a session. You can then say “ask 小明” without remembering its title or sessionId. |
10
16
 
11
- <img width="865" height="983" alt="1788679983177" src="https://github.com/user-attachments/assets/dad2f2be-acf6-41d7-b61a-c11596e48408" />
17
+ ## Compatibility and installation
12
18
 
13
- ## ✨ Features
19
+ | Item | Details |
20
+ | --- | --- |
21
+ | DSH | The current development baseline is `0.2.1-rc.3`; the declared peer range is `>=0.1.7-rc.2 <0.3.0` |
22
+ | Node.js | Required for source builds: `^22.19.0 || >=24.0.0` |
23
+ | Applications | DSH Web and the desktop app, sharing the same client bundle |
14
24
 
15
- - **Auto-deepen consultation pipeline** — one `ask_advisors` call runs up to `maxRounds` rounds automatically: each round is a *driver deep-question → advisor A → advisor B (sees A) → advisor C (sees A+B) → …* sequential relay, closed by a driver-generated synthesis conclusion.
16
- - **Zero-config driver model** — the driver formulation of deep follow-ups reuses the current agent's provider/model, so no extra API key or model setup is needed; it falls back to `discussion.driverModel` when the session header is unavailable.
17
- - **Three ways to activate** — `@顾问群` mention (force-start), same question repeated 3 times without resolution, or main-model self-assessed confidence below the threshold.
18
- - **Optional Jev semantic pre-classification (off by default)** — when `trigger.jev.enabled` is on, a configured Jev model judges non-force-started consultations first (escalation / high-risk / web-search fit, thresholds under `trigger.jev.*`); if Jev is unavailable the local rules-based classifier still decides. With `trigger.jev.useEnglishState`, the judge reads the caller-supplied English gist (`questionEn`) instead of the Chinese question — display and session records stay Chinese.
19
- - **Retro CRT chat cards** — green/amber/blue CRT themes, scanlines, LIVE/DONE headers, auto-expanded thinking panel with auto-scroll; advisor Markdown rendered with a link-protocol whitelist (headings, lists, code, quotes, links, tables).
20
- - **Ask another session (`ask_session`)** — hand one question to another live session — by exact session id, exact title, or a natural partial name such as the workspace name (`极简遥控器`) — and read what that session said between the delivery receipt and its next whole-session idle. The result is explicitly **not** a one-to-one reply, and the asking side renders the exchange in the same retro chat-group card. Guards: hop ≤ 2, no self/subagent target, one wait per session, a 32 KB payload budget; the target's turn is never cancelled.
21
- - **Provider presets (11 platforms · 26 presets)** — DeepSeek, Moonshot Kimi, Kimi Code, Aliyun Bailian, Zhipu AI, OpenAI, Claude, Gemini, SiliconFlow, AIHubMix, OpenRouter (OpenAI/Anthropic-compatible variants included).
22
- - **Security-minded by design** — API keys use official `SecretField` semantics (never returned to the browser; `apiKeysByProvider` key history is server-side only), SSRF-guarded diagnostics (https-only / loopback, no IP literals, no redirects), per-boot token auth on `/advisor-group/*` routes, and an atomic **configurable daily consultation cap** (default 50, can be disabled) persisted across restarts.
23
- - **Runtime toggle** — `toggle_advisor_group` enables/disables the plugin and persists the flag to settings.
25
+ Earlier versions inside the peer range have not all been re-verified on a live host. Cross-session features also require the host to provide `sessionController`; the related tools report an explicit error when it is unavailable, while multi-model consultation remains unaffected.
24
26
 
25
- ## ✅ Compatibility
27
+ ### Install from npm
26
28
 
27
- | Surface | Status |
28
- |---|---|
29
- | Harness | DeepSeek Harness `0.1.7-rc.2` → `0.2.x` (incl. `0.2.0-rc.1`) |
30
- | Node | `^22.19.0 \|\| >=24.0.0` |
31
- | Platforms | DSH Web and the desktop app (same client bundle) + headless host logic |
32
-
33
- ## 📦 Installation
29
+ For DSH Web:
34
30
 
35
31
  ```sh
36
- # 1. Install the plugin (one package, everything included)
37
32
  dsh plugin --profile web add dsh-advisor-group
33
+ ```
34
+
35
+ Restart DSH Web the way you normally do, then hard-refresh the browser (`Ctrl+Shift+R`).
36
+
37
+ **Desktop app**: open **Plugins → Add plugin** inside the app and enter `dsh-advisor-group`. The desktop app owns its profile; `dsh plugin --profile desktop …` is refused by design, not because your permissions are insufficient.
38
+
39
+ ### Install from source (main)
40
+
41
+ ```sh
42
+ git clone https://github.com/xingzhen199186/dsh-advisor-group.git
43
+ cd dsh-advisor-group
44
+ npm install --legacy-peer-deps --no-audit --no-fund
45
+ npm run build
46
+ npm pack
47
+
48
+ # Use the actual filename printed by npm pack.
49
+ # The current package.json version is 0.1.5, producing:
50
+ dsh plugin --profile web add ./dsh-advisor-group-0.1.5.tgz
51
+ ```
52
+
53
+ For a source-built desktop installation, enter the `.tgz` file's absolute path under **Plugins → Add plugin**. Restart the corresponding DSH application after installation.
54
+
55
+ ## Quick start
56
+
57
+ ### Path 1: consult several models
58
+
59
+ 1. Open **Settings → Plugins → 顾问群 (Advisor Group)**.
60
+ 2. Add at least one advisor and select its provider route and model. Use **获取模型列表** (Fetch model list) to retrieve the route's model catalogue.
61
+ 3. Prefix your question with `@顾问群` and include the necessary background. For example:
62
+
63
+ ```text
64
+ @顾问群 Review this plugin's cross-session collaboration design.
65
+ Background: DSH Web and desktop share one codebase. The design must never deliver automatically or guess a target session.
66
+ ```
67
+
68
+ `@顾问群` forces a consultation. The main model can also escalate ordinary professional questions under its routing rules, including when the same unresolved question appears three times or its reported confidence falls below the threshold.
69
+
70
+ <img width="720" alt="Advisor Group settings page: sections for enable and daily quota, advisors, consultation behavior, triggers and classification, global swarm, and advanced settings, all collapsed by default" src="https://raw.githubusercontent.com/xingzhen199186/dsh-advisor-group/main/docs/images/advisor-group-settings.png" />
71
+
72
+ The settings page follows the setup path, with every section folded by default; each open section has its own save row.
73
+
74
+ ### Path 2: ask another session by role name
75
+
76
+ 1. Open the **顾问团 (Advisor Corps)** tab in any conversation. If the tab bar runs out of space, it may appear under the **…** menu.
77
+ 2. Fill in **身份姓名** (Role name) and **主要负责** (Main responsibility) for the target session, then click **加入顾问团** (Add to Advisor Corps) or **保存** (Save). Alternatively, click **用主模型生成** (Generate with main model), review the suggestions, and save them individually or through **保存全部更改** (Save all changes).
78
+ 3. Return to the conversation and ask naturally:
79
+
80
+ ```text
81
+ Ask 小明: what changed in the Advisor Corps role cards today?
82
+ ```
83
+
84
+ Or ask several members at once:
85
+
86
+ ```text
87
+ Ask 小明 and 阿龙: review this design for desktop and Web omissions respectively, then combine both conclusions.
88
+ ```
89
+
90
+ Generated cards only fill drafts; **they take effect only after you save them, and existing cards are never overwritten**. Session titles, working-directory names, and sessionIds remain valid target spellings.
91
+
92
+ ## Core features
93
+
94
+ ### Multi-model consultation: deepen the discussion automatically
95
+
96
+ One `ask_advisors` call runs the whole consultation:
38
97
 
39
- # 2. Restart DSH web
40
- npx @deepseek-ai/dsh web
98
+ ```text
99
+ Original question
100
+ → Round 1: Advisor A → Advisor B (sees A) → Advisor C (sees A and B)
101
+ → Later rounds: a deeper driver question → the same advisor relay
102
+ → A synthesis from the driver model
41
103
  ```
42
104
 
43
- > **From source (development)**
44
- > ```sh
45
- > npm install --legacy-peer-deps --no-audit --no-fund
46
- > npm run build
47
- > dsh plugin --profile web add ./dsh-advisor-group-<version>.tgz # after npm pack (version from package.json)
48
- > ```
105
+ <img width="865" height="983" alt="An Advisor Group consultation card showing actions, thinking, tool calls, and the final response in step order" src="https://github.com/user-attachments/assets/dad2f2be-acf6-41d7-b61a-c11596e48408" />
49
106
 
50
- > **Desktop app**: the desktop app owns its own profile — `dsh plugin --profile desktop …` is refused by design, not by permissions. Install from inside the app instead: **Plugins** in the sidebar → **Add plugin** → enter the package name or the absolute path of a local tarball.
107
+ - **Controlled rounds**: `maxRounds` ranges from 1 to 5 and defaults to 2. With `autoDeepen` disabled, later rounds repeat the original question; the final synthesis is still generated.
108
+ - **No separate driver route to configure**: the driver writes follow-ups and the synthesis through the current session's provider and model. It falls back to `discussion.driverModel` when that route cannot be read.
109
+ - **Advisors can use session tools**: the default scope is read-only. Calls appear in the card, and after at most four tool rounds the advisor must answer in text. Set the scope globally or override it per advisor.
110
+ - **A streaming card that preserves the process**: the green CRT card renders `📋 Action → 💭 Thinking → ⛭ Tool → 📄 Response`. Thinking and tool details start collapsed; the response starts expanded.
111
+ - **Resume from the checkpoint**: after a manual stop or host restart, **▶ 继续聊天** (Resume chat) completes only the missing advisors and rounds instead of asking completed advisors again.
112
+ - **Optional Jev pre-classification**: off by default. When enabled, Jev can judge whether advisors are needed, whether the request is high-risk, and whether web search is more appropriate. Failures fall back to the local rule-based classifier.
113
+ - **28 provider presets across 12 platforms**: DeepSeek, Moonshot Kimi, Kimi Code, Aliyun Bailian, Zhipu AI, OpenAI, Claude, Gemini, SiliconFlow, AIHubMix, OpenRouter, and OpenCode Go.
51
114
 
52
- ## 🚀 Quick start
115
+ ### Advisor Corps: give each session a durable identity
53
116
 
54
- <img width="791" height="797" alt="image" src="https://github.com/user-attachments/assets/5fba07bc-8c36-40f1-b423-d206aa495192" />
117
+ A session title describes a window, but it may not be a durable way to address that session. Advisor Corps binds a **name and duty** to the session id and stores it locally in `$DSH_HOME/storages/advisor-group/corps.json`; it never renames the session.
55
118
 
56
- 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.
58
- 3. Just start a conversation:
59
- - type `@顾问群` in your question to force a consultation, or
60
- - ask a professional/uncertain question — the plugin escalates automatically when appropriate.
119
+ <img width="760" alt="Advisor Corps panel: one row per visible session, showing its role name, duty, copy-name action, and save controls" src="https://raw.githubusercontent.com/xingzhen199186/dsh-advisor-group/main/docs/images/advisor-corps-panel.png" />
61
120
 
62
- `ask_advisors` tools available to the model: `ask_advisors`, `toggle_advisor_group`, `ask_session`.
121
+ - Names are globally unique and case-insensitive, with a 40-character limit. Duties can contain up to 200 characters.
122
+ - **Generate with main model** uses the model of the **currently open conversation**. Each pass proposes cards for at most 12 unassigned sessions, fills drafts only, and never saves automatically.
123
+ - Edit rows, copy role names, save in batch, or remove cards. Cards whose sessions are archived, excluded, outside every workspace, or no longer present appear under **未挂接** (Unlinked), where you can keep or delete them.
124
+ - `list_sessions` includes role names and duties. `ask_session` and `ask_sessions` accept an exact role name, which takes precedence over a session title with the same name.
63
125
 
64
- ## 🔗 Ask another session
126
+ ### Cross-session collaboration: ask one session or a group
65
127
 
66
- Besides consulting advisors, the plugin ships one more tool, `ask_session`: it hands a question to **another live session** (another window or tab in the same host) and wakes that session to answer.
128
+ - **Single-session asks**: `ask_session` waits synchronously by default. With `detach: true`, it returns an `askId` immediately so you can read the result later through `ask_result`.
129
+ - **Batch asks**: `ask_sessions` sends one question to 1–8 explicit targets. `mode: 'all'` waits for every target; `mode: 'any'` returns when the first target settles while the others continue and write their own records.
130
+ - **Automatic answer copy-back**: `swarm.deliverBack` is enabled by default. Answers that finish as `done` are copied back into the asking session; timeouts and errors are not. A batch combines all completed answers into one copy-back message.
131
+ - **Traceable results**: every delivered target gets its own `askId`. An asynchronous record can be `waiting`, `done`, `timeout`, `error`, or `interrupted`; the last state means a host restart prevented the wait from completing.
132
+ - **No guessed targets**: when `ask_session` has no target, routing returns candidates without delivering anything. `ask_sessions` performs no routing; unresolved, ambiguous, or excluded targets are reported in its skipped list with reasons.
133
+ - **Explicit collaboration guards**: at most two relay hops; no self or subagent targets; one waiting `ask_session` per asking session; a 32,000-byte combined limit for question and context; and no cancellation of a target turn when the wait times out.
67
134
 
68
- - **Picking the target**: an exact session id, an exact title, or a natural partial name — the workspace directory name is enough (a session under `I:\极简遥控器\…` is found by saying 「极简遥控器」). One match is used directly; several matches become a candidate list (**running** sessions and recently active ones first) for you or the model to choose from. It never guesses.
69
- - **What you get back**: everything the target said between the **delivery receipt** and its next **whole-session idle** — "what that session said in that window", **not** a one-to-one reply to your message. On timeout (180 s by default, configurable) it returns what it already collected and does **not** interrupt the target.
70
- - **What you see**: the asking side shows a chat-group card — first "已投递,等待对方回应…", then the target's answer bubble and the "📌 投递说明" note (which delivery channel was used and how the wait ended). A failed target lookup opens no card; the error stays in the tool row.
71
- - **Guards**: hop ≤ 2 (no ping-pong relay), no self or subagent target, one concurrent wait per session, a 32 KB payload budget; the target's turn is **never** cancelled.
72
- - **Cost**: one ordinary target turn, billed on the target session's own model; it does **not** count against the daily consultation quota.
135
+ > **What the returned text means**: the tool collects everything the target says between the **delivery receipt** and its next **whole-session idle**. It is not guaranteed to be a one-to-one answer to your question. The `status` field (`ok` / `partial` / `failed`) describes the reliability of that collection.
73
136
 
74
- In one line: say "ask the 极简遥控器 session how far along it is".
137
+ ## Model tool reference
75
138
 
76
- ## ⚙️ Configuration
139
+ The main model calls these tools inside a conversation. **They are not slash commands, and you do not need to write tool arguments yourself.** Cross-session tools should be used only when you explicitly ask to contact another session or window.
140
+
141
+ | Tool | Purpose |
142
+ | --- | --- |
143
+ | `ask_advisors` | Start a multi-model consultation, or append a round with `sessionId + followUp` |
144
+ | `ask_session` | Ask one existing session; supports waiting, detached delivery, and candidate routing |
145
+ | `ask_sessions` | Ask 1–8 explicit targets in a batch; supports `all` and `any` collection modes |
146
+ | `list_sessions` | Read session titles, directories, activity, and Advisor Corps names and duties without waking anyone |
147
+ | `ask_result` | Read one asynchronous result by `askId`, or list recent asks started by this session |
148
+ | `toggle_advisor_group` | Enable or disable multi-model consultation and persist the setting |
149
+
150
+ ## Configuration reference
151
+
152
+ For everyday use, these are the settings you are most likely to adjust:
153
+
154
+ | Key | Default | When to adjust it |
155
+ | --- | --- | --- |
156
+ | `discussion.maxRounds` | `2` | Lower it to reduce cost, or raise it up to 5 for a deeper discussion |
157
+ | `discussion.maxAdvisorsPerCall` | `3` | Control how many advisors join one consultation, from 1 to 10 |
158
+ | `discussion.advisorTools` | `'readonly'` | Use `'off'` to disable advisor tools; reserve `'all'` for fully trusted scenarios |
159
+ | `quota.enabled` / `quota.maxPerDay` | `true` / `50` | Set the daily new-consultation cap; counts continue when the cap is disabled |
160
+ | `swarm.defaultMode` | `'wait'` | Use `'detach'` to make cross-session asks asynchronous by default |
161
+ | `swarm.defaultWaitMs` | `180000` | Adjust the default wait ceiling, from 1000 to 600000 ms |
162
+ | `swarm.deliverBack` | `true` | Disable it to read completed answers only through `ask_result`, without automatic copy-back |
163
+
164
+ <details>
165
+ <summary><strong>Expand the complete configuration reference</strong></summary>
166
+
167
+ ### Discussion and advisors
77
168
 
78
169
  | Key | Type | Default | Description |
79
- |---|---|---|---|
80
- | `enabled` | boolean | `true` | Enable advisor group |
81
- | `discussion.maxRounds` | number | `2` | Total rounds of the auto-deepen pipeline (each round = driver question + all advisors in relay) |
82
- | `discussion.maxAdvisorsPerCall` | number | `3` | Max advisors per call (1–10) |
83
- | `discussion.autoDeepen` | boolean | `true` | Run the auto-deepen pipeline (driver follow-ups + final synthesis) |
84
- | `discussion.driverModel` | object | – | Fallback driver model `{provider, model}` when the session header cannot be read |
85
- | `discussion.advisorTimeoutMs` | number | `600000` | Per-advisor call timeout (ms, 1000–600000), applied to both channels |
86
- | `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. |
88
- | `quota.enabled` | boolean | `true` | Enable the daily new-consultation cap (cost safety valve) |
89
- | `quota.maxPerDay` | number | `50` | Max new consultations per UTC day (1–100000); ignored when `quota.enabled` is `false` |
90
- | `trigger.requireClassifier` | boolean | `true` | Run the pre-classifier before starting |
91
- | `trigger.allowWebFallback` | boolean | `true` | Allow classifier to recommend web search |
92
- | `trigger.confidenceThreshold` | number | `0.6` | Escalate when main-model confidence is below this |
93
- | `trigger.jev.enabled` | boolean | `false` | Use the external Jev model for semantic pre-classification of non-forced consultations; falls back to the local rules-based classifier when Jev is unavailable |
94
- | `trigger.jev.provider` / `trigger.jev.model` | string | `'typesafe'` / `'jev-latest'` | Jev route (`typesafe` or `openrouter`) and model; key via `trigger.jev.apiKey` (secret) or `trigger.jev.apiKeyEnv`; optional `baseURL`, `timeoutMs` (default `10000`) |
95
- | `trigger.jev.confidenceThreshold` | number | `0.6` | Escalate when Jev answers `needsAdvisor` yes (or its 0–1 score reaches this value) |
96
- | `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
- | `trigger.jev.useEnglishState` | boolean | `false` | Judge using the caller-supplied English gist (`questionEn`) instead of the Chinese question; display and session records stay Chinese |
98
- | `ui.theme` | string | `retro-green` | Chat card theme (`retro-green` / `retro-amber` / `retro-blue`) |
99
- | `ui.showTimestamps` | boolean | `true` | Show timestamps |
100
- | `ui.autoExpand` | boolean | `true` | Auto-expand card |
101
- | `advisors[].tools` | string | – | Per-advisor tool-scope override (`readonly`/`all`/`off`; unset follows the global default) |
102
- | `advisors` | array | `[]` | Advisor list (provider/model/baseURL/apiKey/apiKeyEnv/protocol…, keys are `role('secret')`) |
103
-
104
- > `discussion.parallel` and `discussion.stopOnConsensus` are deprecated leftovers kept only for stored-config compatibility.
105
-
106
- ## 🧮 Daily quota
107
-
108
- The plugin ships a cost safety valve: it counts **new consultations** per UTC calendar day, default cap 50, changeable or switchable on the settings page. Only new consultations count — resuming (「▶ continue」) and follow-ups never consume quota. The check and the increment happen inside one synchronous block, so two consultations cannot slip through the same window; the counter is written atomically to `$DSH_HOME/storages/advisor-group/daily-guard.json`, survives restarts, and resets at the UTC day boundary. When the cap is reached `ask_advisors` does not start and returns a one-line explanation; the settings page shows "today's remaining consultations X / Y", and with the cap off the plugin still counts for display without blocking.
109
-
110
- ## 💸 Usage and cost
111
-
112
- One consultation is not one call but a chain of them. With the defaults (`maxRounds = 2`, at most 3 advisors) a new consultation costs roughly 9 model calls: 2 rounds × (1 driver follow-up + 1 per advisor) + 1 driver synthesis. Advisors that use tools add one more model call per tool round (at most 4 rounds per advisor plus one forced text-only round), so the real count can be noticeably higher than 9.
113
-
114
- Billing follows each channel: advisors bill on the route and model you gave them, while the driver bills on the current session's model. To keep costs down, lower `maxRounds`, configure fewer advisors, set `tools: 'off'` for advisors, and pick a daily cap you are comfortable with. Resuming after an interruption ("▶ continue") only asks the advisors that had not finished, so it costs less than starting over.
115
-
116
- `ask_session` is outside the consultation quota: it wakes the target session, which answers with one ordinary turn billed on that session's own model, and it never counts as a new consultation.
117
-
118
- ## 🔒 Security & privacy
119
-
120
- - Direct API keys can be stored in the DSH `settings.yaml` (marked secret, never returned to the browser by `describe`); prefer `apiKeyEnv` (env-var mode) if you don't want keys on disk.
121
- - `/advisor-group/*` routes use a per-boot shared token for local single-user use — **not** multi-user auth. Add a reverse-proxy auth layer before LAN exposure.
122
- - Diagnostics endpoints resolve the real key server-side and validate the target URL against an https-only / loopback SSRF guard; DNS-rebinding protection is a documented out-of-scope limitation.
123
- - The daily consultation cap (configurable via `quota.*`; default 50; can be disabled) is persisted to `$DSH_HOME/storages/advisor-group/daily-guard.json` (UTC day key), so restarts don't reset it.
124
- - Classifier shadow mode appends one observation sample per non-forced classification (read-only `/advisor-group/shadow`), used for threshold tuning only — never influences behavior.
125
- - **`advisorTools: 'all'` is an elevated-risk scope.** It exposes every session-visible tool to the advisor models, including writable/execution ones (`pwsh`, `bash`, `write`, config/SSH tools), executed through the official guarded pipeline. Only enable it for advisors you trust (e.g., your own local models), keep them on the direct-http channel, and note that every non-read-only invocation is logged with `console.warn` for audit. Prefer the default `readonly` (read/grep/glob/web_search/web_fetch/scan_discover/list_imported_sessions) or `off`.
126
-
127
- ## ⚠️ Known limitations
128
-
129
- - A hard crash of the DSH host can leave an already-open card showing **LIVE** until the page is refreshed (a disconnect notice now appears); refresh restores the true state from the session log.
130
- - Risk notes are **fact-based, never text-based**: a consultation reports truncation (an advisor stream cut off by timeout or network) and cancellation/stop. Users see them on **stopped or completed** cards and in the session log, and the main model receives them in the `ask_advisors` result. Advisor prose is not scanned for keywords — the earlier text heuristic was removed in 0.1.1 because it also fired on plain negations such as 「没有任何风险」.
131
- - Cross-session asking (`ask_session`) has no settings switch yet, so the tool is visible in every session and relies on its own description to keep use to explicit user requests; two sessions asking each other at the same time is not detected.
132
-
133
- ## 🛠️ Development
170
+ | --- | --- | --- | --- |
171
+ | `enabled` | boolean | `true` | Enable multi-model consultation |
172
+ | `discussion.maxRounds` | number | `2` | Total consultation rounds, 1–5 |
173
+ | `discussion.maxAdvisorsPerCall` | number | `3` | Maximum advisors in one consultation, 1–10 |
174
+ | `discussion.autoDeepen` | boolean | `true` | Generate a deeper driver question before later rounds; when off, repeat the original question and still synthesize a conclusion |
175
+ | `discussion.driverModel` | object | – | Fallback `{ provider, model }` when the current session route cannot be read |
176
+ | `discussion.advisorTimeoutMs` | number | `600000` | Per-advisor timeout, 1000–600000 ms |
177
+ | `discussion.driverTimeoutMs` | number | `600000` | Timeout for a follow-up or synthesis, 1000–1200000 ms |
178
+ | `discussion.advisorTools` | string | `'readonly'` | Default advisor tool scope: `readonly` / `all` / `off` |
179
+ | `advisors` | array | `[]` | Advisor list, including `provider`, `model`, `systemPrompt`, and optional `baseURL`, `apiKey`, `apiKeyEnv`, `protocol`, and related fields |
180
+ | `advisors[].tools` | string | – | Per-advisor tool scope; unset follows `discussion.advisorTools` |
181
+ | `advisors[].thinking` | string | – | `enabled` / `disabled`; unset follows the provider default |
182
+ | `advisors[].reasoningEffort` | string | – | `off` / `low` / `high` / `max`; unset follows the provider or model default |
183
+ | `advisors[].temperature` | number | – | Sampling temperature, 0–2 |
184
+ | `advisors[].maxTokens` | number | – | Token limit for one answer, at least 1 |
185
+ | `advisors[].authMode` | string | – | Anthropic direct-channel authentication header: `x-api-key` / `bearer` |
186
+ | `advisors[].avatar` | string | – | Short card avatar, using an emoji or ASCII text |
187
+
188
+ Advisor tool calling is supported only on direct OpenAI / Anthropic channels. The DSH built-in channel can reuse host credentials for ordinary responses, but it does not support this advisor tool loop.
189
+
190
+ ### Triggers and Jev
191
+
192
+ | Key | Type | Default | Description |
193
+ | --- | --- | --- | --- |
194
+ | `trigger.requireClassifier` | boolean | `true` | Run pre-classification before a non-forced consultation |
195
+ | `trigger.allowWebFallback` | boolean | `true` | Return a no-consultation hint when the classifier recommends web search |
196
+ | `trigger.confidenceThreshold` | number | `0.6` | Allow escalation below this main-model confidence score, from 0 to 1 |
197
+ | `trigger.jev.enabled` | boolean | `false` | Use Jev for semantic pre-classification; failures fall back to local rules |
198
+ | `trigger.jev.provider` / `trigger.jev.model` | string | `'typesafe'` / `'jev-latest'` | Jev route and model; the route can also be `openrouter` |
199
+ | `trigger.jev.baseURL` | string | – | Optional custom Jev endpoint |
200
+ | `trigger.jev.apiKey` / `trigger.jev.apiKeyEnv` | string | – | Jev key or the environment variable containing it; key fields are treated as secrets |
201
+ | `trigger.jev.timeoutMs` | number | `10000` | Jev request timeout, 1000–60000 ms |
202
+ | `trigger.jev.confidenceThreshold` | number | `0.6` | Threshold for deciding that advisors are needed |
203
+ | `trigger.jev.highRiskThreshold` | number | follows the previous setting | High-risk threshold; unset follows `trigger.jev.confidenceThreshold` |
204
+ | `trigger.jev.useEnglishState` | boolean | `false` | Let Jev read the caller-provided English gist `questionEn`; cards and session records keep the original question |
205
+
206
+ ### Global swarm
207
+
208
+ | Key | Type | Default | Description |
209
+ | --- | --- | --- | --- |
210
+ | `swarm.enabled` | boolean | `true` | Cross-session master switch; when off, all four cross-session tools refuse explicitly |
211
+ | `swarm.defaultMode` | string | `'wait'` | Default `ask_session` behavior: `wait` / `detach` |
212
+ | `swarm.defaultWaitMs` | number | `180000` | Default wait ceiling, 1000–600000 ms |
213
+ | `swarm.resultTtlHours` | number | `24` | How long asynchronous results are retained, 1–720 hours |
214
+ | `swarm.deliverBack` | boolean | `true` | Copy `done` answers back automatically; never auto-copy `timeout` / `error` |
215
+ | `swarm.rosterIncludeSubagents` | boolean | `false` | Show subagents in the roster; delivery tools still refuse subagent targets |
216
+ | `swarm.routingEnabled` | boolean | `true` | Suggest candidates when `ask_session` has no target; never deliver automatically |
217
+ | `swarm.routingTopN` | number | `3` | Number of candidates, 1–8 |
218
+ | `swarm.routingEngine` | string | `'local'` | `local` ordering or `jev` reranking of a closed candidate set; failures fall back to local |
219
+ | `swarm.routingAsk` | string | `'gated'` | Whether to suggest a cross-session ask: `off` / `gated` / `always`; a suggestion never delivers |
220
+ | `swarm.routingExclude` | string[] | `[]` | Sessions excluded from the swarm, its roster, and its candidates; direct asks are refused too |
221
+
222
+ ### Legacy compatibility fields
223
+
224
+ `discussion.parallel` and `discussion.stopOnConsensus` are deprecated and retained only so stored configurations remain readable.
225
+
226
+ `ui.theme`, `ui.showTimestamps`, and `ui.autoExpand` are also legacy fields. The current client does not read them, so changing them has no effect on the card's appearance or expansion behavior. The current card uses a green CRT style; thinking and tool details start collapsed.
227
+
228
+ </details>
229
+
230
+ ## Usage, quota, and cost
231
+
232
+ A consultation that completes `R` rounds with `N` advisors and no tool calls normally makes **`R × (N + 1)` model calls**: `R × N` advisor responses, `R - 1` deepening questions, and one final synthesis. With the default `R = 2` and three advisors actually participating, this produces 8 calls.
233
+
234
+ Actual usage also depends on these behaviors:
235
+
236
+ - Advisor tool loops add model calls. The driver and every advisor are billed through their respective routes.
237
+ - Generating role cards calls the current conversation's model. Jev classification or routing is also billed through its configured route when enabled.
238
+ - Cross-session asks do not consume the advisor-consultation quota, but they wake target sessions and consume each target's own model quota. Batch asks are accounted for per target.
239
+
240
+ The daily quota counts **new consultations per UTC calendar day**, with a default cap of 50. Resumes, follow-up rounds, and cross-session asks do not consume this quota. The counter lives in `$DSH_HOME/storages/advisor-group/daily-guard.json`; disabling the cap keeps counting without blocking requests. To control cost, reduce rounds or advisors, disable advisor tools, and set a daily cap.
241
+
242
+ ## Security and privacy
243
+
244
+ - **Keys are not returned to the browser**: direct API keys are stored in the local DSH configuration under `SecretField` semantics. Use `apiKeyEnv` if you prefer not to write a key into that configuration.
245
+ - **Local routes require a token**: `/advisor-group/*` uses a shared token generated at startup and is designed for a local single-user setup. Add reverse-proxy authentication before exposing it to a LAN or the public internet.
246
+ - **Diagnostic requests are constrained**: model-list and connection-test calls run server-side and validate the target URL, protocol, and redirects. DNS rebinding remains a known architectural boundary.
247
+ - **Use `advisorTools: 'all'` with care**: it exposes every session-visible tool to advisors, including file writes and command execution. Calls still pass through DSH's tool pipeline and permission guards. Keep `readonly` or `off` for everyday use; non-read-only tool calls emit warning logs.
248
+ - **Cross-session content leaves a record**: questions and context are permanently recorded in target session logs, so send only what is necessary. Role-card generation sends session titles, working directories, and activity hints to the current main model. The cards themselves are stored locally and contain no API keys.
249
+ - **Local state is centralized**: consultation snapshots, asynchronous ask records, role cards, and quota data live under `$DSH_HOME/storages/advisor-group/`. Classifier shadow logs are used only for threshold tuning and do not influence runtime decisions.
250
+
251
+ ## Known limitations
252
+
253
+ - After an abrupt DSH host crash, an unrefreshed card may still display **LIVE**. Refreshing restores its actual state from the persisted session log; the client already shows a disconnection notice.
254
+ - Risk notices are based on objective events: truncation caused by timeout or network interruption, and cancellation caused by a manual stop. The plugin no longer guesses risk by scanning advisor prose for words such as “risk” or “uncertain”.
255
+ - Cross-session tools have no per-session visibility switch. When `swarm.enabled` is on, all four tools are visible to sessions; when off, calls are refused explicitly. Mutual waits between two sessions are not specifically detected as deadlocks.
256
+ - The Advisor Corps panel displays at most 50 visible sessions. Automatic generation handles at most 12 unassigned sessions per pass; run it again to continue filling the roster.
257
+
258
+ ## Development
134
259
 
135
260
  ```sh
136
261
  npm install --legacy-peer-deps --no-audit --no-fund
137
262
  npm run typecheck
138
- npm test # vitest suite, incl. provider streaming contracts against a local fake LLM server
139
- npm run build # tsdown; client bundle must NOT be built with minify: true
263
+ npm test
264
+ npm run build
265
+ npm pack --dry-run
140
266
  ```
141
267
 
142
- ## 📄 License
268
+ The Vitest suite covers provider streaming contracts, cross-session delivery and waits, role cards, settings redaction, and recovery boundaries. Builds use tsdown. The client bundle **must not use `minify: true`**, because minification breaks the plugin-id form required by the startup guard.
269
+
270
+ - [Changelog](CHANGELOG.md)
271
+ - [Issue tracker](https://github.com/xingzhen199186/dsh-advisor-group/issues)
272
+ - [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
273
+
274
+ ## License
143
275
 
144
276
  [Apache License 2.0](LICENSE) © 2026 dsh-advisor-group contributors.