@vibecheckai/cli 3.2.5 → 3.3.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.
Files changed (197) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/registry.js +192 -5
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  6. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  7. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  8. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  11. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  12. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  13. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  14. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  15. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  16. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  17. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  18. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  19. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  20. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  21. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  22. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  23. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  24. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  25. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  26. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  27. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  28. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  29. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  30. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  31. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  32. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  35. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  36. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  37. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  38. package/bin/runners/lib/analyzers.js +81 -18
  39. package/bin/runners/lib/api-client.js +269 -0
  40. package/bin/runners/lib/auth-truth.js +193 -193
  41. package/bin/runners/lib/authority-badge.js +425 -0
  42. package/bin/runners/lib/backup.js +62 -62
  43. package/bin/runners/lib/billing.js +107 -107
  44. package/bin/runners/lib/claims.js +118 -118
  45. package/bin/runners/lib/cli-output.js +7 -1
  46. package/bin/runners/lib/cli-ui.js +540 -540
  47. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  48. package/bin/runners/lib/contracts/env-contract.js +181 -181
  49. package/bin/runners/lib/contracts/external-contract.js +206 -206
  50. package/bin/runners/lib/contracts/guard.js +168 -168
  51. package/bin/runners/lib/contracts/index.js +89 -89
  52. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  53. package/bin/runners/lib/contracts/route-contract.js +199 -199
  54. package/bin/runners/lib/contracts.js +804 -804
  55. package/bin/runners/lib/detect.js +89 -89
  56. package/bin/runners/lib/doctor/autofix.js +254 -254
  57. package/bin/runners/lib/doctor/index.js +37 -37
  58. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  59. package/bin/runners/lib/doctor/modules/index.js +46 -46
  60. package/bin/runners/lib/doctor/modules/network.js +250 -250
  61. package/bin/runners/lib/doctor/modules/project.js +312 -312
  62. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  63. package/bin/runners/lib/doctor/modules/security.js +348 -348
  64. package/bin/runners/lib/doctor/modules/system.js +213 -213
  65. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  66. package/bin/runners/lib/doctor/reporter.js +262 -262
  67. package/bin/runners/lib/doctor/service.js +262 -262
  68. package/bin/runners/lib/doctor/types.js +113 -113
  69. package/bin/runners/lib/doctor/ui.js +263 -263
  70. package/bin/runners/lib/doctor-v2.js +608 -608
  71. package/bin/runners/lib/drift.js +425 -425
  72. package/bin/runners/lib/enforcement.js +72 -72
  73. package/bin/runners/lib/enterprise-detect.js +603 -603
  74. package/bin/runners/lib/enterprise-init.js +942 -942
  75. package/bin/runners/lib/env-resolver.js +417 -417
  76. package/bin/runners/lib/env-template.js +66 -66
  77. package/bin/runners/lib/env.js +189 -189
  78. package/bin/runners/lib/error-handler.js +16 -9
  79. package/bin/runners/lib/exit-codes.js +275 -0
  80. package/bin/runners/lib/extractors/client-calls.js +990 -990
  81. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  82. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  83. package/bin/runners/lib/extractors/index.js +363 -363
  84. package/bin/runners/lib/extractors/next-routes.js +524 -524
  85. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  86. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  87. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  88. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  89. package/bin/runners/lib/findings-schema.js +281 -281
  90. package/bin/runners/lib/firewall-prompt.js +50 -50
  91. package/bin/runners/lib/global-flags.js +37 -0
  92. package/bin/runners/lib/graph/graph-builder.js +265 -265
  93. package/bin/runners/lib/graph/html-renderer.js +413 -413
  94. package/bin/runners/lib/graph/index.js +32 -32
  95. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  96. package/bin/runners/lib/graph/static-extractor.js +518 -518
  97. package/bin/runners/lib/help-formatter.js +413 -0
  98. package/bin/runners/lib/html-report.js +650 -650
  99. package/bin/runners/lib/llm.js +75 -75
  100. package/bin/runners/lib/logger.js +38 -0
  101. package/bin/runners/lib/meter.js +61 -61
  102. package/bin/runners/lib/missions/evidence.js +126 -126
  103. package/bin/runners/lib/patch.js +40 -40
  104. package/bin/runners/lib/permissions/auth-model.js +213 -213
  105. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  106. package/bin/runners/lib/permissions/index.js +45 -45
  107. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  108. package/bin/runners/lib/pkgjson.js +28 -28
  109. package/bin/runners/lib/policy.js +295 -295
  110. package/bin/runners/lib/preflight.js +142 -142
  111. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  112. package/bin/runners/lib/reality/index.js +318 -318
  113. package/bin/runners/lib/reality/request-hashing.js +416 -416
  114. package/bin/runners/lib/reality/request-mapper.js +453 -453
  115. package/bin/runners/lib/reality/safety-rails.js +463 -463
  116. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  117. package/bin/runners/lib/reality/toast-detector.js +393 -393
  118. package/bin/runners/lib/reality-findings.js +84 -84
  119. package/bin/runners/lib/receipts.js +179 -179
  120. package/bin/runners/lib/redact.js +29 -29
  121. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  122. package/bin/runners/lib/replay/index.js +263 -263
  123. package/bin/runners/lib/replay/player.js +348 -348
  124. package/bin/runners/lib/replay/recorder.js +331 -331
  125. package/bin/runners/lib/report.js +135 -135
  126. package/bin/runners/lib/route-detection.js +1140 -1140
  127. package/bin/runners/lib/sandbox/index.js +59 -59
  128. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  129. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  130. package/bin/runners/lib/sandbox/worktree.js +174 -174
  131. package/bin/runners/lib/schema-validator.js +350 -350
  132. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  133. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  134. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  135. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  136. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  137. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  138. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  139. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  140. package/bin/runners/lib/schemas/validator.js +438 -438
  141. package/bin/runners/lib/score-history.js +282 -282
  142. package/bin/runners/lib/share-pack.js +239 -239
  143. package/bin/runners/lib/snippets.js +67 -67
  144. package/bin/runners/lib/unified-cli-output.js +604 -0
  145. package/bin/runners/lib/upsell.js +658 -510
  146. package/bin/runners/lib/usage.js +153 -153
  147. package/bin/runners/lib/validate-patch.js +156 -156
  148. package/bin/runners/lib/verdict-engine.js +628 -628
  149. package/bin/runners/reality/engine.js +917 -917
  150. package/bin/runners/reality/flows.js +122 -122
  151. package/bin/runners/reality/report.js +378 -378
  152. package/bin/runners/reality/session.js +193 -193
  153. package/bin/runners/runAgent.d.ts +5 -0
  154. package/bin/runners/runApprove.js +1200 -0
  155. package/bin/runners/runAuth.js +324 -95
  156. package/bin/runners/runCheckpoint.js +39 -21
  157. package/bin/runners/runClassify.js +859 -0
  158. package/bin/runners/runContext.js +136 -24
  159. package/bin/runners/runDoctor.js +108 -68
  160. package/bin/runners/runFirewall.d.ts +5 -0
  161. package/bin/runners/runFirewallHook.d.ts +5 -0
  162. package/bin/runners/runFix.js +6 -5
  163. package/bin/runners/runGuard.js +262 -168
  164. package/bin/runners/runInit.js +3 -2
  165. package/bin/runners/runMcp.js +130 -52
  166. package/bin/runners/runPolish.js +43 -20
  167. package/bin/runners/runProve.js +1 -2
  168. package/bin/runners/runReport.js +3 -2
  169. package/bin/runners/runScan.js +145 -44
  170. package/bin/runners/runShip.js +3 -4
  171. package/bin/runners/runTruth.d.ts +5 -0
  172. package/bin/runners/runValidate.js +19 -2
  173. package/bin/runners/runWatch.js +104 -53
  174. package/bin/vibecheck.js +106 -19
  175. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  176. package/mcp-server/agent-firewall-interceptor.js +367 -31
  177. package/mcp-server/authority-tools.js +569 -0
  178. package/mcp-server/conductor/conflict-resolver.js +588 -0
  179. package/mcp-server/conductor/execution-planner.js +544 -0
  180. package/mcp-server/conductor/index.js +377 -0
  181. package/mcp-server/conductor/lock-manager.js +615 -0
  182. package/mcp-server/conductor/request-queue.js +550 -0
  183. package/mcp-server/conductor/session-manager.js +500 -0
  184. package/mcp-server/conductor/tools.js +510 -0
  185. package/mcp-server/index.js +1199 -208
  186. package/mcp-server/lib/api-client.cjs +305 -0
  187. package/mcp-server/lib/logger.cjs +30 -0
  188. package/mcp-server/logger.js +173 -0
  189. package/mcp-server/package.json +2 -2
  190. package/mcp-server/premium-tools.js +2 -2
  191. package/mcp-server/tier-auth.js +351 -136
  192. package/mcp-server/tools/index.js +72 -72
  193. package/mcp-server/truth-firewall-tools.js +145 -15
  194. package/mcp-server/vibecheck-tools.js +2 -2
  195. package/package.json +2 -3
  196. package/mcp-server/index.old.js +0 -4137
  197. package/mcp-server/package-lock.json +0 -165
