@openviking/dsh-memory-plugin 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -137,7 +137,7 @@ effect for the tools when DSH restarts the bundle, as it does for the runtime.
137
137
 
138
138
  Two consequences follow from the proxy being one process per profile:
139
139
 
140
- - **The actor peer is process-level.** Recall, capture, and commit still resolve a peer per session from that session's workspace repository, but tool calls carry the peer resolved at boot. Set `OPENVIKING_PEER_ID` when one process serves several repositories and you need tool calls attributed exactly.
140
+ - **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.
141
141
  - **`remember` is not session-scoped.** The server's MCP `remember` stores into
142
142
  its own short-lived session rather than the live `dsh-<session-id>` stream —
143
143
  the same behavior the Claude Code, Codex, and Cursor integrations have.
@@ -210,7 +210,18 @@ The patch can also carry plugin config:
210
210
  - `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.
211
211
  - `tools/post-execute` lets a `bash` command 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.
212
212
 
213
- Each DSH session maps to `dsh-<session-id>` in OpenViking. 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 (`git@github.com: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 `-`. Outside a git repository no peer is sent at all, 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. DSH does not read workspace `.openviking/config.json` files, so a `peer.id` written there has no effect; pin a peer with `OPENVIKING_PEER_ID` instead. Memories written under the older path-derived peer stay reachable: the default `recallPeerScope: all` sweeps every peer under the user.
213
+ Each DSH session maps to `dsh-<session-id>` in OpenViking. 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 (`git@github.com: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.
214
+
215
+ Workspace peer settings are loaded from `session.header.cwd` when the session first uses memory. 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:
216
+
217
+ ```json
218
+ {
219
+ "version": 1,
220
+ "peer": { "id": "my-project" }
221
+ }
222
+ ```
223
+
224
+ 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.
214
225
 
215
226
  ## Tools
216
227
 
package/config.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { buildPluginConfig } from "./shared/plugin-config.mjs";
2
2
  import { loadCredentialFiles } from "./shared/credentials.mjs";
3
3
 
4
- export const PLUGIN_VERSION = "0.5.4";
4
+ export const PLUGIN_VERSION = "0.5.6";
5
5
 
6
6
  /**
7
7
  * Namespace for the bridged OpenViking MCP tools. DSH publishes every MCP tool
package/index.mjs CHANGED
@@ -12,7 +12,11 @@ export const inject = ["agents", "sessions", "tools"];
12
12
  export function apply(ctx, input = {}) {
13
13
  const config = resolveConfig(input);
14
14
  const client = new OpenVikingClient(config);
15
- const runtime = new OpenVikingRuntime(client, config, ctx.logger);
15
+ const runtime = new OpenVikingRuntime(client, config, ctx.logger, cwd => (
16
+ // Rebuild from the host input, not the config already merged for boot's cwd.
17
+ // The shared loader preserves host/env precedence over workspace peers.
18
+ resolveConfig(input, process.env, cwd).effectivePeer
19
+ ));
16
20
  const skipMemory = session => (
17
21
  config.skipSubagentSessions && session?.header?.origin === "subagent"
18
22
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openviking/dsh-memory-plugin",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "OpenViking memory and context bundle for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
package/runtime.mjs CHANGED
@@ -18,10 +18,11 @@ import {
18
18
  } from "./capture.mjs";
19
19
 
20
20
  export class OpenVikingRuntime {
21
- constructor(client, config, logger = console) {
21
+ constructor(client, config, logger = console, resolveSessionPeer = null) {
22
22
  this.client = client;
23
23
  this.config = config;
24
24
  this.logger = logger;
25
+ this.resolveSessionPeer = resolveSessionPeer;
25
26
  this.states = new Map();
26
27
  this.drainTimer = null;
27
28
  this.drainRunning = false;
@@ -33,7 +34,7 @@ export class OpenVikingRuntime {
33
34
  let state = this.states.get(session.id);
34
35
  if (state) return state;
35
36
  const cwd = session.header?.cwd || process.cwd();
36
- const peer = resolveEffectivePeerId({
37
+ const peer = this.resolveSessionPeer?.(cwd) ?? resolveEffectivePeerId({
37
38
  cfg: {
38
39
  peerId: this.config.explicitPeerId,
39
40
  peerSource: this.config.peerSource,
@@ -642,16 +642,18 @@ export function shouldCaptureText(text, role, cfg = {}, { filters = true } = {})
642
642
  const sanitized = sanitizeCapturedText(text);
643
643
  if (!sanitized) return { shouldCapture: false, reason: "empty", text: "" };
644
644
 
645
- let capped = truncateCaptureText(sanitized, maxLength);
645
+ // Rules judge the full sanitized turn; the wire cap applies afterward.
646
+ let filtered = sanitized;
646
647
  if (filters) {
647
648
  const compiled = compileInputFilters(cfg?.captureFilters);
648
649
  if (compiled.rules.length) {
649
- const verdict = applyInputFilters(capped, compiled.rules, { role });
650
+ const verdict = applyInputFilters(filtered, compiled.rules, { role });
650
651
  if (verdict.dropped) return { shouldCapture: false, reason: "filtered", text: "" };
651
- capped = verdict.text;
652
- if (!capped) return { shouldCapture: false, reason: "empty", text: "" };
652
+ filtered = verdict.text;
653
+ if (!filtered) return { shouldCapture: false, reason: "empty", text: "" };
653
654
  }
654
655
  }
656
+ const capped = truncateCaptureText(filtered, maxLength);
655
657
  const compact = oneLine(capped);
656
658
  const isToolSummary = /^\[tool-(?:call|result)\b/i.test(compact);
657
659