Repository navigation
Releases: Asaiuta/dsh-blackhole
Release list
0.5.0 — first tagged release
dsh-blackhole v0.5.0 — first tagged release (source archive; consume via profile link: or the npm files layout).
Highlights of this cycle (full details below, carried verbatim from the CHANGELOG): preset-standard re-alignment against dsh-web-app rc.2 (the stale toolFilter rows broke new-session creation), explicit evidence-bearing recall queries with bounded excerpts, source-preserving index checkpoint carry, and the 0.5.0 index checkpoint format with settings routes.
-
The
preset-standardcopy no longer carriestoolFilter. The bundle patch
restates that preset's wholeconfig.pluginslist (patches replace config
wholesale), so it also froze thetoolFilter.deny: [schedule_*]rows that the
0.2.1-alpha.2baseline had ontool-subagent/tool-subagent-fork. Current
@deepseek-ai/dsh-tool-subagent(0.2.1-rc.2) rejectspersonaandtoolFilter
inapply()withObject.hasOwn, which made the whole preset invalid
(agent-preset/invalid) and broke new-session creation as soon asstandard
became the selected default. Re-aligned againstdsh-web-app@0.2.1-rc.2
presets/standard.patch.yml; restricting a child's tool surface is now an
Agent Preset choice (agentPreset), not a filter row. -
Recall queries are explicit and evidence-bearing. Only
/pattern/flags
enables regex; ordinary model names, paths and prose use keyword search.
Bare regex callers must add delimiters (hook|inject→/hook|inject/).
Ranked transcript results append up to three bounded verbatim hit excerpts
when the head preview lacks query terms, with explicit full-context guidance.
Excerpts reuse the cached redacted transcript, never raw Write/Edit bodies.
Mixed Write/Edit schemas now redact unknown payload keys even alongside a
standard body key; malformed object-valued paths are redacted too. Literal
substring hits, including Chinese phrases inside a longer token, get excerpts.
Exact expansion, lineage filtering and the 50 KB response cap are unchanged. -
Local checkpoint carry is source-preserving. Index checkpoints retain rooted
paths, result/call addresses, diagnostics, rules, revocations and omission notices
across rounds.R: <root>declarations use explicitR:filereferences; shell
labels stay literal. Pool sizing includes carried state and the engine enforces
the overall checkpoint cap. Ledger carry preserves original P/F/C/K anchors and
excludes the superseded checkpoint from fresh extraction. -
Active rules survive whole. Index and ledger count/character limits apply
to optional goals. Active constraints/preferences and revocation tombstones
retain their kind and source seq; an insufficient mandatory budget fails
explicitly. Ledger's global cap sheds optional records with a recall notice. -
Index is available in settings and the rendered preview. Local format
shortcuts also clear superseded remote replay state. Recall cache validation
uses a seq map, unchanged logs reuse their index, and ranking reuses its lookup.
Defaults remain blackhole, ledgerBudget 656 and kBudget 0. -
A user message that fits the entry cap is quoted whole. Sentence picking
kept别在整手机了好吗?and dropped直接电脑上操作— the instruction was in
sentence two, soUrecorded a question nobody asked. The rule is now: if
nothing would be lost, keep the sentence selection (so
帮我把这个函数改快一点。先别改测试,也不要动公共接口。still renders as two
joined sentences); if the message fitsWHOLE_MESSAGE_CHARSand the selection
would drop a sentence with content, quote the whole message instead.
5-session user-turn coverage 31/37 → 35/37 for +36 tokens. Revocations
still take the long path:collectRequirementsfilters that kind out, so
folding one into a whole-message goal would have dropped it. -
user turnsis measured separator-insensitively. The census marker is the
first 40 characters of a user message and theUsection joins quoted
sentences with/, so a contiguousincludesreported an instruction as
lost when it was quoted verbatim one separator away — a yardstick that
systematically rewarded formats copying whole messages over formats quoting
their sentences.ledgermoves 5/37 → 6/37; every other implementation is
unaffected. -
Addressability is counted from row lines, not from any
#N. The unanchored
/#(\d+)/also matches theF #12 …diagnostic lines, so a result whose row
was elided counted as addressable whenever it had a diagnostic: fixed pools
reported 721/1,304 when only 677 rows were written, and the pool growth
looked like it bought 583 addresses instead of 627.INDEX_ROW_REFS
anchors to^#12;pi-briefkeeps the unanchored pattern because its
addresses are mid-line ((#3, #5)). -
An enumerated requirement now survives, not just the question above it.
parseRequirementTextkept the first sentence (the goal) plus sentences
containing a rule word, so这些都修复了吗?1. build_dsp_chain 从未把 chain 采样率 下发给各 stage。…48/96kHz 下 IR/FIR 卷积完全无声recorded only
这些都修复了吗?— five sentences of payload dropped by a question with no
content. A numbered sentence is a requirement by construction (the user
enumerated it, so it defines "done"); those are appended after the rule
sentences, so when the 160-character cap bites a prohibition is what survives
rather than item 3. Bullets are excluded on purpose:• Ran Get-ChildItem …is
how a transcript renders a tool call, and a message carrying an injected
context block pushed those in as if the user had listed them. -
The aggregate prints
n/a, not0/0, for a format with no address space.
dcpemits no#seqat all, whileledgerhas an address space it fills with
calls (0 of 1,304 results reachable). Printed in one column as0/0and
0/1,304, two different facts looked like one number, anddcpdisappeared
behind a plausible zero. -
A format with no address space can no longer be a "winner". The
per-session winner requires every diagnosis plus every result addressable;
pointed === totalis trivially true when both are 0, so a hypothetical format
that dropped every result would have qualified. Nowtotal > 0as well. -
A row says which call produced it:
#seq tool@call arg.recall #92
returns the output, and the command that produced it lives only on the
assistant message — so a checkpoint without the call seq lets the model read
every result back and still not know what was run. The readback sweep found
225 of 1,456 addresses in that state, which is also why the row format
changed rather than the readback alone. -
The index pools are sized to the region (
autoIndexOptions). The fixed
2048/768/256 values were calibrated on a 152-result session and do not hold on
a larger one: a 314-result TypeScript session elided 91 rows and kept
21 of 54 failure lines — the format's one promise, broken. Pools are
ceilings, not allocations, so raising them renders a byte-identical body
wherever the region already fit; the cost is only paid where rows would
otherwise be lost. 0 elided now on all six measured regions.
requirementCountgrows with the region too (8 → up to 32; a 30-turn fixture
went 8/30 → 30/30). -
Diagnostics are counted per
(seq, line)pair, not per distinct line.
failsOfde-duplicates within one result, but the renderer prints oneFline
per (result, line) — so the same text printed by two results costs twice. The
budget estimator counted it once and starved one line on a real region. -
renderExpandedMessagereserves the tool-call lines before filling prose.
Blocks were rendered in order against one byte budget, so a long thinking block
consumed it and the* bash <command>line after it never appeared: 225 of
1,456 addresses resolved to a readback that no longer said what was run. The
call lines are the operational half, so they get their bytes first and the
prose takes the remainder. -
Drilling into an address (
#N) renders 2,000 characters of arguments, not
the preview's 300. 222 of 1,004 command lines came back truncated: the
argument identified the call in preview but not in the readback that exists to
identify it. -
R <prefix>only accepts real paths. A command containing a slash was
eligible to elect the root, which producedR ADB="D:/AI/…— a shell
assignment as the "root" of the index, stripped off every row that started
with it. -
A row's argument comes from the raw command, and blank arguments fall
through.shortCommand(compressBash(cmd, 120))folded lines intoa; b; c
and cut at 120 characters, so a two-token slice could be an unterminated
fragment likeAPK="/data/app/~~…that appears nowhere in what was run. A
blank key argument (path: ""means the working directory) used to render an
empty row and hide a later field —fffindsends{path: "", pattern: …}and
lost its pattern. List-shaped arguments (ask_user_question→
questions: [{header, …}]) now yield their label instead of a bare tool name.
14 of 1,456 rows were argument-less; 6 remain, and they are all correct: a
lsof the working directory, an emptybash {}, and foureditcalls that
arrived without apath(flagged!, with the failure line saying so). -
Two new harnesses, because one session is not evidence.
tests/eval-sessions.mjsre-runs the matrix on five independent sessions
across four situations (TypeScript type-break, Rust build break,cargo test
triage, multi-file refactor, pip install failure);tests/recall-verify.mjs
puts every address back through the realrecallSession— resolves, same
message object, body present,Flines verbatim,@callresolves,@call
closes over thetoolCallId, c...