@@ -0,0 +1,490 @@
1
+ /**
2
+ * Time Machine State Reconstructor
3
+ *
4
+ * Rebuilds file and reality state at any timestamp.
5
+ * Shows what the AI thought vs what was actually true.
6
+ *
7
+ * Codename: Time Machine
8
+ */
9
+
10
+ "use strict";
11
+
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const crypto = require("crypto");
15
+
16
+ /**
17
+ * @typedef {Object} FileState
18
+ * @property {string} path - File path
19
+ * @property {boolean} existed - Did file exist at timestamp
20
+ * @property {string} [content] - File content if available
21
+ * @property {string} [hash] - Content hash
22
+ * @property {Object} [metadata] - Additional metadata
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} ReconstructedState
27
+ * @property {Date} timestamp - Point in time
28
+ * @property {FileState[]} files - File states
29
+ * @property {Object} routes - Route registry
30
+ * @property {Object} envVars - Environment variables
31
+ * @property {Object} services - Service definitions
32
+ * @property {string} snapshotId - Snapshot ID used
33
+ */
34
+
35
+ /**
36
+ * @typedef {Object} BeliefVsRealityComparison
37
+ * @property {Object} agentBelief - What agent assumed
38
+ * @property {Object} actualReality - What was true
39
+ * @property {Object[]} discrepancies - Differences found
40
+ * @property {number} accuracyScore - How accurate was the agent
41
+ */
42
+
43
+ /**
44
+ * State Reconstructor class
45
+ */
46
+ class StateReconstructor {
47
+ constructor(options = {}) {
48
+ this.projectRoot = options.projectRoot || process.cwd();
49
+ this.snapshotsDir = path.join(this.projectRoot, ".vibecheck", "snapshots");
50
+ this.packetsDir = path.join(this.projectRoot, ".vibecheck", "packets");
51
+ this.guardrailDir = path.join(this.projectRoot, ".guardrail");
52
+ this.snapshotCache = new Map();
53
+ }
54
+
55
+ /**
56
+ * Reconstruct state at a specific timestamp
57
+ * @param {Date} timestamp - Target timestamp
58
+ * @param {Object} options - Options
59
+ * @returns {ReconstructedState} Reconstructed state
60
+ */
61
+ async reconstructState(timestamp, options = {}) {
62
+ const {
63
+ files = null, // Specific files to reconstruct
64
+ includeContent = false,
65
+ useSnapshots = true,
66
+ } = options;
67
+
68
+ // Find the nearest snapshot before the timestamp
69
+ const nearestSnapshot = await this.findNearestSnapshot(timestamp);
70
+
71
+ // Load the snapshot or build from packets
72
+ let baseState;
73
+
74
+ if (nearestSnapshot && useSnapshots) {
75
+ baseState = await this.loadSnapshot(nearestSnapshot);
76
+ } else {
77
+ baseState = await this.buildStateFromPackets(timestamp);
78
+ }
79
+
80
+ // Apply any changes between snapshot and target time
81
+ if (nearestSnapshot) {
82
+ baseState = await this.applyChanges(baseState, nearestSnapshot.timestamp, timestamp);
83
+ }
84
+
85
+ // Filter to specific files if requested
86
+ if (files && files.length > 0) {
87
+ baseState.files = baseState.files.filter(f =>
88
+ files.some(reqFile => f.path.includes(reqFile) || reqFile.includes(f.path))
89
+ );
90
+ }
91
+
92
+ // Optionally load content
93
+ if (includeContent) {
94
+ await this.loadFileContents(baseState, timestamp);
95
+ }
96
+
97
+ return {
98
+ timestamp,
99
+ snapshotId: nearestSnapshot?.id || `reconstructed_${timestamp.getTime()}`,
100
+ ...baseState,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Find the nearest snapshot before a timestamp
106
+ * @param {Date} timestamp - Target timestamp
107
+ * @returns {Object|null} Nearest snapshot or null
108
+ */
109
+ async findNearestSnapshot(timestamp) {
110
+ if (!fs.existsSync(this.snapshotsDir)) {
111
+ return null;
112
+ }
113
+
114
+ const targetTime = timestamp.getTime();
115
+ let nearestSnapshot = null;
116
+ let nearestDiff = Infinity;
117
+
118
+ const files = fs.readdirSync(this.snapshotsDir)
119
+ .filter(f => f.endsWith(".json"))
120
+ .sort();
121
+
122
+ for (const file of files) {
123
+ try {
124
+ const snapshotPath = path.join(this.snapshotsDir, file);
125
+ const content = fs.readFileSync(snapshotPath, "utf-8");
126
+ const snapshot = JSON.parse(content);
127
+
128
+ const snapshotTime = new Date(snapshot.timestamp).getTime();
129
+
130
+ // Only consider snapshots before or at the target time
131
+ if (snapshotTime <= targetTime) {
132
+ const diff = targetTime - snapshotTime;
133
+ if (diff < nearestDiff) {
134
+ nearestDiff = diff;
135
+ nearestSnapshot = {
136
+ id: file.replace(".json", ""),
137
+ path: snapshotPath,
138
+ timestamp: new Date(snapshot.timestamp),
139
+ data: snapshot,
140
+ };
141
+ }
142
+ }
143
+ } catch {
144
+ // Skip invalid snapshots
145
+ }
146
+ }
147
+
148
+ return nearestSnapshot;
149
+ }
150
+
151
+ /**
152
+ * Load a snapshot
153
+ * @param {Object} snapshotInfo - Snapshot info
154
+ * @returns {Object} Loaded state
155
+ */
156
+ async loadSnapshot(snapshotInfo) {
157
+ if (this.snapshotCache.has(snapshotInfo.id)) {
158
+ return JSON.parse(JSON.stringify(this.snapshotCache.get(snapshotInfo.id)));
159
+ }
160
+
161
+ const state = {
162
+ files: snapshotInfo.data.files || [],
163
+ routes: snapshotInfo.data.routes || {},
164
+ envVars: snapshotInfo.data.envVars || {},
165
+ services: snapshotInfo.data.services || {},
166
+ };
167
+
168
+ this.snapshotCache.set(snapshotInfo.id, state);
169
+
170
+ return JSON.parse(JSON.stringify(state));
171
+ }
172
+
173
+ /**
174
+ * Build state from change packets
175
+ * @param {Date} timestamp - Target timestamp
176
+ * @returns {Object} Built state
177
+ */
178
+ async buildStateFromPackets(timestamp) {
179
+ const state = {
180
+ files: [],
181
+ routes: {},
182
+ envVars: {},
183
+ services: {},
184
+ };
185
+
186
+ // Try to load from guardrail context
187
+ const contextPath = path.join(this.guardrailDir, "context-snapshot.json");
188
+
189
+ if (fs.existsSync(contextPath)) {
190
+ try {
191
+ const context = JSON.parse(fs.readFileSync(contextPath, "utf-8"));
192
+
193
+ // Extract file information
194
+ if (context.files) {
195
+ state.files = Object.entries(context.files).map(([filePath, info]) => ({
196
+ path: filePath,
197
+ existed: true,
198
+ hash: info.hash,
199
+ metadata: info,
200
+ }));
201
+ }
202
+
203
+ // Extract routes
204
+ if (context.routes) {
205
+ state.routes = context.routes;
206
+ }
207
+
208
+ // Extract env vars
209
+ if (context.envVars) {
210
+ state.envVars = context.envVars;
211
+ }
212
+ } catch {
213
+ // Ignore errors
214
+ }
215
+ }
216
+
217
+ // Apply changes from packets up to timestamp
218
+ if (fs.existsSync(this.packetsDir)) {
219
+ const packets = fs.readdirSync(this.packetsDir)
220
+ .filter(f => f.endsWith(".json"))
221
+ .sort();
222
+
223
+ for (const packetFile of packets) {
224
+ try {
225
+ const packet = JSON.parse(
226
+ fs.readFileSync(path.join(this.packetsDir, packetFile), "utf-8")
227
+ );
228
+
229
+ const packetTime = new Date(packet.timestamp || packet.createdAt);
230
+ if (packetTime > timestamp) break;
231
+
232
+ // Apply packet changes to state
233
+ this.applyPacketToState(state, packet);
234
+ } catch {
235
+ // Skip invalid packets
236
+ }
237
+ }
238
+ }
239
+
240
+ return state;
241
+ }
242
+
243
+ /**
244
+ * Apply a packet's changes to state
245
+ * @param {Object} state - State to modify
246
+ * @param {Object} packet - Packet to apply
247
+ */
248
+ applyPacketToState(state, packet) {
249
+ for (const op of packet.operations || []) {
250
+ const filePath = op.path || op.file;
251
+ if (!filePath) continue;
252
+
253
+ const existingIndex = state.files.findIndex(f => f.path === filePath);
254
+
255
+ switch (op.type) {
256
+ case "create":
257
+ if (existingIndex === -1) {
258
+ state.files.push({
259
+ path: filePath,
260
+ existed: true,
261
+ hash: op.content ? this.hashContent(op.content) : null,
262
+ });
263
+ } else {
264
+ state.files[existingIndex].existed = true;
265
+ if (op.content) {
266
+ state.files[existingIndex].hash = this.hashContent(op.content);
267
+ }
268
+ }
269
+ break;
270
+
271
+ case "delete":
272
+ if (existingIndex !== -1) {
273
+ state.files[existingIndex].existed = false;
274
+ }
275
+ break;
276
+
277
+ case "modify":
278
+ if (existingIndex !== -1 && op.content) {
279
+ state.files[existingIndex].hash = this.hashContent(op.content);
280
+ }
281
+ break;
282
+ }
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Apply changes between two timestamps
288
+ * @param {Object} state - Base state
289
+ * @param {Date} fromTime - Start time
290
+ * @param {Date} toTime - End time
291
+ * @returns {Object} Updated state
292
+ */
293
+ async applyChanges(state, fromTime, toTime) {
294
+ if (!fs.existsSync(this.packetsDir)) {
295
+ return state;
296
+ }
297
+
298
+ const packets = fs.readdirSync(this.packetsDir)
299
+ .filter(f => f.endsWith(".json"))
300
+ .sort();
301
+
302
+ for (const packetFile of packets) {
303
+ try {
304
+ const packet = JSON.parse(
305
+ fs.readFileSync(path.join(this.packetsDir, packetFile), "utf-8")
306
+ );
307
+
308
+ const packetTime = new Date(packet.timestamp || packet.createdAt);
309
+
310
+ if (packetTime <= fromTime) continue;
311
+ if (packetTime > toTime) break;
312
+
313
+ this.applyPacketToState(state, packet);
314
+ } catch {
315
+ // Skip invalid packets
316
+ }
317
+ }
318
+
319
+ return state;
320
+ }
321
+
322
+ /**
323
+ * Load file contents for a state
324
+ * @param {Object} state - State to load content for
325
+ * @param {Date} timestamp - Target timestamp
326
+ */
327
+ async loadFileContents(state, timestamp) {
328
+ // For now, we can only load current content
329
+ // Historical content would require git integration
330
+ for (const file of state.files) {
331
+ if (file.existed) {
332
+ const fullPath = path.resolve(this.projectRoot, file.path);
333
+
334
+ if (fs.existsSync(fullPath)) {
335
+ try {
336
+ file.content = fs.readFileSync(fullPath, "utf-8");
337
+ file.currentHash = this.hashContent(file.content);
338
+ } catch {
339
+ // Skip unreadable files
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Compare agent belief vs actual reality
348
+ * @param {Object} agentBelief - What agent assumed
349
+ * @param {ReconstructedState} actualState - Reconstructed reality
350
+ * @returns {BeliefVsRealityComparison} Comparison result
351
+ */
352
+ compareBeliefVsReality(agentBelief, actualState) {
353
+ const discrepancies = [];
354
+ let correctAssumptions = 0;
355
+ let totalAssumptions = 0;
356
+
357
+ for (const assumption of agentBelief.assumptions || []) {
358
+ totalAssumptions++;
359
+
360
+ const check = this.checkAssumption(assumption, actualState);
361
+
362
+ if (!check.valid) {
363
+ discrepancies.push({
364
+ assumption: assumption.type,
365
+ target: assumption.target,
366
+ expected: assumption.expectedValue,
367
+ actual: check.actualValue,
368
+ message: check.message,
369
+ });
370
+ } else {
371
+ correctAssumptions++;
372
+ }
373
+ }
374
+
375
+ const accuracyScore = totalAssumptions > 0
376
+ ? Math.round((correctAssumptions / totalAssumptions) * 100)
377
+ : 100;
378
+
379
+ return {
380
+ agentBelief,
381
+ actualReality: {
382
+ fileCount: actualState.files.filter(f => f.existed).length,
383
+ routeCount: Object.keys(actualState.routes).length,
384
+ envVarCount: Object.keys(actualState.envVars).length,
385
+ },
386
+ discrepancies,
387
+ accuracyScore,
388
+ totalAssumptions,
389
+ correctAssumptions,
390
+ };
391
+ }
392
+
393
+ /**
394
+ * Check if an assumption matches reality
395
+ * @param {Object} assumption - Assumption to check
396
+ * @param {ReconstructedState} state - Reality state
397
+ * @returns {Object} Check result
398
+ */
399
+ checkAssumption(assumption, state) {
400
+ switch (assumption.type) {
401
+ case "file_exists": {
402
+ const file = state.files.find(f => f.path === assumption.target);
403
+ const exists = file?.existed ?? false;
404
+ const expected = assumption.expectedValue !== false;
405
+ return {
406
+ valid: exists === expected,
407
+ actualValue: exists,
408
+ message: exists === expected ? "Match" : `File ${expected ? "does not exist" : "exists unexpectedly"}`,
409
+ };
410
+ }
411
+
412
+ case "route_exists": {
413
+ const routeExists = !!state.routes[assumption.target];
414
+ const expected = assumption.expectedValue !== false;
415
+ return {
416
+ valid: routeExists === expected,
417
+ actualValue: routeExists,
418
+ message: routeExists === expected ? "Match" : `Route ${expected ? "does not exist" : "exists unexpectedly"}`,
419
+ };
420
+ }
421
+
422
+ case "env_exists": {
423
+ const envExists = !!state.envVars[assumption.target];
424
+ const expected = assumption.expectedValue !== false;
425
+ return {
426
+ valid: envExists === expected,
427
+ actualValue: envExists,
428
+ message: envExists === expected ? "Match" : `Env var ${expected ? "does not exist" : "exists unexpectedly"}`,
429
+ };
430
+ }
431
+
432
+ default:
433
+ return {
434
+ valid: true,
435
+ actualValue: "unknown",
436
+ message: "Cannot verify assumption type",
437
+ };
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Create a snapshot of current state
443
+ * @param {string} snapshotId - Optional snapshot ID
444
+ * @returns {string} Snapshot ID
445
+ */
446
+ async createSnapshot(snapshotId = null) {
447
+ const id = snapshotId || `snapshot_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
448
+
449
+ // Build current state
450
+ const state = await this.buildStateFromPackets(new Date());
451
+
452
+ const snapshot = {
453
+ id,
454
+ timestamp: new Date().toISOString(),
455
+ ...state,
456
+ };
457
+
458
+ // Save snapshot
459
+ if (!fs.existsSync(this.snapshotsDir)) {
460
+ fs.mkdirSync(this.snapshotsDir, { recursive: true });
461
+ }
462
+
463
+ fs.writeFileSync(
464
+ path.join(this.snapshotsDir, `${id}.json`),
465
+ JSON.stringify(snapshot, null, 2)
466
+ );
467
+
468
+ return id;
469
+ }
470
+
471
+ /**
472
+ * Hash content for comparison
473
+ * @param {string} content - Content to hash
474
+ * @returns {string} Hash
475
+ */
476
+ hashContent(content) {
477
+ return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Create a state reconstructor instance
483
+ * @param {Object} options - Options
484
+ * @returns {StateReconstructor} State reconstructor
485
+ */
486
+ function createStateReconstructor(options = {}) {
487
+ return new StateReconstructor(options);
488
+ }
489
+
490
+ module.exports = { StateReconstructor, createStateReconstructor };