{"version":3,"file":"ai-model/shared/xml.mjs","sources":["../../../../src/ai-model/shared/xml.ts"],"sourcesContent":["/**\n * Extract content from an XML tag in a string, searching from the end.\n * This approach handles cases where models prepend thinking content (like <think>...</think>)\n * before the actual response tags, or when there are incomplete/nested tags.\n *\n * Strategy: Find the LAST closing tag, then search backwards for the nearest opening tag.\n * This ensures we get the last complete tag pair, even if there are incomplete tags before it.\n *\n * @param xmlString - The XML string to parse\n * @param tagName - The name of the tag to extract (case-insensitive)\n * @returns The trimmed content of the tag, or undefined if not found\n */\nexport function extractXMLTag(\n  xmlString: string,\n  tagName: string,\n): string | undefined {\n  const lowerXmlString = xmlString.toLowerCase();\n  const lowerTagName = tagName.toLowerCase();\n  const closeTag = `</${lowerTagName}>`;\n  const openTag = `<${lowerTagName}>`;\n\n  // Find the last closing tag\n  const lastCloseIndex = lowerXmlString.lastIndexOf(closeTag);\n  if (lastCloseIndex === -1) {\n    // Fallback: handle half-open tags like `<action-type>Input` without\n    // matching close tag. Extract until the next XML tag boundary.\n    const lastOpenIndex = lowerXmlString.lastIndexOf(openTag);\n    if (lastOpenIndex === -1) {\n      return undefined;\n    }\n\n    const contentStart = lastOpenIndex + openTag.length;\n    const remaining = xmlString.substring(contentStart);\n    const nextTagIndex = remaining.indexOf('<');\n    const content =\n      nextTagIndex === -1 ? remaining : remaining.substring(0, nextTagIndex);\n\n    return content.trim();\n  }\n\n  // Search backwards from the closing tag to find the nearest opening tag\n  const searchArea = lowerXmlString.substring(0, lastCloseIndex);\n  const lastOpenIndex = searchArea.lastIndexOf(openTag);\n  if (lastOpenIndex === -1) {\n    return undefined;\n  }\n\n  // Extract content between the tags (use original string to preserve case)\n  const contentStart = lastOpenIndex + openTag.length;\n  const contentEnd = lastCloseIndex;\n  const content = xmlString.substring(contentStart, contentEnd);\n\n  return content.trim();\n}\n\nconst escapeRegExp = (value: string) =>\n  value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/** Extract raw XML from the first protocol tag through the last protocol tag. */\nexport function extractRawXMLFragment(\n  content: string,\n  tagNames: readonly [string, ...string[]],\n): string {\n  const startTagName = tagNames[0];\n  const endTagName = tagNames.at(-1)!;\n  const startTagRegex = new RegExp(`<${escapeRegExp(startTagName)}>`, 'i');\n  const startTag = startTagRegex.exec(content);\n  if (startTag?.index === undefined) {\n    return '';\n  }\n\n  const contentFromStartTag = content.slice(startTag.index);\n  const endTagRegex = new RegExp(`</${escapeRegExp(endTagName)}>`, 'gi');\n  const endTags = [...contentFromStartTag.matchAll(endTagRegex)];\n  const lastEndTag = endTags.at(-1);\n  const end =\n    lastEndTag?.index !== undefined\n      ? lastEndTag.index + lastEndTag[0].length\n      : contentFromStartTag.length;\n\n  return contentFromStartTag.slice(0, end);\n}\n"],"names":["extractXMLTag","xmlString","tagName","lowerXmlString","lowerTagName","closeTag","openTag","lastCloseIndex","lastOpenIndex","contentStart","remaining","nextTagIndex","content","searchArea","contentEnd","escapeRegExp","value","extractRawXMLFragment","tagNames","startTagName","endTagName","startTagRegex","RegExp","startTag","undefined","contentFromStartTag","endTagRegex","endTags","lastEndTag","end"],"mappings":"AAYO,SAASA,cACdC,SAAiB,EACjBC,OAAe;IAEf,MAAMC,iBAAiBF,UAAU,WAAW;IAC5C,MAAMG,eAAeF,QAAQ,WAAW;IACxC,MAAMG,WAAW,CAAC,EAAE,EAAED,aAAa,CAAC,CAAC;IACrC,MAAME,UAAU,CAAC,CAAC,EAAEF,aAAa,CAAC,CAAC;IAGnC,MAAMG,iBAAiBJ,eAAe,WAAW,CAACE;IAClD,IAAIE,AAAmB,OAAnBA,gBAAuB;QAGzB,MAAMC,gBAAgBL,eAAe,WAAW,CAACG;QACjD,IAAIE,AAAkB,OAAlBA,eACF;QAGF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;QACnD,MAAMI,YAAYT,UAAU,SAAS,CAACQ;QACtC,MAAME,eAAeD,UAAU,OAAO,CAAC;QACvC,MAAME,UACJD,AAAiB,OAAjBA,eAAsBD,YAAYA,UAAU,SAAS,CAAC,GAAGC;QAE3D,OAAOC,QAAQ,IAAI;IACrB;IAGA,MAAMC,aAAaV,eAAe,SAAS,CAAC,GAAGI;IAC/C,MAAMC,gBAAgBK,WAAW,WAAW,CAACP;IAC7C,IAAIE,AAAkB,OAAlBA,eACF;IAIF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;IACnD,MAAMQ,aAAaP;IACnB,MAAMK,UAAUX,UAAU,SAAS,CAACQ,cAAcK;IAElD,OAAOF,QAAQ,IAAI;AACrB;AAEA,MAAMG,eAAe,CAACC,QACpBA,MAAM,OAAO,CAAC,uBAAuB;AAGhC,SAASC,sBACdL,OAAe,EACfM,QAAwC;IAExC,MAAMC,eAAeD,QAAQ,CAAC,EAAE;IAChC,MAAME,aAAaF,SAAS,EAAE,CAAC;IAC/B,MAAMG,gBAAgB,IAAIC,OAAO,CAAC,CAAC,EAAEP,aAAaI,cAAc,CAAC,CAAC,EAAE;IACpE,MAAMI,WAAWF,cAAc,IAAI,CAACT;IACpC,IAAIW,UAAU,UAAUC,QACtB,OAAO;IAGT,MAAMC,sBAAsBb,QAAQ,KAAK,CAACW,SAAS,KAAK;IACxD,MAAMG,cAAc,IAAIJ,OAAO,CAAC,EAAE,EAAEP,aAAaK,YAAY,CAAC,CAAC,EAAE;IACjE,MAAMO,UAAU;WAAIF,oBAAoB,QAAQ,CAACC;KAAa;IAC9D,MAAME,aAAaD,QAAQ,EAAE,CAAC;IAC9B,MAAME,MACJD,YAAY,UAAUJ,SAClBI,WAAW,KAAK,GAAGA,UAAU,CAAC,EAAE,CAAC,MAAM,GACvCH,oBAAoB,MAAM;IAEhC,OAAOA,oBAAoB,KAAK,CAAC,GAAGI;AACtC"}