@omercnet/paseo-omp 0.3.0-next.101.1 → 0.3.0-next.102.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.
- package/package.json +1 -1
- package/server/provider/session.ts +2 -1
- package/server/provider/subsessions.ts +290 -72
package/package.json
CHANGED
|
@@ -4554,7 +4554,8 @@ export class OmpProviderSession {
|
|
|
4554
4554
|
turn.deferredAgentEnd = candidate;
|
|
4555
4555
|
void this.subsessions.reconcile(this.runtime).catch(() => {
|
|
4556
4556
|
if (!turn.terminal && this.activeTurn === turn) {
|
|
4557
|
-
this.
|
|
4557
|
+
this.subsessions?.terminalize("failed");
|
|
4558
|
+
this.resumeDeferredAgentEnd();
|
|
4558
4559
|
}
|
|
4559
4560
|
});
|
|
4560
4561
|
return true;
|
|
@@ -21,13 +21,15 @@ import { OmpTimelineProjector, type OmpTimelineScheduler } from "./timeline-proj
|
|
|
21
21
|
|
|
22
22
|
const MAX_CHILDREN = 1_024;
|
|
23
23
|
const MAX_TASK_DISPATCHES = 4_096;
|
|
24
|
-
const MAX_BUFFERED_EVENTS =
|
|
24
|
+
const MAX_BUFFERED_EVENTS = MAX_CHILDREN * 3;
|
|
25
25
|
const MAX_BUFFERED_BYTES = 4 * 1024 * 1024;
|
|
26
26
|
const MAX_CHILD_MESSAGE_IDENTITIES = 2_048;
|
|
27
27
|
const MAX_REPLAY_MESSAGES = 100_000;
|
|
28
28
|
const MAX_REPLAY_BYTES = 64 * 1024 * 1024;
|
|
29
29
|
const MAX_REPLAY_NODES = 400_000;
|
|
30
30
|
const MAX_REPLAY_DEPTH = 16;
|
|
31
|
+
const CHILD_REPLAY_UNAVAILABLE = "OMP subagent history is unavailable or incomplete";
|
|
32
|
+
type BufferedSubagentEvent = { event: OmpSubagentEvent; bytes: number };
|
|
31
33
|
|
|
32
34
|
type Emit = (event: ProviderEvent) => void;
|
|
33
35
|
type ChildTerminalStatus = "completed" | "failed" | "canceled";
|
|
@@ -53,6 +55,7 @@ type ChildState = {
|
|
|
53
55
|
status: ChildStatus;
|
|
54
56
|
terminalRequested?: ChildTerminalStatus;
|
|
55
57
|
sessionClosed: boolean;
|
|
58
|
+
replayUnavailable?: boolean;
|
|
56
59
|
seenInSnapshot: boolean;
|
|
57
60
|
seenAssistantIdentities: BoundedStringSet;
|
|
58
61
|
projector: OmpTimelineProjector;
|
|
@@ -63,6 +66,7 @@ type TaskDispatch = {
|
|
|
63
66
|
childSessionIds: Set<string>;
|
|
64
67
|
acknowledged: boolean;
|
|
65
68
|
};
|
|
69
|
+
type ReplayHistory = { sessionFile: string; messages: OmpMessage[] };
|
|
66
70
|
type ReplayBudget = { messages: number; bytes: number; nodes: number };
|
|
67
71
|
const TaskArgsSchema = z.object({
|
|
68
72
|
tasks: z.array(z.unknown()).max(MAX_CHILDREN).optional(),
|
|
@@ -302,12 +306,18 @@ function terminalStatus(status: string): ChildTerminalStatus | undefined {
|
|
|
302
306
|
return;
|
|
303
307
|
}
|
|
304
308
|
|
|
309
|
+
function bufferedNativeId(event: OmpSubagentEvent): string {
|
|
310
|
+
return event.type === "subagent_progress" ? event.payload.progress.id : event.payload.id;
|
|
311
|
+
}
|
|
312
|
+
|
|
305
313
|
export class OmpSubsessionProjector {
|
|
306
314
|
private readonly children = new Map<string, ChildState>();
|
|
307
315
|
private readonly sessionIdByNativeId = new Map<string, string>();
|
|
308
316
|
private readonly toolOwners = new Map<string, string>();
|
|
309
317
|
private readonly dispatches = new Map<string, TaskDispatch>();
|
|
310
|
-
private readonly bufferedEvents:
|
|
318
|
+
private readonly bufferedEvents: BufferedSubagentEvent[] = [];
|
|
319
|
+
// null means every child observed during this replay is omitted after tombstone saturation.
|
|
320
|
+
private omittedBufferedChildren: Set<string> | null = new Set();
|
|
311
321
|
private bufferedBytes = 0;
|
|
312
322
|
private replaying = false;
|
|
313
323
|
private closed = false;
|
|
@@ -374,15 +384,7 @@ export class OmpSubsessionProjector {
|
|
|
374
384
|
handle(event: OmpSubagentEvent): void {
|
|
375
385
|
if (this.closed) return;
|
|
376
386
|
if (this.replaying) {
|
|
377
|
-
|
|
378
|
-
throw new OmpPublicError("OMP subagent replay event limit reached");
|
|
379
|
-
}
|
|
380
|
-
const bytes = boundedJsonBytes(event, MAX_BUFFERED_BYTES, 1_024, MAX_BUFFERED_BYTES, 4_096);
|
|
381
|
-
if (bytes === Number.POSITIVE_INFINITY || this.bufferedBytes + bytes > MAX_BUFFERED_BYTES) {
|
|
382
|
-
throw new OmpPublicError("OMP subagent replay event limit reached");
|
|
383
|
-
}
|
|
384
|
-
this.bufferedEvents.push(event);
|
|
385
|
-
this.bufferedBytes += bytes;
|
|
387
|
+
this.bufferEvent(event);
|
|
386
388
|
return;
|
|
387
389
|
}
|
|
388
390
|
this.apply(event);
|
|
@@ -409,23 +411,48 @@ export class OmpSubsessionProjector {
|
|
|
409
411
|
signal,
|
|
410
412
|
0,
|
|
411
413
|
);
|
|
412
|
-
|
|
414
|
+
let snapshots: OmpSubagentSnapshot[] = [];
|
|
415
|
+
try {
|
|
416
|
+
snapshots = await waitForReplay(runtimeSession.getSubagents(), signal);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (signal.aborted) throw error;
|
|
419
|
+
}
|
|
413
420
|
await this.replaySnapshots(snapshots, runtimeSession, runtime, visited, budget, signal);
|
|
414
421
|
signal.throwIfAborted();
|
|
415
422
|
this.reconcileSnapshots(snapshots);
|
|
416
423
|
completed = true;
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (signal.aborted) throw error;
|
|
426
|
+
this.terminalize("failed");
|
|
427
|
+
completed = true;
|
|
417
428
|
} finally {
|
|
418
429
|
this.replaying = false;
|
|
419
430
|
const buffered = this.bufferedEvents.splice(0);
|
|
420
431
|
this.bufferedBytes = 0;
|
|
421
432
|
if (completed && !signal.aborted) {
|
|
422
|
-
for (const event of buffered)
|
|
433
|
+
for (const { event } of buffered) {
|
|
434
|
+
if (
|
|
435
|
+
event.type === "subagent_progress" &&
|
|
436
|
+
!terminalStatus(event.payload.progress.status)
|
|
437
|
+
) {
|
|
438
|
+
const sessionId = this.sessionIdByNativeId.get(event.payload.progress.id);
|
|
439
|
+
const child = sessionId ? this.children.get(sessionId) : undefined;
|
|
440
|
+
if (child && child.status !== "running") continue;
|
|
441
|
+
}
|
|
442
|
+
try {
|
|
443
|
+
this.apply(event);
|
|
444
|
+
} catch {
|
|
445
|
+
this.terminalize("failed");
|
|
446
|
+
}
|
|
447
|
+
}
|
|
423
448
|
} else {
|
|
449
|
+
this.terminalize("failed");
|
|
424
450
|
this.closed = true;
|
|
425
451
|
for (const child of this.children.values()) child.projector.close();
|
|
426
452
|
this.dispatches.clear();
|
|
427
453
|
this.toolOwners.clear();
|
|
428
454
|
}
|
|
455
|
+
this.omittedBufferedChildren = new Set();
|
|
429
456
|
}
|
|
430
457
|
}
|
|
431
458
|
|
|
@@ -460,6 +487,126 @@ export class OmpSubsessionProjector {
|
|
|
460
487
|
}
|
|
461
488
|
this.bufferedEvents.length = 0;
|
|
462
489
|
this.bufferedBytes = 0;
|
|
490
|
+
this.omittedBufferedChildren = new Set();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private bufferEvent(event: OmpSubagentEvent): void {
|
|
494
|
+
const isProgress = event.type === "subagent_progress";
|
|
495
|
+
const progressTerminal = isProgress ? terminalStatus(event.payload.progress.status) : undefined;
|
|
496
|
+
const isAdvisoryProgress = isProgress && !progressTerminal;
|
|
497
|
+
const bufferedEvent: OmpSubagentEvent =
|
|
498
|
+
isProgress && progressTerminal
|
|
499
|
+
? {
|
|
500
|
+
type: "subagent_lifecycle",
|
|
501
|
+
payload: {
|
|
502
|
+
id: event.payload.progress.id,
|
|
503
|
+
agent: event.payload.agent,
|
|
504
|
+
status: event.payload.progress.status,
|
|
505
|
+
index: event.payload.index,
|
|
506
|
+
...(event.payload.agentSource ? { agentSource: event.payload.agentSource } : {}),
|
|
507
|
+
...(event.payload.parentToolCallId
|
|
508
|
+
? { parentToolCallId: event.payload.parentToolCallId }
|
|
509
|
+
: {}),
|
|
510
|
+
...(event.payload.detached !== undefined ? { detached: event.payload.detached } : {}),
|
|
511
|
+
},
|
|
512
|
+
}
|
|
513
|
+
: event;
|
|
514
|
+
const incomingId = bufferedNativeId(bufferedEvent);
|
|
515
|
+
if (this.isBufferedChildOmitted(incomingId)) return;
|
|
516
|
+
const bytes = boundedJsonBytes(
|
|
517
|
+
bufferedEvent,
|
|
518
|
+
MAX_BUFFERED_BYTES,
|
|
519
|
+
1_024,
|
|
520
|
+
MAX_BUFFERED_BYTES,
|
|
521
|
+
4_096,
|
|
522
|
+
);
|
|
523
|
+
if (bytes === Number.POSITIVE_INFINITY) {
|
|
524
|
+
if (!isAdvisoryProgress) this.omitBufferedChild(incomingId);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (isAdvisoryProgress) {
|
|
528
|
+
for (let index = this.bufferedEvents.length - 1; index >= 0; index -= 1) {
|
|
529
|
+
const queued = this.bufferedEvents[index]?.event;
|
|
530
|
+
if (!queued) continue;
|
|
531
|
+
if (
|
|
532
|
+
queued.type === "subagent_lifecycle" &&
|
|
533
|
+
queued.payload.id === incomingId &&
|
|
534
|
+
terminalStatus(queued.payload.status)
|
|
535
|
+
) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (queued.type !== "subagent_progress" || queued.payload.progress.id !== incomingId) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (terminalStatus(queued.payload.progress.status)) return;
|
|
542
|
+
const [removed] = this.bufferedEvents.splice(index, 1);
|
|
543
|
+
this.bufferedBytes -= removed?.bytes ?? 0;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const bufferedIds = new Set<string>();
|
|
549
|
+
for (const child of this.children.values()) bufferedIds.add(child.nativeId);
|
|
550
|
+
for (const { event: queued } of this.bufferedEvents) bufferedIds.add(bufferedNativeId(queued));
|
|
551
|
+
if (!bufferedIds.has(incomingId) && bufferedIds.size >= MAX_CHILDREN) {
|
|
552
|
+
if (isAdvisoryProgress) return;
|
|
553
|
+
const advisory = this.bufferedEvents.find(
|
|
554
|
+
({ event: queued }) =>
|
|
555
|
+
queued.type === "subagent_progress" && !terminalStatus(queued.payload.progress.status),
|
|
556
|
+
);
|
|
557
|
+
if (!advisory) {
|
|
558
|
+
this.omitBufferedChild(incomingId);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const evictedId = bufferedNativeId(advisory.event);
|
|
562
|
+
this.omitBufferedChild(evictedId);
|
|
563
|
+
if (this.isBufferedChildOmitted(incomingId)) return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
while (
|
|
567
|
+
this.bufferedEvents.length >= MAX_BUFFERED_EVENTS ||
|
|
568
|
+
this.bufferedBytes + bytes > MAX_BUFFERED_BYTES
|
|
569
|
+
) {
|
|
570
|
+
const advisoryIndex = this.bufferedEvents.findIndex(
|
|
571
|
+
({ event: queued }) =>
|
|
572
|
+
queued.type === "subagent_progress" && !terminalStatus(queued.payload.progress.status),
|
|
573
|
+
);
|
|
574
|
+
if (advisoryIndex < 0) {
|
|
575
|
+
if (!isAdvisoryProgress) this.omitBufferedChild(incomingId);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const [removed] = this.bufferedEvents.splice(advisoryIndex, 1);
|
|
579
|
+
this.bufferedBytes -= removed?.bytes ?? 0;
|
|
580
|
+
}
|
|
581
|
+
this.bufferedEvents.push({ event: bufferedEvent, bytes });
|
|
582
|
+
this.bufferedBytes += bytes;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private isBufferedChildOmitted(nativeId: string): boolean {
|
|
586
|
+
return this.omittedBufferedChildren === null || this.omittedBufferedChildren.has(nativeId);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private omitBufferedChild(nativeId: string): void {
|
|
590
|
+
const omitted = this.omittedBufferedChildren;
|
|
591
|
+
if (!omitted || omitted.has(nativeId)) return;
|
|
592
|
+
if (omitted.size >= MAX_CHILDREN) {
|
|
593
|
+
this.omittedBufferedChildren = null;
|
|
594
|
+
this.bufferedEvents.length = 0;
|
|
595
|
+
this.bufferedBytes = 0;
|
|
596
|
+
for (const child of this.children.values()) child.replayUnavailable = true;
|
|
597
|
+
this.terminalize("failed");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
omitted.add(nativeId);
|
|
601
|
+
for (let index = this.bufferedEvents.length - 1; index >= 0; index -= 1) {
|
|
602
|
+
const queued = this.bufferedEvents[index];
|
|
603
|
+
if (!queued || bufferedNativeId(queued.event) !== nativeId) continue;
|
|
604
|
+
const [removed] = this.bufferedEvents.splice(index, 1);
|
|
605
|
+
this.bufferedBytes -= removed?.bytes ?? 0;
|
|
606
|
+
}
|
|
607
|
+
const sessionId = this.sessionIdByNativeId.get(nativeId);
|
|
608
|
+
const child = sessionId ? this.children.get(sessionId) : undefined;
|
|
609
|
+
if (child?.status === "running") this.failReplayChild(child);
|
|
463
610
|
}
|
|
464
611
|
|
|
465
612
|
private apply(event: OmpSubagentEvent): void {
|
|
@@ -587,6 +734,7 @@ export class OmpSubsessionProjector {
|
|
|
587
734
|
}
|
|
588
735
|
|
|
589
736
|
private restartChild(child: ChildState): void {
|
|
737
|
+
child.replayUnavailable = false;
|
|
590
738
|
if (child.status === "running") return;
|
|
591
739
|
child.status = "running";
|
|
592
740
|
child.terminalRequested = undefined;
|
|
@@ -607,7 +755,12 @@ export class OmpSubsessionProjector {
|
|
|
607
755
|
if (!this.hasDirectActivity(child.sessionId)) this.finishChild(child, status);
|
|
608
756
|
}
|
|
609
757
|
|
|
610
|
-
private finishChild(
|
|
758
|
+
private finishChild(
|
|
759
|
+
child: ChildState,
|
|
760
|
+
status: ChildTerminalStatus,
|
|
761
|
+
force = false,
|
|
762
|
+
errorMessage = "OMP subagent failed",
|
|
763
|
+
): void {
|
|
611
764
|
if (child.status !== "running") return;
|
|
612
765
|
if (!force && this.hasDirectActivity(child.sessionId)) {
|
|
613
766
|
child.terminalRequested = status;
|
|
@@ -620,7 +773,7 @@ export class OmpSubsessionProjector {
|
|
|
620
773
|
sessionId: child.sessionId,
|
|
621
774
|
turnId: child.turnId,
|
|
622
775
|
state: status,
|
|
623
|
-
...(status === "failed" ? { error: { message:
|
|
776
|
+
...(status === "failed" ? { error: { message: errorMessage } } : {}),
|
|
624
777
|
});
|
|
625
778
|
for (const [toolCallId, dispatch] of this.dispatches) {
|
|
626
779
|
if (dispatch.childSessionIds.has(child.sessionId)) this.settleDispatch(toolCallId, dispatch);
|
|
@@ -631,6 +784,10 @@ export class OmpSubsessionProjector {
|
|
|
631
784
|
}
|
|
632
785
|
this.onActivityChange();
|
|
633
786
|
}
|
|
787
|
+
private failReplayChild(child: ChildState): void {
|
|
788
|
+
child.replayUnavailable = true;
|
|
789
|
+
this.finishChild(child, "failed", true, CHILD_REPLAY_UNAVAILABLE);
|
|
790
|
+
}
|
|
634
791
|
|
|
635
792
|
private hasDirectActivity(ownerSessionId: string): boolean {
|
|
636
793
|
for (const child of this.children.values()) {
|
|
@@ -691,6 +848,10 @@ export class OmpSubsessionProjector {
|
|
|
691
848
|
this.resolveParent(snapshot.parentToolCallId, snapshot.sessionFile),
|
|
692
849
|
);
|
|
693
850
|
present.add(child.nativeId);
|
|
851
|
+
if (child.replayUnavailable) {
|
|
852
|
+
child.seenInSnapshot = true;
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
694
855
|
const terminal = terminalStatus(snapshot.status);
|
|
695
856
|
if (terminal) this.requestTerminal(child, terminal);
|
|
696
857
|
else this.restartChild(child);
|
|
@@ -712,36 +873,75 @@ export class OmpSubsessionProjector {
|
|
|
712
873
|
budget: ReplayBudget,
|
|
713
874
|
signal: AbortSignal,
|
|
714
875
|
): Promise<void> {
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
876
|
+
const collected: Array<{
|
|
877
|
+
snapshot: OmpSubagentSnapshot;
|
|
878
|
+
sessionFile?: string;
|
|
879
|
+
messages?: OmpMessage[];
|
|
880
|
+
}> = [];
|
|
881
|
+
for (const snapshot of snapshots) {
|
|
720
882
|
signal.throwIfAborted();
|
|
721
883
|
if (this.sessionIdByNativeId.has(snapshot.id)) continue;
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
884
|
+
try {
|
|
885
|
+
const history = await waitForReplay(
|
|
886
|
+
runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
|
|
887
|
+
signal,
|
|
888
|
+
);
|
|
889
|
+
try {
|
|
890
|
+
this.accountReplay(history.messages, budget, signal);
|
|
891
|
+
collected.push({
|
|
892
|
+
snapshot,
|
|
893
|
+
sessionFile: history.sessionFile,
|
|
894
|
+
messages: history.messages,
|
|
895
|
+
});
|
|
896
|
+
} catch (error) {
|
|
897
|
+
if (signal.aborted) throw error;
|
|
898
|
+
collected.push({ snapshot, sessionFile: history.sessionFile });
|
|
899
|
+
}
|
|
900
|
+
} catch (error) {
|
|
901
|
+
if (signal.aborted) throw error;
|
|
902
|
+
collected.push({ snapshot });
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
collected.sort((left, right) => {
|
|
906
|
+
const leftDepth = left.sessionFile?.split("/").length ?? Number.MAX_SAFE_INTEGER;
|
|
907
|
+
const rightDepth = right.sessionFile?.split("/").length ?? Number.MAX_SAFE_INTEGER;
|
|
908
|
+
return leftDepth - rightDepth;
|
|
909
|
+
});
|
|
910
|
+
const snapshotIds = new Set(collected.map(({ snapshot }) => snapshot.id));
|
|
911
|
+
for (const { snapshot, sessionFile, messages } of collected) {
|
|
727
912
|
signal.throwIfAborted();
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
913
|
+
if (this.sessionIdByNativeId.has(snapshot.id)) continue;
|
|
914
|
+
let child: ChildState | undefined;
|
|
915
|
+
try {
|
|
916
|
+
child = this.ensureChild(
|
|
917
|
+
{ ...snapshot, sessionFile },
|
|
918
|
+
this.resolveParent(snapshot.parentToolCallId, sessionFile),
|
|
919
|
+
);
|
|
920
|
+
if (!messages || this.isBufferedChildOmitted(snapshot.id)) {
|
|
921
|
+
this.failReplayChild(child);
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
this.projectReplay(child, messages, signal);
|
|
925
|
+
if (!sessionFile) {
|
|
926
|
+
this.failReplayChild(child);
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
visited.add(`${sessionFile}\0${snapshot.id}`);
|
|
930
|
+
await this.replayChildren(
|
|
931
|
+
child.sessionId,
|
|
932
|
+
sessionFile,
|
|
933
|
+
messages,
|
|
934
|
+
runtime,
|
|
935
|
+
visited,
|
|
936
|
+
budget,
|
|
937
|
+
signal,
|
|
938
|
+
1,
|
|
939
|
+
snapshotIds,
|
|
940
|
+
);
|
|
941
|
+
} catch (error) {
|
|
942
|
+
if (signal.aborted) throw error;
|
|
943
|
+
if (child) this.failReplayChild(child);
|
|
944
|
+
}
|
|
745
945
|
}
|
|
746
946
|
const activeToolCallIds = new Set(
|
|
747
947
|
snapshots.flatMap((snapshot) =>
|
|
@@ -764,46 +964,64 @@ export class OmpSubsessionProjector {
|
|
|
764
964
|
budget: ReplayBudget,
|
|
765
965
|
signal: AbortSignal,
|
|
766
966
|
depth: number,
|
|
967
|
+
snapshotIds?: ReadonlySet<string>,
|
|
767
968
|
): Promise<void> {
|
|
768
969
|
signal.throwIfAborted();
|
|
769
970
|
if (depth > MAX_REPLAY_DEPTH) throw new OmpPublicError("OMP subagent history is too deep");
|
|
770
971
|
this.indexTaskCalls(parentSessionId, messages);
|
|
771
972
|
for (const ref of replayChildren(messages)) {
|
|
772
973
|
signal.throwIfAborted();
|
|
773
|
-
if (!parentSessionFile)
|
|
774
|
-
throw new OmpPublicError("OMP parent transcript identity is unavailable");
|
|
775
|
-
}
|
|
974
|
+
if (!parentSessionFile || snapshotIds?.has(ref.id)) continue;
|
|
776
975
|
const visitKey = `${parentSessionFile}\0${ref.id}`;
|
|
777
976
|
if (visited.has(visitKey)) continue;
|
|
778
977
|
visited.add(visitKey);
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
978
|
+
let child: ChildState | undefined;
|
|
979
|
+
let history: ReplayHistory | undefined;
|
|
980
|
+
try {
|
|
981
|
+
const loaded = await waitForReplay(
|
|
982
|
+
runtime.readPersistedSubagentTranscript({
|
|
983
|
+
parentSessionFile,
|
|
984
|
+
childTranscriptId: ref.id,
|
|
985
|
+
cwd: this.cwd,
|
|
986
|
+
signal,
|
|
987
|
+
}),
|
|
784
988
|
signal,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
989
|
+
);
|
|
990
|
+
history = loaded;
|
|
991
|
+
child = this.ensureChild({ ...ref, sessionFile: loaded.sessionFile }, parentSessionId);
|
|
992
|
+
this.accountReplay(loaded.messages, budget, signal);
|
|
993
|
+
signal.throwIfAborted();
|
|
994
|
+
this.projectReplay(child, loaded.messages, signal);
|
|
995
|
+
await this.replayChildren(
|
|
996
|
+
child.sessionId,
|
|
997
|
+
history.sessionFile,
|
|
998
|
+
history.messages,
|
|
999
|
+
runtime,
|
|
1000
|
+
visited,
|
|
1001
|
+
budget,
|
|
1002
|
+
signal,
|
|
1003
|
+
depth + 1,
|
|
1004
|
+
snapshotIds,
|
|
1005
|
+
);
|
|
1006
|
+
signal.throwIfAborted();
|
|
1007
|
+
this.requestTerminal(
|
|
1008
|
+
child,
|
|
1009
|
+
ref.status === "derive" ? replayTerminalStatus(history.messages) : ref.status,
|
|
1010
|
+
);
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
if (signal.aborted) throw error;
|
|
1013
|
+
if (!child) {
|
|
1014
|
+
try {
|
|
1015
|
+
child = this.ensureChild(
|
|
1016
|
+
{ ...ref, sessionFile: history?.sessionFile },
|
|
1017
|
+
parentSessionId,
|
|
1018
|
+
);
|
|
1019
|
+
} catch {
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
this.failReplayChild(child);
|
|
1024
|
+
}
|
|
807
1025
|
}
|
|
808
1026
|
}
|
|
809
1027
|
|