@cogitator-ai/self-modifying 0.1.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 +714 -0
- package/dist/architecture-evolution/capability-analyzer.d.ts +32 -0
- package/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -0
- package/dist/architecture-evolution/capability-analyzer.js +264 -0
- package/dist/architecture-evolution/capability-analyzer.js.map +1 -0
- package/dist/architecture-evolution/evolution-strategy.d.ts +29 -0
- package/dist/architecture-evolution/evolution-strategy.d.ts.map +1 -0
- package/dist/architecture-evolution/evolution-strategy.js +176 -0
- package/dist/architecture-evolution/evolution-strategy.js.map +1 -0
- package/dist/architecture-evolution/index.d.ts +5 -0
- package/dist/architecture-evolution/index.d.ts.map +1 -0
- package/dist/architecture-evolution/index.js +5 -0
- package/dist/architecture-evolution/index.js.map +1 -0
- package/dist/architecture-evolution/parameter-optimizer.d.ts +67 -0
- package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -0
- package/dist/architecture-evolution/parameter-optimizer.js +341 -0
- package/dist/architecture-evolution/parameter-optimizer.js.map +1 -0
- package/dist/architecture-evolution/prompts.d.ts +33 -0
- package/dist/architecture-evolution/prompts.d.ts.map +1 -0
- package/dist/architecture-evolution/prompts.js +169 -0
- package/dist/architecture-evolution/prompts.js.map +1 -0
- package/dist/constraints/index.d.ts +4 -0
- package/dist/constraints/index.d.ts.map +1 -0
- package/dist/constraints/index.js +4 -0
- package/dist/constraints/index.js.map +1 -0
- package/dist/constraints/modification-validator.d.ts +26 -0
- package/dist/constraints/modification-validator.d.ts.map +1 -0
- package/dist/constraints/modification-validator.js +313 -0
- package/dist/constraints/modification-validator.js.map +1 -0
- package/dist/constraints/rollback-manager.d.ts +52 -0
- package/dist/constraints/rollback-manager.d.ts.map +1 -0
- package/dist/constraints/rollback-manager.js +113 -0
- package/dist/constraints/rollback-manager.js.map +1 -0
- package/dist/constraints/safety-constraints.d.ts +11 -0
- package/dist/constraints/safety-constraints.d.ts.map +1 -0
- package/dist/constraints/safety-constraints.js +78 -0
- package/dist/constraints/safety-constraints.js.map +1 -0
- package/dist/events/event-emitter.d.ts +12 -0
- package/dist/events/event-emitter.d.ts.map +1 -0
- package/dist/events/event-emitter.js +43 -0
- package/dist/events/event-emitter.js.map +1 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/meta-reasoning/index.d.ts +5 -0
- package/dist/meta-reasoning/index.d.ts.map +1 -0
- package/dist/meta-reasoning/index.js +5 -0
- package/dist/meta-reasoning/index.js.map +1 -0
- package/dist/meta-reasoning/meta-reasoner.d.ts +53 -0
- package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -0
- package/dist/meta-reasoning/meta-reasoner.js +261 -0
- package/dist/meta-reasoning/meta-reasoner.js.map +1 -0
- package/dist/meta-reasoning/observation-collector.d.ts +37 -0
- package/dist/meta-reasoning/observation-collector.d.ts.map +1 -0
- package/dist/meta-reasoning/observation-collector.js +123 -0
- package/dist/meta-reasoning/observation-collector.js.map +1 -0
- package/dist/meta-reasoning/prompts.d.ts +31 -0
- package/dist/meta-reasoning/prompts.d.ts.map +1 -0
- package/dist/meta-reasoning/prompts.js +96 -0
- package/dist/meta-reasoning/prompts.js.map +1 -0
- package/dist/meta-reasoning/strategy-selector.d.ts +27 -0
- package/dist/meta-reasoning/strategy-selector.d.ts.map +1 -0
- package/dist/meta-reasoning/strategy-selector.js +138 -0
- package/dist/meta-reasoning/strategy-selector.js.map +1 -0
- package/dist/self-modifying-agent.d.ts +61 -0
- package/dist/self-modifying-agent.d.ts.map +1 -0
- package/dist/self-modifying-agent.js +449 -0
- package/dist/self-modifying-agent.js.map +1 -0
- package/dist/tool-generation/gap-analyzer.d.ts +25 -0
- package/dist/tool-generation/gap-analyzer.d.ts.map +1 -0
- package/dist/tool-generation/gap-analyzer.js +153 -0
- package/dist/tool-generation/gap-analyzer.js.map +1 -0
- package/dist/tool-generation/generated-tool-store.d.ts +51 -0
- package/dist/tool-generation/generated-tool-store.d.ts.map +1 -0
- package/dist/tool-generation/generated-tool-store.js +195 -0
- package/dist/tool-generation/generated-tool-store.js.map +1 -0
- package/dist/tool-generation/index.d.ts +7 -0
- package/dist/tool-generation/index.d.ts.map +1 -0
- package/dist/tool-generation/index.js +7 -0
- package/dist/tool-generation/index.js.map +1 -0
- package/dist/tool-generation/prompts.d.ts +28 -0
- package/dist/tool-generation/prompts.d.ts.map +1 -0
- package/dist/tool-generation/prompts.js +269 -0
- package/dist/tool-generation/prompts.js.map +1 -0
- package/dist/tool-generation/tool-generator.d.ts +29 -0
- package/dist/tool-generation/tool-generator.d.ts.map +1 -0
- package/dist/tool-generation/tool-generator.js +169 -0
- package/dist/tool-generation/tool-generator.js.map +1 -0
- package/dist/tool-generation/tool-sandbox.d.ts +31 -0
- package/dist/tool-generation/tool-sandbox.d.ts.map +1 -0
- package/dist/tool-generation/tool-sandbox.js +240 -0
- package/dist/tool-generation/tool-sandbox.js.map +1 -0
- package/dist/tool-generation/tool-validator.d.ts +32 -0
- package/dist/tool-generation/tool-validator.d.ts.map +1 -0
- package/dist/tool-generation/tool-validator.js +304 -0
- package/dist/tool-generation/tool-validator.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/llm-helper.d.ts +6 -0
- package/dist/utils/llm-helper.d.ts.map +1 -0
- package/dist/utils/llm-helper.js +18 -0
- package/dist/utils/llm-helper.js.map +1 -0
- package/package.json +61 -0
- package/src/__tests__/architecture-evolution.test.ts +368 -0
- package/src/__tests__/constraints.test.ts +266 -0
- package/src/__tests__/index.test.ts +99 -0
- package/src/__tests__/meta-reasoning.test.ts +343 -0
- package/src/__tests__/tool-generation.test.ts +455 -0
- package/src/architecture-evolution/capability-analyzer.ts +337 -0
- package/src/architecture-evolution/evolution-strategy.ts +224 -0
- package/src/architecture-evolution/index.ts +26 -0
- package/src/architecture-evolution/parameter-optimizer.ts +489 -0
- package/src/architecture-evolution/prompts.ts +216 -0
- package/src/constraints/index.ts +23 -0
- package/src/constraints/modification-validator.ts +402 -0
- package/src/constraints/rollback-manager.ts +173 -0
- package/src/constraints/safety-constraints.ts +103 -0
- package/src/events/event-emitter.ts +62 -0
- package/src/events/index.ts +1 -0
- package/src/index.ts +112 -0
- package/src/meta-reasoning/index.ts +24 -0
- package/src/meta-reasoning/meta-reasoner.ts +381 -0
- package/src/meta-reasoning/observation-collector.ts +161 -0
- package/src/meta-reasoning/prompts.ts +131 -0
- package/src/meta-reasoning/strategy-selector.ts +179 -0
- package/src/self-modifying-agent.ts +585 -0
- package/src/tool-generation/gap-analyzer.ts +234 -0
- package/src/tool-generation/generated-tool-store.ts +268 -0
- package/src/tool-generation/index.ts +19 -0
- package/src/tool-generation/prompts.ts +308 -0
- package/src/tool-generation/tool-generator.ts +243 -0
- package/src/tool-generation/tool-sandbox.ts +332 -0
- package/src/tool-generation/tool-validator.ts +365 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/llm-helper.ts +24 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export {
|
|
2
|
+
DEFAULT_SAFETY_CONSTRAINTS,
|
|
3
|
+
DEFAULT_CAPABILITY_CONSTRAINTS,
|
|
4
|
+
DEFAULT_RESOURCE_CONSTRAINTS,
|
|
5
|
+
createDefaultConstraints,
|
|
6
|
+
mergeSafetyConstraints,
|
|
7
|
+
mergeCapabilityConstraints,
|
|
8
|
+
mergeResourceConstraints,
|
|
9
|
+
mergeConstraints,
|
|
10
|
+
} from './safety-constraints';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
RollbackManager,
|
|
14
|
+
InMemoryCheckpointStore,
|
|
15
|
+
type CheckpointStore,
|
|
16
|
+
type RollbackManagerOptions,
|
|
17
|
+
type CheckpointDiff,
|
|
18
|
+
} from './rollback-manager';
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
ModificationValidator,
|
|
22
|
+
type ModificationValidatorOptions,
|
|
23
|
+
} from './modification-validator';
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ModificationRequest,
|
|
3
|
+
ModificationValidationResult,
|
|
4
|
+
ModificationConstraints,
|
|
5
|
+
ConstraintCheckResult,
|
|
6
|
+
SafetyConstraint,
|
|
7
|
+
CapabilityConstraint,
|
|
8
|
+
ResourceConstraint,
|
|
9
|
+
CustomConstraint,
|
|
10
|
+
ConstraintRule,
|
|
11
|
+
} from '@cogitator-ai/types';
|
|
12
|
+
import { createDefaultConstraints, mergeConstraints } from './safety-constraints';
|
|
13
|
+
|
|
14
|
+
export interface ModificationValidatorOptions {
|
|
15
|
+
constraints?: Partial<ModificationConstraints>;
|
|
16
|
+
strictMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ModificationValidator {
|
|
20
|
+
private constraints: ModificationConstraints;
|
|
21
|
+
private strictMode: boolean;
|
|
22
|
+
|
|
23
|
+
constructor(options: ModificationValidatorOptions = {}) {
|
|
24
|
+
this.constraints = mergeConstraints(
|
|
25
|
+
createDefaultConstraints(),
|
|
26
|
+
options.constraints ?? {}
|
|
27
|
+
);
|
|
28
|
+
this.strictMode = options.strictMode ?? false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async validate(request: ModificationRequest): Promise<ModificationValidationResult> {
|
|
32
|
+
const results: ConstraintCheckResult[] = [];
|
|
33
|
+
const errors: string[] = [];
|
|
34
|
+
const warnings: string[] = [];
|
|
35
|
+
|
|
36
|
+
const safetyResults = await this.checkSafetyConstraints(request);
|
|
37
|
+
results.push(...safetyResults);
|
|
38
|
+
|
|
39
|
+
const capabilityResults = await this.checkCapabilityConstraints(request);
|
|
40
|
+
results.push(...capabilityResults);
|
|
41
|
+
|
|
42
|
+
const resourceResults = await this.checkResourceConstraints(request);
|
|
43
|
+
results.push(...resourceResults);
|
|
44
|
+
|
|
45
|
+
const customResults = await this.checkCustomConstraints(request);
|
|
46
|
+
results.push(...customResults);
|
|
47
|
+
|
|
48
|
+
for (const result of results) {
|
|
49
|
+
if (!result.satisfied) {
|
|
50
|
+
if (result.severity === 'critical' || result.severity === 'error') {
|
|
51
|
+
errors.push(result.message ?? `Constraint ${result.constraintName} violated`);
|
|
52
|
+
} else {
|
|
53
|
+
warnings.push(result.message ?? `Constraint ${result.constraintName} warning`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const hasCriticalViolation = results.some(
|
|
59
|
+
(r) => !r.satisfied && r.severity === 'critical'
|
|
60
|
+
);
|
|
61
|
+
const hasErrorViolation = results.some(
|
|
62
|
+
(r) => !r.satisfied && r.severity === 'error'
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const valid = this.strictMode
|
|
66
|
+
? results.every((r) => r.satisfied)
|
|
67
|
+
: !hasCriticalViolation && !hasErrorViolation;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
valid,
|
|
71
|
+
constraintResults: results,
|
|
72
|
+
errors,
|
|
73
|
+
warnings,
|
|
74
|
+
rollbackRequired: hasCriticalViolation,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async checkSafetyConstraints(
|
|
79
|
+
request: ModificationRequest
|
|
80
|
+
): Promise<ConstraintCheckResult[]> {
|
|
81
|
+
const results: ConstraintCheckResult[] = [];
|
|
82
|
+
|
|
83
|
+
for (const constraint of this.constraints.safety) {
|
|
84
|
+
const satisfied = this.evaluateSafetyRule(constraint.rule, request);
|
|
85
|
+
results.push({
|
|
86
|
+
constraintId: constraint.id,
|
|
87
|
+
constraintName: constraint.name,
|
|
88
|
+
satisfied,
|
|
89
|
+
severity: constraint.severity,
|
|
90
|
+
message: satisfied ? undefined : constraint.description,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private evaluateSafetyRule(rule: ConstraintRule, request: ModificationRequest): boolean {
|
|
98
|
+
if (typeof rule === 'string') {
|
|
99
|
+
return this.evaluateExpression(rule, request.payload as Record<string, unknown>);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const payload = request.payload as Record<string, unknown>;
|
|
103
|
+
|
|
104
|
+
switch (rule.type) {
|
|
105
|
+
case 'invariant':
|
|
106
|
+
return this.evaluateExpression(rule.expression ?? '', payload);
|
|
107
|
+
case 'precondition':
|
|
108
|
+
return this.evaluateExpression(rule.expression ?? '', payload);
|
|
109
|
+
case 'postcondition':
|
|
110
|
+
return true;
|
|
111
|
+
case 'temporal':
|
|
112
|
+
if (rule.pattern?.source === 'never') {
|
|
113
|
+
return !this.evaluateExpression(rule.expression ?? '', payload);
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
default:
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private evaluateExpression(expression: string, context: Record<string, unknown>): boolean {
|
|
122
|
+
const parts = expression.split(/\s+(AND|OR)\s+/i);
|
|
123
|
+
const conditions: boolean[] = [];
|
|
124
|
+
const operators: string[] = [];
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < parts.length; i++) {
|
|
127
|
+
const part = parts[i].trim();
|
|
128
|
+
if (part.toUpperCase() === 'AND' || part.toUpperCase() === 'OR') {
|
|
129
|
+
operators.push(part.toUpperCase());
|
|
130
|
+
} else {
|
|
131
|
+
conditions.push(this.evaluateSimpleCondition(part, context));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (conditions.length === 0) return true;
|
|
136
|
+
|
|
137
|
+
let result = conditions[0];
|
|
138
|
+
for (let i = 0; i < operators.length; i++) {
|
|
139
|
+
if (operators[i] === 'AND') {
|
|
140
|
+
result = result && conditions[i + 1];
|
|
141
|
+
} else {
|
|
142
|
+
result = result || conditions[i + 1];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private evaluateSimpleCondition(
|
|
150
|
+
condition: string,
|
|
151
|
+
context: Record<string, unknown>
|
|
152
|
+
): boolean {
|
|
153
|
+
const operators = ['<=', '>=', '<', '>', '!=', '='];
|
|
154
|
+
for (const op of operators) {
|
|
155
|
+
const idx = condition.indexOf(op);
|
|
156
|
+
if (idx !== -1) {
|
|
157
|
+
const left = condition.substring(0, idx).trim();
|
|
158
|
+
const right = condition.substring(idx + op.length).trim();
|
|
159
|
+
const leftValue = this.resolveValue(left, context);
|
|
160
|
+
const rightValue = this.resolveValue(right, context);
|
|
161
|
+
|
|
162
|
+
switch (op) {
|
|
163
|
+
case '=':
|
|
164
|
+
return leftValue === rightValue;
|
|
165
|
+
case '!=':
|
|
166
|
+
return leftValue !== rightValue;
|
|
167
|
+
case '<':
|
|
168
|
+
return Number(leftValue) < Number(rightValue);
|
|
169
|
+
case '>':
|
|
170
|
+
return Number(leftValue) > Number(rightValue);
|
|
171
|
+
case '<=':
|
|
172
|
+
return Number(leftValue) <= Number(rightValue);
|
|
173
|
+
case '>=':
|
|
174
|
+
return Number(leftValue) >= Number(rightValue);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const value = this.resolveValue(condition, context);
|
|
180
|
+
return Boolean(value);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private resolveValue(expr: string, context: Record<string, unknown>): unknown {
|
|
184
|
+
if (expr === 'true') return true;
|
|
185
|
+
if (expr === 'false') return false;
|
|
186
|
+
if (/^\d+(\.\d+)?$/.test(expr)) return parseFloat(expr);
|
|
187
|
+
|
|
188
|
+
const keys = expr.split('.');
|
|
189
|
+
let current: unknown = context;
|
|
190
|
+
for (const key of keys) {
|
|
191
|
+
if (current && typeof current === 'object') {
|
|
192
|
+
current = (current as Record<string, unknown>)[key];
|
|
193
|
+
} else {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return current;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private async checkCapabilityConstraints(
|
|
201
|
+
request: ModificationRequest
|
|
202
|
+
): Promise<ConstraintCheckResult[]> {
|
|
203
|
+
const results: ConstraintCheckResult[] = [];
|
|
204
|
+
|
|
205
|
+
if (request.type !== 'tool_generation') {
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const payload = request.payload as { category?: string; complexity?: number };
|
|
210
|
+
|
|
211
|
+
for (const constraint of this.constraints.capability) {
|
|
212
|
+
let satisfied = true;
|
|
213
|
+
let message: string | undefined;
|
|
214
|
+
|
|
215
|
+
if (payload.category) {
|
|
216
|
+
if (constraint.forbidden?.includes(payload.category)) {
|
|
217
|
+
satisfied = false;
|
|
218
|
+
message = `Category '${payload.category}' is forbidden`;
|
|
219
|
+
} else if (
|
|
220
|
+
constraint.allowed?.length &&
|
|
221
|
+
!constraint.allowed.includes(payload.category)
|
|
222
|
+
) {
|
|
223
|
+
satisfied = false;
|
|
224
|
+
message = `Category '${payload.category}' is not in allowed list`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
satisfied &&
|
|
230
|
+
constraint.maxComplexity &&
|
|
231
|
+
payload.complexity &&
|
|
232
|
+
payload.complexity > constraint.maxComplexity
|
|
233
|
+
) {
|
|
234
|
+
satisfied = false;
|
|
235
|
+
message = `Complexity ${payload.complexity} exceeds max ${constraint.maxComplexity}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
results.push({
|
|
239
|
+
constraintId: constraint.id,
|
|
240
|
+
constraintName: constraint.name,
|
|
241
|
+
satisfied,
|
|
242
|
+
severity: 'error',
|
|
243
|
+
message,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async checkResourceConstraints(
|
|
251
|
+
request: ModificationRequest
|
|
252
|
+
): Promise<ConstraintCheckResult[]> {
|
|
253
|
+
const results: ConstraintCheckResult[] = [];
|
|
254
|
+
const payload = request.payload as {
|
|
255
|
+
tokensUsed?: number;
|
|
256
|
+
cost?: number;
|
|
257
|
+
activeTools?: number;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
for (const constraint of this.constraints.resource) {
|
|
261
|
+
let satisfied = true;
|
|
262
|
+
let message: string | undefined;
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
constraint.maxTokensPerRun &&
|
|
266
|
+
payload.tokensUsed &&
|
|
267
|
+
payload.tokensUsed > constraint.maxTokensPerRun
|
|
268
|
+
) {
|
|
269
|
+
satisfied = false;
|
|
270
|
+
message = `Tokens ${payload.tokensUsed} exceeds max ${constraint.maxTokensPerRun}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
satisfied &&
|
|
275
|
+
constraint.maxCostPerRun &&
|
|
276
|
+
payload.cost &&
|
|
277
|
+
payload.cost > constraint.maxCostPerRun
|
|
278
|
+
) {
|
|
279
|
+
satisfied = false;
|
|
280
|
+
message = `Cost ${payload.cost} exceeds max ${constraint.maxCostPerRun}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (
|
|
284
|
+
satisfied &&
|
|
285
|
+
constraint.maxToolsActive &&
|
|
286
|
+
payload.activeTools &&
|
|
287
|
+
payload.activeTools > constraint.maxToolsActive
|
|
288
|
+
) {
|
|
289
|
+
satisfied = false;
|
|
290
|
+
message = `Active tools ${payload.activeTools} exceeds max ${constraint.maxToolsActive}`;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
results.push({
|
|
294
|
+
constraintId: constraint.id,
|
|
295
|
+
constraintName: constraint.name,
|
|
296
|
+
satisfied,
|
|
297
|
+
severity: 'error',
|
|
298
|
+
message,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private async checkCustomConstraints(
|
|
306
|
+
request: ModificationRequest
|
|
307
|
+
): Promise<ConstraintCheckResult[]> {
|
|
308
|
+
const results: ConstraintCheckResult[] = [];
|
|
309
|
+
|
|
310
|
+
for (const constraint of this.constraints.custom ?? []) {
|
|
311
|
+
let satisfied: boolean;
|
|
312
|
+
try {
|
|
313
|
+
satisfied = await constraint.predicate(request);
|
|
314
|
+
} catch {
|
|
315
|
+
satisfied = false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
results.push({
|
|
319
|
+
constraintId: constraint.id,
|
|
320
|
+
constraintName: constraint.name,
|
|
321
|
+
satisfied,
|
|
322
|
+
severity: 'error',
|
|
323
|
+
message: satisfied ? undefined : constraint.description,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return results;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
addSafetyConstraint(constraint: SafetyConstraint): void {
|
|
331
|
+
const idx = this.constraints.safety.findIndex((c) => c.id === constraint.id);
|
|
332
|
+
if (idx >= 0) {
|
|
333
|
+
this.constraints.safety[idx] = constraint;
|
|
334
|
+
} else {
|
|
335
|
+
this.constraints.safety.push(constraint);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
addCapabilityConstraint(constraint: CapabilityConstraint): void {
|
|
340
|
+
const idx = this.constraints.capability.findIndex((c) => c.id === constraint.id);
|
|
341
|
+
if (idx >= 0) {
|
|
342
|
+
this.constraints.capability[idx] = constraint;
|
|
343
|
+
} else {
|
|
344
|
+
this.constraints.capability.push(constraint);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
addResourceConstraint(constraint: ResourceConstraint): void {
|
|
349
|
+
const idx = this.constraints.resource.findIndex((c) => c.id === constraint.id);
|
|
350
|
+
if (idx >= 0) {
|
|
351
|
+
this.constraints.resource[idx] = constraint;
|
|
352
|
+
} else {
|
|
353
|
+
this.constraints.resource.push(constraint);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
addCustomConstraint(constraint: CustomConstraint): void {
|
|
358
|
+
if (!this.constraints.custom) {
|
|
359
|
+
this.constraints.custom = [];
|
|
360
|
+
}
|
|
361
|
+
const idx = this.constraints.custom.findIndex((c) => c.id === constraint.id);
|
|
362
|
+
if (idx >= 0) {
|
|
363
|
+
this.constraints.custom[idx] = constraint;
|
|
364
|
+
} else {
|
|
365
|
+
this.constraints.custom.push(constraint);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
removeConstraint(id: string): boolean {
|
|
370
|
+
const safetyIdx = this.constraints.safety.findIndex((c) => c.id === id);
|
|
371
|
+
if (safetyIdx >= 0) {
|
|
372
|
+
this.constraints.safety.splice(safetyIdx, 1);
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const capIdx = this.constraints.capability.findIndex((c) => c.id === id);
|
|
377
|
+
if (capIdx >= 0) {
|
|
378
|
+
this.constraints.capability.splice(capIdx, 1);
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const resIdx = this.constraints.resource.findIndex((c) => c.id === id);
|
|
383
|
+
if (resIdx >= 0) {
|
|
384
|
+
this.constraints.resource.splice(resIdx, 1);
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (this.constraints.custom) {
|
|
389
|
+
const customIdx = this.constraints.custom.findIndex((c) => c.id === id);
|
|
390
|
+
if (customIdx >= 0) {
|
|
391
|
+
this.constraints.custom.splice(customIdx, 1);
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
getConstraints(): ModificationConstraints {
|
|
400
|
+
return { ...this.constraints };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import type {
|
|
3
|
+
ModificationCheckpoint,
|
|
4
|
+
AppliedModification,
|
|
5
|
+
AgentConfig,
|
|
6
|
+
Tool,
|
|
7
|
+
} from '@cogitator-ai/types';
|
|
8
|
+
|
|
9
|
+
export interface CheckpointStore {
|
|
10
|
+
save(checkpoint: ModificationCheckpoint): Promise<void>;
|
|
11
|
+
get(id: string): Promise<ModificationCheckpoint | null>;
|
|
12
|
+
list(agentId: string): Promise<ModificationCheckpoint[]>;
|
|
13
|
+
delete(id: string): Promise<boolean>;
|
|
14
|
+
prune(agentId: string, keepCount: number): Promise<number>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class InMemoryCheckpointStore implements CheckpointStore {
|
|
18
|
+
private checkpoints = new Map<string, ModificationCheckpoint>();
|
|
19
|
+
private agentIndex = new Map<string, Set<string>>();
|
|
20
|
+
|
|
21
|
+
async save(checkpoint: ModificationCheckpoint): Promise<void> {
|
|
22
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
23
|
+
if (!this.agentIndex.has(checkpoint.agentId)) {
|
|
24
|
+
this.agentIndex.set(checkpoint.agentId, new Set());
|
|
25
|
+
}
|
|
26
|
+
this.agentIndex.get(checkpoint.agentId)!.add(checkpoint.id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async get(id: string): Promise<ModificationCheckpoint | null> {
|
|
30
|
+
return this.checkpoints.get(id) ?? null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async list(agentId: string): Promise<ModificationCheckpoint[]> {
|
|
34
|
+
const ids = this.agentIndex.get(agentId);
|
|
35
|
+
if (!ids) return [];
|
|
36
|
+
return [...ids]
|
|
37
|
+
.map((id) => this.checkpoints.get(id)!)
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async delete(id: string): Promise<boolean> {
|
|
43
|
+
const checkpoint = this.checkpoints.get(id);
|
|
44
|
+
if (!checkpoint) return false;
|
|
45
|
+
this.checkpoints.delete(id);
|
|
46
|
+
this.agentIndex.get(checkpoint.agentId)?.delete(id);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async prune(agentId: string, keepCount: number): Promise<number> {
|
|
51
|
+
const checkpoints = await this.list(agentId);
|
|
52
|
+
const toDelete = checkpoints.slice(keepCount);
|
|
53
|
+
for (const cp of toDelete) {
|
|
54
|
+
await this.delete(cp.id);
|
|
55
|
+
}
|
|
56
|
+
return toDelete.length;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RollbackManagerOptions {
|
|
61
|
+
maxCheckpoints: number;
|
|
62
|
+
checkpointStore?: CheckpointStore;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface CheckpointDiff {
|
|
66
|
+
configChanges: Array<{ key: string; before: unknown; after: unknown }>;
|
|
67
|
+
toolsAdded: string[];
|
|
68
|
+
toolsRemoved: string[];
|
|
69
|
+
modificationsApplied: AppliedModification[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class RollbackManager {
|
|
73
|
+
private maxCheckpoints: number;
|
|
74
|
+
private store: CheckpointStore;
|
|
75
|
+
|
|
76
|
+
constructor(options: RollbackManagerOptions) {
|
|
77
|
+
this.maxCheckpoints = options.maxCheckpoints;
|
|
78
|
+
this.store = options.checkpointStore ?? new InMemoryCheckpointStore();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async createCheckpoint(
|
|
82
|
+
agentId: string,
|
|
83
|
+
agentConfig: AgentConfig,
|
|
84
|
+
tools: Tool[],
|
|
85
|
+
modifications: AppliedModification[]
|
|
86
|
+
): Promise<ModificationCheckpoint> {
|
|
87
|
+
const checkpoint: ModificationCheckpoint = {
|
|
88
|
+
id: `ckpt_${nanoid(12)}`,
|
|
89
|
+
agentId,
|
|
90
|
+
timestamp: new Date(),
|
|
91
|
+
agentConfig: { ...agentConfig },
|
|
92
|
+
tools: tools.map((t) => ({ ...t })),
|
|
93
|
+
modifications: [...modifications],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
await this.store.save(checkpoint);
|
|
97
|
+
await this.store.prune(agentId, this.maxCheckpoints);
|
|
98
|
+
|
|
99
|
+
return checkpoint;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async getCheckpoint(id: string): Promise<ModificationCheckpoint | null> {
|
|
103
|
+
return this.store.get(id);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async listCheckpoints(agentId: string): Promise<ModificationCheckpoint[]> {
|
|
107
|
+
return this.store.list(agentId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getLatestCheckpoint(agentId: string): Promise<ModificationCheckpoint | null> {
|
|
111
|
+
const checkpoints = await this.store.list(agentId);
|
|
112
|
+
return checkpoints[0] ?? null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async rollbackTo(
|
|
116
|
+
checkpointId: string
|
|
117
|
+
): Promise<{ agentConfig: AgentConfig; tools: Tool[] } | null> {
|
|
118
|
+
const checkpoint = await this.store.get(checkpointId);
|
|
119
|
+
if (!checkpoint) return null;
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
agentConfig: checkpoint.agentConfig as unknown as AgentConfig,
|
|
123
|
+
tools: checkpoint.tools.map((t) => ({ ...t })),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async rollbackToLatest(
|
|
128
|
+
agentId: string
|
|
129
|
+
): Promise<{ agentConfig: AgentConfig; tools: Tool[] } | null> {
|
|
130
|
+
const latest = await this.getLatestCheckpoint(agentId);
|
|
131
|
+
if (!latest) return null;
|
|
132
|
+
return this.rollbackTo(latest.id);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
compareCheckpoints(
|
|
136
|
+
checkpointA: ModificationCheckpoint,
|
|
137
|
+
checkpointB: ModificationCheckpoint
|
|
138
|
+
): CheckpointDiff {
|
|
139
|
+
const configChanges: CheckpointDiff['configChanges'] = [];
|
|
140
|
+
const keysA = Object.keys(checkpointA.agentConfig) as (keyof AgentConfig)[];
|
|
141
|
+
const keysB = Object.keys(checkpointB.agentConfig) as (keyof AgentConfig)[];
|
|
142
|
+
const allKeys = new Set([...keysA, ...keysB]);
|
|
143
|
+
|
|
144
|
+
for (const key of allKeys) {
|
|
145
|
+
const valA = checkpointA.agentConfig[key];
|
|
146
|
+
const valB = checkpointB.agentConfig[key];
|
|
147
|
+
if (JSON.stringify(valA) !== JSON.stringify(valB)) {
|
|
148
|
+
configChanges.push({ key, before: valA, after: valB });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const toolsA = new Set(checkpointA.tools.map((t) => t.name));
|
|
153
|
+
const toolsB = new Set(checkpointB.tools.map((t) => t.name));
|
|
154
|
+
|
|
155
|
+
const toolsAdded = [...toolsB].filter((t) => !toolsA.has(t));
|
|
156
|
+
const toolsRemoved = [...toolsA].filter((t) => !toolsB.has(t));
|
|
157
|
+
|
|
158
|
+
const modsBefore = new Set(checkpointA.modifications.map((m) => m.id));
|
|
159
|
+
const modificationsApplied = checkpointB.modifications.filter(
|
|
160
|
+
(m) => !modsBefore.has(m.id)
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return { configChanges, toolsAdded, toolsRemoved, modificationsApplied };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async pruneCheckpoints(agentId: string, keepCount?: number): Promise<number> {
|
|
167
|
+
return this.store.prune(agentId, keepCount ?? this.maxCheckpoints);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async deleteCheckpoint(id: string): Promise<boolean> {
|
|
171
|
+
return this.store.delete(id);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SafetyConstraint,
|
|
3
|
+
CapabilityConstraint,
|
|
4
|
+
ResourceConstraint,
|
|
5
|
+
ModificationConstraints,
|
|
6
|
+
} from '@cogitator-ai/types';
|
|
7
|
+
import { DEFAULT_SAFETY_CONSTRAINTS } from '@cogitator-ai/types';
|
|
8
|
+
|
|
9
|
+
export { DEFAULT_SAFETY_CONSTRAINTS };
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_CAPABILITY_CONSTRAINTS: CapabilityConstraint[] = [
|
|
12
|
+
{
|
|
13
|
+
id: 'allowed_tool_categories',
|
|
14
|
+
name: 'Allowed Tool Categories',
|
|
15
|
+
type: 'tool_category',
|
|
16
|
+
description: 'Allowed and forbidden tool categories',
|
|
17
|
+
allowed: ['math', 'text', 'utility', 'data'],
|
|
18
|
+
forbidden: ['system', 'network', 'file'],
|
|
19
|
+
maxComplexity: 100,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_RESOURCE_CONSTRAINTS: ResourceConstraint[] = [
|
|
24
|
+
{
|
|
25
|
+
id: 'default_resource_limits',
|
|
26
|
+
name: 'Default Resource Limits',
|
|
27
|
+
resource: 'runtime',
|
|
28
|
+
description: 'Default runtime resource limits',
|
|
29
|
+
maxMemory: 128,
|
|
30
|
+
maxTokensPerRun: 100000,
|
|
31
|
+
maxCostPerRun: 1.0,
|
|
32
|
+
maxToolsActive: 20,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export function createDefaultConstraints(): ModificationConstraints {
|
|
37
|
+
return {
|
|
38
|
+
safety: [...DEFAULT_SAFETY_CONSTRAINTS],
|
|
39
|
+
capability: [...DEFAULT_CAPABILITY_CONSTRAINTS],
|
|
40
|
+
resource: [...DEFAULT_RESOURCE_CONSTRAINTS],
|
|
41
|
+
custom: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function mergeSafetyConstraints(
|
|
46
|
+
base: SafetyConstraint[],
|
|
47
|
+
additions: SafetyConstraint[]
|
|
48
|
+
): SafetyConstraint[] {
|
|
49
|
+
const result = new Map<string, SafetyConstraint>();
|
|
50
|
+
for (const c of base) {
|
|
51
|
+
result.set(c.id, c);
|
|
52
|
+
}
|
|
53
|
+
for (const c of additions) {
|
|
54
|
+
result.set(c.id, c);
|
|
55
|
+
}
|
|
56
|
+
return [...result.values()];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function mergeCapabilityConstraints(
|
|
60
|
+
base: CapabilityConstraint[],
|
|
61
|
+
additions: CapabilityConstraint[]
|
|
62
|
+
): CapabilityConstraint[] {
|
|
63
|
+
const result = new Map<string, CapabilityConstraint>();
|
|
64
|
+
for (const c of base) {
|
|
65
|
+
result.set(c.id, c);
|
|
66
|
+
}
|
|
67
|
+
for (const c of additions) {
|
|
68
|
+
result.set(c.id, c);
|
|
69
|
+
}
|
|
70
|
+
return [...result.values()];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function mergeResourceConstraints(
|
|
74
|
+
base: ResourceConstraint[],
|
|
75
|
+
additions: ResourceConstraint[]
|
|
76
|
+
): ResourceConstraint[] {
|
|
77
|
+
const result = new Map<string, ResourceConstraint>();
|
|
78
|
+
for (const c of base) {
|
|
79
|
+
result.set(c.id, c);
|
|
80
|
+
}
|
|
81
|
+
for (const c of additions) {
|
|
82
|
+
result.set(c.id, c);
|
|
83
|
+
}
|
|
84
|
+
return [...result.values()];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function mergeConstraints(
|
|
88
|
+
base: ModificationConstraints,
|
|
89
|
+
additions: Partial<ModificationConstraints>
|
|
90
|
+
): ModificationConstraints {
|
|
91
|
+
return {
|
|
92
|
+
safety: additions.safety
|
|
93
|
+
? mergeSafetyConstraints(base.safety, additions.safety)
|
|
94
|
+
: base.safety,
|
|
95
|
+
capability: additions.capability
|
|
96
|
+
? mergeCapabilityConstraints(base.capability, additions.capability)
|
|
97
|
+
: base.capability,
|
|
98
|
+
resource: additions.resource
|
|
99
|
+
? mergeResourceConstraints(base.resource, additions.resource)
|
|
100
|
+
: base.resource,
|
|
101
|
+
custom: [...(base.custom ?? []), ...(additions.custom ?? [])],
|
|
102
|
+
};
|
|
103
|
+
}
|