


















@@ -8,6 +8,13 @@ import { UserMessageComponent } from "./user-message.js";
8899const PENDING_HISTORY_CLOCK_SKEW_TOLERANCE_MS = 60_000;
101011+type RepeatableSystemMessage = {
12+component: Container;
13+textNode: Text;
14+baseText: string;
15+count: number;
16+};
17+1118export class ChatLog extends Container {
1219private readonly maxComponents: number;
1320private toolById = new Map<string, ToolExecutionComponent>();
@@ -22,6 +29,7 @@ export class ChatLog extends Container {
2229>();
2330private btwMessage: BtwInlineMessage | null = null;
2431private toolsExpanded = false;
32+private repeatableSystemMessage: RepeatableSystemMessage | null = null;
25332634constructor(maxComponents = 180) {
2735super();
@@ -47,6 +55,9 @@ export class ChatLog extends Container {
4755if (this.btwMessage === component) {
4856this.btwMessage = null;
4957}
58+if (this.repeatableSystemMessage?.component === component) {
59+this.repeatableSystemMessage = null;
60+}
5061}
51625263private pruneOverflow() {
@@ -65,11 +76,17 @@ export class ChatLog extends Container {
6576this.pruneOverflow();
6677}
677879+private appendNonSystem(component: Component) {
80+this.repeatableSystemMessage = null;
81+this.append(component);
82+}
83+6884clearAll(opts?: { preservePendingUsers?: boolean }) {
6985this.clear();
7086this.toolById.clear();
7187this.streamingRuns.clear();
7288this.btwMessage = null;
89+this.repeatableSystemMessage = null;
7390if (!opts?.preservePendingUsers) {
7491this.pendingUsers.clear();
7592}
@@ -80,7 +97,7 @@ export class ChatLog extends Container {
8097if (this.children.includes(entry.component)) {
8198continue;
8299}
83-this.append(entry.component);
100+this.appendNonSystem(entry.component);
84101}
85102}
86103@@ -91,19 +108,42 @@ export class ChatLog extends Container {
91108this.pendingUsers.clear();
92109}
9311094-private createSystemMessage(text: string): Container {
111+private formatRepeatedSystemText(text: string, count: number) {
112+return count > 1 ? `${text} x${count}` : text;
113+}
114+115+private createSystemMessage(text: string): RepeatableSystemMessage {
95116const entry = new Container();
117+const textNode = new Text(theme.system(text), 1, 0);
96118entry.addChild(new Spacer(1));
97-entry.addChild(new Text(theme.system(text), 1, 0));
98-return entry;
119+entry.addChild(textNode);
120+return {
121+component: entry,
122+ textNode,
123+baseText: text,
124+count: 1,
125+};
99126}
100127101-addSystem(text: string) {
102-this.append(this.createSystemMessage(text));
128+addSystem(text: string, opts?: { coalesceConsecutive?: boolean }) {
129+if (
130+opts?.coalesceConsecutive &&
131+this.repeatableSystemMessage?.baseText === text &&
132+this.children[this.children.length - 1] === this.repeatableSystemMessage.component
133+) {
134+this.repeatableSystemMessage.count += 1;
135+this.repeatableSystemMessage.textNode.setText(
136+theme.system(this.formatRepeatedSystemText(text, this.repeatableSystemMessage.count)),
137+);
138+return;
139+}
140+const message = this.createSystemMessage(text);
141+this.append(message.component);
142+this.repeatableSystemMessage = opts?.coalesceConsecutive ? message : null;
103143}
104144105145addUser(text: string) {
106-this.append(new UserMessageComponent(text));
146+this.appendNonSystem(new UserMessageComponent(text));
107147}
108148109149addPendingUser(runId: string, text: string, createdAt = Date.now()) {
@@ -116,7 +156,7 @@ export class ChatLog extends Container {
116156}
117157const component = new UserMessageComponent(text);
118158this.pendingUsers.set(runId, { component, text, createdAt });
119-this.append(component);
159+this.appendNonSystem(component);
120160return component;
121161}
122162@@ -192,7 +232,7 @@ export class ChatLog extends Container {
192232}
193233const component = new AssistantMessageComponent(text);
194234this.streamingRuns.set(effectiveRunId, component);
195-this.append(component);
235+this.appendNonSystem(component);
196236return component;
197237}
198238@@ -214,7 +254,7 @@ export class ChatLog extends Container {
214254this.streamingRuns.delete(effectiveRunId);
215255return;
216256}
217-this.append(new AssistantMessageComponent(text));
257+this.appendNonSystem(new AssistantMessageComponent(text));
218258}
219259220260dropAssistant(runId?: string) {
@@ -232,13 +272,13 @@ export class ChatLog extends Container {
232272this.btwMessage.setResult(params);
233273if (this.children[this.children.length - 1] !== this.btwMessage) {
234274this.removeChild(this.btwMessage);
235-this.append(this.btwMessage);
275+this.appendNonSystem(this.btwMessage);
236276}
237277return this.btwMessage;
238278}
239279const component = new BtwInlineMessage(params);
240280this.btwMessage = component;
241-this.append(component);
281+this.appendNonSystem(component);
242282return component;
243283}
244284@@ -263,7 +303,7 @@ export class ChatLog extends Container {
263303const component = new ToolExecutionComponent(toolName, args);
264304component.setExpanded(this.toolsExpanded);
265305this.toolById.set(toolCallId, component);
266-this.append(component);
306+this.appendNonSystem(component);
267307return component;
268308}
269309此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。