Skip to content
dsh.fish
Bundle

@slhssb/dsh-advisor

Independent-model advisory review for DeepSeek Harness: after each tool step, a reviewer model audits the agent's operations and injects concerns/guidance into the next step.

Source
slhssb
stars
1 stars
License
MIT
Updated
Updated yesterday

Readme

# dsh-advisor

**English** | [中文](README.zh-CN.md)

[![npm](https://img.shields.io/npm/v/@slhssb/dsh-advisor)](https://www.npmjs.com/package/@slhssb/dsh-advisor)
[![license](https://img.shields.io/npm/l/@slhssb/dsh-advisor)](LICENSE)

Independent-model advisory review for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).

After each tool step, a separate reviewer model audits the agent's most recent
operations. When it finds a real concern (destructive or irreversible
operations, contract/schema violations, deviations from the user's explicit
request, correctness bugs), it injects a short `[advisor]` guidance message
into the next model call so the agent can self-correct. When the work is
sound, nothing is injected and the review costs only the (cheap) reviewer
call.

dsh has no built-in advisor; this plugin implements one on the standard
`agent/pre-step` waterfall (the same injection channel used by
`dsh-agent-instructions` and `dsh-compaction-basic`).

## Install

```sh
dsh plugin add @slhssb/dsh-advisor
```

or add to the profile's `package.json`:

```json
"dependencies": { "@slhssb/dsh-advisor": "^0.1.0" },
"dsh": { "profile": { "bundles": ["@slhssb/dsh-advisor"] } }
```

Then `npm install` (or `pnpm install`) in the profile directory and restart dsh.

## Configuration

Defaults target the DeepSeek official API (`deepseek-official` provider) with
the cheap `deepseek-v4-flash` model. The API key is **not** handled by this
plugin: the `deepseek-official` adapter resolves it per request from the
`DEEPSEEK_API_KEY` environment variable or the credentials store.

The defaults need no override — they already target the DeepSeek official
API (`deepseek-official`/`deepseek-v4-flash`). To point the reviewer
elsewhere, override in the profile's `cordis.patch.yml` (last write per row
wins). This example routes the reviewer through a third-party relay: the API
key is resolved by that provider's adapter (here the `TOKENRHYTHM_API_KEY`
environment variable, per its `apiKeyEnv` in `settings.yaml`) — this file
never holds keys:

```yaml
- id: advisor
  config:
    provider: tokenrhythm
    model: deepseek-v4-pro
    maxTokens: 512
    maxHistoryMessages: 40
    interval: 1
    timeoutMs: 30000
```

| Key | Default | Meaning |
| --- | --- | --- |
| `provider` | `deepseek-official` | Reviewer provider route (any OpenAI-compatible adapter works). |
| `model` | `deepseek-v4-flash` | Reviewer model. |
| `maxTokens` | `512` | Reviewer output cap. |
| `maxHistoryMessages` | `40` | Most recent derived messages sent to the reviewer. |
| `interval` | `1` | Review every N steps that contain tool results (1 = every step). |
| `timeoutMs` | `30000` | Per-review timeout; a timeout degrades silently. |

Disable entirely with `disabled: true` on the row, or set an empty
`provider`/`model` to fall back to the current request route (`agent/session`
request header, then agent options).

## Rules (deterministic checks)

Beyond the LLM review, `rules` give you zero-cost, deterministic checks that
match the latest tool call (tool name + raw arguments JSON) against regular
expressions. They never fail and never cost a token; a `warn` rule injects a
`[advisor] Rule check:` message into the next step, a `block` rule rejects
the step outright (opt-in — default is `warn`).

```yaml
- id: advisor
  config:
    provider: deepseek-official
    rules:
      - id: no-recursive-delete
        pattern: 'Remove-Item|rm\s+(-rf|-r\s*-f)|del\s+/[sq]'
        message: '检测到破坏性删除命令,请确认目标路径与用户授权。'
        action: warn            # or block
        tools: ['pwsh', 'bash'] # optional: only run for these tool names
        enabled: true           # optional, default true
```

| Key | Default | Meaning |
| --- | --- | --- |
| `id` | — | Stable rule id (logged when the rule fires). |
| `pattern` | — | Case-insensitive JS regex matched against `toolName + arguments`. |
| `message` | — | Text injected as `[advisor] Rule check: …`. |
| `action` | `warn` | `warn` injects guidance; `block` rejects the step. |
| `tools` | all | Optional tool-name substrings the rule applies to. |
| `enabled` | `true` | Disable without removing the rule. |

Invalid rules (missing fields, bad regex) are skipped with a warning; the
agent flow is never blocked by a misconfigured rule. Multiple `warn` hits are
merged into one message. `block` fires before the LLM review — the review is
not run for a blocked step.

## How it works

1. `agent/pre-step` (waterfall) fires before every model call.
2. The plugin scans the session log for the newest `tool/result` event. If
   none exists, or its seq was already reviewed, the step passes through.
3. Otherwise the reviewer model receives the recent derived history plus the
   review instruction and streams a response.
4. A non-empty response is wrapped as a `user` message
   (`source: { kind: 'plugin', plugin: 'dsh-advisor' }`, text prefixed
   `[advisor]`) and spliced into the step's messages after the claimed
   messages, before system context — the same insertion point
   `dsh-agent-instructions` uses, so the next model call is guaranteed to see
   it.
5. Review failure (LLM error, timeout, empty output) logs a warning, injects
   nothing, and advances the reviewed-seq marker so the same batch is not
   retried on the next step. The agent's main flow is never blocked.

Injected `[advisor]` messages are ordinary `user/message` log entries; they
never produce `tool/result` events, so review can never recurse on itself.

## Development

```sh
npm install
npx tsc -p tsconfig.json
node test/smoke.mjs   # hand-built fakes; no network
```

`lib/` is committed so git-installed consumers work without a build step;
`node_modules/`, `test/smoke.mjs`, and `package-lock.json` are gitignored.
`npm pack` ships `lib` via the `files` whitelist.

## Publish

Published on npm as `@slhssb/dsh-advisor`; the repository carries the GitHub
`dsh-plugin` topic for discovery. Bump `version` in `package.json`, publish,
then tag the release:

```sh
npm publish --access public
git tag v0.1.0 && git push --tags
```

Install

dsh plugin --profile web add github:slhssb/dsh-advisor

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source