rea-agents 2.2.0 → 2.4.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 (179) hide show
  1. package/README.md +57 -10
  2. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  3. package/dist/application/ArtifactExtraction.js +5 -1
  4. package/dist/application/ArtifactGraphConstruction.js +3 -15
  5. package/dist/application/ArtifactInventory/classify.js +5 -1
  6. package/dist/application/ArtifactInventory/reader.js +2 -0
  7. package/dist/application/BinarySession.js +9 -2
  8. package/dist/application/BinarySessionStatus.js +40 -1
  9. package/dist/application/CapabilityInventory.js +26 -3
  10. package/dist/application/CommandShimReplay.js +6 -3
  11. package/dist/application/CompositeProvider.js +5 -2
  12. package/dist/application/DirectAnalysis.js +1 -20
  13. package/dist/application/DirectAnalysisStatus.js +83 -0
  14. package/dist/application/Doctor.js +34 -25
  15. package/dist/application/DoctorDiagnostics.js +1 -4
  16. package/dist/application/DoctorProjection.js +43 -0
  17. package/dist/application/DoctorScope.js +102 -0
  18. package/dist/application/EvidenceReferenceResolver.js +26 -0
  19. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  20. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  21. package/dist/application/JavaScriptApplicationService.js +3 -2
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  24. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  25. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  26. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  27. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  28. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  29. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  30. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  31. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  32. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  33. package/dist/application/LoopbackReplay.js +277 -133
  34. package/dist/application/LoopbackReplayRecorder.js +101 -0
  35. package/dist/application/PermissionAuthority.js +7 -3
  36. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +25 -13
  39. package/dist/application/ProcessCheckpoints.js +35 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +64 -83
  42. package/dist/application/ProcessNormalization.js +9 -2
  43. package/dist/application/ProcessReactiveCoordinator.js +225 -0
  44. package/dist/application/ProcessReactiveEffects.js +141 -0
  45. package/dist/application/ProcessReactiveHarness.js +85 -0
  46. package/dist/application/ProcessReactiveObservations.js +105 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +11 -10
  50. package/dist/application/SetupPlan.js +1 -1
  51. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  52. package/dist/application/TerminalRenderer.js +3 -1
  53. package/dist/browser/CdpEndpoint.js +37 -4
  54. package/dist/catalogIdentity.js +16 -7
  55. package/dist/cli/coreAnalysisCommands.js +34 -2
  56. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  57. package/dist/cli/setupCommands.js +81 -32
  58. package/dist/cli/utilityCommands.js +10 -2
  59. package/dist/cliApplicationCommands.js +79 -16
  60. package/dist/cliCommandNames.js +3 -0
  61. package/dist/cliElectronCommands.js +23 -33
  62. package/dist/cliObservationOptions.js +1 -1
  63. package/dist/cliOutput.js +56 -0
  64. package/dist/cliProcessCommands.js +32 -1
  65. package/dist/cliSetup.js +30 -21
  66. package/dist/contracts/applicationToolContracts.js +54 -7
  67. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  68. package/dist/contracts/electronToolContracts.js +53 -32
  69. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  70. package/dist/contracts/processCaptureExample.js +1 -0
  71. package/dist/contracts/replayMachineExample.js +37 -0
  72. package/dist/contracts/toolContractExamples.js +2 -0
  73. package/dist/contracts/toolContracts.js +5 -1
  74. package/dist/contracts/toolEffects.js +3 -0
  75. package/dist/contracts/toolOutputSchemaGroups.js +2 -0
  76. package/dist/contracts/toolOutputSchemaPrimitives.js +43 -0
  77. package/dist/doctorRuntime.js +41 -0
  78. package/dist/domain/analysisErrorPresentation.js +5 -3
  79. package/dist/domain/analysisErrorProjection.js +7 -1
  80. package/dist/domain/analysisSnapshot.js +2 -0
  81. package/dist/domain/artifactGraph.js +2 -0
  82. package/dist/domain/binaryTarget.js +3 -6
  83. package/dist/domain/changedBehavior.js +5 -4
  84. package/dist/domain/completionLedgerGeneration.js +190 -0
  85. package/dist/domain/errors.js +4 -2
  86. package/dist/domain/evidence.js +2 -0
  87. package/dist/domain/evidenceCompletionLedger.js +113 -0
  88. package/dist/domain/hopperStartupFailure.js +9 -0
  89. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  90. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  91. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  92. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  93. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  94. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  95. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  96. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  97. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  98. package/dist/domain/javascriptSemanticCalls.js +269 -0
  99. package/dist/domain/javascriptSemanticGraph.js +356 -0
  100. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  101. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  102. package/dist/domain/javascriptSemanticIr.js +14 -1
  103. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  104. package/dist/domain/javascriptSemanticProjection.js +36 -4
  105. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  106. package/dist/domain/javascriptSemanticQuery.js +294 -0
  107. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  108. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  109. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  110. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  111. package/dist/domain/javascriptSemanticReturns.js +187 -0
  112. package/dist/domain/javascriptSemanticState.js +32 -0
  113. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  114. package/dist/domain/javascriptSemanticValues.js +54 -70
  115. package/dist/domain/processCapture.js +100 -0
  116. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  117. package/dist/domain/processCaptureValidation.js +103 -12
  118. package/dist/domain/processComparison.js +78 -25
  119. package/dist/domain/processObservation.js +162 -0
  120. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  121. package/dist/domain/processReactiveMatching.js +210 -0
  122. package/dist/domain/processReactiveRuntime.js +120 -0
  123. package/dist/domain/processReactiveScenario.js +361 -0
  124. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  125. package/dist/domain/processReactiveTransition.js +161 -0
  126. package/dist/domain/processScenario.js +33 -0
  127. package/dist/domain/processTraceComparison.js +43 -0
  128. package/dist/domain/processTraceDimensionProjection.js +50 -0
  129. package/dist/domain/processTraceEvaluation.js +312 -0
  130. package/dist/domain/processTraceSpecification.js +373 -0
  131. package/dist/domain/reconstructionVerification.js +5 -4
  132. package/dist/domain/replayMachine.js +18 -2
  133. package/dist/domain/replayMachineRun.js +216 -0
  134. package/dist/domain/replayMachineRuntime.js +88 -9
  135. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  136. package/dist/domain/zipPackageFormat.js +20 -0
  137. package/dist/evaluation/CodexAgentEval.js +24 -0
  138. package/dist/generatedPackageMetadata.js +1 -1
  139. package/dist/ghidra/GhidraClient.js +13 -2
  140. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  141. package/dist/ghidra/GhidraLauncher.js +7 -3
  142. package/dist/ghidra/GhidraProvider.js +8 -1
  143. package/dist/hopper/BridgeLauncher.js +56 -14
  144. package/dist/hopper/HopperClient.js +23 -10
  145. package/dist/hopper/HopperDiagnostics.js +14 -0
  146. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  147. package/dist/hopper/HopperProvider.js +8 -1
  148. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  149. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  150. package/dist/main/transport.js +2 -0
  151. package/dist/mcpDoctor.js +310 -0
  152. package/dist/native/NativeMacOSProvider.js +1 -0
  153. package/dist/process/ProcessOwnership.js +200 -53
  154. package/dist/process/ProviderRunLineage.js +21 -0
  155. package/dist/server/createServer.js +4 -1
  156. package/dist/server/javascriptApplicationResult.js +22 -1
  157. package/dist/server/registerApplicationTools/characterization.js +3 -3
  158. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  159. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  160. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  161. package/dist/server/registerApplicationTools/coverage.js +3 -3
  162. package/dist/server/registerApplicationTools/helpers.js +37 -25
  163. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  164. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  165. package/dist/server/registerApplicationTools.js +4 -0
  166. package/dist/server/registerEvidenceResources.js +5 -1
  167. package/dist/server/registerProcessComparisonTool.js +17 -7
  168. package/dist/server/registerReplayMachineTool.js +20 -0
  169. package/dist/server/registerSessionTools.js +12 -7
  170. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  171. package/dist/serverIdentity.js +19 -0
  172. package/package.json +62 -30
  173. package/scripts/hopper-demo-x11.py +459 -61
  174. package/scripts/prepack.mjs +55 -0
  175. package/scripts/prepare.mjs +28 -0
  176. package/scripts/rea.mjs +26 -5
  177. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  178. package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
  179. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
