Files
OpenViking/examples/zcode-memory-plugin
Nick Jamesandpc.yu 3841e6f299 fix(plugins): drain the dsh pending queue in-process so a transient write failure self-heals (#4779)
* fix(plugins): drain the pending queue in-process so a transient write failure self-heals

The dsh memory plugin latches capture and commit on the first retryable
write failure (hasPendingWrites) and only reset the latch at session
init, so the long-lived dsh process stayed stuck until restart.

Add a per-process single-flight drainer (default 60s, env
OPENVIKING_PENDING_DRAIN_INTERVAL_MS) that follows the session-start
flow: probe health, replay the queue without consuming retry budgets,
then re-derive every session's latch from the queue. replayPending gains
an optional consumeRetries flag (default true, byte-compatible):
drainers release a failed claim back to its original filename instead of
incrementing the retry count, so the session-start path keeps owning all
retry accounting and D4 deletions. Latch and health transitions are
logged once per flip for observability.

* test(plugins): cover the drainer and non-consuming replay mode

Add pending-queue coverage for consumeRetries:false (retryable failures
stay retryable and ordered, non-retryable and exhausted entries still
delete, commitSession failures keep the run going, default mode
unchanged) and runtime drainer coverage (recovery clears the latch,
outages keep it and leave entries retryable, empty queue means zero
HTTP, commit resumes after the drain, single-flight, per-session latch
isolation, interval wiring with env fallback).

---------

Co-authored-by: pc.yu <nick@fourieralpha.com>
2026-09-10 13:26:46 +08:00
..

OpenViking Memory Plugin for ZCode

This package provides a ZCode lifecycle adapter for OpenViking long-term memory. It reuses the shared memory-plugin-shared runtime — no memory logic is duplicated. Only a thin ZCode adapter is new.

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.

What it does

  • SessionStart — injects user profile and preferences/entities into context.
  • UserPromptSubmit — searches OpenViking for relevant memories and injects them.
  • PreToolUse (Read|Glob|Grep) — denies direct access to viking:// URIs, redirects to MCP tools.
  • Stop — returns immediately, then captures incremental user/assistant turns and commits the OpenViking session in a detached worker.

ZCode does not support PreCompact/SessionEnd/SubagentStart/SubagentStop, so the commit-on-Stop strategy compensates for the absence of compact/end-of-session signals. The rollout file is the authoritative incremental transcript: stable host turnId values drive deduplication and allow a later Stop to recover missed turns. Hook stdin is only a fallback when the rollout file is unavailable.

Invariant: hook groups must OMIT the matcher key rather than writing "matcher": "". Strict parsers treat an empty string as invalid and may silently drop the entire configuration source.

Install

Use the shared installer:

bash examples/memory-plugin-shared/install.sh --harness zcode

The installer detects ZCode via ~/.zcode/ or a zcode binary, merges hooks and MCP config into ~/.zcode/cli/config.json, and writes OpenViking credentials to ~/.openviking/ovcli.conf.

Architecture

The plugin vendors the shared runtime into scripts/shared/ via sync.mjs. The dispatcher (zcode-hook.mjs) branches on event name; three thin shim scripts set an environment variable and import the dispatcher, while the URI guard has its own entry point. Shared runtime modules provide recall, batching, pending queue, credential resolution, and MCP proxying; zcode-capture.mjs owns the ZCode-specific acknowledgement and cursor state transition.

See DESIGN.md for verified ZCode extension-surface facts and decision provenance.

Tests

node --test scripts/*.test.mjs