A Codex review pass on the draft caught four real gaps and two solid suggestions; all addressed except one pushed back on the merits: - hooks-claude/hooks-codex: stderrSummaryMaxChars was the one new knob with NO range validation — a negative/NaN cap would silently misbehave inside slice(). Both bridges now assert a positive integer at the TOP of apply() (before the config-file parse's early return, so a bad value fails the load loudly), with rejection tests. - tool-fs: the read caps count lines/chars/bytes, so positive-FINITE was too loose (a fractional readLimit would flow into windowing arithmetic and the schema description). All four now require a positive integer, matching tool-web's cap. - Doc drift the gates cannot catch: tool-web's README tools table still named WEB_SEARCH_MAX_RESULTS as the mechanism; compact-basic's README/module doc and the compaction-capability-seam RFC still described estimation as fixed char/4 rather than the charsPerToken default. - subagent-acp: the dispose graces were tested only at the startAcpRun level, so a regression that stopped threading plugin config into AcpRunSpec would have survived. A provider-path test now drives the trap-escalation scenario through ctx.subagents.start with small config graces and bounds dispose at 4s. Pushed back on: converting compact-basic's charsPerToken to a schemastery field. The package's whole config is deliberately hand-rolled (resolveConfig, every threshold REQUIRED with no default — a documented design posture); one schemastery field beside it would be incoherent. The knob is cordis.yml-reachable, defaulted, and validated, which is what the convention requires; migrating the package to schemastery wholesale is pre-existing config-surface hygiene out of this change's scope.
2.2 KiB
@deepseek-ai/dsh-tool-web
The model-facing web tool suite — web_search and web_fetch — over the web capability seam (ctx.web). It owns model-facing concerns only: tool names, JSON schemas, snake_case argument names, prompt sections, the result-count bound, result formatting, HTML→markdown presentation, and presentCall. All web access goes through ctx.web; this package never imports a concrete provider.
Each tool is registered independently; a product that wants only one disables the other via config ({ search: false } / { fetch: false }).
Tools
| Tool | Args | Behavior |
|---|---|---|
web_search |
query (string) |
Discovery. Returns an optional answer plus source URLs. max_results is not model-facing — the tool sets the bound (the searchMaxResults config, default 8) and passes it to the seam. |
web_fetch |
url (string), timeout_ms (number, optional) |
Retrieves a specific URL. HTML bodies are rendered to markdown-ish text; text bodies pass through. A non-2xx status is reported, not an error. |
Config
| Key | Default | Meaning |
|---|---|---|
search |
true |
Register web_search. |
fetch |
true |
Register web_fetch. |
searchMaxResults |
8 |
Upper bound on sources returned by one web_search call (the seam truncates a longer provider list and flags it). |
- id: tool-web
name: '@deepseek-ai/dsh-tool-web'
Stable registration
Tool registration follows product enablement, not backend availability. A tool stays visible even when its selected provider is missing, misconfigured, ambiguous, or temporarily unavailable; the seam resolves the provider at execution time and execution fails with a structured WebError (e.g. WEB_PROVIDER_UNAVAILABLE, WEB_PROVIDER_AMBIGUOUS), which ToolRegistry.execute() turns into an error tool result the model can read and hooks/UI can route on. This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. To remove a web tool entirely, disable it here in config.
The tool reads only the aggregated ctx.web.searchStatus() / fetchStatus() for diagnostics — never each provider's status() directly — so provider selection has one owner.