Skip to content

dsh-ab-market-research

Verified

dsh-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

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. offset pages 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 .md report and the .json snapshot share the same no-overwrite rule.