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.
Files changed (80) hide show
  1. package/LICENSE.md +24 -0
  2. package/README.md +170 -0
  3. package/build/cli/history.d.ts +2 -0
  4. package/build/cli/history.js +179 -0
  5. package/build/cli/history.js.map +1 -0
  6. package/build/config.d.ts +38 -0
  7. package/build/config.js +118 -0
  8. package/build/config.js.map +1 -0
  9. package/build/container.d.ts +32 -0
  10. package/build/container.js +102 -0
  11. package/build/container.js.map +1 -0
  12. package/build/db/schema.d.ts +1 -0
  13. package/build/db/schema.js +67 -0
  14. package/build/db/schema.js.map +1 -0
  15. package/build/index.d.ts +2 -0
  16. package/build/index.js +140 -0
  17. package/build/index.js.map +1 -0
  18. package/build/konsilio.json +25 -0
  19. package/build/konsilio.schema.json +140 -0
  20. package/build/logger.d.ts +84 -0
  21. package/build/logger.js +121 -0
  22. package/build/logger.js.map +1 -0
  23. package/build/personas/expert.d.ts +33 -0
  24. package/build/personas/expert.js +46 -0
  25. package/build/personas/expert.js.map +1 -0
  26. package/build/personas/index.d.ts +9 -0
  27. package/build/personas/index.js +11 -0
  28. package/build/personas/index.js.map +1 -0
  29. package/build/personas/lead.d.ts +33 -0
  30. package/build/personas/lead.js +51 -0
  31. package/build/personas/lead.js.map +1 -0
  32. package/build/personas/schemas.d.ts +313 -0
  33. package/build/personas/schemas.js +97 -0
  34. package/build/personas/schemas.js.map +1 -0
  35. package/build/prompts/consolidation/critique.md +59 -0
  36. package/build/prompts/consolidation/decision.md +51 -0
  37. package/build/prompts/consolidation/extraction.md +46 -0
  38. package/build/prompts/consolidation/synthesis.md +42 -0
  39. package/build/prompts/expert-rules.md +50 -0
  40. package/build/prompts/personas/dev-tooling.md +27 -0
  41. package/build/prompts/personas/devops.md +26 -0
  42. package/build/prompts/personas/distributed-systems.md +28 -0
  43. package/build/prompts/personas/graph-dba.md +27 -0
  44. package/build/prompts/personas/node-fullstack.md +27 -0
  45. package/build/prompts/personas/performance.md +26 -0
  46. package/build/prompts/personas/security.md +26 -0
  47. package/build/prompts/personas/test-architect.md +29 -0
  48. package/build/prompts/personas/test-quoted.md +14 -0
  49. package/build/prompts/personas/typescript.md +26 -0
  50. package/build/prompts/personas/ux-dx.md +29 -0
  51. package/build/prompts/workflow-rules.md +3 -0
  52. package/build/server.d.ts +24 -0
  53. package/build/server.js +181 -0
  54. package/build/server.js.map +1 -0
  55. package/build/services/cache.service.d.ts +49 -0
  56. package/build/services/cache.service.js +85 -0
  57. package/build/services/cache.service.js.map +1 -0
  58. package/build/services/council.service.d.ts +111 -0
  59. package/build/services/council.service.js +361 -0
  60. package/build/services/council.service.js.map +1 -0
  61. package/build/services/database.service.d.ts +70 -0
  62. package/build/services/database.service.js +221 -0
  63. package/build/services/database.service.js.map +1 -0
  64. package/build/services/formatter.service.d.ts +52 -0
  65. package/build/services/formatter.service.js +133 -0
  66. package/build/services/formatter.service.js.map +1 -0
  67. package/build/services/index.d.ts +18 -0
  68. package/build/services/index.js +13 -0
  69. package/build/services/index.js.map +1 -0
  70. package/build/services/openrouter.service.d.ts +58 -0
  71. package/build/services/openrouter.service.js +128 -0
  72. package/build/services/openrouter.service.js.map +1 -0
  73. package/build/services/persona.service.d.ts +43 -0
  74. package/build/services/persona.service.js +93 -0
  75. package/build/services/persona.service.js.map +1 -0
  76. package/build/services/prompt.service.d.ts +59 -0
  77. package/build/services/prompt.service.js +209 -0
  78. package/build/services/prompt.service.js.map +1 -0
  79. package/konsilio.schema.json +140 -0
  80. 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>;
@@ -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":";AACA;;;;;;;;GAQG;AAEH,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACjD,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,SAAS;QAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAClD,CAAC;AACH,CAAC;AAED,OAAO,EAAE,YAAY,EAAmC,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAqB,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAoB,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAerD,sBAAsB;AAEtB,MAAM,UAAU,gBAAgB,CAAC,QAAqB,EAAE,MAAc;IACpE,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;QAC9E,MAAM,aAAa,GAAG,UAAU,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAEpD,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAEnE,sBAAsB;QACtB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,GAAG,CAAC,SAAS,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QAEjD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAE1C,IAAI,CAAC;YACH,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC9C,MAAM,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACvD,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzF,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yBAAyB;AAEzB,KAAK,UAAU,YAAY,CACzB,IAAqB,EACrB,GAAmB,EACnB,QAAqB,EACrB,GAAc;IAEd,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAElC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7C,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC;QACvD,QAAQ,CAAC,iBAAiB,CAAC,WAAW,EAAE;KACzC,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC;IAElF,MAAM,MAAM,GAAsB;QAChC,QAAQ,EAAE,QAAQ;QAClB,UAAU,EAAE,QAAQ;KACrB,CAAC;IAEF,IAAI,UAAU,EAAE,CAAC;QACf,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9G,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9G,CAAC;IAED,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;QACrB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;QAC5C,MAAM,EAAE,MAAM;KACf,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACf,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,GAAoB,EACpB,GAAmB,EACnB,QAAqB,EACrB,GAAc;IAEd,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,MAAkF,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/B,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,GAAG,CAC9C;YACE,SAAS,EAAE,MAAM,CAAC,UAAU;YAC5B,SAAS,EAAE,MAAM,CAAC,UAAU;YAC5B,kBAAkB,EAAE,MAAM,CAAC,mBAAmB;SAC/C,CACF,CAAC;QAEF,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACrB,UAAU,EAAE,MAAM,CAAC,SAAS;YAC5B,cAAc,EAAE,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC7C,UAAU,EAAE,CAAC,CAAC,SAAS;gBACvB,YAAY,EAAE,CAAC,CAAC,WAAW;gBAC3B,aAAa,EAAE,CAAC,CAAC,YAAY;gBAC7B,iBAAiB,EAAE,CAAC,CAAC,gBAAgB;gBACrC,WAAW,EAAE,CAAC,CAAC,UAAU;gBACzB,UAAU,EAAE,CAAC,CAAC,SAAS;aACxB,CAAC,CAAC;YACH,iBAAiB,EAAE,MAAM,CAAC,gBAAgB;YAC1C,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,gBAAgB,EAAE,MAAM,CAAC,eAAe;YACxC,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,mBAAmB,EAAE,MAAM,CAAC,kBAAkB;YAC9C,iBAAiB,EAAE,MAAM,CAAC,eAAe;SAC1C,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACf,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACjD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,GAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2BAA2B;AAE3B,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,yBAAyB;IACzB,cAAc,EAAE,CAAC;IAEjB,gBAAgB;IAChB,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC;IAErC,qBAAqB;IACrB,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtC,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACvC,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2BAA2B;AAC3B,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -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
+ }