dsh-im
xmanrui
通过扫码或机器人凭据把IM机器人接入DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Slack、Telegram、Discord和WhatsApp)。 Connect IM bots to DeepSeek Harness via QR code or credentials (9 channels).
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:malko/dsh-malko-prefs
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
Vibe coded. This plugin was written almost entirely by an AI assistant (OpenCode,
deepseek-v4.1-flash) from natural-language prompts, with light human review and testing. It is not a carefully hand-engineered product: read the code before trusting it, expect rough edges, and check what it changes in your dsh profile — in particular it restates your agent presets (see Install). Use at your own risk.
Personal preferences for DeepSeek Harness (dsh): a tunable companion to the official compaction engine, a llama.cpp model enrichment for the Models page, and tab notifications, packaged as a single dsh bundle.
@deepseek-ai/dsh-compaction-basic engine is
the reference behaviour; this plugin wraps it with a settings page so the
threshold, the retention, the summarization model/reasoning and an optional
end-of-turn compaction become user-configurable. It also adds a
/force-compact command.meta.n_ctx, architecture.input_modalities, aliases) and fills
the corresponding llm-pi-ai model entries (context window, text/image input).dsh-notice-center (MIT).0.1.7-rc.1 — built on the current settings domain (Config
fields marked .volatile() on the Host, ctx.configForms on the Client) and
on BasicCompactionEngine. Tested on 0.1.7-rc.2 and 0.2.0-rc.2.llm-pi-ai provider (e.g. your llama.cpp server) for the model
enrichment.From the registry (once published):
dsh plugin --profile web add dsh-malko-prefs
# then restart dsh web
Or from a local checkout / tarball:
dsh plugin --profile web add /path/to/dsh-malko-prefs
dsh plugin --profile web add ./dsh-malko-prefs-0.1.0.tgz
The bundle layer does two things (see cordis.patch.yml):
malkoPrefs and the
malkoModels probe Service, whose strict Typert wire definition is
contributed through the package ./typert export that
@deepseek-ai/dsh-typert-loader registers automatically on mount;preset-cordis, preset-ptc,
preset-standard) with the official compaction-basic row retargeted at
dsh-malko-prefs/compaction.This second point is why the file is generated, not hand-written: a patch replaces a row's
configwholesale and the compaction backend lives nested inside the preset, so swapping it means copying the whole preset. Regenerate it after a dsh upgrade that touches the presets:node scripts/gen-preset-override.mjs # auto-detects dsh on PATH node scripts/gen-preset-override.mjs <presets-dir> # or pass it explicitly DSH_PRESETS_DIR=<presets-dir> node scripts/gen-preset-override.mjsThe generated
cordis.patch.ymlis tied to the preset composition of the dsh version it was generated against — the plugin targets whichever dsh ships those presets (declared through its optionalpeerDependencies).
Rollback: dsh plugin --profile web remove dsh-malko-prefs (then restart).
Adds a section to Settings, split into three sub-tabs: Context compaction, Notifications and llama.cpp models (the tab bar stays pinned while the panel scrolls). All fields are applied live (no restart).

| Field | Meaning |
|---|---|
| Threshold (tokens) | Absolute pressure in tokens, e.g. 130k / 130K / 1.5m. Empty/0 = use the ratio below. |
| Context window (tokens) | Window the absolute threshold is expressed against (e.g. 200k). 0 = ratio only. |
| Threshold ratio | Used when the absolute threshold is empty (0.8 = 80% of the window). |
| Headroom (tokens) | Reserved on top of the output cap. The official default (65536) caps the trigger well below 80%. |
Effective trigger: min(window × ratio, window − output − headroom); when an
absolute threshold and a context window are set, it becomes exactly that
threshold. Default: ratio 0.8 + headroom 32768 → compacts around 160k on a
200k window.
Keep last (tokens) (absolute, e.g. 32k) or Keep ratio — the verbatim
recent-context budget.
Session model (default) or Custom model.Custom model; populated from the
llm-pi-ai catalog.Default, Off, or any level the selected model declares
(session mode exposes only Default / Off). The instruction is appended
after the replayed prefix, so the provider's prompt cache stays warm.Summary output cap, Extra compaction attempts, Overflow recovery attempts.
For every llm-pi-ai provider that declares a baseURL, Fetch from server
reads GET {baseURL}/models. It then shows a review of every discovered
model and what importing would change: context window (from
meta.n_ctx/n_ctx_train), output cap, and input modalities (from
architecture.input_modalities); models that do not exist yet are marked new
model. Tick the models you want (or use Select all) and click Import —
only the selected ones
are written (existing entries are updated, new ones appended). The request
authenticates as the provider does: the endpoint's apiKeyEnv credential is
resolved (dsh credentials, then the host environment) and sent as
Authorization: Bearer …, so an api-key-protected server works.

Two independent features, both driven by the official client signals
(sessions, uiSession.sessionStatus); nothing is persisted beyond the config.

