@agentforge/core 0.14.0 → 0.15.1
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 +28 -897
- package/dist/index.d.cts +1 -679
- package/dist/index.d.ts +1 -679
- package/dist/index.js +28 -880
- package/package.json +15 -8
package/dist/index.cjs
CHANGED
|
@@ -47,8 +47,6 @@ __export(index_exports, {
|
|
|
47
47
|
MissingDescriptionError: () => MissingDescriptionError,
|
|
48
48
|
Profiler: () => Profiler,
|
|
49
49
|
RegistryEvent: () => RegistryEvent,
|
|
50
|
-
SkillRegistry: () => SkillRegistry,
|
|
51
|
-
SkillRegistryEvent: () => SkillRegistryEvent,
|
|
52
50
|
TimeoutError: () => TimeoutError,
|
|
53
51
|
ToolBuilder: () => ToolBuilder,
|
|
54
52
|
ToolCategory: () => ToolCategory,
|
|
@@ -58,7 +56,6 @@ __export(index_exports, {
|
|
|
58
56
|
ToolNameSchema: () => ToolNameSchema,
|
|
59
57
|
ToolRegistry: () => ToolRegistry,
|
|
60
58
|
ToolRelationsSchema: () => ToolRelationsSchema,
|
|
61
|
-
TrustPolicyReason: () => TrustPolicyReason,
|
|
62
59
|
batch: () => batch,
|
|
63
60
|
broadcast: () => broadcast,
|
|
64
61
|
cache: () => cache,
|
|
@@ -72,7 +69,6 @@ __export(index_exports, {
|
|
|
72
69
|
composeWithOptions: () => composeWithOptions,
|
|
73
70
|
conditional: () => conditional,
|
|
74
71
|
configureLangSmith: () => configureLangSmith,
|
|
75
|
-
createActivateSkillTool: () => createActivateSkillTool,
|
|
76
72
|
createAlertManager: () => createAlertManager,
|
|
77
73
|
createApprovalRequiredInterrupt: () => createApprovalRequiredInterrupt,
|
|
78
74
|
createAuditLogger: () => createAuditLogger,
|
|
@@ -101,13 +97,11 @@ __export(index_exports, {
|
|
|
101
97
|
createParallelWorkflow: () => createParallelWorkflow,
|
|
102
98
|
createProfiler: () => createProfiler,
|
|
103
99
|
createProgressTracker: () => createProgressTracker,
|
|
104
|
-
createReadSkillResourceTool: () => createReadSkillResourceTool,
|
|
105
100
|
createSSEFormatter: () => createSSEFormatter,
|
|
106
101
|
createSequentialWorkflow: () => createSequentialWorkflow,
|
|
107
102
|
createSharedCache: () => createSharedCache,
|
|
108
103
|
createSharedConcurrencyController: () => createSharedConcurrencyController,
|
|
109
104
|
createSharedRateLimiter: () => createSharedRateLimiter,
|
|
110
|
-
createSkillActivationTools: () => createSkillActivationTools,
|
|
111
105
|
createSqliteCheckpointer: () => createSqliteCheckpointer,
|
|
112
106
|
createStateAnnotation: () => createStateAnnotation,
|
|
113
107
|
createSubgraph: () => createSubgraph,
|
|
@@ -118,8 +112,6 @@ __export(index_exports, {
|
|
|
118
112
|
createToolUnsafe: () => createToolUnsafe,
|
|
119
113
|
createWebSocketHandler: () => createWebSocketHandler,
|
|
120
114
|
development: () => development,
|
|
121
|
-
evaluateTrustPolicy: () => evaluateTrustPolicy,
|
|
122
|
-
expandHome: () => expandHome,
|
|
123
115
|
filter: () => filter,
|
|
124
116
|
formatAgentResumedEvent: () => formatAgentResumedEvent,
|
|
125
117
|
formatAgentWaitingEvent: () => formatAgentWaitingEvent,
|
|
@@ -139,26 +131,20 @@ __export(index_exports, {
|
|
|
139
131
|
isCustomInterrupt: () => isCustomInterrupt,
|
|
140
132
|
isHumanRequestInterrupt: () => isHumanRequestInterrupt,
|
|
141
133
|
isMemoryCheckpointer: () => isMemoryCheckpointer,
|
|
142
|
-
isScriptResource: () => isScriptResource,
|
|
143
134
|
isTracingEnabled: () => isTracingEnabled,
|
|
144
135
|
loadPrompt: () => loadPrompt,
|
|
145
136
|
map: () => map,
|
|
146
137
|
merge: () => merge,
|
|
147
138
|
mergeState: () => mergeState,
|
|
148
|
-
normalizeRootConfig: () => normalizeRootConfig,
|
|
149
139
|
parallel: () => parallel,
|
|
150
140
|
parseSSEEvent: () => parseSSEEvent,
|
|
151
|
-
parseSkillContent: () => parseSkillContent,
|
|
152
141
|
presets: () => presets,
|
|
153
142
|
production: () => production,
|
|
154
143
|
reduce: () => reduce,
|
|
155
144
|
renderTemplate: () => renderTemplate,
|
|
156
|
-
resolveResourcePath: () => resolveResourcePath,
|
|
157
145
|
retry: () => retry,
|
|
158
146
|
safeValidateSchemaDescriptions: () => safeValidateSchemaDescriptions,
|
|
159
147
|
sanitizeValue: () => sanitizeValue,
|
|
160
|
-
scanAllSkillRoots: () => scanAllSkillRoots,
|
|
161
|
-
scanSkillRoot: () => scanSkillRoot,
|
|
162
148
|
sendMessage: () => sendMessage,
|
|
163
149
|
sequential: () => sequential,
|
|
164
150
|
sequentialBuilder: () => sequentialBuilder,
|
|
@@ -170,9 +156,6 @@ __export(index_exports, {
|
|
|
170
156
|
toLangChainTools: () => toLangChainTools,
|
|
171
157
|
toolBuilder: () => toolBuilder,
|
|
172
158
|
validateSchemaDescriptions: () => validateSchemaDescriptions,
|
|
173
|
-
validateSkillDescription: () => validateSkillDescription,
|
|
174
|
-
validateSkillName: () => validateSkillName,
|
|
175
|
-
validateSkillNameMatchesDir: () => validateSkillNameMatchesDir,
|
|
176
159
|
validateState: () => validateState,
|
|
177
160
|
validateTool: () => validateTool,
|
|
178
161
|
validateToolMetadata: () => validateToolMetadata,
|
|
@@ -1598,7 +1581,7 @@ function createToolExecutor(config = {}) {
|
|
|
1598
1581
|
}
|
|
1599
1582
|
if (attempt < policy.maxAttempts) {
|
|
1600
1583
|
const delay = calculateBackoff(attempt, policy);
|
|
1601
|
-
await new Promise((
|
|
1584
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1602
1585
|
}
|
|
1603
1586
|
}
|
|
1604
1587
|
}
|
|
@@ -1655,12 +1638,12 @@ function createToolExecutor(config = {}) {
|
|
|
1655
1638
|
}
|
|
1656
1639
|
async function execute(tool, input, options = {}) {
|
|
1657
1640
|
const priority = options.priority || priorityFn(tool);
|
|
1658
|
-
return new Promise((
|
|
1641
|
+
return new Promise((resolve, reject) => {
|
|
1659
1642
|
queue.push({
|
|
1660
1643
|
tool,
|
|
1661
1644
|
input,
|
|
1662
1645
|
priority,
|
|
1663
|
-
resolve
|
|
1646
|
+
resolve,
|
|
1664
1647
|
reject,
|
|
1665
1648
|
timestamp: Date.now()
|
|
1666
1649
|
});
|
|
@@ -1960,7 +1943,7 @@ function retry(tool, options = {}) {
|
|
|
1960
1943
|
lastError = error;
|
|
1961
1944
|
if (attempt < maxAttempts) {
|
|
1962
1945
|
const waitTime = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
|
|
1963
|
-
await new Promise((
|
|
1946
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1964
1947
|
}
|
|
1965
1948
|
}
|
|
1966
1949
|
}
|
|
@@ -2021,7 +2004,7 @@ function createMockTool(config) {
|
|
|
2021
2004
|
const startTime = Date.now();
|
|
2022
2005
|
if (latency) {
|
|
2023
2006
|
const delay = typeof latency === "number" ? latency : Math.random() * (latency.max - latency.min) + latency.min;
|
|
2024
|
-
await new Promise((
|
|
2007
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2025
2008
|
}
|
|
2026
2009
|
if (errorRate > 0 && Math.random() < errorRate) {
|
|
2027
2010
|
const error2 = new Error(`Random error from ${name}`);
|
|
@@ -2092,8 +2075,8 @@ function createToolSimulator(config) {
|
|
|
2092
2075
|
if (!latency) return 0;
|
|
2093
2076
|
const u1 = Math.random();
|
|
2094
2077
|
const u2 = Math.random();
|
|
2095
|
-
const
|
|
2096
|
-
return Math.max(0, latency.mean +
|
|
2078
|
+
const z3 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
2079
|
+
return Math.max(0, latency.mean + z3 * latency.stddev);
|
|
2097
2080
|
}
|
|
2098
2081
|
return {
|
|
2099
2082
|
execute: async (toolName, input) => {
|
|
@@ -2104,7 +2087,7 @@ function createToolSimulator(config) {
|
|
|
2104
2087
|
const startTime = Date.now();
|
|
2105
2088
|
if (latency) {
|
|
2106
2089
|
const delay = generateLatency();
|
|
2107
|
-
await new Promise((
|
|
2090
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2108
2091
|
}
|
|
2109
2092
|
if (errorRate > 0 && Math.random() < errorRate) {
|
|
2110
2093
|
const error = new Error(`Simulated error from ${toolName}`);
|
|
@@ -2462,7 +2445,7 @@ function calculateDelay(attempt, strategy, initialDelay, maxDelay) {
|
|
|
2462
2445
|
return Math.min(delay, maxDelay);
|
|
2463
2446
|
}
|
|
2464
2447
|
function sleep(ms) {
|
|
2465
|
-
return new Promise((
|
|
2448
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2466
2449
|
}
|
|
2467
2450
|
function withRetry(node, options = {}) {
|
|
2468
2451
|
const {
|
|
@@ -2708,14 +2691,14 @@ var withLogging = (options) => {
|
|
|
2708
2691
|
onComplete,
|
|
2709
2692
|
onError
|
|
2710
2693
|
} = options;
|
|
2711
|
-
const
|
|
2694
|
+
const logger5 = providedLogger || createLogger(name, { level });
|
|
2712
2695
|
return (node) => {
|
|
2713
2696
|
return async (state) => {
|
|
2714
2697
|
const startTime = Date.now();
|
|
2715
2698
|
try {
|
|
2716
2699
|
if (logInput) {
|
|
2717
2700
|
const data = extractData ? extractData(state) : { state };
|
|
2718
|
-
|
|
2701
|
+
logger5.info("Node execution started", data);
|
|
2719
2702
|
}
|
|
2720
2703
|
if (onStart) {
|
|
2721
2704
|
onStart(state);
|
|
@@ -2725,9 +2708,9 @@ var withLogging = (options) => {
|
|
|
2725
2708
|
if (logOutput) {
|
|
2726
2709
|
const data = extractData ? extractData(result) : { result };
|
|
2727
2710
|
if (logDuration) {
|
|
2728
|
-
|
|
2711
|
+
logger5.info(`Node execution completed (${duration}ms)`, data);
|
|
2729
2712
|
} else {
|
|
2730
|
-
|
|
2713
|
+
logger5.info("Node execution completed", data);
|
|
2731
2714
|
}
|
|
2732
2715
|
}
|
|
2733
2716
|
if (onComplete) {
|
|
@@ -2738,7 +2721,7 @@ var withLogging = (options) => {
|
|
|
2738
2721
|
const duration = Date.now() - startTime;
|
|
2739
2722
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
2740
2723
|
if (logErrors) {
|
|
2741
|
-
|
|
2724
|
+
logger5.error(`Node execution failed (${duration}ms)`, {
|
|
2742
2725
|
error: err.message,
|
|
2743
2726
|
stack: err.stack
|
|
2744
2727
|
});
|
|
@@ -2774,7 +2757,7 @@ function withLogging2(options) {
|
|
|
2774
2757
|
function production(node, options) {
|
|
2775
2758
|
const {
|
|
2776
2759
|
nodeName,
|
|
2777
|
-
logger:
|
|
2760
|
+
logger: logger5,
|
|
2778
2761
|
enableMetrics = true,
|
|
2779
2762
|
enableTracing = true,
|
|
2780
2763
|
enableRetry = true,
|
|
@@ -2782,7 +2765,7 @@ function production(node, options) {
|
|
|
2782
2765
|
retryOptions = {},
|
|
2783
2766
|
errorOptions = {}
|
|
2784
2767
|
} = options;
|
|
2785
|
-
const actualLogger =
|
|
2768
|
+
const actualLogger = logger5 || createLogger(nodeName, { level: "info" /* INFO */ });
|
|
2786
2769
|
const middleware = [];
|
|
2787
2770
|
middleware.push(
|
|
2788
2771
|
withLogging2({
|
|
@@ -2846,9 +2829,9 @@ function development(node, options) {
|
|
|
2846
2829
|
const {
|
|
2847
2830
|
nodeName,
|
|
2848
2831
|
verbose = true,
|
|
2849
|
-
logger:
|
|
2832
|
+
logger: logger5
|
|
2850
2833
|
} = options;
|
|
2851
|
-
const actualLogger =
|
|
2834
|
+
const actualLogger = logger5 || createLogger(nodeName, { level: "debug" /* DEBUG */ });
|
|
2852
2835
|
return withLogging2({
|
|
2853
2836
|
logger: actualLogger,
|
|
2854
2837
|
name: nodeName,
|
|
@@ -2872,7 +2855,7 @@ function testing(node, options) {
|
|
|
2872
2855
|
invocations.push(state);
|
|
2873
2856
|
}
|
|
2874
2857
|
if (delay > 0) {
|
|
2875
|
-
await new Promise((
|
|
2858
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2876
2859
|
}
|
|
2877
2860
|
if (simulateError) {
|
|
2878
2861
|
throw simulateError;
|
|
@@ -3331,12 +3314,12 @@ var ConcurrencyController = class {
|
|
|
3331
3314
|
}
|
|
3332
3315
|
throw new Error(`Queue is full (max size: ${this.maxQueueSize})`);
|
|
3333
3316
|
}
|
|
3334
|
-
return new Promise((
|
|
3317
|
+
return new Promise((resolve, reject) => {
|
|
3335
3318
|
const task = {
|
|
3336
3319
|
state,
|
|
3337
3320
|
priority,
|
|
3338
3321
|
executor,
|
|
3339
|
-
resolve
|
|
3322
|
+
resolve,
|
|
3340
3323
|
reject,
|
|
3341
3324
|
timestamp: Date.now()
|
|
3342
3325
|
};
|
|
@@ -3801,7 +3784,7 @@ async function* throttle(stream, options) {
|
|
|
3801
3784
|
const now = Date.now();
|
|
3802
3785
|
const timeSinceLastEmit = now - lastEmit;
|
|
3803
3786
|
if (timeSinceLastEmit < interval) {
|
|
3804
|
-
await new Promise((
|
|
3787
|
+
await new Promise((resolve) => setTimeout(resolve, interval - timeSinceLastEmit));
|
|
3805
3788
|
}
|
|
3806
3789
|
lastEmit = Date.now();
|
|
3807
3790
|
yield item;
|
|
@@ -4316,17 +4299,17 @@ var ConnectionPool = class {
|
|
|
4316
4299
|
this.options.onAcquire?.(connection);
|
|
4317
4300
|
return connection;
|
|
4318
4301
|
}
|
|
4319
|
-
return new Promise((
|
|
4302
|
+
return new Promise((resolve, reject) => {
|
|
4320
4303
|
const timeout2 = poolConfig.acquireTimeout || 3e4;
|
|
4321
4304
|
const timer = setTimeout(() => {
|
|
4322
|
-
const index = this.pending.findIndex((p) => p.resolve ===
|
|
4305
|
+
const index = this.pending.findIndex((p) => p.resolve === resolve);
|
|
4323
4306
|
if (index !== -1) {
|
|
4324
4307
|
this.pending.splice(index, 1);
|
|
4325
4308
|
this.stats.pending--;
|
|
4326
4309
|
}
|
|
4327
4310
|
reject(new Error("Acquire timeout"));
|
|
4328
4311
|
}, timeout2);
|
|
4329
|
-
this.pending.push({ resolve
|
|
4312
|
+
this.pending.push({ resolve, reject, timeout: timer });
|
|
4330
4313
|
this.stats.pending++;
|
|
4331
4314
|
});
|
|
4332
4315
|
}
|
|
@@ -4410,7 +4393,7 @@ var ConnectionPool = class {
|
|
|
4410
4393
|
this.pending = [];
|
|
4411
4394
|
this.stats.pending = 0;
|
|
4412
4395
|
while (this.connections.some((c) => c.inUse)) {
|
|
4413
|
-
await new Promise((
|
|
4396
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4414
4397
|
}
|
|
4415
4398
|
}
|
|
4416
4399
|
async clear() {
|
|
@@ -4737,10 +4720,10 @@ var BatchProcessor = class {
|
|
|
4737
4720
|
failedBatches: 0
|
|
4738
4721
|
};
|
|
4739
4722
|
add(input) {
|
|
4740
|
-
return new Promise((
|
|
4723
|
+
return new Promise((resolve, reject) => {
|
|
4741
4724
|
this.pending.push({
|
|
4742
4725
|
input,
|
|
4743
|
-
resolve
|
|
4726
|
+
resolve,
|
|
4744
4727
|
reject,
|
|
4745
4728
|
addedAt: Date.now()
|
|
4746
4729
|
});
|
|
@@ -5500,841 +5483,6 @@ function loadPrompt(promptName, options = {}, promptsDir) {
|
|
|
5500
5483
|
);
|
|
5501
5484
|
}
|
|
5502
5485
|
}
|
|
5503
|
-
|
|
5504
|
-
// src/skills/types.ts
|
|
5505
|
-
var TrustPolicyReason = /* @__PURE__ */ ((TrustPolicyReason2) => {
|
|
5506
|
-
TrustPolicyReason2["NOT_SCRIPT"] = "not-script";
|
|
5507
|
-
TrustPolicyReason2["WORKSPACE_TRUST"] = "workspace-trust";
|
|
5508
|
-
TrustPolicyReason2["TRUSTED_ROOT"] = "trusted-root";
|
|
5509
|
-
TrustPolicyReason2["UNTRUSTED_SCRIPT_DENIED"] = "untrusted-script-denied";
|
|
5510
|
-
TrustPolicyReason2["UNTRUSTED_SCRIPT_ALLOWED"] = "untrusted-script-allowed-override";
|
|
5511
|
-
TrustPolicyReason2["UNKNOWN_TRUST_LEVEL"] = "unknown-trust-level";
|
|
5512
|
-
return TrustPolicyReason2;
|
|
5513
|
-
})(TrustPolicyReason || {});
|
|
5514
|
-
var SkillRegistryEvent = /* @__PURE__ */ ((SkillRegistryEvent2) => {
|
|
5515
|
-
SkillRegistryEvent2["SKILL_DISCOVERED"] = "skill:discovered";
|
|
5516
|
-
SkillRegistryEvent2["SKILL_WARNING"] = "skill:warning";
|
|
5517
|
-
SkillRegistryEvent2["SKILL_ACTIVATED"] = "skill:activated";
|
|
5518
|
-
SkillRegistryEvent2["SKILL_RESOURCE_LOADED"] = "skill:resource-loaded";
|
|
5519
|
-
SkillRegistryEvent2["TRUST_POLICY_DENIED"] = "trust:policy-denied";
|
|
5520
|
-
SkillRegistryEvent2["TRUST_POLICY_ALLOWED"] = "trust:policy-allowed";
|
|
5521
|
-
return SkillRegistryEvent2;
|
|
5522
|
-
})(SkillRegistryEvent || {});
|
|
5523
|
-
|
|
5524
|
-
// src/skills/parser.ts
|
|
5525
|
-
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
5526
|
-
var SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
5527
|
-
var SKILL_NAME_MAX_LENGTH = 64;
|
|
5528
|
-
var SKILL_DESCRIPTION_MAX_LENGTH = 1024;
|
|
5529
|
-
function validateSkillName(name) {
|
|
5530
|
-
const errors = [];
|
|
5531
|
-
if (name === void 0 || name === null) {
|
|
5532
|
-
errors.push({ field: "name", message: "name is required" });
|
|
5533
|
-
return errors;
|
|
5534
|
-
}
|
|
5535
|
-
if (typeof name !== "string") {
|
|
5536
|
-
errors.push({ field: "name", message: "name must be a string" });
|
|
5537
|
-
return errors;
|
|
5538
|
-
}
|
|
5539
|
-
if (name.length === 0) {
|
|
5540
|
-
errors.push({ field: "name", message: "name must not be empty" });
|
|
5541
|
-
return errors;
|
|
5542
|
-
}
|
|
5543
|
-
if (name.length > SKILL_NAME_MAX_LENGTH) {
|
|
5544
|
-
errors.push({
|
|
5545
|
-
field: "name",
|
|
5546
|
-
message: `name must be at most ${SKILL_NAME_MAX_LENGTH} characters (got ${name.length})`
|
|
5547
|
-
});
|
|
5548
|
-
return errors;
|
|
5549
|
-
}
|
|
5550
|
-
if (!SKILL_NAME_PATTERN.test(name)) {
|
|
5551
|
-
errors.push({
|
|
5552
|
-
field: "name",
|
|
5553
|
-
message: "name must be lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens"
|
|
5554
|
-
});
|
|
5555
|
-
}
|
|
5556
|
-
if (name.includes("--")) {
|
|
5557
|
-
errors.push({
|
|
5558
|
-
field: "name",
|
|
5559
|
-
message: "name must not contain consecutive hyphens"
|
|
5560
|
-
});
|
|
5561
|
-
}
|
|
5562
|
-
return errors;
|
|
5563
|
-
}
|
|
5564
|
-
function validateSkillDescription(description) {
|
|
5565
|
-
const errors = [];
|
|
5566
|
-
if (description === void 0 || description === null) {
|
|
5567
|
-
errors.push({ field: "description", message: "description is required" });
|
|
5568
|
-
return errors;
|
|
5569
|
-
}
|
|
5570
|
-
if (typeof description !== "string") {
|
|
5571
|
-
errors.push({ field: "description", message: "description must be a string" });
|
|
5572
|
-
return errors;
|
|
5573
|
-
}
|
|
5574
|
-
if (description.trim().length === 0) {
|
|
5575
|
-
errors.push({ field: "description", message: "description must not be empty" });
|
|
5576
|
-
return errors;
|
|
5577
|
-
}
|
|
5578
|
-
if (description.length > SKILL_DESCRIPTION_MAX_LENGTH) {
|
|
5579
|
-
errors.push({
|
|
5580
|
-
field: "description",
|
|
5581
|
-
message: `description must be at most ${SKILL_DESCRIPTION_MAX_LENGTH} characters (got ${description.length})`
|
|
5582
|
-
});
|
|
5583
|
-
}
|
|
5584
|
-
return errors;
|
|
5585
|
-
}
|
|
5586
|
-
function validateSkillNameMatchesDir(name, dirName) {
|
|
5587
|
-
if (name !== dirName) {
|
|
5588
|
-
return [{
|
|
5589
|
-
field: "name",
|
|
5590
|
-
message: `name "${name}" must match parent directory name "${dirName}"`
|
|
5591
|
-
}];
|
|
5592
|
-
}
|
|
5593
|
-
return [];
|
|
5594
|
-
}
|
|
5595
|
-
function parseSkillContent(content, dirName) {
|
|
5596
|
-
let parsed;
|
|
5597
|
-
try {
|
|
5598
|
-
parsed = (0, import_gray_matter.default)(content);
|
|
5599
|
-
} catch (err) {
|
|
5600
|
-
return {
|
|
5601
|
-
success: false,
|
|
5602
|
-
error: `Failed to parse frontmatter: ${err instanceof Error ? err.message : String(err)}`
|
|
5603
|
-
};
|
|
5604
|
-
}
|
|
5605
|
-
const data = parsed.data;
|
|
5606
|
-
const errors = [
|
|
5607
|
-
...validateSkillName(data.name),
|
|
5608
|
-
...validateSkillDescription(data.description)
|
|
5609
|
-
];
|
|
5610
|
-
if (typeof data.name === "string" && data.name.length > 0 && SKILL_NAME_PATTERN.test(data.name)) {
|
|
5611
|
-
errors.push(...validateSkillNameMatchesDir(data.name, dirName));
|
|
5612
|
-
}
|
|
5613
|
-
if (errors.length > 0) {
|
|
5614
|
-
return {
|
|
5615
|
-
success: false,
|
|
5616
|
-
error: errors.map((e) => `${e.field}: ${e.message}`).join("; ")
|
|
5617
|
-
};
|
|
5618
|
-
}
|
|
5619
|
-
const metadata = {
|
|
5620
|
-
name: data.name,
|
|
5621
|
-
description: data.description
|
|
5622
|
-
};
|
|
5623
|
-
if (data.license !== void 0) {
|
|
5624
|
-
metadata.license = String(data.license);
|
|
5625
|
-
}
|
|
5626
|
-
if (Array.isArray(data.compatibility)) {
|
|
5627
|
-
metadata.compatibility = data.compatibility.map(String);
|
|
5628
|
-
}
|
|
5629
|
-
if (data.metadata !== void 0 && typeof data.metadata === "object" && data.metadata !== null) {
|
|
5630
|
-
metadata.metadata = data.metadata;
|
|
5631
|
-
}
|
|
5632
|
-
if (Array.isArray(data["allowed-tools"])) {
|
|
5633
|
-
metadata.allowedTools = data["allowed-tools"].map(String);
|
|
5634
|
-
}
|
|
5635
|
-
return {
|
|
5636
|
-
success: true,
|
|
5637
|
-
metadata,
|
|
5638
|
-
body: parsed.content
|
|
5639
|
-
};
|
|
5640
|
-
}
|
|
5641
|
-
|
|
5642
|
-
// src/skills/scanner.ts
|
|
5643
|
-
var import_node_fs = require("fs");
|
|
5644
|
-
var import_node_path = require("path");
|
|
5645
|
-
var import_node_os = require("os");
|
|
5646
|
-
var logger5 = createLogger("agentforge:core:skills:scanner", { level: "info" /* INFO */ });
|
|
5647
|
-
function expandHome(p) {
|
|
5648
|
-
if (p.startsWith("~/") || p === "~") {
|
|
5649
|
-
return (0, import_node_path.resolve)((0, import_node_os.homedir)(), p.slice(2));
|
|
5650
|
-
}
|
|
5651
|
-
return p;
|
|
5652
|
-
}
|
|
5653
|
-
function scanSkillRoot(rootPath) {
|
|
5654
|
-
const resolvedRoot = (0, import_node_path.resolve)(expandHome(rootPath));
|
|
5655
|
-
const candidates = [];
|
|
5656
|
-
if (!(0, import_node_fs.existsSync)(resolvedRoot)) {
|
|
5657
|
-
logger5.debug("Skill root does not exist, skipping", { rootPath: resolvedRoot });
|
|
5658
|
-
return candidates;
|
|
5659
|
-
}
|
|
5660
|
-
let entries;
|
|
5661
|
-
try {
|
|
5662
|
-
entries = (0, import_node_fs.readdirSync)(resolvedRoot);
|
|
5663
|
-
} catch (err) {
|
|
5664
|
-
logger5.warn("Failed to read skill root directory", {
|
|
5665
|
-
rootPath: resolvedRoot,
|
|
5666
|
-
error: err instanceof Error ? err.message : String(err)
|
|
5667
|
-
});
|
|
5668
|
-
return candidates;
|
|
5669
|
-
}
|
|
5670
|
-
for (const entry of entries) {
|
|
5671
|
-
const entryPath = (0, import_node_path.resolve)(resolvedRoot, entry);
|
|
5672
|
-
let stat;
|
|
5673
|
-
try {
|
|
5674
|
-
stat = (0, import_node_fs.statSync)(entryPath);
|
|
5675
|
-
} catch {
|
|
5676
|
-
continue;
|
|
5677
|
-
}
|
|
5678
|
-
if (!stat.isDirectory()) {
|
|
5679
|
-
continue;
|
|
5680
|
-
}
|
|
5681
|
-
const skillMdPath = (0, import_node_path.resolve)(entryPath, "SKILL.md");
|
|
5682
|
-
if (!(0, import_node_fs.existsSync)(skillMdPath)) {
|
|
5683
|
-
continue;
|
|
5684
|
-
}
|
|
5685
|
-
try {
|
|
5686
|
-
const content = (0, import_node_fs.readFileSync)(skillMdPath, "utf-8");
|
|
5687
|
-
candidates.push({
|
|
5688
|
-
skillPath: entryPath,
|
|
5689
|
-
dirName: (0, import_node_path.basename)(entryPath),
|
|
5690
|
-
content,
|
|
5691
|
-
rootPath: resolvedRoot
|
|
5692
|
-
});
|
|
5693
|
-
} catch (err) {
|
|
5694
|
-
logger5.warn("Failed to read SKILL.md", {
|
|
5695
|
-
path: skillMdPath,
|
|
5696
|
-
error: err instanceof Error ? err.message : String(err)
|
|
5697
|
-
});
|
|
5698
|
-
}
|
|
5699
|
-
}
|
|
5700
|
-
logger5.debug("Scanned skill root", {
|
|
5701
|
-
rootPath: resolvedRoot,
|
|
5702
|
-
candidatesFound: candidates.length
|
|
5703
|
-
});
|
|
5704
|
-
return candidates;
|
|
5705
|
-
}
|
|
5706
|
-
function scanAllSkillRoots(skillRoots) {
|
|
5707
|
-
const allCandidates = [];
|
|
5708
|
-
for (const root of skillRoots) {
|
|
5709
|
-
const candidates = scanSkillRoot(root);
|
|
5710
|
-
allCandidates.push(...candidates);
|
|
5711
|
-
}
|
|
5712
|
-
logger5.info("Skill discovery complete", {
|
|
5713
|
-
rootsScanned: skillRoots.length,
|
|
5714
|
-
totalCandidates: allCandidates.length
|
|
5715
|
-
});
|
|
5716
|
-
return allCandidates;
|
|
5717
|
-
}
|
|
5718
|
-
|
|
5719
|
-
// src/skills/activation.ts
|
|
5720
|
-
var import_node_fs2 = require("fs");
|
|
5721
|
-
var import_node_path2 = require("path");
|
|
5722
|
-
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
5723
|
-
var import_zod3 = require("zod");
|
|
5724
|
-
|
|
5725
|
-
// src/skills/trust.ts
|
|
5726
|
-
var SCRIPT_PATH_PREFIX = "scripts/";
|
|
5727
|
-
var SCRIPT_PATH_EXACT = "scripts";
|
|
5728
|
-
function normalizeRootConfig(root) {
|
|
5729
|
-
if (typeof root === "string") {
|
|
5730
|
-
return { path: root, trust: "untrusted" };
|
|
5731
|
-
}
|
|
5732
|
-
return root;
|
|
5733
|
-
}
|
|
5734
|
-
function isScriptResource(resourcePath) {
|
|
5735
|
-
let normalized = resourcePath.trim().replace(/\\/g, "/");
|
|
5736
|
-
normalized = normalized.replace(/\/+/g, "/");
|
|
5737
|
-
while (normalized.startsWith("./")) {
|
|
5738
|
-
normalized = normalized.slice(2);
|
|
5739
|
-
}
|
|
5740
|
-
const lower = normalized.toLowerCase();
|
|
5741
|
-
return lower === SCRIPT_PATH_EXACT || lower.startsWith(SCRIPT_PATH_PREFIX);
|
|
5742
|
-
}
|
|
5743
|
-
function evaluateTrustPolicy(resourcePath, trustLevel, allowUntrustedScripts = false) {
|
|
5744
|
-
if (!isScriptResource(resourcePath)) {
|
|
5745
|
-
return {
|
|
5746
|
-
allowed: true,
|
|
5747
|
-
reason: "not-script" /* NOT_SCRIPT */,
|
|
5748
|
-
message: "Resource is not a script \u2014 no trust check required"
|
|
5749
|
-
};
|
|
5750
|
-
}
|
|
5751
|
-
switch (trustLevel) {
|
|
5752
|
-
case "workspace":
|
|
5753
|
-
return {
|
|
5754
|
-
allowed: true,
|
|
5755
|
-
reason: "workspace-trust" /* WORKSPACE_TRUST */,
|
|
5756
|
-
message: "Script allowed \u2014 skill root has workspace trust"
|
|
5757
|
-
};
|
|
5758
|
-
case "trusted":
|
|
5759
|
-
return {
|
|
5760
|
-
allowed: true,
|
|
5761
|
-
reason: "trusted-root" /* TRUSTED_ROOT */,
|
|
5762
|
-
message: "Script allowed \u2014 skill root is explicitly trusted"
|
|
5763
|
-
};
|
|
5764
|
-
case "untrusted":
|
|
5765
|
-
if (allowUntrustedScripts) {
|
|
5766
|
-
return {
|
|
5767
|
-
allowed: true,
|
|
5768
|
-
reason: "untrusted-script-allowed-override" /* UNTRUSTED_SCRIPT_ALLOWED */,
|
|
5769
|
-
message: "Script from untrusted root allowed via allowUntrustedScripts override"
|
|
5770
|
-
};
|
|
5771
|
-
}
|
|
5772
|
-
return {
|
|
5773
|
-
allowed: false,
|
|
5774
|
-
reason: "untrusted-script-denied" /* UNTRUSTED_SCRIPT_DENIED */,
|
|
5775
|
-
message: `Script access denied \u2014 skill root is untrusted. Scripts from untrusted roots are blocked by default for security. To allow, set 'allowUntrustedScripts: true' in SkillRegistryConfig or promote the skill root to 'trusted' or 'workspace' trust level.`
|
|
5776
|
-
};
|
|
5777
|
-
default:
|
|
5778
|
-
return {
|
|
5779
|
-
allowed: false,
|
|
5780
|
-
reason: "unknown-trust-level" /* UNKNOWN_TRUST_LEVEL */,
|
|
5781
|
-
message: `Script access denied \u2014 trust level "${trustLevel}" is unknown and is treated as untrusted for security.`
|
|
5782
|
-
};
|
|
5783
|
-
}
|
|
5784
|
-
}
|
|
5785
|
-
|
|
5786
|
-
// src/skills/activation.ts
|
|
5787
|
-
var logger6 = createLogger("agentforge:core:skills:activation", { level: "info" /* INFO */ });
|
|
5788
|
-
var activateSkillSchema = import_zod3.z.object({
|
|
5789
|
-
name: import_zod3.z.string().describe('The name of the skill to activate (e.g., "code-review")')
|
|
5790
|
-
});
|
|
5791
|
-
var readSkillResourceSchema = import_zod3.z.object({
|
|
5792
|
-
name: import_zod3.z.string().describe("The name of the skill that owns the resource"),
|
|
5793
|
-
path: import_zod3.z.string().describe('Relative path to the resource file within the skill directory (e.g., "references/GUIDE.md", "scripts/setup.sh")')
|
|
5794
|
-
});
|
|
5795
|
-
function resolveResourcePath(skillPath, resourcePath) {
|
|
5796
|
-
if ((0, import_node_path2.isAbsolute)(resourcePath)) {
|
|
5797
|
-
return { success: false, error: "Absolute resource paths are not allowed" };
|
|
5798
|
-
}
|
|
5799
|
-
const segments = resourcePath.split(/[/\\]/);
|
|
5800
|
-
if (segments.some((seg) => seg === "..")) {
|
|
5801
|
-
return { success: false, error: "Path traversal is not allowed \u2014 resource paths must stay within the skill directory" };
|
|
5802
|
-
}
|
|
5803
|
-
const resolvedPath = (0, import_node_path2.resolve)(skillPath, resourcePath);
|
|
5804
|
-
const resolvedSkillPath = (0, import_node_path2.resolve)(skillPath);
|
|
5805
|
-
const rel = (0, import_node_path2.relative)(resolvedSkillPath, resolvedPath);
|
|
5806
|
-
if (rel.startsWith("..") || (0, import_node_path2.resolve)(resolvedSkillPath, rel) !== resolvedPath) {
|
|
5807
|
-
return { success: false, error: "Path traversal is not allowed \u2014 resource paths must stay within the skill directory" };
|
|
5808
|
-
}
|
|
5809
|
-
try {
|
|
5810
|
-
const realSkillRoot = (0, import_node_fs2.realpathSync)(resolvedSkillPath);
|
|
5811
|
-
const realTarget = (0, import_node_fs2.realpathSync)(resolvedPath);
|
|
5812
|
-
const realRel = (0, import_node_path2.relative)(realSkillRoot, realTarget);
|
|
5813
|
-
if (realRel.startsWith("..") || (0, import_node_path2.isAbsolute)(realRel)) {
|
|
5814
|
-
return { success: false, error: "Symlink target escapes the skill directory \u2014 access denied" };
|
|
5815
|
-
}
|
|
5816
|
-
} catch {
|
|
5817
|
-
}
|
|
5818
|
-
return { success: true, resolvedPath };
|
|
5819
|
-
}
|
|
5820
|
-
function createActivateSkillTool(registry) {
|
|
5821
|
-
return new ToolBuilder().name("activate-skill").description(
|
|
5822
|
-
"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."
|
|
5823
|
-
).category("skills" /* SKILLS */).tags(["skill", "activation", "agent-skills"]).schema(activateSkillSchema).implement(async ({ name }) => {
|
|
5824
|
-
const skill = registry.get(name);
|
|
5825
|
-
if (!skill) {
|
|
5826
|
-
const availableNames = registry.getNames();
|
|
5827
|
-
const suggestion = availableNames.length > 0 ? ` Available skills: ${availableNames.join(", ")}` : " No skills are currently registered.";
|
|
5828
|
-
const errorMsg = `Skill "${name}" not found.${suggestion}`;
|
|
5829
|
-
logger6.warn("Skill activation failed \u2014 not found", { name, availableCount: availableNames.length });
|
|
5830
|
-
return errorMsg;
|
|
5831
|
-
}
|
|
5832
|
-
const skillMdPath = (0, import_node_path2.resolve)(skill.skillPath, "SKILL.md");
|
|
5833
|
-
try {
|
|
5834
|
-
const content = (0, import_node_fs2.readFileSync)(skillMdPath, "utf-8");
|
|
5835
|
-
const body = extractBody(content);
|
|
5836
|
-
logger6.info("Skill activated", {
|
|
5837
|
-
name: skill.metadata.name,
|
|
5838
|
-
skillPath: skill.skillPath,
|
|
5839
|
-
bodyLength: body.length
|
|
5840
|
-
});
|
|
5841
|
-
registry.emitEvent("skill:activated" /* SKILL_ACTIVATED */, {
|
|
5842
|
-
name: skill.metadata.name,
|
|
5843
|
-
skillPath: skill.skillPath,
|
|
5844
|
-
bodyLength: body.length
|
|
5845
|
-
});
|
|
5846
|
-
return body;
|
|
5847
|
-
} catch (error) {
|
|
5848
|
-
const errorMsg = `Failed to read skill "${name}" instructions: ${error instanceof Error ? error.message : String(error)}`;
|
|
5849
|
-
logger6.error("Skill activation failed \u2014 read error", {
|
|
5850
|
-
name,
|
|
5851
|
-
skillPath: skill.skillPath,
|
|
5852
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5853
|
-
});
|
|
5854
|
-
return errorMsg;
|
|
5855
|
-
}
|
|
5856
|
-
}).build();
|
|
5857
|
-
}
|
|
5858
|
-
function createReadSkillResourceTool(registry) {
|
|
5859
|
-
return new ToolBuilder().name("read-skill-resource").description(
|
|
5860
|
-
"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."
|
|
5861
|
-
).category("skills" /* SKILLS */).tags(["skill", "resource", "agent-skills"]).schema(readSkillResourceSchema).implement(async ({ name, path: resourcePath }) => {
|
|
5862
|
-
const skill = registry.get(name);
|
|
5863
|
-
if (!skill) {
|
|
5864
|
-
const availableNames = registry.getNames();
|
|
5865
|
-
const suggestion = availableNames.length > 0 ? ` Available skills: ${availableNames.join(", ")}` : " No skills are currently registered.";
|
|
5866
|
-
const errorMsg = `Skill "${name}" not found.${suggestion}`;
|
|
5867
|
-
logger6.warn("Skill resource load failed \u2014 skill not found", { name, resourcePath });
|
|
5868
|
-
return errorMsg;
|
|
5869
|
-
}
|
|
5870
|
-
const pathResult = resolveResourcePath(skill.skillPath, resourcePath);
|
|
5871
|
-
if (!pathResult.success) {
|
|
5872
|
-
logger6.warn("Skill resource load blocked \u2014 path traversal", {
|
|
5873
|
-
name,
|
|
5874
|
-
resourcePath,
|
|
5875
|
-
error: pathResult.error
|
|
5876
|
-
});
|
|
5877
|
-
return pathResult.error;
|
|
5878
|
-
}
|
|
5879
|
-
const policyDecision = evaluateTrustPolicy(
|
|
5880
|
-
resourcePath,
|
|
5881
|
-
skill.trustLevel,
|
|
5882
|
-
registry.getAllowUntrustedScripts()
|
|
5883
|
-
);
|
|
5884
|
-
if (!policyDecision.allowed) {
|
|
5885
|
-
logger6.warn("Skill resource load blocked \u2014 trust policy", {
|
|
5886
|
-
name,
|
|
5887
|
-
resourcePath,
|
|
5888
|
-
trustLevel: skill.trustLevel,
|
|
5889
|
-
reason: policyDecision.reason,
|
|
5890
|
-
message: policyDecision.message
|
|
5891
|
-
});
|
|
5892
|
-
registry.emitEvent("trust:policy-denied" /* TRUST_POLICY_DENIED */, {
|
|
5893
|
-
name: skill.metadata.name,
|
|
5894
|
-
resourcePath,
|
|
5895
|
-
trustLevel: skill.trustLevel,
|
|
5896
|
-
reason: policyDecision.reason,
|
|
5897
|
-
message: policyDecision.message
|
|
5898
|
-
});
|
|
5899
|
-
return policyDecision.message;
|
|
5900
|
-
}
|
|
5901
|
-
if (policyDecision.reason !== "not-script" /* NOT_SCRIPT */) {
|
|
5902
|
-
logger6.info("Skill resource trust policy \u2014 allowed", {
|
|
5903
|
-
name,
|
|
5904
|
-
resourcePath,
|
|
5905
|
-
trustLevel: skill.trustLevel,
|
|
5906
|
-
reason: policyDecision.reason
|
|
5907
|
-
});
|
|
5908
|
-
registry.emitEvent("trust:policy-allowed" /* TRUST_POLICY_ALLOWED */, {
|
|
5909
|
-
name: skill.metadata.name,
|
|
5910
|
-
resourcePath,
|
|
5911
|
-
trustLevel: skill.trustLevel,
|
|
5912
|
-
reason: policyDecision.reason
|
|
5913
|
-
});
|
|
5914
|
-
}
|
|
5915
|
-
try {
|
|
5916
|
-
const content = (0, import_node_fs2.readFileSync)(pathResult.resolvedPath, "utf-8");
|
|
5917
|
-
logger6.info("Skill resource loaded", {
|
|
5918
|
-
name: skill.metadata.name,
|
|
5919
|
-
resourcePath,
|
|
5920
|
-
resolvedPath: pathResult.resolvedPath,
|
|
5921
|
-
contentLength: content.length
|
|
5922
|
-
});
|
|
5923
|
-
registry.emitEvent("skill:resource-loaded" /* SKILL_RESOURCE_LOADED */, {
|
|
5924
|
-
name: skill.metadata.name,
|
|
5925
|
-
resourcePath,
|
|
5926
|
-
resolvedPath: pathResult.resolvedPath,
|
|
5927
|
-
contentLength: content.length
|
|
5928
|
-
});
|
|
5929
|
-
return content;
|
|
5930
|
-
} catch (error) {
|
|
5931
|
-
const errorMsg = `Failed to read resource "${resourcePath}" from skill "${name}": ${error instanceof Error ? error.message : String(error)}`;
|
|
5932
|
-
logger6.warn("Skill resource load failed \u2014 file not found or unreadable", {
|
|
5933
|
-
name,
|
|
5934
|
-
resourcePath,
|
|
5935
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5936
|
-
});
|
|
5937
|
-
return errorMsg;
|
|
5938
|
-
}
|
|
5939
|
-
}).build();
|
|
5940
|
-
}
|
|
5941
|
-
function createSkillActivationTools(registry) {
|
|
5942
|
-
return [
|
|
5943
|
-
createActivateSkillTool(registry),
|
|
5944
|
-
createReadSkillResourceTool(registry)
|
|
5945
|
-
];
|
|
5946
|
-
}
|
|
5947
|
-
function extractBody(content) {
|
|
5948
|
-
return (0, import_gray_matter2.default)(content).content.trim();
|
|
5949
|
-
}
|
|
5950
|
-
|
|
5951
|
-
// src/skills/registry.ts
|
|
5952
|
-
var import_node_path3 = require("path");
|
|
5953
|
-
var logger7 = createLogger("agentforge:core:skills:registry", { level: "info" /* INFO */ });
|
|
5954
|
-
var SkillRegistry = class {
|
|
5955
|
-
skills = /* @__PURE__ */ new Map();
|
|
5956
|
-
eventHandlers = /* @__PURE__ */ new Map();
|
|
5957
|
-
config;
|
|
5958
|
-
scanErrors = [];
|
|
5959
|
-
/** Maps resolved root paths → trust levels for skill trust assignment */
|
|
5960
|
-
rootTrustMap = /* @__PURE__ */ new Map();
|
|
5961
|
-
/**
|
|
5962
|
-
* Create a SkillRegistry and immediately scan configured roots for skills.
|
|
5963
|
-
*
|
|
5964
|
-
* @param config - Registry configuration with skill root paths
|
|
5965
|
-
*
|
|
5966
|
-
* @example
|
|
5967
|
-
* ```ts
|
|
5968
|
-
* const registry = new SkillRegistry({
|
|
5969
|
-
* skillRoots: ['.agentskills', '~/.agentskills', './project-skills'],
|
|
5970
|
-
* });
|
|
5971
|
-
* console.log(`Discovered ${registry.size()} skills`);
|
|
5972
|
-
* ```
|
|
5973
|
-
*/
|
|
5974
|
-
constructor(config) {
|
|
5975
|
-
this.config = config;
|
|
5976
|
-
this.discover();
|
|
5977
|
-
}
|
|
5978
|
-
/**
|
|
5979
|
-
* Scan all configured roots and populate the registry.
|
|
5980
|
-
*
|
|
5981
|
-
* Called automatically during construction. Can be called again
|
|
5982
|
-
* to re-scan (clears existing skills first).
|
|
5983
|
-
*/
|
|
5984
|
-
discover() {
|
|
5985
|
-
this.skills.clear();
|
|
5986
|
-
this.scanErrors = [];
|
|
5987
|
-
this.rootTrustMap.clear();
|
|
5988
|
-
const normalizedRoots = this.config.skillRoots.map(normalizeRootConfig);
|
|
5989
|
-
const plainPaths = normalizedRoots.map((r) => r.path);
|
|
5990
|
-
for (const root of normalizedRoots) {
|
|
5991
|
-
const resolvedPath = (0, import_node_path3.resolve)(expandHome(root.path));
|
|
5992
|
-
this.rootTrustMap.set(resolvedPath, root.trust);
|
|
5993
|
-
}
|
|
5994
|
-
const candidates = scanAllSkillRoots(plainPaths);
|
|
5995
|
-
let successCount = 0;
|
|
5996
|
-
let warningCount = 0;
|
|
5997
|
-
for (const candidate of candidates) {
|
|
5998
|
-
const result = parseSkillContent(candidate.content, candidate.dirName);
|
|
5999
|
-
if (!result.success) {
|
|
6000
|
-
warningCount++;
|
|
6001
|
-
this.scanErrors.push({
|
|
6002
|
-
path: candidate.skillPath,
|
|
6003
|
-
error: result.error || "Unknown parse error"
|
|
6004
|
-
});
|
|
6005
|
-
this.emit("skill:warning" /* SKILL_WARNING */, {
|
|
6006
|
-
skillPath: candidate.skillPath,
|
|
6007
|
-
rootPath: candidate.rootPath,
|
|
6008
|
-
error: result.error
|
|
6009
|
-
});
|
|
6010
|
-
logger7.warn("Skipping invalid skill", {
|
|
6011
|
-
skillPath: candidate.skillPath,
|
|
6012
|
-
error: result.error
|
|
6013
|
-
});
|
|
6014
|
-
continue;
|
|
6015
|
-
}
|
|
6016
|
-
const skill = {
|
|
6017
|
-
metadata: result.metadata,
|
|
6018
|
-
skillPath: candidate.skillPath,
|
|
6019
|
-
rootPath: candidate.rootPath,
|
|
6020
|
-
trustLevel: this.rootTrustMap.get(candidate.rootPath) ?? "untrusted"
|
|
6021
|
-
};
|
|
6022
|
-
if (this.skills.has(skill.metadata.name)) {
|
|
6023
|
-
const existing = this.skills.get(skill.metadata.name);
|
|
6024
|
-
warningCount++;
|
|
6025
|
-
const warningMsg = `Duplicate skill name "${skill.metadata.name}" from "${candidate.rootPath}" \u2014 keeping version from "${existing.rootPath}" (first root takes precedence)`;
|
|
6026
|
-
this.scanErrors.push({
|
|
6027
|
-
path: candidate.skillPath,
|
|
6028
|
-
error: warningMsg
|
|
6029
|
-
});
|
|
6030
|
-
this.emit("skill:warning" /* SKILL_WARNING */, {
|
|
6031
|
-
skillPath: candidate.skillPath,
|
|
6032
|
-
rootPath: candidate.rootPath,
|
|
6033
|
-
duplicateOf: existing.skillPath,
|
|
6034
|
-
error: warningMsg
|
|
6035
|
-
});
|
|
6036
|
-
logger7.warn("Duplicate skill name, keeping first", {
|
|
6037
|
-
name: skill.metadata.name,
|
|
6038
|
-
kept: existing.skillPath,
|
|
6039
|
-
skipped: candidate.skillPath
|
|
6040
|
-
});
|
|
6041
|
-
continue;
|
|
6042
|
-
}
|
|
6043
|
-
this.skills.set(skill.metadata.name, skill);
|
|
6044
|
-
successCount++;
|
|
6045
|
-
this.emit("skill:discovered" /* SKILL_DISCOVERED */, skill);
|
|
6046
|
-
logger7.debug("Skill discovered", {
|
|
6047
|
-
name: skill.metadata.name,
|
|
6048
|
-
description: skill.metadata.description.slice(0, 80),
|
|
6049
|
-
skillPath: skill.skillPath
|
|
6050
|
-
});
|
|
6051
|
-
}
|
|
6052
|
-
logger7.info("Skill registry populated", {
|
|
6053
|
-
rootsScanned: this.config.skillRoots.length,
|
|
6054
|
-
skillsDiscovered: successCount,
|
|
6055
|
-
warnings: warningCount
|
|
6056
|
-
});
|
|
6057
|
-
}
|
|
6058
|
-
// ─── Query API (parallel to ToolRegistry) ──────────────────────────────
|
|
6059
|
-
/**
|
|
6060
|
-
* Get a skill by name.
|
|
6061
|
-
*
|
|
6062
|
-
* @param name - The skill name
|
|
6063
|
-
* @returns The skill, or undefined if not found
|
|
6064
|
-
*
|
|
6065
|
-
* @example
|
|
6066
|
-
* ```ts
|
|
6067
|
-
* const skill = registry.get('code-review');
|
|
6068
|
-
* if (skill) {
|
|
6069
|
-
* console.log(skill.metadata.description);
|
|
6070
|
-
* }
|
|
6071
|
-
* ```
|
|
6072
|
-
*/
|
|
6073
|
-
get(name) {
|
|
6074
|
-
return this.skills.get(name);
|
|
6075
|
-
}
|
|
6076
|
-
/**
|
|
6077
|
-
* Get all discovered skills.
|
|
6078
|
-
*
|
|
6079
|
-
* @returns Array of all skills
|
|
6080
|
-
*
|
|
6081
|
-
* @example
|
|
6082
|
-
* ```ts
|
|
6083
|
-
* const allSkills = registry.getAll();
|
|
6084
|
-
* console.log(`Total skills: ${allSkills.length}`);
|
|
6085
|
-
* ```
|
|
6086
|
-
*/
|
|
6087
|
-
getAll() {
|
|
6088
|
-
return Array.from(this.skills.values());
|
|
6089
|
-
}
|
|
6090
|
-
/**
|
|
6091
|
-
* Check if a skill exists in the registry.
|
|
6092
|
-
*
|
|
6093
|
-
* @param name - The skill name
|
|
6094
|
-
* @returns True if the skill exists
|
|
6095
|
-
*
|
|
6096
|
-
* @example
|
|
6097
|
-
* ```ts
|
|
6098
|
-
* if (registry.has('code-review')) {
|
|
6099
|
-
* console.log('Skill available!');
|
|
6100
|
-
* }
|
|
6101
|
-
* ```
|
|
6102
|
-
*/
|
|
6103
|
-
has(name) {
|
|
6104
|
-
return this.skills.has(name);
|
|
6105
|
-
}
|
|
6106
|
-
/**
|
|
6107
|
-
* Get the number of discovered skills.
|
|
6108
|
-
*
|
|
6109
|
-
* @returns Number of skills in the registry
|
|
6110
|
-
*
|
|
6111
|
-
* @example
|
|
6112
|
-
* ```ts
|
|
6113
|
-
* console.log(`Registry has ${registry.size()} skills`);
|
|
6114
|
-
* ```
|
|
6115
|
-
*/
|
|
6116
|
-
size() {
|
|
6117
|
-
return this.skills.size;
|
|
6118
|
-
}
|
|
6119
|
-
/**
|
|
6120
|
-
* Get all skill names.
|
|
6121
|
-
*
|
|
6122
|
-
* @returns Array of skill names
|
|
6123
|
-
*/
|
|
6124
|
-
getNames() {
|
|
6125
|
-
return Array.from(this.skills.keys());
|
|
6126
|
-
}
|
|
6127
|
-
/**
|
|
6128
|
-
* Get errors/warnings from the last scan.
|
|
6129
|
-
*
|
|
6130
|
-
* Useful for diagnostics and observability.
|
|
6131
|
-
*
|
|
6132
|
-
* @returns Array of scan errors with paths
|
|
6133
|
-
*/
|
|
6134
|
-
getScanErrors() {
|
|
6135
|
-
return this.scanErrors;
|
|
6136
|
-
}
|
|
6137
|
-
/**
|
|
6138
|
-
* Check whether untrusted script access is allowed via config override.
|
|
6139
|
-
*
|
|
6140
|
-
* Used by activation tools to pass the override flag to trust policy checks.
|
|
6141
|
-
*
|
|
6142
|
-
* @returns True if `allowUntrustedScripts` is set in config
|
|
6143
|
-
*/
|
|
6144
|
-
getAllowUntrustedScripts() {
|
|
6145
|
-
return this.config.allowUntrustedScripts ?? false;
|
|
6146
|
-
}
|
|
6147
|
-
/**
|
|
6148
|
-
* Get the `allowed-tools` list for a skill.
|
|
6149
|
-
*
|
|
6150
|
-
* Returns the `allowedTools` array from the skill's frontmatter metadata,
|
|
6151
|
-
* enabling agents to filter their tool set based on what the skill expects.
|
|
6152
|
-
*
|
|
6153
|
-
* @param name - The skill name
|
|
6154
|
-
* @returns Array of allowed tool names, or undefined if skill not found or field not set
|
|
6155
|
-
*
|
|
6156
|
-
* @example
|
|
6157
|
-
* ```ts
|
|
6158
|
-
* const allowed = registry.getAllowedTools('code-review');
|
|
6159
|
-
* if (allowed) {
|
|
6160
|
-
* const filteredTools = allTools.filter(t => allowed.includes(t.name));
|
|
6161
|
-
* }
|
|
6162
|
-
* ```
|
|
6163
|
-
*/
|
|
6164
|
-
getAllowedTools(name) {
|
|
6165
|
-
const skill = this.skills.get(name);
|
|
6166
|
-
return skill?.metadata.allowedTools;
|
|
6167
|
-
}
|
|
6168
|
-
// ─── Prompt Generation ─────────────────────────────────────────────────
|
|
6169
|
-
/**
|
|
6170
|
-
* Generate an `<available_skills>` XML block for system prompt injection.
|
|
6171
|
-
*
|
|
6172
|
-
* Returns an empty string when:
|
|
6173
|
-
* - `config.enabled` is `false` (default) — agents operate with unmodified prompts
|
|
6174
|
-
* - No skills match the filter criteria
|
|
6175
|
-
*
|
|
6176
|
-
* The output composes naturally with `toolRegistry.generatePrompt()` —
|
|
6177
|
-
* simply concatenate both into the system prompt.
|
|
6178
|
-
*
|
|
6179
|
-
* @param options - Optional filtering (subset of skill names)
|
|
6180
|
-
* @returns XML string or empty string
|
|
6181
|
-
*
|
|
6182
|
-
* @example
|
|
6183
|
-
* ```ts
|
|
6184
|
-
* // All skills
|
|
6185
|
-
* const xml = registry.generatePrompt();
|
|
6186
|
-
*
|
|
6187
|
-
* // Subset for a focused agent
|
|
6188
|
-
* const xml = registry.generatePrompt({ skills: ['code-review', 'testing'] });
|
|
6189
|
-
*
|
|
6190
|
-
* // Compose with tool prompt
|
|
6191
|
-
* const systemPrompt = [
|
|
6192
|
-
* toolRegistry.generatePrompt(),
|
|
6193
|
-
* skillRegistry.generatePrompt(),
|
|
6194
|
-
* ].filter(Boolean).join('\n\n');
|
|
6195
|
-
* ```
|
|
6196
|
-
*/
|
|
6197
|
-
generatePrompt(options) {
|
|
6198
|
-
if (!this.config.enabled) {
|
|
6199
|
-
logger7.debug("Skill prompt generation skipped (disabled)", {
|
|
6200
|
-
enabled: this.config.enabled ?? false
|
|
6201
|
-
});
|
|
6202
|
-
return "";
|
|
6203
|
-
}
|
|
6204
|
-
let skills = this.getAll();
|
|
6205
|
-
if (options?.skills && options.skills.length > 0) {
|
|
6206
|
-
const requested = new Set(options.skills);
|
|
6207
|
-
skills = skills.filter((s) => requested.has(s.metadata.name));
|
|
6208
|
-
}
|
|
6209
|
-
if (this.config.maxDiscoveredSkills !== void 0 && this.config.maxDiscoveredSkills >= 0) {
|
|
6210
|
-
skills = skills.slice(0, this.config.maxDiscoveredSkills);
|
|
6211
|
-
}
|
|
6212
|
-
if (skills.length === 0) {
|
|
6213
|
-
logger7.debug("Skill prompt generation produced empty result", {
|
|
6214
|
-
totalDiscovered: this.size(),
|
|
6215
|
-
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
6216
|
-
maxCap: this.config.maxDiscoveredSkills
|
|
6217
|
-
});
|
|
6218
|
-
return "";
|
|
6219
|
-
}
|
|
6220
|
-
const skillEntries = skills.map((skill) => {
|
|
6221
|
-
const lines = [
|
|
6222
|
-
" <skill>",
|
|
6223
|
-
` <name>${escapeXml(skill.metadata.name)}</name>`,
|
|
6224
|
-
` <description>${escapeXml(skill.metadata.description)}</description>`,
|
|
6225
|
-
` <location>${escapeXml(skill.skillPath)}</location>`,
|
|
6226
|
-
" </skill>"
|
|
6227
|
-
];
|
|
6228
|
-
return lines.join("\n");
|
|
6229
|
-
});
|
|
6230
|
-
const xml = `<available_skills>
|
|
6231
|
-
${skillEntries.join("\n")}
|
|
6232
|
-
</available_skills>`;
|
|
6233
|
-
const estimatedTokens = Math.ceil(xml.length / 4);
|
|
6234
|
-
logger7.info("Skill prompt generated", {
|
|
6235
|
-
skillCount: skills.length,
|
|
6236
|
-
totalDiscovered: this.size(),
|
|
6237
|
-
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
6238
|
-
maxCap: this.config.maxDiscoveredSkills,
|
|
6239
|
-
estimatedTokens,
|
|
6240
|
-
xmlLength: xml.length
|
|
6241
|
-
});
|
|
6242
|
-
return xml;
|
|
6243
|
-
}
|
|
6244
|
-
// ─── Event System ──────────────────────────────────────────────────────
|
|
6245
|
-
/**
|
|
6246
|
-
* Register an event handler.
|
|
6247
|
-
*
|
|
6248
|
-
* @param event - The event to listen for
|
|
6249
|
-
* @param handler - The handler function
|
|
6250
|
-
*
|
|
6251
|
-
* @example
|
|
6252
|
-
* ```ts
|
|
6253
|
-
* registry.on(SkillRegistryEvent.SKILL_DISCOVERED, (skill) => {
|
|
6254
|
-
* console.log('Found skill:', skill.metadata.name);
|
|
6255
|
-
* });
|
|
6256
|
-
* ```
|
|
6257
|
-
*/
|
|
6258
|
-
on(event, handler) {
|
|
6259
|
-
if (!this.eventHandlers.has(event)) {
|
|
6260
|
-
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
6261
|
-
}
|
|
6262
|
-
this.eventHandlers.get(event).add(handler);
|
|
6263
|
-
}
|
|
6264
|
-
/**
|
|
6265
|
-
* Unregister an event handler.
|
|
6266
|
-
*
|
|
6267
|
-
* @param event - The event to stop listening for
|
|
6268
|
-
* @param handler - The handler function to remove
|
|
6269
|
-
*/
|
|
6270
|
-
off(event, handler) {
|
|
6271
|
-
const handlers = this.eventHandlers.get(event);
|
|
6272
|
-
if (handlers) {
|
|
6273
|
-
handlers.delete(handler);
|
|
6274
|
-
}
|
|
6275
|
-
}
|
|
6276
|
-
/**
|
|
6277
|
-
* Emit an event to all registered handlers.
|
|
6278
|
-
*
|
|
6279
|
-
* @param event - The event to emit
|
|
6280
|
-
* @param data - The event data
|
|
6281
|
-
* @private
|
|
6282
|
-
*/
|
|
6283
|
-
emit(event, data) {
|
|
6284
|
-
const handlers = this.eventHandlers.get(event);
|
|
6285
|
-
if (handlers) {
|
|
6286
|
-
handlers.forEach((handler) => {
|
|
6287
|
-
try {
|
|
6288
|
-
handler(data);
|
|
6289
|
-
} catch (error) {
|
|
6290
|
-
logger7.error("Skill event handler error", {
|
|
6291
|
-
event,
|
|
6292
|
-
error: error instanceof Error ? error.message : String(error),
|
|
6293
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
6294
|
-
});
|
|
6295
|
-
}
|
|
6296
|
-
});
|
|
6297
|
-
}
|
|
6298
|
-
}
|
|
6299
|
-
/**
|
|
6300
|
-
* Emit an event (public API for activation tools).
|
|
6301
|
-
*
|
|
6302
|
-
* Used by skill activation tools to emit `skill:activated` and
|
|
6303
|
-
* `skill:resource-loaded` events through the registry's event system.
|
|
6304
|
-
*
|
|
6305
|
-
* @param event - The event to emit
|
|
6306
|
-
* @param data - The event data
|
|
6307
|
-
*/
|
|
6308
|
-
emitEvent(event, data) {
|
|
6309
|
-
this.emit(event, data);
|
|
6310
|
-
}
|
|
6311
|
-
// ─── Tool Integration ────────────────────────────────────────────────
|
|
6312
|
-
/**
|
|
6313
|
-
* Create activation tools pre-wired to this registry instance.
|
|
6314
|
-
*
|
|
6315
|
-
* Returns `activate-skill` and `read-skill-resource` tools that
|
|
6316
|
-
* agents can use to load skill instructions and resources on demand.
|
|
6317
|
-
*
|
|
6318
|
-
* @returns Array of [activate-skill, read-skill-resource] tools
|
|
6319
|
-
*
|
|
6320
|
-
* @example
|
|
6321
|
-
* ```ts
|
|
6322
|
-
* const agent = createReActAgent({
|
|
6323
|
-
* model: llm,
|
|
6324
|
-
* tools: [
|
|
6325
|
-
* ...toolRegistry.toLangChainTools(),
|
|
6326
|
-
* ...skillRegistry.toActivationTools(),
|
|
6327
|
-
* ],
|
|
6328
|
-
* });
|
|
6329
|
-
* ```
|
|
6330
|
-
*/
|
|
6331
|
-
toActivationTools() {
|
|
6332
|
-
return createSkillActivationTools(this);
|
|
6333
|
-
}
|
|
6334
|
-
};
|
|
6335
|
-
function escapeXml(str) {
|
|
6336
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6337
|
-
}
|
|
6338
5486
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6339
5487
|
0 && (module.exports = {
|
|
6340
5488
|
AgentError,
|
|
@@ -6354,8 +5502,6 @@ function escapeXml(str) {
|
|
|
6354
5502
|
MissingDescriptionError,
|
|
6355
5503
|
Profiler,
|
|
6356
5504
|
RegistryEvent,
|
|
6357
|
-
SkillRegistry,
|
|
6358
|
-
SkillRegistryEvent,
|
|
6359
5505
|
TimeoutError,
|
|
6360
5506
|
ToolBuilder,
|
|
6361
5507
|
ToolCategory,
|
|
@@ -6365,7 +5511,6 @@ function escapeXml(str) {
|
|
|
6365
5511
|
ToolNameSchema,
|
|
6366
5512
|
ToolRegistry,
|
|
6367
5513
|
ToolRelationsSchema,
|
|
6368
|
-
TrustPolicyReason,
|
|
6369
5514
|
batch,
|
|
6370
5515
|
broadcast,
|
|
6371
5516
|
cache,
|
|
@@ -6379,7 +5524,6 @@ function escapeXml(str) {
|
|
|
6379
5524
|
composeWithOptions,
|
|
6380
5525
|
conditional,
|
|
6381
5526
|
configureLangSmith,
|
|
6382
|
-
createActivateSkillTool,
|
|
6383
5527
|
createAlertManager,
|
|
6384
5528
|
createApprovalRequiredInterrupt,
|
|
6385
5529
|
createAuditLogger,
|
|
@@ -6408,13 +5552,11 @@ function escapeXml(str) {
|
|
|
6408
5552
|
createParallelWorkflow,
|
|
6409
5553
|
createProfiler,
|
|
6410
5554
|
createProgressTracker,
|
|
6411
|
-
createReadSkillResourceTool,
|
|
6412
5555
|
createSSEFormatter,
|
|
6413
5556
|
createSequentialWorkflow,
|
|
6414
5557
|
createSharedCache,
|
|
6415
5558
|
createSharedConcurrencyController,
|
|
6416
5559
|
createSharedRateLimiter,
|
|
6417
|
-
createSkillActivationTools,
|
|
6418
5560
|
createSqliteCheckpointer,
|
|
6419
5561
|
createStateAnnotation,
|
|
6420
5562
|
createSubgraph,
|
|
@@ -6425,8 +5567,6 @@ function escapeXml(str) {
|
|
|
6425
5567
|
createToolUnsafe,
|
|
6426
5568
|
createWebSocketHandler,
|
|
6427
5569
|
development,
|
|
6428
|
-
evaluateTrustPolicy,
|
|
6429
|
-
expandHome,
|
|
6430
5570
|
filter,
|
|
6431
5571
|
formatAgentResumedEvent,
|
|
6432
5572
|
formatAgentWaitingEvent,
|
|
@@ -6446,26 +5586,20 @@ function escapeXml(str) {
|
|
|
6446
5586
|
isCustomInterrupt,
|
|
6447
5587
|
isHumanRequestInterrupt,
|
|
6448
5588
|
isMemoryCheckpointer,
|
|
6449
|
-
isScriptResource,
|
|
6450
5589
|
isTracingEnabled,
|
|
6451
5590
|
loadPrompt,
|
|
6452
5591
|
map,
|
|
6453
5592
|
merge,
|
|
6454
5593
|
mergeState,
|
|
6455
|
-
normalizeRootConfig,
|
|
6456
5594
|
parallel,
|
|
6457
5595
|
parseSSEEvent,
|
|
6458
|
-
parseSkillContent,
|
|
6459
5596
|
presets,
|
|
6460
5597
|
production,
|
|
6461
5598
|
reduce,
|
|
6462
5599
|
renderTemplate,
|
|
6463
|
-
resolveResourcePath,
|
|
6464
5600
|
retry,
|
|
6465
5601
|
safeValidateSchemaDescriptions,
|
|
6466
5602
|
sanitizeValue,
|
|
6467
|
-
scanAllSkillRoots,
|
|
6468
|
-
scanSkillRoot,
|
|
6469
5603
|
sendMessage,
|
|
6470
5604
|
sequential,
|
|
6471
5605
|
sequentialBuilder,
|
|
@@ -6477,9 +5611,6 @@ function escapeXml(str) {
|
|
|
6477
5611
|
toLangChainTools,
|
|
6478
5612
|
toolBuilder,
|
|
6479
5613
|
validateSchemaDescriptions,
|
|
6480
|
-
validateSkillDescription,
|
|
6481
|
-
validateSkillName,
|
|
6482
|
-
validateSkillNameMatchesDir,
|
|
6483
5614
|
validateState,
|
|
6484
5615
|
validateTool,
|
|
6485
5616
|
validateToolMetadata,
|