@damian87/omp 0.3.0 → 0.4.1
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/.github/skills/weighted-consensus/SKILL.md +128 -0
- package/README.md +75 -1
- package/catalog/capabilities.json +46 -0
- package/catalog/skills-general.json +29 -1
- package/dist/src/cli.d.ts +8 -0
- package/dist/src/cli.js +125 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/council/config.d.ts +33 -0
- package/dist/src/council/config.js +89 -0
- package/dist/src/council/config.js.map +1 -0
- package/dist/src/council/engine.d.ts +21 -0
- package/dist/src/council/engine.js +216 -0
- package/dist/src/council/engine.js.map +1 -0
- package/dist/src/council/index.d.ts +20 -0
- package/dist/src/council/index.js +72 -0
- package/dist/src/council/index.js.map +1 -0
- package/dist/src/council/prompts.d.ts +32 -0
- package/dist/src/council/prompts.js +172 -0
- package/dist/src/council/prompts.js.map +1 -0
- package/dist/src/council/synth.d.ts +15 -0
- package/dist/src/council/synth.js +57 -0
- package/dist/src/council/synth.js.map +1 -0
- package/dist/src/council/types.d.ts +98 -0
- package/dist/src/council/types.js +4 -0
- package/dist/src/council/types.js.map +1 -0
- package/package.json +3 -1
- package/dist/test/catalog.test.d.ts +0 -1
- package/dist/test/catalog.test.js +0 -21
- package/dist/test/catalog.test.js.map +0 -1
- package/dist/test/jira.test.d.ts +0 -1
- package/dist/test/jira.test.js +0 -26
- package/dist/test/jira.test.js.map +0 -1
- package/dist/test/lint.test.d.ts +0 -1
- package/dist/test/lint.test.js +0 -9
- package/dist/test/lint.test.js.map +0 -1
- package/dist/test/sync.test.d.ts +0 -1
- package/dist/test/sync.test.js +0 -15
- package/dist/test/sync.test.js.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CouncilMemberSpec, CouncilTaskSpec, ResolvedCouncilConfig } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_MIN_SURVIVORS = 2;
|
|
3
|
+
export declare const DEFAULT_PER_MEMBER_TIMEOUT_MS = 120000;
|
|
4
|
+
export declare const DEFAULT_MAX_CONCURRENCY = 4;
|
|
5
|
+
/**
|
|
6
|
+
* Built-in default roster. Uses the free/included lightweight models verified
|
|
7
|
+
* available via the Task 0 spike (gpt-5-mini, gpt-4.1) so the council runs
|
|
8
|
+
* without consuming premium model quota. Users override these in
|
|
9
|
+
* .omp/config.json `council` or inline via --models (e.g. add a Claude member
|
|
10
|
+
* for cross-provider diversity if their plan includes it).
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_MEMBERS: CouncilMemberSpec[];
|
|
13
|
+
export declare const DEFAULT_SYNTHESIZER = "gpt-4.1";
|
|
14
|
+
interface PartialCouncilFileConfig {
|
|
15
|
+
members?: unknown;
|
|
16
|
+
synthesizer?: unknown;
|
|
17
|
+
minSurvivors?: unknown;
|
|
18
|
+
perMemberTimeoutMs?: unknown;
|
|
19
|
+
synthTimeoutMs?: unknown;
|
|
20
|
+
maxConcurrency?: unknown;
|
|
21
|
+
probe?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface LoadCouncilConfigOptions {
|
|
24
|
+
cwd?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Read the `council` block from .omp/config.json; tolerate missing/malformed. */
|
|
27
|
+
export declare function readCouncilFileConfig(options?: LoadCouncilConfigOptions): PartialCouncilFileConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the effective council configuration.
|
|
30
|
+
* Precedence: spec override > .omp/config.json council block > built-in default.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadCouncilConfig(spec?: CouncilTaskSpec, options?: LoadCouncilConfigOptions): ResolvedCouncilConfig;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const DEFAULT_MIN_SURVIVORS = 2;
|
|
4
|
+
export const DEFAULT_PER_MEMBER_TIMEOUT_MS = 120000;
|
|
5
|
+
export const DEFAULT_MAX_CONCURRENCY = 4;
|
|
6
|
+
/**
|
|
7
|
+
* Built-in default roster. Uses the free/included lightweight models verified
|
|
8
|
+
* available via the Task 0 spike (gpt-5-mini, gpt-4.1) so the council runs
|
|
9
|
+
* without consuming premium model quota. Users override these in
|
|
10
|
+
* .omp/config.json `council` or inline via --models (e.g. add a Claude member
|
|
11
|
+
* for cross-provider diversity if their plan includes it).
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_MEMBERS = [
|
|
14
|
+
{ model: "gpt-5-mini", role: "critic", weight: 0.4 },
|
|
15
|
+
{ model: "gpt-4.1", role: "architect", weight: 0.35 },
|
|
16
|
+
{ model: "gpt-5-mini", role: "pragmatist", weight: 0.25 },
|
|
17
|
+
];
|
|
18
|
+
export const DEFAULT_SYNTHESIZER = "gpt-4.1";
|
|
19
|
+
function isValidMember(value) {
|
|
20
|
+
if (typeof value !== "object" || value === null)
|
|
21
|
+
return false;
|
|
22
|
+
const m = value;
|
|
23
|
+
return (typeof m.model === "string" &&
|
|
24
|
+
m.model.length > 0 &&
|
|
25
|
+
typeof m.role === "string" &&
|
|
26
|
+
m.role.length > 0 &&
|
|
27
|
+
typeof m.weight === "number" &&
|
|
28
|
+
Number.isFinite(m.weight) &&
|
|
29
|
+
m.weight > 0);
|
|
30
|
+
}
|
|
31
|
+
/** Read the `council` block from .omp/config.json; tolerate missing/malformed. */
|
|
32
|
+
export function readCouncilFileConfig(options = {}) {
|
|
33
|
+
const cwd = options.cwd ?? process.cwd();
|
|
34
|
+
const configFile = join(cwd, ".omp", "config.json");
|
|
35
|
+
if (!existsSync(configFile))
|
|
36
|
+
return {};
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(readFileSync(configFile, "utf8"));
|
|
39
|
+
return parsed.council ?? {};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {}; // unreadable/invalid config -> treat as absent
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the effective council configuration.
|
|
47
|
+
* Precedence: spec override > .omp/config.json council block > built-in default.
|
|
48
|
+
*/
|
|
49
|
+
export function loadCouncilConfig(spec = { question: "" }, options = {}) {
|
|
50
|
+
const file = readCouncilFileConfig(options);
|
|
51
|
+
const fileMembers = Array.isArray(file.members)
|
|
52
|
+
? file.members.filter(isValidMember)
|
|
53
|
+
: [];
|
|
54
|
+
const members = spec.members && spec.members.length > 0
|
|
55
|
+
? spec.members
|
|
56
|
+
: fileMembers.length > 0
|
|
57
|
+
? fileMembers
|
|
58
|
+
: DEFAULT_MEMBERS;
|
|
59
|
+
const synthesizerModel = spec.synthesizerModel ??
|
|
60
|
+
(typeof file.synthesizer === "string" && file.synthesizer.length > 0
|
|
61
|
+
? file.synthesizer
|
|
62
|
+
: DEFAULT_SYNTHESIZER);
|
|
63
|
+
const minSurvivors = spec.minSurvivors ??
|
|
64
|
+
(typeof file.minSurvivors === "number" ? file.minSurvivors : DEFAULT_MIN_SURVIVORS);
|
|
65
|
+
const perMemberTimeoutMs = spec.perMemberTimeoutMs ??
|
|
66
|
+
(typeof file.perMemberTimeoutMs === "number"
|
|
67
|
+
? file.perMemberTimeoutMs
|
|
68
|
+
: DEFAULT_PER_MEMBER_TIMEOUT_MS);
|
|
69
|
+
const synthTimeoutMs = spec.synthTimeoutMs ??
|
|
70
|
+
(typeof file.synthTimeoutMs === "number"
|
|
71
|
+
? file.synthTimeoutMs
|
|
72
|
+
: perMemberTimeoutMs * 2);
|
|
73
|
+
const maxConcurrency = spec.maxConcurrency ??
|
|
74
|
+
(typeof file.maxConcurrency === "number"
|
|
75
|
+
? file.maxConcurrency
|
|
76
|
+
: DEFAULT_MAX_CONCURRENCY);
|
|
77
|
+
// probe precedence: spec.probe ?? config.probe ?? false
|
|
78
|
+
const probe = spec.probe ?? (typeof file.probe === "boolean" ? file.probe : false);
|
|
79
|
+
return {
|
|
80
|
+
members,
|
|
81
|
+
synthesizerModel,
|
|
82
|
+
minSurvivors,
|
|
83
|
+
perMemberTimeoutMs,
|
|
84
|
+
synthTimeoutMs,
|
|
85
|
+
maxConcurrency: maxConcurrency > 0 ? maxConcurrency : DEFAULT_MAX_CONCURRENCY,
|
|
86
|
+
probe,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/council/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AACpD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAwB;IAClD,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE;IACpD,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;IACrD,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE;CAC1D,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAgB7C,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAC3B,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAClB,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACjB,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACzB,CAAC,CAAC,MAAM,GAAG,CAAC,CACb,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,qBAAqB,CACnC,UAAoC,EAAE;IAEtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAEzD,CAAC;QACF,OAAO,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,+CAA+C;IAC5D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAwB,EAAE,QAAQ,EAAE,EAAE,EAAE,EACxC,UAAoC,EAAE;IAEtC,MAAM,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE5C,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;QACpC,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QACrC,CAAC,CAAC,IAAI,CAAC,OAAO;QACd,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YACtB,CAAC,CAAC,WAAW;YACb,CAAC,CAAC,eAAe,CAAC;IAExB,MAAM,gBAAgB,GACpB,IAAI,CAAC,gBAAgB;QACrB,CAAC,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YAClE,CAAC,CAAC,IAAI,CAAC,WAAW;YAClB,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAE3B,MAAM,YAAY,GAChB,IAAI,CAAC,YAAY;QACjB,CAAC,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;IAEtF,MAAM,kBAAkB,GACtB,IAAI,CAAC,kBAAkB;QACvB,CAAC,OAAO,IAAI,CAAC,kBAAkB,KAAK,QAAQ;YAC1C,CAAC,CAAC,IAAI,CAAC,kBAAkB;YACzB,CAAC,CAAC,6BAA6B,CAAC,CAAC;IAErC,MAAM,cAAc,GAClB,IAAI,CAAC,cAAc;QACnB,CAAC,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;YACtC,CAAC,CAAC,IAAI,CAAC,cAAc;YACrB,CAAC,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC;IAE9B,MAAM,cAAc,GAClB,IAAI,CAAC,cAAc;QACnB,CAAC,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;YACtC,CAAC,CAAC,IAAI,CAAC,cAAc;YACrB,CAAC,CAAC,uBAAuB,CAAC,CAAC;IAE/B,wDAAwD;IACxD,MAAM,KAAK,GACT,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEvE,OAAO;QACL,OAAO;QACP,gBAAgB;QAChB,YAAY;QACZ,kBAAkB;QAClB,cAAc;QACd,cAAc,EAAE,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,uBAAuB;QAC7E,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type LoadCouncilConfigOptions } from "./config.js";
|
|
2
|
+
import type { CouncilDeps, CouncilRunResult, CouncilTaskSpec } from "./types.js";
|
|
3
|
+
export type ProgressCallback = (message: string) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Run `worker` over `items` with at most `limit` in flight. The slot is released
|
|
6
|
+
* in a finally block so a rejected/errored worker frees its slot too (otherwise
|
|
7
|
+
* a rejection would deadlock the queue for subsequent items). Results preserve
|
|
8
|
+
* input order. Worker rejections are reflected as rejected result promises, so
|
|
9
|
+
* callers should make `worker` non-throwing (we do).
|
|
10
|
+
*/
|
|
11
|
+
export declare function runWithConcurrency<T, R>(items: T[], limit: number, worker: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
12
|
+
export interface RunCouncilOptions extends LoadCouncilConfigOptions {
|
|
13
|
+
onProgress?: ProgressCallback;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fan a question out to the council, parse + classify each member, then
|
|
17
|
+
* synthesize from survivors. Degrades gracefully: drops timed-out / unavailable /
|
|
18
|
+
* errored / unparseable members and synthesizes from the rest; fails only if
|
|
19
|
+
* fewer than `minSurvivors` survive.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runCouncil(spec: CouncilTaskSpec, deps: CouncilDeps, options?: RunCouncilOptions): Promise<CouncilRunResult>;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { tmpdir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { loadCouncilConfig } from "./config.js";
|
|
4
|
+
import { buildMemberPrompt, parseMemberOutput } from "./prompts.js";
|
|
5
|
+
import { synthesize } from "./synth.js";
|
|
6
|
+
/** stderr signature emitted by copilot when a --model slug is not entitled. */
|
|
7
|
+
const UNAVAILABLE_SIGNATURE = /is not available/i;
|
|
8
|
+
function isUnavailable(res) {
|
|
9
|
+
return res.exitCode !== 0 && !res.timedOut && UNAVAILABLE_SIGNATURE.test(res.stderr);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run `worker` over `items` with at most `limit` in flight. The slot is released
|
|
13
|
+
* in a finally block so a rejected/errored worker frees its slot too (otherwise
|
|
14
|
+
* a rejection would deadlock the queue for subsequent items). Results preserve
|
|
15
|
+
* input order. Worker rejections are reflected as rejected result promises, so
|
|
16
|
+
* callers should make `worker` non-throwing (we do).
|
|
17
|
+
*/
|
|
18
|
+
export async function runWithConcurrency(items, limit, worker) {
|
|
19
|
+
const results = new Array(items.length);
|
|
20
|
+
let next = 0;
|
|
21
|
+
const effectiveLimit = Math.max(1, Math.min(limit, items.length || 1));
|
|
22
|
+
async function runner() {
|
|
23
|
+
while (true) {
|
|
24
|
+
const current = next++;
|
|
25
|
+
if (current >= items.length)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
results[current] = await worker(items[current], current);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
// slot is freed simply by looping to the next item
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const runners = [];
|
|
36
|
+
for (let i = 0; i < effectiveLimit; i++)
|
|
37
|
+
runners.push(runner());
|
|
38
|
+
await Promise.all(runners);
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
function classify(res) {
|
|
42
|
+
if (res.timedOut)
|
|
43
|
+
return { status: "timeout", dropReason: "member timed out" };
|
|
44
|
+
if (isUnavailable(res)) {
|
|
45
|
+
return { status: "unavailable", dropReason: "model not available to this Copilot plan" };
|
|
46
|
+
}
|
|
47
|
+
if (res.exitCode !== 0) {
|
|
48
|
+
return { status: "error", dropReason: `member exited ${res.exitCode}` };
|
|
49
|
+
}
|
|
50
|
+
return { status: "ok" };
|
|
51
|
+
}
|
|
52
|
+
async function runMember(spec, index, taskSpec, config, deps, tmpDir) {
|
|
53
|
+
const prompt = buildMemberPrompt(taskSpec, spec);
|
|
54
|
+
const started = (deps.now ?? Date.now)();
|
|
55
|
+
let res;
|
|
56
|
+
try {
|
|
57
|
+
res = await deps.spawn({ model: spec.model, prompt, timeoutMs: config.perMemberTimeoutMs });
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
spec,
|
|
62
|
+
status: "error",
|
|
63
|
+
durationMs: 0,
|
|
64
|
+
dropReason: `spawn failed: ${String(err)}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const durationMs = (deps.now ?? Date.now)() - started;
|
|
68
|
+
let { status, dropReason } = classify(res);
|
|
69
|
+
let output;
|
|
70
|
+
if (status === "ok" || status === "timeout") {
|
|
71
|
+
const parsed = parseMemberOutput(res.stdout);
|
|
72
|
+
if (parsed) {
|
|
73
|
+
// Upgrade timed-out members that produced valid output before the kill.
|
|
74
|
+
if (status === "timeout") {
|
|
75
|
+
status = "ok";
|
|
76
|
+
dropReason = undefined;
|
|
77
|
+
}
|
|
78
|
+
output = parsed;
|
|
79
|
+
}
|
|
80
|
+
else if (status === "ok") {
|
|
81
|
+
status = "unparseable";
|
|
82
|
+
dropReason = "no schema-valid JSON in output";
|
|
83
|
+
}
|
|
84
|
+
// timeout with no parseable output stays "timeout"
|
|
85
|
+
}
|
|
86
|
+
const jsonPath = join(tmpDir, `m${index}.json`);
|
|
87
|
+
const artifact = output ?? { rawStdout: res.stdout, rawStderr: res.stderr, status };
|
|
88
|
+
if (deps.writeArtifact) {
|
|
89
|
+
try {
|
|
90
|
+
deps.writeArtifact(jsonPath, JSON.stringify(artifact, null, 2));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// artifact write is best-effort; ignore failures
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
spec,
|
|
98
|
+
status,
|
|
99
|
+
output,
|
|
100
|
+
rawStdout: res.stdout,
|
|
101
|
+
rawStderr: res.stderr,
|
|
102
|
+
exitCode: res.exitCode,
|
|
103
|
+
durationMs,
|
|
104
|
+
dropReason,
|
|
105
|
+
jsonPath,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Opt-in preflight probe: cheaply ping each DISTINCT model and prune only those
|
|
110
|
+
* that report "unavailable" (exit + stderr signature). A slow/timed-out probe is
|
|
111
|
+
* NOT treated as unavailable, so reachable-but-slow models are kept.
|
|
112
|
+
*/
|
|
113
|
+
async function probeRoster(members, config, deps) {
|
|
114
|
+
const distinct = Array.from(new Set(members.map((m) => m.model)));
|
|
115
|
+
const probeTimeout = Math.min(15000, config.perMemberTimeoutMs);
|
|
116
|
+
const verdicts = await runWithConcurrency(distinct, config.maxConcurrency, async (model) => {
|
|
117
|
+
try {
|
|
118
|
+
const res = await deps.spawn({ model, prompt: "Reply with: ok", timeoutMs: probeTimeout });
|
|
119
|
+
return { model, unavailable: isUnavailable(res) };
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return { model, unavailable: false }; // spawn error != entitlement problem
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const unavailable = new Set(verdicts.filter((v) => v.unavailable).map((v) => v.model));
|
|
126
|
+
const kept = members.filter((m) => !unavailable.has(m.model));
|
|
127
|
+
const pruned = members
|
|
128
|
+
.filter((m) => unavailable.has(m.model))
|
|
129
|
+
.map((m) => ({ model: m.model, reason: "model not available to this Copilot plan" }));
|
|
130
|
+
return { kept, pruned };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Fan a question out to the council, parse + classify each member, then
|
|
134
|
+
* synthesize from survivors. Degrades gracefully: drops timed-out / unavailable /
|
|
135
|
+
* errored / unparseable members and synthesizes from the rest; fails only if
|
|
136
|
+
* fewer than `minSurvivors` survive.
|
|
137
|
+
*/
|
|
138
|
+
export async function runCouncil(spec, deps, options = {}) {
|
|
139
|
+
const config = loadCouncilConfig(spec, options);
|
|
140
|
+
const now = deps.now ?? Date.now;
|
|
141
|
+
const tmpDir = spec.tmpDir ?? join(tmpdir(), `council-${now()}`);
|
|
142
|
+
const progress = options.onProgress;
|
|
143
|
+
let roster = config.members;
|
|
144
|
+
const prunedResults = [];
|
|
145
|
+
if (config.probe) {
|
|
146
|
+
progress?.(`Probing ${roster.length} model(s)…`);
|
|
147
|
+
const { kept, pruned } = await probeRoster(roster, config, deps);
|
|
148
|
+
roster = kept;
|
|
149
|
+
for (const p of pruned) {
|
|
150
|
+
const spc = config.members.find((m) => m.model === p.model);
|
|
151
|
+
if (spc) {
|
|
152
|
+
progress?.(` ✗ ${spc.model} (${spc.role}): unavailable`);
|
|
153
|
+
prunedResults.push({
|
|
154
|
+
spec: spc,
|
|
155
|
+
status: "unavailable",
|
|
156
|
+
durationMs: 0,
|
|
157
|
+
dropReason: p.reason,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
progress?.(`Running ${roster.length} member(s)…`);
|
|
163
|
+
let completed = 0;
|
|
164
|
+
const ran = await runWithConcurrency(roster, config.maxConcurrency, async (member, i) => {
|
|
165
|
+
const result = await runMember(member, i, spec, config, deps, tmpDir);
|
|
166
|
+
completed++;
|
|
167
|
+
const dur = (result.durationMs / 1000).toFixed(1);
|
|
168
|
+
if (result.status === "ok") {
|
|
169
|
+
progress?.(` ✓ ${completed}/${roster.length} ${member.model} (${member.role}) ${dur}s`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
progress?.(` ✗ ${completed}/${roster.length} ${member.model} (${member.role}) ${result.status} ${dur}s`);
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
});
|
|
176
|
+
const members = [...ran, ...prunedResults];
|
|
177
|
+
const survivors = members.filter((m) => m.status === "ok");
|
|
178
|
+
const dropped = members.length - survivors.length;
|
|
179
|
+
if (survivors.length < config.minSurvivors) {
|
|
180
|
+
const unavailableModels = members
|
|
181
|
+
.filter((m) => m.status === "unavailable")
|
|
182
|
+
.map((m) => m.spec.model);
|
|
183
|
+
const detail = unavailableModels.length > 0
|
|
184
|
+
? ` Unavailable models: ${unavailableModels.join(", ")}.`
|
|
185
|
+
: "";
|
|
186
|
+
return {
|
|
187
|
+
ok: false,
|
|
188
|
+
members,
|
|
189
|
+
survivors: survivors.length,
|
|
190
|
+
dropped,
|
|
191
|
+
tmpDir,
|
|
192
|
+
error: `too few surviving members (${survivors.length}/${config.minSurvivors}).${detail}`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
progress?.(`Synthesizing from ${survivors.length} survivor(s)…`);
|
|
196
|
+
const synthResult = await synthesize(config, spec, members, deps);
|
|
197
|
+
if (!synthResult.ok || !synthResult.synth) {
|
|
198
|
+
return {
|
|
199
|
+
ok: false,
|
|
200
|
+
members,
|
|
201
|
+
survivors: survivors.length,
|
|
202
|
+
dropped,
|
|
203
|
+
tmpDir,
|
|
204
|
+
error: synthResult.error ?? "synthesis failed",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
synth: synthResult.synth,
|
|
210
|
+
members,
|
|
211
|
+
survivors: survivors.length,
|
|
212
|
+
dropped,
|
|
213
|
+
tmpDir,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/council/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RunCouncilOptions } from "./engine.js";
|
|
2
|
+
import type { CouncilDeps, CouncilRunResult, CouncilSpawn, CouncilTaskSpec } from "./types.js";
|
|
3
|
+
export * from "./types.js";
|
|
4
|
+
export { loadCouncilConfig, readCouncilFileConfig, DEFAULT_MEMBERS, DEFAULT_SYNTHESIZER, DEFAULT_MIN_SURVIVORS, DEFAULT_PER_MEMBER_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, } from "./config.js";
|
|
5
|
+
export { buildMemberPrompt, buildSynthPrompt, parseMemberOutput, parseSynthOutput, extractJsonCandidates, balancedBlocks, isValidMemberOutput, isValidSynthOutput, } from "./prompts.js";
|
|
6
|
+
export { runCouncil, runWithConcurrency, type ProgressCallback } from "./engine.js";
|
|
7
|
+
export { synthesize } from "./synth.js";
|
|
8
|
+
/**
|
|
9
|
+
* Default member invoker: spawn `copilot --model <model> -p <prompt> --allow-all-tools`
|
|
10
|
+
* with PIPED stdio so we can capture stdout. Uses resolveCopilotBin() so the
|
|
11
|
+
* OMP_COPILOT_BIN stub-bin test seam works. Does NOT route through launchCopilot
|
|
12
|
+
* (that tmux-wraps and inherits stdio, which would discard the model's output).
|
|
13
|
+
*/
|
|
14
|
+
export declare function createDefaultSpawn(bin?: string): CouncilSpawn;
|
|
15
|
+
/** Default deps: real spawn + fs artifact writer (mkdir -p on demand). */
|
|
16
|
+
export declare function createDefaultDeps(bin?: string): CouncilDeps;
|
|
17
|
+
/** Convenience entry for the CLI: run the council with the real default deps. */
|
|
18
|
+
export declare function runCouncilWithDefaults(spec: CouncilTaskSpec, options?: RunCouncilOptions & {
|
|
19
|
+
bin?: string;
|
|
20
|
+
}): Promise<CouncilRunResult>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { resolveCopilotBin } from "../copilot/launch.js";
|
|
5
|
+
import { runCouncil } from "./engine.js";
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export { loadCouncilConfig, readCouncilFileConfig, DEFAULT_MEMBERS, DEFAULT_SYNTHESIZER, DEFAULT_MIN_SURVIVORS, DEFAULT_PER_MEMBER_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, } from "./config.js";
|
|
8
|
+
export { buildMemberPrompt, buildSynthPrompt, parseMemberOutput, parseSynthOutput, extractJsonCandidates, balancedBlocks, isValidMemberOutput, isValidSynthOutput, } from "./prompts.js";
|
|
9
|
+
export { runCouncil, runWithConcurrency } from "./engine.js";
|
|
10
|
+
export { synthesize } from "./synth.js";
|
|
11
|
+
/**
|
|
12
|
+
* Default member invoker: spawn `copilot --model <model> -p <prompt> --allow-all-tools`
|
|
13
|
+
* with PIPED stdio so we can capture stdout. Uses resolveCopilotBin() so the
|
|
14
|
+
* OMP_COPILOT_BIN stub-bin test seam works. Does NOT route through launchCopilot
|
|
15
|
+
* (that tmux-wraps and inherits stdio, which would discard the model's output).
|
|
16
|
+
*/
|
|
17
|
+
export function createDefaultSpawn(bin) {
|
|
18
|
+
const copilotBin = resolveCopilotBin(bin);
|
|
19
|
+
return (req) => new Promise((resolveFn) => {
|
|
20
|
+
const child = spawn(copilotBin, ["--model", req.model, "-p", req.prompt, "--allow-all-tools"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
21
|
+
let stdout = "";
|
|
22
|
+
let stderr = "";
|
|
23
|
+
let timedOut = false;
|
|
24
|
+
let settled = false;
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
timedOut = true;
|
|
27
|
+
child.kill("SIGTERM");
|
|
28
|
+
}, req.timeoutMs);
|
|
29
|
+
child.stdout?.on("data", (d) => {
|
|
30
|
+
stdout += d.toString();
|
|
31
|
+
});
|
|
32
|
+
child.stderr?.on("data", (d) => {
|
|
33
|
+
stderr += d.toString();
|
|
34
|
+
});
|
|
35
|
+
child.on("error", () => {
|
|
36
|
+
if (settled)
|
|
37
|
+
return;
|
|
38
|
+
settled = true;
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
resolveFn({ stdout, stderr, exitCode: 127, timedOut });
|
|
41
|
+
});
|
|
42
|
+
child.on("close", (code) => {
|
|
43
|
+
if (settled)
|
|
44
|
+
return;
|
|
45
|
+
settled = true;
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
resolveFn({
|
|
48
|
+
stdout,
|
|
49
|
+
stderr,
|
|
50
|
+
exitCode: typeof code === "number" ? code : timedOut ? 124 : 1,
|
|
51
|
+
timedOut,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Default deps: real spawn + fs artifact writer (mkdir -p on demand). */
|
|
57
|
+
export function createDefaultDeps(bin) {
|
|
58
|
+
return {
|
|
59
|
+
spawn: createDefaultSpawn(bin),
|
|
60
|
+
now: () => Date.now(),
|
|
61
|
+
writeArtifact: (path, data) => {
|
|
62
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
63
|
+
writeFileSync(path, data, "utf8");
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Convenience entry for the CLI: run the council with the real default deps. */
|
|
68
|
+
export async function runCouncilWithDefaults(spec, options = {}) {
|
|
69
|
+
const { bin, ...rest } = options;
|
|
70
|
+
return runCouncil(spec, createDefaultDeps(bin), rest);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/council/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CouncilMemberOutput, CouncilMemberResult, CouncilMemberSpec, CouncilSynthOutput, CouncilTaskSpec, ResolvedCouncilConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a concise member prompt. Role is injected via {{COUNCIL_ROLE}}.
|
|
4
|
+
* Members are told to wrap their JSON in sentinels so the engine can extract it
|
|
5
|
+
* reliably from fence/banner noise (verified necessary in the Task 0 spike).
|
|
6
|
+
* NOTE: sentinels are NOT a security boundary — a hostile --context could embed
|
|
7
|
+
* the markers; the engine schema-validates extracted blocks to mitigate.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildMemberPrompt(spec: CouncilTaskSpec, member: CouncilMemberSpec): string;
|
|
10
|
+
/** Build the low-context synthesizer prompt from surviving members. */
|
|
11
|
+
export declare function buildSynthPrompt(config: ResolvedCouncilConfig, spec: CouncilTaskSpec, survivors: CouncilMemberResult[]): string;
|
|
12
|
+
/**
|
|
13
|
+
* Enumerate top-level balanced {...} blocks in text (brace-depth scan).
|
|
14
|
+
* Returns the raw substrings in order of appearance.
|
|
15
|
+
*/
|
|
16
|
+
export declare function balancedBlocks(text: string): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Extract candidate JSON strings from raw stdout. Sentinel-wrapped content is
|
|
19
|
+
* preferred (primary contract); otherwise fall back to all balanced {...} blocks.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractJsonCandidates(stdout: string): string[];
|
|
22
|
+
export declare function isValidMemberOutput(value: unknown): value is CouncilMemberOutput;
|
|
23
|
+
export declare function isValidSynthOutput(value: unknown): value is CouncilSynthOutput;
|
|
24
|
+
/**
|
|
25
|
+
* Parse a member's stdout into a validated CouncilMemberOutput.
|
|
26
|
+
* Strategy: enumerate candidate blocks, JSON.parse each, return the FIRST that
|
|
27
|
+
* passes schema validation. Tie-break = schema validity, not size/position.
|
|
28
|
+
* Returns null when no candidate validates (caller marks "unparseable").
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseMemberOutput(stdout: string): CouncilMemberOutput | null;
|
|
31
|
+
/** Parse the synthesizer's stdout into a validated CouncilSynthOutput. */
|
|
32
|
+
export declare function parseSynthOutput(stdout: string): CouncilSynthOutput | null;
|