@elisym/sdk 0.13.0 → 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/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/llmClient.ts
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`);
@@ -1139,6 +837,6 @@ function loadSkillsFromDir(skillsDir, options = {}) {
1139
837
  return skills;
1140
838
  }
1141
839
 
1142
- export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, createAnthropicClient, createLlmClient, createOpenAIClient, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
840
+ 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
841
  //# sourceMappingURL=skills.js.map
1144
842
  //# sourceMappingURL=skills.js.map