@vibecheckai/cli 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/bin/runners/lib/agent-firewall/change-packet/builder.js +214 -0
  2. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  3. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  4. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  5. package/bin/runners/lib/agent-firewall/claims/extractor.js +214 -0
  6. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  7. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  8. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  9. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +118 -0
  10. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  11. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +142 -0
  12. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  13. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  14. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  15. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  16. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  17. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  18. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  19. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  20. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  21. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  22. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  23. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  24. package/bin/runners/lib/agent-firewall/policy/default-policy.json +84 -0
  25. package/bin/runners/lib/agent-firewall/policy/engine.js +72 -0
  26. package/bin/runners/lib/agent-firewall/policy/loader.js +143 -0
  27. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  28. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  29. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +61 -0
  30. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +50 -0
  31. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +50 -0
  32. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  33. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  34. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  35. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  36. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  37. package/bin/runners/lib/agent-firewall/truthpack/loader.js +116 -0
  38. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  39. package/bin/runners/lib/analysis-core.js +198 -180
  40. package/bin/runners/lib/analyzers.js +1119 -536
  41. package/bin/runners/lib/cli-output.js +236 -210
  42. package/bin/runners/lib/detectors-v2.js +547 -785
  43. package/bin/runners/lib/fingerprint.js +377 -0
  44. package/bin/runners/lib/route-truth.js +1167 -322
  45. package/bin/runners/lib/scan-output.js +144 -738
  46. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  47. package/bin/runners/lib/terminal-ui.js +188 -770
  48. package/bin/runners/lib/truth.js +1004 -321
  49. package/bin/runners/lib/unified-output.js +162 -158
  50. package/bin/runners/runAgent.js +161 -0
  51. package/bin/runners/runFirewall.js +134 -0
  52. package/bin/runners/runFirewallHook.js +56 -0
  53. package/bin/runners/runScan.js +113 -10
  54. package/bin/runners/runShip.js +7 -8
  55. package/bin/runners/runTruth.js +89 -0
  56. package/mcp-server/agent-firewall-interceptor.js +164 -0
  57. package/mcp-server/index.js +347 -313
  58. package/mcp-server/truth-context.js +131 -90
  59. package/mcp-server/truth-firewall-tools.js +1412 -1045
  60. package/package.json +1 -1
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Change Packet Builder
3
+ *
4
+ * Builds change packets from diffs + agent intent.
5
+ * Each packet is a complete audit artifact of an AI code change attempt.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const crypto = require("crypto");
11
+ const path = require("path");
12
+
13
+ /**
14
+ * Build a change packet from diff and agent intent
15
+ * @param {object} params
16
+ * @param {string} params.agentId - Agent identifier
17
+ * @param {string} params.intent - Agent's stated intent
18
+ * @param {object} params.diff - Diff object { before, after, unified }
19
+ * @param {string} params.filePath - File path (relative to repo root)
20
+ * @param {object} params.claims - Extracted claims
21
+ * @param {object} params.evidence - Evidence resolution results
22
+ * @param {object} params.verdict - Policy verdict
23
+ * @param {object} params.unblockPlan - Unblock plan (if blocked)
24
+ * @param {object} params.policy - Policy used for evaluation
25
+ * @returns {object} Change packet
26
+ */
27
+ function buildChangePacket({
28
+ agentId,
29
+ intent,
30
+ diff,
31
+ filePath,
32
+ claims = [],
33
+ evidence = [],
34
+ verdict,
35
+ unblockPlan = null,
36
+ policy = null
37
+ }) {
38
+ const timestamp = new Date().toISOString();
39
+
40
+ // Generate unique packet ID from content hash
41
+ const packetContent = JSON.stringify({
42
+ agentId,
43
+ intent,
44
+ filePath,
45
+ timestamp,
46
+ diff: diff?.unified || ""
47
+ });
48
+ const id = crypto.createHash("sha256")
49
+ .update(packetContent)
50
+ .digest("hex")
51
+ .slice(0, 16);
52
+
53
+ // Calculate file statistics
54
+ const linesChanged = diff?.unified
55
+ ? diff.unified.split('\n').filter(line => line.startsWith('+') || line.startsWith('-')).length
56
+ : 0;
57
+
58
+ const files = [{
59
+ path: filePath,
60
+ linesChanged,
61
+ domain: classifyFileDomain(filePath)
62
+ }];
63
+
64
+ // Build packet
65
+ const packet = {
66
+ id,
67
+ timestamp,
68
+ agentId,
69
+ intent: intent || "No intent provided",
70
+ diff: diff || null,
71
+ files,
72
+ claims,
73
+ evidence,
74
+ verdict: verdict || {
75
+ decision: "ALLOW",
76
+ violations: [],
77
+ message: "No verdict provided"
78
+ },
79
+ unblockPlan: unblockPlan || null,
80
+ metadata: {
81
+ totalFiles: files.length,
82
+ totalLines: linesChanged,
83
+ policyVersion: policy?.version || "unknown",
84
+ policyProfile: policy?.profile || "unknown"
85
+ }
86
+ };
87
+
88
+ return packet;
89
+ }
90
+
91
+ /**
92
+ * Build change packet from multiple files
93
+ * @param {object} params
94
+ * @param {string} params.agentId - Agent identifier
95
+ * @param {string} params.intent - Agent's stated intent
96
+ * @param {array} params.changes - Array of { filePath, diff, claims }
97
+ * @param {array} params.evidence - Evidence resolution results
98
+ * @param {object} params.verdict - Policy verdict
99
+ * @param {object} params.unblockPlan - Unblock plan (if blocked)
100
+ * @param {object} params.policy - Policy used for evaluation
101
+ * @returns {object} Change packet
102
+ */
103
+ function buildMultiFileChangePacket({
104
+ agentId,
105
+ intent,
106
+ changes = [],
107
+ evidence = [],
108
+ verdict,
109
+ unblockPlan = null,
110
+ policy = null
111
+ }) {
112
+ const timestamp = new Date().toISOString();
113
+
114
+ // Aggregate all file changes
115
+ const files = changes.map(change => ({
116
+ path: change.filePath,
117
+ linesChanged: calculateLinesChanged(change.diff),
118
+ domain: classifyFileDomain(change.filePath)
119
+ }));
120
+
121
+ // Aggregate all claims
122
+ const claims = changes.flatMap(change =>
123
+ (change.claims || []).map(claim => ({
124
+ ...claim,
125
+ file: change.filePath
126
+ }))
127
+ );
128
+
129
+ // Generate unique packet ID from all changes
130
+ const packetContent = JSON.stringify({
131
+ agentId,
132
+ intent,
133
+ timestamp,
134
+ files: files.map(f => f.path),
135
+ claims: claims.map(c => `${c.type}:${c.value}`)
136
+ });
137
+ const id = crypto.createHash("sha256")
138
+ .update(packetContent)
139
+ .digest("hex")
140
+ .slice(0, 16);
141
+
142
+ // Build unified diff (concatenate all diffs)
143
+ const unifiedDiff = changes
144
+ .map(change => {
145
+ const diff = change.diff?.unified || "";
146
+ return diff ? `--- ${change.filePath}\n+++ ${change.filePath}\n${diff}` : "";
147
+ })
148
+ .filter(Boolean)
149
+ .join("\n\n");
150
+
151
+ const packet = {
152
+ id,
153
+ timestamp,
154
+ agentId,
155
+ intent: intent || "No intent provided",
156
+ diff: unifiedDiff ? {
157
+ unified: unifiedDiff,
158
+ before: null, // Multi-file diffs don't store full before/after
159
+ after: null
160
+ } : null,
161
+ files,
162
+ claims,
163
+ evidence,
164
+ verdict: verdict || {
165
+ decision: "ALLOW",
166
+ violations: [],
167
+ message: "No verdict provided"
168
+ },
169
+ unblockPlan: unblockPlan || null,
170
+ metadata: {
171
+ totalFiles: files.length,
172
+ totalLines: files.reduce((sum, f) => sum + f.linesChanged, 0),
173
+ policyVersion: policy?.version || "unknown",
174
+ policyProfile: policy?.profile || "unknown"
175
+ }
176
+ };
177
+
178
+ return packet;
179
+ }
180
+
181
+ /**
182
+ * Calculate number of lines changed from diff
183
+ * @param {object} diff - Diff object
184
+ * @returns {number} Number of lines changed
185
+ */
186
+ function calculateLinesChanged(diff) {
187
+ if (!diff || !diff.unified) return 0;
188
+ return diff.unified
189
+ .split('\n')
190
+ .filter(line => line.startsWith('+') || line.startsWith('-'))
191
+ .length;
192
+ }
193
+
194
+ /**
195
+ * Classify file domain from path
196
+ * @param {string} filePath - File path
197
+ * @returns {string} Domain classification
198
+ */
199
+ function classifyFileDomain(filePath) {
200
+ const s = filePath.toLowerCase();
201
+ if (s.includes("auth")) return "auth";
202
+ if (s.includes("stripe") || s.includes("payment")) return "payments";
203
+ if (s.includes("routes") || s.includes("router") || s.includes("api")) return "routes";
204
+ if (s.includes("schema") || s.includes("contract") || s.includes("openapi")) return "contracts";
205
+ if (s.includes("ui") || s.includes("components") || s.includes("pages")) return "ui";
206
+ return "general";
207
+ }
208
+
209
+ module.exports = {
210
+ buildChangePacket,
211
+ buildMultiFileChangePacket,
212
+ calculateLinesChanged,
213
+ classifyFileDomain
214
+ };
@@ -0,0 +1,228 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["id", "timestamp", "agentId", "intent", "files", "claims", "verdict"],
5
+ "properties": {
6
+ "id": {
7
+ "type": "string",
8
+ "description": "Unique packet ID (hash-based)"
9
+ },
10
+ "timestamp": {
11
+ "type": "string",
12
+ "format": "date-time",
13
+ "description": "ISO timestamp of the change"
14
+ },
15
+ "agentId": {
16
+ "type": "string",
17
+ "description": "Identifier for the AI agent (e.g., 'cursor', 'windsurf', 'copilot')"
18
+ },
19
+ "intent": {
20
+ "type": "string",
21
+ "description": "Agent's stated intent for the change"
22
+ },
23
+ "diff": {
24
+ "type": "object",
25
+ "properties": {
26
+ "before": {
27
+ "type": "string",
28
+ "description": "File content before change"
29
+ },
30
+ "after": {
31
+ "type": "string",
32
+ "description": "File content after change"
33
+ },
34
+ "unified": {
35
+ "type": "string",
36
+ "description": "Unified diff format"
37
+ }
38
+ }
39
+ },
40
+ "files": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "object",
44
+ "required": ["path", "linesChanged"],
45
+ "properties": {
46
+ "path": {
47
+ "type": "string",
48
+ "description": "Relative file path"
49
+ },
50
+ "linesChanged": {
51
+ "type": "number",
52
+ "description": "Number of lines changed"
53
+ },
54
+ "domain": {
55
+ "type": "string",
56
+ "enum": ["auth", "payments", "routes", "contracts", "ui", "general"],
57
+ "description": "File domain classification"
58
+ }
59
+ }
60
+ },
61
+ "description": "List of changed files"
62
+ },
63
+ "claims": {
64
+ "type": "array",
65
+ "items": {
66
+ "type": "object",
67
+ "required": ["type", "value", "criticality"],
68
+ "properties": {
69
+ "type": {
70
+ "type": "string",
71
+ "enum": ["route", "env_used", "auth_boundary", "data_contract", "http_call", "ui_success_claim", "side_effect"],
72
+ "description": "Claim type"
73
+ },
74
+ "value": {
75
+ "type": "string",
76
+ "description": "Claim value (e.g., route path, env var name)"
77
+ },
78
+ "criticality": {
79
+ "type": "string",
80
+ "enum": ["hard", "soft"],
81
+ "description": "Claim criticality"
82
+ },
83
+ "pointer": {
84
+ "type": "string",
85
+ "description": "File:line pointer (e.g., 'file.ts:42-45')"
86
+ },
87
+ "reason": {
88
+ "type": "string",
89
+ "description": "Reason for claim extraction"
90
+ },
91
+ "file": {
92
+ "type": "string",
93
+ "description": "File where claim was found"
94
+ },
95
+ "domain": {
96
+ "type": "string",
97
+ "enum": ["auth", "payments", "routes", "contracts", "ui", "general"]
98
+ }
99
+ }
100
+ },
101
+ "description": "Extracted claims from the change"
102
+ },
103
+ "evidence": {
104
+ "type": "array",
105
+ "items": {
106
+ "type": "object",
107
+ "required": ["claimId", "result"],
108
+ "properties": {
109
+ "claimId": {
110
+ "type": "string",
111
+ "description": "Reference to claim in claims array"
112
+ },
113
+ "result": {
114
+ "type": "string",
115
+ "enum": ["PROVEN", "UNPROVEN", "CONTRADICTS"],
116
+ "description": "Evidence resolution result"
117
+ },
118
+ "sources": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "object",
122
+ "properties": {
123
+ "type": {
124
+ "type": "string",
125
+ "enum": ["truthpack.routes", "truthpack.env", "truthpack.auth", "truthpack.contracts", "repo.search"]
126
+ },
127
+ "pointer": {
128
+ "type": "string"
129
+ },
130
+ "confidence": {
131
+ "type": "number",
132
+ "minimum": 0,
133
+ "maximum": 1
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ },
140
+ "description": "Evidence resolution results"
141
+ },
142
+ "verdict": {
143
+ "type": "object",
144
+ "required": ["decision", "violations"],
145
+ "properties": {
146
+ "decision": {
147
+ "type": "string",
148
+ "enum": ["ALLOW", "WARN", "BLOCK"],
149
+ "description": "Final verdict"
150
+ },
151
+ "violations": {
152
+ "type": "array",
153
+ "items": {
154
+ "type": "object",
155
+ "required": ["rule", "severity", "message"],
156
+ "properties": {
157
+ "rule": {
158
+ "type": "string",
159
+ "description": "Rule ID that was violated"
160
+ },
161
+ "severity": {
162
+ "type": "string",
163
+ "enum": ["allow", "warn", "block"]
164
+ },
165
+ "message": {
166
+ "type": "string",
167
+ "description": "Violation message"
168
+ },
169
+ "claimId": {
170
+ "type": "string",
171
+ "description": "Related claim ID"
172
+ }
173
+ }
174
+ }
175
+ },
176
+ "message": {
177
+ "type": "string",
178
+ "description": "Human-readable verdict message"
179
+ }
180
+ }
181
+ },
182
+ "unblockPlan": {
183
+ "type": "object",
184
+ "properties": {
185
+ "steps": {
186
+ "type": "array",
187
+ "items": {
188
+ "type": "object",
189
+ "required": ["action", "file", "description"],
190
+ "properties": {
191
+ "action": {
192
+ "type": "string",
193
+ "enum": ["add", "modify", "create"]
194
+ },
195
+ "file": {
196
+ "type": "string"
197
+ },
198
+ "line": {
199
+ "type": "number"
200
+ },
201
+ "description": {
202
+ "type": "string"
203
+ }
204
+ }
205
+ }
206
+ }
207
+ },
208
+ "description": "Plan to unblock if verdict is BLOCK"
209
+ },
210
+ "metadata": {
211
+ "type": "object",
212
+ "properties": {
213
+ "totalFiles": {
214
+ "type": "number"
215
+ },
216
+ "totalLines": {
217
+ "type": "number"
218
+ },
219
+ "policyVersion": {
220
+ "type": "string"
221
+ },
222
+ "policyProfile": {
223
+ "type": "string"
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Change Packet Store
3
+ *
4
+ * Stores change packets in .vibecheck/packets/YYYY-MM-DD/<hash>.json
5
+ * Provides querying capabilities by agent, date, verdict.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+
13
+ /**
14
+ * Store a change packet to disk
15
+ * @param {string} projectRoot - Project root directory
16
+ * @param {object} packet - Change packet to store
17
+ * @returns {string} Path where packet was stored
18
+ */
19
+ function storePacket(projectRoot, packet) {
20
+ const packetsDir = path.join(projectRoot, ".vibecheck", "packets");
21
+
22
+ // Create date-based directory
23
+ const date = new Date(packet.timestamp);
24
+ const dateDir = date.toISOString().split('T')[0]; // YYYY-MM-DD
25
+ const dayDir = path.join(packetsDir, dateDir);
26
+
27
+ // Ensure directory exists
28
+ if (!fs.existsSync(dayDir)) {
29
+ fs.mkdirSync(dayDir, { recursive: true });
30
+ }
31
+
32
+ // Write packet file
33
+ const packetPath = path.join(dayDir, `${packet.id}.json`);
34
+ fs.writeFileSync(packetPath, JSON.stringify(packet, null, 2));
35
+
36
+ return packetPath;
37
+ }
38
+
39
+ /**
40
+ * Load a change packet by ID
41
+ * @param {string} projectRoot - Project root directory
42
+ * @param {string} packetId - Packet ID
43
+ * @returns {object|null} Change packet or null if not found
44
+ */
45
+ function loadPacket(projectRoot, packetId) {
46
+ const packetsDir = path.join(projectRoot, ".vibecheck", "packets");
47
+
48
+ // Search all date directories
49
+ if (!fs.existsSync(packetsDir)) {
50
+ return null;
51
+ }
52
+
53
+ const dateDirs = fs.readdirSync(packetsDir, { withFileTypes: true })
54
+ .filter(dirent => dirent.isDirectory())
55
+ .map(dirent => dirent.name);
56
+
57
+ for (const dateDir of dateDirs) {
58
+ const dayDir = path.join(packetsDir, dateDir);
59
+ const packetPath = path.join(dayDir, `${packetId}.json`);
60
+
61
+ if (fs.existsSync(packetPath)) {
62
+ return JSON.parse(fs.readFileSync(packetPath, "utf8"));
63
+ }
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Query change packets
71
+ * @param {string} projectRoot - Project root directory
72
+ * @param {object} filters - Query filters
73
+ * @param {string} filters.agentId - Filter by agent ID
74
+ * @param {string} filters.date - Filter by date (YYYY-MM-DD)
75
+ * @param {string} filters.verdict - Filter by verdict (ALLOW, WARN, BLOCK)
76
+ * @param {number} filters.limit - Maximum number of results
77
+ * @returns {array} Array of change packets
78
+ */
79
+ function queryPackets(projectRoot, filters = {}) {
80
+ const packetsDir = path.join(projectRoot, ".vibecheck", "packets");
81
+
82
+ if (!fs.existsSync(packetsDir)) {
83
+ return [];
84
+ }
85
+
86
+ const results = [];
87
+ const { agentId, date, verdict, limit } = filters;
88
+
89
+ // Determine which date directories to search
90
+ const dateDirs = date
91
+ ? [date]
92
+ : fs.readdirSync(packetsDir, { withFileTypes: true })
93
+ .filter(dirent => dirent.isDirectory())
94
+ .map(dirent => dirent.name)
95
+ .sort()
96
+ .reverse(); // Most recent first
97
+
98
+ for (const dateDir of dateDirs) {
99
+ const dayDir = path.join(packetsDir, dateDir);
100
+
101
+ if (!fs.existsSync(dayDir)) continue;
102
+
103
+ const packetFiles = fs.readdirSync(dayDir)
104
+ .filter(file => file.endsWith('.json'))
105
+ .map(file => path.join(dayDir, file));
106
+
107
+ for (const packetPath of packetFiles) {
108
+ try {
109
+ const packet = JSON.parse(fs.readFileSync(packetPath, "utf8"));
110
+
111
+ // Apply filters
112
+ if (agentId && packet.agentId !== agentId) continue;
113
+ if (verdict && packet.verdict?.decision !== verdict) continue;
114
+
115
+ results.push(packet);
116
+
117
+ // Apply limit
118
+ if (limit && results.length >= limit) {
119
+ return results;
120
+ }
121
+ } catch (error) {
122
+ // Skip invalid packets
123
+ continue;
124
+ }
125
+ }
126
+
127
+ // If we have a date filter, we only need to check that one directory
128
+ if (date) break;
129
+ }
130
+
131
+ return results;
132
+ }
133
+
134
+ /**
135
+ * Get statistics about stored packets
136
+ * @param {string} projectRoot - Project root directory
137
+ * @returns {object} Statistics
138
+ */
139
+ function getPacketStats(projectRoot) {
140
+ const packetsDir = path.join(projectRoot, ".vibecheck", "packets");
141
+
142
+ if (!fs.existsSync(packetsDir)) {
143
+ return {
144
+ total: 0,
145
+ byAgent: {},
146
+ byVerdict: { ALLOW: 0, WARN: 0, BLOCK: 0 },
147
+ byDate: {}
148
+ };
149
+ }
150
+
151
+ const stats = {
152
+ total: 0,
153
+ byAgent: {},
154
+ byVerdict: { ALLOW: 0, WARN: 0, BLOCK: 0 },
155
+ byDate: {}
156
+ };
157
+
158
+ const dateDirs = fs.readdirSync(packetsDir, { withFileTypes: true })
159
+ .filter(dirent => dirent.isDirectory())
160
+ .map(dirent => dirent.name);
161
+
162
+ for (const dateDir of dateDirs) {
163
+ const dayDir = path.join(packetsDir, dateDir);
164
+ if (!fs.existsSync(dayDir)) continue;
165
+
166
+ const packetFiles = fs.readdirSync(dayDir)
167
+ .filter(file => file.endsWith('.json'));
168
+
169
+ stats.byDate[dateDir] = packetFiles.length;
170
+
171
+ for (const file of packetFiles) {
172
+ try {
173
+ const packet = JSON.parse(
174
+ fs.readFileSync(path.join(dayDir, file), "utf8")
175
+ );
176
+
177
+ stats.total++;
178
+
179
+ // Count by agent
180
+ stats.byAgent[packet.agentId] = (stats.byAgent[packet.agentId] || 0) + 1;
181
+
182
+ // Count by verdict
183
+ const verdict = packet.verdict?.decision || "ALLOW";
184
+ stats.byVerdict[verdict] = (stats.byVerdict[verdict] || 0) + 1;
185
+ } catch (error) {
186
+ // Skip invalid packets
187
+ continue;
188
+ }
189
+ }
190
+ }
191
+
192
+ return stats;
193
+ }
194
+
195
+ module.exports = {
196
+ storePacket,
197
+ loadPacket,
198
+ queryPackets,
199
+ getPacketStats
200
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Claim Types
3
+ *
4
+ * Enumeration of claim types and criticality levels.
5
+ */
6
+
7
+ module.exports = {
8
+ CLAIM_TYPES: {
9
+ ROUTE: "route",
10
+ ENV: "env_used",
11
+ AUTH: "auth_boundary",
12
+ CONTRACT: "data_contract",
13
+ HTTP_CALL: "http_call",
14
+ UI_SUCCESS: "ui_success_claim",
15
+ SIDE_EFFECT: "side_effect"
16
+ },
17
+ CRITICALITY: {
18
+ HARD: "hard",
19
+ SOFT: "soft"
20
+ }
21
+ };