rea-agents 2.0.0 → 2.2.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 (273) hide show
  1. package/README.md +103 -38
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/AndroidApplicationService.js +17 -0
  4. package/dist/application/AppleApplicationService.js +17 -0
  5. package/dist/application/ArtifactExtraction.js +1 -1
  6. package/dist/application/ArtifactInventory/classify.js +42 -0
  7. package/dist/application/ArtifactInventory/hash.js +43 -0
  8. package/dist/application/ArtifactInventory/reader.js +41 -0
  9. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  10. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  11. package/dist/application/ArtifactInventory/types.js +10 -0
  12. package/dist/application/ArtifactInventory.js +9 -351
  13. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  14. package/dist/application/BinarySession.js +47 -270
  15. package/dist/application/BinarySessionExecution.js +28 -0
  16. package/dist/application/BinarySessionOpen.js +39 -0
  17. package/dist/application/BinarySessionRecords.js +149 -0
  18. package/dist/application/BinarySessionStatus.js +75 -0
  19. package/dist/application/CapabilityInventory.js +24 -2
  20. package/dist/application/ClientRegistrationStatus.js +11 -4
  21. package/dist/application/CrossVersionInventory.js +8 -2
  22. package/dist/application/CrossVersionInvestigation.js +1 -23
  23. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  24. package/dist/application/DirectAnalysis.js +47 -44
  25. package/dist/application/Doctor.js +156 -238
  26. package/dist/application/DoctorDiagnostics.js +180 -0
  27. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  28. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  29. package/dist/application/EnhancedToolPagination.js +22 -0
  30. package/dist/application/EnhancedToolTypes.js +4 -0
  31. package/dist/application/EnhancedTools.js +20 -37
  32. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  33. package/dist/application/InventoryProjectionEvidence.js +41 -0
  34. package/dist/application/InvestigationProviders.js +12 -0
  35. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  36. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  37. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  38. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  39. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  40. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  41. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  42. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  43. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  44. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  45. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  46. package/dist/application/JavaScriptJsonModules.js +39 -0
  47. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  48. package/dist/application/JavaScriptReplayPermission.js +16 -0
  49. package/dist/application/JavaScriptReplayService.js +182 -156
  50. package/dist/application/LinuxHopper.js +10 -0
  51. package/dist/application/MacHopper.js +22 -11
  52. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  53. package/dist/application/PermissionAuthority.js +106 -63
  54. package/dist/application/ProcessCaptureCapability.js +1 -1
  55. package/dist/application/ProcessCapturePermission.js +13 -0
  56. package/dist/application/ProcessCli.js +2 -12
  57. package/dist/application/ProcessHarness.js +1 -1
  58. package/dist/application/ProcessPairedExperiment.js +72 -0
  59. package/dist/application/ProjectPermissionStore.js +48 -9
  60. package/dist/application/RealHopperAssertions.js +76 -0
  61. package/dist/application/ReconstructionCoverageService.js +69 -0
  62. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  63. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  64. package/dist/application/Setup.js +148 -260
  65. package/dist/application/SetupClientConfiguration.js +224 -0
  66. package/dist/application/SetupClients.js +24 -2
  67. package/dist/application/SetupPlan.js +124 -12
  68. package/dist/application/SetupSkill.js +54 -36
  69. package/dist/application/SupportedClients.js +1 -0
  70. package/dist/application/Uninstall.js +12 -3
  71. package/dist/application/Upgrade.js +16 -1
  72. package/dist/application/WindowsCapabilities.js +86 -0
  73. package/dist/artifacts/AsarArtifactReader.js +11 -0
  74. package/dist/browser/BrowserProcessStartup.js +88 -0
  75. package/dist/browser/CdpCaptureDocuments.js +12 -8
  76. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  77. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  78. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  79. package/dist/browser/CdpCaptureEventState.js +1 -0
  80. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  81. package/dist/browser/CdpCaptureEvents.js +29 -496
  82. package/dist/browser/CdpObservationSession.js +51 -8
  83. package/dist/browser/CdpPageCapture.js +12 -133
  84. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  85. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  86. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  87. package/dist/browser/PngVisualDiff.js +9 -2
  88. package/dist/catalogIdentity.js +4 -0
  89. package/dist/cli/artifactCommands.js +82 -0
  90. package/dist/cli/coreAnalysisCommands.js +232 -0
  91. package/dist/cli/managedCommands.js +397 -0
  92. package/dist/cli/managedProjectGraphCommand.js +24 -0
  93. package/dist/cli/options.js +9 -0
  94. package/dist/cli/setupCommands.js +113 -0
  95. package/dist/cli/types.js +1 -0
  96. package/dist/cli/utilityCommands.js +122 -0
  97. package/dist/cli.js +10 -768
  98. package/dist/cliApplicationCommands.js +118 -4
  99. package/dist/cliBrowserAdvancedCommands.js +73 -16
  100. package/dist/cliBrowserCommands.js +39 -115
  101. package/dist/cliCommandNames.js +4 -0
  102. package/dist/cliElectronCommands.js +33 -62
  103. package/dist/cliEvidenceCommands.js +13 -2
  104. package/dist/cliInvestigationCommands.js +53 -10
  105. package/dist/cliObservationOptions.js +137 -0
  106. package/dist/cliPolicyCommands.js +80 -46
  107. package/dist/cliSetup.js +306 -0
  108. package/dist/config/environment.js +107 -0
  109. package/dist/config/parseConfig.js +228 -0
  110. package/dist/config/parsers.js +68 -0
  111. package/dist/config/permissions.js +30 -0
  112. package/dist/config/types.js +1 -0
  113. package/dist/config.js +1 -375
  114. package/dist/contracts/applicationToolContracts.js +134 -0
  115. package/dist/contracts/electronToolContracts.js +51 -3
  116. package/dist/contracts/managedToolContracts.js +8 -3
  117. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  118. package/dist/contracts/providerSelection.js +2 -0
  119. package/dist/contracts/sessionStatusContract.js +26 -0
  120. package/dist/contracts/toolContractTypes.js +12 -1
  121. package/dist/contracts/toolContracts.js +5 -12
  122. package/dist/contracts/toolEffects.js +14 -0
  123. package/dist/contracts/toolOutputSchemaGroups.js +274 -0
  124. package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
  125. package/dist/contracts/toolOutputSchemas.js +2 -550
  126. package/dist/domain/analysisErrorPresentation.js +234 -0
  127. package/dist/domain/analysisErrorProjection.js +284 -0
  128. package/dist/domain/androidApplication.js +206 -0
  129. package/dist/domain/appleApplication.js +221 -0
  130. package/dist/domain/artifactComparison.js +34 -15
  131. package/dist/domain/binaryTarget.js +87 -37
  132. package/dist/domain/boundedCartesianProjection.js +15 -0
  133. package/dist/domain/browserObservation.js +1 -285
  134. package/dist/domain/browserObservationSchemas.js +286 -0
  135. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  136. package/dist/domain/errors.js +4 -486
  137. package/dist/domain/functionComparison.js +4 -165
  138. package/dist/domain/functionComparisonDimensions.js +164 -0
  139. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  140. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  141. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  142. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  143. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  145. package/dist/domain/javascriptSemanticIr.js +30 -0
  146. package/dist/domain/javascriptSemanticProjection.js +366 -0
  147. package/dist/domain/javascriptSemanticState.js +40 -0
  148. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  149. package/dist/domain/javascriptSemanticValues.js +352 -0
  150. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  151. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  152. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  153. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  154. package/dist/domain/knownPageCoverage.js +21 -0
  155. package/dist/domain/managedApplicationGraph.js +14 -364
  156. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  157. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  158. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  159. package/dist/domain/managedArtifact.js +16 -8
  160. package/dist/domain/managedMemberComparison.js +37 -391
  161. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  162. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  163. package/dist/domain/managedNativeVerification.js +27 -389
  164. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  165. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  166. package/dist/domain/managedReconstruction.js +82 -69
  167. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  168. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  169. package/dist/domain/permissionPolicy.js +2 -0
  170. package/dist/domain/processComparison.js +19 -0
  171. package/dist/domain/processPairedExperiment.js +128 -0
  172. package/dist/domain/reconstructionCoverage.js +387 -0
  173. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  174. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  175. package/dist/domain/replayMachine.js +316 -0
  176. package/dist/domain/replayMachineRuntime.js +215 -0
  177. package/dist/domain/runtimeCharacterization.js +85 -0
  178. package/dist/domain/runtimeIdentification.js +210 -0
  179. package/dist/domain/webBundleAnalyzer.js +3 -409
  180. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  181. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  182. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  183. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  184. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  185. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  186. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  187. package/dist/dotnet/ManagedMemberRows.js +120 -0
  188. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  189. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  190. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  191. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  192. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  193. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  194. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  195. package/dist/dotnet/ManagedPeReader.js +59 -28
  196. package/dist/evaluation/CodexAgentEval.js +132 -0
  197. package/dist/generatedPackageMetadata.js +2 -2
  198. package/dist/ghidra/GhidraClient.js +76 -91
  199. package/dist/ghidra/GhidraClientStartup.js +29 -0
  200. package/dist/ghidra/GhidraClientWire.js +34 -0
  201. package/dist/ghidra/GhidraDefaults.js +1 -1
  202. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  203. package/dist/ghidra/GhidraInstallation.js +20 -15
  204. package/dist/ghidra/GhidraLauncher.js +93 -29
  205. package/dist/ghidra/GhidraProvider.js +61 -161
  206. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  207. package/dist/ghidra/GhidraSessionValues.js +2 -0
  208. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  209. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  210. package/dist/ghidra/GhidraTransport.js +71 -0
  211. package/dist/identity.js +4 -2
  212. package/dist/main/messages.js +5 -0
  213. package/dist/main/reload.js +49 -0
  214. package/dist/main/shutdown.js +25 -0
  215. package/dist/main/startup.js +14 -0
  216. package/dist/main/state.js +26 -0
  217. package/dist/main/transport.js +61 -0
  218. package/dist/main/types.js +1 -0
  219. package/dist/main.js +35 -146
  220. package/dist/process/PrivateRuntimeRoot.js +3 -2
  221. package/dist/process/ProcessOwnership.js +35 -0
  222. package/dist/process/ProviderProcess.js +2 -0
  223. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  224. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  225. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  226. package/dist/replay/ReplayOutcome.js +95 -0
  227. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  228. package/dist/server/ProcessCaptureElicitation.js +157 -0
  229. package/dist/server/createServer.js +124 -56
  230. package/dist/server/javascriptApplicationResult.js +85 -0
  231. package/dist/server/registerApplicationTools/characterization.js +57 -0
  232. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  233. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  234. package/dist/server/registerApplicationTools/coverage.js +84 -0
  235. package/dist/server/registerApplicationTools/helpers.js +42 -0
  236. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  237. package/dist/server/registerApplicationTools/types.js +1 -0
  238. package/dist/server/registerApplicationTools.js +10 -112
  239. package/dist/server/registerBrowserTools.js +59 -83
  240. package/dist/server/registerElectronTools.js +48 -49
  241. package/dist/server/registerEnhancedTools.js +34 -42
  242. package/dist/server/registerEvidenceResources.js +56 -0
  243. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  244. package/dist/server/registerInvestigationTools/types.js +1 -0
  245. package/dist/server/registerInvestigationTools.js +83 -116
  246. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  247. package/dist/server/registerManagedTools.js +18 -1
  248. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  249. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  250. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  251. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  252. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  253. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  254. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  255. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  256. package/dist/server/registerManagedWorkflowTools.js +10 -274
  257. package/dist/server/registerOfficialTools.js +73 -59
  258. package/dist/server/registerSessionRecordTools.js +137 -0
  259. package/dist/server/registerSessionStatusTool.js +97 -26
  260. package/dist/server/registerSessionTools.js +60 -164
  261. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  262. package/dist/server/toolRegistrationOptions.js +101 -3
  263. package/dist/server/toolResult.js +15 -4
  264. package/package.json +11 -11
  265. package/skills/reverse-engineer-anything/SKILL.md +76 -0
  266. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  267. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  268. package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
  269. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  270. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
  271. package/scripts/prepare-node-pty.mjs +0 -35
  272. package/scripts/rebuild-native.mjs +0 -22
  273. package/skills/rea-analysis/SKILL.md +0 -222
