cmdr-agent 2.4.0 → 2.5.1

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 (109) hide show
  1. package/dist/bin/cmdr.js +117 -27
  2. package/dist/bin/cmdr.js.map +1 -1
  3. package/dist/package.json +9 -4
  4. package/dist/src/cli/ink/App.d.ts +2 -2
  5. package/dist/src/cli/ink/App.d.ts.map +1 -1
  6. package/dist/src/cli/ink/App.js +118 -65
  7. package/dist/src/cli/ink/App.js.map +1 -1
  8. package/dist/src/cli/ink/PromptInput.d.ts +17 -0
  9. package/dist/src/cli/ink/PromptInput.d.ts.map +1 -0
  10. package/dist/src/cli/ink/PromptInput.js +202 -0
  11. package/dist/src/cli/ink/PromptInput.js.map +1 -0
  12. package/dist/src/cli/ink/StatusBar.d.ts +1 -1
  13. package/dist/src/cli/ink/StatusBar.d.ts.map +1 -1
  14. package/dist/src/cli/ink/StatusBar.js +116 -21
  15. package/dist/src/cli/ink/StatusBar.js.map +1 -1
  16. package/dist/src/cli/ink/input-buffer.d.ts +34 -0
  17. package/dist/src/cli/ink/input-buffer.d.ts.map +1 -0
  18. package/dist/src/cli/ink/input-buffer.js +152 -0
  19. package/dist/src/cli/ink/input-buffer.js.map +1 -0
  20. package/dist/src/cli/progress.d.ts +59 -0
  21. package/dist/src/cli/progress.d.ts.map +1 -0
  22. package/dist/src/cli/progress.js +172 -0
  23. package/dist/src/cli/progress.js.map +1 -0
  24. package/dist/src/cli/renderer.d.ts +14 -3
  25. package/dist/src/cli/renderer.d.ts.map +1 -1
  26. package/dist/src/cli/renderer.js +93 -32
  27. package/dist/src/cli/renderer.js.map +1 -1
  28. package/dist/src/cli/repl.d.ts.map +1 -1
  29. package/dist/src/cli/repl.js +46 -28
  30. package/dist/src/cli/repl.js.map +1 -1
  31. package/dist/src/cli/spinner.d.ts +4 -1
  32. package/dist/src/cli/spinner.d.ts.map +1 -1
  33. package/dist/src/cli/spinner.js +34 -1
  34. package/dist/src/cli/spinner.js.map +1 -1
  35. package/dist/src/cli/theme.d.ts +14 -12
  36. package/dist/src/cli/theme.d.ts.map +1 -1
  37. package/dist/src/cli/theme.js +192 -123
  38. package/dist/src/cli/theme.js.map +1 -1
  39. package/dist/src/cli/themes.d.ts +17 -1
  40. package/dist/src/cli/themes.d.ts.map +1 -1
  41. package/dist/src/cli/themes.js +125 -27
  42. package/dist/src/cli/themes.js.map +1 -1
  43. package/dist/src/core/agent-runner.d.ts +12 -0
  44. package/dist/src/core/agent-runner.d.ts.map +1 -1
  45. package/dist/src/core/agent-runner.js +176 -5
  46. package/dist/src/core/agent-runner.js.map +1 -1
  47. package/dist/src/core/event-bus.d.ts +12 -0
  48. package/dist/src/core/event-bus.d.ts.map +1 -1
  49. package/dist/src/core/event-bus.js.map +1 -1
  50. package/dist/src/core/presets.d.ts.map +1 -1
  51. package/dist/src/core/presets.js +1 -0
  52. package/dist/src/core/presets.js.map +1 -1
  53. package/dist/src/core/types.d.ts +17 -0
  54. package/dist/src/core/types.d.ts.map +1 -1
  55. package/dist/src/core/types.js +8 -1
  56. package/dist/src/core/types.js.map +1 -1
  57. package/dist/src/llm/anthropic.d.ts +1 -0
  58. package/dist/src/llm/anthropic.d.ts.map +1 -1
  59. package/dist/src/llm/anthropic.js +20 -1
  60. package/dist/src/llm/anthropic.js.map +1 -1
  61. package/dist/src/llm/ollama.d.ts +2 -12
  62. package/dist/src/llm/ollama.d.ts.map +1 -1
  63. package/dist/src/llm/ollama.js +46 -250
  64. package/dist/src/llm/ollama.js.map +1 -1
  65. package/dist/src/llm/openai.d.ts +1 -0
  66. package/dist/src/llm/openai.d.ts.map +1 -1
  67. package/dist/src/llm/openai.js +21 -7
  68. package/dist/src/llm/openai.js.map +1 -1
  69. package/dist/src/llm/repair/retry-policy.d.ts +48 -0
  70. package/dist/src/llm/repair/retry-policy.d.ts.map +1 -0
  71. package/dist/src/llm/repair/retry-policy.js +85 -0
  72. package/dist/src/llm/repair/retry-policy.js.map +1 -0
  73. package/dist/src/llm/repair/tool-repair.d.ts +39 -0
  74. package/dist/src/llm/repair/tool-repair.d.ts.map +1 -0
  75. package/dist/src/llm/repair/tool-repair.js +313 -0
  76. package/dist/src/llm/repair/tool-repair.js.map +1 -0
  77. package/dist/src/llm/shared/text-cleanup.d.ts +16 -0
  78. package/dist/src/llm/shared/text-cleanup.d.ts.map +1 -0
  79. package/dist/src/llm/shared/text-cleanup.js +120 -0
  80. package/dist/src/llm/shared/text-cleanup.js.map +1 -0
  81. package/dist/src/llm/shared/tool-parsing.d.ts +70 -0
  82. package/dist/src/llm/shared/tool-parsing.d.ts.map +1 -0
  83. package/dist/src/llm/shared/tool-parsing.js +355 -0
  84. package/dist/src/llm/shared/tool-parsing.js.map +1 -0
  85. package/dist/src/llm/validation/tool-call-schema.d.ts +83 -0
  86. package/dist/src/llm/validation/tool-call-schema.d.ts.map +1 -0
  87. package/dist/src/llm/validation/tool-call-schema.js +145 -0
  88. package/dist/src/llm/validation/tool-call-schema.js.map +1 -0
  89. package/dist/src/tools/built-in/bash.d.ts +5 -1
  90. package/dist/src/tools/built-in/bash.d.ts.map +1 -1
  91. package/dist/src/tools/built-in/bash.js +34 -46
  92. package/dist/src/tools/built-in/bash.js.map +1 -1
  93. package/dist/src/tools/built-in/index.d.ts +2 -1
  94. package/dist/src/tools/built-in/index.d.ts.map +1 -1
  95. package/dist/src/tools/built-in/index.js +3 -1
  96. package/dist/src/tools/built-in/index.js.map +1 -1
  97. package/dist/src/tools/built-in/pdf-report.d.ts +39 -0
  98. package/dist/src/tools/built-in/pdf-report.d.ts.map +1 -0
  99. package/dist/src/tools/built-in/pdf-report.js +326 -0
  100. package/dist/src/tools/built-in/pdf-report.js.map +1 -0
  101. package/dist/src/tools/executor.d.ts +7 -1
  102. package/dist/src/tools/executor.d.ts.map +1 -1
  103. package/dist/src/tools/executor.js +20 -5
  104. package/dist/src/tools/executor.js.map +1 -1
  105. package/dist/src/tools/shell/shell-executor.d.ts +71 -0
  106. package/dist/src/tools/shell/shell-executor.d.ts.map +1 -0
  107. package/dist/src/tools/shell/shell-executor.js +238 -0
  108. package/dist/src/tools/shell/shell-executor.js.map +1 -0
  109. package/package.json +9 -4
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Tool repair module — attempts to fix malformed tool calls before giving up.
3
+ *
4
+ * Strategies (applied in order):
5
+ * A. Strip surrounding text, re-extract JSON
6
+ * B. Fix common JSON issues (trailing commas, unquoted keys, partial brackets)
7
+ * C. Fuzzy-match partial tool names to closest registered tool
8
+ * D. Coerce argument types ("true" → true, "42" → 42) per Zod schema
9
+ *
10
+ * If any strategy produces a valid tool call, it's returned immediately.
11
+ * If none succeed, null is returned → caller should retry or give up.
12
+ */
13
+ import { validateToolCallShape } from '../validation/tool-call-schema.js';
14
+ /**
15
+ * Attempt to repair a set of rejected tool calls.
16
+ *
17
+ * @param rawText The full LLM output text that contained the failed tool calls.
18
+ * @param rejected The tool calls that failed validation, with error details.
19
+ * @param context Available tool names and schemas for fuzzy matching + type coercion.
20
+ * @returns Repaired tool calls, or null if repair failed entirely.
21
+ */
22
+ export function attemptRepair(rawText, rejected, context) {
23
+ const repaired = [];
24
+ const fixes = [];
25
+ for (const call of rejected) {
26
+ // Strategy C: Fuzzy-match tool name
27
+ let toolName = call.name;
28
+ const nameFixed = fuzzyMatchToolName(toolName, context.availableTools);
29
+ if (nameFixed && nameFixed !== toolName) {
30
+ fixes.push(`Fixed tool name: "${toolName}" → "${nameFixed}"`);
31
+ toolName = nameFixed;
32
+ }
33
+ // Strategy D: Type coercion per Zod schema
34
+ let args = { ...call.arguments };
35
+ const schema = context.toolSchemas.get(toolName);
36
+ if (schema) {
37
+ const coerced = coerceArgumentTypes(args, schema);
38
+ if (coerced.fixed) {
39
+ args = coerced.args;
40
+ fixes.push(...coerced.fixes);
41
+ }
42
+ }
43
+ // Validate after fixes
44
+ const validation = validateToolCallShape({ name: toolName, arguments: args });
45
+ if (validation.ok) {
46
+ // Double-check input schema if available
47
+ if (schema) {
48
+ const result = schema.safeParse(args);
49
+ if (result.success) {
50
+ repaired.push({ name: toolName, arguments: result.data });
51
+ continue;
52
+ }
53
+ }
54
+ else {
55
+ repaired.push(validation.toolCall);
56
+ continue;
57
+ }
58
+ }
59
+ }
60
+ // Strategy A+B: Re-extract from raw text if per-call repair failed
61
+ if (repaired.length < rejected.length && rawText) {
62
+ const extracted = extractFromRawText(rawText, context);
63
+ if (extracted.length > 0) {
64
+ fixes.push(`Re-extracted ${extracted.length} tool call(s) from raw output`);
65
+ for (const tc of extracted) {
66
+ if (!repaired.some(r => r.name === tc.name)) {
67
+ repaired.push(tc);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return repaired.length > 0 ? { repaired, fixes } : null;
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // Strategy A: Re-extract JSON from raw text
76
+ // ---------------------------------------------------------------------------
77
+ function extractFromRawText(text, context) {
78
+ const results = [];
79
+ // Strip markdown code fences and try to find JSON objects
80
+ const stripped = text
81
+ .replace(/```(?:json|tool_call)?\s*\n?/g, '')
82
+ .replace(/```/g, '');
83
+ // Find all JSON-like objects in the text
84
+ const jsonRegex = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
85
+ let match;
86
+ while ((match = jsonRegex.exec(stripped)) !== null) {
87
+ const candidate = match[0];
88
+ const parsed = tryParseAndRepairJson(candidate);
89
+ if (parsed && parsed.name && typeof parsed.name === 'string') {
90
+ const args = (parsed.arguments ?? parsed.args ?? parsed.params ?? parsed.input ?? {});
91
+ const toolName = fuzzyMatchToolName(parsed.name, context.availableTools) ?? parsed.name;
92
+ const validation = validateToolCallShape({ name: toolName, arguments: args });
93
+ if (validation.ok) {
94
+ results.push(validation.toolCall);
95
+ }
96
+ }
97
+ }
98
+ return results;
99
+ }
100
+ // ---------------------------------------------------------------------------
101
+ // Strategy B: Fix common JSON issues
102
+ // ---------------------------------------------------------------------------
103
+ function tryParseAndRepairJson(text) {
104
+ // Try direct parse first
105
+ try {
106
+ const parsed = JSON.parse(text);
107
+ if (typeof parsed === 'object' && parsed !== null)
108
+ return parsed;
109
+ }
110
+ catch {
111
+ // Fall through to repairs
112
+ }
113
+ let repaired = text;
114
+ // Fix trailing commas: { "a": 1, }
115
+ repaired = repaired.replace(/,\s*([}\]])/g, '$1');
116
+ // Fix unquoted keys: { name: "value" } → { "name": "value" }
117
+ repaired = repaired.replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":');
118
+ // Fix single-quoted strings: { 'name': 'value' } → { "name": "value" }
119
+ repaired = repaired.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
120
+ // Fix missing closing bracket
121
+ const openBraces = (repaired.match(/\{/g) || []).length;
122
+ const closeBraces = (repaired.match(/\}/g) || []).length;
123
+ if (openBraces > closeBraces) {
124
+ repaired += '}'.repeat(openBraces - closeBraces);
125
+ }
126
+ // Fix missing closing square bracket in arrays
127
+ const openBrackets = (repaired.match(/\[/g) || []).length;
128
+ const closeBrackets = (repaired.match(/\]/g) || []).length;
129
+ if (openBrackets > closeBrackets) {
130
+ // Insert before last }
131
+ const lastBrace = repaired.lastIndexOf('}');
132
+ if (lastBrace > 0) {
133
+ repaired = repaired.slice(0, lastBrace) + ']'.repeat(openBrackets - closeBrackets) + repaired.slice(lastBrace);
134
+ }
135
+ }
136
+ try {
137
+ const parsed = JSON.parse(repaired);
138
+ if (typeof parsed === 'object' && parsed !== null)
139
+ return parsed;
140
+ }
141
+ catch {
142
+ // Could not repair
143
+ }
144
+ return null;
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Strategy C: Fuzzy tool name matching
148
+ // ---------------------------------------------------------------------------
149
+ /**
150
+ * Find the closest registered tool name for a possibly misspelled/partial name.
151
+ * Uses: exact match → case-insensitive → prefix match → Levenshtein distance.
152
+ */
153
+ function fuzzyMatchToolName(name, available) {
154
+ if (!name || available.length === 0)
155
+ return null;
156
+ // Exact match
157
+ if (available.includes(name))
158
+ return name;
159
+ // Case-insensitive match
160
+ const lower = name.toLowerCase();
161
+ const ciMatch = available.find(t => t.toLowerCase() === lower);
162
+ if (ciMatch)
163
+ return ciMatch;
164
+ // Prefix match (e.g., "file_wr" → "file_write")
165
+ const prefixMatches = available.filter(t => t.toLowerCase().startsWith(lower));
166
+ if (prefixMatches.length === 1)
167
+ return prefixMatches[0];
168
+ // Contains match (e.g., "write" → "file_write")
169
+ const containsMatches = available.filter(t => t.toLowerCase().includes(lower));
170
+ if (containsMatches.length === 1)
171
+ return containsMatches[0];
172
+ // Levenshtein distance (max distance 3 to avoid wild matches)
173
+ let bestMatch = null;
174
+ let bestDist = 4;
175
+ for (const tool of available) {
176
+ const dist = levenshtein(lower, tool.toLowerCase());
177
+ if (dist < bestDist) {
178
+ bestDist = dist;
179
+ bestMatch = tool;
180
+ }
181
+ }
182
+ return bestMatch;
183
+ }
184
+ function levenshtein(a, b) {
185
+ const m = a.length;
186
+ const n = b.length;
187
+ // Use single-row optimization for memory efficiency
188
+ const row = Array.from({ length: n + 1 }, (_, i) => i);
189
+ for (let i = 1; i <= m; i++) {
190
+ let prev = i - 1;
191
+ row[0] = i;
192
+ for (let j = 1; j <= n; j++) {
193
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
194
+ const val = Math.min(row[j] + 1, // deletion
195
+ row[j - 1] + 1, // insertion
196
+ prev + cost);
197
+ prev = row[j];
198
+ row[j] = val;
199
+ }
200
+ }
201
+ return row[n];
202
+ }
203
+ /**
204
+ * Coerce argument types to match the tool's Zod schema.
205
+ * Handles: string "true" → boolean, "42" → number, "null" → null.
206
+ */
207
+ function coerceArgumentTypes(args, schema) {
208
+ const result = { ...args };
209
+ const fixes = [];
210
+ let fixed = false;
211
+ // Extract shape from ZodObject if possible
212
+ const shape = getZodObjectShape(schema);
213
+ if (!shape)
214
+ return { args, fixed: false, fixes };
215
+ for (const [key, value] of Object.entries(args)) {
216
+ const fieldSchema = shape[key];
217
+ if (!fieldSchema)
218
+ continue;
219
+ const expectedType = getZodBaseType(fieldSchema);
220
+ if (!expectedType)
221
+ continue;
222
+ // String → boolean coercion
223
+ if (expectedType === 'boolean' && typeof value === 'string') {
224
+ const lower = value.toLowerCase();
225
+ if (lower === 'true' || lower === 'yes' || lower === '1') {
226
+ result[key] = true;
227
+ fixes.push(`Coerced "${key}": "${value}" → true`);
228
+ fixed = true;
229
+ }
230
+ else if (lower === 'false' || lower === 'no' || lower === '0') {
231
+ result[key] = false;
232
+ fixes.push(`Coerced "${key}": "${value}" → false`);
233
+ fixed = true;
234
+ }
235
+ }
236
+ // String → number coercion
237
+ if (expectedType === 'number' && typeof value === 'string') {
238
+ const num = Number(value);
239
+ if (!isNaN(num) && value.trim() !== '') {
240
+ result[key] = num;
241
+ fixes.push(`Coerced "${key}": "${value}" → ${num}`);
242
+ fixed = true;
243
+ }
244
+ }
245
+ // String → null coercion
246
+ if (expectedType === 'null' && typeof value === 'string') {
247
+ if (value.toLowerCase() === 'null' || value === '') {
248
+ result[key] = null;
249
+ fixes.push(`Coerced "${key}": "${value}" → null`);
250
+ fixed = true;
251
+ }
252
+ }
253
+ // Array string → array coercion (e.g. "[1,2,3]" → [1,2,3])
254
+ if (expectedType === 'array' && typeof value === 'string') {
255
+ try {
256
+ const parsed = JSON.parse(value);
257
+ if (Array.isArray(parsed)) {
258
+ result[key] = parsed;
259
+ fixes.push(`Coerced "${key}": string → array`);
260
+ fixed = true;
261
+ }
262
+ }
263
+ catch {
264
+ // Not valid JSON array
265
+ }
266
+ }
267
+ }
268
+ return { args: result, fixed, fixes };
269
+ }
270
+ // ---------------------------------------------------------------------------
271
+ // Zod introspection helpers
272
+ // ---------------------------------------------------------------------------
273
+ function getZodObjectShape(schema) {
274
+ try {
275
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
276
+ const def = schema?._def;
277
+ if (def?.typeName === 'ZodObject') {
278
+ return def.shape();
279
+ }
280
+ // Handle ZodEffects wrapping (e.g., .transform(), .refine())
281
+ if (def?.typeName === 'ZodEffects' && def?.schema) {
282
+ return getZodObjectShape(def.schema);
283
+ }
284
+ }
285
+ catch {
286
+ // Schema introspection failed
287
+ }
288
+ return null;
289
+ }
290
+ function getZodBaseType(schema) {
291
+ try {
292
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
293
+ const def = schema?._def;
294
+ const typeName = def?.typeName;
295
+ if (!typeName)
296
+ return null;
297
+ switch (typeName) {
298
+ case 'ZodString': return 'string';
299
+ case 'ZodNumber': return 'number';
300
+ case 'ZodBoolean': return 'boolean';
301
+ case 'ZodNull': return 'null';
302
+ case 'ZodArray': return 'array';
303
+ case 'ZodOptional': return getZodBaseType(def.innerType);
304
+ case 'ZodDefault': return getZodBaseType(def.innerType);
305
+ case 'ZodNullable': return getZodBaseType(def.innerType);
306
+ default: return null;
307
+ }
308
+ }
309
+ catch {
310
+ return null;
311
+ }
312
+ }
313
+ //# sourceMappingURL=tool-repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-repair.js","sourceRoot":"","sources":["../../../../src/llm/repair/tool-repair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Pre-parse text cleanup — sanitizes raw LLM output before tool call parsing.
3
+ *
4
+ * Fixes common model artifacts that break JSON/XML parsing:
5
+ * - Markdown code fence wrappers around JSON
6
+ * - Thinking/reasoning tags (<think>, <thought>, etc.)
7
+ * - Control characters
8
+ * - Repeated newlines in JSON
9
+ * - Incomplete/truncated brackets
10
+ */
11
+ /**
12
+ * Clean raw LLM output text before passing it to tool call parsers.
13
+ * This is a pure function that returns sanitized text — idempotent and safe.
14
+ */
15
+ export declare function cleanLLMOutput(text: string): string;
16
+ //# sourceMappingURL=text-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-cleanup.d.ts","sourceRoot":"","sources":["../../../../src/llm/shared/text-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBnD"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Pre-parse text cleanup — sanitizes raw LLM output before tool call parsing.
3
+ *
4
+ * Fixes common model artifacts that break JSON/XML parsing:
5
+ * - Markdown code fence wrappers around JSON
6
+ * - Thinking/reasoning tags (<think>, <thought>, etc.)
7
+ * - Control characters
8
+ * - Repeated newlines in JSON
9
+ * - Incomplete/truncated brackets
10
+ */
11
+ // ---------------------------------------------------------------------------
12
+ // Main cleanup function
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Clean raw LLM output text before passing it to tool call parsers.
16
+ * This is a pure function that returns sanitized text — idempotent and safe.
17
+ */
18
+ export function cleanLLMOutput(text) {
19
+ if (!text)
20
+ return text;
21
+ let cleaned = text;
22
+ // 1. Strip thinking/reasoning tags and their content
23
+ cleaned = stripThinkingTags(cleaned);
24
+ // 2. Strip control characters (except newline, tab, carriage return)
25
+ cleaned = stripControlChars(cleaned);
26
+ // 3. Unwrap bare markdown JSON fences that wrap the entire response
27
+ // (but NOT tool_call fences — those are handled by the parser)
28
+ cleaned = unwrapBareJsonFences(cleaned);
29
+ // 4. Collapse excessive whitespace inside JSON-like structures
30
+ cleaned = collapseJsonWhitespace(cleaned);
31
+ return cleaned;
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // Individual cleanup strategies
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Strip thinking/reasoning tags and their content.
38
+ * Models like Qwen3, DeepSeek emit <think>...</think> blocks.
39
+ */
40
+ function stripThinkingTags(text) {
41
+ // <think>...</think>
42
+ let cleaned = text.replace(/<think>[\s\S]*?<\/think>/gi, '');
43
+ // <thought>...</thought>
44
+ cleaned = cleaned.replace(/<thought>[\s\S]*?<\/thought>/gi, '');
45
+ // <reasoning>...</reasoning>
46
+ cleaned = cleaned.replace(/<reasoning>[\s\S]*?<\/reasoning>/gi, '');
47
+ // <reflection>...</reflection>
48
+ cleaned = cleaned.replace(/<reflection>[\s\S]*?<\/reflection>/gi, '');
49
+ // Ollama-specific thinking channel markers
50
+ cleaned = cleaned.replace(/<\|channel>thought[\s\S]*?<channel\|>/g, '');
51
+ cleaned = cleaned.replace(/<channel\|>/g, '');
52
+ return cleaned.trim();
53
+ }
54
+ /**
55
+ * Strip control characters that break JSON parsing.
56
+ * Preserves: \n (0x0A), \r (0x0D), \t (0x09)
57
+ */
58
+ function stripControlChars(text) {
59
+ // eslint-disable-next-line no-control-regex
60
+ return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
61
+ }
62
+ /**
63
+ * Unwrap bare JSON code fences that wrap the entire response.
64
+ *
65
+ * Some models wrap their entire tool call in ```json ... ``` instead of
66
+ * using the proper ```tool_call format. We unwrap these to expose the
67
+ * raw JSON for the parser.
68
+ *
69
+ * Does NOT unwrap ```tool_call fences — those are handled by the parser.
70
+ */
71
+ function unwrapBareJsonFences(text) {
72
+ const trimmed = text.trim();
73
+ // Check if the entire response is a single ```json fence
74
+ const jsonFenceMatch = trimmed.match(/^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/);
75
+ if (jsonFenceMatch) {
76
+ const inner = jsonFenceMatch[1].trim();
77
+ // Only unwrap if the inner content looks like a tool call JSON
78
+ if (isToolCallLike(inner)) {
79
+ // Re-wrap as ```tool_call so the parser picks it up
80
+ return '```tool_call\n' + inner + '\n```';
81
+ }
82
+ }
83
+ return text;
84
+ }
85
+ /**
86
+ * Collapse excessive whitespace inside JSON structures.
87
+ * Some models emit JSON with many blank lines between keys.
88
+ */
89
+ function collapseJsonWhitespace(text) {
90
+ // Only apply inside ```tool_call blocks to avoid mangling prose
91
+ return text.replace(/```tool_call\s*\n([\s\S]*?)\n```/g, (_match, inner) => {
92
+ const collapsed = inner
93
+ .split('\n')
94
+ .map((line) => line.trimEnd())
95
+ .filter((line, i, arr) => {
96
+ // Remove blank lines between JSON lines, but keep at least one
97
+ if (line === '' && i > 0 && arr[i - 1] === '')
98
+ return false;
99
+ return true;
100
+ })
101
+ .join('\n');
102
+ return '```tool_call\n' + collapsed + '\n```';
103
+ });
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // Helpers
107
+ // ---------------------------------------------------------------------------
108
+ /**
109
+ * Heuristic: does this text look like a tool call JSON object?
110
+ */
111
+ function isToolCallLike(text) {
112
+ const trimmed = text.trim();
113
+ if (!trimmed.startsWith('{'))
114
+ return false;
115
+ // Check for tool call shape indicators
116
+ return ((trimmed.includes('"name"') && trimmed.includes('"arguments"')) ||
117
+ (trimmed.includes('"name"') && trimmed.includes('"args"')) ||
118
+ (trimmed.includes('"function"') && trimmed.includes('"arguments"')));
119
+ }
120
+ //# sourceMappingURL=text-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-cleanup.js","sourceRoot":"","sources":["../../../../src/llm/shared/text-cleanup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Unified tool call parser for all LLM adapters.
3
+ *
4
+ * Consolidates the tool parsing logic that was duplicated across
5
+ * ollama.ts, openai.ts, and anthropic.ts into a single, well-tested module.
6
+ *
7
+ * Supports:
8
+ * - Native API tool calls (Ollama, OpenAI, Anthropic)
9
+ * - Text-based JSON format: ```tool_call\n{JSON}\n```
10
+ * - XML function format: <function=name><parameter=key>value</parameter></function>
11
+ * - Invoke format: <invoke name="..."><parameter name="...">value</parameter></invoke>
12
+ * - MiniMax wrapper: <minimax:tool_call>...</minimax:tool_call>
13
+ *
14
+ * Each strategy runs in order and returns on first match (waterfall).
15
+ */
16
+ import type { ContentBlock } from '../../core/types.js';
17
+ export interface ToolParseConfig {
18
+ /** Enable JSON ```tool_call``` format parsing. Default: true */
19
+ jsonToolFormat?: boolean;
20
+ /** Enable XML <function=name> format parsing. Default: true */
21
+ xmlToolFormat?: boolean;
22
+ /** Enable <invoke> tag format parsing. Default: true */
23
+ invokeFormat?: boolean;
24
+ }
25
+ export interface NativeToolCall {
26
+ function: {
27
+ name: string;
28
+ arguments: Record<string, unknown> | string;
29
+ };
30
+ id?: string;
31
+ }
32
+ /**
33
+ * Three-stage tool resolution waterfall.
34
+ * This is the main entry point for all adapters.
35
+ *
36
+ * 1. Native tool_calls from API (highest confidence)
37
+ * 2. Text-based JSON format
38
+ * 3. Text-based XML format
39
+ *
40
+ * Returns an array of ContentBlocks (text + tool_use mixed).
41
+ */
42
+ export declare function resolveToolCalls(nativeToolCalls: NativeToolCall[] | null | undefined, textContent: string, config?: ToolParseConfig, hasTools?: boolean): ContentBlock[];
43
+ /**
44
+ * Parse tool calls from text output using multiple strategies.
45
+ * Returns ContentBlock[] with interleaved text and tool_use blocks.
46
+ */
47
+ export declare function parseTextToolCalls(text: string, config?: ToolParseConfig): ContentBlock[];
48
+ /**
49
+ * Parse multiple JSON objects from a block that may contain:
50
+ * - A single JSON object
51
+ * - A JSON array
52
+ * - One object per line
53
+ * - Comma-separated objects
54
+ */
55
+ export declare function parseMultipleJsonObjects(block: string): Array<{
56
+ name: string;
57
+ arguments: Record<string, unknown>;
58
+ }>;
59
+ /**
60
+ * Build the tool-call instruction suffix to inject into the system prompt
61
+ * for models that don't support native tool calling.
62
+ */
63
+ export declare function buildToolPromptSuffix(tools: readonly {
64
+ name: string;
65
+ description: string;
66
+ inputSchema: Record<string, unknown>;
67
+ }[], options?: {
68
+ strict?: boolean;
69
+ }): string;
70
+ //# sourceMappingURL=tool-parsing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-parsing.d.ts","sourceRoot":"","sources":["../../../../src/llm/shared/tool-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,qBAAqB,CAAA;AAQhF,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,wDAAwD;IACxD,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAA;KAC5C,CAAA;IACD,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAuBD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,cAAc,EAAE,GAAG,IAAI,GAAG,SAAS,EACpD,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,eAAe,EACxB,QAAQ,CAAC,EAAE,OAAO,GACjB,YAAY,EAAE,CA6ChB;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,eAAe,GACvB,YAAY,EAAE,CAgChB;AA8JD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA+C7D;AAkBD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,EAC7F,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,MAAM,CA0BR"}