File size: 2,230 Bytes
fc93158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * Detects inbound messages that are reflections of assistant-originated content.
 * These patterns indicate internal metadata leaked into a channel and then
 * bounced back as a new inbound message — creating an echo loop.
 */

import { findCodeRegions, isInsideCode } from "../../shared/text/code-regions.js";

const INTERNAL_SEPARATOR_RE = /(?:#\+){2,}#?/;
const ASSISTANT_ROLE_MARKER_RE = /\bassistant\s+to\s*=\s*\w+/i;
// Require closing `>` to avoid false-positives on phrases like "<thought experiment>".
const THINKING_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/i;
const RELEVANT_MEMORIES_TAG_RE = /<\s*\/?\s*relevant[-_]memories\b[^<>]*>/i;
// Require closing `>` to avoid false-positives on phrases like "<final answer>".
const FINAL_TAG_RE = /<\s*\/?\s*final\b[^<>]*>/i;

const REFLECTION_PATTERNS: Array<{ re: RegExp; label: string }> = [
  { re: INTERNAL_SEPARATOR_RE, label: "internal-separator" },
  { re: ASSISTANT_ROLE_MARKER_RE, label: "assistant-role-marker" },
  { re: THINKING_TAG_RE, label: "thinking-tag" },
  { re: RELEVANT_MEMORIES_TAG_RE, label: "relevant-memories-tag" },
  { re: FINAL_TAG_RE, label: "final-tag" },
];

export type ReflectionDetection = {
  isReflection: boolean;
  matchedLabels: string[];
};

function hasMatchOutsideCode(text: string, re: RegExp): boolean {
  const codeRegions = findCodeRegions(text);
  const globalRe = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);

  for (const match of text.matchAll(globalRe)) {
    const start = match.index ?? -1;
    if (start >= 0 && !isInsideCode(start, codeRegions)) {
      return true;
    }
  }

  return false;
}

/**
 * Check whether an inbound message appears to be a reflection of
 * assistant-originated content. Returns matched pattern labels for telemetry.
 */
export function detectReflectedContent(text: string): ReflectionDetection {
  if (!text) {
    return { isReflection: false, matchedLabels: [] };
  }

  const matchedLabels: string[] = [];
  for (const { re, label } of REFLECTION_PATTERNS) {
    if (hasMatchOutsideCode(text, re)) {
      matchedLabels.push(label);
    }
  }

  return {
    isReflection: matchedLabels.length > 0,
    matchedLabels,
  };
}