@sparkleideas/shared 3.0.0-alpha.7

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 (96) hide show
  1. package/README.md +323 -0
  2. package/__tests__/hooks/bash-safety.test.ts +289 -0
  3. package/__tests__/hooks/file-organization.test.ts +335 -0
  4. package/__tests__/hooks/git-commit.test.ts +336 -0
  5. package/__tests__/hooks/index.ts +23 -0
  6. package/__tests__/hooks/session-hooks.test.ts +357 -0
  7. package/__tests__/hooks/task-hooks.test.ts +193 -0
  8. package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
  9. package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
  10. package/docs/EVENTS_README.md +352 -0
  11. package/package.json +39 -0
  12. package/src/core/config/defaults.ts +207 -0
  13. package/src/core/config/index.ts +15 -0
  14. package/src/core/config/loader.ts +271 -0
  15. package/src/core/config/schema.ts +188 -0
  16. package/src/core/config/validator.ts +209 -0
  17. package/src/core/event-bus.ts +236 -0
  18. package/src/core/index.ts +22 -0
  19. package/src/core/interfaces/agent.interface.ts +251 -0
  20. package/src/core/interfaces/coordinator.interface.ts +363 -0
  21. package/src/core/interfaces/event.interface.ts +267 -0
  22. package/src/core/interfaces/index.ts +19 -0
  23. package/src/core/interfaces/memory.interface.ts +332 -0
  24. package/src/core/interfaces/task.interface.ts +223 -0
  25. package/src/core/orchestrator/event-coordinator.ts +122 -0
  26. package/src/core/orchestrator/health-monitor.ts +214 -0
  27. package/src/core/orchestrator/index.ts +89 -0
  28. package/src/core/orchestrator/lifecycle-manager.ts +263 -0
  29. package/src/core/orchestrator/session-manager.ts +279 -0
  30. package/src/core/orchestrator/task-manager.ts +317 -0
  31. package/src/events/domain-events.ts +584 -0
  32. package/src/events/event-store.test.ts +387 -0
  33. package/src/events/event-store.ts +588 -0
  34. package/src/events/example-usage.ts +293 -0
  35. package/src/events/index.ts +90 -0
  36. package/src/events/projections.ts +561 -0
  37. package/src/events/state-reconstructor.ts +349 -0
  38. package/src/events.ts +367 -0
  39. package/src/hooks/INTEGRATION.md +658 -0
  40. package/src/hooks/README.md +532 -0
  41. package/src/hooks/example-usage.ts +499 -0
  42. package/src/hooks/executor.ts +379 -0
  43. package/src/hooks/hooks.test.ts +421 -0
  44. package/src/hooks/index.ts +131 -0
  45. package/src/hooks/registry.ts +333 -0
  46. package/src/hooks/safety/bash-safety.ts +604 -0
  47. package/src/hooks/safety/file-organization.ts +473 -0
  48. package/src/hooks/safety/git-commit.ts +623 -0
  49. package/src/hooks/safety/index.ts +46 -0
  50. package/src/hooks/session-hooks.ts +559 -0
  51. package/src/hooks/task-hooks.ts +513 -0
  52. package/src/hooks/types.ts +357 -0
  53. package/src/hooks/verify-exports.test.ts +125 -0
  54. package/src/index.ts +195 -0
  55. package/src/mcp/connection-pool.ts +438 -0
  56. package/src/mcp/index.ts +183 -0
  57. package/src/mcp/server.ts +774 -0
  58. package/src/mcp/session-manager.ts +428 -0
  59. package/src/mcp/tool-registry.ts +566 -0
  60. package/src/mcp/transport/http.ts +557 -0
  61. package/src/mcp/transport/index.ts +294 -0
  62. package/src/mcp/transport/stdio.ts +324 -0
  63. package/src/mcp/transport/websocket.ts +484 -0
  64. package/src/mcp/types.ts +565 -0
  65. package/src/plugin-interface.ts +663 -0
  66. package/src/plugin-loader.ts +638 -0
  67. package/src/plugin-registry.ts +604 -0
  68. package/src/plugins/index.ts +34 -0
  69. package/src/plugins/official/hive-mind-plugin.ts +330 -0
  70. package/src/plugins/official/index.ts +24 -0
  71. package/src/plugins/official/maestro-plugin.ts +508 -0
  72. package/src/plugins/types.ts +108 -0
  73. package/src/resilience/bulkhead.ts +277 -0
  74. package/src/resilience/circuit-breaker.ts +326 -0
  75. package/src/resilience/index.ts +26 -0
  76. package/src/resilience/rate-limiter.ts +420 -0
  77. package/src/resilience/retry.ts +224 -0
  78. package/src/security/index.ts +39 -0
  79. package/src/security/input-validation.ts +265 -0
  80. package/src/security/secure-random.ts +159 -0
  81. package/src/services/index.ts +16 -0
  82. package/src/services/v3-progress.service.ts +505 -0
  83. package/src/types/agent.types.ts +144 -0
  84. package/src/types/index.ts +22 -0
  85. package/src/types/mcp.types.ts +300 -0
  86. package/src/types/memory.types.ts +263 -0
  87. package/src/types/swarm.types.ts +255 -0
  88. package/src/types/task.types.ts +205 -0
  89. package/src/types.ts +367 -0
  90. package/src/utils/secure-logger.d.ts +69 -0
  91. package/src/utils/secure-logger.d.ts.map +1 -0
  92. package/src/utils/secure-logger.js +208 -0
  93. package/src/utils/secure-logger.js.map +1 -0
  94. package/src/utils/secure-logger.ts +257 -0
  95. package/tmp.json +0 -0
  96. package/tsconfig.json +9 -0
