@elisym/sdk 0.12.4 → 0.13.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/agent-store.cjs +16 -22
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.d.cts +7 -3
- package/dist/agent-store.d.ts +7 -3
- package/dist/agent-store.js +16 -22
- package/dist/agent-store.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.js.map +1 -1
- package/dist/skills.cjs +472 -93
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.d.cts +235 -3
- package/dist/skills.d.ts +235 -3
- package/dist/skills.js +465 -94
- package/dist/skills.js.map +1 -1
- package/package.json +1 -1
package/dist/skills.cjs
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var child_process = require('child_process');
|
|
4
4
|
var string_decoder = require('string_decoder');
|
|
5
|
-
var
|
|
5
|
+
var promises = require('fs/promises');
|
|
6
6
|
var path = require('path');
|
|
7
|
+
var fs = require('fs');
|
|
7
8
|
var YAML = require('yaml');
|
|
8
9
|
var Decimal = require('decimal.js-light');
|
|
9
10
|
|
|
@@ -29,9 +30,9 @@ function sleepWithSignal(ms, signal) {
|
|
|
29
30
|
return Promise.reject(createAbortError());
|
|
30
31
|
}
|
|
31
32
|
if (!signal) {
|
|
32
|
-
return new Promise((
|
|
33
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
33
34
|
}
|
|
34
|
-
return new Promise((
|
|
35
|
+
return new Promise((resolve2, reject) => {
|
|
35
36
|
const cleanup = () => {
|
|
36
37
|
clearTimeout(timer);
|
|
37
38
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -42,7 +43,7 @@ function sleepWithSignal(ms, signal) {
|
|
|
42
43
|
};
|
|
43
44
|
const timer = setTimeout(() => {
|
|
44
45
|
cleanup();
|
|
45
|
-
|
|
46
|
+
resolve2();
|
|
46
47
|
}, ms);
|
|
47
48
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
48
49
|
});
|
|
@@ -309,14 +310,62 @@ function createLlmClient(config) {
|
|
|
309
310
|
}
|
|
310
311
|
return createAnthropicClient(config);
|
|
311
312
|
}
|
|
312
|
-
var
|
|
313
|
-
var
|
|
313
|
+
var MAX_SCRIPT_OUTPUT = 1e6;
|
|
314
|
+
var DEFAULT_SCRIPT_TIMEOUT_MS = 6e4;
|
|
315
|
+
function runScript(cmd, args, opts) {
|
|
316
|
+
return new Promise((resolveResult) => {
|
|
317
|
+
const maxOutput = opts.maxOutput ?? MAX_SCRIPT_OUTPUT;
|
|
318
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS;
|
|
319
|
+
const child = child_process.spawn(cmd, args, {
|
|
320
|
+
cwd: opts.cwd,
|
|
321
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
322
|
+
timeout: timeoutMs,
|
|
323
|
+
killSignal: "SIGKILL",
|
|
324
|
+
signal: opts.signal
|
|
325
|
+
});
|
|
326
|
+
let stdout = "";
|
|
327
|
+
let stderr = "";
|
|
328
|
+
const stdoutDecoder = new string_decoder.StringDecoder("utf8");
|
|
329
|
+
const stderrDecoder = new string_decoder.StringDecoder("utf8");
|
|
330
|
+
child.stdout?.on("data", (data) => {
|
|
331
|
+
if (stdout.length < maxOutput) {
|
|
332
|
+
stdout += stdoutDecoder.write(data);
|
|
333
|
+
if (stdout.length > maxOutput) {
|
|
334
|
+
stdout = stdout.slice(0, maxOutput);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
child.stderr?.on("data", (data) => {
|
|
339
|
+
if (stderr.length < maxOutput) {
|
|
340
|
+
stderr += stderrDecoder.write(data);
|
|
341
|
+
if (stderr.length > maxOutput) {
|
|
342
|
+
stderr = stderr.slice(0, maxOutput);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
child.on("close", (code) => {
|
|
347
|
+
stdout += stdoutDecoder.end();
|
|
348
|
+
stderr += stderrDecoder.end();
|
|
349
|
+
resolveResult({ stdout, stderr, code });
|
|
350
|
+
});
|
|
351
|
+
child.on("error", (err) => {
|
|
352
|
+
resolveResult({ stdout, stderr, code: null, spawnError: err });
|
|
353
|
+
});
|
|
354
|
+
if (child.stdin) {
|
|
355
|
+
child.stdin.on("error", () => {
|
|
356
|
+
});
|
|
357
|
+
child.stdin.end(opts.stdin ?? "");
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}
|
|
314
361
|
var ScriptSkill = class {
|
|
315
362
|
name;
|
|
316
363
|
description;
|
|
317
364
|
capabilities;
|
|
318
365
|
priceSubunits;
|
|
319
366
|
asset;
|
|
367
|
+
mode = "llm";
|
|
368
|
+
llmOverride;
|
|
320
369
|
image;
|
|
321
370
|
imageFile;
|
|
322
371
|
skillDir;
|
|
@@ -330,6 +379,7 @@ var ScriptSkill = class {
|
|
|
330
379
|
this.capabilities = params.capabilities;
|
|
331
380
|
this.priceSubunits = params.priceSubunits;
|
|
332
381
|
this.asset = params.asset;
|
|
382
|
+
this.llmOverride = params.llmOverride;
|
|
333
383
|
this.image = params.image;
|
|
334
384
|
this.imageFile = params.imageFile;
|
|
335
385
|
this.skillDir = params.skillDir;
|
|
@@ -339,11 +389,9 @@ var ScriptSkill = class {
|
|
|
339
389
|
this.logger = params.logger ?? {};
|
|
340
390
|
}
|
|
341
391
|
async execute(input, ctx) {
|
|
342
|
-
|
|
343
|
-
throw new Error("LLM client not configured for skill runtime");
|
|
344
|
-
}
|
|
392
|
+
const llm = this.resolveLlmClient(ctx);
|
|
345
393
|
if (this.tools.length === 0) {
|
|
346
|
-
const result = await
|
|
394
|
+
const result = await llm.complete(this.systemPrompt, input.data, ctx.signal);
|
|
347
395
|
return { data: result };
|
|
348
396
|
}
|
|
349
397
|
const toolDefs = this.tools.map((tool) => ({
|
|
@@ -356,7 +404,6 @@ var ScriptSkill = class {
|
|
|
356
404
|
}))
|
|
357
405
|
}));
|
|
358
406
|
const messages = [{ role: "user", content: input.data }];
|
|
359
|
-
const llm = ctx.llm;
|
|
360
407
|
for (let round = 0; round < this.maxToolRounds; round++) {
|
|
361
408
|
if (ctx.signal?.aborted) {
|
|
362
409
|
throw new Error("Job aborted");
|
|
@@ -388,76 +435,191 @@ var ScriptSkill = class {
|
|
|
388
435
|
}
|
|
389
436
|
throw new Error(`Max tool rounds (${this.maxToolRounds}) exceeded`);
|
|
390
437
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Resolve the LLM client for this skill from the runtime context.
|
|
440
|
+
*
|
|
441
|
+
* Contract:
|
|
442
|
+
* - When `llmOverride` is set, `ctx.getLlm` MUST be wired. Falling back to
|
|
443
|
+
* `ctx.llm` (the agent default) would silently use the wrong configuration
|
|
444
|
+
* for max-tokens-only overrides.
|
|
445
|
+
* - When no override is set, prefer `ctx.getLlm()` (returns the agent
|
|
446
|
+
* default), then fall back to `ctx.llm` for legacy callers that wire only
|
|
447
|
+
* a single client.
|
|
448
|
+
*/
|
|
449
|
+
resolveLlmClient(ctx) {
|
|
450
|
+
let client;
|
|
451
|
+
if (this.llmOverride) {
|
|
452
|
+
client = ctx.getLlm?.(this.llmOverride);
|
|
453
|
+
if (!client) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
`Skill "${this.name}" requires ctx.getLlm to be configured (llmOverride is set)`
|
|
456
|
+
);
|
|
398
457
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
458
|
+
return client;
|
|
459
|
+
}
|
|
460
|
+
client = ctx.getLlm?.() ?? ctx.llm;
|
|
461
|
+
if (!client) {
|
|
462
|
+
throw new Error("LLM client not configured for skill runtime");
|
|
463
|
+
}
|
|
464
|
+
return client;
|
|
465
|
+
}
|
|
466
|
+
async runTool(toolDef, call, signal) {
|
|
467
|
+
const args = [...toolDef.command];
|
|
468
|
+
const cmd = args.shift();
|
|
469
|
+
if (!cmd) {
|
|
470
|
+
return `Error: tool "${toolDef.name}" has an empty command`;
|
|
471
|
+
}
|
|
472
|
+
const params = toolDef.parameters ?? [];
|
|
473
|
+
for (let index = 0; index < params.length; index++) {
|
|
474
|
+
const param = params[index];
|
|
475
|
+
if (!param) {
|
|
476
|
+
continue;
|
|
414
477
|
}
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
478
|
+
const value = call.arguments[param.name];
|
|
479
|
+
if (value === void 0) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (param.required && index === 0) {
|
|
483
|
+
args.push(String(value));
|
|
484
|
+
} else {
|
|
485
|
+
args.push(`--${param.name}`, String(value));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const result = await runScript(cmd, args, { cwd: this.skillDir, signal });
|
|
489
|
+
if (result.spawnError) {
|
|
490
|
+
return `Error: ${result.spawnError.message}`;
|
|
491
|
+
}
|
|
492
|
+
if (result.code === 0) {
|
|
493
|
+
return result.stdout.trim();
|
|
494
|
+
}
|
|
495
|
+
this.logger.debug?.(
|
|
496
|
+
{ tool: toolDef.name, code: result.code, stderrLen: result.stderr.length },
|
|
497
|
+
"skill tool exited non-zero"
|
|
498
|
+
);
|
|
499
|
+
return `Error (exit ${result.code}): ${result.stderr.trim() || result.stdout.trim()}`;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
var MAX_STATIC_FILE_SIZE = 256 * 1024;
|
|
503
|
+
var StaticFileSkill = class {
|
|
504
|
+
name;
|
|
505
|
+
description;
|
|
506
|
+
capabilities;
|
|
507
|
+
priceSubunits;
|
|
508
|
+
asset;
|
|
509
|
+
mode = "static-file";
|
|
510
|
+
image;
|
|
511
|
+
imageFile;
|
|
512
|
+
outputFilePath;
|
|
513
|
+
constructor(params) {
|
|
514
|
+
this.name = params.name;
|
|
515
|
+
this.description = params.description;
|
|
516
|
+
this.capabilities = params.capabilities;
|
|
517
|
+
this.priceSubunits = params.priceSubunits;
|
|
518
|
+
this.asset = params.asset;
|
|
519
|
+
this.image = params.image;
|
|
520
|
+
this.imageFile = params.imageFile;
|
|
521
|
+
this.outputFilePath = params.outputFilePath;
|
|
522
|
+
}
|
|
523
|
+
async execute(_input, _ctx) {
|
|
524
|
+
const buffer = await promises.readFile(this.outputFilePath);
|
|
525
|
+
if (buffer.length > MAX_STATIC_FILE_SIZE) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`static-file output exceeds ${MAX_STATIC_FILE_SIZE} bytes (got ${buffer.length})`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return { data: buffer.toString("utf-8") };
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var StaticScriptSkill = class {
|
|
534
|
+
name;
|
|
535
|
+
description;
|
|
536
|
+
capabilities;
|
|
537
|
+
priceSubunits;
|
|
538
|
+
asset;
|
|
539
|
+
mode = "static-script";
|
|
540
|
+
image;
|
|
541
|
+
imageFile;
|
|
542
|
+
scriptPath;
|
|
543
|
+
scriptArgs;
|
|
544
|
+
scriptTimeoutMs;
|
|
545
|
+
constructor(params) {
|
|
546
|
+
this.name = params.name;
|
|
547
|
+
this.description = params.description;
|
|
548
|
+
this.capabilities = params.capabilities;
|
|
549
|
+
this.priceSubunits = params.priceSubunits;
|
|
550
|
+
this.asset = params.asset;
|
|
551
|
+
this.image = params.image;
|
|
552
|
+
this.imageFile = params.imageFile;
|
|
553
|
+
this.scriptPath = params.scriptPath;
|
|
554
|
+
this.scriptArgs = params.scriptArgs;
|
|
555
|
+
this.scriptTimeoutMs = params.scriptTimeoutMs;
|
|
556
|
+
}
|
|
557
|
+
async execute(_input, ctx) {
|
|
558
|
+
const result = await runScript(this.scriptPath, this.scriptArgs, {
|
|
559
|
+
cwd: path.dirname(this.scriptPath),
|
|
560
|
+
signal: ctx.signal,
|
|
561
|
+
timeoutMs: this.scriptTimeoutMs
|
|
458
562
|
});
|
|
563
|
+
if (result.spawnError) {
|
|
564
|
+
throw new Error(`script spawn failed: ${result.spawnError.message}`);
|
|
565
|
+
}
|
|
566
|
+
if (result.code !== 0) {
|
|
567
|
+
const detail = result.stderr.trim() || result.stdout.trim() || "(no output)";
|
|
568
|
+
throw new Error(`script failed (exit ${result.code}): ${detail}`);
|
|
569
|
+
}
|
|
570
|
+
return { data: result.stdout.trim() };
|
|
459
571
|
}
|
|
460
572
|
};
|
|
573
|
+
var DynamicScriptSkill = class {
|
|
574
|
+
name;
|
|
575
|
+
description;
|
|
576
|
+
capabilities;
|
|
577
|
+
priceSubunits;
|
|
578
|
+
asset;
|
|
579
|
+
mode = "dynamic-script";
|
|
580
|
+
image;
|
|
581
|
+
imageFile;
|
|
582
|
+
scriptPath;
|
|
583
|
+
scriptArgs;
|
|
584
|
+
scriptTimeoutMs;
|
|
585
|
+
constructor(params) {
|
|
586
|
+
this.name = params.name;
|
|
587
|
+
this.description = params.description;
|
|
588
|
+
this.capabilities = params.capabilities;
|
|
589
|
+
this.priceSubunits = params.priceSubunits;
|
|
590
|
+
this.asset = params.asset;
|
|
591
|
+
this.image = params.image;
|
|
592
|
+
this.imageFile = params.imageFile;
|
|
593
|
+
this.scriptPath = params.scriptPath;
|
|
594
|
+
this.scriptArgs = params.scriptArgs;
|
|
595
|
+
this.scriptTimeoutMs = params.scriptTimeoutMs;
|
|
596
|
+
}
|
|
597
|
+
async execute(input, ctx) {
|
|
598
|
+
const result = await runScript(this.scriptPath, this.scriptArgs, {
|
|
599
|
+
cwd: path.dirname(this.scriptPath),
|
|
600
|
+
stdin: input.data,
|
|
601
|
+
signal: ctx.signal,
|
|
602
|
+
timeoutMs: this.scriptTimeoutMs
|
|
603
|
+
});
|
|
604
|
+
if (result.spawnError) {
|
|
605
|
+
throw new Error(`script spawn failed: ${result.spawnError.message}`);
|
|
606
|
+
}
|
|
607
|
+
if (result.code !== 0) {
|
|
608
|
+
const detail = result.stderr.trim() || result.stdout.trim() || "(no output)";
|
|
609
|
+
throw new Error(`script failed (exit ${result.code}): ${detail}`);
|
|
610
|
+
}
|
|
611
|
+
return { data: result.stdout.trim() };
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
function resolveInsidePath(rootDir, value) {
|
|
615
|
+
const root = path.resolve(rootDir);
|
|
616
|
+
const candidate = path.resolve(root, value);
|
|
617
|
+
const rel = path.relative(root, candidate);
|
|
618
|
+
if (rel === "" || rel.startsWith("..") || rel.includes(`..${path.sep}`)) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
return candidate;
|
|
622
|
+
}
|
|
461
623
|
var LAMPORTS_PER_SOL = 1e9;
|
|
462
624
|
var NATIVE_SOL = {
|
|
463
625
|
chain: "solana",
|
|
@@ -526,7 +688,15 @@ function parseAssetAmount(asset, human) {
|
|
|
526
688
|
Decimal__default.default.clone({ toExpNeg: -100, toExpPos: 100, precision: 50 });
|
|
527
689
|
|
|
528
690
|
// src/skills/loader.ts
|
|
691
|
+
var VALID_PROVIDERS = ["anthropic", "openai"];
|
|
692
|
+
var MAX_TOKENS_LIMIT = 2e5;
|
|
529
693
|
var DEFAULT_MAX_TOOL_ROUNDS = 10;
|
|
694
|
+
var VALID_MODES = [
|
|
695
|
+
"llm",
|
|
696
|
+
"static-file",
|
|
697
|
+
"static-script",
|
|
698
|
+
"dynamic-script"
|
|
699
|
+
];
|
|
530
700
|
function solToLamports(sol) {
|
|
531
701
|
const asNumber = typeof sol === "string" ? Number(sol) : sol;
|
|
532
702
|
if (!Number.isFinite(asNumber) || asNumber < 0) {
|
|
@@ -643,6 +813,86 @@ function validateTool(raw, skillName, index) {
|
|
|
643
813
|
parameters
|
|
644
814
|
};
|
|
645
815
|
}
|
|
816
|
+
function validateMode(skillName, raw) {
|
|
817
|
+
if (raw === void 0 || raw === null) {
|
|
818
|
+
return "llm";
|
|
819
|
+
}
|
|
820
|
+
if (typeof raw !== "string") {
|
|
821
|
+
throw new Error(`SKILL.md "${skillName}": "mode" must be a string`);
|
|
822
|
+
}
|
|
823
|
+
if (!VALID_MODES.includes(raw)) {
|
|
824
|
+
throw new Error(
|
|
825
|
+
`SKILL.md "${skillName}": invalid mode "${raw}". Allowed: ${VALID_MODES.join(", ")}`
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
return raw;
|
|
829
|
+
}
|
|
830
|
+
function validateScriptArgs(skillName, raw) {
|
|
831
|
+
if (raw === void 0 || raw === null) {
|
|
832
|
+
return [];
|
|
833
|
+
}
|
|
834
|
+
if (!Array.isArray(raw)) {
|
|
835
|
+
throw new Error(`SKILL.md "${skillName}": "script_args" must be an array of strings`);
|
|
836
|
+
}
|
|
837
|
+
for (const part of raw) {
|
|
838
|
+
if (typeof part !== "string") {
|
|
839
|
+
throw new Error(`SKILL.md "${skillName}": "script_args" entries must be strings`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return raw;
|
|
843
|
+
}
|
|
844
|
+
function validateLlmOverride(skillName, frontmatter, mode) {
|
|
845
|
+
const hasProvider = frontmatter.provider !== void 0 && frontmatter.provider !== null;
|
|
846
|
+
const hasModel = frontmatter.model !== void 0 && frontmatter.model !== null;
|
|
847
|
+
const hasMaxTokens = frontmatter.max_tokens !== void 0 && frontmatter.max_tokens !== null;
|
|
848
|
+
if (!hasProvider && !hasModel && !hasMaxTokens) {
|
|
849
|
+
return void 0;
|
|
850
|
+
}
|
|
851
|
+
if (mode !== "llm") {
|
|
852
|
+
throw new Error(
|
|
853
|
+
`SKILL.md "${skillName}": "provider"/"model"/"max_tokens" are only valid in mode 'llm' (got '${mode}')`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
if (hasProvider !== hasModel) {
|
|
857
|
+
throw new Error(
|
|
858
|
+
`SKILL.md "${skillName}": "provider" and "model" must be set together (declare both, or neither)`
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
const override = {};
|
|
862
|
+
if (hasProvider && hasModel) {
|
|
863
|
+
if (typeof frontmatter.provider !== "string") {
|
|
864
|
+
throw new Error(`SKILL.md "${skillName}": "provider" must be a string`);
|
|
865
|
+
}
|
|
866
|
+
if (!VALID_PROVIDERS.includes(frontmatter.provider)) {
|
|
867
|
+
throw new Error(
|
|
868
|
+
`SKILL.md "${skillName}": invalid provider "${frontmatter.provider}". Allowed: ${VALID_PROVIDERS.join(", ")}`
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
if (typeof frontmatter.model !== "string" || frontmatter.model.length === 0) {
|
|
872
|
+
throw new Error(`SKILL.md "${skillName}": "model" must be a non-empty string`);
|
|
873
|
+
}
|
|
874
|
+
override.provider = frontmatter.provider;
|
|
875
|
+
override.model = frontmatter.model;
|
|
876
|
+
}
|
|
877
|
+
if (hasMaxTokens) {
|
|
878
|
+
if (typeof frontmatter.max_tokens !== "number" || !Number.isInteger(frontmatter.max_tokens) || frontmatter.max_tokens <= 0 || frontmatter.max_tokens > MAX_TOKENS_LIMIT) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
`SKILL.md "${skillName}": "max_tokens" must be a positive integer <= ${MAX_TOKENS_LIMIT}`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
override.maxTokens = frontmatter.max_tokens;
|
|
884
|
+
}
|
|
885
|
+
return override;
|
|
886
|
+
}
|
|
887
|
+
function validateScriptTimeoutMs(skillName, raw) {
|
|
888
|
+
if (raw === void 0 || raw === null) {
|
|
889
|
+
return void 0;
|
|
890
|
+
}
|
|
891
|
+
if (typeof raw !== "number" || !Number.isInteger(raw) || raw <= 0) {
|
|
892
|
+
throw new Error(`SKILL.md "${skillName}": "script_timeout_ms" must be a positive integer`);
|
|
893
|
+
}
|
|
894
|
+
return raw;
|
|
895
|
+
}
|
|
646
896
|
function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
647
897
|
if (typeof frontmatter.name !== "string" || frontmatter.name.length === 0) {
|
|
648
898
|
throw new Error('SKILL.md: missing or invalid "name" field');
|
|
@@ -694,8 +944,14 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
|
694
944
|
);
|
|
695
945
|
}
|
|
696
946
|
}
|
|
947
|
+
const mode = validateMode(frontmatter.name, frontmatter.mode);
|
|
697
948
|
const tools = [];
|
|
698
949
|
if (frontmatter.tools !== void 0) {
|
|
950
|
+
if (mode !== "llm") {
|
|
951
|
+
throw new Error(
|
|
952
|
+
`SKILL.md "${frontmatter.name}": "tools" is only valid in mode 'llm' (got '${mode}')`
|
|
953
|
+
);
|
|
954
|
+
}
|
|
699
955
|
if (!Array.isArray(frontmatter.tools)) {
|
|
700
956
|
throw new Error(`SKILL.md "${frontmatter.name}": "tools" must be an array`);
|
|
701
957
|
}
|
|
@@ -705,6 +961,11 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
|
705
961
|
}
|
|
706
962
|
let maxToolRounds = DEFAULT_MAX_TOOL_ROUNDS;
|
|
707
963
|
if (frontmatter.max_tool_rounds !== void 0) {
|
|
964
|
+
if (mode !== "llm") {
|
|
965
|
+
throw new Error(
|
|
966
|
+
`SKILL.md "${frontmatter.name}": "max_tool_rounds" is only valid in mode 'llm' (got '${mode}')`
|
|
967
|
+
);
|
|
968
|
+
}
|
|
708
969
|
if (typeof frontmatter.max_tool_rounds !== "number" || !Number.isInteger(frontmatter.max_tool_rounds) || frontmatter.max_tool_rounds <= 0) {
|
|
709
970
|
throw new Error(
|
|
710
971
|
`SKILL.md "${frontmatter.name}": "max_tool_rounds" must be a positive integer`
|
|
@@ -712,21 +973,146 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
|
712
973
|
}
|
|
713
974
|
maxToolRounds = frontmatter.max_tool_rounds;
|
|
714
975
|
}
|
|
976
|
+
let outputFile;
|
|
977
|
+
let script;
|
|
978
|
+
let scriptArgs = [];
|
|
979
|
+
let scriptTimeoutMs;
|
|
980
|
+
if (mode === "static-file") {
|
|
981
|
+
if (typeof frontmatter.output_file !== "string" || frontmatter.output_file.length === 0) {
|
|
982
|
+
throw new Error(
|
|
983
|
+
`SKILL.md "${frontmatter.name}": mode 'static-file' requires "output_file" (string)`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
if (frontmatter.script !== void 0) {
|
|
987
|
+
throw new Error(
|
|
988
|
+
`SKILL.md "${frontmatter.name}": "script" is not valid in mode 'static-file'`
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
outputFile = frontmatter.output_file;
|
|
992
|
+
} else if (mode === "static-script" || mode === "dynamic-script") {
|
|
993
|
+
if (typeof frontmatter.script !== "string" || frontmatter.script.length === 0) {
|
|
994
|
+
throw new Error(`SKILL.md "${frontmatter.name}": mode '${mode}' requires "script" (string)`);
|
|
995
|
+
}
|
|
996
|
+
if (frontmatter.output_file !== void 0) {
|
|
997
|
+
throw new Error(
|
|
998
|
+
`SKILL.md "${frontmatter.name}": "output_file" is only valid in mode 'static-file'`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
script = frontmatter.script;
|
|
1002
|
+
scriptArgs = validateScriptArgs(frontmatter.name, frontmatter.script_args);
|
|
1003
|
+
scriptTimeoutMs = validateScriptTimeoutMs(frontmatter.name, frontmatter.script_timeout_ms);
|
|
1004
|
+
} else {
|
|
1005
|
+
if (frontmatter.output_file !== void 0) {
|
|
1006
|
+
throw new Error(
|
|
1007
|
+
`SKILL.md "${frontmatter.name}": "output_file" is only valid in mode 'static-file'`
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
if (frontmatter.script !== void 0) {
|
|
1011
|
+
throw new Error(
|
|
1012
|
+
`SKILL.md "${frontmatter.name}": "script" is only valid in script modes (static-script, dynamic-script)`
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
if (frontmatter.script_args !== void 0) {
|
|
1016
|
+
throw new Error(
|
|
1017
|
+
`SKILL.md "${frontmatter.name}": "script_args" is only valid in script modes`
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
if (frontmatter.script_timeout_ms !== void 0) {
|
|
1021
|
+
throw new Error(
|
|
1022
|
+
`SKILL.md "${frontmatter.name}": "script_timeout_ms" is only valid in script modes`
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
715
1026
|
const image = typeof frontmatter.image === "string" ? frontmatter.image : void 0;
|
|
716
1027
|
const imageFile = typeof frontmatter.image_file === "string" ? frontmatter.image_file : void 0;
|
|
1028
|
+
const llmOverride = validateLlmOverride(frontmatter.name, frontmatter, mode);
|
|
717
1029
|
return {
|
|
718
1030
|
name: frontmatter.name,
|
|
719
1031
|
description: frontmatter.description,
|
|
720
1032
|
capabilities,
|
|
721
1033
|
priceSubunits,
|
|
722
1034
|
asset,
|
|
1035
|
+
mode,
|
|
723
1036
|
systemPrompt,
|
|
724
1037
|
tools,
|
|
725
1038
|
maxToolRounds,
|
|
1039
|
+
llmOverride,
|
|
726
1040
|
image,
|
|
727
|
-
imageFile
|
|
1041
|
+
imageFile,
|
|
1042
|
+
outputFile,
|
|
1043
|
+
script,
|
|
1044
|
+
scriptArgs,
|
|
1045
|
+
scriptTimeoutMs
|
|
728
1046
|
};
|
|
729
1047
|
}
|
|
1048
|
+
function buildSkillFromParsed(parsed, skillDir, logger) {
|
|
1049
|
+
switch (parsed.mode) {
|
|
1050
|
+
case "llm":
|
|
1051
|
+
return new ScriptSkill({
|
|
1052
|
+
name: parsed.name,
|
|
1053
|
+
description: parsed.description,
|
|
1054
|
+
capabilities: parsed.capabilities,
|
|
1055
|
+
priceSubunits: parsed.priceSubunits,
|
|
1056
|
+
asset: parsed.asset,
|
|
1057
|
+
skillDir,
|
|
1058
|
+
systemPrompt: parsed.systemPrompt,
|
|
1059
|
+
tools: parsed.tools,
|
|
1060
|
+
maxToolRounds: parsed.maxToolRounds,
|
|
1061
|
+
llmOverride: parsed.llmOverride,
|
|
1062
|
+
image: parsed.image,
|
|
1063
|
+
imageFile: parsed.imageFile,
|
|
1064
|
+
logger
|
|
1065
|
+
});
|
|
1066
|
+
case "static-file": {
|
|
1067
|
+
if (parsed.outputFile === void 0) {
|
|
1068
|
+
throw new Error(
|
|
1069
|
+
`SKILL.md "${parsed.name}": internal error - outputFile missing for mode 'static-file'`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
const outputFilePath = resolveInsidePath(skillDir, parsed.outputFile);
|
|
1073
|
+
if (!outputFilePath) {
|
|
1074
|
+
throw new Error(
|
|
1075
|
+
`SKILL.md "${parsed.name}": "output_file" must stay inside the skill directory`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
return new StaticFileSkill({
|
|
1079
|
+
name: parsed.name,
|
|
1080
|
+
description: parsed.description,
|
|
1081
|
+
capabilities: parsed.capabilities,
|
|
1082
|
+
priceSubunits: parsed.priceSubunits,
|
|
1083
|
+
asset: parsed.asset,
|
|
1084
|
+
outputFilePath,
|
|
1085
|
+
image: parsed.image,
|
|
1086
|
+
imageFile: parsed.imageFile
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
case "static-script":
|
|
1090
|
+
case "dynamic-script": {
|
|
1091
|
+
if (parsed.script === void 0) {
|
|
1092
|
+
throw new Error(
|
|
1093
|
+
`SKILL.md "${parsed.name}": internal error - script missing for mode '${parsed.mode}'`
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
const scriptPath = resolveInsidePath(skillDir, parsed.script);
|
|
1097
|
+
if (!scriptPath) {
|
|
1098
|
+
throw new Error(`SKILL.md "${parsed.name}": "script" must stay inside the skill directory`);
|
|
1099
|
+
}
|
|
1100
|
+
const Ctor = parsed.mode === "static-script" ? StaticScriptSkill : DynamicScriptSkill;
|
|
1101
|
+
return new Ctor({
|
|
1102
|
+
name: parsed.name,
|
|
1103
|
+
description: parsed.description,
|
|
1104
|
+
capabilities: parsed.capabilities,
|
|
1105
|
+
priceSubunits: parsed.priceSubunits,
|
|
1106
|
+
asset: parsed.asset,
|
|
1107
|
+
scriptPath,
|
|
1108
|
+
scriptArgs: parsed.scriptArgs,
|
|
1109
|
+
scriptTimeoutMs: parsed.scriptTimeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS,
|
|
1110
|
+
image: parsed.image,
|
|
1111
|
+
imageFile: parsed.imageFile
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
730
1116
|
function loadSkillsFromDir(skillsDir, options = {}) {
|
|
731
1117
|
const logger = options.logger ?? {};
|
|
732
1118
|
const skills = [];
|
|
@@ -751,22 +1137,7 @@ function loadSkillsFromDir(skillsDir, options = {}) {
|
|
|
751
1137
|
const content = fs.readFileSync(skillMdPath, "utf-8");
|
|
752
1138
|
const { frontmatter, systemPrompt } = parseSkillMd(content);
|
|
753
1139
|
const parsed = validateSkillFrontmatter(frontmatter, systemPrompt, options);
|
|
754
|
-
skills.push(
|
|
755
|
-
new ScriptSkill({
|
|
756
|
-
name: parsed.name,
|
|
757
|
-
description: parsed.description,
|
|
758
|
-
capabilities: parsed.capabilities,
|
|
759
|
-
priceSubunits: parsed.priceSubunits,
|
|
760
|
-
asset: parsed.asset,
|
|
761
|
-
skillDir: entryPath,
|
|
762
|
-
systemPrompt: parsed.systemPrompt,
|
|
763
|
-
tools: parsed.tools,
|
|
764
|
-
maxToolRounds: parsed.maxToolRounds,
|
|
765
|
-
image: parsed.image,
|
|
766
|
-
imageFile: parsed.imageFile,
|
|
767
|
-
logger
|
|
768
|
-
})
|
|
769
|
-
);
|
|
1140
|
+
skills.push(buildSkillFromParsed(parsed, entryPath, logger));
|
|
770
1141
|
} catch (error) {
|
|
771
1142
|
const message = error instanceof Error ? error.message : String(error);
|
|
772
1143
|
logger.warn?.({ dir: entry, err: message }, "skipping malformed skill directory");
|
|
@@ -776,12 +1147,20 @@ function loadSkillsFromDir(skillsDir, options = {}) {
|
|
|
776
1147
|
}
|
|
777
1148
|
|
|
778
1149
|
exports.DEFAULT_MAX_TOOL_ROUNDS = DEFAULT_MAX_TOOL_ROUNDS;
|
|
1150
|
+
exports.DEFAULT_SCRIPT_TIMEOUT_MS = DEFAULT_SCRIPT_TIMEOUT_MS;
|
|
1151
|
+
exports.DynamicScriptSkill = DynamicScriptSkill;
|
|
1152
|
+
exports.MAX_SCRIPT_OUTPUT = MAX_SCRIPT_OUTPUT;
|
|
1153
|
+
exports.MAX_STATIC_FILE_SIZE = MAX_STATIC_FILE_SIZE;
|
|
779
1154
|
exports.ScriptSkill = ScriptSkill;
|
|
1155
|
+
exports.StaticFileSkill = StaticFileSkill;
|
|
1156
|
+
exports.StaticScriptSkill = StaticScriptSkill;
|
|
780
1157
|
exports.createAnthropicClient = createAnthropicClient;
|
|
781
1158
|
exports.createLlmClient = createLlmClient;
|
|
782
1159
|
exports.createOpenAIClient = createOpenAIClient;
|
|
783
1160
|
exports.loadSkillsFromDir = loadSkillsFromDir;
|
|
784
1161
|
exports.parseSkillMd = parseSkillMd;
|
|
1162
|
+
exports.resolveInsidePath = resolveInsidePath;
|
|
1163
|
+
exports.runScript = runScript;
|
|
785
1164
|
exports.validateSkillFrontmatter = validateSkillFrontmatter;
|
|
786
1165
|
//# sourceMappingURL=skills.cjs.map
|
|
787
1166
|
//# sourceMappingURL=skills.cjs.map
|