@alexeiled/pi-fusion 0.1.1 → 0.2.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.
package/src/status.ts CHANGED
@@ -23,12 +23,16 @@ export function publishFusionStatus(
23
23
  ctx: FusionUiContext | undefined,
24
24
  run: Pick<
25
25
  FusionRun,
26
- "id" | "phase" | "profileName" | "panelRunId" | "judgeRunId"
26
+ "id" | "phase" | "profileName" | "chainRunId" | "panelRunId" | "judgeRunId"
27
27
  >,
28
28
  progress?: FusionProgressCounts,
29
+ phaseLabel?: string,
29
30
  ): void {
30
31
  if (!ctx?.hasUI) return;
31
- ctx.ui.setStatus(FUSION_STATUS_KEY, formatFusionStatusText(run, progress));
32
+ ctx.ui.setStatus(
33
+ FUSION_STATUS_KEY,
34
+ formatFusionStatusText(run, progress, phaseLabel),
35
+ );
32
36
  }
33
37
 
34
38
  export function clearFusionUi(ctx: FusionUiContext | undefined): void {
@@ -37,20 +41,30 @@ export function clearFusionUi(ctx: FusionUiContext | undefined): void {
37
41
  }
38
42
 
39
43
  export function formatFusionStatusText(
40
- run: Pick<FusionRun, "phase" | "profileName" | "panelRunId" | "judgeRunId">,
44
+ run: Pick<
45
+ FusionRun,
46
+ "phase" | "profileName" | "chainRunId" | "panelRunId" | "judgeRunId"
47
+ >,
41
48
  progress?: FusionProgressCounts,
49
+ phaseLabel?: string,
42
50
  ): string {
43
- const activeRunId = run.phase === "judge" ? run.judgeRunId : run.panelRunId;
44
- const progressText = progress ? ` ${formatProgressCounts(progress)}` : "";
45
- const runText = activeRunId ? ` ${activeRunId}` : " starting";
46
- return `fusion: ${run.phase} ${run.profileName}${runText}${progressText}`;
51
+ const activeRunId =
52
+ run.phase === "judge" ? run.judgeRunId : (run.chainRunId ?? run.panelRunId);
53
+ const phase = phaseLabel ?? run.phase;
54
+ if (progress) {
55
+ return `fusion: panel · ${formatProgressCounts(progress)} · ${run.profileName}`;
56
+ }
57
+ if (activeRunId) {
58
+ return `fusion: ${phase} · ${run.profileName} · ${activeRunId}`;
59
+ }
60
+ return `fusion: ${phase} · starting · ${run.profileName}`;
47
61
  }
48
62
 
49
63
  export function extractFusionProgressCounts(
50
64
  payload: unknown,
51
65
  ): FusionProgressCounts | undefined {
52
66
  const container = findProgressContainer(payload);
53
- if (!container) return undefined;
67
+ if (!container || container.length === 0) return undefined;
54
68
 
55
69
  const counts: FusionProgressCounts = {
56
70
  total: container.length,
@@ -84,15 +98,19 @@ function findProgressContainer(
84
98
  payload: unknown,
85
99
  ): readonly unknown[] | undefined {
86
100
  if (!isRecord(payload)) return undefined;
87
- const progress = unknownArray(payload.progress);
101
+ const progress = nonEmptyArray(payload.progress);
88
102
  if (progress) return progress;
89
- const results = unknownArray(payload.results);
103
+ const results = nonEmptyArray(payload.results);
90
104
  if (results) return results;
105
+ const steps = nonEmptyArray(payload.steps);
106
+ if (steps) return steps;
91
107
  if (isRecord(payload.details)) {
92
- const detailsProgress = unknownArray(payload.details.progress);
108
+ const detailsProgress = nonEmptyArray(payload.details.progress);
93
109
  if (detailsProgress) return detailsProgress;
94
- const detailsResults = unknownArray(payload.details.results);
110
+ const detailsResults = nonEmptyArray(payload.details.results);
95
111
  if (detailsResults) return detailsResults;
112
+ const detailsSteps = nonEmptyArray(payload.details.steps);
113
+ if (detailsSteps) return detailsSteps;
96
114
  }
97
115
  if (isRecord(payload.data)) return findProgressContainer(payload.data);
98
116
  return undefined;
@@ -127,8 +145,10 @@ function firstString(...values: readonly unknown[]): string | undefined {
127
145
  return undefined;
128
146
  }
129
147
 
130
- function unknownArray(value: unknown): readonly unknown[] | undefined {
131
- return Array.isArray(value) ? (value as readonly unknown[]) : undefined;
148
+ function nonEmptyArray(value: unknown): readonly unknown[] | undefined {
149
+ return Array.isArray(value) && value.length > 0
150
+ ? (value as readonly unknown[])
151
+ : undefined;
132
152
  }
133
153
 
134
154
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -0,0 +1,40 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ const STATUS_FILE = "status.json";
5
+ const ASYNC_RESULTS_DIR = "async-subagent-results";
6
+ const ASYNC_RUNS_DIR = "async-subagent-runs";
7
+
8
+ export function deriveSubagentResultPath(
9
+ asyncDir: string,
10
+ runId: string,
11
+ ): string | undefined {
12
+ const runsDir = dirname(asyncDir);
13
+ if (!runsDir.endsWith(ASYNC_RUNS_DIR)) return undefined;
14
+ return join(dirname(runsDir), ASYNC_RESULTS_DIR, `${runId}.json`);
15
+ }
16
+
17
+ export function readSubagentStatusArtifact(
18
+ asyncDir: string | undefined,
19
+ ): unknown {
20
+ if (!asyncDir) return undefined;
21
+ return readJsonArtifact(join(asyncDir, STATUS_FILE));
22
+ }
23
+
24
+ export function readSubagentResultArtifact(input: {
25
+ runId?: string;
26
+ asyncDir?: string;
27
+ }): unknown {
28
+ if (!input.runId || !input.asyncDir) return undefined;
29
+ const resultPath = deriveSubagentResultPath(input.asyncDir, input.runId);
30
+ return resultPath ? readJsonArtifact(resultPath) : undefined;
31
+ }
32
+
33
+ function readJsonArtifact(path: string): unknown {
34
+ if (!existsSync(path)) return undefined;
35
+ try {
36
+ return JSON.parse(readFileSync(path, "utf8")) as unknown;
37
+ } catch {
38
+ return undefined;
39
+ }
40
+ }
package/src/types.ts CHANGED
@@ -38,7 +38,32 @@ export interface FusionConfig {
38
38
  profiles: Record<string, FusionProfile>;
39
39
  }
40
40
 
41
- export type FusionPhase = "panel" | "judge" | "done" | "failed" | "cancelled";
41
+ export interface PanelOutput {
42
+ index: number;
43
+ agent: string;
44
+ output: string;
45
+ id?: string;
46
+ label?: string;
47
+ role?: string;
48
+ model?: string;
49
+ artifactPath?: string;
50
+ sessionPath?: string;
51
+ }
52
+
53
+ export interface FailedPanelSummary {
54
+ index: number;
55
+ agent: string;
56
+ summary: string;
57
+ id?: string;
58
+ label?: string;
59
+ role?: string;
60
+ model?: string;
61
+ artifactPath?: string;
62
+ sessionPath?: string;
63
+ }
64
+
65
+ export type FusionPhase =
66
+ "panel" | "chain" | "judge" | "done" | "failed" | "cancelled";
42
67
 
43
68
  export interface FusionRun {
44
69
  id: string;
@@ -47,8 +72,13 @@ export interface FusionRun {
47
72
  phase: FusionPhase;
48
73
  createdAt: number;
49
74
  updatedAt: number;
75
+ chainRunId?: string;
76
+ chainAsyncDir?: string;
50
77
  panelRunId?: string;
51
78
  judgeRunId?: string;
79
+ judgeAsyncDir?: string;
80
+ panelOutputs?: PanelOutput[];
81
+ panelFailures?: FailedPanelSummary[];
52
82
  report?: string;
53
83
  error?: string;
54
84
  }