@@ -0,0 +1,271 @@
1
+ /**
2
+ * V3 Configuration Loader
3
+ * Load configuration from various sources
4
+ */
5
+
6
+ import { readFile } from 'fs/promises';
7
+ import { join, resolve } from 'path';
8
+ import { existsSync } from 'fs';
9
+ import type { SystemConfig } from './schema.js';
10
+ import { validateSystemConfig, type ValidationResult } from './validator.js';
11
+ import { defaultSystemConfig, mergeWithDefaults } from './defaults.js';
12
+
13
+ /**
14
+ * Configuration source type
15
+ */
16
+ export type ConfigSource = 'file' | 'env' | 'default' | 'merged';
17
+
18
+ /**
19
+ * Loaded configuration with metadata
20
+ */
21
+ export interface LoadedConfig {
22
+ config: SystemConfig;
23
+ source: ConfigSource;
24
+ path?: string;
25
+ warnings?: string[];
26
+ }
27
+
28
+ /**
29
+ * Configuration file names to search for
30
+ */
31
+ const CONFIG_FILE_NAMES = [
32
+ '@sparkleideas/claude-flow.config.json',
33
+ '@sparkleideas/claude-flow.config.js',
34
+ '@sparkleideas/claude-flow.json',
35
+ '.@sparkleideas/claude-flow.json',
36
+ ];
37
+
38
+ /**
39
+ * Find configuration file in directory
40
+ */
41
+ async function findConfigFile(directory: string): Promise<string | null> {
42
+ for (const name of CONFIG_FILE_NAMES) {
43
+ const path = join(directory, name);
44
+ if (existsSync(path)) {
45
+ return path;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+
51
+ /**
52
+ * Load configuration from JSON file
53
+ */
54
+ async function loadJsonConfig(path: string): Promise<unknown> {
55
+ const content = await readFile(path, 'utf8');
56
+ return JSON.parse(content);
57
+ }
58
+
59
+ /**
60
+ * Load configuration from environment variables
61
+ */
62
+ function loadEnvConfig(): Partial<SystemConfig> {
63
+ const config: Partial<SystemConfig> = {};
64
+
65
+ // Orchestrator settings
66
+ if (process.env.CLAUDE_FLOW_MAX_AGENTS) {
67
+ config.orchestrator = {
68
+ ...defaultSystemConfig.orchestrator,
69
+ lifecycle: {
70
+ ...defaultSystemConfig.orchestrator.lifecycle,
71
+ maxConcurrentAgents: parseInt(process.env.CLAUDE_FLOW_MAX_AGENTS, 10),
72
+ },
73
+ };
74
+ }
75
+
76
+ // Data directory
77
+ if (process.env.CLAUDE_FLOW_DATA_DIR) {
78
+ config.orchestrator = {
79
+ ...config.orchestrator,
80
+ ...defaultSystemConfig.orchestrator,
81
+ session: {
82
+ ...defaultSystemConfig.orchestrator.session,
83
+ dataDir: process.env.CLAUDE_FLOW_DATA_DIR,
84
+ },
85
+ };
86
+ }
87
+
88
+ // Memory type
89
+ if (process.env.CLAUDE_FLOW_MEMORY_TYPE) {
90
+ const memoryType = process.env.CLAUDE_FLOW_MEMORY_TYPE as NonNullable<SystemConfig['memory']>['type'];
91
+ if (['sqlite', '@sparkleideas/agentdb', 'hybrid', 'redis', 'memory'].includes(memoryType)) {
92
+ config.memory = {
93
+ ...(defaultSystemConfig.memory ?? { type: 'hybrid' }),
94
+ type: memoryType,
95
+ };
96
+ }
97
+ }
98
+
99
+ // MCP transport
100
+ const defaultMcp = defaultSystemConfig.mcp ?? { name: '@sparkleideas/claude-flow', version: '3.0.0', transport: { type: 'stdio' as const } };
101
+ if (process.env.CLAUDE_FLOW_MCP_TRANSPORT) {
102
+ const transport = process.env.CLAUDE_FLOW_MCP_TRANSPORT as 'stdio' | 'http' | 'websocket';
103
+ if (['stdio', 'http', 'websocket'].includes(transport)) {
104
+ config.mcp = {
105
+ ...defaultMcp,
106
+ transport: {
107
+ ...defaultMcp.transport,
108
+ type: transport,
109
+ },
110
+ };
111
+ }
112
+ }
113
+
114
+ if (process.env.CLAUDE_FLOW_MCP_PORT) {
115
+ config.mcp = {
116
+ ...config.mcp,
117
+ ...defaultMcp,
118
+ transport: {
119
+ ...config.mcp?.transport,
120
+ ...defaultMcp.transport,
121
+ port: parseInt(process.env.CLAUDE_FLOW_MCP_PORT, 10),
122
+ },
123
+ };
124
+ }
125
+
126
+ // Swarm topology
127
+ const defaultSwarm = defaultSystemConfig.swarm ?? { topology: 'hierarchical-mesh' as const, maxAgents: 20 };
128
+ if (process.env.CLAUDE_FLOW_SWARM_TOPOLOGY) {
129
+ const topology = process.env.CLAUDE_FLOW_SWARM_TOPOLOGY as NonNullable<SystemConfig['swarm']>['topology'];
130
+ if (['hierarchical', 'mesh', 'ring', 'star', 'adaptive', 'hierarchical-mesh'].includes(topology)) {
131
+ config.swarm = {
132
+ ...defaultSwarm,
133
+ topology,
134
+ };
135
+ }
136
+ }
137
+
138
+ return config;
139
+ }
140
+
141
+ /**
142
+ * Configuration loader class
143
+ */
144
+ export class ConfigLoader {
145
+ private searchPaths: string[] = [];
146
+
147
+ constructor(additionalPaths?: string[]) {
148
+ // Default search paths
149
+ this.searchPaths = [
150
+ process.cwd(),
151
+ resolve(process.cwd(), '..'),
152
+ resolve(process.env.HOME ?? '', '.@sparkleideas/claude-flow'),
153
+ ];
154
+
155
+ if (additionalPaths) {
156
+ this.searchPaths.push(...additionalPaths);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Load configuration from all sources
162
+ */
163
+ async load(): Promise<LoadedConfig> {
164
+ const warnings: string[] = [];
165
+
166
+ // Start with defaults
167
+ let config: SystemConfig = { ...defaultSystemConfig };
168
+ let source: ConfigSource = 'default';
169
+ let path: string | undefined;
170
+
171
+ // Try to load from file
172
+ for (const searchPath of this.searchPaths) {
173
+ const configPath = await findConfigFile(searchPath);
174
+ if (configPath) {
175
+ try {
176
+ const fileConfig = await loadJsonConfig(configPath);
177
+ const validation = validateSystemConfig(fileConfig);
178
+
179
+ if (validation.success) {
180
+ config = mergeWithDefaults(validation.data!, defaultSystemConfig) as SystemConfig;
181
+ source = 'file';
182
+ path = configPath;
183
+ break;
184
+ } else {
185
+ warnings.push(`Invalid config at ${configPath}: ${validation.errors?.map(e => e.message).join(', ')}`);
186
+ }
187
+ } catch (error) {
188
+ warnings.push(`Failed to load config from ${configPath}: ${(error as Error).message}`);
189
+ }
190
+ }
191
+ }
192
+
193
+ // Merge with environment variables
194
+ const envConfig = loadEnvConfig();
195
+ if (Object.keys(envConfig).length > 0) {
196
+ config = this.deepMerge(config, envConfig) as SystemConfig;
197
+ source = source === 'default' ? 'env' : 'merged';
198
+ }
199
+
200
+ return {
201
+ config,
202
+ source,
203
+ path,
204
+ warnings: warnings.length > 0 ? warnings : undefined,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Load configuration from specific file
210
+ */
211
+ async loadFromFile(filePath: string): Promise<LoadedConfig> {
212
+ const absolutePath = resolve(filePath);
213
+ const fileConfig = await loadJsonConfig(absolutePath);
214
+ const validation = validateSystemConfig(fileConfig);
215
+
216
+ if (!validation.success) {
217
+ throw new Error(`Invalid configuration: ${validation.errors?.map(e => e.message).join(', ')}`);
218
+ }
219
+
220
+ const config = mergeWithDefaults(validation.data!, defaultSystemConfig) as SystemConfig;
221
+
222
+ return {
223
+ config,
224
+ source: 'file',
225
+ path: absolutePath,
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Deep merge objects
231
+ */
232
+ private deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
233
+ const result = { ...target };
234
+
235
+ for (const key of Object.keys(source)) {
236
+ const sourceValue = source[key];
237
+ const targetValue = target[key];
238
+
239
+ if (
240
+ sourceValue &&
241
+ typeof sourceValue === 'object' &&
242
+ !Array.isArray(sourceValue) &&
243
+ targetValue &&
244
+ typeof targetValue === 'object' &&
245
+ !Array.isArray(targetValue)
246
+ ) {
247
+ result[key] = this.deepMerge(
248
+ targetValue as Record<string, unknown>,
249
+ sourceValue as Record<string, unknown>,
250
+ );
251
+ } else if (sourceValue !== undefined) {
252
+ result[key] = sourceValue;
253
+ }
254
+ }
255
+
256
+ return result;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Load configuration (convenience function)
262
+ */
263
+ export async function loadConfig(options?: { paths?: string[]; file?: string }): Promise<LoadedConfig> {
264
+ const loader = new ConfigLoader(options?.paths);
265
+
266
+ if (options?.file) {
267
+ return loader.loadFromFile(options.file);
268
+ }
269
+
270
+ return loader.load();
271
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * V3 Configuration Schemas
3
+ * Zod schemas for all configuration types
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ /**
9
+ * Agent configuration schema
10
+ */
11
+ export const AgentConfigSchema = z.object({
12
+ id: z.string().min(1),
13
+ name: z.string().min(1),
14
+ type: z.string().min(1),
15
+ capabilities: z.array(z.string()).default([]),
16
+ maxConcurrentTasks: z.number().int().min(1).default(5),
17
+ priority: z.number().int().min(0).max(100).default(50),
18
+ timeout: z.number().int().positive().optional(),
19
+ retryPolicy: z.object({
20
+ maxRetries: z.number().int().min(0).default(3),
21
+ backoffMs: z.number().int().positive().default(1000),
22
+ backoffMultiplier: z.number().positive().default(2),
23
+ }).optional(),
24
+ resources: z.object({
25
+ maxMemoryMb: z.number().int().positive().optional(),
26
+ maxCpuPercent: z.number().min(0).max(100).optional(),
27
+ }).optional(),
28
+ metadata: z.record(z.unknown()).optional(),
29
+ });
30
+
31
+ /**
32
+ * Task configuration schema
33
+ */
34
+ export const TaskConfigSchema = z.object({
35
+ type: z.string().min(1),
36
+ description: z.string().min(1),
37
+ priority: z.number().int().min(0).max(100).default(50),
38
+ timeout: z.number().int().positive().optional(),
39
+ assignedAgent: z.string().optional(),
40
+ input: z.record(z.unknown()).optional(),
41
+ metadata: z.object({
42
+ requiredCapabilities: z.array(z.string()).optional(),
43
+ retryCount: z.number().int().min(0).optional(),
44
+ maxRetries: z.number().int().min(0).optional(),
45
+ critical: z.boolean().optional(),
46
+ parentTaskId: z.string().optional(),
47
+ childTaskIds: z.array(z.string()).optional(),
48
+ tags: z.array(z.string()).optional(),
49
+ }).optional(),
50
+ });
51
+
52
+ /**
53
+ * Swarm configuration schema
54
+ */
55
+ export const SwarmConfigSchema = z.object({
56
+ topology: z.enum(['hierarchical', 'mesh', 'ring', 'star', 'adaptive', 'hierarchical-mesh']),
57
+ maxAgents: z.number().int().positive().default(20),
58
+ autoScale: z.object({
59
+ enabled: z.boolean().default(false),
60
+ minAgents: z.number().int().min(0).default(1),
61
+ maxAgents: z.number().int().positive().default(20),
62
+ scaleUpThreshold: z.number().min(0).max(1).default(0.8),
63
+ scaleDownThreshold: z.number().min(0).max(1).default(0.3),
64
+ }).optional(),
65
+ coordination: z.object({
66
+ consensusRequired: z.boolean().default(false),
67
+ timeoutMs: z.number().int().positive().default(10000),
68
+ retryPolicy: z.object({
69
+ maxRetries: z.number().int().min(0).default(3),
70
+ backoffMs: z.number().int().positive().default(500),
71
+ }),
72
+ }).optional(),
73
+ communication: z.object({
74
+ protocol: z.enum(['events', 'messages', 'shared-memory']).default('events'),
75
+ batchSize: z.number().int().positive().default(10),
76
+ flushIntervalMs: z.number().int().positive().default(100),
77
+ }).optional(),
78
+ metadata: z.record(z.unknown()).optional(),
79
+ });
80
+
81
+ /**
82
+ * Memory configuration schema
83
+ */
84
+ export const MemoryConfigSchema = z.object({
85
+ type: z.enum(['sqlite', '@sparkleideas/agentdb', 'hybrid', 'redis', 'memory']).default('hybrid'),
86
+ path: z.string().optional(),
87
+ maxSize: z.number().int().positive().optional(),
88
+ ttlMs: z.number().int().positive().optional(),
89
+ sqlite: z.object({
90
+ filename: z.string().optional(),
91
+ inMemory: z.boolean().default(false),
92
+ wal: z.boolean().default(true),
93
+ }).optional(),
94
+ @sparkleideas/agentdb: z.object({
95
+ dimensions: z.number().int().positive().default(1536),
96
+ indexType: z.enum(['hnsw', 'flat', 'ivf']).default('hnsw'),
97
+ efConstruction: z.number().int().positive().default(200),
98
+ m: z.number().int().positive().default(16),
99
+ quantization: z.enum(['none', 'scalar', 'product']).default('none'),
100
+ }).optional(),
101
+ redis: z.object({
102
+ host: z.string().default('localhost'),
103
+ port: z.number().int().positive().default(6379),
104
+ password: z.string().optional(),
105
+ db: z.number().int().min(0).default(0),
106
+ keyPrefix: z.string().default('@sparkleideas/claude-flow:'),
107
+ }).optional(),
108
+ hybrid: z.object({
109
+ vectorThreshold: z.number().int().positive().default(100),
110
+ }).optional(),
111
+ });
112
+
113
+ /**
114
+ * MCP server configuration schema
115
+ */
116
+ export const MCPServerConfigSchema = z.object({
117
+ name: z.string().min(1).default('@sparkleideas/claude-flow'),
118
+ version: z.string().min(1).default('3.0.0'),
119
+ transport: z.object({
120
+ type: z.enum(['stdio', 'http', 'websocket']).default('stdio'),
121
+ port: z.number().int().positive().optional(),
122
+ host: z.string().optional(),
123
+ path: z.string().optional(),
124
+ }),
125
+ capabilities: z.object({
126
+ tools: z.boolean().default(true),
127
+ resources: z.boolean().default(true),
128
+ prompts: z.boolean().default(true),
129
+ logging: z.boolean().default(true),
130
+ experimental: z.record(z.boolean()).optional(),
131
+ }).optional(),
132
+ });
133
+
134
+ /**
135
+ * Orchestrator configuration schema
136
+ */
137
+ export const OrchestratorConfigSchema = z.object({
138
+ session: z.object({
139
+ persistSessions: z.boolean().default(true),
140
+ dataDir: z.string().default('./data'),
141
+ sessionRetentionMs: z.number().int().positive().default(3600000),
142
+ }),
143
+ health: z.object({
144
+ checkInterval: z.number().int().positive().default(30000),
145
+ historyLimit: z.number().int().positive().default(100),
146
+ degradedThreshold: z.number().int().min(0).default(1),
147
+ unhealthyThreshold: z.number().int().min(0).default(2),
148
+ }),
149
+ lifecycle: z.object({
150
+ maxConcurrentAgents: z.number().int().positive().default(20),
151
+ spawnTimeout: z.number().int().positive().default(30000),
152
+ terminateTimeout: z.number().int().positive().default(10000),
153
+ maxSpawnRetries: z.number().int().min(0).default(3),
154
+ }),
155
+ });
156
+
157
+ /**
158
+ * Full system configuration schema
159
+ */
160
+ export const SystemConfigSchema = z.object({
161
+ orchestrator: OrchestratorConfigSchema,
162
+ memory: MemoryConfigSchema.optional(),
163
+ mcp: MCPServerConfigSchema.optional(),
164
+ swarm: SwarmConfigSchema.optional(),
165
+ });
166
+
167
+ /**
168
+ * Export schema types
169
+ * Using z.output to get post-default types (fields with defaults are required in output)
170
+ */
171
+ export type AgentConfig = z.output<typeof AgentConfigSchema>;
172
+ export type TaskConfig = z.output<typeof TaskConfigSchema>;
173
+ export type SwarmConfig = z.output<typeof SwarmConfigSchema>;
174
+ export type MemoryConfig = z.output<typeof MemoryConfigSchema>;
175
+ export type MCPServerConfig = z.output<typeof MCPServerConfigSchema>;
176
+ export type OrchestratorConfig = z.output<typeof OrchestratorConfigSchema>;
177
+ export type SystemConfig = z.output<typeof SystemConfigSchema>;
178
+
179
+ /**
180
+ * Input types (for validation before defaults are applied)
181
+ */
182
+ export type AgentConfigInput = z.input<typeof AgentConfigSchema>;
183
+ export type TaskConfigInput = z.input<typeof TaskConfigSchema>;
184
+ export type SwarmConfigInput = z.input<typeof SwarmConfigSchema>;
185
+ export type MemoryConfigInput = z.input<typeof MemoryConfigSchema>;
186
+ export type MCPServerConfigInput = z.input<typeof MCPServerConfigSchema>;
187
+ export type OrchestratorConfigInput = z.input<typeof OrchestratorConfigSchema>;
188
+ export type SystemConfigInput = z.input<typeof SystemConfigSchema>;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * V3 Configuration Validator
3
+ * Validation logic using Zod schemas
4
+ */
5
+
6
+ import { z, type ZodError } from 'zod';
7
+ import {
8
+ AgentConfigSchema,
9
+ TaskConfigSchema,
10
+ SwarmConfigSchema,
11
+ MemoryConfigSchema,
12
+ MCPServerConfigSchema,
13
+ OrchestratorConfigSchema,
14
+ SystemConfigSchema,
15
+ type AgentConfig,
16
+ type TaskConfig,
17
+ type SwarmConfig,
18
+ type MemoryConfig,
19
+ type MCPServerConfig,
20
+ type OrchestratorConfig,
21
+ type SystemConfig,
22
+ } from './schema.js';
23
+
24
+ /**
25
+ * Validation result
26
+ */
27
+ export interface ValidationResult<T> {
28
+ success: boolean;
29
+ data?: T;
30
+ errors?: ValidationError[];
31
+ }
32
+
33
+ /**
34
+ * Validation error
35
+ */
36
+ export interface ValidationError {
37
+ path: string;
38
+ message: string;
39
+ code: string;
40
+ }
41
+
42
+ /**
43
+ * Convert Zod error to validation errors
44
+ */
45
+ function zodErrorToValidationErrors(error: ZodError): ValidationError[] {
46
+ return error.errors.map((e) => ({
47
+ path: e.path.join('.'),
48
+ message: e.message,
49
+ code: e.code,
50
+ }));
51
+ }
52
+
53
+ /**
54
+ * Generic validation function
55
+ * Uses parse + try/catch to get output types with defaults applied
56
+ */
57
+ function validate<TInput, TOutput>(
58
+ schema: z.ZodType<TOutput, z.ZodTypeDef, TInput>,
59
+ data: unknown
60
+ ): ValidationResult<TOutput> {
61
+ try {
62
+ const parsed = schema.parse(data);
63
+ return {
64
+ success: true,
65
+ data: parsed,
66
+ };
67
+ } catch (error) {
68
+ if (error instanceof z.ZodError) {
69
+ return {
70
+ success: false,
71
+ errors: zodErrorToValidationErrors(error),
72
+ };
73
+ }
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Validate agent configuration
80
+ */
81
+ export function validateAgentConfig(data: unknown): ValidationResult<AgentConfig> {
82
+ return validate(AgentConfigSchema, data);
83
+ }
84
+
85
+ /**
86
+ * Validate task configuration
87
+ */
88
+ export function validateTaskConfig(data: unknown): ValidationResult<TaskConfig> {
89
+ return validate(TaskConfigSchema, data);
90
+ }
91
+
92
+ /**
93
+ * Validate swarm configuration
94
+ */
95
+ export function validateSwarmConfig(data: unknown): ValidationResult<SwarmConfig> {
96
+ return validate(SwarmConfigSchema, data);
97
+ }
98
+
99
+ /**
100
+ * Validate memory configuration
101
+ */
102
+ export function validateMemoryConfig(data: unknown): ValidationResult<MemoryConfig> {
103
+ return validate(MemoryConfigSchema, data);
104
+ }
105
+
106
+ /**
107
+ * Validate MCP server configuration
108
+ */
109
+ export function validateMCPServerConfig(data: unknown): ValidationResult<MCPServerConfig> {
110
+ return validate(MCPServerConfigSchema, data);
111
+ }
112
+
113
+ /**
114
+ * Validate orchestrator configuration
115
+ */
116
+ export function validateOrchestratorConfig(data: unknown): ValidationResult<OrchestratorConfig> {
117
+ return validate(OrchestratorConfigSchema, data);
118
+ }
119
+
120
+ /**
121
+ * Validate full system configuration
122
+ */
123
+ export function validateSystemConfig(data: unknown): ValidationResult<SystemConfig> {
124
+ return validate(SystemConfigSchema, data);
125
+ }
126
+
127
+ /**
128
+ * Configuration validator class
129
+ */
130
+ export class ConfigValidator {
131
+ /**
132
+ * Validate and throw on error
133
+ */
134
+ static validateOrThrow<TInput, TOutput>(
135
+ schema: z.ZodType<TOutput, z.ZodTypeDef, TInput>,
136
+ data: unknown,
137
+ configName: string
138
+ ): TOutput {
139
+ const result = validate(schema, data);
140
+
141
+ if (!result.success) {
142
+ const errorMessages = result.errors
143
+ ?.map((e) => ` - ${e.path}: ${e.message}`)
144
+ .join('\n');
145
+ throw new Error(`Invalid ${configName} configuration:\n${errorMessages}`);
146
+ }
147
+
148
+ return result.data!;
149
+ }
150
+
151
+ /**
152
+ * Validate agent config or throw
153
+ */
154
+ static validateAgentOrThrow(data: unknown): AgentConfig {
155
+ return this.validateOrThrow(AgentConfigSchema, data, 'agent');
156
+ }
157
+
158
+ /**
159
+ * Validate task config or throw
160
+ */
161
+ static validateTaskOrThrow(data: unknown): TaskConfig {
162
+ return this.validateOrThrow(TaskConfigSchema, data, 'task');
163
+ }
164
+
165
+ /**
166
+ * Validate swarm config or throw
167
+ */
168
+ static validateSwarmOrThrow(data: unknown): SwarmConfig {
169
+ return this.validateOrThrow(SwarmConfigSchema, data, 'swarm');
170
+ }
171
+
172
+ /**
173
+ * Validate memory config or throw
174
+ */
175
+ static validateMemoryOrThrow(data: unknown): MemoryConfig {
176
+ return this.validateOrThrow(MemoryConfigSchema, data, 'memory');
177
+ }
178
+
179
+ /**
180
+ * Validate MCP server config or throw
181
+ */
182
+ static validateMCPServerOrThrow(data: unknown): MCPServerConfig {
183
+ return this.validateOrThrow(MCPServerConfigSchema, data, 'MCP server');
184
+ }
185
+
186
+ /**
187
+ * Validate orchestrator config or throw
188
+ */
189
+ static validateOrchestratorOrThrow(data: unknown): OrchestratorConfig {
190
+ return this.validateOrThrow(OrchestratorConfigSchema, data, 'orchestrator');
191
+ }
192
+
193
+ /**
194
+ * Validate system config or throw
195
+ */
196
+ static validateSystemOrThrow(data: unknown): SystemConfig {
197
+ return this.validateOrThrow(SystemConfigSchema, data, 'system');
198
+ }
199
+
200
+ /**
201
+ * Check if data matches schema
202
+ */
203
+ static isValid<TInput, TOutput>(
204
+ schema: z.ZodType<TOutput, z.ZodTypeDef, TInput>,
205
+ data: unknown
206
+ ): boolean {
207
+ return validate(schema, data).success;
208
+ }
209
+ }