@openviking/dsh-memory-plugin 0.5.8 → 0.5.10
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 +1 -1
- package/config.mjs +1 -1
- package/index.mjs +46 -3
- package/lifecycle.mjs +2 -2
- package/mcp-env.mjs +9 -4
- package/package.json +1 -1
- package/servers/mcp-proxy.mjs +15 -5
package/README.md
CHANGED
|
@@ -216,7 +216,7 @@ The older size settings apply to fallback recall, not the primary context reques
|
|
|
216
216
|
|
|
217
217
|
## Behavior
|
|
218
218
|
|
|
219
|
-
-
|
|
219
|
+
- 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.
|
|
220
220
|
- `agent/pre-step` retrieves with the current step input and appends a durable plugin message to that same step.
|
|
221
221
|
- `session/event` captures user, assistant, and optionally tool-result messages without scraping a transcript.
|
|
222
222
|
- `turn/end` checks the OpenViking pending-token threshold and commits when required.
|
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.10";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Namespace for the bridged OpenViking MCP tools. DSH publishes every MCP tool
|
package/index.mjs
CHANGED
|
@@ -34,14 +34,42 @@ export function apply(ctx, input = {}) {
|
|
|
34
34
|
"openvikingMemory.stopDrainer()",
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Per-agent startup: register the session's disposal commit, initialize the
|
|
38
|
+
// OpenViking session, and inject the profile while the agent is still idle.
|
|
39
|
+
// Runs at most once per agent, whichever DSH event reaches it.
|
|
40
|
+
const started = new WeakSet();
|
|
41
|
+
const startAgent = async (agent, signal) => {
|
|
42
|
+
if (skipMemory(agent.session) || started.has(agent)) return false;
|
|
43
|
+
started.add(agent);
|
|
39
44
|
agent.ctx.effect(
|
|
40
45
|
() => () => runtime.dispose(agent.session),
|
|
41
46
|
"openvikingMemory.disposeSession()",
|
|
42
47
|
);
|
|
43
|
-
|
|
48
|
+
try {
|
|
49
|
+
return await injectStartupProfile(agent, runtime, signal);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// A throw from a serial `agent/created` listener fails agent creation;
|
|
52
|
+
// memory startup must never do that. pre-step retries profile delivery.
|
|
53
|
+
runtime.log("startup_error", {
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
});
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// DSH 0.1.7+ has no `agent/session-start`; startup is the serial
|
|
61
|
+
// `agent/created` event with `{ agent, source, signal? }`, and AgentLoop holds
|
|
62
|
+
// queued input until its listeners settle. DSH 0.1.0-rc.6 and 0.1.5 still
|
|
63
|
+
// emit a composition-only `agent/created` with `{ agent }` only, followed by
|
|
64
|
+
// `agent/session-start`, so a missing `source` leaves startup to that event.
|
|
65
|
+
// The listener must resolve to undefined: any other value bails the serial
|
|
66
|
+
// dispatch and skips the listeners registered after this one.
|
|
67
|
+
ctx.on("agent/created", async ({ agent, source, signal }) => {
|
|
68
|
+
if (source === undefined) return undefined;
|
|
69
|
+
await untilAborted(startAgent(agent, signal), signal);
|
|
70
|
+
return undefined;
|
|
44
71
|
});
|
|
72
|
+
ctx.on("agent/session-start", ({ agent }) => startAgent(agent));
|
|
45
73
|
|
|
46
74
|
// prepend: downstream waterfall listeners run first, so this plugin sees
|
|
47
75
|
// the final claimed batch and appends after every other contributor.
|
|
@@ -83,3 +111,18 @@ export function apply(ctx, input = {}) {
|
|
|
83
111
|
mountOpenVikingMcp(ctx, config);
|
|
84
112
|
mountOpenVikingSkills(ctx);
|
|
85
113
|
}
|
|
114
|
+
|
|
115
|
+
// Resolve when `promise` settles or `signal` aborts, whichever comes first. An
|
|
116
|
+
// abandoned initialization keeps running and is reused by the pre-step path.
|
|
117
|
+
function untilAborted(promise, signal) {
|
|
118
|
+
if (!signal) return promise;
|
|
119
|
+
if (signal.aborted) return Promise.resolve();
|
|
120
|
+
return new Promise(resolve => {
|
|
121
|
+
const onAbort = () => resolve();
|
|
122
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
123
|
+
promise.finally(() => {
|
|
124
|
+
signal.removeEventListener("abort", onAbort);
|
|
125
|
+
resolve();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
package/lifecycle.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export async function injectStartupProfile(agent, runtime) {
|
|
1
|
+
export async function injectStartupProfile(agent, runtime, signal) {
|
|
2
2
|
await runtime.initialize(agent);
|
|
3
|
-
if (agent.status !== "idle") return false;
|
|
3
|
+
if (signal?.aborted || agent.status !== "idle") return false;
|
|
4
4
|
const profile = await runtime.profileMessage(agent);
|
|
5
5
|
if (!profile) return false;
|
|
6
6
|
agent.inject(profile);
|
package/mcp-env.mjs
CHANGED
|
@@ -12,15 +12,20 @@ export const PROXY_PATH = fileURLToPath(new URL("./servers/mcp-proxy.mjs", impor
|
|
|
12
12
|
* DSH scrubs credential-shaped names out of the environment a subprocess
|
|
13
13
|
* inherits, and the Cordis patch is invisible to one, so the proxy cannot
|
|
14
14
|
* resolve the connection the way this process did. It gets the answer instead:
|
|
15
|
-
* the resolved connection and this session's peer, forwarded
|
|
16
|
-
* `env` source so the child reads no file and lands on exactly
|
|
17
|
-
*
|
|
15
|
+
* the resolved connection, diagnostics, and this session's peer, forwarded
|
|
16
|
+
* with the forced `env` source so the child reads no file and lands on exactly
|
|
17
|
+
* these values — the empty ones included.
|
|
18
18
|
*/
|
|
19
19
|
export function buildMcpConfig(config) {
|
|
20
20
|
// In DSH Desktop, process.execPath is Electron's executable rather than a
|
|
21
21
|
// standalone Node binary. This tells Electron to run the proxy script as
|
|
22
22
|
// Node instead of attempting to launch a second Desktop instance.
|
|
23
|
-
const env = {
|
|
23
|
+
const env = {
|
|
24
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
25
|
+
...forwardConnectionEnv(config),
|
|
26
|
+
OPENVIKING_DEBUG: config.debug ? "1" : "0",
|
|
27
|
+
OPENVIKING_DEBUG_LOG: config.debugLogPath || "",
|
|
28
|
+
};
|
|
24
29
|
if (config.recallPeerScope) env.OPENVIKING_RECALL_PEER_SCOPE = config.recallPeerScope;
|
|
25
30
|
if (config.timeoutMs) env.OPENVIKING_TIMEOUT_MS = String(config.timeoutMs);
|
|
26
31
|
return {
|
package/package.json
CHANGED
package/servers/mcp-proxy.mjs
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* stdio -> streamable-HTTP MCP proxy for the OpenViking DSH bundle.
|
|
5
5
|
*
|
|
6
6
|
* DSH's MCP bridge starts this process as a local stdio MCP server. The proxy
|
|
7
|
-
* resolves its connection through the same `resolveConfig()`
|
|
8
|
-
* runtime, from the child environment
|
|
7
|
+
* resolves its connection and diagnostics through the same `resolveConfig()`
|
|
8
|
+
* as the in-process runtime, from the child environment built in `mcp-env.mjs`: DSH
|
|
9
9
|
* scrubs credential-shaped names out of what it inherits, and values that came
|
|
10
10
|
* from the Cordis patch are invisible to a subprocess otherwise.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { realpathSync } from "node:fs";
|
|
13
14
|
import { resolve as resolvePath } from "node:path";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
15
16
|
import { resolveConfig } from "../config.mjs";
|
|
@@ -25,11 +26,20 @@ export function readProxyConfig(env = process.env, cwd = process.cwd()) {
|
|
|
25
26
|
// still decides whether the proxy may send it. In broad recall mode the
|
|
26
27
|
// actor header must stay unset; otherwise a process launched in workspace A
|
|
27
28
|
// narrows MCP searches for a session whose runtime is serving workspace B.
|
|
28
|
-
debug:
|
|
29
|
-
debugLogPath: env.OV_DEBUG_LOG ||
|
|
29
|
+
debug: env.OV_DEBUG_LOG ? true : cfg.debug,
|
|
30
|
+
debugLogPath: env.OV_DEBUG_LOG || cfg.debugLogPath,
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
function isDirectRun() {
|
|
35
|
+
if (!process.argv[1]) return false;
|
|
36
|
+
try {
|
|
37
|
+
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
38
|
+
} catch {
|
|
39
|
+
return resolvePath(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isDirectRun()) {
|
|
34
44
|
createOpenVikingMcpProxy({ readConfig: readProxyConfig, loggerFactory: createLogger }).start();
|
|
35
45
|
}
|