@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.
- package/README.md +11 -11
- package/dist/{agent-YR6YK26C.js → agent-TCNIRVHD.js} +1 -1
- package/dist/anthropic-7RIVSEDF.js +4 -0
- package/dist/{checker-N5TIJEN5.js → checker-3LHDOATB.js} +1 -1
- package/dist/chunk-2IBMB3ZM.js +150 -0
- package/dist/chunk-AQ3A5CF4.js +5 -0
- package/dist/chunk-FA26MQ7K.js +257 -0
- package/dist/chunk-TKADLB2Q.js +4 -0
- package/dist/chunk-VA64DJNN.js +4 -0
- package/dist/lib/{agent-5WEKVRTD.js → agent-IRY7KR5D.js} +2 -2
- package/dist/lib/{anthropic-KWO3KLNV.js → anthropic-CL5IK7KN.js} +2 -2
- package/dist/lib/{checker-AXHPKVBY.js → checker-BS4MK3O6.js} +1 -1
- package/dist/lib/{chunk-2IVEVG5N.js → chunk-7QPDFSQE.js} +10 -17
- package/dist/lib/{chunk-3FBS5NB7.js → chunk-I7OPU4K2.js} +27 -11
- package/dist/lib/{chunk-KZSBR4FO.js → chunk-IYPTOY3Y.js} +48 -21
- package/dist/lib/{chunk-GCY44T7S.js → chunk-OOSNCCI7.js} +129 -254
- package/dist/lib/{chunk-Z4CYHTII.js → chunk-OQIQOU5S.js} +85 -26
- package/dist/lib/{chunk-DJ6AILPN.js → chunk-RMB3J46W.js} +13 -16
- package/dist/lib/index.d.ts +325 -123
- package/dist/lib/index.js +791 -1078
- package/dist/lib/{openai-PBF7EWNJ.js → openai-LH4E7ZQS.js} +2 -2
- package/dist/lib/{tool-filter-R6HDDJHE.js → tool-filter-CS6W2GMU.js} +1 -1
- package/dist/main.js +186 -178
- package/dist/{openai-QQ2K7GPF.js → openai-RZLCY55V.js} +15 -15
- package/dist/{server-G23HCFLI.js → server-IVIRQTO7.js} +17 -17
- package/dist/{tool-filter-VBP6WOGO.js → tool-filter-CG3KGQQW.js} +1 -1
- package/package.json +4 -3
- package/dist/anthropic-5GRR5JCT.js +0 -4
- package/dist/chunk-D34FUVGU.js +0 -4
- package/dist/chunk-LIMEBKCY.js +0 -408
- package/dist/chunk-POHIQUFP.js +0 -301
- package/dist/chunk-TMEX7RFA.js +0 -4
- package/dist/chunk-Y6SQB5FG.js +0 -4
- package/dist/glob.wasm +0 -0
- 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
|
|
67
|
-
var
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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-
|
|
254
|
+
// Persists a rule to the project-level YAML file in the `Tool(pattern)`
|
|
261
255
|
// format so "allow always" survives a restart.
|
|
262
|
-
|
|
263
|
-
mkdirSync(dirname2(this.
|
|
264
|
-
const rules = loadRulesFile(this.
|
|
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.
|
|
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 {
|
|
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.
|
|
432
|
+
this.ruleEngine.appendProjectRule({
|
|
436
433
|
tool: toolName,
|
|
437
434
|
pattern,
|
|
438
435
|
effect: "allow"
|