@swifty.js/swifty 0.0.21 → 0.0.22

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 (34) hide show
  1. package/dist/{agent-ZCMBUWLZ.js → agent-5T7KNC7V.js} +1 -1
  2. package/dist/{anthropic-4ZVG7AMA.js → anthropic-P2R4GW6F.js} +1 -1
  3. package/dist/{chunk-Y5WGBAGP.js → chunk-HJIGA37J.js} +1 -1
  4. package/dist/{chunk-DDBH5AEN.js → chunk-L73IHJF4.js} +53 -53
  5. package/dist/{chunk-S6ZADC7C.js → chunk-NEAR6YPZ.js} +1 -1
  6. package/dist/{chunk-MUPVOATV.js → chunk-NLNH3IRT.js} +1 -1
  7. package/dist/{chunk-6E6UA7MT.js → chunk-YQQVB6VB.js} +9 -8
  8. package/dist/lib/agent-2AGRYN3R.js +9 -0
  9. package/dist/lib/anthropic-GFWP3ORB.js +22 -0
  10. package/dist/lib/bwrap-QRGPUP4J.js +6 -0
  11. package/dist/lib/checker-IIXJXQCB.js +19 -0
  12. package/dist/lib/chunk-6GOWRPYS.js +339 -0
  13. package/dist/lib/chunk-7URDLWQN.js +426 -0
  14. package/dist/lib/chunk-C7IHCJZ3.js +849 -0
  15. package/dist/lib/chunk-EJLGB2EJ.js +377 -0
  16. package/dist/lib/chunk-GHF2PSEW.js +8 -0
  17. package/dist/lib/chunk-GNBXECZN.js +243 -0
  18. package/dist/lib/chunk-HK2Z6WP4.js +223 -0
  19. package/dist/lib/chunk-LUEP4JMF.js +549 -0
  20. package/dist/lib/chunk-ORSYNBMM.js +38 -0
  21. package/dist/lib/chunk-RR2CZ6CY.js +598 -0
  22. package/dist/lib/chunk-UHVO63Y7.js +1246 -0
  23. package/dist/lib/chunk-UMFNTXKA.js +88 -0
  24. package/dist/lib/chunk-VUD72RTY.js +39 -0
  25. package/dist/lib/glob.wasm +0 -0
  26. package/dist/lib/index.d.ts +5350 -0
  27. package/dist/lib/index.js +12002 -0
  28. package/dist/lib/openai-HXRMPNB7.js +15 -0
  29. package/dist/lib/seatbelt-FT5IY73W.js +6 -0
  30. package/dist/lib/tool-filter-VF7TZRE5.js +19 -0
  31. package/dist/main.js +225 -225
  32. package/dist/{openai-HXD52MZB.js → openai-TVUUHRG7.js} +1 -1
  33. package/dist/{server-VMHWEO2Y.js → server-ZTLHJWEQ.js} +14 -14
  34. package/package.json +14 -2
