dsh-daruma

modeltested: passnpmMIT

Keeps long DSH sessions alive when a model channel starts failing: daruma takes over after the in-box retry gives up, trips a self-healing circuit breaker on repeated 429/5xx or a terminal error, and moves the next request to another channel in your chain. Works on DSH 0.1.x and 0.2.x hosts, includi

下载量 / Downloads
1,250
依赖体积 / node_modules
2.26 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-daruma

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-daruma

Daruma resilience plugin for DeepSeek Harness — detects model-request failures

and fails over to another channel to keep long tasks alive.

How it works

dsh-daruma is a native Cordis plugin. It sits **downstream of the in-box

dsh-llm-retry** on the agent/request-error waterfall: retry owns the

same-channel retry budget, and when it gives up it delegates (via next()) to

daruma. daruma then:

1. records the failure against the current channel's circuit-breaker state;

2. when the channel trips (failureBudget consecutive failures, or a terminal

code like QUOTA / INVALID_CREDENTIAL / CONTEXT_WINDOW_EXCEEDED), it

arms the next routable channel (the user-chosen backup first, then the

configured chain) and returns { kind: 'retry' };

3. on the retry turn, the agent/request waterfall swaps the request config

onto the armed channel — dropping the caller's reasoningEffort when that

target does not advertise the level (see below);

4. appends one JSON line per decision to the audit log (see below).

A failover target has to accept the request. Two different refusals hide

behind that sentence.

A channel-level refusal is the usual one: DSH validates an explicit

reasoningEffort against the target's own capability and refuses the

request before any provider I/O; that refusal is raised outside

agent/request-error, so a swapped request carrying an effort the target

cannot take kills the turn without ever reaching the provider — the failover

channel never dispatches, and the channel that is actually broken keeps looking

healthy. daruma therefore reads the target's model metadata on every swap: a

supported effort is kept, an unsupported (or unresolvable) one is dropped so

the target can use its own default, and the drop is logged —

dsh-daruma: mt::glm-5.3 does not accept reasoning effort "high" …. Every

other field (maxTokens, temperature, stop sequences) is passed through

untouched.

A request-level refusal is the provider answering 400 INVALID_REQUEST

because of what the conversation contains. The observed case: DeepSeek's

Messages API rejects any assistant turn that carries tool_use but no

thinking block while thinking mode is on, so a conversation that started on a

non-DeepSeek model cannot be replayed there. That channel is healthy for every

other conversation, so daruma does not open its circuit; it records the channel

as unusable for that conversation only (INVALID_REQUEST is request-scoped,

see daruma-core) and picks a different target. The exclusion lasts until the

conversation's scope is disposed, so one rejected step cannot drag the turn

back to the same dead route and burn the give-up budget.

Self-healing: the host exposes no request-success event, so success is

inferred — an agent whose previous model request never tripped

agent/request-error earns its channel a success record at the next

agent/pre-step or agent/turn-stopping. The circuit closes and the failure

counter resets; tripped channels never linger as zombie COOLDOWN entries.

Channel health persists to ~/.dsh/daruma/channel-health.json, so a tripped

channel stays cooled-down across restarts.

Visible failover notices in the conversation

Older builds appended a daruma/failover session event on each channel

switch, and the web client renders that as a small row inside the

conversation (`mt::glm-5.3 failed (RATE_LIMIT) → trying

mt::deepseek-v4-flash`, budget usage on hover; a red-dotted give-up row when

recovery is exhausted). Current builds do not append it. The host cannot mark

a plugin event ignorable, and a session containing an unmarked daruma/*

event fails to load. New switches are recorded only in the audit log.

Usage screenshots / 使用截图

Captured from live DSH Web sessions: the composer and the channel picker on a

real mt provider, the failover row from the mock-a → mock-b demo flow.

The images are linked by absolute URL on purpose: npm's web UI does not resolve

relative README image paths, so a ./assets/… link renders broken on the

package page (it still works on GitHub). The copies shipped in the tarball

(files) are for readers of the package itself.

English

The composer. The daruma control sits at the right end of the tool row, next

to the model selector, and shows the armed backup channel (`Backup:

mt/glm-5.3`):

Picking a backup channel. Clicking the control opens the picker: the current

backup, a provider box, and the candidates discovered from that provider's

adapter:

The model selector the control sits next to:

A failover notice rendered inline in the conversation, right where the switch

happened:

中文

输入框。 daruma 控件在工具行最右端、模型选择器旁边,显示当前已设置的备用渠道

(备用: mt/glm-5.3):

选择备用渠道。 点开控件即出现选择面板:当前备用、provider 输入框,以及从该

provider 适配器发现的候选模型:

模型选择器(控件就在它旁边):

切换提示,直接渲染在会话流里发生切换的位置:

Audit log

Every failover / give-up / boot decision is appended to

~/.dsh/daruma/failover-log.jsonl (rotates at 2 MiB

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::12,最后更新 2026-11-04

Links

GitHub · Homepage · npm

Same category