MCPcopy
hub / github.com/CopilotKit/CopilotKit / ChunkedMessageStream

Class ChunkedMessageStream

packages/bot-slack/src/chunked-message-stream.ts:53–180  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

51const DEFAULT_LIMIT = 3500;
52
53export class ChunkedMessageStream {
54 private buffer = "";
55 /** Sorted positions where a chunk ends (= where the next chunk begins). */
56 private boundaries: number[] = [];
57 private streams: MessageStream[] = [];
58 /** Serialises new-chunk creation so async postPlaceholder calls don't race. */
59 private setupPromise: Promise<void> = Promise.resolve();
60 private finished = false;
61
62 private readonly limit: number;
63 private readonly minIntervalMs: number | undefined;
64 private readonly postPlaceholder: (text: string) => Promise<string>;
65 private readonly updateAt: (ts: string, text: string) => Promise<void>;
66 private readonly transform: (text: string) => string;
67
68 constructor(config: ChunkedMessageStreamConfig) {
69 this.limit = config.limit ?? DEFAULT_LIMIT;
70 this.minIntervalMs = config.minIntervalMs;
71 this.postPlaceholder = config.postPlaceholder;
72 this.updateAt = config.updateAt;
73 this.transform = config.transform ?? ((t) => t);
74 }
75
76 append(fullText: string): void {
77 if (this.finished) return;
78 if (fullText === this.buffer) return;
79 this.buffer = fullText;
80 this.refreezeBoundaries();
81 // Make sure we have one Slack message per chunk, then dispatch.
82 this.setupPromise = this.setupPromise.then(() =>
83 this.ensureStreamsAndDispatch(),
84 );
85 }
86
87 async finish(): Promise<void> {
88 this.finished = true;
89 // Drain any pending setup, then a final dispatch, then finish each stream.
90 this.setupPromise = this.setupPromise.then(() =>
91 this.ensureStreamsAndDispatch(),
92 );
93 await this.setupPromise;
94 for (const s of this.streams) await s.finish();
95 }
96
97 /** Returns the number of Slack messages this stream has posted so far. */
98 get chunkCount(): number {
99 return this.streams.length;
100 }
101
102 /**
103 * Walk forward from the last frozen boundary, freezing new ones whenever
104 * the active chunk's length exceeds the soft limit. Once frozen, a
105 * boundary doesn't move.
106 *
107 * Special case (block-keeps-whole): if the chosen boundary lands INSIDE
108 * an open fenced code block, we try to move the boundary BACK to the
109 * position right before the fence opener, so the *whole* block lives in
110 * the next Slack message rather than being split. The previous chunk

Callers

nothing calls this directly

Calls 1

resolveMethod · 0.80

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…