@@ -2,6 +2,8 @@ import { createHash } from "node:crypto";
2
2
  import { resolve } from "node:path";
3
3
  import canonicalize from "canonicalize";
4
4
  import { z } from "zod";
5
+ import { processReactiveScenarioSchema, } from "./processReactiveScenario.js";
6
+ import { replayMachineSchema } from "./replayMachine.js";
5
7
  const positiveBudget = z.number().int().positive();
6
8
  const timedEventBase = { at_ms: z.number().int().nonnegative() };
7
9
  const environmentName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/u);
@@ -43,6 +45,23 @@ export const digestProcessCommitment = (value) => {
43
45
  throw new TypeError("Process capture commitment is not canonical JSON");
44
46
  return createHash("sha256").update(serialized).digest("hex");
45
47
  };
48
+ const reactiveScenarioCommitment = (scenario) => scenario === null
49
+ ? null
50
+ : {
51
+ ...scenario,
52
+ states: scenario.states.map((state) => ({
53
+ ...state,
54
+ on: state.on.map((transition) => ({
55
+ ...transition,
56
+ actions: transition.actions.map((action) => action.type === "send_input" && action.sensitive
57
+ ? {
58
+ ...action,
59
+ data: `<redacted-input:${String(Buffer.byteLength(action.data))}-bytes>`,
60
+ }
61
+ : action),
62
+ })),
63
+ })),
64
+ };
46
65
  /** Build the secret-safe scenario projection committed by Process Capture v4. */
