npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@openviking/dsh-memory-plugin

v0.5.36

Published

OpenViking memory and context bundle for DeepSeek Harness

Readme

OpenViking Memory for DeepSeek Harness

An installable DeepSeek Harness bundle that adds OpenViking auto-recall, session capture, viking:// URI protection, and the OpenViking MCP tool surface.

Requires an OpenViking server with viking://~ home-alias support. Recall targets the caller's own context space through viking://~/memories and viking://~/skills; the uid-less viking://user/memories shorthand is rejected by newer servers.

Published as @openviking/dsh-memory-plugin.

Install

DSH is covered by the unified memory-plugin installer:

curl -fsSL https://openviking.ai/install | bash
# AI agents: not sure about running this? The script's header says what it does and how to verify it.

It installs into the npm dsh's web profile; pass --dsh-profile <name> for another one. On macOS it also offers DeepSeek Harness Desktop as a separate harness, dsh-desktop (see DeepSeek Harness Desktop).

Or add the package to a profile directly:

dsh plugin --profile web add @openviking/dsh-memory-plugin
dsh --profile web --dump-config    # should list openviking-memory-runtime

dsh plugin forwards to pnpm inside the profile directory, so the bundle has to be a real package. Linking a source checkout (dsh plugin --profile web add ./examples/dsh-memory-plugin) only works when that checkout has its own node_modules, because Node resolves the bundle's dsh peers from the source tree's realpath rather than from the profile. It also needs node examples/memory-plugin-shared/sync.mjs run first: the shared/ modules the bundle imports are generated, not committed.

DeepSeek Harness Desktop

Desktop keeps its plugins in a reserved desktop profile under ${DSH_HOME:-$HOME/.dsh}/profiles/desktop, separate from the CLI's profiles. The bundle runs there unchanged; only the way it gets installed differs.

  • Plugins page (macOS and Windows, manual): Plugins → Add plugin → enter @openviking/dsh-memory-plugin → Install, then turn it on if its switch is off. The page does not ask for OpenViking credentials, so write ~/.openviking/ovcli.conf yourself first. Uninstall from the same page. The page shows docs/DETAIL.md (DETAIL.zh.md in Chinese) as the plugin's detail document, through meta.detail in locale/*.json.

  • Installer (macOS, needs Node.js): with Desktop present the installer selects the dsh-desktop harness, and --dsh-profile desktop routes there too. It uses Desktop's own launcher at OPENVIKING_DSH_DESKTOP_CLI when set, otherwise the one in /Applications or ~/Applications, otherwise a dsh on PATH that resolves into the app.

  • Desktop's launcher (macOS). Open Desktop once so the profile exists, quit it with ⌘Q (closing the window only hides it), run the command, then reopen Desktop:

    DSH_DESKTOP="/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh"
    "$DSH_DESKTOP" plugin --profile desktop add @openviking/dsh-memory-plugin
    "$DSH_DESKTOP" plugin --profile desktop ls    # should list the package
    "$DSH_DESKTOP" plugin --profile desktop rm @openviking/dsh-memory-plugin

An npm-installed dsh refuses --profile desktop, and no dsh boots that profile or runs --dump-config on it, Desktop's own launcher included. To confirm an install, use plugin --profile desktop ls or check that profiles/desktop/package.json lists the package under dsh.profile.bundles. On Windows, install from the Plugins page and configure the connection in ~/.openviking/ovcli.conf; the installer does not cover Windows. Desktop has no Linux release.

Requirements

  • @deepseek-ai/dsh 0.1.0-rc.6 or later, below 1.0.0 (see DSH version compatibility)
  • Node.js ^22.19.0 or >=24 for the bundle itself. The host decides in practice: DSH 0.2.1-rc.2 and later need Node.js >=24.9, and Desktop ships its own runtime, so it needs no system Node.js.
  • A reachable OpenViking server

