@swifty.js/swifty 0.0.27 → 0.0.29

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 (51) hide show
  1. package/README.md +47 -37
  2. package/dist/agent-YR6YK26C.js +4 -0
  3. package/dist/anthropic-5GRR5JCT.js +4 -0
  4. package/dist/checker-N5TIJEN5.js +4 -0
  5. package/dist/chunk-D34FUVGU.js +4 -0
  6. package/dist/{chunk-UGJVMFGH.js → chunk-K5ZK27BX.js} +1 -1
  7. package/dist/chunk-LIMEBKCY.js +408 -0
  8. package/dist/chunk-OTVZGPHD.js +121 -0
  9. package/dist/chunk-POHIQUFP.js +301 -0
  10. package/dist/chunk-QCKJLO6X.js +4 -0
  11. package/dist/chunk-QFSCPD63.js +4 -0
  12. package/dist/chunk-TMEX7RFA.js +4 -0
  13. package/dist/chunk-Y6SQB5FG.js +4 -0
  14. package/dist/lib/agent-5WEKVRTD.js +11 -0
  15. package/dist/lib/{anthropic-RYPJDHQM.js → anthropic-KWO3KLNV.js} +4 -5
  16. package/dist/lib/{checker-6BW4222R.js → checker-AXHPKVBY.js} +2 -2
  17. package/dist/lib/{chunk-HK2Z6WP4.js → chunk-2IVEVG5N.js} +5 -1
  18. package/dist/lib/{chunk-XFSN4LMA.js → chunk-3FBS5NB7.js} +71 -18
  19. package/dist/lib/{chunk-GNI7YX6F.js → chunk-DJ6AILPN.js} +70 -24
  20. package/dist/lib/chunk-GCY44T7S.js +2346 -0
  21. package/dist/lib/{chunk-PZ42NAFA.js → chunk-KG4MJGKQ.js} +19 -21
  22. package/dist/lib/{chunk-2AUSNVIB.js → chunk-KZSBR4FO.js} +44 -37
  23. package/dist/lib/chunk-PIL7M52F.js +127 -0
  24. package/dist/lib/chunk-Z4CYHTII.js +392 -0
  25. package/dist/lib/index.d.ts +227 -132
  26. package/dist/lib/index.js +2195 -1602
  27. package/dist/lib/{openai-VX5VBXZ4.js → openai-PBF7EWNJ.js} +2 -3
  28. package/dist/lib/{tool-filter-VF7TZRE5.js → tool-filter-R6HDDJHE.js} +1 -1
  29. package/dist/main.js +203 -194
  30. package/dist/{openai-5FDSLJNM.js → openai-QQ2K7GPF.js} +14 -14
  31. package/dist/{server-F666APPN.js → server-G23HCFLI.js} +21 -21
  32. package/dist/{tool-filter-NO7I3HFD.js → tool-filter-VBP6WOGO.js} +1 -1
  33. package/package.json +2 -2
  34. package/dist/agent-OMC6YIMW.js +0 -4
  35. package/dist/anthropic-T5K4BQPB.js +0 -4
  36. package/dist/checker-TOQVEG3P.js +0 -4
  37. package/dist/chunk-5I2WXSGV.js +0 -90
  38. package/dist/chunk-A2VR3BC4.js +0 -4
  39. package/dist/chunk-A57FEYSN.js +0 -4
  40. package/dist/chunk-BRC5L644.js +0 -130
  41. package/dist/chunk-FZPTNGTU.js +0 -4
  42. package/dist/chunk-G6YIQW3Z.js +0 -238
  43. package/dist/chunk-I2XG2PXV.js +0 -4
  44. package/dist/chunk-LVSHXIBK.js +0 -389
  45. package/dist/chunk-SULWNDNC.js +0 -4
  46. package/dist/chunk-WO6DL7OB.js +0 -35
  47. package/dist/lib/agent-U7MIUCKT.js +0 -9
  48. package/dist/lib/chunk-2QILP24G.js +0 -1120
  49. package/dist/lib/chunk-3LU4APFB.js +0 -339
  50. package/dist/lib/chunk-OO2CLOEE.js +0 -88
  51. package/dist/lib/chunk-RKJYTYQM.js +0 -1285
