@modelprofile.com/flexharness 5.2.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,
@@ -297,6 +299,7 @@ interface IStorageState {
297
299
  tombstones: Map<string, IFlexSessionTombstone>;
298
300
  tombstoneCleanups: Map<string, Promise<void>>;
299
301
  activeRuns: Map<string, IActiveRun>;
302
+ delegatedRunAdmissionOwners: Set<IDelegatedRunAdmissionOwner>;
300
303
  pendingPermissions: Map<string, IPendingPermission>;
301
304
  scopeQueue: Promise<void>;
302
305
  lifecycle: 'active' | 'retiring' | 'fenced' | 'retired';
@@ -331,6 +334,7 @@ interface IActiveRun {
331
334
  callbacksClosed: boolean;
332
335
  reasoningPartIds: Map<string, string>;
333
336
  toolPartIds: Map<string, string>;
337
+ trustedToolExecutionErrors: Map<string, Error>;
334
338
  subagentCallCount: number;
335
339
  subagentSessionIds: Set<string>;
336
340
  pendingPermissionIds: Set<string>;
@@ -338,9 +342,56 @@ interface IActiveRun {
338
342
  reservedUserMessage?: IFlexMessage;
339
343
  reservedAssistantMessage?: IFlexMessage;
340
344
  modelResolution?: IFlexResolvedModel;
345
+ delegatedRunAdmissionOwner?: IDelegatedRunAdmissionOwner;
346
+ delegatedRunAdmissionClose?: Promise<void>;
341
347
  completion: Promise<IFlexPromptResult>;
342
348
  }
343
349
 
350
+ interface IDelegatedRunAdmissionSeed {
351
+ readonly state: IStorageState;
352
+ readonly childStored: IStoredSessionState;
353
+ readonly parentRun: IActiveRun;
354
+ readonly parentStored: IStoredSessionState;
355
+ readonly scopeId: string;
356
+ readonly scope: unknown;
357
+ readonly storageKey: string;
358
+ readonly sessionId: string;
359
+ readonly sessionGenerationId: string;
360
+ readonly sessionGenerationSequence: number;
361
+ readonly originParentRunId: string;
362
+ readonly originParentToolCallId: string;
363
+ readonly parentSessionId: string;
364
+ readonly parentSessionGenerationId: string;
365
+ readonly parentSessionGenerationSequence: number;
366
+ readonly parentQueueId: string;
367
+ readonly parentRunId: string;
368
+ readonly parentToolCallId: string;
369
+ readonly agent: string;
370
+ readonly depth: number;
371
+ }
372
+
373
+ type TDelegatedRunAdmissionOwnerStatus =
374
+ | 'acquiring'
375
+ | 'acquired'
376
+ | 'close-requested'
377
+ | 'retrying'
378
+ | 'closed';
379
+
380
+ interface IDelegatedRunAdmissionOwner {
381
+ readonly storageState: IStorageState;
382
+ readonly run: IActiveRun;
383
+ readonly context: Readonly<IFlexDelegatedRunAdmissionContext<unknown>>;
384
+ status: TDelegatedRunAdmissionOwnerStatus;
385
+ acquisitionSettled: boolean;
386
+ acquisitionDetached: boolean;
387
+ acquisitionCompletion?: Promise<void>;
388
+ acquisitionError?: Error;
389
+ lease?: IFlexDelegatedRunAdmissionLease;
390
+ closeAttempt?: Promise<void>;
391
+ closeAttemptSequence: number;
392
+ closeFailure?: { sequence: number; error: Error };
393
+ }
394
+
344
395
  interface IQueuedPrompt {
345
396
  state: IStorageState;
346
397
  stored: IStoredSessionState;
@@ -357,6 +408,7 @@ interface IQueuedPrompt {
357
408
  startedAt?: string;
358
409
  prompt?: INormalizedFlexPrompt;
359
410
  options?: IFlexPromptOptions;
411
+ delegationSeed?: IDelegatedRunAdmissionSeed;
360
412
  byteSize: number;
361
413
  completion: Promise<IFlexPromptResult>;
362
414
  resolveCompletion: (result: IFlexPromptResult) => void;
@@ -630,6 +682,7 @@ export class FlexHarness<TScope = unknown> {
630
682
  private readonly toolProvider: IFlexHarnessOptions<TScope>['toolProvider'];
631
683
  private readonly resourceToolProviderResolver: IFlexHarnessOptions<TScope>['resourceToolProviderResolver'];
632
684
  private readonly executionContextProvider: IFlexHarnessOptions<TScope>['executionContextProvider'];
685
+ private readonly delegatedRunAdmissionProvider: IFlexHarnessOptions<TScope>['delegatedRunAdmissionProvider'];
633
686
  private readonly stores: IFlexHarnessStores;
634
687
  private readonly agentSessionPolicy: IFlexAgentSessionPolicy<TScope>;
635
688
  private readonly toolOutputLimits: Required<NonNullable<IFlexHarnessOptions<TScope>['toolOutputLimits']>>;
@@ -693,6 +746,7 @@ export class FlexHarness<TScope = unknown> {
693
746
  this.toolProvider = options.toolProvider;
694
747
  this.resourceToolProviderResolver = options.resourceToolProviderResolver;
695
748
  this.executionContextProvider = options.executionContextProvider;
749
+ this.delegatedRunAdmissionProvider = options.delegatedRunAdmissionProvider;
696
750
  this.stores = requireHarnessStores(options.stores ?? new InMemoryFlexHarnessStores());
697
751
  this.agentSessionPolicy = normalizeAgentSessionPolicy(options.agentSessionPolicy);
698
752
  this.toolOutputLimits = resolveJsonLimits(options.toolOutputLimits);
@@ -1779,7 +1833,7 @@ export class FlexHarness<TScope = unknown> {
1779
1833
  promptOptions,
1780
1834
  undefined,
1781
1835
  undefined,
1782
- false,
1836
+ undefined,
1783
1837
  signal,
1784
1838
  true,
1785
1839
  );
@@ -3085,7 +3139,7 @@ export class FlexHarness<TScope = unknown> {
3085
3139
  options: IFlexPromptOptions,
3086
3140
  scheduleKey?: string,
3087
3141
  debounceMs?: number,
3088
- subagentAdmission = false,
3142
+ delegationSeed?: IDelegatedRunAdmissionSeed,
3089
3143
  admissionSignal?: AbortSignal,
3090
3144
  slashCommandAdmission = false,
3091
3145
  ): Promise<{
@@ -3145,11 +3199,14 @@ export class FlexHarness<TScope = unknown> {
3145
3199
  if (stored.pendingReversion) {
3146
3200
  throw new FlexHarnessSessionBusyError(sessionId, 'has a pending reversion operation');
3147
3201
  }
3148
- if (stored.session.agent !== undefined && !subagentAdmission) {
3202
+ if (stored.session.agent !== undefined && delegationSeed === undefined) {
3149
3203
  throw new FlexHarnessValidationError(
3150
3204
  'Subagent sessions can only be prompted through the foreground delegate tool.',
3151
3205
  );
3152
3206
  }
3207
+ if (delegationSeed !== undefined) {
3208
+ this.assertDelegatedRunAdmissionOwnership(delegationSeed, state, stored, scopeId);
3209
+ }
3153
3210
  if (stored.outstandingPromptsById.size >= this.promptQueueLimits.maxOutstandingPromptsPerSession) {
3154
3211
  throw new FlexHarnessQueueFullError(
3155
3212
  `Session "${sessionId}" has reached its outstanding prompt limit.`,
@@ -3188,7 +3245,7 @@ export class FlexHarness<TScope = unknown> {
3188
3245
  state,
3189
3246
  stored,
3190
3247
  scopeId,
3191
- scope: resolved.scope.scope,
3248
+ scope: delegationSeed === undefined ? resolved.scope.scope : delegationSeed.scope,
3192
3249
  sessionId,
3193
3250
  queueId: plugins.crypto.randomUUID(),
3194
3251
  queueSequence: ++this.promptQueueSequence,
@@ -3197,6 +3254,7 @@ export class FlexHarness<TScope = unknown> {
3197
3254
  ...(scheduleKey === undefined ? {} : { scheduleKey, debounceMs }),
3198
3255
  prompt: normalizedPrompt,
3199
3256
  options: normalizedOptions,
3257
+ ...(delegationSeed === undefined ? {} : { delegationSeed }),
3200
3258
  byteSize,
3201
3259
  completion,
3202
3260
  resolveCompletion,
@@ -3318,6 +3376,7 @@ export class FlexHarness<TScope = unknown> {
3318
3376
  callbacksClosed: false,
3319
3377
  reasoningPartIds: new Map(),
3320
3378
  toolPartIds: new Map(),
3379
+ trustedToolExecutionErrors: new Map(),
3321
3380
  subagentCallCount: 0,
3322
3381
  subagentSessionIds: new Set(),
3323
3382
  pendingPermissionIds: new Set(),
@@ -3336,6 +3395,258 @@ export class FlexHarness<TScope = unknown> {
3336
3395
  || run.stored.outstandingPromptsById.get(run.queueId) !== queued
3337
3396
  || run.state.activeRuns.get(run.sessionId) !== run
3338
3397
  ) throw this.trustInternalError(new FlexHarnessAbortError('The prompt was retired during admission.'));
3398
+ if (queued.delegationSeed) {
3399
+ this.assertDelegatedRunAdmissionOwnership(
3400
+ queued.delegationSeed,
3401
+ run.state,
3402
+ run.stored,
3403
+ run.scopeId,
3404
+ );
3405
+ if (
3406
+ run.queueId !== queued.queueId
3407
+ || run.runId !== queued.runId
3408
+ || run.sessionId !== queued.delegationSeed.sessionId
3409
+ ) {
3410
+ throw this.trustInternalError(
3411
+ new FlexHarnessAbortError('The delegated prompt lost its exact child run ownership.'),
3412
+ );
3413
+ }
3414
+ }
3415
+ }
3416
+
3417
+ private assertDelegatedRunAdmissionOwnership(
3418
+ seed: IDelegatedRunAdmissionSeed,
3419
+ state: IStorageState,
3420
+ childStored: IStoredSessionState,
3421
+ scopeId: string,
3422
+ ): void {
3423
+ const child = childStored.session;
3424
+ const parentRun = seed.parentRun;
3425
+ const parent = seed.parentStored.session;
3426
+ const parentPartId = parentRun.toolPartIds.get(seed.parentToolCallId);
3427
+ const parentPart = parentRun.callbackParts.find((part): part is IFlexToolMessagePart =>
3428
+ part.type === 'tool'
3429
+ && part.partId === parentPartId
3430
+ && part.toolCallId === seed.parentToolCallId);
3431
+ if (
3432
+ seed.state !== state
3433
+ || seed.storageKey !== state.storageKey
3434
+ || seed.scopeId !== scopeId
3435
+ || seed.childStored !== childStored
3436
+ || childStored.storageKey !== seed.storageKey
3437
+ || state.sessions.get(seed.sessionId) !== childStored
3438
+ || state.tombstones.has(seed.sessionId)
3439
+ || state.initializingSessions.has(seed.sessionId)
3440
+ || child.sessionId !== seed.sessionId
3441
+ || child.sessionGenerationId !== seed.sessionGenerationId
3442
+ || child.sessionGenerationSequence !== seed.sessionGenerationSequence
3443
+ || child.parentSessionId !== seed.parentSessionId
3444
+ || child.parentRunId !== seed.originParentRunId
3445
+ || child.parentToolCallId !== seed.originParentToolCallId
3446
+ || child.agent !== seed.agent
3447
+ || child.depth !== seed.depth
3448
+ || seed.parentStored !== parentRun.stored
3449
+ || seed.parentStored.storageKey !== seed.storageKey
3450
+ || parentRun.state !== state
3451
+ || parentRun.scopeId !== seed.scopeId
3452
+ || parentRun.scope !== seed.scope
3453
+ || parentRun.sessionId !== seed.parentSessionId
3454
+ || parentRun.queueId !== seed.parentQueueId
3455
+ || parentRun.runId !== seed.parentRunId
3456
+ || state.sessions.get(seed.parentSessionId) !== seed.parentStored
3457
+ || state.tombstones.has(seed.parentSessionId)
3458
+ || parent.sessionId !== seed.parentSessionId
3459
+ || parent.sessionGenerationId !== seed.parentSessionGenerationId
3460
+ || parent.sessionGenerationSequence !== seed.parentSessionGenerationSequence
3461
+ || state.activeRuns.get(seed.parentSessionId) !== parentRun
3462
+ || parentRun.callbacksClosed
3463
+ || parentRun.controller.signal.aborted
3464
+ || parentPart?.status !== 'running'
3465
+ ) {
3466
+ throw this.trustInternalError(
3467
+ new FlexHarnessAbortError('The delegated prompt lost its exact parent or child ownership.'),
3468
+ );
3469
+ }
3470
+ }
3471
+
3472
+ private async acquireDelegatedRunAdmission(
3473
+ queued: IQueuedPrompt,
3474
+ run: IActiveRun,
3475
+ ): Promise<void> {
3476
+ const seed = queued.delegationSeed;
3477
+ const provider = this.delegatedRunAdmissionProvider;
3478
+ if (!seed || !provider) return;
3479
+ const context = Object.freeze({
3480
+ scopeId: seed.scopeId,
3481
+ scope: seed.scope as TScope,
3482
+ storageKey: seed.storageKey,
3483
+ sessionId: seed.sessionId,
3484
+ sessionGenerationId: seed.sessionGenerationId,
3485
+ sessionGenerationSequence: seed.sessionGenerationSequence,
3486
+ queueId: queued.queueId,
3487
+ runId: run.runId,
3488
+ parentSessionId: seed.parentSessionId,
3489
+ parentSessionGenerationId: seed.parentSessionGenerationId,
3490
+ parentSessionGenerationSequence: seed.parentSessionGenerationSequence,
3491
+ parentQueueId: seed.parentQueueId,
3492
+ parentRunId: seed.parentRunId,
3493
+ parentToolCallId: seed.parentToolCallId,
3494
+ agent: seed.agent,
3495
+ depth: seed.depth,
3496
+ signal: run.controller.signal,
3497
+ });
3498
+ const owner: IDelegatedRunAdmissionOwner = {
3499
+ storageState: run.state,
3500
+ run,
3501
+ context,
3502
+ status: 'acquiring',
3503
+ acquisitionSettled: false,
3504
+ acquisitionDetached: false,
3505
+ closeAttemptSequence: 0,
3506
+ };
3507
+ run.state.delegatedRunAdmissionOwners.add(owner);
3508
+ run.delegatedRunAdmissionOwner = owner;
3509
+ const acquisition = Promise.resolve()
3510
+ .then(() => provider.acquireDelegatedRunAdmission(context))
3511
+ .then((lease) => this.normalizeDelegatedRunAdmissionLease(lease))
3512
+ .then(
3513
+ (lease) => {
3514
+ owner.lease = lease;
3515
+ owner.acquisitionSettled = true;
3516
+ if (owner.status === 'acquiring') owner.status = 'acquired';
3517
+ if (owner.status === 'close-requested') {
3518
+ void this.startDelegatedRunAdmissionCloseAttempt(owner).catch(() => undefined);
3519
+ }
3520
+ },
3521
+ (error: unknown) => {
3522
+ owner.acquisitionError = this.projectExternalError(
3523
+ run,
3524
+ error,
3525
+ 'delegatedRunAdmissionProvider',
3526
+ );
3527
+ owner.acquisitionSettled = true;
3528
+ owner.status = 'closed';
3529
+ run.state.delegatedRunAdmissionOwners.delete(owner);
3530
+ },
3531
+ );
3532
+ owner.acquisitionCompletion = acquisition;
3533
+
3534
+ let resolveAbort!: () => void;
3535
+ const abort = new Promise<'aborted'>((resolve) => {
3536
+ resolveAbort = () => resolve('aborted');
3537
+ });
3538
+ run.controller.signal.addEventListener('abort', resolveAbort, { once: true });
3539
+ if (run.controller.signal.aborted) resolveAbort();
3540
+ try {
3541
+ const outcome = await Promise.race([
3542
+ acquisition.then(() => 'settled' as const),
3543
+ abort,
3544
+ ]);
3545
+ if (outcome === 'aborted') {
3546
+ owner.acquisitionDetached = true;
3547
+ throw run.controller.signal.reason ?? this.trustInternalError(new FlexHarnessAbortError());
3548
+ }
3549
+ if (run.controller.signal.aborted) {
3550
+ throw run.controller.signal.reason ?? this.trustInternalError(new FlexHarnessAbortError());
3551
+ }
3552
+ if (owner.acquisitionError) throw owner.acquisitionError;
3553
+ if (owner.status !== 'acquired' || !owner.lease) {
3554
+ throw this.trustInternalError(
3555
+ new FlexHarnessAbortError('The delegated run admission was retired during acquisition.'),
3556
+ );
3557
+ }
3558
+ } finally {
3559
+ run.controller.signal.removeEventListener('abort', resolveAbort);
3560
+ }
3561
+ }
3562
+
3563
+ private normalizeDelegatedRunAdmissionLease(
3564
+ value: IFlexDelegatedRunAdmissionLease,
3565
+ ): IFlexDelegatedRunAdmissionLease {
3566
+ if (!value || (typeof value !== 'object' && typeof value !== 'function')) {
3567
+ throw new FlexHarnessValidationError(
3568
+ 'Delegated run admission provider returned an invalid lease.',
3569
+ );
3570
+ }
3571
+ const close = value.close;
3572
+ if (typeof close !== 'function') {
3573
+ throw new FlexHarnessValidationError(
3574
+ 'Delegated run admission provider returned an invalid lease.',
3575
+ );
3576
+ }
3577
+ return Object.freeze({ close: () => close.call(value) });
3578
+ }
3579
+
3580
+ private requestDelegatedRunAdmissionClose(
3581
+ owner: IDelegatedRunAdmissionOwner,
3582
+ ): Promise<void> {
3583
+ const priorAttemptSequence = owner.closeAttemptSequence;
3584
+ const joinedAttempt = owner.closeAttempt;
3585
+ if (owner.status === 'closed') return Promise.resolve();
3586
+ if (owner.status !== 'retrying') owner.status = 'close-requested';
3587
+ return (async () => {
3588
+ await owner.acquisitionCompletion;
3589
+ if (owner.status === 'closed') return;
3590
+ if (joinedAttempt) {
3591
+ await joinedAttempt;
3592
+ return;
3593
+ }
3594
+ if (owner.closeAttempt) {
3595
+ await owner.closeAttempt;
3596
+ return;
3597
+ }
3598
+ if (owner.closeFailure && owner.closeFailure.sequence > priorAttemptSequence) {
3599
+ throw owner.closeFailure.error;
3600
+ }
3601
+ await this.startDelegatedRunAdmissionCloseAttempt(owner);
3602
+ })();
3603
+ }
3604
+
3605
+ private startDelegatedRunAdmissionCloseAttempt(
3606
+ owner: IDelegatedRunAdmissionOwner,
3607
+ ): Promise<void> {
3608
+ if (owner.closeAttempt) return owner.closeAttempt;
3609
+ if (!owner.lease) {
3610
+ throw new Error('Acquired delegated run admission has no lease.');
3611
+ }
3612
+ owner.status = 'retrying';
3613
+ owner.closeFailure = undefined;
3614
+ const sequence = ++owner.closeAttemptSequence;
3615
+ let attempt!: Promise<void>;
3616
+ attempt = Promise.resolve()
3617
+ .then(() => owner.lease!.close())
3618
+ .then(() => {
3619
+ owner.status = 'closed';
3620
+ delete owner.lease;
3621
+ owner.storageState.delegatedRunAdmissionOwners.delete(owner);
3622
+ }, (error: unknown) => {
3623
+ const projected = this.projectExternalError(
3624
+ owner.run,
3625
+ error,
3626
+ 'delegatedRunAdmissionProvider',
3627
+ );
3628
+ owner.closeFailure = { sequence, error: projected };
3629
+ owner.status = 'close-requested';
3630
+ throw projected;
3631
+ })
3632
+ .finally(() => {
3633
+ if (owner.closeAttempt === attempt) owner.closeAttempt = undefined;
3634
+ });
3635
+ owner.closeAttempt = attempt;
3636
+ return attempt;
3637
+ }
3638
+
3639
+ private async closeRunDelegatedRunAdmission(
3640
+ run: IActiveRun,
3641
+ awaitSettlement = true,
3642
+ ): Promise<void> {
3643
+ const owner = run.delegatedRunAdmissionOwner;
3644
+ if (!owner) return;
3645
+ if (!run.delegatedRunAdmissionClose) {
3646
+ run.delegatedRunAdmissionClose = this.requestDelegatedRunAdmissionClose(owner);
3647
+ void run.delegatedRunAdmissionClose.catch(() => undefined);
3648
+ }
3649
+ if (awaitSettlement) await run.delegatedRunAdmissionClose;
3339
3650
  }
3340
3651
 
3341
3652
  private async promoteQueuedPrompt(queued: IQueuedPrompt, run: IActiveRun): Promise<void> {
@@ -3343,13 +3654,19 @@ export class FlexHarness<TScope = unknown> {
3343
3654
  const options = queued.options!;
3344
3655
  let projectionReserved = false;
3345
3656
  try {
3657
+ this.assertPromptPromotion(queued, run);
3658
+ await this.acquireDelegatedRunAdmission(queued, run);
3659
+ this.assertPromptPromotion(queued, run);
3346
3660
  await this.commitRevertedBranch(run.state, run.stored, run.scopeId, run.scope as TScope);
3661
+ this.assertPromptPromotion(queued, run);
3662
+ const generationPrompt = cloneSerializable(prompt.modelMessage.content) as Parameters<
3663
+ plugins.IAgentSession['beginGeneration']
3664
+ >[0];
3665
+ this.assertPromptPromotion(queued, run);
3347
3666
  run.transaction = await this.withRunCompactorContext(
3348
3667
  run,
3349
3668
  () => run.stored.agentSession.beginGeneration(
3350
- cloneSerializable(prompt.modelMessage.content) as Parameters<
3351
- plugins.IAgentSession['beginGeneration']
3352
- >[0],
3669
+ generationPrompt,
3353
3670
  { generationId: run.runId },
3354
3671
  ),
3355
3672
  );
@@ -3411,10 +3728,21 @@ export class FlexHarness<TScope = unknown> {
3411
3728
  let cleanupErrors: unknown[] = [];
3412
3729
  let cleanupFailure: unknown;
3413
3730
  try {
3414
- cleanupErrors = await this.rollbackAdmission(run, projectionReserved, safeError);
3731
+ await this.closeRunDelegatedRunAdmission(
3732
+ run,
3733
+ !run.delegatedRunAdmissionOwner?.acquisitionDetached,
3734
+ );
3735
+ } catch (closeError) {
3736
+ cleanupErrors.push(closeError);
3737
+ }
3738
+ try {
3739
+ cleanupErrors.push(...await this.rollbackAdmission(run, projectionReserved, safeError));
3415
3740
  } catch (rollbackError) {
3416
3741
  cleanupFailure = rollbackError;
3417
3742
  }
3743
+ if (cleanupFailure !== undefined && cleanupErrors.length > 0) {
3744
+ cleanupFailure = combineErrors([cleanupFailure, ...cleanupErrors]);
3745
+ }
3418
3746
  if (run.state.lifecycle === 'fenced' && cleanupFailure === undefined) {
3419
3747
  const combined = combineErrors([safeError, ...cleanupErrors]);
3420
3748
  try {
@@ -3526,6 +3854,7 @@ export class FlexHarness<TScope = unknown> {
3526
3854
  stored.outstandingPromptBytes = Math.max(0, stored.outstandingPromptBytes - queued.byteSize);
3527
3855
  delete queued.prompt;
3528
3856
  delete queued.options;
3857
+ delete queued.delegationSeed;
3529
3858
  const terminal: IFlexPromptQueueEntry = {
3530
3859
  ...this.projectPromptQueueEntry(queued),
3531
3860
  status,
@@ -3587,6 +3916,11 @@ export class FlexHarness<TScope = unknown> {
3587
3916
  }
3588
3917
 
3589
3918
  private purgeStoredPromptQueue(stored: IStoredSessionState): void {
3919
+ for (const queued of stored.outstandingPromptsById.values()) {
3920
+ delete queued.prompt;
3921
+ delete queued.options;
3922
+ delete queued.delegationSeed;
3923
+ }
3590
3924
  stored.promptQueue.length = 0;
3591
3925
  stored.outstandingPromptsById.clear();
3592
3926
  stored.terminalPromptQueueEntries.clear();
@@ -3606,6 +3940,7 @@ export class FlexHarness<TScope = unknown> {
3606
3940
  const result = normalizeRunResult(rawResult);
3607
3941
  if (!run.modelResolution) throw new FlexHarnessValidationError('Generation completed without a resolved model.');
3608
3942
  await this.finalizeRunReversion(run, 'completed');
3943
+ await this.closeRunDelegatedRunAdmission(run);
3609
3944
  const terminal = this.buildTerminal(run, 'completed', result);
3610
3945
  try {
3611
3946
  await this.mutateProjection(run.state, run.stored, () => {
@@ -3683,10 +4018,19 @@ export class FlexHarness<TScope = unknown> {
3683
4018
  ? run.ownerCancellation!
3684
4019
  : this.projectExternalError(run, error, generated ? 'persistence' : 'agentSession');
3685
4020
  const errors: unknown[] = [safeError];
4021
+ let reversionFailed = false;
3686
4022
  try {
3687
4023
  await this.finalizeRunReversion(run, cancelled ? 'cancelled' : 'failed');
3688
4024
  } catch (reversionError) {
3689
4025
  errors.push(reversionError);
4026
+ reversionFailed = true;
4027
+ }
4028
+ try {
4029
+ await this.closeRunDelegatedRunAdmission(run);
4030
+ } catch (closeError) {
4031
+ if (!errors.includes(closeError)) errors.push(closeError);
4032
+ }
4033
+ if (reversionFailed) {
3690
4034
  const combined = combineErrors(errors);
3691
4035
  await this.fenceNamespace(run.state, run, combined);
3692
4036
  throw combined;
@@ -4613,15 +4957,27 @@ export class FlexHarness<TScope = unknown> {
4613
4957
  .map((definition) => `- ${definition.name}: ${definition.description}`)
4614
4958
  .join('\n');
4615
4959
  return plugins.tool({
4616
- description: `Run one configured FlexHarness subagent in the foreground and return its final text.\nAvailable subagents:\n${available}`,
4960
+ 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
4961
  inputSchema: plugins.z.object({
4618
4962
  description: plugins.z.string(),
4619
4963
  prompt: plugins.z.string(),
4620
4964
  subagentType: plugins.z.string(),
4621
- taskId: plugins.z.string().optional(),
4965
+ taskId: plugins.z.string()
4966
+ .describe('Omit for a new child. For a later-run resume, use only the exact taskId returned by an earlier completed delegate call.')
4967
+ .optional(),
4622
4968
  }).strict(),
4623
- execute: (input: IFlexSubagentTaskInput, options?: { toolCallId?: string }) =>
4624
- this.executeSubagentTask(run, input, options?.toolCallId),
4969
+ execute: async (input: IFlexSubagentTaskInput, options?: { toolCallId?: string }) => {
4970
+ try {
4971
+ return await this.executeSubagentTask(run, input, options?.toolCallId);
4972
+ } catch (error) {
4973
+ if (
4974
+ options?.toolCallId
4975
+ && error instanceof FlexHarnessValidationError
4976
+ && this.trustedInternalErrors.has(error)
4977
+ ) run.trustedToolExecutionErrors.set(options.toolCallId, error);
4978
+ throw error;
4979
+ }
4980
+ },
4625
4981
  });
4626
4982
  }
4627
4983
 
@@ -4716,6 +5072,12 @@ export class FlexHarness<TScope = unknown> {
4716
5072
  ...(definition.system === undefined ? {} : { system: definition.system }),
4717
5073
  ...(definition.maxSteps === undefined ? {} : { maxSteps: definition.maxSteps }),
4718
5074
  };
5075
+ const delegationSeed = this.createDelegatedRunAdmissionSeed(
5076
+ run,
5077
+ child,
5078
+ definition,
5079
+ toolCallId,
5080
+ );
4719
5081
  queued = await this.enqueuePromptInternal(
4720
5082
  run.scopeId,
4721
5083
  child.session.sessionId,
@@ -4723,7 +5085,7 @@ export class FlexHarness<TScope = unknown> {
4723
5085
  childOptions,
4724
5086
  undefined,
4725
5087
  undefined,
4726
- true,
5088
+ delegationSeed,
4727
5089
  run.controller.signal,
4728
5090
  );
4729
5091
  admission = await queued.started;
@@ -4777,6 +5139,42 @@ export class FlexHarness<TScope = unknown> {
4777
5139
  }
4778
5140
  }
4779
5141
 
5142
+ private createDelegatedRunAdmissionSeed(
5143
+ run: IActiveRun,
5144
+ child: IStoredSessionState,
5145
+ definition: Readonly<IFlexSubagentDefinition>,
5146
+ toolCallId: string,
5147
+ ): IDelegatedRunAdmissionSeed {
5148
+ const childGeneration = requireSessionGeneration(child.session);
5149
+ const parentGeneration = requireSessionGeneration(run.stored.session);
5150
+ if (!child.session.parentRunId || !child.session.parentToolCallId) {
5151
+ throw new FlexHarnessValidationError('Subagent session origin metadata is missing.');
5152
+ }
5153
+ const seed = Object.freeze({
5154
+ state: run.state,
5155
+ childStored: child,
5156
+ parentRun: run,
5157
+ parentStored: run.stored,
5158
+ scopeId: run.scopeId,
5159
+ scope: run.scope,
5160
+ storageKey: run.state.storageKey,
5161
+ sessionId: child.session.sessionId,
5162
+ ...childGeneration,
5163
+ originParentRunId: child.session.parentRunId,
5164
+ originParentToolCallId: child.session.parentToolCallId,
5165
+ parentSessionId: run.sessionId,
5166
+ parentSessionGenerationId: parentGeneration.sessionGenerationId,
5167
+ parentSessionGenerationSequence: parentGeneration.sessionGenerationSequence,
5168
+ parentQueueId: run.queueId,
5169
+ parentRunId: run.runId,
5170
+ parentToolCallId: toolCallId,
5171
+ agent: definition.name,
5172
+ depth: (run.stored.session.depth ?? 0) + 1,
5173
+ });
5174
+ this.assertDelegatedRunAdmissionOwnership(seed, run.state, child, run.scopeId);
5175
+ return seed;
5176
+ }
5177
+
4780
5178
  private async acquireSubagentSession(
4781
5179
  run: IActiveRun,
4782
5180
  definition: Readonly<IFlexSubagentDefinition>,
@@ -4865,7 +5263,11 @@ export class FlexHarness<TScope = unknown> {
4865
5263
  return;
4866
5264
  }
4867
5265
  if (taskId !== undefined || state.tombstones.has(sessionId)) {
4868
- throw new FlexHarnessNotFoundError('Subagent task', sessionId);
5266
+ throw this.trustInternalError(
5267
+ new FlexHarnessValidationError(
5268
+ '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.',
5269
+ ),
5270
+ );
4869
5271
  }
4870
5272
  const timestamp = new Date().toISOString();
4871
5273
  metadata = {
@@ -6430,10 +6832,12 @@ export class FlexHarness<TScope = unknown> {
6430
6832
  const part = run.callbackParts.find((entry) => entry.partId === partId && entry.type === 'tool');
6431
6833
  if (!part || part.type !== 'tool' || part.status !== 'running') return;
6432
6834
  try {
6835
+ const trustedExecutionError = run.trustedToolExecutionErrors.get(event.toolCallId);
6836
+ run.trustedToolExecutionErrors.delete(event.toolCallId);
6433
6837
  const output = event.success ? normalizeJsonValue(event.output, this.toolOutputLimits) : undefined;
6434
6838
  const projectedError = event.success
6435
6839
  ? undefined
6436
- : this.projectExternalError(run, event.error, 'toolCallback');
6840
+ : this.projectExternalError(run, trustedExecutionError ?? event.error, 'toolCallback');
6437
6841
  const bytes = event.success ? jsonBytes(output) : Buffer.byteLength(projectedError!.message);
6438
6842
  if (!this.reserveCallbackCapacity(run, 1, bytes, 0)) return;
6439
6843
  if (event.success) {
@@ -7166,6 +7570,39 @@ export class FlexHarness<TScope = unknown> {
7166
7570
  .filter((error): error is Error => error !== undefined);
7167
7571
  }
7168
7572
 
7573
+ private delegatedRunAdmissionOwnersForGeneration(
7574
+ state: IStorageState,
7575
+ session: Readonly<Pick<
7576
+ IFlexSession,
7577
+ 'sessionId' | 'sessionGenerationId' | 'sessionGenerationSequence'
7578
+ >>,
7579
+ ): IDelegatedRunAdmissionOwner[] {
7580
+ return [...state.delegatedRunAdmissionOwners].filter((owner) =>
7581
+ owner.storageState === state
7582
+ && owner.context.sessionId === session.sessionId
7583
+ && owner.context.sessionGenerationId === session.sessionGenerationId
7584
+ && owner.context.sessionGenerationSequence === session.sessionGenerationSequence);
7585
+ }
7586
+
7587
+ private async settleDelegatedRunAdmissionOwners(
7588
+ owners: readonly IDelegatedRunAdmissionOwner[],
7589
+ ): Promise<Error[]> {
7590
+ const results = await Promise.allSettled(
7591
+ owners.map((owner) => this.requestDelegatedRunAdmissionClose(owner)),
7592
+ );
7593
+ return results.flatMap((result, index) => {
7594
+ if (result.status === 'fulfilled') return [];
7595
+ const error = result.reason instanceof Error
7596
+ ? result.reason
7597
+ : this.projectExternalError(
7598
+ owners[index].run,
7599
+ result.reason,
7600
+ 'delegatedRunAdmissionProvider',
7601
+ );
7602
+ return [error];
7603
+ });
7604
+ }
7605
+
7169
7606
  private projectExternalError(
7170
7607
  run: IActiveRun,
7171
7608
  error: unknown,
@@ -7288,6 +7725,7 @@ export class FlexHarness<TScope = unknown> {
7288
7725
  tombstones: new Map(snapshot.tombstones.map((entry) => [entry.sessionId, entry])),
7289
7726
  tombstoneCleanups: new Map(),
7290
7727
  activeRuns: new Map(),
7728
+ delegatedRunAdmissionOwners: new Set(),
7291
7729
  pendingPermissions: new Map(),
7292
7730
  scopeQueue: Promise.resolve(),
7293
7731
  lifecycle: 'active',
@@ -8576,6 +9014,9 @@ export class FlexHarness<TScope = unknown> {
8576
9014
  this.appendUnexpectedErrors(errors, settled[0].reason);
8577
9015
  }
8578
9016
  }
9017
+ errors.push(...await this.settleDelegatedRunAdmissionOwners(
9018
+ this.delegatedRunAdmissionOwnersForGeneration(state, tombstone),
9019
+ ));
8579
9020
  if (retained) {
8580
9021
  if (retained.stored.promptQueueDrain) {
8581
9022
  const settled = await Promise.allSettled([retained.stored.promptQueueDrain]);
@@ -8680,7 +9121,11 @@ export class FlexHarness<TScope = unknown> {
8680
9121
  for (const result of runResults) {
8681
9122
  if (result.status === 'rejected') this.appendUnexpectedErrors(cleanupErrors, result.reason);
8682
9123
  }
8683
- if (dependentRuns.length > 0) {
9124
+ if (
9125
+ dependentRuns.length > 0
9126
+ || (currentRun.delegatedRunAdmissionOwner !== undefined
9127
+ && state.delegatedRunAdmissionOwners.has(currentRun.delegatedRunAdmissionOwner))
9128
+ ) {
8684
9129
  state.fenceAdditionalErrors.push(cause, ...cleanupErrors);
8685
9130
  const stateLoad = this.stateLoads.get(state.storageKey);
8686
9131
  if (!stateLoad) {
@@ -8697,6 +9142,9 @@ export class FlexHarness<TScope = unknown> {
8697
9142
  void deferredDrain.catch(() => undefined);
8698
9143
  return;
8699
9144
  }
9145
+ cleanupErrors.push(...await this.settleDelegatedRunAdmissionOwners([
9146
+ ...state.delegatedRunAdmissionOwners,
9147
+ ]));
8700
9148
  const detachedCleanupAttempts = this.beginDetachedCleanupSettlement(state);
8701
9149
  await Promise.allSettled([...state.sessionInitializations.values()]);
8702
9150
  cleanupErrors.push(...await this.closeOrphanedResources(state.storageKey));
@@ -8784,6 +9232,7 @@ export class FlexHarness<TScope = unknown> {
8784
9232
  state.tombstoneCleanups.set(currentTombstoneRoot, currentTombstoneCleanup);
8785
9233
  }
8786
9234
  state.activeRuns.clear();
9235
+ state.delegatedRunAdmissionOwners.clear();
8787
9236
  state.pendingPermissions.clear();
8788
9237
  state.initializingSessions.clear();
8789
9238
  state.sessionInitializations.clear();
@@ -9221,6 +9670,9 @@ export class FlexHarness<TScope = unknown> {
9221
9670
  for (const result of runResults) {
9222
9671
  if (result.status === 'rejected') this.appendUnexpectedErrors(errors, result.reason);
9223
9672
  }
9673
+ errors.push(...await this.settleDelegatedRunAdmissionOwners([
9674
+ ...state.delegatedRunAdmissionOwners,
9675
+ ]));
9224
9676
  for (const stored of state.sessions.values()) {
9225
9677
  try {
9226
9678
  await this.abortStoredSession(
@@ -9319,6 +9771,7 @@ export class FlexHarness<TScope = unknown> {
9319
9771
  state.sessionDeletions.clear();
9320
9772
  state.tombstoneCleanups.clear();
9321
9773
  state.activeRuns.clear();
9774
+ state.delegatedRunAdmissionOwners.clear();
9322
9775
  state.pendingPermissions.clear();
9323
9776
  state.initializingSessions.clear();
9324
9777
  state.sessionInitializations.clear();