mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 02:01:25 +00:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -20,13 +20,19 @@ type ThoughtSignatureSanitizeOptions = {
|
||||
|
||||
function isBase64Signature(value: string): boolean {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return false;
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
const compact = trimmed.replace(/\s+/g, "");
|
||||
if (!/^[A-Za-z0-9+/=_-]+$/.test(compact)) return false;
|
||||
if (!/^[A-Za-z0-9+/=_-]+$/.test(compact)) {
|
||||
return false;
|
||||
}
|
||||
const isUrl = compact.includes("-") || compact.includes("_");
|
||||
try {
|
||||
const buf = Buffer.from(compact, isUrl ? "base64url" : "base64");
|
||||
if (buf.length === 0) return false;
|
||||
if (buf.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const encoded = buf.toString(isUrl ? "base64url" : "base64");
|
||||
const normalize = (input: string) => input.replace(/=+$/g, "");
|
||||
return normalize(encoded) === normalize(compact);
|
||||
@@ -45,7 +51,9 @@ export function stripThoughtSignatures<T>(
|
||||
content: T,
|
||||
options?: ThoughtSignatureSanitizeOptions,
|
||||
): T {
|
||||
if (!Array.isArray(content)) return content;
|
||||
if (!Array.isArray(content)) {
|
||||
return content;
|
||||
}
|
||||
const allowBase64Only = options?.allowBase64Only ?? false;
|
||||
const includeCamelCase = options?.includeCamelCase ?? false;
|
||||
const shouldStripSignature = (value: unknown): boolean => {
|
||||
@@ -55,7 +63,9 @@ export function stripThoughtSignatures<T>(
|
||||
return typeof value !== "string" || !isBase64Signature(value);
|
||||
};
|
||||
return content.map((block) => {
|
||||
if (!block || typeof block !== "object") return block;
|
||||
if (!block || typeof block !== "object") {
|
||||
return block;
|
||||
}
|
||||
const rec = block as ContentBlockWithSignature;
|
||||
const stripSnake = shouldStripSignature(rec.thought_signature);
|
||||
const stripCamel = includeCamelCase ? shouldStripSignature(rec.thoughtSignature) : false;
|
||||
@@ -63,8 +73,12 @@ export function stripThoughtSignatures<T>(
|
||||
return block;
|
||||
}
|
||||
const next = { ...rec };
|
||||
if (stripSnake) delete next.thought_signature;
|
||||
if (stripCamel) delete next.thoughtSignature;
|
||||
if (stripSnake) {
|
||||
delete next.thought_signature;
|
||||
}
|
||||
if (stripCamel) {
|
||||
delete next.thoughtSignature;
|
||||
}
|
||||
return next;
|
||||
}) as T;
|
||||
}
|
||||
@@ -162,7 +176,9 @@ export function buildBootstrapContextFiles(
|
||||
continue;
|
||||
}
|
||||
const trimmed = trimBootstrapContent(file.content ?? "", file.name, maxChars);
|
||||
if (!trimmed.content) continue;
|
||||
if (!trimmed.content) {
|
||||
continue;
|
||||
}
|
||||
if (trimmed.truncated) {
|
||||
opts?.warn?.(
|
||||
`workspace bootstrap file ${file.name} is ${trimmed.originalLength} chars (limit ${trimmed.maxChars}); truncating in injected context`,
|
||||
@@ -188,7 +204,9 @@ export function sanitizeGoogleTurnOrdering(messages: AgentMessage[]): AgentMessa
|
||||
) {
|
||||
return messages;
|
||||
}
|
||||
if (role !== "assistant") return messages;
|
||||
if (role !== "assistant") {
|
||||
return messages;
|
||||
}
|
||||
|
||||
// Cloud Code Assist rejects histories that begin with a model turn (tool call or text).
|
||||
// Prepend a tiny synthetic user turn so the rest of the transcript can be used.
|
||||
|
||||
@@ -5,7 +5,9 @@ import { formatSandboxToolPolicyBlockedMessage } from "../sandbox.js";
|
||||
import type { FailoverReason } from "./types.js";
|
||||
|
||||
export function isContextOverflowError(errorMessage?: string): boolean {
|
||||
if (!errorMessage) return false;
|
||||
if (!errorMessage) {
|
||||
return false;
|
||||
}
|
||||
const lower = errorMessage.toLowerCase();
|
||||
const hasRequestSizeExceeds = lower.includes("request size exceeds");
|
||||
const hasContextWindow =
|
||||
@@ -30,15 +32,25 @@ const CONTEXT_OVERFLOW_HINT_RE =
|
||||
/context.*overflow|context window.*(too (?:large|long)|exceed|over|limit|max(?:imum)?|requested|sent|tokens)|(?:prompt|request|input).*(too (?:large|long)|exceed|over|limit|max(?:imum)?)/i;
|
||||
|
||||
export function isLikelyContextOverflowError(errorMessage?: string): boolean {
|
||||
if (!errorMessage) return false;
|
||||
if (CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage)) return false;
|
||||
if (isContextOverflowError(errorMessage)) return true;
|
||||
if (!errorMessage) {
|
||||
return false;
|
||||
}
|
||||
if (CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage)) {
|
||||
return false;
|
||||
}
|
||||
if (isContextOverflowError(errorMessage)) {
|
||||
return true;
|
||||
}
|
||||
return CONTEXT_OVERFLOW_HINT_RE.test(errorMessage);
|
||||
}
|
||||
|
||||
export function isCompactionFailureError(errorMessage?: string): boolean {
|
||||
if (!errorMessage) return false;
|
||||
if (!isContextOverflowError(errorMessage)) return false;
|
||||
if (!errorMessage) {
|
||||
return false;
|
||||
}
|
||||
if (!isContextOverflowError(errorMessage)) {
|
||||
return false;
|
||||
}
|
||||
const lower = errorMessage.toLowerCase();
|
||||
return (
|
||||
lower.includes("summarization failed") ||
|
||||
@@ -73,15 +85,21 @@ const HTTP_ERROR_HINTS = [
|
||||
];
|
||||
|
||||
function stripFinalTagsFromText(text: string): string {
|
||||
if (!text) return text;
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
return text.replace(FINAL_TAG_RE, "");
|
||||
}
|
||||
|
||||
function collapseConsecutiveDuplicateBlocks(text: string): string {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed) return text;
|
||||
if (!trimmed) {
|
||||
return text;
|
||||
}
|
||||
const blocks = trimmed.split(/\n{2,}/);
|
||||
if (blocks.length < 2) return text;
|
||||
if (blocks.length < 2) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const normalizeBlock = (value: string) => value.trim().replace(/\s+/g, " ");
|
||||
const result: string[] = [];
|
||||
@@ -96,15 +114,21 @@ function collapseConsecutiveDuplicateBlocks(text: string): string {
|
||||
lastNormalized = normalized;
|
||||
}
|
||||
|
||||
if (result.length === blocks.length) return text;
|
||||
if (result.length === blocks.length) {
|
||||
return text;
|
||||
}
|
||||
return result.join("\n\n");
|
||||
}
|
||||
|
||||
function isLikelyHttpErrorText(raw: string): boolean {
|
||||
const match = raw.match(HTTP_STATUS_PREFIX_RE);
|
||||
if (!match) return false;
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
const code = Number(match[1]);
|
||||
if (!Number.isFinite(code) || code < 400) return false;
|
||||
if (!Number.isFinite(code) || code < 400) {
|
||||
return false;
|
||||
}
|
||||
const message = match[2].toLowerCase();
|
||||
return HTTP_ERROR_HINTS.some((hint) => message.includes(hint));
|
||||
}
|
||||
@@ -112,10 +136,16 @@ function isLikelyHttpErrorText(raw: string): boolean {
|
||||
type ErrorPayload = Record<string, unknown>;
|
||||
|
||||
function isErrorPayloadObject(payload: unknown): payload is ErrorPayload {
|
||||
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
|
||||
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
||||
return false;
|
||||
}
|
||||
const record = payload as ErrorPayload;
|
||||
if (record.type === "error") return true;
|
||||
if (typeof record.request_id === "string" || typeof record.requestId === "string") return true;
|
||||
if (record.type === "error") {
|
||||
return true;
|
||||
}
|
||||
if (typeof record.request_id === "string" || typeof record.requestId === "string") {
|
||||
return true;
|
||||
}
|
||||
if ("error" in record) {
|
||||
const err = record.error;
|
||||
if (err && typeof err === "object" && !Array.isArray(err)) {
|
||||
@@ -133,18 +163,26 @@ function isErrorPayloadObject(payload: unknown): payload is ErrorPayload {
|
||||
}
|
||||
|
||||
function parseApiErrorPayload(raw: string): ErrorPayload | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return null;
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
const candidates = [trimmed];
|
||||
if (ERROR_PAYLOAD_PREFIX_RE.test(trimmed)) {
|
||||
candidates.push(trimmed.replace(ERROR_PAYLOAD_PREFIX_RE, "").trim());
|
||||
}
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate.startsWith("{") || !candidate.endsWith("}")) continue;
|
||||
if (!candidate.startsWith("{") || !candidate.endsWith("}")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(candidate) as unknown;
|
||||
if (isErrorPayloadObject(parsed)) return parsed;
|
||||
if (isErrorPayloadObject(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
} catch {
|
||||
// ignore parse errors
|
||||
}
|
||||
@@ -166,9 +204,13 @@ function stableStringify(value: unknown): string {
|
||||
}
|
||||
|
||||
export function getApiErrorPayloadFingerprint(raw?: string): string | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const payload = parseApiErrorPayload(raw);
|
||||
if (!payload) return null;
|
||||
if (!payload) {
|
||||
return null;
|
||||
}
|
||||
return stableStringify(payload);
|
||||
}
|
||||
|
||||
@@ -184,9 +226,13 @@ export type ApiErrorInfo = {
|
||||
};
|
||||
|
||||
export function parseApiErrorInfo(raw?: string): ApiErrorInfo | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return null;
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let httpCode: string | undefined;
|
||||
let candidate = trimmed;
|
||||
@@ -198,7 +244,9 @@ export function parseApiErrorInfo(raw?: string): ApiErrorInfo | null {
|
||||
}
|
||||
|
||||
const payload = parseApiErrorPayload(candidate);
|
||||
if (!payload) return null;
|
||||
if (!payload) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const requestId =
|
||||
typeof payload.request_id === "string"
|
||||
@@ -214,9 +262,15 @@ export function parseApiErrorInfo(raw?: string): ApiErrorInfo | null {
|
||||
let errMessage: string | undefined;
|
||||
if (payload.error && typeof payload.error === "object" && !Array.isArray(payload.error)) {
|
||||
const err = payload.error as Record<string, unknown>;
|
||||
if (typeof err.type === "string") errType = err.type;
|
||||
if (typeof err.code === "string" && !errType) errType = err.code;
|
||||
if (typeof err.message === "string") errMessage = err.message;
|
||||
if (typeof err.type === "string") {
|
||||
errType = err.type;
|
||||
}
|
||||
if (typeof err.code === "string" && !errType) {
|
||||
errType = err.code;
|
||||
}
|
||||
if (typeof err.message === "string") {
|
||||
errMessage = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -229,7 +283,9 @@ export function parseApiErrorInfo(raw?: string): ApiErrorInfo | null {
|
||||
|
||||
export function formatRawAssistantErrorForUi(raw?: string): string {
|
||||
const trimmed = (raw ?? "").trim();
|
||||
if (!trimmed) return "LLM request failed with an unknown error.";
|
||||
if (!trimmed) {
|
||||
return "LLM request failed with an unknown error.";
|
||||
}
|
||||
|
||||
const httpMatch = trimmed.match(HTTP_STATUS_PREFIX_RE);
|
||||
if (httpMatch) {
|
||||
@@ -256,8 +312,12 @@ export function formatAssistantErrorText(
|
||||
): string | undefined {
|
||||
// Also format errors if errorMessage is present, even if stopReason isn't "error"
|
||||
const raw = (msg.errorMessage ?? "").trim();
|
||||
if (msg.stopReason !== "error" && !raw) return undefined;
|
||||
if (!raw) return "LLM request failed with an unknown error.";
|
||||
if (msg.stopReason !== "error" && !raw) {
|
||||
return undefined;
|
||||
}
|
||||
if (!raw) {
|
||||
return "LLM request failed with an unknown error.";
|
||||
}
|
||||
|
||||
const unknownTool =
|
||||
raw.match(/unknown tool[:\s]+["']?([a-z0-9_-]+)["']?/i) ??
|
||||
@@ -268,7 +328,9 @@ export function formatAssistantErrorText(
|
||||
sessionKey: opts?.sessionKey,
|
||||
toolName: unknownTool[1],
|
||||
});
|
||||
if (rewritten) return rewritten;
|
||||
if (rewritten) {
|
||||
return rewritten;
|
||||
}
|
||||
}
|
||||
|
||||
if (isContextOverflowError(raw)) {
|
||||
@@ -311,10 +373,14 @@ export function formatAssistantErrorText(
|
||||
}
|
||||
|
||||
export function sanitizeUserFacingText(text: string): string {
|
||||
if (!text) return text;
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
const stripped = stripFinalTagsFromText(text);
|
||||
const trimmed = stripped.trim();
|
||||
if (!trimmed) return stripped;
|
||||
if (!trimmed) {
|
||||
return stripped;
|
||||
}
|
||||
|
||||
if (/incorrect role information|roles must alternate/i.test(trimmed)) {
|
||||
return (
|
||||
@@ -348,7 +414,9 @@ export function sanitizeUserFacingText(text: string): string {
|
||||
}
|
||||
|
||||
export function isRateLimitAssistantError(msg: AssistantMessage | undefined): boolean {
|
||||
if (!msg || msg.stopReason !== "error") return false;
|
||||
if (!msg || msg.stopReason !== "error") {
|
||||
return false;
|
||||
}
|
||||
return isRateLimitErrorMessage(msg.errorMessage ?? "");
|
||||
}
|
||||
|
||||
@@ -404,7 +472,9 @@ const IMAGE_DIMENSION_PATH_RE = /messages\.(\d+)\.content\.(\d+)\.image/i;
|
||||
const IMAGE_SIZE_ERROR_RE = /image exceeds\s*(\d+(?:\.\d+)?)\s*mb/i;
|
||||
|
||||
function matchesErrorPatterns(raw: string, patterns: readonly ErrorPattern[]): boolean {
|
||||
if (!raw) return false;
|
||||
if (!raw) {
|
||||
return false;
|
||||
}
|
||||
const value = raw.toLowerCase();
|
||||
return patterns.some((pattern) =>
|
||||
pattern instanceof RegExp ? pattern.test(value) : value.includes(pattern),
|
||||
@@ -421,8 +491,12 @@ export function isTimeoutErrorMessage(raw: string): boolean {
|
||||
|
||||
export function isBillingErrorMessage(raw: string): boolean {
|
||||
const value = raw.toLowerCase();
|
||||
if (!value) return false;
|
||||
if (matchesErrorPatterns(value, ERROR_PATTERNS.billing)) return true;
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
if (matchesErrorPatterns(value, ERROR_PATTERNS.billing)) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
value.includes("billing") &&
|
||||
(value.includes("upgrade") ||
|
||||
@@ -433,7 +507,9 @@ export function isBillingErrorMessage(raw: string): boolean {
|
||||
}
|
||||
|
||||
export function isBillingAssistantError(msg: AssistantMessage | undefined): boolean {
|
||||
if (!msg || msg.stopReason !== "error") return false;
|
||||
if (!msg || msg.stopReason !== "error") {
|
||||
return false;
|
||||
}
|
||||
return isBillingErrorMessage(msg.errorMessage ?? "");
|
||||
}
|
||||
|
||||
@@ -451,9 +527,13 @@ export function parseImageDimensionError(raw: string): {
|
||||
contentIndex?: number;
|
||||
raw: string;
|
||||
} | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const lower = raw.toLowerCase();
|
||||
if (!lower.includes("image dimensions exceed max allowed size")) return null;
|
||||
if (!lower.includes("image dimensions exceed max allowed size")) {
|
||||
return null;
|
||||
}
|
||||
const limitMatch = raw.match(IMAGE_DIMENSION_ERROR_RE);
|
||||
const pathMatch = raw.match(IMAGE_DIMENSION_PATH_RE);
|
||||
return {
|
||||
@@ -472,9 +552,13 @@ export function parseImageSizeError(raw: string): {
|
||||
maxMb?: number;
|
||||
raw: string;
|
||||
} | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const lower = raw.toLowerCase();
|
||||
if (!lower.includes("image exceeds") || !lower.includes("mb")) return null;
|
||||
if (!lower.includes("image exceeds") || !lower.includes("mb")) {
|
||||
return null;
|
||||
}
|
||||
const match = raw.match(IMAGE_SIZE_ERROR_RE);
|
||||
return {
|
||||
maxMb: match?.[1] ? Number.parseFloat(match[1]) : undefined,
|
||||
@@ -483,7 +567,9 @@ export function parseImageSizeError(raw: string): {
|
||||
}
|
||||
|
||||
export function isImageSizeError(errorMessage?: string): boolean {
|
||||
if (!errorMessage) return false;
|
||||
if (!errorMessage) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(parseImageSizeError(errorMessage));
|
||||
}
|
||||
|
||||
@@ -492,19 +578,37 @@ export function isCloudCodeAssistFormatError(raw: string): boolean {
|
||||
}
|
||||
|
||||
export function isAuthAssistantError(msg: AssistantMessage | undefined): boolean {
|
||||
if (!msg || msg.stopReason !== "error") return false;
|
||||
if (!msg || msg.stopReason !== "error") {
|
||||
return false;
|
||||
}
|
||||
return isAuthErrorMessage(msg.errorMessage ?? "");
|
||||
}
|
||||
|
||||
export function classifyFailoverReason(raw: string): FailoverReason | null {
|
||||
if (isImageDimensionErrorMessage(raw)) return null;
|
||||
if (isImageSizeError(raw)) return null;
|
||||
if (isRateLimitErrorMessage(raw)) return "rate_limit";
|
||||
if (isOverloadedErrorMessage(raw)) return "rate_limit";
|
||||
if (isCloudCodeAssistFormatError(raw)) return "format";
|
||||
if (isBillingErrorMessage(raw)) return "billing";
|
||||
if (isTimeoutErrorMessage(raw)) return "timeout";
|
||||
if (isAuthErrorMessage(raw)) return "auth";
|
||||
if (isImageDimensionErrorMessage(raw)) {
|
||||
return null;
|
||||
}
|
||||
if (isImageSizeError(raw)) {
|
||||
return null;
|
||||
}
|
||||
if (isRateLimitErrorMessage(raw)) {
|
||||
return "rate_limit";
|
||||
}
|
||||
if (isOverloadedErrorMessage(raw)) {
|
||||
return "rate_limit";
|
||||
}
|
||||
if (isCloudCodeAssistFormatError(raw)) {
|
||||
return "format";
|
||||
}
|
||||
if (isBillingErrorMessage(raw)) {
|
||||
return "billing";
|
||||
}
|
||||
if (isTimeoutErrorMessage(raw)) {
|
||||
return "timeout";
|
||||
}
|
||||
if (isAuthErrorMessage(raw)) {
|
||||
return "auth";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -513,6 +617,8 @@ export function isFailoverErrorMessage(raw: string): boolean {
|
||||
}
|
||||
|
||||
export function isFailoverAssistantError(msg: AssistantMessage | undefined): boolean {
|
||||
if (!msg || msg.stopReason !== "error") return false;
|
||||
if (!msg || msg.stopReason !== "error") {
|
||||
return false;
|
||||
}
|
||||
return isFailoverErrorMessage(msg.errorMessage ?? "");
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ export function isAntigravityClaude(params: {
|
||||
}): boolean {
|
||||
const provider = params.provider?.toLowerCase();
|
||||
const api = params.api?.toLowerCase();
|
||||
if (provider !== "google-antigravity" && api !== "google-antigravity") return false;
|
||||
if (provider !== "google-antigravity" && api !== "google-antigravity") {
|
||||
return false;
|
||||
}
|
||||
return params.modelId?.toLowerCase().includes("claude") ?? false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,20 @@ export function isEmptyAssistantMessageContent(
|
||||
message: Extract<AgentMessage, { role: "assistant" }>,
|
||||
): boolean {
|
||||
const content = message.content;
|
||||
if (content == null) return true;
|
||||
if (!Array.isArray(content)) return false;
|
||||
if (content == null) {
|
||||
return true;
|
||||
}
|
||||
if (!Array.isArray(content)) {
|
||||
return false;
|
||||
}
|
||||
return content.every((block) => {
|
||||
if (!block || typeof block !== "object") return true;
|
||||
if (!block || typeof block !== "object") {
|
||||
return true;
|
||||
}
|
||||
const rec = block as { type?: unknown; text?: unknown };
|
||||
if (rec.type !== "text") return false;
|
||||
if (rec.type !== "text") {
|
||||
return false;
|
||||
}
|
||||
return typeof rec.text !== "string" || rec.text.trim().length === 0;
|
||||
});
|
||||
}
|
||||
@@ -110,9 +118,13 @@ export async function sanitizeSessionMessagesImages(
|
||||
: stripThoughtSignatures(content, options?.sanitizeThoughtSignatures); // Strip for Gemini
|
||||
|
||||
const filteredContent = strippedContent.filter((block) => {
|
||||
if (!block || typeof block !== "object") return true;
|
||||
if (!block || typeof block !== "object") {
|
||||
return true;
|
||||
}
|
||||
const rec = block as { type?: unknown; text?: unknown };
|
||||
if (rec.type !== "text" || typeof rec.text !== "string") return true;
|
||||
if (rec.type !== "text" || typeof rec.text !== "string") {
|
||||
return true;
|
||||
}
|
||||
return rec.text.trim().length > 0;
|
||||
});
|
||||
const finalContent = (await sanitizeContentBlocksImages(
|
||||
|
||||
@@ -20,17 +20,27 @@ export function isMessagingToolDuplicateNormalized(
|
||||
normalized: string,
|
||||
normalizedSentTexts: string[],
|
||||
): boolean {
|
||||
if (normalizedSentTexts.length === 0) return false;
|
||||
if (!normalized || normalized.length < MIN_DUPLICATE_TEXT_LENGTH) return false;
|
||||
if (normalizedSentTexts.length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (!normalized || normalized.length < MIN_DUPLICATE_TEXT_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
return normalizedSentTexts.some((normalizedSent) => {
|
||||
if (!normalizedSent || normalizedSent.length < MIN_DUPLICATE_TEXT_LENGTH) return false;
|
||||
if (!normalizedSent || normalizedSent.length < MIN_DUPLICATE_TEXT_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
return normalized.includes(normalizedSent) || normalizedSent.includes(normalized);
|
||||
});
|
||||
}
|
||||
|
||||
export function isMessagingToolDuplicate(text: string, sentTexts: string[]): boolean {
|
||||
if (sentTexts.length === 0) return false;
|
||||
if (sentTexts.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const normalized = normalizeTextForComparison(text);
|
||||
if (!normalized || normalized.length < MIN_DUPLICATE_TEXT_LENGTH) return false;
|
||||
if (!normalized || normalized.length < MIN_DUPLICATE_TEXT_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
return isMessagingToolDuplicateNormalized(normalized, sentTexts.map(normalizeTextForComparison));
|
||||
}
|
||||
|
||||
@@ -12,11 +12,15 @@ type OpenAIReasoningSignature = {
|
||||
};
|
||||
|
||||
function parseOpenAIReasoningSignature(value: unknown): OpenAIReasoningSignature | null {
|
||||
if (!value) return null;
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
let candidate: { id?: unknown; type?: unknown } | null = null;
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
|
||||
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
candidate = JSON.parse(trimmed) as { id?: unknown; type?: unknown };
|
||||
} catch {
|
||||
@@ -25,10 +29,14 @@ function parseOpenAIReasoningSignature(value: unknown): OpenAIReasoningSignature
|
||||
} else if (typeof value === "object") {
|
||||
candidate = value as { id?: unknown; type?: unknown };
|
||||
}
|
||||
if (!candidate) return null;
|
||||
if (!candidate) {
|
||||
return null;
|
||||
}
|
||||
const id = typeof candidate.id === "string" ? candidate.id : "";
|
||||
const type = typeof candidate.type === "string" ? candidate.type : "";
|
||||
if (!id.startsWith("rs_")) return null;
|
||||
if (!id.startsWith("rs_")) {
|
||||
return null;
|
||||
}
|
||||
if (type === "reasoning" || type.startsWith("reasoning.")) {
|
||||
return { id, type };
|
||||
}
|
||||
@@ -41,8 +49,12 @@ function hasFollowingNonThinkingBlock(
|
||||
): boolean {
|
||||
for (let i = index + 1; i < content.length; i++) {
|
||||
const block = content[i];
|
||||
if (!block || typeof block !== "object") return true;
|
||||
if ((block as { type?: unknown }).type !== "thinking") return true;
|
||||
if (!block || typeof block !== "object") {
|
||||
return true;
|
||||
}
|
||||
if ((block as { type?: unknown }).type !== "thinking") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import { normalizeThinkLevel, type ThinkLevel } from "../../auto-reply/thinking.
|
||||
function extractSupportedValues(raw: string): string[] {
|
||||
const match =
|
||||
raw.match(/supported values are:\s*([^\n.]+)/i) ?? raw.match(/supported values:\s*([^\n.]+)/i);
|
||||
if (!match?.[1]) return [];
|
||||
if (!match?.[1]) {
|
||||
return [];
|
||||
}
|
||||
const fragment = match[1];
|
||||
const quoted = Array.from(fragment.matchAll(/['"]([^'"]+)['"]/g)).map((entry) =>
|
||||
entry[1]?.trim(),
|
||||
@@ -22,13 +24,21 @@ export function pickFallbackThinkingLevel(params: {
|
||||
attempted: Set<ThinkLevel>;
|
||||
}): ThinkLevel | undefined {
|
||||
const raw = params.message?.trim();
|
||||
if (!raw) return undefined;
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
const supported = extractSupportedValues(raw);
|
||||
if (supported.length === 0) return undefined;
|
||||
if (supported.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
for (const entry of supported) {
|
||||
const normalized = normalizeThinkLevel(entry);
|
||||
if (!normalized) continue;
|
||||
if (params.attempted.has(normalized)) continue;
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
if (params.attempted.has(normalized)) {
|
||||
continue;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user