Skip to content

feat(dsh-weknora): official DeepSeek Harness plugin for WeKnora retrieval - #60

Closed
lyingbug wants to merge 3 commits into
mainfrom
cursor/dsh-weknora-plugin-46b1
Closed

lyingbug wants to merge 3 commits into
mainfrom
cursor/dsh-weknora-plugin-46b1

Conversation

@lyingbug

Copy link
Copy Markdown
Owner

Description

Adds packages/dsh-weknora, the official DeepSeek Harness (dsh) plugin for WeKnora, plus its tests, CI and documentation.

dsh ships no retrieval, embedding or knowledge-base capability of its own — its search tools read the workspace (grep, glob) or the internet (web_search), and the only documented pattern for external knowledge is "attach an MCP server". This plugin makes WeKnora a first-class capability of the harness instead, with tool schemas, prompt budget and citation rendering under our control.

Four read-only tools are registered on ctx.tools:

Tool WeKnora endpoint What the model gets
weknora_search POST /knowledge-search Ranked passages verbatim, each with knowledge_id, score, chunk index
weknora_read_document GET /chunks/:knowledge_id One document's passages reassembled in order, with paging
weknora_ask POST /sessions + POST /knowledge-chat/:id or POST /agent-chat/:id WeKnora's own answer, its citations, the server-side tools it used, a resumable session_id
weknora_list_knowledge_bases GET /knowledge-bases Knowledge base names and ids, so the agent can scope its own search

