@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/README.md +68 -31
- package/docs/user-guide.md +10 -2
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/orchestrator.ts +801 -89
- package/src/report.ts +21 -3
- package/src/result-extract.ts +21 -2
- package/src/run-builder.ts +148 -28
- package/src/run-store.ts +121 -1
- package/src/status.ts +34 -14
- package/src/subagent-artifacts.ts +40 -0
- package/src/types.ts +31 -1
package/src/report.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { FusionRun } from "./types.js";
|
|
|
3
3
|
|
|
4
4
|
type ReportRun = Pick<
|
|
5
5
|
FusionRun,
|
|
6
|
-
"id" | "prompt" | "profileName" | "panelRunId" | "judgeRunId"
|
|
6
|
+
"id" | "prompt" | "profileName" | "chainRunId" | "panelRunId" | "judgeRunId"
|
|
7
7
|
> &
|
|
8
8
|
Partial<Pick<FusionRun, "phase" | "createdAt" | "updatedAt">>;
|
|
9
9
|
|
|
@@ -11,12 +11,14 @@ export interface RenderPanelFailureReportInput {
|
|
|
11
11
|
run: ReportRun;
|
|
12
12
|
failures: readonly FailedPanelSummary[];
|
|
13
13
|
error?: string;
|
|
14
|
+
judgeModel?: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export interface RenderSinglePanelReportInput {
|
|
17
18
|
run: ReportRun;
|
|
18
19
|
output: PanelOutput;
|
|
19
20
|
failures: readonly FailedPanelSummary[];
|
|
21
|
+
judgeModel?: string;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export interface RenderJudgeReportInput {
|
|
@@ -24,6 +26,7 @@ export interface RenderJudgeReportInput {
|
|
|
24
26
|
judgeOutput: string;
|
|
25
27
|
panelOutputs?: readonly PanelOutput[];
|
|
26
28
|
failures?: readonly FailedPanelSummary[];
|
|
29
|
+
judgeModel?: string;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export interface RenderFailureReportInput {
|
|
@@ -31,6 +34,7 @@ export interface RenderFailureReportInput {
|
|
|
31
34
|
error: string;
|
|
32
35
|
panelOutputs?: readonly PanelOutput[];
|
|
33
36
|
failures?: readonly FailedPanelSummary[];
|
|
37
|
+
judgeModel?: string;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export interface RenderCancelledReportInput {
|
|
@@ -39,6 +43,7 @@ export interface RenderCancelledReportInput {
|
|
|
39
43
|
targetRunId?: string;
|
|
40
44
|
panelOutputs?: readonly PanelOutput[];
|
|
41
45
|
failures?: readonly FailedPanelSummary[];
|
|
46
|
+
judgeModel?: string;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
type ReportSectionTitle =
|
|
@@ -62,6 +67,7 @@ interface AgentStatusOptions {
|
|
|
62
67
|
panelOutputs?: readonly PanelOutput[];
|
|
63
68
|
failures?: readonly FailedPanelSummary[];
|
|
64
69
|
judgeStatus: string;
|
|
70
|
+
judgeModel?: string;
|
|
65
71
|
extra?: readonly string[];
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -80,6 +86,7 @@ export function renderPanelFailureReport(
|
|
|
80
86
|
panelOutputs: [],
|
|
81
87
|
failures: input.failures,
|
|
82
88
|
judgeStatus: "not run - no successful panelists",
|
|
89
|
+
...(input.judgeModel ? { judgeModel: input.judgeModel } : {}),
|
|
83
90
|
}),
|
|
84
91
|
},
|
|
85
92
|
{
|
|
@@ -132,6 +139,7 @@ export function renderSinglePanelReport(
|
|
|
132
139
|
panelOutputs: [input.output],
|
|
133
140
|
failures: input.failures,
|
|
134
141
|
judgeStatus: "skipped - one successful panelist",
|
|
142
|
+
...(input.judgeModel ? { judgeModel: input.judgeModel } : {}),
|
|
135
143
|
}),
|
|
136
144
|
},
|
|
137
145
|
{
|
|
@@ -193,6 +201,7 @@ export function renderJudgeReport(input: RenderJudgeReportInput): string {
|
|
|
193
201
|
panelOutputs,
|
|
194
202
|
failures,
|
|
195
203
|
judgeStatus: "succeeded",
|
|
204
|
+
...(input.judgeModel ? { judgeModel: input.judgeModel } : {}),
|
|
196
205
|
}),
|
|
197
206
|
},
|
|
198
207
|
{
|
|
@@ -244,6 +253,7 @@ export function renderFailureReport(input: RenderFailureReportInput): string {
|
|
|
244
253
|
: {}),
|
|
245
254
|
...(input.failures !== undefined ? { failures: input.failures } : {}),
|
|
246
255
|
judgeStatus: `failed - ${firstLine(input.error)}`,
|
|
256
|
+
...(input.judgeModel ? { judgeModel: input.judgeModel } : {}),
|
|
247
257
|
extra: [`- Phase: ${phase}`],
|
|
248
258
|
}),
|
|
249
259
|
},
|
|
@@ -291,6 +301,7 @@ export function renderCancelledReport(
|
|
|
291
301
|
: {}),
|
|
292
302
|
...(input.failures !== undefined ? { failures: input.failures } : {}),
|
|
293
303
|
judgeStatus: "cancelled or not completed",
|
|
304
|
+
...(input.judgeModel ? { judgeModel: input.judgeModel } : {}),
|
|
294
305
|
extra: [
|
|
295
306
|
`- Phase: ${input.run.phase ?? "unknown"}`,
|
|
296
307
|
`- Cancellation method: ${input.method}`,
|
|
@@ -371,15 +382,21 @@ function formatAgentStatus(options: AgentStatusOptions): string[] {
|
|
|
371
382
|
}
|
|
372
383
|
|
|
373
384
|
lines.push(`- Judge: ${options.judgeStatus}`);
|
|
385
|
+
if (options.judgeModel) lines.push(` Model: ${options.judgeModel}`);
|
|
374
386
|
if (options.extra) lines.push(...options.extra);
|
|
375
387
|
return lines;
|
|
376
388
|
}
|
|
377
389
|
|
|
378
390
|
function formatPanelDetails(
|
|
379
|
-
item: Pick<
|
|
391
|
+
item: Pick<
|
|
392
|
+
PanelOutput,
|
|
393
|
+
"agent" | "role" | "model" | "artifactPath" | "sessionPath"
|
|
394
|
+
>,
|
|
380
395
|
): string[] {
|
|
381
396
|
return [
|
|
382
397
|
` Agent: ${item.agent}`,
|
|
398
|
+
...(item.role ? [` Role: ${item.role}`] : []),
|
|
399
|
+
...(item.model ? [` Model: ${item.model}`] : []),
|
|
383
400
|
...(item.artifactPath ? [` Artifact: ${item.artifactPath}`] : []),
|
|
384
401
|
...(item.sessionPath ? [` Session: ${item.sessionPath}`] : []),
|
|
385
402
|
];
|
|
@@ -391,8 +408,9 @@ function formatRunMetadata(run: ReportRun): string[] {
|
|
|
391
408
|
`- Profile: ${run.profileName}`,
|
|
392
409
|
...(run.phase ? [`- Phase: ${run.phase}`] : []),
|
|
393
410
|
`- Prompt: ${firstLine(run.prompt)}`,
|
|
411
|
+
...(run.chainRunId ? [`- Chain run: ${run.chainRunId}`] : []),
|
|
394
412
|
...(run.panelRunId ? [`- Panel run: ${run.panelRunId}`] : []),
|
|
395
|
-
...(run.judgeRunId ? [`-
|
|
413
|
+
...(run.judgeRunId ? [`- Fallback judge run: ${run.judgeRunId}`] : []),
|
|
396
414
|
...(typeof run.createdAt === "number"
|
|
397
415
|
? [`- Created: ${formatTimestamp(run.createdAt)}`]
|
|
398
416
|
: []),
|
package/src/result-extract.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
appendThinkingSuffix,
|
|
3
|
+
type FailedPanelSummary,
|
|
4
|
+
type PanelOutput,
|
|
5
|
+
} from "./run-builder.js";
|
|
2
6
|
import type { PanelMemberConfig } from "./types.js";
|
|
3
7
|
|
|
4
8
|
export type ResultExtractErrorCode =
|
|
@@ -12,6 +16,7 @@ export interface ResultExtractError {
|
|
|
12
16
|
|
|
13
17
|
export interface ExtractPanelResultsOptions {
|
|
14
18
|
panel?: readonly PanelMemberConfig[];
|
|
19
|
+
limit?: number;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export type ExtractPanelResultsResult =
|
|
@@ -40,7 +45,11 @@ export function extractPanelResults(
|
|
|
40
45
|
|
|
41
46
|
const outputs: PanelOutput[] = [];
|
|
42
47
|
const failures: FailedPanelSummary[] = [];
|
|
43
|
-
|
|
48
|
+
const results =
|
|
49
|
+
options.limit === undefined
|
|
50
|
+
? container.results
|
|
51
|
+
: container.results.slice(0, options.limit);
|
|
52
|
+
for (const [index, rawResult] of results.entries()) {
|
|
44
53
|
const child = normalizeChildResult(rawResult, index, options);
|
|
45
54
|
if (!child.ok) return child;
|
|
46
55
|
if (child.status === "success") outputs.push(child.output);
|
|
@@ -232,12 +241,17 @@ function buildPanelOutput(input: {
|
|
|
232
241
|
artifactPath: string | undefined;
|
|
233
242
|
sessionPath: string | undefined;
|
|
234
243
|
}): PanelOutput {
|
|
244
|
+
const model = input.member
|
|
245
|
+
? appendThinkingSuffix(input.member.model, input.member.thinking)
|
|
246
|
+
: undefined;
|
|
235
247
|
return {
|
|
236
248
|
index: input.index,
|
|
237
249
|
agent: input.agent,
|
|
238
250
|
output: input.output,
|
|
239
251
|
...(input.member?.id ? { id: input.member.id } : {}),
|
|
240
252
|
...(input.member?.label ? { label: input.member.label } : {}),
|
|
253
|
+
...(input.member?.role ? { role: input.member.role } : {}),
|
|
254
|
+
...(model ? { model } : {}),
|
|
241
255
|
...(input.artifactPath ? { artifactPath: input.artifactPath } : {}),
|
|
242
256
|
...(input.sessionPath ? { sessionPath: input.sessionPath } : {}),
|
|
243
257
|
};
|
|
@@ -251,12 +265,17 @@ function buildFailedPanelSummary(input: {
|
|
|
251
265
|
artifactPath: string | undefined;
|
|
252
266
|
sessionPath: string | undefined;
|
|
253
267
|
}): FailedPanelSummary {
|
|
268
|
+
const model = input.member
|
|
269
|
+
? appendThinkingSuffix(input.member.model, input.member.thinking)
|
|
270
|
+
: undefined;
|
|
254
271
|
return {
|
|
255
272
|
index: input.index,
|
|
256
273
|
agent: input.agent,
|
|
257
274
|
summary: input.summary,
|
|
258
275
|
...(input.member?.id ? { id: input.member.id } : {}),
|
|
259
276
|
...(input.member?.label ? { label: input.member.label } : {}),
|
|
277
|
+
...(input.member?.role ? { role: input.member.role } : {}),
|
|
278
|
+
...(model ? { model } : {}),
|
|
260
279
|
...(input.artifactPath ? { artifactPath: input.artifactPath } : {}),
|
|
261
280
|
...(input.sessionPath ? { sessionPath: input.sessionPath } : {}),
|
|
262
281
|
};
|
package/src/run-builder.ts
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
THINKING_LEVELS,
|
|
3
|
+
type FailedPanelSummary,
|
|
3
4
|
type FusionProfile,
|
|
4
5
|
type PanelMemberConfig,
|
|
6
|
+
type PanelOutput,
|
|
5
7
|
type ThinkingLevel,
|
|
6
8
|
} from "./types.js";
|
|
7
9
|
|
|
10
|
+
export const FUSION_ACCEPTANCE_DISABLED = {
|
|
11
|
+
level: "none",
|
|
12
|
+
reason:
|
|
13
|
+
"pi-fusion panelists and judge are read-only advisory tasks; pi-fusion owns final synthesis and acceptance.",
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export type FusionAcceptanceDisabled = typeof FUSION_ACCEPTANCE_DISABLED;
|
|
17
|
+
|
|
8
18
|
export interface PanelSubagentTaskParams {
|
|
9
19
|
agent: string;
|
|
10
20
|
task: string;
|
|
@@ -12,10 +22,16 @@ export interface PanelSubagentTaskParams {
|
|
|
12
22
|
outputMode: "inline";
|
|
13
23
|
progress: true;
|
|
14
24
|
skill: false;
|
|
15
|
-
acceptance:
|
|
25
|
+
acceptance: FusionAcceptanceDisabled;
|
|
16
26
|
model?: string;
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
export interface PanelChainTaskParams extends PanelSubagentTaskParams {
|
|
30
|
+
as: string;
|
|
31
|
+
label: string;
|
|
32
|
+
phase: "Panel";
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
export interface PanelSpawnParams {
|
|
20
36
|
tasks: PanelSubagentTaskParams[];
|
|
21
37
|
async: true;
|
|
@@ -24,43 +40,55 @@ export interface PanelSpawnParams {
|
|
|
24
40
|
context: "fresh" | "fork";
|
|
25
41
|
output: true;
|
|
26
42
|
outputMode: "inline";
|
|
27
|
-
acceptance:
|
|
43
|
+
acceptance: FusionAcceptanceDisabled;
|
|
28
44
|
timeoutMs?: number;
|
|
29
45
|
}
|
|
30
46
|
|
|
31
|
-
export interface
|
|
47
|
+
export interface FusionChainParallelStepParams {
|
|
48
|
+
parallel: PanelChainTaskParams[];
|
|
49
|
+
concurrency: number;
|
|
50
|
+
failFast: false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FusionChainJudgeStepParams {
|
|
32
54
|
agent: string;
|
|
33
55
|
task: string;
|
|
56
|
+
label: "Judge";
|
|
57
|
+
phase: "Judge";
|
|
58
|
+
output: true;
|
|
59
|
+
outputMode: "inline";
|
|
60
|
+
skill: false;
|
|
61
|
+
acceptance: FusionAcceptanceDisabled;
|
|
62
|
+
model?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface FusionChainSpawnParams {
|
|
66
|
+
chain: [FusionChainParallelStepParams, FusionChainJudgeStepParams];
|
|
67
|
+
task: string;
|
|
34
68
|
async: true;
|
|
35
69
|
clarify: false;
|
|
36
70
|
context: "fresh" | "fork";
|
|
37
71
|
output: true;
|
|
38
72
|
outputMode: "inline";
|
|
39
|
-
|
|
40
|
-
acceptance: "none";
|
|
41
|
-
model?: string;
|
|
73
|
+
acceptance: FusionAcceptanceDisabled;
|
|
42
74
|
timeoutMs?: number;
|
|
43
75
|
}
|
|
44
76
|
|
|
45
|
-
export interface
|
|
46
|
-
index: number;
|
|
77
|
+
export interface JudgeSpawnParams {
|
|
47
78
|
agent: string;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
task: string;
|
|
80
|
+
async: true;
|
|
81
|
+
clarify: false;
|
|
82
|
+
context: "fresh" | "fork";
|
|
83
|
+
output: true;
|
|
84
|
+
outputMode: "inline";
|
|
85
|
+
skill: false;
|
|
86
|
+
acceptance: FusionAcceptanceDisabled;
|
|
87
|
+
model?: string;
|
|
88
|
+
timeoutMs?: number;
|
|
53
89
|
}
|
|
54
90
|
|
|
55
|
-
export
|
|
56
|
-
index: number;
|
|
57
|
-
agent: string;
|
|
58
|
-
summary: string;
|
|
59
|
-
id?: string;
|
|
60
|
-
label?: string;
|
|
61
|
-
artifactPath?: string;
|
|
62
|
-
sessionPath?: string;
|
|
63
|
-
}
|
|
91
|
+
export type { FailedPanelSummary, PanelOutput } from "./types.js";
|
|
64
92
|
|
|
65
93
|
export interface BuildJudgeSpawnParamsInput {
|
|
66
94
|
profile: FusionProfile;
|
|
@@ -112,7 +140,49 @@ export function buildPanelSpawnParams(
|
|
|
112
140
|
context: profile.context ?? "fresh",
|
|
113
141
|
output: true,
|
|
114
142
|
outputMode: "inline",
|
|
115
|
-
acceptance:
|
|
143
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
144
|
+
...(profile.timeoutMs !== undefined
|
|
145
|
+
? { timeoutMs: profile.timeoutMs }
|
|
146
|
+
: {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function buildFusionChainSpawnParams(
|
|
151
|
+
profile: FusionProfile,
|
|
152
|
+
prompt: string,
|
|
153
|
+
): FusionChainSpawnParams {
|
|
154
|
+
const model = appendThinkingSuffix(
|
|
155
|
+
profile.judge.model,
|
|
156
|
+
profile.judge.thinking,
|
|
157
|
+
);
|
|
158
|
+
return {
|
|
159
|
+
chain: [
|
|
160
|
+
{
|
|
161
|
+
parallel: profile.panel.map((member, index) =>
|
|
162
|
+
buildPanelChainTaskParams(member, index),
|
|
163
|
+
),
|
|
164
|
+
concurrency: profile.concurrency ?? profile.panel.length,
|
|
165
|
+
failFast: false,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
agent: profile.judge.agent,
|
|
169
|
+
task: buildChainJudgeTask(profile.panel),
|
|
170
|
+
label: "Judge",
|
|
171
|
+
phase: "Judge",
|
|
172
|
+
output: true,
|
|
173
|
+
outputMode: "inline",
|
|
174
|
+
skill: false,
|
|
175
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
176
|
+
...(model ? { model } : {}),
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
task: prompt.trim(),
|
|
180
|
+
async: true,
|
|
181
|
+
clarify: false,
|
|
182
|
+
context: profile.context ?? "fresh",
|
|
183
|
+
output: true,
|
|
184
|
+
outputMode: "inline",
|
|
185
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
116
186
|
...(profile.timeoutMs !== undefined
|
|
117
187
|
? { timeoutMs: profile.timeoutMs }
|
|
118
188
|
: {}),
|
|
@@ -135,7 +205,7 @@ export function buildJudgeSpawnParams(
|
|
|
135
205
|
output: true,
|
|
136
206
|
outputMode: "inline",
|
|
137
207
|
skill: false,
|
|
138
|
-
acceptance:
|
|
208
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
139
209
|
...(model ? { model } : {}),
|
|
140
210
|
...(input.profile.timeoutMs !== undefined
|
|
141
211
|
? { timeoutMs: input.profile.timeoutMs }
|
|
@@ -155,7 +225,27 @@ function buildPanelTaskParams(
|
|
|
155
225
|
outputMode: "inline",
|
|
156
226
|
progress: true,
|
|
157
227
|
skill: false,
|
|
158
|
-
acceptance:
|
|
228
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
229
|
+
...(model ? { model } : {}),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function buildPanelChainTaskParams(
|
|
234
|
+
member: PanelMemberConfig,
|
|
235
|
+
index: number,
|
|
236
|
+
): PanelChainTaskParams {
|
|
237
|
+
const model = appendThinkingSuffix(member.model, member.thinking);
|
|
238
|
+
return {
|
|
239
|
+
agent: member.agent,
|
|
240
|
+
task: buildPanelTask(member, "{task}"),
|
|
241
|
+
as: chainOutputName(member, index),
|
|
242
|
+
label: member.label,
|
|
243
|
+
phase: "Panel",
|
|
244
|
+
output: true,
|
|
245
|
+
outputMode: "inline",
|
|
246
|
+
progress: true,
|
|
247
|
+
skill: false,
|
|
248
|
+
acceptance: FUSION_ACCEPTANCE_DISABLED,
|
|
159
249
|
...(model ? { model } : {}),
|
|
160
250
|
};
|
|
161
251
|
}
|
|
@@ -171,10 +261,10 @@ function buildPanelTask(member: PanelMemberConfig, prompt: string): string {
|
|
|
171
261
|
"",
|
|
172
262
|
"Instructions:",
|
|
173
263
|
"- Work independently from the other panelists.",
|
|
174
|
-
"-
|
|
264
|
+
"- Read-only: inspect only; leave files, git state, and the workspace untouched.",
|
|
175
265
|
"- Do not ask other agents.",
|
|
176
266
|
"- Do not run subagents.",
|
|
177
|
-
"- Use
|
|
267
|
+
"- Use local inspection only when code evidence is needed.",
|
|
178
268
|
"- Be concise and cite evidence when you inspect files.",
|
|
179
269
|
"",
|
|
180
270
|
"Output contract:",
|
|
@@ -187,7 +277,7 @@ function buildJudgeTask(input: BuildJudgeSpawnParamsInput): string {
|
|
|
187
277
|
const sortedFailures = [...input.failedPanelists].sort(comparePanelItems);
|
|
188
278
|
return [
|
|
189
279
|
"You are the fusion judge.",
|
|
190
|
-
"
|
|
280
|
+
"Read-only synthesis only. Leave files, git state, and the workspace untouched. Do not ask other agents. Do not run subagents.",
|
|
191
281
|
"Synthesize the panel results. Preserve disagreement instead of forcing consensus.",
|
|
192
282
|
"",
|
|
193
283
|
"Original task:",
|
|
@@ -207,6 +297,30 @@ function buildJudgeTask(input: BuildJudgeSpawnParamsInput): string {
|
|
|
207
297
|
].join("\n");
|
|
208
298
|
}
|
|
209
299
|
|
|
300
|
+
function buildChainJudgeTask(panel: readonly PanelMemberConfig[]): string {
|
|
301
|
+
return [
|
|
302
|
+
"You are the fusion judge.",
|
|
303
|
+
"Read-only synthesis only. Leave files, git state, and the workspace untouched. Do not ask other agents. Do not run subagents.",
|
|
304
|
+
"Synthesize the panel results. Preserve disagreement instead of forcing consensus.",
|
|
305
|
+
"",
|
|
306
|
+
"Original task:",
|
|
307
|
+
"{task}",
|
|
308
|
+
"",
|
|
309
|
+
"All listed panelists completed successfully.",
|
|
310
|
+
"",
|
|
311
|
+
"Panel outputs:",
|
|
312
|
+
...panel.flatMap((member, index) => [
|
|
313
|
+
`## ${member.label} (${member.id})`,
|
|
314
|
+
`Agent: ${member.agent}`,
|
|
315
|
+
"",
|
|
316
|
+
`{outputs.${chainOutputName(member, index)}}`,
|
|
317
|
+
"",
|
|
318
|
+
]),
|
|
319
|
+
"Output contract:",
|
|
320
|
+
...JUDGE_OUTPUT_CONTRACT,
|
|
321
|
+
].join("\n");
|
|
322
|
+
}
|
|
323
|
+
|
|
210
324
|
function formatPanelStatus(
|
|
211
325
|
outputs: readonly PanelOutput[],
|
|
212
326
|
failures: readonly FailedPanelSummary[],
|
|
@@ -267,6 +381,12 @@ function firstLine(value: string): string {
|
|
|
267
381
|
return value.split(/\r?\n/, 1)[0]?.trim() || "unknown failure";
|
|
268
382
|
}
|
|
269
383
|
|
|
384
|
+
function chainOutputName(member: PanelMemberConfig, index: number): string {
|
|
385
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(member.id)
|
|
386
|
+
? member.id
|
|
387
|
+
: `panel_${index + 1}`;
|
|
388
|
+
}
|
|
389
|
+
|
|
270
390
|
function hasThinkingSuffix(model: string): boolean {
|
|
271
391
|
const colonIndex = model.lastIndexOf(":");
|
|
272
392
|
if (colonIndex === -1) return false;
|
package/src/run-store.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type FusionRunSummary = Omit<
|
|
|
17
17
|
| "phase"
|
|
18
18
|
| "createdAt"
|
|
19
19
|
| "updatedAt"
|
|
20
|
+
| "chainRunId"
|
|
20
21
|
| "panelRunId"
|
|
21
22
|
| "judgeRunId"
|
|
22
23
|
| "report"
|
|
@@ -29,19 +30,26 @@ export interface FusionRunStartInput {
|
|
|
29
30
|
id?: string;
|
|
30
31
|
prompt: string;
|
|
31
32
|
profileName: string;
|
|
33
|
+
phase?: Exclude<FusionPhase, FusionTerminalPhase>;
|
|
32
34
|
createdAt?: number;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export interface FusionRunPatch {
|
|
36
38
|
phase?: Exclude<FusionPhase, FusionTerminalPhase>;
|
|
39
|
+
chainRunId?: string;
|
|
40
|
+
chainAsyncDir?: string;
|
|
37
41
|
panelRunId?: string;
|
|
38
42
|
judgeRunId?: string;
|
|
43
|
+
judgeAsyncDir?: string;
|
|
44
|
+
panelOutputs?: FusionRun["panelOutputs"];
|
|
45
|
+
panelFailures?: FusionRun["panelFailures"];
|
|
39
46
|
report?: string;
|
|
40
47
|
error?: string;
|
|
41
48
|
updatedAt?: number;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
export interface FusionRunTransitionPatch {
|
|
52
|
+
chainRunId?: string;
|
|
45
53
|
panelRunId?: string;
|
|
46
54
|
judgeRunId?: string;
|
|
47
55
|
report?: string;
|
|
@@ -106,7 +114,7 @@ export class FusionRunStore {
|
|
|
106
114
|
id: input.id ?? this.idFactory(),
|
|
107
115
|
prompt: input.prompt,
|
|
108
116
|
profileName: input.profileName,
|
|
109
|
-
phase: "
|
|
117
|
+
phase: input.phase ?? "chain",
|
|
110
118
|
createdAt,
|
|
111
119
|
updatedAt: createdAt,
|
|
112
120
|
};
|
|
@@ -246,8 +254,21 @@ function applyPatch(
|
|
|
246
254
|
const updated = cloneRun(run);
|
|
247
255
|
updated.updatedAt = updatedAt;
|
|
248
256
|
if (patch.phase !== undefined) updated.phase = patch.phase;
|
|
257
|
+
if (patch.chainRunId !== undefined) updated.chainRunId = patch.chainRunId;
|
|
258
|
+
if (patch.chainAsyncDir !== undefined) {
|
|
259
|
+
updated.chainAsyncDir = patch.chainAsyncDir;
|
|
260
|
+
}
|
|
249
261
|
if (patch.panelRunId !== undefined) updated.panelRunId = patch.panelRunId;
|
|
250
262
|
if (patch.judgeRunId !== undefined) updated.judgeRunId = patch.judgeRunId;
|
|
263
|
+
if (patch.judgeAsyncDir !== undefined) {
|
|
264
|
+
updated.judgeAsyncDir = patch.judgeAsyncDir;
|
|
265
|
+
}
|
|
266
|
+
if (patch.panelOutputs !== undefined) {
|
|
267
|
+
updated.panelOutputs = clonePanelOutputs(patch.panelOutputs);
|
|
268
|
+
}
|
|
269
|
+
if (patch.panelFailures !== undefined) {
|
|
270
|
+
updated.panelFailures = clonePanelFailures(patch.panelFailures);
|
|
271
|
+
}
|
|
251
272
|
if (patch.report !== undefined) updated.report = patch.report;
|
|
252
273
|
if (patch.error !== undefined) updated.error = patch.error;
|
|
253
274
|
return updated;
|
|
@@ -264,6 +285,7 @@ function applyTransitionPatch(
|
|
|
264
285
|
phase,
|
|
265
286
|
};
|
|
266
287
|
updated.updatedAt = updatedAt;
|
|
288
|
+
if (patch.chainRunId !== undefined) updated.chainRunId = patch.chainRunId;
|
|
267
289
|
if (patch.panelRunId !== undefined) updated.panelRunId = patch.panelRunId;
|
|
268
290
|
if (patch.judgeRunId !== undefined) updated.judgeRunId = patch.judgeRunId;
|
|
269
291
|
if (patch.report !== undefined) updated.report = patch.report;
|
|
@@ -281,6 +303,7 @@ function toRunSummary(
|
|
|
281
303
|
phase: run.phase,
|
|
282
304
|
createdAt: run.createdAt,
|
|
283
305
|
updatedAt: run.updatedAt,
|
|
306
|
+
...(run.chainRunId !== undefined ? { chainRunId: run.chainRunId } : {}),
|
|
284
307
|
...(run.panelRunId !== undefined ? { panelRunId: run.panelRunId } : {}),
|
|
285
308
|
...(run.judgeRunId !== undefined ? { judgeRunId: run.judgeRunId } : {}),
|
|
286
309
|
...(run.report !== undefined ? { report: run.report } : {}),
|
|
@@ -296,8 +319,21 @@ function cloneRun(run: FusionRun): FusionRun {
|
|
|
296
319
|
phase: run.phase,
|
|
297
320
|
createdAt: run.createdAt,
|
|
298
321
|
updatedAt: run.updatedAt,
|
|
322
|
+
...(run.chainRunId !== undefined ? { chainRunId: run.chainRunId } : {}),
|
|
323
|
+
...(run.chainAsyncDir !== undefined
|
|
324
|
+
? { chainAsyncDir: run.chainAsyncDir }
|
|
325
|
+
: {}),
|
|
299
326
|
...(run.panelRunId !== undefined ? { panelRunId: run.panelRunId } : {}),
|
|
300
327
|
...(run.judgeRunId !== undefined ? { judgeRunId: run.judgeRunId } : {}),
|
|
328
|
+
...(run.judgeAsyncDir !== undefined
|
|
329
|
+
? { judgeAsyncDir: run.judgeAsyncDir }
|
|
330
|
+
: {}),
|
|
331
|
+
...(run.panelOutputs !== undefined
|
|
332
|
+
? { panelOutputs: clonePanelOutputs(run.panelOutputs) }
|
|
333
|
+
: {}),
|
|
334
|
+
...(run.panelFailures !== undefined
|
|
335
|
+
? { panelFailures: clonePanelFailures(run.panelFailures) }
|
|
336
|
+
: {}),
|
|
301
337
|
...(run.report !== undefined ? { report: run.report } : {}),
|
|
302
338
|
...(run.error !== undefined ? { error: run.error } : {}),
|
|
303
339
|
};
|
|
@@ -326,12 +362,39 @@ function isFusionRunState(value: unknown): value is FusionRun {
|
|
|
326
362
|
if (!isFusionPhase(value.phase)) return false;
|
|
327
363
|
if (!isFiniteNumber(value.createdAt)) return false;
|
|
328
364
|
if (!isFiniteNumber(value.updatedAt)) return false;
|
|
365
|
+
if (value.chainRunId !== undefined && typeof value.chainRunId !== "string") {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (
|
|
369
|
+
value.chainAsyncDir !== undefined &&
|
|
370
|
+
typeof value.chainAsyncDir !== "string"
|
|
371
|
+
) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
329
374
|
if (value.panelRunId !== undefined && typeof value.panelRunId !== "string") {
|
|
330
375
|
return false;
|
|
331
376
|
}
|
|
332
377
|
if (value.judgeRunId !== undefined && typeof value.judgeRunId !== "string") {
|
|
333
378
|
return false;
|
|
334
379
|
}
|
|
380
|
+
if (
|
|
381
|
+
value.judgeAsyncDir !== undefined &&
|
|
382
|
+
typeof value.judgeAsyncDir !== "string"
|
|
383
|
+
) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (
|
|
387
|
+
value.panelOutputs !== undefined &&
|
|
388
|
+
!isPanelOutputArray(value.panelOutputs)
|
|
389
|
+
) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
if (
|
|
393
|
+
value.panelFailures !== undefined &&
|
|
394
|
+
!isPanelFailureArray(value.panelFailures)
|
|
395
|
+
) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
335
398
|
if (value.report !== undefined && typeof value.report !== "string") {
|
|
336
399
|
return false;
|
|
337
400
|
}
|
|
@@ -348,6 +411,7 @@ function isFusionRunSummary(value: unknown): value is FusionRunSummary {
|
|
|
348
411
|
function isFusionPhase(value: unknown): value is FusionPhase {
|
|
349
412
|
return (
|
|
350
413
|
value === "panel" ||
|
|
414
|
+
value === "chain" ||
|
|
351
415
|
value === "judge" ||
|
|
352
416
|
value === "done" ||
|
|
353
417
|
value === "failed" ||
|
|
@@ -370,3 +434,59 @@ function isNonEmptyString(value: unknown): value is string {
|
|
|
370
434
|
function isFiniteNumber(value: unknown): value is number {
|
|
371
435
|
return typeof value === "number" && Number.isFinite(value);
|
|
372
436
|
}
|
|
437
|
+
|
|
438
|
+
function isPanelOutputArray(
|
|
439
|
+
value: unknown,
|
|
440
|
+
): value is NonNullable<FusionRun["panelOutputs"]> {
|
|
441
|
+
return Array.isArray(value) && value.every(isPanelOutput);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function isPanelOutput(
|
|
445
|
+
value: unknown,
|
|
446
|
+
): value is NonNullable<FusionRun["panelOutputs"]>[number] {
|
|
447
|
+
return (
|
|
448
|
+
isRecord(value) &&
|
|
449
|
+
isFiniteNumber(value.index) &&
|
|
450
|
+
isNonEmptyString(value.agent) &&
|
|
451
|
+
typeof value.output === "string" &&
|
|
452
|
+
(value.id === undefined || typeof value.id === "string") &&
|
|
453
|
+
(value.label === undefined || typeof value.label === "string") &&
|
|
454
|
+
(value.artifactPath === undefined ||
|
|
455
|
+
typeof value.artifactPath === "string") &&
|
|
456
|
+
(value.sessionPath === undefined || typeof value.sessionPath === "string")
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function isPanelFailureArray(
|
|
461
|
+
value: unknown,
|
|
462
|
+
): value is NonNullable<FusionRun["panelFailures"]> {
|
|
463
|
+
return Array.isArray(value) && value.every(isPanelFailure);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function isPanelFailure(
|
|
467
|
+
value: unknown,
|
|
468
|
+
): value is NonNullable<FusionRun["panelFailures"]>[number] {
|
|
469
|
+
return (
|
|
470
|
+
isRecord(value) &&
|
|
471
|
+
isFiniteNumber(value.index) &&
|
|
472
|
+
isNonEmptyString(value.agent) &&
|
|
473
|
+
typeof value.summary === "string" &&
|
|
474
|
+
(value.id === undefined || typeof value.id === "string") &&
|
|
475
|
+
(value.label === undefined || typeof value.label === "string") &&
|
|
476
|
+
(value.artifactPath === undefined ||
|
|
477
|
+
typeof value.artifactPath === "string") &&
|
|
478
|
+
(value.sessionPath === undefined || typeof value.sessionPath === "string")
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function clonePanelOutputs(
|
|
483
|
+
outputs: NonNullable<FusionRun["panelOutputs"]>,
|
|
484
|
+
): NonNullable<FusionRun["panelOutputs"]> {
|
|
485
|
+
return outputs.map((output) => ({ ...output }));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function clonePanelFailures(
|
|
489
|
+
failures: NonNullable<FusionRun["panelFailures"]>,
|
|
490
|
+
): NonNullable<FusionRun["panelFailures"]> {
|
|
491
|
+
return failures.map((failure) => ({ ...failure }));
|
|
492
|
+
}
|