tribunal-kit 9.2.6 → 9.2.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.
- package/.agent/ARCHITECTURE.md +2 -2
- package/.agent/history/architecture-intelligence.html +1487 -401
- package/.agent/history/architecture-model.json +9365 -5697
- package/.agent/history/architecture-verification.json +37 -9
- package/.agent/routing_index.json +2069 -3340
- package/.agent/rules/GEMINI.md +1 -1
- package/.agent/scripts/adaptive_execution.js +511 -0
- package/.agent/scripts/architecture_diff.js +303 -260
- package/.agent/scripts/architecture_extractor.js +792 -753
- package/.agent/scripts/architecture_health.js +293 -256
- package/.agent/scripts/architecture_intelligence.js +3100 -0
- package/.agent/scripts/architecture_verifier.js +303 -270
- package/.agent/scripts/architecture_visualizer.js +880 -873
- package/.agent/scripts/blast_radius_engine.js +274 -234
- package/.agent/scripts/capability_evolution.js +453 -0
- package/.agent/scripts/concept_extractor.js +240 -0
- package/.agent/scripts/consideration_engine.js +284 -0
- package/.agent/scripts/coverage_engine.js +102 -0
- package/.agent/scripts/cross_domain_engine.js +1883 -0
- package/.agent/scripts/engineering_memory.js +1313 -0
- package/.agent/scripts/integrity_manifest.js +1 -5
- package/.agent/scripts/judgment_engine.js +790 -0
- package/.agent/scripts/materiality_engine.js +251 -0
- package/.agent/scripts/outcome_engine.js +624 -0
- package/.agent/scripts/outcome_optimization_engine.js +1632 -0
- package/.agent/scripts/simulation_engine.js +1478 -0
- package/.agent/scripts/skill_intelligence.js +1371 -0
- package/.agent/scripts/validation_engine.js +236 -236
- package/.agent/scripts/verification_auditor.js +266 -0
- package/.agent/skill-intelligence/taxonomy/engineering-concepts.json +555 -0
- package/.agent/skill_topic_map.json +301 -341
- package/.agent/skills_inventory.json +4272 -6922
- package/.agent/workflows/arch.md +96 -80
- package/.agent/workflows/skill-intelligence.md +216 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +24 -24
- package/bin/adapter-install.js +1 -1
- package/package.json +8 -9
- package/plugins/core-plugin.json +1 -1
- package/plugins/gemini/extension.json +1 -1
- package/src/browser/index.js +2 -1
- package/src/browser/launcher.js +41 -1
- package/src/cli.js +3 -1
- package/src/commands/arch.js +261 -172
- package/src/commands/context.js +27 -8
- package/src/commands/learn.js +21 -6
- package/src/commands/memory.js +10 -1
- package/src/commands/native.js +12 -3
- package/src/commands/skill-intelligence.js +1165 -0
- package/src/commands/system1.js +105 -4
- package/src/context/ranker.js +337 -339
- package/src/evolution/engine.js +296 -295
- package/src/synthesis/claim_check_separator.js +294 -271
- package/src/system1/browser_intelligence.js +300 -0
- package/src/system1/constants.js +49 -0
- package/src/system1/decision_engine.js +498 -0
- package/src/system1/evidence_engine.js +602 -0
- package/src/system1/evolution_pipeline.js +581 -0
- package/src/system1/index.d.ts +114 -0
- package/src/system1/index.js +102 -0
- package/src/system1/reviewer_orchestrator.js +477 -0
- package/src/tui/banner.js +1 -1
- package/src/utils/fs.js +27 -0
package/src/evolution/engine.js
CHANGED
|
@@ -1,295 +1,296 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* evolution/engine.js — Controlled Self-Evolution Engine for Tribunal Kit
|
|
5
|
-
* =======================================================================
|
|
6
|
-
* System-1 Capability 4:
|
|
7
|
-
* Governs the proposal-driven evolution of reviewer routing, context weights,
|
|
8
|
-
* heuristics, skill idioms, and regression fixtures while strictly enforcing:
|
|
9
|
-
*
|
|
10
|
-
* 1. The Human Gate: No evolution can silently deploy without human approval.
|
|
11
|
-
* 2. Trust Boundaries: Protected security boundaries, execution policies, and
|
|
12
|
-
* package integrity validation can NEVER automatically evolve.
|
|
13
|
-
* 3. Anti-Self-Poisoning: Follows strict evidence hierarchy:
|
|
14
|
-
* VERIFIED_TEST_RESULT > DETERMINISTIC_TELEMETRY > REPRODUCIBLE_PATTERN > REVIEWER_CLAIM
|
|
15
|
-
* Proposals based solely on unverified LLM claims are rejected.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
const crypto = require('crypto');
|
|
21
|
-
|
|
22
|
-
const ALLOWED_TARGETS = new Set([
|
|
23
|
-
'reviewer-routing',
|
|
24
|
-
'context-weights',
|
|
25
|
-
'skill-instructions',
|
|
26
|
-
'reviewer-selection',
|
|
27
|
-
'deterministic-heuristics',
|
|
28
|
-
'known-failure-patterns',
|
|
29
|
-
'workflow-guidance',
|
|
30
|
-
'regression-fixtures',
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
const FORBIDDEN_TARGETS = new Set([
|
|
34
|
-
'security-boundaries',
|
|
35
|
-
'shell-execution-policy',
|
|
36
|
-
'package-integrity',
|
|
37
|
-
'dependency-pinning',
|
|
38
|
-
'human-gate',
|
|
39
|
-
'system1-trust-boundary',
|
|
40
|
-
'production-security',
|
|
41
|
-
'arbitrary-executable-code',
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
const EVIDENCE_HIERARCHY = {
|
|
45
|
-
VERIFIED_TEST_RESULT: 4,
|
|
46
|
-
DETERMINISTIC_TELEMETRY: 3,
|
|
47
|
-
REPRODUCIBLE_PATTERN: 2,
|
|
48
|
-
REVIEWER_CLAIM: 1,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Creates and formats a new Evolution Proposal.
|
|
53
|
-
*/
|
|
54
|
-
function createEvolutionProposal({
|
|
55
|
-
target,
|
|
56
|
-
currentBehavior,
|
|
57
|
-
observedEvidence,
|
|
58
|
-
proposedChange,
|
|
59
|
-
expectedEffect,
|
|
60
|
-
risk = 'MEDIUM',
|
|
61
|
-
testsRequired = [],
|
|
62
|
-
regressionRequirements = [],
|
|
63
|
-
evidenceType = 'DETERMINISTIC_TELEMETRY',
|
|
64
|
-
proposalId = null,
|
|
65
|
-
}) {
|
|
66
|
-
const id = proposalId || `EVO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
|
|
67
|
-
|
|
68
|
-
const proposal = {
|
|
69
|
-
proposal_id: id,
|
|
70
|
-
target,
|
|
71
|
-
current_behavior: currentBehavior,
|
|
72
|
-
observed_evidence: observedEvidence,
|
|
73
|
-
proposed_change: proposedChange,
|
|
74
|
-
expected_effect: expectedEffect,
|
|
75
|
-
risk: ['LOW', 'MEDIUM', 'HIGH'].includes(String(risk).toUpperCase())
|
|
76
|
-
? String(risk).toUpperCase()
|
|
77
|
-
: 'MEDIUM',
|
|
78
|
-
tests_required: Array.isArray(testsRequired) ? testsRequired : [testsRequired],
|
|
79
|
-
regression_requirements: Array.isArray(regressionRequirements)
|
|
80
|
-
? regressionRequirements
|
|
81
|
-
: [regressionRequirements],
|
|
82
|
-
evidence_type: EVIDENCE_HIERARCHY[evidenceType] ? evidenceType : 'REVIEWER_CLAIM',
|
|
83
|
-
evidence_weight: EVIDENCE_HIERARCHY[evidenceType] || 1,
|
|
84
|
-
status: 'PROPOSED',
|
|
85
|
-
created_at: new Date().toISOString(),
|
|
86
|
-
approval: null,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
return proposal;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Validates an evolution proposal against Anti-Self-Poisoning & Security Rules.
|
|
94
|
-
*/
|
|
95
|
-
function validateEvolutionProposal(proposal) {
|
|
96
|
-
if (!proposal || typeof proposal !== 'object') {
|
|
97
|
-
return { valid: false, reason: 'Proposal must be an object.' };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 1. Check required fields
|
|
101
|
-
const requiredFields = [
|
|
102
|
-
'proposal_id',
|
|
103
|
-
'target',
|
|
104
|
-
'current_behavior',
|
|
105
|
-
'observed_evidence',
|
|
106
|
-
'proposed_change',
|
|
107
|
-
'expected_effect',
|
|
108
|
-
'risk',
|
|
109
|
-
'tests_required',
|
|
110
|
-
'regression_requirements',
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
for (const field of requiredFields) {
|
|
114
|
-
if (!proposal[field] || (Array.isArray(proposal[field]) && proposal[field].length === 0)) {
|
|
115
|
-
return {
|
|
116
|
-
valid: false,
|
|
117
|
-
reason: `Missing required proposal field: "${field}". Every proposal must be backed by evidence.`,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 2. Security boundary enforcement: Forbidden targets cannot evolve automatically
|
|
123
|
-
if (FORBIDDEN_TARGETS.has(proposal.target)) {
|
|
124
|
-
return {
|
|
125
|
-
valid: false,
|
|
126
|
-
reason: `Target "${proposal.target}" is a protected security boundary. Automatic evolution of security boundaries, shell policies, and package integrity is strictly forbidden.`,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// 3. Allowed target check
|
|
131
|
-
if (!ALLOWED_TARGETS.has(proposal.target)) {
|
|
132
|
-
return {
|
|
133
|
-
valid: false,
|
|
134
|
-
reason: `Target "${proposal.target}" is not an allowed evolution target. Allowed: ${Array.from(ALLOWED_TARGETS).join(', ')}`,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 4. Anti-Self-Poisoning Rule: Reject proposals backed solely by unverified claims
|
|
139
|
-
const evidenceType = proposal.evidence_type || 'REVIEWER_CLAIM';
|
|
140
|
-
const evidenceWeight = EVIDENCE_HIERARCHY[evidenceType] || 0;
|
|
141
|
-
|
|
142
|
-
if (evidenceWeight <= EVIDENCE_HIERARCHY.REVIEWER_CLAIM) {
|
|
143
|
-
return {
|
|
144
|
-
valid: false,
|
|
145
|
-
reason: `Anti-Self-Poisoning violation: Proposal relies solely on "${evidenceType}". Proposals must be backed by reproducible execution patterns, deterministic telemetry, or verified test results.`,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 5. Evidence content check
|
|
150
|
-
if (
|
|
151
|
-
typeof proposal.observed_evidence !== 'string' ||
|
|
152
|
-
proposal.observed_evidence.trim().length < 10
|
|
153
|
-
) {
|
|
154
|
-
return {
|
|
155
|
-
valid: false,
|
|
156
|
-
reason:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
err.
|
|
201
|
-
err.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
proposal
|
|
252
|
-
proposal.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
proposal
|
|
275
|
-
proposal.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* evolution/engine.js — Controlled Self-Evolution Engine for Tribunal Kit
|
|
5
|
+
* =======================================================================
|
|
6
|
+
* System-1 Capability 4:
|
|
7
|
+
* Governs the proposal-driven evolution of reviewer routing, context weights,
|
|
8
|
+
* heuristics, skill idioms, and regression fixtures while strictly enforcing:
|
|
9
|
+
*
|
|
10
|
+
* 1. The Human Gate: No evolution can silently deploy without human approval.
|
|
11
|
+
* 2. Trust Boundaries: Protected security boundaries, execution policies, and
|
|
12
|
+
* package integrity validation can NEVER automatically evolve.
|
|
13
|
+
* 3. Anti-Self-Poisoning: Follows strict evidence hierarchy:
|
|
14
|
+
* VERIFIED_TEST_RESULT > DETERMINISTIC_TELEMETRY > REPRODUCIBLE_PATTERN > REVIEWER_CLAIM
|
|
15
|
+
* Proposals based solely on unverified LLM claims are rejected.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const crypto = require('crypto');
|
|
21
|
+
|
|
22
|
+
const ALLOWED_TARGETS = new Set([
|
|
23
|
+
'reviewer-routing',
|
|
24
|
+
'context-weights',
|
|
25
|
+
'skill-instructions',
|
|
26
|
+
'reviewer-selection',
|
|
27
|
+
'deterministic-heuristics',
|
|
28
|
+
'known-failure-patterns',
|
|
29
|
+
'workflow-guidance',
|
|
30
|
+
'regression-fixtures',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const FORBIDDEN_TARGETS = new Set([
|
|
34
|
+
'security-boundaries',
|
|
35
|
+
'shell-execution-policy',
|
|
36
|
+
'package-integrity',
|
|
37
|
+
'dependency-pinning',
|
|
38
|
+
'human-gate',
|
|
39
|
+
'system1-trust-boundary',
|
|
40
|
+
'production-security',
|
|
41
|
+
'arbitrary-executable-code',
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const EVIDENCE_HIERARCHY = {
|
|
45
|
+
VERIFIED_TEST_RESULT: 4,
|
|
46
|
+
DETERMINISTIC_TELEMETRY: 3,
|
|
47
|
+
REPRODUCIBLE_PATTERN: 2,
|
|
48
|
+
REVIEWER_CLAIM: 1,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates and formats a new Evolution Proposal.
|
|
53
|
+
*/
|
|
54
|
+
function createEvolutionProposal({
|
|
55
|
+
target,
|
|
56
|
+
currentBehavior,
|
|
57
|
+
observedEvidence,
|
|
58
|
+
proposedChange,
|
|
59
|
+
expectedEffect,
|
|
60
|
+
risk = 'MEDIUM',
|
|
61
|
+
testsRequired = [],
|
|
62
|
+
regressionRequirements = [],
|
|
63
|
+
evidenceType = 'DETERMINISTIC_TELEMETRY',
|
|
64
|
+
proposalId = null,
|
|
65
|
+
}) {
|
|
66
|
+
const id = proposalId || `EVO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
|
|
67
|
+
|
|
68
|
+
const proposal = {
|
|
69
|
+
proposal_id: id,
|
|
70
|
+
target,
|
|
71
|
+
current_behavior: currentBehavior,
|
|
72
|
+
observed_evidence: observedEvidence,
|
|
73
|
+
proposed_change: proposedChange,
|
|
74
|
+
expected_effect: expectedEffect,
|
|
75
|
+
risk: ['LOW', 'MEDIUM', 'HIGH'].includes(String(risk).toUpperCase())
|
|
76
|
+
? String(risk).toUpperCase()
|
|
77
|
+
: 'MEDIUM',
|
|
78
|
+
tests_required: Array.isArray(testsRequired) ? testsRequired : [testsRequired],
|
|
79
|
+
regression_requirements: Array.isArray(regressionRequirements)
|
|
80
|
+
? regressionRequirements
|
|
81
|
+
: [regressionRequirements],
|
|
82
|
+
evidence_type: EVIDENCE_HIERARCHY[evidenceType] ? evidenceType : 'REVIEWER_CLAIM',
|
|
83
|
+
evidence_weight: EVIDENCE_HIERARCHY[evidenceType] || 1,
|
|
84
|
+
status: 'PROPOSED',
|
|
85
|
+
created_at: new Date().toISOString(),
|
|
86
|
+
approval: null,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return proposal;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validates an evolution proposal against Anti-Self-Poisoning & Security Rules.
|
|
94
|
+
*/
|
|
95
|
+
function validateEvolutionProposal(proposal) {
|
|
96
|
+
if (!proposal || typeof proposal !== 'object') {
|
|
97
|
+
return { valid: false, reason: 'Proposal must be an object.' };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 1. Check required fields
|
|
101
|
+
const requiredFields = [
|
|
102
|
+
'proposal_id',
|
|
103
|
+
'target',
|
|
104
|
+
'current_behavior',
|
|
105
|
+
'observed_evidence',
|
|
106
|
+
'proposed_change',
|
|
107
|
+
'expected_effect',
|
|
108
|
+
'risk',
|
|
109
|
+
'tests_required',
|
|
110
|
+
'regression_requirements',
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const field of requiredFields) {
|
|
114
|
+
if (!proposal[field] || (Array.isArray(proposal[field]) && proposal[field].length === 0)) {
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
reason: `Missing required proposal field: "${field}". Every proposal must be backed by evidence.`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 2. Security boundary enforcement: Forbidden targets cannot evolve automatically
|
|
123
|
+
if (FORBIDDEN_TARGETS.has(proposal.target)) {
|
|
124
|
+
return {
|
|
125
|
+
valid: false,
|
|
126
|
+
reason: `Target "${proposal.target}" is a protected security boundary. Automatic evolution of security boundaries, shell policies, and package integrity is strictly forbidden.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 3. Allowed target check
|
|
131
|
+
if (!ALLOWED_TARGETS.has(proposal.target)) {
|
|
132
|
+
return {
|
|
133
|
+
valid: false,
|
|
134
|
+
reason: `Target "${proposal.target}" is not an allowed evolution target. Allowed: ${Array.from(ALLOWED_TARGETS).join(', ')}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 4. Anti-Self-Poisoning Rule: Reject proposals backed solely by unverified claims
|
|
139
|
+
const evidenceType = proposal.evidence_type || 'REVIEWER_CLAIM';
|
|
140
|
+
const evidenceWeight = EVIDENCE_HIERARCHY[evidenceType] || 0;
|
|
141
|
+
|
|
142
|
+
if (evidenceWeight <= EVIDENCE_HIERARCHY.REVIEWER_CLAIM) {
|
|
143
|
+
return {
|
|
144
|
+
valid: false,
|
|
145
|
+
reason: `Anti-Self-Poisoning violation: Proposal relies solely on "${evidenceType}". Proposals must be backed by reproducible execution patterns, deterministic telemetry, or verified test results.`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 5. Evidence content check
|
|
150
|
+
if (
|
|
151
|
+
typeof proposal.observed_evidence !== 'string' ||
|
|
152
|
+
proposal.observed_evidence.trim().length < 10
|
|
153
|
+
) {
|
|
154
|
+
return {
|
|
155
|
+
valid: false,
|
|
156
|
+
reason:
|
|
157
|
+
'Observed evidence is too short or vacuous. Measurable, concrete evidence is required.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { valid: true, reason: 'Proposal meets all verification and safety criteria.' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Storage helpers for proposal persistence.
|
|
166
|
+
*/
|
|
167
|
+
function getProposalsDir(agentDir = path.join(process.cwd(), '.agent')) {
|
|
168
|
+
return path.join(agentDir, 'evolution', 'proposals');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function findDuplicateProposal(proposal, agentDir) {
|
|
172
|
+
const existingProposals = loadEvolutionProposals(agentDir);
|
|
173
|
+
return (
|
|
174
|
+
existingProposals.find(
|
|
175
|
+
p =>
|
|
176
|
+
p.target === proposal.target &&
|
|
177
|
+
(p.proposed_change === proposal.proposed_change ||
|
|
178
|
+
JSON.stringify(p.proposed_change) === JSON.stringify(proposal.proposed_change)) &&
|
|
179
|
+
p.status !== 'REJECTED',
|
|
180
|
+
) || null
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function saveEvolutionProposal(proposal, agentDir, options = {}) {
|
|
185
|
+
const validation = validateEvolutionProposal(proposal);
|
|
186
|
+
if (!validation.valid) {
|
|
187
|
+
throw new Error(`Cannot save invalid evolution proposal: ${validation.reason}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const dir = getProposalsDir(agentDir);
|
|
191
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
192
|
+
|
|
193
|
+
// Phase 5 Case D: Duplicate proposal detection
|
|
194
|
+
const duplicate = findDuplicateProposal(proposal, agentDir);
|
|
195
|
+
if (duplicate) {
|
|
196
|
+
if (options.throwOnDuplicate !== false) {
|
|
197
|
+
const err = new Error(
|
|
198
|
+
`Duplicate evolution proposal detected: matches existing proposal ${duplicate.proposal_id}`,
|
|
199
|
+
);
|
|
200
|
+
err.code = 'DUPLICATE_PROPOSAL';
|
|
201
|
+
err.duplicate = true;
|
|
202
|
+
err.existing_proposal_id = duplicate.proposal_id;
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
saved: false,
|
|
207
|
+
duplicate: true,
|
|
208
|
+
existing_proposal_id: duplicate.proposal_id,
|
|
209
|
+
filePath: path.join(dir, `${duplicate.proposal_id}.json`),
|
|
210
|
+
message: `Duplicate proposal detected: matches existing proposal ${duplicate.proposal_id}`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const filePath = path.join(dir, `${proposal.proposal_id}.json`);
|
|
215
|
+
fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
|
|
216
|
+
return filePath;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function loadEvolutionProposals(agentDir) {
|
|
220
|
+
const dir = getProposalsDir(agentDir);
|
|
221
|
+
if (!fs.existsSync(dir)) return [];
|
|
222
|
+
|
|
223
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
224
|
+
const proposals = [];
|
|
225
|
+
|
|
226
|
+
for (const f of files) {
|
|
227
|
+
try {
|
|
228
|
+
const p = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
|
|
229
|
+
proposals.push(p);
|
|
230
|
+
} catch (_) {}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return proposals;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Human Gate: Explicitly approve an evolution proposal.
|
|
238
|
+
*/
|
|
239
|
+
function approveEvolutionProposal(proposalId, approverName, agentDir) {
|
|
240
|
+
if (!approverName || typeof approverName !== 'string') {
|
|
241
|
+
throw new Error('Human approval requires an explicit approver name/signature.');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const dir = getProposalsDir(agentDir);
|
|
245
|
+
const filePath = path.join(dir, `${proposalId}.json`);
|
|
246
|
+
|
|
247
|
+
if (!fs.existsSync(filePath)) {
|
|
248
|
+
throw new Error(`Evolution proposal not found: ${proposalId}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
252
|
+
proposal.status = 'APPROVED';
|
|
253
|
+
proposal.approval = {
|
|
254
|
+
approved_by: approverName,
|
|
255
|
+
approved_at: new Date().toISOString(),
|
|
256
|
+
human_gate_cleared: true,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
|
|
260
|
+
return proposal;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Reject an evolution proposal.
|
|
265
|
+
*/
|
|
266
|
+
function rejectEvolutionProposal(proposalId, reason, agentDir) {
|
|
267
|
+
const dir = getProposalsDir(agentDir);
|
|
268
|
+
const filePath = path.join(dir, `${proposalId}.json`);
|
|
269
|
+
|
|
270
|
+
if (!fs.existsSync(filePath)) {
|
|
271
|
+
throw new Error(`Evolution proposal not found: ${proposalId}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
275
|
+
proposal.status = 'REJECTED';
|
|
276
|
+
proposal.rejection = {
|
|
277
|
+
reason: reason || 'Rejected by developer',
|
|
278
|
+
rejected_at: new Date().toISOString(),
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
|
|
282
|
+
return proposal;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = {
|
|
286
|
+
createEvolutionProposal,
|
|
287
|
+
validateEvolutionProposal,
|
|
288
|
+
saveEvolutionProposal,
|
|
289
|
+
findDuplicateProposal,
|
|
290
|
+
loadEvolutionProposals,
|
|
291
|
+
approveEvolutionProposal,
|
|
292
|
+
rejectEvolutionProposal,
|
|
293
|
+
ALLOWED_TARGETS,
|
|
294
|
+
FORBIDDEN_TARGETS,
|
|
295
|
+
EVIDENCE_HIERARCHY,
|
|
296
|
+
};
|