47
66
  export const processScenarioCommitment = (scenario, executableSha256) => ({
48
67
  ...scenario,
@@ -50,6 +69,7 @@ export const processScenarioCommitment = (scenario, executableSha256) => ({
50
69
  name,
51
70
  scenario.secret_aliases.includes(name) ? "<redacted-secret>" : value,
52
71
  ])),
72
+ reactive: reactiveScenarioCommitment(scenario.reactive),
53
73
  unknown_registry_approved: scenario.unknown_registry_approved === true,
54
74
  executable_sha256: executableSha256 ?? null,
55
75
  });
@@ -70,6 +90,7 @@ export const processComparisonContract = (scenario) => ({
70
90
  normalization: scenario.normalization,
71
91
  command_shims: scenario.command_shims,
72
92
  replay: scenario.replay,
93
+ reactive: reactiveScenarioCommitment(scenario.reactive),
73
94
  });
74
95
  /**
75
96
  * Boundary schema for one explicitly approved, bounded process experiment.
@@ -213,6 +234,7 @@ export const processScenarioSchema = z
213
234
  }),
214
235
  replay: z
215
236
  .object({
237
+ machine: replayMachineSchema.nullable().default(null),
216
238
  http: z
217
239
  .array(z.object({
218
240
  method: z.string().max(16),
@@ -251,10 +273,12 @@ export const processScenarioSchema = z
251
273
  .default([]),
252
274
  })
253
275
  .default({
276
+ machine: null,
254
277
  http: [],
255
278
  websocket_messages: [],
256
279
  websocket_connections: [],
257
280
  }),
281
+ reactive: processReactiveScenarioSchema.nullable().default(null),
258
282
  })
259
283
  .strict()
260
284
  .superRefine((scenario, context) => {
@@ -327,6 +351,15 @@ export const processScenarioSchema = z
327
351
  message: "command shim names must be unique",
328
352
  path: ["command_shims"],
329
353
  });
354
+ if (scenario.replay.machine !== null &&
355
+ (scenario.replay.http.length > 0 ||
356
+ scenario.replay.websocket_messages.length > 0 ||
357
+ scenario.replay.websocket_connections.length > 0))
358
+ context.addIssue({
359
+ code: "custom",
360
+ message: "a replay machine cannot be combined with static HTTP or WebSocket scripts",
361
+ path: ["replay"],
362
+ });
330
363
  });
331
364
  /** Parse untrusted process scenario input and apply safe default budgets. */
332
365
  export const parseProcessScenario = (input) => processScenarioSchema.parse(input);
