@elisym/sdk 0.13.0 → 0.15.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 +28 -17
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.d.cts +19 -15
- package/dist/agent-store.d.ts +19 -15
- package/dist/agent-store.js +28 -17
- package/dist/agent-store.js.map +1 -1
- package/dist/index.cjs +150 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -45
- package/dist/index.d.ts +27 -45
- package/dist/index.js +150 -7
- package/dist/index.js.map +1 -1
- package/dist/llm-health.cjs +426 -0
- package/dist/llm-health.cjs.map +1 -0
- package/dist/llm-health.d.cts +179 -0
- package/dist/llm-health.d.ts +179 -0
- package/dist/llm-health.js +410 -0
- package/dist/llm-health.js.map +1 -0
- package/dist/node.cjs.map +1 -1
- package/dist/node.js.map +1 -1
- package/dist/rateLimiter-CoEmZkSX.d.cts +45 -0
- package/dist/rateLimiter-CoEmZkSX.d.ts +45 -0
- package/dist/skills.cjs +33 -309
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.d.cts +20 -14
- package/dist/skills.d.ts +20 -14
- package/dist/skills.js +34 -307
- package/dist/skills.js.map +1 -1
- package/dist/types-8vJ1I2KQ.d.cts +66 -0
- package/dist/types-8vJ1I2KQ.d.ts +66 -0
- package/package.json +11 -1
package/dist/skills.js
CHANGED
|
@@ -6,303 +6,7 @@ import { readdirSync, statSync, readFileSync } from 'node:fs';
|
|
|
6
6
|
import YAML from 'yaml';
|
|
7
7
|
import Decimal from 'decimal.js-light';
|
|
8
8
|
|
|
9
|
-
// src/skills/
|
|
10
|
-
var LLM_TIMEOUT_MS = 12e4;
|
|
11
|
-
var MAX_RETRIES = 2;
|
|
12
|
-
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
13
|
-
var DEFAULT_MAX_TOKENS = 4096;
|
|
14
|
-
var DEFAULT_ANTHROPIC_MODEL = "claude-haiku-4-5-20251001";
|
|
15
|
-
var DEFAULT_OPENAI_MODEL = "gpt-4o-mini";
|
|
16
|
-
function createAbortError() {
|
|
17
|
-
const err = new Error("The operation was aborted");
|
|
18
|
-
err.name = "AbortError";
|
|
19
|
-
return err;
|
|
20
|
-
}
|
|
21
|
-
function sleepWithSignal(ms, signal) {
|
|
22
|
-
if (signal?.aborted) {
|
|
23
|
-
return Promise.reject(createAbortError());
|
|
24
|
-
}
|
|
25
|
-
if (!signal) {
|
|
26
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
27
|
-
}
|
|
28
|
-
return new Promise((resolve2, reject) => {
|
|
29
|
-
const cleanup = () => {
|
|
30
|
-
clearTimeout(timer);
|
|
31
|
-
signal.removeEventListener("abort", onAbort);
|
|
32
|
-
};
|
|
33
|
-
const onAbort = () => {
|
|
34
|
-
cleanup();
|
|
35
|
-
reject(createAbortError());
|
|
36
|
-
};
|
|
37
|
-
const timer = setTimeout(() => {
|
|
38
|
-
cleanup();
|
|
39
|
-
resolve2();
|
|
40
|
-
}, ms);
|
|
41
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
async function fetchWithTimeout(url, init, signal) {
|
|
45
|
-
if (signal?.aborted) {
|
|
46
|
-
throw createAbortError();
|
|
47
|
-
}
|
|
48
|
-
const controller = new AbortController();
|
|
49
|
-
const timer = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
|
|
50
|
-
const onAbort = () => controller.abort();
|
|
51
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
52
|
-
try {
|
|
53
|
-
return await fetch(url, { ...init, signal: controller.signal });
|
|
54
|
-
} finally {
|
|
55
|
-
clearTimeout(timer);
|
|
56
|
-
signal?.removeEventListener("abort", onAbort);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async function fetchWithRetry(url, init, signal) {
|
|
60
|
-
for (let attempt = 0; ; attempt++) {
|
|
61
|
-
let response;
|
|
62
|
-
try {
|
|
63
|
-
response = await fetchWithTimeout(url, init, signal);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
const name = error instanceof Error ? error.name : "";
|
|
66
|
-
if (attempt >= MAX_RETRIES || name === "AbortError") {
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
await sleepWithSignal(Math.min(1e3 * 2 ** attempt, 8e3), signal);
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (response.ok || attempt >= MAX_RETRIES || !RETRYABLE_STATUSES.has(response.status)) {
|
|
73
|
-
return response;
|
|
74
|
-
}
|
|
75
|
-
const retryAfter = response.headers.get("retry-after");
|
|
76
|
-
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3 || 1e3 * 2 ** attempt, 3e4) : Math.min(1e3 * 2 ** attempt, 8e3);
|
|
77
|
-
await response.body?.cancel().catch(() => void 0);
|
|
78
|
-
await sleepWithSignal(delay, signal);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
var AnthropicClient = class {
|
|
82
|
-
constructor(config) {
|
|
83
|
-
this.config = config;
|
|
84
|
-
}
|
|
85
|
-
async complete(systemPrompt, userInput, signal) {
|
|
86
|
-
const response = await fetchWithRetry(
|
|
87
|
-
"https://api.anthropic.com/v1/messages",
|
|
88
|
-
{
|
|
89
|
-
method: "POST",
|
|
90
|
-
headers: {
|
|
91
|
-
"Content-Type": "application/json",
|
|
92
|
-
"x-api-key": this.config.apiKey,
|
|
93
|
-
"anthropic-version": "2023-06-01"
|
|
94
|
-
},
|
|
95
|
-
body: JSON.stringify({
|
|
96
|
-
model: this.config.model,
|
|
97
|
-
max_tokens: this.config.maxTokens,
|
|
98
|
-
system: systemPrompt,
|
|
99
|
-
messages: [{ role: "user", content: userInput }]
|
|
100
|
-
})
|
|
101
|
-
},
|
|
102
|
-
signal
|
|
103
|
-
);
|
|
104
|
-
if (!response.ok) {
|
|
105
|
-
throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
|
|
106
|
-
}
|
|
107
|
-
const data = await response.json();
|
|
108
|
-
const textBlock = data.content?.find((block) => block.type === "text");
|
|
109
|
-
return textBlock?.text ?? "";
|
|
110
|
-
}
|
|
111
|
-
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
112
|
-
const anthropicTools = tools.map((tool) => ({
|
|
113
|
-
name: tool.name,
|
|
114
|
-
description: tool.description,
|
|
115
|
-
input_schema: {
|
|
116
|
-
type: "object",
|
|
117
|
-
properties: Object.fromEntries(
|
|
118
|
-
tool.parameters.map((param) => [
|
|
119
|
-
param.name,
|
|
120
|
-
{ type: "string", description: param.description }
|
|
121
|
-
])
|
|
122
|
-
),
|
|
123
|
-
required: tool.parameters.filter((param) => param.required).map((param) => param.name)
|
|
124
|
-
}
|
|
125
|
-
}));
|
|
126
|
-
const response = await fetchWithRetry(
|
|
127
|
-
"https://api.anthropic.com/v1/messages",
|
|
128
|
-
{
|
|
129
|
-
method: "POST",
|
|
130
|
-
headers: {
|
|
131
|
-
"Content-Type": "application/json",
|
|
132
|
-
"x-api-key": this.config.apiKey,
|
|
133
|
-
"anthropic-version": "2023-06-01"
|
|
134
|
-
},
|
|
135
|
-
body: JSON.stringify({
|
|
136
|
-
model: this.config.model,
|
|
137
|
-
max_tokens: this.config.maxTokens,
|
|
138
|
-
system: systemPrompt,
|
|
139
|
-
messages,
|
|
140
|
-
tools: anthropicTools
|
|
141
|
-
})
|
|
142
|
-
},
|
|
143
|
-
signal
|
|
144
|
-
);
|
|
145
|
-
if (!response.ok) {
|
|
146
|
-
throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
|
|
147
|
-
}
|
|
148
|
-
const data = await response.json();
|
|
149
|
-
const content = data.content ?? [];
|
|
150
|
-
const toolUses = content.filter((block) => block.type === "tool_use");
|
|
151
|
-
if (toolUses.length > 0) {
|
|
152
|
-
const calls = toolUses.map((block) => ({
|
|
153
|
-
id: block.id ?? "",
|
|
154
|
-
name: block.name ?? "",
|
|
155
|
-
arguments: block.input ?? {}
|
|
156
|
-
}));
|
|
157
|
-
return {
|
|
158
|
-
type: "tool_use",
|
|
159
|
-
calls,
|
|
160
|
-
assistantMessage: { role: "assistant", content }
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
const textBlock = content.find((block) => block.type === "text");
|
|
164
|
-
return { type: "text", text: textBlock?.text ?? "" };
|
|
165
|
-
}
|
|
166
|
-
formatToolResultMessages(results) {
|
|
167
|
-
return [
|
|
168
|
-
{
|
|
169
|
-
role: "user",
|
|
170
|
-
content: results.map((result) => ({
|
|
171
|
-
type: "tool_result",
|
|
172
|
-
tool_use_id: result.callId,
|
|
173
|
-
content: result.content
|
|
174
|
-
}))
|
|
175
|
-
}
|
|
176
|
-
];
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
var OpenAIClient = class {
|
|
180
|
-
constructor(config) {
|
|
181
|
-
this.config = config;
|
|
182
|
-
}
|
|
183
|
-
isReasoningModel() {
|
|
184
|
-
return /^o\d/.test(this.config.model);
|
|
185
|
-
}
|
|
186
|
-
async complete(systemPrompt, userInput, signal) {
|
|
187
|
-
const reasoning = this.isReasoningModel();
|
|
188
|
-
const response = await fetchWithRetry(
|
|
189
|
-
"https://api.openai.com/v1/chat/completions",
|
|
190
|
-
{
|
|
191
|
-
method: "POST",
|
|
192
|
-
headers: {
|
|
193
|
-
"Content-Type": "application/json",
|
|
194
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
195
|
-
},
|
|
196
|
-
body: JSON.stringify({
|
|
197
|
-
model: this.config.model,
|
|
198
|
-
...reasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
199
|
-
messages: [
|
|
200
|
-
{ role: reasoning ? "developer" : "system", content: systemPrompt },
|
|
201
|
-
{ role: "user", content: userInput }
|
|
202
|
-
]
|
|
203
|
-
})
|
|
204
|
-
},
|
|
205
|
-
signal
|
|
206
|
-
);
|
|
207
|
-
if (!response.ok) {
|
|
208
|
-
throw new Error(`OpenAI API error: ${response.status} ${await response.text()}`);
|
|
209
|
-
}
|
|
210
|
-
const data = await response.json();
|
|
211
|
-
return data.choices?.[0]?.message?.content ?? "";
|
|
212
|
-
}
|
|
213
|
-
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
214
|
-
const openaiTools = tools.map((tool) => ({
|
|
215
|
-
type: "function",
|
|
216
|
-
function: {
|
|
217
|
-
name: tool.name,
|
|
218
|
-
description: tool.description,
|
|
219
|
-
parameters: {
|
|
220
|
-
type: "object",
|
|
221
|
-
properties: Object.fromEntries(
|
|
222
|
-
tool.parameters.map((param) => [
|
|
223
|
-
param.name,
|
|
224
|
-
{ type: "string", description: param.description }
|
|
225
|
-
])
|
|
226
|
-
),
|
|
227
|
-
required: tool.parameters.filter((param) => param.required).map((param) => param.name)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}));
|
|
231
|
-
const reasoning = this.isReasoningModel();
|
|
232
|
-
const response = await fetchWithRetry(
|
|
233
|
-
"https://api.openai.com/v1/chat/completions",
|
|
234
|
-
{
|
|
235
|
-
method: "POST",
|
|
236
|
-
headers: {
|
|
237
|
-
"Content-Type": "application/json",
|
|
238
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
239
|
-
},
|
|
240
|
-
body: JSON.stringify({
|
|
241
|
-
model: this.config.model,
|
|
242
|
-
...reasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
243
|
-
messages: [
|
|
244
|
-
{ role: reasoning ? "developer" : "system", content: systemPrompt },
|
|
245
|
-
...messages
|
|
246
|
-
],
|
|
247
|
-
tools: openaiTools
|
|
248
|
-
})
|
|
249
|
-
},
|
|
250
|
-
signal
|
|
251
|
-
);
|
|
252
|
-
if (!response.ok) {
|
|
253
|
-
throw new Error(`OpenAI API error: ${response.status} ${await response.text()}`);
|
|
254
|
-
}
|
|
255
|
-
const data = await response.json();
|
|
256
|
-
const message = data.choices?.[0]?.message;
|
|
257
|
-
const toolCalls = message?.tool_calls ?? [];
|
|
258
|
-
if (toolCalls.length > 0) {
|
|
259
|
-
const calls = toolCalls.map((call) => {
|
|
260
|
-
let args;
|
|
261
|
-
try {
|
|
262
|
-
args = JSON.parse(call.function?.arguments ?? "{}");
|
|
263
|
-
} catch {
|
|
264
|
-
args = {};
|
|
265
|
-
}
|
|
266
|
-
return { id: call.id ?? "", name: call.function?.name ?? "", arguments: args };
|
|
267
|
-
});
|
|
268
|
-
return { type: "tool_use", calls, assistantMessage: message };
|
|
269
|
-
}
|
|
270
|
-
return { type: "text", text: message?.content ?? "" };
|
|
271
|
-
}
|
|
272
|
-
formatToolResultMessages(results) {
|
|
273
|
-
return results.map((result) => ({
|
|
274
|
-
role: "tool",
|
|
275
|
-
tool_call_id: result.callId,
|
|
276
|
-
content: result.content
|
|
277
|
-
}));
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
function createAnthropicClient(config) {
|
|
281
|
-
if (!config.apiKey) {
|
|
282
|
-
throw new Error("ANTHROPIC_API_KEY is required for skill runtime");
|
|
283
|
-
}
|
|
284
|
-
return new AnthropicClient({
|
|
285
|
-
apiKey: config.apiKey,
|
|
286
|
-
model: config.model ?? DEFAULT_ANTHROPIC_MODEL,
|
|
287
|
-
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function createOpenAIClient(config) {
|
|
291
|
-
if (!config.apiKey) {
|
|
292
|
-
throw new Error("OPENAI_API_KEY is required for skill runtime");
|
|
293
|
-
}
|
|
294
|
-
return new OpenAIClient({
|
|
295
|
-
apiKey: config.apiKey,
|
|
296
|
-
model: config.model ?? DEFAULT_OPENAI_MODEL,
|
|
297
|
-
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
function createLlmClient(config) {
|
|
301
|
-
if (config.provider === "openai") {
|
|
302
|
-
return createOpenAIClient(config);
|
|
303
|
-
}
|
|
304
|
-
return createAnthropicClient(config);
|
|
305
|
-
}
|
|
9
|
+
// src/skills/scriptSkill.ts
|
|
306
10
|
var MAX_SCRIPT_OUTPUT = 1e6;
|
|
307
11
|
var DEFAULT_SCRIPT_TIMEOUT_MS = 6e4;
|
|
308
12
|
function runScript(cmd, args, opts) {
|
|
@@ -681,7 +385,6 @@ function parseAssetAmount(asset, human) {
|
|
|
681
385
|
Decimal.clone({ toExpNeg: -100, toExpPos: 100, precision: 50 });
|
|
682
386
|
|
|
683
387
|
// src/skills/loader.ts
|
|
684
|
-
var VALID_PROVIDERS = ["anthropic", "openai"];
|
|
685
388
|
var MAX_TOKENS_LIMIT = 2e5;
|
|
686
389
|
var DEFAULT_MAX_TOOL_ROUNDS = 10;
|
|
687
390
|
var VALID_MODES = [
|
|
@@ -853,13 +556,8 @@ function validateLlmOverride(skillName, frontmatter, mode) {
|
|
|
853
556
|
}
|
|
854
557
|
const override = {};
|
|
855
558
|
if (hasProvider && hasModel) {
|
|
856
|
-
if (typeof frontmatter.provider !== "string") {
|
|
857
|
-
throw new Error(`SKILL.md "${skillName}": "provider" must be a string`);
|
|
858
|
-
}
|
|
859
|
-
if (!VALID_PROVIDERS.includes(frontmatter.provider)) {
|
|
860
|
-
throw new Error(
|
|
861
|
-
`SKILL.md "${skillName}": invalid provider "${frontmatter.provider}". Allowed: ${VALID_PROVIDERS.join(", ")}`
|
|
862
|
-
);
|
|
559
|
+
if (typeof frontmatter.provider !== "string" || frontmatter.provider.length === 0) {
|
|
560
|
+
throw new Error(`SKILL.md "${skillName}": "provider" must be a non-empty string`);
|
|
863
561
|
}
|
|
864
562
|
if (typeof frontmatter.model !== "string" || frontmatter.model.length === 0) {
|
|
865
563
|
throw new Error(`SKILL.md "${skillName}": "model" must be a non-empty string`);
|
|
@@ -877,6 +575,33 @@ function validateLlmOverride(skillName, frontmatter, mode) {
|
|
|
877
575
|
}
|
|
878
576
|
return override;
|
|
879
577
|
}
|
|
578
|
+
var MAX_RATE_LIMIT_WINDOW_SECS = 86400;
|
|
579
|
+
var MAX_RATE_LIMIT_PER_WINDOW = 1e4;
|
|
580
|
+
function validateRateLimit(skillName, raw) {
|
|
581
|
+
if (raw === void 0 || raw === null) {
|
|
582
|
+
return void 0;
|
|
583
|
+
}
|
|
584
|
+
if (typeof raw !== "object") {
|
|
585
|
+
throw new Error(`SKILL.md "${skillName}": "rate_limit" must be an object`);
|
|
586
|
+
}
|
|
587
|
+
const record = raw;
|
|
588
|
+
const perWindowSecs = record.per_window_secs;
|
|
589
|
+
const maxPerWindow = record.max_per_window;
|
|
590
|
+
if (typeof perWindowSecs !== "number" || !Number.isInteger(perWindowSecs) || perWindowSecs < 1 || perWindowSecs > MAX_RATE_LIMIT_WINDOW_SECS) {
|
|
591
|
+
throw new Error(
|
|
592
|
+
`SKILL.md "${skillName}": "rate_limit.per_window_secs" must be an integer between 1 and ${MAX_RATE_LIMIT_WINDOW_SECS}`
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
if (typeof maxPerWindow !== "number" || !Number.isInteger(maxPerWindow) || maxPerWindow < 1 || maxPerWindow > MAX_RATE_LIMIT_PER_WINDOW) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`SKILL.md "${skillName}": "rate_limit.max_per_window" must be an integer between 1 and ${MAX_RATE_LIMIT_PER_WINDOW}`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
perWindowMs: perWindowSecs * 1e3,
|
|
602
|
+
maxPerWindow
|
|
603
|
+
};
|
|
604
|
+
}
|
|
880
605
|
function validateScriptTimeoutMs(skillName, raw) {
|
|
881
606
|
if (raw === void 0 || raw === null) {
|
|
882
607
|
return void 0;
|
|
@@ -1019,6 +744,7 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
|
1019
744
|
const image = typeof frontmatter.image === "string" ? frontmatter.image : void 0;
|
|
1020
745
|
const imageFile = typeof frontmatter.image_file === "string" ? frontmatter.image_file : void 0;
|
|
1021
746
|
const llmOverride = validateLlmOverride(frontmatter.name, frontmatter, mode);
|
|
747
|
+
const rateLimit = validateRateLimit(frontmatter.name, frontmatter.rate_limit);
|
|
1022
748
|
return {
|
|
1023
749
|
name: frontmatter.name,
|
|
1024
750
|
description: frontmatter.description,
|
|
@@ -1035,7 +761,8 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
|
|
|
1035
761
|
outputFile,
|
|
1036
762
|
script,
|
|
1037
763
|
scriptArgs,
|
|
1038
|
-
scriptTimeoutMs
|
|
764
|
+
scriptTimeoutMs,
|
|
765
|
+
rateLimit
|
|
1039
766
|
};
|
|
1040
767
|
}
|
|
1041
768
|
function buildSkillFromParsed(parsed, skillDir, logger) {
|
|
@@ -1139,6 +866,6 @@ function loadSkillsFromDir(skillsDir, options = {}) {
|
|
|
1139
866
|
return skills;
|
|
1140
867
|
}
|
|
1141
868
|
|
|
1142
|
-
export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill,
|
|
869
|
+
export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
|
|
1143
870
|
//# sourceMappingURL=skills.js.map
|
|
1144
871
|
//# sourceMappingURL=skills.js.map
|