






























11import { formatReasoningMessage } from "../agents/embedded-agent-utils.js";
2+import { findCodeRegions, isInsideCode } from "../shared/text/code-regions.js";
23import { stripInlineDirectiveTagsForDelivery } from "../utils/directive-tags.js";
4+import { removeChannelProgressDraftLine } from "./progress-draft-lines.js";
35import {
46createChannelProgressDraftGate,
57type ChannelProgressDraftLine,
@@ -89,10 +91,8 @@ export function createChannelProgressDraftCompositor(params: {
8991});
90929193const clearLine = async (lineId: string) => {
92-const nextLines = lines.filter(
93-(line) => typeof line !== "object" || line.id?.trim() !== lineId,
94-);
95-if (nextLines.length === lines.length) {
94+const nextLines = removeChannelProgressDraftLine(lines, lineId);
95+if (nextLines === lines) {
9696return;
9797}
9898lines = nextLines;
@@ -280,7 +280,11 @@ export function createChannelProgressDraftCompositor(params: {
280280}
281281282282function normalizeReasoningProgressLine(text: string): string {
283-return stripReasoningProgressTags(text)
283+const reasoningText = readReasoningProgressTextOutsideCode(text);
284+if (reasoningText === undefined) {
285+return "";
286+}
287+return stripReasoningProgressTagsOutsideCode(reasoningText)
284288.replace(
285289/^\s*(?:>\s*)?(?:Reasoning:\s*(?:\r?\n|\r)\s*|Thinking\.{0,3}\s*(?:\r?\n|\r)\s*(?:\r?\n|\r)\s*)/i,
286290"",
@@ -289,10 +293,75 @@ function normalizeReasoningProgressLine(text: string): string {
289293.trim();
290294}
291295292-function stripReasoningProgressTags(text: string): string {
293-return text.replace(
294-/<\s*\/?\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/giu,
295-"",
296+const REASONING_PROGRESS_TAG_RE =
297+/<\s*(\/?)\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/giu;
298+const REASONING_PROGRESS_TAG_NAMES = [
299+"think",
300+"thinking",
301+"thought",
302+"antthinking",
303+"antml:think",
304+"antml:thinking",
305+"antml:thought",
306+] as const;
307+const REASONING_PROGRESS_TAG_PREFIXES = REASONING_PROGRESS_TAG_NAMES.flatMap((name) => [
308+`<${name}`,
309+`</${name}`,
310+]);
311+312+function readReasoningProgressTextOutsideCode(text: string): string | undefined {
313+if (isPartialReasoningProgressTagPrefix(text)) {
314+return undefined;
315+}
316+const codeRegions = findCodeRegions(text);
317+let hasTags = false;
318+let inReasoning = false;
319+let cursor = 0;
320+const chunks: string[] = [];
321+for (const match of text.matchAll(REASONING_PROGRESS_TAG_RE)) {
322+const offset = match.index ?? 0;
323+if (isInsideCode(offset, codeRegions)) {
324+continue;
325+}
326+hasTags = true;
327+if (match[1]) {
328+if (inReasoning) {
329+chunks.push(text.slice(cursor, offset));
330+}
331+inReasoning = false;
332+cursor = offset + match[0].length;
333+continue;
334+}
335+if (inReasoning) {
336+chunks.push(text.slice(cursor, offset));
337+}
338+inReasoning = true;
339+cursor = offset + match[0].length;
340+}
341+if (!hasTags) {
342+return text;
343+}
344+if (inReasoning) {
345+chunks.push(text.slice(cursor));
346+}
347+return chunks.join("").trim();
348+}
349+350+function isPartialReasoningProgressTagPrefix(text: string): boolean {
351+const normalized = text.trimStart().toLowerCase();
352+return (
353+normalized.startsWith("<") &&
354+!normalized.includes(">") &&
355+REASONING_PROGRESS_TAG_PREFIXES.some(
356+(prefix) => prefix.startsWith(normalized) || normalized.startsWith(prefix),
357+)
358+);
359+}
360+361+function stripReasoningProgressTagsOutsideCode(text: string): string {
362+const codeRegions = findCodeRegions(text);
363+return text.replace(REASONING_PROGRESS_TAG_RE, (match, _closing: string, offset: number) =>
364+isInsideCode(offset, codeRegions) ? match : "",
296365);
297366}
298367@@ -367,13 +436,18 @@ function mergeReasoningProgressText(
367436}
368437const normalizedCurrent = normalizeReasoningProgressInput(current);
369438const normalizedIncoming = normalizeReasoningProgressInput(incoming);
370-if (!normalizedIncoming || normalizedIncoming === normalizedCurrent) {
439+if (!normalizedIncoming) {
440+return shouldAppendEmptyReasoningProgressDelta(current, incoming)
441+ ? `${current}${incoming}`
442+ : current;
443+}
444+if (normalizedIncoming === normalizedCurrent) {
371445return current;
372446}
373447if (
374448options?.snapshot === true ||
375449isReasoningSnapshotText(incoming) ||
376-normalizedIncoming.startsWith(normalizedCurrent)
450+(normalizedCurrent && normalizedIncoming.startsWith(normalizedCurrent))
377451) {
378452return incoming;
379453}
@@ -385,3 +459,21 @@ function isReasoningSnapshotText(text: string): boolean {
385459text,
386460);
387461}
462+463+function shouldAppendEmptyReasoningProgressDelta(current: string, incoming: string): boolean {
464+return (
465+isPartialReasoningProgressTagPrefix(current) ||
466+isPartialReasoningProgressTagPrefix(incoming) ||
467+hasReasoningProgressTagOutsideCode(incoming)
468+);
469+}
470+471+function hasReasoningProgressTagOutsideCode(text: string): boolean {
472+const codeRegions = findCodeRegions(text);
473+for (const match of text.matchAll(REASONING_PROGRESS_TAG_RE)) {
474+if (!isInsideCode(match.index ?? 0, codeRegions)) {
475+return true;
476+}
477+}
478+return false;
479+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。