@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,267 @@
1
+ /**
2
+ * Time Machine Module
3
+ *
4
+ * Forensic Replay for AI Actions
5
+ * Enables deep analysis and replay of what AI agents did.
6
+ *
7
+ * Codename: Time Machine
8
+ */
9
+
10
+ "use strict";
11
+
12
+ const { ReplayEngine, createReplayEngine } = require("./replay-engine.js");
13
+ const { TimelineBuilder, createTimelineBuilder, EVENT_TYPES } = require("./timeline-builder.js");
14
+ const { StateReconstructor, createStateReconstructor } = require("./state-reconstructor.js");
15
+ const { IncidentCorrelator, createIncidentCorrelator, RELATIONSHIP_TYPES } = require("./incident-correlator.js");
16
+
17
+ /**
18
+ * Time Machine singleton for unified access
19
+ */
20
+ class TimeMachine {
21
+ constructor() {
22
+ this.replayEngine = null;
23
+ this.timelineBuilder = null;
24
+ this.stateReconstructor = null;
25
+ this.incidentCorrelator = null;
26
+ this.projectRoot = null;
27
+ this.initialized = false;
28
+ }
29
+
30
+ /**
31
+ * Initialize the Time Machine for a project
32
+ * @param {string} projectRoot - Project root directory
33
+ * @returns {TimeMachine} This instance
34
+ */
35
+ init(projectRoot) {
36
+ if (this.initialized && this.projectRoot === projectRoot) {
37
+ return this;
38
+ }
39
+
40
+ this.projectRoot = projectRoot;
41
+ this.replayEngine = createReplayEngine({ projectRoot });
42
+ this.timelineBuilder = createTimelineBuilder({ projectRoot });
43
+ this.stateReconstructor = createStateReconstructor({ projectRoot });
44
+ this.incidentCorrelator = createIncidentCorrelator({ projectRoot });
45
+ this.initialized = true;
46
+
47
+ return this;
48
+ }
49
+
50
+ /**
51
+ * Start a replay session
52
+ * @param {Object} options - Replay options
53
+ * @returns {Object} Replay session
54
+ */
55
+ async startReplay(options = {}) {
56
+ this.ensureInitialized();
57
+ return this.replayEngine.startReplay(options);
58
+ }
59
+
60
+ /**
61
+ * Get current snapshot in a replay session
62
+ * @param {string} sessionId - Session ID
63
+ * @returns {Object|null} Current snapshot
64
+ */
65
+ getCurrentSnapshot(sessionId) {
66
+ this.ensureInitialized();
67
+ return this.replayEngine.getCurrentSnapshot(sessionId);
68
+ }
69
+
70
+ /**
71
+ * Move to next snapshot
72
+ * @param {string} sessionId - Session ID
73
+ * @returns {Object|null} Next snapshot
74
+ */
75
+ nextSnapshot(sessionId) {
76
+ this.ensureInitialized();
77
+ return this.replayEngine.nextSnapshot(sessionId);
78
+ }
79
+
80
+ /**
81
+ * Move to previous snapshot
82
+ * @param {string} sessionId - Session ID
83
+ * @returns {Object|null} Previous snapshot
84
+ */
85
+ previousSnapshot(sessionId) {
86
+ this.ensureInitialized();
87
+ return this.replayEngine.previousSnapshot(sessionId);
88
+ }
89
+
90
+ /**
91
+ * Get replay session status
92
+ * @param {string} sessionId - Session ID
93
+ * @returns {Object|null} Session status
94
+ */
95
+ getSessionStatus(sessionId) {
96
+ this.ensureInitialized();
97
+ return this.replayEngine.getSessionStatus(sessionId);
98
+ }
99
+
100
+ /**
101
+ * End a replay session
102
+ * @param {string} sessionId - Session ID
103
+ */
104
+ endReplay(sessionId) {
105
+ this.ensureInitialized();
106
+ this.replayEngine.endReplay(sessionId);
107
+ }
108
+
109
+ /**
110
+ * Build a forensic timeline
111
+ * @param {Object} options - Timeline options
112
+ * @returns {Object} Forensic timeline
113
+ */
114
+ async buildTimeline(options = {}) {
115
+ this.ensureInitialized();
116
+ return this.timelineBuilder.buildTimeline(options);
117
+ }
118
+
119
+ /**
120
+ * Generate a timeline report
121
+ * @param {Object} timeline - Timeline to report on
122
+ * @returns {Object} Report
123
+ */
124
+ generateReport(timeline) {
125
+ this.ensureInitialized();
126
+ return this.timelineBuilder.generateReport(timeline);
127
+ }
128
+
129
+ /**
130
+ * Compare agent belief vs reality for a snapshot
131
+ * @param {Object} snapshot - Snapshot to analyze
132
+ * @returns {Object} Comparison result
133
+ */
134
+ compareBeliefVsReality(snapshot) {
135
+ this.ensureInitialized();
136
+ return this.replayEngine.compareBeliefVsReality(snapshot);
137
+ }
138
+
139
+ /**
140
+ * Quick replay for an incident
141
+ * @param {string} incidentId - Incident ID
142
+ * @param {Object} options - Additional options
143
+ * @returns {Object} Replay result with timeline
144
+ */
145
+ async replayIncident(incidentId, options = {}) {
146
+ this.ensureInitialized();
147
+
148
+ // Build timeline for the incident
149
+ const timeline = await this.buildTimeline({
150
+ ...options,
151
+ includeIncidents: true,
152
+ });
153
+
154
+ // Find the incident
155
+ const incidentEvent = timeline.events.find(
156
+ e => e.id === incidentId || e.details?.incidentId === incidentId
157
+ );
158
+
159
+ if (!incidentEvent) {
160
+ return {
161
+ success: false,
162
+ error: "Incident not found",
163
+ };
164
+ }
165
+
166
+ // Start replay from before the incident
167
+ const incidentTime = new Date(incidentEvent.timestamp);
168
+ const lookbackMs = options.lookbackMs || 24 * 60 * 60 * 1000; // 24 hours default
169
+
170
+ const session = await this.startReplay({
171
+ startTime: new Date(incidentTime.getTime() - lookbackMs),
172
+ endTime: incidentTime,
173
+ ...options,
174
+ });
175
+
176
+ return {
177
+ success: true,
178
+ session,
179
+ timeline,
180
+ incident: incidentEvent,
181
+ rootCause: timeline.rootCause,
182
+ causalChain: timeline.causalChain,
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Reconstruct state at a specific timestamp
188
+ * @param {Date} timestamp - Target timestamp
189
+ * @param {Object} options - Options
190
+ * @returns {Object} Reconstructed state
191
+ */
192
+ async reconstructState(timestamp, options = {}) {
193
+ this.ensureInitialized();
194
+ return this.stateReconstructor.reconstructState(timestamp, options);
195
+ }
196
+
197
+ /**
198
+ * Record a new incident
199
+ * @param {Object} incidentData - Incident data
200
+ * @returns {Object} Created incident
201
+ */
202
+ recordIncident(incidentData) {
203
+ this.ensureInitialized();
204
+ return this.incidentCorrelator.recordIncident(incidentData);
205
+ }
206
+
207
+ /**
208
+ * Correlate an incident with firewall events
209
+ * @param {string} incidentId - Incident ID
210
+ * @param {Object} options - Options
211
+ * @returns {Object} Incident report
212
+ */
213
+ async correlateIncident(incidentId, options = {}) {
214
+ this.ensureInitialized();
215
+ return this.incidentCorrelator.correlateIncident(incidentId, options);
216
+ }
217
+
218
+ /**
219
+ * Get all recorded incidents
220
+ * @returns {Object[]} All incidents
221
+ */
222
+ getAllIncidents() {
223
+ this.ensureInitialized();
224
+ return this.incidentCorrelator.getAllIncidents();
225
+ }
226
+
227
+ /**
228
+ * Create a snapshot of current state
229
+ * @param {string} snapshotId - Optional snapshot ID
230
+ * @returns {string} Snapshot ID
231
+ */
232
+ async createSnapshot(snapshotId = null) {
233
+ this.ensureInitialized();
234
+ return this.stateReconstructor.createSnapshot(snapshotId);
235
+ }
236
+
237
+ /**
238
+ * Ensure Time Machine is initialized
239
+ */
240
+ ensureInitialized() {
241
+ if (!this.initialized) {
242
+ throw new Error("Time Machine not initialized. Call timeMachine.init(projectRoot) first.");
243
+ }
244
+ }
245
+ }
246
+
247
+ // Singleton instance
248
+ const timeMachine = new TimeMachine();
249
+
250
+ module.exports = {
251
+ timeMachine,
252
+ TimeMachine,
253
+ // Replay Engine
254
+ ReplayEngine,
255
+ createReplayEngine,
256
+ // Timeline Builder
257
+ TimelineBuilder,
258
+ createTimelineBuilder,
259
+ EVENT_TYPES,
260
+ // State Reconstructor
261
+ StateReconstructor,
262
+ createStateReconstructor,
263
+ // Incident Correlator
264
+ IncidentCorrelator,
265
+ createIncidentCorrelator,
266
+ RELATIONSHIP_TYPES,
267
+ };
@@ -0,0 +1,436 @@
1
+ /**
2
+ * Time Machine Replay Engine
3
+ *
4
+ * Reconstructs codebase state at any point in time.
5
+ * Enables forensic analysis of AI agent actions.
6
+ *
7
+ * Codename: Time Machine
8
+ */
9
+
10
+ "use strict";
11
+
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const { timeMachineLogger: log, getErrorMessage } = require("../logger.js");
15
+
16
+ /**
17
+ * @typedef {Object} ReplaySnapshot
18
+ * @property {string} snapshotId - Unique snapshot ID
19
+ * @property {Date} timestamp - Point in time
20
+ * @property {Object} realityState - What the repo actually was
21
+ * @property {Object} agentBelief - What the agent assumed
22
+ * @property {Object} proposal - The change proposal
23
+ * @property {Object} verdict - The verdict issued
24
+ * @property {Object} [overrideContext] - Override info if applicable
25
+ */
26
+
27
+ /**
28
+ * @typedef {Object} ReplaySession
29
+ * @property {string} sessionId - Replay session ID
30
+ * @property {Date} startTime - When replay started
31
+ * @property {Date} targetTime - Point being replayed
32
+ * @property {ReplaySnapshot[]} snapshots - Snapshots in this session
33
+ * @property {number} currentIndex - Current position in replay
34
+ */
35
+
36
+ /**
37
+ * Replay Engine class
38
+ */
39
+ class ReplayEngine {
40
+ constructor(options = {}) {
41
+ this.projectRoot = options.projectRoot || process.cwd();
42
+ this.packetsDir = path.join(this.projectRoot, ".vibecheck", "packets");
43
+ this.auditDir = path.join(this.projectRoot, ".vibecheck", "audit");
44
+ this.snapshotsDir = path.join(this.projectRoot, ".vibecheck", "snapshots");
45
+ this.sessions = new Map();
46
+ this.packetCache = new Map();
47
+ }
48
+
49
+ /**
50
+ * Start a replay session for a specific time range
51
+ * @param {Object} options - Replay options
52
+ * @returns {ReplaySession} Replay session
53
+ */
54
+ async startReplay(options = {}) {
55
+ const {
56
+ startTime,
57
+ endTime = new Date(),
58
+ agentId = null,
59
+ file = null,
60
+ incidentId = null,
61
+ } = options;
62
+
63
+ const sessionId = `replay_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
64
+
65
+ // Load relevant packets
66
+ const packets = await this.loadPackets({
67
+ startTime,
68
+ endTime,
69
+ agentId,
70
+ file,
71
+ });
72
+
73
+ // Build snapshots from packets
74
+ const snapshots = await this.buildSnapshots(packets);
75
+
76
+ const session = {
77
+ sessionId,
78
+ startTime: new Date(),
79
+ targetTime: startTime,
80
+ options,
81
+ snapshots,
82
+ currentIndex: 0,
83
+ totalSnapshots: snapshots.length,
84
+ };
85
+
86
+ this.sessions.set(sessionId, session);
87
+
88
+ return session;
89
+ }
90
+
91
+ /**
92
+ * Load change packets for a time range
93
+ * @param {Object} filters - Filter options
94
+ * @returns {Object[]} Change packets
95
+ */
96
+ async loadPackets(filters = {}) {
97
+ const packets = [];
98
+
99
+ if (!fs.existsSync(this.packetsDir)) {
100
+ return packets;
101
+ }
102
+
103
+ const files = fs.readdirSync(this.packetsDir)
104
+ .filter(f => f.endsWith(".json"))
105
+ .sort();
106
+
107
+ for (const file of files) {
108
+ try {
109
+ const content = fs.readFileSync(path.join(this.packetsDir, file), "utf-8");
110
+ const packet = JSON.parse(content);
111
+
112
+ // Apply time filter
113
+ const packetTime = new Date(packet.timestamp || packet.createdAt);
114
+
115
+ if (filters.startTime && packetTime < new Date(filters.startTime)) {
116
+ continue;
117
+ }
118
+
119
+ if (filters.endTime && packetTime > new Date(filters.endTime)) {
120
+ continue;
121
+ }
122
+
123
+ // Apply agent filter
124
+ if (filters.agentId && packet.agentId !== filters.agentId) {
125
+ continue;
126
+ }
127
+
128
+ // Apply file filter
129
+ if (filters.file) {
130
+ const affectedFiles = this.extractAffectedFiles(packet);
131
+ if (!affectedFiles.some(f => f.includes(filters.file))) {
132
+ continue;
133
+ }
134
+ }
135
+
136
+ packets.push(packet);
137
+ } catch (error) {
138
+ log.warn(`Failed to load packet ${file}: ${getErrorMessage(error)}`);
139
+ }
140
+ }
141
+
142
+ // Sort by timestamp
143
+ packets.sort((a, b) => {
144
+ const timeA = new Date(a.timestamp || a.createdAt).getTime();
145
+ const timeB = new Date(b.timestamp || b.createdAt).getTime();
146
+ return timeA - timeB;
147
+ });
148
+
149
+ return packets;
150
+ }
151
+
152
+ /**
153
+ * Extract affected files from a packet
154
+ * @param {Object} packet - Change packet
155
+ * @returns {string[]} Affected file paths
156
+ */
157
+ extractAffectedFiles(packet) {
158
+ const files = [];
159
+
160
+ if (packet.file) files.push(packet.file);
161
+ if (packet.filePath) files.push(packet.filePath);
162
+
163
+ for (const op of packet.operations || []) {
164
+ if (op.path) files.push(op.path);
165
+ if (op.file) files.push(op.file);
166
+ if (op.filePath) files.push(op.filePath);
167
+ }
168
+
169
+ return files;
170
+ }
171
+
172
+ /**
173
+ * Build replay snapshots from packets
174
+ * @param {Object[]} packets - Change packets
175
+ * @returns {ReplaySnapshot[]} Replay snapshots
176
+ */
177
+ async buildSnapshots(packets) {
178
+ const snapshots = [];
179
+
180
+ for (const packet of packets) {
181
+ const snapshot = {
182
+ snapshotId: packet.id || `snap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
183
+ timestamp: new Date(packet.timestamp || packet.createdAt),
184
+ realityState: await this.extractRealityState(packet),
185
+ agentBelief: this.extractAgentBelief(packet),
186
+ proposal: this.extractProposal(packet),
187
+ verdict: this.extractVerdict(packet),
188
+ overrideContext: this.extractOverrideContext(packet),
189
+ metadata: {
190
+ agentId: packet.agentId,
191
+ sessionId: packet.sessionId,
192
+ file: packet.file || this.extractAffectedFiles(packet)[0],
193
+ },
194
+ };
195
+
196
+ snapshots.push(snapshot);
197
+ }
198
+
199
+ return snapshots;
200
+ }
201
+
202
+ /**
203
+ * Extract reality state from a packet
204
+ * @param {Object} packet - Change packet
205
+ * @returns {Object} Reality state
206
+ */
207
+ async extractRealityState(packet) {
208
+ // Try to load from proof artifact
209
+ if (packet.proof?.realitySnapshot) {
210
+ return packet.proof.realitySnapshot;
211
+ }
212
+
213
+ // Build from packet data
214
+ return {
215
+ files: this.extractAffectedFiles(packet).map(f => ({
216
+ path: f,
217
+ exists: true, // Would need historical data to verify
218
+ })),
219
+ timestamp: packet.timestamp || packet.createdAt,
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Extract what the agent believed/assumed
225
+ * @param {Object} packet - Change packet
226
+ * @returns {Object} Agent belief state
227
+ */
228
+ extractAgentBelief(packet) {
229
+ const assumptions = packet.assumptions || packet.proposal?.assumptions || [];
230
+
231
+ return {
232
+ assumptions: assumptions.map(a => ({
233
+ type: a.type,
234
+ target: a.target || a.path,
235
+ expectedValue: a.expectedValue || a.value,
236
+ wasValid: a.valid !== undefined ? a.valid : null,
237
+ })),
238
+ confidence: packet.confidence || packet.proposal?.confidence,
239
+ intent: packet.intent || packet.proposal?.intent,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Extract the proposal from a packet
245
+ * @param {Object} packet - Change packet
246
+ * @returns {Object} Proposal
247
+ */
248
+ extractProposal(packet) {
249
+ return {
250
+ proposalId: packet.proposalId || packet.id,
251
+ intent: packet.intent || packet.proposal?.intent,
252
+ operations: packet.operations || packet.proposal?.operations || [],
253
+ summary: packet.summary || packet.proposal?.summary,
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Extract the verdict from a packet
259
+ * @param {Object} packet - Change packet
260
+ * @returns {Object} Verdict
261
+ */
262
+ extractVerdict(packet) {
263
+ return {
264
+ decision: packet.verdict?.decision || packet.proof?.decision || packet.decision,
265
+ confidence: packet.verdict?.confidence || packet.proof?.confidence,
266
+ rulesTriggered: packet.verdict?.rulesTriggered || packet.proof?.rulesTriggered || [],
267
+ riskScore: packet.riskScore || packet.proof?.riskScore,
268
+ riskLevel: packet.riskLevel || packet.proof?.riskLevel,
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Extract override context if any
274
+ * @param {Object} packet - Change packet
275
+ * @returns {Object|null} Override context
276
+ */
277
+ extractOverrideContext(packet) {
278
+ if (!packet.proof?.overrideUsed && !packet.override?.used) {
279
+ return null;
280
+ }
281
+
282
+ return {
283
+ used: true,
284
+ by: packet.proof?.overrideBy || packet.override?.by,
285
+ reason: packet.proof?.overrideReason || packet.override?.reason,
286
+ timestamp: packet.proof?.overrideTimestamp || packet.override?.timestamp,
287
+ };
288
+ }
289
+
290
+ /**
291
+ * Get the current snapshot in a replay session
292
+ * @param {string} sessionId - Session ID
293
+ * @returns {ReplaySnapshot|null} Current snapshot
294
+ */
295
+ getCurrentSnapshot(sessionId) {
296
+ const session = this.sessions.get(sessionId);
297
+ if (!session) return null;
298
+
299
+ return session.snapshots[session.currentIndex] || null;
300
+ }
301
+
302
+ /**
303
+ * Move to the next snapshot
304
+ * @param {string} sessionId - Session ID
305
+ * @returns {ReplaySnapshot|null} Next snapshot or null if at end
306
+ */
307
+ nextSnapshot(sessionId) {
308
+ const session = this.sessions.get(sessionId);
309
+ if (!session) return null;
310
+
311
+ if (session.currentIndex < session.snapshots.length - 1) {
312
+ session.currentIndex++;
313
+ return session.snapshots[session.currentIndex];
314
+ }
315
+
316
+ return null;
317
+ }
318
+
319
+ /**
320
+ * Move to the previous snapshot
321
+ * @param {string} sessionId - Session ID
322
+ * @returns {ReplaySnapshot|null} Previous snapshot or null if at start
323
+ */
324
+ previousSnapshot(sessionId) {
325
+ const session = this.sessions.get(sessionId);
326
+ if (!session) return null;
327
+
328
+ if (session.currentIndex > 0) {
329
+ session.currentIndex--;
330
+ return session.snapshots[session.currentIndex];
331
+ }
332
+
333
+ return null;
334
+ }
335
+
336
+ /**
337
+ * Jump to a specific snapshot index
338
+ * @param {string} sessionId - Session ID
339
+ * @param {number} index - Index to jump to
340
+ * @returns {ReplaySnapshot|null} Snapshot at index
341
+ */
342
+ jumpToSnapshot(sessionId, index) {
343
+ const session = this.sessions.get(sessionId);
344
+ if (!session) return null;
345
+
346
+ if (index >= 0 && index < session.snapshots.length) {
347
+ session.currentIndex = index;
348
+ return session.snapshots[index];
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * Get session status
356
+ * @param {string} sessionId - Session ID
357
+ * @returns {Object|null} Session status
358
+ */
359
+ getSessionStatus(sessionId) {
360
+ const session = this.sessions.get(sessionId);
361
+ if (!session) return null;
362
+
363
+ return {
364
+ sessionId: session.sessionId,
365
+ totalSnapshots: session.totalSnapshots,
366
+ currentIndex: session.currentIndex,
367
+ currentSnapshot: session.snapshots[session.currentIndex],
368
+ isAtStart: session.currentIndex === 0,
369
+ isAtEnd: session.currentIndex === session.snapshots.length - 1,
370
+ progress: session.totalSnapshots > 0
371
+ ? Math.round((session.currentIndex / (session.totalSnapshots - 1)) * 100)
372
+ : 100,
373
+ };
374
+ }
375
+
376
+ /**
377
+ * End a replay session
378
+ * @param {string} sessionId - Session ID
379
+ */
380
+ endReplay(sessionId) {
381
+ this.sessions.delete(sessionId);
382
+ }
383
+
384
+ /**
385
+ * Get all active replay sessions
386
+ * @returns {Object[]} Active sessions
387
+ */
388
+ getActiveSessions() {
389
+ return Array.from(this.sessions.values()).map(s => ({
390
+ sessionId: s.sessionId,
391
+ startTime: s.startTime,
392
+ targetTime: s.targetTime,
393
+ totalSnapshots: s.totalSnapshots,
394
+ currentIndex: s.currentIndex,
395
+ }));
396
+ }
397
+
398
+ /**
399
+ * Compare what agent believed vs what was true
400
+ * @param {ReplaySnapshot} snapshot - Snapshot to analyze
401
+ * @returns {Object} Comparison result
402
+ */
403
+ compareBeliefVsReality(snapshot) {
404
+ const discrepancies = [];
405
+
406
+ for (const assumption of snapshot.agentBelief.assumptions || []) {
407
+ if (assumption.wasValid === false) {
408
+ discrepancies.push({
409
+ type: assumption.type,
410
+ target: assumption.target,
411
+ expected: assumption.expectedValue,
412
+ actual: "Did not match reality",
413
+ severity: "error",
414
+ });
415
+ }
416
+ }
417
+
418
+ return {
419
+ hadDiscrepancies: discrepancies.length > 0,
420
+ discrepancies,
421
+ confidence: snapshot.agentBelief.confidence,
422
+ verdictIssued: snapshot.verdict.decision,
423
+ };
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Create a replay engine instance
429
+ * @param {Object} options - Options
430
+ * @returns {ReplayEngine} Replay engine
431
+ */
432
+ function createReplayEngine(options = {}) {
433
+ return new ReplayEngine(options);
434
+ }
435
+
436
+ module.exports = { ReplayEngine, createReplayEngine };