@falling-ts/dsh-force-compact
Forces context compression when running low-context llama.cpp models such as Qwen3.8-27B, improving model efficiency and usage smoothness, together with well-refined compaction prompts.
Install
dsh plugin --profile web add @falling-ts/dsh-force-compact
Sandbox test results
| Stage | Result |
|---|---|
| 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-force-compact
Aggressive, local-first context compaction for DeepSeek Harness agents.
A DSH Cordis function plugin that keeps the agent's working context lean by design: serve
Qwen3.8‑27B on a self-hosted llama.cpp with a modest context, and the plugin shrinks the
conversation itself — a large-window feel with no API cost and no data egress.
[中文](README.cn.md)
Why
- Self-hosted inference — the agent talks to a local OpenAI-compatible llama.cpp server
through the standard DeepSeek adapter; no separate adapter needed.
- Low context, high signal — instead of fighting a small cap, the plugin shrinks the
conversation, so the agent reasons over a tight prompt while keeping deep memory in the
compressed head.
- Think off for compactions, passthrough everywhere else —
disableThinking: true
(default) turns thinking off on this plugin's own compaction summarization call only;
every other model request rides the machine's configuration unchanged.
- Private & free — no per-token billing, no egress.
What it does
Two compaction engines coexist behind one facade (resolveCompaction), transparent to callers:
Engine · Used when · Notes
Official · the compaction service resolves in the agent realm · Preferred; delegates to compaction/basic.
Builtin · automatic fallback (typical standard preset isolates the service) · Self-contained persistent transaction on ctx.sessions / ctx.llm.stream / ctx.tokenMeter; reuses the official compaction/* event vocabulary, so it replays safely across builds.
No toggling — official wins when reachable, builtin takes over otherwise.
Trigger points
- Per-request guard (
agent/pre-step) — reads the session's projected context tokens
(the exact number the harness renders bottom-right). At autoThresholdTokens it rejects the
outgoing request and compacts the head instead, retaining the latest retainLatestTokens
verbatim. Below the threshold the request proceeds.
- Turn end / idle (
agent/status→idle) — when the agent quiesces, optionally
compacts via compactNow (gate: turnEndForceCompactionEnabled).
- Manual
/force-compact— immediatecompactNowwhen idle; when busy it queues a
process-local flag consumed at the next model step. Loaded lazily — see "Command
availability" under Install. In the / picker the row carries the same face as the
first-party commands: an icon, a localized name and a localized description (the official
presentation table only covers first-party commands, so the client half attaches the face
itself — see AGENTS.md, "指令行的官方外观").
session/flush— the awaited durability checkpoint.
Every path funnels into the single "compaction result landed in the session" boundary — the
same point where the LiveUI signal fires.
Decision key is projectedTokens (provider-anchored, same figure as the UI corner), so the
plugin never drifts from what you see; the threshold-aware shrink gate skips summarizer calls
that provably cannot pull the session below the threshold (kills the low-threshold dead loop).
The builtin transaction bills shadowedTokenCount from the same tokenMeter.measure
per-node prices the official engine uses, so the meter's collapse protocol settles the drop
correctly — the bottom-right counter goes down after compaction.
Thinking control: scoped to compactions
Since the 2026-08 semantics revision, disableThinking controls one thing: whether this
plugin's own summarization call (engine/builtin.js → engine/summarizer.js →
ctx.llm.stream) carries reasoningEffort:'off'. Everything else is untouched:
Call site · With disableThinking: true
Builtin-engine summarization call · Carries reasoningEffort:'off'
Every other model request (business, sub-agents, tools, other plugins) · Machine's LlmCallConfig unchanged
Official compaction service calls · Not routed through any plugin seam — unaffected
A route that cannot express off degrades instead of failing. Not every catalog model
declares an off level — pi-ai's thinkingLevelMap may map it to null, as
opencode-go/deepseek-v4.1-flash does (it accepts only low / high / max, while
deepseek/deepseek-flash accepts off too). The summarizer probes the route's capability
before the call (llm.resolveCallConfig, a detached lookup with no provider I/O) and, when
off is unavailable, uses the cheapest level the route does accept; a model with no
reasoning support at all gets no effort field. Because a thinking route bills its reasoning
trace against the same maxTokens cap, a fixed reasoning allowance is added on top of
maxSummaryTokens (documented as the cap on the summary text) so the summary still fits.
Both are logged once per route:
summarization route opencode-go/deepseek-v4.1-flash does not support reasoning effort 'off' — using the cheapest supported level 'low' instead; compaction continues
Before this guard, such a route failed every compaction outright (the harness rejects
the unsupported effort before any provider I/O).
When the target is a llama.cpp / OpenAI-compatible endpoint, the
thinking: { type: 'disabled' } field the adapter emits is silently ignored there — so the
summarizer ALSO stamps the llama.cpp-native top-level reasoning_effort: "none", gated on
the exact same condition. One options object carries BOTH fields:
Endpoint family · Reads · Result
Real DeepSeek API · reasoningEffort:'off' → thinking:{type:'disabled'} · Thinking off ✅
llama.cpp / OAI-compatible · reasoning_effort:"none" (top-level) · enable_thinking=false ✅
Each family tolerates-and-ignores the foreign key, so emitting both is harmless. The field is
stamped in src/engine/summarizer.js (immediately before llm.stream(options)), NOT in the
llm/stream waterfall — a prior draft
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::30,最后更新 2026-10-30