@lmzhen/dsh-evolution-learning-graph 0.3.62 → 0.3.64

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/lib/index.js +62 -12
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -10,7 +10,7 @@ Memory node ids carry a trailing snapshot token (`memory:<source>:<index>:<snaps
10
10
 
11
11
  Memory→skill edges are word-level, not substring: the entry is tokenized on non-letter/digit/hyphen runs and a skill name links only when it is a whole token. This prevents a skill named `run` from linking the words `running`/`grunt`.
12
12
 
13
- `graph edit`/`graph delete` of a skill node route through the evolution approval seam when it is mounted (soft-probed; the write executes directly when it is absent). Each approved/executed edit bumps the skill's patch counter and a delete archives it, matching `skill_manage` — including the no-op gate: an edit whose content is byte-equivalent to the current file (`noop`) writes nothing and does not bump the patch counter, exactly as `skill_manage` treats an unchanged update/patch. The command invocation's session rides the approval request (v12 N1), so a `never`-policy session stages nothing instead of deriving every graph write as foreground.
13
+ `graph edit`/`graph delete` route through the evolution approval seam when it is mounted (soft-probed; the write executes directly when it is absent) — for BOTH skill nodes and memory nodes (P2-6, v15; memory staged args mirror the `memory` tool runner's replay shape). Before staging, the seam's `hasRunner` is checked (P2-7, v15): with approval enabled, a session policy that is not `never`, and the write actually staging (foreground stages per approval `stageForeground`; subagent origins always stage), a missing replay runner's row (tool-skill-manage / tool-memory) refuses the write; allow-direct combinations execute unchanged instead of creating a pending record no approver could replay. Each approved/executed edit bumps the skill's patch counter and a delete archives it, matching `skill_manage` — including the no-op gate: an edit whose content is byte-equivalent to the current file (`noop`) writes nothing and does not bump the patch counter, exactly as `skill_manage` treats an unchanged update/patch. The command invocation's session rides the approval request (v12 N1), so a `never`-policy session stages nothing instead of deriving every graph write as foreground.
14
14
 
15
15
  ## Model Experience
16
16
 
package/lib/index.js CHANGED
@@ -183,14 +183,13 @@ async function resolveGraphNode(parsed, repository) {
183
183
  message: content
184
184
  };
185
185
  }
186
- const entries = await repository.readMemory(parsed.source);
187
- const entry = entries[parsed.index];
188
- return entry === void 0 ? {
189
- ok: false,
190
- message: `Memory ${parsed.source}[${parsed.index}] does not exist (${entries.length} entries).`
191
- } : {
186
+ const check = readMemoryIndex(parsed, await repository.readMemory(parsed.source));
187
+ return check.ok ? {
192
188
  ok: true,
193
- message: entry
189
+ message: check.entry ?? ""
190
+ } : {
191
+ ok: false,
192
+ message: check.message ?? `Memory ${parsed.source}[${parsed.index}] is not available.`
194
193
  };
195
194
  }
196
195
  /**
@@ -255,12 +254,14 @@ function apply(ctx, rawConfig = {}) {
255
254
  const memoryEntries = await memory.read("memory");
256
255
  const userEntries = await memory.read("user");
257
256
  const skills = withSkills();
257
+ const names = [...usageMap.keys()];
258
+ const contents = await Promise.all(names.map((name) => skills.read(name)));
258
259
  const related = /* @__PURE__ */ new Map();
259
- for (const name of usageMap.keys()) {
260
- const content = await skills.read(name);
261
- if (content === null) continue;
260
+ names.forEach((name, index) => {
261
+ const content = contents[index];
262
+ if (content === null || content === void 0) return;
262
263
  related.set(name, relatedSkillNames(content, name));
263
- }
264
+ });
264
265
  const graph = buildLearningGraph(usageMap, memoryEntries, userEntries, related);
265
266
  const nodeBlock = capRenderedLines(graph.nodes.map((node) => renderNodeLine(node)));
266
267
  const edgeBlock = capRenderedLines(graph.edges.map((edge) => edge.from + " --" + edge.type + "--> " + edge.to));
@@ -289,7 +290,10 @@ function apply(ctx, rawConfig = {}) {
289
290
  const approval = ctx.get("evolutionApproval");
290
291
  const origins = resolveOrigins(session?.header?.origin);
291
292
  if (approval) {
292
- const sessionPolicy = effectiveSessionPolicy(ctx, session);
293
+ const sessionPolicyEdit = effectiveSessionPolicy(ctx, session);
294
+ const stagesForeground = approval.stageForeground !== false;
295
+ if (approval.isEnabled !== false && sessionPolicyEdit !== "never" && (origins.approval === "background_review" || stagesForeground) && !approval.hasRunner("skill")) return err("Graph skill write cannot be staged: no skill replay runner is registered — mount the tool-skill-manage row (evolution-agent preset) or disable evolution-approval.");
296
+ const sessionPolicy = sessionPolicyEdit;
293
297
  const decision = await approval.request({
294
298
  kind: "skill",
295
299
  summary: `graph edit ${parsed.name}`,
@@ -315,6 +319,29 @@ function apply(ctx, rawConfig = {}) {
315
319
  }
316
320
  const check = readMemoryIndex(parsed, await memory.read(parsed.source));
317
321
  if (!check.ok) return err(check.message ?? "Memory index check failed.");
322
+ const memoryApproval = ctx.get("evolutionApproval");
323
+ if (memoryApproval) {
324
+ const originsM = resolveOrigins(session?.header?.origin);
325
+ const sessionPolicyM = effectiveSessionPolicy(ctx, session);
326
+ if (memoryApproval.isEnabled !== false && sessionPolicyM !== "never" && (originsM.approval === "background_review" || memoryApproval.stageForeground !== false) && !memoryApproval.hasRunner("memory")) return err("Graph memory write cannot be staged: no memory replay runner is registered — mount the tool-memory row (evolution-host/evolution-all bundle) or disable evolution-approval.");
327
+ const decision = await memoryApproval.request({
328
+ kind: "memory",
329
+ summary: `graph edit memory:${parsed.source}:${parsed.index}`,
330
+ args: {
331
+ target: parsed.source,
332
+ operations: [{
333
+ action: "replace",
334
+ old_text: check.entry ?? "",
335
+ facts: content
336
+ }]
337
+ },
338
+ origin: originsM.approval,
339
+ ...session?.id ? { sessionId: session.id } : {},
340
+ ...session ? { session } : {},
341
+ ...sessionPolicyM !== void 0 ? { sessionPolicy: sessionPolicyM } : {}
342
+ });
343
+ if (decision.action === "staged") return ok(decision.message);
344
+ }
318
345
  const result = await memory.applyBatch(parsed.source, [{
319
346
  action: "replace",
320
347
  old_text: check.entry ?? "",
@@ -330,6 +357,7 @@ function apply(ctx, rawConfig = {}) {
330
357
  if (approval) {
331
358
  const origins = resolveOrigins(session?.header?.origin);
332
359
  const sessionPolicy = effectiveSessionPolicy(ctx, session);
360
+ if (approval.isEnabled !== false && sessionPolicy !== "never" && (origins.approval === "background_review" || approval.stageForeground !== false) && !approval.hasRunner("skill")) return err("Graph skill delete cannot be staged: no skill replay runner is registered — mount the tool-skill-manage row (evolution-agent preset) or disable evolution-approval.");
333
361
  const decision = await approval.request({
334
362
  kind: "skill",
335
363
  summary: `graph delete ${parsed.name}`,
@@ -354,6 +382,28 @@ function apply(ctx, rawConfig = {}) {
354
382
  }
355
383
  const check = readMemoryIndex(parsed, await memory.read(parsed.source));
356
384
  if (!check.ok) return err(check.message ?? "Memory index check failed.");
385
+ const memoryApproval = ctx.get("evolutionApproval");
386
+ if (memoryApproval) {
387
+ const origins = resolveOrigins(session?.header?.origin);
388
+ const sessionPolicy = effectiveSessionPolicy(ctx, session);
389
+ if (memoryApproval.isEnabled !== false && sessionPolicy !== "never" && (origins.approval === "background_review" || memoryApproval.stageForeground !== false) && !memoryApproval.hasRunner("memory")) return err("Graph memory delete cannot be staged: no memory replay runner is registered — mount the tool-memory row (evolution-host/evolution-all bundle) or disable evolution-approval.");
390
+ const decision = await memoryApproval.request({
391
+ kind: "memory",
392
+ summary: `graph delete memory:${parsed.source}:${parsed.index}`,
393
+ args: {
394
+ target: parsed.source,
395
+ operations: [{
396
+ action: "remove",
397
+ old_text: check.entry ?? ""
398
+ }]
399
+ },
400
+ origin: origins.approval,
401
+ ...session?.id ? { sessionId: session.id } : {},
402
+ ...session ? { session } : {},
403
+ ...sessionPolicy !== void 0 ? { sessionPolicy } : {}
404
+ });
405
+ if (decision.action === "staged") return ok(decision.message);
406
+ }
357
407
  const result = await memory.applyBatch(parsed.source, [{
358
408
  action: "remove",
359
409
  old_text: check.entry ?? ""
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-learning-graph",
3
3
  "description": "Learning graph over skills and memory (community build)",
4
- "version": "0.3.62",
4
+ "version": "0.3.64",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,8 +31,8 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.62",
35
- "@lmzhen/dsh-evolution-core": "^0.3.62"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.64",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.64"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",