dsh-web-search-ddg
aooyoo
Zero-token DuckDuckGo search provider for the DeepSeek Harness (DSH) web seam — local headless browser, no API key, no m…
Rhymer-Lcy/dsh-plugin-loud-failure
DeepSeek Harness plugin that turns silent tool failures into loud ones: a tools/post-execute policy that matches warning signatures in successful tool output and blocks the result or attaches a notice
PROJECT TOPICS
PROJECT README
A DeepSeek Harness plugin that turns silent tool failures into loud ones.
A tool call can exit 0 and still have failed. pandoc drops a glyph and only warns; a pipe swallows a Python traceback; a ; chain hides command not found; NumPy prints a RuntimeWarning and hands back nan. The exit code says success, the model reads success, and the mistake travels downstream. This plugin is one tools/post-execute waterfall listener: it matches the text a tool returned against a rule table and, on a hit, either blocks the result (it becomes an isError result whose content leads with the explanation and keeps the original output) or attaches a notice that lands in the model's next request. It changes no tool and no loop, so it composes with every other post-execute policy and unmounts cleanly.
Every built-in rule comes from a failure that was observed to hide behind a successful exit code:
| Observed | What the model saw | What had happened |
|---|---|---|
pandoc ... --pdf-engine=xelatex prints Missing character: There is no ₂ in font ... |
a PDF was written, exit 0 | the subscript in SpO₂ was silently dropped from the PDF |
python script.py \| tail -n 20 |
the last 20 lines, exit 0 | a traceback scrolled by; tail supplied the exit status |
pandocc in.md -o out.pdf; ls -l out.pdf |
bash: pandocc: command not found followed by a listing, exit 0 |
nothing was built; ls supplied the exit status |
python calc.py prints RuntimeWarning: invalid value encountered in divide |
an array, exit 0 | the array contained nan |
a PowerShell 5.1 command run with 2>&1 |
NativeCommandError, $? false |
the program had exited 0; PowerShell wrapped its stderr |
a Windows console printing ��� |
text, exit 0 | GBK/UTF-8 code-page mismatch; the text was corrupted |
The harness already records everything the model sees and lets any plugin rewrite a tool result before the model sees it. This plugin uses exactly that seam to make the failure visible at the moment it happens, instead of three steps later.
flowchart LR
call["tool/call"] --> pre["tools/pre-execute"] --> exec["tools/execute"] --> post["tools/post-execute<br/>(this plugin)"] --> result["tool/result"]
post -->|"error rule matched"| block["block: isError result<br/>header + original output"]
post -->|"context rule matched"| notice["accept + additionalContexts<br/>plugin-sourced notice"]
post -->|"no match"| next["next()"]
tools/post-execute, the text blocks of the result (optionally also the JSON of a successful canonical value) are matched against the rules whose when and tools filters apply.action: error, the listener returns { kind: 'block' } with feedback that begins with a header naming every matched rule and its message, followed by the original content blocks unchanged. The registry turns that into an isError result: the canonical value is gone, so a Code Mode program cannot consume a poisoned value either.context matches) the listener delegates with next() and appends one UserMessage to the decision's additionalContexts. Its source is { kind: 'plugin', plugin: 'loud-failure', form: 'notice', summary }, so the Web UI shows a collapsed one-line row and the session log records exactly what the model was told.next(). Rules with action: off never run.Because the listener is registered through ctx.on, it is torn down with the plugin, and a config change under HMR replaces it atomically.
Requires DeepSeek Harness 0.1.0-rc.6 or later (@deepseek-ai/dsh-tools, @deepseek-ai/dsh-llm in the 0.1.x line) and Node.js 20+.
From a release tarball (no build step, no build authorization):
dsh plugin --profile web add https://github.com/Rhymer-Lcy/dsh-plugin-loud-failure/releases/download/v0.1.0/dsh-plugin-loud-failure-0.1.0.tgz
dsh --profile web --dump-config # shows a "# == dsh-plugin-loud-failure" layer
From GitHub at a pinned commit: a git install fetches source, so pnpm must be allowed to run this package's prepare script (it runs tsc). The first add fails and prints the exact key to allow (for a pinned commit the key includes the codeload URL); append it to the profile's pnpm-workspace.yaml, then rerun:
dsh plugin --profile web add github:Rhymer-Lcy/dsh-plugin-loud-failure#<commit-sha>
# $DSH_HOME/profiles/web/pnpm-workspace.yaml (key copied from pnpm's message)
allowBuilds:
"dsh-plugin-loud-failure@https://codeload.github.com/Rhymer-Lcy/dsh-plugin-loud-failure/tar.gz/<commit-sha>": true
Treat that authorization as what it is: code from this repository runs on your machine at install time, outside any agent sandbox. Pin the commit.
From a source checkout, without installing:
git clone https://github.com/Rhymer-Lcy/dsh-plugin-loud-failure.git
cd dsh-plugin-loud-failure && pnpm install && pnpm run build
# overlay.yml
- insert:
- id: loud-failure
name: /absolute/path/to/dsh-plugin-loud-failure/lib/index.js
dsh web --patch ./overlay.yml
Uninstall with dsh plugin --profile web remove dsh-plugin-loud-failure.
The bundle inserts one row, id: loud-failure, with the schema defaults restated. A patch replaces a row's whole config, so an override in your profile's cordis.patch.yml must restate every key it keeps.
| Key | Type | Default | Meaning |
|---|---|---|---|
builtinRules |
boolean | true |
Load the built-in rule table before user rules. |
rules |
Rule[] |
[] |
User rules. An id shared with a built-in rule replaces that rule in place; new ids append in order. |
shellTools |
string[] |
[bash, pwsh, job_output, terminal_read] |
Tools inspected by rules that omit tools. |
excerptChars |
natural | 240 |
Maximum characters of the matched excerpt shown to the model. |
scanValue |
boolean | false |
Also scan the JSON of a successful canonical value, for tools whose text projection hides stderr. |
A rule:
| Field | Type | Meaning |
|---|---|---|
id |
string, required | Stable identifier. |
pattern |
string, required | JavaScript regular-expression source. |
flags |
string | Any of i, m, s, u. g and y are rejected because they carry state between calls. |
tools |
string[] |
Tools the rule applies to. Omitted: shellTools. ['*']: every tool. |
when |
success | error | any |
Which outcomes the rule inspects. Default success, so a result that already failed is not decorated twice. |
action |
error | context | off, required |
error blocks, context attaches a notice, off disables the rule. |
message |
string, required | What the signature means and what the model should do next. It is shown to the model verbatim. |
Disable one built-in rule and add one of your own:
# $DSH_HOME/profiles/web/cordis.patch.yml
- id: loud-failure
config:
builtinRules: true
rules:
- id: no-such-file-with-success
pattern: unused
action: off
message: unused
- id: cuda-oom
pattern: 'CUDA out of memory|torch\.OutOfMemoryError'
tools: [bash, pwsh, job_output]
action: error
message: 'The GPU ran out of memory; whatever this run was supposed to produce is incomplete. Reduce the batch size or free VRAM, then rerun.'
shellTools: [bash, pwsh, job_output, terminal_read]
excerptChars: 240
scanValue: false
All built-in rules use when: success and apply to shellTools.
| id | action | Fires on |
|---|---|---|
pandoc-missing-character |
error | Missing character: There is no (xelatex/lualatex through pandoc; the PDF is missing glyphs) |
python-traceback-with-success |
error | a line starting with Traceback (most recent call last): in a result that reported success |
shell-command-not-found |
error | bash: [line N:] name: command not found and sh: N: name: not found lines |
windows-command-not-recognized |
error | is not recognized as an internal or external command / as the name of a cmdlet |
fatal-signal-with-success |
error | Segmentation fault, Bus error, or Aborted with (core dumped), or a bare Segmentation fault line |
numpy-runtime-warning |
context | RuntimeWarning: invalid value encountered / divide by zero encountered / overflow encountered |
powershell-native-command-error |
context | NativeCommandError |
latex-undefined-references |
context | LaTeX Warning: There were undefined references / Citation ... undefined / Reference ... undefined |
latex-rerun-needed |
context | Rerun to get cross-references right, Rerun to get outlines right, rerunfilecheck Warning |
pandoc-could-not-fetch-resource |
context | [WARNING] Could not fetch resource |
no-such-file-with-success |
context | No such file or directory |
permission-denied-with-success |
context | Permission denied |
unicode-replacement-character |
context | any U+FFFD replacement character in the output |
The error rules are anchored so that a mention of the phrase does not fire (grep -r "command not found" logs/ is not a hit); the context rules are deliberately broader because a notice is cheap and the model keeps the original result.
A blocked result, exactly as rendered (the second content block is the untouched original output):
[loud-failure] Tool "bash" reported success, but its output matched a rule (pandoc-missing-character) that indicates a silent failure. dsh-plugin-loud-failure marked this result as an error; do not treat the call as successful.
- pandoc-missing-character (error): pandoc/xelatex dropped one or more glyphs; the produced PDF is missing characters even though the exit code was 0. Rewrite the character (for example write SpO2 instead of a subscript digit) or switch to a font that has it, then rebuild and re-check for this warning.
excerpt: "pandoc thesis.md -o thesis.pdf --pdf-engine=xelatex\n[WARNING] Missing character: There is no ₂ in font Microsoft YaHei/OT!\n[exit code: 0]"
The original tool output follows unchanged.
A notice attached to a successful result (the result itself is unchanged; this arrives as a user-role message with source {"kind":"plugin","plugin":"loud-failure","form":"notice","summary":"loud-failure: bash output matched numpy-runtime-warning"}):
[loud-failure] Notice for tool "bash": its output matched a rule (numpy-runtime-warning) that often means a silent failure. Verify before relying on this result.
- numpy-runtime-warning (context): NumPy raised a RuntimeWarning about NaN, division by zero, or overflow. Downstream numbers may be NaN or inf while the exit code stays 0. Check the affected arrays before using the result.
excerpt: "python calc.py\ncalc.py:12: RuntimeWarning: invalid value encountered in divide\n[0.5 nan 0.25]"
pnpm run check runs typecheck, tests, build, and a pack dry-run; CI runs it on Ubuntu and Windows with Node 20 and 22.
tests/rules.test.ts covers merging, compilation failures (each with its message), filters, statelessness, excerpt bounds, and every built-in rule against a real signature plus the negative cases (clean output, non-shell tools, already-failed results, substring mentions).tests/plugin.test.ts mounts the plugin next to the real ToolRuntime and drives tool calls through the complete pipeline: block with original output preserved, notice with the plugin source, precedence of error over context, tool and when filters, built-in override, scanValue, composition with a downstream tools/post-execute listener, clean unmount, and fail-loud on a bad rule table.@deepseek-ai/dsh@0.1.0-rc.6 on Windows: dsh plugin add from a checkout, from the v0.1.0 release tarball URL, and from github:...#<sha> (with the allowBuilds key) all compose, and --dump-config shows the layer; booting dsh --profile web with an overlay that injects an invalid rule fails at boot with RuleConfigError: rule "bad": invalid pattern raised from the installed plugin's apply, and booting with the shipped config serves the Web UI.tools/post-execute, not tools/result. tools/result only observes the frozen outcome; the point here is to change what the model sees, which is exactly what post-execute is for (replace content, block with feedback, attach context).error is for signatures that mean the work did not happen; context is for signatures that mean "check before you trust". Anything softer than that is not worth a token.Only when a rule matches. For an error match: the tool result becomes isError: true and its content is one text block rendered as in What the model sees, followed by the original content blocks. For a context-only match: the tool result is unchanged and one user-role notice message with source { kind: 'plugin', plugin: 'loud-failure', form: 'notice', summary } is appended after the recorded tool results of that batch. Rule messages are configuration and appear verbatim; the excerpt is bounded by excerptChars.
Conditional. Zero when nothing matches. On a match: the header plus, per matched rule, its message and up to excerptChars characters of excerpt. A block adds only that header; the original blocks that follow are the same bytes the model would have received anyway.
Append-only. The plugin contributes no system-prompt section and never rewrites earlier history; a block changes the content of the current tool result before it is first recorded, and a notice is appended after that batch's tool results. Neither invalidates any previously reusable prefix.
scanValue, the JSON of a successful value). A failure that leaves no trace in the output, such as a library returning a negative status that the script never prints, cannot be caught here; the pattern for those is to print the value and let a rule catch it.context rules are broad by design; disable one with action: off if it is noisy in your workload. A blocked result still carries the original output, so a false error costs the model one corrective step, not the information.run_code aggregate output. Code Mode sub-dispatches each pass through post-execute and are inspected individually; the outer run_code result (program logs and return value) is inspected as ordinary text when run_code is listed in shellTools, which it is not by default.pnpm install
pnpm run check # typecheck + test + build + pack dry-run
pnpm run test:watch
Source lives in src/ (rules.ts is pure and unit-tested; index.ts is the Cordis adapter); tests in tests/; lib/ is build output produced by tsc and by the prepare script on git installs.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。