@juspay/neurolink 9.84.2 → 9.85.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/browser/neurolink.min.js +436 -401
  3. package/dist/cli/factories/commandFactory.d.ts +17 -0
  4. package/dist/cli/factories/commandFactory.js +254 -0
  5. package/dist/cli/parser.js +2 -0
  6. package/dist/cli/utils/skillsFlags.d.ts +10 -0
  7. package/dist/cli/utils/skillsFlags.js +22 -0
  8. package/dist/index.d.ts +5 -0
  9. package/dist/index.js +6 -0
  10. package/dist/lib/index.d.ts +5 -0
  11. package/dist/lib/index.js +6 -0
  12. package/dist/lib/neurolink.d.ts +28 -0
  13. package/dist/lib/neurolink.js +117 -0
  14. package/dist/lib/server/routes/agentRoutes.js +156 -1
  15. package/dist/lib/server/utils/validation.d.ts +32 -0
  16. package/dist/lib/server/utils/validation.js +18 -0
  17. package/dist/lib/session/globalSessionState.d.ts +10 -1
  18. package/dist/lib/session/globalSessionState.js +18 -0
  19. package/dist/lib/skills/skillMatcher.d.ts +20 -0
  20. package/dist/lib/skills/skillMatcher.js +80 -0
  21. package/dist/lib/skills/skillStoreRedis.d.ts +22 -0
  22. package/dist/lib/skills/skillStoreRedis.js +98 -0
  23. package/dist/lib/skills/skillStoreS3.d.ts +41 -0
  24. package/dist/lib/skills/skillStoreS3.js +233 -0
  25. package/dist/lib/skills/skillStores.d.ts +44 -0
  26. package/dist/lib/skills/skillStores.js +252 -0
  27. package/dist/lib/skills/skillTools.d.ts +19 -0
  28. package/dist/lib/skills/skillTools.js +340 -0
  29. package/dist/lib/skills/skillsManager.d.ts +54 -0
  30. package/dist/lib/skills/skillsManager.js +220 -0
  31. package/dist/lib/types/config.d.ts +10 -0
  32. package/dist/lib/types/generate.d.ts +8 -0
  33. package/dist/lib/types/index.d.ts +1 -0
  34. package/dist/lib/types/index.js +1 -0
  35. package/dist/lib/types/skills.d.ts +296 -0
  36. package/dist/lib/types/skills.js +17 -0
  37. package/dist/lib/types/stream.d.ts +8 -0
  38. package/dist/neurolink.d.ts +28 -0
  39. package/dist/neurolink.js +117 -0
  40. package/dist/server/routes/agentRoutes.js +156 -1
  41. package/dist/server/utils/validation.d.ts +32 -0
  42. package/dist/server/utils/validation.js +18 -0
  43. package/dist/session/globalSessionState.d.ts +10 -1
  44. package/dist/session/globalSessionState.js +18 -0
  45. package/dist/skills/skillMatcher.d.ts +20 -0
  46. package/dist/skills/skillMatcher.js +79 -0
  47. package/dist/skills/skillStoreRedis.d.ts +22 -0
  48. package/dist/skills/skillStoreRedis.js +97 -0
  49. package/dist/skills/skillStoreS3.d.ts +41 -0
  50. package/dist/skills/skillStoreS3.js +232 -0
  51. package/dist/skills/skillStores.d.ts +44 -0
  52. package/dist/skills/skillStores.js +251 -0
  53. package/dist/skills/skillTools.d.ts +19 -0
  54. package/dist/skills/skillTools.js +339 -0
  55. package/dist/skills/skillsManager.d.ts +54 -0
  56. package/dist/skills/skillsManager.js +219 -0
  57. package/dist/types/config.d.ts +10 -0
  58. package/dist/types/generate.d.ts +8 -0
  59. package/dist/types/index.d.ts +1 -0
  60. package/dist/types/index.js +1 -0
  61. package/dist/types/skills.d.ts +296 -0
  62. package/dist/types/skills.js +16 -0
  63. package/dist/types/stream.d.ts +8 -0
  64. package/package.json +2 -1