Tab status light — recolours the browser tab icon:
| Colour | Meaning | Clears when |
|---|---|---|
| amber | a session waits for you (question / approval / plan review) | you handle it |
| working | a session is generating | it stops running |
| green | a main session finished while you were away | you open that session / return to the tab |
| idle | nothing to report (official favicon unless you set a colour) | — |
Priority is amber > working > green > idle, and sub-agent sessions are ignored.
All icon links are repainted together (DSH ships a dark and a light favicon
selected by prefers-color-scheme).
System notifications — settings are grouped by notification type. One global master switch (Enable notifications, opt-in; the browser asks for permission once), one global Notify in the foreground (off = only fire when the tab is not visible and focused) and one global Volume. Then one group per type — When a session finishes and While waiting for you (question / approval / plan review) — each with its own Notify enable, Keep on screen (stay until dismissed) and sound.
Sound (per type) — each group's selector defaults to No sound (both
defaults are silent on a fresh install). The list is: No sound, two built-in
synthesized chimes (Chime Up / Chime Down, no asset needed) and 45 bundled
opencode sounds (see assets/audio/README.md); picking
one previews it and the Host serves the mp3s at /malko-prefs-sounds/<id>.mp3.
If that route is unavailable the player falls back to a chime — but browsers may
block audio until you have interacted with the page at least once.
The sound is decoupled from the banner: once a sound is chosen for a type it plays on that event (completion / waiting) even when notifications — or that type — are disabled; with No sound nothing plays.

| Field | Key | Default |
|---|---|---|
| Color the tab icon | colorsEnabled |
on |
| Finished / Waiting / Working / Idle color | green / amber / working / black |
official sidebar colors for green/amber, blue for working; black empty = official favicon |
| Enable notifications (master) | notifyEnabled |
off |
| Notify in the foreground (global) | notifyForeground |
off |
| Volume (global) | notifyVolume |
0.6 |
| Finished: notify / keep on screen / sound | notifyDoneEnabled / notifyDonePersistent / notifyDoneSound |
on / off / none |
| Waiting: notify / keep on screen / sound | notifyPendingEnabled / notifyPendingPersistent / notifyPendingSound |
on / off / none |
System notifications need a secure context —
http://127.0.0.1:PORTorlocalhost. Opened over a LAN IP the Notification API is unavailable (a browser rule, not a plugin one); the tab status light still works.
/compact — the official dsh command (idle manual compaction)./force-compact — compact now: immediately when the agent is idle,
otherwise queued and consumed at the next model step (bypasses the threshold).host plugin (lib/index.mjs)
├─ Config (volatile) ──▶ settings page "malko-prefs"
├─ service malkoPrefs ──▶ live prefs + force queue
├─ service malkoModels ──▶ llama.cpp probe (wire def in lib/typert.host.mjs)
├─ /malko-prefs-sounds/<id>.mp3 ──▶ bundled sound library
└─ /force-compact command
client plugin (lib/client.js)
├─ mounts its own malkoModels Remote via ctx.remote.$mount()
├─ tab status light + notifications (src/notify.ts, optional uiSession)
└─ settings page: 3 sub-tabs (compaction / models / notifications)
preset plugin (lib/compaction.mjs)
└─ MalkoCompactionEngine extends BasicCompactionEngine
├─ rebuilds its policy from malkoPrefs before each operation
├─ summarize() → model + reasoning control (cache-safe order)
└─ agent/status idle → optional turn-end compaction
The engine reuses the official engine for everything else (durable
transactions, pruning, overflow recovery), so behaviour matches
compaction-basic unless a preference changes it.
Note: service classes avoid JS private members (#x) — cordis binds service
methods to a proxy, and private members would throw
Receiver must be an instance of class ....
node build.mjs # esbuild → lib/{index,compaction,typert.host}.mjs + lib/client.js
node scripts/check.mjs # static conformance checks
| File | Role |
|---|---|
src/prefs.ts |
preference fields, defaults, parseTokenText |
src/index.ts |
host plugin: volatile Config, malkoPrefs, /force-compact |
src/compaction.ts |
MalkoCompactionEngine (extends the official engine) |
src/remote.ts |
shared wire identity + probeInvocation() builder |
src/typert.ts |
Host Typert manifest (zod strict codecs) |
src/typert.host.ts |
the ./typert export entry dsh-typert-loader imports |
src/probe.ts |
malkoModels Service (llama.cpp /models probe) |
src/client.ts |
settings page (three sub-tabs) |
src/notify.ts |
tab status light + notifications + sound player (ported from dsh-notice-center) |
assets/audio/ |
45 bundled notification sounds (opencode, MIT) |
src/whale.ts |
the recolored official whale SVG used as the tab icon |
scripts/gen-preset-override.mjs |
regenerates cordis.patch.yml from the installed presets |
scripts/check.mjs |
manifest / bundle / cache-safety checks |
The package is publication-ready:
private is removed; LICENSE (MIT) and author are set; files ships
lib/, cordis.patch.yml, scripts/, assets/ and LICENSE.prepublishOnly runs the build and scripts/check.mjs.dsh.bundle.patch, exports["./client"] and
exports["./package.json"], the client bundle id equals the package name,
and the client requires only baseline platform modules (react,
@deepseek-ai/dsh-client-ui-primitives).npm publish (unscoped/public).Caveat: cordis.patch.yml is generated from the shipped presets, so regenerate
it against the dsh version you target and keep the peerDependencies ranges in
sync before publishing.
cordis.patch.yml copies the shipped presets; re-run
gen-preset-override.mjs after a dsh upgrade./force-compact busy path uses the engine's context-overflow entry,
which compacts the maximal safe head (retention 0).127.0.0.1/localhost); the
tab status light's green/amber state is in-memory and resets on reload./malko-prefs-sounds route is unavailable the
player falls back to a synthesized chime.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: notifications。