@alexeiled/pi-fusion 0.5.1 → 0.5.2
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/src/orchestrator.ts +83 -30
- package/src/status.ts +31 -0
package/package.json
CHANGED
package/src/orchestrator.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
clearFusionUi,
|
|
22
22
|
extractFusionProgressCounts,
|
|
23
23
|
formatProgressCounts,
|
|
24
|
+
isTerminalFusionProgress,
|
|
24
25
|
publishFusionStatus,
|
|
25
26
|
type FusionProgressCounts,
|
|
26
27
|
type FusionUi,
|
|
@@ -165,7 +166,9 @@ export class FusionOrchestrator {
|
|
|
165
166
|
run = this.runStore.startRun({
|
|
166
167
|
prompt: args.prompt,
|
|
167
168
|
profileName: resolved.name,
|
|
168
|
-
...(args.operationId !== undefined
|
|
169
|
+
...(args.operationId !== undefined
|
|
170
|
+
? { operationId: args.operationId }
|
|
171
|
+
: {}),
|
|
169
172
|
phase: "panel",
|
|
170
173
|
});
|
|
171
174
|
} catch (error: unknown) {
|
|
@@ -188,6 +191,8 @@ export class FusionOrchestrator {
|
|
|
188
191
|
const spawnResult = await this.rpc.spawn(
|
|
189
192
|
buildPanelSpawnParams(resolved.profile, args.prompt),
|
|
190
193
|
);
|
|
194
|
+
const spawnError = extractSubagentFailure(spawnResult);
|
|
195
|
+
if (spawnError) throw new FusionArgsError(spawnError);
|
|
191
196
|
const panelRunId = extractSubagentRunId(spawnResult);
|
|
192
197
|
if (!panelRunId) {
|
|
193
198
|
throw new FusionArgsError(
|
|
@@ -517,13 +522,17 @@ export class FusionOrchestrator {
|
|
|
517
522
|
return { status: "ignored" };
|
|
518
523
|
}
|
|
519
524
|
|
|
520
|
-
const
|
|
521
|
-
snapshot.resultPayload ?? snapshot.statusPayload ?? payload
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
525
|
+
const lifecyclePayload =
|
|
526
|
+
snapshot.resultPayload ?? snapshot.statusPayload ?? payload;
|
|
527
|
+
const lifecycleError = extractSubagentFailure(lifecyclePayload);
|
|
528
|
+
if (!hasLifecycleResults(lifecyclePayload) && lifecycleError) {
|
|
529
|
+
return this.failActiveRun(lifecycleError);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const extracted = extractPanelResults(lifecyclePayload, {
|
|
533
|
+
panel: profile.panel,
|
|
534
|
+
limit: profile.panel.length,
|
|
535
|
+
});
|
|
527
536
|
if (!extracted.ok) {
|
|
528
537
|
return this.failActiveRun(
|
|
529
538
|
`${extracted.error.message} (${extracted.error.path})`,
|
|
@@ -628,16 +637,20 @@ export class FusionOrchestrator {
|
|
|
628
637
|
return { status: "ignored" };
|
|
629
638
|
}
|
|
630
639
|
|
|
631
|
-
const
|
|
632
|
-
snapshot.resultPayload ?? snapshot.statusPayload ?? payload
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
640
|
+
const lifecyclePayload =
|
|
641
|
+
snapshot.resultPayload ?? snapshot.statusPayload ?? payload;
|
|
642
|
+
const lifecycleError = extractSubagentFailure(lifecyclePayload);
|
|
643
|
+
if (!hasLifecycleResults(lifecyclePayload) && lifecycleError) {
|
|
644
|
+
return this.failActiveRun(lifecycleError);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const extracted = extractPanelResults(lifecyclePayload, {
|
|
648
|
+
panel: profile.panel,
|
|
649
|
+
limit: profile.panel.length,
|
|
650
|
+
...(active.panelStoppedIndices
|
|
651
|
+
? { stoppedPanelIndices: active.panelStoppedIndices }
|
|
652
|
+
: {}),
|
|
653
|
+
});
|
|
641
654
|
if (!extracted.ok) {
|
|
642
655
|
return this.failActiveRun(
|
|
643
656
|
`${extracted.error.message} (${extracted.error.path})`,
|
|
@@ -742,6 +755,8 @@ export class FusionOrchestrator {
|
|
|
742
755
|
|
|
743
756
|
try {
|
|
744
757
|
const spawnResult = await this.rpc.spawn(decision.params);
|
|
758
|
+
const spawnError = extractSubagentFailure(spawnResult);
|
|
759
|
+
if (spawnError) throw new FusionArgsError(spawnError);
|
|
745
760
|
const judgeRunId = extractSubagentRunId(spawnResult);
|
|
746
761
|
if (!judgeRunId) {
|
|
747
762
|
throw new FusionArgsError(decision.missingRunIdError);
|
|
@@ -789,9 +804,14 @@ export class FusionOrchestrator {
|
|
|
789
804
|
return { status: "ignored" };
|
|
790
805
|
}
|
|
791
806
|
|
|
792
|
-
const
|
|
793
|
-
snapshot.resultPayload ?? snapshot.statusPayload ?? payload
|
|
794
|
-
);
|
|
807
|
+
const lifecyclePayload =
|
|
808
|
+
snapshot.resultPayload ?? snapshot.statusPayload ?? payload;
|
|
809
|
+
const lifecycleError = extractSubagentFailure(lifecyclePayload);
|
|
810
|
+
if (!hasLifecycleResults(lifecyclePayload) && lifecycleError) {
|
|
811
|
+
return this.failActiveRun(lifecycleError);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const output = extractJudgeOutput(lifecyclePayload);
|
|
795
815
|
if (!output.ok) return this.failActiveRun(output.error);
|
|
796
816
|
|
|
797
817
|
const judgeModel = this.activeProfile
|
|
@@ -850,12 +870,18 @@ export class FusionOrchestrator {
|
|
|
850
870
|
);
|
|
851
871
|
}
|
|
852
872
|
|
|
873
|
+
const statusIsTerminal =
|
|
874
|
+
isTerminalSubagentState(extractSubagentState(statusPayload)) ||
|
|
875
|
+
isTerminalFusionProgress(statusPayload);
|
|
876
|
+
const eventIsTerminal =
|
|
877
|
+
isTerminalSubagentState(extractSubagentState(input.eventPayload)) ||
|
|
878
|
+
isTerminalFusionProgress(input.eventPayload);
|
|
853
879
|
const eventHasResults = hasResultsArray(input.eventPayload);
|
|
854
880
|
if (eventPayloadMatches && eventHasResults) {
|
|
855
881
|
return {
|
|
856
882
|
statusPayload,
|
|
857
883
|
resultPayload: input.eventPayload,
|
|
858
|
-
resultIsTerminal:
|
|
884
|
+
resultIsTerminal: eventIsTerminal || statusIsTerminal,
|
|
859
885
|
};
|
|
860
886
|
}
|
|
861
887
|
|
|
@@ -872,17 +898,14 @@ export class FusionOrchestrator {
|
|
|
872
898
|
}
|
|
873
899
|
|
|
874
900
|
if (hasResultsArray(statusPayload)) {
|
|
875
|
-
const resultIsTerminal =
|
|
876
|
-
eventPayloadMatches ||
|
|
877
|
-
isTerminalSubagentState(extractSubagentState(statusPayload));
|
|
878
901
|
return {
|
|
879
902
|
statusPayload,
|
|
880
903
|
resultPayload: statusPayload,
|
|
881
|
-
resultIsTerminal,
|
|
904
|
+
resultIsTerminal: statusIsTerminal,
|
|
882
905
|
};
|
|
883
906
|
}
|
|
884
907
|
|
|
885
|
-
if (
|
|
908
|
+
if (statusIsTerminal) {
|
|
886
909
|
return {
|
|
887
910
|
statusPayload,
|
|
888
911
|
resultPayload: statusPayload,
|
|
@@ -894,9 +917,7 @@ export class FusionOrchestrator {
|
|
|
894
917
|
return {
|
|
895
918
|
statusPayload,
|
|
896
919
|
resultPayload: input.eventPayload,
|
|
897
|
-
resultIsTerminal:
|
|
898
|
-
extractSubagentState(input.eventPayload),
|
|
899
|
-
),
|
|
920
|
+
resultIsTerminal: eventIsTerminal,
|
|
900
921
|
};
|
|
901
922
|
}
|
|
902
923
|
|
|
@@ -1526,6 +1547,38 @@ function isTerminalSubagentState(state: string | undefined): boolean {
|
|
|
1526
1547
|
);
|
|
1527
1548
|
}
|
|
1528
1549
|
|
|
1550
|
+
function hasLifecycleResults(payload: unknown): boolean {
|
|
1551
|
+
return hasResultsArray(payload) || findStepsArray(payload).length > 0;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
function extractSubagentFailure(payload: unknown): string | undefined {
|
|
1555
|
+
if (!isRecord(payload)) return undefined;
|
|
1556
|
+
const direct = firstNonBlankString(payload.error, payload.errorMessage);
|
|
1557
|
+
if (direct) return direct;
|
|
1558
|
+
if (isRecord(payload.details)) {
|
|
1559
|
+
const detailsError = firstNonBlankString(
|
|
1560
|
+
payload.details.error,
|
|
1561
|
+
payload.details.errorMessage,
|
|
1562
|
+
);
|
|
1563
|
+
if (detailsError) return detailsError;
|
|
1564
|
+
}
|
|
1565
|
+
if (payload.isError === true && Array.isArray(payload.content)) {
|
|
1566
|
+
for (const item of payload.content) {
|
|
1567
|
+
const contentText = isRecord(item)
|
|
1568
|
+
? firstNonBlankString(item.text)
|
|
1569
|
+
: undefined;
|
|
1570
|
+
if (contentText) return contentText;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
const text = firstNonBlankString(
|
|
1574
|
+
payload.text,
|
|
1575
|
+
isRecord(payload.details) ? payload.details.text : undefined,
|
|
1576
|
+
);
|
|
1577
|
+
if (text && /^error(?:\s|:)/i.test(text)) return text;
|
|
1578
|
+
if (isRecord(payload.data)) return extractSubagentFailure(payload.data);
|
|
1579
|
+
return undefined;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1529
1582
|
function firstNonBlankStringFromPayload(payload: unknown): string | undefined {
|
|
1530
1583
|
if (!isRecord(payload)) return undefined;
|
|
1531
1584
|
const direct = firstNonBlankString(
|
package/src/status.ts
CHANGED
|
@@ -92,6 +92,20 @@ export function formatProgressCounts(progress: FusionProgressCounts): string {
|
|
|
92
92
|
return `${progress.completed}/${total} done, ${progress.running} running, ${progress.failed} failed`;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export function isTerminalFusionProgress(payload: unknown): boolean {
|
|
96
|
+
const progressPayload = findTerminalProgressPayload(payload);
|
|
97
|
+
const progress = progressPayload
|
|
98
|
+
? extractFusionProgressCounts(progressPayload)
|
|
99
|
+
: undefined;
|
|
100
|
+
return Boolean(
|
|
101
|
+
progress &&
|
|
102
|
+
progress.total !== undefined &&
|
|
103
|
+
progress.total > 0 &&
|
|
104
|
+
progress.pending === 0 &&
|
|
105
|
+
progress.running === 0,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
95
109
|
type ProgressStatus = "pending" | "running" | "completed" | "failed";
|
|
96
110
|
|
|
97
111
|
function findProgressContainer(
|
|
@@ -116,6 +130,23 @@ function findProgressContainer(
|
|
|
116
130
|
return undefined;
|
|
117
131
|
}
|
|
118
132
|
|
|
133
|
+
function findTerminalProgressPayload(payload: unknown): unknown {
|
|
134
|
+
if (!isRecord(payload)) return undefined;
|
|
135
|
+
if (Array.isArray(payload.progress)) return { progress: payload.progress };
|
|
136
|
+
if (Array.isArray(payload.steps)) return { steps: payload.steps };
|
|
137
|
+
if (isRecord(payload.details)) {
|
|
138
|
+
if (Array.isArray(payload.details.progress)) {
|
|
139
|
+
return { progress: payload.details.progress };
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(payload.details.steps)) {
|
|
142
|
+
return { steps: payload.details.steps };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return isRecord(payload.data)
|
|
146
|
+
? findTerminalProgressPayload(payload.data)
|
|
147
|
+
: undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
119
150
|
function classifyProgressItem(value: unknown): ProgressStatus {
|
|
120
151
|
if (!isRecord(value)) return "failed";
|
|
121
152
|
if (value.success === true) return "completed";
|