@agentforge/skills 0.16.75 → 0.16.77
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.cjs +163 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +163 -46
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
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 (
|
|
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
|
|
578
|
-
|
|
579
|
-
|
|
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
|
|
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
|