@swifty.js/swifty 0.0.29 → 0.0.31

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 (48) hide show
  1. package/README.md +25 -11
  2. package/dist/agent-O6C4FCQY.js +4 -0
  3. package/dist/anthropic-CFM2DXMB.js +4 -0
  4. package/dist/checker-A6PGACKZ.js +4 -0
  5. package/dist/chunk-4ABFUBO4.js +601 -0
  6. package/dist/{chunk-K5ZK27BX.js → chunk-AGJYBJ5M.js} +1 -1
  7. package/dist/chunk-AQ3A5CF4.js +5 -0
  8. package/dist/chunk-MVVSONAE.js +4 -0
  9. package/dist/chunk-OQCEP2F5.js +4 -0
  10. package/dist/{chunk-QFSCPD63.js → chunk-TCQ4EB4G.js} +1 -1
  11. package/dist/chunk-Y2UUXOQA.js +4 -0
  12. package/dist/chunk-YAXZ2OUD.js +150 -0
  13. package/dist/lib/agent-YRTROHXU.js +10 -0
  14. package/dist/lib/{anthropic-KWO3KLNV.js → anthropic-R6DMXE7U.js} +4 -5
  15. package/dist/lib/{checker-AXHPKVBY.js → checker-M7RKMKJQ.js} +2 -3
  16. package/dist/lib/{chunk-KZSBR4FO.js → chunk-4MDCRPVV.js} +76 -40
  17. package/dist/lib/{chunk-DJ6AILPN.js → chunk-A4MXZA5Q.js} +209 -51
  18. package/dist/lib/{chunk-2IVEVG5N.js → chunk-EWE5IWZE.js} +28 -25
  19. package/dist/lib/{chunk-KG4MJGKQ.js → chunk-FZ4Y6MBN.js} +1 -4
  20. package/dist/lib/{chunk-GCY44T7S.js → chunk-J6AV7SIF.js} +221 -351
  21. package/dist/lib/{chunk-3FBS5NB7.js → chunk-OEPCNATU.js} +217 -41
  22. package/dist/lib/{chunk-Z4CYHTII.js → chunk-OQIQOU5S.js} +85 -26
  23. package/dist/lib/{chunk-PIL7M52F.js → chunk-XT67KGWE.js} +16 -3
  24. package/dist/lib/index.d.ts +1212 -932
  25. package/dist/lib/index.js +2836 -1611
  26. package/dist/lib/{openai-PBF7EWNJ.js → openai-5N42ZYIJ.js} +2 -2
  27. package/dist/lib/{tool-filter-R6HDDJHE.js → tool-filter-XG2GXFVN.js} +3 -2
  28. package/dist/main.js +197 -189
  29. package/dist/openai-NQYARTT6.js +34 -0
  30. package/dist/{server-G23HCFLI.js → server-BNT2IIKB.js} +18 -18
  31. package/dist/tool-filter-5NDJMSTD.js +4 -0
  32. package/package.json +4 -3
  33. package/dist/agent-YR6YK26C.js +0 -4
  34. package/dist/anthropic-5GRR5JCT.js +0 -4
  35. package/dist/checker-N5TIJEN5.js +0 -4
  36. package/dist/chunk-D34FUVGU.js +0 -4
  37. package/dist/chunk-E55KH72M.js +0 -4
  38. package/dist/chunk-LIMEBKCY.js +0 -408
  39. package/dist/chunk-POHIQUFP.js +0 -301
  40. package/dist/chunk-QCKJLO6X.js +0 -4
  41. package/dist/chunk-TMEX7RFA.js +0 -4
  42. package/dist/chunk-Y6SQB5FG.js +0 -4
  43. package/dist/glob.wasm +0 -0
  44. package/dist/lib/agent-5WEKVRTD.js +0 -11
  45. package/dist/lib/chunk-GHF2PSEW.js +0 -8
  46. package/dist/lib/glob.wasm +0 -0
  47. package/dist/openai-QQ2K7GPF.js +0 -34
  48. package/dist/tool-filter-VBP6WOGO.js +0 -4
