(text)
| 165 | // ── Helpers ────────────────────────────────────────────────────────────────── |
| 166 | |
| 167 | function _safeParseAny(text) { |
| 168 | if (!text) return null; |
| 169 | const trimmed = text.trim(); |
| 170 | // Strip trailing commas which Qwen sometimes emits. |
| 171 | const cleaned = trimmed.replace(/,(\s*[}\]])/g, '$1'); |
| 172 | try { return JSON.parse(cleaned); } catch {} |
| 173 | // Multiple JSON objects newline-separated → wrap into array. |
| 174 | const lines = cleaned.split(/\n+/).map(s => s.trim()).filter(Boolean); |
| 175 | if (lines.length > 1) { |
| 176 | const arr = []; |
| 177 | for (const line of lines) { |
| 178 | try { arr.push(JSON.parse(line.replace(/,(\s*[}\]])/g, '$1'))); } catch { return null; } |
| 179 | } |
| 180 | return arr.length > 0 ? arr : null; |
| 181 | } |
| 182 | return null; |
| 183 | } |
| 184 | |
| 185 | // Normalise whatever the model emitted into [{ name, arguments }, ...]. |
| 186 | // Filters out entries that don't reference a known tool, when we have a |
no outgoing calls
no test coverage detected