dsh-advisor-group 0.1.4 → 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 +226 -153
- package/README.md +276 -207
- package/lib/client.js +1520 -890
- package/lib/index.d.mts +51 -1
- package/lib/index.mjs +1568 -150
- package/package.json +103 -100
package/README.en.md
CHANGED
|
@@ -1,203 +1,276 @@
|
|
|
1
1
|
# dsh-advisor-group
|
|
2
2
|
|
|
3
|
-
>
|
|
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.
|
|
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**.
|
|
8
4
|
|
|
9
|
-
[
|
|
5
|
+
[中文](README.md) · [Installation](#compatibility-and-installation) · [Quick start](#quick-start) · [Core features](#core-features) · [Configuration](#configuration-reference) · [Development](#development)
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/dsh-advisor-group)
|
|
8
|
+
[](https://github.com/topics/dsh-plugin)
|
|
9
|
+
[](LICENSE)
|
|
12
10
|
|
|
13
|
-
|
|
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. |
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
## Compatibility and installation
|
|
16
18
|
|
|
17
|
-
|
|
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 |
|
|
18
24
|
|
|
19
|
-
|
|
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.
|
|
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.
|
|
30
26
|
|
|
31
|
-
|
|
27
|
+
### Install from npm
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|---|---|
|
|
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` |
|
|
36
|
-
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
37
|
-
| Platforms | DSH Web and the desktop app (same client bundle) + headless host logic |
|
|
29
|
+
For DSH Web:
|
|
38
30
|
|
|
39
|
-
|
|
31
|
+
```sh
|
|
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.
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
### Install from source (main)
|
|
42
40
|
|
|
43
41
|
```sh
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
46
47
|
|
|
47
|
-
#
|
|
48
|
-
|
|
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
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
|
|
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:
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
> dsh plugin --profile web add ./dsh-advisor-group-<version>.tgz # after npm pack (version from package.json)
|
|
58
|
-
> ```
|
|
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
|
+
```
|
|
59
67
|
|
|
60
|
-
|
|
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.
|
|
61
69
|
|
|
62
|
-
|
|
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" />
|
|
63
71
|
|
|
64
|
-
|
|
72
|
+
The settings page follows the setup path, with every section folded by default; each open section has its own save row.
|
|
65
73
|
|
|
66
|
-
|
|
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.
|
|
68
|
-
3. Just start a conversation:
|
|
69
|
-
- type `@顾问群` in your question to force a consultation, or
|
|
70
|
-
- ask a professional/uncertain question — the plugin escalates automatically when appropriate.
|
|
74
|
+
### Path 2: ask another session by role name
|
|
71
75
|
|
|
72
|
-
|
|
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:
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
```text
|
|
81
|
+
Ask 小明: what changed in the Advisor Corps role cards today?
|
|
82
|
+
```
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
Or ask several members at once:
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
```text
|
|
87
|
+
Ask 小明 and 阿龙: review this design for desktop and Web omissions respectively, then combine both conclusions.
|
|
88
|
+
```
|
|
79
89
|
|
|
80
|
-
|
|
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.
|
|
81
91
|
|
|
82
|
-
|
|
92
|
+
## Core features
|
|
83
93
|
|
|
84
|
-
|
|
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.**
|
|
94
|
+
### Multi-model consultation: deepen the discussion automatically
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
One `ask_advisors` call runs the whole consultation:
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
103
|
+
```
|
|
104
|
+
|
|
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" />
|
|
106
|
+
|
|
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.
|
|
114
|
+
|
|
115
|
+
### Advisor Corps: give each session a durable identity
|
|
116
|
+
|
|
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.
|
|
96
118
|
|
|
97
|
-
|
|
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" />
|
|
98
120
|
|
|
99
|
-
|
|
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.
|
|
100
125
|
|
|
101
|
-
|
|
126
|
+
### Cross-session collaboration: ask one session or a group
|
|
102
127
|
|
|
103
|
-
|
|
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.
|
|
104
134
|
|
|
105
|
-
|
|
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.
|
|
106
136
|
|
|
107
|
-
|
|
137
|
+
## Model tool reference
|
|
108
138
|
|
|
109
|
-
|
|
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`.
|
|
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.
|
|
114
140
|
|
|
115
|
-
|
|
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 |
|
|
116
149
|
|
|
117
|
-
##
|
|
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
|
|
118
168
|
|
|
119
169
|
| Key | Type | Default | Description |
|
|
120
|
-
|
|
121
|
-
| `enabled` | boolean | `true` | Enable
|
|
122
|
-
| `discussion.maxRounds` | number | `2` | Total rounds
|
|
123
|
-
| `discussion.maxAdvisorsPerCall` | number | `3` |
|
|
124
|
-
| `discussion.autoDeepen` | boolean | `true` |
|
|
125
|
-
| `discussion.driverModel` | object | – | Fallback
|
|
126
|
-
| `discussion.advisorTimeoutMs` | number | `600000` | Per-advisor
|
|
127
|
-
| `discussion.driverTimeoutMs` | number | `600000` |
|
|
128
|
-
| `discussion.advisorTools` | string | `'readonly'` |
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
|
143
|
-
|
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
##
|
|
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
|
|
193
259
|
|
|
194
260
|
```sh
|
|
195
261
|
npm install --legacy-peer-deps --no-audit --no-fund
|
|
196
262
|
npm run typecheck
|
|
197
|
-
npm test
|
|
198
|
-
npm run build
|
|
263
|
+
npm test
|
|
264
|
+
npm run build
|
|
265
|
+
npm pack --dry-run
|
|
199
266
|
```
|
|
200
267
|
|
|
201
|
-
|
|
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
|
|
202
275
|
|
|
203
276
|
[Apache License 2.0](LICENSE) © 2026 dsh-advisor-group contributors.
|