@@ -0,0 +1,598 @@
1
+ import {
2
+ getMaxOutputTokens,
3
+ resolveAPIKey
4
+ } from "./chunk-6GOWRPYS.js";
5
+ import {
6
+ AuthenticationError,
7
+ ContextTooLongError,
8
+ LLMError,
9
+ NetworkError,
10
+ RateLimitError,
11
+ ensureToolPairing
12
+ } from "./chunk-UMFNTXKA.js";
13
+ import {
14
+ DANGEROUSLY_JSON,
15
+ asRecord,
16
+ asString,
17
+ contentToText,
18
+ createChildLogger,
19
+ isRecord,
20
+ strArg
21
+ } from "./chunk-EJLGB2EJ.js";
22
+
23
+ // src/llm/openai.ts
24
+ import OpenAI from "openai";
25
+ var log = createChildLogger({ module: "llm" });
26
+ var OpenAIClient = class {
27
+ client;
28
+ model;
29
+ systemPrompt;
30
+ maxOutputTokens;
31
+ constructor(config, systemPrompt) {
32
+ const apiKey = resolveAPIKey(config);
33
+ if (!apiKey) {
34
+ throw new AuthenticationError(
35
+ "OpenAI API key not found, set OPENAI_API_KEY in .swifty/config.y(a)ml, or via OPENAI_API_KEY env variable."
36
+ );
37
+ }
38
+ this.client = new OpenAI({
39
+ apiKey,
40
+ baseURL: config.base_url
41
+ });
42
+ this.model = config.model;
43
+ this.systemPrompt = systemPrompt;
44
+ this.maxOutputTokens = getMaxOutputTokens(config);
45
+ }
46
+ async *stream(conversation, toolSchemas, abortSignal) {
47
+ const messages = buildOpenAIInput(ensureToolPairing(conversation.getMessages()));
48
+ const input = [];
49
+ input.push({
50
+ role: "system",
51
+ content: this.systemPrompt
52
+ });
53
+ for (const message of messages) {
54
+ input.push(message);
55
+ }
56
+ const tools = toolSchemas.map((s) => {
57
+ const schema = s.input_schema;
58
+ return {
59
+ type: "function",
60
+ name: s.name,
61
+ description: s.description,
62
+ parameters: schema,
63
+ strict: false
64
+ };
65
+ });
66
+ const params = {
67
+ model: this.model,
68
+ input,
69
+ stream: true,
70
+ max_output_tokens: this.maxOutputTokens,
71
+ ...tools.length > 0 ? { tools } : {}
72
+ };
73
+ let inputTokens = 0;
74
+ let outputTokens = 0;
75
+ let cacheReadInputTokens = 0;
76
+ const cacheCreationInputTokens = 0;
77
+ try {
78
+ const stream = await this.client.responses.create(params, {
79
+ ...abortSignal ? { signal: abortSignal } : {}
80
+ });
81
+ let currentToolName = "";
82
+ let currentToolId = "";
83
+ let jsonAccumulate = "";
84
+ let reasoningId = "";
85
+ let reasoningText = "";
86
+ for await (const event of stream) {
87
+ if (event.type === "response.output_text.delta") {
88
+ yield {
89
+ type: "text_delta",
90
+ text: event.delta
91
+ };
92
+ } else if (event.type === "response.reasoning_summary_text.delta") {
93
+ reasoningText += event.delta;
94
+ yield { type: "thinking_delta", text: event.delta };
95
+ } else if (event.type === "response.reasoning_summary_text.done") {
96
+ yield {
97
+ type: "thinking_complete",
98
+ thinking: reasoningText,
99
+ signature: reasoningId
100
+ };
101
+ } else if (event.type === "response.function_call_arguments.delta") {
102
+ jsonAccumulate += event.delta;
103
+ yield {
104
+ type: "tool_call_delta",
105
+ text: event.delta
106
+ };
107
+ } else if (event.type === "response.output_item.added") {
108
+ if (event.item.type === "function_call") {
109
+ currentToolName = event.item.name;
110
+ currentToolId = event.item.call_id;
111
+ jsonAccumulate = "";
112
+ yield {
113
+ type: "tool_call_start",
114
+ toolName: currentToolName,
115
+ toolId: currentToolId
116
+ };
117
+ } else if (event.item.type === "reasoning") {
118
+ reasoningId = event.item.id ?? "";
119
+ reasoningText = "";
120
+ }
121
+ } else if (event.type === "response.output_item.done") {
122
+ if (event.item.type === "function_call" && currentToolName) {
123
+ let args = {};
124
+ if (jsonAccumulate) {
125
+ try {
126
+ const parsed = JSON.parse(jsonAccumulate);
127
+ args = isRecord(parsed) ? asRecord(parsed) : { [DANGEROUSLY_JSON]: jsonAccumulate };
128
+ } catch (err) {
129
+ log.error({ err }, "llm operation failed");
130
+ args = {
131
+ [DANGEROUSLY_JSON]: jsonAccumulate
132
+ };
133
+ }
134
+ }
135
+ yield {
136
+ type: "tool_call_complete",
137
+ toolId: currentToolId,
138
+ toolName: currentToolName,
139
+ arguments: args
140
+ };
141
+ currentToolName = "";
142
+ currentToolId = "";
143
+ jsonAccumulate = "";
144
+ }
145
+ } else if (event.type === "response.completed") {
146
+ const usage = event.response.usage;
147
+ if (usage) {
148
+ outputTokens = usage.output_tokens;
149
+ cacheReadInputTokens = usage.input_tokens_details.cached_tokens;
150
+ inputTokens = Math.max(0, usage.input_tokens - cacheReadInputTokens);
151
+ }
152
+ let stopReason = "end_turn";
153
+ const resp = event.response;
154
+ if (resp.status === "incomplete") {
155
+ const details = resp.incomplete_details;
156
+ if (details?.reason === "max_output_tokens") {
157
+ stopReason = "max_tokens";
158
+ }
159
+ }
160
+ yield {
161
+ type: "stream_end",
162
+ stopReason,
163
+ usage: {
164
+ inputTokens,
165
+ outputTokens,
166
+ cacheReadInputTokens,
167
+ cacheCreationInputTokens
168
+ // 0
169
+ }
170
+ };
171
+ }
172
+ }
173
+ } catch (err) {
174
+ log.error({ err }, "llm operation failed");
175
+ throw classifyOpenAIError(err);
176
+ }
177
+ }
178
+ setSystemPrompt(prompt) {
179
+ this.systemPrompt = prompt;
180
+ }
181
+ setMaxOutputTokens(maxTokens) {
182
+ this.maxOutputTokens = maxTokens;
183
+ }
184
+ };
185
+ function imageDataUrl(block) {
186
+ if (block.type !== "image") {
187
+ return null;
188
+ }
189
+ const source = block.source;
190
+ if (!isRecord(source) || source.type !== "base64") {
191
+ return null;
192
+ }
193
+ const mediaType = strArg(source, "media_type");
194
+ const data = strArg(source, "data");
195
+ if (!mediaType || !data) {
196
+ return null;
197
+ }
198
+ return `data:${mediaType};base64,${data}`;
199
+ }
200
+ function collectImageContents(tr) {
201
+ const { toolUseId, content } = tr;
202
+ if (typeof content === "string") {
203
+ return [];
204
+ }
205
+ const images = [];
206
+ for (const block of content) {
207
+ const url = imageDataUrl(block);
208
+ if (url) {
209
+ images.push({
210
+ type: "input_image",
211
+ image_url: url,
212
+ detail: "auto"
213
+ });
214
+ }
215
+ }
216
+ if (images.length === 0) {
217
+ return [];
218
+ }
219
+ return [
220
+ {
221
+ type: "input_text",
222
+ text: `[Image(s) returned by tool call ${toolUseId}]`
223
+ },
224
+ ...images
225
+ ];
226
+ }
227
+ function collectImageParts(tr) {
228
+ const { toolUseId, content } = tr;
229
+ if (typeof content === "string") {
230
+ return [];
231
+ }
232
+ const images = [];
233
+ for (const block of content) {
234
+ const url = imageDataUrl(block);
235
+ if (url) {
236
+ images.push({
237
+ type: "image_url",
238
+ image_url: { url }
239
+ });
240
+ }
241
+ }
242
+ if (images.length === 0) {
243
+ return [];
244
+ }
245
+ return [
246
+ {
247
+ type: "text",
248
+ text: `[Image(s) returned by tool call ${toolUseId}]`
249
+ },
250
+ ...images
251
+ ];
252
+ }
253
+ function userContentsFor(content) {
254
+ if (typeof content === "string") {
255
+ return content;
256
+ }
257
+ const parts = [];
258
+ for (const block of content) {
259
+ if (block.type === "text") {
260
+ parts.push({ type: "input_text", text: strArg(block, "text") });
261
+ } else {
262
+ const url = imageDataUrl(block);
263
+ if (url) {
264
+ parts.push({ type: "input_image", image_url: url, detail: "auto" });
265
+ }
266
+ }
267
+ }
268
+ return parts;
269
+ }
270
+ function userPartsFor(content) {
271
+ if (typeof content === "string") {
272
+ return content;
273
+ }
274
+ const parts = [];
275
+ for (const block of content) {
276
+ if (block.type === "text") {
277
+ parts.push({ type: "text", text: strArg(block, "text") });
278
+ } else {
279
+ const url = imageDataUrl(block);
280
+ if (url) {
281
+ parts.push({ type: "image_url", image_url: { url } });
282
+ }
283
+ }
284
+ }
285
+ return parts;
286
+ }
287
+ function buildOpenAIInput(messages) {
288
+ const result = [];
289
+ for (const m of messages) {
290
+ if (m.thinkingBlocks) {
291
+ for (const tb of m.thinkingBlocks) {
292
+ result.push({
293
+ type: "reasoning",
294
+ id: tb.signature,
295
+ summary: [{ type: "summary_text", text: tb.thinking }]
296
+ });
297
+ }
298
+ }
299
+ if (m.toolUses && m.toolUses.length > 0) {
300
+ const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
301
+ if (assistantText) {
302
+ result.push({
303
+ role: "assistant",
304
+ content: assistantText
305
+ });
306
+ }
307
+ for (const tu of m.toolUses) {
308
+ result.push({
309
+ type: "function_call",
310
+ name: tu.toolName,
311
+ call_id: tu.toolUseId,
312
+ arguments: JSON.stringify(tu.arguments)
313
+ });
314
+ }
315
+ } else if (m.toolResults && m.toolResults.length > 0) {
316
+ const pendingImageContents = [];
317
+ for (const tr of m.toolResults) {
318
+ result.push({
319
+ type: "function_call_output",
320
+ call_id: tr.toolUseId,
321
+ output: contentToText(tr.content)
322
+ });
323
+ pendingImageContents.push(...collectImageContents(tr));
324
+ }
325
+ if (pendingImageContents.length > 0) {
326
+ result.push({
327
+ role: "user",
328
+ content: pendingImageContents
329
+ });
330
+ }
331
+ } else if (m.role === "assistant") {
332
+ result.push({
333
+ role: "assistant",
334
+ content: typeof m.content === "string" ? m.content : contentToText(m.content)
335
+ });
336
+ } else {
337
+ result.push({
338
+ role: m.role,
339
+ content: userContentsFor(m.content)
340
+ });
341
+ }
342
+ }
343
+ return result;
344
+ }
345
+ function containsContextLengthError(msg) {
346
+ return /context_length_exceeded/i.test(msg) || /maximum\scontext\slength/i.test(msg) || /prompts?\s+too\s+long/i.test(msg);
347
+ }
348
+ var OpenAICompatClient = class {
349
+ client;
350
+ model;
351
+ systemPrompt;
352
+ maxOutputTokens;
353
+ constructor(config, systemPrompt) {
354
+ const apiKey = resolveAPIKey(config);
355
+ if (!apiKey) {
356
+ throw new AuthenticationError(
357
+ "OpenAI API key not found. Set OPENAI_API_KEY in .swifty/config.y(a)ml, or via OPENAI_API_KEY env variable."
358
+ );
359
+ }
360
+ this.client = new OpenAI({ apiKey, baseURL: config.base_url });
361
+ this.model = config.model;
362
+ this.systemPrompt = systemPrompt;
363
+ this.maxOutputTokens = getMaxOutputTokens(config);
364
+ }
365
+ setSystemPrompt(prompt) {
366
+ this.systemPrompt = prompt;
367
+ }
368
+ setMaxOutputTokens(maxTokens) {
369
+ this.maxOutputTokens = maxTokens;
370
+ }
371
+ async *stream(conversation, toolSchemas, abortSignal) {
372
+ const messages = [
373
+ {
374
+ role: "system",
375
+ content: this.systemPrompt
376
+ },
377
+ ...buildChatCompletionMessages(ensureToolPairing(conversation.getMessages()))
378
+ ];
379
+ const tools = toolSchemas.map((ts) => ({
380
+ // name: ts.name,
381
+ // description: ts.description,
382
+ type: "function",
383
+ function: {
384
+ name: ts.name,
385
+ description: ts.description,
386
+ parameters: ts.input_schema,
387
+ strict: false
388
+ }
389
+ }));
390
+ const params = {
391
+ model: this.model,
392
+ messages,
393
+ stream: true,
394
+ stream_options: { include_usage: true },
395
+ max_tokens: this.maxOutputTokens,
396
+ ...tools.length > 0 ? { tools } : {}
397
+ };
398
+ let inputTokens = 0;
399
+ let outputTokens = 0;
400
+ let cacheReadInputTokens = 0;
401
+ const cacheCreationInputTokens = 0;
402
+ try {
403
+ const stream = await this.client.chat.completions.create(params, {
404
+ ...abortSignal ? { signal: abortSignal } : {}
405
+ });
406
+ const toolCalls = /* @__PURE__ */ new Map();
407
+ let finishReason = null;
408
+ let reasoningAccumulate = "";
409
+ for await (const chunk of stream) {
410
+ if (chunk.usage) {
411
+ outputTokens = chunk.usage.completion_tokens ?? 0;
412
+ cacheReadInputTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
413
+ inputTokens = Math.max(0, (chunk.usage.prompt_tokens ?? 0) - cacheReadInputTokens);
414
+ }
415
+ if (chunk.choices.length === 0) {
416
+ continue;
417
+ }
418
+ const delta = chunk.choices[0].delta;
419
+ if (delta.content) {
420
+ yield { type: "text_delta", text: delta.content };
421
+ }
422
+ const reasoningContent = strArg(asRecord(delta), "reasoning_content");
423
+ if (reasoningContent) {
424
+ reasoningAccumulate += reasoningContent;
425
+ yield { type: "thinking_delta", text: reasoningContent };
426
+ }
427
+ if (delta.tool_calls) {
428
+ for (const tc of delta.tool_calls) {
429
+ if (!toolCalls.has(tc.index)) {
430
+ toolCalls.set(tc.index, {
431
+ id: tc.id ?? "",
432
+ name: tc.function?.name ?? "",
433
+ args: ""
434
+ });
435
+ if (tc.id) {
436
+ yield {
437
+ type: "tool_call_start",
438
+ toolName: tc.function?.name ?? "",
439
+ toolId: tc.id ?? ""
440
+ };
441
+ }
442
+ }
443
+ const existing = toolCalls.get(tc.index);
444
+ if (existing) {
445
+ if (tc.id) {
446
+ existing.id = tc.id;
447
+ }
448
+ if (tc.function?.name) {
449
+ existing.name = tc.function.name;
450
+ }
451
+ if (tc.function?.arguments) {
452
+ existing.args += tc.function.arguments;
453
+ yield {
454
+ type: "tool_call_delta",
455
+ text: tc.function.arguments
456
+ };
457
+ }
458
+ }
459
+ }
460
+ }
461
+ if (chunk.choices[0].finish_reason) {
462
+ finishReason = chunk.choices[0].finish_reason;
463
+ if (reasoningAccumulate) {
464
+ yield {
465
+ type: "thinking_complete",
466
+ thinking: reasoningAccumulate,
467
+ signature: ""
468
+ };
469
+ reasoningAccumulate = "";
470
+ }
471
+ for (const tu of toolCalls.values()) {
472
+ let args = {};
473
+ const jsonArgs = tu.args;
474
+ if (jsonArgs) {
475
+ try {
476
+ const parsed = JSON.parse(jsonArgs);
477
+ args = isRecord(parsed) ? asRecord(parsed) : { [DANGEROUSLY_JSON]: jsonArgs };
478
+ } catch (err) {
479
+ log.error({ err }, "llm operation failed");
480
+ args = {
481
+ [DANGEROUSLY_JSON]: jsonArgs
482
+ };
483
+ }
484
+ yield {
485
+ type: "tool_call_complete",
486
+ toolName: tu.name,
487
+ toolId: tu.id,
488
+ arguments: args
489
+ };
490
+ }
491
+ }
492
+ }
493
+ }
494
+ let stopReason;
495
+ if (finishReason === "length") {
496
+ stopReason = "max_tokens";
497
+ } else if (finishReason === "tool_calls" || toolCalls.size > 0) {
498
+ stopReason = "tool_use";
499
+ } else {
500
+ stopReason = "end_turn";
501
+ }
502
+ yield {
503
+ type: "stream_end",
504
+ stopReason,
505
+ usage: {
506
+ inputTokens,
507
+ outputTokens,
508
+ cacheReadInputTokens,
509
+ cacheCreationInputTokens
510
+ }
511
+ };
512
+ } catch (err) {
513
+ log.error({ err }, "llm operation failed");
514
+ throw classifyOpenAIError(err);
515
+ }
516
+ }
517
+ };
518
+ function classifyOpenAIError(err) {
519
+ if (err instanceof OpenAI.APIError) {
520
+ if (err.status === 413 /* PromptTooLong */ || err.status === 400 /* BadRequest */ && containsContextLengthError(err.message)) {
521
+ return new ContextTooLongError(`Context Too Long: ${err.message}`);
522
+ }
523
+ if (err.status === 401 /* InvalidAPIKey */) {
524
+ return new AuthenticationError(`Invalid API key: ${err.message}`);
525
+ }
526
+ if (err.status === 429 /* RateLimitError */) {
527
+ return new RateLimitError(`Rate limit error, please wait.`);
528
+ }
529
+ return new LLMError(`OpenAI API error (${asString(err.status)}): ${err.message}`);
530
+ }
531
+ return new NetworkError(`Network error: ${err instanceof Error ? err.message : asString(err)}`);
532
+ }
533
+ function buildChatCompletionMessages(messages) {
534
+ const params = [];
535
+ for (const m of messages) {
536
+ const reasoning = m.thinkingBlocks?.map((tb) => tb.thinking).join("") ?? "";
537
+ const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
538
+ if (m.toolUses && m.toolUses.length > 0) {
539
+ params.push({
540
+ role: "assistant",
541
+ content: assistantText || null,
542
+ tool_calls: m.toolUses.map((tu) => ({
543
+ id: tu.toolUseId,
544
+ type: "function",
545
+ function: {
546
+ name: tu.toolName,
547
+ arguments: JSON.stringify(tu.arguments)
548
+ },
549
+ ...reasoning ? {
550
+ reasoning_content: reasoning
551
+ } : {}
552
+ }))
553
+ });
554
+ } else if (m.toolResults && m.toolResults.length > 0) {
555
+ const pendingImageParts = [];
556
+ for (const tr of m.toolResults) {
557
+ params.push({
558
+ role: "tool",
559
+ tool_call_id: tr.toolUseId,
560
+ content: contentToText(tr.content)
561
+ });
562
+ pendingImageParts.push(...collectImageParts(tr));
563
+ }
564
+ if (pendingImageParts.length > 0) {
565
+ params.push({
566
+ role: "user",
567
+ content: pendingImageParts
568
+ });
569
+ }
570
+ } else if (m.role === "assistant") {
571
+ params.push({
572
+ role: "assistant",
573
+ content: assistantText,
574
+ ...reasoning ? {
575
+ reasoning_content: reasoning
576
+ } : {}
577
+ });
578
+ } else if (m.role === "system") {
579
+ params.push({
580
+ role: "system",
581
+ content: typeof m.content === "string" ? m.content : contentToText(m.content)
582
+ });
583
+ } else {
584
+ params.push({
585
+ role: "user",
586
+ content: userPartsFor(m.content)
587
+ });
588
+ }
589
+ }
590
+ return params;
591
+ }
592
+
593
+ export {
594
+ OpenAIClient,
595
+ buildOpenAIInput,
596
+ OpenAICompatClient,
597
+ buildChatCompletionMessages
598
+ };