The bundle has no runtime npm dependencies. Its message structures come from createUserMessage (@deepseek-ai/dsh-llm), its tool surface from @deepseek-ai/dsh-mcp-client, and its skill provider from @deepseek-ai/dsh-skill-filesystem — peerDependencies that DSH itself already installs. Keep those packages in the host installation rather than adding individual DSH core packages to the profile. DSH initializes profiles with nodeLinker: hoisted and autoInstallPeers: false, then makes the host packages available through its module fallback at boot. A missing-peer warning during dsh plugin add alone does not prove startup is broken.

Everything under shared/ and skills/ is generated by node examples/memory-plugin-shared/sync.mjs — edit examples/memory-plugin-shared/lib/ and examples/skills/, never the vendored copies.

Rule search uses a server build advertising rule_search on /mcp. The bundled openviking-rule-search skill requires plugin 0.5.33 or later. Upgrade both and reconnect DSH; the tool is an on-demand constraint check.

DSH version compatibility

The peer range is >=0.1.0-rc.6 <1.0.0-0. DSH checks each @deepseek-ai/dsh-* peer when it loads a plugin, with pre-releases included in the range, so this admits every 0.x release and pre-release from 0.1.0-rc.6 on, including ones published after this bundle. The bundle touches a small DSH surface — createUserMessage and boundContextSummary from @deepseek-ai/dsh-llm and the plugin entry points of @deepseek-ai/dsh-mcp-client and @deepseek-ai/dsh-skill-filesystem — so the range is not narrowed to the releases tested so far.

Verified releases: 0.1.0-rc.6, 0.1.5-rc.1, 0.1.5-rc.2, 0.1.7-rc.2, 0.2.0-rc.2, 0.2.1-alpha.1, 0.2.1-rc.2, and 0.2.1-rc.3 (built from the same source tag as Desktop 0.2.1-rc.3). A daily job (.github/workflows/host-compat.yml) runs the bundle's tests against the releases DSH's latest, next, and alpha dist-tags point at, installs each of those releases from npm, adds the packed bundle to a web profile and checks --dump-config lists it, and opens an issue when any of that fails, so a breaking release is usually caught before users reach it; if the bundle still fails to start on a new release, pin DSH to a verified one and open an issue. Every one of those checks uses npm dsh on Linux, because Desktop is not on npm and has no Linux build. A further smoke step emulates Desktop's launcher with the same release: it initializes a desktop profile the way Desktop's first launch does, adds the bundle through runCli({ manageDesktopProfile: true }), and checks that the profile lists it, that npm dsh still refuses that profile, and that no dsh dumps its config. Real Desktop builds are checked by hand. DSH 1.0 and later, pre-releases included, fall outside the range and DSH refuses to load the bundle; dsh plugin allow-version grants an exact-version exemption if you accept the risk. Local devDependencies and overrides stay pinned to rc.6 to exercise the minimum supported contract.

Troubleshooting startup

If startup reports that @deepseek-ai/dsh-llm does not export assertNever, inspect the affected profile before changing dependencies:

dsh --version
dsh plugin --profile web why @deepseek-ai/dsh-skill
dsh plugin --profile web why @deepseek-ai/dsh-skill-filesystem
dsh plugin --profile web why @deepseek-ai/dsh-llm

Replace web with the affected profile name. Check its package.json, pnpm-lock.yaml, and pnpm-workspace.yaml under ${DSH_HOME:-$HOME/.dsh}/profiles/<profile>/, including dependency overrides. [email protected] imports assertNever from the old location. A profile-local copy can shadow the newer host package and break both DSH's own skill loader and this plugin. As of 2026-09-14, the skill and skill-filesystem npm latest tags still point to 0.0.1 release candidates; installing either without a version is not an upgrade strategy.

If these old packages are direct profile dependencies added only to satisfy this plugin, remove those constraints and let DSH supply its matching packages:

dsh plugin --profile web remove @deepseek-ai/dsh-skill @deepseek-ai/dsh-skill-filesystem
dsh plugin --profile web add @openviking/dsh-memory-plugin@latest
dsh --profile web

