dsh-advisor 0.1.4 → 0.2.1

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.i18n.yaml CHANGED
@@ -3,5 +3,5 @@
3
3
  # editing either side, bring the other along and re-record with:
4
4
  # git hash-object README.md
5
5
  # git hash-object README.zh.md
6
- README.md: c0096d75da8f4c6e528b24a5bf3cdce6722f0358
7
- README.zh.md: 6b7c4425bb81764d4bc47a6dc417b7e352fc3769
6
+ README.md: ef98d163d84f2db4ee6a5345d467eee27644ff3b
7
+ README.zh.md: 228f1d220635cbf7aba0ef72dc32e293a6635eaa
package/README.md CHANGED
@@ -1,136 +1,62 @@
1
1
  # dsh-advisor
2
2
 
3
- English | [中文](README.zh.md)
3
+ [English](README.md) | [中文](README.zh.md)
4
4
 
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  ![node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933.svg)
7
7
  ![dsh](https://img.shields.io/badge/dsh-DeepSeek%20Harness%20compatible-4B32C3.svg)
8
8
  [![dshfind](https://dshfind.com/api/badge/omdsh-dev/dsh-advisor)](https://dshfind.com/plugins/omdsh-dev/dsh-advisor?ref=badge)
9
9
 
10
- A standalone dsh plugin bundle porting the omp "advisor"
11
- subsystem: a per-session reviewer model that observes the primary transcript,
12
- reviews each stepped turn with an explicitly configured model (provider +
13
- model are required), and injects severity-ranked advice (nit / concern /
14
- blocker) back into the session — without polluting or recursively reviewing
15
- itself.
10
+ A standalone dsh (DeepSeek Harness) plugin bundle porting the omp "advisor" subsystem: a per-session independent reviewer model that observes the primary transcript, reviews each stepped turn with an explicitly configured model (provider + model are required), and injects severity-ranked advice (nit / concern / blocker) back into the session — without polluting or recursively reviewing itself.
16
11
 
17
- Install with a single command:
12
+ **Advisory only.** The advisor never approves or rejects the primary agent's actions, and never issues commands as if it were the primary agent. Every delivered message is self-described advisory content, and a misbehaving reviewer is bounded end to end (emission guard, immuneTurns cooldown, failure policy) so it can never stall or pollute the primary loop.
18
13
 
19
- ```sh
20
- dsh plugin --profile web add dsh-advisor # <name> = your profile name
21
- ```
22
-
23
- **Advisory only.** The advisor never approves or rejects the primary agent's
24
- actions; it never issues commands as if it were the primary agent. Every
25
- delivered message is self-described advisory content, and a misbehaving
26
- reviewer is bounded end to end (emission guard, immuneTurns cooldown, failure
27
- policy) so it can never stall or pollute the primary loop.
14
+ Works in both dsh front ends: the **web** profile (Settings → 插件配置 → Advisor card) and the **dsh-tui** terminal profile (`/advisor` + `/advisor config`).
28
15
 
29
- ## Install
16
+ ## Quick start
30
17
 
31
- ### One-line registry install
18
+ ### Install
32
19
 
33
20
  ```sh
34
- dsh plugin --profile web add dsh-advisor # <name> = your profile name
21
+ dsh plugin --profile web add dsh-advisor # web profile (Settings Advisor card)
22
+ dsh plugin --profile dsh-tui add dsh-advisor # dsh-tui terminal profile
35
23
  ```
36
24
 
37
- A registry install fetches the published tarball, which ships the built
38
- artifacts (`lib/` + `cordis.patch.yml`), so no `prepare` build or build
39
- permission is needed. Runtime dependencies (`@deepseek-ai/cordis`, `@deepseek-ai/schemastery`,
40
- and the `@deepseek-ai/dsh-*` peers) are declared as peerDependencies and resolve
41
- through the dsh installation's flat profile module fallback — no extra install
42
- step. Pin an exact version (`dsh-advisor@0.1.0`) for reproducible installs.
25
+ Same plugin, either front end — the only difference is the `--profile` flag. Pin a version with `@<version>` (e.g. `dsh-advisor@0.1.0`). A registry install fetches the published tarball, which ships the built artifacts (`lib/` + `cordis.patch.yml`) — nothing builds on the target machine, and runtime dependencies (`@deepseek-ai/cordis`, `@deepseek-ai/schemastery`, `@deepseek-ai/dsh-*` peers) resolve through the dsh installation's flat profile module fallback — no extra install step. Registry / git / tarball / local-directory variants (local-dir from a built checkout: `dsh plugin --profile web add .` or `dsh plugin --profile dsh-tui add .`), web Settings exposure, uninstall, and `--dump-config` verification → [docs/install.md](docs/install.md).
43
26
 
44
- ### Local directory install (recommended for development / verification)
27
+ ### Configuration
45
28
 
46
- ```sh
47
- pnpm install # build the bundle (the prepare self-build)
48
- dsh plugin --profile web add . # <name> = your profile name
29
+ Add an `advisor:` section to the global dsh settings document (default `$DSH_HOME/settings.yaml` — shared across profiles; the web Settings card writes to this same file):
30
+
31
+ ```yaml
32
+ advisor:
33
+ enabled: true # master switch (default false) — set explicitly to enable
34
+ provider: deepseek-official # REQUIRED when enabled
35
+ model: deepseek-v4-flash # REQUIRED when enabled
36
+ systemPrompt: "" # optional; "" = built-in reviewer prompt
37
+ immuneTurns: 3 # int ≥ 0, default 3 — cooldown after a delivered steer
38
+ maxDeltaMessages: 60 # int ≥ 0, default 60 — delta window; 0 = unbounded
49
39
  ```
50
40
 
51
- ### Verify
41
+ The advisor is off by default. When enabled, `provider` and `model` are **mandatory**: `enabled: true` without both is a hard gate — the advisor never starts a model call and reports a disabled-with-reason status; unknown config keys are rejected.
52
42
 
53
- ```sh
54
- dsh --profile web --dump-config # shows a "# == dsh-advisor" layer with the advisor row
55
- dsh --profile web
56
- ```
43
+ The same keys compose across **three surfaces** (later layers override earlier ones; every surface shares the same key set and the same hard gate, with the host-side gate as the final line of defense on every path):
57
44
 
58
- Tarball install and uninstall are covered in [docs/install.md](docs/install.md).
45
+ 1. **Plugin-row config** the profile patch layer (`$DSH_HOME/profiles/<profile>/cordis.patch.yml`). This is the composition base.
46
+ 2. **dsh web Settings page — the "插件配置" (Plugin Configuration) page** — the Advisor **card** (id `advisor`) with the enabled toggle, provider / model selects restricted to system-configured providers and their models, and the optional fields. Saving writes into the `advisor` settings namespace and applies to new sessions immediately — no restart. The card requires a current dsh web build whose shell declares the `settings.plugin.item` card slot and loads packages that declare `dsh.client`; it reads and writes the namespace through the official `GatewayService` RPC channel (`/api/advisor/get` + `/api/advisor/set`), which is not gated by the settings exposure allowlist. It additionally blocks saving while enabled with a required field empty.
47
+ 3. **`/advisor` command** — per-session and ephemeral: it flips a session override, never the persisted config (see [Verify](#verify)).
59
48
 
60
- ## Config
49
+ In a **dsh-tui** profile the same five keys are editable in the TUI `/settings` screen: run `dsh --profile dsh-tui`, open `/settings`, and edit the **Advisor** section (`enabled` / `provider` / `model` / `immuneTurns` / `maxDeltaMessages`, each with zh/en label + hint). Edits are staged and written on save through the revision-fenced `settings.mutate` into the same `advisor` namespace user layer the web card writes, and re-apply live without a restart. `systemPrompt` is NOT a TUI field (the TUI text control is single-line; a multi-line prompt would be truncated) — edit it via the web card or `$DSH_HOME/settings.yaml`. The section requires dsh-tui ≥ v0.8.0 (shipped in the `dsh-tui-settings-sections` row of the v0.8.0+ bundle); older dsh-tui versions no-op it cleanly and the two file paths — profile patch layer + global `$DSH_HOME/settings.yaml` — remain the edit paths. `/advisor config` stays a read-only readback whose edit hint names the `/settings` screen when the seam is mounted. Save behavior differs from the web card: the TUI seam has no cross-field validation, so a save may set `enabled: true` with empty `provider`/`model` — the explicit model gate resolves that to disabled-with-reason at runtime (visible via `/advisor status` and `/advisor config`); the web card blocks such a save outright. Full reference → [docs/configuration.md](docs/configuration.md).
61
50
 
62
51
  ![Advisor card on the dsh web Settings (插件配置) page](docs/screenshots/advisor-settings-card.webp)
63
52
 
64
- The advisor is off by default. When enabled, `provider` and `model` are
65
- **mandatory**: `enabled: true` without both is a hard gate — the advisor never
66
- starts a model call and reports a disabled-with-reason status. Unknown config
67
- keys are rejected.
68
-
69
- Configuration composes across **three surfaces** (later layers override earlier
70
- ones; every surface uses the same key set):
71
-
72
- 1. **Plugin-row config** — `$DSH_HOME/profiles/web/cordis.patch.yml`
73
- (below). This is the composition base.
74
- 2. **dsh web Settings page — the "插件配置" (Plugin Configuration) page** —
75
- the Advisor **card** (id `advisor`, rendered after the upstream bash /
76
- agent-loop / web-search cards) with the enabled toggle, provider / model
77
- selects restricted to system-configured providers and their models, and the
78
- optional fields. Saving writes into the `advisor` settings namespace and
79
- overrides the plugin-row config without editing it. Saving applies to new
80
- sessions immediately — no restart (the runtime reads the composed value
81
- live). Requires a current dsh web build whose shell declares the
82
- `settings.plugin.item` card slot and loads packages that declare
83
- `dsh.client`. The card reads and writes the namespace through the
84
- **official `GatewayService` RPC channel** (`/api/advisor/get` +
85
- `/api/advisor/set`, claimed by the host's typertGateway — the same
86
- mechanism the dsh `goals` service uses), which is **not gated by the
87
- settings exposure allowlist**: the in-process write
88
- (`ctx.settings.update`) carries no exposed-namespace check. No host
89
- patching is applied or required.
90
- 3. **`/advisor` command** — per-session and ephemeral: it flips a session
91
- override, never the persisted config (see [Usage](#usage)).
92
-
93
- Both persisted surfaces share the same hard gate: `enabled: true` with empty
94
- `provider`/`model` never starts a model call (disabled-with-reason). The
95
- Settings page additionally blocks saving while enabled with a required field
96
- empty; the host-side gate stays the final line of defense on every path.
97
-
98
- Plugin-row config:
53
+ ### Verify
99
54
 
100
- ```yaml
101
- # profiles/web/cordis.patch.yml the profile's user patch layer
102
- - id: advisor
103
- config:
104
- enabled: true # master switch (default false)
105
- provider: deepseek-official # REQUIRED when enabled
106
- model: deepseek-v4-flash # REQUIRED when enabled
107
- systemPrompt: "" # optional; "" = built-in reviewer prompt
108
- immuneTurns: 3 # int ≥ 0, default 3 — cooldown after a delivered interrupt
109
- maxDeltaMessages: 60 # int ≥ 0, default 60 — delta window; 0 = unbounded
55
+ ```sh
56
+ dsh --profile web --dump-config # shows a "# == dsh-advisor" layer with the advisor row
110
57
  ```
111
58
 
112
- | Key | Type / default | Meaning |
113
- |---|---|---|
114
- | `enabled` | bool, `false` | Master switch. |
115
- | `provider` | string, optional | Provider route. Required (non-empty) when `enabled: true`. |
116
- | `model` | string, optional | Model id. Required (non-empty) when `enabled: true`. |
117
- | `systemPrompt` | string, `""` | Overrides the built-in reviewer prompt (severity definitions + JSON-frame output contract). |
118
- | `immuneTurns` | int ≥ 0, `3` | After a concern/blocker is actually steered, the next N stepped primary turns must complete before another interrupting note may steer; notes inside the window downgrade to inject. |
119
- | `maxDeltaMessages` | int ≥ 0, `60` | Bounded advisor input window. Deltas beyond N are truncated with a `… <earlier messages omitted>` marker; `0` = unbounded. |
120
-
121
- **Model capability & budget**: the advisor call runs with `reasoningEffort:
122
- 'off'` — sent only when the configured model's adapter declares that effort
123
- (deepseek models do; any other model gets the option omitted automatically, so
124
- non-reasoning providers keep working) — and a **5120-token** output cap (a
125
- user-directed 20× supersession of the original 256). Extracted notes are
126
- bounded (1000 chars) and the notice summary to 120 chars, so the raised budget
127
- cannot translate into an unbounded injection into the primary session.
128
-
129
- ## Usage
130
-
131
- Once installed and enabled, the advisor observes every session. Control it per
132
- session with the `/advisor` command (available when a command registry is
133
- composed):
59
+ With the advisor installed and enabled, control it in-session with the `/advisor` command (available when a command registry is composed):
134
60
 
135
61
  ```
136
62
  /advisor toggle the advisor for this session
@@ -139,146 +65,53 @@ composed):
139
65
  /advisor status show state, model, runtime status, pending count, last activity
140
66
  ```
141
67
 
142
- `/advisor on|off|toggle` are session-scoped and ephemeral: they flip a
143
- per-session override, never the persisted config. Enabling a session whose
144
- config lacks `provider`/`model` starts no model call — `/advisor status` (and
145
- the `/advisor on` reply) shows the gate reason.
146
-
147
- `/advisor on` is also the manual recovery path: a session advisor paused by a
148
- quota/rate-limit (`quota_exhausted` — KD-5 has no auto-resume timer) resumes in
149
- place, and a halted advisor (permanent model error, e.g. invalid credentials)
150
- is rebuilt fresh for the session.
68
+ `/advisor on|off|toggle` are session-scoped and ephemeral: they flip a per-session override, never the persisted config. Enabling a session whose config lacks `provider`/`model` starts no model call — `/advisor status` (and the `/advisor on` reply) shows the gate reason: the advisor runs only when enabled **with** both configured. `/advisor on` is also the manual recovery path: a session advisor paused by a quota/rate-limit (`quota_exhausted` — no auto-resume timer) resumes in place, and a halted advisor (permanent model error, e.g. invalid credentials) is rebuilt fresh for the session.
151
69
 
152
- The advisor reviews on a dual-mode trigger, depending on the session shape:
70
+ In a **dsh-tui** profile, `/advisor config` additionally reads back the composed configuration — read-only, with edit hints naming the real write paths: the TUI `/settings` screen (Advisor section, dsh-tui ≥ v0.8.0), the profile patch layer, and the shared `$DSH_HOME/settings.yaml` `advisor:` section. The `/advisor` / `on|off|status|config` commands are listed in the TUI `/` menu with subcommand completion (command discovery requires the `dsh-tui-command-trees` row — the shipped dsh-tui bundle has it).
153
71
 
154
- - **Standard stepped sessions** — after each stepped primary turn that ends
155
- normally (`completed`, `max-tokens`, or `error`), the advisor reviews the
156
- incremental transcript delta.
157
- - **Agentic / harness sessions** (never emit `turn/end`) — after each completed
158
- agent reply round: when a new human input arrives (inbox-spliced input
159
- included) after an unreviewed assistant increment, the advisor reviews that
160
- increment.
72
+ ## Features
161
73
 
162
- Either way the advisor emits at most one note per review, ranked by severity:
74
+ - **Independent reviewer per session**: a separate model call observes the primary transcript and reviews each stepped primary turn; advisor messages are excluded from later deltas, so the advisor never reads its own advice back.
75
+ - **Severity-ranked advice with inject/steer semantics**: at most one note per review — **nit** (a minor style, clarity, or quality suggestion; delivered via non-waking `agent.inject`, consumed at the next pre-step boundary), **concern** (a material risk or clearly better direction to weigh before continuing; delivered via waking `agent.steer`, subject to the `immuneTurns` cooldown), **blocker** (continuing clearly wastes work — contradicts an explicit user instruction, going in circles, fundamentally unsound; delivered via `agent.steer`). Delivered messages carry the `[advisor:{severity}]` prefix and are self-described advisory content:
163
76
 
164
- - **nit** — a minor style, clarity, or quality suggestion; delivered via
165
- `agent.inject` (non-waking, consumed at the next pre-step boundary).
166
- - **concern** — a material risk or clearly better direction to weigh before
167
- continuing; delivered via `agent.steer` (waking), subject to the
168
- `immuneTurns` cooldown.
169
- - **blocker** — continuing clearly wastes work (contradicts an explicit user
170
- instruction, going in circles, fundamentally unsound); delivered via
171
- `agent.steer`.
77
+ ```
78
+ [advisor:concern] extract the helper into a module and unit-test it
79
+ ```
172
80
 
173
- Injected advice appears in the session stream as a user-role message carrying
174
- the advisor source kind and self-describing content, e.g.:
175
-
176
- ```
177
- [advisor:concern] extract the helper into a module and unit-test it
178
- ```
179
-
180
- The `[advisor:{severity}]` prefix is the only cue the primary model gets about
181
- how to treat it — the primary system prompt never mentions advisories. Advisor
182
- messages are excluded from later advisor deltas, so the advisor never reads
183
- its own advice back.
81
+ - **Explicit model gate**: `enabled` defaults to off; `enabled: true` without `provider` + `model` never starts a model call — status reports disabled-with-reason. Unknown config keys are rejected.
82
+ - **Zero-tool minimal start**: the reviewer is an independent model call only — no advisor tools, nothing it can do to the session besides advisory messages.
83
+ - **No-stall failure policy**: a failing or quota-limited advisor only drops its own bounded backlog — it can never park or pollute the primary loop.
84
+ - **Session-scoped controls**: `/advisor on|off|status|config` work per session; the toggles are ephemeral overrides, never persisted config.
184
85
 
185
86
  ![Advisor note injected into the session stream](docs/screenshots/advisor-injected-note.webp)
186
87
 
187
- ## How it works
188
-
189
- The plugin subscribes to `session/event`. Two triggers render an incremental
190
- markdown delta of the primary transcript (own advisor messages excluded) and
191
- queue it on a per-session runtime: after each stepped `turn/end` in standard
192
- stepped sessions, and — in agentic/harness sessions that never emit `turn/end`
193
- — when a new human input arrives (inbox-spliced input included) after an
194
- unreviewed assistant increment, i.e. at each completed agent reply round. The
195
- runtime calls a separately configured model via `ctx.llm.stream`, extracts one
196
- `{note, severity}` from the JSON-framed reply, gates it through an emission
197
- guard (normalize / dedupe / content-free suppression / one-note-per-update),
198
- and routes it: nit → inject, concern/blocker → steer. The advisor call runs
199
- with reasoning off and a 20x token budget so the JSON note is never starved by
200
- reasoning output. Compaction and surface rewrites reset the observer, the
201
- emission guard, and the immuneTurns latch
202
- (KD-5); the drain is fully async with a bounded backlog, so a failing or
203
- quota'd advisor can only drop its own backlog — never park the primary loop.
88
+ ## Mount-only (no dsh modification)
204
89
 
205
- ## Limitations & roadmap
90
+ The plugin installs as a **pure mount**: bundle insert + client card (web Settings 插件配置) + its own gateway channel (`/api/advisor/get|set`, claimed by the host's typertGateway — the same mechanism the dsh `goals` service uses, not gated by the settings exposure allowlist) + the `/advisor` commands — no dsh patches, no postinstall step, and dsh upgrades never require re-patching.
206
91
 
207
- The MVP deliberately drops full omp parity. Accepted gaps (tracked in the
208
- harness iteration roadmap):
209
-
210
- - **Single advisor per session** — no parallel advisor roster or WATCHDOG-style
211
- file discovery (next iteration).
212
- - **No advisor tools** — the reviewer is an independent model call only; it
213
- cannot verify claims itself (next-next iteration).
214
- - **No in-session advisor panel** — advice surfaces only as tagged injected
215
- messages (the Advisor card on the "插件配置" settings page is a config
216
- surface, not a session view; an in-session card is next-next iteration).
217
- - **No transcript persistence or cost stats** — no resumable advisor history or
218
- cost observability (next-next iteration).
219
- - **No secret obfuscation of delta content** — secrets present in the transcript
220
- can reach the advisor model; mitigate by configuring a trusted reviewer model.
221
- - **No quarantine of unsafe advisor output** — a misbehaving note can carry
222
- directive text; the JSON frame + validation + advisory-only framing
223
- (`[advisor:…]`, "weigh, don't blindly obey") are the only mitigation, and the
224
- note is delivered as-is into the primary transcript (roadmap).
225
- - **No `syncBacklog` catch-up wait** — a far-behind advisor does not wait for
226
- the primary loop; its backlog is bounded and dropped (never parks the
227
- primary), so advisor notes may arrive after the next primary turn started
228
- (roadmap: context-maintenance batch).
229
- - **Bounded advisor context** — long-session full replays are truncated
230
- (`maxDeltaMessages`), so the advisor may lose early context after compaction;
231
- advisor context maintenance is roadmap (next-next iteration).
232
-
233
- ## Development
234
-
235
- The bundle builds itself on install: `package.json` declares `"prepare":
236
- "pnpm build"` (the same build `prepack` runs), so any clone is immediately
237
- buildable. The private `@deepseek-ai/dsh-*` runtime dependencies are
238
- **peerDependencies only** (never `dependencies` / `devDependencies`);
239
- `pnpm-workspace.yaml` sets `autoInstallPeers: true` + `nodeLinker: hoisted`
240
- (pnpm 11+ ignores non-auth settings in `.npmrc`), so at dev time pnpm
241
- resolves the real `@deepseek-ai/*` packages from the npm registry using the
242
- auth token in your user-level `~/.npmrc`. There is no local link-farm and no
243
- `DSH_HOME` / `DSH_SOURCE_DIR` prerequisite for dependency resolution.
92
+ ## Limitations & roadmap
244
93
 
245
- ```sh
246
- pnpm install # registry deps incl. the @deepseek-ai/* peers (via autoInstallPeers + ~/.npmrc auth)
247
- pnpm test # vitest (unit + the composed integration loop)
248
- pnpm typecheck # tsc --noEmit (node) + tsc -p tsconfig.client.json --noEmit + tsc -p tsconfig.spec.json --noEmit
249
- pnpm build # tsc -p tsconfig.build.json emit to lib/ + node scripts/build-client.mjs (client bundle)
250
- pnpm pack # build + produce dsh-advisor-0.0.1.tgz
251
- ```
94
+ The MVP deliberately drops full omp parity. Accepted gaps (tracked in the harness iteration roadmap):
252
95
 
253
- The in-box `cordis` framework is declared as the scoped peer
254
- `@deepseek-ai/cordis` (never bare `cordis`) — the declared pin is
255
- `"@deepseek-ai/cordis": "^4.0.1"` (`package.json` peerDependencies). Peer
256
- ranges against prerelease publishes must carry the exact publish tag e.g.
257
- the `@deepseek-ai/dsh-*` peers are pinned `^0.1.0-rc.6`; per the node-semver
258
- prerelease-tuple rule a comparator with a prerelease only matches the same
259
- `[major, minor, patch]` tuple, so a range like `^4.0.0-rc.7` never matches a
260
- `4.0.1-rc.1` publish.
261
- The scoped peer resolves from the npm registry like the other
262
- `@deepseek-ai/*` peers, so dev-time `import '@deepseek-ai/cordis'` and the
263
- host see the same package identity.
264
-
265
- `prepack` runs `pnpm build`; `prepare` runs `pnpm build`, so `pnpm pack` runs
266
- the build twice (once per lifecycle) — the documented tradeoff that keeps
267
- git-install builds working. There is no `postinstall` step: already-built
268
- tarball installs skip the build entirely. A local `dsh plugin add .` mounts
269
- the bundle from the working tree, so run `pnpm build` (or `pnpm install`)
270
- first — pnpm does not run `prepare` for `link:` dependencies.
271
-
272
- The integration test (`tests/integration.test.ts`) composes the plugin into a
273
- real cordis context with a stub LLM adapter and drives the full
274
- turn → delta → advisor call → inject/steer cycle.
96
+ - **Single advisor per session** no parallel advisor roster or WATCHDOG-style file discovery (next iteration).
97
+ - **No advisor tools** — the reviewer is an independent model call only; it cannot verify claims itself (next-next iteration).
98
+ - **No in-session advisor panel** — advice surfaces only as tagged injected messages; the web Advisor card is a config surface, not a session view (next-next iteration).
99
+ - **No transcript persistence or cost stats** no resumable advisor history or cost observability (next-next iteration).
100
+ - **No secret obfuscation of delta content** — secrets present in the transcript can reach the advisor model; mitigate by configuring a trusted reviewer model.
101
+ - **No quarantine of unsafe advisor output** — a misbehaving note can carry directive text; the JSON frame + validation + advisory-only framing are the only mitigation, and the note is delivered as-is (roadmap).
102
+ - **No `syncBacklog` catch-up wait** — a far-behind advisor does not wait for the primary loop; its backlog is bounded and dropped, so notes may arrive after the next primary turn started (roadmap: context-maintenance batch).
103
+ - **Bounded advisor context** — long-session full replays are truncated (`maxDeltaMessages`), so the advisor may lose early context after compaction (roadmap: next-next iteration).
275
104
 
276
105
  ## Documentation
277
106
 
278
107
  | Doc | Content |
279
108
  |---|---|
280
- | [docs/install.md](docs/install.md) | full install guide: git / tarball / local-directory install, web Settings exposure, uninstall, `--dump-config` verification |
109
+ | [docs/install.md](docs/install.md) | profile install (web + dsh-tui) / registry / git / tarball / local-directory variants / web Settings exposure / uninstall / `--dump-config` verification |
110
+ | [docs/configuration.md](docs/configuration.md) | full `advisor` namespace reference: keys & defaults, explicit model gate (S4), settings surfaces (web card / patch layer / global settings.yaml), example YAML, live re-apply behavior |
111
+ | [docs/consumer-api.md](docs/consumer-api.md) | developer consumption contract: package-root library API, `dsh-advisor/client` entry, `/advisor` command surface, export inventory, lifecycle |
112
+ | [docs/verification.md](docs/verification.md) | verification records: test matrix (16 files / 319 cases), typecheck/build, CI contract, real-environment steps |
113
+ | [docs/release.md](docs/release.md) | release process: PR-driven Release prep + Release workflows, OIDC trusted publishing, version strategy, rollback |
281
114
 
282
115
  ## License
283
116
 
284
- MIT
117
+ Released under the **MIT** License — see [LICENSE](LICENSE). The LICENSE file is authoritative for copyright and license terms.
package/README.zh.md CHANGED
@@ -1,90 +1,62 @@
1
1
  # dsh-advisor
2
2
 
3
- [English](README.md) | 中文
3
+ [English](README.md) | [中文](README.zh.md)
4
4
 
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  ![node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933.svg)
7
7
  ![dsh](https://img.shields.io/badge/dsh-DeepSeek%20Harness%20compatible-4B32C3.svg)
8
8
  [![dshfind](https://dshfind.com/api/badge/omdsh-dev/dsh-advisor?lang=zh)](https://dshfind.com/zh/plugins/omdsh-dev/dsh-advisor?ref=badge)
9
9
 
10
- 一个移植 omp「advisor」子系统的独立 dsh 插件组合包:一个按会话运行的评审模型,观察主会话 transcript,用显式配置的模型(provider 与 model 均为必填)评审每个已完成的 stepped turn,并把按严重度排序的建议(nit / concern / blocker)注入回会话 —— 不污染主循环,也不递归地评审自己。
11
-
12
- 一条命令即可安装:
13
-
14
- ```sh
15
- dsh plugin --profile web add dsh-advisor # <name> = 你的 profile 名
16
- ```
10
+ 一个移植 omp「advisor」子系统的独立 dsh(DeepSeek Harness)插件组合包:一个按会话运行的独立评审模型,观察主会话 transcript,用显式配置的模型(provider 与 model 均为必填)评审每个已完成的 stepped turn,并把按严重度排序的建议(nit / concern / blocker)注入回会话——不污染主循环,也不递归地评审自己。
17
11
 
18
12
  **仅作建议。** advisor 从不批准或否决主 agent 的动作,也绝不会像主 agent 那样发出命令。每条送达的消息都是自我描述的 advisory 内容;一个行为异常的评审者会被端到端约束(emission guard、immuneTurns 冷却、failure policy),因此它永远不会卡住或污染主循环。
19
13
 
20
- ## 安装
14
+ 两个 dsh 前端均可用:**web** profile(设置 → 插件配置 → Advisor 卡片)与 **dsh-tui** 终端 profile(`/advisor` + `/advisor config`)。
21
15
 
22
- ### 一条命令的 registry 安装
16
+ ## 快速开始
23
17
 
24
- ```sh
25
- dsh plugin --profile web add dsh-advisor # <name> = 你的 profile 名
26
- ```
27
-
28
- registry 安装拉取的是已发布的 tarball,其中自带构建产物(`lib/` + `cordis.patch.yml`),因此不会运行 `prepare` 构建,也无需构建放行。运行时依赖(`@deepseek-ai/cordis`、`@deepseek-ai/schemastery` 与 `@deepseek-ai/dsh-*` peers)声明为 peerDependencies,由 dsh 安装的扁平 profile module fallback 解析——无需额外安装步骤。需要可复现安装时用 `dsh-advisor@0.1.0` 钉住精确版本。
29
-
30
- ### 本地目录安装(推荐用于开发 / 验证)
18
+ ### 安装
31
19
 
32
20
  ```sh
33
- pnpm install # 构建组合包(prepare 自建)
34
- dsh plugin --profile web add . # <name> = 你的 profile
21
+ dsh plugin --profile web add dsh-advisor # web profile(设置 → Advisor 卡片)
22
+ dsh plugin --profile dsh-tui add dsh-advisor # dsh-tui 终端 profile
35
23
  ```
36
24
 
37
- ### 验证
25
+ 同一个插件、两个前端——区别只在 `--profile` 参数。钉版本:加 `@<version>`(如 `dsh-advisor@0.1.0`)。registry 安装拉取的是已发布的 tarball,自带构建产物(`lib/` + `cordis.patch.yml`)——目标机无需构建;运行时依赖(`@deepseek-ai/cordis`、`@deepseek-ai/schemastery` 与 `@deepseek-ai/dsh-*` peers)经 dsh 安装的扁平 profile module fallback 解析——无需额外安装步骤。registry / git / tarball / 本地目录变体(本地目录从已构建 checkout 安装:`dsh plugin --profile web add .` 或 `dsh plugin --profile dsh-tui add .`)、web Settings 暴露、卸载与 `--dump-config` 验证 → [docs/install.zh.md](docs/install.zh.md)。
38
26
 
39
- ```sh
40
- dsh --profile web --dump-config # 显示带 advisor 配置行的 "# == dsh-advisor" 层
41
- dsh --profile web
42
- ```
27
+ ### 配置
43
28
 
44
- tarball 安装与卸载见 [docs/install.zh.md](docs/install.zh.md)。
29
+ 在全局 dsh 设置文档(默认 `$DSH_HOME/settings.yaml`——跨 profile 共享;web Settings 卡片也写入这个文件)中添加 `advisor:` 分节:
45
30
 
46
- ## 配置
31
+ ```yaml
32
+ advisor:
33
+ enabled: true # 总开关(默认 false)——需显式打开后生效
34
+ provider: deepseek-official # enabled: true 时必填(非空)
35
+ model: deepseek-v4-flash # enabled: true 时必填(非空)
36
+ systemPrompt: "" # 可选;"" = 内置评审 prompt
37
+ immuneTurns: 3 # 整数 ≥ 0,默认 3 —— 打断性送达后的冷却步数
38
+ maxDeltaMessages: 60 # 整数 ≥ 0,默认 60 —— delta 窗口;0 = 无上限
39
+ ```
47
40
 
48
- ![dsh web Settings("插件配置")页上的 Advisor 卡片](docs/screenshots/advisor-settings-card.webp)
41
+ advisor 默认关闭。启用后,`provider` `model` 为**必填**:`enabled: true` 而缺少两者之一是一个硬门禁——advisor 不会发起任何模型调用,并报告带原因的禁用状态(disabled-with-reason);未知配置键会被拒绝。
49
42
 
50
- advisor 默认关闭。启用后,`provider` 与 `model` 为**必填**:`enabled: true` 而缺少两者之一是一个硬门禁 —— advisor 不会发起任何模型调用,并报告带原因的禁用状态(disabled-with-reason)。未知配置键会被拒绝。
43
+ 同一组键在**三个配置面**之间合成(后一层覆盖前一层;各处使用同一组键与同一个硬门禁,宿主侧门禁始终是所有路径上的最后防线):
51
44
 
52
- 配置在**三个配置面**之间合成(后一层覆盖前一层;各处使用同一组键):
45
+ 1. **插件行 config** —— profile 补丁层(`$DSH_HOME/profiles/<profile>/cordis.patch.yml`)。这是合成 base。
46
+ 2. **dsh web Settings 页 —— "插件配置"页** —— Advisor **卡片**(id `advisor`),含 enabled 开关、只列出系统内已配置 provider 及其模型的 provider/model 选择框与可选字段。保存写入 `advisor` settings namespace,新会话立即生效,无需重启。卡片要求当前版本的 dsh web 构建(其 web shell 声明了 `settings.plugin.item` 卡片 slot 并能加载 `dsh.client` 声明包);它通过官方 `GatewayService` RPC 通道读写该命名空间(`/api/advisor/get` + `/api/advisor/set`),不受 settings 暴露白名单门控。卡片还会在 enabled 且必填字段为空时阻止保存。
47
+ 3. **`/advisor` 指令** —— 按会话且临时:翻转的是会话级 override,从不修改持久化配置(见[验证](#验证))。
53
48
 
54
- 1. **插件行 config** —— `$DSH_HOME/profiles/web/cordis.patch.yml`(见下)。这是合成 base
55
- 2. **dsh web Settings 页 —— "插件配置"页** —— Advisor **卡片**(id `advisor`,渲染在三张上游卡片 bash / agent-loop / web-search 之后),含 enabled 开关、只列出系统内已配置 provider 及其模型的 provider/model 选择框与可选字段。保存写入 `advisor` settings namespace,覆盖插件行 config 而无需改动它。保存后新会话立即生效,无需重启(运行时 live 读取合成值)。需要当前版本的 dsh web 构建(其 web shell 声明了 `settings.plugin.item` 卡片 slot 并能加载 `dsh.client` 声明包)。卡片通过**官方 `GatewayService` RPC 通道**读写该命名空间(`/api/advisor/get` + `/api/advisor/set`,由宿主的 typertGateway claims——与 dsh 内建 `goals` 服务同一机制),该通道**不受 settings 暴露白名单门控**:进程内写入(`ctx.settings.update`)没有 exposed-namespace 检查。无需也不施加任何宿主补丁。
56
- 3. **`/advisor` 指令** —— 按会话且临时:翻转的是会话级 override,从不修改持久化配置(见[用法](#用法))。
49
+ **dsh-tui** profile 中,同样的五个键可在 TUI `/settings` 屏幕编辑:运行 `dsh --profile dsh-tui`、打开 `/settings`,编辑 **Advisor** 分节(`enabled` / `provider` / `model` / `immuneTurns` / `maxDeltaMessages`,每项均带中英文标签与提示)。编辑先暂存,保存时经 revision 栅栏保护的 `settings.mutate` 写入 web 卡片所写的同一个 `advisor` 命名空间 user layer,并 live 重应用、无需重启。`systemPrompt` **不是** TUI 字段(TUI text 控件为单行;多行 prompt 会被截断)——请经 web 卡片或 `$DSH_HOME/settings.yaml` 编辑。该分节要求 dsh-tui ≥ v0.8.0(随 v0.8.0+ 组合包的 `dsh-tui-settings-sections` 行提供);旧版 dsh-tui 会干净地 no-op,仍以两个文件路径——profile 补丁层 + 全局 `$DSH_HOME/settings.yaml`——作为编辑路径。`/advisor config` 仍是只读回读,seam 挂载时其编辑提示指向 `/settings` 屏幕。保存行为与 web 卡片不同:TUI seam 没有跨字段校验,一次保存可能把 `enabled: true` 与空 `provider`/`model` 一起写入——显式模型门禁会在运行时把它解析为 disabled-with-reason(可见于 `/advisor status` 与 `/advisor config`);web 卡片则会直接阻止这样的保存。完整参考 → [docs/configuration.md](docs/configuration.md)
57
50
 
58
- 两个持久化配置面共享同一个硬门禁:`enabled: true` `provider`/`model` 为空时绝不发起模型调用(disabled-with-reason)。Settings 页还会在 enabled 且必填字段为空时阻止保存;宿主侧硬门禁始终是所有路径上的最后防线。
51
+ ![dsh web Settings("插件配置")页上的 Advisor 卡片](docs/screenshots/advisor-settings-card.webp)
59
52
 
60
- 插件行配置:
53
+ ### 验证
61
54
 
62
- ```yaml
63
- # profiles/web/cordis.patch.yml the profile's user patch layer
64
- - id: advisor
65
- config:
66
- enabled: true # master switch (default false)
67
- provider: deepseek-official # REQUIRED when enabled
68
- model: deepseek-v4-flash # REQUIRED when enabled
69
- systemPrompt: "" # optional; "" = built-in reviewer prompt
70
- immuneTurns: 3 # int ≥ 0, default 3 — cooldown after a delivered interrupt
71
- maxDeltaMessages: 60 # int ≥ 0, default 60 — delta window; 0 = unbounded
55
+ ```sh
56
+ dsh --profile web --dump-config # 显示带 advisor 配置行的 "# == dsh-advisor" 层
72
57
  ```
73
58
 
74
- | | 类型 / 默认值 | 含义 |
75
- |---|---|---|
76
- | `enabled` | bool, `false` | 总开关。 |
77
- | `provider` | string, optional | 供应商路由。`enabled: true` 时必须(非空)。 |
78
- | `model` | string, optional | 模型 id。`enabled: true` 时必须(非空)。 |
79
- | `systemPrompt` | string, `""` | 覆盖内置评审 prompt(严重度定义 + JSON-frame 输出契约)。 |
80
- | `immuneTurns` | int ≥ 0, `3` | 实际 steer 过一次 concern/blocker 后,接下来 N 个完成的 stepped 主 turn 必须走完,另一条打断性 note 才可再次 steer;窗口内的 note 降级为 inject。 |
81
- | `maxDeltaMessages` | int ≥ 0, `60` | 有界的 advisor 输入窗口。超过 N 的 delta 以 `… <earlier messages omitted>` 标记截断;`0` = 无上限。 |
82
-
83
- **模型能力与预算**:advisor 调用以 `reasoningEffort: 'off'` 运行 —— 仅当所配置模型的 adapter 声明该档位时才发送(deepseek 模型声明;其他模型会自动省略该选项,因此非推理供应商照常工作)—— 并以 **5120 tokens** 作为输出上限(用户指示的 256 → 5120 的 20 倍超驰)。抽取出的 note 有界(1000 字符),notice summary 有界(120 字符),因此提高的预算不会变成注入主会话的无界内容。
84
-
85
- ## 用法
86
-
87
- 安装并启用后,advisor 观察每个会话。用 `/advisor` 指令按会话控制它(组合了 command registry 时可用):
59
+ 安装并启用后,在会话内用 `/advisor` 指令控制它(组合了 command registry 时可用):
88
60
 
89
61
  ```
90
62
  /advisor toggle the advisor for this session
@@ -93,72 +65,53 @@ advisor 默认关闭。启用后,`provider` 与 `model` 为**必填**:`enabl
93
65
  /advisor status show state, model, runtime status, pending count, last activity
94
66
  ```
95
67
 
96
- `/advisor on|off|toggle` 是会话级且临时的:它们翻转的是按会话的 override,从不修改持久化配置。启用一个 config 缺少 `provider`/`model` 的会话不会发起模型调用 —— `/advisor status`(以及 `/advisor on` 的回复)会显示门禁原因。
68
+ `/advisor on|off|toggle` 是会话级且临时的:它们翻转的是按会话的 override,从不修改持久化配置。启用一个 config 缺少 `provider`/`model` 的会话不会发起模型调用——`/advisor status`(以及 `/advisor on` 的回复)会显示门禁原因:advisor 只有在启用**且**两者均已配置时才运行。`/advisor on` 也是手动恢复路径:被 quota/rate-limit 暂停的会话 advisor(`quota_exhausted`——无自动恢复定时器)会在原地恢复;被终止的 advisor(永久性模型错误,如凭据无效)会为该会话全新重建。
97
69
 
98
- `/advisor on` 也是手动恢复路径:被 quota/rate-limit 暂停的会话 advisor(`quota_exhausted` —— KD-5 没有自动恢复定时器)会在原地恢复;被终止的 advisor(永久性模型错误,如凭据无效)会为该会话全新重建。
70
+ 在 **dsh-tui** profile 中,`/advisor config` 额外回读组合配置——只读,编辑提示指向真实的写路径:TUI `/settings` 屏幕(Advisor 分节,dsh-tui v0.8.0)、profile 补丁层与共享的 `$DSH_HOME/settings.yaml` 的 `advisor:` 分节。`/advisor` / `on|off|status|config` 指令出现在 TUI 的 `/` 菜单中并带子命令补全(指令发现要求 `dsh-tui-command-trees` 行——随附的 dsh-tui 组合包自带)。
99
71
 
100
- advisor 采用双模式触发,取决于会话形态:
72
+ ## 能力一览
101
73
 
102
- - **标准 stepped 会话** —— 在每个正常结束(`completed`、`max-tokens` 或 `error`)的 stepped 主 turn 之后,评审增量 transcript delta。
103
- - **agentic / harness 会话**(从不发出 `turn/end`)—— 在每个完成的 agent 回复轮次之后:当新的用户输入(含 inbox 拼接输入)在未评审的 assistant 增量之后到达时,评审该增量。
74
+ - **每个会话一个独立评审者**:独立的模型调用观察主 transcript 并评审每个 stepped 主 turn;advisor 消息被排除在此后的 delta 之外,因此 advisor 永远不会读回自己的建议。
75
+ - **按严重度排序的建议 + inject/steer 语义**:每次评审至多发出一条 note——**nit**(轻微的样式、清晰度或质量建议;经非唤醒的 `agent.inject` 送达,在下一个 pre-step 边界消费)、**concern**(继续之前值得权衡的重大风险或明显更优的方向;经唤醒的 `agent.steer` 送达,受 `immuneTurns` 冷却约束)、**blocker**(继续下去明显是在浪费工作——与显式用户指令矛盾、原地打转、根本性不可行;经 `agent.steer` 送达)。送达的消息携带 `[advisor:{severity}]` 前缀且为自我描述的 advisory 内容:
104
76
 
105
- 无论哪种模式,每次评审至多发出一条 note,按严重度排序:
106
-
107
- - **nit** —— 轻微的样式、清晰度或质量建议;通过 `agent.inject` 送达(非唤醒,在下一个 pre-step 边界消费)。
108
- - **concern** —— 在继续之前值得权衡的重大风险或明显更优的方向;通过 `agent.steer` 送达(唤醒),受 `immuneTurns` 冷却约束。
109
- - **blocker** —— 继续下去明显是在浪费工作(与显式用户指令矛盾、原地打转、根本性不可行);通过 `agent.steer` 送达。
110
-
111
- 注入的建议以 user-role 消息出现在会话流中,携带 advisor source kind 与自我描述的内容,例如:
112
-
113
- ```
114
- [advisor:concern] extract the helper into a module and unit-test it
115
- ```
77
+ ```
78
+ [advisor:concern] extract the helper into a module and unit-test it
79
+ ```
116
80
 
117
- `[advisor:{severity}]` 前缀是主模型获得的关于如何对待它的唯一线索 —— system prompt 从不提及 advisory。advisor 消息会被排除在此后的 advisor delta 之外,因此 advisor 永远不会读回自己的建议。
81
+ - **显式模型门禁**:`enabled` 默认关闭;`enabled: true` 而缺少 `provider` + `model` 时绝不发起模型调用——状态报告 disabled-with-reason。未知配置键会被拒绝。
82
+ - **零工具的最小启动**:评审者只是一个独立的模型调用——无 advisor tools,除了 advisory 消息之外它无法对会话做任何事。
83
+ - **不卡主循环的失败策略**:失败或 quota 耗尽的 advisor 只会丢弃自己有界的 backlog——永远不会卡住或污染主循环。
84
+ - **会话级控制**:`/advisor on|off|status|config` 按会话工作;开关是临时的 override,从不修改持久化配置。
118
85
 
119
86
  ![注入到会话流中的 advisor 建议](docs/screenshots/advisor-injected-note.webp)
120
87
 
121
- ## 工作原理
88
+ ## 纯挂载(零 dsh 修改)
122
89
 
123
- 插件订阅 `session/event`。两种触发方式会把主 transcript 的增量 markdown delta(排除 advisor 自己的消息)渲染出来并放入按会话的 runtime 队列:标准 stepped 会话在每个 stepped `turn/end` 之后;agentic/harness 会话(从不发出 `turn/end`)则在新的用户输入(含 inbox 拼接输入)于未评审的 assistant 增量之后到达时 —— 即每个完成的 agent 回复轮次。runtime 通过 `ctx.llm.stream` 调用一个单独配置的模型,从 JSON-framed 回复中提取一条 `{note, severity}`,经过 emission guard 门禁(normalize / dedupe / content-free 抑制 / 每次更新至多一条 note),然后路由:nit → inject,concern/blocker → steer。advisor 调用以关闭推理(reasoning off)和 20 倍 token 预算运行,因此 JSON note 绝不会被推理输出挤占。compaction 与 surface 重写会重置 observer、emission guard 与 immuneTurns latch(KD-5);drain 完全异步且 backlog 有界,因此失败或 quota 耗尽的 advisor 只能丢弃自己的 backlog —— 永远不会卡住主循环。
90
+ 插件以**纯挂载**方式安装:bundle 插入 + 客户端卡片(web Settings "插件配置")+ 自有 gateway 通道(`/api/advisor/get|set`,由宿主 typertGateway 认领——与 dsh 内建 `goals` 服务同一机制,不受 settings 暴露白名单门控)+ `/advisor` 指令——无 dsh 补丁、无 postinstall 步骤,dsh 升级永不需重打。
124
91
 
125
92
  ## 限制与路线图
126
93
 
127
94
  MVP 有意放弃与 omp 的完整对等。已接受的差距(在 harness 迭代路线图中跟踪):
128
95
 
129
- - **每个会话一个 advisor** —— 无并行 advisor roster 或 WATCHDOG 式文件发现(下一迭代)。
130
- - **无 advisor tools** —— 评审者只是一个独立的模型调用;它无法自行核验主张(下下迭代)。
131
- - **无会话内 advisor 面板** —— 建议仅以带标签的注入消息呈现("插件配置"设置页上的 Advisor 卡片是配置面,不是会话内视图;会话内卡片为下下迭代)。
132
- - **无 transcript 持久化或成本统计** —— 无可恢复的 advisor 历史或成本可观测性(下下迭代)。
133
- - **无 delta 内容密钥混淆** —— transcript 中出现的 secrets 可能到达 advisor 模型;请通过配置可信的评审模型来缓解。
134
- - **不隔离不安全的 advisor 输出** —— 行为异常的 note 可能携带指令性文本;JSON frame + 校验 + advisory-only 框架(`[advisor:…]`、"weigh, don't blindly obey")是仅有的缓解手段,且 note 会原样送达主 transcript(路线图)。
135
- - **无 `syncBacklog` 追赶等待** —— 落后很多的 advisor 不会等待主循环;其 backlog 有界且会被丢弃(永远不会卡住主循环),因此 advisor note 可能在下一次主 turn 开始之后才到达(路线图:context-maintenance batch)。
136
- - **advisor 上下文有界** —— 长会话的完整重放会被截断(`maxDeltaMessages`),因此 compaction 后 advisor 可能丢失早期上下文;advisor 上下文维护在路线图中(下下迭代)。
137
-
138
- ## 开发
139
-
140
- 组合包在安装时自行构建:`package.json` 声明了 `"prepare": "pnpm build"`(与 `prepack` 相同的构建),因此任何克隆都立即可构建。私有的 `@deepseek-ai/dsh-*` 运行时依赖**只声明为 peerDependencies**(绝不进 `dependencies` / `devDependencies`);`pnpm-workspace.yaml` 设了 `autoInstallPeers: true` + `nodeLinker: hoisted`(pnpm 11+ 忽略 `.npmrc` 中的非认证设置),因此开发期 pnpm 用你用户级 `~/.npmrc` 里的认证令牌从 npm registry 解析真实的 `@deepseek-ai/*` 包。没有本地链接农场,依赖解析也不需要 `DSH_HOME` / `DSH_SOURCE_DIR` 前置条件。
141
-
142
- ```sh
143
- pnpm install # registry deps,含 @deepseek-ai/* peers(经 autoInstallPeers + ~/.npmrc 认证)
144
- pnpm test # vitest (unit + the composed integration loop)
145
- pnpm typecheck # tsc --noEmit (node) + tsc -p tsconfig.client.json --noEmit + tsc -p tsconfig.spec.json --noEmit
146
- pnpm build # tsc -p tsconfig.build.json emit to lib/ + node scripts/build-client.mjs (client bundle)
147
- pnpm pack # build + produce dsh-advisor-0.0.1.tgz
148
- ```
149
-
150
- 内置 `cordis` 框架声明为 scoped peer `@deepseek-ai/cordis`(绝不用裸名 `cordis`)。针对 prerelease 发布的 peer 范围必须带精确的发布 tag —— 例如 `@deepseek-ai/dsh-*` peers 钉在 `^0.1.0-rc.6`;按 node-semver prerelease-tuple 规则,带 prerelease 的 comparator 只匹配同 `[major, minor, patch]` tuple,因此 `^4.0.0-rc.7` 这样的范围永远匹配不到 `4.0.1-rc.1` 的发布。scoped peer 与其他 `@deepseek-ai/*` peers 一样从 npm registry 解析,所以开发期的 `import '@deepseek-ai/cordis'` 与宿主看到的是同一个包身份。
151
-
152
- `prepack` 运行 `pnpm build`;`prepare` 运行 `pnpm build`,因此 `pnpm pack` 会构建两次(每个生命周期一次)——这是为保持 git 安装可构建而接受的取舍。没有 `postinstall` 步骤:tarball 安装已带构建产物,完全跳过构建。本地 `dsh plugin add .` 从工作树挂载 bundle,因此请先运行 `pnpm build`(或 `pnpm install`)——pnpm 不会为 `link:` 依赖运行 `prepare`。
153
-
154
- 集成测试(`tests/integration.test.ts`)把插件组合进一个带 stub LLM adapter 的真实 cordis 上下文,驱动完整的 turn → delta → advisor call → inject/steer 循环。
96
+ - **每个会话一个 advisor**——无并行 advisor roster 或 WATCHDOG 式文件发现(下一迭代)。
97
+ - **无 advisor tools**——评审者只是一个独立的模型调用;它无法自行核验主张(下下迭代)。
98
+ - **无会话内 advisor 面板**——建议仅以带标签的注入消息呈现;web Advisor 卡片是配置面,不是会话内视图(下下迭代)。
99
+ - **无 transcript 持久化或成本统计**——无可恢复的 advisor 历史或成本可观测性(下下迭代)。
100
+ - **无 delta 内容密钥混淆**——transcript 中出现的 secrets 可能到达 advisor 模型;请通过配置可信的评审模型来缓解。
101
+ - **不隔离不安全的 advisor 输出**——行为异常的 note 可能携带指令性文本;JSON frame + 校验 + advisory-only 框架是仅有的缓解手段,且 note 会原样送达主 transcript(路线图)。
102
+ - **无 `syncBacklog` 追赶等待**——落后很多的 advisor 不会等待主循环;其 backlog 有界且会被丢弃,因此 note 可能在下一次主 turn 开始之后才到达(路线图:context-maintenance batch)。
103
+ - **advisor 上下文有界**——长会话的完整重放会被截断(`maxDeltaMessages`),因此 compaction 后 advisor 可能丢失早期上下文(路线图:下下迭代)。
155
104
 
156
105
  ## 文档
157
106
 
158
107
  | 文档 | 内容 |
159
108
  |---|---|
160
- | [docs/install.zh.md](docs/install.zh.md) | 完整安装指南:git / tarball / 本地目录安装、web Settings 暴露、卸载、`--dump-config` 验证 |
109
+ | [docs/install.zh.md](docs/install.zh.md) | profile 安装(web + dsh-tui)/ registry / git / tarball / 本地目录变体 / web Settings 暴露 / 卸载 / `--dump-config` 验证 |
110
+ | [docs/configuration.md](docs/configuration.md) | `advisor` 命名空间全字段:键与默认值、显式模型门禁(S4)、配置面(web 卡片 / 补丁层 / 全局 settings.yaml)、示例 YAML、live 重应用行为 |
111
+ | [docs/consumer-api.md](docs/consumer-api.md) | 开发者消费契约:包根库 API、`dsh-advisor/client` 入口、`/advisor` 指令面、导出清单、生命周期 |
112
+ | [docs/verification.md](docs/verification.md) | 验证记录:测试矩阵(16 文件 / 319 用例)、typecheck/build、CI 契约、真实环境步骤 |
113
+ | [docs/release.md](docs/release.md) | 发布流程:PR 驱动的 Release prep + Release 工作流、OIDC trusted publishing、版本策略、回滚 |
161
114
 
162
- ## 许可证
115
+ ## 许可
163
116
 
164
- MIT
117
+ 本项目以 **MIT** 许可证发布,全文见 [LICENSE](LICENSE)。版权与许可条款以 LICENSE 文件为准。