@@ -54,9 +54,9 @@ var MCPToolWrapper = class {
54
54
  input_schema: this.inputSchema
55
55
  };
56
56
  }
57
- async execute(_ctx, args) {
57
+ async execute(ctx, args) {
58
58
  try {
59
- return await this.client.callTool(this.originalName, args);
59
+ return await this.client.callTool(this.originalName, args, ctx.abortSignal);
60
60
  } catch (err) {
61
61
  log.error({ err }, "mcp operation failed");
62
62
  return {
@@ -82,7 +82,7 @@ function coerceScalar(value, want) {
82
82
  return value;
83
83
  }
84
84
  const parsed = want === "integer" ? Number.parseInt(text, 10) : Number.parseFloat(text);
85
- return Number.isNaN(parsed) ? value : parsed;
85
+ return Number.isFinite(parsed) ? parsed : value;
86
86
  }
87
87
  if (want === "boolean" && typeof value === "string") {
88
88
  const low = value.trim().toLowerCase();
@@ -177,21 +177,15 @@ var McpCallTool = class {
177
177
  }
178
178
  };
179
179
  }
180
- /**
181
- * Try in order: full name / server+short name / unique short-name suffix match.
182
- *
183
- * The model very often passes only the short name (roughly three in ten calls in
184
- * practice), so this must be tolerant — otherwise it needlessly costs a retry
185
- * round.
186
- */
187
- resolve(server, tool) {
188
- const direct = this.registry.get(tool) ?? this.registry.get(buildMcpToolName(server, tool));
189
- if (direct) {
190
- return direct;
180
+ resolveTarget(args) {
181
+ const server = strArg(args, "server");
182
+ const name = strArg(args, "tool");
183
+ if (!server || !name) {
184
+ return void 0;
191
185
  }
192
- const suffix = MCP_NAME_SEP + sanitizeSegment(tool);
193
- const matches = this.registry.listTools().filter((t) => t.name.startsWith(MCP_TOOL_PREFIX) && t.name.endsWith(suffix));
194
- return matches.length === 1 ? matches[0] : void 0;
186
+ const fullName = name.startsWith(MCP_TOOL_PREFIX) ? name : buildMcpToolName(server, name);
187
+ const target = this.registry.get(fullName);
188
+ return target && isMcpToolLike(target) && sanitizeSegment(target.mcpServerName) === sanitizeSegment(server) ? target : void 0;
195
189
  }
196
190
  availableNames() {
197
191
  return this.registry.listTools().filter((t) => t.name.startsWith(MCP_TOOL_PREFIX)).map((t) => t.name).sort();
@@ -202,7 +196,7 @@ var McpCallTool = class {
202
196
  if (tool === "") {
203
197
  return { output: "McpCall requires a 'tool' name", isError: true };
204
198
  }
205
- const target = this.resolve(server, tool);
199
+ const target = this.resolveTarget(args);
206
200
  if (!target) {
207
201
  const names = this.availableNames();
208
202
  const hint = names.length > 0 ? names.join(", ") : "(none connected)";
@@ -211,10 +205,13 @@ var McpCallTool = class {
211
205
  isError: true
212
206
  };
213
207
  }
214
- let inner = {};
215
- if (typeof args.arguments === "object" && args.arguments !== null && !Array.isArray(args.arguments)) {
216
- inner = asRecord(args.arguments);
208
+ if (typeof args.arguments !== "object" || args.arguments === null || Array.isArray(args.arguments)) {
209
+ return {
210
+ output: "McpCall requires an 'arguments' object matching the target schema",
211
+ isError: true
212
+ };
217
213
  }
214
+ let inner = asRecord(args.arguments);
218
215
  if (isMcpToolLike(target)) {
219
216
  const schema = target.mcpInputSchema();
220
217
  if (Object.keys(schema).length > 0) {
@@ -224,6 +221,7 @@ var McpCallTool = class {
224
221
  }
225
222
  }
226
223
  }
224
+ ctx.abortSignal?.throwIfAborted();
227
225
  return target.execute(ctx, inner);
228
226
  }
229
227
  };
@@ -1,30 +1,30 @@
1
1
  import {
2
- computeCompactThreshold
3
- } from "./chunk-2QILP24G.js";
4
- import {
5
- getContextWindow,
6
- getMaxOutputTokens,
7
- resolveAPIKey
8
- } from "./chunk-3LU4APFB.js";
2
+ normalizeToolResultContentBlock
3
+ } from "./chunk-PIL7M52F.js";
9
4
  import {
10
5
  AuthenticationError,
11
6
  ContextTooLongError,
12
7
  LLMError,
13
8
  NetworkError,
14
9
  RateLimitError,
15
- ensureToolPairing
16
- } from "./chunk-OO2CLOEE.js";
10
+ ensureToolPairing,
11
+ getMaxOutputTokens,
12
+ getThinkingLevel,
13
+ resolveAPIKey,
14
+ thinkingBudgetForLevel
15
+ } from "./chunk-Z4CYHTII.js";
17
16
  import {
18
17
  MCP_TOOL_PREFIX,
19
18
  isMcpToolLike
20
- } from "./chunk-PZ42NAFA.js";
19
+ } from "./chunk-KG4MJGKQ.js";
21
20
  import {
22
21
  asErrorString,
23
22
  asRecord,
24
23
  asString,
25
24
  contentToText,
26
25
  createChildLogger,
27
- isRecord
26
+ isRecord,
27
+ strArg
28
28
  } from "./chunk-EY7HE52Q.js";
29
29
 
30
30
  // src/llm/anthropic.ts
@@ -96,6 +96,8 @@ function decideAndApply(registry, baseUrl, contextWindow) {
96
96
  }
97
97
 
98
98
  // src/llm/anthropic.ts
99
+ var MIN_THINKING_BUDGET_TOKENS = 1024;
100
+ var MIN_ANSWER_TOKENS = 1024;
99
101
  function markToolsForCache(tools) {
100
102
  for (let i = tools.length - 1; i >= 0; i--) {
101
103
  const t = tools[i];
@@ -152,14 +154,17 @@ async function fetchModelContextWindow(config) {
152
154
  clearTimeout(timer);
153
155
  }
154
156
  }
155
- function supportsAdaptiveThinking() {
156
- return true;
157
- }
158
157
  function userBlocksFor(content) {
159
158
  if (typeof content === "string") {
160
159
  return [{ type: "text", text: content }];
161
160
  }
162
- return content;
161
+ return content.map((raw) => {
162
+ const block = normalizeToolResultContentBlock(raw);
163
+ if (!block || block.type === "tool_reference") {
164
+ throw new Error(`Unsupported user content block: ${strArg(raw, "type", "unknown")}`);
165
+ }
166
+ return block;
167
+ });
163
168
  }
164
169
  function buildAnthropicMessages(messages) {
165
170
  const result = [];
@@ -208,6 +213,9 @@ function buildAnthropicMessages(messages) {
208
213
  content: tr.contentBlocks?.length ? tr.contentBlocks : tr.content
209
214
  });
210
215
  }
216
+ if (m.content.length > 0) {
217
+ blocks.push(...userBlocksFor(m.content));
218
+ }
211
219
  result.push({ role: "user", content: blocks });
212
220
  } else {
213
221
  if (result.length === 0) {
@@ -248,18 +256,17 @@ var AnthropicClient = class {
248
256
  client;
249
257
  model;
250
258
  /**
251
- * Whether supports/enable thinking, default false
259
+ * PI-equivalent thinking level; maps to a thinking token budget.
252
260
  */
253
- thinking;
261
+ thinkingLevel;
254
262
  systemPrompt;
255
263
  maxOutputTokens;
256
264
  /** Currently not used */
257
- contextWindow;
258
265
  constructor(config, systemPrompt) {
259
266
  const apiKey = resolveAPIKey(config);
260
267
  if (!apiKey) {
261
268
  throw new AuthenticationError(
262
- "Anthropic API key not found, set ANTHROPIC_API_KEY in .swifty/config.y(a)ml, or via ANTHROPIC_API_KEY env variable."
269
+ "Anthropic API key not found, set ANTHROPIC_API_KEY in ~/.swifty/config.yaml, or via ANTHROPIC_API_KEY env variable."
263
270
  );
264
271
  }
265
272
  this.client = new Anthropic({
@@ -267,10 +274,9 @@ var AnthropicClient = class {
267
274
  baseURL: config.base_url
268
275
  });
269
276
  this.model = config.model;
270
- this.thinking = config.thinking ?? true;
277
+ this.thinkingLevel = getThinkingLevel(config);
271
278
  this.systemPrompt = systemPrompt;
272
279
  this.maxOutputTokens = getMaxOutputTokens(config);
273
- this.contextWindow = getContextWindow(config);
274
280
  }
275
281
  setSystemPrompt(prompt) {
276
282
  this.systemPrompt = prompt;
@@ -278,6 +284,12 @@ var AnthropicClient = class {
278
284
  setMaxOutputTokens(maxTokens) {
279
285
  this.maxOutputTokens = maxTokens;
280
286
  }
287
+ setThinkingLevel(level) {
288
+ this.thinkingLevel = level;
289
+ }
290
+ getThinkingLevel() {
291
+ return this.thinkingLevel;
292
+ }
281
293
  async *stream(conversation, toolSchemas, abortSignal) {
282
294
  const messages = buildAnthropicMessages(ensureToolPairing(conversation.getMessages()));
283
295
  const sendToolSearchBeta = needsToolSearchBeta(toolSchemas);
@@ -316,19 +328,11 @@ var AnthropicClient = class {
316
328
  messages,
317
329
  ...antToolSchemas.length > 0 ? { tools: antToolSchemas } : {}
318
330
  };
319
- if (this.thinking) {
320
- if (supportsAdaptiveThinking()) {
321
- params.thinking = {
322
- type: "enabled",
323
- budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
324
- };
325
- }
326
- } else {
327
- params.thinking = {
328
- type: "enabled",
329
- budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
330
- };
331
- }
331
+ const budgetCeiling = this.maxOutputTokens - MIN_ANSWER_TOKENS;
332
+ params.thinking = this.thinkingLevel !== "off" && budgetCeiling >= MIN_THINKING_BUDGET_TOKENS ? {
333
+ type: "enabled",
334
+ budget_tokens: Math.min(thinkingBudgetForLevel(this.thinkingLevel), budgetCeiling)
335
+ } : { type: "disabled" };
332
336
  let inputTokens = 0;
333
337
  let outputTokens = 0;
334
338
  let cacheReadInputTokens = 0;
@@ -377,7 +381,7 @@ var AnthropicClient = class {
377
381
  text: delta.thinking
378
382
  };
379
383
  } else if (delta.type === "signature_delta") {
380
- thinkingSignature = delta.signature;
384
+ thinkingSignature += delta.signature;
381
385
  } else if (delta.type === "text_delta") {
382
386
  yield {
383
387
  type: "text_delta",
@@ -497,6 +501,7 @@ function markLastUserTailForCache(messages) {
497
501
  Reflect.set(last, "cache_control", {
498
502
  type: "ephemeral"
499
503
  });
504
+ return;
500
505
  }
501
506
  }
502
507
  function classifyAnthropicError(err) {
@@ -508,14 +513,16 @@ function classifyAnthropicError(err) {
508
513
  return new AuthenticationError(`Invalid API key: ${err.message}`);
509
514
  }
510
515
  if (err.status === 429 /* RateLimitError */) {
511
- const retryAfter = asRecord(err.headers)["retry-after"];
516
+ const headers = err.headers;
517
+ const retryAfter = headers instanceof Headers ? headers.get("retry-after") : void 0;
512
518
  let message = "Rate Limited";
513
519
  if (retryAfter) {
514
520
  const s = Number.parseInt(asString(retryAfter));
515
521
  if (Number.isNaN(s)) {
516
522
  message += ", please wait.";
523
+ } else {
524
+ message += `, retry after ${asString(s)}s.`;
517
525
  }
518
- message += `, retry after ${asString(s)}s.`;
519
526
  } else {
520
527
  message += ", please wait.";
521
528
  }
@@ -0,0 +1,127 @@
1
+ // src/tools/types.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4
+ }
5
+ var IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Set([
6
+ "image/jpeg",
7
+ "image/png",
8
+ "image/gif",
9
+ "image/webp"
10
+ ]);
11
+ function isImageMediaType(value) {
12
+ return IMAGE_MEDIA_TYPES.has(value);
13
+ }
14
+ function normalizeTextBlock(value) {
15
+ return isRecord(value) && value.type === "text" && typeof value.text === "string" ? { type: "text", text: value.text } : null;
16
+ }
17
+ function normalizeImageBlock(value) {
18
+ if (!isRecord(value) || value.type !== "image" || !isRecord(value.source)) {
19
+ return null;
20
+ }
21
+ if (value.source.type === "url" && typeof value.source.url === "string") {
22
+ return { type: "image", source: { type: "url", url: value.source.url } };
23
+ }
24
+ if (value.source.type === "base64" && typeof value.source.media_type === "string" && isImageMediaType(value.source.media_type) && typeof value.source.data === "string") {
25
+ return {
26
+ type: "image",
27
+ source: {
28
+ type: "base64",
29
+ media_type: value.source.media_type,
30
+ data: value.source.data
31
+ }
32
+ };
33
+ }
34
+ return null;
35
+ }
36
+ function normalizeDocumentBlock(value) {
37
+ if (value.type !== "document" || !isRecord(value.source)) {
38
+ return null;
39
+ }
40
+ let source = null;
41
+ if (value.source.type === "url" && typeof value.source.url === "string") {
42
+ source = { type: "url", url: value.source.url };
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 };
45
+ } 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 };
47
+ } else if (value.source.type === "content") {
48
+ if (typeof value.source.content === "string") {
49
+ source = { type: "content", content: value.source.content };
50
+ } else if (Array.isArray(value.source.content)) {
51
+ const content = [];
52
+ for (const raw of value.source.content) {
53
+ const block = normalizeTextBlock(raw) ?? normalizeImageBlock(raw);
54
+ if (!block) {
55
+ return null;
56
+ }
57
+ content.push(block);
58
+ }
59
+ source = { type: "content", content };
60
+ }
61
+ }
62
+ if (!source) {
63
+ return null;
64
+ }
65
+ return {
66
+ type: "document",
67
+ source,
68
+ ...typeof value.title === "string" || value.title === null ? { title: value.title } : {},
69
+ ...typeof value.context === "string" || value.context === null ? { context: value.context } : {}
70
+ };
71
+ }
72
+ function normalizeToolResultContentBlock(value) {
73
+ const text = normalizeTextBlock(value);
74
+ if (text) {
75
+ return text;
76
+ }
77
+ const image = normalizeImageBlock(value);
78
+ if (image) {
79
+ return image;
80
+ }
81
+ if (!isRecord(value)) {
82
+ return null;
83
+ }
84
+ if (value.type === "tool_reference" && typeof value.tool_name === "string") {
85
+ return { type: "tool_reference", tool_name: value.tool_name };
86
+ }
87
+ if (value.type === "search_result" && typeof value.source === "string" && typeof value.title === "string" && Array.isArray(value.content)) {
88
+ const content = [];
89
+ for (const raw of value.content) {
90
+ const block = normalizeTextBlock(raw);
91
+ if (!block) {
92
+ return null;
93
+ }
94
+ content.push(block);
95
+ }
96
+ return { type: "search_result", source: value.source, title: value.title, content };
97
+ }
98
+ return normalizeDocumentBlock(value);
99
+ }
100
+ function isToolResultContentBlock(value) {
101
+ return normalizeToolResultContentBlock(value) !== null;
102
+ }
103
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
104
+ ".agents",
105
+ ".git",
106
+ // Git
107
+ ".swifty",
108
+ // Swifty
109
+ ".next",
110
+ // Next.js
111
+ ".venv",
112
+ // Python venv
113
+ ".mypy_cache",
114
+ // Python mypy
115
+ "__pycache__",
116
+ // Python
117
+ "dist",
118
+ // Webpack, Vite
119
+ "node_modules"
120
+ // Node.js
121
+ ]);
122
+
123
+ export {
124
+ normalizeToolResultContentBlock,
125
+ isToolResultContentBlock,
126
+ SKIP_DIRS
127
+ };