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,102 @@
1
+ /**
2
+ * Dependency Injection Container
3
+ *
4
+ * Composition root that wires up all services with their dependencies.
5
+ * This is the single place where all dependencies are created and connected.
6
+ */
7
+ import { logger } from './logger.js';
8
+ import { config, validateConfig } from './config.js';
9
+ import { SCHEMA } from './db/schema.js';
10
+ import { OpenRouterService } from './services/openrouter.service.js';
11
+ import { DatabaseService } from './services/database.service.js';
12
+ import { CacheService } from './services/cache.service.js';
13
+ import { PromptService } from './services/prompt.service.js';
14
+ import { PersonaService } from './services/persona.service.js';
15
+ import { CouncilService } from './services/council.service.js';
16
+ import { FormatterService } from './services/formatter.service.js';
17
+ /**
18
+ * Create and wire up all application services (async)
19
+ */
20
+ export async function createServices() {
21
+ // Validate configuration before creating services
22
+ validateConfig();
23
+ // Create OpenRouter service
24
+ const openRouterService = new OpenRouterService({
25
+ apiKey: config.openrouterApiKey,
26
+ baseUrl: config.openrouterBaseUrl,
27
+ }, logger);
28
+ // Create Database service (async factory due to WASM loading)
29
+ const databaseService = await DatabaseService.create({
30
+ dbPath: config.databasePath,
31
+ maxHistorySessions: config.maxHistorySessions,
32
+ }, logger, SCHEMA);
33
+ // Create Cache service
34
+ const cacheService = new CacheService(config.cacheTtlSeconds * 1000);
35
+ // Create Prompt service
36
+ const promptService = new PromptService(undefined, logger);
37
+ // Create Persona service
38
+ const personaService = new PersonaService({
39
+ promptService,
40
+ cacheService,
41
+ logger,
42
+ });
43
+ // Create Council service with all dependencies
44
+ const councilConfig = {
45
+ enabledPersonaIds: config.enabledPersonas,
46
+ models: config.models,
47
+ personaModels: config.personaModels,
48
+ timeouts: config.timeouts,
49
+ maxTokens: config.maxTokens,
50
+ maxDraftPlanLength: config.maxDraftPlanLength,
51
+ formatterMaxRetries: config.formatterMaxRetries,
52
+ };
53
+ // Create Formatter service
54
+ const formatterConfig = {
55
+ model: config.models.formatter,
56
+ timeoutMs: config.timeouts.formatterMs,
57
+ maxRetries: config.formatterMaxRetries,
58
+ };
59
+ const formatterService = new FormatterService({
60
+ openRouterService,
61
+ logger,
62
+ config: formatterConfig,
63
+ });
64
+ const councilService = new CouncilService({
65
+ logger,
66
+ openRouterService,
67
+ databaseService,
68
+ cacheService,
69
+ promptService,
70
+ personaService,
71
+ formatterService,
72
+ config: councilConfig,
73
+ });
74
+ return {
75
+ openRouterService,
76
+ databaseService,
77
+ cacheService,
78
+ promptService,
79
+ personaService,
80
+ councilService,
81
+ };
82
+ }
83
+ /**
84
+ * Singleton services instance (lazy initialized)
85
+ */
86
+ let _services = null;
87
+ /**
88
+ * Get the singleton services instance (returns a Promise)
89
+ */
90
+ export function getServices() {
91
+ if (!_services) {
92
+ _services = createServices();
93
+ }
94
+ return _services;
95
+ }
96
+ /**
97
+ * Reset services (useful for testing)
98
+ */
99
+ export function resetServices() {
100
+ _services = null;
101
+ }
102
+ //# sourceMappingURL=container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const SCHEMA = "\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n draft_plan_summary TEXT,\n tech_stack TEXT,\n constraints TEXT\n);\n\nCREATE TABLE IF NOT EXISTS messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n role TEXT NOT NULL CHECK(role IN ('user', 'assistant')),\n persona_id TEXT,\n content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS expert_findings (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n persona_id TEXT NOT NULL,\n persona_name TEXT NOT NULL,\n persona_emoji TEXT,\n severity TEXT NOT NULL CHECK(severity IN ('CRITICAL', 'HIGH', 'MEDIUM', 'LOW')),\n component TEXT NOT NULL,\n issue TEXT NOT NULL,\n mitigation TEXT NOT NULL,\n accepted BOOLEAN DEFAULT 0,\n rejection_reason TEXT,\n duration_ms INTEGER,\n model_used TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS expert_risks (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n persona_id TEXT NOT NULL,\n category TEXT NOT NULL,\n probability TEXT NOT NULL,\n impact TEXT NOT NULL,\n description TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS consolidation_phases (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n phase_name TEXT NOT NULL CHECK(phase_name IN ('extraction', 'critique', 'decision', 'synthesis')),\n phase_output TEXT NOT NULL,\n duration_ms INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);\nCREATE INDEX IF NOT EXISTS idx_findings_session ON expert_findings(session_id);\nCREATE INDEX IF NOT EXISTS idx_findings_accepted ON expert_findings(accepted);\nCREATE INDEX IF NOT EXISTS idx_risks_session ON expert_risks(session_id);\nCREATE INDEX IF NOT EXISTS idx_phases_session ON consolidation_phases(session_id);\nCREATE INDEX IF NOT EXISTS idx_sessions_created ON sessions(created_at DESC);\n";
@@ -0,0 +1,67 @@
1
+ export const SCHEMA = `
2
+ CREATE TABLE IF NOT EXISTS sessions (
3
+ id TEXT PRIMARY KEY,
4
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
5
+ draft_plan_summary TEXT,
6
+ tech_stack TEXT,
7
+ constraints TEXT
8
+ );
9
+
10
+ CREATE TABLE IF NOT EXISTS messages (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ session_id TEXT NOT NULL,
13
+ role TEXT NOT NULL CHECK(role IN ('user', 'assistant')),
14
+ persona_id TEXT,
15
+ content TEXT NOT NULL,
16
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
17
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
18
+ );
19
+
20
+ CREATE TABLE IF NOT EXISTS expert_findings (
21
+ id TEXT PRIMARY KEY,
22
+ session_id TEXT NOT NULL,
23
+ persona_id TEXT NOT NULL,
24
+ persona_name TEXT NOT NULL,
25
+ persona_emoji TEXT,
26
+ severity TEXT NOT NULL CHECK(severity IN ('CRITICAL', 'HIGH', 'MEDIUM', 'LOW')),
27
+ component TEXT NOT NULL,
28
+ issue TEXT NOT NULL,
29
+ mitigation TEXT NOT NULL,
30
+ accepted BOOLEAN DEFAULT 0,
31
+ rejection_reason TEXT,
32
+ duration_ms INTEGER,
33
+ model_used TEXT,
34
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
35
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
36
+ );
37
+
38
+ CREATE TABLE IF NOT EXISTS expert_risks (
39
+ id TEXT PRIMARY KEY,
40
+ session_id TEXT NOT NULL,
41
+ persona_id TEXT NOT NULL,
42
+ category TEXT NOT NULL,
43
+ probability TEXT NOT NULL,
44
+ impact TEXT NOT NULL,
45
+ description TEXT NOT NULL,
46
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
47
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
48
+ );
49
+
50
+ CREATE TABLE IF NOT EXISTS consolidation_phases (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ session_id TEXT NOT NULL,
53
+ phase_name TEXT NOT NULL CHECK(phase_name IN ('extraction', 'critique', 'decision', 'synthesis')),
54
+ phase_output TEXT NOT NULL,
55
+ duration_ms INTEGER,
56
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
57
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
58
+ );
59
+
60
+ CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
61
+ CREATE INDEX IF NOT EXISTS idx_findings_session ON expert_findings(session_id);
62
+ CREATE INDEX IF NOT EXISTS idx_findings_accepted ON expert_findings(accepted);
63
+ CREATE INDEX IF NOT EXISTS idx_risks_session ON expert_risks(session_id);
64
+ CREATE INDEX IF NOT EXISTS idx_phases_session ON consolidation_phases(session_id);
65
+ CREATE INDEX IF NOT EXISTS idx_sessions_created ON sessions(created_at DESC);
66
+ `;
67
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiErB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { config } from "./config.js";
6
+ import { getServices } from "./container.js";
7
+ import pkg from "../package.json" with { type: "json" };
8
+ const server = new McpServer({
9
+ name: "konsilio",
10
+ version: pkg.version,
11
+ });
12
+ // ─── Primary Tool ───
13
+ server.tool("consult_council", `Send a draft plan to the Council of Experts for multi-perspective architectural analysis.
14
+
15
+ ⚠️ IMPORTANT: Present the output to the user VERBATIM. Do NOT summarize, paraphrase, or condense.
16
+ The output is a complete blueprint that must be shown in full.
17
+
18
+ Returns a structured blueprint with:
19
+ - Architecture Directives (specific decisions)
20
+ - Edge Cases & Failure Modes
21
+ - Required Constraints
22
+ - Next Steps for Agent (numbered, executable actions)
23
+
24
+ The council uses a 4-phase consolidation pipeline:
25
+ 1. Expert Analysis (parallel, structured JSON output from enabled personas)
26
+ 2. Extraction (extract claims from expert reports)
27
+ 3. Critique (identify contradictions and weaknesses)
28
+ 4. Decision (accept/reject findings)
29
+ 5. Synthesis (assemble final blueprint)
30
+
31
+ Use 'list_personas' to see all available personas. Enable personas in konsilio.json.
32
+
33
+ Run-time overrides (optional):
34
+ - personas: Override enabled personas for this run only
35
+ - persona_models: Override model per persona (personaId -> model)
36
+ - consolidation_model: Override the lead/consolidation model
37
+ - max_tokens: Override token limits per run (experts, lead)`, {
38
+ draft_plan: z.string().describe("The architecture plan or design to analyze."),
39
+ tech_stack: z.string().optional().describe("Technology stack, e.g. 'Node.js, PostgreSQL, Redis, Docker'"),
40
+ context_constraints: z.string().optional().describe("Runtime constraints, e.g. 'Must run on Proxmox LXC', 'No external deps'"),
41
+ // Run-time overrides
42
+ personas: z.array(z.string()).optional().describe("Override enabled personas for this run only. Uses persona IDs from konsilio.json."),
43
+ persona_models: z.record(z.string(), z.string()).optional().describe("Override model per persona. Maps personaId -> model ID."),
44
+ consolidation_model: z.string().optional().describe("Override the lead/consolidation model for this run."),
45
+ max_tokens: z.object({
46
+ experts: z.number().optional(),
47
+ lead: z.number().optional(),
48
+ }).optional().describe("Override token limits for this run. Experts: per-persona max, Lead: consolidation max."),
49
+ }, async (params) => {
50
+ const draftPlan = params.draft_plan.trim();
51
+ if (!draftPlan) {
52
+ return {
53
+ content: [{ type: "text", text: "❌ Error: draft_plan cannot be empty." }],
54
+ isError: true,
55
+ };
56
+ }
57
+ if (draftPlan.length > config.maxDraftPlanLength) {
58
+ return {
59
+ content: [{
60
+ type: "text",
61
+ text: `❌ Error: draft_plan too long (${draftPlan.length}/${config.maxDraftPlanLength} chars).`,
62
+ }],
63
+ isError: true,
64
+ };
65
+ }
66
+ try {
67
+ const services = await getServices();
68
+ const result = await services.councilService.run({
69
+ draftPlan,
70
+ techStack: params.tech_stack,
71
+ contextConstraints: params.context_constraints,
72
+ }, {
73
+ personaOverride: params.personas,
74
+ personaModelsOverride: params.persona_models,
75
+ consolidationModelOverride: params.consolidation_model,
76
+ maxTokensOverride: params.max_tokens,
77
+ });
78
+ return {
79
+ content: [{ type: "text", text: result.finalBlueprint }],
80
+ };
81
+ }
82
+ catch (err) {
83
+ const msg = err instanceof Error ? err.message : "Unknown error";
84
+ return {
85
+ content: [{ type: "text", text: `❌ Council error: ${msg}` }],
86
+ isError: true,
87
+ };
88
+ }
89
+ });
90
+ // ─── Session History ───
91
+ server.tool("get_session_history", "Retrieve previous council session summaries.", {
92
+ limit: z.number().min(1).max(50).default(10).describe("Number of sessions to retrieve"),
93
+ }, async (params) => {
94
+ const services = await getServices();
95
+ const sessions = services.databaseService.getRecentSessions(params.limit);
96
+ if (sessions.length === 0) {
97
+ return { content: [{ type: "text", text: "No previous sessions found." }] };
98
+ }
99
+ let output = "# Recent Council Sessions\n\n";
100
+ for (const s of sessions) {
101
+ output += `## ${s.id.slice(0, 8)}… (${s.created_at})\n`;
102
+ if (s.tech_stack)
103
+ output += `**Stack**: ${s.tech_stack}\n`;
104
+ if (s.draft_plan_summary)
105
+ output += `**Plan**: ${s.draft_plan_summary}…\n`;
106
+ output += "\n";
107
+ }
108
+ return { content: [{ type: "text", text: output }] };
109
+ });
110
+ // ─── Persona Listing ───
111
+ server.tool("list_personas", "List all expert personas in the council.", {}, async () => {
112
+ const services = await getServices();
113
+ const personaIds = services.personaService.getAvailablePersonaIds();
114
+ const allPersonas = services.personaService.createExperts(personaIds, 'default');
115
+ let output = "# Council Personas\n\n";
116
+ for (const p of allPersonas) {
117
+ output += `## ${p.emoji} ${p.name}\n`;
118
+ output += `**ID**: \`${p.id}\`\n`;
119
+ output += `**Focus**: ${p.focusAreas.join(", ")}\n\n`;
120
+ }
121
+ return { content: [{ type: "text", text: output }] };
122
+ });
123
+ // ─── Health Check ───
124
+ server.tool("ping", "Test if Council MCP is running.", {}, async () => ({
125
+ content: [{
126
+ type: "text",
127
+ text: "🏓 Council MCP is running! Use 'consult_council' to get expert analysis.",
128
+ }],
129
+ }));
130
+ // ─── Start ───
131
+ async function main() {
132
+ const transport = new StdioServerTransport();
133
+ await server.connect(transport);
134
+ console.error("Konsilio MCP server running on stdio");
135
+ }
136
+ main().catch((err) => {
137
+ console.error("Fatal error:", err);
138
+ process.exit(1);
139
+ });
140
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "./konsilio.schema.json",
3
+ "personas": {
4
+ "enabled": ["ux-dx", "performance", "node-fullstack", "devops", "dev-tooling", "test-architect"],
5
+ "_comment": "Available personas: security, performance, ux-dx, devops, typescript, graph-dba, node-fullstack, dev-tooling, distributed-systems, test-architect"
6
+ },
7
+ "constitution": "./constitution.txt",
8
+ "models": {
9
+ "experts": "google/gemini-2.5-flash-lite",
10
+ "lead": "google/gemini-2.5-pro"
11
+ },
12
+ "personaModels": {},
13
+ "timeouts": {
14
+ "expertMs": 90000,
15
+ "leadMs": 120000
16
+ },
17
+ "maxTokens": {
18
+ "experts": 16384,
19
+ "lead": 32768
20
+ },
21
+ "maxDraftPlanLength": 12000,
22
+ "maxHistorySessions": 10,
23
+ "databasePath": "./data/db/konsilio.db",
24
+ "cacheTtlSeconds": 3600
25
+ }
@@ -0,0 +1,140 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Konsilio Configuration",
4
+ "description": "Configuration file for the Konsilio Council of Experts MCP server",
5
+ "type": "object",
6
+ "properties": {
7
+ "$schema": {
8
+ "type": "string",
9
+ "description": "Path to this JSON schema file for IDE validation"
10
+ },
11
+ "personas": {
12
+ "type": "object",
13
+ "description": "Persona configuration for expert analysis",
14
+ "properties": {
15
+ "enabled": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "string",
19
+ "description": "Persona ID (e.g., security, performance, ux-dx, devops, or custom personas)"
20
+ },
21
+ "minItems": 2,
22
+ "description": "Expert personas to include in council consultations. Must have at least 2. Built-in personas: security, performance, ux-dx, devops, typescript, graph-dba, node-fullstack, dev-tooling, distributed-systems, test-architect"
23
+ }
24
+ },
25
+ "required": ["enabled"],
26
+ "additionalProperties": false
27
+ },
28
+ "constitution": {
29
+ "type": "string",
30
+ "description": "Path to optional constitution file with custom workflow rules"
31
+ },
32
+ "models": {
33
+ "type": "object",
34
+ "description": "Default model configuration for all personas",
35
+ "properties": {
36
+ "experts": {
37
+ "type": "string",
38
+ "description": "Default model for expert personas. Override per-persona with personaModels."
39
+ },
40
+ "lead": {
41
+ "type": "string",
42
+ "description": "Model for consolidation phases (extraction, critique, decision, synthesis)"
43
+ },
44
+ "formatter": {
45
+ "type": "string",
46
+ "description": "Model used to format prose into structured JSON"
47
+ }
48
+ },
49
+ "additionalProperties": false
50
+ },
51
+ "personaModels": {
52
+ "type": "object",
53
+ "description": "Per-persona model defaults. Maps personaId to model ID. Overrides models.experts for specific personas.",
54
+ "additionalProperties": {
55
+ "type": "string",
56
+ "description": "OpenRouter model ID for this persona"
57
+ },
58
+ "examples": [
59
+ {
60
+ "security": "openai/gpt-4o",
61
+ "performance": "anthropic/claude-sonnet-4"
62
+ }
63
+ ]
64
+ },
65
+ "timeouts": {
66
+ "type": "object",
67
+ "description": "Timeout configuration for API calls",
68
+ "properties": {
69
+ "expertMs": {
70
+ "type": "integer",
71
+ "minimum": 10000,
72
+ "description": "Timeout for expert analysis in milliseconds"
73
+ },
74
+ "leadMs": {
75
+ "type": "integer",
76
+ "minimum": 30000,
77
+ "description": "Timeout for consolidation phases in milliseconds"
78
+ },
79
+ "formatterMs": {
80
+ "type": "integer",
81
+ "minimum": 10000,
82
+ "description": "Timeout for formatting prose in milliseconds"
83
+ }
84
+ },
85
+ "additionalProperties": false
86
+ },
87
+ "maxTokens": {
88
+ "type": "object",
89
+ "description": "Token limits for API calls",
90
+ "properties": {
91
+ "experts": {
92
+ "type": "integer",
93
+ "minimum": 100,
94
+ "description": "Max tokens per expert analysis"
95
+ },
96
+ "lead": {
97
+ "type": "integer",
98
+ "minimum": 100,
99
+ "description": "Max tokens for consolidation phases"
100
+ }
101
+ },
102
+ "additionalProperties": false
103
+ },
104
+ "maxDraftPlanLength": {
105
+ "type": "integer",
106
+ "minimum": 1000,
107
+ "description": "Maximum length of draft plan input in characters"
108
+ },
109
+ "maxHistorySessions": {
110
+ "type": "integer",
111
+ "minimum": 1,
112
+ "maximum": 100,
113
+ "description": "Maximum number of session history entries to retain"
114
+ },
115
+ "databasePath": {
116
+ "type": "string",
117
+ "description": "Path to SQLite database file for session storage"
118
+ },
119
+ "cacheTtlSeconds": {
120
+ "type": "integer",
121
+ "minimum": 60,
122
+ "description": "Cache time-to-live in seconds for persona data"
123
+ },
124
+ "formatter": {
125
+ "type": "object",
126
+ "description": "Formatter service configuration",
127
+ "properties": {
128
+ "maxRetries": {
129
+ "type": "integer",
130
+ "minimum": 0,
131
+ "maximum": 10,
132
+ "description": "Maximum retries for formatting failures"
133
+ }
134
+ },
135
+ "additionalProperties": false
136
+ }
137
+ },
138
+ "required": ["personas"],
139
+ "additionalProperties": false
140
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Pino-based Structured Logger with correlation ID support
3
+ *
4
+ * Outputs structured JSON logs to stderr for observability and debugging.
5
+ * IMPORTANT: MCP servers MUST use stderr for logs - stdout is reserved for JSON-RPC protocol.
6
+ */
7
+ import pino from 'pino';
8
+ import type { LogLevel } from './config.js';
9
+ export type { LogLevel };
10
+ /**
11
+ * LogWriter interface - standard logging methods
12
+ * Used for correlated logger instances
13
+ */
14
+ export interface LogWriter {
15
+ debug(message: string, data?: Record<string, unknown>): void;
16
+ info(message: string, data?: Record<string, unknown>): void;
17
+ warn(message: string, data?: Record<string, unknown>): void;
18
+ error(message: string, data?: Record<string, unknown>): void;
19
+ }
20
+ /**
21
+ * Logger interface - extends LogWriter with correlation ID support
22
+ */
23
+ export interface Logger extends LogWriter {
24
+ /**
25
+ * Create a child logger with correlation ID embedded in all logs
26
+ */
27
+ withCorrelationId(correlationId: string): CorrelatedLogger;
28
+ /**
29
+ * Debug with optional correlation ID (backward compatibility)
30
+ */
31
+ debug(message: string, data?: Record<string, unknown>, correlationId?: string): void;
32
+ /**
33
+ * Info with optional correlation ID (backward compatibility)
34
+ */
35
+ info(message: string, data?: Record<string, unknown>, correlationId?: string): void;
36
+ /**
37
+ * Warn with optional correlation ID (backward compatibility)
38
+ */
39
+ warn(message: string, data?: Record<string, unknown>, correlationId?: string): void;
40
+ /**
41
+ * Error with optional correlation ID (backward compatibility)
42
+ */
43
+ error(message: string, data?: Record<string, unknown>, correlationId?: string): void;
44
+ }
45
+ /**
46
+ * CorrelatedLogger - logger that automatically includes correlation ID
47
+ */
48
+ export interface CorrelatedLogger extends LogWriter {
49
+ }
50
+ /**
51
+ * Pino-backed Logger implementation
52
+ */
53
+ declare class PinoLogger implements Logger {
54
+ protected readonly pino: pino.Logger;
55
+ constructor(level?: LogLevel);
56
+ debug(message: string, data?: Record<string, unknown>, correlationId?: string): void;
57
+ info(message: string, data?: Record<string, unknown>, correlationId?: string): void;
58
+ warn(message: string, data?: Record<string, unknown>, correlationId?: string): void;
59
+ error(message: string, data?: Record<string, unknown>, correlationId?: string): void;
60
+ withCorrelationId(correlationId: string): CorrelatedLogger;
61
+ }
62
+ /**
63
+ * Singleton logger instance configured from LOG_LEVEL env var
64
+ */
65
+ export declare const logger: PinoLogger;
66
+ /**
67
+ * Create a logger instance with the specified log level
68
+ * @param level - Optional log level (defaults to LOG_LEVEL env var or 'info')
69
+ */
70
+ export declare function createLogger(level?: LogLevel): Logger;
71
+ /**
72
+ * Get a child logger with correlation ID embedded in all log entries
73
+ * Convenience function equivalent to logger.withCorrelationId()
74
+ *
75
+ * @param correlationId - The correlation ID to include in all log entries
76
+ * @returns A logger instance with the correlation ID
77
+ */
78
+ export declare function getLogger(correlationId: string): CorrelatedLogger;
79
+ export declare const log: {
80
+ debug: (message: string, data?: Record<string, unknown>) => void;
81
+ info: (message: string, data?: Record<string, unknown>) => void;
82
+ warn: (message: string, data?: Record<string, unknown>) => void;
83
+ error: (message: string, data?: Record<string, unknown>) => void;
84
+ };