@forgeax/engine-intelligence 0.1.6 → 0.1.7

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.
@@ -91,6 +91,76 @@ describe('IntelligenceRuntime', () => {
91
91
  expect(controlled.sinks.size).toBe(0);
92
92
  });
93
93
 
94
+ it('accepts exact-limit input and rejects invalid input before identity, capacity, or provider start', () => {
95
+ const sinks = new Map<string, ActivitySink>();
96
+ let activityIdentityCalls = 0;
97
+ let sessionIdentityCalls = 0;
98
+ let providerStarts = 0;
99
+ const provider: IntelligenceProvider = {
100
+ id: 'input.boundary.provider',
101
+ start(submission, sink) {
102
+ providerStarts += 1;
103
+ sinks.set(submission.id, sink);
104
+ return ok(undefined);
105
+ },
106
+ cancel() {
107
+ return ok(undefined);
108
+ },
109
+ async close() {},
110
+ };
111
+ const runtime = createIntelligenceRuntime(provider, {
112
+ limits: { maxInputChars: 4, maxConcurrentActivities: 1 },
113
+ createActivityId: () => {
114
+ activityIdentityCalls += 1;
115
+ return activityId('input-boundary-activity');
116
+ },
117
+ createSessionId: () => {
118
+ sessionIdentityCalls += 1;
119
+ return 'input-boundary-session';
120
+ },
121
+ });
122
+
123
+ const exact = runtime.submit({ input: 'abcd' });
124
+ expect(exact.ok).toBe(true);
125
+ if (!exact.ok) return;
126
+ expect(providerStarts).toBe(1);
127
+ expect(activityIdentityCalls).toBe(1);
128
+ expect(sessionIdentityCalls).toBe(1);
129
+
130
+ const empty = runtime.submit({ input: '' });
131
+ expect(empty.ok).toBe(false);
132
+ if (!empty.ok) {
133
+ expect(empty.error.code).toBe('intelligence-invalid-request');
134
+ expect(empty.error.detail).toEqual({ field: 'input', reason: 'input is empty' });
135
+ }
136
+
137
+ const overLimit = runtime.submit({ input: 'abcde' });
138
+ expect(overLimit.ok).toBe(false);
139
+ if (!overLimit.ok) {
140
+ expect(overLimit.error.code).toBe('intelligence-invalid-request');
141
+ expect(overLimit.error.detail).toEqual({
142
+ field: 'input',
143
+ reason: 'input exceeds 4 characters',
144
+ });
145
+ }
146
+
147
+ expect(activityIdentityCalls).toBe(1);
148
+ expect(sessionIdentityCalls).toBe(1);
149
+ expect(providerStarts).toBe(1);
150
+ expect(sinks.size).toBe(1);
151
+
152
+ sinks.get(exact.value.id)?.complete('done');
153
+ expect(runtime.poll()).toEqual([
154
+ {
155
+ type: 'completed',
156
+ activityId: exact.value.id,
157
+ sequence: 1,
158
+ session: exact.value.session,
159
+ output: 'done',
160
+ },
161
+ ]);
162
+ });
163
+
94
164
  it('turns queue overflow into one structured terminal failure and cancels the provider', () => {
95
165
  const controlled = controlledProvider();
96
166
  const runtime = createIntelligenceRuntime(controlled.provider, {
@@ -113,6 +183,127 @@ describe('IntelligenceRuntime', () => {
113
183
  expect(controlled.sinks.size).toBe(0);
114
184
  });
115
185
 
186
+ it('fails exactly once when cumulative streamed output crosses the character limit', () => {
187
+ const exactId = activityId('exact-stream');
188
+ const overflowId = activityId('stream-output-overflow');
189
+ const ids = [exactId, overflowId];
190
+ const controlled = controlledProvider('output.stream.provider');
191
+ const runtime = createIntelligenceRuntime(controlled.provider, {
192
+ limits: { maxOutputChars: 5, maxPendingEventsPerActivity: 8 },
193
+ createActivityId: () => {
194
+ const id = ids.shift();
195
+ if (id === undefined) throw new Error('test activity identity exhausted');
196
+ return id;
197
+ },
198
+ });
199
+
200
+ const exact = runtime.submit({ input: 'exact' });
201
+ const overflow = runtime.submit({ input: 'stream' });
202
+ expect(exact.ok).toBe(true);
203
+ expect(overflow.ok).toBe(true);
204
+ if (!exact.ok || !overflow.ok) return;
205
+ const exactSink = controlled.sinks.get(exact.value.id);
206
+ const overflowSink = controlled.sinks.get(overflow.value.id);
207
+ expect(exactSink).toBeDefined();
208
+ expect(overflowSink).toBeDefined();
209
+
210
+ exactSink?.complete('12345');
211
+ overflowSink?.text('12345');
212
+ overflowSink?.text('!');
213
+ overflowSink?.text('late');
214
+ overflowSink?.complete('duplicate');
215
+ overflowSink?.fail(new Error('duplicate'));
216
+
217
+ const events = runtime.poll();
218
+ expect(events).toEqual([
219
+ {
220
+ type: 'completed',
221
+ activityId: exactId,
222
+ sequence: 1,
223
+ session: exact.value.session,
224
+ output: '12345',
225
+ },
226
+ {
227
+ type: 'text-delta',
228
+ activityId: overflowId,
229
+ sequence: 1,
230
+ text: '12345',
231
+ },
232
+ {
233
+ type: 'failed',
234
+ activityId: overflowId,
235
+ sequence: 2,
236
+ error: expect.objectContaining({
237
+ code: 'intelligence-output-overflow',
238
+ detail: {
239
+ activityId: overflowId,
240
+ bound: 'output-chars',
241
+ limit: 5,
242
+ },
243
+ }),
244
+ },
245
+ ]);
246
+ expect(events.filter((event) => event.type === 'failed')).toHaveLength(1);
247
+ expect(controlled.sinks.has(overflowId)).toBe(false);
248
+ });
249
+
250
+ it('fails exactly once when a completion payload exceeds the character limit', () => {
251
+ const exactId = activityId('exact-completion');
252
+ const overflowId = activityId('completion-output-overflow');
253
+ const ids = [exactId, overflowId];
254
+ const controlled = controlledProvider('output.completion.provider');
255
+ const runtime = createIntelligenceRuntime(controlled.provider, {
256
+ limits: { maxOutputChars: 5, maxPendingEventsPerActivity: 8 },
257
+ createActivityId: () => {
258
+ const id = ids.shift();
259
+ if (id === undefined) throw new Error('test activity identity exhausted');
260
+ return id;
261
+ },
262
+ });
263
+
264
+ const exact = runtime.submit({ input: 'exact' });
265
+ const overflow = runtime.submit({ input: 'completion' });
266
+ expect(exact.ok).toBe(true);
267
+ expect(overflow.ok).toBe(true);
268
+ if (!exact.ok || !overflow.ok) return;
269
+ const exactSink = controlled.sinks.get(exact.value.id);
270
+ const overflowSink = controlled.sinks.get(overflow.value.id);
271
+ expect(exactSink).toBeDefined();
272
+ expect(overflowSink).toBeDefined();
273
+
274
+ exactSink?.complete('12345');
275
+ overflowSink?.complete('123456');
276
+ overflowSink?.text('late');
277
+ overflowSink?.complete('duplicate');
278
+ overflowSink?.cancelled();
279
+
280
+ const events = runtime.poll();
281
+ expect(events).toEqual([
282
+ {
283
+ type: 'completed',
284
+ activityId: exactId,
285
+ sequence: 1,
286
+ session: exact.value.session,
287
+ output: '12345',
288
+ },
289
+ {
290
+ type: 'failed',
291
+ activityId: overflowId,
292
+ sequence: 1,
293
+ error: expect.objectContaining({
294
+ code: 'intelligence-output-overflow',
295
+ detail: {
296
+ activityId: overflowId,
297
+ bound: 'output-chars',
298
+ limit: 5,
299
+ },
300
+ }),
301
+ },
302
+ ]);
303
+ expect(events.filter((event) => event.type === 'failed')).toHaveLength(1);
304
+ expect(controlled.sinks.has(overflowId)).toBe(false);
305
+ });
306
+
116
307
  it('preserves the provider error category and cause projection', () => {
117
308
  const controlled = controlledProvider();
118
309
  const runtime = createIntelligenceRuntime(controlled.provider, {
@@ -141,6 +332,45 @@ describe('IntelligenceRuntime', () => {
141
332
  if (!result.ok) expect(result.error.code).toBe('intelligence-closed');
142
333
  });
143
334
 
335
+ it('contains provider close rejection, clears records, and quarantines late callbacks', async () => {
336
+ const activity = activityId('close-rejection');
337
+ let sink: ActivitySink | undefined;
338
+ let closeCalls = 0;
339
+ const provider: IntelligenceProvider = {
340
+ id: 'close.rejection.provider',
341
+ start(_submission, nextSink) {
342
+ sink = nextSink;
343
+ return ok(undefined);
344
+ },
345
+ cancel() {
346
+ return ok(undefined);
347
+ },
348
+ async close() {
349
+ closeCalls += 1;
350
+ throw new Error('sentinel provider close failure');
351
+ },
352
+ };
353
+ const runtime = createIntelligenceRuntime(provider, {
354
+ createActivityId: () => activity,
355
+ });
356
+ const started = runtime.submit({ input: 'close me' });
357
+ expect(started.ok).toBe(true);
358
+
359
+ const closeTask = runtime.close();
360
+ expect(runtime.close()).toBe(closeTask);
361
+ await expect(closeTask).resolves.toBeUndefined();
362
+
363
+ expect(closeCalls).toBe(1);
364
+ expect(runtime.poll()).toEqual([]);
365
+ const late = runtime.submit({ input: 'after close' });
366
+ expect(late.ok).toBe(false);
367
+ if (!late.ok) expect(late.error.code).toBe('intelligence-closed');
368
+ sink?.text('late text');
369
+ sink?.complete('late completion');
370
+ sink?.fail(new Error('late failure'));
371
+ expect(runtime.poll()).toEqual([]);
372
+ });
373
+
144
374
  it('accepts a transport-owned identity and provider session', () => {
145
375
  const controlled = controlledProvider();
146
376
  const runtime = createIntelligenceRuntime(controlled.provider);
@@ -153,4 +383,485 @@ describe('IntelligenceRuntime', () => {
153
383
  expect(result.ok).toBe(true);
154
384
  expect(controlled.sinks.has(submission.id)).toBe(true);
155
385
  });
386
+
387
+ it('contains synchronous provider start throws and permits same-runtime retry', () => {
388
+ const siblingId = activityId('sibling');
389
+ const failedId = activityId('failed');
390
+ const spareId = activityId('spare');
391
+ const failedSession = { providerId: 'test.provider', id: 'failed-session' };
392
+ const ids = [siblingId, failedId, spareId, failedId];
393
+ const sinks = new Map<ActivityId, ActivitySink>();
394
+ const starts = new Map<ActivityId, number>();
395
+ let throwOnFailedStart = true;
396
+ let cancelCalls = 0;
397
+ let closeCalls = 0;
398
+ const provider: IntelligenceProvider = {
399
+ id: 'test.provider',
400
+ start(submission, sink) {
401
+ starts.set(submission.id, (starts.get(submission.id) ?? 0) + 1);
402
+ if (submission.id === failedId && throwOnFailedStart) {
403
+ throw new Error('sentinel provider start failure');
404
+ }
405
+ sinks.set(submission.id, sink);
406
+ return ok(undefined);
407
+ },
408
+ cancel() {
409
+ cancelCalls += 1;
410
+ return ok(undefined);
411
+ },
412
+ async close() {
413
+ closeCalls += 1;
414
+ },
415
+ };
416
+ const runtime = createIntelligenceRuntime(provider, {
417
+ limits: { maxConcurrentActivities: 2 },
418
+ createActivityId: () => {
419
+ const id = ids.shift();
420
+ if (id === undefined) throw new Error('test activity identity exhausted');
421
+ return id;
422
+ },
423
+ });
424
+
425
+ const sibling = runtime.submit({ input: 'sibling' });
426
+ expect(sibling.ok).toBe(true);
427
+ if (!sibling.ok) return;
428
+ const siblingSink = sinks.get(sibling.value.id);
429
+ expect(siblingSink).toBeDefined();
430
+ siblingSink?.text('sibling');
431
+ expect(runtime.poll()).toEqual([
432
+ { type: 'text-delta', activityId: siblingId, sequence: 1, text: 'sibling' },
433
+ ]);
434
+
435
+ let refused: ReturnType<typeof runtime.submit> | undefined;
436
+ let rawThrow: unknown;
437
+ try {
438
+ refused = runtime.submit({ input: 'throw', session: failedSession });
439
+ } catch (error) {
440
+ rawThrow = error;
441
+ }
442
+ expect(rawThrow).toBeUndefined();
443
+ expect(refused?.ok).toBe(false);
444
+ if (refused === undefined || refused.ok) return;
445
+ expect(refused.error.code).toBe('intelligence-provider-failed');
446
+ if (refused.error.code === 'intelligence-provider-failed') {
447
+ expect(refused.error.detail.providerId).toBe(provider.id);
448
+ expect(refused.error.detail.cause).toBeInstanceOf(Error);
449
+ expect(refused.error.message).not.toContain('sentinel provider start failure');
450
+ expect(refused.error.hint).not.toContain('sentinel provider start failure');
451
+ }
452
+ expect(runtime.poll()).toEqual([]);
453
+ expect(sinks.has(failedId)).toBe(false);
454
+ expect(cancelCalls).toBe(0);
455
+ expect(closeCalls).toBe(0);
456
+
457
+ const spare = runtime.submit({ input: 'spare' });
458
+ expect(spare.ok).toBe(true);
459
+ if (!spare.ok) return;
460
+ sinks.get(spare.value.id)?.complete('spare');
461
+ expect(runtime.poll()).toEqual([
462
+ {
463
+ type: 'completed',
464
+ activityId: spareId,
465
+ sequence: 1,
466
+ session: spare.value.session,
467
+ output: 'spare',
468
+ },
469
+ ]);
470
+
471
+ throwOnFailedStart = false;
472
+ const retry = runtime.submit({ input: 'retry', session: failedSession });
473
+ expect(retry.ok).toBe(true);
474
+ if (!retry.ok) return;
475
+ expect(retry.value.id).toBe(failedId);
476
+ expect(retry.value.session).toBe(failedSession);
477
+ siblingSink?.complete('sibling-done');
478
+ sinks.get(retry.value.id)?.complete('retry-done');
479
+ expect(runtime.poll()).toEqual([
480
+ {
481
+ type: 'completed',
482
+ activityId: siblingId,
483
+ sequence: 2,
484
+ session: sibling.value.session,
485
+ output: 'sibling-done',
486
+ },
487
+ {
488
+ type: 'completed',
489
+ activityId: failedId,
490
+ sequence: 1,
491
+ session: failedSession,
492
+ output: 'retry-done',
493
+ },
494
+ ]);
495
+ expect(runtime.poll()).toEqual([]);
496
+ expect(starts.get(siblingId)).toBe(1);
497
+ expect(starts.get(failedId)).toBe(2);
498
+ expect(starts.get(spareId)).toBe(1);
499
+ expect(cancelCalls).toBe(0);
500
+
501
+ const close = runtime.close();
502
+ expect(close).toBe(runtime.close());
503
+ return close.then(() => {
504
+ expect(closeCalls).toBe(1);
505
+ expect(runtime.poll()).toEqual([]);
506
+ });
507
+ });
508
+
509
+ it('contains synchronous provider cancel throws and preserves the same Activity for retry', () => {
510
+ const siblingId = activityId('cancel-sibling');
511
+ const failedId = activityId('cancel-failed');
512
+ const spareId = activityId('cancel-spare');
513
+ const failedSession = { providerId: 'cancel.throw.provider', id: 'failed-session' };
514
+ const ids = [siblingId, failedId, spareId];
515
+ const sinks = new Map<ActivityId, ActivitySink>();
516
+ const starts = new Map<ActivityId, number>();
517
+ const cancelCalls: ActivityId[] = [];
518
+ const sentinel = new Error('sentinel provider cancel failure');
519
+ let throwOnFailedCancel = true;
520
+ const provider: IntelligenceProvider = {
521
+ id: 'cancel.throw.provider',
522
+ start(submission, sink) {
523
+ starts.set(submission.id, (starts.get(submission.id) ?? 0) + 1);
524
+ sinks.set(submission.id, sink);
525
+ return ok(undefined);
526
+ },
527
+ cancel(activity) {
528
+ cancelCalls.push(activity);
529
+ if (activity === failedId && throwOnFailedCancel) throw sentinel;
530
+ const sink = sinks.get(activity);
531
+ if (sink === undefined) {
532
+ return err(
533
+ new IntelligenceError({
534
+ code: 'intelligence-activity-not-found',
535
+ detail: { activityId: activity },
536
+ }),
537
+ );
538
+ }
539
+ sinks.delete(activity);
540
+ sink.cancelled();
541
+ return ok(undefined);
542
+ },
543
+ async close() {},
544
+ };
545
+ const runtime = createIntelligenceRuntime(provider, {
546
+ limits: { maxConcurrentActivities: 2 },
547
+ createActivityId: () => {
548
+ const id = ids.shift();
549
+ if (id === undefined) throw new Error('test activity identity exhausted');
550
+ return id;
551
+ },
552
+ });
553
+
554
+ const sibling = runtime.submit({ input: 'sibling' });
555
+ const failed = runtime.submit({ input: 'cancel', session: failedSession });
556
+ expect(sibling.ok).toBe(true);
557
+ expect(failed.ok).toBe(true);
558
+ if (!sibling.ok || !failed.ok) return;
559
+ expect(runtime.poll()).toEqual([]);
560
+
561
+ let rawThrow: unknown;
562
+ let refused: ReturnType<typeof runtime.cancel> | undefined;
563
+ try {
564
+ refused = runtime.cancel(failed.value.id);
565
+ } catch (error) {
566
+ rawThrow = error;
567
+ }
568
+ expect(rawThrow).toBeUndefined();
569
+ expect(refused?.ok).toBe(false);
570
+ if (refused === undefined || refused.ok) return;
571
+ expect(refused.error.code).toBe('intelligence-provider-failed');
572
+ if (refused.error.code === 'intelligence-provider-failed') {
573
+ expect(refused.error.detail.providerId).toBe(provider.id);
574
+ expect(refused.error.detail.cause).toBe(sentinel);
575
+ expect(refused.error.message).not.toContain(sentinel.message);
576
+ expect(refused.error.hint).not.toContain(sentinel.message);
577
+ }
578
+ expect(cancelCalls).toEqual([failedId]);
579
+ expect(runtime.poll()).toEqual([]);
580
+
581
+ const spare = runtime.submit({ input: 'spare' });
582
+ expect(spare.ok).toBe(false);
583
+ if (!spare.ok) expect(spare.error.code).toBe('intelligence-capacity-exceeded');
584
+ expect(starts.has(spareId)).toBe(false);
585
+
586
+ const siblingSink = sinks.get(sibling.value.id);
587
+ expect(siblingSink).toBeDefined();
588
+ siblingSink?.text('healthy');
589
+ expect(runtime.poll()).toEqual([
590
+ { type: 'text-delta', activityId: siblingId, sequence: 1, text: 'healthy' },
591
+ ]);
592
+
593
+ throwOnFailedCancel = false;
594
+ const retried = runtime.cancel(failed.value.id);
595
+ expect(retried.ok).toBe(true);
596
+ expect(cancelCalls).toEqual([failedId, failedId]);
597
+ expect(runtime.poll()).toEqual([{ type: 'cancelled', activityId: failedId, sequence: 1 }]);
598
+ const afterCancel = runtime.cancel(failed.value.id);
599
+ expect(afterCancel.ok).toBe(false);
600
+ if (!afterCancel.ok) expect(afterCancel.error.code).toBe('intelligence-activity-not-found');
601
+
602
+ siblingSink?.complete('healthy-done');
603
+ expect(runtime.poll()).toEqual([
604
+ {
605
+ type: 'completed',
606
+ activityId: siblingId,
607
+ sequence: 2,
608
+ session: sibling.value.session,
609
+ output: 'healthy-done',
610
+ },
611
+ ]);
612
+ expect(starts.get(siblingId)).toBe(1);
613
+ expect(starts.get(failedId)).toBe(1);
614
+ });
615
+
616
+ it('contains a throwing overflow cancellation and preserves provider Result errors', () => {
617
+ const siblingId = activityId('overflow-sibling');
618
+ const returnedId = activityId('returned-cancel-error');
619
+ const overflowId = activityId('overflow-throw');
620
+ const ids = [siblingId, returnedId, overflowId];
621
+ const sinks = new Map<ActivityId, ActivitySink>();
622
+ const cancelCalls: ActivityId[] = [];
623
+ const overflowSentinel = new Error('sentinel overflow cancel failure');
624
+ const returnedError = new IntelligenceError({
625
+ code: 'intelligence-provider-failed',
626
+ detail: { providerId: 'inner.provider', cause: 'provider returned error' },
627
+ });
628
+ const returnedResult = err(returnedError);
629
+ const provider: IntelligenceProvider = {
630
+ id: 'overflow.throw.provider',
631
+ start(submission, sink) {
632
+ sinks.set(submission.id, sink);
633
+ return ok(undefined);
634
+ },
635
+ cancel(activity) {
636
+ cancelCalls.push(activity);
637
+ if (activity === returnedId) return returnedResult;
638
+ if (activity === overflowId) throw overflowSentinel;
639
+ const sink = sinks.get(activity);
640
+ if (sink === undefined) {
641
+ return err(
642
+ new IntelligenceError({
643
+ code: 'intelligence-activity-not-found',
644
+ detail: { activityId: activity },
645
+ }),
646
+ );
647
+ }
648
+ sinks.delete(activity);
649
+ sink.cancelled();
650
+ return ok(undefined);
651
+ },
652
+ async close() {},
653
+ };
654
+ const runtime = createIntelligenceRuntime(provider, {
655
+ limits: { maxPendingEventsPerActivity: 2, maxConcurrentActivities: 3 },
656
+ createActivityId: () => {
657
+ const id = ids.shift();
658
+ if (id === undefined) throw new Error('test activity identity exhausted');
659
+ return id;
660
+ },
661
+ });
662
+
663
+ const sibling = runtime.submit({ input: 'sibling' });
664
+ const returned = runtime.submit({ input: 'returned' });
665
+ const overflow = runtime.submit({ input: 'overflow' });
666
+ expect(sibling.ok).toBe(true);
667
+ expect(returned.ok).toBe(true);
668
+ expect(overflow.ok).toBe(true);
669
+ if (!sibling.ok || !returned.ok || !overflow.ok) return;
670
+
671
+ const returnedCancel = runtime.cancel(returned.value.id);
672
+ expect(returnedCancel).toBe(returnedResult);
673
+ expect(runtime.poll()).toEqual([]);
674
+ sinks.get(returned.value.id)?.complete('returned-retry');
675
+ expect(runtime.poll()).toEqual([
676
+ {
677
+ type: 'completed',
678
+ activityId: returnedId,
679
+ sequence: 1,
680
+ session: returned.value.session,
681
+ output: 'returned-retry',
682
+ },
683
+ ]);
684
+
685
+ const overflowSink = sinks.get(overflow.value.id);
686
+ expect(overflowSink).toBeDefined();
687
+ overflowSink?.text('first');
688
+ let rawThrow: unknown;
689
+ try {
690
+ overflowSink?.text('second');
691
+ } catch (error) {
692
+ rawThrow = error;
693
+ }
694
+ expect(rawThrow).toBeUndefined();
695
+ const events = runtime.poll();
696
+ expect(events).toHaveLength(2);
697
+ expect(events.filter((event) => event.type === 'failed')).toHaveLength(1);
698
+ expect(events[0]).toEqual({
699
+ type: 'text-delta',
700
+ activityId: overflowId,
701
+ sequence: 1,
702
+ text: 'first',
703
+ });
704
+ const overflowEvent = events[1];
705
+ expect(overflowEvent?.type).toBe('failed');
706
+ if (overflowEvent?.type === 'failed') {
707
+ expect(overflowEvent.error.code).toBe('intelligence-output-overflow');
708
+ if (overflowEvent.error.code === 'intelligence-output-overflow') {
709
+ expect(overflowEvent.error.detail).toEqual({
710
+ activityId: overflowId,
711
+ bound: 'pending-events',
712
+ limit: 2,
713
+ });
714
+ }
715
+ }
716
+ expect(cancelCalls).toEqual([returnedId, overflowId]);
717
+
718
+ const siblingSink = sinks.get(sibling.value.id);
719
+ siblingSink?.text('sibling-alive');
720
+ expect(runtime.poll()).toEqual([
721
+ { type: 'text-delta', activityId: siblingId, sequence: 1, text: 'sibling-alive' },
722
+ ]);
723
+ siblingSink?.complete('sibling-done');
724
+ expect(runtime.poll()).toEqual([
725
+ {
726
+ type: 'completed',
727
+ activityId: siblingId,
728
+ sequence: 2,
729
+ session: sibling.value.session,
730
+ output: 'sibling-done',
731
+ },
732
+ ]);
733
+ });
734
+
735
+ it('releases overflow capacity and quarantines late provider output before a same-session retry', async () => {
736
+ const overflowId = activityId('pending-overflow');
737
+ const siblingId = activityId('healthy-sibling');
738
+ const retryId = activityId('overflow-retry');
739
+ const overflowSession = { providerId: 'overflow.recovery.provider', id: 'session-a' };
740
+ const ids = [overflowId, siblingId, retryId];
741
+ const sinks = new Map<ActivityId, ActivitySink>();
742
+ const starts: Array<{ readonly id: ActivityId; readonly session: string }> = [];
743
+ let overflowCancelCalls = 0;
744
+ let lateDelta: (() => void) | undefined;
745
+ let lateCompletion: (() => void) | undefined;
746
+ let closeCalls = 0;
747
+ const provider: IntelligenceProvider = {
748
+ id: 'overflow.recovery.provider',
749
+ start(submission, sink) {
750
+ starts.push({ id: submission.id, session: submission.session.id });
751
+ sinks.set(submission.id, sink);
752
+ if (submission.input === 'overflow') {
753
+ sink.text('one');
754
+ sink.text('two');
755
+ sink.text('three');
756
+ }
757
+ return ok(undefined);
758
+ },
759
+ cancel(activity) {
760
+ if (activity === overflowId) {
761
+ overflowCancelCalls += 1;
762
+ const sink = sinks.get(activity);
763
+ if (sink !== undefined) {
764
+ sinks.delete(activity);
765
+ lateDelta = () => sink.text('late');
766
+ lateCompletion = () => sink.complete('late');
767
+ }
768
+ throw new Error('sentinel overflow cancel failure');
769
+ }
770
+ const sink = sinks.get(activity);
771
+ if (sink === undefined) {
772
+ return err(
773
+ new IntelligenceError({
774
+ code: 'intelligence-activity-not-found',
775
+ detail: { activityId: activity },
776
+ }),
777
+ );
778
+ }
779
+ sinks.delete(activity);
780
+ sink.cancelled();
781
+ return ok(undefined);
782
+ },
783
+ async close() {
784
+ closeCalls += 1;
785
+ sinks.clear();
786
+ },
787
+ };
788
+ const runtime = createIntelligenceRuntime(provider, {
789
+ limits: { maxConcurrentActivities: 2, maxPendingEventsPerActivity: 3 },
790
+ createActivityId: () => {
791
+ const id = ids.shift();
792
+ if (id === undefined) throw new Error('test activity identity exhausted');
793
+ return id;
794
+ },
795
+ });
796
+
797
+ const overflow = runtime.submit({ input: 'overflow', session: overflowSession });
798
+ const sibling = runtime.submit({ input: 'sibling' });
799
+ expect(overflow.ok).toBe(true);
800
+ expect(sibling.ok).toBe(true);
801
+ if (!overflow.ok || !sibling.ok) return;
802
+
803
+ const overflowEvents = runtime.poll();
804
+ expect(overflowEvents).toHaveLength(3);
805
+ expect(overflowEvents[0]).toEqual({
806
+ type: 'text-delta',
807
+ activityId: overflowId,
808
+ sequence: 1,
809
+ text: 'one',
810
+ });
811
+ expect(overflowEvents[1]).toEqual({
812
+ type: 'text-delta',
813
+ activityId: overflowId,
814
+ sequence: 2,
815
+ text: 'two',
816
+ });
817
+ const overflowEvent = overflowEvents[2];
818
+ expect(overflowEvent?.type).toBe('failed');
819
+ if (overflowEvent?.type === 'failed') {
820
+ expect(overflowEvent.error.code).toBe('intelligence-output-overflow');
821
+ if (overflowEvent.error.code === 'intelligence-output-overflow') {
822
+ expect(overflowEvent.error.detail).toEqual({
823
+ activityId: overflowId,
824
+ bound: 'pending-events',
825
+ limit: 3,
826
+ });
827
+ }
828
+ }
829
+ expect(overflowCancelCalls).toBe(1);
830
+
831
+ const retry = runtime.submit({ input: 'retry', session: overflow.value.session });
832
+ expect(retry.ok).toBe(true);
833
+ if (!retry.ok) return;
834
+ expect(retry.value.session).toEqual(overflow.value.session);
835
+ lateDelta?.();
836
+ lateCompletion?.();
837
+ expect(runtime.poll()).toEqual([]);
838
+
839
+ sinks.get(sibling.value.id)?.complete('healthy');
840
+ sinks.get(retry.value.id)?.complete('retry');
841
+ expect(runtime.poll()).toEqual([
842
+ {
843
+ type: 'completed',
844
+ activityId: siblingId,
845
+ sequence: 1,
846
+ session: sibling.value.session,
847
+ output: 'healthy',
848
+ },
849
+ {
850
+ type: 'completed',
851
+ activityId: retryId,
852
+ sequence: 1,
853
+ session: retry.value.session,
854
+ output: 'retry',
855
+ },
856
+ ]);
857
+ const close = runtime.close();
858
+ expect(close).toBe(runtime.close());
859
+ await close;
860
+ expect(closeCalls).toBe(1);
861
+ expect(starts).toEqual([
862
+ { id: overflowId, session: overflowSession.id },
863
+ { id: siblingId, session: sibling.value.session.id },
864
+ { id: retryId, session: overflowSession.id },
865
+ ]);
866
+ });
156
867
  });