@swifty.js/swifty 0.0.29 → 0.0.30

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 (35) hide show
  1. package/README.md +11 -11
  2. package/dist/{agent-YR6YK26C.js → agent-TCNIRVHD.js} +1 -1
  3. package/dist/anthropic-7RIVSEDF.js +4 -0
  4. package/dist/{checker-N5TIJEN5.js → checker-3LHDOATB.js} +1 -1
  5. package/dist/chunk-2IBMB3ZM.js +150 -0
  6. package/dist/chunk-AQ3A5CF4.js +5 -0
  7. package/dist/chunk-FA26MQ7K.js +257 -0
  8. package/dist/chunk-TKADLB2Q.js +4 -0
  9. package/dist/chunk-VA64DJNN.js +4 -0
  10. package/dist/lib/{agent-5WEKVRTD.js → agent-IRY7KR5D.js} +2 -2
  11. package/dist/lib/{anthropic-KWO3KLNV.js → anthropic-CL5IK7KN.js} +2 -2
  12. package/dist/lib/{checker-AXHPKVBY.js → checker-BS4MK3O6.js} +1 -1
  13. package/dist/lib/{chunk-2IVEVG5N.js → chunk-7QPDFSQE.js} +10 -17
  14. package/dist/lib/{chunk-3FBS5NB7.js → chunk-I7OPU4K2.js} +27 -11
  15. package/dist/lib/{chunk-KZSBR4FO.js → chunk-IYPTOY3Y.js} +48 -21
  16. package/dist/lib/{chunk-GCY44T7S.js → chunk-OOSNCCI7.js} +129 -254
  17. package/dist/lib/{chunk-Z4CYHTII.js → chunk-OQIQOU5S.js} +85 -26
  18. package/dist/lib/{chunk-DJ6AILPN.js → chunk-RMB3J46W.js} +13 -16
  19. package/dist/lib/index.d.ts +325 -123
  20. package/dist/lib/index.js +791 -1078
  21. package/dist/lib/{openai-PBF7EWNJ.js → openai-LH4E7ZQS.js} +2 -2
  22. package/dist/lib/{tool-filter-R6HDDJHE.js → tool-filter-CS6W2GMU.js} +1 -1
  23. package/dist/main.js +186 -178
  24. package/dist/{openai-QQ2K7GPF.js → openai-RZLCY55V.js} +15 -15
  25. package/dist/{server-G23HCFLI.js → server-IVIRQTO7.js} +17 -17
  26. package/dist/{tool-filter-VBP6WOGO.js → tool-filter-CG3KGQQW.js} +1 -1
  27. package/package.json +4 -3
  28. package/dist/anthropic-5GRR5JCT.js +0 -4
  29. package/dist/chunk-D34FUVGU.js +0 -4
  30. package/dist/chunk-LIMEBKCY.js +0 -408
  31. package/dist/chunk-POHIQUFP.js +0 -301
  32. package/dist/chunk-TMEX7RFA.js +0 -4
  33. package/dist/chunk-Y6SQB5FG.js +0 -4
  34. package/dist/glob.wasm +0 -0
  35. package/dist/lib/glob.wasm +0 -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,
@@ -88,11 +88,7 @@ var CONTENT_FIELDS = {
88
88
  Grep: "pattern",
89
89
  InstallSkill: "source"
90
90
  };
