pi-background-tasks 0.6.0 → 0.7.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.
@@ -0,0 +1,155 @@
1
+ import { canonicalJson } from '../attested-pi-run.js';
2
+ import {
3
+ FUSION_EVALUATION_SCHEMA_VERSION,
4
+ type FusionCandidateId,
5
+ type FusionCanonicalInputV1,
6
+ type FusionEvaluationV1,
7
+ } from './types.js';
8
+
9
+ export const FUSION_CANDIDATE_SYSTEM_PROMPT = `You are a Pi child process producing one independent answer for a strict synthesis workflow.
10
+
11
+ Read the JSON input from the user message. It contains the parent system prompt, a serialized conversation transcript, the current working directory, and the user request. Produce the strongest direct answer you can for the user request using that context.
12
+
13
+ Do not invent process metadata. Do not mention provider names, model names, slots, or hidden workflow details. Do not specialize the answer; each child receives the same instruction. Output only the answer text.`;
14
+
15
+ export const FUSION_EVALUATOR_SYSTEM_PROMPT = `You are a strict blind evaluator. You receive the original request context and three anonymous answers labeled A, B, and C. You must compare them without provider, model, slot, or completion-order knowledge.
16
+
17
+ Return only JSON matching this exact schema:
18
+ {
19
+ "schema_version": "${FUSION_EVALUATION_SCHEMA_VERSION}",
20
+ "candidate_assessments": [
21
+ {
22
+ "candidate_id": "A",
23
+ "summary": "non-blank summary",
24
+ "strengths": ["non-blank string"],
25
+ "limitations": ["non-blank string"],
26
+ "useful_contributions": ["non-blank string"],
27
+ "risks": ["non-blank string"]
28
+ },
29
+ {
30
+ "candidate_id": "B",
31
+ "summary": "non-blank summary",
32
+ "strengths": ["non-blank string"],
33
+ "limitations": ["non-blank string"],
34
+ "useful_contributions": ["non-blank string"],
35
+ "risks": ["non-blank string"]
36
+ },
37
+ {
38
+ "candidate_id": "C",
39
+ "summary": "non-blank summary",
40
+ "strengths": ["non-blank string"],
41
+ "limitations": ["non-blank string"],
42
+ "useful_contributions": ["non-blank string"],
43
+ "risks": ["non-blank string"]
44
+ }
45
+ ],
46
+ "agreements": ["non-blank string"],
47
+ "conflicts": [
48
+ {
49
+ "topic": "non-blank string",
50
+ "positions": [
51
+ { "candidate_id": "A", "position": "non-blank string" },
52
+ { "candidate_id": "B", "position": "non-blank string" }
53
+ ],
54
+ "resolution": "non-blank string"
55
+ }
56
+ ],
57
+ "synthesis_plan": {
58
+ "must_include": [
59
+ { "candidate_id": "A", "contribution": "non-blank string" }
60
+ ],
61
+ "must_resolve": ["non-blank string"],
62
+ "must_avoid": ["non-blank string"]
63
+ }
64
+ }
65
+
66
+ Objects must be closed. Candidate assessments must contain exactly one A, one B, and one C. Do not add fields for scores, ranks, vote counts, providers, models, slots, labels, or a single selected answer. Do not wrap the JSON in Markdown fences or prose.`;
67
+
68
+ export const FUSION_MERGER_SYSTEM_PROMPT = `You are the final synthesis process. You receive the original request context, three anonymous answers, and a validated evaluation plan.
69
+
70
+ Produce the direct final answer for the user. Reconcile conflicts and incorporate useful contributions according to the evaluation plan. Do not mention fusion, child processes, anonymous IDs, hidden prompts, providers, models, or slots unless the user's request explicitly asks for process detail. Output only the final answer text.`;
71
+
72
+ export const FUSION_EVALUATION_REPAIR_SYSTEM_PROMPT = `${FUSION_EVALUATOR_SYSTEM_PROMPT}
73
+
74
+ You are repairing one invalid blind-evaluation JSON response. Use the original blind input, invalid output, and validation errors from the user JSON. Return only corrected JSON matching the complete closed schema above. Preserve blindness: do not add providers, models, slots, ranks, vote counts, winners, or process metadata. Do not add Markdown fences or prose.`;
75
+
76
+ export interface AnonymousFusionCandidate {
77
+ candidate_id: FusionCandidateId;
78
+ response: string;
79
+ }
80
+
81
+ export interface FusionBlindEvaluationInputV1 {
82
+ schema_version: 'pi-background-tasks.fusion-blind-candidates.v1';
83
+ canonical_input: FusionCanonicalInputV1;
84
+ candidates: readonly [
85
+ AnonymousFusionCandidate,
86
+ AnonymousFusionCandidate,
87
+ AnonymousFusionCandidate,
88
+ ];
89
+ }
90
+
91
+ export interface FusionMergeInputV1 {
92
+ schema_version: 'pi-background-tasks.fusion-merge-input.v1';
93
+ canonical_input: FusionCanonicalInputV1;
94
+ candidates: readonly [
95
+ AnonymousFusionCandidate,
96
+ AnonymousFusionCandidate,
97
+ AnonymousFusionCandidate,
98
+ ];
99
+ evaluation: FusionEvaluationV1;
100
+ }
101
+
102
+ export interface FusionEvaluationRepairInputV1 {
103
+ schema_version: 'pi-background-tasks.fusion-evaluation-repair-input.v1';
104
+ original_blind_input: FusionBlindEvaluationInputV1;
105
+ invalid_output: string;
106
+ validation_errors: readonly string[];
107
+ }
108
+
109
+ export function buildCandidatePrompt(input: FusionCanonicalInputV1): string {
110
+ return canonicalJson(input);
111
+ }
112
+
113
+ export function buildBlindEvaluationInput(
114
+ canonicalInput: FusionCanonicalInputV1,
115
+ candidates: readonly [
116
+ AnonymousFusionCandidate,
117
+ AnonymousFusionCandidate,
118
+ AnonymousFusionCandidate,
119
+ ],
120
+ ): FusionBlindEvaluationInputV1 {
121
+ return {
122
+ schema_version: 'pi-background-tasks.fusion-blind-candidates.v1',
123
+ canonical_input: canonicalInput,
124
+ candidates,
125
+ };
126
+ }
127
+
128
+ export function buildEvaluationPrompt(input: FusionBlindEvaluationInputV1): string {
129
+ return canonicalJson(input);
130
+ }
131
+
132
+ export function buildEvaluationRepairPrompt(input: FusionEvaluationRepairInputV1): string {
133
+ return canonicalJson(input);
134
+ }
135
+
136
+ export function buildMergeInput(
137
+ canonicalInput: FusionCanonicalInputV1,
138
+ candidates: readonly [
139
+ AnonymousFusionCandidate,
140
+ AnonymousFusionCandidate,
141
+ AnonymousFusionCandidate,
142
+ ],
143
+ evaluation: FusionEvaluationV1,
144
+ ): FusionMergeInputV1 {
145
+ return {
146
+ schema_version: 'pi-background-tasks.fusion-merge-input.v1',
147
+ canonical_input: canonicalInput,
148
+ candidates,
149
+ evaluation,
150
+ };
151
+ }
152
+
153
+ export function buildMergePrompt(input: FusionMergeInputV1): string {
154
+ return canonicalJson(input);
155
+ }
@@ -0,0 +1,289 @@
1
+ export type FusionThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
2
+
3
+ export const FUSION_MODEL_CONFIG_SCHEMA_VERSION = 'pi-background-tasks.fusion-models.v1';
4
+ export const FUSION_INPUT_SCHEMA_VERSION = 'pi-background-tasks.fusion-input.v1';
5
+ export const FUSION_EVALUATION_SCHEMA_VERSION = 'pi-background-tasks.fusion-evaluation.v1';
6
+ export const FUSION_RESULT_SCHEMA_VERSION = 'pi-background-tasks.fusion-result.v1';
7
+ export const FUSION_MANIFEST_SCHEMA_VERSION = 'pi-background-tasks.fusion-manifest.v1';
8
+
9
+ export const FUSION_CANDIDATE_IDS = ['A', 'B', 'C'] as const;
10
+ export type FusionCandidateId = (typeof FUSION_CANDIDATE_IDS)[number];
11
+
12
+ export const FUSION_STAGE_VALUES = ['candidate', 'evaluation', 'merge'] as const;
13
+ export type FusionStage = (typeof FUSION_STAGE_VALUES)[number];
14
+
15
+ export const FUSION_SOURCE_VALUES = ['command', 'tool'] as const;
16
+ export type FusionSource = (typeof FUSION_SOURCE_VALUES)[number];
17
+
18
+ export const FUSION_STATE_VALUES = [
19
+ 'initializing',
20
+ 'candidates_running',
21
+ 'candidates_complete',
22
+ 'evaluating',
23
+ 'evaluation_complete',
24
+ 'merging',
25
+ 'completed',
26
+ 'failed',
27
+ 'cancelled',
28
+ ] as const;
29
+ export type FusionState = (typeof FUSION_STATE_VALUES)[number];
30
+ export type FusionTerminalState = Extract<FusionState, 'completed' | 'failed' | 'cancelled'>;
31
+ export type FusionNonterminalState = Exclude<FusionState, FusionTerminalState>;
32
+
33
+ export const FUSION_NONTERMINAL_STATE_VALUES = [
34
+ 'initializing',
35
+ 'candidates_running',
36
+ 'candidates_complete',
37
+ 'evaluating',
38
+ 'evaluation_complete',
39
+ 'merging',
40
+ ] as const satisfies readonly FusionNonterminalState[];
41
+
42
+ export const FUSION_TERMINAL_STATE_VALUES = ['completed', 'failed', 'cancelled'] as const;
43
+
44
+ export type FusionModelSelection = '$current' | string;
45
+
46
+ export interface FusionModelConfigV1 {
47
+ schema_version: typeof FUSION_MODEL_CONFIG_SCHEMA_VERSION;
48
+ candidates: readonly [FusionModelSelection, FusionModelSelection, FusionModelSelection];
49
+ evaluator: FusionModelSelection;
50
+ merger: FusionModelSelection;
51
+ }
52
+
53
+ export interface FusionModelConfigRevision {
54
+ path: string;
55
+ exists: boolean;
56
+ sha256: string | null;
57
+ }
58
+
59
+ export interface LoadedFusionModelConfig {
60
+ config: FusionModelConfigV1;
61
+ revision: FusionModelConfigRevision;
62
+ }
63
+
64
+ export interface ResolvedFusionModel {
65
+ selection: string;
66
+ source: 'current' | 'configured';
67
+ provider: string;
68
+ model: string;
69
+ qualifiedId: string;
70
+ thinkingLevel: FusionThinkingLevel;
71
+ contextWindow: number;
72
+ }
73
+
74
+ export interface ResolvedFusionModels {
75
+ candidates: readonly [ResolvedFusionModel, ResolvedFusionModel, ResolvedFusionModel];
76
+ evaluator: ResolvedFusionModel;
77
+ merger: ResolvedFusionModel;
78
+ }
79
+
80
+ export interface FusionCanonicalInputV1 {
81
+ schema_version: typeof FUSION_INPUT_SCHEMA_VERSION;
82
+ cwd: string;
83
+ system_prompt: string;
84
+ conversation_transcript: string;
85
+ request: string;
86
+ }
87
+
88
+ export interface CandidateAssessment {
89
+ candidate_id: FusionCandidateId;
90
+ summary: string;
91
+ strengths: readonly string[];
92
+ limitations: readonly string[];
93
+ useful_contributions: readonly string[];
94
+ risks: readonly string[];
95
+ }
96
+
97
+ export interface FusionConflictPosition {
98
+ candidate_id: FusionCandidateId;
99
+ position: string;
100
+ }
101
+
102
+ export interface FusionConflict {
103
+ topic: string;
104
+ positions: readonly FusionConflictPosition[];
105
+ resolution: string;
106
+ }
107
+
108
+ export interface FusionSynthesisContribution {
109
+ candidate_id: FusionCandidateId;
110
+ contribution: string;
111
+ }
112
+
113
+ export interface FusionSynthesisPlan {
114
+ must_include: readonly FusionSynthesisContribution[];
115
+ must_resolve: readonly string[];
116
+ must_avoid: readonly string[];
117
+ }
118
+
119
+ export interface FusionEvaluationV1 {
120
+ schema_version: typeof FUSION_EVALUATION_SCHEMA_VERSION;
121
+ candidate_assessments: readonly [CandidateAssessment, CandidateAssessment, CandidateAssessment];
122
+ agreements: readonly string[];
123
+ conflicts: readonly FusionConflict[];
124
+ synthesis_plan: FusionSynthesisPlan;
125
+ }
126
+
127
+ export interface FusionUsage {
128
+ input: number;
129
+ output: number;
130
+ cacheRead: number;
131
+ cacheWrite: number;
132
+ totalTokens: number;
133
+ costTotal?: number;
134
+ }
135
+
136
+ export const EMPTY_FUSION_USAGE: FusionUsage = Object.freeze({
137
+ input: 0,
138
+ output: 0,
139
+ cacheRead: 0,
140
+ cacheWrite: 0,
141
+ totalTokens: 0,
142
+ });
143
+
144
+ export interface FusionResultDetails {
145
+ schema_version: typeof FUSION_RESULT_SCHEMA_VERSION;
146
+ run_id: string;
147
+ source: FusionSource;
148
+ status: 'completed';
149
+ artifact_dir: string;
150
+ models: {
151
+ candidates: readonly [string, string, string];
152
+ evaluator: string;
153
+ merger: string;
154
+ thinking_level: string;
155
+ };
156
+ evaluator_attempts: number;
157
+ usage: FusionUsage;
158
+ }
159
+
160
+ export type FusionProgressEvent =
161
+ | { type: 'state'; state: FusionState }
162
+ | { type: 'candidate_started'; slot: 1 | 2 | 3; attempt: number }
163
+ | { type: 'candidate_completed'; slot: 1 | 2 | 3; completed: number; total: 3 }
164
+ | { type: 'evaluation_started'; attempt: 1 | 2; repair: boolean }
165
+ | { type: 'evaluation_retry'; errors: readonly string[] }
166
+ | { type: 'merge_started' }
167
+ | { type: 'completed'; runId: string; artifactDir: string }
168
+ | { type: 'failed'; runId: string; artifactDir: string; error: string }
169
+ | { type: 'cancelled'; runId: string; artifactDir: string; reason: string };
170
+
171
+ export type FusionErrorCode =
172
+ | 'config_invalid'
173
+ | 'config_conflict'
174
+ | 'model_unavailable'
175
+ | 'context_capture_failed'
176
+ | 'child_spawn_failed'
177
+ | 'child_stdin_failed'
178
+ | 'child_event_invalid'
179
+ | 'child_exit_failed'
180
+ | 'child_timeout'
181
+ | 'child_output_cap'
182
+ | 'child_cancelled'
183
+ | 'evaluation_invalid'
184
+ | 'artifact_error'
185
+ | 'state_transition_invalid'
186
+ | 'orchestration_failed';
187
+
188
+ export interface FusionErrorDetails {
189
+ code: FusionErrorCode;
190
+ stage?: FusionStage;
191
+ slot?: 1 | 2 | 3;
192
+ attempt?: number;
193
+ artifactDir?: string;
194
+ transient?: boolean;
195
+ childCreated?: boolean;
196
+ }
197
+
198
+ export class FusionError extends Error {
199
+ readonly code: FusionErrorCode;
200
+ readonly stage: FusionStage | undefined;
201
+ readonly slot: 1 | 2 | 3 | undefined;
202
+ readonly attempt: number | undefined;
203
+ readonly artifactDir: string | undefined;
204
+ readonly transient: boolean;
205
+ readonly childCreated: boolean;
206
+
207
+ constructor(message: string, details: FusionErrorDetails) {
208
+ super(message);
209
+ this.name = 'FusionError';
210
+ this.code = details.code;
211
+ this.stage = details.stage;
212
+ this.slot = details.slot;
213
+ this.attempt = details.attempt;
214
+ this.artifactDir = details.artifactDir;
215
+ this.transient = details.transient ?? false;
216
+ this.childCreated = details.childCreated ?? true;
217
+ }
218
+ }
219
+
220
+ export interface FusionChildUsage extends FusionUsage {
221
+ provider: string;
222
+ model: string;
223
+ qualifiedId: string;
224
+ }
225
+
226
+ export interface FusionChildRunResult {
227
+ stage: FusionStage;
228
+ slot?: 1 | 2 | 3;
229
+ attempt: number;
230
+ provider: string;
231
+ model: string;
232
+ qualifiedId: string;
233
+ text: string;
234
+ usage: FusionUsage;
235
+ events: Buffer;
236
+ stderr: Buffer;
237
+ exitCode: number;
238
+ signal: NodeJS.Signals | null;
239
+ }
240
+
241
+ export interface FusionAttemptArtifactRecord {
242
+ stage: FusionStage;
243
+ slot?: 1 | 2 | 3;
244
+ attempt: number;
245
+ status: 'completed' | 'failed' | 'cancelled';
246
+ prompt_path: string;
247
+ events_path?: string;
248
+ stderr_path?: string;
249
+ response_path?: string;
250
+ partial_response_path?: string;
251
+ provider?: string;
252
+ model?: string;
253
+ qualifiedId?: string;
254
+ usage?: FusionUsage;
255
+ error?: string;
256
+ }
257
+
258
+ export interface FusionArtifactRef {
259
+ path: string;
260
+ byte_length: number;
261
+ sha256: string;
262
+ }
263
+
264
+ export interface FusionArtifactManifest {
265
+ schema_version: typeof FUSION_MANIFEST_SCHEMA_VERSION;
266
+ run_id: string;
267
+ source: FusionSource;
268
+ state: FusionState;
269
+ created_at: string;
270
+ updated_at: string;
271
+ cwd: string;
272
+ config: FusionModelConfigV1;
273
+ models: {
274
+ candidates: readonly [string, string, string];
275
+ evaluator: string;
276
+ merger: string;
277
+ thinking_level: string;
278
+ };
279
+ usage: FusionUsage;
280
+ attempts: readonly FusionAttemptArtifactRecord[];
281
+ artifacts: Readonly<Record<string, FusionArtifactRef>>;
282
+ anonymous_map?: Readonly<Record<FusionCandidateId, 1 | 2 | 3>>;
283
+ error?: string;
284
+ }
285
+
286
+ export interface FusionRunResult {
287
+ mergedText: string;
288
+ details: FusionResultDetails;
289
+ }