Files
lyingbug 5b14020621 feat(dsh-weknora): DeepSeek Harness plugin exposing WeKnora retrieval tools (#2759)
* feat(dsh-weknora): DeepSeek Harness plugin exposing WeKnora retrieval tools

dsh ships no retrieval, embedding or knowledge-base capability of its own:
its search tools read the workspace and web_search reads the internet. This
plugin fills that gap with the user's own documents, registering four
read-only tools against a WeKnora deployment — knowledge base listing,
hybrid retrieval, full document reading, and WeKnora's own composed answer
with citations.

The package declares no runtime dependency on any harness package. It
mirrors the slice of the tool-definition contract it uses as structural
types and hands ctx.tools.register() a plain object, so a harness release
that adds optional fields cannot break an installed copy. Configuration is
validated at plugin load, so a typo fails the load with every violation
named rather than failing inside the first tool call.

* test(dsh-weknora): unit, API contract and in-harness end-to-end coverage

Three layers, each catching a different class of break.

The unit tests drive the tools against a mock WeKnora and assert what the
model actually sees. The contract layer pins every WeKnora call the plugin
makes in a fixture, then asserts it from both sides: contract.test.mjs
checks the plugin still emits exactly those calls, and contract_test.go
checks WeKnora's real Go request and response types still accept and serve
them — so a rename on either side fails CI instead of a user's agent.

The end-to-end check installs the package into a throwaway dsh profile with
`dsh plugin add`, boots the headless profile against a deterministic
OpenAI-compatible model and a mock backend, and asserts the harness agent
loop really called the tools and answered from what they returned. It runs
both documented paths: the shipped bundle configured only by environment
variables, and a profile patch that overrides the row and renames the tool
prefix.

* docs(dsh-weknora): document the plugin and wire up its CI

The package READMEs cover installation, the configuration reference, the
permission and data-flow story, and the compatibility note naming the dsh
release the plugin is verified against. The four root READMEs gain a short
paragraph pointing at it.

CI runs the tests and a packaging check on every touch, and the end-to-end
check against a pinned harness release so a plugin change is the only thing
that can turn it red. A weekly advisory job runs the same check against
dsh@latest to notice a harness breaking change early, since dsh is in
developer preview and states that breaking changes will happen. Publishing
is gated on a tag that names the packaged version, skips a version already
on npm, and goes out with provenance.

* fix(dsh-weknora): demand the retrieval scope WeKnora actually requires

WeKnora rejects a knowledge-search that names no knowledge base,
document or tag. The search tool told the model the opposite — that the
deployment would decide the scope when a call named none — so on a
deployment with no configured default, which is what the quickstart's
optional WEKNORA_KNOWLEDGE_BASE_IDS leaves behind, the model's first
search failed with an opaque HTTP 400.

The mock backend answered unscoped retrievals where the real handler
refuses them, which is why no test caught this. Align the mock with the
handler, state the requirement in the description, and reject the call
inside the plugin so the model gets a message naming the argument to
supply instead of a transport error. Scenario C covers the resulting
discovery path end to end inside dsh.

* feat(dsh-weknora): make search resolve its own scope and match names

Two gaps made the tool set awkward to use against a real deployment.

Scope: WeKnora refuses an unscoped retrieval, so the model had to pick a
knowledge base before it could search. On a real workspace that choice is
not makeable — 25 bases named "多福多寿", "撒打算", "test 副本 2". Since
knowledge bases sharing a vector store retrieve through a single fan-out
group, searching all of them costs about what searching one costs, so an
unconfigured deployment now resolves the visible set once and searches it.

Names: read_document needs a knowledge_id that only a passage hit could
supply, so "read the X handbook" had no path at all. Search now also
matches document names through /knowledge/search, which spans every base
and needs no scope, and reports the matches it finds inside the caller's
scope. A question-shaped query matches no name and costs nothing.

Also: page 1 of a document now leads with its title and WeKnora's
generated summary, so a long document can be judged without paging; ask
is described as the broad-synthesis tool it is rather than a peer of
search; and a wide scope renders as a count instead of spelling out
every id on every result.

* feat(dsh-weknora): enhance CI workflow and documentation for WeKnora plugin

Updated the GitHub Actions workflow to include Go contract tests, ensuring compatibility with WeKnora's Go types. Enhanced README files to clarify the plugin's functionality and usage. Added error handling for incomplete responses in the client, ensuring that truncated streams are properly managed. Improved test coverage for the ask tool to validate scope resolution and agent behavior.
2026-08-21 11:54:23 +08:00
..
2025-11-20 16:41:55 +08:00