91
- var DEFAULT_DENY_WRITE = [
92
- ".swifty/config.yaml",
93
- ".swifty/permissions.local.yaml",
94
- ".swifty/skills/"
95
- ];
91
+ var DEFAULT_DENY_WRITE = [".swifty/permissions.yaml", ".agents/skills/"];
96
92
  function extractContent(toolName, args) {
97
93
  if (toolName === MCP_CALL_TOOL_NAME) {
98
94
  return mcpCallPermissionContent(strArg(args, "server", ""), strArg(args, "tool", ""));
@@ -218,12 +214,10 @@ function evaluateRules(rules, toolName, content) {
218
214
  var RuleEngine = class {
219
215
  userPath;
220
216
  projectPath;
221
- localPath;
222
217
  cache = /* @__PURE__ */ new Map();
223
218
  constructor(workDir) {
224
219
  this.userPath = join2(homedir(), ".swifty", "permissions.yaml");
225
220
  this.projectPath = join2(workDir, ".swifty", "permissions.yaml");
226
- this.localPath = join2(workDir, ".swifty", "permissions.local.yaml");
227
221
  }
228
222
  // Read a single rules file; skips disk I/O and parsing on cache hit.
229
223
  rulesFor(path) {
@@ -242,12 +236,12 @@ var RuleEngine = class {
242
236
  this.cache.set(path, { mtimeNs: st.mtimeNs, size: st.size, rules });
243
237
  return rules;
244
238
  }
245
- // Return the merged snapshot of all three rules files. Reuses the previous
239
+ // Return the merged snapshot of both rules files. Reuses the previous
246
240
  // parse result when files are unchanged; re-reads only on change, so edits
247
241
  // take effect on the next evaluation without redundant parsing. One snapshot
248
242
  // is taken per tool call and shared across sub-command checks.
249
243
  snapshot() {
250
- return [this.userPath, this.projectPath, this.localPath].flatMap((p) => this.rulesFor(p));
244
+ return [this.userPath, this.projectPath].flatMap((p) => this.rulesFor(p));
251
245
  }
252
246
  // Take a snapshot then adjudicate: reuses the previous parse result when
253
247
  // files are unchanged; a freshly written "allow always" rule takes effect
@@ -257,11 +251,11 @@ var RuleEngine = class {
257
251
  evaluate(toolName, content) {
258
252
  return evaluateRules(this.snapshot(), toolName, content);
259
253
  }
260
- // Persists a rule to the project-local YAML file in the `Tool(pattern)`
254
+ // Persists a rule to the project-level YAML file in the `Tool(pattern)`
261
255
  // format so "allow always" survives a restart.
262
- appendLocalRule(rule) {
263
- mkdirSync(dirname2(this.localPath), { recursive: true });
264
- const rules = loadRulesFile(this.localPath);
256
+ appendProjectRule(rule) {
257
+ mkdirSync(dirname2(this.projectPath), { recursive: true });
258
+ const rules = loadRulesFile(this.projectPath);
265
259
  const exists = rules.some(
266
260
  (r) => r.tool === rule.tool && r.pattern === rule.pattern && r.effect === rule.effect
267
261
  );
@@ -273,7 +267,7 @@ var RuleEngine = class {
273
267
  rule: `${r.tool}(${r.pattern})`,
274
268
  effect: r.effect
275
269
  }));
276
- writeFileSync(this.localPath, yaml.dump(entries), "utf-8");
270
+ writeFileSync(this.projectPath, yaml.dump(entries), "utf-8");
277
271
  }
278
272
  };
279
273
  function detectDangerous(command) {
@@ -334,7 +328,10 @@ var PermissionChecker = class _PermissionChecker {
334
328
  const rules = () => snapshot ??= this.ruleEngine.snapshot();
335
329
  const explicitEffect = evaluateRules(rules(), toolName, content);
336
330
  if (explicitEffect === "deny" || explicitEffect === "ask") {
337
- return { effect: explicitEffect, reason: `Permission rule: ${explicitEffect}` };
331
+ return {
332
+ effect: explicitEffect,
333
+ reason: `Permission rule: ${explicitEffect}`
334
+ };
338
335
  }
339
336
  if (this.mode === "plan" && (toolName === "WriteFile" || toolName === "EditFile")) {
340
337
  const path = strArg(args, "file_path", "");
@@ -432,7 +429,7 @@ var PermissionChecker = class _PermissionChecker {
432
429
  const words = content.trim().split(/\s+/).slice(0, 2);
433
430
  pattern = words.join(" ") + "*";
434
431
  }
435
- this.ruleEngine.appendLocalRule({
432
+ this.ruleEngine.appendProjectRule({
436
433
  tool: toolName,
437
434
  pattern,
438
435
  effect: "allow"