@maestrofrontier/frontier 1.4.4 → 1.5.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/.agents/plugins/marketplace.json +21 -0
- package/.codex-plugin/plugin.json +29 -0
- package/.cursorrules +197 -194
- package/AGENTS.md +214 -214
- package/CLAUDE.md +29 -29
- package/README.md +368 -278
- package/bin/maestro.cjs +75 -75
- package/commands/compress.md +36 -36
- package/commands/frontier.md +124 -124
- package/commands/terse.md +23 -23
- package/docs/codex.md +167 -98
- package/docs/orchestration.md +168 -168
- package/frontier/cli.cjs +279 -248
- package/frontier/config.cjs +468 -441
- package/frontier/dispatch.cjs +267 -255
- package/frontier/judge.cjs +92 -92
- package/frontier/run.cjs +201 -148
- package/frontier/schema.cjs +112 -112
- package/frontier/semaphore.cjs +49 -49
- package/frontier/synthesize.cjs +79 -79
- package/hooks/frontier-autorun.cjs +127 -124
- package/hooks/hooks.json +103 -103
- package/hooks/maestro-doctrine-guard.cjs +81 -81
- package/hooks/maestro-gate-reminder.cjs +22 -7
- package/hooks/maestro-gate-telemetry.cjs +79 -77
- package/hooks/maestro-phase-scope.cjs +118 -118
- package/hooks/maestro-statusline-sync.cjs +152 -152
- package/hooks/maestro-subagent-guard.cjs +148 -148
- package/hooks/maestro-terse-mode.cjs +189 -189
- package/hooks/maestro-toolbudget-advisory.cjs +127 -127
- package/integrations/README.md +111 -94
- package/integrations/cline/skills/frontier/SKILL.md +75 -75
- package/integrations/codex/prompts/frontier.md +70 -66
- package/integrations/codex/prompts/update.md +39 -36
- package/integrations/codex/skills/maestro-frontier/SKILL.md +122 -0
- package/integrations/codex/skills/{settings → maestro-settings}/SKILL.md +55 -46
- package/integrations/codex/skills/{terse → maestro-terse}/SKILL.md +58 -49
- package/integrations/codex/skills/maestro-update/SKILL.md +31 -0
- package/integrations/cursor/commands/frontier.md +63 -63
- package/integrations/cursor/commands/update.md +34 -34
- package/integrations/gemini/commands/frontier.toml +76 -76
- package/integrations/windsurf/workflows/frontier.md +70 -70
- package/package.json +58 -55
- package/scripts/install.cjs +1014 -605
- package/settings/cli.cjs +140 -140
- package/settings/config.cjs +309 -309
- package/skills/maestro-frontier/SKILL.md +122 -0
- package/skills/maestro-settings/SKILL.md +55 -0
- package/skills/maestro-terse/SKILL.md +58 -0
- package/skills/maestro-update/SKILL.md +31 -0
- package/skills/terse/SKILL.md +74 -0
- package/integrations/codex/skills/frontier/SKILL.md +0 -91
- package/integrations/codex/skills/update/SKILL.md +0 -29
package/frontier/schema.cjs
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Maestro Frontier — shared types-as-validators + helpers.
|
|
3
|
-
// Zero deps, CJS. Ported stripLlmWrapper from scripts/compress.cjs.
|
|
4
|
-
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {{ model:string, content:string, ok:boolean, durationMs:number, tokensEst:number, toolCalls?:unknown[], error?:string }} PanelResponse
|
|
9
|
-
* @typedef {{ model:string, reason:string }} FailedModel
|
|
10
|
-
* @typedef {{ consensus:string[], contradictions:{topic:string,stances:{model:string,stance:string}[]}[], partial_coverage:{models:string[],point:string}[], unique_insights:{model:string,insight:string}[], blind_spots:string[] }} Analysis
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** @type {string[]} */
|
|
14
|
-
const FAILURE_REASONS = [
|
|
15
|
-
'all_panels_failed',
|
|
16
|
-
'insufficient_credits',
|
|
17
|
-
'rate_limited',
|
|
18
|
-
'fusion_invocation_capped',
|
|
19
|
-
'unexpected_error',
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @param {unknown} x
|
|
24
|
-
* @returns {x is PanelResponse}
|
|
25
|
-
*/
|
|
26
|
-
function isPanelResponse(x) {
|
|
27
|
-
if (x === null || typeof x !== 'object') return false;
|
|
28
|
-
const o = /** @type {Record<string,unknown>} */ (x);
|
|
29
|
-
return (
|
|
30
|
-
typeof o.model === 'string' &&
|
|
31
|
-
typeof o.content === 'string' &&
|
|
32
|
-
typeof o.ok === 'boolean' &&
|
|
33
|
-
typeof o.durationMs === 'number' &&
|
|
34
|
-
typeof o.tokensEst === 'number'
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @param {unknown} x
|
|
40
|
-
* @returns {x is Analysis}
|
|
41
|
-
*/
|
|
42
|
-
function isAnalysis(x) {
|
|
43
|
-
if (x === null || typeof x !== 'object') return false;
|
|
44
|
-
const o = /** @type {Record<string,unknown>} */ (x);
|
|
45
|
-
if (!Array.isArray(o.consensus)) return false;
|
|
46
|
-
if (!o.consensus.every(s => typeof s === 'string')) return false;
|
|
47
|
-
if (!Array.isArray(o.blind_spots)) return false;
|
|
48
|
-
if (!o.blind_spots.every(s => typeof s === 'string')) return false;
|
|
49
|
-
if (!Array.isArray(o.contradictions)) return false;
|
|
50
|
-
if (!o.contradictions.every(e => e !== null && typeof e === 'object')) return false;
|
|
51
|
-
if (!Array.isArray(o.partial_coverage)) return false;
|
|
52
|
-
if (!o.partial_coverage.every(e => e !== null && typeof e === 'object')) return false;
|
|
53
|
-
if (!Array.isArray(o.unique_insights)) return false;
|
|
54
|
-
if (!o.unique_insights.every(e => e !== null && typeof e === 'object')) return false;
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @param {string} str
|
|
60
|
-
* @returns {Analysis}
|
|
61
|
-
* @throws {Error}
|
|
62
|
-
*/
|
|
63
|
-
function parseAnalysis(str) {
|
|
64
|
-
let parsed;
|
|
65
|
-
try {
|
|
66
|
-
parsed = JSON.parse(str);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
throw new Error('parseAnalysis: invalid JSON — ' + e.message);
|
|
69
|
-
}
|
|
70
|
-
if (!isAnalysis(parsed)) {
|
|
71
|
-
throw new Error('parseAnalysis: object does not satisfy Analysis shape');
|
|
72
|
-
}
|
|
73
|
-
return /** @type {Analysis} */ (parsed);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @param {PanelResponse} panelResponse
|
|
78
|
-
* @returns {FailedModel}
|
|
79
|
-
*/
|
|
80
|
-
function toFailedModel(panelResponse) {
|
|
81
|
-
return { model: panelResponse.model, reason: panelResponse.error || 'unknown' };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @param {FailedModel[]} failedModels
|
|
86
|
-
* @returns {string}
|
|
87
|
-
*/
|
|
88
|
-
function classify(failedModels) {
|
|
89
|
-
if (!failedModels || failedModels.length === 0) return 'all_panels_failed';
|
|
90
|
-
const combined = failedModels.map(f => f.reason || '').join(' ').toLowerCase();
|
|
91
|
-
if (/rate.?limit|429|too many request/.test(combined)) return 'rate_limited';
|
|
92
|
-
if (/insufficient|credit|quota|billing|payment|exceed.*(usage|plan)/.test(combined)) return 'insufficient_credits';
|
|
93
|
-
if (/enoent|spawn|signal|crash|killed/.test(combined)) return 'unexpected_error';
|
|
94
|
-
return 'all_panels_failed';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Port VERBATIM from scripts/compress.cjs — strips an outer ```fence
|
|
98
|
-
// wrapping the ENTIRE output.
|
|
99
|
-
function stripLlmWrapper(text) {
|
|
100
|
-
const m = text.match(/^\s*(`{3,}|~{3,})[^\n]*\n([\s\S]*)\n\1\s*$/);
|
|
101
|
-
return m ? m[2] : text;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
module.exports = {
|
|
105
|
-
FAILURE_REASONS,
|
|
106
|
-
isPanelResponse,
|
|
107
|
-
isAnalysis,
|
|
108
|
-
parseAnalysis,
|
|
109
|
-
toFailedModel,
|
|
110
|
-
classify,
|
|
111
|
-
stripLlmWrapper,
|
|
112
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Maestro Frontier — shared types-as-validators + helpers.
|
|
3
|
+
// Zero deps, CJS. Ported stripLlmWrapper from scripts/compress.cjs.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {{ model:string, content:string, ok:boolean, durationMs:number, tokensEst:number, toolCalls?:unknown[], error?:string }} PanelResponse
|
|
9
|
+
* @typedef {{ model:string, reason:string }} FailedModel
|
|
10
|
+
* @typedef {{ consensus:string[], contradictions:{topic:string,stances:{model:string,stance:string}[]}[], partial_coverage:{models:string[],point:string}[], unique_insights:{model:string,insight:string}[], blind_spots:string[] }} Analysis
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** @type {string[]} */
|
|
14
|
+
const FAILURE_REASONS = [
|
|
15
|
+
'all_panels_failed',
|
|
16
|
+
'insufficient_credits',
|
|
17
|
+
'rate_limited',
|
|
18
|
+
'fusion_invocation_capped',
|
|
19
|
+
'unexpected_error',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {unknown} x
|
|
24
|
+
* @returns {x is PanelResponse}
|
|
25
|
+
*/
|
|
26
|
+
function isPanelResponse(x) {
|
|
27
|
+
if (x === null || typeof x !== 'object') return false;
|
|
28
|
+
const o = /** @type {Record<string,unknown>} */ (x);
|
|
29
|
+
return (
|
|
30
|
+
typeof o.model === 'string' &&
|
|
31
|
+
typeof o.content === 'string' &&
|
|
32
|
+
typeof o.ok === 'boolean' &&
|
|
33
|
+
typeof o.durationMs === 'number' &&
|
|
34
|
+
typeof o.tokensEst === 'number'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {unknown} x
|
|
40
|
+
* @returns {x is Analysis}
|
|
41
|
+
*/
|
|
42
|
+
function isAnalysis(x) {
|
|
43
|
+
if (x === null || typeof x !== 'object') return false;
|
|
44
|
+
const o = /** @type {Record<string,unknown>} */ (x);
|
|
45
|
+
if (!Array.isArray(o.consensus)) return false;
|
|
46
|
+
if (!o.consensus.every(s => typeof s === 'string')) return false;
|
|
47
|
+
if (!Array.isArray(o.blind_spots)) return false;
|
|
48
|
+
if (!o.blind_spots.every(s => typeof s === 'string')) return false;
|
|
49
|
+
if (!Array.isArray(o.contradictions)) return false;
|
|
50
|
+
if (!o.contradictions.every(e => e !== null && typeof e === 'object')) return false;
|
|
51
|
+
if (!Array.isArray(o.partial_coverage)) return false;
|
|
52
|
+
if (!o.partial_coverage.every(e => e !== null && typeof e === 'object')) return false;
|
|
53
|
+
if (!Array.isArray(o.unique_insights)) return false;
|
|
54
|
+
if (!o.unique_insights.every(e => e !== null && typeof e === 'object')) return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} str
|
|
60
|
+
* @returns {Analysis}
|
|
61
|
+
* @throws {Error}
|
|
62
|
+
*/
|
|
63
|
+
function parseAnalysis(str) {
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(str);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
throw new Error('parseAnalysis: invalid JSON — ' + e.message);
|
|
69
|
+
}
|
|
70
|
+
if (!isAnalysis(parsed)) {
|
|
71
|
+
throw new Error('parseAnalysis: object does not satisfy Analysis shape');
|
|
72
|
+
}
|
|
73
|
+
return /** @type {Analysis} */ (parsed);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {PanelResponse} panelResponse
|
|
78
|
+
* @returns {FailedModel}
|
|
79
|
+
*/
|
|
80
|
+
function toFailedModel(panelResponse) {
|
|
81
|
+
return { model: panelResponse.model, reason: panelResponse.error || 'unknown' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {FailedModel[]} failedModels
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
function classify(failedModels) {
|
|
89
|
+
if (!failedModels || failedModels.length === 0) return 'all_panels_failed';
|
|
90
|
+
const combined = failedModels.map(f => f.reason || '').join(' ').toLowerCase();
|
|
91
|
+
if (/rate.?limit|429|too many request/.test(combined)) return 'rate_limited';
|
|
92
|
+
if (/insufficient|credit|quota|billing|payment|exceed.*(usage|plan)/.test(combined)) return 'insufficient_credits';
|
|
93
|
+
if (/enoent|spawn|signal|crash|killed/.test(combined)) return 'unexpected_error';
|
|
94
|
+
return 'all_panels_failed';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Port VERBATIM from scripts/compress.cjs — strips an outer ```fence
|
|
98
|
+
// wrapping the ENTIRE output.
|
|
99
|
+
function stripLlmWrapper(text) {
|
|
100
|
+
const m = text.match(/^\s*(`{3,}|~{3,})[^\n]*\n([\s\S]*)\n\1\s*$/);
|
|
101
|
+
return m ? m[2] : text;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
FAILURE_REASONS,
|
|
106
|
+
isPanelResponse,
|
|
107
|
+
isAnalysis,
|
|
108
|
+
parseAnalysis,
|
|
109
|
+
toFailedModel,
|
|
110
|
+
classify,
|
|
111
|
+
stripLlmWrapper,
|
|
112
|
+
};
|
package/frontier/semaphore.cjs
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Maestro Frontier — bounded-concurrency map utility.
|
|
3
|
-
// mapLimit(items, limit, asyncFn) -> Promise<settled[]>
|
|
4
|
-
// Each settled item is {ok:true,value} or {ok:false,error}.
|
|
5
|
-
// A rejected task releases its permit and does NOT starve the pool.
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @template T, R
|
|
11
|
-
* @param {T[]} items
|
|
12
|
-
* @param {number} limit
|
|
13
|
-
* @param {(item: T, index: number) => Promise<R>} asyncFn
|
|
14
|
-
* @returns {Promise<({ok:true,value:R}|{ok:false,error:unknown})[]}
|
|
15
|
-
*/
|
|
16
|
-
function mapLimit(items, limit, asyncFn) {
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
const n = items.length;
|
|
19
|
-
if (n === 0) { resolve([]); return; }
|
|
20
|
-
|
|
21
|
-
const results = new Array(n);
|
|
22
|
-
let nextIdx = 0; // index of next item to start
|
|
23
|
-
let inFlight = 0; // currently running tasks
|
|
24
|
-
let done = 0; // settled tasks
|
|
25
|
-
|
|
26
|
-
function run() {
|
|
27
|
-
while (inFlight < limit && nextIdx < n) {
|
|
28
|
-
const idx = nextIdx++;
|
|
29
|
-
inFlight++;
|
|
30
|
-
Promise.resolve()
|
|
31
|
-
.then(() => asyncFn(items[idx], idx))
|
|
32
|
-
.then(
|
|
33
|
-
(value) => { results[idx] = { ok: true, value }; },
|
|
34
|
-
(error) => { results[idx] = { ok: false, error }; }
|
|
35
|
-
)
|
|
36
|
-
.finally(() => {
|
|
37
|
-
inFlight--;
|
|
38
|
-
done++;
|
|
39
|
-
if (done === n) { resolve(results); return; }
|
|
40
|
-
run();
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
run();
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = { mapLimit };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Maestro Frontier — bounded-concurrency map utility.
|
|
3
|
+
// mapLimit(items, limit, asyncFn) -> Promise<settled[]>
|
|
4
|
+
// Each settled item is {ok:true,value} or {ok:false,error}.
|
|
5
|
+
// A rejected task releases its permit and does NOT starve the pool.
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @template T, R
|
|
11
|
+
* @param {T[]} items
|
|
12
|
+
* @param {number} limit
|
|
13
|
+
* @param {(item: T, index: number) => Promise<R>} asyncFn
|
|
14
|
+
* @returns {Promise<({ok:true,value:R}|{ok:false,error:unknown})[]}
|
|
15
|
+
*/
|
|
16
|
+
function mapLimit(items, limit, asyncFn) {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
const n = items.length;
|
|
19
|
+
if (n === 0) { resolve([]); return; }
|
|
20
|
+
|
|
21
|
+
const results = new Array(n);
|
|
22
|
+
let nextIdx = 0; // index of next item to start
|
|
23
|
+
let inFlight = 0; // currently running tasks
|
|
24
|
+
let done = 0; // settled tasks
|
|
25
|
+
|
|
26
|
+
function run() {
|
|
27
|
+
while (inFlight < limit && nextIdx < n) {
|
|
28
|
+
const idx = nextIdx++;
|
|
29
|
+
inFlight++;
|
|
30
|
+
Promise.resolve()
|
|
31
|
+
.then(() => asyncFn(items[idx], idx))
|
|
32
|
+
.then(
|
|
33
|
+
(value) => { results[idx] = { ok: true, value }; },
|
|
34
|
+
(error) => { results[idx] = { ok: false, error }; }
|
|
35
|
+
)
|
|
36
|
+
.finally(() => {
|
|
37
|
+
inFlight--;
|
|
38
|
+
done++;
|
|
39
|
+
if (done === n) { resolve(results); return; }
|
|
40
|
+
run();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
run();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { mapLimit };
|
package/frontier/synthesize.cjs
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Maestro Frontier — synthesis stage: build prompt + invoke Opus for final answer.
|
|
3
|
-
|
|
4
|
-
'use strict';
|
|
5
|
-
|
|
6
|
-
const dispatch = require('./dispatch.cjs');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Build the synthesis prompt for Opus.
|
|
10
|
-
* @param {string} userPrompt
|
|
11
|
-
* @param {{ analysis?: import('./schema.cjs').Analysis, responses: import('./schema.cjs').PanelResponse[] }} bundle
|
|
12
|
-
* @param {object} cfg
|
|
13
|
-
* @returns {string}
|
|
14
|
-
*/
|
|
15
|
-
function buildSynthPrompt(userPrompt, bundle, cfg) {
|
|
16
|
-
const antiMajority =
|
|
17
|
-
'Do NOT majority-vote or pick the most common answer; weigh correctness and evidence — ' +
|
|
18
|
-
'a single correct minority response outweighs a popular wrong one.';
|
|
19
|
-
|
|
20
|
-
let groundingSection;
|
|
21
|
-
if (bundle.analysis) {
|
|
22
|
-
groundingSection =
|
|
23
|
-
`PANEL ANALYSIS (structured):
|
|
24
|
-
${JSON.stringify(bundle.analysis, null, 2)}
|
|
25
|
-
|
|
26
|
-
Ground your final answer in this analysis:
|
|
27
|
-
- Adopt the consensus points as established facts.
|
|
28
|
-
- RESOLVE contradictions by reasoning about which stance is most correct; do not dodge them.
|
|
29
|
-
- Preserve unique insights that add value.
|
|
30
|
-
- Address any blind spots the analysis identified.`;
|
|
31
|
-
} else {
|
|
32
|
-
const raw = bundle.responses.map(
|
|
33
|
-
r => `### Response from ${r.model}\n${r.content}`
|
|
34
|
-
).join('\n\n');
|
|
35
|
-
groundingSection =
|
|
36
|
-
`RAW PANEL RESPONSES:
|
|
37
|
-
${raw}
|
|
38
|
-
|
|
39
|
-
Ground your final answer in these responses.`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return `You are a SYNTHESIZER producing the definitive final answer to a user question.
|
|
43
|
-
|
|
44
|
-
USER QUESTION:
|
|
45
|
-
${userPrompt}
|
|
46
|
-
|
|
47
|
-
${groundingSection}
|
|
48
|
-
|
|
49
|
-
IMPORTANT: ${antiMajority}
|
|
50
|
-
|
|
51
|
-
Write the final answer as clear, direct prose. No JSON, no meta-commentary, no preamble about your process. Output the answer only.`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Run the synthesis stage. Returns the final answer string or '' on failure (degrades gracefully).
|
|
56
|
-
* @param {string} userPrompt
|
|
57
|
-
* @param {{ analysis?: import('./schema.cjs').Analysis, responses: import('./schema.cjs').PanelResponse[] }} bundle
|
|
58
|
-
* @param {object} cfg
|
|
59
|
-
* @param {{ spawn?: Function }} [deps]
|
|
60
|
-
* @returns {Promise<string>}
|
|
61
|
-
*/
|
|
62
|
-
async function runSynth(userPrompt, bundle, cfg, deps) {
|
|
63
|
-
const spawn = (deps && deps.spawn) || dispatch.spawnOne;
|
|
64
|
-
let r;
|
|
65
|
-
try {
|
|
66
|
-
r = await spawn(
|
|
67
|
-
buildSynthPrompt(userPrompt, bundle, cfg),
|
|
68
|
-
cfg.adapters[cfg.synthModel],
|
|
69
|
-
{ timeoutMs: cfg.timeoutMs, fusionDepth: 1 }
|
|
70
|
-
);
|
|
71
|
-
} catch {
|
|
72
|
-
return '';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (r && r.ok && r.content) return r.content;
|
|
76
|
-
return '';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
module.exports = { buildSynthPrompt, runSynth };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Maestro Frontier — synthesis stage: build prompt + invoke Opus for final answer.
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const dispatch = require('./dispatch.cjs');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build the synthesis prompt for Opus.
|
|
10
|
+
* @param {string} userPrompt
|
|
11
|
+
* @param {{ analysis?: import('./schema.cjs').Analysis, responses: import('./schema.cjs').PanelResponse[] }} bundle
|
|
12
|
+
* @param {object} cfg
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function buildSynthPrompt(userPrompt, bundle, cfg) {
|
|
16
|
+
const antiMajority =
|
|
17
|
+
'Do NOT majority-vote or pick the most common answer; weigh correctness and evidence — ' +
|
|
18
|
+
'a single correct minority response outweighs a popular wrong one.';
|
|
19
|
+
|
|
20
|
+
let groundingSection;
|
|
21
|
+
if (bundle.analysis) {
|
|
22
|
+
groundingSection =
|
|
23
|
+
`PANEL ANALYSIS (structured):
|
|
24
|
+
${JSON.stringify(bundle.analysis, null, 2)}
|
|
25
|
+
|
|
26
|
+
Ground your final answer in this analysis:
|
|
27
|
+
- Adopt the consensus points as established facts.
|
|
28
|
+
- RESOLVE contradictions by reasoning about which stance is most correct; do not dodge them.
|
|
29
|
+
- Preserve unique insights that add value.
|
|
30
|
+
- Address any blind spots the analysis identified.`;
|
|
31
|
+
} else {
|
|
32
|
+
const raw = bundle.responses.map(
|
|
33
|
+
r => `### Response from ${r.model}\n${r.content}`
|
|
34
|
+
).join('\n\n');
|
|
35
|
+
groundingSection =
|
|
36
|
+
`RAW PANEL RESPONSES:
|
|
37
|
+
${raw}
|
|
38
|
+
|
|
39
|
+
Ground your final answer in these responses.`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return `You are a SYNTHESIZER producing the definitive final answer to a user question.
|
|
43
|
+
|
|
44
|
+
USER QUESTION:
|
|
45
|
+
${userPrompt}
|
|
46
|
+
|
|
47
|
+
${groundingSection}
|
|
48
|
+
|
|
49
|
+
IMPORTANT: ${antiMajority}
|
|
50
|
+
|
|
51
|
+
Write the final answer as clear, direct prose. No JSON, no meta-commentary, no preamble about your process. Output the answer only.`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run the synthesis stage. Returns the final answer string or '' on failure (degrades gracefully).
|
|
56
|
+
* @param {string} userPrompt
|
|
57
|
+
* @param {{ analysis?: import('./schema.cjs').Analysis, responses: import('./schema.cjs').PanelResponse[] }} bundle
|
|
58
|
+
* @param {object} cfg
|
|
59
|
+
* @param {{ spawn?: Function }} [deps]
|
|
60
|
+
* @returns {Promise<string>}
|
|
61
|
+
*/
|
|
62
|
+
async function runSynth(userPrompt, bundle, cfg, deps) {
|
|
63
|
+
const spawn = (deps && deps.spawn) || dispatch.spawnOne;
|
|
64
|
+
let r;
|
|
65
|
+
try {
|
|
66
|
+
r = await spawn(
|
|
67
|
+
buildSynthPrompt(userPrompt, bundle, cfg),
|
|
68
|
+
cfg.adapters[cfg.synthModel],
|
|
69
|
+
{ timeoutMs: cfg.timeoutMs, fusionDepth: 1 }
|
|
70
|
+
);
|
|
71
|
+
} catch {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (r && r.ok && r.content) return r.content;
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { buildSynthPrompt, runSynth };
|