{"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AAgEA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CA6B9E","sourcesContent":["import { parse as partialParse } from \"partial-json\";\n\n/**\n * Escape raw (unescaped) control characters that appear inside string literals.\n *\n * Models occasionally stream tool-call arguments containing a literal newline,\n * tab, or other control char inside a JSON string (e.g. a multi-line `edit`\n * payload) instead of the escaped `\\n` / `\\t`. That is invalid JSON, so both\n * `JSON.parse` and `partial-json` reject it — previously the whole tool-call\n * argument object was silently dropped to `{}`, so the tool ran with empty args,\n * failed, and the model regenerated the identical malformed call in a loop.\n *\n * This walks the string tracking string-literal / escape state and replaces any\n * raw control char (code point < 0x20) found *inside* a string with its JSON\n * escape, leaving structural whitespace between tokens untouched.\n */\nfunction escapeControlCharsInStrings(json: string): string {\n\tlet out = \"\";\n\tlet inString = false;\n\tlet escaped = false;\n\tfor (const ch of json) {\n\t\tif (escaped) {\n\t\t\tout += ch;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\\\") {\n\t\t\tout += ch;\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '\"') {\n\t\t\tinString = !inString;\n\t\t\tout += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tconst code = ch.charCodeAt(0);\n\t\tif (inString && code < 0x20) {\n\t\t\tswitch (ch) {\n\t\t\t\tcase \"\\n\":\n\t\t\t\t\tout += \"\\\\n\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"\\r\":\n\t\t\t\t\tout += \"\\\\r\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"\\t\":\n\t\t\t\t\tout += \"\\\\t\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"\\b\":\n\t\t\t\t\tout += \"\\\\b\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"\\f\":\n\t\t\t\t\tout += \"\\\\f\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tout += `\\\\u${code.toString(16).padStart(4, \"0\")}`;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tout += ch;\n\t}\n\treturn out;\n}\n\n/**\n * Attempts to parse potentially incomplete JSON during streaming.\n * Always returns a valid object, even if the JSON is incomplete.\n *\n * @param partialJson The partial JSON string from streaming\n * @returns Parsed object or empty object if parsing fails\n */\nexport function parseStreamingJson<T = any>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\n\t// Try standard parsing first (fastest for complete, valid JSON).\n\ttry {\n\t\treturn JSON.parse(partialJson) as T;\n\t} catch {\n\t\t// Repair before any lenient parse: the model may have emitted raw control\n\t\t// characters inside a string literal (an unescaped newline/tab in a\n\t\t// multi-line payload). `partial-json` does NOT throw on these — it silently\n\t\t// truncates the object at the bad char (dropping keys), so we must escape\n\t\t// them first to recover the real arguments instead of losing them (which\n\t\t// loops the model on the same malformed call). Escaping is a no-op for\n\t\t// otherwise-valid JSON, so `partialParse(repaired)` still handles genuine\n\t\t// mid-stream truncation identically to the raw input.\n\t\tconst repaired = escapeControlCharsInStrings(partialJson);\n\t\ttry {\n\t\t\treturn JSON.parse(repaired) as T;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst result = partialParse(repaired);\n\t\t\t\treturn (result ?? {}) as T;\n\t\t\t} catch {\n\t\t\t\treturn {} as T;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}