@agentforge/skills 0.16.75 → 0.16.76

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/dist/index.js CHANGED
@@ -10,6 +10,9 @@ var TrustPolicyReason = /* @__PURE__ */ ((TrustPolicyReason2) => {
10
10
  TrustPolicyReason2["NOT_SCRIPT"] = "not-script";
11
11
  TrustPolicyReason2["WORKSPACE_TRUST"] = "workspace-trust";
12
12
  TrustPolicyReason2["TRUSTED_ROOT"] = "trusted-root";
13
+ TrustPolicyReason2["WORKSPACE_SKILL_ACTIVATION"] = "workspace-skill-activation";
14
+ TrustPolicyReason2["TRUSTED_SKILL_ACTIVATION"] = "trusted-skill-activation";
15
+ TrustPolicyReason2["UNTRUSTED_SKILL_ACTIVATION_DENIED"] = "untrusted-skill-activation-denied";
13
16
  TrustPolicyReason2["UNTRUSTED_SCRIPT_DENIED"] = "untrusted-script-denied";
14
17
  TrustPolicyReason2["UNTRUSTED_SCRIPT_ALLOWED"] = "untrusted-script-allowed-override";
15
18
  TrustPolicyReason2["UNKNOWN_TRUST_LEVEL"] = "unknown-trust-level";
@@ -233,44 +236,6 @@ function buildMissingSkillMessage(registry, name) {
233
236
  };
234
237
  }
235
238
 
236
- // src/activation-activate-tool.ts
237
- function createActivateSkillTool(registry) {
238
- return new ToolBuilder().name("activate-skill").description(
239
- "Activate an Agent Skill by name, loading its full instructions. Returns the complete SKILL.md body content for the named skill. Use this when you see a relevant skill in <available_skills> and want to follow its instructions."
240
- ).category(ToolCategory.SKILLS).tags(["skill", "activation", "agent-skills"]).schema(activateSkillSchema).implement(async ({ name }) => {
241
- const skill = registry.get(name);
242
- if (!skill) {
243
- const { availableCount, errorMessage } = buildMissingSkillMessage(registry, name);
244
- activationLogger.warn("Skill activation failed \u2014 not found", { name, availableCount });
245
- return errorMessage;
246
- }
247
- const skillMdPath = resolve(skill.skillPath, "SKILL.md");
248
- try {
249
- const content = readFileSync(skillMdPath, "utf-8");
250
- const body = extractBody(content);
251
- activationLogger.info("Skill activated", {
252
- name: skill.metadata.name,
253
- skillPath: skill.skillPath,
254
- bodyLength: body.length
255
- });
256
- registry.emitEvent("skill:activated" /* SKILL_ACTIVATED */, {
257
- name: skill.metadata.name,
258
- skillPath: skill.skillPath,
259
- bodyLength: body.length
260
- });
261
- return body;
262
- } catch (error) {
263
- const message = error instanceof Error ? error.message : String(error);
264
- activationLogger.error("Skill activation failed \u2014 read error", {
265
- name,
266
- skillPath: skill.skillPath,
267
- error: message
268
- });
269
- return `Failed to read skill "${name}" instructions: ${message}`;
270
- }
271
- }).build();
272
- }
273
-
274
239
  // src/trust.ts
275
240
  var SCRIPT_PATH_PREFIX = "scripts/";
276
241
  var SCRIPT_PATH_EXACT = "scripts";
@@ -331,6 +296,92 @@ function evaluateTrustPolicy(resourcePath, trustLevel, allowUntrustedScripts = f
331
296
  };
332
297
  }
333
298
  }
