Review found five real defects in the configuration-source work, all confirmed against the code rather than argued: 1. The note claimed --config outranks settings.yaml. It does not: the settings seam registers a plugin's cordis entry config as the `base` layer and the user section layers over it, and the seam cannot tell a shipped value from a --config one. The note now states shipped reality and names --config-replace as the lever for a deployment that must win. Separately, a literal `apiKey` in settings outranked both the environment and .credentials.yaml — the field is removed, so configuration carries a reference and nothing else. 2. DEEPSEEK_SEARCH_BASE_URL was functionally deleted: the shipped inline went away without the provider learning to read it. It now resolves from the environment snapshot, as the README always claimed. 3. The bootstrap deny list missed the interpreter start-up hooks. BASH_ENV is the sharpest: `bash -c` sources it on every bash tool call, so a project .env could run a file of its choosing before every command. The list now covers BASH_ENV and its per-language siblings, the Git hook commands, and the remaining preload and CA variables, organised by what a variable does rather than which runtime owns it. 4. YAML parse errors quoted the offending source line — which in a credentials document is the secret — into boot stderr and the watcher's logger. Only the error code and position are reported now, in credentials-local and settings-local alike, pinned by a test that asserts the secret is absent. 5. 0600 governed only files the harness wrote. A hand-created 0644 document was read normally. POSIX now checks the mode before reading contents, at boot and on every reload; Windows has no mode to inspect and is skipped rather than faked. The project a session is launched in is trusted by default, with no prompt and no stored trust record: it may supply its own endpoint, ordinary variables, and a key ranked below the managed store. Trust stops at the harness itself — a discovered file still cannot set DSH_PERMISSION_MODE, PATH, BASH_ENV, or the rest, because those take effect with no user action, before any turn, outside the permission policy and the sandbox.
@deepseek-ai/dsh-web-search-perplexity
English | 中文
A Perplexity-backed WebSearchProvider for the harness web capability seam (ctx.web). It calls Perplexity's OpenAI-compatible POST /chat/completions endpoint and maps the generated answer plus citations into the seam's normalized WebSearchResult.
This is an implementation package: it registers a provider into ctx.web, it does not own the key and it does not register a model-facing tool. Like @deepseek-ai/dsh-llm-deepseek, it is a function/namespace plugin (inject: ['web']). The OpenAI-compatible wire shape is a provider-private detail — it does not make this provider depend on ctx.llm.
Config
| Key | Default | Meaning |
|---|---|---|
apiKey |
$PERPLEXITY_API_KEY |
Perplexity API key. Empty/absent makes the provider unavailable. |
baseURL |
https://api.perplexity.ai |
Endpoint base; /chat/completions is appended. An unparseable value makes the provider unavailable. |
model |
sonar |
Search model name. |
maxTokens |
1024 |
Upper bound on generated answer tokens (max_tokens). Must be a positive integer. |
searchRecency |
(unset) | Recency window sent as search_recency_filter: day, week, month, or year. Unset sends no filter. |
- id: web-search-perplexity
name: '@deepseek-ai/dsh-web-search-perplexity'
config:
apiKey: !!js process.env.PERPLEXITY_API_KEY
Mapping
content ← choices[0].message.content (the generated answer). sources[] prefers the structured search_results[] (url, title, snippet, publishedAt ← date), falling back to the URL-only citations[] array only when search_results is absent — those sources carry just a url, which is why title/snippet/publishedAt are optional on the seam. Provider failures surface as WebError WEB_PROVIDER_ERROR; an aborted request surfaces as WEB_ABORTED. HTTP redirects are rejected before the Location target is contacted and surface as WEB_PROVIDER_ERROR. Perplexity has no result-count control, so maxResults is enforced by the seam (truncating sources[] and setting truncated).
Model Experience
Auxiliary Perplexity request
What the model sees
A separate Perplexity model receives <query> verbatim as its sole user message through the chat-completions endpoint. This request is not part of the conversation model's context.
Token effect
Separate provider tokens are incurred per search; maxTokens caps the generated answer.
KV Cache effect
Independent of the conversation request cache. An identical query under the same model route may reuse provider cache; a changed query or route establishes a different prefix.
Conversation tool result, indirectly
What the model sees
Through dsh-tool-web, the conversation model sees the generated answer plus structured result metadata or URL-only citations. This provider's exact failures are Perplexity search aborted, Perplexity search request failed: <error>, and Perplexity returned an unprocessable response body: <error>; HTTP failures preserve the provider message. The consumer owns the error wrapper.
Token effect
Zero direct conversation tokens from registration. Answer and source tokens are data-dependent, source count is seam-bounded, and the retained result or error is resent until compaction.
KV Cache effect
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Known Limitations and Deferred Work
- Citation-fallback sources are URL-only — when Perplexity omits structured
search_results[], sources carry notitle/snippet/publishedAt, so the tool renders bare hostname labels. - Over-returned sources still cost tokens and latency — with no result-count control on the wire,
maxResultsis enforced only post-hoc by seam truncation. - Only
model/maxTokens/searchRecencyare exposed — Perplexity's other search controls (domain filters,web_search_optionscontext size, images) wait on provider-neutral seam fields (seam Agent Note). - Abort classification is error-shape-based — only a
DOMExceptionnamedAbortErrormaps toWEB_ABORTED; an abort carrying a custom reason (e.g.dsh-timeout'sTimeoutReason) surfaces asWEB_PROVIDER_ERROR.