@openviking/dsh-memory-plugin 0.5.9 → 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 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
- - `agent/session-start` injects the OpenViking profile, the available-memory index, and the `<available-skills>` catalog through `agent.inject()`.
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.9";
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
- ctx.on("agent/session-start", ({ agent }) => {
38
- if (skipMemory(agent.session)) return false;
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
- return injectStartupProfile(agent, runtime);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openviking/dsh-memory-plugin",
3
- "version": "0.5.9",
3
+ "version": "0.5.10",
4
4
  "description": "OpenViking memory and context bundle for DeepSeek Harness",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",
@@ -10,6 +10,7 @@
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";
@@ -30,6 +31,15 @@ export function readProxyConfig(env = process.env, cwd = process.cwd()) {
30
31
  });
31
32
  }
32
33
 
33
- if (process.argv[1] && fileURLToPath(import.meta.url) === resolvePath(process.argv[1])) {
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
  }