dsh-ab-market-research
Verifieddsh-ab-market-research Β· v0.2.1 Β· MIT Β· Web UI
An out-of-tree DeepSeek Harness plugin: the trade_search tool (multi-round aspect-broadening query planning, publisher-tier ranking, source diversity, JSON snapshot) and its browser card in one package.
Install
dsh plugin add dsh-ab-market-research Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Readme
description: "The out-of-tree trade_search tool: plans multi-round aspect-broadening web queries, ranks sources by publisher tier, diversifies by domain, renders a Markdown report, and writes a JSON snapshot, for maintainers of this machine's dsh profiles." kind: "package-reference"
@deepseek-ai/dsh-ab-market-research
Summary
dsh-ab-market-research gives this machine's dsh profiles one way to gather
broad, precise, credible trade / market / supplier evidence from the open web. A
single trade_search call expands one query into aspect coverage β official
site, market report and size, suppliers and manufacturers, price, export/import
and customs, regulation and standards, and news β runs the rounds through
ctx.web, ranks and deduplicates the candidates by how authoritative their
publisher is, keeps searching the refinement rounds until enough valid
candidates exist, retrieves a window of the strongest sources, and writes a JSON
snapshot and a Markdown report of the results into the workspace when asked.
It registers the trade_search wire name on ctx.tools and the
tool:trade_search routing section on ctx.systemPrompt. The recorded value
stays small β the planned queries, the ranked source window with the two levels
that continue it, the preferred tiers, the fetch facts, the failures, the
snapshot path, and the report path β while the page text lives in the snapshot
file. The ranked roster is the canonical answer and is rendered as a Markdown
report for the model; when asked, the same roster is written to the workspace as
a .md file beside the JSON snapshot. A failing query or page is recorded and
the call continues; a call whose every search failed throws instead, because it
has no evidence to report.
Table of Contents
- Configuration
- Writing under the session's sandbox policy
- Guards and windows
- The search method
- Commands
- Model Experience
- Evidence
- Runtime invariants
- Known Limitations and Deferred Work
Configuration
| Field | Kind | Meaning |
|---|---|---|
maxQueries |
guard | Most distinct queries one call may plan; a larger plan fails the call instead of being cut |
maxTopicChars |
guard | Longest accepted query, in characters |
maxCandidates |
guard | Most ranked candidates one call's search stage may hold |
maxSourcesPerCall |
guard | Most sources one call may retrieve; a call's limit cannot exceed it |
domainCap |
guard | Most sources one domain may contribute to a call's ranked candidates |
fetchConcurrency |
guard | Searches and retrievals running at once |
requestTimeoutMs |
guard | Milliseconds one search or one retrieval may take |
callTimeoutMs |
guard | Milliseconds the whole call may take, every search and retrieval included |
fileNameMaxChars |
guard | Longest snapshot file name stem, before the extension |
maxMetaBytes |
guard | Byte ceiling on the card payload persisted beside the session log |
windowSources |
window | Sources retrieved when a call names no limit (default 8) |
resultsPerQuery |
window | Sources requested from each query (default 8) |
sourceBodyChars |
window | Characters of one retrieved page the snapshot carries (default 6000) |
excerptChars |
window | Characters of opening page text the recorded value carries (default 400) |
minResults |
window | Valid candidates at which the call stops searching refinement rounds early (default 4) |
saveResults |
window | Whether a call writes its JSON snapshot by default when the call does not say (default false) |
saveReport |
window | Whether a call writes its Markdown report by default when the call does not say (default false) |
defaultLanguage |
window | Language to search in when the call names auto; auto also runs the other language (default auto) |
outputDir |
destination | Directory the snapshot is written to; a relative value resolves against the session workspace (default trade-search) |
Writing under the session's sandbox policy
A deployment that mounts @deepseek-ai/dsh-fs-sandbox fences every write by a
per-call sandbox policy, and that policy β not the backend's own default β is the
only thing naming the workspace the calling session runs in. The snapshot write
resolves ctx.sandboxPolicy for the call and stamps it onto the write, so the
file lands inside the session's own workspace. A relative outputDir resolves
against that same root; with no workspace named, the configured value passes
through unchanged so the mounted filesystem applies its own default rather than
this layer reading the directory the server was launched from.
src/sandbox.ts owns the seam. MutationPolicy resolves the policy once at load
and refuses a composition that mounts a confining backend with no policy service,
which cannot be caught at load because that backend declares the service as its own
injection and is never constructed without it. saveText is the one path the
snapshot is written through, and it restates a refusal with the FS_SANDBOX_DENIED
code the backend raised.
Guards and windows
A guard is what a deployment is willing to carry: a physical, cost, or safety limit
a caller cannot raise. Every guard is .required(), because a hidden default would
decide for the deployment how much it agreed to carry and how long a turn may be
held. A window says how much a caller sees and carries a default.
The two per-call budgets are deliberately separate. requestTimeoutMs bounds one
search or one retrieval, so a single slow backend cannot hold the call;
callTimeoutMs bounds the whole call, so a long list of merely slow requests
cannot either. A call that exhausts its budget records the affected stages as
failures and returns the evidence it already has.
The search method
| Stage | What it does |
|---|---|
| Plan | One broadening query per aspect in the primary language (official site, market report, suppliers, price, customs, regulation, news), then the same aspects in the other language when the call asks for breadth; refinement rounds chase year-qualified reports, data, and the tier-specific phrasing the call preferred. A plan that exceeds maxQueries is capped, not silently narrowed. |
| Search | Every planned query runs through ctx.web at fetchConcurrency, bounded per request and per call. |
| Rank | Candidates are canonicalized (fragment, campaign parameters, www., trailing slash, default port) so two links to one page collapse into the better-observed of the two. Survivors are ordered by publisher tier (official β primary β industry β academic β media β vendor β unknown), then freshness when the call names recency, then provider position, then date; a preferred tier earns an extra boost, and domainCap is applied last, while walking that order. |
| Continue | The call keeps searching the refinement rounds until minResults valid candidates exist, so a well-covered topic never pays for the rounds a thin one needs. |
| Retrieve | A window of the ranked candidates is fetched; a non-2xx response or a thrown retrieval is recorded and the rest continue. |
| Write | The snapshot is a structured JSON document β the queries, the ranked, de-duplicated sources with their tier and fetch facts, the failures, and the continuation β so a reader can reopen the exact result this call returned. The same roster is rendered as a Markdown report for the model and, when asked, written as a .md file beside the snapshot; the report is the human-readable roster the browser card redraws from. |
The tier rules read the host alone. They are a heuristic, not an authority: a host
matching none of them is unknown rather than guessed at. For a trade / market
search the tiers weight governments and regulators first, then exchanges and
company filings, then industry associations and academic research, then reputable
media, then commerce and aggregator platforms.
Commands
This package is npm-publishable (see package.json): no private flag, real
semver ranges for every @deepseek-ai/* dependency (never a link: to the
checkout), and the publishConfig / repository / bugs / homepage /
license / files / engines / keywords a published package owes. The build
is self-contained β tsconfig.json has no extends into the checkout and
tsdown.config.ts externalizes every @deepseek-ai/* import, so the published
dependencies resolve at load time.
npm run build # tsc -p tsconfig.json && tsdown
npm run typecheck # tsc -p tsconfig.json --noEmit
npm run test # node --test (build first; runs against lib/)
npm run lint # publint && attw --pack . --profile esm-only
npm run format # prettier --write "src/**/*.{ts,tsx,mjs}" "tests/**/*.mjs" "*.{json,yml,md}"
npm run verify # typecheck && build && test && lint
npm run release # npm publish --access public --no-git-checks (runs verify via prepublishOnly)
npm run test runs against lib/, so build first. npm run verify is the
single gate a publish runs through (prepublishOnly), so a clean verify is a
publishable package.
Model Experience
System-prompt section
What the model sees
tool:trade_search at order 2990, after the built-in tool band. The text is empty
wherever trade_search is not visible in that scope, so a restricted composition is
never told to call a hidden tool.
Token effect
Three sentences, constant.
KV Cache effect
None; the section is a function of tool visibility, not of the conversation.
Tool schema
What the model sees
The query, region, language, recency, sourceTypes, limit, offset, save,
and report parameters. Every guard is a deployment setting rather than a model argument,
so the model cannot raise what a deployment agreed to carry; limit only lowers it.
Token effect
Roughly constant.
KV Cache effect
None; the schema does not vary with the conversation.
Tool-call history and result
What the model sees
The call, then the query, the region, the language and freshness, the preferred
tiers, the rounds and the queries, the source window with its two levels and its
offset, the snapshot path, the report path, the ranked sources with their tier and
fetch state, the continuation when candidates remain, and the failures. The result is
a Markdown report, so the model keeps the roster in the transcript without the page
text; the model reads the snapshot file when it needs the evidence.
Token effect
Proportional to the window size and the excerpt ceiling β never to the number of candidates the search returned.
KV Cache effect
None on its own; the result enters the transcript on the following turn.
Evidence
| File | Proves |
|---|---|
| tests/query.test.mjs | Query planning: language selection, cross-language rounds, aspect coverage, refinement, and the query budget |
| tests/sources.test.mjs | URL identity, publisher tiers, ranking, freshness preference, tier boost, and the domain cap |
| tests/store.test.mjs | The snapshot and report file names, no-overwrite numbering, and the JSON / Markdown writes through the fence |
| tests/search.test.mjs | Orchestration, ranking, windowing and continuation, every ceiling, failure paths, and the snapshot write |
| tests/index.test.mjs | The exported surface, the required guards, the defaulted windows, and the pending call |
| tests/parse.test.mjs | The card payload and the rendered-result fallback for a call dispatched inside a code program |
| tests/load-path.test.mjs | The module form through the real Loader: no default export, inject kept |
| tests/loader-composition.test.mjs | A real cordis.yml boots; the guard enforcement, the seam, the section, the persisted projection, and the snapshot are observed |
| tests/hmr-safety.test.mjs | The registrations leave with their contributing fiber |
| tests/conventions.test.mjs | Source conventions, which no repository gate covers for an out-of-tree package |
Runtime invariants
No ./invariant companion is published. The only registered contribution is a ctx.tools definition, whose presence and disposal the tools registry already owns and observes, so no package-owned relation can diverge independently of it.
Known Limitations and Deferred Work
- Tier classification reads the host, not the page. A government statistics portal on a news domain, or a vendor publishing on its own corporate domain, is classified by its hostname's shape. The tier is a ranking hint, and the card prints it beside each source so a reader can weigh it.
- Page text is what the fetch provider decodes. A page whose body is built by client script is recorded as its shell; the tool does not run a browser.
- A continuation re-runs the searches.
offsetpages through the ranked candidates, and the plan is a pure function of the call's arguments, so a second call repeats the same searches to reach the same ranking. The snapshot the first call wrote is the cheaper way to reach the rest of the evidence. - The tool requires a mounted web capability and a mounted filesystem. Without a fetch provider that can reach the sources, retrieval failures are recorded and the snapshot carries only the source roster.
- Neither the snapshot nor the report is ever overwritten. A repeated query
produces a numbered file rather than replacing the earlier evidence; the
.mdreport and the.jsonsnapshot share the same no-overwrite rule.