@modelprofile.com/flexharness 5.2.0 → 5.3.1

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.
@@ -35,6 +35,8 @@ import type {
35
35
  IFlexCreateSessionOptions,
36
36
  IFlexDeleteSessionGenerationCohortInput,
37
37
  IFlexDeleteSessionGenerationCohortResult,
38
+ IFlexDelegatedRunAdmissionContext,
39
+ IFlexDelegatedRunAdmissionLease,
38
40
  IFlexErrorInfo,
39
41
  IFlexEventArchiveMetadata,
40
42
  IFlexExecutionContextHandle,
@@ -49,6 +51,7 @@ import type {
49
51
  IFlexPermissionRequestInput,
50
52
  IFlexPermissionSnapshot,
51
53
  IFlexProjectGoalResult,
54
+ IFlexProjectManagementSessionContext,
52
55
  IFlexProjectManagementSnapshot,
53
56
  IFlexProjectManagementTombstone,
54
57
  IFlexProjectManagementWriteContext,
@@ -85,6 +88,7 @@ import type {
85
88
  IFlexSlashCommandExecutionOptions,
86
89
  IFlexSlashCommandHandlerRegistration,
87
90
  IFlexSubagentDefinition,
91
+ IFlexSubagentProvenance,
88
92
  IFlexTerminalProjection,
89
93
  IFlexToolHandle,
90
94
  IFlexToolMessagePart,
@@ -297,6 +301,7 @@ interface IStorageState {
297
301
  tombstones: Map<string, IFlexSessionTombstone>;
298
302
  tombstoneCleanups: Map<string, Promise<void>>;
299
303
  activeRuns: Map<string, IActiveRun>;
304
+ delegatedRunAdmissionOwners: Set<IDelegatedRunAdmissionOwner>;
300
305
  pendingPermissions: Map<string, IPendingPermission>;
301
306
  scopeQueue: Promise<void>;
302
307
  lifecycle: 'active' | 'retiring' | 'fenced' | 'retired';
@@ -331,6 +336,7 @@ interface IActiveRun {
331
336
  callbacksClosed: boolean;
332
337
  reasoningPartIds: Map<string, string>;
333
338
  toolPartIds: Map<string, string>;
339
+ trustedToolExecutionErrors: Map<string, Error>;
334
340
  subagentCallCount: number;
335
341
  subagentSessionIds: Set<string>;
336
342
  pendingPermissionIds: Set<string>;
@@ -338,9 +344,56 @@ interface IActiveRun {
338
344
  reservedUserMessage?: IFlexMessage;
339
345
  reservedAssistantMessage?: IFlexMessage;
340
346
  modelResolution?: IFlexResolvedModel;
347
+ delegatedRunAdmissionOwner?: IDelegatedRunAdmissionOwner;
348
+ delegatedRunAdmissionClose?: Promise<void>;
341
349
  completion: Promise<IFlexPromptResult>;
342
350
  }
343
351
 
352
+ interface IDelegatedRunAdmissionSeed {
353
+ readonly state: IStorageState;
354
+ readonly childStored: IStoredSessionState;
355
+ readonly parentRun: IActiveRun;
356
+ readonly parentStored: IStoredSessionState;
357
+ readonly scopeId: string;
358
+ readonly scope: unknown;
359
+ readonly storageKey: string;
360
+ readonly sessionId: string;
361
+ readonly sessionGenerationId: string;
362
+ readonly sessionGenerationSequence: number;
363
+ readonly originParentRunId: string;
364
+ readonly originParentToolCallId: string;
365
+ readonly parentSessionId: string;
366
+ readonly parentSessionGenerationId: string;
367
+ readonly parentSessionGenerationSequence: number;
368
+ readonly parentQueueId: string;
369
+ readonly parentRunId: string;
370
+ readonly parentToolCallId: string;
371
+ readonly agent: string;
372
+ readonly depth: number;
373
+ }
374
+
375
+ type TDelegatedRunAdmissionOwnerStatus =
376
+ | 'acquiring'
377
+ | 'acquired'
378
+ | 'close-requested'
379
+ | 'retrying'
380
+ | 'closed';
381
+
382
+ interface IDelegatedRunAdmissionOwner {
383
+ readonly storageState: IStorageState;
384
+ readonly run: IActiveRun;
385
+ readonly context: Readonly<IFlexDelegatedRunAdmissionContext<unknown>>;
386
+ status: TDelegatedRunAdmissionOwnerStatus;
387
+ acquisitionSettled: boolean;
388
+ acquisitionDetached: boolean;
389
+ acquisitionCompletion?: Promise<void>;
390
+ acquisitionError?: Error;
391
+ lease?: IFlexDelegatedRunAdmissionLease;
392
+ closeAttempt?: Promise<void>;
393
+ closeAttemptSequence: number;
394
+ closeFailure?: { sequence: number; error: Error };
395
+ }
396
+
344
397
  interface IQueuedPrompt {
345
398
  state: IStorageState;
346
399
  stored: IStoredSessionState;
@@ -357,6 +410,7 @@ interface IQueuedPrompt {
357
410
  startedAt?: string;
358
411
  prompt?: INormalizedFlexPrompt;
359
412
  options?: IFlexPromptOptions;
413
+ delegationSeed?: IDelegatedRunAdmissionSeed;
360
414
  byteSize: number;
361
415
  completion: Promise<IFlexPromptResult>;
362
416
  resolveCompletion: (result: IFlexPromptResult) => void;
@@ -493,6 +547,7 @@ const maxBackgroundExecutions = 100;
493
547
  const maxSubagentTaskDescriptionBytes = 256;
494
548
  const maxSubagentPromptBytes = 64 * 1024;
495
549
  const maxSubagentTaskIdBytes = 512;
550
+ const maxSubagentPermissionMetadataBytes = 16 * 1024;
496
551
  const maxSubagentResultTextBytes = 64 * 1024;
497
552
  const defaultMaxSubagentDepth = 1;
498
553
  const maximumMaxSubagentDepth = 8;
@@ -630,6 +685,7 @@ export class FlexHarness<TScope = unknown> {
630
685
  private readonly toolProvider: IFlexHarnessOptions<TScope>['toolProvider'];
631
686
  private readonly resourceToolProviderResolver: IFlexHarnessOptions<TScope>['resourceToolProviderResolver'];
632
687
  private readonly executionContextProvider: IFlexHarnessOptions<TScope>['executionContextProvider'];
688
+ private readonly delegatedRunAdmissionProvider: IFlexHarnessOptions<TScope>['delegatedRunAdmissionProvider'];
633
689
  private readonly stores: IFlexHarnessStores;
634
690
  private readonly agentSessionPolicy: IFlexAgentSessionPolicy<TScope>;
635
691
  private readonly toolOutputLimits: Required<NonNullable<IFlexHarnessOptions<TScope>['toolOutputLimits']>>;
@@ -693,6 +749,7 @@ export class FlexHarness<TScope = unknown> {
693
749
  this.toolProvider = options.toolProvider;
694
750
  this.resourceToolProviderResolver = options.resourceToolProviderResolver;
695
751
  this.executionContextProvider = options.executionContextProvider;
752
+ this.delegatedRunAdmissionProvider = options.delegatedRunAdmissionProvider;
696
753
  this.stores = requireHarnessStores(options.stores ?? new InMemoryFlexHarnessStores());
697
754
  this.agentSessionPolicy = normalizeAgentSessionPolicy(options.agentSessionPolicy);
698
755
  this.toolOutputLimits = resolveJsonLimits(options.toolOutputLimits);
@@ -1326,8 +1383,13 @@ export class FlexHarness<TScope = unknown> {
1326
1383
  const rootDepth = stored.session.depth ?? 0;
1327
1384
  const deletedAt = new Date().toISOString();
1328
1385
  const deletedSessions = subtree.map((entry) => cloneSerializable(entry.session));
1386
+ const provenanceBySessionId = new Map(subtree.map((entry) => [
1387
+ entry.session.sessionId,
1388
+ this.createSubagentProvenance(state, entry.session),
1389
+ ]));
1329
1390
  for (const entry of subtree) {
1330
1391
  const entrySessionId = entry.session.sessionId;
1392
+ const subagent = provenanceBySessionId.get(entrySessionId);
1331
1393
  state.sessions.delete(entrySessionId);
1332
1394
  state.retainedSessionCleanups.set(entrySessionId, {
1333
1395
  stored: entry,
@@ -1342,6 +1404,7 @@ export class FlexHarness<TScope = unknown> {
1342
1404
  ...(entry.session.parentSessionId === undefined
1343
1405
  ? {}
1344
1406
  : { parentSessionId: entry.session.parentSessionId }),
1407
+ ...(subagent === undefined ? {} : { subagent: cloneSerializable(subagent) }),
1345
1408
  });
1346
1409
  }
1347
1410
  return {
@@ -1779,7 +1842,7 @@ export class FlexHarness<TScope = unknown> {
1779
1842
  promptOptions,
1780
1843
  undefined,
1781
1844
  undefined,
1782
- false,
1845
+ undefined,
1783
1846
  signal,
1784
1847
  true,
1785
1848
  );
@@ -3085,7 +3148,7 @@ export class FlexHarness<TScope = unknown> {
3085
3148
  options: IFlexPromptOptions,
3086
3149
  scheduleKey?: string,
3087
3150
  debounceMs?: number,
3088
- subagentAdmission = false,
3151
+ delegationSeed?: IDelegatedRunAdmissionSeed,
3089
3152
  admissionSignal?: AbortSignal,
3090
3153
  slashCommandAdmission = false,
3091
3154
  ): Promise<{
@@ -3145,11 +3208,14 @@ export class FlexHarness<TScope = unknown> {
3145
3208
  if (stored.pendingReversion) {
3146
3209
  throw new FlexHarnessSessionBusyError(sessionId, 'has a pending reversion operation');
3147
3210
  }
3148
- if (stored.session.agent !== undefined && !subagentAdmission) {
3211
+ if (stored.session.agent !== undefined && delegationSeed === undefined) {
3149
3212
  throw new FlexHarnessValidationError(
3150
3213
  'Subagent sessions can only be prompted through the foreground delegate tool.',
3151
3214
  );
3152
3215
  }
3216
+ if (delegationSeed !== undefined) {
3217
+ this.assertDelegatedRunAdmissionOwnership(delegationSeed, state, stored, scopeId);
3218
+ }
3153
3219
  if (stored.outstandingPromptsById.size >= this.promptQueueLimits.maxOutstandingPromptsPerSession) {
3154
3220
  throw new FlexHarnessQueueFullError(
3155
3221
  `Session "${sessionId}" has reached its outstanding prompt limit.`,
@@ -3188,7 +3254,7 @@ export class FlexHarness<TScope = unknown> {
3188
3254
  state,
3189
3255
  stored,
3190
3256
  scopeId,
3191
- scope: resolved.scope.scope,
3257
+ scope: delegationSeed === undefined ? resolved.scope.scope : delegationSeed.scope,
3192
3258
  sessionId,
3193
3259
  queueId: plugins.crypto.randomUUID(),
3194
3260
  queueSequence: ++this.promptQueueSequence,
@@ -3197,6 +3263,7 @@ export class FlexHarness<TScope = unknown> {
3197
3263
  ...(scheduleKey === undefined ? {} : { scheduleKey, debounceMs }),
3198
3264
  prompt: normalizedPrompt,
3199
3265
  options: normalizedOptions,
3266
+ ...(delegationSeed === undefined ? {} : { delegationSeed }),
3200
3267
  byteSize,
3201
3268
  completion,
3202
3269
  resolveCompletion,
@@ -3318,6 +3385,7 @@ export class FlexHarness<TScope = unknown> {
3318
3385
  callbacksClosed: false,
3319
3386
  reasoningPartIds: new Map(),
3320
3387
  toolPartIds: new Map(),
3388
+ trustedToolExecutionErrors: new Map(),
3321
3389
  subagentCallCount: 0,
3322
3390
  subagentSessionIds: new Set(),
3323
3391
  pendingPermissionIds: new Set(),
@@ -3336,6 +3404,260 @@ export class FlexHarness<TScope = unknown> {
3336
3404
  || run.stored.outstandingPromptsById.get(run.queueId) !== queued
3337
3405
  || run.state.activeRuns.get(run.sessionId) !== run
3338
3406
  ) throw this.trustInternalError(new FlexHarnessAbortError('The prompt was retired during admission.'));
3407
+ if (queued.delegationSeed) {
3408
+ this.assertDelegatedRunAdmissionOwnership(
3409
+ queued.delegationSeed,
3410
+ run.state,
3411
+ run.stored,
3412
+ run.scopeId,
3413
+ );
3414
+ if (
3415
+ run.queueId !== queued.queueId
3416
+ || run.runId !== queued.runId
3417
+ || run.sessionId !== queued.delegationSeed.sessionId
3418
+ ) {
3419
+ throw this.trustInternalError(
3420
+ new FlexHarnessAbortError('The delegated prompt lost its exact child run ownership.'),
3421
+ );
3422
+ }
3423
+ }
3424
+ }
3425
+
3426
+ private assertDelegatedRunAdmissionOwnership(
3427
+ seed: IDelegatedRunAdmissionSeed,
3428
+ state: IStorageState,
3429
+ childStored: IStoredSessionState,
3430
+ scopeId: string,
3431
+ ): void {
3432
+ const child = childStored.session;
3433
+ const parentRun = seed.parentRun;
3434
+ const parent = seed.parentStored.session;
3435
+ const parentPartId = parentRun.toolPartIds.get(seed.parentToolCallId);
3436
+ const parentPart = parentRun.callbackParts.find((part): part is IFlexToolMessagePart =>
3437
+ part.type === 'tool'
3438
+ && part.partId === parentPartId
3439
+ && part.toolCallId === seed.parentToolCallId);
3440
+ if (
3441
+ seed.state !== state
3442
+ || seed.storageKey !== state.storageKey
3443
+ || seed.scopeId !== scopeId
3444
+ || seed.childStored !== childStored
3445
+ || childStored.storageKey !== seed.storageKey
3446
+ || state.sessions.get(seed.sessionId) !== childStored
3447
+ || state.tombstones.has(seed.sessionId)
3448
+ || state.initializingSessions.has(seed.sessionId)
3449
+ || child.sessionId !== seed.sessionId
3450
+ || child.sessionGenerationId !== seed.sessionGenerationId
3451
+ || child.sessionGenerationSequence !== seed.sessionGenerationSequence
3452
+ || child.parentSessionId !== seed.parentSessionId
3453
+ || child.parentRunId !== seed.originParentRunId
3454
+ || child.parentToolCallId !== seed.originParentToolCallId
3455
+ || child.agent !== seed.agent
3456
+ || child.depth !== seed.depth
3457
+ || seed.parentStored !== parentRun.stored
3458
+ || seed.parentStored.storageKey !== seed.storageKey
3459
+ || parentRun.state !== state
3460
+ || parentRun.scopeId !== seed.scopeId
3461
+ || parentRun.scope !== seed.scope
3462
+ || parentRun.sessionId !== seed.parentSessionId
3463
+ || parentRun.queueId !== seed.parentQueueId
3464
+ || parentRun.runId !== seed.parentRunId
3465
+ || state.sessions.get(seed.parentSessionId) !== seed.parentStored
3466
+ || state.tombstones.has(seed.parentSessionId)
3467
+ || parent.sessionId !== seed.parentSessionId
3468
+ || parent.sessionGenerationId !== seed.parentSessionGenerationId
3469
+ || parent.sessionGenerationSequence !== seed.parentSessionGenerationSequence
3470
+ || state.activeRuns.get(seed.parentSessionId) !== parentRun
3471
+ || parentRun.callbacksClosed
3472
+ || parentRun.controller.signal.aborted
3473
+ || parentPart?.status !== 'running'
3474
+ ) {
3475
+ throw this.trustInternalError(
3476
+ new FlexHarnessAbortError('The delegated prompt lost its exact parent or child ownership.'),
3477
+ );
3478
+ }
3479
+ }
3480
+
3481
+ private async acquireDelegatedRunAdmission(
3482
+ queued: IQueuedPrompt,
3483
+ run: IActiveRun,
3484
+ ): Promise<void> {
3485
+ const seed = queued.delegationSeed;
3486
+ const provider = this.delegatedRunAdmissionProvider;
3487
+ if (!seed || !provider) return;
3488
+ const context = Object.freeze({
3489
+ scopeId: seed.scopeId,
3490
+ scope: seed.scope as TScope,
3491
+ storageKey: seed.storageKey,
3492
+ sessionId: seed.sessionId,
3493
+ sessionGenerationId: seed.sessionGenerationId,
3494
+ sessionGenerationSequence: seed.sessionGenerationSequence,
3495
+ queueId: queued.queueId,
3496
+ runId: run.runId,
3497
+ parentSessionId: seed.parentSessionId,
3498
+ parentSessionGenerationId: seed.parentSessionGenerationId,
3499
+ parentSessionGenerationSequence: seed.parentSessionGenerationSequence,
3500
+ parentQueueId: seed.parentQueueId,
3501
+ parentRunId: seed.parentRunId,
3502
+ parentToolCallId: seed.parentToolCallId,
3503
+ originParentRunId: seed.originParentRunId,
3504
+ originParentToolCallId: seed.originParentToolCallId,
3505
+ agent: seed.agent,
3506
+ depth: seed.depth,
3507
+ signal: run.controller.signal,
3508
+ });
3509
+ const owner: IDelegatedRunAdmissionOwner = {
3510
+ storageState: run.state,
3511
+ run,
3512
+ context,
3513
+ status: 'acquiring',
3514
+ acquisitionSettled: false,
3515
+ acquisitionDetached: false,
3516
+ closeAttemptSequence: 0,
3517
+ };
3518
+ run.state.delegatedRunAdmissionOwners.add(owner);
3519
+ run.delegatedRunAdmissionOwner = owner;
3520
+ const acquisition = Promise.resolve()
3521
+ .then(() => provider.acquireDelegatedRunAdmission(context))
3522
+ .then((lease) => this.normalizeDelegatedRunAdmissionLease(lease))
3523
+ .then(
3524
+ (lease) => {
3525
+ owner.lease = lease;
3526
+ owner.acquisitionSettled = true;
3527
+ if (owner.status === 'acquiring') owner.status = 'acquired';
3528
+ if (owner.status === 'close-requested') {
3529
+ void this.startDelegatedRunAdmissionCloseAttempt(owner).catch(() => undefined);
3530
+ }
3531
+ },
3532
+ (error: unknown) => {
3533
+ owner.acquisitionError = this.projectExternalError(
3534
+ run,
3535
+ error,
3536
+ 'delegatedRunAdmissionProvider',
3537
+ );
3538
+ owner.acquisitionSettled = true;
3539
+ owner.status = 'closed';
3540
+ run.state.delegatedRunAdmissionOwners.delete(owner);
3541
+ },
3542
+ );
3543
+ owner.acquisitionCompletion = acquisition;
3544
+
3545
+ let resolveAbort!: () => void;
3546
+ const abort = new Promise<'aborted'>((resolve) => {
3547
+ resolveAbort = () => resolve('aborted');
3548
+ });
3549
+ run.controller.signal.addEventListener('abort', resolveAbort, { once: true });
3550
+ if (run.controller.signal.aborted) resolveAbort();
3551
+ try {
3552
+ const outcome = await Promise.race([
3553
+ acquisition.then(() => 'settled' as const),
3554
+ abort,
3555
+ ]);
3556
+ if (outcome === 'aborted') {
3557
+ owner.acquisitionDetached = true;
3558
+ throw run.controller.signal.reason ?? this.trustInternalError(new FlexHarnessAbortError());
3559
+ }
3560
+ if (run.controller.signal.aborted) {
3561
+ throw run.controller.signal.reason ?? this.trustInternalError(new FlexHarnessAbortError());
3562
+ }
3563
+ if (owner.acquisitionError) throw owner.acquisitionError;
3564
+ if (owner.status !== 'acquired' || !owner.lease) {
3565
+ throw this.trustInternalError(
3566
+ new FlexHarnessAbortError('The delegated run admission was retired during acquisition.'),
3567
+ );
3568
+ }
3569
+ } finally {
3570
+ run.controller.signal.removeEventListener('abort', resolveAbort);
3571
+ }
3572
+ }
3573
+
3574
+ private normalizeDelegatedRunAdmissionLease(
3575
+ value: IFlexDelegatedRunAdmissionLease,
3576
+ ): IFlexDelegatedRunAdmissionLease {
3577
+ if (!value || (typeof value !== 'object' && typeof value !== 'function')) {
3578
+ throw new FlexHarnessValidationError(
3579
+ 'Delegated run admission provider returned an invalid lease.',
3580
+ );
3581
+ }
3582
+ const close = value.close;
3583
+ if (typeof close !== 'function') {
3584
+ throw new FlexHarnessValidationError(
3585
+ 'Delegated run admission provider returned an invalid lease.',
3586
+ );
3587
+ }
3588
+ return Object.freeze({ close: () => close.call(value) });
3589
+ }
3590
+
3591
+ private requestDelegatedRunAdmissionClose(
3592
+ owner: IDelegatedRunAdmissionOwner,
3593
+ ): Promise<void> {
3594
+ const priorAttemptSequence = owner.closeAttemptSequence;
3595
+ const joinedAttempt = owner.closeAttempt;
3596
+ if (owner.status === 'closed') return Promise.resolve();
3597
+ if (owner.status !== 'retrying') owner.status = 'close-requested';
3598
+ return (async () => {
3599
+ await owner.acquisitionCompletion;
3600
+ if (owner.status === 'closed') return;
3601
+ if (joinedAttempt) {
3602
+ await joinedAttempt;
3603
+ return;
3604
+ }
3605
+ if (owner.closeAttempt) {
3606
+ await owner.closeAttempt;
3607
+ return;
3608
+ }
3609
+ if (owner.closeFailure && owner.closeFailure.sequence > priorAttemptSequence) {
3610
+ throw owner.closeFailure.error;
3611
+ }
3612
+ await this.startDelegatedRunAdmissionCloseAttempt(owner);
3613
+ })();
3614
+ }
3615
+
3616
+ private startDelegatedRunAdmissionCloseAttempt(
3617
+ owner: IDelegatedRunAdmissionOwner,
3618
+ ): Promise<void> {
3619
+ if (owner.closeAttempt) return owner.closeAttempt;
3620
+ if (!owner.lease) {
3621
+ throw new Error('Acquired delegated run admission has no lease.');
3622
+ }
3623
+ owner.status = 'retrying';
3624
+ owner.closeFailure = undefined;
3625
+ const sequence = ++owner.closeAttemptSequence;
3626
+ let attempt!: Promise<void>;
3627
+ attempt = Promise.resolve()
3628
+ .then(() => owner.lease!.close())
3629
+ .then(() => {
3630
+ owner.status = 'closed';
3631
+ delete owner.lease;
3632
+ owner.storageState.delegatedRunAdmissionOwners.delete(owner);
3633
+ }, (error: unknown) => {
3634
+ const projected = this.projectExternalError(
3635
+ owner.run,
3636
+ error,
3637
+ 'delegatedRunAdmissionProvider',
3638
+ );
3639
+ owner.closeFailure = { sequence, error: projected };
3640
+ owner.status = 'close-requested';
3641
+ throw projected;
3642
+ })
3643
+ .finally(() => {
3644
+ if (owner.closeAttempt === attempt) owner.closeAttempt = undefined;
3645
+ });
3646
+ owner.closeAttempt = attempt;
3647
+ return attempt;
3648
+ }
3649
+
3650
+ private async closeRunDelegatedRunAdmission(
3651
+ run: IActiveRun,
3652
+ awaitSettlement = true,
3653
+ ): Promise<void> {
3654
+ const owner = run.delegatedRunAdmissionOwner;
3655
+ if (!owner) return;
3656
+ if (!run.delegatedRunAdmissionClose) {
3657
+ run.delegatedRunAdmissionClose = this.requestDelegatedRunAdmissionClose(owner);
3658
+ void run.delegatedRunAdmissionClose.catch(() => undefined);
3659
+ }
3660
+ if (awaitSettlement) await run.delegatedRunAdmissionClose;
3339
3661
  }
3340
3662
 
3341
3663
  private async promoteQueuedPrompt(queued: IQueuedPrompt, run: IActiveRun): Promise<void> {
@@ -3343,13 +3665,19 @@ export class FlexHarness<TScope = unknown> {
3343
3665
  const options = queued.options!;
3344
3666
  let projectionReserved = false;
3345
3667
  try {
3668
+ this.assertPromptPromotion(queued, run);
3669
+ await this.acquireDelegatedRunAdmission(queued, run);
3670
+ this.assertPromptPromotion(queued, run);
3346
3671
  await this.commitRevertedBranch(run.state, run.stored, run.scopeId, run.scope as TScope);
3672
+ this.assertPromptPromotion(queued, run);
3673
+ const generationPrompt = cloneSerializable(prompt.modelMessage.content) as Parameters<
3674
+ plugins.IAgentSession['beginGeneration']
3675
+ >[0];
3676
+ this.assertPromptPromotion(queued, run);
3347
3677
  run.transaction = await this.withRunCompactorContext(
3348
3678
  run,
3349
3679
  () => run.stored.agentSession.beginGeneration(
3350
- cloneSerializable(prompt.modelMessage.content) as Parameters<
3351
- plugins.IAgentSession['beginGeneration']
3352
- >[0],
3680
+ generationPrompt,
3353
3681
  { generationId: run.runId },
3354
3682
  ),
3355
3683
  );
@@ -3411,10 +3739,21 @@ export class FlexHarness<TScope = unknown> {
3411
3739
  let cleanupErrors: unknown[] = [];
3412
3740
  let cleanupFailure: unknown;
3413
3741
  try {
3414
- cleanupErrors = await this.rollbackAdmission(run, projectionReserved, safeError);
3742
+ await this.closeRunDelegatedRunAdmission(
3743
+ run,
3744
+ !run.delegatedRunAdmissionOwner?.acquisitionDetached,
3745
+ );
3746
+ } catch (closeError) {
3747
+ cleanupErrors.push(closeError);
3748
+ }
3749
+ try {
3750
+ cleanupErrors.push(...await this.rollbackAdmission(run, projectionReserved, safeError));
3415
3751
  } catch (rollbackError) {
3416
3752
  cleanupFailure = rollbackError;
3417
3753
  }
3754
+ if (cleanupFailure !== undefined && cleanupErrors.length > 0) {
3755
+ cleanupFailure = combineErrors([cleanupFailure, ...cleanupErrors]);
3756
+ }
3418
3757
  if (run.state.lifecycle === 'fenced' && cleanupFailure === undefined) {
3419
3758
  const combined = combineErrors([safeError, ...cleanupErrors]);
3420
3759
  try {
@@ -3526,6 +3865,7 @@ export class FlexHarness<TScope = unknown> {
3526
3865
  stored.outstandingPromptBytes = Math.max(0, stored.outstandingPromptBytes - queued.byteSize);
3527
3866
  delete queued.prompt;
3528
3867
  delete queued.options;
3868
+ delete queued.delegationSeed;
3529
3869
  const terminal: IFlexPromptQueueEntry = {
3530
3870
  ...this.projectPromptQueueEntry(queued),
3531
3871
  status,
@@ -3587,6 +3927,11 @@ export class FlexHarness<TScope = unknown> {
3587
3927
  }
3588
3928
 
3589
3929
  private purgeStoredPromptQueue(stored: IStoredSessionState): void {
3930
+ for (const queued of stored.outstandingPromptsById.values()) {
3931
+ delete queued.prompt;
3932
+ delete queued.options;
3933
+ delete queued.delegationSeed;
3934
+ }
3590
3935
  stored.promptQueue.length = 0;
3591
3936
  stored.outstandingPromptsById.clear();
3592
3937
  stored.terminalPromptQueueEntries.clear();
@@ -3606,6 +3951,7 @@ export class FlexHarness<TScope = unknown> {
3606
3951
  const result = normalizeRunResult(rawResult);
3607
3952
  if (!run.modelResolution) throw new FlexHarnessValidationError('Generation completed without a resolved model.');
3608
3953
  await this.finalizeRunReversion(run, 'completed');
3954
+ await this.closeRunDelegatedRunAdmission(run);
3609
3955
  const terminal = this.buildTerminal(run, 'completed', result);
3610
3956
  try {
3611
3957
  await this.mutateProjection(run.state, run.stored, () => {
@@ -3683,10 +4029,19 @@ export class FlexHarness<TScope = unknown> {
3683
4029
  ? run.ownerCancellation!
3684
4030
  : this.projectExternalError(run, error, generated ? 'persistence' : 'agentSession');
3685
4031
  const errors: unknown[] = [safeError];
4032
+ let reversionFailed = false;
3686
4033
  try {
3687
4034
  await this.finalizeRunReversion(run, cancelled ? 'cancelled' : 'failed');
3688
4035
  } catch (reversionError) {
3689
4036
  errors.push(reversionError);
4037
+ reversionFailed = true;
4038
+ }
4039
+ try {
4040
+ await this.closeRunDelegatedRunAdmission(run);
4041
+ } catch (closeError) {
4042
+ if (!errors.includes(closeError)) errors.push(closeError);
4043
+ }
4044
+ if (reversionFailed) {
3690
4045
  const combined = combineErrors(errors);
3691
4046
  await this.fenceNamespace(run.state, run, combined);
3692
4047
  throw combined;
@@ -3829,10 +4184,11 @@ export class FlexHarness<TScope = unknown> {
3829
4184
  ): Promise<IFlexProjectManagementSnapshot> {
3830
4185
  const store = this.stores.projectManagement;
3831
4186
  const generation = requireSessionGeneration(stored.session);
4187
+ const sessionContext = this.createProjectManagementSessionContext(state, stored.session);
3832
4188
  const record = await store.load(
3833
4189
  state.storageKey,
3834
4190
  stored.session.sessionId,
3835
- Object.freeze({ ...generation }),
4191
+ sessionContext,
3836
4192
  );
3837
4193
  if (record === undefined) {
3838
4194
  return createEmptyFlexProjectManagementSnapshot(
@@ -3925,6 +4281,52 @@ export class FlexHarness<TScope = unknown> {
3925
4281
  return publicSnapshot({ revision: snapshot.revision, state: snapshot });
3926
4282
  }
3927
4283
 
4284
+ private createSubagentProvenance(
4285
+ state: IStorageState,
4286
+ session: Readonly<IFlexSession>,
4287
+ ): Readonly<IFlexSubagentProvenance> | undefined {
4288
+ if (session.parentSessionId === undefined) return undefined;
4289
+ if (
4290
+ session.parentRunId === undefined
4291
+ || session.parentToolCallId === undefined
4292
+ || session.agent === undefined
4293
+ || session.depth === undefined
4294
+ ) throw new FlexHarnessValidationError('Subagent session provenance is incomplete.');
4295
+ const parent = state.sessions.get(session.parentSessionId)?.session
4296
+ ?? state.tombstones.get(session.parentSessionId);
4297
+ if (!parent) {
4298
+ throw new FlexHarnessValidationError(
4299
+ `Subagent session "${session.sessionId}" has no exact parent provenance.`,
4300
+ );
4301
+ }
4302
+ const parentGeneration = requireSessionGeneration(parent);
4303
+ return Object.freeze({
4304
+ parentSessionId: session.parentSessionId,
4305
+ parentSessionGenerationId: parentGeneration.sessionGenerationId,
4306
+ parentSessionGenerationSequence: parentGeneration.sessionGenerationSequence,
4307
+ originParentRunId: session.parentRunId,
4308
+ originParentToolCallId: session.parentToolCallId,
4309
+ agent: session.agent,
4310
+ depth: session.depth,
4311
+ });
4312
+ }
4313
+
4314
+ private createProjectManagementSessionContext(
4315
+ state: IStorageState,
4316
+ session: Readonly<IFlexSession | IFlexSessionTombstone>,
4317
+ ): Readonly<IFlexProjectManagementSessionContext> {
4318
+ const source = 'deletedAt' in session
4319
+ ? session.subagent
4320
+ : this.createSubagentProvenance(state, session);
4321
+ const subagent = source === undefined
4322
+ ? undefined
4323
+ : Object.freeze({ ...cloneSerializable(source) });
4324
+ return Object.freeze({
4325
+ ...requireSessionGeneration(session),
4326
+ ...(subagent === undefined ? {} : { subagent }),
4327
+ });
4328
+ }
4329
+
3928
4330
  private projectTaskResult(
3929
4331
  snapshot: IFlexProjectManagementSnapshot,
3930
4332
  task: IFlexProjectTask,
@@ -4613,15 +5015,27 @@ export class FlexHarness<TScope = unknown> {
4613
5015
  .map((definition) => `- ${definition.name}: ${definition.description}`)
4614
5016
  .join('\n');
4615
5017
  return plugins.tool({
4616
- description: `Run one configured FlexHarness subagent in the foreground and return its final text.\nAvailable subagents:\n${available}`,
5018
+ description: `Run one configured FlexHarness subagent in the foreground and return its final text. Omit taskId to create a new child. Supply taskId only to resume the exact child ID returned by an earlier completed delegate call in a later parent run.\nAvailable subagents:\n${available}`,
4617
5019
  inputSchema: plugins.z.object({
4618
5020
  description: plugins.z.string(),
4619
5021
  prompt: plugins.z.string(),
4620
5022
  subagentType: plugins.z.string(),
4621
- taskId: plugins.z.string().optional(),
5023
+ taskId: plugins.z.string()
5024
+ .describe('Omit for a new child. For a later-run resume, use only the exact taskId returned by an earlier completed delegate call.')
5025
+ .optional(),
4622
5026
  }).strict(),
4623
- execute: (input: IFlexSubagentTaskInput, options?: { toolCallId?: string }) =>
4624
- this.executeSubagentTask(run, input, options?.toolCallId),
5027
+ execute: async (input: IFlexSubagentTaskInput, options?: { toolCallId?: string }) => {
5028
+ try {
5029
+ return await this.executeSubagentTask(run, input, options?.toolCallId);
5030
+ } catch (error) {
5031
+ if (
5032
+ options?.toolCallId
5033
+ && error instanceof FlexHarnessValidationError
5034
+ && this.trustedInternalErrors.has(error)
5035
+ ) run.trustedToolExecutionErrors.set(options.toolCallId, error);
5036
+ throw error;
5037
+ }
5038
+ },
4625
5039
  });
4626
5040
  }
4627
5041
 
@@ -4690,16 +5104,20 @@ export class FlexHarness<TScope = unknown> {
4690
5104
  run.controller.signal.addEventListener('abort', abortChild, { once: true });
4691
5105
  if (run.controller.signal.aborted) abortChild();
4692
5106
  try {
5107
+ const permissionMetadata = {
5108
+ agent: definition.name,
5109
+ description: input.description,
5110
+ childSessionId: reservedSessionId,
5111
+ ...(input.taskId === undefined ? {} : { taskId: input.taskId }),
5112
+ };
5113
+ if (jsonBytes(permissionMetadata) > maxSubagentPermissionMetadataBytes) {
5114
+ throw new FlexHarnessValidationError('Subagent permission metadata exceeds its byte limit.');
5115
+ }
4693
5116
  await this.requestPermission(run.state, run, {
4694
5117
  kind: 'subagent.start',
4695
5118
  description: `Start foreground subagent "${definition.name}": ${input.description}`,
4696
5119
  toolCallId,
4697
- metadata: {
4698
- agent: definition.name,
4699
- description: input.description,
4700
- ...(input.taskId === undefined ? {} : { taskId: input.taskId }),
4701
- },
4702
- });
5120
+ }, Object.freeze(permissionMetadata));
4703
5121
  const acquired = await this.acquireSubagentSession(
4704
5122
  run,
4705
5123
  definition,
@@ -4716,6 +5134,12 @@ export class FlexHarness<TScope = unknown> {
4716
5134
  ...(definition.system === undefined ? {} : { system: definition.system }),
4717
5135
  ...(definition.maxSteps === undefined ? {} : { maxSteps: definition.maxSteps }),
4718
5136
  };
5137
+ const delegationSeed = this.createDelegatedRunAdmissionSeed(
5138
+ run,
5139
+ child,
5140
+ definition,
5141
+ toolCallId,
5142
+ );
4719
5143
  queued = await this.enqueuePromptInternal(
4720
5144
  run.scopeId,
4721
5145
  child.session.sessionId,
@@ -4723,7 +5147,7 @@ export class FlexHarness<TScope = unknown> {
4723
5147
  childOptions,
4724
5148
  undefined,
4725
5149
  undefined,
4726
- true,
5150
+ delegationSeed,
4727
5151
  run.controller.signal,
4728
5152
  );
4729
5153
  admission = await queued.started;
@@ -4777,6 +5201,42 @@ export class FlexHarness<TScope = unknown> {
4777
5201
  }
4778
5202
  }
4779
5203
 
5204
+ private createDelegatedRunAdmissionSeed(
5205
+ run: IActiveRun,
5206
+ child: IStoredSessionState,
5207
+ definition: Readonly<IFlexSubagentDefinition>,
5208
+ toolCallId: string,
5209
+ ): IDelegatedRunAdmissionSeed {
5210
+ const childGeneration = requireSessionGeneration(child.session);
5211
+ const parentGeneration = requireSessionGeneration(run.stored.session);
5212
+ if (!child.session.parentRunId || !child.session.parentToolCallId) {
5213
+ throw new FlexHarnessValidationError('Subagent session origin metadata is missing.');
5214
+ }
5215
+ const seed = Object.freeze({
5216
+ state: run.state,
5217
+ childStored: child,
5218
+ parentRun: run,
5219
+ parentStored: run.stored,
5220
+ scopeId: run.scopeId,
5221
+ scope: run.scope,
5222
+ storageKey: run.state.storageKey,
5223
+ sessionId: child.session.sessionId,
5224
+ ...childGeneration,
5225
+ originParentRunId: child.session.parentRunId,
5226
+ originParentToolCallId: child.session.parentToolCallId,
5227
+ parentSessionId: run.sessionId,
5228
+ parentSessionGenerationId: parentGeneration.sessionGenerationId,
5229
+ parentSessionGenerationSequence: parentGeneration.sessionGenerationSequence,
5230
+ parentQueueId: run.queueId,
5231
+ parentRunId: run.runId,
5232
+ parentToolCallId: toolCallId,
5233
+ agent: definition.name,
5234
+ depth: (run.stored.session.depth ?? 0) + 1,
5235
+ });
5236
+ this.assertDelegatedRunAdmissionOwnership(seed, run.state, child, run.scopeId);
5237
+ return seed;
5238
+ }
5239
+
4780
5240
  private async acquireSubagentSession(
4781
5241
  run: IActiveRun,
4782
5242
  definition: Readonly<IFlexSubagentDefinition>,
@@ -4865,7 +5325,11 @@ export class FlexHarness<TScope = unknown> {
4865
5325
  return;
4866
5326
  }
4867
5327
  if (taskId !== undefined || state.tombstones.has(sessionId)) {
4868
- throw new FlexHarnessNotFoundError('Subagent task', sessionId);
5328
+ throw this.trustInternalError(
5329
+ new FlexHarnessValidationError(
5330
+ 'The supplied taskId does not identify a resumable child. Omit taskId to create a new child, or use only an exact taskId returned by an earlier completed delegate call.',
5331
+ ),
5332
+ );
4869
5333
  }
4870
5334
  const timestamp = new Date().toISOString();
4871
5335
  metadata = {
@@ -4941,6 +5405,7 @@ export class FlexHarness<TScope = unknown> {
4941
5405
  ) throw projected;
4942
5406
  if (state.tombstones.has(sessionId)) throw projected;
4943
5407
  try {
5408
+ const subagent = this.createSubagentProvenance(state, metadata);
4944
5409
  await this.mutateScope(state, () => {
4945
5410
  if (state.sessions.get(sessionId) !== placeholder) return;
4946
5411
  state.sessions.delete(sessionId);
@@ -4951,6 +5416,7 @@ export class FlexHarness<TScope = unknown> {
4951
5416
  rootSessionId: sessionId,
4952
5417
  depth: 0,
4953
5418
  parentSessionId: run.sessionId,
5419
+ ...(subagent === undefined ? {} : { subagent: cloneSerializable(subagent) }),
4954
5420
  });
4955
5421
  }, true);
4956
5422
  completeInitialization();
@@ -6430,10 +6896,12 @@ export class FlexHarness<TScope = unknown> {
6430
6896
  const part = run.callbackParts.find((entry) => entry.partId === partId && entry.type === 'tool');
6431
6897
  if (!part || part.type !== 'tool' || part.status !== 'running') return;
6432
6898
  try {
6899
+ const trustedExecutionError = run.trustedToolExecutionErrors.get(event.toolCallId);
6900
+ run.trustedToolExecutionErrors.delete(event.toolCallId);
6433
6901
  const output = event.success ? normalizeJsonValue(event.output, this.toolOutputLimits) : undefined;
6434
6902
  const projectedError = event.success
6435
6903
  ? undefined
6436
- : this.projectExternalError(run, event.error, 'toolCallback');
6904
+ : this.projectExternalError(run, trustedExecutionError ?? event.error, 'toolCallback');
6437
6905
  const bytes = event.success ? jsonBytes(output) : Buffer.byteLength(projectedError!.message);
6438
6906
  if (!this.reserveCallbackCapacity(run, 1, bytes, 0)) return;
6439
6907
  if (event.success) {
@@ -6516,6 +6984,7 @@ export class FlexHarness<TScope = unknown> {
6516
6984
  state: IStorageState,
6517
6985
  run: IActiveRun,
6518
6986
  input: IFlexPermissionRequestInput,
6987
+ exactMetadata?: TJsonValue,
6519
6988
  ): Promise<void> {
6520
6989
  validateIdentifier(input.kind, 'permission kind');
6521
6990
  validateIdentifier(input.description, 'permission description');
@@ -6538,9 +7007,11 @@ export class FlexHarness<TScope = unknown> {
6538
7007
  description: truncateUtf8(input.description, maxTransferMetadataBytes),
6539
7008
  ...(input.toolCallId ? { toolCallId: input.toolCallId } : {}),
6540
7009
  ...(input.rememberKey ? { rememberKey: input.rememberKey } : {}),
6541
- ...(input.metadata === undefined
6542
- ? {}
6543
- : { metadata: normalizeJsonValue(input.metadata, this.toolOutputLimits) }),
7010
+ ...(exactMetadata !== undefined
7011
+ ? { metadata: cloneSerializable(exactMetadata) }
7012
+ : input.metadata === undefined
7013
+ ? {}
7014
+ : { metadata: normalizeJsonValue(input.metadata, this.toolOutputLimits) }),
6544
7015
  createdAt: new Date().toISOString(),
6545
7016
  };
6546
7017
  let resolvePermission!: () => void;
@@ -7166,6 +7637,39 @@ export class FlexHarness<TScope = unknown> {
7166
7637
  .filter((error): error is Error => error !== undefined);
7167
7638
  }
7168
7639
 
7640
+ private delegatedRunAdmissionOwnersForGeneration(
7641
+ state: IStorageState,
7642
+ session: Readonly<Pick<
7643
+ IFlexSession,
7644
+ 'sessionId' | 'sessionGenerationId' | 'sessionGenerationSequence'
7645
+ >>,
7646
+ ): IDelegatedRunAdmissionOwner[] {
7647
+ return [...state.delegatedRunAdmissionOwners].filter((owner) =>
7648
+ owner.storageState === state
7649
+ && owner.context.sessionId === session.sessionId
7650
+ && owner.context.sessionGenerationId === session.sessionGenerationId
7651
+ && owner.context.sessionGenerationSequence === session.sessionGenerationSequence);
7652
+ }
7653
+
7654
+ private async settleDelegatedRunAdmissionOwners(
7655
+ owners: readonly IDelegatedRunAdmissionOwner[],
7656
+ ): Promise<Error[]> {
7657
+ const results = await Promise.allSettled(
7658
+ owners.map((owner) => this.requestDelegatedRunAdmissionClose(owner)),
7659
+ );
7660
+ return results.flatMap((result, index) => {
7661
+ if (result.status === 'fulfilled') return [];
7662
+ const error = result.reason instanceof Error
7663
+ ? result.reason
7664
+ : this.projectExternalError(
7665
+ owners[index].run,
7666
+ result.reason,
7667
+ 'delegatedRunAdmissionProvider',
7668
+ );
7669
+ return [error];
7670
+ });
7671
+ }
7672
+
7169
7673
  private projectExternalError(
7170
7674
  run: IActiveRun,
7171
7675
  error: unknown,
@@ -7288,6 +7792,7 @@ export class FlexHarness<TScope = unknown> {
7288
7792
  tombstones: new Map(snapshot.tombstones.map((entry) => [entry.sessionId, entry])),
7289
7793
  tombstoneCleanups: new Map(),
7290
7794
  activeRuns: new Map(),
7795
+ delegatedRunAdmissionOwners: new Set(),
7291
7796
  pendingPermissions: new Map(),
7292
7797
  scopeQueue: Promise.resolve(),
7293
7798
  lifecycle: 'active',
@@ -7347,7 +7852,7 @@ export class FlexHarness<TScope = unknown> {
7347
7852
  scopeId,
7348
7853
  scope,
7349
7854
  );
7350
- await this.cleanupSessionDomains(storageKey, tombstone);
7855
+ await this.cleanupSessionDomains(state, tombstone);
7351
7856
  }
7352
7857
  for (const tombstone of group) state.tombstones.delete(tombstone.sessionId);
7353
7858
  scopeChanged = true;
@@ -8328,16 +8833,17 @@ export class FlexHarness<TScope = unknown> {
8328
8833
  }
8329
8834
 
8330
8835
  private async cleanupSessionDomains(
8331
- storageKey: string,
8836
+ state: IStorageState,
8332
8837
  tombstone: IFlexSessionTombstone,
8333
8838
  ): Promise<void> {
8839
+ const storageKey = state.storageKey;
8334
8840
  const sessionId = tombstone.sessionId;
8335
8841
  const projectManagementCleanup = async (): Promise<void> => {
8336
8842
  await this.projectManagementQueues.get(
8337
8843
  this.projectManagementKey(storageKey, sessionId),
8338
8844
  );
8339
8845
  await this.tombstoneProjectManagementSession(
8340
- storageKey,
8846
+ state,
8341
8847
  tombstone,
8342
8848
  );
8343
8849
  };
@@ -8355,18 +8861,20 @@ export class FlexHarness<TScope = unknown> {
8355
8861
  }
8356
8862
 
8357
8863
  private async tombstoneProjectManagementSession(
8358
- storageKey: string,
8864
+ state: IStorageState,
8359
8865
  sessionTombstone: IFlexSessionTombstone,
8360
8866
  ): Promise<void> {
8867
+ const storageKey = state.storageKey;
8361
8868
  const store = this.stores.projectManagement;
8362
8869
  const sessionId = sessionTombstone.sessionId;
8363
8870
  const generation = requireSessionGeneration(sessionTombstone);
8871
+ const sessionContext = this.createProjectManagementSessionContext(state, sessionTombstone);
8364
8872
  let lastConflict: FlexHarnessStoreConflictError | undefined;
8365
8873
  for (let attempt = 0; attempt <= maxProjectManagementTombstoneConflicts; attempt++) {
8366
8874
  const current = await store.load(
8367
8875
  storageKey,
8368
8876
  sessionId,
8369
- Object.freeze({ ...generation }),
8877
+ sessionContext,
8370
8878
  );
8371
8879
  if (current !== undefined) {
8372
8880
  assertFlexProjectManagementRecord(current);
@@ -8407,6 +8915,7 @@ export class FlexHarness<TScope = unknown> {
8407
8915
  sessionId,
8408
8916
  tombstone,
8409
8917
  expectedRevision,
8918
+ sessionContext,
8410
8919
  );
8411
8920
  } catch (error) {
8412
8921
  if (!(error instanceof FlexHarnessStoreConflictError)) throw error;
@@ -8576,6 +9085,9 @@ export class FlexHarness<TScope = unknown> {
8576
9085
  this.appendUnexpectedErrors(errors, settled[0].reason);
8577
9086
  }
8578
9087
  }
9088
+ errors.push(...await this.settleDelegatedRunAdmissionOwners(
9089
+ this.delegatedRunAdmissionOwnersForGeneration(state, tombstone),
9090
+ ));
8579
9091
  if (retained) {
8580
9092
  if (retained.stored.promptQueueDrain) {
8581
9093
  const settled = await Promise.allSettled([retained.stored.promptQueueDrain]);
@@ -8621,7 +9133,7 @@ export class FlexHarness<TScope = unknown> {
8621
9133
  releaseContext.scope,
8622
9134
  retained?.stored,
8623
9135
  );
8624
- await this.cleanupSessionDomains(state.storageKey, tombstone);
9136
+ await this.cleanupSessionDomains(state, tombstone);
8625
9137
  if (retained) retained.domainsCompleted = true;
8626
9138
  }
8627
9139
  }
@@ -8680,7 +9192,11 @@ export class FlexHarness<TScope = unknown> {
8680
9192
  for (const result of runResults) {
8681
9193
  if (result.status === 'rejected') this.appendUnexpectedErrors(cleanupErrors, result.reason);
8682
9194
  }
8683
- if (dependentRuns.length > 0) {
9195
+ if (
9196
+ dependentRuns.length > 0
9197
+ || (currentRun.delegatedRunAdmissionOwner !== undefined
9198
+ && state.delegatedRunAdmissionOwners.has(currentRun.delegatedRunAdmissionOwner))
9199
+ ) {
8684
9200
  state.fenceAdditionalErrors.push(cause, ...cleanupErrors);
8685
9201
  const stateLoad = this.stateLoads.get(state.storageKey);
8686
9202
  if (!stateLoad) {
@@ -8697,6 +9213,9 @@ export class FlexHarness<TScope = unknown> {
8697
9213
  void deferredDrain.catch(() => undefined);
8698
9214
  return;
8699
9215
  }
9216
+ cleanupErrors.push(...await this.settleDelegatedRunAdmissionOwners([
9217
+ ...state.delegatedRunAdmissionOwners,
9218
+ ]));
8700
9219
  const detachedCleanupAttempts = this.beginDetachedCleanupSettlement(state);
8701
9220
  await Promise.allSettled([...state.sessionInitializations.values()]);
8702
9221
  cleanupErrors.push(...await this.closeOrphanedResources(state.storageKey));
@@ -8784,6 +9303,7 @@ export class FlexHarness<TScope = unknown> {
8784
9303
  state.tombstoneCleanups.set(currentTombstoneRoot, currentTombstoneCleanup);
8785
9304
  }
8786
9305
  state.activeRuns.clear();
9306
+ state.delegatedRunAdmissionOwners.clear();
8787
9307
  state.pendingPermissions.clear();
8788
9308
  state.initializingSessions.clear();
8789
9309
  state.sessionInitializations.clear();
@@ -9221,6 +9741,9 @@ export class FlexHarness<TScope = unknown> {
9221
9741
  for (const result of runResults) {
9222
9742
  if (result.status === 'rejected') this.appendUnexpectedErrors(errors, result.reason);
9223
9743
  }
9744
+ errors.push(...await this.settleDelegatedRunAdmissionOwners([
9745
+ ...state.delegatedRunAdmissionOwners,
9746
+ ]));
9224
9747
  for (const stored of state.sessions.values()) {
9225
9748
  try {
9226
9749
  await this.abortStoredSession(
@@ -9319,6 +9842,7 @@ export class FlexHarness<TScope = unknown> {
9319
9842
  state.sessionDeletions.clear();
9320
9843
  state.tombstoneCleanups.clear();
9321
9844
  state.activeRuns.clear();
9845
+ state.delegatedRunAdmissionOwners.clear();
9322
9846
  state.pendingPermissions.clear();
9323
9847
  state.initializingSessions.clear();
9324
9848
  state.sessionInitializations.clear();