// Detector for FABRICATED tool-call transcripts.
//
// Some engines (Hermes/Nous-template instruct models on Ollama, and even the
// Claude Code CLI when asked to "run an agent") role-play an entire tool loop in
// PLAIN TEXT instead of dispatching real tools — emitting `<tool_call>` and a
// hallucinated `<tool_response>` with invented results. Nothing actually runs.
// If that text is saved verbatim as a deliverable or delivered as a chat answer,
// the user gets fiction presented as a real result.
//
// This module spots that pattern so callers can refuse to deliver/persist it.
//
// CRITICAL false-positive guard: a GENUINE report may legitimately *quote*
// `<tool_call>` inside a fenced code block (e.g. a bug write-up about this very
// problem). We must only flag the markers when they appear as BARE text — i.e.
// the model is emitting them as its own turn — never when they sit inside a
// ``` ``` ``` fence or inline `code` span. We strip fenced/inline code first and
// only scan what remains.

// Remove fenced code blocks (``` ... ``` and ~~~ ... ~~~) and inline `code`
// spans so quoted tool syntax inside documentation is NOT mistaken for a real
// (fabricated) emission.
function stripCodeContext(text) {
  return String(text || '')
    .replace(/```[\s\S]*?```/g, ' ')
    .replace(/~~~[\s\S]*?~~~/g, ' ')
    .replace(/`[^`\n]*`/g, ' ');
}

// Bare `<tool_call>` / `<tool_response>` XML tags (Hermes/Nous chat template).
const BARE_TOOL_TAG_RE = /<\s*tool_(call|response)\s*>/i;

// A bare JSON function-call blob the model wrote as text, e.g.
//   {"name": "web-search", "parameters": { ... }}
//   {"name": "web_fetch", "arguments": { ... }}
// Hyphenated tool names are an especially strong tell — the real registry uses
// underscores, so a hyphenated `"name"` call is almost certainly invented.
const BARE_JSON_CALL_RE = /\{\s*"name"\s*:\s*"[a-z0-9._-]+"\s*,\s*"(parameters|arguments)"\s*:/i;

// A fabricated tool RESULT echoed as text — the model inventing what a tool
// "returned". Pairs with the call markers above to raise confidence.
const BARE_TOOL_RESULT_RE = /"(snippet|tool_response)"\s*:/i;

/**
 * True when `text` looks like a model that role-played a tool loop in plain text
 * (fabricated calls + invented results) rather than dispatching real tools.
 *
 * Conservative by design: requires a bare (non-code-fenced) call marker. Quoting
 * tool syntax inside ``` fences ``` or `inline code` does NOT trip it.
 */
export function looksFabricatedToolTranscript(text) {
  const bare = stripCodeContext(text);
  if (!bare.trim()) return false;
  // Primary tell: the Hermes `<tool_call>`/`<tool_response>` XML template.
  if (BARE_TOOL_TAG_RE.test(bare)) return true;
  // Secondary tell: a bare JSON function-call blob. Require a corroborating
  // signal (a second call OR a fabricated result echo) so a single inline JSON
  // object in prose can't false-positive.
  if (BARE_JSON_CALL_RE.test(bare)) {
    const callCount = (bare.match(/\{\s*"name"\s*:\s*"[a-z0-9._-]+"\s*,\s*"(parameters|arguments)"\s*:/gi) || []).length;
    if (callCount >= 2) return true;
    if (BARE_TOOL_RESULT_RE.test(bare)) return true;
  }
  return false;
}

/**
 * Remove fabricated tool-call / tool-response blocks from text, leaving any
 * genuine prose. Used when we want to salvage a clean message rather than reject
 * the whole turn. Best-effort — primarily strips the bare XML template.
 */
export function stripFabricatedToolBlocks(text) {
  return String(text || '')
    // <tool_call> ... </tool_call> (with or without closing tag before EOF)
    .replace(/<\s*tool_call\s*>[\s\S]*?(<\s*\/\s*tool_call\s*>|$)/gi, '')
    .replace(/<\s*tool_response\s*>[\s\S]*?(<\s*\/\s*tool_response\s*>|$)/gi, '')
    .trim();
}
