@lmzhen/dsh-evolution-learning-graph 0.3.61 → 0.3.63

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
@@ -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
@@ -255,12 +255,14 @@ function apply(ctx, rawConfig = {}) {
255
255
  const memoryEntries = await memory.read("memory");
256
256
  const userEntries = await memory.read("user");
257
257
  const skills = withSkills();
258
+ const names = [...usageMap.keys()];
259
+ const contents = await Promise.all(names.map((name) => skills.read(name)));
258
260
  const related = /* @__PURE__ */ new Map();
259
- for (const name of usageMap.keys()) {
260
- const content = await skills.read(name);
261
- if (content === null) continue;
261
+ names.forEach((name, index) => {
262
+ const content = contents[index];
263
+ if (content === null || content === void 0) return;
262
264
  related.set(name, relatedSkillNames(content, name));
263
- }
265
+ });
264
266
  const graph = buildLearningGraph(usageMap, memoryEntries, userEntries, related);
265
267
  const nodeBlock = capRenderedLines(graph.nodes.map((node) => renderNodeLine(node)));
266
268
  const edgeBlock = capRenderedLines(graph.edges.map((edge) => edge.from + " --" + edge.type + "--> " + edge.to));
@@ -287,9 +289,12 @@ function apply(ctx, rawConfig = {}) {
287
289
  if (parsed === null) return err(`Invalid node id "${id}". Skill names or memory:<source>:<index> expected.`);
288
290
  if (parsed.kind === "skill") {
289
291
  const approval = ctx.get("evolutionApproval");
292
+ const origins = resolveOrigins(session?.header?.origin);
290
293
  if (approval) {
291
- const origins = resolveOrigins(session?.header?.origin);
292
- const sessionPolicy = effectiveSessionPolicy(ctx, session);
294
+ const sessionPolicyEdit = effectiveSessionPolicy(ctx, session);
295
+ const stagesForeground = approval.stageForeground !== false;
296
+ 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.");
297
+ const sessionPolicy = sessionPolicyEdit;
293
298
  const decision = await approval.request({
294
299
  kind: "skill",
295
300
  summary: `graph edit ${parsed.name}`,
@@ -309,12 +314,35 @@ function apply(ctx, rawConfig = {}) {
309
314
  });
310
315
  if (decision.action === "staged") return ok(decision.message);
311
316
  }
312
- const result = await withSkills().update(parsed.name, content, "foreground");
317
+ const result = await withSkills().update(parsed.name, content, origins.library);
313
318
  if (result.ok && result.noop !== true) await usageService.record?.(parsed.name, "patch");
314
319
  return result.ok ? ok(result.message) : err(result.message);
315
320
  }
316
321
  const check = readMemoryIndex(parsed, await memory.read(parsed.source));
317
322
  if (!check.ok) return err(check.message ?? "Memory index check failed.");
323
+ const memoryApproval = ctx.get("evolutionApproval");
324
+ if (memoryApproval) {
325
+ const originsM = resolveOrigins(session?.header?.origin);
326
+ const sessionPolicyM = effectiveSessionPolicy(ctx, session);
327
+ 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.");
328
+ const decision = await memoryApproval.request({
329
+ kind: "memory",
330
+ summary: `graph edit memory:${parsed.source}:${parsed.index}`,
331
+ args: {
332
+ target: parsed.source,
333
+ operations: [{
334
+ action: "replace",
335
+ old_text: check.entry ?? "",
336
+ facts: content
337
+ }]
338
+ },
339
+ origin: originsM.approval,
340
+ ...session?.id ? { sessionId: session.id } : {},
341
+ ...session ? { session } : {},
342
+ ...sessionPolicyM !== void 0 ? { sessionPolicy: sessionPolicyM } : {}
343
+ });
344
+ if (decision.action === "staged") return ok(decision.message);
345
+ }
318
346
  const result = await memory.applyBatch(parsed.source, [{
319
347
  action: "replace",
320
348
  old_text: check.entry ?? "",
@@ -330,6 +358,7 @@ function apply(ctx, rawConfig = {}) {
330
358
  if (approval) {
331
359
  const origins = resolveOrigins(session?.header?.origin);
332
360
  const sessionPolicy = effectiveSessionPolicy(ctx, session);
361
+ 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
362
  const decision = await approval.request({
334
363
  kind: "skill",
335
364
  summary: `graph delete ${parsed.name}`,
@@ -354,6 +383,28 @@ function apply(ctx, rawConfig = {}) {
354
383
  }
355
384
  const check = readMemoryIndex(parsed, await memory.read(parsed.source));
356
385
  if (!check.ok) return err(check.message ?? "Memory index check failed.");
386
+ const memoryApproval = ctx.get("evolutionApproval");
387
+ if (memoryApproval) {
388
+ const origins = resolveOrigins(session?.header?.origin);
389
+ const sessionPolicy = effectiveSessionPolicy(ctx, session);
390
+ 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.");
391
+ const decision = await memoryApproval.request({
392
+ kind: "memory",
393
+ summary: `graph delete memory:${parsed.source}:${parsed.index}`,
394
+ args: {
395
+ target: parsed.source,
396
+ operations: [{
397
+ action: "remove",
398
+ old_text: check.entry ?? ""
399
+ }]
400
+ },
401
+ origin: origins.approval,
402
+ ...session?.id ? { sessionId: session.id } : {},
403
+ ...session ? { session } : {},
404
+ ...sessionPolicy !== void 0 ? { sessionPolicy } : {}
405
+ });
406
+ if (decision.action === "staged") return ok(decision.message);
407
+ }
357
408
  const result = await memory.applyBatch(parsed.source, [{
358
409
  action: "remove",
359
410
  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.61",
4
+ "version": "0.3.63",
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.61",
35
- "@lmzhen/dsh-evolution-core": "^0.3.61"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.63",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.63"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",