dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Shared token budgets for DeepSeek Harness agent trees. A root agent, its one-shot and continuable subagents, and workflow descendants can spend from one durable budget.
Status: experimental, verified against DSH
0.1.0-rc.6. The package is not published to npm yet; it is developed as an open-source contribution to the DSH plugin ecosystem.
scope: tree), or each
session independently (scope: session);~/.dsh/agent-budget/,
never in session logs, so uninstalling the plugin cannot break sessions;budget_status tool, not just a human
command;The package is not published to npm yet. The commands below assume the package is available in your profile workspace (for example via a local checkout with
dsh plugin --profile <name> add -w ., or from a GitHub bundle once this repository is public).
Install as a bundle into a profile (recommended after publishing):
dsh plugin --profile <name> add dsh-plugin-agent-budget
Or install directly with npm/pnpm:
pnpm add dsh-plugin-agent-budget
For git installs, the package builds during install via its prepare script.
pnpm ≥10 blocks build scripts for git dependencies by default, so the first
add fails; add the exact package key pnpm printed to the profile's
pnpm-workspace.yaml and re-run:
allowBuilds:
dsh-plugin-agent-budget: true
The package declares dsh.bundle, so dsh plugin adds it to the profile's
dsh.profile.bundles automatically and --dump-config shows the
# == dsh-plugin-agent-budget layer. The layer is provided by the root
cordis.patch.yml:
- insert:
- id: agent-budget
name: dsh-plugin-agent-budget
config:
maxTokens: 200000
missingUsage: exhaust
scope: tree
maxTokens is required and must be a positive safe integer. missingUsage
defaults to exhaust; set it to ignore only when a provider intentionally
omits usage and you accept incomplete enforcement. scope defaults to tree;
set it to session for one independent budget per session. storageDir is
optional and defaults to ~/.dsh/agent-budget/.
Beyond accounting, the plugin can actively slow consumption as a scope nears its limit. Every knob is opt-in except the pressure prompt:
degradeRatio (0–1): when a scope's remaining ratio drops below this
threshold, each further agent/request is degraded — maxTokens is clamped
to the remaining budget (floor 1) and, when degradeModel is set, the model
is swapped for a cheaper one. maxOutputTokens adds a hard output cap while
degraded.maxConcurrentCalls: caps simultaneous provider calls per scope; excess
calls fail before dispatch with TOKEN_BUDGET_CONCURRENT_LIMIT. This also
bounds the concurrent-admission overshoot described in Semantics.pressurePrompt (default true): injects a budget-pressure notice into the
system prompt once usage passes 50%, escalating at 80%, so the model
self-throttles before hitting the hard limit. It rides the optional
@deepseek-ai/dsh-system-prompt peer; compositions without it load
unchanged.Degradation uses the agent/request waterfall because llm/stream requests
are deep-frozen by the loop and cannot be rewritten there. Controls never
write to the ledger: accounting semantics are unchanged.
The plugin exports four named members and no default export:
name: 'agent-budget'inject: ['llm', 'sessions', 'tools', 'agents'] (web server is optional)apply(ctx, config) — the function-plugin entry pointConfig — the loader-facing config schemaThe model can call the read-only budget_status tool. It returns the limit,
used and remaining tokens, exhaustion state, all four usage buckets,
meteringComplete, and unmeteredCalls. It cannot change the limit.
The web client registers a Token 预算 section in Settings. It lists every
open budget scope, shows limit/used/remaining/exhausted state with a progress
bar, and polls every 30 seconds.
Host HTTP API under /agent-budget/api:
The API is local-only. POST requests require Content-Type: application/json
and X-Agent-Budget-Request: 1; bodies are limited to 16 KiB. Cross-origin
requests are rejected. An active scope cannot be reset (HTTP 409).
See SECURITY.md for access and recovery instructions.
GET /scopes → { ok, scopes: [{ scopeKey, limitTokens, usedTokens, ... }] }POST /adjust-limit with { scopeKey, limitTokens } → overwrites the scope's
limitPOST /reset with { scopeKey } → clears usage but keeps the current limitadjust and reset are appended to the sidecar ledger as new event lines.
Old open/sample lines are never modified, so replaying the ledger after a
reload produces the same result as incremental updates.
scope: tree (default), the plugin resolves the tree root using DSH's
runtime agent ownership first, falls back to the durable parentSession
chain, and finally falls back to an independent budget with a warning when
neither is available. It never merges unrelated sessions into one account.scope: session, every session has an independent budget, including
subagents.llm/stream call with a sessionId is included: conversation,
subagent, workflow, compaction, and title-generation calls.TOKEN_BUDGET_EXHAUSTED.sessionId are outside any agent tree and are not metered.The plugin stores its ledger in:
~/.dsh/agent-budget/
ledger.jsonl append-only ledger (including start/end call boundaries)
scope-index.json sessionId -> scopeKey index
writer.lock exclusive writer lease
budget/* events into session logs.~/.dsh/agent-budget/.storageDir is intended for one DSH process at a time. If headless
and web run concurrently, give each profile a distinct storageDir or do
not run them against the same ledger simultaneously.Versions before this sidecar design wrote budget/* events into session logs.
Run the migration once before upgrading a profile that used those versions.
Migrate them with:
node scripts/migrate-session-log.mjs
The migration tool:
~/.dsh/sessions/**/session.jsonl(.zstd);budget/* events into ledger.jsonl and scope-index.json;.bak backup before writing.Stop DSH processes that use the affected profile before running it.
scope: tree may fall back to an independent budget in extreme cold-start
cases when no parent can be resolved. This prefers under-sharing over
incorrectly locking unrelated chats together.0.1.0-rc.6. When upgrading DSH, re-check: the
llm/stream hook signature, the agent/request-error payload shape, and the
ctx.agents runtime ownership API.maxConcurrentCalls to bound it.missingUsage: 'ignore'.src/index.ts plugin implementation
tests/ unit + integration tests (deterministic mock streams)
cordis.patch.yml default bundle layer for dsh plugin profiles
scripts/ lint, test, pack verification, legacy migration
docs/design.md design decisions and compatibility notes
.github/workflows/ci.yml CI on Node.js 22 and 24
See CHANGELOG.md for release history.
pnpm install
pnpm check
The test suite uses a deterministic mock stream. pnpm test:smoke additionally
runs a small real DeepSeek request when DEEPSEEK_API_KEY is present and skips
otherwise. CI covers Node.js 22 and 24.
See CONTRIBUTING.md for contribution guidelines.
MIT