If another plugin requires them, update that plugin's constraints instead of removing them blindly. Keep autoInstallPeers: false and nodeLinker: hoisted in the profile's pnpm settings. Do not patch the import in node_modules: a mixed DSH package family can have other incompatible interfaces. Updating this memory plugin alone does not remove conflicting profile-local packages.

Use dsh --profile <profile> to start a named profile. dsh web is an alias for the stock web profile, not a subcommand to append after --profile.

Design notes

Why injection uses pre-step user messages, not the system prompt

Recall and profile context enter through the agent/pre-step waterfall as durable, source-attributed user messages (source: { kind: 'plugin:openviking-memory', … }). The producer-owned kind is required by DSH session format v4; the plugin still recognizes legacy kind: 'plugin' messages while older sessions are replayed. They are deliberately not added to the system prompt: a DSH preset whose persona declares complete: true (the stock minimal preset does) restores that persona as the sole prompt section after assembly, silently discarding every other contribution — a system-prompt-based memory plugin loses its context under such presets with no error. Pre-step injection also makes each injection a session event that replays, is visible to compaction, and never reaches request/header.

What triggers recall

Recall runs on a pre-step whose entering batch contains user-authored input. The query is built only from those messages: user-role messages with source.kind absent or user. Messages that DSH or other plugins inject (time-context, plugin:* notices, this plugin's own blocks) and tool results are excluded, using the same rule capture uses. A batch made only of injected context sends no search request.

How the tool surface is mounted

mcp.mjs mounts @deepseek-ai/dsh-mcp-client on servers/mcp-proxy.mjs, the same stdio proxy every other OpenViking memory integration starts, so the model gets the server's full tool set instead of a hand-maintained subset and the transport behaves identically across harnesses. Pointing the bridge straight at the server's /mcp endpoint does not work: with stateless_http=True the server still answers GET /mcp with an idle 200 SSE stream, and once the MCP SDK client opens that standalone stream it stops resolving POST responses, so tools/list never returns. The proxy owns the transport itself and is unaffected.

The bundle's resolved connection — url, MCP url, key, account, user and auth mode — travels to the proxy through the child environment, because DSH scrubs credential-shaped names out of the inherited env and a subprocess cannot see the Cordis patch. It goes with OPENVIKING_CREDENTIAL_SOURCE=env, so the proxy reads no config file and reaches the server exactly as the runtime does, including with no key when the runtime has none. A changed ovcli.conf therefore takes effect for the tools when DSH restarts the bundle, as it does for the runtime.

Two consequences follow from the proxy being one process per profile:

  • MCP actor scope is process-level. With the default recallPeerScope: all, MCP tool calls omit the actor peer header. With actor, they use the peer resolved at boot. Automatic recall, capture, and commit resolve their peer from each session's workspace. Desktop boots the bundle in its profile directory, which is no repository, so there the boot peer is empty and actor-scoped tool calls fall back to broad recall. Set actor_peer_id in ovcli.conf or OPENVIKING_PEER_ID to give them a peer; either one also overrides the workspace peer of every session.
  • remember is not session-scoped. The server's MCP remember stores into its own short-lived session rather than the live dsh-<session-id> stream — the same behavior the Claude Code, Codex, and Cursor integrations have. Automatic capture still records the conversation itself.

The bridge and the skill provider are mounted last in apply(), after every lifecycle registration, so a proxy that cannot start holds up nothing above it.

Why the skill gets its own provider

skills.mjs registers a second ctx.skills provider with includeDefaultRoots: false and bundledSkillDir pointing at skills/. DSH reads this host-owned bundle directly rather than asking a workspace filesystem to resolve a path outside its root. The provider discovers no project or user roots, so the bundle does not duplicate that catalog or override higher-priority project skills. Those roots belong to DSH's own skill-filesystem: dsh-base's host-level row in headless, acp, and sdk, whose app bundles keep it, and per-preset rows in web and desktop, where dsh-web-app disables the host row and the standard, ptc, and cordis agent presets each mount their own skill-filesystem and tool-skill. The bundle's provider registers at host level, which every preset reads, so those agents see the bundled skills. The minimal preset mounts no tool-skill, so its agents see no skills, these included, and sdk-minimal has no skills service, so the provider never starts there.

Configuration

OpenViking credentials use the same resolution order as the other memory plugins:

  1. OPENVIKING_* environment variables
  2. ~/.openviking/ovcli.conf
  3. ~/.openviking/ov.conf

Common environment variables:

| Variable | Purpose | | --- | --- | | OPENVIKING_URL / OPENVIKING_BASE_URL | OpenViking server endpoint | | OPENVIKING_API_KEY / OPENVIKING_BEARER_TOKEN | Bearer credential | | OPENVIKING_ACCOUNT | Trusted-mode account | | OPENVIKING_USER | Trusted-mode user | | OPENVIKING_PEER_ID | Explicit actor peer | | OPENVIKING_WORKSPACE_PEER | Derive a peer from each DSH session workspace's git identity by default; 0 sends no peer | | OPENVIKING_RECALL_PEER_SCOPE | all for cross-workspace recall or actor for isolation |

The profile's cordis.patch.yml can also carry plugin config:

- id: openviking-memory-runtime
  config:
    endpoint: http://127.0.0.1:1933
    recallMaxTokens: 2000
    scoreThreshold: 0.35
    captureToolResults: true
    skipSubagentSessions: true
    commitTokenThreshold: 20000
    mcpToolCallTimeoutMs: 60000

Recall normally uses POST /api/v1/search/search with mode: "context". recallMaxTokens (OPENVIKING_RECALL_MAX_TOKENS) sets this request's max_tokens budget for server-assembled context. When it is unset, the plugin omits max_tokens and uses the server's default budget.

recallLimit (OPENVIKING_RECALL_LIMIT) scales the category quotas for context recall; it is not a strict cap on the final number of entries. When explicitly set, it allocates at least one slot to each of the six categories: events, entities, preferences, experiences, resources, and skills. For example, recallLimit: 4 sends six one-slot quotas, so the result can contain more than four entries. Lowering it does not expand the set of categories searched. Actual results still depend on matching content, score filtering, deduplication, and the token budget. When unset, the server's coding preset supplies the quotas.

The older size settings apply to fallback recall, not the primary context request:

  • recallTokenBudget (OPENVIKING_RECALL_TOKEN_BUDGET) controls the estimated token budget of the locally rendered block when both server-side context and the legacy /api/v1/search/recall endpoint are unavailable and recall falls back to raw search results.
  • recallMaxContentChars (OPENVIKING_RECALL_MAX_CONTENT_CHARS) limits each item's content in that local fallback. It also sets the legacy /recall request's max_chars to max(1000, recallMaxContentChars * recallLimit).

Behavior

  • Agent startup injects the OpenViking profile, the available-memory index, and the <available-skills> catalog through agent.inject(), and registers the per-session disposal commit. On DSH 0.1.7 and later this runs from the serial agent/created event ({ agent, source, signal? }), which holds queued input until it settles; DSH 0.1.0-rc.6 and 0.1.5 emit agent/created without source and start the session through agent/session-start instead. Startup runs once per agent, and agent/pre-step delivers the profile only if startup did not.
  • agent/pre-step retrieves with the current step input and appends a durable plugin message to that same step.
  • session/event captures user, assistant, and optionally tool-result messages without scraping a transcript.
  • turn/end checks the OpenViking pending-token threshold and commits when required.
  • skipSubagentSessions: true excludes sessions marked with header.origin: subagent from automatic profile, recall, capture, and commit; it defaults to false.
  • syncTurns: false stops every new write: no captured messages, no threshold or shutdown commit. Writes queued while the toggle was on are still replayed by the background drainer once the server recovers — they were captured with the toggle on. Profile injection and recall are unaffected; it defaults to true.
  • skillCatalog (default true) and skillCatalogTokenBudget (default 1200; 0 also turns the catalog off) govern <available-skills>. The catalog comes from one GET /api/v1/skills?node_limit=200 call: the user's own skills first, then those shared under viking://agent/skills minus any whose name the user also owns, each description cut to about 40 tokens. Its budget is separate from profileTokenBudget. When the descriptions do not fit, the catalog lists names only (with a ... +N more tail if even the names do not all fit); when not even one name fits, it shrinks to a one-line count; with no skills, or a server without the endpoint, it is omitted.
  • Retryable failed writes enter the shared OpenViking pending queue. A background drainer (default every 60s, OPENVIKING_PENDING_DRAIN_INTERVAL_MS) probes the server health and replays the queue in-process, so a transient write failure recovers without restarting dsh; it does not consume the session-start retry budget. Session-start replays keep consuming retries as before.
  • Non-retryable capture, commit, and initialization failures emit a warning once per session and stage, with the HTTP status, a bounded error code, and an endpoint template. Success resets that stage’s warning. Unexpected write exceptions also warn once until a write completes. Warnings omit message payloads and error text. Permanent failures are not queued for replay.
  • tools/pre-execute denies a DSH filesystem tool (read, glob, grep, edit, write, str_replace_editor) whose path argument is a viking:// URI, pointing the model at the bridged mcp__openviking__* tools instead. A write or edit under a skill directory (viking://~/skills/..., viking://user/<id>/skills/..., viking://agent/skills/...) points at mcp__openviking__add_skill instead, which creates or replaces a whole skill from its SKILL.md text. A grep whose pattern is viking:// text still runs.
  • tools/post-execute lets a bash command (pwsh on Windows, where DSH mounts it in place of bash) that carries a viking:// URI run unchanged and attaches a notice for the model: use the bridged tools if it meant OpenViking content, or ignore the notice when the URI is intentional data such as an ov argument or an HTTP payload.

Each DSH session maps to dsh-<session-id> in OpenViking. IDs that contain Windows-invalid path characters are normalized to a readable underscore form plus a stable hash, so two native IDs cannot collapse onto the same OpenViking session; already-portable IDs keep their existing mapping. Existing sessions whose native IDs require normalization start a new OpenViking session after this upgrade. Workspace-derived actor peers are resolved per session and sent on every session-specific request: the peer is the git identity of the session's workspace — the normalized origin URL ([email protected]:volcengine/OpenViking.git becomes github.com-volcengine-openviking), else the repository root path, that fallback keeping the older rule where every non-letter-or-digit character becomes -. With the default peer settings, no peer is sent outside a git repository, and what is remembered there goes to the user-level space viking://user/<you>/memories. One repository therefore keeps one peer across subdirectories, worktrees, clones and machines, while a fork's different origin keeps it separate. Memories written under the older path-derived peer stay reachable: the default recallPeerScope: all sweeps every peer under the user.

Workspace settings (peer, recall.*, capture.*) are loaded from session.header.cwd when the session first uses memory, not from the directory DSH was launched in. A workspace can set peer.id to name its project explicitly, or peer.source to choose a preset or template chain. For example, <root>/.openviking/config.json can contain:

{
  "version": 1,
  "peer": { "id": "my-project" }
}

The shared loader also applies config.local.json and machine registry overrides. Explicit host peerId, environment overrides and pinned ovcli.conf credentials retain their existing precedence. The resolved peer stays fixed for the session's runtime state; new sessions load the current workspace settings. A session without a cwd falls back to the process's working directory.

Tools

The model sees the OpenViking MCP tools under the bridge's server-qualified names — mcp__openviking__search, mcp__openviking__read, mcp__openviking__list, mcp__openviking__tree, mcp__openviking__grep, mcp__openviking__glob, mcp__openviking__remember, mcp__openviking__write, mcp__openviking__edit, mcp__openviking__forget, mcp__openviking__add_resource, mcp__openviking__add_skill, mcp__openviking__rule_search, and the rest of whatever the connected server advertises. The list re-syncs when the server announces a change, so a server upgrade adds tools without a bundle release.

mcp__openviking__forget performs permanent deletion. The calling model should use it only when the user explicitly requests deletion.

The bundle also serves the shared openviking-memory, openviking-skills, ov-experience-memory, and openviking-rule-search skills from skills/ through its own isolated ctx.skills provider, so the model gets the same guidance the other integrations ship: when to search, read, and write, and how to find, use, create, share, and migrate OpenViking skills.

Testing

npm ci          # installs the exact-pinned dsh devDependencies the tests exercise
npm run check   # syntax check every shipped module + package/PLUGIN_VERSION agreement
npm test        # node --test *.test.mjs — runs in the repo's PR workflow

The 0.3.2 compatibility check installed the packed bundle through dsh plugin add in isolated profiles and exercised DSH 0.1.0-rc.6, 0.1.5-rc.1, and 0.1.5-rc.2 with matching core packages. It covered Web startup and browser loading, skill discovery/read, MCP tool calls, profile/recall injection, user/assistant capture, resume deduplication, threshold commits, and disposal commits. The check used a local HTTP/MCP fixture and a deterministic model adapter on macOS with Node.js 26.5.0; it verifies harness integration, not real-server memory extraction. The rc.2 CLI also exited cleanly on SIGTERM.

The startup failure from issue #4944 was reproduced by adding the reported old skill packages to an rc.2 profile; removing those direct dependencies and reinstalling the bundle restored the same integration checks. A clean rc.2 profile with the published 0.3.0 bundle did not reproduce that import error.

live-recall.test.mjs is an opt-in end-to-end gate against a real OpenViking server: it stores a sentinel memory through a session commit, waits for extraction, and asserts recall returns that sentinel — the property no stub can certify. Enable it with OPENVIKING_E2E=1 plus the normal credential chain; it skips otherwise (including in CI until a server secret exists).

Client-side recall compression

Compression stays off by default. To use DSH's configured model instead of the server query planner, merge this into ~/.openviking/ovcli.conf and restart DSH:

{"plugin":{"dsh":{"recallCompress":"client","recallCompressTimeoutMs":10000}}}

The plugin makes one tool-free ctx.llm.stream() call using the agent's options.provider and options.model. recallCompressModel optionally selects another model on that same provider; recallCompressThinking supplies a provider-supported reasoning effort. Credentials and endpoints belong to DSH's provider configuration, not OpenViking's server credentials. DSH does not use recallCompressBaseUrl or the CLI compressor detection settings. “Client-side” means the call runs through DSH; inference is on-device only if that DSH provider points to a local model service. Model switches implemented solely by host request middleware are not inferred from agent.options; configure the compressor model explicitly or restart with the intended model.

client never requests server rewrite. auto prefers the host compressor when its model/service are available, otherwise retains server rewrite: "auto". server and off keep their existing behavior. An unavailable host compressor in client mode leaves the retrieved context uncompressed. Short input skips the model call; the shared input limit, cited-digest handling and fallback still apply. Timeouts, unsuccessful streams and invalid digests fall back to budgeted original fragments; an explicitly irrelevant result injects nothing. Cancellation stops waiting and signals the adapter. An adapter that ignores abort may continue its request, but cannot inject its late result. Auxiliary messages are not appended to the session or captured as memories. Digest caches are separated by connection, identity, session and model. Shared compression status is logged through DSH.

The compressor contract is checked against the published @deepseek-ai/dsh-llm 0.1.0-rc.6 service/types: LlmRuntime.stream, BlockAssembler, successful finish.reason.kind === "stop", and GenerateOptions.signal. The adapter uses no agent creation, prompt assembly, tools, or session append API. Unit and packaged service smoke tests cover Linux; real-model quality/latency and Desktop behavior still require deployment validation.