deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
🇬🇧 English • 🇷🇺 Русский • 🇨🇳 中文说明
|
⭐ If you like this plugin, please star it on GitHub — it shows me that the plugin is useful to you and motivates me to keep developing it.
🐛 If you find a bug or would like to request a feature, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version. |
#307).catch (#315).#311, #314).#308).try ... finally block, preventing key starvation during clean finishes or client stream aborts.PROBE_RETRY_DELAY_MS) in SandboxRunner.probeModels before marking keys as broken.x-ratelimit-remaining-* and Retry-After headers before hitting 429 errors./models endpoint to automatically revive broken keys without token burn.round-robin, least-loaded (concurrency), and lowest-latency (p95 latency).dsh-clinebot Style: Sequential batch "Test All Keys" runner with live progress, Live Event Stream drawer, and Quota Reset countdown badge.en) and Chinese (zh) UI dictionaries.CIRCUIT_OPEN) until a cool-down; half-open probes recover automatically.circuit plus meta.expectedClones / meta.notifyQueue.test/smoke-rotation-080.test.mjs.resolveBaseUrl to map key credential refs to owning provider pools, restoring live probeModels testing.canaryTimer and selfHealTimer in Cordis effect scopes, eliminating background orphaned intervals on hot reload.pickLeastLoaded for uninterrupted least-connections routing.dsh-clinebot baseline: modular section cards, live pool telemetry stat boxes, pill badges, and complete semantic theme token styling (#281).props.t). Active locale: host snapshot → first navigator.languages entry → en (#277).All, Ready, In Cooldown, and With Errors quick filter chips.High-throughput autonomous agent workflows, parallel subagent swarms, and multi-turn tool loops inevitably hit upstream API rate limits (HTTP 429, RPM/TPM exhaustion, daily quotas, or sudden provider outages). In standard DeepSeek Harness deployments, a single exhausted API key breaks the entire agent execution chain, requiring manual intervention and destroying the session's replay state.
dsh-key-rotation provides a seamless, enterprise-ready transparent API key pooling, pre-emptive rate-limiting, and cross-provider failover engine built natively on the Cordis microkernel architecture.
Unlike naive routing proxies that alter provider identifiers, dsh-key-rotation hooks into ctx.credentials.resolve and intercepts llm/stream at runtime:
graph LR
subgraph ClientLayer ["Client & Agent Turn"]
UserMsg["User / Subagent Message"] --> Adapter["pi-ai Model Adapter"]
end
subgraph RotationEngine ["dsh-key-rotation Core Engine"]
Adapter --> StreamHook["llm/stream Interceptor"]
StreamHook --> BucketCheck{"Token Bucket\nRPM / TPM Check"}
BucketCheck -->|Under Limit| ConcurrencyCheck{"Concurrency Tracker\nLeast-Connections"}
BucketCheck -->|Exceeded| NextKey1["Pick Next Healthy Key"]
ConcurrencyCheck -->|Slot Available| KeyResolver["ctx.credentials.resolve"]
ConcurrencyCheck -->|Saturated| NextKey1
KeyResolver --> ActiveKey["Active Key (In Use)"]
ActiveKey -.->|HTTP 429 / Quota / Error| Failover["Instant Failover Handler"]
Failover --> BackoffCalc["Exponential Backoff & Quarantine"]
Failover --> NextKey2["Retry Next Key (Zero Token Loss)"]
Failover -.->|All Pool Keys Exhausted| CascadeEngine["Cross-Provider Cascade"]
BackoffCalc --> QuotaWindow["Calendar Reset / Midnight Window"]
BackoffCalc --> SelfHeal["Self-Heal Idle Sweep"]
SelfHeal -->|Cooldown Expired| PoolReady["Restored to Ready Pool"]
end
subgraph UpstreamLayer ["Model Provider Endpoints"]
ActiveKey --> UpstreamAPI["Primary Provider API"]
CascadeEngine --> FallbackAPI["Backup Provider API"]
end
style ClientLayer fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
style RotationEngine fill:#181825,stroke:#cba6f7,stroke-width:2px,color:#cdd6f4
style UpstreamLayer fill:#11111b,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
INVALID_REPLAY_STATE crashes in pi-ai multi-turn sessions.QUOTA, RATE_LIMIT, SERVER, TIMEOUT, TRANSPORT, EMPTY_RESPONSE, UNKNOWN_MODEL, AUTH, and INVALID error codes.SWITCHABLE_MESSAGE_PATTERN) identifies text-based quota/rate-limit errors thrown as generic exceptions by upstream SDKs.agent/request-error lifecycle hook.lib/bucket.js): Sliding-window tracking of Requests Per Minute (rpmLimit) and Tokens Per Minute (tpmLimit). Quarantines saturated keys before dispatching network requests, preventing 429 roundtrips.lib/concurrency.js): Tracks active in-flight streams per key (inFlight). Distributes concurrent requests evenly across available credentials and enforces maxConcurrency limits.lib/cascade.js): If all keys for a selected provider are in cooldown, requests automatically cascade to an alternative fallback provider pool (e.g., primary provider → fallback proxy / secondary provider).lib/sandbox.js): On-demand /models probes validate a key before returning it to rotation; idle cooldowns are lifted by the self-heal sweep.lib/quota-window.js): Supports scheduled quota reset alignments (midnight_utc, midnight_pst, and rolling_24h) so daily free/tier quotas unfreeze exactly when upstream resets them.lib/pool.js): Successive failures on a key double its quarantine duration (base → ×2 → ×4 → cap ×8). Successful requests gradually restore healthy status.lib/pool.js): Configure dedicated key pools for specific model tiers (e.g. reasoning/heavy models vs fast/cheap utility models).lib/model-quota.js): Give one credential its own local token budget for one model. A key that spends its budget is skipped for that model only — an exhausted claude-sonnet budget never disables the same key for claude-opus.production, background, eval) to match key usage with workload priorities.tokenLimit applies to one credential in one configured model pool. The same
credential can hold an independent budget in every model pool it belongs to:
dsh-key-rotation:
quotaResetWindow:
type: midnight_utc
hour: 0
providers:
- provider: anthropic
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
models:
claude-sonnet:
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
quotas:
CLAUDE_KEY_A:
tokenLimit: 1000000
CLAUDE_KEY_B:
tokenLimit: 1000000
claude-opus:
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
quotas:
CLAUDE_KEY_A:
tokenLimit: 200000
CLAUDE_KEY_B:
tokenLimit: 200000
Behaviour:
claude-sonnet request
→ CLAUDE_KEY_A still has Sonnet budget
→ dispatched on CLAUDE_KEY_A
→ actual usage is charged to CLAUDE_KEY_A / Sonnet
→ CLAUDE_KEY_A / Sonnet reaches its limit
→ later Sonnet requests skip CLAUDE_KEY_A and use CLAUDE_KEY_B
→ claude-opus requests may still use CLAUDE_KEY_A
Rules and limits worth knowing:
tokenLimit is the number of tokens one credential may spend on one model
pool within the current quota window. A missing, null, non-numeric or
non-positive value means no local model token limit — omitting quotas
entirely leaves behaviour byte-identical to previous releases.quotaResetWindow controls reset timing for these budgets, reusing the
existing midnight_utc / midnight_pst / rolling_24h settings. Resets are
lazy: the counter returns to zero when the window elapses, with no per-key timer.usage is charged. Failed, aborted or refused
requests are never billed.quotas.<REF> restores Unlimited.lib/webhook.js): Dispatches rich notifications with HMAC-signed action buttons for Telegram (Inline Keyboards), Discord (Action Rows), and Slack (Block Kit). Administrators can click buttons to reset cooldowns or pause providers directly from their mobile chat.lib/usage-report.js): Per-key daily request counters and estimated cost breakdown with one-click CSV/JSON export (GET /dsh-key-rotation/usage-report).lib/histogram.js): Tracks Time-To-First-Token (TTFT) and stream durations with health score degradation scoring (0..100).The settings card includes an Updater section:
GET /api/dsh-key-rotation/update returns currentVersion, latestVersion, updateAvailable, canAutoUpdate (version metadata only; no secrets).POST with the same path installs the exact latest npm version through the standard dsh plugin add flow. The request is accepted only from loopback with a matching same-origin Origin/Host (and the dedicated header). Cross-origin or missing-origin POST is rejected with 403.No --force, no raw shell, no install from worktree/DEV paths. Update runs only after an explicit click.
Access full visual management under Settings → Key Rotation or via the Header quick-widget.
| Interface Feature | Description |
|---|---|
| Header Status Widget | Compact live badge in DSH header: 🟢 All Healthy | 🟡 Cooldown Active | 🔴 Pool Exhausted with quick popover actions. |
| 1-Click Health Matrix | "Health Matrix" dashboard running parallel sandbox probes across all providers, keys, and models with TTFT latency and status badges. |
| Instant Key Provisioning | Add keys with auto-generated names (<PROVIDER>_API_KEY, _2, _3) and automatic key-tail disambiguation. |
| Live Status Badges | Visual states: In Use, Ready, Cooling Down (with live countdown timer), and Not Found. |
| Drag & Priority Ordering | Reorder keys with ↑ and ↓ buttons to fine-tune selection precedence. |
| Switch Code Toggles | Interactive checkboxes for switchable error conditions. |
| Secret Leak Detector | Real-time input sanitizer (lib/keycheck.js) catching accidental pastes of private keys, SSH keys, or misplaced tokens. |
Batch .env Import |
Parse standard .env key-value pairs directly into corresponding provider pools. |
| 5-Second Undo Bar | Non-destructive undo bar for accidental key or pool removals. |
| Model Sub-Pools & Token Quota Editing | Maintain per-model key lists under each provider, with per-key token limits, live used/limit, percentage, remaining tokens and reset countdown. |
| Usage Analytics Chart | Interactive breakdown of lifetime requests and daily trends per key. |
MY_PROVIDER_API_KEY).$DSH_HOME/.credentials.yaml managed by the DSH Credentials service.keyTail): Full secret values are never sent to the client browser; only the trailing 5 characters are exposed for visual identification. Short keys (<= 5 characters) return a fixed masked placeholder (***) to prevent credential disclosure.isTrustedBridgeRequest): the socket peer and the Host header must both be loopback (127.0.0.1, ::1, localhost), and sec-fetch-site: cross-site is always refused. An attached Origin must be an http(s) loopback origin matching Host exactly. Origin is required on POST/PUT/PATCH/DELETE but optional on GET/HEAD/OPTIONS, because browsers omit it on same-origin reads — requiring it there would reject the Settings card's own requests.LOCAL_POOL_EXHAUSTED error rather than falling back to leaking unmanaged credentials.::ffff:127.0.0.1, ::ffff:7f00:1, 64:ff9b::/96), and enforces connect-time DNS validation via undici agent dispatchers to prevent TOCTOU DNS rebinding.# Install via DSH Plugin Manager (Web Profile):
dsh plugin --profile web add @goodandready/dsh-key-rotation
# Or directly from GitHub:
dsh plugin --profile web add github:GooDAnDReaDY/dsh-key-rotation
[!IMPORTANT] Restart DeepSeek Harness web service after installation and refresh your browser tab:
systemctl --user restart dsh-web
settings.yaml)dsh-key-rotation:
switchCodes:
- QUOTA
- RATE_LIMIT
- SERVER
- TIMEOUT
- TRANSPORT
- EMPTY_RESPONSE
- UNKNOWN_MODEL
- AUTH
cooldownMs: 60000
# v0.8.0 circuit breaker
circuitBreakerEnabled: true
circuitBreakerThreshold: 5
circuitBreakerOpenMs: 30000
circuitBreakerHalfOpenProbes: 1
concurrencyLimit: 5
quotaResetWindow:
type: midnight_utc
hour: 0
cascade:
- provider: backup-provider-id
model: your-backup-model-id
webhookUrl: "https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>"
providers:
- provider: your-primary-provider
rpmLimit: 60
tpmLimit: 100000
keys:
- PRIMARY_API_KEY
- PRIMARY_API_KEY_2
- PRIMARY_API_KEY_BACKUP
models:
reasoning-model-id:
keys:
- PRIMARY_API_KEY
- PRIMARY_API_KEY_2
quotas:
PRIMARY_API_KEY:
tokenLimit: 500000
PRIMARY_API_KEY_2:
tokenLimit: 1000000
- provider: secondary-provider
keys:
- SECONDARY_API_KEY
- SECONDARY_API_KEY_2
| Parameter | Type | Default | Description |
|---|---|---|---|
switchCodes |
string[] |
[QUOTA, RATE_LIMIT, ...] |
List of error codes that immediately trigger failover. |
cooldownMs |
number |
60000 (1 min) |
Base penalty duration (in ms) for quarantined keys. |
circuitBreakerEnabled |
boolean |
true |
Enable per-provider circuit breaker (v0.8.0). |
circuitBreakerThreshold |
number |
5 |
Consecutive failures before opening the circuit. |
circuitBreakerOpenMs |
number |
30000 |
How long the circuit stays open (ms). |
circuitBreakerHalfOpenProbes |
number |
1 |
Probe requests allowed in half-open state. |
verboseLogging |
boolean |
false |
Per-request rotation logs (noisy; off by default). |
concurrencyLimit |
number |
0 (disabled) |
Max concurrent in-flight streams per key (0 = unlimited). |
quotaResetWindow |
object |
null |
Calendar reset alignment for both provider-reported quota cooldowns and local per-model token budgets (midnight_utc, midnight_pst, rolling_24h). |
cascade |
array |
[] |
Fallback provider chain when primary pool is completely exhausted. |
webhookUrl |
string |
"" |
Target URL for interactive Telegram, Discord, Slack, or generic alerts. |
providers |
array |
[] |
List of { provider, keys, rpmLimit, tpmLimit, models } definitions. |
providers[].models |
object |
{} |
Per-model sub-pools: { <model>: { keys: [...], weights: [...], quotas: { <REF>: { tokenLimit } } } }. A model pool may also use credentials the provider base pool does not list. |
providers[].models.<model>.quotas |
object |
{} |
Local token budgets keyed by credential ref. Omit for unlimited. See Per-Model Per-Key Token Quotas. |
All management routes require loopback authentication (127.0.0.1 / ::1) with same-origin validation:
| Route | Method | Description |
|---|---|---|
/dsh-key-rotation/status |
GET |
Real-time health, keys, cooldowns. Since v0.8.0 also providers[].circuit and meta (expectedClones, notifyQueue). Model sub-pools appear as their own entries with an additive model field, and each key carries modelQuota (null = unlimited). |
/dsh-key-rotation/config |
GET / PUT |
Read and update active key rotation settings and provider pools. |
/dsh-key-rotation/key |
PUT / DELETE |
Add, update, or remove credentials in host storage and pool. |
/dsh-key-rotation/reset |
POST |
Instantly resets all cooldowns and restores all keys to ready. |
/dsh-key-rotation/test-matrix |
POST |
Triggers parallel health check across all configured keys and models. |
/dsh-key-rotation/usage-report |
GET |
Returns aggregated usage metrics in JSON or CSV format (?format=csv). |
/dsh-key-rotation/webhook-action |
POST |
Receives and executes interactive actions from Telegram/Slack callbacks (Authorization: Bearer or X-Telegram-Bot-Api-Secret-Token). |
MIT © GooDAnDReaDY
healIdleCooldowns to read from the modern pool.state.lastUsedAt map (with backwards-compatible fallback). credentials.resolve now properly records each key invocation timestamp in lastUsedAt, surfacing accurate "last used" indicators in the status dashboard and enabling background idle cooldown restoration.buildRuntime() calls across periodic sweeps, provider exhaustion handling, and /status query processing.buildRuntime() calls inside the rotate() finish chunk handler by reusing the request-scoped runtime0 snapshot.extractRateLimit() with single-pass header inspection and length guards, completely removing dynamic lowercase/uppercase string allocations on every response chunk.todayIso string calculation once per finishing request instead of creating multiple Date instances for costDays and usageDays.[...values()].reduce() with iterative summation for totalUsage in the /dsh-key-rotation/status endpoint.budgetNotifiedAt, lowHealthNotifiedAt) when provider pools are deleted.pickedRef), start timestamps, and retry counts strictly to each async dispatch context using Node's node:async_hooks. Eliminates race conditions where concurrent streaming requests could penalize healthy keys.isSwitchableError and cascades seamlessly to the next key if no content tokens have been yielded.attemptList), eliminating shared mutations on pool.weightedRefs./dsh-key-rotation/test) automatically lift failedUntil and brokenUntil quarantine flags.compactUsage(pool, 30, now) into the periodic 30-second maintenance sweep to prevent memory growth on high-uptime servers.startMs for accurate p50/p95 latency metrics under concurrent load.shadow, incident, agent-budget, region, canary, maintenance) and dead route registrations.429, 401, 403, 5xx) and gRPC codes (RESOURCE_EXHAUSTED, UNAVAILABLE) alongside regex fallback.document.visibilityState).credentials.resolve patch and ctx.on event handlers (llm/stream, agent/request-error) in ctx.effect scopes with guaranteed unmount cleanup (#238, #239)..role('secret') to incidentGitHubToken and webhookActionToken in Config schema for automatic UI masking (#237).settingsScope snapshot reading/saving in settings card with graceful bridge fallback (#235).props.t with locale: NS; plugin registers only en; no settings.section fallback and no bundled ru/zh tables (#236, #275, #277).mountDashboard routine after header-chip migration (#240).CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。