@@ -1,8 +1,9 @@
1
- import { createHash } from "node:crypto";
2
- import canonicalize from "canonicalize";
3
1
  import { z } from "zod";
4
- import { evidenceSchema, parseEvidence } from "./evidence.js";
5
- import { managedMemberInspectionSchema, } from "./managedArtifact.js";
2
+ import { evidenceSchema } from "./evidence.js";
3
+ import { cliMetadataGuidSchema, } from "./managedArtifact.js";
4
+ import { assessKnownPageCoverage } from "./knownPageCoverage.js";
5
+ import { buildComparisonCoverage, buildComparisonMatching, buildComparisonSummary, comparisonLimitations, sideManifest, } from "./managedMemberComparisonCoverage.js";
6
+ import { buildFieldItems, buildMethodItems, keyMembers, sha256, } from "./managedMemberComparisonMatch.js";
6
7
  const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
8
  const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
8
9
  const tokenSchema = z.string().regex(/^0x[0-9a-f]{8}$/u);
@@ -107,7 +108,7 @@ export const managedMemberComparisonResultSchema = z.strictObject({
107
108
  left: z.strictObject({
108
109
  evidence_id: evidenceIdSchema,
109
110
  artifact_sha256: digestSchema,
110
- mvid: z.string().uuid().nullable(),
111
+ mvid: cliMetadataGuidSchema.nullable(),
111
112
  module_name: z.string().nullable(),
112
113
  metadata_status: z.enum(["absent", "complete", "partial", "malformed"]),
113
114
  methods_total: z.number().int().min(0),
@@ -116,7 +117,7 @@ export const managedMemberComparisonResultSchema = z.strictObject({
116
117
  right: z.strictObject({
117
118
  evidence_id: evidenceIdSchema,
118
119
  artifact_sha256: digestSchema,
119
- mvid: z.string().uuid().nullable(),
120
+ mvid: cliMetadataGuidSchema.nullable(),
120
121
  module_name: z.string().nullable(),
121
122
  metadata_status: z.enum(["absent", "complete", "partial", "malformed"]),
122
123
  methods_total: z.number().int().min(0),
@@ -149,38 +150,37 @@ export const managedMemberComparisonResultSchema = z.strictObject({
149
150
  evidence_links: z.array(evidenceIdSchema).length(2),
150
151
  limitations: z.array(boundedTextSchema).max(1_000),
151
152
  });
152
- const sha256 = (value) => {
153
- const serialized = canonicalize(value);
154
- if (serialized === undefined)
155
- throw new TypeError("Managed comparison canonicalization failed");
156
- return createHash("sha256").update(serialized).digest("hex");
157
- };
158
153
  /** Compare two parsed managed member observations without name-based matching. */
159
154
  export const compareManagedMembers = (left, right, limits) => {
160
- const methodMatches = matchMethods(left.result.methods.items.map(keyMethod), right.result.methods.items.map(keyMethod), limits.max_candidates);
161
- const fieldMatches = matchFields(left.result.fields.items.map(keyField), right.result.fields.items.map(keyField), limits.max_candidates);
162
- const methodItems = buildMethodItems(methodMatches, left.evidenceId, right.evidenceId, limits);
163
- const fieldItems = buildFieldItems(fieldMatches, left.evidenceId, right.evidenceId, limits);
164
- const allItems = [...methodItems.items, ...fieldItems.items];
165
- const summary = {
166
- unchanged: allItems.filter(({ status }) => status === "unchanged").length,
167
- changed: allItems.filter(({ status }) => status === "changed").length,
168
- added: allItems.filter(({ status }) => status === "added").length,
169
- removed: allItems.filter(({ status }) => status === "removed").length,
170
- unknown: allItems.filter(({ status }) => status === "unknown").length,
155
+ const leftMethodPage = assessKnownPageCoverage(left.result.methods);
156
+ const rightMethodPage = assessKnownPageCoverage(right.result.methods);
157
+ const leftFieldPage = assessKnownPageCoverage(left.result.fields);
158
+ const rightFieldPage = assessKnownPageCoverage(right.result.fields);
159
+ const { methodMatches, fieldMatches } = keyMembers(left, right, limits.max_candidates);
160
+ const itemContext = {
161
+ leftEvidenceId: left.evidenceId,
162
+ rightEvidenceId: right.evidenceId,
163
+ leftComplete: leftMethodPage.sourceComplete && leftFieldPage.sourceComplete,
164
+ rightComplete: rightMethodPage.sourceComplete && rightFieldPage.sourceComplete,
165
+ limits,
171
166
  };
172
- const matching = {
173
- exact_il_signature: methodItems.items.filter(({ match }) => match.basis === "exact-il-signature").length,
174
- structural_method_shape: methodItems.items.filter(({ match }) => match.basis === "structural-method-shape").length,
175
- field_signature: fieldItems.items.filter(({ match }) => match.basis === "field-signature").length,
176
- ambiguous: methodItems.items.filter(({ match }) => match.status === "ambiguous")
177
- .length +
178
- fieldItems.items.filter(({ match }) => match.status === "ambiguous")
179
- .length,
180
- unmatched: allItems.filter(({ match }) => match.status === "unmatched")
181
- .length,
182
- };
183
- const limitations = comparisonLimitations(left.result, right.result, methodItems.omitted + fieldItems.omitted, methodMatches.omittedCandidates + fieldMatches.omittedCandidates);
167
+ const methodItems = buildMethodItems(methodMatches, itemContext);
168
+ const fieldItems = buildFieldItems(fieldMatches, itemContext);
169
+ const allItems = [...methodItems.items, ...fieldItems.items];
170
+ const summary = buildComparisonSummary(allItems);
171
+ const matching = buildComparisonMatching(methodItems.items, fieldItems.items);
172
+ const coverage = buildComparisonCoverage({
173
+ left: left.result,
174
+ right: right.result,
175
+ leftMethodPage,
176
+ rightMethodPage,
177
+ leftFieldPage,
178
+ rightFieldPage,
179
+ omittedMethodItems: methodItems.omitted,
180
+ omittedFieldItems: fieldItems.omitted,
181
+ omittedCandidates: methodMatches.omittedCandidates + fieldMatches.omittedCandidates,
182
+ });
183
+ const limitations = comparisonLimitations(left.result, right.result, coverage.omitted_methods + coverage.omitted_fields, coverage.omitted_candidates);
184
184
  const result = {
185
185
  schema_version: 1,
186
186
  comparison_id: `mmc_${sha256({
@@ -202,364 +202,10 @@ export const compareManagedMembers = (left, right, limits) => {
202
202
  matching,
203
203
  methods: methodItems.items,
204
204
  fields: fieldItems.items,
205
- coverage: {
206
- status: methodItems.omitted + fieldItems.omitted > 0 ||
207
- !left.result.methods.complete ||
208
- !right.result.methods.complete ||
209
- !left.result.fields.complete ||
210
- !right.result.fields.complete
211
- ? "truncated"
212
- : left.result.coverage.state === "complete" &&
213
- right.result.coverage.state === "complete"
214
- ? "complete-within-inputs"
215
- : "partial",
216
- left_status: left.result.coverage.state,
217
- right_status: right.result.coverage.state,
218
- omitted_methods: methodItems.omitted,
219
- omitted_fields: fieldItems.omitted,
220
- omitted_candidates: methodMatches.omittedCandidates + fieldMatches.omittedCandidates,
221
- },
205
+ coverage,
222
206
  evidence_links: [left.evidenceId, right.evidenceId],
223
207
  limitations,
224
208
  };
225
209
  return managedMemberComparisonResultSchema.parse(result);
226
210
  };
227
- const sideManifest = (side) => ({
228
- evidence_id: side.evidenceId,
229
- artifact_sha256: side.result.artifact.sha256,
230
- mvid: side.result.module?.mvid ?? null,
231
- module_name: side.result.module?.name ?? null,
232
- metadata_status: side.result.metadata.status,
233
- methods_total: side.result.methods.total,
234
- fields_total: side.result.fields.total,
235
- });
236
- const keyMethod = (item) => ({
237
- item,
238
- exactKey: item.signature.parse_status === "decoded" &&
239
- item.body.normalized_il_sha256 !== null
240
- ? stableKey([
241
- "method-exact",
242
- item.signature.raw_sha256,
243
- item.body.normalized_il_sha256,
244
- ])
245
- : null,
246
- structuralKey: stableKey([
247
- "method-structural",
248
- item.signature.kind,
249
- item.signature.calling_convention,
250
- item.signature.generic_parameter_count,
251
- item.signature.parameter_count,
252
- item.signature.return_type,
253
- item.signature.parameter_types,
254
- item.body.status,
255
- item.body.header_format,
256
- item.body.il_size,
257
- item.body.max_stack,
258
- item.body.init_locals,
259
- item.body.opcode_counts,
260
- item.body.anchors.map(({ opcode, operand_kind }) => [opcode, operand_kind]),
261
- item.body.exception_regions.map((region) => [
262
- region.flags,
263
- region.try_length,
264
- region.handler_length,
265
- region.class_token === null ? null : "type-token",
266
- region.filter_offset === null ? null : "filter",
267
- ]),
268
- ]),
269
- });
270
- const keyField = (item) => ({
271
- item,
272
- exactKey: item.signature.parse_status === "decoded"
273
- ? stableKey(["field-exact", item.signature.raw_sha256])
274
- : null,
275
- structuralKey: stableKey([
276
- "field-structural",
277
- item.signature.kind,
278
- item.signature.field_type,
279
- item.flags,
280
- ]),
281
- });
282
- const stableKey = (value) => sha256(value);
283
- const matchMethods = (left, right, maxCandidates) => matchByKeys(left, right, maxCandidates, "exact-il-signature", [
284
- "structural-method-shape",
285
- ]);
286
- const matchFields = (left, right, maxCandidates) => matchByKeys(left, right, maxCandidates, "field-signature", []);
287
- const matchByKeys = (left, right, maxCandidates, exactBasis, fallbackBases) => {
288
- const usedLeft = new Set();
289
- const usedRight = new Set();
290
- const pairs = [];
291
- const ambiguous = [];
292
- let omittedCandidates = 0;
293
- const rounds = [
294
- { basis: exactBasis, key: ({ exactKey }) => exactKey },
295
- ...fallbackBases.map((basis) => ({
296
- basis,
297
- key: ({ structuralKey }) => structuralKey,
298
- })),
299
- ];
300
- for (const round of rounds) {
301
- const leftGroups = groupBy(left.filter((item) => !usedLeft.has(item)), round.key);
302
- const rightGroups = groupBy(right.filter((item) => !usedRight.has(item)), round.key);
303
- for (const [key, leftItems] of leftGroups) {
304
- const rightItems = rightGroups.get(key);
305
- if (rightItems === undefined)
306
- continue;
307
- if (leftItems.length === 1 && rightItems.length === 1) {
308
- const [leftItem] = leftItems;
309
- const [rightItem] = rightItems;
310
- if (leftItem === undefined || rightItem === undefined)
311
- continue;
312
- usedLeft.add(leftItem);
313
- usedRight.add(rightItem);
314
- pairs.push({
315
- left: leftItem,
316
- right: rightItem,
317
- basis: round.basis,
318
- confidence: round.basis === "exact-il-signature" ||
319
- round.basis === "field-signature"
320
- ? "exact"
321
- : "high",
322
- });
323
- }
324
- else {
325
- for (const item of leftItems)
326
- usedLeft.add(item);
327
- for (const item of rightItems)
328
- usedRight.add(item);
329
- omittedCandidates += Math.max(0, leftItems.length + rightItems.length - maxCandidates * 2);
330
- ambiguous.push({
331
- left: leftItems.slice(0, maxCandidates),
332
- right: rightItems.slice(0, maxCandidates),
333
- basis: round.basis,
334
- });
335
- }
336
- }
337
- }
338
- return {
339
- pairs,
340
- ambiguous,
341
- leftOnly: left.filter((item) => !usedLeft.has(item)),
342
- rightOnly: right.filter((item) => !usedRight.has(item)),
343
- omittedCandidates,
344
- };
345
- };
346
- const groupBy = (items, keyOf) => {
347
- const grouped = new Map();
348
- for (const item of items) {
349
- const key = keyOf(item);
350
- if (key === null)
351
- continue;
352
- const bucket = grouped.get(key) ?? [];
353
- bucket.push(item);
354
- grouped.set(key, bucket);
355
- }
356
- return grouped;
357
- };
358
- const buildMethodItems = (matches, leftEvidenceId, rightEvidenceId, limits) => {
359
- const items = [];
360
- for (const pair of matches.pairs) {
361
- const dimensions = changedMethodDimensions(pair.left.item, pair.right.item);
362
- items.push({
363
- item_id: `mmc_method_${sha256({
364
- left: pair.left.item.token,
365
- right: pair.right.item.token,
366
- basis: pair.basis,
367
- })}`,
368
- status: dimensions.length === 0 ? "unchanged" : "changed",
369
- left: methodIdentity(pair.left.item),
370
- right: methodIdentity(pair.right.item),
371
- match: {
372
- status: "matched",
373
- basis: pair.basis,
374
- confidence: pair.confidence,
375
- candidate_left_tokens: [],
376
- candidate_right_tokens: [],
377
- },
378
- dimensions,
379
- evidence_links: [leftEvidenceId, rightEvidenceId],
380
- limitations: [],
381
- });
382
- }
383
- for (const ambiguous of matches.ambiguous) {
384
- items.push({
385
- item_id: `mmc_method_${sha256({
386
- left: ambiguous.left.map(({ item }) => item.token),
387
- right: ambiguous.right.map(({ item }) => item.token),
388
- basis: ambiguous.basis,
389
- })}`,
390
- status: "unknown",
391
- left: null,
392
- right: null,
393
- match: {
394
- status: "ambiguous",
395
- basis: ambiguous.basis,
396
- confidence: "unknown",
397
- candidate_left_tokens: ambiguous.left.map(({ item }) => item.token),
398
- candidate_right_tokens: ambiguous.right.map(({ item }) => item.token),
399
- },
400
- dimensions: ["availability"],
401
- evidence_links: [leftEvidenceId, rightEvidenceId],
402
- limitations: [
403
- "Multiple managed methods share the same non-name identity key; REA did not guess a token remap.",
404
- ],
405
- });
406
- }
407
- for (const item of matches.leftOnly)
408
- items.push(methodOnlyItem(item.item, leftEvidenceId, rightEvidenceId, true));
409
- for (const item of matches.rightOnly)
410
- items.push(methodOnlyItem(item.item, leftEvidenceId, rightEvidenceId, false));
411
- return limitItems(items, limits.max_method_matches);
412
- };
413
- const buildFieldItems = (matches, leftEvidenceId, rightEvidenceId, limits) => {
414
- const items = [];
415
- for (const pair of matches.pairs) {
416
- const changed = pair.left.item.signature.raw_sha256 !==
417
- pair.right.item.signature.raw_sha256 ||
418
- pair.left.item.flags !== pair.right.item.flags;
419
- items.push({
420
- item_id: `mmc_field_${sha256({
421
- left: pair.left.item.token,
422
- right: pair.right.item.token,
423
- })}`,
424
- status: changed ? "changed" : "unchanged",
425
- left: fieldIdentity(pair.left.item),
426
- right: fieldIdentity(pair.right.item),
427
- match: {
428
- status: "matched",
429
- basis: pair.basis,
430
- confidence: pair.confidence,
431
- candidate_left_tokens: [],
432
- candidate_right_tokens: [],
433
- },
434
- evidence_links: [leftEvidenceId, rightEvidenceId],
435
- limitations: [],
436
- });
437
- }
438
- for (const ambiguous of matches.ambiguous)
439
- items.push({
440
- item_id: `mmc_field_${sha256({
441
- left: ambiguous.left.map(({ item }) => item.token),
442
- right: ambiguous.right.map(({ item }) => item.token),
443
- })}`,
444
- status: "unknown",
445
- left: null,
446
- right: null,
447
- match: {
448
- status: "ambiguous",
449
- basis: ambiguous.basis,
450
- confidence: "unknown",
451
- candidate_left_tokens: ambiguous.left.map(({ item }) => item.token),
452
- candidate_right_tokens: ambiguous.right.map(({ item }) => item.token),
453
- },
454
- evidence_links: [leftEvidenceId, rightEvidenceId],
455
- limitations: [
456
- "Multiple managed fields share the same signature; REA did not guess a token remap from names.",
457
- ],
458
- });
459
- for (const item of matches.leftOnly)
460
- items.push(fieldOnlyItem(item.item, leftEvidenceId, rightEvidenceId, true));
461
- for (const item of matches.rightOnly)
462
- items.push(fieldOnlyItem(item.item, leftEvidenceId, rightEvidenceId, false));
463
- return limitItems(items, limits.max_field_matches);
464
- };
465
- const limitItems = (items, limit) => ({
466
- items: items.slice(0, limit),
467
- omitted: Math.max(0, items.length - limit),
468
- });
469
- const methodOnlyItem = (item, leftEvidenceId, rightEvidenceId, left) => ({
470
- item_id: `mmc_method_${sha256({ token: item.token, side: left ? "left" : "right" })}`,
471
- status: left ? "removed" : "added",
472
- left: left ? methodIdentity(item) : null,
473
- right: left ? null : methodIdentity(item),
474
- match: {
475
- status: "unmatched",
476
- basis: "none",
477
- confidence: "unknown",
478
- candidate_left_tokens: [],
479
- candidate_right_tokens: [],
480
- },
481
- dimensions: ["availability"],
482
- evidence_links: [leftEvidenceId, rightEvidenceId],
483
- limitations: [],
484
- });
485
- const fieldOnlyItem = (item, leftEvidenceId, rightEvidenceId, left) => ({
486
- item_id: `mmc_field_${sha256({ token: item.token, side: left ? "left" : "right" })}`,
487
- status: left ? "removed" : "added",
488
- left: left ? fieldIdentity(item) : null,
489
- right: left ? null : fieldIdentity(item),
490
- match: {
491
- status: "unmatched",
492
- basis: "none",
493
- confidence: "unknown",
494
- candidate_left_tokens: [],
495
- candidate_right_tokens: [],
496
- },
497
- evidence_links: [leftEvidenceId, rightEvidenceId],
498
- limitations: [],
499
- });
500
- const methodIdentity = (item) => ({
501
- token: item.token,
502
- declaring_type: item.declaring_type,
503
- name: item.name,
504
- signature_sha256: item.signature.raw_sha256,
505
- normalized_il_sha256: item.body.normalized_il_sha256,
506
- });
507
- const fieldIdentity = (item) => ({
508
- token: item.token,
509
- declaring_type: item.declaring_type,
510
- name: item.name,
511
- signature_sha256: item.signature.raw_sha256,
512
- });
513
- const changedMethodDimensions = (left, right) => {
514
- const dimensions = [];
515
- if (left.signature.raw_sha256 !== right.signature.raw_sha256)
516
- dimensions.push("signature");
517
- if (left.body.normalized_il_sha256 !== right.body.normalized_il_sha256)
518
- dimensions.push("cil");
519
- if (canonicalize(left.body.opcode_counts) !==
520
- canonicalize(right.body.opcode_counts))
521
- dimensions.push("opcode-shape");
522
- if (callShape(left.body.anchors) !== callShape(right.body.anchors))
523
- dimensions.push("call-shape");
524
- if (fieldShape(left.body.anchors) !== fieldShape(right.body.anchors))
525
- dimensions.push("field-shape");
526
- if (canonicalize(left.body.exception_regions) !==
527
- canonicalize(right.body.exception_regions))
528
- dimensions.push("exception-shape");
529
- if (left.body.status !== right.body.status)
530
- dimensions.push("availability");
531
- return dimensions;
532
- };
533
- const callShape = (anchors) => JSON.stringify(anchors
534
- .filter(({ operand_kind }) => operand_kind === "method")
535
- .map(({ opcode, operand_kind }) => [opcode, operand_kind]));
536
- const fieldShape = (anchors) => JSON.stringify(anchors
537
- .filter(({ operand_kind }) => operand_kind === "field")
538
- .map(({ opcode, operand_kind }) => [opcode, operand_kind]));
539
- const comparisonLimitations = (left, right, omittedItems, omittedCandidates) => {
540
- const limitations = [
541
- "Metadata tokens are build-local coordinates; matched pairs are remaps, not persistent identities.",
542
- "Names are reported as observations but are not used as a matching basis.",
543
- ];
544
- if (!left.methods.complete || !right.methods.complete)
545
- limitations.push("At least one method page is incomplete.");
546
- if (!left.fields.complete || !right.fields.complete)
547
- limitations.push("At least one field page is incomplete.");
548
- if (left.coverage.state !== "complete" || right.coverage.state !== "complete")
549
- limitations.push("At least one managed member observation is partial.");
550
- if (omittedItems > 0)
551
- limitations.push(`${String(omittedItems)} comparison items were omitted by output limits.`);
552
- if (omittedCandidates > 0)
553
- limitations.push(`${String(omittedCandidates)} ambiguous candidates were omitted by candidate limits.`);
554
- return limitations;
555
- };
556
- /** Extract and authenticate a managed member inspection Evidence record. */
557
- export const parseManagedMemberEvidence = (evidence) => {
558
- const parsed = parseEvidence(evidence);
559
- if (parsed.operation !== "inspect_managed_members")
560
- throw new TypeError("Evidence operation is not inspect_managed_members");
561
- return {
562
- evidenceId: parsed.evidence_id,
563
- result: managedMemberInspectionSchema.parse(parsed.normalized_result),
564
- };
565
- };
211
+ export { parseManagedMemberEvidence } from "./managedMemberComparisonMatch.js";
@@ -0,0 +1,72 @@
1
+ export const buildComparisonCoverage = ({ left, right, leftMethodPage, rightMethodPage, leftFieldPage, rightFieldPage, omittedMethodItems, omittedFieldItems, omittedCandidates, }) => {
2
+ const omittedMethods = omittedMethodItems +
3
+ leftMethodPage.sourceOmittedCount +
4
+ rightMethodPage.sourceOmittedCount;
5
+ const omittedFields = omittedFieldItems +
6
+ leftFieldPage.sourceOmittedCount +
7
+ rightFieldPage.sourceOmittedCount;
8
+ const leftStatus = sideCoverageStatus(left, leftMethodPage.sourceComplete && leftFieldPage.sourceComplete);
9
+ const rightStatus = sideCoverageStatus(right, rightMethodPage.sourceComplete && rightFieldPage.sourceComplete);
10
+ const unknownInput = left.coverage.state !== "complete" || right.coverage.state !== "complete";
11
+ const truncated = omittedMethods + omittedFields + omittedCandidates > 0;
12
+ return {
13
+ status: unknownInput
14
+ ? "partial"
15
+ : truncated
16
+ ? "truncated"
17
+ : leftStatus === "complete" && rightStatus === "complete"
18
+ ? "complete-within-inputs"
19
+ : "partial",
20
+ left_status: leftStatus,
21
+ right_status: rightStatus,
22
+ omitted_methods: omittedMethods,
23
+ omitted_fields: omittedFields,
24
+ omitted_candidates: omittedCandidates,
25
+ };
26
+ };
27
+ const sideCoverageStatus = (result, pagesComplete) => result.coverage.state === "unavailable"
28
+ ? "unavailable"
29
+ : result.coverage.state === "complete" && pagesComplete
30
+ ? "complete"
31
+ : "partial";
32
+ export const sideManifest = (side) => ({
33
+ evidence_id: side.evidenceId,
34
+ artifact_sha256: side.result.artifact.sha256,
35
+ mvid: side.result.module?.mvid ?? null,
36
+ module_name: side.result.module?.name ?? null,
37
+ metadata_status: side.result.metadata.status,
38
+ methods_total: side.result.methods.total,
39
+ fields_total: side.result.fields.total,
40
+ });
41
+ export const buildComparisonSummary = (items) => ({
42
+ unchanged: items.filter(({ status }) => status === "unchanged").length,
43
+ changed: items.filter(({ status }) => status === "changed").length,
44
+ added: items.filter(({ status }) => status === "added").length,
45
+ removed: items.filter(({ status }) => status === "removed").length,
46
+ unknown: items.filter(({ status }) => status === "unknown").length,
47
+ });
48
+ export const buildComparisonMatching = (methodItems, fieldItems) => ({
49
+ exact_il_signature: methodItems.filter(({ match }) => match.basis === "exact-il-signature").length,
50
+ structural_method_shape: methodItems.filter(({ match }) => match.basis === "structural-method-shape").length,
51
+ field_signature: fieldItems.filter(({ match }) => match.basis === "field-signature").length,
52
+ ambiguous: methodItems.filter(({ match }) => match.status === "ambiguous").length +
53
+ fieldItems.filter(({ match }) => match.status === "ambiguous").length,
54
+ unmatched: [...methodItems, ...fieldItems].filter(({ match }) => match.status === "unmatched").length,
55
+ });
56
+ export const comparisonLimitations = (left, right, omittedItems, omittedCandidates) => {
57
+ const limitations = [
58
+ "Metadata tokens are build-local coordinates; matched pairs are remaps, not persistent identities.",
59
+ "Names are reported as observations but are not used as a matching basis.",
60
+ ];
61
+ if (!left.methods.complete || !right.methods.complete)
62
+ limitations.push("At least one method page is incomplete.");
63
+ if (!left.fields.complete || !right.fields.complete)
64
+ limitations.push("At least one field page is incomplete.");
65
+ if (left.coverage.state !== "complete" || right.coverage.state !== "complete")
66
+ limitations.push("At least one managed member observation is partial.");
67
+ if (omittedItems > 0)
68
+ limitations.push(`${String(omittedItems)} comparison items were omitted by output limits.`);
69
+ if (omittedCandidates > 0)
70
+ limitations.push(`${String(omittedCandidates)} ambiguous candidates were omitted by candidate limits.`);
71
+ return limitations;
72
+ };