const SECRET_PATTERNS = [ /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/i, /\b(?:password|passwd|api[_ -]?key|access[_ -]?token|refresh[_ -]?token|bearer|client[_ -]?secret)\b/i, /(?:密码|口令|令牌|密钥|私钥|验证码)/, ]; export function boundedText(value, maxLength) { if (typeof value !== "string") return ""; return value.trim().slice(0, maxLength); } export function safeMemoryText(value, maxLength = 8_000) { const text = boundedText(value, maxLength); if (SECRET_PATTERNS.some((pattern) => pattern.test(text))) return "[敏感内容未写入记忆]"; return text; } export function words(value) { const lowered = boundedText(value, 16_000).toLowerCase(); const terms = new Set(lowered.match(/[a-z0-9_-]{2,}|[\u3400-\u9fff]{2}/g) ?? []); const cjk = [...lowered.matchAll(/[\u3400-\u9fff]/g)].map((match) => match[0]).join(""); for (let index = 0; index + 1 < cjk.length; index++) terms.add(cjk.slice(index, index + 2)); return terms; } export function similarity(left, right) { const a = words(left); const b = words(right); if (!a.size || !b.size) return 0; let overlap = 0; for (const term of a) if (b.has(term)) overlap++; return overlap / Math.sqrt(a.size * b.size); }