@agentforge/core 0.12.6 → 0.14.0

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 CHANGED
@@ -47,6 +47,8 @@ __export(index_exports, {
47
47
  MissingDescriptionError: () => MissingDescriptionError,
48
48
  Profiler: () => Profiler,
49
49
  RegistryEvent: () => RegistryEvent,
50
+ SkillRegistry: () => SkillRegistry,
51
+ SkillRegistryEvent: () => SkillRegistryEvent,
50
52
  TimeoutError: () => TimeoutError,
51
53
  ToolBuilder: () => ToolBuilder,
52
54
  ToolCategory: () => ToolCategory,
@@ -56,6 +58,7 @@ __export(index_exports, {
56
58
  ToolNameSchema: () => ToolNameSchema,
57
59
  ToolRegistry: () => ToolRegistry,
58
60
  ToolRelationsSchema: () => ToolRelationsSchema,
61
+ TrustPolicyReason: () => TrustPolicyReason,
59
62
  batch: () => batch,
60
63
  broadcast: () => broadcast,
61
64
  cache: () => cache,
@@ -69,6 +72,7 @@ __export(index_exports, {
69
72
  composeWithOptions: () => composeWithOptions,
70
73
  conditional: () => conditional,
71
74
  configureLangSmith: () => configureLangSmith,
75
+ createActivateSkillTool: () => createActivateSkillTool,
72
76
  createAlertManager: () => createAlertManager,
73
77
  createApprovalRequiredInterrupt: () => createApprovalRequiredInterrupt,
74
78
  createAuditLogger: () => createAuditLogger,
@@ -97,11 +101,13 @@ __export(index_exports, {
97
101
  createParallelWorkflow: () => createParallelWorkflow,
98
102
  createProfiler: () => createProfiler,
99
103
  createProgressTracker: () => createProgressTracker,
104
+ createReadSkillResourceTool: () => createReadSkillResourceTool,
100
105
  createSSEFormatter: () => createSSEFormatter,
101
106
  createSequentialWorkflow: () => createSequentialWorkflow,
102
107
  createSharedCache: () => createSharedCache,
103
108
  createSharedConcurrencyController: () => createSharedConcurrencyController,
104
109
  createSharedRateLimiter: () => createSharedRateLimiter,
110
+ createSkillActivationTools: () => createSkillActivationTools,
105
111
  createSqliteCheckpointer: () => createSqliteCheckpointer,
106
112
  createStateAnnotation: () => createStateAnnotation,
107
113
  createSubgraph: () => createSubgraph,
@@ -112,6 +118,8 @@ __export(index_exports, {
112
118
  createToolUnsafe: () => createToolUnsafe,
113
119
  createWebSocketHandler: () => createWebSocketHandler,
114
120
  development: () => development,
121
+ evaluateTrustPolicy: () => evaluateTrustPolicy,
122
+ expandHome: () => expandHome,
115
123
  filter: () => filter,
116
124
  formatAgentResumedEvent: () => formatAgentResumedEvent,
117
125
  formatAgentWaitingEvent: () => formatAgentWaitingEvent,
@@ -131,20 +139,26 @@ __export(index_exports, {
131
139
  isCustomInterrupt: () => isCustomInterrupt,
132
140
  isHumanRequestInterrupt: () => isHumanRequestInterrupt,
133
141
  isMemoryCheckpointer: () => isMemoryCheckpointer,
142
+ isScriptResource: () => isScriptResource,
134
143
  isTracingEnabled: () => isTracingEnabled,
135
144
  loadPrompt: () => loadPrompt,
136
145
  map: () => map,
137
146
  merge: () => merge,
138
147
  mergeState: () => mergeState,
148
+ normalizeRootConfig: () => normalizeRootConfig,
139
149
  parallel: () => parallel,
140
150
  parseSSEEvent: () => parseSSEEvent,
151
+ parseSkillContent: () => parseSkillContent,
141
152
  presets: () => presets,
142
153
  production: () => production,
143
154
  reduce: () => reduce,
144
155
  renderTemplate: () => renderTemplate,
156
+ resolveResourcePath: () => resolveResourcePath,
145
157
  retry: () => retry,
146
158
  safeValidateSchemaDescriptions: () => safeValidateSchemaDescriptions,
147
159
  sanitizeValue: () => sanitizeValue,
160
+ scanAllSkillRoots: () => scanAllSkillRoots,
161
+ scanSkillRoot: () => scanSkillRoot,
148
162
  sendMessage: () => sendMessage,
149
163
  sequential: () => sequential,
150
164
  sequentialBuilder: () => sequentialBuilder,
@@ -156,6 +170,9 @@ __export(index_exports, {
156
170
  toLangChainTools: () => toLangChainTools,
157
171
  toolBuilder: () => toolBuilder,
158
172
  validateSchemaDescriptions: () => validateSchemaDescriptions,
173
+ validateSkillDescription: () => validateSkillDescription,
174
+ validateSkillName: () => validateSkillName,
175
+ validateSkillNameMatchesDir: () => validateSkillNameMatchesDir,
159
176
  validateState: () => validateState,
160
177
  validateTool: () => validateTool,
161
178
  validateToolMetadata: () => validateToolMetadata,
@@ -182,6 +199,7 @@ var ToolCategory = /* @__PURE__ */ ((ToolCategory2) => {
182
199
  ToolCategory2["API"] = "api";
183
200
  ToolCategory2["UTILITY"] = "utility";
184
201
  ToolCategory2["CUSTOM"] = "custom";
202
+ ToolCategory2["SKILLS"] = "skills";
185
203
  return ToolCategory2;
186
204
  })(ToolCategory || {});
187
205
 
@@ -1580,7 +1598,7 @@ function createToolExecutor(config = {}) {
1580
1598
  }
1581
1599
  if (attempt < policy.maxAttempts) {
1582
1600
  const delay = calculateBackoff(attempt, policy);
1583
- await new Promise((resolve) => setTimeout(resolve, delay));
1601
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
1584
1602
  }
1585
1603
  }
1586
1604
  }
@@ -1637,12 +1655,12 @@ function createToolExecutor(config = {}) {
1637
1655
  }
1638
1656
  async function execute(tool, input, options = {}) {
1639
1657
  const priority = options.priority || priorityFn(tool);
1640
- return new Promise((resolve, reject) => {
1658
+ return new Promise((resolve3, reject) => {
1641
1659
  queue.push({
1642
1660
  tool,
1643
1661
  input,
1644
1662
  priority,
1645
- resolve,
1663
+ resolve: resolve3,
1646
1664
  reject,
1647
1665
  timestamp: Date.now()
1648
1666
  });
@@ -1942,7 +1960,7 @@ function retry(tool, options = {}) {
1942
1960
  lastError = error;
1943
1961
  if (attempt < maxAttempts) {
1944
1962
  const waitTime = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
1945
- await new Promise((resolve) => setTimeout(resolve, waitTime));
1963
+ await new Promise((resolve3) => setTimeout(resolve3, waitTime));
1946
1964
  }
1947
1965
  }
1948
1966
  }
@@ -2003,7 +2021,7 @@ function createMockTool(config) {
2003
2021
  const startTime = Date.now();
2004
2022
  if (latency) {
2005
2023
  const delay = typeof latency === "number" ? latency : Math.random() * (latency.max - latency.min) + latency.min;
2006
- await new Promise((resolve) => setTimeout(resolve, delay));
2024
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
2007
2025
  }
2008
2026
  if (errorRate > 0 && Math.random() < errorRate) {
2009
2027
  const error2 = new Error(`Random error from ${name}`);
@@ -2074,8 +2092,8 @@ function createToolSimulator(config) {
2074
2092
  if (!latency) return 0;
2075
2093
  const u1 = Math.random();
2076
2094
  const u2 = Math.random();
2077
- const z3 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
2078
- return Math.max(0, latency.mean + z3 * latency.stddev);
2095
+ const z4 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
2096
+ return Math.max(0, latency.mean + z4 * latency.stddev);
2079
2097
  }
2080
2098
  return {
2081
2099
  execute: async (toolName, input) => {
@@ -2086,7 +2104,7 @@ function createToolSimulator(config) {
2086
2104
  const startTime = Date.now();
2087
2105
  if (latency) {
2088
2106
  const delay = generateLatency();
2089
- await new Promise((resolve) => setTimeout(resolve, delay));
2107
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
2090
2108
  }
2091
2109
  if (errorRate > 0 && Math.random() < errorRate) {
2092
2110
  const error = new Error(`Simulated error from ${toolName}`);
@@ -2444,7 +2462,7 @@ function calculateDelay(attempt, strategy, initialDelay, maxDelay) {
2444
2462
  return Math.min(delay, maxDelay);
2445
2463
  }
2446
2464
  function sleep(ms) {
2447
- return new Promise((resolve) => setTimeout(resolve, ms));
2465
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
2448
2466
  }
2449
2467
  function withRetry(node, options = {}) {
2450
2468
  const {
@@ -2690,14 +2708,14 @@ var withLogging = (options) => {
2690
2708
  onComplete,
2691
2709
  onError
2692
2710
  } = options;
2693
- const logger5 = providedLogger || createLogger(name, { level });
2711
+ const logger8 = providedLogger || createLogger(name, { level });
2694
2712
  return (node) => {
2695
2713
  return async (state) => {
2696
2714
  const startTime = Date.now();
2697
2715
  try {
2698
2716
  if (logInput) {
2699
2717
  const data = extractData ? extractData(state) : { state };
2700
- logger5.info("Node execution started", data);
2718
+ logger8.info("Node execution started", data);
2701
2719
  }
2702
2720
  if (onStart) {
2703
2721
  onStart(state);
@@ -2707,9 +2725,9 @@ var withLogging = (options) => {
2707
2725
  if (logOutput) {
2708
2726
  const data = extractData ? extractData(result) : { result };
2709
2727
  if (logDuration) {
2710
- logger5.info(`Node execution completed (${duration}ms)`, data);
2728
+ logger8.info(`Node execution completed (${duration}ms)`, data);
2711
2729
  } else {
2712
- logger5.info("Node execution completed", data);
2730
+ logger8.info("Node execution completed", data);
2713
2731
  }
2714
2732
  }
2715
2733
  if (onComplete) {
@@ -2720,7 +2738,7 @@ var withLogging = (options) => {
2720
2738
  const duration = Date.now() - startTime;
2721
2739
  const err = error instanceof Error ? error : new Error(String(error));
2722
2740
  if (logErrors) {
2723
- logger5.error(`Node execution failed (${duration}ms)`, {
2741
+ logger8.error(`Node execution failed (${duration}ms)`, {
2724
2742
  error: err.message,
2725
2743
  stack: err.stack
2726
2744
  });
@@ -2756,7 +2774,7 @@ function withLogging2(options) {
2756
2774
  function production(node, options) {
2757
2775
  const {
2758
2776
  nodeName,
2759
- logger: logger5,
2777
+ logger: logger8,
2760
2778
  enableMetrics = true,
2761
2779
  enableTracing = true,
2762
2780
  enableRetry = true,
@@ -2764,7 +2782,7 @@ function production(node, options) {
2764
2782
  retryOptions = {},
2765
2783
  errorOptions = {}
2766
2784
  } = options;
2767
- const actualLogger = logger5 || createLogger(nodeName, { level: "info" /* INFO */ });
2785
+ const actualLogger = logger8 || createLogger(nodeName, { level: "info" /* INFO */ });
2768
2786
  const middleware = [];
2769
2787
  middleware.push(
2770
2788
  withLogging2({
@@ -2828,9 +2846,9 @@ function development(node, options) {
2828
2846
  const {
2829
2847
  nodeName,
2830
2848
  verbose = true,
2831
- logger: logger5
2849
+ logger: logger8
2832
2850
  } = options;
2833
- const actualLogger = logger5 || createLogger(nodeName, { level: "debug" /* DEBUG */ });
2851
+ const actualLogger = logger8 || createLogger(nodeName, { level: "debug" /* DEBUG */ });
2834
2852
  return withLogging2({
2835
2853
  logger: actualLogger,
2836
2854
  name: nodeName,
@@ -2854,7 +2872,7 @@ function testing(node, options) {
2854
2872
  invocations.push(state);
2855
2873
  }
2856
2874
  if (delay > 0) {
2857
- await new Promise((resolve) => setTimeout(resolve, delay));
2875
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
2858
2876
  }
2859
2877
  if (simulateError) {
2860
2878
  throw simulateError;
@@ -3313,12 +3331,12 @@ var ConcurrencyController = class {
3313
3331
  }
3314
3332
  throw new Error(`Queue is full (max size: ${this.maxQueueSize})`);
3315
3333
  }
3316
- return new Promise((resolve, reject) => {
3334
+ return new Promise((resolve3, reject) => {
3317
3335
  const task = {
3318
3336
  state,
3319
3337
  priority,
3320
3338
  executor,
3321
- resolve,
3339
+ resolve: resolve3,
3322
3340
  reject,
3323
3341
  timestamp: Date.now()
3324
3342
  };
@@ -3783,7 +3801,7 @@ async function* throttle(stream, options) {
3783
3801
  const now = Date.now();
3784
3802
  const timeSinceLastEmit = now - lastEmit;
3785
3803
  if (timeSinceLastEmit < interval) {
3786
- await new Promise((resolve) => setTimeout(resolve, interval - timeSinceLastEmit));
3804
+ await new Promise((resolve3) => setTimeout(resolve3, interval - timeSinceLastEmit));
3787
3805
  }
3788
3806
  lastEmit = Date.now();
3789
3807
  yield item;
@@ -4298,17 +4316,17 @@ var ConnectionPool = class {
4298
4316
  this.options.onAcquire?.(connection);
4299
4317
  return connection;
4300
4318
  }
4301
- return new Promise((resolve, reject) => {
4319
+ return new Promise((resolve3, reject) => {
4302
4320
  const timeout2 = poolConfig.acquireTimeout || 3e4;
4303
4321
  const timer = setTimeout(() => {
4304
- const index = this.pending.findIndex((p) => p.resolve === resolve);
4322
+ const index = this.pending.findIndex((p) => p.resolve === resolve3);
4305
4323
  if (index !== -1) {
4306
4324
  this.pending.splice(index, 1);
4307
4325
  this.stats.pending--;
4308
4326
  }
4309
4327
  reject(new Error("Acquire timeout"));
4310
4328
  }, timeout2);
4311
- this.pending.push({ resolve, reject, timeout: timer });
4329
+ this.pending.push({ resolve: resolve3, reject, timeout: timer });
4312
4330
  this.stats.pending++;
4313
4331
  });
4314
4332
  }
@@ -4392,7 +4410,7 @@ var ConnectionPool = class {
4392
4410
  this.pending = [];
4393
4411
  this.stats.pending = 0;
4394
4412
  while (this.connections.some((c) => c.inUse)) {
4395
- await new Promise((resolve) => setTimeout(resolve, 100));
4413
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
4396
4414
  }
4397
4415
  }
4398
4416
  async clear() {
@@ -4719,10 +4737,10 @@ var BatchProcessor = class {
4719
4737
  failedBatches: 0
4720
4738
  };
4721
4739
  add(input) {
4722
- return new Promise((resolve, reject) => {
4740
+ return new Promise((resolve3, reject) => {
4723
4741
  this.pending.push({
4724
4742
  input,
4725
- resolve,
4743
+ resolve: resolve3,
4726
4744
  reject,
4727
4745
  addedAt: Date.now()
4728
4746
  });
@@ -5482,6 +5500,841 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5482
5500
  );
5483
5501
  }
5484
5502
  }
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
6337
+ }
5485
6338
  // Annotate the CommonJS export names for ESM import in node:
5486
6339
  0 && (module.exports = {
5487
6340
  AgentError,
@@ -5501,6 +6354,8 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5501
6354
  MissingDescriptionError,
5502
6355
  Profiler,
5503
6356
  RegistryEvent,
6357
+ SkillRegistry,
6358
+ SkillRegistryEvent,
5504
6359
  TimeoutError,
5505
6360
  ToolBuilder,
5506
6361
  ToolCategory,
@@ -5510,6 +6365,7 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5510
6365
  ToolNameSchema,
5511
6366
  ToolRegistry,
5512
6367
  ToolRelationsSchema,
6368
+ TrustPolicyReason,
5513
6369
  batch,
5514
6370
  broadcast,
5515
6371
  cache,
@@ -5523,6 +6379,7 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5523
6379
  composeWithOptions,
5524
6380
  conditional,
5525
6381
  configureLangSmith,
6382
+ createActivateSkillTool,
5526
6383
  createAlertManager,
5527
6384
  createApprovalRequiredInterrupt,
5528
6385
  createAuditLogger,
@@ -5551,11 +6408,13 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5551
6408
  createParallelWorkflow,
5552
6409
  createProfiler,
5553
6410
  createProgressTracker,
6411
+ createReadSkillResourceTool,
5554
6412
  createSSEFormatter,
5555
6413
  createSequentialWorkflow,
5556
6414
  createSharedCache,
5557
6415
  createSharedConcurrencyController,
5558
6416
  createSharedRateLimiter,
6417
+ createSkillActivationTools,
5559
6418
  createSqliteCheckpointer,
5560
6419
  createStateAnnotation,
5561
6420
  createSubgraph,
@@ -5566,6 +6425,8 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5566
6425
  createToolUnsafe,
5567
6426
  createWebSocketHandler,
5568
6427
  development,
6428
+ evaluateTrustPolicy,
6429
+ expandHome,
5569
6430
  filter,
5570
6431
  formatAgentResumedEvent,
5571
6432
  formatAgentWaitingEvent,
@@ -5585,20 +6446,26 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5585
6446
  isCustomInterrupt,
5586
6447
  isHumanRequestInterrupt,
5587
6448
  isMemoryCheckpointer,
6449
+ isScriptResource,
5588
6450
  isTracingEnabled,
5589
6451
  loadPrompt,
5590
6452
  map,
5591
6453
  merge,
5592
6454
  mergeState,
6455
+ normalizeRootConfig,
5593
6456
  parallel,
5594
6457
  parseSSEEvent,
6458
+ parseSkillContent,
5595
6459
  presets,
5596
6460
  production,
5597
6461
  reduce,
5598
6462
  renderTemplate,
6463
+ resolveResourcePath,
5599
6464
  retry,
5600
6465
  safeValidateSchemaDescriptions,
5601
6466
  sanitizeValue,
6467
+ scanAllSkillRoots,
6468
+ scanSkillRoot,
5602
6469
  sendMessage,
5603
6470
  sequential,
5604
6471
  sequentialBuilder,
@@ -5610,6 +6477,9 @@ function loadPrompt(promptName, options = {}, promptsDir) {
5610
6477
  toLangChainTools,
5611
6478
  toolBuilder,
5612
6479
  validateSchemaDescriptions,
6480
+ validateSkillDescription,
6481
+ validateSkillName,
6482
+ validateSkillNameMatchesDir,
5613
6483
  validateState,
5614
6484
  validateTool,
5615
6485
  validateToolMetadata,