@openviking/dsh-memory-plugin 0.5.13 → 0.5.15
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 +10 -1
- package/capture.mjs +10 -1
- package/config.mjs +1 -1
- package/package.json +2 -1
- package/runtime.mjs +3 -3
- package/session-id.mjs +21 -0
package/README.md
CHANGED
|
@@ -117,6 +117,15 @@ context under such presets with no error. Pre-step injection also makes each
|
|
|
117
117
|
injection a session event that replays, is visible to compaction, and never
|
|
118
118
|
reaches `request/header`.
|
|
119
119
|
|
|
120
|
+
### What triggers recall
|
|
121
|
+
|
|
122
|
+
Recall runs on a pre-step whose entering batch contains user-authored input.
|
|
123
|
+
The query is built only from those messages: user-role messages with
|
|
124
|
+
`source.kind` absent or `user`. Messages that DSH or other plugins inject
|
|
125
|
+
(`time-context`, `plugin:*` notices, this plugin's own blocks) and tool results
|
|
126
|
+
are excluded, using the same rule capture uses. A batch made only of injected
|
|
127
|
+
context sends no search request.
|
|
128
|
+
|
|
120
129
|
### How the tool surface is mounted
|
|
121
130
|
|
|
122
131
|
`mcp.mjs` mounts `@deepseek-ai/dsh-mcp-client` on `servers/mcp-proxy.mjs`, the
|
|
@@ -228,7 +237,7 @@ The older size settings apply to fallback recall, not the primary context reques
|
|
|
228
237
|
- `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.
|
|
229
238
|
- `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.
|
|
230
239
|
|
|
231
|
-
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.
|
|
240
|
+
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 (`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.
|
|
232
241
|
|
|
233
242
|
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:
|
|
234
243
|
|
package/capture.mjs
CHANGED
|
@@ -96,9 +96,18 @@ function captureMessage(event, message, config, toolNames) {
|
|
|
96
96
|
return payload;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// Recall queries use only what the user typed. Injected context (time-context,
|
|
100
|
+
// job notices, other plugins' blocks, our own recall) and tool results would
|
|
101
|
+
// otherwise trigger recall on their own and pollute the query text.
|
|
102
|
+
function isUserInput(message) {
|
|
103
|
+
if (!message || message.role !== "user") return false;
|
|
104
|
+
if (message.source?.kind === "tool") return false;
|
|
105
|
+
return !isSyntheticUserMessage(message);
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
export function promptText(messages) {
|
|
100
109
|
return (messages || [])
|
|
101
|
-
.filter(
|
|
110
|
+
.filter(isUserInput)
|
|
102
111
|
.map(message => extractTextFromPayload(message))
|
|
103
112
|
.filter(Boolean)
|
|
104
113
|
.join("\n\n")
|
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
|
+
export const PLUGIN_VERSION = "0.5.15";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Namespace for the bridged OpenViking MCP tools. DSH publishes every MCP tool
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openviking/dsh-memory-plugin",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"description": "OpenViking memory and context bundle for DeepSeek Harness",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"mcp.mjs",
|
|
21
21
|
"mcp-env.mjs",
|
|
22
22
|
"runtime.mjs",
|
|
23
|
+
"session-id.mjs",
|
|
23
24
|
"skills.mjs",
|
|
24
25
|
"uri-guard.mjs",
|
|
25
26
|
"servers/",
|
package/runtime.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isCaptureEnabled } from "./shared/capture-utils.mjs";
|
|
2
2
|
import { buildProfileBlock } from "./shared/profile-inject.mjs";
|
|
3
3
|
import { buildRecallBlock, isRecallEnabled } from "./shared/recall-core.mjs";
|
|
4
|
-
import { deriveHarnessSessionId } from "./shared/session-model.mjs";
|
|
5
4
|
import {
|
|
6
5
|
dequeue,
|
|
7
6
|
enqueue,
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
pluginMessage,
|
|
17
16
|
promptText,
|
|
18
17
|
} from "./capture.mjs";
|
|
18
|
+
import { deriveDshSessionId } from "./session-id.mjs";
|
|
19
19
|
|
|
20
20
|
export class OpenVikingRuntime {
|
|
21
21
|
constructor(client, config, logger = console, resolveSessionPeer = null) {
|
|
@@ -45,7 +45,7 @@ export class OpenVikingRuntime {
|
|
|
45
45
|
});
|
|
46
46
|
state = {
|
|
47
47
|
dshSessionId: String(session.id),
|
|
48
|
-
ovSessionId:
|
|
48
|
+
ovSessionId: deriveDshSessionId(session.id),
|
|
49
49
|
config: { ...this.config, peerId: peer.peerId, legacyPeerId: peer.legacyPeerId },
|
|
50
50
|
ready: false,
|
|
51
51
|
profileBlock: "",
|
|
@@ -131,7 +131,7 @@ export class OpenVikingRuntime {
|
|
|
131
131
|
const state = await this.initialize(agent);
|
|
132
132
|
if (!state.ready || !isRecallEnabled(state.config)) return null;
|
|
133
133
|
const query = promptText(messages);
|
|
134
|
-
if (query.length < state.config.minQueryLength) return null;
|
|
134
|
+
if (!query || query.length < state.config.minQueryLength) return null;
|
|
135
135
|
const block = await buildRecallBlock(
|
|
136
136
|
(path, init, options) => this.client.fetchJSON(path, init, options),
|
|
137
137
|
state.config,
|
package/session-id.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { deriveHarnessSessionId } from "./shared/session-model.mjs";
|
|
3
|
+
|
|
4
|
+
// Keep identifiers valid as a single path component on Windows. The `dsh-`
|
|
5
|
+
// prefix also prevents reserved device names such as CON and NUL from matching
|
|
6
|
+
// the whole component.
|
|
7
|
+
const WINDOWS_UNSAFE = /[<>:"/\\|?*\u0000-\u001F]/g;
|
|
8
|
+
const WINDOWS_UNSAFE_TRAILING = /[. ]+$/g;
|
|
9
|
+
|
|
10
|
+
export function deriveDshSessionId(sessionId) {
|
|
11
|
+
const original = deriveHarnessSessionId("dsh-", String(sessionId));
|
|
12
|
+
const portable = original
|
|
13
|
+
.replace(WINDOWS_UNSAFE, "_")
|
|
14
|
+
.replace(WINDOWS_UNSAFE_TRAILING, match => "_".repeat(match.length));
|
|
15
|
+
if (portable === original) return original;
|
|
16
|
+
|
|
17
|
+
// Replacement alone is lossy (`im:a` and `im?a` both become `im_a`). Keep a
|
|
18
|
+
// readable stem and add a stable digest whenever normalization was needed.
|
|
19
|
+
const digest = createHash("sha256").update(original).digest("hex").slice(0, 12);
|
|
20
|
+
return `${portable}__${digest}`;
|
|
21
|
+
}
|