orizu 0.5.32 → 0.5.33
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/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +8 -3
- package/dist/hosted-run-event-sink.d.ts +10 -1
- package/dist/hosted-run-event-sink.d.ts.map +1 -1
- package/dist/hosted-run-event-sink.js +69 -8
- package/package.json +1 -1
- package/src/hosted-loop.ts +8 -3
- package/src/hosted-run-event-sink.ts +74 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/hosted-loop.js
CHANGED
|
@@ -691,7 +691,10 @@ export async function runHostedLoop(opts) {
|
|
|
691
691
|
await sleep(delayMs);
|
|
692
692
|
try {
|
|
693
693
|
await sink.finish(error.intendedStatus, error.finishOptions);
|
|
694
|
-
|
|
694
|
+
// A timer flush may have met the server's 404/410 during the sleep,
|
|
695
|
+
// making finish() a no-op: honour the server, as the drain does.
|
|
696
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus;
|
|
697
|
+
return { status, agentSessionId, installOk, error: null };
|
|
695
698
|
}
|
|
696
699
|
catch (retryError) {
|
|
697
700
|
// The run went terminal server-side while we were retrying: accept
|
|
@@ -705,8 +708,10 @@ export async function runHostedLoop(opts) {
|
|
|
705
708
|
}
|
|
706
709
|
if (sink.sealed) {
|
|
707
710
|
// A concurrent/preceding delivery landed after all — the record is
|
|
708
|
-
// terminal server-side with our intended status semantics preserved
|
|
709
|
-
|
|
711
|
+
// terminal server-side with our intended status semantics preserved —
|
|
712
|
+
// unless a timer flush met the server's 404/410 instead.
|
|
713
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus;
|
|
714
|
+
return { status, agentSessionId, installOk, error: null };
|
|
710
715
|
}
|
|
711
716
|
return {
|
|
712
717
|
status: error.intendedStatus,
|
|
@@ -66,12 +66,15 @@ export interface RunEventSink {
|
|
|
66
66
|
* Terminal kinds are rejected (they must go through `finish`). */
|
|
67
67
|
append(event: HarnessEvent): Promise<void>;
|
|
68
68
|
/** Force-flush any coalesced token snapshots (also runs on every structural
|
|
69
|
-
* event
|
|
69
|
+
* event, on `finish`, and every `tokenFlushMs` while new tokens arrive). */
|
|
70
70
|
flushTokens(): Promise<void>;
|
|
71
71
|
/** Terminal transition via PATCH; records the transcript tail first, then CAS. */
|
|
72
72
|
finish(status: TerminalStatus, opts?: FinishOptions): Promise<void>;
|
|
73
73
|
/** True once a terminal transition has landed and the sink is sealed. */
|
|
74
74
|
readonly sealed: boolean;
|
|
75
|
+
/** True when an append met 404/410, i.e. the server ended the run, not this
|
|
76
|
+
* writer's PATCH. A timer flush can learn this with no caller awaiting it. */
|
|
77
|
+
readonly terminatedServerSide: boolean;
|
|
75
78
|
/** The next sequence the sink will attempt to allocate (test/resume aid). */
|
|
76
79
|
readonly nextSequence: number;
|
|
77
80
|
/** Events the sink has confirmed appended, in order (redacted payloads). */
|
|
@@ -163,7 +166,13 @@ export interface CreateRunEventSinkOptions {
|
|
|
163
166
|
sleepImpl?: (ms: number) => Promise<void>;
|
|
164
167
|
/** Local diagnostic sink for disagreement-rule logs (never throws). */
|
|
165
168
|
onDiagnostic?: (message: string) => void;
|
|
169
|
+
/** How often buffered token snapshots are flushed while a reply is being
|
|
170
|
+
* written (ORI-2340). Default TOKEN_FLUSH_MS. */
|
|
171
|
+
tokenFlushMs?: number;
|
|
166
172
|
}
|
|
173
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
174
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
175
|
+
export declare const TOKEN_FLUSH_MS = 250;
|
|
167
176
|
export declare function createRunEventSink(options: CreateRunEventSinkOptions): RunEventSink;
|
|
168
177
|
export interface ResumeRunEventSinkOptions extends Omit<CreateRunEventSinkOptions, 'startSequence'> {
|
|
169
178
|
/** Page size for the cursor tail while discovering the latest sequence. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-run-event-sink.d.ts","sourceRoot":"","sources":["../src/hosted-run-event-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAIzE,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAUhF,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AACzD,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AAiCzD;oFACoF;AACpF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED,eAAO,MAAM,sBAAsB,gBAAgB,CAAA;AACnD,eAAO,MAAM,2BAA2B,qBAAqB,CAAA;AAC7D,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,QAAS,CAAA;AAY/C,eAAO,MAAM,6BAA6B,uBAAuB,CAAA;AACjE,eAAO,MAAM,iCAAiC,2BAA2B,CAAA;AAEzE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEjE,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC;8EAC0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B;uEACmE;IACnE,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;
|
|
1
|
+
{"version":3,"file":"hosted-run-event-sink.d.ts","sourceRoot":"","sources":["../src/hosted-run-event-sink.ts"],"names":[],"mappings":"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"}
|
|
@@ -156,6 +156,9 @@ const TERMINAL_STATUSES = new Set(['succeeded', 'failed', 'cancelled']);
|
|
|
156
156
|
function isTerminalStatus(status) {
|
|
157
157
|
return status !== null && TERMINAL_STATUSES.has(status);
|
|
158
158
|
}
|
|
159
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
160
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
161
|
+
export const TOKEN_FLUSH_MS = 250; // held equal to SESSION_TIMERS.agentTokenFlushMs by test
|
|
159
162
|
const DEFAULT_MAX_CRITICAL_ATTEMPTS = 4;
|
|
160
163
|
// Non-critical appends get a SMALL bounded retry so a single transient blip
|
|
161
164
|
// (network reset / 5xx) does not drop the event — but on exhaustion they are
|
|
@@ -209,6 +212,53 @@ export function createRunEventSink(options) {
|
|
|
209
212
|
const tokenBuffer = new Map();
|
|
210
213
|
let nextSequence = options.startSequence ?? 1;
|
|
211
214
|
let sealed = false;
|
|
215
|
+
let terminatedServerSide = false;
|
|
216
|
+
// One promise chain for every buffer-touching operation: append, flushTokens,
|
|
217
|
+
// finish and the timer flush below. This is how the timer honours the
|
|
218
|
+
// reentrancy contract on flushTokens — it queues behind an in-flight append
|
|
219
|
+
// or flush instead of running beside it. A rejected step reaches its own
|
|
220
|
+
// caller and never breaks the chain.
|
|
221
|
+
let queue = Promise.resolve();
|
|
222
|
+
function serialize(fn) {
|
|
223
|
+
const run = queue.then(fn);
|
|
224
|
+
queue = run.catch(() => { });
|
|
225
|
+
return run;
|
|
226
|
+
}
|
|
227
|
+
// ORI-2340 timer flush: started by the first token, unref'd so it never keeps
|
|
228
|
+
// the process alive, stopped for good once the sink seals or the bearer is
|
|
229
|
+
// refused. `tokensDirty` keeps a quiet tick (or a dropped snapshot with no
|
|
230
|
+
// newer text) from posting again.
|
|
231
|
+
let flushTimer = null;
|
|
232
|
+
let flushTimerStopped = false;
|
|
233
|
+
let tokensDirty = false;
|
|
234
|
+
let timerFlushQueued = false;
|
|
235
|
+
function stopFlushTimer() {
|
|
236
|
+
flushTimerStopped = true;
|
|
237
|
+
if (flushTimer)
|
|
238
|
+
clearInterval(flushTimer);
|
|
239
|
+
flushTimer = null;
|
|
240
|
+
}
|
|
241
|
+
function onFlushTick() {
|
|
242
|
+
if (sealed)
|
|
243
|
+
return stopFlushTimer();
|
|
244
|
+
if (!tokensDirty || timerFlushQueued)
|
|
245
|
+
return;
|
|
246
|
+
timerFlushQueued = true;
|
|
247
|
+
serialize(async () => {
|
|
248
|
+
timerFlushQueued = false;
|
|
249
|
+
// Queued behind finish(), the sink may have sealed since the tick fired.
|
|
250
|
+
if (sealed)
|
|
251
|
+
return;
|
|
252
|
+
tokensDirty = false;
|
|
253
|
+
await flushTokens();
|
|
254
|
+
}).catch(error => {
|
|
255
|
+
// A refused bearer would be refused again every tick; the loop learns of
|
|
256
|
+
// it at its next structural event or finish.
|
|
257
|
+
if (error instanceof RunAuthError)
|
|
258
|
+
stopFlushTimer();
|
|
259
|
+
diag(`timer token flush failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
212
262
|
function assertWritable() {
|
|
213
263
|
if (sealed) {
|
|
214
264
|
throw new RunTerminalError('run is terminal server-side — refusing to append (Orizu records win)');
|
|
@@ -280,6 +330,7 @@ export function createRunEventSink(options) {
|
|
|
280
330
|
// Run gone/terminal server-side: seal and stop (Orizu records win).
|
|
281
331
|
if (response.status === 404 || response.status === 410) {
|
|
282
332
|
sealed = true;
|
|
333
|
+
terminatedServerSide = true;
|
|
283
334
|
diag(`append rejected (${response.status}) for ${eventType} — sealing sink (run terminal)`);
|
|
284
335
|
throw new RunTerminalError(`append rejected (${response.status}) — run is terminal server-side`);
|
|
285
336
|
}
|
|
@@ -321,10 +372,10 @@ export function createRunEventSink(options) {
|
|
|
321
372
|
// REENTRANCY CONTRACT: flushTokens MUST run to completion before the next
|
|
322
373
|
// append()/flushTokens() begins — it is NOT safe to run concurrently with a
|
|
323
374
|
// token append. Coalescing correctness (and the `===` re-check below) relies on
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
375
|
+
// every append/flush running strictly sequentially, so no newer delta can
|
|
376
|
+
// mutate `tokenBuffer` mid-flush. The public methods and the ORI-2340 timer
|
|
377
|
+
// flush all go through `serialize` to keep that true; any new caller of this
|
|
378
|
+
// inner function MUST too, or the drop-preserving guarantee breaks.
|
|
328
379
|
async function flushTokens() {
|
|
329
380
|
if (tokenBuffer.size === 0)
|
|
330
381
|
return;
|
|
@@ -354,6 +405,11 @@ export function createRunEventSink(options) {
|
|
|
354
405
|
const messageId = event.messageId ?? '_';
|
|
355
406
|
const text = typeof event.payload.text === 'string' ? event.payload.text : '';
|
|
356
407
|
tokenBuffer.set(messageId, text);
|
|
408
|
+
tokensDirty = true;
|
|
409
|
+
if (!flushTimer && !flushTimerStopped) {
|
|
410
|
+
flushTimer = setInterval(onFlushTick, options.tokenFlushMs ?? TOKEN_FLUSH_MS);
|
|
411
|
+
flushTimer.unref?.();
|
|
412
|
+
}
|
|
357
413
|
return;
|
|
358
414
|
}
|
|
359
415
|
const eventType = eventTypeForKind(event.kind);
|
|
@@ -470,12 +526,15 @@ export function createRunEventSink(options) {
|
|
|
470
526
|
}
|
|
471
527
|
}
|
|
472
528
|
return {
|
|
473
|
-
append,
|
|
474
|
-
flushTokens,
|
|
475
|
-
finish,
|
|
529
|
+
append: event => serialize(() => append(event)),
|
|
530
|
+
flushTokens: () => serialize(flushTokens),
|
|
531
|
+
finish: (status, opts) => serialize(() => finish(status, opts)),
|
|
476
532
|
get sealed() {
|
|
477
533
|
return sealed;
|
|
478
534
|
},
|
|
535
|
+
get terminatedServerSide() {
|
|
536
|
+
return terminatedServerSide;
|
|
537
|
+
},
|
|
479
538
|
get nextSequence() {
|
|
480
539
|
return nextSequence;
|
|
481
540
|
},
|
|
@@ -583,7 +642,9 @@ export async function drainHarnessToSink(events, sink, opts = {}) {
|
|
|
583
642
|
: finishOpts;
|
|
584
643
|
try {
|
|
585
644
|
await sink.finish(status, mergedFinishOpts);
|
|
586
|
-
|
|
645
|
+
// A timer flush queued ahead of finish() can meet the server's 404/410
|
|
646
|
+
// after the check above; finish() then no-ops, so honour the server.
|
|
647
|
+
return sink.terminatedServerSide ? 'cancelled' : status;
|
|
587
648
|
}
|
|
588
649
|
catch (error) {
|
|
589
650
|
if (error instanceof RunTerminalError)
|
package/package.json
CHANGED
package/src/hosted-loop.ts
CHANGED
|
@@ -860,7 +860,10 @@ export async function runHostedLoop(opts: RunHostedLoopOptions): Promise<HostedL
|
|
|
860
860
|
await sleep(delayMs)
|
|
861
861
|
try {
|
|
862
862
|
await sink.finish(error.intendedStatus, error.finishOptions)
|
|
863
|
-
|
|
863
|
+
// A timer flush may have met the server's 404/410 during the sleep,
|
|
864
|
+
// making finish() a no-op: honour the server, as the drain does.
|
|
865
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus
|
|
866
|
+
return { status, agentSessionId, installOk, error: null }
|
|
864
867
|
} catch (retryError) {
|
|
865
868
|
// The run went terminal server-side while we were retrying: accept
|
|
866
869
|
// the server record (Orizu records win) instead of retrying further.
|
|
@@ -873,8 +876,10 @@ export async function runHostedLoop(opts: RunHostedLoopOptions): Promise<HostedL
|
|
|
873
876
|
}
|
|
874
877
|
if (sink.sealed) {
|
|
875
878
|
// A concurrent/preceding delivery landed after all — the record is
|
|
876
|
-
// terminal server-side with our intended status semantics preserved
|
|
877
|
-
|
|
879
|
+
// terminal server-side with our intended status semantics preserved —
|
|
880
|
+
// unless a timer flush met the server's 404/410 instead.
|
|
881
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus
|
|
882
|
+
return { status, agentSessionId, installOk, error: null }
|
|
878
883
|
}
|
|
879
884
|
return {
|
|
880
885
|
status: error.intendedStatus,
|
|
@@ -128,12 +128,15 @@ export interface RunEventSink {
|
|
|
128
128
|
* Terminal kinds are rejected (they must go through `finish`). */
|
|
129
129
|
append(event: HarnessEvent): Promise<void>
|
|
130
130
|
/** Force-flush any coalesced token snapshots (also runs on every structural
|
|
131
|
-
* event
|
|
131
|
+
* event, on `finish`, and every `tokenFlushMs` while new tokens arrive). */
|
|
132
132
|
flushTokens(): Promise<void>
|
|
133
133
|
/** Terminal transition via PATCH; records the transcript tail first, then CAS. */
|
|
134
134
|
finish(status: TerminalStatus, opts?: FinishOptions): Promise<void>
|
|
135
135
|
/** True once a terminal transition has landed and the sink is sealed. */
|
|
136
136
|
readonly sealed: boolean
|
|
137
|
+
/** True when an append met 404/410, i.e. the server ended the run, not this
|
|
138
|
+
* writer's PATCH. A timer flush can learn this with no caller awaiting it. */
|
|
139
|
+
readonly terminatedServerSide: boolean
|
|
137
140
|
/** The next sequence the sink will attempt to allocate (test/resume aid). */
|
|
138
141
|
readonly nextSequence: number
|
|
139
142
|
/** Events the sink has confirmed appended, in order (redacted payloads). */
|
|
@@ -251,8 +254,15 @@ export interface CreateRunEventSinkOptions {
|
|
|
251
254
|
sleepImpl?: (ms: number) => Promise<void>
|
|
252
255
|
/** Local diagnostic sink for disagreement-rule logs (never throws). */
|
|
253
256
|
onDiagnostic?: (message: string) => void
|
|
257
|
+
/** How often buffered token snapshots are flushed while a reply is being
|
|
258
|
+
* written (ORI-2340). Default TOKEN_FLUSH_MS. */
|
|
259
|
+
tokenFlushMs?: number
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
263
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
264
|
+
export const TOKEN_FLUSH_MS = 250 // held equal to SESSION_TIMERS.agentTokenFlushMs by test
|
|
265
|
+
|
|
256
266
|
const DEFAULT_MAX_CRITICAL_ATTEMPTS = 4
|
|
257
267
|
// Non-critical appends get a SMALL bounded retry so a single transient blip
|
|
258
268
|
// (network reset / 5xx) does not drop the event — but on exhaustion they are
|
|
@@ -312,6 +322,50 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
312
322
|
|
|
313
323
|
let nextSequence = options.startSequence ?? 1
|
|
314
324
|
let sealed = false
|
|
325
|
+
let terminatedServerSide = false
|
|
326
|
+
|
|
327
|
+
// One promise chain for every buffer-touching operation: append, flushTokens,
|
|
328
|
+
// finish and the timer flush below. This is how the timer honours the
|
|
329
|
+
// reentrancy contract on flushTokens — it queues behind an in-flight append
|
|
330
|
+
// or flush instead of running beside it. A rejected step reaches its own
|
|
331
|
+
// caller and never breaks the chain.
|
|
332
|
+
let queue: Promise<unknown> = Promise.resolve()
|
|
333
|
+
function serialize<T>(fn: () => Promise<T>): Promise<T> {
|
|
334
|
+
const run = queue.then(fn)
|
|
335
|
+
queue = run.catch(() => {})
|
|
336
|
+
return run
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ORI-2340 timer flush: started by the first token, unref'd so it never keeps
|
|
340
|
+
// the process alive, stopped for good once the sink seals or the bearer is
|
|
341
|
+
// refused. `tokensDirty` keeps a quiet tick (or a dropped snapshot with no
|
|
342
|
+
// newer text) from posting again.
|
|
343
|
+
let flushTimer: ReturnType<typeof setInterval> | null = null
|
|
344
|
+
let flushTimerStopped = false
|
|
345
|
+
let tokensDirty = false
|
|
346
|
+
let timerFlushQueued = false
|
|
347
|
+
function stopFlushTimer(): void {
|
|
348
|
+
flushTimerStopped = true
|
|
349
|
+
if (flushTimer) clearInterval(flushTimer)
|
|
350
|
+
flushTimer = null
|
|
351
|
+
}
|
|
352
|
+
function onFlushTick(): void {
|
|
353
|
+
if (sealed) return stopFlushTimer()
|
|
354
|
+
if (!tokensDirty || timerFlushQueued) return
|
|
355
|
+
timerFlushQueued = true
|
|
356
|
+
serialize(async () => {
|
|
357
|
+
timerFlushQueued = false
|
|
358
|
+
// Queued behind finish(), the sink may have sealed since the tick fired.
|
|
359
|
+
if (sealed) return
|
|
360
|
+
tokensDirty = false
|
|
361
|
+
await flushTokens()
|
|
362
|
+
}).catch(error => {
|
|
363
|
+
// A refused bearer would be refused again every tick; the loop learns of
|
|
364
|
+
// it at its next structural event or finish.
|
|
365
|
+
if (error instanceof RunAuthError) stopFlushTimer()
|
|
366
|
+
diag(`timer token flush failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
367
|
+
})
|
|
368
|
+
}
|
|
315
369
|
|
|
316
370
|
function assertWritable(): void {
|
|
317
371
|
if (sealed) {
|
|
@@ -389,6 +443,7 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
389
443
|
// Run gone/terminal server-side: seal and stop (Orizu records win).
|
|
390
444
|
if (response.status === 404 || response.status === 410) {
|
|
391
445
|
sealed = true
|
|
446
|
+
terminatedServerSide = true
|
|
392
447
|
diag(`append rejected (${response.status}) for ${eventType} — sealing sink (run terminal)`)
|
|
393
448
|
throw new RunTerminalError(`append rejected (${response.status}) — run is terminal server-side`)
|
|
394
449
|
}
|
|
@@ -434,10 +489,10 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
434
489
|
// REENTRANCY CONTRACT: flushTokens MUST run to completion before the next
|
|
435
490
|
// append()/flushTokens() begins — it is NOT safe to run concurrently with a
|
|
436
491
|
// token append. Coalescing correctness (and the `===` re-check below) relies on
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
//
|
|
492
|
+
// every append/flush running strictly sequentially, so no newer delta can
|
|
493
|
+
// mutate `tokenBuffer` mid-flush. The public methods and the ORI-2340 timer
|
|
494
|
+
// flush all go through `serialize` to keep that true; any new caller of this
|
|
495
|
+
// inner function MUST too, or the drop-preserving guarantee breaks.
|
|
441
496
|
async function flushTokens(): Promise<void> {
|
|
442
497
|
if (tokenBuffer.size === 0) return
|
|
443
498
|
// Snapshot the pending entries but DO NOT clear the buffer up front: only drop
|
|
@@ -467,6 +522,11 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
467
522
|
const messageId = event.messageId ?? '_'
|
|
468
523
|
const text = typeof event.payload.text === 'string' ? event.payload.text : ''
|
|
469
524
|
tokenBuffer.set(messageId, text)
|
|
525
|
+
tokensDirty = true
|
|
526
|
+
if (!flushTimer && !flushTimerStopped) {
|
|
527
|
+
flushTimer = setInterval(onFlushTick, options.tokenFlushMs ?? TOKEN_FLUSH_MS)
|
|
528
|
+
flushTimer.unref?.()
|
|
529
|
+
}
|
|
470
530
|
return
|
|
471
531
|
}
|
|
472
532
|
const eventType = eventTypeForKind(event.kind)
|
|
@@ -586,12 +646,15 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
586
646
|
}
|
|
587
647
|
|
|
588
648
|
return {
|
|
589
|
-
append,
|
|
590
|
-
flushTokens,
|
|
591
|
-
finish,
|
|
649
|
+
append: event => serialize(() => append(event)),
|
|
650
|
+
flushTokens: () => serialize(flushTokens),
|
|
651
|
+
finish: (status, opts) => serialize(() => finish(status, opts)),
|
|
592
652
|
get sealed() {
|
|
593
653
|
return sealed
|
|
594
654
|
},
|
|
655
|
+
get terminatedServerSide() {
|
|
656
|
+
return terminatedServerSide
|
|
657
|
+
},
|
|
595
658
|
get nextSequence() {
|
|
596
659
|
return nextSequence
|
|
597
660
|
},
|
|
@@ -737,7 +800,9 @@ export async function drainHarnessToSink(
|
|
|
737
800
|
: finishOpts
|
|
738
801
|
try {
|
|
739
802
|
await sink.finish(status, mergedFinishOpts)
|
|
740
|
-
|
|
803
|
+
// A timer flush queued ahead of finish() can meet the server's 404/410
|
|
804
|
+
// after the check above; finish() then no-ops, so honour the server.
|
|
805
|
+
return sink.terminatedServerSide ? 'cancelled' : status
|
|
741
806
|
} catch (error) {
|
|
742
807
|
if (error instanceof RunTerminalError) return 'cancelled'
|
|
743
808
|
throw error
|