zouroboros-core 2.0.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 +21 -0
- package/README.md +85 -0
- package/dist/backup.d.ts +65 -0
- package/dist/backup.js +203 -0
- package/dist/commands.d.ts +69 -0
- package/dist/commands.js +307 -0
- package/dist/config/loader.d.ts +48 -0
- package/dist/config/loader.js +145 -0
- package/dist/config/schema.d.ts +597 -0
- package/dist/config/schema.js +151 -0
- package/dist/constants.d.ts +57 -0
- package/dist/constants.js +225 -0
- package/dist/errors.d.ts +79 -0
- package/dist/errors.js +171 -0
- package/dist/hooks.d.ts +81 -0
- package/dist/hooks.js +231 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -0
- package/dist/instincts.d.ts +117 -0
- package/dist/instincts.js +428 -0
- package/dist/migrations.d.ts +56 -0
- package/dist/migrations.js +123 -0
- package/dist/sessions.d.ts +88 -0
- package/dist/sessions.js +275 -0
- package/dist/token-budget.d.ts +76 -0
- package/dist/token-budget.js +196 -0
- package/dist/types.d.ts +406 -0
- package/dist/types.js +6 -0
- package/dist/utils/index.d.ts +55 -0
- package/dist/utils/index.js +132 -0
- package/package.json +50 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod validation schemas for Zouroboros configuration
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime validation with actionable error messages
|
|
5
|
+
* for all configuration sections.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Primitive Schemas
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const cronExpression = z
|
|
12
|
+
.string()
|
|
13
|
+
.regex(/^[\d*,/-]+ [\d*,/-]+ [\d*,/-]+ [\d*,/-]+ [\d*,/-]+$/, {
|
|
14
|
+
message: 'Must be a valid 5-field cron expression (e.g., "0 5 * * *")',
|
|
15
|
+
});
|
|
16
|
+
const absolutePath = z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1, 'Path must not be empty')
|
|
19
|
+
.refine((p) => p.startsWith('/') || p.startsWith('~'), {
|
|
20
|
+
message: 'Path must be absolute (start with / or ~)',
|
|
21
|
+
});
|
|
22
|
+
const urlString = z
|
|
23
|
+
.string()
|
|
24
|
+
.url('Must be a valid URL (e.g., "http://localhost:11434")');
|
|
25
|
+
const positiveInt = z
|
|
26
|
+
.number()
|
|
27
|
+
.int('Must be a whole number')
|
|
28
|
+
.positive('Must be greater than 0');
|
|
29
|
+
const positiveNumber = z
|
|
30
|
+
.number()
|
|
31
|
+
.positive('Must be greater than 0');
|
|
32
|
+
const unitInterval = z
|
|
33
|
+
.number()
|
|
34
|
+
.min(0, 'Must be between 0 and 1')
|
|
35
|
+
.max(1, 'Must be between 0 and 1');
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Section Schemas
|
|
38
|
+
// ============================================================================
|
|
39
|
+
export const CoreConfigSchema = z.object({
|
|
40
|
+
workspaceRoot: absolutePath,
|
|
41
|
+
dataDir: absolutePath,
|
|
42
|
+
logLevel: z.enum(['debug', 'info', 'warn', 'error'], {
|
|
43
|
+
errorMap: () => ({ message: 'logLevel must be one of: debug, info, warn, error' }),
|
|
44
|
+
}),
|
|
45
|
+
defaultTimezone: z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1, 'Timezone must not be empty (e.g., "America/Phoenix", "UTC")'),
|
|
48
|
+
});
|
|
49
|
+
export const MemoryConfigSchema = z.object({
|
|
50
|
+
enabled: z.boolean(),
|
|
51
|
+
dbPath: absolutePath,
|
|
52
|
+
vectorEnabled: z.boolean(),
|
|
53
|
+
ollamaUrl: urlString,
|
|
54
|
+
ollamaModel: z.string().min(1, 'Ollama model name must not be empty'),
|
|
55
|
+
autoCapture: z.boolean(),
|
|
56
|
+
captureIntervalMinutes: positiveInt.describe('Interval in minutes between auto-captures'),
|
|
57
|
+
graphBoost: z.boolean(),
|
|
58
|
+
hydeExpansion: z.boolean(),
|
|
59
|
+
decayConfig: z.object({
|
|
60
|
+
permanent: positiveNumber,
|
|
61
|
+
long: positiveNumber,
|
|
62
|
+
medium: positiveNumber,
|
|
63
|
+
short: positiveNumber,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
const CircuitBreakerSchema = z.object({
|
|
67
|
+
enabled: z.boolean(),
|
|
68
|
+
failureThreshold: positiveInt.describe('Number of failures before circuit opens'),
|
|
69
|
+
resetTimeoutMs: positiveInt.describe('Milliseconds before circuit resets'),
|
|
70
|
+
});
|
|
71
|
+
const RetryConfigSchema = z.object({
|
|
72
|
+
maxRetries: z.number().int().min(0, 'maxRetries must be 0 or greater'),
|
|
73
|
+
backoffMultiplier: positiveNumber,
|
|
74
|
+
maxBackoffMs: positiveInt,
|
|
75
|
+
});
|
|
76
|
+
export const SwarmConfigSchema = z.object({
|
|
77
|
+
enabled: z.boolean(),
|
|
78
|
+
defaultCombo: z.string().min(1, 'Default combo must not be empty'),
|
|
79
|
+
maxConcurrency: positiveInt.describe('Max parallel tasks across all executors'),
|
|
80
|
+
localConcurrency: positiveInt.describe('Max parallel tasks on local machine'),
|
|
81
|
+
circuitBreaker: CircuitBreakerSchema,
|
|
82
|
+
retryConfig: RetryConfigSchema,
|
|
83
|
+
registryPath: absolutePath,
|
|
84
|
+
});
|
|
85
|
+
export const PersonasConfigSchema = z.object({
|
|
86
|
+
enabled: z.boolean(),
|
|
87
|
+
identityDir: absolutePath,
|
|
88
|
+
defaultSoulPath: absolutePath,
|
|
89
|
+
autoCreateHeartbeat: z.boolean(),
|
|
90
|
+
});
|
|
91
|
+
const MetricThresholdSchema = z
|
|
92
|
+
.object({
|
|
93
|
+
target: unitInterval,
|
|
94
|
+
weight: unitInterval,
|
|
95
|
+
warningThreshold: unitInterval,
|
|
96
|
+
criticalThreshold: unitInterval,
|
|
97
|
+
})
|
|
98
|
+
.refine((m) => m.criticalThreshold <= m.warningThreshold, {
|
|
99
|
+
message: 'criticalThreshold must be ≤ warningThreshold',
|
|
100
|
+
})
|
|
101
|
+
.refine((m) => m.warningThreshold <= m.target, {
|
|
102
|
+
message: 'warningThreshold must be ≤ target',
|
|
103
|
+
});
|
|
104
|
+
export const SelfHealConfigSchema = z.object({
|
|
105
|
+
enabled: z.boolean(),
|
|
106
|
+
introspectionInterval: cronExpression,
|
|
107
|
+
autoPrescribe: z.boolean(),
|
|
108
|
+
governorEnabled: z.boolean(),
|
|
109
|
+
minHealthScore: z.number().min(0).max(100, 'minHealthScore must be 0-100'),
|
|
110
|
+
metrics: z.record(z.string(), MetricThresholdSchema),
|
|
111
|
+
});
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Root Schema
|
|
114
|
+
// ============================================================================
|
|
115
|
+
export const ZouroborosConfigSchema = z.object({
|
|
116
|
+
version: z.string().min(1),
|
|
117
|
+
createdAt: z.string().min(1, 'createdAt timestamp is required'),
|
|
118
|
+
updatedAt: z.string().min(1, 'updatedAt timestamp is required'),
|
|
119
|
+
core: CoreConfigSchema,
|
|
120
|
+
memory: MemoryConfigSchema,
|
|
121
|
+
swarm: SwarmConfigSchema,
|
|
122
|
+
personas: PersonasConfigSchema,
|
|
123
|
+
selfheal: SelfHealConfigSchema,
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Validate a config object and return structured errors.
|
|
127
|
+
* Returns null if valid, or an array of issues with paths.
|
|
128
|
+
*/
|
|
129
|
+
export function validateConfigSchema(config) {
|
|
130
|
+
const result = ZouroborosConfigSchema.safeParse(config);
|
|
131
|
+
if (result.success)
|
|
132
|
+
return null;
|
|
133
|
+
return result.error.issues.map((issue) => ({
|
|
134
|
+
path: issue.path.join('.'),
|
|
135
|
+
message: issue.message,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Format validation issues into a human-readable string.
|
|
140
|
+
*/
|
|
141
|
+
export function formatValidationErrors(issues) {
|
|
142
|
+
const lines = ['Configuration validation failed:', ''];
|
|
143
|
+
for (const issue of issues) {
|
|
144
|
+
const location = issue.path ? ` ${issue.path}` : ' (root)';
|
|
145
|
+
lines.push(`${location}: ${issue.message}`);
|
|
146
|
+
}
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push('Run "zouroboros config list" to see current values.');
|
|
149
|
+
lines.push('Run "zouroboros config set <key> <value>" to fix.');
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core constants for Zouroboros
|
|
3
|
+
*/
|
|
4
|
+
import type { ZouroborosConfig, MetricThreshold } from './types.js';
|
|
5
|
+
export declare const ZOUROBOROS_VERSION = "2.0.0";
|
|
6
|
+
export declare const ZOUROBOROS_NAME = "Zouroboros";
|
|
7
|
+
export declare const DEFAULT_WORKSPACE_ROOT = "/home/workspace";
|
|
8
|
+
export declare const DEFAULT_DATA_DIR: string;
|
|
9
|
+
export declare const DEFAULT_CONFIG_DIR: string;
|
|
10
|
+
export declare const DEFAULT_CONFIG_PATH: string;
|
|
11
|
+
export declare const DEFAULT_MEMORY_DB_PATH: string;
|
|
12
|
+
export declare const DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
13
|
+
export declare const DEFAULT_OLLAMA_MODEL = "nomic-embed-text";
|
|
14
|
+
export declare const DEFAULT_CAPTURE_INTERVAL = 30;
|
|
15
|
+
export declare const DECAY_DAYS: {
|
|
16
|
+
permanent: number;
|
|
17
|
+
long: number;
|
|
18
|
+
medium: number;
|
|
19
|
+
short: number;
|
|
20
|
+
};
|
|
21
|
+
export declare const DEFAULT_SWARM_COMBO = "swarm-mid";
|
|
22
|
+
export declare const DEFAULT_MAX_CONCURRENCY = 5;
|
|
23
|
+
export declare const DEFAULT_LOCAL_CONCURRENCY = 2;
|
|
24
|
+
export declare const CIRCUIT_BREAKER_DEFAULTS: {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
failureThreshold: number;
|
|
27
|
+
resetTimeoutMs: number;
|
|
28
|
+
};
|
|
29
|
+
export declare const RETRY_DEFAULTS: {
|
|
30
|
+
maxRetries: number;
|
|
31
|
+
backoffMultiplier: number;
|
|
32
|
+
maxBackoffMs: number;
|
|
33
|
+
};
|
|
34
|
+
export declare const DEFAULT_LATENCY_PREFERENCE = "balanced";
|
|
35
|
+
export declare const COMPLEXITY_THRESHOLDS: {
|
|
36
|
+
trivial: number;
|
|
37
|
+
simple: number;
|
|
38
|
+
moderate: number;
|
|
39
|
+
complex: number;
|
|
40
|
+
};
|
|
41
|
+
export declare const STATIC_COMBO_MAP: Record<string, string>;
|
|
42
|
+
export declare const DEFAULT_INTROSPECTION_CRON = "0 5 * * *";
|
|
43
|
+
export declare const DEFAULT_MIN_HEALTH_SCORE = 70;
|
|
44
|
+
export declare const DEFAULT_METRIC_THRESHOLDS: Record<string, MetricThreshold>;
|
|
45
|
+
export declare const DEFAULT_CONFIG: ZouroborosConfig;
|
|
46
|
+
export declare const VALID_LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
47
|
+
export declare const VALID_LATENCY_PREFERENCES: readonly ["fast", "balanced", "quality"];
|
|
48
|
+
export declare const VALID_DECAY_CLASSES: readonly ["permanent", "long", "medium", "short"];
|
|
49
|
+
export declare const VALID_TASK_TYPES: readonly ["coding", "review", "planning", "analysis", "debugging", "documentation", "general"];
|
|
50
|
+
export declare const IGNORE_PATTERNS: string[];
|
|
51
|
+
export declare const MEMORY_FILE_EXTENSIONS: string[];
|
|
52
|
+
export declare const CLI_COMMANDS: readonly ["init", "doctor", "config", "memory", "swarm", "persona", "workflow", "selfheal"];
|
|
53
|
+
export declare const MEMORY_SUBCOMMANDS: readonly ["store", "search", "hybrid", "graph", "capture", "stats"];
|
|
54
|
+
export declare const SWARM_SUBCOMMANDS: readonly ["run", "status", "registry", "bridges"];
|
|
55
|
+
export declare const PERSONA_SUBCOMMANDS: readonly ["create", "list", "activate", "deactivate"];
|
|
56
|
+
export declare const WORKFLOW_SUBCOMMANDS: readonly ["interview", "evaluate", "unstuck"];
|
|
57
|
+
export declare const SELFHEAL_SUBCOMMANDS: readonly ["introspect", "prescribe", "evolve"];
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core constants for Zouroboros
|
|
3
|
+
*/
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
export const ZOUROBOROS_VERSION = '2.0.0';
|
|
7
|
+
export const ZOUROBOROS_NAME = 'Zouroboros';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Paths
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const DEFAULT_WORKSPACE_ROOT = '/home/workspace';
|
|
12
|
+
export const DEFAULT_DATA_DIR = join(homedir(), '.zouroboros');
|
|
13
|
+
export const DEFAULT_CONFIG_DIR = DEFAULT_DATA_DIR;
|
|
14
|
+
export const DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR, 'config.json');
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Memory System Defaults
|
|
17
|
+
// ============================================================================
|
|
18
|
+
export const DEFAULT_MEMORY_DB_PATH = join(DEFAULT_DATA_DIR, 'memory.db');
|
|
19
|
+
export const DEFAULT_OLLAMA_URL = 'http://localhost:11434';
|
|
20
|
+
export const DEFAULT_OLLAMA_MODEL = 'nomic-embed-text';
|
|
21
|
+
export const DEFAULT_CAPTURE_INTERVAL = 30; // minutes
|
|
22
|
+
export const DECAY_DAYS = {
|
|
23
|
+
permanent: Infinity,
|
|
24
|
+
long: 365,
|
|
25
|
+
medium: 90,
|
|
26
|
+
short: 30,
|
|
27
|
+
};
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Swarm Defaults
|
|
30
|
+
// ============================================================================
|
|
31
|
+
export const DEFAULT_SWARM_COMBO = 'swarm-mid';
|
|
32
|
+
export const DEFAULT_MAX_CONCURRENCY = 5;
|
|
33
|
+
export const DEFAULT_LOCAL_CONCURRENCY = 2;
|
|
34
|
+
export const CIRCUIT_BREAKER_DEFAULTS = {
|
|
35
|
+
enabled: true,
|
|
36
|
+
failureThreshold: 5,
|
|
37
|
+
resetTimeoutMs: 30000,
|
|
38
|
+
};
|
|
39
|
+
export const RETRY_DEFAULTS = {
|
|
40
|
+
maxRetries: 3,
|
|
41
|
+
backoffMultiplier: 2,
|
|
42
|
+
maxBackoffMs: 30000,
|
|
43
|
+
};
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Routing Service Defaults
|
|
46
|
+
// ============================================================================
|
|
47
|
+
export const DEFAULT_LATENCY_PREFERENCE = 'balanced';
|
|
48
|
+
// Complexity score thresholds
|
|
49
|
+
export const COMPLEXITY_THRESHOLDS = {
|
|
50
|
+
trivial: 0.3,
|
|
51
|
+
simple: 0.5,
|
|
52
|
+
moderate: 0.7,
|
|
53
|
+
complex: 1.0,
|
|
54
|
+
};
|
|
55
|
+
// Static combo mapping (fallback when external combo service is unavailable)
|
|
56
|
+
export const STATIC_COMBO_MAP = {
|
|
57
|
+
trivial: 'swarm-light',
|
|
58
|
+
simple: 'swarm-light',
|
|
59
|
+
moderate: 'swarm-mid',
|
|
60
|
+
complex: 'swarm-heavy',
|
|
61
|
+
};
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Self-Heal Defaults
|
|
64
|
+
// ============================================================================
|
|
65
|
+
export const DEFAULT_INTROSPECTION_CRON = '0 5 * * *'; // 5 AM daily
|
|
66
|
+
export const DEFAULT_MIN_HEALTH_SCORE = 70;
|
|
67
|
+
export const DEFAULT_METRIC_THRESHOLDS = {
|
|
68
|
+
memoryRecall: {
|
|
69
|
+
target: 0.85,
|
|
70
|
+
weight: 0.25,
|
|
71
|
+
warningThreshold: 0.75,
|
|
72
|
+
criticalThreshold: 0.60,
|
|
73
|
+
},
|
|
74
|
+
graphConnectivity: {
|
|
75
|
+
target: 0.80,
|
|
76
|
+
weight: 0.15,
|
|
77
|
+
warningThreshold: 0.70,
|
|
78
|
+
criticalThreshold: 0.55,
|
|
79
|
+
},
|
|
80
|
+
routingAccuracy: {
|
|
81
|
+
target: 0.85,
|
|
82
|
+
weight: 0.20,
|
|
83
|
+
warningThreshold: 0.75,
|
|
84
|
+
criticalThreshold: 0.60,
|
|
85
|
+
},
|
|
86
|
+
evalCalibration: {
|
|
87
|
+
target: 0.85,
|
|
88
|
+
weight: 0.15,
|
|
89
|
+
warningThreshold: 0.75,
|
|
90
|
+
criticalThreshold: 0.60,
|
|
91
|
+
},
|
|
92
|
+
procedureFreshness: {
|
|
93
|
+
target: 0.70,
|
|
94
|
+
weight: 0.15,
|
|
95
|
+
warningThreshold: 0.60,
|
|
96
|
+
criticalThreshold: 0.45,
|
|
97
|
+
},
|
|
98
|
+
episodeVelocity: {
|
|
99
|
+
target: 0.60,
|
|
100
|
+
weight: 0.10,
|
|
101
|
+
warningThreshold: 0.50,
|
|
102
|
+
criticalThreshold: 0.35,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Default Configuration Object
|
|
107
|
+
// ============================================================================
|
|
108
|
+
export const DEFAULT_CONFIG = {
|
|
109
|
+
version: ZOUROBOROS_VERSION,
|
|
110
|
+
createdAt: new Date().toISOString(),
|
|
111
|
+
updatedAt: new Date().toISOString(),
|
|
112
|
+
core: {
|
|
113
|
+
workspaceRoot: DEFAULT_WORKSPACE_ROOT,
|
|
114
|
+
dataDir: DEFAULT_DATA_DIR,
|
|
115
|
+
logLevel: 'info',
|
|
116
|
+
defaultTimezone: 'America/Phoenix',
|
|
117
|
+
},
|
|
118
|
+
memory: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
dbPath: DEFAULT_MEMORY_DB_PATH,
|
|
121
|
+
vectorEnabled: true,
|
|
122
|
+
ollamaUrl: DEFAULT_OLLAMA_URL,
|
|
123
|
+
ollamaModel: DEFAULT_OLLAMA_MODEL,
|
|
124
|
+
autoCapture: true,
|
|
125
|
+
captureIntervalMinutes: DEFAULT_CAPTURE_INTERVAL,
|
|
126
|
+
graphBoost: true,
|
|
127
|
+
hydeExpansion: true,
|
|
128
|
+
decayConfig: DECAY_DAYS,
|
|
129
|
+
},
|
|
130
|
+
swarm: {
|
|
131
|
+
enabled: true,
|
|
132
|
+
defaultCombo: DEFAULT_SWARM_COMBO,
|
|
133
|
+
maxConcurrency: DEFAULT_MAX_CONCURRENCY,
|
|
134
|
+
localConcurrency: DEFAULT_LOCAL_CONCURRENCY,
|
|
135
|
+
circuitBreaker: CIRCUIT_BREAKER_DEFAULTS,
|
|
136
|
+
retryConfig: RETRY_DEFAULTS,
|
|
137
|
+
registryPath: join(DEFAULT_DATA_DIR, 'executor-registry.json'),
|
|
138
|
+
},
|
|
139
|
+
personas: {
|
|
140
|
+
enabled: true,
|
|
141
|
+
identityDir: join(DEFAULT_WORKSPACE_ROOT, 'IDENTITY'),
|
|
142
|
+
defaultSoulPath: join(DEFAULT_WORKSPACE_ROOT, 'SOUL.md'),
|
|
143
|
+
autoCreateHeartbeat: false,
|
|
144
|
+
},
|
|
145
|
+
selfheal: {
|
|
146
|
+
enabled: true,
|
|
147
|
+
introspectionInterval: DEFAULT_INTROSPECTION_CRON,
|
|
148
|
+
autoPrescribe: false,
|
|
149
|
+
governorEnabled: true,
|
|
150
|
+
minHealthScore: DEFAULT_MIN_HEALTH_SCORE,
|
|
151
|
+
metrics: DEFAULT_METRIC_THRESHOLDS,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// Validation Constants
|
|
156
|
+
// ============================================================================
|
|
157
|
+
export const VALID_LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
158
|
+
export const VALID_LATENCY_PREFERENCES = ['fast', 'balanced', 'quality'];
|
|
159
|
+
export const VALID_DECAY_CLASSES = ['permanent', 'long', 'medium', 'short'];
|
|
160
|
+
export const VALID_TASK_TYPES = [
|
|
161
|
+
'coding',
|
|
162
|
+
'review',
|
|
163
|
+
'planning',
|
|
164
|
+
'analysis',
|
|
165
|
+
'debugging',
|
|
166
|
+
'documentation',
|
|
167
|
+
'general',
|
|
168
|
+
];
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// File Patterns
|
|
171
|
+
// ============================================================================
|
|
172
|
+
export const IGNORE_PATTERNS = [
|
|
173
|
+
'node_modules/**',
|
|
174
|
+
'.git/**',
|
|
175
|
+
'dist/**',
|
|
176
|
+
'build/**',
|
|
177
|
+
'.zouroboros/**',
|
|
178
|
+
'*.log',
|
|
179
|
+
'*.tmp',
|
|
180
|
+
'.env*',
|
|
181
|
+
];
|
|
182
|
+
export const MEMORY_FILE_EXTENSIONS = ['.md', '.txt', '.ts', '.js', '.json', '.yaml', '.yml'];
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// CLI Constants
|
|
185
|
+
// ============================================================================
|
|
186
|
+
export const CLI_COMMANDS = [
|
|
187
|
+
'init',
|
|
188
|
+
'doctor',
|
|
189
|
+
'config',
|
|
190
|
+
'memory',
|
|
191
|
+
'swarm',
|
|
192
|
+
'persona',
|
|
193
|
+
'workflow',
|
|
194
|
+
'selfheal',
|
|
195
|
+
];
|
|
196
|
+
export const MEMORY_SUBCOMMANDS = [
|
|
197
|
+
'store',
|
|
198
|
+
'search',
|
|
199
|
+
'hybrid',
|
|
200
|
+
'graph',
|
|
201
|
+
'capture',
|
|
202
|
+
'stats',
|
|
203
|
+
];
|
|
204
|
+
export const SWARM_SUBCOMMANDS = [
|
|
205
|
+
'run',
|
|
206
|
+
'status',
|
|
207
|
+
'registry',
|
|
208
|
+
'bridges',
|
|
209
|
+
];
|
|
210
|
+
export const PERSONA_SUBCOMMANDS = [
|
|
211
|
+
'create',
|
|
212
|
+
'list',
|
|
213
|
+
'activate',
|
|
214
|
+
'deactivate',
|
|
215
|
+
];
|
|
216
|
+
export const WORKFLOW_SUBCOMMANDS = [
|
|
217
|
+
'interview',
|
|
218
|
+
'evaluate',
|
|
219
|
+
'unstuck',
|
|
220
|
+
];
|
|
221
|
+
export const SELFHEAL_SUBCOMMANDS = [
|
|
222
|
+
'introspect',
|
|
223
|
+
'prescribe',
|
|
224
|
+
'evolve',
|
|
225
|
+
];
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error recovery and graceful degradation for Zouroboros subsystems.
|
|
3
|
+
*
|
|
4
|
+
* Tracks subsystem health, provides circuit-breaker behavior,
|
|
5
|
+
* and enables fallback paths when dependencies are unavailable.
|
|
6
|
+
*/
|
|
7
|
+
export type SubsystemName = 'memory' | 'ollama' | 'swarm' | 'selfheal';
|
|
8
|
+
export type SubsystemStatus = 'healthy' | 'degraded' | 'unavailable';
|
|
9
|
+
export interface SubsystemHealth {
|
|
10
|
+
name: SubsystemName;
|
|
11
|
+
status: SubsystemStatus;
|
|
12
|
+
lastCheck: string;
|
|
13
|
+
lastError?: string;
|
|
14
|
+
consecutiveFailures: number;
|
|
15
|
+
totalFailures: number;
|
|
16
|
+
totalSuccesses: number;
|
|
17
|
+
circuitOpen: boolean;
|
|
18
|
+
circuitOpensAt: number;
|
|
19
|
+
circuitResetMs: number;
|
|
20
|
+
circuitOpenedAt?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RecoveryReport {
|
|
23
|
+
timestamp: string;
|
|
24
|
+
subsystems: SubsystemHealth[];
|
|
25
|
+
overallStatus: SubsystemStatus;
|
|
26
|
+
degradedCapabilities: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Record a successful operation for a subsystem.
|
|
30
|
+
*/
|
|
31
|
+
export declare function recordSuccess(name: SubsystemName): void;
|
|
32
|
+
/**
|
|
33
|
+
* Record a failed operation for a subsystem.
|
|
34
|
+
*/
|
|
35
|
+
export declare function recordFailure(name: SubsystemName, error: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a subsystem's circuit breaker allows a call.
|
|
38
|
+
* Returns true if the call should proceed, false if circuit is open.
|
|
39
|
+
* Implements half-open: after resetMs, allows one probe call.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isAvailable(name: SubsystemName): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get the current health of a subsystem.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getHealth(name: SubsystemName): SubsystemHealth;
|
|
46
|
+
/**
|
|
47
|
+
* Get a full recovery report across all tracked subsystems.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getRecoveryReport(): RecoveryReport;
|
|
50
|
+
/**
|
|
51
|
+
* Reset a subsystem's health tracking (e.g., after manual recovery).
|
|
52
|
+
*/
|
|
53
|
+
export declare function resetHealth(name: SubsystemName): void;
|
|
54
|
+
/**
|
|
55
|
+
* Reset all subsystem health tracking.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resetAllHealth(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Configure circuit breaker thresholds for a subsystem.
|
|
60
|
+
*/
|
|
61
|
+
export declare function configureCircuitBreaker(name: SubsystemName, options: {
|
|
62
|
+
opensAt?: number;
|
|
63
|
+
resetMs?: number;
|
|
64
|
+
}): void;
|
|
65
|
+
/**
|
|
66
|
+
* Wrap an async operation with error recovery.
|
|
67
|
+
* Records success/failure and returns fallback value when circuit is open.
|
|
68
|
+
*/
|
|
69
|
+
export declare function withRecovery<T>(name: SubsystemName, operation: () => Promise<T>, fallback: T): Promise<{
|
|
70
|
+
value: T;
|
|
71
|
+
degraded: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Wrap a synchronous operation with error recovery.
|
|
75
|
+
*/
|
|
76
|
+
export declare function withRecoverySync<T>(name: SubsystemName, operation: () => T, fallback: T): {
|
|
77
|
+
value: T;
|
|
78
|
+
degraded: boolean;
|
|
79
|
+
};
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error recovery and graceful degradation for Zouroboros subsystems.
|
|
3
|
+
*
|
|
4
|
+
* Tracks subsystem health, provides circuit-breaker behavior,
|
|
5
|
+
* and enables fallback paths when dependencies are unavailable.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_CIRCUIT_OPENS_AT = 5;
|
|
8
|
+
const DEFAULT_CIRCUIT_RESET_MS = 30_000;
|
|
9
|
+
const subsystems = new Map();
|
|
10
|
+
function getOrCreate(name) {
|
|
11
|
+
if (!subsystems.has(name)) {
|
|
12
|
+
subsystems.set(name, {
|
|
13
|
+
name,
|
|
14
|
+
status: 'healthy',
|
|
15
|
+
lastCheck: new Date().toISOString(),
|
|
16
|
+
consecutiveFailures: 0,
|
|
17
|
+
totalFailures: 0,
|
|
18
|
+
totalSuccesses: 0,
|
|
19
|
+
circuitOpen: false,
|
|
20
|
+
circuitOpensAt: DEFAULT_CIRCUIT_OPENS_AT,
|
|
21
|
+
circuitResetMs: DEFAULT_CIRCUIT_RESET_MS,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return subsystems.get(name);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Record a successful operation for a subsystem.
|
|
28
|
+
*/
|
|
29
|
+
export function recordSuccess(name) {
|
|
30
|
+
const health = getOrCreate(name);
|
|
31
|
+
health.consecutiveFailures = 0;
|
|
32
|
+
health.totalSuccesses++;
|
|
33
|
+
health.status = 'healthy';
|
|
34
|
+
health.lastCheck = new Date().toISOString();
|
|
35
|
+
health.circuitOpen = false;
|
|
36
|
+
health.circuitOpenedAt = undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Record a failed operation for a subsystem.
|
|
40
|
+
*/
|
|
41
|
+
export function recordFailure(name, error) {
|
|
42
|
+
const health = getOrCreate(name);
|
|
43
|
+
health.consecutiveFailures++;
|
|
44
|
+
health.totalFailures++;
|
|
45
|
+
health.lastError = error;
|
|
46
|
+
health.lastCheck = new Date().toISOString();
|
|
47
|
+
if (health.consecutiveFailures >= health.circuitOpensAt) {
|
|
48
|
+
health.circuitOpen = true;
|
|
49
|
+
health.circuitOpenedAt = Date.now();
|
|
50
|
+
health.status = 'unavailable';
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
health.status = 'degraded';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a subsystem's circuit breaker allows a call.
|
|
58
|
+
* Returns true if the call should proceed, false if circuit is open.
|
|
59
|
+
* Implements half-open: after resetMs, allows one probe call.
|
|
60
|
+
*/
|
|
61
|
+
export function isAvailable(name) {
|
|
62
|
+
const health = getOrCreate(name);
|
|
63
|
+
if (!health.circuitOpen)
|
|
64
|
+
return true;
|
|
65
|
+
// Half-open check: allow a probe after reset interval
|
|
66
|
+
if (health.circuitOpenedAt) {
|
|
67
|
+
const elapsed = Date.now() - health.circuitOpenedAt;
|
|
68
|
+
if (elapsed >= health.circuitResetMs) {
|
|
69
|
+
return true; // allow probe — caller must recordSuccess/recordFailure
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the current health of a subsystem.
|
|
76
|
+
*/
|
|
77
|
+
export function getHealth(name) {
|
|
78
|
+
return { ...getOrCreate(name) };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get a full recovery report across all tracked subsystems.
|
|
82
|
+
*/
|
|
83
|
+
export function getRecoveryReport() {
|
|
84
|
+
const all = Array.from(subsystems.values()).map((h) => ({ ...h }));
|
|
85
|
+
const degradedCapabilities = [];
|
|
86
|
+
let overallStatus = 'healthy';
|
|
87
|
+
for (const h of all) {
|
|
88
|
+
if (h.status === 'unavailable') {
|
|
89
|
+
overallStatus = 'unavailable';
|
|
90
|
+
degradedCapabilities.push(...getDegradedCapabilities(h.name));
|
|
91
|
+
}
|
|
92
|
+
else if (h.status === 'degraded' && overallStatus !== 'unavailable') {
|
|
93
|
+
overallStatus = 'degraded';
|
|
94
|
+
degradedCapabilities.push(...getDegradedCapabilities(h.name));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
timestamp: new Date().toISOString(),
|
|
99
|
+
subsystems: all,
|
|
100
|
+
overallStatus,
|
|
101
|
+
degradedCapabilities: [...new Set(degradedCapabilities)],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reset a subsystem's health tracking (e.g., after manual recovery).
|
|
106
|
+
*/
|
|
107
|
+
export function resetHealth(name) {
|
|
108
|
+
subsystems.delete(name);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reset all subsystem health tracking.
|
|
112
|
+
*/
|
|
113
|
+
export function resetAllHealth() {
|
|
114
|
+
subsystems.clear();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Configure circuit breaker thresholds for a subsystem.
|
|
118
|
+
*/
|
|
119
|
+
export function configureCircuitBreaker(name, options) {
|
|
120
|
+
const health = getOrCreate(name);
|
|
121
|
+
if (options.opensAt !== undefined)
|
|
122
|
+
health.circuitOpensAt = options.opensAt;
|
|
123
|
+
if (options.resetMs !== undefined)
|
|
124
|
+
health.circuitResetMs = options.resetMs;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Wrap an async operation with error recovery.
|
|
128
|
+
* Records success/failure and returns fallback value when circuit is open.
|
|
129
|
+
*/
|
|
130
|
+
export async function withRecovery(name, operation, fallback) {
|
|
131
|
+
if (!isAvailable(name)) {
|
|
132
|
+
return { value: fallback, degraded: true };
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const value = await operation();
|
|
136
|
+
recordSuccess(name);
|
|
137
|
+
return { value, degraded: false };
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141
|
+
recordFailure(name, message);
|
|
142
|
+
return { value: fallback, degraded: true };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Wrap a synchronous operation with error recovery.
|
|
147
|
+
*/
|
|
148
|
+
export function withRecoverySync(name, operation, fallback) {
|
|
149
|
+
if (!isAvailable(name)) {
|
|
150
|
+
return { value: fallback, degraded: true };
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const value = operation();
|
|
154
|
+
recordSuccess(name);
|
|
155
|
+
return { value, degraded: false };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
159
|
+
recordFailure(name, message);
|
|
160
|
+
return { value: fallback, degraded: true };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function getDegradedCapabilities(name) {
|
|
164
|
+
const map = {
|
|
165
|
+
memory: ['fact storage', 'episodic memory', 'memory search'],
|
|
166
|
+
ollama: ['vector search', 'semantic similarity', 'HyDE expansion'],
|
|
167
|
+
swarm: ['multi-agent orchestration', 'DAG execution'],
|
|
168
|
+
selfheal: ['auto-introspection', 'self-improvement'],
|
|
169
|
+
};
|
|
170
|
+
return map[name] ?? [];
|
|
171
|
+
}
|