@openrouter/agent 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/inner-loop/call-model.d.ts.map +1 -1
- package/esm/inner-loop/call-model.js +2 -1
- package/esm/inner-loop/call-model.js.map +1 -1
- package/esm/lib/async-params.d.ts +9 -1
- package/esm/lib/async-params.d.ts.map +1 -1
- package/esm/lib/async-params.js +1 -0
- package/esm/lib/async-params.js.map +1 -1
- package/esm/lib/conversation-state.d.ts +1 -1
- package/esm/lib/conversation-state.d.ts.map +1 -1
- package/esm/lib/conversation-state.js +48 -13
- package/esm/lib/conversation-state.js.map +1 -1
- package/esm/lib/hooks-schemas.d.ts +3 -7
- package/esm/lib/hooks-schemas.d.ts.map +1 -1
- package/esm/lib/hooks-schemas.js.map +1 -1
- package/esm/lib/model-result.d.ts +29 -1
- package/esm/lib/model-result.d.ts.map +1 -1
- package/esm/lib/model-result.js +400 -108
- package/esm/lib/model-result.js.map +1 -1
- package/esm/lib/reusable-stream.d.ts +30 -3
- package/esm/lib/reusable-stream.d.ts.map +1 -1
- package/esm/lib/reusable-stream.js +127 -32
- package/esm/lib/reusable-stream.js.map +1 -1
- package/esm/lib/tool-event-broadcaster.d.ts +18 -5
- package/esm/lib/tool-event-broadcaster.d.ts.map +1 -1
- package/esm/lib/tool-event-broadcaster.js +90 -19
- package/esm/lib/tool-event-broadcaster.js.map +1 -1
- package/esm/lib/tool-types.d.ts +5 -1
- package/esm/lib/tool-types.d.ts.map +1 -1
- package/esm/lib/tool-types.js.map +1 -1
- package/esm/lib/turn-context.d.ts +1 -1
- package/esm/lib/turn-context.d.ts.map +1 -1
- package/esm/lib/turn-context.js +1 -2
- package/esm/lib/turn-context.js.map +1 -1
- package/esm/mcp/version.d.ts +1 -1
- package/esm/mcp/version.js +1 -1
- package/package.json +5 -1
|
@@ -5,19 +5,32 @@
|
|
|
5
5
|
* Key features:
|
|
6
6
|
* - Multiple concurrent consumers with independent read positions
|
|
7
7
|
* - New consumers can attach while streaming is active
|
|
8
|
-
* -
|
|
8
|
+
* - Full replay for delayed and sequential consumers by default
|
|
9
|
+
* - Opt-in active-consumer replay compaction for bounded memory
|
|
9
10
|
* - Each consumer can read at their own pace
|
|
10
11
|
*/
|
|
12
|
+
export type StreamReplay = 'full' | 'active-consumers';
|
|
13
|
+
export interface ReusableReadableStreamOptions<T> {
|
|
14
|
+
streamReplay?: StreamReplay;
|
|
15
|
+
onValue?: (value: T) => void;
|
|
16
|
+
isTerminalValue?: (value: T) => boolean;
|
|
17
|
+
}
|
|
11
18
|
export declare class ReusableReadableStream<T> {
|
|
12
19
|
private sourceStream;
|
|
13
20
|
private buffer;
|
|
21
|
+
private bufferHead;
|
|
22
|
+
private trimOffset;
|
|
14
23
|
private consumers;
|
|
15
24
|
private nextConsumerId;
|
|
16
25
|
private sourceReader;
|
|
17
26
|
private sourceComplete;
|
|
18
27
|
private sourceError;
|
|
19
28
|
private pumpStarted;
|
|
20
|
-
|
|
29
|
+
private sourceCancelPromise;
|
|
30
|
+
private readonly streamReplay;
|
|
31
|
+
private readonly onValue;
|
|
32
|
+
private readonly isTerminalValue;
|
|
33
|
+
constructor(sourceStream: ReadableStream<T>, options?: ReusableReadableStreamOptions<T>);
|
|
21
34
|
/**
|
|
22
35
|
* True once the source stream has been fully read into the buffer.
|
|
23
36
|
* A fresh consumer created after this point replays the retained buffer
|
|
@@ -35,13 +48,27 @@ export declare class ReusableReadableStream<T> {
|
|
|
35
48
|
findLastBuffered(predicate: (item: T) => boolean): T | undefined;
|
|
36
49
|
/**
|
|
37
50
|
* Create a new consumer that can independently iterate over the stream.
|
|
38
|
-
*
|
|
51
|
+
* Full-replay consumers start at position 0. Active-consumer replay starts
|
|
52
|
+
* at the current trim watermark. Multiple attached consumers advance
|
|
53
|
+
* independently in either mode.
|
|
39
54
|
*/
|
|
40
55
|
createConsumer(): AsyncIterableIterator<T>;
|
|
56
|
+
private trimConsumed;
|
|
57
|
+
/**
|
|
58
|
+
* Drop the retained backlog once no consumer can ever read it again.
|
|
59
|
+
* Before the first consumer joins, the backlog IS the catch-up history late
|
|
60
|
+
* joiners replay — retain it. Once at least one consumer has existed and
|
|
61
|
+
* none remain, any future consumer starts at the watermark anyway, so the
|
|
62
|
+
* retained bytes would stay pinned until GC for nothing. `findLastBuffered`
|
|
63
|
+
* callers that must observe terminal events after teardown should capture
|
|
64
|
+
* them via `onValue` instead of relying on the backlog surviving.
|
|
65
|
+
*/
|
|
66
|
+
private dropUnreadBacklog;
|
|
41
67
|
/**
|
|
42
68
|
* Start pumping data from the source stream into the buffer
|
|
43
69
|
*/
|
|
44
70
|
private startPump;
|
|
71
|
+
private cancelSourceReader;
|
|
45
72
|
/**
|
|
46
73
|
* Notify all waiting consumers that new data is available
|
|
47
74
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reusable-stream.d.ts","sourceRoot":"","sources":["../../src/lib/reusable-stream.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"reusable-stream.d.ts","sourceRoot":"","sources":["../../src/lib/reusable-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAEvD,MAAM,WAAW,6BAA6B,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;CACzC;AAED,qBAAa,sBAAsB,CAAC,CAAC;IAiBjC,OAAO,CAAC,YAAY;IAhBtB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;gBAG5D,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,EACvC,OAAO,GAAE,6BAA6B,CAAC,CAAC,CAAM;IAOhD;;;;OAIG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;;;;;OAOG;IACH,gBAAgB,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,GAAG,SAAS;IAUhE;;;;;OAKG;IACH,cAAc,IAAI,qBAAqB,CAAC,CAAC,CAAC;IAiI1C,OAAO,CAAC,YAAY;IAqCpB;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkDjB,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAwB9B"}
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A reusable readable stream that allows multiple consumers to read from the same source stream
|
|
3
|
-
* concurrently while it's actively streaming, without forcing consumers to wait for full buffering.
|
|
4
|
-
*
|
|
5
|
-
* Key features:
|
|
6
|
-
* - Multiple concurrent consumers with independent read positions
|
|
7
|
-
* - New consumers can attach while streaming is active
|
|
8
|
-
* - Efficient memory management with automatic cleanup
|
|
9
|
-
* - Each consumer can read at their own pace
|
|
10
|
-
*/
|
|
11
1
|
export class ReusableReadableStream {
|
|
12
|
-
constructor(sourceStream) {
|
|
2
|
+
constructor(sourceStream, options = {}) {
|
|
13
3
|
this.sourceStream = sourceStream;
|
|
14
4
|
this.buffer = [];
|
|
5
|
+
this.bufferHead = 0;
|
|
6
|
+
// Consumer positions are absolute. Buffer index = bufferHead + position - trimOffset.
|
|
7
|
+
this.trimOffset = 0;
|
|
15
8
|
this.consumers = new Map();
|
|
16
9
|
this.nextConsumerId = 0;
|
|
17
10
|
this.sourceReader = null;
|
|
18
11
|
this.sourceComplete = false;
|
|
19
12
|
this.sourceError = null;
|
|
20
13
|
this.pumpStarted = false;
|
|
14
|
+
this.sourceCancelPromise = null;
|
|
15
|
+
this.streamReplay = options.streamReplay ?? 'full';
|
|
16
|
+
this.onValue = options.onValue;
|
|
17
|
+
this.isTerminalValue = options.isTerminalValue;
|
|
21
18
|
}
|
|
22
19
|
/**
|
|
23
20
|
* True once the source stream has been fully read into the buffer.
|
|
@@ -36,9 +33,9 @@ export class ReusableReadableStream {
|
|
|
36
33
|
* buffer asynchronously.
|
|
37
34
|
*/
|
|
38
35
|
findLastBuffered(predicate) {
|
|
39
|
-
for (let i = this.buffer.length - 1; i >=
|
|
36
|
+
for (let i = this.buffer.length - 1; i >= this.bufferHead; i--) {
|
|
40
37
|
const item = this.buffer[i];
|
|
41
|
-
if (predicate(item)) {
|
|
38
|
+
if (item !== undefined && predicate(item)) {
|
|
42
39
|
return item;
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -46,12 +43,14 @@ export class ReusableReadableStream {
|
|
|
46
43
|
}
|
|
47
44
|
/**
|
|
48
45
|
* Create a new consumer that can independently iterate over the stream.
|
|
49
|
-
*
|
|
46
|
+
* Full-replay consumers start at position 0. Active-consumer replay starts
|
|
47
|
+
* at the current trim watermark. Multiple attached consumers advance
|
|
48
|
+
* independently in either mode.
|
|
50
49
|
*/
|
|
51
50
|
createConsumer() {
|
|
52
51
|
const consumerId = this.nextConsumerId++;
|
|
53
52
|
const state = {
|
|
54
|
-
position:
|
|
53
|
+
position: this.trimOffset,
|
|
55
54
|
waitingPromise: null,
|
|
56
55
|
cancelled: false,
|
|
57
56
|
};
|
|
@@ -65,23 +64,20 @@ export class ReusableReadableStream {
|
|
|
65
64
|
return {
|
|
66
65
|
async next() {
|
|
67
66
|
const consumer = self.consumers.get(consumerId);
|
|
68
|
-
if (!consumer) {
|
|
67
|
+
if (!consumer || consumer.cancelled) {
|
|
69
68
|
return {
|
|
70
69
|
done: true,
|
|
71
70
|
value: undefined,
|
|
72
71
|
};
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// If we have buffered data at this position, return it
|
|
81
|
-
if (consumer.position < self.buffer.length) {
|
|
82
|
-
const value = self.buffer[consumer.position];
|
|
73
|
+
const bufferIndex = self.bufferHead + consumer.position - self.trimOffset;
|
|
74
|
+
if (bufferIndex < self.buffer.length) {
|
|
75
|
+
const value = self.buffer[bufferIndex];
|
|
76
|
+
if (value === undefined) {
|
|
77
|
+
throw new Error('ReusableReadableStream buffer invariant violated: consumed slot was cleared');
|
|
78
|
+
}
|
|
83
79
|
consumer.position++;
|
|
84
|
-
|
|
80
|
+
self.trimConsumed();
|
|
85
81
|
return {
|
|
86
82
|
done: false,
|
|
87
83
|
value,
|
|
@@ -110,7 +106,9 @@ export class ReusableReadableStream {
|
|
|
110
106
|
// Immediately check if we should resolve after setting up the promise
|
|
111
107
|
// This handles the case where data arrived or source completed
|
|
112
108
|
// between our initial checks and promise creation
|
|
113
|
-
if (self.sourceComplete ||
|
|
109
|
+
if (self.sourceComplete ||
|
|
110
|
+
self.sourceError ||
|
|
111
|
+
self.bufferHead + consumer.position - self.trimOffset < self.buffer.length) {
|
|
114
112
|
resolve();
|
|
115
113
|
}
|
|
116
114
|
});
|
|
@@ -124,7 +122,15 @@ export class ReusableReadableStream {
|
|
|
124
122
|
const consumer = self.consumers.get(consumerId);
|
|
125
123
|
if (consumer) {
|
|
126
124
|
consumer.cancelled = true;
|
|
125
|
+
// Wake a pending next() so it does not hang forever on a consumer
|
|
126
|
+
// that has just been removed; the re-run next() observes the
|
|
127
|
+
// removal and reports done.
|
|
128
|
+
if (consumer.waitingPromise) {
|
|
129
|
+
consumer.waitingPromise.resolve();
|
|
130
|
+
consumer.waitingPromise = null;
|
|
131
|
+
}
|
|
127
132
|
self.consumers.delete(consumerId);
|
|
133
|
+
self.trimConsumed();
|
|
128
134
|
}
|
|
129
135
|
return {
|
|
130
136
|
done: true,
|
|
@@ -135,7 +141,14 @@ export class ReusableReadableStream {
|
|
|
135
141
|
const consumer = self.consumers.get(consumerId);
|
|
136
142
|
if (consumer) {
|
|
137
143
|
consumer.cancelled = true;
|
|
144
|
+
// Reject a pending next() with the same error so it does not hang
|
|
145
|
+
// on a consumer that has just been removed.
|
|
146
|
+
if (consumer.waitingPromise) {
|
|
147
|
+
consumer.waitingPromise.reject(e instanceof Error ? e : new Error(String(e)));
|
|
148
|
+
consumer.waitingPromise = null;
|
|
149
|
+
}
|
|
138
150
|
self.consumers.delete(consumerId);
|
|
151
|
+
self.trimConsumed();
|
|
139
152
|
}
|
|
140
153
|
throw e;
|
|
141
154
|
},
|
|
@@ -144,6 +157,59 @@ export class ReusableReadableStream {
|
|
|
144
157
|
},
|
|
145
158
|
};
|
|
146
159
|
}
|
|
160
|
+
trimConsumed() {
|
|
161
|
+
if (this.streamReplay === 'full') {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (this.consumers.size === 0) {
|
|
165
|
+
this.dropUnreadBacklog();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
let min = Number.POSITIVE_INFINITY;
|
|
169
|
+
for (const consumer of this.consumers.values()) {
|
|
170
|
+
if (consumer.position < min) {
|
|
171
|
+
min = consumer.position;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const nextHead = this.bufferHead + min - this.trimOffset;
|
|
175
|
+
if (nextHead <= this.bufferHead) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this.trimOffset = min;
|
|
179
|
+
if (nextHead === this.buffer.length) {
|
|
180
|
+
this.buffer = [];
|
|
181
|
+
this.bufferHead = 0;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.buffer.fill(undefined, this.bufferHead, nextHead);
|
|
185
|
+
if (nextHead >= BUFFER_COMPACTION_MIN_HEAD && nextHead * 2 >= this.buffer.length) {
|
|
186
|
+
this.buffer = this.buffer.slice(nextHead);
|
|
187
|
+
this.bufferHead = 0;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.bufferHead = nextHead;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Drop the retained backlog once no consumer can ever read it again.
|
|
194
|
+
* Before the first consumer joins, the backlog IS the catch-up history late
|
|
195
|
+
* joiners replay — retain it. Once at least one consumer has existed and
|
|
196
|
+
* none remain, any future consumer starts at the watermark anyway, so the
|
|
197
|
+
* retained bytes would stay pinned until GC for nothing. `findLastBuffered`
|
|
198
|
+
* callers that must observe terminal events after teardown should capture
|
|
199
|
+
* them via `onValue` instead of relying on the backlog surviving.
|
|
200
|
+
*/
|
|
201
|
+
dropUnreadBacklog() {
|
|
202
|
+
if (this.nextConsumerId === 0) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const dropped = this.buffer.length - this.bufferHead;
|
|
206
|
+
if (dropped === 0) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.trimOffset += dropped;
|
|
210
|
+
this.buffer = [];
|
|
211
|
+
this.bufferHead = 0;
|
|
212
|
+
}
|
|
147
213
|
/**
|
|
148
214
|
* Start pumping data from the source stream into the buffer
|
|
149
215
|
*/
|
|
@@ -153,11 +219,12 @@ export class ReusableReadableStream {
|
|
|
153
219
|
}
|
|
154
220
|
this.pumpStarted = true;
|
|
155
221
|
this.sourceReader = this.sourceStream.getReader();
|
|
222
|
+
const sourceReader = this.sourceReader;
|
|
156
223
|
// biome-ignore lint: IIFE used for fire-and-forget stream pump
|
|
157
224
|
void (async () => {
|
|
158
225
|
try {
|
|
159
226
|
while (true) {
|
|
160
|
-
const result = await
|
|
227
|
+
const result = await sourceReader.read();
|
|
161
228
|
if (result.done) {
|
|
162
229
|
this.sourceComplete = true;
|
|
163
230
|
this.notifyAllConsumers();
|
|
@@ -165,8 +232,20 @@ export class ReusableReadableStream {
|
|
|
165
232
|
}
|
|
166
233
|
// Add to buffer
|
|
167
234
|
this.buffer.push(result.value);
|
|
235
|
+
this.onValue?.(result.value);
|
|
168
236
|
// Notify waiting consumers
|
|
169
237
|
this.notifyAllConsumers();
|
|
238
|
+
if (this.isTerminalValue?.(result.value)) {
|
|
239
|
+
this.sourceComplete = true;
|
|
240
|
+
this.notifyAllConsumers();
|
|
241
|
+
try {
|
|
242
|
+
await this.cancelSourceReader(sourceReader);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// The terminal event is authoritative, cancellation is cleanup only.
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
170
249
|
}
|
|
171
250
|
}
|
|
172
251
|
catch (error) {
|
|
@@ -174,12 +253,19 @@ export class ReusableReadableStream {
|
|
|
174
253
|
this.notifyAllConsumers();
|
|
175
254
|
}
|
|
176
255
|
finally {
|
|
177
|
-
|
|
178
|
-
|
|
256
|
+
sourceReader.releaseLock();
|
|
257
|
+
if (this.sourceReader === sourceReader) {
|
|
258
|
+
this.sourceReader = null;
|
|
179
259
|
}
|
|
180
260
|
}
|
|
181
261
|
})();
|
|
182
262
|
}
|
|
263
|
+
cancelSourceReader(sourceReader) {
|
|
264
|
+
if (!this.sourceCancelPromise) {
|
|
265
|
+
this.sourceCancelPromise = sourceReader.cancel();
|
|
266
|
+
}
|
|
267
|
+
return this.sourceCancelPromise;
|
|
268
|
+
}
|
|
183
269
|
/**
|
|
184
270
|
* Notify all waiting consumers that new data is available
|
|
185
271
|
*/
|
|
@@ -208,11 +294,20 @@ export class ReusableReadableStream {
|
|
|
208
294
|
}
|
|
209
295
|
}
|
|
210
296
|
this.consumers.clear();
|
|
297
|
+
// Cancellation is terminal: every consumer is gone, so nobody can read
|
|
298
|
+
// the buffered backlog anymore. Drop it instead of pinning it until GC.
|
|
299
|
+
this.dropUnreadBacklog();
|
|
211
300
|
// Cancel the source stream
|
|
212
301
|
if (this.sourceReader) {
|
|
213
|
-
await this.sourceReader
|
|
214
|
-
this.sourceReader.releaseLock();
|
|
302
|
+
await this.cancelSourceReader(this.sourceReader);
|
|
215
303
|
}
|
|
304
|
+
/*
|
|
305
|
+
* The pump may have landed one in-flight chunk between the synchronous
|
|
306
|
+
* section above and reader cancellation — sweep once more so the buffer
|
|
307
|
+
* is empty regardless of microtask interleaving.
|
|
308
|
+
*/
|
|
309
|
+
this.dropUnreadBacklog();
|
|
216
310
|
}
|
|
217
311
|
}
|
|
312
|
+
const BUFFER_COMPACTION_MIN_HEAD = 1024;
|
|
218
313
|
//# sourceMappingURL=reusable-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reusable-stream.js","sourceRoot":"","sources":["../../src/lib/reusable-stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reusable-stream.js","sourceRoot":"","sources":["../../src/lib/reusable-stream.ts"],"names":[],"mappings":"AAmBA,MAAM,OAAO,sBAAsB;IAgBjC,YACU,YAA+B,EACvC,UAA4C,EAAE;QADtC,iBAAY,GAAZ,YAAY,CAAmB;QAhBjC,WAAM,GAAsB,EAAE,CAAC;QAC/B,eAAU,GAAG,CAAC,CAAC;QACvB,sFAAsF;QAC9E,eAAU,GAAG,CAAC,CAAC;QACf,cAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;QAC7C,mBAAc,GAAG,CAAC,CAAC;QACnB,iBAAY,GAA0C,IAAI,CAAC;QAC3D,mBAAc,GAAG,KAAK,CAAC;QACvB,gBAAW,GAAiB,IAAI,CAAC;QACjC,gBAAW,GAAG,KAAK,CAAC;QACpB,wBAAmB,GAAyB,IAAI,CAAC;QASvD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;;;;;;OAOG;IACH,gBAAgB,CAAC,SAA+B;QAC9C,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1C,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;OAKG;IACH,cAAc;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACzC,MAAM,KAAK,GAAkB;YAC3B,QAAQ,EAAE,IAAI,CAAC,UAAU;YACzB,cAAc,EAAE,IAAI;YACpB,SAAS,EAAE,KAAK;SACjB,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAEtC,yDAAyD;QACzD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,CAAC;QAED,4DAA4D;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,OAAO;YACL,KAAK,CAAC,IAAI;gBACR,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;oBACpC,OAAO;wBACL,IAAI,EAAE,IAAI;wBACV,KAAK,EAAE,SAAS;qBACjB,CAAC;gBACJ,CAAC;gBAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;gBAC1E,IAAI,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,MAAM,IAAI,KAAK,CACb,6EAA6E,CAC9E,CAAC;oBACJ,CAAC;oBACD,QAAQ,CAAC,QAAQ,EAAE,CAAC;oBACpB,IAAI,CAAC,YAAY,EAAE,CAAC;oBACpB,OAAO;wBACL,IAAI,EAAE,KAAK;wBACX,KAAK;qBACN,CAAC;gBACJ,CAAC;gBAED,8DAA8D;gBAC9D,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;oBACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;oBAClC,OAAO;wBACL,IAAI,EAAE,IAAI;wBACV,KAAK,EAAE,SAAS;qBACjB,CAAC;gBACJ,CAAC;gBAED,uCAAuC;gBACvC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACrB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;oBAClC,MAAM,IAAI,CAAC,WAAW,CAAC;gBACzB,CAAC;gBAED,2DAA2D;gBAC3D,mEAAmE;gBACnE,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACxD,QAAQ,CAAC,cAAc,GAAG;wBACxB,OAAO;wBACP,MAAM;qBACP,CAAC;oBAEF,sEAAsE;oBACtE,+DAA+D;oBAC/D,kDAAkD;oBAClD,IACE,IAAI,CAAC,cAAc;wBACnB,IAAI,CAAC,WAAW;wBAChB,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAC1E,CAAC;wBACD,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,MAAM,WAAW,CAAC;gBAElB,gDAAgD;gBAChD,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC;gBAE/B,wCAAwC;gBACxC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,CAAC;YAED,KAAK,CAAC,MAAM;gBACV,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChD,IAAI,QAAQ,EAAE,CAAC;oBACb,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;oBAC1B,kEAAkE;oBAClE,6DAA6D;oBAC7D,4BAA4B;oBAC5B,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;wBAC5B,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;wBAClC,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC;oBACjC,CAAC;oBACD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;oBAClC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,CAAC;gBACD,OAAO;oBACL,IAAI,EAAE,IAAI;oBACV,KAAK,EAAE,SAAS;iBACjB,CAAC;YACJ,CAAC;YAED,KAAK,CAAC,KAAK,CAAC,CAAW;gBACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChD,IAAI,QAAQ,EAAE,CAAC;oBACb,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;oBAC1B,kEAAkE;oBAClE,4CAA4C;oBAC5C,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;wBAC5B,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC9E,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC;oBACjC,CAAC;oBACD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;oBAClC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,CAAC;gBACD,MAAM,CAAC,CAAC;YACV,CAAC;YAED,CAAC,MAAM,CAAC,aAAa,CAAC;gBACpB,OAAO,IAAI,CAAC;YACd,CAAC;SACF,CAAC;IACJ,CAAC;IAEO,YAAY;QAClB,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC;QACnC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,IAAI,QAAQ,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;gBAC5B,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QACzD,IAAI,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QACtB,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACvD,IAAI,QAAQ,IAAI,0BAA0B,IAAI,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACjF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED;;;;;;;;OAQG;IACK,iBAAiB;QACvB,IAAI,IAAI,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QACrD,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;IACtB,CAAC;IAED;;OAEG;IACK,SAAS;QACf,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;QAElD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACvC,+DAA+D;QAC/D,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;oBAEzC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;wBAChB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;wBAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;wBAC1B,MAAM;oBACR,CAAC;oBAED,gBAAgB;oBAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAC/B,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAE7B,2BAA2B;oBAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBAE1B,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBACzC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;wBAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;wBAC1B,IAAI,CAAC;4BACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;wBAC9C,CAAC;wBAAC,MAAM,CAAC;4BACP,qEAAqE;wBACvE,CAAC;wBACD,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7E,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,WAAW,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;oBACvC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAEO,kBAAkB,CAAC,YAA4C;QACrE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC9B,IAAI,CAAC,mBAAmB,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACrB,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACnD,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;gBACpC,CAAC;gBACD,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,uBAAuB;QACvB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;YAC1B,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC5B,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YACpC,CAAC;QACH,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAEvB,uEAAuE;QACvE,wEAAwE;QACxE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,2BAA2B;QAC3B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC;QACD;;;;WAIG;QACH,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;CACF;AAWD,MAAM,0BAA0B,GAAG,IAAI,CAAC"}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
import type { StreamReplay } from './reusable-stream.js';
|
|
1
2
|
/**
|
|
2
3
|
* A push-based event broadcaster that supports multiple concurrent consumers.
|
|
3
4
|
* Similar to ReusableReadableStream but for push-based events from tool execution.
|
|
4
5
|
*
|
|
5
|
-
* Each consumer gets their own position in the buffer
|
|
6
|
-
*
|
|
7
|
-
* tool preliminary results to multiple consumers simultaneously.
|
|
6
|
+
* Each consumer gets their own position in the buffer. Full replay is the
|
|
7
|
+
* default, and active-consumer replay can compact consumed events.
|
|
8
8
|
*
|
|
9
9
|
* @template T - The event type being broadcast
|
|
10
10
|
*/
|
|
11
11
|
export declare class ToolEventBroadcaster<T> {
|
|
12
|
+
private readonly streamReplay;
|
|
12
13
|
private buffer;
|
|
14
|
+
private bufferHead;
|
|
15
|
+
private trimOffset;
|
|
13
16
|
private consumers;
|
|
14
17
|
private nextConsumerId;
|
|
15
18
|
private isComplete;
|
|
16
19
|
private completionError;
|
|
20
|
+
constructor(streamReplay?: StreamReplay);
|
|
17
21
|
/**
|
|
18
22
|
* Push a new event to all consumers.
|
|
19
23
|
* Events are buffered so late-joining consumers can catch up.
|
|
@@ -32,10 +36,19 @@ export declare class ToolEventBroadcaster<T> {
|
|
|
32
36
|
private cleanup;
|
|
33
37
|
/**
|
|
34
38
|
* Create a new consumer that can independently iterate over events.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
39
|
+
* Full-replay consumers start at position 0. Active-consumer replay starts
|
|
40
|
+
* at the current trim watermark.
|
|
37
41
|
*/
|
|
38
42
|
createConsumer(): AsyncIterableIterator<T>;
|
|
43
|
+
private trimConsumed;
|
|
44
|
+
/**
|
|
45
|
+
* Drop the retained backlog once no consumer can ever read it again.
|
|
46
|
+
* Before the first consumer joins, the buffer IS the catch-up history late
|
|
47
|
+
* joiners replay — retain it. Once at least one consumer has existed and
|
|
48
|
+
* none remain, any future consumer starts at the watermark anyway, so the
|
|
49
|
+
* retained events would stay pinned until GC for nothing.
|
|
50
|
+
*/
|
|
51
|
+
private dropUnreadBacklog;
|
|
39
52
|
/**
|
|
40
53
|
* Notify all waiting consumers that new data is available or stream completed
|
|
41
54
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-event-broadcaster.d.ts","sourceRoot":"","sources":["../../src/lib/tool-event-broadcaster.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tool-event-broadcaster.d.ts","sourceRoot":"","sources":["../../src/lib/tool-event-broadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB,CAAC,CAAC;IAUrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IATzC,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAsB;gBAEhB,YAAY,GAAE,YAAqB;IAEhE;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAQpB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAQ7B;;;OAGG;IACH,OAAO,CAAC,OAAO;IAQf;;;;OAIG;IACH,cAAc,IAAI,qBAAqB,CAAC,CAAC,CAAC;IAuH1C,OAAO,CAAC,YAAY;IAqCpB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAY/B"}
|
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
* A push-based event broadcaster that supports multiple concurrent consumers.
|
|
3
3
|
* Similar to ReusableReadableStream but for push-based events from tool execution.
|
|
4
4
|
*
|
|
5
|
-
* Each consumer gets their own position in the buffer
|
|
6
|
-
*
|
|
7
|
-
* tool preliminary results to multiple consumers simultaneously.
|
|
5
|
+
* Each consumer gets their own position in the buffer. Full replay is the
|
|
6
|
+
* default, and active-consumer replay can compact consumed events.
|
|
8
7
|
*
|
|
9
8
|
* @template T - The event type being broadcast
|
|
10
9
|
*/
|
|
11
10
|
export class ToolEventBroadcaster {
|
|
12
|
-
constructor() {
|
|
11
|
+
constructor(streamReplay = 'full') {
|
|
12
|
+
this.streamReplay = streamReplay;
|
|
13
13
|
this.buffer = [];
|
|
14
|
+
this.bufferHead = 0;
|
|
15
|
+
// Consumer positions are absolute. Buffer index = bufferHead + position - trimOffset.
|
|
16
|
+
this.trimOffset = 0;
|
|
14
17
|
this.consumers = new Map();
|
|
15
18
|
this.nextConsumerId = 0;
|
|
16
19
|
this.isComplete = false;
|
|
@@ -45,19 +48,20 @@ export class ToolEventBroadcaster {
|
|
|
45
48
|
*/
|
|
46
49
|
cleanup() {
|
|
47
50
|
// Only cleanup if complete and all consumers are done
|
|
48
|
-
if (this.isComplete && this.consumers.size === 0) {
|
|
51
|
+
if (this.streamReplay === 'active-consumers' && this.isComplete && this.consumers.size === 0) {
|
|
49
52
|
this.buffer = [];
|
|
53
|
+
this.bufferHead = 0;
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Create a new consumer that can independently iterate over events.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
58
|
+
* Full-replay consumers start at position 0. Active-consumer replay starts
|
|
59
|
+
* at the current trim watermark.
|
|
56
60
|
*/
|
|
57
61
|
createConsumer() {
|
|
58
62
|
const consumerId = this.nextConsumerId++;
|
|
59
63
|
const state = {
|
|
60
|
-
position:
|
|
64
|
+
position: this.trimOffset,
|
|
61
65
|
waitingPromise: null,
|
|
62
66
|
cancelled: false,
|
|
63
67
|
};
|
|
@@ -67,22 +71,20 @@ export class ToolEventBroadcaster {
|
|
|
67
71
|
return {
|
|
68
72
|
async next() {
|
|
69
73
|
const consumer = self.consumers.get(consumerId);
|
|
70
|
-
if (!consumer) {
|
|
74
|
+
if (!consumer || consumer.cancelled) {
|
|
71
75
|
return {
|
|
72
76
|
done: true,
|
|
73
77
|
value: undefined,
|
|
74
78
|
};
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// Return buffered event if available
|
|
83
|
-
if (consumer.position < self.buffer.length) {
|
|
84
|
-
const value = self.buffer[consumer.position];
|
|
80
|
+
const bufferIndex = self.bufferHead + consumer.position - self.trimOffset;
|
|
81
|
+
if (bufferIndex < self.buffer.length) {
|
|
82
|
+
const value = self.buffer[bufferIndex];
|
|
83
|
+
if (value === undefined) {
|
|
84
|
+
throw new Error('ToolEventBroadcaster buffer invariant violated: consumed slot was cleared');
|
|
85
|
+
}
|
|
85
86
|
consumer.position++;
|
|
87
|
+
self.trimConsumed();
|
|
86
88
|
return {
|
|
87
89
|
done: false,
|
|
88
90
|
value,
|
|
@@ -107,7 +109,9 @@ export class ToolEventBroadcaster {
|
|
|
107
109
|
reject,
|
|
108
110
|
};
|
|
109
111
|
// Immediately check if we should resolve after setting up promise
|
|
110
|
-
if (self.isComplete ||
|
|
112
|
+
if (self.isComplete ||
|
|
113
|
+
self.completionError ||
|
|
114
|
+
self.bufferHead + consumer.position - self.trimOffset < self.buffer.length) {
|
|
111
115
|
resolve();
|
|
112
116
|
}
|
|
113
117
|
});
|
|
@@ -120,7 +124,15 @@ export class ToolEventBroadcaster {
|
|
|
120
124
|
const consumer = self.consumers.get(consumerId);
|
|
121
125
|
if (consumer) {
|
|
122
126
|
consumer.cancelled = true;
|
|
127
|
+
// Wake a pending next() so it does not hang forever on a consumer
|
|
128
|
+
// that has just been removed; the re-run next() observes the
|
|
129
|
+
// removal and reports done.
|
|
130
|
+
if (consumer.waitingPromise) {
|
|
131
|
+
consumer.waitingPromise.resolve();
|
|
132
|
+
consumer.waitingPromise = null;
|
|
133
|
+
}
|
|
123
134
|
self.consumers.delete(consumerId);
|
|
135
|
+
self.trimConsumed();
|
|
124
136
|
self.cleanup();
|
|
125
137
|
}
|
|
126
138
|
return {
|
|
@@ -132,7 +144,14 @@ export class ToolEventBroadcaster {
|
|
|
132
144
|
const consumer = self.consumers.get(consumerId);
|
|
133
145
|
if (consumer) {
|
|
134
146
|
consumer.cancelled = true;
|
|
147
|
+
// Reject a pending next() with the same error so it does not hang
|
|
148
|
+
// on a consumer that has just been removed.
|
|
149
|
+
if (consumer.waitingPromise) {
|
|
150
|
+
consumer.waitingPromise.reject(e instanceof Error ? e : new Error(String(e)));
|
|
151
|
+
consumer.waitingPromise = null;
|
|
152
|
+
}
|
|
135
153
|
self.consumers.delete(consumerId);
|
|
154
|
+
self.trimConsumed();
|
|
136
155
|
self.cleanup();
|
|
137
156
|
}
|
|
138
157
|
throw e;
|
|
@@ -142,6 +161,57 @@ export class ToolEventBroadcaster {
|
|
|
142
161
|
},
|
|
143
162
|
};
|
|
144
163
|
}
|
|
164
|
+
trimConsumed() {
|
|
165
|
+
if (this.streamReplay === 'full') {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (this.consumers.size === 0) {
|
|
169
|
+
this.dropUnreadBacklog();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
let min = Number.POSITIVE_INFINITY;
|
|
173
|
+
for (const consumer of this.consumers.values()) {
|
|
174
|
+
if (consumer.position < min) {
|
|
175
|
+
min = consumer.position;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const nextHead = this.bufferHead + min - this.trimOffset;
|
|
179
|
+
if (nextHead <= this.bufferHead) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.trimOffset = min;
|
|
183
|
+
if (nextHead === this.buffer.length) {
|
|
184
|
+
this.buffer = [];
|
|
185
|
+
this.bufferHead = 0;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.buffer.fill(undefined, this.bufferHead, nextHead);
|
|
189
|
+
if (nextHead >= BUFFER_COMPACTION_MIN_HEAD && nextHead * 2 >= this.buffer.length) {
|
|
190
|
+
this.buffer = this.buffer.slice(nextHead);
|
|
191
|
+
this.bufferHead = 0;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.bufferHead = nextHead;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Drop the retained backlog once no consumer can ever read it again.
|
|
198
|
+
* Before the first consumer joins, the buffer IS the catch-up history late
|
|
199
|
+
* joiners replay — retain it. Once at least one consumer has existed and
|
|
200
|
+
* none remain, any future consumer starts at the watermark anyway, so the
|
|
201
|
+
* retained events would stay pinned until GC for nothing.
|
|
202
|
+
*/
|
|
203
|
+
dropUnreadBacklog() {
|
|
204
|
+
if (this.nextConsumerId === 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const dropped = this.buffer.length - this.bufferHead;
|
|
208
|
+
if (dropped === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.trimOffset += dropped;
|
|
212
|
+
this.buffer = [];
|
|
213
|
+
this.bufferHead = 0;
|
|
214
|
+
}
|
|
145
215
|
/**
|
|
146
216
|
* Notify all waiting consumers that new data is available or stream completed
|
|
147
217
|
*/
|
|
@@ -159,4 +229,5 @@ export class ToolEventBroadcaster {
|
|
|
159
229
|
}
|
|
160
230
|
}
|
|
161
231
|
}
|
|
232
|
+
const BUFFER_COMPACTION_MIN_HEAD = 1024;
|
|
162
233
|
//# sourceMappingURL=tool-event-broadcaster.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-event-broadcaster.js","sourceRoot":"","sources":["../../src/lib/tool-event-broadcaster.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-event-broadcaster.js","sourceRoot":"","sources":["../../src/lib/tool-event-broadcaster.ts"],"names":[],"mappings":"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"}
|