@@ -0,0 +1,232 @@
1
+ /**
2
+ * S3-backed skill store.
3
+ *
4
+ * Layout (curator-compatible):
5
+ * <prefix>skills/<id>.json — one JSON-serialized SkillDefinition per object
6
+ * <prefix>index.json — { lastUpdated, skills: SkillIndexItem[] }
7
+ *
8
+ * The index document is upserted on every write and rebuilt from a bucket
9
+ * listing when missing or unparsable (self-healing). Concurrent writers can
10
+ * race the index read-modify-write; the rebuild path recovers from any
11
+ * resulting drift, matching curator's production behavior.
12
+ *
13
+ * @aws-sdk/client-s3 is an optional peer — loaded lazily with the same
14
+ * createRequire pattern as memory's @juspay/hippocampus so importing
15
+ * NeuroLink core never requires the AWS SDK. Tests (and hosts with
16
+ * pre-configured clients) inject a SkillS3ObjectOps implementation instead.
17
+ */
18
+ import { createRequire } from "node:module";
19
+ import { logger } from "../utils/logger.js";
20
+ import { toSkillIndexItem } from "./skillMatcher.js";
21
+ const lazyRequire = createRequire(import.meta.url);
22
+ let cachedS3Module;
23
+ function loadS3Module() {
24
+ if (cachedS3Module !== undefined) {
25
+ return cachedS3Module;
26
+ }
27
+ try {
28
+ cachedS3Module = lazyRequire("@aws-sdk/client-s3");
29
+ return cachedS3Module;
30
+ }
31
+ catch (error) {
32
+ cachedS3Module = null;
33
+ logger.debug("[SkillStoreS3] @aws-sdk/client-s3 is not installed; S3 skill storage unavailable.", { error: error instanceof Error ? error.message : String(error) });
34
+ return null;
35
+ }
36
+ }
37
+ /** Build default object ops from @aws-sdk/client-s3. Throws when absent. */
38
+ function createDefaultOps(config) {
39
+ const mod = loadS3Module();
40
+ if (!mod) {
41
+ throw new Error("Skills S3 storage requires @aws-sdk/client-s3. Run `pnpm add @aws-sdk/client-s3` " +
42
+ "(or your package manager equivalent), or supply a custom store instead.");
43
+ }
44
+ const client = new mod.S3Client({
45
+ ...(config.region ? { region: config.region } : {}),
46
+ ...(config.endpoint ? { endpoint: config.endpoint } : {}),
47
+ ...(config.forcePathStyle !== undefined
48
+ ? { forcePathStyle: config.forcePathStyle }
49
+ : {}),
50
+ ...(config.credentials ? { credentials: config.credentials } : {}),
51
+ });
52
+ const bucket = config.bucket;
53
+ return {
54
+ async getObject(key) {
55
+ try {
56
+ const response = (await client.send(new mod.GetObjectCommand({ Bucket: bucket, Key: key })));
57
+ return (await response.Body?.transformToString()) ?? null;
58
+ }
59
+ catch (error) {
60
+ const name = error.name;
61
+ if (name === "NoSuchKey" || name === "NotFound") {
62
+ return null;
63
+ }
64
+ throw error;
65
+ }
66
+ },
67
+ async putObject(key, body) {
68
+ await client.send(new mod.PutObjectCommand({
69
+ Bucket: bucket,
70
+ Key: key,
71
+ Body: body,
72
+ ContentType: "application/json",
73
+ }));
74
+ },
75
+ async deleteObject(key) {
76
+ await client.send(new mod.DeleteObjectCommand({ Bucket: bucket, Key: key }));
77
+ },
78
+ async listKeys(prefix) {
79
+ const keys = [];
80
+ let continuationToken;
81
+ do {
82
+ const response = (await client.send(new mod.ListObjectsV2Command({
83
+ Bucket: bucket,
84
+ Prefix: prefix,
85
+ ...(continuationToken
86
+ ? { ContinuationToken: continuationToken }
87
+ : {}),
88
+ })));
89
+ for (const item of response.Contents ?? []) {
90
+ if (item.Key) {
91
+ keys.push(item.Key);
92
+ }
93
+ }
94
+ continuationToken = response.IsTruncated
95
+ ? response.NextContinuationToken
96
+ : undefined;
97
+ } while (continuationToken);
98
+ return keys;
99
+ },
100
+ };
101
+ }
102
+ export class S3SkillStore {
103
+ config;
104
+ injectedOps;
105
+ prefix;
106
+ ops = null;
107
+ constructor(config,
108
+ /** Test/host seam — omit to build ops from @aws-sdk/client-s3 lazily. */
109
+ injectedOps) {
110
+ this.config = config;
111
+ this.injectedOps = injectedOps;
112
+ const rawPrefix = config.prefix ?? "neurolink-skills/";
113
+ this.prefix =
114
+ rawPrefix === "" || rawPrefix.endsWith("/") ? rawPrefix : `${rawPrefix}/`;
115
+ }
116
+ getOps() {
117
+ if (!this.ops) {
118
+ this.ops = this.injectedOps ?? createDefaultOps(this.config);
119
+ }
120
+ return this.ops;
121
+ }
122
+ skillKey(id) {
123
+ return `${this.prefix}skills/${id}.json`;
124
+ }
125
+ indexKey() {
126
+ return `${this.prefix}index.json`;
127
+ }
128
+ async get(id) {
129
+ const body = await this.getOps().getObject(this.skillKey(id));
130
+ if (!body) {
131
+ return null;
132
+ }
133
+ try {
134
+ return JSON.parse(body);
135
+ }
136
+ catch (error) {
137
+ logger.warn("[SkillStoreS3] Failed to parse skill object", {
138
+ id,
139
+ error: error instanceof Error ? error.message : String(error),
140
+ });
141
+ return null;
142
+ }
143
+ }
144
+ async put(skill) {
145
+ const ops = this.getOps();
146
+ await ops.putObject(this.skillKey(skill.id), JSON.stringify(skill, null, 2));
147
+ const index = await this.readOrRebuildIndex();
148
+ const entry = toSkillIndexItem(skill);
149
+ const position = index.skills.findIndex((s) => s.id === skill.id);
150
+ if (position >= 0) {
151
+ index.skills[position] = entry;
152
+ }
153
+ else {
154
+ index.skills.push(entry);
155
+ }
156
+ await this.writeIndex(index);
157
+ }
158
+ async delete(id) {
159
+ const ops = this.getOps();
160
+ await ops.deleteObject(this.skillKey(id));
161
+ const index = await this.readOrRebuildIndex();
162
+ index.skills = index.skills.filter((s) => s.id !== id);
163
+ await this.writeIndex(index);
164
+ }
165
+ async index() {
166
+ const index = await this.readOrRebuildIndex();
167
+ return index.skills;
168
+ }
169
+ async writeIndex(index) {
170
+ index.lastUpdated = new Date().toISOString();
171
+ await this.getOps().putObject(this.indexKey(), JSON.stringify(index, null, 2));
172
+ }
173
+ /**
174
+ * Read index.json; when missing or unparsable, rebuild it from a listing
175
+ * of the skills/ prefix and persist the rebuilt document (self-heal).
176
+ */
177
+ async readOrRebuildIndex() {
178
+ const ops = this.getOps();
179
+ const raw = await ops.getObject(this.indexKey());
180
+ if (raw) {
181
+ try {
182
+ const parsed = JSON.parse(raw);
183
+ if (Array.isArray(parsed.skills)) {
184
+ return parsed;
185
+ }
186
+ }
187
+ catch (error) {
188
+ logger.warn("[SkillStoreS3] index.json unparsable — rebuilding", {
189
+ error: error instanceof Error ? error.message : String(error),
190
+ });
191
+ }
192
+ }
193
+ const skillsPrefix = `${this.prefix}skills/`;
194
+ const keys = await ops.listKeys(skillsPrefix);
195
+ const skills = [];
196
+ for (const key of keys) {
197
+ if (!key.endsWith(".json")) {
198
+ continue;
199
+ }
200
+ const body = await ops.getObject(key);
201
+ if (!body) {
202
+ continue;
203
+ }
204
+ try {
205
+ skills.push(toSkillIndexItem(JSON.parse(body)));
206
+ }
207
+ catch {
208
+ logger.warn("[SkillStoreS3] Skipping unparsable skill during rebuild", {
209
+ key,
210
+ });
211
+ }
212
+ }
213
+ const rebuilt = {
214
+ lastUpdated: new Date().toISOString(),
215
+ skills,
216
+ };
217
+ try {
218
+ await this.writeIndex(rebuilt);
219
+ logger.info("[SkillStoreS3] Rebuilt skills index", {
220
+ count: skills.length,
221
+ });
222
+ }
223
+ catch (error) {
224
+ // Index persistence is best-effort; the rebuilt in-memory copy is
225
+ // still returned so reads keep working.
226
+ logger.warn("[SkillStoreS3] Failed to persist rebuilt index", {
227
+ error: error instanceof Error ? error.message : String(error),
228
+ });
229
+ }
230
+ return rebuilt;
231
+ }
232
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Built-in skill stores + the storage-config factory.
3
+ *
4
+ * First-party backends: in-process memory (tests, embedded), filesystem
5
+ * (curator-style JSON alongside Claude-skills-style markdown — `<name>.md`
6
+ * or `<name>/SKILL.md` with YAML frontmatter), S3 (skillStoreS3.ts, optional
7
+ * @aws-sdk/client-s3 peer), and Redis (skillStoreRedis.ts, pooled core
8
+ * client). Anything else plugs in via `{ type: "custom", store }`.
9
+ */
10
+ import type { SkillDefinition, SkillIndexItem, SkillStore, SkillsStorageConfig } from "../types/index.js";
11
+ /** In-process store backed by a Map. */
12
+ export declare class InMemorySkillStore implements SkillStore {
13
+ private skills;
14
+ constructor(seed?: SkillDefinition[]);
15
+ get(id: string): Promise<SkillDefinition | null>;
16
+ put(skill: SkillDefinition): Promise<void>;
17
+ delete(id: string): Promise<void>;
18
+ index(): Promise<SkillIndexItem[]>;
19
+ }
20
+ /**
21
+ * Directory-backed store. Read layouts:
22
+ * - `<dir>/<id>.json` — JSON SkillDefinition (mutable)
23
+ * - `<dir>/<name>.md` — frontmatter markdown (read-only source)
24
+ * - `<dir>/<name>/SKILL.md` — Claude-skills directory layout (read-only source)
25
+ * Mutations always write `<id>.json`; a JSON file shadows a markdown skill
26
+ * with the same id, so updating a markdown-sourced skill "copies up" to JSON.
27
+ */
28
+ export declare class FileSystemSkillStore implements SkillStore {
29
+ private readonly baseDir;
30
+ private cache;
31
+ constructor(baseDir: string);
32
+ invalidate(): void;
33
+ get(id: string): Promise<SkillDefinition | null>;
34
+ put(skill: SkillDefinition): Promise<void>;
35
+ delete(id: string): Promise<void>;
36
+ index(): Promise<SkillIndexItem[]>;
37
+ /**
38
+ * Scan the directory into an id→skill map. Markdown sources load first
39
+ * so JSON files (the mutable layer) shadow them on id collision.
40
+ */
41
+ private load;
42
+ }
43
+ /** Resolve a storage config to a concrete store. Defaults to memory. */
44
+ export declare function createSkillStore(config?: SkillsStorageConfig): SkillStore;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Built-in skill stores + the storage-config factory.
3
+ *
4
+ * First-party backends: in-process memory (tests, embedded), filesystem
5
+ * (curator-style JSON alongside Claude-skills-style markdown — `<name>.md`
6
+ * or `<name>/SKILL.md` with YAML frontmatter), S3 (skillStoreS3.ts, optional
7
+ * @aws-sdk/client-s3 peer), and Redis (skillStoreRedis.ts, pooled core
8
+ * client). Anything else plugs in via `{ type: "custom", store }`.
9
+ */
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ import yaml from "js-yaml";
13
+ import { logger } from "../utils/logger.js";
14
+ import { toSkillIndexItem } from "./skillMatcher.js";
15
+ import { S3SkillStore } from "./skillStoreS3.js";
16
+ import { RedisSkillStore } from "./skillStoreRedis.js";
17
+ /** Fill defaulted fields so downstream logic never branches on undefined. */
18
+ function normalizeSkill(skill) {
19
+ return {
20
+ ...skill,
21
+ scope: skill.scope ?? "global",
22
+ status: skill.status ?? "active",
23
+ version: skill.version ?? 1,
24
+ tags: skill.tags ?? [],
25
+ };
26
+ }
27
+ /** Minimal structural validation for skills loaded from external sources. */
28
+ function isValidSkill(candidate) {
29
+ if (typeof candidate !== "object" || candidate === null) {
30
+ return false;
31
+ }
32
+ const record = candidate;
33
+ return (typeof record.id === "string" &&
34
+ record.id.length > 0 &&
35
+ typeof record.name === "string" &&
36
+ record.name.length > 0 &&
37
+ typeof record.description === "string" &&
38
+ typeof record.instructions === "string");
39
+ }
40
+ /** In-process store backed by a Map. */
41
+ export class InMemorySkillStore {
42
+ skills = new Map();
43
+ constructor(seed) {
44
+ for (const skill of seed ?? []) {
45
+ this.skills.set(skill.id, normalizeSkill(skill));
46
+ }
47
+ }
48
+ async get(id) {
49
+ return this.skills.get(id) ?? null;
50
+ }
51
+ async put(skill) {
52
+ this.skills.set(skill.id, normalizeSkill(skill));
53
+ }
54
+ async delete(id) {
55
+ this.skills.delete(id);
56
+ }
57
+ async index() {
58
+ return Array.from(this.skills.values()).map(toSkillIndexItem);
59
+ }
60
+ }
61
+ /**
62
+ * Parse a markdown document with optional YAML frontmatter into a skill.
63
+ * The body becomes `instructions`; frontmatter supplies index metadata.
64
+ * Returns null when required fields are missing (logged, not thrown —
65
+ * one malformed file must not take down the whole store).
66
+ */
67
+ function parseSkillMarkdown(raw, fallbackId) {
68
+ const frontmatterMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
69
+ let meta = {};
70
+ let body = raw;
71
+ if (frontmatterMatch) {
72
+ try {
73
+ const parsed = yaml.load(frontmatterMatch[1]);
74
+ if (typeof parsed === "object" && parsed !== null) {
75
+ meta = parsed;
76
+ }
77
+ }
78
+ catch (error) {
79
+ logger.warn("[SkillStore] Invalid YAML frontmatter in skill markdown", {
80
+ fallbackId,
81
+ error: error instanceof Error ? error.message : String(error),
82
+ });
83
+ return null;
84
+ }
85
+ body = raw.slice(frontmatterMatch[0].length);
86
+ }
87
+ const name = typeof meta.name === "string" ? meta.name : fallbackId;
88
+ const description = typeof meta.description === "string" ? meta.description : "";
89
+ const instructions = body.trim();
90
+ if (!name || !description || !instructions) {
91
+ logger.warn("[SkillStore] Skipping skill markdown missing name/description/body", { fallbackId });
92
+ return null;
93
+ }
94
+ const candidate = normalizeSkill({
95
+ id: typeof meta.id === "string" ? meta.id : name,
96
+ name,
97
+ description,
98
+ instructions,
99
+ ...(typeof meta.displayName === "string"
100
+ ? { displayName: meta.displayName }
101
+ : {}),
102
+ ...(Array.isArray(meta.tags)
103
+ ? { tags: meta.tags.map((t) => String(t)) }
104
+ : {}),
105
+ ...(meta.scope === "scoped" || meta.scope === "global"
106
+ ? { scope: meta.scope }
107
+ : {}),
108
+ ...(Array.isArray(meta.scopeIds)
109
+ ? { scopeIds: meta.scopeIds.map((s) => String(s)) }
110
+ : {}),
111
+ ...(typeof meta.version === "number" ? { version: meta.version } : {}),
112
+ });
113
+ return candidate;
114
+ }
115
+ /**
116
+ * Directory-backed store. Read layouts:
117
+ * - `<dir>/<id>.json` — JSON SkillDefinition (mutable)
118
+ * - `<dir>/<name>.md` — frontmatter markdown (read-only source)
119
+ * - `<dir>/<name>/SKILL.md` — Claude-skills directory layout (read-only source)
120
+ * Mutations always write `<id>.json`; a JSON file shadows a markdown skill
121
+ * with the same id, so updating a markdown-sourced skill "copies up" to JSON.
122
+ */
123
+ export class FileSystemSkillStore {
124
+ baseDir;
125
+ cache = null;
126
+ constructor(baseDir) {
127
+ this.baseDir = baseDir;
128
+ }
129
+ invalidate() {
130
+ this.cache = null;
131
+ }
132
+ async get(id) {
133
+ const all = this.load();
134
+ return all.get(id) ?? null;
135
+ }
136
+ async put(skill) {
137
+ fs.mkdirSync(this.baseDir, { recursive: true });
138
+ const filePath = path.join(this.baseDir, `${skill.id}.json`);
139
+ fs.writeFileSync(filePath, JSON.stringify(normalizeSkill(skill), null, 2), "utf-8");
140
+ this.invalidate();
141
+ }
142
+ async delete(id) {
143
+ const filePath = path.join(this.baseDir, `${id}.json`);
144
+ try {
145
+ fs.unlinkSync(filePath);
146
+ }
147
+ catch (error) {
148
+ const code = error.code;
149
+ if (code !== "ENOENT") {
150
+ throw error;
151
+ }
152
+ }
153
+ this.invalidate();
154
+ }
155
+ async index() {
156
+ return Array.from(this.load().values()).map(toSkillIndexItem);
157
+ }
158
+ /**
159
+ * Scan the directory into an id→skill map. Markdown sources load first
160
+ * so JSON files (the mutable layer) shadow them on id collision.
161
+ */
162
+ load() {
163
+ if (this.cache) {
164
+ return this.cache;
165
+ }
166
+ const skills = new Map();
167
+ let entries;
168
+ try {
169
+ entries = fs.readdirSync(this.baseDir, { withFileTypes: true });
170
+ }
171
+ catch (error) {
172
+ const code = error.code;
173
+ if (code !== "ENOENT") {
174
+ logger.warn("[SkillStore] Failed to read skills directory", {
175
+ baseDir: this.baseDir,
176
+ error: error instanceof Error ? error.message : String(error),
177
+ });
178
+ }
179
+ this.cache = skills;
180
+ return skills;
181
+ }
182
+ const jsonFiles = [];
183
+ for (const entry of entries) {
184
+ const fullPath = path.join(this.baseDir, entry.name);
185
+ try {
186
+ if (entry.isDirectory()) {
187
+ const skillMd = path.join(fullPath, "SKILL.md");
188
+ if (fs.existsSync(skillMd)) {
189
+ const parsed = parseSkillMarkdown(fs.readFileSync(skillMd, "utf-8"), entry.name);
190
+ if (parsed) {
191
+ skills.set(parsed.id, parsed);
192
+ }
193
+ }
194
+ }
195
+ else if (entry.name.endsWith(".md")) {
196
+ const parsed = parseSkillMarkdown(fs.readFileSync(fullPath, "utf-8"), entry.name.replace(/\.md$/, ""));
197
+ if (parsed) {
198
+ skills.set(parsed.id, parsed);
199
+ }
200
+ }
201
+ else if (entry.name.endsWith(".json")) {
202
+ jsonFiles.push(fullPath);
203
+ }
204
+ }
205
+ catch (error) {
206
+ logger.warn("[SkillStore] Failed to load skill file", {
207
+ file: fullPath,
208
+ error: error instanceof Error ? error.message : String(error),
209
+ });
210
+ }
211
+ }
212
+ // JSON layer last — shadows markdown-sourced skills with the same id.
213
+ for (const filePath of jsonFiles) {
214
+ try {
215
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
216
+ if (isValidSkill(parsed)) {
217
+ skills.set(parsed.id, normalizeSkill(parsed));
218
+ }
219
+ else {
220
+ logger.warn("[SkillStore] Skipping invalid skill JSON", {
221
+ file: filePath,
222
+ });
223
+ }
224
+ }
225
+ catch (error) {
226
+ logger.warn("[SkillStore] Failed to parse skill JSON", {
227
+ file: filePath,
228
+ error: error instanceof Error ? error.message : String(error),
229
+ });
230
+ }
231
+ }
232
+ this.cache = skills;
233
+ return skills;
234
+ }
235
+ }
236
+ /** Resolve a storage config to a concrete store. Defaults to memory. */
237
+ export function createSkillStore(config) {
238
+ if (!config || config.type === "memory") {
239
+ return new InMemorySkillStore(config?.type === "memory" ? config.skills : undefined);
240
+ }
241
+ if (config.type === "filesystem") {
242
+ return new FileSystemSkillStore(config.path);
243
+ }
244
+ if (config.type === "s3") {
245
+ return new S3SkillStore(config);
246
+ }
247
+ if (config.type === "redis") {
248
+ return new RedisSkillStore(config);
249
+ }
250
+ return config.store;
251
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Built-in skill tools, following the createMemoryRetrievalTools /
3
+ * createFileTools / createTaskTools factory pattern.
4
+ *
5
+ * The manager is resolved lazily via a resolver callback so tools can be
6
+ * registered at construction time while the store initializes on first
7
+ * use (mirrors how retrieve_context resolves conversationMemory lazily).
8
+ *
9
+ * Tool descriptions are ported from curator's production skills tools —
10
+ * the "always check skills first / no_match is expected, not an error /
11
+ * pick the best of 2-5 or ask" prompt engineering is proven in prod.
12
+ */
13
+ import type { SkillsManagerLike, SkillToolsOptions, Tool } from "../types/index.js";
14
+ /**
15
+ * Create the built-in skill tools bound to a lazily-resolved manager.
16
+ * Returns Vercel AI SDK tool() objects (description + Zod inputSchema +
17
+ * execute) keyed by tool name.
18
+ */
19
+ export declare function createSkillTools(resolveManager: () => SkillsManagerLike | null, options?: SkillToolsOptions): Record<string, Tool>;