cmdr-agent 2.3.0 → 2.5.0

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 (95) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/cli/progress.d.ts +58 -0
  3. package/dist/src/cli/progress.d.ts.map +1 -0
  4. package/dist/src/cli/progress.js +179 -0
  5. package/dist/src/cli/progress.js.map +1 -0
  6. package/dist/src/cli/repl.d.ts.map +1 -1
  7. package/dist/src/cli/repl.js +56 -0
  8. package/dist/src/cli/repl.js.map +1 -1
  9. package/dist/src/cli/spinner.d.ts +2 -0
  10. package/dist/src/cli/spinner.d.ts.map +1 -1
  11. package/dist/src/cli/spinner.js +18 -0
  12. package/dist/src/cli/spinner.js.map +1 -1
  13. package/dist/src/config/mcp-config.d.ts +41 -0
  14. package/dist/src/config/mcp-config.d.ts.map +1 -0
  15. package/dist/src/config/mcp-config.js +55 -0
  16. package/dist/src/config/mcp-config.js.map +1 -0
  17. package/dist/src/core/agent-runner.d.ts +16 -0
  18. package/dist/src/core/agent-runner.d.ts.map +1 -1
  19. package/dist/src/core/agent-runner.js +213 -5
  20. package/dist/src/core/agent-runner.js.map +1 -1
  21. package/dist/src/core/agent.d.ts +4 -0
  22. package/dist/src/core/agent.d.ts.map +1 -1
  23. package/dist/src/core/agent.js +6 -0
  24. package/dist/src/core/agent.js.map +1 -1
  25. package/dist/src/core/event-bus.d.ts +12 -0
  26. package/dist/src/core/event-bus.d.ts.map +1 -1
  27. package/dist/src/core/event-bus.js.map +1 -1
  28. package/dist/src/core/graph-context.d.ts +83 -0
  29. package/dist/src/core/graph-context.d.ts.map +1 -0
  30. package/dist/src/core/graph-context.js +370 -0
  31. package/dist/src/core/graph-context.js.map +1 -0
  32. package/dist/src/core/intent.d.ts.map +1 -1
  33. package/dist/src/core/intent.js +3 -0
  34. package/dist/src/core/intent.js.map +1 -1
  35. package/dist/src/core/presets.d.ts.map +1 -1
  36. package/dist/src/core/presets.js +6 -2
  37. package/dist/src/core/presets.js.map +1 -1
  38. package/dist/src/core/types.d.ts +19 -0
  39. package/dist/src/core/types.d.ts.map +1 -1
  40. package/dist/src/core/types.js +8 -1
  41. package/dist/src/core/types.js.map +1 -1
  42. package/dist/src/llm/anthropic.d.ts +1 -0
  43. package/dist/src/llm/anthropic.d.ts.map +1 -1
  44. package/dist/src/llm/anthropic.js +20 -1
  45. package/dist/src/llm/anthropic.js.map +1 -1
  46. package/dist/src/llm/ollama.d.ts +2 -12
  47. package/dist/src/llm/ollama.d.ts.map +1 -1
  48. package/dist/src/llm/ollama.js +46 -250
  49. package/dist/src/llm/ollama.js.map +1 -1
  50. package/dist/src/llm/openai.d.ts +1 -0
  51. package/dist/src/llm/openai.d.ts.map +1 -1
  52. package/dist/src/llm/openai.js +21 -7
  53. package/dist/src/llm/openai.js.map +1 -1
  54. package/dist/src/llm/repair/retry-policy.d.ts +48 -0
  55. package/dist/src/llm/repair/retry-policy.d.ts.map +1 -0
  56. package/dist/src/llm/repair/retry-policy.js +85 -0
  57. package/dist/src/llm/repair/retry-policy.js.map +1 -0
  58. package/dist/src/llm/repair/tool-repair.d.ts +39 -0
  59. package/dist/src/llm/repair/tool-repair.d.ts.map +1 -0
  60. package/dist/src/llm/repair/tool-repair.js +313 -0
  61. package/dist/src/llm/repair/tool-repair.js.map +1 -0
  62. package/dist/src/llm/shared/text-cleanup.d.ts +16 -0
  63. package/dist/src/llm/shared/text-cleanup.d.ts.map +1 -0
  64. package/dist/src/llm/shared/text-cleanup.js +120 -0
  65. package/dist/src/llm/shared/text-cleanup.js.map +1 -0
  66. package/dist/src/llm/shared/tool-parsing.d.ts +70 -0
  67. package/dist/src/llm/shared/tool-parsing.d.ts.map +1 -0
  68. package/dist/src/llm/shared/tool-parsing.js +355 -0
  69. package/dist/src/llm/shared/tool-parsing.js.map +1 -0
  70. package/dist/src/llm/validation/tool-call-schema.d.ts +83 -0
  71. package/dist/src/llm/validation/tool-call-schema.d.ts.map +1 -0
  72. package/dist/src/llm/validation/tool-call-schema.js +145 -0
  73. package/dist/src/llm/validation/tool-call-schema.js.map +1 -0
  74. package/dist/src/tools/built-in/bash.d.ts +6 -2
  75. package/dist/src/tools/built-in/bash.d.ts.map +1 -1
  76. package/dist/src/tools/built-in/bash.js +34 -46
  77. package/dist/src/tools/built-in/bash.js.map +1 -1
  78. package/dist/src/tools/built-in/graph-tools.d.ts +31 -0
  79. package/dist/src/tools/built-in/graph-tools.d.ts.map +1 -0
  80. package/dist/src/tools/built-in/graph-tools.js +103 -0
  81. package/dist/src/tools/built-in/graph-tools.js.map +1 -0
  82. package/dist/src/tools/built-in/index.d.ts +2 -1
  83. package/dist/src/tools/built-in/index.d.ts.map +1 -1
  84. package/dist/src/tools/built-in/index.js +3 -1
  85. package/dist/src/tools/built-in/index.js.map +1 -1
  86. package/dist/src/tools/built-in/web-fetch.d.ts +1 -1
  87. package/dist/src/tools/executor.d.ts +7 -1
  88. package/dist/src/tools/executor.d.ts.map +1 -1
  89. package/dist/src/tools/executor.js +20 -5
  90. package/dist/src/tools/executor.js.map +1 -1
  91. package/dist/src/tools/shell/shell-executor.d.ts +71 -0
  92. package/dist/src/tools/shell/shell-executor.d.ts.map +1 -0
  93. package/dist/src/tools/shell/shell-executor.js +238 -0
  94. package/dist/src/tools/shell/shell-executor.js.map +1 -0
  95. package/package.json +1 -1