Design decisions worth reviewing:

  • No runtime dependencies. The harness contract is declared structurally in src/harness.ts and ctx.tools.register() receives a plain object, so the package pins no @deepseek-ai/* version. dsh is in developer preview and states breaking changes will happen; this keeps us off its release treadmill.
  • Read-only by construction. No ingestion, no chunk edits, no deletion. Three of the four tools work with an API key scoped to retrieve; weknora_ask additionally needs chat.
  • Direct REST rather than wrapping our MCP server. dsh's MCP bridge cannot filter or rename tools (all 32 MCP tools would enter every prompt) and does not bridge elicitation, so tool_approval_required / mcp_oauth_required would be lost. A native plugin also lets us clip passages (maxChunkChars) and tell the model when we did.
  • Configuration fails at load, listing every violation, instead of failing inside the first tool call. Env-driven defaults ship in cordis.patch.yml; a profile patch can override the row, including toolPrefix so two deployments can be mounted side by side.

Distribution follows dsh's ecosystem model: DeepSeek does not accept external PRs and asks plugin authors to publish their own package and tag the repository dsh-plugin. The package therefore lives here and publishes to npm as dsh-weknora (name confirmed available) on a dsh-weknora-v* tag, mirroring how mcp-server/ publishes to PyPI.

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation update
  • 🎨 Refactor
  • ⚡ Performance improvement
  • 🧪 Test
  • 🔧 Configuration / Build / CI

Related Issue

None.

Testing

Three layers, all run in this environment.

Unit + contract (35 tests, node --test, no network beyond loopback) — run against the built artifact with a mock WeKnora server whose routes, envelopes and SSE events are derived from the real Go types. Includes a validator for the JSON Schema subset the harness registry enforces, so a canonical value that drifts from its declared output.schema fails locally rather than inside dsh.

$ npm ci && npm run typecheck && npm run build && node --test "test/*.test.mjs"
# tests 35
# pass 35
# fail 0

Two-sided API contract — test/fixtures/api-contract.json records every WeKnora call the plugin makes. test/contract.test.mjs asserts the plugin still emits exactly those calls; contract/contract_test.go decodes those bodies into the real handler structs (session.SearchKnowledgeRequest, session.CreateKnowledgeQARequest, session.CreateSessionRequest, types.Pagination) with DisallowUnknownFields, and asserts the response fields and SSE event names the plugin reads are still served by types.SearchResult, types.Chunk and types.StreamResponse. A rename on either side now fails CI instead of a user's agent.

$ go vet ./packages/dsh-weknora/contract/ && go test ./packages/dsh-weknora/contract/
ok  	github.com/Tencent/WeKnora/packages/dsh-weknora/contract	0.437s

End-to-end inside a real dsh install — test/e2e/run-in-dsh.mjs installs a clean @deepseek-ai/dsh@0.1.0-rc.6 from npm, runs dsh plugin --profile headless add, and boots the harness against the mock WeKnora and a deterministic OpenAI-compatible model. It asserts the profile layer list gained the bundle, --dump-config mounts the row, all four tools appear in the model's tool schemas, the tools reach WeKnora carrying the API key, the retrieved passages arrive at the model verbatim, and the answer dsh prints is grounded in them. Two scenarios run: the shipped env-driven defaults, and a profile override that renames the tool prefix to kb.

# profile bundles: ["@deepseek-ai/dsh-base","@deepseek-ai/dsh-headless","dsh-weknora"]
=== A · shipped bundle defaults from env ===
# tools offered to the model: weknora_ask, weknora_list_knowledge_bases, weknora_read_document, weknora_search
# WeKnora received: /api/v1/knowledge-search, /api/v1/chunks/doc-retrieval-pipeline
=== B · profile override with a renamed tool prefix ===
# tools offered to the model: kb_ask, kb_list_knowledge_bases, kb_read_document, kb_search
# ALL SCENARIOS PASSED

Manual check in the dsh Web UI — installed the plugin into a web profile and asked a Chinese question through the real UI; the agent called weknora_search and weknora_read_document and answered from the retrieved passages, with no approval prompt needed (the tools touch neither filesystem nor shell).

Note for reviewers reproducing this: dsh's session persistence needs a Node build with zstd (≥ 22.15 or ≥ 24); on Node 22.14 the harness fails to boot with createZstdDecompress missing. The plugin itself runs on Node ≥ 20.11. A real WeKnora backend was not exercised here because ingestion needs an embedding-model credential this environment does not have — hence the Go contract test, which pins the wire format to the real server types.

Checklist

  • git diff --check origin/main...HEAD passes
  • Changed source files are formatted (gofmt -l clean, tsc --noEmit clean)
  • Targeted tests for the changed packages/components pass
  • Diff-scoped lint passes where applicable
  • Full-repository checks were run, or any unrelated/environment-dependent failures are documented above
  • Self-reviewed the code
  • Added/updated tests covering the change
  • Updated related documentation (package README.md / README_CN.md, plus a section in the four top-level READMEs)
  • Breaking changes are clearly called out in the description above

Follow-ups deliberately left out of this PR: an Integrations Center tab for the plugin (frontend/src/config/integrations.ts + i18n), and configuring the NPM_TOKEN secret / npm environment the publish job needs.

Screenshots / Recordings

The dsh Web UI calling the plugin's tools end to end:

dsh_web_ui_calling_weknora_tools.mp4

Tool cards and the grounded answer:

dsh Web UI showing weknora_search and weknora_read_document tool calls and the final answer

The expanded weknora_search result, with scores and reusable knowledge_ids:

Expanded weknora_search tool result showing retrieved passages, scores and knowledge_id

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

lyingbug and others added 3 commits August 13, 2026 16:45
… tools

Adds packages/dsh-weknora, an out-of-tree dsh bundle that registers four
read-only tools on ctx.tools: weknora_search (hybrid retrieval, source
passages verbatim), weknora_read_document (a document's chunks reassembled
in order), weknora_ask (WeKnora's own answer over the RAG or ReAct
pipeline) and weknora_list_knowledge_bases.

The package has no runtime dependencies: it declares the harness contract
structurally in src/harness.ts and hands ctx.tools.register() a plain
object, so it pins no harness package version while dsh is in developer
preview. Configuration is validated at plugin load, so a bad row fails the
boot with every violation named instead of failing inside a tool call.

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
Three layers:
- unit tests over the built artifact, including a validator for the JSON
  Schema subset the harness registry enforces, so a canonical value that
  drifts from its declared output schema fails here rather than in dsh;
- a two-sided API contract: test/fixtures/api-contract.json records every
  WeKnora call the plugin makes, contract/contract_test.go decodes those
  bodies into the real handler request structs with DisallowUnknownFields
  and asserts the response fields and SSE event names it reads still exist;
- test/e2e/run-in-dsh.mjs installs the package into a throwaway dsh profile
  with 'dsh plugin add' and boots the headless profile against a mock
  WeKnora and a deterministic OpenAI-compatible model, asserting the agent
  loop called the tools and answered from what they returned.

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
Adds the plugin section to the four top-level READMEs and a workflow that
typechecks, tests, packs, and runs the in-harness end-to-end check against
a pinned dsh release, with a weekly advisory run against dsh@latest to
notice a harness breaking change early. Publishing to npm is gated on a
dsh-weknora-v* tag.

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
@lyingbug
lyingbug marked this pull request as ready for review August 13, 2026 17:01
@lyingbug lyingbug closed this Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant