konsilio 0.3.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/LICENSE.md +24 -0
- package/README.md +170 -0
- package/build/cli/history.d.ts +2 -0
- package/build/cli/history.js +179 -0
- package/build/cli/history.js.map +1 -0
- package/build/config.d.ts +38 -0
- package/build/config.js +118 -0
- package/build/config.js.map +1 -0
- package/build/container.d.ts +32 -0
- package/build/container.js +102 -0
- package/build/container.js.map +1 -0
- package/build/db/schema.d.ts +1 -0
- package/build/db/schema.js +67 -0
- package/build/db/schema.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +140 -0
- package/build/index.js.map +1 -0
- package/build/konsilio.json +25 -0
- package/build/konsilio.schema.json +140 -0
- package/build/logger.d.ts +84 -0
- package/build/logger.js +121 -0
- package/build/logger.js.map +1 -0
- package/build/personas/expert.d.ts +33 -0
- package/build/personas/expert.js +46 -0
- package/build/personas/expert.js.map +1 -0
- package/build/personas/index.d.ts +9 -0
- package/build/personas/index.js +11 -0
- package/build/personas/index.js.map +1 -0
- package/build/personas/lead.d.ts +33 -0
- package/build/personas/lead.js +51 -0
- package/build/personas/lead.js.map +1 -0
- package/build/personas/schemas.d.ts +313 -0
- package/build/personas/schemas.js +97 -0
- package/build/personas/schemas.js.map +1 -0
- package/build/prompts/consolidation/critique.md +59 -0
- package/build/prompts/consolidation/decision.md +51 -0
- package/build/prompts/consolidation/extraction.md +46 -0
- package/build/prompts/consolidation/synthesis.md +42 -0
- package/build/prompts/expert-rules.md +50 -0
- package/build/prompts/personas/dev-tooling.md +27 -0
- package/build/prompts/personas/devops.md +26 -0
- package/build/prompts/personas/distributed-systems.md +28 -0
- package/build/prompts/personas/graph-dba.md +27 -0
- package/build/prompts/personas/node-fullstack.md +27 -0
- package/build/prompts/personas/performance.md +26 -0
- package/build/prompts/personas/security.md +26 -0
- package/build/prompts/personas/test-architect.md +29 -0
- package/build/prompts/personas/test-quoted.md +14 -0
- package/build/prompts/personas/typescript.md +26 -0
- package/build/prompts/personas/ux-dx.md +29 -0
- package/build/prompts/workflow-rules.md +3 -0
- package/build/server.d.ts +24 -0
- package/build/server.js +181 -0
- package/build/server.js.map +1 -0
- package/build/services/cache.service.d.ts +49 -0
- package/build/services/cache.service.js +85 -0
- package/build/services/cache.service.js.map +1 -0
- package/build/services/council.service.d.ts +111 -0
- package/build/services/council.service.js +361 -0
- package/build/services/council.service.js.map +1 -0
- package/build/services/database.service.d.ts +70 -0
- package/build/services/database.service.js +221 -0
- package/build/services/database.service.js.map +1 -0
- package/build/services/formatter.service.d.ts +52 -0
- package/build/services/formatter.service.js +133 -0
- package/build/services/formatter.service.js.map +1 -0
- package/build/services/index.d.ts +18 -0
- package/build/services/index.js +13 -0
- package/build/services/index.js.map +1 -0
- package/build/services/openrouter.service.d.ts +58 -0
- package/build/services/openrouter.service.js +128 -0
- package/build/services/openrouter.service.js.map +1 -0
- package/build/services/persona.service.d.ts +43 -0
- package/build/services/persona.service.js +93 -0
- package/build/services/persona.service.js.map +1 -0
- package/build/services/prompt.service.d.ts +59 -0
- package/build/services/prompt.service.js +209 -0
- package/build/services/prompt.service.js.map +1 -0
- package/konsilio.schema.json +140 -0
- package/package.json +68 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* HTTP Server with Dependency Injection
|
|
4
|
+
*
|
|
5
|
+
* Provides:
|
|
6
|
+
* - /health - Health check for SQLite and OpenRouter connectivity
|
|
7
|
+
* - /analyze - Council analysis endpoint
|
|
8
|
+
*
|
|
9
|
+
* Uses correlation IDs for request tracing and structured JSON logging.
|
|
10
|
+
*/
|
|
11
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
12
|
+
import { Logger } from './logger.js';
|
|
13
|
+
import { type AppServices } from './container.js';
|
|
14
|
+
export interface ComponentHealth {
|
|
15
|
+
status: 'healthy' | 'unhealthy';
|
|
16
|
+
latencyMs: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface HealthCheckResult {
|
|
20
|
+
database: ComponentHealth;
|
|
21
|
+
openrouter: ComponentHealth;
|
|
22
|
+
}
|
|
23
|
+
export declare function createHttpServer(services: AppServices, logger: Logger): import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
24
|
+
export declare function main(): Promise<void>;
|
package/build/server.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* HTTP Server with Dependency Injection
|
|
4
|
+
*
|
|
5
|
+
* Provides:
|
|
6
|
+
* - /health - Health check for SQLite and OpenRouter connectivity
|
|
7
|
+
* - /analyze - Council analysis endpoint
|
|
8
|
+
*
|
|
9
|
+
* Uses correlation IDs for request tracing and structured JSON logging.
|
|
10
|
+
*/
|
|
11
|
+
// ─── Load .env for local development ─────────────────────────────────────────
|
|
12
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
13
|
+
import { resolve, dirname } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const envPath = resolve(__dirname, '..', '.env');
|
|
17
|
+
if (existsSync(envPath)) {
|
|
18
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
19
|
+
for (const line of content.split('\n')) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
22
|
+
continue;
|
|
23
|
+
const eqIdx = trimmed.indexOf('=');
|
|
24
|
+
if (eqIdx === -1)
|
|
25
|
+
continue;
|
|
26
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
27
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
28
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
29
|
+
value = value.slice(1, -1);
|
|
30
|
+
}
|
|
31
|
+
if (!process.env[key])
|
|
32
|
+
process.env[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
import { createServer } from 'node:http';
|
|
36
|
+
import { randomUUID } from 'node:crypto';
|
|
37
|
+
import { createLogger } from './logger.js';
|
|
38
|
+
import { getServices } from './container.js';
|
|
39
|
+
import { config, validateConfig } from './config.js';
|
|
40
|
+
// ─── HTTP Server ───
|
|
41
|
+
export function createHttpServer(services, logger) {
|
|
42
|
+
const server = createServer(async (req, res) => {
|
|
43
|
+
const correlationId = randomUUID();
|
|
44
|
+
const log = logger.withCorrelationId(correlationId);
|
|
45
|
+
log.info('Request received', { method: req.method, url: req.url });
|
|
46
|
+
// Set default headers
|
|
47
|
+
res.setHeader('Content-Type', 'application/json');
|
|
48
|
+
res.setHeader('X-Correlation-Id', correlationId);
|
|
49
|
+
const url = req.url?.split('?')[0] ?? '/';
|
|
50
|
+
try {
|
|
51
|
+
if (url === '/health' && req.method === 'GET') {
|
|
52
|
+
await handleHealth(req, res, services, log);
|
|
53
|
+
}
|
|
54
|
+
else if (url === '/analyze' && req.method === 'POST') {
|
|
55
|
+
await handleAnalyze(req, res, services, log);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
res.statusCode = 404;
|
|
59
|
+
res.end(JSON.stringify({ error: 'Not Found', correlationId }));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
log.error('Request failed', { error: err instanceof Error ? err.message : String(err) });
|
|
64
|
+
res.statusCode = 500;
|
|
65
|
+
res.end(JSON.stringify({ error: 'Internal Server Error', correlationId }));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return server;
|
|
69
|
+
}
|
|
70
|
+
// ─── Route Handlers ───
|
|
71
|
+
async function handleHealth(_req, res, services, log) {
|
|
72
|
+
log.debug('Health check started');
|
|
73
|
+
const [dbHealth, orHealth] = await Promise.all([
|
|
74
|
+
Promise.resolve(services.databaseService.checkHealth()),
|
|
75
|
+
services.openRouterService.checkHealth(),
|
|
76
|
+
]);
|
|
77
|
+
const allHealthy = dbHealth.status === 'healthy' && orHealth.status === 'healthy';
|
|
78
|
+
const result = {
|
|
79
|
+
database: dbHealth,
|
|
80
|
+
openrouter: orHealth,
|
|
81
|
+
};
|
|
82
|
+
if (allHealthy) {
|
|
83
|
+
res.statusCode = 200;
|
|
84
|
+
log.info('Health check passed', { database: result.database.status, openrouter: result.openrouter.status });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
res.statusCode = 503;
|
|
88
|
+
log.warn('Health check failed', { database: result.database.status, openrouter: result.openrouter.status });
|
|
89
|
+
}
|
|
90
|
+
res.end(JSON.stringify({
|
|
91
|
+
status: allHealthy ? 'healthy' : 'unhealthy',
|
|
92
|
+
checks: result,
|
|
93
|
+
}, null, 2));
|
|
94
|
+
}
|
|
95
|
+
async function handleAnalyze(req, res, services, log) {
|
|
96
|
+
const body = await readBody(req);
|
|
97
|
+
let parsed;
|
|
98
|
+
try {
|
|
99
|
+
parsed = JSON.parse(body);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
res.statusCode = 400;
|
|
103
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!parsed.draft_plan?.trim()) {
|
|
107
|
+
res.statusCode = 400;
|
|
108
|
+
res.end(JSON.stringify({ error: 'draft_plan is required and cannot be empty' }));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const result = await services.councilService.run({
|
|
113
|
+
draftPlan: parsed.draft_plan,
|
|
114
|
+
techStack: parsed.tech_stack,
|
|
115
|
+
contextConstraints: parsed.context_constraints,
|
|
116
|
+
});
|
|
117
|
+
res.statusCode = 200;
|
|
118
|
+
res.end(JSON.stringify({
|
|
119
|
+
session_id: result.sessionId,
|
|
120
|
+
expert_reports: result.expertReports.map(r => ({
|
|
121
|
+
persona_id: r.personaId,
|
|
122
|
+
persona_name: r.personaName,
|
|
123
|
+
persona_emoji: r.personaEmoji,
|
|
124
|
+
structured_output: r.structuredOutput,
|
|
125
|
+
duration_ms: r.durationMs,
|
|
126
|
+
model_used: r.modelUsed,
|
|
127
|
+
})),
|
|
128
|
+
extraction_output: result.extractionOutput,
|
|
129
|
+
critique_output: result.critiqueOutput,
|
|
130
|
+
decision_output: result.decisionOutput,
|
|
131
|
+
synthesis_output: result.synthesisOutput,
|
|
132
|
+
final_blueprint: result.finalBlueprint,
|
|
133
|
+
consolidation_model: result.consolidationModel,
|
|
134
|
+
total_duration_ms: result.totalDurationMs,
|
|
135
|
+
}, null, 2));
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
139
|
+
log.error('Analysis failed', { error: message });
|
|
140
|
+
res.statusCode = 500;
|
|
141
|
+
res.end(JSON.stringify({ error: message }));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function readBody(req) {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
let body = '';
|
|
147
|
+
req.on('data', chunk => { body += chunk; });
|
|
148
|
+
req.on('end', () => resolve(body));
|
|
149
|
+
req.on('error', reject);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// ─── Main Entry Point ───
|
|
153
|
+
export async function main() {
|
|
154
|
+
// Validate configuration
|
|
155
|
+
validateConfig();
|
|
156
|
+
// Create logger
|
|
157
|
+
const logger = createLogger(config.logLevel);
|
|
158
|
+
// Get services from container (async due to WASM loading)
|
|
159
|
+
const services = await getServices();
|
|
160
|
+
// Create HTTP server
|
|
161
|
+
const server = createHttpServer(services, logger);
|
|
162
|
+
server.listen(config.port, () => {
|
|
163
|
+
logger.info('Server started', { port: config.port, nodeEnv: config.nodeEnv });
|
|
164
|
+
});
|
|
165
|
+
// Graceful shutdown
|
|
166
|
+
process.on('SIGINT', () => {
|
|
167
|
+
logger.info('Shutting down (SIGINT)');
|
|
168
|
+
services.databaseService.close();
|
|
169
|
+
process.exit(0);
|
|
170
|
+
});
|
|
171
|
+
process.on('SIGTERM', () => {
|
|
172
|
+
logger.info('Shutting down (SIGTERM)');
|
|
173
|
+
services.databaseService.close();
|
|
174
|
+
process.exit(0);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// Run if executed directly
|
|
178
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
179
|
+
main();
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Service
|
|
3
|
+
*
|
|
4
|
+
* Simple in-memory cache with TTL support.
|
|
5
|
+
* Designed for dependency injection to enable testing.
|
|
6
|
+
*/
|
|
7
|
+
export interface CacheEntry<T> {
|
|
8
|
+
value: T;
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* CacheService - In-memory TTL cache
|
|
13
|
+
*/
|
|
14
|
+
export declare class CacheService {
|
|
15
|
+
private cache;
|
|
16
|
+
private readonly defaultTtlMs;
|
|
17
|
+
constructor(defaultTtlMs?: number);
|
|
18
|
+
/**
|
|
19
|
+
* Get a value from cache if it exists and hasn't expired
|
|
20
|
+
*/
|
|
21
|
+
get<T>(key: string): T | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Set a value in cache with optional TTL
|
|
24
|
+
*/
|
|
25
|
+
set<T>(key: string, value: T, ttlMs?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Delete a value from cache
|
|
28
|
+
*/
|
|
29
|
+
delete(key: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a key exists and hasn't expired
|
|
32
|
+
*/
|
|
33
|
+
has(key: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Clear all expired entries
|
|
36
|
+
*/
|
|
37
|
+
prune(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Clear all entries
|
|
40
|
+
*/
|
|
41
|
+
clear(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Get cache statistics
|
|
44
|
+
*/
|
|
45
|
+
stats(): {
|
|
46
|
+
size: number;
|
|
47
|
+
keys: string[];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Service
|
|
3
|
+
*
|
|
4
|
+
* Simple in-memory cache with TTL support.
|
|
5
|
+
* Designed for dependency injection to enable testing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* CacheService - In-memory TTL cache
|
|
9
|
+
*/
|
|
10
|
+
export class CacheService {
|
|
11
|
+
cache = new Map();
|
|
12
|
+
defaultTtlMs;
|
|
13
|
+
constructor(defaultTtlMs = 60_000) {
|
|
14
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get a value from cache if it exists and hasn't expired
|
|
18
|
+
*/
|
|
19
|
+
get(key) {
|
|
20
|
+
const entry = this.cache.get(key);
|
|
21
|
+
if (!entry)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (Date.now() > entry.expiresAt) {
|
|
24
|
+
this.cache.delete(key);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return entry.value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set a value in cache with optional TTL
|
|
31
|
+
*/
|
|
32
|
+
set(key, value, ttlMs) {
|
|
33
|
+
const expiresAt = Date.now() + (ttlMs ?? this.defaultTtlMs);
|
|
34
|
+
this.cache.set(key, { value, expiresAt });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Delete a value from cache
|
|
38
|
+
*/
|
|
39
|
+
delete(key) {
|
|
40
|
+
return this.cache.delete(key);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a key exists and hasn't expired
|
|
44
|
+
*/
|
|
45
|
+
has(key) {
|
|
46
|
+
const entry = this.cache.get(key);
|
|
47
|
+
if (!entry)
|
|
48
|
+
return false;
|
|
49
|
+
if (Date.now() > entry.expiresAt) {
|
|
50
|
+
this.cache.delete(key);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Clear all expired entries
|
|
57
|
+
*/
|
|
58
|
+
prune() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
let pruned = 0;
|
|
61
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
62
|
+
if (now > entry.expiresAt) {
|
|
63
|
+
this.cache.delete(key);
|
|
64
|
+
pruned++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return pruned;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Clear all entries
|
|
71
|
+
*/
|
|
72
|
+
clear() {
|
|
73
|
+
this.cache.clear();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get cache statistics
|
|
77
|
+
*/
|
|
78
|
+
stats() {
|
|
79
|
+
return {
|
|
80
|
+
size: this.cache.size,
|
|
81
|
+
keys: Array.from(this.cache.keys()),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=cache.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.service.js","sourceRoot":"","sources":["../../src/services/cache.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,MAAM,OAAO,YAAY;IACf,KAAK,GAAqC,IAAI,GAAG,EAAE,CAAC;IAC3C,YAAY,CAAS;IAEtC,YAAY,eAAuB,MAAM;QACvC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,GAAG,CAAI,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,CAAC,KAAU,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAAc;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QAEzB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvB,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACrB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SACpC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Council Service - Two-Stage Consulting Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Stage 1: Expert Analysis (parallel, prose output)
|
|
5
|
+
* Stage 2: Formatting (convert prose to structured JSON via FormatterService)
|
|
6
|
+
* Stage 3: Extraction (extract claims from expert reports)
|
|
7
|
+
* Stage 4: Critique (identify contradictions and weaknesses)
|
|
8
|
+
* Stage 5: Decision (accept/reject findings)
|
|
9
|
+
* Stage 6: Synthesis (assemble final blueprint)
|
|
10
|
+
*/
|
|
11
|
+
import type { Logger } from '../logger.js';
|
|
12
|
+
import type { CouncilResult } from '../personas/schemas.js';
|
|
13
|
+
import type { OpenRouterService } from './openrouter.service.js';
|
|
14
|
+
import type { DatabaseService } from './database.service.js';
|
|
15
|
+
import type { CacheService } from './cache.service.js';
|
|
16
|
+
import type { PromptService } from './prompt.service.js';
|
|
17
|
+
import type { PersonaService } from './persona.service.js';
|
|
18
|
+
import type { FormatterService } from './formatter.service.js';
|
|
19
|
+
export interface CouncilConfig {
|
|
20
|
+
enabledPersonaIds: string[];
|
|
21
|
+
models: {
|
|
22
|
+
experts: string;
|
|
23
|
+
lead: string;
|
|
24
|
+
formatter: string;
|
|
25
|
+
};
|
|
26
|
+
personaModels: Record<string, string>;
|
|
27
|
+
timeouts: {
|
|
28
|
+
expertMs: number;
|
|
29
|
+
leadMs: number;
|
|
30
|
+
formatterMs: number;
|
|
31
|
+
};
|
|
32
|
+
maxTokens: {
|
|
33
|
+
experts: number;
|
|
34
|
+
lead: number;
|
|
35
|
+
};
|
|
36
|
+
maxDraftPlanLength: number;
|
|
37
|
+
formatterMaxRetries: number;
|
|
38
|
+
}
|
|
39
|
+
export interface CouncilParams {
|
|
40
|
+
draftPlan: string;
|
|
41
|
+
techStack?: string;
|
|
42
|
+
contextConstraints?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface CouncilOptions {
|
|
45
|
+
personaOverride?: string[];
|
|
46
|
+
personaModelsOverride?: Record<string, string>;
|
|
47
|
+
consolidationModelOverride?: string;
|
|
48
|
+
maxTokensOverride?: {
|
|
49
|
+
experts?: number;
|
|
50
|
+
lead?: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* CouncilService - Orchestrates two-stage expert analysis and consolidation
|
|
55
|
+
*/
|
|
56
|
+
export declare class CouncilService {
|
|
57
|
+
private readonly deps;
|
|
58
|
+
constructor(deps: {
|
|
59
|
+
logger: Logger;
|
|
60
|
+
openRouterService: OpenRouterService;
|
|
61
|
+
databaseService: DatabaseService;
|
|
62
|
+
cacheService: CacheService;
|
|
63
|
+
promptService: PromptService;
|
|
64
|
+
personaService: PersonaService;
|
|
65
|
+
formatterService: FormatterService;
|
|
66
|
+
config: CouncilConfig;
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the model for a specific persona using 3-tier resolution:
|
|
70
|
+
* 1. Per-run override (personaModelsOverride)
|
|
71
|
+
* 2. Persona-level default (config.personaModels)
|
|
72
|
+
* 3. Global default (config.models.experts)
|
|
73
|
+
*/
|
|
74
|
+
private resolvePersonaModel;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the effective token limit for a role
|
|
77
|
+
*/
|
|
78
|
+
private resolveMaxTokens;
|
|
79
|
+
/**
|
|
80
|
+
* Run a council analysis with two-stage consolidation
|
|
81
|
+
*/
|
|
82
|
+
run(params: CouncilParams, options?: CouncilOptions, correlationId?: string): Promise<CouncilResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Call an expert persona for prose analysis
|
|
85
|
+
*/
|
|
86
|
+
private callExpert;
|
|
87
|
+
/**
|
|
88
|
+
* Stage 3: Extraction - Extract structured claims from expert reports
|
|
89
|
+
*/
|
|
90
|
+
private runExtractionPhase;
|
|
91
|
+
/**
|
|
92
|
+
* Stage 4: Critique - Analyze for contradictions and weaknesses
|
|
93
|
+
*/
|
|
94
|
+
private runCritiquePhase;
|
|
95
|
+
/**
|
|
96
|
+
* Stage 5: Decision - Accept/reject findings explicitly
|
|
97
|
+
*/
|
|
98
|
+
private runDecisionPhase;
|
|
99
|
+
/**
|
|
100
|
+
* Stage 6: Synthesis - Assemble final blueprint from accepted findings
|
|
101
|
+
*/
|
|
102
|
+
private runSynthesisPhase;
|
|
103
|
+
/**
|
|
104
|
+
* Build user message for expert analysis
|
|
105
|
+
*/
|
|
106
|
+
private buildUserMessage;
|
|
107
|
+
/**
|
|
108
|
+
* Clean JSON output from LLM responses (strip markdown code fences)
|
|
109
|
+
*/
|
|
110
|
+
private cleanJsonOutput;
|
|
111
|
+
}
|