@@ -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"}
@@ -0,0 +1,355 @@
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 { validateToolCallShape } from '../validation/tool-call-schema.js';
17
+ import { cleanLLMOutput } from './text-cleanup.js';
18
+ const DEFAULT_CONFIG = {
19
+ jsonToolFormat: true,
20
+ xmlToolFormat: true,
21
+ invokeFormat: true,
22
+ };
23
+ // ---------------------------------------------------------------------------
24
+ // ID generation
25
+ // ---------------------------------------------------------------------------
26
+ let _idCounter = 0;
27
+ function generateToolId() {
28
+ _idCounter++;
29
+ return `tool_${Date.now()}_${_idCounter.toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // Unified resolution: native → text waterfall
33
+ // ---------------------------------------------------------------------------
34
+ /**
35
+ * Three-stage tool resolution waterfall.
36
+ * This is the main entry point for all adapters.
37
+ *
38
+ * 1. Native tool_calls from API (highest confidence)
39
+ * 2. Text-based JSON format
40
+ * 3. Text-based XML format
41
+ *
42
+ * Returns an array of ContentBlocks (text + tool_use mixed).
43
+ */
44
+ export function resolveToolCalls(nativeToolCalls, textContent, config, hasTools) {
45
+ const cfg = { ...DEFAULT_CONFIG, ...config };
46
+ // Stage 1: Native tool_calls from API response
47
+ if (nativeToolCalls && nativeToolCalls.length > 0) {
48
+ const content = [];
49
+ if (textContent) {
50
+ content.push({ type: 'text', text: textContent });
51
+ }
52
+ for (const tc of nativeToolCalls) {
53
+ const args = typeof tc.function.arguments === 'string'
54
+ ? safeParseJson(tc.function.arguments)
55
+ : tc.function.arguments;
56
+ const validation = validateToolCallShape({
57
+ name: tc.function.name,
58
+ arguments: args,
59
+ });
60
+ if (validation.ok) {
61
+ content.push({
62
+ type: 'tool_use',
63
+ id: tc.id ?? generateToolId(),
64
+ name: validation.toolCall.name,
65
+ input: validation.toolCall.arguments,
66
+ });
67
+ }
68
+ }
69
+ return content;
70
+ }
71
+ // Stage 0: Pre-parse cleanup for text-based parsing
72
+ const cleanedText = hasTools && textContent ? cleanLLMOutput(textContent) : textContent;
73
+ // Stage 2+3: Parse text-based tool calls only if tools are available
74
+ if (hasTools && cleanedText) {
75
+ const parsed = parseTextToolCalls(cleanedText, cfg);
76
+ if (parsed.length > 0)
77
+ return parsed;
78
+ }
79
+ // No tool calls — return as pure text (use original, uncleaned text for display)
80
+ if (textContent) {
81
+ return [{ type: 'text', text: textContent }];
82
+ }
83
+ return [];
84
+ }
85
+ // ---------------------------------------------------------------------------
86
+ // Text-based tool call extraction (all strategies)
87
+ // ---------------------------------------------------------------------------
88
+ /**
89
+ * Parse tool calls from text output using multiple strategies.
90
+ * Returns ContentBlock[] with interleaved text and tool_use blocks.
91
+ */
92
+ export function parseTextToolCalls(text, config) {
93
+ const cfg = { ...DEFAULT_CONFIG, ...config };
94
+ // Strategy 1: ```tool_call JSON format
95
+ if (cfg.jsonToolFormat) {
96
+ const result = parseJsonToolCalls(text);
97
+ if (result.length > 0 && result.some(b => b.type === 'tool_use')) {
98
+ return result;
99
+ }
100
+ }
101
+ // Strategy 2: <function=name> XML format
102
+ if (cfg.xmlToolFormat) {
103
+ const result = parseXmlFunctionCalls(text);
104
+ if (result.length > 0 && result.some(b => b.type === 'tool_use')) {
105
+ return result;
106
+ }
107
+ }
108
+ // Strategy 3: <invoke name="..."> format (MiniMax, etc.)
109
+ if (cfg.invokeFormat) {
110
+ const result = parseInvokeCalls(text);
111
+ if (result.length > 0 && result.some(b => b.type === 'tool_use')) {
112
+ return result;
113
+ }
114
+ }
115
+ // No tool calls found
116
+ if (text) {
117
+ return [{ type: 'text', text }];
118
+ }
119
+ return [];
120
+ }
121
+ // ---------------------------------------------------------------------------
122
+ // Strategy 1: JSON ```tool_call``` format
123
+ // ---------------------------------------------------------------------------
124
+ function parseJsonToolCalls(text) {
125
+ const content = [];
126
+ const toolCallRegex = /```tool_call\s*\n([\s\S]*?)\n```/g;
127
+ let lastIndex = 0;
128
+ let match;
129
+ let foundToolCalls = false;
130
+ while ((match = toolCallRegex.exec(text)) !== null) {
131
+ foundToolCalls = true;
132
+ // Add preceding text
133
+ if (match.index > lastIndex) {
134
+ const before = text.slice(lastIndex, match.index).trim();
135
+ if (before)
136
+ content.push({ type: 'text', text: before });
137
+ }
138
+ const blockContent = match[1].trim();
139
+ const parsedObjects = parseMultipleJsonObjects(blockContent);
140
+ if (parsedObjects.length > 0) {
141
+ for (const parsed of parsedObjects) {
142
+ const validation = validateToolCallShape(parsed);
143
+ if (validation.ok) {
144
+ content.push({
145
+ type: 'tool_use',
146
+ id: generateToolId(),
147
+ name: validation.toolCall.name,
148
+ input: validation.toolCall.arguments,
149
+ });
150
+ }
151
+ }
152
+ }
153
+ else {
154
+ // Could not parse — keep as text
155
+ content.push({ type: 'text', text: match[0] });
156
+ }
157
+ lastIndex = match.index + match[0].length;
158
+ }
159
+ if (foundToolCalls && lastIndex < text.length) {
160
+ const remainder = text.slice(lastIndex).trim();
161
+ if (remainder)
162
+ content.push({ type: 'text', text: remainder });
163
+ }
164
+ return foundToolCalls ? content : [];
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Strategy 2: XML <function=name> format
168
+ // ---------------------------------------------------------------------------
169
+ function parseXmlFunctionCalls(text) {
170
+ const content = [];
171
+ const xmlToolRegex = /<function=(\w+)>([\s\S]*?)<\/function>/g;
172
+ const paramRegex = /<parameter=(\w+)>([\s\S]*?)<\/parameter>/g;
173
+ let lastIndex = 0;
174
+ let match;
175
+ let foundToolCalls = false;
176
+ while ((match = xmlToolRegex.exec(text)) !== null) {
177
+ foundToolCalls = true;
178
+ if (match.index > lastIndex) {
179
+ const before = text.slice(lastIndex, match.index).trim();
180
+ if (before)
181
+ content.push({ type: 'text', text: before });
182
+ }
183
+ const toolName = match[1];
184
+ const paramsBlock = match[2];
185
+ const input = {};
186
+ let paramMatch;
187
+ while ((paramMatch = paramRegex.exec(paramsBlock)) !== null) {
188
+ input[paramMatch[1]] = paramMatch[2].trim();
189
+ }
190
+ paramRegex.lastIndex = 0;
191
+ content.push({
192
+ type: 'tool_use',
193
+ id: generateToolId(),
194
+ name: toolName,
195
+ input,
196
+ });
197
+ lastIndex = match.index + match[0].length;
198
+ }
199
+ if (foundToolCalls && lastIndex < text.length) {
200
+ const remainder = text.slice(lastIndex).trim();
201
+ if (remainder)
202
+ content.push({ type: 'text', text: remainder });
203
+ }
204
+ return foundToolCalls ? content : [];
205
+ }
206
+ // ---------------------------------------------------------------------------
207
+ // Strategy 3: <invoke name="..."> format (MiniMax/Kimi style)
208
+ // ---------------------------------------------------------------------------
209
+ function parseInvokeCalls(text) {
210
+ const content = [];
211
+ const invokeRegex = /<invoke\s+name="([^"]+)">((?:.|\n)*?)<\/invoke>/g;
212
+ const invokeParamRegex = /<parameter\s+name="([^"]+)">((?:.|\n)*?)<\/parameter>/g;
213
+ let lastIndex = 0;
214
+ let match;
215
+ let foundToolCalls = false;
216
+ while ((match = invokeRegex.exec(text)) !== null) {
217
+ foundToolCalls = true;
218
+ if (match.index > lastIndex) {
219
+ const before = text.slice(lastIndex, match.index).trim();
220
+ // Strip wrapper tags like <minimax:tool_call>
221
+ const cleaned = before.replace(/<\/?[\w:]+>/g, '').trim();
222
+ if (cleaned)
223
+ content.push({ type: 'text', text: cleaned });
224
+ }
225
+ const toolName = match[1];
226
+ const paramsBlock = match[2];
227
+ const input = {};
228
+ let paramMatch;
229
+ while ((paramMatch = invokeParamRegex.exec(paramsBlock)) !== null) {
230
+ input[paramMatch[1]] = paramMatch[2].trim();
231
+ }
232
+ invokeParamRegex.lastIndex = 0;
233
+ content.push({
234
+ type: 'tool_use',
235
+ id: generateToolId(),
236
+ name: toolName,
237
+ input,
238
+ });
239
+ lastIndex = match.index + match[0].length;
240
+ }
241
+ if (foundToolCalls) {
242
+ if (lastIndex < text.length) {
243
+ const remainder = text.slice(lastIndex).replace(/<\/?[\w:]+>/g, '').trim();
244
+ if (remainder)
245
+ content.push({ type: 'text', text: remainder });
246
+ }
247
+ return content;
248
+ }
249
+ return [];
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // JSON parsing utilities
253
+ // ---------------------------------------------------------------------------
254
+ /**
255
+ * Parse multiple JSON objects from a block that may contain:
256
+ * - A single JSON object
257
+ * - A JSON array
258
+ * - One object per line
259
+ * - Comma-separated objects
260
+ */
261
+ export function parseMultipleJsonObjects(block) {
262
+ const results = [];
263
+ // Try 1: Single JSON object or array
264
+ try {
265
+ const parsed = JSON.parse(block);
266
+ if (parsed && typeof parsed === 'object') {
267
+ if (parsed.name && parsed.arguments) {
268
+ results.push(parsed);
269
+ return results;
270
+ }
271
+ if (Array.isArray(parsed)) {
272
+ for (const item of parsed) {
273
+ if (item?.name && item?.arguments)
274
+ results.push(item);
275
+ }
276
+ if (results.length > 0)
277
+ return results;
278
+ }
279
+ }
280
+ }
281
+ catch {
282
+ // Not valid single JSON — try multi-line
283
+ }
284
+ // Try 2: One JSON object per line
285
+ const lines = block.split('\n');
286
+ for (const line of lines) {
287
+ const trimmed = line.trim();
288
+ if (!trimmed || !trimmed.startsWith('{'))
289
+ continue;
290
+ try {
291
+ const parsed = JSON.parse(trimmed);
292
+ if (parsed?.name && parsed?.arguments) {
293
+ results.push(parsed);
294
+ }
295
+ }
296
+ catch {
297
+ // Try stripping trailing comma
298
+ const noComma = trimmed.replace(/,\s*$/, '');
299
+ try {
300
+ const parsed = JSON.parse(noComma);
301
+ if (parsed?.name && parsed?.arguments) {
302
+ results.push(parsed);
303
+ }
304
+ }
305
+ catch {
306
+ // Skip unparseable
307
+ }
308
+ }
309
+ }
310
+ return results;
311
+ }
312
+ /**
313
+ * Safely parse a JSON string, returning an empty object on failure.
314
+ */
315
+ function safeParseJson(str) {
316
+ try {
317
+ const parsed = JSON.parse(str);
318
+ return typeof parsed === 'object' && parsed !== null ? parsed : {};
319
+ }
320
+ catch {
321
+ return {};
322
+ }
323
+ }
324
+ // ---------------------------------------------------------------------------
325
+ // Tool prompt builder (for models without native tool support)
326
+ // ---------------------------------------------------------------------------
327
+ /**
328
+ * Build the tool-call instruction suffix to inject into the system prompt
329
+ * for models that don't support native tool calling.
330
+ */
331
+ export function buildToolPromptSuffix(tools, options) {
332
+ const toolDescs = tools.map(t => `- ${t.name}: ${t.description}\n Input: ${JSON.stringify(t.inputSchema)}`).join('\n');
333
+ return `\n\nYou have access to the following tools. To use a tool, respond with a JSON block:
334
+
335
+ \`\`\`tool_call
336
+ {"name": "tool_name", "arguments": {"arg1": "value1"}}
337
+ \`\`\`
338
+
339
+ To call MULTIPLE tools, use one JSON object per line inside a single \`\`\`tool_call block:
340
+
341
+ \`\`\`tool_call
342
+ {"name": "tool1", "arguments": {"arg": "val"}}
343
+ {"name": "tool2", "arguments": {"arg": "val"}}
344
+ \`\`\`
345
+
346
+ Available tools:
347
+ ${toolDescs}
348
+
349
+ IMPORTANT: Use ONLY the \`\`\`tool_call JSON format shown above. Do NOT use XML, <invoke>, <function>, or any other format for tool calls.
350
+ Always use tools when you need to interact with the filesystem or run commands.
351
+ After receiving a tool result, continue your analysis.${options?.strict ? `
352
+
353
+ CRITICAL: When calling tools, output ONLY the tool call format. Do not include explanatory text, reasoning, or commentary before or after tool calls. Tool calls must use the exact argument types specified in the schema (numbers as numbers, booleans as booleans, not strings).` : ''}`;
354
+ }
355
+ //# sourceMappingURL=tool-parsing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-parsing.js","sourceRoot":"","sources":["../../../../src/llm/shared/tool-parsing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Tool call validation layer using Zod schemas.
3
+ *
4
+ * Mirrors Claude Code's approach: every tool call is validated through its
5
+ * registered Zod schema before execution. Malformed calls are rejected
6
+ * early with clear error messages to the model, enabling retry with
7
+ * corrected format.
8
+ */
9
+ import { z, type ZodSchema } from 'zod';
10
+ import type { ToolUseBlock } from '../../core/types.js';
11
+ /**
12
+ * Schema for a single parsed tool call object.
13
+ * All parsed tool calls (from native API, JSON text, XML text) must conform to this.
14
+ */
15
+ export declare const ToolCallSchema: z.ZodObject<{
16
+ name: z.ZodString;
17
+ arguments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ name: string;
20
+ arguments: Record<string, unknown>;
21
+ }, {
22
+ name: string;
23
+ arguments?: Record<string, unknown> | undefined;
24
+ }>;
25
+ export type ParsedToolCall = z.infer<typeof ToolCallSchema>;
26
+ /**
27
+ * Validate a raw parsed tool call against the canonical schema.
28
+ * Returns a normalized tool call or validation errors.
29
+ */
30
+ export declare function validateToolCallShape(raw: unknown): {
31
+ ok: true;
32
+ toolCall: ParsedToolCall;
33
+ } | {
34
+ ok: false;
35
+ errors: string[];
36
+ };
37
+ /**
38
+ * Validate a tool call's arguments against the tool's registered Zod input schema.
39
+ * Returns { ok: true, parsed } or { ok: false, message } with a corrective hint.
40
+ */
41
+ export declare function validateToolInput(toolName: string, args: Record<string, unknown>, inputSchema: ZodSchema): {
42
+ ok: true;
43
+ parsed: unknown;
44
+ } | {
45
+ ok: false;
46
+ message: string;
47
+ };
48
+ export interface ValidatedToolCall {
49
+ readonly id: string;
50
+ readonly name: string;
51
+ readonly input: Record<string, unknown>;
52
+ readonly rawInput: Record<string, unknown>;
53
+ }
54
+ export interface RejectedToolCall {
55
+ readonly id: string;
56
+ readonly name: string;
57
+ readonly error: string;
58
+ }
59
+ /**
60
+ * Validate a batch of tool use blocks against registered tool definitions.
61
+ * Separates valid tool calls from rejected ones with clear error messages.
62
+ *
63
+ * Mirrors Claude Code's flow: Zod input validation → tool-specific validateInput → execute
64
+ */
65
+ export declare function validateToolCallBatch(toolUseBlocks: ToolUseBlock[], toolLookup: Map<string, {
66
+ inputSchema: ZodSchema;
67
+ }>, availableToolNames: Set<string>): {
68
+ valid: ValidatedToolCall[];
69
+ rejected: RejectedToolCall[];
70
+ };
71
+ /**
72
+ * Detect if text contains leaked tool call formatting.
73
+ * When a model outputs tool call syntax in its text response instead of
74
+ * through the proper tool calling mechanism, we detect it here so the
75
+ * parser can try to extract valid tool calls from it.
76
+ */
77
+ export declare function detectToolCallLeakage(text: string): boolean;
78
+ /**
79
+ * Build a corrective system message to send back to the model
80
+ * when tool call leakage is detected.
81
+ */
82
+ export declare function buildLeakageCorrectionPrompt(): string;
83
+ //# sourceMappingURL=tool-call-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-call-schema.d.ts","sourceRoot":"","sources":["../../../../src/llm/validation/tool-call-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,KAAK,SAAS,EAAiB,MAAM,KAAK,CAAA;AACtD,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAMnE;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;EAGzB,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAE3D;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG;IACnD,EAAE,EAAE,IAAI,CAAA;IACR,QAAQ,EAAE,cAAc,CAAA;CACzB,GAAG;IACF,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CASA;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,SAAS,GACrB;IACD,EAAE,EAAE,IAAI,CAAA;IACR,MAAM,EAAE,OAAO,CAAA;CAChB,GAAG;IACF,EAAE,EAAE,KAAK,CAAA;IACT,OAAO,EAAE,MAAM,CAAA;CAChB,CASA;AAsBD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC3C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,YAAY,EAAE,EAC7B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,SAAS,CAAA;CAAE,CAAC,EACnD,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC9B;IACD,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B,CA6CA;AAoBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAMrD"}