@neurcode-ai/governance-runtime 0.1.2 → 0.1.4
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 +201 -0
- package/dist/admission-provenance.d.ts +111 -0
- package/dist/admission-provenance.d.ts.map +1 -0
- package/dist/admission-provenance.js +735 -0
- package/dist/admission-provenance.js.map +1 -0
- package/dist/agent-guard-posture.d.ts +40 -0
- package/dist/agent-guard-posture.d.ts.map +1 -0
- package/dist/agent-guard-posture.js +117 -0
- package/dist/agent-guard-posture.js.map +1 -0
- package/dist/agent-invocation-observability.d.ts +47 -0
- package/dist/agent-invocation-observability.d.ts.map +1 -0
- package/dist/agent-invocation-observability.js +229 -0
- package/dist/agent-invocation-observability.js.map +1 -0
- package/dist/agent-plan.d.ts +119 -0
- package/dist/agent-plan.d.ts.map +1 -0
- package/dist/agent-plan.js +565 -0
- package/dist/agent-plan.js.map +1 -0
- package/dist/agent-runtime-adapter.d.ts +69 -0
- package/dist/agent-runtime-adapter.d.ts.map +1 -0
- package/dist/agent-runtime-adapter.js +274 -0
- package/dist/agent-runtime-adapter.js.map +1 -0
- package/dist/ai-change-record.d.ts +185 -0
- package/dist/ai-change-record.d.ts.map +1 -0
- package/dist/ai-change-record.js +580 -0
- package/dist/ai-change-record.js.map +1 -0
- package/dist/architecture-graph.d.ts +153 -0
- package/dist/architecture-graph.d.ts.map +1 -0
- package/dist/architecture-graph.js +646 -0
- package/dist/architecture-graph.js.map +1 -0
- package/dist/architecture-obligations.d.ts +153 -0
- package/dist/architecture-obligations.d.ts.map +1 -0
- package/dist/architecture-obligations.js +505 -0
- package/dist/architecture-obligations.js.map +1 -0
- package/dist/constraints.d.ts +9 -1
- package/dist/constraints.d.ts.map +1 -1
- package/dist/constraints.js +285 -34
- package/dist/constraints.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +188 -10
- package/dist/index.js.map +1 -1
- package/dist/profile.d.ts +159 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +611 -0
- package/dist/profile.js.map +1 -0
- package/dist/session.d.ts +428 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +2052 -0
- package/dist/session.js.map +1 -0
- package/package.json +19 -8
- package/src/constraints.ts +0 -432
- package/src/index.test.ts +0 -382
- package/src/index.ts +0 -373
- package/tsconfig.json +0 -19
package/src/index.ts
DELETED
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
export type PlanVerdict = 'PASS' | 'FAIL' | 'WARN';
|
|
2
|
-
export {
|
|
3
|
-
compileDeterministicConstraints,
|
|
4
|
-
type DeterministicConstraintCompilation,
|
|
5
|
-
type DeterministicConstraintCompilationInput,
|
|
6
|
-
type DeterministicConstraintRule,
|
|
7
|
-
type DeterministicConstraintSource,
|
|
8
|
-
} from './constraints';
|
|
9
|
-
import { compileDeterministicConstraints, type DeterministicConstraintRule } from './constraints';
|
|
10
|
-
|
|
11
|
-
export interface PlanFileScopeItem {
|
|
12
|
-
path: string;
|
|
13
|
-
action: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface PlanDiffLine {
|
|
17
|
-
type: 'context' | 'added' | 'removed';
|
|
18
|
-
content: string;
|
|
19
|
-
lineNumber?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface PlanDiffHunk {
|
|
23
|
-
oldStart: number;
|
|
24
|
-
oldLines: number;
|
|
25
|
-
newStart: number;
|
|
26
|
-
newLines: number;
|
|
27
|
-
lines: PlanDiffLine[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface PlanDiffFile {
|
|
31
|
-
path: string;
|
|
32
|
-
oldPath?: string;
|
|
33
|
-
changeType: 'add' | 'delete' | 'modify' | 'rename';
|
|
34
|
-
added: number;
|
|
35
|
-
removed: number;
|
|
36
|
-
hunks?: PlanDiffHunk[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface PlanDiffStats {
|
|
40
|
-
totalAdded: number;
|
|
41
|
-
totalRemoved: number;
|
|
42
|
-
totalFiles: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface PlanVerificationInput {
|
|
46
|
-
planFiles: PlanFileScopeItem[];
|
|
47
|
-
changedFiles: PlanDiffFile[];
|
|
48
|
-
diffStats?: PlanDiffStats;
|
|
49
|
-
intentConstraints?: string;
|
|
50
|
-
policyRules?: string[];
|
|
51
|
-
extraConstraintRules?: DeterministicConstraintRule[];
|
|
52
|
-
fileContents?: Record<string, string>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface PlanDiffSummary {
|
|
56
|
-
added: number;
|
|
57
|
-
removed: number;
|
|
58
|
-
files: Array<{
|
|
59
|
-
path: string;
|
|
60
|
-
oldPath?: string;
|
|
61
|
-
changeType: string;
|
|
62
|
-
added: number;
|
|
63
|
-
removed: number;
|
|
64
|
-
hunks: PlanDiffHunk[];
|
|
65
|
-
}>;
|
|
66
|
-
bloatFiles: string[];
|
|
67
|
-
plannedFilesModified: number;
|
|
68
|
-
totalPlannedFiles: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface PlanVerificationResult {
|
|
72
|
-
adherenceScore: number;
|
|
73
|
-
bloatCount: number;
|
|
74
|
-
bloatFiles: string[];
|
|
75
|
-
plannedFilesModified: number;
|
|
76
|
-
totalPlannedFiles: number;
|
|
77
|
-
scopeGuardPassed: boolean;
|
|
78
|
-
constraintViolations: string[];
|
|
79
|
-
verdict: PlanVerdict;
|
|
80
|
-
diffSummary: PlanDiffSummary;
|
|
81
|
-
message: string;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function normalizeRepoPath(pathValue: string): string {
|
|
85
|
-
return pathValue.replace(/\\/g, '/').replace(/^\.\//, '').trim();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function extractPlannedFilePaths(planFiles: PlanFileScopeItem[]): string[] {
|
|
89
|
-
const plannedFilePaths = new Set<string>();
|
|
90
|
-
|
|
91
|
-
for (const file of planFiles) {
|
|
92
|
-
const normalizedPath = normalizeRepoPath(file.path);
|
|
93
|
-
if (!normalizedPath) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (file.action === 'MODIFY' || file.action === 'CREATE') {
|
|
98
|
-
plannedFilePaths.add(normalizedPath);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return Array.from(plannedFilePaths);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function detectConstraintViolations(
|
|
106
|
-
intentConstraints: string | undefined,
|
|
107
|
-
policyRules: string[] | undefined,
|
|
108
|
-
extraConstraintRules: DeterministicConstraintRule[] | undefined,
|
|
109
|
-
changedFiles: PlanDiffFile[],
|
|
110
|
-
fileContents?: Record<string, string>
|
|
111
|
-
): string[] {
|
|
112
|
-
const compiled = compileDeterministicConstraints({
|
|
113
|
-
intentConstraints,
|
|
114
|
-
policyRules,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const rules = [
|
|
118
|
-
...compiled.rules,
|
|
119
|
-
...(extraConstraintRules || []),
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
if (rules.length === 0) {
|
|
123
|
-
return [];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const violations: string[] = [];
|
|
127
|
-
const seenViolations = new Set<string>();
|
|
128
|
-
|
|
129
|
-
const pathMatchesRule = (rule: DeterministicConstraintRule, filePath: string): boolean => {
|
|
130
|
-
const include = rule.pathIncludes || [];
|
|
131
|
-
const exclude = rule.pathExcludes || [];
|
|
132
|
-
if (include.length > 0 && !include.some((pattern) => pattern.test(filePath))) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
if (exclude.length > 0 && exclude.some((pattern) => pattern.test(filePath))) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const countMatches = (pattern: RegExp, input: string): number => {
|
|
142
|
-
if (!input) return 0;
|
|
143
|
-
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
144
|
-
const re = new RegExp(pattern.source, flags);
|
|
145
|
-
let total = 0;
|
|
146
|
-
for (const _match of input.matchAll(re)) {
|
|
147
|
-
total += 1;
|
|
148
|
-
}
|
|
149
|
-
return total;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
for (const rule of rules) {
|
|
153
|
-
for (const file of changedFiles) {
|
|
154
|
-
if (!pathMatchesRule(rule, file.path)) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (!file.hunks || file.hunks.length === 0) {
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const addedLines = file.hunks.flatMap((hunk) =>
|
|
162
|
-
hunk.lines
|
|
163
|
-
.filter((line) => line.type === 'added')
|
|
164
|
-
.map((line) => line.content)
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
(
|
|
169
|
-
typeof rule.maxMatchesPerFile === 'number'
|
|
170
|
-
&& Number.isFinite(rule.maxMatchesPerFile)
|
|
171
|
-
) || (
|
|
172
|
-
typeof rule.minMatchesPerFile === 'number'
|
|
173
|
-
&& Number.isFinite(rule.minMatchesPerFile)
|
|
174
|
-
)
|
|
175
|
-
) {
|
|
176
|
-
const fallbackText = addedLines.join('\n');
|
|
177
|
-
const fullContent = fileContents?.[file.path];
|
|
178
|
-
const haystack =
|
|
179
|
-
rule.evaluationMode === 'full_file' && typeof fullContent === 'string'
|
|
180
|
-
? fullContent
|
|
181
|
-
: fallbackText;
|
|
182
|
-
const matchCount = countMatches(rule.pattern, haystack);
|
|
183
|
-
if (
|
|
184
|
-
typeof rule.maxMatchesPerFile === 'number'
|
|
185
|
-
&& Number.isFinite(rule.maxMatchesPerFile)
|
|
186
|
-
&& matchCount > rule.maxMatchesPerFile
|
|
187
|
-
) {
|
|
188
|
-
const violation =
|
|
189
|
-
`${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
190
|
-
`(limit ${rule.maxMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
191
|
-
if (!seenViolations.has(violation)) {
|
|
192
|
-
seenViolations.add(violation);
|
|
193
|
-
violations.push(violation);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (
|
|
197
|
-
typeof rule.minMatchesPerFile === 'number'
|
|
198
|
-
&& Number.isFinite(rule.minMatchesPerFile)
|
|
199
|
-
&& matchCount < rule.minMatchesPerFile
|
|
200
|
-
) {
|
|
201
|
-
const violation =
|
|
202
|
-
`${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
203
|
-
`(minimum ${rule.minMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
204
|
-
if (!seenViolations.has(violation)) {
|
|
205
|
-
seenViolations.add(violation);
|
|
206
|
-
violations.push(violation);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
for (const hunk of file.hunks) {
|
|
213
|
-
for (const line of hunk.lines) {
|
|
214
|
-
if (line.type !== 'added') {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
const pattern = new RegExp(rule.pattern.source, rule.pattern.flags);
|
|
218
|
-
if (!pattern.test(line.content)) {
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
const violation = `${rule.matchToken} found in ${file.path} (violates constraint: \"${rule.displayName}\")`;
|
|
222
|
-
if (seenViolations.has(violation)) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
seenViolations.add(violation);
|
|
226
|
-
violations.push(violation);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return violations;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export function resolvePlanVerdict(input: {
|
|
236
|
-
bloatCount: number;
|
|
237
|
-
adherenceScore: number;
|
|
238
|
-
totalPlannedFiles: number;
|
|
239
|
-
plannedFilesModified: number;
|
|
240
|
-
constraintViolations: string[];
|
|
241
|
-
}): PlanVerdict {
|
|
242
|
-
if (input.constraintViolations.length > 0) {
|
|
243
|
-
return 'FAIL';
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (input.totalPlannedFiles === 0 && input.plannedFilesModified === 0) {
|
|
247
|
-
// 0/0 is treated as incomplete, not perfect adherence.
|
|
248
|
-
return 'FAIL';
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (input.bloatCount > 0 && input.adherenceScore < 50) {
|
|
252
|
-
return 'FAIL';
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (input.bloatCount > 0 || input.adherenceScore < 80) {
|
|
256
|
-
return 'WARN';
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return 'PASS';
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function buildPlanVerificationMessage(result: Pick<
|
|
263
|
-
PlanVerificationResult,
|
|
264
|
-
'constraintViolations' | 'totalPlannedFiles' | 'plannedFilesModified' | 'verdict' | 'adherenceScore' | 'bloatCount'
|
|
265
|
-
>): string {
|
|
266
|
-
if (result.constraintViolations.length > 0) {
|
|
267
|
-
return `❌ Constraint violation: ${result.constraintViolations[0]}${result.constraintViolations.length > 1 ? ` (+${result.constraintViolations.length - 1} more)` : ''}`;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (result.totalPlannedFiles === 0 && result.plannedFilesModified === 0) {
|
|
271
|
-
return '❌ Incomplete: No planned files to verify (0/0). Plan may be malformed.';
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (result.verdict === 'PASS') {
|
|
275
|
-
return `✅ Plan adherence: ${result.adherenceScore}% (${result.plannedFilesModified}/${result.totalPlannedFiles} planned files modified)`;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (result.verdict === 'WARN') {
|
|
279
|
-
return `⚠️ Plan adherence: ${result.adherenceScore}% (${result.plannedFilesModified}/${result.totalPlannedFiles} planned files modified)${result.bloatCount > 0 ? `, ${result.bloatCount} unexpected file(s) changed` : ''}`;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return `❌ Plan adherence: ${result.adherenceScore}% (${result.plannedFilesModified}/${result.totalPlannedFiles} planned files modified), ${result.bloatCount} unexpected file(s) changed`;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function evaluatePlanVerification(input: PlanVerificationInput): PlanVerificationResult {
|
|
286
|
-
const plannedFilePaths = extractPlannedFilePaths(input.planFiles);
|
|
287
|
-
const plannedSet = new Set(plannedFilePaths);
|
|
288
|
-
|
|
289
|
-
const normalizedChangedFiles = input.changedFiles.map((file) => ({
|
|
290
|
-
...file,
|
|
291
|
-
path: normalizeRepoPath(file.path),
|
|
292
|
-
oldPath: file.oldPath ? normalizeRepoPath(file.oldPath) : undefined,
|
|
293
|
-
hunks: file.hunks || [],
|
|
294
|
-
}));
|
|
295
|
-
|
|
296
|
-
const changedFilePaths = Array.from(
|
|
297
|
-
new Set(
|
|
298
|
-
normalizedChangedFiles
|
|
299
|
-
.map((file) => file.path)
|
|
300
|
-
.filter(Boolean)
|
|
301
|
-
)
|
|
302
|
-
);
|
|
303
|
-
const changedSet = new Set(changedFilePaths);
|
|
304
|
-
|
|
305
|
-
const bloatFiles = changedFilePaths.filter((pathValue) => !plannedSet.has(pathValue));
|
|
306
|
-
const bloatCount = bloatFiles.length;
|
|
307
|
-
|
|
308
|
-
const totalPlannedFiles = plannedSet.size;
|
|
309
|
-
const plannedFilesModified = plannedFilePaths.filter((pathValue) => changedSet.has(pathValue)).length;
|
|
310
|
-
const adherenceScore = totalPlannedFiles > 0
|
|
311
|
-
? Math.round((plannedFilesModified / totalPlannedFiles) * 100)
|
|
312
|
-
: 0;
|
|
313
|
-
|
|
314
|
-
const constraintViolations = detectConstraintViolations(
|
|
315
|
-
input.intentConstraints,
|
|
316
|
-
input.policyRules,
|
|
317
|
-
input.extraConstraintRules,
|
|
318
|
-
normalizedChangedFiles,
|
|
319
|
-
input.fileContents
|
|
320
|
-
);
|
|
321
|
-
const verdict = resolvePlanVerdict({
|
|
322
|
-
bloatCount,
|
|
323
|
-
adherenceScore,
|
|
324
|
-
totalPlannedFiles,
|
|
325
|
-
plannedFilesModified,
|
|
326
|
-
constraintViolations,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
const totalAdded = input.diffStats
|
|
330
|
-
? input.diffStats.totalAdded
|
|
331
|
-
: normalizedChangedFiles.reduce((sum, file) => sum + file.added, 0);
|
|
332
|
-
const totalRemoved = input.diffStats
|
|
333
|
-
? input.diffStats.totalRemoved
|
|
334
|
-
: normalizedChangedFiles.reduce((sum, file) => sum + file.removed, 0);
|
|
335
|
-
|
|
336
|
-
const diffSummary: PlanDiffSummary = {
|
|
337
|
-
added: totalAdded,
|
|
338
|
-
removed: totalRemoved,
|
|
339
|
-
files: normalizedChangedFiles.map((file) => ({
|
|
340
|
-
path: file.path,
|
|
341
|
-
oldPath: file.oldPath,
|
|
342
|
-
changeType: file.changeType,
|
|
343
|
-
added: file.added,
|
|
344
|
-
removed: file.removed,
|
|
345
|
-
hunks: file.hunks || [],
|
|
346
|
-
})),
|
|
347
|
-
bloatFiles,
|
|
348
|
-
plannedFilesModified,
|
|
349
|
-
totalPlannedFiles,
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
const message = buildPlanVerificationMessage({
|
|
353
|
-
constraintViolations,
|
|
354
|
-
totalPlannedFiles,
|
|
355
|
-
plannedFilesModified,
|
|
356
|
-
verdict,
|
|
357
|
-
adherenceScore,
|
|
358
|
-
bloatCount,
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
return {
|
|
362
|
-
adherenceScore,
|
|
363
|
-
bloatCount,
|
|
364
|
-
bloatFiles,
|
|
365
|
-
plannedFilesModified,
|
|
366
|
-
totalPlannedFiles,
|
|
367
|
-
scopeGuardPassed: bloatCount === 0,
|
|
368
|
-
constraintViolations,
|
|
369
|
-
verdict,
|
|
370
|
-
diffSummary,
|
|
371
|
-
message,
|
|
372
|
-
};
|
|
373
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"lib": ["ES2022"],
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"declaration": true,
|
|
13
|
-
"declarationMap": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"resolveJsonModule": true
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
19
|
-
}
|