@lmzhen/dsh-tool-skill-manage 0.3.71 → 0.3.72

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/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { effectiveSessionPolicy } from "@lmzhen/dsh-evolution-approval";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
- import { DEFAULT_SKILL_LIMITS, DSH_AUTHORING_STANDARDS, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SkillLibrary, authoringFeedback, clampedNumber, computeDedupGroups, contentHash, evolutionIoAdapter, parseFrontmatter, resolveOrigins, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
4
+ import { DEFAULT_SKILL_LIMITS, DSH_AUTHORING_STANDARDS, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SkillLibrary, authoringFeedback, clampedNumber, computeDedupGroups, contentHash, evolutionIoAdapter, parseFrontmatter, resolveOrigins, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
5
5
  //#region lib/types/index.js
6
6
  /**
7
7
  * Model-facing skill_manage tool over ctx.evolutionIo + ctx.skillUsage.
@@ -35,7 +35,8 @@ const Config = z.object({
35
35
  maxSkillContentChars: z.number().min(1).default(DEFAULT_SKILL_LIMITS.maxSkillContentChars),
36
36
  maxSkillFileBytes: z.number().min(1).default(DEFAULT_SKILL_LIMITS.maxSkillFileBytes),
37
37
  descriptionStrict: z.boolean().default(false),
38
- threatExemptLabels: z.array(z.string()).default([])
38
+ threatExemptLabels: z.array(z.string()).default([]),
39
+ strictCrossSource: z.boolean().default(false)
39
40
  });
40
41
  /** v30 REV-02: read the protected-skill list off the (optional) policy
41
42
  * snapshot through an `unknown` boundary — the Context augmentation types the
@@ -67,6 +68,15 @@ function apply(ctx, rawConfig = {}) {
67
68
  ctx.emit("evolution/skill-mutated", event);
68
69
  }, void 0, [...rawConfig.threatExemptLabels ?? []]);
69
70
  if (numericClamped.length > 0) ctx.logger.warn(`tool-skill-manage: ${numericClamped.join(", ")} provided an invalid value; falling back to the default`);
71
+ const catalogWinner = async (name) => {
72
+ const catalog = ctx.get("skills");
73
+ if (catalog === void 0) return void 0;
74
+ try {
75
+ return (await catalog.list()).find((summary) => summary.name === name);
76
+ } catch {
77
+ return;
78
+ }
79
+ };
70
80
  async function executeCore(args, origin = "foreground") {
71
81
  const action = args.action;
72
82
  const name = args.name ?? "";
@@ -121,26 +131,7 @@ function apply(ctx, rawConfig = {}) {
121
131
  skills: []
122
132
  };
123
133
  }
124
- const REQUIRED_ARGS = {
125
- create: ["name", "content"],
126
- edit: ["name", "content"],
127
- update: ["name", "content"],
128
- patch: [
129
- "name",
130
- "old_string",
131
- "new_string"
132
- ],
133
- delete: ["name"],
134
- write_file: [
135
- "name",
136
- "file_path",
137
- "file_content"
138
- ],
139
- remove_file: ["name", "file_path"],
140
- restructure: ["name"],
141
- pin: ["name"],
142
- unpin: ["name"]
143
- };
134
+ const REQUIRED_ARGS = SKILL_ACTION_REQUIRED_FIELDS;
144
135
  const missing = (action === void 0 || typeof action !== "string" || !Object.hasOwn(REQUIRED_ARGS, action) ? [] : REQUIRED_ARGS[action] ?? []).filter((field) => scalarArgs[field] === void 0 || scalarArgs[field] === null);
145
136
  if (missing.length > 0) return {
146
137
  ok: false,
@@ -306,25 +297,44 @@ function apply(ctx, rawConfig = {}) {
306
297
  },
307
298
  isConcurrencySafe: () => false,
308
299
  async execute(args, exec) {
300
+ let operation = args;
309
301
  const origins = resolveOrigins(exec.agent?.session?.header.origin);
310
302
  const reviewOrigin = origins.approval;
311
303
  const libraryOrigin = origins.library;
312
304
  const sessionPolicy = effectiveSessionPolicy(ctx, exec.agent?.session);
305
+ if (typeof args.name === "string" && args.name !== "" && args.action !== "list" && args.action !== "review" && args.action !== "pin" && args.action !== "unpin") {
306
+ const winner = await catalogWinner(args.name);
307
+ if (winner !== void 0 && winner.provider !== "dsh-evolution") {
308
+ const message = `skill "${args.name}" resolves to a higher-priority "${winner.source}" skill (provider "${winner.provider}"); this write lands on the family copy, which the catalog does NOT serve — edit the "${winner.source}" copy or rename.`;
309
+ if (rawConfig.strictCrossSource === true) return {
310
+ ok: false,
311
+ message: `Refused: ${message}`,
312
+ skills: []
313
+ };
314
+ ctx.logger.warn(`skill_manage: ${message}`);
315
+ }
316
+ }
313
317
  const approval = ctx.get("evolutionApproval");
314
318
  if (approval && args.action !== "list" && args.action !== "review" && args.action !== "pin" && args.action !== "unpin") {
315
319
  if ((args.action === "update" || args.action === "edit") && typeof args.name === "string" && args.name !== "") {
316
320
  const stageCurrent = await library.read(args.name).catch(() => null);
317
- if (stageCurrent !== null) args.staged_from_sha256 = contentHash(stageCurrent);
321
+ if (stageCurrent !== null) operation = {
322
+ ...args,
323
+ staged_from_sha256: contentHash(stageCurrent)
324
+ };
318
325
  }
319
326
  if ((args.action === "write_file" || args.action === "remove_file") && typeof args.name === "string" && args.name !== "" && typeof args.file_path === "string" && args.file_path !== "") {
320
327
  const stageFile = await library.readSupportFile(args.name, args.file_path).catch(() => void 0);
321
- if (stageFile !== void 0) args.staged_from_sha256 = stageFile === null ? "absent" : contentHash(stageFile);
328
+ if (stageFile !== void 0) operation = {
329
+ ...args,
330
+ staged_from_sha256: stageFile === null ? "absent" : contentHash(stageFile)
331
+ };
322
332
  }
323
333
  const decision = await approval.request({
324
334
  kind: "skill",
325
335
  summary: `skill ${args.action ?? "?"} ${args.name ?? ""}`.trim(),
326
336
  args: {
327
- operation: args,
337
+ operation,
328
338
  origin: reviewOrigin,
329
339
  libraryOrigin
330
340
  },
@@ -340,7 +350,7 @@ function apply(ctx, rawConfig = {}) {
340
350
  ...decision.pendingId !== void 0 ? { pending_id: decision.pendingId } : {}
341
351
  };
342
352
  }
343
- return await executeCore(args, libraryOrigin);
353
+ return await executeCore(operation, libraryOrigin);
344
354
  }
345
355
  }));
346
356
  ctx.inject(["evolutionApproval"], (approvalCtx) => {
@@ -32,6 +32,11 @@ export interface Config {
32
32
  * strict ANY-hit-blocks behavior is unchanged; deployments opt in per
33
33
  * label for known false-positive content. */
34
34
  threatExemptLabels?: string[];
35
+ /** OPT-19 (2026-09, plan D3): when true, a write whose name the platform
36
+ * catalog resolves to a NON-family source (project/custom sources outrank
37
+ * the family tree) is REFUSED instead of warned about. Default false —
38
+ * warn only, keeping the family tree an autonomous evolution zone. */
39
+ strictCrossSource?: boolean;
35
40
  }
36
41
  export declare const Config: z<Config>;
37
42
  export declare function apply(ctx: Context, rawConfig?: Config): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-tool-skill-manage",
3
3
  "description": "Model-facing skill_manage tool (community build)",
4
- "version": "0.3.71",
4
+ "version": "0.3.72",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,24 +31,26 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.71",
35
- "@lmzhen/dsh-evolution-core": "^0.3.71"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.72",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.72"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",
39
39
  "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
40
+ "@deepseek-ai/dsh-skill": "^0.1.5-rc.2",
40
41
  "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.2",
41
42
  "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
42
- "@lmzhen/dsh-evolution-io": "^0.3.71",
43
- "@lmzhen/dsh-skill-usage": "^0.3.71"
43
+ "@lmzhen/dsh-evolution-io": "^0.3.72",
44
+ "@lmzhen/dsh-skill-usage": "^0.3.72"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.5-rc.2",
47
48
  "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
49
+ "@deepseek-ai/dsh-skill": "^0.1.5-rc.2",
48
50
  "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.2",
49
51
  "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
50
- "@lmzhen/dsh-evolution-core": "^0.3.71",
51
- "@lmzhen/dsh-evolution-io": "^0.3.71",
52
- "@lmzhen/dsh-skill-usage": "^0.3.71"
52
+ "@lmzhen/dsh-evolution-core": "^0.3.72",
53
+ "@lmzhen/dsh-evolution-io": "^0.3.72",
54
+ "@lmzhen/dsh-skill-usage": "^0.3.72"
53
55
  }
54
56
  }