@@ -0,0 +1,43 @@
1
+ import { validateProcessCapture } from "./processCaptureValidation.js";
2
+ import { evaluateProcessTraceSide, processTraceComparisonResultSchema, } from "./processTraceEvaluation.js";
3
+ import { processTraceSpecificationSchema, } from "./processTraceSpecification.js";
4
+ export { processTraceComparisonResultSchema, } from "./processTraceEvaluation.js";
5
+ export { processTraceSourceSchema, processTraceSpecificationSchema, } from "./processTraceSpecification.js";
6
+ /** Compare two captures against an explicit finite trace language. */
7
+ export const compareProcessTraces = (left, right, specificationInput) => {
8
+ const captureIssues = [
9
+ ...validateProcessCapture(left),
10
+ ...validateProcessCapture(right),
11
+ ];
12
+ if (captureIssues.length > 0)
13
+ throw new TypeError(`Invalid Process Capture v4: ${captureIssues[0].path}`);
14
+ const specification = processTraceSpecificationSchema.parse(specificationInput);
15
+ const leftEvaluation = evaluateProcessTraceSide(left, specification);
16
+ const rightEvaluation = evaluateProcessTraceSide(right, specification);
17
+ const diagnostic = leftEvaluation.diagnostic === null
18
+ ? rightEvaluation.diagnostic === null
19
+ ? null
20
+ : { side: "right", ...rightEvaluation.diagnostic }
21
+ : { side: "left", ...leftEvaluation.diagnostic };
22
+ const outcomesDiffer = leftEvaluation.result.status !== rightEvaluation.result.status ||
23
+ JSON.stringify(leftEvaluation.result.raw_trace.map(({ event_id }) => event_id)) !==
24
+ JSON.stringify(rightEvaluation.result.raw_trace.map(({ event_id }) => event_id));
25
+ return processTraceComparisonResultSchema.parse({
26
+ verdict: leftEvaluation.result.status === "unknown" ||
27
+ rightEvaluation.result.status === "unknown"
28
+ ? "unknown"
29
+ : leftEvaluation.result.status === "pass" &&
30
+ rightEvaluation.result.status === "pass"
31
+ ? "equivalent"
32
+ : outcomesDiffer
33
+ ? "different"
34
+ : "nonconforming",
35
+ left: leftEvaluation.result,
36
+ right: rightEvaluation.result,
37
+ diagnostic,
38
+ });
39
+ };
40
+ /** Whether trace conformance or raw declared order differs across captures. */
41
+ export const processTraceOutcomesDiffer = (comparison) => comparison.left.status !== comparison.right.status ||
42
+ JSON.stringify(comparison.left.raw_trace.map(({ event_id }) => event_id)) !==
43
+ JSON.stringify(comparison.right.raw_trace.map(({ event_id }) => event_id));
@@ -0,0 +1,50 @@
1
+ /** Map declared trace sources to legacy process-comparison dimensions. */
2
+ export const dimensionsForTraceSources = (sources) => {
3
+ const dimensions = new Set();
4
+ for (const source of sources) {
5
+ if (source.startsWith("terminal_"))
6
+ dimensions.add("terminal");
7
+ else if (source === "interaction")
8
+ dimensions.add("interaction");
9
+ else if (source === "lifecycle")
10
+ dimensions.add("exit");
11
+ else if (source === "filesystem")
12
+ dimensions.add("filesystem");
13
+ else if (source === "http" ||
14
+ source === "websocket" ||
15
+ source === "replay_transition")
16
+ dimensions.add("protocol");
17
+ else if (source === "process")
18
+ dimensions.add("process");
19
+ else if (source === "shim")
20
+ dimensions.add("shim");
21
+ }
22
+ return dimensions;
23
+ };
24
+ /** Whether a trace specification covers every observed source in a dimension. */
25
+ export const traceCoversObservedDimension = (dimension, sources, left, right) => {
26
+ const any = (leftValues, rightValues) => leftValues.length > 0 || rightValues.length > 0;
27
+ switch (dimension) {
28
+ case "terminal":
29
+ return ((!any(left.frames, right.frames) || sources.has("terminal_raw")) &&
30
+ (!any(left.rendered_frames, right.rendered_frames) ||
31
+ sources.has("terminal_rendered")));
32
+ case "interaction":
33
+ return sources.has("interaction");
34
+ case "exit":
35
+ return sources.has("lifecycle");
36
+ case "filesystem":
37
+ return false;
38
+ case "protocol":
39
+ return ((![...left.protocol_events, ...right.protocol_events].some(({ protocol }) => protocol === "http") ||
40
+ sources.has("http")) &&
41
+ (![...left.protocol_events, ...right.protocol_events].some(({ protocol }) => protocol === "websocket") ||
42
+ sources.has("websocket")) &&
43
+ (!any(left.replay_transitions, right.replay_transitions) ||
44
+ sources.has("replay_transition")));
45
+ case "process":
46
+ return sources.has("process");
47
+ case "shim":
48
+ return sources.has("shim");
49
+ }
50
+ };
@@ -0,0 +1,312 @@
1
+ import { z } from "zod";
2
+ import { processObservationLocationSchema, projectProcessObservation, } from "./processObservation.js";
3
+ import { canonicalTraceJson, comparableTracePayload, processTraceCardinalityBounds, } from "./processTraceSpecification.js";
4
+ const identifierSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._-]{0,63}$/u);
5
+ const locationSchema = processObservationLocationSchema;
6
+ const matchedEventSchema = z.strictObject({
7
+ event_id: identifierSchema,
8
+ location: locationSchema,
9
+ });
10
+ const sideResultSchema = z.strictObject({
11
+ status: z.enum(["pass", "fail", "unknown"]),
12
+ matched_variant: identifierSchema.nullable(),
13
+ satisfied_constraints: z.array(z.string()),
14
+ raw_trace: z.array(matchedEventSchema),
15
+ });
16
+ const diagnosticSchema = z.strictObject({
17
+ kind: z.enum([
18
+ "journal",
19
+ "predicate",
20
+ "cardinality",
21
+ "edge",
22
+ "prefix",
23
+ "suffix",
24
+ "trace",
25
+ ]),
26
+ side: z.enum(["left", "right"]),
27
+ message: z.string(),
28
+ event_ids: z.array(identifierSchema),
29
+ locations: z.array(locationSchema),
30
+ });
31
+ /** Structured verdict for one declared process trace language. */
32
+ export const processTraceComparisonResultSchema = z.strictObject({
33
+ verdict: z.enum(["equivalent", "different", "nonconforming", "unknown"]),
34
+ left: sideResultSchema,
35
+ right: sideResultSchema,
36
+ diagnostic: diagnosticSchema.nullable(),
37
+ });
38
+ const scopesFor = (sources) => new Set([...sources].map((source) => {
39
+ if (source.startsWith("terminal_"))
40
+ return "terminal";
41
+ if (source === "lifecycle")
42
+ return "exit";
43
+ if (source === "http" ||
44
+ source === "websocket" ||
45
+ source === "replay_transition")
46
+ return "protocol";
47
+ if (source === "filesystem")
48
+ return "filesystem";
49
+ if (source === "process")
50
+ return "process";
51
+ if (source === "shim")
52
+ return "shim";
53
+ return "interaction";
54
+ }));
55
+ const unknownSide = () => ({
56
+ status: "unknown",
57
+ matched_variant: null,
58
+ satisfied_constraints: [],
59
+ raw_trace: [],
60
+ });
61
+ const failure = (input) => ({
62
+ result: {
63
+ status: "fail",
64
+ matched_variant: null,
65
+ satisfied_constraints: [],
66
+ raw_trace: input.rawTrace,
67
+ },
68
+ diagnostic: {
69
+ kind: input.kind,
70
+ message: input.message,
71
+ event_ids: [...input.eventIds],
72
+ locations: [...input.locations],
73
+ },
74
+ });
75
+ const evaluateFiniteTrace = (ids, specification) => {
76
+ if (specification.language.kind !== "finite_traces")
77
+ return null;
78
+ for (const variant of specification.language.variants) {
79
+ let offset = 0;
80
+ let accepted = true;
81
+ for (const token of variant.trace) {
82
+ if (typeof token === "string") {
83
+ if (ids[offset] !== token)
84
+ accepted = false;
85
+ offset += 1;
86
+ continue;
87
+ }
88
+ const actual = ids.slice(offset, offset + token.unordered.length).sort();
89
+ const expected = [...token.unordered].sort();
90
+ if (canonicalTraceJson(actual) !== canonicalTraceJson(expected))
91
+ accepted = false;
92
+ offset += token.unordered.length;
93
+ }
94
+ if (accepted && offset === ids.length)
95
+ return variant.id;
96
+ }
97
+ return null;
98
+ };
99
+ const collectRecords = (capture, sources) => {
100
+ const records = [];
101
+ for (const location of capture.event_journal ?? []) {
102
+ const record = projectProcessObservation(capture, location);
103
+ if (record !== null && sources.has(record.source))
104
+ records.push({
105
+ source: record.source,
106
+ payload: record.payload,
107
+ location,
108
+ });
109
+ }
110
+ return records;
111
+ };
112
+ const matchRecords = (records, specification) => {
113
+ const rawTrace = [];
114
+ const locationsById = new Map();
115
+ const ignoredFieldsBySource = new Map();
116
+ const eventsByKey = new Map();
117
+ for (const event of specification.events) {
118
+ const ignoredFields = event.ignore_fields ?? [];
119
+ ignoredFieldsBySource.set(event.source, ignoredFields);
120
+ eventsByKey.set(`${event.source}\0${canonicalTraceJson(comparableTracePayload(event.exact, ignoredFields))}`, event);
121
+ }
122
+ for (const record of records) {
123
+ const match = eventsByKey.get(`${record.source}\0${canonicalTraceJson(comparableTracePayload(record.payload, ignoredFieldsBySource.get(record.source)))}`);
124
+ if (match === undefined)
125
+ return failure({
126
+ kind: "predicate",
127
+ message: "Observed event does not match any declared exact predicate.",
128
+ eventIds: [],
129
+ locations: [record.location],
130
+ rawTrace,
131
+ });
132
+ rawTrace.push({ event_id: match.id, location: record.location });
133
+ const locations = locationsById.get(match.id) ?? [];
134
+ locations.push(record.location);
135
+ locationsById.set(match.id, locations);
136
+ }
137
+ return { rawTrace, locationsById };
138
+ };
139
+ const cardinalityFailure = (trace, specification) => {
140
+ for (const event of specification.events) {
141
+ const [minimum, maximum] = processTraceCardinalityBounds(event.cardinality);
142
+ const locations = trace.locationsById.get(event.id) ?? [];
143
+ if (locations.length < minimum || locations.length > maximum)
144
+ return failure({
145
+ kind: "cardinality",
146
+ message: `${event.id} observed ${String(locations.length)} times; expected ${String(minimum)}..${String(maximum)}.`,
147
+ eventIds: [event.id],
148
+ locations,
149
+ rawTrace: trace.rawTrace,
150
+ });
151
+ }
152
+ return null;
153
+ };
154
+ const evaluateOrderingConstraints = (trace, language) => {
155
+ const satisfied = [];
156
+ for (const edge of language.happens_before) {
157
+ const before = trace.locationsById.get(edge.before) ?? [];
158
+ const after = trace.locationsById.get(edge.after) ?? [];
159
+ const lastBefore = before.at(-1);
160
+ const firstAfter = after[0];
161
+ if (lastBefore === undefined && firstAfter !== undefined)
162
+ return {
163
+ failure: failure({
164
+ kind: "edge",
165
+ message: `${edge.before} is required when ${edge.after} occurs.`,
166
+ eventIds: [edge.before, edge.after],
167
+ locations: [firstAfter],
168
+ rawTrace: trace.rawTrace,
169
+ }),
170
+ satisfied,
171
+ };
172
+ if (lastBefore !== undefined &&
173
+ firstAfter !== undefined &&
174
+ lastBefore.capture_order >= firstAfter.capture_order)
175
+ return {
176
+ failure: failure({
177
+ kind: "edge",
178
+ message: `${edge.before} must happen before ${edge.after}.`,
179
+ eventIds: [edge.before, edge.after],
180
+ locations: [lastBefore, firstAfter],
181
+ rawTrace: trace.rawTrace,
182
+ }),
183
+ satisfied,
184
+ };
185
+ satisfied.push(`happens_before:${edge.before}:${edge.after}`);
186
+ }
187
+ for (const constraint of language.not_before) {
188
+ const events = trace.locationsById.get(constraint.event) ?? [];
189
+ const anchors = trace.locationsById.get(constraint.anchor) ?? [];
190
+ const firstEvent = events[0];
191
+ const firstAnchor = anchors[0];
192
+ if (firstEvent !== undefined && firstAnchor === undefined)
193
+ return {
194
+ failure: failure({
195
+ kind: "edge",
196
+ message: `${constraint.anchor} is required when ${constraint.event} occurs.`,
197
+ eventIds: [constraint.event, constraint.anchor],
198
+ locations: [firstEvent],
199
+ rawTrace: trace.rawTrace,
200
+ }),
201
+ satisfied,
202
+ };
203
+ if (firstEvent !== undefined &&
204
+ firstAnchor !== undefined &&
205
+ firstEvent.capture_order < firstAnchor.capture_order)
206
+ return {
207
+ failure: failure({
208
+ kind: "edge",
209
+ message: `${constraint.event} must not occur before ${constraint.anchor}.`,
210
+ eventIds: [constraint.event, constraint.anchor],
211
+ locations: [firstEvent, firstAnchor],
212
+ rawTrace: trace.rawTrace,
213
+ }),
214
+ satisfied,
215
+ };
216
+ satisfied.push(`not_before:${constraint.event}:${constraint.anchor}`);
217
+ }
218
+ return { failure: null, satisfied };
219
+ };
220
+ const boundaryFailure = (trace, kind, expected) => {
221
+ const ids = trace.rawTrace.map(({ event_id }) => event_id);
222
+ const actual = kind === "prefix"
223
+ ? ids.slice(0, expected.length)
224
+ : expected.length === 0
225
+ ? []
226
+ : ids.slice(-expected.length);
227
+ if (canonicalTraceJson(actual) === canonicalTraceJson(expected))
228
+ return null;
229
+ const locations = kind === "prefix"
230
+ ? trace.rawTrace.slice(0, expected.length)
231
+ : trace.rawTrace.slice(-expected.length);
232
+ return failure({
233
+ kind,
234
+ message: `Observed trace does not satisfy the declared ${kind}.`,
235
+ eventIds: expected,
236
+ locations: locations.map(({ location }) => location),
237
+ rawTrace: trace.rawTrace,
238
+ });
239
+ };
240
+ const evaluatePartialOrder = (trace, language) => {
241
+ const ordering = evaluateOrderingConstraints(trace, language);
242
+ if (ordering.failure !== null)
243
+ return ordering.failure;
244
+ const prefixFailure = boundaryFailure(trace, "prefix", language.prefix);
245
+ if (prefixFailure !== null)
246
+ return prefixFailure;
247
+ const suffixFailure = boundaryFailure(trace, "suffix", language.suffix);
248
+ if (suffixFailure !== null)
249
+ return suffixFailure;
250
+ const satisfied = ordering.satisfied;
251
+ satisfied.push(...language.unordered_groups.map(({ events }) => `unordered:${events.join(":")}`));
252
+ if (language.prefix.length > 0)
253
+ satisfied.push(`prefix:${language.prefix.join(":")}`);
254
+ if (language.suffix.length > 0)
255
+ satisfied.push(`suffix:${language.suffix.join(":")}`);
256
+ return {
257
+ result: {
258
+ status: "pass",
259
+ matched_variant: null,
260
+ satisfied_constraints: satisfied,
261
+ raw_trace: trace.rawTrace,
262
+ },
263
+ diagnostic: null,
264
+ };
265
+ };
266
+ const evaluateMatchedTrace = (trace, specification) => {
267
+ const invalidCardinality = cardinalityFailure(trace, specification);
268
+ if (invalidCardinality !== null)
269
+ return invalidCardinality;
270
+ if (specification.language.kind === "partial_order")
271
+ return evaluatePartialOrder(trace, specification.language);
272
+ const ids = trace.rawTrace.map(({ event_id }) => event_id);
273
+ const variant = evaluateFiniteTrace(ids, specification);
274
+ return variant === null
275
+ ? failure({
276
+ kind: "trace",
277
+ message: "Observed events do not match any finite trace variant.",
278
+ eventIds: ids,
279
+ locations: trace.rawTrace.map(({ location }) => location),
280
+ rawTrace: trace.rawTrace,
281
+ })
282
+ : {
283
+ result: {
284
+ status: "pass",
285
+ matched_variant: variant,
286
+ satisfied_constraints: [`variant:${variant}`],
287
+ raw_trace: trace.rawTrace,
288
+ },
289
+ diagnostic: null,
290
+ };
291
+ };
292
+ export const evaluateProcessTraceSide = (capture, specification) => {
293
+ const sources = new Set(specification.events.map(({ source }) => source));
294
+ const relevantScopes = scopesFor(sources);
295
+ if (capture.truncated ||
296
+ capture.residual_unknowns.some(({ scope }) => relevantScopes.has(scope)))
297
+ return { result: unknownSide(), diagnostic: null };
298
+ if ((capture.event_journal ?? []).length === 0)
299
+ return {
300
+ result: unknownSide(),
301
+ diagnostic: {
302
+ kind: "journal",
303
+ message: "Capture has no cross-source event journal.",
304
+ event_ids: [],
305
+ locations: [],
306
+ },
307
+ };
308
+ const matched = matchRecords(collectRecords(capture, sources), specification);
309
+ return "rawTrace" in matched
310
+ ? evaluateMatchedTrace(matched, specification)
311
+ : matched;
312
+ };