@@ -4,12 +4,14 @@ import {
4
4
  LLMError,
5
5
  NetworkError,
6
6
  RateLimitError,
7
+ clampThinkingLevel,
7
8
  ensureToolPairing,
8
9
  getMaxOutputTokens,
10
+ getSupportedThinkingLevels,
9
11
  getThinkingLevel,
10
12
  resolveAPIKey,
11
13
  toReasoningEffort
12
- } from "./chunk-Z4CYHTII.js";
14
+ } from "./chunk-OQIQOU5S.js";
13
15
  import {
14
16
  asRecord,
15
17
  asString,
@@ -22,12 +24,90 @@ import {
22
24
  // src/llm/openai.ts
23
25
  import OpenAI from "openai";
24
26
  var log = createChildLogger({ module: "llm" });
27
+ function computerActionArguments(action) {
28
+ switch (action.type) {
29
+ case "scroll":
30
+ return {
31
+ type: "scroll",
32
+ x: action.x,
33
+ y: action.y,
34
+ scrollX: action.scroll_x,
35
+ scrollY: action.scroll_y,
36
+ ...action.keys?.length ? { keys: action.keys } : {}
37
+ };
38
+ case "click":
39
+ case "double_click":
40
+ case "drag":
41
+ case "move": {
42
+ const { keys, ...rest } = action;
43
+ return {
44
+ ...rest,
45
+ ...keys?.length ? { keys } : {}
46
+ };
47
+ }
48
+ default:
49
+ return { ...action };
50
+ }
51
+ }
52
+ function computerCallArguments(item) {
53
+ const actions = item.actions ?? (item.action ? [item.action] : []);
54
+ return {
55
+ actions: actions.map(computerActionArguments),
56
+ pendingSafetyChecks: item.pending_safety_checks.map((check) => ({
57
+ id: check.id,
58
+ ...check.code ? { code: check.code } : {},
59
+ ...check.message ? { message: check.message } : {}
60
+ })),
61
+ status: item.status
62
+ };
63
+ }
64
+ function toOpenAIResponsesTool(schema) {
65
+ if ("type" in schema && schema.type === "computer") {
66
+ return { ...schema };
67
+ }
68
+ if ("input_schema" in schema) {
69
+ const tool = schema;
70
+ return {
71
+ type: "function",
72
+ name: tool.name,
73
+ description: tool.description,
74
+ parameters: tool.input_schema,
75
+ strict: tool.strict ?? false
76
+ };
77
+ }
78
+ if ("type" in schema && schema.type === "function" && "parameters" in schema) {
79
+ return { ...schema };
80
+ }
81
+ throw new Error("OpenAI Responses received a tool schema serialized for another protocol.");
82
+ }
83
+ function toOpenAICompatTool(schema) {
84
+ if ("input_schema" in schema) {
85
+ const tool = schema;
86
+ return {
87
+ type: "function",
88
+ function: {
89
+ name: tool.name,
90
+ description: tool.description,
91
+ parameters: tool.input_schema,
92
+ strict: tool.strict ?? false
93
+ }
94
+ };
95
+ }
96
+ if ("function" in schema) {
97
+ return { ...schema };
98
+ }
99
+ throw new Error(
100
+ "OpenAI Chat Completions received a tool schema serialized for another protocol."
101
+ );
102
+ }
25
103
  var OpenAIClient = class {
104
+ protocol = "openai";
26
105
  client;
27
106
  model;
28
107
  systemPrompt;
29
108
  maxOutputTokens;
30
109
  thinkingLevel;
110
+ config;
31
111
  constructor(config, systemPrompt) {
32
112
  const apiKey = resolveAPIKey(config);
33
113
  if (!apiKey) {
@@ -42,6 +122,7 @@ var OpenAIClient = class {
42
122
  this.model = config.model;
43
123
  this.systemPrompt = systemPrompt;
44
124
  this.maxOutputTokens = getMaxOutputTokens(config);
125
+ this.config = { ...config };
45
126
  this.thinkingLevel = getThinkingLevel(config);
46
127
  }
47
128
  async *stream(conversation, toolSchemas, abortSignal) {
@@ -54,25 +135,17 @@ var OpenAIClient = class {
54
135
  for (const message of messages) {
55
136
  input.push(message);
56
137
  }
57
- const tools = toolSchemas.map((s) => {
58
- const schema = s.input_schema;
59
- return {
60
- type: "function",
61
- name: s.name,
62
- description: s.description,
63
- parameters: schema,
64
- strict: false
65
- };
66
- });
138
+ const tools = toolSchemas.map(toOpenAIResponsesTool);
139
+ const effort = toReasoningEffort(this.getThinkingLevel(), this.config);
67
140
  const params = {
68
141
  model: this.model,
69
142
  input,
70
143
  stream: true,
71
144
  max_output_tokens: this.maxOutputTokens,
72
145
  ...tools.length > 0 ? { tools } : {},
73
- // PI-equivalent thinking level Responses API reasoning effort. Omitted
74
- // entirely when thinking is off so non-reasoning models are unaffected.
75
- ...this.thinkingLevel !== "off" ? { reasoning: { effort: toReasoningEffort(this.thinkingLevel), summary: "auto" } } : {}
146
+ // Only explicit reasoning:false omits the field. Off sends none to defeat
147
+ // server-default reasoning; requesting a summary while off is unnecessary.
148
+ ...effort !== null ? { reasoning: { effort, ...effort !== "none" ? { summary: "auto" } : {} } } : {}
76
149
  };
77
150
  let inputTokens = 0;
78
151
  let outputTokens = 0;
@@ -119,6 +192,12 @@ var OpenAIClient = class {
119
192
  toolName: currentToolName,
120
193
  toolId: currentToolId
121
194
  };
195
+ } else if (event.item.type === "computer_call") {
196
+ yield {
197
+ type: "tool_call_start",
198
+ toolName: "ComputerUse",
199
+ toolId: event.item.call_id
200
+ };
122
201
  } else if (event.item.type === "reasoning") {
123
202
  reasoningId = event.item.id ?? "";
124
203
  reasoningText = "";
@@ -144,6 +223,14 @@ var OpenAIClient = class {
144
223
  currentToolName = "";
145
224
  currentToolId = "";
146
225
  jsonAccumulate = "";
226
+ } else if (event.item.type === "computer_call") {
227
+ yield {
228
+ type: "tool_call_complete",
229
+ toolId: event.item.call_id,
230
+ toolName: "ComputerUse",
231
+ arguments: computerCallArguments(event.item),
232
+ providerItemId: event.item.id
233
+ };
147
234
  }
148
235
  } else if (event.type === "response.completed" || event.type === "response.incomplete") {
149
236
  sawTerminalResponse = true;
@@ -198,11 +285,15 @@ var OpenAIClient = class {
198
285
  this.maxOutputTokens = maxTokens;
199
286
  }
200
287
  setThinkingLevel(level) {
201
- this.thinkingLevel = level;
288
+ this.thinkingLevel = clampThinkingLevel(this.config, level);
289
+ return this.thinkingLevel;
202
290
  }
203
291
  getThinkingLevel() {
204
292
  return this.thinkingLevel;
205
293
  }
294
+ getSupportedThinkingLevels() {
295
+ return getSupportedThinkingLevels(this.config);
296
+ }
206
297
  };
207
298
  function imageDataUrl(block) {
208
299
  if (!isRecord(block) || block.type !== "image" || !isRecord(block.source)) {
@@ -283,6 +374,56 @@ function toolOutputForResponses(tr) {
283
374
  }
284
375
  return rich.length > 0 ? rich : tr.content;
285
376
  }
377
+ function computerActionsForResponses(args) {
378
+ if (!Array.isArray(args.actions)) {
379
+ return [];
380
+ }
381
+ const actions = [];
382
+ for (const raw of args.actions) {
383
+ if (!isRecord(raw) || typeof raw.type !== "string") {
384
+ continue;
385
+ }
386
+ if (raw.type === "scroll") {
387
+ actions.push({
388
+ type: "scroll",
389
+ x: Number(raw.x),
390
+ y: Number(raw.y),
391
+ scroll_x: Number(raw.scrollX),
392
+ scroll_y: Number(raw.scrollY),
393
+ ...Array.isArray(raw.keys) ? { keys: raw.keys.map(String) } : {}
394
+ });
395
+ continue;
396
+ }
397
+ actions.push(raw);
398
+ }
399
+ return actions;
400
+ }
401
+ function safetyChecksForResponses(args) {
402
+ if (!Array.isArray(args.pendingSafetyChecks)) {
403
+ return [];
404
+ }
405
+ return args.pendingSafetyChecks.flatMap((raw) => {
406
+ if (!isRecord(raw) || typeof raw.id !== "string") {
407
+ return [];
408
+ }
409
+ return [
410
+ {
411
+ id: raw.id,
412
+ ...typeof raw.code === "string" ? { code: raw.code } : {},
413
+ ...typeof raw.message === "string" ? { message: raw.message } : {}
414
+ }
415
+ ];
416
+ });
417
+ }
418
+ function computerScreenshotUrl(tr) {
419
+ for (const block of tr.contentBlocks ?? []) {
420
+ const url = imageDataUrl(block);
421
+ if (url) {
422
+ return url;
423
+ }
424
+ }
425
+ return void 0;
426
+ }
286
427
  function collectRichParts(tr) {
287
428
  if (!tr.contentBlocks?.length) {
288
429
  return [];
@@ -340,6 +481,14 @@ function userPartsFor(content) {
340
481
  }
341
482
  function buildOpenAIInput(messages) {
342
483
  const result = [];
484
+ const computerCalls = /* @__PURE__ */ new Map();
485
+ for (const message of messages) {
486
+ for (const toolUse of message.toolUses ?? []) {
487
+ if (toolUse.toolName === "ComputerUse") {
488
+ computerCalls.set(toolUse.toolUseId, toolUse);
489
+ }
490
+ }
491
+ }
343
492
  for (const m of messages) {
344
493
  if (m.thinkingBlocks) {
345
494
  for (const tb of m.thinkingBlocks) {
@@ -359,20 +508,49 @@ function buildOpenAIInput(messages) {
359
508
  });
360
509
  }
361
510
  for (const tu of m.toolUses) {
362
- result.push({
363
- type: "function_call",
364
- name: tu.toolName,
365
- call_id: tu.toolUseId,
366
- arguments: JSON.stringify(tu.arguments)
367
- });
511
+ if (tu.toolName === "ComputerUse") {
512
+ const status = tu.arguments.status;
513
+ result.push({
514
+ type: "computer_call",
515
+ id: tu.providerItemId ?? tu.toolUseId,
516
+ call_id: tu.toolUseId,
517
+ status: status === "in_progress" || status === "incomplete" ? status : "completed",
518
+ actions: computerActionsForResponses(tu.arguments),
519
+ pending_safety_checks: safetyChecksForResponses(tu.arguments)
520
+ });
521
+ } else {
522
+ result.push({
523
+ type: "function_call",
524
+ name: tu.toolName,
525
+ call_id: tu.toolUseId,
526
+ arguments: JSON.stringify(tu.arguments)
527
+ });
528
+ }
368
529
  }
369
530
  } else if (m.toolResults && m.toolResults.length > 0) {
370
531
  for (const tr of m.toolResults) {
371
- result.push({
372
- type: "function_call_output",
373
- call_id: tr.toolUseId,
374
- output: toolOutputForResponses(tr)
375
- });
532
+ const computerCall = computerCalls.get(tr.toolUseId);
533
+ if (computerCall) {
534
+ const imageUrl = computerScreenshotUrl(tr);
535
+ result.push({
536
+ type: "computer_call_output",
537
+ call_id: tr.toolUseId,
538
+ output: {
539
+ type: "computer_screenshot",
540
+ ...imageUrl ? { image_url: imageUrl } : {}
541
+ },
542
+ acknowledged_safety_checks: safetyChecksForResponses(computerCall.arguments)
543
+ });
544
+ if (tr.isError || !imageUrl) {
545
+ result.push({ role: "user", content: tr.content });
546
+ }
547
+ } else {
548
+ result.push({
549
+ type: "function_call_output",
550
+ call_id: tr.toolUseId,
551
+ output: toolOutputForResponses(tr)
552
+ });
553
+ }
376
554
  }
377
555
  if (m.content.length > 0) {
378
556
  result.push({ role: "user", content: userContentsFor(m.content) });
@@ -395,11 +573,13 @@ function containsContextLengthError(msg) {
395
573
  return /context_length_exceeded/i.test(msg) || /maximum\scontext\slength/i.test(msg) || /prompts?\s+too\s+long/i.test(msg);
396
574
  }
397
575
  var OpenAICompatClient = class {
576
+ protocol = "openai-compat";
398
577
  client;
399
578
  model;
400
579
  systemPrompt;
401
580
  maxOutputTokens;
402
581
  thinkingLevel;
582
+ config;
403
583
  constructor(config, systemPrompt) {
404
584
  const apiKey = resolveAPIKey(config);
405
585
  if (!apiKey) {
@@ -411,6 +591,7 @@ var OpenAICompatClient = class {
411
591
  this.model = config.model;
412
592
  this.systemPrompt = systemPrompt;
413
593
  this.maxOutputTokens = getMaxOutputTokens(config);
594
+ this.config = { ...config };
414
595
  this.thinkingLevel = getThinkingLevel(config);
415
596
  }
416
597
  setSystemPrompt(prompt) {
@@ -420,11 +601,15 @@ var OpenAICompatClient = class {
420
601
  this.maxOutputTokens = maxTokens;
421
602
  }
422
603
  setThinkingLevel(level) {
423
- this.thinkingLevel = level;
604
+ this.thinkingLevel = clampThinkingLevel(this.config, level);
605
+ return this.thinkingLevel;
424
606
  }
425
607
  getThinkingLevel() {
426
608
  return this.thinkingLevel;
427
609
  }
610
+ getSupportedThinkingLevels() {
611
+ return getSupportedThinkingLevels(this.config);
612
+ }
428
613
  async *stream(conversation, toolSchemas, abortSignal) {
429
614
  const messages = [
430
615
  {
@@ -433,17 +618,8 @@ var OpenAICompatClient = class {
433
618
  },
434
619
  ...buildChatCompletionMessages(ensureToolPairing(conversation.getMessages()))
435
620
  ];
436
- const tools = toolSchemas.map((ts) => ({
437
- // name: ts.name,
438
- // description: ts.description,
439
- type: "function",
440
- function: {
441
- name: ts.name,
442
- description: ts.description,
443
- parameters: ts.input_schema,
444
- strict: false
445
- }
446
- }));
621
+ const tools = toolSchemas.map(toOpenAICompatTool);
622
+ const effort = toReasoningEffort(this.getThinkingLevel(), this.config);
447
623
  const params = {
448
624
  model: this.model,
449
625
  messages,
@@ -451,9 +627,9 @@ var OpenAICompatClient = class {
451
627
  stream_options: { include_usage: true },
452
628
  max_tokens: this.maxOutputTokens,
453
629
  ...tools.length > 0 ? { tools } : {},
454
- // PI-equivalent thinking level Chat Completions reasoning_effort.
455
- // Omitted when thinking is off so non-reasoning models are unaffected.
456
- ...this.thinkingLevel !== "off" ? { reasoning_effort: toReasoningEffort(this.thinkingLevel) } : {}
630
+ // Configured non-reasoning providers omit the field; off otherwise sends
631
+ // none explicitly so a server default cannot silently enable reasoning.
632
+ ...effort !== null ? { reasoning_effort: effort } : {}
457
633
  };
458
634
  let inputTokens = 0;
459
635
  let outputTokens = 0;
@@ -40,7 +40,7 @@ var ContextTooLongError = class extends LLMError {
40
40
  import { existsSync, readFileSync } from "fs";
41
41
  import { homedir } from "os";
42
42
  import { join } from "path";
43
- import { safeParse } from "@modelcontextprotocol/sdk/server/zod-compat.js";
43
+ import { getParseErrorMessage, safeParse } from "@modelcontextprotocol/sdk/server/zod-compat.js";
44
44
  import yaml from "js-yaml";
45
45
  import { z } from "zod";
46
46
  var log = createChildLogger({ module: "config" });
@@ -63,8 +63,18 @@ function globalConfigPath() {
63
63
  return join(homedir(), ".swifty", "config.yaml");
64
64
  }
65
65
  var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
66
- var ThinkingConfigSchema = z.union([z.boolean(), z.enum(THINKING_LEVELS)]);
67
- var ProviderConfigSchema = z.object({
66
+ var ReasoningEffortSchema = z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
67
+ var AnthropicEffortSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
68
+ var ThinkingLevelMapSchema = z.strictObject({
69
+ off: z.literal("none").nullable().optional(),
70
+ minimal: ReasoningEffortSchema.nullable().optional(),
71
+ low: ReasoningEffortSchema.nullable().optional(),
72
+ medium: ReasoningEffortSchema.nullable().optional(),
73
+ high: ReasoningEffortSchema.nullable().optional(),
74
+ xhigh: ReasoningEffortSchema.nullable().optional(),
75
+ max: ReasoningEffortSchema.nullable().optional()
76
+ });
77
+ var ProviderConfigSchema = z.looseObject({
68
78
  name: z.string(),
69
79
  /**
70
80
  * enum: ["anthropic", "openai", "openai-compat"]
@@ -73,7 +83,12 @@ var ProviderConfigSchema = z.object({
73
83
  base_url: z.string(),
74
84
  model: z.string(),
75
85
  api_key: z.string().optional(),
76
- thinking: ThinkingConfigSchema.optional(),
86
+ thinking: z.enum(THINKING_LEVELS).optional(),
87
+ /** Explicit capability metadata, never inferred from model names. */
88
+ reasoning: z.boolean().optional(),
89
+ thinking_level_map: ThinkingLevelMapSchema.optional(),
90
+ /** Only Anthropic uses this mode; existing configurations use budgets. */
91
+ thinking_mode: z.enum(["budget", "adaptive"]).optional(),
77
92
  context_window: z.coerce.number().optional(),
78
93
  /**
79
94
  * The model's output ceiling (PI's `model.maxTokens`). Clamped to the
@@ -92,27 +107,69 @@ var THINKING_BUDGETS = {
92
107
  xhigh: 32768,
93
108
  max: 65536
94
109
  };
110
+ var MIN_THINKING_BUDGET_TOKENS = 1024;
111
+ var MIN_THINKING_ANSWER_TOKENS = 1024;
95
112
  function isValidThinkingLevel(value) {
96
- return THINKING_LEVELS.includes(value);
97
- }
98
- function defaultThinkingLevelFor(protocol) {
99
- return protocol === "anthropic" ? DEFAULT_THINKING_LEVEL : "off";
113
+ return THINKING_LEVELS.some((level) => level === value);
100
114
  }
101
115
  function getThinkingLevel(provider) {
102
- const thinking = provider.thinking;
103
- if (thinking === void 0) {
104
- return defaultThinkingLevelFor(provider.protocol);
105
- }
106
- if (typeof thinking === "boolean") {
107
- return thinking ? defaultThinkingLevelFor(provider.protocol) : "off";
108
- }
109
- return thinking;
116
+ return clampThinkingLevel(provider, provider.thinking ?? DEFAULT_THINKING_LEVEL);
110
117
  }
111
118
  function thinkingBudgetForLevel(level) {
112
119
  return level === "off" ? 0 : THINKING_BUDGETS[level];
113
120
  }
114
- function toReasoningEffort(level) {
115
- return level === "off" ? "none" : level;
121
+ function toReasoningEffort(level, provider) {
122
+ if (provider?.reasoning === false) {
123
+ return null;
124
+ }
125
+ if (level === "off") {
126
+ return "none";
127
+ }
128
+ const mapped = provider?.thinking_level_map?.[level];
129
+ if (mapped !== void 0) {
130
+ return mapped;
131
+ }
132
+ if (provider?.protocol === "anthropic" && provider.thinking_mode === "adaptive") {
133
+ if (level === "minimal") {
134
+ return "low";
135
+ }
136
+ if (level === "xhigh") {
137
+ return "high";
138
+ }
139
+ }
140
+ return level;
141
+ }
142
+ function toAnthropicThinkingEffort(level, provider) {
143
+ const parsed = AnthropicEffortSchema.safeParse(toReasoningEffort(level, provider));
144
+ return parsed.success ? parsed.data : null;
145
+ }
146
+ function getSupportedThinkingLevels(provider) {
147
+ if (provider.reasoning === false || provider.protocol === "anthropic" && provider.thinking_mode !== "adaptive" && getMaxOutputTokens(provider) < MIN_THINKING_BUDGET_TOKENS + MIN_THINKING_ANSWER_TOKENS) {
148
+ return ["off"];
149
+ }
150
+ return THINKING_LEVELS.filter((level) => {
151
+ if (level === "off") {
152
+ return true;
153
+ }
154
+ if (provider.protocol === "anthropic" && provider.thinking_mode === "adaptive") {
155
+ return toAnthropicThinkingEffort(level, provider) !== null;
156
+ }
157
+ const effort = toReasoningEffort(level, provider);
158
+ return effort !== null && effort !== "none";
159
+ });
160
+ }
161
+ function clampThinkingLevel(provider, level) {
162
+ const supported = getSupportedThinkingLevels(provider);
163
+ let effective = "off";
164
+ for (const candidate of THINKING_LEVELS) {
165
+ if (supported.includes(candidate)) {
166
+ effective = candidate;
167
+ }
168
+ if (candidate === level) {
169
+ break;
170
+ }
171
+ }
172
+ return effective;
116
173
  }
117
174
  function withProviderDefaults(provider) {
118
175
  return {
@@ -218,6 +275,10 @@ function loadSingleFile(path) {
218
275
  const parsed2 = safeParse(z.array(ProviderConfigSchema), raw.providers);
219
276
  if (parsed2.success) {
220
277
  providers = parsed2.data.map(withProviderDefaults);
278
+ } else {
279
+ throw new ConfigError(
280
+ `Invalid provider configuration in ${path}: ${getParseErrorMessage(parsed2.error)}`
281
+ );
221
282
  }
222
283
  }
223
284
  if ("permission_mode" in raw && typeof raw.permission_mode === "string") {
@@ -294,13 +355,7 @@ function loadConfig(path, options = {}) {
294
355
  if (options.allowEmptyProviders) {
295
356
  return { providers: [], mcp_servers: [], hooks: [] };
296
357
  }
297
- const legacy = [
298
- join(process.cwd(), ".swifty/config.yaml"),
299
- join(process.cwd(), ".swifty/config.local.yaml")
300
- ].filter((legacyPath) => existsSync(legacyPath)).join(", ");
301
- throw new ConfigError(
302
- `No config file found, expected ${candidate}.` + (legacy ? ` Found project config at ${legacy}; move it to ${candidate}.` : "")
303
- );
358
+ throw new ConfigError(`No config file found, expected ${candidate}.`);
304
359
  }
305
360
  const config = loadSingleFile(candidate);
306
361
  if (!options.allowEmptyProviders || config.providers.length > 0) {
@@ -374,11 +429,15 @@ export {
374
429
  DEFAULT_CONTEXT_WINDOW,
375
430
  DEFAULT_MAX_OUTPUT_TOKENS,
376
431
  THINKING_BUDGETS,
432
+ MIN_THINKING_BUDGET_TOKENS,
433
+ MIN_THINKING_ANSWER_TOKENS,
377
434
  isValidThinkingLevel,
378
- defaultThinkingLevelFor,
379
435
  getThinkingLevel,
380
436
  thinkingBudgetForLevel,
381
437
  toReasoningEffort,
438
+ toAnthropicThinkingEffort,
439
+ getSupportedThinkingLevels,
440
+ clampThinkingLevel,
382
441
  withProviderDefaults,
383
442
  getContextWindow,
384
443
  getMaxOutputTokens,
@@ -41,9 +41,17 @@ function normalizeDocumentBlock(value) {
41
41
  if (value.source.type === "url" && typeof value.source.url === "string") {
42
42
  source = { type: "url", url: value.source.url };
43
43
  } else if (value.source.type === "base64" && value.source.media_type === "application/pdf" && typeof value.source.data === "string") {
44
- source = { type: "base64", media_type: "application/pdf", data: value.source.data };
44
+ source = {
45
+ type: "base64",
46
+ media_type: "application/pdf",
47
+ data: value.source.data
48
+ };
45
49
  } else if (value.source.type === "text" && value.source.media_type === "text/plain" && typeof value.source.data === "string") {
46
- source = { type: "text", media_type: "text/plain", data: value.source.data };
50
+ source = {
51
+ type: "text",
52
+ media_type: "text/plain",
53
+ data: value.source.data
54
+ };
47
55
  } else if (value.source.type === "content") {
48
56
  if (typeof value.source.content === "string") {
49
57
  source = { type: "content", content: value.source.content };
@@ -93,7 +101,12 @@ function normalizeToolResultContentBlock(value) {
93
101
  }
94
102
  content.push(block);
95
103
  }
96
- return { type: "search_result", source: value.source, title: value.title, content };
104
+ return {
105
+ type: "search_result",
106
+ source: value.source,
107
+ title: value.title,
108
+ content
109
+ };
97
110
  }
98
111
  return normalizeDocumentBlock(value);
99
112
  }