299
+ function evaluateSkillActivationPolicy(trustLevel) {
300
+ switch (trustLevel) {
301
+ case "workspace":
302
+ return {
303
+ allowed: true,
304
+ reason: "workspace-skill-activation" /* WORKSPACE_SKILL_ACTIVATION */,
305
+ message: "Skill activation allowed \u2014 skill root has workspace trust"
306
+ };
307
+ case "trusted":
308
+ return {
309
+ allowed: true,
310
+ reason: "trusted-skill-activation" /* TRUSTED_SKILL_ACTIVATION */,
311
+ message: "Skill activation allowed \u2014 skill root is explicitly trusted"
312
+ };
313
+ case "untrusted":
314
+ return {
315
+ allowed: false,
316
+ reason: "untrusted-skill-activation-denied" /* UNTRUSTED_SKILL_ACTIVATION_DENIED */,
317
+ message: `Skill activation blocked \u2014 skill root is untrusted. Untrusted skills remain discoverable, but their full SKILL.md bodies are blocked by default. Promote the skill root to 'trusted' or 'workspace' trust level after review to allow activation.`
318
+ };
319
+ default:
320
+ return {
321
+ allowed: false,
322
+ reason: "unknown-trust-level" /* UNKNOWN_TRUST_LEVEL */,
323
+ message: `Skill activation blocked \u2014 trust level "${trustLevel}" is unknown and is treated as untrusted for security.`
324
+ };
325
+ }
326
+ }
327
+
328
+ // src/activation-activate-tool.ts
329
+ function createActivateSkillTool(registry) {
330
+ return new ToolBuilder().name("activate-skill").description(
331
+ "Activate an Agent Skill by name, loading its full instructions for trusted roots. Returns the complete SKILL.md body content for workspace or explicitly trusted skills. Use this when you see a relevant skill in <available_skills> or <untrusted_skills>; activation is blocked for untrusted roots until they are promoted."
332
+ ).category(ToolCategory.SKILLS).tags(["skill", "activation", "agent-skills"]).schema(activateSkillSchema).implement(async ({ name }) => {
333
+ const skill = registry.get(name);
334
+ if (!skill) {
335
+ const { availableCount, errorMessage } = buildMissingSkillMessage(registry, name);
336
+ activationLogger.warn("Skill activation failed \u2014 not found", { name, availableCount });
337
+ return errorMessage;
338
+ }
339
+ const policyDecision = evaluateSkillActivationPolicy(skill.trustLevel);
340
+ if (!policyDecision.allowed) {
341
+ activationLogger.warn("Skill activation blocked \u2014 trust policy", {
342
+ name,
343
+ trustLevel: skill.trustLevel,
344
+ reason: policyDecision.reason,
345
+ message: policyDecision.message
346
+ });
347
+ registry.emitEvent("trust:policy-denied" /* TRUST_POLICY_DENIED */, {
348
+ name: skill.metadata.name,
349
+ resourcePath: "SKILL.md",
350
+ trustLevel: skill.trustLevel,
351
+ reason: policyDecision.reason,
352
+ message: policyDecision.message
353
+ });
354
+ return policyDecision.message;
355
+ }
356
+ const skillMdPath = resolve(skill.skillPath, "SKILL.md");
357
+ try {
358
+ const content = readFileSync(skillMdPath, "utf-8");
359
+ const body = extractBody(content);
360
+ activationLogger.info("Skill activated", {
361
+ name: skill.metadata.name,
362
+ skillPath: skill.skillPath,
363
+ bodyLength: body.length,
364
+ trustLevel: skill.trustLevel,
365
+ activationReason: policyDecision.reason
366
+ });
367
+ registry.emitEvent("skill:activated" /* SKILL_ACTIVATED */, {
368
+ name: skill.metadata.name,
369
+ skillPath: skill.skillPath,
370
+ bodyLength: body.length,
371
+ trustLevel: skill.trustLevel
372
+ });
373
+ return body;
374
+ } catch (error) {
375
+ const message = error instanceof Error ? error.message : String(error);
376
+ activationLogger.error("Skill activation failed \u2014 read error", {
377
+ name,
378
+ skillPath: skill.skillPath,
379
+ error: message
380
+ });
381
+ return `Failed to read skill "${name}" instructions: ${message}`;
382
+ }
383
+ }).build();
384
+ }
334
385
  function resolveResourcePath(skillPath, resourcePath) {
335
386
  if (isAbsolute(resourcePath)) {
336
387
  return { success: false, error: "Absolute resource paths are not allowed" };
@@ -360,7 +411,7 @@ function resolveResourcePath(skillPath, resourcePath) {
360
411
  // src/activation-resource-tool.ts
361
412
  function createReadSkillResourceTool(registry) {
362
413
  return new ToolBuilder().name("read-skill-resource").description(
363
- "Read a resource file from an activated Agent Skill. Returns the content of a file within the skill directory (e.g., references/, scripts/, assets/). The path must be relative to the skill root and cannot traverse outside it."
414
+ "Read a resource file from an activated Agent Skill. Returns the content of a file within the skill directory (e.g., references/, scripts/, assets/). The path must be relative to the skill root and cannot traverse outside it. SKILL.md is only readable from workspace or trusted roots."
364
415
  ).category(ToolCategory.SKILLS).tags(["skill", "resource", "agent-skills"]).schema(readSkillResourceSchema).implement(async ({ name, path: resourcePath }) => {
365
416
  const skill = registry.get(name);
366
417
  if (!skill) {
@@ -377,6 +428,34 @@ function createReadSkillResourceTool(registry) {
377
428
  });
378
429
  return pathResult.error;
379
430
  }
431
+ const skillInstructionsPath = resolve(skill.skillPath, "SKILL.md");
432
+ let isSkillInstructions = pathResult.resolvedPath.toLowerCase() === skillInstructionsPath.toLowerCase();
433
+ if (!isSkillInstructions) {
434
+ try {
435
+ isSkillInstructions = realpathSync(pathResult.resolvedPath).toLowerCase() === realpathSync(skillInstructionsPath).toLowerCase();
436
+ } catch {
437
+ }
438
+ }
439
+ if (isSkillInstructions) {
440
+ const activationDecision = evaluateSkillActivationPolicy(skill.trustLevel);
441
+ if (!activationDecision.allowed) {
442
+ activationLogger.warn("Skill resource load blocked \u2014 skill instructions trust policy", {
443
+ name,
444
+ resourcePath,
445
+ trustLevel: skill.trustLevel,
446
+ reason: activationDecision.reason,
447
+ message: activationDecision.message
448
+ });
449
+ registry.emitEvent("trust:policy-denied" /* TRUST_POLICY_DENIED */, {
450
+ name: skill.metadata.name,
451
+ resourcePath: "SKILL.md",
452
+ trustLevel: skill.trustLevel,
453
+ reason: activationDecision.reason,
454
+ message: activationDecision.message
455
+ });
456
+ return activationDecision.message;
457
+ }
458
+ }
380
459
  const policyDecision = evaluateTrustPolicy(
381
460
  resourcePath,
382
461
  skill.trustLevel,
@@ -563,10 +642,19 @@ function generateSkillPrompt(config, allSkills, totalDiscovered, options) {
563
642
  const requested = new Set(options.skills);
564
643
  skills = skills.filter((skill) => requested.has(skill.metadata.name));
565
644
  }
645
+ let trustedSkills = skills.filter(
646
+ (skill) => skill.trustLevel === "workspace" || skill.trustLevel === "trusted"
647
+ );
648
+ let untrustedSkills = skills.filter(
649
+ (skill) => skill.trustLevel !== "workspace" && skill.trustLevel !== "trusted"
650
+ );
566
651
  if (config.maxDiscoveredSkills !== void 0 && config.maxDiscoveredSkills >= 0) {
567
- skills = skills.slice(0, config.maxDiscoveredSkills);
652
+ const trustedLimit = Math.min(config.maxDiscoveredSkills, trustedSkills.length);
653
+ trustedSkills = trustedSkills.slice(0, trustedLimit);
654
+ untrustedSkills = untrustedSkills.slice(0, config.maxDiscoveredSkills - trustedLimit);
655
+ skills = [...trustedSkills, ...untrustedSkills];
568
656
  }
569
- if (skills.length === 0) {
657
+ if (trustedSkills.length === 0 && untrustedSkills.length === 0) {
570
658
  logger4.debug("Skill prompt generation produced empty result", {
571
659
  totalDiscovered,
572
660
  filterApplied: !!(options?.skills && options.skills.length > 0),
@@ -574,12 +662,21 @@ function generateSkillPrompt(config, allSkills, totalDiscovered, options) {
574
662
  });
575
663
  return "";
576
664
  }
577
- const xml = `<available_skills>
578
- ${skills.map(renderSkillEntry).join("\n")}
579
- </available_skills>`;
665
+ const sections = [];
666
+ if (trustedSkills.length > 0) {
667
+ sections.push(`<available_skills>
668
+ ${trustedSkills.map(renderTrustedSkillEntry).join("\n")}
669
+ </available_skills>`);
670
+ }
671
+ if (untrustedSkills.length > 0) {
672
+ sections.push(renderUntrustedSkillsSection(untrustedSkills));
673
+ }
674
+ const xml = sections.join("\n");
580
675
  const estimatedTokens = Math.ceil(xml.length / 4);
581
676
  logger4.info("Skill prompt generated", {
582
677
  skillCount: skills.length,
678
+ trustedSkillCount: trustedSkills.length,
679
+ untrustedSkillCount: untrustedSkills.length,
583
680
  totalDiscovered,
584
681
  filterApplied: !!(options?.skills && options.skills.length > 0),
585
682
  ...config.maxDiscoveredSkills !== void 0 ? { maxCap: config.maxDiscoveredSkills } : {},
@@ -588,12 +685,32 @@ ${skills.map(renderSkillEntry).join("\n")}
588
685
  });
589
686
  return xml;
590
687
  }
591
- function renderSkillEntry(skill) {
688
+ function renderTrustedSkillEntry(skill) {
689
+ return [
690
+ " <skill>",
691
+ ` <name>${escapeXml(skill.metadata.name)}</name>`,
692
+ ` <description>${escapeXml(skill.metadata.description)}</description>`,
693
+ ` <location>${escapeXml(skill.skillPath)}</location>`,
694
+ ` <trust>${escapeXml(skill.trustLevel)}</trust>`,
695
+ " </skill>"
696
+ ].join("\n");
697
+ }
698
+ function renderUntrustedSkillsSection(skills) {
699
+ return [
700
+ "<untrusted_skills>",
701
+ " <trust_notice>Untrusted skills are discoverable, but their full SKILL.md bodies stay blocked until the root is promoted to trusted or workspace status.</trust_notice>",
702
+ ...skills.map(renderUntrustedSkillEntry),
703
+ "</untrusted_skills>"
704
+ ].join("\n");
705
+ }
706
+ function renderUntrustedSkillEntry(skill) {
592
707
  return [
593
708
  " <skill>",
594
709
  ` <name>${escapeXml(skill.metadata.name)}</name>`,
595
710
  ` <description>${escapeXml(skill.metadata.description)}</description>`,
596
711
  ` <location>${escapeXml(skill.skillPath)}</location>`,
712
+ ` <trust>${escapeXml(skill.trustLevel)}</trust>`,
713
+ " <activation_policy>requires-trusted-root</activation_policy>",
597
714
  " </skill>"
598
715
  ].join("\n");
599
716
  }
@@ -684,6 +801,6 @@ var SkillRegistry = class {
684
801
  }
685
802
  };
686
803
 
687
- export { SkillRegistry, SkillRegistryEvent, TrustPolicyReason, createActivateSkillTool, createReadSkillResourceTool, createSkillActivationTools, evaluateTrustPolicy, expandHome, isScriptResource, normalizeRootConfig, parseSkillContent, resolveResourcePath, scanAllSkillRoots, scanSkillRoot, validateSkillDescription, validateSkillName, validateSkillNameMatchesDir };
804
+ export { SkillRegistry, SkillRegistryEvent, TrustPolicyReason, createActivateSkillTool, createReadSkillResourceTool, createSkillActivationTools, evaluateSkillActivationPolicy, evaluateTrustPolicy, expandHome, isScriptResource, normalizeRootConfig, parseSkillContent, resolveResourcePath, scanAllSkillRoots, scanSkillRoot, validateSkillDescription, validateSkillName, validateSkillNameMatchesDir };
688
805
  //# sourceMappingURL=index.js.map
689
806
  //# sourceMappingURL=index.js.map