























@@ -71,11 +71,115 @@ function isSilentReplyEnvelopeText(
7171}
7272}
737374+const taggedReasoningPrefixRe =
75+/^\s*<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>[\s\S]*?<\s*\/\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>\s*/i;
76+const openReasoningPrefixRe =
77+/^\s*<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/i;
78+const plainReasoningPrefixRe = /^\s*(?:think(?:ing)?|thought|analysis|reasoning)\s*:?\s*\r?\n/i;
79+80+function stripLeadingReasoningBlocks(text: string): string {
81+let current = text;
82+while (true) {
83+const next = current.replace(taggedReasoningPrefixRe, "");
84+if (next === current) {
85+return current;
86+}
87+current = next;
88+}
89+}
90+91+function stripFinalSilentToken(text: string, token: string): string | null {
92+const escaped = escapeRegExp(token);
93+const stripped = text.replace(new RegExp(`(?:^|[\\s*.])${escaped}\\s*$`, "i"), "").trim();
94+return stripped === text.trim() ? null : stripped;
95+}
96+97+const silentIntentTextRe =
98+/^\s*(?:i|i'll|i\s+will|i'm|i\s+am|we|we'll|we\s+will|the\s+assistant|assistant|the\s+bot|bot|openclaw)\s+(?:(?:will\s+)?(?:stay|remain|keep|be)\s+(?:quiet|silent)(?:\s+(?:here|for\s+now|on\s+this|in\s+this\s+(?:chat|thread|channel|conversation)))?|(?:do\s+not|don't|dont|will\s+not|won't|would\s+not|should\s+not)\s+(?:reply|respond)(?:\s+(?:here|for\s+now|on\s+this|in\s+this\s+(?:chat|thread|channel|conversation)))?|(?:have|has)\s+nothing\s+(?:to|for)\s+(?:say|add|reply|respond))(?:[.!?]+)?\s*$/i;
99+100+function hasSilentIntentFinalSilentToken(text: string, token: string): boolean {
101+const withoutToken = stripFinalSilentToken(text, token);
102+if (withoutToken === null) {
103+return false;
104+}
105+return !withoutToken || silentIntentTextRe.test(withoutToken);
106+}
107+108+const substantiveAnswerCueRe =
109+/\b(?:answer|here(?:'s|\s+is)|tell\s+them|you\s+(?:should|can|could|need|must)|please|try|use|send|service\s+is|resolved|retry|yes|no,|sure)\b/i;
110+const bareReasoningPlaceholderRe =
111+/^\s*(?:(?:internal|private)\s+)?(?:reasoning|thinking|thoughts?|analysis)(?:\s+notes?)?\s*$/i;
112+113+function hasPlainReasoningFinalSilentToken(text: string, token: string): boolean {
114+const withoutToken = stripFinalSilentToken(text, token);
115+if (withoutToken === null) {
116+return false;
117+}
118+if (!withoutToken || silentIntentTextRe.test(withoutToken)) {
119+return true;
120+}
121+const lines = withoutToken
122+.split(/\r?\n/)
123+.map((line) => line.trim())
124+.filter(Boolean);
125+const finalLine = lines.at(-1);
126+const previousLines = lines.slice(0, -1).join("\n");
127+return (
128+Boolean(
129+finalLine &&
130+silentIntentTextRe.test(finalLine) &&
131+previousLines &&
132+!substantiveAnswerCueRe.test(previousLines),
133+) || bareReasoningPlaceholderRe.test(withoutToken)
134+);
135+}
136+137+function isReasoningPrefixedSilentReplyText(
138+text: string | undefined,
139+token: string = SILENT_REPLY_TOKEN,
140+): boolean {
141+if (!text) {
142+return false;
143+}
144+const trimmed = text.trim();
145+if (!trimmed) {
146+return false;
147+}
148+149+const withoutLeadingReasoningBlocks = stripLeadingReasoningBlocks(trimmed);
150+if (withoutLeadingReasoningBlocks !== trimmed) {
151+return (
152+isSilentReplyText(withoutLeadingReasoningBlocks, token) ||
153+hasSilentIntentFinalSilentToken(withoutLeadingReasoningBlocks, token)
154+);
155+}
156+157+if (openReasoningPrefixRe.test(trimmed)) {
158+const withoutOpenReasoningPrefix = trimmed.replace(openReasoningPrefixRe, "");
159+return (
160+isSilentReplyText(withoutOpenReasoningPrefix, token) ||
161+hasPlainReasoningFinalSilentToken(withoutOpenReasoningPrefix, token)
162+);
163+}
164+if (!plainReasoningPrefixRe.test(trimmed)) {
165+return false;
166+}
167+const withoutPlainReasoningPrefix = trimmed.replace(plainReasoningPrefixRe, "");
168+return (
169+isSilentReplyText(withoutPlainReasoningPrefix, token) ||
170+hasPlainReasoningFinalSilentToken(withoutPlainReasoningPrefix, token)
171+);
172+}
173+74174export function isSilentReplyPayloadText(
75175text: string | undefined,
76176token: string = SILENT_REPLY_TOKEN,
77177): boolean {
78-return isSilentReplyText(text, token) || isSilentReplyEnvelopeText(text, token);
178+return (
179+isSilentReplyText(text, token) ||
180+isSilentReplyEnvelopeText(text, token) ||
181+isReasoningPrefixedSilentReplyText(text, token)
182+);
79183}
8018481185/**
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。