@wjarka/cezarion 0.14.9-dev.1020 → 0.14.9-dev.1027

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.
@@ -330,6 +330,8 @@ export declare class RunManager {
330
330
  private readonly workerWaitTimers;
331
331
  private readonly inboxClaimTimers;
332
332
  private reconcilingWorkers;
333
+ private reconcilingFamily;
334
+ private readonly pendingReconciliations;
333
335
  private recovering;
334
336
  /** Quiet window before unread input is resubmitted or left unconfirmed (#505). */
335
337
  private readonly unreadInputGraceMs;
@@ -880,7 +882,8 @@ export declare class RunManager {
880
882
  /** Request deadlines share the existing reconciliation timer registry, never a polling loop. */
881
883
  private reconcileConversations;
882
884
  /** Authority and wait live on disk; these collections are admission/timer caches only. */
883
- reconcileWorkerWaits(): void;
885
+ reconcileWorkerWaits(familyRootId?: string): void;
886
+ private reconcileWorkerFamily;
884
887
  queueWorkerWake(parentId: string): void;
885
888
  /** Shared turn-end path; asks win, worker waits precede DONE/monitoring/nudges. */
886
889
  private parkWorkerWait;
@@ -814,19 +814,24 @@ export class RunManager {
814
814
  workerWaitTimers = new Map();
815
815
  inboxClaimTimers = new Map();
816
816
  reconcilingWorkers = false;
817
+ reconcilingFamily;
818
+ pendingReconciliations = new Set();
817
819
  recovering = false;
818
820
  /** Quiet window before unread input is resubmitted or left unconfirmed (#505). */
819
821
  unreadInputGraceMs;
820
822
  disposed = false;
821
- onDelegationRun = (run) => {
823
+ onDelegationRun = (run, source) => {
822
824
  if (!this.disposed && !['queued', 'running', 'waiting'].includes(run.status))
823
825
  this.withdrawCiWait(run.id);
824
826
  if (run.delegation && !['queued', 'running', 'waiting'].includes(run.status))
825
827
  this.active.get(run.id)?.revokeDelegation?.();
826
- if (this.disposed || this.recovering || this.reconcilingWorkers)
828
+ // Cleanup checkpoints emit terminal records too. With delegation disabled,
829
+ // these observations must not replay the project's conversation histories.
830
+ if (this.disposed || this.recovering || (source === 'delegation-checkpoint' && process.env.CEZ_DELEGATION !== '1'))
827
831
  return;
828
- if (run.delegation && !['queued', 'running', 'waiting'].includes(run.status))
829
- this.reconcileWorkerWaits();
832
+ if (run.delegation && run.delegation.role !== 'invalid' && !['queued', 'running', 'waiting'].includes(run.status)) {
833
+ this.reconcileWorkerWaits(run.delegation.role === 'root' ? run.id : run.delegation.parentRunId);
834
+ }
830
835
  };
831
836
  pendingJobs = new Map();
832
837
  /** Interrupted agent turns recovered after a process restart. Unlike an
@@ -3613,9 +3618,9 @@ export class RunManager {
3613
3618
  }
3614
3619
  }
3615
3620
  /** Request deadlines share the existing reconciliation timer registry, never a polling loop. */
3616
- reconcileConversations() {
3621
+ reconcileConversations(family) {
3617
3622
  const now = new Date().toISOString();
3618
- for (const root of this.store.listRuns()) {
3623
+ for (const root of family) {
3619
3624
  if (root.delegation?.role !== 'root' || !root.delegation.conversation)
3620
3625
  continue;
3621
3626
  const next = reconcileConversationState(root, this.store.listRuns(), now, run => run.delegation?.role === 'worker' ? this.store.readWorkerExecution(run.id)?.phase === 'complete' : !this.isActive(run.id));
@@ -3653,13 +3658,33 @@ export class RunManager {
3653
3658
  }
3654
3659
  }
3655
3660
  /** Authority and wait live on disk; these collections are admission/timer caches only. */
3656
- reconcileWorkerWaits() {
3657
- if (this.disposed || this.reconcilingWorkers)
3661
+ reconcileWorkerWaits(familyRootId) {
3662
+ if (this.disposed)
3663
+ return;
3664
+ if (this.reconcilingWorkers) {
3665
+ // The current pass already observes its own writes. A different family
3666
+ // (or an explicit global pass) must not disappear behind the recursion guard.
3667
+ if (this.reconcilingFamily !== undefined && this.reconcilingFamily !== familyRootId) {
3668
+ this.pendingReconciliations.add(familyRootId);
3669
+ }
3658
3670
  return;
3671
+ }
3672
+ this.pendingReconciliations.add(familyRootId);
3673
+ for (const pending of this.pendingReconciliations) {
3674
+ this.pendingReconciliations.delete(pending);
3675
+ if (this.disposed)
3676
+ break;
3677
+ this.reconcileWorkerFamily(pending);
3678
+ }
3679
+ }
3680
+ reconcileWorkerFamily(familyRootId) {
3659
3681
  this.reconcilingWorkers = true;
3682
+ this.reconcilingFamily = familyRootId;
3660
3683
  try {
3661
- this.reconcileConversations();
3662
- for (const parent of this.store.listRuns()) {
3684
+ const inFamily = (run) => familyRootId === undefined || run.id === familyRootId ||
3685
+ (run.delegation?.role === 'worker' && run.delegation.parentRunId === familyRootId);
3686
+ this.reconcileConversations(this.store.listRuns().filter(inFamily));
3687
+ for (const parent of this.store.listRuns().filter(inFamily)) {
3663
3688
  if (!parent.delegation || parent.delegation.role === 'invalid' || this.historyDeletionPending(parent.id))
3664
3689
  continue;
3665
3690
  if (parent.status === 'cancelled' && parent.delegation.role === 'root' && parent.delegation.finishRequestedAt) {
@@ -3806,6 +3831,7 @@ export class RunManager {
3806
3831
  }
3807
3832
  finally {
3808
3833
  this.reconcilingWorkers = false;
3834
+ this.reconcilingFamily = undefined;
3809
3835
  }
3810
3836
  }
3811
3837
  queueWorkerWake(parentId) {